diff --git a/packages/hardhat/constants.ts b/packages/hardhat/constants.ts index ee518c7..05dfc4a 100644 --- a/packages/hardhat/constants.ts +++ b/packages/hardhat/constants.ts @@ -12,6 +12,6 @@ export const intStateTreeDepth = 1; export const messageTreeDepth = 2; export const voteOptionTreeDepth = 2; export const messageBatchDepth = 1; -export const processMessagesZkeyPath = "./zkeys/ProcessMessages_6-9-2-3/processMessages_6-9-2-3.zkey"; -export const tallyVotesZkeyPath = "./zkeys/TallyVotes_6-2-3/tallyVotes_6-2-3.zkey"; -export const subsidyZkeyPath: string | null = null; +export const processMessagesZkeyPath = "./zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test.0.zkey"; +export const tallyVotesZkeyPath = "./zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test.0.zkey"; +export const useQuadraticVoting = false; diff --git a/packages/hardhat/contracts/PollManager.sol b/packages/hardhat/contracts/PollManager.sol index b121c15..ce24663 100644 --- a/packages/hardhat/contracts/PollManager.sol +++ b/packages/hardhat/contracts/PollManager.sol @@ -15,10 +15,11 @@ contract PollManager is Params, DomainObjs { struct PollData { uint256 id; + uint256 maciPollId; string name; bytes encodedOptions; string ipfsHash; - PollContracts pollContracts; + MACI.PollContracts pollContracts; uint256 startTime; uint256 endTime; uint256 numOfOptions; @@ -36,11 +37,13 @@ contract PollManager is Params, DomainObjs { address public verifier; address public vkRegistry; bool public useSubsidy; + bool public isQv; event PollCreated( uint256 indexed pollId, + uint256 indexed maciPollId, address indexed creator, - PollContracts pollContracts, + MACI.PollContracts pollContracts, string name, string[] options, string ipfsHash, @@ -53,8 +56,9 @@ contract PollManager is Params, DomainObjs { _; } - constructor(MACI _maci) { + constructor(MACI _maci, bool _isQv) { maci = _maci; + isQv = _isQv; } function owner() public view returns (address) { @@ -84,13 +88,13 @@ contract PollManager is Params, DomainObjs { // TODO: check if the number of options are more than limit // deploy the poll contracts - MACI.PollContracts memory c = maci.deployPoll( + MACI.PollContracts memory pollContracts = maci.deployPoll( _duration, treeDepths, coordinatorPubKey, verifier, vkRegistry, - useSubsidy + isQv ); // encode options to bytes for retrieval @@ -99,18 +103,13 @@ contract PollManager is Params, DomainObjs { uint256 endTime = block.timestamp + _duration; uint256 pollId = ++totalPolls; - PollContracts memory pollContracts = PollContracts({ - poll: c.poll, - messageProcessor: c.messageProcessor, - tally: c.tally, - subsidy: c.subsidy - }); - - pollIdByAddress[c.poll] = pollId; + pollIdByAddress[pollContracts.poll] = pollId; + uint256 maciPollId = maci.getPollId(pollContracts.poll); // create poll polls[pollId] = PollData({ id: pollId, + maciPollId: maciPollId, name: _name, encodedOptions: encodedOptions, numOfOptions: _options.length, @@ -123,6 +122,7 @@ contract PollManager is Params, DomainObjs { emit PollCreated( pollId, + maciPollId, msg.sender, pollContracts, _name, diff --git a/packages/hardhat/contracts/maci-contracts/MACI.sol b/packages/hardhat/contracts/maci-contracts/MACI.sol index 8fea372..5156f49 100644 --- a/packages/hardhat/contracts/maci-contracts/MACI.sol +++ b/packages/hardhat/contracts/maci-contracts/MACI.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.10; import { IPollFactory } from "./interfaces/IPollFactory.sol"; import { IMessageProcessorFactory } from "./interfaces/IMPFactory.sol"; -import { ITallySubsidyFactory } from "./interfaces/ITallySubsidyFactory.sol"; +import { ITallyFactory } from "./interfaces/ITallyFactory.sol"; import { InitialVoiceCreditProxy } from "./initialVoiceCreditProxy/InitialVoiceCreditProxy.sol"; import { SignUpGatekeeper } from "./gatekeepers/SignUpGatekeeper.sol"; import { AccQueue } from "./trees/AccQueue.sol"; @@ -39,6 +39,7 @@ contract MACI is IMACI, Params, Utilities, Ownable { /// @notice A mapping of poll IDs to Poll contracts. mapping(uint256 => address) public polls; + mapping(address => uint256) public pollIds; /// @notice Whether the subtrees have been merged (can merge root before new signup) bool public subtreesMerged; @@ -56,10 +57,7 @@ contract MACI is IMACI, Params, Utilities, Ownable { IMessageProcessorFactory public immutable messageProcessorFactory; /// @notice Factory contract that deploy a Tally contract - ITallySubsidyFactory public immutable tallyFactory; - - /// @notice Factory contract that deploy a Subsidy contract - ITallySubsidyFactory public immutable subsidyFactory; + ITallyFactory public immutable tallyFactory; /// @notice The state AccQueue. Represents a mapping between each user's public key /// and their voice credit balance. @@ -83,7 +81,6 @@ contract MACI is IMACI, Params, Utilities, Ownable { address poll; address messageProcessor; address tally; - address subsidy; } // Events @@ -115,6 +112,7 @@ contract MACI is IMACI, Params, Utilities, Ownable { error MaciPubKeyLargerThanSnarkFieldSize(); error PreviousPollNotCompleted(uint256 pollId); error PollDoesNotExist(uint256 pollId); + error PollAddressDoesNotExist(address pollAddr); error SignupTemporaryBlocked(); error PubKeyAlreadyRegistered(); @@ -122,7 +120,6 @@ contract MACI is IMACI, Params, Utilities, Ownable { /// @param _pollFactory The PollFactory contract /// @param _messageProcessorFactory The MessageProcessorFactory contract /// @param _tallyFactory The TallyFactory contract - /// @param _subsidyFactory The SubsidyFactory contract /// @param _signUpGatekeeper The SignUpGatekeeper contract /// @param _initialVoiceCreditProxy The InitialVoiceCreditProxy contract /// @param _topupCredit The TopupCredit contract @@ -130,8 +127,7 @@ contract MACI is IMACI, Params, Utilities, Ownable { constructor( IPollFactory _pollFactory, IMessageProcessorFactory _messageProcessorFactory, - ITallySubsidyFactory _tallyFactory, - ITallySubsidyFactory _subsidyFactory, + ITallyFactory _tallyFactory, SignUpGatekeeper _signUpGatekeeper, InitialVoiceCreditProxy _initialVoiceCreditProxy, TopupCredit _topupCredit, @@ -150,7 +146,6 @@ contract MACI is IMACI, Params, Utilities, Ownable { pollFactory = _pollFactory; messageProcessorFactory = _messageProcessorFactory; tallyFactory = _tallyFactory; - subsidyFactory = _subsidyFactory; topupCredit = _topupCredit; signUpGatekeeper = _signUpGatekeeper; initialVoiceCreditProxy = _initialVoiceCreditProxy; @@ -245,7 +240,7 @@ 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 useSubsidy If true, the Poll will use the Subsidy contract + /// @param _isQv Whether to support QV or not /// @return pollAddr a new Poll contract address function deployPoll( uint256 _duration, @@ -253,8 +248,8 @@ contract MACI is IMACI, Params, Utilities, Ownable { PubKey memory _coordinatorPubKey, address _verifier, address _vkRegistry, - bool useSubsidy - ) public virtual onlyManager returns (PollContracts memory pollAddr) { + bool _isQv + ) public virtual onlyOwner returns (PollContracts memory pollAddr) { // cache the poll to a local variable so we can increment it uint256 pollId = nextPollId; @@ -290,35 +285,26 @@ contract MACI is IMACI, Params, Utilities, Ownable { _verifier, _vkRegistry, p, - _owner + _owner, + _isQv ); address tally = tallyFactory.deploy( _verifier, _vkRegistry, p, mp, - _owner + _owner, + _isQv ); - address subsidy; - if (useSubsidy) { - subsidy = subsidyFactory.deploy( - _verifier, - _vkRegistry, - p, - mp, - _owner - ); - } - polls[pollId] = p; + pollIds[p] = pollId; // store the addresses in a struct so they can be returned pollAddr = PollContracts({ poll: p, messageProcessor: mp, - tally: tally, - subsidy: subsidy + tally: tally }); emit DeployPoll( @@ -364,4 +350,9 @@ contract MACI is IMACI, Params, Utilities, Ownable { if (_pollId >= nextPollId) revert PollDoesNotExist(_pollId); poll = polls[_pollId]; } + + function getPollId(address _poll) public view returns (uint256 pollId) { + if (pollIds[_poll] >= nextPollId) revert PollAddressDoesNotExist(_poll); + pollId = pollIds[_poll]; + } } diff --git a/packages/hardhat/contracts/maci-contracts/MessageProcessor.sol b/packages/hardhat/contracts/maci-contracts/MessageProcessor.sol index 2ecbf03..1d895a7 100644 --- a/packages/hardhat/contracts/maci-contracts/MessageProcessor.sol +++ b/packages/hardhat/contracts/maci-contracts/MessageProcessor.sol @@ -48,15 +48,18 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes IPoll public immutable poll; IVerifier public immutable verifier; IVkRegistry public immutable vkRegistry; + bool public immutable isQv; /// @notice Create a new instance /// @param _verifier The Verifier contract address /// @param _vkRegistry The VkRegistry contract address /// @param _poll The Poll contract address - constructor(address _verifier, address _vkRegistry, address _poll) payable { + /// @param _isQv Whether to support QV or not + constructor(address _verifier, address _vkRegistry, address _poll, bool _isQv) payable { verifier = IVerifier(_verifier); vkRegistry = IVkRegistry(_vkRegistry); poll = IPoll(_poll); + isQv = _isQv; } /// @notice Update the Poll's currentSbCommitment if the proof is valid. @@ -180,11 +183,13 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes ); // Get the verifying key from the VkRegistry + IVkRegistry.Mode mode = isQv ? IVkRegistry.Mode.QV : IVkRegistry.Mode.NON_QV; VerifyingKey memory vk = vkRegistry.getProcessVk( maci.stateTreeDepth(), _messageTreeDepth, _voteOptionTreeDepth, - TREE_ARITY ** _messageTreeSubDepth + TREE_ARITY ** _messageTreeSubDepth, + mode ); isValid = verifier.verify(_proof, vk, publicInputHash); diff --git a/packages/hardhat/contracts/maci-contracts/MessageProcessorFactory.sol b/packages/hardhat/contracts/maci-contracts/MessageProcessorFactory.sol index b9fe35b..b08d9c4 100644 --- a/packages/hardhat/contracts/maci-contracts/MessageProcessorFactory.sol +++ b/packages/hardhat/contracts/maci-contracts/MessageProcessorFactory.sol @@ -14,10 +14,11 @@ contract MessageProcessorFactory is Params, DomainObjs, IMessageProcessorFactory address _verifier, address _vkRegistry, address _poll, - address _owner + address _owner, + bool _isQv ) public returns (address messageProcessorAddr) { // deploy MessageProcessor for this Poll - MessageProcessor messageProcessor = new MessageProcessor(_verifier, _vkRegistry, _poll); + MessageProcessor messageProcessor = new MessageProcessor(_verifier, _vkRegistry, _poll, _isQv); messageProcessor.transferOwnership(_owner); messageProcessorAddr = address(messageProcessor); } diff --git a/packages/hardhat/contracts/maci-contracts/Poll.sol b/packages/hardhat/contracts/maci-contracts/Poll.sol index 0adee8c..4af1cdc 100644 --- a/packages/hardhat/contracts/maci-contracts/Poll.sol +++ b/packages/hardhat/contracts/maci-contracts/Poll.sol @@ -75,10 +75,10 @@ contract Poll is Params, Utilities, SnarkCommon, Ownable, EmptyBallotRoots, IPol event PublishMessage(Message _message, PubKey _encPubKey); event TopupMessage(Message _message); - event MergeMaciStateAqSubRoots(uint256 _numSrQueueOps); - event MergeMaciStateAq(uint256 _stateRoot, uint256 _numSignups); - event MergeMessageAqSubRoots(uint256 _numSrQueueOps); - event MergeMessageAq(uint256 _messageRoot); + event MergeMaciStateAqSubRoots(uint256 indexed _numSrQueueOps); + event MergeMaciStateAq(uint256 indexed _stateRoot, uint256 indexed _numSignups); + event MergeMessageAqSubRoots(uint256 indexed _numSrQueueOps); + event MergeMessageAq(uint256 indexed _messageRoot); /// @notice Each MACI instance can have multiple Polls. /// When a Poll is deployed, its voting period starts immediately. diff --git a/packages/hardhat/contracts/maci-contracts/Subsidy.sol b/packages/hardhat/contracts/maci-contracts/Subsidy.sol deleted file mode 100644 index 7a68ad1..0000000 --- a/packages/hardhat/contracts/maci-contracts/Subsidy.sol +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import { IMACI } from "./interfaces/IMACI.sol"; -import { IMessageProcessor } from "./interfaces/IMessageProcessor.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { IPoll } from "./interfaces/IPoll.sol"; -import { SnarkCommon } from "./crypto/SnarkCommon.sol"; -import { Hasher } from "./crypto/Hasher.sol"; -import { CommonUtilities } from "./utilities/CommonUtilities.sol"; -import { IVerifier } from "./interfaces/IVerifier.sol"; -import { IVkRegistry } from "./interfaces/IVkRegistry.sol"; - -/// @title Subsidy -/// @notice This contract is used to verify that the subsidy calculations -/// are correct. It is also used to update the subsidy commitment if the -/// proof is valid. -contract Subsidy is Ownable, CommonUtilities, Hasher, SnarkCommon { - // row batch index - uint256 public rbi; - // column batch index - uint256 public cbi; - - // The final commitment to the state and ballot roots - uint256 public sbCommitment; - uint256 public subsidyCommitment; - - uint256 private constant TREE_ARITY = 5; - - IVerifier public immutable verifier; - IVkRegistry public immutable vkRegistry; - IPoll public immutable poll; - IMessageProcessor public immutable mp; - - // Custom errors - error ProcessingNotComplete(); - error InvalidSubsidyProof(); - error AllSubsidyCalculated(); - error VkNotSet(); - error NumSignUpsTooLarge(); - error RbiTooLarge(); - error CbiTooLarge(); - - /// @notice Create a new Subsidy 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); - mp = IMessageProcessor(_mp); - } - - /// @notice Update the currentSbCommitment if the proof is valid. - /// @dev currentSbCommitment is the commitment to the state and ballot roots - function updateSbCommitment() public onlyOwner { - // Require that all messages have been processed - if (!mp.processingComplete()) { - revert ProcessingNotComplete(); - } - - // only update it once - if (sbCommitment == 0) { - sbCommitment = mp.sbCommitment(); - } - } - - /// @notice Generate the packed values for the subsidy proof - /// @param _numSignUps The number of signups - /// @return result The packed values - function genSubsidyPackedVals(uint256 _numSignUps) public view returns (uint256 result) { - if (_numSignUps >= 2 ** 50) revert NumSignUpsTooLarge(); - if (rbi >= 2 ** 50) revert RbiTooLarge(); - if (cbi >= 2 ** 50) revert CbiTooLarge(); - result = (_numSignUps << 100) + (rbi << 50) + cbi; - } - - /// @notice Generate the public input hash for the subsidy proof - /// @param _numSignUps The number of signups - /// @param _newSubsidyCommitment The new subsidy commitment - /// @return inputHash The public input hash - function genSubsidyPublicInputHash( - uint256 _numSignUps, - uint256 _newSubsidyCommitment - ) public view returns (uint256 inputHash) { - uint256 packedVals = genSubsidyPackedVals(_numSignUps); - uint256[] memory input = new uint256[](4); - input[0] = packedVals; - input[1] = sbCommitment; - input[2] = subsidyCommitment; - input[3] = _newSubsidyCommitment; - inputHash = sha256Hash(input); - } - - /// @notice Update the subsidy commitment if the proof is valid - /// @param _newSubsidyCommitment The new subsidy commitment - /// @param _proof The proof - function updateSubsidy(uint256 _newSubsidyCommitment, uint256[8] calldata _proof) external onlyOwner { - _votingPeriodOver(poll); - updateSbCommitment(); - - (uint8 intStateTreeDepth, , , ) = poll.treeDepths(); - - uint256 subsidyBatchSize = TREE_ARITY ** intStateTreeDepth; - - (uint256 numSignUps, ) = poll.numSignUpsAndMessages(); - - // Require that there are unfinished ballots left - if (rbi * subsidyBatchSize > numSignUps) { - revert AllSubsidyCalculated(); - } - - bool isValid = verifySubsidyProof(_proof, numSignUps, _newSubsidyCommitment); - if (!isValid) { - revert InvalidSubsidyProof(); - } - subsidyCommitment = _newSubsidyCommitment; - increaseSubsidyIndex(subsidyBatchSize, numSignUps); - } - - /// @notice Increase the subsidy batch index (rbi, cbi) to next, - /// it will try to cbi++ if the whole batch can fit into numLeaves - /// otherwise it will increase row index: rbi++. - /// Each batch for subsidy calculation is 2 dimensional: batchSize*batchSize - /// @param batchSize the size of 1 dimensional batch over the signup users - /// @param numLeaves total number of leaves in stateTree, i.e. number of signup users - function increaseSubsidyIndex(uint256 batchSize, uint256 numLeaves) internal { - if (cbi * batchSize + batchSize < numLeaves) { - cbi++; - } else { - rbi++; - cbi = rbi; - } - } - - /// @notice Verify the subsidy proof using the Groth16 on chain verifier - /// @param _proof The proof - /// @param _numSignUps The number of signups - /// @param _newSubsidyCommitment The new subsidy commitment - /// @return isValid True if the proof is valid - function verifySubsidyProof( - uint256[8] calldata _proof, - uint256 _numSignUps, - uint256 _newSubsidyCommitment - ) public view returns (bool isValid) { - (uint8 intStateTreeDepth, , , uint8 voteOptionTreeDepth) = poll.treeDepths(); - (IMACI maci, , ) = poll.extContracts(); - - // Get the verifying key - VerifyingKey memory vk = vkRegistry.getSubsidyVk(maci.stateTreeDepth(), intStateTreeDepth, voteOptionTreeDepth); - - // Get the public inputs - uint256 publicInputHash = genSubsidyPublicInputHash(_numSignUps, _newSubsidyCommitment); - - // Verify the proof - isValid = verifier.verify(_proof, vk, publicInputHash); - } -} diff --git a/packages/hardhat/contracts/maci-contracts/SubsidyFactory.sol b/packages/hardhat/contracts/maci-contracts/SubsidyFactory.sol deleted file mode 100644 index 0be338d..0000000 --- a/packages/hardhat/contracts/maci-contracts/SubsidyFactory.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import { Subsidy } from "./Subsidy.sol"; -import { ITallySubsidyFactory } from "./interfaces/ITallySubsidyFactory.sol"; - -/// @title SubsidyFactory -/// @notice A factory contract which deploys Subsidy contracts. -contract SubsidyFactory is ITallySubsidyFactory { - /// @inheritdoc ITallySubsidyFactory - function deploy( - address _verifier, - address _vkRegistry, - address _poll, - address _messageProcessor, - address _owner - ) public returns (address subsidyAddr) { - /// @notice deploy Subsidy for this Poll - Subsidy subsidy = new Subsidy(_verifier, _vkRegistry, _poll, _messageProcessor); - subsidy.transferOwnership(_owner); - subsidyAddr = address(subsidy); - } -} diff --git a/packages/hardhat/contracts/maci-contracts/Tally.sol b/packages/hardhat/contracts/maci-contracts/Tally.sol index c05cb14..bc98405 100644 --- a/packages/hardhat/contracts/maci-contracts/Tally.sol +++ b/packages/hardhat/contracts/maci-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. @@ -174,7 +184,8 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher { (IMACI maci, , ) = poll.extContracts(); // Get the verifying key - VerifyingKey memory vk = vkRegistry.getTallyVk(maci.stateTreeDepth(), intStateTreeDepth, voteOptionTreeDepth); + IVkRegistry.Mode mode = isQv ? IVkRegistry.Mode.QV : IVkRegistry.Mode.NON_QV; + VerifyingKey memory vk = vkRegistry.getTallyVk(maci.stateTreeDepth(), intStateTreeDepth, voteOptionTreeDepth, mode); // Get the public inputs uint256 publicInputHash = genTallyVotesPublicInputHash( @@ -231,7 +242,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 +255,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 +316,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 +356,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/packages/hardhat/contracts/maci-contracts/TallyFactory.sol b/packages/hardhat/contracts/maci-contracts/TallyFactory.sol index bbf0746..dc458cc 100644 --- a/packages/hardhat/contracts/maci-contracts/TallyFactory.sol +++ b/packages/hardhat/contracts/maci-contracts/TallyFactory.sol @@ -2,21 +2,22 @@ pragma solidity ^0.8.10; import { Tally } from "./Tally.sol"; -import { ITallySubsidyFactory } from "./interfaces/ITallySubsidyFactory.sol"; +import { ITallyFactory } from "./interfaces/ITallyFactory.sol"; /// @title TallyFactory /// @notice A factory contract which deploys Tally contracts. -contract TallyFactory is ITallySubsidyFactory { - /// @inheritdoc ITallySubsidyFactory +contract TallyFactory is ITallyFactory { + /// @inheritdoc ITallyFactory function deploy( address _verifier, 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/packages/hardhat/contracts/maci-contracts/TallyNonQv.sol b/packages/hardhat/contracts/maci-contracts/TallyNonQv.sol deleted file mode 100644 index 96c8af1..0000000 --- a/packages/hardhat/contracts/maci-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/packages/hardhat/contracts/maci-contracts/TallyNonQvFactory.sol b/packages/hardhat/contracts/maci-contracts/TallyNonQvFactory.sol deleted file mode 100644 index 099fe14..0000000 --- a/packages/hardhat/contracts/maci-contracts/TallyNonQvFactory.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import { TallyNonQv } from "./TallyNonQv.sol"; -import { ITallySubsidyFactory } from "./interfaces/ITallySubsidyFactory.sol"; - -/// @title TallyNonQvFactory -/// @notice A factory contract which deploys TallyNonQv contracts. -contract TallyNonQvFactory is ITallySubsidyFactory { - /// @inheritdoc ITallySubsidyFactory - 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/packages/hardhat/contracts/maci-contracts/VkRegistry.sol b/packages/hardhat/contracts/maci-contracts/VkRegistry.sol index 2d7a4f6..e66bd83 100644 --- a/packages/hardhat/contracts/maci-contracts/VkRegistry.sol +++ b/packages/hardhat/contracts/maci-contracts/VkRegistry.sol @@ -10,330 +10,201 @@ import { IVkRegistry } from "./interfaces/IVkRegistry.sol"; /// Each circuit has a signature which is its compile-time constants represented /// as a uint256. contract VkRegistry is Ownable, SnarkCommon, IVkRegistry { - mapping(uint256 => VerifyingKey) internal processVks; - mapping(uint256 => bool) internal processVkSet; - - mapping(uint256 => VerifyingKey) internal tallyVks; - mapping(uint256 => bool) internal tallyVkSet; - - mapping(uint256 => VerifyingKey) internal subsidyVks; - mapping(uint256 => bool) internal subsidyVkSet; - - event ProcessVkSet(uint256 _sig); - event TallyVkSet(uint256 _sig); - event SubsidyVkSet(uint256 _sig); - - error ProcessVkAlreadySet(); - error TallyVkAlreadySet(); - error SubsidyVkAlreadySet(); - error ProcessVkNotSet(); - error TallyVkNotSet(); - error SubsidyVkNotSet(); - - /// @notice Create a new instance of the VkRegistry contract - // solhint-disable-next-line no-empty-blocks - constructor() payable {} - - /// @notice Check if the process verifying key is set - /// @param _sig The signature - /// @return isSet whether the verifying key is set - function isProcessVkSet(uint256 _sig) public view returns (bool isSet) { - isSet = processVkSet[_sig]; - } - - /// @notice Check if the tally verifying key is set - /// @param _sig The signature - /// @return isSet whether the verifying key is set - function isTallyVkSet(uint256 _sig) public view returns (bool isSet) { - isSet = tallyVkSet[_sig]; - } - - /// @notice Check if the subsidy verifying key is set - /// @param _sig The signature - /// @return isSet whether the verifying key is set - function isSubsidyVkSet(uint256 _sig) public view returns (bool isSet) { - isSet = subsidyVkSet[_sig]; - } - - /// @notice generate the signature for the process verifying key - /// @param _stateTreeDepth The state tree depth - /// @param _messageTreeDepth The message tree depth - /// @param _voteOptionTreeDepth The vote option tree depth - /// @param _messageBatchSize The message batch size - function genProcessVkSig( - uint256 _stateTreeDepth, - uint256 _messageTreeDepth, - uint256 _voteOptionTreeDepth, - uint256 _messageBatchSize - ) public pure returns (uint256 sig) { - sig = - (_messageBatchSize << 192) + - (_stateTreeDepth << 128) + - (_messageTreeDepth << 64) + - _voteOptionTreeDepth; - } - - /// @notice generate the signature for the tally verifying key - /// @param _stateTreeDepth The state tree depth - /// @param _intStateTreeDepth The intermediate state tree depth - /// @param _voteOptionTreeDepth The vote option tree depth - /// @return sig The signature - function genTallyVkSig( - uint256 _stateTreeDepth, - uint256 _intStateTreeDepth, - uint256 _voteOptionTreeDepth - ) public pure returns (uint256 sig) { - sig = - (_stateTreeDepth << 128) + - (_intStateTreeDepth << 64) + - _voteOptionTreeDepth; - } - - /// @notice generate the signature for the subsidy verifying key - /// @param _stateTreeDepth The state tree depth - /// @param _intStateTreeDepth The intermediate state tree depth - /// @param _voteOptionTreeDepth The vote option tree depth - /// @return sig The signature - function genSubsidyVkSig( - uint256 _stateTreeDepth, - uint256 _intStateTreeDepth, - uint256 _voteOptionTreeDepth - ) public pure returns (uint256 sig) { - sig = - (_stateTreeDepth << 128) + - (_intStateTreeDepth << 64) + - _voteOptionTreeDepth; - } - - /// @notice Set the process and tally verifying keys for a certain combination - /// of parameters - /// @param _stateTreeDepth The state tree depth - /// @param _intStateTreeDepth The intermediate state tree depth - /// @param _messageTreeDepth The message tree depth - /// @param _voteOptionTreeDepth The vote option tree depth - /// @param _messageBatchSize The message batch size - /// @param _processVk The process verifying key - /// @param _tallyVk The tally verifying key - function setVerifyingKeys( - uint256 _stateTreeDepth, - uint256 _intStateTreeDepth, - uint256 _messageTreeDepth, - uint256 _voteOptionTreeDepth, - uint256 _messageBatchSize, - VerifyingKey calldata _processVk, - VerifyingKey calldata _tallyVk - ) public onlyOwner { - uint256 processVkSig = genProcessVkSig( - _stateTreeDepth, - _messageTreeDepth, - _voteOptionTreeDepth, - _messageBatchSize - ); - - if (processVkSet[processVkSig]) revert ProcessVkAlreadySet(); - - uint256 tallyVkSig = genTallyVkSig( - _stateTreeDepth, - _intStateTreeDepth, - _voteOptionTreeDepth - ); - - if (tallyVkSet[tallyVkSig]) revert TallyVkAlreadySet(); - - VerifyingKey storage processVk = processVks[processVkSig]; - processVk.alpha1 = _processVk.alpha1; - processVk.beta2 = _processVk.beta2; - processVk.gamma2 = _processVk.gamma2; - processVk.delta2 = _processVk.delta2; - for (uint8 i = 0; i < _processVk.ic.length; i++) { - processVk.ic.push(_processVk.ic[i]); - } - - processVkSet[processVkSig] = true; - - VerifyingKey storage tallyVk = tallyVks[tallyVkSig]; - tallyVk.alpha1 = _tallyVk.alpha1; - tallyVk.beta2 = _tallyVk.beta2; - tallyVk.gamma2 = _tallyVk.gamma2; - tallyVk.delta2 = _tallyVk.delta2; - for (uint8 i = 0; i < _tallyVk.ic.length; i++) { - tallyVk.ic.push(_tallyVk.ic[i]); - } - tallyVkSet[tallyVkSig] = true; - - emit TallyVkSet(tallyVkSig); - emit ProcessVkSet(processVkSig); - } - - /// @notice Set the process verifying key for a certain combination - /// of parameters - /// @param _stateTreeDepth The state tree depth - /// @param _intStateTreeDepth The intermediate state tree depth - /// @param _voteOptionTreeDepth The vote option tree depth - /// @param _subsidyVk The verifying key - function setSubsidyKeys( - uint256 _stateTreeDepth, - uint256 _intStateTreeDepth, - uint256 _voteOptionTreeDepth, - VerifyingKey calldata _subsidyVk - ) public onlyOwner { - uint256 subsidyVkSig = genSubsidyVkSig( - _stateTreeDepth, - _intStateTreeDepth, - _voteOptionTreeDepth - ); - - if (subsidyVkSet[subsidyVkSig]) revert SubsidyVkAlreadySet(); - - VerifyingKey storage subsidyVk = subsidyVks[subsidyVkSig]; - subsidyVk.alpha1 = _subsidyVk.alpha1; - subsidyVk.beta2 = _subsidyVk.beta2; - subsidyVk.gamma2 = _subsidyVk.gamma2; - subsidyVk.delta2 = _subsidyVk.delta2; - for (uint8 i = 0; i < _subsidyVk.ic.length; i++) { - subsidyVk.ic.push(_subsidyVk.ic[i]); - } - subsidyVkSet[subsidyVkSig] = true; - - emit SubsidyVkSet(subsidyVkSig); - } - - /// @notice Check if the process verifying key is set - /// @param _stateTreeDepth The state tree depth - /// @param _messageTreeDepth The message tree depth - /// @param _voteOptionTreeDepth The vote option tree depth - /// @param _messageBatchSize The message batch size - /// @return isSet whether the verifying key is set - function hasProcessVk( - uint256 _stateTreeDepth, - uint256 _messageTreeDepth, - uint256 _voteOptionTreeDepth, - uint256 _messageBatchSize - ) public view returns (bool isSet) { - uint256 sig = genProcessVkSig( - _stateTreeDepth, - _messageTreeDepth, - _voteOptionTreeDepth, - _messageBatchSize - ); - isSet = processVkSet[sig]; - } - - /// @notice Get the process verifying key by signature - /// @param _sig The signature - /// @return vk The verifying key - function getProcessVkBySig( - uint256 _sig - ) public view returns (VerifyingKey memory vk) { - if (!processVkSet[_sig]) revert ProcessVkNotSet(); - - vk = processVks[_sig]; - } - - /// @inheritdoc IVkRegistry - function getProcessVk( - uint256 _stateTreeDepth, - uint256 _messageTreeDepth, - uint256 _voteOptionTreeDepth, - uint256 _messageBatchSize - ) public view returns (VerifyingKey memory vk) { - uint256 sig = genProcessVkSig( - _stateTreeDepth, - _messageTreeDepth, - _voteOptionTreeDepth, - _messageBatchSize - ); - - vk = getProcessVkBySig(sig); - } - - /// @notice Check if the tally verifying key is set - /// @param _stateTreeDepth The state tree depth - /// @param _intStateTreeDepth The intermediate state tree depth - /// @param _voteOptionTreeDepth The vote option tree depth - /// @return isSet whether the verifying key is set - function hasTallyVk( - uint256 _stateTreeDepth, - uint256 _intStateTreeDepth, - uint256 _voteOptionTreeDepth - ) public view returns (bool isSet) { - uint256 sig = genTallyVkSig( - _stateTreeDepth, - _intStateTreeDepth, - _voteOptionTreeDepth - ); - - isSet = tallyVkSet[sig]; - } - - /// @notice Get the tally verifying key by signature - /// @param _sig The signature - /// @return vk The verifying key - function getTallyVkBySig( - uint256 _sig - ) public view returns (VerifyingKey memory vk) { - if (!tallyVkSet[_sig]) revert TallyVkNotSet(); - - vk = tallyVks[_sig]; - } - - /// @inheritdoc IVkRegistry - function getTallyVk( - uint256 _stateTreeDepth, - uint256 _intStateTreeDepth, - uint256 _voteOptionTreeDepth - ) public view returns (VerifyingKey memory vk) { - uint256 sig = genTallyVkSig( - _stateTreeDepth, - _intStateTreeDepth, - _voteOptionTreeDepth - ); - - vk = getTallyVkBySig(sig); - } - - /// @notice Check if the subsidy verifying key is set - /// @param _stateTreeDepth The state tree depth - /// @param _intStateTreeDepth The intermediate state tree depth - /// @param _voteOptionTreeDepth The vote option tree depth - /// @return isSet whether the verifying key is set - function hasSubsidyVk( - uint256 _stateTreeDepth, - uint256 _intStateTreeDepth, - uint256 _voteOptionTreeDepth - ) public view returns (bool isSet) { - uint256 sig = genSubsidyVkSig( - _stateTreeDepth, - _intStateTreeDepth, - _voteOptionTreeDepth - ); - - isSet = subsidyVkSet[sig]; - } - - /// @notice Get the subsidy verifying key by signature - /// @param _sig The signature - /// @return vk The verifying key - function getSubsidyVkBySig( - uint256 _sig - ) public view returns (VerifyingKey memory vk) { - if (!subsidyVkSet[_sig]) revert SubsidyVkNotSet(); - - vk = subsidyVks[_sig]; - } - - /// @inheritdoc IVkRegistry - function getSubsidyVk( - uint256 _stateTreeDepth, - uint256 _intStateTreeDepth, - uint256 _voteOptionTreeDepth - ) public view returns (VerifyingKey memory vk) { - uint256 sig = genSubsidyVkSig( - _stateTreeDepth, - _intStateTreeDepth, - _voteOptionTreeDepth - ); - - vk = getSubsidyVkBySig(sig); - } + mapping(Mode => mapping(uint256 => VerifyingKey)) internal processVks; + mapping(Mode => mapping(uint256 => bool)) internal processVkSet; + + mapping(Mode => mapping(uint256 => VerifyingKey)) internal tallyVks; + mapping(Mode => mapping(uint256 => bool)) internal tallyVkSet; + + event ProcessVkSet(uint256 _sig, Mode mode); + event TallyVkSet(uint256 _sig, Mode mode); + + error ProcessVkAlreadySet(); + error TallyVkAlreadySet(); + error ProcessVkNotSet(); + error TallyVkNotSet(); + error SubsidyVkNotSet(); + + /// @notice Create a new instance of the VkRegistry contract + // solhint-disable-next-line no-empty-blocks + constructor() payable {} + + /// @notice Check if the process verifying key is set + /// @param _sig The signature + /// @param _mode QV or Non-QV + /// @return isSet whether the verifying key is set + function isProcessVkSet(uint256 _sig, Mode _mode) public view returns (bool isSet) { + isSet = processVkSet[_mode][_sig]; + } + + /// @notice Check if the tally verifying key is set + /// @param _sig The signature + /// @param _mode QV or Non-QV + /// @return isSet whether the verifying key is set + function isTallyVkSet(uint256 _sig, Mode _mode) public view returns (bool isSet) { + isSet = tallyVkSet[_mode][_sig]; + } + + /// @notice generate the signature for the process verifying key + /// @param _stateTreeDepth The state tree depth + /// @param _messageTreeDepth The message tree depth + /// @param _voteOptionTreeDepth The vote option tree depth + /// @param _messageBatchSize The message batch size + function genProcessVkSig( + uint256 _stateTreeDepth, + uint256 _messageTreeDepth, + uint256 _voteOptionTreeDepth, + uint256 _messageBatchSize + ) public pure returns (uint256 sig) { + sig = (_messageBatchSize << 192) + (_stateTreeDepth << 128) + (_messageTreeDepth << 64) + _voteOptionTreeDepth; + } + + /// @notice generate the signature for the tally verifying key + /// @param _stateTreeDepth The state tree depth + /// @param _intStateTreeDepth The intermediate state tree depth + /// @param _voteOptionTreeDepth The vote option tree depth + /// @return sig The signature + function genTallyVkSig( + uint256 _stateTreeDepth, + uint256 _intStateTreeDepth, + uint256 _voteOptionTreeDepth + ) public pure returns (uint256 sig) { + sig = (_stateTreeDepth << 128) + (_intStateTreeDepth << 64) + _voteOptionTreeDepth; + } + + /// @notice Set the process and tally verifying keys for a certain combination + /// of parameters + /// @param _stateTreeDepth The state tree depth + /// @param _intStateTreeDepth The intermediate state tree depth + /// @param _messageTreeDepth The message tree depth + /// @param _voteOptionTreeDepth The vote option tree depth + /// @param _messageBatchSize The message batch size + /// @param _mode QV or Non-QV + /// @param _processVk The process verifying key + /// @param _tallyVk The tally verifying key + function setVerifyingKeys( + uint256 _stateTreeDepth, + uint256 _intStateTreeDepth, + uint256 _messageTreeDepth, + uint256 _voteOptionTreeDepth, + uint256 _messageBatchSize, + Mode _mode, + VerifyingKey calldata _processVk, + VerifyingKey calldata _tallyVk + ) public onlyOwner { + uint256 processVkSig = genProcessVkSig(_stateTreeDepth, _messageTreeDepth, _voteOptionTreeDepth, _messageBatchSize); + + if (processVkSet[_mode][processVkSig]) revert ProcessVkAlreadySet(); + + uint256 tallyVkSig = genTallyVkSig(_stateTreeDepth, _intStateTreeDepth, _voteOptionTreeDepth); + + if (tallyVkSet[_mode][tallyVkSig]) revert TallyVkAlreadySet(); + + VerifyingKey storage processVk = processVks[_mode][processVkSig]; + processVk.alpha1 = _processVk.alpha1; + processVk.beta2 = _processVk.beta2; + processVk.gamma2 = _processVk.gamma2; + processVk.delta2 = _processVk.delta2; + + for (uint8 i = 0; i < _processVk.ic.length; i++) { + processVk.ic.push(_processVk.ic[i]); + } + + processVkSet[_mode][processVkSig] = true; + + VerifyingKey storage tallyVk = tallyVks[_mode][tallyVkSig]; + tallyVk.alpha1 = _tallyVk.alpha1; + tallyVk.beta2 = _tallyVk.beta2; + tallyVk.gamma2 = _tallyVk.gamma2; + tallyVk.delta2 = _tallyVk.delta2; + + for (uint8 i = 0; i < _tallyVk.ic.length; i++) { + tallyVk.ic.push(_tallyVk.ic[i]); + } + + tallyVkSet[_mode][tallyVkSig] = true; + + emit TallyVkSet(tallyVkSig, _mode); + emit ProcessVkSet(processVkSig, _mode); + } + + /// @notice Check if the process verifying key is set + /// @param _stateTreeDepth The state tree depth + /// @param _messageTreeDepth The message tree depth + /// @param _voteOptionTreeDepth The vote option tree depth + /// @param _messageBatchSize The message batch size + /// @param _mode QV or Non-QV + /// @return isSet whether the verifying key is set + function hasProcessVk( + uint256 _stateTreeDepth, + uint256 _messageTreeDepth, + uint256 _voteOptionTreeDepth, + uint256 _messageBatchSize, + Mode _mode + ) public view returns (bool isSet) { + uint256 sig = genProcessVkSig(_stateTreeDepth, _messageTreeDepth, _voteOptionTreeDepth, _messageBatchSize); + isSet = processVkSet[_mode][sig]; + } + + /// @notice Get the process verifying key by signature + /// @param _sig The signature + /// @param _mode QV or Non-QV + /// @return vk The verifying key + function getProcessVkBySig(uint256 _sig, Mode _mode) public view returns (VerifyingKey memory vk) { + if (!processVkSet[_mode][_sig]) revert ProcessVkNotSet(); + + vk = processVks[_mode][_sig]; + } + + /// @inheritdoc IVkRegistry + function getProcessVk( + uint256 _stateTreeDepth, + uint256 _messageTreeDepth, + uint256 _voteOptionTreeDepth, + uint256 _messageBatchSize, + Mode _mode + ) public view returns (VerifyingKey memory vk) { + uint256 sig = genProcessVkSig(_stateTreeDepth, _messageTreeDepth, _voteOptionTreeDepth, _messageBatchSize); + + vk = getProcessVkBySig(sig, _mode); + } + + /// @notice Check if the tally verifying key is set + /// @param _stateTreeDepth The state tree depth + /// @param _intStateTreeDepth The intermediate state tree depth + /// @param _voteOptionTreeDepth The vote option tree depth + /// @param _mode QV or Non-QV + /// @return isSet whether the verifying key is set + function hasTallyVk( + uint256 _stateTreeDepth, + uint256 _intStateTreeDepth, + uint256 _voteOptionTreeDepth, + Mode _mode + ) public view returns (bool isSet) { + uint256 sig = genTallyVkSig(_stateTreeDepth, _intStateTreeDepth, _voteOptionTreeDepth); + + isSet = tallyVkSet[_mode][sig]; + } + + /// @notice Get the tally verifying key by signature + /// @param _sig The signature + /// @param _mode QV or Non-QV + /// @return vk The verifying key + function getTallyVkBySig(uint256 _sig, Mode _mode) public view returns (VerifyingKey memory vk) { + if (!tallyVkSet[_mode][_sig]) revert TallyVkNotSet(); + + vk = tallyVks[_mode][_sig]; + } + + /// @inheritdoc IVkRegistry + function getTallyVk( + uint256 _stateTreeDepth, + uint256 _intStateTreeDepth, + uint256 _voteOptionTreeDepth, + Mode _mode + ) public view returns (VerifyingKey memory vk) { + uint256 sig = genTallyVkSig(_stateTreeDepth, _intStateTreeDepth, _voteOptionTreeDepth); + + vk = getTallyVkBySig(sig, _mode); + } } diff --git a/packages/hardhat/contracts/maci-contracts/gatekeepers/hatsGatekeepers/HatsGatekeeperBase.sol b/packages/hardhat/contracts/maci-contracts/gatekeepers/hatsGatekeepers/HatsGatekeeperBase.sol index 81685e1..3dc86b4 100644 --- a/packages/hardhat/contracts/maci-contracts/gatekeepers/hatsGatekeepers/HatsGatekeeperBase.sol +++ b/packages/hardhat/contracts/maci-contracts/gatekeepers/hatsGatekeepers/HatsGatekeeperBase.sol @@ -6,7 +6,7 @@ import { SignUpGatekeeper } from "../SignUpGatekeeper.sol"; import { IHats } from "../../interfaces/IHats.sol"; /// @title HatsGatekeeperBase -/// @notice Abastract contract containing the base elements of a Hats Gatekeeper contract +/// @notice Abstract contract containing the base elements of a Hats Gatekeeper contract abstract contract HatsGatekeeperBase is SignUpGatekeeper, Ownable { /*////////////////////////////////////////////////////////////// CUSTOM ERRORS diff --git a/packages/hardhat/contracts/maci-contracts/interfaces/IMPFactory.sol b/packages/hardhat/contracts/maci-contracts/interfaces/IMPFactory.sol index 362c4a7..d4d2d3e 100644 --- a/packages/hardhat/contracts/maci-contracts/interfaces/IMPFactory.sol +++ b/packages/hardhat/contracts/maci-contracts/interfaces/IMPFactory.sol @@ -9,6 +9,13 @@ interface IMessageProcessorFactory { /// @param _vkRegistry VkRegistry contract /// @param _poll Poll contract /// @param _owner Owner of the MessageProcessor contract + /// @param _isQv Whether to support QV or not /// @return The deployed MessageProcessor contract - function deploy(address _verifier, address _vkRegistry, address _poll, address _owner) external returns (address); + function deploy( + address _verifier, + address _vkRegistry, + address _poll, + address _owner, + bool _isQv + ) external returns (address); } diff --git a/packages/hardhat/contracts/maci-contracts/interfaces/ITallySubsidyFactory.sol b/packages/hardhat/contracts/maci-contracts/interfaces/ITallyFactory.sol similarity index 66% rename from packages/hardhat/contracts/maci-contracts/interfaces/ITallySubsidyFactory.sol rename to packages/hardhat/contracts/maci-contracts/interfaces/ITallyFactory.sol index c772329..1e2c7e4 100644 --- a/packages/hardhat/contracts/maci-contracts/interfaces/ITallySubsidyFactory.sol +++ b/packages/hardhat/contracts/maci-contracts/interfaces/ITallyFactory.sol @@ -1,21 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.10; -/// @title ITallySubsidyFactory -/// @notice TallySubsidyFactory interface -interface ITallySubsidyFactory { - /// @notice Deploy a new Tally or Subsidy contract and return the address. +/// @title ITallyFactory +/// @notice TallyFactory interface +interface ITallyFactory { + /// @notice Deploy a new Tally contract and return the address. /// @param _verifier Verifier contract /// @param _vkRegistry VkRegistry contract /// @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/packages/hardhat/contracts/maci-contracts/interfaces/IVkRegistry.sol b/packages/hardhat/contracts/maci-contracts/interfaces/IVkRegistry.sol index 24c2f5a..baaf62b 100644 --- a/packages/hardhat/contracts/maci-contracts/interfaces/IVkRegistry.sol +++ b/packages/hardhat/contracts/maci-contracts/interfaces/IVkRegistry.sol @@ -6,15 +6,23 @@ import { SnarkCommon } from "../crypto/SnarkCommon.sol"; /// @title IVkRegistry /// @notice VkRegistry interface interface IVkRegistry { + /// @notice Multi store support for QV and Non-QV poll + enum Mode { + QV, + NON_QV + } + /// @notice Get the tally verifying key /// @param _stateTreeDepth The state tree depth /// @param _intStateTreeDepth The intermediate state tree depth /// @param _voteOptionTreeDepth The vote option tree depth + /// @param _mode QV or Non-QV /// @return The verifying key function getTallyVk( uint256 _stateTreeDepth, uint256 _intStateTreeDepth, - uint256 _voteOptionTreeDepth + uint256 _voteOptionTreeDepth, + Mode _mode ) external view returns (SnarkCommon.VerifyingKey memory); /// @notice Get the process verifying key @@ -22,22 +30,13 @@ interface IVkRegistry { /// @param _messageTreeDepth The message tree depth /// @param _voteOptionTreeDepth The vote option tree depth /// @param _messageBatchSize The message batch size + /// @param _mode QV or Non-QV /// @return The verifying key function getProcessVk( uint256 _stateTreeDepth, uint256 _messageTreeDepth, uint256 _voteOptionTreeDepth, - uint256 _messageBatchSize - ) external view returns (SnarkCommon.VerifyingKey memory); - - /// @notice Get the subsidy verifying key - /// @param _stateTreeDepth The state tree depth - /// @param _intStateTreeDepth The intermediate state tree depth - /// @param _voteOptionTreeDepth The vote option tree depth - /// @return The verifying key - function getSubsidyVk( - uint256 _stateTreeDepth, - uint256 _intStateTreeDepth, - uint256 _voteOptionTreeDepth + uint256 _messageBatchSize, + Mode _mode ) external view returns (SnarkCommon.VerifyingKey memory); } diff --git a/packages/hardhat/contracts/maci-contracts/utilities/CommonUtilities.sol b/packages/hardhat/contracts/maci-contracts/utilities/CommonUtilities.sol index 08ade0d..901d017 100644 --- a/packages/hardhat/contracts/maci-contracts/utilities/CommonUtilities.sol +++ b/packages/hardhat/contracts/maci-contracts/utilities/CommonUtilities.sol @@ -6,11 +6,11 @@ import { IPoll } from "../interfaces/IPoll.sol"; /// @title CommonUtilities /// @notice A contract that holds common utilities /// which are to be used by multiple contracts -/// namely Subsidy, Tally and MessageProcessor +/// namely Tally and MessageProcessor contract CommonUtilities { error VotingPeriodNotPassed(); - /// @notice common function for MessageProcessor, Tally and Subsidy + /// @notice common function for MessageProcessor, and Tally /// @param _poll the poll to be checked function _votingPeriodOver(IPoll _poll) internal view { (uint256 deployTime, uint256 duration) = _poll.getDeployTimeAndDuration(); diff --git a/packages/hardhat/deploy/09_maci.ts b/packages/hardhat/deploy/08_maci.ts similarity index 86% rename from packages/hardhat/deploy/09_maci.ts rename to packages/hardhat/deploy/08_maci.ts index 88015e5..d9993a1 100644 --- a/packages/hardhat/deploy/09_maci.ts +++ b/packages/hardhat/deploy/08_maci.ts @@ -6,7 +6,7 @@ import { TopupCreditContractName, stateTreeDepth, } from "../constants"; -import { SignUpGatekeeper } from "../typechain-types"; +import { MACI, SignUpGatekeeper } from "../typechain-types"; // const STATE_TREE_SUBDEPTH = 2; @@ -23,7 +23,6 @@ const deployContracts: DeployFunction = async function (hre: HardhatRuntimeEnvir const pollFactory = await hre.ethers.getContract("PollFactory", deployer); const messageProcessorFactory = await hre.ethers.getContract("MessageProcessorFactory", deployer); const tallyFactory = await hre.ethers.getContract("TallyFactory", deployer); - const subsidyFactory = await hre.ethers.getContract("SubsidyFactory", deployer); await hre.deployments.deploy("MACI", { from: deployer, @@ -31,7 +30,6 @@ const deployContracts: DeployFunction = async function (hre: HardhatRuntimeEnvir await pollFactory.getAddress(), await messageProcessorFactory.getAddress(), await tallyFactory.getAddress(), - await subsidyFactory.getAddress(), await gatekeeper.getAddress(), await initialVoiceCreditProxy.getAddress(), await topupCredit.getAddress(), @@ -47,11 +45,17 @@ const deployContracts: DeployFunction = async function (hre: HardhatRuntimeEnvir autoMine: true, }); - const maci = await hre.ethers.getContract("MACI", deployer); + const maci = await hre.ethers.getContract("MACI", deployer); console.log(`The MACI contract is deployed at ${await maci.getAddress()}`); await gatekeeper.setMaciInstance(await maci.getAddress()); + + // // Save the acc queue to the deployments + // await hre.deployments.save("AccQueue", { + // abi: (await hre.artifacts.readArtifact("AccQueue")).abi, + // address: await maci.stateAq(), + // }); }; export default deployContracts; diff --git a/packages/hardhat/deploy/08_subsidy_factory.ts b/packages/hardhat/deploy/08_subsidy_factory.ts deleted file mode 100644 index 48bcd70..0000000 --- a/packages/hardhat/deploy/08_subsidy_factory.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { DeployFunction } from "hardhat-deploy/types"; - -const deployContracts: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - const { deployer } = await hre.getNamedAccounts(); - - const poseidonT3 = await hre.ethers.getContract("PoseidonT3", deployer); - const poseidonT4 = await hre.ethers.getContract("PoseidonT4", deployer); - const poseidonT5 = await hre.ethers.getContract("PoseidonT5", deployer); - const poseidonT6 = await hre.ethers.getContract("PoseidonT6", deployer); - - await hre.deployments.deploy("SubsidyFactory", { - from: deployer, - args: [], - log: true, - libraries: { - PoseidonT3: await poseidonT3.getAddress(), - PoseidonT4: await poseidonT4.getAddress(), - PoseidonT5: await poseidonT5.getAddress(), - PoseidonT6: await poseidonT6.getAddress(), - }, - autoMine: true, - }); - - const subsidyFactory = await hre.ethers.getContract("SubsidyFactory", deployer); - - console.log(`The subsidy factory is deployed at ${await subsidyFactory.getAddress()}`); -}; - -export default deployContracts; - -deployContracts.tags = ["SubsidyFactory"]; diff --git a/packages/hardhat/deploy/10_vk_registry.ts b/packages/hardhat/deploy/09_vk_registry.ts similarity index 78% rename from packages/hardhat/deploy/10_vk_registry.ts rename to packages/hardhat/deploy/09_vk_registry.ts index b23581d..8c03d25 100644 --- a/packages/hardhat/deploy/10_vk_registry.ts +++ b/packages/hardhat/deploy/09_vk_registry.ts @@ -12,11 +12,16 @@ import { messageTreeDepth, processMessagesZkeyPath, stateTreeDepth, - subsidyZkeyPath, tallyVotesZkeyPath, + useQuadraticVoting, voteOptionTreeDepth, } from "../constants"; +export enum EMode { + QV, + NON_QV, +} + const deployContracts: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { deployer } = await hre.getNamedAccounts(); @@ -30,10 +35,9 @@ const deployContracts: DeployFunction = async function (hre: HardhatRuntimeEnvir const vkRegistry = await hre.ethers.getContract("VkRegistry", deployer); console.log(`The Vk Registry is deployed at ${await vkRegistry.getAddress()}`); - const [processVk, tallyVk, subsidyVk] = await Promise.all([ + const [processVk, tallyVk] = await Promise.all([ extractVk(processMessagesZkeyPath), extractVk(tallyVotesZkeyPath), - subsidyZkeyPath ? extractVk(subsidyZkeyPath) : null, ]).then(vks => vks.map(vk => (vk ? VerifyingKey.fromObj(vk as any) : null))); const messageBatchSize = 5 ** messageBatchDepth; @@ -45,6 +49,7 @@ const deployContracts: DeployFunction = async function (hre: HardhatRuntimeEnvir messageTreeDepth, voteOptionTreeDepth, messageBatchSize, + useQuadraticVoting ? EMode.QV : EMode.NON_QV, ); if (!hasProcessVk) { await vkRegistry.setVerifyingKeys( @@ -53,22 +58,11 @@ const deployContracts: DeployFunction = async function (hre: HardhatRuntimeEnvir messageTreeDepth, voteOptionTreeDepth, messageBatchSize, + useQuadraticVoting ? EMode.QV : EMode.NON_QV, processVkParam, tallyVkParam, ); } - - if (subsidyVk) { - const hasSubsidyVk = await vkRegistry.hasSubsidyVk(stateTreeDepth, intStateTreeDepth, voteOptionTreeDepth); - if (!hasSubsidyVk) { - await vkRegistry.setSubsidyKeys( - stateTreeDepth, - intStateTreeDepth, - voteOptionTreeDepth, - subsidyVk.asContractParam() as IVerifyingKeyStruct, - ); - } - } }; export default deployContracts; diff --git a/packages/hardhat/deploy/11_poll_manager.ts b/packages/hardhat/deploy/10_poll_manager.ts similarity index 94% rename from packages/hardhat/deploy/11_poll_manager.ts rename to packages/hardhat/deploy/10_poll_manager.ts index 83accec..5f09a2c 100644 --- a/packages/hardhat/deploy/11_poll_manager.ts +++ b/packages/hardhat/deploy/10_poll_manager.ts @@ -3,6 +3,7 @@ import { DeployFunction } from "hardhat-deploy/types"; import { MACI, PollManager, Verifier, VkRegistry } from "../typechain-types"; import fs from "fs"; import { Keypair } from "../maci-ts/domainobjs"; +import { useQuadraticVoting } from "../constants"; function fetchOrCreateKeyPair(filePath: string) { let keypair: Keypair | null = null; @@ -26,7 +27,7 @@ const deployContracts: DeployFunction = async function (hre: HardhatRuntimeEnvir await hre.deployments.deploy("PollManager", { from: deployer, - args: [await maci.getAddress()], + args: [await maci.getAddress(), useQuadraticVoting], log: true, autoMine: true, }); diff --git a/packages/hardhat/deploy/99_generateTsAbis.ts b/packages/hardhat/deploy/99_generateTsAbis.ts index 4cd9293..07fb191 100644 --- a/packages/hardhat/deploy/99_generateTsAbis.ts +++ b/packages/hardhat/deploy/99_generateTsAbis.ts @@ -84,14 +84,13 @@ function getContractDataFromDeployments() { const chainId = fs.readFileSync(`${DEPLOYMENTS_DIR}/${chainName}/.chainId`).toString(); const contracts = {} as Record; for (const contractName of getContractNames(`${DEPLOYMENTS_DIR}/${chainName}`)) { - const { - abi, - address, - metadata, - receipt: { blockNumber }, - } = JSON.parse(fs.readFileSync(`${DEPLOYMENTS_DIR}/${chainName}/${contractName}.json`).toString()); - const inheritedFunctions = getInheritedFunctions(JSON.parse(metadata).sources, contractName); - contracts[contractName] = { address, abi, inheritedFunctions, deploymentBlockNumber: blockNumber }; + const { abi, address, metadata, receipt } = JSON.parse( + fs.readFileSync(`${DEPLOYMENTS_DIR}/${chainName}/${contractName}.json`).toString(), + ); + const inheritedFunctions = metadata + ? getInheritedFunctions(JSON.parse(metadata).sources, contractName) + : undefined; + contracts[contractName] = { address, abi, inheritedFunctions, deploymentBlockNumber: receipt?.blockNumber }; } output[chainId] = contracts; } diff --git a/packages/hardhat/hardhat.config.ts b/packages/hardhat/hardhat.config.ts index 251cdeb..e81c976 100644 --- a/packages/hardhat/hardhat.config.ts +++ b/packages/hardhat/hardhat.config.ts @@ -30,6 +30,7 @@ const config: HardhatUserConfig = { // https://docs.soliditylang.org/en/latest/using-the-compiler.html#optimizer-options runs: 200, }, + viaIR: true, }, }, defaultNetwork: "localhost", diff --git a/packages/hardhat/maci-ts/circuits/__tests__/Ecdh.test.ts b/packages/hardhat/maci-ts/circuits/__tests__/Ecdh.test.ts index 3985401..b449552 100644 --- a/packages/hardhat/maci-ts/circuits/__tests__/Ecdh.test.ts +++ b/packages/hardhat/maci-ts/circuits/__tests__/Ecdh.test.ts @@ -1,7 +1,7 @@ import chai, { expect } from "chai"; import chaiAsPromised from "chai-as-promised"; import { type WitnessTester } from "circomkit"; -import { Keypair } from "../domainobjs"; +import { Keypair } from "../../domainobjs"; import { circomkitInstance } from "./utils/utils"; diff --git a/packages/hardhat/maci-ts/core/MaciState.ts b/packages/hardhat/maci-ts/core/MaciState.ts index bb38c48..f0db9bf 100644 --- a/packages/hardhat/maci-ts/core/MaciState.ts +++ b/packages/hardhat/maci-ts/core/MaciState.ts @@ -75,7 +75,6 @@ export class MaciState implements IMaciState { treeDepths, { messageBatchSize, - subsidyBatchSize: STATE_TREE_ARITY ** treeDepths.intStateTreeDepth, tallyBatchSize: STATE_TREE_ARITY ** treeDepths.intStateTreeDepth, }, maxValues, diff --git a/packages/hardhat/maci-ts/core/Poll.ts b/packages/hardhat/maci-ts/core/Poll.ts index 228bb8a..136f56d 100644 --- a/packages/hardhat/maci-ts/core/Poll.ts +++ b/packages/hardhat/maci-ts/core/Poll.ts @@ -40,14 +40,13 @@ import type { IJsonPoll, IProcessMessagesOutput, ITallyCircuitInputs, - ISubsidyCircuitInputs, IProcessMessagesCircuitInputs, } from "./utils/types"; import type { PathElements } from "../crypto"; import { STATE_TREE_ARITY, MESSAGE_TREE_ARITY } from "./utils/constants"; import { ProcessMessageErrors, ProcessMessageError } from "./utils/errors"; -import { packTallyVotesSmallVals, packSubsidySmallVals } from "./utils/utils"; +import { packTallyVotesSmallVals } from "./utils/utils"; /** * A representation of the Poll contract. @@ -112,19 +111,6 @@ export class Poll implements IPoll { totalSpentVoiceCredits = 0n; - // For coefficient and subsidy calculation - subsidy: bigint[] = []; // size: M, M is number of vote options - - subsidySalts: Record = {}; - - rbi = 0; // row batch index - - cbi = 0; // column batch index - - MM = 50; // adjustable parameter - - WW = 4; // number of digits for float representation - // an empty ballot and its hash to be used as zero value in the // ballot tree emptyBallot: Ballot; @@ -169,7 +155,6 @@ export class Poll implements IPoll { this.tallyResult = new Array(this.maxValues.maxVoteOptions).fill(0n) as bigint[]; this.perVOSpentVoiceCredits = new Array(this.maxValues.maxVoteOptions).fill(0n) as bigint[]; - this.subsidy = new Array(this.maxValues.maxVoteOptions).fill(0n) as bigint[]; // we put a blank state leaf to prevent a DoS attack this.emptyBallot = Ballot.genBlankBallot(this.maxValues.maxVoteOptions, treeDepths.voteOptionTreeDepth); @@ -548,8 +533,8 @@ export class Poll implements IPoll { // otherwise we continue processing but add the default blank data instead of // this invalid message if (e instanceof ProcessMessageError) { - // if logging is enabled, print the error - if (!quiet) { + // if logging is enabled, and it's not the first message, print the error + if (!quiet && idx !== 0) { // eslint-disable-next-line no-console console.log(`Error at message index ${idx} - ${e.message}`); } @@ -896,182 +881,6 @@ export class Poll implements IPoll { */ hasUntalliedBallots = (): boolean => this.numBatchesTallied * this.batchSizes.tallyBatchSize < this.ballots.length; - /** - * This method checks if there are any unfinished subsidy calculations. - * @returns Returns true if the product of the row batch index (rbi) and batch size or - * the product of column batch index (cbi) and batch size is less than the length - * of the ballots array, indicating that there are still ballots left to be processed. - * Otherwise, it returns false. - */ - hasUnfinishedSubsidyCalculation = (): boolean => { - const batchSize = this.batchSizes.subsidyBatchSize; - return this.rbi * batchSize < this.ballots.length && this.cbi * batchSize < this.ballots.length; - }; - - /** - * This method calculates the subsidy per batch. - * @returns Returns an array of big integers which represent the circuit inputs for the subsidy calculation. - */ - subsidyPerBatch = (): ISubsidyCircuitInputs => { - const batchSize = this.batchSizes.subsidyBatchSize; - - assert(this.hasUnfinishedSubsidyCalculation(), "No more subsidy batches to calculate"); - - const stateRoot = this.stateTree!.root; - const ballotRoot = this.ballotTree!.root; - const sbSalt = this.sbSalts[this.currentMessageBatchIndex!]; - const sbCommitment = hash3([stateRoot, ballotRoot, sbSalt]); - - const currentSubsidy = this.subsidy.map(x => BigInt(x.toString())); - let currentSubsidyCommitment = 0n; - let currentSubsidySalt = 0n; - let saltIndex = this.previousSubsidyIndexToString(); - - if (this.rbi !== 0 || this.cbi !== 0) { - currentSubsidySalt = BigInt(this.subsidySalts[saltIndex]); - currentSubsidyCommitment = BigInt( - genTreeCommitment(this.subsidy, currentSubsidySalt, this.treeDepths.voteOptionTreeDepth).valueOf(), - ); - } - - const rowStartIndex = this.rbi * batchSize; - const colStartIndex = this.cbi * batchSize; - const [ballots1, ballots2] = this.subsidyCalculation(rowStartIndex, colStartIndex); - - const ballotSubrootProof1 = this.ballotTree?.genSubrootProof(rowStartIndex, rowStartIndex + batchSize); - const ballotSubrootProof2 = this.ballotTree?.genSubrootProof(colStartIndex, colStartIndex + batchSize); - - const newSubsidySalt = genRandomSalt(); - saltIndex = `${this.rbi.toString()}-${this.cbi.toString()}`; - this.subsidySalts[saltIndex] = newSubsidySalt; - const newSubsidyCommitment = genTreeCommitment(this.subsidy, newSubsidySalt, this.treeDepths.voteOptionTreeDepth); - - const packedVals = packSubsidySmallVals(this.rbi, this.cbi, Number(this.numSignups)); - - const inputHash = sha256Hash([packedVals, sbCommitment, currentSubsidyCommitment, newSubsidyCommitment]); - - const circuitInputs = stringifyBigInts({ - stateRoot, - ballotRoot, - sbSalt, - currentSubsidySalt, - newSubsidySalt, - sbCommitment, - currentSubsidyCommitment, - newSubsidyCommitment, - currentSubsidy, - packedVals, - inputHash, - ballots1: ballots1.map(x => x.asCircuitInputs()), - ballots2: ballots2.map(x => x.asCircuitInputs()), - votes1: ballots1.map(x => x.votes), - votes2: ballots2.map(x => x.votes), - ballotPathElements1: ballotSubrootProof1!.pathElements, - ballotPathElements2: ballotSubrootProof2!.pathElements, - }) as unknown as ISubsidyCircuitInputs; - - this.increaseSubsidyIndex(); - return circuitInputs; - }; - - /** - * It increases the index for the subsidy calculation. - */ - private increaseSubsidyIndex = (): void => { - const batchSize = this.batchSizes.subsidyBatchSize; - - if (this.cbi * batchSize + batchSize < this.ballots.length) { - this.cbi += 1; - } else { - this.rbi += 1; - this.cbi = this.rbi; - } - }; - - /** - * This method converts the previous subsidy index to a string. - * @returns Returns a string representation of the previous subsidy index. - * The string is in the format "rbi-cbi", where rbi and cbi are - * the previous row batch index and column batch index respectively. - */ - private previousSubsidyIndexToString = (): string => { - const batchSize = this.batchSizes.subsidyBatchSize; - const numBatches = Math.ceil(this.ballots.length / batchSize); - - let { cbi } = this; - let { rbi } = this; - - if (this.cbi === 0 && this.rbi === 0) { - return "0-0"; - } - - if (this.cbi > this.rbi) { - cbi -= 1; - } else { - rbi -= 1; - cbi = numBatches - 1; - } - - return `${rbi.toString()}-${cbi.toString()}`; - }; - - /** - * This method calculates the coefficient for a pair of ballots. - * @param rowBallot - The ballot in the row. - * @param colBallot - The ballot in the column. - * - * @returns Returns the calculated coefficient. - */ - private coefficientCalculation = (rowBallot: Ballot, colBallot: Ballot): bigint => { - let sum = 0n; - for (let p = 0; p < this.maxValues.maxVoteOptions; p += 1) { - sum += BigInt(rowBallot.votes[p].valueOf()) * colBallot.votes[p]; - } - const res = BigInt(this.MM * 10 ** this.WW) / (BigInt(this.MM) + BigInt(sum)); - return res; - }; - - /** - * This method calculates the subsidy for a batch of ballots. - * @param rowStartIndex - The starting index for the row ballots. - * @param colStartIndex - The starting index for the column ballots. - * @returns Returns a 2D array of ballots. The first array contains the row ballots and the second array contains the column ballots. - */ - private subsidyCalculation = (rowStartIndex: number, colStartIndex: number): Ballot[][] => { - const batchSize = this.batchSizes.subsidyBatchSize; - const ballots1: Ballot[] = []; - const ballots2: Ballot[] = []; - const emptyBallot = new Ballot(this.maxValues.maxVoteOptions, this.treeDepths.voteOptionTreeDepth); - - for (let i = 0; i < batchSize; i += 1) { - const row = rowStartIndex + i; - const col = colStartIndex + i; - const rowBallot = row < this.ballots.length ? this.ballots[row] : emptyBallot; - const colBallot = col < this.ballots.length ? this.ballots[col] : emptyBallot; - ballots1.push(rowBallot); - ballots2.push(colBallot); - } - for (let i = 0; i < batchSize; i += 1) { - for (let j = 0; j < batchSize; j += 1) { - const row = rowStartIndex + i; - const col = colStartIndex + j; - const rowBallot = row < this.ballots.length ? this.ballots[row] : emptyBallot; - const colBallot = col < this.ballots.length ? this.ballots[col] : emptyBallot; - - const kij = this.coefficientCalculation(rowBallot, colBallot); - for (let p = 0; p < this.maxValues.maxVoteOptions; p += 1) { - const vip = BigInt(rowBallot.votes[p].valueOf()); - const vjp = BigInt(colBallot.votes[p].valueOf()); - if (rowStartIndex !== colStartIndex || (rowStartIndex === colStartIndex && i < j)) { - this.subsidy[p] += 2n * kij * vip * vjp; - } - } - } - } - - return [ballots1, ballots2]; - }; - /** * This method tallies a ballots and updates the tally results. * @returns the circuit inputs for the TallyVotes circuit. @@ -1463,7 +1272,6 @@ export class Poll implements IPoll { }, { tallyBatchSize: Number(this.batchSizes.tallyBatchSize.toString()), - subsidyBatchSize: Number(this.batchSizes.subsidyBatchSize.toString()), messageBatchSize: Number(this.batchSizes.messageBatchSize.toString()), }, { @@ -1515,17 +1323,6 @@ export class Poll implements IPoll { copied.spentVoiceCreditSubtotalSalts[k] = BigInt(this.spentVoiceCreditSubtotalSalts[k].toString()); }); - // subsidy related copy - copied.subsidy = this.subsidy.map((x: bigint) => BigInt(x.toString())); - copied.rbi = Number(this.rbi.toString()); - copied.cbi = Number(this.cbi.toString()); - copied.MM = Number(this.MM.toString()); - copied.WW = Number(this.WW.toString()); - - Object.keys(this.subsidySalts).forEach(k => { - copied.subsidySalts[k] = BigInt(this.subsidySalts[k].toString()); - }); - // update the number of signups copied.setNumSignups(this.numSignups); diff --git a/packages/hardhat/maci-ts/core/__tests__/Poll.test.ts b/packages/hardhat/maci-ts/core/__tests__/Poll.test.ts index 1e761f1..96e5bb7 100644 --- a/packages/hardhat/maci-ts/core/__tests__/Poll.test.ts +++ b/packages/hardhat/maci-ts/core/__tests__/Poll.test.ts @@ -541,65 +541,6 @@ describe("Poll", function test() { }); }); - describe("subsidy", () => { - const maciState = new MaciState(STATE_TREE_DEPTH); - const pollId = maciState.deployPoll( - BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, - treeDepths, - messageBatchSize, - coordinatorKeypair, - ); - - const poll = maciState.polls.get(pollId)!; - - const user1Keypair = new Keypair(); - // signup the user - const user1StateIndex = maciState.signUp( - user1Keypair.pubKey, - voiceCreditBalance, - BigInt(Math.floor(Date.now() / 1000)), - ); - - const voteWeight = 5n; - const voteOption = 0n; - - const command = new PCommand( - BigInt(user1StateIndex), - user1Keypair.pubKey, - voteOption, - voteWeight, - 1n, - BigInt(pollId), - ); - - const signature = command.sign(user1Keypair.privKey); - - const ecdhKeypair = new Keypair(); - const sharedKey = Keypair.genEcdhSharedKey(ecdhKeypair.privKey, coordinatorKeypair.pubKey); - - const message = command.encrypt(signature, sharedKey); - - before(() => { - poll.updatePoll(BigInt(maciState.stateLeaves.length)); - poll.publishMessage(message, ecdhKeypair.pubKey); - poll.processAllMessages(); - poll.tallyVotes(); - }); - - it("should calculate the subsidy", () => { - const { rbi, cbi } = poll; - expect(() => poll.subsidyPerBatch()).to.not.throw(); - const { rbi: newRbi, cbi: newCbi } = poll; - expect(newRbi).to.eq(rbi + 1); - expect(newCbi).to.eq(cbi + 1); - }); - - it("should throw when the subsidy was already calculated", () => { - expect(() => poll.subsidyPerBatch()).to.throw("No more subsidy batches to calculate"); - }); - }); - describe("setCoordinatorKeypair", () => { it("should update the coordinator's Keypair", () => { const maciState = new MaciState(STATE_TREE_DEPTH); diff --git a/packages/hardhat/maci-ts/core/__tests__/utils.test.ts b/packages/hardhat/maci-ts/core/__tests__/utils.test.ts index 4e49e29..964aa89 100644 --- a/packages/hardhat/maci-ts/core/__tests__/utils.test.ts +++ b/packages/hardhat/maci-ts/core/__tests__/utils.test.ts @@ -3,12 +3,10 @@ import { expect } from "chai"; import { genProcessVkSig, genTallyVkSig, - genSubsidyVkSig, packProcessMessageSmallVals, unpackProcessMessageSmallVals, packTallyVotesSmallVals, unpackTallyVotesSmallVals, - packSubsidySmallVals, } from "../utils/utils"; describe("Utils", () => { @@ -22,11 +20,6 @@ describe("Utils", () => { expect(result).to.equal(340282366920938463500268095579187314691n); }); - it("genSubsidyVkSig should work", () => { - const result = genSubsidyVkSig(1, 2, 3); - expect(result).to.equal(340282366920938463500268095579187314691n); - }); - it("packProcessMessageSmallVals should work", () => { const result = packProcessMessageSmallVals(1n, 2n, 3, 4); expect(result).to.equal(5708990770823843327184944562488436835454287873n); @@ -54,9 +47,4 @@ describe("Utils", () => { batchStartIndex: 0n, }); }); - - it("packSubsidySmallVals should work", () => { - const result = packSubsidySmallVals(1, 2, 3); - expect(result).to.equal(3802951800684689330390016458754n); - }); }); diff --git a/packages/hardhat/maci-ts/core/index.ts b/packages/hardhat/maci-ts/core/index.ts index 9f756d4..0df07c8 100644 --- a/packages/hardhat/maci-ts/core/index.ts +++ b/packages/hardhat/maci-ts/core/index.ts @@ -5,18 +5,15 @@ export { Poll } from "./Poll"; export { genProcessVkSig, genTallyVkSig, - genSubsidyVkSig, packProcessMessageSmallVals, unpackProcessMessageSmallVals, packTallyVotesSmallVals, unpackTallyVotesSmallVals, - packSubsidySmallVals, } from "./utils/utils"; export type { ITallyCircuitInputs, IProcessMessagesCircuitInputs, - ISubsidyCircuitInputs, CircuitInputs, MaxValues, TreeDepths, diff --git a/packages/hardhat/maci-ts/core/utils/types.ts b/packages/hardhat/maci-ts/core/utils/types.ts index afb65e0..b153b9d 100644 --- a/packages/hardhat/maci-ts/core/utils/types.ts +++ b/packages/hardhat/maci-ts/core/utils/types.ts @@ -39,12 +39,10 @@ export interface TreeDepths { * This interface defines the batch sizes. * @property tallyBatchSize - The size of the tally batch. * @property messageBatchSize - The size of the message batch. - * @property subsidyBatchSize - The size of the subsidy batch. */ export interface BatchSizes { tallyBatchSize: number; messageBatchSize: number; - subsidyBatchSize: number; } /** @@ -92,8 +90,6 @@ export interface IPoll { hasUnprocessedMessages(): boolean; processAllMessages(): { stateLeaves: StateLeaf[]; ballots: Ballot[] }; hasUntalliedBallots(): boolean; - hasUnfinishedSubsidyCalculation(): boolean; - subsidyPerBatch(): ISubsidyCircuitInputs; copy(): Poll; equals(p: Poll): boolean; toJSON(): IJsonPoll; @@ -199,26 +195,3 @@ export interface ITallyCircuitInputs { newPerVOSpentVoiceCreditsRootSalt?: string; newSpentVoiceCreditSubtotalSalt: string; } - -/** - * An interface describing the circuit inputs to the Subsidy circuit - */ -export interface ISubsidyCircuitInputs { - stateRoot: string; - ballotRoot: string; - sbSalt: string; - currentSubsidySalt: string; - newSubsidySalt: string; - sbCommitment: string; - currentSubsidyCommitment: string; - newSubsidyCommitment: string; - currentSubsidy: string[]; - packedVals: string; - inputHash: string; - ballots1: string[]; - ballots2: string[]; - votes1: number[]; - votes2: number[]; - ballotPathElements1: string[]; - ballotPathElements2: string[]; -} diff --git a/packages/hardhat/maci-ts/core/utils/utils.ts b/packages/hardhat/maci-ts/core/utils/utils.ts index 38374ef..14b1622 100644 --- a/packages/hardhat/maci-ts/core/utils/utils.ts +++ b/packages/hardhat/maci-ts/core/utils/utils.ts @@ -38,22 +38,6 @@ export const genTallyVkSig = ( _voteOptionTreeDepth: number, ): bigint => (BigInt(_stateTreeDepth) << 128n) + (BigInt(_intStateTreeDepth) << 64n) + BigInt(_voteOptionTreeDepth); -/** - * This function generates the signature of a Subsidy Verifying Key(VK). - * This can be used to check if a SubsidyCalculations' circuit VK is registered - * in a smart contract that holds several VKs. - * @param _stateTreeDepth - The depth of the state tree. - * @param _intStateTreeDepth - The depth of the intermediate state tree. - * @param _voteOptionTreeDepth - The depth of the vote option tree. - * @returns Returns a signature for querying if a verifying key with - * the given parameters is already registered in the contract. - */ -export const genSubsidyVkSig = ( - _stateTreeDepth: number, - _intStateTreeDepth: number, - _voteOptionTreeDepth: number, -): bigint => (BigInt(_stateTreeDepth) << 128n) + (BigInt(_intStateTreeDepth) << 64n) + BigInt(_voteOptionTreeDepth); - /** * This function packs it's parameters into a single bigint. * @param maxVoteOptions - The maximum number of vote options. @@ -139,17 +123,3 @@ export const unpackTallyVotesSmallVals = (packedVals: bigint): { numSignUps: big return { numSignUps, batchStartIndex }; }; - -/** - * This function packs it's parameters into a single bigint. - * @param row - The row. - * @param col - The column. - * @param numSignUps - The number of signups. - * @returns Returns a single bigint that contains the packed values. - */ -export const packSubsidySmallVals = (row: number, col: number, numSignUps: number): bigint => { - // Note: the << operator has lower precedence than + - const packedVals = (BigInt(numSignUps) << 100n) + (BigInt(row) << 50n) + BigInt(col); - - return packedVals; -}; diff --git a/packages/hardhat/maci-ts/crypto/keys.ts b/packages/hardhat/maci-ts/crypto/keys.ts index 0c83cae..ab9f139 100644 --- a/packages/hardhat/maci-ts/crypto/keys.ts +++ b/packages/hardhat/maci-ts/crypto/keys.ts @@ -41,7 +41,7 @@ export const packPubKey = (pubKey: PubKey): bigint => BigInt(packPublicKey(pubKe */ export const unpackPubKey = (packed: bigint): PubKey => { const pubKey = unpackPublicKey(packed); - return pubKey.map((x: any) => BigInt(x)) as PubKey; + return pubKey; }; /** diff --git a/packages/hardhat/maci-ts/domainobjs/ballot.ts b/packages/hardhat/maci-ts/domainobjs/ballot.ts index 8ba7f6e..910b5a7 100644 --- a/packages/hardhat/maci-ts/domainobjs/ballot.ts +++ b/packages/hardhat/maci-ts/domainobjs/ballot.ts @@ -111,8 +111,8 @@ export class Ballot { */ toJSON(): IJsonBallot { return { - votes: this.votes, - nonce: this.nonce, + votes: this.votes.map(x => x.toString()), + nonce: this.nonce.toString(), voteOptionTreeDepth: this.voteOptionTreeDepth, }; } diff --git a/packages/hardhat/maci-ts/domainobjs/commands/PCommand.ts b/packages/hardhat/maci-ts/domainobjs/commands/PCommand.ts index 4c6adac..a68aed2 100644 --- a/packages/hardhat/maci-ts/domainobjs/commands/PCommand.ts +++ b/packages/hardhat/maci-ts/domainobjs/commands/PCommand.ts @@ -166,7 +166,10 @@ export class PCommand implements ICommand { const ciphertext: Ciphertext = poseidonEncrypt(plaintext, sharedKey, BigInt(0)); - const message = new Message(BigInt(1), ciphertext as bigint[]); + const message = new Message( + BigInt(1), + ciphertext as [bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint], + ); return message; }; @@ -181,8 +184,8 @@ export class PCommand implements ICommand { */ static decrypt = (message: Message, sharedKey: EcdhSharedKey, force = false): IDecryptMessage => { const decrypted = force - ? poseidonDecryptWithoutCheck(message.data, sharedKey, BigInt(0), 7) - : poseidonDecrypt(message.data, sharedKey, BigInt(0), 7); + ? poseidonDecryptWithoutCheck(message.data as unknown as bigint[], sharedKey, BigInt(0), 7) + : poseidonDecrypt(message.data as unknown as bigint[], sharedKey, BigInt(0), 7); const p = BigInt(decrypted[0].toString()); diff --git a/packages/hardhat/maci-ts/domainobjs/message.ts b/packages/hardhat/maci-ts/domainobjs/message.ts index de06c32..2a4ecc4 100644 --- a/packages/hardhat/maci-ts/domainobjs/message.ts +++ b/packages/hardhat/maci-ts/domainobjs/message.ts @@ -11,7 +11,7 @@ import type { IMessageContractParams } from "./types"; export class Message { msgType: bigint; - data: bigint[]; + data: [bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint]; static DATA_LENGTH = 10; @@ -20,7 +20,7 @@ export class Message { * @param msgType the type of the message * @param data the data of the message */ - constructor(msgType: bigint, data: bigint[]) { + constructor(msgType: bigint, data: [bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint]) { assert(data.length === Message.DATA_LENGTH); this.msgType = msgType; this.data = data; @@ -37,8 +37,8 @@ export class Message { * @returns the message as a contract param */ asContractParam = (): IMessageContractParams => ({ - msgType: this.msgType.toString(), - data: this.data, + msgType: this.msgType, + data: this.data as [bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint], }); /** @@ -61,7 +61,18 @@ export class Message { copy = (): Message => new Message( BigInt(this.msgType.toString()), - this.data.map((x: bigint) => BigInt(x.toString())), + this.data.map((x: bigint) => BigInt(x.toString())) as [ + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + ], ); /** @@ -95,7 +106,7 @@ export class Message { static fromJSON(json: IMessageContractParams): Message { return new Message( BigInt(json.msgType), - json.data.map(x => BigInt(x)), + json.data.map(x => BigInt(x)) as [bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint], ); } } diff --git a/packages/hardhat/maci-ts/domainobjs/types.ts b/packages/hardhat/maci-ts/domainobjs/types.ts index b779d95..c12b2d9 100644 --- a/packages/hardhat/maci-ts/domainobjs/types.ts +++ b/packages/hardhat/maci-ts/domainobjs/types.ts @@ -79,12 +79,12 @@ export interface IStateLeafContractParams { } export interface IMessageContractParams { - msgType: string; - data: BigNumberish[]; + msgType: BigNumberish; + data: [bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint]; } export interface IJsonBallot { - votes: BigNumberish[]; - nonce: BigNumberish; + votes: string[]; + nonce: string; voteOptionTreeDepth: number; } diff --git a/packages/hardhat/maci-ts/ts/constants.ts b/packages/hardhat/maci-ts/ts/constants.ts index 6d00761..913ee5c 100644 --- a/packages/hardhat/maci-ts/ts/constants.ts +++ b/packages/hardhat/maci-ts/ts/constants.ts @@ -4,3 +4,11 @@ import path from "path"; export const abiDir = path.resolve(__dirname, "..", "..", "artifacts"); // The directory where the contract source files are stored. export const solDir = path.resolve(__dirname, "..", "contracts"); + +/** + * Supported verification key modes + */ +export enum EMode { + QV, + NON_QV, +} diff --git a/packages/hardhat/maci-ts/ts/deploy.ts b/packages/hardhat/maci-ts/ts/deploy.ts index ca8dd16..df1462e 100644 --- a/packages/hardhat/maci-ts/ts/deploy.ts +++ b/packages/hardhat/maci-ts/ts/deploy.ts @@ -1,7 +1,6 @@ import { type ContractFactory, type Signer, BaseContract } from "ethers"; import type { IDeployMaciArgs, IDeployedMaci, IDeployedPoseidonContracts } from "./types"; -import hre from "hardhat"; import { AccQueueQuinaryMaci, @@ -15,7 +14,6 @@ import { MockVerifier, PollFactory, MessageProcessorFactory, - SubsidyFactory, TallyFactory, PoseidonT3, PoseidonT4, @@ -26,7 +24,6 @@ import { TopupCredit, Verifier, VkRegistry, - TallyNonQvFactory, } from "../../typechain-types"; import { parseArtifact } from "./abi"; @@ -53,8 +50,9 @@ export const linkPoseidonLibraries = async ( quiet = false, ): Promise => { log(`Linking Poseidon libraries to ${solFileToLink}`, quiet); + const { ethers } = await import("hardhat"); - const contractFactory = await hre.ethers.getContractFactory(solFileToLink, { + const contractFactory = await ethers.getContractFactory(solFileToLink, { signer: signer || (await getDefaultSigner()), libraries: { PoseidonT3: poseidonT3Address, @@ -81,8 +79,9 @@ export const deployContract = async ( ...args: unknown[] ): Promise => { log(`Deploying ${contractName}`, quiet); + const { ethers } = await import("hardhat"); - const contractFactory = await hre.ethers.getContractFactory(contractName, signer || (await getDefaultSigner())); + const contractFactory = await ethers.getContractFactory(contractName, signer || (await getDefaultSigner())); const feeData = await getFeeData(); const contract = await contractFactory.deploy(...args, { maxFeePerGas: feeData?.maxFeePerGas, @@ -264,7 +263,6 @@ export const deployMaci = async ({ signer, poseidonAddresses, stateTreeDepth = 10, - useQv = true, quiet = true, }: IDeployMaciArgs): Promise => { const { PoseidonT3Contract, PoseidonT4Contract, PoseidonT5Contract, PoseidonT6Contract } = @@ -282,14 +280,7 @@ export const deployMaci = async ({ poseidonT6, })); - const contractsToLink = [ - "MACI", - "PollFactory", - "MessageProcessorFactory", - "TallyFactory", - "TallyNonQvFactory", - "SubsidyFactory", - ]; + const contractsToLink = ["MACI", "PollFactory", "MessageProcessorFactory", "TallyFactory"]; // Link Poseidon contracts to MACI const linkedContractFactories = await Promise.all( @@ -306,14 +297,9 @@ export const deployMaci = async ({ ), ); - const [ - maciContractFactory, - pollFactoryContractFactory, - messageProcessorFactory, - tallyFactory, - tallyFactoryNonQv, - subsidyFactory, - ] = await Promise.all(linkedContractFactories); + const [maciContractFactory, pollFactoryContractFactory, messageProcessorFactory, tallyFactory] = await Promise.all( + linkedContractFactories, + ); const pollFactoryContract = await deployContractWithLinkedLibraries( pollFactoryContractFactory, @@ -329,21 +315,16 @@ 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 subsidyFactoryContract = await deployContractWithLinkedLibraries( - subsidyFactory, - "SubsidyFactory", + const tallyFactoryContract = await deployContractWithLinkedLibraries( + tallyFactory, + "TallyFactory", quiet, ); - const [pollAddr, mpAddr, tallyAddr, subsidyAddr] = await Promise.all([ + const [pollAddr, mpAddr, tallyAddr] = await Promise.all([ pollFactoryContract.getAddress(), messageProcessorFactoryContract.getAddress(), tallyFactoryContract.getAddress(), - subsidyFactoryContract.getAddress(), ]); const maciContract = await deployContractWithLinkedLibraries( @@ -353,7 +334,6 @@ export const deployMaci = async ({ pollAddr, mpAddr, tallyAddr, - subsidyAddr, signUpTokenGatekeeperContractAddress, initialVoiceCreditBalanceAddress, topupCreditContractAddress, diff --git a/packages/hardhat/maci-ts/ts/genMaciState.ts b/packages/hardhat/maci-ts/ts/genMaciState.ts index 5cc2fb8..3163f6d 100644 --- a/packages/hardhat/maci-ts/ts/genMaciState.ts +++ b/packages/hardhat/maci-ts/ts/genMaciState.ts @@ -174,7 +174,6 @@ export const genMaciStateFromContract = async ( }; const batchSizes = { tallyBatchSize: STATE_TREE_ARITY ** Number(onChainTreeDepths.intStateTreeDepth), - subsidyBatchSize: STATE_TREE_ARITY ** Number(onChainTreeDepths.intStateTreeDepth), messageBatchSize: STATE_TREE_ARITY ** Number(onChainTreeDepths.messageTreeSubDepth), }; @@ -229,7 +228,18 @@ export const genMaciStateFromContract = async ( const message = new Message( BigInt(event.args._message[0]), - event.args._message[1].map(x => BigInt(x)), + event.args._message[1].map(x => BigInt(x)) as [ + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + ], ); const encPubKey = new PubKey(event.args._encPubKey.map(x => BigInt(x.toString())) as [bigint, bigint]); @@ -253,7 +263,18 @@ export const genMaciStateFromContract = async ( }; const message = new Message( BigInt(event.args._message[0]), - event.args._message[1].map(x => BigInt(x)), + event.args._message[1].map(x => BigInt(x)) as [ + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + bigint, + ], ); actions.push({ diff --git a/packages/hardhat/maci-ts/ts/index.ts b/packages/hardhat/maci-ts/ts/index.ts index e4013a8..b6db2f8 100644 --- a/packages/hardhat/maci-ts/ts/index.ts +++ b/packages/hardhat/maci-ts/ts/index.ts @@ -17,7 +17,7 @@ export { export { genJsonRpcDeployer } from "./deployer"; export { genMaciStateFromContract } from "./genMaciState"; export { formatProofForVerifierContract, getDefaultSigner, getDefaultNetwork, getSigners } from "./utils"; -export { abiDir, solDir } from "./constants"; +export { abiDir, solDir, EMode } from "./constants"; export type { IVerifyingKeyStruct, SnarkProof, Groth16Proof } from "./types"; export * from "../../typechain-types"; diff --git a/packages/hardhat/maci-ts/ts/types.ts b/packages/hardhat/maci-ts/ts/types.ts index a09dee6..a1b7cf5 100644 --- a/packages/hardhat/maci-ts/ts/types.ts +++ b/packages/hardhat/maci-ts/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; } /**