From e16a8fc8c934a0a6fe2604236f4c1e621fe8675b Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 12 Oct 2018 16:50:18 +0200 Subject: [PATCH] src/apps/wallet/sign_tx: refactor Zcash, add WIP ZIP243; use ensure instead of assert where possible --- src/apps/cardano/cbor.py | 3 +- src/apps/common/coininfo.py | 38 ---- src/apps/common/coininfo.py.mako | 3 - src/apps/common/writers.py | 15 +- src/apps/wallet/sign_tx/multisig.py | 6 +- src/apps/wallet/sign_tx/scripts.py | 3 +- src/apps/wallet/sign_tx/segwit_bip143.py | 4 +- src/apps/wallet/sign_tx/signing.py | 17 +- src/apps/wallet/sign_tx/writers.py | 7 +- .../{overwinter_zip143.py => zcash.py} | 86 ++++++-- src/trezor/messages/SignTx.py | 3 + src/trezor/messages/TransactionType.py | 3 + tests/test_apps.wallet.zcash.zip143.py | 184 ++++++++++++++++++ tests/test_apps.wallet.zcash.zip243.py | 184 ++++++++++++++++++ vendor/trezor-common | 2 +- 15 files changed, 471 insertions(+), 87 deletions(-) rename src/apps/wallet/sign_tx/{overwinter_zip143.py => zcash.py} (54%) create mode 100644 tests/test_apps.wallet.zcash.zip143.py create mode 100644 tests/test_apps.wallet.zcash.zip243.py diff --git a/src/apps/cardano/cbor.py b/src/apps/cardano/cbor.py index a2b592ca4d..9cf93b609e 100644 --- a/src/apps/cardano/cbor.py +++ b/src/apps/cardano/cbor.py @@ -6,6 +6,7 @@ import ustruct as struct from micropython import const from trezor import log +from trezor.utils import ensure _CBOR_TYPE_MASK = const(0xE0) _CBOR_INFO_BITS = const(0x1F) @@ -167,7 +168,7 @@ class Raw: class IndefiniteLengthArray: def __init__(self, array): - assert isinstance(array, list) + ensure(isinstance(array, list)) self.array = array diff --git a/src/apps/common/coininfo.py b/src/apps/common/coininfo.py index 631b9c87ff..a926641d41 100644 --- a/src/apps/common/coininfo.py +++ b/src/apps/common/coininfo.py @@ -22,7 +22,6 @@ class CoinInfo: segwit: bool, fork_id: int, force_bip143: bool, - version_group_id: int, bip115: bool, decred: bool, curve_name: str, @@ -42,7 +41,6 @@ class CoinInfo: self.segwit = segwit self.fork_id = fork_id self.force_bip143 = force_bip143 - self.version_group_id = version_group_id self.bip115 = bip115 self.decred = decred self.curve_name = curve_name @@ -82,7 +80,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -103,7 +100,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -124,7 +120,6 @@ COINS = [ segwit=False, fork_id=0, force_bip143=True, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -145,7 +140,6 @@ COINS = [ segwit=False, fork_id=0, force_bip143=True, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -166,7 +160,6 @@ COINS = [ segwit=True, fork_id=79, force_bip143=True, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -187,7 +180,6 @@ COINS = [ segwit=True, fork_id=79, force_bip143=True, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -208,7 +200,6 @@ COINS = [ segwit=False, fork_id=42, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -229,7 +220,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -250,7 +240,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -271,7 +260,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=True, curve_name='secp256k1-decred', @@ -292,7 +280,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=True, curve_name='secp256k1-decred', @@ -313,7 +300,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -334,7 +320,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -355,7 +340,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -376,7 +360,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -397,7 +380,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -418,7 +400,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -439,7 +420,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1-groestl', @@ -460,7 +440,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1-groestl', @@ -481,7 +460,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=0x02e7d970, bip115=False, decred=False, curve_name='secp256k1', @@ -502,7 +480,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -523,7 +500,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -544,7 +520,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -565,7 +540,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -586,7 +560,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -607,7 +580,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -628,7 +600,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -649,7 +620,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1-smart', @@ -670,7 +640,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1-smart', @@ -691,7 +660,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -712,7 +680,6 @@ COINS = [ segwit=True, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -733,7 +700,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=0x03c48270, bip115=False, decred=False, curve_name='secp256k1', @@ -754,7 +720,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=0x03c48270, bip115=False, decred=False, curve_name='secp256k1', @@ -775,7 +740,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -796,7 +760,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=False, decred=False, curve_name='secp256k1', @@ -817,7 +780,6 @@ COINS = [ segwit=False, fork_id=None, force_bip143=False, - version_group_id=None, bip115=True, decred=False, curve_name='secp256k1', diff --git a/src/apps/common/coininfo.py.mako b/src/apps/common/coininfo.py.mako index 2635a546b4..085b9b959c 100644 --- a/src/apps/common/coininfo.py.mako +++ b/src/apps/common/coininfo.py.mako @@ -22,7 +22,6 @@ class CoinInfo: segwit: bool, fork_id: int, force_bip143: bool, - version_group_id: int, bip115: bool, decred: bool, curve_name: str, @@ -42,7 +41,6 @@ class CoinInfo: self.segwit = segwit self.fork_id = fork_id self.force_bip143 = force_bip143 - self.version_group_id = version_group_id self.bip115 = bip115 self.decred = decred self.curve_name = curve_name @@ -88,7 +86,6 @@ ATTRIBUTES = ( ("segwit", bool), ("fork_id", black_repr), ("force_bip143", bool), - ("version_group_id", hexfmt), ("bip115", bool), ("decred", bool), ("curve_name", lambda r: repr(r.replace("_", "-"))), diff --git a/src/apps/common/writers.py b/src/apps/common/writers.py index 868e5e4ac4..a4ea21a580 100644 --- a/src/apps/common/writers.py +++ b/src/apps/common/writers.py @@ -1,3 +1,6 @@ +from trezor.utils import ensure + + def empty_bytearray(preallocate: int) -> bytearray: """ Returns bytearray that won't allocate for at least `preallocate` bytes. @@ -9,20 +12,20 @@ def empty_bytearray(preallocate: int) -> bytearray: def write_uint8(w: bytearray, n: int) -> int: - assert 0 <= n <= 0xFF + ensure(0 <= n <= 0xFF) w.append(n) return 1 def write_uint16_le(w: bytearray, n: int) -> int: - assert 0 <= n <= 0xFFFF + ensure(0 <= n <= 0xFFFF) w.append(n & 0xFF) w.append((n >> 8) & 0xFF) return 2 def write_uint32_le(w: bytearray, n: int) -> int: - assert 0 <= n <= 0xFFFFFFFF + ensure(0 <= n <= 0xFFFFFFFF) w.append(n & 0xFF) w.append((n >> 8) & 0xFF) w.append((n >> 16) & 0xFF) @@ -31,7 +34,7 @@ def write_uint32_le(w: bytearray, n: int) -> int: def write_uint32_be(w: bytearray, n: int) -> int: - assert 0 <= n <= 0xFFFFFFFF + ensure(0 <= n <= 0xFFFFFFFF) w.append((n >> 24) & 0xFF) w.append((n >> 16) & 0xFF) w.append((n >> 8) & 0xFF) @@ -40,7 +43,7 @@ def write_uint32_be(w: bytearray, n: int) -> int: def write_uint64_le(w: bytearray, n: int) -> int: - assert 0 <= n <= 0xFFFFFFFFFFFFFFFF + ensure(0 <= n <= 0xFFFFFFFFFFFFFFFF) w.append(n & 0xFF) w.append((n >> 8) & 0xFF) w.append((n >> 16) & 0xFF) @@ -53,7 +56,7 @@ def write_uint64_le(w: bytearray, n: int) -> int: def write_uint64_be(w: bytearray, n: int) -> int: - assert 0 <= n <= 0xFFFFFFFFFFFFFFFF + ensure(0 <= n <= 0xFFFFFFFFFFFFFFFF) w.append((n >> 56) & 0xFF) w.append((n >> 48) & 0xFF) w.append((n >> 40) & 0xFF) diff --git a/src/apps/wallet/sign_tx/multisig.py b/src/apps/wallet/sign_tx/multisig.py index 6c5275b677..4a335e661e 100644 --- a/src/apps/wallet/sign_tx/multisig.py +++ b/src/apps/wallet/sign_tx/multisig.py @@ -3,7 +3,7 @@ from trezor.crypto.hashlib import sha256 from trezor.messages import FailureType from trezor.messages.HDNodePathType import HDNodePathType from trezor.messages.MultisigRedeemScriptType import MultisigRedeemScriptType -from trezor.utils import HashWriter +from trezor.utils import HashWriter, ensure from apps.wallet.sign_tx.writers import write_bytes, write_uint32 @@ -19,7 +19,7 @@ class MultisigFingerprint: def add(self, multisig: MultisigRedeemScriptType): fp = multisig_fingerprint(multisig) - assert fp is not None + ensure(fp is not None) if self.fingerprint is None: self.fingerprint = fp elif self.fingerprint != fp: @@ -27,7 +27,7 @@ class MultisigFingerprint: def matches(self, multisig: MultisigRedeemScriptType): fp = multisig_fingerprint(multisig) - assert fp is not None + ensure(fp is not None) if self.mismatch is False and self.fingerprint == fp: return True else: diff --git a/src/apps/wallet/sign_tx/scripts.py b/src/apps/wallet/sign_tx/scripts.py index b5870d6129..6335fc82f2 100644 --- a/src/apps/wallet/sign_tx/scripts.py +++ b/src/apps/wallet/sign_tx/scripts.py @@ -1,4 +1,5 @@ from trezor.messages.MultisigRedeemScriptType import MultisigRedeemScriptType +from trezor.utils import ensure from apps.common.coininfo import CoinInfo from apps.common.writers import empty_bytearray @@ -56,7 +57,7 @@ def script_replay_protection_bip115( ) -> bytearray: if block_hash is None or block_height is None: return bytearray() - assert len(block_hash) == 32 + ensure(len(block_hash) == 32) s = bytearray(33) s[0] = 0x20 # 32 bytes for block hash s[1:33] = block_hash # block hash diff --git a/src/apps/wallet/sign_tx/segwit_bip143.py b/src/apps/wallet/sign_tx/segwit_bip143.py index 2f2ad01adc..33f6998d86 100644 --- a/src/apps/wallet/sign_tx/segwit_bip143.py +++ b/src/apps/wallet/sign_tx/segwit_bip143.py @@ -3,7 +3,7 @@ from trezor.messages import FailureType, InputScriptType from trezor.messages.SignTx import SignTx from trezor.messages.TxInputType import TxInputType from trezor.messages.TxOutputBinType import TxOutputBinType -from trezor.utils import HashWriter +from trezor.utils import HashWriter, ensure from apps.common.coininfo import CoinInfo from apps.wallet.sign_tx.multisig import multisig_get_pubkeys @@ -58,7 +58,7 @@ class Bip143: ) -> bytes: h_preimage = HashWriter(sha256) - assert not tx.overwintered + ensure(not tx.overwintered) write_uint32(h_preimage, tx.version) # nVersion write_bytes(h_preimage, bytearray(self.get_prevouts_hash(coin))) # hashPrevouts diff --git a/src/apps/wallet/sign_tx/signing.py b/src/apps/wallet/sign_tx/signing.py index 5d56a6a7ec..563281d9c8 100644 --- a/src/apps/wallet/sign_tx/signing.py +++ b/src/apps/wallet/sign_tx/signing.py @@ -21,15 +21,16 @@ from apps.wallet.sign_tx.decred_prefix_hasher import ( ) from apps.wallet.sign_tx.helpers import * from apps.wallet.sign_tx.multisig import * -from apps.wallet.sign_tx.overwinter_zip143 import ( # noqa:F401 - OVERWINTERED, - Zip143, - Zip143Error, -) from apps.wallet.sign_tx.scripts import * from apps.wallet.sign_tx.segwit_bip143 import Bip143, Bip143Error # noqa:F401 from apps.wallet.sign_tx.tx_weight_calculator import * from apps.wallet.sign_tx.writers import * +from apps.wallet.sign_tx.zcash import ( # noqa:F401 + OVERWINTERED, + ZcashError, + Zip143, + Zip243, +) # the number of bip32 levels used in a wallet (chain and address) _BIP32_WALLET_DEPTH = const(2) @@ -379,7 +380,7 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode): write_uint32( h_sign, tx.version | OVERWINTERED ) # nVersion | fOverwintered - write_uint32(h_sign, coin.version_group_id) # nVersionGroupId + write_uint32(h_sign, tx.version_group_id) # nVersionGroupId else: write_uint32(h_sign, tx.version) # nVersion @@ -560,7 +561,7 @@ async def get_prevtx_output_value( if tx.overwintered: write_uint32(txh, tx.version | OVERWINTERED) # nVersion | fOverwintered - write_uint32(txh, coin.version_group_id) # nVersionGroupId + write_uint32(txh, tx.version_group_id) # nVersionGroupId elif coin.decred: write_uint32(txh, tx.version | DECRED_SERIALIZE_NO_WITNESS) else: @@ -629,7 +630,7 @@ def get_tx_header(coin: CoinInfo, tx: SignTx, segwit: bool = False): w_txi = bytearray() if tx.overwintered: write_uint32(w_txi, tx.version | OVERWINTERED) # nVersion | fOverwintered - write_uint32(w_txi, coin.version_group_id) # nVersionGroupId + write_uint32(w_txi, tx.version_group_id) # nVersionGroupId else: write_uint32(w_txi, tx.version) # nVersion if segwit: diff --git a/src/apps/wallet/sign_tx/writers.py b/src/apps/wallet/sign_tx/writers.py index 99541b84c2..0d03334be9 100644 --- a/src/apps/wallet/sign_tx/writers.py +++ b/src/apps/wallet/sign_tx/writers.py @@ -1,6 +1,7 @@ from trezor.crypto.hashlib import sha256 from trezor.messages.TxInputType import TxInputType from trezor.messages.TxOutputBinType import TxOutputBinType +from trezor.utils import ensure from apps.common.writers import ( write_bytes, @@ -59,7 +60,7 @@ def write_tx_output(w, o: TxOutputBinType): def write_op_push(w, n: int): - assert n >= 0 and n <= 0xFFFFFFFF + ensure(n >= 0 and n <= 0xFFFFFFFF) if n < 0x4C: w.append(n & 0xFF) elif n < 0xFF: @@ -78,7 +79,7 @@ def write_op_push(w, n: int): def write_varint(w, n: int): - assert n >= 0 and n <= 0xFFFFFFFF + ensure(n >= 0 and n <= 0xFFFFFFFF) if n < 253: w.append(n & 0xFF) elif n < 0x10000: @@ -94,7 +95,7 @@ def write_varint(w, n: int): def write_scriptnum(w, n: int): - assert n >= 0 and n <= 0xFFFFFFFF + ensure(n >= 0 and n <= 0xFFFFFFFF) if n < 0x100: w.append(1) w.append(n & 0xFF) diff --git a/src/apps/wallet/sign_tx/overwinter_zip143.py b/src/apps/wallet/sign_tx/zcash.py similarity index 54% rename from src/apps/wallet/sign_tx/overwinter_zip143.py rename to src/apps/wallet/sign_tx/zcash.py index 387f46ac3c..519399cf0c 100644 --- a/src/apps/wallet/sign_tx/overwinter_zip143.py +++ b/src/apps/wallet/sign_tx/zcash.py @@ -5,7 +5,7 @@ from trezor.messages import FailureType, InputScriptType from trezor.messages.SignTx import SignTx from trezor.messages.TxInputType import TxInputType from trezor.messages.TxOutputBinType import TxOutputBinType -from trezor.utils import HashWriter +from trezor.utils import HashWriter, ensure from apps.common.coininfo import CoinInfo from apps.wallet.sign_tx.multisig import multisig_get_pubkeys @@ -23,10 +23,27 @@ from apps.wallet.sign_tx.writers import ( OVERWINTERED = const(0x80000000) -class Zip143Error(ValueError): +class ZcashError(ValueError): pass +def derive_script_code(txi: TxInputType, pubkeyhash: bytes) -> bytearray: + + if txi.multisig: + return output_script_multisig( + multisig_get_pubkeys(txi.multisig), txi.multisig.m + ) + + p2pkh = txi.script_type == InputScriptType.SPENDADDRESS + if p2pkh: + return output_script_p2pkh(pubkeyhash) + + else: + raise ZcashError( + FailureType.DataError, "Unknown input script type for zip143 script code" + ) + + class Zip143: def __init__(self): self.h_prevouts = HashWriter(blake2b, outlen=32, personal=b"ZcashPrevoutHash") @@ -62,14 +79,15 @@ class Zip143: ) -> bytes: h_preimage = HashWriter( blake2b, outlen=32, personal=b"ZcashSigHash\x19\x1b\xa8\x5b" - ) # BRANCH_ID = 0x5ba81b19 + ) # BRANCH_ID = 0x5ba81b19 / Overwinter - assert tx.overwintered + ensure(tx.overwintered) + ensure(tx.version == 3) write_uint32( h_preimage, tx.version | OVERWINTERED ) # 1. nVersion | fOverwintered - write_uint32(h_preimage, coin.version_group_id) # 2. nVersionGroupId + write_uint32(h_preimage, tx.version_group_id) # 2. nVersionGroupId write_bytes(h_preimage, bytearray(self.get_prevouts_hash())) # 3. hashPrevouts write_bytes(h_preimage, bytearray(self.get_sequence_hash())) # 4. hashSequence write_bytes(h_preimage, bytearray(self.get_outputs_hash())) # 5. hashOutputs @@ -81,7 +99,7 @@ class Zip143: write_bytes_reversed(h_preimage, txi.prev_hash) # 10a. outpoint write_uint32(h_preimage, txi.prev_index) - script_code = self.derive_script_code(txi, pubkeyhash) # 10b. scriptCode + script_code = derive_script_code(txi, pubkeyhash) # 10b. scriptCode write_varint(h_preimage, len(script_code)) write_bytes(h_preimage, script_code) @@ -91,21 +109,47 @@ class Zip143: return get_tx_hash(h_preimage) - # see https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#specification - # item 5 for details - def derive_script_code(self, txi: TxInputType, pubkeyhash: bytes) -> bytearray: - if txi.multisig: - return output_script_multisig( - multisig_get_pubkeys(txi.multisig), txi.multisig.m - ) +class Zip243(Zip143): + def __init__(self): + super().__init__() - p2pkh = txi.script_type == InputScriptType.SPENDADDRESS - if p2pkh: - return output_script_p2pkh(pubkeyhash) + def preimage_hash( + self, + coin: CoinInfo, + tx: SignTx, + txi: TxInputType, + pubkeyhash: bytes, + sighash: int, + ) -> bytes: + h_preimage = HashWriter( + blake2b, outlen=32, personal=b"ZcashSigHash\xbb\x09\xb8\x76" + ) # BRANCH_ID = 0x76b809bb / Sapling - else: - raise Zip143Error( - FailureType.DataError, - "Unknown input script type for zip143 script code", - ) + ensure(tx.overwintered) + ensure(tx.version == 4) + + write_uint32( + h_preimage, tx.version | OVERWINTERED + ) # 1. nVersion | fOverwintered + write_uint32(h_preimage, tx.version_group_id) # 2. nVersionGroupId + write_bytes(h_preimage, bytearray(self.get_prevouts_hash())) # 3. hashPrevouts + write_bytes(h_preimage, bytearray(self.get_sequence_hash())) # 4. hashSequence + write_bytes(h_preimage, bytearray(self.get_outputs_hash())) # 5. hashOutputs + write_bytes(h_preimage, b"\x00" * 32) # 6. hashJoinSplits + write_uint32(h_preimage, tx.lock_time) # 7. nLockTime + write_uint32(h_preimage, tx.expiry) # 8. expiryHeight + write_uint32(h_preimage, sighash) # 9. nHashType + + write_bytes_reversed(h_preimage, txi.prev_hash) # 10a. outpoint + write_uint32(h_preimage, txi.prev_index) + + script_code = derive_script_code(txi, pubkeyhash) # 10b. scriptCode + write_varint(h_preimage, len(script_code)) + write_bytes(h_preimage, script_code) + + write_uint64(h_preimage, txi.amount) # 10c. value + + write_uint32(h_preimage, txi.sequence) # 10d. nSequence + + return get_tx_hash(h_preimage) diff --git a/src/trezor/messages/SignTx.py b/src/trezor/messages/SignTx.py index 4e801017bb..4b6a4ebe6b 100644 --- a/src/trezor/messages/SignTx.py +++ b/src/trezor/messages/SignTx.py @@ -15,6 +15,7 @@ class SignTx(p.MessageType): lock_time: int = None, expiry: int = None, overwintered: bool = None, + version_group_id: int = None, ) -> None: self.outputs_count = outputs_count self.inputs_count = inputs_count @@ -23,6 +24,7 @@ class SignTx(p.MessageType): self.lock_time = lock_time self.expiry = expiry self.overwintered = overwintered + self.version_group_id = version_group_id @classmethod def get_fields(cls): @@ -34,4 +36,5 @@ class SignTx(p.MessageType): 5: ('lock_time', p.UVarintType, 0), # default=0 6: ('expiry', p.UVarintType, 0), 7: ('overwintered', p.BoolType, 0), + 8: ('version_group_id', p.UVarintType, 0), } diff --git a/src/trezor/messages/TransactionType.py b/src/trezor/messages/TransactionType.py index 5defbc5048..61b9fdb449 100644 --- a/src/trezor/messages/TransactionType.py +++ b/src/trezor/messages/TransactionType.py @@ -28,6 +28,7 @@ class TransactionType(p.MessageType): extra_data_len: int = None, expiry: int = None, overwintered: bool = None, + version_group_id: int = None, ) -> None: self.version = version self.inputs = inputs if inputs is not None else [] @@ -40,6 +41,7 @@ class TransactionType(p.MessageType): self.extra_data_len = extra_data_len self.expiry = expiry self.overwintered = overwintered + self.version_group_id = version_group_id @classmethod def get_fields(cls): @@ -55,4 +57,5 @@ class TransactionType(p.MessageType): 9: ('extra_data_len', p.UVarintType, 0), 10: ('expiry', p.UVarintType, 0), 11: ('overwintered', p.BoolType, 0), + 12: ('version_group_id', p.UVarintType, 0), } diff --git a/tests/test_apps.wallet.zcash.zip143.py b/tests/test_apps.wallet.zcash.zip143.py new file mode 100644 index 0000000000..3c55320a35 --- /dev/null +++ b/tests/test_apps.wallet.zcash.zip143.py @@ -0,0 +1,184 @@ +from common import * +from trezor.messages import InputScriptType +from trezor.messages.SignTx import SignTx +from trezor.messages.TxInputType import TxInputType +from trezor.messages.TxOutputBinType import TxOutputBinType + +from apps.common import coins +from apps.wallet.sign_tx.zcash import Zip143 + + +# test vectors inspired from https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/zip_0143.py +class TestZcashZip143(unittest.TestCase): + + VECTORS = [ + { + "expiry": 71895707, + "inputs": [ + { + "amount": 35268204, + "prevout": [ + "702c35a67cd7364d3fab552fb349e35c15c50250453fd18f7b855992632e2c76", + 4025613248, + ], + "pubkeyhash": "4d9cafb657677f2321fc538e367767dbdf551539", + "script_type": InputScriptType.SPENDADDRESS, + "sequence": 1999822371, + } + ], + "lock_time": 452079490, + "outputs": [ + {"script_pubkey": "06535251635252", "amount": 1246336469307855} + ], + "version": 3, + "version_group_id": 0x3C48270, + "hash_type": 1, + "prevouts_hash": b"bd4318eecf841a0cf01c2be532cf4bc3303e881e2aface159f1882f153152688", + "sequence_hash": b"9ac6a31952ff626bf5a0a30d3d8ac63a0d4298d33d7bc38854bfa5860695e30a", + "outputs_hash": b"d0cadf116b4441f5e1e17814908dee509ec262a79f3c88f7f3389e8200658992", + "preimage_hash": b"287146efaa30b3c1c7bd1a72308c46205712e6944780fdc5a7f91f477fddd55b", + }, + { + "expiry": 231041495, + "inputs": [ + { + "amount": 39263472, + "prevout": [ + "76647d2be4c2cd6b3d17d6870971d7a098baf72c6f6f1214cf1faae488bd7de2", + 1547817817, + ], + "pubkeyhash": "9f5d230603ce57a0e8b31a8c9b6c983ad5b00cd5", + "script_type": InputScriptType.SPENDADDRESS, + "sequence": 3973122135, + }, + { + "amount": 57533728, + "prevout": [ + "cccc0df65a04943ad5cbc13f295f000fe056c40b2d88f27dc34cfeb803be3483", + 3053054889, + ], + "pubkeyhash": "b5e71ef1df5ed3a2589607ca58ed19634f07fb4f", + "script_type": InputScriptType.SPENDADDRESS, + "sequence": 3932380530, + }, + ], + "lock_time": 3087412294, + "outputs": [ + {"script_pubkey": "03ac6552", "amount": 546412698509744}, + {"script_pubkey": "00", "amount": 166856241017532}, + ], + "version": 3, + "version_group_id": 0x3C48270, + "hash_type": 1, + "prevouts_hash": b"8e286c6c0dde3119271c9c1398ef46614b0253c502b00a3691cec2e9047da35b", + "sequence_hash": b"58477fd9ecd5faf3e08159e0ab5fdaab66cab364d081498ddcef41de0af3624e", + "outputs_hash": b"c518797fc6f2c08fc22aa3f66122047b360e1db4df5c3feb28573c00cdf45fa1", + "preimage_hash": b"b71358954aca8ffe1d3f1b5707ed6c082ed2b35e35e293b897bfc12ae7c85396", + }, + { + "expiry": 186996458, + "inputs": [ + { + "amount": 14267260, + "prevout": [ + "6c6fae359f645c276891c0dcab3faf187700c082dc477740fb3f2cd7bb59fb35", + 1290359941, + ], + "script_type": InputScriptType.SPENDADDRESS, + "pubkeyhash": "0873d1baed6c8696e4bd2b26755692b4d8050086", + "sequence": 1230917966, + } + ], + "lock_time": 1520002857, + "outputs": [], + "version": 3, + "version_group_id": 0x3C48270, + "hash_type": 1, + "prevouts_hash": b"445bc6328cd33b3c86259953dd674bded341ff1e1104dc21856919e9761036dd", + "sequence_hash": b"42e1d5c2636f165afaa954afa6d7a50779eb145e947bf668f1a40dd771c711fc", + "outputs_hash": b"869eda84eecf7257f9979a4848bbf52f4969a5736594ab7ba41452e7bb906824", + "preimage_hash": b"19ea4ce2467cb5cdcc4562ce81b22006f1fb55189ea93ccc7a3bc78ff5d97a93", + }, + { + "expiry": 254788522, + "inputs": [ + { + "amount": 36100600, + "prevout": [ + "e818f9057c5abaaa2e5c15b94945cd424c28a5fa385dadfe4907b274d842707d", + 1517971891, + ], + "script_type": InputScriptType.SPENDADDRESS, + "pubkeyhash": "80b6c35a9b2efd77dbfd5d5d3bb1fd03ff40f182", + "sequence": 3833577708, + }, + { + "amount": 71238918, + "prevout": [ + "7350d1014670212efe81fb7c73e8450df814ef6232f7490f63ccf07480f884a6", + 687648622, + ], + "script_type": InputScriptType.SPENDADDRESS, + "pubkeyhash": "fb8c27a442afe0f0b39a3875823c4893fe8f8550", + "sequence": 4190617831, + }, + ], + "lock_time": 1557067344, + "outputs": [ + {"script_pubkey": "076a53655151516a", "amount": 470086065540185} + ], + "version": 3, + "version_group_id": 0x3C48270, + "hash_type": 1, + "prevouts_hash": b"509abdfafcc75265037f1ce6a4658ac9ecadd7b82378c3fbaeb48ab437ff6898", + "sequence_hash": b"2b13f671cd1a9aa04c1e250eef74a316d7d2b049360d20604514ddc2dfacfd23", + "outputs_hash": b"4f01b8785e80779290aa86c16b24952f9b7f8bc09da44e68f760ab1920ab8f2a", + "preimage_hash": b"d0b92233e185a69f320f18bfacff7e5dcc5f0ed02af2f3ecf59359853e105e59", + }, + ] + + def test_zip143(self): + coin = coins.by_name("Zcash") + + for v in self.VECTORS: + tx = SignTx( + coin_name="Zcash", + inputs_count=len(v["inputs"]), + outputs_count=len(v["outputs"]), + version=v["version"], + lock_time=v["lock_time"], + expiry=v["expiry"], + overwintered=(v["version"] >= 3), + version_group_id=v["version_group_id"], + ) + zip143 = Zip143() + for i in v["inputs"]: + txi = TxInputType() + txi.amount = i["amount"] + txi.prev_hash = unhexlify(i["prevout"][0]) + txi.prev_index = i["prevout"][1] + txi.script_type = i["script_type"] + txi.sequence = i["sequence"] + zip143.add_prevouts(txi) + zip143.add_sequence(txi) + for o in v["outputs"]: + txo = TxOutputBinType() + txo.amount = o["amount"] + txo.script_pubkey = unhexlify(o["script_pubkey"]) + zip143.add_output(txo) + + self.assertEqual(hexlify(zip143.get_prevouts_hash()), v["prevouts_hash"]) + self.assertEqual(hexlify(zip143.get_sequence_hash()), v["sequence_hash"]) + self.assertEqual(hexlify(zip143.get_outputs_hash()), v["outputs_hash"]) + self.assertEqual( + hexlify( + zip143.preimage_hash( + coin, tx, txi, unhexlify(i["pubkeyhash"]), v["hash_type"] + ) + ), + v["preimage_hash"], + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_apps.wallet.zcash.zip243.py b/tests/test_apps.wallet.zcash.zip243.py new file mode 100644 index 0000000000..3ab513c1ac --- /dev/null +++ b/tests/test_apps.wallet.zcash.zip243.py @@ -0,0 +1,184 @@ +from common import * +from trezor.messages import InputScriptType +from trezor.messages.SignTx import SignTx +from trezor.messages.TxInputType import TxInputType +from trezor.messages.TxOutputBinType import TxOutputBinType + +from apps.common import coins +from apps.wallet.sign_tx.zcash import Zip243 + + +# test vectors inspired from https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/zip_0243.py +class TestZcashZip243(unittest.TestCase): + + VECTORS = [ + { + "expiry": 71895707, + "inputs": [ + { + "amount": 35268204, + "prevout": [ + "702c35a67cd7364d3fab552fb349e35c15c50250453fd18f7b855992632e2c76", + 4025613248, + ], + "pubkeyhash": "4d9cafb657677f2321fc538e367767dbdf551539", + "script_type": InputScriptType.SPENDADDRESS, + "sequence": 1999822371, + } + ], + "lock_time": 452079490, + "outputs": [ + {"script_pubkey": "06535251635252", "amount": 1246336469307855} + ], + "version": 4, + "version_group_id": 0x892F2085, + "hash_type": 1, + "prevouts_hash": b"bd4318eecf841a0cf01c2be532cf4bc3303e881e2aface159f1882f153152688", + "sequence_hash": b"9ac6a31952ff626bf5a0a30d3d8ac63a0d4298d33d7bc38854bfa5860695e30a", + "outputs_hash": b"d0cadf116b4441f5e1e17814908dee509ec262a79f3c88f7f3389e8200658992", + "preimage_hash": b"80b3cdfbefc9cea6818e148b5af6133dfac7d9ff8435b256ef1958b906b3647f", + }, + { + "expiry": 231041495, + "inputs": [ + { + "amount": 39263472, + "prevout": [ + "76647d2be4c2cd6b3d17d6870971d7a098baf72c6f6f1214cf1faae488bd7de2", + 1547817817, + ], + "pubkeyhash": "9f5d230603ce57a0e8b31a8c9b6c983ad5b00cd5", + "script_type": InputScriptType.SPENDADDRESS, + "sequence": 3973122135, + }, + { + "amount": 57533728, + "prevout": [ + "cccc0df65a04943ad5cbc13f295f000fe056c40b2d88f27dc34cfeb803be3483", + 3053054889, + ], + "pubkeyhash": "b5e71ef1df5ed3a2589607ca58ed19634f07fb4f", + "script_type": InputScriptType.SPENDADDRESS, + "sequence": 3932380530, + }, + ], + "lock_time": 3087412294, + "outputs": [ + {"script_pubkey": "03ac6552", "amount": 546412698509744}, + {"script_pubkey": "00", "amount": 166856241017532}, + ], + "version": 4, + "version_group_id": 0x892F2085, + "hash_type": 1, + "prevouts_hash": b"8e286c6c0dde3119271c9c1398ef46614b0253c502b00a3691cec2e9047da35b", + "sequence_hash": b"58477fd9ecd5faf3e08159e0ab5fdaab66cab364d081498ddcef41de0af3624e", + "outputs_hash": b"c518797fc6f2c08fc22aa3f66122047b360e1db4df5c3feb28573c00cdf45fa1", + "preimage_hash": b"25769ee5a943f0e1fd165c2b049de2732ea88f059658f63932f12b16087337fd", + }, + { + "expiry": 186996458, + "inputs": [ + { + "amount": 14267260, + "prevout": [ + "6c6fae359f645c276891c0dcab3faf187700c082dc477740fb3f2cd7bb59fb35", + 1290359941, + ], + "script_type": InputScriptType.SPENDADDRESS, + "pubkeyhash": "0873d1baed6c8696e4bd2b26755692b4d8050086", + "sequence": 1230917966, + } + ], + "lock_time": 1520002857, + "outputs": [], + "version": 4, + "version_group_id": 0x892F2085, + "hash_type": 1, + "prevouts_hash": b"445bc6328cd33b3c86259953dd674bded341ff1e1104dc21856919e9761036dd", + "sequence_hash": b"42e1d5c2636f165afaa954afa6d7a50779eb145e947bf668f1a40dd771c711fc", + "outputs_hash": b"869eda84eecf7257f9979a4848bbf52f4969a5736594ab7ba41452e7bb906824", + "preimage_hash": b"e2d5a735f8f4c3f6e382405a214daa520a7f80200c177d8daafe1c0344db9dd3", + }, + { + "expiry": 254788522, + "inputs": [ + { + "amount": 36100600, + "prevout": [ + "e818f9057c5abaaa2e5c15b94945cd424c28a5fa385dadfe4907b274d842707d", + 1517971891, + ], + "script_type": InputScriptType.SPENDADDRESS, + "pubkeyhash": "80b6c35a9b2efd77dbfd5d5d3bb1fd03ff40f182", + "sequence": 3833577708, + }, + { + "amount": 71238918, + "prevout": [ + "7350d1014670212efe81fb7c73e8450df814ef6232f7490f63ccf07480f884a6", + 687648622, + ], + "script_type": InputScriptType.SPENDADDRESS, + "pubkeyhash": "fb8c27a442afe0f0b39a3875823c4893fe8f8550", + "sequence": 4190617831, + }, + ], + "lock_time": 1557067344, + "outputs": [ + {"script_pubkey": "076a53655151516a", "amount": 470086065540185} + ], + "version": 4, + "version_group_id": 0x892F2085, + "hash_type": 1, + "prevouts_hash": b"509abdfafcc75265037f1ce6a4658ac9ecadd7b82378c3fbaeb48ab437ff6898", + "sequence_hash": b"2b13f671cd1a9aa04c1e250eef74a316d7d2b049360d20604514ddc2dfacfd23", + "outputs_hash": b"4f01b8785e80779290aa86c16b24952f9b7f8bc09da44e68f760ab1920ab8f2a", + "preimage_hash": b"74908bfb31d1ce68f1b82c12e96fb7799669293d92da84a689a1dd1f9ed74d8d", + }, + ] + + def test_zip243(self): + coin = coins.by_name("Zcash") + + for v in self.VECTORS: + tx = SignTx( + coin_name="Zcash", + inputs_count=len(v["inputs"]), + outputs_count=len(v["outputs"]), + version=v["version"], + lock_time=v["lock_time"], + expiry=v["expiry"], + overwintered=(v["version"] >= 3), + version_group_id=v["version_group_id"], + ) + zip243 = Zip243() + for i in v["inputs"]: + txi = TxInputType() + txi.amount = i["amount"] + txi.prev_hash = unhexlify(i["prevout"][0]) + txi.prev_index = i["prevout"][1] + txi.script_type = i["script_type"] + txi.sequence = i["sequence"] + zip243.add_prevouts(txi) + zip243.add_sequence(txi) + for o in v["outputs"]: + txo = TxOutputBinType() + txo.amount = o["amount"] + txo.script_pubkey = unhexlify(o["script_pubkey"]) + zip243.add_output(txo) + + self.assertEqual(hexlify(zip243.get_prevouts_hash()), v["prevouts_hash"]) + self.assertEqual(hexlify(zip243.get_sequence_hash()), v["sequence_hash"]) + self.assertEqual(hexlify(zip243.get_outputs_hash()), v["outputs_hash"]) + self.assertEqual( + hexlify( + zip243.preimage_hash( + coin, tx, txi, unhexlify(i["pubkeyhash"]), v["hash_type"] + ) + ), + v["preimage_hash"], + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/vendor/trezor-common b/vendor/trezor-common index 41e4a84b5b..ea0262266d 160000 --- a/vendor/trezor-common +++ b/vendor/trezor-common @@ -1 +1 @@ -Subproject commit 41e4a84b5b01d03e980f84fab29c8f0b0ec948f5 +Subproject commit ea0262266d9c42315617e9abbcf0b539c373ebc6