reth_network_p2p/headers/downloader.rs
1use super::error::HeadersDownloaderResult;
2use crate::error::{DownloadError, DownloadResult};
3use alloy_consensus::BlockHeader;
4use alloy_eips::{eip1898::BlockWithParent, BlockHashOrNumber};
5use alloy_primitives::B256;
6use futures::Stream;
7use reth_consensus::HeaderValidator;
8use reth_primitives::SealedHeader;
9use std::fmt::Debug;
10
11/// A downloader capable of fetching and yielding block headers.
12///
13/// A downloader represents a distinct strategy for submitting requests to download block headers,
14/// while a [`HeadersClient`][crate::headers::client::HeadersClient] represents a client capable
15/// of fulfilling these requests.
16///
17/// A [`HeaderDownloader`] is a [Stream] that returns batches of headers.
18pub trait HeaderDownloader:
19 Send
20 + Sync
21 + Stream<Item = HeadersDownloaderResult<Vec<SealedHeader<Self::Header>>, Self::Header>>
22 + Unpin
23{
24 /// The header type being downloaded.
25 type Header: Debug + Send + Sync + Unpin + 'static;
26
27 /// Updates the gap to sync which ranges from local head to the sync target
28 ///
29 /// See also [`HeaderDownloader::update_sync_target`] and
30 /// [`HeaderDownloader::update_local_head`]
31 fn update_sync_gap(&mut self, head: SealedHeader<Self::Header>, target: SyncTarget) {
32 self.update_local_head(head);
33 self.update_sync_target(target);
34 }
35
36 /// Updates the block number of the local database
37 fn update_local_head(&mut self, head: SealedHeader<Self::Header>);
38
39 /// Updates the target we want to sync to
40 fn update_sync_target(&mut self, target: SyncTarget);
41
42 /// Sets the headers batch size that the Stream should return.
43 fn set_batch_size(&mut self, limit: usize);
44}
45
46/// Specifies the target to sync for [`HeaderDownloader::update_sync_target`]
47#[derive(Debug, Clone, Eq, PartialEq)]
48pub enum SyncTarget {
49 /// This represents a range missing headers in the form of `(head,..`
50 ///
51 /// Sync _inclusively_ to the given block hash.
52 ///
53 /// This target specifies the upper end of the sync gap `(head...tip]`
54 Tip(B256),
55 /// This represents a gap missing headers bounded by the given header `h` in the form of
56 /// `(head,..h),h+1,h+2...`
57 ///
58 /// Sync _exclusively_ to the given header's parent which is: `(head..h-1]`
59 ///
60 /// The benefit of this variant is, that this already provides the block number of the highest
61 /// missing block.
62 Gap(BlockWithParent),
63 /// This represents a tip by block number
64 TipNum(u64),
65}
66
67// === impl SyncTarget ===
68
69impl SyncTarget {
70 /// Returns the tip to sync to _inclusively_
71 ///
72 /// This returns the hash if the target is [`SyncTarget::Tip`] or the `parent_hash` of the given
73 /// header in [`SyncTarget::Gap`]
74 pub fn tip(&self) -> BlockHashOrNumber {
75 match self {
76 Self::Tip(tip) => (*tip).into(),
77 Self::Gap(gap) => gap.parent.into(),
78 Self::TipNum(num) => (*num).into(),
79 }
80 }
81}
82
83/// Validate whether the header is valid in relation to it's parent
84///
85/// Returns Ok(false) if the
86pub fn validate_header_download<H: BlockHeader>(
87 consensus: &dyn HeaderValidator<H>,
88 header: &SealedHeader<H>,
89 parent: &SealedHeader<H>,
90) -> DownloadResult<()> {
91 // validate header against parent
92 consensus.validate_header_against_parent(header, parent).map_err(|error| {
93 DownloadError::HeaderValidation {
94 hash: header.hash(),
95 number: header.number(),
96 error: Box::new(error),
97 }
98 })?;
99 // validate header standalone
100 consensus.validate_header(header).map_err(|error| DownloadError::HeaderValidation {
101 hash: header.hash(),
102 number: header.number(),
103 error: Box::new(error),
104 })?;
105 Ok(())
106}