-
Notifications
You must be signed in to change notification settings - Fork 796
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: add support for Geth built-in tracer and config #2121
Changes from all commits
c488909
31350e5
67011ee
92747e7
03de62a
09d099d
867d069
8ecd729
2e15a62
8a06bf4
56eb4eb
7e83da5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
use crate::{ | ||
types::{Address, Bytes, NameOrAddress, H256, U256}, | ||
utils::from_int_or_hex, | ||
}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
// https://github.com/ethereum/go-ethereum/blob/91cb6f863a965481e51d5d9c0e5ccd54796fd967/eth/tracers/native/call.go#L44 | ||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] | ||
pub struct CallFrame { | ||
#[serde(rename = "type")] | ||
pub typ: String, | ||
pub from: Address, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub to: Option<NameOrAddress>, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub value: Option<U256>, | ||
#[serde(default, deserialize_with = "from_int_or_hex")] | ||
pub gas: U256, | ||
#[serde(default, deserialize_with = "from_int_or_hex", rename = "gasUsed")] | ||
pub gas_used: U256, | ||
pub input: Bytes, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub output: Option<Bytes>, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub error: Option<String>, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub calls: Option<Vec<CallFrame>>, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub logs: Option<Vec<CallLogFrame>>, | ||
} | ||
|
||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] | ||
pub struct CallLogFrame { | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
address: Option<Address>, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
topics: Option<Vec<H256>>, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
data: Option<Bytes>, | ||
} | ||
|
||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct CallConfig { | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub only_top_call: Option<bool>, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub with_log: Option<bool>, | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::types::*; | ||
|
||
// See <https://github.com/ethereum/go-ethereum/tree/master/eth/tracers/internal/tracetest/testdata> | ||
const DEFAULT: &str = include_str!("./test_data/call_tracer/default.json"); | ||
const LEGACY: &str = include_str!("./test_data/call_tracer/legacy.json"); | ||
const ONLY_TOP_CALL: &str = include_str!("./test_data/call_tracer/only_top_call.json"); | ||
const WITH_LOG: &str = include_str!("./test_data/call_tracer/with_log.json"); | ||
|
||
#[test] | ||
fn test_serialize_call_trace() { | ||
let mut opts = GethDebugTracingCallOptions::default(); | ||
opts.tracing_options.disable_storage = Some(false); | ||
opts.tracing_options.tracer = | ||
Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer)); | ||
opts.tracing_options.tracer_config = | ||
Some(GethDebugTracerConfig::BuiltInTracer(GethDebugBuiltInTracerConfig::CallTracer( | ||
CallConfig { only_top_call: Some(true), with_log: Some(true) }, | ||
))); | ||
|
||
assert_eq!( | ||
serde_json::to_string(&opts).unwrap(), | ||
r#"{"disableStorage":false,"tracer":"callTracer","tracerConfig":{"onlyTopCall":true,"withLog":true}}"# | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_deserialize_call_trace() { | ||
let _trace: CallFrame = serde_json::from_str(DEFAULT).unwrap(); | ||
let _trace: CallFrame = serde_json::from_str(LEGACY).unwrap(); | ||
let _trace: CallFrame = serde_json::from_str(ONLY_TOP_CALL).unwrap(); | ||
let trace: CallFrame = serde_json::from_str(WITH_LOG).unwrap(); | ||
let _logs = trace.logs.unwrap(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
use serde::{Deserialize, Serialize}; | ||
use std::collections::BTreeMap; | ||
|
||
// https://github.com/ethereum/go-ethereum/blob/91cb6f863a965481e51d5d9c0e5ccd54796fd967/eth/tracers/native/4byte.go#L48 | ||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] | ||
pub struct FourByteFrame(pub BTreeMap<String, u64>); | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::types::*; | ||
|
||
const DEFAULT: &str = r#"{ | ||
"0x27dc297e-128": 1, | ||
"0x38cc4831-0": 2, | ||
"0x524f3889-96": 1, | ||
"0xadf59f99-288": 1, | ||
"0xc281d19e-0": 1 | ||
}"#; | ||
|
||
#[test] | ||
fn test_serialize_four_byte_trace() { | ||
let mut opts = GethDebugTracingCallOptions::default(); | ||
opts.tracing_options.tracer = | ||
Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::FourByteTracer)); | ||
|
||
assert_eq!(serde_json::to_string(&opts).unwrap(), r#"{"tracer":"4byteTracer"}"#); | ||
} | ||
|
||
#[test] | ||
fn test_deserialize_four_byte_trace() { | ||
let _trace: FourByteFrame = serde_json::from_str(DEFAULT).unwrap(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
use serde::{Deserialize, Serialize}; | ||
use std::collections::BTreeMap; | ||
|
||
// https://github.com/ethereum/go-ethereum/blob/91cb6f863a965481e51d5d9c0e5ccd54796fd967/eth/tracers/native/noop.go#L34 | ||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] | ||
pub struct NoopFrame(BTreeMap<Null, Null>); | ||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] | ||
struct Null; | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::types::*; | ||
|
||
const DEFAULT: &str = r#"{}"#; | ||
|
||
#[test] | ||
fn test_serialize_noop_trace() { | ||
let mut opts = GethDebugTracingCallOptions::default(); | ||
opts.tracing_options.tracer = | ||
Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::NoopTracer)); | ||
|
||
assert_eq!(serde_json::to_string(&opts).unwrap(), r#"{"tracer":"noopTracer"}"#); | ||
} | ||
|
||
#[test] | ||
fn test_deserialize_noop_trace() { | ||
let _trace: NoopFrame = serde_json::from_str(DEFAULT).unwrap(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
use crate::{ | ||
types::{Address, H256, U256}, | ||
utils::from_int_or_hex_opt, | ||
}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::collections::BTreeMap; | ||
|
||
// https://github.com/ethereum/go-ethereum/blob/91cb6f863a965481e51d5d9c0e5ccd54796fd967/eth/tracers/native/prestate.go#L38 | ||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] | ||
#[serde(untagged)] | ||
pub enum PreStateFrame { | ||
Default(PreStateMode), | ||
Diff(DiffMode), | ||
} | ||
|
||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] | ||
pub struct PreStateMode(pub BTreeMap<Address, AccountState>); | ||
Comment on lines
+16
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should probably b There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems transparent is enabled for newtype by default? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL |
||
|
||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] | ||
pub struct DiffMode { | ||
pub pre: BTreeMap<Address, AccountState>, | ||
pub post: BTreeMap<Address, AccountState>, | ||
} | ||
|
||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] | ||
pub struct AccountState { | ||
#[serde( | ||
default, | ||
deserialize_with = "from_int_or_hex_opt", | ||
skip_serializing_if = "Option::is_none" | ||
)] | ||
pub balance: Option<U256>, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub code: Option<String>, | ||
#[serde( | ||
default, | ||
deserialize_with = "from_int_or_hex_opt", | ||
skip_serializing_if = "Option::is_none" | ||
)] | ||
pub nonce: Option<U256>, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub storage: Option<BTreeMap<H256, H256>>, | ||
} | ||
|
||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct PreStateConfig { | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub diff_mode: Option<bool>, | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::types::*; | ||
|
||
// See <https://github.com/ethereum/go-ethereum/tree/master/eth/tracers/internal/tracetest/testdata> | ||
const DEFAULT: &str = include_str!("./test_data/pre_state_tracer/default.json"); | ||
const LEGACY: &str = include_str!("./test_data/pre_state_tracer/legacy.json"); | ||
const DIFF_MODE: &str = include_str!("./test_data/pre_state_tracer/diff_mode.json"); | ||
|
||
#[test] | ||
fn test_serialize_pre_state_trace() { | ||
let mut opts = GethDebugTracingCallOptions::default(); | ||
opts.tracing_options.disable_storage = Some(false); | ||
opts.tracing_options.tracer = | ||
Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::PreStateTracer)); | ||
opts.tracing_options.tracer_config = Some(GethDebugTracerConfig::BuiltInTracer( | ||
GethDebugBuiltInTracerConfig::PreStateTracer(PreStateConfig { diff_mode: Some(true) }), | ||
)); | ||
|
||
assert_eq!( | ||
serde_json::to_string(&opts).unwrap(), | ||
r#"{"disableStorage":false,"tracer":"prestateTracer","tracerConfig":{"diffMode":true}}"# | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_deserialize_pre_state_trace() { | ||
let trace: PreStateFrame = serde_json::from_str(DEFAULT).unwrap(); | ||
match trace { | ||
PreStateFrame::Default(PreStateMode(_)) => {} | ||
_ => unreachable!(), | ||
} | ||
let _trace: PreStateFrame = serde_json::from_str(LEGACY).unwrap(); | ||
let trace: PreStateFrame = serde_json::from_str(DIFF_MODE).unwrap(); | ||
match trace { | ||
PreStateFrame::Diff(DiffMode { pre: _pre, post: _post }) => {} | ||
_ => unreachable!(), | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
gas
andgas_used
fields are not compatible with the legacy format. Should we change it toOption
, and which might be a break change?For now, to be compatible with previous versions, I've only added a default handling, which returns
U256::zero()
instead ofNone
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default
is fine w me