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