refactor(core/ethereum): simplify `sign_tx()` digest flow
What changed, and why it matters
This commit is a straightforward internal code cleanup in the Ethereum transaction signing module. It moves existing digest-calculation logic into two new helper functions, _start_digest() and _finish_digest(), without changing what data is hashed or how the transaction is signed. There is no indication of a security fix or behavior change.
No security action required. Treat as normal maintainability refactor. If auditing, verify by inspection that _start_digest and _finish_digest reproduce the prior RLP encoding exactly.
Security signals we found
No security-relevant keywords in commit title or message
No changelog entry ([no changelog])
Pure refactor: identical hash input ordering and RLP length computation
No new imports of cryptographic or parsing routines
No changes to path validation, confirmation UI, or signature output
Evidence from the diff
The patch refactors apps/ethereum/sign_tx.py. It removes the inline keccak256 setup, RLP header writing, field hashing, and EIP-155 replay-protection writes from sign_tx(), placing them in _start_digest() and _finish_digest(). The sequence of hashed bytes and the RLP length calculation remain the same: tx_type (if present), nonce, gas_price, gas_limit, to address, value, then streamed calldata, then chain_id/0/0. The only functional difference is that data_total is now read from msg.data_length inside _start_digest() rather than being passed as an argument. No validation, parsing, or signing logic is altered.
Changed components
core/src/apps/ethereum/sign_tx.pyInspect captured patch +35 / −23
### core/src/apps/ethereum/sign_tx.py
@@ -41,10 +41,9 @@ async def sign_tx(
from apps.common import paths
- from .helpers import format_ethereum_amount, get_fee_items_regular, keccak256
+ from .helpers import format_ethereum_amount, get_fee_items_regular
# local_cache_attribute
- data_length = msg.data_length
tx_type = msg.tx_type
network = defs.network
@@ -82,15 +81,7 @@ async def sign_tx(
amount_size_bytes=32,
)
- sha = keccak256()
- rlp.write_header(sha, _get_digest_length(msg, data_length), rlp.LIST_HEADER_BYTE)
-
- if tx_type is not None:
- rlp.write(sha, tx_type)
-
- for field in (msg.nonce, msg.gas_price, msg.gas_limit, address_bytes, msg.value):
- rlp.write(sha, field)
-
+ sha = _start_digest(msg)
initial_data = await request_initial_data(msg, sha)
# Confirm the transaction, using special layouts for staking, yielding and clear-signing (if supported).
@@ -107,13 +98,7 @@ async def sign_tx(
create_data_chunk_loader(sha),
)
- # eip 155 replay protection
- rlp.write(sha, msg.chain_id)
- rlp.write(sha, 0)
- rlp.write(sha, 0)
-
- digest = sha.get_digest()
-
+ digest = _finish_digest(msg, sha)
# transaction data confirmed, proceed with signing
result = _sign_digest(msg, keychain, digest)
@@ -257,7 +242,9 @@ async def confirm_tx_data(
)
-def _get_digest_length(msg: EthereumSignTx, data_total: int) -> int:
+def _start_digest(msg: EthereumSignTx) -> HashWriter:
+ from .helpers import keccak256
+
length = 0
if msg.tx_type is not None:
length += rlp.length(msg.tx_type)
@@ -268,18 +255,43 @@ def _get_digest_length(msg: EthereumSignTx, data_total: int) -> int:
msg.gas_limit,
bytes_from_address(msg.to),
msg.value,
- msg.chain_id,
- 0,
- 0,
)
+ # fields length
for field in fields:
length += rlp.length(field)
+ # calldata length
+ data_total = msg.data_length
length += rlp.header_length(data_total, msg.data_initial_chunk)
length += data_total
- return length
+ # EIP-155 elements length
+ length += rlp.length(msg.chain_id)
+ length += rlp.length(0)
+ length += rlp.length(0)
+
+ # hash only RLP header, `msg.tx_type` and `fields` (see above).
+ # calldata and EIP-155 elements will be hashed later.
+ sha = keccak256()
+ rlp.write_header(sha, length, rlp.LIST_HEADER_BYTE)
+
+ if msg.tx_type is not None:
+ rlp.write(sha, msg.tx_type)
+
+ for field in fields:
+ rlp.write(sha, field)
+
+ return sha
+
+
+def _finish_digest(msg: EthereumSignTx, sha: HashWriter) -> bytes:
+ # EIP-155 replay protection
+ rlp.write(sha, msg.chain_id)
+ rlp.write(sha, 0)
+ rlp.write(sha, 0)
+
+ return sha.get_digest()
def create_data_chunk_loader(h: HashWriter) -> DataChunkLoader:Why this scored 12/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.