diff --git a/common/protob/messages-bitcoin.proto b/common/protob/messages-bitcoin.proto index 9303baabf7..18ce579731 100644 --- a/common/protob/messages-bitcoin.proto +++ b/common/protob/messages-bitcoin.proto @@ -198,11 +198,7 @@ message SignTx { optional uint32 branch_id = 10; // only for Zcash, BRANCH_ID optional AmountUnit amount_unit = 11 [default=BITCOIN]; // show amounts in optional bool decred_staking_ticket = 12 [default=false]; // only for Decred, this is signing a ticket purchase - - optional uint32 orchard_inputs_count = 13 [default = 0]; // only for Zcash, number of Orchard inputs - optional uint32 orchard_outputs_count = 14 [default = 0]; // only for Zcash, number of Orchard outputs - optional bytes orchard_anchor = 15; // only for Zcash, a root of Orchard Merkle tree - optional uint32 account = 16 [default = 0]; // only for Zcash + optional zcash.ZcashOrchardParams orchard_params = 13; // only for Zcash } /** diff --git a/common/protob/messages-zcash.proto b/common/protob/messages-zcash.proto index 0c182d4126..c0526a47a2 100644 --- a/common/protob/messages-zcash.proto +++ b/common/protob/messages-zcash.proto @@ -62,6 +62,17 @@ message ZcashAddress { optional string address = 1; } +/** + * Orchard signing params + * @embed + */ +message ZcashOrchardParams { + required uint32 inputs_count = 1; + required uint32 outputs_count = 2; + required bytes anchor = 3; + repeated uint32 address_n = 4; +} + /** * Request: Specify transaction Orchard input. * @next TxRequest diff --git a/core/src/apps/zcash/orchard/signer.py b/core/src/apps/zcash/orchard/signer.py index 62a84b8dd6..035717e913 100644 --- a/core/src/apps/zcash/orchard/signer.py +++ b/core/src/apps/zcash/orchard/signer.py @@ -9,7 +9,6 @@ from trezor.messages import TxRequest, ZcashAck, ZcashOrchardInput, ZcashOrchard from trezor.wire import DataError from apps.bitcoin.sign_tx import helpers -from apps.common.paths import HARDENED from .. import unified from ..hasher import ZcashHasher @@ -61,9 +60,13 @@ class OrchardSigner: tx_req: TxRequest, ) -> None: assert tx_req.serialized is not None # typing - - self.inputs_count = tx_info.tx.orchard_inputs_count - self.outputs_count = tx_info.tx.orchard_outputs_count + params = tx_info.tx.orchard_params + if params is None: + self.inputs_count = 0 + self.outputs_count = 0 + else: + self.inputs_count = params.inputs_count + self.outputs_count = params.outputs_count if self.inputs_count + self.outputs_count > 0: self.actions_count = max( @@ -84,15 +87,9 @@ class OrchardSigner: self.tx_req = tx_req assert isinstance(tx_info.sig_hasher, ZcashHasher) self.sig_hasher: ZcashHasher = tx_info.sig_hasher - - account = tx_info.tx.account - assert account is not None # typing - key_path = [ - 32 | HARDENED, # ZIP-32 constant - coin.slip44 | HARDENED, # purpose - account | HARDENED, # account - ] - self.key_node = self.keychain.derive(key_path) + assert params is not None + self.anchor = params.anchor + self.key_node = self.keychain.derive(params.address_n) self.msg_acc = MessageAccumulator( self.keychain.derive_slip21( @@ -175,15 +172,13 @@ class OrchardSigner: self.msg_acc.check() # hash orchard footer - assert self.tx_info.tx.orchard_anchor is not None # typing self.sig_hasher.orchard.finalize( flags=FLAGS, value_balance=self.approver.orchard_balance, - anchor=self.tx_info.tx.orchard_anchor, + anchor=self.anchor, ) def derive_shielding_seed(self) -> bytes: - assert self.tx_info.tx.orchard_anchor is not None # typing ss_slip21 = self.keychain.derive_slip21( [b"Zcash Orchard", b"bundle_shielding_seed"], ).key() @@ -191,7 +186,7 @@ class OrchardSigner: ss_hasher.update(self.sig_hasher.header.digest()) ss_hasher.update(self.sig_hasher.transparent.digest()) ss_hasher.update(self.msg_acc.state) - ss_hasher.update(self.tx_info.tx.orchard_anchor) + ss_hasher.update(self.anchor) ss_hasher.update(ss_slip21) return ss_hasher.digest() diff --git a/core/src/apps/zcash/signer.py b/core/src/apps/zcash/signer.py index abdc6072fd..0d4a3ef7f8 100644 --- a/core/src/apps/zcash/signer.py +++ b/core/src/apps/zcash/signer.py @@ -63,11 +63,10 @@ class Zcash(Bitcoinlike): coin, self.tx_req, ) - self.tx_info.wallet_path.attribute = [ - 44 | HARDENED, # BIP-44 constant - coin.slip44 | HARDENED, - tx.account | HARDENED, - ] + if self.orchard.inputs_count > 0 and tx.inputs_count > 0: + raise DataError( + "Cannot spending transparent and Orchard inputs simultaneously is not supported." + ) def create_sig_hasher(self, tx: SignTx | PrevTx) -> ZcashHasher: return ZcashHasher(tx) diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 4da1c9f419..4039d8ff3c 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -455,6 +455,26 @@ if TYPE_CHECKING: def is_type_of(cls, msg: Any) -> TypeGuard["ZcashAddress"]: return isinstance(msg, cls) + class ZcashOrchardParams(protobuf.MessageType): + inputs_count: "int" + outputs_count: "int" + anchor: "bytes" + address_n: "list[int]" + + def __init__( + self, + *, + inputs_count: "int", + outputs_count: "int", + anchor: "bytes", + address_n: "list[int] | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["ZcashOrchardParams"]: + return isinstance(msg, cls) + class ZcashOrchardInput(protobuf.MessageType): recipient: "bytes" value: "int" @@ -707,10 +727,7 @@ if TYPE_CHECKING: branch_id: "int | None" amount_unit: "AmountUnit" decred_staking_ticket: "bool" - orchard_inputs_count: "int" - orchard_outputs_count: "int" - orchard_anchor: "bytes | None" - account: "int" + orchard_params: "ZcashOrchardParams | None" def __init__( self, @@ -726,10 +743,7 @@ if TYPE_CHECKING: branch_id: "int | None" = None, amount_unit: "AmountUnit | None" = None, decred_staking_ticket: "bool | None" = None, - orchard_inputs_count: "int | None" = None, - orchard_outputs_count: "int | None" = None, - orchard_anchor: "bytes | None" = None, - account: "int | None" = None, + orchard_params: "ZcashOrchardParams | None" = None, ) -> None: pass diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index b58320b0e0..2378be4b81 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -1021,6 +1021,29 @@ class ZcashAddress(protobuf.MessageType): self.address = address +class ZcashOrchardParams(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("inputs_count", "uint32", repeated=False, required=True), + 2: protobuf.Field("outputs_count", "uint32", repeated=False, required=True), + 3: protobuf.Field("anchor", "bytes", repeated=False, required=True), + 4: protobuf.Field("address_n", "uint32", repeated=True, required=False), + } + + def __init__( + self, + *, + inputs_count: "int", + outputs_count: "int", + anchor: "bytes", + address_n: Optional[Sequence["int"]] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.inputs_count = inputs_count + self.outputs_count = outputs_count + self.anchor = anchor + + class ZcashOrchardInput(protobuf.MessageType): MESSAGE_WIRE_TYPE = 906 FIELDS = { @@ -1307,10 +1330,7 @@ class SignTx(protobuf.MessageType): 10: protobuf.Field("branch_id", "uint32", repeated=False, required=False), 11: protobuf.Field("amount_unit", "AmountUnit", repeated=False, required=False), 12: protobuf.Field("decred_staking_ticket", "bool", repeated=False, required=False), - 13: protobuf.Field("orchard_inputs_count", "uint32", repeated=False, required=False), - 14: protobuf.Field("orchard_outputs_count", "uint32", repeated=False, required=False), - 15: protobuf.Field("orchard_anchor", "bytes", repeated=False, required=False), - 16: protobuf.Field("account", "uint32", repeated=False, required=False), + 13: protobuf.Field("orchard_params", "ZcashOrchardParams", repeated=False, required=False), } def __init__( @@ -1328,10 +1348,7 @@ class SignTx(protobuf.MessageType): branch_id: Optional["int"] = None, amount_unit: Optional["AmountUnit"] = AmountUnit.BITCOIN, decred_staking_ticket: Optional["bool"] = False, - orchard_inputs_count: Optional["int"] = 0, - orchard_outputs_count: Optional["int"] = 0, - orchard_anchor: Optional["bytes"] = None, - account: Optional["int"] = 0, + orchard_params: Optional["ZcashOrchardParams"] = None, ) -> None: self.outputs_count = outputs_count self.inputs_count = inputs_count @@ -1345,10 +1362,7 @@ class SignTx(protobuf.MessageType): self.branch_id = branch_id self.amount_unit = amount_unit self.decred_staking_ticket = decred_staking_ticket - self.orchard_inputs_count = orchard_inputs_count - self.orchard_outputs_count = orchard_outputs_count - self.orchard_anchor = orchard_anchor - self.account = account + self.orchard_params = orchard_params class TxRequest(protobuf.MessageType):