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