revm/context/
inner_evm_context.rs

1use crate::{
2    db::Database,
3    interpreter::{
4        analysis::to_analysed, gas, return_ok, AccountLoad, Eip7702CodeLoad, InstructionResult,
5        InterpreterResult, SStoreResult, SelfDestructResult, StateLoad,
6    },
7    journaled_state::JournaledState,
8    primitives::{
9        AccessListItem, Account, Address, AnalysisKind, Bytecode, Bytes, CfgEnv, EVMError, Env,
10        Eof, HashSet, Spec,
11        SpecId::{self, *},
12        B256, EOF_MAGIC_BYTES, EOF_MAGIC_HASH, U256,
13    },
14    JournalCheckpoint,
15};
16use std::{boxed::Box, sync::Arc};
17
18/// EVM contexts contains data that EVM needs for execution.
19#[derive(Debug)]
20pub struct InnerEvmContext<DB: Database> {
21    /// EVM Environment contains all the information about config, block and transaction that
22    /// evm needs.
23    pub env: Box<Env>,
24    /// EVM State with journaling support.
25    pub journaled_state: JournaledState,
26    /// Database to load data from.
27    pub db: DB,
28    /// Error that happened during execution.
29    pub error: Result<(), EVMError<DB::Error>>,
30    /// Used as temporary value holder to store L1 block info.
31    #[cfg(feature = "optimism")]
32    pub l1_block_info: Option<crate::optimism::L1BlockInfo>,
33}
34
35impl<DB: Database + Clone> Clone for InnerEvmContext<DB>
36where
37    DB::Error: Clone,
38{
39    fn clone(&self) -> Self {
40        Self {
41            env: self.env.clone(),
42            journaled_state: self.journaled_state.clone(),
43            db: self.db.clone(),
44            error: self.error.clone(),
45            #[cfg(feature = "optimism")]
46            l1_block_info: self.l1_block_info.clone(),
47        }
48    }
49}
50
51impl<DB: Database> InnerEvmContext<DB> {
52    pub fn new(db: DB) -> Self {
53        Self {
54            env: Box::default(),
55            journaled_state: JournaledState::new(SpecId::LATEST, HashSet::default()),
56            db,
57            error: Ok(()),
58            #[cfg(feature = "optimism")]
59            l1_block_info: None,
60        }
61    }
62
63    /// Creates a new context with the given environment and database.
64    #[inline]
65    pub fn new_with_env(db: DB, env: Box<Env>) -> Self {
66        Self {
67            env,
68            journaled_state: JournaledState::new(SpecId::LATEST, HashSet::default()),
69            db,
70            error: Ok(()),
71            #[cfg(feature = "optimism")]
72            l1_block_info: None,
73        }
74    }
75
76    /// Sets the database.
77    ///
78    /// Note that this will ignore the previous `error` if set.
79    #[inline]
80    pub fn with_db<ODB: Database>(self, db: ODB) -> InnerEvmContext<ODB> {
81        InnerEvmContext {
82            env: self.env,
83            journaled_state: self.journaled_state,
84            db,
85            error: Ok(()),
86            #[cfg(feature = "optimism")]
87            l1_block_info: self.l1_block_info,
88        }
89    }
90
91    /// Returns the configured EVM spec ID.
92    #[inline]
93    pub const fn spec_id(&self) -> SpecId {
94        self.journaled_state.spec
95    }
96
97    /// Load access list for berlin hard fork.
98    ///
99    /// Loading of accounts/storages is needed to make them warm.
100    #[inline]
101    pub fn load_access_list(&mut self) -> Result<(), EVMError<DB::Error>> {
102        for AccessListItem {
103            address,
104            storage_keys,
105        } in self.env.tx.access_list.iter()
106        {
107            self.journaled_state.initial_account_load(
108                *address,
109                storage_keys.iter().map(|i| U256::from_be_bytes(i.0)),
110                &mut self.db,
111            )?;
112        }
113        Ok(())
114    }
115
116    /// Return environment.
117    #[inline]
118    pub fn env(&mut self) -> &mut Env {
119        &mut self.env
120    }
121
122    /// Returns reference to [`CfgEnv`].
123    pub fn cfg(&self) -> &CfgEnv {
124        &self.env.cfg
125    }
126
127    /// Returns the error by replacing it with `Ok(())`, if any.
128    #[inline]
129    pub fn take_error(&mut self) -> Result<(), EVMError<DB::Error>> {
130        core::mem::replace(&mut self.error, Ok(()))
131    }
132
133    /// Fetch block hash from database.
134    #[inline]
135    pub fn block_hash(&mut self, number: u64) -> Result<B256, EVMError<DB::Error>> {
136        self.db.block_hash(number).map_err(EVMError::Database)
137    }
138
139    /// Mark account as touched as only touched accounts will be added to state.
140    #[inline]
141    pub fn touch(&mut self, address: &Address) {
142        self.journaled_state.touch(address);
143    }
144
145    /// Loads an account into memory. Returns `true` if it is cold accessed.
146    #[inline]
147    pub fn load_account(
148        &mut self,
149        address: Address,
150    ) -> Result<StateLoad<&mut Account>, EVMError<DB::Error>> {
151        self.journaled_state.load_account(address, &mut self.db)
152    }
153
154    /// Load account from database to JournaledState.
155    ///
156    /// Return boolean pair where first is `is_cold` second bool `exists`.
157    #[inline]
158    pub fn load_account_delegated(
159        &mut self,
160        address: Address,
161    ) -> Result<AccountLoad, EVMError<DB::Error>> {
162        self.journaled_state
163            .load_account_delegated(address, &mut self.db)
164    }
165
166    /// Return account balance and is_cold flag.
167    #[inline]
168    pub fn balance(&mut self, address: Address) -> Result<StateLoad<U256>, EVMError<DB::Error>> {
169        self.journaled_state
170            .load_account(address, &mut self.db)
171            .map(|acc| acc.map(|a| a.info.balance))
172    }
173
174    /// Return account code bytes and if address is cold loaded.
175    ///
176    /// In case of EOF account it will return `EOF_MAGIC` (0xEF00) as code.
177    #[inline]
178    pub fn code(
179        &mut self,
180        address: Address,
181    ) -> Result<Eip7702CodeLoad<Bytes>, EVMError<DB::Error>> {
182        let a = self.journaled_state.load_code(address, &mut self.db)?;
183        // SAFETY: safe to unwrap as load_code will insert code if it is empty.
184        let code = a.info.code.as_ref().unwrap();
185        if code.is_eof() {
186            return Ok(Eip7702CodeLoad::new_not_delegated(
187                EOF_MAGIC_BYTES.clone(),
188                a.is_cold,
189            ));
190        }
191
192        if let Bytecode::Eip7702(code) = code {
193            let address = code.address();
194            let is_cold = a.is_cold;
195
196            let delegated_account = self.journaled_state.load_code(address, &mut self.db)?;
197
198            // SAFETY: safe to unwrap as load_code will insert code if it is empty.
199            let delegated_code = delegated_account.info.code.as_ref().unwrap();
200
201            let bytes = if delegated_code.is_eof() {
202                EOF_MAGIC_BYTES.clone()
203            } else {
204                delegated_code.original_bytes()
205            };
206
207            return Ok(Eip7702CodeLoad::new(
208                StateLoad::new(bytes, is_cold),
209                delegated_account.is_cold,
210            ));
211        }
212
213        Ok(Eip7702CodeLoad::new_not_delegated(
214            code.original_bytes(),
215            a.is_cold,
216        ))
217    }
218
219    /// Get code hash of address.
220    ///
221    /// In case of EOF account it will return `EOF_MAGIC_HASH`
222    /// (the hash of `0xEF00`).
223    #[inline]
224    pub fn code_hash(
225        &mut self,
226        address: Address,
227    ) -> Result<Eip7702CodeLoad<B256>, EVMError<DB::Error>> {
228        let acc = self.journaled_state.load_code(address, &mut self.db)?;
229        if acc.is_empty() {
230            return Ok(Eip7702CodeLoad::new_not_delegated(B256::ZERO, acc.is_cold));
231        }
232        // SAFETY: safe to unwrap as load_code will insert code if it is empty.
233        let code = acc.info.code.as_ref().unwrap();
234
235        // If bytecode is EIP-7702 then we need to load the delegated account.
236        if let Bytecode::Eip7702(code) = code {
237            let address = code.address();
238            let is_cold = acc.is_cold;
239
240            let delegated_account = self.journaled_state.load_code(address, &mut self.db)?;
241
242            let hash = if delegated_account.is_empty() {
243                B256::ZERO
244            } else if delegated_account.info.code.as_ref().unwrap().is_eof() {
245                EOF_MAGIC_HASH
246            } else {
247                delegated_account.info.code_hash
248            };
249
250            return Ok(Eip7702CodeLoad::new(
251                StateLoad::new(hash, is_cold),
252                delegated_account.is_cold,
253            ));
254        }
255
256        let hash = if code.is_eof() {
257            EOF_MAGIC_HASH
258        } else {
259            acc.info.code_hash
260        };
261
262        Ok(Eip7702CodeLoad::new_not_delegated(hash, acc.is_cold))
263    }
264
265    /// Load storage slot, if storage is not present inside the account then it will be loaded from database.
266    #[inline]
267    pub fn sload(
268        &mut self,
269        address: Address,
270        index: U256,
271    ) -> Result<StateLoad<U256>, EVMError<DB::Error>> {
272        // account is always warm. reference on that statement https://eips.ethereum.org/EIPS/eip-2929 see `Note 2:`
273        self.journaled_state.sload(address, index, &mut self.db)
274    }
275
276    /// Storage change of storage slot, before storing `sload` will be called for that slot.
277    #[inline]
278    pub fn sstore(
279        &mut self,
280        address: Address,
281        index: U256,
282        value: U256,
283    ) -> Result<StateLoad<SStoreResult>, EVMError<DB::Error>> {
284        self.journaled_state
285            .sstore(address, index, value, &mut self.db)
286    }
287
288    /// Returns transient storage value.
289    #[inline]
290    pub fn tload(&mut self, address: Address, index: U256) -> U256 {
291        self.journaled_state.tload(address, index)
292    }
293
294    /// Stores transient storage value.
295    #[inline]
296    pub fn tstore(&mut self, address: Address, index: U256, value: U256) {
297        self.journaled_state.tstore(address, index, value)
298    }
299
300    /// Selfdestructs the account.
301    #[inline]
302    pub fn selfdestruct(
303        &mut self,
304        address: Address,
305        target: Address,
306    ) -> Result<StateLoad<SelfDestructResult>, EVMError<DB::Error>> {
307        self.journaled_state
308            .selfdestruct(address, target, &mut self.db)
309    }
310
311    /// If error is present revert changes, otherwise save EOF bytecode.
312    pub fn eofcreate_return<SPEC: Spec>(
313        &mut self,
314        interpreter_result: &mut InterpreterResult,
315        address: Address,
316        journal_checkpoint: JournalCheckpoint,
317    ) {
318        // Note we still execute RETURN opcode and return the bytes.
319        // In EOF those opcodes should abort execution.
320        //
321        // In RETURN gas is still protecting us from ddos and in oog,
322        // behaviour will be same as if it failed on return.
323        //
324        // Bytes of RETURN will drained in `insert_eofcreate_outcome`.
325        if interpreter_result.result != InstructionResult::ReturnContract {
326            self.journaled_state.checkpoint_revert(journal_checkpoint);
327            return;
328        }
329
330        if interpreter_result.output.len() > self.cfg().max_code_size() {
331            self.journaled_state.checkpoint_revert(journal_checkpoint);
332            interpreter_result.result = InstructionResult::CreateContractSizeLimit;
333            return;
334        }
335
336        // deduct gas for code deployment.
337        let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT;
338        if !interpreter_result.gas.record_cost(gas_for_code) {
339            self.journaled_state.checkpoint_revert(journal_checkpoint);
340            interpreter_result.result = InstructionResult::OutOfGas;
341            return;
342        }
343
344        // commit changes reduces depth by -1.
345        self.journaled_state.checkpoint_commit();
346
347        // decode bytecode has a performance hit, but it has reasonable restrains.
348        let bytecode =
349            Eof::decode(interpreter_result.output.clone()).expect("Eof is already verified");
350
351        // eof bytecode is going to be hashed.
352        self.journaled_state
353            .set_code(address, Bytecode::Eof(Arc::new(bytecode)));
354    }
355
356    /// Handles call return.
357    #[inline]
358    pub fn call_return(
359        &mut self,
360        interpreter_result: &InterpreterResult,
361        journal_checkpoint: JournalCheckpoint,
362    ) {
363        // revert changes or not.
364        if matches!(interpreter_result.result, return_ok!()) {
365            self.journaled_state.checkpoint_commit();
366        } else {
367            self.journaled_state.checkpoint_revert(journal_checkpoint);
368        }
369    }
370
371    /// Handles create return.
372    #[inline]
373    pub fn create_return<SPEC: Spec>(
374        &mut self,
375        interpreter_result: &mut InterpreterResult,
376        address: Address,
377        journal_checkpoint: JournalCheckpoint,
378    ) {
379        // if return is not ok revert and return.
380        if !matches!(interpreter_result.result, return_ok!()) {
381            self.journaled_state.checkpoint_revert(journal_checkpoint);
382            return;
383        }
384        // Host error if present on execution
385        // if ok, check contract creation limit and calculate gas deduction on output len.
386        //
387        // EIP-3541: Reject new contract code starting with the 0xEF byte
388        if SPEC::enabled(LONDON) && interpreter_result.output.first() == Some(&0xEF) {
389            self.journaled_state.checkpoint_revert(journal_checkpoint);
390            interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
391            return;
392        }
393
394        // EIP-170: Contract code size limit
395        // By default limit is 0x6000 (~25kb)
396        if SPEC::enabled(SPURIOUS_DRAGON)
397            && interpreter_result.output.len() > self.cfg().max_code_size()
398        {
399            self.journaled_state.checkpoint_revert(journal_checkpoint);
400            interpreter_result.result = InstructionResult::CreateContractSizeLimit;
401            return;
402        }
403        let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT;
404        if !interpreter_result.gas.record_cost(gas_for_code) {
405            // record code deposit gas cost and check if we are out of gas.
406            // EIP-2 point 3: If contract creation does not have enough gas to pay for the
407            // final gas fee for adding the contract code to the state, the contract
408            //  creation fails (i.e. goes out-of-gas) rather than leaving an empty contract.
409            if SPEC::enabled(HOMESTEAD) {
410                self.journaled_state.checkpoint_revert(journal_checkpoint);
411                interpreter_result.result = InstructionResult::OutOfGas;
412                return;
413            } else {
414                interpreter_result.output = Bytes::new();
415            }
416        }
417        // if we have enough gas we can commit changes.
418        self.journaled_state.checkpoint_commit();
419
420        // Do analysis of bytecode straight away.
421        let bytecode = match self.env.cfg.perf_analyse_created_bytecodes {
422            AnalysisKind::Raw => Bytecode::new_legacy(interpreter_result.output.clone()),
423            AnalysisKind::Analyse => {
424                to_analysed(Bytecode::new_legacy(interpreter_result.output.clone()))
425            }
426        };
427
428        // set code
429        self.journaled_state.set_code(address, bytecode);
430
431        interpreter_result.result = InstructionResult::Return;
432    }
433}