Skip to main content

reth_db/static_file/
mod.rs

1//! reth's static file database table import and access
2
3use reth_nippy_jar::{NippyJar, NippyJarError, CONFIG_FILE_EXTENSION};
4use reth_static_file_types::{
5    SegmentHeader, SegmentRangeInclusive, StaticFileMap, StaticFileSegment,
6};
7use std::path::Path;
8use tracing::warn;
9
10mod cursor;
11pub use cursor::StaticFileCursor;
12
13mod mask;
14pub use mask::*;
15
16mod masks;
17pub use masks::*;
18
19/// Alias type for a map of [`StaticFileSegment`] and sorted lists of existing static file ranges.
20type SortedStaticFiles = StaticFileMap<Vec<(SegmentRangeInclusive, SegmentHeader)>>;
21
22/// Given the `static_files` directory path, it returns a list over the existing `static_files`
23/// organized by [`StaticFileSegment`]. Each segment has a sorted list of block ranges and
24/// segment headers as presented in the file configuration.
25pub fn iter_static_files(path: &Path) -> Result<SortedStaticFiles, NippyJarError> {
26    if !path.exists() {
27        reth_fs_util::create_dir_all(path).map_err(|err| NippyJarError::Custom(err.to_string()))?;
28    }
29
30    let mut static_files = SortedStaticFiles::default();
31    let entries = reth_fs_util::read_dir(path)
32        .map_err(|err| NippyJarError::Custom(err.to_string()))?
33        .filter_map(Result::ok);
34    for entry in entries {
35        if entry.metadata().is_ok_and(|metadata| metadata.is_file()) &&
36            let Some((segment, _)) =
37                StaticFileSegment::parse_filename(&entry.file_name().to_string_lossy())
38        {
39            let data_path = entry.path();
40            // The config is written last on creation; without it the jar was never initialized
41            // and the writer recreates it.
42            if !data_path.with_extension(CONFIG_FILE_EXTENSION).exists() {
43                warn!(
44                    target: "reth::static_file",
45                    path = ?data_path,
46                    "Skipping uninitialized static file"
47                );
48                continue
49            }
50            let jar = NippyJar::<SegmentHeader>::load(&data_path)?;
51
52            if let Some(block_range) = jar.user_header().block_range() {
53                static_files
54                    .entry(segment)
55                    .and_modify(|headers| headers.push((block_range, jar.user_header().clone())))
56                    .or_insert_with(|| vec![(block_range, jar.user_header().clone())]);
57            }
58        }
59    }
60
61    // Sort by block end range.
62    for range_list in static_files.values_mut() {
63        range_list.sort_unstable_by_key(|(block_range, _)| block_range.end());
64    }
65
66    Ok(static_files)
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn skips_static_file_without_config() {
75        let dir = tempfile::tempdir().unwrap();
76        let segment = StaticFileSegment::Headers;
77        let path = dir.path().join(segment.filename(&SegmentRangeInclusive::new(0, 499_999)));
78        std::fs::write(&path, b"").unwrap();
79
80        assert!(iter_static_files(dir.path()).unwrap().is_empty());
81    }
82}