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::{journaled_state::account::JournaledAccountTr, result::FromStringError};
6use context_interface::{
7    context::ContextError,
8    journaled_state::{JournalCheckpoint, JournalTr},
9    local::{FrameToken, OutFrame},
10    Cfg, ContextTr, Database,
11};
12use core::cmp::min;
13use derive_where::derive_where;
14use interpreter::{
15    gas,
16    interpreter::{EthInterpreter, ExtBytecode},
17    interpreter_action::FrameInit,
18    interpreter_types::ReturnData,
19    CallInput, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, CreateScheme,
20    FrameInput, Gas, InputsImpl, InstructionResult, Interpreter, InterpreterAction,
21    InterpreterResult, InterpreterTypes, SharedMemory,
22};
23use primitives::{
24    constants::CALL_STACK_LIMIT,
25    hardfork::SpecId::{self, HOMESTEAD, LONDON, SPURIOUS_DRAGON},
26    keccak256, Address, Bytes, U256,
27};
28use state::Bytecode;
29use std::{borrow::ToOwned, boxed::Box, vec::Vec};
30
31/// Frame implementation for Ethereum.
32#[derive_where(Clone, Debug; IW,
33    <IW as InterpreterTypes>::Stack,
34    <IW as InterpreterTypes>::Memory,
35    <IW as InterpreterTypes>::Bytecode,
36    <IW as InterpreterTypes>::ReturnData,
37    <IW as InterpreterTypes>::Input,
38    <IW as InterpreterTypes>::RuntimeFlag,
39    <IW as InterpreterTypes>::Extend,
40)]
41pub struct EthFrame<IW: InterpreterTypes = EthInterpreter> {
42    /// Frame-specific data (Call, Create, or EOFCreate).
43    pub data: FrameData,
44    /// Input data for the frame.
45    pub input: FrameInput,
46    /// Current call depth in the execution stack.
47    pub depth: usize,
48    /// Journal checkpoint for state reversion.
49    pub checkpoint: JournalCheckpoint,
50    /// Interpreter instance for executing bytecode.
51    pub interpreter: Interpreter<IW>,
52    /// Whether the frame has been finished its execution.
53    /// Frame is considered finished if it has been called and returned a result.
54    pub is_finished: bool,
55}
56
57impl<IT: InterpreterTypes> FrameTr for EthFrame<IT> {
58    type FrameResult = FrameResult;
59    type FrameInit = FrameInit;
60}
61
62impl Default for EthFrame<EthInterpreter> {
63    fn default() -> Self {
64        Self::do_default(Interpreter::default())
65    }
66}
67
68impl EthFrame<EthInterpreter> {
69    /// Creates an new invalid [`EthFrame`].
70    pub fn invalid() -> Self {
71        Self::do_default(Interpreter::invalid())
72    }
73
74    fn do_default(interpreter: Interpreter<EthInterpreter>) -> Self {
75        Self {
76            data: FrameData::Call(CallFrame {
77                return_memory_range: 0..0,
78            }),
79            input: FrameInput::Empty,
80            depth: 0,
81            checkpoint: JournalCheckpoint::default(),
82            interpreter,
83            is_finished: false,
84        }
85    }
86
87    /// Returns true if the frame has finished execution.
88    pub fn is_finished(&self) -> bool {
89        self.is_finished
90    }
91
92    /// Sets the finished state of the frame.
93    pub fn set_finished(&mut self, finished: bool) {
94        self.is_finished = finished;
95    }
96}
97
98/// Type alias for database errors from a context.
99pub type ContextTrDbError<CTX> = <<CTX as ContextTr>::Db as Database>::Error;
100
101impl EthFrame<EthInterpreter> {
102    /// Clear and initialize a frame.
103    #[allow(clippy::too_many_arguments)]
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, bytecode_hash) = if let Some((hash, code)) = inputs.known_bytecode.clone() {
212            // Use provided bytecode and hash
213            (code, hash)
214        } else {
215            // Load account and get its bytecode
216            let account = ctx
217                .journal_mut()
218                .load_account_with_code(inputs.bytecode_address)?;
219            (
220                account.info.code.clone().unwrap_or_default(),
221                account.info.code_hash,
222            )
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, bytecode_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        // Fetch balance of caller.
279        let mut caller_info = context.journal_mut().load_account_mut(inputs.caller)?;
280
281        // Check if caller has enough balance to send to the created contract.
282        // decrement of balance is done in the create_account_checkpoint.
283        if *caller_info.balance() < inputs.value {
284            return return_error(InstructionResult::OutOfFunds);
285        }
286
287        // Increase nonce of caller and check if it overflows
288        let old_nonce = caller_info.nonce();
289        if !caller_info.bump_nonce() {
290            return return_error(InstructionResult::Return);
291        };
292
293        // Create address
294        let mut init_code_hash = None;
295        let created_address = match inputs.scheme {
296            CreateScheme::Create => inputs.caller.create(old_nonce),
297            CreateScheme::Create2 { salt } => {
298                let init_code_hash = *init_code_hash.insert(keccak256(&inputs.init_code));
299                inputs.caller.create2(salt.to_be_bytes(), init_code_hash)
300            }
301            CreateScheme::Custom { address } => address,
302        };
303
304        drop(caller_info); // Drop caller info to avoid borrow checker issues.
305
306        // warm load account.
307        context.journal_mut().load_account(created_address)?;
308
309        // Create account, transfer funds and make the journal checkpoint.
310        let checkpoint = match context.journal_mut().create_account_checkpoint(
311            inputs.caller,
312            created_address,
313            inputs.value,
314            spec,
315        ) {
316            Ok(checkpoint) => checkpoint,
317            Err(e) => return return_error(e.into()),
318        };
319
320        let bytecode = ExtBytecode::new_with_optional_hash(
321            Bytecode::new_legacy(inputs.init_code.clone()),
322            init_code_hash,
323        );
324
325        let interpreter_input = InputsImpl {
326            target_address: created_address,
327            caller_address: inputs.caller,
328            bytecode_address: None,
329            input: CallInput::Bytes(Bytes::new()),
330            call_value: inputs.value,
331        };
332        let gas_limit = inputs.gas_limit;
333
334        this.get(EthFrame::invalid).clear(
335            FrameData::Create(CreateFrame { created_address }),
336            FrameInput::Create(inputs),
337            depth,
338            memory,
339            bytecode,
340            interpreter_input,
341            false,
342            spec,
343            gas_limit,
344            checkpoint,
345        );
346        Ok(ItemOrResult::Item(this.consume()))
347    }
348
349    /// Initializes a frame with the given context and precompiles.
350    pub fn init_with_context<
351        CTX: ContextTr,
352        PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
353    >(
354        this: OutFrame<'_, Self>,
355        ctx: &mut CTX,
356        precompiles: &mut PRECOMPILES,
357        frame_init: FrameInit,
358    ) -> Result<
359        ItemOrResult<FrameToken, FrameResult>,
360        ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
361    > {
362        // TODO cleanup inner make functions
363        let FrameInit {
364            depth,
365            memory,
366            frame_input,
367        } = frame_init;
368
369        match frame_input {
370            FrameInput::Call(inputs) => {
371                Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs)
372            }
373            FrameInput::Create(inputs) => Self::make_create_frame(this, ctx, depth, memory, inputs),
374            FrameInput::Empty => unreachable!(),
375        }
376    }
377}
378
379impl EthFrame<EthInterpreter> {
380    /// Processes the next interpreter action, either creating a new frame or returning a result.
381    pub fn process_next_action<
382        CTX: ContextTr,
383        ERROR: From<ContextTrDbError<CTX>> + FromStringError,
384    >(
385        &mut self,
386        context: &mut CTX,
387        next_action: InterpreterAction,
388    ) -> Result<FrameInitOrResult<Self>, ERROR> {
389        let spec = context.cfg().spec().into();
390
391        // Run interpreter
392
393        let mut interpreter_result = match next_action {
394            InterpreterAction::NewFrame(frame_input) => {
395                let depth = self.depth + 1;
396                return Ok(ItemOrResult::Item(FrameInit {
397                    frame_input,
398                    depth,
399                    memory: self.interpreter.memory.new_child_context(),
400                }));
401            }
402            InterpreterAction::Return(result) => result,
403        };
404
405        // Handle return from frame
406        let result = match &self.data {
407            FrameData::Call(frame) => {
408                // return_call
409                // Revert changes or not.
410                if interpreter_result.result.is_ok() {
411                    context.journal_mut().checkpoint_commit();
412                } else {
413                    context.journal_mut().checkpoint_revert(self.checkpoint);
414                }
415                ItemOrResult::Result(FrameResult::Call(CallOutcome::new(
416                    interpreter_result,
417                    frame.return_memory_range.clone(),
418                )))
419            }
420            FrameData::Create(frame) => {
421                let max_code_size = context.cfg().max_code_size();
422                let is_eip3541_disabled = context.cfg().is_eip3541_disabled();
423                return_create(
424                    context.journal_mut(),
425                    self.checkpoint,
426                    &mut interpreter_result,
427                    frame.created_address,
428                    max_code_size,
429                    is_eip3541_disabled,
430                    spec,
431                );
432
433                ItemOrResult::Result(FrameResult::Create(CreateOutcome::new(
434                    interpreter_result,
435                    Some(frame.created_address),
436                )))
437            }
438        };
439
440        Ok(result)
441    }
442
443    /// Processes a frame result and updates the interpreter state accordingly.
444    pub fn return_result<CTX: ContextTr, ERROR: From<ContextTrDbError<CTX>> + FromStringError>(
445        &mut self,
446        ctx: &mut CTX,
447        result: FrameResult,
448    ) -> Result<(), ERROR> {
449        self.interpreter.memory.free_child_context();
450        match core::mem::replace(ctx.error(), Ok(())) {
451            Err(ContextError::Db(e)) => return Err(e.into()),
452            Err(ContextError::Custom(e)) => return Err(ERROR::from_string(e)),
453            Ok(_) => (),
454        }
455
456        // Insert result to the top frame.
457        match result {
458            FrameResult::Call(outcome) => {
459                let out_gas = outcome.gas();
460                let ins_result = *outcome.instruction_result();
461                let returned_len = outcome.result.output.len();
462
463                let interpreter = &mut self.interpreter;
464                let mem_length = outcome.memory_length();
465                let mem_start = outcome.memory_start();
466                interpreter.return_data.set_buffer(outcome.result.output);
467
468                let target_len = min(mem_length, returned_len);
469
470                if ins_result == InstructionResult::FatalExternalError {
471                    panic!("Fatal external error in insert_call_outcome");
472                }
473
474                let item = if ins_result.is_ok() {
475                    U256::from(1)
476                } else {
477                    U256::ZERO
478                };
479                // Safe to push without stack limit check
480                let _ = interpreter.stack.push(item);
481
482                // Return unspend gas.
483                if ins_result.is_ok_or_revert() {
484                    interpreter.gas.erase_cost(out_gas.remaining());
485                    interpreter
486                        .memory
487                        .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
488                }
489
490                if ins_result.is_ok() {
491                    interpreter.gas.record_refund(out_gas.refunded());
492                }
493            }
494            FrameResult::Create(outcome) => {
495                let instruction_result = *outcome.instruction_result();
496                let interpreter = &mut self.interpreter;
497
498                if instruction_result == InstructionResult::Revert {
499                    // Save data to return data buffer if the create reverted
500                    interpreter
501                        .return_data
502                        .set_buffer(outcome.output().to_owned());
503                } else {
504                    // Otherwise clear it. Note that RETURN opcode should abort.
505                    interpreter.return_data.clear();
506                };
507
508                assert_ne!(
509                    instruction_result,
510                    InstructionResult::FatalExternalError,
511                    "Fatal external error in insert_eofcreate_outcome"
512                );
513
514                let this_gas = &mut interpreter.gas;
515                if instruction_result.is_ok_or_revert() {
516                    this_gas.erase_cost(outcome.gas().remaining());
517                }
518
519                let stack_item = if instruction_result.is_ok() {
520                    this_gas.record_refund(outcome.gas().refunded());
521                    outcome.address.unwrap_or_default().into_word().into()
522                } else {
523                    U256::ZERO
524                };
525
526                // Safe to push without stack limit check
527                let _ = interpreter.stack.push(stack_item);
528            }
529        }
530
531        Ok(())
532    }
533}
534
535/// Handles the result of a CREATE operation, including validation and state updates.
536pub fn return_create<JOURNAL: JournalTr>(
537    journal: &mut JOURNAL,
538    checkpoint: JournalCheckpoint,
539    interpreter_result: &mut InterpreterResult,
540    address: Address,
541    max_code_size: usize,
542    is_eip3541_disabled: bool,
543    spec_id: SpecId,
544) {
545    // If return is not ok revert and return.
546    if !interpreter_result.result.is_ok() {
547        journal.checkpoint_revert(checkpoint);
548        return;
549    }
550    // Host error if present on execution
551    // If ok, check contract creation limit and calculate gas deduction on output len.
552    //
553    // EIP-3541: Reject new contract code starting with the 0xEF byte
554    if !is_eip3541_disabled
555        && spec_id.is_enabled_in(LONDON)
556        && interpreter_result.output.first() == Some(&0xEF)
557    {
558        journal.checkpoint_revert(checkpoint);
559        interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
560        return;
561    }
562
563    // EIP-170: Contract code size limit to 0x6000 (~25kb)
564    // EIP-7907 increased this limit to 0xc000 (~49kb).
565    if spec_id.is_enabled_in(SPURIOUS_DRAGON) && interpreter_result.output.len() > max_code_size {
566        journal.checkpoint_revert(checkpoint);
567        interpreter_result.result = InstructionResult::CreateContractSizeLimit;
568        return;
569    }
570    let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT;
571    if !interpreter_result.gas.record_cost(gas_for_code) {
572        // Record code deposit gas cost and check if we are out of gas.
573        // EIP-2 point 3: If contract creation does not have enough gas to pay for the
574        // final gas fee for adding the contract code to the state, the contract
575        // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract.
576        if spec_id.is_enabled_in(HOMESTEAD) {
577            journal.checkpoint_revert(checkpoint);
578            interpreter_result.result = InstructionResult::OutOfGas;
579            return;
580        } else {
581            interpreter_result.output = Bytes::new();
582        }
583    }
584    // If we have enough gas we can commit changes.
585    journal.checkpoint_commit();
586
587    // Do analysis of bytecode straight away.
588    let bytecode = Bytecode::new_legacy(interpreter_result.output.clone());
589
590    // Set code
591    journal.set_code(address, bytecode);
592
593    interpreter_result.result = InstructionResult::Return;
594}