diff --git a/cli/ts/commands/genLocalState.ts b/cli/ts/commands/genLocalState.ts index 0f8fec58b3..7bf00ff1c2 100644 --- a/cli/ts/commands/genLocalState.ts +++ b/cli/ts/commands/genLocalState.ts @@ -95,7 +95,7 @@ export const genLocalState = async ({ .queryFilter(pollContract.filters.MergeMessageAq(messageRoot), fromBlock) .then((events) => events[events.length - 1]?.blockNumber), pollContract - .queryFilter(pollContract.filters.MergeMaciStateAq(stateRoot, numSignups), fromBlock) + .queryFilter(pollContract.filters.MergeMaciState(stateRoot, numSignups), fromBlock) .then((events) => events[events.length - 1]?.blockNumber), ]).then((blocks) => Math.max(...blocks)); diff --git a/cli/ts/commands/genProofs.ts b/cli/ts/commands/genProofs.ts index 2e87c2107e..a7001a6044 100644 --- a/cli/ts/commands/genProofs.ts +++ b/cli/ts/commands/genProofs.ts @@ -156,7 +156,7 @@ export const genProofs = async ({ const messageAqContract = AccQueueFactory.connect(messageAqContractAddr, signer); // Check that the state and message trees have been merged - if (!(await pollContract.stateAqMerged())) { + if (!(await pollContract.stateMerged())) { logError("The state tree has not been merged yet. Please use the mergeSignups subcommmand to do so."); } @@ -201,7 +201,7 @@ export const genProofs = async ({ .queryFilter(pollContract.filters.MergeMessageAq(messageRoot), fromBlock) .then((events) => events[events.length - 1]?.blockNumber), pollContract - .queryFilter(pollContract.filters.MergeMaciStateAq(stateRoot, numSignups), fromBlock) + .queryFilter(pollContract.filters.MergeMaciState(stateRoot, numSignups), fromBlock) .then((events) => events[events.length - 1]?.blockNumber), ]).then((blocks) => Math.max(...blocks)); diff --git a/cli/ts/commands/mergeMessages.ts b/cli/ts/commands/mergeMessages.ts index c84cb02963..23362b8314 100644 --- a/cli/ts/commands/mergeMessages.ts +++ b/cli/ts/commands/mergeMessages.ts @@ -58,14 +58,6 @@ export const mergeMessages = async ({ const accQueueContract = AccQueueFactory.connect(messageAqContractAddr, signer); - // we need to ensure that the signer is the owner of the poll contract - // this is because only the owner can merge the message AQ - const pollOwner = await pollContract.owner(); - const signerAddress = await signer.getAddress(); - if (pollOwner.toLowerCase() !== signerAddress.toLowerCase()) { - logError("The signer is not the owner of this Poll contract"); - } - // check if it's time to merge the message AQ const dd = await pollContract.getDeployTimeAndDuration(); const deadline = Number(dd[0]) + Number(dd[1]); diff --git a/cli/ts/commands/mergeSignups.ts b/cli/ts/commands/mergeSignups.ts index 282fe85fb1..aa393b10b9 100644 --- a/cli/ts/commands/mergeSignups.ts +++ b/cli/ts/commands/mergeSignups.ts @@ -54,10 +54,10 @@ export const mergeSignups = async ({ pollId, maciAddress, signer, quiet = true } logError("Voting period is not over"); } - if (!(await pollContract.stateAqMerged())) { + if (!(await pollContract.stateMerged())) { // go and merge the state tree - logYellow(quiet, info("Merging subroots to a main state root...")); - const tx = await pollContract.mergeMaciStateAq(); + logYellow(quiet, info("Calculating root and storing on Poll...")); + const tx = await pollContract.mergeMaciState(); const receipt = await tx.wait(); if (receipt?.status !== 1) { diff --git a/cli/ts/commands/poll.ts b/cli/ts/commands/poll.ts index 4b961a5ba2..f0337aac9e 100644 --- a/cli/ts/commands/poll.ts +++ b/cli/ts/commands/poll.ts @@ -42,7 +42,7 @@ export const getPoll = async ({ const [[deployTime, duration], isStateAqMerged] = await Promise.all([ pollContract.getDeployTimeAndDuration(), - pollContract.stateAqMerged(), + pollContract.stateMerged(), ]); const numSignups = await (isStateAqMerged ? pollContract.numSignups() : maciContract.numSignUps()); diff --git a/contracts/contracts/MACI.sol b/contracts/contracts/MACI.sol index a967557f04..5b41cf5c46 100644 --- a/contracts/contracts/MACI.sol +++ b/contracts/contracts/MACI.sol @@ -12,21 +12,19 @@ import { TopupCredit } from "./TopupCredit.sol"; import { Utilities } from "./utilities/Utilities.sol"; import { DomainObjs } from "./utilities/DomainObjs.sol"; import { CurveBabyJubJub } from "./crypto/BabyJubJub.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { InternalLazyIMT, LazyIMTData } from "./trees/LazyIMT.sol"; /// @title MACI - Minimum Anti-Collusion Infrastructure Version 1 /// @notice A contract which allows users to sign up, and deploy new polls -contract MACI is IMACI, DomainObjs, Params, Utilities, Ownable(msg.sender) { +contract MACI is IMACI, DomainObjs, Params, Utilities { /// @notice The state tree depth is fixed. As such it should be as large as feasible - /// so that there can be as many users as possible. i.e. 5 ** 10 = 9765625 + /// so that there can be as many users as possible. i.e. 2 ** 23 = 8388608 /// this should also match the parameter of the circom circuits. - uint8 public immutable stateTreeDepth; - /// @notice IMPORTANT: remember to change the ballot tree depth /// in contracts/ts/genEmptyBallotRootsContract.ts file /// if we change the state tree depth! - uint8 internal constant STATE_TREE_SUBDEPTH = 2; + uint8 public immutable stateTreeDepth; + uint8 internal constant TREE_ARITY = 2; uint8 internal constant MESSAGE_TREE_ARITY = 5; @@ -86,20 +84,11 @@ contract MACI is IMACI, DomainObjs, Params, Utilities, Ownable(msg.sender) { PollContracts pollAddr ); - /// @notice Only allow a Poll contract to call the modified function. - modifier onlyPoll(uint256 _pollId) { - if (msg.sender != address(polls[_pollId])) revert CallerMustBePoll(msg.sender); - _; - } - /// @notice custom errors - error CallerMustBePoll(address _caller); error PoseidonHashLibrariesNotLinked(); error TooManySignups(); error InvalidPubKey(); - error PreviousPollNotCompleted(uint256 pollId); error PollDoesNotExist(uint256 pollId); - error SignupTemporaryBlocked(); /// @notice Create a new instance of the MACI contract. /// @param _pollFactory The PollFactory contract @@ -190,7 +179,7 @@ contract MACI is IMACI, DomainObjs, Params, Utilities, Ownable(msg.sender) { address _verifier, address _vkRegistry, Mode _mode - ) public virtual onlyOwner returns (PollContracts memory pollAddr) { + ) public virtual returns (PollContracts memory pollAddr) { // cache the poll to a local variable so we can increment it uint256 pollId = nextPollId; @@ -210,20 +199,13 @@ contract MACI is IMACI, DomainObjs, Params, Utilities, Ownable(msg.sender) { maxVoteOptions: uint256(MESSAGE_TREE_ARITY) ** _treeDepths.voteOptionTreeDepth }); - address _owner = owner(); + // the owner of the message processor and tally contract will be the msg.sender + address _msgSender = msg.sender; - address p = pollFactory.deploy( - _duration, - maxValues, - _treeDepths, - _coordinatorPubKey, - address(this), - topupCredit, - _owner - ); + address p = pollFactory.deploy(_duration, maxValues, _treeDepths, _coordinatorPubKey, address(this), topupCredit); - address mp = messageProcessorFactory.deploy(_verifier, _vkRegistry, p, _owner, _mode); - address tally = tallyFactory.deploy(_verifier, _vkRegistry, p, mp, _owner, _mode); + address mp = messageProcessorFactory.deploy(_verifier, _vkRegistry, p, _msgSender, _mode); + address tally = tallyFactory.deploy(_verifier, _vkRegistry, p, mp, _msgSender, _mode); polls[pollId] = p; diff --git a/contracts/contracts/MessageProcessor.sol b/contracts/contracts/MessageProcessor.sol index 6a187cd001..455d15047c 100644 --- a/contracts/contracts/MessageProcessor.sol +++ b/contracts/contracts/MessageProcessor.sol @@ -17,13 +17,12 @@ import { DomainObjs } from "./utilities/DomainObjs.sol"; /// @dev MessageProcessor is used to process messages published by signup users. /// It will process message by batch due to large size of messages. /// After it finishes processing, the sbCommitment will be used for Tally and Subsidy contracts. -contract MessageProcessor is Ownable(msg.sender), SnarkCommon, Hasher, CommonUtilities, IMessageProcessor, DomainObjs { +contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMessageProcessor, DomainObjs { /// @notice custom errors error NoMoreMessages(); - error StateAqNotMerged(); + error StateNotMerged(); error MessageAqNotMerged(); error InvalidProcessMessageProof(); - error VkNotSet(); error MaxVoteOptionsTooLarge(); error NumSignUpsTooLarge(); error CurrentMessageBatchIndexTooLarge(); @@ -55,8 +54,15 @@ contract MessageProcessor is Ownable(msg.sender), SnarkCommon, Hasher, CommonUti /// @param _verifier The Verifier contract address /// @param _vkRegistry The VkRegistry contract address /// @param _poll The Poll contract address + /// @param _mpOwner The owner of the MessageProcessor contract /// @param _mode Voting mode - constructor(address _verifier, address _vkRegistry, address _poll, Mode _mode) payable { + constructor( + address _verifier, + address _vkRegistry, + address _poll, + address _mpOwner, + Mode _mode + ) payable Ownable(_mpOwner) { verifier = IVerifier(_verifier); vkRegistry = IVkRegistry(_vkRegistry); poll = IPoll(_poll); @@ -77,8 +83,8 @@ contract MessageProcessor is Ownable(msg.sender), SnarkCommon, Hasher, CommonUti } // The state AccQueue must be merged - if (!poll.stateAqMerged()) { - revert StateAqNotMerged(); + if (!poll.stateMerged()) { + revert StateNotMerged(); } // Retrieve stored vals diff --git a/contracts/contracts/MessageProcessorFactory.sol b/contracts/contracts/MessageProcessorFactory.sol index 89fd994f67..a121da96b2 100644 --- a/contracts/contracts/MessageProcessorFactory.sol +++ b/contracts/contracts/MessageProcessorFactory.sol @@ -18,8 +18,7 @@ contract MessageProcessorFactory is Params, DomainObjs, IMessageProcessorFactory Mode _mode ) public returns (address messageProcessorAddr) { // deploy MessageProcessor for this Poll - MessageProcessor messageProcessor = new MessageProcessor(_verifier, _vkRegistry, _poll, _mode); - messageProcessor.transferOwnership(_owner); + MessageProcessor messageProcessor = new MessageProcessor(_verifier, _vkRegistry, _poll, _owner, _mode); messageProcessorAddr = address(messageProcessor); } } diff --git a/contracts/contracts/Poll.sol b/contracts/contracts/Poll.sol index 49393558bf..dd0bafeb21 100644 --- a/contracts/contracts/Poll.sol +++ b/contracts/contracts/Poll.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import { Params } from "./utilities/Params.sol"; -import { SnarkCommon } from "./crypto/SnarkCommon.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import { Params } from "./utilities/Params.sol"; +import { SnarkCommon } from "./crypto/SnarkCommon.sol"; import { EmptyBallotRoots } from "./trees/EmptyBallotRoots.sol"; import { IPoll } from "./interfaces/IPoll.sol"; import { Utilities } from "./utilities/Utilities.sol"; @@ -16,7 +16,7 @@ import { CurveBabyJubJub } from "./crypto/BabyJubJub.sol"; /// which can be either votes, key change messages or topup messages. /// @dev Do not deploy this directly. Use PollFactory.deploy() which performs some /// checks on the Poll constructor arguments. -contract Poll is Params, Utilities, SnarkCommon, Ownable(msg.sender), EmptyBallotRoots, IPoll { +contract Poll is Params, Utilities, SnarkCommon, EmptyBallotRoots, IPoll { using SafeERC20 for ERC20; /// @notice Whether the Poll has been initialized @@ -38,7 +38,7 @@ contract Poll is Params, Utilities, SnarkCommon, Ownable(msg.sender), EmptyBallo uint256 internal immutable duration; /// @notice Whether the MACI contract's stateAq has been merged by this contract - bool public stateAqMerged; + bool public stateMerged; /// @notice Get the commitment to the state leaves and the ballots. This is /// hash3(stateRoot, ballotRoot, salt). @@ -74,14 +74,12 @@ contract Poll is Params, Utilities, SnarkCommon, Ownable(msg.sender), EmptyBallo error PollAlreadyInit(); error TooManyMessages(); error InvalidPubKey(); - error StateAqAlreadyMerged(); - error StateAqSubtreesNeedMerge(); + error StateAlreadyMerged(); error InvalidBatchLength(); event PublishMessage(Message _message, PubKey _encPubKey); event TopupMessage(Message _message); - event MergeMaciStateAqSubRoots(uint256 indexed _numSrQueueOps); - event MergeMaciStateAq(uint256 indexed _stateRoot, uint256 indexed _numSignups); + event MergeMaciState(uint256 indexed _stateRoot, uint256 indexed _numSignups); event MergeMessageAqSubRoots(uint256 indexed _numSrQueueOps); event MergeMessageAq(uint256 indexed _messageRoot); @@ -229,13 +227,13 @@ contract Poll is Params, Utilities, SnarkCommon, Ownable(msg.sender), EmptyBallo } /// @inheritdoc IPoll - function mergeMaciStateAq() public onlyOwner isAfterVotingDeadline { + function mergeMaciState() public isAfterVotingDeadline { // This function can only be called once per Poll after the voting // deadline - if (stateAqMerged) revert StateAqAlreadyMerged(); + if (stateMerged) revert StateAlreadyMerged(); // set merged to true so it cannot be called again - stateAqMerged = true; + stateMerged = true; mergedStateRoot = extContracts.maci.getStateTreeRoot(); @@ -259,17 +257,17 @@ contract Poll is Params, Utilities, SnarkCommon, Ownable(msg.sender), EmptyBallo actualStateTreeDepth = depth; - emit MergeMaciStateAq(mergedStateRoot, numSignups); + emit MergeMaciState(mergedStateRoot, numSignups); } /// @inheritdoc IPoll - function mergeMessageAqSubRoots(uint256 _numSrQueueOps) public onlyOwner isAfterVotingDeadline { + function mergeMessageAqSubRoots(uint256 _numSrQueueOps) public isAfterVotingDeadline { extContracts.messageAq.mergeSubRoots(_numSrQueueOps); emit MergeMessageAqSubRoots(_numSrQueueOps); } /// @inheritdoc IPoll - function mergeMessageAq() public onlyOwner isAfterVotingDeadline { + function mergeMessageAq() public isAfterVotingDeadline { uint256 root = extContracts.messageAq.merge(treeDepths.messageTreeDepth); emit MergeMessageAq(root); } diff --git a/contracts/contracts/PollFactory.sol b/contracts/contracts/PollFactory.sol index 5cff4bd914..787fc2df8b 100644 --- a/contracts/contracts/PollFactory.sol +++ b/contracts/contracts/PollFactory.sol @@ -31,8 +31,7 @@ contract PollFactory is Params, DomainObjs, IPollFactory { TreeDepths calldata _treeDepths, PubKey calldata _coordinatorPubKey, address _maci, - TopupCredit _topupCredit, - address _pollOwner + TopupCredit _topupCredit ) public virtual returns (address pollAddr) { /// @notice Validate _maxValues /// maxVoteOptions must be less than 2 ** 50 due to circuit limitations; @@ -62,8 +61,6 @@ contract PollFactory is Params, DomainObjs, IPollFactory { // init Poll poll.init(); - poll.transferOwnership(_pollOwner); - pollAddr = address(poll); } } diff --git a/contracts/contracts/Tally.sol b/contracts/contracts/Tally.sol index e3ca1d658f..c2acc7cd39 100644 --- a/contracts/contracts/Tally.sol +++ b/contracts/contracts/Tally.sol @@ -15,7 +15,7 @@ import { DomainObjs } from "./utilities/DomainObjs.sol"; /// @title Tally /// @notice The Tally contract is used during votes tallying /// and by users to verify the tally results. -contract Tally is Ownable(msg.sender), SnarkCommon, CommonUtilities, Hasher, DomainObjs { +contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher, DomainObjs { uint256 internal constant TREE_ARITY = 2; uint256 internal constant VOTE_OPTION_TREE_ARITY = 5; @@ -65,7 +65,16 @@ contract Tally is Ownable(msg.sender), SnarkCommon, CommonUtilities, Hasher, Dom /// @param _vkRegistry The VkRegistry contract /// @param _poll The Poll contract /// @param _mp The MessageProcessor contract - constructor(address _verifier, address _vkRegistry, address _poll, address _mp, Mode _mode) payable { + /// @param _tallyOwner The owner of the Tally contract + /// @param _mode The mode of the poll + constructor( + address _verifier, + address _vkRegistry, + address _poll, + address _mp, + address _tallyOwner, + Mode _mode + ) payable Ownable(_tallyOwner) { verifier = IVerifier(_verifier); vkRegistry = IVkRegistry(_vkRegistry); poll = IPoll(_poll); diff --git a/contracts/contracts/TallyFactory.sol b/contracts/contracts/TallyFactory.sol index dcb501c14b..72d00d43c7 100644 --- a/contracts/contracts/TallyFactory.sol +++ b/contracts/contracts/TallyFactory.sol @@ -18,8 +18,7 @@ contract TallyFactory is ITallyFactory, DomainObjs { Mode _mode ) public virtual returns (address tallyAddr) { // deploy Tally for this Poll - Tally tally = new Tally(_verifier, _vkRegistry, _poll, _messageProcessor, _mode); - tally.transferOwnership(_owner); + Tally tally = new Tally(_verifier, _vkRegistry, _poll, _messageProcessor, _owner, _mode); tallyAddr = address(tally); } } diff --git a/contracts/contracts/interfaces/IPoll.sol b/contracts/contracts/interfaces/IPoll.sol index 919eea19e5..6a9635df65 100644 --- a/contracts/contracts/interfaces/IPoll.sol +++ b/contracts/contracts/interfaces/IPoll.sol @@ -30,7 +30,7 @@ interface IPoll { /// @notice The second step of merging the MACI state AccQueue. This allows the /// ProcessMessages circuit to access the latest state tree and ballots via /// currentSbCommitment. - function mergeMaciStateAq() external; + function mergeMaciState() external; /// @notice The first step in merging the message AccQueue so that the /// ProcessMessages circuit can access the message root. @@ -48,7 +48,7 @@ interface IPoll { /// @notice Get the result of whether the MACI contract's stateAq has been merged by this contract /// @return Whether the MACI contract's stateAq has been merged by this contract - function stateAqMerged() external view returns (bool); + function stateMerged() external view returns (bool); /// @notice Get the depths of the merkle trees /// @return intStateTreeDepth The depth of the state tree diff --git a/contracts/contracts/interfaces/IPollFactory.sol b/contracts/contracts/interfaces/IPollFactory.sol index ee903f3a8e..c22c340244 100644 --- a/contracts/contracts/interfaces/IPollFactory.sol +++ b/contracts/contracts/interfaces/IPollFactory.sol @@ -15,7 +15,6 @@ interface IPollFactory { /// @param _coordinatorPubKey The coordinator's public key /// @param _maci The MACI contract interface reference /// @param _topupCredit The TopupCredit contract - /// @param _pollOwner The owner of the poll /// @return The deployed Poll contract function deploy( uint256 _duration, @@ -23,7 +22,6 @@ interface IPollFactory { Params.TreeDepths memory _treeDepths, DomainObjs.PubKey memory _coordinatorPubKey, address _maci, - TopupCredit _topupCredit, - address _pollOwner + TopupCredit _topupCredit ) external returns (address); } diff --git a/contracts/tasks/helpers/ProofGenerator.ts b/contracts/tasks/helpers/ProofGenerator.ts index f49768a985..97bdf9569b 100644 --- a/contracts/tasks/helpers/ProofGenerator.ts +++ b/contracts/tasks/helpers/ProofGenerator.ts @@ -113,7 +113,7 @@ export class ProofGenerator { .queryFilter(pollContract.filters.MergeMessageAq(messageRoot), fromBlock) .then((events) => events[events.length - 1]?.blockNumber), pollContract - .queryFilter(pollContract.filters.MergeMaciStateAq(stateRoot, numSignups), fromBlock) + .queryFilter(pollContract.filters.MergeMaciState(stateRoot, numSignups), fromBlock) .then((events) => events[events.length - 1]?.blockNumber), ]).then((blocks) => Math.max(...blocks)); diff --git a/contracts/tasks/helpers/TreeMerger.ts b/contracts/tasks/helpers/TreeMerger.ts index 57943abb43..a28105751d 100644 --- a/contracts/tasks/helpers/TreeMerger.ts +++ b/contracts/tasks/helpers/TreeMerger.ts @@ -34,18 +34,6 @@ export class TreeMerger { this.deployer = deployer; } - /** - * Check if signer is an owner. Otherwise, throw an error. - */ - async checkPollOwner(): Promise { - const pollOwner = await this.pollContract.owner(); - const deployerAddress = await this.deployer.getAddress(); - - if (pollOwner.toLowerCase() !== deployerAddress.toLowerCase()) { - throw new Error("The signer is not the owner of this Poll contract"); - } - } - /** * Check if voting period is over. Otherwise, throw an error. */ @@ -70,10 +58,10 @@ export class TreeMerger { */ async mergeSignups(): Promise { // check if the state tree has been fully merged - if (!(await this.pollContract.stateAqMerged())) { + if (!(await this.pollContract.stateMerged())) { // go and merge the state tree console.log("Merging subroots to a main state root..."); - const receipt = await this.pollContract.mergeMaciStateAq().then((tx) => tx.wait()); + const receipt = await this.pollContract.mergeMaciState().then((tx) => tx.wait()); if (receipt?.status !== 1) { throw new Error("Error merging signup state subroots"); diff --git a/contracts/tasks/runner/merge.ts b/contracts/tasks/runner/merge.ts index 5d0188a2d4..8b117beced 100644 --- a/contracts/tasks/runner/merge.ts +++ b/contracts/tasks/runner/merge.ts @@ -49,7 +49,6 @@ task("merge", "Merge signups and messages") console.log("Start balance: ", Number(startBalance / 10n ** 12n) / 1e6); - await treeMerger.checkPollOwner(); await treeMerger.checkPollDuration(); await treeMerger.mergeSignups(); diff --git a/contracts/tasks/runner/prove.ts b/contracts/tasks/runner/prove.ts index 081e7a2217..826d5c9488 100644 --- a/contracts/tasks/runner/prove.ts +++ b/contracts/tasks/runner/prove.ts @@ -95,7 +95,7 @@ task("prove", "Command to generate proof and prove the result of a poll on-chain name: EContracts.AccQueue, address: messageAqContractAddress, }); - const isStateAqMerged = await pollContract.stateAqMerged(); + const isStateAqMerged = await pollContract.stateMerged(); // Check that the state and message trees have been merged for at least the first poll if (!isStateAqMerged && poll.toString() === "0") { diff --git a/contracts/tests/MACI.test.ts b/contracts/tests/MACI.test.ts index a575af2f09..6d7b0168b6 100644 --- a/contracts/tests/MACI.test.ts +++ b/contracts/tests/MACI.test.ts @@ -7,7 +7,7 @@ import { NOTHING_UP_MY_SLEEVE } from "maci-crypto"; import { Keypair, PubKey, Message } from "maci-domainobjs"; import { EMode } from "../ts/constants"; -import { getDefaultSigner } from "../ts/utils"; +import { getDefaultSigner, getSigners } from "../ts/utils"; import { MACI, Poll as PollContract, Poll__factory as PollFactory, Verifier, VkRegistry } from "../typechain-types"; import { @@ -162,7 +162,7 @@ describe("MACI", function test() { ).to.be.revertedWithCustomError(maciContract, "InvalidPubKey"); }); - it("should not allow to sign up more than the supported amount of users (5 ** stateTreeDepth)", async () => { + it("should not allow to sign up more than the supported amount of users (2 ** stateTreeDepth)", async () => { const stateTreeDepthTest = 1; const maxUsers = 2 ** stateTreeDepthTest; const maci = (await deployTestContracts(initialVoiceCreditBalance, stateTreeDepthTest, signer, true)) @@ -259,6 +259,39 @@ describe("MACI", function test() { ]); maciState.polls.get(pollId)?.publishMessage(message, padKey); }); + + it("should allow to deploy a new poll even before the first one is completed", async () => { + const tx = await maciContract.deployPoll( + duration, + treeDepths, + coordinator.pubKey.asContractParam() as { x: BigNumberish; y: BigNumberish }, + verifierContract, + vkRegistryContract, + EMode.QV, + { gasLimit: 10000000 }, + ); + const receipt = await tx.wait(); + expect(receipt?.status).to.eq(1); + expect(await maciContract.nextPollId()).to.eq(2); + }); + + it("should allow any user to deploy a poll", async () => { + const [, user] = await getSigners(); + const tx = await maciContract + .connect(user) + .deployPoll( + duration, + treeDepths, + users[0].pubKey.asContractParam() as { x: BigNumberish; y: BigNumberish }, + verifierContract, + vkRegistryContract, + EMode.QV, + { gasLimit: 10000000 }, + ); + const receipt = await tx.wait(); + expect(receipt?.status).to.eq(1); + expect(await maciContract.nextPollId()).to.eq(3); + }); }); describe("Merge sign-ups", () => { @@ -269,17 +302,17 @@ describe("MACI", function test() { pollContract = PollFactory.connect(pollContractAddress, signer); }); - it("should allow a Poll contract to merge the signUp AccQueue", async () => { + it("should allow a Poll contract to merge the state tree (calculate the state root)", async () => { await timeTravel(signer.provider as unknown as EthereumProvider, Number(duration) + 1); - const tx = await pollContract.mergeMaciStateAq({ + const tx = await pollContract.mergeMaciState({ gasLimit: 3000000, }); const receipt = await tx.wait(); expect(receipt?.status).to.eq(1); }); - it("should have the correct state root on chain after merging the acc queue", async () => { + it("should have the correct state root on chain after calculating the root on chain", async () => { maciState.polls.get(pollId)?.updatePoll(await pollContract.numSignups()); expect(await maciContract.getStateTreeRoot()).to.eq(maciState.polls.get(pollId)?.stateTree?.root.toString()); }); @@ -289,7 +322,7 @@ describe("MACI", function test() { expect(onChainStateRoot.toString()).to.eq(maciState.polls.get(pollId)?.stateTree?.root.toString()); }); - it("should allow a user to signup after the signUp AccQueue was merged", async () => { + it("should allow a user to signup after the state tree root was calculated", async () => { const tx = await maciContract.signUp( users[0].pubKey.asContractParam(), AbiCoder.defaultAbiCoder().encode(["uint256"], [1]), diff --git a/contracts/tests/MessageProcessor.test.ts b/contracts/tests/MessageProcessor.test.ts index 502ec550ae..9c63e6c686 100644 --- a/contracts/tests/MessageProcessor.test.ts +++ b/contracts/tests/MessageProcessor.test.ts @@ -146,14 +146,14 @@ describe("MessageProcessor", () => { it("processMessages() should fail if the state AQ has not been merged", async () => { await expect(mpContract.processMessages(0, [0, 0, 0, 0, 0, 0, 0, 0])).to.be.revertedWithCustomError( mpContract, - "StateAqNotMerged", + "StateNotMerged", ); }); }); describe("after merging acc queues", () => { before(async () => { - await pollContract.mergeMaciStateAq(); + await pollContract.mergeMaciState(); await pollContract.mergeMessageAqSubRoots(0); await pollContract.mergeMessageAq(); diff --git a/contracts/tests/Poll.test.ts b/contracts/tests/Poll.test.ts index df96a69103..f75bfd3e2a 100644 --- a/contracts/tests/Poll.test.ts +++ b/contracts/tests/Poll.test.ts @@ -323,7 +323,7 @@ describe("Poll", () => { messageAqContract = AccQueueQuinaryMaciFactory.connect(messageAqAddress, signer); }); - it("should allow the coordinator to merge the message AccQueue", async () => { + it("should allow to merge the message AccQueue", async () => { let tx = await pollContract.mergeMessageAqSubRoots(0, { gasLimit: 3000000, }); @@ -341,5 +341,26 @@ describe("Poll", () => { expect(onChainMessageRoot.toString()).to.eq(offChainMessageRoot.toString()); }); + + it("should prevent merging subroots again", async () => { + await expect(pollContract.mergeMessageAqSubRoots(0)).to.be.revertedWithCustomError( + messageAqContract, + "SubTreesAlreadyMerged", + ); + }); + + it("should not change the message root if merging a second time", async () => { + await pollContract.mergeMessageAq(); + const onChainMessageRoot = await messageAqContract.getMainRoot(MESSAGE_TREE_DEPTH); + const offChainMessageRoot = maciState.polls.get(pollId)!.messageTree.root; + + expect(onChainMessageRoot.toString()).to.eq(offChainMessageRoot.toString()); + }); + + it("should emit an event with the same root when merging another time", async () => { + expect(await pollContract.mergeMessageAq()) + .to.emit(pollContract, "MergeMessageAq") + .withArgs(maciState.polls.get(pollId)!.messageTree.root); + }); }); }); diff --git a/contracts/tests/PollFactory.test.ts b/contracts/tests/PollFactory.test.ts index 77e361eedf..59fb6956bd 100644 --- a/contracts/tests/PollFactory.test.ts +++ b/contracts/tests/PollFactory.test.ts @@ -27,7 +27,6 @@ describe("pollFactory", () => { coordinatorPubKey.asContractParam(), ZeroAddress, ZeroAddress, - await signer.getAddress(), ); const receipt = await tx.wait(); expect(receipt?.status).to.eq(1); @@ -45,7 +44,6 @@ describe("pollFactory", () => { coordinatorPubKey.asContractParam(), ZeroAddress, ZeroAddress, - await signer.getAddress(), ), ).to.be.revertedWithCustomError(pollFactory, "InvalidMaxValues"); }); diff --git a/contracts/tests/Tally.test.ts b/contracts/tests/Tally.test.ts index bb821c0322..5bd0e9dc08 100644 --- a/contracts/tests/Tally.test.ts +++ b/contracts/tests/Tally.test.ts @@ -183,7 +183,7 @@ describe("TallyVotes", () => { describe("after merging acc queues", () => { let tallyGeneratedInputs: ITallyCircuitInputs; before(async () => { - await pollContract.mergeMaciStateAq(); + await pollContract.mergeMaciState(); await pollContract.mergeMessageAqSubRoots(0); await pollContract.mergeMessageAq(); @@ -337,7 +337,7 @@ describe("TallyVotes", () => { ); await timeTravel(signer.provider! as unknown as EthereumProvider, updatedDuration); - await pollContract.mergeMaciStateAq(); + await pollContract.mergeMaciState(); await pollContract.mergeMessageAqSubRoots(0); await pollContract.mergeMessageAq(); @@ -482,7 +482,7 @@ describe("TallyVotes", () => { ); await timeTravel(signer.provider! as unknown as EthereumProvider, updatedDuration); - await pollContract.mergeMaciStateAq(); + await pollContract.mergeMaciState(); await pollContract.mergeMessageAqSubRoots(0); await pollContract.mergeMessageAq(); diff --git a/contracts/tests/TallyNonQv.test.ts b/contracts/tests/TallyNonQv.test.ts index 7c45850c30..c2ad07c7f7 100644 --- a/contracts/tests/TallyNonQv.test.ts +++ b/contracts/tests/TallyNonQv.test.ts @@ -182,7 +182,7 @@ describe("TallyVotesNonQv", () => { describe("after merging acc queues", () => { let tallyGeneratedInputs: ITallyCircuitInputs; before(async () => { - await pollContract.mergeMaciStateAq(); + await pollContract.mergeMaciState(); await pollContract.mergeMessageAqSubRoots(0); await pollContract.mergeMessageAq(); diff --git a/coordinator/ts/proof/__tests__/proof.service.test.ts b/coordinator/ts/proof/__tests__/proof.service.test.ts index ccf139d378..68c3c26abe 100644 --- a/coordinator/ts/proof/__tests__/proof.service.test.ts +++ b/coordinator/ts/proof/__tests__/proof.service.test.ts @@ -47,7 +47,7 @@ describe("ProofGeneratorService", () => { getMainRoot: jest.fn(), treeDepths: jest.fn(), extContracts: jest.fn(), - stateAqMerged: jest.fn(), + stateMerged: jest.fn(), }; let defaultProofGenerator = { @@ -71,7 +71,7 @@ describe("ProofGeneratorService", () => { getMainRoot: jest.fn(() => Promise.resolve(1n)), treeDepths: jest.fn(() => Promise.resolve([1, 2, 3])), extContracts: jest.fn(() => Promise.resolve({ messageAq: ZeroAddress })), - stateAqMerged: jest.fn(() => Promise.resolve(true)), + stateMerged: jest.fn(() => Promise.resolve(true)), }; defaultProofGenerator = { @@ -103,7 +103,7 @@ describe("ProofGeneratorService", () => { }); test("should throw error if state is not merged yet", async () => { - mockContract.stateAqMerged.mockResolvedValue(false); + mockContract.stateMerged.mockResolvedValue(false); const service = new ProofGeneratorService(); diff --git a/coordinator/ts/proof/proof.service.ts b/coordinator/ts/proof/proof.service.ts index 5e9496f3a5..7e574a120c 100644 --- a/coordinator/ts/proof/proof.service.ts +++ b/coordinator/ts/proof/proof.service.ts @@ -64,7 +64,7 @@ export class ProofGeneratorService { address: messageAqAddress, }); - const isStateAqMerged = await pollContract.stateAqMerged(); + const isStateAqMerged = await pollContract.stateMerged(); if (!isStateAqMerged) { throw new Error("The state tree has not been merged yet. Please use the mergeSignups subcommmand to do so."); diff --git a/package.json b/package.json index ca24c0b48c..fa4100493c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maci", - "version": "1.2.0", + "version": "1.2.1", "description": "Minimal Anti-Collusion Infrastructure", "repository": "https://github.com/privacy-scaling-explorations/maci", "license": "MIT",