@maci-protocol/core 0.0.0-ci.f9da2fc → 0.0.0-ci.fc91dc9
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/MaciState.d.ts +7 -7
- package/build/ts/MaciState.d.ts.map +1 -1
- package/build/ts/MaciState.js +17 -17
- package/build/ts/MaciState.js.map +1 -1
- package/build/ts/Poll.d.ts +27 -28
- package/build/ts/Poll.d.ts.map +1 -1
- package/build/ts/Poll.js +118 -117
- package/build/ts/Poll.js.map +1 -1
- package/build/ts/index.d.ts +2 -2
- package/build/ts/index.d.ts.map +1 -1
- package/build/ts/index.js +5 -5
- package/build/ts/index.js.map +1 -1
- package/build/ts/utils/constants.d.ts +1 -0
- package/build/ts/utils/constants.d.ts.map +1 -1
- package/build/ts/utils/constants.js +2 -1
- package/build/ts/utils/constants.js.map +1 -1
- package/build/ts/utils/types.d.ts +28 -26
- package/build/ts/utils/types.d.ts.map +1 -1
- package/build/ts/utils/utils.d.ts +16 -16
- package/build/ts/utils/utils.d.ts.map +1 -1
- package/build/ts/utils/utils.js +21 -21
- package/build/ts/utils/utils.js.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -5
package/build/ts/Poll.js
CHANGED
|
@@ -27,9 +27,9 @@ class Poll {
|
|
|
27
27
|
this.ballots = [];
|
|
28
28
|
this.messages = [];
|
|
29
29
|
this.commands = [];
|
|
30
|
-
this.
|
|
30
|
+
this.encryptionPublicKeys = [];
|
|
31
31
|
this.stateCopied = false;
|
|
32
|
-
this.
|
|
32
|
+
this.publicKeys = [domainobjs_1.padKey];
|
|
33
33
|
// For message processing
|
|
34
34
|
this.numBatchesProcessed = 0;
|
|
35
35
|
this.sbSalts = {};
|
|
@@ -38,7 +38,7 @@ class Poll {
|
|
|
38
38
|
this.spentVoiceCreditSubtotalSalts = {};
|
|
39
39
|
// For vote tallying
|
|
40
40
|
this.tallyResult = [];
|
|
41
|
-
this.
|
|
41
|
+
this.perVoteOptionSpentVoiceCredits = [];
|
|
42
42
|
this.numBatchesTallied = 0;
|
|
43
43
|
this.totalSpentVoiceCredits = 0n;
|
|
44
44
|
// message chain hash
|
|
@@ -48,7 +48,7 @@ class Poll {
|
|
|
48
48
|
// Poll state tree leaves
|
|
49
49
|
this.pollStateLeaves = [domainobjs_1.blankStateLeaf];
|
|
50
50
|
// how many users signed up
|
|
51
|
-
this.
|
|
51
|
+
this.totalSignups = 0n;
|
|
52
52
|
/**
|
|
53
53
|
* Check if user has already joined the poll by checking if the nullifier is registered
|
|
54
54
|
*/
|
|
@@ -56,12 +56,12 @@ class Poll {
|
|
|
56
56
|
/**
|
|
57
57
|
* Join the anonymous user to the Poll (to the tree)
|
|
58
58
|
* @param nullifier - Hashed private key used as nullifier
|
|
59
|
-
* @param
|
|
59
|
+
* @param publicKey - The poll public key.
|
|
60
60
|
* @param newVoiceCreditBalance - New voice credit balance of the user.
|
|
61
61
|
* @returns The index of added state leaf
|
|
62
62
|
*/
|
|
63
|
-
this.joinPoll = (nullifier,
|
|
64
|
-
const stateLeaf = new domainobjs_1.StateLeaf(
|
|
63
|
+
this.joinPoll = (nullifier, publicKey, newVoiceCreditBalance) => {
|
|
64
|
+
const stateLeaf = new domainobjs_1.StateLeaf(publicKey, newVoiceCreditBalance);
|
|
65
65
|
if (this.hasJoined(nullifier)) {
|
|
66
66
|
throw new Error("UserAlreadyJoined");
|
|
67
67
|
}
|
|
@@ -74,11 +74,11 @@ class Poll {
|
|
|
74
74
|
* Update a Poll with data from MaciState.
|
|
75
75
|
* This is the step where we copy the state from the MaciState instance,
|
|
76
76
|
* and set the number of signups we have so far.
|
|
77
|
-
* @note It should be called to generate the state for poll joining with
|
|
78
|
-
* the number of signups in the MaciState. For message processing, you should set
|
|
77
|
+
* @note It should be called to generate the state for poll joining with totalSignups set as
|
|
78
|
+
* the number of signups in the MaciState. For message processing, you should set totalSignups as
|
|
79
79
|
* the number of users who joined the poll.
|
|
80
80
|
*/
|
|
81
|
-
this.updatePoll = (
|
|
81
|
+
this.updatePoll = (totalSignups) => {
|
|
82
82
|
// there might be occasions where we fetch logs after new signups have been made
|
|
83
83
|
// logs are fetched (and MaciState/Poll created locally).
|
|
84
84
|
// If someone signs up after that and we fetch that record
|
|
@@ -86,15 +86,15 @@ class Poll {
|
|
|
86
86
|
// not match. For this, we must only copy up to the number of signups
|
|
87
87
|
// Copy the state tree, ballot tree, state leaves, and ballot leaves
|
|
88
88
|
// start by setting the number of signups
|
|
89
|
-
this.
|
|
90
|
-
// copy up to
|
|
91
|
-
this.
|
|
89
|
+
this.settotalSignups(totalSignups);
|
|
90
|
+
// copy up to totalSignups state leaves
|
|
91
|
+
this.publicKeys = this.maciStateRef.publicKeys.slice(0, Number(this.totalSignups)).map((x) => x.copy());
|
|
92
92
|
// ensure we have the correct actual state tree depth value
|
|
93
|
-
this.actualStateTreeDepth = Math.max(1, Math.ceil(Math.log2(Number(this.
|
|
93
|
+
this.actualStateTreeDepth = Math.max(1, Math.ceil(Math.log2(Number(this.totalSignups))));
|
|
94
94
|
this.stateTree = new lean_imt_1.LeanIMT(crypto_1.hashLeanIMT);
|
|
95
95
|
// add all leaves
|
|
96
|
-
this.
|
|
97
|
-
this.stateTree?.insert(
|
|
96
|
+
this.publicKeys.forEach((publicKey) => {
|
|
97
|
+
this.stateTree?.insert(publicKey.hash());
|
|
98
98
|
});
|
|
99
99
|
// create a poll state tree
|
|
100
100
|
this.pollStateTree = new crypto_1.IncrementalQuinTree(this.actualStateTreeDepth, domainobjs_1.blankStateLeafHash, constants_1.STATE_TREE_ARITY, crypto_1.hash2);
|
|
@@ -103,10 +103,10 @@ class Poll {
|
|
|
103
103
|
});
|
|
104
104
|
// Create as many ballots as state leaves
|
|
105
105
|
this.emptyBallotHash = this.emptyBallot.hash();
|
|
106
|
-
this.ballotTree = new crypto_1.IncrementalQuinTree(this.stateTreeDepth, this.emptyBallotHash, constants_1.STATE_TREE_ARITY, crypto_1.hash2);
|
|
106
|
+
this.ballotTree = new crypto_1.IncrementalQuinTree(Number(this.treeDepths.stateTreeDepth), this.emptyBallotHash, constants_1.STATE_TREE_ARITY, crypto_1.hash2);
|
|
107
107
|
this.ballotTree.insert(this.emptyBallotHash);
|
|
108
108
|
// we fill the ballotTree with empty ballots hashes to match the number of signups in the tree
|
|
109
|
-
while (this.ballots.length < this.
|
|
109
|
+
while (this.ballots.length < this.publicKeys.length) {
|
|
110
110
|
this.ballotTree.insert(this.emptyBallotHash);
|
|
111
111
|
this.ballots.push(this.emptyBallot);
|
|
112
112
|
}
|
|
@@ -115,13 +115,13 @@ class Poll {
|
|
|
115
115
|
/**
|
|
116
116
|
* Process one message.
|
|
117
117
|
* @param message - The message to process.
|
|
118
|
-
* @param
|
|
118
|
+
* @param encryptionPublicKey - The public key associated with the encryption private key.
|
|
119
119
|
* @returns A number of variables which will be used in the zk-SNARK circuit.
|
|
120
120
|
*/
|
|
121
|
-
this.processMessage = (message,
|
|
121
|
+
this.processMessage = (message, encryptionPublicKey, qv = true) => {
|
|
122
122
|
try {
|
|
123
123
|
// Decrypt the message
|
|
124
|
-
const sharedKey = domainobjs_1.Keypair.
|
|
124
|
+
const sharedKey = domainobjs_1.Keypair.generateEcdhSharedKey(this.coordinatorKeypair.privateKey, encryptionPublicKey);
|
|
125
125
|
const { command, signature } = domainobjs_1.PCommand.decrypt(message, sharedKey);
|
|
126
126
|
const stateLeafIndex = command.stateIndex;
|
|
127
127
|
// If the state tree index in the command is invalid, do nothing
|
|
@@ -135,7 +135,7 @@ class Poll {
|
|
|
135
135
|
// The ballot to update (or not)
|
|
136
136
|
const ballot = this.ballots[Number(stateLeafIndex)];
|
|
137
137
|
// If the signature is invalid, do nothing
|
|
138
|
-
if (!command.verifySignature(signature, stateLeaf.
|
|
138
|
+
if (!command.verifySignature(signature, stateLeaf.publicKey)) {
|
|
139
139
|
throw new errors_1.ProcessMessageError(errors_1.ProcessMessageErrors.InvalidSignature);
|
|
140
140
|
}
|
|
141
141
|
// If the nonce is invalid, do nothing
|
|
@@ -170,7 +170,7 @@ class Poll {
|
|
|
170
170
|
const newStateLeaf = stateLeaf.copy();
|
|
171
171
|
newStateLeaf.voiceCreditBalance = voiceCreditsLeft;
|
|
172
172
|
// if the key changes, this is effectively a key-change message too
|
|
173
|
-
newStateLeaf.
|
|
173
|
+
newStateLeaf.publicKey = command.newPublicKey.copy();
|
|
174
174
|
// Deep-copy the ballot and update its attributes
|
|
175
175
|
const newBallot = ballot.copy();
|
|
176
176
|
// increase the nonce
|
|
@@ -219,24 +219,24 @@ class Poll {
|
|
|
219
219
|
* Inserts a Message and the corresponding public key used to generate the
|
|
220
220
|
* ECDH shared key which was used to encrypt said message.
|
|
221
221
|
* @param message - The message to insert
|
|
222
|
-
* @param
|
|
222
|
+
* @param encryptionPublicKey - The public key used to encrypt the message
|
|
223
223
|
*/
|
|
224
|
-
this.publishMessage = (message,
|
|
225
|
-
(0, assert_1.default)(
|
|
224
|
+
this.publishMessage = (message, encryptionPublicKey) => {
|
|
225
|
+
(0, assert_1.default)(encryptionPublicKey.raw[0] < crypto_1.SNARK_FIELD_SIZE && encryptionPublicKey.raw[1] < crypto_1.SNARK_FIELD_SIZE, "The public key is not in the correct range");
|
|
226
226
|
message.data.forEach((d) => {
|
|
227
227
|
(0, assert_1.default)(d < crypto_1.SNARK_FIELD_SIZE, "The message data is not in the correct range");
|
|
228
228
|
});
|
|
229
|
-
// store the encryption
|
|
230
|
-
this.
|
|
229
|
+
// store the encryption public key
|
|
230
|
+
this.encryptionPublicKeys.push(encryptionPublicKey);
|
|
231
231
|
// store the message locally
|
|
232
232
|
this.messages.push(message);
|
|
233
233
|
// add the message hash to the message tree
|
|
234
|
-
const messageHash = message.hash(
|
|
234
|
+
const messageHash = message.hash(encryptionPublicKey);
|
|
235
235
|
// update chain hash
|
|
236
236
|
this.updateChainHash(messageHash);
|
|
237
237
|
// Decrypt the message and store the Command
|
|
238
238
|
// step 1. we generate the shared key
|
|
239
|
-
const sharedKey = domainobjs_1.Keypair.
|
|
239
|
+
const sharedKey = domainobjs_1.Keypair.generateEcdhSharedKey(this.coordinatorKeypair.privateKey, encryptionPublicKey);
|
|
240
240
|
try {
|
|
241
241
|
// step 2. we decrypt it
|
|
242
242
|
const { command } = domainobjs_1.PCommand.decrypt(message, sharedKey);
|
|
@@ -246,13 +246,13 @@ class Poll {
|
|
|
246
246
|
catch (e) {
|
|
247
247
|
// if there is an error we store an empty command
|
|
248
248
|
const keyPair = new domainobjs_1.Keypair();
|
|
249
|
-
const command = new domainobjs_1.PCommand(0n, keyPair.
|
|
249
|
+
const command = new domainobjs_1.PCommand(0n, keyPair.publicKey, 0n, 0n, 0n, 0n, 0n);
|
|
250
250
|
this.commands.push(command);
|
|
251
251
|
}
|
|
252
252
|
};
|
|
253
253
|
/**
|
|
254
254
|
* Updates message chain hash
|
|
255
|
-
* @param messageHash hash of message with
|
|
255
|
+
* @param messageHash hash of message with encryptionPublicKey
|
|
256
256
|
*/
|
|
257
257
|
this.updateChainHash = (messageHash) => {
|
|
258
258
|
this.chainHash = (0, crypto_1.hash2)([this.chainHash, messageHash]);
|
|
@@ -266,16 +266,16 @@ class Poll {
|
|
|
266
266
|
* @param args Poll joining circuit inputs
|
|
267
267
|
* @returns stringified circuit inputs
|
|
268
268
|
*/
|
|
269
|
-
this.joiningCircuitInputs = ({
|
|
269
|
+
this.joiningCircuitInputs = ({ maciPrivateKey, stateLeafIndex, pollPublicKey, }) => {
|
|
270
270
|
// calculate the path elements for the state tree given the original state tree
|
|
271
271
|
const { siblings, index } = this.stateTree.generateProof(Number(stateLeafIndex));
|
|
272
272
|
const siblingsLength = siblings.length;
|
|
273
273
|
// The index must be converted to a list of indices, 1 for each tree level.
|
|
274
|
-
// The circuit tree depth is this.stateTreeDepth, so the number of siblings must be this.stateTreeDepth,
|
|
274
|
+
// The circuit tree depth is this.treeDepths.stateTreeDepth, so the number of siblings must be this.treeDepths.stateTreeDepth,
|
|
275
275
|
// even if the tree depth is actually 3. The missing siblings can be set to 0, as they
|
|
276
276
|
// won't be used to calculate the root in the circuit.
|
|
277
277
|
const indices = [];
|
|
278
|
-
for (let i = 0; i < this.stateTreeDepth; i += 1) {
|
|
278
|
+
for (let i = 0; i < this.treeDepths.stateTreeDepth; i += 1) {
|
|
279
279
|
// eslint-disable-next-line no-bitwise
|
|
280
280
|
indices.push(BigInt((index >> i) & 1));
|
|
281
281
|
if (i >= siblingsLength) {
|
|
@@ -284,15 +284,15 @@ class Poll {
|
|
|
284
284
|
}
|
|
285
285
|
const siblingsArray = siblings.map((sibling) => [sibling]);
|
|
286
286
|
// Create nullifier from private key
|
|
287
|
-
const inputNullifier = BigInt(
|
|
287
|
+
const inputNullifier = BigInt(maciPrivateKey.asCircuitInputs());
|
|
288
288
|
const nullifier = (0, crypto_1.poseidon)([inputNullifier, this.pollId]);
|
|
289
289
|
// Get state tree's root
|
|
290
290
|
const stateRoot = this.stateTree.root;
|
|
291
291
|
// Set actualStateTreeDepth as number of initial siblings length
|
|
292
292
|
const actualStateTreeDepth = BigInt(siblingsLength);
|
|
293
293
|
const circuitInputs = {
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
privateKey: maciPrivateKey.asCircuitInputs(),
|
|
295
|
+
pollPublicKey: pollPublicKey.asCircuitInputs(),
|
|
296
296
|
siblings: siblingsArray,
|
|
297
297
|
indices,
|
|
298
298
|
nullifier,
|
|
@@ -307,18 +307,18 @@ class Poll {
|
|
|
307
307
|
* @param args Poll joined circuit inputs
|
|
308
308
|
* @returns stringified circuit inputs
|
|
309
309
|
*/
|
|
310
|
-
this.joinedCircuitInputs = ({
|
|
310
|
+
this.joinedCircuitInputs = ({ maciPrivateKey, stateLeafIndex, voiceCreditsBalance, }) => {
|
|
311
311
|
// calculate the path elements for the state tree given the original state tree
|
|
312
312
|
const { root: stateRoot, pathElements, pathIndices } = this.pollStateTree.genProof(Number(stateLeafIndex));
|
|
313
313
|
const elementsLength = pathIndices.length;
|
|
314
|
-
for (let i = 0; i < this.stateTreeDepth; i += 1) {
|
|
314
|
+
for (let i = 0; i < this.treeDepths.stateTreeDepth; i += 1) {
|
|
315
315
|
if (i >= elementsLength) {
|
|
316
316
|
pathElements[i] = [0n];
|
|
317
317
|
pathIndices[i] = 0;
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
320
|
const circuitInputs = {
|
|
321
|
-
|
|
321
|
+
privateKey: maciPrivateKey.asCircuitInputs(),
|
|
322
322
|
pathElements: pathElements.map((item) => item.toString()),
|
|
323
323
|
voiceCreditsBalance: voiceCreditsBalance.toString(),
|
|
324
324
|
pathIndices: pathIndices.map((item) => item.toString()),
|
|
@@ -386,7 +386,7 @@ class Poll {
|
|
|
386
386
|
throw new Error("You must update the poll with the correct data first");
|
|
387
387
|
}
|
|
388
388
|
// Generate circuit inputs
|
|
389
|
-
const circuitInputs = (0, crypto_1.stringifyBigInts)(this.
|
|
389
|
+
const circuitInputs = (0, crypto_1.stringifyBigInts)(this.generateProcessMessagesCircuitInputsPartial(this.currentMessageBatchIndex));
|
|
390
390
|
// we want to store the state leaves at this point in time
|
|
391
391
|
// and the path elements of the state tree
|
|
392
392
|
const currentStateLeaves = [];
|
|
@@ -405,13 +405,13 @@ class Poll {
|
|
|
405
405
|
const idx = this.currentMessageBatchIndex * batchSize - i - 1;
|
|
406
406
|
(0, assert_1.default)(idx >= 0, "The message index must be >= 0");
|
|
407
407
|
let message;
|
|
408
|
-
let
|
|
408
|
+
let encryptionPublicKey;
|
|
409
409
|
if (idx < this.messages.length) {
|
|
410
410
|
message = this.messages[idx];
|
|
411
|
-
|
|
411
|
+
encryptionPublicKey = this.encryptionPublicKeys[idx];
|
|
412
412
|
try {
|
|
413
413
|
// check if the command is valid
|
|
414
|
-
const r = this.processMessage(message,
|
|
414
|
+
const r = this.processMessage(message, encryptionPublicKey, qv);
|
|
415
415
|
const index = r.stateLeafIndex;
|
|
416
416
|
// we add at position 0 the original data
|
|
417
417
|
currentStateLeaves.unshift(r.originalStateLeaf);
|
|
@@ -446,8 +446,8 @@ class Poll {
|
|
|
446
446
|
// which sends a message that when force decrypted on the circuit
|
|
447
447
|
// results in a valid state index thus forcing the circuit to look
|
|
448
448
|
// for a valid state leaf, and failing to generate a proof
|
|
449
|
-
//
|
|
450
|
-
const sharedKey = domainobjs_1.Keypair.
|
|
449
|
+
// generate shared key
|
|
450
|
+
const sharedKey = domainobjs_1.Keypair.generateEcdhSharedKey(this.coordinatorKeypair.privateKey, encryptionPublicKey);
|
|
451
451
|
// force decrypt it
|
|
452
452
|
const { command } = domainobjs_1.PCommand.decrypt(message, sharedKey, true);
|
|
453
453
|
// cache state leaf index
|
|
@@ -528,7 +528,7 @@ class Poll {
|
|
|
528
528
|
// we need to fill the array with 0s to match the length of the state leaves
|
|
529
529
|
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
530
530
|
for (let i = 0; i < currentStateLeavesPathElements.length; i += 1) {
|
|
531
|
-
while (currentStateLeavesPathElements[i].length < this.stateTreeDepth) {
|
|
531
|
+
while (currentStateLeavesPathElements[i].length < this.treeDepths.stateTreeDepth) {
|
|
532
532
|
currentStateLeavesPathElements[i].push([0n]);
|
|
533
533
|
}
|
|
534
534
|
}
|
|
@@ -543,9 +543,9 @@ class Poll {
|
|
|
543
543
|
this.currentMessageBatchIndex -= 1;
|
|
544
544
|
}
|
|
545
545
|
// ensure newSbSalt differs from currentSbSalt
|
|
546
|
-
let newSbSalt = (0, crypto_1.
|
|
546
|
+
let newSbSalt = (0, crypto_1.generateRandomSalt)();
|
|
547
547
|
while (this.sbSalts[this.currentMessageBatchIndex] === newSbSalt) {
|
|
548
|
-
newSbSalt = (0, crypto_1.
|
|
548
|
+
newSbSalt = (0, crypto_1.generateRandomSalt)();
|
|
549
549
|
}
|
|
550
550
|
this.sbSalts[this.currentMessageBatchIndex] = newSbSalt;
|
|
551
551
|
// store the salt in the circuit inputs
|
|
@@ -555,7 +555,7 @@ class Poll {
|
|
|
555
555
|
// create a commitment to the state and ballot tree roots
|
|
556
556
|
// this will be the hash of the roots with a salt
|
|
557
557
|
circuitInputs.newSbCommitment = (0, crypto_1.hash3)([newStateRoot, newBallotRoot, newSbSalt]);
|
|
558
|
-
const coordinatorPublicKeyHash = this.coordinatorKeypair.
|
|
558
|
+
const coordinatorPublicKeyHash = this.coordinatorKeypair.publicKey.hash();
|
|
559
559
|
// If this is the last batch, release the lock
|
|
560
560
|
if (this.numBatchesProcessed * batchSize >= this.messages.length) {
|
|
561
561
|
this.maciStateRef.pollBeingProcessed = false;
|
|
@@ -572,33 +572,33 @@ class Poll {
|
|
|
572
572
|
* @param index - The index of the partial batch.
|
|
573
573
|
* @returns stringified partial circuit inputs
|
|
574
574
|
*/
|
|
575
|
-
this.
|
|
575
|
+
this.generateProcessMessagesCircuitInputsPartial = (index) => {
|
|
576
576
|
const { messageBatchSize } = this.batchSizes;
|
|
577
577
|
(0, assert_1.default)(index <= this.messages.length, "The index must be <= the number of messages");
|
|
578
|
-
// fill the
|
|
578
|
+
// fill the messages array with a copy of the messages we have
|
|
579
579
|
// plus empty messages to fill the batch
|
|
580
580
|
// @note create a message with state index 0 to add as padding
|
|
581
581
|
// this way the message will look for state leaf 0
|
|
582
582
|
// and no effect will take place
|
|
583
583
|
// create a random key
|
|
584
584
|
const key = new domainobjs_1.Keypair();
|
|
585
|
-
//
|
|
586
|
-
const ecdh = domainobjs_1.Keypair.
|
|
585
|
+
// generate ecdh key
|
|
586
|
+
const ecdh = domainobjs_1.Keypair.generateEcdhSharedKey(key.privateKey, this.coordinatorKeypair.publicKey);
|
|
587
587
|
// create an empty command with state index 0n
|
|
588
|
-
const emptyCommand = new domainobjs_1.PCommand(0n, key.
|
|
588
|
+
const emptyCommand = new domainobjs_1.PCommand(0n, key.publicKey, 0n, 0n, 0n, 0n, 0n);
|
|
589
589
|
// encrypt it
|
|
590
|
-
const msg = emptyCommand.encrypt(emptyCommand.sign(key.
|
|
590
|
+
const msg = emptyCommand.encrypt(emptyCommand.sign(key.privateKey), ecdh);
|
|
591
591
|
// copy the messages to a new array
|
|
592
|
-
let
|
|
592
|
+
let messages = this.messages.map((x) => x.asCircuitInputs());
|
|
593
593
|
// pad with our state index 0 message
|
|
594
|
-
while (
|
|
595
|
-
|
|
594
|
+
while (messages.length % messageBatchSize > 0) {
|
|
595
|
+
messages.push(msg.asCircuitInputs());
|
|
596
596
|
}
|
|
597
597
|
// copy the public keys, pad the array with the last keys if needed
|
|
598
|
-
let
|
|
599
|
-
while (
|
|
598
|
+
let encryptionPublicKeys = this.encryptionPublicKeys.map((x) => x.copy());
|
|
599
|
+
while (encryptionPublicKeys.length % messageBatchSize > 0) {
|
|
600
600
|
// pad with the public key used to encrypt the message with state index 0 (padding)
|
|
601
|
-
|
|
601
|
+
encryptionPublicKeys.push(key.publicKey.copy());
|
|
602
602
|
}
|
|
603
603
|
// validate that the batch index is correct, if not fix it
|
|
604
604
|
// this means that the end will be the last message
|
|
@@ -608,11 +608,11 @@ class Poll {
|
|
|
608
608
|
}
|
|
609
609
|
const batchStartIndex = index > 0 ? (index - 1) * messageBatchSize : 0;
|
|
610
610
|
// we only take the messages we need for this batch
|
|
611
|
-
// it slice
|
|
611
|
+
// it slice messages array from index of first message in current batch to
|
|
612
612
|
// index of last message in current batch
|
|
613
|
-
|
|
613
|
+
messages = messages.slice(batchStartIndex, index * messageBatchSize);
|
|
614
614
|
// then take the ones part of this batch
|
|
615
|
-
|
|
615
|
+
encryptionPublicKeys = encryptionPublicKeys.slice(batchStartIndex, index * messageBatchSize);
|
|
616
616
|
// cache tree roots
|
|
617
617
|
const currentStateRoot = this.pollStateTree.root;
|
|
618
618
|
const currentBallotRoot = this.ballotTree.root;
|
|
@@ -623,15 +623,15 @@ class Poll {
|
|
|
623
623
|
const inputBatchHash = this.batchHashes[index - 1];
|
|
624
624
|
const outputBatchHash = this.batchHashes[index];
|
|
625
625
|
return (0, crypto_1.stringifyBigInts)({
|
|
626
|
-
|
|
626
|
+
totalSignups: BigInt(this.totalSignups),
|
|
627
627
|
batchEndIndex: BigInt(batchEndIndex),
|
|
628
628
|
index: BigInt(batchStartIndex),
|
|
629
629
|
inputBatchHash,
|
|
630
630
|
outputBatchHash,
|
|
631
|
-
|
|
631
|
+
messages,
|
|
632
632
|
actualStateTreeDepth: BigInt(this.actualStateTreeDepth),
|
|
633
|
-
|
|
634
|
-
|
|
633
|
+
coordinatorPrivateKey: this.coordinatorKeypair.privateKey.asCircuitInputs(),
|
|
634
|
+
encryptionPublicKeys: encryptionPublicKeys.map((x) => x.asCircuitInputs()),
|
|
635
635
|
currentStateRoot,
|
|
636
636
|
currentBallotRoot,
|
|
637
637
|
currentSbCommitment,
|
|
@@ -677,8 +677,8 @@ class Poll {
|
|
|
677
677
|
const currentPerVOSpentVoiceCreditsRootSalt = batchStartIndex === 0 ? 0n : this.preVOSpentVoiceCreditsRootSalts[batchStartIndex - batchSize];
|
|
678
678
|
const currentSpentVoiceCreditSubtotalSalt = batchStartIndex === 0 ? 0n : this.spentVoiceCreditSubtotalSalts[batchStartIndex - batchSize];
|
|
679
679
|
// generate a commitment to the current results
|
|
680
|
-
const currentResultsCommitment = (0, crypto_1.
|
|
681
|
-
// generate a commitment to the current per
|
|
680
|
+
const currentResultsCommitment = (0, crypto_1.generateTreeCommitment)(this.tallyResult, currentResultsRootSalt, this.treeDepths.voteOptionTreeDepth);
|
|
681
|
+
// generate a commitment to the current per vote option spent voice credits
|
|
682
682
|
const currentPerVOSpentVoiceCreditsCommitment = this.genPerVOSpentVoiceCreditsCommitment(currentPerVOSpentVoiceCreditsRootSalt, batchStartIndex, true);
|
|
683
683
|
// generate a commitment to the current spent voice credits
|
|
684
684
|
const currentSpentVoiceCreditsCommitment = this.genSpentVoiceCreditSubtotalCommitment(currentSpentVoiceCreditSubtotalSalt, batchStartIndex, true);
|
|
@@ -698,7 +698,7 @@ class Poll {
|
|
|
698
698
|
]);
|
|
699
699
|
const ballots = [];
|
|
700
700
|
const currentResults = this.tallyResult.map((x) => BigInt(x.toString()));
|
|
701
|
-
const currentPerVOSpentVoiceCredits = this.
|
|
701
|
+
const currentPerVOSpentVoiceCredits = this.perVoteOptionSpentVoiceCredits.map((x) => BigInt(x.toString()));
|
|
702
702
|
const currentSpentVoiceCreditSubtotal = BigInt(this.totalSpentVoiceCredits.toString());
|
|
703
703
|
// loop in normal order to tally the ballots one by one
|
|
704
704
|
for (let i = this.numBatchesTallied * batchSize; i < this.numBatchesTallied * batchSize + batchSize; i += 1) {
|
|
@@ -714,7 +714,7 @@ class Poll {
|
|
|
714
714
|
// the vote itself will be a quadratic vote (sqrt(voiceCredits))
|
|
715
715
|
this.tallyResult[j] += v;
|
|
716
716
|
// the per vote option spent voice credits will be the sum of the squares of the votes
|
|
717
|
-
this.
|
|
717
|
+
this.perVoteOptionSpentVoiceCredits[j] += v * v;
|
|
718
718
|
// the total spent voice credits will be the sum of the squares of the votes
|
|
719
719
|
this.totalSpentVoiceCredits += v * v;
|
|
720
720
|
}
|
|
@@ -725,18 +725,18 @@ class Poll {
|
|
|
725
725
|
ballots.push(emptyBallot);
|
|
726
726
|
}
|
|
727
727
|
// generate the new salts
|
|
728
|
-
const newResultsRootSalt = (0, crypto_1.
|
|
729
|
-
const newPerVOSpentVoiceCreditsRootSalt = (0, crypto_1.
|
|
730
|
-
const newSpentVoiceCreditSubtotalSalt = (0, crypto_1.
|
|
728
|
+
const newResultsRootSalt = (0, crypto_1.generateRandomSalt)();
|
|
729
|
+
const newPerVOSpentVoiceCreditsRootSalt = (0, crypto_1.generateRandomSalt)();
|
|
730
|
+
const newSpentVoiceCreditSubtotalSalt = (0, crypto_1.generateRandomSalt)();
|
|
731
731
|
// and save them to be used in the next batch
|
|
732
732
|
this.resultRootSalts[batchStartIndex] = newResultsRootSalt;
|
|
733
733
|
this.preVOSpentVoiceCreditsRootSalts[batchStartIndex] = newPerVOSpentVoiceCreditsRootSalt;
|
|
734
734
|
this.spentVoiceCreditSubtotalSalts[batchStartIndex] = newSpentVoiceCreditSubtotalSalt;
|
|
735
735
|
// generate the new results commitment with the new salts and data
|
|
736
|
-
const newResultsCommitment = (0, crypto_1.
|
|
736
|
+
const newResultsCommitment = (0, crypto_1.generateTreeCommitment)(this.tallyResult, newResultsRootSalt, this.treeDepths.voteOptionTreeDepth);
|
|
737
737
|
// generate the new spent voice credits commitment with the new salts and data
|
|
738
738
|
const newSpentVoiceCreditsCommitment = this.genSpentVoiceCreditSubtotalCommitment(newSpentVoiceCreditSubtotalSalt, batchStartIndex + batchSize, true);
|
|
739
|
-
// generate the new per
|
|
739
|
+
// generate the new per vote option spent voice credits commitment with the new salts and data
|
|
740
740
|
const newPerVOSpentVoiceCreditsCommitment = this.genPerVOSpentVoiceCreditsCommitment(newPerVOSpentVoiceCreditsRootSalt, batchStartIndex + batchSize, true);
|
|
741
741
|
// generate the new tally commitment
|
|
742
742
|
const newTallyCommitment = (0, crypto_1.hash3)([
|
|
@@ -756,7 +756,7 @@ class Poll {
|
|
|
756
756
|
ballotRoot,
|
|
757
757
|
sbSalt,
|
|
758
758
|
index: BigInt(batchStartIndex),
|
|
759
|
-
|
|
759
|
+
totalSignups: BigInt(this.totalSignups),
|
|
760
760
|
sbCommitment,
|
|
761
761
|
currentTallyCommitment,
|
|
762
762
|
newTallyCommitment,
|
|
@@ -789,7 +789,7 @@ class Poll {
|
|
|
789
789
|
const currentResultsRootSalt = batchStartIndex === 0 ? 0n : this.resultRootSalts[batchStartIndex - batchSize];
|
|
790
790
|
const currentSpentVoiceCreditSubtotalSalt = batchStartIndex === 0 ? 0n : this.spentVoiceCreditSubtotalSalts[batchStartIndex - batchSize];
|
|
791
791
|
// generate a commitment to the current results
|
|
792
|
-
const currentResultsCommitment = (0, crypto_1.
|
|
792
|
+
const currentResultsCommitment = (0, crypto_1.generateTreeCommitment)(this.tallyResult, currentResultsRootSalt, this.treeDepths.voteOptionTreeDepth);
|
|
793
793
|
// generate a commitment to the current spent voice credits
|
|
794
794
|
const currentSpentVoiceCreditsCommitment = this.genSpentVoiceCreditSubtotalCommitment(currentSpentVoiceCreditSubtotalSalt, batchStartIndex, false);
|
|
795
795
|
// the current commitment for the first batch will be 0
|
|
@@ -824,13 +824,13 @@ class Poll {
|
|
|
824
824
|
ballots.push(emptyBallot);
|
|
825
825
|
}
|
|
826
826
|
// generate the new salts
|
|
827
|
-
const newResultsRootSalt = (0, crypto_1.
|
|
828
|
-
const newSpentVoiceCreditSubtotalSalt = (0, crypto_1.
|
|
827
|
+
const newResultsRootSalt = (0, crypto_1.generateRandomSalt)();
|
|
828
|
+
const newSpentVoiceCreditSubtotalSalt = (0, crypto_1.generateRandomSalt)();
|
|
829
829
|
// and save them to be used in the next batch
|
|
830
830
|
this.resultRootSalts[batchStartIndex] = newResultsRootSalt;
|
|
831
831
|
this.spentVoiceCreditSubtotalSalts[batchStartIndex] = newSpentVoiceCreditSubtotalSalt;
|
|
832
832
|
// generate the new results commitment with the new salts and data
|
|
833
|
-
const newResultsCommitment = (0, crypto_1.
|
|
833
|
+
const newResultsCommitment = (0, crypto_1.generateTreeCommitment)(this.tallyResult, newResultsRootSalt, this.treeDepths.voteOptionTreeDepth);
|
|
834
834
|
// generate the new spent voice credits commitment with the new salts and data
|
|
835
835
|
const newSpentVoiceCreditsCommitment = this.genSpentVoiceCreditSubtotalCommitment(newSpentVoiceCreditSubtotalSalt, batchStartIndex + batchSize, false);
|
|
836
836
|
// generate the new tally commitment
|
|
@@ -847,7 +847,7 @@ class Poll {
|
|
|
847
847
|
ballotRoot,
|
|
848
848
|
sbSalt,
|
|
849
849
|
index: BigInt(batchStartIndex),
|
|
850
|
-
|
|
850
|
+
totalSignups: BigInt(this.totalSignups),
|
|
851
851
|
sbCommitment,
|
|
852
852
|
currentTallyCommitment,
|
|
853
853
|
newTallyCommitment,
|
|
@@ -907,7 +907,7 @@ class Poll {
|
|
|
907
907
|
leaves[j] += useQuadraticVoting ? v * v : v;
|
|
908
908
|
}
|
|
909
909
|
}
|
|
910
|
-
return (0, crypto_1.
|
|
910
|
+
return (0, crypto_1.generateTreeCommitment)(leaves, salt, this.treeDepths.voteOptionTreeDepth);
|
|
911
911
|
};
|
|
912
912
|
/**
|
|
913
913
|
* Create a deep copy of the Poll object.
|
|
@@ -917,23 +917,24 @@ class Poll {
|
|
|
917
917
|
const copied = new Poll(BigInt(this.pollEndTimestamp.toString()), this.coordinatorKeypair.copy(), {
|
|
918
918
|
intStateTreeDepth: Number(this.treeDepths.intStateTreeDepth),
|
|
919
919
|
voteOptionTreeDepth: Number(this.treeDepths.voteOptionTreeDepth),
|
|
920
|
+
stateTreeDepth: Number(this.treeDepths.stateTreeDepth),
|
|
920
921
|
}, {
|
|
921
922
|
tallyBatchSize: Number(this.batchSizes.tallyBatchSize.toString()),
|
|
922
923
|
messageBatchSize: Number(this.batchSizes.messageBatchSize.toString()),
|
|
923
924
|
}, this.maciStateRef, this.voteOptions);
|
|
924
|
-
copied.
|
|
925
|
+
copied.publicKeys = this.publicKeys.map((x) => x.copy());
|
|
925
926
|
copied.pollStateLeaves = this.pollStateLeaves.map((x) => x.copy());
|
|
926
927
|
copied.messages = this.messages.map((x) => x.copy());
|
|
927
928
|
copied.commands = this.commands.map((x) => x.copy());
|
|
928
929
|
copied.ballots = this.ballots.map((x) => x.copy());
|
|
929
|
-
copied.
|
|
930
|
+
copied.encryptionPublicKeys = this.encryptionPublicKeys.map((x) => x.copy());
|
|
930
931
|
if (this.ballotTree) {
|
|
931
932
|
copied.ballotTree = this.ballotTree.copy();
|
|
932
933
|
}
|
|
933
934
|
copied.currentMessageBatchIndex = this.currentMessageBatchIndex;
|
|
934
935
|
copied.maciStateRef = this.maciStateRef;
|
|
935
936
|
copied.tallyResult = this.tallyResult.map((x) => BigInt(x.toString()));
|
|
936
|
-
copied.
|
|
937
|
+
copied.perVoteOptionSpentVoiceCredits = this.perVoteOptionSpentVoiceCredits.map((x) => BigInt(x.toString()));
|
|
937
938
|
copied.numBatchesProcessed = Number(this.numBatchesProcessed.toString());
|
|
938
939
|
copied.numBatchesTallied = Number(this.numBatchesTallied.toString());
|
|
939
940
|
copied.pollId = this.pollId;
|
|
@@ -955,34 +956,34 @@ class Poll {
|
|
|
955
956
|
copied.spentVoiceCreditSubtotalSalts[k] = BigInt(this.spentVoiceCreditSubtotalSalts[k].toString());
|
|
956
957
|
});
|
|
957
958
|
// update the number of signups
|
|
958
|
-
copied.
|
|
959
|
+
copied.settotalSignups(this.totalSignups);
|
|
959
960
|
return copied;
|
|
960
961
|
};
|
|
961
962
|
/**
|
|
962
963
|
* Check if the Poll object is equal to another Poll object.
|
|
963
|
-
* @param
|
|
964
|
+
* @param poll - The Poll object to compare.
|
|
964
965
|
* @returns True if the two Poll objects are equal, false otherwise.
|
|
965
966
|
*/
|
|
966
|
-
this.equals = (
|
|
967
|
-
const result = this.coordinatorKeypair.equals(
|
|
968
|
-
this.treeDepths.intStateTreeDepth ===
|
|
969
|
-
this.treeDepths.voteOptionTreeDepth ===
|
|
970
|
-
this.batchSizes.tallyBatchSize ===
|
|
971
|
-
this.batchSizes.messageBatchSize ===
|
|
972
|
-
this.maxVoteOptions ===
|
|
973
|
-
this.messages.length ===
|
|
974
|
-
this.
|
|
975
|
-
this.
|
|
967
|
+
this.equals = (poll) => {
|
|
968
|
+
const result = this.coordinatorKeypair.equals(poll.coordinatorKeypair) &&
|
|
969
|
+
this.treeDepths.intStateTreeDepth === poll.treeDepths.intStateTreeDepth &&
|
|
970
|
+
this.treeDepths.voteOptionTreeDepth === poll.treeDepths.voteOptionTreeDepth &&
|
|
971
|
+
this.batchSizes.tallyBatchSize === poll.batchSizes.tallyBatchSize &&
|
|
972
|
+
this.batchSizes.messageBatchSize === poll.batchSizes.messageBatchSize &&
|
|
973
|
+
this.maxVoteOptions === poll.maxVoteOptions &&
|
|
974
|
+
this.messages.length === poll.messages.length &&
|
|
975
|
+
this.encryptionPublicKeys.length === poll.encryptionPublicKeys.length &&
|
|
976
|
+
this.totalSignups === poll.totalSignups;
|
|
976
977
|
if (!result) {
|
|
977
978
|
return false;
|
|
978
979
|
}
|
|
979
980
|
for (let i = 0; i < this.messages.length; i += 1) {
|
|
980
|
-
if (!this.messages[i].equals(
|
|
981
|
+
if (!this.messages[i].equals(poll.messages[i])) {
|
|
981
982
|
return false;
|
|
982
983
|
}
|
|
983
984
|
}
|
|
984
|
-
for (let i = 0; i < this.
|
|
985
|
-
if (!this.
|
|
985
|
+
for (let i = 0; i < this.encryptionPublicKeys.length; i += 1) {
|
|
986
|
+
if (!this.encryptionPublicKeys[i].equals(poll.encryptionPublicKeys[i])) {
|
|
986
987
|
return false;
|
|
987
988
|
}
|
|
988
989
|
}
|
|
@@ -993,20 +994,20 @@ class Poll {
|
|
|
993
994
|
* @param serializedPrivateKey - the serialized private key
|
|
994
995
|
*/
|
|
995
996
|
this.setCoordinatorKeypair = (serializedPrivateKey) => {
|
|
996
|
-
this.coordinatorKeypair = new domainobjs_1.Keypair(domainobjs_1.
|
|
997
|
+
this.coordinatorKeypair = new domainobjs_1.Keypair(domainobjs_1.PrivateKey.deserialize(serializedPrivateKey));
|
|
997
998
|
};
|
|
998
999
|
/**
|
|
999
1000
|
* Set the number of signups to match the ones from the contract
|
|
1000
|
-
* @param
|
|
1001
|
+
* @param totalSignups - the number of signups
|
|
1001
1002
|
*/
|
|
1002
|
-
this.
|
|
1003
|
-
this.
|
|
1003
|
+
this.settotalSignups = (totalSignups) => {
|
|
1004
|
+
this.totalSignups = totalSignups;
|
|
1004
1005
|
};
|
|
1005
1006
|
/**
|
|
1006
1007
|
* Get the number of signups
|
|
1007
1008
|
* @returns The number of signups
|
|
1008
1009
|
*/
|
|
1009
|
-
this.
|
|
1010
|
+
this.gettotalSignups = () => this.totalSignups;
|
|
1010
1011
|
this.pollEndTimestamp = pollEndTimestamp;
|
|
1011
1012
|
this.coordinatorKeypair = coordinatorKeypair;
|
|
1012
1013
|
this.treeDepths = treeDepths;
|
|
@@ -1018,12 +1019,11 @@ class Poll {
|
|
|
1018
1019
|
this.maxVoteOptions = constants_1.VOTE_OPTION_TREE_ARITY ** treeDepths.voteOptionTreeDepth;
|
|
1019
1020
|
this.maciStateRef = maciStateRef;
|
|
1020
1021
|
this.pollId = BigInt(maciStateRef.polls.size);
|
|
1021
|
-
this.
|
|
1022
|
-
this.actualStateTreeDepth = maciStateRef.stateTreeDepth;
|
|
1022
|
+
this.actualStateTreeDepth = treeDepths.stateTreeDepth;
|
|
1023
1023
|
this.currentMessageBatchIndex = 0;
|
|
1024
1024
|
this.pollNullifiers = new Map();
|
|
1025
1025
|
this.tallyResult = new Array(this.maxVoteOptions).fill(0n);
|
|
1026
|
-
this.
|
|
1026
|
+
this.perVoteOptionSpentVoiceCredits = new Array(this.maxVoteOptions).fill(0n);
|
|
1027
1027
|
// we put a blank state leaf to prevent a DoS attack
|
|
1028
1028
|
this.emptyBallot = domainobjs_1.Ballot.genBlankBallot(this.maxVoteOptions, treeDepths.voteOptionTreeDepth);
|
|
1029
1029
|
this.ballots.push(this.emptyBallot);
|
|
@@ -1034,6 +1034,7 @@ class Poll {
|
|
|
1034
1034
|
*/
|
|
1035
1035
|
toJSON() {
|
|
1036
1036
|
return {
|
|
1037
|
+
stateTreeDepth: Number(this.treeDepths.stateTreeDepth),
|
|
1037
1038
|
pollEndTimestamp: this.pollEndTimestamp.toString(),
|
|
1038
1039
|
treeDepths: this.treeDepths,
|
|
1039
1040
|
batchSizes: this.batchSizes,
|
|
@@ -1042,13 +1043,13 @@ class Poll {
|
|
|
1042
1043
|
messages: this.messages.map((message) => message.toJSON()),
|
|
1043
1044
|
commands: this.commands.map((command) => command.toJSON()),
|
|
1044
1045
|
ballots: this.ballots.map((ballot) => ballot.toJSON()),
|
|
1045
|
-
|
|
1046
|
+
encryptionPublicKeys: this.encryptionPublicKeys.map((encryptionPublicKey) => encryptionPublicKey.serialize()),
|
|
1046
1047
|
currentMessageBatchIndex: this.currentMessageBatchIndex,
|
|
1047
|
-
|
|
1048
|
+
publicKeys: this.publicKeys.map((leaf) => leaf.toJSON()),
|
|
1048
1049
|
pollStateLeaves: this.pollStateLeaves.map((leaf) => leaf.toJSON()),
|
|
1049
1050
|
results: this.tallyResult.map((result) => result.toString()),
|
|
1050
1051
|
numBatchesProcessed: this.numBatchesProcessed,
|
|
1051
|
-
|
|
1052
|
+
totalSignups: this.totalSignups.toString(),
|
|
1052
1053
|
chainHash: this.chainHash.toString(),
|
|
1053
1054
|
pollNullifiers: [...this.pollNullifiers.keys()].map((nullifier) => nullifier.toString()),
|
|
1054
1055
|
batchHashes: this.batchHashes.map((batchHash) => batchHash.toString()),
|
|
@@ -1065,7 +1066,7 @@ class Poll {
|
|
|
1065
1066
|
// set all properties
|
|
1066
1067
|
poll.pollStateLeaves = json.pollStateLeaves.map((leaf) => domainobjs_1.StateLeaf.fromJSON(leaf));
|
|
1067
1068
|
poll.ballots = json.ballots.map((ballot) => domainobjs_1.Ballot.fromJSON(ballot));
|
|
1068
|
-
poll.
|
|
1069
|
+
poll.encryptionPublicKeys = json.encryptionPublicKeys.map((key) => domainobjs_1.PublicKey.deserialize(key));
|
|
1069
1070
|
poll.messages = json.messages.map((message) => domainobjs_1.Message.fromJSON(message));
|
|
1070
1071
|
poll.commands = json.commands.map((command) => domainobjs_1.PCommand.fromJSON(command));
|
|
1071
1072
|
poll.tallyResult = json.results.map((result) => BigInt(result));
|
|
@@ -1075,7 +1076,7 @@ class Poll {
|
|
|
1075
1076
|
poll.batchHashes = json.batchHashes.map((batchHash) => BigInt(batchHash));
|
|
1076
1077
|
poll.pollNullifiers = new Map(json.pollNullifiers.map((nullifier) => [BigInt(nullifier), true]));
|
|
1077
1078
|
// copy maci state
|
|
1078
|
-
poll.updatePoll(BigInt(json.
|
|
1079
|
+
poll.updatePoll(BigInt(json.totalSignups));
|
|
1079
1080
|
return poll;
|
|
1080
1081
|
}
|
|
1081
1082
|
}
|