From b0b7b38bf008e8a542b550c75ada40ac21854944 Mon Sep 17 00:00:00 2001 From: 0xmad <0xmad@users.noreply.github.com> Date: Fri, 5 Apr 2024 08:42:10 -0500 Subject: [PATCH] feat(contracts): tally qv and non-qv optimisations - [x] Remove TallyNonQv and TallyNonQvFactory - [x] Add non-qv logic to Tally - [x] Compatibility fixes --- .../ceremony-params/ceremonyParams.test.ts | 8 +- cli/tests/constants.ts | 1 + cli/tests/e2e/e2e.nonQv.test.ts | 4 +- cli/tests/e2e/e2e.test.ts | 3 +- cli/ts/commands/deploy.ts | 2 - cli/ts/commands/deployPoll.ts | 2 + cli/ts/commands/verify.ts | 14 +- cli/ts/index.ts | 4 +- cli/ts/utils/interfaces.ts | 10 +- cli/ts/utils/verifiers.ts | 38 +-- contracts/contracts/MACI.sol | 6 +- contracts/contracts/Tally.sol | 74 ++++- contracts/contracts/TallyFactory.sol | 5 +- contracts/contracts/TallyNonQv.sol | 273 ------------------ contracts/contracts/TallyNonQvFactory.sol | 23 -- .../contracts/interfaces/ITallyFactory.sol | 4 +- .../tasks/deploy/maci/08-tallyFactory.ts | 4 +- contracts/tasks/deploy/poll/01-poll.ts | 14 +- contracts/tasks/helpers/Prover.ts | 13 +- contracts/tasks/helpers/types.ts | 15 +- contracts/tasks/runner/prove.ts | 24 +- contracts/tests/EASGatekeeper.test.ts | 9 +- contracts/tests/HatsGatekeeper.test.ts | 2 - contracts/tests/MACI.test.ts | 2 + contracts/tests/MessageProcessor.test.ts | 1 + contracts/tests/Poll.test.ts | 1 + contracts/tests/SignUpGatekeeper.test.ts | 1 - contracts/tests/Tally.test.ts | 9 +- contracts/tests/TallyNonQv.test.ts | 12 +- contracts/tests/utils.ts | 2 - contracts/ts/deploy.ts | 14 +- contracts/ts/types.ts | 5 - .../ts/__tests__/integration.test.ts | 2 + .../ts/__tests__/maci-keys.test.ts | 1 + website/versioned_docs/version-v1.x/cli.md | 4 +- .../versioned_docs/version-v1.x/poll-types.md | 2 +- 36 files changed, 167 insertions(+), 441 deletions(-) delete mode 100644 contracts/contracts/TallyNonQv.sol delete mode 100644 contracts/contracts/TallyNonQvFactory.sol diff --git a/cli/tests/ceremony-params/ceremonyParams.test.ts b/cli/tests/ceremony-params/ceremonyParams.test.ts index c07e1433b..4bfe9cf91 100644 --- a/cli/tests/ceremony-params/ceremonyParams.test.ts +++ b/cli/tests/ceremony-params/ceremonyParams.test.ts @@ -263,9 +263,9 @@ describe("Stress tests with ceremony params (6,9,2,3)", function test() { before(async () => { // deploy the smart contracts - maciAddresses = await deploy({ ...ceremonyDeployArgs, signer, useQv: false }); + maciAddresses = await deploy({ ...ceremonyDeployArgs, signer }); // deploy a poll contract - await deployPoll({ ...deployPollArgs, signer }); + await deployPoll({ ...deployPollArgs, signer, useQuadraticVoting: false }); }); it("should signup one user", async () => { @@ -310,9 +310,9 @@ describe("Stress tests with ceremony params (6,9,2,3)", function test() { before(async () => { // deploy the smart contracts - maciAddresses = await deploy({ ...ceremonyDeployArgs, signer, useQv: false }); + maciAddresses = await deploy({ ...ceremonyDeployArgs, signer }); // deploy a poll contract - await deployPoll({ ...deployPollArgs, signer }); + await deployPoll({ ...deployPollArgs, signer, useQuadraticVoting: false }); }); it("should signup 25 users", async () => { diff --git a/cli/tests/constants.ts b/cli/tests/constants.ts index ca562d759..53a4eb7da 100644 --- a/cli/tests/constants.ts +++ b/cli/tests/constants.ts @@ -158,4 +158,5 @@ export const deployPollArgs: Omit = { messageTreeDepth: MSG_TREE_DEPTH, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, coordinatorPubkey: coordinatorPubKey, + useQuadraticVoting: true, }; diff --git a/cli/tests/e2e/e2e.nonQv.test.ts b/cli/tests/e2e/e2e.nonQv.test.ts index 154b56a7b..5ad6f2316 100644 --- a/cli/tests/e2e/e2e.nonQv.test.ts +++ b/cli/tests/e2e/e2e.nonQv.test.ts @@ -91,9 +91,9 @@ describe("e2e tests with non quadratic voting", function test() { before(async () => { // deploy the smart contracts - maciAddresses = await deploy({ ...deployArgs, signer, useQv: false }); + maciAddresses = await deploy({ ...deployArgs, signer }); // deploy a poll contract - await deployPoll({ ...deployPollArgs, signer }); + await deployPoll({ ...deployPollArgs, signer, useQuadraticVoting: false }); }); it("should signup one user", async () => { diff --git a/cli/tests/e2e/e2e.test.ts b/cli/tests/e2e/e2e.test.ts index 39805691c..b6253930b 100644 --- a/cli/tests/e2e/e2e.test.ts +++ b/cli/tests/e2e/e2e.test.ts @@ -88,6 +88,7 @@ describe("e2e tests", function test() { processWasm: testProcessMessagesWasmPath, tallyWasm: testTallyVotesWasmPath, useWasm, + useQuadraticVoting: true, }; // before all tests we deploy the vk registry contract and set the verifying keys @@ -111,7 +112,7 @@ describe("e2e tests", function test() { // deploy the smart contracts maciAddresses = await deploy({ ...deployArgs, signer }); // deploy a poll contract - pollAddresses = await deployPoll({ ...deployPollArgs, signer }); + pollAddresses = await deployPoll({ ...deployPollArgs, signer, useQuadraticVoting: true }); }); it("should signup one user", async () => { diff --git a/cli/ts/commands/deploy.ts b/cli/ts/commands/deploy.ts index 5249fbada..b4f7bdffe 100644 --- a/cli/ts/commands/deploy.ts +++ b/cli/ts/commands/deploy.ts @@ -32,7 +32,6 @@ export const deploy = async ({ poseidonT4Address, poseidonT5Address, poseidonT6Address, - useQv = true, signer, quiet = true, }: DeployArgs): Promise => { @@ -96,7 +95,6 @@ export const deploy = async ({ signer, stateTreeDepth, quiet: true, - useQv, }); const [maciContractAddress, stateAqContractAddress, pollFactoryContractAddress] = await Promise.all([ diff --git a/cli/ts/commands/deployPoll.ts b/cli/ts/commands/deployPoll.ts index 422de686a..2c0340859 100644 --- a/cli/ts/commands/deployPoll.ts +++ b/cli/ts/commands/deployPoll.ts @@ -29,6 +29,7 @@ export const deployPoll = async ({ vkRegistryAddress, signer, quiet = true, + useQuadraticVoting = false, }: DeployPollArgs): Promise => { banner(quiet); @@ -106,6 +107,7 @@ export const deployPoll = async ({ unserializedKey.asContractParam(), verifierContractAddress, vkRegistry, + useQuadraticVoting, { gasLimit: 10000000 }, ); diff --git a/cli/ts/commands/verify.ts b/cli/ts/commands/verify.ts index 100246096..7b24948ea 100644 --- a/cli/ts/commands/verify.ts +++ b/cli/ts/commands/verify.ts @@ -1,10 +1,7 @@ import { Tally__factory as TallyFactory, - TallyNonQv__factory as TallyNonQvFactory, MACI__factory as MACIFactory, Poll__factory as PollFactory, - Tally, - TallyNonQv, } from "maci-contracts/typechain-types"; import { hash2, hash3, genTreeCommitment, hashLeftRight } from "maci-crypto"; @@ -61,9 +58,7 @@ export const verify = async ({ const pollContract = PollFactory.connect(pollAddr, signer); - const tallyContract = useQv - ? TallyFactory.connect(tallyContractAddress, signer) - : TallyNonQvFactory.connect(tallyContractAddress, signer); + const tallyContract = TallyFactory.connect(tallyContractAddress, signer); // verification const onChainTallyCommitment = BigInt(await tallyContract.tallyCommitment()); @@ -125,7 +120,7 @@ export const verify = async ({ logGreen(quiet, success("The on-chain tally commitment matches.")); // verify total spent voice credits on-chain - const isValid = await (tallyContract as Tally).verifySpentVoiceCredits( + const isValid = await tallyContract.verifySpentVoiceCredits( tallyResults.totalSpentVoiceCredits.spent, tallyResults.totalSpentVoiceCredits.salt, newResultsCommitment, @@ -140,7 +135,7 @@ export const verify = async ({ // verify per vote option voice credits on-chain const failedSpentCredits = await verifyPerVOSpentVoiceCredits( - tallyContract as Tally, + tallyContract, tallyResults, voteOptionTreeDepth, newSpentVoiceCreditsCommitment, @@ -187,10 +182,11 @@ export const verify = async ({ logGreen(quiet, success("The on-chain tally commitment matches.")); // verify total spent voice credits on-chain - const isValid = await (tallyContract as TallyNonQv).verifySpentVoiceCredits( + const isValid = await tallyContract.verifySpentVoiceCredits( tallyResults.totalSpentVoiceCredits.spent, tallyResults.totalSpentVoiceCredits.salt, newResultsCommitment, + 0n, ); if (isValid) { diff --git a/cli/ts/index.ts b/cli/ts/index.ts index 13c97abed..fa586a543 100644 --- a/cli/ts/index.ts +++ b/cli/ts/index.ts @@ -59,7 +59,6 @@ program .option("-g, --signupGatekeeperAddress ", "the signup gatekeeper contract address") .option("-q, --quiet ", "whether to print values to the console", (value) => value === "true", false) .option("-r, --rpc-provider ", "the rpc provider URL") - .option("-uq, --use-quadratic-voting", "whether to use quadratic voting", (value) => value === "true", true) .requiredOption("-s, --stateTreeDepth ", "the state tree depth", parseInt) .action(async (cmdOptions) => { try { @@ -74,7 +73,6 @@ program poseidonT4Address: cmdOptions.poseidonT4Address, poseidonT5Address: cmdOptions.poseidonT5Address, poseidonT6Address: cmdOptions.poseidonT6Address, - useQv: cmdOptions.useQuadraticVoting, quiet: cmdOptions.quiet, signer, }); @@ -200,6 +198,7 @@ program .requiredOption("-m, --msg-tree-depth ", "the message tree depth", parseInt) .requiredOption("-v, --vote-option-tree-depth ", "the vote option tree depth", parseInt) .requiredOption("-pk, --pubkey ", "the coordinator public key") + .option("-uq, --use-quadratic-voting", "whether to use quadratic voting", (value) => value === "true", true) .option("-x, --maci-address ", "the MACI contract address") .option("-q, --quiet ", "whether to print values to the console", (value) => value === "true", false) .option("-r, --rpc-provider ", "the rpc provider URL") @@ -217,6 +216,7 @@ program maciAddress: cmdObj.maciAddress, vkRegistryAddress: cmdObj.vkRegistryAddress, quiet: cmdObj.quiet, + useQuadraticVoting: cmdObj.useQuadraticVoting, signer, }); } catch (error) { diff --git a/cli/ts/utils/interfaces.ts b/cli/ts/utils/interfaces.ts index 181b298ef..bde321545 100644 --- a/cli/ts/utils/interfaces.ts +++ b/cli/ts/utils/interfaces.ts @@ -292,11 +292,6 @@ export interface DeployArgs { * Whether to log the output */ quiet?: boolean; - - /** - * Whether to use quadratic voting or not - */ - useQv?: boolean; } /** @@ -352,6 +347,11 @@ export interface DeployPollArgs { * Whether to log the output to the console */ quiet?: boolean; + + /** + * Whether to use quadratic voting or not + */ + useQuadraticVoting?: boolean; } /** diff --git a/cli/ts/utils/verifiers.ts b/cli/ts/utils/verifiers.ts index bd865783d..ef47542f7 100644 --- a/cli/ts/utils/verifiers.ts +++ b/cli/ts/utils/verifiers.ts @@ -1,7 +1,7 @@ import { genTreeProof } from "maci-crypto"; import type { TallyData } from "./interfaces"; -import type { Tally, TallyNonQv } from "maci-contracts"; +import type { Tally } from "maci-contracts"; /** * Loop through each per vote option spent voice credits and verify it on-chain @@ -57,7 +57,7 @@ export const verifyPerVOSpentVoiceCredits = async ( * @returns list of the indexes of the tally result that failed on-chain verification */ export const verifyTallyResults = async ( - tallyContract: Tally | TallyNonQv, + tallyContract: Tally, tallyData: TallyData, voteOptionTreeDepth: number, newSpentVoiceCreditsCommitment: bigint, @@ -72,30 +72,16 @@ export const verifyTallyResults = async ( voteOptionTreeDepth, ); - let isValid: boolean; - - if (!newPerVOSpentVoiceCreditsCommitment) { - // eslint-disable-next-line no-await-in-loop - isValid = await (tallyContract as TallyNonQv).verifyTallyResult( - i, - tallyData.results.tally[i], - proof, - tallyData.results.salt, - voteOptionTreeDepth, - newSpentVoiceCreditsCommitment, - ); - } else { - // eslint-disable-next-line no-await-in-loop - isValid = await (tallyContract as Tally).verifyTallyResult( - i, - tallyData.results.tally[i], - proof, - tallyData.results.salt, - voteOptionTreeDepth, - newSpentVoiceCreditsCommitment, - newPerVOSpentVoiceCreditsCommitment, - ); - } + // eslint-disable-next-line no-await-in-loop + const isValid = await tallyContract.verifyTallyResult( + i, + tallyData.results.tally[i], + proof, + tallyData.results.salt, + voteOptionTreeDepth, + newSpentVoiceCreditsCommitment, + newPerVOSpentVoiceCreditsCommitment ?? 0n, + ); if (!isValid) { failedIndices.push(i); diff --git a/contracts/contracts/MACI.sol b/contracts/contracts/MACI.sol index 7f5578b5a..7798bb0ed 100644 --- a/contracts/contracts/MACI.sol +++ b/contracts/contracts/MACI.sol @@ -199,13 +199,15 @@ contract MACI is IMACI, Params, Utilities, Ownable { /// @param _coordinatorPubKey The coordinator's public key /// @param _verifier The Verifier Contract /// @param _vkRegistry The VkRegistry Contract + /// @param _isQv Whether to support QV or not /// @return pollAddr a new Poll contract address function deployPoll( uint256 _duration, TreeDepths memory _treeDepths, PubKey memory _coordinatorPubKey, address _verifier, - address _vkRegistry + address _vkRegistry, + bool _isQv ) public virtual onlyOwner returns (PollContracts memory pollAddr) { // cache the poll to a local variable so we can increment it uint256 pollId = nextPollId; @@ -238,7 +240,7 @@ contract MACI is IMACI, Params, Utilities, Ownable { ); address mp = messageProcessorFactory.deploy(_verifier, _vkRegistry, p, _owner); - address tally = tallyFactory.deploy(_verifier, _vkRegistry, p, mp, _owner); + address tally = tallyFactory.deploy(_verifier, _vkRegistry, p, mp, _owner, _isQv); polls[pollId] = p; diff --git a/contracts/contracts/Tally.sol b/contracts/contracts/Tally.sol index c05cb14e1..a503b30ca 100644 --- a/contracts/contracts/Tally.sol +++ b/contracts/contracts/Tally.sol @@ -21,12 +21,19 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher { /// the tally of each batch is proven on-chain via a zk-SNARK, it should be /// updated to: /// + /// QV: /// hash3( /// hashLeftRight(merkle root of current results, salt0) /// hashLeftRight(number of spent voice credits, salt1), /// hashLeftRight(merkle root of the no. of spent voice credits per vote option, salt2) /// ) /// + /// Non-QV: + /// hash2( + /// hashLeftRight(merkle root of current results, salt0) + /// hashLeftRight(number of spent voice credits, salt1), + /// ) + /// /// Where each salt is unique and the merkle roots are of arrays of leaves /// TREE_ARITY ** voteOptionTreeDepth long. uint256 public tallyCommitment; @@ -40,6 +47,7 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher { IVkRegistry public immutable vkRegistry; IPoll public immutable poll; IMessageProcessor public immutable messageProcessor; + bool public immutable isQv; /// @notice custom errors error ProcessingNotComplete(); @@ -48,17 +56,19 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher { error NumSignUpsTooLarge(); error BatchStartIndexTooLarge(); error TallyBatchSizeTooLarge(); + error NotSupported(); /// @notice Create a new Tally contract /// @param _verifier The Verifier contract /// @param _vkRegistry The VkRegistry contract /// @param _poll The Poll contract /// @param _mp The MessageProcessor contract - constructor(address _verifier, address _vkRegistry, address _poll, address _mp) payable { + constructor(address _verifier, address _vkRegistry, address _poll, address _mp, bool _isQv) payable { verifier = IVerifier(_verifier); vkRegistry = IVkRegistry(_vkRegistry); poll = IPoll(_poll); messageProcessor = IMessageProcessor(_mp); + isQv = _isQv; } /// @notice Pack the batch start index and number of signups into a 100-bit value. @@ -231,7 +241,7 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher { /// @param _totalSpent spent field retrieved in the totalSpentVoiceCredits object /// @param _totalSpentSalt the corresponding salt in the totalSpentVoiceCredit object /// @param _resultCommitment hashLeftRight(merkle root of the results.tally, results.salt) in tally.json file - /// @param _perVOSpentVoiceCreditsHash hashLeftRight(merkle root of the no spent voice credits per vote option, salt) + /// @param _perVOSpentVoiceCreditsHash only for QV - hashLeftRight(merkle root of the no spent voice credits per vote option, salt) /// @return isValid Whether the provided values are valid function verifySpentVoiceCredits( uint256 _totalSpent, @@ -244,9 +254,48 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher { tally[1] = hashLeftRight(_totalSpent, _totalSpentSalt); tally[2] = _perVOSpentVoiceCreditsHash; + isValid = isQv + ? verifyQvSpentVoiceCredits(_totalSpent, _totalSpentSalt, _resultCommitment, _perVOSpentVoiceCreditsHash) + : verifyNonQvSpentVoiceCredits(_totalSpent, _totalSpentSalt, _resultCommitment); + } + + /// @notice Verify the number of spent voice credits for QV from the tally.json + /// @param _totalSpent spent field retrieved in the totalSpentVoiceCredits object + /// @param _totalSpentSalt the corresponding salt in the totalSpentVoiceCredit object + /// @param _resultCommitment hashLeftRight(merkle root of the results.tally, results.salt) in tally.json file + /// @param _perVOSpentVoiceCreditsHash hashLeftRight(merkle root of the no spent voice credits per vote option, salt) + /// @return isValid Whether the provided values are valid + function verifyQvSpentVoiceCredits( + uint256 _totalSpent, + uint256 _totalSpentSalt, + uint256 _resultCommitment, + uint256 _perVOSpentVoiceCreditsHash + ) internal view returns (bool isValid) { + uint256[3] memory tally; + tally[0] = _resultCommitment; + tally[1] = hashLeftRight(_totalSpent, _totalSpentSalt); + tally[2] = _perVOSpentVoiceCreditsHash; + isValid = hash3(tally) == tallyCommitment; } + /// @notice Verify the number of spent voice credits for Non-QV from the tally.json + /// @param _totalSpent spent field retrieved in the totalSpentVoiceCredits object + /// @param _totalSpentSalt the corresponding salt in the totalSpentVoiceCredit object + /// @param _resultCommitment hashLeftRight(merkle root of the results.tally, results.salt) in tally.json file + /// @return isValid Whether the provided values are valid + function verifyNonQvSpentVoiceCredits( + uint256 _totalSpent, + uint256 _totalSpentSalt, + uint256 _resultCommitment + ) internal view returns (bool isValid) { + uint256[2] memory tally; + tally[0] = _resultCommitment; + tally[1] = hashLeftRight(_totalSpent, _totalSpentSalt); + + isValid = hash2(tally) == tallyCommitment; + } + /// @notice Verify the number of spent voice credits per vote option from the tally.json /// @param _voteOptionIndex the index of the vote option where credits were spent /// @param _spent the spent voice credits for a given vote option index @@ -266,6 +315,10 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher { uint256 _spentVoiceCreditsHash, uint256 _resultCommitment ) public view returns (bool isValid) { + if (!isQv) { + revert NotSupported(); + } + uint256 computedRoot = computeMerkleRootFromPath(_voteOptionTreeDepth, _voteOptionIndex, _spent, _spentProof); uint256[3] memory tally; @@ -302,11 +355,18 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher { _tallyResultProof ); - uint256[3] memory tally; - tally[0] = hashLeftRight(computedRoot, _tallyResultSalt); - tally[1] = _spentVoiceCreditsHash; - tally[2] = _perVOSpentVoiceCreditsHash; + if (isQv) { + uint256[3] memory tally = [ + hashLeftRight(computedRoot, _tallyResultSalt), + _spentVoiceCreditsHash, + _perVOSpentVoiceCreditsHash + ]; - isValid = hash3(tally) == tallyCommitment; + isValid = hash3(tally) == tallyCommitment; + } else { + uint256[2] memory tally = [hashLeftRight(computedRoot, _tallyResultSalt), _spentVoiceCreditsHash]; + + isValid = hash2(tally) == tallyCommitment; + } } } diff --git a/contracts/contracts/TallyFactory.sol b/contracts/contracts/TallyFactory.sol index 18a71e205..dc458ccbc 100644 --- a/contracts/contracts/TallyFactory.sol +++ b/contracts/contracts/TallyFactory.sol @@ -13,10 +13,11 @@ contract TallyFactory is ITallyFactory { address _vkRegistry, address _poll, address _messageProcessor, - address _owner + address _owner, + bool _isQv ) public virtual returns (address tallyAddr) { // deploy Tally for this Poll - Tally tally = new Tally(_verifier, _vkRegistry, _poll, _messageProcessor); + Tally tally = new Tally(_verifier, _vkRegistry, _poll, _messageProcessor, _isQv); tally.transferOwnership(_owner); tallyAddr = address(tally); } diff --git a/contracts/contracts/TallyNonQv.sol b/contracts/contracts/TallyNonQv.sol deleted file mode 100644 index 96c8af1df..000000000 --- a/contracts/contracts/TallyNonQv.sol +++ /dev/null @@ -1,273 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import { IMACI } from "./interfaces/IMACI.sol"; -import { Hasher } from "./crypto/Hasher.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { IPoll } from "./interfaces/IPoll.sol"; -import { IMessageProcessor } from "./interfaces/IMessageProcessor.sol"; -import { SnarkCommon } from "./crypto/SnarkCommon.sol"; -import { IVerifier } from "./interfaces/IVerifier.sol"; -import { IVkRegistry } from "./interfaces/IVkRegistry.sol"; -import { CommonUtilities } from "./utilities/CommonUtilities.sol"; - -/// @title TallyNonQv -/// @notice The TallyNonQv contract is used during votes tallying -/// and by users to verify the tally results. -contract TallyNonQv is Ownable, SnarkCommon, CommonUtilities, Hasher { - uint256 internal constant TREE_ARITY = 5; - - /// @notice The commitment to the tally results. Its initial value is 0, but after - /// the tally of each batch is proven on-chain via a zk-SNARK, it should be - /// updated to: - /// - /// hash2( - /// hashLeftRight(merkle root of current results, salt0) - /// hashLeftRight(number of spent voice credits, salt1), - /// ) - /// - /// Where each salt is unique and the merkle roots are of arrays of leaves - /// TREE_ARITY ** voteOptionTreeDepth long. - uint256 public tallyCommitment; - - uint256 public tallyBatchNum; - - // The final commitment to the state and ballot roots - uint256 public sbCommitment; - - IVerifier public immutable verifier; - IVkRegistry public immutable vkRegistry; - IPoll public immutable poll; - IMessageProcessor public immutable messageProcessor; - - /// @notice custom errors - error ProcessingNotComplete(); - error InvalidTallyVotesProof(); - error AllBallotsTallied(); - error NumSignUpsTooLarge(); - error BatchStartIndexTooLarge(); - error TallyBatchSizeTooLarge(); - - /// @notice Create a new Tally contract - /// @param _verifier The Verifier contract - /// @param _vkRegistry The VkRegistry contract - /// @param _poll The Poll contract - /// @param _mp The MessageProcessor contract - constructor(address _verifier, address _vkRegistry, address _poll, address _mp) payable { - verifier = IVerifier(_verifier); - vkRegistry = IVkRegistry(_vkRegistry); - poll = IPoll(_poll); - messageProcessor = IMessageProcessor(_mp); - } - - /// @notice Pack the batch start index and number of signups into a 100-bit value. - /// @param _numSignUps: number of signups - /// @param _batchStartIndex: the start index of given batch - /// @param _tallyBatchSize: size of batch - /// @return result an uint256 representing the 3 inputs packed together - function genTallyVotesPackedVals( - uint256 _numSignUps, - uint256 _batchStartIndex, - uint256 _tallyBatchSize - ) public pure returns (uint256 result) { - if (_numSignUps >= 2 ** 50) revert NumSignUpsTooLarge(); - if (_batchStartIndex >= 2 ** 50) revert BatchStartIndexTooLarge(); - if (_tallyBatchSize >= 2 ** 50) revert TallyBatchSizeTooLarge(); - - result = (_batchStartIndex / _tallyBatchSize) + (_numSignUps << uint256(50)); - } - - /// @notice Check if all ballots are tallied - /// @return tallied whether all ballots are tallied - function isTallied() public view returns (bool tallied) { - (uint8 intStateTreeDepth, , , ) = poll.treeDepths(); - (uint256 numSignUps, ) = poll.numSignUpsAndMessages(); - - // Require that there are untallied ballots left - tallied = tallyBatchNum * (TREE_ARITY ** intStateTreeDepth) >= numSignUps; - } - - /// @notice generate hash of public inputs for tally circuit - /// @param _numSignUps: number of signups - /// @param _batchStartIndex: the start index of given batch - /// @param _tallyBatchSize: size of batch - /// @param _newTallyCommitment: the new tally commitment to be updated - /// @return inputHash hash of public inputs - function genTallyVotesPublicInputHash( - uint256 _numSignUps, - uint256 _batchStartIndex, - uint256 _tallyBatchSize, - uint256 _newTallyCommitment - ) public view returns (uint256 inputHash) { - uint256 packedVals = genTallyVotesPackedVals(_numSignUps, _batchStartIndex, _tallyBatchSize); - uint256[] memory input = new uint256[](4); - input[0] = packedVals; - input[1] = sbCommitment; - input[2] = tallyCommitment; - input[3] = _newTallyCommitment; - inputHash = sha256Hash(input); - } - - /// @notice Update the state and ballot root commitment - function updateSbCommitment() public onlyOwner { - // Require that all messages have been processed - if (!messageProcessor.processingComplete()) { - revert ProcessingNotComplete(); - } - - if (sbCommitment == 0) { - sbCommitment = messageProcessor.sbCommitment(); - } - } - - /// @notice Verify the result of a tally batch - /// @param _newTallyCommitment the new tally commitment to be verified - /// @param _proof the proof generated after tallying this batch - function tallyVotes(uint256 _newTallyCommitment, uint256[8] calldata _proof) public onlyOwner { - _votingPeriodOver(poll); - updateSbCommitment(); - - // get the batch size and start index - (uint8 intStateTreeDepth, , , ) = poll.treeDepths(); - uint256 tallyBatchSize = TREE_ARITY ** intStateTreeDepth; - uint256 batchStartIndex = tallyBatchNum * tallyBatchSize; - - // save some gas because we won't overflow uint256 - unchecked { - tallyBatchNum++; - } - - (uint256 numSignUps, ) = poll.numSignUpsAndMessages(); - - // Require that there are untallied ballots left - if (batchStartIndex >= numSignUps) { - revert AllBallotsTallied(); - } - - bool isValid = verifyTallyProof(_proof, numSignUps, batchStartIndex, tallyBatchSize, _newTallyCommitment); - - if (!isValid) { - revert InvalidTallyVotesProof(); - } - - // Update the tally commitment and the tally batch num - tallyCommitment = _newTallyCommitment; - } - - /// @notice Verify the tally proof using the verifying key - /// @param _proof the proof generated after processing all messages - /// @param _numSignUps number of signups for a given poll - /// @param _batchStartIndex the number of batches multiplied by the size of the batch - /// @param _tallyBatchSize batch size for the tally - /// @param _newTallyCommitment the tally commitment to be verified at a given batch index - /// @return isValid whether the proof is valid - function verifyTallyProof( - uint256[8] calldata _proof, - uint256 _numSignUps, - uint256 _batchStartIndex, - uint256 _tallyBatchSize, - uint256 _newTallyCommitment - ) public view returns (bool isValid) { - (uint8 intStateTreeDepth, , , uint8 voteOptionTreeDepth) = poll.treeDepths(); - - (IMACI maci, , ) = poll.extContracts(); - - // Get the verifying key - VerifyingKey memory vk = vkRegistry.getTallyVk(maci.stateTreeDepth(), intStateTreeDepth, voteOptionTreeDepth); - - // Get the public inputs - uint256 publicInputHash = genTallyVotesPublicInputHash( - _numSignUps, - _batchStartIndex, - _tallyBatchSize, - _newTallyCommitment - ); - - // Verify the proof - isValid = verifier.verify(_proof, vk, publicInputHash); - } - - /// @notice Compute the merkle root from the path elements - /// and a leaf - /// @param _depth the depth of the merkle tree - /// @param _index the index of the leaf - /// @param _leaf the leaf - /// @param _pathElements the path elements to reconstruct the merkle root - /// @return current The merkle root - function computeMerkleRootFromPath( - uint8 _depth, - uint256 _index, - uint256 _leaf, - uint256[][] calldata _pathElements - ) internal pure returns (uint256 current) { - uint256 pos = _index % TREE_ARITY; - current = _leaf; - uint8 k; - - uint256[TREE_ARITY] memory level; - - for (uint8 i = 0; i < _depth; ++i) { - for (uint8 j = 0; j < TREE_ARITY; ++j) { - if (j == pos) { - level[j] = current; - } else { - if (j > pos) { - k = j - 1; - } else { - k = j; - } - level[j] = _pathElements[i][k]; - } - } - - _index /= TREE_ARITY; - pos = _index % TREE_ARITY; - current = hash5(level); - } - } - - /// @notice Verify the number of spent voice credits from the tally.json - /// @param _totalSpent spent field retrieved in the totalSpentVoiceCredits object - /// @param _totalSpentSalt the corresponding salt in the totalSpentVoiceCredit object - /// @param _resultCommitment hashLeftRight(merkle root of the results.tally, results.salt) in tally.json file - /// @return isValid Whether the provided values are valid - function verifySpentVoiceCredits( - uint256 _totalSpent, - uint256 _totalSpentSalt, - uint256 _resultCommitment - ) public view returns (bool isValid) { - uint256[2] memory tally; - tally[0] = _resultCommitment; - tally[1] = hashLeftRight(_totalSpent, _totalSpentSalt); - - isValid = hash2(tally) == tallyCommitment; - } - - /// @notice Verify the result generated from the tally.json - /// @param _voteOptionIndex the index of the vote option to verify the correctness of the tally - /// @param _tallyResult Flattened array of the tally - /// @param _tallyResultProof Corresponding proof of the tally result - /// @param _tallyResultSalt the respective salt in the results object in the tally.json - /// @param _voteOptionTreeDepth depth of the vote option tree - /// @param _spentVoiceCreditsHash hashLeftRight(number of spent voice credits, spent salt) - /// @return isValid Whether the provided proof is valid - function verifyTallyResult( - uint256 _voteOptionIndex, - uint256 _tallyResult, - uint256[][] calldata _tallyResultProof, - uint256 _tallyResultSalt, - uint8 _voteOptionTreeDepth, - uint256 _spentVoiceCreditsHash - ) public view returns (bool isValid) { - uint256 computedRoot = computeMerkleRootFromPath( - _voteOptionTreeDepth, - _voteOptionIndex, - _tallyResult, - _tallyResultProof - ); - - uint256[2] memory tally = [hashLeftRight(computedRoot, _tallyResultSalt), _spentVoiceCreditsHash]; - - isValid = hash2(tally) == tallyCommitment; - } -} diff --git a/contracts/contracts/TallyNonQvFactory.sol b/contracts/contracts/TallyNonQvFactory.sol deleted file mode 100644 index a07a3d118..000000000 --- a/contracts/contracts/TallyNonQvFactory.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import { TallyNonQv } from "./TallyNonQv.sol"; -import { ITallyFactory } from "./interfaces/ITallyFactory.sol"; - -/// @title TallyNonQvFactory -/// @notice A factory contract which deploys TallyNonQv contracts. -contract TallyNonQvFactory is ITallyFactory { - /// @inheritdoc ITallyFactory - function deploy( - address _verifier, - address _vkRegistry, - address _poll, - address _messageProcessor, - address _owner - ) public virtual returns (address tallyAddr) { - // deploy Tally for this Poll - TallyNonQv tally = new TallyNonQv(_verifier, _vkRegistry, _poll, _messageProcessor); - tally.transferOwnership(_owner); - tallyAddr = address(tally); - } -} diff --git a/contracts/contracts/interfaces/ITallyFactory.sol b/contracts/contracts/interfaces/ITallyFactory.sol index 9ef226cbd..1e2c7e4bd 100644 --- a/contracts/contracts/interfaces/ITallyFactory.sol +++ b/contracts/contracts/interfaces/ITallyFactory.sol @@ -10,12 +10,14 @@ interface ITallyFactory { /// @param _poll Poll contract /// @param _messageProcessor MessageProcessor contract /// @param _owner Owner of the contract + /// @param _isQv Whether to support QV or not /// @return The deployed contract function deploy( address _verifier, address _vkRegistry, address _poll, address _messageProcessor, - address _owner + address _owner, + bool _isQv ) external returns (address); } diff --git a/contracts/tasks/deploy/maci/08-tallyFactory.ts b/contracts/tasks/deploy/maci/08-tallyFactory.ts index ac3812a83..090b78c59 100644 --- a/contracts/tasks/deploy/maci/08-tallyFactory.ts +++ b/contracts/tasks/deploy/maci/08-tallyFactory.ts @@ -25,10 +25,8 @@ deployment const poseidonT5ContractAddress = storage.mustGetAddress(EContracts.PoseidonT5, hre.network.name); const poseidonT6ContractAddress = storage.mustGetAddress(EContracts.PoseidonT6, hre.network.name); - const useQuadraticVoting = deployment.getDeployConfigField(EContracts.Poll, "useQuadraticVoting"); - const linkedTallyFactoryContract = await deployment.linkPoseidonLibraries( - useQuadraticVoting ? EContracts.TallyFactory : EContracts.TallyNonQvFactory, + EContracts.TallyFactory, poseidonT3ContractAddress, poseidonT4ContractAddress, poseidonT5ContractAddress, diff --git a/contracts/tasks/deploy/poll/01-poll.ts b/contracts/tasks/deploy/poll/01-poll.ts index 1cefb0b75..6d981a7f3 100644 --- a/contracts/tasks/deploy/poll/01-poll.ts +++ b/contracts/tasks/deploy/poll/01-poll.ts @@ -69,6 +69,7 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").setAction(async (_, hre unserializedKey.asContractParam(), verifierContractAddress, vkRegistryContractAddress, + useQuadraticVoting, ); const tx = await maciContract.deployPoll( @@ -82,6 +83,7 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").setAction(async (_, hre unserializedKey.asContractParam(), verifierContractAddress, vkRegistryContractAddress, + useQuadraticVoting, ); const receipt = await tx.wait(); @@ -99,7 +101,7 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").setAction(async (_, hre }); const tallyContract = await deployment.getContract({ - name: useQuadraticVoting ? EContracts.Tally : EContracts.TallyNonQv, + name: EContracts.Tally, address: tallyContractAddress, }); @@ -137,10 +139,16 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").setAction(async (_, hre }), storage.register({ - id: useQuadraticVoting ? EContracts.Tally : EContracts.TallyNonQv, + id: EContracts.Tally, key: `poll-${pollId}`, contract: tallyContract, - args: [verifierContractAddress, vkRegistryContractAddress, pollContractAddress, messageProcessorContractAddress], + args: [ + verifierContractAddress, + vkRegistryContractAddress, + pollContractAddress, + messageProcessorContractAddress, + useQuadraticVoting, + ], network: hre.network.name, }), diff --git a/contracts/tasks/helpers/Prover.ts b/contracts/tasks/helpers/Prover.ts index 4067b0994..e68df92db 100644 --- a/contracts/tasks/helpers/Prover.ts +++ b/contracts/tasks/helpers/Prover.ts @@ -3,16 +3,7 @@ import { G1Point, G2Point, hashLeftRight } from "maci-crypto"; import { VerifyingKey } from "maci-domainobjs"; import type { IVerifyingKeyStruct, Proof } from "../../ts/types"; -import type { - AccQueue, - MACI, - MessageProcessor, - Poll, - Tally, - TallyNonQv, - Verifier, - VkRegistry, -} from "../../typechain-types"; +import type { AccQueue, MACI, MessageProcessor, Poll, Tally, Verifier, VkRegistry } from "../../typechain-types"; import type { BigNumberish } from "ethers"; import { formatProofForVerifierContract, asHex } from "../../ts/utils"; @@ -57,7 +48,7 @@ export class Prover { /** * Tally contract typechain wrapper */ - private tallyContract: Tally | TallyNonQv; + private tallyContract: Tally; /** * Initialize class properties diff --git a/contracts/tasks/helpers/types.ts b/contracts/tasks/helpers/types.ts index 772987fa8..5ca5ed179 100644 --- a/contracts/tasks/helpers/types.ts +++ b/contracts/tasks/helpers/types.ts @@ -1,13 +1,4 @@ -import type { - AccQueue, - MACI, - MessageProcessor, - Poll, - Tally, - TallyNonQv, - Verifier, - VkRegistry, -} from "../../typechain-types"; +import type { AccQueue, MACI, MessageProcessor, Poll, Tally, Verifier, VkRegistry } from "../../typechain-types"; import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; import type { BaseContract, BigNumberish, Signer } from "ethers"; import type { Libraries, TaskArguments } from "hardhat/types"; @@ -326,7 +317,7 @@ export interface IProverParams { /** * Tally contract typechain wrapper */ - tallyContract: Tally | TallyNonQv; + tallyContract: Tally; } /** @@ -486,7 +477,6 @@ export enum EContracts { PollFactory = "PollFactory", MessageProcessorFactory = "MessageProcessorFactory", TallyFactory = "TallyFactory", - TallyNonQvFactory = "TallyNonQvFactory", PoseidonT3 = "PoseidonT3", PoseidonT4 = "PoseidonT4", PoseidonT5 = "PoseidonT5", @@ -494,7 +484,6 @@ export enum EContracts { VkRegistry = "VkRegistry", Poll = "Poll", Tally = "Tally", - TallyNonQv = "TallyNonQv", MessageProcessor = "MessageProcessor", AccQueue = "AccQueue", AccQueueQuinaryBlankSl = "AccQueueQuinaryBlankSl", diff --git a/contracts/tasks/runner/prove.ts b/contracts/tasks/runner/prove.ts index 2e28c073f..8c7f9e4d4 100644 --- a/contracts/tasks/runner/prove.ts +++ b/contracts/tasks/runner/prove.ts @@ -6,16 +6,7 @@ import { Keypair, PrivKey } from "maci-domainobjs"; import fs from "fs"; import type { Proof } from "../../ts/types"; -import type { - VkRegistry, - Verifier, - MACI, - Poll, - AccQueue, - MessageProcessor, - Tally, - TallyNonQv, -} from "../../typechain-types"; +import type { VkRegistry, Verifier, MACI, Poll, AccQueue, MessageProcessor, Tally } from "../../typechain-types"; import { ContractStorage } from "../helpers/ContractStorage"; import { Deployment } from "../helpers/Deployment"; @@ -149,15 +140,10 @@ task("prove", "Command to generate proof and prove the result of a poll on-chain }); // get the tally contract based on the useQuadraticVoting flag - const tallyContract = useQuadraticVoting - ? await deployment.getContract({ - name: EContracts.Tally, - key: `poll-${poll.toString()}`, - }) - : await deployment.getContract({ - name: EContracts.TallyNonQv, - key: `poll-${poll.toString()}`, - }); + const tallyContract = await deployment.getContract({ + name: EContracts.Tally, + key: `poll-${poll.toString()}`, + }); const tallyContractAddress = await tallyContract.getAddress(); const proofGenerator = new ProofGenerator({ diff --git a/contracts/tests/EASGatekeeper.test.ts b/contracts/tests/EASGatekeeper.test.ts index 009248fcc..a8448f52a 100644 --- a/contracts/tests/EASGatekeeper.test.ts +++ b/contracts/tests/EASGatekeeper.test.ts @@ -87,14 +87,7 @@ describe("EAS Gatekeeper", () => { let maciContract: MACI; before(async () => { - const r = await deployTestContracts( - initialVoiceCreditBalance, - STATE_TREE_DEPTH, - signer, - true, - true, - easGatekeeper, - ); + const r = await deployTestContracts(initialVoiceCreditBalance, STATE_TREE_DEPTH, signer, true, easGatekeeper); maciContract = r.maciContract; }); diff --git a/contracts/tests/HatsGatekeeper.test.ts b/contracts/tests/HatsGatekeeper.test.ts index 082b19b82..049f18898 100644 --- a/contracts/tests/HatsGatekeeper.test.ts +++ b/contracts/tests/HatsGatekeeper.test.ts @@ -119,7 +119,6 @@ describe("HatsProtocol Gatekeeper", () => { STATE_TREE_DEPTH, signer, true, - true, hatsGatekeeperSingle, ); @@ -221,7 +220,6 @@ describe("HatsProtocol Gatekeeper", () => { STATE_TREE_DEPTH, signer, true, - true, hatsGatekeeperMultiple, ); diff --git a/contracts/tests/MACI.test.ts b/contracts/tests/MACI.test.ts index 4141fa375..4b36c83d0 100644 --- a/contracts/tests/MACI.test.ts +++ b/contracts/tests/MACI.test.ts @@ -166,6 +166,7 @@ describe("MACI", () => { coordinator.pubKey.asContractParam() as { x: BigNumberish; y: BigNumberish }, verifierContract, vkRegistryContract, + true, { gasLimit: 10000000 }, ); const receipt = await tx.wait(); @@ -211,6 +212,7 @@ describe("MACI", () => { coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, + true, { gasLimit: 10000000, }, diff --git a/contracts/tests/MessageProcessor.test.ts b/contracts/tests/MessageProcessor.test.ts index d7d51adf4..f329e4a15 100644 --- a/contracts/tests/MessageProcessor.test.ts +++ b/contracts/tests/MessageProcessor.test.ts @@ -62,6 +62,7 @@ describe("MessageProcessor", () => { coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, + true, { gasLimit: 10000000, }, diff --git a/contracts/tests/Poll.test.ts b/contracts/tests/Poll.test.ts index d2d2444ec..5c640671a 100644 --- a/contracts/tests/Poll.test.ts +++ b/contracts/tests/Poll.test.ts @@ -54,6 +54,7 @@ describe("Poll", () => { coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, + true, { gasLimit: 10000000, }, diff --git a/contracts/tests/SignUpGatekeeper.test.ts b/contracts/tests/SignUpGatekeeper.test.ts index 254a0338e..bc09aad04 100644 --- a/contracts/tests/SignUpGatekeeper.test.ts +++ b/contracts/tests/SignUpGatekeeper.test.ts @@ -46,7 +46,6 @@ describe("SignUpGatekeeper", () => { STATE_TREE_DEPTH, signer, true, - true, signUpTokenGatekeeperContract, ); diff --git a/contracts/tests/Tally.test.ts b/contracts/tests/Tally.test.ts index 0896f2d0e..c09ba0b94 100644 --- a/contracts/tests/Tally.test.ts +++ b/contracts/tests/Tally.test.ts @@ -59,7 +59,7 @@ describe("TallyVotes", () => { signer = await getDefaultSigner(); - const r = await deployTestContracts(100, STATE_TREE_DEPTH, signer, true, true); + const r = await deployTestContracts(100, STATE_TREE_DEPTH, signer, true); maciContract = r.maciContract; verifierContract = r.mockVerifierContract as Verifier; vkRegistryContract = r.vkRegistryContract; @@ -72,6 +72,7 @@ describe("TallyVotes", () => { coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, + true, { gasLimit: 10000000, }, @@ -220,7 +221,7 @@ describe("TallyVotes", () => { const intStateTreeDepth = 2; - const r = await deployTestContracts(100, STATE_TREE_DEPTH, signer, true, true); + const r = await deployTestContracts(100, STATE_TREE_DEPTH, signer, true); maciContract = r.maciContract; verifierContract = r.mockVerifierContract as Verifier; vkRegistryContract = r.vkRegistryContract; @@ -252,6 +253,7 @@ describe("TallyVotes", () => { coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, + true, { gasLimit: 10000000, }, @@ -360,7 +362,7 @@ describe("TallyVotes", () => { const intStateTreeDepth = 2; - const r = await deployTestContracts(100, STATE_TREE_DEPTH, signer, true, true); + const r = await deployTestContracts(100, STATE_TREE_DEPTH, signer, true); maciContract = r.maciContract; verifierContract = r.mockVerifierContract as Verifier; vkRegistryContract = r.vkRegistryContract; @@ -392,6 +394,7 @@ describe("TallyVotes", () => { coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, + true, { gasLimit: 10000000, }, diff --git a/contracts/tests/TallyNonQv.test.ts b/contracts/tests/TallyNonQv.test.ts index dddf67f16..22c65c9fb 100644 --- a/contracts/tests/TallyNonQv.test.ts +++ b/contracts/tests/TallyNonQv.test.ts @@ -45,7 +45,7 @@ describe("TallyVotesNonQv", () => { const [pollAbi] = parseArtifact("Poll"); const [mpAbi] = parseArtifact("MessageProcessor"); - const [tallyAbi] = parseArtifact("TallyNonQv"); + const [tallyAbi] = parseArtifact("Tally"); let pollId: bigint; let poll: Poll; @@ -59,7 +59,7 @@ describe("TallyVotesNonQv", () => { signer = await getDefaultSigner(); - const r = await deployTestContracts(100, STATE_TREE_DEPTH, signer, false, true); + const r = await deployTestContracts(100, STATE_TREE_DEPTH, signer, false); maciContract = r.maciContract; verifierContract = r.mockVerifierContract as Verifier; vkRegistryContract = r.vkRegistryContract; @@ -72,6 +72,7 @@ describe("TallyVotesNonQv", () => { coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, + false, { gasLimit: 10000000, }, @@ -203,6 +204,13 @@ describe("TallyVotesNonQv", () => { expect(isTallied).to.eq(true); }); + it("should throw error if try to call verifyPerVOSpentVoiceCredits for non-qv", async () => { + await expect(tallyContract.verifyPerVOSpentVoiceCredits(0, 0, [], 0, 0, 0, 0)).to.be.revertedWithCustomError( + tallyContract, + "NotSupported", + ); + }); + it("tallyVotes() should revert when votes have already been tallied", async () => { await expect( tallyContract.tallyVotes(tallyGeneratedInputs.newTallyCommitment, [0, 0, 0, 0, 0, 0, 0, 0]), diff --git a/contracts/tests/utils.ts b/contracts/tests/utils.ts index 4c41c9145..ab3fdb592 100644 --- a/contracts/tests/utils.ts +++ b/contracts/tests/utils.ts @@ -488,7 +488,6 @@ export const deployTestContracts = async ( initialVoiceCreditBalance: number, stateTreeDepth: number, signer?: Signer, - useQv = true, quiet = true, gatekeeper: FreeForAllGatekeeper | undefined = undefined, ): Promise => { @@ -521,7 +520,6 @@ export const deployTestContracts = async ( topupCreditContractAddress, signer, stateTreeDepth, - useQv, quiet, }); diff --git a/contracts/ts/deploy.ts b/contracts/ts/deploy.ts index 3edfdb5ca..a38e04006 100644 --- a/contracts/ts/deploy.ts +++ b/contracts/ts/deploy.ts @@ -24,7 +24,6 @@ import { TopupCredit, Verifier, VkRegistry, - TallyNonQvFactory, } from "../typechain-types"; import { parseArtifact } from "./abi"; @@ -264,7 +263,6 @@ export const deployMaci = async ({ signer, poseidonAddresses, stateTreeDepth = 10, - useQv = true, quiet = true, }: IDeployMaciArgs): Promise => { const { PoseidonT3Contract, PoseidonT4Contract, PoseidonT5Contract, PoseidonT6Contract } = @@ -282,7 +280,7 @@ export const deployMaci = async ({ poseidonT6, })); - const contractsToLink = ["MACI", "PollFactory", "MessageProcessorFactory", "TallyFactory", "TallyNonQvFactory"]; + const contractsToLink = ["MACI", "PollFactory", "MessageProcessorFactory", "TallyFactory"]; // Link Poseidon contracts to MACI const linkedContractFactories = await Promise.all( @@ -299,7 +297,7 @@ export const deployMaci = async ({ ), ); - const [maciContractFactory, pollFactoryContractFactory, messageProcessorFactory, tallyFactory, tallyFactoryNonQv] = + const [maciContractFactory, pollFactoryContractFactory, messageProcessorFactory, tallyFactory] = await Promise.all(linkedContractFactories); const pollFactoryContract = await deployContractWithLinkedLibraries( @@ -316,9 +314,11 @@ export const deployMaci = async ({ // deploy either the qv or non qv tally factory - they both implement the same interface // so as long as maci is concerned, they are interchangeable - const tallyFactoryContract = useQv - ? await deployContractWithLinkedLibraries(tallyFactory, "TallyFactory", quiet) - : await deployContractWithLinkedLibraries(tallyFactoryNonQv, "TallyNonQvFactory", quiet); + const tallyFactoryContract = await deployContractWithLinkedLibraries( + tallyFactory, + "TallyFactory", + quiet, + ); const [pollAddr, mpAddr, tallyAddr] = await Promise.all([ pollFactoryContract.getAddress(), diff --git a/contracts/ts/types.ts b/contracts/ts/types.ts index b0fe921df..22f566b58 100644 --- a/contracts/ts/types.ts +++ b/contracts/ts/types.ts @@ -162,11 +162,6 @@ export interface IDeployMaciArgs { * Whether to suppress console output */ quiet?: boolean; - - /** - * Whether to support QV or not - */ - useQv?: boolean; } /** diff --git a/integrationTests/ts/__tests__/integration.test.ts b/integrationTests/ts/__tests__/integration.test.ts index d9a545e2c..f90752901 100644 --- a/integrationTests/ts/__tests__/integration.test.ts +++ b/integrationTests/ts/__tests__/integration.test.ts @@ -114,6 +114,7 @@ describe("Integration tests", function test() { coordinatorPubkey: coordinatorKeypair.pubKey.serialize(), maciAddress: contracts.maciAddress, signer, + useQuadraticVoting: true, }); const treeDepths: TreeDepths = { @@ -285,6 +286,7 @@ describe("Integration tests", function test() { "../../../cli/zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test_js/TallyVotes_10-1-2_test.wasm", ), useWasm, + useQuadraticVoting: true, signer, }); expect(tallyData).to.not.eq(undefined); diff --git a/integrationTests/ts/__tests__/maci-keys.test.ts b/integrationTests/ts/__tests__/maci-keys.test.ts index 5a2738160..2f21357cb 100644 --- a/integrationTests/ts/__tests__/maci-keys.test.ts +++ b/integrationTests/ts/__tests__/maci-keys.test.ts @@ -92,6 +92,7 @@ describe("integration tests private/public/keypair", () => { coordinatorKeypair.pubKey.asContractParam(), verifier, vkRegistry, + false, ); // we know it's the first poll so id is 0 diff --git a/website/versioned_docs/version-v1.x/cli.md b/website/versioned_docs/version-v1.x/cli.md index 41da8b149..cafd60acd 100644 --- a/website/versioned_docs/version-v1.x/cli.md +++ b/website/versioned_docs/version-v1.x/cli.md @@ -29,14 +29,14 @@ pnpm run hardhat | Command | Description | Options | | -------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `create` | Deploy the contracts | `-i, --initialVoiceCredits `: The initial voice credits
`-p, --initialVoiceCreditsProxyAddress `: The initial voice credits proxy contract address
`-g, --signupGatekeeperAddress `: The signup gatekeeper contract address
`-ph3, --poseidonT3Address `: The PoseidonT3 contract address
`-ph4, --poseidonT4Address `: The PoseidonT4 contract address
`-ph5, --poseidonT5Address `: The PoseidonT5 contract address
`-ph6, --poseidonT6Address `: The PoseidonT6 contract address
`-q, --quiet`: Whether to print values to the console
`-s, --stateTreeDepth `: The state tree depth
`-uq, --use-quadratic-voting"`: Whether to use quadratic voting
`-r, --rpc-provider `: The rpc provider URL | +| `create` | Deploy the contracts | `-i, --initialVoiceCredits `: The initial voice credits
`-p, --initialVoiceCreditsProxyAddress `: The initial voice credits proxy contract address
`-g, --signupGatekeeperAddress `: The signup gatekeeper contract address
`-ph3, --poseidonT3Address `: The PoseidonT3 contract address
`-ph4, --poseidonT4Address `: The PoseidonT4 contract address
`-ph5, --poseidonT5Address `: The PoseidonT5 contract address
`-ph6, --poseidonT6Address `: The PoseidonT6 contract address
`-q, --quiet`: Whether to print values to the console
`-s, --stateTreeDepth `: The state tree depth
`-r, --rpc-provider `: The rpc provider URL | | `checkVerifyingKeys` | Check that the verifying keys in the contract match the local ones | `-q, --quiet`: Whether to print values to the console
`-vk, --vk-contract `: The VkRegistry contract address
`-s, --state-tree-depth `: The state tree depth
`-i, --int-state-tree-depth `: The intermediate state tree depth
`-m, --msg-tree-depth `: The message tree depth
`-v, --vote-option-tree-depth `: The vote option tree depth
`-b, --msg-batch-depth `: The message batch depth
`-p, --process-messages-zkey `: The process messages zkey path (see different options to use specific circuits [Trusted setup](https://maci.pse.dev/docs/trusted-setup) or [Testing](https://maci.pse.dev/docs/testing/#pre-compiled-artifacts-for-testing))
`-t, --tally-votes-zkey `: The tally votes zkey path (see different options to use specific circuits [Trusted setup](https://maci.pse.dev/docs/trusted-setup) or [Testing](https://maci.pse.dev/docs/testing/#pre-compiled-artifacts-for-testing))
`-ss, --subsidy-zkey `: The subsidy zkey path (see different options to use specific circuits [Trusted setup](https://maci.pse.dev/docs/trusted-setup) or [Testing](https://maci.pse.dev/docs/testing/#pre-compiled-artifacts-for-testing)) | | `genMaciPubKey` | Generate a new MACI public key | `-sk, --privkey `: The private key | | `genMaciKeyPair` | Generate a new MACI key pair | `-sp, --seed seed value for keypair` | | `airdrop` | Airdrop topup credits to the coordinator | `-a, --amount `: The amount of topup
`-x, --maci-address `: The MACI contract address
`-o, --poll-id `: Poll id
`-t, --token-address `: The token address
`-q, --quiet`: Whether to print values to the console | | `deployVkRegistry` | Deploy a new verification key registry contract | `-q, --quiet`: Whether to print values to the console | | `show` | Show the deployed contract addresses | No options | -| `deployPoll` | Deploy a new poll | `-t, --duration `: The poll duration
`-i, --int-state-tree-depth `: The int state tree depth
`-b, --msg-batch-depth `: The message tree sub depth
`-m, --msg-tree-depth `: The message tree depth
`-v, --vote-option-tree-depth `: The vote option tree depth
`-pk, --pubkey `: The coordinator public key
`-x, --maci-address `: The MACI contract address
`-q, --quiet`: Whether to print values to the console
"-vk, `--vkRegistryAddress `: The vk registry contract address | +| `deployPoll` | Deploy a new poll | `-t, --duration `: The poll duration
`-i, --int-state-tree-depth `: The int state tree depth
`-b, --msg-batch-depth `: The message tree sub depth
`-m, --msg-tree-depth `: The message tree depth
`-v, --vote-option-tree-depth `: The vote option tree depth
`-pk, --pubkey `: The coordinator public key
`-x, --maci-address `: The MACI contract address
`-uq, --use-quadratic-voting"`: Whether to use quadratic voting
`-q, --quiet`: Whether to print values to the console
"-vk, `--vkRegistryAddress `: The vk registry contract address | | `setVerifyingKeys` | Set the verifying keys | `-s, --state-tree-depth `: The state tree depth
`-i, --int-state-tree-depth `: The intermediate state tree depth
`-m, --msg-tree-depth `: The message tree depth
`-v, --vote-option-tree-depth `: The vote option tree depth
`-b, --msg-batch-depth `: The message batch depth
`-p, --process-messages-zkey `: The process messages zkey path (see different options to use specific circuits [Trusted setup](https://maci.pse.dev/docs/trusted-setup) or [Testing](https://maci.pse.dev/docs/testing/#pre-compiled-artifacts-for-testing))
`-t, --tally-votes-zkey `: The tally votes zkey path (see different options to use specific circuits [Trusted setup](https://maci.pse.dev/docs/trusted-setup) or [Testing](https://maci.pse.dev/docs/testing/#pre-compiled-artifacts-for-testing))
`-k, --vk-registry `: The vk registry contract address
`-q, --quiet`: Whether to print values to the console
`-ss, --subsidy-zkey `: The subsidy zkey path (see different options to use specific circuits [Trusted setup](https://maci.pse.dev/docs/trusted-setup) or [Testing](https://maci.pse.dev/docs/testing/#pre-compiled-artifacts-for-testing)) | | `publish` | Publish a new message to a MACI Poll contract | `-p, --pubkey `: The MACI public key which should replace the user's public key in the state tree
`-x, --maci-address `: The MACI contract address
`-sk, --privkey `: Your serialized MACI private key
`-i, --state-index `: The user's state index
`-v, --vote-option-index `: The vote option index
`-n, --nonce `: The message nonce
`-s, --salt `: The message salt
`-o, --poll-id `: The poll id
`-w, --new-vote-weight `: The new vote weight
`-q, --quiet`: Whether to print values to the console | | `mergeMessages` | Merge the message accumulator queue | `-q, --quiet`: Whether to print values to the console
`-x, --maci-address `: The MACI contract address
`-o, --poll-id `: The poll id
`-n, --num-queue-ops `: The number of queue operations | diff --git a/website/versioned_docs/version-v1.x/poll-types.md b/website/versioned_docs/version-v1.x/poll-types.md index 27c5f0e2f..9f04b172f 100644 --- a/website/versioned_docs/version-v1.x/poll-types.md +++ b/website/versioned_docs/version-v1.x/poll-types.md @@ -43,7 +43,7 @@ If verifying without a tally file, but by passing a tally data object, please en The non quadratic voting option is a new feature that has been added to MACI with the v1.2 release. It allows to conduct polls without the quadratic voting mechanism. This means that the number of voice credits is not reduced by the square of the weight of the vote casted. This option is useful for polls where the quadratic voting mechanism is not necessary, and it is also slightly cheaper for coordinators to tally votes, as there are less checks required in the Tally smart contract. -To run a poll with non quadratic voting, the coordinator must set the `useQuadraticVoting` parameter to `false` when creating the MACI instance. This will make the MACI instance use the `TallyNonQv` smart contract, which is a smaller version of the `Tally` smart contract, as it does not require the checks for the quadratic voting mechanism. +To run a poll with non quadratic voting, the coordinator must set the `useQuadraticVoting` parameter to `false` when creating the Poll instance. This will make the Poll instance use the `NonQv` logic. Using MACI's cli, one can create a MACI instance with non quadratic voting by running the following command: