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.
251seed = self.options.randomseed253if seed is None:254seed = random.randrange(sys.maxsize)255else:256self.log.info("User supplied random seed {}".format(seed))258random.seed(seed)259self.log.info("PRNG seed is: {}".format(seed))261self.log.debug('Setting up network thread')262self.network_thread = NetworkThread()263self.network_thread.start()264self.wait_until(lambda: self.network_thread.network_event_loop is not None and self.network_thread.network_event_loop.is_running())266if self.options.usecli:267if not self.supports_cli:268raise SkipTest("--usecli specified but test does not support using CLI")269self.skip_if_no_cli()270self.skip_test_if_missing_module()271self.setup_chain()272self.setup_network()274self.success = TestStatus.PASSED276def shutdown(self):277"""Call this method to shut down the test framework object."""279if self.success == TestStatus.FAILED and self.options.pdbonfailure:280print("Testcase failed. Attaching python debugger. Enter ? for help")281pdb.set_trace()283self.log.debug('Closing down network thread')284self.network_thread.close(timeout=self.options.timeout_factor * 10)285if self.success == TestStatus.FAILED:286self.log.info("Not stopping nodes as test failed. The dangling processes will be cleaned up later.")287else:288self.log.info("Stopping nodes")289if self.nodes:290self.stop_nodes()292should_clean_up = (293not self.options.nocleanup and294self.success != TestStatus.FAILED295)296if should_clean_up:297self.log.info("Cleaning up {} on exit".format(self.options.tmpdir))298cleanup_tree_on_exit = True299else:300self.log.warning("Not cleaning up dir {}".format(self.options.tmpdir))301cleanup_tree_on_exit = False303if self.success == TestStatus.PASSED:304self.log.info("Tests successful")305exit_code = TEST_EXIT_PASSED306elif self.success == TestStatus.SKIPPED:307self.log.info("Test skipped")308exit_code = TEST_EXIT_SKIPPED309else:310self.log.error("Test failed. Test logging available at %s/test_framework.log", self.options.tmpdir)311self.log.error("")312self.log.error("Hint: Call {} '{}' to consolidate all logs".format(os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../combine_logs.py"), self.options.tmpdir))313self.log.error("")314self.log.error("If this failure happened unexpectedly or intermittently, please file a bug and provide a link or upload of the combined log.")315self.log.error(self.config['environment']['CLIENT_BUGREPORT'])316self.log.error("")317exit_code = TEST_EXIT_FAILED318# Logging.shutdown will not remove stream- and filehandlers, so we must319# do it explicitly. Handlers are removed so the next test run can apply320# different log handler settings.321# See: https://docs.python.org/3/library/logging.html#logging.shutdown322for h in list(self.log.handlers):323h.flush()324h.close()325self.log.removeHandler(h)326rpc_logger = logging.getLogger("BitcoinRPC")327for h in list(rpc_logger.handlers):328h.flush()329rpc_logger.removeHandler(h)330if cleanup_tree_on_exit:331self.cleanup_folder(self.options.tmpdir)333self.nodes.clear()334return exit_code336# Methods to override in subclass test scripts.337def set_test_params(self):338"""Tests must override this method to change default values for number of nodes, topology, etc"""339raise NotImplementedError341def add_options(self, parser):342"""Override this method to add command-line options to the test"""343pass345def skip_test_if_missing_module(self):346"""Override this method to skip a test if a module is not compiled"""347pass349def setup_chain(self):350"""Override this method to customize blockchain setup"""351self.log.info("Initializing test directory " + self.options.tmpdir)352if self.setup_clean_chain:353self._initialize_chain_clean()354else:355self._initialize_chain()357def setup_network(self):358"""Override this method to customize test network topology"""359self.setup_nodes()361# Connect the nodes as a "chain". This allows us362# to split the network between nodes 1 and 2 to get363# two halves that can work on competing chains.364#365# Topology looks like this:366# node0 <-- node1 <-- node2 <-- node3367#368# If all nodes are in IBD (clean chain from genesis), node0 is assumed to be the source of blocks (miner). To369# ensure block propagation, all nodes will establish outgoing connections toward node0.370# See fPreferredDownload in net_processing.371#372# If further outbound connections are needed, they can be added at the beginning of the test with e.g.373# self.connect_nodes(1, 2)374for i in range(self.num_nodes - 1):375self.connect_nodes(i + 1, i)376self.sync_all()378def setup_nodes(self):379"""Override this method to customize test node setup"""380self.add_nodes(self.num_nodes, self.extra_args)381self.start_nodes()382if self.uses_wallet:383self.import_deterministic_coinbase_privkeys()384if not self.setup_clean_chain:385for n in self.nodes:386assert_equal(n.getblockchaininfo()["blocks"], 199)387# To ensure that all nodes are out of IBD, the most recent block388# must have a timestamp not too old (see IsInitialBlockDownload()).389self.log.debug('Generate a block with current time')390block_hash = self.generate(self.nodes[0], 1, sync_fun=self.no_op)[0]391block = self.nodes[0].getblock(blockhash=block_hash, verbosity=0)392for n in self.nodes:393n.submitblock(block)394chain_info = n.getblockchaininfo()395assert_equal(chain_info["blocks"], 200)396assert_equal(chain_info["initialblockdownload"], False)398def import_deterministic_coinbase_privkeys(self):399for i in range(self.num_nodes):400self.init_wallet(node=i)402def init_wallet(self, *, node):403wallet_name = self.default_wallet_name if self.wallet_names is None else self.wallet_names[node] if node < len(self.wallet_names) else False404if wallet_name is not False:405n = self.nodes[node]406if wallet_name is not None:407n.createwallet(wallet_name=wallet_name, load_on_startup=True)408wallet_importprivkey(n.get_wallet_rpc(wallet_name), n.get_deterministic_priv_key().key, 0, label="coinbase")410def run_test(self):411"""Tests must override this method to define test logic"""412raise NotImplementedError414# Public helper methods. These can be accessed by the subclass test scripts.416def add_nodes(self, num_nodes: int, extra_args=None, *, rpchost=None, versions=None):417"""Instantiate TestNode objects.419Should only be called once after the nodes have been specified in420set_test_params()."""421def bin_dir_from_version(version):422if not version:423return None424if version > 219999:425# Starting at client version 220000 the first two digits represent426# the major version, e.g. v22.0 instead of v0.22.0.427version *= 100428return os.path.join(429self.options.previous_releases_path,430re.sub(431r'\.0$' if version <= 219999 else r'(\.0){1,2}$',432'', # Remove trailing dot for point releases, after 22.0 also remove double trailing dot.433'v{}.{}.{}.{}'.format(434(version % 100000000) // 1000000,435(version % 1000000) // 10000,436(version % 10000) // 100,437(version % 100) // 1,438),439),440'bin',441)443if self.bind_to_localhost_only:444extra_confs = [["bind=127.0.0.1"]] * num_nodes445else:446extra_confs = [[]] * num_nodes447if extra_args is None:448extra_args = [[]] * num_nodes449# Whitelist peers to speed up tx relay / mempool sync. Don't use it if testing tx relay or timing.450if self.noban_tx_relay:451for i in range(len(extra_args)):452extra_args[i] = extra_args[i] + ["-whitelist=noban,in,[email protected]"]453if versions is None:454versions = [None] * num_nodes455bin_dirs = []456for v in versions:457bin_dir = bin_dir_from_version(v)458bin_dirs.append(bin_dir)460extra_init = [{}] * num_nodes if self.extra_init is None else self.extra_init # type: ignore[var-annotated]461assert_equal(len(extra_init), num_nodes)462assert_equal(len(extra_confs), num_nodes)463assert_equal(len(extra_args), num_nodes)464assert_equal(len(versions), num_nodes)465assert_equal(len(bin_dirs), num_nodes)466for i in range(num_nodes):467args = list(extra_args[i])468init = dict(469chain=self.chain,470rpchost=rpchost,471timewait=self.rpc_timeout,472timeout_factor=self.options.timeout_factor,473binaries=self.get_binaries(bin_dirs[i]),474version=versions[i],475coverage_dir=self.options.coveragedir,476cwd=self.options.tmpdir,477extra_conf=extra_confs[i],478extra_args=args,479use_cli=self.options.usecli,480v2transport=self.options.v2transport,481uses_wallet=self.uses_wallet,482)483init.update(extra_init[i])484test_node_i = TestNode(485i,486get_datadir_path(self.options.tmpdir, i),487**init)488self.nodes.append(test_node_i)489if not test_node_i.version_is_at_least(170000):490# adjust conf for pre 17491test_node_i.replace_in_config([('[regtest]', '')])493def start_node(self, i, *args, **kwargs):494"""Start a bitcoind"""496node = self.nodes[i]498node.start(*args, **kwargs)499node.wait_for_rpc_connection()
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.