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.
1001assert_equal(wallet.gettransaction(txid=child_txid)["confirmations"], -1)1002assert_equal(wallet.gettransaction(txid=conflict_txid)["confirmations"], 1)1004wallet.unloadwallet()1006def test_hybrid_pubkey(self):1007self.log.info("Test migration when wallet contains a hybrid pubkey")1009wallet = self.create_legacy_wallet("hybrid_keys")1011# Get the hybrid pubkey for one of the keys in the wallet1012normal_pubkey = wallet.getaddressinfo(wallet.getnewaddress())["pubkey"]1013first_byte = bytes.fromhex(normal_pubkey)[0] + 4 # Get the hybrid pubkey first byte1014parsed_pubkey = ECPubKey()1015parsed_pubkey.set(bytes.fromhex(normal_pubkey))1016parsed_pubkey.compressed = False1017hybrid_pubkey_bytes = bytearray(parsed_pubkey.get_bytes())1018hybrid_pubkey_bytes[0] = first_byte # Make it hybrid1019hybrid_pubkey = hybrid_pubkey_bytes.hex()1021# Import the hybrid pubkey1022wallet.importpubkey(hybrid_pubkey)1023p2pkh_addr = key_to_p2pkh(hybrid_pubkey)1024p2pkh_addr_info = wallet.getaddressinfo(p2pkh_addr)1025assert_equal(p2pkh_addr_info["iswatchonly"], True)1026assert_equal(p2pkh_addr_info["ismine"], False) # Things involving hybrid pubkeys are not spendable1028# Also import the p2wpkh for the pubkey to make sure we don't migrate it1029p2wpkh_addr = key_to_p2wpkh(hybrid_pubkey)1030wallet.importaddress(p2wpkh_addr)1032migrate_info, wallet = self.migrate_and_get_rpc("hybrid_keys")1034# Both addresses should only appear in the watchonly wallet1035p2pkh_addr_info = wallet.getaddressinfo(p2pkh_addr)1036assert_equal(p2pkh_addr_info["ismine"], False)1037p2wpkh_addr_info = wallet.getaddressinfo(p2wpkh_addr)1038assert_equal(p2wpkh_addr_info["ismine"], False)1040watchonly_wallet = self.master_node.get_wallet_rpc(migrate_info["watchonly_name"])1041watchonly_p2pkh_addr_info = watchonly_wallet.getaddressinfo(p2pkh_addr)1042assert_equal(watchonly_p2pkh_addr_info["ismine"], True)1043watchonly_p2wpkh_addr_info = watchonly_wallet.getaddressinfo(p2wpkh_addr)1044assert_equal(watchonly_p2wpkh_addr_info["ismine"], True)1046# There should only be raw or addr descriptors1047for desc in watchonly_wallet.listdescriptors()["descriptors"]:1048if desc["desc"].startswith("raw(") or desc["desc"].startswith("addr("):1049continue1050assert False, "Hybrid pubkey watchonly wallet has more than just raw() and addr()"1052wallet.unloadwallet()1054def test_failed_migration_cleanup(self):1055self.log.info("Test that a failed migration is cleaned up")1056wallet = self.create_legacy_wallet("failed")1058# Make a copy of the wallet with the solvables wallet name so that we are unable1059# to create the solvables wallet when migrating, thus failing to migrate1060wallet.unloadwallet()1061solvables_path = self.master_node.wallets_path / "failed_solvables"1062shutil.copytree(self.old_node.wallets_path / "failed", solvables_path)1063original_shasum = sha256sum_file(solvables_path / "wallet.dat")1065self.old_node.loadwallet("failed")1067# Add a multisig so that a solvables wallet is created1068wallet.addmultisigaddress(2, [wallet.getnewaddress(), get_generate_key().pubkey])1069wallet.importaddress(get_generate_key().p2pkh_addr)1071self.old_node.unloadwallet("failed")1072shutil.copytree(self.old_node.wallets_path / "failed", self.master_node.wallets_path / "failed")1073assert_raises_rpc_error(-4, "Failed to create database", self.master_node.migratewallet, "failed")1075assert all(wallet not in self.master_node.listwallets() for wallet in ["failed", "failed_watchonly", "failed_solvables"])1077assert not (self.master_node.wallets_path / "failed_watchonly").exists()1078# Since the file in failed_solvables is one that we put there, migration shouldn't touch it1079assert solvables_path.exists()1080new_shasum = sha256sum_file(solvables_path / "wallet.dat")1081assert_equal(original_shasum, new_shasum)1083# Check the wallet we tried to migrate is still BDB1084self.assert_is_bdb("failed")1086def test_blank(self):1087self.log.info("Test that a blank wallet is migrated")1088wallet = self.create_legacy_wallet("blank", blank=True)1089assert_equal(wallet.getwalletinfo()["blank"], True)1090_, wallet = self.migrate_and_get_rpc("blank")1091assert_equal(wallet.getwalletinfo()["blank"], True)1093def test_avoidreuse(self):1094self.log.info("Test that avoidreuse persists after migration")1095def_wallet = self.master_node.get_wallet_rpc(self.default_wallet_name)1097wallet = self.create_legacy_wallet("avoidreuse")1098wallet.setwalletflag("avoid_reuse", True)1100# Import a pubkey to the test wallet and send some funds to it1101reused_imported_addr = def_wallet.getnewaddress()1102wallet.importpubkey(def_wallet.getaddressinfo(reused_imported_addr)["pubkey"])1103imported_utxos = self.create_outpoints(def_wallet, outputs=[{reused_imported_addr: 2}])1104def_wallet.lockunspent(False, imported_utxos)1106# Send funds to the test wallet1107reused_addr = wallet.getnewaddress()1108def_wallet.sendtoaddress(reused_addr, 2)1110self.generate(self.master_node, 1)1112# Send funds from the test wallet with both its own and the imported1113wallet.sendall([def_wallet.getnewaddress()])1114def_wallet.sendall(recipients=[def_wallet.getnewaddress()], inputs=imported_utxos)1115self.generate(self.master_node, 1)1116balances = wallet.getbalances()1117assert_equal(balances["mine"]["trusted"], 0)1118assert_equal(balances["watchonly"]["trusted"], 0)1120# Reuse the addresses1121def_wallet.sendtoaddress(reused_addr, 1)1122def_wallet.sendtoaddress(reused_imported_addr, 1)1123self.generate(self.master_node, 1)1124balances = wallet.getbalances()1125assert_equal(balances["mine"]["used"], 1)1126# Reused watchonly will not show up in balances1127assert_equal(balances["watchonly"]["trusted"], 0)1128assert_equal(balances["watchonly"]["untrusted_pending"], 0)1129assert_equal(balances["watchonly"]["immature"], 0)1131utxos = wallet.listunspent()1132assert_equal(len(utxos), 2)1133for utxo in utxos:1134assert_equal(utxo["reused"], True)1136# Migrate1137_, wallet = self.migrate_and_get_rpc("avoidreuse")1138watchonly_wallet = self.master_node.get_wallet_rpc("avoidreuse_watchonly")1140# One utxo in each wallet, marked used1141utxos = wallet.listunspent()1142assert_equal(len(utxos), 1)1143assert_equal(utxos[0]["reused"], True)1144watchonly_utxos = watchonly_wallet.listunspent()1145assert_equal(len(watchonly_utxos), 1)1146assert_equal(watchonly_utxos[0]["reused"], True)1148def test_preserve_tx_extra_info(self):1149self.log.info("Test that tx extra data is preserved after migration")1150def_wallet = self.master_node.get_wallet_rpc(self.default_wallet_name)1152# Create and fund wallet1153wallet = self.create_legacy_wallet("persist_comments")1154def_wallet.sendtoaddress(wallet.getnewaddress(), 2)1156self.generate(self.master_node, 6)1158# Create tx and bump it to store 'replaced_by_txid' and 'replaces_txid' data within the transactions.1159# Additionally, store an extra comment within the original tx.1160extra_comment = "don't discard me"1161original_tx_id = wallet.sendtoaddress(address=wallet.getnewaddress(), amount=1, comment=extra_comment)1162bumped_tx = wallet.bumpfee(txid=original_tx_id)1164def check_comments():1165for record in wallet.listtransactions():1166if record["txid"] == original_tx_id:1167assert_equal(record["replaced_by_txid"], bumped_tx["txid"])1168assert_equal(record['comment'], extra_comment)1169elif record["txid"] == bumped_tx["txid"]:1170assert_equal(record["replaces_txid"], original_tx_id)1172# Pre-migration verification1173check_comments()1174# Migrate1175_, wallet = self.migrate_and_get_rpc("persist_comments")1176# Post-migration verification1177check_comments()1179wallet.unloadwallet()1181def test_migrate_simple_watch_only(self):1182self.log.info("Test migrating a watch-only p2pk script")1183wallet = self.create_legacy_wallet("bare_p2pk", blank=True)1184_, pubkey = generate_keypair()1185p2pk_script = key_to_p2pk_script(pubkey)1186wallet.importaddress(address=p2pk_script.hex())1187# Migrate wallet in the latest node1188res, _ = self.migrate_and_get_rpc("bare_p2pk")1189wo_wallet = self.master_node.get_wallet_rpc(res['wallet_name'])1190assert_equal(wo_wallet.listdescriptors()['descriptors'][0]['desc'], descsum_create(f'pk({pubkey.hex()})'))1191assert_equal(wo_wallet.getwalletinfo()["private_keys_enabled"], False)1193# Ensure that migrating a wallet with watch-only scripts does not create a spendable wallet.1194assert_equal('bare_p2pk_watchonly', res['wallet_name'])1195assert "bare_p2pk" not in self.master_node.listwallets()1196assert "bare_p2pk" not in [w["name"] for w in self.master_node.listwalletdir()["wallets"]]1198wo_wallet.unloadwallet()1200def test_manual_keys_import(self):1201self.log.info("Test migrating standalone private keys")1202wallet = self.create_legacy_wallet("import_privkeys", blank=True)1203privkey, pubkey = generate_keypair(wif=True)1204wallet.importprivkey(privkey=privkey, label="hi", rescan=False)1206# Migrate and verify1207res, wallet = self.migrate_and_get_rpc("import_privkeys")1209# There should be descriptors containing the imported key for: pk(), pkh(), sh(wpkh()), wpkh()1210key_origin = hash160(pubkey)[:4].hex()1211pubkey_hex = pubkey.hex()1212combo_desc = descsum_create(f"combo([{key_origin}]{pubkey_hex})")1214# Verify all expected descriptors were migrated1215migrated_desc = [item['desc'] for item in wallet.listdescriptors()['descriptors'] if pubkey.hex() in item['desc']]1216assert_equal([combo_desc], migrated_desc)1217wallet.unloadwallet()1219######################################################1220self.log.info("Test migrating standalone public keys")1221wallet = self.create_legacy_wallet("import_pubkeys", blank=True)1222wallet.importpubkey(pubkey=pubkey_hex, rescan=False)1224res, _ = self.migrate_and_get_rpc("import_pubkeys")1226# Same as before, there should be descriptors in the watch-only wallet for the imported pubkey1227wo_wallet = self.nodes[0].get_wallet_rpc(res['wallet_name'])1228# Assert this is a watch-only wallet1229assert_equal(wo_wallet.getwalletinfo()["private_keys_enabled"], False)1230# As we imported the pubkey only, there will be no key origin in the following descriptors1231pk_desc = descsum_create(f'pk({pubkey_hex})')1232pkh_desc = descsum_create(f'pkh({pubkey_hex})')1233sh_wpkh_desc = descsum_create(f'sh(wpkh({pubkey_hex}))')1234wpkh_desc = descsum_create(f'wpkh({pubkey_hex})')1235expected_descs = [pk_desc, pkh_desc, sh_wpkh_desc, wpkh_desc]1237# Verify all expected descriptors were migrated1238migrated_desc = [item['desc'] for item in wo_wallet.listdescriptors()['descriptors']]1239assert_equal(expected_descs, migrated_desc)1240# Ensure that migrating a wallet with watch-only scripts does not create a spendable wallet.1241assert_equal('import_pubkeys_watchonly', res['wallet_name'])1242assert "import_pubkeys" not in self.master_node.listwallets()1243assert "import_pubkeys" not in [w["name"] for w in self.master_node.listwalletdir()["wallets"]]1244wo_wallet.unloadwallet()1246def test_p2wsh(self):1247self.log.info("Test that non-multisig P2WSH output scripts are migrated")1248def_wallet = self.master_node.get_wallet_rpc(self.default_wallet_name)1250wallet = self.create_legacy_wallet("p2wsh")
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.