Skip to content

Commit 98d39da

Browse files
prestwichclaude
andcommitted
docs: add Parity trace_ namespace design spec
9 methods (trace_block, trace_transaction, trace_replayBlockTransactions, trace_replayTransaction, trace_call, trace_callMany, trace_rawTransaction, trace_get, trace_filter) for Blockscout/tooling compatibility. ENG-1064, ENG-1065. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 700e240 commit 98d39da

1 file changed

Lines changed: 242 additions & 0 deletions

File tree

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
# Parity `trace_` Namespace
2+
3+
Add the Parity/OpenEthereum `trace_` JSON-RPC namespace to signet-rpc for
4+
Blockscout and general tooling compatibility. Driven by ENG-1064 and ENG-1065.
5+
6+
## Scope
7+
8+
9 methods in a new `trace` namespace. No block/uncle reward traces — Signet
9+
is a post-merge L2. No `debug_storageRangeAt` — Geth's hashed-key pagination
10+
format doesn't match Signet's plain-key storage, and reth hasn't implemented
11+
it either.
12+
13+
## Methods
14+
15+
| Method | Input | Output |
16+
|--------|-------|--------|
17+
| `trace_block` | `BlockNumberOrTag` | `Option<Vec<LocalizedTransactionTrace>>` |
18+
| `trace_transaction` | `B256` | `Option<Vec<LocalizedTransactionTrace>>` |
19+
| `trace_replayBlockTransactions` | `BlockNumberOrTag, HashSet<TraceType>` | `Option<Vec<TraceResultsWithTransactionHash>>` |
20+
| `trace_replayTransaction` | `B256, HashSet<TraceType>` | `TraceResults` |
21+
| `trace_call` | `TransactionRequest, HashSet<TraceType>, Option<BlockId>` | `TraceResults` |
22+
| `trace_callMany` | `Vec<(TransactionRequest, HashSet<TraceType>)>, Option<BlockId>` | `Vec<TraceResults>` |
23+
| `trace_rawTransaction` | `Bytes, HashSet<TraceType>, Option<BlockId>` | `TraceResults` |
24+
| `trace_get` | `B256, Vec<usize>` | `Option<LocalizedTransactionTrace>` |
25+
| `trace_filter` | `TraceFilter` | `Vec<LocalizedTransactionTrace>` |
26+
27+
All types from `alloy::rpc::types::trace::parity` and
28+
`alloy::rpc::types::trace::filter`.
29+
30+
## Architecture
31+
32+
### New files
33+
34+
- `crates/rpc/src/trace/mod.rs` — router (9 routes) + re-exports
35+
- `crates/rpc/src/trace/endpoints.rs` — 9 handlers + 2 shared replay helpers
36+
- `crates/rpc/src/trace/error.rs``TraceError` with `IntoErrorPayload`
37+
- `crates/rpc/src/trace/types.rs` — param tuple structs for ajj positional
38+
param deserialization (following `debug/types.rs` pattern)
39+
40+
### Modified files
41+
42+
- `crates/rpc/src/debug/tracer.rs` — add 2 `pub(crate)` Parity tracer
43+
functions
44+
- `crates/rpc/src/config/rpc_config.rs` — add `max_trace_filter_blocks`
45+
- `crates/rpc/src/lib.rs` — add `mod trace`, export `TraceError`, nest router
46+
47+
### No new dependencies
48+
49+
`revm-inspectors` 0.34.2 already has `ParityTraceBuilder`. `alloy` 1.7.3
50+
already has all Parity trace types. The existing `trace_flat_call` in
51+
`debug/tracer.rs` already uses `inspector.into_parity_builder()`.
52+
53+
## Param Types
54+
55+
Tuple structs in `trace/types.rs` for ajj positional param deserialization,
56+
following the pattern in `debug/types.rs`:
57+
58+
```
59+
TraceBlockParams(BlockNumberOrTag)
60+
TraceTransactionParams(B256)
61+
ReplayBlockParams(BlockNumberOrTag, HashSet<TraceType>)
62+
ReplayTransactionParams(B256, HashSet<TraceType>)
63+
TraceCallParams(TransactionRequest, HashSet<TraceType>, Option<BlockId>,
64+
Option<StateOverride>, Option<Box<BlockOverrides>>)
65+
TraceCallManyParams(Vec<(TransactionRequest, HashSet<TraceType>)>,
66+
Option<BlockId>)
67+
TraceRawTransactionParams(Bytes, HashSet<TraceType>, Option<BlockId>)
68+
TraceGetParams(B256, Vec<usize>)
69+
TraceFilterParams(TraceFilter)
70+
```
71+
72+
`trace_call` includes state and block override fields to support reth's
73+
`TraceCallRequest` semantics via positional params.
74+
75+
## Parity Tracer Functions
76+
77+
Two new `pub(crate)` functions in `debug/tracer.rs`:
78+
79+
**`trace_parity_localized(trevm, tx_info)`**
80+
Returns `(Vec<LocalizedTransactionTrace>, EvmNeedsTx)`. Creates
81+
`TracingInspector` with `TracingInspectorConfig::default_parity()`, runs the
82+
tx via `try_with_inspector`, extracts `gas_used` from the result (matching
83+
`trace_flat_call` pattern), converts via
84+
`into_parity_builder().with_transaction_gas_used(gas).into_localized_transaction_traces(tx_info)`.
85+
86+
Used by: `trace_block`, `trace_transaction`, `trace_get`, `trace_filter`.
87+
88+
**`trace_parity_replay(trevm, trace_types)`**
89+
Returns `(TraceResults, EvmNeedsTx)`. Requires `Db: Database + DatabaseRef`
90+
(the `DatabaseRef` bound is needed for `populate_state_diff`). Creates
91+
`TracingInspector` with
92+
`TracingInspectorConfig::from_parity_config(&trace_types)`. Runs the tx,
93+
then:
94+
95+
1. Takes result WITHOUT committing state (holds uncommitted state).
96+
2. Converts via `into_parity_builder().into_trace_results(&result, &trace_types)`.
97+
3. If `StateDiff` is requested: calls `populate_state_diff(&mut state_diff, &db, state.iter())` to enrich with pre-tx balance/nonce from the DB.
98+
4. Then commits state.
99+
100+
This matches reth's `replayBlockTransactions` pattern.
101+
102+
Used by: `trace_replayBlockTransactions`, `trace_call`, `trace_callMany`,
103+
`trace_rawTransaction`.
104+
105+
**Exception:** `trace_replayTransaction` uses
106+
`into_trace_results_with_state(&res, &trace_types, &db)` instead. This
107+
method takes `&ResultAndState` (a different type) and handles state diff
108+
population internally. Matches reth's divergent pattern for single-tx replay.
109+
The `DB::Error` from this call must be mapped into `TraceError`.
110+
111+
## Shared Block Replay Helpers
112+
113+
Two inner functions in `trace/endpoints.rs`:
114+
115+
**`trace_block_localized()`** — replays block txs, calls
116+
`trace_parity_localized` for each. Stops at the first magic-sig tx (using
117+
`peeking_take_while`, same as `debug::trace_block_inner`). Returns
118+
`Vec<LocalizedTransactionTrace>`. No reward traces (Signet is post-merge).
119+
120+
**`trace_block_replay()`** — same replay loop but calls
121+
`trace_parity_replay` with the caller's `HashSet<TraceType>`. Returns
122+
`Vec<TraceResultsWithTransactionHash>`.
123+
124+
Both follow the same EVM setup pattern as `debug::trace_block_inner`:
125+
`signet_evm::signet_evm()`, `fill_cfg`, `fill_block`, iterate txs with
126+
`peeking_take_while`. All handlers use `tracing::debug_span!` +
127+
`.instrument(span)` for instrumentation (matching existing debug endpoints).
128+
129+
## Method Details
130+
131+
### `trace_block`
132+
133+
Semaphore-gated. Resolves block, delegates to `trace_block_localized`.
134+
Returns `None` if block not found. No reward traces.
135+
136+
### `trace_transaction`
137+
138+
Semaphore-gated. Finds tx by hash in cold storage, resolves containing block,
139+
replays preceding txs without tracing (using `run_tx` + `accept_state`),
140+
traces target tx with `trace_parity_localized`. Returns `None` if tx not
141+
found.
142+
143+
### `trace_replayBlockTransactions`
144+
145+
Semaphore-gated. Resolves block, delegates to `trace_block_replay` with the
146+
caller's `HashSet<TraceType>`. For each tx, wraps result in
147+
`TraceResultsWithTransactionHash`. Returns `None` if block not found.
148+
149+
### `trace_replayTransaction`
150+
151+
Semaphore-gated. Replays preceding txs, traces target tx. Uses
152+
`into_trace_results_with_state(&res, &trace_types, &db)` — different from
153+
`replayBlockTransactions` which uses `into_trace_results()` +
154+
`populate_state_diff()`. This matches reth's divergent pattern. Returns error
155+
(not `None`) if tx not found.
156+
157+
### `trace_call`
158+
159+
Semaphore-gated. Resolves EVM state at block via `resolve_evm_block`.
160+
Supports state overrides and block overrides (matching reth). Fills tx from
161+
`TransactionRequest`, traces with `trace_parity_replay`. Defaults to latest
162+
block if unspecified.
163+
164+
### `trace_callMany`
165+
166+
Semaphore-gated. Resolves EVM state once, then processes calls sequentially.
167+
Each call can have different `HashSet<TraceType>`. State is committed between
168+
calls via `db.commit(res.state)` — each subsequent call sees prior state
169+
changes. Last call's state is not committed. Defaults to `BlockId::pending()`
170+
if unspecified (matching reth).
171+
172+
### `trace_rawTransaction`
173+
174+
Semaphore-gated. Decodes RLP bytes into a transaction, recovers sender. Takes
175+
optional `block_id` (defaults to latest). Traces with `trace_parity_replay`.
176+
177+
### `trace_get`
178+
179+
Semaphore-gated. Returns `None` if `indices.len() != 1` (Erigon
180+
compatibility, matching reth). Delegates to `trace_transaction` to get all
181+
traces, then selects the trace at `indices[0]`. Returns `None` if index is
182+
out of bounds or tx not found.
183+
184+
### `trace_filter`
185+
186+
Semaphore-gated. Validates block range:
187+
- `from_block` defaults to 0, `to_block` defaults to latest
188+
- Both must be <= latest block
189+
- `from_block` must be <= `to_block`
190+
- Range must be <= `max_trace_filter_blocks` (default 100, configurable)
191+
192+
Processes blocks sequentially. For each block, calls
193+
`trace_block_localized()`, filters results with
194+
`TraceFilter::matcher().matches(&trace.trace)`. Applies `after` (skip) and
195+
`count` (limit) pagination. No reward traces.
196+
197+
## Error Type
198+
199+
`TraceError` in `trace/error.rs`:
200+
201+
```
202+
Cold(ColdStorageError) — -32000
203+
Hot(StorageError) — -32000
204+
Resolve(ResolveError) — via resolve_error_code()
205+
EvmHalt { reason: String } — -32000
206+
BlockNotFound(BlockId) — -32001
207+
TransactionNotFound(B256) — -32001
208+
RlpDecode(String) — -32602
209+
SenderRecovery — -32000
210+
BlockRangeExceeded — -32602
211+
```
212+
213+
Implements `IntoErrorPayload`. Reuses `resolve_error_code` and
214+
`resolve_error_message` from `crate::eth::error`. Error messages are
215+
sanitized — storage/DB errors return `"server error"`, no internals leaked.
216+
217+
## Configuration
218+
219+
New field in `StorageRpcConfig`:
220+
221+
- `max_trace_filter_blocks: u64` — default 100
222+
- Env var: `SIGNET_RPC_MAX_TRACE_FILTER_BLOCKS`
223+
- Builder setter: `max_trace_filter_blocks(u64)`
224+
225+
## Router
226+
227+
```
228+
router()
229+
|- eth::eth() (41 methods)
230+
|- debug::debug() (9 methods)
231+
|- signet::signet() (2 methods)
232+
|- web3::web3() (2 methods)
233+
|- net::net() (2 methods)
234+
+- trace::trace() (9 methods)
235+
```
236+
237+
65 total routes.
238+
239+
## Ordering
240+
241+
This work depends on PR #120 (namespace completeness) which depends on
242+
PR #119 (structured error codes). Branch off PR #120's head.

0 commit comments

Comments
 (0)