From 64d035273241e89a19a4fb724cf7fc88c0f36777 Mon Sep 17 00:00:00 2001 From: ArtificialPB Date: Thu, 7 Mar 2024 00:21:43 +0100 Subject: [PATCH] feat(rpc-trace-types): add support for mux tracer (#252) * feat(rpc-trace-types): add support for mux tracer * cleanup formatting * fix nits --- crates/rpc-trace-types/src/geth/mod.rs | 25 +++++- crates/rpc-trace-types/src/geth/mux.rs | 115 +++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 crates/rpc-trace-types/src/geth/mux.rs diff --git a/crates/rpc-trace-types/src/geth/mod.rs b/crates/rpc-trace-types/src/geth/mod.rs index 446b82f6bdb..b2f3cc0993f 100644 --- a/crates/rpc-trace-types/src/geth/mod.rs +++ b/crates/rpc-trace-types/src/geth/mod.rs @@ -1,6 +1,7 @@ #![allow(missing_docs)] //! Geth tracing types +use crate::geth::mux::{MuxConfig, MuxFrame}; use alloy_primitives::{Bytes, B256, U256}; use alloy_rpc_types::{state::StateOverride, BlockOverrides}; use serde::{de::DeserializeOwned, ser::SerializeMap, Deserialize, Serialize, Serializer}; @@ -19,6 +20,7 @@ pub use self::{ pub mod call; pub mod four_byte; +pub mod mux; pub mod noop; pub mod pre_state; @@ -116,6 +118,8 @@ pub enum GethTrace { PreStateTracer(PreStateFrame), /// An empty json response NoopTracer(NoopFrame), + /// The response for mux tracer + MuxTracer(MuxFrame), /// Any other trace response, such as custom javascript response objects JS(serde_json::Value), } @@ -150,10 +154,16 @@ impl From for GethTrace { } } +impl From for GethTrace { + fn from(value: MuxFrame) -> Self { + GethTrace::MuxTracer(value) + } +} + /// Available built-in tracers /// /// See -#[derive(Debug, Copy, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[derive(Debug, Copy, PartialEq, Eq, Clone, Hash, Deserialize, Serialize)] pub enum GethDebugBuiltInTracerType { /// The 4byteTracer collects the function selectors of every function executed in the lifetime /// of a transaction, along with the size of the supplied call data. The result is a @@ -179,6 +189,9 @@ pub enum GethDebugBuiltInTracerType { /// This tracer is noop. It returns an empty object and is only meant for testing the setup. #[serde(rename = "noopTracer")] NoopTracer, + /// The mux tracer is a tracer that can run multiple tracers at once. + #[serde(rename = "muxTracer")] + MuxTracer, } /// Available tracers @@ -233,13 +246,21 @@ impl GethDebugTracerConfig { self.0 } - /// Returns the [PreStateConfig] if it is a call config. + /// Returns the [PreStateConfig] if it is a prestate config. pub fn into_pre_state_config(self) -> Result { if self.0.is_null() { return Ok(Default::default()); } self.from_value() } + + /// Returns the [MuxConfig] if it is a mux config. + pub fn into_mux_config(self) -> Result { + if self.0.is_null() { + return Ok(Default::default()); + } + self.from_value() + } } impl From for GethDebugTracerConfig { diff --git a/crates/rpc-trace-types/src/geth/mux.rs b/crates/rpc-trace-types/src/geth/mux.rs new file mode 100644 index 00000000000..975ce77e608 --- /dev/null +++ b/crates/rpc-trace-types/src/geth/mux.rs @@ -0,0 +1,115 @@ +use crate::geth::{GethDebugBuiltInTracerType, GethDebugTracerConfig, GethTrace}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// A `muxTracer` config that contains the configuration for running multiple tracers in one go. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MuxConfig(pub HashMap>); + +/// A `muxTracer` frame response that contains the results of multiple tracers +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct MuxFrame(pub HashMap); + +#[cfg(test)] +mod tests { + use super::*; + use crate::geth::*; + + const FOUR_BYTE_FRAME: &str = r#"{ + "0x27dc297e-128": 1, + "0x38cc4831-0": 2, + "0x524f3889-96": 1, + "0xadf59f99-288": 1, + "0xc281d19e-0": 1 + }"#; + + const CALL_FRAME_WITH_LOG: &str = include_str!("../../test_data/call_tracer/with_log.json"); + + const PRESTATE_FRAME: &str = r#"{ + "0x0000000000000000000000000000000000000002": { + "balance": "0x0" + }, + "0x008b3b2f992c0e14edaa6e2c662bec549caa8df1": { + "balance": "0x2638035a26d133809" + }, + "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { + "balance": "0x7a48734599f7284", + "nonce": 1133 + }, + "0xc8ba32cab1757528daf49033e3673fae77dcf05d": { + "balance": "0x0", + "code": "0x", + "nonce": 1, + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000024aea6", + "0x59fb7853eb21f604d010b94c123acbeae621f09ce15ee5d7616485b1e78a72e9": "0x00000000000000c42b56a52aedf18667c8ae258a0280a8912641c80c48cd9548", + "0x8d8ebb65ec00cb973d4fe086a607728fd1b9de14aa48208381eed9592f0dee9a": "0x00000000000000784ae4881e40b1f5ebb4437905fbb8a5914454123b0293b35f", + "0xff896b09014882056009dedb136458f017fcef9a4729467d0d00b4fd413fb1f1": "0x000000000000000e78ac39cb1c20e9edc753623b153705d0ccc487e31f9d6749" + } + } + }"#; + + #[test] + fn test_serialize_mux_tracer_config() { + let mut opts = GethDebugTracingCallOptions::default(); + opts.tracing_options.tracer = + Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::MuxTracer)); + + let call_config = CallConfig { only_top_call: Some(true), with_log: Some(true) }; + let prestate_config = PreStateConfig { diff_mode: Some(true) }; + + opts.tracing_options.tracer_config = serde_json::to_value(MuxConfig(HashMap::from([ + (GethDebugBuiltInTracerType::FourByteTracer, None), + ( + GethDebugBuiltInTracerType::CallTracer, + Some(GethDebugTracerConfig(serde_json::to_value(call_config).unwrap())), + ), + ( + GethDebugBuiltInTracerType::PreStateTracer, + Some(GethDebugTracerConfig(serde_json::to_value(prestate_config).unwrap())), + ), + ]))) + .unwrap() + .into(); + + assert_eq!( + serde_json::to_string(&opts).unwrap(), + r#"{"tracer":"muxTracer","tracerConfig":{"4byteTracer":null,"callTracer":{"onlyTopCall":true,"withLog":true},"prestateTracer":{"diffMode":true}}}"#, + ); + } + + #[test] + fn test_deserialize_mux_frame() { + let expected = HashMap::from([ + ( + GethDebugBuiltInTracerType::FourByteTracer, + GethTrace::FourByteTracer(serde_json::from_str(FOUR_BYTE_FRAME).unwrap()), + ), + ( + GethDebugBuiltInTracerType::CallTracer, + GethTrace::CallTracer(serde_json::from_str(CALL_FRAME_WITH_LOG).unwrap()), + ), + ( + GethDebugBuiltInTracerType::PreStateTracer, + GethTrace::PreStateTracer(serde_json::from_str(PRESTATE_FRAME).unwrap()), + ), + ]); + + let raw_frame = serde_json::to_string(&expected).unwrap(); + let trace: MuxFrame = serde_json::from_str(&raw_frame).unwrap(); + + assert_eq!( + trace.0[&GethDebugBuiltInTracerType::FourByteTracer], + expected[&GethDebugBuiltInTracerType::FourByteTracer] + ); + assert_eq!( + trace.0[&GethDebugBuiltInTracerType::CallTracer], + expected[&GethDebugBuiltInTracerType::CallTracer] + ); + assert_eq!( + trace.0[&GethDebugBuiltInTracerType::PreStateTracer], + expected[&GethDebugBuiltInTracerType::PreStateTracer] + ); + } +}