revm_context/journal/
inner.rs

1//! Module containing the [`JournalInner`] that is part of [`crate::Journal`].
2use crate::entry::SelfdestructionRevertStatus;
3
4use super::JournalEntryTr;
5use bytecode::Bytecode;
6use context_interface::{
7    context::{SStoreResult, SelfDestructResult, StateLoad},
8    journaled_state::{AccountLoad, JournalCheckpoint, TransferError},
9};
10use core::mem;
11use database_interface::Database;
12use primitives::{
13    hardfork::SpecId::{self, *},
14    hash_map::Entry,
15    Address, HashMap, HashSet, Log, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256,
16};
17use state::{Account, EvmState, EvmStorageSlot, TransientStorage};
18use std::vec::Vec;
19/// Inner journal state that contains journal and state changes.
20///
21/// Spec Id is a essential information for the Journal.
22#[derive(Debug, Clone, PartialEq, Eq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct JournalInner<ENTRY> {
25    /// The current state
26    pub state: EvmState,
27    /// Transient storage that is discarded after every transaction.
28    ///
29    /// See [EIP-1153](https://eips.ethereum.org/EIPS/eip-1153).
30    pub transient_storage: TransientStorage,
31    /// Emitted logs
32    pub logs: Vec<Log>,
33    /// The current call stack depth
34    pub depth: usize,
35    /// The journal of state changes, one for each transaction
36    pub journal: Vec<ENTRY>,
37    /// Global transaction id that represent number of transactions executed (Including reverted ones).
38    /// It can be different from number of `journal_history` as some transaction could be
39    /// reverted or had a error on execution.
40    ///
41    /// This ID is used in `Self::state` to determine if account/storage is touched/warm/cold.
42    pub transaction_id: usize,
43    /// The spec ID for the EVM. Spec is required for some journal entries and needs to be set for
44    /// JournalInner to be functional.
45    ///
46    /// If spec is set it assumed that precompile addresses are set as well for this particular spec.
47    ///
48    /// This spec is used for two things:
49    ///
50    /// - [EIP-161]: Prior to this EIP, Ethereum had separate definitions for empty and non-existing accounts.
51    /// - [EIP-6780]: `SELFDESTRUCT` only in same transaction
52    ///
53    /// [EIP-161]: https://eips.ethereum.org/EIPS/eip-161
54    /// [EIP-6780]: https://eips.ethereum.org/EIPS/eip-6780
55    pub spec: SpecId,
56    /// Warm loaded addresses are used to check if loaded address
57    /// should be considered cold or warm loaded when the account
58    /// is first accessed.
59    ///
60    /// Note that this not include newly loaded accounts, account and storage
61    /// is considered warm if it is found in the `State`.
62    pub warm_preloaded_addresses: HashSet<Address>,
63    /// Warm coinbase address, stored separately to avoid cloning preloaded addresses.
64    pub warm_coinbase_address: Option<Address>,
65    /// Precompile addresses
66    pub precompiles: HashSet<Address>,
67}
68
69impl<ENTRY: JournalEntryTr> Default for JournalInner<ENTRY> {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
76    /// Creates new [`JournalInner`].
77    ///
78    /// `warm_preloaded_addresses` is used to determine if address is considered warm loaded.
79    /// In ordinary case this is precompile or beneficiary.
80    pub fn new() -> JournalInner<ENTRY> {
81        Self {
82            state: HashMap::default(),
83            transient_storage: TransientStorage::default(),
84            logs: Vec::new(),
85            journal: Vec::default(),
86            transaction_id: 0,
87            depth: 0,
88            spec: SpecId::default(),
89            warm_preloaded_addresses: HashSet::default(),
90            precompiles: HashSet::default(),
91            warm_coinbase_address: None,
92        }
93    }
94
95    /// Returns the logs
96    #[inline]
97    pub fn take_logs(&mut self) -> Vec<Log> {
98        mem::take(&mut self.logs)
99    }
100
101    /// Prepare for next transaction, by committing the current journal to history, incrementing the transaction id
102    /// and returning the logs.
103    ///
104    /// This function is used to prepare for next transaction. It will save the current journal
105    /// and clear the journal for the next transaction.
106    ///
107    /// `commit_tx` is used even for discarding transactions so transaction_id will be incremented.
108    pub fn commit_tx(&mut self) {
109        // Clears all field from JournalInner. Doing it this way to avoid
110        // missing any field.
111        let Self {
112            state,
113            transient_storage,
114            logs,
115            depth,
116            journal,
117            transaction_id,
118            spec,
119            warm_preloaded_addresses,
120            precompiles,
121            warm_coinbase_address,
122        } = self;
123        // Spec precompiles and state are not changed. It is always set again execution.
124        let _ = spec;
125        let _ = precompiles;
126        let _ = state;
127        transient_storage.clear();
128        *depth = 0;
129
130        // Do nothing with journal history so we can skip cloning present journal.
131        journal.clear();
132
133        // Clear coinbase address warming for next tx
134        *warm_coinbase_address = None;
135        // Load precompiles into warm_preloaded_addresses.
136        // TODO for precompiles we can use max transaction_id so they are always touched warm loaded.
137        // at least after state clear EIP.
138        reset_preloaded_addresses(warm_preloaded_addresses, precompiles);
139        // increment transaction id.
140        *transaction_id += 1;
141        logs.clear();
142    }
143
144    /// Discard the current transaction, by reverting the journal entries and incrementing the transaction id.
145    pub fn discard_tx(&mut self) {
146        // if there is no journal entries, there has not been any changes.
147        let Self {
148            state,
149            transient_storage,
150            logs,
151            depth,
152            journal,
153            transaction_id,
154            spec,
155            warm_preloaded_addresses,
156            warm_coinbase_address,
157            precompiles,
158        } = self;
159
160        let is_spurious_dragon_enabled = spec.is_enabled_in(SPURIOUS_DRAGON);
161        // iterate over all journals entries and revert our global state
162        journal.drain(..).rev().for_each(|entry| {
163            entry.revert(state, None, is_spurious_dragon_enabled);
164        });
165        transient_storage.clear();
166        *depth = 0;
167        logs.clear();
168        *transaction_id += 1;
169        // Clear coinbase address warming for next tx
170        *warm_coinbase_address = None;
171        reset_preloaded_addresses(warm_preloaded_addresses, precompiles);
172    }
173
174    /// Take the [`EvmState`] and clears the journal by resetting it to initial state.
175    ///
176    /// Note: Precompile addresses and spec are preserved and initial state of
177    /// warm_preloaded_addresses will contain precompiles addresses.
178    #[inline]
179    pub fn finalize(&mut self) -> EvmState {
180        // Clears all field from JournalInner. Doing it this way to avoid
181        // missing any field.
182        let Self {
183            state,
184            transient_storage,
185            logs,
186            depth,
187            journal,
188            transaction_id,
189            spec,
190            warm_preloaded_addresses,
191            warm_coinbase_address,
192            precompiles,
193        } = self;
194        // Spec is not changed. And it is always set again in execution.
195        let _ = spec;
196        // Clear coinbase address warming for next tx
197        *warm_coinbase_address = None;
198        // Load precompiles into warm_preloaded_addresses.
199        reset_preloaded_addresses(warm_preloaded_addresses, precompiles);
200
201        let state = mem::take(state);
202        logs.clear();
203        transient_storage.clear();
204
205        // clear journal and journal history.
206        journal.clear();
207        *depth = 0;
208        // reset transaction id.
209        *transaction_id = 0;
210
211        state
212    }
213
214    /// Return reference to state.
215    #[inline]
216    pub fn state(&mut self) -> &mut EvmState {
217        &mut self.state
218    }
219
220    /// Sets SpecId.
221    #[inline]
222    pub fn set_spec_id(&mut self, spec: SpecId) {
223        self.spec = spec;
224    }
225
226    /// Mark account as touched as only touched accounts will be added to state.
227    /// This is especially important for state clear where touched empty accounts needs to
228    /// be removed from state.
229    #[inline]
230    pub fn touch(&mut self, address: Address) {
231        if let Some(account) = self.state.get_mut(&address) {
232            Self::touch_account(&mut self.journal, address, account);
233        }
234    }
235
236    /// Mark account as touched.
237    #[inline]
238    fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
239        if !account.is_touched() {
240            journal.push(ENTRY::account_touched(address));
241            account.mark_touch();
242        }
243    }
244
245    /// Returns the _loaded_ [Account] for the given address.
246    ///
247    /// This assumes that the account has already been loaded.
248    ///
249    /// # Panics
250    ///
251    /// Panics if the account has not been loaded and is missing from the state set.
252    #[inline]
253    pub fn account(&self, address: Address) -> &Account {
254        self.state
255            .get(&address)
256            .expect("Account expected to be loaded") // Always assume that acc is already loaded
257    }
258
259    /// Set code and its hash to the account.
260    ///
261    /// Note: Assume account is warm and that hash is calculated from code.
262    #[inline]
263    pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
264        let account = self.state.get_mut(&address).unwrap();
265        Self::touch_account(&mut self.journal, address, account);
266
267        self.journal.push(ENTRY::code_changed(address));
268
269        account.info.code_hash = hash;
270        account.info.code = Some(code);
271    }
272
273    /// Use it only if you know that acc is warm.
274    ///
275    /// Assume account is warm.
276    ///
277    /// In case of EIP-7702 code with zero address, the bytecode will be erased.
278    #[inline]
279    pub fn set_code(&mut self, address: Address, code: Bytecode) {
280        if let Bytecode::Eip7702(eip7702_bytecode) = &code {
281            if eip7702_bytecode.address().is_zero() {
282                self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
283                return;
284            }
285        }
286
287        let hash = code.hash_slow();
288        self.set_code_with_hash(address, code, hash)
289    }
290
291    /// Add journal entry for caller accounting.
292    #[inline]
293    pub fn caller_accounting_journal_entry(
294        &mut self,
295        address: Address,
296        old_balance: U256,
297        bump_nonce: bool,
298    ) {
299        // account balance changed.
300        self.journal
301            .push(ENTRY::balance_changed(address, old_balance));
302        // account is touched.
303        self.journal.push(ENTRY::account_touched(address));
304
305        if bump_nonce {
306            // nonce changed.
307            self.journal.push(ENTRY::nonce_changed(address));
308        }
309    }
310
311    /// Increments the balance of the account.
312    ///
313    /// Mark account as touched.
314    #[inline]
315    pub fn balance_incr<DB: Database>(
316        &mut self,
317        db: &mut DB,
318        address: Address,
319        balance: U256,
320    ) -> Result<(), DB::Error> {
321        let account = self.load_account(db, address)?.data;
322        let old_balance = account.info.balance;
323        account.info.balance = account.info.balance.saturating_add(balance);
324
325        // march account as touched.
326        if !account.is_touched() {
327            account.mark_touch();
328            self.journal.push(ENTRY::account_touched(address));
329        }
330
331        // add journal entry for balance increment.
332        self.journal
333            .push(ENTRY::balance_changed(address, old_balance));
334        Ok(())
335    }
336
337    /// Increments the nonce of the account.
338    #[inline]
339    pub fn nonce_bump_journal_entry(&mut self, address: Address) {
340        self.journal.push(ENTRY::nonce_changed(address));
341    }
342
343    /// Transfers balance from two accounts. Returns error if sender balance is not enough.
344    #[inline]
345    pub fn transfer<DB: Database>(
346        &mut self,
347        db: &mut DB,
348        from: Address,
349        to: Address,
350        balance: U256,
351    ) -> Result<Option<TransferError>, DB::Error> {
352        if balance.is_zero() {
353            self.load_account(db, to)?;
354            let to_account = self.state.get_mut(&to).unwrap();
355            Self::touch_account(&mut self.journal, to, to_account);
356            return Ok(None);
357        }
358        // load accounts
359        self.load_account(db, from)?;
360        self.load_account(db, to)?;
361
362        // sub balance from
363        let from_account = self.state.get_mut(&from).unwrap();
364        Self::touch_account(&mut self.journal, from, from_account);
365        let from_balance = &mut from_account.info.balance;
366
367        let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
368            return Ok(Some(TransferError::OutOfFunds));
369        };
370        *from_balance = from_balance_decr;
371
372        // add balance to
373        let to_account = &mut self.state.get_mut(&to).unwrap();
374        Self::touch_account(&mut self.journal, to, to_account);
375        let to_balance = &mut to_account.info.balance;
376        let Some(to_balance_incr) = to_balance.checked_add(balance) else {
377            return Ok(Some(TransferError::OverflowPayment));
378        };
379        *to_balance = to_balance_incr;
380        // Overflow of U256 balance is not possible to happen on mainnet. We don't bother to return funds from from_acc.
381
382        self.journal
383            .push(ENTRY::balance_transfer(from, to, balance));
384
385        Ok(None)
386    }
387
388    /// Creates account or returns false if collision is detected.
389    ///
390    /// There are few steps done:
391    /// 1. Make created account warm loaded (AccessList) and this should
392    ///    be done before subroutine checkpoint is created.
393    /// 2. Check if there is collision of newly created account with existing one.
394    /// 3. Mark created account as created.
395    /// 4. Add fund to created account
396    /// 5. Increment nonce of created account if SpuriousDragon is active
397    /// 6. Decrease balance of caller account.
398    ///
399    /// # Panics
400    ///
401    /// Panics if the caller is not loaded inside the EVM state.
402    /// This should have been done inside `create_inner`.
403    #[inline]
404    pub fn create_account_checkpoint(
405        &mut self,
406        caller: Address,
407        target_address: Address,
408        balance: U256,
409        spec_id: SpecId,
410    ) -> Result<JournalCheckpoint, TransferError> {
411        // Enter subroutine
412        let checkpoint = self.checkpoint();
413
414        // Fetch balance of caller.
415        let caller_balance = self.state.get(&caller).unwrap().info.balance;
416        // Check if caller has enough balance to send to the created contract.
417        if caller_balance < balance {
418            self.checkpoint_revert(checkpoint);
419            return Err(TransferError::OutOfFunds);
420        }
421
422        // Newly created account is present, as we just loaded it.
423        let target_acc = self.state.get_mut(&target_address).unwrap();
424        let last_journal = &mut self.journal;
425
426        // New account can be created if:
427        // Bytecode is not empty.
428        // Nonce is not zero
429        // Account is not precompile.
430        if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
431            self.checkpoint_revert(checkpoint);
432            return Err(TransferError::CreateCollision);
433        }
434
435        // set account status to create.
436        let is_created_globally = target_acc.mark_created_locally();
437
438        // this entry will revert set nonce.
439        last_journal.push(ENTRY::account_created(target_address, is_created_globally));
440        target_acc.info.code = None;
441        // EIP-161: State trie clearing (invariant-preserving alternative)
442        if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
443            // nonce is going to be reset to zero in AccountCreated journal entry.
444            target_acc.info.nonce = 1;
445        }
446
447        // touch account. This is important as for pre SpuriousDragon account could be
448        // saved even empty.
449        Self::touch_account(last_journal, target_address, target_acc);
450
451        // Add balance to created account, as we already have target here.
452        let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
453            self.checkpoint_revert(checkpoint);
454            return Err(TransferError::OverflowPayment);
455        };
456        target_acc.info.balance = new_balance;
457
458        // safe to decrement for the caller as balance check is already done.
459        self.state.get_mut(&caller).unwrap().info.balance -= balance;
460
461        // add journal entry of transferred balance
462        last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
463
464        Ok(checkpoint)
465    }
466
467    /// Makes a checkpoint that in case of Revert can bring back state to this point.
468    #[inline]
469    pub fn checkpoint(&mut self) -> JournalCheckpoint {
470        let checkpoint = JournalCheckpoint {
471            log_i: self.logs.len(),
472            journal_i: self.journal.len(),
473        };
474        self.depth += 1;
475        checkpoint
476    }
477
478    /// Commits the checkpoint.
479    #[inline]
480    pub fn checkpoint_commit(&mut self) {
481        self.depth -= 1;
482    }
483
484    /// Reverts all changes to state until given checkpoint.
485    #[inline]
486    pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
487        let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
488        let state = &mut self.state;
489        let transient_storage = &mut self.transient_storage;
490        self.depth -= 1;
491        self.logs.truncate(checkpoint.log_i);
492
493        // iterate over last N journals sets and revert our global state
494        self.journal
495            .drain(checkpoint.journal_i..)
496            .rev()
497            .for_each(|entry| {
498                entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
499            });
500    }
501
502    /// Performs selfdestruct action.
503    /// Transfers balance from address to target. Check if target exist/is_cold
504    ///
505    /// Note: Balance will be lost if address and target are the same BUT when
506    /// current spec enables Cancun, this happens only when the account associated to address
507    /// is created in the same tx
508    ///
509    /// # References:
510    ///  * <https://github.com/ethereum/go-ethereum/blob/141cd425310b503c5678e674a8c3872cf46b7086/core/vm/instructions.go#L832-L833>
511    ///  * <https://github.com/ethereum/go-ethereum/blob/141cd425310b503c5678e674a8c3872cf46b7086/core/state/statedb.go#L449>
512    ///  * <https://eips.ethereum.org/EIPS/eip-6780>
513    #[inline]
514    pub fn selfdestruct<DB: Database>(
515        &mut self,
516        db: &mut DB,
517        address: Address,
518        target: Address,
519    ) -> Result<StateLoad<SelfDestructResult>, DB::Error> {
520        let spec = self.spec;
521        let account_load = self.load_account(db, target)?;
522        let is_cold = account_load.is_cold;
523        let is_empty = account_load.state_clear_aware_is_empty(spec);
524
525        if address != target {
526            // Both accounts are loaded before this point, `address` as we execute its contract.
527            // and `target` at the beginning of the function.
528            let acc_balance = self.state.get(&address).unwrap().info.balance;
529
530            let target_account = self.state.get_mut(&target).unwrap();
531            Self::touch_account(&mut self.journal, target, target_account);
532            target_account.info.balance += acc_balance;
533        }
534
535        let acc = self.state.get_mut(&address).unwrap();
536        let balance = acc.info.balance;
537
538        let destroyed_status = if !acc.is_selfdestructed() {
539            SelfdestructionRevertStatus::GloballySelfdestroyed
540        } else if !acc.is_selfdestructed_locally() {
541            SelfdestructionRevertStatus::LocallySelfdestroyed
542        } else {
543            SelfdestructionRevertStatus::RepeatedSelfdestruction
544        };
545
546        let is_cancun_enabled = spec.is_enabled_in(CANCUN);
547
548        // EIP-6780 (Cancun hard-fork): selfdestruct only if contract is created in the same tx
549        let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
550            acc.mark_selfdestructed_locally();
551            acc.info.balance = U256::ZERO;
552            Some(ENTRY::account_destroyed(
553                address,
554                target,
555                destroyed_status,
556                balance,
557            ))
558        } else if address != target {
559            acc.info.balance = U256::ZERO;
560            Some(ENTRY::balance_transfer(address, target, balance))
561        } else {
562            // State is not changed:
563            // * if we are after Cancun upgrade and
564            // * Selfdestruct account that is created in the same transaction and
565            // * Specify the target is same as selfdestructed account. The balance stays unchanged.
566            None
567        };
568
569        if let Some(entry) = journal_entry {
570            self.journal.push(entry);
571        };
572
573        Ok(StateLoad {
574            data: SelfDestructResult {
575                had_value: !balance.is_zero(),
576                target_exists: !is_empty,
577                previously_destroyed: destroyed_status
578                    == SelfdestructionRevertStatus::RepeatedSelfdestruction,
579            },
580            is_cold,
581        })
582    }
583
584    /// Loads account into memory. return if it is cold or warm accessed
585    #[inline]
586    pub fn load_account<DB: Database>(
587        &mut self,
588        db: &mut DB,
589        address: Address,
590    ) -> Result<StateLoad<&mut Account>, DB::Error> {
591        self.load_account_optional(db, address, false, [])
592    }
593
594    /// Loads account into memory. If account is EIP-7702 type it will additionally
595    /// load delegated account.
596    ///
597    /// It will mark both this and delegated account as warm loaded.
598    ///
599    /// Returns information about the account (If it is empty or cold loaded) and if present the information
600    /// about the delegated account (If it is cold loaded).
601    #[inline]
602    pub fn load_account_delegated<DB: Database>(
603        &mut self,
604        db: &mut DB,
605        address: Address,
606    ) -> Result<StateLoad<AccountLoad>, DB::Error> {
607        let spec = self.spec;
608        let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
609        let account = self.load_account_optional(db, address, is_eip7702_enabled, [])?;
610        let is_empty = account.state_clear_aware_is_empty(spec);
611
612        let mut account_load = StateLoad::new(
613            AccountLoad {
614                is_delegate_account_cold: None,
615                is_empty,
616            },
617            account.is_cold,
618        );
619
620        // load delegate code if account is EIP-7702
621        if let Some(Bytecode::Eip7702(code)) = &account.info.code {
622            let address = code.address();
623            let delegate_account = self.load_account(db, address)?;
624            account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
625        }
626
627        Ok(account_load)
628    }
629
630    /// Loads account and its code. If account is already loaded it will load its code.
631    ///
632    /// It will mark account as warm loaded. If not existing Database will be queried for data.
633    ///
634    /// In case of EIP-7702 delegated account will not be loaded,
635    /// [`Self::load_account_delegated`] should be used instead.
636    #[inline]
637    pub fn load_code<DB: Database>(
638        &mut self,
639        db: &mut DB,
640        address: Address,
641    ) -> Result<StateLoad<&mut Account>, DB::Error> {
642        self.load_account_optional(db, address, true, [])
643    }
644
645    /// Loads account. If account is already loaded it will be marked as warm.
646    #[inline]
647    pub fn load_account_optional<DB: Database>(
648        &mut self,
649        db: &mut DB,
650        address: Address,
651        load_code: bool,
652        storage_keys: impl IntoIterator<Item = StorageKey>,
653    ) -> Result<StateLoad<&mut Account>, DB::Error> {
654        let load = match self.state.entry(address) {
655            Entry::Occupied(entry) => {
656                let account = entry.into_mut();
657                let is_cold = account.mark_warm_with_transaction_id(self.transaction_id);
658                // if it is colad loaded we need to clear local flags that can interact with selfdestruct
659                if is_cold {
660                    // if it is cold loaded and we have selfdestructed locally it means that
661                    // account was selfdestructed in previous transaction and we need to clear its information and storage.
662                    if account.is_selfdestructed_locally() {
663                        account.selfdestruct();
664                        account.unmark_selfdestructed_locally();
665                    }
666                    // unmark locally created
667                    account.unmark_created_locally();
668                }
669                StateLoad {
670                    data: account,
671                    is_cold,
672                }
673            }
674            Entry::Vacant(vac) => {
675                let account = if let Some(account) = db.basic(address)? {
676                    account.into()
677                } else {
678                    Account::new_not_existing(self.transaction_id)
679                };
680
681                // Precompiles among some other account(coinbase included) are warm loaded so we need to take that into account
682                let is_cold = !self.warm_preloaded_addresses.contains(&address)
683                    && self.warm_coinbase_address.as_ref() != Some(&address);
684
685                StateLoad {
686                    data: vac.insert(account),
687                    is_cold,
688                }
689            }
690        };
691
692        // journal loading of cold account.
693        if load.is_cold {
694            self.journal.push(ENTRY::account_warmed(address));
695        }
696        if load_code {
697            let info = &mut load.data.info;
698            if info.code.is_none() {
699                let code = if info.code_hash == KECCAK_EMPTY {
700                    Bytecode::default()
701                } else {
702                    db.code_by_hash(info.code_hash)?
703                };
704                info.code = Some(code);
705            }
706        }
707
708        for storage_key in storage_keys.into_iter() {
709            sload_with_account(
710                load.data,
711                db,
712                &mut self.journal,
713                self.transaction_id,
714                address,
715                storage_key,
716            )?;
717        }
718        Ok(load)
719    }
720
721    /// Loads storage slot.
722    ///
723    /// # Panics
724    ///
725    /// Panics if the account is not present in the state.
726    #[inline]
727    pub fn sload<DB: Database>(
728        &mut self,
729        db: &mut DB,
730        address: Address,
731        key: StorageKey,
732    ) -> Result<StateLoad<StorageValue>, DB::Error> {
733        // assume acc is warm
734        let account = self.state.get_mut(&address).unwrap();
735        // only if account is created in this tx we can assume that storage is empty.
736        sload_with_account(
737            account,
738            db,
739            &mut self.journal,
740            self.transaction_id,
741            address,
742            key,
743        )
744    }
745
746    /// Stores storage slot.
747    ///
748    /// And returns (original,present,new) slot value.
749    ///
750    /// **Note**: Account should already be present in our state.
751    #[inline]
752    pub fn sstore<DB: Database>(
753        &mut self,
754        db: &mut DB,
755        address: Address,
756        key: StorageKey,
757        new: StorageValue,
758    ) -> Result<StateLoad<SStoreResult>, DB::Error> {
759        // assume that acc exists and load the slot.
760        let present = self.sload(db, address, key)?;
761        let acc = self.state.get_mut(&address).unwrap();
762
763        // if there is no original value in dirty return present value, that is our original.
764        let slot = acc.storage.get_mut(&key).unwrap();
765
766        // new value is same as present, we don't need to do anything
767        if present.data == new {
768            return Ok(StateLoad::new(
769                SStoreResult {
770                    original_value: slot.original_value(),
771                    present_value: present.data,
772                    new_value: new,
773                },
774                present.is_cold,
775            ));
776        }
777
778        self.journal
779            .push(ENTRY::storage_changed(address, key, present.data));
780        // insert value into present state.
781        slot.present_value = new;
782        Ok(StateLoad::new(
783            SStoreResult {
784                original_value: slot.original_value(),
785                present_value: present.data,
786                new_value: new,
787            },
788            present.is_cold,
789        ))
790    }
791
792    /// Read transient storage tied to the account.
793    ///
794    /// EIP-1153: Transient storage opcodes
795    #[inline]
796    pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
797        self.transient_storage
798            .get(&(address, key))
799            .copied()
800            .unwrap_or_default()
801    }
802
803    /// Store transient storage tied to the account.
804    ///
805    /// If values is different add entry to the journal
806    /// so that old state can be reverted if that action is needed.
807    ///
808    /// EIP-1153: Transient storage opcodes
809    #[inline]
810    pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
811        let had_value = if new.is_zero() {
812            // if new values is zero, remove entry from transient storage.
813            // if previous values was some insert it inside journal.
814            // If it is none nothing should be inserted.
815            self.transient_storage.remove(&(address, key))
816        } else {
817            // insert values
818            let previous_value = self
819                .transient_storage
820                .insert((address, key), new)
821                .unwrap_or_default();
822
823            // check if previous value is same
824            if previous_value != new {
825                // if it is different, insert previous values inside journal.
826                Some(previous_value)
827            } else {
828                None
829            }
830        };
831
832        if let Some(had_value) = had_value {
833            // insert in journal only if value was changed.
834            self.journal
835                .push(ENTRY::transient_storage_changed(address, key, had_value));
836        }
837    }
838
839    /// Pushes log into subroutine.
840    #[inline]
841    pub fn log(&mut self, log: Log) {
842        self.logs.push(log);
843    }
844}
845
846/// Loads storage slot with account.
847#[inline]
848pub fn sload_with_account<DB: Database, ENTRY: JournalEntryTr>(
849    account: &mut Account,
850    db: &mut DB,
851    journal: &mut Vec<ENTRY>,
852    transaction_id: usize,
853    address: Address,
854    key: StorageKey,
855) -> Result<StateLoad<StorageValue>, DB::Error> {
856    let is_newly_created = account.is_created();
857    let (value, is_cold) = match account.storage.entry(key) {
858        Entry::Occupied(occ) => {
859            let slot = occ.into_mut();
860            let is_cold = slot.mark_warm_with_transaction_id(transaction_id);
861            (slot.present_value, is_cold)
862        }
863        Entry::Vacant(vac) => {
864            // if storage was cleared, we don't need to ping db.
865            let value = if is_newly_created {
866                StorageValue::ZERO
867            } else {
868                db.storage(address, key)?
869            };
870
871            vac.insert(EvmStorageSlot::new(value, transaction_id));
872
873            (value, true)
874        }
875    };
876
877    if is_cold {
878        // add it to journal as cold loaded.
879        journal.push(ENTRY::storage_warmed(address, key));
880    }
881
882    Ok(StateLoad::new(value, is_cold))
883}
884
885fn reset_preloaded_addresses(
886    warm_preloaded_addresses: &mut HashSet<Address>,
887    precompiles: &HashSet<Address>,
888) {
889    // `warm_preloaded_addresses` is append-only, and is initialized with `precompiles`.
890    // Avoid unnecessarily cloning if it hasn't changed.
891    if warm_preloaded_addresses.len() == precompiles.len() {
892        debug_assert_eq!(warm_preloaded_addresses, precompiles);
893        return;
894    }
895    warm_preloaded_addresses.clone_from(precompiles);
896}