Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(multiple-polls): allow concurrent polls #1417

Merged
merged 1 commit into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/reusable-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ jobs:
- name: Download zkeys
if: ${{ env.CHANGED == 'false' }}
run: |
pnpm download:test-zkeys
pnpm download:test-zkeys-1-3

- name: ${{ matrix.command }}
run: pnpm run ${{ matrix.command }}
Expand Down
65 changes: 40 additions & 25 deletions circuits/circom/core/non-qv/processMessages.circom
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ include "../../trees/incrementalQuinaryTree.circom";
assert(msgTreeDepth >= msgBatchDepth);

// Default for IQT (quinary trees).
var TREE_ARITY = 5;
var batchSize = TREE_ARITY ** msgBatchDepth;
var MESSAGE_TREE_ARITY = 5;
// Default for Binary trees.
var STATE_TREE_ARITY = 2;
var batchSize = MESSAGE_TREE_ARITY ** msgBatchDepth;
var MSG_LENGTH = 11;
var PACKED_CMD_LENGTH = 4;
var STATE_LEAF_LENGTH = 4;
Expand Down Expand Up @@ -64,7 +66,7 @@ include "../../trees/incrementalQuinaryTree.circom";
// The messages.
signal input msgs[batchSize][MSG_LENGTH];
// Sibling messages.
signal input msgSubrootPathElements[msgTreeDepth - msgBatchDepth][TREE_ARITY - 1];
signal input msgSubrootPathElements[msgTreeDepth - msgBatchDepth][MESSAGE_TREE_ARITY - 1];
// The coordinator's private key.
signal input coordPrivKey;
// The cooordinator's public key (derived from the contract).
Expand All @@ -73,6 +75,10 @@ include "../../trees/incrementalQuinaryTree.circom";
signal input encPubKeys[batchSize][2];
// The current state root (before the processing).
signal input currentStateRoot;
// The actual tree depth (might be <= stateTreeDepth).
// @note it is a public input to ensure fair processing from
// the coordinator (no censoring)
signal input actualStateTreeDepth;

// The state leaves upon which messages are applied.
// transform(currentStateLeaf[4], message5) => newStateLeaf4
Expand All @@ -84,7 +90,7 @@ include "../../trees/incrementalQuinaryTree.circom";

signal input currentStateLeaves[batchSize][STATE_LEAF_LENGTH];
// The Merkle path to each incremental new state root.
signal input currentStateLeavesPathElements[batchSize][stateTreeDepth][TREE_ARITY - 1];
signal input currentStateLeavesPathElements[batchSize][stateTreeDepth][STATE_TREE_ARITY - 1];
// The salted commitment to the state and ballot roots.
signal input currentSbCommitment;
signal input currentSbSalt;
Expand All @@ -95,10 +101,10 @@ include "../../trees/incrementalQuinaryTree.circom";
signal input currentBallotRoot;
// Intermediate ballots.
signal input currentBallots[batchSize][BALLOT_LENGTH];
signal input currentBallotsPathElements[batchSize][stateTreeDepth][TREE_ARITY - 1];
signal input currentBallotsPathElements[batchSize][stateTreeDepth][STATE_TREE_ARITY - 1];
// Intermediate vote weights.
signal input currentVoteWeights[batchSize];
signal input currentVoteWeightsPathElements[batchSize][voteOptionTreeDepth][TREE_ARITY - 1];
signal input currentVoteWeightsPathElements[batchSize][voteOptionTreeDepth][MESSAGE_TREE_ARITY - 1];

// nb. The messages are processed in REVERSE order.
// Therefore, the index of the first message to process does not match the index of the
Expand Down Expand Up @@ -138,7 +144,8 @@ include "../../trees/incrementalQuinaryTree.circom";
msgRoot,
currentSbCommitment,
newSbCommitment,
pollEndTimestamp
pollEndTimestamp,
actualStateTreeDepth
);

// The unpacked values from packedVals.
Expand All @@ -152,12 +159,12 @@ include "../../trees/incrementalQuinaryTree.circom";
// -----------------------------------------------------------------------
// 0. Ensure that the maximum vote options signal is valid and if
// the maximum users signal is valid.
var maxVoValid = LessEqThan(32)([maxVoteOptions, TREE_ARITY ** voteOptionTreeDepth]);
var maxVoValid = LessEqThan(32)([maxVoteOptions, MESSAGE_TREE_ARITY ** voteOptionTreeDepth]);
maxVoValid === 1;

// Check numSignUps <= the max number of users (i.e., number of state leaves
// that can fit the state tree).
var numSignUpsValid = LessEqThan(32)([numSignUps, TREE_ARITY ** stateTreeDepth]);
var numSignUpsValid = LessEqThan(32)([numSignUps, STATE_TREE_ARITY ** stateTreeDepth]);
numSignUpsValid === 1;

// Hash each Message to check their existence in the Message tree.
Expand All @@ -171,7 +178,7 @@ include "../../trees/incrementalQuinaryTree.circom";
// e.g. [m, z, z, z, z] if there is only 1 real message in the batch
// This makes possible to have a batch of messages which is only partially full.
var computedLeaves[batchSize];
var computedPathElements[msgTreeDepth - msgBatchDepth][TREE_ARITY - 1];
var computedPathElements[msgTreeDepth - msgBatchDepth][MESSAGE_TREE_ARITY - 1];
var computedPathIndex[msgTreeDepth - msgBatchDepth];

for (var i = 0; i < batchSize; i++) {
Expand All @@ -180,7 +187,7 @@ include "../../trees/incrementalQuinaryTree.circom";
}

for (var i = 0; i < msgTreeDepth - msgBatchDepth; i++) {
for (var j = 0; j < TREE_ARITY - 1; j++) {
for (var j = 0; j < MESSAGE_TREE_ARITY - 1; j++) {
computedPathElements[i][j] = msgSubrootPathElements[i][j];
}
}
Expand Down Expand Up @@ -267,29 +274,31 @@ include "../../trees/incrementalQuinaryTree.circom";
// Start from batchSize and decrement for process in reverse order.
for (var i = batchSize - 1; i >= 0; i--) {
// Process as vote type message.
var currentStateLeavesPathElement[stateTreeDepth][TREE_ARITY - 1];
var currentBallotPathElement[stateTreeDepth][TREE_ARITY - 1];
var currentVoteWeightsPathElement[voteOptionTreeDepth][TREE_ARITY - 1];
var currentStateLeavesPathElement[stateTreeDepth][STATE_TREE_ARITY - 1];
var currentBallotPathElement[stateTreeDepth][STATE_TREE_ARITY - 1];
var currentVoteWeightsPathElement[voteOptionTreeDepth][MESSAGE_TREE_ARITY - 1];

for (var j = 0; j < stateTreeDepth; j++) {
for (var k = 0; k < TREE_ARITY - 1; k++) {
for (var k = 0; k < STATE_TREE_ARITY - 1; k++) {
currentStateLeavesPathElement[j][k] = currentStateLeavesPathElements[i][j][k];
currentBallotPathElement[j][k] = currentBallotsPathElements[i][j][k];
}
}

for (var j = 0; j < voteOptionTreeDepth; j++) {
for (var k = 0; k < TREE_ARITY - 1; k++) {
for (var k = 0; k < MESSAGE_TREE_ARITY - 1; k++) {
currentVoteWeightsPathElement[j][k] = currentVoteWeightsPathElements[i][j][k];
}
}

(computedNewVoteStateRoot[i], computedNewVoteBallotRoot[i]) = ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth)(
msgs[i][0],
numSignUps,
maxVoteOptions,
pollEndTimestamp,
stateRoots[i + 1],
ballotRoots[i + 1],
actualStateTreeDepth,
currentStateLeaves[i],
currentStateLeavesPathElement,
currentBallots[i],
Expand All @@ -315,6 +324,7 @@ include "../../trees/incrementalQuinaryTree.circom";
msgs[i][1],
msgs[i][2],
numSignUps,
actualStateTreeDepth,
currentStateLeaves[i],
currentStateLeavesPathElement
);
Expand Down Expand Up @@ -349,7 +359,8 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) {
var BALLOT_LENGTH = 2;
var MSG_LENGTH = 11;
var PACKED_CMD_LENGTH = 4;
var TREE_ARITY = 5;
var MESSAGE_TREE_ARITY = 5;
var STATE_TREE_ARITY = 2;
var BALLOT_NONCE_IDX = 0;
// Ballot vote option (VO) root index.
var BALLOT_VO_ROOT_IDX = 1;
Expand All @@ -374,19 +385,21 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) {
signal input currentStateRoot;
// The current value of the ballot tree root.
signal input currentBallotRoot;
// The actual tree depth (might be <= stateTreeDepth).
signal input actualStateTreeDepth;

// The state leaf and related path elements.
signal input stateLeaf[STATE_LEAF_LENGTH];
// Sibling nodes at each level of the state tree to verify the specific state leaf.
signal input stateLeafPathElements[stateTreeDepth][TREE_ARITY - 1];
signal input stateLeafPathElements[stateTreeDepth][STATE_TREE_ARITY - 1];

// The ballot and related path elements.
signal input ballot[BALLOT_LENGTH];
signal input ballotPathElements[stateTreeDepth][TREE_ARITY - 1];
signal input ballotPathElements[stateTreeDepth][STATE_TREE_ARITY - 1];

// The current vote weight and related path elements.
signal input currentVoteWeight;
signal input currentVoteWeightsPathElements[voteOptionTreeDepth][TREE_ARITY - 1];
signal input currentVoteWeightsPathElements[voteOptionTreeDepth][MESSAGE_TREE_ARITY - 1];

// Inputs related to the command being processed.
signal input cmdStateIndex;
Expand Down Expand Up @@ -450,12 +463,13 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) {

var stateLeafIndexValid = SafeLessThan(N_BITS)([indexByType, numSignUps]);
var stateIndexMux = Mux1()([0, indexByType], stateLeafIndexValid);
var computedStateLeafPathIndices[stateTreeDepth] = QuinGeneratePathIndices(stateTreeDepth)(stateIndexMux);
var computedStateLeafPathIndices[stateTreeDepth] = MerkleGeneratePathIndices(stateTreeDepth)(stateIndexMux);

// 3. Verify that the original state leaf exists in the given state root.
var stateLeafHash = PoseidonHasher(4)(stateLeaf);
var stateLeafQip = QuinTreeInclusionProof(stateTreeDepth)(
var stateLeafQip = BinaryMerkleRoot(stateTreeDepth)(
stateLeafHash,
actualStateTreeDepth,
computedStateLeafPathIndices,
stateLeafPathElements
);
Expand All @@ -468,7 +482,7 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) {
ballot[BALLOT_VO_ROOT_IDX]
]);

var computedBallotQip = QuinTreeInclusionProof(stateTreeDepth)(
var computedBallotQip = MerkleTreeInclusionProof(stateTreeDepth)(
computedBallot,
computedStateLeafPathIndices,
ballotPathElements
Expand Down Expand Up @@ -538,8 +552,9 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) {
stateLeaf[STATE_LEAF_TIMESTAMP_IDX]
]);

var computedNewStateLeafQip = QuinTreeInclusionProof(stateTreeDepth)(
var computedNewStateLeafQip = BinaryMerkleRoot(stateTreeDepth)(
computedNewStateLeafhash,
actualStateTreeDepth,
computedStateLeafPathIndices,
stateLeafPathElements
);
Expand All @@ -549,7 +564,7 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) {
// 7. Generate a new ballot root.
var newBallotNonceMux = Mux1()([ballot[BALLOT_NONCE_IDX], computedNewBallotNonce], computedIsMessageEqual);
var computedNewBallot = PoseidonHasher(2)([newBallotNonceMux, newBallotVoRoot]);
var computedNewBallotQip = QuinTreeInclusionProof(stateTreeDepth)(
var computedNewBallotQip = MerkleTreeInclusionProof(stateTreeDepth)(
computedNewBallot,
computedStateLeafPathIndices,
ballotPathElements
Expand Down
14 changes: 8 additions & 6 deletions circuits/circom/core/non-qv/tallyVotes.circom
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ include "./comparators.circom";
// zk-kit import
include "./unpack-element.circom";
// local imports
include "../../trees/incrementalMerkleTree.circom";
include "../../trees/incrementalQuinaryTree.circom";
include "../../utils/calculateTotal.circom";
include "../../utils/hashers.circom";
Expand All @@ -28,9 +29,10 @@ template TallyVotesNonQv(

// Number of children per node in the tree, defining the tree's branching factor.
var TREE_ARITY = 5;
var BALLOT_TREE_ARITY = 2;

// The number of ballots processed at once, determined by the depth of the intermediate state tree.
var batchSize = TREE_ARITY ** intStateTreeDepth;
var batchSize = BALLOT_TREE_ARITY ** intStateTreeDepth;
// Number of voting options available, determined by the depth of the vote option tree.
var numVoteOptions = TREE_ARITY ** voteOptionTreeDepth;

Expand Down Expand Up @@ -69,7 +71,7 @@ template TallyVotesNonQv(

// Ballots and their corresponding path elements for verification in the tree.
signal input ballots[batchSize][BALLOT_LENGTH];
signal input ballotPathElements[k][TREE_ARITY - 1];
signal input ballotPathElements[k][BALLOT_TREE_ARITY - 1];
signal input votes[batchSize][numVoteOptions];

// Current results for each vote option.
Expand Down Expand Up @@ -122,14 +124,14 @@ template TallyVotesNonQv(
computedBallotHashers[i] = PoseidonHasher(2)([ballots[i][BALLOT_NONCE_IDX], ballots[i][BALLOT_VO_ROOT_IDX]]);
}

var computedBallotSubroot = QuinCheckRoot(intStateTreeDepth)(computedBallotHashers);
var computedBallotPathIndices[k] = QuinGeneratePathIndices(k)(computedBatchNum);
var computedBallotSubroot = CheckRoot(intStateTreeDepth)(computedBallotHashers);
var computedBallotPathIndices[k] = MerkleGeneratePathIndices(k)(computedBatchNum);

// Verifies each ballot's existence within the ballot tree.
QuinLeafExists(k)(
LeafExists(k)(
computedBallotSubroot,
computedBallotPathIndices,
ballotPathElements,
computedBallotPathIndices,
ballotRoot
);

Expand Down
Loading
Loading