revm_inspector/
handler.rs

1use crate::{Inspector, InspectorEvmTr, InspectorFrame, JournalExt};
2use context::{result::ExecutionResult, ContextTr, JournalEntry, Transaction};
3use handler::{EvmTr, Frame, FrameInitOrResult, FrameOrResult, FrameResult, Handler, ItemOrResult};
4use interpreter::{
5    instructions::InstructionTable,
6    interpreter_types::{Jumps, LoopControl},
7    FrameInput, Host, InitialAndFloorGas, InstructionContext, InstructionResult, Interpreter,
8    InterpreterAction, InterpreterTypes,
9};
10
11use std::{vec, vec::Vec};
12
13/// Trait that extends [`Handler`] with inspection functionality.
14///
15/// Similar how [`Handler::run`] method serves as the entry point,
16/// [`InspectorHandler::inspect_run`] method serves as the entry point for inspection.
17///
18/// Notice that when inspection is run it skips few functions from handler, this can be
19/// a problem if custom EVM is implemented and some of skipped functions have changed logic.
20/// For custom EVM, those changed functions would need to be also changed in [`InspectorHandler`].
21///
22/// List of functions that are skipped in [`InspectorHandler`]:
23/// * [`Handler::run`] replaced with [`InspectorHandler::inspect_run`]
24/// * [`Handler::run_without_catch_error`] replaced with [`InspectorHandler::inspect_run_without_catch_error`]
25/// * [`Handler::execution`] replaced with [`InspectorHandler::inspect_execution`]
26/// * [`Handler::first_frame_init`] replaced with [`InspectorHandler::inspect_first_frame_init`]
27/// * [`Handler::frame_call`] replaced with [`InspectorHandler::inspect_frame_call`]
28/// * [`Handler::run_exec_loop`] replaced with [`InspectorHandler::inspect_run_exec_loop`]
29pub trait InspectorHandler: Handler
30where
31    Self::Evm:
32        InspectorEvmTr<Inspector: Inspector<<<Self as Handler>::Evm as EvmTr>::Context, Self::IT>>,
33    Self::Frame: InspectorFrame<IT = Self::IT>,
34{
35    type IT: InterpreterTypes;
36
37    /// Entry point for inspection.
38    ///
39    /// This method is acts as [`Handler::run`] method for inspection.
40    fn inspect_run(
41        &mut self,
42        evm: &mut Self::Evm,
43    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
44        match self.inspect_run_without_catch_error(evm) {
45            Ok(output) => Ok(output),
46            Err(e) => self.catch_error(evm, e),
47        }
48    }
49
50    /// Run inspection without catching error.
51    ///
52    /// This method is acts as [`Handler::run_without_catch_error`] method for inspection.
53    fn inspect_run_without_catch_error(
54        &mut self,
55        evm: &mut Self::Evm,
56    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
57        let init_and_floor_gas = self.validate(evm)?;
58        let eip7702_refund = self.pre_execution(evm)? as i64;
59        let mut frame_result = self.inspect_execution(evm, &init_and_floor_gas)?;
60        self.post_execution(evm, &mut frame_result, init_and_floor_gas, eip7702_refund)?;
61        self.execution_result(evm, frame_result)
62    }
63
64    /// Run execution loop with inspection support
65    ///
66    /// This method acts as [`Handler::execution`] method for inspection.
67    fn inspect_execution(
68        &mut self,
69        evm: &mut Self::Evm,
70        init_and_floor_gas: &InitialAndFloorGas,
71    ) -> Result<FrameResult, Self::Error> {
72        let gas_limit = evm.ctx().tx().gas_limit() - init_and_floor_gas.initial_gas;
73
74        // Create first frame action
75        let first_frame_input = self.first_frame_input(evm, gas_limit)?;
76        let first_frame = self.inspect_first_frame_init(evm, first_frame_input)?;
77
78        let mut frame_result = match first_frame {
79            ItemOrResult::Item(frame) => self.inspect_run_exec_loop(evm, frame)?,
80            ItemOrResult::Result(result) => result,
81        };
82
83        self.last_frame_result(evm, &mut frame_result)?;
84        Ok(frame_result)
85    }
86
87    /* FRAMES */
88
89    /// Initialize first frame.
90    ///
91    /// This method replaces the [`Handler::first_frame_init`] method from [`Handler`].
92    ///
93    /// * It calls [`Inspector::call`]/[`Inspector::create`]/[`Inspector::eofcreate`] methods to allow inspection of
94    ///   the frame and its modification.
95    /// * If new frame is created a [`Inspector::initialize_interp`] method will be called.
96    /// * If creation of new frame returns the result, the [`Inspector`] `_end` methods will be called.
97    fn inspect_first_frame_init(
98        &mut self,
99        evm: &mut Self::Evm,
100        mut frame_input: <Self::Frame as Frame>::FrameInit,
101    ) -> Result<FrameOrResult<Self::Frame>, Self::Error> {
102        let (ctx, inspector) = evm.ctx_inspector();
103        if let Some(mut output) = frame_start(ctx, inspector, &mut frame_input) {
104            frame_end(ctx, inspector, &frame_input, &mut output);
105            return Ok(ItemOrResult::Result(output));
106        }
107        let mut ret = self.first_frame_init(evm, frame_input.clone());
108
109        // only if new frame is created call initialize_interp hook.
110        if let Ok(ItemOrResult::Item(frame)) = &mut ret {
111            let (context, inspector) = evm.ctx_inspector();
112            inspector.initialize_interp(frame.interpreter(), context);
113        } else if let Ok(ItemOrResult::Result(result)) = &mut ret {
114            let (context, inspector) = evm.ctx_inspector();
115            frame_end(context, inspector, &frame_input, result);
116        }
117        ret
118    }
119
120    /// Run inspection on frame.
121    ///
122    /// This method acts as [`Handler::frame_call`] method for inspection.
123    ///
124    /// Internally it will call [`Inspector::step`], [`Inspector::step_end`] for each instruction.
125    /// And [`Inspector::log`],[`Inspector::selfdestruct`] for each log and selfdestruct instruction.
126    #[inline]
127    fn inspect_frame_call(
128        &mut self,
129        frame: &mut Self::Frame,
130        evm: &mut Self::Evm,
131    ) -> Result<FrameInitOrResult<Self::Frame>, Self::Error> {
132        frame.run_inspect(evm)
133    }
134
135    /// Run inspection on execution loop.
136    ///
137    /// This method acts as [`Handler::run_exec_loop`] method for inspection.
138    ///
139    /// It will call:
140    /// * [`InspectorHandler::inspect_frame_call`] to inspect Interpreter execution loop.
141    /// * [`Inspector::call`],[`Inspector::create`],[`Inspector::eofcreate`] to inspect call, create and eofcreate.
142    /// * [`Inspector::call_end`],[`Inspector::create_end`],[`Inspector::eofcreate_end`] to inspect call, create and eofcreate end.
143    /// * [`Inspector::initialize_interp`] to inspect initialized interpreter.
144    fn inspect_run_exec_loop(
145        &mut self,
146        evm: &mut Self::Evm,
147        frame: Self::Frame,
148    ) -> Result<FrameResult, Self::Error> {
149        let mut frame_stack: Vec<Self::Frame> = vec![frame];
150        loop {
151            let frame = frame_stack.last_mut().unwrap();
152            let call_or_result = self.inspect_frame_call(frame, evm)?;
153
154            let result = match call_or_result {
155                ItemOrResult::Item(mut init) => {
156                    let (context, inspector) = evm.ctx_inspector();
157                    if let Some(mut output) = frame_start(context, inspector, &mut init) {
158                        frame_end(context, inspector, &init, &mut output);
159                        output
160                    } else {
161                        match self.frame_init(frame, evm, init.clone())? {
162                            ItemOrResult::Item(mut new_frame) => {
163                                // only if new frame is created call initialize_interp hook.
164                                let (context, inspector) = evm.ctx_inspector();
165                                inspector.initialize_interp(new_frame.interpreter(), context);
166                                frame_stack.push(new_frame);
167                                continue;
168                            }
169                            // Dont pop the frame as new frame was not created.
170                            ItemOrResult::Result(mut result) => {
171                                let (context, inspector) = evm.ctx_inspector();
172                                frame_end(context, inspector, &init, &mut result);
173                                result
174                            }
175                        }
176                    }
177                }
178                ItemOrResult::Result(mut result) => {
179                    let (context, inspector) = evm.ctx_inspector();
180                    frame_end(context, inspector, frame.frame_input(), &mut result);
181
182                    // Pop frame that returned result
183                    frame_stack.pop();
184                    result
185                }
186            };
187
188            let Some(frame) = frame_stack.last_mut() else {
189                return Ok(result);
190            };
191
192            self.frame_return_result(frame, evm, result)?;
193        }
194    }
195}
196
197pub fn frame_start<CTX, INTR: InterpreterTypes>(
198    context: &mut CTX,
199    inspector: &mut impl Inspector<CTX, INTR>,
200    frame_input: &mut FrameInput,
201) -> Option<FrameResult> {
202    match frame_input {
203        FrameInput::Call(i) => {
204            if let Some(output) = inspector.call(context, i) {
205                return Some(FrameResult::Call(output));
206            }
207        }
208        FrameInput::Create(i) => {
209            if let Some(output) = inspector.create(context, i) {
210                return Some(FrameResult::Create(output));
211            }
212        }
213        FrameInput::EOFCreate(i) => {
214            if let Some(output) = inspector.eofcreate(context, i) {
215                return Some(FrameResult::EOFCreate(output));
216            }
217        }
218    }
219    None
220}
221
222pub fn frame_end<CTX, INTR: InterpreterTypes>(
223    context: &mut CTX,
224    inspector: &mut impl Inspector<CTX, INTR>,
225    frame_input: &FrameInput,
226    frame_output: &mut FrameResult,
227) {
228    match frame_output {
229        FrameResult::Call(outcome) => {
230            let FrameInput::Call(i) = frame_input else {
231                panic!("FrameInput::Call expected");
232            };
233            inspector.call_end(context, i, outcome);
234        }
235        FrameResult::Create(outcome) => {
236            let FrameInput::Create(i) = frame_input else {
237                panic!("FrameInput::Create expected");
238            };
239            inspector.create_end(context, i, outcome);
240        }
241        FrameResult::EOFCreate(outcome) => {
242            let FrameInput::EOFCreate(i) = frame_input else {
243                panic!("FrameInput::EofCreate expected");
244            };
245            inspector.eofcreate_end(context, i, outcome);
246        }
247    }
248}
249
250/// Run Interpreter loop with inspection support.
251///
252/// This function is used to inspect the Interpreter loop.
253/// It will call [`Inspector::step`] and [`Inspector::step_end`] after each instruction.
254/// And [`Inspector::log`],[`Inspector::selfdestruct`] for each log and selfdestruct instruction.
255pub fn inspect_instructions<CTX, IT>(
256    context: &mut CTX,
257    interpreter: &mut Interpreter<IT>,
258    mut inspector: impl Inspector<CTX, IT>,
259    instructions: &InstructionTable<IT, CTX>,
260) -> InterpreterAction
261where
262    CTX: ContextTr<Journal: JournalExt> + Host,
263    IT: InterpreterTypes,
264{
265    let mut log_num = context.journal_mut().logs().len();
266    // Main loop
267    while interpreter.bytecode.is_not_end() {
268        // Get current opcode.
269        let opcode = interpreter.bytecode.opcode();
270
271        // Call Inspector step.
272        inspector.step(interpreter, context);
273        if interpreter.bytecode.is_end() {
274            break;
275        }
276
277        // SAFETY: In analysis we are doing padding of bytecode so that we are sure that last
278        // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction
279        // it will do noop and just stop execution of this contract
280        interpreter.bytecode.relative_jump(1);
281
282        // Execute instruction.
283        let instruction_context = InstructionContext {
284            interpreter,
285            host: context,
286        };
287        instructions[opcode as usize](instruction_context);
288
289        // check if new log is added
290        let new_log = context.journal_mut().logs().len();
291        if log_num < new_log {
292            // as there is a change in log number this means new log is added
293            let log = context.journal_mut().logs().last().unwrap().clone();
294            inspector.log(interpreter, context, log);
295            log_num = new_log;
296        }
297
298        // Call step_end.
299        inspector.step_end(interpreter, context);
300    }
301
302    interpreter.bytecode.revert_to_previous_pointer();
303
304    let next_action = interpreter.take_next_action();
305
306    // handle selfdestruct
307    if let InterpreterAction::Return(result) = &next_action {
308        if result.result == InstructionResult::SelfDestruct {
309            match context.journal_mut().journal().last() {
310                Some(JournalEntry::AccountDestroyed {
311                    address,
312                    target,
313                    had_balance,
314                    ..
315                }) => {
316                    inspector.selfdestruct(*address, *target, *had_balance);
317                }
318                Some(JournalEntry::BalanceTransfer {
319                    from, to, balance, ..
320                }) => {
321                    inspector.selfdestruct(*from, *to, *balance);
322                }
323                _ => {}
324            }
325        }
326    }
327
328    next_action
329}