|
25 | 25 | from test_framework.util import (
|
26 | 26 | assert_equal,
|
27 | 27 | assert_raises_rpc_error,
|
| 28 | + find_vout_for_address, |
28 | 29 | sha256sum_file,
|
29 | 30 | )
|
30 | 31 | from test_framework.wallet_util import (
|
@@ -1255,6 +1256,64 @@ def test_miniscript(self):
|
1255 | 1256 | assert_equal(watchonly.getaddressinfo(some_keys_addr)["ismine"], True)
|
1256 | 1257 | assert_equal(watchonly.getaddressinfo(all_keys_addr)["ismine"], False)
|
1257 | 1258 |
|
| 1259 | + def test_taproot(self): |
| 1260 | + # It turns out that due to how signing logic works, legacy wallets that have the private key for a Taproot |
| 1261 | + # output key will be able to sign and spend those scripts, even though they would not be detected as ISMINE_SPENDABLE. |
| 1262 | + self.log.info("Test migration of Taproot scripts") |
| 1263 | + def_wallet = self.master_node.get_wallet_rpc(self.default_wallet_name) |
| 1264 | + wallet = self.create_legacy_wallet("taproot") |
| 1265 | + |
| 1266 | + privkey, _ = generate_keypair(compressed=True, wif=True) |
| 1267 | + |
| 1268 | + rawtr_desc = descsum_create(f"rawtr({privkey})") |
| 1269 | + rawtr_addr = self.master_node.deriveaddresses(rawtr_desc)[0] |
| 1270 | + rawtr_spk = self.master_node.validateaddress(rawtr_addr)["scriptPubKey"] |
| 1271 | + tr_desc = descsum_create(f"tr({privkey})") |
| 1272 | + tr_addr = self.master_node.deriveaddresses(tr_desc)[0] |
| 1273 | + tr_spk = self.master_node.validateaddress(tr_addr)["scriptPubKey"] |
| 1274 | + tr_script_desc = descsum_create(f"tr(9ffbe722b147f3035c87cb1c60b9a5947dd49c774cc31e94773478711a929ac0,pk({privkey}))") |
| 1275 | + tr_script_addr = self.master_node.deriveaddresses(tr_script_desc)[0] |
| 1276 | + tr_script_spk = self.master_node.validateaddress(tr_script_addr)["scriptPubKey"] |
| 1277 | + |
| 1278 | + wallet.importaddress(rawtr_spk) |
| 1279 | + wallet.importaddress(tr_spk) |
| 1280 | + wallet.importaddress(tr_script_spk) |
| 1281 | + wallet.importprivkey(privkey) |
| 1282 | + |
| 1283 | + txid = def_wallet.send([{rawtr_addr: 1},{tr_addr: 2}, {tr_script_addr: 3}])["txid"] |
| 1284 | + rawtr_vout = find_vout_for_address(self.master_node, txid, rawtr_addr) |
| 1285 | + tr_vout = find_vout_for_address(self.master_node, txid, tr_addr) |
| 1286 | + tr_script_vout = find_vout_for_address(self.master_node, txid, tr_script_addr) |
| 1287 | + self.generate(self.master_node, 6) |
| 1288 | + assert_equal(wallet.getbalances()["watchonly"]["trusted"], 6) |
| 1289 | + |
| 1290 | + # Check that the rawtr can be spent by the legacy wallet |
| 1291 | + send_res = wallet.send(outputs=[{rawtr_addr: 0.5}], include_watching=True, change_address=def_wallet.getnewaddress(), inputs=[{"txid": txid, "vout": rawtr_vout}]) |
| 1292 | + assert_equal(send_res["complete"], True) |
| 1293 | + self.generate(self.old_node, 6) |
| 1294 | + assert_equal(wallet.getbalances()["watchonly"]["trusted"], 5.5) |
| 1295 | + assert_equal(wallet.getbalances()["mine"]["trusted"], 0) |
| 1296 | + |
| 1297 | + # Check that the tr() cannot be spent by the legacy wallet |
| 1298 | + send_res = wallet.send(outputs=[{def_wallet.getnewaddress(): 4}], include_watching=True, inputs=[{"txid": txid, "vout": tr_vout}, {"txid": txid, "vout": tr_script_vout}]) |
| 1299 | + assert_equal(send_res["complete"], False) |
| 1300 | + |
| 1301 | + res, wallet = self.migrate_and_get_rpc("taproot") |
| 1302 | + |
| 1303 | + # The rawtr should be migrated |
| 1304 | + assert_equal(wallet.getbalances()["mine"], {"trusted": 0.5, "untrusted_pending": 0, "immature": 0}) |
| 1305 | + assert_equal(wallet.getaddressinfo(rawtr_addr)["ismine"], True) |
| 1306 | + assert_equal(wallet.getaddressinfo(tr_addr)["ismine"], False) |
| 1307 | + assert_equal(wallet.getaddressinfo(tr_script_addr)["ismine"], False) |
| 1308 | + |
| 1309 | + # The tr() with some keys should be in the watchonly wallet |
| 1310 | + assert "taproot_watchonly" in self.master_node.listwallets() |
| 1311 | + watchonly = self.master_node.get_wallet_rpc("taproot_watchonly") |
| 1312 | + assert_equal(watchonly.getbalances()["mine"], {"trusted": 5, "untrusted_pending": 0, "immature": 0}) |
| 1313 | + assert_equal(watchonly.getaddressinfo(rawtr_addr)["ismine"], False) |
| 1314 | + assert_equal(watchonly.getaddressinfo(tr_addr)["ismine"], True) |
| 1315 | + assert_equal(watchonly.getaddressinfo(tr_script_addr)["ismine"], True) |
| 1316 | + |
1258 | 1317 | def run_test(self):
|
1259 | 1318 | self.master_node = self.nodes[0]
|
1260 | 1319 | self.old_node = self.nodes[1]
|
@@ -1285,6 +1344,7 @@ def run_test(self):
|
1285 | 1344 | self.test_p2wsh()
|
1286 | 1345 | self.test_disallowed_p2wsh()
|
1287 | 1346 | self.test_miniscript()
|
| 1347 | + self.test_taproot() |
1288 | 1348 |
|
1289 | 1349 | if __name__ == '__main__':
|
1290 | 1350 | WalletMigrationTest(__file__).main()
|
0 commit comments