revm_inspector/
handler.rs

1use crate::{Inspector, InspectorEvmTr, JournalExt};
2use context::{result::ExecutionResult, ContextTr, JournalEntry, Transaction};
3use handler::{evm::FrameTr, EvmTr, 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
11/// Trait that extends [`Handler`] with inspection functionality.
12///
13/// Similar how [`Handler::run`] method serves as the entry point,
14/// [`InspectorHandler::inspect_run`] method serves as the entry point for inspection.
15///
16/// Notice that when inspection is run it skips few functions from handler, this can be
17/// a problem if custom EVM is implemented and some of skipped functions have changed logic.
18/// For custom EVM, those changed functions would need to be also changed in [`InspectorHandler`].
19///
20/// List of functions that are skipped in [`InspectorHandler`]:
21/// * [`Handler::run`] replaced with [`InspectorHandler::inspect_run`]
22/// * [`Handler::run_without_catch_error`] replaced with [`InspectorHandler::inspect_run_without_catch_error`]
23/// * [`Handler::execution`] replaced with [`InspectorHandler::inspect_execution`]
24/// * [`Handler::run_exec_loop`] replaced with [`InspectorHandler::inspect_run_exec_loop`]
25///   * `run_exec_loop` calls `inspect_frame_init` and `inspect_frame_run` that call inspector inside.
26pub trait InspectorHandler: Handler
27where
28    Self::Evm:
29        InspectorEvmTr<Inspector: Inspector<<<Self as Handler>::Evm as EvmTr>::Context, Self::IT>>,
30{
31    /// The interpreter types used by this handler.
32    type IT: InterpreterTypes;
33
34    /// Entry point for inspection.
35    ///
36    /// This method is acts as [`Handler::run`] method for inspection.
37    fn inspect_run(
38        &mut self,
39        evm: &mut Self::Evm,
40    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
41        match self.inspect_run_without_catch_error(evm) {
42            Ok(output) => Ok(output),
43            Err(e) => self.catch_error(evm, e),
44        }
45    }
46
47    /// Run inspection without catching error.
48    ///
49    /// This method is acts as [`Handler::run_without_catch_error`] method for inspection.
50    fn inspect_run_without_catch_error(
51        &mut self,
52        evm: &mut Self::Evm,
53    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
54        let init_and_floor_gas = self.validate(evm)?;
55        let eip7702_refund = self.pre_execution(evm)? as i64;
56        let mut frame_result = self.inspect_execution(evm, &init_and_floor_gas)?;
57        self.post_execution(evm, &mut frame_result, init_and_floor_gas, eip7702_refund)?;
58        self.execution_result(evm, frame_result)
59    }
60
61    /// Run execution loop with inspection support
62    ///
63    /// This method acts as [`Handler::execution`] method for inspection.
64    fn inspect_execution(
65        &mut self,
66        evm: &mut Self::Evm,
67        init_and_floor_gas: &InitialAndFloorGas,
68    ) -> Result<FrameResult, Self::Error> {
69        let gas_limit = evm.ctx().tx().gas_limit() - init_and_floor_gas.initial_gas;
70        // Create first frame action
71        let first_frame_input = self.first_frame_input(evm, gas_limit)?;
72
73        // Run execution loop
74        let mut frame_result = self.inspect_run_exec_loop(evm, first_frame_input)?;
75
76        // Handle last frame result
77        self.last_frame_result(evm, &mut frame_result)?;
78        Ok(frame_result)
79    }
80
81    /* FRAMES */
82
83    /// Run inspection on execution loop.
84    ///
85    /// This method acts as [`Handler::run_exec_loop`] method for inspection.
86    ///
87    /// It will call:
88    /// * [`Inspector::call`],[`Inspector::create`] to inspect call, create and eofcreate.
89    /// * [`Inspector::call_end`],[`Inspector::create_end`] to inspect call, create and eofcreate end.
90    /// * [`Inspector::initialize_interp`] to inspect initialized interpreter.
91    fn inspect_run_exec_loop(
92        &mut self,
93        evm: &mut Self::Evm,
94        first_frame_input: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameInit,
95    ) -> Result<FrameResult, Self::Error> {
96        let res = evm.inspect_frame_init(first_frame_input)?;
97
98        if let ItemOrResult::Result(frame_result) = res {
99            return Ok(frame_result);
100        }
101
102        loop {
103            let call_or_result = evm.inspect_frame_run()?;
104
105            let result = match call_or_result {
106                ItemOrResult::Item(init) => {
107                    match evm.inspect_frame_init(init)? {
108                        ItemOrResult::Item(_) => {
109                            continue;
110                        }
111                        // Do not pop the frame since no new frame was created
112                        ItemOrResult::Result(result) => result,
113                    }
114                }
115                ItemOrResult::Result(result) => result,
116            };
117
118            if let Some(result) = evm.frame_return_result(result)? {
119                return Ok(result);
120            }
121        }
122    }
123}
124
125/// Handles the start of a frame by calling the appropriate inspector method.
126pub fn frame_start<CTX, INTR: InterpreterTypes>(
127    context: &mut CTX,
128    inspector: &mut impl Inspector<CTX, INTR>,
129    frame_input: &mut FrameInput,
130) -> Option<FrameResult> {
131    match frame_input {
132        FrameInput::Call(i) => {
133            if let Some(output) = inspector.call(context, i) {
134                return Some(FrameResult::Call(output));
135            }
136        }
137        FrameInput::Create(i) => {
138            if let Some(output) = inspector.create(context, i) {
139                return Some(FrameResult::Create(output));
140            }
141        }
142        FrameInput::Empty => unreachable!(),
143    }
144    None
145}
146
147/// Handles the end of a frame by calling the appropriate inspector method.
148pub fn frame_end<CTX, INTR: InterpreterTypes>(
149    context: &mut CTX,
150    inspector: &mut impl Inspector<CTX, INTR>,
151    frame_input: &FrameInput,
152    frame_output: &mut FrameResult,
153) {
154    match frame_output {
155        FrameResult::Call(outcome) => {
156            let FrameInput::Call(i) = frame_input else {
157                panic!("FrameInput::Call expected {frame_input:?}");
158            };
159            inspector.call_end(context, i, outcome);
160        }
161        FrameResult::Create(outcome) => {
162            let FrameInput::Create(i) = frame_input else {
163                panic!("FrameInput::Create expected {frame_input:?}");
164            };
165            inspector.create_end(context, i, outcome);
166        }
167    }
168}
169
170/// Run Interpreter loop with inspection support.
171///
172/// This function is used to inspect the Interpreter loop.
173/// It will call [`Inspector::step`] and [`Inspector::step_end`] after each instruction.
174/// And [`Inspector::log`],[`Inspector::selfdestruct`] for each log and selfdestruct instruction.
175pub fn inspect_instructions<CTX, IT>(
176    context: &mut CTX,
177    interpreter: &mut Interpreter<IT>,
178    mut inspector: impl Inspector<CTX, IT>,
179    instructions: &InstructionTable<IT, CTX>,
180) -> InterpreterAction
181where
182    CTX: ContextTr<Journal: JournalExt> + Host,
183    IT: InterpreterTypes,
184{
185    let mut log_num = context.journal_mut().logs().len();
186    // Main loop
187    while interpreter.bytecode.is_not_end() {
188        // Get current opcode.
189        let opcode = interpreter.bytecode.opcode();
190
191        // Call Inspector step.
192        inspector.step(interpreter, context);
193        if interpreter.bytecode.is_end() {
194            break;
195        }
196
197        // SAFETY: In analysis we are doing padding of bytecode so that we are sure that last
198        // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction
199        // it will do noop and just stop execution of this contract
200        interpreter.bytecode.relative_jump(1);
201
202        // Execute instruction.
203        let instruction_context = InstructionContext {
204            interpreter,
205            host: context,
206        };
207        instructions[opcode as usize](instruction_context);
208
209        // check if new log is added
210        let new_log = context.journal_mut().logs().len();
211        if log_num < new_log {
212            // as there is a change in log number this means new log is added
213            let log = context.journal_mut().logs().last().unwrap().clone();
214            inspector.log(interpreter, context, log);
215            log_num = new_log;
216        }
217
218        // if loops is ending, break the loop so we can revert to the previous pointer and then call step_end.
219        if interpreter.bytecode.is_end() {
220            break;
221        }
222
223        // Call step_end.
224        inspector.step_end(interpreter, context);
225    }
226
227    interpreter.bytecode.revert_to_previous_pointer();
228    // call step_end again to handle the last instruction
229    inspector.step_end(interpreter, context);
230
231    let next_action = interpreter.take_next_action();
232
233    // handle selfdestruct
234    if let InterpreterAction::Return(result) = &next_action {
235        if result.result == InstructionResult::SelfDestruct {
236            match context.journal_mut().journal().last() {
237                Some(JournalEntry::AccountDestroyed {
238                    address,
239                    target,
240                    had_balance,
241                    ..
242                }) => {
243                    inspector.selfdestruct(*address, *target, *had_balance);
244                }
245                Some(JournalEntry::BalanceTransfer {
246                    from, to, balance, ..
247                }) => {
248                    inspector.selfdestruct(*from, *to, *balance);
249                }
250                _ => {}
251            }
252        }
253    }
254
255    next_action
256}