aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAndrew Chow <github@achow101.com>2023-10-08 12:01:31 -0400
committerAndrew Chow <github@achow101.com>2023-10-08 12:10:12 -0400
commitdb283a6b6f1419291bcd15d74d51c8598aefe06a (patch)
treec183be4f6dc9a74517483af6a5e2ccc7c4bd91f5 /test
parentd2b8c5e1234cdaff84bd1f60aea598d219cdac5e (diff)
parentec0fc14a22f38b487929ec21145945966f301eb5 (diff)
Merge bitcoin/bitcoin#27255: MiniTapscript: port Miniscript to Tapscript
ec0fc14a22f38b487929ec21145945966f301eb5 miniscript: remove P2WSH-specific part of GetStackSize doc comment (Antoine Poinsot) 128bc104ef07e1edaad5378e2ca53e97672a1652 qa: bound testing for TapMiniscript (Antoine Poinsot) 117927bd5f30c8bf09aaf91e62f5244990788084 miniscript: have a custom Node destructor (Antoine Poinsot) b917c715ace19fb23661b4b245039da48cefc6bf qa: Tapscript Miniscript signing functional tests (Antoine Poinsot) 5dc341dfe672c7195bc71f2834152b86a710c313 qa: list descriptors in Miniscript signing functional tests (Antoine Poinsot) 4f473ea515bc77b9138323dab8a741c063d32e8f script/sign: Miniscript support in Tapscript (Antoine Poinsot) febe2abc0e3f67b8b0ac9ece1890efb4a0bba83c MOVEONLY: script/sign: move Satisfier declaration above Tapscript signing (Antoine Poinsot) bd4b11ee06096655b74586413efe40c14d9ef44c qa: functional test Miniscript inside Taproot descriptors (Antoine Poinsot) 8571b89a7fce50229242ef3c6d9f807949f716a3 descriptor: parse Miniscript expressions within Taproot descriptors (Antoine Poinsot) 8ff9489422009284967aeb9ff4232b135f5ddad8 descriptor: Tapscript-specific Miniscript key serialization / parsing (Antoine Poinsot) 5e76f3f0ddbce513c2b626b5ab45242f931d5d60 fuzz: miniscript: higher sensitivity for max stack size limit under Tapscript (Antoine Poinsot) 6f529cbaaf773dd77f3262b1fbd5039856434898 qa: test Miniscript max stack size tracking (Antoine Poinsot) 770ba5b51979b99de513d5a9d15e451b7d29b647 miniscript: check maximum stack size during execution (Antoine Poinsot) 574523dbe030f5fb8aca4d7fd41cdc304bd913d3 fuzz: adapt Miniscript targets to Tapscript (Antoine Poinsot) 84623722ef3a1ff6fc302517adc554ba6cb023a7 qa: Tapscript-Miniscript unit tests (Antoine Poinsot) fcb6f13f442d6a3f27689a87e3ed2bb9b431a332 pubkey: introduce a GetEvenCorrespondingCPubKey helper (Antoine Poinsot) ce8845f5dda403461178c08e7363978fda423999 miniscript: account for keys as being 32 bytes under Taproot context (Antoine Poinsot) f4f978d38ee4920c5cd0de5d93b407ec37bfd9c0 miniscript: adapt resources checks depending on context (Antoine Poinsot) 9cb4c68b89a5715f82026f4aa446b876addd8472 serialize: make GetSizeOfCompactSize constexpr (Antoine Poinsot) 892436c7d575ffdb9bada5fe4e62d6c1f5053c42 miniscript: sanity asserts context in ComputeType (Antoine Poinsot) e5aaa3d77af7459b37c0c4a37eb22c5fd0cda3e1 miniscript: make 'd:' have the 'u' property under Tapscript context (Antoine Poinsot) 687a0b0fa53ddd5632287b9e00ad8b0550830287 miniscript: introduce a multi_a fragment (Antoine Poinsot) 9164c2eca164d78cbae5351d383f39320711efb9 miniscript: restrict multi() usage to P2WSH context (Antoine Poinsot) 91b4db859023f5cf59f4b27f880484c863ccae66 miniscript: store the script context within the Node structure (Antoine Poinsot) c3738d0344f589162b9ffb78b8e2d78f612d3786 miniscript: introduce a MsContext() helper to contexts (Antoine Poinsot) bba9340a947446cd1c70852f58dcd8aee35be9ac miniscript: don't anticipate signature presence in CalcStackSize() (Antoine Poinsot) a3793f2d1a43624631d6329f6c900a83e7dd0e98 miniscript: add a missing dup key check bypass in Parse() (Antoine Poinsot) Pull request description: Miniscript was targeting P2WSH, and as such can currently only be used in `wsh()` descriptors. This pull request introduces support for Tapscript in Miniscript and makes Miniscript available inside `tr()` descriptors. It adds support for both watching *and* signing TapMiniscript descriptors. The main changes to Miniscript for Tapscript are the following: - A new `multi_a` fragment is introduced with the same semantics as `multi`. Like in other descriptors `multi` and `multi_a` can exclusively be used in respectively P2WSH and Tapscript. - The `d:` fragment has the `u` property under Tapscript, since the `MINIMALIF` rule is now consensus. See also https://github.com/bitcoin/bitcoin/pull/24906. - Keys are now serialized as 32 bytes. (Note this affects the key hashes.) - The resource consumption checks and calculation changed. Some limits were lifted in Tapscript, and signatures are now 64 bytes long. The largest amount of complexity probably lies in the last item. Scripts under Taproot can now run into the maximum stack size while executing a fragment. For instance if you've got a stack size of `999` due to the initial witness plus some execution that happened before and try to execute a `hash256` it would `DUP` (increasing the stack size `1000`), `HASH160` and then push the hash on the stack making the script fail. To make sure this does not happen on any of the spending paths of a sane Miniscript, we introduce a tracking of the maximum stack size during execution of a fragment. See the commits messages for details. Those commits were separated from the resource consumption change, and the fuzz target was tweaked to sometimes pad the witness so the script runs on the brink of the stack size limit to make sure the stack size was not underestimated. Existing Miniscript unit, functional and fuzz tests are extended with Tapscript logic and test cases. Care was taken for seed stability in the fuzz targets where we cared more about them. The design of Miniscript for Tapscript is the result of discussions between various people over the past year(s). To the extent of my knowledge at least Pieter Wuille, Sanket Kanjalkar, Andrew Poelstra and Andrew Chow contributed thoughts and ideas. ACKs for top commit: sipa: ACK ec0fc14a22f38b487929ec21145945966f301eb5 achow101: ACK ec0fc14a22f38b487929ec21145945966f301eb5 Tree-SHA512: f3cf98a3ec8e565650ccf51b7ee7e4b4c2b3949a1168bee16ec03d2942b4d9f20dedc2820457f67a3216161022263573d08419c8346d807a693169ad3a436e07
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/wallet_miniscript.py159
2 files changed, 128 insertions, 33 deletions
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 933ea276e7..fbf48a0e4d 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -105,6 +105,7 @@ BASE_SCRIPTS = [
'feature_maxuploadtarget.py',
'mempool_updatefromblock.py',
'mempool_persist.py --descriptors',
+ 'wallet_miniscript.py --descriptors',
# vv Tests less than 60s vv
'rpc_psbt.py --legacy-wallet',
'rpc_psbt.py --descriptors',
@@ -242,7 +243,6 @@ BASE_SCRIPTS = [
'wallet_keypool.py --legacy-wallet',
'wallet_keypool.py --descriptors',
'wallet_descriptor.py --descriptors',
- 'wallet_miniscript.py --descriptors',
'p2p_nobloomfilter_messages.py',
'p2p_filter.py',
'rpc_setban.py',
diff --git a/test/functional/wallet_miniscript.py b/test/functional/wallet_miniscript.py
index 45f0df1c76..d174b525b3 100755
--- a/test/functional/wallet_miniscript.py
+++ b/test/functional/wallet_miniscript.py
@@ -22,6 +22,7 @@ TPUBS = [
"tpubD6NzVbkrYhZ4XRMcMFMMFvzVt6jaDAtjZhD7JLwdPdMm9xa76DnxYYP7w9TZGJDVFkek3ArwVsuacheqqPog8TH5iBCX1wuig8PLXim4n9a",
"tpubD6NzVbkrYhZ4WsqRzDmkL82SWcu42JzUvKWzrJHQ8EC2vEHRHkXj1De93sD3biLrKd8XGnamXURGjMbYavbszVDXpjXV2cGUERucLJkE6cy",
"tpubDEFLeBkKTm8aiYkySz8hXAXPVnPSfxMi7Fxhg9sejUrkwJuRWvPdLEiXjTDbhGbjLKCZUDUUibLxTnK5UP1q7qYrSnPqnNe7M8mvAW1STcc",
+ "tpubD6NzVbkrYhZ4WR99ygpiJvPMAJiwahjLgGywc5vJx2gUfKUfEPCrbKmQczDPJZmLcyZzRb5Ti6rfUb89S2WFyPH7FDtD6RFDA1hdgTEgEUL",
]
PUBKEYS = [
"02aebf2d10b040eb936a6f02f44ee82f8b34f5c1ccb20ff3949c2b28206b7c1068",
@@ -33,7 +34,7 @@ PUBKEYS = [
"0211c7b2e18b6fd330f322de087da62da92ae2ae3d0b7cec7e616479cce175f183",
]
-MINISCRIPTS = [
+P2WSH_MINISCRIPTS = [
# One of two keys
f"or_b(pk({TPUBS[0]}/*),s:pk({TPUBS[1]}/*))",
# A script similar (same spending policy) to BOLT3's offered HTLC (with anchor outputs)
@@ -44,10 +45,22 @@ MINISCRIPTS = [
f"or_i(and_b(pk({PUBKEYS[0]}),a:and_b(pk({PUBKEYS[1]}),a:and_b(pk({PUBKEYS[2]}),a:and_b(pk({PUBKEYS[3]}),s:pk({PUBKEYS[4]}))))),and_v(v:thresh(2,pkh({TPUBS[0]}/*),a:pkh({PUBKEYS[5]}),a:pkh({PUBKEYS[6]})),older(4209713)))",
]
-MINISCRIPTS_PRIV = [
+DESCS = [
+ *[f"wsh({ms})" for ms in P2WSH_MINISCRIPTS],
+ # A Taproot with one of the above scripts as the single script path.
+ f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{P2WSH_MINISCRIPTS[0]})",
+ # A Taproot with two script paths among the above scripts.
+ f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{{{P2WSH_MINISCRIPTS[0]},{P2WSH_MINISCRIPTS[1]}}})",
+ # A Taproot with three script paths among the above scripts.
+ f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{{{{{P2WSH_MINISCRIPTS[0]},{P2WSH_MINISCRIPTS[1]}}},{P2WSH_MINISCRIPTS[2].replace('multi', 'multi_a')}}})",
+ # A Taproot with all above scripts in its tree.
+ f"tr(4d54bb9928a0683b7e383de72943b214b0716f58aa54c7ba6bcea2328bc9c768,{{{{{P2WSH_MINISCRIPTS[0]},{P2WSH_MINISCRIPTS[1]}}},{{{P2WSH_MINISCRIPTS[2].replace('multi', 'multi_a')},{P2WSH_MINISCRIPTS[3]}}}}})",
+]
+
+DESCS_PRIV = [
# One of two keys, of which one private key is known
{
- "ms": f"or_i(pk({TPRVS[0]}/*),pk({TPUBS[0]}/*))",
+ "desc": f"wsh(or_i(pk({TPRVS[0]}/*),pk({TPUBS[0]}/*)))",
"sequence": None,
"locktime": None,
"sigs_count": 1,
@@ -55,7 +68,7 @@ MINISCRIPTS_PRIV = [
},
# A more complex policy, that can't be satisfied through the first branch (need for a preimage)
{
- "ms": f"andor(ndv:older(2),and_v(v:pk({TPRVS[0]}),sha256(2a8ce30189b2ec3200b47aeb4feaac8fcad7c0ba170389729f4898b0b7933bcb)),and_v(v:pkh({TPRVS[1]}),pk({TPRVS[2]}/*)))",
+ "desc": f"wsh(andor(ndv:older(2),and_v(v:pk({TPRVS[0]}),sha256(2a8ce30189b2ec3200b47aeb4feaac8fcad7c0ba170389729f4898b0b7933bcb)),and_v(v:pkh({TPRVS[1]}),pk({TPRVS[2]}/*))))",
"sequence": 2,
"locktime": None,
"sigs_count": 3,
@@ -63,7 +76,7 @@ MINISCRIPTS_PRIV = [
},
# The same policy but we provide the preimage. This path will be chosen as it's a smaller witness.
{
- "ms": f"andor(ndv:older(2),and_v(v:pk({TPRVS[0]}),sha256(61e33e9dbfefc45f6a194187684d278f789fd4d5e207a357e79971b6519a8b12)),and_v(v:pkh({TPRVS[1]}),pk({TPRVS[2]}/*)))",
+ "desc": f"wsh(andor(ndv:older(2),and_v(v:pk({TPRVS[0]}),sha256(61e33e9dbfefc45f6a194187684d278f789fd4d5e207a357e79971b6519a8b12)),and_v(v:pkh({TPRVS[1]}),pk({TPRVS[2]}/*))))",
"sequence": 2,
"locktime": None,
"sigs_count": 3,
@@ -74,7 +87,7 @@ MINISCRIPTS_PRIV = [
},
# Signature with a relative timelock
{
- "ms": f"and_v(v:older(2),pk({TPRVS[0]}/*))",
+ "desc": f"wsh(and_v(v:older(2),pk({TPRVS[0]}/*)))",
"sequence": 2,
"locktime": None,
"sigs_count": 1,
@@ -82,7 +95,7 @@ MINISCRIPTS_PRIV = [
},
# Signature with an absolute timelock
{
- "ms": f"and_v(v:after(20),pk({TPRVS[0]}/*))",
+ "desc": f"wsh(and_v(v:after(20),pk({TPRVS[0]}/*)))",
"sequence": None,
"locktime": 20,
"sigs_count": 1,
@@ -90,7 +103,7 @@ MINISCRIPTS_PRIV = [
},
# Signature with both
{
- "ms": f"and_v(v:older(4),and_v(v:after(30),pk({TPRVS[0]}/*)))",
+ "desc": f"wsh(and_v(v:older(4),and_v(v:after(30),pk({TPRVS[0]}/*))))",
"sequence": 4,
"locktime": 30,
"sigs_count": 1,
@@ -98,7 +111,7 @@ MINISCRIPTS_PRIV = [
},
# We have one key on each branch; Core signs both (can't finalize)
{
- "ms": f"c:andor(pk({TPRVS[0]}/*),pk_k({TPUBS[0]}),and_v(v:pk({TPRVS[1]}),pk_k({TPUBS[1]})))",
+ "desc": f"wsh(c:andor(pk({TPRVS[0]}/*),pk_k({TPUBS[0]}),and_v(v:pk({TPRVS[1]}),pk_k({TPUBS[1]}))))",
"sequence": None,
"locktime": None,
"sigs_count": 2,
@@ -106,7 +119,7 @@ MINISCRIPTS_PRIV = [
},
# We have all the keys, wallet selects the timeout path to sign since it's smaller and sequence is set
{
- "ms": f"andor(pk({TPRVS[0]}/*),pk({TPRVS[2]}),and_v(v:pk({TPRVS[1]}),older(10)))",
+ "desc": f"wsh(andor(pk({TPRVS[0]}/*),pk({TPRVS[2]}),and_v(v:pk({TPRVS[1]}),older(10))))",
"sequence": 10,
"locktime": None,
"sigs_count": 3,
@@ -114,7 +127,7 @@ MINISCRIPTS_PRIV = [
},
# We have all the keys, wallet selects the primary path to sign unconditionally since nsequence wasn't set to be valid for timeout path
{
- "ms": f"andor(pk({TPRVS[0]}/*),pk({TPRVS[2]}),and_v(v:pkh({TPRVS[1]}),older(10)))",
+ "desc": f"wsh(andor(pk({TPRVS[0]}/*),pk({TPRVS[2]}),and_v(v:pkh({TPRVS[1]}),older(10))))",
"sequence": None,
"locktime": None,
"sigs_count": 3,
@@ -122,7 +135,7 @@ MINISCRIPTS_PRIV = [
},
# Finalizes to the smallest valid witness, regardless of sequence
{
- "ms": f"or_d(pk({TPRVS[0]}/*),and_v(v:pk({TPRVS[1]}),and_v(v:pk({TPRVS[2]}),older(10))))",
+ "desc": f"wsh(or_d(pk({TPRVS[0]}/*),and_v(v:pk({TPRVS[1]}),and_v(v:pk({TPRVS[2]}),older(10)))))",
"sequence": 12,
"locktime": None,
"sigs_count": 3,
@@ -130,7 +143,57 @@ MINISCRIPTS_PRIV = [
},
# Liquid-like federated pegin with emergency recovery privkeys
{
- "ms": f"or_i(and_b(pk({TPUBS[0]}/*),a:and_b(pk({TPUBS[1]}),a:and_b(pk({TPUBS[2]}),a:and_b(pk({TPUBS[3]}),s:pk({PUBKEYS[0]}))))),and_v(v:thresh(2,pkh({TPRVS[0]}),a:pkh({TPRVS[1]}),a:pkh({TPUBS[4]})),older(42)))",
+ "desc": f"wsh(or_i(and_b(pk({TPUBS[0]}/*),a:and_b(pk({TPUBS[1]}),a:and_b(pk({TPUBS[2]}),a:and_b(pk({TPUBS[3]}),s:pk({PUBKEYS[0]}))))),and_v(v:thresh(2,pkh({TPRVS[0]}),a:pkh({TPRVS[1]}),a:pkh({TPUBS[4]})),older(42))))",
+ "sequence": 42,
+ "locktime": None,
+ "sigs_count": 2,
+ "stack_size": 8,
+ },
+ # Each leaf needs two sigs. We've got one key on each. Will sign both but can't finalize.
+ {
+ "desc": f"tr({TPUBS[0]}/*,{{and_v(v:pk({TPRVS[0]}/*),pk({TPUBS[1]})),and_v(v:pk({TPRVS[1]}/*),pk({TPUBS[2]}))}})",
+ "sequence": None,
+ "locktime": None,
+ "sigs_count": 2,
+ "stack_size": None,
+ },
+ # The same but now the two leaves are identical. Will add a single sig that is valid for both. Can't finalize.
+ {
+ "desc": f"tr({TPUBS[0]}/*,{{and_v(v:pk({TPRVS[0]}/*),pk({TPUBS[1]})),and_v(v:pk({TPRVS[0]}/*),pk({TPUBS[1]}))}})",
+ "sequence": None,
+ "locktime": None,
+ "sigs_count": 1,
+ "stack_size": None,
+ },
+ # The same but we have the two necessary privkeys on one of the leaves. Also it uses a pubkey hash.
+ {
+ "desc": f"tr({TPUBS[0]}/*,{{and_v(v:pk({TPRVS[0]}/*),pk({TPUBS[1]})),and_v(v:pkh({TPRVS[1]}/*),pk({TPRVS[2]}))}})",
+ "sequence": None,
+ "locktime": None,
+ "sigs_count": 3,
+ "stack_size": 5,
+ },
+ # A key immediately or one of two keys after a timelock. If both paths are available it'll use the
+ # non-timelocked path because it's a smaller witness.
+ {
+ "desc": f"tr({TPUBS[0]}/*,{{pk({TPRVS[0]}/*),and_v(v:older(42),multi_a(1,{TPRVS[1]},{TPRVS[2]}))}})",
+ "sequence": 42,
+ "locktime": None,
+ "sigs_count": 3,
+ "stack_size": 3,
+ },
+ # A key immediately or one of two keys after a timelock. If the "primary" key isn't available though it'll
+ # use the timelocked path. Same remark for multi_a.
+ {
+ "desc": f"tr({TPUBS[0]}/*,{{pk({TPUBS[1]}/*),and_v(v:older(42),multi_a(1,{TPRVS[0]},{TPRVS[1]}))}})",
+ "sequence": 42,
+ "locktime": None,
+ "sigs_count": 2,
+ "stack_size": 4,
+ },
+ # Liquid-like federated pegin with emergency recovery privkeys, but in a Taproot.
+ {
+ "desc": f"tr({TPUBS[1]}/*,{{and_b(pk({TPUBS[2]}/*),a:and_b(pk({TPUBS[3]}),a:and_b(pk({TPUBS[4]}),a:and_b(pk({TPUBS[5]}),s:pk({PUBKEYS[0]}))))),and_v(v:thresh(2,pkh({TPRVS[0]}),a:pkh({TPRVS[1]}),a:pkh({TPUBS[6]})),older(42))}})",
"sequence": 42,
"locktime": None,
"sigs_count": 2,
@@ -142,6 +205,7 @@ MINISCRIPTS_PRIV = [
class WalletMiniscriptTest(BitcoinTestFramework):
def add_options(self, parser):
self.add_wallet_options(parser, legacy=False)
+ self.rpc_timeout = 480
def set_test_params(self):
self.num_nodes = 1
@@ -150,9 +214,9 @@ class WalletMiniscriptTest(BitcoinTestFramework):
self.skip_if_no_wallet()
self.skip_if_no_sqlite()
- def watchonly_test(self, ms):
- self.log.info(f"Importing Miniscript '{ms}'")
- desc = descsum_create(f"wsh({ms})")
+ def watchonly_test(self, desc):
+ self.log.info(f"Importing descriptor '{desc}'")
+ desc = descsum_create(f"{desc}")
assert self.ms_wo_wallet.importdescriptors(
[
{
@@ -166,11 +230,14 @@ class WalletMiniscriptTest(BitcoinTestFramework):
)[0]["success"]
self.log.info("Testing we derive new addresses for it")
+ addr_type = "bech32m" if desc.startswith("tr(") else "bech32"
assert_equal(
- self.ms_wo_wallet.getnewaddress(), self.funder.deriveaddresses(desc, 0)[0]
+ self.ms_wo_wallet.getnewaddress(address_type=addr_type),
+ self.funder.deriveaddresses(desc, 0)[0],
)
assert_equal(
- self.ms_wo_wallet.getnewaddress(), self.funder.deriveaddresses(desc, 1)[1]
+ self.ms_wo_wallet.getnewaddress(address_type=addr_type),
+ self.funder.deriveaddresses(desc, 1)[1],
)
self.log.info("Testing we detect funds sent to one of them")
@@ -183,10 +250,11 @@ class WalletMiniscriptTest(BitcoinTestFramework):
assert utxo["txid"] == txid and utxo["solvable"]
def signing_test(
- self, ms, sequence, locktime, sigs_count, stack_size, sha256_preimages
+ self, desc, sequence, locktime, sigs_count, stack_size, sha256_preimages
):
- self.log.info(f"Importing private Miniscript '{ms}'")
- desc = descsum_create(f"wsh({ms})")
+ self.log.info(f"Importing private Miniscript descriptor '{desc}'")
+ is_taproot = desc.startswith("tr(")
+ desc = descsum_create(desc)
res = self.ms_sig_wallet.importdescriptors(
[
{
@@ -201,7 +269,8 @@ class WalletMiniscriptTest(BitcoinTestFramework):
assert res[0]["success"], res
self.log.info("Generating an address for it and testing it detects funds")
- addr = self.ms_sig_wallet.getnewaddress()
+ addr_type = "bech32m" if is_taproot else "bech32"
+ addr = self.ms_sig_wallet.getnewaddress(address_type=addr_type)
txid = self.funder.sendtoaddress(addr, 0.01)
self.wait_until(lambda: txid in self.funder.getrawmempool())
self.funder.generatetoaddress(1, self.funder.getnewaddress())
@@ -233,7 +302,8 @@ class WalletMiniscriptTest(BitcoinTestFramework):
psbt = psbt.to_base64()
res = self.ms_sig_wallet.walletprocesspsbt(psbt=psbt, finalize=False)
psbtin = self.nodes[0].rpc.decodepsbt(res["psbt"])["inputs"][0]
- assert len(psbtin["partial_signatures"]) == sigs_count
+ sigs_field_name = "taproot_script_path_sigs" if is_taproot else "partial_signatures"
+ assert len(psbtin[sigs_field_name]) == sigs_count
res = self.ms_sig_wallet.finalizepsbt(res["psbt"])
assert res["complete"] == (stack_size is not None)
@@ -290,20 +360,45 @@ class WalletMiniscriptTest(BitcoinTestFramework):
assert not res["success"] and "is not satisfiable" in res["error"]["message"]
# Test we can track any type of Miniscript
- for ms in MINISCRIPTS:
- self.watchonly_test(ms)
+ for desc in DESCS:
+ self.watchonly_test(desc)
# Test we can sign for any Miniscript.
- for ms in MINISCRIPTS_PRIV:
+ for desc in DESCS_PRIV:
self.signing_test(
- ms["ms"],
- ms["sequence"],
- ms["locktime"],
- ms["sigs_count"],
- ms["stack_size"],
- ms.get("sha256_preimages"),
+ desc["desc"],
+ desc["sequence"],
+ desc["locktime"],
+ desc["sigs_count"],
+ desc["stack_size"],
+ desc.get("sha256_preimages"),
)
+ # Test we can sign for a max-size TapMiniscript. Recompute the maximum accepted size
+ # for a TapMiniscript (see cpp file for details). Then pad a simple pubkey check up
+ # to the maximum size. Make sure we can import and spend this script.
+ leeway_weight = (4 + 4 + 1 + 36 + 4 + 1 + 1 + 8 + 1 + 1 + 33) * 4 + 2
+ max_tapmini_size = 400_000 - 3 - (1 + 65) * 1_000 - 3 - (33 + 32 * 128) - leeway_weight - 5
+ padding = max_tapmini_size - 33 - 1
+ ms = f"pk({TPRVS[0]}/*)"
+ ms = "n" * padding + ":" + ms
+ desc = f"tr({PUBKEYS[0]},{ms})"
+ self.signing_test(desc, None, None, 1, 3, None)
+ # This was really the maximum size, one more byte and we can't import it.
+ ms = "n" + ms
+ desc = f"tr({PUBKEYS[0]},{ms})"
+ res = self.ms_wo_wallet.importdescriptors(
+ [
+ {
+ "desc": descsum_create(desc),
+ "active": False,
+ "timestamp": "now",
+ }
+ ]
+ )[0]
+ assert not res["success"]
+ assert "is not a valid descriptor function" in res["error"]["message"]
+
if __name__ == "__main__":
WalletMiniscriptTest().main()