Skip to main content

revm_database/states/
state.rs

1use crate::states::block_hash_cache::BlockHashCache;
2
3use super::{
4    bundle_state::BundleRetention, cache::CacheState, plain_account::PlainStorage, BundleState,
5    CacheAccount, StateBuilder, TransitionAccount, TransitionState,
6};
7use bytecode::Bytecode;
8use database_interface::{
9    bal::{BalState, EvmDatabaseError},
10    Database, DatabaseCommit, DatabaseRef, EmptyDB, OnStateHook,
11};
12use primitives::{hash_map, Address, AddressMap, HashMap, StorageKey, StorageValue, B256};
13use state::{
14    bal::{alloy::AlloyBal, Bal, BlockAccessIndex},
15    Account, AccountId, AccountInfo, EvmStorage,
16};
17use std::{borrow::Cow, boxed::Box, sync::Arc};
18
19/// Database boxed with a lifetime and Send
20pub type DBBox<'a, E> = Box<dyn Database<Error = E> + Send + 'a>;
21
22/// More constrained version of State that uses Boxed database with a lifetime
23///
24/// This is used to make it easier to use State.
25pub type StateDBBox<'a, E> = State<DBBox<'a, E>>;
26
27/// State of blockchain
28///
29/// State clear flag is handled by the EVM journal in `finalize()` based on
30/// the spec. The database layer always applies post-EIP-161 commit semantics.
31#[derive(derive_more::Debug)]
32pub struct State<DB> {
33    /// Cached state contains both changed from evm execution and cached/loaded account/storages
34    /// from database
35    ///
36    /// This allows us to have only one layer of cache where we can fetch data.
37    ///
38    /// Additionally, we can introduce some preloading of data from database.
39    pub cache: CacheState,
40    /// Optional database that we use to fetch data from
41    ///
42    /// If database is not present, we will return not existing account and storage.
43    ///
44    /// **Note**: It is marked as Send so database can be shared between threads.
45    pub database: DB,
46    /// Block state, it aggregates transactions transitions into one state
47    ///
48    /// Build reverts and state that gets applied to the state.
49    pub transition_state: Option<TransitionState>,
50    /// After block finishes we merge those changes inside bundle
51    ///
52    /// Bundle is used to update database and create changesets.
53    ///
54    /// Bundle state can be set on initialization if we want to use preloaded bundle.
55    pub bundle_state: BundleState,
56    /// Additional layer that is going to be used to fetch values before fetching values
57    /// from database
58    ///
59    /// Bundle is the main output of the state execution and this allows setting previous bundle
60    /// and using its values for execution.
61    pub use_preloaded_bundle: bool,
62    /// If EVM asks for block hash, we will first check if they are found here,
63    /// then ask the database
64    ///
65    /// This map can be used to give different values for block hashes if in case.
66    ///
67    /// The fork block is different or some blocks are not saved inside database.
68    pub block_hashes: BlockHashCache,
69    /// BAL state.
70    ///
71    /// Can contain both the BAL for reads and BAL builder that is used to build BAL.
72    pub bal_state: BalState,
73    /// Hook invoked whenever state changes are committed.
74    #[debug(skip)]
75    pub state_hook: Option<Box<dyn OnStateHook>>,
76}
77
78// Have ability to call State::builder without having to specify the type.
79impl State<EmptyDB> {
80    /// Return the builder that build the State.
81    pub fn builder() -> StateBuilder<EmptyDB> {
82        StateBuilder::default()
83    }
84}
85
86impl<DB: Database> State<DB> {
87    /// Returns the size hint for the inner bundle state.
88    ///
89    /// See [BundleState::size_hint] for more info.
90    pub fn bundle_size_hint(&self) -> usize {
91        self.bundle_state.size_hint()
92    }
93
94    /// Inserts a non-existing account into the state.
95    pub fn insert_not_existing(&mut self, address: Address) {
96        self.cache.insert_not_existing(address)
97    }
98
99    /// Inserts an account into the state.
100    pub fn insert_account(&mut self, address: Address, info: AccountInfo) {
101        self.cache.insert_account(address, info)
102    }
103
104    /// Inserts an account with storage into the state.
105    pub fn insert_account_with_storage(
106        &mut self,
107        address: Address,
108        info: AccountInfo,
109        storage: PlainStorage,
110    ) {
111        self.cache
112            .insert_account_with_storage(address, info, storage)
113    }
114
115    /// Applies evm transitions to transition state.
116    pub fn apply_transition<'a>(
117        &mut self,
118        transitions: impl IntoIterator<Item = (Address, TransitionAccount<Option<Cow<'a, EvmStorage>>>)>,
119    ) {
120        // Add transition to transition state.
121        if let Some(s) = self.transition_state.as_mut() {
122            s.add_transitions(transitions)
123        }
124    }
125
126    /// Take all transitions and merge them inside bundle state.
127    ///
128    /// This action will create final post state and all reverts so that
129    /// we at any time revert state of bundle to the state before transition
130    /// is applied.
131    pub fn merge_transitions(&mut self, retention: BundleRetention) {
132        if let Some(transition_state) = self.transition_state.as_mut().map(TransitionState::take) {
133            self.bundle_state
134                .apply_transitions_and_create_reverts(transition_state, retention);
135        }
136    }
137
138    /// Get a mutable reference to the [`CacheAccount`] for the given address.
139    ///
140    /// If the account is not found in the cache, it will be loaded from the
141    /// database and inserted into the cache.
142    pub fn load_cache_account(&mut self, address: Address) -> Result<&mut CacheAccount, DB::Error> {
143        Self::load_cache_account_with(
144            &mut self.cache,
145            self.use_preloaded_bundle,
146            &self.bundle_state,
147            &mut self.database,
148            address,
149        )
150    }
151
152    /// Get a mutable reference to the [`CacheAccount`] for the given address.
153    ///
154    /// If the account is not found in the cache, it will be loaded from the
155    /// database and inserted into the cache.
156    ///
157    /// This function accepts destructed fields of [`Self`] as arguments and
158    /// returns a cached account with the lifetime of the provided cache reference.
159    fn load_cache_account_with<'a>(
160        cache: &'a mut CacheState,
161        use_preloaded_bundle: bool,
162        bundle_state: &BundleState,
163        database: &mut DB,
164        address: Address,
165    ) -> Result<&'a mut CacheAccount, DB::Error> {
166        Ok(match cache.accounts.entry(address) {
167            hash_map::Entry::Vacant(entry) => {
168                if use_preloaded_bundle {
169                    // Load account from bundle state
170                    if let Some(account) = bundle_state.account(&address).map(Into::into) {
171                        return Ok(entry.insert(account));
172                    }
173                }
174                // If not found in bundle, load it from database
175                let info = database.basic(address)?;
176                let account = match info {
177                    None => CacheAccount::new_loaded_not_existing(),
178                    Some(acc) if acc.is_empty() => {
179                        CacheAccount::new_loaded_empty_eip161(HashMap::default())
180                    }
181                    Some(acc) => CacheAccount::new_loaded(acc, HashMap::default()),
182                };
183                entry.insert(account)
184            }
185            hash_map::Entry::Occupied(entry) => entry.into_mut(),
186        })
187    }
188
189    // TODO : Make cache aware of transitions dropping by having global transition counter.
190    /// Takes the [`BundleState`] changeset from the [`State`], replacing it
191    /// with an empty one.
192    ///
193    /// This will not apply any pending [`TransitionState`].
194    ///
195    /// It is recommended to call [`State::merge_transitions`] before taking the bundle.
196    ///
197    /// If the `State` has been built with the
198    /// [`StateBuilder::with_bundle_prestate`] option, the pre-state will be
199    /// taken along with any changes made by [`State::merge_transitions`].
200    pub fn take_bundle(&mut self) -> BundleState {
201        core::mem::take(&mut self.bundle_state)
202    }
203
204    /// Takes build bal from bal state.
205    #[inline]
206    pub const fn take_built_bal(&mut self) -> Option<Bal> {
207        self.bal_state.take_built_bal()
208    }
209
210    /// Takes built alloy bal from bal state.
211    #[inline]
212    pub fn take_built_alloy_bal(&mut self) -> Option<AlloyBal> {
213        self.bal_state.take_built_alloy_bal()
214    }
215
216    /// Bump BAL index.
217    #[inline]
218    pub const fn bump_bal_index(&mut self) {
219        self.bal_state.bump_bal_index();
220    }
221
222    /// Set BAL index.
223    #[inline]
224    pub const fn set_bal_index(&mut self, index: BlockAccessIndex) {
225        self.bal_state.bal_index = index;
226    }
227
228    /// Reset BAL index.
229    #[inline]
230    pub const fn reset_bal_index(&mut self) {
231        self.bal_state.reset_bal_index();
232    }
233
234    /// Set BAL.
235    #[inline]
236    pub fn set_bal(&mut self, bal: Option<Arc<Bal>>) {
237        self.bal_state.bal = bal;
238    }
239
240    /// Set whether reads not covered by the BAL fall back to the underlying database.
241    ///
242    /// See [`BalState::allow_db_fallback`](database_interface::bal::BalState).
243    #[inline]
244    pub const fn set_allow_bal_db_fallback(&mut self, allow: bool) {
245        self.bal_state.allow_db_fallback = allow;
246    }
247
248    /// Sets the hook invoked whenever state changes are committed.
249    #[inline]
250    pub fn set_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>) {
251        self.state_hook = hook;
252    }
253
254    /// Sets the hook invoked whenever state changes are committed.
255    #[inline]
256    #[must_use]
257    pub fn with_state_hook(mut self, hook: Option<Box<dyn OnStateHook>>) -> Self {
258        self.set_state_hook(hook);
259        self
260    }
261
262    /// Returns whether the state has a BAL configured.
263    #[inline]
264    pub const fn has_bal(&self) -> bool {
265        self.bal_state.bal.is_some()
266    }
267
268    /// Gets storage value of address at index.
269    #[inline]
270    fn storage(&mut self, address: Address, index: StorageKey) -> Result<StorageValue, DB::Error> {
271        // If account is not found in cache, it will be loaded from database.
272        let account = Self::load_cache_account_with(
273            &mut self.cache,
274            self.use_preloaded_bundle,
275            &self.bundle_state,
276            &mut self.database,
277            address,
278        )?;
279
280        // Account will always be some, but if it is not, StorageValue::ZERO will be returned.
281        let is_storage_known = account.status.is_storage_known();
282        Ok(account
283            .account
284            .as_mut()
285            .map(|account| match account.storage.entry(index) {
286                hash_map::Entry::Occupied(entry) => Ok(*entry.get()),
287                hash_map::Entry::Vacant(entry) => {
288                    // If account was destroyed or account is newly built
289                    // we return zero and don't ask database.
290                    let value = if is_storage_known {
291                        StorageValue::ZERO
292                    } else {
293                        self.database.storage(address, index)?
294                    };
295                    entry.insert(value);
296                    Ok(value)
297                }
298            })
299            .transpose()?
300            .unwrap_or_default())
301    }
302}
303
304impl<DB: Database> Database for State<DB> {
305    type Error = EvmDatabaseError<DB::Error>;
306
307    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
308        // if bal is existing but account is not found, error will be returned.
309        let account_id = self
310            .bal_state
311            .get_account_id(&address)
312            .map_err(EvmDatabaseError::Bal)?;
313
314        let mut basic = self
315            .load_cache_account(address)
316            .map(|a| a.account_info())
317            .map_err(EvmDatabaseError::Database)?;
318        // will populate account code if there was a bal change to it. If there is no change
319        // it will be fetched in code_by_hash.
320        if let Some(account_id) = account_id {
321            self.bal_state
322                .basic_by_account_id(account_id, &mut basic)
323                .map_err(EvmDatabaseError::Bal)?;
324        }
325        Ok(basic)
326    }
327
328    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
329        let res = match self.cache.contracts.entry(code_hash) {
330            hash_map::Entry::Occupied(entry) => Ok(entry.get().clone()),
331            hash_map::Entry::Vacant(entry) => {
332                if self.use_preloaded_bundle {
333                    if let Some(code) = self.bundle_state.contracts.get(&code_hash) {
334                        entry.insert(code.clone());
335                        return Ok(code.clone());
336                    }
337                }
338                // If not found in bundle ask database
339                let code = self
340                    .database
341                    .code_by_hash(code_hash)
342                    .map_err(EvmDatabaseError::Database)?;
343                entry.insert(code.clone());
344                Ok(code)
345            }
346        };
347        res
348    }
349
350    fn storage(
351        &mut self,
352        address: Address,
353        index: StorageKey,
354    ) -> Result<StorageValue, Self::Error> {
355        if let Some(storage) = self
356            .bal_state
357            .storage(&address, index)
358            .map_err(EvmDatabaseError::Bal)?
359        {
360            // return bal value if it is found
361            return Ok(storage);
362        }
363        self.storage(address, index)
364            .map_err(EvmDatabaseError::Database)
365    }
366
367    fn storage_by_account_id(
368        &mut self,
369        address: Address,
370        account_id: AccountId,
371        key: StorageKey,
372    ) -> Result<StorageValue, Self::Error> {
373        if let Some(storage) = self.bal_state.storage_by_account_id(account_id, key)? {
374            return Ok(storage);
375        }
376
377        self.storage(address, key)
378            .map_err(EvmDatabaseError::Database)
379    }
380
381    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
382        // Check cache first
383        if let Some(hash) = self.block_hashes.get(number) {
384            return Ok(hash);
385        }
386
387        // Not in cache, fetch from database
388        let hash = self
389            .database
390            .block_hash(number)
391            .map_err(EvmDatabaseError::Database)?;
392
393        // Insert into cache
394        self.block_hashes.insert(number, hash);
395
396        Ok(hash)
397    }
398}
399
400impl<DB: Database> DatabaseCommit for State<DB> {
401    fn commit(&mut self, changes: AddressMap<Account>) {
402        self.bal_state.commit(&changes);
403
404        if let Some(hook) = self.state_hook.as_mut() {
405            let transitions = self.cache.apply_evm_state_iter(
406                changes
407                    .iter()
408                    .map(|(address, account)| (*address, Cow::Borrowed(account))),
409                |_, _| {},
410            );
411
412            if let Some(s) = self.transition_state.as_mut() {
413                s.add_transitions(transitions)
414            } else {
415                // Advance the iter to apply all state updates.
416                transitions.for_each(|_| {});
417            }
418
419            hook.on_state(changes);
420        } else {
421            let transitions = self.cache.apply_evm_state_iter(
422                changes
423                    .into_iter()
424                    .map(|(address, account)| (address, Cow::Owned(account))),
425                |_, _| {},
426            );
427
428            if let Some(s) = self.transition_state.as_mut() {
429                s.add_transitions(transitions)
430            } else {
431                // Advance the iter to apply all state updates.
432                transitions.for_each(|_| {});
433            }
434        }
435    }
436
437    fn commit_iter(&mut self, changes: &mut dyn Iterator<Item = (Address, Account)>) {
438        if self.state_hook.is_some() {
439            let changes = changes.collect::<AddressMap<_>>();
440            self.commit(changes);
441            return;
442        }
443
444        if let Some(s) = self.transition_state.as_mut() {
445            for (address, account) in changes {
446                self.bal_state.commit_one(address, &account);
447                if let Some(transition) =
448                    self.cache.apply_account_state(address, Cow::Owned(account))
449                {
450                    s.add_transition(address, transition);
451                }
452            }
453        } else {
454            for (address, account) in changes {
455                self.bal_state.commit_one(address, &account);
456                _ = self.cache.apply_account_state(address, Cow::Owned(account));
457            }
458        }
459    }
460}
461
462impl<DB: DatabaseRef> DatabaseRef for State<DB> {
463    type Error = EvmDatabaseError<DB::Error>;
464
465    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
466        // if bal is present and account is not found, error will be returned.
467        let account_id = self.bal_state.get_account_id(&address)?;
468
469        // Account is already in cache
470        let mut loaded_account = None;
471        if let Some(account) = self.cache.accounts.get(&address) {
472            loaded_account = Some(account.account_info());
473        };
474
475        // If bundle state is used, check if account is in bundle state
476        if self.use_preloaded_bundle && loaded_account.is_none() {
477            if let Some(account) = self.bundle_state.account(&address) {
478                loaded_account = Some(account.account_info());
479            }
480        }
481
482        // If not found, load it from database
483        if loaded_account.is_none() {
484            loaded_account = Some(
485                self.database
486                    .basic_ref(address)
487                    .map_err(EvmDatabaseError::Database)?,
488            );
489        }
490
491        // safe to unwrap as it in some in condition above
492        let mut account = loaded_account.unwrap();
493
494        // if it is inside bal, overwrite the account with the bal changes.
495        if let Some(account_id) = account_id {
496            self.bal_state
497                .basic_by_account_id(account_id, &mut account)
498                .map_err(EvmDatabaseError::Bal)?;
499        }
500        Ok(account)
501    }
502
503    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
504        // Check if code is in cache
505        if let Some(code) = self.cache.contracts.get(&code_hash) {
506            return Ok(code.clone());
507        }
508        // If bundle state is used, check if code is in bundle state
509        if self.use_preloaded_bundle {
510            if let Some(code) = self.bundle_state.contracts.get(&code_hash) {
511                return Ok(code.clone());
512            }
513        }
514        // If not found, load it from database
515        self.database
516            .code_by_hash_ref(code_hash)
517            .map_err(EvmDatabaseError::Database)
518    }
519
520    fn storage_ref(
521        &self,
522        address: Address,
523        index: StorageKey,
524    ) -> Result<StorageValue, Self::Error> {
525        // if bal has storage value, return it
526        if let Some(storage) = self.bal_state.storage(&address, index)? {
527            return Ok(storage);
528        }
529
530        // Check if account is in cache, the account is not guaranteed to be loaded
531        if let Some(account) = self.cache.accounts.get(&address) {
532            if let Some(plain_account) = &account.account {
533                // If storage is known, we can return it
534                if let Some(storage_value) = plain_account.storage.get(&index) {
535                    return Ok(*storage_value);
536                }
537                // If account was destroyed or account is newly built
538                // we return zero and don't ask database.
539                if account.status.is_storage_known() {
540                    return Ok(StorageValue::ZERO);
541                }
542            }
543        }
544
545        // If not found, load it from database
546        self.database
547            .storage_ref(address, index)
548            .map_err(EvmDatabaseError::Database)
549    }
550
551    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
552        if let Some(hash) = self.block_hashes.get(number) {
553            return Ok(hash);
554        }
555        // If not found, load it from database
556        self.database
557            .block_hash_ref(number)
558            .map_err(EvmDatabaseError::Database)
559    }
560}
561
562#[cfg(test)]
563mod tests {
564    use super::*;
565    use crate::{
566        states::{reverts::AccountInfoRevert, StorageSlot},
567        AccountRevert, AccountStatus, BundleAccount, RevertToSlot,
568    };
569    use primitives::{keccak256, Bytes, BLOCK_HASH_HISTORY, U256};
570    use state::{EvmStorageSlot, TransactionId};
571
572    fn evm_storage<const N: usize>(
573        slots: [(StorageKey, EvmStorageSlot); N],
574    ) -> Option<Cow<'static, EvmStorage>> {
575        Some(Cow::Owned(HashMap::from_iter(slots)))
576    }
577
578    #[test]
579    fn has_bal_helper() {
580        let state = State::builder().build();
581        assert!(!state.has_bal());
582
583        let state = State::builder().with_bal(Arc::new(Bal::new())).build();
584        assert!(state.has_bal());
585    }
586
587    #[test]
588    fn block_hash_cache() {
589        let mut state = State::builder().build();
590        state.block_hash(1u64).unwrap();
591        state.block_hash(2u64).unwrap();
592
593        let test_number = BLOCK_HASH_HISTORY + 2;
594
595        let block1_hash = keccak256(U256::from(1).to_string().as_bytes());
596        let block2_hash = keccak256(U256::from(2).to_string().as_bytes());
597        let block_test_hash = keccak256(U256::from(test_number).to_string().as_bytes());
598
599        // Verify blocks 1 and 2 are in cache
600        assert_eq!(state.block_hashes.get(1), Some(block1_hash));
601        assert_eq!(state.block_hashes.get(2), Some(block2_hash));
602
603        // Fetch block beyond BLOCK_HASH_HISTORY
604        // Block 258 % 256 = 2, so it will overwrite block 2
605        state.block_hash(test_number).unwrap();
606
607        // Block 2 should be evicted (wrapped around), but block 1 should still be present
608        assert_eq!(state.block_hashes.get(1), Some(block1_hash));
609        assert_eq!(state.block_hashes.get(2), None);
610        assert_eq!(state.block_hashes.get(test_number), Some(block_test_hash));
611    }
612
613    /// Test that block 0 can be correctly fetched and cached.
614    /// This is a regression test for a bug where the cache was initialized with
615    /// `(0, B256::ZERO)` entries, causing block 0 lookups to incorrectly match
616    /// the default entry instead of fetching from the database.
617    #[test]
618    fn block_hash_cache_block_zero() {
619        let mut state = State::builder().build();
620
621        // Block 0 should not be in cache initially
622        assert_eq!(state.block_hashes.get(0), None);
623
624        // Fetch block 0 - this should go to database and cache the result
625        let block0_hash = state.block_hash(0u64).unwrap();
626
627        // EmptyDB returns keccak256("0") for block 0
628        let expected_hash = keccak256(U256::from(0).to_string().as_bytes());
629        assert_eq!(block0_hash, expected_hash);
630
631        // Block 0 should now be in cache with correct value
632        assert_eq!(state.block_hashes.get(0), Some(expected_hash));
633    }
634
635    #[test]
636    fn created_contract_code_is_available_before_transition_merge() {
637        let bytecode = Bytecode::new_raw(Bytes::from_static(&[0x00]));
638        let code_hash = bytecode.hash_slow();
639        let account = Account::default()
640            .with_info(AccountInfo::default().with_code(bytecode.clone()))
641            .with_touched_mark()
642            .with_created_mark();
643        let mut state = State::builder().with_bundle_update().build();
644
645        state.commit(HashMap::from_iter([(Address::ZERO, account)]));
646
647        assert_eq!(state.code_by_hash(code_hash).unwrap(), bytecode);
648    }
649
650    /// Checks that if accounts is touched multiple times in the same block,
651    /// then the old values from the first change are preserved and not overwritten.
652    ///
653    /// This is important because the state transitions from different transactions in the same block may see
654    /// different states of the same account as the old value, but the revert should reflect the
655    /// state of the account before the block.
656    #[test]
657    fn reverts_preserve_old_values() {
658        let mut state = State::builder().with_bundle_update().build();
659
660        let (slot1, slot2, slot3) = (
661            StorageKey::from(1),
662            StorageKey::from(2),
663            StorageKey::from(3),
664        );
665
666        // Non-existing account for testing account state transitions.
667        // [LoadedNotExisting] -> [Changed] (nonce: 1, balance: 1) -> [Changed] (nonce: 2) -> [Changed] (nonce: 3)
668        let new_account_address = Address::from_slice(&[0x1; 20]);
669        let new_account_created_info = AccountInfo {
670            nonce: 1,
671            balance: U256::from(1),
672            ..Default::default()
673        };
674        let new_account_changed_info = AccountInfo {
675            nonce: 2,
676            ..new_account_created_info.clone()
677        };
678        let new_account_changed_info2 = AccountInfo {
679            nonce: 3,
680            ..new_account_changed_info.clone()
681        };
682
683        // Existing account for testing storage state transitions.
684        let existing_account_address = Address::from_slice(&[0x2; 20]);
685        let existing_account_initial_info = AccountInfo {
686            nonce: 1,
687            ..Default::default()
688        };
689        let existing_account_initial_storage = HashMap::<StorageKey, StorageValue>::from_iter([
690            (slot1, StorageValue::from(100)), // 0x01 => 100
691            (slot2, StorageValue::from(200)), // 0x02 => 200
692        ]);
693        let existing_account_changed_info = AccountInfo {
694            nonce: 2,
695            ..existing_account_initial_info.clone()
696        };
697
698        // A transaction in block 1 creates one account and changes an existing one.
699        state.apply_transition(Vec::from([
700            (
701                new_account_address,
702                TransitionAccount {
703                    status: AccountStatus::InMemoryChange,
704                    info: Some(new_account_created_info.clone()),
705                    previous_status: AccountStatus::LoadedNotExisting,
706                    previous_info: None,
707                    storage: None,
708                    ..Default::default()
709                },
710            ),
711            (
712                existing_account_address,
713                TransitionAccount {
714                    status: AccountStatus::InMemoryChange,
715                    info: Some(existing_account_changed_info.clone()),
716                    previous_status: AccountStatus::Loaded,
717                    previous_info: Some(existing_account_initial_info.clone()),
718                    storage: evm_storage([(
719                        slot1,
720                        EvmStorageSlot::new_changed(
721                            *existing_account_initial_storage.get(&slot1).unwrap(),
722                            StorageValue::from(1000),
723                            TransactionId::ZERO,
724                        ),
725                    )]),
726                    storage_was_destroyed: false,
727                },
728            ),
729        ]));
730
731        // A transaction in block 1 then changes the same account.
732        state.apply_transition(Vec::from([(
733            new_account_address,
734            TransitionAccount {
735                status: AccountStatus::InMemoryChange,
736                info: Some(new_account_changed_info.clone()),
737                previous_status: AccountStatus::InMemoryChange,
738                previous_info: Some(new_account_created_info.clone()),
739                ..Default::default()
740            },
741        )]));
742
743        // Another transaction in block 1 then changes the newly created account yet again and modifies the storage in an existing one.
744        state.apply_transition(Vec::from([
745            (
746                new_account_address,
747                TransitionAccount {
748                    status: AccountStatus::InMemoryChange,
749                    info: Some(new_account_changed_info2.clone()),
750                    previous_status: AccountStatus::InMemoryChange,
751                    previous_info: Some(new_account_changed_info),
752                    storage: evm_storage([(
753                        slot1,
754                        EvmStorageSlot::new_changed(
755                            StorageValue::ZERO,
756                            StorageValue::from(1),
757                            TransactionId::ZERO,
758                        ),
759                    )]),
760                    storage_was_destroyed: false,
761                },
762            ),
763            (
764                existing_account_address,
765                TransitionAccount {
766                    status: AccountStatus::InMemoryChange,
767                    info: Some(existing_account_changed_info.clone()),
768                    previous_status: AccountStatus::InMemoryChange,
769                    previous_info: Some(existing_account_changed_info.clone()),
770                    storage: evm_storage([
771                        (
772                            slot1,
773                            EvmStorageSlot::new_changed(
774                                StorageValue::from(100),
775                                StorageValue::from(1_000),
776                                TransactionId::ZERO,
777                            ),
778                        ),
779                        (
780                            slot2,
781                            EvmStorageSlot::new_changed(
782                                *existing_account_initial_storage.get(&slot2).unwrap(),
783                                StorageValue::from(2_000),
784                                TransactionId::ZERO,
785                            ),
786                        ),
787                        // Create new slot
788                        (
789                            slot3,
790                            EvmStorageSlot::new_changed(
791                                StorageValue::ZERO,
792                                StorageValue::from(3_000),
793                                TransactionId::ZERO,
794                            ),
795                        ),
796                    ]),
797                    storage_was_destroyed: false,
798                },
799            ),
800        ]));
801
802        state.merge_transitions(BundleRetention::Reverts);
803        let mut bundle_state = state.take_bundle();
804
805        // The new account revert should be `DeleteIt` since this was an account creation.
806        // The existing account revert should be reverted to its previous state.
807        bundle_state.reverts.sort();
808        assert_eq!(
809            bundle_state.reverts.as_ref(),
810            Vec::from([Vec::from([
811                (
812                    new_account_address,
813                    AccountRevert {
814                        account: AccountInfoRevert::DeleteIt,
815                        previous_status: AccountStatus::LoadedNotExisting,
816                        storage: HashMap::from_iter([(
817                            slot1,
818                            RevertToSlot::Some(StorageValue::ZERO)
819                        )]),
820                        wipe_storage: false,
821                    }
822                ),
823                (
824                    existing_account_address,
825                    AccountRevert {
826                        account: AccountInfoRevert::RevertTo(existing_account_initial_info.clone()),
827                        previous_status: AccountStatus::Loaded,
828                        storage: HashMap::from_iter([
829                            (
830                                slot1,
831                                RevertToSlot::Some(
832                                    *existing_account_initial_storage.get(&slot1).unwrap()
833                                )
834                            ),
835                            (
836                                slot2,
837                                RevertToSlot::Some(
838                                    *existing_account_initial_storage.get(&slot2).unwrap()
839                                )
840                            ),
841                            (slot3, RevertToSlot::Some(StorageValue::ZERO))
842                        ]),
843                        wipe_storage: false,
844                    }
845                ),
846            ])]),
847            "The account or storage reverts are incorrect"
848        );
849
850        // The latest state of the new account should be: nonce = 3, balance = 1, code & code hash = None.
851        // Storage: 0x01 = 1.
852        assert_eq!(
853            bundle_state.account(&new_account_address),
854            Some(&BundleAccount {
855                info: Some(new_account_changed_info2),
856                original_info: None,
857                status: AccountStatus::InMemoryChange,
858                storage: HashMap::from_iter([(
859                    slot1,
860                    StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(1))
861                )]),
862            }),
863            "The latest state of the new account is incorrect"
864        );
865
866        // The latest state of the existing account should be: nonce = 2.
867        // Storage: 0x01 = 1000, 0x02 = 2000, 0x03 = 3000.
868        assert_eq!(
869            bundle_state.account(&existing_account_address),
870            Some(&BundleAccount {
871                info: Some(existing_account_changed_info),
872                original_info: Some(existing_account_initial_info),
873                status: AccountStatus::InMemoryChange,
874                storage: HashMap::from_iter([
875                    (
876                        slot1,
877                        StorageSlot::new_changed(
878                            *existing_account_initial_storage.get(&slot1).unwrap(),
879                            StorageValue::from(1_000)
880                        )
881                    ),
882                    (
883                        slot2,
884                        StorageSlot::new_changed(
885                            *existing_account_initial_storage.get(&slot2).unwrap(),
886                            StorageValue::from(2_000)
887                        )
888                    ),
889                    // Create new slot
890                    (
891                        slot3,
892                        StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(3_000))
893                    ),
894                ]),
895            }),
896            "The latest state of the existing account is incorrect"
897        );
898    }
899
900    /// Checks that the accounts and storages that are changed within the
901    /// block and reverted to their previous state do not appear in the reverts.
902    #[test]
903    fn bundle_scoped_reverts_collapse() {
904        let mut state = State::builder().with_bundle_update().build();
905
906        // Non-existing account.
907        let new_account_address = Address::from_slice(&[0x1; 20]);
908        let new_account_created_info = AccountInfo {
909            nonce: 1,
910            balance: U256::from(1),
911            ..Default::default()
912        };
913
914        // Existing account.
915        let existing_account_address = Address::from_slice(&[0x2; 20]);
916        let existing_account_initial_info = AccountInfo {
917            nonce: 1,
918            ..Default::default()
919        };
920        let existing_account_updated_info = AccountInfo {
921            nonce: 1,
922            balance: U256::from(1),
923            ..Default::default()
924        };
925
926        // Existing account with storage.
927        let (slot1, slot2) = (StorageKey::from(1), StorageKey::from(2));
928        let existing_account_with_storage_address = Address::from_slice(&[0x3; 20]);
929        let existing_account_with_storage_info = AccountInfo {
930            nonce: 1,
931            ..Default::default()
932        };
933        // A transaction in block 1 creates a new account.
934        state.apply_transition(Vec::from([
935            (
936                new_account_address,
937                TransitionAccount {
938                    status: AccountStatus::InMemoryChange,
939                    info: Some(new_account_created_info.clone()),
940                    previous_status: AccountStatus::LoadedNotExisting,
941                    previous_info: None,
942                    ..Default::default()
943                },
944            ),
945            (
946                existing_account_address,
947                TransitionAccount {
948                    status: AccountStatus::Changed,
949                    info: Some(existing_account_updated_info.clone()),
950                    previous_status: AccountStatus::Loaded,
951                    previous_info: Some(existing_account_initial_info.clone()),
952                    ..Default::default()
953                },
954            ),
955            (
956                existing_account_with_storage_address,
957                TransitionAccount {
958                    status: AccountStatus::Changed,
959                    info: Some(existing_account_with_storage_info.clone()),
960                    previous_status: AccountStatus::Loaded,
961                    previous_info: Some(existing_account_with_storage_info.clone()),
962                    storage: evm_storage([
963                        (
964                            slot1,
965                            EvmStorageSlot::new_changed(
966                                StorageValue::from(1),
967                                StorageValue::from(10),
968                                TransactionId::ZERO,
969                            ),
970                        ),
971                        (
972                            slot2,
973                            EvmStorageSlot::new_changed(
974                                StorageValue::ZERO,
975                                StorageValue::from(20),
976                                TransactionId::ZERO,
977                            ),
978                        ),
979                    ]),
980                    storage_was_destroyed: false,
981                },
982            ),
983        ]));
984
985        // Another transaction in block 1 destroys new account.
986        state.apply_transition(Vec::from([
987            (
988                new_account_address,
989                TransitionAccount {
990                    status: AccountStatus::Destroyed,
991                    info: None,
992                    previous_status: AccountStatus::InMemoryChange,
993                    previous_info: Some(new_account_created_info),
994                    ..Default::default()
995                },
996            ),
997            (
998                existing_account_address,
999                TransitionAccount {
1000                    status: AccountStatus::Changed,
1001                    info: Some(existing_account_initial_info),
1002                    previous_status: AccountStatus::Changed,
1003                    previous_info: Some(existing_account_updated_info),
1004                    ..Default::default()
1005                },
1006            ),
1007            (
1008                existing_account_with_storage_address,
1009                TransitionAccount {
1010                    status: AccountStatus::Changed,
1011                    info: Some(existing_account_with_storage_info.clone()),
1012                    previous_status: AccountStatus::Changed,
1013                    previous_info: Some(existing_account_with_storage_info.clone()),
1014                    storage: evm_storage([
1015                        (
1016                            slot1,
1017                            EvmStorageSlot::new_changed(
1018                                StorageValue::from(10),
1019                                StorageValue::from(1),
1020                                TransactionId::ZERO,
1021                            ),
1022                        ),
1023                        (
1024                            slot2,
1025                            EvmStorageSlot::new_changed(
1026                                StorageValue::from(20),
1027                                StorageValue::ZERO,
1028                                TransactionId::ZERO,
1029                            ),
1030                        ),
1031                    ]),
1032                    storage_was_destroyed: false,
1033                },
1034            ),
1035        ]));
1036
1037        state.merge_transitions(BundleRetention::Reverts);
1038
1039        let mut bundle_state = state.take_bundle();
1040        bundle_state.reverts.sort();
1041
1042        // both account info and storage are left as before transitions,
1043        // therefore there is nothing to revert
1044        assert_eq!(bundle_state.reverts.as_ref(), Vec::from([Vec::from([])]));
1045    }
1046
1047    /// Checks that the behavior of selfdestruct within the block is correct.
1048    #[test]
1049    fn selfdestruct_state_and_reverts() {
1050        let mut state = State::builder().with_bundle_update().build();
1051
1052        // Existing account.
1053        let existing_account_address = Address::from_slice(&[0x1; 20]);
1054        let existing_account_info = AccountInfo {
1055            nonce: 1,
1056            ..Default::default()
1057        };
1058
1059        let (slot1, slot2) = (StorageKey::from(1), StorageKey::from(2));
1060
1061        // Existing account is destroyed.
1062        state.apply_transition(Vec::from([(
1063            existing_account_address,
1064            TransitionAccount {
1065                status: AccountStatus::Destroyed,
1066                info: None,
1067                previous_status: AccountStatus::Loaded,
1068                previous_info: Some(existing_account_info.clone()),
1069                storage: Some(Cow::Owned(HashMap::default())),
1070                storage_was_destroyed: true,
1071            },
1072        )]));
1073
1074        // Existing account is re-created and slot 0x01 is changed.
1075        state.apply_transition(Vec::from([(
1076            existing_account_address,
1077            TransitionAccount {
1078                status: AccountStatus::DestroyedChanged,
1079                info: Some(existing_account_info.clone()),
1080                previous_status: AccountStatus::Destroyed,
1081                previous_info: None,
1082                storage: evm_storage([(
1083                    slot1,
1084                    EvmStorageSlot::new_changed(
1085                        StorageValue::ZERO,
1086                        StorageValue::from(1),
1087                        TransactionId::ZERO,
1088                    ),
1089                )]),
1090                storage_was_destroyed: false,
1091            },
1092        )]));
1093
1094        // Slot 0x01 is changed, but existing account is destroyed again.
1095        state.apply_transition(Vec::from([(
1096            existing_account_address,
1097            TransitionAccount {
1098                status: AccountStatus::DestroyedAgain,
1099                info: None,
1100                previous_status: AccountStatus::DestroyedChanged,
1101                previous_info: Some(existing_account_info.clone()),
1102                // storage change should be ignored
1103                storage: Some(Cow::Owned(HashMap::default())),
1104                storage_was_destroyed: true,
1105            },
1106        )]));
1107
1108        // Existing account is re-created and slot 0x02 is changed.
1109        state.apply_transition(Vec::from([(
1110            existing_account_address,
1111            TransitionAccount {
1112                status: AccountStatus::DestroyedChanged,
1113                info: Some(existing_account_info.clone()),
1114                previous_status: AccountStatus::DestroyedAgain,
1115                previous_info: None,
1116                storage: evm_storage([(
1117                    slot2,
1118                    EvmStorageSlot::new_changed(
1119                        StorageValue::ZERO,
1120                        StorageValue::from(2),
1121                        TransactionId::ZERO,
1122                    ),
1123                )]),
1124                storage_was_destroyed: false,
1125            },
1126        )]));
1127
1128        state.merge_transitions(BundleRetention::Reverts);
1129
1130        let bundle_state = state.take_bundle();
1131
1132        assert_eq!(
1133            bundle_state.state,
1134            HashMap::from_iter([(
1135                existing_account_address,
1136                BundleAccount {
1137                    info: Some(existing_account_info.clone()),
1138                    original_info: Some(existing_account_info.clone()),
1139                    storage: HashMap::from_iter([(
1140                        slot2,
1141                        StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(2))
1142                    )]),
1143                    status: AccountStatus::DestroyedChanged,
1144                }
1145            )])
1146        );
1147
1148        assert_eq!(
1149            bundle_state.reverts.as_ref(),
1150            Vec::from([Vec::from([(
1151                existing_account_address,
1152                AccountRevert {
1153                    account: AccountInfoRevert::DoNothing,
1154                    previous_status: AccountStatus::Loaded,
1155                    storage: HashMap::from_iter([(slot2, RevertToSlot::Destroyed)]),
1156                    wipe_storage: true,
1157                }
1158            )])])
1159        )
1160    }
1161}