mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-15 09:50:57 +00:00
stellar: refactoring
This commit is contained in:
parent
2af33a6893
commit
501ea6bb2c
@ -9,3 +9,8 @@ REVIEWER = Jan Pochyla <jan.pochyla@satoshilabs.com>
|
||||
-----
|
||||
|
||||
TODO
|
||||
|
||||
# Stellar transactions consist of sha256 of:
|
||||
# - sha256(network passphrase)
|
||||
# - 4-byte unsigned big-endian int type constant (2 for tx)
|
||||
# - public key
|
||||
|
@ -34,6 +34,7 @@ op_wire_types = [
|
||||
wire_types.StellarSetOptionsOp,
|
||||
]
|
||||
|
||||
# https://github.com/stellar/go/blob/e0ffe19f58879d3c31e2976b97a5bf10e13a337b/xdr/xdr_generated.go#L584
|
||||
ASSET_TYPE_NATIVE = const(0)
|
||||
ASSET_TYPE_ALPHANUM4 = const(1)
|
||||
ASSET_TYPE_ALPHANUM12 = const(2)
|
||||
|
@ -36,5 +36,5 @@ async def _show(ctx, address: str):
|
||||
cancel_style=ui.BTN_KEY)
|
||||
|
||||
|
||||
def _split_address(address: str):
|
||||
def _split_address(address: str): # todo merge with NEM
|
||||
return chunks(address, 17)
|
||||
|
@ -1,4 +1,4 @@
|
||||
from apps.common.confirm import *
|
||||
from apps.common.confirm import require_confirm, require_hold_to_confirm
|
||||
from apps.stellar import consts
|
||||
from apps.stellar import helpers
|
||||
from trezor import ui
|
||||
@ -66,6 +66,7 @@ def format_address(pubkey: bytes) -> str:
|
||||
return helpers.address_from_public_key(pubkey)
|
||||
|
||||
|
||||
# todo merge with nem
|
||||
def split(text):
|
||||
return utils.chunks(text, 17)
|
||||
|
||||
|
@ -1,44 +1,46 @@
|
||||
from apps.stellar.operations.serialize import *
|
||||
from apps.stellar.operations.layout import *
|
||||
from apps.stellar.operations import serialize
|
||||
from apps.stellar.operations import layout
|
||||
from apps.stellar import consts
|
||||
from apps.stellar import writers
|
||||
|
||||
|
||||
async def operation(ctx, w, op):
|
||||
if op.source_account:
|
||||
await confirm_source_account(ctx, op.source_account)
|
||||
serialize_account(w, op.source_account)
|
||||
write_uint32(w, get_op_code(op))
|
||||
if isinstance(op, StellarAccountMergeOp):
|
||||
await confirm_account_merge_op(ctx, op)
|
||||
serialize_account_merge_op(w, op)
|
||||
elif isinstance(op, StellarAllowTrustOp):
|
||||
await confirm_allow_trust_op(ctx, op)
|
||||
serialize_allow_trust_op(w, op)
|
||||
elif isinstance(op, StellarBumpSequenceOp):
|
||||
await confirm_bump_sequence_op(ctx, op)
|
||||
serialize_bump_sequence_op(w, op)
|
||||
elif isinstance(op, StellarChangeTrustOp):
|
||||
await confirm_change_trust_op(ctx, op)
|
||||
serialize_change_trust_op(w, op)
|
||||
elif isinstance(op, StellarCreateAccountOp):
|
||||
await confirm_create_account_op(ctx, op)
|
||||
serialize_create_account_op(w, op)
|
||||
elif isinstance(op, StellarCreatePassiveOfferOp):
|
||||
await confirm_create_passive_offer_op(ctx, op)
|
||||
serialize_create_passive_offer_op(w, op)
|
||||
elif isinstance(op, StellarManageDataOp):
|
||||
await confirm_manage_data_op(ctx, op)
|
||||
serialize_manage_data_op(w, op)
|
||||
elif isinstance(op, StellarManageOfferOp):
|
||||
await confirm_manage_offer_op(ctx, op)
|
||||
serialize_manage_offer_op(w, op)
|
||||
elif isinstance(op, StellarPathPaymentOp):
|
||||
await confirm_path_payment_op(ctx, op)
|
||||
serialize_path_payment_op(w, op)
|
||||
elif isinstance(op, StellarPaymentOp):
|
||||
await confirm_payment_op(ctx, op)
|
||||
serialize_payment_op(w, op)
|
||||
elif isinstance(op, StellarSetOptionsOp):
|
||||
await confirm_set_options_op(ctx, op)
|
||||
serialize_set_options_op(w, op)
|
||||
await layout.confirm_source_account(ctx, op.source_account)
|
||||
serialize.serialize_account(w, op.source_account)
|
||||
writers.write_uint32(w, consts.get_op_code(op))
|
||||
if isinstance(op, serialize.StellarAccountMergeOp):
|
||||
await layout.confirm_account_merge_op(ctx, op)
|
||||
serialize.serialize_account_merge_op(w, op)
|
||||
elif isinstance(op, serialize.StellarAllowTrustOp):
|
||||
await layout.confirm_allow_trust_op(ctx, op)
|
||||
serialize.serialize_allow_trust_op(w, op)
|
||||
elif isinstance(op, serialize.StellarBumpSequenceOp):
|
||||
await layout.confirm_bump_sequence_op(ctx, op)
|
||||
serialize.serialize_bump_sequence_op(w, op)
|
||||
elif isinstance(op, serialize.StellarChangeTrustOp):
|
||||
await layout.confirm_change_trust_op(ctx, op)
|
||||
serialize.serialize_change_trust_op(w, op)
|
||||
elif isinstance(op, serialize.StellarCreateAccountOp):
|
||||
await layout.confirm_create_account_op(ctx, op)
|
||||
serialize.serialize_create_account_op(w, op)
|
||||
elif isinstance(op, serialize.StellarCreatePassiveOfferOp):
|
||||
await layout.confirm_create_passive_offer_op(ctx, op)
|
||||
serialize.serialize_create_passive_offer_op(w, op)
|
||||
elif isinstance(op, serialize.StellarManageDataOp):
|
||||
await layout.confirm_manage_data_op(ctx, op)
|
||||
serialize.serialize_manage_data_op(w, op)
|
||||
elif isinstance(op, serialize.StellarManageOfferOp):
|
||||
await layout.confirm_manage_offer_op(ctx, op)
|
||||
serialize.serialize_manage_offer_op(w, op)
|
||||
elif isinstance(op, serialize.StellarPathPaymentOp):
|
||||
await layout.confirm_path_payment_op(ctx, op)
|
||||
serialize.serialize_path_payment_op(w, op)
|
||||
elif isinstance(op, serialize.StellarPaymentOp):
|
||||
await layout.confirm_payment_op(ctx, op)
|
||||
serialize.serialize_payment_op(w, op)
|
||||
elif isinstance(op, serialize.StellarSetOptionsOp):
|
||||
await layout.confirm_set_options_op(ctx, op)
|
||||
serialize.serialize_set_options_op(w, op)
|
||||
else:
|
||||
raise ValueError('Stellar: unknown operation')
|
||||
raise ValueError('serialize.Stellar: unknown operation')
|
||||
|
@ -1,7 +1,7 @@
|
||||
from apps.stellar.layout import *
|
||||
from apps.stellar.layout import split, format_address, format_amount, ui, trim_to_rows, require_confirm
|
||||
from apps.stellar import consts
|
||||
from trezor.messages import ButtonRequestType
|
||||
from trezor.ui.text import Text
|
||||
|
||||
from trezor.messages.StellarAccountMergeOp import StellarAccountMergeOp
|
||||
from trezor.messages.StellarAssetType import StellarAssetType
|
||||
from trezor.messages.StellarAllowTrustOp import StellarAllowTrustOp
|
||||
@ -27,12 +27,12 @@ async def confirm_source_account(ctx, source_account: bytes):
|
||||
|
||||
async def confirm_allow_trust_op(ctx, op: StellarAllowTrustOp):
|
||||
if op.is_authorized:
|
||||
text = 'Allow'
|
||||
text = 'Allow Trust'
|
||||
else:
|
||||
text = 'Revoke'
|
||||
text = 'Revoke Trust'
|
||||
content = Text('Confirm operation', ui.ICON_CONFIRM,
|
||||
ui.BOLD, text + ' Trust',
|
||||
ui.NORMAL, "of '" + op.asset_code + "' by:",
|
||||
ui.BOLD, text,
|
||||
ui.NORMAL, 'of %s by:' % op.asset_code,
|
||||
ui.MONO, *split(trim_to_rows(format_address(op.trusted_account), 3)),
|
||||
icon_color=ui.GREEN)
|
||||
|
||||
@ -59,13 +59,13 @@ async def confirm_bump_sequence_op(ctx, op: StellarBumpSequenceOp):
|
||||
|
||||
async def confirm_change_trust_op(ctx, op: StellarChangeTrustOp):
|
||||
if op.limit == 0:
|
||||
text = 'Delete'
|
||||
text = 'Delete Trust'
|
||||
else:
|
||||
text = 'Add'
|
||||
text = 'Add Trust'
|
||||
content = Text('Confirm operation', ui.ICON_CONFIRM,
|
||||
ui.BOLD, text + ' Trust',
|
||||
ui.NORMAL, 'Asset: ' + op.asset.code,
|
||||
ui.NORMAL, 'Amount: ' + format_amount(op.limit, ticker=False),
|
||||
ui.BOLD, text,
|
||||
ui.NORMAL, 'Asset: %s' % op.asset.code,
|
||||
ui.NORMAL, 'Amount: %s' % format_amount(op.limit, ticker=False),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
await confirm_asset_issuer(ctx, op.asset)
|
||||
@ -74,7 +74,7 @@ async def confirm_change_trust_op(ctx, op: StellarChangeTrustOp):
|
||||
async def confirm_create_account_op(ctx, op: StellarCreateAccountOp):
|
||||
content = Text('Confirm operation', ui.ICON_CONFIRM,
|
||||
ui.BOLD, 'Create Account',
|
||||
ui.NORMAL, 'with ' + format_amount(op.starting_balance),
|
||||
ui.NORMAL, 'with %s' % format_amount(op.starting_balance),
|
||||
ui.MONO, *split(trim_to_rows(format_address(op.new_account), 3)),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
@ -96,32 +96,30 @@ async def confirm_manage_offer_op(ctx, op: StellarManageOfferOp):
|
||||
text = 'Delete'
|
||||
else:
|
||||
text = 'Update'
|
||||
text += ' #' + str(op.offer_id)
|
||||
text += ' #%d' % op.offer_id
|
||||
await _confirm_offer(ctx, text, op)
|
||||
|
||||
|
||||
# todo scale? this is inconsistent with T1 (op.amount should? use divisibility)
|
||||
async def _confirm_offer(ctx, text, op):
|
||||
content = Text('Confirm operation', ui.ICON_CONFIRM,
|
||||
ui.BOLD, text,
|
||||
ui.NORMAL, 'Sell ' + str(op.amount) + ' ' + op.selling_asset.code,
|
||||
ui.NORMAL, 'For ' + str(op.price_n / op.price_d),
|
||||
ui.NORMAL, 'Per ' + format_asset_code(op.buying_asset),
|
||||
ui.NORMAL, 'Sell %s %s' % (format_amount(op.amount, ticker=False), op.selling_asset.code),
|
||||
ui.NORMAL, 'For %f' % (op.price_n / op.price_d),
|
||||
ui.NORMAL, 'Per %s' % format_asset_code(op.buying_asset),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
await confirm_asset_issuer(ctx, op.selling_asset)
|
||||
await confirm_asset_issuer(ctx, op.buying_asset)
|
||||
|
||||
|
||||
# todo done
|
||||
async def confirm_manage_data_op(ctx, op: StellarManageDataOp):
|
||||
from trezor.crypto.hashlib import sha256
|
||||
if op.value:
|
||||
text = 'Set'
|
||||
title = 'Set'
|
||||
else:
|
||||
text = 'Clear'
|
||||
title = 'Clear'
|
||||
content = Text('Confirm operation', ui.ICON_CONFIRM,
|
||||
ui.BOLD, text + ' data value key',
|
||||
ui.BOLD, '%s data value key' % title,
|
||||
ui.MONO, *split(op.key),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
@ -129,7 +127,7 @@ async def confirm_manage_data_op(ctx, op: StellarManageDataOp):
|
||||
digest = sha256(op.value).digest()
|
||||
digest_str = hexlify(digest).decode()
|
||||
content = Text('Confirm operation', ui.ICON_CONFIRM,
|
||||
ui.BOLD, ' Value (SHA-256):',
|
||||
ui.BOLD, 'Value (SHA-256):',
|
||||
ui.MONO, *split(digest_str),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
@ -137,8 +135,8 @@ async def confirm_manage_data_op(ctx, op: StellarManageDataOp):
|
||||
|
||||
async def confirm_path_payment_op(ctx, op: StellarPathPaymentOp):
|
||||
content = Text('Confirm operation', ui.ICON_CONFIRM,
|
||||
ui.BOLD, 'Path Pay ' + format_amount(op.destination_amount, ticker=False),
|
||||
ui.BOLD, format_asset_code(op.destination_asset) + ' to:',
|
||||
ui.BOLD, 'Path Pay %s' % format_amount(op.destination_amount, ticker=False),
|
||||
ui.BOLD, '%s to:' % format_asset_code(op.destination_asset),
|
||||
ui.MONO, *split(trim_to_rows(format_address(op.destination_account), 3)),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
@ -157,8 +155,8 @@ async def confirm_path_payment_op(ctx, op: StellarPathPaymentOp):
|
||||
|
||||
async def confirm_payment_op(ctx, op: StellarPaymentOp):
|
||||
content = Text('Confirm operation', ui.ICON_CONFIRM,
|
||||
ui.BOLD, 'Pay ' + format_amount(op.amount, ticker=False),
|
||||
ui.BOLD, format_asset_code(op.asset) + ' to:',
|
||||
ui.BOLD, 'Pay %s' % format_amount(op.amount, ticker=False),
|
||||
ui.BOLD, '%s to:' % format_asset_code(op.asset),
|
||||
ui.MONO, *split(trim_to_rows(format_address(op.destination_account), 3)),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
@ -201,23 +199,22 @@ async def confirm_set_options_op(ctx, op: StellarSetOptionsOp):
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
if op.signer_type is not None:
|
||||
if op.signer_weight > 0:
|
||||
text = 'Add Signer'
|
||||
text = 'Add Signer (%s)'
|
||||
else:
|
||||
text = 'Remove Signer'
|
||||
text = 'Remove Signer (%s)'
|
||||
if op.signer_type == consts.SIGN_TYPE_ACCOUNT:
|
||||
text += ' (acc)'
|
||||
content = Text('Confirm operation', ui.ICON_CONFIRM,
|
||||
ui.BOLD, text,
|
||||
ui.BOLD, text % 'acc',
|
||||
ui.MONO, *split(format_address(op.signer_key)),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
elif op.signer_type in [consts.SIGN_TYPE_PRE_AUTH, consts.SIGN_TYPE_HASH]:
|
||||
elif op.signer_type in (consts.SIGN_TYPE_PRE_AUTH, consts.SIGN_TYPE_HASH):
|
||||
if op.signer_type == consts.SIGN_TYPE_PRE_AUTH:
|
||||
text += ' (auth)'
|
||||
signer_type = 'auth'
|
||||
else:
|
||||
text += ' (hash)'
|
||||
signer_type = 'hash'
|
||||
content = Text('Confirm operation', ui.ICON_CONFIRM,
|
||||
ui.BOLD, text,
|
||||
ui.BOLD, text % signer_type,
|
||||
ui.MONO, *split(hexlify(op.signer_key).decode()),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
@ -225,20 +222,20 @@ async def confirm_set_options_op(ctx, op: StellarSetOptionsOp):
|
||||
raise ValueError('Stellar: invalid signer type')
|
||||
|
||||
|
||||
def _format_thresholds(op: StellarSetOptionsOp) -> ():
|
||||
def _format_thresholds(op: StellarSetOptionsOp) -> tuple:
|
||||
text = ()
|
||||
if op.master_weight is not None:
|
||||
text += ('Master Weight: ' + str(op.master_weight), )
|
||||
text += ('Master Weight: %d' % op.master_weight, )
|
||||
if op.low_threshold is not None:
|
||||
text += ('Low: ' + str(op.low_threshold), )
|
||||
text += ('Low: %d' % op.low_threshold, )
|
||||
if op.medium_threshold is not None:
|
||||
text += ('Medium: ' + str(op.medium_threshold), )
|
||||
text += ('Medium: %d' % op.medium_threshold, )
|
||||
if op.high_threshold is not None:
|
||||
text += ('High: ' + str(op.high_threshold), )
|
||||
text += ('High: %d' % op.high_threshold, )
|
||||
return text
|
||||
|
||||
|
||||
def _format_flags(flags: int) -> ():
|
||||
def _format_flags(flags: int) -> tuple:
|
||||
if flags > consts.FLAGS_MAX_SIZE:
|
||||
raise ValueError('Stellar: invalid')
|
||||
text = ()
|
||||
@ -261,7 +258,7 @@ async def confirm_asset_issuer(ctx, asset: StellarAssetType):
|
||||
if asset is None or asset.type == consts.ASSET_TYPE_NATIVE:
|
||||
return
|
||||
content = Text('Confirm issuer', ui.ICON_CONFIRM,
|
||||
ui.BOLD, asset.code + ' issuer:',
|
||||
ui.BOLD, '%s issuer:' % asset.code,
|
||||
ui.MONO, *split(format_address(asset.issuer)),
|
||||
icon_color=ui.GREEN)
|
||||
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
|
||||
|
@ -1,3 +1,5 @@
|
||||
from apps.stellar import writers
|
||||
from apps.stellar import consts
|
||||
from trezor.messages.StellarAccountMergeOp import StellarAccountMergeOp
|
||||
from trezor.messages.StellarAssetType import StellarAssetType
|
||||
from trezor.messages.StellarAllowTrustOp import StellarAllowTrustOp
|
||||
@ -10,156 +12,154 @@ from trezor.messages.StellarManageOfferOp import StellarManageOfferOp
|
||||
from trezor.messages.StellarPathPaymentOp import StellarPathPaymentOp
|
||||
from trezor.messages.StellarPaymentOp import StellarPaymentOp
|
||||
from trezor.messages.StellarSetOptionsOp import StellarSetOptionsOp
|
||||
from apps.stellar.consts import *
|
||||
from apps.stellar.writers import *
|
||||
|
||||
|
||||
def serialize_account_merge_op(w, msg: StellarAccountMergeOp):
|
||||
write_pubkey(w, msg.destination_account)
|
||||
writers.write_pubkey(w, msg.destination_account)
|
||||
|
||||
|
||||
def serialize_allow_trust_op(w, msg: StellarAllowTrustOp):
|
||||
# trustor account (the account being allowed to access the asset)
|
||||
write_pubkey(w, msg.trusted_account)
|
||||
write_uint32(w, msg.asset_type)
|
||||
writers.write_pubkey(w, msg.trusted_account)
|
||||
writers.write_uint32(w, msg.asset_type)
|
||||
_serialize_asset_code(w, msg.asset_type, msg.asset_code)
|
||||
|
||||
write_bool(w, msg.is_authorized)
|
||||
writers.write_bool(w, msg.is_authorized)
|
||||
|
||||
|
||||
def serialize_bump_sequence_op(w, msg: StellarBumpSequenceOp):
|
||||
write_uint64(w, msg.bump_to)
|
||||
writers.write_uint64(w, msg.bump_to)
|
||||
|
||||
|
||||
def serialize_change_trust_op(w, msg: StellarChangeTrustOp):
|
||||
_serialize_asset(w, msg.asset)
|
||||
write_uint64(w, msg.limit)
|
||||
writers.write_uint64(w, msg.limit)
|
||||
|
||||
|
||||
def serialize_create_account_op(w, msg: StellarCreateAccountOp):
|
||||
write_pubkey(w, msg.new_account)
|
||||
write_uint64(w, msg.starting_balance)
|
||||
writers.write_pubkey(w, msg.new_account)
|
||||
writers.write_uint64(w, msg.starting_balance)
|
||||
|
||||
|
||||
def serialize_create_passive_offer_op(w, msg: StellarCreatePassiveOfferOp):
|
||||
_serialize_asset(w, msg.selling_asset)
|
||||
_serialize_asset(w, msg.buying_asset)
|
||||
write_uint64(w, msg.amount)
|
||||
write_uint32(w, msg.price_n)
|
||||
write_uint32(w, msg.price_d)
|
||||
writers.write_uint64(w, msg.amount)
|
||||
writers.write_uint32(w, msg.price_n)
|
||||
writers.write_uint32(w, msg.price_d)
|
||||
|
||||
|
||||
def serialize_manage_data_op(w, msg: StellarManageDataOp):
|
||||
if len(msg.key) > 64:
|
||||
raise ValueError('Stellar: max length of a key is 64 bytes')
|
||||
write_string(w, msg.key)
|
||||
write_bool(w, bool(msg.value))
|
||||
writers.write_string(w, msg.key)
|
||||
writers.write_bool(w, bool(msg.value))
|
||||
if msg.value:
|
||||
write_uint32(w, len(msg.value))
|
||||
write_bytes(w, msg.value)
|
||||
writers.write_uint32(w, len(msg.value))
|
||||
writers.write_bytes(w, msg.value)
|
||||
|
||||
|
||||
def serialize_manage_offer_op(w, msg: StellarManageOfferOp):
|
||||
_serialize_asset(w, msg.selling_asset)
|
||||
_serialize_asset(w, msg.buying_asset)
|
||||
write_uint64(w, msg.amount) # amount to sell
|
||||
write_uint32(w, msg.price_n) # numerator
|
||||
write_uint32(w, msg.price_d) # denominator
|
||||
write_uint64(w, msg.offer_id)
|
||||
writers.write_uint64(w, msg.amount) # amount to sell
|
||||
writers.write_uint32(w, msg.price_n) # numerator
|
||||
writers.write_uint32(w, msg.price_d) # denominator
|
||||
writers.write_uint64(w, msg.offer_id)
|
||||
|
||||
|
||||
def serialize_path_payment_op(w, msg: StellarPathPaymentOp):
|
||||
_serialize_asset(w, msg.send_asset)
|
||||
write_uint64(w, msg.send_max)
|
||||
write_pubkey(w, msg.destination_account)
|
||||
writers.write_uint64(w, msg.send_max)
|
||||
writers.write_pubkey(w, msg.destination_account)
|
||||
|
||||
_serialize_asset(w, msg.destination_asset)
|
||||
write_uint64(w, msg.destination_amount)
|
||||
write_uint32(w, len(msg.paths))
|
||||
writers.write_uint64(w, msg.destination_amount)
|
||||
writers.write_uint32(w, len(msg.paths))
|
||||
for p in msg.paths:
|
||||
_serialize_asset(w, p)
|
||||
|
||||
|
||||
def serialize_payment_op(w, msg: StellarPaymentOp):
|
||||
write_pubkey(w, msg.destination_account)
|
||||
writers.write_pubkey(w, msg.destination_account)
|
||||
_serialize_asset(w, msg.asset)
|
||||
write_uint64(w, msg.amount)
|
||||
writers.write_uint64(w, msg.amount)
|
||||
|
||||
|
||||
def serialize_set_options_op(w, msg: StellarSetOptionsOp):
|
||||
# inflation destination
|
||||
write_bool(w, bool(msg.inflation_destination_account))
|
||||
writers.write_bool(w, bool(msg.inflation_destination_account))
|
||||
if msg.inflation_destination_account:
|
||||
write_pubkey(w, msg.inflation_destination_account)
|
||||
writers.write_pubkey(w, msg.inflation_destination_account)
|
||||
|
||||
# clear flags
|
||||
write_bool(w, bool(msg.clear_flags))
|
||||
writers.write_bool(w, bool(msg.clear_flags))
|
||||
if msg.clear_flags:
|
||||
write_uint32(w, msg.clear_flags)
|
||||
writers.write_uint32(w, msg.clear_flags)
|
||||
|
||||
# set flags
|
||||
write_bool(w, bool(msg.set_flags))
|
||||
writers.write_bool(w, bool(msg.set_flags))
|
||||
if msg.set_flags:
|
||||
write_uint32(w, msg.set_flags)
|
||||
writers.write_uint32(w, msg.set_flags)
|
||||
|
||||
# account thresholds
|
||||
write_bool(w, bool(msg.master_weight))
|
||||
writers.write_bool(w, bool(msg.master_weight))
|
||||
if msg.master_weight:
|
||||
write_uint32(w, msg.master_weight)
|
||||
writers.write_uint32(w, msg.master_weight)
|
||||
|
||||
write_bool(w, bool(msg.low_threshold))
|
||||
writers.write_bool(w, bool(msg.low_threshold))
|
||||
if msg.low_threshold:
|
||||
write_uint32(w, msg.low_threshold)
|
||||
writers.write_uint32(w, msg.low_threshold)
|
||||
|
||||
write_bool(w, bool(msg.medium_threshold))
|
||||
writers.write_bool(w, bool(msg.medium_threshold))
|
||||
if msg.medium_threshold:
|
||||
write_uint32(w, msg.medium_threshold)
|
||||
writers.write_uint32(w, msg.medium_threshold)
|
||||
|
||||
write_bool(w, bool(msg.high_threshold))
|
||||
writers.write_bool(w, bool(msg.high_threshold))
|
||||
if msg.high_threshold:
|
||||
write_uint32(w, msg.high_threshold)
|
||||
writers.write_uint32(w, msg.high_threshold)
|
||||
|
||||
# home domain
|
||||
write_bool(w, bool(msg.home_domain))
|
||||
writers.write_bool(w, bool(msg.home_domain))
|
||||
if msg.home_domain:
|
||||
if len(msg.home_domain) > 32:
|
||||
raise ValueError('Stellar: max length of a home domain is 32 bytes')
|
||||
write_string(w, msg.home_domain)
|
||||
writers.write_string(w, msg.home_domain)
|
||||
|
||||
# signer
|
||||
write_bool(w, bool(msg.signer_type))
|
||||
writers.write_bool(w, bool(msg.signer_type))
|
||||
if msg.signer_type:
|
||||
# signer type
|
||||
write_uint32(w, msg.signer_type)
|
||||
write_bytes(w, msg.signer_key)
|
||||
write_uint32(w, msg.signer_weight)
|
||||
writers.write_uint32(w, msg.signer_type)
|
||||
writers.write_bytes(w, msg.signer_key)
|
||||
writers.write_uint32(w, msg.signer_weight)
|
||||
|
||||
|
||||
def serialize_account(w, source_account: bytes):
|
||||
if source_account is None:
|
||||
write_bool(w, False)
|
||||
writers.write_bool(w, False)
|
||||
return
|
||||
write_pubkey(w, source_account)
|
||||
writers.write_pubkey(w, source_account)
|
||||
|
||||
|
||||
def _serialize_asset_code(w, asset_type: int, asset_code: str):
|
||||
code = bytearray(asset_code)
|
||||
if asset_type == ASSET_TYPE_NATIVE:
|
||||
if asset_type == consts.ASSET_TYPE_NATIVE:
|
||||
return # nothing is needed
|
||||
elif asset_type == ASSET_TYPE_ALPHANUM4:
|
||||
elif asset_type == consts.ASSET_TYPE_ALPHANUM4:
|
||||
# pad with zeros to 4 chars
|
||||
write_bytes(w, code + bytearray([0] * (4 - len(code))))
|
||||
elif asset_type == ASSET_TYPE_ALPHANUM12:
|
||||
writers.write_bytes(w, code + bytearray([0] * (4 - len(code))))
|
||||
elif asset_type == consts.ASSET_TYPE_ALPHANUM12:
|
||||
# pad with zeros to 12 chars
|
||||
write_bytes(w, code + bytearray([0] * (12 - len(code))))
|
||||
writers.write_bytes(w, code + bytearray([0] * (12 - len(code))))
|
||||
else:
|
||||
raise ValueError('Stellar: invalid asset type')
|
||||
|
||||
|
||||
def _serialize_asset(w, asset: StellarAssetType):
|
||||
if asset is None:
|
||||
write_uint32(w, 0)
|
||||
writers.write_uint32(w, 0)
|
||||
return
|
||||
write_uint32(w, asset.type)
|
||||
writers.write_uint32(w, asset.type)
|
||||
_serialize_asset_code(w, asset.type, asset.code)
|
||||
write_pubkey(w, asset.issuer)
|
||||
writers.write_pubkey(w, asset.issuer)
|
||||
|
@ -1,9 +1,8 @@
|
||||
from apps.common import seed
|
||||
from apps.stellar.writers import *
|
||||
from apps.stellar import writers
|
||||
from apps.stellar.operations import operation
|
||||
from apps.stellar import layout
|
||||
from apps.stellar import consts
|
||||
from apps.stellar import helpers
|
||||
from trezor.messages.StellarSignTx import StellarSignTx
|
||||
from trezor.messages.StellarTxOpRequest import StellarTxOpRequest
|
||||
from trezor.messages.StellarSignedTx import StellarSignedTx
|
||||
@ -16,73 +15,17 @@ async def sign_tx(ctx, msg: StellarSignTx):
|
||||
if msg.num_operations == 0:
|
||||
raise ValueError('Stellar: At least one operation is required')
|
||||
|
||||
network_passphrase_hash = sha256(msg.network_passphrase).digest()
|
||||
|
||||
# Stellar transactions consist of sha256 of:
|
||||
# - sha256(network passphrase)
|
||||
# - 4-byte unsigned big-endian int type constant (2 for tx)
|
||||
# - public key
|
||||
|
||||
w = bytearray()
|
||||
write_bytes(w, network_passphrase_hash)
|
||||
write_bytes(w, consts.TX_TYPE)
|
||||
|
||||
node = await seed.derive_node(ctx, msg.address_n, consts.STELLAR_CURVE)
|
||||
pubkey = seed.remove_ed25519_public_key_prefix(node.public_key())
|
||||
write_pubkey(w, pubkey)
|
||||
if msg.source_account != pubkey:
|
||||
raise ValueError('Stellar: source account does not match address_n')
|
||||
|
||||
# confirm init
|
||||
await layout.require_confirm_init(ctx, pubkey, msg.network_passphrase)
|
||||
|
||||
write_uint32(w, msg.fee)
|
||||
write_uint64(w, msg.sequence_number)
|
||||
|
||||
# timebounds are only present if timebounds_start or timebounds_end is non-zero
|
||||
if msg.timebounds_start or msg.timebounds_end:
|
||||
write_bool(w, True)
|
||||
# timebounds are sent as uint32s since that's all we can display, but they must be hashed as 64bit
|
||||
write_uint64(w, msg.timebounds_start)
|
||||
write_uint64(w, msg.timebounds_end)
|
||||
else:
|
||||
write_bool(w, False)
|
||||
|
||||
write_uint32(w, msg.memo_type)
|
||||
if msg.memo_type == consts.MEMO_TYPE_NONE:
|
||||
# nothing is serialized
|
||||
memo_confirm_text = ''
|
||||
elif msg.memo_type == consts.MEMO_TYPE_TEXT:
|
||||
# Text: 4 bytes (size) + up to 28 bytes
|
||||
if len(msg.memo_text) > 28:
|
||||
raise ValueError('Stellar: max length of a memo text is 28 bytes')
|
||||
write_string(w, msg.memo_text)
|
||||
memo_confirm_text = msg.memo_text
|
||||
elif msg.memo_type == consts.MEMO_TYPE_ID:
|
||||
# ID: 64 bit unsigned integer
|
||||
write_uint64(w, msg.memo_id)
|
||||
memo_confirm_text = str(msg.memo_id)
|
||||
elif msg.memo_type in [consts.MEMO_TYPE_HASH, consts.MEMO_TYPE_RETURN]:
|
||||
# Hash/Return: 32 byte hash
|
||||
write_bytes(w, bytearray(msg.memo_hash))
|
||||
memo_confirm_text = hexlify(msg.memo_hash).decode()
|
||||
else:
|
||||
raise ValueError('Stellar invalid memo type')
|
||||
await layout.require_confirm_memo(ctx, msg.memo_type, memo_confirm_text)
|
||||
|
||||
write_uint32(w, msg.num_operations)
|
||||
for i in range(msg.num_operations):
|
||||
op = await ctx.call(StellarTxOpRequest(), *consts.op_wire_types)
|
||||
await operation(ctx, w, op)
|
||||
|
||||
# 4 null bytes representing a (currently unused) empty union
|
||||
write_uint32(w, 0)
|
||||
|
||||
# final confirm
|
||||
await layout.require_confirm_final(ctx, msg.fee, msg.num_operations)
|
||||
w = bytearray()
|
||||
await _init(ctx, w, pubkey, msg)
|
||||
_timebounds(w, msg.timebounds_start, msg.timebounds_end)
|
||||
await _memo(ctx, w, msg)
|
||||
await _operations(ctx, w, msg.num_operations)
|
||||
await _final(ctx, w, msg)
|
||||
|
||||
# sign
|
||||
# (note that the signature does not include the 4-byte hint since it can be calculated from the public key)
|
||||
digest = sha256(w).digest()
|
||||
signature = ed25519.sign(node.private_key(), digest)
|
||||
|
||||
@ -90,6 +33,70 @@ async def sign_tx(ctx, msg: StellarSignTx):
|
||||
return StellarSignedTx(pubkey, signature)
|
||||
|
||||
|
||||
async def _final(ctx, w: bytearray, msg: StellarSignTx):
|
||||
# 4 null bytes representing a (currently unused) empty union
|
||||
writers.write_uint32(w, 0)
|
||||
# final confirm
|
||||
await layout.require_confirm_final(ctx, msg.fee, msg.num_operations)
|
||||
|
||||
|
||||
async def _init(ctx, w: bytearray, pubkey: bytes, msg: StellarSignTx):
|
||||
network_passphrase_hash = sha256(msg.network_passphrase).digest()
|
||||
writers.write_bytes(w, network_passphrase_hash)
|
||||
writers.write_bytes(w, consts.TX_TYPE)
|
||||
|
||||
writers.write_pubkey(w, pubkey)
|
||||
if msg.source_account != pubkey:
|
||||
raise ValueError('Stellar: source account does not match address_n')
|
||||
writers.write_uint32(w, msg.fee)
|
||||
writers.write_uint64(w, msg.sequence_number)
|
||||
|
||||
# confirm init
|
||||
await layout.require_confirm_init(ctx, pubkey, msg.network_passphrase)
|
||||
|
||||
|
||||
def _timebounds(w: bytearray, start: int, end: int):
|
||||
# timebounds are only present if timebounds_start or timebounds_end is non-zero
|
||||
if start or end:
|
||||
writers.write_bool(w, True)
|
||||
# timebounds are sent as uint32s since that's all we can display, but they must be hashed as 64bit
|
||||
writers.write_uint64(w, start)
|
||||
writers.write_uint64(w, end)
|
||||
else:
|
||||
writers.write_bool(w, False)
|
||||
|
||||
|
||||
async def _operations(ctx, w: bytearray, num_operations: int):
|
||||
writers.write_uint32(w, num_operations)
|
||||
for i in range(num_operations):
|
||||
op = await ctx.call(StellarTxOpRequest(), *consts.op_wire_types)
|
||||
await operation(ctx, w, op)
|
||||
|
||||
|
||||
async def _memo(ctx, w: bytearray, msg: StellarSignTx):
|
||||
writers.write_uint32(w, msg.memo_type)
|
||||
if msg.memo_type == consts.MEMO_TYPE_NONE:
|
||||
# nothing is serialized
|
||||
memo_confirm_text = ''
|
||||
elif msg.memo_type == consts.MEMO_TYPE_TEXT:
|
||||
# Text: 4 bytes (size) + up to 28 bytes
|
||||
if len(msg.memo_text) > 28:
|
||||
raise ValueError('Stellar: max length of a memo text is 28 bytes')
|
||||
writers.write_string(w, msg.memo_text)
|
||||
memo_confirm_text = msg.memo_text
|
||||
elif msg.memo_type == consts.MEMO_TYPE_ID:
|
||||
# ID: 64 bit unsigned integer
|
||||
writers.write_uint64(w, msg.memo_id)
|
||||
memo_confirm_text = str(msg.memo_id)
|
||||
elif msg.memo_type in (consts.MEMO_TYPE_HASH, consts.MEMO_TYPE_RETURN):
|
||||
# Hash/Return: 32 byte hash
|
||||
writers.write_bytes(w, bytearray(msg.memo_hash))
|
||||
memo_confirm_text = hexlify(msg.memo_hash).decode()
|
||||
else:
|
||||
raise ValueError('Stellar invalid memo type')
|
||||
await layout.require_confirm_memo(ctx, msg.memo_type, memo_confirm_text)
|
||||
|
||||
|
||||
def node_derive(root, address_n: list):
|
||||
node = root.clone()
|
||||
node.derive_path(address_n)
|
||||
|
Loading…
Reference in New Issue
Block a user