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