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.
252for dir in dirs:253self.cleanup_folder(node.chain_path / dir)254shutil.move(node.chain_path / f"{dir}_bak", node.chain_path / dir)256def init_pid_test(self):257BITCOIN_PID_FILENAME_CUSTOM = "my_fancy_bitcoin_pid_file.foobar"259self.log.info("Test specifying custom pid file via -pid command line option")260custom_pidfile_relative = BITCOIN_PID_FILENAME_CUSTOM261self.log.info(f"-> path relative to datadir ({custom_pidfile_relative})")262self.restart_node(0, [f"-pid={custom_pidfile_relative}"])263datadir = self.nodes[0].chain_path264assert not (datadir / BITCOIN_PID_FILENAME_DEFAULT).exists()265assert (datadir / custom_pidfile_relative).exists()266self.stop_node(0)267assert not (datadir / custom_pidfile_relative).exists()269custom_pidfile_absolute = Path(self.options.tmpdir) / BITCOIN_PID_FILENAME_CUSTOM270self.log.info(f"-> absolute path ({custom_pidfile_absolute})")271self.restart_node(0, [f"-pid={custom_pidfile_absolute}"])272assert not (datadir / BITCOIN_PID_FILENAME_DEFAULT).exists()273assert custom_pidfile_absolute.exists()274self.stop_node(0)275assert not custom_pidfile_absolute.exists()277def break_wait_test(self):278"""Test what happens when a break signal is sent during a279waitforblockheight RPC call with a long timeout. Ctrl-Break is sent on280Windows and SIGTERM is sent on other platforms, to trigger the same node281shutdown sequence that would happen if Ctrl-C were pressed in a282terminal. (This can be different than the node shutdown sequence that283happens when the stop RPC is sent.)285The waitforblockheight call should be interrupted and return right away,286and not time out."""288self.log.info("Testing waitforblockheight RPC call followed by break signal")289node = self.nodes[0]291self.start_node(node.index, **get_process_group_kwargs())293current_height = node.getblock(node.getbestblockhash())['height']295with ThreadPoolExecutor(max_workers=1) as ex:296# Call waitforblockheight with wait timeout longer than RPC timeout,297# so it is possible to distinguish whether it times out or returns298# early. If it times out it will throw an exception, and if it299# returns early it will return the current block height.300self.log.debug(f"Calling waitforblockheight with {self.rpc_timeout} sec RPC timeout")301fut = ex.submit(node.waitforblockheight, height=current_height+1, timeout=self.rpc_timeout*1000*2)303self.wait_until(lambda: any(c["method"] == "waitforblockheight" for c in node.cli.getrpcinfo()["active_commands"]))305self.log.debug(f"Sending break signal to pid {node.process.pid}")306if platform.system() == 'Windows':307# Note: CTRL_C_EVENT should not be sent here because unlike308# CTRL_BREAK_EVENT it can not be targeted at a specific process309# group and may behave unpredictably.310node.process.send_signal(signal.CTRL_BREAK_EVENT)311else:312# Note: signal.SIGINT would work here as well313node.process.send_signal(signal.SIGTERM)314node.process.wait()316result = fut.result()317self.log.debug(f"waitforblockheight returned {result!r}")318assert_equal(result["height"], current_height)319node.wait_until_stopped()321def init_empty_test(self):322self.log.info("Test that stopping and restarting a node that has done nothing is not causing a failure")323options = [324[],325ALL_INDEX_ARGS,326]327for option in options:328self.restart_node(1, option)330def restart_node_with_fd_limit(self, limit):331"""Restart node 1 with a given soft RLIMIT_NOFILE. Skips if the limit cannot be set."""332import resource333soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)334try:335resource.setrlimit(resource.RLIMIT_NOFILE, (limit, hard))336except (ValueError, OSError):337self.log.warning(f"Skipping rlimit test: cannot set soft limit (hard={hard})")338return339try:340self.restart_node(1)341self.log.debug(f"Node started successfully with RLIM_INFINITY limit (soft={limit})")342finally:343resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard))344self.log.debug(f"Restored previous RLIMIT_NOFILE limits (soft={soft}, hard={hard})")346def init_rlimit_test(self):347"""Test that bitcoind starts correctly when the soft RLIMIT_NOFILE limit is RLIM_INFINITY."""348if self.RLIM_INFINITY is None:349self.log.warning("Skipping: resource module not available")350return352self.log.info("Testing node startup with RLIM_INFINITY fd limit")353self.restart_node_with_fd_limit(self.RLIM_INFINITY)355def init_rlimit_large_test(self):356"""Test that bitcoind starts correctly when the soft RLIMIT_NOFILE limit is above INT_MAX."""357if self.RLIM_INFINITY is None:358self.log.warning("Skipping: resource module not available")359return361self.log.info("Testing node startup with fd limit above INT_MAX")362self.restart_node_with_fd_limit(1 << 31)364def init_fd_overflow_test(self):365node = self.nodes[1]366if node.running:367self.stop_node(1)369# A value larger than any possible int saturates to INT_MAX during arg parsing.370# Adding in other file descriptor requirements is guaranteed to overflow,371# so expect an InitError before RaiseFileDescriptorLimit() is called.372self.log.info("Checking -rpcmaxconnections setting that would overflow int is rejected")373node.assert_start_raises_init_error(374extra_args=[f"-rpcmaxconnections={2**64}"],375expected_msg="Error: Too many file descriptors requested.",376match=ErrorMatch.PARTIAL_REGEX377)379self.log.info("Checking -rpcmaxconnections is ignored when disabling the HTTP server")380with node.assert_debug_log(381expected_msgs = ["net thread start"],382unexpected_msgs = ["Initialized HTTP server"],383timeout = 10384):385node.start(extra_args=[f"-rpcmaxconnections={2**64}", "-server=0"], **get_process_group_kwargs())386# No HTTP server, no RPC `stop`387sigterm_node(node)389# From net.h390MAX_PEER_CONNECTIONS = 2000392self.log.info("Checking -maxconnections is capped before reserving file descriptors")393for requested, parsed in [(MAX_PEER_CONNECTIONS + 1, MAX_PEER_CONNECTIONS + 1), (2**64, 2**31 - 1)]:394warning = f"Reducing -maxconnections from {parsed} to {MAX_PEER_CONNECTIONS}."395self.restart_node(1, extra_args=[f"-maxconnections={requested}"])396self.stop_node(1, expected_stderr=re.compile(re.escape(warning)))398if self.RLIM_INFINITY is not None:399# Get the platform's file descriptor limit, if possible400import resource401soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)402# Stay within the -maxconnections cap so the descriptor limit is what reduces it403soft = min(soft, MAX_PEER_CONNECTIONS)405# Lower the hard limit so RaiseFileDescriptorLimit() has a ceiling.406# The hard limit can not be raised again without root privilges,407# so this test should always be left for last in the process.408try:409resource.setrlimit(resource.RLIMIT_NOFILE, (soft, soft))410except (ValueError, OSError):411self.log.warning(f"Skipping rlimit test: cannot reduce hard limit (soft={soft}, hard={hard})")412return414self.log.info("Checking that large -maxconnections setting gets adjusted for available file descriptors")415# Note this prints a message to the log and stderr but does not abort the process416with node.assert_debug_log(expected_msgs=[f"Reducing -maxconnections from {soft} "]):417self.restart_node(1, extra_args=[f"-maxconnections={soft}"])418self.stop_node(1, expected_stderr=re.compile(f"Reducing -maxconnections from {soft} "))420# From httpserver.h421DEFAULT_MAX_HTTP_CONNECTIONS = 16423self.log.info("Checking -rpcmaxconnections gets blamed if available file descriptors are insufficient")424node.assert_start_raises_init_error(425extra_args=[f"-rpcmaxconnections={DEFAULT_MAX_HTTP_CONNECTIONS + 1}", f"-maxconnections={soft}"],426expected_msg="Not enough file descriptors available. Try reducing -rpcmaxconnections",427match=ErrorMatch.PARTIAL_REGEX428)430def run_test(self):431self.init_pid_test()432self.init_stress_test_interrupt()433self.init_stress_test_removals()434self.break_wait_test()435self.init_empty_test()436self.init_rlimit_test()437self.init_rlimit_large_test()438self.init_fd_overflow_test()441if __name__ == '__main__':442InitTest(__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.