1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-18 05:28:40 +00:00

refactor(core): convert most of apps.monero to layouts

Progress popups are not ported yet as they're unlike anything else.

Introduces paginate_paragraphs.
This commit is contained in:
Martin Milata 2021-03-01 13:48:59 +01:00 committed by matejcik
parent 6c926ad82e
commit 16094df0c5
25 changed files with 221 additions and 231 deletions

View File

@ -514,10 +514,8 @@ if utils.BITCOIN_ONLY:
import apps.monero.get_watch_only
apps.monero.key_image_sync
import apps.monero.key_image_sync
apps.monero.layout.common
import apps.monero.layout.common
apps.monero.layout.confirms
import apps.monero.layout.confirms
apps.monero.layout
import apps.monero.layout
apps.monero.live_refresh
import apps.monero.live_refresh
apps.monero.misc

View File

@ -78,6 +78,7 @@ async def get_ownership_proof(
data=hexlify(msg.commitment_data).decode(),
icon=ui.ICON_CONFIG,
icon_color=ui.ORANGE_ICON,
truncate=True, # commitment data, probably should show all
truncate_middle=True,
)

View File

@ -56,6 +56,7 @@ async def confirm_output(
title="OP_RETURN",
data=hexlify(data).decode(),
br_code=ButtonRequestType.ConfirmOutput,
truncate=True, # 80 bytes - not truncated 2 screens max
)
else:
assert output.address is not None

View File

@ -336,20 +336,3 @@ def is_hardened(i: int) -> bool:
def path_is_hardened(address_n: Bip32Path) -> bool:
return all(is_hardened(n) for n in address_n)
def break_address_n_to_lines(address_n: Bip32Path) -> list[str]:
from trezor.ui.constants import MONO_CHARS_PER_LINE
from .layout import address_n_to_str
lines = []
path_str = address_n_to_str(address_n)
per_line = MONO_CHARS_PER_LINE
while len(path_str) > per_line:
i = path_str[:per_line].rfind("/")
lines.append(path_str[:i])
path_str = path_str[i:]
lines.append(path_str)
return lines

View File

@ -30,6 +30,7 @@ async def show_internal_entropy(ctx, entropy: bytes):
icon_color=ui.ORANGE_ICON,
width=16,
br_code=ButtonRequestType.ResetDevice,
truncate=True, # 32 bytes always fits
)

View File

@ -53,6 +53,7 @@ async def require_confirm_ecdh_session_key(
serialize_identity_without_proto(identity),
icon=ui.ICON_DEFAULT,
icon_color=ui.ORANGE_ICON,
truncate=True, # uri without protocol, probably should show entire
)

View File

@ -1,10 +1,10 @@
from trezor.messages import MoneroAddress
from trezor.ui.layouts import show_address
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.common.layout import address_n_to_str, show_qr
from apps.common.layout import address_n_to_str
from apps.monero import misc
from apps.monero.layout import confirms
from apps.monero.xmr import addresses, crypto, monero
from apps.monero.xmr.networks import net_version
@ -42,10 +42,11 @@ async def get_address(ctx, msg, keychain):
if msg.show_display:
desc = address_n_to_str(msg.address_n)
while True:
if await confirms.show_address(ctx, addr.decode(), desc=desc):
break
if await show_qr(ctx, "monero:" + addr.decode(), desc=desc):
break
await show_address(
ctx,
address=addr.decode(),
address_qr="monero:" + addr.decode(),
desc=desc,
)
return MoneroAddress(address=addr)

View File

@ -20,8 +20,7 @@ from trezor.messages import MoneroGetTxKeyAck, MoneroGetTxKeyRequest
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.monero import misc
from apps.monero.layout import confirms
from apps.monero import layout, misc
from apps.monero.xmr import crypto
from apps.monero.xmr.crypto import chacha_poly
@ -34,7 +33,7 @@ async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain):
await paths.validate_path(ctx, keychain, msg.address_n)
do_deriv = msg.reason == _GET_TX_KEY_REASON_TX_DERIVATION
await confirms.require_confirm_tx_key(ctx, export_key=not do_deriv)
await layout.require_confirm_tx_key(ctx, export_key=not do_deriv)
creds = misc.get_creds(keychain, msg.address_n, msg.network_type)

View File

@ -2,8 +2,7 @@ from trezor.messages import MoneroGetWatchKey, MoneroWatchKey
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.monero import misc
from apps.monero.layout import confirms
from apps.monero import layout, misc
from apps.monero.xmr import crypto
@ -11,7 +10,7 @@ from apps.monero.xmr import crypto
async def get_watch_only(ctx, msg: MoneroGetWatchKey, keychain):
await paths.validate_path(ctx, keychain, msg.address_n)
await confirms.require_confirm_watchkey(ctx)
await layout.require_confirm_watchkey(ctx)
creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
address = creds.address

View File

@ -12,8 +12,7 @@ from trezor.messages import (
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.monero import misc
from apps.monero.layout import confirms
from apps.monero import layout, misc
from apps.monero.xmr import crypto, key_image, monero
from apps.monero.xmr.crypto import chacha_poly
@ -49,7 +48,7 @@ async def _init_step(s, ctx, msg, keychain):
s.creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
await confirms.require_confirm_keyimage_sync(ctx)
await layout.require_confirm_keyimage_sync(ctx)
s.num_outputs = msg.num
s.expected_hash = msg.hash
@ -71,7 +70,7 @@ async def _sync_step(s, ctx, tds):
buff = bytearray(32 * 3)
buff_mv = memoryview(buff)
await confirms.keyimage_sync_step(ctx, s.current_output, s.num_outputs)
await layout.keyimage_sync_step(ctx, s.current_output, s.num_outputs)
for td in tds.tdis:
s.current_output += 1

View File

@ -1,14 +1,14 @@
from ubinascii import hexlify
from trezor import ui, wire
from trezor import strings, ui
from trezor.enums import ButtonRequestType
from trezor.ui.components.tt.text import Text
from trezor.ui.layouts import confirm_action
from trezor.ui.layouts import (
confirm_action,
confirm_hex,
confirm_metadata,
confirm_output,
)
from trezor.ui.popup import Popup
from trezor.utils import chunks
from apps.common.confirm import require_confirm, require_hold_to_confirm
from apps.monero.layout import common
DUMMY_PAYMENT_ID = b"\x00\x00\x00\x00\x00\x00\x00\x00"
@ -21,6 +21,10 @@ if False:
)
def _format_amount(value):
return "%s XMR" % strings.format_amount(value, 12)
async def require_confirm_watchkey(ctx):
await confirm_action(
ctx,
@ -123,43 +127,46 @@ async def _require_confirm_output(
version, dst.addr.spend_public_key, dst.addr.view_public_key, payment_id
)
text_addr = common.split_address(addr.decode())
text_amount = common.format_amount(dst.amount)
if not await common.naive_pagination(
await confirm_output(
ctx,
[ui.BOLD, text_amount, ui.MONO] + list(text_addr),
"Confirm send",
ui.ICON_SEND,
ui.GREEN,
4,
):
raise wire.ActionCancelled
address=addr.decode(),
amount=_format_amount(dst.amount),
font_amount=ui.BOLD,
br_code=ButtonRequestType.SignTx,
)
async def _require_confirm_payment_id(ctx, payment_id: bytes):
if not await common.naive_pagination(
await confirm_hex(
ctx,
[ui.MONO] + list(chunks(hexlify(payment_id).decode(), 16)),
"Payment ID",
ui.ICON_SEND,
ui.GREEN,
):
raise wire.ActionCancelled
"confirm_payment_id",
title="Payment ID",
data=hexlify(payment_id).decode(),
br_code=ButtonRequestType.SignTx,
)
async def _require_confirm_fee(ctx, fee):
content = Text("Confirm fee", ui.ICON_SEND, ui.GREEN)
content.bold(common.format_amount(fee))
await require_hold_to_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
await confirm_metadata(
ctx,
"confirm_final",
title="Confirm fee",
content="{}",
param=_format_amount(fee),
hide_continue=True,
hold=True,
)
async def _require_confirm_unlock_time(ctx, unlock_time):
content = Text("Confirm unlock time", ui.ICON_SEND, ui.GREEN)
content.normal("Unlock time for this transaction is set to")
content.bold(str(unlock_time))
content.normal("Continue?")
await require_confirm(ctx, content, ButtonRequestType.SignTx)
await confirm_metadata(
ctx,
"confirm_locktime",
"Confirm unlock time",
"Unlock time for this transaction is set to {}",
str(unlock_time),
br_code=ButtonRequestType.SignTx,
)
class TransactionStep(ui.Component):
@ -242,28 +249,3 @@ async def live_refresh_step(ctx, current):
if current is None:
return
await Popup(LiveRefreshStep(current))
async def show_address(
ctx, address: str, desc: str = "Confirm address", network: str = None
):
from apps.common.confirm import confirm
from trezor.enums import ButtonRequestType
from trezor.ui.components.tt.button import ButtonDefault
from trezor.ui.components.tt.scroll import Paginated
pages = []
for lines in common.paginate_lines(common.split_address(address), 5):
text = Text(desc, ui.ICON_RECEIVE, ui.GREEN)
if network is not None:
text.normal("%s network" % network)
text.mono(*lines)
pages.append(text)
return await confirm(
ctx,
Paginated(pages),
code=ButtonRequestType.Address,
cancel="QR",
cancel_style=ButtonDefault,
)

View File

@ -1,70 +0,0 @@
from trezor import strings, ui, utils
from trezor.enums import ButtonRequestType
from trezor.ui.components.tt.text import Text
from apps.common import button_request
async def naive_pagination(
ctx, lines, title, icon=ui.ICON_RESET, icon_color=ui.ORANGE, per_page=5
):
from trezor.ui.components.tt.scroll import (
CANCELLED,
CONFIRMED,
PaginatedWithButtons,
)
pages = []
page_lines = paginate_lines(lines, per_page)
for i, lines in enumerate(page_lines):
if len(page_lines) > 1:
paging = "%s/%s" % (i + 1, len(page_lines))
else:
paging = ""
text = Text("%s %s" % (title, paging), icon, icon_color)
text.normal(*lines)
pages.append(text)
paginated = PaginatedWithButtons(pages, one_by_one=True)
while True:
await button_request(ctx, code=ButtonRequestType.SignTx)
result = await ctx.wait(paginated)
if result is CONFIRMED:
return True
if result is CANCELLED:
return False
def paginate_lines(lines, lines_per_page=5):
"""Paginates lines across pages with preserving formatting modifiers (e.g., mono)"""
pages = []
cpage = []
nlines = 0
last_modifier = None
for line in lines:
cpage.append(line)
if not isinstance(line, int):
nlines += 1
else:
last_modifier = line
if nlines >= lines_per_page:
pages.append(cpage)
cpage = []
nlines = 0
if last_modifier is not None:
cpage.append(last_modifier)
if nlines > 0:
pages.append(cpage)
return pages
def format_amount(value):
return "%s XMR" % strings.format_amount(value, 12)
def split_address(address):
return utils.chunks(address, 16)

View File

@ -13,8 +13,7 @@ from trezor.messages import (
from apps.common import paths
from apps.common.keychain import auto_keychain
from apps.monero import misc
from apps.monero.layout import confirms
from apps.monero import layout, misc
from apps.monero.xmr import crypto, key_image, monero
from apps.monero.xmr.crypto import chacha_poly
@ -52,7 +51,7 @@ async def _init_step(
await paths.validate_path(ctx, keychain, msg.address_n)
if not storage.cache.get(storage.cache.APP_MONERO_LIVE_REFRESH):
await confirms.require_confirm_live_refresh(ctx)
await layout.require_confirm_live_refresh(ctx)
storage.cache.set(storage.cache.APP_MONERO_LIVE_REFRESH, b"\x01")
s.creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
@ -64,7 +63,7 @@ async def _refresh_step(s: LiveRefreshState, ctx, msg: MoneroLiveRefreshStepRequ
buff = bytearray(32 * 3)
buff_mv = memoryview(buff)
await confirms.live_refresh_step(ctx, s.current_output)
await layout.live_refresh_step(ctx, s.current_output)
s.current_output += 1
if __debug__:

View File

@ -4,8 +4,7 @@ Initializes a new transaction.
import gc
from apps.monero import misc, signing
from apps.monero.layout import confirms
from apps.monero import layout, misc, signing
from apps.monero.signing.state import State
from apps.monero.xmr import crypto, monero
@ -48,7 +47,7 @@ async def init_transaction(
state.progress_cur = 0
# Ask for confirmation
await confirms.require_confirm_transaction(
await layout.require_confirm_transaction(
state.ctx, state, tsx_data, state.creds.network_type
)
state.creds.address = None

View File

@ -11,7 +11,7 @@ If number of inputs is small, in-memory mode is used = alpha, pseudo_outs are ke
Otherwise pseudo_outs are offloaded with HMAC, alpha is offloaded encrypted under chacha_poly with
key derived for exactly this purpose.
"""
from apps.monero.layout import confirms
from apps.monero import layout
from apps.monero.xmr import crypto, monero, serialize
from .state import State
@ -32,7 +32,7 @@ async def set_input(
state.current_input_index += 1
await confirms.transaction_step(state, state.STEP_INP, state.current_input_index)
await layout.transaction_step(state, state.STEP_INP, state.current_input_index)
if state.last_step > state.STEP_INP:
raise ValueError("Invalid state transition")

View File

@ -16,7 +16,7 @@ HMAC correctness (host sends original sort idx) and ordering check
on the key images. This step is skipped.
"""
from apps.monero.layout.confirms import transaction_step
from apps.monero.layout import transaction_step
from .state import State

View File

@ -3,7 +3,7 @@ This step serves for an incremental hashing of tx.vin[i] to the tx_prefix_hasher
after the sorting on tx.vin[i].ki. The sorting order was received in the previous step.
"""
from apps.monero.layout import confirms
from apps.monero import layout
from apps.monero.signing import offloading_keys
from apps.monero.xmr import crypto
@ -25,9 +25,7 @@ async def input_vini(
) -> MoneroTransactionInputViniAck:
from trezor.messages import MoneroTransactionInputViniAck
await confirms.transaction_step(
state, state.STEP_VINI, state.current_input_index + 1
)
await layout.transaction_step(state, state.STEP_VINI, state.current_input_index + 1)
if state.last_step not in (state.STEP_INP, state.STEP_PERM, state.STEP_VINI):
raise ValueError("Invalid state transition")
if state.current_input_index >= state.input_count:

View File

@ -3,7 +3,7 @@ All inputs set. Defining range signature parameters.
If in the applicable offloading mode, generate commitment masks.
"""
from apps.monero.layout import confirms
from apps.monero import layout
from apps.monero.xmr import crypto
from .state import State
@ -15,7 +15,7 @@ if False:
async def all_inputs_set(state: State) -> MoneroTransactionAllInputsSetAck:
state.mem_trace(0)
await confirms.transaction_step(state, state.STEP_ALL_IN)
await layout.transaction_step(state, state.STEP_ALL_IN)
from trezor.messages import MoneroTransactionAllInputsSetAck

View File

@ -6,8 +6,7 @@ import gc
from trezor import utils
from apps.monero import signing
from apps.monero.layout import confirms
from apps.monero import layout, signing
from apps.monero.signing import offloading_keys
from apps.monero.xmr import crypto, serialize
@ -36,7 +35,7 @@ async def set_output(
# Progress update only for master message (skip for offloaded BP msg)
if not is_offloaded_bp:
await confirms.transaction_step(
await layout.transaction_step(
state, state.STEP_OUT, state.current_output_index + 1
)

View File

@ -8,7 +8,7 @@ import gc
from trezor import utils
from apps.monero.layout import confirms
from apps.monero import layout
from apps.monero.xmr import crypto
from .state import State
@ -20,7 +20,7 @@ if False:
async def all_outputs_set(state: State) -> MoneroTransactionAllOutSetAck:
state.mem_trace(0)
await confirms.transaction_step(state, state.STEP_ALL_OUT)
await layout.transaction_step(state, state.STEP_ALL_OUT)
state.mem_trace(1)
_validate(state)

View File

@ -14,7 +14,7 @@ import gc
from trezor import utils
from apps.monero.layout import confirms
from apps.monero import layout
from apps.monero.xmr import crypto
from .state import State
@ -48,9 +48,7 @@ async def sign_input(
:param orig_idx: original index of the src_entr before sorting (HMAC check)
:return: Generated signature MGs[i]
"""
await confirms.transaction_step(
state, state.STEP_SIGN, state.current_input_index + 1
)
await layout.transaction_step(state, state.STEP_SIGN, state.current_input_index + 1)
state.current_input_index += 1
if state.last_step not in (state.STEP_ALL_OUT, state.STEP_SIGN):

View File

@ -9,6 +9,12 @@ from .confirm import CANCELLED, CONFIRMED, Confirm
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 ..common.text import TextContent
_PAGINATED_LINE_WIDTH = const(204)
WAS_PAGED = object()
@ -301,3 +307,71 @@ def paginate_text(
pages[-1] = Confirm(pages[-1])
return Paginated(pages)
def paginate_paragraphs(
para: Iterable[tuple[int, str]],
header: str,
header_icon: str = ui.ICON_DEFAULT,
icon_color: int = ui.ORANGE_ICON,
break_words: bool = False,
confirm_kwargs: Dict[str, Any] = {},
) -> Union[Confirm, Paginated]:
span = Span("", 0, ui.NORMAL, break_words=break_words)
lines = 0
content: list[TextContent] = []
for font, text in para:
span.reset(text, 0, font, break_words=break_words)
lines += span.count_lines()
# we'll need this for multipage too
if content:
content.append("\n")
content.append(font)
content.append(text)
if lines <= TEXT_MAX_LINES:
result = Text(
header,
header_icon=header_icon,
icon_color=icon_color,
new_lines=False,
break_words=break_words,
)
for font, text in para:
if len(result.content) != 0:
result.content.append("\n")
result.content.append(font)
result.content.append(text)
return Confirm(result, **confirm_kwargs)
else:
pages: list[ui.Component] = []
lines_left = 0
for i, (font, text) in enumerate(para):
span.reset(
text, 0, font, break_words=break_words, line_width=_PAGINATED_LINE_WIDTH
)
while span.has_more_content():
span.next_line()
if lines_left <= 0:
page = Text(
header,
header_icon=header_icon,
icon_color=icon_color,
new_lines=False,
content_offset=i * 3 + 1, # font, _text_, newline
char_offset=span.start,
line_width=_PAGINATED_LINE_WIDTH,
render_page_overflow=False,
break_words=break_words,
)
page.content = content
pages.append(page)
lines_left = TEXT_MAX_LINES - 1
else:
lines_left -= 1
pages[-1] = Confirm(pages[-1], **confirm_kwargs)
return Paginated(pages)

View File

@ -6,7 +6,7 @@ TEXT_LINE_HEIGHT_HALF = const(13)
TEXT_MARGIN_LEFT = const(14)
TEXT_MAX_LINES = const(5)
MONO_CHARS_PER_LINE = const(17)
MONO_ADDR_PER_LINE = const(17)
MONO_HEX_PER_LINE = const(18)
QR_X = const(120)

View File

@ -11,10 +11,10 @@ from ..components.common import break_path_to_lines
from ..components.common.confirm import is_confirmed, raise_if_cancelled
from ..components.tt.button import ButtonCancel, ButtonDefault
from ..components.tt.confirm import Confirm, HoldToConfirm
from ..components.tt.scroll import Paginated, paginate_text
from ..components.tt.scroll import Paginated, paginate_paragraphs, paginate_text
from ..components.tt.text import Span, Text
from ..constants.tt import (
MONO_CHARS_PER_LINE,
MONO_ADDR_PER_LINE,
MONO_HEX_PER_LINE,
QR_SIZE_THRESHOLD,
QR_X,
@ -182,7 +182,7 @@ async def confirm_backup(ctx: wire.GenericContext) -> bool:
async def confirm_path_warning(ctx: wire.GenericContext, path: str) -> None:
text = Text("Confirm path", ui.ICON_WRONG, ui.RED)
text.normal("Path")
text.mono(*break_path_to_lines(path, MONO_CHARS_PER_LINE))
text.mono(*break_path_to_lines(path, MONO_ADDR_PER_LINE))
text.normal("is unknown.", "Are you sure?")
await raise_if_cancelled(
interact(
@ -207,7 +207,7 @@ def _show_qr(
def _split_address(address: str) -> Iterator[str]:
return chunks_intersperse(address, MONO_CHARS_PER_LINE)
return chunks_intersperse(address, MONO_ADDR_PER_LINE)
def _truncate_hex(
@ -232,13 +232,18 @@ def _show_address(
address: str,
desc: str,
network: str | None = None,
) -> Confirm:
text = Text(desc, ui.ICON_RECEIVE, ui.GREEN, new_lines=False)
if network is not None:
text.normal("%s network\n" % network)
text.mono(*_split_address(address))
return Confirm(text, cancel="QR", cancel_style=ButtonDefault)
) -> Confirm | Paginated:
para = [(ui.NORMAL, "%s network" % network)] if network is not None else []
para.extend(
(ui.MONO, address_line) for address_line in chunks(address, MONO_ADDR_PER_LINE)
)
return paginate_paragraphs(
para,
header=desc,
header_icon=ui.ICON_RECEIVE,
icon_color=ui.GREEN,
confirm_kwargs={"cancel": "QR", "cancel_style": ButtonDefault},
)
def _show_xpub(xpub: str, desc: str, cancel: str) -> Paginated:
@ -332,6 +337,7 @@ def show_pubkey(
data=pubkey,
br_code=ButtonRequestType.PublicKey,
icon=ui.ICON_RECEIVE,
truncate=True, # should fit?
)
@ -441,14 +447,22 @@ async def confirm_output(
amount: str,
font_amount: int = ui.NORMAL, # TODO cleanup @ redesign
color_to: int = ui.FG, # TODO cleanup @ redesign
width: int = MONO_ADDR_PER_LINE,
width_paginated: int = MONO_ADDR_PER_LINE - 1,
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
) -> None:
text = Text("Confirm sending", ui.ICON_SEND, ui.GREEN, new_lines=False)
text.content = [font_amount, amount, ui.NORMAL, color_to, " to\n", ui.FG]
text.mono(*_split_address(address))
await raise_if_cancelled(
interact(ctx, Confirm(text), "confirm_output", br_code)
)
title = "Confirm sending"
if len(address) > (TEXT_MAX_LINES - 1) * width:
para = [(font_amount, amount)]
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)
text.content = [font_amount, amount, ui.NORMAL, color_to, " to\n", ui.FG]
text.mono(*chunks_intersperse(address, width))
content = Confirm(text)
await raise_if_cancelled(interact(ctx, content, "confirm_output", br_code))
async def confirm_decred_sstx_submission(
@ -482,25 +496,34 @@ async def confirm_hex(
font_description: int = ui.NORMAL, # TODO cleanup @ redesign
color_description: int = ui.FG, # TODO cleanup @ redesign
width: int = MONO_HEX_PER_LINE,
width_paginated: int = MONO_HEX_PER_LINE - 2,
truncate: bool = False,
truncate_middle: bool = False,
) -> None:
text = Text(title, icon, icon_color, new_lines=False)
description_lines = 0
if description is not None:
description_lines = Span(description, 0, font_description).count_lines()
text.content.extend(
(font_description, color_description, description, ui.FG)
if truncate:
text = Text(title, icon, icon_color, new_lines=False)
description_lines = 0
if description is not None:
description_lines = Span(description, 0, font_description).count_lines()
text.content.extend(
(font_description, color_description, description, ui.FG)
)
text.br()
text.mono(
*_truncate_hex(
data,
lines=TEXT_MAX_LINES - description_lines,
width=width,
middle=truncate_middle,
)
)
text.br()
text.mono(
*_truncate_hex(
data,
lines=TEXT_MAX_LINES - description_lines,
width=width,
middle=truncate_middle,
)
)
content: ui.Layout = Confirm(text)
content: ui.Layout = Confirm(text)
else:
width_paginated = min(width, MONO_HEX_PER_LINE - 2)
assert color_description == ui.FG # only ethereum uses this and it truncates
para = [(font_description, description)] if description is not None else []
para.extend((ui.MONO, line) for line in chunks(data, width_paginated))
content = paginate_paragraphs(para, title, icon, icon_color)
await raise_if_cancelled(interact(ctx, content, br_type, br_code))
@ -554,14 +577,19 @@ async def confirm_metadata(
content: str,
param: str | None = None,
br_code: ButtonRequestType = ButtonRequestType.SignTx,
hide_continue: bool = False,
hold: bool = False,
) -> None:
text = Text(title, ui.ICON_SEND, ui.GREEN, new_lines=False)
text.format_parametrized(content, param if param is not None else "")
text.br()
text.normal("Continue?")
if not hide_continue:
text.br()
text.normal("Continue?")
await raise_if_cancelled(interact(ctx, Confirm(text), br_type, br_code))
cls = HoldToConfirm if hold else Confirm
await raise_if_cancelled(interact(ctx, cls(text), br_type, br_code))
async def confirm_replacement(

View File

@ -29,14 +29,14 @@ class TestMsgMoneroGetaddress:
@pytest.mark.setup_client(mnemonic=MNEMONIC12)
def test_monero_getaddress(self, client):
assert (
monero.get_address(client, parse_path("m/44h/128h/0h"))
monero.get_address(client, parse_path("m/44h/128h/0h"), show_display=True)
== b"4Ahp23WfMrMFK3wYL2hLWQFGt87ZTeRkufS6JoQZu6MEFDokAQeGWmu9MA3GFq1yVLSJQbKJqVAn9F9DLYGpRzRAEXqAXKM"
)
assert (
monero.get_address(client, parse_path("m/44h/128h/1h"))
monero.get_address(client, parse_path("m/44h/128h/1h"), show_display=True)
== b"44iAazhoAkv5a5RqLNVyh82a1n3ceNggmN4Ho7bUBJ14WkEVR8uFTe9f7v5rNnJ2kEbVXxfXiRzsD5Jtc6NvBi4D6WNHPie"
)
assert (
monero.get_address(client, parse_path("m/44h/128h/2h"))
monero.get_address(client, parse_path("m/44h/128h/2h"), show_display=True)
== b"47ejhmbZ4wHUhXaqA4b7PN667oPMkokf4ZkNdWrMSPy9TNaLVr7vLqVUQHh2MnmaAEiyrvLsX8xUf99q3j1iAeMV8YvSFcH"
)