refactor: Set TestNode.cli only after RPC is connected
What changed, and why it matters
This is a code cleanup in Bitcoin Core's internal testing tools. It changes when and how the test framework creates a command-line interface (CLI) helper for test nodes, and removes some redundant settings. There is no indication this fixes a security bug or affects live Bitcoin software.
No security action needed. Treat as ordinary test-framework refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors TestNodeCLI in the functional test framework. TestNode.cli is now initialized to None in init and only set after the RPC connection is established (inside wait_for_rpc_connection). The TestNodeCLI constructor no longer stores datadir or rpc_timeout; those are passed as command-line options when invoking bitcoin-cli. A manual cli override in rpc_bind.py is removed, and tool_signet_miner.py now reads node.datadir_path directly instead of node.cli.datadir.
Changed components
test/functional/test_framework/test_node.pytest/functional/rpc_bind.pytest/functional/tool_signet_miner.pyInspect captured patch +14 / −18
diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py
index 672db9b8..42278351 100755
--- a/test/functional/rpc_bind.py
+++ b/test/functional/rpc_bind.py
@@ -39,7 +39,6 @@ class RPCBindTest(BitcoinTestFramework):
base_args += ['-rpcallowip=' + x for x in allow_ips]
binds = ['-rpcbind='+addr for addr in addresses]
self.nodes[0].rpchost = connect_to
- self.nodes[0].cli = self.nodes[0].create_new_rpc_connection(mode="CLI") # Pass `connect_to` to cli.
self.start_node(0, base_args + binds)
pid = self.nodes[0].process.pid
assert_equal(set(get_bind_addrs(pid)), set(expected))
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index dbf2e6f0..6b7f2648 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -189,11 +189,7 @@ class TestNode():
self.args.append("-v2transport=0")
# if v2transport is requested via global flag but not supported for node version, ignore it
- self.cli = TestNodeCLI(
- binaries,
- self.datadir_path,
- self.rpc_timeout // 2, # timeout identical to the one used in self._rpc
- )
+ self.cli = None
self.use_cli = use_cli
self.start_perf = start_perf
@@ -328,7 +324,12 @@ class TestNode():
rpc.auth_service_proxy_instance.reuse_http_connections = self.reuse_http_connections
return rpc
else: # mode==CLI
- return self.cli(f"-rpcclienttimeout={client_timeout}", f"-rpcconnect={host}", f"-rpcport={port}")
+ return TestNodeCLI(self.binaries)(
+ f"-datadir={self.datadir_path}",
+ f"-rpcclienttimeout={client_timeout}",
+ f"-rpcconnect={host}",
+ f"-rpcport={port}",
+ )
def wait_for_rpc_connection(self, *, wait_for_import=True):
"""Sets up an RPC connection to the bitcoind process. Returns False if unable to connect."""
@@ -378,6 +379,7 @@ class TestNode():
self.log.debug("RPC successfully started")
# Set rpc_connected even if we are in use_cli mode so that we know we can call self.stop() if needed.
self.rpc_connected = True
+ self.cli = self.create_new_rpc_connection(mode="CLI")
if self.use_cli:
return
self._rpc = rpc
@@ -977,18 +979,16 @@ def arg_to_cli(arg):
class TestNodeCLI():
"""Interface to bitcoin-cli for an individual node"""
- def __init__(self, binaries, datadir, rpc_timeout):
+ def __init__(self, binaries):
self.options = []
self.binaries = binaries
- self.datadir = datadir
- self.rpc_timeout = rpc_timeout
self.input = None
self.log = logging.getLogger('TestFramework.bitcoincli')
def __call__(self, *options, input=None):
# TestNodeCLI is callable with bitcoin-cli command-line options
- cli = TestNodeCLI(self.binaries, self.datadir, self.rpc_timeout)
- cli.options = [str(o) for o in options]
+ cli = TestNodeCLI(self.binaries)
+ cli.options = self.options + [str(o) for o in options]
cli.input = input
return cli
@@ -1008,10 +1008,7 @@ class TestNodeCLI():
"""Run bitcoin-cli command. Deserializes returned string as python object."""
pos_args = [arg_to_cli(arg) for arg in args]
named_args = [key + "=" + arg_to_cli(value) for (key, value) in kwargs.items() if value is not None]
- p_args = self.binaries.rpc_argv() + [
- f"-datadir={self.datadir}",
- f"-rpcclienttimeout={int(self.rpc_timeout)}",
- ] + self.options
+ p_args = self.binaries.rpc_argv() + self.options
if named_args:
p_args += ["-named"]
base_arg_pos = len(p_args)
diff --git a/test/functional/tool_signet_miner.py b/test/functional/tool_signet_miner.py
index 2be47332..cb22dd92 100755
--- a/test/functional/tool_signet_miner.py
+++ b/test/functional/tool_signet_miner.py
@@ -69,7 +69,7 @@ class SignetMinerTest(BitcoinTestFramework):
n_blocks = node.getblockcount()
base_dir = self.config["environment"]["SRCDIR"]
signet_miner_path = os.path.join(base_dir, "contrib", "signet", "miner")
- rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.cli.datadir}"]
+ rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.datadir_path}"]
util_argv = node.binaries.util_argv() + ["grind"]
subprocess.run([
sys.executable,
@@ -89,7 +89,7 @@ class SignetMinerTest(BitcoinTestFramework):
n_blocks = node.getblockcount()
base_dir = self.config["environment"]["SRCDIR"]
signet_miner_path = os.path.join(base_dir, "contrib", "signet", "miner")
- rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.cli.datadir}"]
+ rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.datadir_path}"]
util_argv = node.binaries.util_argv() + ["grind"]
base_cmd = [
sys.executable,
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.