revm_inspector/
test_inspector.rs

1//! Test inspector for testing EVM execution.
2
3extern crate alloc;
4
5use crate::Inspector;
6use alloc::{format, string::String, vec::Vec};
7use interpreter::{
8    interpreter_types::{Jumps, MemoryTr, StackTr},
9    CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterTypes,
10};
11use primitives::{Address, Log, U256};
12
13/// Interpreter state at a specific point in execution.
14#[derive(Debug, Clone)]
15pub struct InterpreterState {
16    /// Program counter.
17    pub pc: usize,
18    /// Stack length.
19    pub stack_len: usize,
20    /// Memory size.
21    pub memory_size: usize,
22}
23
24/// Step execution record.
25#[derive(Debug, Clone)]
26pub struct StepRecord {
27    /// State before instruction execution.
28    pub before: InterpreterState,
29    /// State after instruction execution.
30    pub after: Option<InterpreterState>,
31    /// Opcode name.
32    pub opcode_name: String,
33}
34
35/// Events captured during EVM execution.
36#[derive(Debug, Clone)]
37pub enum InspectorEvent {
38    /// Execution step.
39    Step(StepRecord),
40    /// Call operation.
41    Call {
42        /// Call inputs.
43        inputs: CallInputs,
44        /// Call outcome.
45        outcome: Option<CallOutcome>,
46    },
47    /// Create operation.
48    Create {
49        /// Create inputs.
50        inputs: CreateInputs,
51        /// Create outcome.
52        outcome: Option<CreateOutcome>,
53    },
54    /// Log emission.
55    Log(Log),
56    /// Selfdestruct operation.
57    Selfdestruct {
58        /// Contract address.
59        address: Address,
60        /// Beneficiary address.
61        beneficiary: Address,
62        /// Value transferred.
63        value: U256,
64    },
65}
66
67/// Test inspector that records execution events.
68#[derive(Debug, Default)]
69pub struct TestInspector {
70    /// Captured events.
71    pub events: Vec<InspectorEvent>,
72    /// Total step count.
73    pub step_count: usize,
74    /// Current call depth.
75    pub call_depth: usize,
76}
77
78impl TestInspector {
79    /// Create a new TestInspector.
80    pub fn new() -> Self {
81        Self::default()
82    }
83
84    fn capture_interpreter_state<INTR: InterpreterTypes>(
85        interp: &Interpreter<INTR>,
86    ) -> InterpreterState
87    where
88        INTR::Bytecode: Jumps,
89        INTR::Stack: StackTr,
90        INTR::Memory: MemoryTr,
91    {
92        InterpreterState {
93            pc: interp.bytecode.pc(),
94            stack_len: interp.stack.len(),
95            memory_size: interp.memory.size(),
96        }
97    }
98
99    /// Get all captured events.
100    pub fn get_events(&self) -> Vec<InspectorEvent> {
101        self.events.clone()
102    }
103
104    /// Get the total step count.
105    pub fn get_step_count(&self) -> usize {
106        self.step_count
107    }
108}
109
110impl<CTX, INTR> Inspector<CTX, INTR> for TestInspector
111where
112    INTR: InterpreterTypes,
113    INTR::Bytecode: Jumps,
114    INTR::Stack: StackTr,
115    INTR::Memory: MemoryTr,
116{
117    fn step(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
118        self.step_count += 1;
119
120        let state = Self::capture_interpreter_state(interp);
121        let opcode = interp.bytecode.opcode();
122        let opcode_name = if let Some(op) = state::bytecode::opcode::OpCode::new(opcode) {
123            format!("{op}")
124        } else {
125            format!("Unknown(0x{opcode:02x})")
126        };
127
128        self.events.push(InspectorEvent::Step(StepRecord {
129            before: state,
130            after: None,
131            opcode_name,
132        }));
133    }
134
135    fn step_end(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
136        let state = Self::capture_interpreter_state(interp);
137
138        if let Some(InspectorEvent::Step(record)) = self.events.last_mut() {
139            record.after = Some(state);
140        }
141    }
142
143    fn log(&mut self, _ctx: &mut CTX, log: Log) {
144        self.events.push(InspectorEvent::Log(log));
145    }
146
147    fn call(&mut self, _ctx: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
148        self.call_depth += 1;
149        self.events.push(InspectorEvent::Call {
150            inputs: inputs.clone(),
151            outcome: None,
152        });
153        None
154    }
155
156    fn call_end(&mut self, _ctx: &mut CTX, _inputs: &CallInputs, outcome: &mut CallOutcome) {
157        self.call_depth -= 1;
158        if let Some(InspectorEvent::Call {
159            outcome: ref mut out,
160            ..
161        }) = self
162            .events
163            .iter_mut()
164            .rev()
165            .find(|e| matches!(e, InspectorEvent::Call { outcome: None, .. }))
166        {
167            *out = Some(outcome.clone());
168        }
169    }
170
171    fn create(&mut self, _ctx: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
172        self.events.push(InspectorEvent::Create {
173            inputs: inputs.clone(),
174            outcome: None,
175        });
176        None
177    }
178
179    fn create_end(&mut self, _ctx: &mut CTX, _inputs: &CreateInputs, outcome: &mut CreateOutcome) {
180        if let Some(InspectorEvent::Create {
181            outcome: ref mut out,
182            ..
183        }) = self
184            .events
185            .iter_mut()
186            .rev()
187            .find(|e| matches!(e, InspectorEvent::Create { outcome: None, .. }))
188        {
189            *out = Some(outcome.clone());
190        }
191    }
192
193    fn selfdestruct(&mut self, contract: Address, beneficiary: Address, value: U256) {
194        self.events.push(InspectorEvent::Selfdestruct {
195            address: contract,
196            beneficiary,
197            value,
198        });
199    }
200}
201
202/// Default tests for EVM implementations.
203#[cfg(feature = "std")]
204pub mod default_tests {
205    use super::*;
206    use alloc::{string::ToString, vec, vec::Vec};
207    use primitives::Bytes;
208    use state::bytecode::opcode;
209
210    /// Run default test suite on an EVM implementation.
211    /// The execute function should set up the EVM, run the bytecode, and return the TestInspector.
212    pub fn run_tests<F>(mut execute: F) -> Result<(), Vec<(&'static str, String)>>
213    where
214        F: FnMut(Bytes) -> Result<TestInspector, String>,
215    {
216        let mut failures = Vec::new();
217
218        // Test basic stack operations: PUSH, ADD, MSTORE
219        let stack_test = Bytes::from(vec![
220            opcode::PUSH1,
221            0x42,
222            opcode::PUSH1,
223            0x10,
224            opcode::ADD,
225            opcode::PUSH1,
226            0x00,
227            opcode::MSTORE,
228            opcode::STOP,
229        ]);
230
231        match execute(stack_test) {
232            Ok(inspector) => {
233                if inspector.step_count < 5 {
234                    failures.push(("stack_operations", "Not enough steps recorded".to_string()));
235                }
236            }
237            Err(e) => failures.push(("stack_operations", e)),
238        }
239
240        // Test JUMP control flow
241        let jump_test = Bytes::from(vec![
242            opcode::PUSH1,
243            0x05,
244            opcode::JUMP,
245            opcode::INVALID,
246            opcode::INVALID,
247            opcode::JUMPDEST,
248            opcode::STOP,
249        ]);
250
251        match execute(jump_test) {
252            Ok(inspector) => {
253                let has_jump = inspector
254                    .events
255                    .iter()
256                    .any(|e| matches!(e, InspectorEvent::Step(s) if s.opcode_name == "JUMP"));
257                if !has_jump {
258                    failures.push(("jump", "JUMP not recorded".to_string()));
259                }
260            }
261            Err(e) => failures.push(("jump", e)),
262        }
263
264        // Test LOG0
265        let log_test = Bytes::from(vec![
266            opcode::PUSH1,
267            0x20,
268            opcode::PUSH1,
269            0x00,
270            opcode::LOG0,
271            opcode::STOP,
272        ]);
273
274        match execute(log_test) {
275            Ok(inspector) => {
276                let has_log = inspector
277                    .events
278                    .iter()
279                    .any(|e| matches!(e, InspectorEvent::Log(_)));
280                if !has_log {
281                    failures.push(("log", "LOG0 not recorded".to_string()));
282                }
283            }
284            Err(e) => failures.push(("log", e)),
285        }
286
287        if failures.is_empty() {
288            Ok(())
289        } else {
290            Err(failures)
291        }
292    }
293}