anvil/eth/backend/mem/
inspector.rs

1//! Anvil specific [`revm::Inspector`] implementation
2
3use crate::eth::macros::node_info;
4use alloy_evm::eth::EthEvmContext;
5use alloy_primitives::{Address, Log, U256};
6use foundry_evm::{
7    backend::DatabaseError,
8    call_inspectors,
9    decode::decode_console_logs,
10    inspectors::{LogCollector, TracingInspector},
11    traces::{
12        render_trace_arena_inner, CallTraceDecoder, SparsedTraceArena, TracingInspectorConfig,
13    },
14};
15use revm::{
16    context::ContextTr,
17    inspector::JournalExt,
18    interpreter::{
19        interpreter::EthInterpreter, CallInputs, CallOutcome, CreateInputs, CreateOutcome,
20        Interpreter,
21    },
22    Database, Inspector,
23};
24
25/// The [`revm::Inspector`] used when transacting in the evm
26#[derive(Clone, Debug, Default)]
27pub struct AnvilInspector {
28    /// Collects all traces
29    pub tracer: Option<TracingInspector>,
30    /// Collects all `console.sol` logs
31    pub log_collector: Option<LogCollector>,
32}
33
34impl AnvilInspector {
35    /// Called after the inspecting the evm
36    ///
37    /// This will log all `console.sol` logs
38    pub fn print_logs(&self) {
39        if let Some(collector) = &self.log_collector {
40            print_logs(&collector.logs);
41        }
42    }
43
44    /// Consumes the type and prints the traces.
45    pub fn into_print_traces(mut self) {
46        if let Some(a) = self.tracer.take() {
47            print_traces(a)
48        }
49    }
50
51    /// Called after the inspecting the evm
52    /// This will log all traces
53    pub fn print_traces(&self) {
54        if let Some(a) = self.tracer.clone() {
55            print_traces(a)
56        }
57    }
58
59    /// Configures the `Tracer` [`revm::Inspector`]
60    pub fn with_tracing(mut self) -> Self {
61        self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().set_steps(false)));
62        self
63    }
64
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) {
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            let decoder = CallTraceDecoder::new();
101            decoder.populate_traces(arena.nodes_mut()).await;
102            arena
103        })
104    });
105
106    let traces = SparsedTraceArena { arena, ignored: Default::default() };
107    node_info!("Traces:");
108    node_info!("{}", render_trace_arena_inner(&traces, false, true));
109}
110
111impl<CTX, D> Inspector<CTX, EthInterpreter> for AnvilInspector
112where
113    D: Database<Error = DatabaseError>,
114    CTX: ContextTr<Db = D>,
115    CTX::Journal: JournalExt,
116{
117    fn initialize_interp(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
118        call_inspectors!([&mut self.tracer], |inspector| {
119            inspector.initialize_interp(interp, ecx);
120        });
121    }
122
123    fn step(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
124        call_inspectors!([&mut self.tracer], |inspector| {
125            inspector.step(interp, ecx);
126        });
127    }
128
129    fn step_end(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
130        call_inspectors!([&mut self.tracer], |inspector| {
131            inspector.step_end(interp, ecx);
132        });
133    }
134
135    fn log(&mut self, interp: &mut Interpreter, ecx: &mut CTX, log: Log) {
136        call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
137            // TODO: rm the log.clone
138            inspector.log(interp, ecx, log.clone());
139        });
140    }
141
142    fn call(&mut self, ecx: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
143        call_inspectors!(
144            #[ret]
145            [&mut self.tracer, &mut self.log_collector],
146            |inspector| inspector.call(ecx, inputs).map(Some),
147        );
148        None
149    }
150
151    fn call_end(&mut self, ecx: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
152        if let Some(tracer) = &mut self.tracer {
153            tracer.call_end(ecx, inputs, outcome);
154        }
155    }
156
157    fn create(&mut self, ecx: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
158        if let Some(tracer) = &mut self.tracer {
159            if let Some(out) = tracer.create(ecx, inputs) {
160                return Some(out);
161            }
162        }
163        None
164    }
165
166    fn create_end(&mut self, ecx: &mut CTX, inputs: &CreateInputs, outcome: &mut CreateOutcome) {
167        if let Some(tracer) = &mut self.tracer {
168            tracer.create_end(ecx, inputs, outcome);
169        }
170    }
171
172    #[inline]
173    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
174        if let Some(tracer) = &mut self.tracer {
175            Inspector::<EthEvmContext<D>>::selfdestruct(tracer, contract, target, value);
176        }
177    }
178}
179
180/// Prints all the logs
181pub fn print_logs(logs: &[Log]) {
182    for log in decode_console_logs(logs) {
183        tracing::info!(target: crate::logging::EVM_CONSOLE_LOG_TARGET, "{}", log);
184    }
185}