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: ProviderNodeTypes> MetadataProvider for ProviderFactory<N> {
588 fn get_metadata(&self, key: &str) -> ProviderResult<Option<Vec<u8>>> {
589 self.provider()?.get_metadata(key)
590 }
591}
592
593impl<N> fmt::Debug for ProviderFactory<N>
594where
595 N: NodeTypesWithDB<DB: fmt::Debug, ChainSpec: fmt::Debug, Storage: fmt::Debug>,
596{
597 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
598 let Self { db, chain_spec, static_file_provider, prune_modes, storage, storage_settings } =
599 self;
600 f.debug_struct("ProviderFactory")
601 .field("db", &db)
602 .field("chain_spec", &chain_spec)
603 .field("static_file_provider", &static_file_provider)
604 .field("prune_modes", &prune_modes)
605 .field("storage", &storage)
606 .field("storage_settings", &*storage_settings.read())
607 .finish()
608 }
609}
610
611impl<N: NodeTypesWithDB> Clone for ProviderFactory<N> {
612 fn clone(&self) -> Self {
613 Self {
614 db: self.db.clone(),
615 chain_spec: self.chain_spec.clone(),
616 static_file_provider: self.static_file_provider.clone(),
617 prune_modes: self.prune_modes.clone(),
618 storage: self.storage.clone(),
619 storage_settings: self.storage_settings.clone(),
620 }
621 }
622}
623
624#[cfg(test)]
625mod tests {
626 use super::*;
627 use crate::{
628 providers::{StaticFileProvider, StaticFileWriter},
629 test_utils::{blocks::TEST_BLOCK, create_test_provider_factory, MockNodeTypesWithDB},
630 BlockHashReader, BlockNumReader, BlockWriter, DBProvider, HeaderSyncGapProvider,
631 TransactionsProvider,
632 };
633 use alloy_primitives::{TxNumber, B256};
634 use assert_matches::assert_matches;
635 use reth_chainspec::ChainSpecBuilder;
636 use reth_db::{
637 mdbx::DatabaseArguments,
638 test_utils::{create_test_static_files_dir, ERROR_TEMPDIR},
639 };
640 use reth_db_api::tables;
641 use reth_primitives_traits::SignerRecoverable;
642 use reth_prune_types::{PruneMode, PruneModes};
643 use reth_storage_errors::provider::ProviderError;
644 use reth_testing_utils::generators::{self, random_block, random_header, BlockParams};
645 use std::{ops::RangeInclusive, sync::Arc};
646
647 #[test]
648 fn common_history_provider() {
649 let factory = create_test_provider_factory();
650 let _ = factory.latest();
651 }
652
653 #[test]
654 fn default_chain_info() {
655 let factory = create_test_provider_factory();
656 let provider = factory.provider().unwrap();
657
658 let chain_info = provider.chain_info().expect("should be ok");
659 assert_eq!(chain_info.best_number, 0);
660 assert_eq!(chain_info.best_hash, B256::ZERO);
661 }
662
663 #[test]
664 fn provider_flow() {
665 let factory = create_test_provider_factory();
666 let provider = factory.provider().unwrap();
667 provider.block_hash(0).unwrap();
668 let provider_rw = factory.provider_rw().unwrap();
669 provider_rw.block_hash(0).unwrap();
670 provider.block_hash(0).unwrap();
671 }
672
673 #[test]
674 fn provider_factory_with_database_path() {
675 let chain_spec = ChainSpecBuilder::mainnet().build();
676 let (_static_dir, static_dir_path) = create_test_static_files_dir();
677 let factory = ProviderFactory::<MockNodeTypesWithDB<DatabaseEnv>>::new_with_database_path(
678 tempfile::TempDir::new().expect(ERROR_TEMPDIR).keep(),
679 Arc::new(chain_spec),
680 DatabaseArguments::new(Default::default()),
681 StaticFileProvider::read_write(static_dir_path).unwrap(),
682 )
683 .unwrap();
684 let provider = factory.provider().unwrap();
685 provider.block_hash(0).unwrap();
686 let provider_rw = factory.provider_rw().unwrap();
687 provider_rw.block_hash(0).unwrap();
688 provider.block_hash(0).unwrap();
689 }
690
691 #[test]
692 fn insert_block_with_prune_modes() {
693 let block = TEST_BLOCK.clone();
694
695 {
696 let factory = create_test_provider_factory();
697 let provider = factory.provider_rw().unwrap();
698 assert_matches!(provider.insert_block(block.clone().try_recover().unwrap()), Ok(_));
699 assert_matches!(
700 provider.transaction_sender(0), Ok(Some(sender))
701 if sender == block.body().transactions[0].recover_signer().unwrap()
702 );
703 assert_matches!(
704 provider.transaction_id(*block.body().transactions[0].tx_hash()),
705 Ok(Some(0))
706 );
707 }
708
709 {
710 let prune_modes = PruneModes {
711 sender_recovery: Some(PruneMode::Full),
712 transaction_lookup: Some(PruneMode::Full),
713 ..PruneModes::default()
714 };
715 let factory = create_test_provider_factory();
716 let provider = factory.with_prune_modes(prune_modes).provider_rw().unwrap();
717 assert_matches!(provider.insert_block(block.clone().try_recover().unwrap()), Ok(_));
718 assert_matches!(provider.transaction_sender(0), Ok(None));
719 assert_matches!(
720 provider.transaction_id(*block.body().transactions[0].tx_hash()),
721 Ok(None)
722 );
723 }
724 }
725
726 #[test]
727 fn take_block_transaction_range_recover_senders() {
728 let mut rng = generators::rng();
729 let block =
730 random_block(&mut rng, 0, BlockParams { tx_count: Some(3), ..Default::default() });
731
732 let tx_ranges: Vec<RangeInclusive<TxNumber>> = vec![0..=0, 1..=1, 2..=2, 0..=1, 1..=2];
733 for range in tx_ranges {
734 let factory = create_test_provider_factory();
735 let provider = factory.provider_rw().unwrap();
736
737 assert_matches!(provider.insert_block(block.clone().try_recover().unwrap()), Ok(_));
738
739 let senders = provider.take::<tables::TransactionSenders>(range.clone());
740 assert_eq!(
741 senders,
742 Ok(range
743 .clone()
744 .map(|tx_number| (
745 tx_number,
746 block.body().transactions[tx_number as usize].recover_signer().unwrap()
747 ))
748 .collect())
749 );
750
751 let db_senders = provider.senders_by_tx_range(range);
752 assert!(matches!(db_senders, Ok(ref v) if v.is_empty()));
753 }
754 }
755
756 #[test]
757 fn header_sync_gap_lookup() {
758 let factory = create_test_provider_factory();
759 let provider = factory.provider_rw().unwrap();
760
761 let mut rng = generators::rng();
762
763 let checkpoint = 0;
765 let head = random_header(&mut rng, 0, None);
766
767 assert_matches!(
769 provider.local_tip_header(checkpoint),
770 Err(ProviderError::HeaderNotFound(block_number))
771 if block_number.as_number().unwrap() == checkpoint
772 );
773
774 let static_file_provider = provider.static_file_provider();
776 let mut static_file_writer =
777 static_file_provider.latest_writer(StaticFileSegment::Headers).unwrap();
778 static_file_writer.append_header(head.header(), &head.hash()).unwrap();
779 static_file_writer.commit().unwrap();
780 drop(static_file_writer);
781
782 let local_head = provider.local_tip_header(checkpoint).unwrap();
783
784 assert_eq!(local_head, head);
785 }
786}