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