Skip to content

Commit 53a5bd0

Browse files
achow101Tom Trevethan
authored andcommitted
Merge bitcoin/bitcoin#33296: net: check for empty header before calling FillBlock
8b6264768030db1840041abeeaeefd6c227a2644 test: send duplicate blocktxn message in p2p_compactblocks.py (Eugene Siegel) 5e585a0fc4fd68dd7b4982054b34deae2e7aeb89 net: check for empty header before calling FillBlock (Eugene Siegel) Pull request description: This avoids an Assume crash if multiple blocktxn messages are received. The first call to `FillBlock` would make the header empty via `SetNull` and the call right before the second `FillBlock` would crash [here](https://github.com/bitcoin/bitcoin/blob/689a32197638e92995dd8eb071425715f5fdc3a4/src/net_processing.cpp#L3333) since `LookupBlockIndex` won't find anything. Fix that by checking for an empty header before the Assume. ACKs for top commit: instagibbs: reACK bitcoin/bitcoin@8b62647 fjahr: tACK 8b6264768030db1840041abeeaeefd6c227a2644 achow101: ACK 8b6264768030db1840041abeeaeefd6c227a2644 mzumsande: Code Review ACK 8b6264768030db1840041abeeaeefd6c227a2644 Tree-SHA512: d43a6f652161d4f7e6137f207a3e95259fc51509279d20347b1698c91179c39c8fcb75d2668b13a6b220f478a03578573208a415804be1d8843acb057fa1a73a
1 parent 98bd718 commit 53a5bd0

File tree

2 files changed

+56
-2
lines changed

2 files changed

+56
-2
lines changed

src/net_processing.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3810,13 +3810,27 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
38103810
}
38113811

38123812
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
3813+
3814+
if (partialBlock.header.IsNull()) {
3815+
// It is possible for the header to be empty if a previous call to FillBlock wiped the header, but left
3816+
// the PartiallyDownloadedBlock pointer around (i.e. did not call RemoveBlockRequest). In this case, we
3817+
// should not call LookupBlockIndex below.
3818+
RemoveBlockRequest(resp.blockhash, pfrom.GetId());
3819+
Misbehaving(pfrom.GetId(), 100, "previous compact block reconstruction attempt failed");
3820+
LogPrint(BCLog::NET, "Peer %d sent compact block transactions multiple times", pfrom.GetId());
3821+
return;
3822+
}
3823+
38133824
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
38143825
if (status == READ_STATUS_INVALID) {
38153826
RemoveBlockRequest(resp.blockhash, pfrom.GetId()); // Reset in-flight state in case Misbehaving does not result in a disconnect
38163827
Misbehaving(pfrom.GetId(), 100, "invalid compact block/non-matching block transactions");
38173828
return;
38183829
} else if (status == READ_STATUS_FAILED) {
38193830
// Might have collided, fall back to getdata now :(
3831+
// We keep the failed partialBlock to disallow processing another compact block announcement from the same
3832+
// peer for the same block. We let the full block download below continue under the same m_downloading_since
3833+
// timer.
38203834
std::vector<CInv> invs;
38213835
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash));
38223836
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));

test/functional/p2p_compactblocks.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ def test_invalid_cmpctblock_message(self):
285285
# This index will be too high
286286
prefilled_txn = PrefilledTransaction(1, block.vtx[0])
287287
cmpct_block.prefilled_txn = [prefilled_txn]
288-
self.segwit_node.send_await_disconnect(msg_cmpctblock(cmpct_block))
288+
self.additional_segwit_node.send_await_disconnect(msg_cmpctblock(cmpct_block))
289289
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock)
290290

291291
# Compare the generated shortids to what we expect based on BIP 152, given
@@ -603,6 +603,42 @@ def test_incorrect_blocktxn_response(self, test_node):
603603
test_node.send_and_ping(msg_no_witness_block(block))
604604
assert_equal(int(node.getbestblockhash(), 16), block.sha256)
605605

606+
# Multiple blocktxn responses will cause a node to get disconnected.
607+
def test_multiple_blocktxn_response(self, test_node):
608+
node = self.nodes[0]
609+
utxo = self.utxos[0]
610+
611+
block = self.build_block_with_transactions(node, utxo, 2)
612+
613+
# Send compact block
614+
comp_block = HeaderAndShortIDs()
615+
comp_block.initialize_from_block(block, prefill_list=[0], use_witness=True)
616+
test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
617+
absolute_indexes = []
618+
with p2p_lock:
619+
assert "getblocktxn" in test_node.last_message
620+
absolute_indexes = test_node.last_message["getblocktxn"].block_txn_request.to_absolute()
621+
assert_equal(absolute_indexes, [1, 2])
622+
623+
# Send a blocktxn that does not succeed in reconstruction, triggering
624+
# getdata fallback.
625+
msg = msg_blocktxn()
626+
msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[2]] + [block.vtx[1]])
627+
test_node.send_and_ping(msg)
628+
629+
# Tip should not have updated
630+
assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
631+
632+
# We should receive a getdata request
633+
test_node.wait_for_getdata([block.sha256], timeout=10)
634+
assert test_node.last_message["getdata"].inv[0].type == MSG_BLOCK or \
635+
test_node.last_message["getdata"].inv[0].type == MSG_BLOCK | MSG_WITNESS_FLAG
636+
637+
# Send the same blocktxn and assert the sender gets disconnected.
638+
with node.assert_debug_log(['previous compact block reconstruction attempt failed']):
639+
test_node.send_message(msg)
640+
test_node.wait_for_disconnect()
641+
606642
def test_getblocktxn_handler(self, test_node):
607643
version = test_node.cmpct_version
608644
node = self.nodes[0]
@@ -897,12 +933,16 @@ def run_test(self):
897933
self.test_invalid_tx_in_compactblock(self.segwit_node)
898934
self.test_invalid_tx_in_compactblock(self.old_node)
899935

936+
self.log.info("Testing handling of multiple blocktxn responses...")
937+
self.test_multiple_blocktxn_response(self.segwit_node)
938+
939+
self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=2))
940+
900941
self.log.info("Testing invalid index in cmpctblock message...")
901942
self.test_invalid_cmpctblock_message()
902943

903944
self.log.info("Testing high-bandwidth mode states via getpeerinfo...")
904945
self.test_highbandwidth_mode_states_via_getpeerinfo()
905946

906-
907947
if __name__ == '__main__':
908948
CompactBlocksTest().main()

0 commit comments

Comments
 (0)