ethereum-optimism / ethereum-optimism/optimism

op-service/sources: recover the transaction hash / decode caching lost with RawTransactions

Open
#22,255 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
6.5k
Forks
4k
Avg merge
2d 9h
Merged PRs (30d)
165

Description

`op-service/sources` now keeps block transactions as canonical bytes
([`RawTransactions`](https://github.com/ethereum-optimism/optimism/blob/454d3080862a38faa3e8a11688969fefe1108b88/op-service/sources/raw_transaction.go#L120-L141))
instead of `types.Transactions` (#21908, part of #20264). That trade is deliberate — it is what
lets an L2 block round-trip OP synthetic transactions without go-ethereum's typed decoding — but
it drops two memoizations that `types.Transaction` provided for free. Observed by @joshklop while
reviewing #21908.

## What is lost

1. **Hash cache.** `types.Transaction` caches its hash after the first `Hash()` call.
[`RawTransaction.Hash()`](https://github.com/ethereum-optimism/optimism/blob/454d3080862a38faa3e8a11688969fefe1108b88/op-service/sources/raw_transaction.go#L36-L40)
keccaks the bytes on every call, and
[`RawTransactions.Hashes()`](https://github.com/ethereum-optimism/optimism/blob/454d3080862a38faa3e8a11688969fefe1108b88/op-service/sources/raw_transaction.go#L134-L141)
does it for the whole block.
2. **Decoded-tx reuse.** The client's
[`transactionsCache`](https://github.com/ethereum-optimism/optimism/blob/454d3080862a38faa3e8a11688969fefe1108b88/op-service/sources/eth_client.go#L137)
holds raw bytes, so the typed views —
[`Geth()`](https://github.com/ethereum-optimism/optimism/blob/454d3080862a38faa3e8a11688969fefe1108b88/op-service/sources/raw_transaction.go#L149-L159),
`UserTxs()`, `Deposits()` — RLP-decode the block again on every cache hit, where the old cache
returned already-decoded transactions.

Both are per-call costs on cache hits; the first fetch of a block is not slower (it was decoding
and hashing then too). The paths that pay repeatedly:

- [`FetchReceipts`](https://github.com/ethereum-optimism/optimism/blob/454d3080862a38faa3e8a11688969fefe1108b88/op-service/sources/eth_client.go#L529-L543)
— rehashes every transaction of the block per call, including on cache hits and retries.
- [`HeaderAndTxsByHash`](https://github.com/ethereum-optimism/optimism/blob/454d3080862a38faa3e8a11688969fefe1108b88/op-service/sources/eth_client.go#L339-L352)
/ `InfoAndTxsBy*` and the class-partitioned accessors — re-decode per call.

## What is gained (the other side of the trade)

Transactions-root derivation and execution-payload construction operate on the canonical bytes
directly and no longer re-encode each transaction, and JSON ingress encodes once per transaction
rather than decode-then-re-encode on demand.

## Possible fix

Measure first — for L1 blocks in op-node derivation this is likely noise next to the RPC round
trip, and no cache is worth adding blind. If it does show up:

- **Cache next to the bytes, not inside them.** Have `transactionsCache` hold the raw transactions
plus their lazily-derived hashes (and possibly the decoded typed view), keyed by block hash.
Keeps `RawTransaction` a plain `[]byte` alias.
- **Memoize on the type.** Turn `RawTransaction` into a struct with a lazily computed hash. Costs
the `[]byte` semantics that `EncodeIndex`, the JSON codecs, and payload pass-through rely on.

The first option looks cheaper and is confined to the client.

Refs #20264, #21908.

🤖 *Co-created with Claude Opus 5*

Contributor guide

Open the contributing guide

Research direction

Start in op-service/sources/raw_transaction.go and eth_client.go, then measure cache-hit behavior through FetchReceipts, HeaderAndTxsByHash, and the class-partitioned accessors. Compare repeated hashing and decoding costs before choosing an approach; done means the impact is documented and, if significant, the selected caching design is implemented with evidence that repeated work is reduced.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend, performance
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.