Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
10955a7
fix(build-circuits): discover nested wrapper circuits
cedoor Feb 26, 2026
a0a6c9c
fix(build-circuits): preserve vk_hash per circuit in shared target dirs
cedoor Feb 26, 2026
11ed107
refactor: set non-zk verification for wrapper circuits
cedoor Feb 26, 2026
e20f735
build: add recursive verification keys to build-circuits.ts
cedoor Feb 26, 2026
a5cf20c
feat: add recursive aggregation module to zk-prover
cedoor Feb 26, 2026
e559144
style: format with rustfmt
cedoor Feb 26, 2026
d8721d3
test: add new test for 2-proof wrapper generation
cedoor Feb 27, 2026
831e86e
refactor(build-circuits): generate recursive VK only for wrappers
cedoor Feb 27, 2026
57a1bf6
feat: add fold proof generation and verification
cedoor Feb 27, 2026
2143873
refactor: update wrapper & fold circuit inputs
cedoor Feb 27, 2026
2b923ec
fix(build-circuits): fail fast when VK generation fails
cedoor Mar 2, 2026
a256cdc
refactor(scripts): replace --oracle-hash with -t verifierTarget
cedoor Mar 2, 2026
456fb42
refactor: use evm target for proof generation and verification
cedoor Mar 2, 2026
82a1978
refactor: add vk hash chain to wrapper and fold circuits
cedoor Mar 2, 2026
b6b614e
style: fix linting errors
cedoor Mar 2, 2026
4e17e8d
fix(zk-prover,build): recursive aggregation cleanup, docs, VK validation
cedoor Mar 3, 2026
c5cd0f7
fix: use verify_proof instead of verify on ZkProver
cedoor Mar 3, 2026
9bdfc1e
fix(zk-prover): validate exact length of fold proof public inputs
cedoor Mar 3, 2026
ead3304
fix(zk-prover): validate circuit compatibility before recursive proof…
cedoor Mar 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions circuits/bin/recursive_aggregation/fold/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,47 @@
// or FITNESS FOR A PARTICULAR PURPOSE.

use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};
use lib::math::commitments::compute_recursive_aggregation_commitment;
use lib::math::commitments::{compute_recursive_aggregation_commitment, compute_vk_hash};

fn main(
verification_key: UltraHonkVerificationKey,
proofs: [UltraHonkProof; 2],
commitments: pub [Field; 2],
key_hash: Field,
) -> pub Field {
verify_honk_proof_non_zk(verification_key, proofs[0], [commitments[0]], key_hash);
verify_honk_proof_non_zk(verification_key, proofs[1], [commitments[1]], key_hash);
proof1_verification_key: UltraHonkVerificationKey,
proof1_proof: UltraHonkProof,
proof1_public_inputs: [Field; 2],
proof1_key_hash: Field,
proof2_verification_key: UltraHonkVerificationKey,
proof2_proof: UltraHonkProof,
proof2_public_inputs: [Field; 2],
proof2_key_hash: Field,
) -> pub (Field, Field) {
verify_honk_proof_non_zk(
proof1_verification_key,
proof1_proof,
proof1_public_inputs,
proof1_key_hash,
);
verify_honk_proof_non_zk(
proof2_verification_key,
proof2_proof,
proof2_public_inputs,
proof2_key_hash,
);

// Hash the two commitments with Poseidon so the verifier can check the folded proof used the expected public inputs.
let mut commitments_vec = Vec::new();
commitments_vec.push(proof1_public_inputs[1]);
commitments_vec.push(proof2_public_inputs[1]);

commitments_vec.push(commitments[0]);
commitments_vec.push(commitments[1]);
let commitment = compute_recursive_aggregation_commitment(commitments_vec);

compute_recursive_aggregation_commitment(commitments_vec)
// Hash the full VK chain: attested key hashes from inner proofs (public_inputs[0]) plus the VKs
// that verified them (proof*_key_hash). This combined fingerprint lets the verifier check the
// entire proof genealogy: which circuits were folded and which verified each level.
let mut vk_hashes = Vec::new();
vk_hashes.push(proof1_public_inputs[0]); // key_hash attested by proof1 (from its inner folds)
vk_hashes.push(proof2_public_inputs[0]); // key_hash attested by proof2 (from its inner folds)
vk_hashes.push(proof1_key_hash); // VK hash of circuit that produced proof1
vk_hashes.push(proof2_key_hash); // VK hash of circuit that produced proof2
let key_hash = compute_vk_hash(vk_hashes);

(key_hash, commitment)
}
8 changes: 4 additions & 4 deletions circuits/bin/recursive_aggregation/wrapper/dkg/pk/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof};
use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};
use lib::math::commitments::compute_recursive_aggregation_commitment;

// Number of proofs.
Expand All @@ -14,12 +14,12 @@ pub global N_PUBLIC_INPUTS: u32 = 1;

fn main(
verification_key: UltraHonkVerificationKey,
proofs: [UltraHonkZKProof; N_PROOFS],
public_inputs: pub [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
proofs: [UltraHonkProof; N_PROOFS],
public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
key_hash: pub Field,
) -> pub Field {
for i in 0..N_PROOFS {
verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash);
verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash);
}

let mut aggregated_public_inputs = Vec::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof};
use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};
use lib::configs::default::dkg::L_THRESHOLD;
use lib::{configs::default::N_PARTIES, math::commitments::compute_recursive_aggregation_commitment};

Expand All @@ -15,12 +15,12 @@ pub global N_PUBLIC_INPUTS: u32 = (L_THRESHOLD * N_PARTIES) + 1;

fn main(
verification_key: UltraHonkVerificationKey,
proofs: [UltraHonkZKProof; N_PROOFS],
public_inputs: pub [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
proofs: [UltraHonkProof; N_PROOFS],
public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
key_hash: pub Field,
) -> pub Field {
for i in 0..N_PROOFS {
verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash);
verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash);
}

let mut aggregated_public_inputs = Vec::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof};
use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};
use lib::configs::default::dkg::L_THRESHOLD;
use lib::configs::default::H;
use lib::math::commitments::compute_recursive_aggregation_commitment;
Expand All @@ -16,12 +16,12 @@ pub global N_PUBLIC_INPUTS: u32 = (H * L_THRESHOLD) + 1;

fn main(
verification_key: UltraHonkVerificationKey,
proofs: [UltraHonkZKProof; N_PROOFS],
public_inputs: pub [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
proofs: [UltraHonkProof; N_PROOFS],
public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
key_hash: pub Field,
) -> pub Field {
for i in 0..N_PROOFS {
verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash);
verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash);
}

let mut aggregated_public_inputs = Vec::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof};
use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};
use lib::{
configs::default::dkg::{L, N},
math::commitments::compute_recursive_aggregation_commitment,
Expand All @@ -17,12 +17,12 @@ pub global N_PUBLIC_INPUTS: u32 = (2 * L * N) + 2;

fn main(
verification_key: UltraHonkVerificationKey,
proofs: [UltraHonkZKProof; N_PROOFS],
public_inputs: pub [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
proofs: [UltraHonkProof; N_PROOFS],
public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
key_hash: pub Field,
) -> pub Field {
for i in 0..N_PROOFS {
verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash);
verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash);
Comment thread
cedoor marked this conversation as resolved.
}

let mut aggregated_public_inputs = Vec::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof};
use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};
use lib::{
configs::default::{MAX_MSG_NON_ZERO_COEFFS, T, threshold::L},
math::commitments::compute_recursive_aggregation_commitment,
Expand All @@ -18,12 +18,12 @@ pub global N_PUBLIC_INPUTS: u32 =

fn main(
verification_key: UltraHonkVerificationKey,
proofs: [UltraHonkZKProof; N_PROOFS],
public_inputs: pub [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
proofs: [UltraHonkProof; N_PROOFS],
public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
key_hash: pub Field,
) -> pub Field {
for i in 0..N_PROOFS {
verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash);
verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash);
}

let mut aggregated_public_inputs = Vec::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof};
use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};
use lib::{configs::default::H, math::commitments::compute_recursive_aggregation_commitment};

// Number of proofs.
Expand All @@ -14,12 +14,12 @@ pub global N_PUBLIC_INPUTS: u32 = H + 1;

fn main(
verification_key: UltraHonkVerificationKey,
proofs: [UltraHonkZKProof; N_PROOFS],
public_inputs: pub [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
proofs: [UltraHonkProof; N_PROOFS],
public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
key_hash: pub Field,
) -> pub Field {
for i in 0..N_PROOFS {
verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash);
verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash);
}

let mut aggregated_public_inputs = Vec::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof};
use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};
use lib::configs::default::threshold::{L, N};
use lib::math::commitments::compute_recursive_aggregation_commitment;

Expand All @@ -15,12 +15,12 @@ pub global N_PUBLIC_INPUTS: u32 = (L * N) + 3;

fn main(
verification_key: UltraHonkVerificationKey,
proofs: [UltraHonkZKProof; N_PROOFS],
public_inputs: pub [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
proofs: [UltraHonkProof; N_PROOFS],
public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
key_hash: pub Field,
) -> pub Field {
for i in 0..N_PROOFS {
verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash);
verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash);
}

let mut aggregated_public_inputs = Vec::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

use bb_proof_verification::{UltraHonkVerificationKey, UltraHonkZKProof, verify_honk_proof};
use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk};
use lib::configs::default::threshold::{L, N};
use lib::math::commitments::compute_recursive_aggregation_commitment;

Expand All @@ -15,12 +15,12 @@ pub global N_PUBLIC_INPUTS: u32 = 2 + 3 * L * N;

fn main(
verification_key: UltraHonkVerificationKey,
proofs: [UltraHonkZKProof; N_PROOFS],
public_inputs: pub [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
proofs: [UltraHonkProof; N_PROOFS],
public_inputs: [[Field; N_PUBLIC_INPUTS]; N_PROOFS],
key_hash: pub Field,
) -> pub Field {
for i in 0..N_PROOFS {
verify_honk_proof(verification_key, proofs[i], public_inputs[i], key_hash);
verify_honk_proof_non_zk(verification_key, proofs[i], public_inputs[i], key_hash);
}

let mut aggregated_public_inputs = Vec::new();
Expand Down
11 changes: 11 additions & 0 deletions circuits/lib/src/math/commitments.nr
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ pub global DS_AGGREGATED_SHARES: [u8; 64] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
// Domain separator - "VK_HASH"
pub global DS_VK_HASH: [u8; 64] = [
0x56, 0x4b, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
// Domain separator - "RECURSIVE_AGGREGATION"
pub global DS_RECURSIVE_AGGREGATION: [u8; 64] = [
0x52, 0x45, 0x43, 0x55, 0x52, 0x53, 0x49, 0x56, 0x45, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47,
Expand Down Expand Up @@ -228,6 +235,10 @@ pub fn compute_recursive_aggregation_commitment(payload: Vec<Field>) -> Field {
compute_commitment(payload, DS_RECURSIVE_AGGREGATION)
}

pub fn compute_vk_hash(vk_hashes: Vec<Field>) -> Field {
compute_commitment(vk_hashes, DS_VK_HASH)
}

pub fn compute_ciphertext_commitment<let N: u32, let L: u32, let BIT_CT: u32>(
ct0: [Polynomial<N>; L],
ct1: [Polynomial<N>; L],
Expand Down
9 changes: 9 additions & 0 deletions crates/events/src/enclave_event/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ pub enum CircuitName {
DecryptedSharesAggregationBn,
/// Decrypted shares aggregation proof — Modular variant (C7b).
DecryptedSharesAggregationMod,
/// Recursive aggregation fold circuit (independent; lives at recursive_aggregation/fold).
Fold,
}

impl CircuitName {
Expand All @@ -71,6 +73,7 @@ impl CircuitName {
CircuitName::ThresholdShareDecryption => "share_decryption",
CircuitName::DecryptedSharesAggregationBn => "decrypted_shares_aggregation_bn",
CircuitName::DecryptedSharesAggregationMod => "decrypted_shares_aggregation_mod",
CircuitName::Fold => "fold",
}
}

Expand All @@ -86,12 +89,18 @@ impl CircuitName {
CircuitName::PkAggregation => "threshold",
CircuitName::DecryptedSharesAggregationBn => "threshold",
CircuitName::DecryptedSharesAggregationMod => "threshold",
CircuitName::Fold => "recursive_aggregation",
}
}

pub fn dir_path(&self) -> String {
format!("{}/{}", self.group(), self.as_str())
}

/// Wrapper circuit path: `recursive_aggregation/wrapper/{group}/{name}`.
pub fn wrapper_dir_path(&self) -> String {
format!("recursive_aggregation/wrapper/{}", self.dir_path())
}
Comment thread
cedoor marked this conversation as resolved.
}

impl fmt::Display for CircuitName {
Expand Down
4 changes: 2 additions & 2 deletions crates/multithread/src/multithread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ fn handle_verify_share_proofs(

// 2. ZK proof verification
let proof = &signed_proof.payload.proof;
let result = prover.verify(proof, &e3_id_str, sender);
let result = prover.verify_proof(proof, &e3_id_str, sender);
match result {
Ok(true) => continue,
Ok(false) | Err(_) => {
Expand Down Expand Up @@ -929,7 +929,7 @@ fn handle_verify_share_decryption_proofs(

// 2. ZK proof verification
let proof = &signed_proof.payload.proof;
let result = prover.verify(proof, &e3_id_str, sender);
let result = prover.verify_proof(proof, &e3_id_str, sender);
match result {
Ok(true) => continue,
Ok(false) | Err(_) => {
Expand Down
10 changes: 3 additions & 7 deletions crates/zk-prover/src/actors/zk_actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,9 @@ impl Handler<TypedEvent<ZkVerificationRequest>> for ZkActor {
);

let e3_id_str = msg.e3_id.to_string();
let result = self.prover.verify_proof(
msg.proof.circuit,
&msg.proof.data,
&msg.proof.public_signals,
&e3_id_str,
msg.key.party_id,
);
let result = self
.prover
.verify_proof(&msg.proof, &e3_id_str, msg.key.party_id);

let response = TypedEvent::new(
match result {
Expand Down
1 change: 1 addition & 0 deletions crates/zk-prover/src/circuits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
// or FITNESS FOR A PARTICULAR PURPOSE.

mod dkg;
pub mod recursive_aggregation;
mod threshold;
pub(crate) mod utils;
Loading
Loading