anvil/eth/backend/mem/
inspector.rs

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