revm_handler/
frame.rs

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