diff --git a/circuits/circom/circuits.json b/circuits/circom/circuits.json index 203c696876..22650b1349 100644 --- a/circuits/circom/circuits.json +++ b/circuits/circom/circuits.json @@ -11,7 +11,8 @@ "currentSbCommitment", "newSbCommitment", "pollEndTimestamp", - "actualStateTreeDepth" + "actualStateTreeDepth", + "coordinatorPublicKeyHash" ] }, "ProcessMessagesNonQv_10-2-1-2_test": { @@ -26,7 +27,8 @@ "currentSbCommitment", "newSbCommitment", "pollEndTimestamp", - "actualStateTreeDepth" + "actualStateTreeDepth", + "coordinatorPublicKeyHash" ] }, "TallyVotes_10-1-2_test": { diff --git a/circuits/circom/core/non-qv/processMessages.circom b/circuits/circom/core/non-qv/processMessages.circom index e0057d4c26..05dda47029 100644 --- a/circuits/circom/core/non-qv/processMessages.circom +++ b/circuits/circom/core/non-qv/processMessages.circom @@ -60,8 +60,6 @@ include "../../trees/incrementalQuinaryTree.circom"; 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). - signal input coordPubKey[2]; // The ECDH public key per message. signal input encPubKeys[batchSize][2]; // The current state root (before the processing). @@ -74,6 +72,8 @@ include "../../trees/incrementalQuinaryTree.circom"; signal input batchEndIndex; // The batch index of current message batch signal input index; + // The coordinator public key hash + signal input coordinatorPublicKeyHash; // The state leaves upon which messages are applied. // transform(currentStateLeaf[4], message5) => newStateLeaf4 @@ -185,7 +185,8 @@ include "../../trees/incrementalQuinaryTree.circom"; // based on the given private key - that is, the prover knows the // coordinator's private key. var derivedPubKey[2] = PrivToPubKey()(coordPrivKey); - derivedPubKey === coordPubKey; + var derivedPubKeyHash = PoseidonHasher(2)(derivedPubKey); + derivedPubKeyHash === coordinatorPublicKeyHash; // Decrypt each Message into a Command. // The command i-th is composed by the following fields. diff --git a/circuits/circom/core/qv/processMessages.circom b/circuits/circom/core/qv/processMessages.circom index e012f63afd..f1ad356f36 100644 --- a/circuits/circom/core/qv/processMessages.circom +++ b/circuits/circom/core/qv/processMessages.circom @@ -60,8 +60,6 @@ template ProcessMessages( 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). - signal input coordPubKey[2]; // The ECDH public key per message. signal input encPubKeys[batchSize][2]; // The current state root (before the processing). @@ -74,6 +72,8 @@ template ProcessMessages( signal input batchEndIndex; // The batch index of current message batch signal input index; + // The coordinator public key hash + signal input coordinatorPublicKeyHash; // The state leaves upon which messages are applied. // transform(currentStateLeaf[4], message5) => newStateLeaf4 @@ -180,7 +180,8 @@ template ProcessMessages( // based on the given private key - that is, the prover knows the // coordinator's private key. var derivedPubKey[2] = PrivToPubKey()(coordPrivKey); - derivedPubKey === coordPubKey; + var derivedPubKeyHash = PoseidonHasher(2)(derivedPubKey); + derivedPubKeyHash === coordinatorPublicKeyHash; // Decrypt each Message into a Command. // The command i-th is composed by the following fields. diff --git a/circuits/circom/test/ProcessMessages_10-2-1-2_test.circom b/circuits/circom/test/ProcessMessages_10-2-1-2_test.circom index 6dc034fa6f..9272a45ae0 100644 --- a/circuits/circom/test/ProcessMessages_10-2-1-2_test.circom +++ b/circuits/circom/test/ProcessMessages_10-2-1-2_test.circom @@ -3,4 +3,4 @@ pragma circom 2.0.0; include ".././core/qv/processMessages.circom"; -component main {public[numSignUps, index, batchEndIndex, msgRoot, currentSbCommitment, newSbCommitment, pollEndTimestamp, actualStateTreeDepth]} = ProcessMessages(10, 2, 1, 2); +component main {public[numSignUps, index, batchEndIndex, msgRoot, currentSbCommitment, newSbCommitment, pollEndTimestamp, actualStateTreeDepth, coordinatorPublicKeyHash]} = ProcessMessages(10, 2, 1, 2); diff --git a/circuits/ts/__tests__/CeremonyParams.test.ts b/circuits/ts/__tests__/CeremonyParams.test.ts index db9a0d69f0..8d2f21a3a6 100644 --- a/circuits/ts/__tests__/CeremonyParams.test.ts +++ b/circuits/ts/__tests__/CeremonyParams.test.ts @@ -50,7 +50,7 @@ describe("Ceremony param tests", () => { "msgs", "msgSubrootPathElements", "coordPrivKey", - "coordPubKey", + "coordinatorPublicKeyHash", "encPubKeys", "currentStateRoot", "currentStateLeaves", diff --git a/circuits/ts/__tests__/ProcessMessages.test.ts b/circuits/ts/__tests__/ProcessMessages.test.ts index 9a587f19d5..05d8e77912 100644 --- a/circuits/ts/__tests__/ProcessMessages.test.ts +++ b/circuits/ts/__tests__/ProcessMessages.test.ts @@ -24,7 +24,7 @@ describe("ProcessMessage circuit", function test() { "msgs", "msgSubrootPathElements", "coordPrivKey", - "coordPubKey", + "coordinatorPublicKeyHash", "encPubKeys", "currentStateRoot", "currentStateLeaves", diff --git a/circuits/ts/types.ts b/circuits/ts/types.ts index bd05e8a98a..78dd1a54ce 100644 --- a/circuits/ts/types.ts +++ b/circuits/ts/types.ts @@ -54,7 +54,7 @@ export interface IProcessMessagesInputs { msgs: bigint[]; msgSubrootPathElements: bigint[][]; coordPrivKey: bigint; - coordPubKey: [bigint, bigint]; + coordinatorPublicKeyHash: bigint; encPubKeys: bigint[]; currentStateRoot: bigint; currentStateLeaves: bigint[]; diff --git a/cli/ts/commands/proveOnChain.ts b/cli/ts/commands/proveOnChain.ts index ed3dbd7c17..27636c3e77 100644 --- a/cli/ts/commands/proveOnChain.ts +++ b/cli/ts/commands/proveOnChain.ts @@ -11,7 +11,7 @@ import { Verifier__factory as VerifierFactory, } from "maci-contracts/typechain-types"; import { MESSAGE_TREE_ARITY, STATE_TREE_ARITY } from "maci-core"; -import { G1Point, G2Point, hashLeftRight } from "maci-crypto"; +import { G1Point, G2Point } from "maci-crypto"; import { VerifyingKey } from "maci-domainobjs"; import fs from "fs"; @@ -214,13 +214,9 @@ export const proveOnChain = async ({ logError("currentSbCommitment mismatch."); } - const coordPubKeyHashOnChain = BigInt(await pollContract.coordinatorPubKeyHash()); - if ( - hashLeftRight( - BigInt((circuitInputs.coordPubKey as BigNumberish[])[0]), - BigInt((circuitInputs.coordPubKey as BigNumberish[])[1]), - ).toString() !== coordPubKeyHashOnChain.toString() - ) { + const coordPubKeyHashOnChain = await pollContract.coordinatorPubKeyHash(); + + if (circuitInputs.coordinatorPublicKeyHash.toString() !== coordPubKeyHashOnChain.toString()) { logError("coordPubKey mismatch."); } diff --git a/contracts/contracts/MessageProcessor.sol b/contracts/contracts/MessageProcessor.sol index 94946318ec..8c037a6459 100644 --- a/contracts/contracts/MessageProcessor.sol +++ b/contracts/contracts/MessageProcessor.sol @@ -146,6 +146,7 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes ) public view override returns (uint256[] memory publicInputs) { (, uint8 messageTreeSubDepth, uint8 messageTreeDepth, uint8 voteOptionTreeDepth) = poll.treeDepths(); (, AccQueue messageAq) = poll.extContracts(); + uint256 coordinatorPubKeyHash = poll.coordinatorPubKeyHash(); uint256 messageBatchSize = TREE_ARITY ** messageTreeSubDepth; (uint256 numSignUps, uint256 numMessages) = poll.numSignUpsAndMessages(); (uint256 deployTime, uint256 duration) = poll.getDeployTimeAndDuration(); @@ -155,15 +156,16 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes batchEndIndex = numMessages; } - publicInputs = new uint256[](8); + publicInputs = new uint256[](9); publicInputs[0] = numSignUps; publicInputs[1] = deployTime + duration; publicInputs[2] = messageAq.getMainRoot(messageTreeDepth); publicInputs[3] = poll.actualStateTreeDepth(); publicInputs[4] = batchEndIndex; publicInputs[5] = _currentMessageBatchIndex; - publicInputs[6] = (sbCommitment == 0 ? poll.currentSbCommitment() : sbCommitment); - publicInputs[7] = _newSbCommitment; + publicInputs[6] = coordinatorPubKeyHash; + publicInputs[7] = (sbCommitment == 0 ? poll.currentSbCommitment() : sbCommitment); + publicInputs[8] = _newSbCommitment; } /// @notice Verify the proof for processMessage diff --git a/contracts/tasks/helpers/Prover.ts b/contracts/tasks/helpers/Prover.ts index 087d111e4e..6f90a90ecf 100644 --- a/contracts/tasks/helpers/Prover.ts +++ b/contracts/tasks/helpers/Prover.ts @@ -1,5 +1,5 @@ /* eslint-disable no-console, no-await-in-loop */ -import { G1Point, G2Point, hashLeftRight } from "maci-crypto"; +import { G1Point, G2Point } from "maci-crypto"; import { VerifyingKey } from "maci-domainobjs"; import type { IVerifyingKeyStruct, Proof } from "../../ts/types"; @@ -156,13 +156,7 @@ export class Prover { this.validateCommitment(circuitInputs.currentSbCommitment as BigNumberish, currentSbCommitmentOnChain); - const coordPubKeyHashOnChain = BigInt(coordinatorPubKeyHash); - const coordPubKeyHashOffChain = hashLeftRight( - BigInt((circuitInputs.coordPubKey as BigNumberish[])[0]), - BigInt((circuitInputs.coordPubKey as BigNumberish[])[1]), - ).toString(); - - if (coordPubKeyHashOffChain !== coordPubKeyHashOnChain.toString()) { + if (circuitInputs.coordinatorPublicKeyHash.toString() !== coordinatorPubKeyHash.toString()) { throw new Error("coordPubKey mismatch."); } diff --git a/contracts/ts/genMaciState.ts b/contracts/ts/genMaciState.ts index b23bdee505..395dcd19e7 100644 --- a/contracts/ts/genMaciState.ts +++ b/contracts/ts/genMaciState.ts @@ -113,14 +113,13 @@ export const genMaciStateFromContract = async ( const pollContractAddress = pollContractAddresses.get(pollId)!; const pollContract = PollFactory.connect(pollContractAddress, provider); - const [coordinatorPubKeyOnChain, [deployTime, duration], onChainTreeDepths] = await Promise.all([ - pollContract.coordinatorPubKey(), + const [coordinatorPubKeyHashOnChain, [deployTime, duration], onChainTreeDepths] = await Promise.all([ + pollContract.coordinatorPubKeyHash(), pollContract.getDeployTimeAndDuration().then((values) => values.map(Number)), pollContract.treeDepths(), ]); - assert(coordinatorPubKeyOnChain[0].toString() === coordinatorKeypair.pubKey.rawPubKey[0].toString()); - assert(coordinatorPubKeyOnChain[1].toString() === coordinatorKeypair.pubKey.rawPubKey[1].toString()); + assert(coordinatorKeypair.pubKey.hash().toString() === coordinatorPubKeyHashOnChain.toString()); const treeDepths = { intStateTreeDepth: Number(onChainTreeDepths.intStateTreeDepth), diff --git a/core/ts/Poll.ts b/core/ts/Poll.ts index 336042e0f2..459c4fec82 100644 --- a/core/ts/Poll.ts +++ b/core/ts/Poll.ts @@ -655,6 +655,7 @@ export class Poll implements IPoll { // ensure we pass the dynamic tree depth circuitInputs.actualStateTreeDepth = this.actualStateTreeDepth.toString(); + circuitInputs.coordinatorPublicKeyHash = this.coordinatorKeypair.pubKey.hash(); return stringifyBigInts(circuitInputs) as unknown as IProcessMessagesCircuitInputs; }; @@ -747,7 +748,6 @@ export class Poll implements IPoll { msgs, msgSubrootPathElements: messageSubrootPath.pathElements, coordPrivKey: this.coordinatorKeypair.privKey.asCircuitInputs(), - coordPubKey: this.coordinatorKeypair.pubKey.asCircuitInputs(), encPubKeys: encPubKeys.map((x) => x.asCircuitInputs()), currentStateRoot, currentBallotRoot, diff --git a/core/ts/utils/types.ts b/core/ts/utils/types.ts index 0610616965..f4ffb0a9fc 100644 --- a/core/ts/utils/types.ts +++ b/core/ts/utils/types.ts @@ -138,10 +138,10 @@ export interface IProcessMessagesCircuitInputs { index: string; maxVoteOptions: string; msgRoot: string; + coordinatorPublicKeyHash: string; msgs: string[]; msgSubrootPathElements: string[][]; coordPrivKey: string; - coordPubKey: string; encPubKeys: string[]; currentStateRoot: string; currentBallotRoot: string; diff --git a/website/versioned_docs/version-v2.0_alpha/core-concepts/spec.md b/website/versioned_docs/version-v2.0_alpha/core-concepts/spec.md index d13da6e343..a126a1bc71 100644 --- a/website/versioned_docs/version-v2.0_alpha/core-concepts/spec.md +++ b/website/versioned_docs/version-v2.0_alpha/core-concepts/spec.md @@ -663,16 +663,16 @@ The state tree, message tree, and vote option trees all have an arity of 5. As s | Input signal | Description | | -------------------------------- | --------------------------------------------------------------------------------------- | -| `inputHash` | The SHA256 hash of inputs supplied by the contract | -| `packedVals` | As described below | +| `numSignUps` | Number of users that have completed the sign up | +| `index` | The batch index of current message batch | | `pollEndTimestamp` | The Unix timestamp at which the poll ends | | `msgRoot` | The root of the message tree | | `msgs` | The batch of messages as an array of arrays | | `msgSubrootPathElements` | As described below | -| `coordinatorPubKeyHash` | $\mathsf{poseidon_2}([cPk_x, cPk_y])$ | +| `coordinatorPublicKeyHash` | $\mathsf{poseidon_2}([cPk_x, cPk_y])$ | | `newSbCommitment` | As described below | | `coordPrivKey` | The coordinator's private key | -| `coordPubKey` | The coordinator's public key | +| `batchEndIndex` | The last batch index | | `encPubKeys` | The public keys used to generate shared ECDH encryption keys to encrypt the messages | | `currentStateRoot` | The state root before the commands are applied | | `currentStateLeaves` | The state leaves upon which messages are applied | @@ -687,35 +687,6 @@ The state tree, message tree, and vote option trees all have an arity of 5. As s | `currentVoteWeights` | The existing vote weight for the vote option in the ballot which each command refers to | | `currentVoteWeightsPathElements` | The Merkle path from each vote weight to the vote option root in its ballot | -##### `inputHash` - -All inputs to this circuit are private except for `inputHash`. To save gas during verification, the `PollProcessorAndTallyer` contract hashes the following values using SHA256 and uses the hash as the sole element of $ic$: - -1. `packedVals` -2. `coordinatorPubKeyHash` -3. `msgRoot` -4. `currentSbCommitment` -5. `newSbCommitment` -6. `pollEndTimestamp` - -The hash is computed using the `sha256` Solidity function and is then subject to modulo $p$. - -##### `packedVals` - -`packedVals` is the following values represented as one field element. Consider that a field element is roughly 253 bits. The big-endian bit-representation is as such: - -| Bits | Value | -| ----------- | -------------------------- | -| 1st 53 bits | `0` | -| 2nd 50 bits | `batchEndIndex` | -| 3rd 50 bits | `currentMessageBatchIndex` | -| 4th 50 bits | `numSignUps` | -| 5th 50 bits | `maxVoteOptions` | - -For instance, if `maxVoteOptions` is 25 and `batchEndIndex` is `5`, and all other values are 0, the following is the `packedVals` representation in hexadecimal: - -`140000000000000000000000000000000000019` - ##### `currentSbCommitment` and `newSbCommitment` The `currentSbCommitment` is the $\mathsf{poseidon_3}$ hash of the state tree root, the ballot tree root, and a random salt. The purpose of the random salt, which should be unique to each batch, is to ensure that the value of `currentSbCommitment` always changes even if all the commands in a batch are invalid and therefore do not change the state tree or ballot tree root. @@ -754,14 +725,12 @@ This method requires fewer circuit constraints than if we verified a Merkle proo #### Statements that the circuit proves -1. That the prover knows the preimage to `inputHash` (see above) -2. That the prover knows the preimage to `currentSbCommitment` (that is, the state root, ballot root, and `currentSbSalt`) -3. That `maxVoteOptions <= (5 ^ voteOptionTreeDepth)` -4. That `numSignUps <== (5 ^ stateTreeDepth)` -5. That `coordPubKey` is correctly derived from `coordPrivKey` -6. That `coordPubKey` is the preimage to the Poseidon hash of `coordPubKey` (provided by the contract) -7. That each message in `msgs` exists in the message tree -8. That after decrypting and applying each message, in reverse order, to the corresponding state and ballot leaves, the new state root, new ballot root, and `newSbSalt` are the preimage to `newSbCommitment` +1. That the prover knows the preimage to `currentSbCommitment` (that is, the state root, ballot root, and `currentSbSalt`) +2. That `maxVoteOptions <= (5 ^ voteOptionTreeDepth)` +3. That `numSignUps <== (5 ^ stateTreeDepth)` +4. That `coordinatorPublicKeyHash` is a hash of public key that is correctly derived from `coordPrivKey` +5. That each message in `msgs` exists in the message tree +6. That after decrypting and applying each message, in reverse order, to the corresponding state and ballot leaves, the new state root, new ballot root, and `newSbSalt` are the preimage to `newSbCommitment` #### How messages are decrypted and applied @@ -858,8 +827,8 @@ The coordinator uses the ballot tallying circuit (`tallyVotes.circom`) to genera | Input signal | Description | | --------------------------------------- | ---------------------------------------------------------------- | -| `inputHash` | The SHA256 hash of inputs supplied by the contract | -| `packedVals` | As described below | +| `numSignUps` | The number of users that signup | +| `index` | Start index of given batch | | `sbCommitment` | As described below | | `currentTallyCommitment` | As described below | | `newTallyCommitment` | As described below | @@ -879,35 +848,6 @@ The coordinator uses the ballot tallying circuit (`tallyVotes.circom`) to genera | `newPerVOSpentVoiceCreditsRootSalt` | A random value | | `newSpentVoiceCreditSubtotalSalt` | A random value | -##### `inputHash` - -All inputs to this circuit are private except for `inputHash`. To save gas during verification, the `PollProcessorAndTallyer` contract hashes the following values using SHA256 and uses the hash as the sole element of $ic$: - -1. `packedVals` -2. `sbCommitment` -3. `currentTallyCommitment` -4. `newTallyCommitment` - -The hash is computed using the `sha256` Solidity function and is then subject to modulo $p$. - -##### `packedVals` - -`packedVals` is the following values represented as one field element. Consider that a field element is roughly 253 bits. The big-endian bit-representation is as such: - -| Bits | Value | -| ----------- | ----------------- | -| 1st 53 bits | `0` | -| 2nd 50 bits | `0` | -| 3rd 50 bits | `0` | -| 4th 50 bits | `numSignUps` | -| 5th 50 bits | `batchStartIndex` | - -`numSignUps`, a value provided by the contract, is the number of users who have signed up. This is one less than the number of leaves inserted in the state tree (since the 0th state leaf is a blank state leaf [2.8.1]). `batchStartIndex` is the ballot tree index at which the batch begins. - -For instance, if `numSignUps` is 25 and the batch index is `5`, and all other values are 0, the following is the `packedVals` representation in hexadecimal: - -`64000000000005` - ##### `sbCommitment` The commitment to `stateRoot`, `ballotRoot`, and `sbSalt`: @@ -934,11 +874,10 @@ $\mathsf{poseidon_3}([tc_r, tc_t, tc_p])$ #### Statements that the circuit proves 1. That the coordinator knows the preimage of `sbCommitment` (see above) -2. That the coordinator knows the preimage of `inputHash` (see above) -3. That `batchStartIndex` is less than or equal to `numSignUps` -4. That each ballot in `ballots` is in a member of the ballot tree with the Merkle root `ballotRoot` at indices `batchStartIndex` to `batchStartIndex + (5 ** intStateTreeDepth)` -5. That each set of votes (`votes[i]`) has the Merkle root $blt_r$ whose value equals `ballots[i][1]` -6. That the tally is valid, which is: +2. That `index` is less than or equal to `numSignUps` +3. That each ballot in `ballots` is in a member of the ballot tree with the Merkle root `ballotRoot` at indices `batchStartIndex` to `batchStartIndex + (5 ** intStateTreeDepth)` +4. That each set of votes (`votes[i]`) has the Merkle root $blt_r$ whose value equals `ballots[i][1]` +5. That the tally is valid, which is: - That the sum of votes per vote option is correct - That the sum of voice credits per vote option is correct - That the subtotal of the spent voice credits is correct diff --git a/website/versioned_docs/version-v2.0_alpha/developers-references/zk-snark-circuits/processMessages.md b/website/versioned_docs/version-v2.0_alpha/developers-references/zk-snark-circuits/processMessages.md index a36743da8b..0597b56bfd 100644 --- a/website/versioned_docs/version-v2.0_alpha/developers-references/zk-snark-circuits/processMessages.md +++ b/website/versioned_docs/version-v2.0_alpha/developers-references/zk-snark-circuits/processMessages.md @@ -37,59 +37,30 @@ A version working with non quadratic voting (non-qv) is also [available](https:/ | Input signal | Description | | -------------------------------- | --------------------------------------------------------------------------------------- | -| `inputHash` | The SHA256 hash of inputs supplied by the contract | -| `packedVals` | Described below | +| `numSignUps` | Number of users that have completed the sign up | +| `index` | The batch index of current message batch | | `pollEndTimestamp` | The Unix timestamp at which the poll ends | | `msgRoot` | The root of the message tree | | `msgs` | The batch of messages as an array of arrays | -| `msgSubrootPathElements` | Described below | -| `coordinatorPubKeyHash` | $poseidon_2([cPk_x, cPk_y])$ | -| `newSbCommitment` | Described below | +| `msgSubrootPathElements` | As described below | +| `coordinatorPublicKeyHash` | $\mathsf{poseidon_2}([cPk_x, cPk_y])$ | +| `newSbCommitment` | As described below | | `coordPrivKey` | The coordinator's private key | -| `coordPubKey` | The coordinator's public key | +| `batchEndIndex` | The last batch index | | `encPubKeys` | The public keys used to generate shared ECDH encryption keys to encrypt the messages | | `currentStateRoot` | The state root before the commands are applied | | `currentStateLeaves` | The state leaves upon which messages are applied | | `currentStateLeavesPathElements` | The Merkle path to each incremental state root | -| `currentSbCommitment` | Described below | -| `currentSbSalt` | Described below | -| `newSbCommitment` | Described below | -| `newSbSalt` | Described below | +| `currentSbCommitment` | As described below | +| `currentSbSalt` | As described below | +| `newSbCommitment` | As described below | +| `newSbSalt` | As described below | | `currentBallotRoot` | The root of the ballot tree before messages are applied | | `currentBallots` | The ballots upon which ballots are applied | | `currentBallotsPathElements` | The Merkle path to each incremental ballot root | | `currentVoteWeights` | The existing vote weight for the vote option in the ballot which each command refers to | | `currentVoteWeightsPathElements` | The Merkle path from each vote weight to the vote option root in its ballot | -##### `inputHash` - -All inputs to this circuit are private except for `inputHash`. To save gas during verification, the `MessageProcessor` contract hashes the following values using SHA256 and uses the hash as the sole element of $ic$: - -1. `packedVals` -2. `coordinatorPubKeyHash` -3. `msgRoot` -4. `currentSbCommitment` -5. `newSbCommitment` -6. `pollEndTimestamp` - -The hash is computed using the `sha256` Solidity function and is then subject to modulo $p$. - -##### `packedVals` - -`packedVals` is the following values represented as one field element. Consider that a field element is roughly 253 bits. The big-endian bit-representation is as such: - -| Bits | Value | -| ----------- | -------------------------- | -| 1st 53 bits | `0` | -| 2nd 50 bits | `batchEndIndex` | -| 3rd 50 bits | `currentMessageBatchIndex` | -| 4th 50 bits | `numSignUps` | -| 5th 50 bits | `maxVoteOptions` | - -For instance, if `maxVoteOptions` is 25 and `batchEndIndex` is `5`, and all other values are 0, the following is the `packedVals` representation in hexadecimal: - -`140000000000000000000000000000000000019` - ##### `currentSbCommitment` and `newSbCommitment` The `currentSbCommitment` is the $poseidon_3$ hash of the state tree root, the ballot tree root, and a random salt. The purpose of the random salt, which should be unique to each batch, is to ensure that the value of `currentSbCommitment` always changes even if all the commands in a batch are invalid and therefore do not change the state tree or ballot tree root. @@ -128,11 +99,9 @@ This method requires fewer circuit constraints than if we verified a Merkle proo #### Statements that the circuit proves -1. That the prover knows the preimage to `inputHash` (see above) -2. That the prover knows the preimage to `currentSbCommitment` (that is, the state root, ballot root, and `currentSbSalt`) -3. That `maxVoteOptions <= (5 ^ voteOptionTreeDepth)` -4. That `numSignUps <= (2 ^ stateTreeDepth)` -5. That `coordPubKey` is correctly derived from `coordPrivKey` -6. That `coordPubKey` is the preimage to the Poseidon hash of `coordPubKey` (provided by the contract) -7. That each message in `msgs` exists in the message tree -8. That after decrypting and applying each message, in reverse order, to the corresponding state and ballot leaves, the new state root, new ballot root, and `newSbSalt` are the preimage to `newSbCommitment` +1. That the prover knows the preimage to `currentSbCommitment` (that is, the state root, ballot root, and `currentSbSalt`) +2. That `maxVoteOptions <= (5 ^ voteOptionTreeDepth)` +3. That `numSignUps <= (2 ^ stateTreeDepth)` +4. That `coordinatorPublicKeyHash` is a hash of public key that is correctly derived from `coordPrivKey` +5. That each message in `msgs` exists in the message tree +6. That after decrypting and applying each message, in reverse order, to the corresponding state and ballot leaves, the new state root, new ballot root, and `newSbSalt` are the preimage to `newSbCommitment` diff --git a/website/versioned_docs/version-v2.0_alpha/developers-references/zk-snark-circuits/tallyVotes.md b/website/versioned_docs/version-v2.0_alpha/developers-references/zk-snark-circuits/tallyVotes.md index 01febd3ac9..854af64e9e 100644 --- a/website/versioned_docs/version-v2.0_alpha/developers-references/zk-snark-circuits/tallyVotes.md +++ b/website/versioned_docs/version-v2.0_alpha/developers-references/zk-snark-circuits/tallyVotes.md @@ -25,8 +25,8 @@ A version working with non quadratic voting (non-qv) is also [available](https:/ | Input signal | Description | | --------------------------------------- | ---------------------------------------------------------------- | -| `inputHash` | The SHA256 hash of inputs supplied by the contract | -| `packedVals` | Described below | +| `numSignUps` | The number of users that signup | +| `index` | Start index of given batch | | `sbCommitment` | Described below | | `currentTallyCommitment` | Described below | | `newTallyCommitment` | Described below | @@ -46,35 +46,6 @@ A version working with non quadratic voting (non-qv) is also [available](https:/ | `newPerVOSpentVoiceCreditsRootSalt` | A random value | | `newSpentVoiceCreditSubtotalSalt` | A random value | -##### `inputHash` - -All inputs to this circuit are private except for `inputHash`. To save gas during verification, the `Tally` contract hashes the following values using SHA256 and uses the hash as the sole element of $ic$: - -1. `packedVals` -2. `sbCommitment` -3. `currentTallyCommitment` -4. `newTallyCommitment` - -The hash is computed using the `sha256` Solidity function and is then subject to modulo $p$. - -##### `packedVals` - -`packedVals` is the following values represented as one field element. Consider that a field element is roughly 253 bits. The big-endian bit-representation is as such: - -| Bits | Value | -| ----------- | ----------------- | -| 1st 53 bits | `0` | -| 2nd 50 bits | `0` | -| 3rd 50 bits | `0` | -| 4th 50 bits | `numSignUps` | -| 5th 50 bits | `batchStartIndex` | - -`numSignUps`, a value provided by the contract, is the number of users who have signed up. This is one less than the number of leaves inserted in the state tree (since the 0th state leaf is a [blank state leaf](/docs/core-concepts/state-leaf)). `batchStartIndex` is the ballot tree index at which the batch begins. - -For instance, if `numSignUps` is 25 and the batch index is `5`, and all other values are 0, the following is the `packedVals` representation in hexadecimal: - -`64000000000005` - ##### `sbCommitment` The commitment to `stateRoot`, `ballotRoot`, and `sbSalt`: @@ -101,9 +72,8 @@ $poseidon_3([tc_r, tc_t, tc_p])$ #### Statements that the circuit proves 1. That the coordinator knows the preimage of `sbCommitment` -2. That the coordinator knows the preimage of `inputHash` -3. That `batchStartIndex` is less than or equal to `numSignUps` -4. That each ballot in `ballots` is in a member of the ballot tree with the Merkle root `ballotRoot` at indices `batchStartIndex` to `batchStartIndex + (5 ** intStateTreeDepth)` -5. That each set of votes (`votes[i]`) has the Merkle root $blt_r$ whose value equals `ballots[i][1]` -6. That the tally is valid, which is: +2. That `index` is less than or equal to `numSignUps` +3. That each ballot in `ballots` is in a member of the ballot tree with the Merkle root `ballotRoot` at indices `batchStartIndex` to `batchStartIndex + (5 ** intStateTreeDepth)` +4. That each set of votes (`votes[i]`) has the Merkle root $blt_r$ whose value equals `ballots[i][1]` +5. That the tally is valid, which is: - That the sum of votes per vote option is correct diff --git a/website/versioned_docs/version-v2.0_alpha/quick-start/installation.md b/website/versioned_docs/version-v2.0_alpha/quick-start/installation.md index fb47444d50..3232f3d415 100644 --- a/website/versioned_docs/version-v2.0_alpha/quick-start/installation.md +++ b/website/versioned_docs/version-v2.0_alpha/quick-start/installation.md @@ -101,25 +101,45 @@ Edit `circuits/circom/circuits` to include the circuits you would like to compil "file": "processMessages", "template": "ProcessMessages", "params": [10, 2, 1, 2], - "pubs": ["inputHash"] + "pubs": [ + "numSignUps", + "index", + "batchEndIndex", + "msgRoot", + "currentSbCommitment", + "newSbCommitment", + "pollEndTimestamp", + "actualStateTreeDepth", + "coordinatorPublicKeyHash" + ] }, "ProcessMessagesNonQv_10-2-1-2_test": { "file": "processMessagesNonQv", "template": "ProcessMessagesNonQv", "params": [10, 2, 1, 2], - "pubs": ["inputHash"] + "pubs": [ + "numSignUps", + "index", + "batchEndIndex", + "msgRoot", + "currentSbCommitment", + "newSbCommitment", + "pollEndTimestamp", + "actualStateTreeDepth", + "coordinatorPublicKeyHash" + ] }, "TallyVotes_10-1-2_test": { "file": "tallyVotes", "template": "TallyVotes", "params": [10, 1, 2], - "pubs": ["inputHash"] + "pubs": ["index", "numSignUps", "sbCommitment", "currentTallyCommitment", "newTallyCommitment"] }, "TallyVotesNonQv_10-1-2_test": { "file": "tallyVotesNonQv", "template": "TallyVotesNonQv", "params": [10, 1, 2], - "pubs": ["inputHash"] + "pubs": ["index", "numSignUps", "sbCommitment", "currentTallyCommitment", "newTallyCommitment"] } } ```