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