test(clear_signing): use jsons for testing external defintions
What changed, and why it matters
This commit only changes test code. It switches the way Trezor's automated tests store and load Ethereum 'clear signing' definitions from pre-generated binary files to plain JSON files that are converted and signed on the fly during tests. There is no change to the actual firmware or wallet behavior, and no security fix or vulnerability is present.
No security action needed. Review as normal test refactoring if desired.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff modifies three test-side files. It adds a JsonSource helper in tests/definitions.py that reads JSON test fixtures, converts them to protobuf messages, wraps them in the definition payload format, and dev-signs them. The README is updated to document the new JSON format and conversion workflow. tests/device_tests/ethereum/test_signtx.py replaces a FilesystemSource pointing at binary .dat files with a JsonSource pointing at JSON fixtures. No firmware, bootloader, or production signing code is touched.
Changed components
tests/definitions.pytests/device_tests/ethereum/test_signtx.pycommon/tests/fixtures/ethereum/definitions/eth/chain-id/1/README.mdInspect captured patch +103 / −31
### common/tests/fixtures/ethereum/definitions/eth/chain-id/1/README.md
@@ -1,29 +1,74 @@
# External Definitions Test Fixtures
-Directory for encoded (dev) signed token definitions and ERC-7730 contract
-descriptors, used for device testing with external definitions. These files
-back the fixtures in [`sign_tx_external_definitions.json`](../../../../sign_tx_external_definitions.json).
+Directory for network and token definitions and ERC-7730 contract descriptors,
+stored as JSON and used for device testing with external definitions. These
+files back the fixtures in [`sign_tx_external_definitions.json`](../../../../sign_tx_external_definitions.json).
-## Updating the definitions
+## Format
-Keep this directory up to date with the latest definitions from the
-`definitions.tar.xz` or `deploy.tar.xz` tarballs, that can be obtained from the [`trezor/definitions`](https://github.com/trezor/trezor-common-definitions)
-repo by running:
+Each file holds the definition type and the decoded protobuf message (bytes as
+hex, enums by name):
+
+```json
+{
+ "data_type": "ETHEREUM_NETWORK",
+ "message": {
+ "chain_id": 1,
+ "symbol": "ETH",
+ "slip44": 60,
+ "name": "Ethereum"
+ }
+}
+```
+
+The tests serve them through `JsonSource` (in `tests/definitions.py`), which
+encodes and dev-signs each definition when the device requests it, always with
+format version 2 and the maximum timestamp, so the fixtures never expire.
+
+The files are arranged as:
+
+```
+definitions/eth/chain-id/<chain-id>/network.json
+definitions/eth/chain-id/<chain-id>/token-<token-address>.json
+definitions/eth/chain-id/<chain-id>/display-format/<contract-address>-<function-signature>.json
+```
+
+## Adding definitions
+
+Generate the definitions with the [`trezor/definitions`](https://github.com/trezor/trezor-common-definitions)
+repo, in an external directory:
```sh
python cli.py generate --dev-sign -o <output_folder>
```
-Run the command above in an external directory, then pick the contract call
-you want to test.
+(Or you can ask the last release manager for the latest definitions or the
+ERC-7730 FW maintainer(s).)
-(Or you can ask the last release manager for the latest definitions or the ERC-7730 FW maintainer(s))
+Pick the `.dat` files for the contract calls you want to test, and convert
+each one to JSON next to it:
-The generated files are arranged as:
+```python
+import io, json, sys
+from pathlib import Path
+from trezorlib import definitions, messages, protobuf
+
+TYPES = {
+ messages.DefinitionType.ETHEREUM_NETWORK: messages.EthereumNetworkInfo,
+ messages.DefinitionType.ETHEREUM_TOKEN: messages.EthereumTokenInfo,
+ messages.DefinitionType.ETHEREUM_DISPLAY_FORMAT: messages.EthereumDisplayFormatInfo,
+}
+
+for name in sys.argv[1:]:
+ path = Path(name)
+ payload = definitions.Definition.parse(path.read_bytes()).payload
+ msg = protobuf.load_message(io.BytesIO(payload.data), TYPES[payload.data_type])
+ converted = {"data_type": payload.data_type.name, "message": protobuf.to_dict(msg)}
+ path.with_suffix(".json").write_text(json.dumps(converted, indent=2) + "\n")
```
-definitions/eth/<chain-id>/<display-format>/<contract-address>-<function-signature>.dat
-```
+
+Then copy the `.json` files here, keeping the layout above.
## Important
### tests/definitions.py
@@ -17,8 +17,10 @@
from __future__ import annotations
import io
+import json
import typing as t
from hashlib import sha256
+from pathlib import Path
from trezorlib import cosi, definitions, messages, protobuf
from trezorlib.testing.common import PRIVATE_KEYS_DEV
@@ -59,13 +61,7 @@ def make_payload(
format_version: bytes = b"2",
data_type: messages.DefinitionType = messages.DefinitionType.ETHEREUM_NETWORK,
timestamp: int = 0xFFFF_FFFF,
- message: (
- messages.EthereumNetworkInfo
- | messages.EthereumTokenInfo
- | messages.SolanaTokenInfo
- | messages.EthereumDisplayFormatInfo
- | bytes
- ) = make_eth_network(),
+ message: protobuf.MessageType | bytes = make_eth_network(),
) -> bytes:
if isinstance(message, bytes):
message_bytes = message
@@ -213,3 +209,38 @@ def encode_eth_display_format(
)
proof, signature = sign_payload(payload, [])
return payload + proof + signature
+
+
+_MESSAGE_TYPES: dict[messages.DefinitionType, type[protobuf.MessageType]] = {
+ messages.DefinitionType.ETHEREUM_NETWORK: messages.EthereumNetworkInfo,
+ messages.DefinitionType.ETHEREUM_TOKEN: messages.EthereumTokenInfo,
+ messages.DefinitionType.SOLANA_TOKEN: messages.SolanaTokenInfo,
+ messages.DefinitionType.ETHEREUM_DISPLAY_FORMAT: messages.EthereumDisplayFormatInfo,
+}
+
+
+def json_to_dat(d: dict[str, t.Any]) -> bytes:
+ """Encode and dev-sign a JSON definition.
+
+ Always uses format version 2 and the maximum timestamp (the `make_payload`
+ defaults), so the test definitions never expire."""
+ data_type = messages.DefinitionType[d["data_type"]]
+ payload = make_payload(
+ data_type=data_type,
+ message=protobuf.dict_to_proto(_MESSAGE_TYPES[data_type], d["message"]),
+ )
+ proof, signature = sign_payload(payload, [])
+ return payload + proof + signature
+
+
+class JsonSource(definitions.Source):
+ """Serves JSON definitions as freshly dev-signed .dat blobs."""
+
+ def __init__(self, root: Path) -> None:
+ self.root = root
+
+ def fetch_path(self, *components: str) -> bytes | None:
+ path = self.root.joinpath(*components).with_suffix(".json")
+ if not path.exists():
+ return None
+ return json_to_dat(json.loads(path.read_text()))
### tests/device_tests/ethereum/test_signtx.py
@@ -18,20 +18,18 @@
import typing as t
from itertools import product
-from pathlib import Path
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
from trezorlib.exceptions import TrezorFailure
from trezorlib.protobuf import MessageType
from trezorlib.tools import parse_path, unharden
-from ...common import parametrize_using_common_fixtures
-from ...definitions import encode_eth_network
+from ...common import COMMON_FIXTURES_DIR, parametrize_using_common_fixtures
+from ...definitions import JsonSource, encode_eth_network
from ...input_flows import (
InputFlowConfirmAllWarnings,
InputFlowEthereumSignTxData,
@@ -119,13 +117,11 @@ def _do_test_signtx(
assert sig.v == result["sig_v"]
-# Directory of dev-signed Ethereum definitions (network / token / clear-signing
-# display formats), laid out as eth/chain-id/<n>/... exactly like the deploy
-# tarball you would pass to `trezorctl ethereum --definitions <dir> sign-tx ...`.
-# Curated to only the definitions the cases below actually pull; drop more .dat
-# files in to clear-sign more contracts/functions.
-_DEFINITIONS_DIR = Path(__file__).parent / "definitions"
-_DEFINITIONS_SOURCE = FilesystemSource(_DEFINITIONS_DIR)
+# Directory of Ethereum definitions (network / token / clear-signing display
+# formats) as JSON, laid out as eth/chain-id/<n>/... like the deploy tarball.
+# `JsonSource` dev-signs each one on request. Curated to only the definitions
+# the cases below actually pull; add more to clear-sign more contracts/functions.
+_DEFINITIONS_SOURCE = JsonSource(COMMON_FIXTURES_DIR / "ethereum" / "definitions")
@parametrize_using_common_fixtures("ethereum/sign_tx_external_definitions.json")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.