diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h --- a/src/avalanche/peermanager.h +++ b/src/avalanche/peermanager.h @@ -159,6 +159,7 @@ * exists, then it is created. */ PeerId getPeerId(const Proof &proof); + PeerId getPeerId(const Proof &proof, ProofValidationState &state); /** * Remove an existing peer. @@ -189,7 +190,8 @@ std::vector getNodeIdsForPeer(PeerId peerId) const; private: - PeerSet::iterator fetchOrCreatePeer(const Proof &proof); + PeerSet::iterator fetchOrCreatePeer(const Proof &proof, + ProofValidationState &state); bool addNodeToPeer(const PeerSet::iterator &it); bool removeNodeFromPeer(const PeerSet::iterator &it, uint32_t count = 1); }; diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp --- a/src/avalanche/peermanager.cpp +++ b/src/avalanche/peermanager.cpp @@ -15,7 +15,8 @@ bool PeerManager::addNode(NodeId nodeid, const Proof &proof, const Delegation &delegation) { - auto it = fetchOrCreatePeer(proof); + ProofValidationState validationState; + auto it = fetchOrCreatePeer(proof, validationState); if (it == peers.end()) { return false; } @@ -181,12 +182,18 @@ } PeerId PeerManager::getPeerId(const Proof &proof) { - auto it = fetchOrCreatePeer(proof); + ProofValidationState state; + return getPeerId(proof, state); +} + +PeerId PeerManager::getPeerId(const Proof &proof, ProofValidationState &state) { + auto it = fetchOrCreatePeer(proof, state); return it == peers.end() ? NO_PEER : it->peerid; } PeerManager::PeerSet::iterator -PeerManager::fetchOrCreatePeer(const Proof &proof) { +PeerManager::fetchOrCreatePeer(const Proof &proof, + ProofValidationState &state) { { // Check if we already know of that peer. auto &pview = peers.get(); @@ -201,7 +208,6 @@ LOCK(cs_main); const CCoinsViewCache &coins = ::ChainstateActive().CoinsTip(); - ProofValidationState state; if (!proof.verify(state, coins)) { return peers.end(); } diff --git a/src/avalanche/processor.h b/src/avalanche/processor.h --- a/src/avalanche/processor.h +++ b/src/avalanche/processor.h @@ -261,6 +261,12 @@ class NotificationsHandler; std::unique_ptr chainNotificationsHandler; + /** + * Flag indicating that the proof must be registered at first new block + * after IBD + */ + bool mustRegisterProof = false; + public: Processor(interfaces::Chain &chain, CConnman *connmanIn, NodePeerManager *nodePeerManagerIn); diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include // For DecodeSecret #include // For ::PeerManager @@ -149,6 +150,20 @@ void updatedBlockTip() override { LOCK(m_processor->cs_peerManager); + + if (m_processor->mustRegisterProof && + !::ChainstateActive().IsInitialBlockDownload()) { + ProofValidationState state; + m_processor->peerManager->getPeerId(m_processor->peerData->proof, + state); + // If proof validation failed due to missing UTXO, maybe the UTXO + // will be in a future block. + if (state.IsValid() || + state.GetResult() != ProofValidationResult::MISSING_UTXO) { + m_processor->mustRegisterProof = false; + } + } + m_processor->peerManager->updatedBlockTip(); } }; @@ -174,13 +189,8 @@ SER_NETWORK, 0); stream >> peerData->proof; - // Ensure the peer manager knows about it. - // FIXME: There is no way to register the proof at this time because - // we might not have the proper chainstate at the moment. We need to - // find a way to delay the registration of the proof until after IBD - // has finished and the chain state is settled. - // LOCK(cs_peerManager); - // peerManager->getPeerId(peerData->proof); + // Schedule proof registration at the first new block after IBD. + mustRegisterProof = true; } // Generate the delegation to the session key. diff --git a/test/functional/abc_p2p_avalanche.py b/test/functional/abc_p2p_avalanche.py --- a/test/functional/abc_p2p_avalanche.py +++ b/test/functional/abc_p2p_avalanche.py @@ -373,15 +373,29 @@ wait_until(has_parked_new_tip, timeout=15) assert_equal(node.getbestblockhash(), fork_tip) - # Restart the node and rebuild the quorum + # Restart the node + minchainwork = int(node.getblockchaininfo()["chainwork"], 16) + 1 self.restart_node(0, self.extra_args[0] + [ "-avaproof={}".format(proof), "-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", + "-minimumchainwork=0x{:x}".format(minchainwork), ]) + self.log.info( + "The proof verification should be delayed until IBD is complete") + assert node.getblockchaininfo()["initialblockdownload"] is True + # Our proof cannot be verified during IBD, so we should have no peer + assert not node.getavalanchepeerinfo() + # Mining a few more blocks should cause us to leave IBD + node.generate(2) + # Our proof is now verified and our node is added as a peer + assert node.getblockchaininfo()["initialblockdownload"] is False + assert_equal(len(node.getavalanchepeerinfo()), 1) + + # Rebuild the quorum + self.log.info("Test the avahello signature") quorum = get_quorum() poll_node = quorum[0] - # Check the avahello is consistent avahello = poll_node.wait_for_avahello().hello avakey.set(bytes.fromhex(node.getavalanchekey()))