Skip to content

Commit

Permalink
test: Test migration of miniscript in legacy wallets
Browse files Browse the repository at this point in the history
  • Loading branch information
achow101 committed Dec 13, 2024
1 parent 5408ca2 commit fd98416
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/wallet/scriptpubkeyman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1955,6 +1955,7 @@ std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
std::string desc_str;
bool watchonly = !desc->ToPrivateString(*this, desc_str);
if (watchonly && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
WalletLogPrintf("%s\n", desc->ToString());
out.watch_descs.emplace_back(desc->ToString(), creation_time);

// Get the scriptPubKeys without writing this to the wallet
Expand Down
55 changes: 55 additions & 0 deletions test/functional/wallet_migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,60 @@ def test_manual_keys_import(self):
assert_equal(expected_descs, migrated_desc)
wo_wallet.unloadwallet()

def test_miniscript(self):
# It turns out that due to how signing logic works, legacy wallets that have valid miniscript witnessScripts
# and the private keys for them can still sign and spend them, even though output scripts involving them
# as a witnessScript would not be detected as ISMINE_SPENDABLE.
self.log.info("Test migration of a legacy wallet containing miniscript")
def_wallet = self.master_node.get_wallet_rpc(self.default_wallet_name)
wallet = self.create_legacy_wallet("miniscript")

privkey, _ = generate_keypair(compressed=True, wif=True)

# Make a descriptor where we only have some of the keys. This will be migrated to the watchonly wallet.
some_keys_priv_desc = descsum_create(f"wsh(or_b(pk({privkey}),s:pk(029ffbe722b147f3035c87cb1c60b9a5947dd49c774cc31e94773478711a929ac0)))")
some_keys_addr = self.master_node.deriveaddresses(some_keys_priv_desc)[0]

# Make a descriptor where we have all of the keys. This will stay in the migrated wallet
all_keys_priv_desc = descsum_create(f"wsh(and_v(v:pk({privkey}),1))")
all_keys_addr = self.master_node.deriveaddresses(all_keys_priv_desc)[0]

imp = wallet.importmulti([
{
"desc": some_keys_priv_desc,
"timestamp": "now",
},
{
"desc": all_keys_priv_desc,
"timestamp": "now",
}
])
assert_equal(imp[0]["success"], True)
assert_equal(imp[1]["success"], True)

def_wallet.sendtoaddress(some_keys_addr, 1)
def_wallet.sendtoaddress(all_keys_addr, 1)
self.generate(self.master_node, 6)
# Double check that the miniscript can be spent by the legacy wallet
send_res = wallet.send(outputs=[{some_keys_addr: 1},{all_keys_addr: 0.75}], include_watching=True, change_address=def_wallet.getnewaddress())
assert_equal(send_res["complete"], True)
self.generate(self.old_node, 6)
assert_equal(wallet.getbalances()["watchonly"]["trusted"], 1.75)

res, wallet = self.migrate_and_get_rpc("miniscript")

# The miniscript with all keys should be in the migrated wallet
assert_equal(wallet.getbalances()["mine"], {"trusted": 0.75, "untrusted_pending": 0, "immature": 0})
assert_equal(wallet.getaddressinfo(all_keys_addr)["ismine"], True)
assert_equal(wallet.getaddressinfo(some_keys_addr)["ismine"], False)

# The miniscript with some keys should be in the watchonly wallet
assert "miniscript_watchonly" in self.master_node.listwallets()
watchonly = self.master_node.get_wallet_rpc("miniscript_watchonly")
assert_equal(watchonly.getbalances()["mine"], {"trusted": 1, "untrusted_pending": 0, "immature": 0})
assert_equal(watchonly.getaddressinfo(some_keys_addr)["ismine"], True)
assert_equal(watchonly.getaddressinfo(all_keys_addr)["ismine"], False)

def run_test(self):
self.master_node = self.nodes[0]
self.old_node = self.nodes[1]
Expand Down Expand Up @@ -1087,6 +1141,7 @@ def run_test(self):
self.test_blank()
self.test_migrate_simple_watch_only()
self.test_manual_keys_import()
self.test_miniscript()


if __name__ == '__main__':
Expand Down

0 comments on commit fd98416

Please sign in to comment.