Skip to content

Commit 8bcdf4f

Browse files
apollo_propeller: added merkle tree implementation
1 parent 58eae7a commit 8bcdf4f

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

crates/apollo_propeller/src/merkle.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ use sha2::{Digest, Sha256};
88
/// A hash value in the Merkle tree (32 bytes from SHA-256).
99
pub type MerkleHash = [u8; 32];
1010

11+
/// A Merkle tree for verifying data integrity.
12+
#[derive(Debug, Clone)]
13+
pub struct MerkleTree {
14+
/// All nodes in the tree, stored level by level from bottom to top.
15+
/// Index 0 contains the leaves, and the last element contains the root.
16+
nodes_by_level: Vec<Vec<MerkleHash>>,
17+
}
18+
1119
/// A Merkle proof that can be used to verify a leaf is part of the tree.
1220
///
1321
/// MerkleProof is succinct because it is sent over the wire, so it doesn't contain proof
@@ -18,6 +26,85 @@ pub struct MerkleProof {
1826
pub siblings: Vec<MerkleHash>,
1927
}
2028

29+
impl MerkleTree {
30+
/// Hash a data chunk to create a leaf hash.
31+
pub fn hash_leaf(data: &[u8]) -> MerkleHash {
32+
let mut hasher = Sha256::new();
33+
// TODO(AndrewL): Talk with the team about these hash wrappings and what to do with them.
34+
hasher.update(b"<leaf>");
35+
hasher.update(data);
36+
hasher.update(b"</leaf>");
37+
hasher.finalize().into()
38+
}
39+
40+
/// Get the root hash of the tree.
41+
/// Returns `None` if the tree is empty.
42+
pub fn root(&self) -> Option<MerkleHash> {
43+
self.nodes_by_level.last().and_then(|level| level.first().copied())
44+
}
45+
46+
/// Get the number of leaves in the tree.
47+
pub fn leaf_count(&self) -> usize {
48+
self.nodes_by_level.first().map(|level| level.len()).unwrap_or(0)
49+
}
50+
51+
/// Get the leaves of the tree.
52+
/// Returns `None` if the tree is empty.
53+
pub fn leaves(&self) -> Option<&[MerkleHash]> {
54+
self.nodes_by_level.first().map(|level| level.as_slice())
55+
}
56+
57+
// TODO(AndrewL): CRITICAL: make leaves have different depths instead of this hack.
58+
59+
/// Generate a Merkle proof for a specific leaf index.
60+
///
61+
/// Returns `None` if the index is out of bounds.
62+
pub fn prove(&self, leaf_index: usize) -> Option<MerkleProof> {
63+
let leaves = self.nodes_by_level.first()?;
64+
if leaf_index >= leaves.len() {
65+
return None;
66+
}
67+
68+
let mut siblings = Vec::new();
69+
let mut index = leaf_index;
70+
71+
// Iterate through levels from bottom to top (excluding the root)
72+
for level in &self.nodes_by_level[..self.nodes_by_level.len() - 1] {
73+
let level_size = level.len();
74+
75+
// Check if sibling exists
76+
if level_size <= 1 {
77+
index /= 2;
78+
continue;
79+
}
80+
81+
// If the index is even, take the left sibling, else take the right sibling
82+
let sibling_index = index ^ 1;
83+
84+
// Add sibling hash
85+
debug_assert!(index < level_size);
86+
let sibling_index = if sibling_index == level_size { index } else { sibling_index };
87+
// TODO(AndrewL): consider skipping the sibling if it is the same as the current node.
88+
siblings.push(level[sibling_index]);
89+
90+
// Move to parent level
91+
index /= 2;
92+
}
93+
94+
Some(MerkleProof { siblings })
95+
}
96+
97+
/// Verify a Merkle proof against this tree's root.
98+
pub fn verify(
99+
&self,
100+
leaf_hash: &MerkleHash,
101+
proof: &MerkleProof,
102+
leaf_index: usize,
103+
) -> Option<bool> {
104+
self.root().and_then(|root| Some(proof.verify(&root, leaf_hash, leaf_index)))
105+
}
106+
}
107+
21108
/// Hash a pair of nodes to create a parent hash.
22109
fn hash_pair(left: &MerkleHash, right: &MerkleHash) -> MerkleHash {
23110
let mut hasher = Sha256::new();

0 commit comments

Comments
 (0)