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