reth_static_file_types/
lib.rs

1//! Commonly used types for static file usage.
2
3#![doc(
4    html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
5    html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
6    issue_tracker_base_url = "https://github.com/SeismicSystems/seismic-reth/issues/"
7)]
8#![cfg_attr(not(test), warn(unused_crate_dependencies))]
9#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
10
11mod compression;
12mod event;
13mod segment;
14
15use alloy_primitives::BlockNumber;
16pub use compression::Compression;
17pub use event::StaticFileProducerEvent;
18pub use segment::{SegmentConfig, SegmentHeader, SegmentRangeInclusive, StaticFileSegment};
19use std::ops::RangeInclusive;
20
21/// Default static file block count.
22pub const DEFAULT_BLOCKS_PER_STATIC_FILE: u64 = 500_000;
23
24/// Highest static file block numbers, per data segment.
25#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
26pub struct HighestStaticFiles {
27    /// Highest static file block of headers, inclusive.
28    /// If [`None`], no static file is available.
29    pub headers: Option<BlockNumber>,
30    /// Highest static file block of receipts, inclusive.
31    /// If [`None`], no static file is available.
32    pub receipts: Option<BlockNumber>,
33    /// Highest static file block of transactions, inclusive.
34    /// If [`None`], no static file is available.
35    pub transactions: Option<BlockNumber>,
36}
37
38impl HighestStaticFiles {
39    /// Returns the highest static file if it exists for a segment
40    pub const fn highest(&self, segment: StaticFileSegment) -> Option<BlockNumber> {
41        match segment {
42            StaticFileSegment::Headers => self.headers,
43            StaticFileSegment::Transactions => self.transactions,
44            StaticFileSegment::Receipts => self.receipts,
45        }
46    }
47
48    /// Returns a mutable reference to a static file segment
49    pub fn as_mut(&mut self, segment: StaticFileSegment) -> &mut Option<BlockNumber> {
50        match segment {
51            StaticFileSegment::Headers => &mut self.headers,
52            StaticFileSegment::Transactions => &mut self.transactions,
53            StaticFileSegment::Receipts => &mut self.receipts,
54        }
55    }
56
57    /// Returns the minimum block of all segments.
58    pub fn min_block_num(&self) -> Option<u64> {
59        [self.headers, self.transactions, self.receipts].iter().filter_map(|&option| option).min()
60    }
61
62    /// Returns the maximum block of all segments.
63    pub fn max_block_num(&self) -> Option<u64> {
64        [self.headers, self.transactions, self.receipts].iter().filter_map(|&option| option).max()
65    }
66}
67
68/// Static File targets, per data segment, measured in [`BlockNumber`].
69#[derive(Debug, Clone, Eq, PartialEq)]
70pub struct StaticFileTargets {
71    /// Targeted range of headers.
72    pub headers: Option<RangeInclusive<BlockNumber>>,
73    /// Targeted range of receipts.
74    pub receipts: Option<RangeInclusive<BlockNumber>>,
75    /// Targeted range of transactions.
76    pub transactions: Option<RangeInclusive<BlockNumber>>,
77}
78
79impl StaticFileTargets {
80    /// Returns `true` if any of the targets are [Some].
81    pub const fn any(&self) -> bool {
82        self.headers.is_some() || self.receipts.is_some() || self.transactions.is_some()
83    }
84
85    /// Returns `true` if all targets are either [`None`] or has beginning of the range equal to the
86    /// highest static file.
87    pub fn is_contiguous_to_highest_static_files(&self, static_files: HighestStaticFiles) -> bool {
88        [
89            (self.headers.as_ref(), static_files.headers),
90            (self.receipts.as_ref(), static_files.receipts),
91            (self.transactions.as_ref(), static_files.transactions),
92        ]
93        .iter()
94        .all(|(target_block_range, highest_static_fileted_block)| {
95            target_block_range.is_none_or(|target_block_range| {
96                *target_block_range.start() ==
97                    highest_static_fileted_block.map_or(0, |highest_static_fileted_block| {
98                        highest_static_fileted_block + 1
99                    })
100            })
101        })
102    }
103}
104
105/// Each static file has a fixed number of blocks. This gives out the range where the requested
106/// block is positioned. Used for segment filename.
107pub const fn find_fixed_range(
108    block: BlockNumber,
109    blocks_per_static_file: u64,
110) -> SegmentRangeInclusive {
111    let start = (block / blocks_per_static_file) * blocks_per_static_file;
112    SegmentRangeInclusive::new(start, start + blocks_per_static_file - 1)
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_highest_static_files_highest() {
121        let files =
122            HighestStaticFiles { headers: Some(100), receipts: Some(200), transactions: None };
123
124        // Test for headers segment
125        assert_eq!(files.highest(StaticFileSegment::Headers), Some(100));
126
127        // Test for receipts segment
128        assert_eq!(files.highest(StaticFileSegment::Receipts), Some(200));
129
130        // Test for transactions segment
131        assert_eq!(files.highest(StaticFileSegment::Transactions), None);
132    }
133
134    #[test]
135    fn test_highest_static_files_as_mut() {
136        let mut files = HighestStaticFiles::default();
137
138        // Modify headers value
139        *files.as_mut(StaticFileSegment::Headers) = Some(150);
140        assert_eq!(files.headers, Some(150));
141
142        // Modify receipts value
143        *files.as_mut(StaticFileSegment::Receipts) = Some(250);
144        assert_eq!(files.receipts, Some(250));
145
146        // Modify transactions value
147        *files.as_mut(StaticFileSegment::Transactions) = Some(350);
148        assert_eq!(files.transactions, Some(350));
149    }
150
151    #[test]
152    fn test_highest_static_files_min() {
153        let files =
154            HighestStaticFiles { headers: Some(300), receipts: Some(100), transactions: None };
155
156        // Minimum value among the available segments
157        assert_eq!(files.min_block_num(), Some(100));
158
159        let empty_files = HighestStaticFiles::default();
160        // No values, should return None
161        assert_eq!(empty_files.min_block_num(), None);
162    }
163
164    #[test]
165    fn test_highest_static_files_max() {
166        let files =
167            HighestStaticFiles { headers: Some(300), receipts: Some(100), transactions: Some(500) };
168
169        // Maximum value among the available segments
170        assert_eq!(files.max_block_num(), Some(500));
171
172        let empty_files = HighestStaticFiles::default();
173        // No values, should return None
174        assert_eq!(empty_files.max_block_num(), None);
175    }
176
177    #[test]
178    fn test_find_fixed_range() {
179        // Test with default block size
180        let block: BlockNumber = 600_000;
181        let range = find_fixed_range(block, DEFAULT_BLOCKS_PER_STATIC_FILE);
182        assert_eq!(range.start(), 500_000);
183        assert_eq!(range.end(), 999_999);
184
185        // Test with a custom block size
186        let block: BlockNumber = 1_200_000;
187        let range = find_fixed_range(block, 1_000_000);
188        assert_eq!(range.start(), 1_000_000);
189        assert_eq!(range.end(), 1_999_999);
190    }
191}