Skip to main content

revm_interpreter/
interpreter.rs

1//! Core interpreter implementation and components.
2
3/// Extended bytecode functionality.
4pub mod ext_bytecode;
5mod input;
6mod return_data;
7mod runtime_flags;
8mod shared_memory;
9mod stack;
10
11// re-exports
12pub 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
19// imports
20use 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/// Main interpreter structure that contains all components defined in [`InterpreterTypes`].
29#[derive(Debug, Clone)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31pub struct Interpreter<WIRE: InterpreterTypes = EthInterpreter> {
32    /// Bytecode being executed.
33    pub bytecode: WIRE::Bytecode,
34    /// Gas tracking for execution costs.
35    pub gas: Gas,
36    /// EVM stack for computation.
37    pub stack: WIRE::Stack,
38    /// Buffer for return data from calls.
39    pub return_data: WIRE::ReturnData,
40    /// EVM memory for data storage.
41    pub memory: WIRE::Memory,
42    /// Input data for current execution context.
43    pub input: WIRE::Input,
44    /// Runtime flags controlling execution behavior.
45    pub runtime_flag: WIRE::RuntimeFlag,
46    /// Extended functionality and customizations.
47    pub extend: WIRE::Extend,
48}
49
50impl<EXT: Default> Interpreter<EthInterpreter<EXT>> {
51    /// Create new interpreter
52    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    /// Create a new interpreter with default extended functionality.
72    pub fn default_ext() -> Self {
73        Self::do_default(Stack::new(), SharedMemory::new())
74    }
75
76    /// Create a new invalid interpreter.
77    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    /// Clears and reinitializes the interpreter with new parameters.
115    #[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    /// Sets the bytecode that is going to be executed
152    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/// Default types for Ethereum interpreter.
165#[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    /// Performs EVM memory resize.
183    #[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    /// Takes the next action from the control and returns it.
194    #[inline]
195    pub fn take_next_action(&mut self) -> InterpreterAction {
196        self.bytecode.reset_action();
197        // Return next action if it is some.
198        let action = core::mem::take(self.bytecode.action()).expect("Interpreter to set action");
199        action
200    }
201
202    /// Halt the interpreter with the given result.
203    ///
204    /// This will set the action to [`InterpreterAction::Return`] and set the gas to the current gas.
205    #[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    /// Halt the interpreter with the given result.
216    ///
217    /// This will set the action to [`InterpreterAction::Return`] and set the gas to the current gas.
218    #[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    /// Halt the interpreter due to a [`LoadError`].
228    #[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    /// Halt the interpreter with an out-of-gas error.
238    #[cold]
239    #[inline(never)]
240    pub fn halt_oog(&mut self) {
241        self.gas.spend_all();
242        self.halt(InstructionResult::OutOfGas);
243    }
244
245    /// Halt the interpreter with an out-of-gas error.
246    #[cold]
247    #[inline(never)]
248    pub fn halt_memory_oog(&mut self) {
249        self.halt(InstructionResult::MemoryOOG);
250    }
251
252    /// Halt the interpreter with an out-of-gas error.
253    #[cold]
254    #[inline(never)]
255    pub fn halt_memory_limit_oog(&mut self) {
256        self.halt(InstructionResult::MemoryLimitOOG);
257    }
258
259    /// Halt the interpreter with and overflow error.
260    #[cold]
261    #[inline(never)]
262    pub fn halt_overflow(&mut self) {
263        self.halt(InstructionResult::StackOverflow);
264    }
265
266    /// Halt the interpreter with and underflow error.
267    #[cold]
268    #[inline(never)]
269    pub fn halt_underflow(&mut self) {
270        self.halt(InstructionResult::StackUnderflow);
271    }
272
273    /// Halt the interpreter with and not activated error.
274    #[cold]
275    #[inline(never)]
276    pub fn halt_not_activated(&mut self) {
277        self.halt(InstructionResult::NotActivated);
278    }
279
280    /// Return with the given output.
281    ///
282    /// This will set the action to [`InterpreterAction::Return`] and set the gas to the current gas.
283    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    /// Executes the instruction at the current instruction pointer.
292    ///
293    /// Internally it will increment instruction pointer by one.
294    #[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        // Get current opcode.
302        let opcode = self.bytecode.opcode();
303
304        // SAFETY: In analysis we are doing padding of bytecode so that we are sure that last
305        // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction
306        // it will do noop and just stop execution of this contract
307        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    /// Executes the interpreter until it returns or stops.
324    #[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/*
346#[doc(hidden)]
347#[unsafe(no_mangle)]
348pub fn asm_run(
349    interpreter: &mut Interpreter<EthInterpreter>,
350    host: &mut context_interface::DummyHost,
351) {
352    let table = crate::instruction_table();
353    let gas_table = crate::gas_table();
354    interpreter.run_plain(&table, &gas_table, host);
355}
356*/
357
358/// The result of an interpreter operation.
359#[derive(Clone, Debug, PartialEq, Eq)]
360#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
361pub struct InterpreterResult {
362    /// The result of the instruction execution.
363    pub result: InstructionResult,
364    /// The output of the instruction execution.
365    pub output: Bytes,
366    /// The gas usage information.
367    pub gas: Gas,
368}
369
370impl InterpreterResult {
371    /// Returns a new `InterpreterResult` with the given values.
372    pub const fn new(result: InstructionResult, output: Bytes, gas: Gas) -> Self {
373        Self {
374            result,
375            output,
376            gas,
377        }
378    }
379
380    /// Returns a new `InterpreterResult` for an out-of-gas error with the given gas limit.
381    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    /// Returns whether the instruction result is a success.
390    #[inline]
391    pub const fn is_ok(&self) -> bool {
392        self.result.is_ok()
393    }
394
395    /// Returns whether the instruction result is a revert.
396    #[inline]
397    pub const fn is_revert(&self) -> bool {
398        self.result.is_revert()
399    }
400
401    /// Returns whether the instruction result is an error.
402    #[deprecated(note = "use `is_halt` instead")]
403    #[inline]
404    pub const fn is_error(&self) -> bool {
405        self.result.is_halt()
406    }
407
408    /// Returns whether the instruction result is a halt (error).
409    #[inline]
410    pub const fn is_halt(&self) -> bool {
411        self.result.is_halt()
412    }
413}
414
415// Special implementation for types where Output can be created from InterpreterAction
416impl<IW: InterpreterTypes> Interpreter<IW>
417where
418    IW::Output: From<InterpreterAction>,
419{
420    /// Takes the next action from the control and returns it as the specific Output type.
421    #[inline]
422    pub fn take_next_action_as_output(&mut self) -> IW::Output {
423        From::from(self.take_next_action())
424    }
425
426    /// Executes the interpreter until it returns or stops, returning the specific Output type.
427    #[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, // PUSH1 0x00
481            0x61, 0x27, 0x10, // PUSH2 0x2710  (10,000)
482            0x52, // MSTORE
483            0x00, // STOP
484        ][..],
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, // PUSH1 0x00
523            0x61, 0x27, 0x10, // PUSH2 0x2710  (10,000)
524            0x52, // MSTORE
525            0x00, // STOP
526        ][..],
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}