From 2af33a6893362dd7255c590b3412c7de9d1bc72e Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 5 Jun 2018 16:29:49 +0200 Subject: [PATCH] stellar: operations layout --- src/apps/stellar/consts.py | 25 ++- src/apps/stellar/layout.py | 35 +++- src/apps/stellar/operations.py | 192 ----------------- src/apps/stellar/operations/__init__.py | 44 ++++ src/apps/stellar/operations/layout.py | 267 ++++++++++++++++++++++++ 5 files changed, 359 insertions(+), 204 deletions(-) delete mode 100644 src/apps/stellar/operations.py create mode 100644 src/apps/stellar/operations/__init__.py create mode 100644 src/apps/stellar/operations/layout.py diff --git a/src/apps/stellar/consts.py b/src/apps/stellar/consts.py index cd4c80633e..dcb886036e 100644 --- a/src/apps/stellar/consts.py +++ b/src/apps/stellar/consts.py @@ -1,7 +1,11 @@ from trezor.messages import wire_types from micropython import const -# source: https://github.com/stellar/go/blob/master/xdr/Stellar-transaction.x +STELLAR_CURVE = 'ed25519' +TX_TYPE = bytearray('\x00\x00\x00\x02') + +# source: https://github.com/stellar/go/blob/3d2c1defe73dbfed00146ebe0e8d7e07ce4bb1b6/xdr/Stellar-transaction.x#L16 +# Inflation not supported see https://github.com/trezor/trezor-core/issues/202#issuecomment-393342089 op_codes = { 'StellarAccountMergeOp': const(8), 'StellarAllowTrustOp': const(7), @@ -30,23 +34,36 @@ op_wire_types = [ wire_types.StellarSetOptionsOp, ] -ASSET_TYPE_CREDIT_ALPHANUM4 = const(1) -ASSET_TYPE_CREDIT_ALPHANUM12 = const(2) +ASSET_TYPE_NATIVE = const(0) +ASSET_TYPE_ALPHANUM4 = const(1) +ASSET_TYPE_ALPHANUM12 = const(2) # https://www.stellar.org/developers/guides/concepts/accounts.html#balance -# https://github.com/stellar/go/blob/master/amount/main.go +# https://github.com/stellar/go/blob/3d2c1defe73dbfed00146ebe0e8d7e07ce4bb1b6/amount/main.go#L23 AMOUNT_DIVISIBILITY = const(7) # https://github.com/stellar/go/blob/master/network/main.go NETWORK_PASSPHRASE_PUBLIC = 'Public Global Stellar Network ; September 2015' NETWORK_PASSPHRASE_TESTNET = 'Test SDF Network ; September 2015' +# https://www.stellar.org/developers/guides/concepts/accounts.html#flags +FLAG_AUTH_REQUIRED = const(1) +FLAG_AUTH_REVOCABLE = const(2) +FLAG_AUTH_IMMUTABLE = const(4) +FLAGS_MAX_SIZE = 7 + +# https://github.com/stellar/go/blob/e0ffe19f58879d3c31e2976b97a5bf10e13a337b/xdr/Stellar-transaction.x#L275 MEMO_TYPE_NONE = 0 MEMO_TYPE_TEXT = 1 MEMO_TYPE_ID = 2 MEMO_TYPE_HASH = 3 MEMO_TYPE_RETURN = 4 +# https://github.com/stellar/go/blob/3d2c1defe73dbfed00146ebe0e8d7e07ce4bb1b6/xdr/xdr_generated.go#L156 +SIGN_TYPE_ACCOUNT = const(0) +SIGN_TYPE_PRE_AUTH = const(1) +SIGN_TYPE_HASH = const(2) + def get_op_code(msg) -> int: if msg.__qualname__ not in op_codes: diff --git a/src/apps/stellar/layout.py b/src/apps/stellar/layout.py index afa6b39442..44f8c95082 100644 --- a/src/apps/stellar/layout.py +++ b/src/apps/stellar/layout.py @@ -4,13 +4,13 @@ from apps.stellar import helpers from trezor import ui from trezor.messages import ButtonRequestType from trezor.ui.text import Text -from trezor.utils import chunks, format_amount +from trezor import utils async def require_confirm_init(ctx, pubkey: bytes, network_passphrase: str): content = Text('Confirm Stellar', ui.ICON_SEND, ui.NORMAL, 'Initialize singing with', - ui.MONO, *format_address(pubkey), + ui.MONO, *split(format_address(pubkey)), icon_color=ui.GREEN) await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) network = get_network_warning(network_passphrase) @@ -32,8 +32,7 @@ async def require_confirm_memo(ctx, memo_type: int, memo_text: str): elif memo_type == consts.MEMO_TYPE_RETURN: title = 'Memo (RETURN)' else: # MEMO_TYPE_NONE - title = 'No memo set!' - # todo ugly + title = 'No memo set!' # todo format this as ui.NORMAL not MONO memo_text = 'Important: Many exchanges require a memo when depositing' content = Text('Confirm memo', ui.ICON_CONFIRM, ui.BOLD, title, @@ -49,20 +48,40 @@ async def require_confirm_final(ctx, fee: int, num_operations: int): content = Text('Final confirm', ui.ICON_SEND, ui.NORMAL, 'Sign this transaction', ui.NORMAL, 'made up of ' + op_str, - ui.BOLD, 'and pay ' + format_amount(fee, consts.AMOUNT_DIVISIBILITY) + ' XLM', + ui.BOLD, 'and pay ' + format_amount(fee), ui.NORMAL, 'for fee?', icon_color=ui.GREEN) # we use SignTx, not ConfirmOutput, for compatibility with T1 await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) +def format_amount(amount: int, ticker=True) -> str: + t = '' + if ticker: + t = ' XLM' + return utils.format_amount(amount, consts.AMOUNT_DIVISIBILITY) + t + + def format_address(pubkey: bytes) -> str: - address = helpers.address_from_public_key(pubkey) - return split(address) + return helpers.address_from_public_key(pubkey) def split(text): - return chunks(text, 17) + return utils.chunks(text, 17) + + +# todo merge with nem +def trim(payload: str, length: int, dots=True) -> str: + if len(payload) > length: + if dots: + return payload[:length - 2] + '..' + return payload[:length - 2] + return payload + + +# todo merge with nem +def trim_to_rows(payload: str, rows: int=1) -> str: + return trim(payload, rows * 17) def get_network_warning(network_passphrase: str): diff --git a/src/apps/stellar/operations.py b/src/apps/stellar/operations.py deleted file mode 100644 index cc2538b143..0000000000 --- a/src/apps/stellar/operations.py +++ /dev/null @@ -1,192 +0,0 @@ -from trezor.messages.StellarAccountMergeOp import StellarAccountMergeOp -from trezor.messages.StellarAssetType import StellarAssetType -from trezor.messages.StellarAllowTrustOp import StellarAllowTrustOp -from trezor.messages.StellarBumpSequenceOp import StellarBumpSequenceOp -from trezor.messages.StellarChangeTrustOp import StellarChangeTrustOp -from trezor.messages.StellarCreateAccountOp import StellarCreateAccountOp -from trezor.messages.StellarCreatePassiveOfferOp import StellarCreatePassiveOfferOp -from trezor.messages.StellarManageDataOp import StellarManageDataOp -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 * - -# todo layout - - -def serialize_op(w, op): - _serialize_account(w, op.source_account) - write_uint32(w, get_op_code(op)) - if isinstance(op, StellarAccountMergeOp): - serialize_account_merge_op(w, op) - elif isinstance(op, StellarAllowTrustOp): - serialize_allow_trust_op(w, op) - elif isinstance(op, StellarBumpSequenceOp): - serialize_bump_sequence_op(w, op) - elif isinstance(op, StellarChangeTrustOp): - serialize_change_trust_op(w, op) - elif isinstance(op, StellarCreateAccountOp): - serialize_create_account_op(w, op) - elif isinstance(op, StellarCreatePassiveOfferOp): - serialize_create_passive_offer_op(w, op) - elif isinstance(op, StellarManageDataOp): - serialize_manage_data_op(w, op) - elif isinstance(op, StellarManageOfferOp): - serialize_manage_offer_op(w, op) - elif isinstance(op, StellarPathPaymentOp): - serialize_path_payment_op(w, op) - elif isinstance(op, StellarPaymentOp): - serialize_payment_op(w, op) - elif isinstance(op, StellarSetOptionsOp): - serialize_set_options_op(w, op) - else: - raise ValueError('Stellar: unknown operation') - - -def serialize_account_merge_op(w, msg: StellarAccountMergeOp): - 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) - _serialize_asset_code(w, msg.asset_type, msg.asset_code) - - write_bool(w, msg.is_authorized) - - -def serialize_bump_sequence_op(w, msg: StellarBumpSequenceOp): - write_uint64(w, msg.bump_to) - - -def serialize_change_trust_op(w, msg: StellarChangeTrustOp): - _serialize_asset(w, msg.asset) - 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) - - -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) - - -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)) - if msg.value: - write_uint32(w, len(msg.value)) - 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) - - -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) - - _serialize_asset(w, msg.destination_asset) - write_uint64(w, msg.destination_amount) - 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) - _serialize_asset(w, msg.asset) - write_uint64(w, msg.amount) - - -def serialize_set_options_op(w, msg: StellarSetOptionsOp): - # inflation destination - write_bool(w, bool(msg.inflation_destination_account)) - if msg.inflation_destination_account: - write_pubkey(w, msg.inflation_destination_account) - - # clear flags - write_bool(w, bool(msg.clear_flags)) - if msg.clear_flags: - write_uint32(w, msg.clear_flags) - - # set flags - write_bool(w, bool(msg.set_flags)) - if msg.set_flags: - write_uint32(w, msg.set_flags) - - # account thresholds - write_bool(w, bool(msg.master_weight)) - if msg.master_weight: - write_uint32(w, msg.master_weight) - - write_bool(w, bool(msg.low_threshold)) - if msg.low_threshold: - write_uint32(w, msg.low_threshold) - - write_bool(w, bool(msg.medium_threshold)) - if msg.medium_threshold: - write_uint32(w, msg.medium_threshold) - - write_bool(w, bool(msg.high_threshold)) - if msg.high_threshold: - write_uint32(w, msg.high_threshold) - - # home domain - 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) - - # signer - 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) - - -def _serialize_account(w, source_account: bytes): - if source_account is None: - write_bool(w, False) - return - - 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_CREDIT_ALPHANUM4: - # pad with zeros to 4 chars - write_bytes(w, code + bytearray([0] * (4 - len(code)))) - elif asset_type == ASSET_TYPE_CREDIT_ALPHANUM12: - # pad with zeros to 12 chars - write_bytes(w, code + bytearray([0] * (12 - len(code)))) - else: - raise ValueError('Stellar: invalid asset type') - - -def _serialize_asset(w, asset: StellarAssetType): - write_uint32(w, asset.type) - _serialize_asset_code(w, asset.type, asset.code) - write_pubkey(w, asset.issuer) diff --git a/src/apps/stellar/operations/__init__.py b/src/apps/stellar/operations/__init__.py new file mode 100644 index 0000000000..48ef64aecf --- /dev/null +++ b/src/apps/stellar/operations/__init__.py @@ -0,0 +1,44 @@ +from apps.stellar.operations.serialize import * +from apps.stellar.operations.layout import * + + +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) + else: + raise ValueError('Stellar: unknown operation') diff --git a/src/apps/stellar/operations/layout.py b/src/apps/stellar/operations/layout.py new file mode 100644 index 0000000000..7932e1a569 --- /dev/null +++ b/src/apps/stellar/operations/layout.py @@ -0,0 +1,267 @@ +from apps.stellar.layout import * +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 +from trezor.messages.StellarBumpSequenceOp import StellarBumpSequenceOp +from trezor.messages.StellarChangeTrustOp import StellarChangeTrustOp +from trezor.messages.StellarCreateAccountOp import StellarCreateAccountOp +from trezor.messages.StellarCreatePassiveOfferOp import StellarCreatePassiveOfferOp +from trezor.messages.StellarManageDataOp import StellarManageDataOp +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 ubinascii import hexlify + + +async def confirm_source_account(ctx, source_account: bytes): + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, 'Source account:', + ui.MONO, *split(format_address(source_account)), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +async def confirm_allow_trust_op(ctx, op: StellarAllowTrustOp): + if op.is_authorized: + text = 'Allow' + else: + text = 'Revoke' + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, text + ' Trust', + ui.NORMAL, "of '" + op.asset_code + "' by:", + ui.MONO, *split(trim_to_rows(format_address(op.trusted_account), 3)), + icon_color=ui.GREEN) + + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +async def confirm_account_merge_op(ctx, op: StellarAccountMergeOp): + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, 'Account Merge', + ui.NORMAL, 'All XLM will be sent to:', + ui.MONO, *split(trim_to_rows(format_address(op.destination_account), 3)), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +async def confirm_bump_sequence_op(ctx, op: StellarBumpSequenceOp): + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, 'Bump Sequence', + ui.NORMAL, 'Set sequence to', + ui.MONO, str(op.bump_to), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +async def confirm_change_trust_op(ctx, op: StellarChangeTrustOp): + if op.limit == 0: + text = 'Delete' + else: + text = 'Add' + 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), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + await confirm_asset_issuer(ctx, op.asset) + + +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.MONO, *split(trim_to_rows(format_address(op.new_account), 3)), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +async def confirm_create_passive_offer_op(ctx, op: StellarCreatePassiveOfferOp): + if op.amount == 0: + text = 'Delete Passive Offer' + else: + text = 'New Passive Offer' + await _confirm_offer(ctx, text, op) + + +async def confirm_manage_offer_op(ctx, op: StellarManageOfferOp): + if op.offer_id == 0: + text = 'New Offer' + else: + if op.amount == 0: + text = 'Delete' + else: + text = 'Update' + text += ' #' + str(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), + 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' + else: + text = 'Clear' + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, text + ' data value key', + ui.MONO, *split(op.key), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + if op.value: + digest = sha256(op.value).digest() + digest_str = hexlify(digest).decode() + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, ' Value (SHA-256):', + ui.MONO, *split(digest_str), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +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.MONO, *split(trim_to_rows(format_address(op.destination_account), 3)), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + await confirm_asset_issuer(ctx, op.destination_asset) + # confirm what the sender is using to pay + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.NORMAL, 'Pay using', + ui.BOLD, format_amount(op.send_max, ticker=False), + ui.BOLD, format_asset_code(op.send_asset), + ui.NORMAL, 'This amount is debited', + ui.NORMAL, 'from your account.', + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + await confirm_asset_issuer(ctx, op.send_asset) + + +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.MONO, *split(trim_to_rows(format_address(op.destination_account), 3)), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + await confirm_asset_issuer(ctx, op.asset) + + +async def confirm_set_options_op(ctx, op: StellarSetOptionsOp): + if op.inflation_destination_account: + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, 'Set Inflation Destination', + ui.MONO, *split(format_address(op.inflation_destination_account)), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + if op.clear_flags: + text = _format_flags(op.clear_flags) + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, 'Clear Flags', + ui.MONO, *text, + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + if op.set_flags: + text = _format_flags(op.set_flags) + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, 'Set Flags', + ui.MONO, *text, + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + thresholds = _format_thresholds(op) + if thresholds: + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, 'Account Thresholds', + ui.MONO, *thresholds, + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + if op.home_domain: + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, 'Home Domain', + ui.MONO, *split(op.home_domain), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + if op.signer_type is not None: + if op.signer_weight > 0: + text = 'Add Signer' + else: + text = 'Remove Signer' + if op.signer_type == consts.SIGN_TYPE_ACCOUNT: + text += ' (acc)' + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, text, + 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]: + if op.signer_type == consts.SIGN_TYPE_PRE_AUTH: + text += ' (auth)' + else: + text += ' (hash)' + content = Text('Confirm operation', ui.ICON_CONFIRM, + ui.BOLD, text, + ui.MONO, *split(hexlify(op.signer_key).decode()), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + else: + raise ValueError('Stellar: invalid signer type') + + +def _format_thresholds(op: StellarSetOptionsOp) -> (): + text = () + if op.master_weight is not None: + text += ('Master Weight: ' + str(op.master_weight), ) + if op.low_threshold is not None: + text += ('Low: ' + str(op.low_threshold), ) + if op.medium_threshold is not None: + text += ('Medium: ' + str(op.medium_threshold), ) + if op.high_threshold is not None: + text += ('High: ' + str(op.high_threshold), ) + return text + + +def _format_flags(flags: int) -> (): + if flags > consts.FLAGS_MAX_SIZE: + raise ValueError('Stellar: invalid') + text = () + if flags & consts.FLAG_AUTH_REQUIRED: + text += ('AUTH_REQUIRED', ) + if flags & consts.FLAG_AUTH_REVOCABLE: + text += ('AUTH_REVOCABLE', ) + if flags & consts.FLAG_AUTH_IMMUTABLE: + text += ('AUTH_IMMUTABLE', ) + return text + + +def format_asset_code(asset: StellarAssetType) -> str: + if asset is None or asset.type == consts.ASSET_TYPE_NATIVE: + return 'XLM (native)' + return asset.code + + +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.MONO, *split(format_address(asset.issuer)), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)