Skip to content

Commit 7d0a118

Browse files
prestwichclaude
andauthored
feat(rpc): implement Parity trace_ namespace (9 methods) (#121)
* docs: add Parity trace namespace implementation plan 12 tasks covering TraceError, param types, config, Parity tracer functions, block replay helpers, 9 trace_ endpoints, and router wiring. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(rpc): add TraceError for Parity trace namespace Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(rpc): add param types for Parity trace namespace Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(rpc): add max_trace_filter_blocks config Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(rpc): add Parity tracer functions (localized + replay) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(rpc): add Parity block replay helpers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(rpc): add trace_block and trace_transaction Implements the trace_block and trace_transaction async RPC handlers, calling the existing trace_block_localized helper for block-level and per-transaction Parity trace output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(rpc): add trace_call, trace_callMany, trace_rawTransaction, trace_get, and trace_filter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(rpc): wire Parity trace namespace into router Creates trace/mod.rs with the router constructor and wires it into the combined router. Fixes HashSet type mismatch (std to alloy) and removes erroneous accept_state() call on already-needs-tx state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style(rpc): apply nightly fmt to TraceError * chore: remove accidentally committed planning docs The docs/ directory is already in .gitignore — these were force-added. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(rpc): address trace namespace PR review feedback - Add [default: 100] to trace_filter config description - Move itertools import to top-level, remove inline imports - Remove unused _block_hash param from trace_block_replay - Remove unnecessary #[allow(clippy::too_many_arguments)] - Fix off-by-one in trace_filter block range distance - Add InvalidBlockRange error variant for fromBlock > toBlock - Return specific block numbers in BlockNotFound errors Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3ef2bcc commit 7d0a118

7 files changed

Lines changed: 965 additions & 7 deletions

File tree

crates/rpc/src/config/rpc_config.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ pub struct StorageRpcConfig {
6161
/// Default: `25`.
6262
pub max_tracing_requests: usize,
6363

64+
/// Maximum block range for `trace_filter` queries.
65+
///
66+
/// Default: `100`.
67+
pub max_trace_filter_blocks: u64,
68+
6469
/// Time-to-live for stale filters and subscriptions.
6570
///
6671
/// Default: `5 minutes`.
@@ -136,6 +141,7 @@ impl Default for StorageRpcConfig {
136141
max_logs_per_response: 20_000,
137142
max_log_query_deadline: Duration::from_secs(10),
138143
max_tracing_requests: 25,
144+
max_trace_filter_blocks: 100,
139145
stale_filter_ttl: Duration::from_secs(5 * 60),
140146
gas_oracle_block_count: 20,
141147
gas_oracle_percentile: 60.0,
@@ -188,6 +194,12 @@ impl StorageRpcConfigBuilder {
188194
self
189195
}
190196

197+
/// Set the max block range for trace_filter.
198+
pub const fn max_trace_filter_blocks(mut self, max: u64) -> Self {
199+
self.inner.max_trace_filter_blocks = max;
200+
self
201+
}
202+
191203
/// Set the time-to-live for stale filters and subscriptions.
192204
pub const fn stale_filter_ttl(mut self, ttl: Duration) -> Self {
193205
self.inner.stale_filter_ttl = ttl;
@@ -298,6 +310,13 @@ pub struct StorageRpcConfigEnv {
298310
optional
299311
)]
300312
max_tracing_requests: Option<u64>,
313+
/// Maximum block range for trace_filter queries.
314+
#[from_env(
315+
var = "SIGNET_RPC_MAX_TRACE_FILTER_BLOCKS",
316+
desc = "Maximum block range for trace_filter queries [default: 100]",
317+
optional
318+
)]
319+
max_trace_filter_blocks: Option<u64>,
301320
/// Filter TTL in seconds.
302321
#[from_env(
303322
var = "SIGNET_RPC_STALE_FILTER_TTL_SECS",
@@ -385,6 +404,9 @@ impl From<StorageRpcConfigEnv> for StorageRpcConfig {
385404
max_tracing_requests: env
386405
.max_tracing_requests
387406
.map_or(defaults.max_tracing_requests, |v| v as usize),
407+
max_trace_filter_blocks: env
408+
.max_trace_filter_blocks
409+
.unwrap_or(defaults.max_trace_filter_blocks),
388410
stale_filter_ttl: env
389411
.stale_filter_ttl_secs
390412
.map_or(defaults.stale_filter_ttl, Duration::from_secs),

crates/rpc/src/debug/tracer.rs

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33
//! Largely adapted from reth: `crates/rpc/rpc/src/debug.rs`.
44
55
use crate::debug::DebugError;
6-
use alloy::rpc::types::{
7-
TransactionInfo,
8-
trace::geth::{
9-
FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerConfig, GethDebugTracerType,
10-
GethDebugTracingOptions, GethTrace, NoopFrame,
6+
use alloy::{
7+
primitives::map::HashSet,
8+
rpc::types::{
9+
TransactionInfo,
10+
trace::{
11+
geth::{
12+
FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerConfig,
13+
GethDebugTracerType, GethDebugTracingOptions, GethTrace, NoopFrame,
14+
},
15+
parity::{LocalizedTransactionTrace, TraceResults, TraceType},
16+
},
1117
},
1218
};
1319
use revm_inspectors::tracing::{
@@ -197,6 +203,67 @@ where
197203
Ok((frame.into(), trevm))
198204
}
199205

206+
/// Trace a transaction and return Parity-format localized traces.
207+
///
208+
/// Used by `trace_block`, `trace_transaction`, `trace_get`, and
209+
/// `trace_filter`.
210+
pub(crate) fn trace_parity_localized<Db, Insp>(
211+
trevm: EvmReady<Db, Insp>,
212+
tx_info: TransactionInfo,
213+
) -> Result<(Vec<LocalizedTransactionTrace>, EvmNeedsTx<Db, Insp>), DebugError>
214+
where
215+
Db: Database + DatabaseCommit + DatabaseRef,
216+
Insp: Inspector<Ctx<Db>>,
217+
{
218+
let gas_limit = trevm.gas_limit();
219+
let mut inspector = TracingInspector::new(TracingInspectorConfig::default_parity());
220+
let trevm = trevm
221+
.try_with_inspector(&mut inspector, |trevm| trevm.run())
222+
.map_err(|err| DebugError::EvmHalt { reason: err.into_error().to_string() })?;
223+
224+
let traces = inspector
225+
.with_transaction_gas_limit(gas_limit)
226+
.into_parity_builder()
227+
.into_localized_transaction_traces(tx_info);
228+
229+
Ok((traces, trevm.accept_state()))
230+
}
231+
232+
/// Trace a transaction and return Parity-format [`TraceResults`].
233+
///
234+
/// When [`TraceType::StateDiff`] is in `trace_types`, the state diff is
235+
/// enriched with pre-transaction balance/nonce from the database.
236+
///
237+
/// Used by `trace_replayBlockTransactions`, `trace_call`,
238+
/// `trace_callMany`, and `trace_rawTransaction`.
239+
pub(crate) fn trace_parity_replay<Db, Insp>(
240+
trevm: EvmReady<Db, Insp>,
241+
trace_types: &HashSet<TraceType>,
242+
) -> Result<(TraceResults, EvmNeedsTx<Db, Insp>), DebugError>
243+
where
244+
Db: Database + DatabaseCommit + DatabaseRef,
245+
<Db as DatabaseRef>::Error: std::fmt::Debug,
246+
Insp: Inspector<Ctx<Db>>,
247+
{
248+
let mut inspector =
249+
TracingInspector::new(TracingInspectorConfig::from_parity_config(trace_types));
250+
let trevm = trevm
251+
.try_with_inspector(&mut inspector, |trevm| trevm.run())
252+
.map_err(|err| DebugError::EvmHalt { reason: err.into_error().to_string() })?;
253+
254+
let (result, mut trevm) = trevm.take_result_and_state();
255+
256+
let trace_res = inspector
257+
.into_parity_builder()
258+
.into_trace_results_with_state(&result, trace_types, trevm.inner_mut_unchecked().db_mut())
259+
.map_err(|e| DebugError::EvmHalt { reason: format!("state diff: {e:?}") })?;
260+
261+
// Equivalent to `trevm.accept_state()`.
262+
trevm.inner_mut_unchecked().db_mut().commit(result.state);
263+
264+
Ok((trace_res, trevm))
265+
}
266+
200267
// Some code in this file has been copied and modified from reth
201268
// <https://github.com/paradigmxyz/reth>
202269
// The original license is included below:

crates/rpc/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ pub use interest::{ChainEvent, NewBlockNotification, RemovedBlock, ReorgNotifica
2525
mod debug;
2626
pub use debug::DebugError;
2727

28+
mod trace;
29+
pub use trace::TraceError;
30+
2831
mod signet;
2932
pub use signet::error::SignetError;
3033

@@ -34,8 +37,8 @@ mod web3;
3437
pub mod serve;
3538
pub use serve::{RpcServerGuard, ServeConfig, ServeConfigEnv, ServeError};
3639

37-
/// Instantiate a combined router with `eth`, `debug`, `signet`, `web3`, and
38-
/// `net` namespaces.
40+
/// Instantiate a combined router with `eth`, `debug`, `trace`, `signet`,
41+
/// `web3`, and `net` namespaces.
3942
pub fn router<H>() -> ajj::Router<StorageRpcCtx<H>>
4043
where
4144
H: signet_hot::HotKv + Send + Sync + 'static,
@@ -44,6 +47,7 @@ where
4447
ajj::Router::new()
4548
.nest("eth", eth::eth())
4649
.nest("debug", debug::debug())
50+
.nest("trace", trace::trace())
4751
.nest("signet", signet::signet())
4852
.nest("web3", web3::web3())
4953
.nest("net", net::net())

0 commit comments

Comments
 (0)