Skip to content

Commit

Permalink
feat(core): add Zcash shielded transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
krnak committed Nov 17, 2022
1 parent 6311675 commit ac73bfa
Show file tree
Hide file tree
Showing 64 changed files with 3,789 additions and 158 deletions.
16 changes: 15 additions & 1 deletion common/protob/messages-bitcoin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ option (include_in_bitcoin_only) = true;

import "messages.proto";
import "messages-common.proto";
import "messages-zcash.proto";

/**
* Type of script which will be used for transaction input
Expand Down Expand Up @@ -197,6 +198,11 @@ 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
}

/**
Expand All @@ -211,6 +217,8 @@ message SignTx {
* @next TxAckPrevOutput
* @next TxAckPrevExtraData
* @next TxAckPaymentRequest
* @next ZcashOrchardInput
* @next ZcashOrchardOutput
*/
message TxRequest {
optional RequestType request_type = 1; // what should be filled in TxAck message?
Expand All @@ -228,6 +236,9 @@ message TxRequest {
TXORIGINPUT = 5;
TXORIGOUTPUT = 6;
TXPAYMENTREQ = 7;
TXORCHARDOUTPUT = 8;
TXORCHARDINPUT = 9;
NO_OP = 10;
}
/**
* Structure representing request details
Expand All @@ -245,6 +256,10 @@ message TxRequest {
optional uint32 signature_index = 1; // 'signature' field contains signed input of this index
optional bytes signature = 2; // signature of the signature_index input
optional bytes serialized_tx = 3; // part of serialized and signed transaction

optional zcash.ZcashSignatureType signature_type = 4; // for Zcash only
optional bytes zcash_shielding_seed = 5; // for Zcash only
optional bytes tx_sighash = 6; // for Zcash only
}
}

Expand Down Expand Up @@ -607,4 +622,3 @@ message AuthorizeCoinJoin {
optional InputScriptType script_type = 7 [default=SPENDADDRESS]; // used to distinguish between various address formats (non-segwit, segwit, etc.)
optional AmountUnit amount_unit = 8 [default=BITCOIN]; // show amounts in
}

89 changes: 89 additions & 0 deletions common/protob/messages-zcash.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
syntax = "proto2";
package hw.trezor.messages.zcash;

// Sugar for easier handling in Java
option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorMessageZcash";

import "messages.proto";

enum ZcashSignatureType {
reserved 1, 2, 4;
TRANSPARENT = 0;
// SAPLING_SPEND_AUTH = 1;
// SAPLING_BINDING = 2;
ORCHARD_SPEND_AUTH = 3;
// ORCHARD_BINDING = 4;
}

/**
* Request: Ask device for Orchard Viewing Key.
* If field `full` is true, then Full Viewing Key will be returned.
* Otherwise Incoming Viewing Key will be returned.
*
* @start
* @next Failure
* @next ZcashViewingKey
*/
message ZcashGetViewingKey {
optional string coin_name = 1 [default = "Zcash"];
repeated uint32 z_address_n = 2; // z-address ZIP 32 path
optional bool full = 3 [default = true]; // true -> Full Viewing Key requested
// false -> Incoming Viewing Key requested
}

/**
* Response: Contains unified Full/Incoming Viewing Key.
* @end
*/
message ZcashViewingKey {
required string key = 1;
}

/**
* Request: Ask device for a Unified Address.
* @start
* @next Failure
* @next ZcashAddress
*/
message ZcashGetAddress {
optional string coin_name = 1 [default = "Zcash"];
repeated uint32 t_address_n = 2; // t-address BIP 32 path (P2PKH)
repeated uint32 z_address_n = 3; // z-address ZIP 32 path (Orchard)
optional uint64 diversifier_index = 4 [default = 0]; // z-address diversifier index
optional bool show_display = 5 [default = false]; // Optionally show on display before sending the result
}

/**
* Response: Contains Zcash diversified payment address derived from device private seed
* @end
*/
message ZcashAddress {
optional string address = 1;
}

/**
* Request: Specify transaction Orchard input.
* @next TxRequest
*/
message ZcashOrchardInput {
required bytes recipient = 1;
required uint64 value = 2;
required bytes rho = 3;
required bytes rseed = 4;
}

/**
* Request: Specify transaction Orchard output.
* Let the `address` and `memo` fields empty for change outputs.
* @next TxRequest
*/
message ZcashOrchardOutput {
optional string address = 1; // for outgoing transfers
required uint64 amount = 2;
optional string memo = 3; // an optional message for a recepient
}

message ZcashAck {
option (wire_type) = 22;
}
8 changes: 8 additions & 0 deletions common/protob/messages.proto
Original file line number Diff line number Diff line change
Expand Up @@ -357,4 +357,12 @@ enum MessageType {
MessageType_WebAuthnCredentials = 801 [(wire_out) = true];
MessageType_WebAuthnAddResidentCredential = 802 [(wire_in) = true];
MessageType_WebAuthnRemoveResidentCredential = 803 [(wire_in) = true];

// Zcash
MessageType_ZcashGetAddress = 900 [(wire_in) = true];
MessageType_ZcashAddress = 901 [(wire_out) = true];
MessageType_ZcashGetViewingKey = 902 [(wire_in) = true];
MessageType_ZcashViewingKey = 903 [(wire_out) = true];
MessageType_ZcashOrchardInput = 906 [(wire_in) = true];
MessageType_ZcashOrchardOutput = 907 [(wire_in) = true];
}
1 change: 1 addition & 0 deletions core/.changelog.d/2472.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add Zcash shielded transactions
19 changes: 15 additions & 4 deletions core/SConscript.firmware
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T')
UI2 = ARGUMENTS.get('UI2', '0') == '1' or TREZOR_MODEL in ('1', 'R')

FEATURE_FLAGS = {
"RDI": True,
"SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot)
"RDI": False,
"SECP256K1_ZKP": False, # required for trezor.crypto.curve.bip340 (BIP340/Taproot)
"SYSTEM_VIEW": False,
"ZCASH_SHIELDED": False,
}
Expand Down Expand Up @@ -674,7 +674,12 @@ if FROZEN:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/tezos/*.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Tezos*.py'))

SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/*.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/*.py',
exclude=[
SOURCE_PY_DIR + 'apps/zcash/get_address.py',
SOURCE_PY_DIR + 'apps/zcash/get_viewing_key.py',
])
)

SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/webauthn/*.py'))

Expand All @@ -683,7 +688,13 @@ if FROZEN:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/zcash_v4.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Zcash*.py'))

source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY, zcash_shielded=FEATURE_FLAGS['ZCASH_SHIELDED'])
if FEATURE_FLAGS["ZCASH_SHIELDED"]:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/orchard/*.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/orchard/*/*.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/get_address.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/get_viewing_key.py'))

source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY)

source_mpyc = env.FrozenCFile(
target='frozen_mpy.c', source=source_mpy, qstr_header=qstr_preprocessed)
Expand Down
15 changes: 13 additions & 2 deletions core/SConscript.unix
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,12 @@ if FROZEN:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/tezos/*.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Tezos*.py'))

SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/*.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/*.py',
exclude=[
SOURCE_PY_DIR + 'apps/zcash/get_address.py',
SOURCE_PY_DIR + 'apps/zcash/get_viewing_key.py',
])
)

SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/webauthn/*.py'))

Expand All @@ -639,7 +644,13 @@ if FROZEN:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/zcash_v4.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Zcash*.py'))

source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY, zcash_shielded=FEATURE_FLAGS['ZCASH_SHIELDED'])
if FEATURE_FLAGS["ZCASH_SHIELDED"]:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/orchard/*.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/orchard/*/*.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/get_address.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/zcash/get_viewing_key.py'))

source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY)

source_mpyc = env.FrozenCFile(
target='frozen_mpy.c', source=source_mpy, qstr_header=qstr_preprocessed)
Expand Down
1 change: 0 additions & 1 deletion core/embed/firmware/memory_T.ld
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ SECTIONS {
build/firmware/vendor/secp256k1-zkp/src/precomputed_ecmult_gen.o(.rodata*);
. = ALIGN(512);
} >FLASH2 AT>FLASH2

.flash : ALIGN(512) {
KEEP(*(.vector_table));
. = ALIGN(4);
Expand Down
4 changes: 2 additions & 2 deletions core/embed/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions core/embed/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ default_features = false

[dependencies.pasta_curves]
optional = true
version = "0.4.0"
version = "0.4.1"
default-features = false
features = ["uninline-portable"]

# Build dependencies

Expand All @@ -88,4 +89,4 @@ path = "./blake2b_hal"

[patch.crates-io.pasta_curves]
git = "https://github.com/jarys/pasta_curves"
rev = "a4f755013aad344982383c9f5af362697d928325"
rev = "e11dfe4089d0da24483094a99cceb89f32974c17"
9 changes: 3 additions & 6 deletions core/embed/rust/src/zcash_primitives/pallas/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,14 @@ mod scalar;
#[no_mangle]
pub static mp_module_trezorpallas: Module = obj_module! {
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorpallas.to_obj(),
/// # https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
/// def to_base(x: bytes) -> Fp:
/// ...
/// """https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents"""
Qstr::MP_QSTR_to_base => obj_fn_1!(fp::to_base).as_obj(),
/// # https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
/// def to_scalar(x: bytes) -> Scalar:
/// ...
/// """https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents"""
Qstr::MP_QSTR_to_scalar => obj_fn_1!(scalar::to_scalar).as_obj(),
/// # https://zips.z.cash/protocol/protocol.pdf#concretegrouphashpallasandvesta
/// def group_hash(domain: str, message: bytes) -> Point:
/// ...
/// """https://zips.z.cash/protocol/protocol.pdf#concretegrouphashpallasandvesta"""
Qstr::MP_QSTR_group_hash => obj_fn_2!(point::group_hash).as_obj(),
/// def scalar_from_i64(x: int) -> Scalar:
/// """Converts integer to Scalar."""
Expand Down
13 changes: 10 additions & 3 deletions core/embed/rust/src/zcash_primitives/pallas/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,16 @@ unsafe extern "C" fn scalar_binary_op(op: ffi::mp_binary_op_t, this: Obj, other:
let this = this.deref().inner();
match op {
ffi::mp_binary_op_t_MP_BINARY_OP_MULTIPLY => {
let other = Gc::<Wrapped<Point>>::try_from(other)?;
let other = other.deref().inner();
(other * this).wrap()
let point = Gc::<Wrapped<Point>>::try_from(other);
if point.is_ok() {
let point = point.unwrap();
let point = point.deref().inner();
(point * this).wrap()
} else {
let scalar = Gc::<Wrapped<Scalar>>::try_from(other)?;
let scalar = scalar.deref().inner();
(this * scalar).wrap()
}
}
ffi::mp_binary_op_t_MP_BINARY_OP_ADD => {
let other = Gc::<Wrapped<Scalar>>::try_from(other)?;
Expand Down
13 changes: 5 additions & 8 deletions core/mocks/generated/trezorpallas.pyi
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
from typing import *
# https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents


# rust/src/zcash_primitives/pallas/mod.rs
def to_base(x: bytes) -> Fp:
...
# https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
"""https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents"""


# rust/src/zcash_primitives/pallas/mod.rs
def to_scalar(x: bytes) -> Scalar:
...
# https://zips.z.cash/protocol/protocol.pdf#concretegrouphashpallasandvesta
"""https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents"""


# rust/src/zcash_primitives/pallas/mod.rs
def group_hash(domain: str, message: bytes) -> Point:
...
"""https://zips.z.cash/protocol/protocol.pdf#concretegrouphashpallasandvesta"""


# rust/src/zcash_primitives/pallas/mod.rs
Expand All @@ -40,14 +37,14 @@ class Scalar:
...
def to_bytes(self) -> bytes:
...
def is_not_zero(self) -> bool:
...
def __mul__(self, other: Point) -> Point:
...
def __add__(self, other: Scalar) -> Scalar:
...
def __neg__(self) -> Point:
...
def __bool__(self) -> bool:
...


# rust/src/zcash_primitives/pallas/mod.rs
Expand Down
Loading

0 comments on commit ac73bfa

Please sign in to comment.