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_traits::{NodePrimitives, SignedTransaction};
11use reth_static_file_types::StaticFileSegment;
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    const 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    #[expect(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_ethereum_primitives::Receipt;
238    use reth_execution_types::ExecutionOutcome;
239    use reth_primitives_traits::{Account, StorageEntry};
240    use reth_storage_api::{DatabaseProviderFactory, HashedPostStateProvider};
241    use reth_trie::{
242        test_utils::{state_root, storage_root_prehashed},
243        HashedPostState, HashedStorage, StateRoot, StorageRoot,
244    };
245    use reth_trie_db::{DatabaseStateRoot, DatabaseStorageRoot};
246    use revm_database::{
247        states::{
248            bundle_state::BundleRetention, changes::PlainStorageRevert, PlainStorageChangeset,
249        },
250        BundleState, State,
251    };
252    use revm_database_interface::{DatabaseCommit, EmptyDB};
253    use revm_state::{
254        Account as RevmAccount, AccountInfo as RevmAccountInfo, AccountStatus, EvmStorageSlot,
255    };
256    use std::{collections::BTreeMap, str::FromStr};
257
258    #[test]
259    fn wiped_entries_are_removed() {
260        let provider_factory = create_test_provider_factory();
261
262        let addresses = (0..10).map(|_| Address::random()).collect::<Vec<_>>();
263        let destroyed_address = *addresses.first().unwrap();
264        let destroyed_address_hashed = keccak256(destroyed_address);
265        let slot = B256::with_last_byte(1);
266        let hashed_slot = keccak256(slot);
267        {
268            let provider_rw = provider_factory.provider_rw().unwrap();
269            let mut accounts_cursor =
270                provider_rw.tx_ref().cursor_write::<tables::HashedAccounts>().unwrap();
271            let mut storage_cursor =
272                provider_rw.tx_ref().cursor_write::<tables::HashedStorages>().unwrap();
273
274            for address in addresses {
275                let hashed_address = keccak256(address);
276                accounts_cursor
277                    .insert(hashed_address, &Account { nonce: 1, ..Default::default() })
278                    .unwrap();
279                storage_cursor
280                    .insert(
281                        hashed_address,
282                        &StorageEntry { key: hashed_slot, value: U256::from(1) },
283                    )
284                    .unwrap();
285            }
286            provider_rw.commit().unwrap();
287        }
288
289        let mut hashed_state = HashedPostState::default();
290        hashed_state.accounts.insert(destroyed_address_hashed, None);
291        hashed_state.storages.insert(destroyed_address_hashed, HashedStorage::new(true));
292
293        let provider_rw = provider_factory.provider_rw().unwrap();
294        assert!(matches!(provider_rw.write_hashed_state(&hashed_state.into_sorted()), Ok(())));
295        provider_rw.commit().unwrap();
296
297        let provider = provider_factory.provider().unwrap();
298        assert_eq!(
299            provider.tx_ref().get::<tables::HashedAccounts>(destroyed_address_hashed),
300            Ok(None)
301        );
302        assert_eq!(
303            provider
304                .tx_ref()
305                .cursor_read::<tables::HashedStorages>()
306                .unwrap()
307                .seek_by_key_subkey(destroyed_address_hashed, hashed_slot),
308            Ok(None)
309        );
310    }
311
312    #[test]
313    fn write_to_db_account_info() {
314        let factory = create_test_provider_factory();
315        let provider = factory.provider_rw().unwrap();
316
317        let address_a = Address::ZERO;
318        let address_b = Address::repeat_byte(0xff);
319
320        let account_a = RevmAccountInfo { balance: U256::from(1), nonce: 1, ..Default::default() };
321        let account_b = RevmAccountInfo { balance: U256::from(2), nonce: 2, ..Default::default() };
322        let account_b_changed =
323            RevmAccountInfo { balance: U256::from(3), nonce: 3, ..Default::default() };
324
325        let mut state = State::builder().with_bundle_update().build();
326        state.insert_not_existing(address_a);
327        state.insert_account(address_b, account_b.clone());
328
329        // 0x00.. is created
330        state.commit(HashMap::from_iter([(
331            address_a,
332            RevmAccount {
333                info: account_a.clone(),
334                status: AccountStatus::Touched | AccountStatus::Created,
335                storage: HashMap::default(),
336            },
337        )]));
338
339        // 0xff.. is changed (balance + 1, nonce + 1)
340        state.commit(HashMap::from_iter([(
341            address_b,
342            RevmAccount {
343                info: account_b_changed.clone(),
344                status: AccountStatus::Touched,
345                storage: HashMap::default(),
346            },
347        )]));
348
349        state.merge_transitions(BundleRetention::Reverts);
350        let mut revm_bundle_state = state.take_bundle();
351
352        // Write plain state and reverts separately.
353        let reverts = revm_bundle_state.take_all_reverts().to_plain_state_reverts();
354        let plain_state = revm_bundle_state.to_plain_state(OriginalValuesKnown::Yes);
355        assert!(plain_state.storage.is_empty());
356        assert!(plain_state.contracts.is_empty());
357        provider.write_state_changes(plain_state).expect("Could not write plain state to DB");
358
359        assert_eq!(reverts.storage, [[]]);
360        provider.write_state_reverts(reverts, 1).expect("Could not write reverts to DB");
361
362        let reth_account_a = account_a.into();
363        let reth_account_b = account_b.into();
364        let reth_account_b_changed = (&account_b_changed).into();
365
366        // Check plain state
367        assert_eq!(
368            provider.basic_account(&address_a).expect("Could not read account state"),
369            Some(reth_account_a),
370            "Account A state is wrong"
371        );
372        assert_eq!(
373            provider.basic_account(&address_b).expect("Could not read account state"),
374            Some(reth_account_b_changed),
375            "Account B state is wrong"
376        );
377
378        // Check change set
379        let mut changeset_cursor = provider
380            .tx_ref()
381            .cursor_dup_read::<tables::AccountChangeSets>()
382            .expect("Could not open changeset cursor");
383        assert_eq!(
384            changeset_cursor.seek_exact(1).expect("Could not read account change set"),
385            Some((1, AccountBeforeTx { address: address_a, info: None })),
386            "Account A changeset is wrong"
387        );
388        assert_eq!(
389            changeset_cursor.next_dup().expect("Changeset table is malformed"),
390            Some((1, AccountBeforeTx { address: address_b, info: Some(reth_account_b) })),
391            "Account B changeset is wrong"
392        );
393
394        let mut state = State::builder().with_bundle_update().build();
395        state.insert_account(address_b, account_b_changed.clone());
396
397        // 0xff.. is destroyed
398        state.commit(HashMap::from_iter([(
399            address_b,
400            RevmAccount {
401                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
402                info: account_b_changed,
403                storage: HashMap::default(),
404            },
405        )]));
406
407        state.merge_transitions(BundleRetention::Reverts);
408        let mut revm_bundle_state = state.take_bundle();
409
410        // Write plain state and reverts separately.
411        let reverts = revm_bundle_state.take_all_reverts().to_plain_state_reverts();
412        let plain_state = revm_bundle_state.to_plain_state(OriginalValuesKnown::Yes);
413        // Account B selfdestructed so flag for it should be present.
414        assert_eq!(
415            plain_state.storage,
416            [PlainStorageChangeset { address: address_b, wipe_storage: true, storage: vec![] }]
417        );
418        assert!(plain_state.contracts.is_empty());
419        provider.write_state_changes(plain_state).expect("Could not write plain state to DB");
420
421        assert_eq!(
422            reverts.storage,
423            [[PlainStorageRevert { address: address_b, wiped: true, storage_revert: vec![] }]]
424        );
425        provider.write_state_reverts(reverts, 2).expect("Could not write reverts to DB");
426
427        // Check new plain state for account B
428        assert_eq!(
429            provider.basic_account(&address_b).expect("Could not read account state"),
430            None,
431            "Account B should be deleted"
432        );
433
434        // Check change set
435        assert_eq!(
436            changeset_cursor.seek_exact(2).expect("Could not read account change set"),
437            Some((2, AccountBeforeTx { address: address_b, info: Some(reth_account_b_changed) })),
438            "Account B changeset is wrong after deletion"
439        );
440    }
441
442    #[test]
443    fn write_to_db_storage() {
444        let factory = create_test_provider_factory();
445        let provider = factory.database_provider_rw().unwrap();
446
447        let address_a = Address::ZERO;
448        let address_b = Address::repeat_byte(0xff);
449
450        let account_b = RevmAccountInfo { balance: U256::from(2), nonce: 2, ..Default::default() };
451
452        let mut state = State::builder().with_bundle_update().build();
453        state.insert_not_existing(address_a);
454        state.insert_account_with_storage(
455            address_b,
456            account_b.clone(),
457            HashMap::from_iter([(U256::from(1), U256::from(1))]),
458        );
459
460        state.commit(HashMap::from_iter([
461            (
462                address_a,
463                RevmAccount {
464                    status: AccountStatus::Touched | AccountStatus::Created,
465                    info: RevmAccountInfo::default(),
466                    // 0x00 => 0 => 1
467                    // 0x01 => 0 => 2
468                    storage: HashMap::from_iter([
469                        (
470                            U256::from(0),
471                            EvmStorageSlot { present_value: U256::from(1), ..Default::default() },
472                        ),
473                        (
474                            U256::from(1),
475                            EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
476                        ),
477                    ]),
478                },
479            ),
480            (
481                address_b,
482                RevmAccount {
483                    status: AccountStatus::Touched,
484                    info: account_b,
485                    // 0x01 => 1 => 2
486                    storage: HashMap::from_iter([(
487                        U256::from(1),
488                        EvmStorageSlot {
489                            present_value: U256::from(2),
490                            original_value: U256::from(1),
491                            ..Default::default()
492                        },
493                    )]),
494                },
495            ),
496        ]));
497
498        state.merge_transitions(BundleRetention::Reverts);
499
500        let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 1, Vec::new());
501        provider
502            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
503            .expect("Could not write bundle state to DB");
504
505        // Check plain storage state
506        let mut storage_cursor = provider
507            .tx_ref()
508            .cursor_dup_read::<tables::PlainStorageState>()
509            .expect("Could not open plain storage state cursor");
510
511        assert_eq!(
512            storage_cursor.seek_exact(address_a).unwrap(),
513            Some((address_a, StorageEntry { key: B256::ZERO, value: U256::from(1) })),
514            "Slot 0 for account A should be 1"
515        );
516        assert_eq!(
517            storage_cursor.next_dup().unwrap(),
518            Some((
519                address_a,
520                StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(2) }
521            )),
522            "Slot 1 for account A should be 2"
523        );
524        assert_eq!(
525            storage_cursor.next_dup().unwrap(),
526            None,
527            "Account A should only have 2 storage slots"
528        );
529
530        assert_eq!(
531            storage_cursor.seek_exact(address_b).unwrap(),
532            Some((
533                address_b,
534                StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(2) }
535            )),
536            "Slot 1 for account B should be 2"
537        );
538        assert_eq!(
539            storage_cursor.next_dup().unwrap(),
540            None,
541            "Account B should only have 1 storage slot"
542        );
543
544        // Check change set
545        let mut changeset_cursor = provider
546            .tx_ref()
547            .cursor_dup_read::<tables::StorageChangeSets>()
548            .expect("Could not open storage changeset cursor");
549        assert_eq!(
550            changeset_cursor.seek_exact(BlockNumberAddress((1, address_a))).unwrap(),
551            Some((
552                BlockNumberAddress((1, address_a)),
553                StorageEntry { key: B256::ZERO, value: U256::from(0) }
554            )),
555            "Slot 0 for account A should have changed from 0"
556        );
557        assert_eq!(
558            changeset_cursor.next_dup().unwrap(),
559            Some((
560                BlockNumberAddress((1, address_a)),
561                StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(0) }
562            )),
563            "Slot 1 for account A should have changed from 0"
564        );
565        assert_eq!(
566            changeset_cursor.next_dup().unwrap(),
567            None,
568            "Account A should only be in the changeset 2 times"
569        );
570
571        assert_eq!(
572            changeset_cursor.seek_exact(BlockNumberAddress((1, address_b))).unwrap(),
573            Some((
574                BlockNumberAddress((1, address_b)),
575                StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(1) }
576            )),
577            "Slot 1 for account B should have changed from 1"
578        );
579        assert_eq!(
580            changeset_cursor.next_dup().unwrap(),
581            None,
582            "Account B should only be in the changeset 1 time"
583        );
584
585        // Delete account A
586        let mut state = State::builder().with_bundle_update().build();
587        state.insert_account(address_a, RevmAccountInfo::default());
588
589        state.commit(HashMap::from_iter([(
590            address_a,
591            RevmAccount {
592                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
593                info: RevmAccountInfo::default(),
594                storage: HashMap::default(),
595            },
596        )]));
597
598        state.merge_transitions(BundleRetention::Reverts);
599        let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 2, Vec::new());
600        provider
601            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
602            .expect("Could not write bundle state to DB");
603
604        assert_eq!(
605            storage_cursor.seek_exact(address_a).unwrap(),
606            None,
607            "Account A should have no storage slots after deletion"
608        );
609
610        assert_eq!(
611            changeset_cursor.seek_exact(BlockNumberAddress((2, address_a))).unwrap(),
612            Some((
613                BlockNumberAddress((2, address_a)),
614                StorageEntry { key: B256::ZERO, value: U256::from(1) }
615            )),
616            "Slot 0 for account A should have changed from 1 on deletion"
617        );
618        assert_eq!(
619            changeset_cursor.next_dup().unwrap(),
620            Some((
621                BlockNumberAddress((2, address_a)),
622                StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(2) }
623            )),
624            "Slot 1 for account A should have changed from 2 on deletion"
625        );
626        assert_eq!(
627            changeset_cursor.next_dup().unwrap(),
628            None,
629            "Account A should only be in the changeset 2 times on deletion"
630        );
631    }
632
633    #[test]
634    fn write_to_db_multiple_selfdestructs() {
635        let factory = create_test_provider_factory();
636        let provider = factory.database_provider_rw().unwrap();
637
638        let address1 = Address::random();
639        let account_info = RevmAccountInfo { nonce: 1, ..Default::default() };
640
641        // Block #0: initial state.
642        let mut init_state = State::builder().with_bundle_update().build();
643        init_state.insert_not_existing(address1);
644        init_state.commit(HashMap::from_iter([(
645            address1,
646            RevmAccount {
647                info: account_info.clone(),
648                status: AccountStatus::Touched | AccountStatus::Created,
649                // 0x00 => 0 => 1
650                // 0x01 => 0 => 2
651                storage: HashMap::from_iter([
652                    (
653                        U256::ZERO,
654                        EvmStorageSlot { present_value: U256::from(1), ..Default::default() },
655                    ),
656                    (
657                        U256::from(1),
658                        EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
659                    ),
660                ]),
661            },
662        )]));
663        init_state.merge_transitions(BundleRetention::Reverts);
664
665        let outcome =
666            ExecutionOutcome::new(init_state.take_bundle(), Default::default(), 0, Vec::new());
667        provider
668            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
669            .expect("Could not write bundle state to DB");
670
671        let mut state = State::builder().with_bundle_update().build();
672        state.insert_account_with_storage(
673            address1,
674            account_info.clone(),
675            HashMap::from_iter([(U256::ZERO, U256::from(1)), (U256::from(1), U256::from(2))]),
676        );
677
678        // Block #1: change storage.
679        state.commit(HashMap::from_iter([(
680            address1,
681            RevmAccount {
682                status: AccountStatus::Touched,
683                info: account_info.clone(),
684                // 0x00 => 1 => 2
685                storage: HashMap::from_iter([(
686                    U256::ZERO,
687                    EvmStorageSlot {
688                        original_value: U256::from(1),
689                        present_value: U256::from(2),
690                        ..Default::default()
691                    },
692                )]),
693            },
694        )]));
695        state.merge_transitions(BundleRetention::Reverts);
696
697        // Block #2: destroy account.
698        state.commit(HashMap::from_iter([(
699            address1,
700            RevmAccount {
701                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
702                info: account_info.clone(),
703                storage: HashMap::default(),
704            },
705        )]));
706        state.merge_transitions(BundleRetention::Reverts);
707
708        // Block #3: re-create account and change storage.
709        state.commit(HashMap::from_iter([(
710            address1,
711            RevmAccount {
712                status: AccountStatus::Touched | AccountStatus::Created,
713                info: account_info.clone(),
714                storage: HashMap::default(),
715            },
716        )]));
717        state.merge_transitions(BundleRetention::Reverts);
718
719        // Block #4: change storage.
720        state.commit(HashMap::from_iter([(
721            address1,
722            RevmAccount {
723                status: AccountStatus::Touched,
724                info: account_info.clone(),
725                // 0x00 => 0 => 2
726                // 0x02 => 0 => 4
727                // 0x06 => 0 => 6
728                storage: HashMap::from_iter([
729                    (
730                        U256::ZERO,
731                        EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
732                    ),
733                    (
734                        U256::from(2),
735                        EvmStorageSlot { present_value: U256::from(4), ..Default::default() },
736                    ),
737                    (
738                        U256::from(6),
739                        EvmStorageSlot { present_value: U256::from(6), ..Default::default() },
740                    ),
741                ]),
742            },
743        )]));
744        state.merge_transitions(BundleRetention::Reverts);
745
746        // Block #5: Destroy account again.
747        state.commit(HashMap::from_iter([(
748            address1,
749            RevmAccount {
750                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
751                info: account_info.clone(),
752                storage: HashMap::default(),
753            },
754        )]));
755        state.merge_transitions(BundleRetention::Reverts);
756
757        // Block #6: Create, change, destroy and re-create in the same block.
758        state.commit(HashMap::from_iter([(
759            address1,
760            RevmAccount {
761                status: AccountStatus::Touched | AccountStatus::Created,
762                info: account_info.clone(),
763                storage: HashMap::default(),
764            },
765        )]));
766        state.commit(HashMap::from_iter([(
767            address1,
768            RevmAccount {
769                status: AccountStatus::Touched,
770                info: account_info.clone(),
771                // 0x00 => 0 => 2
772                storage: HashMap::from_iter([(
773                    U256::ZERO,
774                    EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
775                )]),
776            },
777        )]));
778        state.commit(HashMap::from_iter([(
779            address1,
780            RevmAccount {
781                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
782                info: account_info.clone(),
783                storage: HashMap::default(),
784            },
785        )]));
786        state.commit(HashMap::from_iter([(
787            address1,
788            RevmAccount {
789                status: AccountStatus::Touched | AccountStatus::Created,
790                info: account_info.clone(),
791                storage: HashMap::default(),
792            },
793        )]));
794        state.merge_transitions(BundleRetention::Reverts);
795
796        // Block #7: Change storage.
797        state.commit(HashMap::from_iter([(
798            address1,
799            RevmAccount {
800                status: AccountStatus::Touched,
801                info: account_info,
802                // 0x00 => 0 => 9
803                storage: HashMap::from_iter([(
804                    U256::ZERO,
805                    EvmStorageSlot { present_value: U256::from(9), ..Default::default() },
806                )]),
807            },
808        )]));
809        state.merge_transitions(BundleRetention::Reverts);
810
811        let bundle = state.take_bundle();
812
813        let outcome: ExecutionOutcome =
814            ExecutionOutcome::new(bundle, Default::default(), 1, Vec::new());
815        provider
816            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
817            .expect("Could not write bundle state to DB");
818
819        let mut storage_changeset_cursor = provider
820            .tx_ref()
821            .cursor_dup_read::<tables::StorageChangeSets>()
822            .expect("Could not open plain storage state cursor");
823        let mut storage_changes = storage_changeset_cursor.walk_range(..).unwrap();
824
825        // Iterate through all storage changes
826
827        // Block <number>
828        // <slot>: <expected value before>
829        // ...
830
831        // Block #0
832        // 0x00: 0
833        // 0x01: 0
834        assert_eq!(
835            storage_changes.next(),
836            Some(Ok((
837                BlockNumberAddress((0, address1)),
838                StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO }
839            )))
840        );
841        assert_eq!(
842            storage_changes.next(),
843            Some(Ok((
844                BlockNumberAddress((0, address1)),
845                StorageEntry { key: B256::with_last_byte(1), value: U256::ZERO }
846            )))
847        );
848
849        // Block #1
850        // 0x00: 1
851        assert_eq!(
852            storage_changes.next(),
853            Some(Ok((
854                BlockNumberAddress((1, address1)),
855                StorageEntry { key: B256::with_last_byte(0), value: U256::from(1) }
856            )))
857        );
858
859        // Block #2 (destroyed)
860        // 0x00: 2
861        // 0x01: 2
862        assert_eq!(
863            storage_changes.next(),
864            Some(Ok((
865                BlockNumberAddress((2, address1)),
866                StorageEntry { key: B256::with_last_byte(0), value: U256::from(2) }
867            )))
868        );
869        assert_eq!(
870            storage_changes.next(),
871            Some(Ok((
872                BlockNumberAddress((2, address1)),
873                StorageEntry { key: B256::with_last_byte(1), value: U256::from(2) }
874            )))
875        );
876
877        // Block #3
878        // no storage changes
879
880        // Block #4
881        // 0x00: 0
882        // 0x02: 0
883        // 0x06: 0
884        assert_eq!(
885            storage_changes.next(),
886            Some(Ok((
887                BlockNumberAddress((4, address1)),
888                StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO }
889            )))
890        );
891        assert_eq!(
892            storage_changes.next(),
893            Some(Ok((
894                BlockNumberAddress((4, address1)),
895                StorageEntry { key: B256::with_last_byte(2), value: U256::ZERO }
896            )))
897        );
898        assert_eq!(
899            storage_changes.next(),
900            Some(Ok((
901                BlockNumberAddress((4, address1)),
902                StorageEntry { key: B256::with_last_byte(6), value: U256::ZERO }
903            )))
904        );
905
906        // Block #5 (destroyed)
907        // 0x00: 2
908        // 0x02: 4
909        // 0x06: 6
910        assert_eq!(
911            storage_changes.next(),
912            Some(Ok((
913                BlockNumberAddress((5, address1)),
914                StorageEntry { key: B256::with_last_byte(0), value: U256::from(2) }
915            )))
916        );
917        assert_eq!(
918            storage_changes.next(),
919            Some(Ok((
920                BlockNumberAddress((5, address1)),
921                StorageEntry { key: B256::with_last_byte(2), value: U256::from(4) }
922            )))
923        );
924        assert_eq!(
925            storage_changes.next(),
926            Some(Ok((
927                BlockNumberAddress((5, address1)),
928                StorageEntry { key: B256::with_last_byte(6), value: U256::from(6) }
929            )))
930        );
931
932        // Block #6
933        // no storage changes (only inter block changes)
934
935        // Block #7
936        // 0x00: 0
937        assert_eq!(
938            storage_changes.next(),
939            Some(Ok((
940                BlockNumberAddress((7, address1)),
941                StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO }
942            )))
943        );
944        assert_eq!(storage_changes.next(), None);
945    }
946
947    #[test]
948    fn storage_change_after_selfdestruct_within_block() {
949        let factory = create_test_provider_factory();
950        let provider = factory.database_provider_rw().unwrap();
951
952        let address1 = Address::random();
953        let account1 = RevmAccountInfo { nonce: 1, ..Default::default() };
954
955        // Block #0: initial state.
956        let mut init_state = State::builder().with_bundle_update().build();
957        init_state.insert_not_existing(address1);
958        init_state.commit(HashMap::from_iter([(
959            address1,
960            RevmAccount {
961                info: account1.clone(),
962                status: AccountStatus::Touched | AccountStatus::Created,
963                // 0x00 => 0 => 1
964                // 0x01 => 0 => 2
965                storage: HashMap::from_iter([
966                    (
967                        U256::ZERO,
968                        EvmStorageSlot { present_value: U256::from(1), ..Default::default() },
969                    ),
970                    (
971                        U256::from(1),
972                        EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
973                    ),
974                ]),
975            },
976        )]));
977        init_state.merge_transitions(BundleRetention::Reverts);
978        let outcome =
979            ExecutionOutcome::new(init_state.take_bundle(), Default::default(), 0, Vec::new());
980        provider
981            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
982            .expect("Could not write bundle state to DB");
983
984        let mut state = State::builder().with_bundle_update().build();
985        state.insert_account_with_storage(
986            address1,
987            account1.clone(),
988            HashMap::from_iter([(U256::ZERO, U256::from(1)), (U256::from(1), U256::from(2))]),
989        );
990
991        // Block #1: Destroy, re-create, change storage.
992        state.commit(HashMap::from_iter([(
993            address1,
994            RevmAccount {
995                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
996                info: account1.clone(),
997                storage: HashMap::default(),
998            },
999        )]));
1000
1001        state.commit(HashMap::from_iter([(
1002            address1,
1003            RevmAccount {
1004                status: AccountStatus::Touched | AccountStatus::Created,
1005                info: account1.clone(),
1006                storage: HashMap::default(),
1007            },
1008        )]));
1009
1010        state.commit(HashMap::from_iter([(
1011            address1,
1012            RevmAccount {
1013                status: AccountStatus::Touched,
1014                info: account1,
1015                // 0x01 => 0 => 5
1016                storage: HashMap::from_iter([(
1017                    U256::from(1),
1018                    EvmStorageSlot { present_value: U256::from(5), ..Default::default() },
1019                )]),
1020            },
1021        )]));
1022
1023        // Commit block #1 changes to the database.
1024        state.merge_transitions(BundleRetention::Reverts);
1025        let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 1, Vec::new());
1026        provider
1027            .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
1028            .expect("Could not write bundle state to DB");
1029
1030        let mut storage_changeset_cursor = provider
1031            .tx_ref()
1032            .cursor_dup_read::<tables::StorageChangeSets>()
1033            .expect("Could not open plain storage state cursor");
1034        let range = BlockNumberAddress::range(1..=1);
1035        let mut storage_changes = storage_changeset_cursor.walk_range(range).unwrap();
1036
1037        assert_eq!(
1038            storage_changes.next(),
1039            Some(Ok((
1040                BlockNumberAddress((1, address1)),
1041                StorageEntry { key: B256::with_last_byte(0), value: U256::from(1) }
1042            )))
1043        );
1044        assert_eq!(
1045            storage_changes.next(),
1046            Some(Ok((
1047                BlockNumberAddress((1, address1)),
1048                StorageEntry { key: B256::with_last_byte(1), value: U256::from(2) }
1049            )))
1050        );
1051        assert_eq!(storage_changes.next(), None);
1052    }
1053
1054    #[test]
1055    fn revert_to_indices() {
1056        let base: ExecutionOutcome = ExecutionOutcome {
1057            bundle: BundleState::default(),
1058            receipts: vec![vec![Receipt::default(); 2]; 7],
1059            first_block: 10,
1060            requests: Vec::new(),
1061        };
1062
1063        let mut this = base.clone();
1064        assert!(this.revert_to(10));
1065        assert_eq!(this.receipts.len(), 1);
1066
1067        let mut this = base.clone();
1068        assert!(!this.revert_to(9));
1069        assert_eq!(this.receipts.len(), 7);
1070
1071        let mut this = base.clone();
1072        assert!(this.revert_to(15));
1073        assert_eq!(this.receipts.len(), 6);
1074
1075        let mut this = base.clone();
1076        assert!(this.revert_to(16));
1077        assert_eq!(this.receipts.len(), 7);
1078
1079        let mut this = base;
1080        assert!(!this.revert_to(17));
1081        assert_eq!(this.receipts.len(), 7);
1082    }
1083
1084    #[test]
1085    fn bundle_state_state_root() {
1086        type PreState = BTreeMap<Address, (Account, BTreeMap<B256, U256>)>;
1087        let mut prestate: PreState = (0..10)
1088            .map(|key| {
1089                let account = Account { nonce: 1, balance: U256::from(key), bytecode_hash: None };
1090                let storage =
1091                    (1..11).map(|key| (B256::with_last_byte(key), U256::from(key))).collect();
1092                (Address::with_last_byte(key), (account, storage))
1093            })
1094            .collect();
1095
1096        let provider_factory = create_test_provider_factory();
1097        let provider_rw = provider_factory.database_provider_rw().unwrap();
1098
1099        // insert initial state to the database
1100        let tx = provider_rw.tx_ref();
1101        for (address, (account, storage)) in &prestate {
1102            let hashed_address = keccak256(address);
1103            tx.put::<tables::HashedAccounts>(hashed_address, *account).unwrap();
1104            for (slot, value) in storage {
1105                tx.put::<tables::HashedStorages>(
1106                    hashed_address,
1107                    StorageEntry { key: keccak256(slot), value: *value },
1108                )
1109                .unwrap();
1110            }
1111        }
1112
1113        let (_, updates) = StateRoot::from_tx(tx).root_with_updates().unwrap();
1114        provider_rw.write_trie_updates(&updates).unwrap();
1115
1116        let mut state = State::builder().with_bundle_update().build();
1117
1118        let assert_state_root = |state: &State<EmptyDB>, expected: &PreState, msg| {
1119            assert_eq!(
1120                StateRoot::overlay_root(
1121                    tx,
1122                    provider_factory.hashed_post_state(&state.bundle_state)
1123                )
1124                .unwrap(),
1125                state_root(expected.clone().into_iter().map(|(address, (account, storage))| (
1126                    address,
1127                    (account, storage.into_iter())
1128                ))),
1129                "{msg}"
1130            );
1131        };
1132
1133        // database only state root is correct
1134        assert_state_root(&state, &prestate, "empty");
1135
1136        // destroy account 1
1137        let address1 = Address::with_last_byte(1);
1138        let account1_old = prestate.remove(&address1).unwrap();
1139        state.insert_account(address1, account1_old.0.into());
1140        state.commit(HashMap::from_iter([(
1141            address1,
1142            RevmAccount {
1143                status: AccountStatus::Touched | AccountStatus::SelfDestructed,
1144                info: RevmAccountInfo::default(),
1145                storage: HashMap::default(),
1146            },
1147        )]));
1148        state.merge_transitions(BundleRetention::PlainState);
1149        assert_state_root(&state, &prestate, "destroyed account");
1150
1151        // change slot 2 in account 2
1152        let address2 = Address::with_last_byte(2);
1153        let slot2 = U256::from(2);
1154        let slot2_key = B256::from(slot2);
1155        let account2 = prestate.get_mut(&address2).unwrap();
1156        let account2_slot2_old_value = *account2.1.get(&slot2_key).unwrap();
1157        state.insert_account_with_storage(
1158            address2,
1159            account2.0.into(),
1160            HashMap::from_iter([(slot2, account2_slot2_old_value)]),
1161        );
1162
1163        let account2_slot2_new_value = U256::from(100);
1164        account2.1.insert(slot2_key, account2_slot2_new_value);
1165        state.commit(HashMap::from_iter([(
1166            address2,
1167            RevmAccount {
1168                status: AccountStatus::Touched,
1169                info: account2.0.into(),
1170                storage: HashMap::from_iter([(
1171                    slot2,
1172                    EvmStorageSlot::new_changed(account2_slot2_old_value, account2_slot2_new_value),
1173                )]),
1174            },
1175        )]));
1176        state.merge_transitions(BundleRetention::PlainState);
1177        assert_state_root(&state, &prestate, "changed storage");
1178
1179        // change balance of account 3
1180        let address3 = Address::with_last_byte(3);
1181        let account3 = prestate.get_mut(&address3).unwrap();
1182        state.insert_account(address3, account3.0.into());
1183
1184        account3.0.balance = U256::from(24);
1185        state.commit(HashMap::from_iter([(
1186            address3,
1187            RevmAccount {
1188                status: AccountStatus::Touched,
1189                info: account3.0.into(),
1190                storage: HashMap::default(),
1191            },
1192        )]));
1193        state.merge_transitions(BundleRetention::PlainState);
1194        assert_state_root(&state, &prestate, "changed balance");
1195
1196        // change nonce of account 4
1197        let address4 = Address::with_last_byte(4);
1198        let account4 = prestate.get_mut(&address4).unwrap();
1199        state.insert_account(address4, account4.0.into());
1200
1201        account4.0.nonce = 128;
1202        state.commit(HashMap::from_iter([(
1203            address4,
1204            RevmAccount {
1205                status: AccountStatus::Touched,
1206                info: account4.0.into(),
1207                storage: HashMap::default(),
1208            },
1209        )]));
1210        state.merge_transitions(BundleRetention::PlainState);
1211        assert_state_root(&state, &prestate, "changed nonce");
1212
1213        // recreate account 1
1214        let account1_new =
1215            Account { nonce: 56, balance: U256::from(123), bytecode_hash: Some(B256::random()) };
1216        prestate.insert(address1, (account1_new, BTreeMap::default()));
1217        state.commit(HashMap::from_iter([(
1218            address1,
1219            RevmAccount {
1220                status: AccountStatus::Touched | AccountStatus::Created,
1221                info: account1_new.into(),
1222                storage: HashMap::default(),
1223            },
1224        )]));
1225        state.merge_transitions(BundleRetention::PlainState);
1226        assert_state_root(&state, &prestate, "recreated");
1227
1228        // update storage for account 1
1229        let slot20 = U256::from(20);
1230        let slot20_key = B256::from(slot20);
1231        let account1_slot20_value = U256::from(12345);
1232        prestate.get_mut(&address1).unwrap().1.insert(slot20_key, account1_slot20_value);
1233        state.commit(HashMap::from_iter([(
1234            address1,
1235            RevmAccount {
1236                status: AccountStatus::Touched | AccountStatus::Created,
1237                info: account1_new.into(),
1238                storage: HashMap::from_iter([(
1239                    slot20,
1240                    EvmStorageSlot::new_changed(U256::ZERO, account1_slot20_value),
1241                )]),
1242            },
1243        )]));
1244        state.merge_transitions(BundleRetention::PlainState);
1245        assert_state_root(&state, &prestate, "recreated changed storage");
1246    }
1247
1248    #[test]
1249    fn prepend_state() {
1250        let address1 = Address::random();
1251        let address2 = Address::random();
1252
1253        let account1 = RevmAccountInfo { nonce: 1, ..Default::default() };
1254        let account1_changed = RevmAccountInfo { nonce: 1, ..Default::default() };
1255        let account2 = RevmAccountInfo { nonce: 1, ..Default::default() };
1256
1257        let present_state = BundleState::builder(2..=2)
1258            .state_present_account_info(address1, account1_changed.clone())
1259            .build();
1260        assert_eq!(present_state.reverts.len(), 1);
1261        let previous_state = BundleState::builder(1..=1)
1262            .state_present_account_info(address1, account1)
1263            .state_present_account_info(address2, account2.clone())
1264            .build();
1265        assert_eq!(previous_state.reverts.len(), 1);
1266
1267        let mut test: ExecutionOutcome = ExecutionOutcome {
1268            bundle: present_state,
1269            receipts: vec![vec![Receipt::default(); 2]; 1],
1270            first_block: 2,
1271            requests: Vec::new(),
1272        };
1273
1274        test.prepend_state(previous_state);
1275
1276        assert_eq!(test.receipts.len(), 1);
1277        let end_state = test.state();
1278        assert_eq!(end_state.state.len(), 2);
1279        // reverts num should stay the same.
1280        assert_eq!(end_state.reverts.len(), 1);
1281        // account1 is not overwritten.
1282        assert_eq!(end_state.state.get(&address1).unwrap().info, Some(account1_changed));
1283        // account2 got inserted
1284        assert_eq!(end_state.state.get(&address2).unwrap().info, Some(account2));
1285    }
1286
1287    #[test]
1288    fn hashed_state_storage_root() {
1289        let address = Address::random();
1290        let hashed_address = keccak256(address);
1291        let provider_factory = create_test_provider_factory();
1292        let provider_rw = provider_factory.provider_rw().unwrap();
1293        let tx = provider_rw.tx_ref();
1294
1295        // insert initial account storage
1296        let init_storage = HashedStorage::from_iter(
1297            false,
1298            [
1299                "50000000000000000000000000000004253371b55351a08cb3267d4d265530b6",
1300                "512428ed685fff57294d1a9cbb147b18ae5db9cf6ae4b312fa1946ba0561882e",
1301                "51e6784c736ef8548f856909870b38e49ef7a4e3e77e5e945e0d5e6fcaa3037f",
1302            ]
1303            .into_iter()
1304            .map(|str| (B256::from_str(str).unwrap(), U256::from(1))),
1305        );
1306        let mut state = HashedPostState::default();
1307        state.storages.insert(hashed_address, init_storage.clone());
1308        provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
1309
1310        // calculate database storage root and write intermediate storage nodes.
1311        let (storage_root, _, storage_updates) =
1312            StorageRoot::from_tx_hashed(tx, hashed_address).calculate(true).unwrap();
1313        assert_eq!(storage_root, storage_root_prehashed(init_storage.storage));
1314        assert!(!storage_updates.is_empty());
1315        provider_rw
1316            .write_individual_storage_trie_updates(hashed_address, &storage_updates)
1317            .unwrap();
1318
1319        // destroy the storage and re-create with new slots
1320        let updated_storage = HashedStorage::from_iter(
1321            true,
1322            [
1323                "00deb8486ad8edccfdedfc07109b3667b38a03a8009271aac250cce062d90917",
1324                "88d233b7380bb1bcdc866f6871c94685848f54cf0ee033b1480310b4ddb75fc9",
1325            ]
1326            .into_iter()
1327            .map(|str| (B256::from_str(str).unwrap(), U256::from(1))),
1328        );
1329        let mut state = HashedPostState::default();
1330        state.storages.insert(hashed_address, updated_storage.clone());
1331        provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
1332
1333        // re-calculate database storage root
1334        let storage_root = StorageRoot::overlay_root(tx, address, updated_storage.clone()).unwrap();
1335        assert_eq!(storage_root, storage_root_prehashed(updated_storage.storage));
1336    }
1337}