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