aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xtest/functional/wallet_backwards_compatibility.py341
-rwxr-xr-xtest/get_previous_releases.py9
2 files changed, 203 insertions, 147 deletions
diff --git a/test/functional/wallet_backwards_compatibility.py b/test/functional/wallet_backwards_compatibility.py
index 49e36b21c5..4d6e6024c5 100755
--- a/test/functional/wallet_backwards_compatibility.py
+++ b/test/functional/wallet_backwards_compatibility.py
@@ -33,11 +33,12 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 11
+ self.num_nodes = 12
# Add new version after each release:
self.extra_args = [
["-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to mine blocks. noban for immediate tx relay
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to receive coins, swap wallets, etc
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v25.0
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v24.0.1
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v23.0
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v22.0
@@ -58,6 +59,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
None,
None,
+ 250000,
240001,
230000,
220000,
@@ -72,20 +74,72 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.start_nodes()
self.import_deterministic_coinbase_privkeys()
- def nodes_wallet_dir(self, node):
- if node.version < 170000:
- return node.chain_path
- return node.wallets_path
+ def split_version(self, node):
+ major = node.version // 10000
+ minor = (node.version % 10000) // 100
+ patch = (node.version % 100)
+ return (major, minor, patch)
+
+ def major_version_equals(self, node, major):
+ node_major, _, _ = self.split_version(node)
+ return node_major == major
+
+ def major_version_less_than(self, node, major):
+ node_major, _, _ = self.split_version(node)
+ return node_major < major
+
+ def major_version_at_least(self, node, major):
+ node_major, _, _ = self.split_version(node)
+ return node_major >= major
+
+ def test_v19_addmultisigaddress(self):
+ if not self.is_bdb_compiled():
+ return
+ # Specific test for addmultisigaddress using v19
+ # See #18075
+ self.log.info("Testing 0.19 addmultisigaddress case (#18075)")
+ node_master = self.nodes[1]
+ node_v19 = self.nodes[self.num_nodes - 4]
+ node_v19.rpc.createwallet(wallet_name="w1_v19")
+ wallet = node_v19.get_wallet_rpc("w1_v19")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] > 0
+ # Use addmultisigaddress (see #18075)
+ address_18075 = wallet.rpc.addmultisigaddress(1, ["0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52", "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"], "", "legacy")["address"]
+ assert wallet.getaddressinfo(address_18075)["solvable"]
+ node_v19.unloadwallet("w1_v19")
+
+ # Copy the 0.19 wallet to the last Bitcoin Core version and open it:
+ shutil.copytree(
+ os.path.join(node_v19.wallets_path, "w1_v19"),
+ os.path.join(node_master.wallets_path, "w1_v19")
+ )
+ node_master.loadwallet("w1_v19")
+ wallet = node_master.get_wallet_rpc("w1_v19")
+ assert wallet.getaddressinfo(address_18075)["solvable"]
+
+ # Now copy that same wallet back to 0.19 to make sure no automatic upgrade breaks it
+ node_master.unloadwallet("w1_v19")
+ shutil.rmtree(os.path.join(node_v19.wallets_path, "w1_v19"))
+ shutil.copytree(
+ os.path.join(node_master.wallets_path, "w1_v19"),
+ os.path.join(node_v19.wallets_path, "w1_v19")
+ )
+ node_v19.loadwallet("w1_v19")
+ wallet = node_v19.get_wallet_rpc("w1_v19")
+ assert wallet.getaddressinfo(address_18075)["solvable"]
def run_test(self):
node_miner = self.nodes[0]
node_master = self.nodes[1]
- node_v19 = self.nodes[self.num_nodes - 4]
- node_v18 = self.nodes[self.num_nodes - 3]
+ node_v21 = self.nodes[self.num_nodes - 6]
node_v17 = self.nodes[self.num_nodes - 2]
node_v16 = self.nodes[self.num_nodes - 1]
- legacy_nodes = self.nodes[2:]
+ legacy_nodes = self.nodes[2:] # Nodes that support legacy wallets
+ legacy_only_nodes = self.nodes[-5:] # Nodes that only support legacy wallets
+ descriptors_nodes = self.nodes[2:-5] # Nodes that support descriptor wallets
self.generatetoaddress(node_miner, COINBASE_MATURITY + 1, node_miner.getnewaddress())
@@ -121,24 +175,6 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
# Abandon transaction, but don't confirm
node_master.abandontransaction(tx3_id)
- # w1_v19: regular wallet, created with v0.19
- node_v19.rpc.createwallet(wallet_name="w1_v19")
- wallet = node_v19.get_wallet_rpc("w1_v19")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] > 0
- # Use addmultisigaddress (see #18075)
- address_18075 = wallet.rpc.addmultisigaddress(1, ["0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52", "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"], "", "legacy")["address"]
- assert wallet.getaddressinfo(address_18075)["solvable"]
- node_v19.unloadwallet("w1_v19")
-
- # w1_v18: regular wallet, created with v0.18
- node_v18.rpc.createwallet(wallet_name="w1_v18")
- wallet = node_v18.get_wallet_rpc("w1_v18")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] > 0
-
# w2: wallet with private keys disabled, created on master: update this
# test when default wallets private keys disabled can no longer be
# opened by older versions.
@@ -158,9 +194,6 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
# Unload wallets and copy to older nodes:
node_master_wallets_dir = node_master.wallets_path
- node_v19_wallets_dir = node_v19.wallets_path
- node_v17_wallets_dir = node_v17.wallets_path
- node_v16_wallets_dir = node_v16.chain_path
node_master.unloadwallet("w1")
node_master.unloadwallet("w2")
node_master.unloadwallet("w3")
@@ -168,24 +201,35 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
for node in legacy_nodes:
# Copy wallets to previous version
for wallet in os.listdir(node_master_wallets_dir):
- shutil.copytree(
- os.path.join(node_master_wallets_dir, wallet),
- os.path.join(self.nodes_wallet_dir(node), wallet)
- )
-
- if not self.options.descriptors:
- # Descriptor wallets break compatibility, only run this test for legacy wallet
- # Load modern wallet with older nodes
- for node in legacy_nodes:
- for wallet_name in ["w1", "w2", "w3"]:
- if node.version < 170000:
- # loadwallet was introduced in v0.17.0
- continue
- if node.version < 180000 and wallet_name == "w3":
- # Blank wallets were introduced in v0.18.0. We test the loading error below.
- continue
- node.loadwallet(wallet_name)
- wallet = node.get_wallet_rpc(wallet_name)
+ dest = node.wallets_path / wallet
+ source = node_master_wallets_dir / wallet
+ if self.major_version_equals(node, 16):
+ # 0.16 node expect the wallet to be in the wallet dir but as a plain file rather than in directories
+ shutil.copyfile(source / "wallet.dat", dest)
+ else:
+ shutil.copytree(source, dest)
+
+ self.test_v19_addmultisigaddress()
+
+ self.log.info("Test that a wallet made on master can be opened on:")
+ # In descriptors wallet mode, run this test on the nodes that support descriptor wallets
+ # In legacy wallets mode, run this test on the nodes that support legacy wallets
+ for node in descriptors_nodes if self.options.descriptors else legacy_nodes:
+ if self.major_version_less_than(node, 17):
+ # loadwallet was introduced in v0.17.0
+ continue
+ self.log.info(f"- {node.version}")
+ for wallet_name in ["w1", "w2", "w3"]:
+ if self.major_version_less_than(node, 18) and wallet_name == "w3":
+ # Blank wallets were introduced in v0.18.0. We test the loading error below.
+ continue
+ if self.major_version_less_than(node, 22) and wallet_name == "w1" and self.options.descriptors:
+ # Descriptor wallets created after 0.21 have taproot descriptors which 0.21 does not support, tested below
+ continue
+ # Also try to reopen on master after opening on old
+ for n in [node, node_master]:
+ n.loadwallet(wallet_name)
+ wallet = n.get_wallet_rpc(wallet_name)
info = wallet.getwalletinfo()
if wallet_name == "w1":
assert info['private_keys_enabled'] == True
@@ -208,130 +252,133 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
else:
assert info['private_keys_enabled'] == True
assert info['keypoolsize'] == 0
- else:
- for node in legacy_nodes:
- # Descriptor wallets appear to be corrupted wallets to old software
- # and loadwallet is introduced in v0.17.0
- if node.version >= 170000 and node.version < 210000:
- for wallet_name in ["w1", "w2", "w3"]:
- assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node.loadwallet, wallet_name)
- # RPC loadwallet failure causes bitcoind to exit, in addition to the RPC
- # call failure, so the following test won't work:
- # assert_raises_rpc_error(-4, "Wallet loading failed.", node_v17.loadwallet, 'w3')
+ # Copy back to master
+ wallet.unloadwallet()
+ if n == node:
+ shutil.rmtree(node_master.wallets_path / wallet_name)
+ shutil.copytree(
+ n.wallets_path / wallet_name,
+ node_master.wallets_path / wallet_name,
+ )
+
+ # Check that descriptor wallets don't work on legacy only nodes
+ if self.options.descriptors:
+ self.log.info("Test descriptor wallet incompatibility on:")
+ for node in legacy_only_nodes:
+ # RPC loadwallet failure causes bitcoind to exit in <= 0.17, in addition to the RPC
+ # call failure, so the following test won't work:
+ # assert_raises_rpc_error(-4, "Wallet loading failed.", node_v17.loadwallet, 'w3')
+ if self.major_version_less_than(node, 18):
+ continue
+ self.log.info(f"- {node.version}")
+ # Descriptor wallets appear to be corrupted wallets to old software
+ assert self.major_version_at_least(node, 18) and self.major_version_less_than(node, 21)
+ for wallet_name in ["w1", "w2", "w3"]:
+ assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node.loadwallet, wallet_name)
# Instead, we stop node and try to launch it with the wallet:
self.stop_node(node_v17.index)
if self.options.descriptors:
+ self.log.info("Test descriptor wallet incompatibility with 0.17")
# Descriptor wallets appear to be corrupted wallets to old software
node_v17.assert_start_raises_init_error(["-wallet=w1"], "Error: wallet.dat corrupt, salvage failed")
node_v17.assert_start_raises_init_error(["-wallet=w2"], "Error: wallet.dat corrupt, salvage failed")
node_v17.assert_start_raises_init_error(["-wallet=w3"], "Error: wallet.dat corrupt, salvage failed")
else:
+ self.log.info("Test blank wallet incompatibility with v17")
node_v17.assert_start_raises_init_error(["-wallet=w3"], "Error: Error loading w3: Wallet requires newer version of Bitcoin Core")
self.start_node(node_v17.index)
- if not self.options.descriptors:
- # Descriptor wallets break compatibility, only run this test for legacy wallets
- # Open most recent wallet in v0.16 (no loadwallet RPC)
- self.restart_node(node_v16.index, extra_args=["-wallet=w2"])
- wallet = node_v16.get_wallet_rpc("w2")
- info = wallet.getwalletinfo()
- assert info['keypoolsize'] == 1
-
- # Create upgrade wallet in v0.16
- self.restart_node(node_v16.index, extra_args=["-wallet=u1_v16"])
- wallet = node_v16.get_wallet_rpc("u1_v16")
- v16_addr = wallet.getnewaddress('', "bech32")
- v16_info = wallet.validateaddress(v16_addr)
- v16_pubkey = v16_info['pubkey']
+ # No wallet created in master can be opened in 0.16
+ self.log.info("Test that wallets created in master are too new for 0.16")
self.stop_node(node_v16.index)
+ for wallet_name in ["w1", "w2", "w3"]:
+ if self.options.descriptors:
+ node_v16.assert_start_raises_init_error([f"-wallet={wallet_name}"], f"Error: {wallet_name} corrupt, salvage failed")
+ else:
+ node_v16.assert_start_raises_init_error([f"-wallet={wallet_name}"], f"Error: Error loading {wallet_name}: Wallet requires newer version of Bitcoin Core")
+
+ # When descriptors are enabled, w1 cannot be opened by 0.21 since it contains a taproot descriptor
+ if self.options.descriptors:
+ self.log.info("Test that 0.21 cannot open wallet containing tr() descriptors")
+ assert_raises_rpc_error(-1, "map::at", node_v21.loadwallet, "w1")
+
+ self.log.info("Test that a wallet can upgrade to and downgrade from master, from:")
+ for node in descriptors_nodes if self.options.descriptors else legacy_nodes:
+ self.log.info(f"- {node.version}")
+ wallet_name = f"up_{node.version}"
+ if self.major_version_less_than(node, 17):
+ # createwallet is only available in 0.17+
+ self.restart_node(node.index, extra_args=[f"-wallet={wallet_name}"])
+ wallet_prev = node.get_wallet_rpc(wallet_name)
+ address = wallet_prev.getnewaddress('', "bech32")
+ addr_info = wallet_prev.validateaddress(address)
+ else:
+ if self.major_version_at_least(node, 21):
+ node.rpc.createwallet(wallet_name=wallet_name, descriptors=self.options.descriptors)
+ else:
+ node.rpc.createwallet(wallet_name=wallet_name)
+ wallet_prev = node.get_wallet_rpc(wallet_name)
+ address = wallet_prev.getnewaddress('', "bech32")
+ addr_info = wallet_prev.getaddressinfo(address)
+
+ hdkeypath = addr_info["hdkeypath"].replace("'", "h")
+ pubkey = addr_info["pubkey"]
+
+ # Make a backup of the wallet file
+ backup_path = os.path.join(self.options.tmpdir, f"{wallet_name}.dat")
+ wallet_prev.backupwallet(backup_path)
+
+ # Remove the wallet from old node
+ if self.major_version_at_least(node, 17):
+ wallet_prev.unloadwallet()
+ else:
+ self.stop_node(node.index)
+
+ # Restore the wallet to master
+ load_res = node_master.restorewallet(wallet_name, backup_path)
- self.log.info("Test wallet upgrade path...")
- # u1: regular wallet, created with v0.17
- node_v17.rpc.createwallet(wallet_name="u1_v17")
- wallet = node_v17.get_wallet_rpc("u1_v17")
- address = wallet.getnewaddress("bech32")
- v17_info = wallet.getaddressinfo(address)
- hdkeypath = v17_info["hdkeypath"].replace("'", "h")
- pubkey = v17_info["pubkey"]
-
- if self.is_bdb_compiled():
- # Old wallets are BDB and will only work if BDB is compiled
- # Copy the 0.16 wallet to the last Bitcoin Core version and open it:
- shutil.copyfile(
- os.path.join(node_v16_wallets_dir, "wallets/u1_v16"),
- os.path.join(node_master_wallets_dir, "u1_v16")
- )
- load_res = node_master.loadwallet("u1_v16")
# Make sure this wallet opens with only the migration warning. See https://github.com/bitcoin/bitcoin/pull/19054
- if int(node_master.getnetworkinfo()["version"]) >= 249900:
- # loadwallet#warnings (added in v25) -- only present if there is a warning
+ if not self.options.descriptors:
# Legacy wallets will have only a deprecation warning
assert_equal(load_res["warnings"], ["Wallet loaded successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future. Legacy wallets can be migrated to a descriptor wallet with migratewallet."])
else:
- # loadwallet#warning (deprecated in v25) -- always present, but empty string if no warning
- assert_equal(load_res["warning"], '')
- wallet = node_master.get_wallet_rpc("u1_v16")
- info = wallet.getaddressinfo(v16_addr)
- descriptor = f"wpkh([{info['hdmasterfingerprint']}{hdkeypath[1:]}]{v16_pubkey})"
- assert_equal(info["desc"], descsum_create(descriptor))
+ assert "warnings" not in load_res
- # Now copy that same wallet back to 0.16 to make sure no automatic upgrade breaks it
- node_master.unloadwallet("u1_v16")
- os.remove(os.path.join(node_v16_wallets_dir, "wallets/u1_v16"))
- shutil.copyfile(
- os.path.join(node_master_wallets_dir, "u1_v16"),
- os.path.join(node_v16_wallets_dir, "wallets/u1_v16")
- )
- self.start_node(node_v16.index, extra_args=["-wallet=u1_v16"])
- wallet = node_v16.get_wallet_rpc("u1_v16")
- info = wallet.validateaddress(v16_addr)
- assert_equal(info, v16_info)
-
- # Copy the 0.17 wallet to the last Bitcoin Core version and open it:
- node_v17.unloadwallet("u1_v17")
- shutil.copytree(
- os.path.join(node_v17_wallets_dir, "u1_v17"),
- os.path.join(node_master_wallets_dir, "u1_v17")
- )
- node_master.loadwallet("u1_v17")
- wallet = node_master.get_wallet_rpc("u1_v17")
+ wallet = node_master.get_wallet_rpc(wallet_name)
info = wallet.getaddressinfo(address)
descriptor = f"wpkh([{info['hdmasterfingerprint']}{hdkeypath[1:]}]{pubkey})"
assert_equal(info["desc"], descsum_create(descriptor))
- # Now copy that same wallet back to 0.17 to make sure no automatic upgrade breaks it
- node_master.unloadwallet("u1_v17")
- shutil.rmtree(os.path.join(node_v17_wallets_dir, "u1_v17"))
- shutil.copytree(
- os.path.join(node_master_wallets_dir, "u1_v17"),
- os.path.join(node_v17_wallets_dir, "u1_v17")
- )
- node_v17.loadwallet("u1_v17")
- wallet = node_v17.get_wallet_rpc("u1_v17")
- info = wallet.getaddressinfo(address)
- assert_equal(info, v17_info)
-
- # Copy the 0.19 wallet to the last Bitcoin Core version and open it:
- shutil.copytree(
- os.path.join(node_v19_wallets_dir, "w1_v19"),
- os.path.join(node_master_wallets_dir, "w1_v19")
- )
- node_master.loadwallet("w1_v19")
- wallet = node_master.get_wallet_rpc("w1_v19")
- assert wallet.getaddressinfo(address_18075)["solvable"]
-
- # Now copy that same wallet back to 0.19 to make sure no automatic upgrade breaks it
- node_master.unloadwallet("w1_v19")
- shutil.rmtree(os.path.join(node_v19_wallets_dir, "w1_v19"))
- shutil.copytree(
- os.path.join(node_master_wallets_dir, "w1_v19"),
- os.path.join(node_v19_wallets_dir, "w1_v19")
- )
- node_v19.loadwallet("w1_v19")
- wallet = node_v19.get_wallet_rpc("w1_v19")
- assert wallet.getaddressinfo(address_18075)["solvable"]
+ # Make backup so the wallet can be copied back to old node
+ down_wallet_name = f"re_down_{node.version}"
+ down_backup_path = os.path.join(self.options.tmpdir, f"{down_wallet_name}.dat")
+ wallet.backupwallet(down_backup_path)
+ wallet.unloadwallet()
+
+ # Check that no automatic upgrade broke the downgrading the wallet
+ if self.major_version_less_than(node, 17):
+ # loadwallet is only available in 0.17+
+ shutil.copyfile(
+ down_backup_path,
+ node.wallets_path / down_wallet_name
+ )
+ self.start_node(node.index, extra_args=[f"-wallet={down_wallet_name}"])
+ wallet_res = node.get_wallet_rpc(down_wallet_name)
+ info = wallet_res.validateaddress(address)
+ assert_equal(info, addr_info)
+ else:
+ target_dir = node.wallets_path / down_wallet_name
+ os.makedirs(target_dir, exist_ok=True)
+ shutil.copyfile(
+ down_backup_path,
+ target_dir / "wallet.dat"
+ )
+ node.loadwallet(down_wallet_name)
+ wallet_res = node.get_wallet_rpc(down_wallet_name)
+ info = wallet_res.getaddressinfo(address)
+ assert_equal(info, addr_info)
if __name__ == '__main__':
BackwardsCompatibilityTest().main()
diff --git a/test/get_previous_releases.py b/test/get_previous_releases.py
index 07a5ee8691..459693102b 100755
--- a/test/get_previous_releases.py
+++ b/test/get_previous_releases.py
@@ -89,6 +89,15 @@ SHA256_SUMS = {
"6b163cef7de4beb07b8cb3347095e0d76a584019b1891135cd1268a1f05b9d88": {"tag": "v24.0.1", "tarball": "bitcoin-24.0.1-riscv64-linux-gnu.tar.gz"},
"e2f751512f3c0f00eb68ba946d9c829e6cf99422a61e8f5e0a7c109c318674d0": {"tag": "v24.0.1", "tarball": "bitcoin-24.0.1-x86_64-apple-darwin.tar.gz"},
"49df6e444515d457ea0b885d66f521f2a26ca92ccf73d5296082e633544253bf": {"tag": "v24.0.1", "tarball": "bitcoin-24.0.1-x86_64-linux-gnu.tar.gz"},
+
+ "3a7bdd959a0b426624f63f394f25e5b7769a5a2f96f8126dcc2ea53f3fa5212b": {"tag": "v25.0", "tarball": "bitcoin-25.0-aarch64-linux-gnu.tar.gz"},
+ "e537c8630b05e63242d979c3004f851fd73c2a10b5b4fdbb161788427c7b3c0f": {"tag": "v25.0", "tarball": "bitcoin-25.0-arm-linux-gnueabihf.tar.gz"},
+ "3b35075d6c1209743611c705a13575be2668bc069bc6301ce78a2e1e53ebe7cc": {"tag": "v25.0", "tarball": "bitcoin-25.0-arm64-apple-darwin.tar.gz"},
+ "0c8e135a6fd297270d3b65196042d761453493a022b5ff7fb847fc911e938214": {"tag": "v25.0", "tarball": "bitcoin-25.0-powerpc64-linux-gnu.tar.gz"},
+ "fa8af160782f5adfcea570f72b947073c1663b3e9c3cd0f82b216b609fe47573": {"tag": "v25.0", "tarball": "bitcoin-25.0-powerpc64le-linux-gnu.tar.gz"},
+ "fe6e347a66043946920c72c9c4afca301968101e6b82fb90a63d7885ebcceb32": {"tag": "v25.0", "tarball": "bitcoin-25.0-riscv64-linux-gnu.tar.gz"},
+ "5708fc639cdfc27347cccfd50db9b73b53647b36fb5f3a4a93537cbe8828c27f": {"tag": "v25.0", "tarball": "bitcoin-25.0-x86_64-apple-darwin.tar.gz"},
+ "33930d432593e49d58a9bff4c30078823e9af5d98594d2935862788ce8a20aec": {"tag": "v25.0", "tarball": "bitcoin-25.0-x86_64-linux-gnu.tar.gz"},
}