Skip to content

Commit e20cffc

Browse files
committed
feat: update sp1 programs to correctly compute the merkle tree
1 parent a095608 commit e20cffc

File tree

4 files changed

+44
-50
lines changed

4 files changed

+44
-50
lines changed

aggregation_mode/aggregation_programs/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,3 @@ members = ["sp1", "risc0"]
88
[patch.crates-io]
99
# Adding RISC Zero keccak precompile support
1010
tiny-keccak = { git = "https://github.com/risc0/tiny-keccak", tag = "tiny-keccak/v2.0.2-risczero.0" }
11-
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
11
#![no_main]
22
sp1_zkvm::entrypoint!(main);
33

4+
use lambdaworks_crypto::merkle_tree::merkle::MerkleTree;
45
use sha2::{Digest, Sha256};
5-
use sp1_aggregation_program::{compute_merkle_root, Input};
6+
use sp1_aggregation_program::{ChunkAggregatorInput, SP1VkAndPubInputs};
67

78
pub fn main() {
8-
let input = sp1_zkvm::io::read::<Input>();
9-
10-
let mut proofs_commitment: Vec<[u8; 32]> = vec![];
9+
let input = sp1_zkvm::io::read::<ChunkAggregatorInput>();
1110

1211
// Verify the proofs.
1312
for proof in input.proofs_vk_and_pub_inputs.iter() {
1413
let vkey = proof.vk;
1514
let public_values = &proof.public_inputs;
1615
let public_values_digest = Sha256::digest(public_values);
1716

18-
proofs_commitment.push(proof.commitment());
19-
2017
sp1_zkvm::lib::verify::verify_sp1_proof(&vkey, &public_values_digest.into());
2118
}
2219

23-
let merkle_root = compute_merkle_root(proofs_commitment);
20+
let merkle_tree =
21+
MerkleTree::<SP1VkAndPubInputs>::build(&input.proofs_vk_and_pub_inputs).unwrap();
2422

25-
sp1_zkvm::io::commit_slice(&merkle_root);
23+
sp1_zkvm::io::commit_slice(&merkle_tree.root);
2624
}

aggregation_mode/aggregation_programs/sp1/src/lib.rs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,30 @@ impl IsMerkleTreeBackend for SP1VkAndPubInputs {
4646
}
4747

4848
#[derive(Serialize, Deserialize)]
49-
pub struct Input {
50-
pub proofs_vk_and_pub_inputs: Vec<SP1VkAndPubInputs>,
51-
}
49+
pub struct Hash32(pub [u8; 32]);
5250

53-
fn combine_hashes(hash_a: &[u8; 32], hash_b: &[u8; 32]) -> [u8; 32] {
54-
let mut hasher = Keccak256::new();
55-
hasher.update(hash_a);
56-
hasher.update(hash_b);
57-
hasher.finalize().into()
58-
}
51+
impl IsMerkleTreeBackend for Hash32 {
52+
type Data = Hash32;
53+
type Node = [u8; 32];
5954

60-
/// Computes the merkle root for the given proofs
61-
pub fn compute_merkle_root(mut leaves_hash: Vec<[u8; 32]>) -> [u8; 32] {
62-
while leaves_hash.len() > 1 {
63-
leaves_hash = leaves_hash
64-
.chunks(2)
65-
.map(|chunk| match chunk {
66-
[a, b] => combine_hashes(&a, &b),
67-
[a] => combine_hashes(&a, &a),
68-
_ => panic!("Unexpected chunk size in leaves"),
69-
})
70-
.collect()
55+
fn hash_data(leaf: &Self::Data) -> Self::Node {
56+
leaf.0
57+
}
58+
59+
fn hash_new_parent(child_1: &Self::Node, child_2: &Self::Node) -> Self::Node {
60+
let mut hasher = Keccak256::new();
61+
hasher.update(child_1);
62+
hasher.update(child_2);
63+
hasher.finalize().into()
7164
}
65+
}
7266

73-
leaves_hash[0]
67+
#[derive(Serialize, Deserialize)]
68+
pub struct ChunkAggregatorInput {
69+
pub proofs_vk_and_pub_inputs: Vec<SP1VkAndPubInputs>,
70+
}
71+
72+
#[derive(Serialize, Deserialize)]
73+
pub struct RootAggregatorInput {
74+
pub proofs_and_leaves_commitment: Vec<(SP1VkAndPubInputs, Vec<[u8; 32]>)>,
7475
}
Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,47 @@
11
#![no_main]
22
sp1_zkvm::entrypoint!(main);
33

4+
use lambdaworks_crypto::merkle_tree::merkle::MerkleTree;
45
use sha2::{Digest, Sha256};
5-
use sp1_aggregation_program::{compute_merkle_root, Input};
6+
use sp1_aggregation_program::{Hash32, RootAggregatorInput};
67

7-
pub const LEAVES_AGG_PROGRAM_VK_HASH: [u32; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
8+
pub const CHUNK_AGGREGATOR_PROGRAM_VK_HASH: [u32; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
89

910
pub fn main() {
10-
let input = sp1_zkvm::io::read::<Input>();
11-
12-
println!("Number of proofs in input: {}", input.proofs_vk_and_pub_inputs.len());
11+
let input = sp1_zkvm::io::read::<RootAggregatorInput>();
1312

14-
let mut proofs_hash: Vec<[u8; 32]> = vec![];
15-
println!("Initial proofs_hash length: {}", proofs_hash.len());
13+
let mut leaves = vec![];
1614

1715
// Verify the proofs.
18-
for (i, proof) in input.proofs_vk_and_pub_inputs.iter().enumerate() {
19-
println!("Processing proof {}, public_inputs length: {}", i, proof.public_inputs.len());
20-
16+
for (proof, leaves_commitment) in input.proofs_and_leaves_commitment {
2117
let vkey = proof.vk;
2218
let public_values_digest = Sha256::digest(&proof.public_inputs);
2319

2420
// Ensure the aggregated chunk originates from the L1 aggregation program.
2521
// This validation step guarantees that the proof was genuinely verified
2622
// by this program. Without this check, a different program using the
2723
// same public inputs could bypass verification.
28-
29-
// TODO: Add the assert here
30-
//assert!(proof.vk == LEAVES_AGG_PROGRAM_VK_HASH);
24+
assert!(proof.vk == CHUNK_AGGREGATOR_PROGRAM_VK_HASH);
3125

3226
let merkle_root: [u8; 32] = proof
3327
.public_inputs
3428
.clone()
3529
.try_into()
3630
.expect("Public input to be the hash of the chunk tree");
3731

38-
proofs_hash.push(merkle_root);
39-
println!("proofs_hash length after push: {}", proofs_hash.len());
32+
// Reconstruct the merkle tree and verify that the roots match
33+
let leaves_commitment: Vec<Hash32> =
34+
leaves_commitment.into_iter().map(|el| Hash32(el)).collect();
35+
let merkle_tree: MerkleTree<Hash32> = MerkleTree::build(&leaves_commitment).unwrap();
36+
assert!(merkle_tree.root == merkle_root);
4037

41-
println!("vkey (debug): {:?}", vkey);
42-
println!("public_values_digest (debug): {:?}", public_values_digest);
38+
leaves.extend(leaves_commitment);
4339

4440
sp1_zkvm::lib::verify::verify_sp1_proof(&vkey, &public_values_digest.into());
4541
}
4642

47-
println!("Final proofs_hash length before compute_merkle_root: {}", proofs_hash.len());
48-
let merkle_root = compute_merkle_root(proofs_hash);
43+
// Finally, compute the final merkle root with all the leaves
44+
let merkle_tree: MerkleTree<Hash32> = MerkleTree::build(&leaves).unwrap();
4945

50-
sp1_zkvm::io::commit_slice(&merkle_root);
46+
sp1_zkvm::io::commit_slice(&merkle_tree.root);
5147
}

0 commit comments

Comments
 (0)