Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c3ef68d
feat(zk): add c3_fold and c3_fold_merge for ad-hoc C3 aggregation
cedoor Apr 9, 2026
069398f
feat(zk): compact C3/C6 ciphertext commitments for aggregation
cedoor Apr 9, 2026
124a10e
style: format code with rustfmt & nargo fmt
cedoor Apr 9, 2026
699c7f5
feat(zk): sequential c3_fold with is_first_step; remove c3_fold_merge
cedoor Apr 10, 2026
b08de8f
feat(circuits): recursive aggregation folds and aggregators
cedoor Apr 13, 2026
4bfdb7b
Merge branch 'main' into refactor/new-proof-aggregation
cedoor Apr 13, 2026
4da2a43
refactor: remove unused code
cedoor Apr 14, 2026
3edebc3
fix(zk): bind all CRT limbs in c3 fold aggregation
cedoor Apr 14, 2026
e60345b
feat(zk-prover): C3 fold kernel and sequential aggregation API
cedoor Apr 15, 2026
fe14170
feat(zk-prover): add c6 fold kernel and sequential c6 fold
cedoor Apr 15, 2026
f770c91
test(zk-prover): add node_fold pipeline artifact and compile checks
cedoor Apr 15, 2026
88d3f87
test(zk-prover): add node_fold correlated e2e and rename fold accumul…
cedoor Apr 15, 2026
d5f746c
feat(zk): recursive DKG aggregation, nodes_fold kernel, ZK proof padding
cedoor Apr 16, 2026
f82f3ef
style: apply rustfmt to aggregator, zk events, multithread
cedoor Apr 16, 2026
d954ca5
fix: clarify comment on cross-node C0 to C3 share grid assertion in m…
cedoor Apr 20, 2026
477af3f
refactor: use dkg and decryption aggregator verifiers across stack
cedoor Apr 20, 2026
62f5d2d
Merge branch 'main' into refactor/new-proof-aggregation
cedoor Apr 20, 2026
1273968
fix(circuits): enforce genesis-only fold_kernel circuits
cedoor Apr 21, 2026
36adc71
refactor(circuits): derive fold public IO offsets from layout constants
cedoor Apr 21, 2026
573a822
docs(circuits): clarify C0 vs C1 pk commitments in node_fold
cedoor Apr 21, 2026
5566ab6
fix(aggregator): use T+1 for DecryptionAggregation c6_total_slots
cedoor Apr 21, 2026
9810263
fix(events): require PublicKeyAggregated pk_commitment in serde
cedoor Apr 21, 2026
75bc385
chore: use btreemap to keep party id ordering
ctrlc03 Apr 21, 2026
0158474
fix(zk-prover): thread slot_index through C6 fold kernel genesis proof
cedoor Apr 21, 2026
29dc2c5
fix: ordering
ctrlc03 Apr 21, 2026
7958dbd
chore: format
ctrlc03 Apr 21, 2026
593aa26
fix(threshold): align C6 commitment and decryption aggregation wiring
cedoor Apr 23, 2026
dc8a2fa
chore: update CRISP config and cli
cedoor Apr 23, 2026
ea1c5ec
style: format code with rustfmt
cedoor Apr 23, 2026
bfeb744
chore: review and refactor code
hmzakhalid Apr 25, 2026
091e993
fix: prettier
hmzakhalid Apr 25, 2026
c4a2b1e
fix: prettier
hmzakhalid Apr 25, 2026
310d78d
fix: review comments
hmzakhalid Apr 25, 2026
330a23d
fix: template integration test
hmzakhalid Apr 25, 2026
f3be7c7
fix: refactor
hmzakhalid Apr 26, 2026
03f5216
fix(threshold): commit d_native_trunc with native CRT bit width
cedoor Apr 27, 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
3 changes: 3 additions & 0 deletions Cargo.lock

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

32 changes: 21 additions & 11 deletions agent/flow-trace/04_DKG_AND_COMPUTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,15 +262,24 @@ ProofRequestActor receives ThresholdSharePending
→ Ensures no incomplete data is gossiped
```

**C2 inner vs wrapper:** For each C2a/C2b request, the prover builds a **recursive** proof for
`sk_share_computation` / `e_sm_share_computation`. That **inner** `Proof` is what
`PendingThresholdProofs` stores and what gets ECDSA-signed for gossip
(`ProofType::C2aSkShareComputation` / `C2bESmShareComputation`). When node proof aggregation is
enabled, the prover also returns a **wrapper** proof (`CircuitName::ShareComputation`); the actor
publishes `DKGInnerProofReady` with that wrapper for folding, but does not replace the signed inner
payload. Verifiers treat C2a/C2b as allowing both inner and wrapper circuit names
(`ProofType::circuit_names()`); multithread ZK verify uses standard `bb verify` for inner circuits
and the wrapper VK path when the bundle’s circuit is `ShareComputation`.
**C2 proofs:** For each C2a/C2b request, the prover builds a **recursive** proof for
`sk_share_computation` / `e_sm_share_computation`. That `Proof` is what `PendingThresholdProofs`
stores and what gets ECDSA-signed for gossip (`ProofType::C2aSkShareComputation` /
`C2bESmShareComputation`). The old generic `recursive_aggregation/wrapper/*` circuits and two-proof
`recursive_aggregation/fold` were removed; aggregation is done by ad-hoc Noir bins under
`circuits/bin/recursive_aggregation/` (e.g. `c2ab_fold`, `c3ab_fold`, `c6_fold`, `node_fold`,
`nodes_fold`, `dkg_aggregator`, `decryption_aggregator` — `nodes_fold` chains `H` `node_fold` proofs
for `dkg_aggregator`; `decryption_aggregator` folds C6 via non-ZK `c6_fold` then checks C7 with ZK).
The per-circuit `wrapper/` Noir step was removed; aggregator response structs no longer carry a
`wrapped_proof` field — the inner recursive proof itself is what flows between stages.

**Ciphernode / aggregator integration:** `ZkRequest::FoldProofs` was removed. The multithread actor
implements `ZkRequest::NodeDkgFold` (full per-node pipeline to a `NodeFold` proof),
`ZkRequest::DkgAggregation` (`NodesFold` + C5 + `DkgAggregator`), and
`ZkRequest::DecryptionAggregation` (per-ciphertext `C6Fold` + C7 + `DecryptionAggregator`).
`NodeProofAggregator` buffers all `DKGInnerProofReady` proofs then issues one `NodeDkgFold` request;
`PublicKeyAggregator` and `ThresholdPlaintextAggregator` dispatch the aggregator requests instead of
pairwise folding.

### Step 6: Collect All Threshold Shares (with C2/C3 Verification)

Expand Down Expand Up @@ -348,6 +357,7 @@ ShareVerificationActor receives ShareVerificationDispatched(kind=ShareProofs)
│ │ │ C4a→C6 (SameParty): C4a's commitment == C6's expected_sk_commitment
│ │ │ C4b→C6 (SameParty): C4b's commitment == C6's expected_e_sm_commitment
│ │ │ C6→C7 (CrossParty): C6's d_commitment matches C7's expected_d_commitment
│ │ │ (on-chain / E3 state) C3 `ct_commitment` output and C6 `ct_commitment` input bind to the same ciphertext as user_data_encryption (not a CommitmentLink row)
│ │ │
│ │ ├─ On mismatch: publishes CommitmentConsistencyViolation
│ │ │ → AccusationManager initiates accusation quorum (see Part 5)
Expand All @@ -363,8 +373,8 @@ ShareVerificationActor receives ShareVerificationDispatched(kind=ShareProofs)
│ │ party_proofs, // consistency-passing parties' ZK proof data
│ │ })
│ │
│ ├─ Multithread ZK verify: inner circuits → `bb verify` with recursive VK;
│ │ `ShareComputation` (wrapper) → wrapper VK / `verify_wrapper_proof` path
│ ├─ Multithread ZK verify: `bb verify` on inner recursive circuits (same path as
│ │ `ZkProver::verify_proof`)
│ │ → Returns per-party pass/fail results
│ │
│ └─ On ComputeResponse:
Expand Down
8 changes: 4 additions & 4 deletions circuits/bin/dkg/share_encryption/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ fn main(
expected_message_commitment: pub Field,
pk0is: [Polynomial<N>; L],
pk1is: [Polynomial<N>; L],
ct0is: pub [Polynomial<N>; L],
ct1is: pub [Polynomial<N>; L],
ct0is: [Polynomial<N>; L],
ct1is: [Polynomial<N>; L],
u: Polynomial<N>,
e0: Polynomial<N>,
e0is: [Polynomial<N>; L],
Expand All @@ -30,7 +30,7 @@ fn main(
r2is: [Polynomial<N - 1>; L],
p1is: [Polynomial<(2 * N) - 1>; L],
p2is: [Polynomial<N - 1>; L],
) {
) -> pub Field {
let share_encryption: ShareEncryption<N, L, SHARE_ENCRYPTION_BIT_PK, SHARE_ENCRYPTION_BIT_CT, SHARE_ENCRYPTION_BIT_U, SHARE_ENCRYPTION_BIT_E0, SHARE_ENCRYPTION_BIT_E1, SHARE_ENCRYPTION_BIT_MSG, SHARE_ENCRYPTION_BIT_R1, SHARE_ENCRYPTION_BIT_R2, SHARE_ENCRYPTION_BIT_P1, SHARE_ENCRYPTION_BIT_P2> = ShareEncryption::new(
SHARE_ENCRYPTION_CONFIGS,
expected_pk_commitment,
Expand All @@ -50,5 +50,5 @@ fn main(
p1is,
p2is,
);
share_encryption.execute();
share_encryption.execute()
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "pk_generation"
name = "c2ab_fold"
type = "bin"
authors = ["Gnosis Guild / Enclave"]

[dependencies]
lib = { path = "../../../../../lib" }
lib = { path = "../../../lib" }
bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" }
55 changes: 55 additions & 0 deletions circuits/bin/recursive_aggregation/c2ab_fold/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: LGPL-3.0-only
//
// This file is provided WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

//! Combines C2a + C2b (two ZK Honk proofs) and surfaces their public inputs for `node_fold`.

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

/// C2 inner public IO: `expected_secret_commitment` + `[[Field; L_THRESHOLD]; N_PARTIES]`.
pub global C2_PUBLIC_LEN: u32 = 1 + (N_PARTIES * L_THRESHOLD);

/// Two key-hash params + combined key hash + two C2-sized public vectors (a and b).
pub global C2AB_FOLD_PUBLIC_LEN: u32 = 3 + (2 * C2_PUBLIC_LEN);

fn main(
c2a_vk: UltraHonkVerificationKey,
c2a_proof: UltraHonkZKProof,
c2a_public: [Field; C2_PUBLIC_LEN],
c2b_vk: UltraHonkVerificationKey,
c2b_proof: UltraHonkZKProof,
c2b_public: [Field; C2_PUBLIC_LEN],
c2a_key_hash: pub Field,
c2b_key_hash: pub Field,
) -> pub (Field, Field, [[Field; L_THRESHOLD]; N_PARTIES], Field, [[Field; L_THRESHOLD]; N_PARTIES]) {
verify_honk_proof(c2a_vk, c2a_proof, c2a_public, c2a_key_hash);
verify_honk_proof(c2b_vk, c2b_proof, c2b_public, c2b_key_hash);

let mut vk_hashes = Vec::new();
vk_hashes.push(c2a_key_hash);
vk_hashes.push(c2b_key_hash);
let key_hash = compute_vk_hash(vk_hashes);

let c2a_expected = c2a_public[0];
let mut c2a_shares: [[Field; L_THRESHOLD]; N_PARTIES] = [[0; L_THRESHOLD]; N_PARTIES];
for j in 0..N_PARTIES {
for l in 0..L_THRESHOLD {
c2a_shares[j][l] = c2a_public[1 + j * L_THRESHOLD + l];
}
}

let c2b_expected = c2b_public[0];
let mut c2b_shares: [[Field; L_THRESHOLD]; N_PARTIES] = [[0; L_THRESHOLD]; N_PARTIES];
for j in 0..N_PARTIES {
for l in 0..L_THRESHOLD {
c2b_shares[j][l] = c2b_public[1 + j * L_THRESHOLD + l];
}
}

(key_hash, c2a_expected, c2a_shares, c2b_expected, c2b_shares)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "fold"
name = "c3_fold"
type = "bin"
authors = ["Gnosis Guild / Enclave"]

Expand Down
85 changes: 85 additions & 0 deletions circuits/bin/recursive_aggregation/c3_fold/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: LGPL-3.0-only
//
// This file is provided WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

//! Sequential C3 fold: one inner ZK `ShareEncryption` proof per step, chained with the previous
//! accumulator (`c3_fold` non-ZK proof, or a fixed genesis proof from `c3_fold_kernel` on the first
//! step; see `crates/zk-prover` and `c3_fold_kernel`).

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

/// Total slots: one per (party, threshold-modulus) pair.
pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD;

/// Total public fields for a `c3_fold` proof: 4 pub parameters + 3*C3_SLOTS (pk, msg, ct).
pub global C3_FOLD_PUBLIC_LEN: u32 = 4 + (3 * C3_SLOTS);

pub global C3_FOLD_PREFIX_LEN: u32 = C3_FOLD_PUBLIC_LEN - (3 * C3_SLOTS);

fn main(
inner_vk: UltraHonkVerificationKey,
inner_proof: UltraHonkZKProof,
c3_public_inputs: [Field; 3],
acc_vk: UltraHonkVerificationKey,
acc_proof: UltraHonkProof,
acc_public_inputs: [Field; C3_FOLD_PUBLIC_LEN],
inner_key_hash: pub Field,
acc_key_hash: pub Field,
is_first_step: pub bool,
slot_index: pub u32,
) -> pub ([Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS]) {
assert(slot_index < C3_SLOTS);

verify_honk_proof(inner_vk, inner_proof, c3_public_inputs, inner_key_hash);

verify_honk_proof_non_zk(acc_vk, acc_proof, acc_public_inputs, acc_key_hash);

let pk_new = c3_public_inputs[0];
let msg_new = c3_public_inputs[1];
let ct_new = c3_public_inputs[2];

let mut out_pk: [Field; C3_SLOTS] = [0; C3_SLOTS];
let mut out_msg: [Field; C3_SLOTS] = [0; C3_SLOTS];
let mut out_ct: [Field; C3_SLOTS] = [0; C3_SLOTS];

for i in 0..C3_SLOTS {
let idx = i as u32;
if is_first_step {
if idx == slot_index {
out_pk[i] = pk_new;
out_msg[i] = msg_new;
out_ct[i] = ct_new;
} else {
out_pk[i] = 0;
out_msg[i] = 0;
out_ct[i] = 0;
}
} else {
let prior_pk = acc_public_inputs[C3_FOLD_PREFIX_LEN + i];
let prior_msg = acc_public_inputs[C3_FOLD_PREFIX_LEN + C3_SLOTS + i];
let prior_ct = acc_public_inputs[C3_FOLD_PREFIX_LEN + C3_SLOTS + C3_SLOTS + i];

if idx == slot_index {
assert(prior_pk == 0);
assert(prior_msg == 0);
assert(prior_ct == 0);
out_pk[i] = pk_new;
out_msg[i] = msg_new;
out_ct[i] = ct_new;
} else {
out_pk[i] = prior_pk;
out_msg[i] = prior_msg;
out_ct[i] = prior_ct;
}
}
}

(out_pk, out_msg, out_ct)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "pk_aggregation"
name = "c3_fold_kernel"
type = "bin"
authors = ["Gnosis Guild / Enclave"]

[dependencies]
lib = { path = "../../../../../lib" }
lib = { path = "../../../lib" }
bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" }
64 changes: 64 additions & 0 deletions circuits/bin/recursive_aggregation/c3_fold_kernel/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: LGPL-3.0-only
//
// This file is provided WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

//! Same IO as [`c3_fold`](../c3_fold/src/main.nr) but **only** verifies the inner `ShareEncryption`
//! proof. Used to mint a fixed genesis `UltraHonkProof` that bootstraps the real `c3_fold` circuit,
//! which always runs `verify_honk_proof_non_zk` on the accumulator (see `c3_fold`).

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

pub global C3_SLOTS: u32 = N_PARTIES * L_THRESHOLD;

pub global C3_FOLD_PUBLIC_LEN: u32 = 4 + (3 * C3_SLOTS);

fn main(
inner_vk: UltraHonkVerificationKey,
inner_proof: UltraHonkZKProof,
c3_public_inputs: [Field; 3],
acc_vk: UltraHonkVerificationKey,
acc_proof: UltraHonkProof,
acc_public_inputs: [Field; C3_FOLD_PUBLIC_LEN],
inner_key_hash: pub Field,
acc_key_hash: pub Field,
is_first_step: pub bool,
slot_index: pub u32,
) -> pub ([Field; C3_SLOTS], [Field; C3_SLOTS], [Field; C3_SLOTS]) {
assert(slot_index < C3_SLOTS);

verify_honk_proof(inner_vk, inner_proof, c3_public_inputs, inner_key_hash);

// Signature parity with `c3_fold`: kernel mints the genesis accumulator, so acc_* is unused.
let _ = (acc_vk, acc_proof, acc_key_hash, acc_public_inputs);

assert(is_first_step);

let pk_new = c3_public_inputs[0];
let msg_new = c3_public_inputs[1];
let ct_new = c3_public_inputs[2];

let mut out_pk: [Field; C3_SLOTS] = [0; C3_SLOTS];
let mut out_msg: [Field; C3_SLOTS] = [0; C3_SLOTS];
let mut out_ct: [Field; C3_SLOTS] = [0; C3_SLOTS];

for i in 0..C3_SLOTS {
let idx = i as u32;
if idx == slot_index {
out_pk[i] = pk_new;
out_msg[i] = msg_new;
out_ct[i] = ct_new;
} else {
out_pk[i] = 0;
out_msg[i] = 0;
out_ct[i] = 0;
}
}

(out_pk, out_msg, out_ct)
}
8 changes: 8 additions & 0 deletions circuits/bin/recursive_aggregation/c3ab_fold/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "c3ab_fold"
type = "bin"
authors = ["Gnosis Guild / Enclave"]

[dependencies]
lib = { path = "../../../lib" }
bb_proof_verification = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-nightly.20260102", directory = "barretenberg/noir/bb_proof_verification" }
Loading
Loading