|
12 | 12 | from test_framework.descriptors import descsum_create
|
13 | 13 | from test_framework.script import (
|
14 | 14 | CScript,
|
| 15 | + MAX_PUBKEYS_PER_MULTI_A, |
15 | 16 | OP_1,
|
16 | 17 | OP_CHECKSIG,
|
| 18 | + OP_CHECKSIGADD, |
| 19 | + OP_NUMEQUAL, |
17 | 20 | taproot_construct,
|
18 | 21 | )
|
19 | 22 | from test_framework.segwit_addr import encode_segwit_address
|
@@ -167,6 +170,17 @@ def pk(hex_key):
|
167 | 170 | """Construct a script expression for taproot_construct for pk(hex_key)."""
|
168 | 171 | return (None, CScript([bytes.fromhex(hex_key), OP_CHECKSIG]))
|
169 | 172 |
|
| 173 | +def multi_a(k, hex_keys, sort=False): |
| 174 | + """Construct a script expression for taproot_construct for a multi_a script.""" |
| 175 | + xkeys = [bytes.fromhex(hex_key) for hex_key in hex_keys] |
| 176 | + if sort: |
| 177 | + xkeys.sort() |
| 178 | + ops = [xkeys[0], OP_CHECKSIG] |
| 179 | + for i in range(1, len(hex_keys)): |
| 180 | + ops += [xkeys[i], OP_CHECKSIGADD] |
| 181 | + ops += [k, OP_NUMEQUAL] |
| 182 | + return (None, CScript(ops)) |
| 183 | + |
170 | 184 | def compute_taproot_address(pubkey, scripts):
|
171 | 185 | """Compute the address for a taproot output with given inner key and scripts."""
|
172 | 186 | tap = taproot_construct(pubkey, scripts)
|
@@ -275,7 +289,8 @@ def do_test_sendtoaddress(self, comment, pattern, privmap, treefn, keys_pay, key
|
275 | 289 | self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
|
276 | 290 | test_balance = int(self.rpc_online.getbalance() * 100000000)
|
277 | 291 | ret_amnt = random.randrange(100000, test_balance)
|
278 |
| - res = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=Decimal(ret_amnt) / 100000000, subtractfeefromamount=True) |
| 292 | + # Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends. |
| 293 | + res = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=Decimal(ret_amnt) / 100000000, subtractfeefromamount=True, fee_rate=200) |
279 | 294 | self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
|
280 | 295 | assert(self.rpc_online.gettransaction(res)["confirmations"] > 0)
|
281 | 296 |
|
@@ -306,7 +321,8 @@ def do_test_psbt(self, comment, pattern, privmap, treefn, keys_pay, keys_change)
|
306 | 321 | self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
|
307 | 322 | test_balance = int(self.psbt_online.getbalance() * 100000000)
|
308 | 323 | ret_amnt = random.randrange(100000, test_balance)
|
309 |
| - psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0]})['psbt'] |
| 324 | + # Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends. |
| 325 | + psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0], "fee_rate": 200})['psbt'] |
310 | 326 | res = self.psbt_offline.walletprocesspsbt(psbt)
|
311 | 327 | assert(res['complete'])
|
312 | 328 | rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
|
@@ -408,6 +424,56 @@ def run_test(self):
|
408 | 424 | lambda k1, k2: (key(k1), [pk(k2), [[pk(k2), [pk(H_POINT), pk(H_POINT)]], [[pk(H_POINT), pk(H_POINT)], pk(k2)]]]),
|
409 | 425 | 2
|
410 | 426 | )
|
| 427 | + self.do_test( |
| 428 | + "tr(H,multi_a(1,XPRV))", |
| 429 | + "tr($H,multi_a(1,$1/*))", |
| 430 | + [True], |
| 431 | + lambda k1: (key(H_POINT), [multi_a(1, [k1])]), |
| 432 | + 1 |
| 433 | + ) |
| 434 | + self.do_test( |
| 435 | + "tr(H,sortedmulti_a(1,XPRV,XPUB))", |
| 436 | + "tr($H,sortedmulti_a(1,$1/*,$2/*))", |
| 437 | + [True, False], |
| 438 | + lambda k1, k2: (key(H_POINT), [multi_a(1, [k1, k2], True)]), |
| 439 | + 2 |
| 440 | + ) |
| 441 | + self.do_test( |
| 442 | + "tr(H,multi_a(1,XPUB,XPRV))", |
| 443 | + "tr($H,multi_a(1,$1/*,$2/*))", |
| 444 | + [False, True], |
| 445 | + lambda k1, k2: (key(H_POINT), [multi_a(1, [k1, k2])]), |
| 446 | + 2 |
| 447 | + ) |
| 448 | + self.do_test( |
| 449 | + "tr(H,sortedmulti_a(1,XPUB,XPRV,XPRV))", |
| 450 | + "tr($H,sortedmulti_a(1,$1/*,$2/*,$3/*))", |
| 451 | + [False, True, True], |
| 452 | + lambda k1, k2, k3: (key(H_POINT), [multi_a(1, [k1, k2, k3], True)]), |
| 453 | + 3 |
| 454 | + ) |
| 455 | + self.do_test( |
| 456 | + "tr(H,multi_a(2,XPRV,XPUB,XPRV))", |
| 457 | + "tr($H,multi_a(2,$1/*,$2/*,$3/*))", |
| 458 | + [True, False, True], |
| 459 | + lambda k1, k2, k3: (key(H_POINT), [multi_a(2, [k1, k2, k3])]), |
| 460 | + 3 |
| 461 | + ) |
| 462 | + self.do_test( |
| 463 | + "tr(XPUB,{{XPUB,{XPUB,sortedmulti_a(2,XPRV,XPUB,XPRV)}})", |
| 464 | + "tr($2/*,{pk($2/*),{pk($2/*),sortedmulti_a(2,$1/*,$2/*,$3/*)}})", |
| 465 | + [True, False, True], |
| 466 | + lambda k1, k2, k3: (key(k2), [pk(k2), [pk(k2), multi_a(2, [k1, k2, k3], True)]]), |
| 467 | + 3 |
| 468 | + ) |
| 469 | + rnd_pos = random.randrange(MAX_PUBKEYS_PER_MULTI_A) |
| 470 | + self.do_test( |
| 471 | + "tr(XPUB,multi_a(1,H...,XPRV,H...))", |
| 472 | + "tr($2/*,multi_a(1" + (",$H" * rnd_pos) + ",$1/*" + (",$H" * (MAX_PUBKEYS_PER_MULTI_A - 1 - rnd_pos)) + "))", |
| 473 | + [True, False], |
| 474 | + lambda k1, k2: (key(k2), [multi_a(1, ([H_POINT] * rnd_pos) + [k1] + ([H_POINT] * (MAX_PUBKEYS_PER_MULTI_A - 1 - rnd_pos)))]), |
| 475 | + 2 |
| 476 | + ) |
411 | 477 |
|
412 | 478 | self.log.info("Sending everything back...")
|
413 | 479 |
|
|
0 commit comments