diff --git a/tv_output.py b/tv_output.py index 304e50d..552fccd 100644 --- a/tv_output.py +++ b/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' 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) diff --git a/zip_0244.py b/zip_0244.py index 82411a9..1159e32 100755 --- a/zip_0244.py +++ b/zip_0244.py @@ -221,38 +221,55 @@ 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(' 0: + digest.update(hash_type(tx, nHashType, txin)) + 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() +def hash_type(tx, nHashType, txin): + if txin is None: + # Sapling Spend or Orchard Action + assert nHashType == SIGHASH_ALL + else: + # Transparent input + assert nHashType in [ + SIGHASH_ALL, + SIGHASH_NONE, + SIGHASH_SINGLE, + SIGHASH_ALL | SIGHASH_ANYONECANPAY, + SIGHASH_NONE | SIGHASH_ANYONECANPAY, + SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, + ] + assert (nHashType & 0x1f) != SIGHASH_SINGLE or 0 <= txin.nIn and txin.nIn < len(tx.vout) + return struct.pack('B', nHashType) + def prevouts_sig_digest(tx, nHashType): # If the SIGHASH_ANYONECANPAY flag is not set: if not (nHashType & SIGHASH_ANYONECANPAY): @@ -260,14 +277,29 @@ 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(' 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) - for nHashType in [ + 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, - ] - ] + ] if txin is None or txin.nIn < len(tx.vout) else [ + SIGHASH_ALL, + SIGHASH_NONE, + SIGHASH_ALL | SIGHASH_ANYONECANPAY, + SIGHASH_NONE | 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( @@ -357,19 +400,20 @@ def main(): ('tx', {'rust_type': 'Vec', 'bitcoin_flavoured': False}), ('txid', '[u8; 32]'), ('auth_digest', '[u8; 32]'), + ('amounts', '[i64; %d]' % len(t_inputs)), + ('script_codes', { + 'rust_type': '[Vec; %d]' % len(t_inputs), + 'bitcoin_flavoured': False, + }), ('transparent_input', { 'rust_type': 'Option', 'rust_fmt': lambda x: None if x is None else Some(x), }), - ('script_code', { - 'rust_type': 'Option>', + ('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', - '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),