refactor(core): convert apps.cardano to layouts

pull/1679/head
Martin Milata 3 years ago committed by matejcik
parent ffb400180d
commit ddac852acf

@ -1,8 +1,9 @@
from trezor import log, wire
from trezor.messages import CardanoAddress
from trezor.ui.layouts import show_address
from apps.common import paths
from apps.common.layout import address_n_to_str, show_qr
from apps.common.layout import address_n_to_str
from . import seed
from .address import derive_human_readable_address, validate_address_parameters
@ -10,7 +11,7 @@ from .helpers import protocol_magics, staking_use_cases
from .helpers.paths import SCHEMA_ADDRESS
from .helpers.utils import to_account_path
from .layout import (
show_address,
ADDRESS_TYPE_NAMES,
show_warning_address_foreign_staking_key,
show_warning_address_pointer,
)
@ -70,19 +71,15 @@ async def _display_address(
if not protocol_magics.is_mainnet(protocol_magic):
network_name = protocol_magics.to_ui_string(protocol_magic)
while True:
if await show_address(
ctx,
address,
address_parameters.address_type,
address_parameters.address_n,
network=network_name,
):
break
if await show_qr(
ctx, address, desc=address_n_to_str(address_parameters.address_n)
):
break
address_n = address_n_to_str(address_parameters.address_n)
await show_address(
ctx,
address=address,
title="%s address" % ADDRESS_TYPE_NAMES[address_parameters.address_type],
network=network_name,
address_extra=address_n,
title_qr=address_n,
)
async def _show_staking_warnings(

@ -1,15 +1,15 @@
import math
from ubinascii import hexlify
from trezor import ui
from trezor.enums import ButtonRequestType, CardanoAddressType, CardanoCertificateType
from trezor.strings import format_amount
from trezor.ui.components.tt.button import ButtonDefault
from trezor.ui.components.tt.scroll import Paginated
from trezor.ui.components.tt.text import Text
from trezor.utils import chunks
from trezor.ui.layouts import (
confirm_metadata,
confirm_output,
confirm_path_warning,
confirm_properties,
)
from apps.common.confirm import confirm, require_confirm, require_hold_to_confirm
from apps.common.layout import address_n_to_str
from . import seed
@ -55,9 +55,6 @@ CERTIFICATE_TYPE_NAMES = {
CardanoCertificateType.STAKE_POOL_REGISTRATION: "Stakepool registration",
}
# Maximum number of characters per line in monospace font.
_MAX_MONO_LINE = 18
def format_coin_amount(amount: int) -> str:
return "%s %s" % (format_amount(amount, 6), "ADA")
@ -75,73 +72,72 @@ async def confirm_sending(
) -> None:
await confirm_sending_token_bundle(ctx, token_bundle)
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Confirm sending:")
page1.bold(format_coin_amount(ada_amount))
page1.normal("to")
pages = _paginate_text(
page1,
"Confirm transaction",
ui.ICON_SEND,
await confirm_output(
ctx,
to,
lines_per_page=4,
lines_used_on_first_page=3,
format_coin_amount(ada_amount),
title="Confirm transaction",
subtitle="Confirm sending:",
font_amount=ui.BOLD,
width_paginated=17,
to_str="\nto\n",
to_paginated=True,
br_code=ButtonRequestType.Other,
)
await require_confirm(ctx, Paginated(pages))
async def confirm_sending_token_bundle(
ctx: wire.Context, token_bundle: list[CardanoAssetGroupType]
) -> None:
for token_group in token_bundle:
for token in token_group.tokens:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Asset fingerprint:")
page1.bold(
format_asset_fingerprint(
policy_id=token_group.policy_id,
asset_name_bytes=token.asset_name_bytes,
)
await confirm_properties(
ctx,
"confirm_token",
title="Confirm transaction",
props=[
(
"Asset fingerprint:",
format_asset_fingerprint(
policy_id=token_group.policy_id,
asset_name_bytes=token.asset_name_bytes,
),
),
("Amount sent:", format_amount(token.amount, 0)),
],
br_code=ButtonRequestType.Other,
)
page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page2.normal("Amount sent:")
page2.bold(format_amount(token.amount, 0))
await require_confirm(ctx, Paginated([page1, page2]))
async def show_warning_tx_output_contains_tokens(ctx: wire.Context) -> None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("The following")
page1.normal("transaction output")
page1.normal("contains tokens.")
page1.br_half()
page1.normal("Continue?")
await require_confirm(ctx, page1)
await confirm_metadata(
ctx,
"confirm_tokens",
title="Confirm transaction",
content="The following\ntransaction output\ncontains tokens.",
larger_vspace=True,
br_code=ButtonRequestType.Other,
)
async def show_warning_path(ctx: wire.Context, path: list[int], title: str) -> None:
page1 = Text("Confirm path", ui.ICON_WRONG, ui.RED)
page1.normal(title)
page1.bold(address_n_to_str(path))
page1.normal("is unknown.")
page1.normal("Are you sure?")
await require_confirm(ctx, page1)
await confirm_path_warning(ctx, address_n_to_str(path), path_type=title)
async def show_warning_tx_no_staking_info(
ctx: wire.Context, address_type: CardanoAddressType, amount: int
) -> None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Change " + ADDRESS_TYPE_NAMES[address_type].lower())
page1.normal("address has no stake")
page1.normal("rights.")
page1.normal("Change amount:")
page1.bold(format_coin_amount(amount))
await require_confirm(ctx, page1)
atype = ADDRESS_TYPE_NAMES[address_type].lower()
content = "Change %s address has no stake rights.\nChange amount:\n{}" % atype
await confirm_metadata(
ctx,
"warning_staking",
title="Confirm transaction",
content=content,
param=format_coin_amount(amount),
hide_continue=True,
br_code=ButtonRequestType.Other,
)
async def show_warning_tx_pointer_address(
@ -149,21 +145,25 @@ async def show_warning_tx_pointer_address(
pointer: CardanoBlockchainPointerType,
amount: int,
) -> None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Change address has a")
page1.normal("pointer with staking")
page1.normal("rights.")
page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page2.normal("Pointer:")
page2.bold(
"%s, %s, %s"
% (pointer.block_index, pointer.tx_index, pointer.certificate_index)
await confirm_properties(
ctx,
"warning_pointer",
title="Confirm transaction",
props=[
("Change address has a\npointer with staking\nrights.\n\n\n", None),
(
"Pointer:",
"%s, %s, %s"
% (
pointer.block_index,
pointer.tx_index,
pointer.certificate_index,
),
),
("Change amount:", format_coin_amount(amount)),
],
br_code=ButtonRequestType.Other,
)
page2.normal("Change amount:")
page2.bold(format_coin_amount(amount))
await require_confirm(ctx, Paginated([page1, page2]))
async def show_warning_tx_different_staking_account(
@ -171,18 +171,23 @@ async def show_warning_tx_different_staking_account(
staking_account_path: list[int],
amount: int,
) -> None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Change address staking")
page1.normal("rights do not match")
page1.normal("the current account.")
page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page2.normal("Staking account %s:" % format_account_number(staking_account_path))
page2.bold(address_n_to_str(staking_account_path))
page2.normal("Change amount:")
page2.bold(format_coin_amount(amount))
await require_confirm(ctx, Paginated([page1, page2]))
await confirm_properties(
ctx,
"warning_differentstaking",
title="Confirm transaction",
props=[
(
"Change address staking rights do not match the current account.\n\n",
None,
),
(
"Staking account %s:" % format_account_number(staking_account_path),
address_n_to_str(staking_account_path),
),
("Change amount:", format_coin_amount(amount)),
],
br_code=ButtonRequestType.Other,
)
async def show_warning_tx_staking_key_hash(
@ -190,20 +195,19 @@ async def show_warning_tx_staking_key_hash(
staking_key_hash: bytes,
amount: int,
) -> None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Change address staking")
page1.normal("rights do not match")
page1.normal("the current account.")
page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page2.normal("Staking key hash:")
page2.mono(*chunks(hexlify(staking_key_hash).decode(), 17))
page3 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page3.normal("Change amount:")
page3.bold(format_coin_amount(amount))
props = [
("Change address staking rights do not match the current account.\n\n", None),
("Staking key hash:", hexlify(staking_key_hash).decode()),
("Change amount:", format_coin_amount(amount)),
]
await require_confirm(ctx, Paginated([page1, page2, page3]))
await confirm_properties(
ctx,
"confirm_different_stakingrights",
title="Confirm transaction",
props=props,
br_code=ButtonRequestType.Other,
)
async def confirm_transaction(
@ -215,24 +219,27 @@ async def confirm_transaction(
validity_interval_start: int | None,
is_network_id_verifiable: bool,
) -> None:
pages: list[ui.Component] = []
props = [
("Transaction amount:", format_coin_amount(amount)),
("Transaction fee:", format_coin_amount(fee)),
]
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Transaction amount:")
page1.bold(format_coin_amount(amount))
page1.normal("Transaction fee:")
page1.bold(format_coin_amount(fee))
pages.append(page1)
page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
if is_network_id_verifiable:
page2.normal("Network:")
page2.bold(protocol_magics.to_ui_string(protocol_magic))
page2.normal("Valid since: %s" % format_optional_int(validity_interval_start))
page2.normal("TTL: %s" % format_optional_int(ttl))
pages.append(page2)
props.append(("Network:", protocol_magics.to_ui_string(protocol_magic)))
props.append(
("Valid since: %s" % format_optional_int(validity_interval_start), None)
)
props.append(("TTL: %s" % format_optional_int(ttl), None))
await require_hold_to_confirm(ctx, Paginated(pages))
await confirm_properties(
ctx,
"confirm_total",
title="Confirm transaction",
props=props,
hold=True,
br_code=ButtonRequestType.Other,
)
async def confirm_certificate(
@ -242,23 +249,24 @@ async def confirm_certificate(
# in this call
assert certificate.type != CardanoCertificateType.STAKE_POOL_REGISTRATION
pages: list[ui.Component] = []
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Confirm:")
page1.bold(CERTIFICATE_TYPE_NAMES[certificate.type])
page1.normal("for account %s:" % format_account_number(certificate.path))
page1.bold(address_n_to_str(to_account_path(certificate.path)))
pages.append(page1)
props = [
("Confirm:", CERTIFICATE_TYPE_NAMES[certificate.type]),
(
"for account %s:" % format_account_number(certificate.path),
address_n_to_str(to_account_path(certificate.path)),
),
]
if certificate.type == CardanoCertificateType.STAKE_DELEGATION:
assert certificate.pool is not None # validate_certificate
page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page2.normal("to pool:")
page2.bold(format_stake_pool_id(certificate.pool))
pages.append(page2)
props.append(("to pool:", format_stake_pool_id(certificate.pool)))
await require_confirm(ctx, Paginated(pages))
await confirm_properties(
ctx,
"confirm_certificate",
title="Confirm transaction",
props=props,
br_code=ButtonRequestType.Other,
)
async def confirm_stake_pool_parameters(
@ -267,25 +275,31 @@ async def confirm_stake_pool_parameters(
network_id: int,
protocol_magic: int,
) -> None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.bold("Stake pool registration")
page1.normal("Pool ID:")
page1.bold(format_stake_pool_id(pool_parameters.pool_id))
page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page2.normal("Pool reward account:")
page2.bold(pool_parameters.reward_account)
page3 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page3.normal("Pledge: " + format_coin_amount(pool_parameters.pledge))
page3.normal("Cost: " + format_coin_amount(pool_parameters.cost))
margin_percentage = (
100.0 * pool_parameters.margin_numerator / pool_parameters.margin_denominator
)
percentage_formatted = ("%f" % margin_percentage).rstrip("0").rstrip(".")
page3.normal("Margin: %s%%" % percentage_formatted)
await require_confirm(ctx, Paginated([page1, page2, page3]))
await confirm_properties(
ctx,
"confirm_pool_registration",
title="Confirm transaction",
props=[
(
"Stake pool registration\nPool ID:",
format_stake_pool_id(pool_parameters.pool_id),
),
("Pool reward account:", pool_parameters.reward_account),
(
"Pledge: {}\nCost: {}\nMargin: {}%".format(
format_coin_amount(pool_parameters.pledge),
format_coin_amount(pool_parameters.cost),
percentage_formatted,
),
None,
),
],
br_code=ButtonRequestType.Other,
)
async def confirm_stake_pool_owners(
@ -294,56 +308,67 @@ async def confirm_stake_pool_owners(
owners: list[CardanoPoolOwnerType],
network_id: int,
) -> None:
pages: list[ui.Component] = []
props: list[tuple[str, str | None]] = []
for index, owner in enumerate(owners, 1):
page = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page.normal("Pool owner #%d:" % (index))
if owner.staking_key_path:
page.bold(address_n_to_str(owner.staking_key_path))
page.normal(
encode_human_readable_address(
pack_reward_address_bytes(
get_public_key_hash(keychain, owner.staking_key_path),
network_id,
)
props.append(
("Pool owner #%d:" % index, address_n_to_str(owner.staking_key_path))
)
props.append(
(
encode_human_readable_address(
pack_reward_address_bytes(
get_public_key_hash(keychain, owner.staking_key_path),
network_id,
)
),
None,
)
)
else:
assert owner.staking_key_hash is not None # validate_pool_owners
page.bold(
encode_human_readable_address(
pack_reward_address_bytes(owner.staking_key_hash, network_id)
props.append(
(
"Pool owner #%d:" % index,
encode_human_readable_address(
pack_reward_address_bytes(owner.staking_key_hash, network_id)
),
)
)
pages.append(page)
await require_confirm(ctx, Paginated(pages))
await confirm_properties(
ctx,
"confirm_pool_owners",
title="Confirm transaction",
props=props,
br_code=ButtonRequestType.Other,
)
async def confirm_stake_pool_metadata(
ctx: wire.Context,
metadata: CardanoPoolMetadataType | None,
) -> None:
if metadata is None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Pool has no metadata")
page1.normal("(anonymous pool)")
await require_confirm(ctx, page1)
await confirm_properties(
ctx,
"confirm_pool_metadata",
title="Confirm transaction",
props=[("Pool has no metadata (anonymous pool)", None)],
br_code=ButtonRequestType.Other,
)
return
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Pool metadata url:")
page1.bold(metadata.url)
page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page2.normal("Pool metadata hash:")
page2.bold(hexlify(metadata.hash).decode())
await require_confirm(ctx, Paginated([page1, page2]))
await confirm_properties(
ctx,
"confirm_pool_metadata",
title="Confirm transaction",
props=[
("Pool metadata url:", metadata.url),
("Pool metadata hash:", hexlify(metadata.hash).decode()),
],
br_code=ButtonRequestType.Other,
)
async def confirm_transaction_network_ttl(
@ -352,36 +377,53 @@ async def confirm_transaction_network_ttl(
ttl: int | None,
validity_interval_start: int | None,
) -> None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Network:")
page1.bold(protocol_magics.to_ui_string(protocol_magic))
page1.normal("Valid since: %s" % format_optional_int(validity_interval_start))
page1.normal("TTL: %s" % format_optional_int(ttl))
await require_confirm(ctx, page1)
await confirm_properties(
ctx,
"confirm_pool_network",
title="Confirm transaction",
props=[
("Network:", protocol_magics.to_ui_string(protocol_magic)),
(
"Valid since: %s" % format_optional_int(validity_interval_start),
None,
),
("TTL: %s" % format_optional_int(ttl), None),
],
br_code=ButtonRequestType.Other,
)
async def confirm_stake_pool_registration_final(
ctx: wire.Context,
) -> None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Confirm signing the stake pool registration as an owner")
await require_hold_to_confirm(ctx, page1)
await confirm_metadata(
ctx,
"confirm_pool_final",
title="Confirm transaction",
content="Confirm signing the stake pool registration as an owner",
hide_continue=True,
hold=True,
br_code=ButtonRequestType.Other,
)
async def confirm_withdrawal(
ctx: wire.Context, withdrawal: CardanoTxWithdrawalType
) -> None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Confirm withdrawal")
page1.normal("for account %s:" % format_account_number(withdrawal.path))
page1.bold(address_n_to_str(to_account_path(withdrawal.path)))
page1.normal("Amount:")
page1.bold(format_coin_amount(withdrawal.amount))
await require_confirm(ctx, page1)
await confirm_properties(
ctx,
"confirm_withdrawal",
title="Confirm transaction",
props=[
(
"Confirm withdrawal\nfor account %s:"
% format_account_number(withdrawal.path),
address_n_to_str(to_account_path(withdrawal.path)),
),
("Amount:", format_coin_amount(withdrawal.amount)),
],
br_code=ButtonRequestType.Other,
)
async def confirm_catalyst_registration(
@ -391,166 +433,98 @@ async def confirm_catalyst_registration(
reward_address: str,
nonce: int,
) -> None:
pages: list[ui.Component] = []
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.bold("Catalyst voting key")
page1.bold("registration")
pages.append(page1)
page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page2.normal("Voting public key:")
page2.bold(*chunks(public_key, 17))
pages.append(page2)
page3 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page3.normal("Staking key for")
page3.normal("account %s:" % format_account_number(staking_path))
page3.bold(address_n_to_str(staking_path))
pages.append(page3)
page4 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page4.normal("Rewards go to:")
pages.extend(
_paginate_text(page4, "Confirm transaction", ui.ICON_SEND, reward_address)
await confirm_properties(
ctx,
"confirm_catalyst_registration",
title="Confirm transaction",
props=[
("Catalyst voting key registration", None),
("Voting public key:", public_key),
(
"Staking key for account %s:" % format_account_number(staking_path),
address_n_to_str(staking_path),
),
("Rewards go to:", reward_address),
("Nonce:", str(nonce)),
],
br_code=ButtonRequestType.Other,
)
last_page = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
last_page.normal("Nonce: %s" % nonce)
pages.append(last_page)
await require_confirm(ctx, Paginated(pages))
async def show_auxiliary_data_hash(
ctx: wire.Context, auxiliary_data_hash: bytes
) -> None:
page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
page1.normal("Auxiliary data hash:")
page1.bold(hexlify(auxiliary_data_hash).decode())
await require_confirm(ctx, page1)
async def show_address(
ctx: wire.Context,
address: str,
address_type: CardanoAddressType,
path: list[int],
network: str | None = None,
) -> bool:
"""
Custom show_address function is needed because cardano addresses don't
fit on a single screen.
"""
address_type_label = "%s address" % ADDRESS_TYPE_NAMES[address_type]
page1 = Text(address_type_label, ui.ICON_RECEIVE, ui.GREEN)
lines_per_page = 5
lines_used_on_first_page = 0
# assemble first page to be displayed (path + network + whatever part of the address fits)
if network is not None:
page1.normal("%s network" % network)
lines_used_on_first_page += 1
path_str = address_n_to_str(path)
page1.mono(path_str)
lines_used_on_first_page = min(
lines_used_on_first_page + math.ceil(len(path_str) / _MAX_MONO_LINE),
lines_per_page,
)
pages = _paginate_text(
page1,
address_type_label,
ui.ICON_RECEIVE,
address,
lines_per_page=lines_per_page,
lines_used_on_first_page=lines_used_on_first_page,
)
return await confirm(
await confirm_properties(
ctx,
Paginated(pages),
code=ButtonRequestType.Address,
cancel="QR",
cancel_style=ButtonDefault,
"confirm_auxiliary_data",
title="Confirm transaction",
props=[("Auxiliary data hash:", hexlify(auxiliary_data_hash).decode())],
br_code=ButtonRequestType.Other,
)
def _paginate_text(
first_page: Text,
page_desc: str,
page_icon: str,
text: str,
lines_per_page: int = 5,
lines_used_on_first_page: int = 1,
) -> list[ui.Component]:
lines = list(chunks(text, 17))
offset = lines_per_page - lines_used_on_first_page
for text_line in lines[:offset]:
first_page.bold(text_line)
pages: list[ui.Component] = [first_page]
if len(lines) > offset:
to_pages = list(chunks(lines[offset:], lines_per_page))
for page in to_pages:
t = Text(page_desc, page_icon, ui.GREEN)
for line in page:
t.bold(line)
pages.append(t)
return pages
async def show_warning_address_foreign_staking_key(
ctx: wire.Context,
account_path: list[int],
staking_account_path: list[int],
staking_key_hash: bytes | None,
) -> None:
page1 = Text("Warning", ui.ICON_WRONG, ui.RED)
page1.normal("Stake rights associated")
page1.normal("with this address do")
page1.normal("not match your")
page1.normal("account %s:" % format_account_number(account_path))
page1.bold(address_n_to_str(account_path))
page2 = Text("Warning", ui.ICON_WRONG, ui.RED)
props: list[tuple[str, str | None]] = [
(
"Stake rights associated with this address do not match your account %s:"
% format_account_number(account_path),
address_n_to_str(account_path),
)
]
if staking_account_path:
page2.normal("Stake account %s:" % format_account_number(staking_account_path))
page2.bold(address_n_to_str(staking_account_path))
page2.br_half()
props.append(
(
"Stake account %s:" % format_account_number(staking_account_path),
address_n_to_str(staking_account_path),
)
)
else:
assert staking_key_hash is not None # _validate_base_address_staking_info
page2.normal("Staking key:")
page2.bold(hexlify(staking_key_hash).decode())
page2.normal("Continue?")
props.append(("Staking key:", hexlify(staking_key_hash).decode()))
props.append(("Continue?", None))
await require_confirm(ctx, Paginated([page1, page2]))
await confirm_properties(
ctx,
"warning_foreign_stakingkey",
title="Warning",
props=props,
icon=ui.ICON_WRONG,
icon_color=ui.RED,
br_code=ButtonRequestType.Other,
)
async def show_warning_tx_network_unverifiable(ctx: wire.Context) -> None:
page1 = Text("Warning", ui.ICON_SEND, ui.GREEN)
page1.normal("Transaction has no outputs, network cannot be verified.")
page1.br_half()
page1.normal("Continue?")
await require_confirm(ctx, page1)
await confirm_metadata(
ctx,
"warning_no_outputs",
title="Warning",
content="Transaction has no outputs, network cannot be verified.",
larger_vspace=True,
br_code=ButtonRequestType.Other,
)
async def show_warning_address_pointer(
ctx: wire.Context, pointer: CardanoBlockchainPointerType
) -> None:
text = Text("Warning", ui.ICON_WRONG, ui.RED)
text.normal("Pointer address:")
text.normal("Block: %s" % pointer.block_index)
text.normal("Transaction: %s" % pointer.tx_index)
text.normal("Certificate: %s" % pointer.certificate_index)
text.normal("Continue?")
await require_confirm(ctx, text)
content = "Pointer address:\nBlock: %s\nTransaction: %s\nCertificate: %s" % (
pointer.block_index,
pointer.tx_index,
pointer.certificate_index,
)
await confirm_metadata(
ctx,
"warning_pointer",
title="Warning",
icon=ui.ICON_WRONG,
icon_color=ui.RED,
content=content,
br_code=ButtonRequestType.Other,
)

@ -1,42 +1,9 @@
from micropython import const
from trezor import ui
from trezor.enums import ButtonRequestType
from trezor.ui.components.tt.button import ButtonDefault
from trezor.ui.components.tt.text import Text
from trezor.ui.container import Container
from trezor.ui.qr import Qr
from trezor.utils import chunks
from apps.common import HARDENED
from apps.common.confirm import confirm
if False:
from typing import Iterable, Iterator
from trezor import wire
async def show_qr(
ctx: wire.Context,
address: str,
desc: str = "Confirm address",
cancel: str = "Address",
) -> bool:
QR_X = const(120)
QR_Y = const(115)
QR_SIZE_THRESHOLD = const(63)
QR_COEF = const(4) if len(address) < QR_SIZE_THRESHOLD else const(3)
qr = Qr(address, QR_X, QR_Y, QR_COEF)
text = Text(desc, ui.ICON_RECEIVE, ui.GREEN)
content = Container(qr, text)
return await confirm(
ctx,
content,
code=ButtonRequestType.Address,
cancel=cancel,
cancel_style=ButtonDefault,
)
def split_address(address: str) -> Iterator[str]:

@ -10,7 +10,7 @@ from .swipe import SWIPE_DOWN, SWIPE_UP, SWIPE_VERTICAL, Swipe
from .text import TEXT_MAX_LINES, Span, Text
if False:
from typing import Iterable, Any
from typing import Callable, Iterable
from ..common.text import TextContent
@ -266,7 +266,8 @@ def paginate_text(
header_icon: str = ui.ICON_DEFAULT,
icon_color: int = ui.ORANGE_ICON,
break_words: bool = False,
) -> Confirm | Paginated:
confirm: Callable[[ui.Component], ui.Layout] = Confirm,
) -> ui.Layout:
span = Span(text, 0, font, break_words=break_words)
if span.count_lines() <= TEXT_MAX_LINES:
result = Text(
@ -277,7 +278,7 @@ def paginate_text(
break_words=break_words,
)
result.content = [font, text]
return Confirm(result)
return confirm(result)
else:
pages: list[ui.Component] = []
@ -305,7 +306,7 @@ def paginate_text(
for _ in range(TEXT_MAX_LINES - 1):
span.next_line()
pages[-1] = Confirm(pages[-1])
pages[-1] = confirm(pages[-1])
return Paginated(pages)
@ -315,8 +316,8 @@ def paginate_paragraphs(
header_icon: str = ui.ICON_DEFAULT,
icon_color: int = ui.ORANGE_ICON,
break_words: bool = False,
confirm_kwargs: Dict[str, Any] = {},
) -> Union[Confirm, Paginated]:
confirm: Callable[[ui.Component], ui.Layout] = Confirm,
) -> ui.Layout:
span = Span("", 0, ui.NORMAL, break_words=break_words)
lines = 0
content: list[TextContent] = []
@ -343,7 +344,7 @@ def paginate_paragraphs(
result.content.append("\n")
result.content.append(font)
result.content.append(text)
return Confirm(result, **confirm_kwargs)
return confirm(result)
else:
pages: list[ui.Component] = []
@ -373,5 +374,5 @@ def paginate_paragraphs(
else:
lines_left -= 1
pages[-1] = Confirm(pages[-1], **confirm_kwargs)
pages[-1] = confirm(pages[-1])
return Paginated(pages)

@ -25,15 +25,18 @@ from .common import interact
if False:
from typing import (
Awaitable,
Iterator,
NoReturn,
Optional,
Sequence,
Tuple,
Type,
Union,
Awaitable,
NoReturn,
)
ExceptionType = Union[BaseException, Type[BaseException]]
PropertyType = Tuple[str, Optional[str]]
__all__ = (
@ -52,6 +55,7 @@ __all__ = (
"confirm_output",
"confirm_decred_sstx_submission",
"confirm_hex",
"confirm_properties",
"confirm_total",
"confirm_total_ethereum",
"confirm_total_ripple",
@ -182,9 +186,11 @@ async def confirm_backup(ctx: wire.GenericContext) -> bool:
return confirmed
async def confirm_path_warning(ctx: wire.GenericContext, path: str) -> None:
async def confirm_path_warning(
ctx: wire.GenericContext, path: str, path_type: str = "Path"
) -> None:
text = Text("Confirm path", ui.ICON_WRONG, ui.RED)
text.normal("Path")
text.normal(path_type)
text.mono(*break_path_to_lines(path, MONO_ADDR_PER_LINE))
text.normal("is unknown.", "Are you sure?")
await raise_if_cancelled(
@ -237,8 +243,11 @@ def _show_address(
address: str,
title: str,
network: str | None = None,
) -> Confirm | Paginated:
extra: str | None = None,
) -> ui.Layout:
para = [(ui.NORMAL, "%s network" % network)] if network is not None else []
if extra is not None:
para.append((ui.BOLD, extra))
para.extend(
(ui.MONO, address_line) for address_line in chunks(address, MONO_ADDR_PER_LINE)
)
@ -247,7 +256,9 @@ def _show_address(
header=title,
header_icon=ui.ICON_RECEIVE,
icon_color=ui.GREEN,
confirm_kwargs={"cancel": "QR", "cancel_style": ButtonDefault},
confirm=lambda content: Confirm(
content, cancel="QR", cancel_style=ButtonDefault
),
)
@ -290,6 +301,8 @@ async def show_address(
network: str | None = None,
multisig_index: int | None = None,
xpubs: Sequence[str] = [],
address_extra: str | None = None,
title_qr: str | None = None,
) -> None:
is_multisig = len(xpubs) > 0
while True:
@ -300,6 +313,7 @@ async def show_address(
address,
title,
network,
extra=address_extra,
),
"show_address",
ButtonRequestType.Address,
@ -311,7 +325,7 @@ async def show_address(
ctx,
_show_qr(
address if address_qr is None else address_qr,
title,
title if title_qr is None else title_qr,
cancel="XPUBs" if is_multisig else "Address",
),
"show_qr",
@ -455,19 +469,29 @@ async def confirm_output(
address: str,
amount: str,
font_amount: int = ui.NORMAL, # TODO cleanup @ redesign
title: str = "Confirm sending",
subtitle: str | None = None, # TODO cleanup @ redesign
color_to: int = ui.FG, # TODO cleanup @ redesign
to_str: str = " to\n", # TODO cleanup @ redesign
to_paginated: bool = False, # TODO cleanup @ redesign
width: int = MONO_ADDR_PER_LINE,
width_paginated: int = MONO_ADDR_PER_LINE - 1,
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
) -> None:
title = "Confirm sending"
if len(address) > (TEXT_MAX_LINES - 1) * width:
para = [(font_amount, amount)]
header_lines = to_str.count("\n") + int(subtitle is not None)
if len(address) > (TEXT_MAX_LINES - header_lines) * width:
para = []
if subtitle is not None:
para.append((ui.NORMAL, subtitle))
para.append((font_amount, amount))
if to_paginated:
para.append((ui.NORMAL, "to"))
para.extend((ui.MONO, line) for line in chunks(address, width_paginated))
content: ui.Layout = paginate_paragraphs(para, title, ui.ICON_SEND, ui.GREEN)
else:
text = Text(title, ui.ICON_SEND, ui.GREEN, new_lines=False)
if subtitle is not None:
text.normal(subtitle, "\n")
text.content = [font_amount, amount, ui.NORMAL, color_to, to_str, ui.FG]
text.mono(*chunks_intersperse(address, width))
content = Confirm(text)
@ -552,6 +576,28 @@ async def confirm_hex(
await raise_if_cancelled(interact(ctx, content, br_type, br_code))
# TODO keep name and value on the same page if possible
async def confirm_properties(
ctx: wire.GenericContext,
br_type: str,
title: str,
props: Sequence[PropertyType],
icon: str = ui.ICON_SEND, # TODO cleanup @ redesign
icon_color: int = ui.GREEN, # TODO cleanup @ redesign
hold: bool = False,
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
) -> None:
para = []
for p in props:
para.append((ui.NORMAL, p[0]))
if p[1] is not None:
para.append((ui.BOLD, p[1]))
content = paginate_paragraphs(
para, title, icon, icon_color, confirm=HoldToConfirm if hold else Confirm
)
await raise_if_cancelled(interact(ctx, content, br_type, br_code))
async def confirm_total(
ctx: wire.GenericContext,
total_amount: str,
@ -630,6 +676,7 @@ async def confirm_metadata(
param_font: int = ui.BOLD,
icon: str = ui.ICON_SEND, # TODO cleanup @ redesign
icon_color: int = ui.GREEN, # TODO cleanup @ redesign
larger_vspace: bool = False, # TODO cleanup @ redesign
) -> None:
text = Text(title, icon, icon_color, new_lines=False)
text.format_parametrized(
@ -638,6 +685,8 @@ async def confirm_metadata(
if not hide_continue:
text.br()
if larger_vspace:
text.br_half()
text.normal("Continue?")
cls = HoldToConfirm if hold else Confirm

@ -107,7 +107,6 @@ def test_cardano_sign_tx_with_multiple_chunks(client, parameters, result):
# If that changes, we'll need to figure out something else.
messages.ButtonRequest(page_number=1),
messages.ButtonRequest(page_number=2),
messages.ButtonRequest(page_number=3),
messages.ButtonRequest(page_number=1),
messages.ButtonRequest(page_number=2),
]

Loading…
Cancel
Save