From 28641fd195db2a175fd43fee2e32758aef9816a6 Mon Sep 17 00:00:00 2001 From: furszy Date: Tue, 28 Jul 2026 10:56:27 -0400 Subject: [PATCH] p2p: reject empty getblocktxn requests A getblocktxn msg is only needed when at least one tx is missing from a compact block. If no txs are missing, the block can be reconstructed without sending the request. This avoids reading the requested block from disk unnecessarily and also alerts the peer operator about their node's buggy behavior. --- src/net_processing.cpp | 8 ++++++++ test/functional/p2p_compactblocks.py | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 7387f78b931..f5f81a65f56 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -4345,6 +4345,14 @@ void PeerManagerImpl::ProcessMessage(Peer& peer, CNode& pfrom, const std::string if (msg_type == NetMsgType::GETBLOCKTXN) { BlockTransactionsRequest req; vRecv >> req; + + // No legitimate reason to send indexes empty + if (req.indexes.empty()) { + LogDebug(BCLog::NET, "getblocktxn received with no transaction indexes, %s", pfrom.DisconnectMsg()); + pfrom.fDisconnect = true; + return; + } + // Verify differential encoding invariant: indexes must be strictly increasing // DifferenceFormatter should guarantee this property during deserialization for (size_t i = 1; i < req.indexes.size(); ++i) { diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py index 8db0fab75bd..7466b833ac2 100755 --- a/test/functional/p2p_compactblocks.py +++ b/test/functional/p2p_compactblocks.py @@ -843,6 +843,17 @@ class CompactBlocksTest(BitcoinTestFramework): msg = msg_cmpctblock(comp_block.to_p2p()) test_node.send_await_disconnect(msg) + def test_empty_getblocktxn_disconnects(self): + self.log.info("Testing empty getblocktxn disconnects the peer...") + node = self.nodes[0] + block_hash = int(node.getbestblockhash(), 16) + peer = node.add_p2p_connection(P2PInterface()) + msg = msg_getblocktxn() + msg.block_txn_request = BlockTransactionsRequest(blockhash=block_hash, indexes=[]) + with node.assert_debug_log(['getblocktxn received with no transaction indexes']): + peer.send_without_ping(msg) + peer.wait_for_disconnect() + # peer generates a block and sends it to node, which makes the peer a # candidate for high-bandwidth 'to' (up to 3 peers according to BIP 152) def make_peer_hb_to_candidate(self, node, peer): @@ -1097,6 +1108,7 @@ class CompactBlocksTest(BitcoinTestFramework): self.log.info("Testing handling of invalid compact blocks...") self.test_invalid_tx_in_compactblock(self.segwit_node) + self.test_empty_getblocktxn_disconnects() # The previous test will lead to a disconnection. Reconnect before continuing. self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn())