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.
251extra_args = self.extra_args253# If listening and no -bind is given, then bitcoind would bind P2P ports on254# 0.0.0.0:P and 127.0.0.1:P+1 (for incoming Tor connections), where P is255# a unique port chosen by the test framework and configured as port=P in256# bitcoin.conf. To avoid collisions, change it to 127.0.0.1:tor_port().257will_listen = all(e != "-nolisten" and e != "-listen=0" for e in extra_args)258has_explicit_bind = self.has_explicit_bind or any(e.startswith("-bind=") for e in extra_args)259if will_listen and not has_explicit_bind:260extra_args.append(f"-bind=0.0.0.0:{p2p_port(self.index)}")261extra_args.append(f"-bind=127.0.0.1:{tor_port(self.index)}=onion")263self.use_v2transport = "-v2transport=1" in extra_args or (self.default_to_v2 and "-v2transport=0" not in extra_args)265# Add a new stdout and stderr file each time bitcoind is started266if stderr is None:267stderr = tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False)268if stdout is None:269stdout = tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False)270self.stderr = stderr271self.stdout = stdout273if cwd is None:274cwd = self.cwd276# Delete any existing cookie file -- if such a file exists (eg due to277# unclean shutdown), it will get overwritten anyway by bitcoind, and278# potentially interfere with our attempt to authenticate279delete_cookie_file(self.datadir_path, self.chain)281# add environment variable LIBC_FATAL_STDERR_=1 so that libc errors are written to stderr and not the terminal282subp_env = dict(os.environ, LIBC_FATAL_STDERR_="1")283if self.use_gui:284subp_env.setdefault("QT_QPA_PLATFORM", "minimal")285if platform.system() == "Darwin":286# QMacStyle assumes a Cocoa platform window, which the minimal platform287# does not provide. In particular, painting a QGroupBox can make Qt call288# addSubview: on an invalid native object (QTBUG-49686).289subp_env.setdefault("QT_STYLE_OVERRIDE", "fusion")290if platform.system() == "OpenBSD":291# The system Qt packages are built with GLib support, so Qt uses292# QEventDispatcherGlib, which pushes/pops the GLib thread-default293# main context in each thread. On OpenBSD the pop can run during294# thread exit after GLib's per-thread context stack has already295# been torn down, so shutdown emits messages like296# (process:NNN): GLib-CRITICAL **: g_main_context_pop_thread_default:297# assertion 'stack != NULL' failed298# on stderr, which the test framework treats as a failure.299# Fall back to Qt's poll-based event dispatcher instead.300subp_env.setdefault("QT_NO_GLIB", "1")301subp_env.setdefault("LC_ALL", "nl_NL.UTF-8") # Set language to try to trigger translation bugs302if sys.platform.startswith("linux") and "XDG_RUNTIME_DIR" not in subp_env:303# Qt prints warnings to stderr when XDG_RUNTIME_DIR is unset or has wrong304# permissions (e.g. in CI environments without a desktop session), which305# would cause tests to fail due to unexpected stderr output. The two306# warnings are:307# "QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'"308# "QStandardPaths: wrong permissions on runtime directory /path, 0755 instead of 0700"309# Use a dedicated subdirectory with the required 0700 permissions.310xdg_runtime_dir = self.datadir_path / "xdg_runtime"311xdg_runtime_dir.mkdir(mode=0o700, exist_ok=True)312subp_env["XDG_RUNTIME_DIR"] = str(xdg_runtime_dir)313if env is not None:314subp_env.update(env)316self.process = subprocess.Popen(self.args + extra_args, env=subp_env, stdout=stdout, stderr=stderr, cwd=cwd, **kwargs)318self.running = True319self.log.debug("bitcoind started, waiting for RPC to come up")321def create_new_rpc_connection(self, *, mode="AUTO", client_timeout=None):322"""Create an additional RPC connection, likely to be used in a new thread."""323mode = RPCConnectionType[mode]324if mode == RPCConnectionType.AUTO:325mode = RPCConnectionType.CLI if self.use_cli else RPCConnectionType.AUTHPROXY326client_timeout = client_timeout or (self.rpc_timeout // 2) # Shorter timeout to allow for one retry in case of ETIMEDOUT327host = "127.0.0.1"328port = rpc_port(self.index)329if self.rpchost:330parts = self.rpchost.split(":")331if len(parts) == 2:332host, port = parts333else:334host = self.rpchost335if mode == RPCConnectionType.AUTHPROXY:336rpc_u, rpc_p = get_auth_cookie(self.datadir_path, self.chain)337url = f"http://{rpc_u}:{rpc_p}@{host}:{port}"338proxy = AuthServiceProxy(url, timeout=int(client_timeout))339coverage_logfile = coverage.get_filename(self.coverage_dir, self.index) if self.coverage_dir else None340rpc = coverage.AuthServiceProxyWrapper(proxy, url, coverage_logfile)341rpc.auth_service_proxy_instance.reuse_http_connections = self.reuse_http_connections342return rpc343else: # mode==CLI344return TestNodeCLI(self.binaries)(345f"-datadir={self.datadir_path}",346f"-rpcclienttimeout={client_timeout}",347f"-rpcconnect={host}",348f"-rpcport={port}",349)351def wait_for_rpc_connection(self, *, wait_for_import=True):352"""Sets up an RPC connection to the bitcoind process. Returns False if unable to connect."""353# Poll at a rate of four times per second354poll_per_s = 4356suppressed_errors = collections.defaultdict(int)357latest_error = None358def suppress_error(category: str, e: Exception):359suppressed_errors[category] += 1360return (category, repr(e))362for _ in range(poll_per_s * self.rpc_timeout):363if self.process.poll() is not None:364# Attach abrupt shutdown error/s to the exception message365self.stderr.seek(0)366str_error = ''.join(line.decode('utf-8') for line in self.stderr)367str_error += "************************\n" if str_error else ''369raise FailedToStartError(self._node_msg(370f'bitcoind exited with status {self.process.returncode} during initialization. {str_error}'))371try:372rpc = self.create_new_rpc_connection(mode="AUTHPROXY")373rpc.getblockcount()374# If the call to getblockcount() succeeds then the RPC connection is up375if self.version_is_at_least(190000) and wait_for_import:376# getmempoolinfo.loaded is available since commit377# bb8ae2c (version 0.19.0)378self.wait_until(lambda: rpc.getmempoolinfo()['loaded'])379# Wait for the node to finish reindex, block import, and380# loading the mempool. Usually importing happens fast or381# even "immediate" when the node is started. However, there382# is no guarantee and sometimes ImportBlocks might finish383# later. This is going to cause intermittent test failures,384# because generally the tests assume the node is fully385# ready after being started.386#387# For example, the node will reject block messages from p2p388# when it is still importing with the error "Unexpected389# block message received"390#391# The wait is done here to make tests as robust as possible392# and prevent racy tests and intermittent failures as much393# as possible. Some tests might not need this, but the394# overhead is trivial, and the added guarantees are worth395# the minimal performance cost.396self.log.debug("RPC successfully started")397# Set rpc_connected even if we are in use_cli mode so that we know we can call self.stop() if needed.398self.rpc_connected = True399self.url = rpc.rpc_url400self.cli = self.create_new_rpc_connection(mode="CLI")401if self.use_cli:402return403self._rpc = rpc404return405except JSONRPCException as e:406# Suppress these as they are expected during initialization.407# -28 RPC in warmup408# -342 Service unavailable, could be starting up or shutting down409if e.error['code'] not in [-28, -342]:410raise # unknown JSON RPC exception411latest_error = suppress_error(f"JSONRPCException {e.error['code']}", e)412except OSError as e:413error_num = e.errno414if error_num is None:415# Work around issue where socket timeouts don't have errno set.416# https://github.com/python/cpython/issues/109601417if isinstance(e, TimeoutError):418error_num = errno.ETIMEDOUT419# http.client.RemoteDisconnected inherits from this type and420# doesn't specify errno.421elif isinstance(e, ConnectionResetError):422error_num = errno.ECONNRESET423# Windows can raise this while bitcoind shuts down during startup.424elif isinstance(e, ConnectionAbortedError):425error_num = errno.ECONNABORTED427# Suppress similarly to the above JSONRPCException errors.428if error_num not in [429errno.ECONNABORTED, # Treat identical to ECONNRESET430errno.ECONNRESET, # This might happen when the RPC server is in warmup,431# but shut down before the call to getblockcount succeeds.432errno.ETIMEDOUT, # Treat identical to ECONNRESET433errno.ECONNREFUSED # Port not yet open?434]:435raise # unknown OS error436latest_error = suppress_error(f"OSError {errno.errorcode[error_num]}", e)437except ValueError as e:438# Suppress if cookie file isn't generated yet and no rpcuser or rpcpassword; bitcoind may be starting.439if "No RPC credentials" not in str(e):440raise441latest_error = suppress_error("missing_credentials", e)442time.sleep(1.0 / poll_per_s)443self._raise_assertion_error(f"Unable to connect to bitcoind after {self.rpc_timeout}s (ignored errors: {dict(suppressed_errors)!s}{'' if latest_error is None else f', latest: {latest_error[0]!r}/{latest_error[1]}'})")445def wait_for_cookie_credentials(self):446"""Ensures auth cookie credentials can be read, e.g. for testing CLI with -rpcwait before RPC connection is up."""447self.log.debug("Waiting for cookie credentials")448# Poll at a rate of four times per second.449poll_per_s = 4450for _ in range(poll_per_s * self.rpc_timeout):451try:452get_auth_cookie(self.datadir_path, self.chain)453self.log.debug("Cookie credentials successfully retrieved")454return455except ValueError: # cookie file not found and no rpcuser or rpcpassword; bitcoind is still starting456pass # so we continue polling until RPC credentials are retrieved457time.sleep(1.0 / poll_per_s)458self._raise_assertion_error("Unable to retrieve cookie credentials after {}s".format(self.rpc_timeout))460def generate(self, nblocks, maxtries=1000000, **kwargs):461self.log.debug("TestNode.generate() dispatches `generate` call to `generatetoaddress`")462return self.generatetoaddress(nblocks=nblocks, address=self.get_deterministic_priv_key().address, maxtries=maxtries, **kwargs)464def generateblock(self, *args, called_by_framework, **kwargs):465assert called_by_framework, "Direct call of this mining RPC is discouraged. Please use one of the self.generate* methods on the test framework, which sync the nodes to avoid intermittent test issues. You may use sync_fun=self.no_op to disable the sync explicitly."466return self.__getattr__('generateblock')(*args, **kwargs)468def generatetoaddress(self, *args, called_by_framework, **kwargs):469assert called_by_framework, "Direct call of this mining RPC is discouraged. Please use one of the self.generate* methods on the test framework, which sync the nodes to avoid intermittent test issues. You may use sync_fun=self.no_op to disable the sync explicitly."470return self.__getattr__('generatetoaddress')(*args, **kwargs)472def generatetodescriptor(self, *args, called_by_framework, **kwargs):473assert called_by_framework, "Direct call of this mining RPC is discouraged. Please use one of the self.generate* methods on the test framework, which sync the nodes to avoid intermittent test issues. You may use sync_fun=self.no_op to disable the sync explicitly."474return self.__getattr__('generatetodescriptor')(*args, **kwargs)476def setmocktime(self, timestamp):477"""Wrapper for setmocktime RPC, sets self.mocktime"""478if timestamp == 0:479# setmocktime(0) resets to system time.480self.mocktime = None481else:482self.mocktime = timestamp483return self.__getattr__('setmocktime')(timestamp)485def get_wallet_rpc(self, wallet_name):486if self.use_cli:487return self.cli("-rpcwallet={}".format(wallet_name))488else:489assert self.rpc_connected and self._rpc, self._node_msg("RPC not connected")490wallet_path = "wallet/{}".format(urllib.parse.quote(wallet_name))491return self._rpc / wallet_path493def version_is_at_least(self, ver):494return self.version is None or self.version >= ver496def stop_node(self, expected_stderr='', *, wait=0, wait_until_stopped=True):497"""Stop the node."""498if not self.running:499return500assert self.rpc_connected, self._node_msg(
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.