Skip to content

Commit a095608

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

File tree

7 files changed

+93
-100
lines changed

7 files changed

+93
-100
lines changed

aggregation_mode/aggregation_programs/risc0/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ path = "./src/lib.rs"
1717

1818
[[bin]]
1919
name = "risc0_chunk_aggregator_program"
20-
path = "./bin/chunk_aggregator.rs"
20+
path = "./src/chunk_aggregator_main.rs"
2121

2222
[[bin]]
2323
name = "risc0_root_aggregator_program"
24-
path = "./bin/root_aggregator.rs"
24+
path = "./src/root_aggregator_main.rs"

aggregation_mode/aggregation_programs/risc0/bin/chunk_aggregator.rs

Lines changed: 0 additions & 22 deletions
This file was deleted.

aggregation_mode/aggregation_programs/risc0/bin/root_aggregator.rs

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![no_main]
2+
3+
use lambdaworks_crypto::merkle_tree::merkle::MerkleTree;
4+
use risc0_aggregation_program::{ChunkAggregatorInput, Risc0ImageIdAndPubInputs};
5+
use risc0_zkvm::guest::env;
6+
7+
risc0_zkvm::guest::entry!(main);
8+
9+
fn main() {
10+
let input = env::read::<ChunkAggregatorInput>();
11+
12+
for proof in &input.proofs_image_id_and_pub_inputs {
13+
env::verify(proof.image_id.clone(), &proof.public_inputs)
14+
.expect("proof to be verified correctly");
15+
}
16+
17+
let merkle_tree =
18+
MerkleTree::<Risc0ImageIdAndPubInputs>::build(&input.proofs_image_id_and_pub_inputs)
19+
.unwrap();
20+
21+
env::commit_slice(&merkle_tree.root);
22+
}

aggregation_mode/aggregation_programs/risc0/src/lib.rs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -52,32 +52,32 @@ impl IsMerkleTreeBackend for Risc0ImageIdAndPubInputs {
5252
}
5353

5454
#[derive(Serialize, Deserialize)]
55-
pub struct Input {
56-
pub proofs_image_id_and_pub_inputs: Vec<Risc0ImageIdAndPubInputs>,
57-
}
55+
pub struct Hash32(pub [u8; 32]);
5856

59-
fn combine_hashes(hash_a: &[u8; 32], hash_b: &[u8; 32]) -> [u8; 32] {
60-
let mut hasher = Keccak::v256();
61-
hasher.update(hash_a);
62-
hasher.update(hash_b);
57+
impl IsMerkleTreeBackend for Hash32 {
58+
type Data = Hash32;
59+
type Node = [u8; 32];
6360

64-
let mut hash = [0u8; 32];
65-
hasher.finalize(&mut hash);
66-
hash
67-
}
61+
fn hash_data(leaf: &Self::Data) -> Self::Node {
62+
leaf.0
63+
}
6864

69-
/// Computes the merkle root for the given proofs
70-
pub fn compute_merkle_root(mut leaves: Vec<[u8; 32]>) -> [u8; 32] {
71-
while leaves.len() > 1 {
72-
leaves = leaves
73-
.chunks(2)
74-
.map(|chunk| match chunk {
75-
[a, b] => combine_hashes(&a, &b),
76-
[a] => combine_hashes(&a, &a),
77-
_ => panic!("Unexpected chunk size in leaves"),
78-
})
79-
.collect()
65+
fn hash_new_parent(child_1: &Self::Node, child_2: &Self::Node) -> Self::Node {
66+
let mut hasher = Keccak::v256();
67+
hasher.update(child_1);
68+
hasher.update(child_2);
69+
let mut hash = [0u8; 32];
70+
hasher.finalize(&mut hash);
71+
hash
8072
}
73+
}
8174

82-
leaves[0]
75+
#[derive(Serialize, Deserialize)]
76+
pub struct ChunkAggregatorInput {
77+
pub proofs_image_id_and_pub_inputs: Vec<Risc0ImageIdAndPubInputs>,
78+
}
79+
80+
#[derive(Serialize, Deserialize)]
81+
pub struct RootAggregatorInput {
82+
pub proofs_and_leaves_commitment: Vec<(Risc0ImageIdAndPubInputs, Vec<[u8; 32]>)>,
8383
}

aggregation_mode/aggregation_programs/risc0/src/main.rs

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#![no_main]
2+
3+
use lambdaworks_crypto::merkle_tree::merkle::MerkleTree;
4+
use risc0_aggregation_program::{Hash32, RootAggregatorInput};
5+
use risc0_zkvm::guest::env;
6+
7+
risc0_zkvm::guest::entry!(main);
8+
9+
pub const CHUNK_AGGREGATOR_PROGRAM_IMAGE_ID: [u8; 32] = [0u8; 32];
10+
11+
fn main() {
12+
let input = env::read::<RootAggregatorInput>();
13+
14+
let mut leaves: Vec<Hash32> = vec![];
15+
16+
for (proof, leaves_commitment) in input.proofs_and_leaves_commitment {
17+
let image_id = proof.image_id;
18+
19+
// Ensure the aggregated chunk originates from the L1 aggregation program.
20+
// This validation step guarantees that the proof was genuinely verified
21+
// by this program. Without this check, a different program using the
22+
// same public inputs could bypass verification.
23+
assert!(image_id == CHUNK_AGGREGATOR_PROGRAM_IMAGE_ID);
24+
25+
// Ensure the committed root matches the root of the provided leaves
26+
let merkle_root: [u8; 32] = proof
27+
.public_inputs
28+
.clone()
29+
.try_into()
30+
.expect("Public input to be the chunk merkle root");
31+
32+
let leaves_commitment: Vec<Hash32> =
33+
leaves_commitment.into_iter().map(|el| Hash32(el)).collect();
34+
let merkle_tree = MerkleTree::<Hash32>::build(&leaves_commitment).unwrap();
35+
assert!(merkle_root == merkle_tree.root);
36+
37+
leaves.extend(leaves_commitment);
38+
39+
// finally verify the proof
40+
env::verify(image_id, &proof.public_inputs).expect("proof to be verified correctly");
41+
}
42+
43+
let merkle_tree = MerkleTree::<Hash32>::build(&leaves).unwrap();
44+
45+
env::commit_slice(&merkle_tree.root);
46+
}

0 commit comments

Comments
 (0)