test: add coverage for migrating ancient wallets
What changed, and why it matters
This commit adds a new automated test to Bitcoin Core that checks whether very old-style wallets (those created before a 2012-era change) can still be successfully migrated to the modern wallet format. It does not change any production wallet code; it only adds a test that deliberately corrupts a test wallet file to simulate an ancient wallet and then verifies migration still works.
No security action required. This is a regression/functional test addition. Reviewers may optionally verify that the test correctly exercises the intended ancient-wallet migration path and that the BDB record erasure is robust enough for CI.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff is a pure test addition in test/functional/wallet_migration.py. It introduces a helper erase_bdb_record() that overwrites BDB records in a wallet.dat by byte-searching for a key and zeroing it, plus a test_missing_bestblock() case. The test creates a legacy BDB wallet, unloads it, erases the ‘bestblock_nomerkle’ and ‘bestblock’ records to mimic pre-PR #152 wallets that lacked a best block, copies the wallet to the migration node, runs migratewallet, and asserts that a rescan occurs and the resulting wallet is a descriptor-based SQLite wallet. The test is then registered in run_test().
Changed components
test/functional/wallet_migration.pyInspect captured patch +31 / −0
diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py
index 45ebd435..d3d776d1 100755
--- a/test/functional/wallet_migration.py
+++ b/test/functional/wallet_migration.py
@@ -1659,6 +1659,36 @@ class WalletMigrationTest(BitcoinTestFramework):
self.clear_default_wallet(backup_path)
+ @staticmethod
+ def erase_bdb_record(wallet_dat_path, key):
+ data = bytearray(wallet_dat_path.read_bytes())
+ idx = data.find(key)
+ assert idx != -1, f"{key!r} not found in wallet.dat"
+
+ for i in range(idx, idx + len(key)):
+ data[i] = 0
+
+ wallet_dat_path.write_bytes(data)
+
+ def test_missing_bestblock(self):
+ self.log.info("Test migrating legacy BDB wallet without bestblock record")
+ wallet_name = "nobestblock"
+ wallet = self.create_legacy_wallet(wallet_name)
+ wallet.unloadwallet()
+
+ # Erase block locator records like if this would be a pre-#152 wallet
+ self.erase_bdb_record(self.old_node.wallets_path / wallet_name / "wallet.dat", b"bestblock_nomerkle")
+ self.erase_bdb_record(self.old_node.wallets_path / wallet_name / "wallet.dat", b"bestblock")
+
+ shutil.copytree(self.old_node.wallets_path / wallet_name, self.master_node.wallets_path / wallet_name, dirs_exist_ok=True)
+ # Migrate, checking that rescan occurs
+ with self.master_node.assert_debug_log(expected_msgs=["Rescanning"], unexpected_msgs=[]):
+ self.master_node.migratewallet(wallet_name)
+
+ wallet = self.master_node.get_wallet_rpc(wallet_name)
+ info = wallet.getwalletinfo()
+ assert_equal(info["descriptors"], True)
+ assert_equal(info["format"], "sqlite")
def run_test(self):
self.master_node = self.nodes[0]
@@ -1708,6 +1738,7 @@ class WalletMigrationTest(BitcoinTestFramework):
self.test_taproot()
self.test_solvable_no_privs()
self.test_loading_failure_after_migration()
+ self.test_missing_bestblock()
# Note: After this test the first 250 blocks of 'master_node' are pruned
self.unsynced_wallet_on_pruned_node_fails()
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.