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) 2015-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 block processing."""6import copy7import time9from test_framework.blocktools import (10add_witness_commitment,11create_block,12create_coinbase,13create_tx_with_script,14get_legacy_sigopcount_block,15MAX_BLOCK_SIGOPS,16REGTEST_N_BITS,17)18from test_framework.messages import (19CBlock,20COIN,21COutPoint,22CTransaction,23CTxIn,24CTxOut,25MAX_BLOCK_WEIGHT,26SEQUENCE_FINAL,27uint256_from_compact,28uint256_from_str,29)30from test_framework.p2p import P2PDataStore31from test_framework.script import (32CScript,33MAX_SCRIPT_ELEMENT_SIZE,34MAX_SCRIPT_SIZE,35OP_2DUP,36OP_CHECKMULTISIG,37OP_CHECKMULTISIGVERIFY,38OP_CHECKSIG,39OP_CHECKSIGVERIFY,40OP_ELSE,41OP_ENDIF,42OP_DROP,43OP_FALSE,44OP_IF,45OP_INVALIDOPCODE,46OP_RETURN,47OP_TRUE,48sign_input_legacy,49)50from test_framework.script_util import (51key_to_p2pk_script,52script_to_p2sh_script,53)54from test_framework.test_framework import BitcoinTestFramework55from test_framework.util import (56assert_equal,57assert_greater_than,58assert_raises_rpc_error,59)60from test_framework.wallet_util import generate_keypair61from data import invalid_txs64# Use this class for tests that require behavior other than normal p2p behavior.65# For now, it is used to serialize a bloated varint (b64).66class CBrokenBlock(CBlock):67def initialize(self, base_block):68self.vtx = copy.deepcopy(base_block.vtx)69self.hashMerkleRoot = self.calc_merkle_root()71def serialize(self, with_witness=False):72r = b""73r += super(CBlock, self).serialize()74r += (255).to_bytes(1, "little") + len(self.vtx).to_bytes(8, "little")75for tx in self.vtx:76if with_witness:77r += tx.serialize_with_witness()78else:79r += tx.serialize_without_witness()80return r82def normal_serialize(self):83return super().serialize()86DUPLICATE_COINBASE_SCRIPT_SIG = b'\x01\x78' # Valid for block at height 12089class FullBlockTest(BitcoinTestFramework):90def set_test_params(self):91self.num_nodes = 192self.setup_clean_chain = True93self.extra_args = [[94'-testactivationheight=bip34@2',95# Override the functional-test default of 1 thread to exercise the multi-threaded96# prevout prefetching path in this block-heavy test.97'-prevoutfetchthreads=8',98]]100def add_options(self, parser):101parser.add_argument("--skipreorg", action='store_true', dest="skip_reorg", help="Skip the large re-org test", default=False)103def run_test(self):104node = self.nodes[0] # convenience reference to the node106self.bootstrap_p2p() # Add one p2p connection to the node108self.block_heights = {}109self.coinbase_key, self.coinbase_pubkey = generate_keypair()110self.tip = None111self.blocks = {}112self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)113self.block_heights[self.genesis_hash] = 0114self.spendable_outputs = []116# Create a new block117b_dup_cb = self.next_block('dup_cb')118b_dup_cb.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG119duplicate_tx = b_dup_cb.vtx[0]120b_dup_cb = self.update_block('dup_cb', [])121self.send_blocks([b_dup_cb])123# Add gigantic boundary scripts that respect all other limits124max_valid_script = CScript([b'\x01' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [b'\x01' * 62])125assert_equal(len(max_valid_script), MAX_SCRIPT_SIZE)126min_invalid_script = CScript([b'\x01' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [b'\x01' * 63])127assert_equal(len(min_invalid_script), MAX_SCRIPT_SIZE + 1)129b0 = self.next_block(0, additional_output_scripts=[max_valid_script, min_invalid_script])130self.save_spendable_output()131self.send_blocks([b0])133# Will test spending once possibly-mature134max_size_spendable_output = CTxIn(COutPoint(b0.vtx[0].txid_int, 1))135min_size_unspendable_output = CTxIn(COutPoint(b0.vtx[0].txid_int, 2))137# These constants chosen specifically to trigger an immature coinbase spend138# at a certain time below.139NUM_BUFFER_BLOCKS_TO_GENERATE = 99140NUM_OUTPUTS_TO_COLLECT = 33142# Allow the block to mature143blocks = []144for i in range(NUM_BUFFER_BLOCKS_TO_GENERATE):145blocks.append(self.next_block(f"maturitybuffer.{i}"))146self.save_spendable_output()147self.send_blocks(blocks)149# MAX_SCRIPT_SIZE testing now that coins are mature150tx = CTransaction()151tx.vin.append(max_size_spendable_output)152tx.vout.append(CTxOut(0, CScript([])))153block = self.generateblock(self.nodes[0], output="raw(55)", transactions=[tx.serialize().hex()])154assert_equal(block["hash"], self.nodes[0].getbestblockhash())155self.nodes[0].invalidateblock(block["hash"])156assert_equal(self.nodes[0].getrawmempool(), [])158# MAX_SCRIPT_SIZE + 1 wasn't added to the utxo set159tx.vin[0] = min_size_unspendable_output160assert_raises_rpc_error(-25, f'TestBlockValidity failed: bad-txns-inputs-missingorspent, CheckTxInputs: inputs missing/spent in transaction {tx.txid_hex}', self.generateblock, self.nodes[0], output="raw(55)", transactions=[tx.serialize().hex()])162# collect spendable outputs now to avoid cluttering the code later on163out = []164for _ in range(NUM_OUTPUTS_TO_COLLECT):165out.append(self.get_spendable_output())167# Start by building a couple of blocks on top (which output is spent is168# in parentheses):169# genesis -> b1 (0) -> b2 (1)170b1 = self.next_block(1, spend=out[0])171self.save_spendable_output()173b2 = self.next_block(2, spend=out[1])174self.save_spendable_output()176self.send_blocks([b1, b2], timeout=4)178# Select a txn with an output eligible for spending. This won't actually be spent,179# since we're testing submission of a series of blocks with invalid txns.180attempt_spend_tx = out[2]182# Submit blocks for rejection, each of which contains a single transaction183# (aside from coinbase) which should be considered invalid.184for TxTemplate in invalid_txs.iter_all_templates():185template = TxTemplate(spend_tx=attempt_spend_tx)187if template.valid_in_block:188continue190assert template.block_reject_reason or template.reject_reason192self.log.info(f"Reject block with invalid tx: {TxTemplate.__name__}")193blockname = f"for_invalid.{TxTemplate.__name__}"194self.next_block(blockname)195badtx = template.get_tx()196if TxTemplate != invalid_txs.InputMissing:197self.sign_tx(badtx, attempt_spend_tx)198badblock = self.update_block(blockname, [badtx])199reject_reason = (template.block_reject_reason or template.reject_reason)200if reject_reason.startswith("mempool-script-verify-flag-failed"):201reject_reason = "block-script-verify-flag-failed" + reject_reason[33:]202self.send_blocks(203[badblock], success=False,204reject_reason=reject_reason,205reconnect=True, timeout=2)207self.move_tip(2)209# Fork like this:210#211# genesis -> b1 (0) -> b2 (1)212# \-> b3 (1)213#214# Nothing should happen at this point. We saw b2 first so it takes priority.215self.log.info("Don't reorg to a chain of the same length")216self.move_tip(1)217b3 = self.next_block(3, spend=out[1])218txout_b3 = b3.vtx[1]219self.send_blocks([b3], False)221# Now we add another block to make the alternative chain longer.222#223# genesis -> b1 (0) -> b2 (1)224# \-> b3 (1) -> b4 (2)225self.log.info("Reorg to a longer chain")226b4 = self.next_block(4, spend=out[2])227self.send_blocks([b4])229# ... and back to the first chain.230# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)231# \-> b3 (1) -> b4 (2)232self.move_tip(2)233b5 = self.next_block(5, spend=out[2])234self.save_spendable_output()235self.send_blocks([b5], False)237self.log.info("Reorg back to the original chain")238b6 = self.next_block(6, spend=out[3])239self.send_blocks([b6], True)241# Try to create a fork that double-spends242# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)243# \-> b7 (2) -> b8 (4)244# \-> b3 (1) -> b4 (2)245self.log.info("Reject a chain with a double spend, even if it is longer")246self.move_tip(5)247b7 = self.next_block(7, spend=out[2])248self.send_blocks([b7], False)250b8 = self.next_block(8, spend=out[4])
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.