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