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