Skip to main content

revm_database/states/
state.rs

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