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