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