1
0
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:
Tomas Susanka 2018-04-04 16:23:44 +02:00 committed by Jan Pochyla
parent 6aef64d367
commit 561ca35a08
4 changed files with 167 additions and 58 deletions

View File

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

View File

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

View File

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

View File

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