From 47f7ec6972623d89c01a5f96877040f1fececb6c Mon Sep 17 00:00:00 2001 From: grdddj Date: Mon, 2 Jan 2023 17:31:37 +0100 Subject: [PATCH] WIP - design of Receive flow --- core/embed/rust/librust_qstr.h | 4 + .../rust/src/ui/model_tr/component/button.rs | 15 +++ .../rust/src/ui/model_tr/component/flow.rs | 8 +- .../src/ui/model_tr/component/flow_pages.rs | 30 ++++-- .../component/flow_pages_poc_helpers.rs | 83 +++++++++++++--- core/embed/rust/src/ui/model_tr/layout.rs | 94 +++++++++++++++---- core/embed/rust/src/ui/model_tr/theme.rs | 6 +- core/mocks/generated/trezorui2.pyi | 8 +- core/src/apps/binance/get_address.py | 9 +- core/src/apps/bitcoin/get_address.py | 29 +++++- core/src/apps/cardano/layout.py | 10 +- core/src/apps/ethereum/get_address.py | 9 +- core/src/apps/monero/get_address.py | 5 +- core/src/apps/nem/get_address.py | 5 +- core/src/apps/ripple/get_address.py | 9 +- core/src/apps/stellar/get_address.py | 10 +- core/src/apps/tezos/get_address.py | 9 +- core/src/trezor/ui/layouts/tr/__init__.py | 66 ++++--------- core/src/trezor/ui/layouts/tt_v2/__init__.py | 13 ++- 19 files changed, 307 insertions(+), 115 deletions(-) diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index e33252df1b..3c40e04df5 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -60,6 +60,7 @@ static void _librust_qstrs(void) { MP_QSTR_show_remaining_shares; MP_QSTR_show_share_words; MP_QSTR_show_progress; + MP_QSTR_show_receive_address; MP_QSTR_progress_event; MP_QSTR_usb_event; @@ -128,4 +129,7 @@ static void _librust_qstrs(void) { MP_QSTR_fee_label; MP_QSTR_address_title; MP_QSTR_amount_title; + MP_QSTR_show_receive_address; + MP_QSTR_derivation_path; + MP_QSTR_address_qr; } diff --git a/core/embed/rust/src/ui/model_tr/component/button.rs b/core/embed/rust/src/ui/model_tr/component/button.rs index e9e9cc40c4..577eb59795 100644 --- a/core/embed/rust/src/ui/model_tr/component/button.rs +++ b/core/embed/rust/src/ui/model_tr/component/button.rs @@ -755,6 +755,16 @@ impl ButtonActions { ) } + /// Going to last page with left, to the next page with right and confirm + /// with middle + pub fn last_confirm_next() -> Self { + Self::new( + Some(ButtonAction::GoToIndex(-1)), + Some(ButtonAction::Confirm), + Some(ButtonAction::NextPage), + ) + } + /// Cancelling with left, going to the next page with right pub fn cancel_next() -> Self { Self::new( @@ -769,6 +779,11 @@ impl ButtonActions { Self::new(None, None, Some(ButtonAction::NextPage)) } + /// Only going to the prev page with left + pub fn only_prev() -> Self { + Self::new(Some(ButtonAction::PrevPage), None, None) + } + /// Cancelling with left, confirming with right pub fn cancel_confirm() -> Self { Self::new( diff --git a/core/embed/rust/src/ui/model_tr/component/flow.rs b/core/embed/rust/src/ui/model_tr/component/flow.rs index 51d583f522..0c023ed3a2 100644 --- a/core/embed/rust/src/ui/model_tr/component/flow.rs +++ b/core/embed/rust/src/ui/model_tr/component/flow.rs @@ -234,12 +234,16 @@ where fn paint(&mut self) { self.pad.paint(); // Scrollbars are painted only with a title - if let Some(title) = &self.common_title { + if let Some(title) = self.common_title { self.scrollbar.paint(); common::paint_header_left(title, self.title_area); } - self.current_page.paint(); self.buttons.paint(); + // On purpose painting current page at the end, after buttons, + // because we sometimes (in the case of QR code) need to use the + // whole height of the display for showing the content + // (and painting buttons last would cover the lower part). + self.current_page.paint(); } } diff --git a/core/embed/rust/src/ui/model_tr/component/flow_pages.rs b/core/embed/rust/src/ui/model_tr/component/flow_pages.rs index 8ab0dfd38c..d00208bed5 100644 --- a/core/embed/rust/src/ui/model_tr/component/flow_pages.rs +++ b/core/embed/rust/src/ui/model_tr/component/flow_pages.rs @@ -3,7 +3,7 @@ use crate::{ ui::{ component::Paginate, display::{Font, Icon, IconAndName}, - geometry::{Alignment, Offset, Rect}, + geometry::{Alignment, Offset, Point, Rect}, model_tr::theme, util::ResultExt, }, @@ -13,7 +13,8 @@ use heapless::Vec; use super::{ flow_pages_poc_helpers::{ - LayoutFit, LayoutSink, Op, TextLayout, TextNoOp, TextRenderer, TextStyle, ToDisplay, + LayoutFit, LayoutSink, Op, QrCodeInfo, TextLayout, TextNoOp, TextRenderer, TextStyle, + ToDisplay, }, ButtonActions, ButtonDetails, ButtonLayout, }; @@ -148,13 +149,15 @@ impl Page { current.btn_left }; - let btn_right = if self.has_next_page() { - Some(ButtonDetails::down_arrow_icon_wide()) + // Middle button should be shown only on the last page, not to collide + // with the fat right button. + let (btn_middle, btn_right) = if self.has_next_page() { + (None, Some(ButtonDetails::down_arrow_icon_wide())) } else { - current.btn_right + (current.btn_middle, current.btn_right) }; - ButtonLayout::new(btn_left, current.btn_middle, btn_right) + ButtonLayout::new(btn_left, btn_middle, btn_right) } pub fn btn_actions(&self) -> ButtonActions { @@ -211,6 +214,21 @@ impl Page { self.with_new_item(Op::Icon(Icon::new(icon))) } + pub fn qr_code( + self, + text: StrBuffer, + max_size: i16, + case_sensitive: bool, + center: Point, + ) -> Self { + self.with_new_item(Op::QrCode(QrCodeInfo::new( + text, + max_size, + case_sensitive, + center, + ))) + } + pub fn font(self, font: Font) -> Self { self.with_new_item(Op::Font(font)) } diff --git a/core/embed/rust/src/ui/model_tr/component/flow_pages_poc_helpers.rs b/core/embed/rust/src/ui/model_tr/component/flow_pages_poc_helpers.rs index 2cee012b9c..a46b491d34 100644 --- a/core/embed/rust/src/ui/model_tr/component/flow_pages_poc_helpers.rs +++ b/core/embed/rust/src/ui/model_tr/component/flow_pages_poc_helpers.rs @@ -12,7 +12,14 @@ use crate::{ use heapless::Vec; -// TODO: document this +/// To account for operations that are not made of characters +/// but need to be accounted for somehow. +/// Number of processed characters will be increased by this +/// to account for the operation. +const PROCESSED_CHARS_ONE: usize = 1; + +/// Container for text allowing for its displaying by chunks +/// without the need to allocate a new String each time. #[derive(Clone)] pub struct ToDisplay { pub text: StrBuffer, @@ -28,15 +35,26 @@ impl ToDisplay { } } +/// Holding information about a QR code to be displayed. #[derive(Clone)] pub struct QrCodeInfo { - pub text: StrBuffer, + pub data: StrBuffer, pub max_size: i16, pub case_sensitive: bool, pub center: Point, } -// TODO: add QrCode(QrCodeInfo) +impl QrCodeInfo { + pub fn new(data: StrBuffer, max_size: i16, case_sensitive: bool, center: Point) -> Self { + Self { + data, + max_size, + case_sensitive, + center, + } + } +} + /// Operations that can be done on the screen. #[derive(Clone)] pub enum Op { @@ -44,6 +62,8 @@ pub enum Op { Text(ToDisplay), /// Render icon. Icon(Icon), + /// Render QR Code. + QrCode(QrCodeInfo), /// Set current text color. Color(Color), /// Set currently used font. @@ -218,13 +238,15 @@ impl TextLayout { } } Op::Icon(_) if skipped < skip_bytes => { - // Assume the icon accounts for one character - skipped = skipped.saturating_add(1); + skipped = skipped.saturating_add(PROCESSED_CHARS_ONE); + None + } + Op::QrCode(_) if skipped < skip_bytes => { + skipped = skipped.saturating_add(PROCESSED_CHARS_ONE); None } Op::NextPage if skipped < skip_bytes => { - // Skip the next page and consider it one character - skipped = skipped.saturating_add(1); + skipped = skipped.saturating_add(PROCESSED_CHARS_ONE); None } Op::CursorOffset(_) if skipped < skip_bytes => { @@ -258,16 +280,28 @@ impl TextLayout { Op::NextPage => { // Pretending that nothing more fits on current page to force // continuing on the next one - // Making that to account for one character for pagination purposes - total_processed_chars += 1; + total_processed_chars += PROCESSED_CHARS_ONE; return LayoutFit::OutOfBounds { processed_chars: total_processed_chars, height: self.layout_height(init_cursor, *cursor), }; } + Op::QrCode(qr_details) => { + self.layout_qr_code(qr_details, sink); + // QR codes are always the last component that can be shown + // on the given page (meaning a series of Op's). + // Throwing Fitting to force the end of the whole page. + // (It would be too complicated to account for it by modifying cursor, etc., + // and there is not a need for it currently. If we want QR code together + // with some other things on the same screen, just first render the other + // things and do the QR code last.) + total_processed_chars += PROCESSED_CHARS_ONE; + return LayoutFit::Fitting { + processed_chars: total_processed_chars, + height: self.layout_height(init_cursor, *cursor), + }; + } // Drawing text or icon - // TODO: add QRCode support - always returning OOB - // to force going to the next page Op::Text(_) | Op::Icon(_) => { // Text and Icon behave similarly - we try to fit them // on the current page and if they do not fit, @@ -452,12 +486,18 @@ impl TextLayout { cursor.x += icon.width() as i16; LayoutFit::Fitting { - // TODO: unify the 1 being returned - make it a CONST probably - processed_chars: 1, + processed_chars: PROCESSED_CHARS_ONE, height: 0, // it should just draw on one line } } + /// Fitting the QR code on the current screen. + /// Not returning `LayoutFit`, QR codes are handled differently, + /// they automatically throw out of bounds. + pub fn layout_qr_code(&self, qr_code_info: QrCodeInfo, sink: &mut dyn LayoutSink) { + sink.qrcode(qr_code_info); + } + /// Overall height of the content, including paddings. fn layout_height(&self, init_cursor: Point, end_cursor: Point) -> i16 { self.padding_top @@ -495,7 +535,7 @@ pub trait LayoutSink { /// Icon should be displayed. fn icon(&mut self, _cursor: Point, _layout: &TextLayout, _icon: Icon) {} /// QR code should be displayed. - fn qrcode(&mut self, _cursor: Point, _layout: &TextLayout, _qr_code: QrCodeInfo) {} + fn qrcode(&mut self, _qr_code: QrCodeInfo) {} /// Hyphen at the end of line. fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {} /// Ellipsis at the end of the page. @@ -594,6 +634,16 @@ impl LayoutSink for TextRenderer { layout.style.background_color, ); } + + fn qrcode(&mut self, qr_code: QrCodeInfo) { + display::qrcode( + qr_code.center, + qr_code.data.as_ref(), + qr_code.max_size as _, + qr_code.case_sensitive, + ) + .unwrap_or(()) + } } /// `LayoutSink` for debugging purposes. @@ -613,6 +663,11 @@ impl<'a> LayoutSink for TraceSink<'a> { icon.trace(self.0); } + fn qrcode(&mut self, qr_code: QrCodeInfo) { + self.0.string("QR code: "); + self.0.string(qr_code.data.as_ref()); + } + fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) { self.0.string("-"); } diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index 3a310923bc..19b40969df 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -18,7 +18,6 @@ use crate::{ component::{ base::Component, paginated::{PageMsg, Paginate}, - painter, text::paragraphs::{ Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, Paragraphs, VecExt, }, @@ -41,7 +40,7 @@ use super::{ NoBtnDialogMsg, Page, PassphraseEntry, PassphraseEntryMsg, PinEntry, PinEntryMsg, Progress, QRCodePage, QRCodePageMessage, ShareWords, SimpleChoice, SimpleChoiceMsg, }, - theme, + constant, theme, }; pub enum CancelConfirmMsg { @@ -373,24 +372,81 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } -extern "C" fn new_show_qr(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { +extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?; - let verb_cancel: StrBuffer = kwargs.get(Qstr::MP_QSTR_verb_cancel)?.try_into()?; + let address_qr: StrBuffer = kwargs.get(Qstr::MP_QSTR_address_qr)?.try_into()?; + let account: StrBuffer = kwargs.get(Qstr::MP_QSTR_account)?.try_into()?; + let derivation_path: StrBuffer = kwargs.get(Qstr::MP_QSTR_derivation_path)?.try_into()?; let case_sensitive: bool = kwargs.get(Qstr::MP_QSTR_case_sensitive)?.try_into()?; - let verb: StrBuffer = "CONFIRM".into(); + let get_page = move |page_index| { + // Showing two screens - the recipient address and summary confirmation + match page_index { + 0 => { + // RECEIVE ADDRESS + let btn_layout = ButtonLayout::new( + Some(ButtonDetails::cancel_icon()), + Some(ButtonDetails::armed_text("CONFIRM".into())), + Some(ButtonDetails::text("i".into())), + ); + let btn_actions = ButtonActions::last_confirm_next(); + Page::<15>::new(btn_layout, btn_actions, Font::BOLD) + .text_bold(title) + .newline() + .newline_half() + .text_mono(address) + } + 1 => { + // QR CODE + let btn_layout = ButtonLayout::new( + Some(ButtonDetails::left_arrow_icon()), + None, + Some(ButtonDetails::right_arrow_icon()), + ); + let btn_actions = ButtonActions::prev_next(); + Page::<15>::new(btn_layout, btn_actions, Font::MONO).qr_code( + address_qr, + theme::QR_SIDE_MAX, + case_sensitive, + constant::screen().center(), + ) + } + 2 => { + // ADDRESS INFO + let btn_layout = + ButtonLayout::new(Some(ButtonDetails::left_arrow_icon()), None, None); + let btn_actions = ButtonActions::only_prev(); + Page::<15>::new(btn_layout, btn_actions, Font::MONO) + .text_bold("Account:".into()) + .newline() + .text_mono(account) + .newline() + .text_bold("Derivation path:".into()) + .newline() + .text_mono(derivation_path) + } + 3 => { + // ADDRESS MISMATCH + let btn_layout = ButtonLayout::new( + Some(ButtonDetails::left_arrow_icon()), + None, + Some(ButtonDetails::text("QUIT".into())), + ); + let btn_actions = ButtonActions::beginning_cancel(); + Page::<15>::new(btn_layout, btn_actions, Font::MONO) + .text_bold("ADDRESS MISMATCH?".into()) + .newline() + .newline_half() + .text_mono("Please contact Trezor support on trezor.io/support".into()) + } + _ => unreachable!(), + } + }; + let pages = FlowPages::new(get_page, 4); - let qr_code = painter::qrcode_painter(address, theme::QR_SIDE_MAX as u32, case_sensitive); - - let btn_layout = ButtonLayout::new( - Some(ButtonDetails::text(verb_cancel)), - None, - Some(ButtonDetails::text(verb)), - ); - - let obj = LayoutObj::new(QRCodePage::new(title, qr_code, btn_layout))?; + let obj = LayoutObj::new(Flow::new(pages))?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -760,15 +816,17 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Confirm summary of a transaction. Specific for model R.""" Qstr::MP_QSTR_confirm_total_r => obj_fn_kw!(0, new_confirm_total).as_obj(), - /// def show_qr( + /// def show_receive_address( /// *, /// title: str, /// address: str, - /// verb_cancel: str, + /// address_qr: str, + /// account: str, + /// derivation_path: str, /// case_sensitive: bool, /// ) -> object: - /// """Show QR code.""" - Qstr::MP_QSTR_show_qr => obj_fn_kw!(0, new_show_qr).as_obj(), + /// """Show receive address together with QR code and details about it.""" + Qstr::MP_QSTR_show_receive_address => obj_fn_kw!(0, new_show_receive_address).as_obj(), /// def show_info( /// *, diff --git a/core/embed/rust/src/ui/model_tr/theme.rs b/core/embed/rust/src/ui/model_tr/theme.rs index c5546d7d78..80daaa1e5e 100644 --- a/core/embed/rust/src/ui/model_tr/theme.rs +++ b/core/embed/rust/src/ui/model_tr/theme.rs @@ -65,5 +65,7 @@ pub const BUTTON_CONTENT_HEIGHT: i16 = 7; pub const BUTTON_OUTLINE: i16 = 3; pub const BUTTON_HEIGHT: i16 = BUTTON_CONTENT_HEIGHT + 2 * BUTTON_OUTLINE; -// Full-size QR code. -pub const QR_SIDE_MAX: i16 = 64 - BUTTON_HEIGHT; +/// Full-size QR code. +/// Accounting for little larger QR code than the screen, +/// to fit taproot addresses (top and bottom row will not be visible). +pub const QR_SIDE_MAX: i16 = 66; diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 0221008535..65529c7df6 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -60,14 +60,16 @@ def confirm_total_r( # rust/src/ui/model_tr/layout.rs -def show_qr( +def show_receive_address( *, title: str, address: str, - verb_cancel: str, + address_qr: str, + account: str, + derivation_path: str, case_sensitive: bool, ) -> object: - """Show QR code.""" + """Show receive address together with QR code and details about it.""" # rust/src/ui/model_tr/layout.rs diff --git a/core/src/apps/binance/get_address.py b/core/src/apps/binance/get_address.py index 1b114e3be9..e4d41a486f 100644 --- a/core/src/apps/binance/get_address.py +++ b/core/src/apps/binance/get_address.py @@ -28,7 +28,12 @@ async def get_address( pubkey = node.public_key() address = address_from_public_key(pubkey, HRP) if msg.show_display: - title = paths.address_n_to_str(address_n) - await show_address(ctx, address, title=title) + derivation_path = paths.address_n_to_str(address_n) + await show_address( + ctx, + address, + derivation_path=derivation_path, + account="Binance", + ) return BinanceAddress(address=address) diff --git a/core/src/apps/bitcoin/get_address.py b/core/src/apps/bitcoin/get_address.py index 3feeeceac6..50ba78e315 100644 --- a/core/src/apps/bitcoin/get_address.py +++ b/core/src/apps/bitcoin/get_address.py @@ -112,13 +112,38 @@ async def get_address( xpubs=_get_xpubs(coin, multisig_xpub_magic, pubnodes), ) else: - title = address_n_to_str(address_n) + derivation_path = address_n_to_str(address_n) await show_address( ctx, address_short, address_qr=address, case_sensitive=address_case_sensitive, - title=title, + derivation_path=derivation_path, + account=_path_to_account(derivation_path, coin.coin_shortcut), ) return Address(address=address, mac=mac) + + +def _path_to_account(path: str, coin_shortcut: str) -> str: + """Transforms a BIP-32 path to a human-readable account name. + + Examples: (m/44'/0'/0/0/0', BTC) -> BTC Legacy #1 + (m/84'/0'/3/0/0', BTC) -> BTC Segwit #4 + """ + path = path.lstrip("m/") + purpose = path.split("/")[0].rstrip("'hH") + + try: + account_num = int(path.split("/")[2].rstrip("'hH")) + except (IndexError, ValueError): + account_num = 0 + + purpose_str = { + "44": "Legacy", + "49": "L.Segwit", + "84": "Segwit", + "86": "Taproot", + }.get(purpose, "Unknown") + + return f"{coin_shortcut} {purpose_str} #{account_num + 1}" diff --git a/core/src/apps/cardano/layout.py b/core/src/apps/cardano/layout.py index 754b436745..b7d0e9c9fd 100644 --- a/core/src/apps/cardano/layout.py +++ b/core/src/apps/cardano/layout.py @@ -924,7 +924,7 @@ async def show_cardano_address( network_name = protocol_magics.to_ui_string(protocol_magic) title = f"{ADDRESS_TYPE_NAMES[address_parameters.address_type]} address" - address_extra = None + derivation_path = None title_qr = title if address_parameters.address_type in ( CAT.BYRON, @@ -935,10 +935,10 @@ async def show_cardano_address( CAT.REWARD, ): if address_parameters.address_n: - address_extra = address_n_to_str(address_parameters.address_n) + derivation_path = address_n_to_str(address_parameters.address_n) title_qr = address_n_to_str(address_parameters.address_n) elif address_parameters.address_n_staking: - address_extra = address_n_to_str(address_parameters.address_n_staking) + derivation_path = address_n_to_str(address_parameters.address_n_staking) title_qr = address_n_to_str(address_parameters.address_n_staking) await layouts.show_address( @@ -946,6 +946,8 @@ async def show_cardano_address( address, title=title, network=network_name, - address_extra=address_extra, + address_extra=derivation_path, title_qr=title_qr, + derivation_path=derivation_path, + account="Cardano", ) diff --git a/core/src/apps/ethereum/get_address.py b/core/src/apps/ethereum/get_address.py index f33a5390a7..d4a0efe5a1 100644 --- a/core/src/apps/ethereum/get_address.py +++ b/core/src/apps/ethereum/get_address.py @@ -32,7 +32,12 @@ async def get_address( address = address_from_bytes(node.ethereum_pubkeyhash(), network) if msg.show_display: - title = paths.address_n_to_str(address_n) - await show_address(ctx, address, title=title) + derivation_path = paths.address_n_to_str(address_n) + await show_address( + ctx, + address, + derivation_path=derivation_path, + account="Ethereum", + ) return EthereumAddress(address=address) diff --git a/core/src/apps/monero/get_address.py b/core/src/apps/monero/get_address.py index 143d4c5087..8e57852a42 100644 --- a/core/src/apps/monero/get_address.py +++ b/core/src/apps/monero/get_address.py @@ -68,12 +68,13 @@ async def get_address( ) if msg.show_display: - title = paths.address_n_to_str(msg.address_n) + derivation_path = paths.address_n_to_str(msg.address_n) await show_address( ctx, addr, address_qr="monero:" + addr, - title=title, + derivation_path=derivation_path, + account="Monero", ) return MoneroAddress(address=addr.encode()) diff --git a/core/src/apps/nem/get_address.py b/core/src/apps/nem/get_address.py index 1d7ecfec6b..b673d5ad99 100644 --- a/core/src/apps/nem/get_address.py +++ b/core/src/apps/nem/get_address.py @@ -30,13 +30,14 @@ async def get_address( address = node.nem_address(network) if msg.show_display: - title = address_n_to_str(address_n) + derivation_path = address_n_to_str(address_n) await show_address( ctx, address, case_sensitive=False, - title=title, network=get_network_str(network), + derivation_path=derivation_path, + account="NEM", ) return NEMAddress(address=address) diff --git a/core/src/apps/ripple/get_address.py b/core/src/apps/ripple/get_address.py index b1f9e1d284..43a66043a0 100644 --- a/core/src/apps/ripple/get_address.py +++ b/core/src/apps/ripple/get_address.py @@ -25,7 +25,12 @@ async def get_address( address = address_from_public_key(pubkey) if msg.show_display: - title = paths.address_n_to_str(msg.address_n) - await show_address(ctx, address, title=title) + derivation_path = paths.address_n_to_str(msg.address_n) + await show_address( + ctx, + address, + derivation_path=derivation_path, + account="Ripple", + ) return RippleAddress(address=address) diff --git a/core/src/apps/stellar/get_address.py b/core/src/apps/stellar/get_address.py index 2ead7530cd..b5fb49c11f 100644 --- a/core/src/apps/stellar/get_address.py +++ b/core/src/apps/stellar/get_address.py @@ -24,7 +24,13 @@ async def get_address( address = helpers.address_from_public_key(pubkey) if msg.show_display: - title = paths.address_n_to_str(msg.address_n) - await show_address(ctx, address, case_sensitive=False, title=title) + derivation_path = paths.address_n_to_str(msg.address_n) + await show_address( + ctx, + address, + case_sensitive=False, + derivation_path=derivation_path, + account="Stellar", + ) return StellarAddress(address=address) diff --git a/core/src/apps/tezos/get_address.py b/core/src/apps/tezos/get_address.py index d5d146736d..3589329968 100644 --- a/core/src/apps/tezos/get_address.py +++ b/core/src/apps/tezos/get_address.py @@ -29,7 +29,12 @@ async def get_address( address = helpers.base58_encode_check(pkh, helpers.TEZOS_ED25519_ADDRESS_PREFIX) if msg.show_display: - title = paths.address_n_to_str(msg.address_n) - await show_address(ctx, address, title=title) + derivation_path = paths.address_n_to_str(msg.address_n) + await show_address( + ctx, + address, + derivation_path=derivation_path, + account="Tezos", + ) return TezosAddress(address=address) diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index 5e44ec410c..80b54410f5 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -655,69 +655,39 @@ async def show_address( *, case_sensitive: bool = True, address_qr: str | None = None, - title: str = "Confirm address", + title: str | None = None, network: str | None = None, multisig_index: int | None = None, xpubs: Sequence[str] = (), address_extra: str | None = None, title_qr: str | None = None, + derivation_path: str | None = None, + account: str | None = None, ) -> None: - is_multisig = len(xpubs) > 0 - # TODO: replace with confirm_blob - data = address - if network: - data += f"\n\n{network}" - if address_extra: - data += f"\n\n{address_extra}" - while True: - result = await interact( + account = account or "Unknown" + derivation_path = derivation_path or "Unknown" + title = title or "Receive address" + + await raise_if_cancelled( + interact( ctx, RustLayout( - trezorui2.confirm_action( + trezorui2.show_receive_address( title=title.upper(), - action=data, - description=None, - verb="CONFIRM", - verb_cancel="QR", - reverse=False, - hold=False, + address=address, + address_qr=address if address_qr is None else address_qr, + account=account, + derivation_path=derivation_path, + case_sensitive=case_sensitive, ) ), "show_address", ButtonRequestType.Address, ) - if result is trezorui2.CONFIRMED: - break + ) - result = await interact( - ctx, - RustLayout( - trezorui2.show_qr( - address=address if address_qr is None else address_qr, - case_sensitive=case_sensitive, - title=title.upper() if title_qr is None else title_qr.upper(), - verb_cancel="XPUBs" if is_multisig else "ADDRESS", - ) - ), - "show_qr", - ButtonRequestType.Address, - ) - if result is trezorui2.CONFIRMED: - break - - if is_multisig: - for i, xpub in enumerate(xpubs): - cancel = "NEXT" if i < len(xpubs) - 1 else "ADDRESS" - title_xpub = f"XPUB #{i + 1}" - title_xpub += " (yours)" if i == multisig_index else " (cosigner)" - result = await interact( - ctx, - _show_xpub(xpub, title=title_xpub, cancel=cancel), - "show_xpub", - ButtonRequestType.PublicKey, - ) - if result is trezorui2.CONFIRMED: - return + # TODO: support showing multisig xpubs? + # TODO: send button requests in the flow above? def show_pubkey( diff --git a/core/src/trezor/ui/layouts/tt_v2/__init__.py b/core/src/trezor/ui/layouts/tt_v2/__init__.py index 667793f38d..4d881223be 100644 --- a/core/src/trezor/ui/layouts/tt_v2/__init__.py +++ b/core/src/trezor/ui/layouts/tt_v2/__init__.py @@ -360,20 +360,29 @@ async def show_address( *, address_qr: str | None = None, case_sensitive: bool = True, - title: str = "Confirm address", + title: str | None = None, network: str | None = None, multisig_index: int | None = None, xpubs: Sequence[str] = (), address_extra: str | None = None, title_qr: str | None = None, + derivation_path: str | None = None, + account: str | None = None, ) -> None: + # TODO: could show the derivation path and account, the same was as TR is_multisig = len(xpubs) > 0 + if title: + title = title.upper() + elif derivation_path: + title = derivation_path.upper() + else: + title = "CONFIRM ADDRESS" while True: result = await interact( ctx, RustLayout( trezorui2.confirm_blob( - title=title.upper(), + title=title, data=address, description=network or "", extra=address_extra or "",