Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f86fa47
feat: onchain verification for C7 proof
cedoor Mar 17, 2026
63ec6d7
feat: onchain verification for C5 proof
cedoor Mar 18, 2026
e794e9c
chore: update script to generate verifiers
cedoor Mar 18, 2026
5898b1a
fix: update function to encode zk proofs in rust
cedoor Mar 18, 2026
9e63ed5
chore: update pk_aggregation onchain verifier
cedoor Mar 18, 2026
6805ae3
refactor: move pk verification to registry
cedoor Mar 18, 2026
32f96e3
fix: use encode_params
ctrlc03 Mar 18, 2026
d99c46c
feat: make aggregation optional
ctrlc03 Mar 18, 2026
aeeb438
fix: solve several issues after E3 struct update
cedoor Mar 19, 2026
cffcc89
fix: align pk commitment computation in c5 & ude
cedoor Mar 19, 2026
d4a27a4
fix: agg_pk1 in c5 is now not scaled by h
cedoor Mar 19, 2026
3365f48
chore: decrease e3 duration
cedoor Mar 19, 2026
0260f4f
refactor: align message encoding/decoding in C7 & crisp
cedoor Mar 20, 2026
1f16121
fix: handle disabled aggregation for C6 proofs
cedoor Mar 20, 2026
0fc6d6b
refactor: make all pk polynomials center correctly
cedoor Mar 20, 2026
bf97b0a
refactor: remove unused imports
cedoor Mar 20, 2026
b1d8418
style: format code with noir fmt
cedoor Mar 20, 2026
19a70f8
fix(e3-evm): pair zk proof ABI test with abi_decode_params
cedoor Mar 20, 2026
0b7e7a8
feat(enclave-contracts): improve deploy chain keys and verifier flow
cedoor Mar 20, 2026
c305572
fix(crisp-server): default e3_proof_aggregation_enabled when unset
cedoor Mar 20, 2026
0df905f
feat(zk-prover): pass CircuitVariant through e2e_proof_tests macro
cedoor Mar 20, 2026
6058876
chore(crisp-server): ensure .env.example ends with newline
cedoor Mar 20, 2026
0c4d7bf
chore: update contract addresses
cedoor Mar 20, 2026
f481491
fix(zk-prover): delegate share_computation prove_with_variant to prove
cedoor Mar 20, 2026
666a02d
chore: update run option in hardhat config
cedoor Mar 20, 2026
d0e1e62
chore: update template program contract address
cedoor Mar 20, 2026
61cd24c
fix: prevent premature PublicKeyAggregated when DKG proofs pending
cedoor Mar 20, 2026
aa03e09
style: format code with rustfmt
cedoor Mar 20, 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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion circuits/lib/src/configs/default/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ pub use super::insecure::threshold;

/// Max number of non-zero coefficients in the message polynomial.
/// This is a conservative estimate that should be okay for most use cases.
pub global MAX_MSG_NON_ZERO_COEFFS: u32 = 80;
pub global MAX_MSG_NON_ZERO_COEFFS: u32 = 100;
1,070 changes: 535 additions & 535 deletions circuits/lib/src/configs/insecure/threshold.nr

Large diffs are not rendered by default.

32,562 changes: 16,281 additions & 16,281 deletions circuits/lib/src/configs/secure/threshold.nr

Large diffs are not rendered by default.

45 changes: 29 additions & 16 deletions circuits/lib/src/core/threshold/pk_aggregation.nr
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ impl<let L: u32> Configs<L> {
/// Public Key Aggregation (Circuit 5).
///
/// Verifies that for each CRT basis l and each coefficient i:
/// - pk0_agg[l][i] = sum_h(pk0[h][l][i]) mod q_l
/// - pk1_agg[l][i] = sum_h(pk1[h][l][i]) mod q_l
/// - pk0_agg[l][i] = sum_h(pk0[h][l][i]) mod q_l (witnesses use centered coefficients; aggregation
/// uses a half-q shift so [`ModU128::reduce_mod`] sees small non-negative integers)
/// - pk1_agg[l][i] = pk1[0][l][i] (all parties share pk1 = a, aggregated pk1 = a)
pub struct PkAggregation<let N: u32, let H: u32, let L: u32, let BIT_PK: u32> {
/// Circuit parameters including CRT moduli
configs: Configs<L>,
Expand Down Expand Up @@ -74,7 +75,7 @@ impl<let N: u32, let H: u32, let L: u32, let BIT_PK: u32> PkAggregation<N, H, L,
}
}

fn verify_pk_for_basis(
fn verify_pk0_for_basis(
self,
pk: [[Polynomial<N>; L]; H],
pk_agg: [Polynomial<N>; L],
Expand All @@ -83,21 +84,33 @@ impl<let N: u32, let H: u32, let L: u32, let BIT_PK: u32> PkAggregation<N, H, L,
let q_l = self.configs.qis[basis_idx];
let mod_q_l = ModU128::new(q_l);

let half_qi: Field = (q_l - 1) / 2;
let h_half_qi: Field = H as Field * half_qi;

for coeff_idx in 0..N {
// Sum pk coefficients from all honest parties
let mut sum_pk: Field = 0;
let mut sum_shifted: Field = 0;
for party_idx in 0..H {
sum_pk = sum_pk + pk[party_idx][basis_idx].coefficients[coeff_idx];
sum_shifted =
sum_shifted + pk[party_idx][basis_idx].coefficients[coeff_idx] + half_qi;
}
let sum_reduced = mod_q_l.reduce_mod(sum_shifted);

// Reduce mod q_l
let sum_pk_reduced = mod_q_l.reduce_mod(sum_pk);
let expected =
mod_q_l.reduce_mod(pk_agg[basis_idx].coefficients[coeff_idx] + h_half_qi);
assert(sum_reduced == expected, "pk0 aggregation mismatch");
}
}

// Verify equality
assert(
sum_pk_reduced == pk_agg[basis_idx].coefficients[coeff_idx],
"pk aggregation mismatch",
);
/// pk1 is the same (a) for all parties; pk1_agg = a
fn verify_pk1(self, basis_idx: u32) {
for coeff_idx in 0..N {
for party_idx in 0..H {
assert(
self.pk1[party_idx][basis_idx].coefficients[coeff_idx]
== self.pk1_agg[basis_idx].coefficients[coeff_idx],
"pk1 mismatch",
);
}
}
}

Expand All @@ -107,10 +120,10 @@ impl<let N: u32, let H: u32, let L: u32, let BIT_PK: u32> PkAggregation<N, H, L,
// 0. Verify pk commitments
self.verify_pk_commitments();

// 1. Verify pk0 & pk1 aggregations for each CRT basis
// 1. Verify pk0 aggregation (sum) and pk1 (all equal to a, pk1_agg = a)
for basis_idx in 0..L {
self.verify_pk_for_basis(self.pk0, self.pk0_agg, basis_idx);
self.verify_pk_for_basis(self.pk1, self.pk1_agg, basis_idx);
self.verify_pk0_for_basis(self.pk0, self.pk0_agg, basis_idx);
self.verify_pk1(basis_idx);
}

// 2. Commit to aggregated threshold public key
Expand Down
1 change: 1 addition & 0 deletions crates/aggregator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ e3-trbfv = { workspace = true }
e3-bfv-client = { workspace = true }
e3-request = { workspace = true }
e3-sortition = { workspace = true }
e3-zk-helpers = { workspace = true }
e3-utils = { workspace = true }
serde = { workspace = true }
tracing = { workspace = true }
16 changes: 14 additions & 2 deletions crates/aggregator/src/proof_fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use e3_events::{
prelude::*, BusHandle, ComputeRequest, CorrelationId, E3id, EventContext, Proof, Sequenced,
ZkRequest,
};
use tracing::{error, info, warn};
use tracing::{error, info};

/// Manages the state of a sequential `FoldProofs` operation.
///
Expand All @@ -28,6 +28,9 @@ pub struct ProofFoldState {
total_steps: Option<usize>,
/// Set when all fold steps have completed.
pub result: Option<Proof>,
/// `start` was called with zero proofs — folding is complete with no aggregate.
#[serde(default)]
pub fold_input_was_empty: bool,
}

impl ProofFoldState {
Expand All @@ -38,6 +41,7 @@ impl ProofFoldState {
remaining: Vec::new(),
total_steps: None,
result: None,
fold_input_was_empty: false,
}
}

Expand All @@ -59,7 +63,7 @@ impl ProofFoldState {

/// Begin folding `proofs` sequentially.
///
/// - 0 proofs → `result` stays `None`
/// - 0 proofs → `result` stays `None`, `fold_input_was_empty` is set (caller can treat fold as done)
/// - 1 proof → `result` is set immediately, no ZK request dispatched
/// - N proofs → first fold step dispatched; subsequent steps follow via `handle_response`
pub fn start(
Expand All @@ -70,9 +74,17 @@ impl ProofFoldState {
e3_id: &E3id,
ec: &EventContext<Sequenced>,
) -> Result<()> {
self.correlation = None;
self.accumulated = None;
self.remaining.clear();
self.total_steps = None;
self.result = None;
self.fold_input_was_empty = false;
Comment thread
cedoor marked this conversation as resolved.

match proofs.len() {
0 => {
info!("{label}: no proofs to fold");
self.fold_input_was_empty = true;
Ok(())
}
1 => {
Expand Down
Loading
Loading