diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h index 6013fc9fe..3d46cb0f0 100644 --- a/src/avalanche/peermanager.h +++ b/src/avalanche/peermanager.h @@ -1,321 +1,289 @@ // Copyright (c) 2020 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_AVALANCHE_PEERMANAGER_H #define BITCOIN_AVALANCHE_PEERMANAGER_H #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace avalanche { /** * Maximum number of stakes in the orphanProofs. * Benchmarking on a consumer grade computer shows that 10000 stakes can be * verified in less than 1 second. */ static constexpr size_t AVALANCHE_ORPHANPROOFPOOL_SIZE = 10000; class Delegation; namespace { struct TestPeerManager; } struct Slot { private: uint64_t start; uint32_t score; PeerId peerid; public: Slot(uint64_t startIn, uint32_t scoreIn, PeerId peeridIn) : start(startIn), score(scoreIn), peerid(peeridIn) {} Slot withStart(uint64_t startIn) const { return Slot(startIn, score, peerid); } Slot withScore(uint64_t scoreIn) const { return Slot(start, scoreIn, peerid); } Slot withPeerId(PeerId peeridIn) const { return Slot(start, score, peeridIn); } uint64_t getStart() const { return start; } uint64_t getStop() const { return start + score; } uint32_t getScore() const { return score; } PeerId getPeerId() const { return peerid; } bool contains(uint64_t slot) const { return getStart() <= slot && slot < getStop(); } bool precedes(uint64_t slot) const { return slot >= getStop(); } bool follows(uint64_t slot) const { return getStart() > slot; } }; struct Peer { PeerId peerid; uint32_t index = -1; uint32_t node_count = 0; ProofRef proof; // The network stack uses timestamp in seconds, so we oblige. std::chrono::seconds registration_time; Peer(PeerId peerid_, ProofRef proof_) : peerid(peerid_), proof(std::move(proof_)), registration_time(GetTime()) {} const ProofId &getProofId() const { return proof->getId(); } uint32_t getScore() const { return proof->getScore(); } }; -struct ProofPoolEntry { - size_t utxoIndex; - ProofRef proof; - - const COutPoint &getUTXO() const { - return proof->getStakes().at(utxoIndex).getStake().getUTXO(); - } - - ProofPoolEntry(size_t _utxoIndex, const ProofRef &_proof) - : utxoIndex(_utxoIndex), proof(_proof) {} -}; - -struct by_utxo; - -template struct proof_index { +struct proof_index { using result_type = ProofId; - result_type operator()(const StructWithProof &s) const { - return s.proof->getId(); - } + result_type operator()(const Peer &p) const { return p.proof->getId(); } }; struct next_request_time {}; struct PendingNode { ProofId proofid; NodeId nodeid; PendingNode(ProofId proofid_, NodeId nodeid_) : proofid(proofid_), nodeid(nodeid_){}; }; struct by_proofid; struct by_nodeid; namespace bmi = boost::multi_index; class PeerManager { std::vector slots; uint64_t slotCount = 0; uint64_t fragmentation = 0; /** * Several nodes can make an avalanche peer. In this case, all nodes are * considered interchangeable parts of the same peer. */ using PeerSet = boost::multi_index_container< Peer, bmi::indexed_by< // index by peerid bmi::hashed_unique>, // index by proof - bmi::hashed_unique, proof_index, + bmi::hashed_unique, proof_index, SaltedProofIdHasher>>>; PeerId nextPeerId = 0; PeerSet peers; - /** - * Map a proof to each utxo. A proof can be mapped with several utxos. - */ - using ProofPool = boost::multi_index_container< - ProofPoolEntry, - bmi::indexed_by< - // index by utxo - bmi::hashed_unique< - bmi::tag, - bmi::const_mem_fun, - SaltedOutpointHasher>, - // index by proofid - bmi::hashed_non_unique, - proof_index, - SaltedProofIdHasher>>>; - ProofPool validProofPool; using NodeSet = boost::multi_index_container< Node, bmi::indexed_by< // index by nodeid bmi::hashed_unique>, // sorted by peerid/nextRequestTime bmi::ordered_non_unique< bmi::tag, bmi::composite_key< Node, bmi::member, bmi::member>>>>; NodeSet nodes; using PendingNodeSet = boost::multi_index_container< PendingNode, bmi::indexed_by< // index by proofid bmi::hashed_non_unique< bmi::tag, bmi::member, SaltedProofIdHasher>, // index by nodeid bmi::hashed_unique< bmi::tag, bmi::member>>>; PendingNodeSet pendingNodes; static constexpr int SELECT_PEER_MAX_RETRY = 3; static constexpr int SELECT_NODE_MAX_RETRY = 3; /** * Tracks proof which for which the UTXO are unavailable. */ OrphanProofPool orphanProofs{AVALANCHE_ORPHANPROOFPOOL_SIZE}; /** * Track proof ids to broadcast */ std::unordered_set m_unbroadcast_proofids; public: /** * Node API. */ bool addNode(NodeId nodeid, const ProofId &proofid); bool removeNode(NodeId nodeid); // Update when a node is to be polled next. bool updateNextRequestTime(NodeId nodeid, TimePoint timeout); // Randomly select a node to poll. NodeId selectNode(); template bool forNode(NodeId nodeid, Callable &&func) const { auto it = nodes.find(nodeid); return it != nodes.end() && func(*it); } template void forEachNode(const Peer &peer, Callable &&func) const { auto &nview = nodes.get(); auto range = nview.equal_range(peer.peerid); for (auto it = range.first; it != range.second; ++it) { func(*it); } } /** * Proof and Peer related API. */ bool registerProof(const ProofRef &proof); bool exists(const ProofId &proofid) const { return getProof(proofid) != nullptr; } template bool forPeer(const ProofId &proofid, Callable &&func) const { auto &pview = peers.get(); auto it = pview.find(proofid); return it != pview.end() && func(*it); } template void forEachPeer(Callable &&func) const { for (const auto &p : peers) { func(p); } } /** * Update the peer set when a new block is connected. */ void updatedBlockTip(); /** * Proof broadcast API. */ void addUnbroadcastProof(const ProofId &proofid); void removeUnbroadcastProof(const ProofId &proofid); auto getUnbroadcastProofs() const { return m_unbroadcast_proofids; } /**************************************************** * Functions which are public for testing purposes. * ****************************************************/ /** * Remove an existing peer. */ bool removePeer(const PeerId peerid); /** * Randomly select a peer to poll. */ PeerId selectPeer() const; /** * Trigger maintenance of internal data structures. * Returns how much slot space was saved after compaction. */ uint64_t compact(); /** * Perform consistency check on internal data structures. */ bool verify() const; // Accessors. uint64_t getSlotCount() const { return slotCount; } uint64_t getFragmentation() const { return fragmentation; } ProofRef getProof(const ProofId &proofid) const; bool isBoundToPeer(const ProofId &proofid) const; bool isOrphan(const ProofId &proofid) const; private: bool createPeer(const ProofRef &proof); bool addOrUpdateNode(const PeerSet::iterator &it, NodeId nodeid); bool addNodeToPeer(const PeerSet::iterator &it); bool removeNodeFromPeer(const PeerSet::iterator &it, uint32_t count = 1); friend struct ::avalanche::TestPeerManager; }; /** * Internal methods that are exposed for testing purposes. */ PeerId selectPeerImpl(const std::vector &slots, const uint64_t slot, const uint64_t max); } // namespace avalanche #endif // BITCOIN_AVALANCHE_PEERMANAGER_H diff --git a/src/avalanche/proofpool.h b/src/avalanche/proofpool.h new file mode 100644 index 000000000..8b708d6dd --- /dev/null +++ b/src/avalanche/proofpool.h @@ -0,0 +1,63 @@ +// Copyright (c) 2021 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_AVALANCHE_PROOFPOOL_H +#define BITCOIN_AVALANCHE_PROOFPOOL_H + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace avalanche { + +struct ProofPoolEntry { + size_t utxoIndex; + ProofRef proof; + + const COutPoint &getUTXO() const { + return proof->getStakes().at(utxoIndex).getStake().getUTXO(); + } + + ProofPoolEntry(size_t _utxoIndex, ProofRef _proof) + : utxoIndex(_utxoIndex), proof(std::move(_proof)) {} +}; + +struct by_utxo; +struct by_proofid; + +struct ProofPoolEntryProofIdKeyExtractor { + using result_type = ProofId; + result_type operator()(const ProofPoolEntry &entry) const { + return entry.proof->getId(); + } +}; + +namespace bmi = boost::multi_index; + +/** + * Map a proof to each utxo. A proof can be mapped with several utxos. + */ +using ProofPool = boost::multi_index_container< + ProofPoolEntry, + bmi::indexed_by< + // index by utxo + bmi::hashed_unique, + bmi::const_mem_fun, + SaltedOutpointHasher>, + // index by proofid + bmi::hashed_non_unique, + ProofPoolEntryProofIdKeyExtractor, + SaltedProofIdHasher>>>; + +} // namespace avalanche + +#endif // BITCOIN_AVALANCHE_PROOFPOOL_H