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