Skip to main content

revm_inspector/
handler.rs

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