diff --git a/core/src/apps/nem/get_address.py b/core/src/apps/nem/get_address.py index e00f08c66..1d7ecfec6 100644 --- a/core/src/apps/nem/get_address.py +++ b/core/src/apps/nem/get_address.py @@ -1,41 +1,42 @@ from typing import TYPE_CHECKING -from trezor.messages import NEMAddress -from trezor.ui.layouts import show_address - from apps.common.keychain import with_slip44_keychain -from apps.common.paths import address_n_to_str, validate_path from . import CURVE, PATTERNS, SLIP44_ID -from .helpers import check_path, get_network_str -from .validators import validate_network if TYPE_CHECKING: from apps.common.keychain import Keychain from trezor.wire import Context - from trezor.messages import NEMGetAddress + from trezor.messages import NEMGetAddress, NEMAddress @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def get_address( ctx: Context, msg: NEMGetAddress, keychain: Keychain ) -> NEMAddress: - validate_network(msg.network) - await validate_path( - ctx, keychain, msg.address_n, check_path(msg.address_n, msg.network) - ) + from trezor.messages import NEMAddress + from trezor.ui.layouts import show_address + from apps.common.paths import address_n_to_str, validate_path + from .helpers import check_path, get_network_str + from .validators import validate_network - node = keychain.derive(msg.address_n) - address = node.nem_address(msg.network) + address_n = msg.address_n # local_cache_attribute + network = msg.network # local_cache_attribute + + validate_network(network) + await validate_path(ctx, keychain, address_n, check_path(address_n, network)) + + node = keychain.derive(address_n) + address = node.nem_address(network) if msg.show_display: - title = address_n_to_str(msg.address_n) + title = address_n_to_str(address_n) await show_address( ctx, - address=address, + address, case_sensitive=False, title=title, - network=get_network_str(msg.network), + network=get_network_str(network), ) return NEMAddress(address=address) diff --git a/core/src/apps/nem/helpers.py b/core/src/apps/nem/helpers.py index 609a3480c..7471ac106 100644 --- a/core/src/apps/nem/helpers.py +++ b/core/src/apps/nem/helpers.py @@ -1,8 +1,9 @@ from micropython import const +from typing import TYPE_CHECKING -from apps.common import paths +if TYPE_CHECKING: + from apps.common import paths -from . import SLIP44_ID NEM_NETWORK_MAINNET = const(0x68) NEM_NETWORK_TESTNET = const(0x98) @@ -44,6 +45,9 @@ def get_network_str(network: int) -> str: def check_path(path: paths.Bip32Path, network: int) -> bool: """Validates that the appropriate coin_type is set for the given network.""" + from apps.common import paths + from . import SLIP44_ID + if len(path) < 2: return False diff --git a/core/src/apps/nem/layout.py b/core/src/apps/nem/layout.py index 1e93bc849..db4c0b0f9 100644 --- a/core/src/apps/nem/layout.py +++ b/core/src/apps/nem/layout.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING from trezor.enums import ButtonRequestType from trezor.strings import format_amount -from trezor.ui.layouts import confirm_metadata, confirm_properties +from trezor.ui.layouts import confirm_metadata from .helpers import NEM_MAX_DIVISIBILITY @@ -14,8 +14,8 @@ async def require_confirm_text(ctx: Context, action: str) -> None: await confirm_metadata( ctx, "confirm_nem", - title="Confirm action", - content=action, + "Confirm action", + action, hide_continue=True, br_code=ButtonRequestType.ConfirmOutput, ) @@ -25,20 +25,22 @@ async def require_confirm_fee(ctx: Context, action: str, fee: int) -> None: await confirm_metadata( ctx, "confirm_fee", - title="Confirm fee", - content=action + "\n{}", - param=f"{format_amount(fee, NEM_MAX_DIVISIBILITY)} XEM", + "Confirm fee", + action + "\n{}", + f"{format_amount(fee, NEM_MAX_DIVISIBILITY)} XEM", + ButtonRequestType.ConfirmOutput, hide_continue=True, - br_code=ButtonRequestType.ConfirmOutput, ) async def require_confirm_content(ctx: Context, headline: str, content: list) -> None: + from trezor.ui.layouts import confirm_properties + await confirm_properties( ctx, "confirm_content", - title=headline, - props=content, + headline, + content, ) @@ -47,9 +49,9 @@ async def require_confirm_final(ctx: Context, fee: int) -> None: await confirm_metadata( ctx, "confirm_final", - title="Final confirm", - content="Sign this transaction\n{}\nfor network fee?", - param=f"and pay {format_amount(fee, NEM_MAX_DIVISIBILITY)} XEM", + "Final confirm", + "Sign this transaction\n{}\nfor network fee?", + f"and pay {format_amount(fee, NEM_MAX_DIVISIBILITY)} XEM", hide_continue=True, hold=True, ) diff --git a/core/src/apps/nem/mosaic/helpers.py b/core/src/apps/nem/mosaic/helpers.py index e7a233554..e9877e9f5 100644 --- a/core/src/apps/nem/mosaic/helpers.py +++ b/core/src/apps/nem/mosaic/helpers.py @@ -1,7 +1,5 @@ from typing import TYPE_CHECKING -from .nem_mosaics import mosaics_iterator - if TYPE_CHECKING: from trezor.messages import NEMMosaic @@ -11,6 +9,8 @@ if TYPE_CHECKING: def get_mosaic_definition( namespace_name: str, mosaic_name: str, network: int ) -> Mosaic | None: + from .nem_mosaics import mosaics_iterator + for mosaic in mosaics_iterator(): if namespace_name == mosaic.namespace and mosaic_name == mosaic.mosaic: if (mosaic.networks is None) or (network in mosaic.networks): diff --git a/core/src/apps/nem/mosaic/layout.py b/core/src/apps/nem/mosaic/layout.py index 0b40dd3ff..950380124 100644 --- a/core/src/apps/nem/mosaic/layout.py +++ b/core/src/apps/nem/mosaic/layout.py @@ -1,15 +1,6 @@ from typing import TYPE_CHECKING -from trezor import ui -from trezor.enums import NEMMosaicLevy, NEMSupplyChangeType -from trezor.ui.layouts import confirm_properties - -from ..layout import ( - require_confirm_content, - require_confirm_fee, - require_confirm_final, - require_confirm_text, -) +from ..layout import require_confirm_content, require_confirm_final if TYPE_CHECKING: from trezor.messages import ( @@ -24,8 +15,14 @@ if TYPE_CHECKING: async def ask_mosaic_creation( ctx: Context, common: NEMTransactionCommon, creation: NEMMosaicCreation ) -> None: - await require_confirm_content(ctx, "Create mosaic", _creation_message(creation)) - await require_confirm_properties(ctx, creation.definition) + from ..layout import require_confirm_fee + + creation_message = [ + ("Create mosaic", creation.definition.mosaic), + ("under namespace", creation.definition.namespace), + ] + await require_confirm_content(ctx, "Create mosaic", creation_message) + await _require_confirm_properties(ctx, creation.definition) await require_confirm_fee(ctx, "Confirm creation fee", creation.fee) await require_confirm_final(ctx, common.fee) @@ -34,57 +31,49 @@ async def ask_mosaic_creation( async def ask_supply_change( ctx: Context, common: NEMTransactionCommon, change: NEMMosaicSupplyChange ) -> None: - await require_confirm_content(ctx, "Supply change", _supply_message(change)) + from trezor.enums import NEMSupplyChangeType + from ..layout import require_confirm_text + + supply_message = [ + ("Modify supply for", change.mosaic), + ("under namespace", change.namespace), + ] + await require_confirm_content(ctx, "Supply change", supply_message) if change.type == NEMSupplyChangeType.SupplyChange_Decrease: - msg = "Decrease supply by " + str(change.delta) + " whole units?" + action = "Decrease" elif change.type == NEMSupplyChangeType.SupplyChange_Increase: - msg = "Increase supply by " + str(change.delta) + " whole units?" + action = "Increase" else: raise ValueError("Invalid supply change type") - await require_confirm_text(ctx, msg) + await require_confirm_text(ctx, f"{action} supply by {change.delta} whole units?") await require_confirm_final(ctx, common.fee) -def _creation_message(mosaic_creation: NEMMosaicCreation) -> list[tuple[str, str]]: - return [ - ("Create mosaic", mosaic_creation.definition.mosaic), - ("under namespace", mosaic_creation.definition.namespace), - ] - - -def _supply_message(supply_change: NEMMosaicSupplyChange) -> list[tuple[str, str]]: - return [ - ("Modify supply for", supply_change.mosaic), - ("under namespace", supply_change.namespace), - ] - - -async def require_confirm_properties( +async def _require_confirm_properties( ctx: Context, definition: NEMMosaicDefinition ) -> None: + from trezor.enums import NEMMosaicLevy + from trezor import ui + from trezor.ui.layouts import confirm_properties + properties = [] + append = properties.append # local_cache_attribute # description if definition.description: - properties.append(("Description:", definition.description)) + append(("Description:", definition.description)) # transferable - if definition.transferable: - transferable = "Yes" - else: - transferable = "No" - properties.append(("Transferable?", transferable)) + transferable = "Yes" if definition.transferable else "No" + append(("Transferable?", transferable)) # mutable_supply - if definition.mutable_supply: - imm = "mutable" - else: - imm = "immutable" + imm = "mutable" if definition.mutable_supply else "immutable" if definition.supply: - properties.append(("Initial supply:", str(definition.supply) + "\n" + imm)) + append(("Initial supply:", str(definition.supply) + "\n" + imm)) else: - properties.append(("Initial supply:", imm)) + append(("Initial supply:", imm)) # levy if definition.levy: @@ -93,24 +82,25 @@ async def require_confirm_properties( assert definition.levy_namespace is not None assert definition.levy_mosaic is not None - properties.append(("Levy recipient:", definition.levy_address)) + append(("Levy recipient:", definition.levy_address)) - properties.append(("Levy fee:", str(definition.fee))) - properties.append(("Levy divisibility:", str(definition.divisibility))) + append(("Levy fee:", str(definition.fee))) + append(("Levy divisibility:", str(definition.divisibility))) - properties.append(("Levy namespace:", definition.levy_namespace)) - properties.append(("Levy mosaic:", definition.levy_mosaic)) + append(("Levy namespace:", definition.levy_namespace)) + append(("Levy mosaic:", definition.levy_mosaic)) - if definition.levy == NEMMosaicLevy.MosaicLevy_Absolute: - levy_type = "absolute" - else: - levy_type = "percentile" - properties.append(("Levy type:", levy_type)) + levy_type = ( + "absolute" + if definition.levy == NEMMosaicLevy.MosaicLevy_Absolute + else "percentile" + ) + append(("Levy type:", levy_type)) await confirm_properties( ctx, "confirm_properties", - title="Confirm properties", - props=properties, + "Confirm properties", + properties, icon_color=ui.ORANGE_ICON, ) diff --git a/core/src/apps/nem/mosaic/nem_mosaics.py b/core/src/apps/nem/mosaic/nem_mosaics.py index 3b428b0bc..e57393262 100644 --- a/core/src/apps/nem/mosaic/nem_mosaics.py +++ b/core/src/apps/nem/mosaic/nem_mosaics.py @@ -2,6 +2,9 @@ # (by running `make templates` in `core`) # do not edit manually! +# NOTE: not supplying the kwargs saves 120 bytes of code size +# `networks` needs kwarg as `levy` above is optional + from typing import Iterator from trezor.enums import NEMMosaicLevy @@ -43,61 +46,65 @@ class Mosaic: def mosaics_iterator() -> Iterator[Mosaic]: yield Mosaic( - name="NEM", - ticker=" XEM", - namespace="nem", - mosaic="xem", - divisibility=6, + "NEM", # name + " XEM", # ticker + "nem", # namespace + "xem", # mosaic + 6, # divisibility + None, # levy ) yield Mosaic( - name="DIMCOIN", - ticker=" DIM", - namespace="dim", - mosaic="coin", - divisibility=6, - levy=MosaicLevy( - type=NEMMosaicLevy.MosaicLevy_Percentile, - fee=10, - namespace="dim", - mosaic="coin", + "DIMCOIN", # name + " DIM", # ticker + "dim", # namespace + "coin", # mosaic + 6, # divisibility + MosaicLevy( # levy + NEMMosaicLevy.MosaicLevy_Percentile, # type + 10, # fee + "dim", # namespace + "coin", # mosaic ), - networks=(104,), + (104,), # networks ) yield Mosaic( - name="DIM TOKEN", - ticker=" DIMTOK", - namespace="dim", - mosaic="token", - divisibility=6, - networks=(104,), + "DIM TOKEN", # name + " DIMTOK", # ticker + "dim", # namespace + "token", # mosaic + 6, # divisibility + None, # levy + (104,), # networks ) yield Mosaic( - name="Breeze Token", - ticker=" BREEZE", - namespace="breeze", - mosaic="breeze-token", - divisibility=0, - networks=(104,), + "Breeze Token", # name + " BREEZE", # ticker + "breeze", # namespace + "breeze-token", # mosaic + 0, # divisibility + None, # levy + (104,), # networks ) yield Mosaic( - name="PacNEM Game Credits", - ticker=" PAC:HRT", - namespace="pacnem", - mosaic="heart", - divisibility=0, - networks=(104,), + "PacNEM Game Credits", # name + " PAC:HRT", # ticker + "pacnem", # namespace + "heart", # mosaic + 0, # divisibility + None, # levy + (104,), # networks ) yield Mosaic( - name="PacNEM Score Tokens", - ticker=" PAC:CHS", - namespace="pacnem", - mosaic="cheese", - divisibility=6, - levy=MosaicLevy( - type=NEMMosaicLevy.MosaicLevy_Percentile, - fee=100, - namespace="nem", - mosaic="xem", + "PacNEM Score Tokens", # name + " PAC:CHS", # ticker + "pacnem", # namespace + "cheese", # mosaic + 6, # divisibility + MosaicLevy( # levy + NEMMosaicLevy.MosaicLevy_Percentile, # type + 100, # fee + "nem", # namespace + "xem", # mosaic ), - networks=(104,), + (104,), # networks ) diff --git a/core/src/apps/nem/mosaic/nem_mosaics.py.mako b/core/src/apps/nem/mosaic/nem_mosaics.py.mako index 4da8347ab..e97520310 100644 --- a/core/src/apps/nem/mosaic/nem_mosaics.py.mako +++ b/core/src/apps/nem/mosaic/nem_mosaics.py.mako @@ -2,6 +2,9 @@ # (by running `make templates` in `core`) # do not edit manually! +# NOTE: not supplying the kwargs saves 120 bytes of code size +# `networks` needs kwarg as `levy` above is optional + from typing import Iterator from trezor.enums import NEMMosaicLevy @@ -44,21 +47,23 @@ class Mosaic: def mosaics_iterator() -> Iterator[Mosaic]: % for m in supported_on("trezor2", nem): yield Mosaic( - name="${m.name}", - ticker=" ${m.ticker}", - namespace="${m.namespace}", - mosaic="${m.mosaic}", - divisibility=${m.divisibility}, + "${m.name}", # name + " ${m.ticker}", # ticker + "${m.namespace}", # namespace + "${m.mosaic}", # mosaic + ${m.divisibility}, # divisibility % if "levy" in m: - levy=MosaicLevy( - type=NEMMosaicLevy.${m.levy}, - fee=${m.fee}, - namespace="${m.levy_namespace}", - mosaic="${m.levy_mosaic}", + MosaicLevy( # levy + NEMMosaicLevy.${m.levy}, # type + ${m.fee}, # fee + "${m.levy_namespace}", # namespace + "${m.levy_mosaic}", # mosaic ), +% else: + None, # levy % endif % if "networks" in m: - networks=${tuple(m.networks)}, + ${tuple(m.networks)}, # networks % endif ) % endfor diff --git a/core/src/apps/nem/mosaic/serialize.py b/core/src/apps/nem/mosaic/serialize.py index c6c2d617d..0782a956e 100644 --- a/core/src/apps/nem/mosaic/serialize.py +++ b/core/src/apps/nem/mosaic/serialize.py @@ -1,9 +1,5 @@ from typing import TYPE_CHECKING -from ..helpers import ( - NEM_TRANSACTION_TYPE_MOSAIC_CREATION, - NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE, -) from ..writers import ( serialize_tx_common, write_bytes_with_len, @@ -23,44 +19,43 @@ if TYPE_CHECKING: def serialize_mosaic_creation( common: NEMTransactionCommon, creation: NEMMosaicCreation, public_key: bytes ) -> bytes: + from ..helpers import NEM_TRANSACTION_TYPE_MOSAIC_CREATION + w = serialize_tx_common(common, public_key, NEM_TRANSACTION_TYPE_MOSAIC_CREATION) mosaics_w = bytearray() write_bytes_with_len(mosaics_w, public_key) + definition = creation.definition # local_cache_attribute identifier_w = bytearray() - write_bytes_with_len(identifier_w, creation.definition.namespace.encode()) - write_bytes_with_len(identifier_w, creation.definition.mosaic.encode()) + write_bytes_with_len(identifier_w, definition.namespace.encode()) + write_bytes_with_len(identifier_w, definition.mosaic.encode()) write_bytes_with_len(mosaics_w, identifier_w) - write_bytes_with_len(mosaics_w, creation.definition.description.encode()) + write_bytes_with_len(mosaics_w, definition.description.encode()) write_uint32_le(mosaics_w, 4) # number of properties - _write_property(mosaics_w, "divisibility", creation.definition.divisibility) - _write_property(mosaics_w, "initialSupply", creation.definition.supply) - _write_property(mosaics_w, "supplyMutable", creation.definition.mutable_supply) - _write_property(mosaics_w, "transferable", creation.definition.transferable) + _write_property(mosaics_w, "divisibility", definition.divisibility) + _write_property(mosaics_w, "initialSupply", definition.supply) + _write_property(mosaics_w, "supplyMutable", definition.mutable_supply) + _write_property(mosaics_w, "transferable", definition.transferable) - if creation.definition.levy: + if definition.levy: # all below asserts checked by nem.validators._validate_mosaic_creation - assert creation.definition.levy_namespace is not None - assert creation.definition.levy_mosaic is not None - assert creation.definition.levy_address is not None - assert creation.definition.fee is not None + assert definition.levy_namespace is not None + assert definition.levy_mosaic is not None + assert definition.levy_address is not None + assert definition.fee is not None levy_identifier_w = bytearray() - write_bytes_with_len( - levy_identifier_w, creation.definition.levy_namespace.encode() - ) - write_bytes_with_len( - levy_identifier_w, creation.definition.levy_mosaic.encode() - ) + write_bytes_with_len(levy_identifier_w, definition.levy_namespace.encode()) + write_bytes_with_len(levy_identifier_w, definition.levy_mosaic.encode()) levy_w = bytearray() - write_uint32_le(levy_w, creation.definition.levy) - write_bytes_with_len(levy_w, creation.definition.levy_address.encode()) + write_uint32_le(levy_w, definition.levy) + write_bytes_with_len(levy_w, definition.levy_address.encode()) write_bytes_with_len(levy_w, levy_identifier_w) - write_uint64_le(levy_w, creation.definition.fee) + write_uint64_le(levy_w, definition.fee) write_bytes_with_len(mosaics_w, levy_w) else: @@ -77,6 +72,8 @@ def serialize_mosaic_creation( def serialize_mosaic_supply_change( common: NEMTransactionCommon, change: NEMMosaicSupplyChange, public_key: bytes ) -> bytes: + from ..helpers import NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE + w = serialize_tx_common( common, public_key, NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE ) diff --git a/core/src/apps/nem/multisig/layout.py b/core/src/apps/nem/multisig/layout.py index 507bc5010..b194db470 100644 --- a/core/src/apps/nem/multisig/layout.py +++ b/core/src/apps/nem/multisig/layout.py @@ -1,11 +1,6 @@ from typing import TYPE_CHECKING -from trezor import ui from trezor.crypto import nem -from trezor.enums import ButtonRequestType, NEMModificationType -from trezor.ui.layouts import confirm_address - -from ..layout import require_confirm_fee, require_confirm_final, require_confirm_text if TYPE_CHECKING: from trezor.messages import ( @@ -17,6 +12,8 @@ if TYPE_CHECKING: async def ask_multisig(ctx: Context, msg: NEMSignTx) -> None: + from ..layout import require_confirm_fee + assert msg.multisig is not None # sign_tx assert msg.multisig.signer is not None # sign_tx address = nem.compute_address(msg.multisig.signer, msg.transaction.network) @@ -33,6 +30,9 @@ async def ask_aggregate_modification( mod: NEMAggregateModification, multisig: bool, ) -> None: + from trezor.enums import NEMModificationType + from ..layout import require_confirm_final, require_confirm_text + if not multisig: await require_confirm_text(ctx, "Convert account to multisig account?") @@ -55,12 +55,14 @@ async def ask_aggregate_modification( async def _require_confirm_address(ctx: Context, action: str, address: str) -> None: + from trezor.enums import ButtonRequestType + from trezor.ui.layouts import confirm_address + await confirm_address( ctx, - br_type="confirm_multisig", - title="Confirm address", - description=action, - address=address, - br_code=ButtonRequestType.ConfirmOutput, - icon=ui.ICON_SEND, + "Confirm address", + address, + action, + "confirm_multisig", + ButtonRequestType.ConfirmOutput, ) diff --git a/core/src/apps/nem/multisig/serialize.py b/core/src/apps/nem/multisig/serialize.py index e02f203ea..1ce0c7c77 100644 --- a/core/src/apps/nem/multisig/serialize.py +++ b/core/src/apps/nem/multisig/serialize.py @@ -1,12 +1,5 @@ from typing import TYPE_CHECKING -from trezor.crypto import hashlib, nem - -from ..helpers import ( - NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, - NEM_TRANSACTION_TYPE_MULTISIG, - NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE, -) from ..writers import serialize_tx_common, write_bytes_with_len, write_uint32_le if TYPE_CHECKING: @@ -17,6 +10,8 @@ if TYPE_CHECKING: def serialize_multisig( common: NEMTransactionCommon, public_key: bytes, inner: bytes ) -> bytes: + from ..helpers import NEM_TRANSACTION_TYPE_MULTISIG + w = serialize_tx_common(common, public_key, NEM_TRANSACTION_TYPE_MULTISIG) write_bytes_with_len(w, inner) return w @@ -28,6 +23,9 @@ def serialize_multisig_signature( inner: bytes, address_public_key: bytes, ) -> bytes: + from trezor.crypto import hashlib, nem + from ..helpers import NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE + w = serialize_tx_common(common, public_key, NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE) digest = hashlib.sha3_256(inner, keccak=True).digest() address = nem.compute_address(address_public_key, common.network) @@ -41,6 +39,8 @@ def serialize_multisig_signature( def serialize_aggregate_modification( common: NEMTransactionCommon, mod: NEMAggregateModification, public_key: bytes ) -> bytearray: + from ..helpers import NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION + version = common.network << 24 | 1 if mod.relative_change: version = common.network << 24 | 2 diff --git a/core/src/apps/nem/namespace/__init__.py b/core/src/apps/nem/namespace/__init__.py index 46125b625..12560a027 100644 --- a/core/src/apps/nem/namespace/__init__.py +++ b/core/src/apps/nem/namespace/__init__.py @@ -1,7 +1,5 @@ from typing import TYPE_CHECKING -from . import layout, serialize - if TYPE_CHECKING: from trezor.messages import NEMProvisionNamespace, NEMTransactionCommon from trezor.wire import Context @@ -13,5 +11,7 @@ async def namespace( common: NEMTransactionCommon, namespace: NEMProvisionNamespace, ) -> bytes: + from . import layout, serialize + await layout.ask_provision_namespace(ctx, common, namespace) return serialize.serialize_provision_namespace(common, namespace, public_key) diff --git a/core/src/apps/nem/namespace/layout.py b/core/src/apps/nem/namespace/layout.py index 400e29a4e..bc8456f6a 100644 --- a/core/src/apps/nem/namespace/layout.py +++ b/core/src/apps/nem/namespace/layout.py @@ -1,7 +1,5 @@ from typing import TYPE_CHECKING -from ..layout import require_confirm_content, require_confirm_fee, require_confirm_final - if TYPE_CHECKING: from trezor.messages import NEMProvisionNamespace, NEMTransactionCommon from trezor.wire import Context @@ -10,6 +8,12 @@ if TYPE_CHECKING: async def ask_provision_namespace( ctx: Context, common: NEMTransactionCommon, namespace: NEMProvisionNamespace ) -> None: + from ..layout import ( + require_confirm_content, + require_confirm_fee, + require_confirm_final, + ) + if namespace.parent: content = [ ("Create namespace", namespace.namespace), diff --git a/core/src/apps/nem/namespace/serialize.py b/core/src/apps/nem/namespace/serialize.py index f4c8e7cba..2c3c9b688 100644 --- a/core/src/apps/nem/namespace/serialize.py +++ b/core/src/apps/nem/namespace/serialize.py @@ -1,13 +1,5 @@ from typing import TYPE_CHECKING -from ..helpers import NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE -from ..writers import ( - serialize_tx_common, - write_bytes_with_len, - write_uint32_le, - write_uint64_le, -) - if TYPE_CHECKING: from trezor.messages import NEMProvisionNamespace, NEMTransactionCommon @@ -15,6 +7,14 @@ if TYPE_CHECKING: def serialize_provision_namespace( common: NEMTransactionCommon, namespace: NEMProvisionNamespace, public_key: bytes ) -> bytes: + from ..helpers import NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE + from ..writers import ( + serialize_tx_common, + write_bytes_with_len, + write_uint32_le, + write_uint64_le, + ) + tx = serialize_tx_common( common, public_key, NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE ) diff --git a/core/src/apps/nem/sign_tx.py b/core/src/apps/nem/sign_tx.py index 6be98987c..9eb223672 100644 --- a/core/src/apps/nem/sign_tx.py +++ b/core/src/apps/nem/sign_tx.py @@ -1,44 +1,50 @@ from typing import TYPE_CHECKING -from trezor import wire -from trezor.crypto.curve import ed25519 -from trezor.messages import NEMSignedTx - -from apps.common import seed from apps.common.keychain import with_slip44_keychain -from apps.common.paths import validate_path -from . import CURVE, PATTERNS, SLIP44_ID, mosaic, multisig, namespace, transfer -from .helpers import NEM_HASH_ALG, check_path -from .validators import validate +from . import CURVE, PATTERNS, SLIP44_ID if TYPE_CHECKING: - from trezor.messages import NEMSignTx + from trezor.messages import NEMSignTx, NEMSignedTx from apps.common.keychain import Keychain + from trezor.wire import Context @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) -async def sign_tx(ctx: wire.Context, msg: NEMSignTx, keychain: Keychain) -> NEMSignedTx: +async def sign_tx(ctx: Context, msg: NEMSignTx, keychain: Keychain) -> NEMSignedTx: + from trezor.wire import DataError + from trezor.crypto.curve import ed25519 + from trezor.messages import NEMSignedTx + + from apps.common import seed + from apps.common.paths import validate_path + + from . import mosaic, multisig, namespace, transfer + from .helpers import NEM_HASH_ALG, check_path + from .validators import validate + validate(msg) + msg_multisig = msg.multisig # local_cache_attribute + transaction = msg.transaction # local_cache_attribute await validate_path( ctx, keychain, - msg.transaction.address_n, - check_path(msg.transaction.address_n, msg.transaction.network), + transaction.address_n, + check_path(transaction.address_n, transaction.network), ) - node = keychain.derive(msg.transaction.address_n) + node = keychain.derive(transaction.address_n) - if msg.multisig: - if msg.multisig.signer is None: - raise wire.DataError("No signer provided") - public_key = msg.multisig.signer - common = msg.multisig + if msg_multisig: + if msg_multisig.signer is None: + raise DataError("No signer provided") + public_key = msg_multisig.signer + common = msg_multisig await multisig.ask(ctx, msg) else: public_key = seed.remove_ed25519_prefix(node.public_key()) - common = msg.transaction + common = transaction if msg.transfer: tx = await transfer.transfer(ctx, public_key, common, msg.transfer, node) @@ -54,28 +60,28 @@ async def sign_tx(ctx: wire.Context, msg: NEMSignTx, keychain: Keychain) -> NEMS public_key, common, msg.aggregate_modification, - msg.multisig is not None, + msg_multisig is not None, ) elif msg.importance_transfer: tx = await transfer.importance_transfer( ctx, public_key, common, msg.importance_transfer ) else: - raise wire.DataError("No transaction provided") + raise DataError("No transaction provided") - if msg.multisig: + if msg_multisig: # wrap transaction in multisig wrapper if msg.cosigning: - assert msg.multisig.signer is not None + assert msg_multisig.signer is not None tx = multisig.cosign( seed.remove_ed25519_prefix(node.public_key()), - msg.transaction, + transaction, tx, - msg.multisig.signer, + msg_multisig.signer, ) else: tx = multisig.initiate( - seed.remove_ed25519_prefix(node.public_key()), msg.transaction, tx + seed.remove_ed25519_prefix(node.public_key()), transaction, tx ) signature = ed25519.sign(node.private_key(), tx, NEM_HASH_ALG) diff --git a/core/src/apps/nem/transfer/layout.py b/core/src/apps/nem/transfer/layout.py index b279b21e7..a62ccf53e 100644 --- a/core/src/apps/nem/transfer/layout.py +++ b/core/src/apps/nem/transfer/layout.py @@ -1,22 +1,12 @@ from typing import TYPE_CHECKING from trezor import ui -from trezor.enums import ButtonRequestType, NEMImportanceTransferMode, NEMMosaicLevy +from trezor.enums import ButtonRequestType from trezor.strings import format_amount -from trezor.ui.layouts import ( - confirm_action, - confirm_output, - confirm_properties, - confirm_text, -) -from ..helpers import ( - NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE, - NEM_MAX_DIVISIBILITY, - NEM_MOSAIC_AMOUNT_DIVISOR, -) -from ..layout import require_confirm_final, require_confirm_text -from ..mosaic.helpers import get_mosaic_definition, is_nem_xem_mosaic +from ..helpers import NEM_MOSAIC_AMOUNT_DIVISOR +from ..layout import require_confirm_final +from ..mosaic.helpers import is_nem_xem_mosaic if TYPE_CHECKING: from trezor.messages import ( @@ -26,7 +16,6 @@ if TYPE_CHECKING: NEMTransfer, ) from trezor.wire import Context - from ..mosaic.nem_mosaics import MosaicLevy async def ask_transfer( @@ -35,17 +24,45 @@ async def ask_transfer( transfer: NEMTransfer, encrypted: bool, ) -> None: + from trezor.ui.layouts import confirm_output, confirm_text + from ..helpers import NEM_MAX_DIVISIBILITY + if transfer.payload: - await _require_confirm_payload(ctx, transfer.payload, encrypted) + # require_confirm_payload + await confirm_text( + ctx, + "confirm_payload", + "Confirm payload", + bytes(transfer.payload).decode(), + "Encrypted:" if encrypted else "Unencrypted:", + ButtonRequestType.ConfirmOutput, + icon_color=ui.GREEN if encrypted else ui.RED, + ) + for mosaic in transfer.mosaics: - await ask_transfer_mosaic(ctx, common, transfer, mosaic) - await _require_confirm_transfer(ctx, transfer.recipient, _get_xem_amount(transfer)) + await _ask_transfer_mosaic(ctx, common, transfer, mosaic) + + # require_confirm_transfer + await confirm_output( + ctx, + transfer.recipient, + f"Send {format_amount(_get_xem_amount(transfer), NEM_MAX_DIVISIBILITY)} XEM", + ui.BOLD, + "Confirm transfer", + to_str="\nto\n", + ) + await require_confirm_final(ctx, common.fee) -async def ask_transfer_mosaic( +async def _ask_transfer_mosaic( ctx: Context, common: NEMTransactionCommon, transfer: NEMTransfer, mosaic: NEMMosaic ) -> None: + from trezor.enums import NEMMosaicLevy + from trezor.ui.layouts import confirm_action, confirm_properties + from ..mosaic.helpers import get_mosaic_definition + from ..helpers import NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE + if is_nem_xem_mosaic(mosaic): return @@ -56,19 +73,26 @@ async def ask_transfer_mosaic( await confirm_properties( ctx, "confirm_mosaic", - title="Confirm mosaic", - props=[ + "Confirm mosaic", + ( ( "Confirm transfer of", format_amount(mosaic_quantity, definition.divisibility) + definition.ticker, ), ("of", definition.name), - ], + ), ) + levy = definition.levy # local_cache_attribute + + if levy is not None: + if levy == NEMMosaicLevy.MosaicLevy_Absolute: + levy_fee = levy.fee + else: + levy_fee = ( + mosaic_quantity * levy.fee // NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE + ) - if definition.levy is not None: - levy_fee = _get_levy_fee(definition.levy, mosaic_quantity) levy_msg = ( format_amount(levy_fee, definition.divisibility) + definition.ticker ) @@ -76,19 +100,17 @@ async def ask_transfer_mosaic( await confirm_properties( ctx, "confirm_mosaic_levy", - title="Confirm mosaic", - props=[ - ("Confirm mosaic\nlevy fee of", levy_msg), - ], + "Confirm mosaic", + (("Confirm mosaic\nlevy fee of", levy_msg),), ) else: await confirm_action( ctx, "confirm_mosaic_unknown", - title="Confirm mosaic", - action="Unknown mosaic!", - description="Divisibility and levy cannot be shown for unknown mosaics", + "Confirm mosaic", + "Unknown mosaic!", + "Divisibility and levy cannot be shown for unknown mosaics", icon=ui.ICON_SEND, icon_color=ui.RED, br_code=ButtonRequestType.ConfirmOutput, @@ -97,11 +119,11 @@ async def ask_transfer_mosaic( await confirm_properties( ctx, "confirm_mosaic_transfer", - title="Confirm mosaic", - props=[ + "Confirm mosaic", + ( ("Confirm transfer of", f"{mosaic_quantity} raw units"), ("of", f"{mosaic.namespace}.{mosaic.mosaic}"), - ], + ), ) @@ -117,44 +139,15 @@ def _get_xem_amount(transfer: NEMTransfer) -> int: return 0 -def _get_levy_fee(levy: MosaicLevy, quantity: int) -> int: - if levy.type == NEMMosaicLevy.MosaicLevy_Absolute: - return levy.fee - else: - return quantity * levy.fee // NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE - - async def ask_importance_transfer( ctx: Context, common: NEMTransactionCommon, imp: NEMImportanceTransfer ) -> None: + from trezor.enums import NEMImportanceTransferMode + from ..layout import require_confirm_text + if imp.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate: m = "Activate" else: m = "Deactivate" await require_confirm_text(ctx, m + " remote harvesting?") await require_confirm_final(ctx, common.fee) - - -async def _require_confirm_transfer(ctx: Context, recipient: str, value: int) -> None: - await confirm_output( - ctx, - recipient, - amount=f"Send {format_amount(value, NEM_MAX_DIVISIBILITY)} XEM", - font_amount=ui.BOLD, - title="Confirm transfer", - to_str="\nto\n", - ) - - -async def _require_confirm_payload( - ctx: Context, payload: bytes, encrypted: bool = False -) -> None: - await confirm_text( - ctx, - "confirm_payload", - title="Confirm payload", - description="Encrypted:" if encrypted else "Unencrypted:", - data=bytes(payload).decode(), - icon_color=ui.GREEN if encrypted else ui.RED, - br_code=ButtonRequestType.ConfirmOutput, - ) diff --git a/core/src/apps/nem/transfer/serialize.py b/core/src/apps/nem/transfer/serialize.py index 764302129..894346331 100644 --- a/core/src/apps/nem/transfer/serialize.py +++ b/core/src/apps/nem/transfer/serialize.py @@ -1,19 +1,5 @@ from typing import TYPE_CHECKING -from trezor.crypto import random -from trezor.messages import ( - NEMImportanceTransfer, - NEMMosaic, - NEMTransactionCommon, - NEMTransfer, -) - -from ..helpers import ( - AES_BLOCK_SIZE, - NEM_SALT_SIZE, - NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER, - NEM_TRANSACTION_TYPE_TRANSFER, -) from ..writers import ( serialize_tx_common, write_bytes_with_len, @@ -24,6 +10,12 @@ from ..writers import ( if TYPE_CHECKING: from trezor.crypto import bip32 from trezor.utils import Writer + from trezor.messages import ( + NEMImportanceTransfer, + NEMMosaic, + NEMTransactionCommon, + NEMTransfer, + ) def serialize_transfer( @@ -33,11 +25,15 @@ def serialize_transfer( payload: bytes, encrypted: bool, ) -> bytearray: + from ..helpers import NEM_TRANSACTION_TYPE_TRANSFER + from ..writers import write_uint32_le + + version = common.network << 24 | 2 if transfer.mosaics else common.network << 24 | 1 tx = serialize_tx_common( common, public_key, NEM_TRANSACTION_TYPE_TRANSFER, - _get_version(common.network, transfer.mosaics), + version, ) write_bytes_with_len(tx, transfer.recipient.encode()) @@ -75,6 +71,8 @@ def serialize_mosaic(w: Writer, namespace: str, mosaic: str, quantity: int) -> N def serialize_importance_transfer( common: NEMTransactionCommon, imp: NEMImportanceTransfer, public_key: bytes ) -> bytes: + from ..helpers import NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER + w = serialize_tx_common( common, public_key, NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER ) @@ -87,55 +85,45 @@ def serialize_importance_transfer( def get_transfer_payload( transfer: NEMTransfer, node: bip32.HDNode ) -> tuple[bytes, bool]: + from trezor.crypto import random + from ..helpers import ( + AES_BLOCK_SIZE, + NEM_SALT_SIZE, + ) + if transfer.public_key is not None: if not transfer.payload: raise ValueError("Public key provided but no payload to encrypt") - encrypted_payload = _encrypt(node, transfer.public_key, transfer.payload) + + # encrypt payload + salt = random.bytes(NEM_SALT_SIZE) + iv = random.bytes(AES_BLOCK_SIZE) + encrypted = node.nem_encrypt(transfer.public_key, iv, salt, transfer.payload) + encrypted_payload = iv + salt + encrypted + return encrypted_payload, True else: return transfer.payload or b"", False -def _encrypt(node: bip32.HDNode, public_key: bytes, payload: bytes) -> bytes: - salt = random.bytes(NEM_SALT_SIZE) - iv = random.bytes(AES_BLOCK_SIZE) - encrypted = node.nem_encrypt(public_key, iv, salt, payload) - return iv + salt + encrypted - - -def _get_version(network: int, mosaics: list[NEMMosaic] | None = None) -> int: - if mosaics: - return network << 24 | 2 - return network << 24 | 1 - - def canonicalize_mosaics(mosaics: list[NEMMosaic]) -> list[NEMMosaic]: if len(mosaics) <= 1: return mosaics - mosaics = merge_mosaics(mosaics) - return sort_mosaics(mosaics) + mosaics = _merge_mosaics(mosaics) + return sorted(mosaics, key=lambda m: (m.namespace, m.mosaic)) -def are_mosaics_equal(a: NEMMosaic, b: NEMMosaic) -> bool: - if a.namespace == b.namespace and a.mosaic == b.mosaic: - return True - return False - - -def merge_mosaics(mosaics: list[NEMMosaic]) -> list[NEMMosaic]: +def _merge_mosaics(mosaics: list[NEMMosaic]) -> list[NEMMosaic]: if not mosaics: return [] ret: list[NEMMosaic] = [] for i in mosaics: found = False for k, y in enumerate(ret): - if are_mosaics_equal(i, y): + # are_mosaics_equal + if i.namespace == y.namespace and i.mosaic == y.mosaic: ret[k].quantity += i.quantity found = True if not found: ret.append(i) return ret - - -def sort_mosaics(mosaics: list[NEMMosaic]) -> list[NEMMosaic]: - return sorted(mosaics, key=lambda m: (m.namespace, m.mosaic)) diff --git a/core/src/apps/nem/validators.py b/core/src/apps/nem/validators.py index 9aa65b2c0..df486d4d2 100644 --- a/core/src/apps/nem/validators.py +++ b/core/src/apps/nem/validators.py @@ -1,72 +1,37 @@ from typing import TYPE_CHECKING -from trezor.crypto import nem -from trezor.enums import NEMModificationType from trezor.wire import ProcessError -from .helpers import ( - NEM_MAX_DIVISIBILITY, - NEM_MAX_ENCRYPTED_PAYLOAD_SIZE, - NEM_MAX_PLAIN_PAYLOAD_SIZE, - NEM_MAX_SUPPLY, - NEM_NETWORK_MAINNET, - NEM_NETWORK_MIJIN, - NEM_NETWORK_TESTNET, - NEM_PUBLIC_KEY_SIZE, -) - if TYPE_CHECKING: - from trezor.messages import ( - NEMAggregateModification, - NEMImportanceTransfer, - NEMMosaicCreation, - NEMMosaicSupplyChange, - NEMProvisionNamespace, - NEMSignTx, - NEMTransactionCommon, - NEMTransfer, - ) + from trezor.messages import NEMSignTx, NEMTransactionCommon def validate(msg: NEMSignTx) -> None: - _validate_single_tx(msg) - _validate_common(msg.transaction) + from trezor.crypto import nem + from trezor.enums import NEMModificationType + from trezor.wire import ProcessError # local_cache_global + from .helpers import ( + NEM_MAX_ENCRYPTED_PAYLOAD_SIZE, + NEM_MAX_PLAIN_PAYLOAD_SIZE, + NEM_MAX_DIVISIBILITY, + NEM_MAX_SUPPLY, + ) - if msg.multisig: - _validate_common(msg.multisig, inner=True) - _validate_multisig(msg.multisig, msg.transaction.network) - if not msg.multisig and msg.cosigning: - raise ProcessError("No multisig transaction to cosign") + validate_address = nem.validate_address # local_cache_attribute + transfer = msg.transfer # local_cache_attribute + aggregate_modification = msg.aggregate_modification # local_cache_attribute + multisig = msg.multisig # local_cache_attribute + mosaic_creation = msg.mosaic_creation # local_cache_attribute + network = msg.transaction.network # local_cache_attribute - if msg.transfer: - _validate_transfer(msg.transfer, msg.transaction.network) - if msg.provision_namespace: - _validate_provision_namespace(msg.provision_namespace, msg.transaction.network) - if msg.mosaic_creation: - _validate_mosaic_creation(msg.mosaic_creation, msg.transaction.network) - if msg.supply_change: - _validate_supply_change(msg.supply_change) - if msg.aggregate_modification: - _validate_aggregate_modification( - msg.aggregate_modification, msg.multisig is None - ) - if msg.importance_transfer: - _validate_importance_transfer(msg.importance_transfer) - - -def validate_network(network: int) -> None: - if network not in (NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN): - raise ProcessError("Invalid NEM network") - - -def _validate_single_tx(msg: NEMSignTx) -> None: + # _validate_single_tx # ensure exactly one transaction is provided tx_count = ( - bool(msg.transfer) + bool(transfer) + bool(msg.provision_namespace) - + bool(msg.mosaic_creation) + + bool(mosaic_creation) + bool(msg.supply_change) - + bool(msg.aggregate_modification) + + bool(aggregate_modification) + bool(msg.importance_transfer) ) if tx_count == 0: @@ -74,16 +39,145 @@ def _validate_single_tx(msg: NEMSignTx) -> None: if tx_count > 1: raise ProcessError("More than one transaction provided") + _validate_common(msg.transaction) + + if multisig: + _validate_common(multisig, True) + # _validate_multisig + if multisig.network != network: + raise ProcessError("Inner transaction network is different") + _validate_public_key( + multisig.signer, "Invalid multisig signer public key provided" + ) + # END _validate_multisig + if not multisig and msg.cosigning: + raise ProcessError("No multisig transaction to cosign") + + if transfer: + payload = transfer.payload # local_cache_attribute + + if transfer.public_key is not None: + _validate_public_key(transfer.public_key, "Invalid recipient public key") + if not payload: + raise ProcessError("Public key provided but no payload to encrypt") + + if payload: + if len(payload) > NEM_MAX_PLAIN_PAYLOAD_SIZE: + raise ProcessError("Payload too large") + if transfer.public_key and len(payload) > NEM_MAX_ENCRYPTED_PAYLOAD_SIZE: + raise ProcessError("Payload too large") + + if not validate_address(transfer.recipient, network): + raise ProcessError("Invalid recipient address") + # END _validate_transfer + if msg.provision_namespace: + # _validate_provision_namespace + if not validate_address(msg.provision_namespace.sink, network): + raise ProcessError("Invalid rental sink address") + # END _validate_provision_namespace + if mosaic_creation: + # _validate_mosaic_creation + if not validate_address(mosaic_creation.sink, network): + raise ProcessError("Invalid creation sink address") + definition = mosaic_creation.definition # local_cache_attribute + supply = definition.supply # local_cache_attribute + divisibility = definition.divisibility # local_cache_attribute + + if definition.name is not None: + raise ProcessError("Name not allowed in mosaic creation transactions") + if definition.ticker is not None: + raise ProcessError("Ticker not allowed in mosaic creation transactions") + if definition.networks: + raise ProcessError("Networks not allowed in mosaic creation transactions") + + if supply is not None and divisibility is None: + raise ProcessError( + "Definition divisibility needs to be provided when supply is" + ) + if supply is None and divisibility is not None: + raise ProcessError( + "Definition supply needs to be provided when divisibility is" + ) + + if definition.levy is not None: + if definition.fee is None: + raise ProcessError("No levy fee provided") + if definition.levy_address is None: + raise ProcessError("No levy address provided") + if definition.levy_namespace is None: + raise ProcessError("No levy namespace provided") + if definition.levy_mosaic is None: + raise ProcessError("No levy mosaic name provided") + + if divisibility is None: + raise ProcessError("No divisibility provided") + if supply is None: + raise ProcessError("No supply provided") + if definition.mutable_supply is None: + raise ProcessError("No supply mutability provided") + if definition.transferable is None: + raise ProcessError("No mosaic transferability provided") + if definition.description is None: + raise ProcessError("No description provided") + + if divisibility > NEM_MAX_DIVISIBILITY: + raise ProcessError("Invalid divisibility provided") + if supply > NEM_MAX_SUPPLY: + raise ProcessError("Invalid supply provided") + + if not validate_address(definition.levy_address, network): + raise ProcessError("Invalid levy address") + # END _validate_mosaic_creation + if msg.supply_change: + # _validate_supply_change + pass + # END _validate_supply_change + if aggregate_modification: + # _validate_aggregate_modification + creation = multisig is None + if creation and not aggregate_modification.modifications: + raise ProcessError("No modifications provided") + + for m in aggregate_modification.modifications: + if ( + creation + and m.type == NEMModificationType.CosignatoryModification_Delete + ): + raise ProcessError("Cannot remove cosignatory when converting account") + _validate_public_key( + m.public_key, "Invalid cosignatory public key provided" + ) + # END _validate_aggregate_modification + if msg.importance_transfer: + # _validate_importance_transfer + _validate_public_key( + msg.importance_transfer.public_key, + "Invalid remote account public key provided", + ) + # END _validate_importance_transfer + + +def validate_network(network: int) -> None: + from .helpers import ( + NEM_NETWORK_MAINNET, + NEM_NETWORK_MIJIN, + NEM_NETWORK_TESTNET, + ) + + if network not in (NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN): + raise ProcessError("Invalid NEM network") + def _validate_common(common: NEMTransactionCommon, inner: bool = False) -> None: validate_network(common.network) + signer = common.signer # local_cache_attribute err = None - if not inner and common.signer: + if not inner and signer: raise ProcessError("Signer not allowed in outer transaction") - if inner and common.signer is None: + if inner and signer is None: err = "signer" if err: @@ -92,125 +186,14 @@ def _validate_common(common: NEMTransactionCommon, inner: bool = False) -> None: else: raise ProcessError(f"No {err} provided") - if common.signer is not None: - _validate_public_key( - common.signer, "Invalid signer public key in inner transaction" - ) + if signer is not None: + _validate_public_key(signer, "Invalid signer public key in inner transaction") def _validate_public_key(public_key: bytes | None, err_msg: str) -> None: + from .helpers import NEM_PUBLIC_KEY_SIZE + if not public_key: raise ProcessError(f"{err_msg} (none provided)") if len(public_key) != NEM_PUBLIC_KEY_SIZE: raise ProcessError(f"{err_msg} (invalid length)") - - -def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer) -> None: - _validate_public_key( - importance_transfer.public_key, "Invalid remote account public key provided" - ) - - -def _validate_multisig(multisig: NEMTransactionCommon, network: int) -> None: - if multisig.network != network: - raise ProcessError("Inner transaction network is different") - _validate_public_key(multisig.signer, "Invalid multisig signer public key provided") - - -def _validate_aggregate_modification( - aggregate_modification: NEMAggregateModification, creation: bool = False -) -> None: - - if creation and not aggregate_modification.modifications: - raise ProcessError("No modifications provided") - - for m in aggregate_modification.modifications: - if creation and m.type == NEMModificationType.CosignatoryModification_Delete: - raise ProcessError("Cannot remove cosignatory when converting account") - _validate_public_key(m.public_key, "Invalid cosignatory public key provided") - - -def _validate_supply_change(supply_change: NEMMosaicSupplyChange) -> None: - pass - - -def _validate_mosaic_creation(mosaic_creation: NEMMosaicCreation, network: int) -> None: - if not nem.validate_address(mosaic_creation.sink, network): - raise ProcessError("Invalid creation sink address") - - if mosaic_creation.definition.name is not None: - raise ProcessError("Name not allowed in mosaic creation transactions") - if mosaic_creation.definition.ticker is not None: - raise ProcessError("Ticker not allowed in mosaic creation transactions") - if mosaic_creation.definition.networks: - raise ProcessError("Networks not allowed in mosaic creation transactions") - - if ( - mosaic_creation.definition.supply is not None - and mosaic_creation.definition.divisibility is None - ): - raise ProcessError( - "Definition divisibility needs to be provided when supply is" - ) - if ( - mosaic_creation.definition.supply is None - and mosaic_creation.definition.divisibility is not None - ): - raise ProcessError( - "Definition supply needs to be provided when divisibility is" - ) - - if mosaic_creation.definition.levy is not None: - if mosaic_creation.definition.fee is None: - raise ProcessError("No levy fee provided") - if mosaic_creation.definition.levy_address is None: - raise ProcessError("No levy address provided") - if mosaic_creation.definition.levy_namespace is None: - raise ProcessError("No levy namespace provided") - if mosaic_creation.definition.levy_mosaic is None: - raise ProcessError("No levy mosaic name provided") - - if mosaic_creation.definition.divisibility is None: - raise ProcessError("No divisibility provided") - if mosaic_creation.definition.supply is None: - raise ProcessError("No supply provided") - if mosaic_creation.definition.mutable_supply is None: - raise ProcessError("No supply mutability provided") - if mosaic_creation.definition.transferable is None: - raise ProcessError("No mosaic transferability provided") - if mosaic_creation.definition.description is None: - raise ProcessError("No description provided") - - if mosaic_creation.definition.divisibility > NEM_MAX_DIVISIBILITY: - raise ProcessError("Invalid divisibility provided") - if mosaic_creation.definition.supply > NEM_MAX_SUPPLY: - raise ProcessError("Invalid supply provided") - - if not nem.validate_address(mosaic_creation.definition.levy_address, network): - raise ProcessError("Invalid levy address") - - -def _validate_provision_namespace( - provision_namespace: NEMProvisionNamespace, network: int -) -> None: - if not nem.validate_address(provision_namespace.sink, network): - raise ProcessError("Invalid rental sink address") - - -def _validate_transfer(transfer: NEMTransfer, network: int) -> None: - if transfer.public_key is not None: - _validate_public_key(transfer.public_key, "Invalid recipient public key") - if not transfer.payload: - raise ProcessError("Public key provided but no payload to encrypt") - - if transfer.payload: - if len(transfer.payload) > NEM_MAX_PLAIN_PAYLOAD_SIZE: - raise ProcessError("Payload too large") - if ( - transfer.public_key - and len(transfer.payload) > NEM_MAX_ENCRYPTED_PAYLOAD_SIZE - ): - raise ProcessError("Payload too large") - - if not nem.validate_address(transfer.recipient, network): - raise ProcessError("Invalid recipient address") diff --git a/core/tests/test_apps.nem.mosaic.py b/core/tests/test_apps.nem.mosaic.py index e84d66a35..fc26af9ae 100644 --- a/core/tests/test_apps.nem.mosaic.py +++ b/core/tests/test_apps.nem.mosaic.py @@ -5,6 +5,7 @@ if not utils.BITCOIN_ONLY: from apps.nem.mosaic.helpers import get_mosaic_definition from apps.nem.transfer import * from apps.nem.transfer.serialize import * + from apps.nem.transfer.serialize import _merge_mosaics def get_mosaic(namespace: str, quantity: int, mosaic: str) -> NEMMosaic: @@ -15,6 +16,11 @@ def get_mosaic(namespace: str, quantity: int, mosaic: str) -> NEMMosaic: ) +# NOTE: copy-pasted from apps.nem.transfer.serialize.py +def sort_mosaics(mosaics: list[NEMMosaic]) -> list[NEMMosaic]: + return sorted(mosaics, key=lambda m: (m.namespace, m.mosaic)) + + @unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin") class TestNemMosaic(unittest.TestCase): @@ -54,20 +60,20 @@ class TestNemMosaic(unittest.TestCase): b = get_mosaic("abc", 1, "mosaic") c = get_mosaic("abc", 2, "xxx") - merged = merge_mosaics([a, b]) + merged = _merge_mosaics([a, b]) self.assertEqual(merged[0].quantity, 2) self.assertEqual(len(merged), 1) a.quantity = 1 b.quantity = 10 - merged = merge_mosaics([a, b]) + merged = _merge_mosaics([a, b]) self.assertEqual(merged[0].quantity, 11) a.namespace = "abcdef" - merged = merge_mosaics([a, b]) + merged = _merge_mosaics([a, b]) self.assertEqual(len(merged), 2) - merged = merge_mosaics([a, b, c]) + merged = _merge_mosaics([a, b, c]) self.assertEqual(len(merged), 3) a.namespace = "abcdef" @@ -79,7 +85,7 @@ class TestNemMosaic(unittest.TestCase): c.namespace = "abc" c.mosaic = "mosaic" c.quantity = 3 - merged = merge_mosaics([a, b, c]) + merged = _merge_mosaics([a, b, c]) self.assertEqual(merged[0].quantity, 1) self.assertEqual(merged[1].quantity, 5) self.assertEqual(len(merged), 2) @@ -93,7 +99,7 @@ class TestNemMosaic(unittest.TestCase): c.namespace = "abc" c.mosaic = "mosaic" c.quantity = 3 - merged = merge_mosaics([a, b, c]) + merged = _merge_mosaics([a, b, c]) self.assertEqual(merged[0].quantity, 6) self.assertEqual(len(merged), 1) diff --git a/core/tests/test_apps.nem.transfer.py b/core/tests/test_apps.nem.transfer.py index e9a1d4025..587a00c93 100644 --- a/core/tests/test_apps.nem.transfer.py +++ b/core/tests/test_apps.nem.transfer.py @@ -7,8 +7,7 @@ if not utils.BITCOIN_ONLY: from apps.nem.mosaic import * from apps.nem.transfer import * from apps.nem.transfer.serialize import * - from trezor.messages import NEMTransfer - from trezor.messages import NEMSignTx + from trezor.messages import NEMTransfer, NEMTransactionCommon, NEMSignTx, NEMMosaic @unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")