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