Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions examples/CRISP/circuits/src/ecdsa.nr
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ pub fn verify_signature(
pub_key_x: [u8; 32],
pub_key_y: [u8; 32],
signature: [u8; 64],
) {
let valid_signature =
std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message);
assert(valid_signature);
) -> bool {
std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message)
}

/// Given a public key, derive the Ethereum address
Expand Down Expand Up @@ -89,7 +87,7 @@ fn test_verify_signature() {
124, 118, 143, 228, 126, 216, 173, 160, 231, 62, 52, 188, 154, 110, 230, 183, 71, 36, 161,
171, 163, 213, 62, 223, 152,
];
verify_signature(hashed_message, pub_key_x, pub_key_y, signature);
assert(verify_signature(hashed_message, pub_key_x, pub_key_y, signature) == true);
}

#[test]
Expand All @@ -114,7 +112,7 @@ fn test_verify_signature_sdk_input() {
236, 18,
];

verify_signature(hashed_message, pub_key_x, pub_key_y, signature);
assert(verify_signature(hashed_message, pub_key_x, pub_key_y, signature) == true);
}

#[test]
Expand All @@ -137,9 +135,8 @@ fn test_fail_verify_signature() {
124, 118, 143, 228, 126, 216, 173, 160, 231, 62, 52, 188, 154, 110, 230, 183, 71, 36, 161,
171, 163, 213, 62, 223, 151,
];
let valid_signature =
std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message);
assert(!valid_signature);

assert(verify_signature(hashed_message, pub_key_x, pub_key_y, signature) == false);
}

#[test]
Expand Down
5 changes: 3 additions & 2 deletions examples/CRISP/circuits/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ fn main(
merkle_proof_siblings: [Field; 20],
) {
// First verify the signature.
verify_signature(hashed_message, public_key_x, public_key_y, signature);
let is_signature_valid =
verify_signature(hashed_message, public_key_x, public_key_y, signature);

// Then derive the address.
let address = address_to_field(derive_address(public_key_x, public_key_y));
Expand All @@ -56,7 +57,7 @@ fn main(
// 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) {
if ((merkle_root_calculated == merkle_root) & (is_signature_valid == true)) {
is_voter = true;
}

Expand Down
14 changes: 14 additions & 0 deletions examples/CRISP/packages/crisp-sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,17 @@ export interface NoirSignatureInputs {
*/
hashed_message: Uint8Array
}

/**
* Parameters for encryptVoteAndGenerateCRISPInputs function
*/
export interface EncryptVoteAndGenerateCRISPInputsParams {
encodedVote: string[]
publicKey: Uint8Array
previousCiphertext: Uint8Array
signature: `0x${string}`
message: string
merkleData: IMerkleProof
balance: bigint
bfvParams?: BFVParams
}
91 changes: 52 additions & 39 deletions examples/CRISP/packages/crisp-sdk/src/vote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// or FITNESS FOR A PARTICULAR PURPOSE.

import { ZKInputsGenerator } from '@enclave/crisp-zk-inputs'
import { BFVParams, type CRISPCircuitInputs, type IMerkleProof, type IVote, VotingMode } from './types'
import { BFVParams, type CRISPCircuitInputs, type EncryptVoteAndGenerateCRISPInputsParams, type IVote, VotingMode } from './types'
import { toBinary } from './utils'
import { MAXIMUM_VOTE_VALUE, DEFAULT_BFV_PARAMS } from './constants'
import { extractSignature } from './signature'
Expand Down Expand Up @@ -151,12 +151,16 @@ export const validateVote = (votingMode: VotingMode, vote: IVote, votingPower: b
* @param bfvParams The BFV parameters to use for encryption
* @returns The CRISP circuit inputs
*/
export const encryptVoteAndGenerateCRISPInputs = async (
encodedVote: string[],
publicKey: Uint8Array,
previousCiphertext: Uint8Array,
bfvParams: BFVParams = DEFAULT_BFV_PARAMS,
): Promise<CRISPCircuitInputs> => {
export const encryptVoteAndGenerateCRISPInputs = async ({
encodedVote,
publicKey,
previousCiphertext,
bfvParams = DEFAULT_BFV_PARAMS,
merkleData,
message,
signature,
balance,
}: EncryptVoteAndGenerateCRISPInputsParams): Promise<CRISPCircuitInputs> => {
if (encodedVote.length !== bfvParams.degree) {
throw new RangeError(`encodedVote length ${encodedVote.length} does not match BFV degree ${bfvParams.degree}`)
}
Expand All @@ -167,41 +171,10 @@ export const encryptVoteAndGenerateCRISPInputs = async (

const crispInputs = (await zkInputsGenerator.generateInputs(previousCiphertext, publicKey, vote)) as CRISPCircuitInputs

// the rest of the public and private inputs will need to be generated before calling the circuit to generate the CRISP proof
return {
...crispInputs,
public_key_x: [],
public_key_y: [],
signature: [],
hashed_message: [],
balance: '0',
merkle_root: '0',
merkle_proof_length: '0',
merkle_proof_indices: [],
merkle_proof_siblings: [],
}
}

/**
* Generate the CRISP circuit inputs by extracting signature components and adding them to the partial inputs
* @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: bigint,
): Promise<CRISPCircuitInputs> => {
const { hashed_message, pub_key_x, pub_key_y, signature: extractedSignature } = await extractSignature(message, signature)

return {
...partialInputs,
...crispInputs,
hashed_message: Array.from(hashed_message).map((b) => b.toString()),
public_key_x: Array.from(pub_key_x).map((b) => b.toString()),
public_key_y: Array.from(pub_key_y).map((b) => b.toString()),
Expand All @@ -213,3 +186,43 @@ export const generateCRISPInputs = async (
balance: balance.toString(),
}
}

/**
* A function to generate the data required to mask a vote
* @param voter The voter's address
* @param publicKey The voter's public key
* @param previousCiphertext The previous ciphertext
* @returns The CRISP circuit inputs for a mask vote
*/
Comment thread
cedoor marked this conversation as resolved.
export const generateMaskVote = async (
publicKey: Uint8Array,
previousCiphertext: Uint8Array,
bfvParams = DEFAULT_BFV_PARAMS,
merkleRoot: bigint,
): Promise<CRISPCircuitInputs> => {
const plaintextVote: IVote = {
yes: 0n,
no: 0n,
}

const encodedVote = encodeVote(plaintextVote, VotingMode.GOVERNANCE, 0n, bfvParams)

const zkInputsGenerator: ZKInputsGenerator = new ZKInputsGenerator(bfvParams.degree, bfvParams.plaintextModulus, bfvParams.moduli)

const vote = BigInt64Array.from(encodedVote.map(BigInt))

const crispInputs = (await zkInputsGenerator.generateInputs(previousCiphertext, publicKey, vote)) as CRISPCircuitInputs

return {
...crispInputs,
public_key_x: Array.from({ length: 32 }, () => '0'),
public_key_y: Array.from({ length: 32 }, () => '0'),
signature: Array.from({ length: 64 }, () => '0'),
hashed_message: Array.from({ length: 32 }, () => '0'),
merkle_proof_indices: Array.from({ length: 4 }, () => '0'),
merkle_proof_siblings: Array.from({ length: 4 }, () => '0'),
merkle_proof_length: '1',
Comment thread
cedoor marked this conversation as resolved.
merkle_root: merkleRoot.toString(),
balance: '0',
}
}
5 changes: 5 additions & 0 deletions examples/CRISP/packages/crisp-sdk/tests/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

import { generateMerkleProof } from '../src'

export const CRISP_SERVER_URL = 'http://localhost:4000'

export const MESSAGE = 'Vote for round 0'
Expand All @@ -25,3 +27,6 @@ export const LEAVES = [
]

export const MAX_DEPTH = 20

export const votingPowerLeaf = 1000n
export const merkleProof = generateMerkleProof(0n, votingPowerLeaf, '0x1234567890123456789012345678901234567890', LEAVES, MAX_DEPTH)
8 changes: 4 additions & 4 deletions examples/CRISP/packages/crisp-sdk/tests/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('Utils', () => {
const address = '0x1234567890123456789012345678901234567890'
const balance = 1000n
it('should generate a valid merkle proof for a leaf', () => {
const tree = generateMerkleTree(LEAVES);
const tree = generateMerkleTree(LEAVES)

const proof = generateMerkleProof(0n, balance, address, LEAVES, MAX_DEPTH)
expect(proof.leaf).toBe(hashLeaf(address, balance.toString()))
Expand All @@ -37,10 +37,10 @@ describe('Utils', () => {
// Unpad the proof for verification
const unpaddedProof = {
...proof.proof,
siblings: proof.proof.siblings.slice(0, proof.length)
};
siblings: proof.proof.siblings.slice(0, proof.length),
}

expect(tree.verifyProof(unpaddedProof)).toBe(true);
expect(tree.verifyProof(unpaddedProof)).toBe(true)
})
it('should throw if the leaf does not exist in the tree', () => {
expect(() => generateMerkleProof(0n, balance, address, [], MAX_DEPTH)).toThrow('Leaf not found in the tree')
Expand Down
37 changes: 25 additions & 12 deletions examples/CRISP/packages/crisp-sdk/tests/vote.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import {
decodeTally,
encodeVote,
encryptVoteAndGenerateCRISPInputs,
generateCRISPInputs,
generateMaskVote,
validateVote,
} from '../src/vote'
import { BFVParams, VotingMode } from '../src/types'
import { DEFAULT_BFV_PARAMS, generateMerkleProof, MAXIMUM_VOTE_VALUE } from '../src'
import { DEFAULT_BFV_PARAMS, MAXIMUM_VOTE_VALUE } from '../src'

import { LEAVES, MAX_DEPTH, MESSAGE, SIGNATURE, VOTE } from './constants'
import { merkleProof, MESSAGE, SIGNATURE, VOTE, votingPowerLeaf } from './constants'

describe('Vote', () => {
const votingPower = 10n
Expand Down Expand Up @@ -128,7 +128,15 @@ describe('Vote', () => {
describe('encryptVoteAndGenerateCRISPInputs', () => {
it('should encrypt a vote and generate the circuit inputs', async () => {
const encodedVote = encodeVote(VOTE, VotingMode.GOVERNANCE, votingPower)
const crispInputs = await encryptVoteAndGenerateCRISPInputs(encodedVote, publicKey, previousCiphertext)
const crispInputs = await encryptVoteAndGenerateCRISPInputs({
encodedVote,
publicKey,
previousCiphertext,
signature: SIGNATURE,
message: MESSAGE,
merkleData: merkleProof,
balance: votingPowerLeaf,
})

expect(crispInputs.ct_add).toBeInstanceOf(Object)
expect(crispInputs.params).toBeInstanceOf(Object)
Expand All @@ -139,16 +147,21 @@ describe('Vote', () => {
expect(crispInputs.r1is).toBeInstanceOf(Array)
expect(crispInputs.r2is).toBeInstanceOf(Array)
expect(crispInputs.p1is).toBeInstanceOf(Array)
expect(crispInputs.hashed_message).toBeInstanceOf(Array)
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())
})
})

describe('generateCRISPInputs', () => {
const votingPowerLeaf = 1000n
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)
const crispInputs = await generateCRISPInputs(partialInputs, SIGNATURE, MESSAGE, merkleProof, votingPowerLeaf)
describe('generateMaskVote', () => {
it('should generate a mask vote and its inputs', async () => {
const crispInputs = await generateMaskVote(publicKey, previousCiphertext, DEFAULT_BFV_PARAMS, merkleProof.proof.root)

expect(crispInputs.ct_add).toBeInstanceOf(Object)
expect(crispInputs.params).toBeInstanceOf(Object)
Expand All @@ -167,7 +180,7 @@ describe('Vote', () => {
expect(crispInputs.merkle_proof_siblings).toBeDefined()
expect(crispInputs.merkle_proof_length).toBeDefined()
expect(crispInputs.merkle_root).toBeDefined()
expect(crispInputs.balance).toBe(votingPowerLeaf.toString())
expect(crispInputs.balance).toBeDefined()
})
})
})
Loading