1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-22 20:42:03 +00:00

fixup! feat(core): add Zcash shielded transactions

This commit is contained in:
Tomas Krnak 2022-11-17 20:55:24 +07:00
parent 8bddb42785
commit 8384dc6e58
3 changed files with 24 additions and 50 deletions

View File

@ -402,6 +402,15 @@ def sanitize_sign_tx(tx: SignTx, coin: CoinInfo) -> SignTx:
if tx.branch_id is not None: if tx.branch_id is not None:
raise wire.DataError("Branch ID not enabled on this coin.") raise wire.DataError("Branch ID not enabled on this coin.")
if tx.orchard_params is not None:
if not utils.ZCASH_SHIELDED:
raise wire.DataError("Shielded transaction disabled.")
elif tx.orchard_params.inputs_count + tx.orchard_params.outputs_count == 0:
raise wire.DataError("Orchard bundle must not be empty.")
elif tx.orchard_params.inputs_count > 0 and tx.inputs_count > 0:
raise wire.DataError(
"Spending transparent and Orchard inputs simultaneously is not supported."
)
return tx return tx

View File

@ -35,21 +35,6 @@ FLAGS = const(0b0000_0011) # spends enbled and output enabled
MAX_SILENT_ORCHARD_INPUTS = const(8) MAX_SILENT_ORCHARD_INPUTS = const(8)
def skip_if_empty(func):
"""
A function decorated by this will not be evaluated,
if the Orchard bundle is impty.
"""
async def wrapper(self):
if self.actions_count == 0:
return
else:
await func(self)
return wrapper
class OrchardSigner: class OrchardSigner:
def __init__( def __init__(
self, self,
@ -59,26 +44,13 @@ class OrchardSigner:
coin: CoinInfo, coin: CoinInfo,
tx_req: TxRequest, tx_req: TxRequest,
) -> None: ) -> None:
assert tx_req.serialized is not None # typing
params = tx_info.tx.orchard_params params = tx_info.tx.orchard_params
if params is None: assert params is not None # checked in sanitize_sign_tx
self.inputs_count = 0 self.actions_count = max(
self.outputs_count = 0 2, # minimal required amount of actions
else: self.inputs_count,
self.inputs_count = params.inputs_count self.outputs_count,
self.outputs_count = params.outputs_count )
if self.inputs_count + self.outputs_count > 0:
self.actions_count = max(
2, # minimal required amount of actions
self.inputs_count,
self.outputs_count,
)
else:
self.actions_count = 0
if self.actions_count == 0:
return # no need to initialize other attributes
self.tx_info = tx_info self.tx_info = tx_info
self.keychain = OrchardKeychain.from_seed_and_coin(seed, coin) self.keychain = OrchardKeychain.from_seed_and_coin(seed, coin)
@ -87,7 +59,6 @@ class OrchardSigner:
self.tx_req = tx_req self.tx_req = tx_req
assert isinstance(tx_info.sig_hasher, ZcashHasher) assert isinstance(tx_info.sig_hasher, ZcashHasher)
self.sig_hasher: ZcashHasher = tx_info.sig_hasher self.sig_hasher: ZcashHasher = tx_info.sig_hasher
assert params is not None
self.anchor = params.anchor self.anchor = params.anchor
self.key_node = self.keychain.derive(params.address_n) self.key_node = self.keychain.derive(params.address_n)
@ -99,7 +70,6 @@ class OrchardSigner:
self.rng = None self.rng = None
@skip_if_empty
async def process_inputs(self) -> None: async def process_inputs(self) -> None:
await self.check_orchard_inputs_count() await self.check_orchard_inputs_count()
for i in range(self.inputs_count): for i in range(self.inputs_count):
@ -111,7 +81,6 @@ class OrchardSigner:
if self.inputs_count > MAX_SILENT_ORCHARD_INPUTS: if self.inputs_count > MAX_SILENT_ORCHARD_INPUTS:
yield ConfirmOrchardInputsCountOverThreshold(self.inputs_count) yield ConfirmOrchardInputsCountOverThreshold(self.inputs_count)
@skip_if_empty
async def approve_outputs(self) -> None: async def approve_outputs(self) -> None:
for i in range(self.outputs_count): for i in range(self.outputs_count):
txo = await self.get_output(i) txo = await self.get_output(i)
@ -121,7 +90,6 @@ class OrchardSigner:
else: else:
await self.approver.add_orchard_external_output(txo) await self.approver.add_orchard_external_output(txo)
@skip_if_empty
async def compute_digest(self) -> None: async def compute_digest(self) -> None:
# derive shielding seed # derive shielding seed
shielding_seed = self.derive_shielding_seed() shielding_seed = self.derive_shielding_seed()
@ -233,7 +201,6 @@ class OrchardSigner:
ovk = fvk.outgoing_viewing_key() ovk = fvk.outgoing_viewing_key()
return builder.OutputInfo(ovk, address, txo.amount, txo.memo) return builder.OutputInfo(ovk, address, txo.amount, txo.memo)
@skip_if_empty
@watch_gc_async @watch_gc_async
async def sign_inputs(self) -> None: async def sign_inputs(self) -> None:
sighash = self.sig_hasher.signature_digest() sighash = self.sig_hasher.signature_digest()

View File

@ -55,7 +55,7 @@ class Zcash(Bitcoinlike):
super().__init__(tx, keychain, coin, approver) super().__init__(tx, keychain, coin, approver)
if ZCASH_SHIELDED: if ZCASH_SHIELDED and tx.orchard_params is not None:
self.orchard = OrchardSigner( self.orchard = OrchardSigner(
self.tx_info, self.tx_info,
keychain.seed, keychain.seed,
@ -63,10 +63,8 @@ class Zcash(Bitcoinlike):
coin, coin,
self.tx_req, self.tx_req,
) )
if self.orchard.inputs_count > 0 and tx.inputs_count > 0: else:
raise DataError( self.orchard = None
"Cannot spending transparent and Orchard inputs simultaneously is not supported."
)
def create_sig_hasher(self, tx: SignTx | PrevTx) -> ZcashHasher: def create_sig_hasher(self, tx: SignTx | PrevTx) -> ZcashHasher:
return ZcashHasher(tx) return ZcashHasher(tx)
@ -78,12 +76,12 @@ class Zcash(Bitcoinlike):
async def step1_process_inputs(self): async def step1_process_inputs(self):
await super().step1_process_inputs() await super().step1_process_inputs()
if ZCASH_SHIELDED: if ZCASH_SHIELDED and self.orchard is not None:
await self.orchard.process_inputs() await self.orchard.process_inputs()
async def step2_approve_outputs(self): async def step2_approve_outputs(self):
await super().step2_approve_outputs() await super().step2_approve_outputs()
if ZCASH_SHIELDED: if ZCASH_SHIELDED and self.orchard is not None:
await self.orchard.approve_outputs() await self.orchard.approve_outputs()
async def step3_verify_inputs(self) -> None: async def step3_verify_inputs(self) -> None:
@ -97,7 +95,7 @@ class Zcash(Bitcoinlike):
self.taproot_only = False # turn off taproot behavior self.taproot_only = False # turn off taproot behavior
async def step4_serialize_inputs(self): async def step4_serialize_inputs(self):
if ZCASH_SHIELDED: if ZCASH_SHIELDED and self.orchard is not None:
# shield actions first to get a sighash # shield actions first to get a sighash
await self.orchard.compute_digest() await self.orchard.compute_digest()
await super().step4_serialize_inputs() await super().step4_serialize_inputs()
@ -111,14 +109,14 @@ class Zcash(Bitcoinlike):
write_compact_size(self.serialized_tx, 0) # nOutputsSapling write_compact_size(self.serialized_tx, 0) # nOutputsSapling
# nActionsOrchard # nActionsOrchard
if ZCASH_SHIELDED: if ZCASH_SHIELDED and self.orchard is not None::
write_compact_size(self.serialized_tx, self.orchard.actions_count) write_compact_size(self.serialized_tx, self.orchard.actions_count)
else: else:
write_compact_size(self.serialized_tx, 0) write_compact_size(self.serialized_tx, 0)
async def step6_sign_segwit_inputs(self): async def step6_sign_segwit_inputs(self):
# transparent inputs were signed in step 4 # transparent inputs were signed in step 4
if ZCASH_SHIELDED: if ZCASH_SHIELDED and self.orchard is not None:
await self.orchard.sign_inputs() await self.orchard.sign_inputs()
async def sign_nonsegwit_input(self, i_sign: int) -> None: async def sign_nonsegwit_input(self, i_sign: int) -> None:
@ -186,5 +184,5 @@ class Zcash(Bitcoinlike):
def set_serialized_signature(self, i: int, signature: bytes) -> None: def set_serialized_signature(self, i: int, signature: bytes) -> None:
super().set_serialized_signature(i, signature) super().set_serialized_signature(i, signature)
assert self.tx_req.serialized is not None assert self.tx_req.serialized is not None
if ZCASH_SHIELDED: if ZCASH_SHIELDED and self.orchard is not None:
self.tx_req.serialized.signature_type = ZcashSignatureType.TRANSPARENT self.tx_req.serialized.signature_type = ZcashSignatureType.TRANSPARENT