0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-08 10:31:50 -05:00

Compare commits

...

2 commits

Author SHA1 Message Date
Chris Stewart
cdc90108e7
Merge 6ffdabd4a0 into 8fa10edcd1 2025-01-31 02:43:01 +01:00
Chris Stewart
6ffdabd4a0 test: Add leaf_version parameter to taproot_tree_helper() 2024-11-05 08:27:50 -06:00
5 changed files with 35 additions and 33 deletions

View file

@ -656,7 +656,7 @@ def spenders_taproot_active():
# These are primarily tested through the test vectors implemented in libsecp256k1, and in src/tests/key_tests.cpp.
# Some things are tested programmatically as well here.
tap = taproot_construct(pubs[0])
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT)
# Test with key with bit flipped.
add_spender(spenders, "sig/key", tap=tap, key=secs[0], failure={"key_tweaked": bitflipper(default_key_tweaked)}, **ERR_SIG_SCHNORR)
# Test with sighash with bit flipped.
@ -699,11 +699,11 @@ def spenders_taproot_active():
# Implement a test case that detects validation logic which maps invalid public keys to the
# point at infinity in the tweaking logic.
tap = taproot_construct(invalid_pub, [("true", CScript([OP_1]))], treat_internal_as_infinity=True)
tap = taproot_construct(invalid_pub, LEAF_VERSION_TAPSCRIPT, [("true", CScript([OP_1]))], treat_internal_as_infinity=True)
add_spender(spenders, "output/invalid_x", tap=tap, key_tweaked=tap.tweak, failure={"leaf": "true", "inputs": []}, **ERR_WITNESS_PROGRAM_MISMATCH)
# Do the same thing without invalid point, to make sure there is no mistake in the test logic.
tap = taproot_construct(pubs[0], [("true", CScript([OP_1]))])
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT, [("true", CScript([OP_1]))])
add_spender(spenders, "output/invalid_x_mock", tap=tap, key=secs[0], leaf="true", inputs=[])
# == Tests for signature hashing ==
@ -718,12 +718,12 @@ def spenders_taproot_active():
common = {"annex": annex, "hashtype": hashtype, "standard": no_annex}
# Pure pubkey
tap = taproot_construct(pubs[0])
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT)
add_spender(spenders, "sighash/purepk", tap=tap, key=secs[0], **common, **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
# Pubkey/P2PK script combination
scripts = [("s0", CScript(random_checksig_style(pubs[1])))]
tap = taproot_construct(pubs[0], scripts)
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT, scripts)
add_spender(spenders, "sighash/keypath_hashtype_%x" % hashtype, tap=tap, key=secs[0], **common, **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
add_spender(spenders, "sighash/scriptpath_hashtype_%x" % hashtype, tap=tap, leaf="s0", key=secs[1], **common, **SINGLE_SIG, **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
@ -745,7 +745,7 @@ def spenders_taproot_active():
# pushes (e.g. `OP_PUSH1 1` instead of `OP_1`), we set a minimum data size of 2 bytes.
]
random.shuffle(scripts)
tap = taproot_construct(pubs[0], scripts)
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT, 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/branched_codesep/left", tap=tap, leaf="branched_codesep", key=secs[0], codeseppos=3, **common, inputs=[getter("sign"), b'\x01'], **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
@ -785,7 +785,7 @@ def spenders_taproot_active():
("csa_neg", CScript([OP_2, pubs[2], OP_CHECKSIGADD, OP_2, OP_EQUAL]))
]
random.shuffle(scripts)
tap = taproot_construct(pubs[3], scripts)
tap = taproot_construct(pubs[3], LEAF_VERSION_TAPSCRIPT, 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)
@ -825,7 +825,7 @@ def spenders_taproot_active():
prog += bytes([0 for _ in range(witlen - 32)])
return CScript([CScriptOp.encode_op_n(witver), prog])
scripts = [("s0", CScript([pubs[0], OP_CHECKSIG])), ("dummy", CScript([OP_RETURN]))]
tap = taproot_construct(pubs[1], scripts)
tap = taproot_construct(pubs[1], LEAF_VERSION_TAPSCRIPT, scripts)
if not p2sh and witver == 1 and witlen == 32:
add_spender(spenders, "applic/keypath", p2sh=p2sh, spk_mutate_pre_p2sh=mutate, tap=tap, key=secs[1], **SIGHASH_BITFLIP, **ERR_SIG_SCHNORR)
add_spender(spenders, "applic/scriptpath", p2sh=p2sh, leaf="s0", spk_mutate_pre_p2sh=mutate, tap=tap, key=secs[0], **SINGLE_SIG, failure={"leaf": "dummy"}, **ERR_OP_RETURN)
@ -861,7 +861,7 @@ def spenders_taproot_active():
# Add 127 nodes on top of that tree, so that "128deep" and "129deep" end up at their designated depths.
for _ in range(127):
scripts = [scripts, random.choice(PARTNER_MERKLE_FN)]
tap = taproot_construct(pubs[0], scripts)
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT, scripts)
# Test that spends with a depth of 128 work, but 129 doesn't (even with a tree with weird Merkle branches in it).
add_spender(spenders, "spendpath/merklelimit", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"leaf": "129deep"}, **ERR_CONTROLBLOCK_SIZE)
# Test that flipping the negation bit invalidates spends.
@ -878,7 +878,7 @@ def spenders_taproot_active():
add_spender(spenders, "spendpath/trunclongcontrol", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_merklebranch(ctx)[0:random.randrange(1, 32)]}, **ERR_CONTROLBLOCK_SIZE)
scripts = [("s", CScript([pubs[0], OP_CHECKSIG]))]
tap = taproot_construct(pubs[1], scripts)
tap = taproot_construct(pubs[1], LEAF_VERSION_TAPSCRIPT, scripts)
# Test that adding garbage to the control block invalidates it.
add_spender(spenders, "spendpath/padshortcontrol", tap=tap, leaf="s", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_controlblock(ctx) + random.randbytes(random.randrange(1, 32))}, **ERR_CONTROLBLOCK_SIZE)
# Test that truncating the control block invalidates it.
@ -997,7 +997,7 @@ def spenders_taproot_active():
for j in range(100000):
scripts.append((None, CScript([OP_RETURN, random.randrange(100000)])))
random.shuffle(scripts)
tap = taproot_construct(pubs[0], scripts)
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT, scripts)
common = {
"hashtype": hashtype,
"key": secs[1],
@ -1106,7 +1106,7 @@ def spenders_taproot_active():
scripts = [("s", fn(n, pubkey)[0])]
for _ in range(merkledepth):
scripts = [scripts, random.choice(PARTNER_MERKLE_FN)]
tap = taproot_construct(pubs[0], scripts)
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT, scripts)
standard = annex is None and dummylen <= 80 and len(pubkey) == 32
add_spender(spenders, "tapscript/sigopsratio_%i" % fn_num, tap=tap, leaf="s", annex=annex, hashtype=hashtype, key=secs[1], inputs=[getter("sign"), random.randbytes(dummylen)], standard=standard, failure={"inputs": [getter("sign"), random.randbytes(dummylen - 1)]}, **ERR_SIGOPS_RATIO)
@ -1128,7 +1128,7 @@ def spenders_taproot_active():
("1001push_unkver", CScript([OP_0] * 1001), leafver),
]
random.shuffle(scripts)
tap = taproot_construct(pubs[0], scripts)
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT, scripts)
add_spender(spenders, "unkver/bare", standard=False, tap=tap, leaf="bare_unkver", failure={"leaf": "bare_c0"}, **ERR_CLEANSTACK)
add_spender(spenders, "unkver/return", standard=False, tap=tap, leaf="return_unkver", failure={"leaf": "return_c0"}, **ERR_OP_RETURN)
add_spender(spenders, "unkver/undecodable", standard=False, tap=tap, leaf="undecodable_unkver", failure={"leaf": "undecodable_c0"}, **ERR_UNDECODABLE)
@ -1158,7 +1158,7 @@ def spenders_taproot_active():
("1001push_nop", CScript([OP_0] * 1001 + [OP_NOP])),
]
random.shuffle(scripts)
tap = taproot_construct(pubs[0], scripts)
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT, scripts)
add_spender(spenders, "opsuccess/bare", standard=False, tap=tap, leaf="bare_success", failure={"leaf": "bare_nop"}, **ERR_CLEANSTACK)
add_spender(spenders, "opsuccess/unexecif", standard=False, tap=tap, leaf="unexecif_success", failure={"leaf": "unexecif_nop"}, **ERR_CLEANSTACK)
add_spender(spenders, "opsuccess/return", standard=False, tap=tap, leaf="return_success", failure={"leaf": "return_nop"}, **ERR_OP_RETURN)
@ -1177,13 +1177,13 @@ def spenders_taproot_active():
("normal", CScript([OP_RETURN, opcode] + [OP_NOP] * 75)),
("op_success", CScript([OP_RETURN, CScriptOp(0x50)]))
]
tap = taproot_construct(pubs[0], scripts)
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT, scripts)
add_spender(spenders, "alwaysvalid/notsuccessx", tap=tap, leaf="op_success", inputs=[], standard=False, failure={"leaf": "normal"}) # err_msg differs based on opcode
# == Test case for https://github.com/bitcoin/bitcoin/issues/24765 ==
zero_fn = lambda h: bytes([0 for _ in range(32)])
tap = taproot_construct(pubs[0], [("leaf", CScript([pubs[1], OP_CHECKSIG, pubs[1], OP_CHECKSIGADD, OP_2, OP_EQUAL])), zero_fn])
tap = taproot_construct(pubs[0], LEAF_VERSION_TAPSCRIPT, [("leaf", CScript([pubs[1], OP_CHECKSIG, pubs[1], OP_CHECKSIGADD, OP_2, OP_EQUAL])), zero_fn])
add_spender(spenders, "case24765", tap=tap, leaf="leaf", inputs=[getter("sign"), getter("sign")], key=secs[1], no_fail=True)
# == Legacy tests ==
@ -1220,7 +1220,7 @@ def spenders_taproot_nonstandard():
("future_leaf", CScript([pub, OP_CHECKSIG]), 0xc2),
("op_success", CScript([pub, OP_CHECKSIG, OP_0, OP_IF, CScriptOp(0x50), OP_ENDIF])),
]
tap = taproot_construct(pub, scripts)
tap = taproot_construct(pub, LEAF_VERSION_TAPSCRIPT, scripts)
# Test that features like annex, leaf versions, or OP_SUCCESS are valid but non-standard
add_spender(spenders, "inactive/scriptpath_valid_unkleaf", key=sec, tap=tap, leaf="future_leaf", standard=False, inputs=[getter("sign")])
@ -1565,7 +1565,7 @@ class TaprootTest(BitcoinTestFramework):
[("1", CScript([pubs[58], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT), ("2", CScript([pubs[59], OP_CHECKSIG]), LEAF_VERSION_TAPSCRIPT)]
],
]
taps = [taproot_construct(inner_keys[i], script_lists[i]) for i in range(len(inner_keys))]
taps = [taproot_construct(inner_keys[i], LEAF_VERSION_TAPSCRIPT, script_lists[i]) for i in range(len(inner_keys))]
# Require negated taps[0]
assert taps[0].negflag

View file

@ -19,6 +19,7 @@ from .script import (
hash256,
sha256,
taproot_construct,
LEAF_VERSION_TAPSCRIPT
)
from .util import assert_equal
from test_framework.script_util import (
@ -56,7 +57,7 @@ def create_deterministic_address_bcrt1_p2tr_op_true(explicit_internal_key=None):
Returns a tuple with the generated address and the TaprootInfo object.
"""
internal_key = explicit_internal_key or (1).to_bytes(32, 'big')
taproot_info = taproot_construct(internal_key, [("only-path", CScript([OP_TRUE]))])
taproot_info = taproot_construct(internal_key, LEAF_VERSION_TAPSCRIPT, [("only-path", CScript([OP_TRUE]))])
address = output_key_to_p2tr(taproot_info.output_pubkey)
if explicit_internal_key is None:
assert_equal(address, 'bcrt1p9yfmy5h72durp7zrhlw9lf7jpwjgvwdg0jr0lqmmjtgg83266lqsekaqka')

View file

@ -856,7 +856,7 @@ def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index=0, *, scriptpa
def TaprootSignatureHash(*args, **kwargs):
return TaggedHash("TapSighash", TaprootSignatureMsg(*args, **kwargs))
def taproot_tree_helper(scripts):
def taproot_tree_helper(scripts, leaf_version):
if len(scripts) == 0:
return ([], bytes())
if len(scripts) == 1:
@ -864,30 +864,29 @@ def taproot_tree_helper(scripts):
script = scripts[0]
assert not callable(script)
if isinstance(script, list):
return taproot_tree_helper(script)
return taproot_tree_helper(script, leaf_version)
assert isinstance(script, tuple)
version = LEAF_VERSION_TAPSCRIPT
name = script[0]
code = script[1]
if len(script) == 3:
version = script[2]
assert version & 1 == 0
leaf_version = script[2]
assert leaf_version & 1 == 0
assert isinstance(code, bytes)
h = TaggedHash("TapLeaf", bytes([version]) + ser_string(code))
h = TaggedHash("TapLeaf", bytes([leaf_version]) + ser_string(code))
if name is None:
return ([], h)
return ([(name, version, code, bytes(), h)], h)
return ([(name, leaf_version, code, bytes(), h)], h)
elif len(scripts) == 2 and callable(scripts[1]):
# Two entries, and the right one is a function
left, left_h = taproot_tree_helper(scripts[0:1])
left, left_h = taproot_tree_helper(scripts[0:1], leaf_version)
right_h = scripts[1](left_h)
left = [(name, version, script, control + right_h, leaf) for name, version, script, control, leaf in left]
right = []
else:
# Two or more entries: descend into each side
split_pos = len(scripts) // 2
left, left_h = taproot_tree_helper(scripts[0:split_pos])
right, right_h = taproot_tree_helper(scripts[split_pos:])
left, left_h = taproot_tree_helper(scripts[0:split_pos], leaf_version)
right, right_h = taproot_tree_helper(scripts[split_pos:], leaf_version)
left = [(name, version, script, control + right_h, leaf) for name, version, script, control, leaf in left]
right = [(name, version, script, control + left_h, leaf) for name, version, script, control, leaf in right]
if right_h < left_h:
@ -910,7 +909,7 @@ TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,internal_pubkey,negflag,tw
# - 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, leaf_version, scripts=None, treat_internal_as_infinity=False):
"""Construct a tree of Taproot spending conditions
pubkey: a 32-byte xonly pubkey for the internal pubkey (bytes)
@ -927,7 +926,7 @@ def taproot_construct(pubkey, scripts=None, treat_internal_as_infinity=False):
if scripts is None:
scripts = []
ret, h = taproot_tree_helper(scripts)
ret, h = taproot_tree_helper(scripts, leaf_version)
tweak = TaggedHash("TapTweak", pubkey + h)
if treat_internal_as_infinity:
tweaked, negated = compute_xonly_pubkey(tweak)

View file

@ -36,6 +36,7 @@ from test_framework.messages import (
ser_compact_size,
)
from test_framework.script import (
LEAF_VERSION_TAPSCRIPT,
CScript,
OP_1,
OP_NOP,
@ -445,7 +446,7 @@ def getnewdestination(address_type='bech32m'):
scriptpubkey = key_to_p2wpkh_script(pubkey)
address = key_to_p2wpkh(pubkey)
elif address_type == 'bech32m':
tap = taproot_construct(compute_xonly_pubkey(key.get_bytes())[0])
tap = taproot_construct(compute_xonly_pubkey(key.get_bytes())[0], LEAF_VERSION_TAPSCRIPT)
pubkey = tap.output_pubkey
scriptpubkey = tap.scriptPubKey
address = output_key_to_p2tr(pubkey)

View file

@ -20,6 +20,7 @@ from test_framework.script import (
OP_CHECKSIGADD,
OP_NUMEQUAL,
taproot_construct,
LEAF_VERSION_TAPSCRIPT
)
from test_framework.segwit_addr import encode_segwit_address
@ -179,7 +180,7 @@ def multi_a(k, hex_keys, sort=False):
def compute_taproot_address(pubkey, scripts):
"""Compute the address for a taproot output with given inner key and scripts."""
return output_key_to_p2tr(taproot_construct(pubkey, scripts).output_pubkey)
return output_key_to_p2tr(taproot_construct(pubkey, LEAF_VERSION_TAPSCRIPT, scripts).output_pubkey)
def compute_raw_taproot_address(pubkey):
return encode_segwit_address("bcrt", 1, pubkey)