tests: give feature_taproot access to sighash preimages

This commit is contained in:
Pieter Wuille 2021-10-26 14:34:04 -04:00
parent 5140825096
commit a5bde018b4
2 changed files with 54 additions and 22 deletions

View file

@ -25,8 +25,9 @@ from test_framework.script import (
CScript, CScript,
CScriptNum, CScriptNum,
CScriptOp, CScriptOp,
hash256,
LEAF_VERSION_TAPSCRIPT, LEAF_VERSION_TAPSCRIPT,
LegacySignatureHash, LegacySignatureMsg,
LOCKTIME_THRESHOLD, LOCKTIME_THRESHOLD,
MAX_SCRIPT_ELEMENT_SIZE, MAX_SCRIPT_ELEMENT_SIZE,
OP_0, OP_0,
@ -70,8 +71,9 @@ from test_framework.script import (
SIGHASH_NONE, SIGHASH_NONE,
SIGHASH_SINGLE, SIGHASH_SINGLE,
SIGHASH_ANYONECANPAY, SIGHASH_ANYONECANPAY,
SegwitV0SignatureHash, SegwitV0SignatureMsg,
TaprootSignatureHash, TaggedHash,
TaprootSignatureMsg,
is_op_success, is_op_success,
taproot_construct, taproot_construct,
) )
@ -194,8 +196,8 @@ def default_controlblock(ctx):
"""Default expression for "controlblock": combine leafversion, negflag, pubkey_internal, merklebranch.""" """Default expression for "controlblock": combine leafversion, negflag, pubkey_internal, merklebranch."""
return bytes([get(ctx, "leafversion") + get(ctx, "negflag")]) + get(ctx, "pubkey_internal") + get(ctx, "merklebranch") return bytes([get(ctx, "leafversion") + get(ctx, "negflag")]) + get(ctx, "pubkey_internal") + get(ctx, "merklebranch")
def default_sighash(ctx): def default_sigmsg(ctx):
"""Default expression for "sighash": depending on mode, compute BIP341, BIP143, or legacy sighash.""" """Default expression for "sigmsg": depending on mode, compute BIP341, BIP143, or legacy sigmsg."""
tx = get(ctx, "tx") tx = get(ctx, "tx")
idx = get(ctx, "idx") idx = get(ctx, "idx")
hashtype = get(ctx, "hashtype_actual") hashtype = get(ctx, "hashtype_actual")
@ -208,18 +210,30 @@ def default_sighash(ctx):
codeseppos = get(ctx, "codeseppos") codeseppos = get(ctx, "codeseppos")
leaf_ver = get(ctx, "leafversion") leaf_ver = get(ctx, "leafversion")
script = get(ctx, "script_taproot") script = get(ctx, "script_taproot")
return TaprootSignatureHash(tx, utxos, hashtype, idx, scriptpath=True, script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex) return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=True, script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex)
else: else:
return TaprootSignatureHash(tx, utxos, hashtype, idx, scriptpath=False, annex=annex) return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=False, annex=annex)
elif mode == "witv0": elif mode == "witv0":
# BIP143 signature hash # BIP143 signature hash
scriptcode = get(ctx, "scriptcode") scriptcode = get(ctx, "scriptcode")
utxos = get(ctx, "utxos") utxos = get(ctx, "utxos")
return SegwitV0SignatureHash(scriptcode, tx, idx, hashtype, utxos[idx].nValue) return SegwitV0SignatureMsg(scriptcode, tx, idx, hashtype, utxos[idx].nValue)
else: else:
# Pre-segwit signature hash # Pre-segwit signature hash
scriptcode = get(ctx, "scriptcode") scriptcode = get(ctx, "scriptcode")
return LegacySignatureHash(scriptcode, tx, idx, hashtype)[0] return LegacySignatureMsg(scriptcode, tx, idx, hashtype)[0]
def default_sighash(ctx):
"""Default expression for "sighash": depending on mode, compute tagged hash or dsha256 of sigmsg."""
msg = get(ctx, "sigmsg")
mode = get(ctx, "mode")
if mode == "taproot":
return TaggedHash("TapSighash", msg)
else:
if msg is None:
return (1).to_bytes(32, 'little')
else:
return hash256(msg)
def default_tweak(ctx): def default_tweak(ctx):
"""Default expression for "tweak": None if a leaf is specified, tap[0] otherwise.""" """Default expression for "tweak": None if a leaf is specified, tap[0] otherwise."""
@ -340,6 +354,8 @@ DEFAULT_CONTEXT = {
"key_tweaked": default_key_tweaked, "key_tweaked": default_key_tweaked,
# The tweak to use (None for script path spends, the actual tweak for key path spends). # The tweak to use (None for script path spends, the actual tweak for key path spends).
"tweak": default_tweak, "tweak": default_tweak,
# The sigmsg value (preimage of sighash)
"sigmsg": default_sigmsg,
# The sighash value (32 bytes) # The sighash value (32 bytes)
"sighash": default_sighash, "sighash": default_sighash,
# The information about the chosen script path spend (TaprootLeafInfo object). # The information about the chosen script path spend (TaprootLeafInfo object).

View file

@ -619,16 +619,15 @@ def FindAndDelete(script, sig):
r += script[last_sop_idx:] r += script[last_sop_idx:]
return CScript(r) return CScript(r)
def LegacySignatureHash(script, txTo, inIdx, hashtype): def LegacySignatureMsg(script, txTo, inIdx, hashtype):
"""Consensus-correct SignatureHash """Preimage of the signature hash, if it exists.
Returns (hash, err) to precisely match the consensus-critical behavior of Returns either (None, err) to indicate error (which translates to sighash 1),
the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity) or (msg, None).
""" """
HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
if inIdx >= len(txTo.vin): if inIdx >= len(txTo.vin):
return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) return (None, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin)))
txtmp = CTransaction(txTo) txtmp = CTransaction(txTo)
for txin in txtmp.vin: for txin in txtmp.vin:
@ -645,7 +644,7 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype):
elif (hashtype & 0x1f) == SIGHASH_SINGLE: elif (hashtype & 0x1f) == SIGHASH_SINGLE:
outIdx = inIdx outIdx = inIdx
if outIdx >= len(txtmp.vout): if outIdx >= len(txtmp.vout):
return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) return (None, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout)))
tmp = txtmp.vout[outIdx] tmp = txtmp.vout[outIdx]
txtmp.vout = [] txtmp.vout = []
@ -665,15 +664,27 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype):
s = txtmp.serialize_without_witness() s = txtmp.serialize_without_witness()
s += struct.pack(b"<I", hashtype) s += struct.pack(b"<I", hashtype)
hash = hash256(s) return (s, None)
return (hash, None) def LegacySignatureHash(*args, **kwargs):
"""Consensus-correct SignatureHash
Returns (hash, err) to precisely match the consensus-critical behavior of
the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity)
"""
HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
msg, err = LegacySignatureMsg(*args, **kwargs)
if msg is None:
return (HASH_ONE, err)
else:
return (hash256(msg), err)
# TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided. # TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided.
# Performance optimization probably not necessary for python tests, however. # Performance optimization probably not necessary for python tests, however.
# Note that this corresponds to sigversion == 1 in EvalScript, which is used # Note that this corresponds to sigversion == 1 in EvalScript, which is used
# for version 0 witnesses. # for version 0 witnesses.
def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount): def SegwitV0SignatureMsg(script, txTo, inIdx, hashtype, amount):
hashPrevouts = 0 hashPrevouts = 0
hashSequence = 0 hashSequence = 0
@ -711,8 +722,10 @@ def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount):
ss += ser_uint256(hashOutputs) ss += ser_uint256(hashOutputs)
ss += struct.pack("<i", txTo.nLockTime) ss += struct.pack("<i", txTo.nLockTime)
ss += struct.pack("<I", hashtype) ss += struct.pack("<I", hashtype)
return ss
return hash256(ss) def SegwitV0SignatureHash(*args, **kwargs):
return hash256(SegwitV0SignatureMsg(*args, **kwargs))
class TestFrameworkScript(unittest.TestCase): class TestFrameworkScript(unittest.TestCase):
def test_bn2vch(self): def test_bn2vch(self):
@ -742,7 +755,7 @@ class TestFrameworkScript(unittest.TestCase):
for value in values: for value in values:
self.assertEqual(CScriptNum.decode(CScriptNum.encode(CScriptNum(value))), value) self.assertEqual(CScriptNum.decode(CScriptNum.encode(CScriptNum(value))), value)
def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpath = False, script = CScript(), codeseparator_pos = -1, annex = None, leaf_ver = LEAF_VERSION_TAPSCRIPT): def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index = 0, scriptpath = False, script = CScript(), codeseparator_pos = -1, annex = None, leaf_ver = LEAF_VERSION_TAPSCRIPT):
assert (len(txTo.vin) == len(spent_utxos)) assert (len(txTo.vin) == len(spent_utxos))
assert (input_index < len(txTo.vin)) assert (input_index < len(txTo.vin))
out_type = SIGHASH_ALL if hash_type == 0 else hash_type & 3 out_type = SIGHASH_ALL if hash_type == 0 else hash_type & 3
@ -783,7 +796,10 @@ def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpa
ss += bytes([0]) ss += bytes([0])
ss += struct.pack("<i", codeseparator_pos) ss += struct.pack("<i", codeseparator_pos)
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 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 TaggedHash("TapSighash", ss) return ss
def TaprootSignatureHash(*args, **kwargs):
return TaggedHash("TapSighash", TaprootSignatureMsg(*args, **kwargs))
def taproot_tree_helper(scripts): def taproot_tree_helper(scripts):
if len(scripts) == 0: if len(scripts) == 0: