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