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.
1#!/usr/bin/env python32# Copyright (c) 2016-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Test compact blocks (BIP 152)."""6import random8from test_framework.blocktools import (9COINBASE_MATURITY,10NORMAL_GBT_REQUEST_PARAMS,11add_witness_commitment,12create_block,13)14from test_framework.messages import (15BlockTransactions,16BlockTransactionsRequest,17CBlock,18CBlockHeader,19CInv,20COutPoint,21CTransaction,22CTxIn,23CTxInWitness,24CTxOut,25from_hex,26HeaderAndShortIDs,27MSG_BLOCK,28MSG_CMPCT_BLOCK,29MSG_WITNESS_FLAG,30P2PHeaderAndShortIDs,31PrefilledTransaction,32calculate_shortid,33msg_block,34msg_blocktxn,35msg_cmpctblock,36msg_getblocktxn,37msg_getdata,38msg_getheaders,39msg_headers,40msg_inv,41msg_no_witness_block,42msg_no_witness_blocktxn,43msg_sendcmpct,44msg_sendheaders,45msg_tx,46ser_uint256,47tx_from_hex,48)49from test_framework.p2p import (50P2PInterface,51p2p_lock,52)53from test_framework.script import (54CScript,55OP_DROP,56OP_TRUE,57OP_RETURN,58)59from test_framework.test_framework import BitcoinTestFramework60from test_framework.test_node import TestNode61from test_framework.util import (62assert_not_equal,63assert_equal,64softfork_active,65)66from test_framework.wallet import MiniWallet69# TestP2PConn: A peer we use to send messages to bitcoind, and store responses.70class TestP2PConn(P2PInterface):71def __init__(self):72super().__init__()73self.last_sendcmpct = []74self.block_announced = False75# Store the hashes of blocks we've seen announced.76# This is for synchronizing the p2p message traffic,77# so we can eg wait until a particular block is announced.78self.announced_blockhashes = set()80def on_sendcmpct(self, message):81self.last_sendcmpct.append(message)83def on_cmpctblock(self, message):84self.block_announced = True85self.announced_blockhashes.add(self.last_message["cmpctblock"].header_and_shortids.header.hash_int)87def on_headers(self, message):88self.block_announced = True89for x in self.last_message["headers"].headers:90self.announced_blockhashes.add(x.hash_int)92def on_inv(self, message):93for x in self.last_message["inv"].inv:94if x.type == MSG_BLOCK:95self.block_announced = True96self.announced_blockhashes.add(x.hash)98# Requires caller to hold p2p_lock99def received_block_announcement(self):100return self.block_announced102def clear_block_announcement(self):103with p2p_lock:104self.block_announced = False105self.last_message.pop("inv", None)106self.last_message.pop("headers", None)107self.last_message.pop("cmpctblock", None)109def clear_getblocktxn(self):110with p2p_lock:111self.last_message.pop("getblocktxn", None)113def get_headers(self, locator, hashstop):114msg = msg_getheaders()115msg.locator.vHave = locator116msg.hashstop = hashstop117self.send_without_ping(msg)119def send_header_for_blocks(self, new_blocks):120headers_message = msg_headers()121headers_message.headers = [CBlockHeader(b) for b in new_blocks]122self.send_without_ping(headers_message)124def request_headers_and_sync(self, locator, hashstop=0):125self.clear_block_announcement()126self.get_headers(locator, hashstop)127self.wait_until(self.received_block_announcement, timeout=30)128self.clear_block_announcement()130# Block until a block announcement for a particular block hash is131# received.132def wait_for_block_announcement(self, block_hash, timeout=30):133def received_hash():134return (block_hash in self.announced_blockhashes)135self.wait_until(received_hash, timeout=timeout)137def send_await_disconnect(self, message, timeout=30):138"""Sends a message to the node and wait for disconnect.140This is used when we want to send a message into the node that we expect141will get us disconnected, eg an invalid block."""142self.send_without_ping(message)143self.wait_for_disconnect(timeout=timeout)145class CompactBlocksTest(BitcoinTestFramework):146def set_test_params(self):147self.setup_clean_chain = True148self.num_nodes = 1149self.extra_args = [[150"-acceptnonstdtxn=1",151]]152self.utxos = []154def getblocktxn_expected(self, peer, blockhash, indices=None):155with p2p_lock:156assert "getblocktxn" in peer.last_message157gbt = peer.last_message["getblocktxn"].block_txn_request159assert_equal(gbt.blockhash, blockhash)160if indices is not None:161assert_equal(gbt.to_absolute(), indices)162if isinstance(peer, TestNode):163assert_not_equal(peer.getbestblockhash(), blockhash)165def build_block_on_tip(self, node):166block = create_block(tmpl=node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS))167block.solve()168return block170# Create 12 more anyone-can-spend utxo's for testing.171def make_utxos(self):172COUNT = 12173block = self.build_block_on_tip(self.nodes[0])174self.segwit_node.send_and_ping(msg_no_witness_block(block))175assert_equal(self.nodes[0].getbestblockhash(), block.hash_hex)176self.generate(self.wallet, COINBASE_MATURITY)178total_value = block.vtx[0].vout[0].nValue179out_value = total_value // COUNT180tx = CTransaction()181tx.vin.append(CTxIn(COutPoint(block.vtx[0].txid_int, 0), b''))182for _ in range(COUNT):183tx.vout.append(CTxOut(out_value, CScript([OP_TRUE])))185block2 = self.build_block_on_tip(self.nodes[0])186block2.vtx.append(tx)187block2.hashMerkleRoot = block2.calc_merkle_root()188block2.solve()189self.segwit_node.send_and_ping(msg_no_witness_block(block2))190assert_equal(self.nodes[0].getbestblockhash(), block2.hash_hex)191self.utxos.extend([[tx.txid_int, i, out_value] for i in range(COUNT)])193def announce_cmpct_block(self, node, peer, txn_count=5, solicit=False):194utxo = self.utxos.pop(0)195block = self.build_block_with_transactions(node, utxo, txn_count)197cmpct_block = HeaderAndShortIDs()198cmpct_block.initialize_from_block(block)199msg = msg_cmpctblock(cmpct_block.to_p2p())200if solicit:201peer.send_without_ping(msg_headers([block]))202peer.wait_for_getdata([block.hash_int], timeout=30)204peer.clear_getblocktxn()205peer.send_and_ping(msg)206self.getblocktxn_expected(peer, block.hash_int)207return block, cmpct_block209# Test "sendcmpct" (between peers preferring the same version):210# - No compact block announcements unless sendcmpct is sent.211# - If sendcmpct is sent with version = 1, the message is ignored.212# - If sendcmpct is sent with version > 2, the message is ignored.213# - If sendcmpct is sent with boolean 0, then block announcements are not214# made with compact blocks.215# - If sendcmpct is then sent with boolean 1, then new block announcements216# are made with compact blocks.217def test_sendcmpct(self, test_node):218node = self.nodes[0]220# Make sure we get a SENDCMPCT message from our peer221def received_sendcmpct():222return (len(test_node.last_sendcmpct) > 0)223test_node.wait_until(received_sendcmpct, timeout=30)224with p2p_lock:225# Check that version 2 is received.226assert_equal(test_node.last_sendcmpct[0].version, 2)227test_node.last_sendcmpct = []229tip = int(node.getbestblockhash(), 16)231def check_announcement_of_new_block(node, peer, predicate):232peer.clear_block_announcement()233block_hash = int(self.generate(node, 1)[0], 16)234peer.wait_for_block_announcement(block_hash, timeout=30)235assert peer.block_announced237with p2p_lock:238assert predicate(peer), (239"block_hash={!r}, cmpctblock={!r}, inv={!r}".format(240block_hash, peer.last_message.get("cmpctblock", None), peer.last_message.get("inv", None)))242# We shouldn't get any block announcements via cmpctblock yet.243check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)245# Try one more time, this time after requesting headers.246test_node.request_headers_and_sync(locator=[tip])247check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message and "inv" in p.last_message)249# Test a few ways of using sendcmpct that should NOT250# result in compact block announcements.
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.