test: Fix SignatureHash RPC test helper

- Check validity of inIdx.
- ZIP 143 and 243 support.
This commit is contained in:
Jack Grigg 2020-11-17 13:00:04 +00:00
parent bbed3ecc3c
commit 030a685392
1 changed files with 137 additions and 36 deletions

View File

@ -19,9 +19,11 @@ if sys.version > '3':
bchr = lambda x: bytes([x])
bord = lambda x: x
from pyblake2 import blake2b
import struct
from test_framework import bignum
from test_framework.mininode import (CTransaction, CTxOut, hash256, ser_string, ser_uint256)
MAX_SCRIPT_SIZE = 10000
MAX_SCRIPT_ELEMENT_SIZE = 520
@ -820,53 +822,152 @@ SIGHASH_NONE = 2
SIGHASH_SINGLE = 3
SIGHASH_ANYONECANPAY = 0x80
def getHashPrevouts(tx):
digest = blake2b(digest_size=32, person=b'ZcashPrevoutHash')
for x in tx.vin:
digest.update(x.prevout.serialize())
return digest.digest()
def SignatureHash(script, txTo, inIdx, hashtype):
"""Consensus-correct SignatureHash
def getHashSequence(tx):
digest = blake2b(digest_size=32, person=b'ZcashSequencHash')
for x in tx.vin:
digest.update(struct.pack('<I', x.nSequence))
return digest.digest()
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'
def getHashOutputs(tx):
digest = blake2b(digest_size=32, person=b'ZcashOutputsHash')
for x in tx.vout:
digest.update(x.serialize())
return digest.digest()
def getHashJoinSplits(tx):
digest = blake2b(digest_size=32, person=b'ZcashJSplitsHash')
for jsdesc in tx.vJoinSplit:
digest.update(jsdesc.serialize())
digest.update(tx.joinSplitPubKey)
return digest.digest()
def getHashShieldedSpends(tx):
digest = blake2b(digest_size=32, person=b'ZcashSSpendsHash')
for desc in tx.shieldedSpends:
# We don't pass in serialized form of desc as spendAuthSig is not part of the hash
digest.update(ser_uint256(desc.cv))
digest.update(ser_uint256(desc.anchor))
digest.update(ser_uint256(desc.nullifier))
digest.update(ser_uint256(desc.rk))
digest.update(desc.proof)
return digest.digest()
def getHashShieldedOutputs(tx):
digest = blake2b(digest_size=32, person=b'ZcashSOutputHash')
for desc in tx.shieldedOutputs:
digest.update(desc.serialize())
return digest.digest()
def SignatureHash(script, txTo, inIdx, hashtype, amount, consensusBranchId):
"""Consensus-correct SignatureHash"""
if inIdx >= len(txTo.vin):
return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin)))
txtmp = CTransaction(txTo)
raise ValueError("inIdx %d out of range (%d)" % (inIdx, len(txTo.vin)))
for txin in txtmp.vin:
txin.scriptSig = b''
txtmp.vin[inIdx].scriptSig = script
if consensusBranchId != 0:
# ZIP 243
hashPrevouts = b'\x00'*32
hashSequence = b'\x00'*32
hashOutputs = b'\x00'*32
hashJoinSplits = b'\x00'*32
hashShieldedSpends = b'\x00'*32
hashShieldedOutputs = b'\x00'*32
if (hashtype & 0x1f) == SIGHASH_NONE:
txtmp.vout = []
if not (hashtype & SIGHASH_ANYONECANPAY):
hashPrevouts = getHashPrevouts(txTo)
for i in range(len(txtmp.vin)):
if i != inIdx:
txtmp.vin[i].nSequence = 0
if (not (hashtype & SIGHASH_ANYONECANPAY)) and \
(hashtype & 0x1f) != SIGHASH_SINGLE and \
(hashtype & 0x1f) != SIGHASH_NONE:
hashSequence = getHashSequence(txTo)
elif (hashtype & 0x1f) == SIGHASH_SINGLE:
outIdx = inIdx
if outIdx >= len(txtmp.vout):
return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout)))
if (hashtype & 0x1f) != SIGHASH_SINGLE and \
(hashtype & 0x1f) != SIGHASH_NONE:
hashOutputs = getHashOutputs(txTo)
elif (hashtype & 0x1f) == SIGHASH_SINGLE and \
0 <= inIdx and inIdx < len(txTo.vout):
digest = blake2b(digest_size=32, person=b'ZcashOutputsHash')
digest.update(txTo.vout[inIdx].serialize())
hashOutputs = digest.digest()
tmp = txtmp.vout[outIdx]
txtmp.vout = []
for i in range(outIdx):
txtmp.vout.append(CTxOut())
txtmp.vout.append(tmp)
if len(txTo.vJoinSplit) > 0:
hashJoinSplits = getHashJoinSplits(txTo)
for i in range(len(txtmp.vin)):
if i != inIdx:
txtmp.vin[i].nSequence = 0
if len(txTo.shieldedSpends) > 0:
hashShieldedSpends = getHashShieldedSpends(txTo)
if hashtype & SIGHASH_ANYONECANPAY:
tmp = txtmp.vin[inIdx]
txtmp.vin = []
txtmp.vin.append(tmp)
if len(txTo.shieldedOutputs) > 0:
hashShieldedOutputs = getHashShieldedOutputs(txTo)
s = txtmp.serialize()
s += struct.pack(b"<I", hashtype)
digest = blake2b(
digest_size=32,
person=b'ZcashSigHash' + struct.pack('<I', consensusBranchId),
)
hash = hash256(s)
digest.update(struct.pack('<I', (int(txTo.fOverwintered)<<31) | txTo.nVersion))
digest.update(struct.pack('<I', txTo.nVersionGroupId))
digest.update(hashPrevouts)
digest.update(hashSequence)
digest.update(hashOutputs)
digest.update(hashJoinSplits)
digest.update(hashShieldedSpends)
digest.update(hashShieldedOutputs)
digest.update(struct.pack('<I', txTo.nLockTime))
digest.update(struct.pack('<I', txTo.nExpiryHeight))
digest.update(struct.pack('<Q', txTo.valueBalance))
digest.update(struct.pack('<I', hashtype))
return (hash, None)
if inIdx is not None:
digest.update(txTo.vin[inIdx].prevout.serialize())
digest.update(ser_string(script))
digest.update(struct.pack('<Q', amount))
digest.update(struct.pack('<I', txTo.vin[inIdx].nSequence))
return (digest.digest(), None)
else:
# Pre-Overwinter
txtmp = CTransaction(txTo)
for txin in txtmp.vin:
txin.scriptSig = b''
txtmp.vin[inIdx].scriptSig = script
if (hashtype & 0x1f) == SIGHASH_NONE:
txtmp.vout = []
for i in range(len(txtmp.vin)):
if i != inIdx:
txtmp.vin[i].nSequence = 0
elif (hashtype & 0x1f) == SIGHASH_SINGLE:
outIdx = inIdx
if outIdx >= len(txtmp.vout):
raise ValueError("outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout)))
tmp = txtmp.vout[outIdx]
txtmp.vout = []
for i in range(outIdx):
txtmp.vout.append(CTxOut())
txtmp.vout.append(tmp)
for i in range(len(txtmp.vin)):
if i != inIdx:
txtmp.vin[i].nSequence = 0
if hashtype & SIGHASH_ANYONECANPAY:
tmp = txtmp.vin[inIdx]
txtmp.vin = []
txtmp.vin.append(tmp)
s = txtmp.serialize()
s += struct.pack(b"<I", hashtype)
hash = hash256(s)
return (hash, None)