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        // Call step_end.
219        inspector.step_end(interpreter, context);
220    }
221
222    interpreter.bytecode.revert_to_previous_pointer();
223
224    let next_action = interpreter.take_next_action();
225
226    // handle selfdestruct
227    if let InterpreterAction::Return(result) = &next_action {
228        if result.result == InstructionResult::SelfDestruct {
229            match context.journal_mut().journal().last() {
230                Some(JournalEntry::AccountDestroyed {
231                    address,
232                    target,
233                    had_balance,
234                    ..
235                }) => {
236                    inspector.selfdestruct(*address, *target, *had_balance);
237                }
238                Some(JournalEntry::BalanceTransfer {
239                    from, to, balance, ..
240                }) => {
241                    inspector.selfdestruct(*from, *to, *balance);
242                }
243                _ => {}
244            }
245        }
246    }
247
248    next_action
249}