diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/fs.rs | 53 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/merkletree.rs | 16 | ||||
| -rw-r--r-- | src/proof.rs | 30 |
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 +} @@ -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) } } |
