mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-28 16:21:03 +00:00
nem: correct layout and confirms
This commit is contained in:
parent
6aef64d367
commit
561ca35a08
@ -22,3 +22,6 @@ NEM_SALT_SIZE = const(32)
|
||||
AES_BLOCK_SIZE = const(16)
|
||||
NEM_HASH_ALG = 'keccak'
|
||||
NEM_PUBLIC_KEY_SIZE = const(32) # ed25519 public key
|
||||
|
||||
NEM_MAX_PLAIN_PAYLOAD_SIZE = const(1024)
|
||||
NEM_MAX_ENCRYPTED_PAYLOAD_SIZE = const(960)
|
||||
|
@ -2,28 +2,80 @@ from apps.common.confirm import *
|
||||
from trezor import ui
|
||||
from trezor.messages import ButtonRequestType
|
||||
from trezor.messages.NEMMosaicDefinition import NEMMosaicDefinition
|
||||
from trezor.messages import NEMMosaicLevy
|
||||
from trezor.ui.text import Text
|
||||
from trezor.ui.scroll import Scrollpage, animate_swipe, paginate
|
||||
from trezor.utils import chunks, format_amount, split_words
|
||||
from .helpers import *
|
||||
|
||||
|
||||
async def require_confirm_tx(ctx, recipient, value):
|
||||
content = Text('Confirm sending', ui.ICON_SEND,
|
||||
ui.BOLD, format_amount(value, NEM_MAX_DIVISIBILITY) + ' NEM',
|
||||
async def require_confirm_action(ctx, action: str):
|
||||
content = Text('Confirm action', ui.ICON_SEND,
|
||||
ui.NORMAL, *split_words(action, 18),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
|
||||
|
||||
async def require_confirm_fee(ctx, action: str, fee: int):
|
||||
content = Text('Confirm fee', ui.ICON_SEND,
|
||||
ui.NORMAL, action,
|
||||
ui.BOLD, format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM',
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
|
||||
|
||||
async def require_confirm_transfer(ctx, recipient, value):
|
||||
content = Text('Confirm transfer', ui.ICON_SEND,
|
||||
ui.BOLD, 'Send ' + format_amount(value, NEM_MAX_DIVISIBILITY) + ' XEM',
|
||||
ui.NORMAL, 'to',
|
||||
ui.MONO, *split_address(recipient),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
|
||||
|
||||
async def require_confirm_address(ctx, action: str, address: str):
|
||||
content = Text('Confirm address', ui.ICON_SEND,
|
||||
ui.NORMAL, action,
|
||||
ui.MONO, *split_address(address),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
|
||||
|
||||
async def require_confirm_final(ctx, fee: int):
|
||||
content = Text('Final confirm', ui.ICON_SEND,
|
||||
ui.NORMAL, 'Sign this transaction',
|
||||
ui.BOLD, 'and pay ' + format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM',
|
||||
ui.NORMAL, 'for transaction fee?',
|
||||
icon_color=ui.GREEN)
|
||||
await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1
|
||||
|
||||
|
||||
async def require_confirm_action(ctx, action: str):
|
||||
content = Text('Confirm sending', ui.ICON_SEND,
|
||||
ui.NORMAL, *split_words(action, 18),
|
||||
icon_color=ui.GREEN)
|
||||
async def require_confirm_payload(ctx, payload: bytes, encrypt=False):
|
||||
payload = str(payload, 'utf-8')
|
||||
|
||||
if len(payload) > 48:
|
||||
payload = payload[:48] + '..'
|
||||
print(len(payload))
|
||||
if encrypt:
|
||||
content = Text('Confirm payload', ui.ICON_SEND,
|
||||
ui.BOLD, 'Encrypted:',
|
||||
ui.NORMAL, *split_words(payload, 22),
|
||||
icon_color=ui.GREEN)
|
||||
else:
|
||||
content = Text('Confirm payload', ui.ICON_SEND,
|
||||
ui.BOLD, 'Unencrypted:',
|
||||
ui.NORMAL, *split_words(payload, 22),
|
||||
icon_color=ui.RED)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
|
||||
|
||||
async def require_confirm_properties(ctx, definition: NEMMosaicDefinition):
|
||||
properties = _get_mosaic_properties(definition)
|
||||
first_page = const(0)
|
||||
paginator = paginate(_show_page, len(properties), first_page, properties)
|
||||
await ctx.wait(paginator)
|
||||
|
||||
|
||||
@ui.layout
|
||||
async def _show_page(page: int, page_count: int, content):
|
||||
content = Scrollpage(content[page], page, page_count)
|
||||
@ -34,13 +86,6 @@ async def _show_page(page: int, page_count: int, content):
|
||||
await animate_swipe()
|
||||
|
||||
|
||||
async def require_confirm_properties(ctx, definition: NEMMosaicDefinition):
|
||||
properties = _get_mosaic_properties(definition)
|
||||
first_page = const(0)
|
||||
paginator = paginate(_show_page, len(properties), first_page, properties)
|
||||
await ctx.wait(paginator)
|
||||
|
||||
|
||||
def _get_mosaic_properties(definition: NEMMosaicDefinition):
|
||||
properties = []
|
||||
if definition.description:
|
||||
@ -63,31 +108,34 @@ def _get_mosaic_properties(definition: NEMMosaicDefinition):
|
||||
if definition.supply:
|
||||
t = Text('Confirm properties', ui.ICON_SEND,
|
||||
ui.BOLD, 'Initial supply:',
|
||||
ui.NORMAL, format_amount(definition.supply, definition.divisibility),
|
||||
ui.NORMAL, str(definition.supply),
|
||||
ui.NORMAL, imm)
|
||||
properties.append(t)
|
||||
return properties
|
||||
|
||||
|
||||
async def require_confirm_final(ctx, action: str, fee: int):
|
||||
content = Text('Confirm sending', ui.ICON_SEND,
|
||||
ui.NORMAL, 'Create ', action,
|
||||
ui.BOLD, 'paying ' + format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM',
|
||||
ui.NORMAL, 'for transaction fee?',
|
||||
icon_color=ui.GREEN)
|
||||
await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1
|
||||
|
||||
|
||||
async def require_confirm_payload(ctx, payload: bytes, encrypt=False):
|
||||
payload = str(payload, 'utf-8')
|
||||
if encrypt:
|
||||
content = Text('Send encrypted?', ui.ICON_SEND,
|
||||
ui.NORMAL, *split_words(payload, 18))
|
||||
else:
|
||||
content = Text('Send unencrypted?', ui.ICON_SEND,
|
||||
ui.NORMAL, *split_words(payload, 18),
|
||||
icon_color=ui.RED)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
t = Text('Confirm properties', ui.ICON_SEND,
|
||||
ui.BOLD, 'Initial supply:',
|
||||
ui.NORMAL, imm)
|
||||
properties.append(t)
|
||||
if definition.levy:
|
||||
t = Text('Confirm properties', ui.ICON_SEND,
|
||||
ui.BOLD, 'Levy recipient:',
|
||||
ui.MONO, *split_address(definition.levy_address))
|
||||
properties.append(t)
|
||||
t = Text('Confirm properties', ui.ICON_SEND,
|
||||
ui.BOLD, 'Levy namespace:',
|
||||
ui.NORMAL, definition.levy_namespace,
|
||||
ui.BOLD, 'Levy mosaic:',
|
||||
ui.NORMAL, definition.levy_mosaic)
|
||||
properties.append(t)
|
||||
if definition.levy == NEMMosaicLevy.MosaicLevy_Absolute:
|
||||
levy_type = 'absolute'
|
||||
else:
|
||||
levy_type = 'percentile'
|
||||
t = Text('Confirm properties', ui.ICON_SEND,
|
||||
ui.BOLD, 'Levy type:',
|
||||
ui.NORMAL, levy_type)
|
||||
properties.append(t)
|
||||
|
||||
return properties
|
||||
|
||||
|
||||
def split_address(data):
|
||||
|
@ -4,10 +4,14 @@ from apps.nem.mosaic import *
|
||||
from apps.nem.validators import validate
|
||||
from apps.nem import helpers
|
||||
from apps.common import seed
|
||||
from trezor.messages import NEMSupplyChangeType
|
||||
from trezor.messages import NEMModificationType
|
||||
from trezor.messages import NEMImportanceTransferMode
|
||||
from trezor.messages.NEMSignTx import NEMSignTx
|
||||
from trezor.messages.NEMSignedTx import NEMSignedTx
|
||||
from trezor.crypto.curve import ed25519
|
||||
from trezor.crypto import random
|
||||
from trezor.crypto import nem
|
||||
|
||||
|
||||
async def nem_sign_tx(ctx, msg: NEMSignTx):
|
||||
@ -39,7 +43,13 @@ async def nem_sign_tx(ctx, msg: NEMSignTx):
|
||||
|
||||
|
||||
async def _importance_transfer(ctx, node, msg: NEMSignTx):
|
||||
# todo confirms!
|
||||
if msg.importance_transfer.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate:
|
||||
m = 'Activate'
|
||||
else:
|
||||
m = 'Deactivate'
|
||||
await require_confirm_action(ctx, m + ' remote harvesting?')
|
||||
await require_confirm_final(ctx, msg.transaction.fee)
|
||||
|
||||
w = nem_transaction_create_importance_transfer(
|
||||
msg.transaction.network,
|
||||
msg.transaction.timestamp,
|
||||
@ -53,28 +63,52 @@ async def _importance_transfer(ctx, node, msg: NEMSignTx):
|
||||
|
||||
|
||||
async def _aggregate_modification(ctx, node, msg: NEMSignTx):
|
||||
# todo confirms!
|
||||
w = nem_transaction_create_aggregate_modification(
|
||||
msg.transaction.network,
|
||||
msg.transaction.timestamp,
|
||||
_get_public_key(node),
|
||||
msg.transaction.fee,
|
||||
msg.transaction.deadline,
|
||||
len(msg.aggregate_modification.modifications),
|
||||
msg.aggregate_modification.relative_change
|
||||
)
|
||||
if not msg.multisig:
|
||||
await require_confirm_action(ctx, 'Convert account to multisig account?')
|
||||
w = nem_transaction_create_aggregate_modification(
|
||||
msg.transaction.network,
|
||||
msg.transaction.timestamp,
|
||||
_get_public_key(node),
|
||||
msg.transaction.fee,
|
||||
msg.transaction.deadline,
|
||||
len(msg.aggregate_modification.modifications),
|
||||
msg.aggregate_modification.relative_change
|
||||
)
|
||||
|
||||
for m in msg.aggregate_modification.modifications:
|
||||
if m.type == NEMModificationType.CosignatoryModification_Add:
|
||||
action = 'Add'
|
||||
else:
|
||||
action = 'Remove'
|
||||
address = nem.compute_address(m.public_key, msg.transaction.network)
|
||||
await require_confirm_address(ctx, action + ' cosignatory?', address)
|
||||
nem_transaction_write_cosignatory_modification(w, m.type, m.public_key)
|
||||
|
||||
if msg.aggregate_modification.relative_change:
|
||||
if not msg.multisig:
|
||||
action = 'Set minimum cosignatories to '
|
||||
else:
|
||||
action = 'Modify the number of cosignatories by '
|
||||
await require_confirm_action(ctx, action + str(msg.aggregate_modification.relative_change) + '?')
|
||||
|
||||
nem_transaction_write_minimum_cosignatories(w, msg.aggregate_modification.relative_change)
|
||||
|
||||
await require_confirm_final(ctx, msg.transaction.fee)
|
||||
return w
|
||||
|
||||
|
||||
async def _supply_change(ctx, node, msg: NEMSignTx):
|
||||
# todo confirms!
|
||||
await require_confirm_action(ctx, 'Modify supply for "' + msg.supply_change.mosaic + '" under namespace "'
|
||||
+ msg.supply_change.namespace + '"?')
|
||||
if msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Decrease:
|
||||
ask_msg = 'Decrease supply by ' + str(msg.supply_change.delta) + ' whole units?'
|
||||
elif msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Increase:
|
||||
ask_msg = 'Increase supply by ' + str(msg.supply_change.delta) + ' whole units?'
|
||||
else:
|
||||
raise ValueError('Invalid supply change type')
|
||||
await require_confirm_action(ctx, ask_msg)
|
||||
|
||||
await require_confirm_final(ctx, msg.transaction.fee)
|
||||
return nem_transaction_create_mosaic_supply_change(
|
||||
msg.transaction.network,
|
||||
msg.transaction.timestamp,
|
||||
@ -91,10 +125,10 @@ async def _supply_change(ctx, node, msg: NEMSignTx):
|
||||
async def _mosaic_creation(ctx, node, msg: NEMSignTx) -> bytearray:
|
||||
await require_confirm_action(ctx, 'Create mosaic "' + msg.mosaic_creation.definition.mosaic + '" under namespace "'
|
||||
+ msg.mosaic_creation.definition.namespace + '"?')
|
||||
# todo confirm levy!
|
||||
await require_confirm_properties(ctx, msg.mosaic_creation.definition)
|
||||
await require_confirm_final(ctx, 'mosaic', msg.transaction.fee)
|
||||
await require_confirm_fee(ctx, 'Confirm creation fee', msg.mosaic_creation.fee)
|
||||
|
||||
await require_confirm_final(ctx, msg.transaction.fee)
|
||||
return nem_transaction_create_mosaic_creation(
|
||||
msg.transaction.network,
|
||||
msg.transaction.timestamp,
|
||||
@ -118,8 +152,14 @@ async def _mosaic_creation(ctx, node, msg: NEMSignTx) -> bytearray:
|
||||
|
||||
|
||||
async def _provision_namespace(ctx, node, msg: NEMSignTx) -> bytearray:
|
||||
await require_confirm_action(ctx, 'Create provision namespace "' + msg.provision_namespace.namespace + '"?')
|
||||
await require_confirm_final(ctx, 'provision namespace', msg.transaction.fee)
|
||||
if msg.provision_namespace.parent:
|
||||
await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"' +
|
||||
'under namespace "' + msg.provision_namespace.parent + '"?')
|
||||
else:
|
||||
await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"?')
|
||||
await require_confirm_fee(ctx, 'Confirm rental fee', msg.provision_namespace.fee)
|
||||
|
||||
await require_confirm_final(ctx, msg.transaction.fee)
|
||||
return nem_transaction_create_provision_namespace(
|
||||
msg.transaction.network,
|
||||
msg.transaction.timestamp,
|
||||
@ -148,14 +188,16 @@ async def _transfer(ctx, node, msg: NEMSignTx) -> bytes:
|
||||
)
|
||||
|
||||
for mosaic in msg.transfer.mosaics:
|
||||
await require_confirm_action(ctx, 'Confirm transfer of ' + str(mosaic.quantity) +
|
||||
' raw units of ' + mosaic.namespace + '.' + mosaic.mosaic)
|
||||
nem_transaction_write_mosaic(tx, mosaic.namespace, mosaic.mosaic, mosaic.quantity)
|
||||
|
||||
if payload: # confirm unencrypted
|
||||
await require_confirm_transfer(ctx, msg.transfer.recipient, msg.transfer.amount)
|
||||
|
||||
if payload:
|
||||
await require_confirm_payload(ctx, msg.transfer.payload, encrypted)
|
||||
|
||||
await require_confirm_action(ctx, 'todo') # todo!
|
||||
await require_confirm_tx(ctx, msg.transfer.recipient, msg.transfer.amount)
|
||||
|
||||
await require_confirm_final(ctx, msg.transaction.fee)
|
||||
return tx
|
||||
|
||||
|
||||
|
@ -19,6 +19,11 @@ def validate(msg: NEMSignTx):
|
||||
_validate_single_tx(msg)
|
||||
_validate_common(msg.transaction)
|
||||
|
||||
if msg.multisig:
|
||||
_validate_multisig(msg.multisig, msg.transaction.network)
|
||||
if not msg.multisig and msg.cosigning:
|
||||
raise ValueError('No multisig transaction to cosign')
|
||||
|
||||
if msg.transfer:
|
||||
_validate_transfer(msg.transfer, msg.transaction.network)
|
||||
if msg.provision_namespace:
|
||||
@ -28,7 +33,7 @@ def validate(msg: NEMSignTx):
|
||||
if msg.supply_change:
|
||||
_validate_supply_change(msg.supply_change)
|
||||
if msg.aggregate_modification:
|
||||
_validate_aggregate_modification(msg.aggregate_modification, msg.multisig is not None)
|
||||
_validate_aggregate_modification(msg.aggregate_modification, msg.multisig is None)
|
||||
if msg.importance_transfer:
|
||||
_validate_importance_transfer(msg.importance_transfer)
|
||||
|
||||
@ -100,6 +105,11 @@ def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer):
|
||||
_validate_public_key(importance_transfer.public_key, 'Invalid remote account public key provided')
|
||||
|
||||
|
||||
def _validate_multisig(multisig: NEMTransactionCommon, network: int):
|
||||
if multisig.network != network:
|
||||
raise ValueError('Inner transaction network is different')
|
||||
|
||||
|
||||
def _validate_aggregate_modification(aggregate_modification: NEMAggregateModification, creation: bool=False):
|
||||
|
||||
if creation and len(aggregate_modification.modifications) == 0:
|
||||
@ -208,6 +218,12 @@ def _validate_transfer(transfer: NEMTransfer, network: int):
|
||||
if transfer.public_key is not None:
|
||||
_validate_public_key(transfer.public_key, 'Invalid recipient public key')
|
||||
|
||||
if transfer.payload:
|
||||
if len(transfer.payload) > NEM_MAX_PLAIN_PAYLOAD_SIZE:
|
||||
raise ValueError('Payload too large')
|
||||
if transfer.public_key and len(transfer.payload) > NEM_MAX_ENCRYPTED_PAYLOAD_SIZE:
|
||||
raise ValueError('Payload too large')
|
||||
|
||||
if not nem.validate_address(transfer.recipient, network):
|
||||
raise ValueError('Invalid recipient address')
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user