Skip to main content

revm_handler/
frame.rs

1use crate::{
2    evm::FrameTr, item_or_result::FrameInitOrResult, precompile_provider::PrecompileProvider,
3    CallFrame, CreateFrame, FrameData, FrameResult, ItemOrResult,
4};
5use context::result::FromStringError;
6use context_interface::{
7    context::{take_error, ContextError},
8    journaled_state::{account::JournaledAccountTr, JournalCheckpoint, JournalTr},
9    local::{FrameToken, OutFrame},
10    Cfg, ContextTr, Database,
11};
12use core::cmp::min;
13use derive_where::derive_where;
14use interpreter::{
15    interpreter::{EthInterpreter, ExtBytecode},
16    interpreter_action::FrameInit,
17    interpreter_types::ReturnData,
18    CallInput, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, CreateScheme,
19    FrameInput, Gas, InputsImpl, InstructionResult, Interpreter, InterpreterAction,
20    InterpreterResult, InterpreterTypes, SharedMemory,
21};
22use primitives::{
23    constants::CALL_STACK_LIMIT,
24    hardfork::SpecId::{self, HOMESTEAD, LONDON, SPURIOUS_DRAGON},
25    keccak256, Address, Bytes, U256,
26};
27use state::Bytecode;
28use std::{borrow::ToOwned, boxed::Box, vec::Vec};
29
30/// Frame implementation for Ethereum.
31#[derive_where(Clone, Debug; IW,
32    <IW as InterpreterTypes>::Stack,
33    <IW as InterpreterTypes>::Memory,
34    <IW as InterpreterTypes>::Bytecode,
35    <IW as InterpreterTypes>::ReturnData,
36    <IW as InterpreterTypes>::Input,
37    <IW as InterpreterTypes>::RuntimeFlag,
38    <IW as InterpreterTypes>::Extend,
39)]
40pub struct EthFrame<IW: InterpreterTypes = EthInterpreter> {
41    /// Frame-specific data (Call, Create, or EOFCreate).
42    pub data: FrameData,
43    /// Input data for the frame.
44    pub input: FrameInput,
45    /// Current call depth in the execution stack.
46    pub depth: usize,
47    /// Journal checkpoint for state reversion.
48    pub checkpoint: JournalCheckpoint,
49    /// Interpreter instance for executing bytecode.
50    pub interpreter: Interpreter<IW>,
51    /// Whether the frame has been finished its execution.
52    /// Frame is considered finished if it has been called and returned a result.
53    pub is_finished: bool,
54}
55
56impl<IT: InterpreterTypes> FrameTr for EthFrame<IT> {
57    type FrameResult = FrameResult;
58    type FrameInit = FrameInit;
59}
60
61impl Default for EthFrame<EthInterpreter> {
62    fn default() -> Self {
63        Self::do_default(Interpreter::default())
64    }
65}
66
67impl EthFrame<EthInterpreter> {
68    /// Creates an new invalid [`EthFrame`].
69    pub fn invalid() -> Self {
70        Self::do_default(Interpreter::invalid())
71    }
72
73    fn do_default(interpreter: Interpreter<EthInterpreter>) -> Self {
74        Self {
75            data: FrameData::Call(CallFrame {
76                return_memory_range: 0..0,
77            }),
78            input: FrameInput::Empty,
79            depth: 0,
80            checkpoint: JournalCheckpoint::default(),
81            interpreter,
82            is_finished: false,
83        }
84    }
85
86    /// Returns true if the frame has finished execution.
87    pub fn is_finished(&self) -> bool {
88        self.is_finished
89    }
90
91    /// Sets the finished state of the frame.
92    pub fn set_finished(&mut self, finished: bool) {
93        self.is_finished = finished;
94    }
95}
96
97/// Type alias for database errors from a context.
98pub type ContextTrDbError<CTX> = <<CTX as ContextTr>::Db as Database>::Error;
99
100impl EthFrame<EthInterpreter> {
101    /// Clear and initialize a frame.
102    #[allow(clippy::too_many_arguments)]
103    #[inline(always)]
104    pub fn clear(
105        &mut self,
106        data: FrameData,
107        input: FrameInput,
108        depth: usize,
109        memory: SharedMemory,
110        bytecode: ExtBytecode,
111        inputs: InputsImpl,
112        is_static: bool,
113        spec_id: SpecId,
114        gas_limit: u64,
115        checkpoint: JournalCheckpoint,
116    ) {
117        let Self {
118            data: data_ref,
119            input: input_ref,
120            depth: depth_ref,
121            interpreter,
122            checkpoint: checkpoint_ref,
123            is_finished: is_finished_ref,
124        } = self;
125        *data_ref = data;
126        *input_ref = input;
127        *depth_ref = depth;
128        *is_finished_ref = false;
129        interpreter.clear(memory, bytecode, inputs, is_static, spec_id, gas_limit);
130        *checkpoint_ref = checkpoint;
131    }
132
133    /// Make call frame
134    #[inline]
135    pub fn make_call_frame<
136        CTX: ContextTr,
137        PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
138        ERROR: From<ContextTrDbError<CTX>> + FromStringError,
139    >(
140        mut this: OutFrame<'_, Self>,
141        ctx: &mut CTX,
142        precompiles: &mut PRECOMPILES,
143        depth: usize,
144        memory: SharedMemory,
145        inputs: Box<CallInputs>,
146    ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
147        let gas = Gas::new(inputs.gas_limit);
148        let return_result = |instruction_result: InstructionResult| {
149            Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
150                result: InterpreterResult {
151                    result: instruction_result,
152                    gas,
153                    output: Bytes::new(),
154                },
155                memory_offset: inputs.return_memory_offset.clone(),
156                was_precompile_called: false,
157                precompile_call_logs: Vec::new(),
158            })))
159        };
160
161        // Check depth
162        if depth > CALL_STACK_LIMIT as usize {
163            return return_result(InstructionResult::CallTooDeep);
164        }
165
166        // Create subroutine checkpoint
167        let checkpoint = ctx.journal_mut().checkpoint();
168
169        // Touch address. For "EIP-158 State Clear", this will erase empty accounts.
170        if let CallValue::Transfer(value) = inputs.value {
171            // Transfer value from caller to called account
172            // Target will get touched even if balance transferred is zero.
173            if let Some(i) =
174                ctx.journal_mut()
175                    .transfer_loaded(inputs.caller, inputs.target_address, value)
176            {
177                ctx.journal_mut().checkpoint_revert(checkpoint);
178                return return_result(i.into());
179            }
180        }
181
182        let interpreter_input = InputsImpl {
183            target_address: inputs.target_address,
184            caller_address: inputs.caller,
185            bytecode_address: Some(inputs.bytecode_address),
186            input: inputs.input.clone(),
187            call_value: inputs.value.get(),
188        };
189        let is_static = inputs.is_static;
190        let gas_limit = inputs.gas_limit;
191
192        if let Some(result) = precompiles.run(ctx, &inputs).map_err(ERROR::from_string)? {
193            let mut logs = Vec::new();
194            if result.result.is_ok() {
195                ctx.journal_mut().checkpoint_commit();
196            } else {
197                // clone logs that precompile created, only possible with custom precompiles.
198                // checkpoint.log_i will be always correct.
199                logs = ctx.journal_mut().logs()[checkpoint.log_i..].to_vec();
200                ctx.journal_mut().checkpoint_revert(checkpoint);
201            }
202            return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
203                result,
204                memory_offset: inputs.return_memory_offset.clone(),
205                was_precompile_called: true,
206                precompile_call_logs: logs,
207            })));
208        }
209
210        // Get bytecode and hash - either from known_bytecode or load from account
211        let (bytecode_hash, bytecode) = inputs.known_bytecode.clone();
212
213        // Returns success if bytecode is empty.
214        if bytecode.is_empty() {
215            ctx.journal_mut().checkpoint_commit();
216            return return_result(InstructionResult::Stop);
217        }
218
219        // Create interpreter and executes call and push new CallStackFrame.
220        this.get(EthFrame::invalid).clear(
221            FrameData::Call(CallFrame {
222                return_memory_range: inputs.return_memory_offset.clone(),
223            }),
224            FrameInput::Call(inputs),
225            depth,
226            memory,
227            ExtBytecode::new_with_hash(bytecode, bytecode_hash),
228            interpreter_input,
229            is_static,
230            ctx.cfg().spec().into(),
231            gas_limit,
232            checkpoint,
233        );
234        Ok(ItemOrResult::Item(this.consume()))
235    }
236
237    /// Make create frame.
238    #[inline]
239    pub fn make_create_frame<
240        CTX: ContextTr,
241        ERROR: From<ContextTrDbError<CTX>> + FromStringError,
242    >(
243        mut this: OutFrame<'_, Self>,
244        context: &mut CTX,
245        depth: usize,
246        memory: SharedMemory,
247        inputs: Box<CreateInputs>,
248    ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
249        let spec = context.cfg().spec().into();
250        let return_error = |e| {
251            Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome {
252                result: InterpreterResult {
253                    result: e,
254                    gas: Gas::new(inputs.gas_limit()),
255                    output: Bytes::new(),
256                },
257                address: None,
258            })))
259        };
260
261        // Check depth
262        if depth > CALL_STACK_LIMIT as usize {
263            return return_error(InstructionResult::CallTooDeep);
264        }
265
266        // Fetch balance of caller.
267        let journal = context.journal_mut();
268        let mut caller_info = journal.load_account_mut(inputs.caller())?;
269
270        // Check if caller has enough balance to send to the created contract.
271        // decrement of balance is done in the create_account_checkpoint.
272        if *caller_info.balance() < inputs.value() {
273            return return_error(InstructionResult::OutOfFunds);
274        }
275
276        // Increase nonce of caller and check if it overflows
277        let old_nonce = caller_info.nonce();
278        if !caller_info.bump_nonce() {
279            return return_error(InstructionResult::Return);
280        };
281
282        // Create address
283        let mut init_code_hash = None;
284        let created_address = match inputs.scheme() {
285            CreateScheme::Create => inputs.caller().create(old_nonce),
286            CreateScheme::Create2 { salt } => {
287                let init_code_hash = *init_code_hash.insert(keccak256(inputs.init_code()));
288                inputs.caller().create2(salt.to_be_bytes(), init_code_hash)
289            }
290            CreateScheme::Custom { address } => address,
291        };
292
293        drop(caller_info); // Drop caller info to avoid borrow checker issues.
294
295        // warm load account.
296        journal.load_account(created_address)?;
297
298        // Create account, transfer funds and make the journal checkpoint.
299        let checkpoint = match context.journal_mut().create_account_checkpoint(
300            inputs.caller(),
301            created_address,
302            inputs.value(),
303            spec,
304        ) {
305            Ok(checkpoint) => checkpoint,
306            Err(e) => return return_error(e.into()),
307        };
308
309        let bytecode = ExtBytecode::new_with_optional_hash(
310            Bytecode::new_legacy(inputs.init_code().clone()),
311            init_code_hash,
312        );
313
314        let interpreter_input = InputsImpl {
315            target_address: created_address,
316            caller_address: inputs.caller(),
317            bytecode_address: None,
318            input: CallInput::Bytes(Bytes::new()),
319            call_value: inputs.value(),
320        };
321        let gas_limit = inputs.gas_limit();
322
323        this.get(EthFrame::invalid).clear(
324            FrameData::Create(CreateFrame { created_address }),
325            FrameInput::Create(inputs),
326            depth,
327            memory,
328            bytecode,
329            interpreter_input,
330            false,
331            spec,
332            gas_limit,
333            checkpoint,
334        );
335        Ok(ItemOrResult::Item(this.consume()))
336    }
337
338    /// Initializes a frame with the given context and precompiles.
339    pub fn init_with_context<
340        CTX: ContextTr,
341        PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
342    >(
343        this: OutFrame<'_, Self>,
344        ctx: &mut CTX,
345        precompiles: &mut PRECOMPILES,
346        frame_init: FrameInit,
347    ) -> Result<
348        ItemOrResult<FrameToken, FrameResult>,
349        ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
350    > {
351        // TODO cleanup inner make functions
352        let FrameInit {
353            depth,
354            memory,
355            frame_input,
356        } = frame_init;
357
358        match frame_input {
359            FrameInput::Call(inputs) => {
360                Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs)
361            }
362            FrameInput::Create(inputs) => Self::make_create_frame(this, ctx, depth, memory, inputs),
363            FrameInput::Empty => unreachable!(),
364        }
365    }
366}
367
368impl EthFrame<EthInterpreter> {
369    /// Processes the next interpreter action, either creating a new frame or returning a result.
370    pub fn process_next_action<
371        CTX: ContextTr,
372        ERROR: From<ContextTrDbError<CTX>> + FromStringError,
373    >(
374        &mut self,
375        context: &mut CTX,
376        next_action: InterpreterAction,
377    ) -> Result<FrameInitOrResult<Self>, ERROR> {
378        // Run interpreter
379
380        let mut interpreter_result = match next_action {
381            InterpreterAction::NewFrame(frame_input) => {
382                let depth = self.depth + 1;
383                return Ok(ItemOrResult::Item(FrameInit {
384                    frame_input,
385                    depth,
386                    memory: self.interpreter.memory.new_child_context(),
387                }));
388            }
389            InterpreterAction::Return(result) => result,
390        };
391
392        // Handle return from frame
393        let result = match &self.data {
394            FrameData::Call(frame) => {
395                // return_call
396                // Revert changes or not.
397                if interpreter_result.result.is_ok() {
398                    context.journal_mut().checkpoint_commit();
399                } else {
400                    context.journal_mut().checkpoint_revert(self.checkpoint);
401                }
402                ItemOrResult::Result(FrameResult::Call(CallOutcome::new(
403                    interpreter_result,
404                    frame.return_memory_range.clone(),
405                )))
406            }
407            FrameData::Create(frame) => {
408                let (cfg, journal) = context.cfg_journal_mut();
409                return_create(
410                    journal,
411                    cfg,
412                    self.checkpoint,
413                    &mut interpreter_result,
414                    frame.created_address,
415                );
416
417                ItemOrResult::Result(FrameResult::Create(CreateOutcome::new(
418                    interpreter_result,
419                    Some(frame.created_address),
420                )))
421            }
422        };
423
424        Ok(result)
425    }
426
427    /// Processes a frame result and updates the interpreter state accordingly.
428    pub fn return_result<CTX: ContextTr, ERROR: From<ContextTrDbError<CTX>> + FromStringError>(
429        &mut self,
430        ctx: &mut CTX,
431        result: FrameResult,
432    ) -> Result<(), ERROR> {
433        self.interpreter.memory.free_child_context();
434        take_error::<ERROR, _>(ctx.error())?;
435
436        // Insert result to the top frame.
437        match result {
438            FrameResult::Call(outcome) => {
439                let out_gas = outcome.gas();
440                let ins_result = *outcome.instruction_result();
441                let returned_len = outcome.result.output.len();
442
443                let interpreter = &mut self.interpreter;
444                let mem_length = outcome.memory_length();
445                let mem_start = outcome.memory_start();
446                interpreter.return_data.set_buffer(outcome.result.output);
447
448                let target_len = min(mem_length, returned_len);
449
450                if ins_result == InstructionResult::FatalExternalError {
451                    panic!("Fatal external error in insert_call_outcome");
452                }
453
454                let item = if ins_result.is_ok() {
455                    U256::from(1)
456                } else {
457                    U256::ZERO
458                };
459                // Safe to push without stack limit check
460                let _ = interpreter.stack.push(item);
461
462                // Return unspend gas.
463                if ins_result.is_ok_or_revert() {
464                    interpreter.gas.erase_cost(out_gas.remaining());
465                    interpreter
466                        .memory
467                        .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
468                }
469
470                if ins_result.is_ok() {
471                    interpreter.gas.record_refund(out_gas.refunded());
472                }
473            }
474            FrameResult::Create(outcome) => {
475                let instruction_result = *outcome.instruction_result();
476                let interpreter = &mut self.interpreter;
477
478                if instruction_result == InstructionResult::Revert {
479                    // Save data to return data buffer if the create reverted
480                    interpreter
481                        .return_data
482                        .set_buffer(outcome.output().to_owned());
483                } else {
484                    // Otherwise clear it. Note that RETURN opcode should abort.
485                    interpreter.return_data.clear();
486                };
487
488                assert_ne!(
489                    instruction_result,
490                    InstructionResult::FatalExternalError,
491                    "Fatal external error in insert_eofcreate_outcome"
492                );
493
494                let this_gas = &mut interpreter.gas;
495                if instruction_result.is_ok_or_revert() {
496                    this_gas.erase_cost(outcome.gas().remaining());
497                }
498
499                let stack_item = if instruction_result.is_ok() {
500                    this_gas.record_refund(outcome.gas().refunded());
501                    outcome.address.unwrap_or_default().into_word().into()
502                } else {
503                    U256::ZERO
504                };
505
506                // Safe to push without stack limit check
507                let _ = interpreter.stack.push(stack_item);
508            }
509        }
510
511        Ok(())
512    }
513}
514
515/// Handles the result of a CREATE operation, including validation and state updates.
516pub fn return_create<JOURNAL: JournalTr, CFG: Cfg>(
517    journal: &mut JOURNAL,
518    cfg: CFG,
519    checkpoint: JournalCheckpoint,
520    interpreter_result: &mut InterpreterResult,
521    address: Address,
522) {
523    let max_code_size = cfg.max_code_size();
524    let is_eip3541_disabled = cfg.is_eip3541_disabled();
525    let spec_id = cfg.spec().into();
526
527    // If return is not ok revert and return.
528    if !interpreter_result.result.is_ok() {
529        journal.checkpoint_revert(checkpoint);
530        return;
531    }
532    // Host error if present on execution
533    // If ok, check contract creation limit and calculate gas deduction on output len.
534    //
535    // EIP-3541: Reject new contract code starting with the 0xEF byte
536    if !is_eip3541_disabled
537        && spec_id.is_enabled_in(LONDON)
538        && interpreter_result.output.first() == Some(&0xEF)
539    {
540        journal.checkpoint_revert(checkpoint);
541        interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
542        return;
543    }
544
545    // EIP-170: Contract code size limit to 0x6000 (~25kb)
546    // EIP-7954 increased this limit to 0x8000 (~32kb).
547    if spec_id.is_enabled_in(SPURIOUS_DRAGON) && interpreter_result.output.len() > max_code_size {
548        journal.checkpoint_revert(checkpoint);
549        interpreter_result.result = InstructionResult::CreateContractSizeLimit;
550        return;
551    }
552    let gas_for_code = cfg
553        .gas_params()
554        .code_deposit_cost(interpreter_result.output.len());
555    if !interpreter_result.gas.record_cost(gas_for_code) {
556        // Record code deposit gas cost and check if we are out of gas.
557        // EIP-2 point 3: If contract creation does not have enough gas to pay for the
558        // final gas fee for adding the contract code to the state, the contract
559        // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract.
560        if spec_id.is_enabled_in(HOMESTEAD) {
561            journal.checkpoint_revert(checkpoint);
562            interpreter_result.result = InstructionResult::OutOfGas;
563            return;
564        } else {
565            interpreter_result.output = Bytes::new();
566        }
567    }
568    // If we have enough gas we can commit changes.
569    journal.checkpoint_commit();
570
571    // Do analysis of bytecode straight away.
572    let bytecode = Bytecode::new_legacy(interpreter_result.output.clone());
573
574    // Set code
575    journal.set_code(address, bytecode);
576
577    interpreter_result.result = InstructionResult::Return;
578}