mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-07 22:24:34 +01:00
Merge bitcoin/bitcoin#29775: Testnet4 including PoW difficulty adjustment fix
6bfa26048dtestnet: Add timewarp attack prevention for Testnet4 (Fabian Jahr)0100907ca1testnet: Add Testnet4 difficulty adjustment rules fix (Fabian Jahr)74a04f9e7atestnet: Introduce Testnet4 (Fabian Jahr) Pull request description: To supplement the [ongoing conceptual discussion about a testnet reset](https://groups.google.com/g/bitcoindev/c/9bL00vRj7OU/m/9yCPo3uUBwAJ) I have drafted a move to v4 including a fix to the difficulty adjustment mechanism, which was part of the motivation that started the discussion. Conceptual considerations: - The conceptual discussion about doing a testnet4 or softforking the fix into testnet3 is outside of the scope of this PR and I would ask reviewers to contribute their opinions on this on the ML instead. However, I am happy to adapt this PR to a softfork change on testnet3 if there is consensus for that instead. - The difficulty adjustment fix suggested here touches the `CalculateNextWorkRequired` function and uses the same logic used in `GetNextWorkRequired` to find the last previous block that was not mined with difficulty 1 under the exceptionf. An alternative fix briefly mentioned on the mailing list by Jameson Lopp would be to "restrict the special testnet minimum difficulty rule so that it can't be triggered on the block right before a difficulty retarget". That would also fix the issue but I find my suggestion here a bit more elegant. ACKs for top commit: jsarenik: tACK6bfa26048dachow101: ACK6bfa26048dmurchandamus: tACK6bfa26048dTree-SHA512: 0b8b69a621406a944da5be551b863d065358ba94d85dd3b80d83c412660e230ee93b27316081fbee9b4851cc4ff8585db64c7dfa26cb5148ac835663f2712c3d
This commit is contained in:
@@ -371,11 +371,44 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
def test_acceptstalefeeestimates_arg_support(self):
|
||||
self.log.info("Test -acceptstalefeeestimates option support")
|
||||
conf_file = self.nodes[0].datadir_path / "bitcoin.conf"
|
||||
for chain, chain_name in {("main", ""), ("test", "testnet3"), ("signet", "signet")}:
|
||||
for chain, chain_name in {("main", ""), ("test", "testnet3"), ("signet", "signet"), ("testnet4", "testnet4")}:
|
||||
util.write_config(conf_file, n=0, chain=chain_name, extra_config='acceptstalefeeestimates=1\n')
|
||||
self.nodes[0].assert_start_raises_init_error(expected_msg=f'Error: acceptstalefeeestimates is not supported on {chain} chain.')
|
||||
util.write_config(conf_file, n=0, chain="regtest") # Reset to regtest
|
||||
|
||||
def test_testnet3_deprecation_msg(self):
|
||||
self.log.info("Test testnet3 deprecation warning")
|
||||
t3_warning_log = "Warning: Support for testnet3 is deprecated and will be removed in an upcoming release. Consider switching to testnet4."
|
||||
|
||||
def warning_msg(node, approx_size):
|
||||
return f'Warning: Disk space for "{node.datadir_path / node.chain / "blocks" }" may not accommodate the block files. Approximately {approx_size} GB of data will be stored in this directory.'
|
||||
|
||||
# Testnet3 node will log the warning
|
||||
self.nodes[0].chain = 'testnet3'
|
||||
self.nodes[0].replace_in_config([('regtest=', 'testnet='), ('[regtest]', '[test]')])
|
||||
with self.nodes[0].assert_debug_log([t3_warning_log]):
|
||||
self.start_node(0)
|
||||
# Some CI environments will have limited space and some others won't
|
||||
# so we need to handle both cases as a valid result.
|
||||
self.nodes[0].stderr.seek(0)
|
||||
err = self.nodes[0].stdout.read()
|
||||
self.nodes[0].stderr.seek(0)
|
||||
self.nodes[0].stderr.truncate()
|
||||
if err != b'' and err != warning_msg(self.nodes[0], 42):
|
||||
raise AssertionError("Unexpected stderr after shutdown of Testnet3 node")
|
||||
self.stop_node(0)
|
||||
|
||||
# Testnet4 node will not log the warning
|
||||
self.nodes[0].chain = 'testnet4'
|
||||
self.nodes[0].replace_in_config([('testnet=', 'testnet4='), ('[test]', '[testnet4]')])
|
||||
with self.nodes[0].assert_debug_log([], unexpected_msgs=[t3_warning_log]):
|
||||
self.start_node(0)
|
||||
self.stop_node(0)
|
||||
|
||||
# Reset to regtest
|
||||
self.nodes[0].chain = 'regtest'
|
||||
self.nodes[0].replace_in_config([('testnet4=', 'regtest='), ('[testnet4]', '[regtest]')])
|
||||
|
||||
def run_test(self):
|
||||
self.test_log_buffer()
|
||||
self.test_args_log()
|
||||
@@ -389,6 +422,7 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
self.test_ignored_conf()
|
||||
self.test_ignored_default_conf()
|
||||
self.test_acceptstalefeeestimates_arg_support()
|
||||
self.test_testnet3_deprecation_msg()
|
||||
|
||||
# Remove the -datadir argument so it doesn't override the config file
|
||||
self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")]
|
||||
|
||||
@@ -11,7 +11,7 @@ class WalletCrossChain(BitcoinTestFramework):
|
||||
self.add_wallet_options(parser)
|
||||
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 2
|
||||
self.num_nodes = 3
|
||||
self.setup_clean_chain = True
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
@@ -24,6 +24,12 @@ class WalletCrossChain(BitcoinTestFramework):
|
||||
self.nodes[1].chain = 'testnet3'
|
||||
self.nodes[1].extra_args = ['-maxconnections=0', '-prune=550'] # disable testnet sync
|
||||
self.nodes[1].replace_in_config([('regtest=', 'testnet='), ('[regtest]', '[test]')])
|
||||
|
||||
# Switch node 2 to testnet4 before starting it.
|
||||
self.nodes[2].chain = 'testnet4'
|
||||
self.nodes[2].extra_args = ['-maxconnections=0', '-prune=550'] # disable testnet4 sync
|
||||
self.nodes[2].replace_in_config([('regtest=', 'testnet4='), ('[regtest]', '[testnet4]')])
|
||||
|
||||
self.start_nodes()
|
||||
|
||||
def run_test(self):
|
||||
@@ -39,19 +45,40 @@ class WalletCrossChain(BitcoinTestFramework):
|
||||
self.nodes[1].createwallet(node1_wallet)
|
||||
self.nodes[1].backupwallet(node1_wallet_backup)
|
||||
self.nodes[1].unloadwallet(node1_wallet)
|
||||
node2_wallet = self.nodes[2].datadir_path / 'node2_wallet'
|
||||
node2_wallet_backup = self.nodes[0].datadir_path / 'node2_wallet.bak'
|
||||
self.nodes[2].createwallet(node2_wallet)
|
||||
self.nodes[2].backupwallet(node2_wallet_backup)
|
||||
self.nodes[2].unloadwallet(node2_wallet)
|
||||
|
||||
self.log.info("Loading/restoring wallets into nodes with a different genesis block")
|
||||
|
||||
if self.options.descriptors:
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].loadwallet, node1_wallet)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].loadwallet, node2_wallet)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[1].loadwallet, node0_wallet)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[2].loadwallet, node0_wallet)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[1].loadwallet, node2_wallet)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[2].loadwallet, node1_wallet)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].restorewallet, 'w', node1_wallet_backup)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].restorewallet, 'w', node2_wallet_backup)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[1].restorewallet, 'w', node0_wallet_backup)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[2].restorewallet, 'w', node0_wallet_backup)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[1].restorewallet, 'w', node2_wallet_backup)
|
||||
assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[2].restorewallet, 'w', node1_wallet_backup)
|
||||
else:
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].loadwallet, node1_wallet)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].loadwallet, node2_wallet)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[1].loadwallet, node0_wallet)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[2].loadwallet, node0_wallet)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[1].loadwallet, node2_wallet)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[2].loadwallet, node1_wallet)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].restorewallet, 'w', node1_wallet_backup)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].restorewallet, 'w', node2_wallet_backup)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[1].restorewallet, 'w', node0_wallet_backup)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[2].restorewallet, 'w', node0_wallet_backup)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[1].restorewallet, 'w', node2_wallet_backup)
|
||||
assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[2].restorewallet, 'w', node1_wallet_backup)
|
||||
|
||||
if not self.options.descriptors:
|
||||
self.log.info("Override cross-chain wallet load protection")
|
||||
|
||||
Reference in New Issue
Block a user