@crisp-e3/sdk 0.0.1-test → 0.0.2-test

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/vote.js DELETED
@@ -1,222 +0,0 @@
1
- // SPDX-License-Identifier: LGPL-3.0-only
2
- //
3
- // This file is provided WITHOUT ANY WARRANTY;
4
- // without even the implied warranty of MERCHANTABILITY
5
- // or FITNESS FOR A PARTICULAR PURPOSE.
6
- import { ZKInputsGenerator } from '@crisp-e3/zk-inputs';
7
- import { VotingMode } from './types';
8
- import { toBinary } from './utils';
9
- import { MAXIMUM_VOTE_VALUE, DEFAULT_BFV_PARAMS, MESSAGE } from './constants';
10
- import { extractSignature } from './signature';
11
- import { Noir } from '@noir-lang/noir_js';
12
- import { UltraHonkBackend } from '@aztec/bb.js';
13
- import circuit from '../../../circuits/target/crisp_circuit.json';
14
- import { privateKeyToAccount } from 'viem/accounts';
15
- /**
16
- * This utility function calculates the first valid index for vote options
17
- * based on the total voting power and degree.
18
- * @dev This is needed to calculate the decoded plaintext
19
- * @dev Also, we will need to check in the circuit that anything within these indices is
20
- * either 0 or 1.
21
- * @param totalVotingPower The maximum vote amount (if a single voter had all of the power)
22
- * @param degree The degree of the polynomial
23
- */
24
- export const calculateValidIndicesForPlaintext = (totalVotingPower, degree) => {
25
- // Sanity check: degree must be even and positive
26
- if (degree <= 0 || degree % 2 !== 0) {
27
- throw new Error('Degree must be a positive even number');
28
- }
29
- // Calculate the number of bits needed to represent the total voting power
30
- const bitsNeeded = totalVotingPower.toString(2).length;
31
- const halfLength = Math.floor(degree / 2);
32
- // Check if bits needed exceed half the degree
33
- if (bitsNeeded > halfLength) {
34
- throw new Error('Total voting power exceeds maximum representable votes for the given degree');
35
- }
36
- // For "yes": right-align in first half
37
- // Start index = (half length) - (bits needed)
38
- const yesIndex = halfLength - bitsNeeded;
39
- // For "no": right-align in second half
40
- // Start index = (full length) - (bits needed)
41
- const noIndex = degree - bitsNeeded;
42
- return {
43
- yesIndex: yesIndex,
44
- noIndex: noIndex,
45
- };
46
- };
47
- /**
48
- * Encode a vote based on the voting mode
49
- * @param vote The vote to encode
50
- * @param votingMode The voting mode to use for encoding
51
- * @param votingPower The voting power of the voter
52
- * @param bfvParams The BFV parameters to use for encoding
53
- * @returns The encoded vote as a string
54
- */
55
- export const encodeVote = (vote, votingMode, votingPower, bfvParams) => {
56
- validateVote(votingMode, vote, votingPower);
57
- switch (votingMode) {
58
- case VotingMode.GOVERNANCE:
59
- const voteArray = [];
60
- const length = bfvParams?.degree || DEFAULT_BFV_PARAMS.degree;
61
- const halfLength = length / 2;
62
- const yesBinary = toBinary(vote.yes).split('');
63
- const noBinary = toBinary(vote.no).split('');
64
- // Fill first half with 'yes' binary representation (pad with leading 0s if needed)
65
- for (let i = 0; i < halfLength; i++) {
66
- const offset = halfLength - yesBinary.length;
67
- voteArray.push(i < offset ? '0' : yesBinary[i - offset]);
68
- }
69
- // Fill second half with 'no' binary representation (pad with leading 0s if needed)
70
- for (let i = 0; i < length - halfLength; i++) {
71
- const offset = length - halfLength - noBinary.length;
72
- voteArray.push(i < offset ? '0' : noBinary[i - offset]);
73
- }
74
- return voteArray;
75
- default:
76
- throw new Error('Unsupported voting mode');
77
- }
78
- };
79
- /**
80
- * Given an encoded tally, decode it into its decimal representation
81
- * @param tally The encoded tally to decode
82
- * @param votingMode The voting mode
83
- */
84
- export const decodeTally = (tally, votingMode) => {
85
- switch (votingMode) {
86
- case VotingMode.GOVERNANCE:
87
- const halfLength = tally.length / 2;
88
- // Split the tally into two halves
89
- const yesBinary = tally.slice(0, halfLength);
90
- const noBinary = tally.slice(halfLength, tally.length);
91
- let yes = 0n;
92
- let no = 0n;
93
- // Convert each half back to decimal
94
- for (let i = 0; i < halfLength; i += 1) {
95
- const weight = 2n ** BigInt(halfLength - 1 - i);
96
- yes += BigInt(yesBinary[i]) * weight;
97
- no += BigInt(noBinary[i]) * weight;
98
- }
99
- return {
100
- yes,
101
- no,
102
- };
103
- default:
104
- throw new Error('Unsupported voting mode');
105
- }
106
- };
107
- /**
108
- * Validate whether a vote is valid for a given voting mode
109
- * @param votingMode The voting mode to validate against
110
- * @param vote The vote to validate
111
- * @param votingPower The voting power of the voter
112
- */
113
- export const validateVote = (votingMode, vote, votingPower) => {
114
- switch (votingMode) {
115
- case VotingMode.GOVERNANCE:
116
- if (vote.yes > 0n && vote.no > 0n) {
117
- throw new Error('Invalid vote for GOVERNANCE mode: cannot spread votes between options');
118
- }
119
- if (vote.yes > votingPower || vote.no > votingPower) {
120
- throw new Error('Invalid vote for GOVERNANCE mode: vote exceeds voting power');
121
- }
122
- if (vote.yes > MAXIMUM_VOTE_VALUE || vote.no > MAXIMUM_VOTE_VALUE) {
123
- throw new Error('Invalid vote for GOVERNANCE mode: vote exceeds maximum allowed value');
124
- }
125
- }
126
- };
127
- /**
128
- * This is a wrapper around enclave-e3/sdk encryption functions as CRISP circuit will require some more
129
- * input values which generic Greco do not need.
130
- * @param encodedVote The encoded vote as string array
131
- * @param publicKey The public key to use for encryption
132
- * @param previousCiphertext The previous ciphertext to use for addition operation
133
- * @param bfvParams The BFV parameters to use for encryption
134
- * @param merkleData The merkle proof data
135
- * @param message The message that was signed
136
- * @param signature The signature of the message
137
- * @param balance The voter's balance
138
- * @param slotAddress The voter's slot address
139
- * @param isFirstVote Whether this is the first vote for this slot
140
- * @returns The CRISP circuit inputs
141
- */
142
- export const encryptVoteAndGenerateCRISPInputs = async ({ encodedVote, publicKey, previousCiphertext, bfvParams = DEFAULT_BFV_PARAMS, merkleData, message, signature, balance, slotAddress, isFirstVote, }) => {
143
- if (encodedVote.length !== bfvParams.degree) {
144
- throw new RangeError(`encodedVote length ${encodedVote.length} does not match BFV degree ${bfvParams.degree}`);
145
- }
146
- const zkInputsGenerator = new ZKInputsGenerator(bfvParams.degree, bfvParams.plaintextModulus, bfvParams.moduli);
147
- const vote = BigInt64Array.from(encodedVote.map(BigInt));
148
- const crispInputs = (await zkInputsGenerator.generateInputs(previousCiphertext, publicKey, vote));
149
- const { hashed_message, pub_key_x, pub_key_y, signature: extractedSignature } = await extractSignature(message, signature);
150
- return {
151
- ...crispInputs,
152
- hashed_message: Array.from(hashed_message).map((b) => b.toString()),
153
- public_key_x: Array.from(pub_key_x).map((b) => b.toString()),
154
- public_key_y: Array.from(pub_key_y).map((b) => b.toString()),
155
- signature: Array.from(extractedSignature).map((b) => b.toString()),
156
- merkle_proof_length: merkleData.length.toString(),
157
- merkle_proof_indices: merkleData.indices.map((i) => i.toString()),
158
- merkle_proof_siblings: merkleData.proof.siblings.map((s) => s.toString()),
159
- merkle_root: merkleData.proof.root.toString(),
160
- slot_address: slotAddress,
161
- balance: balance.toString(),
162
- is_first_vote: isFirstVote,
163
- };
164
- };
165
- /**
166
- * A function to generate the data required to mask a vote
167
- * @param voter The voter's address
168
- * @param publicKey The voter's public key
169
- * @param previousCiphertext The previous ciphertext
170
- * @param bfvParams The BFV parameters
171
- * @param merkleRoot The merkle root of the census tree
172
- * @param slotAddress The voter's slot address
173
- * @param isFirstVote Whether this is the first vote for this slot
174
- * @returns The CRISP circuit inputs for a mask vote
175
- */
176
- export const generateMaskVote = async (publicKey, previousCiphertext, bfvParams = DEFAULT_BFV_PARAMS, merkleRoot, slotAddress, isFirstVote) => {
177
- const plaintextVote = {
178
- yes: 0n,
179
- no: 0n,
180
- };
181
- const encodedVote = encodeVote(plaintextVote, VotingMode.GOVERNANCE, 0n, bfvParams);
182
- const zkInputsGenerator = new ZKInputsGenerator(bfvParams.degree, bfvParams.plaintextModulus, bfvParams.moduli);
183
- const vote = BigInt64Array.from(encodedVote.map(BigInt));
184
- const crispInputs = (await zkInputsGenerator.generateInputs(previousCiphertext, publicKey, vote));
185
- // hardhat default private key
186
- const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
187
- const account = privateKeyToAccount(privateKey);
188
- const signature = await account.signMessage({ message: MESSAGE });
189
- const { hashed_message, pub_key_x, pub_key_y, signature: extractedSignature } = await extractSignature(MESSAGE, signature);
190
- return {
191
- ...crispInputs,
192
- hashed_message: Array.from(hashed_message).map((b) => b.toString()),
193
- public_key_x: Array.from(pub_key_x).map((b) => b.toString()),
194
- public_key_y: Array.from(pub_key_y).map((b) => b.toString()),
195
- signature: Array.from(extractedSignature).map((b) => b.toString()),
196
- merkle_proof_indices: Array.from({ length: 20 }, () => '0'),
197
- merkle_proof_siblings: Array.from({ length: 20 }, () => '0'),
198
- merkle_proof_length: '1',
199
- merkle_root: merkleRoot.toString(),
200
- slot_address: slotAddress,
201
- balance: '0',
202
- is_first_vote: isFirstVote,
203
- };
204
- };
205
- export const generateProof = async (crispInputs) => {
206
- const noir = new Noir(circuit);
207
- const backend = new UltraHonkBackend(circuit.bytecode);
208
- const { witness } = await noir.execute(crispInputs);
209
- const proof = await backend.generateProof(witness);
210
- return proof;
211
- };
212
- export const generateProofWithReturnValue = async (crispInputs) => {
213
- const noir = new Noir(circuit);
214
- const backend = new UltraHonkBackend(circuit.bytecode);
215
- const { witness, returnValue } = await noir.execute(crispInputs);
216
- const proof = await backend.generateProof(witness);
217
- return { returnValue, proof };
218
- };
219
- export const verifyProof = async (proof) => {
220
- const backend = new UltraHonkBackend(circuit.bytecode);
221
- return await backend.verifyProof(proof);
222
- };