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.
1251b84 = self.update_block(84, [tx1, tx2, tx3, tx4, tx5])1252self.send_blocks([b84], True)1253self.save_spendable_output()1255self.move_tip(83)1256b85 = self.next_block(85, spend=out[29])1257self.send_blocks([b85], False) # other chain is same length1259b86 = self.next_block(86, spend=out[30])1260self.send_blocks([b86], True)1262self.move_tip(84)1263b87 = self.next_block(87, spend=out[30])1264self.send_blocks([b87], False) # other chain is same length1265self.save_spendable_output()1267b88 = self.next_block(88, spend=out[31])1268self.send_blocks([b88], True)1269self.save_spendable_output()1271# trying to spend the OP_RETURN output is rejected1272self.next_block("89a", spend=out[32])1273tx = self.create_tx(tx1, 0, 0, CScript([OP_TRUE]))1274b89a = self.update_block("89a", [tx])1275self.send_blocks([b89a], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)1277self.move_tip(88)1278self.log.info("Reject a block with an invalid block header version")1279b_v1 = self.next_block('b_v1', version=1)1280self.send_blocks([b_v1], success=False, force_send=True, reject_reason='bad-version(0x00000001)', reconnect=True)1282self.move_tip(87)1283b_cb34 = self.next_block('b_cb34')1284b_cb34.vtx[0].vin[0].scriptSig = b_cb34.vtx[0].vin[0].scriptSig[:-1]1285b_cb34.hashMerkleRoot = b_cb34.calc_merkle_root()1286b_cb34.solve()1287self.send_blocks([b_cb34], success=False, reject_reason='bad-cb-height', reconnect=True)1289# Don't use v2transport for the large reorg, which is too slow with the unoptimized python ChaCha20 implementation1290if self.options.v2transport:1291self.nodes[0].disconnect_p2ps()1292self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore(), supports_v2_p2p=False)1294self.move_tip(88)1295if not self.options.skip_reorg:1296self.log.info("Test a re-org of one week's worth of blocks (1088 blocks)")1297LARGE_REORG_SIZE = 10881298blocks = []1299spend = out[32]1300for i in range(89, LARGE_REORG_SIZE + 89):1301b = self.next_block(i, spend)1302tx = CTransaction()1303script_length = (MAX_BLOCK_WEIGHT - b.get_weight() - 276) // 41304script_output = CScript([b'\x00' * script_length])1305tx.vout.append(CTxOut(0, script_output))1306tx.vin.append(CTxIn(COutPoint(b.vtx[1].txid_int, 0)))1307b = self.update_block(i, [tx])1308assert_equal(b.get_weight(), MAX_BLOCK_WEIGHT)1309blocks.append(b)1310self.save_spendable_output()1311spend = self.get_spendable_output()1313self.send_blocks(blocks, True, timeout=2440)1314chain1_tip = i1316# now create alt chain of same length1317self.move_tip(88)1318blocks2 = []1319for i in range(89, LARGE_REORG_SIZE + 89):1320blocks2.append(self.next_block("alt" + str(i)))1321self.send_blocks(blocks2, False, force_send=False)1323# extend alt chain to trigger re-org1324block = self.next_block("alt" + str(chain1_tip + 1))1325self.send_blocks([block], True, timeout=2440)1327# ... and re-org back to the first chain1328self.move_tip(chain1_tip)1329block = self.next_block(chain1_tip + 1)1330self.send_blocks([block], False, force_send=True)1331block = self.next_block(chain1_tip + 2)1332self.send_blocks([block], True, timeout=2440)1334# Helper methods1335################1337def add_transactions_to_block(self, block, tx_list):1338block.vtx.extend(tx_list)1340# this is a little handier to use than the version in blocktools.py1341def create_tx(self, spend_tx, n, value, output_script=None):1342if output_script is None:1343output_script = CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])1344return create_tx_with_script(spend_tx, n, amount=value, output_script=output_script)1346# sign a transaction, using the key we know about1347# this signs input 0 in tx, which is assumed to be spending output 0 in spend_tx1348def sign_tx(self, tx, spend_tx):1349scriptPubKey = bytearray(spend_tx.vout[0].scriptPubKey)1350if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend1351tx.vin[0].scriptSig = CScript()1352return1353sign_input_legacy(tx, 0, spend_tx.vout[0].scriptPubKey, self.coinbase_key)1355def create_and_sign_transaction(self, spend_tx, value, output_script=None):1356if output_script is None:1357output_script = CScript([OP_TRUE])1358tx = self.create_tx(spend_tx, 0, value, output_script=output_script)1359self.sign_tx(tx, spend_tx)1360return tx1362def next_block(self, number, spend=None, additional_coinbase_value=0, *, script=None, version=4, additional_output_scripts=None):1363if script is None:1364script = CScript([OP_TRUE])1365if additional_output_scripts is None:1366additional_output_scripts = []1367if self.tip is None:1368base_block_hash = self.genesis_hash1369block_time = int(time.time()) + 11370else:1371base_block_hash = self.tip.hash_int1372block_time = self.tip.nTime + 11373# First create the coinbase1374height = self.block_heights[base_block_hash] + 11375coinbase = create_coinbase(height, self.coinbase_pubkey)1376coinbase.vout[0].nValue += additional_coinbase_value1377for additional_script in additional_output_scripts:1378coinbase.vout.append(CTxOut(0, additional_script))1379if spend is None:1380block = create_block(base_block_hash, coinbase, ntime=block_time, version=version)1381else:1382coinbase.vout[0].nValue += spend.vout[0].nValue - 1 # all but one satoshi to fees1383tx = self.create_tx(spend, 0, 1, output_script=script) # spend 1 satoshi1384self.sign_tx(tx, spend)1385block = create_block(base_block_hash, coinbase, ntime=block_time, version=version, txlist=[tx])1386# Block is created. Find a valid nonce.1387block.solve()1388self.tip = block1389self.block_heights[block.hash_int] = height1390assert number not in self.blocks1391self.blocks[number] = block1392return block1394# save the current tip so it can be spent by a later block1395def save_spendable_output(self):1396self.log.debug(f"saving spendable output {self.tip.vtx[0]}")1397self.spendable_outputs.append(self.tip)1399# get an output that we previously marked as spendable1400def get_spendable_output(self):1401self.log.debug(f"getting spendable output {self.spendable_outputs[0].vtx[0]}")1402return self.spendable_outputs.pop(0).vtx[0]1404# move the tip back to a previous block1405def move_tip(self, number):1406self.tip = self.blocks[number]1408# adds transactions to the block and updates state1409def update_block(self, block_number, new_transactions, *, nTime=None):1410block = self.blocks[block_number]1411self.add_transactions_to_block(block, new_transactions)1412old_hash_int = block.hash_int1413if nTime is not None:1414block.nTime = nTime1415block.hashMerkleRoot = block.calc_merkle_root()1416has_witness_tx = any(not tx.wit.is_null() for tx in block.vtx)1417if has_witness_tx:1418add_witness_commitment(block)1419block.solve()1420# Update the internal state just like in next_block1421self.tip = block1422if block.hash_int != old_hash_int:1423self.block_heights[block.hash_int] = self.block_heights[old_hash_int]1424del self.block_heights[old_hash_int]1425self.blocks[block_number] = block1426return block1428def bootstrap_p2p(self, timeout=10):1429"""Add a P2P connection to the node.1431Helper to connect and wait for version handshake."""1432self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())1433# We need to wait for the initial getheaders from the peer before we1434# start populating our blockstore. If we don't, then we may run ahead1435# to the next subtest before we receive the getheaders. We'd then send1436# an INV for the next block and receive two getheaders - one for the1437# IBD and one for the INV. We'd respond to both and could get1438# unexpectedly disconnected if the DoS score for that error is 50.1439self.helper_peer.wait_for_getheaders(timeout=timeout)1441def reconnect_p2p(self, timeout=60):1442"""Tear down and bootstrap the P2P connection to the node.1444The node gets disconnected several times in this test. This helper1445method reconnects the p2p and restarts the network thread."""1446self.nodes[0].disconnect_p2ps()1447self.bootstrap_p2p(timeout=timeout)1449def send_blocks(self, blocks, success=True, reject_reason=None, force_send=False, reconnect=False, timeout=960):1450"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.1452Call with success = False if the tip shouldn't advance to the most recent block."""1453self.helper_peer.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect)1455if reconnect:1456self.reconnect_p2p(timeout=timeout)1459if __name__ == '__main__':1460FullBlockTest(__file__).main()
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.