diff options
author | fanquake <fanquake@gmail.com> | 2023-10-05 16:44:12 +0100 |
---|---|---|
committer | fanquake <fanquake@gmail.com> | 2023-10-05 16:51:15 +0100 |
commit | d9c1cc5f1f5460f6adcce95779f34f2ed356dda3 (patch) | |
tree | 96870cae9b2cf5cf8aa5f7cbfa19f91ff2c72b65 /test | |
parent | db19a7e89d19d97094bcf8753b37619a16b7f033 (diff) | |
parent | afd9a673c458e97305da49a70a1ddbf60e651876 (diff) | |
download | bitcoin-d9c1cc5f1f5460f6adcce95779f34f2ed356dda3.tar.xz |
Merge bitcoin/bitcoin#28027: test: Fixes and updates to wallet_backwards_compatibility.py for 25.0 and descriptor wallets
afd9a673c458e97305da49a70a1ddbf60e651876 test: roundtrip wallet backwards compat downgrade (Andrew Chow)
bbf43c63b9472a79462e625a1f0592973c22b47c test: Add 25.0 to wallet backwards compatibiilty test (Andrew Chow)
538939ec39e146bedffb80cf84849a450ea8fead test: Run upgrade test on all nodes (Andrew Chow)
6d4699028b17cb33953f7d11764e06069dd58915 test: Run downgrade test on descriptor wallets (Andrew Chow)
f158573be12746991b75587cc9e41a74a5e986eb test: Add 0.21 tr() incompatibility test (Andrew Chow)
f41215c3f08f99d1bfa524f2da8055b6a4458bbb test: add logging 0.17 incompatibilities in wallet back compat (Andrew Chow)
71c03aeff7e1c63c21fa72d119311230f0b30e73 test: Refactor v19 addmultisigaddress test to be distinct (Andrew Chow)
53f35d02cb7b67ddecc9514559083f85093b6ce5 test: Remove w1_v18 from wallet backwards compatibility (Andrew Chow)
313d665437079ce8426916a41a11972e97c73d6d test: Fix 0.16 wallet paths and downgrade test (Andrew Chow)
5d8469362acfb7a03e0f767dbb7166830355bead test: Add helper functions for checking node versions (Andrew Chow)
Pull request description:
It was somewhat surprising to me that wallet_backwards_compatibility.py did not catch #27915 since the purpose of the test is to find downgrade issues such as that. It turns out the test was deficient in several places when it came to testing descriptor wallets, as well as deficient in addition to failing to correctly test some releases.
This PR fixes these test cases, adds more informative logging, slightly refactors the entire test in order to better test future versions, and adds a 25.0 node to the test.
Notable changes:
* The compatibility test with 0.16 should not have been passing. The wallets were being copied incorrectly for 0.16 and resulting in 0.16 creating new wallets rather than testing the target wallets.
* The downgrade test will actually be run on descriptor wallets and it will test that downgrades are successful, and a subsequent upgrade is also successful. This catches #27915.
* The upgrade and downgrade test will be run on all versions up to master, rather than just 0.16, 0.17, and 0.19.
ACKs for top commit:
Sjors:
re-ACK afd9a673c458e97305da49a70a1ddbf60e651876
furszy:
ACK afd9a67
Tree-SHA512: dd2d85cab29a636da93020681c533534af4a9cda18d8550c9db9d8937719b3a225025966981c5d4d2f30486448a772b760f0e723a25ea6bc49df80387dc7b8b0
Diffstat (limited to 'test')
-rwxr-xr-x | test/functional/wallet_backwards_compatibility.py | 341 | ||||
-rwxr-xr-x | test/get_previous_releases.py | 9 |
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"}, } |