From 164be3ac8fb210324e53678dbe1acbc2ac5b3343 Mon Sep 17 00:00:00 2001 From: grdddj Date: Fri, 16 Sep 2022 18:48:52 +0200 Subject: [PATCH] chore(core): decrease tezos size by 860 bytes --- core/src/apps/tezos/get_address.py | 21 +- core/src/apps/tezos/get_public_key.py | 15 +- core/src/apps/tezos/helpers.py | 21 +- core/src/apps/tezos/layout.py | 117 ++++---- core/src/apps/tezos/sign_tx.py | 403 ++++++++++++-------------- core/tests/test_apps.tezos.encode.py | 11 +- 6 files changed, 285 insertions(+), 303 deletions(-) diff --git a/core/src/apps/tezos/get_address.py b/core/src/apps/tezos/get_address.py index fcfd16507..d5d146736 100644 --- a/core/src/apps/tezos/get_address.py +++ b/core/src/apps/tezos/get_address.py @@ -1,16 +1,11 @@ from typing import TYPE_CHECKING -from trezor.crypto import hashlib -from trezor.messages import TezosAddress -from trezor.ui.layouts import show_address - -from apps.common import paths, seed from apps.common.keychain import with_slip44_keychain -from . import CURVE, PATTERNS, SLIP44_ID, helpers +from . import CURVE, PATTERNS, SLIP44_ID if TYPE_CHECKING: - from trezor.messages import TezosGetAddress + from trezor.messages import TezosGetAddress, TezosAddress from apps.common.keychain import Keychain from trezor.wire import Context @@ -19,18 +14,22 @@ if TYPE_CHECKING: async def get_address( ctx: Context, msg: TezosGetAddress, keychain: Keychain ) -> TezosAddress: + from trezor.crypto import hashlib + from trezor.messages import TezosAddress + from trezor.ui.layouts import show_address + from apps.common import paths, seed + from . import helpers + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pk = seed.remove_ed25519_prefix(node.public_key()) pkh = hashlib.blake2b(pk, outlen=helpers.PUBLIC_KEY_HASH_SIZE).digest() - address = helpers.base58_encode_check( - pkh, prefix=helpers.TEZOS_ED25519_ADDRESS_PREFIX - ) + address = helpers.base58_encode_check(pkh, helpers.TEZOS_ED25519_ADDRESS_PREFIX) if msg.show_display: title = paths.address_n_to_str(msg.address_n) - await show_address(ctx, address=address, title=title) + await show_address(ctx, address, title=title) return TezosAddress(address=address) diff --git a/core/src/apps/tezos/get_public_key.py b/core/src/apps/tezos/get_public_key.py index 077ae9c66..62fc04aa2 100644 --- a/core/src/apps/tezos/get_public_key.py +++ b/core/src/apps/tezos/get_public_key.py @@ -1,15 +1,11 @@ from typing import TYPE_CHECKING -from trezor.messages import TezosPublicKey -from trezor.ui.layouts import show_pubkey - -from apps.common import paths, seed from apps.common.keychain import with_slip44_keychain -from . import CURVE, PATTERNS, SLIP44_ID, helpers +from . import CURVE, PATTERNS, SLIP44_ID if TYPE_CHECKING: - from trezor.messages import TezosGetPublicKey + from trezor.messages import TezosGetPublicKey, TezosPublicKey from apps.common.keychain import Keychain from trezor.wire import Context @@ -18,11 +14,16 @@ if TYPE_CHECKING: async def get_public_key( ctx: Context, msg: TezosGetPublicKey, keychain: Keychain ) -> TezosPublicKey: + from trezor.messages import TezosPublicKey + from trezor.ui.layouts import show_pubkey + from apps.common import paths, seed + from . import helpers + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pk = seed.remove_ed25519_prefix(node.public_key()) - pk_prefixed = helpers.base58_encode_check(pk, prefix=helpers.TEZOS_PUBLICKEY_PREFIX) + pk_prefixed = helpers.base58_encode_check(pk, helpers.TEZOS_PUBLICKEY_PREFIX) if msg.show_display: await show_pubkey(ctx, pk_prefixed) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index 62d1f78cc..501069794 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -1,12 +1,10 @@ from micropython import const from typing import TYPE_CHECKING -from trezor import wire -from trezor.crypto import base58 from trezor.utils import BufferReader, ensure +from trezor.wire import DataError from apps.common.readers import read_uint32_be -from apps.common.writers import write_bytes_unchecked, write_uint8 if TYPE_CHECKING: from trezor.utils import Writer @@ -83,6 +81,8 @@ _EP_TAG_NAMED = const(255) def base58_encode_check(payload: bytes, prefix: str | None = None) -> str: + from trezor.crypto import base58 + result = payload if prefix is not None: result = TEZOS_PREFIX_BYTES[prefix] + payload @@ -90,13 +90,14 @@ def base58_encode_check(payload: bytes, prefix: str | None = None) -> str: def write_bool(w: Writer, boolean: bool) -> None: - if boolean: - write_uint8(w, 255) - else: - write_uint8(w, 0) + from apps.common.writers import write_uint8 + + write_uint8(w, 255 if boolean else 0) def write_instruction(w: Writer, instruction: str) -> None: + from apps.common.writers import write_bytes_unchecked + write_bytes_unchecked(w, MICHELSON_INSTRUCTION_BYTES[instruction]) @@ -108,7 +109,7 @@ def check_script_size(script: bytes) -> None: n = read_uint32_be(r) ensure(r.remaining_count() == n) except (AssertionError, EOFError): - raise wire.DataError("Invalid script") + raise DataError("Invalid script") def check_tx_params_size(params: bytes) -> None: @@ -119,8 +120,8 @@ def check_tx_params_size(params: bytes) -> None: n = r.get() r.read(n) elif tag > 4: - raise wire.DataError("Unknown entrypoint tag") + raise DataError("Unknown entrypoint tag") n = read_uint32_be(r) ensure(r.remaining_count() == n) except (AssertionError, EOFError): - raise wire.DataError("Invalid transaction parameters") + raise DataError("Invalid transaction parameters") diff --git a/core/src/apps/tezos/layout.py b/core/src/apps/tezos/layout.py index 39ca4cce6..ca193331a 100644 --- a/core/src/apps/tezos/layout.py +++ b/core/src/apps/tezos/layout.py @@ -2,39 +2,38 @@ from typing import TYPE_CHECKING from trezor import ui from trezor.enums import ButtonRequestType -from trezor.strings import format_amount -from trezor.ui.layouts import ( - confirm_address, - confirm_metadata, - confirm_output, - confirm_properties, - confirm_total, -) - -from .helpers import TEZOS_AMOUNT_DECIMALS +from trezor.ui.layouts import confirm_address, confirm_metadata, confirm_properties if TYPE_CHECKING: from trezor.wire import Context +BR_SIGN_TX = ButtonRequestType.SignTx # global_import_cache +BLUE = ui.BLUE # global_import_cache + + async def require_confirm_tx(ctx: Context, to: str, value: int) -> None: + from trezor.ui.layouts import confirm_output + await confirm_output( ctx, to, format_tezos_amount(value), - font_amount=ui.BOLD, + ui.BOLD, to_str="\nto\n", width=18, - br_code=ButtonRequestType.SignTx, + br_code=BR_SIGN_TX, ) async def require_confirm_fee(ctx: Context, value: int, fee: int) -> None: + from trezor.ui.layouts import confirm_total + await confirm_total( ctx, - total_amount=format_tezos_amount(value), + format_tezos_amount(value), + format_tezos_amount(fee), total_label="Amount:\n", - fee_amount=format_tezos_amount(fee), fee_label="\nFee:\n", ) @@ -42,25 +41,25 @@ async def require_confirm_fee(ctx: Context, value: int, fee: int) -> None: async def require_confirm_origination(ctx: Context, address: str) -> None: await confirm_address( ctx, - title="Confirm origination", - address=address, - description="Address:", - br_type="confirm_origination", + "Confirm origination", + address, + "Address:", + "confirm_origination", + BR_SIGN_TX, icon_color=ui.ORANGE, - br_code=ButtonRequestType.SignTx, ) async def require_confirm_origination_fee(ctx: Context, balance: int, fee: int) -> None: await confirm_properties( ctx, - title="Confirm origination", - props=( + "confirm_origination_final", + "Confirm origination", + ( ("Balance:", format_tezos_amount(balance)), ("Fee:", format_tezos_amount(fee)), ), icon_color=ui.ORANGE, - br_type="confirm_origination_final", hold=True, ) @@ -68,12 +67,12 @@ async def require_confirm_origination_fee(ctx: Context, balance: int, fee: int) async def require_confirm_delegation_baker(ctx: Context, baker: str) -> None: await confirm_address( ctx, - title="Confirm delegation", - address=baker, - description="Baker address:", - br_type="confirm_delegation", - icon_color=ui.BLUE, - br_code=ButtonRequestType.SignTx, + "Confirm delegation", + baker, + "Baker address:", + "confirm_delegation", + BR_SIGN_TX, + icon_color=BLUE, ) @@ -81,13 +80,13 @@ async def require_confirm_set_delegate(ctx: Context, fee: int) -> None: await confirm_metadata( ctx, "confirm_delegation_final", - title="Confirm delegation", - content="Fee:\n{}", - param=format_tezos_amount(fee), - hold=True, + "Confirm delegation", + "Fee:\n{}", + format_tezos_amount(fee), + BR_SIGN_TX, hide_continue=True, - icon_color=ui.BLUE, - br_code=ButtonRequestType.SignTx, + hold=True, + icon_color=BLUE, ) @@ -97,17 +96,20 @@ async def require_confirm_register_delegate( await confirm_properties( ctx, "confirm_register_delegate", - title="Register delegate", - props=( + "Register delegate", + ( ("Fee:", format_tezos_amount(fee)), ("Address:", address), ), - icon_color=ui.BLUE, - br_code=ButtonRequestType.SignTx, + icon_color=BLUE, + br_code=BR_SIGN_TX, ) def format_tezos_amount(value: int) -> str: + from trezor.strings import format_amount + from .helpers import TEZOS_AMOUNT_DECIMALS + formatted_value = format_amount(value, TEZOS_AMOUNT_DECIMALS) return formatted_value + " XTZ" @@ -116,31 +118,24 @@ async def require_confirm_ballot(ctx: Context, proposal: str, ballot: str) -> No await confirm_properties( ctx, "confirm_ballot", - title="Submit ballot", - props=( + "Submit ballot", + ( ("Ballot:", ballot), ("Proposal:", proposal), ), icon_color=ui.PURPLE, - br_code=ButtonRequestType.SignTx, + br_code=BR_SIGN_TX, ) async def require_confirm_proposals(ctx: Context, proposals: list[str]) -> None: - if len(proposals) > 1: - title = "Submit proposals" - else: - title = "Submit proposal" - await confirm_properties( ctx, "confirm_proposals", - title=title, - props=[ - ("Proposal " + str(i), proposal) for i, proposal in enumerate(proposals, 1) - ], + "Submit proposals" if len(proposals) > 1 else "Submit proposal", + [("Proposal " + str(i), proposal) for i, proposal in enumerate(proposals, 1)], icon_color=ui.PURPLE, - br_code=ButtonRequestType.SignTx, + br_code=BR_SIGN_TX, ) @@ -149,13 +144,13 @@ async def require_confirm_delegation_manager_withdraw( ) -> None: await confirm_address( ctx, - title="Remove delegation", - address=address, - description="Delegator:", - br_type="confirm_undelegation", + "Remove delegation", + address, + "Delegator:", + "confirm_undelegation", + BR_SIGN_TX, icon=ui.ICON_RECEIVE, icon_color=ui.RED, - br_code=ButtonRequestType.SignTx, ) @@ -163,12 +158,12 @@ async def require_confirm_manager_remove_delegate(ctx: Context, fee: int) -> Non await confirm_metadata( ctx, "confirm_undelegation_final", - title="Remove delegation", - content="Fee:\n{}", - param=format_tezos_amount(fee), - hold=True, + "Remove delegation", + "Fee:\n{}", + format_tezos_amount(fee), + BR_SIGN_TX, hide_continue=True, + hold=True, icon=ui.ICON_RECEIVE, icon_color=ui.RED, - br_code=ButtonRequestType.SignTx, ) diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index aa39ff03b..43cfb9e86 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -1,21 +1,19 @@ from typing import TYPE_CHECKING -from trezor import wire -from trezor.crypto import hashlib -from trezor.crypto.curve import ed25519 -from trezor.enums import TezosBallotType, TezosContractType -from trezor.messages import TezosSignedTx +from trezor.enums import TezosContractType +from trezor.wire import DataError -from apps.common import paths from apps.common.keychain import with_slip44_keychain -from apps.common.writers import ( - write_bytes_fixed, - write_bytes_unchecked, - write_uint8, - write_uint32_be, -) +from apps.common.writers import write_bytes_fixed, write_uint8, write_uint32_be -from . import CURVE, PATTERNS, SLIP44_ID, helpers, layout +from . import CURVE, PATTERNS, SLIP44_ID, helpers +from .helpers import ( # symbols used more than once + CONTRACT_ID_SIZE, + TAGGED_PUBKEY_HASH_SIZE, + base58_encode_check, + write_bool, + write_instruction, +) if TYPE_CHECKING: from apps.common.keychain import Keychain @@ -23,99 +21,115 @@ if TYPE_CHECKING: from trezor.messages import ( TezosSignTx, TezosContractID, - TezosManagerTransfer, - TezosBallotOp, - TezosProposalOp, TezosRevealOp, TezosDelegationOp, TezosTransactionOp, TezosOriginationOp, + TezosSignedTx, ) from trezor.utils import Writer @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def sign_tx(ctx: Context, msg: TezosSignTx, keychain: Keychain) -> TezosSignedTx: - await paths.validate_path(ctx, keychain, msg.address_n) + from trezor.crypto import hashlib + from trezor.crypto.curve import ed25519 + from apps.common.paths import validate_path + from trezor.messages import TezosSignedTx + from trezor.enums import TezosBallotType + from . import layout + + await validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) + transaction = msg.transaction # local_cache_attribute + origination = msg.origination # local_cache_attribute + delegation = msg.delegation # local_cache_attribute + + if transaction is not None: + fee = transaction.fee # local_cache_attribute + parameters_manager = transaction.parameters_manager # local_cache_attribute - if msg.transaction is not None: # if the transaction operation is used to execute code on a smart contract - if msg.transaction.parameters_manager is not None: - parameters_manager = msg.transaction.parameters_manager + if parameters_manager is not None: + transfer = parameters_manager.transfer # local_cache_attribute # operation to delegate from a smart contract with manager.tz if parameters_manager.set_delegate is not None: delegate = _get_address_by_tag(parameters_manager.set_delegate) await layout.require_confirm_delegation_baker(ctx, delegate) - await layout.require_confirm_set_delegate(ctx, msg.transaction.fee) + await layout.require_confirm_set_delegate(ctx, fee) # operation to remove delegate from the smart contract with manager.tz elif parameters_manager.cancel_delegate is not None: - address = _get_address_from_contract(msg.transaction.destination) + address = _get_address_from_contract(transaction.destination) await layout.require_confirm_delegation_manager_withdraw(ctx, address) - await layout.require_confirm_manager_remove_delegate( - ctx, msg.transaction.fee - ) + await layout.require_confirm_manager_remove_delegate(ctx, fee) # operation to transfer tokens from a smart contract to an implicit account or a smart contract - elif parameters_manager.transfer is not None: - to = _get_address_from_contract(parameters_manager.transfer.destination) - await layout.require_confirm_tx( - ctx, to, parameters_manager.transfer.amount - ) - await layout.require_confirm_fee( - ctx, parameters_manager.transfer.amount, msg.transaction.fee - ) + elif transfer is not None: + to = _get_address_from_contract(transfer.destination) + await layout.require_confirm_tx(ctx, to, transfer.amount) + await layout.require_confirm_fee(ctx, transfer.amount, fee) else: # transactions from an implicit account - to = _get_address_from_contract(msg.transaction.destination) - await layout.require_confirm_tx(ctx, to, msg.transaction.amount) - await layout.require_confirm_fee( - ctx, msg.transaction.amount, msg.transaction.fee - ) + to = _get_address_from_contract(transaction.destination) + await layout.require_confirm_tx(ctx, to, transaction.amount) + await layout.require_confirm_fee(ctx, transaction.amount, fee) - elif msg.origination is not None: - source = _get_address_by_tag(msg.origination.source) + elif origination is not None: + source = _get_address_by_tag(origination.source) await layout.require_confirm_origination(ctx, source) # if we are immediately delegating contract - if msg.origination.delegate is not None: - delegate = _get_address_by_tag(msg.origination.delegate) + if origination.delegate is not None: + delegate = _get_address_by_tag(origination.delegate) await layout.require_confirm_delegation_baker(ctx, delegate) await layout.require_confirm_origination_fee( - ctx, msg.origination.balance, msg.origination.fee + ctx, origination.balance, origination.fee ) - elif msg.delegation is not None: - source = _get_address_by_tag(msg.delegation.source) + elif delegation is not None: + source = _get_address_by_tag(delegation.source) delegate_address: str | None = None - if msg.delegation.delegate is not None: - delegate_address = _get_address_by_tag(msg.delegation.delegate) + if delegation.delegate is not None: + delegate_address = _get_address_by_tag(delegation.delegate) if delegate_address is not None and source != delegate_address: await layout.require_confirm_delegation_baker(ctx, delegate_address) - await layout.require_confirm_set_delegate(ctx, msg.delegation.fee) + await layout.require_confirm_set_delegate(ctx, delegation.fee) # if account registers itself as a delegate else: - await layout.require_confirm_register_delegate( - ctx, source, msg.delegation.fee - ) + await layout.require_confirm_register_delegate(ctx, source, delegation.fee) elif msg.proposal is not None: - proposed_protocols = [_get_protocol_hash(p) for p in msg.proposal.proposals] + proposed_protocols = [ + # _get_protocol_hash + base58_encode_check(p, "P") + for p in msg.proposal.proposals + ] await layout.require_confirm_proposals(ctx, proposed_protocols) elif msg.ballot is not None: - proposed_protocol = _get_protocol_hash(msg.ballot.proposal) - submitted_ballot = _get_ballot(msg.ballot.ballot) + # _get_protocol_hash + proposed_protocol = base58_encode_check(msg.ballot.proposal, "P") + + # _get_ballot + if msg.ballot.ballot == TezosBallotType.Yay: + submitted_ballot = "yay" + elif msg.ballot.ballot == TezosBallotType.Nay: + submitted_ballot = "nay" + elif msg.ballot.ballot == TezosBallotType.Pass: + submitted_ballot = "pass" + else: + raise RuntimeError # unrecognized enum value + await layout.require_confirm_ballot(ctx, proposed_protocol, submitted_ballot) else: - raise wire.DataError("Invalid operation") + raise DataError("Invalid operation") w = bytearray() _get_operation_bytes(w, msg) @@ -131,11 +145,9 @@ async def sign_tx(ctx: Context, msg: TezosSignTx, keychain: Keychain) -> TezosSi sig_op_contents = opbytes + signature sig_op_contents_hash = hashlib.blake2b(sig_op_contents, outlen=32).digest() - ophash = helpers.base58_encode_check(sig_op_contents_hash, prefix="o") + ophash = base58_encode_check(sig_op_contents_hash, "o") - sig_prefixed = helpers.base58_encode_check( - signature, prefix=helpers.TEZOS_SIGNATURE_PREFIX - ) + sig_prefixed = base58_encode_check(signature, helpers.TEZOS_SIGNATURE_PREFIX) return TezosSignedTx( signature=sig_prefixed, sig_op_contents=sig_op_contents, operation_hash=ophash @@ -147,8 +159,8 @@ def _get_address_by_tag(address_hash: bytes) -> str: tag = int(address_hash[0]) if 0 <= tag < len(prefixes): - return helpers.base58_encode_check(address_hash[1:], prefix=prefixes[tag]) - raise wire.DataError("Invalid tag in address hash") + return base58_encode_check(address_hash[1:], prefixes[tag]) + raise DataError("Invalid tag in address hash") def _get_address_from_contract(address: TezosContractID) -> str: @@ -156,94 +168,144 @@ def _get_address_from_contract(address: TezosContractID) -> str: return _get_address_by_tag(address.hash) elif address.tag == TezosContractType.Originated: - return helpers.base58_encode_check( - address.hash[:-1], prefix=helpers.TEZOS_ORIGINATED_ADDRESS_PREFIX + return base58_encode_check( + address.hash[:-1], helpers.TEZOS_ORIGINATED_ADDRESS_PREFIX ) - raise wire.DataError("Invalid contract type") - - -def _get_protocol_hash(proposal: bytes) -> str: - return helpers.base58_encode_check(proposal, prefix="P") - - -def _get_ballot(ballot: TezosBallotType) -> str: - if ballot == TezosBallotType.Yay: - return "yay" - elif ballot == TezosBallotType.Nay: - return "nay" - elif ballot == TezosBallotType.Pass: - return "pass" - - raise RuntimeError # unrecognized enum value + raise DataError("Invalid contract type") def _get_operation_bytes(w: Writer, msg: TezosSignTx) -> None: + from apps.common.writers import write_bytes_unchecked + from .helpers import PROPOSAL_HASH_SIZE + + reveal = msg.reveal # local_cache_attribute + write_bytes_fixed(w, msg.branch, helpers.BRANCH_HASH_SIZE) # when the account sends first operation in lifetime, # we need to reveal its public key - if msg.reveal is not None: - _encode_common(w, msg.reveal, "reveal") - tag = int(msg.reveal.public_key[0]) + if reveal is not None: + _encode_common(w, reveal, "reveal") + tag = int(reveal.public_key[0]) try: public_key_size = helpers.PUBLIC_KEY_TAG_TO_SIZE[tag] except KeyError: - raise wire.DataError("Invalid tag in public key") + raise DataError("Invalid tag in public key") - write_bytes_fixed(w, msg.reveal.public_key, 1 + public_key_size) + write_bytes_fixed(w, reveal.public_key, 1 + public_key_size) # transaction operation if msg.transaction is not None: - _encode_common(w, msg.transaction, "transaction") - _encode_zarith(w, msg.transaction.amount) - _encode_contract_id(w, msg.transaction.destination) + transaction = msg.transaction + _encode_common(w, transaction, "transaction") + _encode_zarith(w, transaction.amount) + + # _encode_contract_id + contract_id = transaction.destination + write_uint8(w, contract_id.tag) + write_bytes_fixed(w, contract_id.hash, CONTRACT_ID_SIZE - 1) # support delegation and transfer from the old scriptless contracts (now with manager.tz script) - if msg.transaction.parameters_manager is not None: - parameters_manager = msg.transaction.parameters_manager + if transaction.parameters_manager is not None: + parameters_manager = transaction.parameters_manager # local_cache_attribute + transfer = parameters_manager.transfer # local_cache_attribute if parameters_manager.set_delegate is not None: - _encode_manager_delegation(w, parameters_manager.set_delegate) + # _encode_manager_delegation + delegate = parameters_manager.set_delegate + michelson_length = 42 + _encode_manager_common(w, michelson_length, "PUSH") + write_bytes_fixed(w, delegate, TAGGED_PUBKEY_HASH_SIZE) + for i in ("SOME", "SET_DELEGATE", "CONS"): + write_instruction(w, i) elif parameters_manager.cancel_delegate is not None: - _encode_manager_delegation_remove(w) - elif parameters_manager.transfer is not None: - assert parameters_manager.transfer.destination is not None - if ( - parameters_manager.transfer.destination.tag - == TezosContractType.Implicit - ): - _encode_manager_to_implicit_transfer(w, parameters_manager.transfer) + # _encode_manager_delegation_remove + michelson_length = 14 + _encode_manager_common(w, michelson_length, "NONE") + for i in ("SET_DELEGATE", "CONS"): + write_instruction(w, i) + elif transfer is not None: + assert transfer.destination is not None + if transfer.destination.tag == TezosContractType.Implicit: + # _encode_manager_to_implicit_transfer + manager_transfer = transfer + michelson_length = 48 + value_natural = bytearray() + _encode_natural(value_natural, manager_transfer.amount) + sequence_length = michelson_length + len(value_natural) + + _encode_manager_common(w, sequence_length, "PUSH") + write_bytes_fixed( + w, manager_transfer.destination.hash, TAGGED_PUBKEY_HASH_SIZE + ) + for i in ("IMPLICIT_ACCOUNT", "PUSH", "mutez"): + write_instruction(w, i) + _encode_natural(w, manager_transfer.amount) + for i in ("UNIT", "TRANSFER_TOKENS", "CONS"): + write_instruction(w, i) else: - _encode_manager_to_manager_transfer(w, parameters_manager.transfer) + # _encode_manager_to_manager_transfer + manager_transfer = transfer + michelson_length = 77 + + value_natural = bytearray() + _encode_natural(value_natural, manager_transfer.amount) + sequence_length = michelson_length + len(value_natural) + + _encode_manager_common(w, sequence_length, "PUSH", to_contract=True) + + # _encode_contract_id + contract_id = manager_transfer.destination + write_uint8(w, contract_id.tag) + write_bytes_fixed(w, contract_id.hash, CONTRACT_ID_SIZE - 1) + + for i in ("CONTRACT", "unit", "ASSERT_SOME", "PUSH", "mutez"): + write_instruction(w, i) + _encode_natural(w, manager_transfer.amount) + for i in ("UNIT", "TRANSFER_TOKENS", "CONS"): + write_instruction(w, i) else: - if msg.transaction.parameters: - helpers.write_bool(w, True) - helpers.check_tx_params_size(msg.transaction.parameters) - write_bytes_unchecked(w, msg.transaction.parameters) + parameters = transaction.parameters # local_cache_attribute + if parameters: + write_bool(w, True) + helpers.check_tx_params_size(parameters) + write_bytes_unchecked(w, parameters) else: - helpers.write_bool(w, False) + write_bool(w, False) # origination operation elif msg.origination is not None: - _encode_common(w, msg.origination, "origination") - _encode_zarith(w, msg.origination.balance) - _encode_data_with_bool_prefix( - w, msg.origination.delegate, helpers.TAGGED_PUBKEY_HASH_SIZE - ) - helpers.check_script_size(msg.origination.script) - write_bytes_unchecked(w, msg.origination.script) + origination = msg.origination + _encode_common(w, origination, "origination") + _encode_zarith(w, origination.balance) + _encode_data_with_bool_prefix(w, origination.delegate, TAGGED_PUBKEY_HASH_SIZE) + helpers.check_script_size(origination.script) + write_bytes_unchecked(w, origination.script) # delegation operation elif msg.delegation is not None: _encode_common(w, msg.delegation, "delegation") _encode_data_with_bool_prefix( - w, msg.delegation.delegate, helpers.TAGGED_PUBKEY_HASH_SIZE + w, msg.delegation.delegate, TAGGED_PUBKEY_HASH_SIZE ) elif msg.proposal is not None: - _encode_proposal(w, msg.proposal) + # _encode_proposal + proposal = msg.proposal + write_uint8(w, helpers.OP_TAG_PROPOSALS) + write_bytes_fixed(w, proposal.source, TAGGED_PUBKEY_HASH_SIZE) + write_uint32_be(w, proposal.period) + write_uint32_be(w, len(proposal.proposals) * PROPOSAL_HASH_SIZE) + for proposal_hash in proposal.proposals: + write_bytes_fixed(w, proposal_hash, PROPOSAL_HASH_SIZE) elif msg.ballot is not None: - _encode_ballot(w, msg.ballot) + # _encode_ballot + ballot = msg.ballot + write_uint8(w, helpers.OP_TAG_BALLOT) + write_bytes_fixed(w, ballot.source, TAGGED_PUBKEY_HASH_SIZE) + write_uint32_be(w, ballot.period) + write_bytes_fixed(w, ballot.proposal, PROPOSAL_HASH_SIZE) + write_uint8(w, ballot.ballot) def _encode_common( @@ -261,16 +323,14 @@ def _encode_common( "delegation": helpers.OP_TAG_DELEGATION, } write_uint8(w, operation_tags[str_operation]) - write_bytes_fixed(w, operation.source, helpers.TAGGED_PUBKEY_HASH_SIZE) - _encode_zarith(w, operation.fee) - _encode_zarith(w, operation.counter) - _encode_zarith(w, operation.gas_limit) - _encode_zarith(w, operation.storage_limit) - - -def _encode_contract_id(w: Writer, contract_id: TezosContractID) -> None: - write_uint8(w, contract_id.tag) - write_bytes_fixed(w, contract_id.hash, helpers.CONTRACT_ID_SIZE - 1) + write_bytes_fixed(w, operation.source, TAGGED_PUBKEY_HASH_SIZE) + for num in ( + operation.fee, + operation.counter, + operation.gas_limit, + operation.storage_limit, + ): + _encode_zarith(w, num) def _encode_data_with_bool_prefix( @@ -295,23 +355,6 @@ def _encode_zarith(w: Writer, num: int) -> None: write_uint8(w, 128 | byte) -def _encode_proposal(w: Writer, proposal: TezosProposalOp) -> None: - write_uint8(w, helpers.OP_TAG_PROPOSALS) - write_bytes_fixed(w, proposal.source, helpers.TAGGED_PUBKEY_HASH_SIZE) - write_uint32_be(w, proposal.period) - write_uint32_be(w, len(proposal.proposals) * helpers.PROPOSAL_HASH_SIZE) - for proposal_hash in proposal.proposals: - write_bytes_fixed(w, proposal_hash, helpers.PROPOSAL_HASH_SIZE) - - -def _encode_ballot(w: Writer, ballot: TezosBallotOp) -> None: - write_uint8(w, helpers.OP_TAG_BALLOT) - write_bytes_fixed(w, ballot.source, helpers.TAGGED_PUBKEY_HASH_SIZE) - write_uint32_be(w, ballot.period) - write_bytes_fixed(w, ballot.proposal, helpers.PROPOSAL_HASH_SIZE) - write_uint8(w, ballot.ballot) - - def _encode_natural(w: Writer, num: int) -> None: # encode a natural integer with its signed bit on position 7 # as we do not expect negative numbers in a transfer operation the bit is never set @@ -334,84 +377,20 @@ def _encode_manager_common( # 5 = tag and sequence_length (1 byte + 4 bytes) argument_length = sequence_length + 5 - helpers.write_bool(w, True) + write_bool(w, True) write_uint8(w, helpers.DO_ENTRYPOINT_TAG) write_uint32_be(w, argument_length) write_uint8(w, helpers.MICHELSON_SEQUENCE_TAG) write_uint32_be(w, sequence_length) - helpers.write_instruction(w, "DROP") - helpers.write_instruction(w, "NIL") - helpers.write_instruction(w, "operation") - helpers.write_instruction(w, operation) + for i in ("DROP", "NIL", "operation", operation): + write_instruction(w, i) if to_contract: - helpers.write_instruction(w, "address") + write_instruction(w, "address") else: - helpers.write_instruction(w, "key_hash") + write_instruction(w, "key_hash") if operation == "PUSH": write_uint8(w, 10) # byte sequence if to_contract: - write_uint32_be(w, helpers.CONTRACT_ID_SIZE) + write_uint32_be(w, CONTRACT_ID_SIZE) else: - write_uint32_be(w, helpers.TAGGED_PUBKEY_HASH_SIZE) - - -def _encode_manager_to_implicit_transfer( - w: Writer, manager_transfer: TezosManagerTransfer -) -> None: - MICHELSON_LENGTH = 48 - - value_natural = bytearray() - _encode_natural(value_natural, manager_transfer.amount) - sequence_length = MICHELSON_LENGTH + len(value_natural) - - _encode_manager_common(w, sequence_length, "PUSH") - write_bytes_fixed( - w, manager_transfer.destination.hash, helpers.TAGGED_PUBKEY_HASH_SIZE - ) - helpers.write_instruction(w, "IMPLICIT_ACCOUNT") - helpers.write_instruction(w, "PUSH") - helpers.write_instruction(w, "mutez") - _encode_natural(w, manager_transfer.amount) - helpers.write_instruction(w, "UNIT") - helpers.write_instruction(w, "TRANSFER_TOKENS") - helpers.write_instruction(w, "CONS") - - -# smart_contract_delegation -def _encode_manager_delegation(w: Writer, delegate: bytes) -> None: - MICHELSON_LENGTH = 42 # length is fixed this time(no variable length fields) - - _encode_manager_common(w, MICHELSON_LENGTH, "PUSH") - write_bytes_fixed(w, delegate, helpers.TAGGED_PUBKEY_HASH_SIZE) - helpers.write_instruction(w, "SOME") - helpers.write_instruction(w, "SET_DELEGATE") - helpers.write_instruction(w, "CONS") - - -def _encode_manager_delegation_remove(w: Writer) -> None: - MICHELSON_LENGTH = 14 # length is fixed this time(no variable length fields) - _encode_manager_common(w, MICHELSON_LENGTH, "NONE") - helpers.write_instruction(w, "SET_DELEGATE") - helpers.write_instruction(w, "CONS") - - -def _encode_manager_to_manager_transfer( - w: Writer, manager_transfer: TezosManagerTransfer -) -> None: - MICHELSON_LENGTH = 77 - - value_natural = bytearray() - _encode_natural(value_natural, manager_transfer.amount) - sequence_length = MICHELSON_LENGTH + len(value_natural) - - _encode_manager_common(w, sequence_length, "PUSH", to_contract=True) - _encode_contract_id(w, manager_transfer.destination) - helpers.write_instruction(w, "CONTRACT") - helpers.write_instruction(w, "unit") - helpers.write_instruction(w, "ASSERT_SOME") - helpers.write_instruction(w, "PUSH") - helpers.write_instruction(w, "mutez") - _encode_natural(w, manager_transfer.amount) - helpers.write_instruction(w, "UNIT") - helpers.write_instruction(w, "TRANSFER_TOKENS") - helpers.write_instruction(w, "CONS") + write_uint32_be(w, TAGGED_PUBKEY_HASH_SIZE) diff --git a/core/tests/test_apps.tezos.encode.py b/core/tests/test_apps.tezos.encode.py index 78ef0b6fa..a7f463a01 100644 --- a/core/tests/test_apps.tezos.encode.py +++ b/core/tests/test_apps.tezos.encode.py @@ -3,15 +3,22 @@ from common import * if not utils.BITCOIN_ONLY: from trezor.enums import TezosContractType from trezor.messages import TezosContractID - from apps.tezos.helpers import base58_encode_check, write_bool + from apps.tezos.helpers import base58_encode_check, write_bool, CONTRACT_ID_SIZE from apps.tezos.sign_tx import ( - _encode_contract_id, + write_uint8, + write_bytes_fixed, _encode_data_with_bool_prefix, _encode_zarith, _encode_natural, ) +# NOTE: copy-pasted from apps.tezos.sign_tx +def _encode_contract_id(w: "Writer", contract_id: TezosContractID) -> None: + write_uint8(w, contract_id.tag) + write_bytes_fixed(w, contract_id.hash, CONTRACT_ID_SIZE - 1) + + @unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin") class TestTezosEncoding(unittest.TestCase): def test_tezos_encode_zarith(self):