1use crate::{
2 providers::{
3 state::latest::LatestStateProvider, NodeTypesForProvider, RocksDBProvider,
4 StaticFileProvider, StaticFileProviderRWRefMut,
5 },
6 to_range,
7 traits::{BlockSource, ReceiptProvider},
8 BalProvider, BalStoreHandle, BlockHashReader, BlockNumReader, BlockReader, ChainSpecProvider,
9 DatabaseProviderFactory, EitherWriterDestination, HashedPostStateProvider, HeaderProvider,
10 HeaderSyncGapProvider, InMemoryBalStore, MetadataProvider, ProviderError,
11 PruneCheckpointReader, RocksDBProviderFactory, StageCheckpointReader, StateProviderBox,
12 StaticFileProviderFactory, StaticFileWriter, 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 notify::{RecommendedWatcher, RecursiveMode, Watcher};
19use parking_lot::RwLock;
20use reth_chainspec::ChainInfo;
21use reth_db::{init_db, mdbx::DatabaseArguments, DatabaseEnv};
22use reth_db_api::{database::Database, models::StoredBlockBodyIndices};
23use reth_errors::{RethError, RethResult};
24use reth_node_types::{
25 BlockTy, HeaderTy, NodeTypesWithDB, NodeTypesWithDBAdapter, ReceiptTy, TxTy,
26};
27use reth_primitives_traits::{RecoveredBlock, SealedHeader};
28use reth_prune_types::{PruneCheckpoint, PruneModes, PruneSegment, MINIMUM_UNWIND_SAFE_DISTANCE};
29use reth_stages_types::{PipelineTarget, StageCheckpoint, StageId};
30use reth_static_file_types::StaticFileSegment;
31use reth_storage_api::{
32 BlockBodyIndicesProvider, ChainStateBlockReader, ChainStateBlockWriter, DBProvider,
33 NodePrimitivesProvider, StorageSettings, StorageSettingsCache, TryIntoHistoricalStateProvider,
34};
35use reth_storage_errors::provider::ProviderResult;
36use reth_trie::HashedPostState;
37use reth_trie_db::ChangesetCache;
38use revm::database::BundleState;
39use std::{
40 ops::{RangeBounds, RangeInclusive},
41 path::Path,
42 sync::{
43 atomic::{AtomicU64, Ordering},
44 Arc, Mutex,
45 },
46};
47use tracing::{info, instrument, trace, warn};
48
49mod provider;
50pub use provider::{
51 CommitOrder, DatabaseProvider, DatabaseProviderRO, DatabaseProviderRW, SaveBlocksMode,
52};
53
54use super::ProviderNodeTypes;
55use reth_trie::KeccakKeyHasher;
56
57mod builder;
58pub use builder::{ProviderFactoryBuilder, ReadOnlyConfig};
59
60mod metrics;
61pub use metrics::DatabaseProviderMetrics;
62
63mod chain;
64pub use chain::*;
65
66struct ReadOnlySyncState {
68 last_synced_txnid: AtomicU64,
70 sync_lock: Mutex<()>,
72}
73
74pub struct ProviderFactory<N: NodeTypesWithDB> {
78 db: N::DB,
80 chain_spec: Arc<N::ChainSpec>,
82 static_file_provider: StaticFileProvider<N::Primitives>,
84 prune_modes: PruneModes,
86 storage: Arc<N::Storage>,
88 storage_settings: Arc<RwLock<StorageSettings>>,
90 rocksdb_provider: RocksDBProvider,
92 changeset_cache: ChangesetCache,
94 bal_store: BalStoreHandle,
96 runtime: reth_tasks::Runtime,
98 minimum_pruning_distance: u64,
100 database_provider_metrics: Arc<DatabaseProviderMetrics>,
102 read_only_sync: Option<Arc<ReadOnlySyncState>>,
107}
108
109impl<N: NodeTypesForProvider> ProviderFactory<NodeTypesWithDBAdapter<N, DatabaseEnv>> {
110 pub fn builder() -> ProviderFactoryBuilder<N> {
112 ProviderFactoryBuilder::default()
113 }
114}
115
116impl<N: ProviderNodeTypes> ProviderFactory<N> {
117 pub fn new(
125 db: N::DB,
126 chain_spec: Arc<N::ChainSpec>,
127 static_file_provider: StaticFileProvider<N::Primitives>,
128 rocksdb_provider: RocksDBProvider,
129 runtime: reth_tasks::Runtime,
130 ) -> ProviderResult<Self> {
131 let legacy_settings = StorageSettings::v1();
136 let database_provider_metrics = Arc::new(DatabaseProviderMetrics::default());
137 let storage_settings = DatabaseProvider::<_, N>::new(
138 db.tx()?,
139 chain_spec.clone(),
140 static_file_provider.clone(),
141 Default::default(),
142 Default::default(),
143 Arc::new(RwLock::new(legacy_settings)),
144 rocksdb_provider.clone(),
145 ChangesetCache::new(),
146 runtime.clone(),
147 db.path(),
148 database_provider_metrics.clone(),
149 )
150 .storage_settings()?
151 .unwrap_or(legacy_settings);
152
153 Ok(Self {
154 db,
155 chain_spec,
156 static_file_provider,
157 prune_modes: PruneModes::default(),
158 storage: Default::default(),
159 storage_settings: Arc::new(RwLock::new(storage_settings)),
160 rocksdb_provider,
161 changeset_cache: ChangesetCache::new(),
162 bal_store: BalStoreHandle::new(InMemoryBalStore::default()),
163 runtime,
164 minimum_pruning_distance: MINIMUM_UNWIND_SAFE_DISTANCE,
165 database_provider_metrics,
166 read_only_sync: None,
167 })
168 }
169
170 pub fn new_checked(
177 db: N::DB,
178 chain_spec: Arc<N::ChainSpec>,
179 static_file_provider: StaticFileProvider<N::Primitives>,
180 rocksdb_provider: RocksDBProvider,
181 runtime: reth_tasks::Runtime,
182 ) -> ProviderResult<Self> {
183 Self::new(db, chain_spec, static_file_provider, rocksdb_provider, runtime)
184 .and_then(Self::assert_consistent)
185 }
186}
187
188impl<N: NodeTypesWithDB> ProviderFactory<N> {
189 pub fn with_prune_modes(mut self, prune_modes: PruneModes) -> Self {
191 self.prune_modes = prune_modes;
192 self
193 }
194
195 pub fn with_bal_store(mut self, bal_store: BalStoreHandle) -> Self {
197 self.bal_store = bal_store;
198 self
199 }
200
201 pub fn with_changeset_cache(mut self, changeset_cache: ChangesetCache) -> Self {
203 self.changeset_cache = changeset_cache;
204 self
205 }
206
207 pub(crate) fn changeset_cache(&self) -> ChangesetCache {
209 self.changeset_cache.clone()
210 }
211
212 pub const fn with_minimum_pruning_distance(mut self, distance: u64) -> Self {
217 self.minimum_pruning_distance = distance;
218 self
219 }
220
221 pub fn with_read_only_sync(mut self, watch: bool) -> Self
227 where
228 N::DB: Database,
229 {
230 let state = Arc::new(ReadOnlySyncState {
234 last_synced_txnid: AtomicU64::new(0),
235 sync_lock: Mutex::new(()),
236 });
237 self.read_only_sync = Some(state);
238
239 if watch {
240 self.watch_db_directory();
241 }
242 self
243 }
244
245 fn watch_db_directory(&self)
248 where
249 N::DB: Database,
250 {
251 let factory = self.clone();
252 let db_path = self.db.path();
253 reth_tasks::spawn_os_thread("ro-sync", move || {
254 let (tx, rx) = std::sync::mpsc::channel();
255 let mut watcher = RecommendedWatcher::new(
256 move |res| {
257 let _ = tx.send(res);
258 },
259 notify::Config::default(),
260 )
261 .expect("failed to create watcher");
262
263 watcher
264 .watch(&db_path, RecursiveMode::NonRecursive)
265 .expect("failed to watch MDBX path");
266
267 while let Ok(res) = rx.recv() {
268 match res {
269 Ok(event) => {
270 if !matches!(
271 event.kind,
272 notify::EventKind::Modify(_) | notify::EventKind::Create(_)
273 ) {
274 continue;
275 }
276
277 if let Err(err) = factory.sync_providers_if_needed() {
278 warn!(target: "reth::provider", %err, "background ro-sync failed");
279 }
280 }
281 Err(err) => {
282 warn!(target: "reth::provider", ?err, "MDBX directory watcher error");
283 }
284 }
285 }
286 });
287 }
288
289 pub fn sync_providers_if_needed(&self) -> ProviderResult<()> {
295 let Some(sync_state) = &self.read_only_sync else { return Ok(()) };
296 let current_txnid = self.db.last_txnid().unwrap_or(0);
297
298 if current_txnid == sync_state.last_synced_txnid.load(Ordering::Relaxed) {
300 return Ok(());
301 }
302
303 let _guard = sync_state.sync_lock.lock().unwrap_or_else(|e| e.into_inner());
305
306 if current_txnid == sync_state.last_synced_txnid.load(Ordering::Relaxed) {
308 return Ok(());
309 }
310
311 self.rocksdb_provider.try_catch_up_with_primary()?;
312 self.static_file_provider.initialize_index()?;
313 sync_state.last_synced_txnid.store(current_txnid, Ordering::Relaxed);
314 Ok(())
315 }
316
317 pub const fn db_ref(&self) -> &N::DB {
319 &self.db
320 }
321
322 #[cfg(any(test, feature = "test-utils"))]
323 pub fn into_db(self) -> N::DB {
325 self.db
326 }
327}
328
329impl<N: NodeTypesWithDB> StorageSettingsCache for ProviderFactory<N> {
330 fn cached_storage_settings(&self) -> StorageSettings {
331 *self.storage_settings.read()
332 }
333
334 fn set_storage_settings_cache(&self, settings: StorageSettings) {
335 *self.storage_settings.write() = settings;
336 }
337}
338
339impl<N: NodeTypesWithDB> RocksDBProviderFactory for ProviderFactory<N> {
340 fn rocksdb_provider(&self) -> RocksDBProvider {
341 self.rocksdb_provider.clone()
342 }
343
344 fn set_pending_rocksdb_batch(&self, _batch: rocksdb::WriteBatchWithTransaction<true>) {
345 unimplemented!("ProviderFactory is a factory, not a provider - use DatabaseProvider::set_pending_rocksdb_batch instead")
346 }
347
348 fn commit_pending_rocksdb_batches(&self) -> ProviderResult<()> {
349 unimplemented!("ProviderFactory is a factory, not a provider - use DatabaseProvider::commit_pending_rocksdb_batches instead")
350 }
351}
352
353impl<N: ProviderNodeTypes<DB = DatabaseEnv>> ProviderFactory<N> {
354 pub fn new_with_database_path<P: AsRef<Path>>(
357 path: P,
358 chain_spec: Arc<N::ChainSpec>,
359 args: DatabaseArguments,
360 static_file_provider: StaticFileProvider<N::Primitives>,
361 rocksdb_provider: RocksDBProvider,
362 runtime: reth_tasks::Runtime,
363 ) -> RethResult<Self> {
364 Self::new(
365 init_db(path, args).map_err(RethError::msg)?,
366 chain_spec,
367 static_file_provider,
368 rocksdb_provider,
369 runtime,
370 )
371 .map_err(RethError::Provider)
372 }
373}
374
375impl<N: ProviderNodeTypes> ProviderFactory<N> {
376 #[track_caller]
383 pub fn provider(&self) -> ProviderResult<DatabaseProviderRO<N::DB, N>> {
384 let db_tx = self.db.tx()?;
385
386 self.sync_providers_if_needed()?;
392
393 Ok(DatabaseProvider::new(
394 db_tx,
395 self.chain_spec.clone(),
396 self.static_file_provider.clone(),
397 self.prune_modes.clone(),
398 self.storage.clone(),
399 self.storage_settings.clone(),
400 self.rocksdb_provider.clone(),
401 self.changeset_cache.clone(),
402 self.runtime.clone(),
403 self.db.path(),
404 self.database_provider_metrics.clone(),
405 )
406 .with_minimum_pruning_distance(self.minimum_pruning_distance))
407 }
408
409 #[track_caller]
414 pub fn provider_rw(&self) -> ProviderResult<DatabaseProviderRW<N::DB, N>> {
415 Ok(DatabaseProviderRW(
416 DatabaseProvider::new_rw(
417 self.db.tx_mut()?,
418 self.chain_spec.clone(),
419 self.static_file_provider.clone(),
420 self.prune_modes.clone(),
421 self.storage.clone(),
422 self.storage_settings.clone(),
423 self.rocksdb_provider.clone(),
424 self.changeset_cache.clone(),
425 self.runtime.clone(),
426 self.db.path(),
427 self.database_provider_metrics.clone(),
428 )
429 .with_reader_txn_tracker(self.db.clone())
430 .with_minimum_pruning_distance(self.minimum_pruning_distance),
431 ))
432 }
433
434 #[track_caller]
441 pub fn unwind_provider_rw(
442 &self,
443 ) -> ProviderResult<DatabaseProvider<<N::DB as Database>::TXMut, N>> {
444 Ok(DatabaseProvider::new_unwind_rw(
445 self.db.tx_mut()?,
446 self.chain_spec.clone(),
447 self.static_file_provider.clone(),
448 self.prune_modes.clone(),
449 self.storage.clone(),
450 self.storage_settings.clone(),
451 self.rocksdb_provider.clone(),
452 self.changeset_cache.clone(),
453 self.runtime.clone(),
454 self.db.path(),
455 self.database_provider_metrics.clone(),
456 )
457 .with_reader_txn_tracker(self.db.clone())
458 .with_minimum_pruning_distance(self.minimum_pruning_distance))
459 }
460
461 #[track_caller]
463 pub fn latest(&self) -> ProviderResult<StateProviderBox> {
464 trace!(target: "providers::db", "Returning latest state provider");
465 Ok(Box::new(LatestStateProvider::new(self.database_provider_ro()?)))
466 }
467
468 pub fn history_by_block_number(
470 &self,
471 block_number: BlockNumber,
472 ) -> ProviderResult<StateProviderBox> {
473 let state_provider = self.provider()?.try_into_history_at_block(block_number)?;
474 trace!(target: "providers::db", ?block_number, "Returning historical state provider for block number");
475 Ok(state_provider)
476 }
477
478 pub fn history_by_block_hash(&self, block_hash: BlockHash) -> ProviderResult<StateProviderBox> {
480 let provider = self.provider()?;
481
482 let block_number = provider
483 .block_number(block_hash)?
484 .ok_or(ProviderError::BlockHashNotFound(block_hash))?;
485
486 let state_provider = provider.try_into_history_at_block(block_number)?;
487 trace!(target: "providers::db", ?block_number, %block_hash, "Returning historical state provider for block hash");
488 Ok(state_provider)
489 }
490
491 pub fn assert_consistent(self) -> ProviderResult<Self> {
496 let (rocksdb_unwind, static_file_unwind) = self.check_consistency()?;
497
498 let source = match (rocksdb_unwind, static_file_unwind) {
499 (None, None) => return Ok(self),
500 (Some(_), Some(_)) => "RocksDB and Static Files",
501 (Some(_), None) => "RocksDB",
502 (None, Some(_)) => "Static Files",
503 };
504
505 Err(ProviderError::MustUnwind {
506 data_source: source,
507 unwind_to: rocksdb_unwind
508 .into_iter()
509 .chain(static_file_unwind)
510 .min()
511 .expect("at least one unwind target must be Some"),
512 })
513 }
514
515 #[instrument(err, skip(self))]
519 pub fn check_consistency(&self) -> ProviderResult<(Option<u64>, Option<u64>)> {
520 let provider_ro = self
521 .database_provider_ro()?
522 .disable_long_read_transaction_safety();
526
527 self.static_file_provider().check_file_consistency(&provider_ro)?;
529
530 let rocksdb_unwind = self.rocksdb_provider().check_consistency(&provider_ro)?;
532
533 let static_file_unwind = self.static_file_provider().check_consistency(&provider_ro)?.map(
535 |target| match target {
536 PipelineTarget::Unwind(block) => block,
537 PipelineTarget::Sync(_) => unreachable!("check_consistency returns Unwind"),
538 },
539 );
540
541 if rocksdb_unwind.is_none() && static_file_unwind.is_none() {
546 self.heal_chain_state_block_numbers(&provider_ro)?;
547 }
548
549 Ok((rocksdb_unwind, static_file_unwind))
550 }
551
552 fn heal_chain_state_block_numbers(
555 &self,
556 provider_ro: &DatabaseProvider<<N::DB as Database>::TX, N>,
557 ) -> ProviderResult<()> {
558 let highest_header = self.last_block_number()?;
559
560 let finalized = provider_ro.last_finalized_block_number()?;
561 let safe = provider_ro.last_safe_block_number()?;
562
563 if finalized.is_none_or(|f| f <= highest_header) && safe.is_none_or(|s| s <= highest_header)
564 {
565 return Ok(());
566 }
567
568 let provider_rw = self.provider_rw()?;
569
570 if let Some(finalized) = finalized.filter(|&f| f > highest_header) {
571 info!(
572 target: "providers::db",
573 finalized,
574 highest_header,
575 "Healing finalized block number",
576 );
577 provider_rw.save_finalized_block_number(highest_header)?;
578 }
579
580 if let Some(safe) = safe.filter(|&s| s > highest_header) {
581 info!(
582 target: "providers::db",
583 safe,
584 highest_header,
585 "Healing safe block number",
586 );
587 provider_rw.save_safe_block_number(highest_header)?;
588 }
589
590 provider_rw.commit()?;
591
592 Ok(())
593 }
594
595 pub fn caught_up_static_file_provider(
598 &self,
599 ) -> ProviderResult<StaticFileProvider<N::Primitives>> {
600 self.sync_providers_if_needed()?;
601 Ok(self.static_file_provider.clone())
602 }
603}
604
605impl<N: NodeTypesWithDB> NodePrimitivesProvider for ProviderFactory<N> {
606 type Primitives = N::Primitives;
607}
608
609impl<N: NodeTypesWithDB> BalProvider for ProviderFactory<N> {
610 fn bal_store(&self) -> &BalStoreHandle {
611 &self.bal_store
612 }
613}
614
615impl<N: ProviderNodeTypes> DatabaseProviderFactory for ProviderFactory<N> {
616 type DB = N::DB;
617 type Provider = DatabaseProvider<<N::DB as Database>::TX, N>;
618 type ProviderRW = DatabaseProvider<<N::DB as Database>::TXMut, N>;
619
620 fn database_provider_ro(&self) -> ProviderResult<Self::Provider> {
621 self.provider()
622 }
623
624 fn database_provider_rw(&self) -> ProviderResult<Self::ProviderRW> {
625 self.provider_rw().map(|provider| provider.0)
626 }
627}
628
629impl<N: NodeTypesWithDB> StaticFileProviderFactory for ProviderFactory<N> {
630 fn static_file_provider(&self) -> StaticFileProvider<Self::Primitives> {
632 self.static_file_provider.clone()
633 }
634
635 fn get_static_file_writer(
636 &self,
637 block: BlockNumber,
638 segment: StaticFileSegment,
639 ) -> ProviderResult<StaticFileProviderRWRefMut<'_, Self::Primitives>> {
640 self.static_file_provider.get_writer(block, segment)
641 }
642}
643
644impl<N: ProviderNodeTypes> HeaderSyncGapProvider for ProviderFactory<N> {
645 type Header = HeaderTy<N>;
646 fn local_tip_header(
647 &self,
648 highest_uninterrupted_block: BlockNumber,
649 ) -> ProviderResult<SealedHeader<Self::Header>> {
650 self.provider()?.local_tip_header(highest_uninterrupted_block)
651 }
652}
653
654impl<N: ProviderNodeTypes> HeaderProvider for ProviderFactory<N> {
655 type Header = HeaderTy<N>;
656
657 fn header(&self, block_hash: BlockHash) -> ProviderResult<Option<Self::Header>> {
658 self.provider()?.header(block_hash)
659 }
660
661 fn header_by_number(&self, num: BlockNumber) -> ProviderResult<Option<Self::Header>> {
662 self.caught_up_static_file_provider()?.header_by_number(num)
663 }
664
665 fn headers_range(
666 &self,
667 range: impl RangeBounds<BlockNumber>,
668 ) -> ProviderResult<Vec<Self::Header>> {
669 self.caught_up_static_file_provider()?.headers_range(range)
670 }
671
672 fn sealed_header(
673 &self,
674 number: BlockNumber,
675 ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
676 self.caught_up_static_file_provider()?.sealed_header(number)
677 }
678
679 fn sealed_headers_range(
680 &self,
681 range: impl RangeBounds<BlockNumber>,
682 ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
683 self.caught_up_static_file_provider()?.sealed_headers_range(range)
684 }
685
686 fn sealed_headers_while(
687 &self,
688 range: impl RangeBounds<BlockNumber>,
689 predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
690 ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
691 self.caught_up_static_file_provider()?.sealed_headers_while(range, predicate)
692 }
693}
694
695impl<N: ProviderNodeTypes> BlockHashReader for ProviderFactory<N> {
696 fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
697 self.caught_up_static_file_provider()?.block_hash(number)
698 }
699
700 fn canonical_hashes_range(
701 &self,
702 start: BlockNumber,
703 end: BlockNumber,
704 ) -> ProviderResult<Vec<B256>> {
705 self.caught_up_static_file_provider()?.canonical_hashes_range(start, end)
706 }
707}
708
709impl<N: ProviderNodeTypes> BlockNumReader for ProviderFactory<N> {
710 fn chain_info(&self) -> ProviderResult<ChainInfo> {
711 self.provider()?.chain_info()
712 }
713
714 fn best_block_number(&self) -> ProviderResult<BlockNumber> {
715 self.provider()?.best_block_number()
716 }
717
718 fn last_block_number(&self) -> ProviderResult<BlockNumber> {
719 self.caught_up_static_file_provider()?.last_block_number()
720 }
721
722 fn earliest_block_number(&self) -> ProviderResult<BlockNumber> {
723 Ok(self.caught_up_static_file_provider()?.earliest_history_height())
726 }
727
728 fn block_number(&self, hash: B256) -> ProviderResult<Option<BlockNumber>> {
729 self.provider()?.block_number(hash)
730 }
731}
732
733impl<N: ProviderNodeTypes> BlockReader for ProviderFactory<N> {
734 type Block = BlockTy<N>;
735
736 fn find_block_by_hash(
737 &self,
738 hash: B256,
739 source: BlockSource,
740 ) -> ProviderResult<Option<Self::Block>> {
741 self.provider()?.find_block_by_hash(hash, source)
742 }
743
744 fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
745 self.provider()?.block(id)
746 }
747
748 fn pending_block(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
749 self.provider()?.pending_block()
750 }
751
752 fn pending_block_and_receipts(
753 &self,
754 ) -> ProviderResult<Option<(RecoveredBlock<Self::Block>, Vec<Self::Receipt>)>> {
755 self.provider()?.pending_block_and_receipts()
756 }
757
758 fn recovered_block(
759 &self,
760 id: BlockHashOrNumber,
761 transaction_kind: TransactionVariant,
762 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
763 self.provider()?.recovered_block(id, transaction_kind)
764 }
765
766 fn sealed_block_with_senders(
767 &self,
768 id: BlockHashOrNumber,
769 transaction_kind: TransactionVariant,
770 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
771 self.provider()?.sealed_block_with_senders(id, transaction_kind)
772 }
773
774 fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
775 self.provider()?.block_range(range)
776 }
777
778 fn block_with_senders_range(
779 &self,
780 range: RangeInclusive<BlockNumber>,
781 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
782 self.provider()?.block_with_senders_range(range)
783 }
784
785 fn recovered_block_range(
786 &self,
787 range: RangeInclusive<BlockNumber>,
788 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
789 self.provider()?.recovered_block_range(range)
790 }
791
792 fn block_by_transaction_id(&self, id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
793 self.provider()?.block_by_transaction_id(id)
794 }
795}
796
797impl<N: ProviderNodeTypes> TransactionsProvider for ProviderFactory<N> {
798 type Transaction = TxTy<N>;
799
800 fn transaction_id(&self, tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
801 self.provider()?.transaction_id(tx_hash)
802 }
803
804 fn transaction_by_id(&self, id: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
805 self.caught_up_static_file_provider()?.transaction_by_id(id)
806 }
807
808 fn transaction_by_id_unhashed(
809 &self,
810 id: TxNumber,
811 ) -> ProviderResult<Option<Self::Transaction>> {
812 self.caught_up_static_file_provider()?.transaction_by_id_unhashed(id)
813 }
814
815 fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
816 self.provider()?.transaction_by_hash(hash)
817 }
818
819 fn transaction_by_hash_with_meta(
820 &self,
821 tx_hash: TxHash,
822 ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
823 self.provider()?.transaction_by_hash_with_meta(tx_hash)
824 }
825
826 fn transactions_by_block(
827 &self,
828 id: BlockHashOrNumber,
829 ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
830 self.provider()?.transactions_by_block(id)
831 }
832
833 fn transactions_by_block_range(
834 &self,
835 range: impl RangeBounds<BlockNumber>,
836 ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
837 self.provider()?.transactions_by_block_range(range)
838 }
839
840 fn transactions_by_tx_range(
841 &self,
842 range: impl RangeBounds<TxNumber>,
843 ) -> ProviderResult<Vec<Self::Transaction>> {
844 self.caught_up_static_file_provider()?.transactions_by_tx_range(range)
845 }
846
847 fn senders_by_tx_range(
848 &self,
849 range: impl RangeBounds<TxNumber>,
850 ) -> ProviderResult<Vec<Address>> {
851 if EitherWriterDestination::senders(self).is_static_file() {
852 self.caught_up_static_file_provider()?.senders_by_tx_range(range)
853 } else {
854 self.provider()?.senders_by_tx_range(range)
855 }
856 }
857
858 fn transaction_sender(&self, id: TxNumber) -> ProviderResult<Option<Address>> {
859 if EitherWriterDestination::senders(self).is_static_file() {
860 self.caught_up_static_file_provider()?.transaction_sender(id)
861 } else {
862 self.provider()?.transaction_sender(id)
863 }
864 }
865}
866
867impl<N: ProviderNodeTypes> ReceiptProvider for ProviderFactory<N> {
868 type Receipt = ReceiptTy<N>;
869
870 fn receipt(&self, id: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
871 self.caught_up_static_file_provider()?.get_with_static_file_or_database(
872 StaticFileSegment::Receipts,
873 id,
874 |static_file| static_file.receipt(id),
875 || self.provider()?.receipt(id),
876 )
877 }
878
879 fn receipt_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
880 self.provider()?.receipt_by_hash(hash)
881 }
882
883 fn receipts_by_block(
884 &self,
885 block: BlockHashOrNumber,
886 ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
887 self.provider()?.receipts_by_block(block)
888 }
889
890 fn receipts_by_tx_range(
891 &self,
892 range: impl RangeBounds<TxNumber>,
893 ) -> ProviderResult<Vec<Self::Receipt>> {
894 self.caught_up_static_file_provider()?.get_range_with_static_file_or_database(
895 StaticFileSegment::Receipts,
896 to_range(range),
897 |static_file, range, _| static_file.receipts_by_tx_range(range),
898 |range, _| self.provider()?.receipts_by_tx_range(range),
899 |_| true,
900 )
901 }
902
903 fn receipts_by_block_range(
904 &self,
905 block_range: RangeInclusive<BlockNumber>,
906 ) -> ProviderResult<Vec<Vec<Self::Receipt>>> {
907 self.provider()?.receipts_by_block_range(block_range)
908 }
909}
910
911impl<N: ProviderNodeTypes> BlockBodyIndicesProvider for ProviderFactory<N> {
912 fn block_body_indices(
913 &self,
914 number: BlockNumber,
915 ) -> ProviderResult<Option<StoredBlockBodyIndices>> {
916 self.provider()?.block_body_indices(number)
917 }
918
919 fn block_body_indices_range(
920 &self,
921 range: RangeInclusive<BlockNumber>,
922 ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
923 self.provider()?.block_body_indices_range(range)
924 }
925}
926
927impl<N: ProviderNodeTypes> StageCheckpointReader for ProviderFactory<N> {
928 fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult<Option<StageCheckpoint>> {
929 self.provider()?.get_stage_checkpoint(id)
930 }
931
932 fn get_stage_checkpoint_progress(&self, id: StageId) -> ProviderResult<Option<Vec<u8>>> {
933 self.provider()?.get_stage_checkpoint_progress(id)
934 }
935 fn get_all_checkpoints(&self) -> ProviderResult<Vec<(String, StageCheckpoint)>> {
936 self.provider()?.get_all_checkpoints()
937 }
938}
939
940impl<N: NodeTypesWithDB> ChainSpecProvider for ProviderFactory<N> {
941 type ChainSpec = N::ChainSpec;
942
943 fn chain_spec(&self) -> Arc<N::ChainSpec> {
944 self.chain_spec.clone()
945 }
946}
947
948impl<N: ProviderNodeTypes> PruneCheckpointReader for ProviderFactory<N> {
949 fn get_prune_checkpoint(
950 &self,
951 segment: PruneSegment,
952 ) -> ProviderResult<Option<PruneCheckpoint>> {
953 self.provider()?.get_prune_checkpoint(segment)
954 }
955
956 fn get_prune_checkpoints(&self) -> ProviderResult<Vec<(PruneSegment, PruneCheckpoint)>> {
957 self.provider()?.get_prune_checkpoints()
958 }
959}
960
961impl<N: ProviderNodeTypes> HashedPostStateProvider for ProviderFactory<N> {
962 fn hashed_post_state(&self, bundle_state: &BundleState) -> HashedPostState {
963 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
964 }
965}
966
967impl<N: ProviderNodeTypes> MetadataProvider for ProviderFactory<N> {
968 fn get_metadata(&self, key: &str) -> ProviderResult<Option<Vec<u8>>> {
969 self.provider()?.get_metadata(key)
970 }
971}
972
973impl<N> fmt::Debug for ProviderFactory<N>
974where
975 N: NodeTypesWithDB<DB: fmt::Debug, ChainSpec: fmt::Debug, Storage: fmt::Debug>,
976{
977 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
978 let Self {
979 db,
980 chain_spec,
981 static_file_provider,
982 prune_modes,
983 storage,
984 storage_settings,
985 rocksdb_provider,
986 changeset_cache,
987 bal_store,
988 runtime,
989 minimum_pruning_distance,
990 database_provider_metrics: _,
991 read_only_sync,
992 } = self;
993 f.debug_struct("ProviderFactory")
994 .field("db", &db)
995 .field("chain_spec", &chain_spec)
996 .field("static_file_provider", &static_file_provider)
997 .field("prune_modes", &prune_modes)
998 .field("storage", &storage)
999 .field("storage_settings", &*storage_settings.read())
1000 .field("rocksdb_provider", &rocksdb_provider)
1001 .field("changeset_cache", &changeset_cache)
1002 .field("bal_store", &bal_store)
1003 .field("runtime", &runtime)
1004 .field("minimum_pruning_distance", &minimum_pruning_distance)
1005 .field(
1006 "read_only_sync",
1007 &read_only_sync.as_ref().map(|s| s.last_synced_txnid.load(Ordering::Relaxed)),
1008 )
1009 .finish()
1010 }
1011}
1012
1013impl<N: NodeTypesWithDB> Clone for ProviderFactory<N> {
1014 fn clone(&self) -> Self {
1015 Self {
1016 db: self.db.clone(),
1017 chain_spec: self.chain_spec.clone(),
1018 static_file_provider: self.static_file_provider.clone(),
1019 prune_modes: self.prune_modes.clone(),
1020 storage: self.storage.clone(),
1021 storage_settings: self.storage_settings.clone(),
1022 rocksdb_provider: self.rocksdb_provider.clone(),
1023 changeset_cache: self.changeset_cache.clone(),
1024 bal_store: self.bal_store.clone(),
1025 runtime: self.runtime.clone(),
1026 minimum_pruning_distance: self.minimum_pruning_distance,
1027 database_provider_metrics: self.database_provider_metrics.clone(),
1028 read_only_sync: self.read_only_sync.clone(),
1029 }
1030 }
1031}
1032
1033#[cfg(test)]
1034mod tests {
1035 use super::*;
1036 use crate::{
1037 providers::{StaticFileProvider, StaticFileWriter},
1038 test_utils::{blocks::TEST_BLOCK, create_test_provider_factory, MockNodeTypesWithDB},
1039 BlockHashReader, BlockNumReader, BlockWriter, DBProvider, HeaderSyncGapProvider,
1040 TransactionsProvider,
1041 };
1042 use alloy_primitives::{TxNumber, B256};
1043 use assert_matches::assert_matches;
1044 use reth_chainspec::ChainSpecBuilder;
1045 use reth_db::{
1046 mdbx::DatabaseArguments,
1047 test_utils::{create_test_rocksdb_dir, create_test_static_files_dir, ERROR_TEMPDIR},
1048 };
1049 use reth_db_api::tables;
1050 use reth_primitives_traits::SignerRecoverable;
1051 use reth_prune_types::{PruneMode, PruneModes};
1052 use reth_storage_errors::provider::ProviderError;
1053 use reth_testing_utils::generators::{self, random_block, random_header, BlockParams};
1054 use std::{ops::RangeInclusive, sync::Arc};
1055
1056 #[test]
1057 fn common_history_provider() {
1058 let factory = create_test_provider_factory();
1059 let _ = factory.latest();
1060 }
1061
1062 #[test]
1063 fn default_chain_info() {
1064 let factory = create_test_provider_factory();
1065 let provider = factory.provider().unwrap();
1066
1067 let chain_info = provider.chain_info().expect("should be ok");
1068 assert_eq!(chain_info.best_number, 0);
1069 assert_eq!(chain_info.best_hash, B256::ZERO);
1070 }
1071
1072 #[test]
1073 fn provider_flow() {
1074 let factory = create_test_provider_factory();
1075 let provider = factory.provider().unwrap();
1076 provider.block_hash(0).unwrap();
1077 let provider_rw = factory.provider_rw().unwrap();
1078 provider_rw.block_hash(0).unwrap();
1079 provider.block_hash(0).unwrap();
1080 }
1081
1082 #[test]
1083 fn provider_factory_with_database_path() {
1084 let chain_spec = ChainSpecBuilder::mainnet().build();
1085 let (_static_dir, static_dir_path) = create_test_static_files_dir();
1086 let (_rocksdb_dir, rocksdb_path) = create_test_rocksdb_dir();
1087 let _db_tempdir = tempfile::TempDir::new().expect(ERROR_TEMPDIR);
1088 let factory = ProviderFactory::<MockNodeTypesWithDB<DatabaseEnv>>::new_with_database_path(
1089 _db_tempdir.path(),
1090 Arc::new(chain_spec),
1091 DatabaseArguments::new(Default::default()),
1092 StaticFileProvider::read_write(static_dir_path).unwrap(),
1093 RocksDBProvider::builder(&rocksdb_path).build().unwrap(),
1094 reth_tasks::Runtime::test(),
1095 )
1096 .unwrap();
1097 let provider = factory.provider().unwrap();
1098 provider.block_hash(0).unwrap();
1099 let provider_rw = factory.provider_rw().unwrap();
1100 provider_rw.block_hash(0).unwrap();
1101 provider.block_hash(0).unwrap();
1102 }
1103
1104 #[test]
1105 fn insert_block_with_prune_modes() {
1106 let block = TEST_BLOCK.clone();
1107
1108 {
1109 let factory = create_test_provider_factory();
1110 let provider = factory.provider_rw().unwrap();
1111 assert_matches!(provider.insert_block(&block.clone().try_recover().unwrap()), Ok(_));
1112 assert_matches!(
1113 provider.transaction_sender(0), Ok(Some(sender))
1114 if sender == block.body().transactions[0].recover_signer().unwrap()
1115 );
1116 assert_matches!(
1117 provider.transaction_id(*block.body().transactions[0].tx_hash()),
1118 Ok(Some(0))
1119 );
1120 }
1121
1122 {
1123 let prune_modes = PruneModes {
1124 sender_recovery: Some(PruneMode::Full),
1125 transaction_lookup: Some(PruneMode::Full),
1126 ..PruneModes::default()
1127 };
1128 let factory = create_test_provider_factory().with_prune_modes(prune_modes);
1130 let provider = factory.provider_rw().unwrap();
1131 assert_matches!(provider.insert_block(&block.clone().try_recover().unwrap()), Ok(_));
1132 assert_matches!(provider.transaction_sender(0), Ok(None));
1133 assert_matches!(
1134 provider.transaction_id(*block.body().transactions[0].tx_hash()),
1135 Ok(None)
1136 );
1137 }
1138 }
1139
1140 #[test]
1141 fn take_block_transaction_range_recover_senders() {
1142 let mut rng = generators::rng();
1143 let block =
1144 random_block(&mut rng, 0, BlockParams { tx_count: Some(3), ..Default::default() });
1145
1146 let tx_ranges: Vec<RangeInclusive<TxNumber>> = vec![0..=0, 1..=1, 2..=2, 0..=1, 1..=2];
1147 for range in tx_ranges {
1148 let factory = create_test_provider_factory();
1149 let provider = factory.provider_rw().unwrap();
1150
1151 assert_matches!(provider.insert_block(&block.clone().try_recover().unwrap()), Ok(_));
1152
1153 let senders = provider.take::<tables::TransactionSenders>(range.clone()).unwrap();
1154 assert_eq!(
1155 senders,
1156 range
1157 .clone()
1158 .map(|tx_number| (
1159 tx_number,
1160 block.body().transactions[tx_number as usize].recover_signer().unwrap()
1161 ))
1162 .collect::<Vec<_>>()
1163 );
1164
1165 let db_senders = provider.senders_by_tx_range(range);
1166 assert!(matches!(db_senders, Ok(ref v) if v.is_empty()));
1167 }
1168 }
1169
1170 #[test]
1171 fn header_sync_gap_lookup() {
1172 let factory = create_test_provider_factory();
1173 let provider = factory.provider_rw().unwrap();
1174
1175 let mut rng = generators::rng();
1176
1177 let checkpoint = 0;
1179 let head = random_header(&mut rng, 0, None);
1180
1181 assert_matches!(
1183 provider.local_tip_header(checkpoint),
1184 Err(ProviderError::HeaderNotFound(block_number))
1185 if block_number.as_number().unwrap() == checkpoint
1186 );
1187
1188 let static_file_provider = provider.static_file_provider();
1190 let mut static_file_writer =
1191 static_file_provider.latest_writer(StaticFileSegment::Headers).unwrap();
1192 static_file_writer.append_header(head.header(), &head.hash()).unwrap();
1193 static_file_writer.commit().unwrap();
1194 drop(static_file_writer);
1195
1196 let local_head = provider.local_tip_header(checkpoint).unwrap();
1197
1198 assert_eq!(local_head, head);
1199 }
1200}