1use crate::{
2 providers::{state::latest::LatestStateProvider, StaticFileProvider},
3 to_range,
4 traits::{BlockSource, ReceiptProvider},
5 BlockHashReader, BlockNumReader, BlockReader, ChainSpecProvider, DatabaseProviderFactory,
6 HashedPostStateProvider, HeaderProvider, 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};
38
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 local_tip_header(
232 &self,
233 highest_uninterrupted_block: BlockNumber,
234 ) -> ProviderResult<SealedHeader<Self::Header>> {
235 self.provider()?.local_tip_header(highest_uninterrupted_block)
236 }
237}
238
239impl<N: ProviderNodeTypes> HeaderProvider for ProviderFactory<N> {
240 type Header = HeaderTy<N>;
241
242 fn header(&self, block_hash: &BlockHash) -> ProviderResult<Option<Self::Header>> {
243 self.provider()?.header(block_hash)
244 }
245
246 fn header_by_number(&self, num: BlockNumber) -> ProviderResult<Option<Self::Header>> {
247 self.static_file_provider.get_with_static_file_or_database(
248 StaticFileSegment::Headers,
249 num,
250 |static_file| static_file.header_by_number(num),
251 || self.provider()?.header_by_number(num),
252 )
253 }
254
255 fn header_td(&self, hash: &BlockHash) -> ProviderResult<Option<U256>> {
256 self.provider()?.header_td(hash)
257 }
258
259 fn header_td_by_number(&self, number: BlockNumber) -> ProviderResult<Option<U256>> {
260 self.provider()?.header_td_by_number(number)
261 }
262
263 fn headers_range(
264 &self,
265 range: impl RangeBounds<BlockNumber>,
266 ) -> ProviderResult<Vec<Self::Header>> {
267 self.static_file_provider.get_range_with_static_file_or_database(
268 StaticFileSegment::Headers,
269 to_range(range),
270 |static_file, range, _| static_file.headers_range(range),
271 |range, _| self.provider()?.headers_range(range),
272 |_| true,
273 )
274 }
275
276 fn sealed_header(
277 &self,
278 number: BlockNumber,
279 ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
280 self.static_file_provider.get_with_static_file_or_database(
281 StaticFileSegment::Headers,
282 number,
283 |static_file| static_file.sealed_header(number),
284 || self.provider()?.sealed_header(number),
285 )
286 }
287
288 fn sealed_headers_range(
289 &self,
290 range: impl RangeBounds<BlockNumber>,
291 ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
292 self.sealed_headers_while(range, |_| true)
293 }
294
295 fn sealed_headers_while(
296 &self,
297 range: impl RangeBounds<BlockNumber>,
298 predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
299 ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
300 self.static_file_provider.get_range_with_static_file_or_database(
301 StaticFileSegment::Headers,
302 to_range(range),
303 |static_file, range, predicate| static_file.sealed_headers_while(range, predicate),
304 |range, predicate| self.provider()?.sealed_headers_while(range, predicate),
305 predicate,
306 )
307 }
308}
309
310impl<N: ProviderNodeTypes> BlockHashReader for ProviderFactory<N> {
311 fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
312 self.static_file_provider.get_with_static_file_or_database(
313 StaticFileSegment::Headers,
314 number,
315 |static_file| static_file.block_hash(number),
316 || self.provider()?.block_hash(number),
317 )
318 }
319
320 fn canonical_hashes_range(
321 &self,
322 start: BlockNumber,
323 end: BlockNumber,
324 ) -> ProviderResult<Vec<B256>> {
325 self.static_file_provider.get_range_with_static_file_or_database(
326 StaticFileSegment::Headers,
327 start..end,
328 |static_file, range, _| static_file.canonical_hashes_range(range.start, range.end),
329 |range, _| self.provider()?.canonical_hashes_range(range.start, range.end),
330 |_| true,
331 )
332 }
333}
334
335impl<N: ProviderNodeTypes> BlockNumReader for ProviderFactory<N> {
336 fn chain_info(&self) -> ProviderResult<ChainInfo> {
337 self.provider()?.chain_info()
338 }
339
340 fn best_block_number(&self) -> ProviderResult<BlockNumber> {
341 self.provider()?.best_block_number()
342 }
343
344 fn last_block_number(&self) -> ProviderResult<BlockNumber> {
345 self.provider()?.last_block_number()
346 }
347
348 fn block_number(&self, hash: B256) -> ProviderResult<Option<BlockNumber>> {
349 self.provider()?.block_number(hash)
350 }
351}
352
353impl<N: ProviderNodeTypes> BlockReader for ProviderFactory<N> {
354 type Block = BlockTy<N>;
355
356 fn find_block_by_hash(
357 &self,
358 hash: B256,
359 source: BlockSource,
360 ) -> ProviderResult<Option<Self::Block>> {
361 self.provider()?.find_block_by_hash(hash, source)
362 }
363
364 fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
365 self.provider()?.block(id)
366 }
367
368 fn pending_block(&self) -> ProviderResult<Option<SealedBlock<Self::Block>>> {
369 self.provider()?.pending_block()
370 }
371
372 fn pending_block_with_senders(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
373 self.provider()?.pending_block_with_senders()
374 }
375
376 fn pending_block_and_receipts(
377 &self,
378 ) -> ProviderResult<Option<(SealedBlock<Self::Block>, Vec<Self::Receipt>)>> {
379 self.provider()?.pending_block_and_receipts()
380 }
381
382 fn recovered_block(
383 &self,
384 id: BlockHashOrNumber,
385 transaction_kind: TransactionVariant,
386 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
387 self.provider()?.recovered_block(id, transaction_kind)
388 }
389
390 fn sealed_block_with_senders(
391 &self,
392 id: BlockHashOrNumber,
393 transaction_kind: TransactionVariant,
394 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
395 self.provider()?.sealed_block_with_senders(id, transaction_kind)
396 }
397
398 fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
399 self.provider()?.block_range(range)
400 }
401
402 fn block_with_senders_range(
403 &self,
404 range: RangeInclusive<BlockNumber>,
405 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
406 self.provider()?.block_with_senders_range(range)
407 }
408
409 fn recovered_block_range(
410 &self,
411 range: RangeInclusive<BlockNumber>,
412 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
413 self.provider()?.recovered_block_range(range)
414 }
415}
416
417impl<N: ProviderNodeTypes> TransactionsProvider for ProviderFactory<N> {
418 type Transaction = TxTy<N>;
419
420 fn transaction_id(&self, tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
421 self.provider()?.transaction_id(tx_hash)
422 }
423
424 fn transaction_by_id(&self, id: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
425 self.static_file_provider.get_with_static_file_or_database(
426 StaticFileSegment::Transactions,
427 id,
428 |static_file| static_file.transaction_by_id(id),
429 || self.provider()?.transaction_by_id(id),
430 )
431 }
432
433 fn transaction_by_id_unhashed(
434 &self,
435 id: TxNumber,
436 ) -> ProviderResult<Option<Self::Transaction>> {
437 self.static_file_provider.get_with_static_file_or_database(
438 StaticFileSegment::Transactions,
439 id,
440 |static_file| static_file.transaction_by_id_unhashed(id),
441 || self.provider()?.transaction_by_id_unhashed(id),
442 )
443 }
444
445 fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
446 self.provider()?.transaction_by_hash(hash)
447 }
448
449 fn transaction_by_hash_with_meta(
450 &self,
451 tx_hash: TxHash,
452 ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
453 self.provider()?.transaction_by_hash_with_meta(tx_hash)
454 }
455
456 fn transaction_block(&self, id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
457 self.provider()?.transaction_block(id)
458 }
459
460 fn transactions_by_block(
461 &self,
462 id: BlockHashOrNumber,
463 ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
464 self.provider()?.transactions_by_block(id)
465 }
466
467 fn transactions_by_block_range(
468 &self,
469 range: impl RangeBounds<BlockNumber>,
470 ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
471 self.provider()?.transactions_by_block_range(range)
472 }
473
474 fn transactions_by_tx_range(
475 &self,
476 range: impl RangeBounds<TxNumber>,
477 ) -> ProviderResult<Vec<Self::Transaction>> {
478 self.provider()?.transactions_by_tx_range(range)
479 }
480
481 fn senders_by_tx_range(
482 &self,
483 range: impl RangeBounds<TxNumber>,
484 ) -> ProviderResult<Vec<Address>> {
485 self.provider()?.senders_by_tx_range(range)
486 }
487
488 fn transaction_sender(&self, id: TxNumber) -> ProviderResult<Option<Address>> {
489 self.provider()?.transaction_sender(id)
490 }
491}
492
493impl<N: ProviderNodeTypes> ReceiptProvider for ProviderFactory<N> {
494 type Receipt = ReceiptTy<N>;
495 fn receipt(&self, id: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
496 self.static_file_provider.get_with_static_file_or_database(
497 StaticFileSegment::Receipts,
498 id,
499 |static_file| static_file.receipt(id),
500 || self.provider()?.receipt(id),
501 )
502 }
503
504 fn receipt_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
505 self.provider()?.receipt_by_hash(hash)
506 }
507
508 fn receipts_by_block(
509 &self,
510 block: BlockHashOrNumber,
511 ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
512 self.provider()?.receipts_by_block(block)
513 }
514
515 fn receipts_by_tx_range(
516 &self,
517 range: impl RangeBounds<TxNumber>,
518 ) -> ProviderResult<Vec<Self::Receipt>> {
519 self.static_file_provider.get_range_with_static_file_or_database(
520 StaticFileSegment::Receipts,
521 to_range(range),
522 |static_file, range, _| static_file.receipts_by_tx_range(range),
523 |range, _| self.provider()?.receipts_by_tx_range(range),
524 |_| true,
525 )
526 }
527}
528
529impl<N: ProviderNodeTypes> WithdrawalsProvider for ProviderFactory<N> {
530 fn withdrawals_by_block(
531 &self,
532 id: BlockHashOrNumber,
533 timestamp: u64,
534 ) -> ProviderResult<Option<Withdrawals>> {
535 self.provider()?.withdrawals_by_block(id, timestamp)
536 }
537}
538
539impl<N: ProviderNodeTypes> OmmersProvider for ProviderFactory<N> {
540 fn ommers(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Vec<Self::Header>>> {
541 self.provider()?.ommers(id)
542 }
543}
544
545impl<N: ProviderNodeTypes> BlockBodyIndicesProvider for ProviderFactory<N> {
546 fn block_body_indices(
547 &self,
548 number: BlockNumber,
549 ) -> ProviderResult<Option<StoredBlockBodyIndices>> {
550 self.static_file_provider.get_with_static_file_or_database(
551 StaticFileSegment::BlockMeta,
552 number,
553 |static_file| static_file.block_body_indices(number),
554 || self.provider()?.block_body_indices(number),
555 )
556 }
557
558 fn block_body_indices_range(
559 &self,
560 range: RangeInclusive<BlockNumber>,
561 ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
562 self.static_file_provider.get_range_with_static_file_or_database(
563 StaticFileSegment::BlockMeta,
564 *range.start()..*range.end() + 1,
565 |static_file, range, _| {
566 static_file.block_body_indices_range(range.start..=range.end.saturating_sub(1))
567 },
568 |range, _| {
569 self.provider()?.block_body_indices_range(range.start..=range.end.saturating_sub(1))
570 },
571 |_| true,
572 )
573 }
574}
575
576impl<N: ProviderNodeTypes> StageCheckpointReader for ProviderFactory<N> {
577 fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult<Option<StageCheckpoint>> {
578 self.provider()?.get_stage_checkpoint(id)
579 }
580
581 fn get_stage_checkpoint_progress(&self, id: StageId) -> ProviderResult<Option<Vec<u8>>> {
582 self.provider()?.get_stage_checkpoint_progress(id)
583 }
584 fn get_all_checkpoints(&self) -> ProviderResult<Vec<(String, StageCheckpoint)>> {
585 self.provider()?.get_all_checkpoints()
586 }
587}
588
589impl<N: NodeTypesWithDB> ChainSpecProvider for ProviderFactory<N> {
590 type ChainSpec = N::ChainSpec;
591
592 fn chain_spec(&self) -> Arc<N::ChainSpec> {
593 self.chain_spec.clone()
594 }
595}
596
597impl<N: ProviderNodeTypes> PruneCheckpointReader for ProviderFactory<N> {
598 fn get_prune_checkpoint(
599 &self,
600 segment: PruneSegment,
601 ) -> ProviderResult<Option<PruneCheckpoint>> {
602 self.provider()?.get_prune_checkpoint(segment)
603 }
604
605 fn get_prune_checkpoints(&self) -> ProviderResult<Vec<(PruneSegment, PruneCheckpoint)>> {
606 self.provider()?.get_prune_checkpoints()
607 }
608}
609
610impl<N: ProviderNodeTypes> HashedPostStateProvider for ProviderFactory<N> {
611 fn hashed_post_state(&self, bundle_state: &BundleState) -> HashedPostState {
612 HashedPostState::from_bundle_state::<<N::StateCommitment as StateCommitment>::KeyHasher>(
613 bundle_state.state(),
614 )
615 }
616}
617
618impl<N> fmt::Debug for ProviderFactory<N>
619where
620 N: NodeTypesWithDB<DB: fmt::Debug, ChainSpec: fmt::Debug, Storage: fmt::Debug>,
621{
622 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
623 let Self { db, chain_spec, static_file_provider, prune_modes, storage } = self;
624 f.debug_struct("ProviderFactory")
625 .field("db", &db)
626 .field("chain_spec", &chain_spec)
627 .field("static_file_provider", &static_file_provider)
628 .field("prune_modes", &prune_modes)
629 .field("storage", &storage)
630 .finish()
631 }
632}
633
634impl<N: NodeTypesWithDB> Clone for ProviderFactory<N> {
635 fn clone(&self) -> Self {
636 Self {
637 db: self.db.clone(),
638 chain_spec: self.chain_spec.clone(),
639 static_file_provider: self.static_file_provider.clone(),
640 prune_modes: self.prune_modes.clone(),
641 storage: self.storage.clone(),
642 }
643 }
644}
645
646#[cfg(test)]
647mod tests {
648 use super::*;
649 use crate::{
650 providers::{StaticFileProvider, StaticFileWriter},
651 test_utils::{blocks::TEST_BLOCK, create_test_provider_factory, MockNodeTypesWithDB},
652 BlockHashReader, BlockNumReader, BlockWriter, DBProvider, HeaderSyncGapProvider,
653 StorageLocation, TransactionsProvider,
654 };
655 use alloy_primitives::{TxNumber, B256, U256};
656 use assert_matches::assert_matches;
657 use reth_chainspec::ChainSpecBuilder;
658 use reth_db::{
659 mdbx::DatabaseArguments,
660 test_utils::{create_test_static_files_dir, ERROR_TEMPDIR},
661 };
662 use reth_db_api::tables;
663 use reth_prune_types::{PruneMode, PruneModes};
664 use reth_storage_errors::provider::ProviderError;
665 use reth_testing_utils::generators::{self, random_block, random_header, BlockParams};
666 use std::{ops::RangeInclusive, sync::Arc};
667
668 #[test]
669 fn common_history_provider() {
670 let factory = create_test_provider_factory();
671 let _ = factory.latest();
672 }
673
674 #[test]
675 fn default_chain_info() {
676 let factory = create_test_provider_factory();
677 let provider = factory.provider().unwrap();
678
679 let chain_info = provider.chain_info().expect("should be ok");
680 assert_eq!(chain_info.best_number, 0);
681 assert_eq!(chain_info.best_hash, B256::ZERO);
682 }
683
684 #[test]
685 fn provider_flow() {
686 let factory = create_test_provider_factory();
687 let provider = factory.provider().unwrap();
688 provider.block_hash(0).unwrap();
689 let provider_rw = factory.provider_rw().unwrap();
690 provider_rw.block_hash(0).unwrap();
691 provider.block_hash(0).unwrap();
692 }
693
694 #[test]
695 fn provider_factory_with_database_path() {
696 let chain_spec = ChainSpecBuilder::mainnet().build();
697 let (_static_dir, static_dir_path) = create_test_static_files_dir();
698 let factory = ProviderFactory::<MockNodeTypesWithDB<DatabaseEnv>>::new_with_database_path(
699 tempfile::TempDir::new().expect(ERROR_TEMPDIR).into_path(),
700 Arc::new(chain_spec),
701 DatabaseArguments::new(Default::default()),
702 StaticFileProvider::read_write(static_dir_path).unwrap(),
703 )
704 .unwrap();
705
706 let provider = factory.provider().unwrap();
707 provider.block_hash(0).unwrap();
708 let provider_rw = factory.provider_rw().unwrap();
709 provider_rw.block_hash(0).unwrap();
710 provider.block_hash(0).unwrap();
711 }
712
713 #[test]
714 fn insert_block_with_prune_modes() {
715 let factory = create_test_provider_factory();
716
717 let block = TEST_BLOCK.clone();
718 {
719 let provider = factory.provider_rw().unwrap();
720 assert_matches!(
721 provider
722 .insert_block(block.clone().try_recover().unwrap(), StorageLocation::Database),
723 Ok(_)
724 );
725 assert_matches!(
726 provider.transaction_sender(0), Ok(Some(sender))
727 if sender == block.body().transactions[0].recover_signer().unwrap()
728 );
729 assert_matches!(
730 provider.transaction_id(*block.body().transactions[0].tx_hash()),
731 Ok(Some(0))
732 );
733 }
734
735 {
736 let prune_modes = PruneModes {
737 sender_recovery: Some(PruneMode::Full),
738 transaction_lookup: Some(PruneMode::Full),
739 ..PruneModes::none()
740 };
741 let provider = factory.with_prune_modes(prune_modes).provider_rw().unwrap();
742 assert_matches!(
743 provider
744 .insert_block(block.clone().try_recover().unwrap(), StorageLocation::Database),
745 Ok(_)
746 );
747 assert_matches!(provider.transaction_sender(0), Ok(None));
748 assert_matches!(
749 provider.transaction_id(*block.body().transactions[0].tx_hash()),
750 Ok(None)
751 );
752 }
753 }
754
755 #[test]
756 fn take_block_transaction_range_recover_senders() {
757 let factory = create_test_provider_factory();
758
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 provider = factory.provider_rw().unwrap();
766
767 assert_matches!(
768 provider
769 .insert_block(block.clone().try_recover().unwrap(), StorageLocation::Database),
770 Ok(_)
771 );
772
773 let senders = provider.take::<tables::TransactionSenders>(range.clone());
774 assert_eq!(
775 senders,
776 Ok(range
777 .clone()
778 .map(|tx_number| (
779 tx_number,
780 block.body().transactions[tx_number as usize].recover_signer().unwrap()
781 ))
782 .collect())
783 );
784
785 let db_senders = provider.senders_by_tx_range(range);
786 assert!(matches!(db_senders, Ok(ref v) if v.is_empty()));
787 }
788 }
789
790 #[test]
791 fn header_sync_gap_lookup() {
792 let factory = create_test_provider_factory();
793 let provider = factory.provider_rw().unwrap();
794
795 let mut rng = generators::rng();
796
797 let checkpoint = 0;
799 let head = random_header(&mut rng, 0, None);
800
801 assert_matches!(
803 provider.local_tip_header(checkpoint),
804 Err(ProviderError::HeaderNotFound(block_number))
805 if block_number.as_number().unwrap() == checkpoint
806 );
807
808 let static_file_provider = provider.static_file_provider();
810 let mut static_file_writer =
811 static_file_provider.latest_writer(StaticFileSegment::Headers).unwrap();
812 static_file_writer.append_header(head.header(), U256::ZERO, &head.hash()).unwrap();
813 static_file_writer.commit().unwrap();
814 drop(static_file_writer);
815
816 let local_head = provider.local_tip_header(checkpoint).unwrap();
817
818 assert_eq!(local_head, head);
819 }
820}