reth_provider/writer/
mod.rs

1use crate::{
2    providers::{StaticFileProvider, StaticFileWriter as SfWriter},
3    BlockExecutionWriter, BlockWriter, HistoryWriter, StateWriter, StaticFileProviderFactory,
4    StorageLocation, TrieWriter,
5};
6use alloy_consensus::BlockHeader;
7use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates};
8use reth_db_api::transaction::{DbTx, DbTxMut};
9use reth_errors::ProviderResult;
10use reth_primitives::{NodePrimitives, StaticFileSegment};
11use reth_primitives_traits::SignedTransaction;
12use reth_storage_api::{DBProvider, StageCheckpointWriter, TransactionsProviderExt};
13use reth_storage_errors::writer::UnifiedStorageWriterError;
14use revm_database::OriginalValuesKnown;
15use std::sync::Arc;
16use tracing::debug;
17
18/// [`UnifiedStorageWriter`] is responsible for managing the writing to storage with both database
19/// and static file providers.
20#[derive(Debug)]
21pub struct UnifiedStorageWriter<'a, ProviderDB, ProviderSF> {
22    database: &'a ProviderDB,
23    static_file: Option<ProviderSF>,
24}
25
26impl<'a, ProviderDB, ProviderSF> UnifiedStorageWriter<'a, ProviderDB, ProviderSF> {
27    /// Creates a new instance of [`UnifiedStorageWriter`].
28    ///
29    /// # Parameters
30    /// - `database`: An optional reference to a database provider.
31    /// - `static_file`: An optional mutable reference to a static file instance.
32    pub const fn new(database: &'a ProviderDB, static_file: Option<ProviderSF>) -> Self {
33        Self { database, static_file }
34    }
35
36    /// Creates a new instance of [`UnifiedStorageWriter`] from a database provider and a static
37    /// file instance.
38    pub fn from<P>(database: &'a P, static_file: ProviderSF) -> Self
39    where
40        P: AsRef<ProviderDB>,
41    {
42        Self::new(database.as_ref(), Some(static_file))
43    }
44
45    /// Creates a new instance of [`UnifiedStorageWriter`] from a database provider.
46    pub fn from_database<P>(database: &'a P) -> Self
47    where
48        P: AsRef<ProviderDB>,
49    {
50        Self::new(database.as_ref(), None)
51    }
52
53    /// Returns a reference to the database writer.
54    ///
55    /// # Panics
56    /// If the database provider is not set.
57    const fn database(&self) -> &ProviderDB {
58        self.database
59    }
60
61    /// Returns a reference to the static file instance.
62    ///
63    /// # Panics
64    /// If the static file instance is not set.
65    fn static_file(&self) -> &ProviderSF {
66        self.static_file.as_ref().expect("should exist")
67    }
68
69    /// Ensures that the static file instance is set.
70    ///
71    /// # Returns
72    /// - `Ok(())` if the static file instance is set.
73    /// - `Err(StorageWriterError::MissingStaticFileWriter)` if the static file instance is not set.
74    #[allow(unused)]
75    const fn ensure_static_file(&self) -> Result<(), UnifiedStorageWriterError> {
76        if self.static_file.is_none() {
77            return Err(UnifiedStorageWriterError::MissingStaticFileWriter)
78        }
79        Ok(())
80    }
81}
82
83impl UnifiedStorageWriter<'_, (), ()> {
84    /// Commits both storage types in the right order.
85    ///
86    /// For non-unwinding operations it makes more sense to commit the static files first, since if
87    /// it is interrupted before the database commit, we can just truncate
88    /// the static files according to the checkpoints on the next
89    /// start-up.
90    ///
91    /// NOTE: If unwinding data from storage, use `commit_unwind` instead!
92    pub fn commit<P>(provider: P) -> ProviderResult<()>
93    where
94        P: DBProvider<Tx: DbTxMut> + StaticFileProviderFactory,
95    {
96        let static_file = provider.static_file_provider();
97        static_file.commit()?;
98        provider.commit()?;
99        Ok(())
100    }
101
102    /// Commits both storage types in the right order for an unwind operation.
103    ///
104    /// For unwinding it makes more sense to commit the database first, since if
105    /// it is interrupted before the static files commit, we can just
106    /// truncate the static files according to the
107    /// checkpoints on the next start-up.
108    ///
109    /// NOTE: Should only be used after unwinding data from storage!
110    pub fn commit_unwind<P>(provider: P) -> ProviderResult<()>
111    where
112        P: DBProvider<Tx: DbTxMut> + StaticFileProviderFactory,
113    {
114        let static_file = provider.static_file_provider();
115        provider.commit()?;
116        static_file.commit()?;
117        Ok(())
118    }
119}
120
121impl<ProviderDB> UnifiedStorageWriter<'_, ProviderDB, &StaticFileProvider<ProviderDB::Primitives>>
122where
123    ProviderDB: DBProvider<Tx: DbTx + DbTxMut>
124        + BlockWriter
125        + TransactionsProviderExt
126        + TrieWriter
127        + StateWriter
128        + HistoryWriter
129        + StageCheckpointWriter
130        + BlockExecutionWriter
131        + AsRef<ProviderDB>
132        + StaticFileProviderFactory,
133{
134    /// Writes executed blocks and receipts to storage.
135    pub fn save_blocks<N>(&self, blocks: Vec<ExecutedBlockWithTrieUpdates<N>>) -> ProviderResult<()>
136    where
137        N: NodePrimitives<SignedTx: SignedTransaction>,
138        ProviderDB: BlockWriter<Block = N::Block> + StateWriter<Receipt = N::Receipt>,
139    {
140        if blocks.is_empty() {
141            debug!(target: "provider::storage_writer", "Attempted to write empty block range");
142            return Ok(())
143        }
144
145        // NOTE: checked non-empty above
146        let first_block = blocks.first().unwrap().recovered_block();
147
148        let last_block = blocks.last().unwrap().recovered_block();
149        let first_number = first_block.number();
150        let last_block_number = last_block.number();
151
152        debug!(target: "provider::storage_writer", block_count = %blocks.len(), "Writing blocks and execution data to storage");
153
154        // TODO: Do performant / batched writes for each type of object
155        // instead of a loop over all blocks,
156        // meaning:
157        //  * blocks
158        //  * state
159        //  * hashed state
160        //  * trie updates (cannot naively extend, need helper)
161        //  * indices (already done basically)
162        // Insert the blocks
163        for ExecutedBlockWithTrieUpdates {
164            block: ExecutedBlock { recovered_block, execution_output, hashed_state },
165            trie,
166        } in blocks
167        {
168            self.database()
169                .insert_block(Arc::unwrap_or_clone(recovered_block), StorageLocation::Both)?;
170
171            // Write state and changesets to the database.
172            // Must be written after blocks because of the receipt lookup.
173            self.database().write_state(
174                &execution_output,
175                OriginalValuesKnown::No,
176                StorageLocation::StaticFiles,
177            )?;
178
179            // insert hashes and intermediate merkle nodes
180            self.database()
181                .write_hashed_state(&Arc::unwrap_or_clone(hashed_state).into_sorted())?;
182            self.database().write_trie_updates(&trie)?;
183        }
184
185        // update history indices
186        self.database().update_history_indices(first_number..=last_block_number)?;
187
188        // Update pipeline progress
189        self.database().update_pipeline_stages(last_block_number, false)?;
190
191        debug!(target: "provider::storage_writer", range = ?first_number..=last_block_number, "Appended block data");
192
193        Ok(())
194    }
195
196    /// Removes all block, transaction and receipt data above the given block number from the
197    /// database and static files. This is exclusive, i.e., it only removes blocks above
198    /// `block_number`, and does not remove `block_number`.
199    pub fn remove_blocks_above(&self, block_number: u64) -> ProviderResult<()> {
200        // IMPORTANT: we use `block_number+1` to make sure we remove only what is ABOVE the block
201        debug!(target: "provider::storage_writer", ?block_number, "Removing blocks from database above block_number");
202        self.database().remove_block_and_execution_above(block_number, StorageLocation::Both)?;
203
204        // Get highest static file block for the total block range
205        let highest_static_file_block = self
206            .static_file()
207            .get_highest_static_file_block(StaticFileSegment::Headers)
208            .expect("todo: error handling, headers should exist");
209
210        // IMPORTANT: we use `highest_static_file_block.saturating_sub(block_number)` to make sure
211        // we remove only what is ABOVE the block.
212        //
213        // i.e., if the highest static file block is 8, we want to remove above block 5 only, we
214        // will have three blocks to remove, which will be block 8, 7, and 6.
215        debug!(target: "provider::storage_writer", ?block_number, "Removing static file blocks above block_number");
216        self.static_file()
217            .get_writer(block_number, StaticFileSegment::Headers)?
218            .prune_headers(highest_static_file_block.saturating_sub(block_number))?;
219
220        Ok(())
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227    use crate::{
228        test_utils::create_test_provider_factory, AccountReader, StorageTrieWriter, TrieWriter,
229    };
230    use alloy_primitives::{keccak256, map::HashMap, Address, B256, U256};
231    use reth_db_api::{
232        cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO},
233        models::{AccountBeforeTx, BlockNumberAddress},
234        tables,
235        transaction::{DbTx, DbTxMut},
236    };
237    use reth_execution_types::ExecutionOutcome;
238    use reth_primitives::{Account, Receipt, StorageEntry};
239    use reth_storage_api::{DatabaseProviderFactory, HashedPostStateProvider};
240    use reth_trie::{
241        test_utils::{state_root, storage_root_prehashed},
242        HashedPostState, HashedStorage, StateRoot, StorageRoot,
243    };
244    use reth_trie_db::{DatabaseStateRoot, DatabaseStorageRoot};
245    use revm_database::{
246        states::{
247            bundle_state::BundleRetention, changes::PlainStorageRevert, PlainStorageChangeset,
248        },
249        BundleState, State,
250    };
251    use revm_database_interface::{DatabaseCommit, EmptyDB};
252    use revm_state::{
253        Account as RevmAccount, AccountInfo as RevmAccountInfo, AccountStatus, EvmStorageSlot,
254    };
255    use std::{collections::BTreeMap, str::FromStr};
256
257    #[test]
258    fn wiped_entries_are_removed() {
259        let provider_factory = create_test_provider_factory();
260
261        let addresses = (0..10).map(|_| Address::random()).collect::<Vec<_>>();
262        let destroyed_address = *addresses.first().unwrap();
263        let destroyed_address_hashed = keccak256(destroyed_address);
264        let slot = B256::with_last_byte(1);
265        let hashed_slot = keccak256(slot);
266        {
267            let provider_rw = provider_factory.provider_rw().unwrap();
268            let mut accounts_cursor =
269                provider_rw.tx_ref().cursor_write::<tables::HashedAccounts>().unwrap();
270            let mut storage_cursor =
271                provider_rw.tx_ref().cursor_write::<tables::HashedStorages>().unwrap();
272
273            for address in addresses {
274                let hashed_address = keccak256(address);
275                accounts_cursor
276                    .insert(hashed_address, &Account { nonce: 1, ..Default::default() })
277                    .unwrap();
278                storage_cursor
279                    .insert(
280                        hashed_address,
281                        &StorageEntry { key: hashed_slot, value: U256::from(1) },
282                    )
283                    .unwrap();
284            }
285            provider_rw.commit().unwrap();
286        }
287
288        let mut hashed_state = HashedPostState::default();
289        hashed_state.accounts.insert(destroyed_address_hashed, None);
290        hashed_state.storages.insert(destroyed_address_hashed, HashedStorage::new(true));
291
292        let provider_rw = provider_factory.provider_rw().unwrap();
293        assert!(matches!(provider_rw.write_hashed_state(&hashed_state.into_sorted()), Ok(())));
294        provider_rw.commit().unwrap();
295
296        let provider = provider_factory.provider().unwrap();
297        assert_eq!(
298            provider.tx_ref().get::<tables::HashedAccounts>(destroyed_address_hashed),
299            Ok(None)
300        );
301        assert_eq!(
302            provider
303                .tx_ref()
304                .cursor_read::<tables::HashedStorages>()
305                .unwrap()
306                .seek_by_key_subkey(destroyed_address_hashed, hashed_slot),
307            Ok(None)
308        );
309    }
310
311    #[test]
312    fn write_to_db_account_info() {
313        let factory = create_test_provider_factory();
314        let provider = factory.provider_rw().unwrap();
315
316        let address_a = Address::ZERO;
317        let address_b = Address::repeat_byte(0xff);
318
319        let account_a = RevmAccountInfo { balance: U256::from(1), nonce: 1, ..Default::default() };
320        let account_b = RevmAccountInfo { balance: U256::from(2), nonce: 2, ..Default::default() };
321        let account_b_changed =
322            RevmAccountInfo { balance: U256::from(3), nonce: 3, ..Default::default() };
323
324        let mut state = State::builder().with_bundle_update().build();
325        state.insert_not_existing(address_a);
326        state.insert_account(address_b, account_b.clone());
327
328        // 0x00.. is created
329        state.commit(HashMap::from_iter([(
330            address_a,
331            RevmAccount {
332                info: account_a.clone(),
333                status: AccountStatus::Touched | AccountStatus::Created,
334                storage: HashMap::default(),
335            },
336        )]));
337
338        // 0xff.. is changed (balance + 1, nonce + 1)
339        state.commit(HashMap::from_iter([(
340            address_b,
341            RevmAccount {
342                info: account_b_changed.clone(),
343                status: AccountStatus::Touched,
344                storage: HashMap::default(),
345            },
346        )]));
347
348        state.merge_transitions(BundleRetention::Reverts);
349        let mut revm_bundle_state = state.take_bundle();
350
351        // Write plain state and reverts separately.
352        let reverts = revm_bundle_state.take_all_reverts().to_plain_state_reverts();
353        let plain_state = revm_bundle_state.to_plain_state(OriginalValuesKnown::Yes);
354        assert!(plain_state.storage.is_empty());
355        assert!(plain_state.contracts.is_empty());
356        provider.write_state_changes(plain_state).expect("Could not write plain state to DB");
357
358        assert_eq!(reverts.storage, [[]]);
359        provider.write_state_reverts(reverts, 1).expect("Could not write reverts to DB");
360
361        let reth_account_a = account_a.into();
362        let reth_account_b = account_b.into();
363        let reth_account_b_changed = (&account_b_changed).into();
364
365        // Check plain state
366        assert_eq!(
367            provider.basic_account(&address_a).expect("Could not read account state"),
368            Some(reth_account_a),
369            "Account A state is wrong"
370        );
371        assert_eq!(
372            provider.basic_account(&address_b).expect("Could not read account state"),
373            Some(reth_account_b_changed),
374            "Account B state is wrong"
375        );
376
377        // Check change set
378        let mut changeset_cursor = provider
379            .tx_ref()
380            .cursor_dup_read::<tables::AccountChangeSets>()
381            .expect("Could not open changeset cursor");
382        assert_eq!(
383            changeset_cursor.seek_exact(1).expect("Could not read account change set"),
384            Some((1, AccountBeforeTx { address: address_a, info: None })),
385            "Account A changeset is wrong"
386        );
387        assert_eq!(
388            changeset_cursor.next_dup().expect("Changeset table is malformed"),
389            Some((1, AccountBeforeTx { address: address_b, info: Some(reth_account_b) })),
390            "Account B changeset is wrong"
391        );
392
393        let mut state = State::builder().with_bundle_update().build();
394        state.insert_account(address_b, account_b_changed.clone());
395
396        // 0xff.. is destroyed
397        state.commit(HashMap::from_iter([(
398            address_b,
399            RevmAccount {
400                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
401                info: account_b_changed,
402                storage: HashMap::default(),
403            },
404        )]));
405
406        state.merge_transitions(BundleRetention::Reverts);
407        let mut revm_bundle_state = state.take_bundle();
408
409        // Write plain state and reverts separately.
410        let reverts = revm_bundle_state.take_all_reverts().to_plain_state_reverts();
411        let plain_state = revm_bundle_state.to_plain_state(OriginalValuesKnown::Yes);
412        // Account B selfdestructed so flag for it should be present.
413        assert_eq!(
414            plain_state.storage,
415            [PlainStorageChangeset { address: address_b, wipe_storage: true, storage: vec![] }]
416        );
417        assert!(plain_state.contracts.is_empty());
418        provider.write_state_changes(plain_state).expect("Could not write plain state to DB");
419
420        assert_eq!(
421            reverts.storage,
422            [[PlainStorageRevert { address: address_b, wiped: true, storage_revert: vec![] }]]
423        );
424        provider.write_state_reverts(reverts, 2).expect("Could not write reverts to DB");
425
426        // Check new plain state for account B
427        assert_eq!(
428            provider.basic_account(&address_b).expect("Could not read account state"),
429            None,
430            "Account B should be deleted"
431        );
432
433        // Check change set
434        assert_eq!(
435            changeset_cursor.seek_exact(2).expect("Could not read account change set"),
436            Some((2, AccountBeforeTx { address: address_b, info: Some(reth_account_b_changed) })),
437            "Account B changeset is wrong after deletion"
438        );
439    }
440
441    #[test]
442    fn write_to_db_storage() {
443        let factory = create_test_provider_factory();
444        let provider = factory.database_provider_rw().unwrap();
445
446        let address_a = Address::ZERO;
447        let address_b = Address::repeat_byte(0xff);
448
449        let account_b = RevmAccountInfo { balance: U256::from(2), nonce: 2, ..Default::default() };
450
451        let mut state = State::builder().with_bundle_update().build();
452        state.insert_not_existing(address_a);
453        state.insert_account_with_storage(
454            address_b,
455            account_b.clone(),
456            HashMap::from_iter([(U256::from(1), U256::from(1))]),
457        );
458
459        state.commit(HashMap::from_iter([
460            (
461                address_a,
462                RevmAccount {
463                    status: AccountStatus::Touched | AccountStatus::Created,
464                    info: RevmAccountInfo::default(),
465                    // 0x00 => 0 => 1
466                    // 0x01 => 0 => 2
467                    storage: HashMap::from_iter([
468                        (
469                            U256::from(0),
470                            EvmStorageSlot { present_value: U256::from(1), ..Default::default() },
471                        ),
472                        (
473                            U256::from(1),
474                            EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
475                        ),
476                    ]),
477                },
478            ),
479            (
480                address_b,
481                RevmAccount {
482                    status: AccountStatus::Touched,
483                    info: account_b,
484                    // 0x01 => 1 => 2
485                    storage: HashMap::from_iter([(
486                        U256::from(1),
487                        EvmStorageSlot {
488                            present_value: U256::from(2),
489                            original_value: U256::from(1),
490                            ..Default::default()
491                        },
492                    )]),
493                },
494            ),
495        ]));
496
497        state.merge_transitions(BundleRetention::Reverts);
498
499        let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 1, Vec::new());
500        provider
501            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
502            .expect("Could not write bundle state to DB");
503
504        // Check plain storage state
505        let mut storage_cursor = provider
506            .tx_ref()
507            .cursor_dup_read::<tables::PlainStorageState>()
508            .expect("Could not open plain storage state cursor");
509
510        assert_eq!(
511            storage_cursor.seek_exact(address_a).unwrap(),
512            Some((address_a, StorageEntry { key: B256::ZERO, value: U256::from(1) })),
513            "Slot 0 for account A should be 1"
514        );
515        assert_eq!(
516            storage_cursor.next_dup().unwrap(),
517            Some((
518                address_a,
519                StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(2) }
520            )),
521            "Slot 1 for account A should be 2"
522        );
523        assert_eq!(
524            storage_cursor.next_dup().unwrap(),
525            None,
526            "Account A should only have 2 storage slots"
527        );
528
529        assert_eq!(
530            storage_cursor.seek_exact(address_b).unwrap(),
531            Some((
532                address_b,
533                StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(2) }
534            )),
535            "Slot 1 for account B should be 2"
536        );
537        assert_eq!(
538            storage_cursor.next_dup().unwrap(),
539            None,
540            "Account B should only have 1 storage slot"
541        );
542
543        // Check change set
544        let mut changeset_cursor = provider
545            .tx_ref()
546            .cursor_dup_read::<tables::StorageChangeSets>()
547            .expect("Could not open storage changeset cursor");
548        assert_eq!(
549            changeset_cursor.seek_exact(BlockNumberAddress((1, address_a))).unwrap(),
550            Some((
551                BlockNumberAddress((1, address_a)),
552                StorageEntry { key: B256::ZERO, value: U256::from(0) }
553            )),
554            "Slot 0 for account A should have changed from 0"
555        );
556        assert_eq!(
557            changeset_cursor.next_dup().unwrap(),
558            Some((
559                BlockNumberAddress((1, address_a)),
560                StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(0) }
561            )),
562            "Slot 1 for account A should have changed from 0"
563        );
564        assert_eq!(
565            changeset_cursor.next_dup().unwrap(),
566            None,
567            "Account A should only be in the changeset 2 times"
568        );
569
570        assert_eq!(
571            changeset_cursor.seek_exact(BlockNumberAddress((1, address_b))).unwrap(),
572            Some((
573                BlockNumberAddress((1, address_b)),
574                StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(1) }
575            )),
576            "Slot 1 for account B should have changed from 1"
577        );
578        assert_eq!(
579            changeset_cursor.next_dup().unwrap(),
580            None,
581            "Account B should only be in the changeset 1 time"
582        );
583
584        // Delete account A
585        let mut state = State::builder().with_bundle_update().build();
586        state.insert_account(address_a, RevmAccountInfo::default());
587
588        state.commit(HashMap::from_iter([(
589            address_a,
590            RevmAccount {
591                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
592                info: RevmAccountInfo::default(),
593                storage: HashMap::default(),
594            },
595        )]));
596
597        state.merge_transitions(BundleRetention::Reverts);
598        let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 2, Vec::new());
599        provider
600            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
601            .expect("Could not write bundle state to DB");
602
603        assert_eq!(
604            storage_cursor.seek_exact(address_a).unwrap(),
605            None,
606            "Account A should have no storage slots after deletion"
607        );
608
609        assert_eq!(
610            changeset_cursor.seek_exact(BlockNumberAddress((2, address_a))).unwrap(),
611            Some((
612                BlockNumberAddress((2, address_a)),
613                StorageEntry { key: B256::ZERO, value: U256::from(1) }
614            )),
615            "Slot 0 for account A should have changed from 1 on deletion"
616        );
617        assert_eq!(
618            changeset_cursor.next_dup().unwrap(),
619            Some((
620                BlockNumberAddress((2, address_a)),
621                StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(2) }
622            )),
623            "Slot 1 for account A should have changed from 2 on deletion"
624        );
625        assert_eq!(
626            changeset_cursor.next_dup().unwrap(),
627            None,
628            "Account A should only be in the changeset 2 times on deletion"
629        );
630    }
631
632    #[test]
633    fn write_to_db_multiple_selfdestructs() {
634        let factory = create_test_provider_factory();
635        let provider = factory.database_provider_rw().unwrap();
636
637        let address1 = Address::random();
638        let account_info = RevmAccountInfo { nonce: 1, ..Default::default() };
639
640        // Block #0: initial state.
641        let mut init_state = State::builder().with_bundle_update().build();
642        init_state.insert_not_existing(address1);
643        init_state.commit(HashMap::from_iter([(
644            address1,
645            RevmAccount {
646                info: account_info.clone(),
647                status: AccountStatus::Touched | AccountStatus::Created,
648                // 0x00 => 0 => 1
649                // 0x01 => 0 => 2
650                storage: HashMap::from_iter([
651                    (
652                        U256::ZERO,
653                        EvmStorageSlot { present_value: U256::from(1), ..Default::default() },
654                    ),
655                    (
656                        U256::from(1),
657                        EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
658                    ),
659                ]),
660            },
661        )]));
662        init_state.merge_transitions(BundleRetention::Reverts);
663
664        let outcome =
665            ExecutionOutcome::new(init_state.take_bundle(), Default::default(), 0, Vec::new());
666        provider
667            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
668            .expect("Could not write bundle state to DB");
669
670        let mut state = State::builder().with_bundle_update().build();
671        state.insert_account_with_storage(
672            address1,
673            account_info.clone(),
674            HashMap::from_iter([(U256::ZERO, U256::from(1)), (U256::from(1), U256::from(2))]),
675        );
676
677        // Block #1: change storage.
678        state.commit(HashMap::from_iter([(
679            address1,
680            RevmAccount {
681                status: AccountStatus::Touched,
682                info: account_info.clone(),
683                // 0x00 => 1 => 2
684                storage: HashMap::from_iter([(
685                    U256::ZERO,
686                    EvmStorageSlot {
687                        original_value: U256::from(1),
688                        present_value: U256::from(2),
689                        ..Default::default()
690                    },
691                )]),
692            },
693        )]));
694        state.merge_transitions(BundleRetention::Reverts);
695
696        // Block #2: destroy account.
697        state.commit(HashMap::from_iter([(
698            address1,
699            RevmAccount {
700                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
701                info: account_info.clone(),
702                storage: HashMap::default(),
703            },
704        )]));
705        state.merge_transitions(BundleRetention::Reverts);
706
707        // Block #3: re-create account and change storage.
708        state.commit(HashMap::from_iter([(
709            address1,
710            RevmAccount {
711                status: AccountStatus::Touched | AccountStatus::Created,
712                info: account_info.clone(),
713                storage: HashMap::default(),
714            },
715        )]));
716        state.merge_transitions(BundleRetention::Reverts);
717
718        // Block #4: change storage.
719        state.commit(HashMap::from_iter([(
720            address1,
721            RevmAccount {
722                status: AccountStatus::Touched,
723                info: account_info.clone(),
724                // 0x00 => 0 => 2
725                // 0x02 => 0 => 4
726                // 0x06 => 0 => 6
727                storage: HashMap::from_iter([
728                    (
729                        U256::ZERO,
730                        EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
731                    ),
732                    (
733                        U256::from(2),
734                        EvmStorageSlot { present_value: U256::from(4), ..Default::default() },
735                    ),
736                    (
737                        U256::from(6),
738                        EvmStorageSlot { present_value: U256::from(6), ..Default::default() },
739                    ),
740                ]),
741            },
742        )]));
743        state.merge_transitions(BundleRetention::Reverts);
744
745        // Block #5: Destroy account again.
746        state.commit(HashMap::from_iter([(
747            address1,
748            RevmAccount {
749                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
750                info: account_info.clone(),
751                storage: HashMap::default(),
752            },
753        )]));
754        state.merge_transitions(BundleRetention::Reverts);
755
756        // Block #6: Create, change, destroy and re-create in the same block.
757        state.commit(HashMap::from_iter([(
758            address1,
759            RevmAccount {
760                status: AccountStatus::Touched | AccountStatus::Created,
761                info: account_info.clone(),
762                storage: HashMap::default(),
763            },
764        )]));
765        state.commit(HashMap::from_iter([(
766            address1,
767            RevmAccount {
768                status: AccountStatus::Touched,
769                info: account_info.clone(),
770                // 0x00 => 0 => 2
771                storage: HashMap::from_iter([(
772                    U256::ZERO,
773                    EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
774                )]),
775            },
776        )]));
777        state.commit(HashMap::from_iter([(
778            address1,
779            RevmAccount {
780                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
781                info: account_info.clone(),
782                storage: HashMap::default(),
783            },
784        )]));
785        state.commit(HashMap::from_iter([(
786            address1,
787            RevmAccount {
788                status: AccountStatus::Touched | AccountStatus::Created,
789                info: account_info.clone(),
790                storage: HashMap::default(),
791            },
792        )]));
793        state.merge_transitions(BundleRetention::Reverts);
794
795        // Block #7: Change storage.
796        state.commit(HashMap::from_iter([(
797            address1,
798            RevmAccount {
799                status: AccountStatus::Touched,
800                info: account_info,
801                // 0x00 => 0 => 9
802                storage: HashMap::from_iter([(
803                    U256::ZERO,
804                    EvmStorageSlot { present_value: U256::from(9), ..Default::default() },
805                )]),
806            },
807        )]));
808        state.merge_transitions(BundleRetention::Reverts);
809
810        let bundle = state.take_bundle();
811
812        let outcome: ExecutionOutcome =
813            ExecutionOutcome::new(bundle, Default::default(), 1, Vec::new());
814        provider
815            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
816            .expect("Could not write bundle state to DB");
817
818        let mut storage_changeset_cursor = provider
819            .tx_ref()
820            .cursor_dup_read::<tables::StorageChangeSets>()
821            .expect("Could not open plain storage state cursor");
822        let mut storage_changes = storage_changeset_cursor.walk_range(..).unwrap();
823
824        // Iterate through all storage changes
825
826        // Block <number>
827        // <slot>: <expected value before>
828        // ...
829
830        // Block #0
831        // 0x00: 0
832        // 0x01: 0
833        assert_eq!(
834            storage_changes.next(),
835            Some(Ok((
836                BlockNumberAddress((0, address1)),
837                StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO }
838            )))
839        );
840        assert_eq!(
841            storage_changes.next(),
842            Some(Ok((
843                BlockNumberAddress((0, address1)),
844                StorageEntry { key: B256::with_last_byte(1), value: U256::ZERO }
845            )))
846        );
847
848        // Block #1
849        // 0x00: 1
850        assert_eq!(
851            storage_changes.next(),
852            Some(Ok((
853                BlockNumberAddress((1, address1)),
854                StorageEntry { key: B256::with_last_byte(0), value: U256::from(1) }
855            )))
856        );
857
858        // Block #2 (destroyed)
859        // 0x00: 2
860        // 0x01: 2
861        assert_eq!(
862            storage_changes.next(),
863            Some(Ok((
864                BlockNumberAddress((2, address1)),
865                StorageEntry { key: B256::with_last_byte(0), value: U256::from(2) }
866            )))
867        );
868        assert_eq!(
869            storage_changes.next(),
870            Some(Ok((
871                BlockNumberAddress((2, address1)),
872                StorageEntry { key: B256::with_last_byte(1), value: U256::from(2) }
873            )))
874        );
875
876        // Block #3
877        // no storage changes
878
879        // Block #4
880        // 0x00: 0
881        // 0x02: 0
882        // 0x06: 0
883        assert_eq!(
884            storage_changes.next(),
885            Some(Ok((
886                BlockNumberAddress((4, address1)),
887                StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO }
888            )))
889        );
890        assert_eq!(
891            storage_changes.next(),
892            Some(Ok((
893                BlockNumberAddress((4, address1)),
894                StorageEntry { key: B256::with_last_byte(2), value: U256::ZERO }
895            )))
896        );
897        assert_eq!(
898            storage_changes.next(),
899            Some(Ok((
900                BlockNumberAddress((4, address1)),
901                StorageEntry { key: B256::with_last_byte(6), value: U256::ZERO }
902            )))
903        );
904
905        // Block #5 (destroyed)
906        // 0x00: 2
907        // 0x02: 4
908        // 0x06: 6
909        assert_eq!(
910            storage_changes.next(),
911            Some(Ok((
912                BlockNumberAddress((5, address1)),
913                StorageEntry { key: B256::with_last_byte(0), value: U256::from(2) }
914            )))
915        );
916        assert_eq!(
917            storage_changes.next(),
918            Some(Ok((
919                BlockNumberAddress((5, address1)),
920                StorageEntry { key: B256::with_last_byte(2), value: U256::from(4) }
921            )))
922        );
923        assert_eq!(
924            storage_changes.next(),
925            Some(Ok((
926                BlockNumberAddress((5, address1)),
927                StorageEntry { key: B256::with_last_byte(6), value: U256::from(6) }
928            )))
929        );
930
931        // Block #6
932        // no storage changes (only inter block changes)
933
934        // Block #7
935        // 0x00: 0
936        assert_eq!(
937            storage_changes.next(),
938            Some(Ok((
939                BlockNumberAddress((7, address1)),
940                StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO }
941            )))
942        );
943        assert_eq!(storage_changes.next(), None);
944    }
945
946    #[test]
947    fn storage_change_after_selfdestruct_within_block() {
948        let factory = create_test_provider_factory();
949        let provider = factory.database_provider_rw().unwrap();
950
951        let address1 = Address::random();
952        let account1 = RevmAccountInfo { nonce: 1, ..Default::default() };
953
954        // Block #0: initial state.
955        let mut init_state = State::builder().with_bundle_update().build();
956        init_state.insert_not_existing(address1);
957        init_state.commit(HashMap::from_iter([(
958            address1,
959            RevmAccount {
960                info: account1.clone(),
961                status: AccountStatus::Touched | AccountStatus::Created,
962                // 0x00 => 0 => 1
963                // 0x01 => 0 => 2
964                storage: HashMap::from_iter([
965                    (
966                        U256::ZERO,
967                        EvmStorageSlot { present_value: U256::from(1), ..Default::default() },
968                    ),
969                    (
970                        U256::from(1),
971                        EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
972                    ),
973                ]),
974            },
975        )]));
976        init_state.merge_transitions(BundleRetention::Reverts);
977        let outcome =
978            ExecutionOutcome::new(init_state.take_bundle(), Default::default(), 0, Vec::new());
979        provider
980            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
981            .expect("Could not write bundle state to DB");
982
983        let mut state = State::builder().with_bundle_update().build();
984        state.insert_account_with_storage(
985            address1,
986            account1.clone(),
987            HashMap::from_iter([(U256::ZERO, U256::from(1)), (U256::from(1), U256::from(2))]),
988        );
989
990        // Block #1: Destroy, re-create, change storage.
991        state.commit(HashMap::from_iter([(
992            address1,
993            RevmAccount {
994                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
995                info: account1.clone(),
996                storage: HashMap::default(),
997            },
998        )]));
999
1000        state.commit(HashMap::from_iter([(
1001            address1,
1002            RevmAccount {
1003                status: AccountStatus::Touched | AccountStatus::Created,
1004                info: account1.clone(),
1005                storage: HashMap::default(),
1006            },
1007        )]));
1008
1009        state.commit(HashMap::from_iter([(
1010            address1,
1011            RevmAccount {
1012                status: AccountStatus::Touched,
1013                info: account1,
1014                // 0x01 => 0 => 5
1015                storage: HashMap::from_iter([(
1016                    U256::from(1),
1017                    EvmStorageSlot { present_value: U256::from(5), ..Default::default() },
1018                )]),
1019            },
1020        )]));
1021
1022        // Commit block #1 changes to the database.
1023        state.merge_transitions(BundleRetention::Reverts);
1024        let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 1, Vec::new());
1025        provider
1026            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
1027            .expect("Could not write bundle state to DB");
1028
1029        let mut storage_changeset_cursor = provider
1030            .tx_ref()
1031            .cursor_dup_read::<tables::StorageChangeSets>()
1032            .expect("Could not open plain storage state cursor");
1033        let range = BlockNumberAddress::range(1..=1);
1034        let mut storage_changes = storage_changeset_cursor.walk_range(range).unwrap();
1035
1036        assert_eq!(
1037            storage_changes.next(),
1038            Some(Ok((
1039                BlockNumberAddress((1, address1)),
1040                StorageEntry { key: B256::with_last_byte(0), value: U256::from(1) }
1041            )))
1042        );
1043        assert_eq!(
1044            storage_changes.next(),
1045            Some(Ok((
1046                BlockNumberAddress((1, address1)),
1047                StorageEntry { key: B256::with_last_byte(1), value: U256::from(2) }
1048            )))
1049        );
1050        assert_eq!(storage_changes.next(), None);
1051    }
1052
1053    #[test]
1054    fn revert_to_indices() {
1055        let base: ExecutionOutcome = ExecutionOutcome {
1056            bundle: BundleState::default(),
1057            receipts: vec![vec![Receipt::default(); 2]; 7],
1058            first_block: 10,
1059            requests: Vec::new(),
1060        };
1061
1062        let mut this = base.clone();
1063        assert!(this.revert_to(10));
1064        assert_eq!(this.receipts.len(), 1);
1065
1066        let mut this = base.clone();
1067        assert!(!this.revert_to(9));
1068        assert_eq!(this.receipts.len(), 7);
1069
1070        let mut this = base.clone();
1071        assert!(this.revert_to(15));
1072        assert_eq!(this.receipts.len(), 6);
1073
1074        let mut this = base.clone();
1075        assert!(this.revert_to(16));
1076        assert_eq!(this.receipts.len(), 7);
1077
1078        let mut this = base;
1079        assert!(!this.revert_to(17));
1080        assert_eq!(this.receipts.len(), 7);
1081    }
1082
1083    #[test]
1084    fn bundle_state_state_root() {
1085        type PreState = BTreeMap<Address, (Account, BTreeMap<B256, U256>)>;
1086        let mut prestate: PreState = (0..10)
1087            .map(|key| {
1088                let account = Account { nonce: 1, balance: U256::from(key), bytecode_hash: None };
1089                let storage =
1090                    (1..11).map(|key| (B256::with_last_byte(key), U256::from(key))).collect();
1091                (Address::with_last_byte(key), (account, storage))
1092            })
1093            .collect();
1094
1095        let provider_factory = create_test_provider_factory();
1096        let provider_rw = provider_factory.database_provider_rw().unwrap();
1097
1098        // insert initial state to the database
1099        let tx = provider_rw.tx_ref();
1100        for (address, (account, storage)) in &prestate {
1101            let hashed_address = keccak256(address);
1102            tx.put::<tables::HashedAccounts>(hashed_address, *account).unwrap();
1103            for (slot, value) in storage {
1104                tx.put::<tables::HashedStorages>(
1105                    hashed_address,
1106                    StorageEntry { key: keccak256(slot), value: *value },
1107                )
1108                .unwrap();
1109            }
1110        }
1111
1112        let (_, updates) = StateRoot::from_tx(tx).root_with_updates().unwrap();
1113        provider_rw.write_trie_updates(&updates).unwrap();
1114
1115        let mut state = State::builder().with_bundle_update().build();
1116
1117        let assert_state_root = |state: &State<EmptyDB>, expected: &PreState, msg| {
1118            assert_eq!(
1119                StateRoot::overlay_root(
1120                    tx,
1121                    provider_factory.hashed_post_state(&state.bundle_state)
1122                )
1123                .unwrap(),
1124                state_root(expected.clone().into_iter().map(|(address, (account, storage))| (
1125                    address,
1126                    (account, storage.into_iter())
1127                ))),
1128                "{msg}"
1129            );
1130        };
1131
1132        // database only state root is correct
1133        assert_state_root(&state, &prestate, "empty");
1134
1135        // destroy account 1
1136        let address1 = Address::with_last_byte(1);
1137        let account1_old = prestate.remove(&address1).unwrap();
1138        state.insert_account(address1, account1_old.0.into());
1139        state.commit(HashMap::from_iter([(
1140            address1,
1141            RevmAccount {
1142                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
1143                info: RevmAccountInfo::default(),
1144                storage: HashMap::default(),
1145            },
1146        )]));
1147        state.merge_transitions(BundleRetention::PlainState);
1148        assert_state_root(&state, &prestate, "destroyed account");
1149
1150        // change slot 2 in account 2
1151        let address2 = Address::with_last_byte(2);
1152        let slot2 = U256::from(2);
1153        let slot2_key = B256::from(slot2);
1154        let account2 = prestate.get_mut(&address2).unwrap();
1155        let account2_slot2_old_value = *account2.1.get(&slot2_key).unwrap();
1156        state.insert_account_with_storage(
1157            address2,
1158            account2.0.into(),
1159            HashMap::from_iter([(slot2, account2_slot2_old_value)]),
1160        );
1161
1162        let account2_slot2_new_value = U256::from(100);
1163        account2.1.insert(slot2_key, account2_slot2_new_value);
1164        state.commit(HashMap::from_iter([(
1165            address2,
1166            RevmAccount {
1167                status: AccountStatus::Touched,
1168                info: account2.0.into(),
1169                storage: HashMap::from_iter([(
1170                    slot2,
1171                    EvmStorageSlot::new_changed(account2_slot2_old_value, account2_slot2_new_value),
1172                )]),
1173            },
1174        )]));
1175        state.merge_transitions(BundleRetention::PlainState);
1176        assert_state_root(&state, &prestate, "changed storage");
1177
1178        // change balance of account 3
1179        let address3 = Address::with_last_byte(3);
1180        let account3 = prestate.get_mut(&address3).unwrap();
1181        state.insert_account(address3, account3.0.into());
1182
1183        account3.0.balance = U256::from(24);
1184        state.commit(HashMap::from_iter([(
1185            address3,
1186            RevmAccount {
1187                status: AccountStatus::Touched,
1188                info: account3.0.into(),
1189                storage: HashMap::default(),
1190            },
1191        )]));
1192        state.merge_transitions(BundleRetention::PlainState);
1193        assert_state_root(&state, &prestate, "changed balance");
1194
1195        // change nonce of account 4
1196        let address4 = Address::with_last_byte(4);
1197        let account4 = prestate.get_mut(&address4).unwrap();
1198        state.insert_account(address4, account4.0.into());
1199
1200        account4.0.nonce = 128;
1201        state.commit(HashMap::from_iter([(
1202            address4,
1203            RevmAccount {
1204                status: AccountStatus::Touched,
1205                info: account4.0.into(),
1206                storage: HashMap::default(),
1207            },
1208        )]));
1209        state.merge_transitions(BundleRetention::PlainState);
1210        assert_state_root(&state, &prestate, "changed nonce");
1211
1212        // recreate account 1
1213        let account1_new =
1214            Account { nonce: 56, balance: U256::from(123), bytecode_hash: Some(B256::random()) };
1215        prestate.insert(address1, (account1_new, BTreeMap::default()));
1216        state.commit(HashMap::from_iter([(
1217            address1,
1218            RevmAccount {
1219                status: AccountStatus::Touched | AccountStatus::Created,
1220                info: account1_new.into(),
1221                storage: HashMap::default(),
1222            },
1223        )]));
1224        state.merge_transitions(BundleRetention::PlainState);
1225        assert_state_root(&state, &prestate, "recreated");
1226
1227        // update storage for account 1
1228        let slot20 = U256::from(20);
1229        let slot20_key = B256::from(slot20);
1230        let account1_slot20_value = U256::from(12345);
1231        prestate.get_mut(&address1).unwrap().1.insert(slot20_key, account1_slot20_value);
1232        state.commit(HashMap::from_iter([(
1233            address1,
1234            RevmAccount {
1235                status: AccountStatus::Touched | AccountStatus::Created,
1236                info: account1_new.into(),
1237                storage: HashMap::from_iter([(
1238                    slot20,
1239                    EvmStorageSlot::new_changed(U256::ZERO, account1_slot20_value),
1240                )]),
1241            },
1242        )]));
1243        state.merge_transitions(BundleRetention::PlainState);
1244        assert_state_root(&state, &prestate, "recreated changed storage");
1245    }
1246
1247    #[test]
1248    fn prepend_state() {
1249        let address1 = Address::random();
1250        let address2 = Address::random();
1251
1252        let account1 = RevmAccountInfo { nonce: 1, ..Default::default() };
1253        let account1_changed = RevmAccountInfo { nonce: 1, ..Default::default() };
1254        let account2 = RevmAccountInfo { nonce: 1, ..Default::default() };
1255
1256        let present_state = BundleState::builder(2..=2)
1257            .state_present_account_info(address1, account1_changed.clone())
1258            .build();
1259        assert_eq!(present_state.reverts.len(), 1);
1260        let previous_state = BundleState::builder(1..=1)
1261            .state_present_account_info(address1, account1)
1262            .state_present_account_info(address2, account2.clone())
1263            .build();
1264        assert_eq!(previous_state.reverts.len(), 1);
1265
1266        let mut test: ExecutionOutcome = ExecutionOutcome {
1267            bundle: present_state,
1268            receipts: vec![vec![Receipt::default(); 2]; 1],
1269            first_block: 2,
1270            requests: Vec::new(),
1271        };
1272
1273        test.prepend_state(previous_state);
1274
1275        assert_eq!(test.receipts.len(), 1);
1276        let end_state = test.state();
1277        assert_eq!(end_state.state.len(), 2);
1278        // reverts num should stay the same.
1279        assert_eq!(end_state.reverts.len(), 1);
1280        // account1 is not overwritten.
1281        assert_eq!(end_state.state.get(&address1).unwrap().info, Some(account1_changed));
1282        // account2 got inserted
1283        assert_eq!(end_state.state.get(&address2).unwrap().info, Some(account2));
1284    }
1285
1286    #[test]
1287    fn hashed_state_storage_root() {
1288        let address = Address::random();
1289        let hashed_address = keccak256(address);
1290        let provider_factory = create_test_provider_factory();
1291        let provider_rw = provider_factory.provider_rw().unwrap();
1292        let tx = provider_rw.tx_ref();
1293
1294        // insert initial account storage
1295        let init_storage = HashedStorage::from_iter(
1296            false,
1297            [
1298                "50000000000000000000000000000004253371b55351a08cb3267d4d265530b6",
1299                "512428ed685fff57294d1a9cbb147b18ae5db9cf6ae4b312fa1946ba0561882e",
1300                "51e6784c736ef8548f856909870b38e49ef7a4e3e77e5e945e0d5e6fcaa3037f",
1301            ]
1302            .into_iter()
1303            .map(|str| (B256::from_str(str).unwrap(), U256::from(1))),
1304        );
1305        let mut state = HashedPostState::default();
1306        state.storages.insert(hashed_address, init_storage.clone());
1307        provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
1308
1309        // calculate database storage root and write intermediate storage nodes.
1310        let (storage_root, _, storage_updates) =
1311            StorageRoot::from_tx_hashed(tx, hashed_address).calculate(true).unwrap();
1312        assert_eq!(storage_root, storage_root_prehashed(init_storage.storage));
1313        assert!(!storage_updates.is_empty());
1314        provider_rw
1315            .write_individual_storage_trie_updates(hashed_address, &storage_updates)
1316            .unwrap();
1317
1318        // destroy the storage and re-create with new slots
1319        let updated_storage = HashedStorage::from_iter(
1320            true,
1321            [
1322                "00deb8486ad8edccfdedfc07109b3667b38a03a8009271aac250cce062d90917",
1323                "88d233b7380bb1bcdc866f6871c94685848f54cf0ee033b1480310b4ddb75fc9",
1324            ]
1325            .into_iter()
1326            .map(|str| (B256::from_str(str).unwrap(), U256::from(1))),
1327        );
1328        let mut state = HashedPostState::default();
1329        state.storages.insert(hashed_address, updated_storage.clone());
1330        provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
1331
1332        // re-calculate database storage root
1333        let storage_root = StorageRoot::overlay_root(tx, address, updated_storage.clone()).unwrap();
1334        assert_eq!(storage_root, storage_root_prehashed(updated_storage.storage));
1335    }
1336}