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>,
163    frame_input: &mut FrameInput,
164) -> Option<FrameResult> {
165    match frame_input {
166        FrameInput::Call(i) => {
167            if let Some(output) = inspector.call(context, i) {
168                return Some(FrameResult::Call(output));
169            }
170        }
171        FrameInput::Create(i) => {
172            if let Some(output) = inspector.create(context, i) {
173                return Some(FrameResult::Create(output));
174            }
175        }
176        FrameInput::Empty => unreachable!(),
177    }
178    None
179}
180
181/// Handles the end of a frame by calling the appropriate inspector method.
182pub fn frame_end<CTX, INTR: InterpreterTypes>(
183    context: &mut CTX,
184    inspector: &mut impl Inspector<CTX, INTR>,
185    frame_input: &FrameInput,
186    frame_output: &mut FrameResult,
187) {
188    match frame_output {
189        FrameResult::Call(outcome) => {
190            let FrameInput::Call(i) = frame_input else {
191                panic!("FrameInput::Call expected {frame_input:?}");
192            };
193            inspector.call_end(context, i, outcome);
194        }
195        FrameResult::Create(outcome) => {
196            let FrameInput::Create(i) = frame_input else {
197                panic!("FrameInput::Create expected {frame_input:?}");
198            };
199            inspector.create_end(context, i, outcome);
200        }
201    }
202}
203
204/// Run Interpreter loop with inspection support.
205///
206/// This function is used to inspect the Interpreter loop.
207/// It will call [`Inspector::step`] and [`Inspector::step_end`] after each instruction.
208/// And [`Inspector::log`],[`Inspector::selfdestruct`] for each log and selfdestruct instruction.
209pub fn inspect_instructions<CTX, IT>(
210    context: &mut CTX,
211    interpreter: &mut Interpreter<IT>,
212    mut inspector: impl Inspector<CTX, IT>,
213    instructions: &InstructionTable<IT, CTX>,
214) -> InterpreterAction
215where
216    CTX: ContextTr<Journal: JournalExt> + Host,
217    IT: InterpreterTypes,
218{
219    loop {
220        inspector.step(interpreter, context);
221        if interpreter.bytecode.is_end() {
222            break;
223        }
224
225        let opcode = interpreter.bytecode.opcode();
226        interpreter.step(instructions, context);
227
228        if (opcode::LOG0..=opcode::LOG4).contains(&opcode) {
229            inspect_log(interpreter, context, &mut inspector);
230        }
231
232        inspector.step_end(interpreter, context);
233
234        if interpreter.bytecode.is_end() {
235            break;
236        }
237    }
238
239    let next_action = interpreter.take_next_action();
240
241    // Handle selfdestruct.
242    if let InterpreterAction::Return(result) = &next_action {
243        if result.result == InstructionResult::SelfDestruct {
244            inspect_selfdestruct(context, &mut inspector);
245        }
246    }
247
248    next_action
249}
250
251#[inline(never)]
252#[cold]
253fn inspect_log<CTX, IT>(
254    interpreter: &mut Interpreter<IT>,
255    context: &mut CTX,
256    inspector: &mut impl Inspector<CTX, IT>,
257) where
258    CTX: ContextTr<Journal: JournalExt> + Host,
259    IT: InterpreterTypes,
260{
261    // `LOG*` instruction reverted.
262    if interpreter
263        .bytecode
264        .action()
265        .as_ref()
266        .is_some_and(|x| x.is_return())
267    {
268        return;
269    }
270
271    let log = context.journal_mut().logs().last().unwrap().clone();
272    inspector.log_full(interpreter, context, log);
273}
274
275#[inline(never)]
276#[cold]
277fn inspect_selfdestruct<CTX, IT>(context: &mut CTX, inspector: &mut impl Inspector<CTX, IT>)
278where
279    CTX: ContextTr<Journal: JournalExt> + Host,
280    IT: InterpreterTypes,
281{
282    if let Some(
283        JournalEntry::AccountDestroyed {
284            address: contract,
285            target: to,
286            had_balance: balance,
287            ..
288        }
289        | JournalEntry::BalanceTransfer {
290            from: contract,
291            to,
292            balance,
293            ..
294        },
295    ) = context.journal_mut().journal().last()
296    {
297        inspector.selfdestruct(*contract, *to, *balance);
298    }
299}