What changed, and why it matters
This commit is a routine code cleanup: it pulls a repeated user-interface loop into a shared helper function called confirm_linear_flow. There is no change to security behavior, no bug fix, and no indication of a vulnerability.
No action required; this is a non-security refactor.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The refactor extracts the ‘confirm address, then confirm amount, allow going back’ pattern into a reusable confirm_linear_flow helper in core/src/trezor/ui/layouts/common.py. The delizia and eckhart layout modules now import and call this helper instead of duplicating the while-loop logic. The control flow remains identical: CONFIRMED advances, BACK moves to the previous step if possible, and anything else raises ActionCancelled. No functional or security change is introduced.
Changed components
core/src/trezor/ui/layouts/common.pycore/src/trezor/ui/layouts/delizia/__init__.pycore/src/trezor/ui/layouts/eckhart/__init__.pyInspect captured patch +62 / −40
diff --git a/core/src/trezor/ui/layouts/common.py b/core/src/trezor/ui/layouts/common.py
index 10a74489a..f68caf061 100644
--- a/core/src/trezor/ui/layouts/common.py
+++ b/core/src/trezor/ui/layouts/common.py
@@ -134,5 +134,20 @@ async def with_info(
raise RuntimeError # unexpected result
+async def confirm_linear_flow(
+ *confirm_factories: Callable[[], Awaitable[ui.UiResult]]
+) -> None:
+ i = 0
+ while i < len(confirm_factories):
+ layout = confirm_factories[i]()
+ res = await layout
+ if res is trezorui_api.CONFIRMED:
+ i += 1
+ elif res is trezorui_api.BACK and i > 0:
+ i -= 1
+ else:
+ raise ActionCancelled
+
+
def draw_simple(layout: trezorui_api.LayoutObj[Any]) -> None:
ui.Layout(layout).start()
diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py
index b934ef284..78fa39153 100644
--- a/core/src/trezor/ui/layouts/delizia/__init__.py
+++ b/core/src/trezor/ui/layouts/delizia/__init__.py
@@ -5,7 +5,13 @@ 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
+from ..common import (
+ confirm_linear_flow,
+ draw_simple,
+ interact,
+ raise_if_not_confirmed,
+ with_info,
+)
if TYPE_CHECKING:
from buffer_types import AnyBytes, StrOrBytes
@@ -615,8 +621,8 @@ async def confirm_output(
]
else:
info_items = []
- while True:
- await confirm_value(
+ await confirm_linear_flow(
+ lambda: confirm_value(
TR.words__address,
address,
description or "",
@@ -626,8 +632,8 @@ async def confirm_output(
chunkify=chunkify,
cancel_text=TR.send__cancel_sign,
info_items=info_items,
- )
- amount_response = await confirm_value(
+ ),
+ lambda: confirm_value(
TR.words__amount,
amount,
description="",
@@ -637,11 +643,8 @@ async def confirm_output(
cancel_text=TR.send__cancel_sign,
info_items=info_items,
can_go_back=True,
- )
- if amount_response is BACK:
- continue
- else:
- break
+ ),
+ )
else:
await raise_if_not_confirmed(
trezorui_api.flow_confirm_output(
diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py
index 5d3cd53d9..f9fc8cfb6 100644
--- a/core/src/trezor/ui/layouts/eckhart/__init__.py
+++ b/core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -5,7 +5,13 @@ 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
+from ..common import (
+ confirm_linear_flow,
+ draw_simple,
+ interact,
+ raise_if_not_confirmed,
+ with_info,
+)
if TYPE_CHECKING:
from buffer_types import AnyBytes, StrOrBytes
@@ -576,7 +582,7 @@ async def confirm_output(
cancel_text: str | None = None,
description: str | None = None,
) -> None:
- from trezor.ui.layouts.menu import Menu, confirm_with_menu, interact_with_menu
+ from trezor.ui.layouts.menu import Menu, interact_with_menu
if address_label is not None:
title = address_label
@@ -610,39 +616,37 @@ async def confirm_output(
]
else:
menu_items = []
+
menu = Menu.root(
menu_items,
cancel=TR.buttons__cancel,
)
- while True:
- address_layout = trezorui_api.confirm_value(
- title=TR.words__send,
- value=address,
- description=description,
- subtitle=title,
- verb=TR.buttons__continue,
- chunkify=chunkify,
- page_counter=True, # TODO: this is for test_cardano_sign_tx_show_details - maybe we can do without?
- external_menu=True,
- )
- await confirm_with_menu(address_layout, menu, "confirm_output", br_code)
- amount_layout = trezorui_api.confirm_value(
- title=TR.words__send,
- value=amount,
- description=TR.words__amount,
- is_data=False,
- subtitle=title,
- external_menu=True,
- back_button=True,
- )
- amount_response = await interact_with_menu(
- amount_layout, menu, "confirm_output", br_code
- )
- if amount_response is BACK:
- continue
- else:
- break
+ address_layout = trezorui_api.confirm_value(
+ title=TR.words__send,
+ value=address,
+ description=description,
+ subtitle=title,
+ verb=TR.buttons__continue,
+ chunkify=chunkify,
+ page_counter=True, # TODO: this is for test_cardano_sign_tx_show_details - maybe we can do without?
+ external_menu=True,
+ )
+
+ amount_layout = trezorui_api.confirm_value(
+ title=TR.words__send,
+ value=amount,
+ description=TR.words__amount,
+ is_data=False,
+ subtitle=title,
+ external_menu=True,
+ back_button=True,
+ )
+
+ await confirm_linear_flow(
+ lambda: interact_with_menu(address_layout, menu, "confirm_output", br_code),
+ lambda: interact_with_menu(amount_layout, menu, "confirm_output", br_code),
+ )
else:
await raise_if_not_confirmed(
trezorui_api.flow_confirm_output(
Why this scored 15/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.