From 1d5cd5af62c07d30caab7afa0c668413d894b618 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 9 Sep 2022 16:17:44 +1000 Subject: [PATCH] tests: Add ANYPREVOUT testing to feature_taproot --- .../fuzz/script_assets_test_minimizer.cpp | 1 + test/functional/feature_taproot.py | 99 ++++++++++++------- test/functional/test_framework/script.py | 9 +- 3 files changed, 70 insertions(+), 39 deletions(-) diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp index 511b581f606395..a803ad37542fc9 100644 --- a/src/test/fuzz/script_assets_test_minimizer.cpp +++ b/src/test/fuzz/script_assets_test_minimizer.cpp @@ -96,6 +96,7 @@ const std::map FLAG_NAMES = { {std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY}, {std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS}, {std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT}, + {std::string("ANYPREVOUT"), (unsigned int)SCRIPT_VERIFY_ANYPREVOUT}, }; std::vector AllFlags() diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py index c688ef97872c7f..9e44ec68dfbb55 100755 --- a/test/functional/feature_taproot.py +++ b/test/functional/feature_taproot.py @@ -77,6 +77,8 @@ SIGHASH_NONE, SIGHASH_SINGLE, SIGHASH_ANYONECANPAY, + SIGHASH_ANYPREVOUT, + SIGHASH_ANYPREVOUTANYSCRIPT, SegwitV0SignatureMsg, TaggedHash, TaprootSignatureMsg, @@ -189,6 +191,10 @@ def default_hashtype(ctx): else: return SIGHASH_ALL +def default_keyver(ctx): + """Default expression for "keyver": 0 (bip341/bip342)""" + return get(ctx, "tap").keyver + def default_tapleaf(ctx): """Default expression for "tapleaf": looking up leaf in tap[2].""" return get(ctx, "tap").leaves[get(ctx, "leaf")] @@ -231,7 +237,8 @@ def default_sigmsg(ctx): codeseppos = get(ctx, "codeseppos") leaf_ver = get(ctx, "leafversion") script = get(ctx, "script_taproot") - return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=True, script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex) + keyver = get(ctx, "keyver") + return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=True, script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex, key_ver=keyver) else: return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=False, annex=annex) elif mode == "witv0": @@ -393,6 +400,8 @@ def default_scriptsig(ctx): "negflag": default_negflag, # The leaf version to include in the sighash (this does not affect the one in the control block). "leafversion": default_leafversion, + # The key version to include in the sighash. + "keyver": default_keyver, # The Merkle path to include in the control block for a script path spend. "merklebranch": default_merklebranch, # The control block to push for a taproot script path spend. @@ -625,6 +634,14 @@ def byte_popper(expr): ] VALID_SIGHASHES_TAPROOT = [SIGHASH_DEFAULT] + VALID_SIGHASHES_ECDSA +VALID_SIGHASHES_ANYPREVOUT = [SIGHASH_DEFAULT] + VALID_SIGHASHES_ECDSA + [ + SIGHASH_ANYPREVOUT + SIGHASH_ALL, + SIGHASH_ANYPREVOUT + SIGHASH_NONE, + SIGHASH_ANYPREVOUT + SIGHASH_SINGLE, + SIGHASH_ANYPREVOUTANYSCRIPT + SIGHASH_ALL, + SIGHASH_ANYPREVOUTANYSCRIPT + SIGHASH_NONE, + SIGHASH_ANYPREVOUTANYSCRIPT + SIGHASH_SINGLE, +] VALID_SIGHASHES_TAPROOT_SINGLE = [ SIGHASH_SINGLE, @@ -775,41 +792,48 @@ def spenders_taproot_active(): add_spender(spenders, "sighash/hashtype1to0_keypath", tap=tap, key=secs[0], hashtype=SIGHASH_ALL, failure={"bytes_hashtype": b''}, **ERR_SIG_SCHNORR) add_spender(spenders, "sighash/hashtype1to0_scriptpath", tap=tap, leaf="pk_codesep", key=secs[1], **SINGLE_SIG, hashtype=SIGHASH_ALL, failure={"bytes_hashtype": b''}, **ERR_SIG_SCHNORR) - # Test aspects of signatures with unusual lengths - for hashtype in [SIGHASH_DEFAULT, random.choice(VALID_SIGHASHES_TAPROOT)]: - scripts = [ - ("csv", CScript([pubs[2], OP_CHECKSIGVERIFY, OP_1])), - ("cs_pos", CScript([pubs[2], OP_CHECKSIG])), - ("csa_pos", CScript([OP_0, pubs[2], OP_CHECKSIGADD, OP_1, OP_EQUAL])), - ("cs_neg", CScript([pubs[2], OP_CHECKSIG, OP_NOT])), - ("csa_neg", CScript([OP_2, pubs[2], OP_CHECKSIGADD, OP_2, OP_EQUAL])) - ] - random.shuffle(scripts) - tap = taproot_construct(pubs[3], scripts) - # Empty signatures - add_spender(spenders, "siglen/empty_keypath", tap=tap, key=secs[3], hashtype=hashtype, failure={"sign": b""}, **ERR_SIG_SIZE) - add_spender(spenders, "siglen/empty_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_CHECKSIGVERIFY) - add_spender(spenders, "siglen/empty_cs", tap=tap, key=secs[2], leaf="cs_pos", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_NO_SUCCESS) - add_spender(spenders, "siglen/empty_csa", tap=tap, key=secs[2], leaf="csa_pos", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_NO_SUCCESS) - add_spender(spenders, "siglen/empty_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": lambda _: random.randbytes(random.randrange(1, 63))}, **ERR_SIG_SIZE) - add_spender(spenders, "siglen/empty_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": lambda _: random.randbytes(random.randrange(66, 100))}, **ERR_SIG_SIZE) - # Appending a zero byte to signatures invalidates them - add_spender(spenders, "siglen/padzero_keypath", tap=tap, key=secs[3], hashtype=hashtype, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) - add_spender(spenders, "siglen/padzero_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) - add_spender(spenders, "siglen/padzero_cs", tap=tap, key=secs[2], leaf="cs_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) - add_spender(spenders, "siglen/padzero_csa", tap=tap, key=secs[2], leaf="csa_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) - add_spender(spenders, "siglen/padzero_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) - add_spender(spenders, "siglen/padzero_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) - # Removing the last byte from signatures invalidates them - add_spender(spenders, "siglen/popbyte_keypath", tap=tap, key=secs[3], hashtype=hashtype, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) - add_spender(spenders, "siglen/popbyte_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) - add_spender(spenders, "siglen/popbyte_cs", tap=tap, key=secs[2], leaf="cs_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) - add_spender(spenders, "siglen/popbyte_csa", tap=tap, key=secs[2], leaf="csa_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) - add_spender(spenders, "siglen/popbyte_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) - add_spender(spenders, "siglen/popbyte_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) - # Verify that an invalid signature is not allowed, not even when the CHECKSIG* is expected to fail. - add_spender(spenders, "siglen/invalid_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": default_sign, "sighash": bitflipper(default_sighash)}, **ERR_SIG_SCHNORR) - add_spender(spenders, "siglen/invalid_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": default_sign, "sighash": bitflipper(default_sighash)}, **ERR_SIG_SCHNORR) + def spenders_taproot_active_script_path(prefix, valid_sighashes, keyver=1, cmt=""): + # Test aspects of signatures with unusual lengths + for hashtype in [SIGHASH_DEFAULT, random.choice(valid_sighashes)]: + scripts = [ + ("csv", CScript([prefix+pubs[2], OP_CHECKSIGVERIFY, OP_1])), + ("cs_pos", CScript([prefix+pubs[2], OP_CHECKSIG])), + ("csa_pos", CScript([OP_0, prefix+pubs[2], OP_CHECKSIGADD, OP_1, OP_EQUAL])), + ("cs_neg", CScript([prefix+pubs[2], OP_CHECKSIG, OP_NOT])), + ("csa_neg", CScript([OP_2, prefix+pubs[2], OP_CHECKSIGADD, OP_2, OP_EQUAL])) + ] + random.shuffle(scripts) + tap = taproot_construct(pubs[3], scripts, keyver=keyver) + # key path + if keyver == 0: + add_spender(spenders, cmt+"siglen/empty_keypath", tap=tap, key=secs[3], hashtype=hashtype, failure={"sign": b""}, **ERR_SIG_SIZE) + add_spender(spenders, cmt+"siglen/padzero_keypath", tap=tap, key=secs[3], hashtype=hashtype, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) + add_spender(spenders, cmt+"siglen/popbyte_keypath", tap=tap, key=secs[3], hashtype=hashtype, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) + + # Empty signatures + add_spender(spenders, cmt+"siglen/empty_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_CHECKSIGVERIFY) + add_spender(spenders, cmt+"siglen/empty_cs", tap=tap, key=secs[2], leaf="cs_pos", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_NO_SUCCESS) + add_spender(spenders, cmt+"siglen/empty_csa", tap=tap, key=secs[2], leaf="csa_pos", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_NO_SUCCESS) + add_spender(spenders, cmt+"siglen/empty_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": lambda _: random.randbytes(random.randrange(1, 63))}, **ERR_SIG_SIZE) + add_spender(spenders, cmt+"siglen/empty_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": lambda _: random.randbytes(random.randrange(66, 100))}, **ERR_SIG_SIZE) + # Appending a zero byte to signatures invalidates them + add_spender(spenders, cmt+"siglen/padzero_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) + add_spender(spenders, cmt+"siglen/padzero_cs", tap=tap, key=secs[2], leaf="cs_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) + add_spender(spenders, cmt+"siglen/padzero_csa", tap=tap, key=secs[2], leaf="csa_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) + add_spender(spenders, cmt+"siglen/padzero_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) + add_spender(spenders, cmt+"siglen/padzero_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE)) + # Removing the last byte from signatures invalidates them + add_spender(spenders, cmt+"siglen/popbyte_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) + add_spender(spenders, cmt+"siglen/popbyte_cs", tap=tap, key=secs[2], leaf="cs_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) + add_spender(spenders, cmt+"siglen/popbyte_csa", tap=tap, key=secs[2], leaf="csa_pos", hashtype=hashtype, **SINGLE_SIG, **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) + add_spender(spenders, cmt+"siglen/popbyte_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) + add_spender(spenders, cmt+"siglen/popbyte_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", **SIG_POP_BYTE, **(ERR_SIG_SIZE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SCHNORR)) + # Verify that an invalid signature is not allowed, not even when the CHECKSIG* is expected to fail. + add_spender(spenders, cmt+"siglen/invalid_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": default_sign, "sighash": bitflipper(default_sighash)}, **ERR_SIG_SCHNORR) + add_spender(spenders, cmt+"siglen/invalid_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": default_sign, "sighash": bitflipper(default_sighash)}, **ERR_SIG_SCHNORR) + + spenders_taproot_active_script_path(b'', VALID_SIGHASHES_TAPROOT, keyver=0) + spenders_taproot_active_script_path(b'\x01', VALID_SIGHASHES_ANYPREVOUT, keyver=1, cmt="apo/") # == Test that BIP341 spending only applies to witness version 1, program length 32, no P2SH == @@ -1234,11 +1258,14 @@ def spenders_taproot_nonstandard(): LEGACY_FLAGS = "P2SH,DERSIG,CHECKLOCKTIMEVERIFY,CHECKSEQUENCEVERIFY,WITNESS,NULLDUMMY" # Consensus validation flags to use in dumps for all other tests. TAPROOT_FLAGS = "P2SH,DERSIG,CHECKLOCKTIMEVERIFY,CHECKSEQUENCEVERIFY,WITNESS,NULLDUMMY,TAPROOT" +APO_FLAGS = "P2SH,DERSIG,CHECKLOCKTIMEVERIFY,CHECKSEQUENCEVERIFY,WITNESS,NULLDUMMY,TAPROOT,ANYPREVOUT" def dump_json_test(tx, input_utxos, idx, success, failure): spender = input_utxos[idx].spender # Determine flags to dump flags = LEGACY_FLAGS if spender.comment.startswith("legacy/") or spender.comment.startswith("inactive/") else TAPROOT_FLAGS + if spender.comment.startswith("apo/"): + flags = APO_FLAGS fields = [ ("tx", tx.serialize().hex()), diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 133127fda5628f..99abb47686a726 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -915,7 +915,7 @@ def taproot_tree_helper(scripts): # - tweak: the tweak (32 bytes) # - leaves: a dict of name -> TaprootLeafInfo objects for all known leaves # - merkle_root: the script tree's Merkle root, or bytes() if no leaves are present -TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,internal_pubkey,negflag,tweak,leaves,merkle_root,output_pubkey") +TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,internal_pubkey,negflag,tweak,leaves,merkle_root,output_pubkey,keyver") # A TaprootLeafInfo object has the following fields: # - script: the leaf script (CScript or bytes) @@ -923,7 +923,7 @@ def taproot_tree_helper(scripts): # - merklebranch: the merkle branch to use for this leaf (32*N bytes) TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch,leaf_hash") -def taproot_construct(pubkey, scripts=None, treat_internal_as_infinity=False): +def taproot_construct(pubkey, scripts=None, *, keyver=None, treat_internal_as_infinity=False): """Construct a tree of Taproot spending conditions pubkey: a 32-byte xonly pubkey for the internal pubkey (bytes) @@ -940,6 +940,9 @@ def taproot_construct(pubkey, scripts=None, treat_internal_as_infinity=False): if scripts is None: scripts = [] + if keyver is None: + keyver = 0 + ret, h = taproot_tree_helper(scripts) tweak = TaggedHash("TapTweak", pubkey + h) if treat_internal_as_infinity: @@ -947,7 +950,7 @@ def taproot_construct(pubkey, scripts=None, treat_internal_as_infinity=False): else: tweaked, negated = tweak_add_pubkey(pubkey, tweak) leaves = dict((name, TaprootLeafInfo(script, version, merklebranch, leaf)) for name, version, script, merklebranch, leaf in ret) - return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves, h, tweaked) + return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves, h, tweaked, keyver) def is_op_success(o): return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xfe)