Skip to content

fix(crisp): account for remainder in multi-option segment boundaries#1345

Merged
cedoor merged 6 commits into
mainfrom
fix/multi-option-crisp
Feb 18, 2026
Merged

fix(crisp): account for remainder in multi-option segment boundaries#1345
cedoor merged 6 commits into
mainfrom
fix/multi-option-crisp

Conversation

@cedoor

@cedoor cedoor commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

Closes #1344


  • Add remainder offset to start_idx when D is not evenly divisible by num_options in check_coefficient_values_with_balance and check_coefficient_zero
  • TypeScript puts remainder zeros at end; after k1.reverse() they appear at start — use start_idx = remainder + (circuit_segment * segment_size) to align segments
  • Fixes total_votes <= balance constraint failure for 3+ options with D=512 (remainder=2)
  • Add generateTestLeaves helper and refactor tests to use dynamic leaves from (address, balance) pairs
  • Replace hardcoded LEAVES with generateTestLeaves in vote and utils tests

Summary by CodeRabbit

  • Bug Fixes

    • Corrected index computation and remainder handling in circuit utilities to support non-divisible cases.
  • Contracts

    • Updated verifier verification key and related constant for proof verification.
  • Tests

    • Replaced static test leaves with a generateTestLeaves helper and SLOT_ADDRESS constant; updated tests to use dynamic leaves and adjusted vote inputs and proofs.
  • Chores

    • Bumped package and client SDK versions across example packages.

@vercel

vercel Bot commented Feb 18, 2026

Copy link
Copy Markdown

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

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
crisp Skipped Skipped Feb 18, 2026 10:50am
enclave-docs Skipped Skipped Feb 18, 2026 10:50am

Request Review

@coderabbitai

coderabbitai Bot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Adds remainder-based offset to circuit segment indexing to handle non-divisible polynomial degrees; introduces test helper and SLOT_ADDRESS constant replacing static LEAVES; updates tests to use generated leaves; updates verifier verification key and bumps several package versions.

Changes

Cohort / File(s) Summary
Circuit Index Correction
examples/CRISP/circuits/src/utils.nr
Compute remainder = D - (segment_size * num_options) and use start_idx = remainder + (circuit_segment * segment_size) in check_coefficient_values_with_balance and check_coefficient_zero; add tests for non-divisible D.
Test Constants & Helpers
examples/CRISP/packages/crisp-sdk/tests/constants.ts, examples/CRISP/packages/crisp-sdk/tests/helpers.ts
Remove static LEAVES; add SLOT_ADDRESS constant and new generateTestLeaves(entries, minSize) helper that builds/pads Merkle leaves using hashLeaf.
Test Suite Updates
examples/CRISP/packages/crisp-sdk/tests/utils.test.ts, examples/CRISP/packages/crisp-sdk/tests/vote.test.ts
Replace hard-coded leaves/slot with generateTestLeaves and SLOT_ADDRESS; adjust tests to use generated leaves and updated vote tuple/inputs.
Verifier & Packaging
examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol, examples/CRISP/**/package.json
Replace verifier VK coordinates and update VK_HASH; bump package versions (crisp-contracts, crisp-sdk, crisp-zk-inputs, client) to 0.5.11.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • ctrlc03
  • 0xjei

Poem

🐇 I hopped through bits and found the gap,
Remainders tucked back into their map,
Leaves now sprout when tests request,
Slot addresses dressed in their best,
Circuits sing — I nibble a nap.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(crisp): account for remainder in multi-option segment boundaries' directly and clearly describes the main change: adding remainder offset handling for segment boundary calculations when D is not divisible by num_options.
Linked Issues check ✅ Passed All coding requirements from issue #1344 are met: remainder offset added to start_idx calculation in check_coefficient_values_with_balance and check_coefficient_zero, with supporting test cases validating the fix for non-divisible D scenarios.
Out of Scope Changes check ✅ Passed Changes include in-scope fixes (remainder offset in Noir code), necessary test refactoring (generateTestLeaves helper, dynamic leaves), and version bumps aligned with the release cycle; no extraneous modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/multi-option-crisp

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.

@cedoor cedoor requested a review from ctrlc03 February 18, 2026 09:51
@cedoor cedoor changed the title fix(crisp): account for remainder in multi-option vote segment boundaries fix(crisp): account for remainder in multi-option segment boundaries Feb 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 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 `@examples/CRISP/circuits/src/utils.nr`:
- Line 32: Add a targeted regression test that exercises the non-divisible D
case so the new remainder logic is actually used: create a unit test that sets D
and num_options such that D % num_options != 0 (e.g., D=100, num_options=3) and
supplies a non-zero vote/coefficient so start_idx (computed from remainder,
segment_size, circuit_segment) must be correct; verify the circuit output or
index access that previously could go out-of-bounds. Update or add this test
alongside the existing tests (e.g., augment test_zero_vote_passes /
test_check_coefficient_zero_passes or add a new test) and ensure it runs for the
code paths using remainder, D, segment_size, num_options, start_idx and any
k1.reverse() logic; replicate the same regression test for the other occurrences
noted (lines referenced around 44, 105, 110) so all similar fixes are exercised.

Comment thread examples/CRISP/circuits/src/utils.nr
@vercel vercel Bot temporarily deployed to Preview – enclave-docs February 18, 2026 10:03 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp February 18, 2026 10:03 Inactive
ctrlc03
ctrlc03 previously approved these changes Feb 18, 2026

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

<3

@vercel vercel Bot temporarily deployed to Preview – enclave-docs February 18, 2026 10:05 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp February 18, 2026 10:05 Inactive
@cedoor cedoor requested a review from ctrlc03 February 18, 2026 10:05
@cedoor cedoor enabled auto-merge (squash) February 18, 2026 10:05
ctrlc03
ctrlc03 previously approved these changes Feb 18, 2026
cedoor and others added 5 commits February 18, 2026 11:22
…ries

- Add remainder offset to start_idx when D is not evenly divisible by num_options
- TypeScript puts remainder zeros at end; after k1.reverse() they appear at start
- Fixes valid_vote constraint failure for 3+ options with D=512 (remainder=2)
- Apply fix to both check_coefficient_values_with_balance and check_coefficient_zero

Co-authored-by: Cursor <cursoragent@cursor.com>
- Updated @crisp-e3/sdk to 0.5.11
- Updated @crisp-e3/contracts to 0.5.11
- Updated @crisp-e3/zk-inputs to 0.5.11
- Published to npm
- Add test_non_divisible_d_vote_passes for check_coefficient_values_with_balance (D=100, num_options=3)
- Add test_check_coefficient_zero_non_divisible_d_fails for check_coefficient_zero
- Exercise remainder, segment_size, start_idx indexing when D % num_options != 0

Co-authored-by: Cursor <cursoragent@cursor.com>
ctrlc03
ctrlc03 previously approved these changes Feb 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
examples/CRISP/packages/crisp-sdk/tests/vote.test.ts (1)

37-37: zeroVote uses 2 options while vote uses 3 — intentional but potentially fragile.

getZeroVote(2) on line 37 is used for previousCiphertext (line 69) and the mask vote tests (numOptions: 2 on lines 265, 288), while the real vote [10n, 0n, 0n] has 3 options. These are independent test scenarios so it's currently fine, but if someone later unifies numOptions across tests, the zeroVote(2) will silently produce the wrong-sized ciphertext.

Consider deriving zeroVote from a shared constant or adding a brief comment clarifying the intentional mismatch.

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

In `@examples/CRISP/packages/crisp-sdk/tests/vote.test.ts` at line 37, zeroVote is
created with getZeroVote(2) while the real vote array (vote) has 3 options,
which may lead to silent size mismatches later; update tests to derive zeroVote
from a shared constant or the same source used for vote (e.g., replace
getZeroVote(2) with getZeroVote(NUM_OPTIONS) or construct NUM_OPTIONS and use it
for both zeroVote and vote), and/or add a short comment next to getZeroVote(2)
explaining the intentional mismatch referencing zeroVote, getZeroVote, vote,
previousCiphertext, and numOptions so future maintainers won’t accidentally
unify sizes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@examples/CRISP/packages/crisp-sdk/tests/vote.test.ts`:
- Line 37: zeroVote is created with getZeroVote(2) while the real vote array
(vote) has 3 options, which may lead to silent size mismatches later; update
tests to derive zeroVote from a shared constant or the same source used for vote
(e.g., replace getZeroVote(2) with getZeroVote(NUM_OPTIONS) or construct
NUM_OPTIONS and use it for both zeroVote and vote), and/or add a short comment
next to getZeroVote(2) explaining the intentional mismatch referencing zeroVote,
getZeroVote, vote, previousCiphertext, and numOptions so future maintainers
won’t accidentally unify sizes.

@vercel vercel Bot temporarily deployed to Preview – enclave-docs February 18, 2026 10:50 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp February 18, 2026 10:50 Inactive
@cedoor cedoor requested a review from ctrlc03 February 18, 2026 10:51
@cedoor cedoor merged commit 2433a12 into main Feb 18, 2026
27 checks passed
@github-actions github-actions Bot deleted the fix/multi-option-crisp branch February 26, 2026 03:13
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.

CRISP multi-option fails when polynomial degree is not divisible by num_options

2 participants