revm_interpreter/
interpreter.rs

1pub mod analysis;
2mod contract;
3#[cfg(feature = "serde")]
4pub mod serde;
5mod shared_memory;
6mod stack;
7
8pub use contract::Contract;
9pub use shared_memory::{num_words, SharedMemory, EMPTY_SHARED_MEMORY};
10pub use stack::{Stack, STACK_LIMIT};
11
12use crate::{
13    gas, primitives::Bytes, push, push_b256, return_ok, return_revert, CallOutcome, CreateOutcome,
14    FunctionStack, Gas, Host, InstructionResult, InterpreterAction,
15};
16use core::cmp::min;
17use revm_primitives::{Bytecode, Eof, U256};
18use std::borrow::ToOwned;
19use std::sync::Arc;
20
21/// EVM bytecode interpreter.
22#[derive(Debug)]
23pub struct Interpreter {
24    /// The current instruction pointer.
25    pub instruction_pointer: *const u8,
26    /// The gas state.
27    pub gas: Gas,
28    /// Contract information and invoking data
29    pub contract: Contract,
30    /// The execution control flag. If this is not set to `Continue`, the interpreter will stop
31    /// execution.
32    pub instruction_result: InstructionResult,
33    /// Currently run Bytecode that instruction result will point to.
34    /// Bytecode is owned by the contract.
35    pub bytecode: Bytes,
36    /// Whether we are Interpreting the Ethereum Object Format (EOF) bytecode.
37    /// This is local field that is set from `contract.is_eof()`.
38    pub is_eof: bool,
39    /// Is init flag for eof create
40    pub is_eof_init: bool,
41    /// Shared memory.
42    ///
43    /// Note: This field is only set while running the interpreter loop.
44    /// Otherwise it is taken and replaced with empty shared memory.
45    pub shared_memory: SharedMemory,
46    /// Stack.
47    pub stack: Stack,
48    /// EOF function stack.
49    pub function_stack: FunctionStack,
50    /// The return data buffer for internal calls.
51    /// It has multi usage:
52    ///
53    /// * It contains the output bytes of call sub call.
54    /// * When this interpreter finishes execution it contains the output bytes of this contract.
55    pub return_data_buffer: Bytes,
56    /// Whether the interpreter is in "staticcall" mode, meaning no state changes can happen.
57    pub is_static: bool,
58    /// Actions that the EVM should do.
59    ///
60    /// Set inside CALL or CREATE instructions and RETURN or REVERT instructions. Additionally those instructions will set
61    /// InstructionResult to CallOrCreate/Return/Revert so we know the reason.
62    pub next_action: InterpreterAction,
63}
64
65impl Default for Interpreter {
66    fn default() -> Self {
67        Self::new(Contract::default(), u64::MAX, false)
68    }
69}
70
71impl Interpreter {
72    /// Create new interpreter
73    pub fn new(contract: Contract, gas_limit: u64, is_static: bool) -> Self {
74        if !contract.bytecode.is_execution_ready() {
75            panic!("Contract is not execution ready {:?}", contract.bytecode);
76        }
77        let is_eof = contract.bytecode.is_eof();
78        let bytecode = contract.bytecode.bytecode().clone();
79        Self {
80            instruction_pointer: bytecode.as_ptr(),
81            bytecode,
82            contract,
83            gas: Gas::new(gas_limit),
84            instruction_result: InstructionResult::Continue,
85            function_stack: FunctionStack::default(),
86            is_static,
87            is_eof,
88            is_eof_init: false,
89            return_data_buffer: Bytes::new(),
90            shared_memory: EMPTY_SHARED_MEMORY,
91            stack: Stack::new(),
92            next_action: InterpreterAction::None,
93        }
94    }
95
96    /// Set is_eof_init to true, this is used to enable `RETURNCONTRACT` opcode.
97    #[inline]
98    pub fn set_is_eof_init(&mut self) {
99        self.is_eof_init = true;
100    }
101
102    #[inline]
103    pub fn eof(&self) -> Option<&Arc<Eof>> {
104        self.contract.bytecode.eof()
105    }
106
107    /// Test related helper
108    #[cfg(test)]
109    pub fn new_bytecode(bytecode: Bytecode) -> Self {
110        Self::new(
111            Contract::new(
112                Bytes::new(),
113                bytecode,
114                None,
115                crate::primitives::Address::default(),
116                None,
117                crate::primitives::Address::default(),
118                U256::ZERO,
119            ),
120            0,
121            false,
122        )
123    }
124
125    /// Load EOF code into interpreter. PC is assumed to be correctly set
126    pub(crate) fn load_eof_code(&mut self, idx: usize, pc: usize) {
127        // SAFETY: eof flag is true only if bytecode is Eof.
128        let Bytecode::Eof(eof) = &self.contract.bytecode else {
129            panic!("Expected EOF code section")
130        };
131        let Some(code) = eof.body.code(idx) else {
132            panic!("Code not found")
133        };
134        self.bytecode = code.clone();
135        self.instruction_pointer = unsafe { self.bytecode.as_ptr().add(pc) };
136    }
137
138    /// Inserts the output of a `create` call into the interpreter.
139    ///
140    /// This function is used after a `create` call has been executed. It processes the outcome
141    /// of that call and updates the state of the interpreter accordingly.
142    ///
143    /// # Arguments
144    ///
145    /// * `create_outcome` - A `CreateOutcome` struct containing the results of the `create` call.
146    ///
147    /// # Behavior
148    ///
149    /// The function updates the `return_data_buffer` with the data from `create_outcome`.
150    /// Depending on the `InstructionResult` indicated by `create_outcome`, it performs one of the following:
151    ///
152    /// - `Ok`: Pushes the address from `create_outcome` to the stack, updates gas costs, and records any gas refunds.
153    /// - `Revert`: Pushes `U256::ZERO` to the stack and updates gas costs.
154    /// - `FatalExternalError`: Sets the `instruction_result` to `InstructionResult::FatalExternalError`.
155    /// - `Default`: Pushes `U256::ZERO` to the stack.
156    ///
157    /// # Side Effects
158    ///
159    /// - Updates `return_data_buffer` with the data from `create_outcome`.
160    /// - Modifies the stack by pushing values depending on the `InstructionResult`.
161    /// - Updates gas costs and records refunds in the interpreter's `gas` field.
162    /// - May alter `instruction_result` in case of external errors.
163    pub fn insert_create_outcome(&mut self, create_outcome: CreateOutcome) {
164        self.instruction_result = InstructionResult::Continue;
165
166        let instruction_result = create_outcome.instruction_result();
167        self.return_data_buffer = if instruction_result.is_revert() {
168            // Save data to return data buffer if the create reverted
169            create_outcome.output().to_owned()
170        } else {
171            // Otherwise clear it
172            Bytes::new()
173        };
174
175        match instruction_result {
176            return_ok!() => {
177                let address = create_outcome.address;
178                push_b256!(self, address.unwrap_or_default().into_word());
179                self.gas.erase_cost(create_outcome.gas().remaining());
180                self.gas.record_refund(create_outcome.gas().refunded());
181            }
182            return_revert!() => {
183                push!(self, U256::ZERO);
184                self.gas.erase_cost(create_outcome.gas().remaining());
185            }
186            InstructionResult::FatalExternalError => {
187                panic!("Fatal external error in insert_create_outcome");
188            }
189            _ => {
190                push!(self, U256::ZERO);
191            }
192        }
193    }
194
195    pub fn insert_eofcreate_outcome(&mut self, create_outcome: CreateOutcome) {
196        self.instruction_result = InstructionResult::Continue;
197        let instruction_result = create_outcome.instruction_result();
198
199        self.return_data_buffer = if *instruction_result == InstructionResult::Revert {
200            // Save data to return data buffer if the create reverted
201            create_outcome.output().to_owned()
202        } else {
203            // Otherwise clear it. Note that RETURN opcode should abort.
204            Bytes::new()
205        };
206
207        match instruction_result {
208            InstructionResult::ReturnContract => {
209                push_b256!(
210                    self,
211                    create_outcome.address.expect("EOF Address").into_word()
212                );
213                self.gas.erase_cost(create_outcome.gas().remaining());
214                self.gas.record_refund(create_outcome.gas().refunded());
215            }
216            return_revert!() => {
217                push!(self, U256::ZERO);
218                self.gas.erase_cost(create_outcome.gas().remaining());
219            }
220            InstructionResult::FatalExternalError => {
221                panic!("Fatal external error in insert_eofcreate_outcome");
222            }
223            _ => {
224                push!(self, U256::ZERO);
225            }
226        }
227    }
228
229    /// Inserts the outcome of a call into the virtual machine's state.
230    ///
231    /// This function takes the result of a call, represented by `CallOutcome`,
232    /// and updates the virtual machine's state accordingly. It involves updating
233    /// the return data buffer, handling gas accounting, and setting the memory
234    /// in shared storage based on the outcome of the call.
235    ///
236    /// # Arguments
237    ///
238    /// * `shared_memory` - A mutable reference to the shared memory used by the virtual machine.
239    /// * `call_outcome` - The outcome of the call to be processed, containing details such as
240    ///   instruction result, gas information, and output data.
241    ///
242    /// # Behavior
243    ///
244    /// The function first copies the output data from the call outcome to the virtual machine's
245    /// return data buffer. It then checks the instruction result from the call outcome:
246    ///
247    /// - `return_ok!()`: Processes successful execution, refunds gas, and updates shared memory.
248    /// - `return_revert!()`: Handles a revert by only updating the gas usage and shared memory.
249    /// - `InstructionResult::FatalExternalError`: Sets the instruction result to a fatal external error.
250    /// - Any other result: No specific action is taken.
251    pub fn insert_call_outcome(
252        &mut self,
253        shared_memory: &mut SharedMemory,
254        call_outcome: CallOutcome,
255    ) {
256        self.instruction_result = InstructionResult::Continue;
257
258        let out_offset = call_outcome.memory_start();
259        let out_len = call_outcome.memory_length();
260        let out_ins_result = *call_outcome.instruction_result();
261        let out_gas = call_outcome.gas();
262        self.return_data_buffer = call_outcome.result.output;
263
264        let target_len = min(out_len, self.return_data_buffer.len());
265        match out_ins_result {
266            return_ok!() => {
267                // return unspend gas.
268                self.gas.erase_cost(out_gas.remaining());
269                self.gas.record_refund(out_gas.refunded());
270                shared_memory.set(out_offset, &self.return_data_buffer[..target_len]);
271                push!(
272                    self,
273                    if self.is_eof {
274                        U256::ZERO
275                    } else {
276                        U256::from(1)
277                    }
278                );
279            }
280            return_revert!() => {
281                self.gas.erase_cost(out_gas.remaining());
282                shared_memory.set(out_offset, &self.return_data_buffer[..target_len]);
283                push!(
284                    self,
285                    if self.is_eof {
286                        U256::from(1)
287                    } else {
288                        U256::ZERO
289                    }
290                );
291            }
292            InstructionResult::FatalExternalError => {
293                panic!("Fatal external error in insert_call_outcome");
294            }
295            _ => {
296                push!(
297                    self,
298                    if self.is_eof {
299                        U256::from(2)
300                    } else {
301                        U256::ZERO
302                    }
303                );
304            }
305        }
306    }
307
308    /// Returns the opcode at the current instruction pointer.
309    #[inline]
310    pub fn current_opcode(&self) -> u8 {
311        unsafe { *self.instruction_pointer }
312    }
313
314    /// Returns a reference to the contract.
315    #[inline]
316    pub fn contract(&self) -> &Contract {
317        &self.contract
318    }
319
320    /// Returns a reference to the interpreter's gas state.
321    #[inline]
322    pub fn gas(&self) -> &Gas {
323        &self.gas
324    }
325
326    /// Returns a reference to the interpreter's stack.
327    #[inline]
328    pub fn stack(&self) -> &Stack {
329        &self.stack
330    }
331
332    /// Returns a mutable reference to the interpreter's stack.
333    #[inline]
334    pub fn stack_mut(&mut self) -> &mut Stack {
335        &mut self.stack
336    }
337
338    /// Returns the current program counter.
339    #[inline]
340    pub fn program_counter(&self) -> usize {
341        // SAFETY: `instruction_pointer` should be at an offset from the start of the bytecode.
342        // In practice this is always true unless a caller modifies the `instruction_pointer` field manually.
343        unsafe { self.instruction_pointer.offset_from(self.bytecode.as_ptr()) as usize }
344    }
345
346    /// Executes the instruction at the current instruction pointer.
347    ///
348    /// Internally it will increment instruction pointer by one.
349    #[inline]
350    pub(crate) fn step<FN, H: Host + ?Sized>(&mut self, instruction_table: &[FN; 256], host: &mut H)
351    where
352        FN: Fn(&mut Interpreter, &mut H),
353    {
354        // Get current opcode.
355        let opcode = unsafe { *self.instruction_pointer };
356
357        // SAFETY: In analysis we are doing padding of bytecode so that we are sure that last
358        // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction
359        // it will do noop and just stop execution of this contract
360        self.instruction_pointer = unsafe { self.instruction_pointer.offset(1) };
361
362        // execute instruction.
363        (instruction_table[opcode as usize])(self, host)
364    }
365
366    /// Take memory and replace it with empty memory.
367    pub fn take_memory(&mut self) -> SharedMemory {
368        core::mem::replace(&mut self.shared_memory, EMPTY_SHARED_MEMORY)
369    }
370
371    /// Executes the interpreter until it returns or stops.
372    pub fn run<FN, H: Host + ?Sized>(
373        &mut self,
374        shared_memory: SharedMemory,
375        instruction_table: &[FN; 256],
376        host: &mut H,
377    ) -> InterpreterAction
378    where
379        FN: Fn(&mut Interpreter, &mut H),
380    {
381        self.next_action = InterpreterAction::None;
382        self.shared_memory = shared_memory;
383        // main loop
384        while self.instruction_result == InstructionResult::Continue {
385            self.step(instruction_table, host);
386        }
387
388        // Return next action if it is some.
389        if self.next_action.is_some() {
390            return core::mem::take(&mut self.next_action);
391        }
392        // If not, return action without output as it is a halt.
393        InterpreterAction::Return {
394            result: InterpreterResult {
395                result: self.instruction_result,
396                // return empty bytecode
397                output: Bytes::new(),
398                gas: self.gas,
399            },
400        }
401    }
402
403    /// Resize the memory to the new size. Returns whether the gas was enough to resize the memory.
404    #[inline]
405    #[must_use]
406    pub fn resize_memory(&mut self, new_size: usize) -> bool {
407        resize_memory(&mut self.shared_memory, &mut self.gas, new_size)
408    }
409}
410
411/// The result of an interpreter operation.
412#[derive(Clone, Debug, PartialEq, Eq)]
413#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
414pub struct InterpreterResult {
415    /// The result of the instruction execution.
416    pub result: InstructionResult,
417    /// The output of the instruction execution.
418    pub output: Bytes,
419    /// The gas usage information.
420    pub gas: Gas,
421}
422
423impl InterpreterResult {
424    /// Returns a new `InterpreterResult` with the given values.
425    pub fn new(result: InstructionResult, output: Bytes, gas: Gas) -> Self {
426        Self {
427            result,
428            output,
429            gas,
430        }
431    }
432
433    /// Returns whether the instruction result is a success.
434    #[inline]
435    pub const fn is_ok(&self) -> bool {
436        self.result.is_ok()
437    }
438
439    /// Returns whether the instruction result is a revert.
440    #[inline]
441    pub const fn is_revert(&self) -> bool {
442        self.result.is_revert()
443    }
444
445    /// Returns whether the instruction result is an error.
446    #[inline]
447    pub const fn is_error(&self) -> bool {
448        self.result.is_error()
449    }
450}
451
452/// Resize the memory to the new size. Returns whether the gas was enough to resize the memory.
453#[inline(never)]
454#[cold]
455#[must_use]
456pub fn resize_memory(memory: &mut SharedMemory, gas: &mut Gas, new_size: usize) -> bool {
457    let new_words = num_words(new_size as u64);
458    let new_cost = gas::memory_gas(new_words);
459    let current_cost = memory.current_expansion_cost();
460    let cost = new_cost - current_cost;
461    let success = gas.record_cost(cost);
462    if success {
463        memory.resize((new_words as usize) * 32);
464    }
465    success
466}
467
468#[cfg(test)]
469mod tests {
470    use super::*;
471    use crate::{opcode::InstructionTable, DummyHost};
472    use revm_primitives::CancunSpec;
473
474    #[test]
475    fn object_safety() {
476        let mut interp = Interpreter::new(Contract::default(), u64::MAX, false);
477
478        let mut host = crate::DummyHost::default();
479        let table: &InstructionTable<DummyHost> =
480            &crate::opcode::make_instruction_table::<DummyHost, CancunSpec>();
481        let _ = interp.run(EMPTY_SHARED_MEMORY, table, &mut host);
482
483        let host: &mut dyn Host = &mut host as &mut dyn Host;
484        let table: &InstructionTable<dyn Host> =
485            &crate::opcode::make_instruction_table::<dyn Host, CancunSpec>();
486        let _ = interp.run(EMPTY_SHARED_MEMORY, table, host);
487    }
488}