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