anvil/eth/backend/mem/
inspector.rs

1//! Anvil specific [`revm::Inspector`] implementation
2
3use crate::eth::macros::node_info;
4use alloy_primitives::{Address, Log, U256};
5use foundry_evm::{
6    call_inspectors,
7    decode::decode_console_logs,
8    inspectors::{LogCollector, TracingInspector},
9    traces::{
10        CallTraceDecoder, SparsedTraceArena, TracingInspectorConfig, render_trace_arena_inner,
11    },
12};
13use revm::{
14    Inspector,
15    context::ContextTr,
16    inspector::JournalExt,
17    interpreter::{
18        CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter,
19        interpreter::EthInterpreter,
20    },
21};
22use std::sync::Arc;
23
24/// The [`revm::Inspector`] used when transacting in the evm
25#[derive(Clone, Debug, Default)]
26pub struct AnvilInspector {
27    /// Collects all traces
28    pub tracer: Option<TracingInspector>,
29    /// Collects all `console.sol` logs
30    pub log_collector: Option<LogCollector>,
31}
32
33impl AnvilInspector {
34    /// Called after the inspecting the evm
35    ///
36    /// This will log all `console.sol` logs
37    pub fn print_logs(&self) {
38        if let Some(collector) = &self.log_collector {
39            print_logs(&collector.logs);
40        }
41    }
42
43    /// Consumes the type and prints the traces.
44    pub fn into_print_traces(mut self, decoder: Arc<CallTraceDecoder>) {
45        if let Some(a) = self.tracer.take() {
46            print_traces(a, decoder);
47        }
48    }
49
50    /// Called after the inspecting the evm
51    /// This will log all traces
52    pub fn print_traces(&self, decoder: Arc<CallTraceDecoder>) {
53        if let Some(a) = self.tracer.clone() {
54            print_traces(a, decoder);
55        }
56    }
57
58    /// Configures the `Tracer` [`revm::Inspector`]
59    pub fn with_tracing(mut self) -> Self {
60        self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().set_steps(false)));
61        self
62    }
63
64    /// Configures the `TracingInspector` [`revm::Inspector`]
65    pub fn with_tracing_config(mut self, config: TracingInspectorConfig) -> Self {
66        self.tracer = Some(TracingInspector::new(config));
67        self
68    }
69
70    /// Enables steps recording for `Tracer`.
71    pub fn with_steps_tracing(mut self) -> Self {
72        self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
73        self
74    }
75
76    /// Configures the `Tracer` [`revm::Inspector`] with a log collector
77    pub fn with_log_collector(mut self) -> Self {
78        self.log_collector = Some(Default::default());
79        self
80    }
81
82    /// Configures the `Tracer` [`revm::Inspector`] with a trace printer
83    pub fn with_trace_printer(mut self) -> Self {
84        self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
85        self
86    }
87}
88
89/// Prints the traces for the inspector
90///
91/// Caution: This blocks on call trace decoding
92///
93/// # Panics
94///
95/// If called outside tokio runtime
96fn print_traces(tracer: TracingInspector, decoder: Arc<CallTraceDecoder>) {
97    let arena = tokio::task::block_in_place(move || {
98        tokio::runtime::Handle::current().block_on(async move {
99            let mut arena = tracer.into_traces();
100            decoder.populate_traces(arena.nodes_mut()).await;
101            arena
102        })
103    });
104
105    let traces = SparsedTraceArena { arena, ignored: Default::default() };
106    node_info!("Traces:");
107    node_info!("{}", render_trace_arena_inner(&traces, false, true));
108}
109
110impl<CTX> Inspector<CTX, EthInterpreter> for AnvilInspector
111where
112    CTX: ContextTr<Journal: JournalExt>,
113{
114    fn initialize_interp(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
115        call_inspectors!([&mut self.tracer], |inspector| {
116            inspector.initialize_interp(interp, ecx);
117        });
118    }
119
120    fn step(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
121        call_inspectors!([&mut self.tracer], |inspector| {
122            inspector.step(interp, ecx);
123        });
124    }
125
126    fn step_end(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
127        call_inspectors!([&mut self.tracer], |inspector| {
128            inspector.step_end(interp, ecx);
129        });
130    }
131
132    #[allow(clippy::redundant_clone)]
133    fn log(&mut self, interp: &mut Interpreter, ecx: &mut CTX, log: Log) {
134        call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
135            inspector.log(interp, ecx, log.clone());
136        });
137    }
138
139    fn call(&mut self, ecx: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
140        call_inspectors!(
141            #[ret]
142            [&mut self.tracer, &mut self.log_collector],
143            |inspector| inspector.call(ecx, inputs).map(Some),
144        );
145        None
146    }
147
148    fn call_end(&mut self, ecx: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
149        if let Some(tracer) = &mut self.tracer {
150            tracer.call_end(ecx, inputs, outcome);
151        }
152    }
153
154    fn create(&mut self, ecx: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
155        if let Some(tracer) = &mut self.tracer
156            && let Some(out) = tracer.create(ecx, inputs)
157        {
158            return Some(out);
159        }
160        None
161    }
162
163    fn create_end(&mut self, ecx: &mut CTX, inputs: &CreateInputs, outcome: &mut CreateOutcome) {
164        if let Some(tracer) = &mut self.tracer {
165            tracer.create_end(ecx, inputs, outcome);
166        }
167    }
168
169    #[inline]
170    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
171        if let Some(tracer) = &mut self.tracer {
172            <TracingInspector as Inspector<CTX>>::selfdestruct(tracer, contract, target, value);
173        }
174    }
175}
176
177/// Prints all the logs
178pub fn print_logs(logs: &[Log]) {
179    for log in decode_console_logs(logs) {
180        tracing::info!(target: crate::logging::EVM_CONSOLE_LOG_TARGET, "{}", log);
181    }
182}