Skip to content

feat: opt/c5 assert zero mod [skip-line-limit]#1579

Merged
0xjei merged 12 commits into
mainfrom
opt/c5-assert-zero-mod
Jun 5, 2026
Merged

feat: opt/c5 assert zero mod [skip-line-limit]#1579
0xjei merged 12 commits into
mainfrom
opt/c5-assert-zero-mod

Conversation

@zahrajavar

@zahrajavar zahrajavar commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

C5 pk_aggregation constraint reduction (~40–45% estimated)

Two optimizations to the threshold public key aggregation circuit (C5):

  1. assert_zero_mod in verify_pk_for_basis
    Replace the two ModU64::reduce_mod calls + equality assert per coefficient position with a single assert_zero_mod on the difference. A compile-time offset (H × q_l) keeps the delta non-negative in the field. Saves ~37 Barretenberg gates per position across L × N = 24,576 positions.

  2. Remove per-party pk1 witness; pin CRP directly
    pk1 = a (CRS) is a public constant — including it as a per-party committed witness only served as an indirect chain check back to C1. Instead:

compute_threshold_pk_commitment now hashes pk0 only (domain separator unchanged; input length is encoded in the SAFE io_pattern so old/new hashes can't collide).
C1 still verifies pk1 = CRS internally — the invariant is unchanged.
C5 passes the circuit-constant CRP directly to compute_pk_aggregation_commitment, so the user-facing output commitment still covers both pk0_agg and pk1 = a.
At H=10 this removes (H+1) × L × N = 270,336 field elements from the prover witness and eliminates the verify_pk1 loop.

Summary by CodeRabbit

  • New Features

    • Public key aggregation circuits now use optimized computation logic.
    • Added concurrent processing for DKG folding operations to improve performance.
    • Enhanced circuit artifact resolution to support multiple on-disk layout variants.
  • Chores

    • Updated required circuit version to 0.2.0.
    • Added rayon dependency for parallel processing support.

@vercel

vercel Bot commented Jun 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
crisp Ready Ready Preview, Comment Jun 5, 2026 2:45pm
enclave-docs Ready Ready Preview, Comment Jun 5, 2026 2:45pm
enclave-enclave-dashboard Ready Ready Preview, Comment Jun 5, 2026 2:45pm

Request Review

@zahrajavar zahrajavar requested a review from 0xjei June 4, 2026 21:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@circuits/lib/src/core/threshold/pk_aggregation.nr`:
- Around line 35-68: The Inputs struct and related functions still supply
pk1/pk1_agg and compute commitments using them; update Inputs, compute(), and
to_json() to remove pk1 and pk1_agg, accept and pass the circuit CRP (crp)
instead, and compute expected_threshold_pk_commitments using the same
routine/signature the Noir circuit expects (replace calls that used data.a or
data.public_key.c[1] with the CRP input and pk0/ pk0_agg); specifically, modify
Inputs to include crp and pk0/pk0_agg only, change compute() to call
compute_threshold_pk_commitment(&pk0[..], &crp[..], ...) or the exact updated
helper signature, and emit the updated fields in to_json() so the public input
matches expected_threshold_pk_commitments, pk0, pk0_agg, and crp.

In `@circuits/lib/src/math/commitments.nr`:
- Around line 172-177: The Rust implementation of
compute_threshold_pk_commitment currently flattens both pk0 and pk1, causing a
mismatch with the Noir circuit which computes the commitment from only pk0 with
DS_PK_GENERATION; update the Rust function
(crates/zk-helpers/src/circuits/commitments.rs::compute_threshold_pk_commitment)
and any callers (e.g.,
crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs) to build
the payload from only pk0 (drop pk1 flattening) and use DS_PK_GENERATION as the
domain separator so the computed hash matches the Noir circuit’s
compute_threshold_pk_commitment(pk0).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1284d0e3-7856-4def-9751-f7b80de5db10

📥 Commits

Reviewing files that changed from the base of the PR and between e680c67 and 92a1f82.

📒 Files selected for processing (4)
  • circuits/bin/threshold/pk_aggregation/src/main.nr
  • circuits/lib/src/core/threshold/pk_aggregation.nr
  • circuits/lib/src/core/threshold/pk_generation.nr
  • circuits/lib/src/math/commitments.nr

Comment thread circuits/lib/src/core/threshold/pk_aggregation.nr
Comment thread circuits/lib/src/math/commitments.nr
@0xjei 0xjei changed the title Opt/c5 assert zero mod feat: opt/c5 assert zero mod [skip-line-limit] Jun 5, 2026
@theinterfold theinterfold deleted a comment from coderabbitai Bot Jun 5, 2026
@0xjei 0xjei force-pushed the opt/c5-assert-zero-mod branch from 92a1f82 to 3d056cb Compare June 5, 2026 09:52
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

The PR removes per-party pk1/pk1_agg, introduces a deterministic circuit-constant crp, changes commitment APIs to use only pk0, updates PK aggregation verification and execution to use crp, and adjusts inputs, serialization, tests, codegen, and default configs.

Changes

PK1 elimination and CRS integration

Layer / File(s) Summary
Commitment function contract
circuits/lib/src/math/commitments.nr, crates/zk-helpers/src/circuits/commitments.rs, tests
compute_threshold_pk_commitment signature simplified to accept only pk0; public helper and tests now compute/hash commitments from pk0 only.
PkAggregation struct and verification refactoring
circuits/lib/src/core/threshold/pk_aggregation.nr
PkAggregation removes pk1/pk1_agg, adds crp: [Polynomial<N>; L] as a circuit constant, updates new(...) signature, rewrites verify_pk_for_basis to a single assert_zero_mod divisibility check with an offset, and simplifies execute to verify pk0 commitments and pk0_agg then compute final commitment with crp.
Inputs model, deterministic CRP, and sample/codegen updates
crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs, crates/zk-helpers/src/circuits/threshold/pk_aggregation/sample.rs, crates/zk-helpers/src/circuits/threshold/pk_aggregation/codegen.rs
Inputs removes pk1/pk1_agg, adds crp and pk0_agg; compute builds pk0_agg from data.public_key.c[0], constructs deterministic crp, normalizes polynomials, computes expected per-party commitments from pk0 only, and to_json/tests serialize and assert the updated fields.
Entrypoint and pk-generation integration
circuits/bin/threshold/pk_aggregation/src/main.nr, circuits/lib/src/core/threshold/pk_generation.nr
main removes pk1/pk1_agg and passes CRP into PkAggregation::new. PkGeneration::execute computes pk_commitment from pk0 only (affecting Fiat–Shamir transcript inputs).
Config preset and tests
circuits/lib/src/configs/default/mod.nr, crates/zk-prover/tests/local_e2e_tests.rs
Default config re-exports switched to secure-8192; tests updated to match new commitment helper signatures, remove deterministic CRP derivation, drop pk1_agg usage, and remove unused imports.
CI Noir circuits fetch and versioning
.github/workflows/ci.yml
Adds steps in three workflow jobs to fetch/extract Noir circuit artifacts from origin/circuit-artifacts and generate a version.json from crates/zk-prover/versions.json.

Sequence Diagram

sequenceDiagram
  participant PkGeneration
  participant Commitments
  participant Inputs
  participant PkAggregation
  PkGeneration->>Commitments: compute_threshold_pk_commitment(pk0)
  Inputs->>Commitments: compute_threshold_pk_commitment(pk0[i]) (per party)
  Inputs->>PkAggregation: supply expected_threshold_pk_commitments, pk0, pk0_agg, crp
  PkAggregation->>PkAggregation: verify pk0 commitments and per-basis aggregation (assert_zero_mod)
  PkAggregation->>Commitments: compute_pk_aggregation_commitment(pk0_agg, crp)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • gnosisguild/enclave#1486: Related refactor removing pk1 from pk generation and adjusting commitment derivation to rely on pk0/CRS.

Suggested reviewers

  • 0xjei
  • cedoor

Poem

🐰 I hop through circuits, tidy and spry,
pk1 tucked away beneath a calm sky,
crp hums steady while pk0 leads the song,
commitments now lean, the flow swift and strong.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and generic, using non-descriptive technical jargon ('opt/c5 assert zero mod') that does not clearly convey the main changes to a teammate scanning the history. Consider a clearer title such as 'Optimize threshold PK aggregation: replace modular reduction with assert_zero_mod and remove pk1 witness' that better summarizes the primary objectives.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch opt/c5-assert-zero-mod

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/zk-helpers/src/circuits/commitments.rs (1)

223-231: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the helper docs to match the new pk0-only contract.

Line 250 now computes the commitment from pk0 only, but the doc block still says this hashes pk0 together with CRP (pk1). That makes the public helper contract misleading for downstream audit callers.

Diff to align the docs
-/// Deserializes the keyshare, extracts the pk0 polynomial, and hashes it
-/// together with the CRP (pk1) to produce the commitment. Returns 32
+/// Deserializes the keyshare, extracts the pk0 polynomial, and hashes only
+/// pk0 to produce the commitment. Returns 32
 /// big-endian bytes, ready to compare against
 /// `Proof::extract_output("pk_commitment")` from a C1 proof.
 ///
-/// The caller supplies pre-built `params` and `crp` so that batch calls
-/// (multiple keyshares with the same parameters) don't rebuild them each time.
+/// The caller supplies pre-built `params` and `crp`; `crp` is only needed to
+/// deserialize `PublicKeyShare`, not as commitment input.

Also applies to: 250-250

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/zk-helpers/src/circuits/commitments.rs` around lines 223 - 231, The
doc comment for the pk commitment helper is out of date: it still claims the
commitment hashes "pk0 together with the CRP (pk1)" but the code at the
pk_commitment computation now uses only pk0. Update the doc block in
crates/zk-helpers/src/circuits/commitments.rs (the comment above the pk
commitment helper that references pk0, CRP (pk1), and
Proof::extract_output("pk_commitment")) to reflect the new pk0-only contract,
clearly stating the function computes the commitment from pk0 alone and that
callers supply params and crp for batching but the CRP is not mixed into the
hash.
🧹 Nitpick comments (1)
crates/zk-helpers/src/circuits/commitments.rs (1)

699-705: ⚡ Quick win

Make the roundtrip expectation independent of compute_threshold_pk_commitment.

Line 704 reuses the same helper that the production path now depends on, so a regression in the new pk0-only hashing contract could still pass this test. Build expected from the flattened pk0 payload and DS_PK_GENERATION directly, like the other manual-commitment tests in this file.

Diff to harden the test
-        let expected = compute_threshold_pk_commitment(&pk0, bit_pk);
+        let mut payload = Vec::new();
+        payload = flatten(payload, &pk0.limbs, bit_pk);
+        let io_pattern = [0x80000000 | payload.len() as u32, 1];
+        let expected =
+            field_to_bigint(compute_commitments(payload, DS_PK_GENERATION, io_pattern)[0]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/zk-helpers/src/circuits/commitments.rs` around lines 699 - 705, The
test currently builds expected via compute_threshold_pk_commitment which mirrors
production logic and can mask regressions; instead construct expected by
flattening pk0's payload bytes and hashing them with DS_PK_GENERATION directly
(the same pattern used in other manual-commitment tests). Locate the pk0
variable (CrtPolynomial from pk_share.p0_share()), call its
flatten/serialization to big-endian bytes (the same output used by to_bytes_be),
prepend or combine the DS_PK_GENERATION domain/tag, run the hash/commitment
routine used in other tests to produce expected and then call to_bytes_be on
that result for be_bytes; replace the compute_threshold_pk_commitment call with
this explicit flatten+DS_PK_GENERATION construction.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs`:
- Around line 160-166: The computation currently derives crp from data.a which
can drift from the circuit's hard-coded CRP; update the code in computation.rs
so crp is obtained from the preset constant (e.g. use preset.crp or
lib::configs::default::threshold::CRP) instead of data.a when building the
commitment for pk0_agg, or alternatively add a fast-fail check that asserts
data.a equals the preset CRP and returns an error if not; adjust the block
around pk0_agg/Cr tPolynomial::from_fhe_polynomial, crp.reverse(), and
crp.center(threshold_params.moduli()) to use the preset-derived crp (or perform
the equality check) so commit(pk0_agg, crp) matches the proof generator.

---

Outside diff comments:
In `@crates/zk-helpers/src/circuits/commitments.rs`:
- Around line 223-231: The doc comment for the pk commitment helper is out of
date: it still claims the commitment hashes "pk0 together with the CRP (pk1)"
but the code at the pk_commitment computation now uses only pk0. Update the doc
block in crates/zk-helpers/src/circuits/commitments.rs (the comment above the pk
commitment helper that references pk0, CRP (pk1), and
Proof::extract_output("pk_commitment")) to reflect the new pk0-only contract,
clearly stating the function computes the commitment from pk0 alone and that
callers supply params and crp for batching but the CRP is not mixed into the
hash.

---

Nitpick comments:
In `@crates/zk-helpers/src/circuits/commitments.rs`:
- Around line 699-705: The test currently builds expected via
compute_threshold_pk_commitment which mirrors production logic and can mask
regressions; instead construct expected by flattening pk0's payload bytes and
hashing them with DS_PK_GENERATION directly (the same pattern used in other
manual-commitment tests). Locate the pk0 variable (CrtPolynomial from
pk_share.p0_share()), call its flatten/serialization to big-endian bytes (the
same output used by to_bytes_be), prepend or combine the DS_PK_GENERATION
domain/tag, run the hash/commitment routine used in other tests to produce
expected and then call to_bytes_be on that result for be_bytes; replace the
compute_threshold_pk_commitment call with this explicit flatten+DS_PK_GENERATION
construction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5e0fe746-1979-4f8e-98bc-671cd42a82ba

📥 Commits

Reviewing files that changed from the base of the PR and between 3d056cb and 279878a.

📒 Files selected for processing (9)
  • circuits/bin/threshold/pk_aggregation/src/main.nr
  • circuits/lib/src/configs/default/mod.nr
  • circuits/lib/src/core/threshold/pk_aggregation.nr
  • circuits/lib/src/core/threshold/pk_generation.nr
  • circuits/lib/src/math/commitments.nr
  • crates/zk-helpers/src/circuits/commitments.rs
  • crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs
  • crates/zk-helpers/src/circuits/threshold/pk_aggregation/sample.rs
  • crates/zk-prover/tests/local_e2e_tests.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • circuits/lib/src/core/threshold/pk_generation.nr
  • circuits/lib/src/math/commitments.nr
  • circuits/bin/threshold/pk_aggregation/src/main.nr
  • circuits/lib/src/core/threshold/pk_aggregation.nr

Comment thread crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

542-554: ⚡ Quick win

Consider validating jq output and extracting to a reusable action.

The script reads version values using jq but doesn't validate the extraction succeeded. If a key is missing, jq outputs "null" as a string, leading to an invalid version.json that fails later during setup checks rather than immediately.

Additionally, this exact pattern is duplicated in three jobs (lines 542-554, 815-827, 1259-1271). Consider extracting it to a composite action for maintainability.

✨ Suggested improvements

Add validation after jq extraction:

 REQUIRED_BB="$(jq -r '.required_bb_version' "$VERSIONS_JSON")"
 REQUIRED_CIRCUITS="$(jq -r '.required_circuits_version' "$VERSIONS_JSON")"
+if [ "$REQUIRED_BB" = "null" ] || [ -z "$REQUIRED_BB" ] || [ "$REQUIRED_CIRCUITS" = "null" ] || [ -z "$REQUIRED_CIRCUITS" ]; then
+  echo "Error: Failed to extract required versions from $VERSIONS_JSON"
+  exit 1
+fi

Consider creating .github/actions/setup-noir-circuits/action.yml:

name: 'Setup Noir Circuits'
description: 'Fetches circuit artifacts and writes version.json'
inputs:
  noir-dir:
    description: 'Path to Noir directory'
    required: true
runs:
  using: "composite"
  steps:
    - shell: bash
      run: |
        VERSIONS_JSON="crates/zk-prover/versions.json"
        REQUIRED_BB="$(jq -r '.required_bb_version' "$VERSIONS_JSON")"
        REQUIRED_CIRCUITS="$(jq -r '.required_circuits_version' "$VERSIONS_JSON")"
        # ... rest of script using ${{ inputs.noir-dir }}

Then replace duplicated blocks with:

- name: 'Setup circuit artifacts'
  uses: ./.github/actions/setup-noir-circuits
  with:
    noir-dir: tests/integration/.enclave/noir  # or $HOME/.enclave/noir
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 542 - 554, The jq extraction into
REQUIRED_BB and REQUIRED_CIRCUITS (using VERSIONS_JSON) can return "null"
strings and currently isn't validated, and the same block manipulating
INTEGRATION_NOIR/version.json is duplicated across jobs; update each block to
check that jq produced non-empty/non-"null" values (fail early with a clear
error if validation fails) before writing version.json and fetching artifacts,
and factor the logic into a reusable composite action (e.g.,
.github/actions/setup-noir-circuits) that accepts a noir-dir input and performs
the VERSIONS_JSON read, validation of REQUIRED_BB/REQUIRED_CIRCUITS, directory
setup, git fetch/archive, and writing of version.json so the three duplicated
blocks can be replaced with a single action use.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 542-554: The jq extraction into REQUIRED_BB and REQUIRED_CIRCUITS
(using VERSIONS_JSON) can return "null" strings and currently isn't validated,
and the same block manipulating INTEGRATION_NOIR/version.json is duplicated
across jobs; update each block to check that jq produced non-empty/non-"null"
values (fail early with a clear error if validation fails) before writing
version.json and fetching artifacts, and factor the logic into a reusable
composite action (e.g., .github/actions/setup-noir-circuits) that accepts a
noir-dir input and performs the VERSIONS_JSON read, validation of
REQUIRED_BB/REQUIRED_CIRCUITS, directory setup, git fetch/archive, and writing
of version.json so the three duplicated blocks can be replaced with a single
action use.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2cc440b0-7f96-466c-86ce-5a54906e493d

📥 Commits

Reviewing files that changed from the base of the PR and between 098ca97 and 65aa8e0.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/build-circuits.ts (1)

528-539: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Store the concrete committee in .active-preset.json for per-committee builds.

With --committee all, the active stamp currently writes this.options.committee ("all") instead of the concrete loop committee. That makes isBinReadyForPreset() fail the committee match and prevents --skip-if-built/hydrate fast-path reuse for specific committees.

Suggested fix
-  private writeActiveBinPresetStamp(preset: string, sourceHash: string): void {
+  private writeActiveBinPresetStamp(preset: string, committee: CircuitCommittee, sourceHash: string): void {
     const stamp: PresetBuildStamp = {
       preset,
-      committee: this.options.committee,
+      committee,
       sourceHash,
       builtAt: new Date().toISOString(),
     }
     writeFileSync(join(this.circuitsDir, '.active-preset.json'), JSON.stringify(stamp, null, 2) + '\n')
   }

-  private hydrateBinFromDist(preset: string, committee: string, sourceHash: string): void {
+  private hydrateBinFromDist(preset: string, committee: CircuitCommittee, sourceHash: string): void {
@@
-    this.writeActiveBinPresetStamp(preset, sourceHash)
+    this.writeActiveBinPresetStamp(preset, committee, sourceHash)
   }
@@
-        this.writeActiveBinPresetStamp(preset, sourceHash)
+        this.writeActiveBinPresetStamp(preset, committee, sourceHash)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/build-circuits.ts` around lines 528 - 539, The active preset stamp
currently records this.options.committee (e.g. "all") instead of the concrete
loop variable committee, breaking isBinReadyForPreset() matches; update the call
and implementation to store the concrete committee: change the invocation
this.writeActiveBinPresetStamp(preset, sourceHash) to pass the loop committee
(e.g. this.writeActiveBinPresetStamp(preset, committee, sourceHash)) and adjust
the writeActiveBinPresetStamp function to accept a committee parameter and
persist that committee into .active-preset.json (instead of using
this.options.committee), so per-committee builds can be recognized for
--skip-if-built/hydrate reuse.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@scripts/build-circuits.ts`:
- Around line 528-539: The active preset stamp currently records
this.options.committee (e.g. "all") instead of the concrete loop variable
committee, breaking isBinReadyForPreset() matches; update the call and
implementation to store the concrete committee: change the invocation
this.writeActiveBinPresetStamp(preset, sourceHash) to pass the loop committee
(e.g. this.writeActiveBinPresetStamp(preset, committee, sourceHash)) and adjust
the writeActiveBinPresetStamp function to accept a committee parameter and
persist that committee into .active-preset.json (instead of using
this.options.committee), so per-committee builds can be recognized for
--skip-if-built/hydrate reuse.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 210591a6-0542-4ed8-b3cb-5a16063dcc6d

📥 Commits

Reviewing files that changed from the base of the PR and between 098ca97 and 762d818.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • crates/zk-prover/Cargo.toml
  • crates/zk-prover/src/backend/download.rs
  • crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs
  • crates/zk-prover/versions.json
  • scripts/build-circuits.ts

zahrajavar and others added 12 commits June 5, 2026 16:44
…C5 verify_pk_for_basis

verify_pk_for_basis previously reduced both sides of the congruence check
independently and compared them (2x ModU64::reduce_mod + assert == 0).
It is equivalent and cheaper to check the difference is divisible by q_l
using a single ModU64::assert_zero_mod call.

A compile-time offset (H * q_l) is added before the subtraction to
guarantee the delta is a small non-negative integer in the Field (not a
field-wrapped negative), which is required by __reduce_witness_u64.
Bound: delta + H*q_l in [(H-1)*q_l/2, (H+2)*q_l/2); max quotient <= H+2,
safely fits in u64 for realistic committee sizes.

ACIR opcode count (insecure-512, H=3): 43,830 -> 7,990 (-82%).
Expected ~24% reduction in Barretenberg constraint count for secure
config (N=8192, L=3) where 2x L*N = 49,152 reduce_mod calls are replaced
by L*N = 24,576 assert_zero_mod calls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pk1 = CRS a is a public constant shared by all parties. Including it as
a per-party committed witness in C5 served only as a chain check back to
C1's commitment, but that check is now unnecessary because:

1. C1 still verifies pk1 = CRS internally (circuit constraint unchanged).
2. C5 now passes the circuit-constant CRP directly as pk1_agg, giving
   the same binding guarantee without any per-party witness.

Changes:
- compute_threshold_pk_commitment: remove pk1 parameter; hash pk0 only.
  Domain separator DS_PK_GENERATION is unchanged; the io_pattern encodes
  input length so old (pk0+pk1) and new (pk0-only) hashes cannot collide.
- C1 (pk_generation): update the one call site accordingly.
- C5 lib (pk_aggregation): remove pk1/pk1_agg fields; add crp field;
  remove verify_pk1; pass crp to compute_pk_aggregation_commitment so
  the user-facing final commitment still covers both pk0_agg and pk1=CRP.
- C5 bin (main.nr): remove pk1/pk1_agg circuit inputs; import and pass CRP.

ACIR opcodes (insecure-512, H=3): 7,990 -> 4,002 (-50%).
Combined with the assert_zero_mod commit: 43,830 -> 4,002 (-91% total).
Witness reduction at H=10: removes (H+1)*L*N = 11*3*8192 = 270,336
field elements from the prover witness.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@0xjei 0xjei force-pushed the opt/c5-assert-zero-mod branch from 1f8bf14 to 2736b72 Compare June 5, 2026 14:44
@0xjei 0xjei enabled auto-merge (squash) June 5, 2026 14:44
@0xjei 0xjei merged commit 0997166 into main Jun 5, 2026
32 of 33 checks passed
@ctrlc03 ctrlc03 deleted the opt/c5-assert-zero-mod branch June 5, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants