From 7fa5836ec68d39679d181f935e9570c6c10ceaf3 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 18 Feb 2026 10:46:32 +0100 Subject: [PATCH 1/6] fix(crisp): account for remainder in multi-option vote segment boundaries - 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 --- examples/CRISP/circuits/src/utils.nr | 7 +++- .../packages/crisp-sdk/tests/constants.ts | 10 +---- .../CRISP/packages/crisp-sdk/tests/helpers.ts | 23 +++++++++++ .../packages/crisp-sdk/tests/utils.test.ts | 17 +++++---- .../packages/crisp-sdk/tests/vote.test.ts | 38 ++++++++++--------- 5 files changed, 59 insertions(+), 36 deletions(-) create mode 100644 examples/CRISP/packages/crisp-sdk/tests/helpers.ts diff --git a/examples/CRISP/circuits/src/utils.nr b/examples/CRISP/circuits/src/utils.nr index eb83d5bd70..caf4b77197 100644 --- a/examples/CRISP/circuits/src/utils.nr +++ b/examples/CRISP/circuits/src/utils.nr @@ -29,6 +29,7 @@ pub fn check_coefficient_values_with_balance( assert(num_options as u64 <= MAX_OPTIONS as u64); let segment_size: u32 = D / num_options; + let remainder: u32 = D - (segment_size * num_options); let mut sums: [u64; MAX_OPTIONS] = [0; MAX_OPTIONS]; let mut non_zero_count: u64 = 0; @@ -38,8 +39,9 @@ pub fn check_coefficient_values_with_balance( for opt_idx in 0..MAX_OPTIONS { if (opt_idx as u64) < (num_options as u64) { // Due to reversal, TypeScript option opt_idx is at circuit segment (num_options - 1 - opt_idx) + // Remainder zeros at end of TypeScript layout become start after k1.reverse() let circuit_segment: u32 = num_options - 1 - opt_idx; - let start_idx: u32 = circuit_segment * segment_size; + let start_idx: u32 = remainder + (circuit_segment * segment_size); let mut sum: u64 = 0; @@ -97,11 +99,12 @@ pub fn check_coefficient_zero(k1: Polynomial, num_options: u32) { assert(num_options as u64 <= MAX_OPTIONS as u64); let segment_size: u32 = D / num_options; + let remainder: u32 = D - (segment_size * num_options); for opt_idx in 0..MAX_OPTIONS { if (opt_idx as u64) < (num_options as u64) { let circuit_segment: u32 = num_options - 1 - opt_idx; - let start_idx: u32 = circuit_segment * segment_size; + let start_idx: u32 = remainder + (circuit_segment * segment_size); for bit_pos in 0..MAX_VOTE_BITS { if (bit_pos as u64) < (segment_size as u64) { diff --git a/examples/CRISP/packages/crisp-sdk/tests/constants.ts b/examples/CRISP/packages/crisp-sdk/tests/constants.ts index a7765d1ef5..73f5652ae1 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/constants.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/constants.ts @@ -6,12 +6,4 @@ export const CRISP_SERVER_URL = 'http://localhost:4000' export const ECDSA_PRIVATE_KEY = '0x04da7c413e00d26569910a463c51ab514dca3bc5168c5ceb174b361cd33f9ecc' -export const LEAVES = [ - // First leaf has been generated using the address of the private key above. - 18271417062681489396127637067161377206991493997437479705544007695491481029940n, - 21386066522994147417428722670584127893127936643085609370651005837318304141270n, - 1983869233929151201074336534851649337028918489325523209164772930983460428207n, - 19370325120536147692670316510448470434381674466568115984868372181385560126045n, - 16673264759365860972077611357899248038817564448666677924174132959085122318936n, - 1983869233929151201074336534851649337028918489325523209164772930983460428207n, -] +export const SLOT_ADDRESS = '0x145B2260E2DAa2965F933A76f5ff5aE3be5A7e5a' diff --git a/examples/CRISP/packages/crisp-sdk/tests/helpers.ts b/examples/CRISP/packages/crisp-sdk/tests/helpers.ts new file mode 100644 index 0000000000..d825f03961 --- /dev/null +++ b/examples/CRISP/packages/crisp-sdk/tests/helpers.ts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +import { hashLeaf } from '../src/utils' + +/** + * Generate Merkle tree leaves for tests. Includes leaves for each (address, balance) + * pair and pads with unique filler values to reach minSize. + */ +export const generateTestLeaves = ( + entries: { address: string; balance: bigint }[], + minSize = 6, +): bigint[] => { + const leaves = entries.map(({ address, balance }) => hashLeaf(address.toLowerCase(), balance)) + const fillerCount = Math.max(0, minSize - leaves.length) + for (let i = 0; i < fillerCount; i++) { + leaves.push(BigInt(1000 + i)) + } + return leaves +} diff --git a/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts b/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts index 4f8728ce73..3510852c8d 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts @@ -6,7 +6,8 @@ import { expect, describe, it } from 'vitest' import { extractSignatureComponents, generateMerkleProof, generateMerkleTree, hashLeaf } from '../src/utils' -import { LEAVES } from './constants' +import { SLOT_ADDRESS } from './constants' +import { generateTestLeaves } from './helpers' import { MASK_SIGNATURE } from '../src/constants' describe('Utils', () => { @@ -21,24 +22,25 @@ describe('Utils', () => { describe('generateMerkleTree', () => { it('Should generate a merkle tree', () => { - const tree = generateMerkleTree(LEAVES) + const leaves = generateTestLeaves([{ address: SLOT_ADDRESS, balance: 100n }]) + const tree = generateMerkleTree(leaves) expect(tree.root).toBeDefined() }) }) describe('generateMerkleProof', () => { - const address = '0x145B2260E2DAa2965F933A76f5ff5aE3be5A7e5a' + const address = SLOT_ADDRESS const balance = 100n it('Should generate a valid merkle proof for a leaf', () => { - const tree = generateMerkleTree(LEAVES) + const leaves = generateTestLeaves([{ address, balance }]) + const tree = generateMerkleTree(leaves) - const proof = generateMerkleProof(balance, address, LEAVES) + const proof = generateMerkleProof(balance, address, leaves) expect(proof.leaf).toBe(hashLeaf(address, balance)) expect(proof.length).toBe(3) - // Unpad the proof for verification const unpaddedProof = { ...proof.proof, siblings: proof.proof.siblings.slice(0, proof.length), @@ -49,7 +51,8 @@ describe('Utils', () => { it('Should throw if the leaf does not exist in the tree', () => { expect(() => generateMerkleProof(balance, address, [])).toThrow('Leaf not found in the tree') - expect(() => generateMerkleProof(999n, address, LEAVES)).toThrow('Leaf not found in the tree') + const leaves = generateTestLeaves([{ address, balance }]) + expect(() => generateMerkleProof(999n, address, leaves)).toThrow('Leaf not found in the tree') }) }) diff --git a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts index cce3f4003b..f2b5eae760 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -19,7 +19,8 @@ import { } from '../src/vote' import { publicKeyToAddress, signMessage } from 'viem/accounts' import { Hex, recoverPublicKey } from 'viem' -import { CRISP_SERVER_URL, ECDSA_PRIVATE_KEY, LEAVES } from './constants' +import { CRISP_SERVER_URL, ECDSA_PRIVATE_KEY, SLOT_ADDRESS } from './constants' +import { generateTestLeaves } from './helpers' import { CrispSDK } from '../src/sdk' describe('Vote', () => { @@ -27,7 +28,7 @@ describe('Vote', () => { let signature: Hex let balance: bigint let address: string - let slotAddress: string + let leaves: bigint[] let publicKey: Uint8Array let previousCiphertext: Uint8Array let e3Id: number @@ -55,14 +56,15 @@ describe('Vote', () => { vi.restoreAllMocks() }) - // Setup the test environment. beforeAll(async () => { - vote = [10n, 0n] + vote = [10n, 0n, 0n] signature = await signMessage({ message: SIGNATURE_MESSAGE, privateKey: ECDSA_PRIVATE_KEY }) - balance = 100n + balance = 10n address = publicKeyToAddress(await recoverPublicKey({ hash: SIGNATURE_MESSAGE_HASH, signature })) - // Address of the last leaf in the Merkle tree, used for mask votes. - slotAddress = '0x145B2260E2DAa2965F933A76f5ff5aE3be5A7e5a' + leaves = generateTestLeaves([ + { address, balance }, + { address: SLOT_ADDRESS, balance }, + ]) publicKey = generatePublicKey() previousCiphertext = encryptVote(zeroVote, publicKey) e3Id = 0 @@ -158,7 +160,7 @@ describe('Vote', () => { // This test simulates a real vote (i.e. generateVoteProof). // Using generateCircuitInputs directly to check the output of the circuit. - const merkleProof = generateMerkleProof(balance, address, LEAVES) + const merkleProof = generateMerkleProof(balance, address, leaves) const { crispInputs } = await generateCircuitInputs({ vote, @@ -181,12 +183,12 @@ describe('Vote', () => { // This test simulates a mask vote (i.e. generateMaskVoteProof). // Using generateCircuitInputs directly to check the output of the circuit. - const merkleProof = generateMerkleProof(balance, slotAddress, LEAVES) + const merkleProof = generateMerkleProof(balance, SLOT_ADDRESS, leaves) const { crispInputs } = await generateCircuitInputs({ publicKey, balance, - slotAddress, + slotAddress: SLOT_ADDRESS, merkleProof, vote: zeroVote, signature: MASK_SIGNATURE, @@ -205,7 +207,7 @@ describe('Vote', () => { // This test simulates a mask vote (i.e. generateMaskVoteProof). // Using generateCircuitInputs directly to check the output of the circuit. - const merkleProof = generateMerkleProof(balance, slotAddress, LEAVES) + const merkleProof = generateMerkleProof(balance, SLOT_ADDRESS, leaves) const { crispInputs } = await generateCircuitInputs({ vote: zeroVote, @@ -214,7 +216,7 @@ describe('Vote', () => { messageHash: SIGNATURE_MESSAGE_HASH, merkleProof, balance, - slotAddress, + slotAddress: SLOT_ADDRESS, isMaskVote: true, }) @@ -233,10 +235,10 @@ describe('Vote', () => { vote, publicKey, signature, - merkleLeaves: LEAVES, + merkleLeaves: leaves, balance, messageHash: SIGNATURE_MESSAGE_HASH, - slotAddress, + slotAddress: SLOT_ADDRESS, e3Id, }) @@ -256,9 +258,9 @@ describe('Vote', () => { const proof = await sdk.generateMaskVoteProof({ balance, - slotAddress, + slotAddress: SLOT_ADDRESS, publicKey, - merkleLeaves: LEAVES, + merkleLeaves: leaves, e3Id: 0, numOptions: 2, }) @@ -279,9 +281,9 @@ describe('Vote', () => { const proof = await sdk.generateMaskVoteProof({ balance, - slotAddress, + slotAddress: SLOT_ADDRESS, publicKey, - merkleLeaves: LEAVES, + merkleLeaves: leaves, e3Id: 0, numOptions: 2, }) From 3dd1c9622d30d1678c345c73f3a420f96d30d21f Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 18 Feb 2026 11:01:35 +0100 Subject: [PATCH 2/6] chore(crisp): publish version 0.5.11 - 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 --- examples/CRISP/client/package.json | 2 +- examples/CRISP/packages/crisp-contracts/package.json | 2 +- examples/CRISP/packages/crisp-sdk/package.json | 2 +- examples/CRISP/packages/crisp-sdk/tests/helpers.ts | 5 +---- examples/CRISP/packages/crisp-zk-inputs/package.json | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/examples/CRISP/client/package.json b/examples/CRISP/client/package.json index 8571c9b2ae..164a6b16a4 100644 --- a/examples/CRISP/client/package.json +++ b/examples/CRISP/client/package.json @@ -18,7 +18,7 @@ "deploy": "gh-pages -d dist" }, "dependencies": { - "@crisp-e3/sdk": "0.5.10", + "@crisp-e3/sdk": "0.5.11", "@emotion/babel-plugin": "^11.11.0", "@emotion/react": "^11.11.4", "@phosphor-icons/react": "^2.1.4", diff --git a/examples/CRISP/packages/crisp-contracts/package.json b/examples/CRISP/packages/crisp-contracts/package.json index 3d98e33962..6a0548a5fa 100644 --- a/examples/CRISP/packages/crisp-contracts/package.json +++ b/examples/CRISP/packages/crisp-contracts/package.json @@ -1,6 +1,6 @@ { "name": "@crisp-e3/contracts", - "version": "0.5.10", + "version": "0.5.11", "type": "module", "files": [ "contracts", diff --git a/examples/CRISP/packages/crisp-sdk/package.json b/examples/CRISP/packages/crisp-sdk/package.json index 2e96711112..92748b58e9 100644 --- a/examples/CRISP/packages/crisp-sdk/package.json +++ b/examples/CRISP/packages/crisp-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@crisp-e3/sdk", - "version": "0.5.10", + "version": "0.5.11", "type": "module", "author": { "name": "gnosisguild", diff --git a/examples/CRISP/packages/crisp-sdk/tests/helpers.ts b/examples/CRISP/packages/crisp-sdk/tests/helpers.ts index d825f03961..c9f2dea6ec 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/helpers.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/helpers.ts @@ -10,10 +10,7 @@ import { hashLeaf } from '../src/utils' * Generate Merkle tree leaves for tests. Includes leaves for each (address, balance) * pair and pads with unique filler values to reach minSize. */ -export const generateTestLeaves = ( - entries: { address: string; balance: bigint }[], - minSize = 6, -): bigint[] => { +export const generateTestLeaves = (entries: { address: string; balance: bigint }[], minSize = 6): bigint[] => { const leaves = entries.map(({ address, balance }) => hashLeaf(address.toLowerCase(), balance)) const fillerCount = Math.max(0, minSize - leaves.length) for (let i = 0; i < fillerCount; i++) { diff --git a/examples/CRISP/packages/crisp-zk-inputs/package.json b/examples/CRISP/packages/crisp-zk-inputs/package.json index c706b99768..90fbd1de00 100644 --- a/examples/CRISP/packages/crisp-zk-inputs/package.json +++ b/examples/CRISP/packages/crisp-zk-inputs/package.json @@ -2,7 +2,7 @@ "name": "@crisp-e3/zk-inputs", "type": "module", "description": "Core logic to pre-compute CRISP ZK inputs (WASM/JavaScript bindings).", - "version": "0.5.10", + "version": "0.5.11", "license": "LGPL-3.0-only", "repository": { "type": "git", From 0bbe68b3d9661151e1dd422193af6ed1f08091f0 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 18 Feb 2026 11:01:42 +0100 Subject: [PATCH 3/6] chore: update pnpm lock file --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11fcbada06..e4637ce47d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -146,7 +146,7 @@ importers: examples/CRISP/client: dependencies: '@crisp-e3/sdk': - specifier: 0.5.10 + specifier: 0.5.11 version: link:../packages/crisp-sdk '@emotion/babel-plugin': specifier: ^11.11.0 From 75af91a6608f3c73840b846d39e444e43a122b59 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 18 Feb 2026 11:02:23 +0100 Subject: [PATCH 4/6] chore: update CRISPVerifier.sol --- .../contracts/CRISPVerifier.sol | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol b/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol index 04a372f081..96c3e5f6f1 100644 --- a/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol +++ b/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol @@ -8,7 +8,7 @@ pragma solidity >=0.8.21; uint256 constant N = 524288; uint256 constant LOG_N = 19; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 23; -uint256 constant VK_HASH = 0x2349c3d182cb0307ecfa17d2cb91dcf9e7742a8daa67aa106831c761dd22030d; +uint256 constant VK_HASH = 0x1e72a054f11b33c1d4d258f63c292ec8ef2b83604556a1ae434191a7f52ada58; library HonkVerificationKey { function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ @@ -16,76 +16,76 @@ library HonkVerificationKey { logCircuitSize: uint256(19), publicInputsSize: uint256(23), ql: Honk.G1Point({ - x: uint256(0x253c5186ca1b682f7d302cd73b85ea3447a5a2c64fc82b70652f7fc44b203128), - y: uint256(0x14f602413e183c57d3d3124290ab48b6c8b0cbace773766a573ead30bf5c2f4f) + x: uint256(0x21f5c0d0b25a6ce6d61c74e3d145e37acff5a34968e6ca0eb4eb33b0a3fb2c48), + y: uint256(0x2dd97628c4630565372d299afe2715fd4476c47219e3bbbcc604dfa2218ae33f) }), qr: Honk.G1Point({ - x: uint256(0x2d20633d112c7c7efd2821f2860e9d1287f8f68ddd4b0216bb179078497a571c), - y: uint256(0x11928235f13ba529b1ee2f75fe26dda5ed5c79ec36028c09ad2bd8b957546344) + x: uint256(0x0ae4aa30110f2d696cb44b1f0cb9e12a9a973fb9cce0444f9f52b2151c08bf19), + y: uint256(0x055a049e934311bfaa2e75d0967ceb2e6d487596597935b3ec846b548b1c166b) }), qo: Honk.G1Point({ - x: uint256(0x2b570f8e2eccc265f39b9e73778cf6e282554452adb19d2a2406952447d4aa6e), - y: uint256(0x14afd678c64e0dacd64724504e36bca6eccb5a477a5e2987b6543246b8135a90) + x: uint256(0x1fb9fa4171c80201d9e8ee886c878e506d5882af44e3f829cd69181c63162a1f), + y: uint256(0x1fbe5c1bd1f94c8254e6c91f86044f4b4f41b088ab3846513edf141e4664664c) }), q4: Honk.G1Point({ - x: uint256(0x2d7f53a245e9c855325eeb73c8fea788f5b457293cacfd7e3e332210a27a7d2f), - y: uint256(0x182d5ef8840426caa829856d7e800ee98509383d9ee637d1facbc11f7a8fba0d) + x: uint256(0x06aad3f11418cf7552b34fafade9c7c9aa56512353abe3f4f04a8371ef9cafbc), + y: uint256(0x28d1737cf830407816bc69a8785f55a7b406f01b0785d80c979aa726cc8c4294) }), qm: Honk.G1Point({ - x: uint256(0x19d90118c106191d2f2f0350a56547c943c1ed5910d47dd067761099b54d11f3), - y: uint256(0x0f8f03c2def6108448f72f5177b6a2eed01250670efb63ea6f5ab65abc2a6885) + x: uint256(0x020724754e128ca04c5c0af02167fd7f33d2a1ee7ead892bff7e34d76af462cb), + y: uint256(0x1afe760472e856a3f8fc3a032111558d2eaaa771d68666c745707ffa29a643a1) }), qc: Honk.G1Point({ - x: uint256(0x2ead500c79e717f5cdbba88ffef46477c4f30926bf59547a9c141c4158f7c843), - y: uint256(0x027d6b571b395cf61aae9626f172ce4e41e598456f7b8e535c027e9129b2c98e) + x: uint256(0x19dd4e7a50d04ec2a9d132e143018c33b8b67564759841caa5831b33acee1ac0), + y: uint256(0x0993f02b4e80bb14b134883fd3d1ec18d9091a3a13a070eb9cf20fa8afb6e525) }), qLookup: Honk.G1Point({ x: uint256(0x136236e1bfc2af648ac078e134c1b4b9114b11937ebafcdd87f8ca7660715ebb), y: uint256(0x02293c705250462935a653b7b993e13e2e8bc6480c45c84976d526cbdbd071df) }), qArith: Honk.G1Point({ - x: uint256(0x074e7ead679c76d2c2d6d3f158cb522e71d14a169b2b1d0792cdfdad726f17a4), - y: uint256(0x21f9acd78d4150858f29c259c07e4552f6ec4bffac6c1e174cfb90e2e26bd68a) + x: uint256(0x0dc562c3f105ce5682c1a599da52240f3ece1719bf1deeed61e5cf8193a9e8a0), + y: uint256(0x1e6878094b88e8e5ae1ab58eeb0b907133ca4a7cbb6650246f6b9eb5e9857c61) }), qDeltaRange: Honk.G1Point({ - x: uint256(0x0cebfba75751a4eff3d7855bf0f314e523e2c709a75992eb5674a546627248e3), - y: uint256(0x16b715150f4de399940c535932174f5c634c9fd9137a94008bb38f89f1350b4c) + x: uint256(0x065e3c67bfdef11d3a5144140ba6d82c9f8e4514e2e17545ed017c4a25ac143f), + y: uint256(0x038cf59ea9d7f8f5a3944aed793811c3db13052572297e4e97f725a518939640) }), qElliptic: Honk.G1Point({ - x: uint256(0x174dba7fa4e38a55a78e43dda6c3fb81a1293285cf33eeb333ee186bb4331563), - y: uint256(0x0a2ff722359e35596e5abeec834c6cd73f4b06f5f2e837b31364f199f449ca77) + x: uint256(0x14a0839d791c4a9aa354cb8586473442278ea3934373c32b57b35bbe97ba6553), + y: uint256(0x15aef4d0e56a6e88ffa2fb8a6ff665f04ad3be5bd3e1a7fa90f87e1e28e7e284) }), qMemory: Honk.G1Point({ - x: uint256(0x29f116092db8eb3773b017110ca5d3a7b35fa1b064cde91c9e280e20399d7ec2), - y: uint256(0x1f0e39e18ded75167a270d6e31b9a6ad9a9d1b0d67c365eeb172a3fea4d1371e) + x: uint256(0x24051e7a236389b7c193506d547dfcfb346a9580df27c99333145d2721981272), + y: uint256(0x04cfb7f6908a5303846db9a4307290cca5cb98e6a1c959519abba9c32c7b0107) }), qNnf: Honk.G1Point({ - x: uint256(0x19da50fca27dd36a006892b7cb6b36d7cfdac9d65fec6ae7a1a2e092e0988de5), - y: uint256(0x08f96f1d8e37be6dc83c9fbb3b912493841e4e81ab28a60c0fc85337cc4ed977) + x: uint256(0x14e4b7747add290e0f99281e762a9535c42ca8c496553e4ba708011b8848c9d9), + y: uint256(0x2155eab22ae66014ad897cd7410371b7fdb1f5e65d94fa41b503a33083512366) }), qPoseidon2External: Honk.G1Point({ - x: uint256(0x2d20446a333063e66c1a552fecdfe12d0fdddeef0034569ac350f3299b09955a), - y: uint256(0x175e6e8776625134217ce9516a11cbbeab8c4c1c8b41cfb75f43de1f500b07d2) + x: uint256(0x1c1d28ffd2622815f1028b6ab17e1c2c2efe83e5166a6c57938a5a96c3a7148a), + y: uint256(0x201c46abb5727128fe4dde7d62f5098b84c6470f6b08085d44d9bca87b9f38f9) }), qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x090a76cca3c754a2ba40abdc2572fff64645060cfa5c48239a1df4dbb8360828), - y: uint256(0x0499d4c6aed1f78b33a6701459cd069e9bf7262754d2f15074a6c2401950761f) + x: uint256(0x08b01c7c97f11f4df307075d6dbd81290fdaaeba9e87b57c0eb648b43527d6a9), + y: uint256(0x02bd9e6722331239be005c82cf18f7ccc471055ccad98b85081368332237cc33) }), s1: Honk.G1Point({ - x: uint256(0x012609fcd0fa67214d78f2d5f5e3e2b459a3c4b21531646de7d51d9cd5383aa9), - y: uint256(0x21394cebbe5f66f3aecf43d318b3cb9fc7640825baba0c5a5c190a20ceeb5edb) + x: uint256(0x0378ec94ac4dcecff53262a5b500bae08f56a37153f0465b8f5ae5727be587b1), + y: uint256(0x17542b39e79a0e8d2eb759c9bd9cf7f96d77d754566e1c665e0c201c1f422133) }), s2: Honk.G1Point({ - x: uint256(0x080c7f024d9c813c60c84fc3e9bcad553c51f276d55e8d7a03c044b41cedef36), - y: uint256(0x2109db10b2da84ec2c4a1b62689d0952ababe94915293dfee09f1083010e5cfb) + x: uint256(0x059f3a98b2e34042a051247eca0d5da4bcb048c8c5e378b33722da4ce96396fa), + y: uint256(0x1566123757111fdee7c72c1c54bcdca8bd0d4279cdadd705229b4151f35c3605) }), s3: Honk.G1Point({ - x: uint256(0x2f7d6b77cd5c3ee56c255be61d0e90fd7da5898a7ce4850aaf1060513cb7cd9d), - y: uint256(0x2ce5fdacbf91fc3358f4eba637d60f109f70fa929a6a1bc1f9d86e7856b87dcd) + x: uint256(0x28469b1cae4e731a184dd30dc4995dfe7197ee236723a57b1f9ef495528d8ac3), + y: uint256(0x14842ec4dcfc16383da132bc593ea783478f52a3dd5e2247b6110f920b89ecec) }), s4: Honk.G1Point({ - x: uint256(0x1db1540beb4dc13e8519b82205e9b4cb48833040c8b322d626df57e078afecc4), - y: uint256(0x28eb12f9f02ac092326277365f9eeaff536b8dbf771f9ace22d0e122f922196a) + x: uint256(0x27263eb5d5052d6d619815920298f66bddbf13442145578fedf69a0d3e527688), + y: uint256(0x268fb51fa97fdcc44ae7d8f420581d200f01c082e6004cd12da703562bb5ed44) }), t1: Honk.G1Point({ x: uint256(0x1f16b037f0b4c96ea2a30a118a44e139881c0db8a4d6c9fde7db5c1c1738e61f), @@ -104,28 +104,28 @@ library HonkVerificationKey { y: uint256(0x2d7e8c1ecb92e2490049b50efc811df63f1ca97e58d5e82852dbec0c29715d71) }), id1: Honk.G1Point({ - x: uint256(0x0a10b2e79989b15e3a69bd491cdae007b0bf9c82d9be3d7867b33e2287b91dac), - y: uint256(0x257794eaba7a0e7aed16e03d4d8c4cf7d878b029f4ea45c559bbc19b5ec4d1de) + x: uint256(0x18846eadee71cfdb37254ea322e3f91e0e6e6a4ad479a63be51cf1f133e073a4), + y: uint256(0x0f6ac81bc59337c2c226f3d1c04af834105fd093c968bb45238cf143da34cd88) }), id2: Honk.G1Point({ - x: uint256(0x25791b725ea7c712316ac4ffe10fdfcf37bd2b8f9d730ba2e26fa709bc7c3ae0), - y: uint256(0x15fba3e7928d36dc4dd6d6ff198b928c7246310974d4f74161cfd2781b9d3686) + x: uint256(0x212ce533844849396d1a3365d6fd63c81312814f16a7432b7395a414248f25e2), + y: uint256(0x283d5def216705b320614654a586c72b1d50605bb67d08c43373974a23649709) }), id3: Honk.G1Point({ - x: uint256(0x270be32427e6404801b9b014f983d80acf18b828c6cf049f430d98fbe34e85e0), - y: uint256(0x2e7d27a99c4b574557ea6117c390c65072cdfa51a08621141ae26d7e143d0669) + x: uint256(0x238a6ba0f312e86aa3349328689524ac5524b92119a556397f6396702300ca1f), + y: uint256(0x218e36920fd847dca92888ecb71899f543a8b18c7b93d285fcd3b7245ffbb603) }), id4: Honk.G1Point({ - x: uint256(0x18e2902febdb3e45358fe65981f338a7ffd1b16edd917de670fabe5d15307e20), - y: uint256(0x2f6f36f307a0f7012dc3918795312e71a1d4752966c2bc3151e5f5b3151fc236) + x: uint256(0x127316510c7d83c3d499c53c91fae2867b44b48d265644bde10f304a9371fd7f), + y: uint256(0x1d7d27d50252e14fd680d9892ca1be4699afd39ca948f156661a922bcd73d0d5) }), lagrangeFirst: Honk.G1Point({ x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ - x: uint256(0x12b14f226e24a52e0bc85bc5478c806023107f257430aae49136064dd3315c60), - y: uint256(0x0dfe490435cf839caf81df5c8a164afc5e3c8646f36bf27c19850b3f913edce4) + x: uint256(0x23f061c4a4ac013b20babeb51654c432752a23fde2f89dab38768dddee9a50d2), + y: uint256(0x1dd756be22a06912566da16400a17eb8b2e4b02e6bed9d90ad8784c081a40c72) }) }); return vk; From c8c1b8b20b0fc0ba3a1e2da9faf9c32e0bd47749 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 18 Feb 2026 11:04:47 +0100 Subject: [PATCH 5/6] test(crisp): add regression tests for non-divisible D remainder logic - 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 --- examples/CRISP/circuits/src/utils.nr | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/examples/CRISP/circuits/src/utils.nr b/examples/CRISP/circuits/src/utils.nr index caf4b77197..a036d0f946 100644 --- a/examples/CRISP/circuits/src/utils.nr +++ b/examples/CRISP/circuits/src/utils.nr @@ -256,6 +256,18 @@ fn test_zero_vote_passes() { check_coefficient_values_with_balance(pol, 1, 100, 3); } +#[test] +fn test_non_divisible_d_vote_passes() { + // D=100, num_options=3 => D % num_options != 0 (remainder=1) + // segment_size=33, remainder=1; option 0 at circuit_segment 2 [67..99] + let mut coeffs = [0; 100]; + coeffs[67] = 1; // Option 0, bit 0 (2^0) + coeffs[69] = 1; // Option 0, bit 2 (2^2) => vote value 5 + + let pol = Polynomial { coefficients: coeffs }; + check_coefficient_values_with_balance(pol, 1, 100, 3); +} + #[test(should_fail)] fn test_invalid_coefficient_fails() { let mut coeffs = [0; 100]; @@ -279,3 +291,13 @@ fn test_check_coefficient_zero_fails() { let pol = Polynomial { coefficients: coeffs }; check_coefficient_zero(pol, 3); } + +#[test(should_fail)] +fn test_check_coefficient_zero_non_divisible_d_fails() { + // D=100, num_options=3 => remainder=1; option 2 at circuit_segment 0 [1..33] + let mut coeffs = [0; 100]; + coeffs[1] = 1; // Option 2 LSB + + let pol = Polynomial { coefficients: coeffs }; + check_coefficient_zero(pol, 3); +} From f20a21c4d50907d5e334ef90018ebd0bf4e673f2 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Wed, 18 Feb 2026 11:49:56 +0100 Subject: [PATCH 6/6] chore: update CRISPVerifier.sol --- .../contracts/CRISPVerifier.sol | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol b/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol index 96c3e5f6f1..34d44ed223 100644 --- a/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol +++ b/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol @@ -8,7 +8,7 @@ pragma solidity >=0.8.21; uint256 constant N = 524288; uint256 constant LOG_N = 19; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 23; -uint256 constant VK_HASH = 0x1e72a054f11b33c1d4d258f63c292ec8ef2b83604556a1ae434191a7f52ada58; +uint256 constant VK_HASH = 0x0b9c1b2dd5f380cd949f20a3a3f09c947df53c467285680777412ea4868a8d18; library HonkVerificationKey { function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ @@ -16,76 +16,76 @@ library HonkVerificationKey { logCircuitSize: uint256(19), publicInputsSize: uint256(23), ql: Honk.G1Point({ - x: uint256(0x21f5c0d0b25a6ce6d61c74e3d145e37acff5a34968e6ca0eb4eb33b0a3fb2c48), - y: uint256(0x2dd97628c4630565372d299afe2715fd4476c47219e3bbbcc604dfa2218ae33f) + x: uint256(0x240f688502a6acfbcaeaaa0846cd10fca763cc54deb740784eb4d79ed50a478e), + y: uint256(0x1f085373708e94340f4f4741a11126be4f87589358cebb953ee6dddd1dca1172) }), qr: Honk.G1Point({ - x: uint256(0x0ae4aa30110f2d696cb44b1f0cb9e12a9a973fb9cce0444f9f52b2151c08bf19), - y: uint256(0x055a049e934311bfaa2e75d0967ceb2e6d487596597935b3ec846b548b1c166b) + x: uint256(0x03ca30044543a7c02e08505b4ed256125807d7bda68712c65bd674e7be18907e), + y: uint256(0x223b9b4b0bfd2328aa898531ee94dde61fcaa9133773dc33184e167bb33fb343) }), qo: Honk.G1Point({ - x: uint256(0x1fb9fa4171c80201d9e8ee886c878e506d5882af44e3f829cd69181c63162a1f), - y: uint256(0x1fbe5c1bd1f94c8254e6c91f86044f4b4f41b088ab3846513edf141e4664664c) + x: uint256(0x01df0faac1b219c2932d044ff18952ccfdb0da9f3ca8d6d98d19d3b5dcaa75a8), + y: uint256(0x2676231938eab7bf9e8b609d039dc7f0e9c28c8f94cf0ffcc8ce0ee2c40966e5) }), q4: Honk.G1Point({ - x: uint256(0x06aad3f11418cf7552b34fafade9c7c9aa56512353abe3f4f04a8371ef9cafbc), - y: uint256(0x28d1737cf830407816bc69a8785f55a7b406f01b0785d80c979aa726cc8c4294) + x: uint256(0x05150a77def0a11380c5a171f15aac571dc0737a55aeddde2fdb147742cfccd3), + y: uint256(0x1fab3e3345368910a19c18ce2c9649a27cc721d406fdec42e02204ac277161cb) }), qm: Honk.G1Point({ - x: uint256(0x020724754e128ca04c5c0af02167fd7f33d2a1ee7ead892bff7e34d76af462cb), - y: uint256(0x1afe760472e856a3f8fc3a032111558d2eaaa771d68666c745707ffa29a643a1) + x: uint256(0x264abf2ea58ab66dc9310c0d6360bd1fe8d7408259cedfbbeb119d6004583d53), + y: uint256(0x089c345678f0ed3d16009853498f4189927c1c49eaab388e535b4ed5abc179ba) }), qc: Honk.G1Point({ - x: uint256(0x19dd4e7a50d04ec2a9d132e143018c33b8b67564759841caa5831b33acee1ac0), - y: uint256(0x0993f02b4e80bb14b134883fd3d1ec18d9091a3a13a070eb9cf20fa8afb6e525) + x: uint256(0x24bfc2a0618dd709363c0f726d12b55f556749f83ce5b7ccdfe214d2761cca2e), + y: uint256(0x237c95ed74cf4ba0eedec3e8172cc943b4ea2b171806c3a528d1f13f83816180) }), qLookup: Honk.G1Point({ x: uint256(0x136236e1bfc2af648ac078e134c1b4b9114b11937ebafcdd87f8ca7660715ebb), y: uint256(0x02293c705250462935a653b7b993e13e2e8bc6480c45c84976d526cbdbd071df) }), qArith: Honk.G1Point({ - x: uint256(0x0dc562c3f105ce5682c1a599da52240f3ece1719bf1deeed61e5cf8193a9e8a0), - y: uint256(0x1e6878094b88e8e5ae1ab58eeb0b907133ca4a7cbb6650246f6b9eb5e9857c61) + x: uint256(0x1f15325c29f480d9fa82d2c7ed4994844919e3d613ba98aca98155abbe8af1d0), + y: uint256(0x1eaf5badfb1f6604278dbe5b8cc935bc0cf19eaef7b77c7ca2f231a65ca94c83) }), qDeltaRange: Honk.G1Point({ - x: uint256(0x065e3c67bfdef11d3a5144140ba6d82c9f8e4514e2e17545ed017c4a25ac143f), - y: uint256(0x038cf59ea9d7f8f5a3944aed793811c3db13052572297e4e97f725a518939640) + x: uint256(0x01319d2844fd5491e4140d6f0c43f4b708e1192c5bbdd2c0a53a7da62983efd8), + y: uint256(0x164d72be753ed477363f4af7c5e0110d46c83ec7ebf4b753eb228e4f42b37ac8) }), qElliptic: Honk.G1Point({ - x: uint256(0x14a0839d791c4a9aa354cb8586473442278ea3934373c32b57b35bbe97ba6553), - y: uint256(0x15aef4d0e56a6e88ffa2fb8a6ff665f04ad3be5bd3e1a7fa90f87e1e28e7e284) + x: uint256(0x0672a9cbd7c1f9768e8d307bdd295145b3547b2783065b521a23487b927c0cfb), + y: uint256(0x1252d1d2d8b7841d1e92ae3167104d924eabd7607bb5a655ad42cc3fedcd25f3) }), qMemory: Honk.G1Point({ - x: uint256(0x24051e7a236389b7c193506d547dfcfb346a9580df27c99333145d2721981272), - y: uint256(0x04cfb7f6908a5303846db9a4307290cca5cb98e6a1c959519abba9c32c7b0107) + x: uint256(0x23d2fc90f4b3243cf25c79e052fddb5489397999ed49771b59da5ac27677647b), + y: uint256(0x0523ed03b53e8f80c7588938b2e5487f6cc4a63918d3f1263cfab7a7ad61737d) }), qNnf: Honk.G1Point({ - x: uint256(0x14e4b7747add290e0f99281e762a9535c42ca8c496553e4ba708011b8848c9d9), - y: uint256(0x2155eab22ae66014ad897cd7410371b7fdb1f5e65d94fa41b503a33083512366) + x: uint256(0x0b3512e728f8ace54f9a6d83334f4f35e073c641b843a6ac52d4d99917b02349), + y: uint256(0x1c89ed6a83ea8bdce1e560ab0204ee0939e1784dffc427cc11e321b61bed74c6) }), qPoseidon2External: Honk.G1Point({ - x: uint256(0x1c1d28ffd2622815f1028b6ab17e1c2c2efe83e5166a6c57938a5a96c3a7148a), - y: uint256(0x201c46abb5727128fe4dde7d62f5098b84c6470f6b08085d44d9bca87b9f38f9) + x: uint256(0x0b6006bc7b12f44177d3b59d197fe11d2fc41599f6f1fde44c7f268dd32d337e), + y: uint256(0x0c45abdec36bd5b61f2edca097daccb7a81455a2d88496a6d1a72aef33b8b7b0) }), qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x08b01c7c97f11f4df307075d6dbd81290fdaaeba9e87b57c0eb648b43527d6a9), - y: uint256(0x02bd9e6722331239be005c82cf18f7ccc471055ccad98b85081368332237cc33) + x: uint256(0x0be8cb27fcd19968dfa54fd667491a0e95b7a92518a9a82b506dfdd9d8bb0323), + y: uint256(0x01c52d04c4d69fcaecdacb0b9dba750320704ae6375f8ef985a9fc2cb555ac48) }), s1: Honk.G1Point({ - x: uint256(0x0378ec94ac4dcecff53262a5b500bae08f56a37153f0465b8f5ae5727be587b1), - y: uint256(0x17542b39e79a0e8d2eb759c9bd9cf7f96d77d754566e1c665e0c201c1f422133) + x: uint256(0x1a7257ea10514736801482f89408f5925618409f43bf217880f9ba8564708b3c), + y: uint256(0x21bdeab9c20c3e3d0727429b2a6efdbd7473e1b239ef02d9581de8499444a82c) }), s2: Honk.G1Point({ - x: uint256(0x059f3a98b2e34042a051247eca0d5da4bcb048c8c5e378b33722da4ce96396fa), - y: uint256(0x1566123757111fdee7c72c1c54bcdca8bd0d4279cdadd705229b4151f35c3605) + x: uint256(0x1cdd60998e69b08a3178183583434d5d1747fe2b59d53198cee0189184ef4137), + y: uint256(0x0e1ec93697a66ada78411a4343a73a94162688bc96ceec69e06b6b292ad6ae15) }), s3: Honk.G1Point({ - x: uint256(0x28469b1cae4e731a184dd30dc4995dfe7197ee236723a57b1f9ef495528d8ac3), - y: uint256(0x14842ec4dcfc16383da132bc593ea783478f52a3dd5e2247b6110f920b89ecec) + x: uint256(0x2e3739fca228a028616288bded555bda201546582e0fbfb15ba1d79ecedb99ff), + y: uint256(0x2d3fa85df312d814ce05e6424c0789aabd29780e2751d06191b5c19385c495e8) }), s4: Honk.G1Point({ - x: uint256(0x27263eb5d5052d6d619815920298f66bddbf13442145578fedf69a0d3e527688), - y: uint256(0x268fb51fa97fdcc44ae7d8f420581d200f01c082e6004cd12da703562bb5ed44) + x: uint256(0x2cc67ab5542d9dcb11e2f47c61991d3b8c1530be0fb65ae78bb579575472be37), + y: uint256(0x1a9453a6379fcd5d9357de3c3c92f09a37a566d01452ccbc053a79cbf5d3062b) }), t1: Honk.G1Point({ x: uint256(0x1f16b037f0b4c96ea2a30a118a44e139881c0db8a4d6c9fde7db5c1c1738e61f), @@ -104,28 +104,28 @@ library HonkVerificationKey { y: uint256(0x2d7e8c1ecb92e2490049b50efc811df63f1ca97e58d5e82852dbec0c29715d71) }), id1: Honk.G1Point({ - x: uint256(0x18846eadee71cfdb37254ea322e3f91e0e6e6a4ad479a63be51cf1f133e073a4), - y: uint256(0x0f6ac81bc59337c2c226f3d1c04af834105fd093c968bb45238cf143da34cd88) + x: uint256(0x242bbe50c5583ccc277f71ef51aa243eda251285787baac14b27a1f4cc411edf), + y: uint256(0x154f6eb3c9511184ca082c61b12292332f5a4d3a20f6612c8b337b988df6441e) }), id2: Honk.G1Point({ - x: uint256(0x212ce533844849396d1a3365d6fd63c81312814f16a7432b7395a414248f25e2), - y: uint256(0x283d5def216705b320614654a586c72b1d50605bb67d08c43373974a23649709) + x: uint256(0x28516035c0e7f925849fc922c1cd3cc13d6da9c37e723131dbff7c7bb672d762), + y: uint256(0x25a236e395d2e5b5b6bfa4c732982c53aeb626cd4f84624ced88724a2b271820) }), id3: Honk.G1Point({ - x: uint256(0x238a6ba0f312e86aa3349328689524ac5524b92119a556397f6396702300ca1f), - y: uint256(0x218e36920fd847dca92888ecb71899f543a8b18c7b93d285fcd3b7245ffbb603) + x: uint256(0x296663882374f237a8fc9915de61b3a2f558a731cb10204dfa257273d90f6b93), + y: uint256(0x2b20baa201e9ddcbbf5eba8a2d5cff0cb1b5e0ba96be4bbd3b0da781cca7b47d) }), id4: Honk.G1Point({ - x: uint256(0x127316510c7d83c3d499c53c91fae2867b44b48d265644bde10f304a9371fd7f), - y: uint256(0x1d7d27d50252e14fd680d9892ca1be4699afd39ca948f156661a922bcd73d0d5) + x: uint256(0x1438d3dc0bd7d25324cfddbbdbe88df7782c33920c2276cafa2b5e316639721b), + y: uint256(0x0bc0958c27e76138920ac5aa682f2b724456ebf2bd169a863244b9cc153639db) }), lagrangeFirst: Honk.G1Point({ x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ - x: uint256(0x23f061c4a4ac013b20babeb51654c432752a23fde2f89dab38768dddee9a50d2), - y: uint256(0x1dd756be22a06912566da16400a17eb8b2e4b02e6bed9d90ad8784c081a40c72) + x: uint256(0x0ffb0f958447e12cda902c71cb70f85d2ebfcd4b93f50569c2f00eff7e60368e), + y: uint256(0x1230b28595aee83bc86d1c7fb3d0fc726604d0e86455cf648b9ddaa89c173ce4) }) }); return vk;