reth_provider/providers/database/
mod.rs

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