1use super::encoding::{
19 heap_commitment_preimage, merkle_node_preimage, nullifier_leaf_preimage, resource_leaf_preimage,
20};
21use super::heap_impl::Heap;
22use super::resource::{Resource, ResourceId};
23use std::marker::PhantomData;
24use telltale_types::{DefaultContentHasher, Hasher};
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum Direction {
29 Left,
31 Right,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct ProofStep<H: Hasher = DefaultContentHasher> {
38 pub direction: Direction,
40 pub sibling_hash: H::Digest,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct MerkleProof<H: Hasher = DefaultContentHasher> {
49 pub leaf_hash: H::Digest,
51 pub path: Vec<ProofStep<H>>,
53 pub root: H::Digest,
55}
56
57impl<H: Hasher> MerkleProof<H> {
58 pub fn verify(&self) -> bool {
60 let computed_root =
61 self.path
62 .iter()
63 .fold(self.leaf_hash.clone(), |current, step| {
64 match step.direction {
65 Direction::Left => merkle_node_hash::<H>(&step.sibling_hash, ¤t),
66 Direction::Right => merkle_node_hash::<H>(¤t, &step.sibling_hash),
67 }
68 });
69 computed_root == self.root
70 }
71}
72
73pub fn merkle_node_hash<H: Hasher>(left: &H::Digest, right: &H::Digest) -> H::Digest {
78 let preimage = merkle_node_preimage(left.as_ref(), right.as_ref());
79 H::digest(&preimage)
80}
81
82pub fn resource_leaf_hash<H: Hasher>(rid: &ResourceId<H>, resource: &Resource) -> H::Digest {
87 let resource_bytes = resource.canonical_bytes();
88 let preimage = resource_leaf_preimage(rid.as_bytes(), &resource_bytes);
89 H::digest(&preimage)
90}
91
92pub fn nullifier_leaf_hash<H: Hasher>(rid: &ResourceId<H>) -> H::Digest {
97 let preimage = nullifier_leaf_preimage(rid.as_bytes());
98 H::digest(&preimage)
99}
100
101fn empty_root<H: Hasher>() -> H::Digest {
103 H::digest(&[])
104}
105
106#[derive(Debug, Clone)]
108pub struct MerkleTree<H: Hasher = DefaultContentHasher> {
109 pub root: H::Digest,
111 leaves: Vec<H::Digest>,
113 levels: Vec<Vec<H::Digest>>,
116 _hasher: PhantomData<H>,
117}
118
119impl<H: Hasher> MerkleTree<H> {
120 pub fn from_leaves(leaves: Vec<H::Digest>) -> Self {
122 if leaves.is_empty() {
123 return Self {
124 root: empty_root::<H>(),
125 leaves: Vec::new(),
126 levels: Vec::new(),
127 _hasher: PhantomData,
128 };
129 }
130
131 let mut levels = vec![leaves.clone()];
132 let mut current_level = leaves.clone();
133
134 while current_level.len() > 1 {
135 if current_level.len() % 2 == 1 {
136 current_level.push(current_level.last().expect("non-empty level").clone());
137 }
138
139 let next_level: Vec<H::Digest> = current_level
140 .chunks(2)
141 .map(|pair| merkle_node_hash::<H>(&pair[0], &pair[1]))
142 .collect();
143
144 levels.push(next_level.clone());
145 current_level = next_level;
146 }
147
148 let root = current_level[0].clone();
149
150 Self {
151 root,
152 leaves,
153 levels,
154 _hasher: PhantomData,
155 }
156 }
157
158 pub fn from_heap(heap: &Heap<H>) -> Self {
160 let leaves: Vec<H::Digest> = heap
161 .active_resources()
162 .map(|(rid, resource)| resource_leaf_hash::<H>(rid, resource))
163 .collect();
164 Self::from_leaves(leaves)
165 }
166
167 pub fn size(&self) -> usize {
169 self.leaves.len()
170 }
171
172 pub fn prove(&self, index: usize) -> Option<MerkleProof<H>> {
174 if index >= self.leaves.len() {
175 return None;
176 }
177
178 let leaf_hash = self.leaves[index].clone();
179 let mut path = Vec::new();
180 let mut current_index = index;
181
182 for level in &self.levels[..self.levels.len().saturating_sub(1)] {
183 let sibling_index = if current_index % 2 == 0 {
184 current_index + 1
185 } else {
186 current_index - 1
187 };
188
189 let sibling_hash = if sibling_index < level.len() {
190 level[sibling_index].clone()
191 } else {
192 level[current_index].clone()
193 };
194
195 let direction = if current_index % 2 == 0 {
196 Direction::Right
197 } else {
198 Direction::Left
199 };
200
201 path.push(ProofStep {
202 direction,
203 sibling_hash,
204 });
205
206 current_index /= 2;
207 }
208
209 Some(MerkleProof {
210 leaf_hash,
211 path,
212 root: self.root.clone(),
213 })
214 }
215}
216
217#[derive(Debug, Clone, PartialEq, Eq)]
219pub struct HeapCommitment<H: Hasher = DefaultContentHasher> {
220 pub resource_root: H::Digest,
222 pub nullifier_root: H::Digest,
224 pub counter: u64,
226}
227
228impl<H: Hasher> HeapCommitment<H> {
229 pub fn from_heap(heap: &Heap<H>) -> Self {
231 let resource_leaves: Vec<H::Digest> = heap
232 .active_resources()
233 .map(|(rid, resource)| resource_leaf_hash::<H>(rid, resource))
234 .collect();
235 let resource_tree = MerkleTree::<H>::from_leaves(resource_leaves);
236
237 let nullifier_leaves: Vec<H::Digest> =
238 heap.consumed_ids().map(nullifier_leaf_hash::<H>).collect();
239 let nullifier_tree = MerkleTree::<H>::from_leaves(nullifier_leaves);
240
241 Self {
242 resource_root: resource_tree.root,
243 nullifier_root: nullifier_tree.root,
244 counter: heap.alloc_counter(),
245 }
246 }
247
248 pub fn hash(&self) -> H::Digest {
253 let preimage = heap_commitment_preimage(
254 self.resource_root.as_ref(),
255 self.nullifier_root.as_ref(),
256 self.counter,
257 );
258 H::digest(&preimage)
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265 use telltale_types::DefaultContentHasher;
266
267 fn to_hex(bytes: &[u8]) -> String {
268 bytes.iter().map(|byte| format!("{:02x}", byte)).collect()
269 }
270
271 fn from_hex(hex: &str) -> Vec<u8> {
272 assert_eq!(hex.len() % 2, 0, "hex input must have even length");
273 hex.as_bytes()
274 .chunks(2)
275 .map(|pair| {
276 let hi = (pair[0] as char).to_digit(16).expect("valid hex") as u8;
277 let lo = (pair[1] as char).to_digit(16).expect("valid hex") as u8;
278 (hi << 4) | lo
279 })
280 .collect()
281 }
282
283 #[test]
284 fn test_empty_tree() {
285 let tree = MerkleTree::<DefaultContentHasher>::from_leaves(vec![]);
286 assert_eq!(tree.root, empty_root::<DefaultContentHasher>());
287 assert_eq!(tree.size(), 0);
288 }
289
290 #[test]
291 fn test_single_leaf() {
292 let leaf = DefaultContentHasher::digest(b"hello");
293 let tree = MerkleTree::<DefaultContentHasher>::from_leaves(vec![leaf]);
294 assert_eq!(tree.root, leaf);
295 assert_eq!(tree.size(), 1);
296 }
297
298 #[test]
299 fn test_two_leaves() {
300 let leaf1 = DefaultContentHasher::digest(b"hello");
301 let leaf2 = DefaultContentHasher::digest(b"world");
302 let tree = MerkleTree::<DefaultContentHasher>::from_leaves(vec![leaf1, leaf2]);
303
304 let expected_root = merkle_node_hash::<DefaultContentHasher>(&leaf1, &leaf2);
305 assert_eq!(tree.root, expected_root);
306 assert_eq!(tree.size(), 2);
307 }
308
309 #[test]
310 fn test_four_leaves() {
311 let leaves: Vec<_> = (0_u8..4)
312 .map(|i| DefaultContentHasher::digest(&[i]))
313 .collect();
314 let tree = MerkleTree::<DefaultContentHasher>::from_leaves(leaves.clone());
315
316 let h01 = merkle_node_hash::<DefaultContentHasher>(&leaves[0], &leaves[1]);
317 let h23 = merkle_node_hash::<DefaultContentHasher>(&leaves[2], &leaves[3]);
318 let expected_root = merkle_node_hash::<DefaultContentHasher>(&h01, &h23);
319
320 assert_eq!(tree.root, expected_root);
321 assert_eq!(tree.size(), 4);
322 }
323
324 #[test]
325 fn test_proof_generation_and_verification() {
326 let leaves: Vec<_> = (0_u8..4)
327 .map(|i| DefaultContentHasher::digest(&[i]))
328 .collect();
329 let tree = MerkleTree::<DefaultContentHasher>::from_leaves(leaves);
330
331 for i in 0..4 {
332 let proof = tree.prove(i).expect("should generate proof");
333 assert!(proof.verify(), "proof for leaf {} should verify", i);
334 }
335 }
336
337 #[test]
338 fn test_proof_for_out_of_bounds() {
339 let leaves: Vec<_> = (0_u8..4)
340 .map(|i| DefaultContentHasher::digest(&[i]))
341 .collect();
342 let tree = MerkleTree::<DefaultContentHasher>::from_leaves(leaves);
343
344 assert!(tree.prove(4).is_none());
345 assert!(tree.prove(100).is_none());
346 }
347
348 #[test]
349 fn test_odd_number_of_leaves() {
350 let leaves: Vec<_> = (0_u8..3)
351 .map(|i| DefaultContentHasher::digest(&[i]))
352 .collect();
353 let tree = MerkleTree::<DefaultContentHasher>::from_leaves(leaves);
354
355 for i in 0..3 {
356 let proof = tree.prove(i).expect("should generate proof");
357 assert!(proof.verify(), "proof for leaf {} should verify", i);
358 }
359 }
360
361 #[test]
362 fn test_heap_merkle_root() {
363 let heap = Heap::<DefaultContentHasher>::new();
364 let (_, heap) = heap.alloc_channel("Alice", "Bob");
365 let (_, heap) = heap.alloc_message("Alice", "Bob", "Hello", vec![], 0);
366
367 let root = MerkleTree::<DefaultContentHasher>::from_heap(&heap).root;
368 assert_ne!(root, empty_root::<DefaultContentHasher>());
369 }
370
371 #[test]
372 fn test_heap_commitment() {
373 let heap = Heap::<DefaultContentHasher>::new();
374 let (rid, heap) = heap.alloc_channel("Alice", "Bob");
375 let heap = heap.consume(&rid).unwrap();
376
377 let commitment = HeapCommitment::<DefaultContentHasher>::from_heap(&heap);
378
379 assert_eq!(commitment.counter, 1);
380 }
381
382 #[test]
383 fn test_leaf_and_node_fixed_vectors() {
384 let resource = Resource::message("Alice", "Bob", "Hello", vec![1, 2, 3], 7);
385 let resource_id = ResourceId::<DefaultContentHasher>::from_resource(&resource, 1);
386 let resource_leaf = resource_leaf_hash::<DefaultContentHasher>(&resource_id, &resource);
387 let nullifier_leaf = nullifier_leaf_hash::<DefaultContentHasher>(&resource_id);
388
389 assert_eq!(
390 to_hex(resource_leaf.as_ref()),
391 "7526f89408115ad41ff135f26fd3c0be0ca91c93589c2f17cea2d8115bda9cf3"
392 );
393 assert_eq!(
394 to_hex(nullifier_leaf.as_ref()),
395 "c8ff09f3959b1a2d654b86e29d8f12df277db70f05256f0e433bf26a6c98acb1"
396 );
397
398 let sibling: [u8; 32] =
399 from_hex("981652f6785b2e7147a3ba489d050f3924700e808b9a7b229470754507c5a13c")
400 .try_into()
401 .expect("32-byte test vector");
402 let root = merkle_node_hash::<DefaultContentHasher>(&resource_leaf, &sibling);
403 assert_eq!(
404 to_hex(root.as_ref()),
405 "1555554394a0a595200d7717fa5c4710ce608dab3ffd793ff3644eecb99df1fa"
406 );
407 }
408
409 #[test]
410 fn test_heap_commitment_fixed_vector() {
411 let heap = Heap::<DefaultContentHasher>::new();
412 let (rid0, heap) = heap.alloc_channel("Alice", "Bob");
413 let (_rid1, heap) = heap.alloc_message("Alice", "Bob", "Hello", vec![1, 2, 3], 7);
414 let (_rid2, heap) = heap.alloc_session("Alice", 12345);
415 let heap = heap.consume(&rid0).unwrap();
416
417 let commitment = HeapCommitment::<DefaultContentHasher>::from_heap(&heap);
418
419 assert_eq!(
420 to_hex(commitment.resource_root.as_ref()),
421 "1555554394a0a595200d7717fa5c4710ce608dab3ffd793ff3644eecb99df1fa"
422 );
423 assert_eq!(
424 to_hex(commitment.nullifier_root.as_ref()),
425 "c045d1ac8bad212b29fffbe54d774d1401242771c75275fc30b6136b007f52d6"
426 );
427 assert_eq!(
428 to_hex(commitment.hash().as_ref()),
429 "57d07742f7a22b083e88dcae71804c9f03a727df11fe9d1d4c0aa553a34dac0d"
430 );
431 }
432
433 #[test]
434 fn test_merkle_proof_fixed_vector() {
435 let heap = Heap::<DefaultContentHasher>::new();
436 let (rid0, heap) = heap.alloc_channel("Alice", "Bob");
437 let (_rid1, heap) = heap.alloc_message("Alice", "Bob", "Hello", vec![1, 2, 3], 7);
438 let (_rid2, heap) = heap.alloc_session("Alice", 12345);
439 let heap = heap.consume(&rid0).unwrap();
440
441 let tree = MerkleTree::<DefaultContentHasher>::from_heap(&heap);
442 let proof = tree.prove(0).expect("proof should exist");
443
444 assert_eq!(
445 to_hex(proof.leaf_hash.as_ref()),
446 "7526f89408115ad41ff135f26fd3c0be0ca91c93589c2f17cea2d8115bda9cf3"
447 );
448 assert_eq!(proof.path.len(), 1);
449 assert_eq!(proof.path[0].direction, Direction::Right);
450 assert_eq!(
451 to_hex(proof.path[0].sibling_hash.as_ref()),
452 "981652f6785b2e7147a3ba489d050f3924700e808b9a7b229470754507c5a13c"
453 );
454 assert_eq!(
455 to_hex(proof.root.as_ref()),
456 "1555554394a0a595200d7717fa5c4710ce608dab3ffd793ff3644eecb99df1fa"
457 );
458 assert!(proof.verify());
459 }
460
461 #[test]
462 fn test_commitment_determinism() {
463 let heap1 = Heap::<DefaultContentHasher>::new();
464 let (_, heap1) = heap1.alloc_channel("Alice", "Bob");
465
466 let heap2 = Heap::<DefaultContentHasher>::new();
467 let (_, heap2) = heap2.alloc_channel("Alice", "Bob");
468
469 let c1 = HeapCommitment::<DefaultContentHasher>::from_heap(&heap1);
470 let c2 = HeapCommitment::<DefaultContentHasher>::from_heap(&heap2);
471
472 assert_eq!(c1, c2);
473 assert_eq!(c1.hash(), c2.hash());
474 }
475}