Skip to content

Commit

Permalink
Merge pull request #1417 from privacy-scaling-explorations/feat/multi…
Browse files Browse the repository at this point in the history
…ple-polls

feat(multiple-polls): allow concurrent polls by using a lazyIMT structure
  • Loading branch information
ctrlc03 authored May 2, 2024
2 parents 153326b + 14e89ba commit 85b4815
Show file tree
Hide file tree
Showing 60 changed files with 823 additions and 801 deletions.
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

0 comments on commit 85b4815

Please sign in to comment.