perfect_merkle_forest/
lib.rs

1//! A simple implementation of a merkle forest with perfectly balanced trees.
2//! This library should not be used for anything practical, it is provided
3//! just as a reference.
4//!
5//! There can be up to 256 levels of trees and up to 256 trees.
6//! When the first tree has 4 levels there can be at most 4 total trees, then
7//! the trees get merged into a single tree with 5 levels and so on.
8//! A tree of n levels contains 2^n entries.
9//!
10//! You can only add entries, never remove or change them.
11//!
12//! The actual entries added are not kept by this library, only their hashes.
13//! A merkle proof is returned for every added entry, together with the index
14//! of the tree they belong to. That merkle proof is only valid at that point.
15//! As new entries are added the proofs will be invalidated as the entries will
16//! be rearranged in the trees.
17
18use bitcoin_hashes::{sha256, Hash};
19
20pub struct Forest {
21    pub roots: Vec<[u8; 33]>, // here the first byte encodes the height of the tree, the rest 32 are the hash
22}
23
24pub struct Proof {
25    pub tree_index: usize,
26
27    // here the first byte is either zero or one, for ordering the hashing pair
28    // zero means 'prepend this path item to the thing you have', one means 'append'
29    pub merkle_path: Vec<[u8; 33]>,
30}
31
32impl Forest {
33    pub fn new() -> Self {
34        Forest { roots: Vec::new() }
35    }
36
37    pub fn add(&mut self, entry: &[u8]) -> ([u8; 32], Proof) {
38        let hash = sha256::Hash::hash(entry);
39        let proof = self.add_hash(hash.as_byte_array());
40        (*hash.as_byte_array(), proof)
41    }
42
43    pub fn add_hash(&mut self, hash: &[u8; 32]) -> Proof {
44        let mut new_merkle_root = vec![0u8; 33];
45        new_merkle_root[1..33].copy_from_slice(hash);
46
47        let mut proof = Proof {
48            tree_index: 0,
49            merkle_path: Vec::with_capacity(32),
50        };
51
52        // the log entry we're adding is by default a new merkle root at level 0
53        let mut level = 0;
54        loop {
55            // check if we can merge with the previous one
56            match self.roots.last() {
57                Some(prev_root) if prev_root[0] == level => {
58                    let mut path_item = vec![0u8; 33];
59                    path_item[1..33].copy_from_slice(&prev_root[1..33]);
60                    // path_item[0] = 0; // no need to set the first byte to zero as that is already set
61
62                    proof.merkle_path.push(path_item.try_into().unwrap());
63
64                    // yes, we can
65                    let mut combined_merkle = vec![0u8; 64];
66                    combined_merkle[0..32].copy_from_slice(&prev_root[1..33]);
67                    combined_merkle[32..64].copy_from_slice(&new_merkle_root[1..33]);
68
69                    new_merkle_root[1..33]
70                        .copy_from_slice(sha256::Hash::hash(&combined_merkle).as_byte_array());
71                    new_merkle_root[0] = level + 1;
72
73                    // remove that previous as we will replace it
74                    self.roots.remove(self.roots.len() - 1);
75
76                    // increase the level of the thing we're adding and check again
77                    level += 1;
78
79                    continue;
80                }
81                _ => {
82                    proof.tree_index = self.roots.len();
83                    self.roots.push(new_merkle_root.try_into().unwrap());
84                    return proof;
85                }
86            }
87        }
88    }
89
90    pub fn check_presence(&self, entry: [u8; 32], proof: &Proof) -> bool {
91        self.roots.get(proof.tree_index).map_or(false, |root| {
92            let mut current_value = entry;
93            let mut combined_merkle = vec![0u8; 64];
94            for path_item in &proof.merkle_path {
95                if path_item[0] == 0 {
96                    combined_merkle[0..32].copy_from_slice(&path_item[1..33]);
97                    combined_merkle[32..64].copy_from_slice(&current_value);
98                } else {
99                    combined_merkle[0..32].copy_from_slice(&current_value);
100                    combined_merkle[32..64].copy_from_slice(&path_item[1..33]);
101                }
102
103                current_value = *sha256::Hash::hash(&combined_merkle).as_byte_array();
104            }
105
106            current_value == root[1..33]
107        })
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn tree_building_manual() {
117        let mut forest = Forest::new();
118        forest.add(&vec![1, 2, 3, 4, 5]);
119        assert_eq!(forest.roots.len(), 1, "length 1 at 1");
120        forest.add(&vec![1, 2, 3, 4, 5]);
121        forest.add(&vec![1, 2, 3, 4, 5]);
122        assert_eq!(forest.roots.len(), 2, "length 2 at 3");
123        forest.add(&vec![1, 2, 3, 4, 5]);
124        assert_eq!(forest.roots.len(), 1, "length 1 at 4");
125        assert_eq!(forest.roots[0][0], 2, "level 2 at 4");
126        forest.add(&vec![1, 2, 3, 4, 5]);
127        forest.add(&vec![1, 2, 3, 4, 5]);
128        forest.add(&vec![1, 2, 3, 4, 5]);
129        forest.add(&vec![1, 2, 3, 4, 5]);
130        forest.add(&vec![1, 2, 3, 4, 5]);
131        forest.add(&vec![1, 2, 3, 4, 5]);
132        forest.add(&vec![1, 2, 3, 4, 5]);
133        assert_eq!(forest.roots.len(), 3, "length 3 at 12");
134        assert_eq!(forest.roots[0][0], 3, "level 3 at 12");
135    }
136
137    #[test]
138    fn tree_building() {
139        let mut forest = Forest::new();
140        for i in 0..(u64::pow(2, 16) - 1) {
141            forest.add(format!("entry:{}", i).as_bytes());
142        }
143        assert_eq!(forest.roots.len(), 16, "16 trees right before level 16");
144        forest.add("last".as_bytes());
145        assert_eq!(forest.roots.len(), 1, "perfect single tree with 16 levels");
146        assert_eq!(forest.roots[0][0], 16, "level 16 after 2**16 entries");
147    }
148
149    #[test]
150    fn correct_hashing() {
151        let mut forest1 = Forest::new();
152        let h = sha256::Hash::hash(&vec![1, 2, 3, 4, 5]);
153        forest1.add_hash(h.as_byte_array());
154
155        assert_eq!(
156            forest1.roots[0],
157            [
158                0u8, 116u8, 248u8, 31u8, 225u8, 103u8, 217u8, 155u8, 76u8, 180u8, 29u8, 109u8,
159                12u8, 205u8, 168u8, 34u8, 120u8, 202u8, 238u8, 159u8, 62u8, 47u8, 37u8, 213u8,
160                229u8, 163u8, 147u8, 111u8, 243u8, 220u8, 236u8, 96u8, 208u8,
161            ]
162        );
163
164        forest1.add_hash(h.as_byte_array());
165        forest1.add_hash(h.as_byte_array());
166        forest1.add_hash(h.as_byte_array());
167        forest1.add_hash(h.as_byte_array());
168
169        let mut forest2 = Forest::new();
170        forest2.add(&vec![1, 2, 3, 4, 5]);
171        forest2.add(&vec![1, 2, 3, 4, 5]);
172        forest2.add(&vec![1, 2, 3, 4, 5]);
173        forest2.add(&vec![1, 2, 3, 4, 5]);
174        forest2.add(&vec![1, 2, 3, 4, 5]);
175
176        assert_eq!(forest1.roots, forest2.roots);
177        assert_eq!(
178            forest2.roots[0],
179            [
180                2u8, 217u8, 21u8, 129u8, 3u8, 237u8, 23u8, 184u8, 184u8, 149u8, 37u8, 236u8, 24u8,
181                71u8, 86u8, 103u8, 102u8, 187u8, 212u8, 251u8, 124u8, 220u8, 154u8, 90u8, 54u8,
182                144u8, 46u8, 225u8, 145u8, 124u8, 70u8, 214u8, 70u8,
183            ]
184        );
185    }
186
187    #[test]
188    fn proofs() {
189        let mut forest = Forest::new();
190        forest.add(&vec![1, 2, 3, 4, 5]);
191        let (hash, proof) = forest.add(&vec![0, 0, 0, 0, 0]);
192        assert_eq!(proof.tree_index, 0, "proof tree index");
193        assert_eq!(proof.merkle_path.len(), 1, "merkle path size");
194        assert_eq!(
195            forest.check_presence(hash, &proof),
196            true,
197            "presence should be confirmed"
198        );
199        forest.add(&vec![1, 2, 3, 4, 5]);
200        forest.add(&vec![1, 2, 3, 4, 5]);
201        forest.add(&vec![1, 2, 3, 4, 5]);
202        forest.add(&vec![1, 2, 3, 4, 5]);
203        forest.add(&vec![1, 2, 3, 4, 5]);
204        let (hash, proof) = forest.add(&vec![1, 2, 3, 4, 5]);
205        assert_eq!(proof.tree_index, 0, "proof tree index");
206        assert_eq!(proof.merkle_path.len(), 3, "merkle path size");
207        assert_eq!(
208            forest.check_presence(hash, &proof),
209            true,
210            "presence should be confirmed"
211        );
212
213        let (hash, _) = forest.add(&vec![0, 0, 0, 0, 0]);
214        assert_eq!(
215            forest.check_presence(hash, &proof),
216            false,
217            "proof for a different entry shouldn't work"
218        );
219    }
220}