1pub mod ext_bytecode;
5mod input;
6mod return_data;
7mod runtime_flags;
8mod shared_memory;
9mod stack;
10
11pub use ext_bytecode::ExtBytecode;
13pub use input::InputsImpl;
14pub use return_data::ReturnDataImpl;
15pub use runtime_flags::RuntimeFlags;
16pub use shared_memory::{num_words, resize_memory, SharedMemory};
17pub use stack::{Stack, STACK_LIMIT};
18
19use crate::{
21 instruction_context::InstructionContext, interpreter_types::*, Gas, GasTable, Host,
22 InstructionExecResult, InstructionResult, InstructionTable, InterpreterAction,
23};
24use bytecode::Bytecode;
25use context_interface::{cfg::GasParams, host::LoadError};
26use primitives::{hardfork::SpecId, hints_util::cold_path, Bytes};
27
28#[derive(Debug, Clone)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31pub struct Interpreter<WIRE: InterpreterTypes = EthInterpreter> {
32 pub bytecode: WIRE::Bytecode,
34 pub gas: Gas,
36 pub stack: WIRE::Stack,
38 pub return_data: WIRE::ReturnData,
40 pub memory: WIRE::Memory,
42 pub input: WIRE::Input,
44 pub runtime_flag: WIRE::RuntimeFlag,
46 pub extend: WIRE::Extend,
48}
49
50impl<EXT: Default> Interpreter<EthInterpreter<EXT>> {
51 pub fn new(
53 memory: SharedMemory,
54 bytecode: ExtBytecode,
55 input: InputsImpl,
56 is_static: bool,
57 spec_id: SpecId,
58 gas_limit: u64,
59 ) -> Self {
60 Self::new_inner(
61 Stack::new(),
62 memory,
63 bytecode,
64 input,
65 is_static,
66 spec_id,
67 gas_limit,
68 )
69 }
70
71 pub fn default_ext() -> Self {
73 Self::do_default(Stack::new(), SharedMemory::new())
74 }
75
76 pub fn invalid() -> Self {
78 Self::do_default(Stack::invalid(), SharedMemory::invalid())
79 }
80
81 fn do_default(stack: Stack, memory: SharedMemory) -> Self {
82 Self::new_inner(
83 stack,
84 memory,
85 ExtBytecode::default(),
86 InputsImpl::default(),
87 false,
88 SpecId::default(),
89 u64::MAX,
90 )
91 }
92
93 fn new_inner(
94 stack: Stack,
95 memory: SharedMemory,
96 bytecode: ExtBytecode,
97 input: InputsImpl,
98 is_static: bool,
99 spec_id: SpecId,
100 gas_limit: u64,
101 ) -> Self {
102 Self {
103 bytecode,
104 gas: Gas::new(gas_limit),
105 stack,
106 return_data: Default::default(),
107 memory,
108 input,
109 runtime_flag: RuntimeFlags { is_static, spec_id },
110 extend: Default::default(),
111 }
112 }
113
114 #[expect(clippy::too_many_arguments)]
116 #[inline(always)]
117 pub fn clear(
118 &mut self,
119 memory: SharedMemory,
120 bytecode: ExtBytecode,
121 input: InputsImpl,
122 is_static: bool,
123 spec_id: SpecId,
124 gas_limit: u64,
125 reservoir_remaining_gas: u64,
126 ) {
127 let Self {
128 bytecode: bytecode_ref,
129 gas,
130 stack,
131 return_data,
132 memory: memory_ref,
133 input: input_ref,
134 runtime_flag,
135 extend,
136 } = self;
137 *bytecode_ref = bytecode;
138 *gas = Gas::new_with_regular_gas_and_reservoir(gas_limit, reservoir_remaining_gas);
139 if stack.data().capacity() == 0 {
140 *stack = Stack::new();
141 } else {
142 stack.clear();
143 }
144 return_data.0.clear();
145 *memory_ref = memory;
146 *input_ref = input;
147 *runtime_flag = RuntimeFlags { spec_id, is_static };
148 *extend = EXT::default();
149 }
150
151 pub fn with_bytecode(mut self, bytecode: Bytecode) -> Self {
153 self.bytecode = ExtBytecode::new(bytecode);
154 self
155 }
156}
157
158impl Default for Interpreter<EthInterpreter> {
159 fn default() -> Self {
160 Self::default_ext()
161 }
162}
163
164#[derive(Debug)]
166pub struct EthInterpreter<EXT = (), MG = SharedMemory> {
167 _phantom: core::marker::PhantomData<fn() -> (EXT, MG)>,
168}
169
170impl<EXT> InterpreterTypes for EthInterpreter<EXT> {
171 type Stack = Stack;
172 type Memory = SharedMemory;
173 type Bytecode = ExtBytecode;
174 type ReturnData = ReturnDataImpl;
175 type Input = InputsImpl;
176 type RuntimeFlag = RuntimeFlags;
177 type Extend = EXT;
178 type Output = InterpreterAction;
179}
180
181impl<IW: InterpreterTypes> Interpreter<IW> {
182 #[inline]
184 pub fn resize_memory(
185 &mut self,
186 gas_params: &GasParams,
187 offset: usize,
188 len: usize,
189 ) -> Result<(), InstructionResult> {
190 resize_memory(&mut self.gas, &mut self.memory, gas_params, offset, len)
191 }
192
193 #[inline]
195 pub fn take_next_action(&mut self) -> InterpreterAction {
196 self.bytecode.reset_action();
197 let action = core::mem::take(self.bytecode.action()).expect("Interpreter to set action");
199 action
200 }
201
202 #[cold]
206 #[inline(never)]
207 pub fn halt(&mut self, result: InstructionResult) {
208 if result == InstructionResult::OutOfGas {
209 self.gas.spend_all();
210 }
211 self.bytecode
212 .set_action(InterpreterAction::new_halt(result, self.gas));
213 }
214
215 #[cold]
219 #[inline(never)]
220 pub fn halt_fatal(&mut self) {
221 self.bytecode.set_action(InterpreterAction::new_halt(
222 InstructionResult::FatalExternalError,
223 self.gas,
224 ));
225 }
226
227 #[cold]
229 #[inline(never)]
230 pub fn halt_load_error(&mut self, err: LoadError) {
231 match err {
232 LoadError::ColdLoadSkipped => self.halt_oog(),
233 LoadError::DBError => self.halt_fatal(),
234 }
235 }
236
237 #[cold]
239 #[inline(never)]
240 pub fn halt_oog(&mut self) {
241 self.gas.spend_all();
242 self.halt(InstructionResult::OutOfGas);
243 }
244
245 #[cold]
247 #[inline(never)]
248 pub fn halt_memory_oog(&mut self) {
249 self.halt(InstructionResult::MemoryOOG);
250 }
251
252 #[cold]
254 #[inline(never)]
255 pub fn halt_memory_limit_oog(&mut self) {
256 self.halt(InstructionResult::MemoryLimitOOG);
257 }
258
259 #[cold]
261 #[inline(never)]
262 pub fn halt_overflow(&mut self) {
263 self.halt(InstructionResult::StackOverflow);
264 }
265
266 #[cold]
268 #[inline(never)]
269 pub fn halt_underflow(&mut self) {
270 self.halt(InstructionResult::StackUnderflow);
271 }
272
273 #[cold]
275 #[inline(never)]
276 pub fn halt_not_activated(&mut self) {
277 self.halt(InstructionResult::NotActivated);
278 }
279
280 pub fn return_with_output(&mut self, output: Bytes) {
284 self.bytecode.set_action(InterpreterAction::new_return(
285 InstructionResult::Return,
286 output,
287 self.gas,
288 ));
289 }
290
291 #[inline]
295 pub fn step<H: Host + ?Sized>(
296 &mut self,
297 instruction_table: &InstructionTable<IW, H>,
298 gas_table: &GasTable,
299 host: &mut H,
300 ) -> InstructionExecResult {
301 let opcode = self.bytecode.opcode();
303
304 self.bytecode.relative_jump(1);
308
309 let instruction = instruction_table[opcode as usize];
310 let static_gas = unsafe { *gas_table.get_unchecked(opcode as usize) };
311
312 if self.gas.record_cost_unsafe(static_gas as u64) {
313 cold_path();
314 return Err(InstructionResult::OutOfGas);
315 }
316
317 instruction.execute(InstructionContext {
318 interpreter: self,
319 host,
320 })
321 }
322
323 #[inline]
325 pub fn run_plain<H: Host + ?Sized>(
326 &mut self,
327 instruction_table: &InstructionTable<IW, H>,
328 gas_table: &GasTable,
329 host: &mut H,
330 ) -> InterpreterAction {
331 let e = loop {
332 if let Err(e) = self.step(instruction_table, gas_table, host) {
333 cold_path();
334 break e;
335 }
336 };
337 if self.bytecode.action().is_none() {
338 self.halt(e);
339 }
340 debug_assert!(self.bytecode.is_end());
341 self.take_next_action()
342 }
343}
344
345#[derive(Clone, Debug, PartialEq, Eq)]
360#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
361pub struct InterpreterResult {
362 pub result: InstructionResult,
364 pub output: Bytes,
366 pub gas: Gas,
368}
369
370impl InterpreterResult {
371 pub const fn new(result: InstructionResult, output: Bytes, gas: Gas) -> Self {
373 Self {
374 result,
375 output,
376 gas,
377 }
378 }
379
380 pub fn new_oog(gas_limit: u64, reservoir: u64) -> Self {
382 Self {
383 result: InstructionResult::OutOfGas,
384 output: Bytes::default(),
385 gas: Gas::new_spent_with_reservoir(gas_limit, reservoir),
386 }
387 }
388
389 #[inline]
391 pub const fn is_ok(&self) -> bool {
392 self.result.is_ok()
393 }
394
395 #[inline]
397 pub const fn is_revert(&self) -> bool {
398 self.result.is_revert()
399 }
400
401 #[deprecated(note = "use `is_halt` instead")]
403 #[inline]
404 pub const fn is_error(&self) -> bool {
405 self.result.is_halt()
406 }
407
408 #[inline]
410 pub const fn is_halt(&self) -> bool {
411 self.result.is_halt()
412 }
413}
414
415impl<IW: InterpreterTypes> Interpreter<IW>
417where
418 IW::Output: From<InterpreterAction>,
419{
420 #[inline]
422 pub fn take_next_action_as_output(&mut self) -> IW::Output {
423 From::from(self.take_next_action())
424 }
425
426 #[inline]
428 pub fn run_plain_as_output<H: Host + ?Sized>(
429 &mut self,
430 instruction_table: &InstructionTable<IW, H>,
431 gas_table: &GasTable,
432 host: &mut H,
433 ) -> IW::Output {
434 From::from(self.run_plain(instruction_table, gas_table, host))
435 }
436}
437
438#[cfg(test)]
439mod tests {
440 #[test]
441 #[cfg(feature = "serde")]
442 fn test_interpreter_serde() {
443 use super::*;
444 use bytecode::Bytecode;
445 use primitives::Bytes;
446
447 let bytecode = Bytecode::new_raw(Bytes::from(&[0x60, 0x00, 0x60, 0x00, 0x01][..]));
448 let interpreter = Interpreter::<EthInterpreter>::new(
449 SharedMemory::new(),
450 ExtBytecode::new(bytecode),
451 InputsImpl::default(),
452 false,
453 SpecId::default(),
454 u64::MAX,
455 );
456
457 let serialized = serde_json::to_string_pretty(&interpreter).unwrap();
458 let deserialized: Interpreter<EthInterpreter> = serde_json::from_str(&serialized).unwrap();
459
460 assert_eq!(
461 interpreter.bytecode.pc(),
462 deserialized.bytecode.pc(),
463 "Program counter should be preserved"
464 );
465 }
466}
467
468#[test]
469fn test_mstore_big_offset_memory_oog() {
470 use super::*;
471 use crate::{
472 host::DummyHost,
473 instructions::{gas_table, instruction_table},
474 };
475 use bytecode::Bytecode;
476 use primitives::Bytes;
477
478 let code = Bytes::from(
479 &[
480 0x60, 0x00, 0x61, 0x27, 0x10, 0x52, 0x00, ][..],
485 );
486 let bytecode = Bytecode::new_raw(code);
487
488 let mut interpreter = Interpreter::<EthInterpreter>::new(
489 SharedMemory::new(),
490 ExtBytecode::new(bytecode),
491 InputsImpl::default(),
492 false,
493 SpecId::default(),
494 1000,
495 );
496
497 let table = instruction_table::<EthInterpreter, DummyHost>();
498 let gas = gas_table();
499 let mut host = DummyHost::default();
500 let action = interpreter.run_plain(&table, &gas, &mut host);
501
502 assert!(action.is_return());
503 assert_eq!(
504 action.instruction_result(),
505 Some(InstructionResult::MemoryOOG)
506 );
507}
508
509#[test]
510#[cfg(feature = "memory_limit")]
511fn test_mstore_big_offset_memory_limit_oog() {
512 use super::*;
513 use crate::{
514 host::DummyHost,
515 instructions::{gas_table, instruction_table},
516 };
517 use bytecode::Bytecode;
518 use primitives::Bytes;
519
520 let code = Bytes::from(
521 &[
522 0x60, 0x00, 0x61, 0x27, 0x10, 0x52, 0x00, ][..],
527 );
528 let bytecode = Bytecode::new_raw(code);
529
530 let mut interpreter = Interpreter::<EthInterpreter>::new(
531 SharedMemory::new_with_memory_limit(1000),
532 ExtBytecode::new(bytecode),
533 InputsImpl::default(),
534 false,
535 SpecId::default(),
536 100000,
537 );
538
539 let table = instruction_table::<EthInterpreter, DummyHost>();
540 let gas = gas_table();
541 let mut host = DummyHost::default();
542 let action = interpreter.run_plain(&table, &gas, &mut host);
543
544 assert!(action.is_return());
545 assert_eq!(
546 action.instruction_result(),
547 Some(InstructionResult::MemoryLimitOOG)
548 );
549}