|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# Copyright (c) 2022 The Bitcoin Core developers |
| 3 | +# Distributed under the MIT software license, see the accompanying |
| 4 | +# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
| 5 | +"""Test Miniscript descriptors integration in the wallet.""" |
| 6 | + |
| 7 | +from test_framework.descriptors import descsum_create |
| 8 | +from test_framework.test_framework import BitcoinTestFramework |
| 9 | +from test_framework.util import assert_equal |
| 10 | + |
| 11 | + |
| 12 | +MINISCRIPTS = [ |
| 13 | + # One of two keys |
| 14 | + "or_b(pk(tpubD6NzVbkrYhZ4XRMcMFMMFvzVt6jaDAtjZhD7JLwdPdMm9xa76DnxYYP7w9TZGJDVFkek3ArwVsuacheqqPog8TH5iBCX1wuig8PLXim4n9a/*),s:pk(tpubD6NzVbkrYhZ4WsqRzDmkL82SWcu42JzUvKWzrJHQ8EC2vEHRHkXj1De93sD3biLrKd8XGnamXURGjMbYavbszVDXpjXV2cGUERucLJkE6cy/*))", |
| 15 | + # A script similar (same spending policy) to BOLT3's offered HTLC (with anchor outputs) |
| 16 | + "or_d(pk(tpubD6NzVbkrYhZ4XRMcMFMMFvzVt6jaDAtjZhD7JLwdPdMm9xa76DnxYYP7w9TZGJDVFkek3ArwVsuacheqqPog8TH5iBCX1wuig8PLXim4n9a/*),and_v(and_v(v:pk(tpubD6NzVbkrYhZ4WsqRzDmkL82SWcu42JzUvKWzrJHQ8EC2vEHRHkXj1De93sD3biLrKd8XGnamXURGjMbYavbszVDXpjXV2cGUERucLJkE6cy/*),or_c(pk(tpubD6NzVbkrYhZ4YNwtTWrKRJQzQX3PjPKeUQg1gYh1hiLMkk1cw8SRLgB1yb7JzE8bHKNt6EcZXkJ6AqpCZL1aaRSjnG36mLgbQvJZBNsjWnG/*),v:hash160(7f999c905d5e35cefd0a37673f746eb13fba3640))),older(1)))", |
| 17 | + # A Revault Unvault policy with the older() replaced by an after() |
| 18 | + "andor(multi(2,tpubD6NzVbkrYhZ4YMQC15JS7QcrsAyfGrGiykweqMmPxTkEVScu7vCZLNpPXW1XphHwzsgmqdHWDQAfucbM72EEB1ZEyfgZxYvkZjYVXx1xS9p/*,tpubD6NzVbkrYhZ4WkCyc7E3z6g6NkypHMiecnwc4DpWHTPqFdteRGkEKukdrSSyJGNnGrHNMfy4BCw2UXo5soYRCtCDDfy4q8pc8oyB7RgTFv8/*),and_v(v:multi(4,030f64b922aee2fd597f104bc6cb3b670f1ca2c6c49b1071a1a6c010575d94fe5a,02abe475b199ec3d62fa576faee16a334fdb86ffb26dce75becebaaedf328ac3fe,0314f3dc33595b0d016bb522f6fe3a67680723d842c1b9b8ae6b59fdd8ab5cccb4,025eba3305bd3c829e4e1551aac7358e4178832c739e4fc4729effe428de0398ab),after(424242)),thresh(4,pkh(tpubD6NzVbkrYhZ4YVrNggiT2ptVHwnFbLBqDkCtV5HkxR4WtcRLAQReKTkqZGNcV6GE7cQsmpBzzSzhk16DUwB1gn1L7ZPnJF2dnNePP1uMBCY/*),a:pkh(tpubD6NzVbkrYhZ4YU9vM1s53UhD75UyJatx8EMzMZ3VUjR2FciNfLLkAw6a4pWACChzobTseNqdWk4G7ZdBqRDLtLSACKykTScmqibb1ZrCvJu/*),a:pkh(tpubD6NzVbkrYhZ4YUHcFfuH9iEBLiH8CBRJTpS7X3qjHmh82m1KCNbzs6w9gyK8oWHSZmKHWcakAXCGfbKg6xoCvKzQCWAHyxaC7QcWfmzyBf4/*),a:pkh(tpubD6NzVbkrYhZ4XXEmQtS3sgxpJbMyMg4McqRR1Af6ULzyrTRnhwjyr1etPD7svap9oFtJf4MM72brUb5o7uvF2Jyszc5c1t836fJW7SX2e8D/*)))", |
| 19 | + # Liquid-like federated pegin with emergency recovery keys |
| 20 | + "or_i(and_b(pk(029ffbe722b147f3035c87cb1c60b9a5947dd49c774cc31e94773478711a929ac0),a:and_b(pk(025f05815e3a1a8a83bfbb03ce016c9a2ee31066b98f567f6227df1d76ec4bd143),a:and_b(pk(025625f41e4a065efc06d5019cbbd56fe8c07595af1231e7cbc03fafb87ebb71ec),a:and_b(pk(02a27c8b850a00f67da3499b60562673dcf5fdfb82b7e17652a7ac54416812aefd),s:pk(03e618ec5f384d6e19ca9ebdb8e2119e5bef978285076828ce054e55c4daf473e2))))),and_v(v:thresh(2,pkh(tpubD6NzVbkrYhZ4YK67cd5fDe4fBVmGB2waTDrAt1q4ey9HPq9veHjWkw3VpbaCHCcWozjkhgAkWpFrxuPMUrmXVrLHMfEJ9auoZA6AS1g3grC/*),a:pkh(033841045a531e1adf9910a6ec279589a90b3b8a904ee64ffd692bd08a8996c1aa),a:pkh(02aebf2d10b040eb936a6f02f44ee82f8b34f5c1ccb20ff3949c2b28206b7c1068)),older(4209713)))", |
| 21 | +] |
| 22 | + |
| 23 | + |
| 24 | +class WalletMiniscriptTest(BitcoinTestFramework): |
| 25 | + def set_test_params(self): |
| 26 | + self.num_nodes = 1 |
| 27 | + |
| 28 | + def skip_test_if_missing_module(self): |
| 29 | + self.skip_if_no_wallet() |
| 30 | + self.skip_if_no_sqlite() |
| 31 | + |
| 32 | + def watchonly_test(self, ms): |
| 33 | + self.log.info(f"Importing Miniscript '{ms}'") |
| 34 | + desc = descsum_create(f"wsh({ms})") |
| 35 | + assert self.ms_wo_wallet.importdescriptors( |
| 36 | + [ |
| 37 | + { |
| 38 | + "desc": desc, |
| 39 | + "active": True, |
| 40 | + "range": 2, |
| 41 | + "next_index": 0, |
| 42 | + "timestamp": "now", |
| 43 | + } |
| 44 | + ] |
| 45 | + )[0]["success"] |
| 46 | + |
| 47 | + self.log.info("Testing we derive new addresses for it") |
| 48 | + assert_equal( |
| 49 | + self.ms_wo_wallet.getnewaddress(), self.funder.deriveaddresses(desc, 0)[0] |
| 50 | + ) |
| 51 | + assert_equal( |
| 52 | + self.ms_wo_wallet.getnewaddress(), self.funder.deriveaddresses(desc, 1)[1] |
| 53 | + ) |
| 54 | + |
| 55 | + self.log.info("Testing we detect funds sent to one of them") |
| 56 | + addr = self.ms_wo_wallet.getnewaddress() |
| 57 | + txid = self.funder.sendtoaddress(addr, 0.01) |
| 58 | + self.wait_until( |
| 59 | + lambda: len(self.ms_wo_wallet.listunspent(minconf=0, addresses=[addr])) == 1 |
| 60 | + ) |
| 61 | + utxo = self.ms_wo_wallet.listunspent(minconf=0, addresses=[addr])[0] |
| 62 | + assert utxo["txid"] == txid and not utxo["solvable"] # No satisfaction logic (yet) |
| 63 | + |
| 64 | + def run_test(self): |
| 65 | + self.log.info("Making a descriptor wallet") |
| 66 | + self.funder = self.nodes[0].get_wallet_rpc(self.default_wallet_name) |
| 67 | + self.nodes[0].createwallet( |
| 68 | + wallet_name="ms_wo", descriptors=True, disable_private_keys=True |
| 69 | + ) |
| 70 | + self.ms_wo_wallet = self.nodes[0].get_wallet_rpc("ms_wo") |
| 71 | + |
| 72 | + # Sanity check we wouldn't let an insane Miniscript descriptor in |
| 73 | + res = self.ms_wo_wallet.importdescriptors( |
| 74 | + [ |
| 75 | + { |
| 76 | + "desc": descsum_create( |
| 77 | + "wsh(and_b(ripemd160(1fd9b55a054a2b3f658d97e6b84cf3ee00be429a),a:1))" |
| 78 | + ), |
| 79 | + "active": False, |
| 80 | + "timestamp": "now", |
| 81 | + } |
| 82 | + ] |
| 83 | + )[0] |
| 84 | + assert not res["success"] |
| 85 | + assert "is not sane: witnesses without signature exist" in res["error"]["message"] |
| 86 | + |
| 87 | + # Test we can track any type of Miniscript |
| 88 | + for ms in MINISCRIPTS: |
| 89 | + self.watchonly_test(ms) |
| 90 | + |
| 91 | + |
| 92 | +if __name__ == "__main__": |
| 93 | + WalletMiniscriptTest().main() |
0 commit comments