Skip to main content

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))]
10#![cfg_attr(not(feature = "std"), no_std)]
11
12extern crate alloc;
13
14mod compression;
15mod event;
16mod segment;
17
18#[cfg(all(feature = "std", unix))]
19mod changeset_offsets;
20#[cfg(all(feature = "std", unix))]
21pub use changeset_offsets::{ChangesetOffsetReader, ChangesetOffsetWriter};
22
23use alloy_primitives::BlockNumber;
24pub use compression::Compression;
25use core::ops::RangeInclusive;
26pub use event::StaticFileProducerEvent;
27pub use segment::{
28    ChangesetOffset, SegmentConfig, SegmentHeader, SegmentRangeInclusive, StaticFileSegment,
29};
30
31/// Map keyed by [`StaticFileSegment`].
32pub type StaticFileMap<T> = alloc::boxed::Box<fixed_map::Map<StaticFileSegment, T>>;
33
34/// Default static file block count.
35pub const DEFAULT_BLOCKS_PER_STATIC_FILE: u64 = 500_000;
36
37/// Highest static file block numbers, per data segment.
38#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
39pub struct HighestStaticFiles {
40    /// Highest static file block of receipts, inclusive.
41    /// If [`None`], no static file is available.
42    pub receipts: Option<BlockNumber>,
43}
44
45impl HighestStaticFiles {
46    /// Returns an iterator over all static file segments
47    fn iter(&self) -> impl Iterator<Item = Option<BlockNumber>> {
48        [self.receipts].into_iter()
49    }
50
51    /// Returns the minimum block of all segments.
52    pub fn min_block_num(&self) -> Option<u64> {
53        self.iter().flatten().min()
54    }
55
56    /// Returns the maximum block of all segments.
57    pub fn max_block_num(&self) -> Option<u64> {
58        self.iter().flatten().max()
59    }
60}
61
62/// Static File targets, per data segment, measured in [`BlockNumber`].
63#[derive(Debug, Clone, Eq, PartialEq)]
64pub struct StaticFileTargets {
65    /// Targeted range of receipts.
66    pub receipts: Option<RangeInclusive<BlockNumber>>,
67}
68
69impl StaticFileTargets {
70    /// Returns `true` if any of the targets are [Some].
71    pub const fn any(&self) -> bool {
72        self.receipts.is_some()
73    }
74
75    /// Returns `true` if all targets are either [`None`] or has beginning of the range equal to the
76    /// highest static file.
77    pub fn is_contiguous_to_highest_static_files(&self, static_files: HighestStaticFiles) -> bool {
78        core::iter::once(&(self.receipts.as_ref(), static_files.receipts)).all(
79            |(target_block_range, highest_static_file_block)| {
80                target_block_range.is_none_or(|target_block_range| {
81                    *target_block_range.start() ==
82                        highest_static_file_block
83                            .map_or(0, |highest_static_file_block| highest_static_file_block + 1)
84                })
85            },
86        )
87    }
88}
89
90/// Each static file has a fixed number of blocks. This gives out the range where the requested
91/// block is positioned, according to the specified number of blocks per static file.
92pub const fn find_fixed_range(
93    block: BlockNumber,
94    blocks_per_static_file: u64,
95) -> SegmentRangeInclusive {
96    let start = (block / blocks_per_static_file) * blocks_per_static_file;
97    SegmentRangeInclusive::new(start, start + blocks_per_static_file - 1)
98}
99
100/// Derives the number of blocks per static file for a segment that is pruned with a distance
101/// prune mode.
102///
103/// Static file segments are pruned by deleting whole files, so the file size bounds how much
104/// data is retained beyond the configured distance: a file only becomes deletable once the
105/// prune target has moved past its end. A quarter of the distance keeps the worst-case
106/// retention overshoot at ~25% while only a handful of files are live at any time. The result
107/// is clamped to avoid excessive file churn for small distances and is never larger than
108/// [`DEFAULT_BLOCKS_PER_STATIC_FILE`].
109pub const fn blocks_per_file_for_prune_distance(distance: u64) -> u64 {
110    let blocks = distance / 4;
111    if blocks < 1_000 {
112        1_000
113    } else if blocks > DEFAULT_BLOCKS_PER_STATIC_FILE {
114        DEFAULT_BLOCKS_PER_STATIC_FILE
115    } else {
116        blocks
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn test_highest_static_files_min() {
126        let files = HighestStaticFiles { receipts: Some(100) };
127
128        // Minimum value among the available segments
129        assert_eq!(files.min_block_num(), Some(100));
130
131        let empty_files = HighestStaticFiles::default();
132        // No values, should return None
133        assert_eq!(empty_files.min_block_num(), None);
134    }
135
136    #[test]
137    fn test_highest_static_files_max() {
138        let files = HighestStaticFiles { receipts: Some(100) };
139
140        // Maximum value among the available segments
141        assert_eq!(files.max_block_num(), Some(100));
142
143        let empty_files = HighestStaticFiles::default();
144        // No values, should return None
145        assert_eq!(empty_files.max_block_num(), None);
146    }
147
148    #[test]
149    fn test_find_fixed_range() {
150        // Test with default block size
151        let block: BlockNumber = 600_000;
152        let range = find_fixed_range(block, DEFAULT_BLOCKS_PER_STATIC_FILE);
153        assert_eq!(range.start(), 500_000);
154        assert_eq!(range.end(), 999_999);
155
156        // Test with a custom block size
157        let block: BlockNumber = 1_200_000;
158        let range = find_fixed_range(block, 1_000_000);
159        assert_eq!(range.start(), 1_000_000);
160        assert_eq!(range.end(), 1_999_999);
161    }
162}