1extern 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#[derive(Debug, Clone)]
15pub struct InterpreterState {
16 pub pc: usize,
18 pub stack_len: usize,
20 pub memory_size: usize,
22}
23
24#[derive(Debug, Clone)]
26pub struct StepRecord {
27 pub before: InterpreterState,
29 pub after: Option<InterpreterState>,
31 pub opcode_name: String,
33}
34
35#[derive(Debug, Clone)]
37pub enum InspectorEvent {
38 Step(StepRecord),
40 Call {
42 inputs: CallInputs,
44 outcome: Option<CallOutcome>,
46 },
47 Create {
49 inputs: CreateInputs,
51 outcome: Option<CreateOutcome>,
53 },
54 Log(Log),
56 Selfdestruct {
58 address: Address,
60 beneficiary: Address,
62 value: U256,
64 },
65}
66
67#[derive(Debug, Default)]
69pub struct TestInspector {
70 pub events: Vec<InspectorEvent>,
72 pub step_count: usize,
74 pub call_depth: usize,
76}
77
78impl TestInspector {
79 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 pub fn get_events(&self) -> Vec<InspectorEvent> {
101 self.events.clone()
102 }
103
104 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#[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 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 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 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 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}