@maci-protocol/circuits 0.0.0-ci.2653bc0 → 0.0.0-ci.2d2f5fb
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/LICENSE +1 -2
- package/build/ts/{genZkeys.d.ts → generateZkeys.d.ts} +1 -1
- package/build/ts/generateZkeys.d.ts.map +1 -0
- package/build/ts/{genZkeys.js → generateZkeys.js} +1 -1
- package/build/ts/generateZkeys.js.map +1 -0
- package/build/ts/types.d.ts +11 -12
- package/build/ts/types.d.ts.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/circom/circuits.json +7 -7
- package/circom/coordinator/non-qv/processMessages.circom +106 -107
- package/circom/coordinator/non-qv/tallyVotes.circom +38 -32
- package/circom/coordinator/qv/processMessages.circom +107 -105
- package/circom/coordinator/qv/tallyVotes.circom +56 -54
- package/circom/utils/{calculateTotal.circom → CalculateTotal.circom} +2 -0
- package/circom/utils/{verifySignature.circom → EdDSAPoseidonVerifier.circom} +40 -66
- package/circom/utils/MessageHasher.circom +57 -0
- package/circom/utils/MessageToCommand.circom +107 -0
- package/circom/utils/PoseidonHasher.circom +29 -0
- package/circom/utils/{privToPubKey.circom → PrivateToPublicKey.circom} +12 -10
- package/circom/utils/VerifySignature.circom +39 -0
- package/circom/utils/full/MessageValidator.circom +93 -0
- package/circom/utils/full/StateLeafAndBallotTransformer.circom +122 -0
- package/circom/utils/non-qv/{messageValidator.circom → MessageValidator.circom} +15 -13
- package/circom/utils/non-qv/{stateLeafAndBallotTransformer.circom → StateLeafAndBallotTransformer.circom} +36 -36
- package/circom/utils/qv/{messageValidator.circom → MessageValidator.circom} +15 -13
- package/circom/utils/qv/{stateLeafAndBallotTransformer.circom → StateLeafAndBallotTransformer.circom} +36 -36
- package/circom/utils/trees/BinaryMerkleRoot.circom +62 -0
- package/circom/utils/trees/CheckRoot.circom +49 -0
- package/circom/utils/trees/LeafExists.circom +27 -0
- package/circom/utils/trees/MerklePathIndicesGenerator.circom +44 -0
- package/circom/utils/trees/MerkleTreeInclusionProof.circom +50 -0
- package/circom/utils/trees/incrementalQuinaryTree.circom +2 -2
- package/circom/voter/PollJoined.circom +43 -0
- package/circom/voter/PollJoining.circom +54 -0
- package/package.json +15 -12
- package/build/ts/genZkeys.d.ts.map +0 -1
- package/build/ts/genZkeys.js.map +0 -1
- package/circom/utils/hashers.circom +0 -78
- package/circom/utils/messageToCommand.circom +0 -78
- package/circom/utils/trees/incrementalMerkleTree.circom +0 -198
- package/circom/voter/poll.circom +0 -93
|
@@ -5,12 +5,17 @@ include "./mux1.circom";
|
|
|
5
5
|
// zk-kit imports
|
|
6
6
|
include "./safe-comparators.circom";
|
|
7
7
|
// local imports
|
|
8
|
-
include "../../utils/
|
|
9
|
-
include "../../utils/
|
|
10
|
-
include "../../utils/
|
|
11
|
-
include "../../utils/
|
|
8
|
+
include "../../utils/PoseidonHasher.circom";
|
|
9
|
+
include "../../utils/MessageHasher.circom";
|
|
10
|
+
include "../../utils/MessageToCommand.circom";
|
|
11
|
+
include "../../utils/PrivateToPublicKey.circom";
|
|
12
|
+
include "../../utils/qv/StateLeafAndBallotTransformer.circom";
|
|
12
13
|
include "../../utils/trees/incrementalQuinaryTree.circom";
|
|
13
|
-
include "../../utils/trees/
|
|
14
|
+
include "../../utils/trees/MerkleTreeInclusionProof.circom";
|
|
15
|
+
include "../../utils/trees/LeafExists.circom";
|
|
16
|
+
include "../../utils/trees/CheckRoot.circom";
|
|
17
|
+
include "../../utils/trees/MerklePathIndicesGenerator.circom";
|
|
18
|
+
include "../../utils/trees/BinaryMerkleRoot.circom";
|
|
14
19
|
|
|
15
20
|
/**
|
|
16
21
|
* Proves the correctness of processing a batch of MACI messages.
|
|
@@ -30,32 +35,31 @@ template ProcessMessages(
|
|
|
30
35
|
var VOTE_OPTION_TREE_ARITY = 5;
|
|
31
36
|
// Default for binary trees.
|
|
32
37
|
var STATE_TREE_ARITY = 2;
|
|
33
|
-
var
|
|
34
|
-
var
|
|
35
|
-
var STATE_LEAF_LENGTH =
|
|
38
|
+
var MESSAGE_LENGTH = 10;
|
|
39
|
+
var PACKED_COMMAND_LENGTH = 4;
|
|
40
|
+
var STATE_LEAF_LENGTH = 3;
|
|
36
41
|
var BALLOT_LENGTH = 2;
|
|
37
|
-
var
|
|
38
|
-
var
|
|
39
|
-
var
|
|
40
|
-
var
|
|
41
|
-
var
|
|
42
|
-
var
|
|
43
|
-
var msgTreeZeroValue = 8370432830353022751713833565135785980866757267633941821328460903436894336785;
|
|
42
|
+
var BALLOT_NONCE_INDEX = 0;
|
|
43
|
+
var BALLOT_VOTE_OPTION_ROOT_INDEX = 1;
|
|
44
|
+
var STATE_LEAF_PUBLIC_X_INDEX = 0;
|
|
45
|
+
var STATE_LEAF_PUBLIC_Y_INDEX = 1;
|
|
46
|
+
var STATE_LEAF_VOICE_CREDIT_BALANCE_INDEX = 2;
|
|
47
|
+
var MESSAGE_TREE_ZERO_VALUE = 8370432830353022751713833565135785980866757267633941821328460903436894336785;
|
|
44
48
|
// Number of options for this poll.
|
|
45
49
|
var maxVoteOptions = VOTE_OPTION_TREE_ARITY ** voteOptionTreeDepth;
|
|
46
50
|
|
|
47
51
|
// Number of users that have completed the sign up.
|
|
48
|
-
signal input
|
|
52
|
+
signal input totalSignups;
|
|
49
53
|
// Value of chainHash at beginning of batch
|
|
50
54
|
signal input inputBatchHash;
|
|
51
55
|
// Value of chainHash at end of batch
|
|
52
56
|
signal input outputBatchHash;
|
|
53
57
|
// The messages.
|
|
54
|
-
signal input
|
|
58
|
+
signal input messages[batchSize][MESSAGE_LENGTH];
|
|
55
59
|
// The coordinator's private key.
|
|
56
|
-
signal input
|
|
60
|
+
signal input coordinatorPrivateKey;
|
|
57
61
|
// The ECDH public key per message.
|
|
58
|
-
signal input
|
|
62
|
+
signal input encryptionPublicKeys[batchSize][2];
|
|
59
63
|
// The current state root (before the processing).
|
|
60
64
|
signal input currentStateRoot;
|
|
61
65
|
// The actual tree depth (might be <= stateTreeDepth).
|
|
@@ -118,10 +122,10 @@ template ProcessMessages(
|
|
|
118
122
|
var voteOptionsValid = LessEqThan(32)([voteOptions, VOTE_OPTION_TREE_ARITY ** voteOptionTreeDepth]);
|
|
119
123
|
voteOptionsValid === 1;
|
|
120
124
|
|
|
121
|
-
// Check
|
|
125
|
+
// Check totalSignups <= the max number of users (i.e., number of state leaves
|
|
122
126
|
// that can fit the state tree).
|
|
123
|
-
var
|
|
124
|
-
|
|
127
|
+
var totalSignupsValid = LessEqThan(32)([totalSignups, STATE_TREE_ARITY ** stateTreeDepth]);
|
|
128
|
+
totalSignupsValid === 1;
|
|
125
129
|
|
|
126
130
|
// Hash each Message to check their existence in the Message tree.
|
|
127
131
|
var computedMessageHashers[batchSize];
|
|
@@ -131,7 +135,7 @@ template ProcessMessages(
|
|
|
131
135
|
|
|
132
136
|
for (var i = 0; i < batchSize; i++) {
|
|
133
137
|
// calculate message hash
|
|
134
|
-
computedMessageHashers[i] = MessageHasher()(
|
|
138
|
+
computedMessageHashers[i] = MessageHasher()(messages[i], encryptionPublicKeys[i]);
|
|
135
139
|
// check if message is valid or not (if index of message is less than index of last valid message in batch)
|
|
136
140
|
var batchStartIndexValid = SafeLessThan(32)([index + i, batchEndIndex]);
|
|
137
141
|
// calculate chain hash if message is valid
|
|
@@ -155,38 +159,38 @@ template ProcessMessages(
|
|
|
155
159
|
// Ensure that the coordinator's public key from the contract is correct
|
|
156
160
|
// based on the given private key - that is, the prover knows the
|
|
157
161
|
// coordinator's private key.
|
|
158
|
-
var
|
|
159
|
-
var
|
|
160
|
-
|
|
162
|
+
var derivedPublicKey[2] = PrivateToPublicKey()(coordinatorPrivateKey);
|
|
163
|
+
var derivedPublicKeyHash = PoseidonHasher(2)(derivedPublicKey);
|
|
164
|
+
derivedPublicKeyHash === coordinatorPublicKeyHash;
|
|
161
165
|
|
|
162
166
|
// Decrypt each Message into a Command.
|
|
163
167
|
// The command i-th is composed by the following fields.
|
|
164
168
|
// e.g., command 0 is made of commandsStateIndex[0],
|
|
165
|
-
//
|
|
169
|
+
// commandsNewPublicKey[0], ..., commandsPackedCommandOut[0]
|
|
166
170
|
var computedCommandsStateIndex[batchSize];
|
|
167
|
-
var
|
|
171
|
+
var computedCommandsNewPublicKey[batchSize][2];
|
|
168
172
|
var computedCommandsVoteOptionIndex[batchSize];
|
|
169
173
|
var computedCommandsNewVoteWeight[batchSize];
|
|
170
174
|
var computedCommandsNonce[batchSize];
|
|
171
175
|
var computedCommandsPollId[batchSize];
|
|
172
176
|
var computedCommandsSalt[batchSize];
|
|
173
|
-
var
|
|
174
|
-
var
|
|
175
|
-
var computedCommandsPackedCommandOut[batchSize][
|
|
177
|
+
var computedCommandsSignaturePoint[batchSize][2];
|
|
178
|
+
var computedCommandsSignatureScalar[batchSize];
|
|
179
|
+
var computedCommandsPackedCommandOut[batchSize][PACKED_COMMAND_LENGTH];
|
|
176
180
|
|
|
177
181
|
for (var i = 0; i < batchSize; i++) {
|
|
178
182
|
(
|
|
179
183
|
computedCommandsStateIndex[i],
|
|
180
|
-
|
|
184
|
+
computedCommandsNewPublicKey[i],
|
|
181
185
|
computedCommandsVoteOptionIndex[i],
|
|
182
186
|
computedCommandsNewVoteWeight[i],
|
|
183
187
|
computedCommandsNonce[i],
|
|
184
188
|
computedCommandsPollId[i],
|
|
185
189
|
computedCommandsSalt[i],
|
|
186
|
-
|
|
187
|
-
|
|
190
|
+
computedCommandsSignaturePoint[i],
|
|
191
|
+
computedCommandsSignatureScalar[i],
|
|
188
192
|
computedCommandsPackedCommandOut[i]
|
|
189
|
-
) = MessageToCommand()(
|
|
193
|
+
) = MessageToCommand()(messages[i], coordinatorPrivateKey, encryptionPublicKeys[i]);
|
|
190
194
|
}
|
|
191
195
|
|
|
192
196
|
// Process messages in reverse order.
|
|
@@ -219,7 +223,7 @@ template ProcessMessages(
|
|
|
219
223
|
}
|
|
220
224
|
|
|
221
225
|
(computedNewVoteStateRoot[i], computedNewVoteBallotRoot[i]) = ProcessOne(stateTreeDepth, voteOptionTreeDepth)(
|
|
222
|
-
|
|
226
|
+
totalSignups,
|
|
223
227
|
stateRoots[i + 1],
|
|
224
228
|
ballotRoots[i + 1],
|
|
225
229
|
actualStateTreeDepth,
|
|
@@ -230,14 +234,14 @@ template ProcessMessages(
|
|
|
230
234
|
currentVoteWeights[i],
|
|
231
235
|
currentVoteWeightsPathElement,
|
|
232
236
|
computedCommandsStateIndex[i],
|
|
233
|
-
|
|
237
|
+
computedCommandsNewPublicKey[i],
|
|
234
238
|
computedCommandsVoteOptionIndex[i],
|
|
235
239
|
computedCommandsNewVoteWeight[i],
|
|
236
240
|
computedCommandsNonce[i],
|
|
237
241
|
computedCommandsPollId[i],
|
|
238
242
|
computedCommandsSalt[i],
|
|
239
|
-
|
|
240
|
-
|
|
243
|
+
computedCommandsSignaturePoint[i],
|
|
244
|
+
computedCommandsSignatureScalar[i],
|
|
241
245
|
computedCommandsPackedCommandOut[i],
|
|
242
246
|
voteOptions
|
|
243
247
|
);
|
|
@@ -260,28 +264,26 @@ template ProcessMessages(
|
|
|
260
264
|
*/
|
|
261
265
|
template ProcessOne(stateTreeDepth, voteOptionTreeDepth) {
|
|
262
266
|
// Constants defining the structure and size of state and ballots.
|
|
263
|
-
var STATE_LEAF_LENGTH =
|
|
267
|
+
var STATE_LEAF_LENGTH = 3;
|
|
264
268
|
var BALLOT_LENGTH = 2;
|
|
265
|
-
var
|
|
266
|
-
var
|
|
269
|
+
var MESSAGE_LENGTH = 10;
|
|
270
|
+
var PACKED_COMMAND_LENGTH = 4;
|
|
267
271
|
var VOTE_OPTION_TREE_ARITY = 5;
|
|
268
272
|
var STATE_TREE_ARITY = 2;
|
|
269
|
-
var
|
|
270
|
-
// Ballot vote option (
|
|
271
|
-
var
|
|
273
|
+
var BALLOT_NONCE_INDEX = 0;
|
|
274
|
+
// Ballot vote option (vote option) root index.
|
|
275
|
+
var BALLOT_VOTE_OPTION_ROOT_INDEX = 1;
|
|
272
276
|
|
|
273
277
|
// Indices for elements within a state leaf.
|
|
274
278
|
// Public key.
|
|
275
|
-
var
|
|
276
|
-
var
|
|
279
|
+
var STATE_LEAF_PUBLIC_X_INDEX = 0;
|
|
280
|
+
var STATE_LEAF_PUBLIC_Y_INDEX = 1;
|
|
277
281
|
// Voice Credit balance.
|
|
278
|
-
var
|
|
279
|
-
|
|
280
|
-
var STATE_LEAF_TIMESTAMP_IDX = 3;
|
|
281
|
-
var N_BITS = 252;
|
|
282
|
+
var STATE_LEAF_VOICE_CREDIT_BALANCE_INDEX = 2;
|
|
283
|
+
var NUMBER_BITS = 252;
|
|
282
284
|
|
|
283
285
|
// Number of users that have completed the sign up.
|
|
284
|
-
signal input
|
|
286
|
+
signal input totalSignups;
|
|
285
287
|
// The current value of the state tree root.
|
|
286
288
|
signal input currentStateRoot;
|
|
287
289
|
// The current value of the ballot tree root.
|
|
@@ -303,16 +305,16 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) {
|
|
|
303
305
|
signal input currentVoteWeightsPathElements[voteOptionTreeDepth][VOTE_OPTION_TREE_ARITY - 1];
|
|
304
306
|
|
|
305
307
|
// Inputs related to the command being processed.
|
|
306
|
-
signal input
|
|
307
|
-
signal input
|
|
308
|
-
signal input
|
|
309
|
-
signal input
|
|
310
|
-
signal input
|
|
311
|
-
signal input
|
|
312
|
-
signal input
|
|
313
|
-
signal input
|
|
314
|
-
signal input
|
|
315
|
-
signal input
|
|
308
|
+
signal input commandStateIndex;
|
|
309
|
+
signal input commandPublicKey[2];
|
|
310
|
+
signal input commandVoteOptionIndex;
|
|
311
|
+
signal input commandNewVoteWeight;
|
|
312
|
+
signal input commandNonce;
|
|
313
|
+
signal input commandPollId;
|
|
314
|
+
signal input commandSalt;
|
|
315
|
+
signal input commandSignaturePoint[2];
|
|
316
|
+
signal input commandSignatureScalar;
|
|
317
|
+
signal input packedCommand[PACKED_COMMAND_LENGTH];
|
|
316
318
|
|
|
317
319
|
// The number of valid vote options for the poll.
|
|
318
320
|
signal input voteOptions;
|
|
@@ -323,40 +325,40 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) {
|
|
|
323
325
|
// Intermediate signals.
|
|
324
326
|
// currentVoteWeight * currentVoteWeight.
|
|
325
327
|
signal currentVoteWeightSquare;
|
|
326
|
-
//
|
|
327
|
-
signal
|
|
328
|
-
// equal to
|
|
329
|
-
signal
|
|
328
|
+
// commandNewVoteWeight * commandNewVoteWeight.
|
|
329
|
+
signal commandNewVoteWeightSquare;
|
|
330
|
+
// equal to newBallotVoteOptionRootMux (Mux1).
|
|
331
|
+
signal newBallotVoteOptionRoot;
|
|
330
332
|
|
|
331
333
|
// 1. Transform a state leaf and a ballot with a command.
|
|
332
334
|
// The result is a new state leaf, a new ballot, and an isValid signal (0 or 1).
|
|
333
|
-
var
|
|
334
|
-
(
|
|
335
|
-
|
|
335
|
+
var computedNewStateLeafPublicKey[2], computedNewBallotNonce, computedIsValid, computedIsStateLeafIndexValid, computedIsVoteOptionIndexValid;
|
|
336
|
+
(computedNewStateLeafPublicKey, computedNewBallotNonce, computedIsValid, computedIsStateLeafIndexValid, computedIsVoteOptionIndexValid) = StateLeafAndBallotTransformer()(
|
|
337
|
+
totalSignups,
|
|
336
338
|
voteOptions,
|
|
337
|
-
[stateLeaf[
|
|
338
|
-
stateLeaf[
|
|
339
|
-
ballot[
|
|
339
|
+
[stateLeaf[STATE_LEAF_PUBLIC_X_INDEX], stateLeaf[STATE_LEAF_PUBLIC_Y_INDEX]],
|
|
340
|
+
stateLeaf[STATE_LEAF_VOICE_CREDIT_BALANCE_INDEX],
|
|
341
|
+
ballot[BALLOT_NONCE_INDEX],
|
|
340
342
|
currentVoteWeight,
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
343
|
+
commandStateIndex,
|
|
344
|
+
commandPublicKey,
|
|
345
|
+
commandVoteOptionIndex,
|
|
346
|
+
commandNewVoteWeight,
|
|
347
|
+
commandNonce,
|
|
348
|
+
commandPollId,
|
|
349
|
+
commandSalt,
|
|
350
|
+
commandSignaturePoint,
|
|
351
|
+
commandSignatureScalar,
|
|
352
|
+
packedCommand
|
|
351
353
|
);
|
|
352
354
|
|
|
353
355
|
// 2. If computedIsStateLeafIndexValid is equal to zero, generate indices for leaf zero.
|
|
354
356
|
// Otherwise, generate indices for command.stateIndex.
|
|
355
|
-
var stateIndexMux = Mux1()([0,
|
|
356
|
-
var computedStateLeafPathIndices[stateTreeDepth] =
|
|
357
|
+
var stateIndexMux = Mux1()([0, commandStateIndex], computedIsStateLeafIndexValid);
|
|
358
|
+
var computedStateLeafPathIndices[stateTreeDepth] = MerklePathIndicesGenerator(stateTreeDepth)(stateIndexMux);
|
|
357
359
|
|
|
358
360
|
// 3. Verify that the original state leaf exists in the given state root.
|
|
359
|
-
var stateLeafHash = PoseidonHasher(
|
|
361
|
+
var stateLeafHash = PoseidonHasher(3)(stateLeaf);
|
|
360
362
|
var stateLeafQip = BinaryMerkleRoot(stateTreeDepth)(
|
|
361
363
|
stateLeafHash,
|
|
362
364
|
actualStateTreeDepth,
|
|
@@ -368,8 +370,8 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) {
|
|
|
368
370
|
|
|
369
371
|
// 4. Verify that the original ballot exists in the given ballot root.
|
|
370
372
|
var computedBallot = PoseidonHasher(2)([
|
|
371
|
-
ballot[
|
|
372
|
-
ballot[
|
|
373
|
+
ballot[BALLOT_NONCE_INDEX],
|
|
374
|
+
ballot[BALLOT_VOTE_OPTION_ROOT_INDEX]
|
|
373
375
|
]);
|
|
374
376
|
|
|
375
377
|
var computedBallotQip = MerkleTreeInclusionProof(stateTreeDepth)(
|
|
@@ -381,12 +383,12 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) {
|
|
|
381
383
|
computedBallotQip === currentBallotRoot;
|
|
382
384
|
|
|
383
385
|
// 5. Verify that currentVoteWeight exists in the ballot's vote option root
|
|
384
|
-
// at
|
|
386
|
+
// at commandVoteOptionIndex.
|
|
385
387
|
currentVoteWeightSquare <== currentVoteWeight * currentVoteWeight;
|
|
386
|
-
|
|
388
|
+
commandNewVoteWeightSquare <== commandNewVoteWeight * commandNewVoteWeight;
|
|
387
389
|
|
|
388
|
-
var
|
|
389
|
-
var computedCurrentVoteWeightPathIndices[voteOptionTreeDepth] = QuinGeneratePathIndices(voteOptionTreeDepth)(
|
|
390
|
+
var commandVoteOptionIndexMux = Mux1()([0, commandVoteOptionIndex], computedIsVoteOptionIndexValid);
|
|
391
|
+
var computedCurrentVoteWeightPathIndices[voteOptionTreeDepth] = QuinGeneratePathIndices(voteOptionTreeDepth)(commandVoteOptionIndexMux);
|
|
390
392
|
|
|
391
393
|
var computedCurrentVoteWeightQip = QuinTreeInclusionProof(voteOptionTreeDepth)(
|
|
392
394
|
currentVoteWeight,
|
|
@@ -394,13 +396,13 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) {
|
|
|
394
396
|
currentVoteWeightsPathElements
|
|
395
397
|
);
|
|
396
398
|
|
|
397
|
-
computedCurrentVoteWeightQip === ballot[
|
|
399
|
+
computedCurrentVoteWeightQip === ballot[BALLOT_VOTE_OPTION_ROOT_INDEX];
|
|
398
400
|
|
|
399
|
-
var voteWeightMux = Mux1()([currentVoteWeight,
|
|
401
|
+
var voteWeightMux = Mux1()([currentVoteWeight, commandNewVoteWeight], computedIsValid);
|
|
400
402
|
var voiceCreditBalanceMux = Mux1()(
|
|
401
403
|
[
|
|
402
|
-
stateLeaf[
|
|
403
|
-
stateLeaf[
|
|
404
|
+
stateLeaf[STATE_LEAF_VOICE_CREDIT_BALANCE_INDEX],
|
|
405
|
+
stateLeaf[STATE_LEAF_VOICE_CREDIT_BALANCE_INDEX] + currentVoteWeightSquare - commandNewVoteWeightSquare
|
|
404
406
|
],
|
|
405
407
|
computedIsValid
|
|
406
408
|
);
|
|
@@ -413,23 +415,22 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) {
|
|
|
413
415
|
);
|
|
414
416
|
|
|
415
417
|
// The new vote option root in the ballot
|
|
416
|
-
var
|
|
417
|
-
[ballot[
|
|
418
|
+
var newBallotVoteOptionRootMux = Mux1()(
|
|
419
|
+
[ballot[BALLOT_VOTE_OPTION_ROOT_INDEX], computedNewVoteOptionTreeQip],
|
|
418
420
|
computedIsValid
|
|
419
421
|
);
|
|
420
422
|
|
|
421
|
-
|
|
423
|
+
newBallotVoteOptionRoot <== newBallotVoteOptionRootMux;
|
|
422
424
|
|
|
423
425
|
// 6. Generate a new state root.
|
|
424
|
-
var
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
voiceCreditBalanceMux
|
|
428
|
-
stateLeaf[STATE_LEAF_TIMESTAMP_IDX]
|
|
426
|
+
var computedNewStateLeafHash = PoseidonHasher(3)([
|
|
427
|
+
computedNewStateLeafPublicKey[STATE_LEAF_PUBLIC_X_INDEX],
|
|
428
|
+
computedNewStateLeafPublicKey[STATE_LEAF_PUBLIC_Y_INDEX],
|
|
429
|
+
voiceCreditBalanceMux
|
|
429
430
|
]);
|
|
430
431
|
|
|
431
432
|
var computedNewStateLeafQip = BinaryMerkleRoot(stateTreeDepth)(
|
|
432
|
-
|
|
433
|
+
computedNewStateLeafHash,
|
|
433
434
|
actualStateTreeDepth,
|
|
434
435
|
computedStateLeafPathIndices,
|
|
435
436
|
stateLeafPathElements
|
|
@@ -438,7 +439,7 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) {
|
|
|
438
439
|
newStateRoot <== computedNewStateLeafQip;
|
|
439
440
|
|
|
440
441
|
// 7. Generate a new ballot root.
|
|
441
|
-
var computedNewBallot = PoseidonHasher(2)([computedNewBallotNonce,
|
|
442
|
+
var computedNewBallot = PoseidonHasher(2)([computedNewBallotNonce, newBallotVoteOptionRoot]);
|
|
442
443
|
var computedNewBallotQip = MerkleTreeInclusionProof(stateTreeDepth)(
|
|
443
444
|
computedNewBallot,
|
|
444
445
|
computedStateLeafPathIndices,
|
|
@@ -447,3 +448,4 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) {
|
|
|
447
448
|
|
|
448
449
|
newBallotRoot <== computedNewBallotQip;
|
|
449
450
|
}
|
|
451
|
+
|
|
@@ -5,10 +5,12 @@ include "./comparators.circom";
|
|
|
5
5
|
// zk-kit import
|
|
6
6
|
include "./unpack-element.circom";
|
|
7
7
|
// local imports
|
|
8
|
-
include "../../utils/trees/
|
|
8
|
+
include "../../utils/trees/CheckRoot.circom";
|
|
9
|
+
include "../../utils/trees/MerklePathIndicesGenerator.circom";
|
|
10
|
+
include "../../utils/trees/LeafExists.circom";
|
|
9
11
|
include "../../utils/trees/incrementalQuinaryTree.circom";
|
|
10
|
-
include "../../utils/
|
|
11
|
-
include "../../utils/
|
|
12
|
+
include "../../utils/CalculateTotal.circom";
|
|
13
|
+
include "../../utils/PoseidonHasher.circom";
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Processes batches of votes and verifies their validity in a Merkle tree structure.
|
|
@@ -33,16 +35,16 @@ template TallyVotes(
|
|
|
33
35
|
// The number of ballots processed at once, determined by the depth of the intermediate state tree.
|
|
34
36
|
var batchSize = BALLOT_TREE_ARITY ** intStateTreeDepth;
|
|
35
37
|
// Number of voting options available, determined by the depth of the vote option tree.
|
|
36
|
-
var
|
|
38
|
+
var totalVoteOptions = TREE_ARITY ** voteOptionTreeDepth;
|
|
37
39
|
|
|
38
40
|
// Number of elements in each ballot.
|
|
39
41
|
var BALLOT_LENGTH = 2;
|
|
40
42
|
// Index for the nonce in the ballot array.
|
|
41
|
-
var
|
|
43
|
+
var BALLOT_NONCE_INDEX = 0;
|
|
42
44
|
// Index for the voting option root in the ballot array.
|
|
43
|
-
var
|
|
45
|
+
var BALLOT_VOTE_OPTION_ROOT_INDEX = 1;
|
|
44
46
|
// Difference in tree depths, used in path calculations.
|
|
45
|
-
var
|
|
47
|
+
var STATE_INT_TREE_DEPTH_DIFFERENCE = stateTreeDepth - intStateTreeDepth;
|
|
46
48
|
|
|
47
49
|
// Root of the state Merkle tree, representing the overall state before voting.
|
|
48
50
|
signal input stateRoot;
|
|
@@ -59,13 +61,13 @@ template TallyVotes(
|
|
|
59
61
|
// Start index of given batch
|
|
60
62
|
signal input index;
|
|
61
63
|
// Number of users that signup
|
|
62
|
-
signal input
|
|
64
|
+
signal input totalSignups;
|
|
63
65
|
// Ballots and their corresponding path elements for verification in the tree.
|
|
64
66
|
signal input ballots[batchSize][BALLOT_LENGTH];
|
|
65
|
-
signal input ballotPathElements[
|
|
66
|
-
signal input votes[batchSize][
|
|
67
|
+
signal input ballotPathElements[STATE_INT_TREE_DEPTH_DIFFERENCE][BALLOT_TREE_ARITY - 1];
|
|
68
|
+
signal input votes[batchSize][totalVoteOptions];
|
|
67
69
|
// Current results for each vote option.
|
|
68
|
-
signal input currentResults[
|
|
70
|
+
signal input currentResults[totalVoteOptions];
|
|
69
71
|
// Salt for the root of the current results.
|
|
70
72
|
signal input currentResultsRootSalt;
|
|
71
73
|
// Total voice credits spent so far.
|
|
@@ -73,13 +75,13 @@ template TallyVotes(
|
|
|
73
75
|
// Salt for the total spent voice credits.
|
|
74
76
|
signal input currentSpentVoiceCreditSubtotalSalt;
|
|
75
77
|
// Spent voice credits per vote option.
|
|
76
|
-
signal input
|
|
78
|
+
signal input currentPerVoteOptionSpentVoiceCredits[totalVoteOptions];
|
|
77
79
|
// Salt for the root of spent credits per option.
|
|
78
|
-
signal input
|
|
80
|
+
signal input currentPerVoteOptionSpentVoiceCreditsRootSalt;
|
|
79
81
|
// Salt for the root of the new results.
|
|
80
82
|
signal input newResultsRootSalt;
|
|
81
83
|
// Salt for the new spent credits per vote option root.
|
|
82
|
-
signal input
|
|
84
|
+
signal input newPerVoteOptionSpentVoiceCreditsRootSalt;
|
|
83
85
|
// Salt for the new total spent voice credits root.
|
|
84
86
|
signal input newSpentVoiceCreditSubtotalSalt;
|
|
85
87
|
|
|
@@ -88,21 +90,21 @@ template TallyVotes(
|
|
|
88
90
|
computedSbCommitment === sbCommitment;
|
|
89
91
|
|
|
90
92
|
// Validates that the index is within the valid range of sign-ups.
|
|
91
|
-
var
|
|
92
|
-
|
|
93
|
+
var totalSignupsValid = LessEqThan(50)([index, totalSignups]);
|
|
94
|
+
totalSignupsValid === 1;
|
|
93
95
|
|
|
94
96
|
// Hashes each ballot for subroot generation, and checks the existence of the leaf in the Merkle tree.
|
|
95
97
|
var computedBallotHashers[batchSize];
|
|
96
98
|
|
|
97
99
|
for (var i = 0; i < batchSize; i++) {
|
|
98
|
-
computedBallotHashers[i] = PoseidonHasher(2)([ballots[i][
|
|
100
|
+
computedBallotHashers[i] = PoseidonHasher(2)([ballots[i][BALLOT_NONCE_INDEX], ballots[i][BALLOT_VOTE_OPTION_ROOT_INDEX]]);
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
var computedBallotSubroot = CheckRoot(intStateTreeDepth)(computedBallotHashers);
|
|
102
|
-
var computedBallotPathIndices[
|
|
104
|
+
var computedBallotPathIndices[STATE_INT_TREE_DEPTH_DIFFERENCE] = MerklePathIndicesGenerator(STATE_INT_TREE_DEPTH_DIFFERENCE)(index / batchSize);
|
|
103
105
|
|
|
104
106
|
// Verifies each ballot's existence within the ballot tree.
|
|
105
|
-
LeafExists(
|
|
107
|
+
LeafExists(STATE_INT_TREE_DEPTH_DIFFERENCE)(
|
|
106
108
|
computedBallotSubroot,
|
|
107
109
|
ballotPathElements,
|
|
108
110
|
computedBallotPathIndices,
|
|
@@ -113,7 +115,7 @@ template TallyVotes(
|
|
|
113
115
|
var computedVoteTree[batchSize];
|
|
114
116
|
for (var i = 0; i < batchSize; i++) {
|
|
115
117
|
computedVoteTree[i] = QuinCheckRoot(voteOptionTreeDepth)(votes[i]);
|
|
116
|
-
computedVoteTree[i] === ballots[i][
|
|
118
|
+
computedVoteTree[i] === ballots[i][BALLOT_VOTE_OPTION_ROOT_INDEX];
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
// Calculates new results and spent voice credits based on the current and incoming votes.
|
|
@@ -121,8 +123,8 @@ template TallyVotes(
|
|
|
121
123
|
var computedIsZero = IsZero()(computedIsFirstBatch);
|
|
122
124
|
|
|
123
125
|
// Tally the new results.
|
|
124
|
-
var computedCalculateTotalResult[
|
|
125
|
-
for (var i = 0; i <
|
|
126
|
+
var computedCalculateTotalResult[totalVoteOptions];
|
|
127
|
+
for (var i = 0; i < totalVoteOptions; i++) {
|
|
126
128
|
var numsRC[batchSize + 1];
|
|
127
129
|
numsRC[batchSize] = currentResults[i] * computedIsZero;
|
|
128
130
|
for (var j = 0; j < batchSize; j++) {
|
|
@@ -133,27 +135,27 @@ template TallyVotes(
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
// Tally the new spent voice credit total.
|
|
136
|
-
var numsSVC[batchSize *
|
|
137
|
-
numsSVC[batchSize *
|
|
138
|
+
var numsSVC[batchSize * totalVoteOptions + 1];
|
|
139
|
+
numsSVC[batchSize * totalVoteOptions] = currentSpentVoiceCreditSubtotal * computedIsZero;
|
|
138
140
|
for (var i = 0; i < batchSize; i++) {
|
|
139
|
-
for (var j = 0; j <
|
|
140
|
-
numsSVC[i *
|
|
141
|
+
for (var j = 0; j < totalVoteOptions; j++) {
|
|
142
|
+
numsSVC[i * totalVoteOptions + j] = votes[i][j] * votes[i][j];
|
|
141
143
|
}
|
|
142
144
|
}
|
|
143
145
|
|
|
144
|
-
var computedNewSpentVoiceCreditSubtotal = CalculateTotal(batchSize *
|
|
146
|
+
var computedNewSpentVoiceCreditSubtotal = CalculateTotal(batchSize * totalVoteOptions + 1)(numsSVC);
|
|
145
147
|
|
|
146
148
|
// Tally the spent voice credits per vote option.
|
|
147
|
-
var computedNewPerVOSpentVoiceCredits[
|
|
149
|
+
var computedNewPerVOSpentVoiceCredits[totalVoteOptions];
|
|
148
150
|
|
|
149
|
-
for (var i = 0; i <
|
|
150
|
-
var
|
|
151
|
-
|
|
151
|
+
for (var i = 0; i < totalVoteOptions; i++) {
|
|
152
|
+
var computedTotalVoiceCreditSpent[batchSize + 1];
|
|
153
|
+
computedTotalVoiceCreditSpent[batchSize] = currentPerVoteOptionSpentVoiceCredits[i] * computedIsZero;
|
|
152
154
|
for (var j = 0; j < batchSize; j++) {
|
|
153
|
-
|
|
155
|
+
computedTotalVoiceCreditSpent[j] = votes[j][i] * votes[j][i];
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
computedNewPerVOSpentVoiceCredits[i] = CalculateTotal(batchSize + 1)(
|
|
158
|
+
computedNewPerVOSpentVoiceCredits[i] = CalculateTotal(batchSize + 1)(computedTotalVoiceCreditSpent);
|
|
157
159
|
}
|
|
158
160
|
|
|
159
161
|
// Verifies the updated results and spent credits, ensuring consistency and correctness of tally updates.
|
|
@@ -169,10 +171,10 @@ template TallyVotes(
|
|
|
169
171
|
currentSpentVoiceCreditSubtotalSalt,
|
|
170
172
|
computedNewSpentVoiceCreditSubtotal,
|
|
171
173
|
newSpentVoiceCreditSubtotalSalt,
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
currentPerVoteOptionSpentVoiceCredits,
|
|
175
|
+
currentPerVoteOptionSpentVoiceCreditsRootSalt,
|
|
174
176
|
computedNewPerVOSpentVoiceCredits,
|
|
175
|
-
|
|
177
|
+
newPerVoteOptionSpentVoiceCreditsRootSalt
|
|
176
178
|
);
|
|
177
179
|
}
|
|
178
180
|
|
|
@@ -185,7 +187,7 @@ template ResultCommitmentVerifier(voteOptionTreeDepth) {
|
|
|
185
187
|
// Number of children per node in the tree, defining the tree's branching factor.
|
|
186
188
|
var TREE_ARITY = 5;
|
|
187
189
|
// Number of voting options available, determined by the depth of the vote option tree.
|
|
188
|
-
var
|
|
190
|
+
var totalVoteOptions = TREE_ARITY ** voteOptionTreeDepth;
|
|
189
191
|
|
|
190
192
|
// Equal to 1 if this is the first batch, otherwise 0.
|
|
191
193
|
signal input isFirstBatch;
|
|
@@ -195,12 +197,12 @@ template ResultCommitmentVerifier(voteOptionTreeDepth) {
|
|
|
195
197
|
signal input newTallyCommitment;
|
|
196
198
|
|
|
197
199
|
// Current results for each vote option.
|
|
198
|
-
signal input currentResults[
|
|
200
|
+
signal input currentResults[totalVoteOptions];
|
|
199
201
|
// Salt for the root of the current results.
|
|
200
202
|
signal input currentResultsRootSalt;
|
|
201
203
|
|
|
202
204
|
// New results for each vote option.
|
|
203
|
-
signal input newResults[
|
|
205
|
+
signal input newResults[totalVoteOptions];
|
|
204
206
|
// Salt for the root of the new results.
|
|
205
207
|
signal input newResultsRootSalt;
|
|
206
208
|
|
|
@@ -215,14 +217,14 @@ template ResultCommitmentVerifier(voteOptionTreeDepth) {
|
|
|
215
217
|
signal input newSpentVoiceCreditSubtotalSalt;
|
|
216
218
|
|
|
217
219
|
// Spent voice credits per vote option.
|
|
218
|
-
signal input
|
|
220
|
+
signal input currentPerVoteOptionSpentVoiceCredits[totalVoteOptions];
|
|
219
221
|
// Salt for the root of spent credits per option.
|
|
220
|
-
signal input
|
|
222
|
+
signal input currentPerVoteOptionSpentVoiceCreditsRootSalt;
|
|
221
223
|
|
|
222
224
|
// New spent voice credits per vote option.
|
|
223
|
-
signal input
|
|
225
|
+
signal input newPerVoteOptionSpentVoiceCredits[totalVoteOptions];
|
|
224
226
|
// Salt for the root of new spent credits per option.
|
|
225
|
-
signal input
|
|
227
|
+
signal input newPerVoteOptionSpentVoiceCreditsRootSalt;
|
|
226
228
|
|
|
227
229
|
// Compute the commitment to the current results.
|
|
228
230
|
var computedCurrentResultsRoot = QuinCheckRoot(voteOptionTreeDepth)(currentResults);
|
|
@@ -234,14 +236,14 @@ template ResultCommitmentVerifier(voteOptionTreeDepth) {
|
|
|
234
236
|
var computedCurrentSpentVoiceCreditsCommitment = PoseidonHasher(2)([currentSpentVoiceCreditSubtotal, currentSpentVoiceCreditSubtotalSalt]);
|
|
235
237
|
|
|
236
238
|
// Compute the root of the spent voice credits per vote option.
|
|
237
|
-
var
|
|
238
|
-
var
|
|
239
|
+
var computedCurrentPerVoteOptionSpentVoiceCreditsRoot = QuinCheckRoot(voteOptionTreeDepth)(currentPerVoteOptionSpentVoiceCredits);
|
|
240
|
+
var computedCurrentPerVoteOptionSpentVoiceCreditsCommitment = PoseidonHasher(2)([computedCurrentPerVoteOptionSpentVoiceCreditsRoot, currentPerVoteOptionSpentVoiceCreditsRootSalt]);
|
|
239
241
|
|
|
240
242
|
// Commit to the current tally.
|
|
241
243
|
var computedCurrentTallyCommitment = PoseidonHasher(3)([
|
|
242
244
|
computedCurrentResultsCommitment,
|
|
243
245
|
computedCurrentSpentVoiceCreditsCommitment,
|
|
244
|
-
|
|
246
|
+
computedCurrentPerVoteOptionSpentVoiceCreditsCommitment
|
|
245
247
|
]);
|
|
246
248
|
|
|
247
249
|
// Check if the current tally commitment is correct only if this is not the first batch.
|
|
@@ -249,11 +251,11 @@ template ResultCommitmentVerifier(voteOptionTreeDepth) {
|
|
|
249
251
|
// computedIsZero.out is 0 if this is the first batch.
|
|
250
252
|
var computedIsZero = IsZero()(isFirstBatch);
|
|
251
253
|
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
signal
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
// isFirstCommitment is 0 if this is the first batch, currentTallyCommitment should be 0 if this is the first batch.
|
|
255
|
+
// isFirstCommitment is 1 if this is not the first batch, currentTallyCommitment should not be 0 if this is the first batch.
|
|
256
|
+
signal isFirstCommitment;
|
|
257
|
+
isFirstCommitment <== computedIsZero * computedCurrentTallyCommitment;
|
|
258
|
+
isFirstCommitment === currentTallyCommitment;
|
|
257
259
|
|
|
258
260
|
// Compute the root of the new results.
|
|
259
261
|
var computedNewResultsRoot = QuinCheckRoot(voteOptionTreeDepth)(newResults);
|
|
@@ -263,14 +265,14 @@ template ResultCommitmentVerifier(voteOptionTreeDepth) {
|
|
|
263
265
|
var computedNewSpentVoiceCreditsCommitment = PoseidonHasher(2)([newSpentVoiceCreditSubtotal, newSpentVoiceCreditSubtotalSalt]);
|
|
264
266
|
|
|
265
267
|
// Compute the root of the spent voice credits per vote option.
|
|
266
|
-
var
|
|
267
|
-
var
|
|
268
|
+
var computedNewPerVoteOptionSpentVoiceCreditsRoot = QuinCheckRoot(voteOptionTreeDepth)(newPerVoteOptionSpentVoiceCredits);
|
|
269
|
+
var computedNewPerVoteOptionSpentVoiceCreditsCommitment = PoseidonHasher(2)([computedNewPerVoteOptionSpentVoiceCreditsRoot, newPerVoteOptionSpentVoiceCreditsRootSalt]);
|
|
268
270
|
|
|
269
271
|
// Commit to the new tally.
|
|
270
272
|
var computedNewTallyCommitment = PoseidonHasher(3)([
|
|
271
273
|
computedNewResultsCommitment,
|
|
272
274
|
computedNewSpentVoiceCreditsCommitment,
|
|
273
|
-
|
|
275
|
+
computedNewPerVoteOptionSpentVoiceCreditsCommitment
|
|
274
276
|
]);
|
|
275
277
|
|
|
276
278
|
computedNewTallyCommitment === newTallyCommitment;
|