refactor(core/ethereum): make `ConfirmDataFn` a class
What changed, and why it matters
This commit is a straightforward internal code cleanup in the Ethereum transaction signing flow. It turns a callback-creating helper function into a class and moves where that object is created. There is no change to user-visible behavior, security checks, or cryptographic handling.
No security action required. Treat as a normal maintainability refactor during code review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors get_data_confirmer() and get_progress_indicator() in core/src/apps/ethereum/helpers.py into a single DataChunkConfirmer class. _confirm_data_chunks() in core/src/apps/ethereum/sign_tx.py now instantiates this class locally instead of receiving a pre-built callback. The progress callback is made synchronous (non-async). The diff shows only structural reorganization; the control flow, UI prompts, cancellation behavior, and data hashing remain identical.
Changed components
core/src/apps/ethereum/helpers.pycore/src/apps/ethereum/sign_tx.pyInspect captured patch +48 / −55
diff --git a/core/src/apps/ethereum/helpers.py b/core/src/apps/ethereum/helpers.py
index 0f7e0bea..e1bfd4e5 100644
--- a/core/src/apps/ethereum/helpers.py
+++ b/core/src/apps/ethereum/helpers.py
@@ -15,8 +15,6 @@ if TYPE_CHECKING:
from .networks import EthereumNetworkInfo
- ConfirmDataFn = Callable[[AnyBytes], Awaitable[None]]
-
# Fetch the next calldata chunk from host.
# `data_left: int` argument is provided.
DataChunkLoader = Callable[[int], Awaitable[AnyBytes]]
@@ -233,46 +231,24 @@ def _from_bytes_bigendian_signed(b: AnyBytes) -> int:
return int.from_bytes(b, "big")
-def get_progress_indicator(total_len: int, progress_len: int = 0) -> ConfirmDataFn:
- from trezor.ui.layouts.progress import progress
-
- def _progress_value() -> int:
- assert 0 <= progress_len <= total_len
- if total_len == 0:
- return 1000
- return (1000 * progress_len) // total_len
-
- layout = progress(title=TR.progress__loading_transaction)
- layout.value = _progress_value()
-
- async def confirm_fn(chunk: AnyBytes) -> None:
- nonlocal progress_len
- progress_len += len(chunk)
- layout.report(_progress_value())
-
- return confirm_fn
-
+class DataChunkConfirmer:
+ def __init__(self, total_len: int) -> None:
+ self.total_len = total_len
+ self.confirmed_len = 0
+ self.progress_bar = None
+ self.first: bool = True
-def get_data_confirmer(total_len: int) -> ConfirmDataFn:
- from trezor.enums import ButtonRequestType
- from trezor.ui.layouts import confirm_blob_intro, confirm_blob_prefix
+ async def confirm(self, chunk: AnyBytes) -> None:
+ from trezor.enums import ButtonRequestType
+ from trezor.ui.layouts import confirm_blob_intro, confirm_blob_prefix
- confirmed_len = 0
- progress_bar: ConfirmDataFn | None = None
- first: bool = True
-
- async def confirm_fn(chunk: AnyBytes) -> None:
- nonlocal confirmed_len
- nonlocal progress_bar
- nonlocal first
-
- if first:
- first = False
+ if self.first:
+ self.first = False
# show intro layout
skip = await confirm_blob_intro(
title=TR.ethereum__title_input_data,
value=chunk,
- subtitle=TR.ethereum__data_size_template.format(total_len),
+ subtitle=TR.ethereum__data_size_template.format(self.total_len),
verb=TR.buttons__confirm,
verb_cancel=TR.send__cancel_sign,
br_name="confirm_data",
@@ -280,34 +256,50 @@ def get_data_confirmer(total_len: int) -> ConfirmDataFn:
)
if skip:
# skip following chunks confirmation - use a progress bar instead
- progress_bar = get_progress_indicator(total_len, progress_len=0)
+ self.progress_bar = self._get_progress_indicator()
- if progress_bar is not None:
- return await progress_bar(chunk)
+ if self.progress_bar is not None:
+ return self.progress_bar(chunk)
# for efficient chunk slicing (see below)
chunk = memoryview(chunk)
while True:
- assert 0 <= confirmed_len <= total_len
+ assert 0 <= self.confirmed_len <= self.total_len
prefix_len = await confirm_blob_prefix(
data=chunk,
- total_len=total_len,
- confirmed_len=confirmed_len,
+ total_len=self.total_len,
+ confirmed_len=self.confirmed_len,
br_name="confirm_data",
br_code=ButtonRequestType.SignTx,
)
if prefix_len is None:
# skip this and following chunks confirmation - use a progress bar instead
- assert progress_bar is None
- progress_bar = get_progress_indicator(total_len, confirmed_len)
- return await progress_bar(chunk)
+ assert self.progress_bar is None
+ self.progress_bar = self._get_progress_indicator()
+ return self.progress_bar(chunk)
else:
- confirmed_len += prefix_len
+ self.confirmed_len += prefix_len
chunk = chunk[prefix_len:]
if not chunk:
return
- return confirm_fn
+ def _get_progress_indicator(self) -> Callable[[AnyBytes], None]:
+ from trezor.ui.layouts.progress import progress
+
+ def _progress_value() -> int:
+ assert 0 <= self.confirmed_len <= self.total_len
+ if self.total_len == 0:
+ return 1000
+ return (1000 * self.confirmed_len) // self.total_len
+
+ layout = progress(title=TR.progress__loading_transaction)
+ layout.value = _progress_value()
+
+ def confirm_fn(chunk: AnyBytes) -> None:
+ self.confirmed_len += len(chunk)
+ layout.report(_progress_value())
+
+ return confirm_fn
def keccak256(data: AnyBytes | None = None) -> HashWriter:
diff --git a/core/src/apps/ethereum/sign_tx.py b/core/src/apps/ethereum/sign_tx.py
index 9edfc910..78abed84 100644
--- a/core/src/apps/ethereum/sign_tx.py
+++ b/core/src/apps/ethereum/sign_tx.py
@@ -6,7 +6,7 @@ from trezor.crypto import rlp
from trezor.messages import EthereumTxRequest
from trezor.wire import DataError
-from .helpers import address_from_bytes, bytes_from_address, get_data_confirmer
+from .helpers import address_from_bytes, bytes_from_address
from .keychain import with_keychain_from_chain_id
if TYPE_CHECKING:
@@ -21,7 +21,7 @@ if TYPE_CHECKING:
from apps.common.payment_request import PaymentRequestVerifier
from .definitions import Definitions
- from .helpers import ConfirmDataFn, DataChunkLoader
+ from .helpers import DataChunkLoader
from .keychain import MsgInSignTx
@@ -238,7 +238,6 @@ async def confirm_tx_data(
if data_length > 0:
# Stream, confirm and hash the rest of the calldata chunks.
await _confirm_data_chunks(
- get_data_confirmer(data_length),
initial_data,
data_length,
data_chunk_loader,
@@ -293,18 +292,20 @@ def create_data_chunk_loader(h: HashWriter) -> DataChunkLoader:
async def _confirm_data_chunks(
- confirm_data_chunk: ConfirmDataFn,
initial_data: AnyBytes,
data_length: int,
data_chunk_loader: DataChunkLoader,
) -> None:
- await confirm_data_chunk(initial_data)
+ from .helpers import DataChunkConfirmer
+
+ data_chunk_confirmer = DataChunkConfirmer(data_length)
+ await data_chunk_confirmer.confirm(initial_data)
data_left = data_length - len(initial_data)
while data_left > 0:
chunk = await data_chunk_loader(data_left)
- # `confirm_data_chunk` will raise on cancellation, so
- # `data_chunk_loader`-computed hash will be discarded.
- await confirm_data_chunk(chunk)
+ # `data_chunk_confirmer.confirm` will raise on cancellation,
+ # so `data_chunk_loader`-computed hash will be discarded.
+ await data_chunk_confirmer.confirm(chunk)
data_left -= len(chunk)
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.