diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h --- a/src/avalanche/peermanager.h +++ b/src/avalanche/peermanager.h @@ -131,31 +131,41 @@ public: /** - * Provide the peer associated with the given proof. If the peer does not - * exists, then it is created. + * Node API. */ - PeerId getPeer(const Proof &proof); + bool addNode(NodeId nodeid, const Proof &proof, + const Delegation &delegation); + bool removeNode(NodeId nodeid); + + bool forNode(NodeId nodeid, std::function func) const; + bool updateNextRequestTime(NodeId nodeid, TimePoint timeout); /** - * Remove an existing peer. - * This is not meant for public consumption. + * Randomly select a node to poll. */ - bool removePeer(const PeerId peerid); + NodeId selectNode(); /** - * Node API. + * Update the peer set when a nw block is connected. */ - bool addNode(NodeId nodeid, const Proof &proof, - const Delegation &delegation); - bool removeNode(NodeId nodeid); + void updatedBlockTip(); - NodeId selectNode(); + /**************************************************** + * Functions which are public for testing purposes. * + ****************************************************/ + /** + * Provide the PeerId associated with the given proof. If the peer does not + * exists, then it is created. + */ + PeerId getPeerId(const Proof &proof); - bool forNode(NodeId nodeid, std::function func) const; - bool updateNextRequestTime(NodeId nodeid, TimePoint timeout); + /** + * Remove an existing peer. + */ + bool removePeer(const PeerId peerid); /** - * Exposed for tests. + * Randomly select a peer to poll. */ PeerId selectPeer() const; @@ -167,7 +177,6 @@ /** * Perform consistency check on internal data structures. - * Mostly useful for tests. */ bool verify() const; @@ -175,10 +184,8 @@ uint64_t getSlotCount() const { return slotCount; } uint64_t getFragmentation() const { return fragmentation; } - /** - * Update the peer set when a nw block is connected. - */ - void updatedBlockTip(); +private: + PeerSet::iterator fetchOrCreatePeer(const Proof &proof); }; /** diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp --- a/src/avalanche/peermanager.cpp +++ b/src/avalanche/peermanager.cpp @@ -13,13 +13,106 @@ namespace avalanche { -PeerId PeerManager::getPeer(const Proof &proof) { +bool PeerManager::addNode(NodeId nodeid, const Proof &proof, + const Delegation &delegation) { + const PeerId peerid = getPeerId(proof); + if (peerid == NO_PEER) { + return false; + } + + DelegationState state; + CPubKey pubkey; + if (!delegation.verify(state, proof, pubkey)) { + return false; + } + + auto nit = nodes.find(nodeid); + if (nit == nodes.end()) { + return nodes.emplace(nodeid, peerid, std::move(pubkey)).second; + } + + // We actually have this node already, we need to update it. + return nodes.modify(nit, [&](Node &n) { + n.peerid = peerid; + n.pubkey = std::move(pubkey); + }); +} + +bool PeerManager::removeNode(NodeId nodeid) { + return nodes.erase(nodeid) > 0; +} + +bool PeerManager::forNode(NodeId nodeid, + std::function func) const { + auto it = nodes.find(nodeid); + return it != nodes.end() && func(*it); +} + +bool PeerManager::updateNextRequestTime(NodeId nodeid, TimePoint timeout) { + auto it = nodes.find(nodeid); + if (it == nodes.end()) { + return false; + } + + return nodes.modify(it, [&](Node &n) { n.nextRequestTime = timeout; }); +} + +NodeId PeerManager::selectNode() { + for (int retry = 0; retry < SELECT_NODE_MAX_RETRY; retry++) { + const PeerId p = selectPeer(); + + // If we cannot find a peer, it may be due to the fact that it is + // unlikely due to high fragmentation, so compact and retry. + if (p == NO_PEER) { + compact(); + continue; + } + + // See if that peer has an available node. + auto &nview = nodes.get(); + auto it = nview.lower_bound(boost::make_tuple(p, TimePoint())); + if (it != nview.end() && it->peerid == p && + it->nextRequestTime <= std::chrono::steady_clock::now()) { + return it->nodeid; + } + } + + return NO_NODE; +} + +void PeerManager::updatedBlockTip() { + std::vector invalidPeers; + + { + LOCK(cs_main); + + const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); + for (const auto &p : peers) { + ProofValidationState state; + if (!p.proof.verify(state, coins)) { + invalidPeers.push_back(p.peerid); + } + } + } + + for (const auto &pid : invalidPeers) { + removePeer(pid); + } +} + +PeerId PeerManager::getPeerId(const Proof &proof) { + auto it = fetchOrCreatePeer(proof); + return it == peers.end() ? NO_PEER : it->peerid; +} + +PeerManager::PeerSet::iterator +PeerManager::fetchOrCreatePeer(const Proof &proof) { { // Check if we already know of that peer. auto &pview = peers.get(); auto it = pview.find(proof.getId()); if (it != pview.end()) { - return it->peerid; + return peers.project<0>(it); } } @@ -30,7 +123,7 @@ ProofValidationState state; if (!proof.verify(state, coins)) { - return NO_PEER; + return peers.end(); } } @@ -59,7 +152,7 @@ } } - return NO_PEER; + return peers.end(); } // We have no peer for this proof, time to create it. @@ -70,7 +163,8 @@ const uint64_t start = slotCount; slots.emplace_back(start, score, peerid); slotCount = start + score; - return peerid; + + return inserted.first; } bool PeerManager::removePeer(const PeerId peerid) { @@ -108,73 +202,6 @@ return true; } -bool PeerManager::addNode(NodeId nodeid, const Proof &proof, - const Delegation &delegation) { - const PeerId peerid = getPeer(proof); - if (peerid == NO_PEER) { - return false; - } - - DelegationState state; - CPubKey pubkey; - if (!delegation.verify(state, proof, pubkey)) { - return false; - } - - auto nit = nodes.find(nodeid); - if (nit == nodes.end()) { - return nodes.emplace(nodeid, peerid, std::move(pubkey)).second; - } - - // We actually have this node already, we need to update it. - return nodes.modify(nit, [&](Node &n) { - n.peerid = peerid; - n.pubkey = std::move(pubkey); - }); -} - -bool PeerManager::removeNode(NodeId nodeid) { - return nodes.erase(nodeid) > 0; -} - -bool PeerManager::forNode(NodeId nodeid, - std::function func) const { - auto it = nodes.find(nodeid); - return it != nodes.end() && func(*it); -} - -bool PeerManager::updateNextRequestTime(NodeId nodeid, TimePoint timeout) { - auto it = nodes.find(nodeid); - if (it == nodes.end()) { - return false; - } - - return nodes.modify(it, [&](Node &n) { n.nextRequestTime = timeout; }); -} - -NodeId PeerManager::selectNode() { - for (int retry = 0; retry < SELECT_NODE_MAX_RETRY; retry++) { - const PeerId p = selectPeer(); - - // If we cannot find a peer, it may be due to the fact that it is - // unlikely due to high fragmentation, so compact and retry. - if (p == NO_PEER) { - compact(); - continue; - } - - // See if that peer has an available node. - auto &nview = nodes.get(); - auto it = nview.lower_bound(boost::make_tuple(p, TimePoint())); - if (it != nview.end() && it->peerid == p && - it->nextRequestTime <= std::chrono::steady_clock::now()) { - return it->nodeid; - } - } - - return NO_NODE; -} - PeerId PeerManager::selectPeer() const { if (slots.empty() || slotCount == 0) { return NO_PEER; @@ -314,24 +341,4 @@ return NO_PEER; } -void PeerManager::updatedBlockTip() { - std::vector invalidPeers; - - { - LOCK(cs_main); - - const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); - for (const auto &p : peers) { - ProofValidationState state; - if (!p.proof.verify(state, coins)) { - invalidPeers.push_back(p.peerid); - } - } - } - - for (const auto &pid : invalidPeers) { - removePeer(pid); - } -} - } // namespace avalanche diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -171,8 +171,10 @@ CDataStream stream(ParseHex(gArgs.GetArg("-avaproof", "")), SER_NETWORK, 0); stream >> peerData->proof; + + // Ensure the peer manager knows about it. LOCK(cs_peerManager); - peerManager->getPeer(peerData->proof); + peerManager->getPeerId(peerData->proof); } // Generate the delegation to the session key. diff --git a/src/avalanche/test/peermanager_tests.cpp b/src/avalanche/test/peermanager_tests.cpp --- a/src/avalanche/test/peermanager_tests.cpp +++ b/src/avalanche/test/peermanager_tests.cpp @@ -204,7 +204,7 @@ // Add 4 peers. std::array peerids; for (int i = 0; i < 4; i++) { - peerids[i] = pm.getPeer(buildRandomProof(100)); + peerids[i] = pm.getPeerId(buildRandomProof(100)); } BOOST_CHECK_EQUAL(pm.getSlotCount(), 400); @@ -234,7 +234,7 @@ // Add 4 more peers. for (int i = 0; i < 4; i++) { - peerids[i + 4] = pm.getPeer(buildRandomProof(100)); + peerids[i + 4] = pm.getPeerId(buildRandomProof(100)); } BOOST_CHECK_EQUAL(pm.getSlotCount(), 700); @@ -274,7 +274,7 @@ // Add 4 peers. std::array peerids; for (int i = 0; i < 4; i++) { - peerids[i] = pm.getPeer(buildRandomProof(100)); + peerids[i] = pm.getPeerId(buildRandomProof(100)); } // Remove all peers. @@ -387,7 +387,7 @@ pb.addUTXO(o, v, height, false, key); } - return pm.getPeer(pb.build()); + return pm.getPeerId(pb.build()); }; // Add one peer.