revm/context/
evm_context.rs

1use revm_interpreter::CallValue;
2use revm_precompile::PrecompileErrors;
3
4use super::inner_evm_context::InnerEvmContext;
5use crate::{
6    db::Database,
7    interpreter::{
8        analysis::validate_eof, CallInputs, Contract, CreateInputs, EOFCreateInputs, EOFCreateKind,
9        Gas, InstructionResult, Interpreter, InterpreterResult,
10    },
11    primitives::{
12        keccak256, Address, Bytecode, Bytes, CreateScheme, EVMError, Env, Eof,
13        SpecId::{self, *},
14        B256, EOF_MAGIC_BYTES,
15    },
16    ContextPrecompiles, FrameOrResult, CALL_STACK_LIMIT,
17};
18use core::{
19    fmt,
20    ops::{Deref, DerefMut},
21};
22use std::{boxed::Box, sync::Arc};
23
24/// EVM context that contains the inner EVM context and precompiles.
25pub struct EvmContext<DB: Database> {
26    /// Inner EVM context.
27    pub inner: InnerEvmContext<DB>,
28    /// Precompiles that are available for evm.
29    pub precompiles: ContextPrecompiles<DB>,
30}
31
32impl<DB: Database + Clone> Clone for EvmContext<DB>
33where
34    DB::Error: Clone,
35{
36    fn clone(&self) -> Self {
37        Self {
38            inner: self.inner.clone(),
39            precompiles: ContextPrecompiles::default(),
40        }
41    }
42}
43
44impl<DB> fmt::Debug for EvmContext<DB>
45where
46    DB: Database + fmt::Debug,
47    DB::Error: fmt::Debug,
48{
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        f.debug_struct("EvmContext")
51            .field("inner", &self.inner)
52            .field("precompiles", &self.inner)
53            .finish_non_exhaustive()
54    }
55}
56
57impl<DB: Database> Deref for EvmContext<DB> {
58    type Target = InnerEvmContext<DB>;
59
60    fn deref(&self) -> &Self::Target {
61        &self.inner
62    }
63}
64
65impl<DB: Database> DerefMut for EvmContext<DB> {
66    fn deref_mut(&mut self) -> &mut Self::Target {
67        &mut self.inner
68    }
69}
70
71impl<DB: Database> EvmContext<DB> {
72    /// Create new context with database.
73    pub fn new(db: DB) -> Self {
74        Self {
75            inner: InnerEvmContext::new(db),
76            precompiles: ContextPrecompiles::default(),
77        }
78    }
79
80    /// Creates a new context with the given environment and database.
81    #[inline]
82    pub fn new_with_env(db: DB, env: Box<Env>) -> Self {
83        Self {
84            inner: InnerEvmContext::new_with_env(db, env),
85            precompiles: ContextPrecompiles::default(),
86        }
87    }
88
89    /// Sets the database.
90    ///
91    /// Note that this will ignore the previous `error` if set.
92    #[inline]
93    pub fn with_db<ODB: Database>(self, db: ODB) -> EvmContext<ODB> {
94        EvmContext {
95            inner: self.inner.with_db(db),
96            precompiles: ContextPrecompiles::default(),
97        }
98    }
99
100    /// Sets precompiles
101    #[inline]
102    pub fn set_precompiles(&mut self, precompiles: ContextPrecompiles<DB>) {
103        // set warm loaded addresses.
104        self.journaled_state
105            .warm_preloaded_addresses
106            .extend(precompiles.addresses_set());
107        self.precompiles = precompiles;
108    }
109
110    /// Call precompile contract
111    #[inline]
112    fn call_precompile(
113        &mut self,
114        address: &Address,
115        input_data: &Bytes,
116        gas: Gas,
117    ) -> Result<Option<InterpreterResult>, EVMError<DB::Error>> {
118        let Some(outcome) =
119            self.precompiles
120                .call(address, input_data, gas.limit(), &mut self.inner)
121        else {
122            return Ok(None);
123        };
124
125        let mut result = InterpreterResult {
126            result: InstructionResult::Return,
127            gas,
128            output: Bytes::new(),
129        };
130
131        match outcome {
132            Ok(output) => {
133                if result.gas.record_cost(output.gas_used) {
134                    result.result = InstructionResult::Return;
135                    result.output = output.bytes;
136                } else {
137                    result.result = InstructionResult::PrecompileOOG;
138                }
139            }
140            Err(PrecompileErrors::Error(e)) => {
141                result.result = if e.is_oog() {
142                    InstructionResult::PrecompileOOG
143                } else {
144                    InstructionResult::PrecompileError
145                };
146            }
147            Err(PrecompileErrors::Fatal { msg }) => return Err(EVMError::Precompile(msg)),
148        }
149        Ok(Some(result))
150    }
151
152    /// Make call frame
153    #[inline]
154    pub fn make_call_frame(
155        &mut self,
156        inputs: &CallInputs,
157    ) -> Result<FrameOrResult, EVMError<DB::Error>> {
158        let gas = Gas::new(inputs.gas_limit);
159
160        let return_result = |instruction_result: InstructionResult| {
161            Ok(FrameOrResult::new_call_result(
162                InterpreterResult {
163                    result: instruction_result,
164                    gas,
165                    output: Bytes::new(),
166                },
167                inputs.return_memory_offset.clone(),
168            ))
169        };
170
171        // Check depth
172        if self.journaled_state.depth() > CALL_STACK_LIMIT {
173            return return_result(InstructionResult::CallTooDeep);
174        }
175
176        // Make account warm and loaded
177        let _ = self
178            .inner
179            .journaled_state
180            .load_account_delegated(inputs.bytecode_address, &mut self.inner.db)?;
181
182        // Create subroutine checkpoint
183        let checkpoint = self.journaled_state.checkpoint();
184
185        // Touch address. For "EIP-158 State Clear", this will erase empty accounts.
186        match inputs.value {
187            // if transfer value is zero, load account and force the touch.
188            CallValue::Transfer(value) if value.is_zero() => {
189                self.load_account(inputs.target_address)?;
190                self.journaled_state.touch(&inputs.target_address);
191            }
192            CallValue::Transfer(value) => {
193                // Transfer value from caller to called account. As value get transferred
194                // target gets touched.
195                if let Some(result) = self.inner.journaled_state.transfer(
196                    &inputs.caller,
197                    &inputs.target_address,
198                    value,
199                    &mut self.inner.db,
200                )? {
201                    self.journaled_state.checkpoint_revert(checkpoint);
202                    return return_result(result);
203                }
204            }
205            _ => {}
206        };
207
208        let is_ext_delegate = inputs.scheme.is_ext_delegate_call();
209
210        if !is_ext_delegate {
211            if let Some(result) =
212                self.call_precompile(&inputs.bytecode_address, &inputs.input, gas)?
213            {
214                if result.result.is_ok() {
215                    self.journaled_state.checkpoint_commit();
216                } else {
217                    self.journaled_state.checkpoint_revert(checkpoint);
218                }
219                return Ok(FrameOrResult::new_call_result(
220                    result,
221                    inputs.return_memory_offset.clone(),
222                ));
223            }
224        }
225        // load account and bytecode
226        let account = self
227            .inner
228            .journaled_state
229            .load_code(inputs.bytecode_address, &mut self.inner.db)?;
230
231        let code_hash = account.info.code_hash();
232        let mut bytecode = account.info.code.clone().unwrap_or_default();
233
234        // ExtDelegateCall is not allowed to call non-EOF contracts.
235        if is_ext_delegate && !bytecode.bytes_slice().starts_with(&EOF_MAGIC_BYTES) {
236            return return_result(InstructionResult::InvalidExtDelegateCallTarget);
237        }
238
239        if bytecode.is_empty() {
240            self.journaled_state.checkpoint_commit();
241            return return_result(InstructionResult::Stop);
242        }
243
244        if let Bytecode::Eip7702(eip7702_bytecode) = bytecode {
245            bytecode = self
246                .inner
247                .journaled_state
248                .load_code(eip7702_bytecode.delegated_address, &mut self.inner.db)?
249                .info
250                .code
251                .clone()
252                .unwrap_or_default();
253        }
254
255        let contract =
256            Contract::new_with_context(inputs.input.clone(), bytecode, Some(code_hash), inputs);
257        // Create interpreter and executes call and push new CallStackFrame.
258        Ok(FrameOrResult::new_call_frame(
259            inputs.return_memory_offset.clone(),
260            checkpoint,
261            Interpreter::new(contract, gas.limit(), inputs.is_static),
262        ))
263    }
264
265    /// Make create frame.
266    #[inline]
267    pub fn make_create_frame(
268        &mut self,
269        spec_id: SpecId,
270        inputs: &CreateInputs,
271    ) -> Result<FrameOrResult, EVMError<DB::Error>> {
272        let return_error = |e| {
273            Ok(FrameOrResult::new_create_result(
274                InterpreterResult {
275                    result: e,
276                    gas: Gas::new(inputs.gas_limit),
277                    output: Bytes::new(),
278                },
279                None,
280            ))
281        };
282
283        // Check depth
284        if self.journaled_state.depth() > CALL_STACK_LIMIT {
285            return return_error(InstructionResult::CallTooDeep);
286        }
287
288        // Prague EOF
289        if spec_id.is_enabled_in(OSAKA) && inputs.init_code.starts_with(&EOF_MAGIC_BYTES) {
290            return return_error(InstructionResult::CreateInitCodeStartingEF00);
291        }
292
293        // Fetch balance of caller.
294        let caller_balance = self.balance(inputs.caller)?;
295
296        // Check if caller has enough balance to send to the created contract.
297        if caller_balance.data < inputs.value {
298            return return_error(InstructionResult::OutOfFunds);
299        }
300
301        // Increase nonce of caller and check if it overflows
302        let old_nonce;
303        if let Some(nonce) = self.journaled_state.inc_nonce(inputs.caller) {
304            old_nonce = nonce - 1;
305        } else {
306            return return_error(InstructionResult::Return);
307        }
308
309        // Create address
310        let mut init_code_hash = B256::ZERO;
311        let created_address = match inputs.scheme {
312            CreateScheme::Create => inputs.caller.create(old_nonce),
313            CreateScheme::Create2 { salt } => {
314                init_code_hash = keccak256(&inputs.init_code);
315                inputs.caller.create2(salt.to_be_bytes(), init_code_hash)
316            }
317        };
318
319        // created address is not allowed to be a precompile.
320        if self.precompiles.contains(&created_address) {
321            return return_error(InstructionResult::CreateCollision);
322        }
323
324        // warm load account.
325        self.load_account(created_address)?;
326
327        // create account, transfer funds and make the journal checkpoint.
328        let checkpoint = match self.journaled_state.create_account_checkpoint(
329            inputs.caller,
330            created_address,
331            inputs.value,
332            spec_id,
333        ) {
334            Ok(checkpoint) => checkpoint,
335            Err(e) => {
336                return return_error(e);
337            }
338        };
339
340        let bytecode = Bytecode::new_legacy(inputs.init_code.clone());
341
342        let contract = Contract::new(
343            Bytes::new(),
344            bytecode,
345            Some(init_code_hash),
346            created_address,
347            None,
348            inputs.caller,
349            inputs.value,
350        );
351
352        Ok(FrameOrResult::new_create_frame(
353            created_address,
354            checkpoint,
355            Interpreter::new(contract, inputs.gas_limit, false),
356        ))
357    }
358
359    /// Make create frame.
360    #[inline]
361    pub fn make_eofcreate_frame(
362        &mut self,
363        spec_id: SpecId,
364        inputs: &EOFCreateInputs,
365    ) -> Result<FrameOrResult, EVMError<DB::Error>> {
366        let return_error = |e| {
367            Ok(FrameOrResult::new_eofcreate_result(
368                InterpreterResult {
369                    result: e,
370                    gas: Gas::new(inputs.gas_limit),
371                    output: Bytes::new(),
372                },
373                None,
374            ))
375        };
376
377        let (input, initcode, created_address) = match &inputs.kind {
378            EOFCreateKind::Opcode {
379                initcode,
380                input,
381                created_address,
382            } => (input.clone(), initcode.clone(), Some(*created_address)),
383            EOFCreateKind::Tx { initdata } => {
384                // decode eof and init code.
385                // TODO handle inc_nonce handling more gracefully.
386                let Ok((eof, input)) = Eof::decode_dangling(initdata.clone()) else {
387                    self.journaled_state.inc_nonce(inputs.caller);
388                    return return_error(InstructionResult::InvalidEOFInitCode);
389                };
390
391                if validate_eof(&eof).is_err() {
392                    // TODO (EOF) new error type.
393                    self.journaled_state.inc_nonce(inputs.caller);
394                    return return_error(InstructionResult::InvalidEOFInitCode);
395                }
396
397                // Use nonce from tx (if set) to calculate address.
398                // If not set, use the nonce from the account.
399                let nonce = self
400                    .env
401                    .tx
402                    .nonce
403                    .map(|nonce| self.env.tx.caller.create(nonce));
404
405                (input, eof, nonce)
406            }
407        };
408
409        // Check depth
410        if self.journaled_state.depth() > CALL_STACK_LIMIT {
411            return return_error(InstructionResult::CallTooDeep);
412        }
413
414        // Fetch balance of caller.
415        let caller_balance = self.balance(inputs.caller)?;
416
417        // Check if caller has enough balance to send to the created contract.
418        if caller_balance.data < inputs.value {
419            return return_error(InstructionResult::OutOfFunds);
420        }
421
422        // Increase nonce of caller and check if it overflows
423        let Some(nonce) = self.journaled_state.inc_nonce(inputs.caller) else {
424            // can't happen on mainnet.
425            return return_error(InstructionResult::Return);
426        };
427        let old_nonce = nonce - 1;
428
429        let created_address = created_address.unwrap_or_else(|| inputs.caller.create(old_nonce));
430
431        // created address is not allowed to be a precompile.
432        if self.precompiles.contains(&created_address) {
433            return return_error(InstructionResult::CreateCollision);
434        }
435
436        // Load account so it needs to be marked as warm for access list.
437        self.load_account(created_address)?;
438
439        // create account, transfer funds and make the journal checkpoint.
440        let checkpoint = match self.journaled_state.create_account_checkpoint(
441            inputs.caller,
442            created_address,
443            inputs.value,
444            spec_id,
445        ) {
446            Ok(checkpoint) => checkpoint,
447            Err(e) => {
448                return return_error(e);
449            }
450        };
451
452        let contract = Contract::new(
453            input.clone(),
454            // fine to clone as it is Bytes.
455            Bytecode::Eof(Arc::new(initcode.clone())),
456            None,
457            created_address,
458            None,
459            inputs.caller,
460            inputs.value,
461        );
462
463        let mut interpreter = Interpreter::new(contract, inputs.gas_limit, false);
464        // EOF init will enable RETURNCONTRACT opcode.
465        interpreter.set_is_eof_init();
466
467        Ok(FrameOrResult::new_eofcreate_frame(
468            created_address,
469            checkpoint,
470            interpreter,
471        ))
472    }
473}
474
475/// Test utilities for the [`EvmContext`].
476#[cfg(any(test, feature = "test-utils"))]
477pub(crate) mod test_utils {
478    use super::*;
479    use crate::primitives::U256;
480    use crate::{
481        db::{CacheDB, EmptyDB},
482        journaled_state::JournaledState,
483        primitives::{address, HashSet, SpecId, B256},
484    };
485
486    /// Mock caller address.
487    pub const MOCK_CALLER: Address = address!("0000000000000000000000000000000000000000");
488
489    /// Creates `CallInputs` that calls a provided contract address from the mock caller.
490    pub fn create_mock_call_inputs(to: Address) -> CallInputs {
491        CallInputs {
492            input: Bytes::new(),
493            gas_limit: 0,
494            bytecode_address: to,
495            target_address: to,
496            caller: MOCK_CALLER,
497            value: CallValue::Transfer(U256::ZERO),
498            scheme: revm_interpreter::CallScheme::Call,
499            is_eof: false,
500            is_static: false,
501            return_memory_offset: 0..0,
502        }
503    }
504
505    /// Creates an evm context with a cache db backend.
506    /// Additionally loads the mock caller account into the db,
507    /// and sets the balance to the provided U256 value.
508    pub fn create_cache_db_evm_context_with_balance(
509        env: Box<Env>,
510        mut db: CacheDB<EmptyDB>,
511        balance: U256,
512    ) -> EvmContext<CacheDB<EmptyDB>> {
513        db.insert_account_info(
514            test_utils::MOCK_CALLER,
515            crate::primitives::AccountInfo {
516                nonce: 0,
517                balance,
518                code_hash: B256::default(),
519                code: None,
520            },
521        );
522        create_cache_db_evm_context(env, db)
523    }
524
525    /// Creates a cached db evm context.
526    pub fn create_cache_db_evm_context(
527        env: Box<Env>,
528        db: CacheDB<EmptyDB>,
529    ) -> EvmContext<CacheDB<EmptyDB>> {
530        EvmContext {
531            inner: InnerEvmContext {
532                env,
533                journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::default()),
534                db,
535                error: Ok(()),
536                #[cfg(feature = "optimism")]
537                l1_block_info: None,
538            },
539            precompiles: ContextPrecompiles::default(),
540        }
541    }
542
543    /// Returns a new `EvmContext` with an empty journaled state.
544    pub fn create_empty_evm_context(env: Box<Env>, db: EmptyDB) -> EvmContext<EmptyDB> {
545        EvmContext {
546            inner: InnerEvmContext {
547                env,
548                journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::default()),
549                db,
550                error: Ok(()),
551                #[cfg(feature = "optimism")]
552                l1_block_info: None,
553            },
554            precompiles: ContextPrecompiles::default(),
555        }
556    }
557}
558
559#[cfg(test)]
560mod tests {
561    use super::*;
562    use crate::primitives::U256;
563    use crate::{
564        db::{CacheDB, EmptyDB},
565        primitives::{address, Bytecode},
566        Frame, JournalEntry,
567    };
568    use std::boxed::Box;
569    use test_utils::*;
570
571    // Tests that the `EVMContext::make_call_frame` function returns an error if the
572    // call stack is too deep.
573    #[test]
574    fn test_make_call_frame_stack_too_deep() {
575        let env = Env::default();
576        let db = EmptyDB::default();
577        let mut context = test_utils::create_empty_evm_context(Box::new(env), db);
578        context.journaled_state.depth = CALL_STACK_LIMIT as usize + 1;
579        let contract = address!("dead10000000000000000000000000000001dead");
580        let call_inputs = test_utils::create_mock_call_inputs(contract);
581        let res = context.make_call_frame(&call_inputs);
582        let Ok(FrameOrResult::Result(err)) = res else {
583            panic!("Expected FrameOrResult::Result");
584        };
585        assert_eq!(
586            err.interpreter_result().result,
587            InstructionResult::CallTooDeep
588        );
589    }
590
591    // Tests that the `EVMContext::make_call_frame` function returns an error if the
592    // transfer fails on the journaled state. It also verifies that the revert was
593    // checkpointed on the journaled state correctly.
594    #[test]
595    fn test_make_call_frame_transfer_revert() {
596        let env = Env::default();
597        let db = EmptyDB::default();
598        let mut evm_context = test_utils::create_empty_evm_context(Box::new(env), db);
599        let contract = address!("dead10000000000000000000000000000001dead");
600        let mut call_inputs = test_utils::create_mock_call_inputs(contract);
601        call_inputs.value = CallValue::Transfer(U256::from(1));
602        let res = evm_context.make_call_frame(&call_inputs);
603        let Ok(FrameOrResult::Result(result)) = res else {
604            panic!("Expected FrameOrResult::Result");
605        };
606        assert_eq!(
607            result.interpreter_result().result,
608            InstructionResult::OutOfFunds
609        );
610        let checkpointed = vec![vec![JournalEntry::AccountWarmed { address: contract }]];
611        assert_eq!(evm_context.journaled_state.journal, checkpointed);
612        assert_eq!(evm_context.journaled_state.depth, 0);
613    }
614
615    #[test]
616    fn test_make_call_frame_missing_code_context() {
617        let env = Env::default();
618        let cdb = CacheDB::new(EmptyDB::default());
619        let bal = U256::from(3_000_000_000_u128);
620        let mut context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal);
621        let contract = address!("dead10000000000000000000000000000001dead");
622        let call_inputs = test_utils::create_mock_call_inputs(contract);
623        let res = context.make_call_frame(&call_inputs);
624        let Ok(FrameOrResult::Result(result)) = res else {
625            panic!("Expected FrameOrResult::Result");
626        };
627        assert_eq!(result.interpreter_result().result, InstructionResult::Stop);
628    }
629
630    #[test]
631    fn test_make_call_frame_succeeds() {
632        let env = Env::default();
633        let mut cdb = CacheDB::new(EmptyDB::default());
634        let bal = U256::from(3_000_000_000_u128);
635        let by = Bytecode::new_raw(Bytes::from(vec![0x60, 0x00, 0x60, 0x00]));
636        let contract = address!("dead10000000000000000000000000000001dead");
637        cdb.insert_account_info(
638            contract,
639            crate::primitives::AccountInfo {
640                nonce: 0,
641                balance: bal,
642                code_hash: by.clone().hash_slow(),
643                code: Some(by),
644            },
645        );
646        let mut evm_context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal);
647        let call_inputs = test_utils::create_mock_call_inputs(contract);
648        let res = evm_context.make_call_frame(&call_inputs);
649        let Ok(FrameOrResult::Frame(Frame::Call(call_frame))) = res else {
650            panic!("Expected FrameOrResult::Frame(Frame::Call(..))");
651        };
652        assert_eq!(call_frame.return_memory_range, 0..0,);
653    }
654}