@maci-protocol/core 0.0.0-ci.ba71b1e → 0.0.0-ci.bd42e06
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/build/ts/MaciState.d.ts +3 -1
- package/build/ts/MaciState.d.ts.map +1 -1
- package/build/ts/MaciState.js +4 -3
- package/build/ts/MaciState.js.map +1 -1
- package/build/ts/Poll.d.ts +24 -15
- package/build/ts/Poll.d.ts.map +1 -1
- package/build/ts/Poll.js +152 -190
- package/build/ts/Poll.js.map +1 -1
- package/build/ts/index.d.ts +1 -0
- package/build/ts/index.d.ts.map +1 -1
- package/build/ts/index.js +7 -5
- package/build/ts/index.js.map +1 -1
- package/build/ts/utils/constants.d.ts +8 -0
- package/build/ts/utils/constants.d.ts.map +1 -1
- package/build/ts/utils/constants.js +10 -1
- package/build/ts/utils/constants.js.map +1 -1
- package/build/ts/utils/errors.d.ts +2 -1
- package/build/ts/utils/errors.d.ts.map +1 -1
- package/build/ts/utils/errors.js +1 -0
- package/build/ts/utils/errors.js.map +1 -1
- package/build/ts/utils/types.d.ts +32 -9
- package/build/ts/utils/types.d.ts.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +8 -6
package/build/ts/Poll.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.Poll = void 0;
|
|
|
7
7
|
const crypto_1 = require("@maci-protocol/crypto");
|
|
8
8
|
const domainobjs_1 = require("@maci-protocol/domainobjs");
|
|
9
9
|
const lean_imt_1 = require("@zk-kit/lean-imt");
|
|
10
|
+
const omit_1 = __importDefault(require("lodash/omit"));
|
|
10
11
|
const assert_1 = __importDefault(require("assert"));
|
|
11
12
|
const constants_1 = require("./utils/constants");
|
|
12
13
|
const errors_1 = require("./utils/errors");
|
|
@@ -23,7 +24,7 @@ class Poll {
|
|
|
23
24
|
* @param maciStateRef - The reference to the MACI state.
|
|
24
25
|
* @param pollId - The poll id
|
|
25
26
|
*/
|
|
26
|
-
constructor(pollEndTimestamp, coordinatorKeypair, treeDepths, batchSizes, maciStateRef, voteOptions) {
|
|
27
|
+
constructor(pollEndTimestamp, coordinatorKeypair, treeDepths, batchSizes, maciStateRef, voteOptions, mode) {
|
|
27
28
|
this.ballots = [];
|
|
28
29
|
this.messages = [];
|
|
29
30
|
this.commands = [];
|
|
@@ -34,7 +35,7 @@ class Poll {
|
|
|
34
35
|
this.numBatchesProcessed = 0;
|
|
35
36
|
this.sbSalts = {};
|
|
36
37
|
this.resultRootSalts = {};
|
|
37
|
-
this.
|
|
38
|
+
this.perVoteOptionSpentVoiceCreditsRootSalts = {};
|
|
38
39
|
this.spentVoiceCreditSubtotalSalts = {};
|
|
39
40
|
// For vote tallying
|
|
40
41
|
this.tallyResult = [];
|
|
@@ -86,7 +87,7 @@ class Poll {
|
|
|
86
87
|
// not match. For this, we must only copy up to the number of signups
|
|
87
88
|
// Copy the state tree, ballot tree, state leaves, and ballot leaves
|
|
88
89
|
// start by setting the number of signups
|
|
89
|
-
this.
|
|
90
|
+
this.setTotalSignups(totalSignups);
|
|
90
91
|
// copy up to totalSignups state leaves
|
|
91
92
|
this.publicKeys = this.maciStateRef.publicKeys.slice(0, Number(this.totalSignups)).map((x) => x.copy());
|
|
92
93
|
// ensure we have the correct actual state tree depth value
|
|
@@ -118,11 +119,11 @@ class Poll {
|
|
|
118
119
|
* @param encryptionPublicKey - The public key associated with the encryption private key.
|
|
119
120
|
* @returns A number of variables which will be used in the zk-SNARK circuit.
|
|
120
121
|
*/
|
|
121
|
-
this.processMessage = (message, encryptionPublicKey
|
|
122
|
+
this.processMessage = (message, encryptionPublicKey) => {
|
|
122
123
|
try {
|
|
123
124
|
// Decrypt the message
|
|
124
125
|
const sharedKey = domainobjs_1.Keypair.generateEcdhSharedKey(this.coordinatorKeypair.privateKey, encryptionPublicKey);
|
|
125
|
-
const { command, signature } = domainobjs_1.
|
|
126
|
+
const { command, signature } = domainobjs_1.VoteCommand.decrypt(message, sharedKey);
|
|
126
127
|
const stateLeafIndex = command.stateIndex;
|
|
127
128
|
// If the state tree index in the command is invalid, do nothing
|
|
128
129
|
if (stateLeafIndex >= BigInt(this.ballots.length) ||
|
|
@@ -148,24 +149,20 @@ class Poll {
|
|
|
148
149
|
}
|
|
149
150
|
const voteOptionIndex = Number(command.voteOptionIndex);
|
|
150
151
|
const originalVoteWeight = ballot.votes[voteOptionIndex];
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// but we need to ensure that we are not going >= balance
|
|
158
|
-
// @note that above comment is valid for quadratic voting
|
|
159
|
-
// for non quadratic voting, we simply remove the exponentiation
|
|
160
|
-
const voiceCreditsLeft = qv
|
|
161
|
-
? stateLeaf.voiceCreditBalance +
|
|
162
|
-
originalVoteWeight * originalVoteWeight -
|
|
163
|
-
command.newVoteWeight * command.newVoteWeight
|
|
164
|
-
: stateLeaf.voiceCreditBalance + originalVoteWeight - command.newVoteWeight;
|
|
152
|
+
const voiceCreditsLeft = this.getVoiceCreditsLeft({
|
|
153
|
+
stateLeaf,
|
|
154
|
+
originalVoteWeight,
|
|
155
|
+
newVoteWeight: command.newVoteWeight,
|
|
156
|
+
mode: this.mode,
|
|
157
|
+
});
|
|
165
158
|
// If the remaining voice credits is insufficient, do nothing
|
|
166
159
|
if (voiceCreditsLeft < 0n) {
|
|
167
160
|
throw new errors_1.ProcessMessageError(errors_1.ProcessMessageErrors.InsufficientVoiceCredits);
|
|
168
161
|
}
|
|
162
|
+
// If there are some voice credits left for full spent mode, do nothing
|
|
163
|
+
if (this.mode === constants_1.EMode.FULL && voiceCreditsLeft > 0n) {
|
|
164
|
+
throw new errors_1.ProcessMessageError(errors_1.ProcessMessageErrors.InvalidVoiceCredits);
|
|
165
|
+
}
|
|
169
166
|
// Deep-copy the state leaf and update its attributes
|
|
170
167
|
const newStateLeaf = stateLeaf.copy();
|
|
171
168
|
newStateLeaf.voiceCreditBalance = voiceCreditsLeft;
|
|
@@ -177,20 +174,23 @@ class Poll {
|
|
|
177
174
|
newBallot.nonce += 1n;
|
|
178
175
|
// we change the vote for this exact vote option
|
|
179
176
|
newBallot.votes[voteOptionIndex] = command.newVoteWeight;
|
|
177
|
+
if (this.mode === constants_1.EMode.FULL) {
|
|
178
|
+
newBallot.votes = newBallot.votes.map((votes, index) => (voteOptionIndex === index ? votes : 0n));
|
|
179
|
+
}
|
|
180
180
|
// calculate the path elements for the state tree given the original state tree (before any changes)
|
|
181
181
|
// changes could effectively be made by this new vote - either a key change or vote change
|
|
182
182
|
// would result in a different state leaf
|
|
183
|
-
const originalStateLeafPathElements = this.pollStateTree
|
|
183
|
+
const { pathElements: originalStateLeafPathElements } = this.pollStateTree.genProof(Number(stateLeafIndex));
|
|
184
184
|
// calculate the path elements for the ballot tree given the original ballot tree (before any changes)
|
|
185
185
|
// changes could effectively be made by this new ballot
|
|
186
|
-
const originalBallotPathElements = this.ballotTree
|
|
186
|
+
const { pathElements: originalBallotPathElements } = this.ballotTree.genProof(Number(stateLeafIndex));
|
|
187
187
|
// create a new quinary tree where we insert the votes of the origin (up until this message is processed) ballot
|
|
188
|
-
const
|
|
188
|
+
const voteTree = new crypto_1.IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, constants_1.VOTE_OPTION_TREE_ARITY, crypto_1.hash5);
|
|
189
189
|
for (let i = 0; i < this.ballots[0].votes.length; i += 1) {
|
|
190
|
-
|
|
190
|
+
voteTree.insert(ballot.votes[i]);
|
|
191
191
|
}
|
|
192
192
|
// calculate the path elements for the vote option tree given the original vote option tree (before any changes)
|
|
193
|
-
const originalVoteWeightsPathElements =
|
|
193
|
+
const { pathElements: originalVoteWeightsPathElements } = voteTree.genProof(voteOptionIndex);
|
|
194
194
|
// we return the data which is then to be used in the processMessage circuit
|
|
195
195
|
// to generate a proof of processing
|
|
196
196
|
return {
|
|
@@ -239,14 +239,14 @@ class Poll {
|
|
|
239
239
|
const sharedKey = domainobjs_1.Keypair.generateEcdhSharedKey(this.coordinatorKeypair.privateKey, encryptionPublicKey);
|
|
240
240
|
try {
|
|
241
241
|
// step 2. we decrypt it
|
|
242
|
-
const { command } = domainobjs_1.
|
|
242
|
+
const { command } = domainobjs_1.VoteCommand.decrypt(message, sharedKey);
|
|
243
243
|
// step 3. we store it in the commands array
|
|
244
244
|
this.commands.push(command);
|
|
245
245
|
}
|
|
246
246
|
catch (e) {
|
|
247
247
|
// if there is an error we store an empty command
|
|
248
|
-
const
|
|
249
|
-
const command = new domainobjs_1.
|
|
248
|
+
const keypair = new domainobjs_1.Keypair();
|
|
249
|
+
const command = new domainobjs_1.VoteCommand(0n, keypair.publicKey, 0n, 0n, 0n, 0n, 0n);
|
|
250
250
|
this.commands.push(command);
|
|
251
251
|
}
|
|
252
252
|
};
|
|
@@ -362,7 +362,7 @@ class Poll {
|
|
|
362
362
|
* @param quiet - Whether to log errors or not
|
|
363
363
|
* @returns stringified circuit inputs
|
|
364
364
|
*/
|
|
365
|
-
this.processMessages = (pollId,
|
|
365
|
+
this.processMessages = (pollId, quiet = true) => {
|
|
366
366
|
(0, assert_1.default)(this.hasUnprocessedMessages(), "No more messages to process");
|
|
367
367
|
const batchSize = this.batchSizes.messageBatchSize;
|
|
368
368
|
if (this.numBatchesProcessed === 0) {
|
|
@@ -411,23 +411,23 @@ class Poll {
|
|
|
411
411
|
encryptionPublicKey = this.encryptionPublicKeys[idx];
|
|
412
412
|
try {
|
|
413
413
|
// check if the command is valid
|
|
414
|
-
const
|
|
415
|
-
const index =
|
|
414
|
+
const { stateLeafIndex, originalStateLeaf, originalBallot, originalVoteWeight, originalVoteWeightsPathElements, originalStateLeafPathElements, originalBallotPathElements, newStateLeaf, newBallot, } = this.processMessage(message, encryptionPublicKey);
|
|
415
|
+
const index = stateLeafIndex;
|
|
416
416
|
// we add at position 0 the original data
|
|
417
|
-
currentStateLeaves.unshift(
|
|
418
|
-
currentBallots.unshift(
|
|
419
|
-
currentVoteWeights.unshift(
|
|
420
|
-
currentVoteWeightsPathElements.unshift(
|
|
421
|
-
currentStateLeavesPathElements.unshift(
|
|
422
|
-
currentBallotsPathElements.unshift(
|
|
417
|
+
currentStateLeaves.unshift(originalStateLeaf);
|
|
418
|
+
currentBallots.unshift(originalBallot);
|
|
419
|
+
currentVoteWeights.unshift(originalVoteWeight);
|
|
420
|
+
currentVoteWeightsPathElements.unshift(originalVoteWeightsPathElements);
|
|
421
|
+
currentStateLeavesPathElements.unshift(originalStateLeafPathElements);
|
|
422
|
+
currentBallotsPathElements.unshift(originalBallotPathElements);
|
|
423
423
|
// update the state leaves with the new state leaf (result of processing the message)
|
|
424
|
-
this.pollStateLeaves[index] =
|
|
424
|
+
this.pollStateLeaves[index] = newStateLeaf.copy();
|
|
425
425
|
// we also update the state tree with the hash of the new state leaf
|
|
426
|
-
this.pollStateTree?.update(index,
|
|
426
|
+
this.pollStateTree?.update(index, newStateLeaf.hash());
|
|
427
427
|
// store the new ballot
|
|
428
|
-
this.ballots[index] =
|
|
428
|
+
this.ballots[index] = newBallot;
|
|
429
429
|
// update the ballot tree
|
|
430
|
-
this.ballotTree?.update(index,
|
|
430
|
+
this.ballotTree?.update(index, newBallot.hash());
|
|
431
431
|
}
|
|
432
432
|
catch (e) {
|
|
433
433
|
// if the error is not a ProcessMessageError we throw it and exit here
|
|
@@ -449,7 +449,7 @@ class Poll {
|
|
|
449
449
|
// generate shared key
|
|
450
450
|
const sharedKey = domainobjs_1.Keypair.generateEcdhSharedKey(this.coordinatorKeypair.privateKey, encryptionPublicKey);
|
|
451
451
|
// force decrypt it
|
|
452
|
-
const { command } = domainobjs_1.
|
|
452
|
+
const { command } = domainobjs_1.VoteCommand.decrypt(message, sharedKey, true);
|
|
453
453
|
// cache state leaf index
|
|
454
454
|
const stateLeafIndex = command.stateIndex;
|
|
455
455
|
// if the state leaf index is valid then use it
|
|
@@ -467,24 +467,24 @@ class Poll {
|
|
|
467
467
|
if (command.voteOptionIndex < this.voteOptions) {
|
|
468
468
|
currentVoteWeights.unshift(ballot.votes[Number(command.voteOptionIndex)]);
|
|
469
469
|
// create a new quinary tree and add all votes we have so far
|
|
470
|
-
const
|
|
470
|
+
const voteTree = new crypto_1.IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, constants_1.VOTE_OPTION_TREE_ARITY, crypto_1.hash5);
|
|
471
471
|
// fill the vote option tree with the votes we have so far
|
|
472
472
|
for (let j = 0; j < this.ballots[0].votes.length; j += 1) {
|
|
473
|
-
|
|
473
|
+
voteTree.insert(ballot.votes[j]);
|
|
474
474
|
}
|
|
475
475
|
// get the path elements for the first vote leaf
|
|
476
|
-
currentVoteWeightsPathElements.unshift(
|
|
476
|
+
currentVoteWeightsPathElements.unshift(voteTree.genProof(Number(command.voteOptionIndex)).pathElements);
|
|
477
477
|
}
|
|
478
478
|
else {
|
|
479
479
|
currentVoteWeights.unshift(ballot.votes[0]);
|
|
480
480
|
// create a new quinary tree and add all votes we have so far
|
|
481
|
-
const
|
|
481
|
+
const voteTree = new crypto_1.IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, constants_1.VOTE_OPTION_TREE_ARITY, crypto_1.hash5);
|
|
482
482
|
// fill the vote option tree with the votes we have so far
|
|
483
483
|
for (let j = 0; j < this.ballots[0].votes.length; j += 1) {
|
|
484
|
-
|
|
484
|
+
voteTree.insert(ballot.votes[j]);
|
|
485
485
|
}
|
|
486
486
|
// get the path elements for the first vote leaf
|
|
487
|
-
currentVoteWeightsPathElements.unshift(
|
|
487
|
+
currentVoteWeightsPathElements.unshift(voteTree.genProof(0).pathElements);
|
|
488
488
|
}
|
|
489
489
|
}
|
|
490
490
|
else {
|
|
@@ -496,10 +496,10 @@ class Poll {
|
|
|
496
496
|
// Since the command is invalid, we use a zero vote weight
|
|
497
497
|
currentVoteWeights.unshift(this.ballots[0].votes[0]);
|
|
498
498
|
// create a new quinary tree and add an empty vote
|
|
499
|
-
const
|
|
500
|
-
|
|
499
|
+
const voteTree = new crypto_1.IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, constants_1.VOTE_OPTION_TREE_ARITY, crypto_1.hash5);
|
|
500
|
+
voteTree.insert(this.ballots[0].votes[0]);
|
|
501
501
|
// get the path elements for this empty vote weight leaf
|
|
502
|
-
currentVoteWeightsPathElements.unshift(
|
|
502
|
+
currentVoteWeightsPathElements.unshift(voteTree.genProof(0).pathElements);
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
else {
|
|
@@ -517,10 +517,10 @@ class Poll {
|
|
|
517
517
|
// Since the command is invalid, we use a zero vote weight
|
|
518
518
|
currentVoteWeights.unshift(this.ballots[0].votes[0]);
|
|
519
519
|
// create a new quinary tree and add an empty vote
|
|
520
|
-
const
|
|
521
|
-
|
|
520
|
+
const voteTree = new crypto_1.IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, constants_1.VOTE_OPTION_TREE_ARITY, crypto_1.hash5);
|
|
521
|
+
voteTree.insert(this.ballots[0].votes[0]);
|
|
522
522
|
// get the path elements for this empty vote weight leaf
|
|
523
|
-
currentVoteWeightsPathElements.unshift(
|
|
523
|
+
currentVoteWeightsPathElements.unshift(voteTree.genProof(0).pathElements);
|
|
524
524
|
}
|
|
525
525
|
}
|
|
526
526
|
// store the data in the circuit inputs object
|
|
@@ -585,14 +585,14 @@ class Poll {
|
|
|
585
585
|
// generate ecdh key
|
|
586
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.
|
|
588
|
+
const emptyCommand = new domainobjs_1.VoteCommand(0n, key.publicKey, 0n, 0n, 0n, 0n, 0n);
|
|
589
589
|
// encrypt it
|
|
590
|
-
const
|
|
590
|
+
const emptyMessage = emptyCommand.encrypt(emptyCommand.sign(key.privateKey), ecdh);
|
|
591
591
|
// copy the messages to a new array
|
|
592
592
|
let messages = this.messages.map((x) => x.asCircuitInputs());
|
|
593
593
|
// pad with our state index 0 message
|
|
594
594
|
while (messages.length % messageBatchSize > 0) {
|
|
595
|
-
messages.push(
|
|
595
|
+
messages.push(emptyMessage.asCircuitInputs());
|
|
596
596
|
}
|
|
597
597
|
// copy the public keys, pad the array with the last keys if needed
|
|
598
598
|
let encryptionPublicKeys = this.encryptionPublicKeys.map((x) => x.copy());
|
|
@@ -661,6 +661,7 @@ class Poll {
|
|
|
661
661
|
this.hasUntalliedBallots = () => this.numBatchesTallied * this.batchSizes.tallyBatchSize < this.ballots.length;
|
|
662
662
|
/**
|
|
663
663
|
* This method tallies a ballots and updates the tally results.
|
|
664
|
+
*
|
|
664
665
|
* @returns the circuit inputs for the TallyVotes circuit.
|
|
665
666
|
*/
|
|
666
667
|
this.tallyVotes = () => {
|
|
@@ -674,31 +675,40 @@ class Poll {
|
|
|
674
675
|
const batchStartIndex = this.numBatchesTallied * batchSize;
|
|
675
676
|
// get the salts needed for the commitments
|
|
676
677
|
const currentResultsRootSalt = batchStartIndex === 0 ? 0n : this.resultRootSalts[batchStartIndex - batchSize];
|
|
677
|
-
const
|
|
678
|
+
const currentPerVoteOptionSpentVoiceCreditsRootSalt = batchStartIndex === 0 ? 0n : this.perVoteOptionSpentVoiceCreditsRootSalts[batchStartIndex - batchSize];
|
|
678
679
|
const currentSpentVoiceCreditSubtotalSalt = batchStartIndex === 0 ? 0n : this.spentVoiceCreditSubtotalSalts[batchStartIndex - batchSize];
|
|
679
680
|
// generate a commitment to the current results
|
|
680
681
|
const currentResultsCommitment = (0, crypto_1.generateTreeCommitment)(this.tallyResult, currentResultsRootSalt, this.treeDepths.voteOptionTreeDepth);
|
|
681
682
|
// generate a commitment to the current per vote option spent voice credits
|
|
682
|
-
const
|
|
683
|
+
const currentPerVoteOptionSpentVoiceCreditsCommitment = this.generatePerVoteOptionSpentVoiceCreditsCommitment(currentPerVoteOptionSpentVoiceCreditsRootSalt, batchStartIndex, this.mode);
|
|
683
684
|
// generate a commitment to the current spent voice credits
|
|
684
|
-
const currentSpentVoiceCreditsCommitment = this.
|
|
685
|
+
const currentSpentVoiceCreditsCommitment = this.generateSpentVoiceCreditSubtotalCommitment(currentSpentVoiceCreditSubtotalSalt, batchStartIndex, this.mode);
|
|
685
686
|
// the current commitment for the first batch will be 0
|
|
686
687
|
// otherwise calculate as
|
|
687
688
|
// hash([
|
|
688
689
|
// currentResultsCommitment,
|
|
689
690
|
// currentSpentVoiceCreditsCommitment,
|
|
690
|
-
// currentPerVOSpentVoiceCreditsCommitment
|
|
691
691
|
// ])
|
|
692
|
-
|
|
692
|
+
// or for QV
|
|
693
|
+
// hash([
|
|
694
|
+
// currentResultsCommitment,
|
|
695
|
+
// currentSpentVoiceCreditsCommitment,
|
|
696
|
+
// currentPerVoteOptionSpentVoiceCreditsCommitment
|
|
697
|
+
// ])
|
|
698
|
+
const currentTallyCommitmentQv = this.mode !== constants_1.EMode.QV || batchStartIndex === 0
|
|
693
699
|
? 0n
|
|
694
700
|
: (0, crypto_1.hash3)([
|
|
695
701
|
currentResultsCommitment,
|
|
696
702
|
currentSpentVoiceCreditsCommitment,
|
|
697
|
-
|
|
703
|
+
currentPerVoteOptionSpentVoiceCreditsCommitment,
|
|
698
704
|
]);
|
|
705
|
+
const currentTallyCommitmentNonQv = this.mode === constants_1.EMode.QV || batchStartIndex === 0
|
|
706
|
+
? 0n
|
|
707
|
+
: (0, crypto_1.hashLeftRight)(currentResultsCommitment, currentSpentVoiceCreditsCommitment);
|
|
708
|
+
const currentTallyCommitment = currentTallyCommitmentNonQv || currentTallyCommitmentQv;
|
|
699
709
|
const ballots = [];
|
|
700
710
|
const currentResults = this.tallyResult.map((x) => BigInt(x.toString()));
|
|
701
|
-
const
|
|
711
|
+
const currentPerVoteOptionSpentVoiceCredits = this.perVoteOptionSpentVoiceCredits.map((x) => BigInt(x.toString()));
|
|
702
712
|
const currentSpentVoiceCreditSubtotal = BigInt(this.totalSpentVoiceCredits.toString());
|
|
703
713
|
// loop in normal order to tally the ballots one by one
|
|
704
714
|
for (let i = this.numBatchesTallied * batchSize; i < this.numBatchesTallied * batchSize + batchSize; i += 1) {
|
|
@@ -711,12 +721,17 @@ class Poll {
|
|
|
711
721
|
// for each possible vote option we loop and calculate
|
|
712
722
|
for (let j = 0; j < this.maxVoteOptions; j += 1) {
|
|
713
723
|
const v = this.ballots[i].votes[j];
|
|
714
|
-
// the vote itself will be a quadratic vote (sqrt(voiceCredits))
|
|
715
724
|
this.tallyResult[j] += v;
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
725
|
+
if (this.mode === constants_1.EMode.QV) {
|
|
726
|
+
// the per vote option spent voice credits will be the sum of the squares of the votes
|
|
727
|
+
this.perVoteOptionSpentVoiceCredits[j] += v * v;
|
|
728
|
+
// the total spent voice credits will be the sum of the squares of the votes
|
|
729
|
+
this.totalSpentVoiceCredits += v * v;
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
// the total spent voice credits will be the sum of the votes
|
|
733
|
+
this.totalSpentVoiceCredits += v;
|
|
734
|
+
}
|
|
720
735
|
}
|
|
721
736
|
}
|
|
722
737
|
const emptyBallot = new domainobjs_1.Ballot(this.maxVoteOptions, this.treeDepths.voteOptionTreeDepth);
|
|
@@ -726,24 +741,22 @@ class Poll {
|
|
|
726
741
|
}
|
|
727
742
|
// generate the new salts
|
|
728
743
|
const newResultsRootSalt = (0, crypto_1.generateRandomSalt)();
|
|
729
|
-
const
|
|
744
|
+
const newPerVoteOptionSpentVoiceCreditsRootSalt = (0, crypto_1.generateRandomSalt)();
|
|
730
745
|
const newSpentVoiceCreditSubtotalSalt = (0, crypto_1.generateRandomSalt)();
|
|
731
746
|
// and save them to be used in the next batch
|
|
732
747
|
this.resultRootSalts[batchStartIndex] = newResultsRootSalt;
|
|
733
|
-
this.
|
|
748
|
+
this.perVoteOptionSpentVoiceCreditsRootSalts[batchStartIndex] = newPerVoteOptionSpentVoiceCreditsRootSalt;
|
|
734
749
|
this.spentVoiceCreditSubtotalSalts[batchStartIndex] = newSpentVoiceCreditSubtotalSalt;
|
|
735
750
|
// generate the new results commitment with the new salts and data
|
|
736
751
|
const newResultsCommitment = (0, crypto_1.generateTreeCommitment)(this.tallyResult, newResultsRootSalt, this.treeDepths.voteOptionTreeDepth);
|
|
737
752
|
// generate the new spent voice credits commitment with the new salts and data
|
|
738
|
-
const newSpentVoiceCreditsCommitment = this.
|
|
753
|
+
const newSpentVoiceCreditsCommitment = this.generateSpentVoiceCreditSubtotalCommitment(newSpentVoiceCreditSubtotalSalt, batchStartIndex + batchSize, this.mode);
|
|
739
754
|
// generate the new per vote option spent voice credits commitment with the new salts and data
|
|
740
|
-
const
|
|
755
|
+
const newPerVoteOptionSpentVoiceCreditsCommitment = this.generatePerVoteOptionSpentVoiceCreditsCommitment(newPerVoteOptionSpentVoiceCreditsRootSalt, batchStartIndex + batchSize, this.mode);
|
|
741
756
|
// generate the new tally commitment
|
|
742
|
-
const newTallyCommitment =
|
|
743
|
-
newResultsCommitment,
|
|
744
|
-
newSpentVoiceCreditsCommitment
|
|
745
|
-
newPerVOSpentVoiceCreditsCommitment,
|
|
746
|
-
]);
|
|
757
|
+
const newTallyCommitment = this.mode === constants_1.EMode.QV
|
|
758
|
+
? (0, crypto_1.hash3)([newResultsCommitment, newSpentVoiceCreditsCommitment, newPerVoteOptionSpentVoiceCreditsCommitment])
|
|
759
|
+
: (0, crypto_1.hashLeftRight)(newResultsCommitment, newSpentVoiceCreditsCommitment);
|
|
747
760
|
// cache vars
|
|
748
761
|
const stateRoot = this.pollStateTree.root;
|
|
749
762
|
const ballotRoot = this.ballotTree.root;
|
|
@@ -751,7 +764,7 @@ class Poll {
|
|
|
751
764
|
const sbCommitment = (0, crypto_1.hash3)([stateRoot, ballotRoot, sbSalt]);
|
|
752
765
|
const ballotSubrootProof = this.ballotTree?.genSubrootProof(batchStartIndex, batchStartIndex + batchSize);
|
|
753
766
|
const votes = ballots.map((x) => x.votes);
|
|
754
|
-
const circuitInputs = (0, crypto_1.stringifyBigInts)({
|
|
767
|
+
const circuitInputs = (0, crypto_1.stringifyBigInts)((0, omit_1.default)({
|
|
755
768
|
stateRoot,
|
|
756
769
|
ballotRoot,
|
|
757
770
|
sbSalt,
|
|
@@ -767,100 +780,18 @@ class Poll {
|
|
|
767
780
|
currentResultsRootSalt,
|
|
768
781
|
currentSpentVoiceCreditSubtotal,
|
|
769
782
|
currentSpentVoiceCreditSubtotalSalt,
|
|
770
|
-
|
|
771
|
-
|
|
783
|
+
currentPerVoteOptionSpentVoiceCredits,
|
|
784
|
+
currentPerVoteOptionSpentVoiceCreditsRootSalt,
|
|
785
|
+
newPerVoteOptionSpentVoiceCreditsRootSalt,
|
|
772
786
|
newResultsRootSalt,
|
|
773
|
-
newPerVOSpentVoiceCreditsRootSalt,
|
|
774
787
|
newSpentVoiceCreditSubtotalSalt,
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
throw new Error("You must process the messages first");
|
|
783
|
-
}
|
|
784
|
-
const batchSize = this.batchSizes.tallyBatchSize;
|
|
785
|
-
(0, assert_1.default)(this.hasUntalliedBallots(), "No more ballots to tally");
|
|
786
|
-
// calculate where we start tallying next
|
|
787
|
-
const batchStartIndex = this.numBatchesTallied * batchSize;
|
|
788
|
-
// get the salts needed for the commitments
|
|
789
|
-
const currentResultsRootSalt = batchStartIndex === 0 ? 0n : this.resultRootSalts[batchStartIndex - batchSize];
|
|
790
|
-
const currentSpentVoiceCreditSubtotalSalt = batchStartIndex === 0 ? 0n : this.spentVoiceCreditSubtotalSalts[batchStartIndex - batchSize];
|
|
791
|
-
// generate a commitment to the current results
|
|
792
|
-
const currentResultsCommitment = (0, crypto_1.generateTreeCommitment)(this.tallyResult, currentResultsRootSalt, this.treeDepths.voteOptionTreeDepth);
|
|
793
|
-
// generate a commitment to the current spent voice credits
|
|
794
|
-
const currentSpentVoiceCreditsCommitment = this.genSpentVoiceCreditSubtotalCommitment(currentSpentVoiceCreditSubtotalSalt, batchStartIndex, false);
|
|
795
|
-
// the current commitment for the first batch will be 0
|
|
796
|
-
// otherwise calculate as
|
|
797
|
-
// hash([
|
|
798
|
-
// currentResultsCommitment,
|
|
799
|
-
// currentSpentVoiceCreditsCommitment,
|
|
800
|
-
// ])
|
|
801
|
-
const currentTallyCommitment = batchStartIndex === 0 ? 0n : (0, crypto_1.hashLeftRight)(currentResultsCommitment, currentSpentVoiceCreditsCommitment);
|
|
802
|
-
const ballots = [];
|
|
803
|
-
const currentResults = this.tallyResult.map((x) => BigInt(x.toString()));
|
|
804
|
-
const currentSpentVoiceCreditSubtotal = BigInt(this.totalSpentVoiceCredits.toString());
|
|
805
|
-
// loop in normal order to tally the ballots one by one
|
|
806
|
-
for (let i = this.numBatchesTallied * batchSize; i < this.numBatchesTallied * batchSize + batchSize; i += 1) {
|
|
807
|
-
// we stop if we have no more ballots to tally
|
|
808
|
-
if (i >= this.ballots.length) {
|
|
809
|
-
break;
|
|
810
|
-
}
|
|
811
|
-
// save to the local ballot array
|
|
812
|
-
ballots.push(this.ballots[i]);
|
|
813
|
-
// for each possible vote option we loop and calculate
|
|
814
|
-
for (let j = 0; j < this.maxVoteOptions; j += 1) {
|
|
815
|
-
const v = this.ballots[i].votes[j];
|
|
816
|
-
this.tallyResult[j] += v;
|
|
817
|
-
// the total spent voice credits will be the sum of the votes
|
|
818
|
-
this.totalSpentVoiceCredits += v;
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
const emptyBallot = new domainobjs_1.Ballot(this.maxVoteOptions, this.treeDepths.voteOptionTreeDepth);
|
|
822
|
-
// pad the ballots array
|
|
823
|
-
while (ballots.length < batchSize) {
|
|
824
|
-
ballots.push(emptyBallot);
|
|
825
|
-
}
|
|
826
|
-
// generate the new salts
|
|
827
|
-
const newResultsRootSalt = (0, crypto_1.generateRandomSalt)();
|
|
828
|
-
const newSpentVoiceCreditSubtotalSalt = (0, crypto_1.generateRandomSalt)();
|
|
829
|
-
// and save them to be used in the next batch
|
|
830
|
-
this.resultRootSalts[batchStartIndex] = newResultsRootSalt;
|
|
831
|
-
this.spentVoiceCreditSubtotalSalts[batchStartIndex] = newSpentVoiceCreditSubtotalSalt;
|
|
832
|
-
// generate the new results commitment with the new salts and data
|
|
833
|
-
const newResultsCommitment = (0, crypto_1.generateTreeCommitment)(this.tallyResult, newResultsRootSalt, this.treeDepths.voteOptionTreeDepth);
|
|
834
|
-
// generate the new spent voice credits commitment with the new salts and data
|
|
835
|
-
const newSpentVoiceCreditsCommitment = this.genSpentVoiceCreditSubtotalCommitment(newSpentVoiceCreditSubtotalSalt, batchStartIndex + batchSize, false);
|
|
836
|
-
// generate the new tally commitment
|
|
837
|
-
const newTallyCommitment = (0, crypto_1.hashLeftRight)(newResultsCommitment, newSpentVoiceCreditsCommitment);
|
|
838
|
-
// cache vars
|
|
839
|
-
const stateRoot = this.pollStateTree.root;
|
|
840
|
-
const ballotRoot = this.ballotTree.root;
|
|
841
|
-
const sbSalt = this.sbSalts[this.currentMessageBatchIndex];
|
|
842
|
-
const sbCommitment = (0, crypto_1.hash3)([stateRoot, ballotRoot, sbSalt]);
|
|
843
|
-
const ballotSubrootProof = this.ballotTree?.genSubrootProof(batchStartIndex, batchStartIndex + batchSize);
|
|
844
|
-
const votes = ballots.map((x) => x.votes);
|
|
845
|
-
const circuitInputs = (0, crypto_1.stringifyBigInts)({
|
|
846
|
-
stateRoot,
|
|
847
|
-
ballotRoot,
|
|
848
|
-
sbSalt,
|
|
849
|
-
index: BigInt(batchStartIndex),
|
|
850
|
-
totalSignups: BigInt(this.totalSignups),
|
|
851
|
-
sbCommitment,
|
|
852
|
-
currentTallyCommitment,
|
|
853
|
-
newTallyCommitment,
|
|
854
|
-
ballots: ballots.map((x) => x.asCircuitInputs()),
|
|
855
|
-
ballotPathElements: ballotSubrootProof.pathElements,
|
|
856
|
-
votes,
|
|
857
|
-
currentResults,
|
|
858
|
-
currentResultsRootSalt,
|
|
859
|
-
currentSpentVoiceCreditSubtotal,
|
|
860
|
-
currentSpentVoiceCreditSubtotalSalt,
|
|
861
|
-
newResultsRootSalt,
|
|
862
|
-
newSpentVoiceCreditSubtotalSalt,
|
|
863
|
-
});
|
|
788
|
+
}, this.mode !== constants_1.EMode.QV
|
|
789
|
+
? [
|
|
790
|
+
"currentPerVoteOptionSpentVoiceCredits",
|
|
791
|
+
"currentPerVoteOptionSpentVoiceCreditsRootSalt",
|
|
792
|
+
"newPerVoteOptionSpentVoiceCreditsRootSalt",
|
|
793
|
+
]
|
|
794
|
+
: []));
|
|
864
795
|
this.numBatchesTallied += 1;
|
|
865
796
|
return circuitInputs;
|
|
866
797
|
};
|
|
@@ -869,19 +800,19 @@ class Poll {
|
|
|
869
800
|
*
|
|
870
801
|
* This is the hash of the total spent voice credits and a salt, computed as Poseidon([totalCredits, _salt]).
|
|
871
802
|
* @param salt - The salt used in the hash function.
|
|
872
|
-
* @param
|
|
873
|
-
* @param
|
|
803
|
+
* @param ballotsToCount - The number of ballots to count for the calculation.
|
|
804
|
+
* @param mode - Voting mode, default QV.
|
|
874
805
|
* @returns Returns the hash of the total spent voice credits and a salt, computed as Poseidon([totalCredits, _salt]).
|
|
875
806
|
*/
|
|
876
|
-
this.
|
|
807
|
+
this.generateSpentVoiceCreditSubtotalCommitment = (salt, ballotsToCount, mode = constants_1.EMode.QV) => {
|
|
877
808
|
let subtotal = 0n;
|
|
878
|
-
for (let i = 0; i <
|
|
809
|
+
for (let i = 0; i < ballotsToCount; i += 1) {
|
|
879
810
|
if (this.ballots.length <= i) {
|
|
880
811
|
break;
|
|
881
812
|
}
|
|
882
813
|
for (let j = 0; j < this.tallyResult.length; j += 1) {
|
|
883
814
|
const v = BigInt(`${this.ballots[i].votes[j]}`);
|
|
884
|
-
subtotal +=
|
|
815
|
+
subtotal += mode === constants_1.EMode.QV ? v * v : v;
|
|
885
816
|
}
|
|
886
817
|
}
|
|
887
818
|
return (0, crypto_1.hashLeftRight)(subtotal, salt);
|
|
@@ -891,20 +822,20 @@ class Poll {
|
|
|
891
822
|
*
|
|
892
823
|
* This is the hash of the Merkle root of the spent voice credits per vote option and a salt, computed as Poseidon([root, _salt]).
|
|
893
824
|
* @param salt - The salt used in the hash function.
|
|
894
|
-
* @param
|
|
895
|
-
* @param
|
|
825
|
+
* @param ballotsToCount - The number of ballots to count for the calculation.
|
|
826
|
+
* @param mode - Voting mode, default is QV.
|
|
896
827
|
* @returns Returns the hash of the Merkle root of the spent voice credits per vote option and a salt, computed as Poseidon([root, _salt]).
|
|
897
828
|
*/
|
|
898
|
-
this.
|
|
829
|
+
this.generatePerVoteOptionSpentVoiceCreditsCommitment = (salt, ballotsToCount, mode = constants_1.EMode.QV) => {
|
|
899
830
|
const leaves = Array(this.tallyResult.length).fill(0n);
|
|
900
|
-
for (let i = 0; i <
|
|
831
|
+
for (let i = 0; i < ballotsToCount; i += 1) {
|
|
901
832
|
// check that is a valid index
|
|
902
833
|
if (i >= this.ballots.length) {
|
|
903
834
|
break;
|
|
904
835
|
}
|
|
905
836
|
for (let j = 0; j < this.tallyResult.length; j += 1) {
|
|
906
837
|
const v = this.ballots[i].votes[j];
|
|
907
|
-
leaves[j] +=
|
|
838
|
+
leaves[j] += mode === constants_1.EMode.QV ? v * v : v;
|
|
908
839
|
}
|
|
909
840
|
}
|
|
910
841
|
return (0, crypto_1.generateTreeCommitment)(leaves, salt, this.treeDepths.voteOptionTreeDepth);
|
|
@@ -915,13 +846,13 @@ class Poll {
|
|
|
915
846
|
*/
|
|
916
847
|
this.copy = () => {
|
|
917
848
|
const copied = new Poll(BigInt(this.pollEndTimestamp.toString()), this.coordinatorKeypair.copy(), {
|
|
918
|
-
|
|
849
|
+
tallyProcessingStateTreeDepth: Number(this.treeDepths.tallyProcessingStateTreeDepth),
|
|
919
850
|
voteOptionTreeDepth: Number(this.treeDepths.voteOptionTreeDepth),
|
|
920
851
|
stateTreeDepth: Number(this.treeDepths.stateTreeDepth),
|
|
921
852
|
}, {
|
|
922
853
|
tallyBatchSize: Number(this.batchSizes.tallyBatchSize.toString()),
|
|
923
854
|
messageBatchSize: Number(this.batchSizes.messageBatchSize.toString()),
|
|
924
|
-
}, this.maciStateRef, this.voteOptions);
|
|
855
|
+
}, this.maciStateRef, this.voteOptions, this.mode);
|
|
925
856
|
copied.publicKeys = this.publicKeys.map((x) => x.copy());
|
|
926
857
|
copied.pollStateLeaves = this.pollStateLeaves.map((x) => x.copy());
|
|
927
858
|
copied.messages = this.messages.map((x) => x.copy());
|
|
@@ -941,7 +872,7 @@ class Poll {
|
|
|
941
872
|
copied.totalSpentVoiceCredits = BigInt(this.totalSpentVoiceCredits.toString());
|
|
942
873
|
copied.sbSalts = {};
|
|
943
874
|
copied.resultRootSalts = {};
|
|
944
|
-
copied.
|
|
875
|
+
copied.perVoteOptionSpentVoiceCreditsRootSalts = {};
|
|
945
876
|
copied.spentVoiceCreditSubtotalSalts = {};
|
|
946
877
|
Object.keys(this.sbSalts).forEach((k) => {
|
|
947
878
|
copied.sbSalts[k] = BigInt(this.sbSalts[k].toString());
|
|
@@ -949,14 +880,14 @@ class Poll {
|
|
|
949
880
|
Object.keys(this.resultRootSalts).forEach((k) => {
|
|
950
881
|
copied.resultRootSalts[k] = BigInt(this.resultRootSalts[k].toString());
|
|
951
882
|
});
|
|
952
|
-
Object.keys(this.
|
|
953
|
-
copied.
|
|
883
|
+
Object.keys(this.perVoteOptionSpentVoiceCreditsRootSalts).forEach((k) => {
|
|
884
|
+
copied.perVoteOptionSpentVoiceCreditsRootSalts[k] = BigInt(this.perVoteOptionSpentVoiceCreditsRootSalts[k].toString());
|
|
954
885
|
});
|
|
955
886
|
Object.keys(this.spentVoiceCreditSubtotalSalts).forEach((k) => {
|
|
956
887
|
copied.spentVoiceCreditSubtotalSalts[k] = BigInt(this.spentVoiceCreditSubtotalSalts[k].toString());
|
|
957
888
|
});
|
|
958
889
|
// update the number of signups
|
|
959
|
-
copied.
|
|
890
|
+
copied.setTotalSignups(this.totalSignups);
|
|
960
891
|
return copied;
|
|
961
892
|
};
|
|
962
893
|
/**
|
|
@@ -966,7 +897,7 @@ class Poll {
|
|
|
966
897
|
*/
|
|
967
898
|
this.equals = (poll) => {
|
|
968
899
|
const result = this.coordinatorKeypair.equals(poll.coordinatorKeypair) &&
|
|
969
|
-
this.treeDepths.
|
|
900
|
+
this.treeDepths.tallyProcessingStateTreeDepth === poll.treeDepths.tallyProcessingStateTreeDepth &&
|
|
970
901
|
this.treeDepths.voteOptionTreeDepth === poll.treeDepths.voteOptionTreeDepth &&
|
|
971
902
|
this.batchSizes.tallyBatchSize === poll.batchSizes.tallyBatchSize &&
|
|
972
903
|
this.batchSizes.messageBatchSize === poll.batchSizes.messageBatchSize &&
|
|
@@ -1000,7 +931,7 @@ class Poll {
|
|
|
1000
931
|
* Set the number of signups to match the ones from the contract
|
|
1001
932
|
* @param totalSignups - the number of signups
|
|
1002
933
|
*/
|
|
1003
|
-
this.
|
|
934
|
+
this.setTotalSignups = (totalSignups) => {
|
|
1004
935
|
this.totalSignups = totalSignups;
|
|
1005
936
|
};
|
|
1006
937
|
/**
|
|
@@ -1021,6 +952,7 @@ class Poll {
|
|
|
1021
952
|
this.pollId = BigInt(maciStateRef.polls.size);
|
|
1022
953
|
this.actualStateTreeDepth = treeDepths.stateTreeDepth;
|
|
1023
954
|
this.currentMessageBatchIndex = 0;
|
|
955
|
+
this.mode = mode;
|
|
1024
956
|
this.pollNullifiers = new Map();
|
|
1025
957
|
this.tallyResult = new Array(this.maxVoteOptions).fill(0n);
|
|
1026
958
|
this.perVoteOptionSpentVoiceCredits = new Array(this.maxVoteOptions).fill(0n);
|
|
@@ -1028,6 +960,35 @@ class Poll {
|
|
|
1028
960
|
this.emptyBallot = domainobjs_1.Ballot.genBlankBallot(this.maxVoteOptions, treeDepths.voteOptionTreeDepth);
|
|
1029
961
|
this.ballots.push(this.emptyBallot);
|
|
1030
962
|
}
|
|
963
|
+
/**
|
|
964
|
+
* Get voice credits left for the voting command.
|
|
965
|
+
*
|
|
966
|
+
* @param args - arguments for getting voice credits
|
|
967
|
+
* @returns voice credits left
|
|
968
|
+
*/
|
|
969
|
+
getVoiceCreditsLeft({ stateLeaf, originalVoteWeight, newVoteWeight, mode }) {
|
|
970
|
+
switch (mode) {
|
|
971
|
+
case constants_1.EMode.QV: {
|
|
972
|
+
// the voice credits left are:
|
|
973
|
+
// voiceCreditsBalance (how many the user has) +
|
|
974
|
+
// voiceCreditsPreviouslySpent (the original vote weight for this option) ** 2 -
|
|
975
|
+
// command.newVoteWeight ** 2 (the new vote weight squared)
|
|
976
|
+
// basically we are replacing the previous vote weight for this
|
|
977
|
+
// particular vote option with the new one
|
|
978
|
+
// but we need to ensure that we are not going >= balance
|
|
979
|
+
return stateLeaf.voiceCreditBalance + originalVoteWeight * originalVoteWeight - newVoteWeight * newVoteWeight;
|
|
980
|
+
}
|
|
981
|
+
case constants_1.EMode.NON_QV:
|
|
982
|
+
case constants_1.EMode.FULL: {
|
|
983
|
+
// for non quadratic voting, we simply remove the exponentiation
|
|
984
|
+
// for full spent voting, it will be zero
|
|
985
|
+
return stateLeaf.voiceCreditBalance + originalVoteWeight - newVoteWeight;
|
|
986
|
+
}
|
|
987
|
+
default: {
|
|
988
|
+
throw new Error("Voting mode is not supported");
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
1031
992
|
/**
|
|
1032
993
|
* Serialize the Poll object to a JSON object
|
|
1033
994
|
* @returns a JSON object
|
|
@@ -1053,6 +1014,7 @@ class Poll {
|
|
|
1053
1014
|
chainHash: this.chainHash.toString(),
|
|
1054
1015
|
pollNullifiers: [...this.pollNullifiers.keys()].map((nullifier) => nullifier.toString()),
|
|
1055
1016
|
batchHashes: this.batchHashes.map((batchHash) => batchHash.toString()),
|
|
1017
|
+
mode: this.mode,
|
|
1056
1018
|
};
|
|
1057
1019
|
}
|
|
1058
1020
|
/**
|
|
@@ -1062,13 +1024,13 @@ class Poll {
|
|
|
1062
1024
|
* @returns a new Poll instance
|
|
1063
1025
|
*/
|
|
1064
1026
|
static fromJSON(json, maciState) {
|
|
1065
|
-
const poll = new Poll(BigInt(json.pollEndTimestamp), new domainobjs_1.Keypair(), json.treeDepths, json.batchSizes, maciState, BigInt(json.voteOptions));
|
|
1027
|
+
const poll = new Poll(BigInt(json.pollEndTimestamp), new domainobjs_1.Keypair(), json.treeDepths, json.batchSizes, maciState, BigInt(json.voteOptions), json.mode);
|
|
1066
1028
|
// set all properties
|
|
1067
1029
|
poll.pollStateLeaves = json.pollStateLeaves.map((leaf) => domainobjs_1.StateLeaf.fromJSON(leaf));
|
|
1068
1030
|
poll.ballots = json.ballots.map((ballot) => domainobjs_1.Ballot.fromJSON(ballot));
|
|
1069
1031
|
poll.encryptionPublicKeys = json.encryptionPublicKeys.map((key) => domainobjs_1.PublicKey.deserialize(key));
|
|
1070
1032
|
poll.messages = json.messages.map((message) => domainobjs_1.Message.fromJSON(message));
|
|
1071
|
-
poll.commands = json.commands.map((command) => domainobjs_1.
|
|
1033
|
+
poll.commands = json.commands.map((command) => domainobjs_1.VoteCommand.fromJSON(command));
|
|
1072
1034
|
poll.tallyResult = json.results.map((result) => BigInt(result));
|
|
1073
1035
|
poll.currentMessageBatchIndex = json.currentMessageBatchIndex;
|
|
1074
1036
|
poll.numBatchesProcessed = json.numBatchesProcessed;
|