revm_database/states/
state.rs

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