revm_context/journal/
inner.rs

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