summaryrefslogtreecommitdiffstats
path: root/src/proof.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/proof.rs')
-rw-r--r--src/proof.rs140
1 files changed, 140 insertions, 0 deletions
diff --git a/src/proof.rs b/src/proof.rs
new file mode 100644
index 0000000..82f4a77
--- /dev/null
+++ b/src/proof.rs
@@ -0,0 +1,140 @@
+//! Merkle tree proof and verification implementation
+
+use crate::{hasher::Hasher, node::Node};
+
+/// Represents a single step in a Merkle proof path
+#[derive(Debug, Clone)]
+pub struct ProofNode {
+ /// The hash value of the sibling node
+ pub hash: String,
+ /// Whether this sibling is on the left (true) or right (false) side
+ pub is_left: bool,
+}
+
+/// A Merkle proof containing the path from a leaf to the root
+#[derive(Debug)]
+pub struct MerkleProof {
+ /// The sequence of sibling hashes needed to reconstruct the path to root
+ pub path: Vec<ProofNode>,
+ /// The index of the leaf node this proof corresponds to
+ pub leaf_index: usize,
+}
+
+pub trait Proofer {
+ /// Generates a Merkle proof for the data at the specified index
+ ///
+ /// # Arguments
+ ///
+ /// * `index` - The index of the leaf node to generate a proof for
+ ///
+ /// # Returns
+ ///
+ /// `Some(MerkleProof)` if the index is valid, `None` otherwise
+ fn generate(&self, index: usize) -> Option<MerkleProof>;
+
+ /// Verifies that a piece of data exists in the tree using a Merkle proof/
+ ///
+ /// # Arguments
+ ///
+ /// * `proof` - The Merkle proof.
+ /// * `data` - The original data to verify.
+ /// * `root_hash` - The expected root hash of the tree.
+ /// * `hasher` - The hasher used to construct the tree.
+ ///
+ /// # Returns
+ ///
+ /// `true` if the proof is valid and the data exists in the tree, `false` otherwise
+ fn verify<T>(&self, proof: &MerkleProof, data: T, root_hash: &str, hasher: &dyn Hasher) -> bool
+ where
+ T: AsRef<[u8]>;
+}
+
+pub struct DefaultProofer {
+ hasher: Box<dyn Hasher>,
+ leaves: Vec<Node>,
+}
+
+impl DefaultProofer {
+ pub fn new<T: Hasher + 'static>(hasher: T, leaves: Vec<Node>) -> Self {
+ Self {
+ hasher: Box::new(hasher),
+ leaves,
+ }
+ }
+}
+
+impl Proofer for DefaultProofer {
+ fn generate(&self, index: usize) -> Option<MerkleProof> {
+ if index >= self.leaves.len() {
+ return None;
+ }
+
+ let mut path = Vec::new();
+ let mut current_index = index;
+ let mut current_level = self.leaves.clone();
+
+ // Buildthe proof by walking up the tree
+ while current_level.len() > 1 {
+ // Ensure even number of nodes at this level
+ if current_level.len() % 2 != 0 {
+ current_level.push(current_level[current_level.len() - 1].clone());
+ }
+
+ // Find the sibling of the current node
+ let sibling_index = if current_index % 2 == 0 {
+ current_index + 1 // Right sibling
+ } else {
+ current_index - 1 // Left sibling
+ };
+
+ let is_left = sibling_index < current_index;
+ path.push(ProofNode {
+ hash: current_level[sibling_index].hash().to_string(),
+ is_left,
+ });
+
+ // Move to the next level
+ let mut next_level = Vec::new();
+ for pair in current_level.chunks(2) {
+ let (left, right) = (pair[0].clone(), pair[1].clone());
+
+ let mut buffer = Vec::<u8>::new();
+ buffer.extend_from_slice(left.hash().as_bytes());
+ buffer.extend_from_slice(right.hash().as_bytes());
+ let hash = self.hasher.hash(&buffer);
+ next_level.push(Node::new_internal(&buffer, hash, left, right));
+ }
+ current_level = next_level;
+ current_index /= 2;
+ }
+
+ Some(MerkleProof {
+ path,
+ leaf_index: index,
+ })
+ }
+
+ fn verify<T>(&self, proof: &MerkleProof, data: T, root_hash: &str, hasher: &dyn Hasher) -> bool
+ where
+ T: AsRef<[u8]>,
+ {
+ // Start with the hash of the data
+ let mut current_hash = hasher.hash(data.as_ref());
+
+ // Walk up the tree using the proof path
+ for proof_node in &proof.path {
+ current_hash = if proof_node.is_left {
+ // Sibling is on the left, current node is on the right
+ let combined = format!("{}{}", proof_node.hash, current_hash);
+ hasher.hash(combined.as_bytes())
+ } else {
+ // Sibling is on the right, current node is on the left
+ let combined = format!("{}{}", current_hash, proof_node.hash,);
+ hasher.hash(combined.as_bytes())
+ };
+ }
+
+ // Check if the computed root matches the expected root
+ current_hash == root_hash
+ }
+}