Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rpc-trace-types): add support for mux tracer #252

Merged
merged 3 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+one line doc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

/// 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]
);
}
}
Loading