Skip to content

feat: add chunked C2 proving [skip-line-limit] - #1772

Draft
0xjei wants to merge 24 commits into
lbfvfrom
chunk-circuits
Draft

feat: add chunked C2 proving [skip-line-limit]#1772
0xjei wants to merge 24 commits into
lbfvfrom
chunk-circuits

Conversation

@0xjei

@0xjei 0xjei commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

EXPERIMENTAL WIP DO NOT MERGE!

@vercel

vercel Bot commented Aug 6, 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 Aug 8, 2026 9:33am
interfold-dashboard Error Error Aug 8, 2026 9:33am
interfold-docs Ready Ready Preview Aug 8, 2026 9:33am

Request Review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 79b7ccbe-2c97-4a0b-afd9-4d6bdba8ef8c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds chunked C2 proving with recursive aggregation across DKG circuits and the Rust proving pipeline, and reworks committee publication into a proof-backed commitment plus a separate repeatable public-key candidate. It also introduces request-time sortition snapshots, bonding-asset/pricing configuration overhaul, proposal-scoped slashing, a ComputeDomain-bound compute proof journal, and matching contract, script, and documentation updates.

Changes

Chunked C2 Proving

Layer / File(s) Summary
Chunk configuration and commitments
circuits/lib/src/configs/..., circuits/lib/src/math/commitments.nr, crates/zk-helpers/...
New chunk-size/count globals, root-commitment helpers, CLI --chunk-size flag, and generated Noir config functions support chunked DKG share proving.
DKG base and chunk circuits
circuits/bin/dkg/..., circuits/lib/src/core/dkg/...
New Noir binaries validate secret and chunk commitments and verify per-chunk share relations for secret-key and smudging-noise inputs.
Recursive C2 fold/finalize circuits
circuits/bin/recursive_aggregation/c2_chunk_*, c2ab_chunk_fold
New Noir circuits verify base/chunk proofs, track coverage, fold results, and finalize accumulators into legacy C2 output layouts.
Rust chunked proving and events
crates/zk-prover/..., crates/multithread/..., crates/events/..., crates/wasm/...
Rust APIs generate chunked witnesses/proofs, batch and fold them, select compatible C2 circuits at runtime, and add ciphertext-commitment helpers.
End-to-end tests and benchmarks
crates/zk-prover/tests/..., circuits/benchmarks/...
Integration tests validate chunked flows for insecure and secure presets. Benchmark metadata and artifacts are regenerated.

Estimated code review effort: 5 (Critical) | ~150 minutes

Committee, Sortition, and Bonding Protocol

Layer / File(s) Summary
Committee/E3 events and EVM effects
crates/events/..., crates/evm/..., crates/indexer/...
CommitteeRequested/E3Requested gain ticket-price/request-block fields. Committee publication splits into commitment submission and separate public-key publication.
Sortition snapshot and node registry
crates/sortition/...
The sortition actor and node registry record request-time eligibility, ticket balances, and price snapshots for consistent historical node selection.
Solidity contracts
packages/interfold-contracts/contracts/...
Interfold, CiphernodeRegistryOwnable, BondingRegistry, SlashingManager, and E3RefundManager implement proof-backed committee publication, atomic bonding-asset configuration, and proposal-scoped slashing.
Deployment scripts and build tooling
packages/interfold-contracts/scripts/..., ignition/..., scripts/build-circuits.ts
Ignition modules and deployment scripts wire the new bonding, committee, and verifier configuration into deployments and generate ActiveCryptoConfig.sol.
Contract test suites
packages/interfold-contracts/test/...
Hardhat tests cover committee publication, sortition eligibility, pricing, bonding, and slashing changes.

Estimated code review effort: 5 (Critical) | ~180 minutes

Compute Domain and Support Services

Layer / File(s) Summary
ComputeDomain types and host/guest wiring
crates/support/..., crates/compute-provider/...
Support types add ComputeDomain/ComputeJournal. Host, guest, and app code bind and verify domain-scoped compute journals. The merkle tree builder enforces a minimum depth of 1.

Estimated code review effort: 4 (Complex) | ~60 minutes

CRISP Examples and Default Templates

Layer / File(s) Summary
CRISP contracts and server
examples/CRISP/...
CRISPProgram and MockInterfold adopt Risc0ComputeProof verification. The CRISP server sends chain, contract, encryption-scheme, and committee-key data with compute requests.
Default template
templates/default/...
The template's MyProgram contract, client/server input publication, and runner code adopt ciphertext commitments and compute-domain metadata.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Protocol Documentation

Layer / File(s) Summary
Flow-trace and invariants updates
agent/flow-trace/*, agent/INVARIANTS.md, agent/CHUNKED_C2_PLAN.md
Documentation describes reward-recipient freezing, committee publication, bonding-asset rotation, sortition snapshots, chunked C2/DKG binding, and slashing/refund behavior.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Node as Ciphernode
    participant Registry as CiphernodeRegistryOwnable
    participant BFVProver as BFV Pk Verifier
    participant Interfold

    Node->>Registry: publishCommittee(e3Id, pkCommitment, proof, attestationBundle)
    Registry->>BFVProver: verify(proof, pkCommitment)
    BFVProver-->>Registry: verification result
    Registry->>Registry: store commitment, mark proof published
    Registry->>Interfold: notify committee proof published
    Registry-->>Node: CommitteeProofPublished event

    Node->>Registry: publishCommitteePublicKey(e3Id, publicKey)
    Registry->>Registry: validate length, match against pkCommitment
    Registry-->>Node: CommitteePublished event (compatibility, empty proof)
Loading
sequenceDiagram
    participant Prover as ZkProver
    participant ChunkCircuit as Chunk Circuit
    participant BatchCircuit as Chunk Batch Circuit
    participant FoldCircuit as Chunk Fold/Finalize Circuit

    Prover->>Prover: split secret into N chunks
    loop each chunk
        Prover->>ChunkCircuit: generate witness and proof
    end
    Prover->>BatchCircuit: verify batch of chunk proofs
    BatchCircuit-->>Prover: aggregate commitment
    Prover->>FoldCircuit: fold base + chunk + accumulator proofs
    FoldCircuit-->>Prover: updated accumulator state
    Prover->>FoldCircuit: finalize accumulator into legacy C2 layout
    FoldCircuit-->>Prover: terminal C2 proof
Loading

Possibly related PRs

  • theinterfold/interfold#1400: The main PR directly extends the split C2 base/chunk proving and wrapper architecture introduced in PR #1400, reusing and expanding its circuits, commitments, recursive aggregation, and proving flow.
  • theinterfold/interfold#1365: The main PR directly extends the recursive aggregation approach from PR #1365 by adding chunked C2 batch/fold/finalization circuits and corresponding prover workflows.
  • theinterfold/interfold#1161: The main PR's committee publication changes directly overlap with the retrieved PR's CiphernodeRegistryOwnable committee lifecycle and deadline/finalization modifications.

Suggested labels: contracts, documentation

Suggested reviewers: hmzakhalid

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 88.02% 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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the pull request’s primary change: adding chunked C2 proving.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chunk-circuits

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

@vercel
vercel Bot temporarily deployed to Preview – crisp August 6, 2026 10:32 Inactive
@vercel
vercel Bot temporarily deployed to Preview – interfold-docs August 6, 2026 10:32 Inactive
@0xjei 0xjei changed the title feat: add chunked C2 proving feat: add chunked C2 proving [skip-line-limit] Aug 6, 2026

@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: 7

🧹 Nitpick comments (9)
crates/zk-prover/src/lib.rs (1)

30-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider keeping the fold steps internal.

generate_sequential_c2_chunk_fold and finalize_c2_chunk_fold are the two halves of the flow that prove_chunked_share_computation already orchestrates. Exporting them lets callers combine a fold and a finalizer that do not match, and the validation for that combination lives only inside each function. If no external caller needs the steps, restrict them to pub(crate).

🤖 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-prover/src/lib.rs` around lines 30 - 32, Restrict the re-exports of
generate_sequential_c2_chunk_fold and finalize_c2_chunk_fold in the crate root
to pub(crate), keeping both fold steps internal while preserving their use by
prove_chunked_share_computation.
crates/zk-prover/src/circuits/aggregation/c2_chunk_accumulator.rs (3)

424-443: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the redundant length check and the identity match.

Line 424 checks len() <= 2 * chunk_count. The checked_sub on lines 429-432 already covers that case with a stricter bound, and both produce the same error text. The match on lines 439-443 maps each variant to itself; accumulator.circuit is already validated on lines 409-416.

♻️ Proposed simplification
-    let acc_public_inputs = bytes_to_field_strings(accumulator.public_signals.as_ref())?;
-    if acc_public_inputs.len() <= 2 * chunk_count {
-        return Err(ZkError::InvalidInput(
-            "C2 chunk accumulator layout is too short".into(),
-        ));
-    }
-    let c2_public_len = acc_public_inputs
+    let acc_public_inputs = bytes_to_field_strings(accumulator.public_signals.as_ref())?;
+    let c2_public_len = acc_public_inputs
         .len()
         .checked_sub(C2_CHUNK_FOLD_PUBLIC_PREFIX_LEN + (2 * chunk_count))
         .ok_or_else(|| ZkError::InvalidInput("C2 chunk accumulator layout is too short".into()))?;
@@
-    let acc_vk = match accumulator.circuit {
-        CircuitName::C2ChunkFold => CircuitName::C2ChunkFold,
-        CircuitName::C2ChunkFoldKernel => CircuitName::C2ChunkFoldKernel,
-        _ => unreachable!("circuit checked above"),
-    };
     let acc_vk = vk::load_vk_artifacts(
         &prover.circuits_dir(CircuitVariant::Default, artifacts_dir),
-        acc_vk,
+        accumulator.circuit,
     )?;
🤖 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-prover/src/circuits/aggregation/c2_chunk_accumulator.rs` around
lines 424 - 443, Remove the redundant acc_public_inputs.len() <= 2 * chunk_count
validation and rely on the existing checked_sub error path for layout
validation. Simplify the acc_vk assignment by using the already validated
accumulator.circuit directly, eliminating the identity match while preserving
the surrounding circuit validation.

18-18: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Bind the prefix length to the circuit definition.

C2_CHUNK_FOLD_PUBLIC_PREFIX_LEN duplicates the Noir global C2_CHUNK_FOLD_PREFIX_LEN in the terminal and fold circuits. If the circuit prefix changes, this constant drifts silently, and every length check in this file validates the wrong layout. Add a comment that names the Noir source of truth, or derive the value from a shared generated constant.

♻️ Minimum change: document the source of truth
+/// Must equal `C2_CHUNK_FOLD_PREFIX_LEN` in
+/// `circuits/bin/recursive_aggregation/c2_chunk_fold/src/main.nr`.
 const C2_CHUNK_FOLD_PUBLIC_PREFIX_LEN: usize = 6;
🤖 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-prover/src/circuits/aggregation/c2_chunk_accumulator.rs` at line
18, Document that C2_CHUNK_FOLD_PUBLIC_PREFIX_LEN must match the Noir global
C2_CHUNK_FOLD_PREFIX_LEN defined in the terminal and fold circuits, identifying
that global as the source of truth.

253-285: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Use the kernel proof as the initial accumulator.

C2ChunkFold ignores the kernel state when is_first_step is true, so the first chunk is not folded twice. However, the current code verifies the base and first chunk in c2_chunk_fold_kernel, then verifies them again in C2ChunkFold and discards the kernel state.

Generate the kernel proof once, use it as prior, and run C2ChunkFold only for the remaining chunks. The finalizer already accepts C2ChunkFoldKernel.

🤖 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-prover/src/circuits/aggregation/c2_chunk_accumulator.rs` around
lines 253 - 285, Update the first-step handling around is_first_step and
generate_kernel_genesis_proof so the kernel proof becomes the initial
accumulator/prior state instead of being discarded. Ensure C2ChunkFold is
invoked only for subsequent chunks, while preserving the existing public-input
validation and finalizer flow that accepts C2ChunkFoldKernel.
scripts/build-circuits.ts (1)

714-719: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The exclusion also forces an unused EVM verification key.

generateVk uses isFoldOrAggregation for two separate decisions. The else branch produces the noir-recursive VK that the finalize circuits need, but it also runs runWriteVk('evm', ...) and throws when that fails. The finalize circuits are never published on-chain, because isAggregationWithEvmOnChain returns false for them. The build therefore spends time on an EVM VK that nothing consumes, and an EVM VK failure blocks the whole build.

Split the two decisions. Add a predicate such as needsNoirRecursiveVk(circuit) and gate the EVM VK on isAggregationWithEvmOnChain for aggregation-group circuits.

The method name is also now inaccurate. It returns false for circuits that are in the aggregation group. Rename it to describe the VK profile it selects, for example usesNonZkAccumulatorVkProfile.

♻️ Sketch of the split
-  private isFoldOrAggregation(circuit: CircuitInfo): boolean {
-    // C2 terminal projections are ZK leaves for C2abFold, not non-ZK accumulators.
-    const name = basename(circuit.path)
-    return circuit.group === CIRCUIT_GROUPS.AGGREGATION &&
-      !['c2_chunk_finalize', 'sk_c2_chunk_finalize', 'esm_c2_chunk_finalize'].includes(name)
-  }
+  /** C2 terminal projections are ZK leaves for C2abFold, not non-ZK accumulators. */
+  private static readonly ZK_LEAF_AGGREGATION_CIRCUITS = new Set([
+    'c2_chunk_finalize',
+    'sk_c2_chunk_finalize',
+    'esm_c2_chunk_finalize',
+  ])
+
+  /** True when the circuit only needs the non-ZK accumulator VK profile. */
+  private usesNonZkAccumulatorVkProfile(circuit: CircuitInfo): boolean {
+    return (
+      circuit.group === CIRCUIT_GROUPS.AGGREGATION &&
+      !NoirCircuitBuilder.ZK_LEAF_AGGREGATION_CIRCUITS.has(basename(circuit.path))
+    )
+  }
+
+  /** True when an EVM VK must be produced for this circuit. */
+  private needsEvmVk(circuit: CircuitInfo): boolean {
+    return circuit.group !== CIRCUIT_GROUPS.AGGREGATION || this.isAggregationWithEvmOnChain(circuit)
+  }
🤖 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 714 - 719, Split the VK decisions in
generateVk: add a needsNoirRecursiveVk(circuit) predicate for selecting the
noir-recursive profile, and gate runWriteVk('evm', ...) for aggregation circuits
with isAggregationWithEvmOnChain so finalize circuits do not generate or require
an unused EVM VK. Rename isFoldOrAggregation to a profile-oriented name such as
usesNonZkAccumulatorVkProfile and update all callers.
crates/zk-prover/src/circuits/dkg/share_computation.rs (2)

105-127: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Load the chunk circuit once, outside the loop.

circuit_path and CompiledCircuit::from_file run on every iteration. The compiled ACIR JSON is re-read and re-parsed once per chunk. For a degree of 32768 with the default chunk size of 512, that is 64 loads of the same file.

Hoist both out of the loop.

♻️ Proposed refactor
+    let chunk_circuit_path = prover
+        .circuits_dir(e3_events::CircuitVariant::Recursive, artifacts_dir)
+        .join(CircuitName::ShareComputationChunk.dir_path())
+        .join(format!(
+            "{}.json",
+            CircuitName::ShareComputationChunk.as_str()
+        ));
+    let compiled = crate::witness::CompiledCircuit::from_file(&chunk_circuit_path)?;
     let mut chunks = Vec::with_capacity(chunk_count);
     let mut indices = Vec::with_capacity(chunk_count);
     for chunk_idx in 0..chunk_count {
@@
         let input_map = inputs_json_to_input_map(&Value::Object(chunk_json))?;
-        let circuit_path = prover
-            .circuits_dir(e3_events::CircuitVariant::Recursive, artifacts_dir)
-            .join(CircuitName::ShareComputationChunk.dir_path())
-            .join(format!(
-                "{}.json",
-                CircuitName::ShareComputationChunk.as_str()
-            ));
-        let compiled = crate::witness::CompiledCircuit::from_file(&circuit_path)?;
         let witness =
             crate::witness::WitnessGenerator::new().generate_witness(&compiled, input_map)?;
🤖 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-prover/src/circuits/dkg/share_computation.rs` around lines 105 -
127, Hoist the `circuit_path` construction and `CompiledCircuit::from_file` call
out of the chunk-processing loop so the shared `compiled` circuit is loaded once
before iterating over `chunk_idx`. Keep each iteration’s input-map creation and
witness generation in the loop, reusing the preloaded circuit.

92-101: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Validate y before the base proof is generated.

The check on the coefficient count runs after prove_recursive_circuit produced the base proof. A malformed or missing y therefore costs one full recursive proof before the error is returned. Move the y extraction and the length check above the base proving step.

🤖 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-prover/src/circuits/dkg/share_computation.rs` around lines 92 -
101, Move the y extraction and degree-length validation in the share computation
flow to before the prove_recursive_circuit base-proof generation call. Preserve
the existing missing-y serialization error and coefficient-count InvalidInput
error, and only invoke prove_recursive_circuit after y has passed both checks.
crates/events/src/interfold_event/signed_proof.rs (1)

60-67: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Cover both chunk-finalizer circuits in the test helper.

Production consumers use membership checks against proof.circuit. However, workflow_tests/mod.rs:33 uses [0] for C2a and C2b proofs, so the tests never construct SkC2ChunkFinalize or ESmC2ChunkFinalize. Select circuits explicitly or add cases for both entries.

🤖 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/events/src/interfold_event/signed_proof.rs` around lines 60 - 67,
Update the test helper that selects circuits for C2a and C2b proofs to cover
both entries in each corresponding circuit list, including SkC2ChunkFinalize and
ESmC2ChunkFinalize. Avoid relying only on the first element; select each circuit
explicitly or add separate test cases while preserving existing proof
construction behavior.
crates/events/src/interfold_event/proof.rs (1)

161-162: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused C2ChunkFinalize variant and circuit package.

Only SkC2ChunkFinalize and ESmC2ChunkFinalize are used by the chunked C2 proving path. Remove the untyped variant, its mappings, and circuits/bin/recursive_aggregation/c2_chunk_finalize to avoid compiling and generating verification keys for an unused circuit.

🤖 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/events/src/interfold_event/proof.rs` around lines 161 - 162, Remove
the unused C2ChunkFinalize variant from the relevant proof enum, then delete its
associated mappings and references while preserving SkC2ChunkFinalize and
ESmC2ChunkFinalize. Remove the
circuits/bin/recursive_aggregation/c2_chunk_finalize circuit package and any
build or generation entries that compile it.
🤖 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 `@agent/flow-trace/04_DKG_AND_COMPUTATION.md`:
- Around line 319-327: Update the chunked C2 documentation near the statement
that each chunk has 512 coefficients to clarify that 512 is the default chunk
size, while noting that --chunk-size allows any nonzero divisor of the preset
polynomial degree.

In `@circuits/bin/recursive_aggregation/c2_chunk_finalize/src/main.nr`:
- Around line 20-26: Bind every nested proof to its expected circuit identity
and variant. In c2_chunk_finalize/src/main.nr (lines 20-26), assert acc_key_hash
matches the computed C2ChunkFold accumulator circuit hash before projecting
state; in c2_chunk_fold/src/main.nr (lines 39-63), bind base_key_hash and
chunk_key_hash to C2Base and C2Chunk hashes, bind non-initial acc_key_hash to
C2ChunkFold, and propagate SK/ESM variant selection through accumulator and
finalizer; in sk_c2_chunk_finalize/src/main.nr (line 29), apply the same
identity check and pass the variant through accumulator verification to enforce
SK-only folding.

In `@circuits/bin/recursive_aggregation/c2_chunk_fold_kernel/src/main.nr`:
- Around line 44-45: The recursive verification paths accept caller-supplied key
hashes without circuit-specific validation. In
circuits/bin/recursive_aggregation/c2_chunk_fold_kernel/src/main.nr:44-45,
circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr:28-30, and
circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/src/main.nr:29, and
likewise the c2_chunk_fold, c2ab_fold, sk_c2_chunk_finalize, and
c2_chunk_finalize circuits, compare each supplied verification-key hash against
that circuit’s expected hash before calling verify_honk_proof; preserve
verification only after all bindings pass.

In `@circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/src/main.nr`:
- Around line 13-34: Update main to validate the six-field accumulator prefix
and bind the supplied acc_vk and acc_key_hash to the intended C2ChunkFold or
C2ChunkFoldKernel context. Also validate the base_key_hash and chunk_key_hash
fields against the expected SK context, while preserving the existing ESM
variant distinction and flag checks. Reject proofs whose accumulator context or
key hashes do not match the selected circuit variant.

In `@circuits/lib/src/core/dkg/share_computation_base.nr`:
- Around line 51-64: Enforce the complete chunk partition by adding an assertion
that N equals CHUNK_SIZE multiplied by N_CHUNKS before generating commitments in
both circuits/lib/src/core/dkg/share_computation_base.nr lines 51-64 and
125-138. Apply the assertion in the SK commitment path and the ESM commitment
path, before their respective chunk-processing loops.

In `@crates/zk-prover/src/circuits/dkg/share_computation.rs`:
- Around line 49-72: In prove_chunked_share_computation_with_chunk_size, after
deriving chunk_count and before generating the base proof, compare it with the
compile-time SHARE_COMPUTATION_N_CHUNKS value and return ZkError::InvalidInput
on mismatch. In crates/multithread/src/multithread.rs lines 938-952, verify that
the zk_cli-configured chunk size reaches this handler; if it does not, document
that the path always uses DEFAULT_C2_CHUNK_SIZE and preserve that behavior.

In `@crates/zk-prover/tests/local_e2e_tests.rs`:
- Around line 653-689: Update crates/zk-prover/tests/local_e2e_tests.rs:653-689
test_chunked_share_computation_proof to use or construct an SK fixture producing
at least two C2 chunks and assert the expected chunk_count; update
crates/zk-prover/tests/local_e2e_tests.rs:691-728 to do the same for the ESM
fixture; update crates/zk-prover/tests/node_fold_correlated_e2e_tests.rs:589-640
to pass the resulting multi-chunk C2 proofs through the C2abChunkFold NodeFold
verification path.

---

Nitpick comments:
In `@crates/events/src/interfold_event/proof.rs`:
- Around line 161-162: Remove the unused C2ChunkFinalize variant from the
relevant proof enum, then delete its associated mappings and references while
preserving SkC2ChunkFinalize and ESmC2ChunkFinalize. Remove the
circuits/bin/recursive_aggregation/c2_chunk_finalize circuit package and any
build or generation entries that compile it.

In `@crates/events/src/interfold_event/signed_proof.rs`:
- Around line 60-67: Update the test helper that selects circuits for C2a and
C2b proofs to cover both entries in each corresponding circuit list, including
SkC2ChunkFinalize and ESmC2ChunkFinalize. Avoid relying only on the first
element; select each circuit explicitly or add separate test cases while
preserving existing proof construction behavior.

In `@crates/zk-prover/src/circuits/aggregation/c2_chunk_accumulator.rs`:
- Around line 424-443: Remove the redundant acc_public_inputs.len() <= 2 *
chunk_count validation and rely on the existing checked_sub error path for
layout validation. Simplify the acc_vk assignment by using the already validated
accumulator.circuit directly, eliminating the identity match while preserving
the surrounding circuit validation.
- Line 18: Document that C2_CHUNK_FOLD_PUBLIC_PREFIX_LEN must match the Noir
global C2_CHUNK_FOLD_PREFIX_LEN defined in the terminal and fold circuits,
identifying that global as the source of truth.
- Around line 253-285: Update the first-step handling around is_first_step and
generate_kernel_genesis_proof so the kernel proof becomes the initial
accumulator/prior state instead of being discarded. Ensure C2ChunkFold is
invoked only for subsequent chunks, while preserving the existing public-input
validation and finalizer flow that accepts C2ChunkFoldKernel.

In `@crates/zk-prover/src/circuits/dkg/share_computation.rs`:
- Around line 105-127: Hoist the `circuit_path` construction and
`CompiledCircuit::from_file` call out of the chunk-processing loop so the shared
`compiled` circuit is loaded once before iterating over `chunk_idx`. Keep each
iteration’s input-map creation and witness generation in the loop, reusing the
preloaded circuit.
- Around line 92-101: Move the y extraction and degree-length validation in the
share computation flow to before the prove_recursive_circuit base-proof
generation call. Preserve the existing missing-y serialization error and
coefficient-count InvalidInput error, and only invoke prove_recursive_circuit
after y has passed both checks.

In `@crates/zk-prover/src/lib.rs`:
- Around line 30-32: Restrict the re-exports of
generate_sequential_c2_chunk_fold and finalize_c2_chunk_fold in the crate root
to pub(crate), keeping both fold steps internal while preserving their use by
prove_chunked_share_computation.

In `@scripts/build-circuits.ts`:
- Around line 714-719: Split the VK decisions in generateVk: add a
needsNoirRecursiveVk(circuit) predicate for selecting the noir-recursive
profile, and gate runWriteVk('evm', ...) for aggregation circuits with
isAggregationWithEvmOnChain so finalize circuits do not generate or require an
unused EVM VK. Rename isFoldOrAggregation to a profile-oriented name such as
usesNonZkAccumulatorVkProfile and update all callers.
🪄 Autofix

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 Plus

Run ID: 99154c0c-7891-4c5f-8c51-266bd97840af

📥 Commits

Reviewing files that changed from the base of the PR and between 5a8a36a and 23a911b.

📒 Files selected for processing (47)
  • agent/CHUNKED_C2_PLAN.md
  • agent/flow-trace/04_DKG_AND_COMPUTATION.md
  • circuits/benchmarks/results_insecure_minimum/crisp_verify_gas.json
  • circuits/benchmarks/results_secure_minimum/benchmark_run_meta.json
  • circuits/benchmarks/results_secure_minimum/crisp_verify_gas.json
  • circuits/benchmarks/results_secure_minimum/integration_summary.json
  • circuits/benchmarks/results_secure_minimum/report.md
  • circuits/bin/dkg/Nargo.toml
  • circuits/bin/dkg/e_sm_share_computation_base/Nargo.toml
  • circuits/bin/dkg/e_sm_share_computation_base/src/main.nr
  • circuits/bin/dkg/share_computation_chunk/Nargo.toml
  • circuits/bin/dkg/share_computation_chunk/src/main.nr
  • circuits/bin/dkg/sk_share_computation_base/Nargo.toml
  • circuits/bin/dkg/sk_share_computation_base/src/main.nr
  • circuits/bin/recursive_aggregation/c2_chunk_finalize/Nargo.toml
  • circuits/bin/recursive_aggregation/c2_chunk_finalize/src/main.nr
  • circuits/bin/recursive_aggregation/c2_chunk_fold/Nargo.toml
  • circuits/bin/recursive_aggregation/c2_chunk_fold/src/main.nr
  • circuits/bin/recursive_aggregation/c2_chunk_fold_kernel/Nargo.toml
  • circuits/bin/recursive_aggregation/c2_chunk_fold_kernel/src/main.nr
  • circuits/bin/recursive_aggregation/c2ab_chunk_fold/Nargo.toml
  • circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr
  • circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/Nargo.toml
  • circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/src/main.nr
  • circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/Nargo.toml
  • circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/src/main.nr
  • circuits/lib/src/configs/insecure/dkg.nr
  • circuits/lib/src/configs/secure/dkg.nr
  • circuits/lib/src/core/dkg/mod.nr
  • circuits/lib/src/core/dkg/share_computation_base.nr
  • circuits/lib/src/core/dkg/share_computation_chunk.nr
  • circuits/lib/src/math/commitments.nr
  • crates/events/src/interfold_event/proof.rs
  • crates/events/src/interfold_event/signed_proof.rs
  • crates/multithread/src/multithread.rs
  • crates/zk-helpers/README.md
  • crates/zk-helpers/src/bin/zk_cli.rs
  • crates/zk-helpers/src/circuits/dkg/share_computation/codegen.rs
  • crates/zk-prover/src/circuits/aggregation/c2_chunk_accumulator.rs
  • crates/zk-prover/src/circuits/aggregation/mod.rs
  • crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs
  • crates/zk-prover/src/circuits/dkg/share_computation.rs
  • crates/zk-prover/src/lib.rs
  • crates/zk-prover/tests/common/helpers.rs
  • crates/zk-prover/tests/local_e2e_tests.rs
  • crates/zk-prover/tests/node_fold_correlated_e2e_tests.rs
  • scripts/build-circuits.ts

Comment thread agent/flow-trace/04_DKG_AND_COMPUTATION.md Outdated
Comment thread circuits/bin/recursive_aggregation/c2_chunk_finalize/src/main.nr
Comment thread circuits/bin/recursive_aggregation/c2_chunk_fold_kernel/src/main.nr
Comment thread circuits/bin/recursive_aggregation/sk_c2_chunk_finalize/src/main.nr
Comment thread circuits/lib/src/core/dkg/share_computation_base.nr
Comment thread crates/zk-prover/src/circuits/dkg/share_computation.rs Outdated
Comment thread crates/zk-prover/tests/local_e2e_tests.rs
@0xjei

0xjei commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 19

Caution

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

⚠️ Outside diff range comments (2)
crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs (1)

239-259: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject chunk sizes that do not match the committed circuit configuration.

Lines 239 and 256 always commit with chunk size 512. The chunked prover accepts any non-zero divisor and slices the witnesses with that supplied size. A caller can therefore request a non-512 chunk size that passes validation but cannot match the generated chunk circuit or expected root commitment.

Reject every size except the configured SHARE_COMPUTATION_CHUNK_SIZE. If multiple sizes are supported, propagate the selected size through the circuit selection and root commitment calculation.

🤖 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/dkg/share_computation/computation.rs` around
lines 239 - 259, Update the chunk-size validation in the chunked prover to
reject every size except the configured SHARE_COMPUTATION_CHUNK_SIZE, rather
than accepting any non-zero divisor. Ensure circuit selection, witness slicing,
and root commitment calls such as compute_sc_sk_secret_root_commitment and
compute_sc_esm_secret_root_commitment all use the same configured size.
circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr (1)

18-29: 🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

Bind every recursive child verification key to its canonical circuit.

The recursive verifiers accept caller-selected verification keys and matching hashes. A replacement circuit can produce the required public-input shape and chosen chunk roots without proving the intended C2 computation.

  • circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr#L18-L29: compare c2a_key_hash and c2b_key_hash with canonical C2 finalizer verification-key hashes before consuming either child public output.
  • circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/src/main.nr#L27-L47: compare batch_key_hash with the canonical c2_chunk_batch verification-key hash before consuming batch public outputs.
🤖 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 `@circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr` around lines
18 - 29, Bind each recursive child proof to its canonical circuit before using
its public outputs: in
circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr lines 18-29,
validate c2a_key_hash and c2b_key_hash against the canonical C2 finalizer
verification-key hashes before the verify_honk_proof calls or any output
consumption; in
circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/src/main.nr lines
27-47, validate batch_key_hash against the canonical c2_chunk_batch
verification-key hash before consuming batch public outputs.
🧹 Nitpick comments (23)
circuits/bin/recursive_aggregation/node_fold/src/main.nr (1)

154-157: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the literal indices 3 and 4 with named globals.

The file already defines layout globals such as C2AB_PREFIX_LEN, C2A_INNER_START, and C2B_INNER_START. The two new emitted fields use raw indices 3 and 4. If the c2ab_fold prefix layout changes again, these literals silently select the wrong fields.

Define named globals for the two root-commitment slots and use them here.

♻️ Proposed refactor
 pub global C2A_SHARES_START: u32 = C2A_INNER_START + 1;
 pub global C2B_SHARES_START: u32 = C2B_INNER_START + 1;
+
+/// Root commitments carried by `c2ab_chunk_fold` in the expanded prefix.
+pub global C2A_ROOT_INDEX: u32 = 3;
+pub global C2B_ROOT_INDEX: u32 = 4;
     (
-        key_hash, c2ab_public[3], c2ab_public[4], c0_pk, c1_pk, c3_pk, c2a, c2b, c4a_exp, c4b_exp,
+        key_hash, c2ab_public[C2A_ROOT_INDEX], c2ab_public[C2B_ROOT_INDEX], c0_pk, c1_pk, c3_pk, c2a, c2b, c4a_exp, c4b_exp,
         sk_agg, esm_agg,
     )
🤖 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 `@circuits/bin/recursive_aggregation/node_fold/src/main.nr` around lines 154 -
157, Define named globals for the root-commitment slots currently represented by
indices 3 and 4, alongside the existing c2ab layout globals, then replace
c2ab_public[3] and c2ab_public[4] in the emitted tuple with those named globals.
crates/keyshare/src/threshold_keyshare/derive_decryption_key.rs (1)

339-339: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use own_party_id directly instead of party_id as u64.

party_id is own_party_id as usize (line 65). party_id as u64 converts the value back. The round trip is lossy on 32-bit targets and hides that the field is the caller-supplied party identifier. recipient_party_id is bound into the C4 proof, so the value must stay exact.

♻️ Proposed refactor
-        recipient_party_id: party_id as u64,
+        recipient_party_id: own_party_id,

Apply the same change at line 355.

Also applies to: 355-355

🤖 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/keyshare/src/threshold_keyshare/derive_decryption_key.rs` at line 339,
In the decryption-key derivation flow, replace both recipient_party_id
assignments in the C4 proof construction with own_party_id directly, at the
locations currently using party_id as u64. Preserve the exact caller-supplied
identifier without converting through party_id.
crates/zk-prover/src/circuits/dkg/share_computation.rs (1)

91-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Hoist the loop-invariant work out of the chunk loop.

Four items do not depend on chunk_idx:

  • The secret_key name and the secret lookup at lines 91-97.
  • circuit_path at lines 148-151.
  • CompiledCircuit::from_file at line 152, which re-reads and re-parses the circuit artifact for every chunk.
  • WitnessGenerator::new() at line 153.

For the secure preset the circuit artifact is large and chunk_count is 16, so the artifact is read and parsed 16 times. Compute all four before the loop.

♻️ Proposed refactor
+    let secret_key = match data.dkg_input_type {
+        DkgInputType::SecretKey => "sk_secret",
+        DkgInputType::SmudgingNoise => "e_sm_secret",
+    };
+    let secret = base_json.get(secret_key).ok_or_else(|| {
+        ZkError::SerializationError(format!("C2 input is missing {secret_key}"))
+    })?;
+    let circuit_path = prover
+        .circuits_dir(e3_events::CircuitVariant::Recursive, artifacts_dir)
+        .join(chunk_circuit.dir_path())
+        .join(format!("{}.json", chunk_circuit.as_str()));
+    let compiled = crate::witness::CompiledCircuit::from_file(&circuit_path)?;
+    let witness_gen = crate::witness::WitnessGenerator::new();
+
     let mut chunks = Vec::with_capacity(chunk_count);
     for chunk_idx in 0..chunk_count {

Also applies to: 148-157

🤖 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-prover/src/circuits/dkg/share_computation.rs` around lines 91 - 97,
Move the chunk-independent setup out of the chunk loop: resolve `secret_key` and
`secret` once, compute `circuit_path`, create the `CompiledCircuit` via
`CompiledCircuit::from_file`, and initialize `WitnessGenerator::new()` before
iterating chunks. Reuse these values for every chunk while preserving the
existing per-chunk computation and error behavior.
crates/zk-prover/src/circuits/aggregation/c2_chunk_batch.rs (1)

202-215: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extend the test module beyond chunks_per_batch.

The tests cover only the batch-size helper. The validation branches in generate_c2_chunk_batches and finalize_c2_chunk_batches are pure input checks and do not need artifacts: wrong chunk_count, non-divisible chunk count, a mismatched chunk_circuit, an empty batch list, an invalid finalizer circuit, and unequal batch public-input lengths. Add tests for those branches.

🤖 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-prover/src/circuits/aggregation/c2_chunk_batch.rs` around lines 202
- 215, Extend the tests module around chunks_per_batch with focused tests for
validation branches in generate_c2_chunk_batches and finalize_c2_chunk_batches,
using lightweight inputs without generating artifacts. Cover wrong and
non-divisible chunk_count, mismatched chunk_circuit, empty batches, invalid
finalizer circuit, and unequal batch public-input lengths, asserting each input
is rejected.
crates/zk-prover/src/circuits/aggregation/c3_accumulator.rs (1)

128-136: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Define field_keys constants for party_idx and mod_idx. The share_encryption entry point exposes only expected_pk_commitment and expected_message_commitment as public fields; it does not declare party_idx or mod_idx as circuit inputs. Do not require those names in the circuit's public-input metadata.

🤖 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-prover/src/circuits/aggregation/c3_accumulator.rs` around lines 128
- 136, Update the field extraction logic near the fields array to use field_keys
constants for party_idx and mod_idx, defining those constants alongside the
existing field key definitions. Preserve share_encryption’s public-input
metadata contract by ensuring these internal indices are not required as
declared circuit inputs.
crates/program-server/src/lib.rs (1)

307-310: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Clone only the BFV parameters instead of the complete FHEInputs.

Line 309 clones the whole job.inputs, including ciphertexts. The request body limit is 10 MB, so this duplicates the largest field on every computation. Line 316 uses only fhe_inputs.params.

♻️ Proposed change to clone only `params`
-    let fhe_inputs = job.inputs.clone();
+    let params_bytes = job.inputs.params.clone();
     match runner(job).await {

Then update the commitment call:

-            let params = decode_bfv_params_arc(&fhe_inputs.params)
+            let params = decode_bfv_params_arc(&params_bytes)
                 .context("failed to decode BFV params for commitment")?;
🤖 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/program-server/src/lib.rs` around lines 307 - 310, In the computation
handler around runner(job).await, replace the full job.inputs clone with a clone
of only its BFV params field. Update the later commitment call to use this
params value while preserving the existing runner and result flow.
examples/CRISP/server/src/cli/commands.rs (1)

191-201: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Bound the event query.

Line 196 scans all registry logs from genesis for every readiness check. The range grows indefinitely and can exceed RPC provider log limits. Start the query at the registry deployment block or the E3 request block.

🤖 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 `@examples/CRISP/server/src/cli/commands.rs` around lines 191 - 201, Update the
event query in the readiness-check flow using CommitteePublished_filter to set a
bounded starting block instead of from_block(0), choosing the registry
deployment block or the E3 request block, while preserving the existing topic
filter and readiness result.
packages/interfold-contracts/contracts/interfaces/IInterfold.sol (1)

646-655: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Return ICiphertextVerifier from the getter for symmetry.

setCiphertextVerifier accepts ICiphertextVerifier, but getCiphertextVerifier returns address. The pk-verifier pair uses the interface type on both sides. Use the interface type here so callers do not need a cast.

♻️ Proposed change
     /// `@notice` Returns the ciphertext verifier configured for future requests.
     function getCiphertextVerifier(
         bytes32 encryptionSchemeId
-    ) external view returns (address);
+    ) external view returns (ICiphertextVerifier);
🤖 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 `@packages/interfold-contracts/contracts/interfaces/IInterfold.sol` around
lines 646 - 655, Update the getCiphertextVerifier declaration in IInterfold so
its return type is ICiphertextVerifier, matching setCiphertextVerifier and the
interface-typed getter contract used by the pk-verifier pair.
packages/interfold-contracts/contracts/registry/CiphernodeRegistryOwnable.sol (1)

295-295: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a configuration error for a zero ticket price.

InvalidTicketNumber describes a bad caller-supplied ticket index. Here the revert means the bonding registry has no ticket price configured. A distinct error makes the misconfiguration diagnosable from the revert alone.

🤖 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
`@packages/interfold-contracts/contracts/registry/CiphernodeRegistryOwnable.sol`
at line 295, Replace InvalidTicketNumber in the ticketPrice validation with the
contract’s distinct configuration error for an unset or zero ticket price, while
preserving the existing ticketPrice > 0 condition.
packages/interfold-contracts/contracts/interfaces/ICiphertextVerifier.sol (1)

13-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the failure contract of verify.

The return type is bool, but the NatSpec does not state whether an implementation reverts with a typed error or returns false on rejection. IDecryptionVerifier.verify documents this explicitly. Add the same statement here so every implementation and caller agrees on the rejection path.

🤖 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 `@packages/interfold-contracts/contracts/interfaces/ICiphertextVerifier.sol`
around lines 13 - 29, Update the NatSpec for ICiphertextVerifier.verify to
explicitly document its rejection behavior, matching the established wording and
contract used by IDecryptionVerifier.verify: specify whether invalid
verification reverts with the typed error or returns false. Keep the function
signature unchanged.
packages/interfold-contracts/scripts/deployAndSave/bondingRegistry.ts (1)

50-51: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reconsider silently defaulting the token decimal values.

BondingRegistry validates expectedTicketDecimals and expectedLicenseDecimals against the tokens' reported ERC-20 decimals and reverts with BondingAssetDecimalsMismatch. These defaults of 6 and 0 fit only the deployInterfold.ts case, where the license token is ethers.ZeroAddress. A caller that supplies a real 18-decimal license token and omits licenseTokenDecimals gets a deployment-time revert instead of a clear argument error.

Every other value the contract depends on is required in the guard at Lines 64-89. Treat these two the same way, or keep the defaults and document that 0 means "no license token configured".

🤖 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 `@packages/interfold-contracts/scripts/deployAndSave/bondingRegistry.ts` around
lines 50 - 51, Remove the implicit defaults for ticketTokenDecimals and
licenseTokenDecimals in the deployment argument configuration, making both
values required and validating them alongside the other contract-dependent
inputs in the existing guard. If preserving the defaults is necessary,
explicitly document and enforce that licenseTokenDecimals of 0 represents no
configured license token, while allowing real license tokens to supply their
actual decimals.
packages/interfold-contracts/contracts/slashing/SlashingManager.sol (1)

369-379: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

policy.failureReason and p.failureReason are now write-only.

setSlashPolicy validates policy.failureReason, but both proposal paths ignore the stored value and derive p.failureReason from affectsCommittee. _executeSlash then hardcodes IInterfold.FailureReason.InsufficientCommitteeMembers at Line 871 instead of reading p.failureReason. A policy with affectsCommittee = true and failureReason = 0 still produces InsufficientCommitteeMembers, so the validation has no effect on behavior.

The comment at Line 369 states the field is retained for ABI and storage compatibility. Make that explicit in code: either read p.failureReason in _executeSlash, or drop the derivation at Lines 536 and 627 and mark both fields deprecated so a future reader does not assume the policy value is honored.

Also applies to: 536-538, 627-629

🤖 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 `@packages/interfold-contracts/contracts/slashing/SlashingManager.sol` around
lines 369 - 379, Make failureReason behavior consistent across setSlashPolicy,
both proposal paths, and _executeSlash: either propagate and use p.failureReason
through execution, or remove the derivations and explicitly mark
policy.failureReason and p.failureReason deprecated because they are
compatibility-only fields. Ensure the implementation does not imply that the
stored policy value affects the slashing reason when _executeSlash still
hardcodes InsufficientCommitteeMembers.
packages/interfold-contracts/test/E3Lifecycle/E3Integration.spec.ts (1)

214-228: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider reusing the shared committee publication helper.

finalizeAndPublishCommittee publishes only the commitment. The shared fixture packages/interfold-contracts/test/fixtures/helpers.ts setupAndPublishCommittee publishes the commitment and then calls publishCommitteePublicKey(e3Id, publicKey). The two helpers now leave the committee in different states.

The current tests pass with the commitment alone. A future test that reads the committee public key would fail in a way that is hard to trace. Either delegate to setupAndPublishCommittee, or add the publishCommitteePublicKey call here for parity.

♻️ Proposed change to publish the key candidate as well
       await registry.publishCommittee(
         0,
         pkCommitment,
         encodeMockDkgProof(pkCommitment),
         "0x01",
       );
+      await registry.publishCommitteePublicKey(0, publicKey);
     };
🤖 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 `@packages/interfold-contracts/test/E3Lifecycle/E3Integration.spec.ts` around
lines 214 - 228, Update finalizeAndPublishCommittee so it leaves the committee
in the same fully published state as setupAndPublishCommittee: after
publishCommittee, also publish the corresponding public key using
publishCommitteePublicKey with e3Id 0 and the same publicKey, or delegate to the
shared helper while preserving the existing setup flow.
packages/interfold-contracts/contracts/interfaces/IBondingRegistry.sol (1)

542-552: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The function name still says "AtBlock" while the parameter is a timestamp.

The documentation now describes timepoint as a timestamp-mode checkpoint, but the exported name getTicketBalanceAtBlock says block number. An integrator that passes block.number gets a wrong balance without any revert.

Rename the function to getTicketBalanceAt in the same change set that already breaks the interface, or state the timestamp unit in the name.

🤖 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 `@packages/interfold-contracts/contracts/interfaces/IBondingRegistry.sol`
around lines 542 - 552, Rename the IBondingRegistry function
getTicketBalanceAtBlock to getTicketBalanceAt so its exported name matches the
timestamp-based timepoint parameter and documentation. Update all corresponding
declarations, implementations, and call sites in the same change set, preserving
the existing behavior.
packages/interfold-contracts/contracts/E3RefundManager.sol (1)

895-899: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Emit the treasury credit event in _redistributeHeldSuccess.

The treasury fallback in _creditBaseTopUps emits TreasurySlashedCredited. This fallback increments _pendingTreasury without any event, so indexers cannot observe the credit.

♻️ Proposed change
         if (eligible == 0) {
             address treasuryAddress = _treasuryFor(e3Id);
             _pendingTreasury[treasuryAddress][token] += amount;
+            emit TreasurySlashedCredited(treasuryAddress, token, amount);
             return;
         }
🤖 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 `@packages/interfold-contracts/contracts/E3RefundManager.sol` around lines 895
- 899, Update the eligible == 0 treasury fallback in _redistributeHeldSuccess to
emit the same TreasurySlashedCredited event used by _creditBaseTopUps after
incrementing _pendingTreasury, preserving the existing treasury address, token,
and amount values.
packages/interfold-contracts/contracts/lib/BondingAssetLib.sol (1)

85-102: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Return the new configuration version instead of only emitting it.

validateBondingAssetConfig computes configurationVersion + (assetChanged ? 1 : 0) for the event. It returns only assetChanged. The caller in BondingRegistry must reproduce the same increment rule to store the version. Two copies of the rule can diverge and make the emitted version disagree with bondingAssetConfigurationVersion().

Return the computed version and let the caller store exactly that value.

♻️ Proposed change
-    ) external returns (bool assetChanged) {
+    ) external returns (bool assetChanged, uint64 newConfigurationVersion) {
@@
+        newConfigurationVersion =
+            configurationVersion +
+            (assetChanged ? 1 : 0);
         emit IBondingRegistry.BondingAssetConfigUpdated(
             InterfoldTicketToken(config.ticketToken),
             IERC20(config.licenseToken),
             config.ticketPrice,
             config.licenseRequiredBond,
             config.expectedTicketDecimals,
             config.expectedLicenseDecimals,
-            configurationVersion + (assetChanged ? 1 : 0)
+            newConfigurationVersion
         );
🤖 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 `@packages/interfold-contracts/contracts/lib/BondingAssetLib.sol` around lines
85 - 102, Update validateBondingAssetConfig to return the computed configuration
version, configurationVersion + (assetChanged ? 1 : 0), alongside or instead of
the current assetChanged result, and use that returned value in BondingRegistry
when storing the configuration version. Remove the caller’s duplicate increment
logic so the emitted event and bondingAssetConfigurationVersion() use the same
value.
packages/interfold-contracts/contracts/lib/BondingEligibilityLib.sol (1)

121-131: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Two new storage-slot accessors omit the memory-safe assembly annotation. BondingAssetLib._slashingLayout and BondingSlashingLib._layout use assembly ("memory-safe"). These two accessors use plain assembly, which blocks the optimizer assumptions the other accessors keep.

  • packages/interfold-contracts/contracts/lib/BondingEligibilityLib.sol#L121-L131: change assembly { to assembly ("memory-safe") { in _layout.
  • packages/interfold-contracts/contracts/lib/InterfoldLifecycle.sol#L268-L277: change assembly { to assembly ("memory-safe") { in _ciphertextVerifierLayout.
🤖 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 `@packages/interfold-contracts/contracts/lib/BondingEligibilityLib.sol` around
lines 121 - 131, Add the "memory-safe" annotation to the assembly blocks in
BondingEligibilityLib._layout
(packages/interfold-contracts/contracts/lib/BondingEligibilityLib.sol:121-131)
and InterfoldLifecycle._ciphertextVerifierLayout
(packages/interfold-contracts/contracts/lib/InterfoldLifecycle.sol:268-277),
matching the existing storage-slot accessors; no other logic changes are needed.
packages/interfold-contracts/contracts/Interfold.sol (1)

1009-1018: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

_setFeeAssetConfig allow-lists the new token and keeps the previous one allowed.

The function sets _feeTokenAllowed[token] = true for the new asset. It never clears the entry for the rotated-out token. The allow-list therefore accumulates stale entries after each rotation, and the guard in request at Line 260 can no longer fail for a freshly configured asset.

If the allow-list is meant to be an explicit owner decision, keep setFeeAssetConfig and setFeeTokenAllowed independent. If the automatic entry is intended, document it on the setter.

🤖 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 `@packages/interfold-contracts/contracts/Interfold.sol` around lines 1009 -
1018, Update _setFeeAssetConfig so rotating the fee token does not automatically
add the new token to _feeTokenAllowed or leave stale allow-list entries; keep
setFeeAssetConfig and setFeeTokenAllowed independent, allowing only explicit
owner actions to manage the allow-list.
packages/interfold-contracts/test/BfvPkVerifier.spec.ts (1)

280-300: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a mismatch test for expectedESmC2ChunkKeyHash.

This test only verifies rejection at index 5 + H. Add the same test with index 6 + H changed. This protects the ESM C2 chunk binding from removal or an offset error.

Proposed test
+    it("reverts VkHashMismatch when the ESM C2 chunk key hash does not match", async function () {
+      const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit);
+      const { e3Id, root, nodes } = ctx();
+      const pkCommitment = ethers.keccak256("0xabcd");
+      const publicInputs = minimalDkgPublicInputs(pkCommitment).map((v, i) =>
+        i === 6 + H ? ethers.id("wrong-esm-c2-chunk") : v,
+      );
+
+      await expect(
+        bfvPkVerifier.verify.staticCall(
+          e3Id,
+          root,
+          nodes,
+          pkCommitment,
+          ethers.ZeroHash,
+          encodeProof("0x01", publicInputs),
+        ),
+      ).to.be.revertedWithCustomError(bfvPkVerifier, "VkHashMismatch");
+    });
🤖 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 `@packages/interfold-contracts/test/BfvPkVerifier.spec.ts` around lines 280 -
300, Add a second test alongside the existing SK C2 mismatch case, changing the
public input at index 6 + H to an incorrect ESM C2 chunk key hash while keeping
the remaining fixture, proof, verification call, and VkHashMismatch assertion
consistent.
packages/interfold-contracts/test/Slashing/CommitteeExpulsion.spec.ts (1)

36-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Import the failure-reason value from the shared fixtures.

INSUFFICIENT_COMMITTEE_MEMBERS = 2 encodes a contract enum value as a local literal. The file already imports COMMITTEE_THRESHOLDS_ONCHAIN from ../fixtures. Move this constant into packages/interfold-contracts/test/fixtures/constants.ts and import it. The enum value then has one definition for every spec that asserts a failure reason.

🤖 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 `@packages/interfold-contracts/test/Slashing/CommitteeExpulsion.spec.ts` at
line 36, Move INSUFFICIENT_COMMITTEE_MEMBERS from CommitteeExpulsion.spec.ts
into the shared constants fixture, then import and reuse it alongside
COMMITTEE_THRESHOLDS_ONCHAIN. Remove the local literal so all specs asserting
this failure reason use the single shared definition.
packages/interfold-contracts/test/Slashing/SlashingManager.spec.ts (1)

285-299: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Deploy this system through a fixture.

Every other test in this file obtains state from loadFixture(setup). This test calls deployInterfoldSystem inline, so the deployment runs on every invocation and is not covered by the snapshot cache. Extract a second fixture function, for example setupWiredSlashingManager, and load it with loadFixture. The file then keeps one state-setup mechanism.

🤖 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 `@packages/interfold-contracts/test/Slashing/SlashingManager.spec.ts` around
lines 285 - 299, Extract the inline deployment in the “requires policies to be
refreshed after an asset rotation” test into a dedicated fixture function such
as setupWiredSlashingManager, preserving its deployInterfoldSystem options and
returned system state. Update the test to obtain that state through
loadFixture(setupWiredSlashingManager), while retaining the existing signer
setup as appropriate.
scripts/build-circuits.ts (1)

311-313: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the preset/committee derivation and honor skipUtilsPatch.

Two concerns in writeActiveCryptoConfig:

  1. Lines 313-315 repeat the paramSet and committeeSize derivation from patchUtilsTs (lines 254-256). Extract one helper so the two generated artifacts cannot diverge.
  2. patchUtilsTs returns early when skipUtilsPatch is set and when the target file is absent ("optional in minimal checkouts"). writeActiveCryptoConfig has neither guard, so writeFileSync at line 362 throws ENOENT in a checkout without packages/interfold-contracts/contracts/lib/. Add the same guards.
♻️ Proposed guard for parity with `patchUtilsTs`
   private writeActiveCryptoConfig(preset: CircuitPreset, committee: CircuitCommittee): void {
+    if (this.options.skipUtilsPatch) return
     const { h, t, n } = COMMITTEE_PARAMS[committee]
     const paramSet = preset === CIRCUIT_PRESETS.INSECURE_512 ? 0 : 1
     const committeeSize = ALL_COMMITTEES.indexOf(committee)
     const params = paramSet === 0 ? BFV_PARAMS.insecure512 : BFV_PARAMS.secure8192
     const path = join(this.rootDir, 'packages', 'interfold-contracts', 'contracts', 'lib', 'ActiveCryptoConfig.sol')
+    if (!existsSync(dirname(path))) return // optional in minimal checkouts

Also applies to: 322-323

🤖 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 311 - 313, Extract the shared
preset/committee derivation used by patchUtilsTs and writeActiveCryptoConfig
into one helper, then reuse it in both paths. Update writeActiveCryptoConfig to
return early when skipUtilsPatch is enabled or its target file is absent,
matching patchUtilsTs before calling writeFileSync.
packages/interfold-contracts/test/Registry/BondingRegistry.spec.ts (1)

115-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Impersonation is acquired without a guaranteed release in both specs. Each helper calls impersonateAccount and then relies on a later stopImpersonatingAccount that a failed assertion or a reverting call can skip. Impersonation is node-level state that loadFixture snapshot restore does not clear, so one failing test can leak signing authority for that address into the remaining tests.

  • packages/interfold-contracts/test/Registry/BondingRegistry.spec.ts#L115-L122: change impersonateSlashingManager into a scoped helper that runs the caller's callback and calls stopImpersonatingAccount in a finally block, then drop the manual cleanup at each call site.
  • packages/interfold-contracts/test/Slashing/CommitteeExpulsion.spec.ts#L189-L206: wrap the expelCommitteeMember loop in try/finally so stopImpersonatingAccount runs when an expulsion reverts, or reuse the scoped helper.
🤖 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 `@packages/interfold-contracts/test/Registry/BondingRegistry.spec.ts` around
lines 115 - 122, Ensure impersonated accounts are always released: in
packages/interfold-contracts/test/Registry/BondingRegistry.spec.ts#L115-L122,
make impersonateSlashingManager execute a caller callback and stop impersonating
in a finally block, removing manual cleanup at its call sites; in
packages/interfold-contracts/test/Slashing/CommitteeExpulsion.spec.ts#L189-L206,
wrap the expelCommitteeMember loop in try/finally or reuse the scoped helper so
cleanup occurs on failures.
🤖 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 `@agent/flow-trace/04_DKG_AND_COMPUTATION.md`:
- Around line 329-331: Update the paragraph describing BfvPkVerifier so it
states that publicInputs[0] anchors the nodesFold VK hash and publicInputs[1]
anchors the C5 VK hash; remove the claim that SK/ESM chunk VK hashes are direct
immutable anchors while preserving the surrounding terminal-proof flow.

In `@agent/flow-trace/05_FAILURE_REFUND_SLASHING.md`:
- Around line 216-222: Update the claimant terminology in the E3 reward-claim
flow to identify the frozen reward recipient rather than the bond owner. Revise
the caller requirement around msg.sender == recipient and the related transfer
description so they consistently state that the frozen recipient is the
claimant, including the corresponding section at the additional referenced
location.

In `@crates/events/src/interfold_event/compute_request/zk.rs`:
- Around line 193-195: Update the slot-index calculation in the surrounding
proof-request method to use checked multiplication for recipient_party_id and
n_moduli, checked addition with row_index, and a fallible conversion to u32.
Propagate the resulting failure so proof requests with unrepresentable slot
indices are rejected rather than producing a wrapped or saturated value.

In `@crates/multithread/src/multithread.rs`:
- Around line 1154-1155: In the proof-generation flow constructing the values
with party_idx and mod_idx, replace the truncating casts with checked
conversions. Validate req.recipient_party_id against the committee size and
req.row_index against the configured modulus count before generating the proof,
rejecting out-of-range identifiers so the circuit-bound values match the
response identifiers.

In `@crates/support/README.md`:
- Around line 183-186: Update the request example’s JSON key from
committee_public_key to committee_public_key_hash so it matches the required
ComputeRequest field and copied curl requests are accepted.

In `@crates/zk-prover/src/circuits/dkg/share_computation.rs`:
- Around line 98-146: Validate secret coefficient lengths once before the chunk
loop, using the same expected degree as the existing y validation: check the
SecretKey coefficient array and every ESM CRT limb, returning
ZkError::InvalidInput on mismatch. Add or reuse a slice_coefficients helper that
performs bounds-safe chunk extraction, then replace both direct coefficient
range slices in the secret_chunk construction with slice_coefficients(...)?
while preserving the existing JSON validation errors.

In `@crates/zk-prover/src/commitment_links/c2_to_c4/mod.rs`:
- Around line 213-223: Update the C2 commitment-link flow to receive the
public-signal layout explicitly, deriving it from the C2 proof circuit name
rather than inferring it from signal lengths or alternative positions. In
crates/zk-prover/src/commitment_links/c2_to_c4/mod.rs lines 213-223, replace the
hardcoded [3usize, 9, 19] length test with the layout-provided prefix length. In
crates/zk-prover/src/commitment_links/c1_to_c2.rs lines 70-72 and 109-112, make
the C2 and C2b checks each compare the commitment at the single position
specified by that layout, removing the field 0/field 1 alternatives.

In `@packages/interfold-contracts/contracts/E3RefundManager.sol`:
- Around line 1086-1091: Update the expulsion resolution flow around
openExpulsionProposal and _resolveExpulsionOutcome so an expelled operator’s
base reward is forfeited whenever _distributions[e3Id].calculated is true,
regardless of proposal.holdsBaseReward or when the proposal opened. Ensure
_takeForfeitedBaseReward receives the calculated-distribution case and
_consumeBaseReward is invoked as needed so the operator share and final dust are
accounted for.

In `@packages/interfold-contracts/contracts/interfaces/ICiphernodeRegistry.sol`:
- Around line 71-84: Update the NatSpec block for CommitteeRequested to document
its ticketPrice parameter, describing the ticket price associated with committee
formation while preserving the existing parameter documentation.

In `@packages/interfold-contracts/contracts/lib/ActiveCryptoConfig.sol`:
- Line 15: Update the generator that produces ActiveCryptoConfig so
COMMITTEE_SIZE uses the active generated committee size rather than zero,
preserving the supported preset values 3, 9, and 19. Add a request-creation test
covering the active preset and verifying it succeeds with the generated
committee size.

In `@packages/interfold-contracts/contracts/lib/BondingEligibilityLib.sol`:
- Around line 38-52: Update invalidateConfiguration to return a reset
active-operator count alongside newVersion, and set the checkpointed count to
that same reset value. Modify the caller of invalidateConfiguration to assign
the returned reset value to its local activeOperatorCount before any subsequent
updateOperator call, ensuring both storage representations are reset
consistently.

In `@packages/interfold-contracts/contracts/lib/InterfoldLifecycle.sol`:
- Around line 163-188: In publishCiphertext, call _verifyCiphertext before
updating e3.ciphertextOutput, e3.ciphertextCommitment, stages[e3Id], or
deadlines[e3Id].decryptionDeadline. Keep the existing state writes and
CiphertextOutputPublished/E3StageChanged emissions after successful verification
so reentrant calls cannot observe CiphertextReady before publication events.

In `@packages/interfold-contracts/contracts/lib/InterfoldPricing.sol`:
- Around line 142-160: Align _creditReward and rewardDisposition so excluded
operators are treated as non-held and are not credited, preventing
holdSuccessReward from reverting when pending expulsions coexist with exclusion.
Also inspect the active-node flow around getActiveCommitteeNodes,
expelCommitteeMember, and snapshotRewardRecipients to ensure every returned node
has a snapshotted recipient, preserving reward completion without
RewardRecipientNotSnapshotted failures.

In
`@packages/interfold-contracts/contracts/registry/CiphernodeRegistryOwnable.sol`:
- Around line 294-296: Update requestCommittee and _validateNodeEligibility to
freeze the bonding asset context when an E3 is requested, not just
sortitionTicketPrices[e3Id]. Snapshot the ticket token address (or
bondingAssetConfigurationVersion()) alongside the price, then reject submissions
if the current context differs from the request-time snapshot before calculating
ticket weight.

In
`@packages/interfold-contracts/contracts/verifiers/bfv/Risc0BfvCiphertextVerifier.sol`:
- Around line 30-35: Update the Risc0BfvCiphertextVerifier constructor to reject
verifier addresses with no deployed code, in addition to the existing
zero-address check. Validate address(verifier).code.length is nonzero before
assigning risc0Verifier, while preserving the existing InvalidVerifier handling.

In `@packages/interfold-contracts/scripts/benchmarkGasFromRaw.ts`:
- Around line 328-330: Update the failure message for the dkgPublicInputs
validation to report all four validated VK hashes: retain nodes_fold and
pk_aggregation, and add sk_share_computation_chunk and
esm_share_computation_chunk for the two C2 chunk checks.

In `@packages/interfold-contracts/test/E3Lifecycle/E3Integration.spec.ts`:
- Around line 1700-1702: Make the final claimHonestNodeReward call in the E3
lifecycle test an explicit assertion, using the test’s established assertion
style to state the intended successful claim for operator1. Preserve the
existing requester connection and claim arguments.

In `@scripts/check-committee.sh`:
- Around line 87-93: Add a default branch to the EXPECTED_SIZE case mapping in
scripts/check-committee.sh that calls fail for unknown ACTIVE_COMMITTEE values,
before the subsequent SOL_SIZE comparison. Preserve the existing minimum, micro,
and small mappings so unexpected committees produce the intended drift failure
instead of leaving EXPECTED_SIZE unset.

In `@templates/default/contracts/MyProgram.sol`:
- Around line 79-89: Update publishInput to verify that ciphertext matches
ciphertextCommitment using the configured ciphertext verifier or an equivalent
proof before calling inputs[e3Id]._insert. Reject invalid bindings before
mutating the input tree, while preserving the existing empty-ciphertext check
and insertion flow for valid submissions.

---

Outside diff comments:
In `@circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr`:
- Around line 18-29: Bind each recursive child proof to its canonical circuit
before using its public outputs: in
circuits/bin/recursive_aggregation/c2ab_chunk_fold/src/main.nr lines 18-29,
validate c2a_key_hash and c2b_key_hash against the canonical C2 finalizer
verification-key hashes before the verify_honk_proof calls or any output
consumption; in
circuits/bin/recursive_aggregation/esm_c2_chunk_finalize/src/main.nr lines
27-47, validate batch_key_hash against the canonical c2_chunk_batch
verification-key hash before consuming batch public outputs.

In `@crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs`:
- Around line 239-259: Update the chunk-size validation in the chunked prover to
reject every size except the configured SHARE_COMPUTATION_CHUNK_SIZE, rather
than accepting any non-zero divisor. Ensure circuit selection, witness slicing,
and root commitment calls such as compute_sc_sk_secret_root_commitment and
compute_sc_esm_secret_root_commitment all use the same configured size.

---

Nitpick comments:
In `@circuits/bin/recursive_aggregation/node_fold/src/main.nr`:
- Around line 154-157: Define named globals for the root-commitment slots
currently represented by indices 3 and 4, alongside the existing c2ab layout
globals, then replace c2ab_public[3] and c2ab_public[4] in the emitted tuple
with those named globals.

In `@crates/keyshare/src/threshold_keyshare/derive_decryption_key.rs`:
- Line 339: In the decryption-key derivation flow, replace both
recipient_party_id assignments in the C4 proof construction with own_party_id
directly, at the locations currently using party_id as u64. Preserve the exact
caller-supplied identifier without converting through party_id.

In `@crates/program-server/src/lib.rs`:
- Around line 307-310: In the computation handler around runner(job).await,
replace the full job.inputs clone with a clone of only its BFV params field.
Update the later commitment call to use this params value while preserving the
existing runner and result flow.

In `@crates/zk-prover/src/circuits/aggregation/c2_chunk_batch.rs`:
- Around line 202-215: Extend the tests module around chunks_per_batch with
focused tests for validation branches in generate_c2_chunk_batches and
finalize_c2_chunk_batches, using lightweight inputs without generating
artifacts. Cover wrong and non-divisible chunk_count, mismatched chunk_circuit,
empty batches, invalid finalizer circuit, and unequal batch public-input
lengths, asserting each input is rejected.

In `@crates/zk-prover/src/circuits/aggregation/c3_accumulator.rs`:
- Around line 128-136: Update the field extraction logic near the fields array
to use field_keys constants for party_idx and mod_idx, defining those constants
alongside the existing field key definitions. Preserve share_encryption’s
public-input metadata contract by ensuring these internal indices are not
required as declared circuit inputs.

In `@crates/zk-prover/src/circuits/dkg/share_computation.rs`:
- Around line 91-97: Move the chunk-independent setup out of the chunk loop:
resolve `secret_key` and `secret` once, compute `circuit_path`, create the
`CompiledCircuit` via `CompiledCircuit::from_file`, and initialize
`WitnessGenerator::new()` before iterating chunks. Reuse these values for every
chunk while preserving the existing per-chunk computation and error behavior.

In `@examples/CRISP/server/src/cli/commands.rs`:
- Around line 191-201: Update the event query in the readiness-check flow using
CommitteePublished_filter to set a bounded starting block instead of
from_block(0), choosing the registry deployment block or the E3 request block,
while preserving the existing topic filter and readiness result.

In `@packages/interfold-contracts/contracts/E3RefundManager.sol`:
- Around line 895-899: Update the eligible == 0 treasury fallback in
_redistributeHeldSuccess to emit the same TreasurySlashedCredited event used by
_creditBaseTopUps after incrementing _pendingTreasury, preserving the existing
treasury address, token, and amount values.

In `@packages/interfold-contracts/contracts/interfaces/IBondingRegistry.sol`:
- Around line 542-552: Rename the IBondingRegistry function
getTicketBalanceAtBlock to getTicketBalanceAt so its exported name matches the
timestamp-based timepoint parameter and documentation. Update all corresponding
declarations, implementations, and call sites in the same change set, preserving
the existing behavior.

In `@packages/interfold-contracts/contracts/interfaces/ICiphertextVerifier.sol`:
- Around line 13-29: Update the NatSpec for ICiphertextVerifier.verify to
explicitly document its rejection behavior, matching the established wording and
contract used by IDecryptionVerifier.verify: specify whether invalid
verification reverts with the typed error or returns false. Keep the function
signature unchanged.

In `@packages/interfold-contracts/contracts/interfaces/IInterfold.sol`:
- Around line 646-655: Update the getCiphertextVerifier declaration in
IInterfold so its return type is ICiphertextVerifier, matching
setCiphertextVerifier and the interface-typed getter contract used by the
pk-verifier pair.

In `@packages/interfold-contracts/contracts/Interfold.sol`:
- Around line 1009-1018: Update _setFeeAssetConfig so rotating the fee token
does not automatically add the new token to _feeTokenAllowed or leave stale
allow-list entries; keep setFeeAssetConfig and setFeeTokenAllowed independent,
allowing only explicit owner actions to manage the allow-list.

In `@packages/interfold-contracts/contracts/lib/BondingAssetLib.sol`:
- Around line 85-102: Update validateBondingAssetConfig to return the computed
configuration version, configurationVersion + (assetChanged ? 1 : 0), alongside
or instead of the current assetChanged result, and use that returned value in
BondingRegistry when storing the configuration version. Remove the caller’s
duplicate increment logic so the emitted event and
bondingAssetConfigurationVersion() use the same value.

In `@packages/interfold-contracts/contracts/lib/BondingEligibilityLib.sol`:
- Around line 121-131: Add the "memory-safe" annotation to the assembly blocks
in BondingEligibilityLib._layout
(packages/interfold-contracts/contracts/lib/BondingEligibilityLib.sol:121-131)
and InterfoldLifecycle._ciphertextVerifierLayout
(packages/interfold-contracts/contracts/lib/InterfoldLifecycle.sol:268-277),
matching the existing storage-slot accessors; no other logic changes are needed.

In
`@packages/interfold-contracts/contracts/registry/CiphernodeRegistryOwnable.sol`:
- Line 295: Replace InvalidTicketNumber in the ticketPrice validation with the
contract’s distinct configuration error for an unset or zero ticket price, while
preserving the existing ticketPrice > 0 condition.

In `@packages/interfold-contracts/contracts/slashing/SlashingManager.sol`:
- Around line 369-379: Make failureReason behavior consistent across
setSlashPolicy, both proposal paths, and _executeSlash: either propagate and use
p.failureReason through execution, or remove the derivations and explicitly mark
policy.failureReason and p.failureReason deprecated because they are
compatibility-only fields. Ensure the implementation does not imply that the
stored policy value affects the slashing reason when _executeSlash still
hardcodes InsufficientCommitteeMembers.

In `@packages/interfold-contracts/scripts/deployAndSave/bondingRegistry.ts`:
- Around line 50-51: Remove the implicit defaults for ticketTokenDecimals and
licenseTokenDecimals in the deployment argument configuration, making both
values required and validating them alongside the other contract-dependent
inputs in the existing guard. If preserving the defaults is necessary,
explicitly document and enforce that licenseTokenDecimals of 0 represents no
configured license token, while allowing real license tokens to supply their
actual decimals.

In `@packages/interfold-contracts/test/BfvPkVerifier.spec.ts`:
- Around line 280-300: Add a second test alongside the existing SK C2 mismatch
case, changing the public input at index 6 + H to an incorrect ESM C2 chunk key
hash while keeping the remaining fixture, proof, verification call, and
VkHashMismatch assertion consistent.

In `@packages/interfold-contracts/test/E3Lifecycle/E3Integration.spec.ts`:
- Around line 214-228: Update finalizeAndPublishCommittee so it leaves the
committee in the same fully published state as setupAndPublishCommittee: after
publishCommittee, also publish the corresponding public key using
publishCommitteePublicKey with e3Id 0 and the same publicKey, or delegate to the
shared helper while preserving the existing setup flow.

In `@packages/interfold-contracts/test/Registry/BondingRegistry.spec.ts`:
- Around line 115-122: Ensure impersonated accounts are always released: in
packages/interfold-contracts/test/Registry/BondingRegistry.spec.ts#L115-L122,
make impersonateSlashingManager execute a caller callback and stop impersonating
in a finally block, removing manual cleanup at its call sites; in
packages/interfold-contracts/test/Slashing/CommitteeExpulsion.spec.ts#L189-L206,
wrap the expelCommitteeMember loop in try/finally or reuse the scoped helper so
cleanup occurs on failures.

In `@packages/interfold-contracts/test/Slashing/CommitteeExpulsion.spec.ts`:
- Line 36: Move INSUFFICIENT_COMMITTEE_MEMBERS from CommitteeExpulsion.spec.ts
into the shared constants fixture, then import and reuse it alongside
COMMITTEE_THRESHOLDS_ONCHAIN. Remove the local literal so all specs asserting
this failure reason use the single shared definition.

In `@packages/interfold-contracts/test/Slashing/SlashingManager.spec.ts`:
- Around line 285-299: Extract the inline deployment in the “requires policies
to be refreshed after an asset rotation” test into a dedicated fixture function
such as setupWiredSlashingManager, preserving its deployInterfoldSystem options
and returned system state. Update the test to obtain that state through
loadFixture(setupWiredSlashingManager), while retaining the existing signer
setup as appropriate.

In `@scripts/build-circuits.ts`:
- Around line 311-313: Extract the shared preset/committee derivation used by
patchUtilsTs and writeActiveCryptoConfig into one helper, then reuse it in both
paths. Update writeActiveCryptoConfig to return early when skipUtilsPatch is
enabled or its target file is absent, matching patchUtilsTs before calling
writeFileSync.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

Comment thread agent/flow-trace/04_DKG_AND_COMPUTATION.md Outdated
Comment thread agent/flow-trace/05_FAILURE_REFUND_SLASHING.md
Comment thread crates/events/src/interfold_event/compute_request/zk.rs Outdated
Comment thread crates/multithread/src/multithread.rs Outdated
Comment thread crates/support/README.md Outdated
Comment thread packages/interfold-contracts/scripts/benchmarkGasFromRaw.ts
Comment thread packages/interfold-contracts/test/E3Lifecycle/E3Integration.spec.ts Outdated
Comment thread scripts/check-committee.sh
Comment thread templates/default/contracts/MyProgram.sol
@0xjei

0xjei commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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 `@crates/zk-prover/src/commitment_links/c2_to_c4/mod.rs`:
- Around line 230-242: Update the target-length bounds check near c4_row_end to
compute c4_row_end plus FIELD_BYTE_LEN with checked_add, returning false if that
addition overflows; use the checked result for the target_public_signals length
comparison.

In `@crates/zk-prover/src/node_proof_aggregation/effects.rs`:
- Around line 170-178: Update the failure handling around the E3Failed
publication to publish the terminal event before removing self.states[e3_id].
Handle the bus.publish result explicitly: only delete the aggregation state
after successful publication, and retain or queue it when publication fails so
the failure can be retried.
🪄 Autofix

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 Plus

Run ID: 8a13a13e-7677-4f4b-888f-6bcb2e5797d7

📥 Commits

Reviewing files that changed from the base of the PR and between 05e0860 and 8b39ce1.

📒 Files selected for processing (20)
  • agent/flow-trace/04_DKG_AND_COMPUTATION.md
  • agent/flow-trace/05_FAILURE_REFUND_SLASHING.md
  • crates/events/src/interfold_event/compute_request/zk.rs
  • crates/multithread/src/multithread.rs
  • crates/support/README.md
  • crates/zk-prover/src/circuits/dkg/share_computation.rs
  • crates/zk-prover/src/commitment_links/c1_to_c2.rs
  • crates/zk-prover/src/commitment_links/c2_to_c4/mod.rs
  • crates/zk-prover/src/commitment_links/c2_to_c4/tests.rs
  • crates/zk-prover/src/commitment_links/mod.rs
  • crates/zk-prover/src/node_proof_aggregation/effects.rs
  • crates/zk-prover/src/node_proof_aggregation/workflow.rs
  • packages/interfold-contracts/contracts/E3RefundManager.sol
  • packages/interfold-contracts/contracts/interfaces/ICiphernodeRegistry.sol
  • packages/interfold-contracts/contracts/lib/InterfoldLifecycle.sol
  • packages/interfold-contracts/contracts/verifiers/bfv/Risc0BfvCiphertextVerifier.sol
  • packages/interfold-contracts/scripts/benchmarkGasFromRaw.ts
  • packages/interfold-contracts/test/E3Lifecycle/E3Integration.spec.ts
  • packages/interfold-contracts/test/Risc0BfvCiphertextVerifier.spec.ts
  • scripts/check-committee.sh
🚧 Files skipped from review as they are similar to previous changes (10)
  • crates/support/README.md
  • scripts/check-committee.sh
  • crates/zk-prover/src/commitment_links/c1_to_c2.rs
  • crates/multithread/src/multithread.rs
  • packages/interfold-contracts/test/Risc0BfvCiphertextVerifier.spec.ts
  • packages/interfold-contracts/scripts/benchmarkGasFromRaw.ts
  • packages/interfold-contracts/contracts/verifiers/bfv/Risc0BfvCiphertextVerifier.sol
  • packages/interfold-contracts/contracts/interfaces/ICiphernodeRegistry.sol
  • packages/interfold-contracts/contracts/lib/InterfoldLifecycle.sol
  • agent/flow-trace/05_FAILURE_REFUND_SLASHING.md

Comment on lines +230 to 242
let Some(c4_row_start) = src_idx
.checked_mul(l)
.and_then(|offset| offset.checked_mul(FIELD_BYTE_LEN))
else {
return false;
};
let Some(c4_row_len) = l.checked_mul(FIELD_BYTE_LEN) else {
return false;
};
let Some(c4_row_end) = c4_row_start.checked_add(c4_row_len) else {
return false;
};
if target_public_signals.len() < c4_row_end + FIELD_BYTE_LEN {

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Complete the checked target-length calculation.

Line 242 uses c4_row_end + FIELD_BYTE_LEN without checked addition. For a large src_party_id, this can overflow before the bounds check. Debug builds panic. Release builds can wrap and later slice at an invalid offset. Use checked_add and return false when it fails.

Proposed fix
     let Some(c4_row_end) = c4_row_start.checked_add(c4_row_len) else {
         return false;
     };
-    if target_public_signals.len() < c4_row_end + FIELD_BYTE_LEN {
+    let Some(c4_required_len) = c4_row_end.checked_add(FIELD_BYTE_LEN) else {
+        return false;
+    };
+    if target_public_signals.len() < c4_required_len {
         return false;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let Some(c4_row_start) = src_idx
.checked_mul(l)
.and_then(|offset| offset.checked_mul(FIELD_BYTE_LEN))
else {
return false;
};
let Some(c4_row_len) = l.checked_mul(FIELD_BYTE_LEN) else {
return false;
};
let Some(c4_row_end) = c4_row_start.checked_add(c4_row_len) else {
return false;
};
if target_public_signals.len() < c4_row_end + FIELD_BYTE_LEN {
let Some(c4_row_start) = src_idx
.checked_mul(l)
.and_then(|offset| offset.checked_mul(FIELD_BYTE_LEN))
else {
return false;
};
let Some(c4_row_len) = l.checked_mul(FIELD_BYTE_LEN) else {
return false;
};
let Some(c4_row_end) = c4_row_start.checked_add(c4_row_len) else {
return false;
};
let Some(c4_required_len) = c4_row_end.checked_add(FIELD_BYTE_LEN) else {
return false;
};
if target_public_signals.len() < c4_required_len {
return false;
}
🤖 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-prover/src/commitment_links/c2_to_c4/mod.rs` around lines 230 -
242, Update the target-length bounds check near c4_row_end to compute c4_row_end
plus FIELD_BYTE_LEN with checked_add, returning false if that addition
overflows; use the checked result for the target_public_signals length
comparison.

Comment on lines +170 to +178
self.states.remove(e3_id);
let _ = self.bus.publish(
E3Failed {
e3_id: e3_id.clone(),
failed_at_stage: E3Stage::CommitteeFinalized,
reason: FailureReason::DKGInvalidShares,
},
ec,
);

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not delete aggregation state before terminal failure publication succeeds.

The code removes self.states[e3_id] and discards the E3Failed publication result. If bus.publish fails, the E3 has no terminal event and no retained state for retry. Publish the failure first. If publication fails, retain or queue the state for a retry.

🤖 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-prover/src/node_proof_aggregation/effects.rs` around lines 170 -
178, Update the failure handling around the E3Failed publication to publish the
terminal event before removing self.states[e3_id]. Handle the bus.publish result
explicitly: only delete the aggregation state after successful publication, and
retain or queue it when publication fails so the failure can be retried.

Base automatically changed from lbfv to main August 7, 2026 16:37
@0xjei
0xjei changed the base branch from main to lbfv August 8, 2026 09:00
0xjei added 24 commits August 8, 2026 11:14
…ommittee/canonical shapes

Audit follow-up across C2/C3/C4/C5/C7 and downstream construction sites:

- F1/F2/F3: thread chunk_size (DEFAULT_C2_CHUNK_SIZE) through C2/C3/C4
  circuit data and sample/computation; C3/C4 also emit
  SHARE_COMPUTATION_CHUNK_SIZE / SHARE_COMPUTATION_N_CHUNKS from their
  configs, requiring a recompile for non-512 chunk sizes. Ranges verified
  against canonical committee (CiphernodesCommittee).
- F7: validate C4 recipient_party_id against canonical.n in DKG,
  matching the C3 row/coeff fixes from the prior commit.
- F8: drop DKG n/l duplication from preset metadata; pk /
  share_encryption / share_decryption generate_configs now take &Configs
  holding n/l/chunk_size; apply threshold_l correction to C3.
- C7 (threshold decrypted_shares_aggregation): enforce exact T+1 shares,
  reconstructing_parties length/bounds/unique (BTreeSet), and
  non-zero-coeff drop check vs max_msg_non_zero_coeffs.
- C5 pk_aggregation: assert canonical.h instead of raw h.
- C3 mod_idx/row_index now bound by threshold moduli len, not DKG moduli
  (insecure-512 DKG L=1, threshold L=2).
- Wire new chunk_size/committee fields into multithread (3 sites) and
  node_fold_witness test data; update e3-events input layout test to the
  4 C3 public inputs.
- Add secure-preset codegen test; delete obsolete CHUNKED_C2_PLAN.
- Flow-trace: document chunk-size loading across C2/C3/C4, N/L sourcing,
  512 default, and recompile-on-non-512 warning.
Add per-step wall-time tracking to the chunked DKG share-computation prove
path (chunks, batches, finalize), mirroring the existing NodeDkgFold timing
pattern, and emit them in the multithread report as ZkShareComputation/*.

Update the benchmark report generator to put fold and share-computation
sub-steps under a single "Operation sub-steps" section and relabel the
isolated C2 rows to make clear they measure a single chunk circuit.
Regenerate results_insecure_minimum report; drop obsolete results and the
old monolithic C2 circuits in favour of the chunked build.
… benches

Parallelize the independent chunk proofs (16 for secure degree) with rayon
par_iter and the per-batch aggregation proofs (4) with par_chunks; both are
IndexedParallelIterators so collect preserves the ordering the finalizer
needs. Emit per-step prove timings (chunks, batches, finalize) so the
benchmark report can attribute the C2 regression to the aggregation tree.

Also switch the active committee to micro (benchmark run config) and add
the regenerated secure-micro results.
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.

1 participant