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