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