Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
501utxo = self.utxos.pop(0)503block = self.build_block_with_transactions(node, utxo, 5)504self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])505comp_block = HeaderAndShortIDs()506comp_block.initialize_from_block(block, use_witness=True)508test_getblocktxn_response(comp_block, test_node, [1, 2, 3, 4, 5])510msg_bt = msg_no_witness_blocktxn()511msg_bt = msg_blocktxn() # serialize with witnesses512msg_bt.block_transactions = BlockTransactions(block.hash_int, block.vtx[1:])513test_tip_after_message(node, test_node, msg_bt, block.hash_int)515utxo = self.utxos.pop(0)516block = self.build_block_with_transactions(node, utxo, 5)517self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])519# Now try interspersing the prefilled transactions520comp_block.initialize_from_block(block, prefill_list=[0, 1, 5], use_witness=True)521test_getblocktxn_response(comp_block, test_node, [2, 3, 4])522msg_bt.block_transactions = BlockTransactions(block.hash_int, block.vtx[2:5])523test_tip_after_message(node, test_node, msg_bt, block.hash_int)525# Now try giving one transaction ahead of time.526utxo = self.utxos.pop(0)527block = self.build_block_with_transactions(node, utxo, 5)528self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])529test_node.send_and_ping(msg_tx(block.vtx[1]))530assert block.vtx[1].txid_hex in node.getrawmempool()532# Prefill 4 out of the 6 transactions, and verify that only the one533# that was not in the mempool is requested.534comp_block.initialize_from_block(block, prefill_list=[0, 2, 3, 4], use_witness=True)535test_getblocktxn_response(comp_block, test_node, [5])537msg_bt.block_transactions = BlockTransactions(block.hash_int, [block.vtx[5]])538test_tip_after_message(node, test_node, msg_bt, block.hash_int)540# Now provide all transactions to the node before the block is541# announced and verify reconstruction happens immediately.542utxo = self.utxos.pop(0)543block = self.build_block_with_transactions(node, utxo, 10)544self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])545for tx in block.vtx[1:]:546test_node.send_without_ping(msg_tx(tx))547test_node.sync_with_ping()548# Make sure all transactions were accepted.549mempool = node.getrawmempool()550for tx in block.vtx[1:]:551assert tx.txid_hex in mempool553# Clear out last request.554test_node.clear_getblocktxn()556# Send compact block557comp_block.initialize_from_block(block, prefill_list=[0], use_witness=True)558test_tip_after_message(node, test_node, msg_cmpctblock(comp_block.to_p2p()), block.hash_int)559with p2p_lock:560# Shouldn't have gotten a request for any transaction561assert "getblocktxn" not in test_node.last_message563# Incorrectly responding to a getblocktxn shouldn't cause the block to be564# permanently failed.565def test_incorrect_blocktxn_response(self, test_node):566node = self.nodes[0]567utxo = self.utxos.pop(0)569block = self.build_block_with_transactions(node, utxo, 10)570self.utxos.append([block.vtx[-1].txid_int, 0, block.vtx[-1].vout[0].nValue])571# Relay the first 5 transactions from the block in advance572for tx in block.vtx[1:6]:573test_node.send_without_ping(msg_tx(tx))574test_node.sync_with_ping()575# Make sure all transactions were accepted.576mempool = node.getrawmempool()577for tx in block.vtx[1:6]:578assert tx.txid_hex in mempool580# Send compact block581comp_block = HeaderAndShortIDs()582comp_block.initialize_from_block(block, prefill_list=[0], use_witness=True)583test_node.clear_getblocktxn()584test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))585expected_indices = [6, 7, 8, 9, 10]586self.getblocktxn_expected(test_node, block.hash_int, expected_indices)588# Now give an incorrect response.589# Note that it's possible for bitcoind to be smart enough to know we're590# lying, since it could check to see if the shortid matches what we're591# sending, and eg disconnect us for misbehavior. If that behavior592# change was made, we could just modify this test by having a593# different peer provide the block further down, so that we're still594# verifying that the block isn't marked bad permanently. This is good595# enough for now.596msg = msg_blocktxn()597msg.block_transactions = BlockTransactions(block.hash_int, [block.vtx[5]] + block.vtx[7:])598test_node.send_and_ping(msg)600# Tip should not have updated601assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)603# We should receive a getdata request604test_node.wait_for_getdata([block.hash_int], timeout=10)605assert test_node.last_message["getdata"].inv[0].type in (MSG_BLOCK, MSG_BLOCK | MSG_WITNESS_FLAG)607# Deliver the block608test_node.send_and_ping(msg_block(block))609assert_equal(node.getbestblockhash(), block.hash_hex)611# Multiple blocktxn responses will cause a node to get disconnected.612def test_multiple_blocktxn_response(self, test_node):613node = self.nodes[0]614utxo = self.utxos.pop(0)616block = self.build_block_with_transactions(node, utxo, 2)618# The attacker sends the block header so that we request it.619test_node.send_without_ping(msg_headers([block]))620test_node.wait_for_getdata([block.hash_int], timeout=30)622# Send compact block623comp_block = HeaderAndShortIDs()624comp_block.initialize_from_block(block, prefill_list=[0], use_witness=True)625test_node.clear_getblocktxn()626test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))627self.getblocktxn_expected(test_node, block.hash_int, indices=[1,2])629# Send a blocktxn that does not succeed in reconstruction, triggering630# getdata fallback.631msg = msg_blocktxn()632msg.block_transactions = BlockTransactions(block.hash_int, [block.vtx[2]] + [block.vtx[1]])633test_node.send_and_ping(msg)635# Tip should not have updated636assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)638# We should receive a getdata request639test_node.wait_for_getdata([block.hash_int], timeout=10)640assert test_node.last_message["getdata"].inv[0].type in (MSG_BLOCK, MSG_BLOCK | MSG_WITNESS_FLAG)642# Send the same blocktxn and assert the sender gets disconnected.643with node.assert_debug_log(['previous compact block reconstruction attempt failed']):644test_node.send_without_ping(msg)645test_node.wait_for_disconnect()647def test_getblocktxn_handler(self, test_node):648node = self.nodes[0]649# bitcoind will not send blocktxn responses for blocks whose height is650# more than 10 blocks deep.651MAX_GETBLOCKTXN_DEPTH = 10652chain_height = node.getblockcount()653current_height = chain_height654while (current_height >= chain_height - MAX_GETBLOCKTXN_DEPTH):655block_hash = node.getblockhash(current_height)656block = from_hex(CBlock(), node.getblock(block_hash, False))658msg = msg_getblocktxn()659msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [])660num_to_request = random.randint(1, len(block.vtx))661msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request)))662test_node.send_without_ping(msg)663test_node.wait_until(lambda: "blocktxn" in test_node.last_message, timeout=10)665with p2p_lock:666assert_equal(test_node.last_message["blocktxn"].block_transactions.blockhash, int(block_hash, 16))667all_indices = msg.block_txn_request.to_absolute()668for index in all_indices:669tx = test_node.last_message["blocktxn"].block_transactions.transactions.pop(0)670assert_equal(tx.txid_hex, block.vtx[index].txid_hex)671# Check that the witness matches672assert_equal(tx.wtxid_hex, block.vtx[index].wtxid_hex)673test_node.last_message.pop("blocktxn", None)674current_height -= 1676# Next request should send a full block response, as we're past the677# allowed depth for a blocktxn response.678block_hash = node.getblockhash(current_height)679msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0])680with p2p_lock:681test_node.last_message.pop("block", None)682test_node.last_message.pop("blocktxn", None)683test_node.send_and_ping(msg)684with p2p_lock:685assert_equal(test_node.last_message["block"].block.hash_hex, block_hash)686assert "blocktxn" not in test_node.last_message688# Request with out-of-bounds tx index results in disconnect689bad_peer = self.nodes[0].add_p2p_connection(TestP2PConn())690block_hash = node.getblockhash(chain_height)691block = from_hex(CBlock(), node.getblock(block_hash, False))692msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [len(block.vtx)])693with node.assert_debug_log(['getblocktxn with out-of-bounds tx indices']):694bad_peer.send_without_ping(msg)695bad_peer.wait_for_disconnect()697def test_low_work_compactblocks(self, test_node):698# A compactblock with insufficient work won't get its header included699node = self.nodes[0]700hashPrevBlock = int(node.getblockhash(node.getblockcount() - 150), 16)701block = self.build_block_on_tip(node)702block.hashPrevBlock = hashPrevBlock703block.solve()705comp_block = HeaderAndShortIDs()706comp_block.initialize_from_block(block)707with self.nodes[0].assert_debug_log(['[net] Ignoring low-work compact block from peer 0']):708test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))710tips = node.getchaintips()711found = False712for x in tips:713if x["hash"] == block.hash_hex:714found = True715break716assert not found718def test_compactblocks_not_at_tip(self, test_node):719node = self.nodes[0]720# Test that requesting old compactblocks doesn't work.721MAX_CMPCTBLOCK_DEPTH = 5722new_blocks = []723for _ in range(MAX_CMPCTBLOCK_DEPTH + 1):724test_node.clear_block_announcement()725new_blocks.append(self.generate(node, 1)[0])726test_node.wait_until(test_node.received_block_announcement, timeout=30)728test_node.clear_block_announcement()729test_node.send_without_ping(msg_getdata([CInv(MSG_CMPCT_BLOCK, int(new_blocks[0], 16))]))730test_node.wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30)732test_node.clear_block_announcement()733self.generate(node, 1)734test_node.wait_until(test_node.received_block_announcement, timeout=30)735test_node.clear_block_announcement()736with p2p_lock:737test_node.last_message.pop("block", None)738test_node.send_without_ping(msg_getdata([CInv(MSG_CMPCT_BLOCK, int(new_blocks[0], 16))]))739test_node.wait_until(lambda: "block" in test_node.last_message, timeout=30)740with p2p_lock:741assert_equal(test_node.last_message["block"].block.hash_hex, new_blocks[0])743# Generate an old compactblock, and verify that it's not accepted.744cur_height = node.getblockcount()745hashPrevBlock = int(node.getblockhash(cur_height - 5), 16)746block = self.build_block_on_tip(node)747block.hashPrevBlock = hashPrevBlock748block.solve()750comp_block = HeaderAndShortIDs()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.