From a9ed3f31fc6c5b7b71116b77a4327c9ff895e2f8 Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Thu, 3 Apr 2025 16:20:46 +0300 Subject: [PATCH] feat(core): send address & public key response before showing `StatusScreen` --- core/.changelog.d/3666.fixed | 1 + core/embed/rust/librust_qstr.h | 1 - .../rust/src/ui/api/firmware_micropython.rs | 3 --- .../rust/src/ui/layout_bolt/ui_firmware.rs | 1 - .../rust/src/ui/layout_caesar/ui_firmware.rs | 1 - .../layout_delizia/component/status_screen.rs | 4 +-- .../src/ui/layout_delizia/flow/get_address.rs | 16 ++--------- .../rust/src/ui/layout_delizia/ui_firmware.rs | 23 +++++++++++----- core/embed/rust/src/ui/ui_firmware.rs | 1 - core/mocks/generated/trezorui_api.pyi | 1 - core/src/apps/binance/get_address.py | 16 ++++++++--- core/src/apps/binance/get_public_key.py | 13 ++++++--- core/src/apps/bitcoin/get_address.py | 15 +++++++++-- core/src/apps/bitcoin/get_public_key.py | 27 ++++++++++++------- core/src/apps/cardano/get_address.py | 13 +++++++-- core/src/apps/cardano/get_public_key.py | 9 ++++++- core/src/apps/common/keychain.py | 2 +- core/src/apps/eos/get_public_key.py | 15 +++++++++-- core/src/apps/ethereum/get_address.py | 13 ++++++--- core/src/apps/ethereum/get_public_key.py | 20 +++++++++----- .../apps/management/reset_device/__init__.py | 1 + core/src/apps/monero/get_address.py | 16 ++++++++--- core/src/apps/nem/get_address.py | 13 ++++++--- core/src/apps/ripple/get_address.py | 15 ++++++++--- core/src/apps/solana/get_address.py | 13 ++++++--- core/src/apps/solana/get_public_key.py | 13 ++++++--- core/src/apps/stellar/get_address.py | 15 ++++++++--- core/src/apps/tezos/get_address.py | 13 ++++++--- core/src/apps/tezos/get_public_key.py | 15 ++++++++--- core/src/trezor/ui/layouts/bolt/__init__.py | 4 +++ core/src/trezor/ui/layouts/caesar/__init__.py | 4 +++ .../src/trezor/ui/layouts/delizia/__init__.py | 24 ++++++++++------- core/src/trezor/wire/context.py | 10 +++++++ 33 files changed, 254 insertions(+), 97 deletions(-) create mode 100644 core/.changelog.d/3666.fixed diff --git a/core/.changelog.d/3666.fixed b/core/.changelog.d/3666.fixed new file mode 100644 index 0000000000..ae3f743ed1 --- /dev/null +++ b/core/.changelog.d/3666.fixed @@ -0,0 +1 @@ +[T3T1] Show confirmation layout after sending response to host. diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index fe57b057d1..2b0975ef18 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -689,7 +689,6 @@ static void _librust_qstrs(void) { MP_QSTR_time_ms; MP_QSTR_timer; MP_QSTR_title; - MP_QSTR_title_success; MP_QSTR_total_fee_new; MP_QSTR_total_len; MP_QSTR_touch_event; diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs index 0f1a3b901b..0390e5e231 100644 --- a/core/embed/rust/src/ui/api/firmware_micropython.rs +++ b/core/embed/rust/src/ui/api/firmware_micropython.rs @@ -532,7 +532,6 @@ extern "C" fn new_flow_get_address(n_args: usize, args: *const Obj, kwargs: *mut let account: Option = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?; let path: Option = kwargs.get(Qstr::MP_QSTR_path)?.try_into_option()?; let xpubs: Obj = kwargs.get(Qstr::MP_QSTR_xpubs)?; - let title_success: TString = kwargs.get(Qstr::MP_QSTR_title_success)?.try_into()?; let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?; let br_name: TString = kwargs.get(Qstr::MP_QSTR_br_name)?.try_into()?; @@ -547,7 +546,6 @@ extern "C" fn new_flow_get_address(n_args: usize, args: *const Obj, kwargs: *mut account, path, xpubs, - title_success, br_code, br_name, )?; @@ -1401,7 +1399,6 @@ pub static mp_module_trezorui_api: Module = obj_module! { /// account: str | None, /// path: str | None, /// xpubs: list[tuple[str, str]], - /// title_success: str, /// br_code: ButtonRequestType, /// br_name: str, /// ) -> LayoutObj[UiResult]: diff --git a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs index de11a6c7c3..3616f2ad75 100644 --- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs @@ -599,7 +599,6 @@ impl FirmwareUI for UIBolt { _account: Option>, _path: Option>, _xpubs: Obj, - _title_success: TString<'static>, _br_code: u16, _br_name: TString<'static>, ) -> Result { diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs index dc1677f389..9839895147 100644 --- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs @@ -729,7 +729,6 @@ impl FirmwareUI for UICaesar { _account: Option>, _path: Option>, _xpubs: Obj, - _title_success: TString<'static>, _br_code: u16, _br_name: TString<'static>, ) -> Result { diff --git a/core/embed/rust/src/ui/layout_delizia/component/status_screen.rs b/core/embed/rust/src/ui/layout_delizia/component/status_screen.rs index 9016a74a0a..131763ea0a 100644 --- a/core/embed/rust/src/ui/layout_delizia/component/status_screen.rs +++ b/core/embed/rust/src/ui/layout_delizia/component/status_screen.rs @@ -172,12 +172,12 @@ impl StatusScreen { ) } - pub fn new_success_timeout(msg: TString<'static>) -> Self { + pub fn new_success_timeout(msg: TString<'static>, time_ms: u32) -> Self { Self::new( theme::ICON_SIMPLE_CHECKMARK30, theme::GREEN_LIME, theme::GREEN_LIGHT, - DismissType::Timeout(Timeout::new(TIMEOUT_MS)), + DismissType::Timeout(Timeout::new(time_ms)), msg, ) } diff --git a/core/embed/rust/src/ui/layout_delizia/flow/get_address.rs b/core/embed/rust/src/ui/layout_delizia/flow/get_address.rs index db03d2541f..14b535ec3b 100644 --- a/core/embed/rust/src/ui/layout_delizia/flow/get_address.rs +++ b/core/embed/rust/src/ui/layout_delizia/flow/get_address.rs @@ -20,7 +20,7 @@ use crate::{ }; use super::super::{ - component::{AddressDetails, Frame, PromptScreen, StatusScreen, SwipeContent, VerticalMenu}, + component::{AddressDetails, Frame, PromptScreen, SwipeContent, VerticalMenu}, theme, }; @@ -30,7 +30,6 @@ const QR_BORDER: i16 = 4; pub enum GetAddress { Address, Tap, - Confirmed, Menu, QrCode, AccountInfo, @@ -64,9 +63,8 @@ impl FlowController for GetAddress { fn handle_event(&'static self, msg: FlowMsg) -> Decision { match (self, msg) { (Self::Address, FlowMsg::Info) => Self::Menu.goto(), - (Self::Tap, FlowMsg::Confirmed) => Self::Confirmed.swipe_up(), + (Self::Tap, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed), (Self::Tap, FlowMsg::Info) => Self::Menu.swipe_left(), - (Self::Confirmed, _) => self.return_msg(FlowMsg::Confirmed), (Self::Menu, FlowMsg::Choice(0)) => Self::QrCode.swipe_left(), (Self::Menu, FlowMsg::Choice(1)) => Self::AccountInfo.swipe_left(), (Self::Menu, FlowMsg::Choice(2)) => Self::Cancel.swipe_left(), @@ -93,7 +91,6 @@ pub fn new_get_address( account: Option>, path: Option>, xpubs: Obj, // TODO: get rid of Obj - title_success: TString<'static>, br_code: u16, br_name: TString<'static>, ) -> Result { @@ -131,14 +128,6 @@ pub fn new_get_address( .with_swipe(Direction::Left, SwipeSettings::default()) .map(super::util::map_to_confirm); - let content_confirmed = Frame::left_aligned( - TR::words__title_success.into(), - StatusScreen::new_success_timeout(title_success), - ) - .with_footer(TR::instructions__continue_in_app.into(), None) - .with_result_icon(theme::ICON_BULLET_CHECKMARK, theme::GREEN_LIGHT) - .map(|_| Some(FlowMsg::Confirmed)); - // Menu let content_menu = Frame::left_aligned( "".into(), @@ -200,7 +189,6 @@ pub fn new_get_address( let mut res = SwipeFlow::new(&GetAddress::Address)?; res.add_page(&GetAddress::Address, content_address)? .add_page(&GetAddress::Tap, content_tap)? - .add_page(&GetAddress::Confirmed, content_confirmed)? .add_page(&GetAddress::Menu, content_menu)? .add_page(&GetAddress::QrCode, content_qr)? .add_page(&GetAddress::AccountInfo, content_account)? diff --git a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs index b0baf63e71..2b02f3f47d 100644 --- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs @@ -9,6 +9,7 @@ use crate::{ ui::{ component::{ connect::Connect, + swipe_detect::SwipeSettings, text::{ op::OpTextLayout, paragraphs::{ @@ -19,7 +20,7 @@ use crate::{ }, Border, CachedJpeg, ComponentExt, Empty, FormattedText, Never, Timeout, }, - geometry::{self, Offset}, + geometry::{self, Direction, Offset}, layout::{ obj::{LayoutMaybeTrace, LayoutObj, RootComponent}, util::{PropsList, RecoveryType}, @@ -625,7 +626,6 @@ impl FirmwareUI for UIDelizia { account: Option>, path: Option>, xpubs: Obj, - title_success: TString<'static>, br_code: u16, br_name: TString<'static>, ) -> Result { @@ -640,7 +640,6 @@ impl FirmwareUI for UIDelizia { account, path, xpubs, - title_success, br_code, br_name, )?; @@ -1046,24 +1045,34 @@ impl FirmwareUI for UIDelizia { fn show_success( title: TString<'static>, - _button: TString<'static>, + button: TString<'static>, description: TString<'static>, _allow_cancel: bool, - _time_ms: u32, + time_ms: u32, ) -> Result, Error> { + let instruction = if button.is_empty() { + TR::instructions__tap_to_continue.into() + } else { + button + }; // description used in the Footer let description = if description.is_empty() { None } else { Some(description) }; - let content = StatusScreen::new_success(title); + let content = if time_ms > 0 { + StatusScreen::new_success_timeout(title, time_ms) + } else { + StatusScreen::new_success(title) + }; let layout = LayoutObj::new(SwipeUpScreen::new( Frame::left_aligned( TR::words__title_success.into(), SwipeContent::new(content).with_no_attach_anim(), ) - .with_swipeup_footer(description) + .with_footer(instruction, description) + .with_swipe(Direction::Up, SwipeSettings::default()) .with_result_icon(theme::ICON_BULLET_CHECKMARK, theme::GREEN_LIGHT), ))?; Ok(layout) diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs index 112e998e4b..fa659fb5ec 100644 --- a/core/embed/rust/src/ui/ui_firmware.rs +++ b/core/embed/rust/src/ui/ui_firmware.rs @@ -200,7 +200,6 @@ pub trait FirmwareUI { account: Option>, path: Option>, xpubs: Obj, // TODO: replace Obj - title_success: TString<'static>, br_code: u16, br_name: TString<'static>, ) -> Result; diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi index a70518b4b0..3da747558b 100644 --- a/core/mocks/generated/trezorui_api.pyi +++ b/core/mocks/generated/trezorui_api.pyi @@ -360,7 +360,6 @@ def flow_get_address( account: str | None, path: str | None, xpubs: list[tuple[str, str]], - title_success: str, br_code: ButtonRequestType, br_name: str, ) -> LayoutObj[UiResult]: diff --git a/core/src/apps/binance/get_address.py b/core/src/apps/binance/get_address.py index 3e017a7d3d..4a9fb0bb5d 100644 --- a/core/src/apps/binance/get_address.py +++ b/core/src/apps/binance/get_address.py @@ -9,9 +9,12 @@ if TYPE_CHECKING: @auto_keychain(__name__) -async def get_address(msg: BinanceGetAddress, keychain: Keychain) -> BinanceAddress: +async def get_address( + msg: BinanceGetAddress, keychain: Keychain +) -> BinanceAddress | None: from trezor.messages import BinanceAddress - from trezor.ui.layouts import show_address + from trezor.ui.layouts import show_address, show_continue_in_app + from trezor.wire import context from apps.common import paths @@ -25,7 +28,11 @@ async def get_address(msg: BinanceGetAddress, keychain: Keychain) -> BinanceAddr node = keychain.derive(address_n) pubkey = node.public_key() address = address_from_public_key(pubkey, HRP) + response = BinanceAddress(address=address) + if msg.show_display: + from trezor import TR + from . import PATTERN, SLIP44_ID await show_address( @@ -34,5 +41,8 @@ async def get_address(msg: BinanceGetAddress, keychain: Keychain) -> BinanceAddr account=paths.get_account_name("BNB", address_n, PATTERN, SLIP44_ID), chunkify=bool(msg.chunkify), ) + await context.write(response) + await show_continue_in_app(TR.address__confirmed) + return None - return BinanceAddress(address=address) + return response diff --git a/core/src/apps/binance/get_public_key.py b/core/src/apps/binance/get_public_key.py index 89f3b13dfc..a0c245d86a 100644 --- a/core/src/apps/binance/get_public_key.py +++ b/core/src/apps/binance/get_public_key.py @@ -11,19 +11,23 @@ if TYPE_CHECKING: @auto_keychain(__name__) async def get_public_key( msg: BinanceGetPublicKey, keychain: Keychain -) -> BinancePublicKey: +) -> BinancePublicKey | None: from ubinascii import hexlify from trezor.messages import BinancePublicKey - from trezor.ui.layouts import show_pubkey + from trezor.ui.layouts import show_continue_in_app, show_pubkey + from trezor.wire import context from apps.common import paths await paths.validate_path(keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = node.public_key() + response = BinancePublicKey(public_key=pubkey) if msg.show_display: + from trezor import TR + from . import PATTERN, SLIP44_ID path = paths.address_n_to_str(msg.address_n) @@ -32,5 +36,8 @@ async def get_public_key( account=paths.get_account_name("BNB", msg.address_n, PATTERN, SLIP44_ID), path=path, ) + await context.write(response) + await show_continue_in_app(TR.address__public_key_confirmed) + return None - return BinancePublicKey(public_key=pubkey) + return response diff --git a/core/src/apps/bitcoin/get_address.py b/core/src/apps/bitcoin/get_address.py index e3a053cf80..467544f0bb 100644 --- a/core/src/apps/bitcoin/get_address.py +++ b/core/src/apps/bitcoin/get_address.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING +from trezor import TR from trezor.enums import MultisigPubkeysOrder from apps.common import safety_checks @@ -35,14 +36,18 @@ def _get_xpubs( @with_keychain -async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Address: +async def get_address( + msg: GetAddress, keychain: Keychain, coin: CoinInfo +) -> Address | None: from trezor.enums import InputScriptType from trezor.messages import Address from trezor.ui.layouts import ( confirm_multisig_different_paths_warning, confirm_multisig_warning, show_address, + show_continue_in_app, ) + from trezor.wire import context from apps.common.address_mac import get_address_mac from apps.common.paths import address_n_to_str, validate_path @@ -103,6 +108,8 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad ): mac = get_address_mac(address, coin.slip44, keychain) + response = Address(address=address, mac=mac) + if msg.show_display: path = address_n_to_str(address_n) if multisig: @@ -156,4 +163,8 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad chunkify=bool(msg.chunkify), ) - return Address(address=address, mac=mac) + await context.write(response) + await show_continue_in_app(TR.address__confirmed) + return None + else: + return response diff --git a/core/src/apps/bitcoin/get_public_key.py b/core/src/apps/bitcoin/get_public_key.py index 272aac54db..b75db240ae 100644 --- a/core/src/apps/bitcoin/get_public_key.py +++ b/core/src/apps/bitcoin/get_public_key.py @@ -13,10 +13,11 @@ async def get_public_key( msg: GetPublicKey, auth_msg: MessageType | None = None, keychain: Keychain | None = None, -) -> PublicKey: +) -> PublicKey | None: from trezor import TR, wire from trezor.enums import InputScriptType from trezor.messages import HDNodeType, PublicKey, UnlockPath + from trezor.wire import context from apps.common import coininfo, paths from apps.common.keychain import FORBIDDEN_KEY_PATH, get_keychain @@ -86,9 +87,19 @@ async def get_public_key( descriptor = _xpub_descriptor( node, xpub_magic, address_n, script_type, keychain.root_fingerprint() ) + response = PublicKey( + node=node_type, + xpub=node_xpub, + root_fingerprint=keychain.root_fingerprint(), + descriptor=descriptor, + ) if msg.show_display: - from trezor.ui.layouts import confirm_path_warning, show_pubkey + from trezor.ui.layouts import ( + confirm_path_warning, + show_continue_in_app, + show_pubkey, + ) from apps.common.paths import address_n_to_str @@ -116,13 +127,11 @@ async def get_public_key( mismatch_title=TR.addr_mismatch__xpub_mismatch, br_name="show_xpub", ) - - return PublicKey( - node=node_type, - xpub=node_xpub, - root_fingerprint=keychain.root_fingerprint(), - descriptor=descriptor, - ) + await context.write(response) + await show_continue_in_app(TR.address__public_key_confirmed) + return None + else: + return response def _xpub_descriptor( diff --git a/core/src/apps/cardano/get_address.py b/core/src/apps/cardano/get_address.py index e1685aff0d..8d3ae3579b 100644 --- a/core/src/apps/cardano/get_address.py +++ b/core/src/apps/cardano/get_address.py @@ -9,9 +9,11 @@ if TYPE_CHECKING: @seed.with_keychain async def get_address( msg: CardanoGetAddress, keychain: seed.Keychain -) -> CardanoAddress: +) -> CardanoAddress | None: from trezor import log, wire from trezor.messages import CardanoAddress + from trezor.ui.layouts import show_continue_in_app + from trezor.wire import context from . import addresses from .helpers.credential import Credential, should_show_credentials @@ -32,7 +34,11 @@ async def get_address( log.exception(__name__, e) raise wire.ProcessError("Deriving address failed") + response = CardanoAddress(address=address) + if msg.show_display: + from trezor import TR + # _display_address if should_show_credentials(address_parameters): await show_credentials( @@ -42,5 +48,8 @@ async def get_address( await show_cardano_address( address_parameters, address, msg.protocol_magic, chunkify=bool(msg.chunkify) ) + await context.write(response) + await show_continue_in_app(TR.address__confirmed) + return None - return CardanoAddress(address=address) + return response diff --git a/core/src/apps/cardano/get_public_key.py b/core/src/apps/cardano/get_public_key.py index 8b2856e87a..0028ff470f 100644 --- a/core/src/apps/cardano/get_public_key.py +++ b/core/src/apps/cardano/get_public_key.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: @seed.with_keychain async def get_public_key( msg: CardanoGetPublicKey, keychain: seed.Keychain -) -> CardanoPublicKey: +) -> CardanoPublicKey | None: from trezor import log, wire from trezor.ui.layouts import show_pubkey @@ -37,10 +37,17 @@ async def get_public_key( raise wire.ProcessError("Deriving public key failed") if msg.show_display: + from trezor.ui.layouts import show_continue_in_app + from trezor.wire import context + from apps.common.paths import address_n_to_str path = address_n_to_str(address_n) await show_pubkey(key.xpub, TR.address__public_key, path=path) + await context.write(key) + await show_continue_in_app(TR.address__public_key_confirmed) + return None + return key diff --git a/core/src/apps/common/keychain.py b/core/src/apps/common/keychain.py index 16913d1529..b79d2ed5cc 100644 --- a/core/src/apps/common/keychain.py +++ b/core/src/apps/common/keychain.py @@ -25,7 +25,7 @@ if TYPE_CHECKING: NodeType = TypeVar("NodeType", bound=NodeProtocol) MsgIn = TypeVar("MsgIn", bound=MessageType) - MsgOut = TypeVar("MsgOut", bound=MessageType) + MsgOut = TypeVar("MsgOut", bound=MessageType | None) Handler = Callable[[MsgIn], Awaitable[MsgOut]] HandlerWithKeychain = Callable[[MsgIn, "Keychain"], Awaitable[MsgOut]] diff --git a/core/src/apps/eos/get_public_key.py b/core/src/apps/eos/get_public_key.py index 19678f0551..e7c7a7ecc6 100644 --- a/core/src/apps/eos/get_public_key.py +++ b/core/src/apps/eos/get_public_key.py @@ -9,9 +9,13 @@ if TYPE_CHECKING: @auto_keychain(__name__) -async def get_public_key(msg: EosGetPublicKey, keychain: Keychain) -> EosPublicKey: +async def get_public_key( + msg: EosGetPublicKey, keychain: Keychain +) -> EosPublicKey | None: from trezor.crypto.curve import secp256k1 from trezor.messages import EosPublicKey + from trezor.ui.layouts import show_continue_in_app + from trezor.wire import context from apps.common import paths @@ -24,11 +28,18 @@ async def get_public_key(msg: EosGetPublicKey, keychain: Keychain) -> EosPublicK public_key = secp256k1.publickey(node.private_key(), True) wif = public_key_to_wif(public_key) + response = EosPublicKey(wif_public_key=wif, raw_public_key=public_key) if msg.show_display: + from trezor import TR + from . import PATTERN, SLIP44_ID path = paths.address_n_to_str(msg.address_n) account = paths.get_account_name("EOS", msg.address_n, PATTERN, SLIP44_ID) await require_get_public_key(wif, path, account) - return EosPublicKey(wif_public_key=wif, raw_public_key=public_key) + await context.write(response) + await show_continue_in_app(TR.address__public_key_confirmed) + return None + + return response diff --git a/core/src/apps/ethereum/get_address.py b/core/src/apps/ethereum/get_address.py index 92ab88bd08..75004e6290 100644 --- a/core/src/apps/ethereum/get_address.py +++ b/core/src/apps/ethereum/get_address.py @@ -15,9 +15,10 @@ async def get_address( msg: EthereumGetAddress, keychain: Keychain, defs: Definitions, -) -> EthereumAddress: +) -> EthereumAddress | None: from trezor.messages import EthereumAddress - from trezor.ui.layouts import show_address + from trezor.ui.layouts import show_address, show_continue_in_app + from trezor.wire import context from apps.common import paths @@ -30,8 +31,11 @@ async def get_address( node = keychain.derive(address_n) address = address_from_bytes(node.ethereum_pubkeyhash(), defs.network) + response = EthereumAddress(address=address) if msg.show_display: + from trezor import TR + slip44_id = address_n[1] # it depends on the network (ETH vs ETC...) await show_address( address, @@ -41,5 +45,8 @@ async def get_address( ), chunkify=bool(msg.chunkify), ) + await context.write(response) + await show_continue_in_app(TR.address__confirmed) + return None - return EthereumAddress(address=address) + return response diff --git a/core/src/apps/ethereum/get_public_key.py b/core/src/apps/ethereum/get_public_key.py index c39061d3ed..256df11b79 100644 --- a/core/src/apps/ethereum/get_public_key.py +++ b/core/src/apps/ethereum/get_public_key.py @@ -4,19 +4,27 @@ if TYPE_CHECKING: from trezor.messages import EthereumGetPublicKey, EthereumPublicKey -async def get_public_key(msg: EthereumGetPublicKey) -> EthereumPublicKey: +async def get_public_key(msg: EthereumGetPublicKey) -> EthereumPublicKey | None: from ubinascii import hexlify from trezor.messages import EthereumPublicKey, GetPublicKey - from trezor.ui.layouts import show_pubkey + from trezor.ui.layouts import show_continue_in_app, show_pubkey + from trezor.wire import context from apps.bitcoin import get_public_key as bitcoin_get_public_key # we use the Bitcoin format for Ethereum xpubs - btc_pubkey_msg = GetPublicKey(address_n=msg.address_n) - resp = await bitcoin_get_public_key.get_public_key(btc_pubkey_msg) + btc_pubkey_msg = GetPublicKey(address_n=msg.address_n, show_display=False) + btc_resp = await bitcoin_get_public_key.get_public_key(btc_pubkey_msg) + assert btc_resp is not None + response = EthereumPublicKey(node=btc_resp.node, xpub=btc_resp.xpub) if msg.show_display: - await show_pubkey(hexlify(resp.node.public_key).decode()) + from trezor import TR - return EthereumPublicKey(node=resp.node, xpub=resp.xpub) + await show_pubkey(hexlify(response.node.public_key).decode()) + await context.write(response) + await show_continue_in_app(TR.address__public_key_confirmed) + return None + + return response diff --git a/core/src/apps/management/reset_device/__init__.py b/core/src/apps/management/reset_device/__init__.py index 4634f47a5a..8ce07db4e2 100644 --- a/core/src/apps/management/reset_device/__init__.py +++ b/core/src/apps/management/reset_device/__init__.py @@ -171,6 +171,7 @@ async def _entropy_check(secret: bytes) -> bool: curve_name = req.ecdsa_curve_name or coininfo.by_name(req.coin_name).curve_name keychain = Keychain(seed, curve_name, [paths.AlwaysMatchingSchema]) msg = await get_public_key(req, keychain=keychain) + assert msg is not None async def _backup_bip39(mnemonic: str) -> None: diff --git a/core/src/apps/monero/get_address.py b/core/src/apps/monero/get_address.py index 3653f0af11..027f016064 100644 --- a/core/src/apps/monero/get_address.py +++ b/core/src/apps/monero/get_address.py @@ -9,10 +9,13 @@ if TYPE_CHECKING: @auto_keychain(__name__) -async def get_address(msg: MoneroGetAddress, keychain: Keychain) -> MoneroAddress: +async def get_address( + msg: MoneroGetAddress, keychain: Keychain +) -> MoneroAddress | None: from trezor import wire from trezor.messages import MoneroAddress - from trezor.ui.layouts import show_address + from trezor.ui.layouts import show_address, show_continue_in_app + from trezor.wire import context from apps.common import paths from apps.monero import misc @@ -65,7 +68,11 @@ async def get_address(msg: MoneroGetAddress, keychain: Keychain) -> MoneroAddres crypto_helpers.encodepoint(pub_view), ) + response = MoneroAddress(address=addr.encode()) + if msg.show_display: + from trezor import TR + from . import PATTERN, SLIP44_ID await show_address( @@ -75,5 +82,8 @@ async def get_address(msg: MoneroGetAddress, keychain: Keychain) -> MoneroAddres account=paths.get_account_name("XMR", msg.address_n, PATTERN, SLIP44_ID), chunkify=bool(msg.chunkify), ) + await context.write(response) + await show_continue_in_app(TR.address__confirmed) + return None - return MoneroAddress(address=addr.encode()) + return response diff --git a/core/src/apps/nem/get_address.py b/core/src/apps/nem/get_address.py index c09299ae73..3a1ed577ab 100644 --- a/core/src/apps/nem/get_address.py +++ b/core/src/apps/nem/get_address.py @@ -11,9 +11,10 @@ if TYPE_CHECKING: @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) -async def get_address(msg: NEMGetAddress, keychain: Keychain) -> NEMAddress: +async def get_address(msg: NEMGetAddress, keychain: Keychain) -> NEMAddress | None: from trezor.messages import NEMAddress - from trezor.ui.layouts import show_address + from trezor.ui.layouts import show_address, show_continue_in_app + from trezor.wire import context from apps.common import paths @@ -28,8 +29,11 @@ async def get_address(msg: NEMGetAddress, keychain: Keychain) -> NEMAddress: node = keychain.derive(address_n) address = node.nem_address(network) + response = NEMAddress(address=address) if msg.show_display: + from trezor import TR + from . import PATTERNS, SLIP44_ID await show_address( @@ -40,5 +44,8 @@ async def get_address(msg: NEMGetAddress, keychain: Keychain) -> NEMAddress: network=get_network_str(network), chunkify=bool(msg.chunkify), ) + await context.write(response) + await show_continue_in_app(TR.address__confirmed) + return None - return NEMAddress(address=address) + return response diff --git a/core/src/apps/ripple/get_address.py b/core/src/apps/ripple/get_address.py index 1450669817..904075f790 100644 --- a/core/src/apps/ripple/get_address.py +++ b/core/src/apps/ripple/get_address.py @@ -9,10 +9,13 @@ if TYPE_CHECKING: @auto_keychain(__name__) -async def get_address(msg: RippleGetAddress, keychain: Keychain) -> RippleAddress: +async def get_address( + msg: RippleGetAddress, keychain: Keychain +) -> RippleAddress | None: # NOTE: local imports here saves 20 bytes from trezor.messages import RippleAddress - from trezor.ui.layouts import show_address + from trezor.ui.layouts import show_address, show_continue_in_app + from trezor.wire import context from apps.common import paths @@ -25,8 +28,11 @@ async def get_address(msg: RippleGetAddress, keychain: Keychain) -> RippleAddres node = keychain.derive(address_n) pubkey = node.public_key() address = address_from_public_key(pubkey) + response = RippleAddress(address=address) if msg.show_display: + from trezor import TR + from . import PATTERN, SLIP44_ID await show_address( @@ -35,5 +41,8 @@ async def get_address(msg: RippleGetAddress, keychain: Keychain) -> RippleAddres account=paths.get_account_name("XRP", msg.address_n, PATTERN, SLIP44_ID), chunkify=bool(msg.chunkify), ) + await context.write(response) + await show_continue_in_app(TR.address__confirmed) + return None - return RippleAddress(address=address) + return response diff --git a/core/src/apps/solana/get_address.py b/core/src/apps/solana/get_address.py index b543b8245d..26cf51357a 100644 --- a/core/src/apps/solana/get_address.py +++ b/core/src/apps/solana/get_address.py @@ -16,9 +16,10 @@ if TYPE_CHECKING: async def get_address( msg: SolanaGetAddress, keychain: Keychain, -) -> SolanaAddress: +) -> SolanaAddress | None: from trezor.messages import SolanaAddress - from trezor.ui.layouts import show_address + from trezor.ui.layouts import show_address, show_continue_in_app + from trezor.wire import context from apps.common import paths @@ -26,12 +27,18 @@ async def get_address( public_key = derive_public_key(keychain, msg.address_n) address = base58.encode(public_key) + response = SolanaAddress(address=address) if msg.show_display: + from trezor import TR + await show_address( address, path=paths.address_n_to_str(msg.address_n), chunkify=bool(msg.chunkify), ) + await context.write(response) + await show_continue_in_app(TR.address__confirmed) + return None - return SolanaAddress(address=address) + return response diff --git a/core/src/apps/solana/get_public_key.py b/core/src/apps/solana/get_public_key.py index 787ff1beff..0639f8f4ad 100644 --- a/core/src/apps/solana/get_public_key.py +++ b/core/src/apps/solana/get_public_key.py @@ -16,19 +16,26 @@ if TYPE_CHECKING: @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def get_public_key( msg: SolanaGetPublicKey, keychain: Keychain -) -> SolanaPublicKey: +) -> SolanaPublicKey | None: from trezor.messages import SolanaPublicKey - from trezor.ui.layouts import show_pubkey + from trezor.ui.layouts import show_continue_in_app, show_pubkey + from trezor.wire import context public_key = derive_public_key(keychain, msg.address_n) + response = SolanaPublicKey(public_key=public_key) if msg.show_display: + from trezor import TR + from apps.common.paths import address_n_to_str path = address_n_to_str(msg.address_n) await show_pubkey(base58.encode(public_key), path=path) + await context.write(response) + await show_continue_in_app(TR.address__public_key_confirmed) + return None - return SolanaPublicKey(public_key=public_key) + return response def derive_public_key(keychain: Keychain, address_n: list[int]) -> bytes: diff --git a/core/src/apps/stellar/get_address.py b/core/src/apps/stellar/get_address.py index 65b323bbf8..b3226385be 100644 --- a/core/src/apps/stellar/get_address.py +++ b/core/src/apps/stellar/get_address.py @@ -9,9 +9,12 @@ if TYPE_CHECKING: @auto_keychain(__name__) -async def get_address(msg: StellarGetAddress, keychain: Keychain) -> StellarAddress: +async def get_address( + msg: StellarGetAddress, keychain: Keychain +) -> StellarAddress | None: from trezor.messages import StellarAddress - from trezor.ui.layouts import show_address + from trezor.ui.layouts import show_address, show_continue_in_app + from trezor.wire import context from apps.common import paths, seed @@ -24,8 +27,11 @@ async def get_address(msg: StellarGetAddress, keychain: Keychain) -> StellarAddr node = keychain.derive(address_n) pubkey = seed.remove_ed25519_prefix(node.public_key()) address = helpers.address_from_public_key(pubkey) + response = StellarAddress(address=address) if msg.show_display: + from trezor import TR + from . import PATTERN, SLIP44_ID await show_address( @@ -35,5 +41,8 @@ async def get_address(msg: StellarGetAddress, keychain: Keychain) -> StellarAddr account=paths.get_account_name("XLM", msg.address_n, PATTERN, SLIP44_ID), chunkify=bool(msg.chunkify), ) + await context.write(response) + await show_continue_in_app(TR.address__confirmed) + return None - return StellarAddress(address=address) + return response diff --git a/core/src/apps/tezos/get_address.py b/core/src/apps/tezos/get_address.py index 8269fce154..35e613d4d3 100644 --- a/core/src/apps/tezos/get_address.py +++ b/core/src/apps/tezos/get_address.py @@ -11,10 +11,11 @@ if TYPE_CHECKING: @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) -async def get_address(msg: TezosGetAddress, keychain: Keychain) -> TezosAddress: +async def get_address(msg: TezosGetAddress, keychain: Keychain) -> TezosAddress | None: from trezor.crypto import hashlib from trezor.messages import TezosAddress - from trezor.ui.layouts import show_address + from trezor.ui.layouts import show_address, show_continue_in_app + from trezor.wire import context from apps.common import paths, seed @@ -29,8 +30,11 @@ async def get_address(msg: TezosGetAddress, keychain: Keychain) -> TezosAddress: pk = seed.remove_ed25519_prefix(node.public_key()) pkh = hashlib.blake2b(pk, outlen=helpers.PUBLIC_KEY_HASH_SIZE).digest() address = helpers.base58_encode_check(pkh, helpers.TEZOS_ED25519_ADDRESS_PREFIX) + response = TezosAddress(address=address) if msg.show_display: + from trezor import TR + from . import PATTERNS, SLIP44_ID await show_address( @@ -39,5 +43,8 @@ async def get_address(msg: TezosGetAddress, keychain: Keychain) -> TezosAddress: account=paths.get_account_name("XTZ", address_n, PATTERNS, SLIP44_ID), chunkify=bool(msg.chunkify), ) + await context.write(response) + await show_continue_in_app(TR.address__confirmed) + return None - return TezosAddress(address=address) + return response diff --git a/core/src/apps/tezos/get_public_key.py b/core/src/apps/tezos/get_public_key.py index aab680d71e..bc2aaa2e40 100644 --- a/core/src/apps/tezos/get_public_key.py +++ b/core/src/apps/tezos/get_public_key.py @@ -11,9 +11,12 @@ if TYPE_CHECKING: @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) -async def get_public_key(msg: TezosGetPublicKey, keychain: Keychain) -> TezosPublicKey: +async def get_public_key( + msg: TezosGetPublicKey, keychain: Keychain +) -> TezosPublicKey | None: from trezor.messages import TezosPublicKey - from trezor.ui.layouts import show_pubkey + from trezor.ui.layouts import show_continue_in_app, show_pubkey + from trezor.wire import context from apps.common import paths, seed @@ -24,12 +27,18 @@ async def get_public_key(msg: TezosGetPublicKey, keychain: Keychain) -> TezosPub node = keychain.derive(msg.address_n) pk = seed.remove_ed25519_prefix(node.public_key()) pk_prefixed = helpers.base58_encode_check(pk, helpers.TEZOS_PUBLICKEY_PREFIX) + response = TezosPublicKey(public_key=pk_prefixed) if msg.show_display: + from trezor import TR + from . import PATTERNS, SLIP44_ID account = paths.get_account_name("XTZ", msg.address_n, PATTERNS, SLIP44_ID) path = paths.address_n_to_str(msg.address_n) await show_pubkey(pk_prefixed, account=account, path=path) + await context.write(response) + await show_continue_in_app(TR.address__public_key_confirmed) + return None - return TezosPublicKey(public_key=pk_prefixed) + return response diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py index ece5f36402..24f7c7e4cb 100644 --- a/core/src/trezor/ui/layouts/bolt/__init__.py +++ b/core/src/trezor/ui/layouts/bolt/__init__.py @@ -399,6 +399,10 @@ def show_success( ) +async def show_continue_in_app(content: str) -> None: + return + + async def confirm_output( address: str, amount: str, diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py index 92fb15a19e..80abc44f55 100644 --- a/core/src/trezor/ui/layouts/caesar/__init__.py +++ b/core/src/trezor/ui/layouts/caesar/__init__.py @@ -463,6 +463,10 @@ def show_success( ) +async def show_continue_in_app(content: str) -> None: + return + + async def confirm_output( address: str, amount: str, diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py index d161a8f556..cc49d9c299 100644 --- a/core/src/trezor/ui/layouts/delizia/__init__.py +++ b/core/src/trezor/ui/layouts/delizia/__init__.py @@ -235,12 +235,6 @@ async def show_address( ) return result - title_success = ( - TR.address__public_key_confirmed - if title in ("XPUB", TR.address__public_key) - else TR.address__confirmed - ) - await raise_if_not_confirmed( trezorui_api.flow_get_address( address=address, @@ -253,7 +247,6 @@ async def show_address( account=account, path=path, xpubs=[(xpub_title(i), xpub) for i, xpub in enumerate(xpubs)], - title_success=title_success, br_name=br_name, br_code=br_code, ), @@ -349,22 +342,33 @@ def show_danger( def show_success( - br_name: str, + br_name: str | None, content: str, subheader: str | None = None, button: str | None = None, + time_ms: int = 0, ) -> Awaitable[None]: return raise_if_not_confirmed( trezorui_api.show_success( title=content, - button="", - description=subheader if subheader else "", + button=button or "", + description=subheader or "", + time_ms=time_ms, ), br_name, ButtonRequestType.Success, ) +def show_continue_in_app(content: str) -> Awaitable[None]: + return show_success( + content=content, + button=TR.instructions__continue_in_app, + time_ms=3200, + br_name=None, + ) + + async def confirm_output( address: str, amount: str | None = None, diff --git a/core/src/trezor/wire/context.py b/core/src/trezor/wire/context.py index 56df34fbc5..1d4144b979 100644 --- a/core/src/trezor/wire/context.py +++ b/core/src/trezor/wire/context.py @@ -79,6 +79,16 @@ async def call_any( return await CURRENT_CONTEXT.read(expected_wire_types) +async def write(msg: protobuf.MessageType) -> None: + """Send a message to the host. + + Raises if there is no context for this workflow.""" + if CURRENT_CONTEXT is None: + raise RuntimeError("No wire context") + + await CURRENT_CONTEXT.write(msg) + + async def maybe_call( msg: protobuf.MessageType, expected_type: type[LoadedMessageType] ) -> None: