From ef20f9f476aa9e38df8358f4a0293cb669f2a336 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Fri, 13 Dec 2024 16:49:40 -0500 Subject: [PATCH] test: Test migration of taproot output scripts --- test/functional/wallet_migration.py | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py index dcdc8c5609d93f..fb94ae08519cc8 100755 --- a/test/functional/wallet_migration.py +++ b/test/functional/wallet_migration.py @@ -24,6 +24,7 @@ from test_framework.util import ( assert_equal, assert_raises_rpc_error, + find_vout_for_address, sha256sum_file, ) from test_framework.wallet_util import ( @@ -1117,6 +1118,62 @@ def test_miniscript(self): assert_equal(watchonly.getaddressinfo(some_keys_addr)["ismine"], True) assert_equal(watchonly.getaddressinfo(all_keys_addr)["ismine"], False) + def test_taproot(self): + # It turns out that due to how signing logic works, legacy wallets that have the private key for a Taproot + # output key will be able to sign and spend those scripts, even though they would not be detected as ISMINE_SPENDABLE. + self.log.info("Test migration of Taproot scripts") + def_wallet = self.master_node.get_wallet_rpc(self.default_wallet_name) + wallet = self.create_legacy_wallet("taproot") + + privkey, _ = generate_keypair(compressed=True, wif=True) + + rawtr_desc = descsum_create(f"rawtr({privkey})") + rawtr_addr = self.master_node.deriveaddresses(rawtr_desc)[0] + rawtr_spk = self.master_node.validateaddress(rawtr_addr)["scriptPubKey"] + tr_desc = descsum_create(f"tr({privkey})") + tr_addr = self.master_node.deriveaddresses(tr_desc)[0] + tr_spk = self.master_node.validateaddress(tr_addr)["scriptPubKey"] + tr_script_desc = descsum_create(f"tr(9ffbe722b147f3035c87cb1c60b9a5947dd49c774cc31e94773478711a929ac0,pk({privkey}))") + tr_script_addr = self.master_node.deriveaddresses(tr_script_desc)[0] + tr_script_spk = self.master_node.validateaddress(tr_script_addr)["scriptPubKey"] + + wallet.importaddress(rawtr_spk) + wallet.importaddress(tr_spk) + wallet.importaddress(tr_script_spk) + wallet.importprivkey(privkey) + + txid = def_wallet.send([{rawtr_addr: 1},{tr_addr: 2}, {tr_script_addr: 3}])["txid"] + rawtr_vout = find_vout_for_address(self.master_node, txid, rawtr_addr) + tr_vout = find_vout_for_address(self.master_node, txid, tr_addr) + tr_script_vout = find_vout_for_address(self.master_node, txid, tr_script_addr) + self.generate(self.master_node, 6) + + # Double check that the rawtr can be spent by the legacy wallet + send_res = wallet.send(outputs=[{rawtr_addr: 0.5}], include_watching=True, change_address=def_wallet.getnewaddress(), inputs=[{"txid": txid, "vout": rawtr_vout}]) + assert_equal(send_res["complete"], True) + self.generate(self.old_node, 6) + assert_equal(wallet.getbalances()["watchonly"]["trusted"], 5.5) + + # Check that the tr() cannot be spent by the legacy wallet + send_res = wallet.send(outputs=[{def_wallet.getnewaddress(): 4}], include_watching=True, inputs=[{"txid": txid, "vout": tr_vout}, {"txid": txid, "vout": tr_script_vout}]) + assert_equal(send_res["complete"], False) + + res, wallet = self.migrate_and_get_rpc("taproot") + + # The rawtr should be migrated + assert_equal(wallet.getbalances()["mine"], {"trusted": 0.5, "untrusted_pending": 0, "immature": 0}) + assert_equal(wallet.getaddressinfo(rawtr_addr)["ismine"], True) + assert_equal(wallet.getaddressinfo(tr_addr)["ismine"], False) + assert_equal(wallet.getaddressinfo(tr_script_addr)["ismine"], False) + + # The miniscript with some keys should be in the watchonly wallet + assert "taproot_watchonly" in self.master_node.listwallets() + watchonly = self.master_node.get_wallet_rpc("taproot_watchonly") + assert_equal(watchonly.getbalances()["mine"], {"trusted": 5, "untrusted_pending": 0, "immature": 0}) + assert_equal(watchonly.getaddressinfo(rawtr_addr)["ismine"], False) + assert_equal(watchonly.getaddressinfo(tr_addr)["ismine"], True) + assert_equal(watchonly.getaddressinfo(tr_script_addr)["ismine"], True) + def run_test(self): self.master_node = self.nodes[0] self.old_node = self.nodes[1] @@ -1145,6 +1202,7 @@ def run_test(self): self.test_migrate_simple_watch_only() self.test_manual_keys_import() self.test_miniscript() + self.test_taproot() if __name__ == '__main__':