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"""Class for bitcoind node under test"""7import contextlib8import decimal9import errno10from enum import Enum11import json12import logging13import os14import platform15import re16import subprocess17import tempfile18import time19import urllib.parse20import collections21import sys22from collections.abc import Iterable23from pathlib import Path25from .authproxy import (26AuthServiceProxy,27JSONRPCException,28serialization_fallback,29)30from . import coverage31from .messages import NODE_P2P_V232from .p2p import P2P_SERVICES, P2P_SUBVERSION33from .util import (34MAX_NODES,35assert_equal,36assert_not_equal,37append_config,38delete_cookie_file,39get_auth_cookie,40rpc_port,41wait_until_helper_internal,42p2p_port,43tor_port,44)46BITCOIND_PROC_WAIT_TIMEOUT = 6047# The size of the blocks xor key48# from InitBlocksdirXorKey::xor_key.size()49NUM_XOR_BYTES = 850# Many systems have a 128kB limit for a command size. Depending on the51# platform, this limit may be larger or smaller. Moreover, when using the52# 'bitcoin' command, it may internally insert more args, which must be53# accounted for. There is no need to pick the largest possible value here54# anyway and it should be fine to set it to 1kB in tests.55TEST_CLI_MAX_ARG_SIZE = 102457# The null blocks key (all 0s)58NULL_BLK_XOR_KEY = bytes([0] * NUM_XOR_BYTES)59BITCOIN_PID_FILENAME_DEFAULT = "bitcoind.pid"61if sys.platform.startswith("linux"):62UNIX_PATH_MAX = 108 # includes the trailing NUL63elif sys.platform.startswith(("darwin", "freebsd", "netbsd", "openbsd")):64UNIX_PATH_MAX = 10465else: # safest portable value66UNIX_PATH_MAX = 9269class FailedToStartError(Exception):70"""Raised when a node fails to start correctly."""73class ErrorMatch(Enum):74FULL_TEXT = 175FULL_REGEX = 276PARTIAL_REGEX = 379RPCConnectionType = Enum("RPCConnectionType", ["AUTO", "AUTHPROXY", "CLI"])82class TestNode():83"""A class for representing a bitcoind node under test.85This class contains:87- state about the node (whether it's running, etc)88- a Python subprocess.Popen object representing the running process89- an RPC connection to the node90- one or more P2P connections to the node93To make things easier for the test writer, any unrecognised messages will94be dispatched to the RPC connection."""96def __init__(97self,98i,99datadir_path,100*,101chain,102rpchost,103timewait,104timeout_factor,105binaries,106coverage_dir,107cwd,108extra_conf=None,109extra_args=None,110use_cli=False,111version=None,112v2transport=False,113uses_wallet=False,114ipcbind=False,115use_gui=False,116):117self.index = i118self.datadir_path = datadir_path119self.bitcoinconf = self.datadir_path / "bitcoin.conf"120self.stdout_dir = self.datadir_path / "stdout"121self.stderr_dir = self.datadir_path / "stderr"122self.chain = chain123self.rpchost = rpchost124self.rpc_timeout = timewait # Already multiplied by timeout_factor125self.timeout_factor = timeout_factor126self.binaries = binaries127self.coverage_dir = coverage_dir128self.cwd = cwd129self.use_gui = use_gui130self.has_explicit_bind = False131if extra_conf is not None:132append_config(self.datadir_path, extra_conf)133# Remember if there is bind=... in the config file.134self.has_explicit_bind = any(e.startswith("bind=") for e in extra_conf)135# Most callers will just need to add extra args to the standard list below.136# For those callers that need more flexibility, they can just set the args property directly.137# Note that common args are set in the config file (see initialize_datadir)138self.extra_args = extra_args139self.version = version140# Configuration for logging is set as command-line args rather than in the bitcoin.conf file.141# This means that starting a bitcoind using the temp dir to debug a failed test won't142# spam debug.log.143self.args = self.binaries.node_argv(need_ipc=ipcbind, use_gui=use_gui) + [144f"-datadir={self.datadir_path}",145"-logtimemicros",146"-debug",147"-debugexclude=leveldb",148"-debugexclude=rand",149"-uacomment=testnode%d" % i, # required for subversion uniqueness across peers150]151if uses_wallet is not None and not uses_wallet:152self.args.append("-disablewallet")154self.ipc_tmp_dir = None155if ipcbind:156self.ipc_socket_path = self.chain_path / "node.sock"157if len(os.fsencode(self.ipc_socket_path)) < UNIX_PATH_MAX:158self.args.append("-ipcbind=unix")159else:160# Work around default CI path exceeding maximum socket path length.161self.ipc_tmp_dir = tempfile.TemporaryDirectory(prefix="test-ipc-")162self.ipc_socket_path = Path(self.ipc_tmp_dir.name) / "node.sock"163self.args.append(f"-ipcbind=unix:{self.ipc_socket_path}")165if self.version_is_at_least(190000):166self.args.append("-logthreadnames")167if self.version_is_at_least(219900):168self.args.append("-logsourcelocations")169if self.version_is_at_least(239000):170self.args.append("-loglevel=trace")171if self.version_is_at_least(290100):172self.args.append("-nologratelimit")174# Default behavior from global -v2transport flag is added to args to persist it over restarts.175# May be overwritten in individual tests, using extra_args.176self.default_to_v2 = v2transport177if self.version_is_at_least(260000):178# 26.0 and later support v2transport179if v2transport:180self.args.append("-v2transport=1")181else:182self.args.append("-v2transport=0")183# if v2transport is requested via global flag but not supported for node version, ignore it185self.cli = None186self.use_cli = use_cli188self.running = False189self.process = None190self.rpc_connected = False191self._rpc = None # Should usually not be accessed directly in tests to allow for --usecli mode192self.reuse_http_connections = True # Must be set before create_new_rpc_connection(), i.e. before restarting node193self.url = None194self.log = logging.getLogger('TestFramework.node%d' % i)196self.p2ps = []198self.mocktime = None200AddressKeyPair = collections.namedtuple('AddressKeyPair', ['address', 'key'])201PRIV_KEYS = [202# address , privkey203AddressKeyPair('mjTkW3DjgyZck4KbiRusZsqTgaYTxdSz6z', 'cVpF924EspNh8KjYsfhgY96mmxvT6DgdWiTYMtMjuM74hJaU5psW'),204AddressKeyPair('msX6jQXvxiNhx3Q62PKeLPrhrqZQdSimTg', 'cUxsWyKyZ9MAQTaAhUQWJmBbSvHMwSmuv59KgxQV7oZQU3PXN3KE'),205AddressKeyPair('mnonCMyH9TmAsSj3M59DsbH8H63U3RKoFP', 'cTrh7dkEAeJd6b3MRX9bZK8eRmNqVCMH3LSUkE3dSFDyzjU38QxK'),206AddressKeyPair('mqJupas8Dt2uestQDvV2NH3RU8uZh2dqQR', 'cVuKKa7gbehEQvVq717hYcbE9Dqmq7KEBKqWgWrYBa2CKKrhtRim'),207AddressKeyPair('msYac7Rvd5ywm6pEmkjyxhbCDKqWsVeYws', 'cQDCBuKcjanpXDpCqacNSjYfxeQj8G6CAtH1Dsk3cXyqLNC4RPuh'),208AddressKeyPair('n2rnuUnwLgXqf9kk2kjvVm8R5BZK1yxQBi', 'cQakmfPSLSqKHyMFGwAqKHgWUiofJCagVGhiB4KCainaeCSxeyYq'),209AddressKeyPair('myzuPxRwsf3vvGzEuzPfK9Nf2RfwauwYe6', 'cQMpDLJwA8DBe9NcQbdoSb1BhmFxVjWD5gRyrLZCtpuF9Zi3a9RK'),210AddressKeyPair('mumwTaMtbxEPUswmLBBN3vM9oGRtGBrys8', 'cSXmRKXVcoouhNNVpcNKFfxsTsToY5pvB9DVsFksF1ENunTzRKsy'),211AddressKeyPair('mpV7aGShMkJCZgbW7F6iZgrvuPHjZjH9qg', 'cSoXt6tm3pqy43UMabY6eUTmR3eSUYFtB2iNQDGgb3VUnRsQys2k'),212AddressKeyPair('mq4fBNdckGtvY2mijd9am7DRsbRB4KjUkf', 'cN55daf1HotwBAgAKWVgDcoppmUNDtQSfb7XLutTLeAgVc3u8hik'),213AddressKeyPair('mpFAHDjX7KregM3rVotdXzQmkbwtbQEnZ6', 'cT7qK7g1wkYEMvKowd2ZrX1E5f6JQ7TM246UfqbCiyF7kZhorpX3'),214AddressKeyPair('mzRe8QZMfGi58KyWCse2exxEFry2sfF2Y7', 'cPiRWE8KMjTRxH1MWkPerhfoHFn5iHPWVK5aPqjW8NxmdwenFinJ'),215]217def get_deterministic_priv_key(self):218"""Return a deterministic priv key in base58, that only depends on the node's index"""219assert_equal(len(self.PRIV_KEYS), MAX_NODES)220return self.PRIV_KEYS[self.index]222def _node_msg(self, msg: str) -> str:223"""Return a modified msg that identifies this node by its index as a debugging aid."""224return "[node %d] %s" % (self.index, msg)226def _raise_assertion_error(self, msg: str):227"""Raise an AssertionError with msg modified to identify this node."""228raise AssertionError(self._node_msg(msg))230def __del__(self):231# Ensure that we don't leave any bitcoind processes lying around after232# the test ends233if self.process:234# Should only happen on test failure235# Avoid using logger, as that may have already been shutdown when236# this destructor is called.237print(self._node_msg("Cleaning up leftover process"), file=sys.stderr)238self.process.kill()240def __getattr__(self, name):241"""Dispatches any unrecognised messages to the RPC connection or a CLI instance."""242if self.use_cli:243return getattr(self.cli, name)244else:245assert self.rpc_connected and self._rpc is not None, self._node_msg("Error: no RPC connection")246return getattr(self._rpc, name)248def start(self, extra_args=None, *, cwd=None, stdout=None, stderr=None, env=None, **kwargs):249"""Start the node."""250if extra_args is None:
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.