revm_handler/
frame.rs

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