reth_era_downloader/
fs.rs1use crate::EraMeta;
2use alloy_primitives::{hex, hex::ToHexExt};
3use eyre::{eyre, OptionExt};
4use futures_util::{stream, Stream};
5use reth_fs_util as fs;
6use sha2::{Digest, Sha256};
7use std::{fmt::Debug, io, io::BufRead, path::Path, str::FromStr};
8
9pub fn read_dir(
11 dir: impl AsRef<Path> + Send + Sync + 'static,
12) -> eyre::Result<impl Stream<Item = eyre::Result<EraLocalMeta>> + Send + Sync + 'static + Unpin> {
13 let mut checksums = None;
14 let mut entries = fs::read_dir(dir)?
15 .filter_map(|entry| {
16 (|| {
17 let path = entry?.path();
18
19 if path.extension() == Some("era1".as_ref()) {
20 if let Some(last) = path.components().next_back() {
21 let str = last.as_os_str().to_string_lossy().to_string();
22 let parts = str.split('-').collect::<Vec<_>>();
23
24 if parts.len() == 3 {
25 let number = usize::from_str(parts[1])?;
26
27 return Ok(Some((number, path.into_boxed_path())));
28 }
29 }
30 }
31 if path.file_name() == Some("checksums.txt".as_ref()) {
32 let file = fs::open(path)?;
33 let reader = io::BufReader::new(file);
34 let lines = reader.lines();
35 checksums = Some(lines);
36 }
37
38 Ok(None)
39 })()
40 .transpose()
41 })
42 .collect::<eyre::Result<Vec<_>>>()?;
43 let mut checksums = checksums.ok_or_eyre("Missing file `checksums.txt` in the `dir`")?;
44
45 entries.sort_by(|(left, _), (right, _)| left.cmp(right));
46
47 Ok(stream::iter(entries.into_iter().map(move |(_, path)| {
48 let expected_checksum =
49 checksums.next().transpose()?.ok_or_eyre("Got less checksums than ERA files")?;
50 let expected_checksum = hex::decode(expected_checksum)?;
51
52 let mut hasher = Sha256::new();
53 let mut reader = io::BufReader::new(fs::open(&path)?);
54
55 io::copy(&mut reader, &mut hasher)?;
56 let actual_checksum = hasher.finalize().to_vec();
57
58 if actual_checksum != expected_checksum {
59 return Err(eyre!(
60 "Checksum mismatch, got: {}, expected: {}",
61 actual_checksum.encode_hex(),
62 expected_checksum.encode_hex()
63 ));
64 }
65
66 Ok(EraLocalMeta::new(path))
67 })))
68}
69
70#[derive(Debug)]
72pub struct EraLocalMeta {
73 path: Box<Path>,
74}
75
76impl EraLocalMeta {
77 const fn new(path: Box<Path>) -> Self {
78 Self { path }
79 }
80}
81
82impl<T: AsRef<Path>> PartialEq<T> for EraLocalMeta {
83 fn eq(&self, other: &T) -> bool {
84 self.as_ref().eq(other.as_ref())
85 }
86}
87
88impl AsRef<Path> for EraLocalMeta {
89 fn as_ref(&self) -> &Path {
90 self.path.as_ref()
91 }
92}
93
94impl EraMeta for EraLocalMeta {
95 fn mark_as_processed(self) -> eyre::Result<()> {
97 Ok(())
98 }
99}