From f84962b3755576da717efde9b0ff8d836813a3d6 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Mon, 8 Jun 2026 18:56:11 -0500 Subject: [PATCH 1/3] fix: penalize oversized notfound messages --- src/net_processing.cpp | 33 ++++++++++++++++-------------- test/functional/p2p_tx_download.py | 9 +++++++- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 015408b9306a..3aabbcbdbdff 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -5464,24 +5464,27 @@ void PeerManagerImpl::ProcessMessage( } if (msg_type == NetMsgType::NOTFOUND) { // Remove the NOTFOUND transactions from the peer - LOCK(cs_main); - CNodeState *state = State(pfrom.GetId()); std::vector vInv; vRecv >> vInv; - if (vInv.size() <= MAX_PEER_OBJECT_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - for (CInv &inv : vInv) { - if (inv.IsKnownType()) { - // If we receive a NOTFOUND message for a txid we requested, erase - // it from our data structures for this peer. - auto in_flight_it = state->m_object_download.m_object_in_flight.find(inv); - if (in_flight_it == state->m_object_download.m_object_in_flight.end()) { - // Skip any further work if this is a spurious NOTFOUND - // message. - continue; - } - state->m_object_download.m_object_in_flight.erase(in_flight_it); - state->m_object_download.m_object_announced.erase(inv); + if (vInv.size() > MAX_PEER_OBJECT_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + Misbehaving(pfrom.GetId(), 10, strprintf("notfound message size = %u", vInv.size())); + return; + } + + LOCK(cs_main); + CNodeState *state = State(pfrom.GetId()); + for (CInv &inv : vInv) { + if (inv.IsKnownType()) { + // If we receive a NOTFOUND message for a txid we requested, erase + // it from our data structures for this peer. + auto in_flight_it = state->m_object_download.m_object_in_flight.find(inv); + if (in_flight_it == state->m_object_download.m_object_in_flight.end()) { + // Skip any further work if this is a spurious NOTFOUND + // message. + continue; } + state->m_object_download.m_object_in_flight.erase(in_flight_it); + state->m_object_download.m_object_announced.erase(inv); } } return; diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py index 957305f2299f..ff42e2ed3d0c 100755 --- a/test/functional/p2p_tx_download.py +++ b/test/functional/p2p_tx_download.py @@ -142,13 +142,20 @@ def test_spurious_notfound(self): self.log.info('Check that spurious notfound is ignored') self.nodes[0].p2ps[0].send_message(msg_notfound(vec=[CInv(1, 1)])) + def test_oversized_notfound(self): + self.log.info('Check that oversized notfound increases misbehavior score') + invs = [CInv(t=1, h=i) for i in range(MAX_GETDATA_IN_FLIGHT + 17)] + with self.nodes[0].assert_debug_log(["Misbehaving", "notfound message size = 117"]): + self.nodes[0].p2ps[0].send_message(msg_notfound(vec=invs)) + self.nodes[0].p2ps[0].sync_with_ping() + def run_test(self): self.wallet = MiniWallet(self.nodes[0]) self.wallet.rescan_utxos() # Run each test against new bitcoind instances, as setting mocktimes has long-term effects on when # the next trickle relay event happens. - for test in [self.test_spurious_notfound, self.test_in_flight_max, self.test_inv_block, self.test_tx_requests]: + for test in [self.test_spurious_notfound, self.test_oversized_notfound, self.test_in_flight_max, self.test_inv_block, self.test_tx_requests]: self.stop_nodes() self.start_nodes() self.connect_nodes(1, 0) From a43a27f068eab4a819a84c201a7d71c0d5b434e8 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Mon, 8 Jun 2026 22:38:11 -0500 Subject: [PATCH 2/3] test: avoid hardcoded oversized notfound count --- test/functional/p2p_tx_download.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py index ff42e2ed3d0c..7bd616841556 100755 --- a/test/functional/p2p_tx_download.py +++ b/test/functional/p2p_tx_download.py @@ -40,6 +40,8 @@ def on_getdata(self, message): MAX_GETDATA_RANDOM_DELAY = 2 # seconds INBOUND_PEER_TX_DELAY = 2 # seconds MAX_GETDATA_IN_FLIGHT = 100 +MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16 +MAX_NOTFOUND_SIZE = MAX_GETDATA_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER TX_EXPIRY_INTERVAL = GETDATA_TX_INTERVAL * 10 # Python test constants @@ -144,8 +146,9 @@ def test_spurious_notfound(self): def test_oversized_notfound(self): self.log.info('Check that oversized notfound increases misbehavior score') - invs = [CInv(t=1, h=i) for i in range(MAX_GETDATA_IN_FLIGHT + 17)] - with self.nodes[0].assert_debug_log(["Misbehaving", "notfound message size = 117"]): + oversized_notfound_count = MAX_NOTFOUND_SIZE + 1 + invs = [CInv(t=1, h=i) for i in range(oversized_notfound_count)] + with self.nodes[0].assert_debug_log(["Misbehaving", f"notfound message size = {oversized_notfound_count}"]): self.nodes[0].p2ps[0].send_message(msg_notfound(vec=invs)) self.nodes[0].p2ps[0].sync_with_ping() From a97b7b4526cc40cb107415c26bcd056083c6b423 Mon Sep 17 00:00:00 2001 From: PastaClaw Date: Mon, 15 Jun 2026 07:22:34 -0500 Subject: [PATCH 3/3] fix: align oversized notfound penalty --- src/net_processing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 3aabbcbdbdff..af63f399b2bd 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -5467,7 +5467,7 @@ void PeerManagerImpl::ProcessMessage( std::vector vInv; vRecv >> vInv; if (vInv.size() > MAX_PEER_OBJECT_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - Misbehaving(pfrom.GetId(), 10, strprintf("notfound message size = %u", vInv.size())); + Misbehaving(pfrom.GetId(), 20, strprintf("notfound message size = %u", vInv.size())); return; }