Skip to content

feat: add proof of correct aggregration#1374

Merged
ctrlc03 merged 2 commits into
mainfrom
feat/t4-integration
Mar 3, 2026
Merged

feat: add proof of correct aggregration#1374
ctrlc03 merged 2 commits into
mainfrom
feat/t4-integration

Conversation

@ctrlc03

@ctrlc03 ctrlc03 commented Feb 28, 2026

Copy link
Copy Markdown
Collaborator

fix #1368

Summary by CodeRabbit

  • New Features

    • Enhanced public-key aggregation with multi-step verification, optional C5 (PK-aggregation) proof, and events now carrying an optional aggregation proof.
    • ZK prover backend wired in and multithreaded worker support added to generate PK-aggregation proofs end-to-end.
  • Chores

    • Added BFV parameter dependency and propagated a preset through aggregation setup and builder wiring.
    • Updated integration tests, deployment templates, deployed-contract records, shortened an example wait, and removed a debug log.

@vercel

vercel Bot commented Feb 28, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
crisp Ready Ready Preview, Comment Mar 3, 2026 3:25pm
enclave-docs Ready Ready Preview, Comment Mar 3, 2026 3:25pm

Request Review

@coderabbitai

coderabbitai Bot commented Feb 28, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a multi-stage public-key aggregation flow: collects optional C1 proofs, verifies shares (C1), performs threshold aggregation, generates a C5 PK-aggregation ZK proof, and threads a BFV preset and ZK backend through the aggregator, multithread prover, events, builder, tests, and configs.

Changes

Cohort / File(s) Summary
Dependencies
crates/aggregator/Cargo.toml
Added workspace dependency e3-fhe-params to provide BfvPreset.
Public Key Aggregator
crates/aggregator/src/publickey_aggregator.rs
Reworked state machine: added VerifyingC1 and GeneratingC5Proof; Collecting stores threshold_m and c1_proofs; init now takes threshold_m; added C1 verification and C5 proof handlers; compute responses routed to new handlers; BFV preset threaded into params.
Aggregator Extension
crates/aggregator/src/ext.rs
PublicKeyAggregatorExtension::create now accepts BfvPreset and stores params_preset; hydration and on-event flows forward the preset; sync state includes threshold_m.
Builder & Entrypoint
crates/ciphernode-builder/src/ciphernode_builder.rs, crates/entrypoint/src/start/aggregator_start.rs
Ensure multithread worker exists before extension creation; create PublicKeyAggregatorExtension with a BFV preset (default preset used); wire and ensure ZK backend via .with_zkproof(backend).
Events / Types
crates/events/src/enclave_event/compute_request/zk.rs, crates/events/src/enclave_event/compute_request/mod.rs, crates/events/src/enclave_event/publickey_aggregated.rs
Added ZkRequest::PkAggregation / ZkResponse::PkAggregation and PkAggregationProofRequest/PkAggregationProofResponse (includes params_preset and committee params); ComputeRequest stringification extended; PublicKeyAggregated gains optional pk_aggregation_proof.
Multithread ZK Handler
crates/multithread/src/multithread.rs
Added handle_pk_aggregation_proof: builds BFV params/CRP, deserializes keyshares and aggregated PK, prepares circuit data, runs PkAggregation circuit, returns PkAggregationProofResponse; wired ZkRequest::PkAggregation to handler.
Tests & Integration
crates/tests/tests/integration.rs
Provisioned PK-aggregation circuit artifacts in test setup; thread ZkBackend through test setup and ciphernode creation; updated assertions and expected compute events; PublicKeyAggregated test assertions include pk_aggregation_proof: None where applicable.
Config, Examples, Misc
templates/default/enclave.config.yaml, templates/default/server/index.ts, examples/CRISP/test/crisp.spec.ts, templates/default/deployed_contracts.json
Updated deploy_block numbers and deployed contract addresses/records; removed a debug log emitting sessions; reduced CRISP MetaMask wait from 45s to 25s; deployed_contracts.json updated with renames/address changes and initData updates.

Sequence Diagram(s)

sequenceDiagram
    participant Pka as PublicKeyAggregator
    participant Bus as Extension/Bus
    participant Zk as Multithread ZK Prover
    participant State as Aggregator State

    rect rgba(100,150,255,0.5)
    Note over Pka,State: Collecting phase
    Pka->>Pka: add_keyshare(keyshare, c1_proof?)
    Pka->>State: accumulate keyshares + c1_proofs
    Note over Pka: on threshold_n reached -> trigger C1 verification
    end

    rect rgba(150,100,255,0.5)
    Note over Pka,Zk: C1 verification
    Pka->>Bus: dispatch_c1_verification(request with proofs)
    Bus->>Zk: VerifyShareProofs request
    Zk-->>Bus: VerifyShareProofs response (honest/dishonest)
    Bus->>Pka: deliver verification response
    Pka->>Pka: handle_c1_verification_response()
    Pka->>State: require >= threshold_m honest -> aggregate keyshares
    end

    rect rgba(255,150,100,0.5)
    Note over Pka,Zk: C5 PK-aggregation proof
    Pka->>Bus: dispatch_c5_proof(pk, keyshare_bytes, params_preset)
    Bus->>Zk: PkAggregation request
    Zk->>Zk: Generate C5 proof (circuit)
    Zk-->>Bus: PkAggregation response (proof)
    Bus->>Pka: deliver proof
    Pka->>Pka: handle_c5_proof_response() -> Publish PublicKeyAggregated
    Pka->>State: transition to Complete
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

ciphernode

Suggested reviewers

  • hmzakhalid
  • ryardley
  • cedoor

Poem

🐰 I hopped through proofs both C1 and C5,
I gathered shares to keep keys alive,
I chased the lies, then stitched the sum,
A C5 sparkle — now the proof is done! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Most changes align with T4 integration objectives. However, the deploy block updates in enclave.config.yaml, contract address changes in deployed_contracts.json, test timing adjustment in crisp.spec.ts, and debug logging removal appear unrelated to T4/C5 proof integration. Separate out-of-scope changes (deploy blocks, contract deployments, test timing, debug logging) into a separate PR to keep T4 integration focused and reviewable.
Docstring Coverage ⚠️ Warning Docstring coverage is 48.28% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'feat: add proof of correct aggregration' describes a key feature addition but contains a typo ('aggregration' instead of 'aggregation') and is somewhat vague. Correct the typo to 'feat: add proof of correct aggregation' and consider clarifying the specific proof type (C5 proof) or aggregation context for better clarity.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR implements comprehensive C5 proof support including new BFV preset parameter support, multi-stage verification flow, circuit integration, and ZK backend wiring as required by issue #1368.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/t4-integration

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
templates/default/enclave.config.yaml (1)

7-19: Consider generating deploy_block values to avoid config drift.

Lines 7, 10, 16, and 19 are hand-maintained and can silently drift when deployment order changes. A generated template (from deployment artifacts) would be safer long-term.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@templates/default/enclave.config.yaml` around lines 7 - 19, The static
deploy_block values (deploy_block keys under ciphernode_registry,
bonding_registry, fee_token, e3_program) can drift; change the template
generation so deploy_block is populated from the deployment artifacts instead of
being hand-edited: update the template generator (or add a small script run
during build) to read the deployment manifest/ABI output (the artifacts that
record addresses and block numbers) and inject the correct deploy_block for each
registry/key into the enclave.config.yaml template (ensuring it fills
deploy_block for ciphernode_registry, bonding_registry, fee_token, e3_program),
and remove/replace hard-coded numbers in the template so the file is produced
from the artifact data automatically.
crates/multithread/src/multithread.rs (1)

262-325: Add upfront structural validation for PK aggregation inputs.

Before deserialization/proving, validate n/h/threshold relationships and keyshare count consistency to fail fast with clear errors.

Proposed hardening
 fn handle_pk_aggregation_proof(
     prover: &ZkProver,
     req: PkAggregationProofRequest,
     request: ComputeRequest,
 ) -> Result<ComputeResponse, ComputeRequestError> {
+    if req.committee_n == 0
+        || req.committee_h == 0
+        || req.committee_h > req.committee_n
+        || req.committee_threshold == 0
+        || req.committee_threshold > req.committee_h
+    {
+        return Err(make_zk_error(
+            &request,
+            format!(
+                "Invalid committee values: n={}, h={}, threshold={}",
+                req.committee_n, req.committee_h, req.committee_threshold
+            ),
+        ));
+    }
+    if req.keyshare_bytes.len() != req.committee_h {
+        return Err(make_zk_error(
+            &request,
+            format!(
+                "Expected {} keyshares, got {}",
+                req.committee_h,
+                req.keyshare_bytes.len()
+            ),
+        ));
+    }
+
     // 1. Build threshold BFV parameters from preset
     let (threshold_params, _dkg_params) = build_pair_for_preset(req.params_preset.clone())
         .map_err(|e| make_zk_error(&request, format!("build_pair_for_preset: {}", e)))?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/multithread/src/multithread.rs` around lines 262 - 325, Add upfront
validation in handle_pk_aggregation_proof before deserializing keyshares: verify
req.committee_threshold > 0 and that req.committee_threshold <= req.committee_h
&& req.committee_h <= req.committee_n, and that req.keyshare_bytes.len() ==
req.committee_n (or the expected count per your protocol) — if any check fails
return an early error (use make_zk_error(&request, format!(...)) or
ComputeRequestError consistent with existing error handling) so the function
fails fast with a clear message; place this block just before the keyshare
deserialization loop that references pk0_shares and use the same request/context
(request, req) for error construction.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/aggregator/src/publickey_aggregator.rs`:
- Around line 151-154: The code currently derives sender_party_id from the
iterator index (idx as u64) which is wrong; instead use the actual party
identifier embedded in the proof or upstream tuple so proofs remain bound to
their true sender. Locate the closure that builds PartyProofsToVerify (the
filter_map producing PartyProofsToVerify { sender_party_id: idx as u64,
signed_proofs: vec![proof.clone()] }) and replace the idx-based assignment with
the real party id from the proof (e.g., proof.sender_party_id or proof.party_id)
or from the surrounding upstream data structure that carries the party id;
ensure PartyProofsToVerify.sender_party_id is populated from that authoritative
field and update any upstream mapping to pass the party id through if necessary.
- Around line 148-163: The current construction of party_proofs using
c1_proofs.iter().enumerate().filter_map(...) silently drops None entries so
keyshares with missing C1 proofs bypass verification; update the logic in the
c1_proofs -> party_proofs conversion to explicitly fail when any proof_opt is
None (for example, iterate with enumerate and return an Err if
proof_opt.is_none()), ensuring missing proofs cause an early Err instead of
being omitted, and keep references to PartyProofsToVerify and honest_keyshares
intact so only fully-proven parties proceed.
- Around line 278-281: The code currently hardcodes committee_threshold: 0 when
constructing the statement (the fields committee_n, committee_h,
committee_threshold), which produces an invalid C5 input; instead populate
committee_threshold with the actual threshold value resolved from your
preset/handler (e.g., use preset.committee_threshold or the resolved variable
from the handler, or compute it via resolve_threshold(committee_h.len()) and
pass that variable into the struct). Replace committee_threshold: 0 with the
resolved threshold variable so the downstream proof handler receives the correct
value.
- Around line 339-347: Ensure this actor only processes ComputeResponse events
for its own e3_id by adding an e3_id guard before matching on response.response:
compare response.e3_id (or the event’s e3_id field) to this aggregator’s
self.e3_id (or the ec/econtext e3 id if that's the authoritative source) and
return Ok(()) early if they differ; then proceed to match ComputeResponseKind
and call handle_c1_verification_response(...) and handle_c5_proof_response(...)
as before.

In `@crates/ciphernode-builder/src/ciphernode_builder.rs`:
- Around line 466-475: The code currently unconditionally enables
PublicKeyAggregatorExtension with the insecure BfvPreset::InsecureThreshold512
and does not check for a zk backend; update the builder to fail fast or require
configuration: ensure that before calling PublicKeyAggregatorExtension::create
(and e3_builder.with) you (1) check that a zk_backend is present/configured and
return an error if missing, and (2) disallow using
BfvPreset::InsecureThreshold512 in non-test contexts by making the preset
configurable via the builder API (or validate a provided preset) and returning
an explicit error if the insecure preset is selected; keep references to
ensure_multithread(&bus), PublicKeyAggregatorExtension::create,
BfvPreset::InsecureThreshold512, and e3_builder.with to locate and change the
logic.

In `@crates/events/src/enclave_event/publickey_aggregated.rs`:
- Around line 22-23: The Display implementation currently formats the
pk_aggregation_proof field with a debug formatter and will emit the entire proof
payload; update the Display (impl Display for the enclosing type) to avoid
printing the full Proof by replacing the {:?} usage for pk_aggregation_proof
with a concise/redacted representation (e.g., show "present"/"none", a short
hex/hash or length, or call a summary method on Proof) so logs no longer contain
the full proof data while keeping clear indication that the proof exists.

---

Nitpick comments:
In `@crates/multithread/src/multithread.rs`:
- Around line 262-325: Add upfront validation in handle_pk_aggregation_proof
before deserializing keyshares: verify req.committee_threshold > 0 and that
req.committee_threshold <= req.committee_h && req.committee_h <=
req.committee_n, and that req.keyshare_bytes.len() == req.committee_n (or the
expected count per your protocol) — if any check fails return an early error
(use make_zk_error(&request, format!(...)) or ComputeRequestError consistent
with existing error handling) so the function fails fast with a clear message;
place this block just before the keyshare deserialization loop that references
pk0_shares and use the same request/context (request, req) for error
construction.

In `@templates/default/enclave.config.yaml`:
- Around line 7-19: The static deploy_block values (deploy_block keys under
ciphernode_registry, bonding_registry, fee_token, e3_program) can drift; change
the template generation so deploy_block is populated from the deployment
artifacts instead of being hand-edited: update the template generator (or add a
small script run during build) to read the deployment manifest/ABI output (the
artifacts that record addresses and block numbers) and inject the correct
deploy_block for each registry/key into the enclave.config.yaml template
(ensuring it fills deploy_block for ciphernode_registry, bonding_registry,
fee_token, e3_program), and remove/replace hard-coded numbers in the template so
the file is produced from the artifact data automatically.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 739b53a and 0e1f125.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • crates/aggregator/Cargo.toml
  • crates/aggregator/src/ext.rs
  • crates/aggregator/src/publickey_aggregator.rs
  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/entrypoint/src/start/aggregator_start.rs
  • crates/events/src/enclave_event/compute_request/mod.rs
  • crates/events/src/enclave_event/compute_request/zk.rs
  • crates/events/src/enclave_event/publickey_aggregated.rs
  • crates/multithread/src/multithread.rs
  • crates/tests/tests/integration.rs
  • examples/CRISP/test/crisp.spec.ts
  • templates/default/enclave.config.yaml
  • templates/default/server/index.ts
💤 Files with no reviewable changes (1)
  • templates/default/server/index.ts

Comment thread crates/aggregator/src/publickey_aggregator.rs Outdated
Comment thread crates/aggregator/src/publickey_aggregator.rs Outdated
Comment thread crates/aggregator/src/publickey_aggregator.rs
Comment thread crates/aggregator/src/publickey_aggregator.rs
Comment thread crates/ciphernode-builder/src/ciphernode_builder.rs
Comment thread crates/events/src/enclave_event/publickey_aggregated.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
crates/aggregator/src/publickey_aggregator.rs (1)

427-447: Snapshot-then-mutate pattern for c1_proofs.

The code snapshots c1_proofs before mutation (lines 428-435) to have a complete list when dispatching verification (line 445). This works but is slightly fragile since it relies on the snapshot including the newly added proof.

Consider: The snapshot adds the new proof manually (line 431), which mirrors what add_keyshare does. This duplication is functional but could diverge if logic changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/aggregator/src/publickey_aggregator.rs` around lines 427 - 447, The
current code creates c1_proofs_snapshot by cloning and manually appending
c1_proof before calling add_keyshare, which duplicates logic and may diverge;
instead, call add_keyshare(pubkey, node, c1_proof, &ec) first and then read the
updated proofs from self.state (match PublicKeyAggregatorState::Collecting {
c1_proofs, .. } or VerifyingC1 { .. } as appropriate) to obtain the
authoritative list to pass into dispatch_c1_verification(&c1_proofs, ec), or
modify add_keyshare to return the updated c1_proofs (or a flag/Option indicating
transition) and use that return value to drive dispatch_c1_verification.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/aggregator/src/publickey_aggregator.rs`:
- Around line 427-447: The current code creates c1_proofs_snapshot by cloning
and manually appending c1_proof before calling add_keyshare, which duplicates
logic and may diverge; instead, call add_keyshare(pubkey, node, c1_proof, &ec)
first and then read the updated proofs from self.state (match
PublicKeyAggregatorState::Collecting { c1_proofs, .. } or VerifyingC1 { .. } as
appropriate) to obtain the authoritative list to pass into
dispatch_c1_verification(&c1_proofs, ec), or modify add_keyshare to return the
updated c1_proofs (or a flag/Option indicating transition) and use that return
value to drive dispatch_c1_verification.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e1f125 and 2837456.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • crates/aggregator/Cargo.toml
  • crates/aggregator/src/ext.rs
  • crates/aggregator/src/publickey_aggregator.rs
  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/entrypoint/src/start/aggregator_start.rs
  • crates/events/src/enclave_event/compute_request/mod.rs
  • crates/events/src/enclave_event/compute_request/zk.rs
  • crates/events/src/enclave_event/publickey_aggregated.rs
  • crates/multithread/src/multithread.rs
  • crates/tests/tests/integration.rs
  • examples/CRISP/test/crisp.spec.ts
  • templates/default/enclave.config.yaml
  • templates/default/server/index.ts
💤 Files with no reviewable changes (1)
  • templates/default/server/index.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/events/src/enclave_event/compute_request/mod.rs
  • examples/CRISP/test/crisp.spec.ts
  • crates/aggregator/Cargo.toml
  • crates/entrypoint/src/start/aggregator_start.rs

@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

Caution

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

⚠️ Outside diff range comments (1)
crates/tests/tests/integration.rs (1)

1270-1276: ⚠️ Potential issue | 🟡 Minor

These assertions hardcode pk_aggregation_proof: None, which is likely stale for C5-enabled flow.

At Line 1275 and Line 1318, prefer asserting field-wise with is_some() (or pattern matching) instead of full-struct equality with None.

Also applies to: 1313-1319

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/tests/tests/integration.rs` around lines 1270 - 1276, The test
currently constructs/compares a PublicKeyAggregated instance with
pk_aggregation_proof: None which is stale for C5-enabled flows; update the
assertions that reference PublicKeyAggregated and pk_aggregation_proof (e.g.,
the instances built around test_pubkey/public_key_hash/E3id and nodes:
OrderedSet::from(eth_addrs.clone())) to check the pk_aggregation_proof field
presence instead of exact equality—use field-wise assertions (e.g.,
public.pk_aggregation_proof.is_some() or pattern-match PublicKeyAggregated {
pk_aggregation_proof: Some(_), .. } ) and keep other fields asserted as before
so tests no longer fail when a proof is present.
♻️ Duplicate comments (5)
crates/events/src/enclave_event/publickey_aggregated.rs (1)

26-29: ⚠️ Potential issue | 🟠 Major

Avoid dumping full proof data in Display.

At Line 28, {:?} will include pk_aggregation_proof content, which can heavily bloat logs.

Proposed fix
 impl Display for PublicKeyAggregated {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{:?}", self)
+        write!(
+            f,
+            "PublicKeyAggregated {{ e3_id: {:?}, nodes: {}, pk_aggregation_proof: {} }}",
+            self.e3_id,
+            self.nodes.len(),
+            self.pk_aggregation_proof.is_some()
+        )
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/events/src/enclave_event/publickey_aggregated.rs` around lines 26 -
29, The Display impl for PublicKeyAggregated currently uses write!(f, "{:?}",
self) which dumps the full struct (including pk_aggregation_proof) into logs;
change the fmt method on the Display impl for PublicKeyAggregated to avoid
printing pk_aggregation_proof directly — instead format only concise fields
(e.g., epoch_index, public_key_aggregated identifier or short hex/prefix, and/or
the proof length) and redact or shorten pk_aggregation_proof to a fixed-size
summary so logs are not bloated.
crates/aggregator/src/publickey_aggregator.rs (3)

303-310: ⚠️ Potential issue | 🔴 Critical

Do not send committee_threshold: 0 into C5 proof generation.

At Line 309, this produces an invalid/weak committee statement for PkAggregationProofRequest.

Proposed fix
-                committee_threshold: 0, // Will be resolved from preset in handler
+                committee_threshold: threshold_m,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/aggregator/src/publickey_aggregator.rs` around lines 303 - 310, The
PkAggregationProofRequest is being constructed with committee_threshold: 0 which
yields an invalid/weak committee statement; instead compute the real threshold
from the params preset before building the request and pass that value.
Concretely, read the committee threshold from self.params_preset (e.g., call the
preset method that returns the committee threshold for the given committee size
or preset) into a local variable (e.g., committee_threshold) and replace
committee_threshold: 0 in the ZkRequest::PkAggregation(PkAggregationProofRequest
{ ... }) construction with that computed value so the request carries the
correct threshold.

363-376: ⚠️ Potential issue | 🔴 Critical

Scope ComputeResponse handling to this actor’s e3_id.

Without an e3_id guard before Line 368 matching, this actor can consume responses from other flows and transition incorrectly.

Proposed fix
     fn handle_compute_response(
         &mut self,
         response: ComputeResponse,
         ec: EventContext<Sequenced>,
     ) -> Result<()> {
+        if response.e3_id != self.e3_id {
+            return Ok(());
+        }
+
         match response.response {
             ComputeResponseKind::Zk(ZkResponse::VerifyShareProofs(data)) => {
                 self.handle_c1_verification_response(data, ec)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/aggregator/src/publickey_aggregator.rs` around lines 363 - 376, In
handle_compute_response, scope incoming ComputeResponse to this actor's e3_id by
returning early when the response's e3_id (or flow identifier field on
ComputeResponse) does not equal self.e3_id; add an explicit guard at the start
of handle_compute_response (before matching response.response) that compares
response.e3_id (or the appropriate flow id field on ComputeResponse) to
self.e3_id and returns Ok(()) on mismatch so only responses for this actor
proceed to handle_c1_verification_response and handle_c5_proof_response.

154-160: ⚠️ Potential issue | 🔴 Critical

Bind C1 verification to authoritative party IDs, not collection index.

At Line 158, sender_party_id: idx as u64 ties proof identity to arrival order, which can mis-associate proofs and parties when ordering changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/aggregator/src/publickey_aggregator.rs` around lines 154 - 160, The
loop over c1_proofs currently uses the enumeration index for sender_party_id
(sender_party_id: idx as u64), which binds identity to arrival order; instead
extract and use the authoritative party ID from the proof itself (e.g.,
proof.sender_party_id or the proof's canonical identifier) when constructing
PartyProofsToVerify so proofs remain associated with the correct party
regardless of ordering; update the loop that builds party_proofs (the
c1_proofs.iter().enumerate() block and PartyProofsToVerify construction) to read
the ID from the proof object rather than using idx.
crates/ciphernode-builder/src/ciphernode_builder.rs (1)

464-475: ⚠️ Potential issue | 🟠 Major

Fail fast if pubkey_agg is enabled without a ZK backend.

This branch enables PublicKeyAggregatorExtension, but without zk_backend the C1/C5 proof flow cannot complete reliably.

Proposed fix
         if self.pubkey_agg {
             info!("Setting up PublicKeyAggregationExtension");
             // Ensure multithread worker is available for C1 verification and C5 proof generation
             let _ = self.ensure_multithread(&bus);
+            if self.zk_backend.is_none() {
+                return Err(anyhow::anyhow!(
+                    "ZK backend is required when public key aggregation is enabled"
+                ));
+            }
             // TODO: Make BfvPreset configurable via builder method.
             // Currently hardcoded to InsecureThreshold512 for C5 proof generation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/ciphernode-builder/src/ciphernode_builder.rs` around lines 464 - 475,
If self.pubkey_agg is true, fail fast when there is no ZK backend configured:
before calling PublicKeyAggregatorExtension::create (and before
ensure_multithread / e3_builder.with), check the builder's zk_backend
(self.zk_backend) and return an Err (or propagate an appropriate construction
error) with a clear message that pubkey_agg requires a ZK backend; do this in
the same branch where pubkey_agg is handled so the builder doesn't proceed to
create the extension without zk_backend configured.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/multithread/src/multithread.rs`:
- Around line 264-306: In handle_pk_aggregation_proof validate committee
invariants and keyshare count before any deserialization or circuit
construction: check req.committee_n, req.committee_h, req.committee_threshold
satisfy expected relationships (e.g., 0 < h <= n, threshold <= n and > 0,
threshold <= h as applicable) and ensure req.keyshare_bytes.len() ==
req.committee_n (or the required number of shares) and return an early Err via
make_zk_error(&request, "...") on violation; perform these checks at the top of
handle_pk_aggregation_proof before build_pair_for_preset/deserialize loops so
PkAggregationCircuitData, pk0_shares population, and PublicKey::from_bytes are
only executed for well-formed inputs.

In `@examples/CRISP/test/crisp.spec.ts`:
- Line 145: The WAIT constant is computed from process.env.E3_DURATION without
validation, which yields NaN if the env var is absent; update the initialization
of WAIT (the const named WAIT) to first guard E3_DURATION: check that
process.env.E3_DURATION is defined and a valid integer (e.g., use
Number.isFinite(Number.parseInt(...)) or a regex) and either provide a safe
default (like 0) or throw a clear error, then compute WAIT using the
validated/parsed value so parseInt never receives undefined and WAIT cannot
become NaN.

---

Outside diff comments:
In `@crates/tests/tests/integration.rs`:
- Around line 1270-1276: The test currently constructs/compares a
PublicKeyAggregated instance with pk_aggregation_proof: None which is stale for
C5-enabled flows; update the assertions that reference PublicKeyAggregated and
pk_aggregation_proof (e.g., the instances built around
test_pubkey/public_key_hash/E3id and nodes: OrderedSet::from(eth_addrs.clone()))
to check the pk_aggregation_proof field presence instead of exact equality—use
field-wise assertions (e.g., public.pk_aggregation_proof.is_some() or
pattern-match PublicKeyAggregated { pk_aggregation_proof: Some(_), .. } ) and
keep other fields asserted as before so tests no longer fail when a proof is
present.

---

Duplicate comments:
In `@crates/aggregator/src/publickey_aggregator.rs`:
- Around line 303-310: The PkAggregationProofRequest is being constructed with
committee_threshold: 0 which yields an invalid/weak committee statement; instead
compute the real threshold from the params preset before building the request
and pass that value. Concretely, read the committee threshold from
self.params_preset (e.g., call the preset method that returns the committee
threshold for the given committee size or preset) into a local variable (e.g.,
committee_threshold) and replace committee_threshold: 0 in the
ZkRequest::PkAggregation(PkAggregationProofRequest { ... }) construction with
that computed value so the request carries the correct threshold.
- Around line 363-376: In handle_compute_response, scope incoming
ComputeResponse to this actor's e3_id by returning early when the response's
e3_id (or flow identifier field on ComputeResponse) does not equal self.e3_id;
add an explicit guard at the start of handle_compute_response (before matching
response.response) that compares response.e3_id (or the appropriate flow id
field on ComputeResponse) to self.e3_id and returns Ok(()) on mismatch so only
responses for this actor proceed to handle_c1_verification_response and
handle_c5_proof_response.
- Around line 154-160: The loop over c1_proofs currently uses the enumeration
index for sender_party_id (sender_party_id: idx as u64), which binds identity to
arrival order; instead extract and use the authoritative party ID from the proof
itself (e.g., proof.sender_party_id or the proof's canonical identifier) when
constructing PartyProofsToVerify so proofs remain associated with the correct
party regardless of ordering; update the loop that builds party_proofs (the
c1_proofs.iter().enumerate() block and PartyProofsToVerify construction) to read
the ID from the proof object rather than using idx.

In `@crates/ciphernode-builder/src/ciphernode_builder.rs`:
- Around line 464-475: If self.pubkey_agg is true, fail fast when there is no ZK
backend configured: before calling PublicKeyAggregatorExtension::create (and
before ensure_multithread / e3_builder.with), check the builder's zk_backend
(self.zk_backend) and return an Err (or propagate an appropriate construction
error) with a clear message that pubkey_agg requires a ZK backend; do this in
the same branch where pubkey_agg is handled so the builder doesn't proceed to
create the extension without zk_backend configured.

In `@crates/events/src/enclave_event/publickey_aggregated.rs`:
- Around line 26-29: The Display impl for PublicKeyAggregated currently uses
write!(f, "{:?}", self) which dumps the full struct (including
pk_aggregation_proof) into logs; change the fmt method on the Display impl for
PublicKeyAggregated to avoid printing pk_aggregation_proof directly — instead
format only concise fields (e.g., epoch_index, public_key_aggregated identifier
or short hex/prefix, and/or the proof length) and redact or shorten
pk_aggregation_proof to a fixed-size summary so logs are not bloated.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2837456 and e126556.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • crates/aggregator/Cargo.toml
  • crates/aggregator/src/ext.rs
  • crates/aggregator/src/publickey_aggregator.rs
  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/entrypoint/src/start/aggregator_start.rs
  • crates/events/src/enclave_event/compute_request/mod.rs
  • crates/events/src/enclave_event/compute_request/zk.rs
  • crates/events/src/enclave_event/publickey_aggregated.rs
  • crates/multithread/src/multithread.rs
  • crates/tests/tests/integration.rs
  • examples/CRISP/test/crisp.spec.ts
  • templates/default/enclave.config.yaml
  • templates/default/server/index.ts
💤 Files with no reviewable changes (1)
  • templates/default/server/index.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/aggregator/Cargo.toml
  • crates/aggregator/src/ext.rs
  • crates/events/src/enclave_event/compute_request/mod.rs
  • crates/entrypoint/src/start/aggregator_start.rs

Comment thread crates/multithread/src/multithread.rs
Comment thread examples/CRISP/test/crisp.spec.ts

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

♻️ Duplicate comments (6)
crates/aggregator/src/publickey_aggregator.rs (3)

154-160: ⚠️ Potential issue | 🔴 Critical

Do not derive sender_party_id from insertion index.

Binding sender_party_id to idx can mis-associate proofs with the wrong party identity. Use the authoritative party ID carried by the proof/event metadata and persist that mapping through collection.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/aggregator/src/publickey_aggregator.rs` around lines 154 - 160, The
loop currently sets PartyProofsToVerify.sender_party_id using the enumeration
index (idx) which can mis-associate proofs; instead extract the authoritative
party ID from the proof/event metadata (e.g., a field like
proof.sender_party_id, proof.party_id, or proof.metadata.party_id) and use that
when constructing PartyProofsToVerify in the c1_proofs iteration that fills
party_proofs; ensure you handle the Option case (Some(proof)) by reading the
party id from the proof before cloning/consuming it and propagate that mapping
so sender_party_id is sourced from the proof metadata rather than idx.

363-376: ⚠️ Potential issue | 🔴 Critical

Scope compute responses to this aggregator’s e3_id.

Without an e3_id guard, this actor can consume ComputeResponse events from other E3 flows.

Proposed fix
     fn handle_compute_response(
         &mut self,
         response: ComputeResponse,
         ec: EventContext<Sequenced>,
     ) -> Result<()> {
+        if response.e3_id != self.e3_id {
+            return Ok(());
+        }
+
         match response.response {
             ComputeResponseKind::Zk(ZkResponse::VerifyShareProofs(data)) => {
                 self.handle_c1_verification_response(data, ec)
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/aggregator/src/publickey_aggregator.rs` around lines 363 - 376, The
handler handle_compute_response currently processes any ComputeResponse; add a
guard that only proceeds if the response's e3_id matches this aggregator's
current e3 id (e.g., compare response.e3_id to self.state.e3_id or the
appropriate stored field); if they differ, return Ok(()) to ignore the event.
Apply this check before matching on response.response so c1/c5 handling
(handle_c1_verification_response and handle_c5_proof_response) only runs for
responses belonging to this aggregator's e3 flow.

303-310: ⚠️ Potential issue | 🔴 Critical

committee_threshold must use the actual threshold value.

Sending committee_threshold: 0 builds an invalid C5 statement input.

Proposed fix
         let request = ComputeRequest::zk(
             ZkRequest::PkAggregation(PkAggregationProofRequest {
                 keyshare_bytes: honest_keyshares,
                 aggregated_pk_bytes: ArcBytes::from_bytes(&pubkey),
                 params_preset: self.params_preset.clone(),
                 committee_n: committee_h, // N = all parties in this aggregation
                 committee_h,
-                committee_threshold: 0, // Will be resolved from preset in handler
+                committee_threshold: threshold_m,
             }),
             CorrelationId::new(),
             self.e3_id.clone(),
         );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/aggregator/src/publickey_aggregator.rs` around lines 303 - 310, The
PkAggregation request is setting committee_threshold: 0 which produces an
invalid C5 statement; instead compute and pass the real threshold before
constructing ZkRequest::PkAggregation — locate the PkAggregationProofRequest
construction and replace the hardcoded 0 with the actual threshold derived from
the preset/committee (e.g. resolve from self.params_preset or from the
committee_h/Committee type via its threshold getter) so committee_threshold
contains the real threshold value used for this committee.
crates/ciphernode-builder/src/ciphernode_builder.rs (1)

466-475: ⚠️ Potential issue | 🟠 Major

Require ZK backend (and avoid fixed insecure preset) when enabling public-key aggregation.

This branch can enable PublicKeyAggregatorExtension without a configured prover, which causes runtime C1/C5 failures. Also, hardcoding InsecureThreshold512 here should be replaced by builder-provided configuration.

Proposed hardening
 if self.pubkey_agg {
     info!("Setting up PublicKeyAggregationExtension");
     // Ensure multithread worker is available for C1 verification and C5 proof generation
     let _ = self.ensure_multithread(&bus);
+    if self.zk_backend.is_none() {
+        return Err(anyhow::anyhow!(
+            "ZK backend is required when public key aggregation is enabled"
+        ));
+    }
     // TODO: Make BfvPreset configurable via builder method.
     // Currently hardcoded to InsecureThreshold512 for C5 proof generation.
     // Production deployments should use the appropriate threshold preset.
     let aggregator_preset = BfvPreset::InsecureThreshold512;
     e3_builder = e3_builder.with(PublicKeyAggregatorExtension::create(
         &bus,
         aggregator_preset,
     ))
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/ciphernode-builder/src/ciphernode_builder.rs` around lines 466 - 475,
The current code unconditionally enables PublicKeyAggregatorExtension via
PublicKeyAggregatorExtension::create with a hardcoded
BfvPreset::InsecureThreshold512 and may run without a configured prover, causing
runtime C1/C5 failures; update the builder (where e3_builder is composed) to (1)
require/verify that a ZK prover/backend is configured before calling
PublicKeyAggregatorExtension::create (use the same config/field used to register
the prover), and (2) replace the fixed BfvPreset::InsecureThreshold512 with a
preset passed through the builder API (add a builder field like
aggregator_preset and a setter method) so PublicKeyAggregatorExtension::create
uses the configured preset; also ensure ensure_multithread(&bus) still runs but
do not enable aggregation unless the prover and preset are valid.
crates/events/src/enclave_event/publickey_aggregated.rs (1)

22-29: ⚠️ Potential issue | 🟠 Major

Redact proof content from Display output.

With pk_aggregation_proof now included, write!(f, "{:?}", self) can emit full proof payloads and bloat logs.

Proposed fix
 impl Display for PublicKeyAggregated {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{:?}", self)
+        write!(
+            f,
+            "PublicKeyAggregated {{ e3_id: {:?}, nodes: {}, pk_aggregation_proof: {} }}",
+            self.e3_id,
+            self.nodes.len(),
+            self.pk_aggregation_proof.is_some()
+        )
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/events/src/enclave_event/publickey_aggregated.rs` around lines 22 -
29, The Display impl for PublicKeyAggregated currently uses write!(f, "{:?}",
self) which will include pk_aggregation_proof and can leak/oversize logs; change
the fmt implementation on PublicKeyAggregated to format all fields manually but
redact or replace pk_aggregation_proof with a placeholder (e.g., "<redacted>" or
its presence/len only). Update the fmt function for PublicKeyAggregated to
reference pk_aggregation_proof by name and omit its payload while preserving
other fields in the output.
crates/multithread/src/multithread.rs (1)

264-326: ⚠️ Potential issue | 🟠 Major

Validate committee invariants before deserialization/proving.

handle_pk_aggregation_proof currently trusts committee fields and share counts. Fail fast on malformed requests to avoid invalid C5 statements and hard-to-diagnose proof failures.

Proposed fix
 fn handle_pk_aggregation_proof(
     prover: &ZkProver,
     req: PkAggregationProofRequest,
     request: ComputeRequest,
 ) -> Result<ComputeResponse, ComputeRequestError> {
+    if req.keyshare_bytes.len() != req.committee_h
+        || req.committee_n == 0
+        || req.committee_h == 0
+        || req.committee_h > req.committee_n
+        || req.committee_threshold == 0
+        || req.committee_threshold > req.committee_h
+    {
+        return Err(make_zk_error(
+            &request,
+            format!(
+                "invalid committee params: keyshares={}, n={}, h={}, threshold={}",
+                req.keyshare_bytes.len(),
+                req.committee_n,
+                req.committee_h,
+                req.committee_threshold
+            ),
+        ));
+    }
+
     // 1. Build threshold BFV parameters from preset
     let (threshold_params, _dkg_params) = build_pair_for_preset(req.params_preset.clone())
         .map_err(|e| make_zk_error(&request, format!("build_pair_for_preset: {}", e)))?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/multithread/src/multithread.rs` around lines 264 - 326,
handle_pk_aggregation_proof currently proceeds without validating committee
invariants; before any deserialization or proof generation, validate
req.committee_n, req.committee_h, req.committee_threshold and req.keyshare_bytes
length: ensure n > 0, h <= n, threshold <= h and threshold > 0, and
req.keyshare_bytes.len() == n (and optionally fail if aggregated_pk_bytes
empty). If any check fails, return an early ComputeRequestError (use the same
error construction as existing map_err / make_zk_error patterns) so malformed
requests fail fast; update validation near the top of
handle_pk_aggregation_proof (before build_pair_for_preset/deserialization) and
mention symbols committee_n, committee_h, committee_threshold, keyshare_bytes,
PkAggregationCircuitData, and PublicKey usage locations to guide placement.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/aggregator/src/publickey_aggregator.rs`:
- Around line 154-160: The loop currently sets
PartyProofsToVerify.sender_party_id using the enumeration index (idx) which can
mis-associate proofs; instead extract the authoritative party ID from the
proof/event metadata (e.g., a field like proof.sender_party_id, proof.party_id,
or proof.metadata.party_id) and use that when constructing PartyProofsToVerify
in the c1_proofs iteration that fills party_proofs; ensure you handle the Option
case (Some(proof)) by reading the party id from the proof before
cloning/consuming it and propagate that mapping so sender_party_id is sourced
from the proof metadata rather than idx.
- Around line 363-376: The handler handle_compute_response currently processes
any ComputeResponse; add a guard that only proceeds if the response's e3_id
matches this aggregator's current e3 id (e.g., compare response.e3_id to
self.state.e3_id or the appropriate stored field); if they differ, return Ok(())
to ignore the event. Apply this check before matching on response.response so
c1/c5 handling (handle_c1_verification_response and handle_c5_proof_response)
only runs for responses belonging to this aggregator's e3 flow.
- Around line 303-310: The PkAggregation request is setting committee_threshold:
0 which produces an invalid C5 statement; instead compute and pass the real
threshold before constructing ZkRequest::PkAggregation — locate the
PkAggregationProofRequest construction and replace the hardcoded 0 with the
actual threshold derived from the preset/committee (e.g. resolve from
self.params_preset or from the committee_h/Committee type via its threshold
getter) so committee_threshold contains the real threshold value used for this
committee.

In `@crates/ciphernode-builder/src/ciphernode_builder.rs`:
- Around line 466-475: The current code unconditionally enables
PublicKeyAggregatorExtension via PublicKeyAggregatorExtension::create with a
hardcoded BfvPreset::InsecureThreshold512 and may run without a configured
prover, causing runtime C1/C5 failures; update the builder (where e3_builder is
composed) to (1) require/verify that a ZK prover/backend is configured before
calling PublicKeyAggregatorExtension::create (use the same config/field used to
register the prover), and (2) replace the fixed BfvPreset::InsecureThreshold512
with a preset passed through the builder API (add a builder field like
aggregator_preset and a setter method) so PublicKeyAggregatorExtension::create
uses the configured preset; also ensure ensure_multithread(&bus) still runs but
do not enable aggregation unless the prover and preset are valid.

In `@crates/events/src/enclave_event/publickey_aggregated.rs`:
- Around line 22-29: The Display impl for PublicKeyAggregated currently uses
write!(f, "{:?}", self) which will include pk_aggregation_proof and can
leak/oversize logs; change the fmt implementation on PublicKeyAggregated to
format all fields manually but redact or replace pk_aggregation_proof with a
placeholder (e.g., "<redacted>" or its presence/len only). Update the fmt
function for PublicKeyAggregated to reference pk_aggregation_proof by name and
omit its payload while preserving other fields in the output.

In `@crates/multithread/src/multithread.rs`:
- Around line 264-326: handle_pk_aggregation_proof currently proceeds without
validating committee invariants; before any deserialization or proof generation,
validate req.committee_n, req.committee_h, req.committee_threshold and
req.keyshare_bytes length: ensure n > 0, h <= n, threshold <= h and threshold >
0, and req.keyshare_bytes.len() == n (and optionally fail if aggregated_pk_bytes
empty). If any check fails, return an early ComputeRequestError (use the same
error construction as existing map_err / make_zk_error patterns) so malformed
requests fail fast; update validation near the top of
handle_pk_aggregation_proof (before build_pair_for_preset/deserialization) and
mention symbols committee_n, committee_h, committee_threshold, keyshare_bytes,
PkAggregationCircuitData, and PublicKey usage locations to guide placement.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e126556 and 4696d0e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • crates/aggregator/Cargo.toml
  • crates/aggregator/src/ext.rs
  • crates/aggregator/src/publickey_aggregator.rs
  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/entrypoint/src/start/aggregator_start.rs
  • crates/events/src/enclave_event/compute_request/mod.rs
  • crates/events/src/enclave_event/compute_request/zk.rs
  • crates/events/src/enclave_event/publickey_aggregated.rs
  • crates/multithread/src/multithread.rs
  • crates/tests/tests/integration.rs
  • examples/CRISP/test/crisp.spec.ts
  • templates/default/enclave.config.yaml
  • templates/default/server/index.ts
💤 Files with no reviewable changes (1)
  • templates/default/server/index.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • crates/entrypoint/src/start/aggregator_start.rs
  • examples/CRISP/test/crisp.spec.ts
  • crates/events/src/enclave_event/compute_request/mod.rs
  • crates/tests/tests/integration.rs
  • crates/events/src/enclave_event/compute_request/zk.rs
  • templates/default/enclave.config.yaml

Comment thread crates/ciphernode-builder/src/ciphernode_builder.rs Outdated
Comment thread crates/aggregator/src/publickey_aggregator.rs

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

♻️ Duplicate comments (1)
crates/ciphernode-builder/src/ciphernode_builder.rs (1)

466-475: ⚠️ Potential issue | 🟠 Major

Fail fast if pubkey_agg is enabled without a ZK backend.

This block enables C5 proof generation, but it still allows self.zk_backend to be None; in that case ensure_multithread falls back to non-ZK attach (Line 557), which can lead to missing proofs/runtime errors instead of a clear config error.

Suggested hardening
 if self.pubkey_agg {
     info!("Setting up PublicKeyAggregationExtension");
+    if self.zk_backend.is_none() {
+        return Err(anyhow::anyhow!(
+            "ZK backend is required when public key aggregation is enabled"
+        ));
+    }
     // Ensure multithread worker is available for C1 verification and C5 proof generation
     let _ = self.ensure_multithread(&bus);
     // TODO: Make BfvPreset configurable via builder method.
     // Currently hardcoded to InsecureThreshold512 for C5 proof generation.
     // Production deployments should use the appropriate threshold preset.
     let aggregator_preset = DEFAULT_BFV_PRESET;
     e3_builder = e3_builder.with(PublicKeyAggregatorExtension::create(
         &bus,
         aggregator_preset,
     ))
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/ciphernode-builder/src/ciphernode_builder.rs` around lines 466 - 475,
The code currently adds PublicKeyAggregatorExtension (via
PublicKeyAggregatorExtension::create and with DEFAULT_BFV_PRESET) even when
self.zk_backend is None, which can lead to missing C5 proofs at runtime; update
the builder to fail fast by checking self.zk_backend (or equivalent config flag)
before calling ensure_multithread or PublicKeyAggregatorExtension::create and
return a clear configuration error (or Err) if pubkey aggregation is enabled but
self.zk_backend.is_none(); locate the check around ensure_multithread,
e3_builder, and PublicKeyAggregatorExtension::create and add the validation
(with a descriptive error message) so the builder refuses to construct the node
when pubkey_agg is requested without a ZK backend.
🧹 Nitpick comments (2)
templates/default/deployed_contracts.json (1)

63-63: Harden reproducibility for ABI-encoded initData literals.

These long encoded payloads are correct-by-construction only when generated, not hand-maintained. Consider enforcing a generation/validation step in CI so constructorArgs and initData cannot diverge silently.

Also applies to: 85-85, 108-108

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@templates/default/deployed_contracts.json` at line 63, The literal
ABI-encoded initData in deployed_contracts.json (key "initData") can drift from
its human-readable constructorArgs; add a generation/validation step to CI that
regenerates initData from constructorArgs (or validates equality) during
pre-merge checks and fails the build when they diverge, ensuring initData values
(and other occurrences noted at lines ~85 and ~108) are produced
programmatically from the ABI and constructor arguments rather than hand-edited.
templates/default/enclave.config.yaml (1)

7-19: Consider adding a CI guard for config/snapshot drift.

These values are easy to desynchronize over time. A lightweight CI check comparing this file’s deploy_block values against templates/default/deployed_contracts.json would catch stale updates early.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@templates/default/enclave.config.yaml` around lines 7 - 19, The YAML contains
hard-coded deploy_block values (e.g., deploy_block under ciphernode_registry,
bonding_registry, fee_token, e3_program) that can drift from the canonical
source; add a CI guard that loads templates/default/deployed_contracts.json and
compares each contract's deploy_block and address against the values in
templates/default/enclave.config.yaml, failing the check (non-zero exit) when
any deploy_block or address mismatches are found so PRs must update both files
or the JSON; implement the check as a lightweight script (Node/Python/bash)
invoked in CI that reports which keys (contract names) differ and a suggestion
to sync the files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/ciphernode-builder/src/ciphernode_builder.rs`:
- Around line 466-475: The code currently adds PublicKeyAggregatorExtension (via
PublicKeyAggregatorExtension::create and with DEFAULT_BFV_PRESET) even when
self.zk_backend is None, which can lead to missing C5 proofs at runtime; update
the builder to fail fast by checking self.zk_backend (or equivalent config flag)
before calling ensure_multithread or PublicKeyAggregatorExtension::create and
return a clear configuration error (or Err) if pubkey aggregation is enabled but
self.zk_backend.is_none(); locate the check around ensure_multithread,
e3_builder, and PublicKeyAggregatorExtension::create and add the validation
(with a descriptive error message) so the builder refuses to construct the node
when pubkey_agg is requested without a ZK backend.

---

Nitpick comments:
In `@templates/default/deployed_contracts.json`:
- Line 63: The literal ABI-encoded initData in deployed_contracts.json (key
"initData") can drift from its human-readable constructorArgs; add a
generation/validation step to CI that regenerates initData from constructorArgs
(or validates equality) during pre-merge checks and fails the build when they
diverge, ensuring initData values (and other occurrences noted at lines ~85 and
~108) are produced programmatically from the ABI and constructor arguments
rather than hand-edited.

In `@templates/default/enclave.config.yaml`:
- Around line 7-19: The YAML contains hard-coded deploy_block values (e.g.,
deploy_block under ciphernode_registry, bonding_registry, fee_token, e3_program)
that can drift from the canonical source; add a CI guard that loads
templates/default/deployed_contracts.json and compares each contract's
deploy_block and address against the values in
templates/default/enclave.config.yaml, failing the check (non-zero exit) when
any deploy_block or address mismatches are found so PRs must update both files
or the JSON; implement the check as a lightweight script (Node/Python/bash)
invoked in CI that reports which keys (contract names) differ and a suggestion
to sync the files.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4696d0e and c47ca0a.

📒 Files selected for processing (3)
  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • templates/default/deployed_contracts.json
  • templates/default/enclave.config.yaml

@ctrlc03 ctrlc03 requested a review from cedoor March 3, 2026 15:32

@cedoor cedoor 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.

utACK

@ctrlc03 ctrlc03 merged commit e06363a into main Mar 3, 2026
27 checks passed
@ctrlc03 ctrlc03 deleted the feat/t4-integration branch March 3, 2026 16:03
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.

Integrate T4 (circuit 5)

2 participants