diff --git a/Cargo.lock b/Cargo.lock index 1130edd46..cfa24a16f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5664,6 +5664,7 @@ dependencies = [ "reth-primitives", "reth-rpc-api", "reth-rpc-types", + "serde_json", "tokio", ] diff --git a/crates/rpc/rpc-testing-util/Cargo.toml b/crates/rpc/rpc-testing-util/Cargo.toml index e9c7b8aec..c3e5cafd4 100644 --- a/crates/rpc/rpc-testing-util/Cargo.toml +++ b/crates/rpc/rpc-testing-util/Cargo.toml @@ -22,6 +22,7 @@ futures = { workspace = true } # misc jsonrpsee = { version = "0.18", features = ["client", "async-client"] } +serde_json.workspace = true [dev-dependencies] tokio = { workspace = true, features = ["rt-multi-thread", "macros", "rt"] } diff --git a/crates/rpc/rpc-testing-util/assets/noop-tracer.js b/crates/rpc/rpc-testing-util/assets/noop-tracer.js new file mode 100644 index 000000000..6906e3ee7 --- /dev/null +++ b/crates/rpc/rpc-testing-util/assets/noop-tracer.js @@ -0,0 +1,8 @@ +{ + // required function that is invoked when a step fails + fault: function(log, db) { }, + // required function that returns the result of the tracer: empty object + result: function(ctx, db) { return {}; }, + // optional function that is invoked for every opcode + step: function(log, db) { } +} \ No newline at end of file diff --git a/crates/rpc/rpc-testing-util/src/debug.rs b/crates/rpc/rpc-testing-util/src/debug.rs new file mode 100644 index 000000000..da94fb3ee --- /dev/null +++ b/crates/rpc/rpc-testing-util/src/debug.rs @@ -0,0 +1,79 @@ +//! Helpers for testing debug trace calls. + +use reth_primitives::H256; +use reth_rpc_api::clients::DebugApiClient; +use reth_rpc_types::trace::geth::{GethDebugTracerType, GethDebugTracingOptions}; + +const NOOP_TRACER: &str = include_str!("../assets/noop-tracer.js"); + +/// An extension trait for the Trace API. +#[async_trait::async_trait] +pub trait DebugApiExt { + /// The provider type that is used to make the requests. + type Provider; + + /// Same as [DebugApiClient::debug_trace_transaction] but returns the result as json. + async fn debug_trace_transaction_json( + &self, + hash: H256, + opts: GethDebugTracingOptions, + ) -> Result; +} + +#[async_trait::async_trait] +impl DebugApiExt for T { + type Provider = T; + + async fn debug_trace_transaction_json( + &self, + hash: H256, + opts: GethDebugTracingOptions, + ) -> Result { + let mut params = jsonrpsee::core::params::ArrayParams::new(); + params.insert(hash).unwrap(); + params.insert(opts).unwrap(); + self.request("debug_traceTransaction", params).await + } +} + +/// A javascript tracer that does nothing +#[derive(Debug, Clone, Copy, Default)] +#[non_exhaustive] +pub struct NoopJsTracer; + +impl From for GethDebugTracingOptions { + fn from(_: NoopJsTracer) -> Self { + GethDebugTracingOptions { + tracer: Some(GethDebugTracerType::JsTracer(NOOP_TRACER.to_string())), + tracer_config: serde_json::Value::Object(Default::default()).into(), + ..Default::default() + } + } +} +impl From for Option { + fn from(_: NoopJsTracer) -> Self { + Some(NoopJsTracer.into()) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + debug::{DebugApiExt, NoopJsTracer}, + utils::parse_env_url, + }; + use jsonrpsee::http_client::HttpClientBuilder; + + #[tokio::test] + #[ignore] + async fn can_trace_noop_sepolia() { + // random tx + let tx = + "0x5525c63a805df2b83c113ebcc8c7672a3b290673c4e81335b410cd9ebc64e085".parse().unwrap(); + let url = parse_env_url("RETH_RPC_TEST_NODE_URL").unwrap(); + let client = HttpClientBuilder::default().build(url).unwrap(); + let res = + client.debug_trace_transaction_json(tx, NoopJsTracer::default().into()).await.unwrap(); + assert_eq!(res, serde_json::Value::Object(Default::default())); + } +} diff --git a/crates/rpc/rpc-testing-util/src/lib.rs b/crates/rpc/rpc-testing-util/src/lib.rs index a3e2b6af1..67f20baff 100644 --- a/crates/rpc/rpc-testing-util/src/lib.rs +++ b/crates/rpc/rpc-testing-util/src/lib.rs @@ -7,6 +7,7 @@ //! Reth RPC testing utilities. +pub mod debug; pub mod trace; pub mod utils;