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(anvil): add support for trace logging in Anvil #9895

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions crates/anvil/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ impl NodeArgs {
.with_genesis(self.init)
.with_steps_tracing(self.evm.steps_tracing)
.with_print_logs(!self.evm.disable_console_log)
.with_print_traces(self.evm.print_traces)
.with_auto_impersonate(self.evm.auto_impersonate)
.with_ipc(self.ipc)
.with_code_size_limit(self.evm.code_size_limit)
Expand Down Expand Up @@ -559,6 +560,10 @@ pub struct AnvilEvmArgs {
#[arg(long, visible_alias = "no-console-log")]
pub disable_console_log: bool,

/// Enable printing of traces for executed transactions and `eth_call` to stdout.
#[arg(long, visible_alias = "enable-trace-printing")]
pub print_traces: bool,

/// Enables automatic impersonation on startup. This allows any transaction sender to be
/// simulated as different accounts, which is useful for testing contract behavior.
#[arg(long, visible_alias = "auto-unlock")]
Expand Down
11 changes: 11 additions & 0 deletions crates/anvil/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ pub struct NodeConfig {
pub enable_steps_tracing: bool,
/// Enable printing of `console.log` invocations.
pub print_logs: bool,
/// Enable printing of traces.
pub print_traces: bool,
/// Enable auto impersonation of accounts on startup
pub enable_auto_impersonate: bool,
/// Configure the code size limit
Expand Down Expand Up @@ -435,6 +437,7 @@ impl Default for NodeConfig {
enable_tracing: true,
enable_steps_tracing: false,
print_logs: true,
print_traces: false,
enable_auto_impersonate: false,
no_storage_caching: false,
server_config: Default::default(),
Expand Down Expand Up @@ -863,6 +866,13 @@ impl NodeConfig {
self
}

/// Sets whether to print traces to stdout.
#[must_use]
pub fn with_print_traces(mut self, print_traces: bool) -> Self {
self.print_traces = print_traces;
self
}

/// Sets whether to enable autoImpersonate
#[must_use]
pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
Expand Down Expand Up @@ -1048,6 +1058,7 @@ impl NodeConfig {
Arc::new(RwLock::new(fork)),
self.enable_steps_tracing,
self.print_logs,
self.print_traces,
self.odyssey,
self.prune_history,
self.max_persisted_states,
Expand Down
8 changes: 8 additions & 0 deletions crates/anvil/src/eth/backend/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> {
pub enable_steps_tracing: bool,
pub odyssey: bool,
pub print_logs: bool,
pub print_traces: bool,
/// Precompiles to inject to the EVM.
pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
}
Expand Down Expand Up @@ -312,6 +313,9 @@ impl<DB: Db + ?Sized, V: TransactionValidator> Iterator for &mut TransactionExec
if self.print_logs {
inspector = inspector.with_log_collector();
}
if self.print_traces {
inspector = inspector.with_trace_printer();
}

let exec_result = {
let mut evm = new_evm_with_inspector(&mut *self.db, env, &mut inspector, self.odyssey);
Expand Down Expand Up @@ -345,6 +349,10 @@ impl<DB: Db + ?Sized, V: TransactionValidator> Iterator for &mut TransactionExec
}
}
};

if self.print_traces {
inspector.print_traces();
}
inspector.print_logs();

let (exit_reason, gas_used, out, logs) = match exec_result {
Expand Down
54 changes: 50 additions & 4 deletions crates/anvil/src/eth/backend/mem/inspector.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Anvil specific [`revm::Inspector`] implementation

use crate::revm::Database;
use crate::{eth::macros::node_info, revm::Database};
use alloy_primitives::{Address, Log};
use foundry_evm::{
call_inspectors,
Expand All @@ -13,14 +13,17 @@ use foundry_evm::{
primitives::U256,
EvmContext,
},
traces::TracingInspectorConfig,
traces::{
render_trace_arena_inner, CallTraceDecoder, SparsedTraceArena, TracingInspectorConfig,
},
};

/// The [`revm::Inspector`] used when transacting in the evm
#[derive(Clone, Debug, Default)]
pub struct Inspector {
/// Collects all traces
pub tracer: Option<TracingInspector>,
/// collects all `console.sol` logs
/// Collects all `console.sol` logs
pub log_collector: Option<LogCollector>,
}

Expand All @@ -34,6 +37,21 @@ impl Inspector {
}
}

/// Consumes the type and prints the traces.
pub fn into_print_traces(mut self) {
if let Some(a) = self.tracer.take() {
print_traces(a)
}
}

/// Called after the inspecting the evm
/// This will log all traces
pub fn print_traces(&self) {
if let Some(a) = self.tracer.clone() {
print_traces(a)
}
}

/// Configures the `Tracer` [`revm::Inspector`]
pub fn with_tracing(mut self) -> Self {
self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().set_steps(false)));
Expand All @@ -51,11 +69,39 @@ impl Inspector {
self
}

/// Configures the `Tracer` [`revm::Inspector`]
/// Configures the `Tracer` [`revm::Inspector`] with a log collector
pub fn with_log_collector(mut self) -> Self {
self.log_collector = Some(Default::default());
self
}

/// Configures the `Tracer` [`revm::Inspector`] with a trace printer
pub fn with_trace_printer(mut self) -> Self {
self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
self
}
}

/// Prints the traces for the inspector
///
/// Caution: This blocks on call trace decoding
///
/// # Panics
///
/// If called outside tokio runtime
fn print_traces(tracer: TracingInspector) {
let arena = tokio::task::block_in_place(move || {
tokio::runtime::Handle::current().block_on(async move {
let mut arena = tracer.into_traces();
let decoder = CallTraceDecoder::new();
decoder.populate_traces(arena.nodes_mut()).await;
arena
})
});

let traces = SparsedTraceArena { arena, ignored: Default::default() };
node_info!("Traces:");
node_info!("{}", render_trace_arena_inner(&traces, false, true));
}

impl<DB: Database> revm::Inspector<DB> for Inspector {
Expand Down
19 changes: 18 additions & 1 deletion crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ pub struct Backend {
active_state_snapshots: Arc<Mutex<HashMap<U256, (u64, B256)>>>,
enable_steps_tracing: bool,
print_logs: bool,
print_traces: bool,
odyssey: bool,
/// How to keep history state
prune_state_history_config: PruneStateHistoryConfig,
Expand Down Expand Up @@ -221,6 +222,7 @@ impl Backend {
fork: Arc<RwLock<Option<ClientFork>>>,
enable_steps_tracing: bool,
print_logs: bool,
print_traces: bool,
odyssey: bool,
prune_state_history_config: PruneStateHistoryConfig,
max_persisted_states: Option<usize>,
Expand Down Expand Up @@ -324,6 +326,7 @@ impl Backend {
active_state_snapshots: Arc::new(Mutex::new(Default::default())),
enable_steps_tracing,
print_logs,
print_traces,
odyssey,
prune_state_history_config,
transaction_block_keeper,
Expand Down Expand Up @@ -1055,6 +1058,10 @@ impl Backend {
drop(evm);
inspector.print_logs();

if self.print_traces {
inspector.print_traces();
}

Ok((exit_reason, out, gas_used, state, logs.unwrap_or_default()))
}

Expand Down Expand Up @@ -1095,6 +1102,7 @@ impl Backend {
blob_gas_used: 0,
enable_steps_tracing: self.enable_steps_tracing,
print_logs: self.print_logs,
print_traces: self.print_traces,
precompile_factory: self.precompile_factory.clone(),
odyssey: self.odyssey,
};
Expand Down Expand Up @@ -1178,6 +1186,7 @@ impl Backend {
blob_gas_used: 0,
enable_steps_tracing: self.enable_steps_tracing,
print_logs: self.print_logs,
print_traces: self.print_traces,
odyssey: self.odyssey,
precompile_factory: self.precompile_factory.clone(),
};
Expand Down Expand Up @@ -1424,13 +1433,16 @@ impl Backend {
env
}

/// Builds [`Inspector`] with the configured options
/// Builds [`Inspector`] with the configured options.
fn build_inspector(&self) -> Inspector {
let mut inspector = Inspector::default();

if self.print_logs {
inspector = inspector.with_log_collector();
}
if self.print_traces {
inspector = inspector.with_trace_printer();
}

inspector
}
Expand Down Expand Up @@ -1458,6 +1470,11 @@ impl Backend {
};
drop(evm);
inspector.print_logs();

if self.print_traces {
inspector.into_print_traces();
}

Ok((exit_reason, out, gas_used as u128, state))
}

Expand Down
Loading