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"""Base class for RPC testing."""7import configparser8from enum import Enum9import argparse10from datetime import datetime, timezone11from importlib.util import find_spec12import logging13import os14from pathlib import Path15import platform16import pdb17import random18import re19import shutil20import subprocess21import sys22import tempfile23import time25from .address import create_deterministic_address_bcrt1_p2tr_op_true26from . import coverage27from .messages import CAddress28from .p2p import NetworkThread29from .test_node import TestNode30from .util import (31Binaries,32MAX_NODES,33PortSeed,34assert_equal,35check_json_precision,36export_env_build_path,37find_vout_for_address,38get_binary_paths,39get_datadir_path,40initialize_datadir,41p2p_port,42wait_until_helper_internal,43wallet_importprivkey,44JSONRPCException,45)48class TestStatus(Enum):49PASSED = 150FAILED = 251SKIPPED = 353TEST_EXIT_PASSED = 054TEST_EXIT_FAILED = 155TEST_EXIT_SKIPPED = 7757TMPDIR_PREFIX = "bitcoin_func_test_"60class SkipTest(Exception):61"""This exception is raised to skip a test"""63def __init__(self, message):64self.message = message67class BitcoinTestMetaClass(type):68"""Metaclass for BitcoinTestFramework.70Ensures that any attempt to register a subclass of `BitcoinTestFramework`71adheres to a standard whereby the subclass overrides `set_test_params` and72`run_test` but DOES NOT override either `__init__` or `main`. If any of73those standards are violated, a ``TypeError`` is raised."""75def __new__(cls, clsname, bases, dct):76if not clsname == 'BitcoinTestFramework':77if not ('run_test' in dct and 'set_test_params' in dct):78raise TypeError("BitcoinTestFramework subclasses must override "79"'run_test' and 'set_test_params'")80if '__init__' in dct or 'main' in dct:81raise TypeError("BitcoinTestFramework subclasses may not override "82"'__init__' or 'main'")84return super().__new__(cls, clsname, bases, dct)87class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):88"""Base class for a bitcoin test script.90Individual bitcoin test scripts should subclass this class and override the set_test_params() and run_test() methods.92Individual tests can also override the following methods to customize the test setup:94- add_options()95- setup_chain()96- setup_network()97- setup_nodes()99The __init__() and main() methods should not be overridden.101This class also contains various public and private helper methods."""103def __init__(self, test_file) -> None:104"""Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method"""105self.chain: str = 'regtest'106self.setup_clean_chain: bool = False107self.noban_tx_relay: bool = False108self.nodes: list[TestNode] = []109self.extra_args = None110self.extra_init = None111self.network_thread = None112self.rpc_timeout = 60 # Wait for up to 60 seconds for the RPC server to respond113self.supports_cli = True114self.bind_to_localhost_only = True115self.parse_args(test_file)116self.default_wallet_name = "default_wallet"117self.wallet_data_filename = "wallet.dat"118# Optional list of wallet names that can be set in set_test_params to119# create and import keys to. If unset, default is len(nodes) *120# [default_wallet_name]. If wallet names are None, wallet creation is121# skipped. If list is truncated, wallet creation is skipped and keys122# are not imported.123self.wallet_names = None124# By default the wallet is not required. Set to true by skip_if_no_wallet().125# Can also be set to None to indicate that the wallet will be used if available.126# When False or None, we ignore wallet_names in setup_nodes().127self.uses_wallet = False128# Disable ThreadOpenConnections by default, so that adding entries to129# addrman will not result in automatic connections to them.130self.disable_autoconnect = True131self.set_test_params()132assert self.wallet_names is None or len(self.wallet_names) <= self.num_nodes133self.rpc_timeout = int(self.rpc_timeout * self.options.timeout_factor) # optionally, increase timeout by a factor135def main(self):136"""Main function. This should not be overridden by the subclass test scripts."""138assert hasattr(self, "num_nodes"), "Test must set self.num_nodes in set_test_params()"140try:141self.setup()142if self.options.test_methods:143self.run_test_methods()144else:145self.run_test()147except SkipTest as e:148self.log.warning("Test Skipped: %s" % e.message)149self.success = TestStatus.SKIPPED150except subprocess.CalledProcessError as e:151self.log.exception(f"Called Process failed with stdout='{e.stdout}'; stderr='{e.stderr}';")152self.success = TestStatus.FAILED153except BaseException:154# The `exception` log will add the exception info to the message.155# https://docs.python.org/3/library/logging.html#logging.exception156self.log.exception("Unexpected exception:")157self.success = TestStatus.FAILED158finally:159exit_code = self.shutdown()160sys.exit(exit_code)162def run_test_methods(self):163for method_name in self.options.test_methods:164self.log.info(f"Attempting to execute method: {method_name}")165method = getattr(self, method_name)166method()167self.log.info(f"Method '{method_name}' executed successfully.")169def parse_args(self, test_file):170previous_releases_path = os.getenv("PREVIOUS_RELEASES_DIR") or os.getcwd() + "/releases"171parser = argparse.ArgumentParser(usage="%(prog)s [options]")172parser.add_argument("--nocleanup", dest="nocleanup", default=False, action="store_true",173help="Leave bitcoinds and test.* datadir on exit or error")174parser.add_argument("--cachedir", dest="cachedir", default=os.path.abspath(os.path.dirname(test_file) + "/../cache"),175help="Directory for caching pregenerated datadirs (default: %(default)s)")176parser.add_argument("--tmpdir", dest="tmpdir", help="Root directory for datadirs (must not exist)")177parser.add_argument("-l", "--loglevel", dest="loglevel", default="INFO",178help="log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console. Note that logs at all levels are always written to the test_framework.log file in the temporary test directory.")179parser.add_argument("--tracerpc", dest="trace_rpc", default=False, action="store_true",180help="Print out all RPC calls as they are made")181parser.add_argument("--portseed", dest="port_seed", default=os.getpid(), type=int,182help="The seed to use for assigning port numbers (default: current process id)")183parser.add_argument("--previous-releases", dest="prev_releases", action="store_true",184default=os.path.isdir(previous_releases_path) and bool(os.listdir(previous_releases_path)),185help="Force test of previous releases (default: %(default)s). Previous releases binaries can be downloaded via `test/get_previous_releases.py`.")186parser.add_argument("--coveragedir", dest="coveragedir",187help="Write tested RPC commands into this directory")188parser.add_argument("--configfile", dest="configfile",189default=os.path.abspath(os.path.dirname(test_file) + "/../config.ini"),190help="Location of the test framework config file (default: %(default)s)")191parser.add_argument("--pdbonfailure", dest="pdbonfailure", default=False, action="store_true",192help="Attach a python debugger if test fails")193parser.add_argument("--usecli", dest="usecli", default=False, action="store_true",194help="use bitcoin-cli instead of RPC for all commands")195parser.add_argument("--valgrind", dest="valgrind", default=False, action="store_true",196help="Run binaries under the valgrind memory error detector: Expect at least a ~10x slowdown. Does not apply to previous release binaries.")197parser.add_argument("--randomseed", type=int,198help="set a random seed for deterministically reproducing a previous test run")199parser.add_argument("--timeout-factor", dest="timeout_factor", type=float, help="adjust test timeouts by a factor. Setting it to 0 disables all timeouts")200parser.add_argument("--v2transport", dest="v2transport", default=False, action="store_true",201help="use BIP324 v2 connections between all nodes by default")202parser.add_argument("--v1transport", dest="v1transport", default=False, action="store_true",203help="Explicitly use v1 transport (can be used to overwrite global --v2transport option)")204parser.add_argument("--test_methods", dest="test_methods", nargs='*',205help="Run specified test methods sequentially instead of the full test. Use only for methods that do not depend on any context set up in run_test or other methods.")207self.add_options(parser)208# Running TestShell in a Jupyter notebook causes an additional -f argument209# To keep TestShell from failing with an "unrecognized argument" error, we add a dummy "-f" argument210# source: https://stackoverflow.com/questions/48796169/how-to-fix-ipykernel-launcher-py-error-unrecognized-arguments-in-jupyter/56349168#56349168211parser.add_argument("-f", "--fff", help="a dummy argument to fool ipython", default="1")212self.options = parser.parse_args()213if self.options.timeout_factor == 0:214self.options.timeout_factor = 999215self.options.timeout_factor = self.options.timeout_factor or (4 if self.options.valgrind else 1)216self.options.previous_releases_path = previous_releases_path218self.config = configparser.ConfigParser()219self.config.read_file(open(self.options.configfile))220self.binary_paths = get_binary_paths(self.config)221if self.options.v1transport:222self.options.v2transport=False224PortSeed.n = self.options.port_seed226def get_binaries(self, bin_dir=None):227return Binaries(self.binary_paths, bin_dir, use_valgrind=self.options.valgrind)229def setup(self):230"""Call this method to start up the test framework object with options set."""232check_json_precision()233export_env_build_path(self.config)235self.options.cachedir = os.path.abspath(self.options.cachedir)237# Set up temp directory and start logging238if self.options.tmpdir:239self.options.tmpdir = os.path.abspath(self.options.tmpdir)240os.makedirs(self.options.tmpdir, exist_ok=False)241else:242self.options.tmpdir = tempfile.mkdtemp(prefix=TMPDIR_PREFIX)243self._start_logging()245# Seed the PRNG. Note that test runs are reproducible if and only if246# a single thread accesses the PRNG. For more information, see247# https://docs.python.org/3/library/random.html#notes-on-reproducibility.248# The network thread shouldn't access random. If we need to change the249# network thread to access randomness, it should instantiate its own250# random.Random object.
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.