refactor(core): enforce layout scoping for more Rust layouts
What changed, and why it matters
This commit is a follow-up code cleanup that wraps more on-screen layout calls in Python 'with' blocks. It changes return-type annotations from LayoutObj to LayoutContext and makes several UI helper functions async. There is no direct evidence in the commit that it fixes an exploitable security bug; it appears to be a defensive refactoring to enforce layout scoping consistently.
Treat as a normal refactoring commit. Review PR #6812 and any related tests to confirm the scoping change does not introduce lifecycle regressions for sensitive layouts (PIN, passphrase, recovery, firmware update). No urgent security response is indicated by this diff alone.
Security signals we found
Refactoring only: no change to cryptographic, authentication, or authorization logic
Type annotation change from LayoutObj to LayoutContext
Introduction/consistent use of context-manager scoping for UI layouts
Conversion of UI helpers from sync Awaitable return to async functions
Evidence from the diff
The patch extends the layout-scoping pattern introduced in PR #6812 to additional Rust-backed UI layouts. It updates type stubs and Rust doc comments from LayoutObj[T] to LayoutContext[T], and rewrites Python layout wrappers (bolt/caesar/delizia/eckhart) to use ‘with trezorui_api.xxx(…) as layout:’ before passing the layout to interact()/raise_if_not_confirmed()/draw_simple(). Several functions are converted from sync returning Awaitable to async returning None/str. The change is architectural and does not, on its own, alter security-critical logic such as PIN/passphrase handling, firmware verification, or recovery flow semantics.
Changed components
core/embed/rust/src/ui/api/firmware_micropython.rscore/mocks/generated/trezorui_api.pyicore/src/apps/homescreen/device_menu.pycore/src/trezor/ui/layouts/bolt/__init__.pycore/src/trezor/ui/layouts/bolt/recovery.pycore/src/trezor/ui/layouts/bolt/reset.pycore/src/trezor/ui/layouts/caesar/__init__.pycore/src/trezor/ui/layouts/caesar/recovery.pycore/src/trezor/ui/layouts/caesar/reset.pycore/src/trezor/ui/layouts/delizia/__init__.pycore/src/trezor/ui/layouts/delizia/recovery.pycore/src/trezor/ui/layouts/delizia/reset.pycore/src/trezor/ui/layouts/eckhart/__init__.pycore/src/trezor/ui/layouts/eckhart/recovery.pycore/src/trezor/ui/layouts/eckhart/reset.pycore/src/trezor/wire/thp/ui.pyInspect captured patch +436 / −425
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index c203317d..aab705b1 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -1631,7 +1631,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// *,
/// description: str,
/// fingerprint: str,
- /// ) -> LayoutObj[UiResult]:
+ /// ) -> LayoutContext[UiResult]:
/// """Ask whether to update firmware, optionally show fingerprint."""
Qstr::MP_QSTR_confirm_firmware_update => obj_fn_kw!(0, new_confirm_firmware_update).as_obj(),
@@ -1639,7 +1639,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// *,
/// title: str,
/// image: AnyBytes,
- /// ) -> LayoutObj[UiResult]:
+ /// ) -> LayoutContext[UiResult]:
/// """Confirm homescreen."""
Qstr::MP_QSTR_confirm_homescreen => obj_fn_kw!(0, new_confirm_homescreen).as_obj(),
@@ -1741,7 +1741,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// recovery_type: RecoveryType,
/// show_instructions: bool = False, # unused on bolt
/// remaining_shares: Iterable[tuple[str, str]] | None = None,
- /// ) -> LayoutObj[UiResult]:
+ /// ) -> LayoutContext[UiResult]:
/// """Device recovery homescreen."""
Qstr::MP_QSTR_continue_recovery_homepage => obj_fn_kw!(0, new_continue_recovery_homepage).as_obj(),
@@ -1828,7 +1828,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// max_count: int,
/// description: str | None = None,
/// more_info_callback: Callable[[int], str] | None = None,
- /// ) -> LayoutObj[tuple[UiResult, int]]:
+ /// ) -> LayoutContext[tuple[UiResult, int]]:
/// """Number input with + and - buttons, optional static description and optional dynamic
/// description."""
Qstr::MP_QSTR_request_number => obj_fn_kw!(0, new_request_number).as_obj(),
@@ -1840,7 +1840,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// min_ms: int,
/// max_ms: int,
/// description: str | None = None,
- /// ) -> LayoutObj[tuple[UiResult, int]]:
+ /// ) -> LayoutContext[tuple[UiResult, int]]:
/// """Duration input with + and - buttons, optional static description. """
Qstr::MP_QSTR_request_duration => obj_fn_kw!(0, new_request_duration).as_obj(),
@@ -1851,7 +1851,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// allow_cancel: bool = True,
/// wrong_pin: bool = False,
/// last_attempt: bool = False,
- /// ) -> LayoutObj[str | UiResult]:
+ /// ) -> LayoutContext[str | UiResult]:
/// """Request pin on device."""
Qstr::MP_QSTR_request_pin => obj_fn_kw!(0, new_request_pin).as_obj(),
@@ -1860,7 +1860,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// prompt: str,
/// prompt_empty: str,
/// max_len: int,
- /// ) -> LayoutObj[str | UiResult]:
+ /// ) -> LayoutContext[str | UiResult]:
/// """Passphrase input keyboard."""
Qstr::MP_QSTR_request_passphrase => obj_fn_kw!(0, new_request_passphrase).as_obj(),
@@ -1870,7 +1870,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// max_len: int,
/// allow_empty: bool,
/// prefill: str | None,
- /// ) -> LayoutObj[str | UiResult]:
+ /// ) -> LayoutContext[str | UiResult]:
/// """Label input keyboard."""
Qstr::MP_QSTR_request_string => obj_fn_kw!(0, new_request_string).as_obj(),
@@ -2134,7 +2134,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// text: str,
/// title: str | None = None,
/// button: str | None = None,
- /// ) -> LayoutObj[UiResult]:
+ /// ) -> LayoutContext[UiResult]:
/// """Simple dialog with text. TT: optional button."""
Qstr::MP_QSTR_show_simple => obj_fn_kw!(0, new_show_simple).as_obj(),
@@ -2145,7 +2145,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// description: str = "",
/// allow_cancel: bool = False,
/// time_ms: int = 0,
- /// ) -> LayoutObj[UiResult]:
+ /// ) -> LayoutContext[UiResult]:
/// """Success modal. No buttons shown when `button` is empty string."""
Qstr::MP_QSTR_show_success => obj_fn_kw!(0, new_show_success).as_obj(),
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index adc6099c..2183dfdc 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -261,7 +261,7 @@ def confirm_firmware_update(
*,
description: str,
fingerprint: str,
-) -> LayoutObj[UiResult]:
+) -> LayoutContext[UiResult]:
"""Ask whether to update firmware, optionally show fingerprint."""
@@ -270,7 +270,7 @@ def confirm_homescreen(
*,
title: str,
image: AnyBytes,
-) -> LayoutObj[UiResult]:
+) -> LayoutContext[UiResult]:
"""Confirm homescreen."""
@@ -378,7 +378,7 @@ def continue_recovery_homepage(
recovery_type: RecoveryType,
show_instructions: bool = False, # unused on bolt
remaining_shares: Iterable[tuple[str, str]] | None = None,
-) -> LayoutObj[UiResult]:
+) -> LayoutContext[UiResult]:
"""Device recovery homescreen."""
@@ -473,7 +473,7 @@ def request_number(
max_count: int,
description: str | None = None,
more_info_callback: Callable[[int], str] | None = None,
-) -> LayoutObj[tuple[UiResult, int]]:
+) -> LayoutContext[tuple[UiResult, int]]:
"""Number input with + and - buttons, optional static description and optional dynamic
description."""
@@ -486,7 +486,7 @@ def request_duration(
min_ms: int,
max_ms: int,
description: str | None = None,
-) -> LayoutObj[tuple[UiResult, int]]:
+) -> LayoutContext[tuple[UiResult, int]]:
"""Duration input with + and - buttons, optional static description. """
@@ -498,7 +498,7 @@ def request_pin(
allow_cancel: bool = True,
wrong_pin: bool = False,
last_attempt: bool = False,
-) -> LayoutObj[str | UiResult]:
+) -> LayoutContext[str | UiResult]:
"""Request pin on device."""
@@ -508,7 +508,7 @@ def request_passphrase(
prompt: str,
prompt_empty: str,
max_len: int,
-) -> LayoutObj[str | UiResult]:
+) -> LayoutContext[str | UiResult]:
"""Passphrase input keyboard."""
@@ -519,7 +519,7 @@ def request_string(
max_len: int,
allow_empty: bool,
prefill: str | None,
-) -> LayoutObj[str | UiResult]:
+) -> LayoutContext[str | UiResult]:
"""Label input keyboard."""
@@ -810,7 +810,7 @@ def show_simple(
text: str,
title: str | None = None,
button: str | None = None,
-) -> LayoutObj[UiResult]:
+) -> LayoutContext[UiResult]:
"""Simple dialog with text. TT: optional button."""
@@ -822,7 +822,7 @@ def show_success(
description: str = "",
allow_cancel: bool = False,
time_ms: int = 0,
-) -> LayoutObj[UiResult]:
+) -> LayoutContext[UiResult]:
"""Success modal. No buttons shown when `button` is empty string."""
diff --git a/core/src/apps/homescreen/device_menu.py b/core/src/apps/homescreen/device_menu.py
index 28491d41..fc2390cb 100644
--- a/core/src/apps/homescreen/device_menu.py
+++ b/core/src/apps/homescreen/device_menu.py
@@ -322,16 +322,17 @@ async def handle_SetAutoLockUSB() -> None:
min_ms = storage_device.AUTOLOCK_DELAY_USB_MIN_MS
max_ms = storage_device.AUTOLOCK_DELAY_USB_MAX_MS
- auto_lock_delay_ms = await interact(
- trezorui_api.request_duration(
- title=TR.auto_lock__title,
- duration_ms=duration_ms,
- min_ms=min_ms,
- max_ms=max_ms,
- description=TR.auto_lock__description,
- ),
- br_name=None,
- )
+ with trezorui_api.request_duration(
+ title=TR.auto_lock__title,
+ duration_ms=duration_ms,
+ min_ms=min_ms,
+ max_ms=max_ms,
+ description=TR.auto_lock__description,
+ ) as layout:
+ auto_lock_delay_ms = await interact(
+ layout,
+ br_name=None,
+ )
# Necessary for the style check not to raise type error
assert isinstance(auto_lock_delay_ms, int)
settings = ApplySettings(
@@ -351,16 +352,17 @@ async def handle_SetAutoLockBattery() -> None:
min_ms = storage_device.AUTOLOCK_DELAY_BATT_MIN_MS
max_ms = storage_device.AUTOLOCK_DELAY_BATT_MAX_MS
- auto_lock_delay_ms = await interact(
- trezorui_api.request_duration(
- title=TR.auto_lock__title,
- duration_ms=duration_ms,
- min_ms=min_ms,
- max_ms=max_ms,
- description=TR.auto_lock__description,
- ),
- br_name=None,
- )
+ with trezorui_api.request_duration(
+ title=TR.auto_lock__title,
+ duration_ms=duration_ms,
+ min_ms=min_ms,
+ max_ms=max_ms,
+ description=TR.auto_lock__description,
+ ) as layout:
+ auto_lock_delay_ms = await interact(
+ layout,
+ br_name=None,
+ )
# Necessary for the style check not to raise type error
assert isinstance(auto_lock_delay_ms, int)
settings = ApplySettings(
@@ -411,15 +413,16 @@ async def handle_SetDeviceName() -> None:
utils.ensure(storage_device.is_initialized())
- label = await interact(
- trezorui_api.request_string(
- prompt=TR.device_name__enter,
- max_len=storage_device.LABEL_MAXLENGTH,
- allow_empty=True,
- prefill=storage_device.get_label(),
- ),
- "device_name",
- )
+ with trezorui_api.request_string(
+ prompt=TR.device_name__enter,
+ max_len=storage_device.LABEL_MAXLENGTH,
+ allow_empty=True,
+ prefill=storage_device.get_label(),
+ ) as layout:
+ label = await interact(
+ layout,
+ "device_name",
+ )
# Necessary for the style check not to raise type error
assert isinstance(label, str)
await apply_settings(ApplySettings(label=label))
diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py
index 859637df..1a2b9952 100644
--- a/core/src/trezor/ui/layouts/bolt/__init__.py
+++ b/core/src/trezor/ui/layouts/bolt/__init__.py
@@ -206,15 +206,16 @@ def lock_time_disabled_warning() -> Awaitable[None]:
)
-def confirm_homescreen(image: AnyBytes) -> Awaitable[None]:
- return raise_if_not_confirmed(
- trezorui_api.confirm_homescreen(
- title=TR.homescreen__title_set,
- image=image,
- ),
- "set_homesreen",
- ButtonRequestType.ProtectCall,
- )
+async def confirm_homescreen(image: AnyBytes) -> None:
+ with trezorui_api.confirm_homescreen(
+ title=TR.homescreen__title_set,
+ image=image,
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ "set_homesreen",
+ ButtonRequestType.ProtectCall,
+ )
async def confirm_change_label(
@@ -466,23 +467,24 @@ def show_danger(
)
-def show_success(
+async def show_success(
br_name: str,
content: str,
subheader: str | None = None,
button: str | None = None,
-) -> Awaitable[None]:
+) -> None:
button = button or TR.buttons__continue # def_arg
- return raise_if_not_confirmed(
- trezorui_api.show_success(
- title=content,
- description=subheader or "",
- button=button,
- allow_cancel=False,
- ),
- br_name,
- ButtonRequestType.Success,
- )
+ with trezorui_api.show_success(
+ title=content,
+ description=subheader or "",
+ button=button,
+ allow_cancel=False,
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ br_name,
+ ButtonRequestType.Success,
+ )
def show_continue_in_app(content: str) -> None:
@@ -2135,12 +2137,10 @@ def error_popup(
def request_passphrase_on_host() -> None:
- draw_simple(
- trezorui_api.show_simple(
- title=None,
- text=TR.passphrase__please_enter,
- )
- )
+ with trezorui_api.show_simple(
+ title=None, text=TR.passphrase__please_enter
+ ) as layout:
+ draw_simple(layout)
def show_wait_text(message: str) -> None:
@@ -2148,18 +2148,19 @@ def show_wait_text(message: str) -> None:
async def request_passphrase_on_device(max_len: int) -> str:
- result = await interact(
- trezorui_api.request_passphrase(
- prompt=TR.passphrase__title_enter,
- prompt_empty=TR.passphrase__continue_with_empty_passphrase,
- max_len=max_len,
- ),
- "passphrase_device",
- ButtonRequestType.PassphraseEntry,
- raise_on_cancel=ActionCancelled("Passphrase entry cancelled"),
- )
- assert isinstance(result, str)
- return result
+ with trezorui_api.request_passphrase(
+ prompt=TR.passphrase__title_enter,
+ prompt_empty=TR.passphrase__continue_with_empty_passphrase,
+ max_len=max_len,
+ ) as layout:
+ result = await interact(
+ layout,
+ "passphrase_device",
+ ButtonRequestType.PassphraseEntry,
+ raise_on_cancel=ActionCancelled("Passphrase entry cancelled"),
+ )
+ assert isinstance(result, str)
+ return result
async def request_pin_on_device(
@@ -2177,18 +2178,19 @@ async def request_pin_on_device(
else:
attempts = f"{attempts_remaining} {TR.pin__tries_left}"
- result = await interact(
- trezorui_api.request_pin(
- prompt=prompt,
- attempts=attempts,
- allow_cancel=allow_cancel,
- wrong_pin=wrong_pin,
- ),
- "pin_device",
- ButtonRequestType.PinEntry,
- raise_on_cancel=PinCancelled,
- )
- return result # type: ignore ["UiResult" is not assignable to "str"]
+ with trezorui_api.request_pin(
+ prompt=prompt,
+ attempts=attempts,
+ allow_cancel=allow_cancel,
+ wrong_pin=wrong_pin,
+ ) as layout:
+ result = await interact(
+ layout,
+ "pin_device",
+ ButtonRequestType.PinEntry,
+ raise_on_cancel=PinCancelled,
+ )
+ return result # type: ignore ["UiResult" is not assignable to "str"]
async def confirm_reenter_pin(is_wipe_code: bool = False) -> None:
diff --git a/core/src/trezor/ui/layouts/bolt/recovery.py b/core/src/trezor/ui/layouts/bolt/recovery.py
index 22156da8..94802239 100644
--- a/core/src/trezor/ui/layouts/bolt/recovery.py
+++ b/core/src/trezor/ui/layouts/bolt/recovery.py
@@ -159,33 +159,33 @@ async def continue_recovery(
if remaining_shares_info
else None
)
- homepage = trezorui_api.continue_recovery_homepage(
+
+ with trezorui_api.continue_recovery_homepage(
text=text,
subtext=description,
button=button_label,
recovery_type=recovery_type,
remaining_shares=remaining_shares,
- )
-
- while True:
- result = await interact(
- homepage,
- "recovery",
- ButtonRequestType.RecoveryHomepage,
- raise_on_cancel=None,
- )
+ ) as homepage:
+ while True:
+ result = await interact(
+ homepage,
+ "recovery",
+ ButtonRequestType.RecoveryHomepage,
+ raise_on_cancel=None,
+ )
- if result is trezorui_api.CONFIRMED:
- return True
- elif result is trezorui_api.INFO and remaining_shares is not None:
- await show_remaining_shares(remaining_shares)
- else:
- try:
- await _confirm_abort(recovery_type != RecoveryType.NormalRecovery)
- except ActionCancelled:
- pass
+ if result is trezorui_api.CONFIRMED:
+ return True
+ elif result is trezorui_api.INFO and remaining_shares is not None:
+ await show_remaining_shares(remaining_shares)
else:
- return False
+ try:
+ await _confirm_abort(recovery_type != RecoveryType.NormalRecovery)
+ except ActionCancelled:
+ pass
+ else:
+ return False
async def show_invalid_mnemonic(word_count: int) -> None:
diff --git a/core/src/trezor/ui/layouts/bolt/reset.py b/core/src/trezor/ui/layouts/bolt/reset.py
index 5d47250e..763a27fc 100644
--- a/core/src/trezor/ui/layouts/bolt/reset.py
+++ b/core/src/trezor/ui/layouts/bolt/reset.py
@@ -113,42 +113,38 @@ async def _prompt_number(
max_count: int,
br_name: str,
) -> int:
- num_input = trezorui_api.request_number(
+ with trezorui_api.request_number(
title=title,
count=count,
min_count=min_count,
max_count=max_count,
description=None,
more_info_callback=description,
- )
-
- while True:
- result = await interact(
- num_input,
- br_name,
- ButtonRequestType.ResetDevice,
- raise_on_cancel=None,
- )
- if __debug__:
- if not isinstance(result, tuple):
- # DebugLink currently can't send number of shares and it doesn't
- # change the counter either so just use the initial value.
- result = result, count
- status, value = result
-
- if status == CONFIRMED:
- assert isinstance(value, int)
- return value
-
- await interact(
- trezorui_api.show_simple(
+ ) as num_input:
+ while True:
+ result = await interact(
+ num_input,
+ br_name,
+ ButtonRequestType.ResetDevice,
+ raise_on_cancel=None,
+ )
+ if __debug__:
+ if not isinstance(result, tuple):
+ # DebugLink currently can't send number of shares and it doesn't
+ # change the counter either so just use the initial value.
+ result = result, count
+ status, value = result
+
+ if status == CONFIRMED:
+ assert isinstance(value, int)
+ return value
+
+ with trezorui_api.show_simple(
title=None,
text=info(value),
button=TR.buttons__ok_i_understand,
- ),
- None,
- raise_on_cancel=None,
- )
+ ) as layout:
+ await interact(layout, None, raise_on_cancel=None)
def slip39_prompt_threshold(
diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py
index a2882128..124a469b 100644
--- a/core/src/trezor/ui/layouts/caesar/__init__.py
+++ b/core/src/trezor/ui/layouts/caesar/__init__.py
@@ -224,15 +224,16 @@ def lock_time_disabled_warning() -> Awaitable[ui.UiResult]:
)
-def confirm_homescreen(image: AnyBytes) -> Awaitable[None]:
- return raise_if_not_confirmed(
- trezorui_api.confirm_homescreen(
- title=TR.homescreen__title_set,
- image=image,
- ),
- "set_homesreen",
- ButtonRequestType.ProtectCall,
- )
+async def confirm_homescreen(image: AnyBytes) -> None:
+ with trezorui_api.confirm_homescreen(
+ title=TR.homescreen__title_set,
+ image=image,
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ "set_homesreen",
+ ButtonRequestType.ProtectCall,
+ )
async def confirm_change_label(
@@ -2160,12 +2161,10 @@ def error_popup(
def request_passphrase_on_host() -> None:
- draw_simple(
- trezorui_api.show_simple(
- title=None,
- text=TR.passphrase__please_enter,
- )
- )
+ with trezorui_api.show_simple(
+ title=None, text=TR.passphrase__please_enter
+ ) as layout:
+ draw_simple(layout)
def show_wait_text(message: str) -> None:
@@ -2173,16 +2172,17 @@ def show_wait_text(message: str) -> None:
async def request_passphrase_on_device(max_len: int) -> str:
- result = await interact(
- trezorui_api.request_passphrase(
- prompt=TR.passphrase__title_enter,
- prompt_empty=TR.passphrase__continue_with_empty_passphrase,
- max_len=max_len,
- ),
- "passphrase_device",
- ButtonRequestType.PassphraseEntry,
- raise_on_cancel=ActionCancelled("Passphrase entry cancelled"),
- )
+ with trezorui_api.request_passphrase(
+ prompt=TR.passphrase__title_enter,
+ prompt_empty=TR.passphrase__continue_with_empty_passphrase,
+ max_len=max_len,
+ ) as layout:
+ result = await interact(
+ layout,
+ "passphrase_device",
+ ButtonRequestType.PassphraseEntry,
+ raise_on_cancel=ActionCancelled("Passphrase entry cancelled"),
+ )
assert isinstance(result, str)
return result
@@ -2204,17 +2204,18 @@ async def request_pin_on_device(
else:
attempts = f"{attempts_remaining} {TR.pin__tries_left}"
- result = await interact(
- trezorui_api.request_pin(
- prompt=prompt,
- attempts=attempts,
- allow_cancel=allow_cancel,
- wrong_pin=wrong_pin,
- ),
- "pin_device",
- ButtonRequestType.PinEntry,
- raise_on_cancel=wire.PinCancelled,
- )
+ with trezorui_api.request_pin(
+ prompt=prompt,
+ attempts=attempts,
+ allow_cancel=allow_cancel,
+ wrong_pin=wrong_pin,
+ ) as layout:
+ result = await interact(
+ layout,
+ "pin_device",
+ ButtonRequestType.PinEntry,
+ raise_on_cancel=wire.PinCancelled,
+ )
return result # type: ignore ["UiResult" is not assignable to "str"]
@@ -2377,14 +2378,15 @@ async def success_pin_change(curpin: str | None, newpin: str | None) -> None:
await show_success("success_pin", msg_screen)
-def confirm_firmware_update(description: str, fingerprint: str) -> Awaitable[None]:
- return raise_if_not_confirmed(
- trezorui_api.confirm_firmware_update(
- description=description, fingerprint=fingerprint
- ),
- "firmware_update",
- BR_CODE_OTHER,
- )
+async def confirm_firmware_update(description: str, fingerprint: str) -> None:
+ with trezorui_api.confirm_firmware_update(
+ description=description, fingerprint=fingerprint
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ "firmware_update",
+ BR_CODE_OTHER,
+ )
def create_details(name: str, value: list[StrPropertyType] | str) -> Details:
diff --git a/core/src/trezor/ui/layouts/caesar/recovery.py b/core/src/trezor/ui/layouts/caesar/recovery.py
index a5809485..8b954bef 100644
--- a/core/src/trezor/ui/layouts/caesar/recovery.py
+++ b/core/src/trezor/ui/layouts/caesar/recovery.py
@@ -129,30 +129,30 @@ async def continue_recovery(
if subtext:
text += f"\n\n{subtext}"
- homepage = trezorui_api.continue_recovery_homepage(
+ with trezorui_api.continue_recovery_homepage(
text=text,
subtext=None,
button=button_label,
recovery_type=recovery_type,
show_instructions=show_instructions,
remaining_shares=None,
- )
- while True:
- result = await interact(
- homepage,
- "recovery",
- ButtonRequestType.RecoveryHomepage,
- raise_on_cancel=None,
- )
- if result is trezorui_api.CONFIRMED:
- return True
-
- try:
- await _confirm_abort(recovery_type != RecoveryType.NormalRecovery)
- except ActionCancelled:
- pass
- else:
- return False
+ ) as homepage:
+ while True:
+ result = await interact(
+ homepage,
+ "recovery",
+ ButtonRequestType.RecoveryHomepage,
+ raise_on_cancel=None,
+ )
+ if result is trezorui_api.CONFIRMED:
+ return True
+
+ try:
+ await _confirm_abort(recovery_type != RecoveryType.NormalRecovery)
+ except ActionCancelled:
+ pass
+ else:
+ return False
async def show_invalid_mnemonic(word_count: int) -> None:
diff --git a/core/src/trezor/ui/layouts/caesar/reset.py b/core/src/trezor/ui/layouts/caesar/reset.py
index 27028be0..21d88a2b 100644
--- a/core/src/trezor/ui/layouts/caesar/reset.py
+++ b/core/src/trezor/ui/layouts/caesar/reset.py
@@ -139,18 +139,18 @@ async def _prompt_number(
max_count: int,
br_name: str,
) -> int:
- num_input = trezorui_api.request_number(
+ with trezorui_api.request_number(
title=title,
count=count,
min_count=min_count,
max_count=max_count,
- )
+ ) as num_input:
- result = await interact(
- num_input,
- br_name,
- ButtonRequestType.ResetDevice,
- )
+ result = await interact(
+ num_input,
+ br_name,
+ ButtonRequestType.ResetDevice,
+ )
if __debug__:
if isinstance(result, str):
diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py
index 506cbe5b..694a68aa 100644
--- a/core/src/trezor/ui/layouts/delizia/__init__.py
+++ b/core/src/trezor/ui/layouts/delizia/__init__.py
@@ -15,7 +15,7 @@ from ..common import (
if TYPE_CHECKING:
from buffer_types import AnyBytes, StrOrBytes
- from typing import Any, Awaitable, Coroutine, Iterable, NoReturn, Sequence, TypeVar
+ from typing import Awaitable, Iterable, NoReturn, Sequence, TypeVar
from trezor.messages import StellarAsset
@@ -143,11 +143,14 @@ async def prompt_recovery_check(recovery_type: RecoveryType) -> None:
async def show_wallet_created_success() -> None:
- await interact(
- trezorui_api.show_success(title=TR.backup__new_wallet_created, button=""),
- "backup_device",
- ButtonRequestType.ResetDevice,
- )
+ with trezorui_api.show_success(
+ title=TR.backup__new_wallet_created, button=""
+ ) as layout:
+ await interact(
+ layout,
+ "backup_device",
+ ButtonRequestType.ResetDevice,
+ )
async def prompt_backup() -> bool:
@@ -217,9 +220,7 @@ def lock_time_disabled_warning() -> Awaitable[None]:
)
-def confirm_homescreen(
- image: AnyBytes,
-) -> Awaitable[None]:
+async def confirm_homescreen(image: AnyBytes) -> None:
from trezor import workflow
@@ -227,14 +228,15 @@ def confirm_homescreen(
# in order to display the new homescreen image.
workflow.close_others()
- return raise_if_not_confirmed(
- trezorui_api.confirm_homescreen(
- title=TR.homescreen__title_set,
- image=image,
- ),
- "set_homesreen",
- ButtonRequestType.ProtectCall,
- )
+ with trezorui_api.confirm_homescreen(
+ title=TR.homescreen__title_set,
+ image=image,
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ "set_homesreen",
+ ButtonRequestType.ProtectCall,
+ )
async def confirm_change_label(
@@ -461,23 +463,24 @@ def show_danger(
)
-def show_success(
+async def show_success(
br_name: str | None,
content: str,
subheader: str | None = None,
button: str | None = None,
time_ms: int = 0,
-) -> Coroutine[Any, Any, None]:
- return raise_if_not_confirmed(
- trezorui_api.show_success(
- title=content,
- button=button or "",
- description=subheader or "",
- time_ms=time_ms,
- ),
- br_name,
- ButtonRequestType.Success,
- )
+) -> None:
+ with trezorui_api.show_success(
+ title=content,
+ button=button or "",
+ description=subheader or "",
+ time_ms=time_ms,
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ br_name,
+ ButtonRequestType.Success,
+ )
def show_continue_in_app(content: str) -> None:
@@ -2143,38 +2146,37 @@ def error_popup(
def request_passphrase_on_host() -> None:
- draw_simple(
- trezorui_api.show_simple(
- title=None,
- text=TR.passphrase__please_enter,
- )
- )
+ with trezorui_api.show_simple(
+ title=None, text=TR.passphrase__please_enter
+ ) as layout:
+ draw_simple(layout)
def show_wait_text(message: str) -> None:
draw_simple(trezorui_api.show_wait_text(message))
-def request_passphrase_on_device(max_len: int) -> Awaitable[str]:
- result = interact(
- trezorui_api.request_passphrase(
- prompt=TR.passphrase__title_enter,
- prompt_empty=TR.passphrase__continue_with_empty_passphrase,
- max_len=max_len,
- ),
- "passphrase_device",
- ButtonRequestType.PassphraseEntry,
- raise_on_cancel=ActionCancelled("Passphrase entry cancelled"),
- )
+async def request_passphrase_on_device(max_len: int) -> str:
+ with trezorui_api.request_passphrase(
+ prompt=TR.passphrase__title_enter,
+ prompt_empty=TR.passphrase__continue_with_empty_passphrase,
+ max_len=max_len,
+ ) as layout:
+ result = await interact(
+ layout,
+ "passphrase_device",
+ ButtonRequestType.PassphraseEntry,
+ raise_on_cancel=ActionCancelled("Passphrase entry cancelled"),
+ )
return result # type: ignore ["UiResult" is not assignable to "str"]
-def request_pin_on_device(
+async def request_pin_on_device(
prompt: str,
attempts_remaining: int | None,
allow_cancel: bool,
wrong_pin: bool = False,
-) -> Awaitable[str]:
+) -> str:
from trezor.wire import PinCancelled
if attempts_remaining is None:
@@ -2184,17 +2186,18 @@ def request_pin_on_device(
else:
attempts = f"{attempts_remaining} {TR.pin__tries_left}"
- result = interact(
- trezorui_api.request_pin(
- prompt=prompt,
- attempts=attempts,
- allow_cancel=allow_cancel,
- wrong_pin=wrong_pin,
- ),
- "pin_device",
- ButtonRequestType.PinEntry,
- raise_on_cancel=PinCancelled,
- )
+ with trezorui_api.request_pin(
+ prompt=prompt,
+ attempts=attempts,
+ allow_cancel=allow_cancel,
+ wrong_pin=wrong_pin,
+ ) as layout:
+ result = await interact(
+ layout,
+ "pin_device",
+ ButtonRequestType.PinEntry,
+ raise_on_cancel=PinCancelled,
+ )
return result # type: ignore ["UiResult" is not assignable to "str"]
@@ -2302,14 +2305,15 @@ async def success_pin_change(curpin: str | None, newpin: str | None) -> None:
await show_success("success_pin", msg_screen)
-def confirm_firmware_update(description: str, fingerprint: str) -> Awaitable[None]:
- return raise_if_not_confirmed(
- trezorui_api.confirm_firmware_update(
- description=description, fingerprint=fingerprint
- ),
- "firmware_update",
- BR_CODE_OTHER,
- )
+async def confirm_firmware_update(description: str, fingerprint: str) -> None:
+ with trezorui_api.confirm_firmware_update(
+ description=description, fingerprint=fingerprint
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ "firmware_update",
+ BR_CODE_OTHER,
+ )
async def set_brightness(current: int | None = None) -> None:
diff --git a/core/src/trezor/ui/layouts/delizia/recovery.py b/core/src/trezor/ui/layouts/delizia/recovery.py
index 9c22b030..f5cd863f 100644
--- a/core/src/trezor/ui/layouts/delizia/recovery.py
+++ b/core/src/trezor/ui/layouts/delizia/recovery.py
@@ -110,24 +110,25 @@ async def continue_recovery(
show_instructions: bool = False,
remaining_shares_info: "RemainingSharesInfo | None" = None,
) -> bool:
- result = await interact(
- trezorui_api.continue_recovery_homepage(
- text=text,
- subtext=subtext,
- button=None,
- recovery_type=recovery_type,
- show_instructions=show_instructions,
- remaining_shares=(
- format_remaining_shares_info(remaining_shares_info)
- if remaining_shares_info
- else None
- ),
+ with trezorui_api.continue_recovery_homepage(
+ text=text,
+ subtext=subtext,
+ button=None,
+ recovery_type=recovery_type,
+ show_instructions=show_instructions,
+ remaining_shares=(
+ format_remaining_shares_info(remaining_shares_info)
+ if remaining_shares_info
+ else None
),
- None,
- ButtonRequestType.Other,
- raise_on_cancel=None,
- )
- return result is CONFIRMED
+ ) as layout:
+ result = await interact(
+ layout,
+ None,
+ ButtonRequestType.Other,
+ raise_on_cancel=None,
+ )
+ return result is CONFIRMED
async def show_invalid_mnemonic(word_count: int) -> None:
diff --git a/core/src/trezor/ui/layouts/delizia/reset.py b/core/src/trezor/ui/layouts/delizia/reset.py
index 372e7cc6..adff0a21 100644
--- a/core/src/trezor/ui/layouts/delizia/reset.py
+++ b/core/src/trezor/ui/layouts/delizia/reset.py
@@ -154,19 +154,20 @@ async def _prompt_number(
max_count: int,
br_name: str,
) -> int:
- result = await interact(
- trezorui_api.request_number(
- title=title,
- count=count,
- min_count=min_count,
- max_count=max_count,
- description=description,
- more_info_callback=info,
- ),
- br_name,
- ButtonRequestType.ResetDevice,
- raise_on_cancel=None,
- )
+ with trezorui_api.request_number(
+ title=title,
+ count=count,
+ min_count=min_count,
+ max_count=max_count,
+ description=description,
+ more_info_callback=info,
+ ) as layout:
+ result = await interact(
+ layout,
+ br_name,
+ ButtonRequestType.ResetDevice,
+ raise_on_cancel=None,
+ )
if __debug__ and result is CONFIRMED:
# sent by debuglink. debuglink does not change the number of shares anyway
diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py
index 4bae744b..75bc5e37 100644
--- a/core/src/trezor/ui/layouts/eckhart/__init__.py
+++ b/core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -15,7 +15,7 @@ from ..common import (
if TYPE_CHECKING:
from buffer_types import AnyBytes, StrOrBytes
- from typing import Any, Awaitable, Coroutine, Iterable, NoReturn, Sequence, TypeVar
+ from typing import Awaitable, Iterable, NoReturn, Sequence, TypeVar
from trezor.messages import StellarAsset
from trezor.ui.layouts.menu import Details
@@ -98,16 +98,17 @@ async def prompt_recovery_check(recovery_type: RecoveryType) -> None:
async def show_wallet_created_success() -> None:
- await interact(
- trezorui_api.show_success(
- title=TR.words__title_done,
- description=TR.backup__new_wallet_created,
- button=TR.buttons__continue,
- allow_cancel=False,
- ),
- "backup_device",
- ButtonRequestType.ResetDevice,
- )
+ with trezorui_api.show_success(
+ title=TR.words__title_done,
+ description=TR.backup__new_wallet_created,
+ button=TR.buttons__continue,
+ allow_cancel=False,
+ ) as layout:
+ await interact(
+ layout,
+ "backup_device",
+ ButtonRequestType.ResetDevice,
+ )
async def prompt_backup() -> bool:
@@ -179,17 +180,16 @@ def lock_time_disabled_warning() -> Awaitable[None]:
)
-def confirm_homescreen(
- image: AnyBytes,
-) -> Awaitable[None]:
- return raise_if_not_confirmed(
- trezorui_api.confirm_homescreen(
- title=TR.homescreen__title_set,
- image=image,
- ),
- "set_homesreen",
- ButtonRequestType.ProtectCall,
- )
+async def confirm_homescreen(image: AnyBytes) -> None:
+ with trezorui_api.confirm_homescreen(
+ title=TR.homescreen__title_set,
+ image=image,
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ "set_homesreen",
+ ButtonRequestType.ProtectCall,
+ )
async def confirm_change_label(
@@ -418,25 +418,26 @@ def show_danger(
)
-def show_success(
+async def show_success(
br_name: str | None,
content: str,
subheader: str | None = None,
button: str | None = None,
time_ms: int = 0,
-) -> Coroutine[Any, Any, None]:
+) -> None:
button = button or TR.buttons__continue # def_arg
- return raise_if_not_confirmed(
- trezorui_api.show_success(
- title=subheader if subheader else TR.words__title_done,
- button=button,
- description=content,
- allow_cancel=False,
- time_ms=time_ms,
- ),
- br_name,
- ButtonRequestType.Success,
- )
+ with trezorui_api.show_success(
+ title=subheader if subheader else TR.words__title_done,
+ button=button,
+ description=content,
+ allow_cancel=False,
+ time_ms=time_ms,
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ br_name,
+ ButtonRequestType.Success,
+ )
def show_continue_in_app(content: str) -> None:
@@ -2238,38 +2239,37 @@ def error_popup(
def request_passphrase_on_host() -> None:
- draw_simple(
- trezorui_api.show_simple(
- title=None,
- text=TR.passphrase__please_enter,
- )
- )
+ with trezorui_api.show_simple(
+ title=None, text=TR.passphrase__please_enter
+ ) as layout:
+ draw_simple(layout)
def show_wait_text(message: str) -> None:
draw_simple(trezorui_api.show_wait_text(message))
-def request_passphrase_on_device(max_len: int) -> Awaitable[str]:
- result = interact(
- trezorui_api.request_passphrase(
- prompt=TR.passphrase__title_enter,
- prompt_empty=TR.passphrase__continue_with_empty_passphrase,
- max_len=max_len,
- ),
- "passphrase_device",
- ButtonRequestType.PassphraseEntry,
- raise_on_cancel=ActionCancelled("Passphrase entry cancelled"),
- )
+async def request_passphrase_on_device(max_len: int) -> str:
+ with trezorui_api.request_passphrase(
+ prompt=TR.passphrase__title_enter,
+ prompt_empty=TR.passphrase__continue_with_empty_passphrase,
+ max_len=max_len,
+ ) as layout:
+ result = await interact(
+ layout,
+ "passphrase_device",
+ ButtonRequestType.PassphraseEntry,
+ raise_on_cancel=ActionCancelled("Passphrase entry cancelled"),
+ )
return result # type: ignore ["UiResult" is not assignable to "str"]
-def request_pin_on_device(
+async def request_pin_on_device(
prompt: str,
attempts_remaining: int | None,
allow_cancel: bool,
wrong_pin: bool = False,
-) -> Awaitable[str]:
+) -> str:
from trezor.wire import PinCancelled
if attempts_remaining is None:
@@ -2282,18 +2282,19 @@ def request_pin_on_device(
attempts = f"{attempts_remaining}\n{TR.pin__tries_left}"
last_attempt = False
- result = interact(
- trezorui_api.request_pin(
- prompt=prompt,
- attempts=attempts,
- allow_cancel=allow_cancel,
- wrong_pin=wrong_pin,
- last_attempt=last_attempt,
- ),
- "pin_device",
- ButtonRequestType.PinEntry,
- raise_on_cancel=PinCancelled,
- )
+ with trezorui_api.request_pin(
+ prompt=prompt,
+ attempts=attempts,
+ allow_cancel=allow_cancel,
+ wrong_pin=wrong_pin,
+ last_attempt=last_attempt,
+ ) as layout:
+ result = await interact(
+ layout,
+ "pin_device",
+ ButtonRequestType.PinEntry,
+ raise_on_cancel=PinCancelled,
+ )
return result # type: ignore ["UiResult" is not assignable to "str"]
@@ -2400,14 +2401,15 @@ async def success_pin_change(curpin: str | None, newpin: str | None) -> None:
await show_success("success_pin", msg_screen, button=TR.buttons__close)
-def confirm_firmware_update(description: str, fingerprint: str) -> Awaitable[None]:
- return raise_if_not_confirmed(
- trezorui_api.confirm_firmware_update(
- description=description, fingerprint=fingerprint
- ),
- "firmware_update",
- BR_CODE_OTHER,
- )
+async def confirm_firmware_update(description: str, fingerprint: str) -> None:
+ with trezorui_api.confirm_firmware_update(
+ description=description, fingerprint=fingerprint
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ "firmware_update",
+ BR_CODE_OTHER,
+ )
def set_brightness(current: int | None = None) -> Awaitable[None]:
diff --git a/core/src/trezor/ui/layouts/eckhart/recovery.py b/core/src/trezor/ui/layouts/eckhart/recovery.py
index 0af393ef..1e6b05f6 100644
--- a/core/src/trezor/ui/layouts/eckhart/recovery.py
+++ b/core/src/trezor/ui/layouts/eckhart/recovery.py
@@ -114,24 +114,25 @@ async def continue_recovery(
show_instructions: bool = False,
remaining_shares_info: "RemainingSharesInfo | None" = None,
) -> bool:
- result = await interact(
- trezorui_api.continue_recovery_homepage(
- text=text,
- subtext=subtext,
- button=None,
- recovery_type=recovery_type,
- show_instructions=show_instructions,
- remaining_shares=(
- format_remaining_shares_info(remaining_shares_info)
- if remaining_shares_info
- else None
- ),
+ with trezorui_api.continue_recovery_homepage(
+ text=text,
+ subtext=subtext,
+ button=None,
+ recovery_type=recovery_type,
+ show_instructions=show_instructions,
+ remaining_shares=(
+ format_remaining_shares_info(remaining_shares_info)
+ if remaining_shares_info
+ else None
),
- None,
- ButtonRequestType.Other,
- raise_on_cancel=None,
- )
- return result is CONFIRMED
+ ) as layout:
+ result = await interact(
+ layout,
+ None,
+ ButtonRequestType.Other,
+ raise_on_cancel=None,
+ )
+ return result is CONFIRMED
async def show_invalid_mnemonic(word_count: int) -> None:
diff --git a/core/src/trezor/ui/layouts/eckhart/reset.py b/core/src/trezor/ui/layouts/eckhart/reset.py
index a793d039..0ac823d4 100644
--- a/core/src/trezor/ui/layouts/eckhart/reset.py
+++ b/core/src/trezor/ui/layouts/eckhart/reset.py
@@ -178,19 +178,20 @@ async def _prompt_number(
max_count: int,
br_name: str,
) -> int:
- result = await interact(
- trezorui_api.request_number(
- title=title,
- count=count,
- min_count=min_count,
- max_count=max_count,
- description=description,
- more_info_callback=info,
- ),
- br_name,
- ButtonRequestType.ResetDevice,
- raise_on_cancel=None,
- )
+ with trezorui_api.request_number(
+ title=title,
+ count=count,
+ min_count=min_count,
+ max_count=max_count,
+ description=description,
+ more_info_callback=info,
+ ) as layout:
+ result = await interact(
+ layout,
+ br_name,
+ ButtonRequestType.ResetDevice,
+ raise_on_cancel=None,
+ )
if __debug__ and result is CONFIRMED:
# sent by debuglink. debuglink does not change the number of shares anyway
diff --git a/core/src/trezor/wire/thp/ui.py b/core/src/trezor/wire/thp/ui.py
index 7e793aeb..ddeafebc 100644
--- a/core/src/trezor/wire/thp/ui.py
+++ b/core/src/trezor/wire/thp/ui.py
@@ -96,14 +96,12 @@ async def show_nfc_screen() -> UiResult:
from trezor.ui.layouts.common import interact
from trezorui_api import show_simple
- return await interact(
- show_simple(
- title=None,
- text=TR.thp__nfc_text,
- button=TR.buttons__cancel,
- ),
- br_name=None,
- )
+ with show_simple(
+ title=None,
+ text=TR.thp__nfc_text,
+ button=TR.buttons__cancel,
+ ) as layout:
+ return await interact(layout, br_name=None)
async def show_qr_code_screen(qr_code_str: str) -> UiResult:
Why this scored 11/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.