ZIP 244: Add new commitments to transparent inputs
Signatures for shielded inputs also commit to these new commitments, meaning that for mixed transactions, shielded signatures are no longer equivalent to signing the txid. This property remains for fully shielded transactions.
This commit is contained in:
parent
29872dcaa0
commit
bda224b133
29
tv_output.py
29
tv_output.py
|
@ -27,11 +27,17 @@ def tv_value_json(value, bitcoin_flavoured):
|
|||
if isinstance(value, Some):
|
||||
value = value.thing
|
||||
|
||||
if type(value) == bytes:
|
||||
if bitcoin_flavoured and len(value) == 32:
|
||||
value = value[::-1]
|
||||
value = hexlify(value).decode()
|
||||
return value
|
||||
def bitcoinify(value):
|
||||
if type(value) == bytes:
|
||||
if bitcoin_flavoured and len(value) == 32:
|
||||
value = value[::-1]
|
||||
value = hexlify(value).decode()
|
||||
return value
|
||||
|
||||
if type(value) == list:
|
||||
return [bitcoinify(v) for v in value]
|
||||
else:
|
||||
return bitcoinify(value)
|
||||
|
||||
def tv_json(filename, parts, vectors, bitcoin_flavoured):
|
||||
if type(vectors) == type({}):
|
||||
|
@ -148,11 +154,22 @@ def tv_part_rust(name, value, config, indent=3):
|
|||
name,
|
||||
))
|
||||
for item in value:
|
||||
if type(item) == bytes:
|
||||
if 'Vec<u8>' in config['rust_type']:
|
||||
print('''%svec![
|
||||
%s%s
|
||||
%s],''' % (
|
||||
' ' * (indent + 1),
|
||||
' ' * (indent + 1),
|
||||
chunk(hexlify(item)),
|
||||
' ' * (indent + 1),
|
||||
))
|
||||
elif type(item) == bytes:
|
||||
print('''%s[%s],''' % (
|
||||
' ' * (indent + 1),
|
||||
chunk(hexlify(item)),
|
||||
))
|
||||
elif type(item) == int:
|
||||
print('%s%d,' % (' ' * (indent + 1), item))
|
||||
elif type(item) == list:
|
||||
print('''%s[''' % (
|
||||
' ' * (indent + 1)
|
||||
|
|
102
zip_0244.py
102
zip_0244.py
|
@ -221,35 +221,34 @@ def auth_digest(tx):
|
|||
# Signatures
|
||||
|
||||
class TransparentInput(object):
|
||||
def __init__(self, tx, rand):
|
||||
def __init__(self, nIn, rand):
|
||||
self.nIn = nIn
|
||||
self.scriptCode = Script(rand)
|
||||
self.nIn = rand.u8() % len(tx.vin)
|
||||
self.amount = rand.u64() % (MAX_MONEY + 1)
|
||||
|
||||
def signature_digest(tx, nHashType, txin):
|
||||
def signature_digest(tx, t_inputs, nHashType, txin):
|
||||
digest = blake2b(
|
||||
digest_size=32,
|
||||
person=b'ZcashTxHash_' + struct.pack('<I', tx.nConsensusBranchId),
|
||||
)
|
||||
|
||||
digest.update(header_digest(tx))
|
||||
digest.update(transparent_sig_digest(tx, nHashType, txin))
|
||||
digest.update(transparent_sig_digest(tx, t_inputs, nHashType, txin))
|
||||
digest.update(sapling_digest(tx))
|
||||
digest.update(orchard_digest(tx))
|
||||
|
||||
return digest.digest()
|
||||
|
||||
def transparent_sig_digest(tx, nHashType, txin):
|
||||
# Sapling Spend or Orchard Action
|
||||
if txin is None:
|
||||
return transparent_digest(tx)
|
||||
|
||||
def transparent_sig_digest(tx, t_inputs, nHashType, txin):
|
||||
digest = blake2b(digest_size=32, person=b'ZTxIdTranspaHash')
|
||||
|
||||
digest.update(prevouts_sig_digest(tx, nHashType))
|
||||
digest.update(sequence_sig_digest(tx, nHashType))
|
||||
digest.update(outputs_sig_digest(tx, nHashType, txin))
|
||||
digest.update(txin_sig_digest(tx, txin))
|
||||
if len(tx.vin) + len(tx.vout) > 0:
|
||||
digest.update(prevouts_sig_digest(tx, nHashType))
|
||||
digest.update(amounts_sig_digest(t_inputs, nHashType))
|
||||
digest.update(script_codes_sig_digest(t_inputs, nHashType))
|
||||
digest.update(sequence_sig_digest(tx, nHashType))
|
||||
digest.update(outputs_sig_digest(tx, nHashType, txin))
|
||||
digest.update(txin_sig_digest(tx, txin))
|
||||
|
||||
return digest.digest()
|
||||
|
||||
|
@ -260,6 +259,26 @@ def prevouts_sig_digest(tx, nHashType):
|
|||
else:
|
||||
return blake2b(digest_size=32, person=b'ZTxIdPrevoutHash').digest()
|
||||
|
||||
def amounts_sig_digest(t_inputs, nHashType):
|
||||
# If the SIGHASH_ANYONECANPAY flag is not set:
|
||||
if not (nHashType & SIGHASH_ANYONECANPAY):
|
||||
digest = blake2b(digest_size=32, person=b'ZTxTrAmountsHash')
|
||||
for x in t_inputs:
|
||||
digest.update(struct.pack('<Q', x.amount))
|
||||
return digest.digest()
|
||||
else:
|
||||
return blake2b(digest_size=32, person=b'ZTxTrAmountsHash').digest()
|
||||
|
||||
def script_codes_sig_digest(t_inputs, nHashType):
|
||||
# If the SIGHASH_ANYONECANPAY flag is not set:
|
||||
if not (nHashType & SIGHASH_ANYONECANPAY):
|
||||
digest = blake2b(digest_size=32, person=b'ZTxTrScriptsHash')
|
||||
for x in t_inputs:
|
||||
digest.update(bytes(x.scriptCode))
|
||||
return digest.digest()
|
||||
else:
|
||||
return blake2b(digest_size=32, person=b'ZTxTrScriptsHash').digest()
|
||||
|
||||
def sequence_sig_digest(tx, nHashType):
|
||||
# if the SIGHASH_ANYONECANPAY flag is not set:
|
||||
if not (nHashType & SIGHASH_ANYONECANPAY):
|
||||
|
@ -285,10 +304,11 @@ def outputs_sig_digest(tx, nHashType, txin):
|
|||
|
||||
def txin_sig_digest(tx, txin):
|
||||
digest = blake2b(digest_size=32, person=b'Zcash___TxInHash')
|
||||
digest.update(bytes(tx.vin[txin.nIn].prevout))
|
||||
digest.update(bytes(txin.scriptCode))
|
||||
digest.update(struct.pack('<Q', txin.amount))
|
||||
digest.update(struct.pack('<I', tx.vin[txin.nIn].nSequence))
|
||||
if txin is not None:
|
||||
digest.update(bytes(tx.vin[txin.nIn].prevout))
|
||||
digest.update(bytes(txin.scriptCode))
|
||||
digest.update(struct.pack('<Q', txin.amount))
|
||||
digest.update(struct.pack('<I', tx.vin[txin.nIn].nSequence))
|
||||
return digest.digest()
|
||||
|
||||
|
||||
|
@ -312,37 +332,42 @@ def main():
|
|||
txid = txid_digest(tx)
|
||||
auth = auth_digest(tx)
|
||||
|
||||
# Generate amounts and scriptCodes for each transparent input.
|
||||
t_inputs = [TransparentInput(nIn, rand) for nIn in range(len(tx.vin))]
|
||||
|
||||
# If there are any transparent inputs, derive a corresponding transparent sighash.
|
||||
if len(tx.vin) > 0:
|
||||
txin = TransparentInput(tx, rand)
|
||||
if len(t_inputs) > 0:
|
||||
txin = rand.a(t_inputs)
|
||||
else:
|
||||
txin = None
|
||||
|
||||
sighash_all = signature_digest(tx, SIGHASH_ALL, txin)
|
||||
other_sighashes = None if txin is None else [
|
||||
signature_digest(tx, nHashType, txin)
|
||||
sighash_shielded = signature_digest(tx, t_inputs, SIGHASH_ALL, None)
|
||||
other_sighashes = {
|
||||
nHashType: None if txin is None else signature_digest(tx, t_inputs, nHashType, txin)
|
||||
for nHashType in [
|
||||
SIGHASH_ALL,
|
||||
SIGHASH_NONE,
|
||||
SIGHASH_SINGLE,
|
||||
SIGHASH_ALL | SIGHASH_ANYONECANPAY,
|
||||
SIGHASH_NONE | SIGHASH_ANYONECANPAY,
|
||||
SIGHASH_SINGLE | SIGHASH_ANYONECANPAY,
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
test_vectors.append({
|
||||
'tx': bytes(tx),
|
||||
'txid': txid,
|
||||
'auth_digest': auth,
|
||||
'amounts': [x.amount for x in t_inputs],
|
||||
'script_codes': [x.scriptCode.raw() for x in t_inputs],
|
||||
'transparent_input': None if txin is None else txin.nIn,
|
||||
'script_code': None if txin is None else txin.scriptCode.raw(),
|
||||
'amount': None if txin is None else txin.amount,
|
||||
'sighash_all': sighash_all,
|
||||
'sighash_none': None if txin is None else other_sighashes[0],
|
||||
'sighash_single': None if txin is None else other_sighashes[1],
|
||||
'sighash_all_anyone': None if txin is None else other_sighashes[2],
|
||||
'sighash_none_anyone': None if txin is None else other_sighashes[3],
|
||||
'sighash_single_anyone': None if txin is None else other_sighashes[4],
|
||||
'sighash_shielded': sighash_shielded,
|
||||
'sighash_all': other_sighashes.get(SIGHASH_ALL),
|
||||
'sighash_none': other_sighashes.get(SIGHASH_NONE),
|
||||
'sighash_single': other_sighashes.get(SIGHASH_SINGLE),
|
||||
'sighash_all_anyone': other_sighashes.get(SIGHASH_ALL | SIGHASH_ANYONECANPAY),
|
||||
'sighash_none_anyone': other_sighashes.get(SIGHASH_NONE | SIGHASH_ANYONECANPAY),
|
||||
'sighash_single_anyone': other_sighashes.get(SIGHASH_SINGLE | SIGHASH_ANYONECANPAY),
|
||||
})
|
||||
|
||||
render_tv(
|
||||
|
@ -352,19 +377,20 @@ def main():
|
|||
('tx', {'rust_type': 'Vec<u8>', 'bitcoin_flavoured': False}),
|
||||
('txid', '[u8; 32]'),
|
||||
('auth_digest', '[u8; 32]'),
|
||||
('amounts', '[i64; %d]' % len(t_inputs)),
|
||||
('script_codes', {
|
||||
'rust_type': '[Vec<u8>; %d]' % len(t_inputs),
|
||||
'bitcoin_flavoured': False,
|
||||
}),
|
||||
('transparent_input', {
|
||||
'rust_type': 'Option<u32>',
|
||||
'rust_fmt': lambda x: None if x is None else Some(x),
|
||||
}),
|
||||
('script_code', {
|
||||
'rust_type': 'Option<Vec<u8>>',
|
||||
('sighash_shielded', '[u8; 32]'),
|
||||
('sighash_all', {
|
||||
'rust_type': 'Option<[u8; 32]>',
|
||||
'rust_fmt': lambda x: None if x is None else Some(x),
|
||||
}),
|
||||
('amount', {
|
||||
'rust_type': 'Option<i64>',
|
||||
'rust_fmt': lambda x: None if x is None else Some(x),
|
||||
}),
|
||||
('sighash_all', '[u8; 32]'),
|
||||
('sighash_none', {
|
||||
'rust_type': 'Option<[u8; 32]>',
|
||||
'rust_fmt': lambda x: None if x is None else Some(x),
|
||||
|
|
Loading…
Reference in New Issue