diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -499,6 +499,8 @@ .Make(NetMsgType::AVAHELLO, Hello(peerData->delegation, sig))); + pfrom->AddKnownProof(peerData->delegation.getProofId()); + return true; } diff --git a/src/net.h b/src/net.h --- a/src/net.h +++ b/src/net.h @@ -998,6 +998,9 @@ struct ProofRelay { mutable RecursiveMutex cs_proof_inventory; + // Prevent sending proof invs if the peer already knows about them + CRollingBloomFilter filterProofKnown GUARDED_BY(cs_proof_inventory){ + 10000, 0.000001}; std::set setInventoryProofToSend GUARDED_BY(cs_proof_inventory); }; @@ -1170,13 +1173,22 @@ } } + void AddKnownProof(const avalanche::ProofId &proofid) { + if (m_proof_relay != nullptr) { + WITH_LOCK(m_proof_relay->cs_proof_inventory, + m_proof_relay->filterProofKnown.insert(proofid)); + } + } + void PushProofInventory(const avalanche::ProofId &proofid) { if (m_proof_relay == nullptr) { m_proof_relay = std::make_unique(); } - WITH_LOCK(m_proof_relay->cs_proof_inventory, - m_proof_relay->setInventoryProofToSend.insert(proofid)); + LOCK(m_proof_relay->cs_proof_inventory); + if (!m_proof_relay->filterProofKnown.contains(proofid)) { + m_proof_relay->setInventoryProofToSend.insert(proofid); + } } void CloseSocketDisconnect(); diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2985,15 +2985,14 @@ if (g_avalanche && gArgs.GetBoolArg("-enableavalanche", AVALANCHE_DEFAULT_ENABLED)) { - for (const avalanche::Peer &peer : g_avalanche->getPeers()) { - pfrom.PushProofInventory(peer.proof.getId()); - } - if ((pfrom.nServices & NODE_AVALANCHE) && g_avalanche->sendHello(&pfrom)) { LogPrint(BCLog::NET, "Send avahello to peer %d\n", pfrom.GetId()); } + for (const avalanche::Peer &peer : g_avalanche->getPeers()) { + pfrom.PushProofInventory(peer.proof.getId()); + } } pfrom.fSuccessfullyConnected = true; @@ -5377,6 +5376,7 @@ while (it != pto->m_proof_relay->setInventoryProofToSend.end()) { addInvAndMaybeFlush(MSG_AVA_PROOF, *it); + pto->AddKnownProof(*it); it = pto->m_proof_relay->setInventoryProofToSend.erase(it); } } diff --git a/test/functional/abc_p2p_proof_inventory.py b/test/functional/abc_p2p_proof_inventory.py --- a/test/functional/abc_p2p_proof_inventory.py +++ b/test/functional/abc_p2p_proof_inventory.py @@ -23,20 +23,26 @@ ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( + assert_equal, + assert_greater_than_or_equal, connect_nodes, wait_until, ) +import time + class ProofInvStoreP2PInterface(P2PInterface): def __init__(self): super().__init__() self.proof_invs = set() + self.proof_invs_counter = 0 def on_inv(self, message): for i in message.inv: if i.type & MSG_TYPE_MASK == MSG_AVA_PROOF: self.proof_invs.add(i.hash) + self.proof_invs_counter += 1 class ProofInventoryTest(BitcoinTestFramework): @@ -113,6 +119,29 @@ wait_until(lambda: set(get_proof_ids(node)) == peer.proof_invs) + self.log.info("Test that we don't send the same inv several times") + + _, spam_proof = self.gen_proof(node) + node.sendavalancheproof(spam_proof.serialize().hex()) + assert spam_proof.proofid in get_proof_ids(node) + + wait_until(lambda: set(get_proof_ids(node)) == peer.proof_invs) + + initial_num_proof_invs = peer.proof_invs_counter + assert_greater_than_or_equal( + initial_num_proof_invs, len( + peer.proof_invs)) + + # From now we will stop broadcasting this proof to the same peer + for i in range(10): + node.sendavalancheproof(spam_proof.serialize().hex()) + + # Force the node to run the send loop by sending it some message, and + # give enough time for our peer to receive the possible invs + peer.sync_with_ping() + time.sleep(2) + assert_equal(peer.proof_invs_counter, initial_num_proof_invs) + def run_test(self): self.test_send_proof_inv()