diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h --- a/src/avalanche/peermanager.h +++ b/src/avalanche/peermanager.h @@ -174,6 +174,11 @@ */ std::unordered_set m_unbroadcast_proofids; + /** + * Track proof ids from conflicting proofs + */ + std::unordered_set m_conflicting_proofids; + public: class ConflictingProofHandler { public: @@ -185,10 +190,12 @@ std::unordered_set> conflictingProofHandlers; - /** Conflicting proof callback */ + /** Conflicting proofs API */ std::unique_ptr handleConflictingProof(std::shared_ptr handler); + bool isConflictingProof(const ProofId &proofid) const; + /** * Node API. */ diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp --- a/src/avalanche/peermanager.cpp +++ b/src/avalanche/peermanager.cpp @@ -45,6 +45,10 @@ std::move(handler)); } +bool PeerManager::isConflictingProof(const ProofId &proofid) const { + return !!m_conflicting_proofids.count(proofid); +} + bool PeerManager::addNode(NodeId nodeid, const ProofId &proofid) { auto &pview = peers.get(); auto it = pview.find(proofid); @@ -337,6 +341,8 @@ } } + m_conflicting_proofids.insert(proofid); + for (auto &cb : conflictingProofHandlers) { cb->onConflictingProof(proof, accepted); } diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -4458,11 +4458,12 @@ LogPrint(BCLog::NET, "New avalanche proof: peer=%d, proofid %s\n", nodeid, proofid.ToString()); } else { - // If the proof couldn't be added, it can be either orphan or + // If the proof couldn't be added, it can be orphan, conflicting or // invalid. In the latter case we should increase the ban score. // TODO improve the ban reason by printing the validation state if (!g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) { - return pm.getOrphan(proofid); + return pm.getOrphan(proofid) || + pm.isConflictingProof(proofid); })) { WITH_LOCK(cs_rejectedProofs, rejectedProofs->insert(proofid)); Misbehaving(nodeid, 100, "invalid-avaproof"); diff --git a/test/functional/abc_p2p_avalanche_proof_voting.py b/test/functional/abc_p2p_avalanche_proof_voting.py --- a/test/functional/abc_p2p_avalanche_proof_voting.py +++ b/test/functional/abc_p2p_avalanche_proof_voting.py @@ -93,6 +93,16 @@ # Conflicting proof with lower sequence number check_proof_is_polled(proofid_seq10, proof_seq10) + self.log.info( + "Check the node will poll for conflicting proofs received via p2p") + + proofid_seq40, proof_seq40 = get_proof_with_sequence_number(40) + peer = get_ava_p2p_interface(node) + peer.send_avaproof(proof_seq40) + + check_proof_is_polled(proofid_seq40, proof_seq40) + assert peer.is_connected + if __name__ == '__main__': AvalancheProofVotingTest().main() diff --git a/test/functional/test_framework/avatools.py b/test/functional/test_framework/avatools.py --- a/test/functional/test_framework/avatools.py +++ b/test/functional/test_framework/avatools.py @@ -23,6 +23,7 @@ hash256, msg_avahello, msg_avapoll, + msg_avaproof, msg_tcpavaresponse, ) from .p2p import P2PInterface, p2p_lock @@ -241,6 +242,15 @@ return delegation.proofid + def send_avaproof(self, proof_hex: str) -> int: + proof = FromHex(AvalancheProof(), proof_hex) + + msg = msg_avaproof() + msg.proof = proof + self.send_message(msg) + + return proof.proofid + def get_ava_p2p_interface( node: TestNode,