1 #!/usr/bin/env python3
2 # Copyright (c) 2017-present The Limenka developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test various command line arguments and configuration file parameters."""
6 7 import os
8 from pathlib import Path
9 import platform
10 import re
11 import tempfile
12 import time
13 14 from test_framework.netutil import UNREACHABLE_PROXY_ARG
15 from test_framework.test_framework import LimenkaTestFramework
16 from test_framework.test_node import ErrorMatch
17 from test_framework import util
18 19 20 class ConfArgsTest(LimenkaTestFramework):
21 def add_options(self, parser):
22 self.add_wallet_options(parser)
23 24 def set_test_params(self):
25 self.setup_clean_chain = True
26 self.num_nodes = 1
27 # Prune to prevent disk space warning on CI systems with limited space,
28 # when using networks other than regtest.
29 self.extra_args = [["-prune=550"]]
30 self.supports_cli = False
31 self.wallet_names = []
32 self.disable_autoconnect = False
33 34 # Overridden to avoid attempt to sync not yet started nodes.
35 def setup_network(self):
36 self.setup_nodes()
37 38 # Overridden to not start nodes automatically - doing so is the
39 # responsibility of each test function.
40 def setup_nodes(self):
41 self.add_nodes(self.num_nodes, self.extra_args)
42 # Ensure a log file exists as TestNode.assert_debug_log() expects it.
43 self.nodes[0].debug_log_path.parent.mkdir()
44 self.nodes[0].debug_log_path.touch()
45 46 def test_dir_config(self):
47 self.log.info('Error should be emitted if config file is a directory')
48 conf_path = self.nodes[0].datadir_path / 'limenka.conf'
49 os.rename(conf_path, conf_path.with_suffix('.confbkp'))
50 conf_path.mkdir()
51 self.stop_node(0)
52 self.nodes[0].assert_start_raises_init_error(
53 extra_args=['-regtest'],
54 expected_msg=f'Error: Error reading configuration file: Config file "{conf_path}" is a directory.',
55 )
56 conf_path.rmdir()
57 os.rename(conf_path.with_suffix('.confbkp'), conf_path)
58 59 self.log.debug('Verifying includeconf directive pointing to directory is caught')
60 with open(conf_path, 'a', encoding='utf-8') as conf:
61 conf.write(f'includeconf={self.nodes[0].datadir_path}\n')
62 self.nodes[0].assert_start_raises_init_error(
63 extra_args=['-regtest'],
64 expected_msg=f'Error: Error reading configuration file: Included config file "{self.nodes[0].datadir_path}" is a directory.',
65 )
66 67 self.nodes[0].replace_in_config([(f'includeconf={self.nodes[0].datadir_path}', '')])
68 69 def test_negated_config(self):
70 self.log.info('Disabling configuration via -noconf')
71 72 conf_path = self.nodes[0].datadir_path / 'limenka.conf'
73 with open(conf_path, encoding='utf-8') as conf:
74 settings = [f'-{line.rstrip()}' for line in conf if len(line) > 1 and line[0] != '[']
75 os.rename(conf_path, conf_path.with_suffix('.confbkp'))
76 77 self.log.debug('Verifying garbage in config can be detected')
78 with open(conf_path, 'a', encoding='utf-8') as conf:
79 conf.write('garbage\n')
80 self.nodes[0].assert_start_raises_init_error(
81 extra_args=['-regtest'],
82 expected_msg='Error: Error reading configuration file: parse error on line 1: garbage',
83 )
84 85 self.log.debug('Verifying that disabling of the config file means garbage inside of it does ' \
86 'not prevent the node from starting, and message about existing config file is logged')
87 ignored_file_message = [f'Data directory "{self.nodes[0].datadir_path}" contains a "limenka.conf" file which is explicitly ignored using -noconf.']
88 with self.nodes[0].assert_debug_log(timeout=60, expected_msgs=ignored_file_message):
89 self.start_node(0, extra_args=settings + ['-noconf'])
90 self.stop_node(0)
91 92 self.log.debug('Verifying no message appears when removing config file')
93 os.remove(conf_path)
94 with self.nodes[0].assert_debug_log(timeout=60, expected_msgs=[], unexpected_msgs=ignored_file_message):
95 self.start_node(0, extra_args=settings + ['-noconf'])
96 self.stop_node(0)
97 98 os.rename(conf_path.with_suffix('.confbkp'), conf_path)
99 100 def test_config_file_parser(self):
101 self.log.info('Test config file parser')
102 103 # Check that startup fails if conf= is set in limenka.conf or in an included conf file
104 bad_conf_file_path = self.nodes[0].datadir_path / "limenka_bad.conf"
105 util.write_config(bad_conf_file_path, n=0, chain='', extra_config='conf=some.conf\n')
106 conf_in_config_file_err = 'Error: Error reading configuration file: conf cannot be set in the configuration file; use includeconf= if you want to include additional config files'
107 self.nodes[0].assert_start_raises_init_error(
108 extra_args=[f'-conf={bad_conf_file_path}'],
109 expected_msg=conf_in_config_file_err,
110 )
111 inc_conf_file_path = self.nodes[0].datadir_path / 'include.conf'
112 with open(self.nodes[0].datadir_path / 'limenka.conf', 'a', encoding='utf-8') as conf:
113 conf.write(f'includeconf={inc_conf_file_path}\n')
114 with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
115 conf.write('conf=some.conf\n')
116 self.nodes[0].assert_start_raises_init_error(
117 expected_msg=conf_in_config_file_err,
118 )
119 120 self.nodes[0].assert_start_raises_init_error(
121 expected_msg='Error: Error parsing command line arguments: Invalid parameter -dash_cli=1',
122 extra_args=['-dash_cli=1'],
123 )
124 with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
125 conf.write('dash_conf=1\n')
126 127 with self.nodes[0].assert_debug_log(expected_msgs=['Ignoring unknown configuration value dash_conf']):
128 self.start_node(0)
129 self.stop_node(0)
130 131 with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
132 conf.write('reindex=1\n')
133 134 with self.nodes[0].assert_debug_log(expected_msgs=["[warning] reindex=1 is set in the configuration file, which will significantly slow down startup. Consider removing or commenting out this option for better performance, unless there is currently a condition which makes rebuilding the indexes necessary"]):
135 self.start_node(0)
136 self.stop_node(0)
137 138 with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
139 conf.write('-dash=1\n')
140 self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 1: -dash=1, options in configuration file must be specified without leading -')
141 142 if self.is_wallet_compiled():
143 with open(inc_conf_file_path, 'w', encoding='utf8') as conf:
144 conf.write("wallet=foo\n")
145 self.nodes[0].assert_start_raises_init_error(expected_msg=f'Error: Config setting for -wallet only applied on {self.chain} network when in [{self.chain}] section.')
146 147 main_conf_file_path = self.nodes[0].datadir_path / "limenka_main.conf"
148 util.write_config(main_conf_file_path, n=0, chain='', extra_config=f'includeconf={inc_conf_file_path}\n')
149 150 with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
151 conf.write('nono\n')
152 self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 1: nono, if you intended to specify a negated option, use nono=1 instead')
153 154 with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
155 conf.write('server=1\nrpcuser=someuser\nrpcpassword=some#pass')
156 self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
157 158 with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
159 conf.write('server=1\nrpcuser=someuser\nmain.rpcpassword=some#pass')
160 self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
161 162 with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
163 conf.write('server=1\nrpcuser=someuser\n[main]\nrpcpassword=some#pass')
164 self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Error reading configuration file: parse error on line 4, using # in rpcpassword can be ambiguous and should be avoided')
165 166 inc_conf_file2_path = self.nodes[0].datadir_path / 'include2.conf'
167 with open(self.nodes[0].datadir_path / 'limenka.conf', 'a', encoding='utf-8') as conf:
168 conf.write(f'includeconf={inc_conf_file2_path}\n')
169 170 with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
171 conf.write('testnot.datadir=1\n')
172 with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf:
173 conf.write('[testnet]\n')
174 self.restart_node(0)
175 self.nodes[0].stop_node(expected_stderr=f'Warning: {inc_conf_file_path}:1 Section [testnot] is not recognized.{os.linesep}{inc_conf_file2_path}:1 Section [testnet] is not recognized.')
176 177 with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
178 conf.write('') # clear
179 with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf:
180 conf.write('') # clear
181 182 def test_config_file_log(self):
183 # Disable this test for windows currently because trying to override
184 # the default datadir through the environment does not seem to work.
185 if platform.system() == "Windows":
186 return
187 188 self.log.info('Test that correct configuration path is changed when configuration file changes the datadir')
189 190 # Create a temporary directory that will be treated as the default data
191 # directory by limenkad.
192 env, default_datadir = util.get_temp_default_datadir(Path(self.options.tmpdir, "test_config_file_log"))
193 default_datadir.mkdir(parents=True)
194 195 # Write a limenka.conf file in the default data directory containing a
196 # datadir= line pointing at the node datadir.
197 node = self.nodes[0]
198 conf_text = node.limenkaconf.read_text()
199 conf_path = default_datadir / "limenka.conf"
200 conf_path.write_text(f"datadir={node.datadir_path}\n{conf_text}")
201 202 # Drop the node -datadir= argument during this test, because if it is
203 # specified it would take precedence over the datadir setting in the
204 # config file.
205 node_args = node.args
206 node.args = [arg for arg in node.args if not arg.startswith("-datadir=")]
207 208 # Check that correct configuration file path is actually logged
209 # (conf_path, not node.limenkaconf)
210 with self.nodes[0].assert_debug_log(expected_msgs=[f"Config file: {conf_path}"]):
211 self.start_node(0, ["-allowignoredconf"], env=env)
212 self.stop_node(0)
213 214 # Restore node arguments after the test
215 node.args = node_args
216 217 def test_invalid_command_line_options(self):
218 self.nodes[0].assert_start_raises_init_error(
219 expected_msg='Error: Error parsing command line arguments: Can not set -proxy with no value. Please specify value with -proxy=value.',
220 extra_args=['-proxy'],
221 )
222 # Provide a value different from 1 to the -wallet negated option
223 if self.is_wallet_compiled():
224 for value in [0, 'not_a_boolean']:
225 self.nodes[0].assert_start_raises_init_error(
226 expected_msg="Error: Invalid value detected for '-wallet' or '-nowallet'. '-wallet' requires a string value, while '-nowallet' accepts only '1' to disable all wallets",
227 extra_args=[f'-nowallet={value}'],
228 )
229 230 def test_log_buffer(self):
231 with self.nodes[0].assert_debug_log(expected_msgs=["[warning] Parsed potentially confusing double-negative -listen=0\n"]):
232 self.start_node(0, extra_args=['-nolisten=0'])
233 self.stop_node(0)
234 235 def test_args_log(self):
236 self.log.info('Test config args logging')
237 with self.nodes[0].assert_debug_log(
238 expected_msgs=[
239 'Command-line arg: addnode="some.node"',
240 'Command-line arg: rpcauth=****',
241 'Command-line arg: rpcpassword=****',
242 'Command-line arg: rpcuser=****',
243 'Command-line arg: torpassword=****',
244 f'Config file arg: {self.chain}="1"',
245 f'Config file arg: [{self.chain}] server="1"',
246 ],
247 unexpected_msgs=[
248 'alice:f7efda5c189b999524f151318c0c86$d5b51b3beffbc0',
249 'secret-rpcuser',
250 'secret-torpassword',
251 'Command-line arg: rpcbind=****',
252 'Command-line arg: rpcallowip=****',
253 ]):
254 self.start_node(0, extra_args=[
255 '-addnode=some.node',
256 '-rpcauth=alice:f7efda5c189b999524f151318c0c86$d5b51b3beffbc0',
257 '-rpcbind=127.0.0.1',
258 "-rpcallowip=127.0.0.1",
259 '-rpcpassword=',
260 '-rpcuser=secret-rpcuser',
261 '-torpassword=secret-torpassword',
262 UNREACHABLE_PROXY_ARG,
263 ])
264 self.stop_node(0)
265 266 def test_networkactive(self):
267 self.log.info('Test -networkactive option')
268 with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
269 self.start_node(0)
270 271 self.stop_node(0)
272 with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
273 self.start_node(0, extra_args=['-networkactive'])
274 275 self.stop_node(0)
276 with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']):
277 self.start_node(0, extra_args=['-networkactive=1'])
278 279 self.stop_node(0)
280 with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
281 self.start_node(0, extra_args=['-networkactive=0'])
282 283 self.stop_node(0)
284 with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
285 self.start_node(0, extra_args=['-nonetworkactive'])
286 287 self.stop_node(0)
288 with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
289 self.start_node(0, extra_args=['-nonetworkactive=1'])
290 self.stop_node(0)
291 292 def test_seed_peers(self):
293 self.log.info('Test seed peers')
294 default_data_dir = self.nodes[0].datadir_path
295 peer_dat = default_data_dir / 'peers.dat'
296 297 # No peers.dat exists and -dnsseed=1
298 # We expect the node will use DNS Seeds, but Regtest mode does not have
299 # any valid DNS seeds. So after 60 seconds, the node should fallback to
300 # fixed seeds
301 assert not peer_dat.exists()
302 start = int(time.time())
303 with self.nodes[0].assert_debug_log(
304 expected_msgs=[
305 "Loaded 0 addresses from peers.dat",
306 "0 addresses found from DNS seeds",
307 "opencon thread start", # Ensure ThreadOpenConnections::start time is properly set
308 ],
309 timeout=10,
310 ):
311 self.start_node(0, extra_args=['-dnsseed=1', '-fixedseeds=1', f'-mocktime={start}', UNREACHABLE_PROXY_ARG])
312 313 # Only regtest has no fixed seeds. To avoid connections to random
314 # nodes, regtest is the only network where it is safe to enable
315 # -fixedseeds in tests
316 util.assert_equal(self.nodes[0].getblockchaininfo()['chain'],'regtest')
317 318 with self.nodes[0].assert_debug_log(expected_msgs=[
319 "Adding fixed seeds as 60 seconds have passed and addrman is empty",
320 ]):
321 self.nodes[0].setmocktime(start + 65)
322 self.stop_node(0)
323 324 # No peers.dat exists and -dnsseed=0
325 # We expect the node will fallback immediately to fixed seeds
326 assert not peer_dat.exists()
327 with self.nodes[0].assert_debug_log(expected_msgs=[
328 "Loaded 0 addresses from peers.dat",
329 "DNS seeding disabled",
330 "Adding fixed seeds as -dnsseed=0 (or IPv4/IPv6 connections are disabled via -onlynet) and neither -addnode nor -seednode are provided\n",
331 ]):
332 self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=1'])
333 self.stop_node(0)
334 self.nodes[0].assert_start_raises_init_error(['-dnsseed=1', '-onlynet=i2p', '-i2psam=127.0.0.1:7656'], "Error: Incompatible options: -dnsseed=1 was explicitly specified, but -onlynet forbids connections to IPv4/IPv6")
335 336 # No peers.dat exists and dns seeds are disabled.
337 # We expect the node will not add fixed seeds when explicitly disabled.
338 assert not peer_dat.exists()
339 with self.nodes[0].assert_debug_log(expected_msgs=[
340 "Loaded 0 addresses from peers.dat",
341 "DNS seeding disabled",
342 "Fixed seeds are disabled",
343 ], timeout=2):
344 self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=0'])
345 self.stop_node(0)
346 347 # No peers.dat exists and -dnsseed=0, but a -addnode is provided
348 # We expect the node will allow 60 seconds prior to using fixed seeds
349 assert not peer_dat.exists()
350 start = int(time.time())
351 with self.nodes[0].assert_debug_log(
352 expected_msgs=[
353 "Loaded 0 addresses from peers.dat",
354 "DNS seeding disabled",
355 "opencon thread start", # Ensure ThreadOpenConnections::start time is properly set
356 ],
357 timeout=10,
358 ):
359 self.start_node(0, extra_args=['-dnsseed=0', '-fixedseeds=1', '-addnode=fakenodeaddr', f'-mocktime={start}', UNREACHABLE_PROXY_ARG])
360 with self.nodes[0].assert_debug_log(expected_msgs=[
361 "Adding fixed seeds as 60 seconds have passed and addrman is empty",
362 ]):
363 self.nodes[0].setmocktime(start + 65)
364 self.stop_node(0)
365 366 def test_connect_with_seednode(self):
367 self.log.info('Test -connect with -seednode')
368 seednode_ignored = ['-seednode is ignored when -connect is used\n']
369 dnsseed_ignored = ['-dnsseed is ignored when -connect is used and -proxy is specified\n']
370 addcon_thread_started = ['addcon thread start\n']
371 dnsseed_disabled = "parameter interaction: -connect or -maxconnections=0 set -> setting -dnsseed=0"
372 listen_disabled = "parameter interaction: -connect or -maxconnections=0 set -> setting -listen=0"
373 374 # When -connect is supplied, expanding addrman via getaddr calls to ADDR_FETCH(-seednode)
375 # nodes is irrelevant and -seednode is ignored.
376 with self.nodes[0].assert_debug_log(expected_msgs=seednode_ignored):
377 self.start_node(0, extra_args=['-connect=fakeaddress1', '-seednode=fakeaddress2', UNREACHABLE_PROXY_ARG])
378 379 # With -proxy, an ADDR_FETCH connection is made to a peer that the dns seed resolves to.
380 # ADDR_FETCH connections are not used when -connect is used.
381 with self.nodes[0].assert_debug_log(expected_msgs=dnsseed_ignored):
382 self.restart_node(0, extra_args=['-connect=fakeaddress1', '-dnsseed=1', UNREACHABLE_PROXY_ARG])
383 384 # If the user did not disable -dnsseed, but it was soft-disabled because they provided -connect,
385 # they shouldn't see a warning about -dnsseed being ignored.
386 with self.nodes[0].assert_debug_log(expected_msgs=addcon_thread_started,
387 unexpected_msgs=dnsseed_ignored, timeout=2):
388 self.restart_node(0, extra_args=['-connect=fakeaddress1', UNREACHABLE_PROXY_ARG])
389 390 # We have to supply expected_msgs as it's a required argument
391 # The expected_msg must be something we are confident will be logged after the unexpected_msg
392 # These cases test for -connect being supplied but only to disable it
393 for connect_arg in ['-connect=0', '-noconnect']:
394 with self.nodes[0].assert_debug_log(expected_msgs=addcon_thread_started,
395 unexpected_msgs=seednode_ignored, timeout=2):
396 self.restart_node(0, extra_args=[connect_arg, '-seednode=fakeaddress2'])
397 398 # Make sure -noconnect soft-disables -listen and -dnsseed.
399 # Need to temporarily remove these settings from the config file in
400 # order for the two log messages to appear
401 self.nodes[0].replace_in_config([("bind=", "#bind="), ("dnsseed=", "#dnsseed=")])
402 with self.nodes[0].assert_debug_log(expected_msgs=[dnsseed_disabled, listen_disabled]):
403 self.restart_node(0, extra_args=[connect_arg])
404 self.nodes[0].replace_in_config([("#bind=", "bind="), ("#dnsseed=", "dnsseed=")])
405 406 # Make sure -proxy and -noconnect warn about -dnsseed setting being
407 # ignored, just like -proxy and -connect do.
408 with self.nodes[0].assert_debug_log(expected_msgs=dnsseed_ignored):
409 self.restart_node(0, extra_args=[connect_arg, '-dnsseed', '-proxy=localhost:1080'])
410 self.stop_node(0)
411 412 def test_ignored_conf(self):
413 self.log.info('Test error is triggered when the datadir in use contains a limenka.conf file that would be ignored '
414 'because a conflicting -conf file argument is passed.')
415 node = self.nodes[0]
416 with tempfile.NamedTemporaryFile(dir=self.options.tmpdir, mode="wt", delete=False) as temp_conf:
417 temp_conf.write(f"datadir={node.datadir_path}\n")
418 node.assert_start_raises_init_error([f"-conf={temp_conf.name}"], re.escape(
419 f'Error: Data directory "{node.datadir_path}" contains a "limenka.conf" file which is ignored, because a '
420 f'different configuration file "{temp_conf.name}" from command line argument "-conf={temp_conf.name}" '
421 f'is being used instead.') + r"[\s\S]*", match=ErrorMatch.FULL_REGEX)
422 423 # Test that passing a redundant -conf command line argument pointing to
424 # the same limenka.conf that would be loaded anyway does not trigger an
425 # error.
426 self.start_node(0, [f'-conf={node.datadir_path}/limenka.conf'])
427 self.stop_node(0)
428 429 def test_ignored_default_conf(self):
430 # Disable this test for windows currently because trying to override
431 # the default datadir through the environment does not seem to work.
432 if platform.system() == "Windows":
433 return
434 435 self.log.info('Test error is triggered when limenka.conf in the default data directory sets another datadir '
436 'and it contains a different limenka.conf file that would be ignored')
437 438 # Create a temporary directory that will be treated as the default data
439 # directory by limenkad.
440 env, default_datadir = util.get_temp_default_datadir(Path(self.options.tmpdir, "home"))
441 default_datadir.mkdir(parents=True)
442 443 # Write a limenka.conf file in the default data directory containing a
444 # datadir= line pointing at the node datadir. This will trigger a
445 # startup error because the node datadir contains a different
446 # limenka.conf that would be ignored.
447 node = self.nodes[0]
448 (default_datadir / "limenka.conf").write_text(f"datadir={node.datadir_path}\n")
449 450 # Drop the node -datadir= argument during this test, because if it is
451 # specified it would take precedence over the datadir setting in the
452 # config file.
453 node_args = node.args
454 node.args = [arg for arg in node.args if not arg.startswith("-datadir=")]
455 node.assert_start_raises_init_error([], re.escape(
456 f'Error: Data directory "{node.datadir_path}" contains a "limenka.conf" file which is ignored, because a '
457 f'different configuration file "{default_datadir}/limenka.conf" from data directory "{default_datadir}" '
458 f'is being used instead.') + r"[\s\S]*", env=env, match=ErrorMatch.FULL_REGEX)
459 node.args = node_args
460 461 def test_acceptstalefeeestimates_arg_support(self):
462 self.log.info("Test -acceptstalefeeestimates option support")
463 conf_file = self.nodes[0].datadir_path / "limenka.conf"
464 for chain, chain_name in {("main", ""), ("test", "testnet3"), ("signet", "signet"), ("testnet4", "testnet4")}:
465 util.write_config(conf_file, n=0, chain=chain_name, extra_config='acceptstalefeeestimates=1\n')
466 self.nodes[0].assert_start_raises_init_error(expected_msg=f'Error: acceptstalefeeestimates is not supported on {chain} chain.')
467 util.write_config(conf_file, n=0, chain="regtest") # Reset to regtest
468 469 def test_testnet3_deprecation_msg(self):
470 self.log.info("Test testnet3 deprecation warning")
471 t3_warning_log = "Warning: Support for testnet3 is deprecated and will be removed in an upcoming release. Consider switching to testnet4."
472 473 self.log.debug("Testnet3 node will log the deprecation warning")
474 self.nodes[0].chain = 'testnet3'
475 self.nodes[0].replace_in_config([('regtest=', 'testnet='), ('[regtest]', '[test]')])
476 with self.nodes[0].assert_debug_log([t3_warning_log]):
477 self.start_node(0)
478 self.stop_node(0)
479 480 self.log.debug("Testnet4 node will not log the deprecation warning")
481 self.nodes[0].chain = 'testnet4'
482 self.nodes[0].replace_in_config([('testnet=', 'testnet4='), ('[test]', '[testnet4]')])
483 with self.nodes[0].assert_debug_log([], unexpected_msgs=[t3_warning_log]):
484 self.start_node(0)
485 self.stop_node(0)
486 487 self.log.debug("Reset to regtest")
488 self.nodes[0].chain = 'regtest'
489 self.nodes[0].replace_in_config([('testnet4=', 'regtest='), ('[testnet4]', '[regtest]')])
490 491 def run_test(self):
492 self.test_log_buffer()
493 self.test_args_log()
494 self.test_seed_peers()
495 self.test_networkactive()
496 self.test_connect_with_seednode()
497 498 self.test_dir_config()
499 self.test_negated_config()
500 self.test_config_file_parser()
501 self.test_config_file_log()
502 self.test_invalid_command_line_options()
503 self.test_ignored_conf()
504 self.test_ignored_default_conf()
505 self.test_testnet3_deprecation_msg()
506 507 # Remove the -datadir argument so it doesn't override the config file
508 self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")]
509 510 default_data_dir = self.nodes[0].datadir_path
511 new_data_dir = default_data_dir / 'newdatadir'
512 new_data_dir_2 = default_data_dir / 'newdatadir2'
513 514 # Check that using -datadir argument on non-existent directory fails
515 self.nodes[0].datadir_path = new_data_dir
516 self.nodes[0].assert_start_raises_init_error([f'-datadir={new_data_dir}'], f'Error: Specified data directory "{new_data_dir}" does not exist.')
517 518 # Check that using non-existent datadir in conf file fails
519 conf_file = default_data_dir / "limenka.conf"
520 521 # datadir needs to be set before [chain] section
522 with open(conf_file, encoding='utf8') as f:
523 conf_file_contents = f.read()
524 with open(conf_file, 'w', encoding='utf8') as f:
525 f.write(f"datadir={new_data_dir}\n")
526 f.write(conf_file_contents)
527 528 self.nodes[0].assert_start_raises_init_error([f'-conf={conf_file}'], f'Error: Error reading configuration file: specified data directory "{new_data_dir}" does not exist.')
529 530 # Check that an explicitly specified config file that cannot be opened fails
531 none_existent_conf_file = default_data_dir / "none_existent_limenka.conf"
532 self.nodes[0].assert_start_raises_init_error(['-conf=' + f'{none_existent_conf_file}'], 'Error: Error reading configuration file: specified config file "' + f'{none_existent_conf_file}' + '" could not be opened.')
533 534 # Create the directory and ensure the config file now works
535 new_data_dir.mkdir()
536 self.start_node(0, [f'-conf={conf_file}'])
537 self.stop_node(0)
538 assert (new_data_dir / self.chain / 'blocks').exists()
539 540 # Ensure command line argument overrides datadir in conf
541 new_data_dir_2.mkdir()
542 self.nodes[0].datadir_path = new_data_dir_2
543 self.start_node(0, [f'-datadir={new_data_dir_2}', f'-conf={conf_file}'])
544 assert (new_data_dir_2 / self.chain / 'blocks').exists()
545 546 547 if __name__ == '__main__':
548 ConfArgsTest(__file__).main()
549