Skip to content

Commit e2b80fa

Browse files
authored
feat: address TODO by implementing Merkle Tree-Based commitment in the aggregation example (#2262)
Co-authored-by: yahya <[email protected]>
1 parent 74a4646 commit e2b80fa

File tree

1 file changed

+66
-25
lines changed
  • examples/aggregation/program/src

1 file changed

+66
-25
lines changed

examples/aggregation/program/src/main.rs

+66-25
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,66 @@ pub fn words_to_bytes_le(words: &[u32; 8]) -> [u8; 32] {
1414
bytes
1515
}
1616

17-
/// Encode a list of vkeys and committed values into a single byte array. In the future this could
18-
/// be a merkle tree or some other commitment scheme.
17+
/// Encode a vkey and a committed value into a single byte array.
1918
///
20-
/// ( vkeys.len() || vkeys || committed_values[0].len as u32 || committed_values[0] || ... )
21-
pub fn commit_proof_pairs(vkeys: &[[u32; 8]], committed_values: &[Vec<u8>]) -> Vec<u8> {
22-
assert_eq!(vkeys.len(), committed_values.len());
23-
let mut res = Vec::with_capacity(
24-
4 + vkeys.len() * 32
25-
+ committed_values.len() * 4
26-
+ committed_values.iter().map(|vals| vals.len()).sum::<usize>(),
27-
);
28-
19+
/// ( words_to_bytes_le(vkey) || (committed_value.len() as u32).to_be_bytes() || committed_value )
20+
pub fn commit_proof_pair(vkey: &[u32; 8], committed_value: &Vec<u8>) -> Vec<u8> {
21+
let mut res = Vec::new();
22+
res.extend_from_slice(&words_to_bytes_le(vkey));
2923
// Note we use big endian because abi.encodePacked in solidity does also
30-
res.extend_from_slice(&(vkeys.len() as u32).to_be_bytes());
31-
for vkey in vkeys.iter() {
32-
res.extend_from_slice(&words_to_bytes_le(vkey));
33-
}
34-
for vals in committed_values.iter() {
35-
res.extend_from_slice(&(vals.len() as u32).to_be_bytes());
36-
res.extend_from_slice(vals);
37-
}
24+
res.extend_from_slice(&(committed_value.len() as u32).to_be_bytes());
25+
res.extend_from_slice(committed_value);
26+
res
27+
}
3828

29+
/// Computes hash of a leaf in a merkle tree.
30+
///
31+
/// A leaf in a merkle tree is a pair of a verification key and a committed value.
32+
/// The leaf is encoded as a byte array using `commit_proof_pair` and then hashed using sha256.
33+
pub fn compute_leaf_hash(vkey: &[u32; 8], committed_value: &Vec<u8>) -> [u8; 32] {
34+
// encode the leaf as a byte array
35+
let leaf = commit_proof_pair(vkey, committed_value);
36+
let digest = Sha256::digest(&leaf);
37+
let mut res = [0u8; 32];
38+
res.copy_from_slice(&digest);
39+
res
40+
}
41+
42+
/// Hashes a pair of already hashed leaves.
43+
///
44+
/// The hash is computed as sha256(left || right).
45+
pub fn hash_pair(left: &[u8], right: &[u8]) -> [u8; 32] {
46+
let mut hasher = Sha256::new();
47+
hasher.update(left);
48+
hasher.update(right);
49+
let digest = hasher.finalize();
50+
let mut res = [0u8; 32];
51+
res.copy_from_slice(&digest);
3952
res
4053
}
4154

55+
/// Computes the root of a merkle tree given the leaves.
56+
///
57+
/// The leaves are hashed using `compute_leaf_hash` and then the hashes are combined to form the root.
58+
/// The root is computed by hashing pairs of hashes until only one hash remains.
59+
pub fn compute_merkle_root(mut leaves: Vec<[u8; 32]>) -> [u8; 32] {
60+
if leaves.is_empty() {
61+
return [0u8; 32];
62+
}
63+
64+
65+
while leaves.len() > 1 {
66+
let mut next = Vec::new();
67+
for i in (0..leaves.len()).step_by(2) {
68+
let left = &leaves[i];
69+
let right = if i + 1 < leaves.len() { &leaves[i + 1] } else { &leaves[i] };
70+
next.push(hash_pair(left, right));
71+
}
72+
leaves = next;
73+
}
74+
leaves[0]
75+
}
76+
4277
pub fn main() {
4378
// Read the verification keys.
4479
let vkeys = sp1_zkvm::io::read::<Vec<[u32; 8]>>();
@@ -55,10 +90,16 @@ pub fn main() {
5590
sp1_zkvm::lib::verify::verify_sp1_proof(vkey, &public_values_digest.into());
5691
}
5792

58-
// TODO: Do something interesting with the proofs here.
59-
//
60-
// For example, commit to the verified proofs in a merkle tree. For now, we'll just commit to
61-
// all the (vkey, input) pairs.
62-
let commitment = commit_proof_pairs(&vkeys, &public_values);
63-
sp1_zkvm::io::commit_slice(&commitment);
93+
// Convert the (vkey, public_value) pairs into leaves of a merkle tree.
94+
let leaves: Vec<[u8; 32]> = vkeys
95+
.iter()
96+
.zip(public_values.iter())
97+
.map(|(vkey, public_value)| compute_leaf_hash(vkey, public_value))
98+
.collect();
99+
100+
// Traverse the merkle tree bottom-up to compute the root.
101+
let merkle_root = compute_merkle_root(leaves);
102+
103+
// Commit the root.
104+
sp1_zkvm::io::commit_slice(&merkle_root);
64105
}

0 commit comments

Comments
 (0)