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