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