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}