reth_provider/providers/database/
mod.rs

1use crate::{
2    providers::{
3        state::latest::LatestStateProvider, NodeTypesForProvider, RocksDBProvider,
4        StaticFileProvider, StaticFileProviderRWRefMut,
5    },
6    to_range,
7    traits::{BlockSource, ReceiptProvider},
8    BlockHashReader, BlockNumReader, BlockReader, ChainSpecProvider, DatabaseProviderFactory,
9    EitherWriterDestination, HashedPostStateProvider, HeaderProvider, HeaderSyncGapProvider,
10    MetadataProvider, ProviderError, PruneCheckpointReader, RocksDBProviderFactory,
11    StageCheckpointReader, StateProviderBox, StaticFileProviderFactory, StaticFileWriter,
12    TransactionVariant, TransactionsProvider,
13};
14use alloy_consensus::transaction::TransactionMeta;
15use alloy_eips::BlockHashOrNumber;
16use alloy_primitives::{Address, BlockHash, BlockNumber, TxHash, TxNumber, B256};
17use core::fmt;
18use parking_lot::RwLock;
19use reth_chainspec::ChainInfo;
20use reth_db::{init_db, mdbx::DatabaseArguments, DatabaseEnv};
21use reth_db_api::{database::Database, models::StoredBlockBodyIndices};
22use reth_errors::{RethError, RethResult};
23use reth_node_types::{
24    BlockTy, HeaderTy, NodeTypesWithDB, NodeTypesWithDBAdapter, ReceiptTy, TxTy,
25};
26use reth_primitives_traits::{RecoveredBlock, SealedHeader};
27use reth_prune_types::{PruneCheckpoint, PruneModes, PruneSegment};
28use reth_stages_types::{StageCheckpoint, StageId};
29use reth_static_file_types::StaticFileSegment;
30use reth_storage_api::{
31    BlockBodyIndicesProvider, NodePrimitivesProvider, StorageSettings, StorageSettingsCache,
32    TryIntoHistoricalStateProvider,
33};
34use reth_storage_errors::provider::ProviderResult;
35use reth_trie::HashedPostState;
36use revm_database::BundleState;
37use std::{
38    ops::{RangeBounds, RangeInclusive},
39    path::Path,
40    sync::Arc,
41};
42
43use tracing::trace;
44
45mod provider;
46pub use provider::{DatabaseProvider, DatabaseProviderRO, DatabaseProviderRW};
47
48use super::ProviderNodeTypes;
49use reth_trie::KeccakKeyHasher;
50
51mod builder;
52pub use builder::{ProviderFactoryBuilder, ReadOnlyConfig};
53
54mod metrics;
55
56mod chain;
57pub use chain::*;
58
59/// A common provider that fetches data from a database or static file.
60///
61/// This provider implements most provider or provider factory traits.
62pub struct ProviderFactory<N: NodeTypesWithDB> {
63    /// Database instance
64    db: N::DB,
65    /// Chain spec
66    chain_spec: Arc<N::ChainSpec>,
67    /// Static File Provider
68    static_file_provider: StaticFileProvider<N::Primitives>,
69    /// Optional pruning configuration
70    prune_modes: PruneModes,
71    /// The node storage handler.
72    storage: Arc<N::Storage>,
73    /// Storage configuration settings for this node
74    storage_settings: Arc<RwLock<StorageSettings>>,
75    /// `RocksDB` provider
76    rocksdb_provider: RocksDBProvider,
77}
78
79impl<N: NodeTypesForProvider> ProviderFactory<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>> {
80    /// Instantiates the builder for this type
81    pub fn builder() -> ProviderFactoryBuilder<N> {
82        ProviderFactoryBuilder::default()
83    }
84}
85
86impl<N: ProviderNodeTypes> ProviderFactory<N> {
87    /// Create new database provider factory.
88    pub fn new(
89        db: N::DB,
90        chain_spec: Arc<N::ChainSpec>,
91        static_file_provider: StaticFileProvider<N::Primitives>,
92        rocksdb_provider: RocksDBProvider,
93    ) -> ProviderResult<Self> {
94        // Load storage settings from database at init time. Creates a temporary provider
95        // to read persisted settings, falling back to legacy defaults if none exist.
96        //
97        // Both factory and all providers it creates should share these cached settings.
98        let legacy_settings = StorageSettings::legacy();
99        let storage_settings = DatabaseProvider::<_, N>::new(
100            db.tx()?,
101            chain_spec.clone(),
102            static_file_provider.clone(),
103            Default::default(),
104            Default::default(),
105            Arc::new(RwLock::new(legacy_settings)),
106            rocksdb_provider.clone(),
107        )
108        .storage_settings()?
109        .unwrap_or(legacy_settings);
110
111        Ok(Self {
112            db,
113            chain_spec,
114            static_file_provider,
115            prune_modes: PruneModes::default(),
116            storage: Default::default(),
117            storage_settings: Arc::new(RwLock::new(storage_settings)),
118            rocksdb_provider,
119        })
120    }
121}
122
123impl<N: NodeTypesWithDB> ProviderFactory<N> {
124    /// Sets the pruning configuration for an existing [`ProviderFactory`].
125    pub fn with_prune_modes(mut self, prune_modes: PruneModes) -> Self {
126        self.prune_modes = prune_modes;
127        self
128    }
129
130    /// Returns reference to the underlying database.
131    pub const fn db_ref(&self) -> &N::DB {
132        &self.db
133    }
134
135    #[cfg(any(test, feature = "test-utils"))]
136    /// Consumes Self and returns DB
137    pub fn into_db(self) -> N::DB {
138        self.db
139    }
140}
141
142impl<N: NodeTypesWithDB> StorageSettingsCache for ProviderFactory<N> {
143    fn cached_storage_settings(&self) -> StorageSettings {
144        *self.storage_settings.read()
145    }
146
147    fn set_storage_settings_cache(&self, settings: StorageSettings) {
148        *self.storage_settings.write() = settings;
149    }
150}
151
152impl<N: NodeTypesWithDB> RocksDBProviderFactory for ProviderFactory<N> {
153    fn rocksdb_provider(&self) -> RocksDBProvider {
154        self.rocksdb_provider.clone()
155    }
156
157    #[cfg(all(unix, feature = "rocksdb"))]
158    fn set_pending_rocksdb_batch(&self, _batch: rocksdb::WriteBatchWithTransaction<true>) {
159        unimplemented!("ProviderFactory is a factory, not a provider - use DatabaseProvider::set_pending_rocksdb_batch instead")
160    }
161}
162
163impl<N: ProviderNodeTypes<DB = Arc<DatabaseEnv>>> ProviderFactory<N> {
164    /// Create new database provider by passing a path. [`ProviderFactory`] will own the database
165    /// instance.
166    pub fn new_with_database_path<P: AsRef<Path>>(
167        path: P,
168        chain_spec: Arc<N::ChainSpec>,
169        args: DatabaseArguments,
170        static_file_provider: StaticFileProvider<N::Primitives>,
171        rocksdb_provider: RocksDBProvider,
172    ) -> RethResult<Self> {
173        Self::new(
174            Arc::new(init_db(path, args).map_err(RethError::msg)?),
175            chain_spec,
176            static_file_provider,
177            rocksdb_provider,
178        )
179        .map_err(RethError::Provider)
180    }
181}
182
183impl<N: ProviderNodeTypes> ProviderFactory<N> {
184    /// Returns a provider with a created `DbTx` inside, which allows fetching data from the
185    /// database using different types of providers. Example: [`HeaderProvider`]
186    /// [`BlockHashReader`]. This may fail if the inner read database transaction fails to open.
187    ///
188    /// This sets the [`PruneModes`] to [`None`], because they should only be relevant for writing
189    /// data.
190    #[track_caller]
191    pub fn provider(&self) -> ProviderResult<DatabaseProviderRO<N::DB, N>> {
192        Ok(DatabaseProvider::new(
193            self.db.tx()?,
194            self.chain_spec.clone(),
195            self.static_file_provider.clone(),
196            self.prune_modes.clone(),
197            self.storage.clone(),
198            self.storage_settings.clone(),
199            self.rocksdb_provider.clone(),
200        ))
201    }
202
203    /// Returns a provider with a created `DbTxMut` inside, which allows fetching and updating
204    /// data from the database using different types of providers. Example: [`HeaderProvider`]
205    /// [`BlockHashReader`].  This may fail if the inner read/write database transaction fails to
206    /// open.
207    #[track_caller]
208    pub fn provider_rw(&self) -> ProviderResult<DatabaseProviderRW<N::DB, N>> {
209        Ok(DatabaseProviderRW(DatabaseProvider::new_rw(
210            self.db.tx_mut()?,
211            self.chain_spec.clone(),
212            self.static_file_provider.clone(),
213            self.prune_modes.clone(),
214            self.storage.clone(),
215            self.storage_settings.clone(),
216            self.rocksdb_provider.clone(),
217        )))
218    }
219
220    /// State provider for latest block
221    #[track_caller]
222    pub fn latest(&self) -> ProviderResult<StateProviderBox> {
223        trace!(target: "providers::db", "Returning latest state provider");
224        Ok(Box::new(LatestStateProvider::new(self.database_provider_ro()?)))
225    }
226
227    /// Storage provider for state at that given block
228    pub fn history_by_block_number(
229        &self,
230        block_number: BlockNumber,
231    ) -> ProviderResult<StateProviderBox> {
232        let state_provider = self.provider()?.try_into_history_at_block(block_number)?;
233        trace!(target: "providers::db", ?block_number, "Returning historical state provider for block number");
234        Ok(state_provider)
235    }
236
237    /// Storage provider for state at that given block hash
238    pub fn history_by_block_hash(&self, block_hash: BlockHash) -> ProviderResult<StateProviderBox> {
239        let provider = self.provider()?;
240
241        let block_number = provider
242            .block_number(block_hash)?
243            .ok_or(ProviderError::BlockHashNotFound(block_hash))?;
244
245        let state_provider = provider.try_into_history_at_block(block_number)?;
246        trace!(target: "providers::db", ?block_number, %block_hash, "Returning historical state provider for block hash");
247        Ok(state_provider)
248    }
249}
250
251impl<N: NodeTypesWithDB> NodePrimitivesProvider for ProviderFactory<N> {
252    type Primitives = N::Primitives;
253}
254
255impl<N: ProviderNodeTypes> DatabaseProviderFactory for ProviderFactory<N> {
256    type DB = N::DB;
257    type Provider = DatabaseProvider<<N::DB as Database>::TX, N>;
258    type ProviderRW = DatabaseProvider<<N::DB as Database>::TXMut, N>;
259
260    fn database_provider_ro(&self) -> ProviderResult<Self::Provider> {
261        self.provider()
262    }
263
264    fn database_provider_rw(&self) -> ProviderResult<Self::ProviderRW> {
265        self.provider_rw().map(|provider| provider.0)
266    }
267}
268
269impl<N: NodeTypesWithDB> StaticFileProviderFactory for ProviderFactory<N> {
270    /// Returns static file provider
271    fn static_file_provider(&self) -> StaticFileProvider<Self::Primitives> {
272        self.static_file_provider.clone()
273    }
274
275    fn get_static_file_writer(
276        &self,
277        block: BlockNumber,
278        segment: StaticFileSegment,
279    ) -> ProviderResult<StaticFileProviderRWRefMut<'_, Self::Primitives>> {
280        self.static_file_provider.get_writer(block, segment)
281    }
282}
283
284impl<N: ProviderNodeTypes> HeaderSyncGapProvider for ProviderFactory<N> {
285    type Header = HeaderTy<N>;
286    fn local_tip_header(
287        &self,
288        highest_uninterrupted_block: BlockNumber,
289    ) -> ProviderResult<SealedHeader<Self::Header>> {
290        self.provider()?.local_tip_header(highest_uninterrupted_block)
291    }
292}
293
294impl<N: ProviderNodeTypes> HeaderProvider for ProviderFactory<N> {
295    type Header = HeaderTy<N>;
296
297    fn header(&self, block_hash: BlockHash) -> ProviderResult<Option<Self::Header>> {
298        self.provider()?.header(block_hash)
299    }
300
301    fn header_by_number(&self, num: BlockNumber) -> ProviderResult<Option<Self::Header>> {
302        self.static_file_provider.header_by_number(num)
303    }
304
305    fn headers_range(
306        &self,
307        range: impl RangeBounds<BlockNumber>,
308    ) -> ProviderResult<Vec<Self::Header>> {
309        self.static_file_provider.headers_range(range)
310    }
311
312    fn sealed_header(
313        &self,
314        number: BlockNumber,
315    ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
316        self.static_file_provider.sealed_header(number)
317    }
318
319    fn sealed_headers_range(
320        &self,
321        range: impl RangeBounds<BlockNumber>,
322    ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
323        self.static_file_provider.sealed_headers_range(range)
324    }
325
326    fn sealed_headers_while(
327        &self,
328        range: impl RangeBounds<BlockNumber>,
329        predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
330    ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
331        self.static_file_provider.sealed_headers_while(range, predicate)
332    }
333}
334
335impl<N: ProviderNodeTypes> BlockHashReader for ProviderFactory<N> {
336    fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
337        self.static_file_provider.block_hash(number)
338    }
339
340    fn canonical_hashes_range(
341        &self,
342        start: BlockNumber,
343        end: BlockNumber,
344    ) -> ProviderResult<Vec<B256>> {
345        self.static_file_provider.canonical_hashes_range(start, end)
346    }
347}
348
349impl<N: ProviderNodeTypes> BlockNumReader for ProviderFactory<N> {
350    fn chain_info(&self) -> ProviderResult<ChainInfo> {
351        self.provider()?.chain_info()
352    }
353
354    fn best_block_number(&self) -> ProviderResult<BlockNumber> {
355        self.provider()?.best_block_number()
356    }
357
358    fn last_block_number(&self) -> ProviderResult<BlockNumber> {
359        self.static_file_provider.last_block_number()
360    }
361
362    fn earliest_block_number(&self) -> ProviderResult<BlockNumber> {
363        // earliest history height tracks the lowest block number that has __not__ been expired, in
364        // other words, the first/earliest available block.
365        Ok(self.static_file_provider.earliest_history_height())
366    }
367
368    fn block_number(&self, hash: B256) -> ProviderResult<Option<BlockNumber>> {
369        self.provider()?.block_number(hash)
370    }
371}
372
373impl<N: ProviderNodeTypes> BlockReader for ProviderFactory<N> {
374    type Block = BlockTy<N>;
375
376    fn find_block_by_hash(
377        &self,
378        hash: B256,
379        source: BlockSource,
380    ) -> ProviderResult<Option<Self::Block>> {
381        self.provider()?.find_block_by_hash(hash, source)
382    }
383
384    fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
385        self.provider()?.block(id)
386    }
387
388    fn pending_block(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
389        self.provider()?.pending_block()
390    }
391
392    fn pending_block_and_receipts(
393        &self,
394    ) -> ProviderResult<Option<(RecoveredBlock<Self::Block>, Vec<Self::Receipt>)>> {
395        self.provider()?.pending_block_and_receipts()
396    }
397
398    fn recovered_block(
399        &self,
400        id: BlockHashOrNumber,
401        transaction_kind: TransactionVariant,
402    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
403        self.provider()?.recovered_block(id, transaction_kind)
404    }
405
406    fn sealed_block_with_senders(
407        &self,
408        id: BlockHashOrNumber,
409        transaction_kind: TransactionVariant,
410    ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
411        self.provider()?.sealed_block_with_senders(id, transaction_kind)
412    }
413
414    fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
415        self.provider()?.block_range(range)
416    }
417
418    fn block_with_senders_range(
419        &self,
420        range: RangeInclusive<BlockNumber>,
421    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
422        self.provider()?.block_with_senders_range(range)
423    }
424
425    fn recovered_block_range(
426        &self,
427        range: RangeInclusive<BlockNumber>,
428    ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
429        self.provider()?.recovered_block_range(range)
430    }
431
432    fn block_by_transaction_id(&self, id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
433        self.provider()?.block_by_transaction_id(id)
434    }
435}
436
437impl<N: ProviderNodeTypes> TransactionsProvider for ProviderFactory<N> {
438    type Transaction = TxTy<N>;
439
440    fn transaction_id(&self, tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
441        self.provider()?.transaction_id(tx_hash)
442    }
443
444    fn transaction_by_id(&self, id: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
445        self.static_file_provider.transaction_by_id(id)
446    }
447
448    fn transaction_by_id_unhashed(
449        &self,
450        id: TxNumber,
451    ) -> ProviderResult<Option<Self::Transaction>> {
452        self.static_file_provider.transaction_by_id_unhashed(id)
453    }
454
455    fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
456        self.provider()?.transaction_by_hash(hash)
457    }
458
459    fn transaction_by_hash_with_meta(
460        &self,
461        tx_hash: TxHash,
462    ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
463        self.provider()?.transaction_by_hash_with_meta(tx_hash)
464    }
465
466    fn transactions_by_block(
467        &self,
468        id: BlockHashOrNumber,
469    ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
470        self.provider()?.transactions_by_block(id)
471    }
472
473    fn transactions_by_block_range(
474        &self,
475        range: impl RangeBounds<BlockNumber>,
476    ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
477        self.provider()?.transactions_by_block_range(range)
478    }
479
480    fn transactions_by_tx_range(
481        &self,
482        range: impl RangeBounds<TxNumber>,
483    ) -> ProviderResult<Vec<Self::Transaction>> {
484        self.static_file_provider.transactions_by_tx_range(range)
485    }
486
487    fn senders_by_tx_range(
488        &self,
489        range: impl RangeBounds<TxNumber>,
490    ) -> ProviderResult<Vec<Address>> {
491        if EitherWriterDestination::senders(self).is_static_file() {
492            self.static_file_provider.senders_by_tx_range(range)
493        } else {
494            self.provider()?.senders_by_tx_range(range)
495        }
496    }
497
498    fn transaction_sender(&self, id: TxNumber) -> ProviderResult<Option<Address>> {
499        if EitherWriterDestination::senders(self).is_static_file() {
500            self.static_file_provider.transaction_sender(id)
501        } else {
502            self.provider()?.transaction_sender(id)
503        }
504    }
505}
506
507impl<N: ProviderNodeTypes> ReceiptProvider for ProviderFactory<N> {
508    type Receipt = ReceiptTy<N>;
509
510    fn receipt(&self, id: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
511        self.static_file_provider.get_with_static_file_or_database(
512            StaticFileSegment::Receipts,
513            id,
514            |static_file| static_file.receipt(id),
515            || self.provider()?.receipt(id),
516        )
517    }
518
519    fn receipt_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
520        self.provider()?.receipt_by_hash(hash)
521    }
522
523    fn receipts_by_block(
524        &self,
525        block: BlockHashOrNumber,
526    ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
527        self.provider()?.receipts_by_block(block)
528    }
529
530    fn receipts_by_tx_range(
531        &self,
532        range: impl RangeBounds<TxNumber>,
533    ) -> ProviderResult<Vec<Self::Receipt>> {
534        self.static_file_provider.get_range_with_static_file_or_database(
535            StaticFileSegment::Receipts,
536            to_range(range),
537            |static_file, range, _| static_file.receipts_by_tx_range(range),
538            |range, _| self.provider()?.receipts_by_tx_range(range),
539            |_| true,
540        )
541    }
542
543    fn receipts_by_block_range(
544        &self,
545        block_range: RangeInclusive<BlockNumber>,
546    ) -> ProviderResult<Vec<Vec<Self::Receipt>>> {
547        self.provider()?.receipts_by_block_range(block_range)
548    }
549}
550
551impl<N: ProviderNodeTypes> BlockBodyIndicesProvider for ProviderFactory<N> {
552    fn block_body_indices(
553        &self,
554        number: BlockNumber,
555    ) -> ProviderResult<Option<StoredBlockBodyIndices>> {
556        self.provider()?.block_body_indices(number)
557    }
558
559    fn block_body_indices_range(
560        &self,
561        range: RangeInclusive<BlockNumber>,
562    ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
563        self.provider()?.block_body_indices_range(range)
564    }
565}
566
567impl<N: ProviderNodeTypes> StageCheckpointReader for ProviderFactory<N> {
568    fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult<Option<StageCheckpoint>> {
569        self.provider()?.get_stage_checkpoint(id)
570    }
571
572    fn get_stage_checkpoint_progress(&self, id: StageId) -> ProviderResult<Option<Vec<u8>>> {
573        self.provider()?.get_stage_checkpoint_progress(id)
574    }
575    fn get_all_checkpoints(&self) -> ProviderResult<Vec<(String, StageCheckpoint)>> {
576        self.provider()?.get_all_checkpoints()
577    }
578}
579
580impl<N: NodeTypesWithDB> ChainSpecProvider for ProviderFactory<N> {
581    type ChainSpec = N::ChainSpec;
582
583    fn chain_spec(&self) -> Arc<N::ChainSpec> {
584        self.chain_spec.clone()
585    }
586}
587
588impl<N: ProviderNodeTypes> PruneCheckpointReader for ProviderFactory<N> {
589    fn get_prune_checkpoint(
590        &self,
591        segment: PruneSegment,
592    ) -> ProviderResult<Option<PruneCheckpoint>> {
593        self.provider()?.get_prune_checkpoint(segment)
594    }
595
596    fn get_prune_checkpoints(&self) -> ProviderResult<Vec<(PruneSegment, PruneCheckpoint)>> {
597        self.provider()?.get_prune_checkpoints()
598    }
599}
600
601impl<N: ProviderNodeTypes> HashedPostStateProvider for ProviderFactory<N> {
602    fn hashed_post_state(&self, bundle_state: &BundleState) -> HashedPostState {
603        HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
604    }
605}
606
607impl<N: ProviderNodeTypes> MetadataProvider for ProviderFactory<N> {
608    fn get_metadata(&self, key: &str) -> ProviderResult<Option<Vec<u8>>> {
609        self.provider()?.get_metadata(key)
610    }
611}
612
613impl<N> fmt::Debug for ProviderFactory<N>
614where
615    N: NodeTypesWithDB<DB: fmt::Debug, ChainSpec: fmt::Debug, Storage: fmt::Debug>,
616{
617    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618        let Self {
619            db,
620            chain_spec,
621            static_file_provider,
622            prune_modes,
623            storage,
624            storage_settings,
625            rocksdb_provider,
626        } = self;
627        f.debug_struct("ProviderFactory")
628            .field("db", &db)
629            .field("chain_spec", &chain_spec)
630            .field("static_file_provider", &static_file_provider)
631            .field("prune_modes", &prune_modes)
632            .field("storage", &storage)
633            .field("storage_settings", &*storage_settings.read())
634            .field("rocksdb_provider", &rocksdb_provider)
635            .finish()
636    }
637}
638
639impl<N: NodeTypesWithDB> Clone for ProviderFactory<N> {
640    fn clone(&self) -> Self {
641        Self {
642            db: self.db.clone(),
643            chain_spec: self.chain_spec.clone(),
644            static_file_provider: self.static_file_provider.clone(),
645            prune_modes: self.prune_modes.clone(),
646            storage: self.storage.clone(),
647            storage_settings: self.storage_settings.clone(),
648            rocksdb_provider: self.rocksdb_provider.clone(),
649        }
650    }
651}
652
653#[cfg(test)]
654mod tests {
655    use super::*;
656    use crate::{
657        providers::{StaticFileProvider, StaticFileWriter},
658        test_utils::{blocks::TEST_BLOCK, create_test_provider_factory, MockNodeTypesWithDB},
659        BlockHashReader, BlockNumReader, BlockWriter, DBProvider, HeaderSyncGapProvider,
660        TransactionsProvider,
661    };
662    use alloy_primitives::{TxNumber, B256};
663    use assert_matches::assert_matches;
664    use reth_chainspec::ChainSpecBuilder;
665    use reth_db::{
666        mdbx::DatabaseArguments,
667        test_utils::{create_test_rocksdb_dir, create_test_static_files_dir, ERROR_TEMPDIR},
668    };
669    use reth_db_api::tables;
670    use reth_primitives_traits::SignerRecoverable;
671    use reth_prune_types::{PruneMode, PruneModes};
672    use reth_storage_errors::provider::ProviderError;
673    use reth_testing_utils::generators::{self, random_block, random_header, BlockParams};
674    use std::{ops::RangeInclusive, sync::Arc};
675
676    #[test]
677    fn common_history_provider() {
678        let factory = create_test_provider_factory();
679        let _ = factory.latest();
680    }
681
682    #[test]
683    fn default_chain_info() {
684        let factory = create_test_provider_factory();
685        let provider = factory.provider().unwrap();
686
687        let chain_info = provider.chain_info().expect("should be ok");
688        assert_eq!(chain_info.best_number, 0);
689        assert_eq!(chain_info.best_hash, B256::ZERO);
690    }
691
692    #[test]
693    fn provider_flow() {
694        let factory = create_test_provider_factory();
695        let provider = factory.provider().unwrap();
696        provider.block_hash(0).unwrap();
697        let provider_rw = factory.provider_rw().unwrap();
698        provider_rw.block_hash(0).unwrap();
699        provider.block_hash(0).unwrap();
700    }
701
702    #[test]
703    fn provider_factory_with_database_path() {
704        let chain_spec = ChainSpecBuilder::mainnet().build();
705        let (_static_dir, static_dir_path) = create_test_static_files_dir();
706        let (_, rocksdb_path) = create_test_rocksdb_dir();
707        let factory = ProviderFactory::<MockNodeTypesWithDB<DatabaseEnv>>::new_with_database_path(
708            tempfile::TempDir::new().expect(ERROR_TEMPDIR).keep(),
709            Arc::new(chain_spec),
710            DatabaseArguments::new(Default::default()),
711            StaticFileProvider::read_write(static_dir_path).unwrap(),
712            RocksDBProvider::builder(&rocksdb_path).build().unwrap(),
713        )
714        .unwrap();
715        let provider = factory.provider().unwrap();
716        provider.block_hash(0).unwrap();
717        let provider_rw = factory.provider_rw().unwrap();
718        provider_rw.block_hash(0).unwrap();
719        provider.block_hash(0).unwrap();
720    }
721
722    #[test]
723    fn insert_block_with_prune_modes() {
724        let block = TEST_BLOCK.clone();
725
726        {
727            let factory = create_test_provider_factory();
728            let provider = factory.provider_rw().unwrap();
729            assert_matches!(provider.insert_block(&block.clone().try_recover().unwrap()), Ok(_));
730            assert_matches!(
731                provider.transaction_sender(0), Ok(Some(sender))
732                if sender == block.body().transactions[0].recover_signer().unwrap()
733            );
734            assert_matches!(
735                provider.transaction_id(*block.body().transactions[0].tx_hash()),
736                Ok(Some(0))
737            );
738        }
739
740        {
741            let prune_modes = PruneModes {
742                sender_recovery: Some(PruneMode::Full),
743                transaction_lookup: Some(PruneMode::Full),
744                ..PruneModes::default()
745            };
746            let factory = create_test_provider_factory();
747            let provider = factory.with_prune_modes(prune_modes).provider_rw().unwrap();
748            assert_matches!(provider.insert_block(&block.clone().try_recover().unwrap()), Ok(_));
749            assert_matches!(provider.transaction_sender(0), Ok(None));
750            assert_matches!(
751                provider.transaction_id(*block.body().transactions[0].tx_hash()),
752                Ok(None)
753            );
754        }
755    }
756
757    #[test]
758    fn take_block_transaction_range_recover_senders() {
759        let mut rng = generators::rng();
760        let block =
761            random_block(&mut rng, 0, BlockParams { tx_count: Some(3), ..Default::default() });
762
763        let tx_ranges: Vec<RangeInclusive<TxNumber>> = vec![0..=0, 1..=1, 2..=2, 0..=1, 1..=2];
764        for range in tx_ranges {
765            let factory = create_test_provider_factory();
766            let provider = factory.provider_rw().unwrap();
767
768            assert_matches!(provider.insert_block(&block.clone().try_recover().unwrap()), Ok(_));
769
770            let senders = provider.take::<tables::TransactionSenders>(range.clone());
771            assert_eq!(
772                senders,
773                Ok(range
774                    .clone()
775                    .map(|tx_number| (
776                        tx_number,
777                        block.body().transactions[tx_number as usize].recover_signer().unwrap()
778                    ))
779                    .collect())
780            );
781
782            let db_senders = provider.senders_by_tx_range(range);
783            assert!(matches!(db_senders, Ok(ref v) if v.is_empty()));
784        }
785    }
786
787    #[test]
788    fn header_sync_gap_lookup() {
789        let factory = create_test_provider_factory();
790        let provider = factory.provider_rw().unwrap();
791
792        let mut rng = generators::rng();
793
794        // Genesis
795        let checkpoint = 0;
796        let head = random_header(&mut rng, 0, None);
797
798        // Empty database
799        assert_matches!(
800            provider.local_tip_header(checkpoint),
801            Err(ProviderError::HeaderNotFound(block_number))
802                if block_number.as_number().unwrap() == checkpoint
803        );
804
805        // Checkpoint and no gap
806        let static_file_provider = provider.static_file_provider();
807        let mut static_file_writer =
808            static_file_provider.latest_writer(StaticFileSegment::Headers).unwrap();
809        static_file_writer.append_header(head.header(), &head.hash()).unwrap();
810        static_file_writer.commit().unwrap();
811        drop(static_file_writer);
812
813        let local_head = provider.local_tip_header(checkpoint).unwrap();
814
815        assert_eq!(local_head, head);
816    }
817}