1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-17 21:22:10 +00:00

chore(core): decrease nem size by 1550 bytes

This commit is contained in:
grdddj 2022-09-17 18:27:15 +02:00 committed by matejcik
parent 32125ef51f
commit d182ac5b53
19 changed files with 501 additions and 514 deletions

View File

@ -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)

View File

@ -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

View File

@ -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,
)

View File

@ -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):

View File

@ -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,
)

View File

@ -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
)

View File

@ -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

View File

@ -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
)

View File

@ -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,
)

View File

@ -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

View File

@ -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)

View File

@ -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),

View File

@ -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
)

View File

@ -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)

View File

@ -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,
)

View File

@ -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))

View File

@ -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)
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")
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
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.importance_transfer:
_validate_importance_transfer(msg.importance_transfer)
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
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")

View File

@ -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)

View File

@ -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")