From f26ae04f74c68bf6906ab1c9d4eb43f4a2ad2a6d Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Tue, 28 Oct 2025 11:32:05 +0000 Subject: [PATCH 1/4] feat: add merkle tree proof inputs to circuit and sdk --- examples/CRISP/circuits/src/main.nr | 10 +++++- examples/CRISP/circuits/src/merkle_tree.nr | 35 +++++++++++++++++++ .../CRISP/packages/crisp-sdk/src/types.ts | 3 ++ .../CRISP/packages/crisp-sdk/src/utils.ts | 24 +++++++++++-- examples/CRISP/packages/crisp-sdk/src/vote.ts | 14 ++++++-- .../packages/crisp-sdk/tests/constants.ts | 15 ++++++++ .../packages/crisp-sdk/tests/utils.test.ts | 21 +++++------ .../packages/crisp-sdk/tests/vote.test.ts | 14 ++++++-- 8 files changed, 115 insertions(+), 21 deletions(-) diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index 4371f59745..55e84dec3f 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -33,6 +33,7 @@ fn main( signature: [u8; 64], hashed_message: [u8; 32], balance: Field, + merkle_root: pub Field, merkle_proof_length: u32, merkle_proof_indices: [u1; 20], merkle_proof_siblings: [Field; 20], @@ -44,7 +45,7 @@ fn main( let address = address_to_field(derive_address(public_key_x, public_key_y)); // Then get Merkle root. - let merkle_root = get_merkle_root( + let merkle_root_calculated = get_merkle_root( address, balance, merkle_proof_length, @@ -52,6 +53,13 @@ fn main( merkle_proof_siblings, ); + // compare the merkle root with the param to confirm whether it's the voter + // or a masker + let mut is_voter = false; + if (merkle_root_calculated == merkle_root) { + is_voter = true; + } + // then verify that the vote was encrypted correctly let circuit: Greco<2048, 1, 54, 54, 5, 5, 20, 20, 54, 16, 54> = Greco::new( params, diff --git a/examples/CRISP/circuits/src/merkle_tree.nr b/examples/CRISP/circuits/src/merkle_tree.nr index 1db90d50f9..bad53ad9af 100644 --- a/examples/CRISP/circuits/src/merkle_tree.nr +++ b/examples/CRISP/circuits/src/merkle_tree.nr @@ -42,3 +42,38 @@ fn test_get_merkle_root() { assert(merkle_root == expected); } + +#[test] +fn test_get_merkle_root_ts_sdk() { + let address = 0x1234567890123456789012345678901234567890; + let balance = 1000; + + let depth = 4; + let indices: [u1; 20] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + let siblings: [Field; 20] = [ + 3427403932201889290042771220465494458496044723860771244846412090114641612933, + 142173982782527023041821996079117549632814652929360547003021612347642943458, + 5984039385283196232308319035002972848802042601279200765710389913462508845879, + 13230920196039626330844247773673397239708271370016925050843639396057862269188, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]; + + let merkle_root = get_merkle_root(address, balance, depth, indices, siblings); + let expected = 1459018155640011149834119814743866717977769794914747043488779523926892000355; + assert(merkle_root == expected); +} diff --git a/examples/CRISP/packages/crisp-sdk/src/types.ts b/examples/CRISP/packages/crisp-sdk/src/types.ts index 6e9e2e547a..13dabfda38 100644 --- a/examples/CRISP/packages/crisp-sdk/src/types.ts +++ b/examples/CRISP/packages/crisp-sdk/src/types.ts @@ -60,6 +60,8 @@ export interface IMerkleProof { leaf: bigint index: number proof: LeanIMTMerkleProof + length: number + indices: number[] } /** @@ -163,6 +165,7 @@ export interface CRISPCircuitInputs { signature: string[] hashed_message: string[] balance: string + merkle_root: string merkle_proof_length: string merkle_proof_indices: string[] merkle_proof_siblings: string[] diff --git a/examples/CRISP/packages/crisp-sdk/src/utils.ts b/examples/CRISP/packages/crisp-sdk/src/utils.ts index f05bddef2b..e30c3089bd 100644 --- a/examples/CRISP/packages/crisp-sdk/src/utils.ts +++ b/examples/CRISP/packages/crisp-sdk/src/utils.ts @@ -34,8 +34,9 @@ export const generateMerkleTree = (leaves: bigint[]): LeanIMT => { * @param balance The voter's balance * @param address The voter's address * @param leaves The leaves of the Merkle tree + * @param maxDepth The maximum depth of the Merkle tree */ -export const generateMerkleProof = (threshold: number, balance: number, address: string, leaves: bigint[]): IMerkleProof => { +export const generateMerkleProof = (threshold: number, balance: number, address: string, leaves: bigint[], maxDepth: number): IMerkleProof => { if (balance < threshold) { throw new Error('Balance is below the threshold') } @@ -52,10 +53,29 @@ export const generateMerkleProof = (threshold: number, balance: number, address: const proof = tree.generateProof(index) + // Pad siblings with zeros + const paddedSiblings = [ + ...proof.siblings, + ...Array(maxDepth - proof.siblings.length).fill(0n) + ] + + // Pad indices with zeros + const indices = proof.siblings.map((_, i) => (index >> i) & 1) + const paddedIndices = [ + ...indices, + ...Array(maxDepth - indices.length).fill(0) + ] + return { leaf, index, - proof, + proof: { + ...proof, + siblings: paddedSiblings, + }, + // Original length before padding + length: proof.siblings.length, + indices: paddedIndices, } } diff --git a/examples/CRISP/packages/crisp-sdk/src/vote.ts b/examples/CRISP/packages/crisp-sdk/src/vote.ts index 18135b59d2..ca36377c7f 100644 --- a/examples/CRISP/packages/crisp-sdk/src/vote.ts +++ b/examples/CRISP/packages/crisp-sdk/src/vote.ts @@ -5,7 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { ZKInputsGenerator } from '@enclave/crisp-zk-inputs' -import { BFVParams, type CRISPCircuitInputs, type IVote, VotingMode } from './types' +import { BFVParams, type CRISPCircuitInputs, type IMerkleProof, type IVote, VotingMode } from './types' import { toBinary } from './utils' import { MAXIMUM_VOTE_VALUE, DEFAULT_BFV_PARAMS } from './constants' import { extractSignature } from './signature' @@ -170,12 +170,12 @@ export const encryptVoteAndGenerateCRISPInputs = async ( // the rest of the public and private inputs will need to be generated before calling the circuit to generate the CRISP proof return { ...crispInputs, - // @todo fill the rest of the inputs needed for CRISP public_key_x: [], public_key_y: [], signature: [], hashed_message: [], balance: '0', + merkle_root: '0', merkle_proof_length: '0', merkle_proof_indices: [], merkle_proof_siblings: [], @@ -184,16 +184,19 @@ export const encryptVoteAndGenerateCRISPInputs = async ( /** * Generate the CRISP circuit inputs by extracting signature components and adding them to the partial inputs - * @todo Add the merkle tree inputs too * @param partialInputs The partial CRISP circuit inputs * @param signature The voter's signature * @param message The signed message + * @param merkleData The voter's Merkle proof data + * @param balance The voter's balance * @returns The complete CRISP circuit inputs */ export const generateCRISPInputs = async ( partialInputs: CRISPCircuitInputs, signature: `0x${string}`, message: string, + merkleData: IMerkleProof, + balance: number, ): Promise => { const { hashed_message, pub_key_x, pub_key_y, signature: extractedSignature } = await extractSignature(message, signature) @@ -203,5 +206,10 @@ export const generateCRISPInputs = async ( public_key_x: Array.from(pub_key_x).map((b) => b.toString()), public_key_y: Array.from(pub_key_y).map((b) => b.toString()), signature: Array.from(extractedSignature).map((b) => b.toString()), + merkle_proof_length: merkleData.length.toString(), + merkle_proof_indices: merkleData.indices.map((i) => i.toString()), + merkle_proof_siblings: merkleData.proof.siblings.map((s) => s.toString()), + merkle_root: merkleData.proof.root.toString(), + balance: balance.toString(), } } diff --git a/examples/CRISP/packages/crisp-sdk/tests/constants.ts b/examples/CRISP/packages/crisp-sdk/tests/constants.ts index 2261a03a66..e57e54d0cf 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/constants.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/constants.ts @@ -10,3 +10,18 @@ export const MESSAGE = 'Vote for round 0' export const SIGNATURE = '0x1641431d0ed3fd86814f026da62e11434b53c6a85162fea7f99218bf3c307dec7f361c235f07b658780afd91a5c9d68d6a4b14d5eb0511f6688d3e91140eec121b' export const VOTE = { yes: 10n, no: 0n } + +export const LEAVES = [ + 5744770974032406598001112375731623179326875761382288642755141437508907349272n, + 3427403932201889290042771220465494458496044723860771244846412090114641612933n, + 2345497045557010425836789889102383730351985634595867684180656101146305513188n, + 16333345332467701633145728130049893741621620444233378624735176290889202182510n, + 7181270461626747418571270128374653017252322331096300145108059576488978730010n, + 10652491433271864584861721641100216833021493709204436430564590071100900917428n, + 20897323534773557936302566533365152086278248537724825381610164527249213072770n, + 4720511075913887710172192848636076523165432993226978491435561065722130431597n, + 14131255645332550266535358189863475289290770471998199141522479556687499890181n, + 4935126455042678253283865781346660214959064333962120811317994370162001200675n +] + +export const MAX_DEPTH = 20; diff --git a/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts b/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts index 182b7bd67b..d489407a6a 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts @@ -4,17 +4,11 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import { expect, describe, it, beforeAll } from 'vitest' +import { expect, describe, it } from 'vitest' import { generateMerkleProof, generateMerkleTree, hashLeaf } from '../src/utils' -import { getTreeData } from '../src' -import { CRISP_SERVER_URL } from './constants' +import { LEAVES, MAX_DEPTH } from './constants' describe('Utils', () => { - let leaves: bigint[] - - beforeAll(async () => { - leaves = await getTreeData(CRISP_SERVER_URL, 0) - }) describe('hashLeaf', () => { it('should return a bigint hash of the two values', () => { @@ -25,7 +19,7 @@ describe('Utils', () => { describe('generateMerkleTree', () => { it('should generate a merkle tree', () => { - const tree = generateMerkleTree(leaves) + const tree = generateMerkleTree(LEAVES) expect(tree.root).toBeDefined() }) }) @@ -34,12 +28,15 @@ describe('Utils', () => { const address = '0x1234567890123456789012345678901234567890' const balance = 1000 it('should generate a merkle proof for a leaf', () => { - const proof = generateMerkleProof(0, balance, address, leaves) + const proof = generateMerkleProof(0, balance, address, LEAVES, MAX_DEPTH) expect(proof.leaf).toBe(hashLeaf(address, balance.toString())) + + expect(proof.length).toBe(4) + expect(proof.indices.length).toBe(MAX_DEPTH) }) it('should throw if the leaf does not exist in the tree', () => { - expect(() => generateMerkleProof(0, balance, address, [])).toThrow('Leaf not found in the tree') - expect(() => generateMerkleProof(0, 999, address, leaves)).toThrow('Leaf not found in the tree') + expect(() => generateMerkleProof(0, balance, address, [], MAX_DEPTH)).toThrow('Leaf not found in the tree') + expect(() => generateMerkleProof(0, 999, address, LEAVES, MAX_DEPTH)).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 11e3c3998d..602196464f 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -16,9 +16,10 @@ import { validateVote, } from '../src/vote' import { BFVParams, VotingMode } from '../src/types' -import { DEFAULT_BFV_PARAMS, MAXIMUM_VOTE_VALUE } from '../src' +import { DEFAULT_BFV_PARAMS, generateMerkleProof, MAXIMUM_VOTE_VALUE } from '../src' -import { MESSAGE, SIGNATURE, VOTE } from './constants' +import { LEAVES, MAX_DEPTH, MESSAGE, SIGNATURE, VOTE } from './constants' +import exp from 'constants' describe('Vote', () => { const votingPower = 10n @@ -143,10 +144,12 @@ describe('Vote', () => { }) describe('generateCRISPInputs', () => { + const votingPowerLeaf = 1000; + const merkleProof = generateMerkleProof(0, votingPowerLeaf, '0x1234567890123456789012345678901234567890', LEAVES, MAX_DEPTH); it('should add the remaining inputs to the CRISP inputs object', async () => { const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower) const partialInputs = await encryptVoteAndGenerateCRISPInputs(encodedVote, publicKey, previousCiphertext) - const crispInputs = await generateCRISPInputs(partialInputs, SIGNATURE, MESSAGE) + const crispInputs = await generateCRISPInputs(partialInputs, SIGNATURE, MESSAGE, merkleProof, votingPowerLeaf) expect(crispInputs.ct_add).toBeInstanceOf(Object) expect(crispInputs.params).toBeInstanceOf(Object) @@ -161,6 +164,11 @@ describe('Vote', () => { expect(crispInputs.public_key_x).toBeInstanceOf(Array) expect(crispInputs.public_key_y).toBeInstanceOf(Array) expect(crispInputs.signature).toBeInstanceOf(Array) + expect(crispInputs.merkle_proof_indices).toBeDefined() + expect(crispInputs.merkle_proof_siblings).toBeDefined() + expect(crispInputs.merkle_proof_length).toBeDefined() + expect(crispInputs.merkle_root).toBeDefined() + expect(crispInputs.balance).toBe(votingPowerLeaf.toString()) }) }) }) From e3215f29adba618619359dc334913410e59ce55c Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:21:20 +0000 Subject: [PATCH 2/4] chore: remove unused import --- examples/CRISP/packages/crisp-sdk/tests/vote.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts index 602196464f..a1e7fa7eaf 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -19,7 +19,6 @@ import { BFVParams, VotingMode } from '../src/types' import { DEFAULT_BFV_PARAMS, generateMerkleProof, MAXIMUM_VOTE_VALUE } from '../src' import { LEAVES, MAX_DEPTH, MESSAGE, SIGNATURE, VOTE } from './constants' -import exp from 'constants' describe('Vote', () => { const votingPower = 10n From 1f4df03a1d2bb9d85897f88e30ee6247f3acd42b Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Tue, 28 Oct 2025 16:55:26 +0000 Subject: [PATCH 3/4] chore: use bigint for balance --- .../CRISP/packages/crisp-sdk/src/types.ts | 2 +- .../CRISP/packages/crisp-sdk/src/utils.ts | 20 +++++++++---------- examples/CRISP/packages/crisp-sdk/src/vote.ts | 2 +- .../packages/crisp-sdk/tests/constants.ts | 4 ++-- .../packages/crisp-sdk/tests/utils.test.ts | 5 ++--- .../packages/crisp-sdk/tests/vote.test.ts | 6 +++--- 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/examples/CRISP/packages/crisp-sdk/src/types.ts b/examples/CRISP/packages/crisp-sdk/src/types.ts index 13dabfda38..1008cad09c 100644 --- a/examples/CRISP/packages/crisp-sdk/src/types.ts +++ b/examples/CRISP/packages/crisp-sdk/src/types.ts @@ -60,7 +60,7 @@ export interface IMerkleProof { leaf: bigint index: number proof: LeanIMTMerkleProof - length: number + length: number indices: number[] } diff --git a/examples/CRISP/packages/crisp-sdk/src/utils.ts b/examples/CRISP/packages/crisp-sdk/src/utils.ts index e30c3089bd..370def35a2 100644 --- a/examples/CRISP/packages/crisp-sdk/src/utils.ts +++ b/examples/CRISP/packages/crisp-sdk/src/utils.ts @@ -36,7 +36,13 @@ export const generateMerkleTree = (leaves: bigint[]): LeanIMT => { * @param leaves The leaves of the Merkle tree * @param maxDepth The maximum depth of the Merkle tree */ -export const generateMerkleProof = (threshold: number, balance: number, address: string, leaves: bigint[], maxDepth: number): IMerkleProof => { +export const generateMerkleProof = ( + threshold: number, + balance: bigint, + address: string, + leaves: bigint[], + maxDepth: number, +): IMerkleProof => { if (balance < threshold) { throw new Error('Balance is below the threshold') } @@ -54,17 +60,11 @@ export const generateMerkleProof = (threshold: number, balance: number, address: const proof = tree.generateProof(index) // Pad siblings with zeros - const paddedSiblings = [ - ...proof.siblings, - ...Array(maxDepth - proof.siblings.length).fill(0n) - ] + const paddedSiblings = [...proof.siblings, ...Array(maxDepth - proof.siblings.length).fill(0n)] // Pad indices with zeros const indices = proof.siblings.map((_, i) => (index >> i) & 1) - const paddedIndices = [ - ...indices, - ...Array(maxDepth - indices.length).fill(0) - ] + const paddedIndices = [...indices, ...Array(maxDepth - indices.length).fill(0)] return { leaf, @@ -74,7 +74,7 @@ export const generateMerkleProof = (threshold: number, balance: number, address: siblings: paddedSiblings, }, // Original length before padding - length: proof.siblings.length, + length: proof.siblings.length, indices: paddedIndices, } } diff --git a/examples/CRISP/packages/crisp-sdk/src/vote.ts b/examples/CRISP/packages/crisp-sdk/src/vote.ts index ca36377c7f..2fdd810c9d 100644 --- a/examples/CRISP/packages/crisp-sdk/src/vote.ts +++ b/examples/CRISP/packages/crisp-sdk/src/vote.ts @@ -196,7 +196,7 @@ export const generateCRISPInputs = async ( signature: `0x${string}`, message: string, merkleData: IMerkleProof, - balance: number, + balance: bigint, ): Promise => { const { hashed_message, pub_key_x, pub_key_y, signature: extractedSignature } = await extractSignature(message, signature) diff --git a/examples/CRISP/packages/crisp-sdk/tests/constants.ts b/examples/CRISP/packages/crisp-sdk/tests/constants.ts index e57e54d0cf..f1cd2e7340 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/constants.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/constants.ts @@ -21,7 +21,7 @@ export const LEAVES = [ 20897323534773557936302566533365152086278248537724825381610164527249213072770n, 4720511075913887710172192848636076523165432993226978491435561065722130431597n, 14131255645332550266535358189863475289290770471998199141522479556687499890181n, - 4935126455042678253283865781346660214959064333962120811317994370162001200675n + 4935126455042678253283865781346660214959064333962120811317994370162001200675n, ] -export const MAX_DEPTH = 20; +export const MAX_DEPTH = 20 diff --git a/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts b/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts index d489407a6a..963f2a91a3 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts @@ -9,7 +9,6 @@ import { generateMerkleProof, generateMerkleTree, hashLeaf } from '../src/utils' import { LEAVES, MAX_DEPTH } from './constants' describe('Utils', () => { - describe('hashLeaf', () => { it('should return a bigint hash of the two values', () => { const leaf = hashLeaf('0x1234567890123456789012345678901234567890', '1000') @@ -26,7 +25,7 @@ describe('Utils', () => { describe('generateMerkleProof', () => { const address = '0x1234567890123456789012345678901234567890' - const balance = 1000 + const balance = 1000n it('should generate a merkle proof for a leaf', () => { const proof = generateMerkleProof(0, balance, address, LEAVES, MAX_DEPTH) expect(proof.leaf).toBe(hashLeaf(address, balance.toString())) @@ -36,7 +35,7 @@ describe('Utils', () => { }) it('should throw if the leaf does not exist in the tree', () => { expect(() => generateMerkleProof(0, balance, address, [], MAX_DEPTH)).toThrow('Leaf not found in the tree') - expect(() => generateMerkleProof(0, 999, address, LEAVES, MAX_DEPTH)).toThrow('Leaf not found in the tree') + expect(() => generateMerkleProof(0, 999n, address, LEAVES, MAX_DEPTH)).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 a1e7fa7eaf..7170917234 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -143,8 +143,8 @@ describe('Vote', () => { }) describe('generateCRISPInputs', () => { - const votingPowerLeaf = 1000; - const merkleProof = generateMerkleProof(0, votingPowerLeaf, '0x1234567890123456789012345678901234567890', LEAVES, MAX_DEPTH); + const votingPowerLeaf = 1000n + const merkleProof = generateMerkleProof(0, votingPowerLeaf, '0x1234567890123456789012345678901234567890', LEAVES, MAX_DEPTH) it('should add the remaining inputs to the CRISP inputs object', async () => { const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower) const partialInputs = await encryptVoteAndGenerateCRISPInputs(encodedVote, publicKey, previousCiphertext) @@ -165,7 +165,7 @@ describe('Vote', () => { expect(crispInputs.signature).toBeInstanceOf(Array) expect(crispInputs.merkle_proof_indices).toBeDefined() expect(crispInputs.merkle_proof_siblings).toBeDefined() - expect(crispInputs.merkle_proof_length).toBeDefined() + expect(crispInputs.merkle_proof_length).toBeDefined() expect(crispInputs.merkle_root).toBeDefined() expect(crispInputs.balance).toBe(votingPowerLeaf.toString()) }) From 67fea9785af65478179c37ee9562eb4e7994d8f8 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Tue, 28 Oct 2025 17:19:08 +0000 Subject: [PATCH 4/4] test: add proof verification test --- examples/CRISP/packages/crisp-sdk/src/utils.ts | 4 ++-- .../packages/crisp-sdk/tests/utils.test.ts | 17 +++++++++++++---- .../CRISP/packages/crisp-sdk/tests/vote.test.ts | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/examples/CRISP/packages/crisp-sdk/src/utils.ts b/examples/CRISP/packages/crisp-sdk/src/utils.ts index 370def35a2..d3928eca4b 100644 --- a/examples/CRISP/packages/crisp-sdk/src/utils.ts +++ b/examples/CRISP/packages/crisp-sdk/src/utils.ts @@ -37,7 +37,7 @@ export const generateMerkleTree = (leaves: bigint[]): LeanIMT => { * @param maxDepth The maximum depth of the Merkle tree */ export const generateMerkleProof = ( - threshold: number, + threshold: bigint, balance: bigint, address: string, leaves: bigint[], @@ -63,7 +63,7 @@ export const generateMerkleProof = ( const paddedSiblings = [...proof.siblings, ...Array(maxDepth - proof.siblings.length).fill(0n)] // Pad indices with zeros - const indices = proof.siblings.map((_, i) => (index >> i) & 1) + const indices = proof.siblings.map((_, i) => Number((BigInt(index) >> BigInt(i)) & 1n)) const paddedIndices = [...indices, ...Array(maxDepth - indices.length).fill(0)] return { diff --git a/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts b/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts index 963f2a91a3..5f3bcc6744 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/utils.test.ts @@ -26,16 +26,25 @@ describe('Utils', () => { describe('generateMerkleProof', () => { const address = '0x1234567890123456789012345678901234567890' const balance = 1000n - it('should generate a merkle proof for a leaf', () => { - const proof = generateMerkleProof(0, balance, address, LEAVES, MAX_DEPTH) + it('should generate a valid merkle proof for a leaf', () => { + const tree = generateMerkleTree(LEAVES); + + const proof = generateMerkleProof(0n, balance, address, LEAVES, MAX_DEPTH) expect(proof.leaf).toBe(hashLeaf(address, balance.toString())) expect(proof.length).toBe(4) expect(proof.indices.length).toBe(MAX_DEPTH) + // Unpad the proof for verification + const unpaddedProof = { + ...proof.proof, + siblings: proof.proof.siblings.slice(0, proof.length) + }; + + expect(tree.verifyProof(unpaddedProof)).toBe(true); }) it('should throw if the leaf does not exist in the tree', () => { - expect(() => generateMerkleProof(0, balance, address, [], MAX_DEPTH)).toThrow('Leaf not found in the tree') - expect(() => generateMerkleProof(0, 999n, address, LEAVES, MAX_DEPTH)).toThrow('Leaf not found in the tree') + expect(() => generateMerkleProof(0n, balance, address, [], MAX_DEPTH)).toThrow('Leaf not found in the tree') + expect(() => generateMerkleProof(0n, 999n, address, LEAVES, MAX_DEPTH)).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 7170917234..3be7ccbe6b 100644 --- a/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts +++ b/examples/CRISP/packages/crisp-sdk/tests/vote.test.ts @@ -144,7 +144,7 @@ describe('Vote', () => { describe('generateCRISPInputs', () => { const votingPowerLeaf = 1000n - const merkleProof = generateMerkleProof(0, votingPowerLeaf, '0x1234567890123456789012345678901234567890', LEAVES, MAX_DEPTH) + const merkleProof = generateMerkleProof(0n, votingPowerLeaf, '0x1234567890123456789012345678901234567890', LEAVES, MAX_DEPTH) it('should add the remaining inputs to the CRISP inputs object', async () => { const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower) const partialInputs = await encryptVoteAndGenerateCRISPInputs(encodedVote, publicKey, previousCiphertext)