revm_database/states/
state.rs

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