mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-01 16:53:52 +02:00
Merge bitcoin/bitcoin#31212: util: Improve documentation and negation of args
95a0104f2etest: Add tests for directories in place of config files (Hodlinator)e85abe92c7args: Catch directories in place of config files (Hodlinator)e4b6b1822ctest: Add tests for -noconf (Hodlinator)483f0dacc4args: Properly support -noconf (Hodlinator)312ec64cc0test refactor: feature_config_args.py - Stop nodes at the end of tests, not at the beginning (Hodlinator)7402658bc2test: -norpccookiefile (Hodlinator)39cbd4f37cargs: Support -norpccookiefile for bitcoind and bitcoin-cli (Hodlinator)e82ad88452logs: Use correct path and more appropriate macros in cookie-related code (Hodlinator)6e28c76907test: Harden testing of cookie file existence (Hodlinator)75bacabb55test: combine_logs.py - Output debug.log paths on error (Hodlinator)bffd92f00fargs: Support -nopid (Hodlinator)12f8d848fdargs: Disallow -nodatadir (Hodlinator)6ff9662760scripted-diff: Avoid printing version information for -noversion (Hodlinator)e8a2054edcdoc args: Document narrow scope of -color (Hodlinator) Pull request description: - Document `-color` as only applying to `-getinfo`, to be less confusing for bitcoin-cli users. - No longer print version information when getting passed `-noversion`. - Disallow `-nodatadir` as we cannot run without one. It was previously interpreted as a mix of unset and as a relative path of "0". - Support `-norpccookiefile` - Support `-nopid` - Properly support `-noconf` (instead of working by accident). Also detect when directories are specified instead of files. Prompted by investigation in https://github.com/bitcoin/bitcoin/pull/16545#pullrequestreview-2316714013. ACKs for top commit: l0rinc: utACK95a0104f2eachow101: ACK95a0104f2eryanofsky: Code review ACK95a0104f2e. Looks good! Thanks for all your work on this breaking the changes down and making them simple. Tree-SHA512: 5174251e6b9196a9c6d135eddcb94130295c551bcfccc78e633d9e118ff91523b1be0d72828fb49603ceae312e6e1f8ee2651c6a2b9e0f195603a73a9a622785
This commit is contained in:
@@ -79,11 +79,12 @@ def read_logs(tmp_dir):
|
||||
Delegates to generator function get_log_events() to provide individual log events
|
||||
for each of the input log files."""
|
||||
|
||||
# Find out what the folder is called that holds the debug.log file
|
||||
glob = pathlib.Path(tmp_dir).glob('node0/**/debug.log')
|
||||
path = next(glob, None)
|
||||
if path:
|
||||
assert next(glob, None) is None # more than one debug.log, should never happen
|
||||
# Find out what the folder is called that holds node 0's debug.log file
|
||||
debug_logs = list(pathlib.Path(tmp_dir).glob('node0/**/debug.log'))
|
||||
if len(debug_logs) > 0:
|
||||
assert len(debug_logs) < 2, 'Max one debug.log is supported, ' \
|
||||
'found several:\n\t' + '\n\t'.join([str(f) for f in debug_logs])
|
||||
path = debug_logs[0]
|
||||
chain = re.search(r'node0/(.+?)/debug\.log$', path.as_posix()).group(1) # extract the chain name
|
||||
else:
|
||||
chain = 'regtest' # fallback to regtest (should only happen when none exists)
|
||||
|
||||
@@ -27,9 +27,74 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
self.wallet_names = []
|
||||
self.disable_autoconnect = False
|
||||
|
||||
# Overridden to avoid attempt to sync not yet started nodes.
|
||||
def setup_network(self):
|
||||
self.setup_nodes()
|
||||
|
||||
# Overriden to not start nodes automatically - doing so is the
|
||||
# responsibility of each test function.
|
||||
def setup_nodes(self):
|
||||
self.add_nodes(self.num_nodes, self.extra_args)
|
||||
# Ensure a log file exists as TestNode.assert_debug_log() expects it.
|
||||
self.nodes[0].debug_log_path.parent.mkdir()
|
||||
self.nodes[0].debug_log_path.touch()
|
||||
|
||||
def test_dir_config(self):
|
||||
self.log.info('Error should be emitted if config file is a directory')
|
||||
conf_path = self.nodes[0].datadir_path / 'bitcoin.conf'
|
||||
os.rename(conf_path, conf_path.with_suffix('.confbkp'))
|
||||
conf_path.mkdir()
|
||||
self.stop_node(0)
|
||||
self.nodes[0].assert_start_raises_init_error(
|
||||
extra_args=['-regtest'],
|
||||
expected_msg=f'Error: Error reading configuration file: Config file "{conf_path}" is a directory.',
|
||||
)
|
||||
conf_path.rmdir()
|
||||
os.rename(conf_path.with_suffix('.confbkp'), conf_path)
|
||||
|
||||
self.log.debug('Verifying includeconf directive pointing to directory is caught')
|
||||
with open(conf_path, 'a', encoding='utf-8') as conf:
|
||||
conf.write(f'includeconf={self.nodes[0].datadir_path}\n')
|
||||
self.nodes[0].assert_start_raises_init_error(
|
||||
extra_args=['-regtest'],
|
||||
expected_msg=f'Error: Error reading configuration file: Included config file "{self.nodes[0].datadir_path}" is a directory.',
|
||||
)
|
||||
|
||||
self.nodes[0].replace_in_config([(f'includeconf={self.nodes[0].datadir_path}', '')])
|
||||
|
||||
def test_negated_config(self):
|
||||
self.log.info('Disabling configuration via -noconf')
|
||||
|
||||
conf_path = self.nodes[0].datadir_path / 'bitcoin.conf'
|
||||
with open(conf_path, encoding='utf-8') as conf:
|
||||
settings = [f'-{line.rstrip()}' for line in conf if len(line) > 1 and line[0] != '[']
|
||||
os.rename(conf_path, conf_path.with_suffix('.confbkp'))
|
||||
|
||||
self.log.debug('Verifying garbage in config can be detected')
|
||||
with open(conf_path, 'a', encoding='utf-8') as conf:
|
||||
conf.write(f'garbage\n')
|
||||
self.nodes[0].assert_start_raises_init_error(
|
||||
extra_args=['-regtest'],
|
||||
expected_msg='Error: Error reading configuration file: parse error on line 1: garbage',
|
||||
)
|
||||
|
||||
self.log.debug('Verifying that disabling of the config file means garbage inside of it does ' \
|
||||
'not prevent the node from starting, and message about existing config file is logged')
|
||||
ignored_file_message = [f'[InitConfig] Data directory "{self.nodes[0].datadir_path}" contains a "bitcoin.conf" file which is explicitly ignored using -noconf.']
|
||||
with self.nodes[0].assert_debug_log(timeout=60, expected_msgs=ignored_file_message):
|
||||
self.start_node(0, extra_args=settings + ['-noconf'])
|
||||
self.stop_node(0)
|
||||
|
||||
self.log.debug('Verifying no message appears when removing config file')
|
||||
os.remove(conf_path)
|
||||
with self.nodes[0].assert_debug_log(timeout=60, expected_msgs=[], unexpected_msgs=ignored_file_message):
|
||||
self.start_node(0, extra_args=settings + ['-noconf'])
|
||||
self.stop_node(0)
|
||||
|
||||
os.rename(conf_path.with_suffix('.confbkp'), conf_path)
|
||||
|
||||
def test_config_file_parser(self):
|
||||
self.log.info('Test config file parser')
|
||||
self.stop_node(0)
|
||||
|
||||
# Check that startup fails if conf= is set in bitcoin.conf or in an included conf file
|
||||
bad_conf_file_path = self.nodes[0].datadir_path / "bitcoin_bad.conf"
|
||||
@@ -162,12 +227,11 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
)
|
||||
|
||||
def test_log_buffer(self):
|
||||
self.stop_node(0)
|
||||
with self.nodes[0].assert_debug_log(expected_msgs=['Warning: parsed potentially confusing double-negative -connect=0\n']):
|
||||
self.start_node(0, extra_args=['-noconnect=0'])
|
||||
self.stop_node(0)
|
||||
|
||||
def test_args_log(self):
|
||||
self.stop_node(0)
|
||||
self.log.info('Test config args logging')
|
||||
with self.nodes[0].assert_debug_log(
|
||||
expected_msgs=[
|
||||
@@ -196,10 +260,10 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
'-rpcuser=secret-rpcuser',
|
||||
'-torpassword=secret-torpassword',
|
||||
])
|
||||
self.stop_node(0)
|
||||
|
||||
def test_networkactive(self):
|
||||
self.log.info('Test -networkactive option')
|
||||
self.stop_node(0)
|
||||
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
|
||||
self.start_node(0)
|
||||
|
||||
@@ -222,16 +286,12 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
self.stop_node(0)
|
||||
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
|
||||
self.start_node(0, extra_args=['-nonetworkactive=1'])
|
||||
self.stop_node(0)
|
||||
|
||||
def test_seed_peers(self):
|
||||
self.log.info('Test seed peers')
|
||||
default_data_dir = self.nodes[0].datadir_path
|
||||
peer_dat = default_data_dir / 'peers.dat'
|
||||
# Only regtest has no fixed seeds. To avoid connections to random
|
||||
# nodes, regtest is the only network where it is safe to enable
|
||||
# -fixedseeds in tests
|
||||
util.assert_equal(self.nodes[0].getblockchaininfo()['chain'],'regtest')
|
||||
self.stop_node(0)
|
||||
|
||||
# No peers.dat exists and -dnsseed=1
|
||||
# We expect the node will use DNS Seeds, but Regtest mode does not have
|
||||
@@ -248,6 +308,12 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
timeout=10,
|
||||
):
|
||||
self.start_node(0, extra_args=['-dnsseed=1', '-fixedseeds=1', f'-mocktime={start}'])
|
||||
|
||||
# Only regtest has no fixed seeds. To avoid connections to random
|
||||
# nodes, regtest is the only network where it is safe to enable
|
||||
# -fixedseeds in tests
|
||||
util.assert_equal(self.nodes[0].getblockchaininfo()['chain'],'regtest')
|
||||
|
||||
with self.nodes[0].assert_debug_log(expected_msgs=[
|
||||
"Adding fixed seeds as 60 seconds have passed and addrman is empty",
|
||||
]):
|
||||
@@ -294,13 +360,13 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
"Adding fixed seeds as 60 seconds have passed and addrman is empty",
|
||||
]):
|
||||
self.nodes[0].setmocktime(start + 65)
|
||||
self.stop_node(0)
|
||||
|
||||
def test_connect_with_seednode(self):
|
||||
self.log.info('Test -connect with -seednode')
|
||||
seednode_ignored = ['-seednode is ignored when -connect is used\n']
|
||||
dnsseed_ignored = ['-dnsseed is ignored when -connect is used and -proxy is specified\n']
|
||||
addcon_thread_started = ['addcon thread start\n']
|
||||
self.stop_node(0)
|
||||
|
||||
# When -connect is supplied, expanding addrman via getaddr calls to ADDR_FETCH(-seednode)
|
||||
# nodes is irrelevant and -seednode is ignored.
|
||||
@@ -325,6 +391,7 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
with self.nodes[0].assert_debug_log(expected_msgs=addcon_thread_started,
|
||||
unexpected_msgs=seednode_ignored):
|
||||
self.restart_node(0, extra_args=[connect_arg, '-seednode=fakeaddress2'])
|
||||
self.stop_node(0)
|
||||
|
||||
def test_ignored_conf(self):
|
||||
self.log.info('Test error is triggered when the datadir in use contains a bitcoin.conf file that would be ignored '
|
||||
@@ -423,6 +490,8 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
self.test_networkactive()
|
||||
self.test_connect_with_seednode()
|
||||
|
||||
self.test_dir_config()
|
||||
self.test_negated_config()
|
||||
self.test_config_file_parser()
|
||||
self.test_config_file_log()
|
||||
self.test_invalid_command_line_options()
|
||||
|
||||
@@ -164,6 +164,9 @@ class TestBitcoinCli(BitcoinTestFramework):
|
||||
self.log.info("Test connecting with non-existing RPC cookie file")
|
||||
assert_raises_process_error(1, "Could not locate RPC credentials", self.nodes[0].cli('-rpccookiefile=does-not-exist', '-rpcpassword=').echo)
|
||||
|
||||
self.log.info("Test connecting without RPC cookie file and with password arg")
|
||||
assert_equal(BLOCKS, self.nodes[0].cli('-norpccookiefile', f'-rpcuser={user}', f'-rpcpassword={password}').getblockcount())
|
||||
|
||||
self.log.info("Test -getinfo with arguments fails")
|
||||
assert_raises_process_error(1, "-getinfo takes no arguments", self.nodes[0].cli('-getinfo').help)
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@ import sys
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def call_with_auth(node, user, password):
|
||||
def call_with_auth(node, user, password, method="getbestblockhash"):
|
||||
url = urllib.parse.urlparse(node.url)
|
||||
headers = {"Authorization": "Basic " + str_to_b64str('{}:{}'.format(user, password))}
|
||||
|
||||
conn = http.client.HTTPConnection(url.hostname, url.port)
|
||||
conn.connect()
|
||||
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
|
||||
conn.request('POST', '/', f'{{"method": "{method}"}}', headers)
|
||||
resp = conn.getresponse()
|
||||
conn.close()
|
||||
return resp
|
||||
@@ -121,6 +121,25 @@ class HTTPBasicsTest(BitcoinTestFramework):
|
||||
for perm in ["owner", "group", "all"]:
|
||||
test_perm(perm)
|
||||
|
||||
def test_norpccookiefile(self, node0_cookie_path):
|
||||
assert self.nodes[0].is_node_stopped(), "We expect previous test to stopped the node"
|
||||
assert not node0_cookie_path.exists()
|
||||
|
||||
self.log.info('Starting with -norpccookiefile')
|
||||
# Start, but don't wait for RPC connection as TestNode.wait_for_rpc_connection() requires the cookie.
|
||||
with self.nodes[0].busy_wait_for_debug_log([b'init message: Done loading']):
|
||||
self.nodes[0].start(extra_args=["-norpccookiefile"])
|
||||
assert not node0_cookie_path.exists()
|
||||
|
||||
self.log.info('Testing user/password authentication still works without cookie file')
|
||||
assert_equal(200, call_with_auth(self.nodes[0], "rt", self.rtpassword).status)
|
||||
# After confirming that we could log in, check that cookie file does not exist.
|
||||
assert not node0_cookie_path.exists()
|
||||
|
||||
# Need to shut down in slightly unorthodox way since cookie auth can't be used
|
||||
assert_equal(200, call_with_auth(self.nodes[0], "rt", self.rtpassword, method="stop").status)
|
||||
self.nodes[0].wait_until_stopped()
|
||||
|
||||
def run_test(self):
|
||||
self.conf_setup()
|
||||
self.log.info('Check correctness of the rpcauth config option')
|
||||
@@ -166,11 +185,19 @@ class HTTPBasicsTest(BitcoinTestFramework):
|
||||
self.stop_node(0)
|
||||
|
||||
self.log.info('Check that failure to write cookie file will abort the node gracefully')
|
||||
(self.nodes[0].chain_path / ".cookie.tmp").mkdir()
|
||||
cookie_path = self.nodes[0].chain_path / ".cookie"
|
||||
cookie_path_tmp = self.nodes[0].chain_path / ".cookie.tmp"
|
||||
cookie_path_tmp.mkdir()
|
||||
self.nodes[0].assert_start_raises_init_error(expected_msg=init_error)
|
||||
cookie_path_tmp.rmdir()
|
||||
assert not cookie_path.exists()
|
||||
self.restart_node(0)
|
||||
assert cookie_path.exists()
|
||||
self.stop_node(0)
|
||||
|
||||
self.test_rpccookieperms()
|
||||
|
||||
self.test_norpccookiefile(cookie_path)
|
||||
|
||||
if __name__ == '__main__':
|
||||
HTTPBasicsTest(__file__).main()
|
||||
|
||||
Reference in New Issue
Block a user