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    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    /// If using this to implement withdrawals, zero balances must be filtered out before calling this function.
91    pub fn increment_balances(
92        &mut self,
93        balances: impl IntoIterator<Item = (Address, u128)>,
94    ) -> Result<(), DB::Error> {
95        // Make transition and update cache state
96        let mut transitions = Vec::new();
97        for (address, balance) in balances {
98            if balance == 0 {
99                continue;
100            }
101            let original_account = self.load_cache_account(address)?;
102            transitions.push((
103                address,
104                original_account
105                    .increment_balance(balance)
106                    .expect("Balance is not zero"),
107            ))
108        }
109        // Append transition
110        if let Some(s) = self.transition_state.as_mut() {
111            s.add_transitions(transitions)
112        }
113        Ok(())
114    }
115
116    /// Drains balances from given account and return those values.
117    ///
118    /// It is used for DAO hardfork state change to move values from given accounts.
119    pub fn drain_balances(
120        &mut self,
121        addresses: impl IntoIterator<Item = Address>,
122    ) -> Result<Vec<u128>, DB::Error> {
123        // Make transition and update cache state
124        let mut transitions = Vec::new();
125        let mut balances = Vec::new();
126        for address in addresses {
127            let original_account = self.load_cache_account(address)?;
128            let (balance, transition) = original_account.drain_balance();
129            balances.push(balance);
130            transitions.push((address, transition))
131        }
132        // Append transition
133        if let Some(s) = self.transition_state.as_mut() {
134            s.add_transitions(transitions)
135        }
136        Ok(balances)
137    }
138
139    /// State clear EIP-161 is enabled in Spurious Dragon hardfork.
140    pub fn set_state_clear_flag(&mut self, has_state_clear: bool) {
141        self.cache.set_state_clear_flag(has_state_clear);
142    }
143
144    /// Inserts a non-existing account into the state.
145    pub fn insert_not_existing(&mut self, address: Address) {
146        self.cache.insert_not_existing(address)
147    }
148
149    /// Inserts an account into the state.
150    pub fn insert_account(&mut self, address: Address, info: AccountInfo) {
151        self.cache.insert_account(address, info)
152    }
153
154    /// Inserts an account with storage into the state.
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) = self.bundle_state.account(&address).map(Into::into) {
195                        return Ok(entry.insert(account));
196                    }
197                }
198                // If not found in bundle, load it from database
199                let info = self.database.basic(address)?;
200                let account = match info {
201                    None => CacheAccount::new_loaded_not_existing(),
202                    Some(acc) if acc.is_empty() => {
203                        CacheAccount::new_loaded_empty_eip161(HashMap::default())
204                    }
205                    Some(acc) => CacheAccount::new_loaded(acc, HashMap::default()),
206                };
207                Ok(entry.insert(account))
208            }
209            hash_map::Entry::Occupied(entry) => Ok(entry.into_mut()),
210        }
211    }
212
213    // TODO : Make cache aware of transitions dropping by having global transition counter.
214    /// Takess the [`BundleState`] changeset from the [`State`], replacing it
215    /// with an empty one.
216    ///
217    /// This will not apply any pending [`TransitionState`].
218    ///
219    /// It is recommended to call [`State::merge_transitions`] before taking the bundle.
220    ///
221    /// If the `State` has been built with the
222    /// [`StateBuilder::with_bundle_prestate`] option, the pre-state will be
223    /// taken along with any changes made by [`State::merge_transitions`].
224    pub fn take_bundle(&mut self) -> BundleState {
225        core::mem::take(&mut self.bundle_state)
226    }
227}
228
229impl<DB: Database> Database for State<DB> {
230    type Error = DB::Error;
231
232    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
233        self.load_cache_account(address).map(|a| a.account_info())
234    }
235
236    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
237        let res = match self.cache.contracts.entry(code_hash) {
238            hash_map::Entry::Occupied(entry) => Ok(entry.get().clone()),
239            hash_map::Entry::Vacant(entry) => {
240                if self.use_preloaded_bundle {
241                    if let Some(code) = self.bundle_state.contracts.get(&code_hash) {
242                        entry.insert(code.clone());
243                        return Ok(code.clone());
244                    }
245                }
246                // If not found in bundle ask database
247                let code = self.database.code_by_hash(code_hash)?;
248                entry.insert(code.clone());
249                Ok(code)
250            }
251        };
252        res
253    }
254
255    fn storage(
256        &mut self,
257        address: Address,
258        index: StorageKey,
259    ) -> Result<StorageValue, Self::Error> {
260        // Account is guaranteed to be loaded.
261        // Note that storage from bundle is already loaded with account.
262        if let Some(account) = self.cache.accounts.get_mut(&address) {
263            // Account will always be some, but if it is not, StorageValue::ZERO will be returned.
264            let is_storage_known = account.status.is_storage_known();
265            Ok(account
266                .account
267                .as_mut()
268                .map(|account| match account.storage.entry(index) {
269                    hash_map::Entry::Occupied(entry) => Ok(*entry.get()),
270                    hash_map::Entry::Vacant(entry) => {
271                        // If account was destroyed or account is newly built
272                        // we return zero and don't ask database.
273                        let value = if is_storage_known {
274                            StorageValue::ZERO
275                        } else {
276                            self.database.storage(address, index)?
277                        };
278                        entry.insert(value);
279                        Ok(value)
280                    }
281                })
282                .transpose()?
283                .unwrap_or_default())
284        } else {
285            unreachable!("For accessing any storage account is guaranteed to be loaded beforehand")
286        }
287    }
288
289    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
290        match self.block_hashes.entry(number) {
291            btree_map::Entry::Occupied(entry) => Ok(*entry.get()),
292            btree_map::Entry::Vacant(entry) => {
293                let ret = *entry.insert(self.database.block_hash(number)?);
294
295                // Prune all hashes that are older than BLOCK_HASH_HISTORY
296                let last_block = number.saturating_sub(BLOCK_HASH_HISTORY);
297                while let Some(entry) = self.block_hashes.first_entry() {
298                    if *entry.key() < last_block {
299                        entry.remove();
300                    } else {
301                        break;
302                    }
303                }
304
305                Ok(ret)
306            }
307        }
308    }
309}
310
311impl<DB: Database> DatabaseCommit for State<DB> {
312    fn commit(&mut self, evm_state: HashMap<Address, Account>) {
313        let transitions = self.cache.apply_evm_state(evm_state);
314        self.apply_transition(transitions);
315    }
316}
317
318impl<DB: DatabaseRef> DatabaseRef for State<DB> {
319    type Error = DB::Error;
320
321    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
322        // Account is already in cache
323        if let Some(account) = self.cache.accounts.get(&address) {
324            return Ok(account.account_info());
325        }
326        // If bundle state is used, check if account is in bundle state
327        if self.use_preloaded_bundle {
328            if let Some(account) = self.bundle_state.account(&address) {
329                return Ok(account.account_info());
330            }
331        }
332        // If not found, load it from database
333        self.database.basic_ref(address)
334    }
335
336    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
337        // Check if code is in cache
338        if let Some(code) = self.cache.contracts.get(&code_hash) {
339            return Ok(code.clone());
340        }
341        // If bundle state is used, check if code is in bundle state
342        if self.use_preloaded_bundle {
343            if let Some(code) = self.bundle_state.contracts.get(&code_hash) {
344                return Ok(code.clone());
345            }
346        }
347        // If not found, load it from database
348        self.database.code_by_hash_ref(code_hash)
349    }
350
351    fn storage_ref(
352        &self,
353        address: Address,
354        index: StorageKey,
355    ) -> Result<StorageValue, Self::Error> {
356        // Check if account is in cache, the account is not guaranteed to be loaded
357        if let Some(account) = self.cache.accounts.get(&address) {
358            if let Some(plain_account) = &account.account {
359                // If storage is known, we can return it
360                if let Some(storage_value) = plain_account.storage.get(&index) {
361                    return Ok(*storage_value);
362                }
363                // If account was destroyed or account is newly built
364                // we return zero and don't ask database.
365                if account.status.is_storage_known() {
366                    return Ok(StorageValue::ZERO);
367                }
368            }
369        }
370        // If not found, load it from database
371        self.database.storage_ref(address, index)
372    }
373
374    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
375        if let Some(entry) = self.block_hashes.get(&number) {
376            return Ok(*entry);
377        }
378        // If not found, load it from database
379        self.database.block_hash_ref(number)
380    }
381}
382
383#[cfg(test)]
384mod tests {
385    use super::*;
386    use crate::{
387        states::{reverts::AccountInfoRevert, StorageSlot},
388        AccountRevert, AccountStatus, BundleAccount, RevertToSlot,
389    };
390    use primitives::{keccak256, U256};
391
392    #[test]
393    fn block_hash_cache() {
394        let mut state = State::builder().build();
395        state.block_hash(1u64).unwrap();
396        state.block_hash(2u64).unwrap();
397
398        let test_number = BLOCK_HASH_HISTORY + 2;
399
400        let block1_hash = keccak256(U256::from(1).to_string().as_bytes());
401        let block2_hash = keccak256(U256::from(2).to_string().as_bytes());
402        let block_test_hash = keccak256(U256::from(test_number).to_string().as_bytes());
403
404        assert_eq!(
405            state.block_hashes,
406            BTreeMap::from([(1, block1_hash), (2, block2_hash)])
407        );
408
409        state.block_hash(test_number).unwrap();
410        assert_eq!(
411            state.block_hashes,
412            BTreeMap::from([(test_number, block_test_hash), (2, block2_hash)])
413        );
414    }
415
416    /// Checks that if accounts is touched multiple times in the same block,
417    /// then the old values from the first change are preserved and not overwritten.
418    ///
419    /// This is important because the state transitions from different transactions in the same block may see
420    /// different states of the same account as the old value, but the revert should reflect the
421    /// state of the account before the block.
422    #[test]
423    fn reverts_preserve_old_values() {
424        let mut state = State::builder().with_bundle_update().build();
425
426        let (slot1, slot2, slot3) = (
427            StorageKey::from(1),
428            StorageKey::from(2),
429            StorageKey::from(3),
430        );
431
432        // Non-existing account for testing account state transitions.
433        // [LoadedNotExisting] -> [Changed] (nonce: 1, balance: 1) -> [Changed] (nonce: 2) -> [Changed] (nonce: 3)
434        let new_account_address = Address::from_slice(&[0x1; 20]);
435        let new_account_created_info = AccountInfo {
436            nonce: 1,
437            balance: U256::from(1),
438            ..Default::default()
439        };
440        let new_account_changed_info = AccountInfo {
441            nonce: 2,
442            ..new_account_created_info.clone()
443        };
444        let new_account_changed_info2 = AccountInfo {
445            nonce: 3,
446            ..new_account_changed_info.clone()
447        };
448
449        // Existing account for testing storage state transitions.
450        let existing_account_address = Address::from_slice(&[0x2; 20]);
451        let existing_account_initial_info = AccountInfo {
452            nonce: 1,
453            ..Default::default()
454        };
455        let existing_account_initial_storage = HashMap::<StorageKey, StorageValue>::from_iter([
456            (slot1, StorageValue::from(100)), // 0x01 => 100
457            (slot2, StorageValue::from(200)), // 0x02 => 200
458        ]);
459        let existing_account_changed_info = AccountInfo {
460            nonce: 2,
461            ..existing_account_initial_info.clone()
462        };
463
464        // A transaction in block 1 creates one account and changes an existing one.
465        state.apply_transition(Vec::from([
466            (
467                new_account_address,
468                TransitionAccount {
469                    status: AccountStatus::InMemoryChange,
470                    info: Some(new_account_created_info.clone()),
471                    previous_status: AccountStatus::LoadedNotExisting,
472                    previous_info: None,
473                    ..Default::default()
474                },
475            ),
476            (
477                existing_account_address,
478                TransitionAccount {
479                    status: AccountStatus::InMemoryChange,
480                    info: Some(existing_account_changed_info.clone()),
481                    previous_status: AccountStatus::Loaded,
482                    previous_info: Some(existing_account_initial_info.clone()),
483                    storage: HashMap::from_iter([(
484                        slot1,
485                        StorageSlot::new_changed(
486                            *existing_account_initial_storage.get(&slot1).unwrap(),
487                            StorageValue::from(1000),
488                        ),
489                    )]),
490                    storage_was_destroyed: false,
491                },
492            ),
493        ]));
494
495        // A transaction in block 1 then changes the same account.
496        state.apply_transition(Vec::from([(
497            new_account_address,
498            TransitionAccount {
499                status: AccountStatus::InMemoryChange,
500                info: Some(new_account_changed_info.clone()),
501                previous_status: AccountStatus::InMemoryChange,
502                previous_info: Some(new_account_created_info.clone()),
503                ..Default::default()
504            },
505        )]));
506
507        // Another transaction in block 1 then changes the newly created account yet again and modifies the storage in an existing one.
508        state.apply_transition(Vec::from([
509            (
510                new_account_address,
511                TransitionAccount {
512                    status: AccountStatus::InMemoryChange,
513                    info: Some(new_account_changed_info2.clone()),
514                    previous_status: AccountStatus::InMemoryChange,
515                    previous_info: Some(new_account_changed_info),
516                    storage: HashMap::from_iter([(
517                        slot1,
518                        StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(1)),
519                    )]),
520                    storage_was_destroyed: false,
521                },
522            ),
523            (
524                existing_account_address,
525                TransitionAccount {
526                    status: AccountStatus::InMemoryChange,
527                    info: Some(existing_account_changed_info.clone()),
528                    previous_status: AccountStatus::InMemoryChange,
529                    previous_info: Some(existing_account_changed_info.clone()),
530                    storage: HashMap::from_iter([
531                        (
532                            slot1,
533                            StorageSlot::new_changed(
534                                StorageValue::from(100),
535                                StorageValue::from(1_000),
536                            ),
537                        ),
538                        (
539                            slot2,
540                            StorageSlot::new_changed(
541                                *existing_account_initial_storage.get(&slot2).unwrap(),
542                                StorageValue::from(2_000),
543                            ),
544                        ),
545                        // Create new slot
546                        (
547                            slot3,
548                            StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(3_000)),
549                        ),
550                    ]),
551                    storage_was_destroyed: false,
552                },
553            ),
554        ]));
555
556        state.merge_transitions(BundleRetention::Reverts);
557        let mut bundle_state = state.take_bundle();
558
559        // The new account revert should be `DeleteIt` since this was an account creation.
560        // The existing account revert should be reverted to its previous state.
561        bundle_state.reverts.sort();
562        assert_eq!(
563            bundle_state.reverts.as_ref(),
564            Vec::from([Vec::from([
565                (
566                    new_account_address,
567                    AccountRevert {
568                        account: AccountInfoRevert::DeleteIt,
569                        previous_status: AccountStatus::LoadedNotExisting,
570                        storage: HashMap::from_iter([(
571                            slot1,
572                            RevertToSlot::Some(StorageValue::ZERO)
573                        )]),
574                        wipe_storage: false,
575                    }
576                ),
577                (
578                    existing_account_address,
579                    AccountRevert {
580                        account: AccountInfoRevert::RevertTo(existing_account_initial_info.clone()),
581                        previous_status: AccountStatus::Loaded,
582                        storage: HashMap::from_iter([
583                            (
584                                slot1,
585                                RevertToSlot::Some(
586                                    *existing_account_initial_storage.get(&slot1).unwrap()
587                                )
588                            ),
589                            (
590                                slot2,
591                                RevertToSlot::Some(
592                                    *existing_account_initial_storage.get(&slot2).unwrap()
593                                )
594                            ),
595                            (slot3, RevertToSlot::Some(StorageValue::ZERO))
596                        ]),
597                        wipe_storage: false,
598                    }
599                ),
600            ])]),
601            "The account or storage reverts are incorrect"
602        );
603
604        // The latest state of the new account should be: nonce = 3, balance = 1, code & code hash = None.
605        // Storage: 0x01 = 1.
606        assert_eq!(
607            bundle_state.account(&new_account_address),
608            Some(&BundleAccount {
609                info: Some(new_account_changed_info2),
610                original_info: None,
611                status: AccountStatus::InMemoryChange,
612                storage: HashMap::from_iter([(
613                    slot1,
614                    StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(1))
615                )]),
616            }),
617            "The latest state of the new account is incorrect"
618        );
619
620        // The latest state of the existing account should be: nonce = 2.
621        // Storage: 0x01 = 1000, 0x02 = 2000, 0x03 = 3000.
622        assert_eq!(
623            bundle_state.account(&existing_account_address),
624            Some(&BundleAccount {
625                info: Some(existing_account_changed_info),
626                original_info: Some(existing_account_initial_info),
627                status: AccountStatus::InMemoryChange,
628                storage: HashMap::from_iter([
629                    (
630                        slot1,
631                        StorageSlot::new_changed(
632                            *existing_account_initial_storage.get(&slot1).unwrap(),
633                            StorageValue::from(1_000)
634                        )
635                    ),
636                    (
637                        slot2,
638                        StorageSlot::new_changed(
639                            *existing_account_initial_storage.get(&slot2).unwrap(),
640                            StorageValue::from(2_000)
641                        )
642                    ),
643                    // Create new slot
644                    (
645                        slot3,
646                        StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(3_000))
647                    ),
648                ]),
649            }),
650            "The latest state of the existing account is incorrect"
651        );
652    }
653
654    /// Checks that the accounts and storages that are changed within the
655    /// block and reverted to their previous state do not appear in the reverts.
656    #[test]
657    fn bundle_scoped_reverts_collapse() {
658        let mut state = State::builder().with_bundle_update().build();
659
660        // Non-existing account.
661        let new_account_address = Address::from_slice(&[0x1; 20]);
662        let new_account_created_info = AccountInfo {
663            nonce: 1,
664            balance: U256::from(1),
665            ..Default::default()
666        };
667
668        // Existing account.
669        let existing_account_address = Address::from_slice(&[0x2; 20]);
670        let existing_account_initial_info = AccountInfo {
671            nonce: 1,
672            ..Default::default()
673        };
674        let existing_account_updated_info = AccountInfo {
675            nonce: 1,
676            balance: U256::from(1),
677            ..Default::default()
678        };
679
680        // Existing account with storage.
681        let (slot1, slot2) = (StorageKey::from(1), StorageKey::from(2));
682        let existing_account_with_storage_address = Address::from_slice(&[0x3; 20]);
683        let existing_account_with_storage_info = AccountInfo {
684            nonce: 1,
685            ..Default::default()
686        };
687        // A transaction in block 1 creates a new account.
688        state.apply_transition(Vec::from([
689            (
690                new_account_address,
691                TransitionAccount {
692                    status: AccountStatus::InMemoryChange,
693                    info: Some(new_account_created_info.clone()),
694                    previous_status: AccountStatus::LoadedNotExisting,
695                    previous_info: None,
696                    ..Default::default()
697                },
698            ),
699            (
700                existing_account_address,
701                TransitionAccount {
702                    status: AccountStatus::Changed,
703                    info: Some(existing_account_updated_info.clone()),
704                    previous_status: AccountStatus::Loaded,
705                    previous_info: Some(existing_account_initial_info.clone()),
706                    ..Default::default()
707                },
708            ),
709            (
710                existing_account_with_storage_address,
711                TransitionAccount {
712                    status: AccountStatus::Changed,
713                    info: Some(existing_account_with_storage_info.clone()),
714                    previous_status: AccountStatus::Loaded,
715                    previous_info: Some(existing_account_with_storage_info.clone()),
716                    storage: HashMap::from_iter([
717                        (
718                            slot1,
719                            StorageSlot::new_changed(StorageValue::from(1), StorageValue::from(10)),
720                        ),
721                        (
722                            slot2,
723                            StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(20)),
724                        ),
725                    ]),
726                    storage_was_destroyed: false,
727                },
728            ),
729        ]));
730
731        // Another transaction in block 1 destroys new account.
732        state.apply_transition(Vec::from([
733            (
734                new_account_address,
735                TransitionAccount {
736                    status: AccountStatus::Destroyed,
737                    info: None,
738                    previous_status: AccountStatus::InMemoryChange,
739                    previous_info: Some(new_account_created_info),
740                    ..Default::default()
741                },
742            ),
743            (
744                existing_account_address,
745                TransitionAccount {
746                    status: AccountStatus::Changed,
747                    info: Some(existing_account_initial_info),
748                    previous_status: AccountStatus::Changed,
749                    previous_info: Some(existing_account_updated_info),
750                    ..Default::default()
751                },
752            ),
753            (
754                existing_account_with_storage_address,
755                TransitionAccount {
756                    status: AccountStatus::Changed,
757                    info: Some(existing_account_with_storage_info.clone()),
758                    previous_status: AccountStatus::Changed,
759                    previous_info: Some(existing_account_with_storage_info.clone()),
760                    storage: HashMap::from_iter([
761                        (
762                            slot1,
763                            StorageSlot::new_changed(StorageValue::from(10), StorageValue::from(1)),
764                        ),
765                        (
766                            slot2,
767                            StorageSlot::new_changed(StorageValue::from(20), StorageValue::ZERO),
768                        ),
769                    ]),
770                    storage_was_destroyed: false,
771                },
772            ),
773        ]));
774
775        state.merge_transitions(BundleRetention::Reverts);
776
777        let mut bundle_state = state.take_bundle();
778        bundle_state.reverts.sort();
779
780        // both account info and storage are left as before transitions,
781        // therefore there is nothing to revert
782        assert_eq!(bundle_state.reverts.as_ref(), Vec::from([Vec::from([])]));
783    }
784
785    /// Checks that the behavior of selfdestruct within the block is correct.
786    #[test]
787    fn selfdestruct_state_and_reverts() {
788        let mut state = State::builder().with_bundle_update().build();
789
790        // Existing account.
791        let existing_account_address = Address::from_slice(&[0x1; 20]);
792        let existing_account_info = AccountInfo {
793            nonce: 1,
794            ..Default::default()
795        };
796
797        let (slot1, slot2) = (StorageKey::from(1), StorageKey::from(2));
798
799        // Existing account is destroyed.
800        state.apply_transition(Vec::from([(
801            existing_account_address,
802            TransitionAccount {
803                status: AccountStatus::Destroyed,
804                info: None,
805                previous_status: AccountStatus::Loaded,
806                previous_info: Some(existing_account_info.clone()),
807                storage: HashMap::default(),
808                storage_was_destroyed: true,
809            },
810        )]));
811
812        // Existing account is re-created and slot 0x01 is changed.
813        state.apply_transition(Vec::from([(
814            existing_account_address,
815            TransitionAccount {
816                status: AccountStatus::DestroyedChanged,
817                info: Some(existing_account_info.clone()),
818                previous_status: AccountStatus::Destroyed,
819                previous_info: None,
820                storage: HashMap::from_iter([(
821                    slot1,
822                    StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(1)),
823                )]),
824                storage_was_destroyed: false,
825            },
826        )]));
827
828        // Slot 0x01 is changed, but existing account is destroyed again.
829        state.apply_transition(Vec::from([(
830            existing_account_address,
831            TransitionAccount {
832                status: AccountStatus::DestroyedAgain,
833                info: None,
834                previous_status: AccountStatus::DestroyedChanged,
835                previous_info: Some(existing_account_info.clone()),
836                // storage change should be ignored
837                storage: HashMap::default(),
838                storage_was_destroyed: true,
839            },
840        )]));
841
842        // Existing account is re-created and slot 0x02 is changed.
843        state.apply_transition(Vec::from([(
844            existing_account_address,
845            TransitionAccount {
846                status: AccountStatus::DestroyedChanged,
847                info: Some(existing_account_info.clone()),
848                previous_status: AccountStatus::DestroyedAgain,
849                previous_info: None,
850                storage: HashMap::from_iter([(
851                    slot2,
852                    StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(2)),
853                )]),
854                storage_was_destroyed: false,
855            },
856        )]));
857
858        state.merge_transitions(BundleRetention::Reverts);
859
860        let bundle_state = state.take_bundle();
861
862        assert_eq!(
863            bundle_state.state,
864            HashMap::from_iter([(
865                existing_account_address,
866                BundleAccount {
867                    info: Some(existing_account_info.clone()),
868                    original_info: Some(existing_account_info.clone()),
869                    storage: HashMap::from_iter([(
870                        slot2,
871                        StorageSlot::new_changed(StorageValue::ZERO, StorageValue::from(2))
872                    )]),
873                    status: AccountStatus::DestroyedChanged,
874                }
875            )])
876        );
877
878        assert_eq!(
879            bundle_state.reverts.as_ref(),
880            Vec::from([Vec::from([(
881                existing_account_address,
882                AccountRevert {
883                    account: AccountInfoRevert::DoNothing,
884                    previous_status: AccountStatus::Loaded,
885                    storage: HashMap::from_iter([(slot2, RevertToSlot::Destroyed)]),
886                    wipe_storage: true,
887                }
888            )])])
889        )
890    }
891}