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