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