diff --git a/zip_0143.py b/zip_0143.py index 0403d84..72d6eb5 100644 --- a/zip_0143.py +++ b/zip_0143.py @@ -19,20 +19,20 @@ SIGHASH_ANYONECANPAY = 0x80 NOT_AN_INPUT = -1 # For portability of the test vectors; replaced with None for Rust -def getHashPrevouts(tx): - digest = blake2b(digest_size=32, person=b'ZcashPrevoutHash') +def getHashPrevouts(tx, person=b'ZcashPrevoutHash'): + digest = blake2b(digest_size=32, person=person) for x in tx.vin: digest.update(bytes(x.prevout)) return digest.digest() -def getHashSequence(tx): - digest = blake2b(digest_size=32, person=b'ZcashSequencHash') +def getHashSequence(tx, person=b'ZcashSequencHash'): + digest = blake2b(digest_size=32, person=person) for x in tx.vin: digest.update(struct.pack(' 0: + digest.update(getHashPrevouts(tx, b'ZTxIdPrevoutHash')) + digest.update(getHashSequence(tx, b'ZTxIdSequencHash')) + digest.update(getHashOutputs(tx, b'ZTxIdOutputsHash')) + + return digest.digest() + +# Sapling + +def sapling_digest(tx): + digest = blake2b(digest_size=32, person=b'ZTxIdSaplingHash') + + if len(tx.vSpendsSapling) + len(tx.vOutputsSapling) > 0: + digest.update(sapling_spends_digest(tx)) + digest.update(sapling_outputs_digest(tx)) + digest.update(struct.pack(' 0: + digest.update(sapling_spends_compact_digest(tx)) + digest.update(sapling_spends_noncompact_digest(tx)) + + return digest.digest() + +def sapling_spends_compact_digest(tx): + digest = blake2b(digest_size=32, person=b'ZTxIdSSpendCHash') + for desc in tx.vSpendsSapling: + digest.update(desc.nullifier) + return digest.digest() + +def sapling_spends_noncompact_digest(tx): + digest = blake2b(digest_size=32, person=b'ZTxIdSSpendNHash') + for desc in tx.vSpendsSapling: + digest.update(bytes(desc.cv)) + digest.update(bytes(desc.anchor)) + digest.update(bytes(desc.rk)) + return digest.digest() + +# - Outputs + +def sapling_outputs_digest(tx): + digest = blake2b(digest_size=32, person=b'ZTxIdSOutputHash') + + if len(tx.vOutputsSapling) > 0: + digest.update(sapling_outputs_compact_digest(tx)) + digest.update(sapling_outputs_memos_digest(tx)) + digest.update(sapling_outputs_noncompact_digest(tx)) + + return digest.digest() + +def sapling_outputs_compact_digest(tx): + digest = blake2b(digest_size=32, person=b'ZTxIdSOutC__Hash') + for desc in tx.vOutputsSapling: + digest.update(bytes(desc.cmu)) + digest.update(bytes(desc.ephemeralKey)) + digest.update(desc.encCiphertext[:52]) + return digest.digest() + +def sapling_outputs_memos_digest(tx): + digest = blake2b(digest_size=32, person=b'ZTxIdSOutM__Hash') + for desc in tx.vOutputsSapling: + digest.update(desc.encCiphertext[52:564]) + return digest.digest() + +def sapling_outputs_noncompact_digest(tx): + digest = blake2b(digest_size=32, person=b'ZTxIdSOutN__Hash') + for desc in tx.vOutputsSapling: + digest.update(bytes(desc.cv)) + digest.update(desc.encCiphertext[564:]) + digest.update(desc.outCipherText) + return digest.digest() + +# Orchard + +def orchard_digest(tx): + digest = blake2b(digest_size=32, person=b'ZTxIdOrchardHash') + + if len(tx.vActionsOrchard) > 0: + digest.update(orchard_actions_compact_digest(tx)) + digest.update(orchard_actions_memos_digest(tx)) + digest.update(orchard_actions_noncompact_digest(tx)) + digest.update(struct.pack(' 0: + txin = TransparentInput(tx, rand) + else: + txin = None + + sighash_all = signature_digest(tx, SIGHASH_ALL, txin) + other_sighashes = None if txin is None else [ + signature_digest(tx, nHashType, txin) + for nHashType in [ + SIGHASH_NONE, + SIGHASH_SINGLE, + SIGHASH_ALL | SIGHASH_ANYONECANPAY, + SIGHASH_NONE | SIGHASH_ANYONECANPAY, + SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, + ] + ] + + test_vectors.append({ + 'tx': bytes(tx), + 'txid': txid, + '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], + }) + + render_tv( + args, + 'zip_0244', + ( + ('tx', {'rust_type': 'Vec', 'bitcoin_flavoured': False}), + ('txid', '[u8; 32]'), + ('transparent_input', { + 'rust_type': 'Option', + 'rust_fmt': lambda x: None if x is None else Some(x), + }), + ('script_code', { + 'rust_type': 'Option>', + 'rust_fmt': lambda x: None if x is None else Some(x), + }), + ('amount', { + 'rust_type': 'Option', + '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), + }), + ('sighash_single', { + 'rust_type': 'Option<[u8; 32]>', + 'rust_fmt': lambda x: None if x is None else Some(x), + }), + ('sighash_all_anyone', { + 'rust_type': 'Option<[u8; 32]>', + 'rust_fmt': lambda x: None if x is None else Some(x), + }), + ('sighash_none_anyone', { + 'rust_type': 'Option<[u8; 32]>', + 'rust_fmt': lambda x: None if x is None else Some(x), + }), + ('sighash_single_anyone', { + 'rust_type': 'Option<[u8; 32]>', + 'rust_fmt': lambda x: None if x is None else Some(x), + }), + ), + test_vectors, + ) + + +if __name__ == '__main__': + main()