chore(trezorlib): refactor device definition requests handling
What changed, and why it matters
This is a routine internal code cleanup in the Python Trezor library. It moves the logic that answers the hardware wallet's mid-transaction definition requests from one module to another and changes the public API from accepting a callback function to accepting a definition source object. There is no indication this fixes or introduces a security vulnerability; it is a refactor to make the code easier to test and maintain.
No security action required. Treat as a normal refactor: verify downstream consumers of `trezorlib.ethereum.sign_tx` and `sign_tx_eip1559` that previously passed `definition_provider` are updated to pass `definition_source` instead, since this is a breaking API change in the library.
Security signals we found
No security-relevant behavior change: the same request/response flow and cancellation-on-exception logic is preserved.
API surface change: parameter renamed from `definition_provider` to `definition_source` and type changed from callable to `definitions.Source`.
Test refactor: callbacks replaced with mocked `Source` objects to assert which source methods are called.
No new parsing, no new trust assumptions, no relaxed validation, and no new network or filesystem access introduced.
Evidence from the diff
The commit refactors how trezorlib handles EthereumDefinitionRequest messages from the firmware during Ethereum transaction signing. Previously, definitions.py exposed definition_provider(source, req) and callers passed a functools.partial callback to ethereum.sign_tx / sign_tx_eip1559. The refactor moves the request-answering logic into ethereum.py as _answer_definition_request(source, req) and changes the API parameter from definition_provider (a callable) to definition_source (a definitions.Source instance). Tests are updated to use unittest.mock.Mock objects implementing the Source interface instead of hand-written callback functions. The behavior of the signing loop is unchanged: it still answers display-format and network/token requests from the source, cancels on exceptions, and falls back to EthereumDefinitionAck(definitions=None) when no source is provided.
Changed components
python/src/trezorlib/cli/ethereum.pypython/src/trezorlib/definitions.pypython/src/trezorlib/ethereum.pytests/device_tests/ethereum/test_definitions_bad.pytests/device_tests/ethereum/test_definitions_request.pytests/device_tests/ethereum/test_signtx.pyInspect captured patch +169 / −213
diff --git a/python/src/trezorlib/cli/ethereum.py b/python/src/trezorlib/cli/ethereum.py
index e97c5f4c..33e27851 100644
--- a/python/src/trezorlib/cli/ethereum.py
+++ b/python/src/trezorlib/cli/ethereum.py
@@ -14,7 +14,6 @@
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
-import functools
import json
import re
import sys
@@ -230,14 +229,6 @@ def _network_def_from_address_n(address_n: tools.Address) -> Optional[bytes]:
return DEFINITIONS_SOURCE.get_eth_network_by_slip44(slip44)
-# Answers firmware `EthereumDefinitionRequest`s from `DEFINITIONS_SOURCE`.
-# `DEFINITIONS_SOURCE` is mutated in place by the group callback, so binding the
-# object here still sees the delegate/overrides configured on the command line.
-_definition_provider = functools.partial(
- definitions.definition_provider, DEFINITIONS_SOURCE
-)
-
-
#####################
#
# commands start here
@@ -505,7 +496,7 @@ def sign_tx(
definitions=defs,
chunkify=chunkify,
supports_definition_request=True,
- definition_provider=_definition_provider,
+ definition_source=DEFINITIONS_SOURCE,
)
else:
if gas_price is None:
@@ -525,7 +516,7 @@ def sign_tx(
definitions=defs,
chunkify=chunkify,
supports_definition_request=True,
- definition_provider=_definition_provider,
+ definition_source=DEFINITIONS_SOURCE,
)
to = ethereum.decode_hex(to_address)
diff --git a/python/src/trezorlib/definitions.py b/python/src/trezorlib/definitions.py
index 5d807c9a..01a1ba24 100644
--- a/python/src/trezorlib/definitions.py
+++ b/python/src/trezorlib/definitions.py
@@ -25,12 +25,7 @@ from construct_classes import Struct, subcon
from . import cosi, merkle_tree
from .construct_helpers import EnumAdapter
-from .messages import (
- DefinitionType,
- EthereumDefinitionAck,
- EthereumDefinitionRequest,
- EthereumDefinitions,
-)
+from .messages import DefinitionType
LOG = logging.getLogger(__name__)
@@ -193,44 +188,3 @@ class TarSource(Source):
except Exception:
LOG.info("Requested definition at %s was not found", inner_name)
return None
-
-
-def definition_provider(
- source: Source,
- req: EthereumDefinitionRequest,
-) -> EthereumDefinitionAck:
- """Answer a firmware `EthereumDefinitionRequest` from `source`.
-
- The firmware issues these mid-flow while signing a transaction:
-
- - With a `func_sig`, it is asking for an ERC-7730 contract descriptor
- (clear-signing display format) for `token_address` on `chain_id`.
- - Without a `func_sig`, it is asking for a network + token definition
- (e.g. to resolve a token referenced by a descriptor field).
- """
- if req.func_sig:
- encoded_display_format = source.get_eth_display_format(
- req.chain_id, req.token_address, req.func_sig
- )
- if encoded_display_format is None:
- return EthereumDefinitionAck(definitions=None)
- return EthereumDefinitionAck(
- definitions=EthereumDefinitions(
- encoded_display_format=encoded_display_format,
- )
- )
-
- encoded_network = source.get_eth_network(req.chain_id)
- encoded_token = (
- source.get_eth_token(req.chain_id, req.token_address)
- if req.token_address is not None
- else None
- )
- if encoded_network is None and encoded_token is None:
- return EthereumDefinitionAck(definitions=None)
- return EthereumDefinitionAck(
- definitions=EthereumDefinitions(
- encoded_network=encoded_network,
- encoded_token=encoded_token,
- )
- )
diff --git a/python/src/trezorlib/ethereum.py b/python/src/trezorlib/ethereum.py
index 0037552e..8bcd8679 100644
--- a/python/src/trezorlib/ethereum.py
+++ b/python/src/trezorlib/ethereum.py
@@ -15,13 +15,14 @@
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import re
-from typing import TYPE_CHECKING, Any, AnyStr, Callable, Dict, List, Optional, Tuple
+from typing import TYPE_CHECKING, Any, AnyStr, Dict, List, Optional, Tuple
from . import exceptions, messages
from .tools import prepare_message_bytes, workflow
if TYPE_CHECKING:
from .client import Session
+ from .definitions import Source
from .tools import Address
@@ -179,15 +180,46 @@ def get_public_node(
)
+def _answer_definition_request(
+ source: "Source",
+ req: messages.EthereumDefinitionRequest,
+) -> messages.EthereumDefinitionAck:
+ """Answer a firmware `EthereumDefinitionRequest` from `source`."""
+ if req.func_sig:
+ encoded_display_format = source.get_eth_display_format(
+ req.chain_id, req.token_address, req.func_sig
+ )
+ if encoded_display_format is None:
+ return messages.EthereumDefinitionAck(definitions=None)
+ return messages.EthereumDefinitionAck(
+ definitions=messages.EthereumDefinitions(
+ encoded_display_format=encoded_display_format,
+ )
+ )
+
+ encoded_network = source.get_eth_network(req.chain_id)
+ encoded_token = (
+ source.get_eth_token(req.chain_id, req.token_address)
+ if req.token_address is not None
+ else None
+ )
+ if encoded_network is None and encoded_token is None:
+ return messages.EthereumDefinitionAck(definitions=None)
+ return messages.EthereumDefinitionAck(
+ definitions=messages.EthereumDefinitions(
+ encoded_network=encoded_network,
+ encoded_token=encoded_token,
+ )
+ )
+
+
def _ethereum_sign_loop(
session: "Session",
msg_type: type,
response: Any,
data: bytes,
chain_id: int,
- definition_provider: Optional[
- Callable[[messages.EthereumDefinitionRequest], messages.EthereumDefinitionAck]
- ],
+ definition_source: Optional["Source"],
) -> Tuple[int, bytes, bytes]:
"""Shared request/response loop for sign_tx and sign_tx_eip1559."""
while True:
@@ -212,9 +244,10 @@ def _ethereum_sign_loop(
response = session.call(messages.EthereumTxAck(data_chunk=chunk))
elif isinstance(response, messages.EthereumDefinitionRequest):
# We are being asked for a function definition.
- if definition_provider is not None:
+ if definition_source is not None:
try:
- ack = definition_provider(response)
+ # Response is a request for definitions.
+ ack = _answer_definition_request(definition_source, response)
except Exception:
session.cancel()
raise
@@ -243,9 +276,7 @@ def sign_tx(
chunkify: bool = False,
payment_req: Optional[messages.PaymentRequest] = None,
supports_definition_request: Optional[bool] = None,
- definition_provider: Optional[
- Callable[[messages.EthereumDefinitionRequest], messages.EthereumDefinitionAck]
- ] = None,
+ definition_source: Optional["Source"] = None,
) -> Tuple[int, bytes, bytes]:
if chain_id is None:
raise exceptions.TrezorException("Chain ID cannot be undefined")
@@ -275,7 +306,7 @@ def sign_tx(
response = session.call(msg)
return _ethereum_sign_loop(
- session, messages.EthereumSignTx, response, data, chain_id, definition_provider
+ session, messages.EthereumSignTx, response, data, chain_id, definition_source
)
@@ -297,9 +328,7 @@ def sign_tx_eip1559(
chunkify: bool = False,
payment_req: Optional[messages.PaymentRequest] = None,
supports_definition_request: Optional[bool] = None,
- definition_provider: Optional[
- Callable[[messages.EthereumDefinitionRequest], messages.EthereumDefinitionAck]
- ] = None,
+ definition_source: Optional["Source"] = None,
) -> Tuple[int, bytes, bytes]:
length = len(data)
data, chunk = data[1024:], data[:1024]
@@ -329,7 +358,7 @@ def sign_tx_eip1559(
response,
data,
chain_id,
- definition_provider,
+ definition_source,
)
diff --git a/tests/device_tests/ethereum/test_definitions_bad.py b/tests/device_tests/ethereum/test_definitions_bad.py
index 9e0f5ed9..dbb2f42e 100644
--- a/tests/device_tests/ethereum/test_definitions_bad.py
+++ b/tests/device_tests/ethereum/test_definitions_bad.py
@@ -1,11 +1,13 @@
from __future__ import annotations
from hashlib import sha256
+from unittest.mock import Mock
import pytest
from trezorlib import ethereum, messages, models
from trezorlib.debuglink import DebugSession as Session
+from trezorlib.definitions import Source
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import DefinitionType
from trezorlib.tools import parse_path
@@ -61,28 +63,26 @@ def _fails_display_format(session: Session, display_format: bytes, match: str) -
def _fails_display_format_via_request(
session: Session, display_format: bytes, match: str
) -> None:
- calls: list[messages.EthereumDefinitionRequest] = []
-
- def provider(
- req: messages.EthereumDefinitionRequest,
- ) -> messages.EthereumDefinitionAck:
- calls.append(req)
- return messages.EthereumDefinitionAck(
- definitions=messages.EthereumDefinitions(
- encoded_display_format=display_format,
- )
- )
+ # A spying `Source` serving the invalid display format under test. The explicit
+ # `return_value`s matter: an unconfigured `Mock` returns a `Mock`, which would be
+ # put in `EthereumDefinitions` and only fail later, while encoding the reply.
+ source = Mock(
+ spec_set=Source,
+ get_eth_network=Mock(return_value=None),
+ get_eth_token=Mock(return_value=None),
+ get_eth_display_format=Mock(return_value=display_format),
+ )
with pytest.raises(TrezorFailure, match=match):
ethereum.sign_tx(
session,
**get_clear_signing_sign_tx_params(supports_definition_request=True),
- definition_provider=provider,
+ definition_source=source,
)
# Firmware requests the display format once then fails validation. No token requests follow.
- assert len(calls) == 1
- assert calls[0].func_sig is not None
+ source.get_eth_display_format.assert_called_once()
+ source.get_eth_token.assert_not_called()
def _make_token_payload(
diff --git a/tests/device_tests/ethereum/test_definitions_request.py b/tests/device_tests/ethereum/test_definitions_request.py
index d3e0208b..f9d5669b 100644
--- a/tests/device_tests/ethereum/test_definitions_request.py
+++ b/tests/device_tests/ethereum/test_definitions_request.py
@@ -2,11 +2,13 @@ from __future__ import annotations
from binascii import hexlify
from typing import Callable
+from unittest.mock import Mock
import pytest
from trezorlib import ethereum, messages
from trezorlib.debuglink import DebugSession as Session
+from trezorlib.definitions import Source
from trezorlib.tools import parse_path
from ... import definitions
@@ -27,48 +29,60 @@ from .test_definitions import (
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum, pytest.mark.models("core")]
-def _make_display_format_definition_provider(
- display_format_requests: list,
- token_requests: list,
+def _addr_hex(address: bytes) -> str:
+ return hexlify(address).decode("ascii").lower()
+
+
+def _encode_network(chain_id: int) -> bytes:
+ return definitions.encode_eth_network(chain_id=chain_id)
+
+
+def _mock_source(**methods: Mock) -> Mock:
+ """A spying `Source` that serves no definitions unless told otherwise.
+
+ A firmware `EthereumDefinitionRequest` reaches a `Source` as a method call --
+ a clear-signing display format as `get_eth_display_format`, a token definition
+ as `get_eth_token` (whose network is fetched alongside it) -- so what the
+ firmware asked for is asserted via `assert_called_*` / `call_args_list`.
+
+ Every method needs an explicit `return_value`: an unconfigured `Mock` returns
+ a `Mock`, which would be put in `EthereumDefinitions` and only fail later,
+ while encoding the reply.
+ """
+ source = Mock(
+ spec_set=Source,
+ get_eth_network=Mock(return_value=None),
+ get_eth_token=Mock(return_value=None),
+ get_eth_display_format=Mock(return_value=None),
+ )
+ source.configure_mock(**methods)
+ return source
+
+
+def _display_format_source(
display_format_info: messages.EthereumDisplayFormatInfo,
token_definitions: dict[str, dict] | None = None,
-) -> Callable[[messages.EthereumDefinitionRequest], messages.EthereumDefinitionAck]:
+) -> Mock:
+ """A spying `Source` serving one display format, plus tokens keyed by address."""
if token_definitions is None:
token_definitions = {
WETH_TOKEN_DEFINITION["address"][2:].lower(): WETH_TOKEN_DEFINITION
}
- def provider(
- req: messages.EthereumDefinitionRequest,
- ) -> messages.EthereumDefinitionAck:
- if not req.func_sig:
- # No func_sig means the firmware is requesting a token/network definition
- # only (e.g. from `TokenAmountFormatter` during field formatting).
- token_requests.append(req)
- addr = hexlify(req.token_address).decode("ascii").lower()
- token_def = token_definitions.get(addr)
- assert token_def is not None, f"Unexpected token request for {addr}"
- assert req.chain_id == token_def["chain_id"]
-
- return messages.EthereumDefinitionAck(
- definitions=messages.EthereumDefinitions(
- encoded_network=definitions.encode_eth_network(
- chain_id=token_def["chain_id"]
- ),
- encoded_token=definitions.encode_eth_token(**token_def),
- ),
- )
- else: # Display format was requested.
- display_format_requests.append(req)
- return messages.EthereumDefinitionAck(
- definitions=messages.EthereumDefinitions(
- encoded_display_format=definitions.encode_eth_display_format(
- display_format_info
- )
- ),
- )
-
- return provider
+ def get_eth_token(chain_id: int, address: bytes) -> bytes:
+ addr = _addr_hex(address)
+ token_def = token_definitions.get(addr)
+ assert token_def is not None, f"Unexpected token request for {addr}"
+ assert chain_id == token_def["chain_id"]
+ return definitions.encode_eth_token(**token_def)
+
+ return _mock_source(
+ get_eth_network=Mock(side_effect=_encode_network),
+ get_eth_token=Mock(side_effect=get_eth_token),
+ get_eth_display_format=Mock(
+ return_value=definitions.encode_eth_display_format(display_format_info)
+ ),
+ )
def test_definition_request_sent(session: Session) -> None:
@@ -77,12 +91,6 @@ def test_definition_request_sent(session: Session) -> None:
# Verify it is called with the right fields and that signing completes
# without clear signing when we reply with no definition.
- def provider(
- req: messages.EthereumDefinitionRequest,
- ) -> messages.EthereumDefinitionAck:
- definition_requests.append(req)
- return messages.EthereumDefinitionAck(definitions=None)
-
for sign_tx, param_getter in [
(ethereum.sign_tx, get_clear_signing_sign_tx_params),
(ethereum.sign_tx_eip1559, get_clear_signing_sign_tx_eip1559_params),
@@ -93,7 +101,7 @@ def test_definition_request_sent(session: Session) -> None:
| {"WETH", "USDT", "UNKN"},
)
- definition_requests: list[messages.EthereumDefinitionRequest] = []
+ source = _mock_source()
with session.test_ctx as client:
if not session.debug.legacy_debug:
client.set_input_flow(
@@ -102,14 +110,13 @@ def test_definition_request_sent(session: Session) -> None:
sign_tx(
session,
**param_getter(supports_definition_request=True),
- definition_provider=provider,
+ definition_source=source,
)
- assert len(definition_requests) == 1
- req = definition_requests[0]
- assert req.chain_id == 1
- assert req.token_address == bytes.fromhex(UNISWAP_V3_ROUTER2[2:].lower())
- assert req.func_sig == FUNC_SIG_FAKE
+ source.get_eth_display_format.assert_called_once_with(
+ 1, bytes.fromhex(UNISWAP_V3_ROUTER2[2:]), FUNC_SIG_FAKE
+ )
+ source.get_eth_token.assert_not_called()
assert_all_seen()
@@ -118,12 +125,6 @@ def test_definition_request_not_sent(session: Session) -> None:
# When clear signing data is present the firmware does not request a display format
# mid-flow via EthereumDefinitionRequest if the host did not signal that it supports that.
- def provider(
- req: messages.EthereumDefinitionRequest,
- ) -> messages.EthereumDefinitionAck:
- definition_requests.append(req)
- return messages.EthereumDefinitionAck(definitions=None)
-
for sign_tx, param_getter in [
(ethereum.sign_tx, get_clear_signing_sign_tx_params),
(ethereum.sign_tx_eip1559, get_clear_signing_sign_tx_eip1559_params),
@@ -134,7 +135,7 @@ def test_definition_request_not_sent(session: Session) -> None:
| {"WETH", "USDT", "UNKN"},
)
- definition_requests: list[messages.EthereumDefinitionRequest] = []
+ source = _mock_source()
with session.test_ctx as client:
if not session.debug.legacy_debug:
client.set_input_flow(
@@ -143,10 +144,11 @@ def test_definition_request_not_sent(session: Session) -> None:
sign_tx(
session,
**param_getter(supports_definition_request=False),
- definition_provider=provider,
+ definition_source=source,
)
- assert len(definition_requests) == 0
+ source.get_eth_display_format.assert_not_called()
+ source.get_eth_token.assert_not_called()
assert_all_seen()
@@ -165,8 +167,7 @@ def test_definition_request_with_display_format(session: Session) -> None:
absent={"UNKN"},
)
- display_format_requests: list[messages.EthereumDefinitionRequest] = []
- token_requests: list[messages.EthereumDefinitionRequest] = []
+ source = _display_format_source(UNISWAP_EXACT_INPUT_SINGLE_DISPLAY_FORMAT)
with session.test_ctx as client:
if not session.debug.legacy_debug:
client.set_input_flow(
@@ -175,15 +176,12 @@ def test_definition_request_with_display_format(session: Session) -> None:
sign_tx(
session,
**param_getter(supports_definition_request=True),
- definition_provider=_make_display_format_definition_provider(
- display_format_requests,
- token_requests,
- UNISWAP_EXACT_INPUT_SINGLE_DISPLAY_FORMAT,
- ),
+ definition_source=source,
)
assert_all_seen()
- assert len(display_format_requests) == 1
- assert len(token_requests) == 1 # WETH requested, built in USDT not requested
+ source.get_eth_display_format.assert_called_once()
+ # WETH requested, built in USDT not requested
+ assert source.get_eth_token.call_count == 1
def test_definition_request_with_invalid_display_format(session: Session) -> None:
@@ -224,8 +222,7 @@ def test_definition_request_with_invalid_display_format(session: Session) -> Non
| {"UNKN", "WETH", "USDT"},
)
- display_format_requests: list[messages.EthereumDefinitionRequest] = []
- token_requests: list[messages.EthereumDefinitionRequest] = []
+ source = _display_format_source(bad_display_format)
with session.test_ctx as client:
if not session.debug.legacy_debug:
client.set_input_flow(
@@ -234,13 +231,11 @@ def test_definition_request_with_invalid_display_format(session: Session) -> Non
sign_tx(
session,
**param_getter(supports_definition_request=True),
- definition_provider=_make_display_format_definition_provider(
- display_format_requests, token_requests, bad_display_format
- ),
+ definition_source=source,
)
- assert len(display_format_requests) == 1
- assert len(token_requests) == 0
+ source.get_eth_display_format.assert_called_once()
+ source.get_eth_token.assert_not_called()
assert_all_seen()
@@ -260,8 +255,9 @@ def test_definition_request_two_tokens(session: Session) -> None:
| {"FAKE WETH", "FAKE WETH2"},
absent={"UNKN"},
)
- display_format_requests: list[messages.EthereumDefinitionRequest] = []
- token_requests: list[messages.EthereumDefinitionRequest] = []
+ source = _display_format_source(
+ UNISWAP_EXACT_INPUT_SINGLE_DISPLAY_FORMAT, token_definitions=token_defs
+ )
with session.test_ctx as client:
if not session.debug.legacy_debug:
client.set_input_flow(
@@ -273,16 +269,11 @@ def test_definition_request_two_tokens(session: Session) -> None:
data=UNISWAP_WETH_WETH2_CALLDATA,
supports_definition_request=True,
),
- definition_provider=_make_display_format_definition_provider(
- display_format_requests,
- token_requests,
- UNISWAP_EXACT_INPUT_SINGLE_DISPLAY_FORMAT,
- token_definitions=token_defs,
- ),
+ definition_source=source,
)
assert_all_seen()
- assert len(display_format_requests) == 1
- assert len(token_requests) == 2
+ source.get_eth_display_format.assert_called_once()
+ assert source.get_eth_token.call_count == 2
# --- Test descriptor (debug-only built-ins) token-definition handling ---
@@ -347,9 +338,7 @@ TEST_DESCRIPTOR_CALLDATAS = (TEST_TOKEN_CALLDATA, TEST_PATHS_CALLDATA)
def _test_descriptor_sign_tx(
session: Session,
- provider: Callable[
- [messages.EthereumDefinitionRequest], messages.EthereumDefinitionAck
- ],
+ source: Source,
calldata: bytes,
on_page: Callable | None = None,
) -> None:
@@ -376,66 +365,62 @@ def _test_descriptor_sign_tx(
)
),
supports_definition_request=True,
- definition_provider=provider,
+ definition_source=source,
+ )
+
+
+def _any_token_source() -> Mock:
+ """A spying `Source` resolving every requested token to a fake definition."""
+
+ def get_eth_token(chain_id: int, address: bytes) -> bytes:
+ return definitions.encode_eth_token(
+ address="0x" + _addr_hex(address),
+ chain_id=chain_id,
+ symbol="FAKE TOK",
+ decimals=18,
+ name="FAKE Token",
)
+ return _mock_source(
+ get_eth_network=Mock(side_effect=_encode_network),
+ get_eth_token=Mock(side_effect=get_eth_token),
+ )
+
def test_descriptor_token_request_non_responsive(session: Session) -> None:
# Reply with no definition: the non-built-in tokens stay unknown and render
# "UNKN", signing still completes, and the device asked for each of those
# token addresses on the right chain (but not the native-currency sentinel).
- token_requests: list[messages.EthereumDefinitionRequest] = []
-
- def provider(
- req: messages.EthereumDefinitionRequest,
- ) -> messages.EthereumDefinitionAck:
- if not req.func_sig:
- token_requests.append(req)
- return messages.EthereumDefinitionAck(definitions=None)
+ source = _mock_source()
on_page, assert_all_seen = make_label_checker(
expected={"UNKN"}, absent={"FAKE TOK"}
)
for calldata in TEST_DESCRIPTOR_CALLDATAS:
- _test_descriptor_sign_tx(session, provider, calldata, on_page=on_page)
+ _test_descriptor_sign_tx(session, source, calldata, on_page=on_page)
assert_all_seen()
- requested = {
- hexlify(r.token_address).decode("ascii").lower() for r in token_requests
- }
+ # descriptor is built-in, so only token requests are expected
+ source.get_eth_display_format.assert_not_called()
+ token_calls = [call.args for call in source.get_eth_token.call_args_list]
+ requested = {_addr_hex(address) for _, address in token_calls}
assert TEST_DESCRIPTOR_TOKENS <= requested
assert TEST_DESCRIPTOR_NATIVE not in requested
- for r in token_requests:
- assert r.chain_id == TEST_DESCRIPTOR_CHAIN_ID
+ for chain_id, _ in token_calls:
+ assert chain_id == TEST_DESCRIPTOR_CHAIN_ID
def test_descriptor_token_request_responsive(session: Session) -> None:
# Resolve every requested token from a provided (fake) definition; the symbol
# is then rendered for each token field and "UNKN" never appears.
- def provider(
- req: messages.EthereumDefinitionRequest,
- ) -> messages.EthereumDefinitionAck:
- # descriptor is built-in, so only token (no-func_sig) requests are expected
- assert not req.func_sig
- addr = hexlify(req.token_address).decode("ascii").lower()
- return messages.EthereumDefinitionAck(
- definitions=messages.EthereumDefinitions(
- encoded_network=definitions.encode_eth_network(
- chain_id=TEST_DESCRIPTOR_CHAIN_ID
- ),
- encoded_token=definitions.encode_eth_token(
- address="0x" + addr,
- chain_id=TEST_DESCRIPTOR_CHAIN_ID,
- symbol="FAKE TOK",
- decimals=18,
- name="FAKE Token",
- ),
- )
- )
+ source = _any_token_source()
on_page, assert_all_seen = make_label_checker(
expected={"FAKE TOK"}, absent={"UNKN"}
)
for calldata in TEST_DESCRIPTOR_CALLDATAS:
- _test_descriptor_sign_tx(session, provider, calldata, on_page=on_page)
+ _test_descriptor_sign_tx(session, source, calldata, on_page=on_page)
assert_all_seen()
+
+ # descriptor is built-in, so only token requests are expected
+ source.get_eth_display_format.assert_not_called()
diff --git a/tests/device_tests/ethereum/test_signtx.py b/tests/device_tests/ethereum/test_signtx.py
index a60cd192..28ff68a3 100644
--- a/tests/device_tests/ethereum/test_signtx.py
+++ b/tests/device_tests/ethereum/test_signtx.py
@@ -16,7 +16,6 @@
from __future__ import annotations
-import functools
import typing as t
from itertools import product
from pathlib import Path
@@ -26,7 +25,7 @@ import pytest
from trezorlib import ethereum, exceptions, messages, models
from trezorlib.debuglink import DebugSession as Session
from trezorlib.debuglink import message_filters
-from trezorlib.definitions import FilesystemSource, definition_provider
+from trezorlib.definitions import FilesystemSource
from trezorlib.exceptions import TrezorFailure
from trezorlib.protobuf import MessageType
from trezorlib.tools import parse_path, unharden
@@ -152,9 +151,7 @@ def test_signtx_external_definitions(
encoded_network=_DEFINITIONS_SOURCE.get_eth_network(chain_id)
),
supports_definition_request=True,
- definition_provider=functools.partial(
- definition_provider, _DEFINITIONS_SOURCE
- ),
+ definition_source=_DEFINITIONS_SOURCE,
chunkify=True,
)
Why this scored 18/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.