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