summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSanto Cariotti <santo@dcariotti.me>2025-07-07 15:14:12 +0000
committerSanto Cariotti <santo@dcariotti.me>2025-07-07 15:14:12 +0000
commit5fb45710f57f95eec527958400f8ec0a049c5fe4 (patch)
tree5a698f60ceec8954fcc5b2e19cbc7b9beea06a43 /src
parent2ef7371f7a4eefe7478cad43cb4922efaa12876a (diff)
Make tree for folders
Diffstat (limited to 'src')
-rw-r--r--src/fs.rs53
-rw-r--r--src/lib.rs1
-rw-r--r--src/merkletree.rs16
-rw-r--r--src/proof.rs30
4 files changed, 86 insertions, 14 deletions
diff --git a/src/fs.rs b/src/fs.rs
new file mode 100644
index 0000000..cb8ce66
--- /dev/null
+++ b/src/fs.rs
@@ -0,0 +1,53 @@
+//! Provides the module used for filesystem operations made by this library.
+
+use std::path::Path;
+
+use crate::{hasher::Hasher, node::Node};
+
+/// Reads the entire content of a file into a `Vec<u8>`.
+///
+/// If the file cannot be read, an error message is printed to stderr, and the program exits.
+///
+/// `path` is a reference to a `String` representing the path to the file.
+fn read_file_content(path: &String) -> Vec<u8> {
+ match std::fs::read(path) {
+ Ok(contents) => contents,
+ Err(e) => {
+ eprintln!("Failed to read file '{}': {}", path, e);
+ std::process::exit(1);
+ }
+ }
+}
+
+/// Recursively hashes the contents of files and directories.
+///
+/// This function iterates through a list of filenames. For each file, it reads its content,
+/// hashes it using the provided `Hasher`, and creates a leaf `Node`. If an entry is a directory,
+/// it recursively calls itself to hash the directory's contents and extends the current
+/// list of nodes with the results.
+pub fn hash_dir<H>(hasher: H, filenames: Vec<String>) -> Vec<Node>
+where
+ H: Hasher + 'static + std::marker::Sync + Clone,
+{
+ let mut nodes: Vec<Node> = vec![];
+ for filename in &filenames {
+ let file = Path::new(filename);
+ if file.is_file() {
+ let hash = hasher.hash(read_file_content(filename).as_slice());
+
+ nodes.push(Node::new_leaf(hash));
+ } else if file.is_dir() {
+ let mut filenames_in_dir: Vec<String> = file
+ .read_dir()
+ .unwrap()
+ .map(|entry| String::from(entry.unwrap().path().to_str().unwrap()))
+ .collect();
+
+ filenames_in_dir.sort();
+
+ nodes.extend(hash_dir(hasher.clone(), filenames_in_dir));
+ }
+ }
+
+ nodes
+}
diff --git a/src/lib.rs b/src/lib.rs
index b23f7d1..7252ebb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -51,6 +51,7 @@
//! ));
//!
//! ```
+pub mod fs;
pub mod hasher;
pub mod merkletree;
pub mod node;
diff --git a/src/merkletree.rs b/src/merkletree.rs
index c3c9fd7..fdbb5ac 100644
--- a/src/merkletree.rs
+++ b/src/merkletree.rs
@@ -1,7 +1,7 @@
//! Provides the MerkleTree structure and associated methods for creating and interacting
//! with binary Merkle trees using custom hashers.
-use crate::{hasher::Hasher, node::Node};
+use crate::{fs, hasher::Hasher, node::Node};
use rayon::prelude::*;
/// A binary Merkle tree implementation.
@@ -60,6 +60,20 @@ impl MerkleTree {
Self::build(hasher, leaves)
}
+ /// Construct a Merkletree from an iter of String-s.
+ pub fn from_paths<H>(hasher: H, paths: Vec<String>) -> Self
+ where
+ H: Hasher + 'static + std::marker::Sync + Clone,
+ {
+ let mut leaves = fs::hash_dir(hasher.clone(), paths);
+
+ if leaves.len() % 2 != 0 {
+ leaves.push(leaves.last().unwrap().clone());
+ }
+
+ Self::build(hasher, leaves)
+ }
+
/// Constructs the internal nodes of the tree from the leaves upward and computes the root.
fn build<H>(hasher: H, nodes: Vec<Node>) -> Self
where
diff --git a/src/proof.rs b/src/proof.rs
index af0af94..b6b5860 100644
--- a/src/proof.rs
+++ b/src/proof.rs
@@ -64,6 +64,21 @@ where
pub fn new(hasher: H, leaves: Vec<Node>) -> Self {
Self { hasher, leaves }
}
+
+ pub fn verify_hash(&self, proof: &MerkleProof, hash: String, root_hash: &str) -> bool {
+ let mut current_hash = hash;
+ // Walk up the tree using the proof path
+ for proof_node in &proof.path {
+ let combined: String = match proof_node.child_type {
+ NodeChildType::Left => format!("{}{}", proof_node.hash, current_hash),
+ NodeChildType::Right => format!("{}{}", current_hash, proof_node.hash),
+ };
+ current_hash = self.hasher.hash(combined.as_bytes());
+ }
+
+ // Check if the computed root matches the expected root
+ current_hash == root_hash
+ }
}
impl<H> Proofer for DefaultProofer<H>
@@ -138,19 +153,8 @@ where
T: AsRef<[u8]>,
{
// Start with the hash of the data
- let mut current_hash = self.hasher.hash(data.as_ref());
-
- // Walk up the tree using the proof path
- for proof_node in &proof.path {
- let combined: String = match proof_node.child_type {
- NodeChildType::Left => format!("{}{}", proof_node.hash, current_hash),
- NodeChildType::Right => format!("{}{}", current_hash, proof_node.hash),
- };
- current_hash = self.hasher.hash(combined.as_bytes());
- }
-
- // Check if the computed root matches the expected root
- current_hash == root_hash
+ let hash: String = self.hasher.hash(data.as_ref());
+ self.verify_hash(proof, hash, root_hash)
}
}