diff --git a/core/.changelog.d/3666.fixed b/core/.changelog.d/3666.fixed new file mode 100644 index 0000000000..bba3dee515 --- /dev/null +++ b/core/.changelog.d/3666.fixed @@ -0,0 +1 @@ +[T3T1] Show confirmation layout after sending address, public key or signature to host. diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index b227ed24c2..6e371ffc73 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -757,7 +757,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 761e3f9540..97ddf23404 100644 --- a/core/embed/rust/src/ui/api/firmware_micropython.rs +++ b/core/embed/rust/src/ui/api/firmware_micropython.rs @@ -553,7 +553,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()?; @@ -568,7 +567,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, )?; @@ -1507,7 +1505,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 fc4241ce5b..31089545e6 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 c5800363eb..738436e259 100644 --- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs @@ -735,7 +735,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 6a5a7a1825..65fc8f779a 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, @@ -57,9 +56,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(), @@ -86,7 +84,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 { @@ -122,14 +119,6 @@ pub fn new_get_address( .with_swipe(Direction::Down, 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(), @@ -187,7 +176,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 545126e2e7..3fecd225e7 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}, @@ -628,7 +629,6 @@ impl FirmwareUI for UIDelizia { account: Option>, path: Option>, xpubs: Obj, - title_success: TString<'static>, br_code: u16, br_name: TString<'static>, ) -> Result { @@ -643,7 +643,6 @@ impl FirmwareUI for UIDelizia { account, path, xpubs, - title_success, br_code, br_name, )?; @@ -1088,24 +1087,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 a70efeabab..29c2b98d56 100644 --- a/core/embed/rust/src/ui/ui_firmware.rs +++ b/core/embed/rust/src/ui/ui_firmware.rs @@ -205,7 +205,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 bb6501b4a3..7e2406ad94 100644 --- a/core/mocks/generated/trezorui_api.pyi +++ b/core/mocks/generated/trezorui_api.pyi @@ -367,7 +367,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/bitcoin/sign_tx/__init__.py b/core/src/apps/bitcoin/sign_tx/__init__.py index 17063fc3de..d564b470b4 100644 --- a/core/src/apps/bitcoin/sign_tx/__init__.py +++ b/core/src/apps/bitcoin/sign_tx/__init__.py @@ -52,8 +52,10 @@ async def sign_tx( coin: CoinInfo, authorization: CoinJoinAuthorization | None = None, ) -> TxRequest: + from trezor import TR from trezor.enums import RequestType from trezor.messages import TxRequest + from trezor.ui.layouts import show_continue_in_app from trezor.wire.context import call from ..common import BITCOIN_NAMES @@ -93,6 +95,7 @@ async def sign_tx( request_class, req = req assert TxRequest.is_type_of(req) if req.request_type == RequestType.TXFINISHED: + show_continue_in_app(TR.send__transaction_signed) return req res = await call(req, request_class) elif isinstance(req, helpers.UiConfirm): diff --git a/core/src/apps/cardano/sign_tx/__init__.py b/core/src/apps/cardano/sign_tx/__init__.py index c9144580e5..cc2148cd8f 100644 --- a/core/src/apps/cardano/sign_tx/__init__.py +++ b/core/src/apps/cardano/sign_tx/__init__.py @@ -12,9 +12,10 @@ if TYPE_CHECKING: async def sign_tx( msg: CardanoSignTxInit, keychain: seed.Keychain ) -> CardanoSignTxFinished: - from trezor import log, wire + from trezor import TR, log, wire from trezor.enums import CardanoTxSigningMode from trezor.messages import CardanoSignTxFinished + from trezor.ui.layouts import show_continue_in_app from .signer import Signer @@ -49,4 +50,5 @@ async def sign_tx( log.exception(__name__, e) raise wire.ProcessError("Signing failed") + show_continue_in_app(TR.send__transaction_signed) return CardanoSignTxFinished() diff --git a/core/src/apps/eos/sign_tx.py b/core/src/apps/eos/sign_tx.py index 11c66a8b0b..047ca99af5 100644 --- a/core/src/apps/eos/sign_tx.py +++ b/core/src/apps/eos/sign_tx.py @@ -10,9 +10,11 @@ if TYPE_CHECKING: @auto_keychain(__name__) async def sign_tx(msg: EosSignTx, keychain: Keychain) -> EosSignedTx: + from trezor import TR from trezor.crypto.curve import secp256k1 from trezor.crypto.hashlib import sha256 from trezor.messages import EosSignedTx, EosTxActionAck, EosTxActionRequest + from trezor.ui.layouts import show_continue_in_app from trezor.utils import HashWriter from trezor.wire import DataError from trezor.wire.context import call @@ -55,4 +57,5 @@ async def sign_tx(msg: EosSignTx, keychain: Keychain) -> EosSignedTx: node.private_key(), digest, True, secp256k1.CANONICAL_SIG_EOS ) + show_continue_in_app(TR.send__transaction_signed) return EosSignedTx(signature=base58_encode("SIG_", "K1", signature)) diff --git a/core/src/apps/ethereum/sign_tx.py b/core/src/apps/ethereum/sign_tx.py index 4de0fae6b4..79ab0b17c4 100644 --- a/core/src/apps/ethereum/sign_tx.py +++ b/core/src/apps/ethereum/sign_tx.py @@ -40,6 +40,7 @@ async def sign_tx( ) -> EthereumTxRequest: from trezor import TR from trezor.crypto.hashlib import sha3_256 + from trezor.ui.layouts import show_continue_in_app from trezor.ui.layouts.progress import progress from trezor.utils import HashWriter @@ -111,6 +112,7 @@ async def sign_tx( progress_obj.stop() + show_continue_in_app(TR.send__transaction_signed) return result diff --git a/core/src/apps/ethereum/sign_tx_eip1559.py b/core/src/apps/ethereum/sign_tx_eip1559.py index a4cb1d2862..c09792ff8a 100644 --- a/core/src/apps/ethereum/sign_tx_eip1559.py +++ b/core/src/apps/ethereum/sign_tx_eip1559.py @@ -35,9 +35,10 @@ async def sign_tx_eip1559( keychain: Keychain, defs: Definitions, ) -> EthereumTxRequest: - from trezor import wire + from trezor import TR, wire from trezor.crypto import rlp # local_cache_global from trezor.crypto.hashlib import sha3_256 + from trezor.ui.layouts import show_continue_in_app from trezor.utils import HashWriter from apps.common import paths @@ -121,6 +122,7 @@ async def sign_tx_eip1559( digest = sha.get_digest() result = _sign_digest(msg, keychain, digest) + show_continue_in_app(TR.send__transaction_signed) return result diff --git a/core/src/apps/monero/sign_tx.py b/core/src/apps/monero/sign_tx.py index 842b7c589f..86924fbe94 100644 --- a/core/src/apps/monero/sign_tx.py +++ b/core/src/apps/monero/sign_tx.py @@ -14,7 +14,8 @@ if TYPE_CHECKING: async def sign_tx(received_msg, keychain: Keychain) -> MoneroTransactionFinalAck: import gc - from trezor import log, utils + from trezor import TR, log, utils + from trezor.ui.layouts import show_continue_in_app from trezor.wire.context import get_context from apps.monero.signing.state import State @@ -45,6 +46,7 @@ async def sign_tx(received_msg, keychain: Keychain) -> MoneroTransactionFinalAck received_msg = await ctx.read(accept_msgs) utils.unimport_end(mods) + show_continue_in_app(TR.send__transaction_signed) return result_msg diff --git a/core/src/apps/ripple/sign_tx.py b/core/src/apps/ripple/sign_tx.py index c8957bec4d..d031a4e3cc 100644 --- a/core/src/apps/ripple/sign_tx.py +++ b/core/src/apps/ripple/sign_tx.py @@ -11,10 +11,12 @@ if TYPE_CHECKING: # NOTE: it is one big function because that way it is the most flash-space-efficient @auto_keychain(__name__) async def sign_tx(msg: RippleSignTx, keychain: Keychain) -> RippleSignedTx: + from trezor import TR from trezor.crypto import der from trezor.crypto.curve import secp256k1 from trezor.crypto.hashlib import sha512 from trezor.messages import RippleSignedTx + from trezor.ui.layouts import show_continue_in_app from trezor.wire import ProcessError from apps.common import paths @@ -58,4 +60,5 @@ async def sign_tx(msg: RippleSignTx, keychain: Keychain) -> RippleSignedTx: sig_encoded = der.encode_seq((sig[1:33], sig[33:65])) tx = serialize(msg, source_address, node.public_key(), sig_encoded) + show_continue_in_app(TR.send__transaction_signed) return RippleSignedTx(signature=sig_encoded, serialized_tx=tx) diff --git a/core/src/apps/solana/sign_tx.py b/core/src/apps/solana/sign_tx.py index c98985e57d..0cd3d1f324 100644 --- a/core/src/apps/solana/sign_tx.py +++ b/core/src/apps/solana/sign_tx.py @@ -23,7 +23,7 @@ async def sign_tx( from trezor.crypto.curve import ed25519 from trezor.enums import ButtonRequestType from trezor.messages import SolanaTxSignature - from trezor.ui.layouts import confirm_metadata, show_warning + from trezor.ui.layouts import confirm_metadata, show_continue_in_app, show_warning from apps.common import seed @@ -79,7 +79,7 @@ async def sign_tx( ) signature = ed25519.sign(node.private_key(), serialized_tx) - + show_continue_in_app(TR.send__transaction_signed) return SolanaTxSignature(signature=signature) diff --git a/core/src/apps/stellar/sign_tx.py b/core/src/apps/stellar/sign_tx.py index f151eaeaf1..532a84a8ff 100644 --- a/core/src/apps/stellar/sign_tx.py +++ b/core/src/apps/stellar/sign_tx.py @@ -12,10 +12,12 @@ if TYPE_CHECKING: async def sign_tx(msg: StellarSignTx, keychain: Keychain) -> StellarSignedTx: from ubinascii import hexlify + from trezor import TR from trezor.crypto.curve import ed25519 from trezor.crypto.hashlib import sha256 from trezor.enums import StellarMemoType from trezor.messages import StellarSignedTx, StellarTxOpRequest + from trezor.ui.layouts import show_continue_in_app from trezor.wire import DataError, ProcessError from trezor.wire.context import call_any @@ -114,6 +116,7 @@ async def sign_tx(msg: StellarSignTx, keychain: Keychain) -> StellarSignedTx: # sign digest = sha256(w).digest() signature = ed25519.sign(node.private_key(), digest) + show_continue_in_app(TR.send__transaction_signed) # Add the public key for verification that the right account was used for signing return StellarSignedTx(public_key=pubkey, signature=signature) diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index 648decf88d..5153657fc1 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -32,10 +32,12 @@ if TYPE_CHECKING: @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def sign_tx(msg: TezosSignTx, keychain: Keychain) -> TezosSignedTx: + from trezor import TR from trezor.crypto import hashlib from trezor.crypto.curve import ed25519 from trezor.enums import TezosBallotType from trezor.messages import TezosSignedTx + from trezor.ui.layouts import show_continue_in_app from apps.common.paths import validate_path @@ -155,6 +157,7 @@ async def sign_tx(msg: TezosSignTx, keychain: Keychain) -> TezosSignedTx: sig_prefixed = base58_encode_check(signature, helpers.TEZOS_SIGNATURE_PREFIX) + show_continue_in_app(TR.send__transaction_signed) return TezosSignedTx( signature=sig_prefixed, sig_op_contents=sig_op_contents, operation_hash=ophash ) diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py index 0ecd355e20..006619be98 100644 --- a/core/src/trezor/ui/layouts/bolt/__init__.py +++ b/core/src/trezor/ui/layouts/bolt/__init__.py @@ -443,6 +443,10 @@ def show_success( ) +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 b35d9db0c1..2f5a823098 100644 --- a/core/src/trezor/ui/layouts/caesar/__init__.py +++ b/core/src/trezor/ui/layouts/caesar/__init__.py @@ -507,6 +507,10 @@ def show_success( ) +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/common.py b/core/src/trezor/ui/layouts/common.py index 9cdce81b83..3d56635350 100644 --- a/core/src/trezor/ui/layouts/common.py +++ b/core/src/trezor/ui/layouts/common.py @@ -10,7 +10,7 @@ if __debug__: from trezor import log if TYPE_CHECKING: - from typing import Any, Awaitable, Callable, TypeVar + from typing import Any, Awaitable, Callable, Coroutine, TypeVar PropertyType = tuple[str | None, str | bytes | None] ExceptionType = BaseException | type[BaseException] @@ -62,7 +62,7 @@ def raise_if_not_confirmed( br_name: str | None, br_code: ButtonRequestType = ButtonRequestType.Other, exc: ExceptionType = ActionCancelled, -) -> Awaitable[None]: +) -> Coroutine[Any, Any, None]: action = interact(layout_obj, br_name, br_code, exc) return action # type: ignore ["UiResult" is incompatible with "None"] diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py index c1f388458b..e8b806da73 100644 --- a/core/src/trezor/ui/layouts/delizia/__init__.py +++ b/core/src/trezor/ui/layouts/delizia/__init__.py @@ -1,14 +1,14 @@ from typing import TYPE_CHECKING import trezorui_api -from trezor import TR, ui, utils +from trezor import TR, ui, utils, workflow from trezor.enums import ButtonRequestType, RecoveryType from trezor.wire import ActionCancelled from ..common import draw_simple, interact, raise_if_not_confirmed, with_info if TYPE_CHECKING: - from typing import Awaitable, Iterable, NoReturn, Sequence, TypeVar + from typing import Any, Awaitable, Coroutine, Iterable, NoReturn, Sequence, TypeVar from ..common import ExceptionType, PropertyType @@ -279,12 +279,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, @@ -297,13 +291,18 @@ 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, ), None, ) + show_continue_in_app( + TR.address__public_key_confirmed + if title in ("XPUB", TR.address__public_key) + else TR.address__confirmed + ) + def show_pubkey( pubkey: str, @@ -393,22 +392,34 @@ def show_danger( def show_success( - br_name: str, + br_name: str | None, content: str, subheader: str | None = None, button: str | None = None, -) -> Awaitable[None]: + time_ms: int = 0, +) -> Coroutine[Any, Any, 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) -> None: + task = show_success( + content=content, + button=TR.instructions__continue_in_app, + time_ms=3200, + br_name=None, + ) + workflow.spawn(task) + + async def confirm_output( address: str, amount: str | None = None, diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py index 9390a0ff1d..75a04c358d 100644 --- a/core/src/trezor/ui/layouts/eckhart/__init__.py +++ b/core/src/trezor/ui/layouts/eckhart/__init__.py @@ -1,14 +1,14 @@ from typing import TYPE_CHECKING import trezorui_api -from trezor import TR, ui, utils +from trezor import TR, ui, utils, workflow from trezor.enums import ButtonRequestType, RecoveryType from trezor.wire import ActionCancelled from ..common import draw_simple, interact, raise_if_not_confirmed, with_info if TYPE_CHECKING: - from typing import Awaitable, Iterable, NoReturn, Sequence, TypeVar + from typing import Any, Awaitable, Coroutine, Iterable, NoReturn, Sequence, TypeVar from ..common import ExceptionType, PropertyType @@ -297,6 +297,12 @@ async def show_address( None, ) + show_continue_in_app( + TR.address__public_key_confirmed + if title in ("XPUB", TR.address__public_key) + else TR.address__confirmed + ) + def show_pubkey( pubkey: str, @@ -393,7 +399,7 @@ def show_success( subheader: str | None = None, button: str | None = None, time_ms: int = 0, -) -> Awaitable[None]: +) -> Coroutine[Any, Any, None]: button = button or TR.buttons__continue # def_arg return raise_if_not_confirmed( trezorui_api.show_success( @@ -408,13 +414,14 @@ def show_success( ) -def show_continue_in_app(content: str) -> Awaitable[None]: - return show_success( +def show_continue_in_app(content: str) -> None: + task = show_success( content=content, button=TR.instructions__continue_in_app, time_ms=3200, br_name=None, ) + workflow.spawn(task) async def confirm_output( diff --git a/core/src/trezor/wire/__init__.py b/core/src/trezor/wire/__init__.py index b831710e58..8706252ea3 100644 --- a/core/src/trezor/wire/__init__.py +++ b/core/src/trezor/wire/__init__.py @@ -27,6 +27,7 @@ from typing import TYPE_CHECKING from trezor import loop, protobuf, utils +from .. import workflow from . import message_handler, protocol_common from .codec.codec_context import CodecContext from .context import UnexpectedMessageException @@ -118,6 +119,8 @@ async def handle_session(iface: WireInterface) -> None: utils.unimport_end(modules) if not do_not_restart: + # Wait for all active workflows to finish. + await workflow.join_all() # Let the session be restarted from `main`. loop.clear() return # pylint: disable=lost-exception diff --git a/core/src/trezor/workflow.py b/core/src/trezor/workflow.py index 67b88f8e68..72b1186f55 100644 --- a/core/src/trezor/workflow.py +++ b/core/src/trezor/workflow.py @@ -84,6 +84,16 @@ def spawn(workflow: loop.Task) -> loop.spawn: return task +async def join_all() -> None: + """Block until all workflows are over.""" + if __debug__: + log.debug(__name__, "joining %d workflows", len(tasks)) + + # Don't iterate over `tasks` since it will be modified by its finalizers + while tasks: + await next(iter(tasks)) + + def start_default() -> None: """Start a default workflow.