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