summaryrefslogtreecommitdiffstats
path: root/src/proof.rs
blob: 82f4a776d89f4166e3a1551463cc6c155e41d582 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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
    }
}