reth_provider/providers/database/
mod.rs

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