revm/db/states/
state.rs

1use super::{
2    bundle_state::BundleRetention, cache::CacheState, plain_account::PlainStorage, BundleState,
3    CacheAccount, StateBuilder, TransitionAccount, TransitionState,
4};
5use crate::db::EmptyDB;
6use revm_interpreter::primitives::{
7    db::{Database, DatabaseCommit},
8    hash_map, Account, AccountInfo, Address, Bytecode, HashMap, B256, BLOCK_HASH_HISTORY, U256,
9};
10use std::{
11    boxed::Box,
12    collections::{btree_map, BTreeMap},
13    vec::Vec,
14};
15
16/// Database boxed with a lifetime and Send.
17pub type DBBox<'a, E> = Box<dyn Database<Error = E> + Send + 'a>;
18
19/// More constrained version of State that uses Boxed database with a lifetime.
20///
21/// This is used to make it easier to use State.
22pub type StateDBBox<'a, E> = State<DBBox<'a, E>>;
23
24/// State of blockchain.
25///
26/// State clear flag is set inside CacheState and by default it is enabled.
27/// If you want to disable it use `set_state_clear_flag` function.
28#[derive(Debug)]
29pub struct State<DB> {
30    /// Cached state contains both changed from evm execution and cached/loaded account/storages
31    /// from database. This allows us to have only one layer of cache where we can fetch data.
32    /// Additionally we can introduce some preloading of data from database.
33    pub cache: CacheState,
34    /// Optional database that we use to fetch data from. If database is not present, we will
35    /// return not existing account and storage.
36    ///
37    /// Note: It is marked as Send so database can be shared between threads.
38    pub database: DB,
39    /// Block state, it aggregates transactions transitions into one state.
40    ///
41    /// Build reverts and state that gets applied to the state.
42    pub transition_state: Option<TransitionState>,
43    /// After block is finishes we merge those changes inside bundle.
44    /// Bundle is used to update database and create changesets.
45    /// Bundle state can be set on initialization if we want to use preloaded bundle.
46    pub bundle_state: BundleState,
47    /// Addition layer that is going to be used to fetched values before fetching values
48    /// from database.
49    ///
50    /// Bundle is the main output of the state execution and this allows setting previous bundle
51    /// and using its values for execution.
52    pub use_preloaded_bundle: bool,
53    /// If EVM asks for block hash we will first check if they are found here.
54    /// and then ask the database.
55    ///
56    /// This map can be used to give different values for block hashes if in case
57    /// The fork block is different or some blocks are not saved inside database.
58    pub block_hashes: BTreeMap<u64, B256>,
59}
60
61// Have ability to call State::builder without having to specify the type.
62impl State<EmptyDB> {
63    /// Return the builder that build the State.
64    pub fn builder() -> StateBuilder<EmptyDB> {
65        StateBuilder::default()
66    }
67}
68
69impl<DB: Database> State<DB> {
70    /// Returns the size hint for the inner bundle state.
71    /// See [BundleState::size_hint] for more info.
72    pub fn bundle_size_hint(&self) -> usize {
73        self.bundle_state.size_hint()
74    }
75
76    /// Iterate over received balances and increment all account balances.
77    /// If account is not found inside cache state it will be loaded from database.
78    ///
79    /// Update will create transitions for all accounts that are updated.
80    ///
81    /// Like [CacheAccount::increment_balance], this assumes that incremented balances are not
82    /// zero, and will not overflow once incremented. If using this to implement withdrawals, zero
83    /// balances must be filtered out before calling this function.
84    pub fn increment_balances(
85        &mut self,
86        balances: impl IntoIterator<Item = (Address, u128)>,
87    ) -> Result<(), DB::Error> {
88        // make transition and update cache state
89        let mut transitions = Vec::new();
90        for (address, balance) in balances {
91            if balance == 0 {
92                continue;
93            }
94            let original_account = self.load_cache_account(address)?;
95            transitions.push((
96                address,
97                original_account
98                    .increment_balance(balance)
99                    .expect("Balance is not zero"),
100            ))
101        }
102        // append transition
103        if let Some(s) = self.transition_state.as_mut() {
104            s.add_transitions(transitions)
105        }
106        Ok(())
107    }
108
109    /// Drain balances from given account and return those values.
110    ///
111    /// It is used for DAO hardfork state change to move values from given accounts.
112    pub fn drain_balances(
113        &mut self,
114        addresses: impl IntoIterator<Item = Address>,
115    ) -> Result<Vec<u128>, DB::Error> {
116        // make transition and update cache state
117        let mut transitions = Vec::new();
118        let mut balances = Vec::new();
119        for address in addresses {
120            let original_account = self.load_cache_account(address)?;
121            let (balance, transition) = original_account.drain_balance();
122            balances.push(balance);
123            transitions.push((address, transition))
124        }
125        // append transition
126        if let Some(s) = self.transition_state.as_mut() {
127            s.add_transitions(transitions)
128        }
129        Ok(balances)
130    }
131
132    /// State clear EIP-161 is enabled in Spurious Dragon hardfork.
133    pub fn set_state_clear_flag(&mut self, has_state_clear: bool) {
134        self.cache.set_state_clear_flag(has_state_clear);
135    }
136
137    pub fn insert_not_existing(&mut self, address: Address) {
138        self.cache.insert_not_existing(address)
139    }
140
141    pub fn insert_account(&mut self, address: Address, info: AccountInfo) {
142        self.cache.insert_account(address, info)
143    }
144
145    pub fn insert_account_with_storage(
146        &mut self,
147        address: Address,
148        info: AccountInfo,
149        storage: PlainStorage,
150    ) {
151        self.cache
152            .insert_account_with_storage(address, info, storage)
153    }
154
155    /// Apply evm transitions to transition state.
156    pub fn apply_transition(&mut self, transitions: Vec<(Address, TransitionAccount)>) {
157        // add transition to transition state.
158        if let Some(s) = self.transition_state.as_mut() {
159            s.add_transitions(transitions)
160        }
161    }
162
163    /// Take all transitions and merge them inside bundle state.
164    /// This action will create final post state and all reverts so that
165    /// we at any time revert state of bundle to the state before transition
166    /// is applied.
167    pub fn merge_transitions(&mut self, retention: BundleRetention) {
168        if let Some(transition_state) = self.transition_state.as_mut().map(TransitionState::take) {
169            self.bundle_state
170                .apply_transitions_and_create_reverts(transition_state, retention);
171        }
172    }
173
174    /// Get a mutable reference to the [`CacheAccount`] for the given address.
175    /// If the account is not found in the cache, it will be loaded from the
176    /// database and inserted into the cache.
177    pub fn load_cache_account(&mut self, address: Address) -> Result<&mut CacheAccount, DB::Error> {
178        match self.cache.accounts.entry(address) {
179            hash_map::Entry::Vacant(entry) => {
180                if self.use_preloaded_bundle {
181                    // load account from bundle state
182                    if let Some(account) =
183                        self.bundle_state.account(&address).cloned().map(Into::into)
184                    {
185                        return Ok(entry.insert(account));
186                    }
187                }
188                // if not found in bundle, load it from database
189                let info = self.database.basic(address)?;
190                let account = match info {
191                    None => CacheAccount::new_loaded_not_existing(),
192                    Some(acc) if acc.is_empty() => {
193                        CacheAccount::new_loaded_empty_eip161(HashMap::default())
194                    }
195                    Some(acc) => CacheAccount::new_loaded(acc, HashMap::default()),
196                };
197                Ok(entry.insert(account))
198            }
199            hash_map::Entry::Occupied(entry) => Ok(entry.into_mut()),
200        }
201    }
202
203    // TODO make cache aware of transitions dropping by having global transition counter.
204    /// Takes the [`BundleState`] changeset from the [`State`], replacing it
205    /// with an empty one.
206    ///
207    /// This will not apply any pending [`TransitionState`]. It is recommended
208    /// to call [`State::merge_transitions`] before taking the bundle.
209    ///
210    /// If the `State` has been built with the
211    /// [`StateBuilder::with_bundle_prestate`] option, the pre-state will be
212    /// taken along with any changes made by [`State::merge_transitions`].
213    pub fn take_bundle(&mut self) -> BundleState {
214        core::mem::take(&mut self.bundle_state)
215    }
216}
217
218impl<DB: Database> Database for State<DB> {
219    type Error = DB::Error;
220
221    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
222        self.load_cache_account(address).map(|a| a.account_info())
223    }
224
225    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
226        let res = match self.cache.contracts.entry(code_hash) {
227            hash_map::Entry::Occupied(entry) => Ok(entry.get().clone()),
228            hash_map::Entry::Vacant(entry) => {
229                if self.use_preloaded_bundle {
230                    if let Some(code) = self.bundle_state.contracts.get(&code_hash) {
231                        entry.insert(code.clone());
232                        return Ok(code.clone());
233                    }
234                }
235                // if not found in bundle ask database
236                let code = self.database.code_by_hash(code_hash)?;
237                entry.insert(code.clone());
238                Ok(code)
239            }
240        };
241        res
242    }
243
244    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
245        // Account is guaranteed to be loaded.
246        // Note that storage from bundle is already loaded with account.
247        if let Some(account) = self.cache.accounts.get_mut(&address) {
248            // account will always be some, but if it is not, U256::ZERO will be returned.
249            let is_storage_known = account.status.is_storage_known();
250            Ok(account
251                .account
252                .as_mut()
253                .map(|account| match account.storage.entry(index) {
254                    hash_map::Entry::Occupied(entry) => Ok(*entry.get()),
255                    hash_map::Entry::Vacant(entry) => {
256                        // if account was destroyed or account is newly built
257                        // we return zero and don't ask database.
258                        let value = if is_storage_known {
259                            U256::ZERO
260                        } else {
261                            self.database.storage(address, index)?
262                        };
263                        entry.insert(value);
264                        Ok(value)
265                    }
266                })
267                .transpose()?
268                .unwrap_or_default())
269        } else {
270            unreachable!("For accessing any storage account is guaranteed to be loaded beforehand")
271        }
272    }
273
274    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
275        match self.block_hashes.entry(number) {
276            btree_map::Entry::Occupied(entry) => Ok(*entry.get()),
277            btree_map::Entry::Vacant(entry) => {
278                let ret = *entry.insert(self.database.block_hash(number)?);
279
280                // prune all hashes that are older then BLOCK_HASH_HISTORY
281                let last_block = number.saturating_sub(BLOCK_HASH_HISTORY);
282                while let Some(entry) = self.block_hashes.first_entry() {
283                    if *entry.key() < last_block {
284                        entry.remove();
285                    } else {
286                        break;
287                    }
288                }
289
290                Ok(ret)
291            }
292        }
293    }
294}
295
296impl<DB: Database> DatabaseCommit for State<DB> {
297    fn commit(&mut self, evm_state: HashMap<Address, Account>) {
298        let transitions = self.cache.apply_evm_state(evm_state);
299        self.apply_transition(transitions);
300    }
301}
302
303#[cfg(test)]
304mod tests {
305    use super::*;
306    use crate::db::{
307        states::{reverts::AccountInfoRevert, StorageSlot},
308        AccountRevert, AccountStatus, BundleAccount, RevertToSlot,
309    };
310    use revm_interpreter::primitives::keccak256;
311
312    #[test]
313    fn block_hash_cache() {
314        let mut state = State::builder().build();
315        state.block_hash(1u64).unwrap();
316        state.block_hash(2u64).unwrap();
317
318        let test_number = BLOCK_HASH_HISTORY + 2;
319
320        let block1_hash = keccak256(U256::from(1).to_string().as_bytes());
321        let block2_hash = keccak256(U256::from(2).to_string().as_bytes());
322        let block_test_hash = keccak256(U256::from(test_number).to_string().as_bytes());
323
324        assert_eq!(
325            state.block_hashes,
326            BTreeMap::from([(1, block1_hash), (2, block2_hash)])
327        );
328
329        state.block_hash(test_number).unwrap();
330        assert_eq!(
331            state.block_hashes,
332            BTreeMap::from([(test_number, block_test_hash), (2, block2_hash)])
333        );
334    }
335
336    /// Checks that if accounts is touched multiple times in the same block,
337    /// then the old values from the first change are preserved and not overwritten.
338    ///
339    /// This is important because the state transitions from different transactions in the same block may see
340    /// different states of the same account as the old value, but the revert should reflect the
341    /// state of the account before the block.
342    #[test]
343    fn reverts_preserve_old_values() {
344        let mut state = State::builder().with_bundle_update().build();
345
346        let (slot1, slot2, slot3) = (U256::from(1), U256::from(2), U256::from(3));
347
348        // Non-existing account for testing account state transitions.
349        // [LoadedNotExisting] -> [Changed] (nonce: 1, balance: 1) -> [Changed] (nonce: 2) -> [Changed] (nonce: 3)
350        let new_account_address = Address::from_slice(&[0x1; 20]);
351        let new_account_created_info = AccountInfo {
352            nonce: 1,
353            balance: U256::from(1),
354            ..Default::default()
355        };
356        let new_account_changed_info = AccountInfo {
357            nonce: 2,
358            ..new_account_created_info.clone()
359        };
360        let new_account_changed_info2 = AccountInfo {
361            nonce: 3,
362            ..new_account_changed_info.clone()
363        };
364
365        // Existing account for testing storage state transitions.
366        let existing_account_address = Address::from_slice(&[0x2; 20]);
367        let existing_account_initial_info = AccountInfo {
368            nonce: 1,
369            ..Default::default()
370        };
371        let existing_account_initial_storage = HashMap::<U256, U256>::from_iter([
372            (slot1, U256::from(100)), // 0x01 => 100
373            (slot2, U256::from(200)), // 0x02 => 200
374        ]);
375        let existing_account_changed_info = AccountInfo {
376            nonce: 2,
377            ..existing_account_initial_info.clone()
378        };
379
380        // A transaction in block 1 creates one account and changes an existing one.
381        state.apply_transition(Vec::from([
382            (
383                new_account_address,
384                TransitionAccount {
385                    status: AccountStatus::InMemoryChange,
386                    info: Some(new_account_created_info.clone()),
387                    previous_status: AccountStatus::LoadedNotExisting,
388                    previous_info: None,
389                    ..Default::default()
390                },
391            ),
392            (
393                existing_account_address,
394                TransitionAccount {
395                    status: AccountStatus::InMemoryChange,
396                    info: Some(existing_account_changed_info.clone()),
397                    previous_status: AccountStatus::Loaded,
398                    previous_info: Some(existing_account_initial_info.clone()),
399                    storage: HashMap::from_iter([(
400                        slot1,
401                        StorageSlot::new_changed(
402                            *existing_account_initial_storage.get(&slot1).unwrap(),
403                            U256::from(1000),
404                        ),
405                    )]),
406                    storage_was_destroyed: false,
407                },
408            ),
409        ]));
410
411        // A transaction in block 1 then changes the same account.
412        state.apply_transition(Vec::from([(
413            new_account_address,
414            TransitionAccount {
415                status: AccountStatus::InMemoryChange,
416                info: Some(new_account_changed_info.clone()),
417                previous_status: AccountStatus::InMemoryChange,
418                previous_info: Some(new_account_created_info.clone()),
419                ..Default::default()
420            },
421        )]));
422
423        // Another transaction in block 1 then changes the newly created account yet again and modifies the storage in an existing one.
424        state.apply_transition(Vec::from([
425            (
426                new_account_address,
427                TransitionAccount {
428                    status: AccountStatus::InMemoryChange,
429                    info: Some(new_account_changed_info2.clone()),
430                    previous_status: AccountStatus::InMemoryChange,
431                    previous_info: Some(new_account_changed_info),
432                    storage: HashMap::from_iter([(
433                        slot1,
434                        StorageSlot::new_changed(U256::ZERO, U256::from(1)),
435                    )]),
436                    storage_was_destroyed: false,
437                },
438            ),
439            (
440                existing_account_address,
441                TransitionAccount {
442                    status: AccountStatus::InMemoryChange,
443                    info: Some(existing_account_changed_info.clone()),
444                    previous_status: AccountStatus::InMemoryChange,
445                    previous_info: Some(existing_account_changed_info.clone()),
446                    storage: HashMap::from_iter([
447                        (
448                            slot1,
449                            StorageSlot::new_changed(U256::from(100), U256::from(1_000)),
450                        ),
451                        (
452                            slot2,
453                            StorageSlot::new_changed(
454                                *existing_account_initial_storage.get(&slot2).unwrap(),
455                                U256::from(2_000),
456                            ),
457                        ),
458                        // Create new slot
459                        (
460                            slot3,
461                            StorageSlot::new_changed(U256::ZERO, U256::from(3_000)),
462                        ),
463                    ]),
464                    storage_was_destroyed: false,
465                },
466            ),
467        ]));
468
469        state.merge_transitions(BundleRetention::Reverts);
470        let mut bundle_state = state.take_bundle();
471
472        // The new account revert should be `DeleteIt` since this was an account creation.
473        // The existing account revert should be reverted to its previous state.
474        bundle_state.reverts.sort();
475        assert_eq!(
476            bundle_state.reverts.as_ref(),
477            Vec::from([Vec::from([
478                (
479                    new_account_address,
480                    AccountRevert {
481                        account: AccountInfoRevert::DeleteIt,
482                        previous_status: AccountStatus::LoadedNotExisting,
483                        storage: HashMap::from_iter([(slot1, RevertToSlot::Some(U256::ZERO))]),
484                        wipe_storage: false,
485                    }
486                ),
487                (
488                    existing_account_address,
489                    AccountRevert {
490                        account: AccountInfoRevert::RevertTo(existing_account_initial_info.clone()),
491                        previous_status: AccountStatus::Loaded,
492                        storage: HashMap::from_iter([
493                            (
494                                slot1,
495                                RevertToSlot::Some(
496                                    *existing_account_initial_storage.get(&slot1).unwrap()
497                                )
498                            ),
499                            (
500                                slot2,
501                                RevertToSlot::Some(
502                                    *existing_account_initial_storage.get(&slot2).unwrap()
503                                )
504                            ),
505                            (slot3, RevertToSlot::Some(U256::ZERO))
506                        ]),
507                        wipe_storage: false,
508                    }
509                ),
510            ])]),
511            "The account or storage reverts are incorrect"
512        );
513
514        // The latest state of the new account should be: nonce = 3, balance = 1, code & code hash = None.
515        // Storage: 0x01 = 1.
516        assert_eq!(
517            bundle_state.account(&new_account_address),
518            Some(&BundleAccount {
519                info: Some(new_account_changed_info2),
520                original_info: None,
521                status: AccountStatus::InMemoryChange,
522                storage: HashMap::from_iter([(
523                    slot1,
524                    StorageSlot::new_changed(U256::ZERO, U256::from(1))
525                )]),
526            }),
527            "The latest state of the new account is incorrect"
528        );
529
530        // The latest state of the existing account should be: nonce = 2.
531        // Storage: 0x01 = 1000, 0x02 = 2000, 0x03 = 3000.
532        assert_eq!(
533            bundle_state.account(&existing_account_address),
534            Some(&BundleAccount {
535                info: Some(existing_account_changed_info),
536                original_info: Some(existing_account_initial_info),
537                status: AccountStatus::InMemoryChange,
538                storage: HashMap::from_iter([
539                    (
540                        slot1,
541                        StorageSlot::new_changed(
542                            *existing_account_initial_storage.get(&slot1).unwrap(),
543                            U256::from(1_000)
544                        )
545                    ),
546                    (
547                        slot2,
548                        StorageSlot::new_changed(
549                            *existing_account_initial_storage.get(&slot2).unwrap(),
550                            U256::from(2_000)
551                        )
552                    ),
553                    // Create new slot
554                    (
555                        slot3,
556                        StorageSlot::new_changed(U256::ZERO, U256::from(3_000))
557                    ),
558                ]),
559            }),
560            "The latest state of the existing account is incorrect"
561        );
562    }
563
564    /// Checks that the accounts and storages that are changed within the
565    /// block and reverted to their previous state do not appear in the reverts.
566    #[test]
567    fn bundle_scoped_reverts_collapse() {
568        let mut state = State::builder().with_bundle_update().build();
569
570        // Non-existing account.
571        let new_account_address = Address::from_slice(&[0x1; 20]);
572        let new_account_created_info = AccountInfo {
573            nonce: 1,
574            balance: U256::from(1),
575            ..Default::default()
576        };
577
578        // Existing account.
579        let existing_account_address = Address::from_slice(&[0x2; 20]);
580        let existing_account_initial_info = AccountInfo {
581            nonce: 1,
582            ..Default::default()
583        };
584        let existing_account_updated_info = AccountInfo {
585            nonce: 1,
586            balance: U256::from(1),
587            ..Default::default()
588        };
589
590        // Existing account with storage.
591        let (slot1, slot2) = (U256::from(1), U256::from(2));
592        let existing_account_with_storage_address = Address::from_slice(&[0x3; 20]);
593        let existing_account_with_storage_info = AccountInfo {
594            nonce: 1,
595            ..Default::default()
596        };
597        // A transaction in block 1 creates a new account.
598        state.apply_transition(Vec::from([
599            (
600                new_account_address,
601                TransitionAccount {
602                    status: AccountStatus::InMemoryChange,
603                    info: Some(new_account_created_info.clone()),
604                    previous_status: AccountStatus::LoadedNotExisting,
605                    previous_info: None,
606                    ..Default::default()
607                },
608            ),
609            (
610                existing_account_address,
611                TransitionAccount {
612                    status: AccountStatus::Changed,
613                    info: Some(existing_account_updated_info.clone()),
614                    previous_status: AccountStatus::Loaded,
615                    previous_info: Some(existing_account_initial_info.clone()),
616                    ..Default::default()
617                },
618            ),
619            (
620                existing_account_with_storage_address,
621                TransitionAccount {
622                    status: AccountStatus::Changed,
623                    info: Some(existing_account_with_storage_info.clone()),
624                    previous_status: AccountStatus::Loaded,
625                    previous_info: Some(existing_account_with_storage_info.clone()),
626                    storage: HashMap::from_iter([
627                        (
628                            slot1,
629                            StorageSlot::new_changed(U256::from(1), U256::from(10)),
630                        ),
631                        (slot2, StorageSlot::new_changed(U256::ZERO, U256::from(20))),
632                    ]),
633                    storage_was_destroyed: false,
634                },
635            ),
636        ]));
637
638        // Another transaction in block 1 destroys new account.
639        state.apply_transition(Vec::from([
640            (
641                new_account_address,
642                TransitionAccount {
643                    status: AccountStatus::Destroyed,
644                    info: None,
645                    previous_status: AccountStatus::InMemoryChange,
646                    previous_info: Some(new_account_created_info),
647                    ..Default::default()
648                },
649            ),
650            (
651                existing_account_address,
652                TransitionAccount {
653                    status: AccountStatus::Changed,
654                    info: Some(existing_account_initial_info),
655                    previous_status: AccountStatus::Changed,
656                    previous_info: Some(existing_account_updated_info),
657                    ..Default::default()
658                },
659            ),
660            (
661                existing_account_with_storage_address,
662                TransitionAccount {
663                    status: AccountStatus::Changed,
664                    info: Some(existing_account_with_storage_info.clone()),
665                    previous_status: AccountStatus::Changed,
666                    previous_info: Some(existing_account_with_storage_info.clone()),
667                    storage: HashMap::from_iter([
668                        (
669                            slot1,
670                            StorageSlot::new_changed(U256::from(10), U256::from(1)),
671                        ),
672                        (slot2, StorageSlot::new_changed(U256::from(20), U256::ZERO)),
673                    ]),
674                    storage_was_destroyed: false,
675                },
676            ),
677        ]));
678
679        state.merge_transitions(BundleRetention::Reverts);
680
681        let mut bundle_state = state.take_bundle();
682        bundle_state.reverts.sort();
683
684        // both account info and storage are left as before transitions,
685        // therefore there is nothing to revert
686        assert_eq!(bundle_state.reverts.as_ref(), Vec::from([Vec::from([])]));
687    }
688
689    /// Checks that the behavior of selfdestruct within the block is correct.
690    #[test]
691    fn selfdestruct_state_and_reverts() {
692        let mut state = State::builder().with_bundle_update().build();
693
694        // Existing account.
695        let existing_account_address = Address::from_slice(&[0x1; 20]);
696        let existing_account_info = AccountInfo {
697            nonce: 1,
698            ..Default::default()
699        };
700
701        let (slot1, slot2) = (U256::from(1), U256::from(2));
702
703        // Existing account is destroyed.
704        state.apply_transition(Vec::from([(
705            existing_account_address,
706            TransitionAccount {
707                status: AccountStatus::Destroyed,
708                info: None,
709                previous_status: AccountStatus::Loaded,
710                previous_info: Some(existing_account_info.clone()),
711                storage: HashMap::default(),
712                storage_was_destroyed: true,
713            },
714        )]));
715
716        // Existing account is re-created and slot 0x01 is changed.
717        state.apply_transition(Vec::from([(
718            existing_account_address,
719            TransitionAccount {
720                status: AccountStatus::DestroyedChanged,
721                info: Some(existing_account_info.clone()),
722                previous_status: AccountStatus::Destroyed,
723                previous_info: None,
724                storage: HashMap::from_iter([(
725                    slot1,
726                    StorageSlot::new_changed(U256::ZERO, U256::from(1)),
727                )]),
728                storage_was_destroyed: false,
729            },
730        )]));
731
732        // Slot 0x01 is changed, but existing account is destroyed again.
733        state.apply_transition(Vec::from([(
734            existing_account_address,
735            TransitionAccount {
736                status: AccountStatus::DestroyedAgain,
737                info: None,
738                previous_status: AccountStatus::DestroyedChanged,
739                previous_info: Some(existing_account_info.clone()),
740                // storage change should be ignored
741                storage: HashMap::default(),
742                storage_was_destroyed: true,
743            },
744        )]));
745
746        // Existing account is re-created and slot 0x02 is changed.
747        state.apply_transition(Vec::from([(
748            existing_account_address,
749            TransitionAccount {
750                status: AccountStatus::DestroyedChanged,
751                info: Some(existing_account_info.clone()),
752                previous_status: AccountStatus::DestroyedAgain,
753                previous_info: None,
754                storage: HashMap::from_iter([(
755                    slot2,
756                    StorageSlot::new_changed(U256::ZERO, U256::from(2)),
757                )]),
758                storage_was_destroyed: false,
759            },
760        )]));
761
762        state.merge_transitions(BundleRetention::Reverts);
763
764        let bundle_state = state.take_bundle();
765
766        assert_eq!(
767            bundle_state.state,
768            HashMap::from_iter([(
769                existing_account_address,
770                BundleAccount {
771                    info: Some(existing_account_info.clone()),
772                    original_info: Some(existing_account_info.clone()),
773                    storage: HashMap::from_iter([(
774                        slot2,
775                        StorageSlot::new_changed(U256::ZERO, U256::from(2))
776                    )]),
777                    status: AccountStatus::DestroyedChanged,
778                }
779            )])
780        );
781
782        assert_eq!(
783            bundle_state.reverts.as_ref(),
784            Vec::from([Vec::from([(
785                existing_account_address,
786                AccountRevert {
787                    account: AccountInfoRevert::DoNothing,
788                    previous_status: AccountStatus::Loaded,
789                    storage: HashMap::from_iter([(slot2, RevertToSlot::Destroyed)]),
790                    wipe_storage: true,
791                }
792            )])])
793        )
794    }
795}