import ustruct as struct from micropython import const from typing import TYPE_CHECKING from trezor import wire from trezor.crypto.hashlib import blake2b from trezor.messages import PrevTx, SignTx, TxInput, TxOutput from trezor.utils import HashWriter, ensure from apps.common.coininfo import CoinInfo from apps.common.keychain import Keychain from apps.common.writers import write_bitcoin_varint from ..scripts import write_bip143_script_code_prefixed from ..writers import ( TX_HASH_SIZE, get_tx_hash, write_bytes_fixed, write_bytes_reversed, write_tx_output, write_uint32, write_uint64, ) from . import approvers, helpers from .bitcoinlike import Bitcoinlike if TYPE_CHECKING: from typing import Sequence from apps.common import coininfo from .sig_hasher import SigHasher from .tx_info import OriginalTxInfo, TxInfo from ..common import SigHashType from ..writers import Writer OVERWINTERED = const(0x8000_0000) class ZcashSigHasher: def __init__(self) -> None: self.h_prevouts = HashWriter(blake2b(outlen=32, personal=b"ZcashPrevoutHash")) self.h_sequence = HashWriter(blake2b(outlen=32, personal=b"ZcashSequencHash")) self.h_outputs = HashWriter(blake2b(outlen=32, personal=b"ZcashOutputsHash")) def add_input(self, txi: TxInput, script_pubkey: bytes) -> None: write_bytes_reversed(self.h_prevouts, txi.prev_hash, TX_HASH_SIZE) write_uint32(self.h_prevouts, txi.prev_index) write_uint32(self.h_sequence, txi.sequence) def add_output(self, txo: TxOutput, script_pubkey: bytes) -> None: write_tx_output(self.h_outputs, txo, script_pubkey) def hash143( self, txi: TxInput, public_keys: Sequence[bytes | memoryview], threshold: int, tx: SignTx | PrevTx, coin: coininfo.CoinInfo, hash_type: int, ) -> bytes: h_preimage = HashWriter( blake2b( outlen=32, personal=b"ZcashSigHash" + struct.pack(" bytes: raise NotImplementedError class Zcashlike(Bitcoinlike): def __init__( self, tx: SignTx, keychain: Keychain, coin: CoinInfo, approver: approvers.Approver | None, ) -> None: ensure(coin.overwintered) super().__init__(tx, keychain, coin, approver) if tx.version != 4: raise wire.DataError("Unsupported transaction version.") def create_sig_hasher(self) -> SigHasher: return ZcashSigHasher() async def step7_finish(self) -> None: self.write_tx_footer(self.serialized_tx, self.tx_info.tx) write_uint64(self.serialized_tx, 0) # valueBalance write_bitcoin_varint(self.serialized_tx, 0) # nShieldedSpend write_bitcoin_varint(self.serialized_tx, 0) # nShieldedOutput write_bitcoin_varint(self.serialized_tx, 0) # nJoinSplit await helpers.request_tx_finish(self.tx_req) async def sign_nonsegwit_input(self, i_sign: int) -> None: await self.sign_nonsegwit_bip143_input(i_sign) async def get_tx_digest( self, i: int, txi: TxInput, tx_info: TxInfo | OriginalTxInfo, public_keys: Sequence[bytes | memoryview], threshold: int, script_pubkey: bytes, tx_hash: bytes | None = None, ) -> bytes: return tx_info.sig_hasher.hash143( txi, public_keys, threshold, tx_info.tx, self.coin, self.get_sighash_type(txi), ) def write_tx_header( self, w: Writer, tx: SignTx | PrevTx, witness_marker: bool ) -> None: if tx.version < 3: # pre-overwinter write_uint32(w, tx.version) else: if tx.version_group_id is None: raise wire.DataError("Version group ID is missing") # nVersion | fOverwintered write_uint32(w, tx.version | OVERWINTERED) write_uint32(w, tx.version_group_id) # nVersionGroupId def write_tx_footer(self, w: Writer, tx: SignTx | PrevTx) -> None: assert tx.expiry is not None # checked in sanitize_* write_uint32(w, tx.lock_time) if tx.version >= 3: write_uint32(w, tx.expiry) # expiryHeight