1#!/usr/bin/env python3
2# Copyright (c) 2021 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 generation and spending of P2TR addresses."""
6
7import random
8
9from decimal import Decimal
10from test_framework.test_framework import BitcoinTestFramework
11from test_framework.util import assert_equal
12from test_framework.descriptors import descsum_create
13from test_framework.script import (CScript, OP_CHECKSIG, taproot_construct)
14from test_framework.segwit_addr import encode_segwit_address
15
16# xprvs/xpubs, and m/* derived x-only pubkeys (created using independent implementation)
17KEYS = [
18    {
19        "xprv": "tprv8ZgxMBicQKsPeNLUGrbv3b7qhUk1LQJZAGMuk9gVuKh9sd4BWGp1eMsehUni6qGb8bjkdwBxCbgNGdh2bYGACK5C5dRTaif9KBKGVnSezxV",
20        "xpub": "tpubD6NzVbkrYhZ4XqNGAWGWSzmxGWFwVjVTjZxh2fioKbVYi7Jx8fdbprVWsdW7mHwqjchBVas8TLZG4Xwuz4RKU4iaCqiCvoSkFCzQptqk5Y1",
21        "pubs": [
22            "83d8ee77a0f3a32a5cea96fd1624d623b836c1e5d1ac2dcde46814b619320c18",
23            "a30253b018ea6fca966135bf7dd8026915427f24ccf10d4e03f7870f4128569b",
24            "a61e5749f2f3db9dc871d7b187e30bfd3297eea2557e9be99897ea8ff7a29a21",
25            "8110cf482f66dc37125e619d73075af932521724ffc7108309e88f361efe8c8a",
26        ]
27    },
28    {
29        "xprv": "tprv8ZgxMBicQKsPe98QUPieXy5KFPVjuZNpcC9JY7K7buJEm8nWvJogK4kTda7eLjK9U4PnMNbSjEkpjDJazeBZ4rhYNYD7N6GEdaysj1AYSb5",
30        "xpub": "tpubD6NzVbkrYhZ4XcACN3PEwNjRpR1g4tZjBVk5pdMR2B6dbd3HYhdGVZNKofAiFZd9okBserZvv58A6tBX4pE64UpXGNTSesfUW7PpW36HuKz",
31        "pubs": [
32            "f95886b02a84928c5c15bdca32784993105f73de27fa6ad8c1a60389b999267c",
33            "71522134160685eb779857033bfc84c7626f13556154653a51dd42619064e679",
34            "48957b4158b2c5c3f4c000f51fd2cf0fd5ff8868ebfb194256f5e9131fc74bd8",
35            "086dda8139b3a84944010648d2b674b70447be3ae59322c09a4907bc80be62c1",
36        ]
37    },
38    {
39        "xprv": "tprv8ZgxMBicQKsPe3ZJmcj9aJ2EPZJYYCh6Lp3v82p75wspgaXmtDZ2RBtkAtWcGnW2VQDzMHQPBkCKMoYTqh1RfJKjv4PcmWVR7KqTpjsdboN",
40        "xpub": "tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy",
41        "pubs": [
42            "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f7",
43            "8a104c54cd34acba60c97dd8f1f7abc89ba9587afd88dc928e91aca7b1c50d20",
44            "13ba6b252a4eb5ef31d39cb521724cdab19a698323f5c17093f28fb1821d052f",
45            "f6c2b4863fd5ba1ba09e3a890caed8b75ffbe013ebab31a06ab87cd6f72506af",
46        ]
47    },
48    {
49        "xprv": "tprv8ZgxMBicQKsPdKziibn63Rm6aNzp7dSjDnufZMStXr71Huz7iihCRpbZZZ6Voy5HyuHCWx6foHMipzMzUq4tZrtkZ24DJwz5EeNWdsuwX5h",
50        "xpub": "tpubD6NzVbkrYhZ4Wo2WcFSgSqRD9QWkGxddo6WSqsVBx7uQ8QEtM7WncKDRjhFEexK119NigyCsFygA4b7sAPQxqebyFGAZ9XVV1BtcgNzbCRR",
51        "pubs": [
52            "03a669ea926f381582ec4a000b9472ba8a17347f5fb159eddd4a07036a6718eb",
53            "bbf56b14b119bccafb686adec2e3d2a6b51b1626213590c3afa815d1fd36f85d",
54            "2994519e31bbc238a07d82f85c9832b831705d2ee4a2dbb477ecec8a3f570fe5",
55            "68991b5c139a4c479f8c89d6254d288c533aefc0c5b91fac6c89019c4de64988",
56        ]
57    },
58    {
59        "xprv": "tprv8ZgxMBicQKsPen4PGtDwURYnCtVMDejyE8vVwMGhQWfVqB2FBPdekhTacDW4vmsKTsgC1wsncVqXiZdX2YFGAnKoLXYf42M78fQJFzuDYFN",
60        "xpub": "tpubD6NzVbkrYhZ4YF6BAXtXsqCtmv1HNyvsoSXHDsJzpnTtffH1onTEwC5SnLzCHPKPebh2i7Gxvi9kJNADcpuSmH8oM3rCYcHVtdXHjpYoKnX",
61        "pubs": [
62            "aba457d16a8d59151c387f24d1eb887efbe24644c1ee64b261282e7baebdb247",
63            "c8558b7caf198e892032d91f1a48ee9bdc25462b83b4d0ac62bb7fb2a0df630e",
64            "8a4bcaba0e970685858d133a4d0079c8b55bbc755599e212285691eb779ce3dc",
65            "b0d68ada13e0d954b3921b88160d4453e9c151131c2b7c724e08f538a666ceb3",
66        ]
67    },
68    {
69        "xprv": "tprv8ZgxMBicQKsPd91vCgRmbzA13wyip2RimYeVEkAyZvsEN5pUSB3T43SEBxPsytkxb42d64W2EiRE9CewpJQkzR8HKHLV8Uhk4dMF5yRPaTv",
70        "xpub": "tpubD6NzVbkrYhZ4Wc3i6L6N1Pp7cyVeyMcdLrFGXGDGzCfdCa5F4Zs3EY46N72Ws8QDEUYBVwXfDfda2UKSseSdU1fsBegJBhGCZyxkf28bkQ6",
71        "pubs": [
72            "9b4d495b74887815a1ff623c055c6eac6b6b2e07d2a016d6526ebac71dd99744",
73            "8e971b781b7ce7ab742d80278f2dfe7dd330f3efd6d00047f4a2071f2e7553cb",
74            "b811d66739b9f07435ccda907ec5cd225355321c35e0a7c7791232f24cf10632",
75            "4cd27a5552c272bc80ba544e9cc6340bb906969f5e7a1510b6cef9592683fbc9",
76        ]
77    },
78    {
79        "xprv": "tprv8ZgxMBicQKsPdEhLRxxwzTv2t18j7ruoffPeqAwVA2qXJ2P66RaMZLUWQ85SjoA7xPxdSgCB9UZ72m65qbnaLPtFTfHVP3MEmkpZk1Bv8RT",
80        "xpub": "tpubD6NzVbkrYhZ4Whj8KcdYPsa9T2efHC6iExzS7gynaJdv8WdripPwjq6NaH5gQJGrLmvUwHY1smhiakUosXNDTEa6qfKUQdLKV6DJBre6XvQ",
81        "pubs": [
82            "d0c19def28bb1b39451c1a814737615983967780d223b79969ba692182c6006b",
83            "cb1d1b1dc62fec1894d4c3d9a1b6738e5ff9c273a64f74e9ab363095f45e9c47",
84            "245be588f41acfaeb9481aa132717db56ee1e23eb289729fe2b8bde8f9a00830",
85            "5bc4ad6d6187fa82728c85a073b428483295288f8aef5722e47305b5872f7169",
86        ]
87    },
88    {
89        "xprv": "tprv8ZgxMBicQKsPcxbqxzcMAwQpiCD8x6qaZEJTxdKxw4w9GuMzDACTD9yhEsHGfqQcfYX4LivosLDDngTykYEp9JnTdcqY7cHqU8PpeFFKyV3",
90        "xpub": "tpubD6NzVbkrYhZ4WRddreGwaM4wHDj57S2V8XuFF9NGMLjY7PckqZ23PebZR1wGA4w84uX2vZphdZVsnREjij1ibYjEBTaTVQCEZCLs4xUDapx",
91        "pubs": [
92            "065cc1b92bd99e5a3e626e8296a366b2d132688eb43aea19bc14fd8f43bf07fb",
93            "5b95633a7dda34578b6985e6bfd85d83ec38b7ded892a9b74a3d899c85890562",
94            "dc86d434b9a34495c8e845b969d51f80d19a8df03b400353ffe8036a0c22eb60",
95            "06c8ffde238745b29ae8a97ae533e1f3edf214bba6ec58b5e7b9451d1d61ec19",
96        ]
97    },
98    {
99        "xprv": "tprv8ZgxMBicQKsPe6zLoU8MTTXgsdJVNBErrYGpoGwHf5VGvwUzdNc7NHeCSzkJkniCxBhZWujXjmD4HZmBBrnr3URgJjM6GxRgMmEhLdqNTWG",
100        "xpub": "tpubD6NzVbkrYhZ4Xa28h7nwrsBoSepRXWRmRqsc5nyb5MHfmRjmFmRhYnG4d9dC7uxixN5AfsEv1Lz3mCAuWvERyvPgKozHUVjfo8EG6foJGy7",
101        "pubs": [
102            "d826a0a53abb6ffc60df25b9c152870578faef4b2eb5a09bdd672bbe32cdd79b",
103            "939365e0359ff6bc6f6404ee220714c5d4a0d1e36838b9e2081ede217674e2ba",
104            "4e8767edcf7d3d90258cfbbea01b784f4d2de813c4277b51279cf808bac410a2",
105            "d42a2c280940bfc6ede971ae72cde2e1df96c6da7dab06a132900c6751ade208",
106        ]
107    },
108    {
109        "xprv": "tprv8ZgxMBicQKsPeB5o5oCsN2dVxM2mtJiYERQEBRc4JNwC1DFGYaEdNkmh8jJYVPU76YhkFoRoWTdh1p3yQGykG8TfDW34dKgrgSx28gswUyL",
110        "xpub": "tpubD6NzVbkrYhZ4Xe7aySsTmSHcXNYi3duSoj11TweMiejaqhW3Ay4DZFPZJses4sfpk4b9VHRhn8v4cKTMjugMM3hqXcqSSmRdiW8QvASXjfY",
111        "pubs": [
112            "e360564b2e0e8d06681b6336a29d0750210e8f34afd9afb5e6fd5fe6dba26c81",
113            "76b4900f00a1dcce463b6d8e02b768518fce4f9ecd6679a13ad78ea1e4815ad3",
114            "5575556e263c8ed52e99ab02147cc05a738869afe0039911b5a60a780f4e43d2",
115            "593b00e2c8d4bd6dda0fd9e238888acf427bb4e128887fd5a40e0e9da78cbc01",
116        ]
117    },
118    {
119        "xprv": "tprv8ZgxMBicQKsPfEH6jHemkGDjZRnAaKFJVGH8pQU638E6SdbX9hxit1tK2sfFPfL6KS7v8FfUKxstbfEpzSymbdfBM9Y5UkrxErF9fJaKLK3",
120        "xpub": "tpubD6NzVbkrYhZ4YhJtcwKN9fsr8TJ6jeSD4Zsv6vWPTQ2VH7rHn6nK4WWBCzKK7FkdVVwm3iztCU1UmStY4hX6gRbBmp9UzK9C59dQEzeXS12",
121        "pubs": [
122            "7631cacec3343052d87ef4d0065f61dde82d7d2db0c1cc02ef61ef3c982ea763",
123            "c05e44a9e735d1b1bef62e2c0d886e6fb4923b2649b67828290f5cacc51c71b7",
124            "b33198b20701afe933226c92fd0e3d51d3f266f1113d864dbd026ae3166ef7f2",
125            "f99643ac3f4072ee4a949301e86963a9ca0ad57f2ef29f6b84fda037d7cac85b",
126        ]
127    },
128    {
129        "xprv": "tprv8ZgxMBicQKsPdNWU38dT6aGxtqJR4oYS5kPpLVBcuKiiu7gqTYqMMqhUG6DP7pPahzPQu36sWSmeLCP1C4AwqcR5FX2RyRoZfd4B8pAnSdX",
130        "xpub": "tpubD6NzVbkrYhZ4WqYFvnJ3Vyw5TrpME8jLf3zbd1DvKbX7jbwc5wewYLKLSFRzZWV6hZj7XhsXAy7fhE5jB25DiWyNM3ztXbsXHRVCrp5BiPY",
131        "pubs": [
132            "2258b1c3160be0864a541854eec9164a572f094f7562628281a8073bb89173a7",
133            "83df59d0a5c951cdd62b7ab225a62079f48d2a333a86e66c35420d101446e92e",
134            "2a654bf234d819055312f9ca03fad5836f9163b09cdd24d29678f694842b874a",
135            "aa0334ab910047387c912a21ec0dab806a47ffa38365060dbc5d47c18c6e66e7",
136        ]
137    },
138    {
139        "xprv": "tprv8mGPkMVz5mZuJDnC2NjjAv7E9Zqa5LCgX4zawbZu5nzTtLb5kGhPwycX4H1gtW1f5ZdTKTNtQJ61hk71F2TdcQ93EFDTpUcPBr98QRji615",
140        "xpub": "tpubDHxRtmYEE9FaBgoyv2QKaKmLibMWEfPb6NbNE7cCW4nripqrNfWz8UEPEPbHCrakwLvwFfsqoaf4pjX4gWStp4nECRf1QwBKPkLqnY8pHbj",
141        "pubs": [
142            "00a9da96087a72258f83b338ef7f0ea8cbbe05da5f18f091eb397d1ecbf7c3d3",
143            "b2749b74d51a78f5fe3ebb3a7c0ff266a468cade143dfa265c57e325177edf00",
144            "6b8747a6bbe4440d7386658476da51f6e49a220508a7ec77fe7bccc3e7baa916",
145            "4674bf4d9ebbe01bf0aceaca2472f63198655ecf2df810f8d69b38421972318e",
146        ]
147    }
148]
149
150CHANGE_XPRV = "tprv8ZgxMBicQKsPcyDrWwiecVnTtFmfRwbfFqEfR4ZGWvq5aTTwLBWmAm5zrbMcYtb9gQNFfhRfqhhrBG37U3nhmXxEgeEPBJGHAPrHCrAd1WX"
151CHANGE_XPUB = "tpubD6NzVbkrYhZ4WSFeQbPF1uSaTHHbbGnZq8qShabZwCdUQwihxaLMMFhs2kidGF2qrRKiQVqw8VoyuTHj1bZqmMXMeciaU1gBjWA1sim2zUB"
152
153# Point with no known discrete log.
154H_POINT = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
155
156
157def key(hex_key):
158    """Construct an x-only pubkey from its hex representation."""
159    return bytes.fromhex(hex_key)
160
161def pk(hex_key):
162    """Construct a script expression for taproot_construct for pk(hex_key)."""
163    return (None, CScript([bytes.fromhex(hex_key), OP_CHECKSIG]))
164
165def compute_taproot_address(pubkey, scripts):
166    """Compute the address for a taproot output with given inner key and scripts."""
167    tap = taproot_construct(pubkey, scripts)
168    assert tap.scriptPubKey[0] == 0x51
169    assert tap.scriptPubKey[1] == 0x20
170    return encode_segwit_address("bcrt", 1, tap.scriptPubKey[2:])
171
172class WalletTaprootTest(BitcoinTestFramework):
173    """Test generation and spending of P2TR address outputs."""
174
175    def set_test_params(self):
176        self.num_nodes = 3
177        self.setup_clean_chain = True
178        self.extra_args = [['-keypool=100'], ['-keypool=100'], ["-vbparams=taproot:1:1"]]
179        self.supports_cli = False
180
181    def skip_test_if_missing_module(self):
182        self.skip_if_no_wallet()
183        self.skip_if_no_sqlite()
184
185    def setup_network(self):
186        self.setup_nodes()
187
188    def init_wallet(self, i):
189        pass
190
191    @staticmethod
192    def rand_keys(n):
193        ret = []
194        idxes = set()
195        for _ in range(n):
196            while True:
197                i = random.randrange(len(KEYS))
198                if not i in idxes:
199                    break
200            idxes.add(i)
201            ret.append(KEYS[i])
202        return ret
203
204    @staticmethod
205    def make_desc(pattern, privmap, keys, pub_only = False):
206        pat = pattern.replace("$H", H_POINT)
207        for i in range(len(privmap)):
208            if privmap[i] and not pub_only:
209                pat = pat.replace("$%i" % (i + 1), keys[i]['xprv'])
210            else:
211                pat = pat.replace("$%i" % (i + 1), keys[i]['xpub'])
212        return descsum_create(pat)
213
214    @staticmethod
215    def make_addr(treefn, keys, i):
216        args = []
217        for j in range(len(keys)):
218            args.append(keys[j]['pubs'][i])
219        return compute_taproot_address(*treefn(*args))
220
221    def do_test_addr(self, comment, pattern, privmap, treefn, keys):
222        self.log.info("Testing %s address derivation" % comment)
223        desc = self.make_desc(pattern, privmap, keys, False)
224        desc_pub = self.make_desc(pattern, privmap, keys, True)
225        assert_equal(self.nodes[0].getdescriptorinfo(desc)['descriptor'], desc_pub)
226        result = self.addr_gen.importdescriptors([{"desc": desc_pub, "active": True, "timestamp": "now"}])
227        assert(result[0]['success'])
228        for i in range(4):
229            addr_g = self.addr_gen.getnewaddress(address_type='bech32m')
230            if treefn is not None:
231                addr_r = self.make_addr(treefn, keys, i)
232                assert_equal(addr_g, addr_r)
233            desc_a = self.addr_gen.getaddressinfo(addr_g)['desc']
234            if desc.startswith("tr("):
235                assert desc_a.startswith("tr(")
236            rederive = self.nodes[1].deriveaddresses(desc_a)
237            assert_equal(len(rederive), 1)
238            assert_equal(rederive[0], addr_g)
239
240        # tr descriptors cannot be imported when Taproot is not active
241        result = self.privs_tr_enabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
242        assert(result[0]["success"])
243        result = self.pubs_tr_enabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
244        assert(result[0]["success"])
245        if desc.startswith("tr"):
246            result = self.privs_tr_disabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
247            assert(not result[0]["success"])
248            assert_equal(result[0]["error"]["code"], -4)
249            assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
250            result = self.pubs_tr_disabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
251            assert(not result[0]["success"])
252            assert_equal(result[0]["error"]["code"], -4)
253            assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
254
255    def do_test_sendtoaddress(self, comment, pattern, privmap, treefn, keys_pay, keys_change):
256        self.log.info("Testing %s through sendtoaddress" % comment)
257        desc_pay = self.make_desc(pattern, privmap, keys_pay)
258        desc_change = self.make_desc(pattern, privmap, keys_change)
259        desc_pay_pub = self.make_desc(pattern, privmap, keys_pay, True)
260        desc_change_pub = self.make_desc(pattern, privmap, keys_change, True)
261        assert_equal(self.nodes[0].getdescriptorinfo(desc_pay)['descriptor'], desc_pay_pub)
262        assert_equal(self.nodes[0].getdescriptorinfo(desc_change)['descriptor'], desc_change_pub)
263        result = self.rpc_online.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}])
264        assert(result[0]['success'])
265        result = self.rpc_online.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}])
266        assert(result[0]['success'])
267        for i in range(4):
268            addr_g = self.rpc_online.getnewaddress(address_type='bech32m')
269            if treefn is not None:
270                addr_r = self.make_addr(treefn, keys_pay, i)
271                assert_equal(addr_g, addr_r)
272            boring_balance = int(self.boring.getbalance() * 100000000)
273            to_amnt = random.randrange(1000000, boring_balance)
274            self.boring.sendtoaddress(address=addr_g, amount=Decimal(to_amnt) / 100000000, subtractfeefromamount=True)
275            self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
276            test_balance = int(self.rpc_online.getbalance() * 100000000)
277            ret_amnt = random.randrange(100000, test_balance)
278            res = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=Decimal(ret_amnt) / 100000000, subtractfeefromamount=True)
279            self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
280            assert(self.rpc_online.gettransaction(res)["confirmations"] > 0)
281
282    def do_test_psbt(self, comment, pattern, privmap, treefn, keys_pay, keys_change):
283        self.log.info("Testing %s through PSBT" % comment)
284        desc_pay = self.make_desc(pattern, privmap, keys_pay, False)
285        desc_change = self.make_desc(pattern, privmap, keys_change, False)
286        desc_pay_pub = self.make_desc(pattern, privmap, keys_pay, True)
287        desc_change_pub = self.make_desc(pattern, privmap, keys_change, True)
288        assert_equal(self.nodes[0].getdescriptorinfo(desc_pay)['descriptor'], desc_pay_pub)
289        assert_equal(self.nodes[0].getdescriptorinfo(desc_change)['descriptor'], desc_change_pub)
290        result = self.psbt_online.importdescriptors([{"desc": desc_pay_pub, "active": True, "timestamp": "now"}])
291        assert(result[0]['success'])
292        result = self.psbt_online.importdescriptors([{"desc": desc_change_pub, "active": True, "timestamp": "now", "internal": True}])
293        assert(result[0]['success'])
294        result = self.psbt_offline.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}])
295        assert(result[0]['success'])
296        result = self.psbt_offline.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}])
297        assert(result[0]['success'])
298        for i in range(4):
299            addr_g = self.psbt_online.getnewaddress(address_type='bech32m')
300            if treefn is not None:
301                addr_r = self.make_addr(treefn, keys_pay, i)
302                assert_equal(addr_g, addr_r)
303            boring_balance = int(self.boring.getbalance() * 100000000)
304            to_amnt = random.randrange(1000000, boring_balance)
305            self.boring.sendtoaddress(address=addr_g, amount=Decimal(to_amnt) / 100000000, subtractfeefromamount=True)
306            self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
307            test_balance = int(self.psbt_online.getbalance() * 100000000)
308            ret_amnt = random.randrange(100000, test_balance)
309            psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0]})['psbt']
310            res = self.psbt_offline.walletprocesspsbt(psbt)
311            assert(res['complete'])
312            rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
313            txid = self.nodes[0].sendrawtransaction(rawtx)
314            self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
315            assert(self.psbt_online.gettransaction(txid)['confirmations'] > 0)
316
317    def do_test(self, comment, pattern, privmap, treefn, nkeys):
318        keys = self.rand_keys(nkeys * 4)
319        self.do_test_addr(comment, pattern, privmap, treefn, keys[0:nkeys])
320        self.do_test_sendtoaddress(comment, pattern, privmap, treefn, keys[0:nkeys], keys[nkeys:2*nkeys])
321        self.do_test_psbt(comment, pattern, privmap, treefn, keys[2*nkeys:3*nkeys], keys[3*nkeys:4*nkeys])
322
323    def run_test(self):
324        self.log.info("Creating wallets...")
325        self.nodes[0].createwallet(wallet_name="privs_tr_enabled", descriptors=True, blank=True)
326        self.privs_tr_enabled = self.nodes[0].get_wallet_rpc("privs_tr_enabled")
327        self.nodes[2].createwallet(wallet_name="privs_tr_disabled", descriptors=True, blank=True)
328        self.privs_tr_disabled=self.nodes[2].get_wallet_rpc("privs_tr_disabled")
329        self.nodes[0].createwallet(wallet_name="pubs_tr_enabled", descriptors=True, blank=True, disable_private_keys=True)
330        self.pubs_tr_enabled = self.nodes[0].get_wallet_rpc("pubs_tr_enabled")
331        self.nodes[2].createwallet(wallet_name="pubs_tr_disabled", descriptors=True, blank=True, disable_private_keys=True)
332        self.pubs_tr_disabled=self.nodes[2].get_wallet_rpc("pubs_tr_disabled")
333        self.nodes[0].createwallet(wallet_name="boring")
334        self.nodes[0].createwallet(wallet_name="addr_gen", descriptors=True, disable_private_keys=True, blank=True)
335        self.nodes[0].createwallet(wallet_name="rpc_online", descriptors=True, blank=True)
336        self.nodes[0].createwallet(wallet_name="psbt_online", descriptors=True, disable_private_keys=True, blank=True)
337        self.nodes[1].createwallet(wallet_name="psbt_offline", descriptors=True, blank=True)
338        self.boring = self.nodes[0].get_wallet_rpc("boring")
339        self.addr_gen = self.nodes[0].get_wallet_rpc("addr_gen")
340        self.rpc_online = self.nodes[0].get_wallet_rpc("rpc_online")
341        self.psbt_online = self.nodes[0].get_wallet_rpc("psbt_online")
342        self.psbt_offline = self.nodes[1].get_wallet_rpc("psbt_offline")
343
344        self.log.info("Mining blocks...")
345        gen_addr = self.boring.getnewaddress()
346        self.nodes[0].generatetoaddress(101, gen_addr)
347
348        self.do_test(
349            "tr(XPRV)",
350            "tr($1/*)",
351            [True],
352            lambda k1: (key(k1), []),
353            1
354        )
355        self.do_test(
356            "tr(H,XPRV)",
357            "tr($H,pk($1/*))",
358            [True],
359            lambda k1: (key(H_POINT), [pk(k1)]),
360            1
361        )
362        self.do_test(
363            "wpkh(XPRV)",
364            "wpkh($1/*)",
365            [True],
366            None,
367            1
368        )
369        self.do_test(
370            "tr(XPRV,{H,{H,XPUB}})",
371            "tr($1/*,{pk($H),{pk($H),pk($2/*)}})",
372            [True, False],
373            lambda k1, k2: (key(k1), [pk(H_POINT), [pk(H_POINT), pk(k2)]]),
374            2
375        )
376        self.do_test(
377            "wsh(multi(1,XPRV,XPUB))",
378            "wsh(multi(1,$1/*,$2/*))",
379            [True, False],
380            None,
381            2
382        )
383        self.do_test(
384            "tr(XPRV,{XPUB,XPUB})",
385            "tr($1/*,{pk($2/*),pk($2/*)})",
386            [True, False],
387            lambda k1, k2: (key(k1), [pk(k2), pk(k2)]),
388            2
389        )
390        self.do_test(
391            "tr(XPRV,{{XPUB,H},{H,XPUB}})",
392            "tr($1/*,{{pk($2/*),pk($H)},{pk($H),pk($2/*)}})",
393            [True, False],
394            lambda k1, k2: (key(k1), [[pk(k2), pk(H_POINT)], [pk(H_POINT), pk(k2)]]),
395            2
396        )
397        self.do_test(
398            "tr(XPUB,{{H,{H,XPUB}},{H,{H,{H,XPRV}}}})",
399            "tr($1/*,{{pk($H),{pk($H),pk($2/*)}},{pk($H),{pk($H),{pk($H),pk($3/*)}}}})",
400            [False, False, True],
401            lambda k1, k2, k3: (key(k1), [[pk(H_POINT), [pk(H_POINT), pk(k2)]], [pk(H_POINT), [pk(H_POINT), [pk(H_POINT), pk(k3)]]]]),
402            3
403        )
404        self.do_test(
405            "tr(XPRV,{XPUB,{{XPUB,{H,H}},{{H,H},XPUB}}})",
406            "tr($1/*,{pk($2/*),{{pk($2/*),{pk($H),pk($H)}},{{pk($H),pk($H)},pk($2/*)}}})",
407            [True, False],
408            lambda k1, k2: (key(k1), [pk(k2), [[pk(k2), [pk(H_POINT), pk(H_POINT)]], [[pk(H_POINT), pk(H_POINT)], pk(k2)]]]),
409            2
410        )
411
412        self.log.info("Sending everything back...")
413
414        txid = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=self.rpc_online.getbalance(), subtractfeefromamount=True)
415        self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
416        assert(self.rpc_online.gettransaction(txid)["confirmations"] > 0)
417
418        psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): self.psbt_online.getbalance()}], None, {"subtractFeeFromOutputs": [0]})['psbt']
419        res = self.psbt_offline.walletprocesspsbt(psbt)
420        assert(res['complete'])
421        rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
422        txid = self.nodes[0].sendrawtransaction(rawtx)
423        self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
424        assert(self.psbt_online.gettransaction(txid)['confirmations'] > 0)
425
426if __name__ == '__main__':
427    WalletTaprootTest().main()
428