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) 2014-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 behavior of headers messages to announce blocks.7Setup:9- Two nodes:10- node0 is the node-under-test. We create two p2p connections to it. The11first p2p connection is a control and should only ever receive inv's. The12second p2p connection tests the headers sending logic.13- node1 is used to create reorgs.15test_null_locators16==================18Sends two getheaders requests with null locator values. First request's hashstop19value refers to validated block, while second request's hashstop value refers to20a block which hasn't been validated. Verifies only the first request returns21headers.23test_nonnull_locators24=====================26Part 1: No headers announcements before "sendheaders"27a. node mines a block [expect: inv]28send getdata for the block [expect: block]29b. node mines another block [expect: inv]30send getheaders and getdata [expect: headers, then block]31c. node mines another block [expect: inv]32peer mines a block, announces with header [expect: getdata]33d. node mines another block [expect: inv]35Part 2: After "sendheaders", headers announcements should generally work.36a. peer sends sendheaders [expect: no response]37peer sends getheaders with current tip [expect: no response]38b. node mines a block [expect: tip header]39c. for N in 1, ..., 10:40* for announce-type in {inv, header}41- peer mines N blocks, announces with announce-type42[ expect: getheaders/getdata or getdata, deliver block(s) ]43- node mines a block [ expect: 1 header ]45Part 3: Headers announcements stop after large reorg and resume after getheaders or inv from peer.46- For response-type in {inv, getheaders}47* node mines a 7 block reorg [ expect: headers announcement of 8 blocks ]48* node mines an 8-block reorg [ expect: inv at tip ]49* peer responds with getblocks/getdata [expect: inv, blocks ]50* node mines another block [ expect: inv at tip, peer sends getdata, expect: block ]51* node mines another block at tip [ expect: inv ]52* peer responds with getheaders with an old hashstop more than 8 blocks back [expect: headers]53* peer requests block [ expect: block ]54* node mines another block at tip [ expect: inv, peer sends getdata, expect: block ]55* peer sends response-type [expect headers if getheaders, getheaders/getdata if mining new block]56* node mines 1 block [expect: 1 header, peer responds with getdata]58Part 4: Test direct fetch behavior59a. Announce 2 old block headers.60Expect: no getdata requests.61b. Announce 3 new blocks via 1 headers message.62Expect: one getdata request for all 3 blocks.63(Send blocks.)64c. Announce 1 header that forks off the last two blocks.65Expect: no response.66d. Announce 1 more header that builds on that fork.67Expect: one getdata request for two blocks.68e. Announce 16 more headers that build on that fork.69Expect: getdata request for 14 more blocks.70f. Announce 1 more header that builds on that fork.71Expect: no response.73Part 5: Test handling of headers that don't connect.74a. Repeat 100 times:751. Announce a header that doesn't connect.76Expect: getheaders message772. Send headers chain.78Expect: getdata for the missing blocks, tip update.79b. Then send 99 more headers that don't connect.80Expect: getheaders message each time.81"""82from test_framework.blocktools import create_block83from test_framework.messages import CInv84from test_framework.p2p import (85CBlockHeader,86NODE_WITNESS,87P2PInterface,88p2p_lock,89MSG_BLOCK,90msg_block,91msg_getblocks,92msg_getdata,93msg_getheaders,94msg_headers,95msg_inv,96msg_sendheaders,97)98from test_framework.test_framework import BitcoinTestFramework99from test_framework.util import (100assert_equal,101)103DIRECT_FETCH_RESPONSE_TIME = 0.05105class BaseNode(P2PInterface):106def __init__(self):107super().__init__()109self.block_announced = False110self.last_blockhash_announced = None111self.recent_headers_announced = []113def send_get_data(self, block_hashes):114"""Request data for a list of block hashes."""115msg = msg_getdata()116for x in block_hashes:117msg.inv.append(CInv(MSG_BLOCK, x))118self.send_without_ping(msg)120def send_get_headers(self, locator, hashstop):121msg = msg_getheaders()122msg.locator.vHave = locator123msg.hashstop = hashstop124self.send_without_ping(msg)126def send_block_inv(self, blockhash):127msg = msg_inv()128msg.inv = [CInv(MSG_BLOCK, blockhash)]129self.send_without_ping(msg)131def send_header_for_blocks(self, new_blocks):132headers_message = msg_headers()133headers_message.headers = [CBlockHeader(b) for b in new_blocks]134self.send_without_ping(headers_message)136def send_getblocks(self, locator):137getblocks_message = msg_getblocks()138getblocks_message.locator.vHave = locator139self.send_without_ping(getblocks_message)141def wait_for_block_announcement(self, block_hash, timeout=60):142def test_function():143return self.last_blockhash_announced == block_hash145self.wait_until(test_function, timeout=timeout)147def on_inv(self, message):148self.block_announced = True149self.last_blockhash_announced = message.inv[-1].hash151def on_headers(self, message):152if len(message.headers):153self.block_announced = True154for x in message.headers:155# append because headers may be announced over multiple messages.156self.recent_headers_announced.append(x.hash_int)157self.last_blockhash_announced = message.headers[-1].hash_int159def clear_block_announcements(self):160with p2p_lock:161self.block_announced = False162self.last_message.pop("inv", None)163self.last_message.pop("headers", None)164self.recent_headers_announced = []167def check_last_headers_announcement(self, headers):168"""Test whether the last headers announcements received are right.169Headers may be announced across more than one message."""170def test_function():171return len(self.recent_headers_announced) >= len(headers)173self.wait_until(test_function)174with p2p_lock:175assert_equal(self.recent_headers_announced, headers)176self.block_announced = False177self.last_message.pop("headers", None)178self.recent_headers_announced = []180def check_last_inv_announcement(self, inv):181"""Test whether the last announcement received had the right inv.182inv should be a list of block hashes."""184def test_function():185return self.block_announced187self.wait_until(test_function)189with p2p_lock:190compare_inv = []191if "inv" in self.last_message:192compare_inv = [x.hash for x in self.last_message["inv"].inv]193assert_equal(compare_inv, inv)194self.block_announced = False195self.last_message.pop("inv", None)197class SendHeadersTest(BitcoinTestFramework):198def set_test_params(self):199self.setup_clean_chain = True200self.num_nodes = 2202def mine_blocks(self, count):203"""Mine count blocks and return the new tip."""205# Clear out block announcements from each p2p listener206[x.clear_block_announcements() for x in self.nodes[0].p2ps]207self.generatetoaddress(self.nodes[0], count, self.nodes[0].get_deterministic_priv_key().address)208return int(self.nodes[0].getbestblockhash(), 16)210def mine_reorg(self, length):211"""Mine a reorg that invalidates length blocks (replacing them with # length+1 blocks).213Note: we clear the state of our p2p connections after the214to-be-reorged-out blocks are mined, so that we don't break later tests.215return the list of block hashes newly mined."""217# make sure all invalidated blocks are node0's218self.generatetoaddress(self.nodes[0], length, self.nodes[0].get_deterministic_priv_key().address)219for x in self.nodes[0].p2ps:220x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16))221x.clear_block_announcements()223tip_height = self.nodes[1].getblockcount()224hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1))225self.nodes[1].invalidateblock(hash_to_invalidate)226all_hashes = self.generatetoaddress(self.nodes[1], length + 1, self.nodes[1].get_deterministic_priv_key().address) # Must be longer than the orig chain227return [int(x, 16) for x in all_hashes]229def run_test(self):230# Setup the p2p connections231inv_node = self.nodes[0].add_p2p_connection(BaseNode())232# Make sure NODE_NETWORK is not set for test_node, so no block download233# will occur outside of direct fetching234test_node = self.nodes[0].add_p2p_connection(BaseNode(), services=NODE_WITNESS)236self.test_null_locators(test_node, inv_node)237self.test_nonnull_locators(test_node, inv_node)239def test_null_locators(self, test_node, inv_node):240tip = self.nodes[0].getblockheader(self.generatetoaddress(self.nodes[0], 1, self.nodes[0].get_deterministic_priv_key().address)[0])241tip_hash = int(tip["hash"], 16)243inv_node.check_last_inv_announcement(inv=[tip_hash])244test_node.check_last_inv_announcement(inv=[tip_hash])246self.log.info("Verify getheaders with null locator and valid hashstop returns headers.")247test_node.clear_block_announcements()248test_node.send_get_headers(locator=[], hashstop=tip_hash)249test_node.check_last_headers_announcement(headers=[tip_hash])
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.