Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1501assert "taproot_watchonly" in self.master_node.listwallets()1502watchonly = self.master_node.get_wallet_rpc("taproot_watchonly")1503assert_equal(watchonly.getbalances()["mine"], {"trusted": 5, "untrusted_pending": 0, "immature": 0, "nonmempool": 0})1504assert_equal(watchonly.getaddressinfo(rawtr_addr)["ismine"], False)1505assert_equal(watchonly.getaddressinfo(tr_addr)["ismine"], True)1506assert_equal(watchonly.getaddressinfo(tr_script_addr)["ismine"], True)1508def test_no_load_after_migration(self):1509self.log.info("Test migration with load_wallet disabled")1510default = self.master_node.get_wallet_rpc(self.default_wallet_name)1512wallet_name = "no_load_after_migration"1513wallet = self.create_legacy_wallet(wallet_name)1514addr = wallet.getnewaddress()1515txid = default.sendtoaddress(addr, 1)1516self.generate(self.master_node, 1)1517bals = wallet.getbalances()1519# Copy wallet from old node to master node1520self.old_node.unloadwallet(wallet_name)1521shutil.copytree(1522self.old_node.wallets_path / wallet_name,1523self.master_node.wallets_path / wallet_name,1524dirs_exist_ok=True,1525)1527migrate_info = self.master_node.migratewallet(wallet_name=wallet_name, load_wallet=False)1529# The wallet should NOT be loaded after migration1530assert wallet_name not in self.master_node.listwallets()1532# The returned wallet_name should be populated correctly1533assert_equal(migrate_info["wallet_name"], wallet_name)1535# The backup path should be reported and exist on disk1536assert os.path.exists(migrate_info["backup_path"])1538# The migrated wallet files should be on disk in SQLite format1539self.assert_is_sqlite(wallet_name, wallet_loaded = False)1541# Load the wallet and verify its state is correct after migration1542self.master_node.loadwallet(wallet_name)1543loaded_wallet = self.master_node.get_wallet_rpc(wallet_name)1544info = loaded_wallet.getwalletinfo()1545assert_equal(info["descriptors"], True)1546assert_equal(info["format"], "sqlite")1547loaded_wallet.gettransaction(txid)1548assert_equal(loaded_wallet.getbalance(), bals["mine"]["trusted"])1549loaded_wallet.unloadwallet()1551def test_no_load_unnamed_wallet_does_not_autoload(self):1552self.log.info("Test no-load migration of unnamed wallet suppresses default autoload")1553self.master_node = self.nodes[0]1554self.old_node = self.nodes[1]1556wallet = self.create_legacy_wallet("", load_on_startup=False)1557wallet.unloadwallet()1559self.stop_node(0)1561def remove_wallet_setting(node):1562settings_path = node.chain_path / "settings.json"1563if not settings_path.exists():1564return1565with settings_path.open(encoding="utf8") as settings_file:1566settings = json.load(settings_file)1567settings.pop("wallet", None)1568with settings_path.open("w", encoding="utf8") as settings_file:1569json.dump(settings, settings_file, indent=4)1570settings_file.write("\n")1572remove_wallet_setting(self.master_node)1573(self.master_node.wallets_path / "wallet.dat").unlink(missing_ok=True)1574shutil.copyfile(self.old_node.wallets_path / "wallet.dat", self.master_node.wallets_path / "wallet.dat")1575self.start_node(0)1577assert_equal(self.master_node.listwallets(), [])1579migrate_info = self.master_node.migratewallet(wallet_name="", load_wallet=False)1580assert_equal(migrate_info["wallet_name"], "")1581backup_path = Path(migrate_info["backup_path"])1582assert "" not in self.master_node.listwallets()1584with (self.master_node.chain_path / "settings.json").open(encoding="utf8") as settings_file:1585wallet_setting_after_migration = json.load(settings_file).get("wallet")1587self.restart_node(0)1588wallets_after_restart = self.master_node.listwallets()1590assert "" not in wallets_after_restart1592self.clear_default_wallet(backup_path)1593self.master_node.loadwallet(self.default_wallet_name, load_on_startup=True)1595assert_equal(wallet_setting_after_migration, [])1597def test_no_load_reports_auxiliary_wallet_names(self):1598self.log.info("Test no-load migration reports auxiliary wallet names")1599wallet_name = "no_load_auxiliary_names"1600wallet = self.create_legacy_wallet(wallet_name)1602wallet.importaddress(address=self.master_node.get_wallet_rpc(self.default_wallet_name).getnewaddress(), rescan=False)1603_, pubkey = generate_keypair(compressed=True, wif=True)1604wallet.addmultisigaddress(nrequired=1, keys=[pubkey.hex()])1606# Simulate that the wallet was created by the master node with an old version1607# so it should already know the wallet prior migration. Set load_on_startup true,1608# to simulate that the wallet was automatically being loaded on each restart.1609self.master_node.createwallet(wallet_name=wallet_name, load_on_startup=True)1610self.master_node.unloadwallet(wallet_name)1611self.cleanup_folder(self.master_node.wallets_path / wallet_name)1612with (self.master_node.chain_path / "settings.json").open(encoding="utf8") as settings_file:1613assert wallet_name in json.load(settings_file).get("wallet", [])1615self.old_node.unloadwallet(wallet_name)1616shutil.copytree(self.old_node.wallets_path / wallet_name, self.master_node.wallets_path / wallet_name)1618migrate_info = self.master_node.migratewallet(wallet_name=wallet_name, load_wallet=False)1620assert_equal(migrate_info["wallet_name"], wallet_name)1621assert_equal(migrate_info["watchonly_name"], f"{wallet_name}_watchonly")1622assert_equal(migrate_info["solvables_name"], f"{wallet_name}_solvables")1623assert wallet_name not in self.master_node.listwallets()1624assert f"{wallet_name}_watchonly" not in self.master_node.listwallets()1625assert f"{wallet_name}_solvables" not in self.master_node.listwallets()1627# Migrate wallet with load_wallet=False should have overwritten the wallet settings1628# and the load_on_startup setting must removed.1629with (self.master_node.chain_path / "settings.json").open(encoding="utf8") as settings_file:1630startup_wallets = json.load(settings_file).get("wallet", [])1631assert wallet_name not in startup_wallets1632assert f"{wallet_name}_watchonly" not in startup_wallets1633assert f"{wallet_name}_solvables" not in startup_wallets1636def test_solvable_no_privs(self):1637self.log.info("Test migrating a multisig that we do not have any private keys for")1638wallet = self.create_legacy_wallet("multisig_noprivs")1640_, pubkey = generate_keypair(compressed=True, wif=True)1642add_ms_res = wallet.addmultisigaddress(nrequired=1, keys=[pubkey.hex()])1643addr = add_ms_res["address"]1645# The multisig address should be ISMINE_NO but we should have the script info1646addr_info = wallet.getaddressinfo(addr)1647assert_equal(addr_info["ismine"], False)1648assert "hex" in addr_info1650migrate_res, wallet = self.migrate_and_get_rpc("multisig_noprivs")1651assert_equal(migrate_res["solvables_name"], "multisig_noprivs_solvables")1652solvables = self.master_node.get_wallet_rpc(migrate_res["solvables_name"])1654# The multisig should not be in the spendable wallet1655addr_info = wallet.getaddressinfo(addr)1656assert_equal(addr_info["ismine"], False)1657assert "hex" not in addr_info1659# The multisig address should be in the solvables wallet1660addr_info = solvables.getaddressinfo(addr)1661assert_equal(addr_info["ismine"], True)1662assert_equal(addr_info["solvable"], True)1663assert "hex" in addr_info1665def test_loading_failure_after_migration(self):1666self.log.info("Test that a failed loading of the wallet at the end of migration restores the backup")1667self.stop_node(self.old_node.index)1668self.old_node.chain = "signet"1669self.old_node.replace_in_config([("regtest=", "signet="), ("[regtest]", "[signet]")])1670# Disable network sync and prevent disk space warning on small (tmp)fs1671self.start_node(self.old_node.index, extra_args=self.old_node.extra_args + ["-maxconnections=0", "-prune=550"])1673wallet_name = "failed_load_after_migrate"1674self.create_legacy_wallet(wallet_name)1675assert_raises_rpc_error(-4, "Wallet loading failed. Wallet files should not be reused across chains.", lambda: self.migrate_and_get_rpc(wallet_name))1677# Check the wallet we tried to migrate is still BDB1678self.assert_is_bdb(wallet_name)1680self.stop_node(self.old_node.index)1681self.old_node.chain = "regtest"1682self.old_node.replace_in_config([("signet=", "regtest="), ("[signet]", "[regtest]")])1683self.start_node(self.old_node.index)1684self.connect_nodes(1, 0)1686def unsynced_wallet_on_pruned_node_fails(self):1687self.log.info("Test migration of an unsynced wallet on a pruned node fails gracefully if loadwallet is set")1688wallet = self.create_legacy_wallet("", load_on_startup=False)1689last_wallet_synced_block = wallet.getwalletinfo()['lastprocessedblock']['height']1690wallet.unloadwallet()1692shutil.copyfile(self.old_node.wallets_path / "wallet.dat", self.master_node.wallets_path / "wallet.dat")1694# Generate blocks just so the wallet best block is pruned1695self.restart_node(0, ["-fastprune", "-prune=1", "-nowallet"])1696self.connect_nodes(0, 1)1697self.generate(self.master_node, 450, sync_fun=self.no_op)1698self.master_node.pruneblockchain(250)1699# Ensure next block to sync is unavailable1700assert_raises_rpc_error(-1, "Block not available (pruned data)", self.master_node.getblock, self.master_node.getblockhash(last_wallet_synced_block + 1))1702# Check migration failure1703mocked_time = int(time.time())1704self.master_node.setmocktime(mocked_time)1705assert_raises_rpc_error(-4, "last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of a pruned node)", self.master_node.migratewallet, wallet_name="")1706self.master_node.setmocktime(0)1708# Verify the /wallets/ path exists, the wallet is still BDB and the backup file is there.1709assert self.master_node.wallets_path.exists()1710self.assert_is_bdb("")1711backup_path = self.master_node.wallets_path / f"default_wallet_{mocked_time}.legacy.bak"1712assert backup_path.exists()1714self.log.info("Test migration of an unsynced wallet on a pruned node does not fails if loadwallet is not set")1715self.master_node.migratewallet("", load_wallet=False)1716# The wallet should NOT be loaded after migration1717assert "" not in self.master_node.listwallets()1718# Load the wallet should fail1719assert_raises_rpc_error(-4, "last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of a pruned node)", self.master_node.loadwallet, filename="")1721self.clear_default_wallet(backup_path)1723@staticmethod1724def erase_bdb_record(wallet_dat_path, key):1725data = bytearray(wallet_dat_path.read_bytes())1726idx = data.find(key)1727assert idx != -1, f"{key!r} not found in wallet.dat"1729for i in range(idx, idx + len(key)):1730data[i] = 01732wallet_dat_path.write_bytes(data)1734def test_missing_bestblock(self):1735self.log.info("Test migrating legacy BDB wallet without bestblock record")1736wallet_name = "nobestblock"1737wallet = self.create_legacy_wallet(wallet_name)1738wallet.unloadwallet()1740# Erase block locator records like if this would be a pre-#152 wallet1741self.erase_bdb_record(self.old_node.wallets_path / wallet_name / "wallet.dat", b"bestblock_nomerkle")1742self.erase_bdb_record(self.old_node.wallets_path / wallet_name / "wallet.dat", b"bestblock")1744shutil.copytree(self.old_node.wallets_path / wallet_name, self.master_node.wallets_path / wallet_name, dirs_exist_ok=True)1745# Migrate, checking that rescan occurs1746with self.master_node.assert_debug_log(expected_msgs=["Rescanning"], unexpected_msgs=[]):1747self.master_node.migratewallet(wallet_name)1749wallet = self.master_node.get_wallet_rpc(wallet_name)1750info = wallet.getwalletinfo()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.