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) 2017-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"""An example functional test7The module-level docstring should include a high-level description of8what the test is doing. It's the first thing people see when they open9the file and should give the reader information about *what* the test10is testing and *how* it's being tested11"""12# Imports should be in PEP8 ordering (std library first, then third party13# libraries then local imports).14from collections import defaultdict16# Avoid wildcard * imports17# Use lexicographically sorted multi-line imports18from test_framework.blocktools import (19create_block,20)21from test_framework.messages import (22CInv,23MSG_BLOCK,24)25from test_framework.p2p import (26P2PInterface,27msg_block,28msg_getdata,29p2p_lock,30)31from test_framework.test_framework import BitcoinTestFramework32from test_framework.util import (33assert_equal,34)36# P2PInterface is a class containing callbacks to be executed when a P2P37# message is received from the node-under-test. Subclass P2PInterface and38# override the on_*() methods if you need custom behaviour.39class BaseNode(P2PInterface):40def __init__(self):41"""Initialize the P2PInterface43Used to initialize custom properties for the Node that aren't44included by default in the base class. Be aware that the P2PInterface45base class already stores a counter for each P2P message type and the46last received message of each type, which should be sufficient for the47needs of most tests.49Call super().__init__() first for standard initialization and then50initialize custom properties."""51super().__init__()52# Stores a dictionary of all blocks received53self.block_receive_map = defaultdict(int)55def on_block(self, message):56"""Override the standard on_block callback58Store the hash of a received block in the dictionary."""59self.block_receive_map[message.block.hash_int] += 161def on_inv(self, message):62"""Override the standard on_inv callback"""63pass65def custom_function():66"""Do some custom behaviour68If this function is more generally useful for other tests, consider69moving it to a module in test_framework."""70# self.log.info("running custom_function") # Oops! Can't run self.log outside the BitcoinTestFramework71pass74class ExampleTest(BitcoinTestFramework):75# Each functional test is a subclass of the BitcoinTestFramework class.77# Override the set_test_params(), skip_test_if_missing_module(), add_options(), setup_chain(), setup_network()78# and setup_nodes() methods to customize the test setup as required.80def set_test_params(self):81"""Override test parameters for your individual test.83This method must be overridden and num_nodes must be explicitly set."""84# By default every test loads a pre-mined chain of 200 blocks from cache.85# Set setup_clean_chain to True to skip this and start from the Genesis86# block.87self.setup_clean_chain = True88self.num_nodes = 389# Use self.extra_args to change command-line arguments for the nodes90self.extra_args = [[], ["-logips"], []]92# self.log.info("I've finished set_test_params") # Oops! Can't run self.log before run_test()94# Use skip_test_if_missing_module() to skip the test if your test requires certain modules to be present.95# This test uses generate which requires wallet to be compiled96def skip_test_if_missing_module(self):97self.skip_if_no_wallet()99# Use add_options() to add specific command-line options for your test.100# In practice this is not used very much, since the tests are mostly written101# to be run in automated environments without command-line options.102# def add_options()103# pass105# Use setup_chain() to customize the node data directories. In practice106# this is not used very much since the default behaviour is almost always107# fine108# def setup_chain():109# pass111def setup_network(self):112"""Setup the test network topology114Often you won't need to override this, since the standard network topology115(linear: node0 <-> node1 <-> node2 <-> ...) is fine for most tests.117If you do override this method, remember to start the nodes, assign118them to self.nodes, connect them and then sync."""120self.setup_nodes()122# In this test, we're not connecting node2 to node0 or node1. Calls to123# sync_all() should not include node2, since we're not expecting it to124# sync.125self.connect_nodes(0, 1)126self.sync_all(self.nodes[0:2])128# Use setup_nodes() to customize the node start behaviour (for example if129# you don't want to start all nodes at the start of the test).130# def setup_nodes():131# pass133def custom_method(self):134"""Do some custom behaviour for this test136Define it in a method here because you're going to use it repeatedly.137If you think it's useful in general, consider moving it to the base138BitcoinTestFramework class so other tests can use it."""140self.log.info("Running custom_method")142def run_test(self):143"""Main test logic"""145# Create P2P connections will wait for a verack to make sure the connection is fully up146peer_messaging = self.nodes[0].add_p2p_connection(BaseNode())148# Generating a block on one of the nodes will get us out of IBD149blocks = [int(self.generate(self.nodes[0], sync_fun=lambda: self.sync_all(self.nodes[0:2]), nblocks=1)[0], 16)]151# Notice above how we called an RPC by calling a method with the same152# name on the node object. Notice also how we used a keyword argument153# to specify a named RPC argument. Neither of those are defined on the154# node object. Instead there's some __getattr__() magic going on under155# the covers to dispatch unrecognised attribute calls to the RPC156# interface.158# Logs are nice. Do plenty of them. They can be used in place of comments for159# breaking the test into sub-sections.160self.log.info("Starting test!")162self.log.info("Calling a custom function")163custom_function()165self.log.info("Calling a custom method")166self.custom_method()168self.log.info("Create some blocks")169self.tip = int(self.nodes[0].getbestblockhash(), 16)170self.block_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + 1172height = self.nodes[0].getblockcount()174for _ in range(10):175# Use the blocktools functionality to manually build a block.176# Calling the generate() rpc is easier, but this allows us to exactly177# control the blocks and transactions.178block = create_block(self.tip, height=height+1, ntime=self.block_time)179block.solve()180block_message = msg_block(block)181# Send message is used to send a P2P message to the node over our P2PInterface182peer_messaging.send_without_ping(block_message)183self.tip = block.hash_int184blocks.append(self.tip)185self.block_time += 1186height += 1188self.log.info("Wait for node1 to reach current tip (height 11) using RPC")189self.nodes[1].waitforblockheight(11)191self.log.info("Connect node2 and node1")192self.connect_nodes(1, 2)194self.log.info("Wait for node2 to receive all the blocks from node1")195self.sync_all()197self.log.info("Add P2P connection to node2")198self.nodes[0].disconnect_p2ps()200peer_receiving = self.nodes[2].add_p2p_connection(BaseNode())202self.log.info("Test that node2 propagates all the blocks to us")204getdata_request = msg_getdata()205for block in blocks:206getdata_request.inv.append(CInv(MSG_BLOCK, block))207peer_receiving.send_without_ping(getdata_request)209# wait_until() will loop until a predicate condition is met. Use it to test properties of the210# P2PInterface objects.211peer_receiving.wait_until(lambda: sorted(blocks) == sorted(list(peer_receiving.block_receive_map.keys())), timeout=5)213self.log.info("Check that each block was received only once")214# The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving215# messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking216# and synchronization issues. Note p2p.wait_until() acquires this global lock internally when testing the predicate.217with p2p_lock:218for block in peer_receiving.block_receive_map.values():219assert_equal(block, 1)222if __name__ == '__main__':223ExampleTest(__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.