1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-18 05:28:40 +00:00

chore(core): decrease tezos size by 860 bytes

This commit is contained in:
grdddj 2022-09-16 18:48:52 +02:00 committed by matejcik
parent 80ab7f1c29
commit 164be3ac8f
6 changed files with 285 additions and 303 deletions

View File

@ -1,16 +1,11 @@
from typing import TYPE_CHECKING 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 apps.common.keychain import with_slip44_keychain
from . import CURVE, PATTERNS, SLIP44_ID, helpers from . import CURVE, PATTERNS, SLIP44_ID
if TYPE_CHECKING: if TYPE_CHECKING:
from trezor.messages import TezosGetAddress from trezor.messages import TezosGetAddress, TezosAddress
from apps.common.keychain import Keychain from apps.common.keychain import Keychain
from trezor.wire import Context from trezor.wire import Context
@ -19,18 +14,22 @@ if TYPE_CHECKING:
async def get_address( async def get_address(
ctx: Context, msg: TezosGetAddress, keychain: Keychain ctx: Context, msg: TezosGetAddress, keychain: Keychain
) -> TezosAddress: ) -> 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) await paths.validate_path(ctx, keychain, msg.address_n)
node = keychain.derive(msg.address_n) node = keychain.derive(msg.address_n)
pk = seed.remove_ed25519_prefix(node.public_key()) pk = seed.remove_ed25519_prefix(node.public_key())
pkh = hashlib.blake2b(pk, outlen=helpers.PUBLIC_KEY_HASH_SIZE).digest() pkh = hashlib.blake2b(pk, outlen=helpers.PUBLIC_KEY_HASH_SIZE).digest()
address = helpers.base58_encode_check( address = helpers.base58_encode_check(pkh, helpers.TEZOS_ED25519_ADDRESS_PREFIX)
pkh, prefix=helpers.TEZOS_ED25519_ADDRESS_PREFIX
)
if msg.show_display: if msg.show_display:
title = paths.address_n_to_str(msg.address_n) 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) return TezosAddress(address=address)

View File

@ -1,15 +1,11 @@
from typing import TYPE_CHECKING 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 apps.common.keychain import with_slip44_keychain
from . import CURVE, PATTERNS, SLIP44_ID, helpers from . import CURVE, PATTERNS, SLIP44_ID
if TYPE_CHECKING: if TYPE_CHECKING:
from trezor.messages import TezosGetPublicKey from trezor.messages import TezosGetPublicKey, TezosPublicKey
from apps.common.keychain import Keychain from apps.common.keychain import Keychain
from trezor.wire import Context from trezor.wire import Context
@ -18,11 +14,16 @@ if TYPE_CHECKING:
async def get_public_key( async def get_public_key(
ctx: Context, msg: TezosGetPublicKey, keychain: Keychain ctx: Context, msg: TezosGetPublicKey, keychain: Keychain
) -> TezosPublicKey: ) -> 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) await paths.validate_path(ctx, keychain, msg.address_n)
node = keychain.derive(msg.address_n) node = keychain.derive(msg.address_n)
pk = seed.remove_ed25519_prefix(node.public_key()) 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: if msg.show_display:
await show_pubkey(ctx, pk_prefixed) await show_pubkey(ctx, pk_prefixed)

View File

@ -1,12 +1,10 @@
from micropython import const from micropython import const
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from trezor import wire
from trezor.crypto import base58
from trezor.utils import BufferReader, ensure from trezor.utils import BufferReader, ensure
from trezor.wire import DataError
from apps.common.readers import read_uint32_be from apps.common.readers import read_uint32_be
from apps.common.writers import write_bytes_unchecked, write_uint8
if TYPE_CHECKING: if TYPE_CHECKING:
from trezor.utils import Writer 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: def base58_encode_check(payload: bytes, prefix: str | None = None) -> str:
from trezor.crypto import base58
result = payload result = payload
if prefix is not None: if prefix is not None:
result = TEZOS_PREFIX_BYTES[prefix] + payload 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: def write_bool(w: Writer, boolean: bool) -> None:
if boolean: from apps.common.writers import write_uint8
write_uint8(w, 255)
else: write_uint8(w, 255 if boolean else 0)
write_uint8(w, 0)
def write_instruction(w: Writer, instruction: str) -> None: def write_instruction(w: Writer, instruction: str) -> None:
from apps.common.writers import write_bytes_unchecked
write_bytes_unchecked(w, MICHELSON_INSTRUCTION_BYTES[instruction]) write_bytes_unchecked(w, MICHELSON_INSTRUCTION_BYTES[instruction])
@ -108,7 +109,7 @@ def check_script_size(script: bytes) -> None:
n = read_uint32_be(r) n = read_uint32_be(r)
ensure(r.remaining_count() == n) ensure(r.remaining_count() == n)
except (AssertionError, EOFError): except (AssertionError, EOFError):
raise wire.DataError("Invalid script") raise DataError("Invalid script")
def check_tx_params_size(params: bytes) -> None: def check_tx_params_size(params: bytes) -> None:
@ -119,8 +120,8 @@ def check_tx_params_size(params: bytes) -> None:
n = r.get() n = r.get()
r.read(n) r.read(n)
elif tag > 4: elif tag > 4:
raise wire.DataError("Unknown entrypoint tag") raise DataError("Unknown entrypoint tag")
n = read_uint32_be(r) n = read_uint32_be(r)
ensure(r.remaining_count() == n) ensure(r.remaining_count() == n)
except (AssertionError, EOFError): except (AssertionError, EOFError):
raise wire.DataError("Invalid transaction parameters") raise DataError("Invalid transaction parameters")

View File

@ -2,39 +2,38 @@ from typing import TYPE_CHECKING
from trezor import ui from trezor import ui
from trezor.enums import ButtonRequestType from trezor.enums import ButtonRequestType
from trezor.strings import format_amount from trezor.ui.layouts import confirm_address, confirm_metadata, confirm_properties
from trezor.ui.layouts import (
confirm_address,
confirm_metadata,
confirm_output,
confirm_properties,
confirm_total,
)
from .helpers import TEZOS_AMOUNT_DECIMALS
if TYPE_CHECKING: if TYPE_CHECKING:
from trezor.wire import Context 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: async def require_confirm_tx(ctx: Context, to: str, value: int) -> None:
from trezor.ui.layouts import confirm_output
await confirm_output( await confirm_output(
ctx, ctx,
to, to,
format_tezos_amount(value), format_tezos_amount(value),
font_amount=ui.BOLD, ui.BOLD,
to_str="\nto\n", to_str="\nto\n",
width=18, width=18,
br_code=ButtonRequestType.SignTx, br_code=BR_SIGN_TX,
) )
async def require_confirm_fee(ctx: Context, value: int, fee: int) -> None: async def require_confirm_fee(ctx: Context, value: int, fee: int) -> None:
from trezor.ui.layouts import confirm_total
await confirm_total( await confirm_total(
ctx, ctx,
total_amount=format_tezos_amount(value), format_tezos_amount(value),
format_tezos_amount(fee),
total_label="Amount:\n", total_label="Amount:\n",
fee_amount=format_tezos_amount(fee),
fee_label="\nFee:\n", 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: async def require_confirm_origination(ctx: Context, address: str) -> None:
await confirm_address( await confirm_address(
ctx, ctx,
title="Confirm origination", "Confirm origination",
address=address, address,
description="Address:", "Address:",
br_type="confirm_origination", "confirm_origination",
BR_SIGN_TX,
icon_color=ui.ORANGE, icon_color=ui.ORANGE,
br_code=ButtonRequestType.SignTx,
) )
async def require_confirm_origination_fee(ctx: Context, balance: int, fee: int) -> None: async def require_confirm_origination_fee(ctx: Context, balance: int, fee: int) -> None:
await confirm_properties( await confirm_properties(
ctx, ctx,
title="Confirm origination", "confirm_origination_final",
props=( "Confirm origination",
(
("Balance:", format_tezos_amount(balance)), ("Balance:", format_tezos_amount(balance)),
("Fee:", format_tezos_amount(fee)), ("Fee:", format_tezos_amount(fee)),
), ),
icon_color=ui.ORANGE, icon_color=ui.ORANGE,
br_type="confirm_origination_final",
hold=True, 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: async def require_confirm_delegation_baker(ctx: Context, baker: str) -> None:
await confirm_address( await confirm_address(
ctx, ctx,
title="Confirm delegation", "Confirm delegation",
address=baker, baker,
description="Baker address:", "Baker address:",
br_type="confirm_delegation", "confirm_delegation",
icon_color=ui.BLUE, BR_SIGN_TX,
br_code=ButtonRequestType.SignTx, icon_color=BLUE,
) )
@ -81,13 +80,13 @@ async def require_confirm_set_delegate(ctx: Context, fee: int) -> None:
await confirm_metadata( await confirm_metadata(
ctx, ctx,
"confirm_delegation_final", "confirm_delegation_final",
title="Confirm delegation", "Confirm delegation",
content="Fee:\n{}", "Fee:\n{}",
param=format_tezos_amount(fee), format_tezos_amount(fee),
hold=True, BR_SIGN_TX,
hide_continue=True, hide_continue=True,
icon_color=ui.BLUE, hold=True,
br_code=ButtonRequestType.SignTx, icon_color=BLUE,
) )
@ -97,17 +96,20 @@ async def require_confirm_register_delegate(
await confirm_properties( await confirm_properties(
ctx, ctx,
"confirm_register_delegate", "confirm_register_delegate",
title="Register delegate", "Register delegate",
props=( (
("Fee:", format_tezos_amount(fee)), ("Fee:", format_tezos_amount(fee)),
("Address:", address), ("Address:", address),
), ),
icon_color=ui.BLUE, icon_color=BLUE,
br_code=ButtonRequestType.SignTx, br_code=BR_SIGN_TX,
) )
def format_tezos_amount(value: int) -> str: 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) formatted_value = format_amount(value, TEZOS_AMOUNT_DECIMALS)
return formatted_value + " XTZ" return formatted_value + " XTZ"
@ -116,31 +118,24 @@ async def require_confirm_ballot(ctx: Context, proposal: str, ballot: str) -> No
await confirm_properties( await confirm_properties(
ctx, ctx,
"confirm_ballot", "confirm_ballot",
title="Submit ballot", "Submit ballot",
props=( (
("Ballot:", ballot), ("Ballot:", ballot),
("Proposal:", proposal), ("Proposal:", proposal),
), ),
icon_color=ui.PURPLE, icon_color=ui.PURPLE,
br_code=ButtonRequestType.SignTx, br_code=BR_SIGN_TX,
) )
async def require_confirm_proposals(ctx: Context, proposals: list[str]) -> None: 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( await confirm_properties(
ctx, ctx,
"confirm_proposals", "confirm_proposals",
title=title, "Submit proposals" if len(proposals) > 1 else "Submit proposal",
props=[ [("Proposal " + str(i), proposal) for i, proposal in enumerate(proposals, 1)],
("Proposal " + str(i), proposal) for i, proposal in enumerate(proposals, 1)
],
icon_color=ui.PURPLE, 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: ) -> None:
await confirm_address( await confirm_address(
ctx, ctx,
title="Remove delegation", "Remove delegation",
address=address, address,
description="Delegator:", "Delegator:",
br_type="confirm_undelegation", "confirm_undelegation",
BR_SIGN_TX,
icon=ui.ICON_RECEIVE, icon=ui.ICON_RECEIVE,
icon_color=ui.RED, 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( await confirm_metadata(
ctx, ctx,
"confirm_undelegation_final", "confirm_undelegation_final",
title="Remove delegation", "Remove delegation",
content="Fee:\n{}", "Fee:\n{}",
param=format_tezos_amount(fee), format_tezos_amount(fee),
hold=True, BR_SIGN_TX,
hide_continue=True, hide_continue=True,
hold=True,
icon=ui.ICON_RECEIVE, icon=ui.ICON_RECEIVE,
icon_color=ui.RED, icon_color=ui.RED,
br_code=ButtonRequestType.SignTx,
) )

View File

@ -1,21 +1,19 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from trezor import wire from trezor.enums import TezosContractType
from trezor.crypto import hashlib from trezor.wire import DataError
from trezor.crypto.curve import ed25519
from trezor.enums import TezosBallotType, TezosContractType
from trezor.messages import TezosSignedTx
from apps.common import paths
from apps.common.keychain import with_slip44_keychain from apps.common.keychain import with_slip44_keychain
from apps.common.writers import ( from apps.common.writers import write_bytes_fixed, write_uint8, write_uint32_be
write_bytes_fixed,
write_bytes_unchecked,
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: if TYPE_CHECKING:
from apps.common.keychain import Keychain from apps.common.keychain import Keychain
@ -23,99 +21,115 @@ if TYPE_CHECKING:
from trezor.messages import ( from trezor.messages import (
TezosSignTx, TezosSignTx,
TezosContractID, TezosContractID,
TezosManagerTransfer,
TezosBallotOp,
TezosProposalOp,
TezosRevealOp, TezosRevealOp,
TezosDelegationOp, TezosDelegationOp,
TezosTransactionOp, TezosTransactionOp,
TezosOriginationOp, TezosOriginationOp,
TezosSignedTx,
) )
from trezor.utils import Writer from trezor.utils import Writer
@with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE)
async def sign_tx(ctx: Context, msg: TezosSignTx, keychain: Keychain) -> TezosSignedTx: 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) 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 the transaction operation is used to execute code on a smart contract
if msg.transaction.parameters_manager is not None: if parameters_manager is not None:
parameters_manager = msg.transaction.parameters_manager transfer = parameters_manager.transfer # local_cache_attribute
# operation to delegate from a smart contract with manager.tz # operation to delegate from a smart contract with manager.tz
if parameters_manager.set_delegate is not None: if parameters_manager.set_delegate is not None:
delegate = _get_address_by_tag(parameters_manager.set_delegate) delegate = _get_address_by_tag(parameters_manager.set_delegate)
await layout.require_confirm_delegation_baker(ctx, 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 # operation to remove delegate from the smart contract with manager.tz
elif parameters_manager.cancel_delegate is not None: 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_delegation_manager_withdraw(ctx, address)
await layout.require_confirm_manager_remove_delegate( await layout.require_confirm_manager_remove_delegate(ctx, fee)
ctx, msg.transaction.fee
)
# operation to transfer tokens from a smart contract to an implicit account or a smart contract # operation to transfer tokens from a smart contract to an implicit account or a smart contract
elif parameters_manager.transfer is not None: elif transfer is not None:
to = _get_address_from_contract(parameters_manager.transfer.destination) to = _get_address_from_contract(transfer.destination)
await layout.require_confirm_tx( await layout.require_confirm_tx(ctx, to, transfer.amount)
ctx, to, parameters_manager.transfer.amount await layout.require_confirm_fee(ctx, transfer.amount, fee)
)
await layout.require_confirm_fee(
ctx, parameters_manager.transfer.amount, msg.transaction.fee
)
else: else:
# transactions from an implicit account # transactions from an implicit account
to = _get_address_from_contract(msg.transaction.destination) to = _get_address_from_contract(transaction.destination)
await layout.require_confirm_tx(ctx, to, msg.transaction.amount) await layout.require_confirm_tx(ctx, to, transaction.amount)
await layout.require_confirm_fee( await layout.require_confirm_fee(ctx, transaction.amount, fee)
ctx, msg.transaction.amount, msg.transaction.fee
)
elif msg.origination is not None: elif origination is not None:
source = _get_address_by_tag(msg.origination.source) source = _get_address_by_tag(origination.source)
await layout.require_confirm_origination(ctx, source) await layout.require_confirm_origination(ctx, source)
# if we are immediately delegating contract # if we are immediately delegating contract
if msg.origination.delegate is not None: if origination.delegate is not None:
delegate = _get_address_by_tag(msg.origination.delegate) delegate = _get_address_by_tag(origination.delegate)
await layout.require_confirm_delegation_baker(ctx, delegate) await layout.require_confirm_delegation_baker(ctx, delegate)
await layout.require_confirm_origination_fee( await layout.require_confirm_origination_fee(
ctx, msg.origination.balance, msg.origination.fee ctx, origination.balance, origination.fee
) )
elif msg.delegation is not None: elif delegation is not None:
source = _get_address_by_tag(msg.delegation.source) source = _get_address_by_tag(delegation.source)
delegate_address: str | None = None delegate_address: str | None = None
if msg.delegation.delegate is not None: if delegation.delegate is not None:
delegate_address = _get_address_by_tag(msg.delegation.delegate) delegate_address = _get_address_by_tag(delegation.delegate)
if delegate_address is not None and source != delegate_address: if delegate_address is not None and source != delegate_address:
await layout.require_confirm_delegation_baker(ctx, 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 # if account registers itself as a delegate
else: else:
await layout.require_confirm_register_delegate( await layout.require_confirm_register_delegate(ctx, source, delegation.fee)
ctx, source, msg.delegation.fee
)
elif msg.proposal is not None: 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) await layout.require_confirm_proposals(ctx, proposed_protocols)
elif msg.ballot is not None: elif msg.ballot is not None:
proposed_protocol = _get_protocol_hash(msg.ballot.proposal) # _get_protocol_hash
submitted_ballot = _get_ballot(msg.ballot.ballot) 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) await layout.require_confirm_ballot(ctx, proposed_protocol, submitted_ballot)
else: else:
raise wire.DataError("Invalid operation") raise DataError("Invalid operation")
w = bytearray() w = bytearray()
_get_operation_bytes(w, msg) _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 = opbytes + signature
sig_op_contents_hash = hashlib.blake2b(sig_op_contents, outlen=32).digest() 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( sig_prefixed = base58_encode_check(signature, helpers.TEZOS_SIGNATURE_PREFIX)
signature, prefix=helpers.TEZOS_SIGNATURE_PREFIX
)
return TezosSignedTx( return TezosSignedTx(
signature=sig_prefixed, sig_op_contents=sig_op_contents, operation_hash=ophash 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]) tag = int(address_hash[0])
if 0 <= tag < len(prefixes): if 0 <= tag < len(prefixes):
return helpers.base58_encode_check(address_hash[1:], prefix=prefixes[tag]) return base58_encode_check(address_hash[1:], prefixes[tag])
raise wire.DataError("Invalid tag in address hash") raise DataError("Invalid tag in address hash")
def _get_address_from_contract(address: TezosContractID) -> str: 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) return _get_address_by_tag(address.hash)
elif address.tag == TezosContractType.Originated: elif address.tag == TezosContractType.Originated:
return helpers.base58_encode_check( return base58_encode_check(
address.hash[:-1], prefix=helpers.TEZOS_ORIGINATED_ADDRESS_PREFIX address.hash[:-1], helpers.TEZOS_ORIGINATED_ADDRESS_PREFIX
) )
raise wire.DataError("Invalid contract type") raise 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
def _get_operation_bytes(w: Writer, msg: TezosSignTx) -> None: 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) write_bytes_fixed(w, msg.branch, helpers.BRANCH_HASH_SIZE)
# when the account sends first operation in lifetime, # when the account sends first operation in lifetime,
# we need to reveal its public key # we need to reveal its public key
if msg.reveal is not None: if reveal is not None:
_encode_common(w, msg.reveal, "reveal") _encode_common(w, reveal, "reveal")
tag = int(msg.reveal.public_key[0]) tag = int(reveal.public_key[0])
try: try:
public_key_size = helpers.PUBLIC_KEY_TAG_TO_SIZE[tag] public_key_size = helpers.PUBLIC_KEY_TAG_TO_SIZE[tag]
except KeyError: 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 # transaction operation
if msg.transaction is not None: if msg.transaction is not None:
_encode_common(w, msg.transaction, "transaction") transaction = msg.transaction
_encode_zarith(w, msg.transaction.amount) _encode_common(w, transaction, "transaction")
_encode_contract_id(w, msg.transaction.destination) _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) # support delegation and transfer from the old scriptless contracts (now with manager.tz script)
if msg.transaction.parameters_manager is not None: if transaction.parameters_manager is not None:
parameters_manager = msg.transaction.parameters_manager parameters_manager = transaction.parameters_manager # local_cache_attribute
transfer = parameters_manager.transfer # local_cache_attribute
if parameters_manager.set_delegate is not None: 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: elif parameters_manager.cancel_delegate is not None:
_encode_manager_delegation_remove(w) # _encode_manager_delegation_remove
elif parameters_manager.transfer is not None: michelson_length = 14
assert parameters_manager.transfer.destination is not None _encode_manager_common(w, michelson_length, "NONE")
if ( for i in ("SET_DELEGATE", "CONS"):
parameters_manager.transfer.destination.tag write_instruction(w, i)
== TezosContractType.Implicit elif transfer is not None:
): assert transfer.destination is not None
_encode_manager_to_implicit_transfer(w, parameters_manager.transfer) 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: 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: else:
if msg.transaction.parameters: parameters = transaction.parameters # local_cache_attribute
helpers.write_bool(w, True) if parameters:
helpers.check_tx_params_size(msg.transaction.parameters) write_bool(w, True)
write_bytes_unchecked(w, msg.transaction.parameters) helpers.check_tx_params_size(parameters)
write_bytes_unchecked(w, parameters)
else: else:
helpers.write_bool(w, False) write_bool(w, False)
# origination operation # origination operation
elif msg.origination is not None: elif msg.origination is not None:
_encode_common(w, msg.origination, "origination") origination = msg.origination
_encode_zarith(w, msg.origination.balance) _encode_common(w, origination, "origination")
_encode_data_with_bool_prefix( _encode_zarith(w, origination.balance)
w, msg.origination.delegate, helpers.TAGGED_PUBKEY_HASH_SIZE _encode_data_with_bool_prefix(w, origination.delegate, TAGGED_PUBKEY_HASH_SIZE)
) helpers.check_script_size(origination.script)
helpers.check_script_size(msg.origination.script) write_bytes_unchecked(w, origination.script)
write_bytes_unchecked(w, msg.origination.script)
# delegation operation # delegation operation
elif msg.delegation is not None: elif msg.delegation is not None:
_encode_common(w, msg.delegation, "delegation") _encode_common(w, msg.delegation, "delegation")
_encode_data_with_bool_prefix( _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: 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: 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( def _encode_common(
@ -261,16 +323,14 @@ def _encode_common(
"delegation": helpers.OP_TAG_DELEGATION, "delegation": helpers.OP_TAG_DELEGATION,
} }
write_uint8(w, operation_tags[str_operation]) write_uint8(w, operation_tags[str_operation])
write_bytes_fixed(w, operation.source, helpers.TAGGED_PUBKEY_HASH_SIZE) write_bytes_fixed(w, operation.source, TAGGED_PUBKEY_HASH_SIZE)
_encode_zarith(w, operation.fee) for num in (
_encode_zarith(w, operation.counter) operation.fee,
_encode_zarith(w, operation.gas_limit) operation.counter,
_encode_zarith(w, operation.storage_limit) operation.gas_limit,
operation.storage_limit,
):
def _encode_contract_id(w: Writer, contract_id: TezosContractID) -> None: _encode_zarith(w, num)
write_uint8(w, contract_id.tag)
write_bytes_fixed(w, contract_id.hash, helpers.CONTRACT_ID_SIZE - 1)
def _encode_data_with_bool_prefix( def _encode_data_with_bool_prefix(
@ -295,23 +355,6 @@ def _encode_zarith(w: Writer, num: int) -> None:
write_uint8(w, 128 | byte) 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: def _encode_natural(w: Writer, num: int) -> None:
# encode a natural integer with its signed bit on position 7 # 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 # 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) # 5 = tag and sequence_length (1 byte + 4 bytes)
argument_length = sequence_length + 5 argument_length = sequence_length + 5
helpers.write_bool(w, True) write_bool(w, True)
write_uint8(w, helpers.DO_ENTRYPOINT_TAG) write_uint8(w, helpers.DO_ENTRYPOINT_TAG)
write_uint32_be(w, argument_length) write_uint32_be(w, argument_length)
write_uint8(w, helpers.MICHELSON_SEQUENCE_TAG) write_uint8(w, helpers.MICHELSON_SEQUENCE_TAG)
write_uint32_be(w, sequence_length) write_uint32_be(w, sequence_length)
helpers.write_instruction(w, "DROP") for i in ("DROP", "NIL", "operation", operation):
helpers.write_instruction(w, "NIL") write_instruction(w, i)
helpers.write_instruction(w, "operation")
helpers.write_instruction(w, operation)
if to_contract: if to_contract:
helpers.write_instruction(w, "address") write_instruction(w, "address")
else: else:
helpers.write_instruction(w, "key_hash") write_instruction(w, "key_hash")
if operation == "PUSH": if operation == "PUSH":
write_uint8(w, 10) # byte sequence write_uint8(w, 10) # byte sequence
if to_contract: if to_contract:
write_uint32_be(w, helpers.CONTRACT_ID_SIZE) write_uint32_be(w, CONTRACT_ID_SIZE)
else: else:
write_uint32_be(w, helpers.TAGGED_PUBKEY_HASH_SIZE) write_uint32_be(w, 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")

View File

@ -3,15 +3,22 @@ from common import *
if not utils.BITCOIN_ONLY: if not utils.BITCOIN_ONLY:
from trezor.enums import TezosContractType from trezor.enums import TezosContractType
from trezor.messages import TezosContractID 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 ( from apps.tezos.sign_tx import (
_encode_contract_id, write_uint8,
write_bytes_fixed,
_encode_data_with_bool_prefix, _encode_data_with_bool_prefix,
_encode_zarith, _encode_zarith,
_encode_natural, _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") @unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
class TestTezosEncoding(unittest.TestCase): class TestTezosEncoding(unittest.TestCase):
def test_tezos_encode_zarith(self): def test_tezos_encode_zarith(self):