Skip to content

feat: verify fold proofs onchain#1465

Merged
ctrlc03 merged 1 commit into
mainfrom
feat/onchain-zk-fold-verification
Mar 23, 2026
Merged

feat: verify fold proofs onchain#1465
ctrlc03 merged 1 commit into
mainfrom
feat/onchain-zk-fold-verification

Conversation

@cedoor

@cedoor cedoor commented Mar 20, 2026

Copy link
Copy Markdown
Contributor
  • BFV verifiers: BfvPkVerifier and BfvDecryptionVerifier take a separate foldProof (abi.encode(bytes, bytes32[])), decode it when non-empty, and verify it via RecursiveAggregationFoldVerifier (ICircuitVerifier).
  • Interfaces & entrypoints: IPkVerifier / IDecryptionVerifier signatures updated; CiphernodeRegistry.publishCommittee and Enclave.publishPlaintextOutput accept an extra foldProof calldata argument (use 0x to skip).
  • Aggregator / EVM: Rust encode_zk_proof is used for the main proof and again for the fold proof when present; empty bytes when aggregation is off.
  • Tooling: Ignition + deploy scripts pass RecursiveAggregationFoldVerifier into BFV verifier constructors; Hardhat tasks/tests updated for the new parameters.

Summary by CodeRabbit

  • New Features

    • Added optional "fold proof" support across publishing and verification flows to enable recursive fold-proof verification for plaintext outputs and committee publication.
  • Tests

    • Expanded and updated unit and integration tests to cover fold-proof success/failure and integration scenarios.
  • Chores

    • Updated deployment wiring, CLI/task options, config/deployment manifests, and contract artifacts to support the new fold verifier dependency.

@cedoor cedoor requested review from ctrlc03 and hmzakhalid March 20, 2026 16:07
@cedoor cedoor linked an issue Mar 20, 2026 that may be closed by this pull request
@vercel

vercel Bot commented Mar 20, 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 23, 2026 8:42am
enclave-docs Ready Ready Preview, Comment Mar 23, 2026 8:42am

Request Review

@coderabbitai

coderabbitai Bot commented Mar 20, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds an optional foldProof parameter and propagates it across ABIs, Solidity interfaces/implementations, BFV verifiers, Rust bindings/writers, deployment wiring, tasks/CLI, and tests to support optional recursive fold verification.

Changes

Cohort / File(s) Summary
Contract Interfaces & Artifacts
packages/enclave-contracts/contracts/interfaces/IEnclave.sol, .../ICiphernodeRegistry.sol, .../IDecryptionVerifier.sol, .../IPkVerifier.sol, packages/enclave-contracts/artifacts/.../*.json
Added bytes foldProof parameter to multiple ABI/interface signatures (e.g., publishPlaintextOutput, publishCommittee, verify); regenerated artifacts/update buildInfoId.
Core Contracts
packages/enclave-contracts/contracts/Enclave.sol, .../registry/CiphernodeRegistryOwnable.sol
Extended publishPlaintextOutput and publishCommittee to accept and forward foldProof to verifier calls; verifier call sites updated.
BFV Verifiers
packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol, .../BfvPkVerifier.sol
Added foldVerifier dependency and constructor arg, extended verify signatures to accept foldProof, implemented conditional fold decoding + delegated fold verification via new helpers.
Mocks & Tests
packages/enclave-contracts/contracts/test/*, packages/enclave-contracts/test/**/*.spec.ts
Updated mock function signatures to accept foldProof; updated many tests to pass empty fold ("0x") and added tests covering fold success/failure; deployment fixtures updated to deploy mock fold verifier.
Rust Bindings & Writers
crates/evm-helpers/src/contracts.rs, crates/evm/src/enclave_sol_writer.rs, crates/evm/src/ciphernode_registry_sol.rs
ABI bindings and writer methods updated to accept fold_proof/fold-proof optional args, encode optional proofs to Bytes (empty when None), and forward/cloned into contract calls and retry closures.
Deployment / Ignition / Scripts
packages/enclave-contracts/ignition/modules/*, packages/enclave-contracts/scripts/deployAndSave/*
Wired RecursiveAggregationFoldVerifier into Ignition modules and deploy scripts; deployment checks/read args updated and fold verifier address passed as extra constructor arg.
Hardhat Tasks & CLI & Examples
packages/enclave-contracts/tasks/enclave.ts, examples/CRISP/server/src/cli/commands.rs, examples/CRISP/*
Added foldProof (and foldProofFile) task/CLI options and pass foldProof to contract calls; example manifests updated (deploy_block / blockNumber decremented by 1).
Docs / Comments
crates/evm/src/helpers.rs
Simplified a test doc comment about ABI format for encoded ZK proof.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI/Task
  participant Rust as Rust Writer/Binding
  participant Contract as Enclave/Registry
  participant Verifier as BFV Verifier
  CLI->>Rust: publishPlaintextOutput(e3Id, data, proof, foldProof)
  Rust->>Contract: tx.publishPlaintextOutput(e3Id, data, proof, foldProof)
  Contract->>Verifier: verify(hash(data), proof, foldProof)
  alt foldProof non-empty
    Verifier->>Verifier: decode foldProof -> (foldRawProof, foldPublicInputs)
    Verifier->>Verifier: foldVerifier.verify(foldRawProof, foldPublicInputs)
  end
  Verifier-->>Contract: verification result
  Contract-->>Rust: tx receipt / revert
  Rust-->>CLI: result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • hmzakhalid
  • ctrlc03
  • 0xjei

Poem

🐰 A fold proof hops into view,
It travels through contracts, tests, and code,
Verifiers nibble, decode, and review,
Transactions carry the tiny load,
Hooray — recursive hops lighten the road!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: verify fold proofs onchain' clearly and accurately describes the main change—adding on-chain verification for fold proofs—which is the central objective of this PR.

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

✨ 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 feat/onchain-zk-fold-verification

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.

@ctrlc03 ctrlc03 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

utACK

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol (1)

297-314: ⚠️ Potential issue | 🟠 Major

Validate the published committee payload against canonical state.

nodes.length is the only check on the caller-supplied committee, and publicKey is never checked here. That means CommitteePublished can emit a node list or raw key bytes that disagree with c.topNodes and the verified publicKeyHash, while the contract state proceeds as if publication succeeded. Off-chain consumers that trust the event can diverge from on-chain state.

Emit c.topNodes directly and validate publicKey against the same digest the proof exposes before emitting.

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

In `@packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol`
around lines 297 - 314, The contract currently only checks nodes.length and
never verifies or emits canonical values; after decoding publicInputs and
calling e3.pkVerifier.verify(proof, foldProof), add a check that the supplied
publicKey matches the digest from the proof (e.g. require(keccak256(publicKey)
== publicKeyHash, "C5: publicKey mismatch")), then emit the canonical stored
values (emit CommitteePublished(e3Id, c.topNodes, c.publicKey)) instead of the
caller-supplied nodes/publicKey so events reflect on-chain state; reference
c.topNodes, publicKey, publicKeyHash, publicInputs, e3.pkVerifier.verify, and
emit CommitteePublished in your change.
🧹 Nitpick comments (4)
packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol (1)

11-19: Minor: Redundant statement on line 16.

The data; statement on line 16 appears unnecessary since data is already used in the condition on line 18, which should satisfy the compiler's unused variable check.

♻️ Suggested cleanup
     function verify(
         bytes32,
         bytes memory data,
         bytes memory /* foldProof */
     ) external pure returns (bool success) {
-        data;
-
         if (data.length > 0) success = true;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol` around
lines 11 - 19, In the MockDecryptionVerifier.verify function remove the
redundant no-op statement "data;" since the parameter is already referenced in
the conditional (if (data.length > 0)), leaving the parameter use to satisfy the
compiler; simply delete the standalone "data;" line to clean up the function
body while keeping the existing behavior that sets success = true when
data.length > 0.
packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol (1)

30-33: Consider validating _foldVerifier in constructor.

If _foldVerifier is address(0) and a caller passes a non-empty foldProof, the call to foldVerifier.verify() on line 77 will revert with an obscure error. Consider adding an explicit check or documenting the requirement.

♻️ Optional: Add constructor validation
 constructor(address _circuitVerifier, address _foldVerifier) {
+    require(_circuitVerifier != address(0), "Invalid circuit verifier");
+    require(_foldVerifier != address(0), "Invalid fold verifier");
     circuitVerifier = ICircuitVerifier(_circuitVerifier);
     foldVerifier = ICircuitVerifier(_foldVerifier);
 }

Alternatively, if _foldVerifier being address(0) is a valid deployment configuration (fold verification disabled), the _verifyFold function should handle this case explicitly.

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

In `@packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol`
around lines 30 - 33, The constructor currently assigns foldVerifier without
checking `_foldVerifier`; either validate it by requiring `_foldVerifier !=
address(0)` in the constructor (so fold verification is always available) or
make `_verifyFold` tolerant of a zero address by checking `address(foldVerifier)
!= address(0)` before calling `foldVerifier.verify()` and returning success/skip
appropriately when zero; update the constructor or the `_verifyFold` function
(and document behavior) depending on which approach you choose, referencing the
constructor, `_foldVerifier`, `foldVerifier`, `_verifyFold`, `foldProof`, and
`verify()` symbols.
packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts (1)

19-21: Inconsistent chain resolution compared to sibling script.

This script uses hre.globalOptions.network (Line 21) while bfvPkVerifier.ts uses networkName from hre.network.connect() (Line 21 in that file). This inconsistency could cause deployment mismatches if the two methods return different values in edge cases.

Suggested fix for consistency
 export const deployAndSaveBfvDecryptionVerifier = async (
   hre: HardhatRuntimeEnvironment,
 ): Promise<{
   bfvDecryptionVerifier: BfvDecryptionVerifier;
 }> => {
-  const { ethers } = await hre.network.connect();
+  const { ethers, networkName } = await hre.network.connect();
   const [signer] = await ethers.getSigners();
-  const chain = hre.globalOptions.network ?? "localhost";
+  const chain = networkName ?? "localhost";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts`
around lines 19 - 21, The chain name resolution is inconsistent with
bfvPkVerifier.ts: instead of using hre.globalOptions.network, get networkName
from the object returned by await hre.network.connect() (e.g., destructure
networkName alongside ethers) and use that (with a "localhost" fallback) to set
the chain variable so both scripts resolve the network the same way.
packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts (1)

167-185: Add a malformed foldProof regression test.

The new fold-path coverage only exercises true/false verifier results. The other new branch here is decoding non-empty foldProof, so a bad payload can still slip through untested unless the suite asserts that malformed fold data reverts.

🧪 Suggested test
+    it("reverts on invalid fold proof encoding", async function () {
+      const { bfvDecryptionVerifier, mockCircuit } = await loadFixture(
+        deployWithMockCircuit,
+      );
+      await mockCircuit.setReturnValue(true);
+
+      const messageCoeffs = [1n, 2n, 3n];
+      const publicInputs = buildPublicInputsWithMessage(messageCoeffs);
+      const plaintextHash = plaintextToHash(messageCoeffs);
+      const proof = encodeProof("0x01", publicInputs);
+
+      await expect(
+        bfvDecryptionVerifier.verify.staticCall(
+          plaintextHash,
+          proof,
+          "0xdeadbeef",
+        ),
+      ).to.be.revert(ethers);
+    });

Also applies to: 232-250

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

In `@packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts` around lines
167 - 185, Add a regression test that submits a malformed non-empty foldProof
and asserts the call reverts: in the existing spec where you use
loadFixture(deployWithMockCircuit) and construct foldProof via encodeProof,
create a malformed foldProof payload (e.g. wrong-length proof bytes or invalid
pi array) and call bfvDecryptionVerifier.verify.staticCall(plaintextHash, proof,
foldProof) expecting a revert; set mockCircuit and mockFold return values as
needed (mockCircuit.setReturnValue, mockFold.setReturnValue) to exercise the
fold-path decoding branch and ensure the test fails if malformed foldProof is
not properly rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol`:
- Around line 297-314: The contract currently only checks nodes.length and never
verifies or emits canonical values; after decoding publicInputs and calling
e3.pkVerifier.verify(proof, foldProof), add a check that the supplied publicKey
matches the digest from the proof (e.g. require(keccak256(publicKey) ==
publicKeyHash, "C5: publicKey mismatch")), then emit the canonical stored values
(emit CommitteePublished(e3Id, c.topNodes, c.publicKey)) instead of the
caller-supplied nodes/publicKey so events reflect on-chain state; reference
c.topNodes, publicKey, publicKeyHash, publicInputs, e3.pkVerifier.verify, and
emit CommitteePublished in your change.

---

Nitpick comments:
In `@packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol`:
- Around line 11-19: In the MockDecryptionVerifier.verify function remove the
redundant no-op statement "data;" since the parameter is already referenced in
the conditional (if (data.length > 0)), leaving the parameter use to satisfy the
compiler; simply delete the standalone "data;" line to clean up the function
body while keeping the existing behavior that sets success = true when
data.length > 0.

In
`@packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol`:
- Around line 30-33: The constructor currently assigns foldVerifier without
checking `_foldVerifier`; either validate it by requiring `_foldVerifier !=
address(0)` in the constructor (so fold verification is always available) or
make `_verifyFold` tolerant of a zero address by checking `address(foldVerifier)
!= address(0)` before calling `foldVerifier.verify()` and returning success/skip
appropriately when zero; update the constructor or the `_verifyFold` function
(and document behavior) depending on which approach you choose, referencing the
constructor, `_foldVerifier`, `foldVerifier`, `_verifyFold`, `foldProof`, and
`verify()` symbols.

In `@packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts`:
- Around line 19-21: The chain name resolution is inconsistent with
bfvPkVerifier.ts: instead of using hre.globalOptions.network, get networkName
from the object returned by await hre.network.connect() (e.g., destructure
networkName alongside ethers) and use that (with a "localhost" fallback) to set
the chain variable so both scripts resolve the network the same way.

In `@packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts`:
- Around line 167-185: Add a regression test that submits a malformed non-empty
foldProof and asserts the call reverts: in the existing spec where you use
loadFixture(deployWithMockCircuit) and construct foldProof via encodeProof,
create a malformed foldProof payload (e.g. wrong-length proof bytes or invalid
pi array) and call bfvDecryptionVerifier.verify.staticCall(plaintextHash, proof,
foldProof) expecting a revert; set mockCircuit and mockFold return values as
needed (mockCircuit.setReturnValue, mockFold.setReturnValue) to exercise the
fold-path decoding branch and ensure the test fails if malformed foldProof is
not properly rejected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 331b6b9c-7c13-4c67-8559-89da862b77d0

📥 Commits

Reviewing files that changed from the base of the PR and between 3018387 and 36c95c4.

📒 Files selected for processing (35)
  • crates/evm-helpers/src/contracts.rs
  • crates/evm/src/ciphernode_registry_sol.rs
  • crates/evm/src/enclave_sol_writer.rs
  • crates/evm/src/helpers.rs
  • examples/CRISP/enclave.config.yaml
  • examples/CRISP/packages/crisp-contracts/deployed_contracts.json
  • examples/CRISP/server/src/cli/commands.rs
  • packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json
  • packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json
  • packages/enclave-contracts/contracts/Enclave.sol
  • packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol
  • packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol
  • packages/enclave-contracts/contracts/interfaces/IEnclave.sol
  • packages/enclave-contracts/contracts/interfaces/IPkVerifier.sol
  • packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol
  • packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol
  • packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol
  • packages/enclave-contracts/contracts/test/MockPkVerifier.sol
  • packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol
  • packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol
  • packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts
  • packages/enclave-contracts/ignition/modules/bfvPkVerifier.ts
  • packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts
  • packages/enclave-contracts/scripts/deployAndSave/bfvPkVerifier.ts
  • packages/enclave-contracts/tasks/enclave.ts
  • packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts
  • packages/enclave-contracts/test/BfvPkVerifier.spec.ts
  • packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts
  • packages/enclave-contracts/test/Enclave.spec.ts
  • packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts
  • packages/enclave-contracts/test/Slashing/CommitteeExpulsion.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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/enclave-contracts/tasks/enclave.ts`:
- Around line 457-467: The code reads foldProofFile (and similarly the other
proof file block) with file.toString() and sends it verbatim, which can include
trailing whitespace/newlines and invalid hex; update the logic around
foldProofToSend (and proofToSend) to trim() the string, strip any
leading/trailing whitespace/newlines, normalize the hex by ensuring a "0x"
prefix (add if missing), validate that the remaining characters are valid hex
and that the hex payload has even length, and throw/return a clear CLI error
before sending the transaction if validation fails so the tx is not attempted
with malformed bytes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e47332d0-ec91-4a25-8811-60d34fdf63ad

📥 Commits

Reviewing files that changed from the base of the PR and between 36c95c4 and 2f9b5c1.

📒 Files selected for processing (2)
  • examples/CRISP/enclave.config.yaml
  • packages/enclave-contracts/tasks/enclave.ts
✅ Files skipped from review due to trivial changes (1)
  • examples/CRISP/enclave.config.yaml

Comment thread packages/enclave-contracts/tasks/enclave.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.

🧹 Nitpick comments (3)
packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol (1)

23-26: Consider adding zero-address validation for verifier addresses.

Neither _circuitVerifier nor _foldVerifier are validated for the zero address. If either is accidentally set to address(0), the contract would be permanently misconfigured (immutable). This is a defense-in-depth suggestion.

🛡️ Proposed validation
 constructor(address _circuitVerifier, address _foldVerifier) {
+    require(_circuitVerifier != address(0), "BfvPkVerifier: zero circuit verifier");
+    require(_foldVerifier != address(0), "BfvPkVerifier: zero fold verifier");
     circuitVerifier = ICircuitVerifier(_circuitVerifier);
     foldVerifier = ICircuitVerifier(_foldVerifier);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol` around
lines 23 - 26, Add require checks in the constructor to guard against zero
addresses: validate _circuitVerifier and _foldVerifier before assigning to
circuitVerifier and foldVerifier (the constructor that currently assigns
ICircuitVerifier(_circuitVerifier) and ICircuitVerifier(_foldVerifier)).
Specifically, in the constructor function, require(_circuitVerifier !=
address(0), "circuitVerifier zero address") and require(_foldVerifier !=
address(0), "foldVerifier zero address") (or similar messages) so the contract
cannot be initialized with address(0) for circuitVerifier or foldVerifier.
packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts (1)

449-449: Optional: replace repeated "0x" literals with a shared constant.

Using a single EMPTY_FOLD_PROOF constant will reduce accidental call-site drift and make future edits safer.

♻️ Small cleanup
+  const EMPTY_FOLD_PROOF = "0x";
...
-      await registry.publishCommittee(0, nodes, publicKey, pkProof, "0x");
+      await registry.publishCommittee(0, nodes, publicKey, pkProof, EMPTY_FOLD_PROOF);
...
-      await enclave.publishPlaintextOutput(0, plaintextOutput, proofBytes, "0x");
+      await enclave.publishPlaintextOutput(0, plaintextOutput, proofBytes, EMPTY_FOLD_PROOF);

Also applies to: 494-494, 756-757, 860-861, 1112-1113, 1191-1192, 1434-1434, 1487-1487, 1565-1565, 1580-1580, 1624-1624, 1635-1635

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

In `@packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts` at line
449, Several test call sites repeatedly pass the literal "0x" into
registry.publishCommittee (and other similar calls); define a shared constant
(e.g., EMPTY_FOLD_PROOF) near the top of the spec and replace all occurrences of
the literal "0x" used as the fold proof argument (including calls to
registry.publishCommittee and the other repeated call sites) with that constant
to avoid duplication and accidental drift.
packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts (1)

167-185: Add a malformed non-empty foldProof revert test.

You test non-empty fold proof success/failure at verifier level, but not ABI decode failure for malformed non-empty foldProof. That decode path is part of the new behavior and should be locked down with a dedicated revert test.

🧪 Suggested test addition
+    it("reverts on invalid fold proof encoding when fold proof is non-empty", async function () {
+      const { bfvDecryptionVerifier, mockCircuit } = await loadFixture(
+        deployWithMockCircuit,
+      );
+      await mockCircuit.setReturnValue(true);
+
+      const messageCoeffs = [1n, 2n, 3n];
+      const publicInputs = buildPublicInputsWithMessage(messageCoeffs);
+      const plaintextHash = plaintextToHash(messageCoeffs);
+      const proof = encodeProof("0x01", publicInputs);
+      const invalidFoldProof = "0xdeadbeef"; // non-empty but not abi.encode(bytes, bytes32[])
+
+      await expect(
+        bfvDecryptionVerifier.verify.staticCall(
+          plaintextHash,
+          proof,
+          invalidFoldProof,
+        ),
+      ).to.be.revert(ethers);
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts` around lines
167 - 185, Add a new unit test that asserts the verifier reverts when given a
non-empty but malformed foldProof (so ABI decoding fails); locate the test suite
using BfvDecryptionVerifier.spec.ts and reuse fixtures (deployWithMockCircuit)
and helpers like encodeProof, buildPublicInputsWithMessage, plaintextToHash to
construct a valid proof and then pass an intentionally malformed foldProof (e.g.
non-empty bytes that are not a properly encoded proof) into
bfvDecryptionVerifier.verify.staticCall and expect a revert; ensure the test
covers the code path that handles non-empty foldProof ABI decode errors so the
malformed-case is locked down.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol`:
- Around line 23-26: Add require checks in the constructor to guard against zero
addresses: validate _circuitVerifier and _foldVerifier before assigning to
circuitVerifier and foldVerifier (the constructor that currently assigns
ICircuitVerifier(_circuitVerifier) and ICircuitVerifier(_foldVerifier)).
Specifically, in the constructor function, require(_circuitVerifier !=
address(0), "circuitVerifier zero address") and require(_foldVerifier !=
address(0), "foldVerifier zero address") (or similar messages) so the contract
cannot be initialized with address(0) for circuitVerifier or foldVerifier.

In `@packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts`:
- Around line 167-185: Add a new unit test that asserts the verifier reverts
when given a non-empty but malformed foldProof (so ABI decoding fails); locate
the test suite using BfvDecryptionVerifier.spec.ts and reuse fixtures
(deployWithMockCircuit) and helpers like encodeProof,
buildPublicInputsWithMessage, plaintextToHash to construct a valid proof and
then pass an intentionally malformed foldProof (e.g. non-empty bytes that are
not a properly encoded proof) into bfvDecryptionVerifier.verify.staticCall and
expect a revert; ensure the test covers the code path that handles non-empty
foldProof ABI decode errors so the malformed-case is locked down.

In `@packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts`:
- Line 449: Several test call sites repeatedly pass the literal "0x" into
registry.publishCommittee (and other similar calls); define a shared constant
(e.g., EMPTY_FOLD_PROOF) near the top of the spec and replace all occurrences of
the literal "0x" used as the fold proof argument (including calls to
registry.publishCommittee and the other repeated call sites) with that constant
to avoid duplication and accidental drift.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d1642227-cbde-4415-b374-281ce6fa81c7

📥 Commits

Reviewing files that changed from the base of the PR and between 2f9b5c1 and 23a5077.

📒 Files selected for processing (35)
  • crates/evm-helpers/src/contracts.rs
  • crates/evm/src/ciphernode_registry_sol.rs
  • crates/evm/src/enclave_sol_writer.rs
  • crates/evm/src/helpers.rs
  • examples/CRISP/enclave.config.yaml
  • examples/CRISP/packages/crisp-contracts/deployed_contracts.json
  • examples/CRISP/server/src/cli/commands.rs
  • packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json
  • packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json
  • packages/enclave-contracts/contracts/Enclave.sol
  • packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol
  • packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol
  • packages/enclave-contracts/contracts/interfaces/IEnclave.sol
  • packages/enclave-contracts/contracts/interfaces/IPkVerifier.sol
  • packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol
  • packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol
  • packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol
  • packages/enclave-contracts/contracts/test/MockPkVerifier.sol
  • packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol
  • packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol
  • packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts
  • packages/enclave-contracts/ignition/modules/bfvPkVerifier.ts
  • packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts
  • packages/enclave-contracts/scripts/deployAndSave/bfvPkVerifier.ts
  • packages/enclave-contracts/tasks/enclave.ts
  • packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts
  • packages/enclave-contracts/test/BfvPkVerifier.spec.ts
  • packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts
  • packages/enclave-contracts/test/Enclave.spec.ts
  • packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts
  • packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts
✅ Files skipped from review due to trivial changes (5)
  • crates/evm/src/helpers.rs
  • packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts
  • packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json
  • packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol
🚧 Files skipped from review as they are similar to previous changes (19)
  • examples/CRISP/server/src/cli/commands.rs
  • packages/enclave-contracts/scripts/deployAndSave/bfvDecryptionVerifier.ts
  • packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol
  • packages/enclave-contracts/contracts/test/MockPkVerifier.sol
  • packages/enclave-contracts/contracts/test/MockDecryptionVerifier.sol
  • packages/enclave-contracts/test/Enclave.spec.ts
  • packages/enclave-contracts/ignition/modules/bfvDecryptionVerifier.ts
  • packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json
  • examples/CRISP/enclave.config.yaml
  • packages/enclave-contracts/contracts/interfaces/IDecryptionVerifier.sol
  • packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol
  • packages/enclave-contracts/contracts/interfaces/IEnclave.sol
  • packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts
  • packages/enclave-contracts/test/BfvPkVerifier.spec.ts
  • crates/evm/src/ciphernode_registry_sol.rs
  • crates/evm-helpers/src/contracts.rs
  • packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json
  • packages/enclave-contracts/contracts/Enclave.sol
  • examples/CRISP/packages/crisp-contracts/deployed_contracts.json

@ctrlc03 ctrlc03 merged commit 12e2752 into main Mar 23, 2026
30 checks passed
@ctrlc03 ctrlc03 deleted the feat/onchain-zk-fold-verification branch March 28, 2026 10:05
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.

Post and verify aggregated proofs

2 participants