From 80ab7f1c298a7486196f75eaf74bdf247212386d Mon Sep 17 00:00:00 2001 From: grdddj Date: Fri, 16 Sep 2022 15:59:09 +0200 Subject: [PATCH] chore(core): decrease eos size by 1kb --- core/src/apps/eos/actions/__init__.py | 76 ++--- core/src/apps/eos/actions/layout.py | 347 ++++++++++----------- core/src/apps/eos/get_public_key.py | 35 +-- core/src/apps/eos/helpers.py | 9 +- core/src/apps/eos/layout.py | 21 +- core/src/apps/eos/sign_tx.py | 71 +++-- core/src/apps/eos/writers.py | 30 +- core/tests/test_apps.eos.check_action.py | 72 ++--- core/tests/test_apps.eos.get_public_key.py | 11 +- 9 files changed, 335 insertions(+), 337 deletions(-) diff --git a/core/src/apps/eos/actions/__init__.py b/core/src/apps/eos/actions/__init__.py index edfe792081..24afff5c2e 100644 --- a/core/src/apps/eos/actions/__init__.py +++ b/core/src/apps/eos/actions/__init__.py @@ -1,119 +1,125 @@ from typing import TYPE_CHECKING -from trezor.crypto.hashlib import sha256 -from trezor.messages import EosTxActionAck, EosTxActionRequest -from trezor.utils import HashWriter - -from .. import helpers, writers -from . import layout - if TYPE_CHECKING: from trezor import wire - from trezor.utils import Writer + from trezor.utils import Writer, HashWriter + from trezor.messages import EosTxActionAck async def process_action( ctx: wire.Context, sha: HashWriter, action: EosTxActionAck ) -> None: + from .. import helpers, writers + from . import layout + name = helpers.eos_name_to_string(action.common.name) account = helpers.eos_name_to_string(action.common.account) - if not check_action(action, name, account): + if not _check_action(action, name, account): raise ValueError("Invalid action") w = bytearray() if account == "eosio": if name == "buyram": - assert action.buy_ram is not None # check_action + assert action.buy_ram is not None # _check_action await layout.confirm_action_buyram(ctx, action.buy_ram) writers.write_action_buyram(w, action.buy_ram) elif name == "buyrambytes": - assert action.buy_ram_bytes is not None # check_action + assert action.buy_ram_bytes is not None # _check_action await layout.confirm_action_buyrambytes(ctx, action.buy_ram_bytes) writers.write_action_buyrambytes(w, action.buy_ram_bytes) elif name == "sellram": - assert action.sell_ram is not None # check_action + assert action.sell_ram is not None # _check_action await layout.confirm_action_sellram(ctx, action.sell_ram) writers.write_action_sellram(w, action.sell_ram) elif name == "delegatebw": - assert action.delegate is not None # check_action + assert action.delegate is not None # _check_action await layout.confirm_action_delegate(ctx, action.delegate) writers.write_action_delegate(w, action.delegate) elif name == "undelegatebw": - assert action.undelegate is not None # check_action + assert action.undelegate is not None # _check_action await layout.confirm_action_undelegate(ctx, action.undelegate) writers.write_action_undelegate(w, action.undelegate) elif name == "refund": - assert action.refund is not None # check_action + assert action.refund is not None # _check_action await layout.confirm_action_refund(ctx, action.refund) writers.write_action_refund(w, action.refund) elif name == "voteproducer": - assert action.vote_producer is not None # check_action + assert action.vote_producer is not None # _check_action await layout.confirm_action_voteproducer(ctx, action.vote_producer) writers.write_action_voteproducer(w, action.vote_producer) elif name == "updateauth": - assert action.update_auth is not None # check_action + assert action.update_auth is not None # _check_action await layout.confirm_action_updateauth(ctx, action.update_auth) writers.write_action_updateauth(w, action.update_auth) elif name == "deleteauth": - assert action.delete_auth is not None # check_action + assert action.delete_auth is not None # _check_action await layout.confirm_action_deleteauth(ctx, action.delete_auth) writers.write_action_deleteauth(w, action.delete_auth) elif name == "linkauth": - assert action.link_auth is not None # check_action + assert action.link_auth is not None # _check_action await layout.confirm_action_linkauth(ctx, action.link_auth) writers.write_action_linkauth(w, action.link_auth) elif name == "unlinkauth": - assert action.unlink_auth is not None # check_action + assert action.unlink_auth is not None # _check_action await layout.confirm_action_unlinkauth(ctx, action.unlink_auth) writers.write_action_unlinkauth(w, action.unlink_auth) elif name == "newaccount": - assert action.new_account is not None # check_action + assert action.new_account is not None # _check_action await layout.confirm_action_newaccount(ctx, action.new_account) writers.write_action_newaccount(w, action.new_account) else: raise ValueError("Unrecognized action type for eosio") elif name == "transfer": - assert action.transfer is not None # check_action + assert action.transfer is not None # _check_action await layout.confirm_action_transfer(ctx, action.transfer, account) writers.write_action_transfer(w, action.transfer) else: - await process_unknown_action(ctx, w, action) + await _process_unknown_action(ctx, w, action) writers.write_action_common(sha, action.common) writers.write_bytes_prefixed(sha, w) -async def process_unknown_action( +async def _process_unknown_action( ctx: wire.Context, w: Writer, action: EosTxActionAck ) -> None: - assert action.unknown is not None - checksum = HashWriter(sha256()) - writers.write_uvarint(checksum, action.unknown.data_size) - checksum.extend(action.unknown.data_chunk) + from trezor.crypto.hashlib import sha256 + from trezor.utils import HashWriter + from trezor.messages import EosTxActionAck, EosTxActionRequest + from .. import writers + from . import layout - writers.write_bytes_unchecked(w, action.unknown.data_chunk) - bytes_left = action.unknown.data_size - len(action.unknown.data_chunk) + unknown = action.unknown # local_cache_attribute + assert unknown is not None + data_chunk = unknown.data_chunk # local_cache_attribute + + checksum = HashWriter(sha256()) + writers.write_uvarint(checksum, unknown.data_size) + checksum.extend(data_chunk) + + writers.write_bytes_unchecked(w, data_chunk) + bytes_left = unknown.data_size - len(data_chunk) while bytes_left != 0: action = await ctx.call( EosTxActionRequest(data_size=bytes_left), EosTxActionAck ) - if action.unknown is None: + if unknown is None: raise ValueError("Bad response. Unknown struct expected.") - checksum.extend(action.unknown.data_chunk) - writers.write_bytes_unchecked(w, action.unknown.data_chunk) + checksum.extend(data_chunk) + writers.write_bytes_unchecked(w, data_chunk) - bytes_left -= len(action.unknown.data_chunk) + bytes_left -= len(data_chunk) if bytes_left < 0: raise ValueError("Bad response. Buffer overflow.") await layout.confirm_action_unknown(ctx, action.common, checksum.get_digest()) -def check_action(action: EosTxActionAck, name: str, account: str) -> bool: +def _check_action(action: EosTxActionAck, name: str, account: str) -> bool: if account == "eosio": return ( (name == "buyram" and action.buy_ram is not None) diff --git a/core/src/apps/eos/actions/layout.py b/core/src/apps/eos/actions/layout.py index 4526ac5c79..be46681a58 100644 --- a/core/src/apps/eos/actions/layout.py +++ b/core/src/apps/eos/actions/layout.py @@ -1,12 +1,14 @@ from typing import TYPE_CHECKING -from trezor import ui, wire +from trezor import ui from trezor.enums import ButtonRequestType from trezor.ui.layouts import confirm_properties -from .. import helpers +from ..helpers import eos_asset_to_string, eos_name_to_string if TYPE_CHECKING: + from typing import Iterable + from trezor.wire import Context from trezor.messages import ( EosActionBuyRam, EosActionBuyRamBytes, @@ -27,313 +29,294 @@ if TYPE_CHECKING: from trezor.ui.layouts import PropertyType -async def confirm_action_buyram(ctx: wire.Context, msg: EosActionBuyRam) -> None: +# Because icon and br_code are almost always the same +# (and also calling with positional arguments takes less space) +async def _confirm_properties( + ctx: Context, + br_type: str, + title: str, + props: Iterable[PropertyType], +) -> None: await confirm_properties( + ctx, + br_type, + title, + props, + icon=ui.ICON_CONFIRM, + br_code=ButtonRequestType.ConfirmOutput, + ) + + +async def confirm_action_buyram(ctx: Context, msg: EosActionBuyRam) -> None: + await _confirm_properties( ctx, "confirm_buyram", - title="Buy RAM", - props=[ - ("Payer:", helpers.eos_name_to_string(msg.payer)), - ("Receiver:", helpers.eos_name_to_string(msg.receiver)), - ("Amount:", helpers.eos_asset_to_string(msg.quantity)), - ], - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Buy RAM", + ( + ("Payer:", eos_name_to_string(msg.payer)), + ("Receiver:", eos_name_to_string(msg.receiver)), + ("Amount:", eos_asset_to_string(msg.quantity)), + ), ) -async def confirm_action_buyrambytes( - ctx: wire.Context, msg: EosActionBuyRamBytes -) -> None: - await confirm_properties( +async def confirm_action_buyrambytes(ctx: Context, msg: EosActionBuyRamBytes) -> None: + await _confirm_properties( ctx, "confirm_buyrambytes", - title="Buy RAM", - props=[ - ("Payer:", helpers.eos_name_to_string(msg.payer)), - ("Receiver:", helpers.eos_name_to_string(msg.receiver)), + "Buy RAM", + ( + ("Payer:", eos_name_to_string(msg.payer)), + ("Receiver:", eos_name_to_string(msg.receiver)), ("Bytes:", str(msg.bytes)), - ], - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + ), ) -async def confirm_action_delegate(ctx: wire.Context, msg: EosActionDelegate) -> None: +async def confirm_action_delegate(ctx: Context, msg: EosActionDelegate) -> None: props = [ - ("Sender:", helpers.eos_name_to_string(msg.sender)), - ("Receiver:", helpers.eos_name_to_string(msg.receiver)), - ("CPU:", helpers.eos_asset_to_string(msg.cpu_quantity)), - ("NET:", helpers.eos_asset_to_string(msg.net_quantity)), + ("Sender:", eos_name_to_string(msg.sender)), + ("Receiver:", eos_name_to_string(msg.receiver)), + ("CPU:", eos_asset_to_string(msg.cpu_quantity)), + ("NET:", eos_asset_to_string(msg.net_quantity)), ] + append = props.append # local_cache_attribute if msg.transfer: - props.append(("Transfer:", "Yes")) - props.append(("Receiver:", helpers.eos_name_to_string(msg.receiver))) + append(("Transfer:", "Yes")) + append(("Receiver:", eos_name_to_string(msg.receiver))) else: - props.append(("Transfer:", "No")) + append(("Transfer:", "No")) - await confirm_properties( + await _confirm_properties( ctx, "confirm_delegate", - title="Delegate", - props=props, - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Delegate", + props, ) -async def confirm_action_sellram(ctx: wire.Context, msg: EosActionSellRam) -> None: - await confirm_properties( +async def confirm_action_sellram(ctx: Context, msg: EosActionSellRam) -> None: + await _confirm_properties( ctx, "confirm_sellram", - title="Sell RAM", - props=[ - ("Receiver:", helpers.eos_name_to_string(msg.account)), + "Sell RAM", + ( + ("Receiver:", eos_name_to_string(msg.account)), ("Bytes:", str(msg.bytes)), - ], - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + ), ) -async def confirm_action_undelegate( - ctx: wire.Context, msg: EosActionUndelegate -) -> None: - await confirm_properties( +async def confirm_action_undelegate(ctx: Context, msg: EosActionUndelegate) -> None: + await _confirm_properties( ctx, "confirm_undelegate", - title="Undelegate", - props=[ - ("Sender:", helpers.eos_name_to_string(msg.sender)), - ("Receiver:", helpers.eos_name_to_string(msg.receiver)), - ("CPU:", helpers.eos_asset_to_string(msg.cpu_quantity)), - ("NET:", helpers.eos_asset_to_string(msg.net_quantity)), - ], - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Undelegate", + ( + ("Sender:", eos_name_to_string(msg.sender)), + ("Receiver:", eos_name_to_string(msg.receiver)), + ("CPU:", eos_asset_to_string(msg.cpu_quantity)), + ("NET:", eos_asset_to_string(msg.net_quantity)), + ), ) -async def confirm_action_refund(ctx: wire.Context, msg: EosActionRefund) -> None: - await confirm_properties( +async def confirm_action_refund(ctx: Context, msg: EosActionRefund) -> None: + await _confirm_properties( ctx, "confirm_refund", - title="Refund", - props=[ - ("Owner:", helpers.eos_name_to_string(msg.owner)), - ], - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Refund", + (("Owner:", eos_name_to_string(msg.owner)),), ) -async def confirm_action_voteproducer( - ctx: wire.Context, msg: EosActionVoteProducer -) -> None: - if msg.proxy and not msg.producers: +async def confirm_action_voteproducer(ctx: Context, msg: EosActionVoteProducer) -> None: + producers = msg.producers # local_cache_attribute + + if msg.proxy and not producers: # PROXY - await confirm_properties( + await _confirm_properties( ctx, "confirm_voteproducer", - title="Vote for proxy", - props=[ - ("Voter:", helpers.eos_name_to_string(msg.voter)), - ("Proxy:", helpers.eos_name_to_string(msg.proxy)), - ], - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Vote for proxy", + ( + ("Voter:", eos_name_to_string(msg.voter)), + ("Proxy:", eos_name_to_string(msg.proxy)), + ), ) - elif msg.producers: + elif producers: # PRODUCERS - await confirm_properties( + await _confirm_properties( ctx, "confirm_voteproducer", - title="Vote for producers", - props=( - (f"{wi:2d}. {helpers.eos_name_to_string(producer)}", None) - for wi, producer in enumerate(msg.producers, 1) + "Vote for producers", + ( + (f"{wi:2d}. {eos_name_to_string(producer)}", None) + for wi, producer in enumerate(producers, 1) ), - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, ) else: # Cancel vote - await confirm_properties( + await _confirm_properties( ctx, "confirm_voteproducer", - title="Cancel vote", - props=[ - ("Voter:", helpers.eos_name_to_string(msg.voter)), - ], - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Cancel vote", + (("Voter:", eos_name_to_string(msg.voter)),), ) async def confirm_action_transfer( - ctx: wire.Context, msg: EosActionTransfer, account: str + ctx: Context, msg: EosActionTransfer, account: str ) -> None: props = [ - ("From:", helpers.eos_name_to_string(msg.sender)), - ("To:", helpers.eos_name_to_string(msg.receiver)), - ("Amount:", helpers.eos_asset_to_string(msg.quantity)), + ("From:", eos_name_to_string(msg.sender)), + ("To:", eos_name_to_string(msg.receiver)), + ("Amount:", eos_asset_to_string(msg.quantity)), ("Contract:", account), ] if msg.memo is not None: props.append(("Memo", msg.memo[:512])) - await confirm_properties( + await _confirm_properties( ctx, "confirm_transfer", - title="Transfer", - props=props, - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Transfer", + props, ) -async def confirm_action_updateauth( - ctx: wire.Context, msg: EosActionUpdateAuth -) -> None: +async def confirm_action_updateauth(ctx: Context, msg: EosActionUpdateAuth) -> None: props: list[PropertyType] = [ - ("Account:", helpers.eos_name_to_string(msg.account)), - ("Permission:", helpers.eos_name_to_string(msg.permission)), - ("Parent:", helpers.eos_name_to_string(msg.parent)), + ("Account:", eos_name_to_string(msg.account)), + ("Permission:", eos_name_to_string(msg.permission)), + ("Parent:", eos_name_to_string(msg.parent)), ] props.extend(authorization_fields(msg.auth)) - await confirm_properties( + await _confirm_properties( ctx, "confirm_updateauth", - title="Update Auth", - props=props, - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Update Auth", + props, ) -async def confirm_action_deleteauth( - ctx: wire.Context, msg: EosActionDeleteAuth -) -> None: - await confirm_properties( +async def confirm_action_deleteauth(ctx: Context, msg: EosActionDeleteAuth) -> None: + await _confirm_properties( ctx, "confirm_deleteauth", - title="Delete Auth", - props=[ - ("Account:", helpers.eos_name_to_string(msg.account)), - ("Permission:", helpers.eos_name_to_string(msg.permission)), - ], - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Delete Auth", + ( + ("Account:", eos_name_to_string(msg.account)), + ("Permission:", eos_name_to_string(msg.permission)), + ), ) -async def confirm_action_linkauth(ctx: wire.Context, msg: EosActionLinkAuth) -> None: - await confirm_properties( +async def confirm_action_linkauth(ctx: Context, msg: EosActionLinkAuth) -> None: + await _confirm_properties( ctx, "confirm_linkauth", - title="Link Auth", - props=[ - ("Account:", helpers.eos_name_to_string(msg.account)), - ("Code:", helpers.eos_name_to_string(msg.code)), - ("Type:", helpers.eos_name_to_string(msg.type)), - ("Requirement:", helpers.eos_name_to_string(msg.requirement)), - ], - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Link Auth", + ( + ("Account:", eos_name_to_string(msg.account)), + ("Code:", eos_name_to_string(msg.code)), + ("Type:", eos_name_to_string(msg.type)), + ("Requirement:", eos_name_to_string(msg.requirement)), + ), ) -async def confirm_action_unlinkauth( - ctx: wire.Context, msg: EosActionUnlinkAuth -) -> None: - await confirm_properties( +async def confirm_action_unlinkauth(ctx: Context, msg: EosActionUnlinkAuth) -> None: + await _confirm_properties( ctx, "confirm_unlinkauth", - title="Unlink Auth", - props=[ - ("Account:", helpers.eos_name_to_string(msg.account)), - ("Code:", helpers.eos_name_to_string(msg.code)), - ("Type:", helpers.eos_name_to_string(msg.type)), - ], - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "Unlink Auth", + ( + ("Account:", eos_name_to_string(msg.account)), + ("Code:", eos_name_to_string(msg.code)), + ("Type:", eos_name_to_string(msg.type)), + ), ) -async def confirm_action_newaccount( - ctx: wire.Context, msg: EosActionNewAccount -) -> None: +async def confirm_action_newaccount(ctx: Context, msg: EosActionNewAccount) -> None: props: list[PropertyType] = [ - ("Creator:", helpers.eos_name_to_string(msg.creator)), - ("Name:", helpers.eos_name_to_string(msg.name)), + ("Creator:", eos_name_to_string(msg.creator)), + ("Name:", eos_name_to_string(msg.name)), ] props.extend(authorization_fields(msg.owner)) props.extend(authorization_fields(msg.active)) - await confirm_properties( + await _confirm_properties( ctx, "confirm_newaccount", - title="New Account", - props=props, - icon=ui.ICON_CONFIRM, - br_code=ButtonRequestType.ConfirmOutput, + "New Account", + props, ) async def confirm_action_unknown( - ctx: wire.Context, action: EosActionCommon, checksum: bytes + ctx: Context, action: EosActionCommon, checksum: bytes ) -> None: await confirm_properties( ctx, "confirm_unknown", - title="Arbitrary data", - props=[ - ("Contract:", helpers.eos_name_to_string(action.account)), - ("Action Name:", helpers.eos_name_to_string(action.name)), + "Arbitrary data", + ( + ("Contract:", eos_name_to_string(action.account)), + ("Action Name:", eos_name_to_string(action.name)), ("Checksum:", checksum), - ], - icon=ui.ICON_WIPE, - icon_color=ui.RED, + ), + ui.ICON_WIPE, + ui.RED, br_code=ButtonRequestType.ConfirmOutput, ) def authorization_fields(auth: EosAuthorization) -> list[PropertyType]: + from trezor.wire import DataError + from ..helpers import public_key_to_wif + fields: list[PropertyType] = [] - fields.append(("Threshold:", str(auth.threshold))) + append = fields.append # local_cache_attribute + + append(("Threshold:", str(auth.threshold))) + + # NOTE: getting rid of f-strings saved almost 100 bytes for i, key in enumerate(auth.keys, 1): if key.key is None: - raise wire.DataError("Key must be provided explicitly.") + raise DataError("Key must be provided explicitly.") - _key = helpers.public_key_to_wif(bytes(key.key)) + _key = public_key_to_wif(bytes(key.key)) _weight = str(key.weight) - header = f"Key #{i}:" - w_header = f"Key #{i} Weight:" + header = "Key #" + str(i) + ":" + w_header = "Key #" + str(i) + " Weight:" - fields.append((header, _key)) - fields.append((w_header, _weight)) + append((header, _key)) + append((w_header, _weight)) for i, account in enumerate(auth.accounts, 1): - _account = helpers.eos_name_to_string(account.account.actor) - _permission = helpers.eos_name_to_string(account.account.permission) + _account = eos_name_to_string(account.account.actor) + _permission = eos_name_to_string(account.account.permission) - a_header = f"Account #{i}:" - p_header = f"Acc Permission #{i}:" - w_header = f"Account #{i} weight:" + i = str(i) + a_header = "Account #" + i + ":" + p_header = "Acc Permission #" + i + ":" + w_header = "Account #" + i + " weight:" - fields.append((a_header, _account)) - fields.append((p_header, _permission)) - fields.append((w_header, str(account.weight))) + append((a_header, _account)) + append((p_header, _permission)) + append((w_header, str(account.weight))) for i, wait in enumerate(auth.waits, 1): _wait = str(wait.wait_sec) _weight = str(wait.weight) - header = f"Delay #{i}" - w_header = f"Delay #{i} weight:" - fields.append((header, f"{_wait} sec")) - fields.append((w_header, _weight)) + header = "Delay #" + str(i) + w_header = header + " weight:" + append((header, _wait + " sec")) + append((w_header, _weight)) return fields diff --git a/core/src/apps/eos/get_public_key.py b/core/src/apps/eos/get_public_key.py index 0a1d1a60a4..51ffcec478 100644 --- a/core/src/apps/eos/get_public_key.py +++ b/core/src/apps/eos/get_public_key.py @@ -1,35 +1,30 @@ from typing import TYPE_CHECKING -from trezor import wire -from trezor.crypto.curve import secp256k1 -from trezor.messages import EosPublicKey - -from apps.common import paths -from apps.common.keychain import Keychain, auto_keychain - -from .helpers import public_key_to_wif -from .layout import require_get_public_key +from apps.common.keychain import auto_keychain if TYPE_CHECKING: - from trezor.messages import EosGetPublicKey - from trezor.crypto import bip32 - - -def _get_public_key(node: bip32.HDNode) -> tuple[str, bytes]: - seckey = node.private_key() - public_key = secp256k1.publickey(seckey, True) - wif = public_key_to_wif(public_key) - return wif, public_key + from trezor.messages import EosGetPublicKey, EosPublicKey + from apps.common.keychain import Keychain + from trezor.wire import Context @auto_keychain(__name__) async def get_public_key( - ctx: wire.Context, msg: EosGetPublicKey, keychain: Keychain + ctx: Context, msg: EosGetPublicKey, keychain: Keychain ) -> EosPublicKey: + from trezor.crypto.curve import secp256k1 + from trezor.messages import EosPublicKey + from apps.common import paths + from .helpers import public_key_to_wif + from .layout import require_get_public_key + await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) - wif, public_key = _get_public_key(node) + + public_key = secp256k1.publickey(node.private_key(), True) + wif = public_key_to_wif(public_key) + if msg.show_display: await require_get_public_key(ctx, wif) return EosPublicKey(wif_public_key=wif, raw_public_key=public_key) diff --git a/core/src/apps/eos/helpers.py b/core/src/apps/eos/helpers.py index a4ddd0c515..08f103921b 100644 --- a/core/src/apps/eos/helpers.py +++ b/core/src/apps/eos/helpers.py @@ -1,13 +1,12 @@ from typing import TYPE_CHECKING -from trezor import wire -from trezor.crypto import base58 - if TYPE_CHECKING: from trezor.messages import EosAsset def base58_encode(prefix: str, sig_prefix: str, data: bytes) -> str: + from trezor.crypto import base58 + b58 = base58.encode(data + base58.ripemd160_32(data + sig_prefix.encode())) if sig_prefix: return prefix + sig_prefix + "_" + b58 @@ -45,11 +44,13 @@ def eos_asset_to_string(asset: EosAsset) -> str: def public_key_to_wif(pub_key: bytes) -> str: + from trezor.wire import DataError + if pub_key[0] == 0x04 and len(pub_key) == 65: head = b"\x03" if pub_key[64] & 0x01 else b"\x02" compressed_pub_key = head + pub_key[1:33] elif pub_key[0] in [0x02, 0x03] and len(pub_key) == 33: compressed_pub_key = pub_key else: - raise wire.DataError("invalid public key") + raise DataError("invalid public key") return base58_encode("EOS", "", compressed_pub_key) diff --git a/core/src/apps/eos/layout.py b/core/src/apps/eos/layout.py index 00e0b7eaba..99d883447a 100644 --- a/core/src/apps/eos/layout.py +++ b/core/src/apps/eos/layout.py @@ -1,18 +1,25 @@ -from trezor import ui, wire -from trezor.enums import ButtonRequestType -from trezor.strings import format_plural -from trezor.ui.layouts import confirm_action, show_pubkey +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from trezor.wire import Context -async def require_get_public_key(ctx: wire.Context, public_key: str) -> None: +async def require_get_public_key(ctx: Context, public_key: str) -> None: + from trezor.ui.layouts import show_pubkey + await show_pubkey(ctx, public_key) -async def require_sign_tx(ctx: wire.Context, num_actions: int) -> None: +async def require_sign_tx(ctx: Context, num_actions: int) -> None: + from trezor import ui + from trezor.enums import ButtonRequestType + from trezor.strings import format_plural + from trezor.ui.layouts import confirm_action + await confirm_action( ctx, "confirm_tx", - title="Sign transaction", + "Sign transaction", description="You are about to sign {}.", description_param=format_plural("{count} {plural}", num_actions, "action"), icon=ui.ICON_SEND, diff --git a/core/src/apps/eos/sign_tx.py b/core/src/apps/eos/sign_tx.py index 990b6bb535..c8ec557d54 100644 --- a/core/src/apps/eos/sign_tx.py +++ b/core/src/apps/eos/sign_tx.py @@ -1,36 +1,50 @@ from typing import TYPE_CHECKING -from trezor import wire -from trezor.crypto.curve import secp256k1 -from trezor.crypto.hashlib import sha256 -from trezor.messages import EosSignedTx, EosTxActionAck, EosTxActionRequest -from trezor.utils import HashWriter - -from apps.common import paths -from apps.common.keychain import Keychain, auto_keychain - -from . import writers -from .actions import process_action -from .helpers import base58_encode -from .layout import require_sign_tx +from apps.common.keychain import auto_keychain if TYPE_CHECKING: - from trezor.messages import EosSignTx + from trezor.wire import Context + from trezor.messages import EosSignTx, EosSignedTx + from apps.common.keychain import Keychain @auto_keychain(__name__) -async def sign_tx(ctx: wire.Context, msg: EosSignTx, keychain: Keychain) -> EosSignedTx: - if not msg.num_actions: - raise wire.DataError("No actions") +async def sign_tx(ctx: Context, msg: EosSignTx, keychain: Keychain) -> EosSignedTx: + from trezor.wire import DataError + from trezor.crypto.curve import secp256k1 + from trezor.crypto.hashlib import sha256 + from trezor.messages import EosSignedTx, EosTxActionAck, EosTxActionRequest + from trezor.utils import HashWriter + from apps.common import paths + from .writers import write_uvarint, write_header, write_bytes_fixed + from .actions import process_action + from .helpers import base58_encode + from .layout import require_sign_tx + + num_actions = msg.num_actions # local_cache_attribute + + if not num_actions: + raise DataError("No actions") await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) sha = HashWriter(sha256()) - await _init(ctx, sha, msg) - await _actions(ctx, sha, msg.num_actions) - writers.write_uvarint(sha, 0) - writers.write_bytes_fixed(sha, bytearray(32), 32) + + # init + write_bytes_fixed(sha, msg.chain_id, 32) + write_header(sha, msg.header) + write_uvarint(sha, 0) + write_uvarint(sha, num_actions) + await require_sign_tx(ctx, num_actions) + + # actions + for _ in range(num_actions): + action = await ctx.call(EosTxActionRequest(), EosTxActionAck) + await process_action(ctx, sha, action) + + write_uvarint(sha, 0) + write_bytes_fixed(sha, bytearray(32), 32) digest = sha.get_digest() signature = secp256k1.sign( @@ -38,18 +52,3 @@ async def sign_tx(ctx: wire.Context, msg: EosSignTx, keychain: Keychain) -> EosS ) return EosSignedTx(signature=base58_encode("SIG_", "K1", signature)) - - -async def _init(ctx: wire.Context, sha: HashWriter, msg: EosSignTx) -> None: - writers.write_bytes_fixed(sha, msg.chain_id, 32) - writers.write_header(sha, msg.header) - writers.write_uvarint(sha, 0) - writers.write_uvarint(sha, msg.num_actions) - - await require_sign_tx(ctx, msg.num_actions) - - -async def _actions(ctx: wire.Context, sha: HashWriter, num_actions: int) -> None: - for _ in range(num_actions): - action = await ctx.call(EosTxActionRequest(), EosTxActionAck) - await process_action(ctx, sha, action) diff --git a/core/src/apps/eos/writers.py b/core/src/apps/eos/writers.py index d23c52cb92..df0525b675 100644 --- a/core/src/apps/eos/writers.py +++ b/core/src/apps/eos/writers.py @@ -1,7 +1,5 @@ from typing import TYPE_CHECKING -from trezor import wire - from apps.common.writers import ( write_bytes_fixed, write_bytes_unchecked, @@ -36,11 +34,13 @@ if TYPE_CHECKING: def write_auth(w: Writer, auth: EosAuthorization) -> None: + from trezor.wire import DataError + write_uint32_le(w, auth.threshold) write_uvarint(w, len(auth.keys)) for key in auth.keys: if key.key is None: - raise wire.DataError("Key must be provided explicitly.") + raise DataError("Key must be provided explicitly.") write_uvarint(w, key.type) write_bytes_fixed(w, key.key, 33) write_uint16_le(w, key.weight) @@ -91,14 +91,13 @@ def write_action_sellram(w: Writer, msg: EosActionSellRam) -> None: def write_action_delegate(w: Writer, msg: EosActionDelegate) -> None: - write_uint64_le(w, msg.sender) - write_uint64_le(w, msg.receiver) - write_asset(w, msg.net_quantity) - write_asset(w, msg.cpu_quantity) + write_action_undelegate(w, msg) write_uint8(w, 1 if msg.transfer else 0) -def write_action_undelegate(w: Writer, msg: EosActionUndelegate) -> None: +def write_action_undelegate( + w: Writer, msg: EosActionUndelegate | EosActionDelegate +) -> None: write_uint64_le(w, msg.sender) write_uint64_le(w, msg.receiver) write_asset(w, msg.net_quantity) @@ -118,25 +117,26 @@ def write_action_voteproducer(w: Writer, msg: EosActionVoteProducer) -> None: def write_action_updateauth(w: Writer, msg: EosActionUpdateAuth) -> None: - write_uint64_le(w, msg.account) - write_uint64_le(w, msg.permission) + write_action_deleteauth(w, msg) write_uint64_le(w, msg.parent) write_auth(w, msg.auth) -def write_action_deleteauth(w: Writer, msg: EosActionDeleteAuth) -> None: +def write_action_deleteauth( + w: Writer, msg: EosActionDeleteAuth | EosActionUpdateAuth +) -> None: write_uint64_le(w, msg.account) write_uint64_le(w, msg.permission) def write_action_linkauth(w: Writer, msg: EosActionLinkAuth) -> None: - write_uint64_le(w, msg.account) - write_uint64_le(w, msg.code) - write_uint64_le(w, msg.type) + write_action_unlinkauth(w, msg) write_uint64_le(w, msg.requirement) -def write_action_unlinkauth(w: Writer, msg: EosActionUnlinkAuth) -> None: +def write_action_unlinkauth( + w: Writer, msg: EosActionUnlinkAuth | EosActionLinkAuth +) -> None: write_uint64_le(w, msg.account) write_uint64_le(w, msg.code) write_uint64_le(w, msg.type) diff --git a/core/tests/test_apps.eos.check_action.py b/core/tests/test_apps.eos.check_action.py index 87ca4c6815..436dc107bb 100644 --- a/core/tests/test_apps.eos.check_action.py +++ b/core/tests/test_apps.eos.check_action.py @@ -1,7 +1,7 @@ from common import * if not utils.BITCOIN_ONLY: - from apps.eos.actions import check_action + from apps.eos.actions import _check_action from trezor.messages import EosTxActionAck @@ -9,43 +9,43 @@ if not utils.BITCOIN_ONLY: class TestEosActions(unittest.TestCase): def test_check_action(self): # return True - self.assertEqual(check_action(EosTxActionAck(common=object(), buy_ram=object()), 'buyram', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), buy_ram_bytes=object()), 'buyrambytes', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), sell_ram=object()), 'sellram', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), delegate=object()), 'delegatebw', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), undelegate=object()), 'undelegatebw', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), refund=object()), 'refund', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), vote_producer=object()), 'voteproducer', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), update_auth=object()), 'updateauth', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), delete_auth=object()), 'deleteauth', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), link_auth=object()), 'linkauth', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), unlink_auth=object()), 'unlinkauth', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), new_account=object()), 'newaccount', 'eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), transfer=object()), 'transfer', 'not_eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), unknown=[]), 'unknown', 'not_eosio'), True) - self.assertEqual(check_action(EosTxActionAck(common=object(), unknown=[]), 'buyram', 'buygoods'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), buy_ram=object()), 'buyram', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), buy_ram_bytes=object()), 'buyrambytes', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), sell_ram=object()), 'sellram', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), delegate=object()), 'delegatebw', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), undelegate=object()), 'undelegatebw', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), refund=object()), 'refund', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), vote_producer=object()), 'voteproducer', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), update_auth=object()), 'updateauth', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), delete_auth=object()), 'deleteauth', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), link_auth=object()), 'linkauth', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), unlink_auth=object()), 'unlinkauth', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), new_account=object()), 'newaccount', 'eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), transfer=object()), 'transfer', 'not_eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), unknown=[]), 'unknown', 'not_eosio'), True) + self.assertEqual(_check_action(EosTxActionAck(common=object(), unknown=[]), 'buyram', 'buygoods'), True) # returns False - self.assertEqual(check_action(EosTxActionAck(common=object(), buy_ram=object()), 'buyram', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object()), 'buyram', 'eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), buy_ram_bytes=object()), 'buyrambytes', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), sell_ram=object()), 'sellram', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), delegate=object()), 'delegatebw', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), undelegate=object()), 'undelegatebw', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), refund=object()), 'refund', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object()), 'refund', 'eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), vote_producer=object()), 'voteproducer', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), update_auth=object()), 'updateauth', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), delete_auth=object()), 'deleteauth', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), link_auth=object()), 'linkauth', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), unlink_auth=object()), 'unlinkauth', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object()), 'unlinkauth', 'eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), new_account=object()), 'newaccount', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), transfer=object()), 'transfer', 'eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object()), 'unknown', 'not_eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), buy_ram=object()), 'test', 'eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), unknown=[]), 'buyram', 'eosio'), False) - self.assertEqual(check_action(EosTxActionAck(common=object(), unknown=[]), 'transfer', 'loveme'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), buy_ram=object()), 'buyram', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object()), 'buyram', 'eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), buy_ram_bytes=object()), 'buyrambytes', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), sell_ram=object()), 'sellram', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), delegate=object()), 'delegatebw', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), undelegate=object()), 'undelegatebw', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), refund=object()), 'refund', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object()), 'refund', 'eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), vote_producer=object()), 'voteproducer', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), update_auth=object()), 'updateauth', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), delete_auth=object()), 'deleteauth', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), link_auth=object()), 'linkauth', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), unlink_auth=object()), 'unlinkauth', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object()), 'unlinkauth', 'eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), new_account=object()), 'newaccount', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), transfer=object()), 'transfer', 'eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object()), 'unknown', 'not_eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), buy_ram=object()), 'test', 'eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), unknown=[]), 'buyram', 'eosio'), False) + self.assertEqual(_check_action(EosTxActionAck(common=object(), unknown=[]), 'transfer', 'loveme'), False) if __name__ == '__main__': diff --git a/core/tests/test_apps.eos.get_public_key.py b/core/tests/test_apps.eos.get_public_key.py index 517aeb16d5..9e206c476d 100644 --- a/core/tests/test_apps.eos.get_public_key.py +++ b/core/tests/test_apps.eos.get_public_key.py @@ -1,13 +1,20 @@ from common import * from trezor.crypto import bip32, bip39 -from apps.common.paths import HARDENED +from trezor.crypto.curve import secp256k1 if not utils.BITCOIN_ONLY: - from apps.eos.get_public_key import _get_public_key from apps.eos.helpers import public_key_to_wif +# NOTE: copy-pasted from apps.eos.get_public_key +def _get_public_key(node: bip32.HDNode) -> tuple[str, bytes]: + seckey = node.private_key() + public_key = secp256k1.publickey(seckey, True) + wif = public_key_to_wif(public_key) + return wif, public_key + + @unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin") class TestEosGetPublicKey(unittest.TestCase): def test_get_public_key_scheme(self):