reth_cli_commands/download/
verify.rs1use super::{manifest::OutputFileChecksum, progress::ArchiveVerificationProgress};
2use blake3::Hasher;
3use eyre::Result;
4use reth_fs_util as fs;
5use std::{io::Read, path::Path};
6
7pub(crate) struct OutputVerifier<'a> {
9 target_dir: &'a Path,
11}
12
13impl<'a> OutputVerifier<'a> {
14 pub(crate) const fn new(target_dir: &'a Path) -> Self {
16 Self { target_dir }
17 }
18
19 pub(crate) fn verify(&self, output_files: &[OutputFileChecksum]) -> Result<bool> {
22 self.verify_with_progress(output_files, None)
23 }
24
25 pub(crate) fn verify_with_progress(
28 &self,
29 output_files: &[OutputFileChecksum],
30 mut progress: Option<&mut ArchiveVerificationProgress<'_>>,
31 ) -> Result<bool> {
32 if output_files.is_empty() {
33 return Ok(false);
34 }
35
36 for expected in output_files {
37 let output_path = self.target_dir.join(&expected.path);
38 let meta = match fs::metadata(&output_path) {
39 Ok(meta) => meta,
40 Err(_) => return Ok(false),
41 };
42 if meta.len() != expected.size {
43 return Ok(false);
44 }
45
46 let actual = Self::file_blake3_hex(&output_path, progress.as_deref_mut())?;
47 if !actual.eq_ignore_ascii_case(&expected.blake3) {
48 return Ok(false);
49 }
50 }
51
52 Ok(true)
53 }
54
55 pub(crate) fn cleanup(&self, output_files: &[OutputFileChecksum]) {
57 for output in output_files {
58 let _ = fs::remove_file(self.target_dir.join(&output.path));
59 }
60 }
61
62 fn file_blake3_hex(
64 path: &Path,
65 mut progress: Option<&mut ArchiveVerificationProgress<'_>>,
66 ) -> Result<String> {
67 let mut file = fs::open(path)?;
68 let mut hasher = Hasher::new();
69 let mut buf = [0_u8; 64 * 1024];
70
71 loop {
72 let n = file.read(&mut buf)?;
73 if n == 0 {
74 break;
75 }
76 hasher.update(&buf[..n]);
77 if let Some(progress) = progress.as_deref_mut() {
78 progress.record_verified(n as u64);
79 }
80 }
81
82 Ok(hasher.finalize().to_hex().to_string())
83 }
84}