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