Skip to content

Commit

Permalink
feat(rpc-trace-types): add support for mux tracer (#252)
Browse files Browse the repository at this point in the history
* feat(rpc-trace-types): add support for mux tracer

* cleanup formatting

* fix nits
  • Loading branch information
ArtificialPB authored Mar 6, 2024
1 parent a77ce1d commit 64d0352
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 2 deletions.
25 changes: 23 additions & 2 deletions crates/rpc-trace-types/src/geth/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -19,6 +20,7 @@ pub use self::{

pub mod call;
pub mod four_byte;
pub mod mux;
pub mod noop;
pub mod pre_state;

Expand Down Expand Up @@ -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),
}
Expand Down Expand Up @@ -150,10 +154,16 @@ impl From<NoopFrame> for GethTrace {
}
}

impl From<MuxFrame> for GethTrace {
fn from(value: MuxFrame) -> Self {
GethTrace::MuxTracer(value)
}
}

/// Available built-in tracers
///
/// See <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
#[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
Expand All @@ -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
Expand Down Expand Up @@ -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<PreStateConfig, serde_json::Error> {
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<MuxConfig, serde_json::Error> {
if self.0.is_null() {
return Ok(Default::default());
}
self.from_value()
}
}

impl From<serde_json::Value> for GethDebugTracerConfig {
Expand Down
115 changes: 115 additions & 0 deletions crates/rpc-trace-types/src/geth/mux.rs
Original file line number Diff line number Diff line change
@@ -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<GethDebugBuiltInTracerType, Option<GethDebugTracerConfig>>);

/// A `muxTracer` frame response that contains the results of multiple tracers
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct MuxFrame(pub HashMap<GethDebugBuiltInTracerType, GethTrace>);

#[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]
);
}
}

0 comments on commit 64d0352

Please sign in to comment.