Skip to content

Commit eb0667e

Browse files
committed
Add tests for (sorted)multi_a derivation/signing
1 parent c17c6aa commit eb0667e

File tree

2 files changed

+69
-2
lines changed

2 files changed

+69
-2
lines changed

test/functional/test_framework/script.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from .ripemd160 import ripemd160
2828

2929
MAX_SCRIPT_ELEMENT_SIZE = 520
30+
MAX_PUBKEYS_PER_MULTI_A = 512
3031
LOCKTIME_THRESHOLD = 500000000
3132
ANNEX_TAG = 0x50
3233

test/functional/wallet_taproot.py

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
from test_framework.descriptors import descsum_create
1313
from test_framework.script import (
1414
CScript,
15+
MAX_PUBKEYS_PER_MULTI_A,
1516
OP_1,
1617
OP_CHECKSIG,
18+
OP_CHECKSIGADD,
19+
OP_NUMEQUAL,
1720
taproot_construct,
1821
)
1922
from test_framework.segwit_addr import encode_segwit_address
@@ -167,6 +170,17 @@ def pk(hex_key):
167170
"""Construct a script expression for taproot_construct for pk(hex_key)."""
168171
return (None, CScript([bytes.fromhex(hex_key), OP_CHECKSIG]))
169172

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+
170184
def compute_taproot_address(pubkey, scripts):
171185
"""Compute the address for a taproot output with given inner key and scripts."""
172186
tap = taproot_construct(pubkey, scripts)
@@ -275,7 +289,8 @@ def do_test_sendtoaddress(self, comment, pattern, privmap, treefn, keys_pay, key
275289
self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
276290
test_balance = int(self.rpc_online.getbalance() * 100000000)
277291
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)
279294
self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
280295
assert(self.rpc_online.gettransaction(res)["confirmations"] > 0)
281296

@@ -306,7 +321,8 @@ def do_test_psbt(self, comment, pattern, privmap, treefn, keys_pay, keys_change)
306321
self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
307322
test_balance = int(self.psbt_online.getbalance() * 100000000)
308323
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']
310326
res = self.psbt_offline.walletprocesspsbt(psbt)
311327
assert(res['complete'])
312328
rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
@@ -408,6 +424,56 @@ def run_test(self):
408424
lambda k1, k2: (key(k1), [pk(k2), [[pk(k2), [pk(H_POINT), pk(H_POINT)]], [[pk(H_POINT), pk(H_POINT)], pk(k2)]]]),
409425
2
410426
)
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+
)
411477

412478
self.log.info("Sending everything back...")
413479

0 commit comments

Comments
 (0)