Skip to content

Commit

Permalink
Merge pull request #1700 from privacy-scaling-explorations/chore/cont…
Browse files Browse the repository at this point in the history
…racts-optimisations-mp

chore: optimize message processor and tally
  • Loading branch information
0xmad authored Aug 1, 2024
2 parents f1f0b4f + d75b244 commit 624b8de
Show file tree
Hide file tree
Showing 43 changed files with 438 additions and 1,268 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ module.exports = {
"import/no-extraneous-dependencies": [
"error",
{
devDependencies: ["**/*.test.ts", "**/__benchmarks__/**"],
devDependencies: ["**/*.test.ts", "**/__benchmarks__/**", "**/tests/**", "**/__tests__/**"],
},
],
"no-debugger": isProduction ? "error" : "off",
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/reusable-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,6 @@ jobs:
run: |
pnpm run build
- name: Run hardhat fork
run: |
cd contracts
pnpm run hardhat &
sleep 5
- name: Download rapidsnark (1c137)
run: |
mkdir -p ~/rapidsnark/build
Expand All @@ -90,6 +84,12 @@ jobs:
run: |
pnpm download-zkeys:test
- name: Run hardhat fork
run: |
pnpm run hardhat &
sleep 5
working-directory: contracts

- name: ${{ matrix.command }}
run: pnpm run ${{ matrix.command }}

Expand Down
2 changes: 2 additions & 0 deletions circuits/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ ptau
input.json
circom/main
circom/test
zkeys/

26 changes: 22 additions & 4 deletions circuits/circom/circuits.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,42 @@
"file": "./core/qv/processMessages",
"template": "ProcessMessages",
"params": [10, 2, 1, 2],
"pubs": ["inputHash"]
"pubs": [
"numSignUps",
"index",
"batchEndIndex",
"msgRoot",
"currentSbCommitment",
"newSbCommitment",
"pollEndTimestamp",
"actualStateTreeDepth"
]
},
"ProcessMessagesNonQv_10-2-1-2_test": {
"file": "./core/non-qv/processMessages",
"template": "ProcessMessagesNonQv",
"params": [10, 2, 1, 2],
"pubs": ["inputHash"]
"pubs": [
"numSignUps",
"index",
"batchEndIndex",
"msgRoot",
"currentSbCommitment",
"newSbCommitment",
"pollEndTimestamp",
"actualStateTreeDepth"
]
},
"TallyVotes_10-1-2_test": {
"file": "./core/qv/tallyVotes",
"template": "TallyVotes",
"params": [10, 1, 2],
"pubs": ["inputHash"]
"pubs": ["index", "numSignUps", "sbCommitment", "currentTallyCommitment", "newTallyCommitment"]
},
"TallyVotesNonQv_10-1-2_test": {
"file": "./core/non-qv/tallyVotes",
"template": "TallyVotesNonQv",
"params": [10, 1, 2],
"pubs": ["inputHash"]
"pubs": ["index", "numSignUps", "sbCommitment", "currentTallyCommitment", "newTallyCommitment"]
}
}
61 changes: 11 additions & 50 deletions circuits/circom/core/non-qv/processMessages.circom
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ include "./safe-comparators.circom";
include "../../utils/hashers.circom";
include "../../utils/messageToCommand.circom";
include "../../utils/privToPubKey.circom";
include "../../utils/processMessagesInputHasher.circom";
include "../../utils/non-qv/stateLeafAndBallotTransformer.circom";
include "../../trees/incrementalMerkleTree.circom";
include "../../trees/incrementalQuinaryTree.circom";
Expand Down Expand Up @@ -36,6 +35,7 @@ include "../../trees/incrementalQuinaryTree.circom";
// Default for Binary trees.
var STATE_TREE_ARITY = 2;
var batchSize = MESSAGE_TREE_ARITY ** msgBatchDepth;
var maxVoteOptions = MESSAGE_TREE_ARITY ** voteOptionTreeDepth;
var MSG_LENGTH = 10;
var PACKED_CMD_LENGTH = 4;
var STATE_LEAF_LENGTH = 4;
Expand All @@ -48,17 +48,8 @@ include "../../trees/incrementalQuinaryTree.circom";
var STATE_LEAF_TIMESTAMP_IDX = 3;
var msgTreeZeroValue = 8370432830353022751713833565135785980866757267633941821328460903436894336785;

// nb. The usage of SHA-256 hash is necessary to save some gas costs at verification time
// at the cost of more constraints for the prover.
// Basically, some values from the contract are passed as private inputs and the hash as a public input.

// The SHA-256 hash of values provided by the contract.
signal input inputHash;
signal input packedVals;
// Number of users that have completed the sign up.
signal numSignUps;
// Number of options for this poll.
signal maxVoteOptions;
signal input numSignUps;
// Time when the poll ends.
signal input pollEndTimestamp;
// The existing message tree root.
Expand All @@ -79,6 +70,10 @@ include "../../trees/incrementalQuinaryTree.circom";
// @note it is a public input to ensure fair processing from
// the coordinator (no censoring)
signal input actualStateTreeDepth;
// The last batch index
signal input batchEndIndex;
// The batch index of current message batch
signal input index;

// The state leaves upon which messages are applied.
// transform(currentStateLeaf[4], message5) => newStateLeaf4
Expand Down Expand Up @@ -110,14 +105,6 @@ include "../../trees/incrementalQuinaryTree.circom";
// Therefore, the index of the first message to process does not match the index of the
// first message (e.g., [msg1, msg2, msg3] => first message to process has index 3).

// The index of the first message leaf in the batch, inclusive.
signal batchStartIndex;

// The index of the last message leaf in the batch to process, exclusive.
// This value may be less than batchStartIndex + batchSize if this batch is
// the last batch and the total number of messages is not a multiple of the batch size.
signal batchEndIndex;

// The history of state and ballot roots and temporary intermediate
// signals (for processing purposes).
signal stateRoots[batchSize + 1];
Expand All @@ -131,31 +118,6 @@ include "../../trees/incrementalQuinaryTree.circom";
var computedCurrentSbCommitment = PoseidonHasher(3)([currentStateRoot, currentBallotRoot, currentSbSalt]);
computedCurrentSbCommitment === currentSbCommitment;

// Verify public inputs and assign unpacked values.
var (
computedMaxVoteOptions,
computedNumSignUps,
computedBatchStartIndex,
computedBatchEndIndex,
computedHash
) = ProcessMessagesInputHasher()(
packedVals,
coordPubKey,
msgRoot,
currentSbCommitment,
newSbCommitment,
pollEndTimestamp,
actualStateTreeDepth
);

// The unpacked values from packedVals.
computedMaxVoteOptions ==> maxVoteOptions;
computedNumSignUps ==> numSignUps;
computedBatchStartIndex ==> batchStartIndex;
computedBatchEndIndex ==> batchEndIndex;
// Matching constraints.
computedHash === inputHash;

// -----------------------------------------------------------------------
// 0. Ensure that the maximum vote options signal is valid and if
// the maximum users signal is valid.
Expand All @@ -173,7 +135,7 @@ include "../../trees/incrementalQuinaryTree.circom";
computedMessageHashers[i] = MessageHasher()(msgs[i], encPubKeys[i]);
}

// If batchEndIndex - batchStartIndex < batchSize, the remaining
// If endIndex - startIndex < batchSize, the remaining
// message hashes should be the zero value.
// 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.
Expand All @@ -182,7 +144,7 @@ include "../../trees/incrementalQuinaryTree.circom";
var computedPathIndex[msgTreeDepth - msgBatchDepth];

for (var i = 0; i < batchSize; i++) {
var batchStartIndexValid = SafeLessThan(32)([batchStartIndex + i, batchEndIndex]);
var batchStartIndexValid = SafeLessThan(32)([index + i, batchEndIndex]);
computedLeaves[i] = Mux1()([msgTreeZeroValue, computedMessageHashers[i]], batchStartIndexValid);
}

Expand All @@ -195,8 +157,8 @@ include "../../trees/incrementalQuinaryTree.circom";
// Computing the path_index values. Since msgBatchLeavesExists tests
// the existence of a subroot, the length of the proof correspond to the last
// n elements of a proof from the root to a leaf, where n = msgTreeDepth - msgBatchDepth.
// e.g. if batchStartIndex = 25, msgTreeDepth = 4, msgBatchDepth = 2, then path_index = [1, 0].
var computedMsgBatchPathIndices[msgTreeDepth] = QuinGeneratePathIndices(msgTreeDepth)(batchStartIndex);
// e.g. if startIndex = 25, msgTreeDepth = 4, msgBatchDepth = 2, then path_index = [1, 0].
var computedMsgBatchPathIndices[msgTreeDepth] = QuinGeneratePathIndices(msgTreeDepth)(index);

for (var i = msgBatchDepth; i < msgTreeDepth; i++) {
computedPathIndex[i - msgBatchDepth] = computedMsgBatchPathIndices[i];
Expand Down Expand Up @@ -286,7 +248,6 @@ include "../../trees/incrementalQuinaryTree.circom";

(computedNewVoteStateRoot[i], computedNewVoteBallotRoot[i]) = ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth)(
numSignUps,
maxVoteOptions,
pollEndTimestamp,
stateRoots[i + 1],
ballotRoots[i + 1],
Expand Down Expand Up @@ -336,6 +297,7 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) {
var BALLOT_NONCE_IDX = 0;
// Ballot vote option (VO) root index.
var BALLOT_VO_ROOT_IDX = 1;
var maxVoteOptions = MESSAGE_TREE_ARITY ** voteOptionTreeDepth;

// Indices for elements within a state leaf.
// Public key.
Expand All @@ -348,7 +310,6 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) {

// Inputs representing the message and the current state.
signal input numSignUps;
signal input maxVoteOptions;
signal input pollEndTimestamp;

// The current value of the state tree root.
Expand Down
47 changes: 8 additions & 39 deletions circuits/circom/core/non-qv/tallyVotes.circom
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ include "../../trees/incrementalMerkleTree.circom";
include "../../trees/incrementalQuinaryTree.circom";
include "../../utils/calculateTotal.circom";
include "../../utils/hashers.circom";
include "../../utils/tallyVotesInputHasher.circom";

/**
* Processes batches of votes and verifies their validity in a Merkle tree structure.
Expand Down Expand Up @@ -51,70 +50,40 @@ template TallyVotesNonQv(
signal input ballotRoot;
// Salt used in commitment to secure the ballot data.
signal input sbSalt;

// Inputs combined into a hash to verify the integrity and authenticity of the data.
signal input packedVals;
// Commitment to the state and ballots.
signal input sbCommitment;
// Commitment to the current tally before this batch.
signal input currentTallyCommitment;
// Commitment to the new tally after processing this batch.
signal input newTallyCommitment;

// A tally commitment is the hash of the following salted values:
// - the vote results,
// - the number of voice credits spent per vote option,
// - the total number of spent voice credits.

// Hash of all inputs to ensure they are unchanged and authentic.
signal input inputHash;

// Start index of given batch
signal input index;
// Number of users that signup
signal input numSignUps;
// Ballots and their corresponding path elements for verification in the tree.
signal input ballots[batchSize][BALLOT_LENGTH];
signal input ballotPathElements[k][BALLOT_TREE_ARITY - 1];
signal input votes[batchSize][numVoteOptions];

// Current results for each vote option.
signal input currentResults[numVoteOptions];
// Salt for the root of the current results.
signal input currentResultsRootSalt;

// Total voice credits spent so far.
signal input currentSpentVoiceCreditSubtotal;
// Salt for the total spent voice credits.
signal input currentSpentVoiceCreditSubtotalSalt;

// Salt for the root of the new results.
signal input newResultsRootSalt;
// Salt for the new total spent voice credits root.
signal input newSpentVoiceCreditSubtotalSalt;
// The number of total registrations, used to validate the batch index.
signal numSignUps;
// Index of the first ballot in this batch.
signal batchStartIndex;

// Verify sbCommitment.
var computedSbCommitment = PoseidonHasher(3)([stateRoot, ballotRoot, sbSalt]);
computedSbCommitment === sbCommitment;

// Verify inputHash.
var (
computedNumSignUps,
computedBatchNum,
computedHash
) = TallyVotesInputHasher()(
sbCommitment,
currentTallyCommitment,
newTallyCommitment,
packedVals
);

inputHash === computedHash;
numSignUps <== computedNumSignUps;
batchStartIndex <== computedBatchNum * batchSize;

// Validates that the batchStartIndex is within the valid range of sign-ups.
var numSignUpsValid = LessEqThan(50)([batchStartIndex, numSignUps]);
// Validates that the index is within the valid range of sign-ups.
var numSignUpsValid = LessEqThan(50)([index, numSignUps]);
numSignUpsValid === 1;

// Hashes each ballot for subroot generation, and checks the existence of the leaf in the Merkle tree.
Expand All @@ -125,7 +94,7 @@ template TallyVotesNonQv(
}

var computedBallotSubroot = CheckRoot(intStateTreeDepth)(computedBallotHashers);
var computedBallotPathIndices[k] = MerkleGeneratePathIndices(k)(computedBatchNum);
var computedBallotPathIndices[k] = MerkleGeneratePathIndices(k)(index / batchSize);

// Verifies each ballot's existence within the ballot tree.
LeafExists(k)(
Expand All @@ -143,7 +112,7 @@ template TallyVotesNonQv(
}

// Calculates new results and spent voice credits based on the current and incoming votes.
var computedIsFirstBatch = IsZero()(batchStartIndex);
var computedIsFirstBatch = IsZero()(index);
var computedIsZero = IsZero()(computedIsFirstBatch);

// Tally the new results.
Expand Down
Loading

0 comments on commit 624b8de

Please sign in to comment.