mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
test: add BIP-348 coverage to feature_taproot
This commit is contained in:
parent
7be5c60a57
commit
7e7b3784dd
3 changed files with 86 additions and 4 deletions
|
@ -52,10 +52,12 @@ from test_framework.script import (
|
|||
OP_16,
|
||||
OP_2DROP,
|
||||
OP_2DUP,
|
||||
OP_3DUP,
|
||||
OP_CHECKMULTISIG,
|
||||
OP_CHECKMULTISIGVERIFY,
|
||||
OP_CHECKSIG,
|
||||
OP_CHECKSIGADD,
|
||||
OP_CHECKSIGFROMSTACK,
|
||||
OP_CHECKSIGVERIFY,
|
||||
OP_CODESEPARATOR,
|
||||
OP_DROP,
|
||||
|
@ -410,7 +412,7 @@ DEFAULT_CONTEXT = {
|
|||
# The annex (only when mode=="taproot").
|
||||
"annex": None,
|
||||
# The codeseparator position (only when mode=="taproot").
|
||||
"codeseppos": -1,
|
||||
"codeseppos": 0xffffffff,
|
||||
# The redeemscript to add to the scriptSig (if P2SH; None implies not P2SH).
|
||||
"script_p2sh": None,
|
||||
# The script to add to the witness in (if P2WSH; None implies P2WPKH)
|
||||
|
@ -750,6 +752,8 @@ def spenders_taproot_active():
|
|||
tap = taproot_construct(pubs[0], scripts)
|
||||
add_spender(spenders, "sighash/pk_codesep", tap=tap, leaf="pk_codesep", key=secs[1], **common, **SINGLE_SIG, **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
|
||||
add_spender(spenders, "sighash/codesep_pk", tap=tap, leaf="codesep_pk", key=secs[1], codeseppos=0, **common, **SINGLE_SIG, **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
|
||||
add_spender(spenders, "sighash/codesep_pk_wrongpos1", tap=tap, leaf="codesep_pk", key=secs[1], codeseppos=0, **common, **SINGLE_SIG, failure={"codeseppos": 1}, **ERR_SIG_SCHNORR)
|
||||
add_spender(spenders, "sighash/codesep_pk_wrongpos2", tap=tap, leaf="codesep_pk", key=secs[1], codeseppos=0, **common, **SINGLE_SIG, failure={"codeseppos": 0xfffffffe}, **ERR_SIG_SCHNORR)
|
||||
add_spender(spenders, "sighash/branched_codesep/left", tap=tap, leaf="branched_codesep", key=secs[0], codeseppos=3, **common, inputs=[getter("sign"), b'\x01'], **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
|
||||
add_spender(spenders, "sighash/branched_codesep/right", tap=tap, leaf="branched_codesep", key=secs[1], codeseppos=6, **common, inputs=[getter("sign"), b''], **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
|
||||
|
||||
|
@ -1054,6 +1058,13 @@ def spenders_taproot_active():
|
|||
|
||||
# == Test for sigops ratio limit ==
|
||||
|
||||
# BIP348 CSFS signatures are embedded directly into the tapleaves vs the witness stack
|
||||
# since they do not introspect directly
|
||||
CSFS_MSG = b'\x00\x00'
|
||||
# Signature should pass even if random unknown key is used, just use real privkey
|
||||
# to pass in case it's the defined pubkey
|
||||
CSFS_SIG = sign_schnorr(secs[1], CSFS_MSG)
|
||||
|
||||
# Given a number n, and a public key pk, functions that produce a (CScript, sigops). Each script takes as
|
||||
# input a valid signature with the passed pk followed by a dummy push of bytes that are to be dropped, and
|
||||
# will execute sigops signature checks.
|
||||
|
@ -1070,7 +1081,15 @@ def spenders_taproot_active():
|
|||
lambda n, pk: (CScript([OP_DROP, OP_0, pk, OP_CHECKSIG, OP_NOT, OP_VERIFY, pk] + [OP_2DUP, OP_CHECKSIG, OP_VERIFY] * n + [OP_CHECKSIG]), n + 1),
|
||||
# n OP_CHECKSIGADDs and 1 OP_CHECKSIG, but also an OP_CHECKSIGADD with an empty signature.
|
||||
lambda n, pk: (CScript([OP_DROP, OP_0, OP_10, pk, OP_CHECKSIGADD, OP_10, OP_EQUALVERIFY, pk] + [OP_2DUP, OP_16, OP_SWAP, OP_CHECKSIGADD, b'\x11', OP_EQUALVERIFY] * n + [OP_CHECKSIG]), n + 1),
|
||||
# n OP_CHECKSIGFROMSTACKs, dropping the signature given, and just validate against embedded sigs
|
||||
lambda n, pk: (CScript([OP_2DROP, CSFS_SIG, CSFS_MSG, pk] + [OP_3DUP, OP_CHECKSIGFROMSTACK, OP_DROP] * n + [OP_2DROP]), n),
|
||||
# 1 CHECKSIGVERIFY followed by n OP_CHECKSIGFROMSTACKs, all signatures non-empty and validated
|
||||
lambda n, pk: (CScript([OP_DROP, pk, OP_CHECKSIGVERIFY, CSFS_SIG, CSFS_MSG, pk] + [OP_3DUP, OP_CHECKSIGFROMSTACK, OP_DROP] * n + [OP_2DROP]), n+1),
|
||||
# 1 empty CHECKSIG followed by 1 empty OP_CHECKSIGFROMSTACKs, then finally n OP_CHECKSIGFROMSTACKs
|
||||
lambda n, pk: (CScript([OP_2DROP, OP_0, pk, OP_CHECKSIG, OP_DROP, OP_0, CSFS_MSG, pk, OP_CHECKSIGFROMSTACK, OP_DROP, CSFS_SIG, CSFS_MSG, pk] + [OP_3DUP, OP_CHECKSIGFROMSTACK, OP_DROP] * n + [OP_2DROP]), n),
|
||||
|
||||
]
|
||||
|
||||
for annex in [None, bytes([ANNEX_TAG]) + random.randbytes(random.randrange(1000))]:
|
||||
for hashtype in [SIGHASH_DEFAULT, SIGHASH_ALL]:
|
||||
for pubkey in [pubs[1], random.randbytes(random.choice([x for x in range(2, 81) if x != 32]))]:
|
||||
|
@ -1234,6 +1253,67 @@ def spenders_taproot_nonstandard():
|
|||
|
||||
return spenders
|
||||
|
||||
def bip348_csfs_spenders():
|
||||
secs = [generate_privkey() for _ in range(2)]
|
||||
pubs = [compute_xonly_pubkey(sec)[0] for sec in secs]
|
||||
|
||||
CSFS_MSG = random.randbytes(random.randrange(0, 520))
|
||||
|
||||
# Grow, shrink the message being signed, and pick random bytes
|
||||
TRUNC_CSFS_MSG = CSFS_MSG[:] if len(CSFS_MSG) > 0 else None
|
||||
if TRUNC_CSFS_MSG is not None:
|
||||
prune_index = random.randrange(len(TRUNC_CSFS_MSG))
|
||||
TRUNC_CSFS_MSG = TRUNC_CSFS_MSG[:prune_index] + TRUNC_CSFS_MSG[prune_index+1:]
|
||||
extendable_length = 520 - len(CSFS_MSG)
|
||||
EXTEND_CSFS_MSG = None
|
||||
if extendable_length > 0:
|
||||
EXTEND_CSFS_MSG = CSFS_MSG + random.randbytes(random.randrange(1, extendable_length))
|
||||
OTHER_CSFS_MSG = CSFS_MSG
|
||||
|
||||
while OTHER_CSFS_MSG == CSFS_MSG:
|
||||
OTHER_CSFS_MSG = random.randbytes(random.randrange(0, 520))
|
||||
|
||||
UNK_PUBKEY = random.randbytes(random.randrange(1, 520))
|
||||
while len(UNK_PUBKEY) == 32:
|
||||
UNK_PUBKEY = random.randbytes(random.randrange(1, 520))
|
||||
|
||||
# Sigops ratio test is included elsewhere to mix and match with other sigops
|
||||
scripts = [
|
||||
("simple_csfs", CScript([CSFS_MSG, pubs[0], OP_CHECKSIGFROMSTACK, OP_1, OP_EQUAL])),
|
||||
("simple_fail_csfs", CScript([CSFS_MSG, pubs[0], OP_CHECKSIGFROMSTACK, OP_0, OP_EQUAL])),
|
||||
("unk_pubkey_csfs", CScript([CSFS_MSG, UNK_PUBKEY, OP_CHECKSIGFROMSTACK])),
|
||||
("onearg_csfs", CScript([pubs[0], OP_CHECKSIGFROMSTACK])),
|
||||
("twoargs_csfs", CScript([CSFS_MSG, pubs[0], OP_CHECKSIGFROMSTACK])),
|
||||
("empty_pk_csfs", CScript([CSFS_MSG, OP_0, OP_CHECKSIGFROMSTACK, OP_0, OP_EQUAL])),
|
||||
]
|
||||
|
||||
tap = taproot_construct(pubs[0], scripts)
|
||||
|
||||
spenders = []
|
||||
|
||||
# "sighash" is actually the bip340 message being directly verified against
|
||||
add_spender(spenders, comment="bip348_csfs/simple", tap=tap, leaf="simple_csfs", key=secs[0], inputs=[getter("sign")], sighash=CSFS_MSG, failure={"sighash": OTHER_CSFS_MSG}, **ERR_SIG_SCHNORR)
|
||||
if TRUNC_CSFS_MSG is not None:
|
||||
add_spender(spenders, comment="bip348_csfs/trunc_msg", tap=tap, leaf="onearg_csfs", key=secs[0], inputs=[getter("sign"), CSFS_MSG], standard=len(CSFS_MSG)<=80, sighash=CSFS_MSG, failure={"inputs": [getter("sign"), TRUNC_CSFS_MSG]}, **ERR_SIG_SCHNORR)
|
||||
if EXTEND_CSFS_MSG is not None:
|
||||
add_spender(spenders, comment="bip348_csfs/extend_msg", tap=tap, leaf="onearg_csfs", key=secs[0], inputs=[getter("sign"), CSFS_MSG], standard=len(CSFS_MSG)<=80, sighash=CSFS_MSG, failure={"inputs": [getter("sign"), EXTEND_CSFS_MSG]}, **ERR_SIG_SCHNORR)
|
||||
|
||||
# Empty signature pushes zero onto stack and continues, unless the pubkey is empty
|
||||
add_spender(spenders, comment="bip348_csfs/simple_fail", tap=tap, leaf="simple_fail_csfs", inputs=[b''], failure={"leaf": "empty_pk_csfs", "inputs": [OTHER_CSFS_MSG]}, **ERR_UNKNOWN_PUBKEY)
|
||||
|
||||
# Unknown pubkey of non-zero size is unconditionally valid regardless of signature (but signature must exist)
|
||||
add_spender(spenders, comment="bip348_csfs/unk_pubkey", tap=tap, leaf="unk_pubkey_csfs", standard=False, key=secs[0], inputs=[getter("sign")], sighash=CSFS_MSG, failure={"inputs": []}, **ERR_STACK_EMPTY)
|
||||
|
||||
# You need three args for CSFS regardless of what is passed
|
||||
add_spender(spenders, comment="bip348_csfs/onearg", tap=tap, leaf="onearg_csfs", key=secs[0], inputs=[getter("sign"), CSFS_MSG], standard=len(CSFS_MSG)<=80, sighash=CSFS_MSG, failure={"inputs": []}, **ERR_STACK_EMPTY)
|
||||
add_spender(spenders, comment="bip348_csfs/twoarg", tap=tap, leaf="twoargs_csfs", key=secs[0], inputs=[getter("sign")], sighash=CSFS_MSG, failure={"inputs": []}, **ERR_STACK_EMPTY)
|
||||
|
||||
# If a known pubkey's signature is not 64 bytes or empty it MUST fail immediately
|
||||
add_spender(spenders, comment="bip348_csfs/simple_65_sig", tap=tap, leaf="simple_csfs", key=secs[0], inputs=[getter("sign")], sighash=CSFS_MSG, failure={"leaf": "simple_fail_csfs", "inputs": [zero_appender(getter("sign"))]}, **ERR_SIG_SCHNORR)
|
||||
add_spender(spenders, comment="bip348_csfs/simple_63_sig", tap=tap, leaf="simple_csfs", key=secs[0], inputs=[getter("sign")], sighash=CSFS_MSG, failure={"leaf": "simple_fail_csfs", "inputs": [byte_popper(getter("sign"))]}, **ERR_SIG_SCHNORR)
|
||||
|
||||
return spenders
|
||||
|
||||
def sample_spenders():
|
||||
|
||||
# Create key(s) for output creation, as well as key and script-spends
|
||||
|
@ -1800,6 +1880,7 @@ class TaprootTest(BitcoinTestFramework):
|
|||
# to allow for increased coverage across input types.
|
||||
# See sample_spenders for a minimal example
|
||||
consensus_spenders = sample_spenders()
|
||||
consensus_spenders += bip348_csfs_spenders()
|
||||
consensus_spenders += spenders_taproot_active()
|
||||
self.test_spenders(self.nodes[0], consensus_spenders, input_counts=[1, 2, 2, 2, 2, 3])
|
||||
|
||||
|
|
|
@ -272,7 +272,6 @@ def sign_schnorr(key, msg, aux=None, flip_p=False, flip_r=False):
|
|||
aux = bytes(32)
|
||||
|
||||
assert len(key) == 32
|
||||
assert len(msg) == 32
|
||||
assert len(aux) == 32
|
||||
|
||||
sec = int.from_bytes(key, 'big')
|
||||
|
|
|
@ -233,7 +233,6 @@ OP_CHECKSIG = CScriptOp(0xac)
|
|||
OP_CHECKSIGVERIFY = CScriptOp(0xad)
|
||||
OP_CHECKMULTISIG = CScriptOp(0xae)
|
||||
OP_CHECKMULTISIGVERIFY = CScriptOp(0xaf)
|
||||
OP_CHECKSIGFROMSTACK = CScriptOp(0xcc)
|
||||
|
||||
# expansion
|
||||
OP_NOP1 = CScriptOp(0xb0)
|
||||
|
@ -250,6 +249,9 @@ OP_NOP10 = CScriptOp(0xb9)
|
|||
# BIP 342 opcodes (Tapscript)
|
||||
OP_CHECKSIGADD = CScriptOp(0xba)
|
||||
|
||||
# BIP 348 (OP_SUCCESS204)
|
||||
OP_CHECKSIGFROMSTACK = CScriptOp(0xcc)
|
||||
|
||||
OP_INVALIDOPCODE = CScriptOp(0xff)
|
||||
|
||||
OPCODE_NAMES.update({
|
||||
|
@ -850,7 +852,7 @@ def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index=0, *, scriptpa
|
|||
if scriptpath:
|
||||
ss += TaggedHash("TapLeaf", bytes([leaf_ver]) + ser_string(leaf_script))
|
||||
ss += bytes([0])
|
||||
ss += codeseparator_pos.to_bytes(4, "little", signed=True)
|
||||
ss += codeseparator_pos.to_bytes(4, "little", signed=False)
|
||||
assert len(ss) == 175 - (in_type == SIGHASH_ANYONECANPAY) * 49 - (out_type != SIGHASH_ALL and out_type != SIGHASH_SINGLE) * 32 + (annex is not None) * 32 + scriptpath * 37
|
||||
return ss
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue