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:
parent
32125ef51f
commit
d182ac5b53
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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))
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user