From e84f61047ff4d7adacff702cda4647715b7e5180 Mon Sep 17 00:00:00 2001 From: aleksandar-veljkovic <97101657+aleksandar-veljkovic@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:11:09 +0200 Subject: [PATCH] feat: anonymous poll joining milestone 1 (#1625) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(poll): add chain hash features BREAKING CHANGE: message processing is changed * fix(ipoll): add missing parameter * fix(poll-tests): add missing parameter maxMessagebatchSize * feat(poll.ts): add chain hash updating * test(poll tests): add test for checking chain hash computation * feat(poll.ts): add batch hashes array computation * feat(poll.sol): pad zeroes to the maximum size of batch * feat(messageprocessor): update process messages to use chain hash * refactor(vkregistry): refactor function call * feat(processmessages.circom): add chainHash feature in circuits and test for that * test(processmessages): rearrange test for key-change * refactor(mergemessages): refactor functions calls which include mergemessages * refactor(mergemessages): add some more changes about functions call which include mergemessages * test(all tests): fixing tests after refactoring code * refactor(accqueue): remove all calls for accqueue * fix(currentmessagebatchindex): fix message batch indexing * refactor(circuit tests): refactor code for circuit testing * test(ceremonyparams.test): correct constants for CeremonyParams test * perf(processmessages.circom + contracts): optimize last batch padding, remove unused inputs * docs(padlastbatch method): update doc comment * docs(poll.ts): remove stale comments * docs(test comments): fix typos * ci(treedepths mock): modify interface for mocked function * fix(ceremony params test): fix circuit inputs * test(messagevalidator): fix function calls for messagevalidator circuit in tests * chore(comments): fix unusefull comments * refactor(poll.sol): replace external contracts with maci only * perf(messageprocessor.sol): hardcode initialization for batchHashes array * docs(comments): fix some more comments * test(test for pr checks): correct some of tests for PR checks * ci: 🎡 renamed old ProcessMessages_10-2-1-2_test * ci: 🎡 correct rapidsnark/build/prover path * style(reviews): solve some reviews for merging * refactor(messageaqq): remove more message merging and message aqq * style(messageaqq): remove more message merging and message aqq * refactor(messageaqq): remove message aqq from subgraph * test(coordinator): hide NOT_MERGED_MESSAGE_TREE error * test(coordinator): fix test about message merging * test(proveonchain): change chainHash calculation * test(proveonchain): fix chainHashes declaration * test(proveonchain): fix chainHash calculation * test(proveonchain): fix chainHashes calculations * test(proveonchain): fix chainHashes calculation * test(proveonchain): fix loop limit * style(review comments): resolve some of review comments * style(review comments): resolve some of review comments * test(lint:ts): fix e2e test because of lint:ts check * docs(wrong changes): fix wrong changes about documentation that is not in our scope * refactor(batchsizes): change batchSizes struct with messageBatchSize variable * refactor(contracts): rollback to provide external contract references * docs(messageprocessor.sol): fix typo * refactor(messagebatchsize): chenge messageBatchSize location from Params.sol to Poll.sol * refactor(maxmessages): remove maxMessages from maxValues * refactor(sltimestemp): remove slTimestamp from circuits * refactor(review comments): resolve more review comments * fix(subgraph): fix bug about maxVoteOptions dunction call * fix(sltimestamp): fix test for removing slTimestap signal * refactor(promise.all): refactor promise.all for only one async call * fix(subgraph): try to fix subgraph build * revert(.nx folder): remove .nx folder from cli folder --------- Co-authored-by: radojevicMihailo Co-authored-by: Aleksandar Veljković Co-authored-by: Boris Cvitak --- .github/workflows/coordinator-build.yml | 2 +- circuits/circom/circuits.json | 8 +- .../circom/core/non-qv/processMessages.circom | 98 +-- .../circom/core/qv/processMessages.circom | 95 +-- .../test/ProcessMessages_10-2-1-2_test.circom | 2 +- .../utils/non-qv/messageValidator.circom | 14 +- .../stateLeafAndBallotTransformer.circom | 6 - .../utils/processMessagesInputHasher.circom | 10 +- .../circom/utils/qv/messageValidator.circom | 16 +- .../qv/stateLeafAndBallotTransformer.circom | 6 - circuits/package.json | 1 - circuits/ts/__tests__/CeremonyParams.test.ts | 49 +- .../ts/__tests__/MessageValidator.test.ts | 26 - circuits/ts/__tests__/ProcessMessages.test.ts | 182 ++--- .../StateLeafAndBallotTransformer.test.ts | 14 - circuits/ts/__tests__/TallyVotes.test.ts | 8 +- circuits/ts/__tests__/utils/constants.ts | 8 +- circuits/ts/__tests__/utils/types.ts | 2 - circuits/ts/types.ts | 4 +- cli/testScript.sh | 7 +- .../ceremony-params/ceremonyParams.test.ts | 17 +- cli/tests/constants.ts | 36 +- cli/tests/e2e/e2e.nonQv.test.ts | 3 - cli/tests/e2e/e2e.test.ts | 16 - cli/tests/e2e/keyChange.test.ts | 5 - cli/tests/unit/poll.test.ts | 2 - cli/ts/commands/checkVerifyingKeys.ts | 5 +- cli/ts/commands/deploy.ts | 2 +- cli/ts/commands/deployPoll.ts | 15 +- cli/ts/commands/genLocalState.ts | 19 +- cli/ts/commands/genProofs.ts | 25 +- cli/ts/commands/index.ts | 1 - cli/ts/commands/mergeMessages.ts | 119 ---- cli/ts/commands/pollJoining.ts | 0 cli/ts/commands/proveOnChain.ts | 72 +- cli/ts/commands/publish.ts | 9 +- cli/ts/commands/setVerifyingKeys.ts | 45 +- cli/ts/index.ts | 43 +- cli/ts/sdk/index.ts | 2 - cli/ts/utils/index.ts | 1 - cli/ts/utils/interfaces.ts | 57 +- contracts/.nx/workspace-data/file-map.json | 572 ++++++++++++++++ .../.nx/workspace-data/project-graph.json | 6 + contracts/contracts/MACI.sol | 25 +- contracts/contracts/MessageProcessor.sol | 142 ++-- contracts/contracts/Poll.sol | 90 ++- contracts/contracts/PollFactory.sol | 24 +- contracts/contracts/Tally.sol | 8 +- contracts/contracts/VkRegistry.sol | 28 +- contracts/contracts/interfaces/IMACI.sol | 2 - contracts/contracts/interfaces/IPoll.sol | 42 +- .../contracts/interfaces/IPollFactory.sol | 8 +- .../contracts/interfaces/IVkRegistry.sol | 4 +- contracts/contracts/trees/AccQueue.sol | 467 ------------- contracts/contracts/trees/AccQueueBinary.sol | 59 -- contracts/contracts/trees/AccQueueBinary0.sol | 22 - .../contracts/trees/AccQueueBinaryMaci.sol | 21 - contracts/contracts/trees/AccQueueQuinary.sol | 82 --- .../contracts/trees/AccQueueQuinary0.sol | 22 - .../trees/AccQueueQuinaryBlankSl.sol | 22 - .../contracts/trees/AccQueueQuinaryMaci.sol | 22 - contracts/contracts/utilities/Params.sol | 10 - contracts/deploy-config-example.json | 36 +- contracts/package.json | 2 - contracts/tasks/deploy/maci/09-vkRegistry.ts | 2 - contracts/tasks/deploy/poll/01-poll.ts | 35 +- contracts/tasks/helpers/ProofGenerator.ts | 25 +- contracts/tasks/helpers/Prover.ts | 78 +-- contracts/tasks/helpers/TreeMerger.ts | 75 +-- contracts/tasks/helpers/types.ts | 20 +- contracts/tasks/runner/merge.ts | 20 +- contracts/tasks/runner/prove.ts | 24 +- contracts/tests/AccQueue.test.ts | 496 -------------- contracts/tests/AccQueueBenchmark.test.ts | 290 -------- contracts/tests/MACI.test.ts | 7 +- contracts/tests/MessageProcessor.test.ts | 61 +- contracts/tests/Poll.test.ts | 81 +-- contracts/tests/PollFactory.test.ts | 16 +- contracts/tests/Tally.test.ts | 30 +- contracts/tests/TallyNonQv.test.ts | 16 +- contracts/tests/VkRegistry.test.ts | 9 - contracts/tests/constants.ts | 11 +- contracts/tests/utils.ts | 437 +----------- contracts/ts/genMaciState.ts | 76 +-- coordinator/.env.example | 2 +- coordinator/tests/app.test.ts | 39 +- .../ts/proof/__tests__/proof.service.test.ts | 8 - coordinator/ts/proof/proof.service.ts | 21 +- core/ts/MaciState.ts | 8 +- core/ts/Poll.ts | 228 +++---- core/ts/__benchmarks__/index.ts | 2 +- core/ts/__benchmarks__/utils/constants.ts | 3 - core/ts/__tests__/MaciState.test.ts | 44 +- core/ts/__tests__/Poll.test.ts | 38 +- core/ts/__tests__/e2e.test.ts | 79 +-- core/ts/__tests__/utils.test.ts | 4 +- core/ts/__tests__/utils/constants.ts | 5 +- core/ts/__tests__/utils/utils.ts | 2 +- core/ts/index.ts | 2 +- core/ts/utils/constants.ts | 2 +- core/ts/utils/types.ts | 16 +- core/ts/utils/utils.ts | 13 +- crypto/package.json | 1 - crypto/ts/AccQueue.ts | 627 ------------------ crypto/ts/__tests__/AccQueue.test.ts | 319 --------- crypto/ts/__tests__/utils.ts | 132 ---- crypto/ts/index.ts | 2 - .../ts/__tests__/data/suites.json | 2 +- .../ts/__tests__/integration.test.ts | 41 +- .../ts/__tests__/maci-keys.test.ts | 6 +- .../ts/__tests__/utils/constants.ts | 6 +- subgraph/schemas/schema.v1.graphql | 5 - subgraph/src/maci.ts | 5 +- subgraph/src/poll.ts | 22 - subgraph/templates/subgraph.template.yaml | 4 - subgraph/tests/common.ts | 7 +- subgraph/tests/poll/poll.test.ts | 36 +- subgraph/tests/poll/utils.ts | 25 +- website/static/img/completingAPoll.svg | 2 +- 119 files changed, 1449 insertions(+), 4886 deletions(-) delete mode 100644 cli/ts/commands/mergeMessages.ts create mode 100644 cli/ts/commands/pollJoining.ts create mode 100644 contracts/.nx/workspace-data/file-map.json create mode 100644 contracts/.nx/workspace-data/project-graph.json delete mode 100644 contracts/contracts/trees/AccQueue.sol delete mode 100644 contracts/contracts/trees/AccQueueBinary.sol delete mode 100644 contracts/contracts/trees/AccQueueBinary0.sol delete mode 100644 contracts/contracts/trees/AccQueueBinaryMaci.sol delete mode 100644 contracts/contracts/trees/AccQueueQuinary.sol delete mode 100644 contracts/contracts/trees/AccQueueQuinary0.sol delete mode 100644 contracts/contracts/trees/AccQueueQuinaryBlankSl.sol delete mode 100644 contracts/contracts/trees/AccQueueQuinaryMaci.sol delete mode 100644 contracts/tests/AccQueue.test.ts delete mode 100644 contracts/tests/AccQueueBenchmark.test.ts delete mode 100644 crypto/ts/AccQueue.ts delete mode 100644 crypto/ts/__tests__/AccQueue.test.ts delete mode 100644 crypto/ts/__tests__/utils.ts diff --git a/.github/workflows/coordinator-build.yml b/.github/workflows/coordinator-build.yml index cb525c6e78..a8880f888d 100644 --- a/.github/workflows/coordinator-build.yml +++ b/.github/workflows/coordinator-build.yml @@ -10,7 +10,7 @@ env: COORDINATOR_PUBLIC_KEY_PATH: "./pub.key" COORDINATOR_PRIVATE_KEY_PATH: "./priv.key" COORDINATOR_TALLY_ZKEY_NAME: "TallyVotes_10-1-2_test" - COORDINATOR_MESSAGE_PROCESS_ZKEY_NAME: "ProcessMessages_10-2-1-2_test" + COORDINATOR_MESSAGE_PROCESS_ZKEY_NAME: "ProcessMessages_10-20-2_test" COORDINATOR_ZKEY_PATH: "./zkeys/" COORDINATOR_RAPIDSNARK_EXE: "~/rapidsnark/build/prover" diff --git a/circuits/circom/circuits.json b/circuits/circom/circuits.json index b6dbd207b0..4248803b9f 100644 --- a/circuits/circom/circuits.json +++ b/circuits/circom/circuits.json @@ -1,14 +1,14 @@ { - "ProcessMessages_10-2-1-2_test": { + "ProcessMessages_10-20-2_test": { "file": "./core/qv/processMessages", "template": "ProcessMessages", - "params": [10, 2, 1, 2], + "params": [10, 20, 2], "pubs": ["inputHash"] }, - "ProcessMessagesNonQv_10-2-1-2_test": { + "ProcessMessagesNonQv_10-20-2_test": { "file": "./core/non-qv/processMessages", "template": "ProcessMessagesNonQv", - "params": [10, 2, 1, 2], + "params": [10, 20, 2], "pubs": ["inputHash"] }, "TallyVotes_10-1-2_test": { diff --git a/circuits/circom/core/non-qv/processMessages.circom b/circuits/circom/core/non-qv/processMessages.circom index 4d9a49c9f3..d98f3eabba 100644 --- a/circuits/circom/core/non-qv/processMessages.circom +++ b/circuits/circom/core/non-qv/processMessages.circom @@ -15,27 +15,22 @@ include "../../trees/incrementalQuinaryTree.circom"; /** * Proves the correctness of processing a batch of MACI messages. - * The msgBatchDepth parameter is known as msgSubtreeDepth and indicates the depth - * of the shortest tree that can fit all the messages in a batch. * This template does not support Quadratic Voting (QV). */ template ProcessMessagesNonQv( stateTreeDepth, - msgTreeDepth, - msgBatchDepth, + batchSize, voteOptionTreeDepth ) { // Must ensure that the trees have a valid structure. assert(stateTreeDepth > 0); - assert(msgBatchDepth > 0); + assert(batchSize > 0); assert(voteOptionTreeDepth > 0); - assert(msgTreeDepth >= msgBatchDepth); // Default for IQT (quinary trees). - var MESSAGE_TREE_ARITY = 5; + var VOTE_OPTION_TREE_ARITY = 5; // Default for Binary trees. var STATE_TREE_ARITY = 2; - var batchSize = MESSAGE_TREE_ARITY ** msgBatchDepth; var MSG_LENGTH = 10; var PACKED_CMD_LENGTH = 4; var STATE_LEAF_LENGTH = 4; @@ -59,14 +54,12 @@ include "../../trees/incrementalQuinaryTree.circom"; signal numSignUps; // Number of options for this poll. signal maxVoteOptions; - // Time when the poll ends. - signal input pollEndTimestamp; - // The existing message tree root. - signal input msgRoot; + // Value of chainHash at beginning of batch + signal input inputBatchHash; + // Value of chainHash at end of batch + signal input outputBatchHash; // The messages. signal input msgs[batchSize][MSG_LENGTH]; - // Sibling messages. - signal input msgSubrootPathElements[msgTreeDepth - msgBatchDepth][MESSAGE_TREE_ARITY - 1]; // The coordinator's private key. signal input coordPrivKey; // The cooordinator's public key (derived from the contract). @@ -104,16 +97,16 @@ include "../../trees/incrementalQuinaryTree.circom"; signal input currentBallotsPathElements[batchSize][stateTreeDepth][STATE_TREE_ARITY - 1]; // Intermediate vote weights. signal input currentVoteWeights[batchSize]; - signal input currentVoteWeightsPathElements[batchSize][voteOptionTreeDepth][MESSAGE_TREE_ARITY - 1]; + signal input currentVoteWeightsPathElements[batchSize][voteOptionTreeDepth][VOTE_OPTION_TREE_ARITY - 1]; // nb. The messages are processed in REVERSE order. // Therefore, the index of the first message to process does not match the index of the // first message (e.g., [msg1, msg2, msg3] => first message to process has index 3). - // The index of the first message leaf in the batch, inclusive. + // The index of the first message in the batch, inclusive. signal batchStartIndex; - // The index of the last message leaf in the batch to process, exclusive. + // The index of the last message in the batch to process, exclusive. // This value may be less than batchStartIndex + batchSize if this batch is // the last batch and the total number of messages is not a multiple of the batch size. signal batchEndIndex; @@ -135,16 +128,15 @@ include "../../trees/incrementalQuinaryTree.circom"; var ( computedMaxVoteOptions, computedNumSignUps, - computedBatchStartIndex, + computedBatchStartIndex, computedBatchEndIndex, computedHash ) = ProcessMessagesInputHasher()( packedVals, coordPubKey, - msgRoot, + outputBatchHash, currentSbCommitment, newSbCommitment, - pollEndTimestamp, actualStateTreeDepth ); @@ -159,7 +151,7 @@ include "../../trees/incrementalQuinaryTree.circom"; // ----------------------------------------------------------------------- // 0. Ensure that the maximum vote options signal is valid and if // the maximum users signal is valid. - var maxVoValid = LessEqThan(32)([maxVoteOptions, MESSAGE_TREE_ARITY ** voteOptionTreeDepth]); + var maxVoValid = LessEqThan(32)([maxVoteOptions, VOTE_OPTION_TREE_ARITY ** voteOptionTreeDepth]); maxVoValid === 1; // Check numSignUps <= the max number of users (i.e., number of state leaves @@ -167,52 +159,29 @@ include "../../trees/incrementalQuinaryTree.circom"; var numSignUpsValid = LessEqThan(32)([numSignUps, STATE_TREE_ARITY ** stateTreeDepth]); numSignUpsValid === 1; - // Hash each Message to check their existence in the Message tree. + // Hash each Message to check their existence in the Message chain hash. var computedMessageHashers[batchSize]; + var computedChainHashes[batchSize]; + var chainHash[batchSize + 1]; + chainHash[0] = inputBatchHash; for (var i = 0; i < batchSize; i++) { + // calculate message hash computedMessageHashers[i] = MessageHasher()(msgs[i], encPubKeys[i]); + // check if message is valid or not (if index of message is less than index of last valid message in batch) + var batchStartIndexValid = SafeLessThan(32)([batchStartIndex + i, batchEndIndex]); + // calculate chain hash if message is valid + computedChainHashes[i] = PoseidonHasher(2)([chainHash[i], computedMessageHashers[i]]); + // choose between old chain hash value and new chain hash value depending if message is valid or not + chainHash[i + 1] = Mux1()([chainHash[i], computedChainHashes[i]], batchStartIndexValid); } - // If batchEndIndex - batchStartIndex < batchSize, the remaining + // If batchEndIndex < batchStartIndex + i, the remaining // message hashes should be the zero value. // e.g. [m, z, z, z, z] if there is only 1 real message in the batch // This makes possible to have a batch of messages which is only partially full. - var computedLeaves[batchSize]; - var computedPathElements[msgTreeDepth - msgBatchDepth][MESSAGE_TREE_ARITY - 1]; - var computedPathIndex[msgTreeDepth - msgBatchDepth]; - - for (var i = 0; i < batchSize; i++) { - var batchStartIndexValid = SafeLessThan(32)([batchStartIndex + i, batchEndIndex]); - computedLeaves[i] = Mux1()([msgTreeZeroValue, computedMessageHashers[i]], batchStartIndexValid); - } - - for (var i = 0; i < msgTreeDepth - msgBatchDepth; i++) { - for (var j = 0; j < MESSAGE_TREE_ARITY - 1; j++) { - computedPathElements[i][j] = msgSubrootPathElements[i][j]; - } - } - - // Computing the path_index values. Since msgBatchLeavesExists tests - // the existence of a subroot, the length of the proof correspond to the last - // n elements of a proof from the root to a leaf, where n = msgTreeDepth - msgBatchDepth. - // e.g. if batchStartIndex = 25, msgTreeDepth = 4, msgBatchDepth = 2, then path_index = [1, 0]. - var computedMsgBatchPathIndices[msgTreeDepth] = QuinGeneratePathIndices(msgTreeDepth)(batchStartIndex); - for (var i = msgBatchDepth; i < msgTreeDepth; i++) { - computedPathIndex[i - msgBatchDepth] = computedMsgBatchPathIndices[i]; - } - - // Check whether each message exists in the Message tree. - // Otherwise, throws (needs constraint to prevent such a proof). - // To save constraints, compute the subroot of the messages and check - // whether the subroot is a member of the message tree. This means that - // batchSize must be the message tree arity raised to some power (e.g. 5 ^ n). - QuinBatchLeavesExists(msgTreeDepth, msgBatchDepth)( - msgRoot, - computedLeaves, - computedPathIndex, - computedPathElements - ); + // Ensure that right output batch hash was sent to circuit + chainHash[batchSize] === outputBatchHash; // Decrypt each Message to a Command. // MessageToCommand derives the ECDH shared key from the coordinator's @@ -274,7 +243,7 @@ include "../../trees/incrementalQuinaryTree.circom"; // Process as vote type message. var currentStateLeavesPathElement[stateTreeDepth][STATE_TREE_ARITY - 1]; var currentBallotPathElement[stateTreeDepth][STATE_TREE_ARITY - 1]; - var currentVoteWeightsPathElement[voteOptionTreeDepth][MESSAGE_TREE_ARITY - 1]; + var currentVoteWeightsPathElement[voteOptionTreeDepth][VOTE_OPTION_TREE_ARITY - 1]; for (var j = 0; j < stateTreeDepth; j++) { for (var k = 0; k < STATE_TREE_ARITY - 1; k++) { @@ -284,7 +253,7 @@ include "../../trees/incrementalQuinaryTree.circom"; } for (var j = 0; j < voteOptionTreeDepth; j++) { - for (var k = 0; k < MESSAGE_TREE_ARITY - 1; k++) { + for (var k = 0; k < VOTE_OPTION_TREE_ARITY - 1; k++) { currentVoteWeightsPathElement[j][k] = currentVoteWeightsPathElements[i][j][k]; } } @@ -292,7 +261,6 @@ include "../../trees/incrementalQuinaryTree.circom"; (computedNewVoteStateRoot[i], computedNewVoteBallotRoot[i]) = ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth)( numSignUps, maxVoteOptions, - pollEndTimestamp, stateRoots[i + 1], ballotRoots[i + 1], actualStateTreeDepth, @@ -336,7 +304,7 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) { var BALLOT_LENGTH = 2; var MSG_LENGTH = 10; var PACKED_CMD_LENGTH = 4; - var MESSAGE_TREE_ARITY = 5; + var VOTE_OPTION_TREE_ARITY = 5; var STATE_TREE_ARITY = 2; var BALLOT_NONCE_IDX = 0; // Ballot vote option (VO) root index. @@ -355,7 +323,6 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) { // Inputs representing the message and the current state. signal input numSignUps; signal input maxVoteOptions; - signal input pollEndTimestamp; // The current value of the state tree root. signal input currentStateRoot; @@ -375,7 +342,7 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) { // The current vote weight and related path elements. signal input currentVoteWeight; - signal input currentVoteWeightsPathElements[voteOptionTreeDepth][MESSAGE_TREE_ARITY - 1]; + signal input currentVoteWeightsPathElements[voteOptionTreeDepth][VOTE_OPTION_TREE_ARITY - 1]; // Inputs related to the command being processed. signal input cmdStateIndex; @@ -408,8 +375,7 @@ template ProcessOneNonQv(stateTreeDepth, voteOptionTreeDepth) { maxVoteOptions, [stateLeaf[STATE_LEAF_PUB_X_IDX], stateLeaf[STATE_LEAF_PUB_Y_IDX]], stateLeaf[STATE_LEAF_VOICE_CREDIT_BALANCE_IDX], - stateLeaf[STATE_LEAF_TIMESTAMP_IDX], - pollEndTimestamp, + // stateLeaf[STATE_LEAF_TIMESTAMP_IDX], ballot[BALLOT_NONCE_IDX], currentVoteWeight, cmdStateIndex, diff --git a/circuits/circom/core/qv/processMessages.circom b/circuits/circom/core/qv/processMessages.circom index 8aeeae7d5f..41ce79f482 100644 --- a/circuits/circom/core/qv/processMessages.circom +++ b/circuits/circom/core/qv/processMessages.circom @@ -15,27 +15,22 @@ include "../../trees/incrementalMerkleTree.circom"; /** * Proves the correctness of processing a batch of MACI messages. - * The msgBatchDepth parameter is known as msgSubtreeDepth and indicates the depth - * of the shortest tree that can fit all the messages in a batch. * This template supports the Quadratic Voting (QV). */ template ProcessMessages( stateTreeDepth, - msgTreeDepth, - msgBatchDepth, + batchSize, voteOptionTreeDepth ) { // Must ensure that the trees have a valid structure. assert(stateTreeDepth > 0); - assert(msgBatchDepth > 0); + assert(batchSize > 0); assert(voteOptionTreeDepth > 0); - assert(msgTreeDepth >= msgBatchDepth); // Default for IQT (quinary trees). - var MESSAGE_TREE_ARITY = 5; + var VOTE_OPTION_TREE_ARITY = 5; // Default for binary trees. var STATE_TREE_ARITY = 2; - var batchSize = MESSAGE_TREE_ARITY ** msgBatchDepth; var MSG_LENGTH = 10; var PACKED_CMD_LENGTH = 4; var STATE_LEAF_LENGTH = 4; @@ -59,14 +54,12 @@ template ProcessMessages( signal numSignUps; // Number of options for this poll. signal maxVoteOptions; - // Time when the poll ends. - signal input pollEndTimestamp; - // The existing message tree root. - signal input msgRoot; + // Value of chainHash at beginning of batch + signal input inputBatchHash; + // Value of chainHash at end of batch + signal input outputBatchHash; // The messages. signal input msgs[batchSize][MSG_LENGTH]; - // Sibling messages. - signal input msgSubrootPathElements[msgTreeDepth - msgBatchDepth][MESSAGE_TREE_ARITY - 1]; // The coordinator's private key. signal input coordPrivKey; // The cooordinator's public key (derived from the contract). @@ -104,17 +97,17 @@ template ProcessMessages( signal input currentBallotsPathElements[batchSize][stateTreeDepth][STATE_TREE_ARITY - 1]; // Intermediate vote weights. signal input currentVoteWeights[batchSize]; - signal input currentVoteWeightsPathElements[batchSize][voteOptionTreeDepth][MESSAGE_TREE_ARITY - 1]; + signal input currentVoteWeightsPathElements[batchSize][voteOptionTreeDepth][VOTE_OPTION_TREE_ARITY - 1]; // nb. The messages are processed in REVERSE order. // Therefore, the index of the first message to process does not match the index of the // first message (e.g., [msg1, msg2, msg3] => first message to process has index 3). - // The index of the first message leaf in the batch, inclusive. + // The index of the first message in the batch, inclusive. signal batchStartIndex; - // The index of the last message leaf in the batch to process, exclusive. - // This value may be less than batchStartIndex + batchSize if this batch is + // The index of the last message in the batch to process, exclusive. + // This value may be less than batchSize if this batch is // the last batch and the total number of messages is not a multiple of the batch size. signal batchEndIndex; @@ -137,10 +130,9 @@ template ProcessMessages( ) = ProcessMessagesInputHasher()( packedVals, coordPubKey, - msgRoot, + outputBatchHash, currentSbCommitment, newSbCommitment, - pollEndTimestamp, actualStateTreeDepth ); @@ -154,7 +146,7 @@ template ProcessMessages( // 0. Ensure that the maximum vote options signal is valid and if // the maximum users signal is valid. - var maxVoValid = LessEqThan(32)([maxVoteOptions, MESSAGE_TREE_ARITY ** voteOptionTreeDepth]); + var maxVoValid = LessEqThan(32)([maxVoteOptions, VOTE_OPTION_TREE_ARITY ** voteOptionTreeDepth]); maxVoValid === 1; // Check numSignUps <= the max number of users (i.e., number of state leaves @@ -164,50 +156,26 @@ template ProcessMessages( // Hash each Message to check their existence in the Message tree. var computedMessageHashers[batchSize]; + var computedChainHashes[batchSize]; + var chainHash[batchSize + 1]; + chainHash[0] = inputBatchHash; for (var i = 0; i < batchSize; i++) { + // calculate message hash computedMessageHashers[i] = MessageHasher()(msgs[i], encPubKeys[i]); + // check if message is valid or not (if index of message is less than index of last valid message in batch) + var batchStartIndexValid = SafeLessThan(32)([batchStartIndex + i, batchEndIndex]); + // calculate chain hash if message is valid + computedChainHashes[i] = PoseidonHasher(2)([chainHash[i], computedMessageHashers[i]]); + // choose between old chain hash value and new chain hash value depending if message is valid or not + chainHash[i + 1] = Mux1()([chainHash[i], computedChainHashes[i]], batchStartIndexValid); } - // If batchEndIndex - batchStartIndex < batchSize, the remaining + // If batchEndIndex < batchStartIndex + i, the remaining // message hashes should be the zero value. // e.g. [m, z, z, z, z] if there is only 1 real message in the batch // This makes possible to have a batch of messages which is only partially full. - var computedLeaves[batchSize]; - var computedPathElements[msgTreeDepth - msgBatchDepth][MESSAGE_TREE_ARITY - 1]; - var computedPathIndex[msgTreeDepth - msgBatchDepth]; - - for (var i = 0; i < batchSize; i++) { - var batchStartIndexValid = SafeLessThan(32)([batchStartIndex + i, batchEndIndex]); - computedLeaves[i] = Mux1()([msgTreeZeroValue, computedMessageHashers[i]], batchStartIndexValid); - } - - for (var i = 0; i < msgTreeDepth - msgBatchDepth; i++) { - for (var j = 0; j < MESSAGE_TREE_ARITY - 1; j++) { - computedPathElements[i][j] = msgSubrootPathElements[i][j]; - } - } - - // Computing the path_index values. Since msgBatchLeavesExists tests - // the existence of a subroot, the length of the proof correspond to the last - // n elements of a proof from the root to a leaf, where n = msgTreeDepth - msgBatchDepth. - // e.g. if batchStartIndex = 25, msgTreeDepth = 4, msgBatchDepth = 2, then path_index = [1, 0]. - var computedMsgBatchPathIndices[msgTreeDepth] = QuinGeneratePathIndices(msgTreeDepth)(batchStartIndex); - for (var i = msgBatchDepth; i < msgTreeDepth; i++) { - computedPathIndex[i - msgBatchDepth] = computedMsgBatchPathIndices[i]; - } - - // Check whether each message exists in the Message tree. - // Otherwise, throws (needs constraint to prevent such a proof). - // To save constraints, compute the subroot of the messages and check - // whether the subroot is a member of the message tree. This means that - // batchSize must be the message tree arity raised to some power (e.g. 5 ^ n). - QuinBatchLeavesExists(msgTreeDepth, msgBatchDepth)( - msgRoot, - computedLeaves, - computedPathIndex, - computedPathElements - ); + chainHash[batchSize] === outputBatchHash; // Decrypt each Message to a Command. // MessageToCommand derives the ECDH shared key from the coordinator's @@ -269,7 +237,7 @@ template ProcessMessages( // Process as vote type message. var currentStateLeavesPathElement[stateTreeDepth][STATE_TREE_ARITY - 1]; var currentBallotPathElement[stateTreeDepth][STATE_TREE_ARITY - 1]; - var currentVoteWeightsPathElement[voteOptionTreeDepth][MESSAGE_TREE_ARITY - 1]; + var currentVoteWeightsPathElement[voteOptionTreeDepth][VOTE_OPTION_TREE_ARITY - 1]; for (var j = 0; j < stateTreeDepth; j++) { for (var k = 0; k < STATE_TREE_ARITY - 1; k++) { @@ -279,7 +247,7 @@ template ProcessMessages( } for (var j = 0; j < voteOptionTreeDepth; j++) { - for (var k = 0; k < MESSAGE_TREE_ARITY - 1; k++) { + for (var k = 0; k < VOTE_OPTION_TREE_ARITY - 1; k++) { currentVoteWeightsPathElement[j][k] = currentVoteWeightsPathElements[i][j][k]; } } @@ -287,7 +255,6 @@ template ProcessMessages( (computedNewVoteStateRoot[i], computedNewVoteBallotRoot[i]) = ProcessOne(stateTreeDepth, voteOptionTreeDepth)( numSignUps, maxVoteOptions, - pollEndTimestamp, stateRoots[i + 1], ballotRoots[i + 1], actualStateTreeDepth, @@ -331,7 +298,7 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) { var BALLOT_LENGTH = 2; var MSG_LENGTH = 10; var PACKED_CMD_LENGTH = 4; - var MESSAGE_TREE_ARITY = 5; + var VOTE_OPTION_TREE_ARITY = 5; var STATE_TREE_ARITY = 2; var BALLOT_NONCE_IDX = 0; // Ballot vote option (VO) root index. @@ -350,7 +317,6 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) { // Inputs representing the message and the current state. signal input numSignUps; signal input maxVoteOptions; - signal input pollEndTimestamp; // The current value of the state tree root. signal input currentStateRoot; @@ -370,7 +336,7 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) { // The current vote weight and related path elements. signal input currentVoteWeight; - signal input currentVoteWeightsPathElements[voteOptionTreeDepth][MESSAGE_TREE_ARITY - 1]; + signal input currentVoteWeightsPathElements[voteOptionTreeDepth][VOTE_OPTION_TREE_ARITY - 1]; // Inputs related to the command being processed. signal input cmdStateIndex; @@ -403,8 +369,7 @@ template ProcessOne(stateTreeDepth, voteOptionTreeDepth) { maxVoteOptions, [stateLeaf[STATE_LEAF_PUB_X_IDX], stateLeaf[STATE_LEAF_PUB_Y_IDX]], stateLeaf[STATE_LEAF_VOICE_CREDIT_BALANCE_IDX], - stateLeaf[STATE_LEAF_TIMESTAMP_IDX], - pollEndTimestamp, + // stateLeaf[STATE_LEAF_TIMESTAMP_IDX], ballot[BALLOT_NONCE_IDX], currentVoteWeight, cmdStateIndex, diff --git a/circuits/circom/test/ProcessMessages_10-2-1-2_test.circom b/circuits/circom/test/ProcessMessages_10-2-1-2_test.circom index 843111f37a..fdcc506b12 100644 --- a/circuits/circom/test/ProcessMessages_10-2-1-2_test.circom +++ b/circuits/circom/test/ProcessMessages_10-2-1-2_test.circom @@ -3,4 +3,4 @@ pragma circom 2.0.0; include ".././core/qv/processMessages.circom"; -component main {public[inputHash]} = ProcessMessages(10, 2, 1, 2); +component main {public[inputHash]} = ProcessMessages(10, 20, 2); diff --git a/circuits/circom/utils/non-qv/messageValidator.circom b/circuits/circom/utils/non-qv/messageValidator.circom index 07c9d3238b..ae235804f3 100644 --- a/circuits/circom/utils/non-qv/messageValidator.circom +++ b/circuits/circom/utils/non-qv/messageValidator.circom @@ -33,10 +33,6 @@ template MessageValidatorNonQv() { signal input sigR8[2]; // ECDSA signature of the command (S part). signal input sigS; - // State leaf signup timestamp. - signal input slTimestamp; - // Timestamp indicating when the poll ends. - signal input pollEndTimestamp; // State leaf current voice credit balance. signal input currentVoiceCreditBalance; // Current number of votes for specific option. @@ -61,11 +57,8 @@ template MessageValidatorNonQv() { // Check (4) - The signature must be correct. var computedIsSignatureValid = VerifySignature()(pubKey, sigR8, sigS, cmd); - - // Check (5) - The state leaf must be inserted before the Poll period end. - var computedIsTimestampValid = SafeLessEqThan(252)([slTimestamp, pollEndTimestamp]); - // Check (6) - There must be sufficient voice credits. + // Check (5) - There must be sufficient voice credits. // The check ensure that currentVoiceCreditBalance + (currentVotesForOption) >= (voteWeight). var computedAreVoiceCreditsSufficient = SafeGreaterEqThan(252)( [ @@ -74,15 +67,14 @@ template MessageValidatorNonQv() { ] ); - // When all six checks are correct, then isValid = 1. + // When all five checks are correct, then isValid = 1. var computedIsUpdateValid = IsEqual()( [ - 6, + 5, computedIsSignatureValid + computedAreVoiceCreditsSufficient + computedIsNonceValid + computedIsStateLeafIndexValid + - computedIsTimestampValid + computedIsVoteOptionIndexValid ] ); diff --git a/circuits/circom/utils/non-qv/stateLeafAndBallotTransformer.circom b/circuits/circom/utils/non-qv/stateLeafAndBallotTransformer.circom index 8357644ddc..c3da7eea82 100644 --- a/circuits/circom/utils/non-qv/stateLeafAndBallotTransformer.circom +++ b/circuits/circom/utils/non-qv/stateLeafAndBallotTransformer.circom @@ -25,10 +25,6 @@ template StateLeafAndBallotTransformerNonQv() { signal input slPubKey[2]; // Current voice credit balance. signal input slVoiceCreditBalance; - // Signup timestamp. - signal input slTimestamp; - // Timestamp indicating when the poll ends. - signal input pollEndTimestamp; // The following signals represents a ballot. // Nonce. @@ -79,8 +75,6 @@ template StateLeafAndBallotTransformerNonQv() { slPubKey, cmdSigR8, cmdSigS, - slTimestamp, - pollEndTimestamp, slVoiceCreditBalance, ballotCurrentVotesForOption, cmdNewVoteWeight diff --git a/circuits/circom/utils/processMessagesInputHasher.circom b/circuits/circom/utils/processMessagesInputHasher.circom index d9dd1999a7..ec7ebbfc08 100644 --- a/circuits/circom/utils/processMessagesInputHasher.circom +++ b/circuits/circom/utils/processMessagesInputHasher.circom @@ -19,16 +19,15 @@ template ProcessMessagesInputHasher() { // Hash coordPubKey: // - coordPubKeyHash // Other inputs that can't be compressed or packed: - // - msgRoot, currentSbCommitment, newSbCommitment + // - currentSbCommitment, newSbCommitment, outputBatchHash var UNPACK_ELEM_LENGTH = 4; signal input packedVals; signal input coordPubKey[2]; - signal input msgRoot; + signal input outputBatchHash; // The current state and ballot root commitment (hash(stateRoot, ballotRoot, salt)). signal input currentSbCommitment; signal input newSbCommitment; - signal input pollEndTimestamp; signal input actualStateTreeDepth; signal output maxVoteOptions; @@ -49,13 +48,12 @@ template ProcessMessagesInputHasher() { var computedPubKey = PoseidonHasher(2)(coordPubKey); // 3. Hash the 6 inputs with SHA256. - hash <== Sha256Hasher(7)([ + hash <== Sha256Hasher(6)([ packedVals, computedPubKey, - msgRoot, + outputBatchHash, currentSbCommitment, newSbCommitment, - pollEndTimestamp, actualStateTreeDepth ]); } \ No newline at end of file diff --git a/circuits/circom/utils/qv/messageValidator.circom b/circuits/circom/utils/qv/messageValidator.circom index 7fddd10bf0..9e7910108b 100644 --- a/circuits/circom/utils/qv/messageValidator.circom +++ b/circuits/circom/utils/qv/messageValidator.circom @@ -33,10 +33,6 @@ template MessageValidator() { signal input sigR8[2]; // ECDSA signature of the command (S part). signal input sigS; - // State leaf signup timestamp. - signal input slTimestamp; - // Timestamp indicating when the poll ends. - signal input pollEndTimestamp; // State leaf current voice credit balance. signal input currentVoiceCreditBalance; // Current number of votes for specific option. @@ -62,15 +58,12 @@ template MessageValidator() { // Check (4) - The signature must be correct. var computedIsSignatureValid = VerifySignature()(pubKey, sigR8, sigS, cmd); - // Check (5) - The state leaf must be inserted before the Poll period end. - var computedIsTimestampValid = SafeLessEqThan(252)([slTimestamp, pollEndTimestamp]); - - // Check (6) - There must be sufficient voice credits. + // Check (5) - There must be sufficient voice credits. // The check ensure that the voteWeight is < sqrt(field size) // so that voteWeight ^ 2 will not overflow. var computedIsVoteWeightValid = SafeLessEqThan(252)([voteWeight, 147946756881789319005730692170996259609]); - // Check (7) - Check the current voice credit balance. + // Check (6) - Check the current voice credit balance. // The check ensure that currentVoiceCreditBalance + (currentVotesForOption ** 2) >= (voteWeight ** 2) var computedAreVoiceCreditsSufficient = SafeGreaterEqThan(252)( [ @@ -79,16 +72,15 @@ template MessageValidator() { ] ); - // When all seven checks are correct, then isValid = 1. + // When all six checks are correct, then isValid = 1. var computedIsUpdateValid = IsEqual()( [ - 7, + 6, computedIsSignatureValid + computedAreVoiceCreditsSufficient + computedIsVoteWeightValid + computedIsNonceValid + computedIsStateLeafIndexValid + - computedIsTimestampValid + computedIsVoteOptionIndexValid ] ); diff --git a/circuits/circom/utils/qv/stateLeafAndBallotTransformer.circom b/circuits/circom/utils/qv/stateLeafAndBallotTransformer.circom index c8cb4a2519..efdd60a741 100644 --- a/circuits/circom/utils/qv/stateLeafAndBallotTransformer.circom +++ b/circuits/circom/utils/qv/stateLeafAndBallotTransformer.circom @@ -25,10 +25,6 @@ template StateLeafAndBallotTransformer() { signal input slPubKey[2]; // Current voice credit balance. signal input slVoiceCreditBalance; - // Signup timestamp. - signal input slTimestamp; - // Timestamp indicating when the poll ends. - signal input pollEndTimestamp; // The following signals represents a ballot. // Nonce. @@ -79,8 +75,6 @@ template StateLeafAndBallotTransformer() { slPubKey, cmdSigR8, cmdSigS, - slTimestamp, - pollEndTimestamp, slVoiceCreditBalance, ballotCurrentVotesForOption, cmdNewVoteWeight diff --git a/circuits/package.json b/circuits/package.json index 48702bef4a..f0954b0051 100644 --- a/circuits/package.json +++ b/circuits/package.json @@ -28,7 +28,6 @@ "test:messageToCommand": "pnpm run mocha-test ts/__tests__/MessageToCommand.test.ts", "test:messageValidator": "pnpm run mocha-test ts/__tests__/MessageValidator.test.ts", "test:verifySignature": "pnpm run mocha-test ts/__tests__/VerifySignature.test.ts", - "test:splicer": "pnpm run mocha-test ts/__tests__/Splicer.test.ts", "test:privToPubKey": "pnpm run mocha-test ts/__tests__/PrivToPubKey.test.ts", "test:calculateTotal": "pnpm run mocha-test ts/__tests__/CalculateTotal.test.ts", "test:processMessages": "pnpm run mocha-test ts/__tests__/ProcessMessages.test.ts", diff --git a/circuits/ts/__tests__/CeremonyParams.test.ts b/circuits/ts/__tests__/CeremonyParams.test.ts index ebc48b08f4..35b86820e7 100644 --- a/circuits/ts/__tests__/CeremonyParams.test.ts +++ b/circuits/ts/__tests__/CeremonyParams.test.ts @@ -1,6 +1,7 @@ import { expect } from "chai"; import { type WitnessTester } from "circomkit"; -import { MaciState, Poll, packProcessMessageSmallVals, STATE_TREE_ARITY, MESSAGE_TREE_ARITY } from "maci-core"; +import { MaciState, Poll, packProcessMessageSmallVals, STATE_TREE_ARITY } from "maci-core"; +import { MESSAGE_BATCH_SIZE, VOTE_OPTION_TREE_ARITY } from "maci-core/build/ts/utils/constants"; import { hash5, IncrementalQuinTree } from "maci-crypto"; import { PrivKey, Keypair, PCommand, Message, Ballot } from "maci-domainobjs"; @@ -11,11 +12,7 @@ import { generateRandomIndex, getSignal, circomkitInstance } from "./utils/utils describe("Ceremony param tests", () => { const params = { // processMessages and Tally - stateTreeDepth: 6, - // processMessages - messageTreeDepth: 9, - // processMessages - messageBatchTreeDepth: 2, + stateTreeDepth: 14, // processMessages and Tally voteOptionTreeDepth: 3, // Tally @@ -24,19 +21,14 @@ describe("Ceremony param tests", () => { const maxValues = { maxUsers: STATE_TREE_ARITY ** params.stateTreeDepth, - maxMessages: MESSAGE_TREE_ARITY ** params.messageTreeDepth, - maxVoteOptions: MESSAGE_TREE_ARITY ** params.voteOptionTreeDepth, + maxVoteOptions: VOTE_OPTION_TREE_ARITY ** params.voteOptionTreeDepth, }; const treeDepths = { - intStateTreeDepth: params.messageBatchTreeDepth, - messageTreeDepth: params.messageTreeDepth, - messageTreeSubDepth: params.messageBatchTreeDepth, + intStateTreeDepth: 1, voteOptionTreeDepth: params.voteOptionTreeDepth, }; - const messageBatchSize = MESSAGE_TREE_ARITY ** params.messageBatchTreeDepth; - const voiceCreditBalance = BigInt(100); const duration = 30; @@ -49,10 +41,9 @@ describe("Ceremony param tests", () => { [ "inputHash", "packedVals", - "pollEndTimestamp", - "msgRoot", + "inputBatchHash", + "outputBatchHash", "msgs", - "msgSubrootPathElements", "coordPrivKey", "coordPubKey", "encPubKeys", @@ -72,7 +63,14 @@ describe("Ceremony param tests", () => { >; let hasherCircuit: WitnessTester< - ["packedVals", "coordPubKey", "msgRoot", "currentSbCommitment", "newSbCommitment", "pollEndTimestamp"], + [ + "packedVals", + "coordPubKey", + "outputBatchHash", + "currentSbCommitment", + "newSbCommitment", + "actualStateTreeDepth", + ], ["maxVoteOptions", "numSignUps", "batchStartIndex", "batchEndIndex", "hash"] >; @@ -80,7 +78,7 @@ describe("Ceremony param tests", () => { circuit = await circomkitInstance.WitnessTester("processMessages", { file: "./core/qv/processMessages", template: "ProcessMessages", - params: [6, 9, 2, 3], + params: [14, MESSAGE_BATCH_SIZE, 3], }); hasherCircuit = await circomkitInstance.WitnessTester("processMessageInputHasher", { @@ -108,9 +106,9 @@ describe("Ceremony param tests", () => { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, - messageBatchSize, + MESSAGE_BATCH_SIZE, coordinatorKeypair, ); @@ -160,7 +158,7 @@ describe("Ceremony param tests", () => { it("should produce the correct state root and ballot root", async () => { // The current roots - const emptyBallot = new Ballot(poll.maxValues.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); + const emptyBallot = new Ballot(poll.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); const emptyBallotHash = emptyBallot.hash(); const ballotTree = new IncrementalQuinTree(params.stateTreeDepth, emptyBallot.hash(), STATE_TREE_ARITY, hash5); ballotTree.insert(emptyBallot.hash()); @@ -197,10 +195,9 @@ describe("Ceremony param tests", () => { const hasherCircuitInputs = { packedVals, coordPubKey: inputs.coordPubKey, - msgRoot: inputs.msgRoot, + outputBatchHash: inputs.outputBatchHash, currentSbCommitment: inputs.currentSbCommitment, newSbCommitment: inputs.newSbCommitment, - pollEndTimestamp: inputs.pollEndTimestamp, actualStateTreeDepth: inputs.actualStateTreeDepth, }; @@ -243,7 +240,7 @@ describe("Ceremony param tests", () => { testCircuit = await circomkitInstance.WitnessTester("tallyVotes", { file: "./core/qv/tallyVotes", template: "TallyVotes", - params: [6, 2, 3], + params: [14, 1, 3], }); }); @@ -267,9 +264,9 @@ describe("Ceremony param tests", () => { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, - messageBatchSize, + MESSAGE_BATCH_SIZE, coordinatorKeypair, ); diff --git a/circuits/ts/__tests__/MessageValidator.test.ts b/circuits/ts/__tests__/MessageValidator.test.ts index 97697ff3a2..1d5e0a327f 100644 --- a/circuits/ts/__tests__/MessageValidator.test.ts +++ b/circuits/ts/__tests__/MessageValidator.test.ts @@ -27,8 +27,6 @@ describe("MessageValidator circuit", function test() { "currentVoiceCreditBalance", "currentVotesForOption", "voteWeight", - "slTimestamp", - "pollEndTimestamp", ], ["isValid"] >; @@ -70,8 +68,6 @@ describe("MessageValidator circuit", function test() { currentVoiceCreditBalance: 100n, currentVotesForOption: 0n, voteWeight: 9n, - slTimestamp: 1n, - pollEndTimestamp: 2n, }; }); @@ -144,15 +140,6 @@ describe("MessageValidator circuit", function test() { const isValid = await getSignal(circuit, witness, "isValid"); expect(isValid.toString()).to.be.eq("0"); }); - - it("should be invalid if the state leaf timestamp is too high", async () => { - const circuitInputs2 = circuitInputs; - circuitInputs2.slTimestamp = 3n; - const witness = await circuit.calculateWitness(circuitInputs2); - await circuit.expectConstraintPass(witness); - const isValid = await getSignal(circuit, witness, "isValid"); - expect(isValid.toString()).to.be.eq("0"); - }); }); describe("MessageValidatorNonQV", () => { @@ -173,8 +160,6 @@ describe("MessageValidator circuit", function test() { "currentVoiceCreditBalance", "currentVotesForOption", "voteWeight", - "slTimestamp", - "pollEndTimestamp", ], ["isValid"] >; @@ -216,8 +201,6 @@ describe("MessageValidator circuit", function test() { currentVoiceCreditBalance: 100n, currentVotesForOption: 0n, voteWeight: 9n, - slTimestamp: 1n, - pollEndTimestamp: 2n, }; }); @@ -290,14 +273,5 @@ describe("MessageValidator circuit", function test() { const isValid = await getSignal(circuit, witness, "isValid"); expect(isValid.toString()).to.be.eq("0"); }); - - it("should be invalid if the state leaf timestamp is too high", async () => { - const circuitInputs2 = circuitInputs; - circuitInputs2.slTimestamp = 3n; - const witness = await circuit.calculateWitness(circuitInputs2); - await circuit.expectConstraintPass(witness); - const isValid = await getSignal(circuit, witness, "isValid"); - expect(isValid.toString()).to.be.eq("0"); - }); }); }); diff --git a/circuits/ts/__tests__/ProcessMessages.test.ts b/circuits/ts/__tests__/ProcessMessages.test.ts index 6562ec6501..5fedae6622 100644 --- a/circuits/ts/__tests__/ProcessMessages.test.ts +++ b/circuits/ts/__tests__/ProcessMessages.test.ts @@ -17,17 +17,16 @@ import { import { getSignal, circomkitInstance } from "./utils/utils"; describe("ProcessMessage circuit", function test() { - this.timeout(900000); + this.timeout(9000000); const coordinatorKeypair = new Keypair(); type ProcessMessageCircuitInputs = [ "inputHash", "packedVals", - "pollEndTimestamp", - "msgRoot", + "inputBatchHash", + "outputBatchHash", "msgs", - "msgSubrootPathElements", "coordPrivKey", "coordPubKey", "encPubKeys", @@ -50,7 +49,7 @@ describe("ProcessMessage circuit", function test() { let circuitNonQv: WitnessTester; let hasherCircuit: WitnessTester< - ["packedVals", "coordPubKey", "msgRoot", "currentSbCommitment", "newSbCommitment", "pollEndTimestamp"], + ["packedVals", "coordPubKey", "outputBatchHash", "currentSbCommitment", "newSbCommitment", "actualStateTreeDepth"], ["maxVoteOptions", "numSignUps", "batchStartIndex", "batchEndIndex", "hash"] >; @@ -58,13 +57,13 @@ describe("ProcessMessage circuit", function test() { circuit = await circomkitInstance.WitnessTester("processMessages", { file: "./core/qv/processMessages", template: "ProcessMessages", - params: [10, 2, 1, 2], + params: [10, 20, 2], }); circuitNonQv = await circomkitInstance.WitnessTester("processMessagesNonQv", { file: "./core/non-qv/processMessages", template: "ProcessMessagesNonQv", - params: [10, 2, 1, 2], + params: [10, 20, 2], }); hasherCircuit = await circomkitInstance.WitnessTester("ProcessMessagesInputHasher", { @@ -73,7 +72,7 @@ describe("ProcessMessage circuit", function test() { }); }); - describe("5 users, 1 messages", () => { + describe("1) 5 users, 1 messages", () => { const maciState = new MaciState(STATE_TREE_DEPTH); const voteWeight = BigInt(9); const voteOptionIndex = BigInt(1); @@ -92,7 +91,7 @@ describe("ProcessMessage circuit", function test() { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -151,7 +150,7 @@ describe("ProcessMessage circuit", function test() { }); }); - describe("1 user, 2 messages (non-quadratic voting)", () => { + describe("2) 1 user, 2 messages (non-quadratic voting)", () => { const maciState = new MaciState(STATE_TREE_DEPTH); const voteWeight = BigInt(9); const voteOptionIndex = BigInt(0); @@ -170,7 +169,7 @@ describe("ProcessMessage circuit", function test() { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -220,7 +219,7 @@ describe("ProcessMessage circuit", function test() { it("should produce the correct state root and ballot root", async () => { // The current roots - const emptyBallot = new Ballot(poll.maxValues.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); + const emptyBallot = new Ballot(poll.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); const emptyBallotHash = emptyBallot.hash(); const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), STATE_TREE_ARITY, hash2); @@ -258,10 +257,9 @@ describe("ProcessMessage circuit", function test() { const hasherCircuitInputs = { packedVals, coordPubKey: inputs.coordPubKey, - msgRoot: inputs.msgRoot, + outputBatchHash: inputs.outputBatchHash, currentSbCommitment: inputs.currentSbCommitment, newSbCommitment: inputs.newSbCommitment, - pollEndTimestamp: inputs.pollEndTimestamp, actualStateTreeDepth: inputs.actualStateTreeDepth, }; @@ -272,7 +270,7 @@ describe("ProcessMessage circuit", function test() { }); }); - describe("2 users, 1 message", () => { + describe("3) 2 users, 1 message", () => { const maciState = new MaciState(STATE_TREE_DEPTH); let pollId: bigint; let poll: Poll; @@ -297,7 +295,7 @@ describe("ProcessMessage circuit", function test() { pollId = maciState.deployPoll( BigInt(2 + duration), // BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -329,7 +327,7 @@ describe("ProcessMessage circuit", function test() { it("should produce the correct state root and ballot root", async () => { // The current roots - const emptyBallot = new Ballot(poll.maxValues.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); + const emptyBallot = new Ballot(poll.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); const emptyBallotHash = emptyBallot.hash(); const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), STATE_TREE_ARITY, hash2); @@ -357,7 +355,7 @@ describe("ProcessMessage circuit", function test() { }); }); - describe("1 user, key-change", () => { + describe("4) 1 user, key-change", () => { const maciState = new MaciState(STATE_TREE_DEPTH); const voteWeight = BigInt(9); let stateIndex: number; @@ -366,8 +364,6 @@ describe("ProcessMessage circuit", function test() { const messages: Message[] = []; const commands: PCommand[] = []; - const NUM_BATCHES = 2; - before(() => { // Sign up and publish const userKeypair = new Keypair(new PrivKey(BigInt(123))); @@ -381,7 +377,7 @@ describe("ProcessMessage circuit", function test() { pollId = maciState.deployPoll( BigInt(2 + duration), // BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -449,57 +445,93 @@ describe("ProcessMessage circuit", function test() { poll.publishMessage(message3, ecdhKeypair3.pubKey); }); - describe(`1 user, ${messageBatchSize * NUM_BATCHES} messages`, () => { - it("should produce the correct state root and ballot root", async () => { - const state = new MaciState(STATE_TREE_DEPTH); - const userKeypair = new Keypair(); - const index = state.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); - - // Sign up and publish - const id = state.deployPoll( - BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, - treeDepths, - messageBatchSize, - coordinatorKeypair, - ); + it("should produce the correct state root and ballot root", async () => { + // The current roots + const emptyBallot = new Ballot(poll.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); + const emptyBallotHash = emptyBallot.hash(); + const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), STATE_TREE_ARITY, hash2); + + ballotTree.insert(emptyBallot.hash()); - const selectedPoll = state.polls.get(id); - - selectedPoll?.updatePoll(BigInt(state.stateLeaves.length)); - - // Second batch is not a full batch - const numMessages = messageBatchSize * NUM_BATCHES - 1; - for (let i = 0; i < numMessages; i += 1) { - const command = new PCommand( - BigInt(index), - userKeypair.pubKey, - BigInt(i), // vote option index - BigInt(1), // vote weight - BigInt(numMessages - i), // nonce - BigInt(id), - ); - - const signature = command.sign(userKeypair.privKey); - - const ecdhKeypair = new Keypair(); - const sharedKey = Keypair.genEcdhSharedKey(ecdhKeypair.privKey, coordinatorKeypair.pubKey); - const message = command.encrypt(signature, sharedKey); - selectedPoll?.publishMessage(message, ecdhKeypair.pubKey); - } - - for (let i = 0; i < 2; i += 1) { - const inputs = selectedPoll?.processMessages(id) as unknown as IProcessMessagesInputs; - // eslint-disable-next-line no-await-in-loop - const witness = await circuit.calculateWitness(inputs); - // eslint-disable-next-line no-await-in-loop - await circuit.expectConstraintPass(witness); - } + poll.stateLeaves.forEach(() => { + ballotTree.insert(emptyBallotHash); }); + + const currentStateRoot = poll.stateTree?.root; + const currentBallotRoot = ballotTree.root; + + const inputs = poll.processMessages(pollId) as unknown as IProcessMessagesInputs; + // Calculate the witness + const witness = await circuit.calculateWitness(inputs); + await circuit.expectConstraintPass(witness); + + // The new roots, which should differ, since at least one of the + // messages modified a Ballot or State Leaf + const newStateRoot = poll.stateTree?.root; + const newBallotRoot = poll.ballotTree?.root; + + expect(newStateRoot?.toString()).not.to.be.eq(currentStateRoot?.toString()); + expect(newBallotRoot?.toString()).not.to.be.eq(currentBallotRoot.toString()); + }); + }); + + const NUM_BATCHES = 2; + describe(`5) 1 user, ${messageBatchSize * NUM_BATCHES - 1} messages`, () => { + const maciState = new MaciState(STATE_TREE_DEPTH); + let stateIndex: number; + let pollId: bigint; + let poll: Poll; + + before(() => { + const userKeypair = new Keypair(new PrivKey(BigInt(1))); + stateIndex = maciState.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); + + // Sign up and publish + pollId = maciState.deployPoll( + BigInt(Math.floor(Date.now() / 1000) + duration), + maxValues.maxVoteOptions, + treeDepths, + messageBatchSize, + coordinatorKeypair, + ); + + poll = maciState.polls.get(pollId)!; + + poll.updatePoll(BigInt(maciState.stateLeaves.length)); + + // Second batch is not a full batch + const numMessages = messageBatchSize * NUM_BATCHES - 1; + for (let i = 0; i < numMessages; i += 1) { + const command = new PCommand( + BigInt(stateIndex), + userKeypair.pubKey, + BigInt(i), // vote option index + BigInt(1), // vote weight + BigInt(numMessages - i), // nonce + BigInt(pollId), + ); + + const signature = command.sign(userKeypair.privKey); + + const ecdhKeypair = new Keypair(); + const sharedKey = Keypair.genEcdhSharedKey(ecdhKeypair.privKey, coordinatorKeypair.pubKey); + const message = command.encrypt(signature, sharedKey); + poll.publishMessage(message, ecdhKeypair.pubKey); + } + }); + + it("should produce a proof", async () => { + for (let i = 0; i < NUM_BATCHES; i += 1) { + const inputs = poll.processMessages(pollId) as unknown as IProcessMessagesInputs; + // eslint-disable-next-line no-await-in-loop + const witness = await circuit.calculateWitness(inputs); + // eslint-disable-next-line no-await-in-loop + await circuit.expectConstraintPass(witness); + } }); }); - describe("1 user, 2 messages", () => { + describe("6) 1 user, 2 messages", () => { const maciState = new MaciState(STATE_TREE_DEPTH); const voteOptionIndex = 1n; let stateIndex: bigint; @@ -517,7 +549,7 @@ describe("ProcessMessage circuit", function test() { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -587,7 +619,7 @@ describe("ProcessMessage circuit", function test() { it("should produce the correct state root and ballot root", async () => { // The current roots - const emptyBallot = new Ballot(poll.maxValues.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); + const emptyBallot = new Ballot(poll.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); const emptyBallotHash = emptyBallot.hash(); const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), STATE_TREE_ARITY, hash2); @@ -616,7 +648,7 @@ describe("ProcessMessage circuit", function test() { }); }); - describe("1 user, 2 messages in different batches", () => { + describe("7) 1 user, 2 messages in different batches", () => { const maciState = new MaciState(STATE_TREE_DEPTH); const voteOptionIndex = 1n; let stateIndex: bigint; @@ -634,7 +666,7 @@ describe("ProcessMessage circuit", function test() { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -709,7 +741,7 @@ describe("ProcessMessage circuit", function test() { it("should produce the correct state root and ballot root", async () => { // The current roots - const emptyBallot = new Ballot(poll.maxValues.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); + const emptyBallot = new Ballot(poll.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); const emptyBallotHash = emptyBallot.hash(); const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), STATE_TREE_ARITY, hash2); @@ -741,7 +773,7 @@ describe("ProcessMessage circuit", function test() { }); }); - describe("1 user, 3 messages in different batches", () => { + describe("8) 1 user, 3 messages in different batches", () => { const maciState = new MaciState(STATE_TREE_DEPTH); const voteOptionIndex = 1n; let stateIndex: bigint; @@ -759,7 +791,7 @@ describe("ProcessMessage circuit", function test() { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -853,7 +885,7 @@ describe("ProcessMessage circuit", function test() { it("should produce the correct state root and ballot root", async () => { // The current roots - const emptyBallot = new Ballot(poll.maxValues.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); + const emptyBallot = new Ballot(poll.maxVoteOptions, poll.treeDepths.voteOptionTreeDepth); const emptyBallotHash = emptyBallot.hash(); const ballotTree = new IncrementalQuinTree(STATE_TREE_DEPTH, emptyBallot.hash(), STATE_TREE_ARITY, hash2); diff --git a/circuits/ts/__tests__/StateLeafAndBallotTransformer.test.ts b/circuits/ts/__tests__/StateLeafAndBallotTransformer.test.ts index e2e319f495..feb183645c 100644 --- a/circuits/ts/__tests__/StateLeafAndBallotTransformer.test.ts +++ b/circuits/ts/__tests__/StateLeafAndBallotTransformer.test.ts @@ -26,8 +26,6 @@ describe("StateLeafAndBallotTransformer circuit", function test() { const slVoiceCreditBalance = BigInt(100); const ballotNonce = BigInt(0); const ballotCurrentVotesForOption = BigInt(0); - const slTimestamp = 1n; - const pollEndTimestamp = 2n; const command: PCommand = new PCommand(stateIndex, newPubKey, voteOptionIndex, newVoteWeight, nonce, pollId, salt); @@ -39,8 +37,6 @@ describe("StateLeafAndBallotTransformer circuit", function test() { "maxVoteOptions", "slPubKey", "slVoiceCreditBalance", - "slTimestamp", - "pollEndTimestamp", "ballotNonce", "ballotCurrentVotesForOption", "cmdStateIndex", @@ -63,8 +59,6 @@ describe("StateLeafAndBallotTransformer circuit", function test() { "maxVoteOptions", "slPubKey", "slVoiceCreditBalance", - "slTimestamp", - "pollEndTimestamp", "ballotNonce", "ballotCurrentVotesForOption", "cmdStateIndex", @@ -99,8 +93,6 @@ describe("StateLeafAndBallotTransformer circuit", function test() { maxVoteOptions, slPubKey: slPubKey.asCircuitInputs() as unknown as [bigint, bigint], slVoiceCreditBalance, - slTimestamp, - pollEndTimestamp, ballotNonce, ballotCurrentVotesForOption, cmdStateIndex: command.stateIndex, @@ -136,8 +128,6 @@ describe("StateLeafAndBallotTransformer circuit", function test() { maxVoteOptions, slPubKey: slPubKey.asCircuitInputs() as unknown as [bigint, bigint], slVoiceCreditBalance, - slTimestamp, - pollEndTimestamp, ballotNonce, ballotCurrentVotesForOption, cmdStateIndex: command.stateIndex, @@ -173,8 +163,6 @@ describe("StateLeafAndBallotTransformer circuit", function test() { maxVoteOptions, slPubKey: slPubKey.asCircuitInputs() as unknown as [bigint, bigint], slVoiceCreditBalance, - slTimestamp, - pollEndTimestamp, ballotNonce, ballotCurrentVotesForOption, cmdStateIndex: command.stateIndex, @@ -210,8 +198,6 @@ describe("StateLeafAndBallotTransformer circuit", function test() { maxVoteOptions, slPubKey: slPubKey.asCircuitInputs() as unknown as [bigint, bigint], slVoiceCreditBalance, - slTimestamp, - pollEndTimestamp, ballotNonce, ballotCurrentVotesForOption, cmdStateIndex: command.stateIndex, diff --git a/circuits/ts/__tests__/TallyVotes.test.ts b/circuits/ts/__tests__/TallyVotes.test.ts index e907652e0f..de51f636a5 100644 --- a/circuits/ts/__tests__/TallyVotes.test.ts +++ b/circuits/ts/__tests__/TallyVotes.test.ts @@ -12,8 +12,6 @@ describe("TallyVotes circuit", function test() { const treeDepths = { intStateTreeDepth: 1, - messageTreeDepth: 2, - messageTreeSubDepth: 1, voteOptionTreeDepth: 2, }; @@ -80,7 +78,7 @@ describe("TallyVotes circuit", function test() { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -154,7 +152,7 @@ describe("TallyVotes circuit", function test() { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -224,7 +222,7 @@ describe("TallyVotes circuit", function test() { const pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, diff --git a/circuits/ts/__tests__/utils/constants.ts b/circuits/ts/__tests__/utils/constants.ts index 590e66b9dd..e6a4574763 100644 --- a/circuits/ts/__tests__/utils/constants.ts +++ b/circuits/ts/__tests__/utils/constants.ts @@ -1,17 +1,17 @@ export const STATE_TREE_DEPTH = 10; export const voiceCreditBalance = BigInt(100); export const duration = 30; + export const maxValues = { maxUsers: 25, - maxMessages: 25, maxVoteOptions: 25, }; + export const treeDepths = { intStateTreeDepth: 5, - messageTreeDepth: 2, - messageTreeSubDepth: 1, voteOptionTreeDepth: 2, }; -export const messageBatchSize = 5; + +export const messageBatchSize = 20; export const L = 2736030358979909402780800718157159386076813972158567259200215660948447373041n; diff --git a/circuits/ts/__tests__/utils/types.ts b/circuits/ts/__tests__/utils/types.ts index 934108a0e9..776078a927 100644 --- a/circuits/ts/__tests__/utils/types.ts +++ b/circuits/ts/__tests__/utils/types.ts @@ -21,6 +21,4 @@ export interface IMessageValidatorCircuitInputs { currentVoiceCreditBalance: SignalValueType; currentVotesForOption: SignalValueType; voteWeight: SignalValueType; - slTimestamp: SignalValueType; - pollEndTimestamp: SignalValueType; } diff --git a/circuits/ts/types.ts b/circuits/ts/types.ts index 20ed2cd39f..9a22e68c98 100644 --- a/circuits/ts/types.ts +++ b/circuits/ts/types.ts @@ -48,9 +48,9 @@ export interface IProcessMessagesInputs { inputHash: bigint; packedVals: bigint; pollEndTimestamp: bigint; - msgRoot: bigint; + inputBatchHash: bigint; + outputBatchHash: bigint; msgs: bigint[]; - msgSubrootPathElements: bigint[][]; coordPrivKey: bigint; coordPubKey: [bigint, bigint]; encPubKeys: bigint[]; diff --git a/cli/testScript.sh b/cli/testScript.sh index d8bfc6b372..73a20e2c3b 100755 --- a/cli/testScript.sh +++ b/cli/testScript.sh @@ -8,7 +8,7 @@ node build/ts/index.js setVerifyingKeys \ --msg-tree-depth 2 \ --vote-option-tree-depth 2 \ --msg-batch-depth 1 \ - --process-messages-zkey-qv ./zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test.0.zkey \ + --process-messages-zkey-qv ./zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test.0.zkey \ --tally-votes-zkey-qv ./zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test.0.zkey node build/ts/index.js create -s 10 node build/ts/index.js deployPoll \ @@ -39,16 +39,15 @@ node build/ts/index.js publish \ --poll-id 0 node build/ts/index.js timeTravel -s 100 node build/ts/index.js mergeSignups --poll-id 0 -node build/ts/index.js mergeMessages --poll-id 0 node build/ts/index.js genProofs \ --privkey macisk.bf92af7614b07e2ba19dce65bb7fef2b93d83b84da2cf2e3af690104fbc52511 \ --poll-id 0 \ - --process-zkey ./zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test.0.zkey \ + --process-zkey ./zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test.0.zkey \ --tally-zkey ./zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test.0.zkey \ --tally-file tally.json \ --output proofs/ \ -tw ./zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test_js/TallyVotes_10-1-2_test.wasm \ - -pw ./zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test_js/ProcessMessages_10-2-1-2_test.wasm \ + -pw ./zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test_js/ProcessMessages_10-20-2_test.wasm \ -w true \ -q false node build/ts/index.js proveOnChain \ diff --git a/cli/tests/ceremony-params/ceremonyParams.test.ts b/cli/tests/ceremony-params/ceremonyParams.test.ts index 116821f40d..37f0166096 100644 --- a/cli/tests/ceremony-params/ceremonyParams.test.ts +++ b/cli/tests/ceremony-params/ceremonyParams.test.ts @@ -9,7 +9,6 @@ import { deployPoll, deployVkRegistryContract, genProofs, - mergeMessages, mergeSignups, proveOnChain, publish, @@ -31,7 +30,6 @@ import { testTallyFilePath, ceremonyTallyVotesWasmPath, ceremonyTallyVotesWitnessPath, - mergeMessagesArgs, mergeSignupsArgs, proveOnChainArgs, verifyArgs, @@ -48,12 +46,11 @@ import { } from "../constants"; import { clean, isArm } from "../utils"; -describe("Stress tests with ceremony params (6,9,2,3)", function test() { - const messageTreeDepth = 9; +describe("Stress tests with ceremony params (6,3,2,20)", function test() { const stateTreeDepth = 6; const voteOptionTreeDepth = 3; - const messageBatchDepth = 2; const intStateTreeDepth = 2; + const messageBatchSize = 20; const pollDuration = 60000; @@ -67,9 +64,8 @@ describe("Stress tests with ceremony params (6,9,2,3)", function test() { quiet: true, stateTreeDepth, intStateTreeDepth, - messageTreeDepth, voteOptionTreeDepth, - messageBatchDepth, + messageBatchSize, processMessagesZkeyPathQv: ceremonyProcessMessagesZkeyPath, tallyVotesZkeyPathQv: ceremonyTallyVotesZkeyPath, processMessagesZkeyPathNonQv: ceremonyProcessMessagesNonQvZkeyPath, @@ -84,8 +80,7 @@ describe("Stress tests with ceremony params (6,9,2,3)", function test() { const deployPollArgs: Omit = { pollDuration, intStateTreeDepth, - messageTreeSubDepth: messageBatchDepth, - messageTreeDepth, + messageBatchSize, voteOptionTreeDepth, coordinatorPubkey: coordinatorPubKey, useQuadraticVoting: true, @@ -161,7 +156,6 @@ describe("Stress tests with ceremony params (6,9,2,3)", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ seconds: pollDuration, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); await genProofs({ ...genProofsCeremonyArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -212,7 +206,6 @@ describe("Stress tests with ceremony params (6,9,2,3)", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ seconds: pollDuration, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); await genProofs({ ...genProofsCeremonyArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -283,7 +276,6 @@ describe("Stress tests with ceremony params (6,9,2,3)", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ seconds: pollDuration, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); const tallyFileData = await genProofs({ ...genProofsArgs, signer, useQuadraticVoting: false }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -340,7 +332,6 @@ describe("Stress tests with ceremony params (6,9,2,3)", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ seconds: pollDuration, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); const tallyFileData = await genProofs({ ...genProofsArgs, signer, useQuadraticVoting: false }); await proveOnChain({ ...proveOnChainArgs, signer }); diff --git a/cli/tests/constants.ts b/cli/tests/constants.ts index eade24f114..80f7933b09 100644 --- a/cli/tests/constants.ts +++ b/cli/tests/constants.ts @@ -6,7 +6,6 @@ import { CheckVerifyingKeysArgs, DeployArgs, DeployPollArgs, - MergeMessagesArgs, MergeSignupsArgs, ProveOnChainArgs, SetVerifyingKeysArgs, @@ -18,29 +17,28 @@ import { export const STATE_TREE_DEPTH = 10; export const INT_STATE_TREE_DEPTH = 1; -export const MSG_TREE_DEPTH = 2; export const VOTE_OPTION_TREE_DEPTH = 2; -export const MSG_BATCH_DEPTH = 1; +export const MESSAGE_BATCH_SIZE = 20; const coordinatorKeypair = new Keypair(); export const coordinatorPubKey = coordinatorKeypair.pubKey.serialize(); export const coordinatorPrivKey = coordinatorKeypair.privKey.serialize(); -export const processMessageTestZkeyPath = "./zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test.0.zkey"; +export const processMessageTestZkeyPath = "./zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test.0.zkey"; export const tallyVotesTestZkeyPath = "./zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test.0.zkey"; export const processMessageTestNonQvZkeyPath = - "./zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test.0.zkey"; + "./zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test.0.zkey"; export const tallyVotesTestNonQvZkeyPath = "./zkeys/TallyVotesNonQv_10-1-2_test/TallyVotesNonQv_10-1-2_test.0.zkey"; export const testTallyFilePath = "./tally.json"; export const testProofsDirPath = "./proofs"; export const testProcessMessagesWitnessPath = - "./zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test_cpp/ProcessMessages_10-2-1-2_test"; + "./zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test_cpp/ProcessMessages_10-20-2_test"; export const testProcessMessagesWitnessDatPath = - "./zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test_cpp/ProcessMessages_10-2-1-2_test.dat"; + "./zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test_cpp/ProcessMessages_10-20-2_test.dat"; export const testTallyVotesWitnessPath = "./zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test_cpp/TallyVotes_10-1-2_test"; export const testTallyVotesWitnessDatPath = "./zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test_cpp/TallyVotes_10-1-2_test.dat"; export const testProcessMessagesWasmPath = - "./zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test_js/ProcessMessages_10-2-1-2_test.wasm"; + "./zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test_js/ProcessMessages_10-20-2_test.wasm"; export const testTallyVotesWasmPath = "./zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test_js/TallyVotes_10-1-2_test.wasm"; export const testRapidsnarkPath = `${homedir()}/rapidsnark/build/prover`; @@ -71,15 +69,15 @@ export const ceremonyTallyVotesWasmPath = "./zkeys/TallyVotes_6-2-3/TallyVotes_6 export const ceremonyTallyVotesNonQvWasmPath = "./zkeys/TallyVotesNonQv_6-2-3/TallyVotesNonQv_6-2-3_js/TallyVotesNonQv_6-2-3.wasm"; export const testProcessMessagesNonQvWitnessPath = - "./zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test_cpp/ProcessMessagesNonQv_10-2-1-2_test"; + "./zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test_cpp/ProcessMessagesNonQv_10-20-2_test"; export const testProcessMessagesNonQvWitnessDatPath = - "./zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test_cpp/ProcessMessagesNonQv_10-2-1-2_test.dat"; + "./zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test_cpp/ProcessMessagesNonQv_10-20-2_test.dat"; export const testTallyVotesNonQvWitnessPath = "./zkeys/TallyVotesNonQv_10-1-2_test/TallyVotesNonQv_10-1-2_test_cpp/TallyVotesNonQv_10-1-2_test"; export const testTallyVotesNonQvWitnessDatPath = "./zkeys/TallyVotesNonQv_10-1-2_test/TallyVotesNonQv_10-1-2_test_cpp/TallyVotesNonQv_10-1-2_test.dat"; export const testProcessMessagesNonQvWasmPath = - "./zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test_js/ProcessMessagesNonQv_10-2-1-2_test.wasm"; + "./zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test_js/ProcessMessagesNonQv_10-20-2_test.wasm"; export const testTallyVotesNonQvWasmPath = "./zkeys/TallyVotesNonQv_10-1-2_test/TallyVotesNonQv_10-1-2_test_js/TallyVotesNonQv_10-1-2_test.wasm"; @@ -91,9 +89,8 @@ export const setVerifyingKeysArgs: Omit = { quiet: true, stateTreeDepth: STATE_TREE_DEPTH, intStateTreeDepth: INT_STATE_TREE_DEPTH, - messageTreeDepth: MSG_TREE_DEPTH, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, - messageBatchDepth: MSG_BATCH_DEPTH, + messageBatchSize: MESSAGE_BATCH_SIZE, processMessagesZkeyPathQv: processMessageTestZkeyPath, tallyVotesZkeyPathQv: tallyVotesTestZkeyPath, }; @@ -102,9 +99,8 @@ export const setVerifyingKeysNonQvArgs: Omit = { quiet: true, stateTreeDepth: STATE_TREE_DEPTH, intStateTreeDepth: INT_STATE_TREE_DEPTH, - messageTreeDepth: MSG_TREE_DEPTH, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, - messageBatchDepth: MSG_BATCH_DEPTH, + messageBatchSize: MESSAGE_BATCH_SIZE, processMessagesZkeyPathNonQv: processMessageTestNonQvZkeyPath, tallyVotesZkeyPathNonQv: tallyVotesTestNonQvZkeyPath, }; @@ -112,9 +108,8 @@ export const setVerifyingKeysNonQvArgs: Omit = { export const checkVerifyingKeysArgs: Omit = { stateTreeDepth: STATE_TREE_DEPTH, intStateTreeDepth: INT_STATE_TREE_DEPTH, - messageTreeDepth: MSG_TREE_DEPTH, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, - messageBatchDepth: MSG_BATCH_DEPTH, + messageBatchSize: MESSAGE_BATCH_SIZE, processMessagesZkeyPath: processMessageTestZkeyPath, tallyVotesZkeyPath: tallyVotesTestZkeyPath, }; @@ -123,10 +118,6 @@ export const timeTravelArgs: Omit = { seconds: pollDuration, }; -export const mergeMessagesArgs: Omit = { - pollId: 0n, -}; - export const mergeSignupsArgs: Omit = { pollId: 0n, }; @@ -154,8 +145,7 @@ export const deployArgs: Omit = { export const deployPollArgs: Omit = { pollDuration, intStateTreeDepth: INT_STATE_TREE_DEPTH, - messageTreeSubDepth: MSG_BATCH_DEPTH, - messageTreeDepth: MSG_TREE_DEPTH, + messageBatchSize: MESSAGE_BATCH_SIZE, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, coordinatorPubkey: coordinatorPubKey, useQuadraticVoting: true, diff --git a/cli/tests/e2e/e2e.nonQv.test.ts b/cli/tests/e2e/e2e.nonQv.test.ts index 72d81c5454..558ab8509e 100644 --- a/cli/tests/e2e/e2e.nonQv.test.ts +++ b/cli/tests/e2e/e2e.nonQv.test.ts @@ -9,7 +9,6 @@ import { deployPoll, deployVkRegistryContract, genProofs, - mergeMessages, mergeSignups, proveOnChain, publish, @@ -25,7 +24,6 @@ import { pollDuration, proveOnChainArgs, verifyArgs, - mergeMessagesArgs, mergeSignupsArgs, testProofsDirPath, testRapidsnarkPath, @@ -117,7 +115,6 @@ describe("e2e tests with non quadratic voting", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ seconds: pollDuration, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); const tallyFileData = await genProofs({ ...genProofsArgs, signer, useQuadraticVoting: false }); await proveOnChain({ ...proveOnChainArgs, signer }); diff --git a/cli/tests/e2e/e2e.test.ts b/cli/tests/e2e/e2e.test.ts index f022d08be8..a2a74a9981 100644 --- a/cli/tests/e2e/e2e.test.ts +++ b/cli/tests/e2e/e2e.test.ts @@ -14,7 +14,6 @@ import { deployVkRegistryContract, genLocalState, genProofs, - mergeMessages, mergeSignups, proveOnChain, publish, @@ -32,7 +31,6 @@ import { pollDuration, proveOnChainArgs, verifyArgs, - mergeMessagesArgs, mergeSignupsArgs, processMessageTestZkeyPath, setVerifyingKeysArgs, @@ -133,7 +131,6 @@ describe("e2e tests", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ seconds: pollDuration, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); const tallyFileData = await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -180,7 +177,6 @@ describe("e2e tests", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); const tallyFileData = await genProofs({ ...genProofsArgs, signer }); await signup({ maciAddress: maciAddresses.maciAddress, maciPubKey: user.pubKey.serialize(), signer }); @@ -318,7 +314,6 @@ describe("e2e tests", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -375,7 +370,6 @@ describe("e2e tests", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); const tallyFileData = await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -424,7 +418,6 @@ describe("e2e tests", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -511,7 +504,6 @@ describe("e2e tests", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); const tallyFileData = await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -559,7 +551,6 @@ describe("e2e tests", function test() { // time travel await timeTravel({ ...timeTravelArgs, signer }); // generate proofs - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); const tallyFileData = await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -588,7 +579,6 @@ describe("e2e tests", function test() { it("should generate proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ pollId: 1n, signer }); await mergeSignups({ pollId: 1n, signer }); await genProofs({ ...genProofsArgs, pollId: 1n, signer }); await proveOnChain({ ...proveOnChainArgs, pollId: 1n, signer }); @@ -629,7 +619,6 @@ describe("e2e tests", function test() { // time travel await timeTravel({ ...timeTravelArgs, signer }); // generate proofs - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); const tallyFileData = await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -680,7 +669,6 @@ describe("e2e tests", function test() { it("should generate proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ pollId: 1n, signer }); await mergeSignups({ pollId: 1n, signer }); await genProofs({ ...genProofsArgs, pollId: 1n, signer }); await proveOnChain({ ...proveOnChainArgs, pollId: 1n, signer }); @@ -748,7 +736,6 @@ describe("e2e tests", function test() { // time travel await timeTravel({ ...timeTravelArgs, signer }); // generate proofs - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -846,7 +833,6 @@ describe("e2e tests", function test() { it("should complete the second poll", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ pollId: 1n, signer }); await mergeSignups({ pollId: 1n, signer }); const tallyData = await genProofs({ ...genProofsArgs, pollId: 1n, signer }); await proveOnChain({ @@ -869,7 +855,6 @@ describe("e2e tests", function test() { }); it("should complete the third poll", async () => { - await mergeMessages({ pollId: 2n, signer }); await mergeSignups({ pollId: 2n, signer }); const tallyData = await genProofs({ ...genProofsArgs, pollId: 2n, signer }); await proveOnChain({ @@ -932,7 +917,6 @@ describe("e2e tests", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); await genLocalState({ outputPath: stateOutPath, diff --git a/cli/tests/e2e/keyChange.test.ts b/cli/tests/e2e/keyChange.test.ts index a7b0c0628f..3c7257533d 100644 --- a/cli/tests/e2e/keyChange.test.ts +++ b/cli/tests/e2e/keyChange.test.ts @@ -13,7 +13,6 @@ import { deployPoll, deployVkRegistryContract, genProofs, - mergeMessages, mergeSignups, proveOnChain, publish, @@ -28,7 +27,6 @@ import { deployArgs, deployPollArgs, processMessageTestZkeyPath, - mergeMessagesArgs, mergeSignupsArgs, setVerifyingKeysArgs, tallyVotesTestZkeyPath, @@ -139,7 +137,6 @@ describe("keyChange tests", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -209,7 +206,6 @@ describe("keyChange tests", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); @@ -279,7 +275,6 @@ describe("keyChange tests", function test() { it("should generate zk-SNARK proofs and verify them", async () => { await timeTravel({ ...timeTravelArgs, signer }); - await mergeMessages({ ...mergeMessagesArgs, signer }); await mergeSignups({ ...mergeSignupsArgs, signer }); await genProofs({ ...genProofsArgs, signer }); await proveOnChain({ ...proveOnChainArgs, signer }); diff --git a/cli/tests/unit/poll.test.ts b/cli/tests/unit/poll.test.ts index 300f76e600..180a790c2c 100644 --- a/cli/tests/unit/poll.test.ts +++ b/cli/tests/unit/poll.test.ts @@ -10,7 +10,6 @@ import { setVerifyingKeys, getPoll, timeTravel, - mergeMessages, mergeSignups, } from "../../ts/commands"; import { DeployedContracts, PollContracts } from "../../ts/utils"; @@ -56,7 +55,6 @@ describe("poll", () => { const pollData = await getPoll({ maciAddress: maciAddresses.maciAddress, provider: signer.provider! }); await timeTravel({ seconds: Number(pollData.duration), signer }); - await mergeMessages({ pollId: BigInt(pollData.id), signer }); await mergeSignups({ pollId: BigInt(pollData.id), signer }); const finishedPollData = await getPoll({ maciAddress: maciAddresses.maciAddress, signer }); diff --git a/cli/ts/commands/checkVerifyingKeys.ts b/cli/ts/commands/checkVerifyingKeys.ts index cd1b683725..b37b57f7dd 100644 --- a/cli/ts/commands/checkVerifyingKeys.ts +++ b/cli/ts/commands/checkVerifyingKeys.ts @@ -27,9 +27,8 @@ import { export const checkVerifyingKeys = async ({ stateTreeDepth, intStateTreeDepth, - messageTreeDepth, voteOptionTreeDepth, - messageBatchDepth, + messageBatchSize, processMessagesZkeyPath, tallyVotesZkeyPath, vkRegistry, @@ -69,11 +68,9 @@ export const checkVerifyingKeys = async ({ try { logYellow(quiet, info("Retrieving verifying keys from the contract...")); // retrieve the verifying keys from the contract - const messageBatchSize = 5 ** messageBatchDepth; const processVkOnChain = await vkRegistryContractInstance.getProcessVk( stateTreeDepth, - messageTreeDepth, voteOptionTreeDepth, messageBatchSize, useQuadraticVoting ? EMode.QV : EMode.NON_QV, diff --git a/cli/ts/commands/deploy.ts b/cli/ts/commands/deploy.ts index 1c27885965..fffb8feea1 100644 --- a/cli/ts/commands/deploy.ts +++ b/cli/ts/commands/deploy.ts @@ -75,7 +75,7 @@ export const deploy = async ({ const verifierContractAddress = await verifierContract.getAddress(); - // deploy MACI, stateAq, PollFactory and poseidon + // deploy MACI, PollFactory and poseidon const { maciContract, pollFactoryContract, poseidonAddrs } = await deployMaci({ signUpTokenGatekeeperContractAddress: signupGatekeeperContractAddress, initialVoiceCreditBalanceAddress: initialVoiceCreditProxyContractAddress, diff --git a/cli/ts/commands/deployPoll.ts b/cli/ts/commands/deployPoll.ts index 2da430d20e..c86806be49 100644 --- a/cli/ts/commands/deployPoll.ts +++ b/cli/ts/commands/deployPoll.ts @@ -21,8 +21,7 @@ import { export const deployPoll = async ({ pollDuration, intStateTreeDepth, - messageTreeSubDepth, - messageTreeDepth, + messageBatchSize, voteOptionTreeDepth, coordinatorPubkey, maciAddress, @@ -59,13 +58,10 @@ export const deployPoll = async ({ if (intStateTreeDepth <= 0) { logError("Int state tree depth cannot be <= 0"); } - // required arg -> message tree sub depth - if (messageTreeSubDepth <= 0) { - logError("Message tree sub depth cannot be <= 0"); - } + // required arg -> message tree depth - if (messageTreeDepth <= 0) { - logError("Message tree depth cannot be <= 0"); + if (messageBatchSize <= 0) { + logError("Message batch size cannot be <= 0"); } // required arg -> vote option tree depth if (voteOptionTreeDepth <= 0) { @@ -100,10 +96,9 @@ export const deployPoll = async ({ pollDuration, { intStateTreeDepth, - messageTreeSubDepth, - messageTreeDepth, voteOptionTreeDepth, }, + messageBatchSize, unserializedKey.asContractParam(), verifierContractAddress, vkRegistry, diff --git a/cli/ts/commands/genLocalState.ts b/cli/ts/commands/genLocalState.ts index 7bf00ff1c2..12d9fe620d 100644 --- a/cli/ts/commands/genLocalState.ts +++ b/cli/ts/commands/genLocalState.ts @@ -1,10 +1,5 @@ import { JsonRpcProvider } from "ethers"; -import { - MACI__factory as MACIFactory, - AccQueue__factory as AccQueueFactory, - Poll__factory as PollFactory, - genMaciStateFromContract, -} from "maci-contracts"; +import { MACI__factory as MACIFactory, Poll__factory as PollFactory, genMaciStateFromContract } from "maci-contracts"; import { Keypair, PrivKey } from "maci-domainobjs"; import fs from "fs"; @@ -72,28 +67,18 @@ export const genLocalState = async ({ } const pollContract = PollFactory.connect(pollAddr, signer); - const [{ messageAq }, { messageTreeDepth }] = await Promise.all([ - pollContract.extContracts(), - pollContract.treeDepths(), - ]); - const messageAqContract = AccQueueFactory.connect(messageAq, signer); - - const [defaultStartBlockSignup, defaultStartBlockPoll, stateRoot, numSignups, messageRoot] = await Promise.all([ + const [defaultStartBlockSignup, defaultStartBlockPoll, stateRoot, numSignups] = await Promise.all([ maciContract.queryFilter(maciContract.filters.SignUp(), startBlock).then((events) => events[0]?.blockNumber ?? 0), maciContract .queryFilter(maciContract.filters.DeployPoll(), startBlock) .then((events) => events[0]?.blockNumber ?? 0), maciContract.getStateTreeRoot(), maciContract.numSignUps(), - messageAqContract.getMainRoot(messageTreeDepth), ]); const defaultStartBlock = Math.min(defaultStartBlockPoll, defaultStartBlockSignup); let fromBlock = startBlock ? Number(startBlock) : defaultStartBlock; const defaultEndBlock = await Promise.all([ - pollContract - .queryFilter(pollContract.filters.MergeMessageAq(messageRoot), fromBlock) - .then((events) => events[events.length - 1]?.blockNumber), pollContract .queryFilter(pollContract.filters.MergeMaciState(stateRoot, numSignups), fromBlock) .then((events) => events[events.length - 1]?.blockNumber), diff --git a/cli/ts/commands/genProofs.ts b/cli/ts/commands/genProofs.ts index 62c48fc7a3..97be9b969b 100644 --- a/cli/ts/commands/genProofs.ts +++ b/cli/ts/commands/genProofs.ts @@ -1,10 +1,5 @@ import { extractVk, genProof, verifyProof } from "maci-circuits"; -import { - MACI__factory as MACIFactory, - AccQueue__factory as AccQueueFactory, - Poll__factory as PollFactory, - genMaciStateFromContract, -} from "maci-contracts"; +import { MACI__factory as MACIFactory, Poll__factory as PollFactory, genMaciStateFromContract } from "maci-contracts"; import { type CircuitInputs, type IJsonMaciState, MaciState } from "maci-core"; import { hash3, hashLeftRight, genTreeCommitment } from "maci-crypto"; import { Keypair, PrivKey } from "maci-domainobjs"; @@ -151,23 +146,11 @@ export const genProofs = async ({ } const pollContract = PollFactory.connect(pollAddr, signer); - const extContracts = await pollContract.extContracts(); - const messageAqContractAddr = extContracts.messageAq; - const messageAqContract = AccQueueFactory.connect(messageAqContractAddr, signer); - // Check that the state and message trees have been merged if (!(await pollContract.stateMerged())) { logError("The state tree has not been merged yet. Please use the mergeSignups subcommand to do so."); } - const messageTreeDepth = Number((await pollContract.treeDepths()).messageTreeDepth); - - // check that the main root is set - const mainRoot = (await messageAqContract.getMainRoot(messageTreeDepth.toString())).toString(); - if (mainRoot === "0") { - logError("The message tree has not been merged yet. Please use the mergeMessages subcommand to do so."); - } - let maciState: MaciState | undefined; if (stateFile) { const content = JSON.parse(fs.readFileSync(stateFile).toString()) as unknown as IJsonMaciState; @@ -184,22 +167,18 @@ export const genProofs = async ({ } } else { // build an off-chain representation of the MACI contract using data in the contract storage - const [defaultStartBlockSignup, defaultStartBlockPoll, stateRoot, numSignups, messageRoot] = await Promise.all([ + const [defaultStartBlockSignup, defaultStartBlockPoll, stateRoot, numSignups] = await Promise.all([ maciContract.queryFilter(maciContract.filters.SignUp(), startBlock).then((events) => events[0]?.blockNumber ?? 0), maciContract .queryFilter(maciContract.filters.DeployPoll(), startBlock) .then((events) => events[0]?.blockNumber ?? 0), maciContract.getStateTreeRoot(), maciContract.numSignUps(), - messageAqContract.getMainRoot(messageTreeDepth), ]); const defaultStartBlock = Math.min(defaultStartBlockPoll, defaultStartBlockSignup); let fromBlock = startBlock ? Number(startBlock) : defaultStartBlock; const defaultEndBlock = await Promise.all([ - pollContract - .queryFilter(pollContract.filters.MergeMessageAq(messageRoot), fromBlock) - .then((events) => events[events.length - 1]?.blockNumber), pollContract .queryFilter(pollContract.filters.MergeMaciState(stateRoot, numSignups), fromBlock) .then((events) => events[events.length - 1]?.blockNumber), diff --git a/cli/ts/commands/index.ts b/cli/ts/commands/index.ts index b3f413b6ba..89155da1bf 100644 --- a/cli/ts/commands/index.ts +++ b/cli/ts/commands/index.ts @@ -4,7 +4,6 @@ export { getPoll } from "./poll"; export { deployVkRegistryContract } from "./deployVkRegistry"; export { genKeyPair } from "./genKeyPair"; export { genMaciPubKey } from "./genPubKey"; -export { mergeMessages } from "./mergeMessages"; export { mergeSignups } from "./mergeSignups"; export { publish, publishBatch } from "./publish"; export { setVerifyingKeys } from "./setVerifyingKeys"; diff --git a/cli/ts/commands/mergeMessages.ts b/cli/ts/commands/mergeMessages.ts deleted file mode 100644 index c446806894..0000000000 --- a/cli/ts/commands/mergeMessages.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { - MACI__factory as MACIFactory, - Poll__factory as PollFactory, - AccQueue__factory as AccQueueFactory, -} from "maci-contracts/typechain-types"; - -import type { MergeMessagesArgs } from "../utils/interfaces"; - -import { banner } from "../utils/banner"; -import { contractExists, currentBlockTimestamp } from "../utils/contracts"; -import { DEFAULT_SR_QUEUE_OPS } from "../utils/defaults"; -import { readContractAddress } from "../utils/storage"; -import { info, logError, logGreen, logYellow, success } from "../utils/theme"; - -/** - * Merge the message queue on chain - * @param MergeMessagesArgs - The arguments for the mergeMessages command - */ -export const mergeMessages = async ({ - pollId, - quiet = true, - maciAddress, - numQueueOps, - signer, -}: MergeMessagesArgs): Promise => { - banner(quiet); - const network = await signer.provider?.getNetwork(); - - // maci contract validation - if (!readContractAddress("MACI", network?.name) && !maciAddress) { - logError("Could not read contracts"); - } - const maciContractAddress = maciAddress || readContractAddress("MACI", network?.name); - if (!(await contractExists(signer.provider!, maciContractAddress))) { - logError("MACI contract does not exist"); - } - - if (pollId < 0) { - logError("Invalid poll id"); - } - - const maciContract = MACIFactory.connect(maciContractAddress, signer); - const pollAddress = await maciContract.polls(pollId); - - if (!(await contractExists(signer.provider!, pollAddress))) { - logError("Poll contract does not exist"); - } - - const pollContract = PollFactory.connect(pollAddress, signer); - const extContracts = await pollContract.extContracts(); - const messageAqContractAddr = extContracts.messageAq; - - const accQueueContract = AccQueueFactory.connect(messageAqContractAddr, signer); - - // check if it's time to merge the message AQ - const dd = await pollContract.getDeployTimeAndDuration(); - const deadline = Number(dd[0]) + Number(dd[1]); - const now = await currentBlockTimestamp(signer.provider!); - - if (now < deadline) { - logError("The voting period is not over yet"); - } - - let subTreesMerged = false; - - // infinite loop to merge the sub trees - while (!subTreesMerged) { - // eslint-disable-next-line no-await-in-loop - subTreesMerged = await accQueueContract.subTreesMerged(); - - if (subTreesMerged) { - logGreen(quiet, success("All message subtrees have been merged.")); - } else { - // eslint-disable-next-line no-await-in-loop - await accQueueContract - .getSrIndices() - .then((data) => data.map((x) => Number(x))) - .then((indices) => { - logYellow(quiet, info(`Merging message subroots ${indices[0] + 1} / ${indices[1] + 1}`)); - }); - - // eslint-disable-next-line no-await-in-loop - const tx = await pollContract.mergeMessageAqSubRoots(numQueueOps || DEFAULT_SR_QUEUE_OPS); - // eslint-disable-next-line no-await-in-loop - const receipt = await tx.wait(); - - if (receipt?.status !== 1) { - logError("Transaction failed"); - } - - logGreen(quiet, success(`Executed mergeMessageAqSubRoots(); gas used: ${receipt!.gasUsed.toString()}`)); - - logYellow(quiet, info(`Transaction hash: ${receipt!.hash}`)); - } - } - - // check if the message AQ has been fully merged - const messageTreeDepth = Number((await pollContract.treeDepths()).messageTreeDepth); - - // check if the main root was not already computed - const mainRoot = (await accQueueContract.getMainRoot(messageTreeDepth.toString())).toString(); - if (mainRoot === "0") { - // go and merge the message tree - - logYellow(quiet, info("Merging subroots to a main message root...")); - const tx = await pollContract.mergeMessageAq(); - const receipt = await tx.wait(); - - if (receipt?.status !== 1) { - logError("Transaction failed"); - } - - logGreen(quiet, success(`Executed mergeMessageAq(); gas used: ${receipt!.gasUsed.toString()}`)); - logYellow(quiet, info(`Transaction hash: ${receipt!.hash}`)); - logGreen(quiet, success("The message tree has been merged.")); - } else { - logYellow(quiet, info("The message tree has already been merged.")); - } -}; diff --git a/cli/ts/commands/pollJoining.ts b/cli/ts/commands/pollJoining.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cli/ts/commands/proveOnChain.ts b/cli/ts/commands/proveOnChain.ts index a7e4256746..b5eaa3cfc2 100644 --- a/cli/ts/commands/proveOnChain.ts +++ b/cli/ts/commands/proveOnChain.ts @@ -1,16 +1,16 @@ /* eslint-disable no-await-in-loop */ import { type BigNumberish } from "ethers"; -import { type IVerifyingKeyStruct, formatProofForVerifierContract } from "maci-contracts"; import { MACI__factory as MACIFactory, - AccQueue__factory as AccQueueFactory, Tally__factory as TallyFactory, MessageProcessor__factory as MessageProcessorFactory, Poll__factory as PollFactory, VkRegistry__factory as VkRegistryFactory, Verifier__factory as VerifierFactory, -} from "maci-contracts/typechain-types"; -import { MESSAGE_TREE_ARITY, STATE_TREE_ARITY } from "maci-core"; + formatProofForVerifierContract, + type IVerifyingKeyStruct, +} from "maci-contracts"; +import { STATE_TREE_ARITY } from "maci-core"; import { G1Point, G2Point, hashLeftRight } from "maci-crypto"; import { VerifyingKey } from "maci-domainobjs"; @@ -91,13 +91,6 @@ export const proveOnChain = async ({ const mpContract = MessageProcessorFactory.connect(messageProcessorContractAddress, signer); const tallyContract = TallyFactory.connect(tallyContractAddress, signer); - const messageAqContractAddress = (await pollContract.extContracts()).messageAq; - - if (!(await contractExists(signer.provider!, messageAqContractAddress))) { - logError("There is no MessageAq contract linked to the specified MACI contract."); - } - - const messageAqContract = AccQueueFactory.connect(messageAqContractAddress, signer); const vkRegistryContractAddress = await tallyContract.vkRegistry(); if (!(await contractExists(signer.provider!, vkRegistryContractAddress))) { @@ -144,13 +137,12 @@ export const proveOnChain = async ({ const numSignUpsAndMessages = await pollContract.numSignUpsAndMessages(); const numSignUps = Number(numSignUpsAndMessages[0]); const numMessages = Number(numSignUpsAndMessages[1]); - const messageBatchSize = MESSAGE_TREE_ARITY ** Number(treeDepths.messageTreeSubDepth); + const messageBatchSize = Number(await pollContract.messageBatchSize()); const tallyBatchSize = STATE_TREE_ARITY ** Number(treeDepths.intStateTreeDepth); - let totalMessageBatches = numMessages <= messageBatchSize ? 1 : Math.floor(numMessages / messageBatchSize); - - if (numMessages > messageBatchSize && numMessages % messageBatchSize > 0) { - totalMessageBatches += 1; - } + const pollBatchHashes = await pollContract.getBatchHashes(); + const batchHashes = [...pollBatchHashes]; + const totalMessageBatches = batchHashes.length; + const lastChainHash = await pollContract.chainHash(); // perform validation if (numProcessProofs !== totalMessageBatches) { @@ -171,55 +163,45 @@ export const proveOnChain = async ({ logError("Tally and MessageProcessor modes are not compatible"); } - const messageRootOnChain = await messageAqContract.getMainRoot(Number(treeDepths.messageTreeDepth)); - const stateTreeDepth = Number(await maciContract.stateTreeDepth()); const onChainProcessVk = await vkRegistryContract.getProcessVk( stateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, mpMode, ); - const dd = await pollContract.getDeployTimeAndDuration(); - const pollEndTimestampOnChain = BigInt(dd[0]) + BigInt(dd[1]); - if (numberBatchesProcessed < totalMessageBatches) { logYellow(quiet, info("Submitting proofs of message processing...")); } // process all batches left for (let i = numberBatchesProcessed; i < totalMessageBatches; i += 1) { - let currentMessageBatchIndex: number; + let currentMessageBatchIndex = totalMessageBatches; if (numberBatchesProcessed === 0) { - const r = numMessages % messageBatchSize; - - currentMessageBatchIndex = numMessages; + const chainHash = lastChainHash; + if (numMessages % messageBatchSize !== 0) { + batchHashes.push(chainHash); + } + currentMessageBatchIndex = batchHashes.length; if (currentMessageBatchIndex > 0) { - if (r === 0) { - currentMessageBatchIndex -= messageBatchSize; - } else { - currentMessageBatchIndex -= r; - } + currentMessageBatchIndex -= 1; } - } else { - currentMessageBatchIndex = (totalMessageBatches - numberBatchesProcessed) * messageBatchSize; - } - - if (numberBatchesProcessed > 0 && currentMessageBatchIndex > 0) { - currentMessageBatchIndex -= messageBatchSize; } const { proof, circuitInputs, publicInputs } = data.processProofs[i]; // validation - if (circuitInputs.pollEndTimestamp !== pollEndTimestampOnChain.toString()) { - logError("pollEndTimestamp mismatch."); + + const inputBatchHash = batchHashes[currentMessageBatchIndex - 1]; + if (BigInt(circuitInputs.inputBatchHash as BigNumberish).toString() !== inputBatchHash.toString()) { + logError("input batch hash mismatch."); } - if (BigInt(circuitInputs.msgRoot as BigNumberish).toString() !== messageRootOnChain.toString()) { - logError("message root mismatch."); + + const outputBatchHash = batchHashes[currentMessageBatchIndex]; + if (BigInt(circuitInputs.outputBatchHash as BigNumberish).toString() !== outputBatchHash.toString()) { + logError("output batch hash mismatch."); } let currentSbCommitmentOnChain: bigint; @@ -248,7 +230,7 @@ export const proveOnChain = async ({ currentMessageBatchIndex, numSignUps, numMessages, - treeDepths.messageTreeSubDepth, + messageBatchSize, treeDepths.voteOptionTreeDepth, ), ).toString(); @@ -262,12 +244,12 @@ export const proveOnChain = async ({ const publicInputHashOnChain = BigInt( await mpContract.genProcessMessagesPublicInputHash( currentMessageBatchIndex, - messageRootOnChain.toString(), + outputBatchHash.toString(), numSignUps, numMessages, circuitInputs.currentSbCommitment as BigNumberish, circuitInputs.newSbCommitment as BigNumberish, - treeDepths.messageTreeSubDepth, + messageBatchSize as BigNumberish, treeDepths.voteOptionTreeDepth, ), ); diff --git a/cli/ts/commands/publish.ts b/cli/ts/commands/publish.ts index ac2599f260..9b9ab47df2 100644 --- a/cli/ts/commands/publish.ts +++ b/cli/ts/commands/publish.ts @@ -86,9 +86,8 @@ export const publish = async ({ const pollContract = PollFactory.connect(pollAddress, signer); - const maxValues = await pollContract.maxValues(); + const maxVoteOptions = Number(await pollContract.maxVoteOptions()); const coordinatorPubKeyResult = await pollContract.coordinatorPubKey(); - const maxVoteOptions = Number(maxValues.maxVoteOptions); // validate the vote options index against the max leaf index on-chain if (maxVoteOptions < voteOptionIndex) { @@ -172,11 +171,11 @@ export const publishBatch = async ({ const pollContract = PollFactory.connect(pollAddress, signer); - const [maxValues, coordinatorPubKeyResult] = await Promise.all([ - pollContract.maxValues(), + const [onChainMaxVoteOptions, coordinatorPubKeyResult] = await Promise.all([ + pollContract.maxVoteOptions(), pollContract.coordinatorPubKey(), ]); - const maxVoteOptions = Number(maxValues.maxVoteOptions); + const maxVoteOptions = Number(onChainMaxVoteOptions); // validate the vote options index against the max leaf index on-chain messages.forEach(({ stateIndex, voteOptionIndex, salt, nonce }) => { diff --git a/cli/ts/commands/setVerifyingKeys.ts b/cli/ts/commands/setVerifyingKeys.ts index 4fbcdec9cd..496a77d90e 100644 --- a/cli/ts/commands/setVerifyingKeys.ts +++ b/cli/ts/commands/setVerifyingKeys.ts @@ -1,7 +1,6 @@ import { extractVk } from "maci-circuits"; -import { type IVerifyingKeyStruct, EMode } from "maci-contracts"; -import { VkRegistry__factory as VkRegistryFactory } from "maci-contracts/typechain-types"; -import { genProcessVkSig, genTallyVkSig, MESSAGE_TREE_ARITY } from "maci-core"; +import { type IVerifyingKeyStruct, VkRegistry__factory as VkRegistryFactory, EMode } from "maci-contracts"; +import { genProcessVkSig, genTallyVkSig } from "maci-core"; import { VerifyingKey } from "maci-domainobjs"; import fs from "fs"; @@ -27,9 +26,8 @@ import { export const setVerifyingKeys = async ({ stateTreeDepth, intStateTreeDepth, - messageTreeDepth, voteOptionTreeDepth, - messageBatchDepth, + messageBatchSize, processMessagesZkeyPathQv, tallyVotesZkeyPathQv, processMessagesZkeyPathNonQv, @@ -75,13 +73,7 @@ export const setVerifyingKeys = async ({ const tallyVkNonQv = tallyVotesZkeyPathNonQv && VerifyingKey.fromObj(await extractVk(tallyVotesZkeyPathNonQv)); // validate args - if ( - stateTreeDepth < 1 || - intStateTreeDepth < 1 || - messageTreeDepth < 1 || - voteOptionTreeDepth < 1 || - messageBatchDepth < 1 - ) { + if (stateTreeDepth < 1 || intStateTreeDepth < 1 || voteOptionTreeDepth < 1 || messageBatchSize < 1) { logError("Invalid depth or batch size parameters"); } @@ -93,8 +85,7 @@ export const setVerifyingKeys = async ({ processMessagesZkeyPath: processMessagesZkeyPathQv!, tallyVotesZkeyPath: tallyVotesZkeyPathQv!, stateTreeDepth, - messageTreeDepth, - messageBatchDepth, + messageBatchSize, voteOptionTreeDepth, intStateTreeDepth, }); @@ -103,8 +94,7 @@ export const setVerifyingKeys = async ({ processMessagesZkeyPath: processMessagesZkeyPathNonQv!, tallyVotesZkeyPath: tallyVotesZkeyPathNonQv!, stateTreeDepth, - messageTreeDepth, - messageBatchDepth, + messageBatchSize, voteOptionTreeDepth, intStateTreeDepth, }); @@ -117,10 +107,8 @@ export const setVerifyingKeys = async ({ // connect to VkRegistry contract const vkRegistryContract = VkRegistryFactory.connect(vkRegistryAddress, signer); - const messageBatchSize = MESSAGE_TREE_ARITY ** messageBatchDepth; - // check if the process messages vk was already set - const processVkSig = genProcessVkSig(stateTreeDepth, messageTreeDepth, voteOptionTreeDepth, messageBatchSize); + const processVkSig = genProcessVkSig(stateTreeDepth, voteOptionTreeDepth, messageBatchSize); if (useQuadraticVoting && (await vkRegistryContract.isProcessVkSet(processVkSig, EMode.QV))) { logError("This process verifying key is already set in the contract"); @@ -165,7 +153,6 @@ export const setVerifyingKeys = async ({ const tx = await vkRegistryContract.setVerifyingKeysBatch( stateTreeDepth, intStateTreeDepth, - messageTreeDepth, voteOptionTreeDepth, messageBatchSize, modes, @@ -185,7 +172,6 @@ export const setVerifyingKeys = async ({ if (useQuadraticVoting) { const processVkOnChain = await vkRegistryContract.getProcessVk( stateTreeDepth, - messageTreeDepth, voteOptionTreeDepth, messageBatchSize, EMode.QV, @@ -208,7 +194,6 @@ export const setVerifyingKeys = async ({ } else { const processVkOnChain = await vkRegistryContract.getProcessVk( stateTreeDepth, - messageTreeDepth, voteOptionTreeDepth, messageBatchSize, EMode.NON_QV, @@ -238,8 +223,7 @@ export const setVerifyingKeys = async ({ interface ICheckZkeyFilepathsArgs { stateTreeDepth: number; - messageTreeDepth: number; - messageBatchDepth: number; + messageBatchSize: number; voteOptionTreeDepth: number; intStateTreeDepth: number; processMessagesZkeyPath?: string; @@ -250,8 +234,7 @@ function checkZkeyFilepaths({ processMessagesZkeyPath, tallyVotesZkeyPath, stateTreeDepth, - messageTreeDepth, - messageBatchDepth, + messageBatchSize, voteOptionTreeDepth, intStateTreeDepth, }: ICheckZkeyFilepathsArgs): void { @@ -260,7 +243,7 @@ function checkZkeyFilepaths({ } // Check the pm zkey filename against specified params - const pmMatch = processMessagesZkeyPath.match(/.+_(\d+)-(\d+)-(\d+)-(\d+)/); + const pmMatch = processMessagesZkeyPath.match(/.+_(\d+)-(\d+)-(\d+)/); if (!pmMatch) { logError(`${processMessagesZkeyPath} has an invalid filename`); @@ -268,9 +251,8 @@ function checkZkeyFilepaths({ } const pmStateTreeDepth = Number(pmMatch[1]); - const pmMsgTreeDepth = Number(pmMatch[2]); - const pmMsgBatchDepth = Number(pmMatch[3]); - const pmVoteOptionTreeDepth = Number(pmMatch[4]); + const pmMsgBatchSize = Number(pmMatch[2]); + const pmVoteOptionTreeDepth = Number(pmMatch[3]); const tvMatch = tallyVotesZkeyPath.match(/.+_(\d+)-(\d+)-(\d+)/); @@ -285,8 +267,7 @@ function checkZkeyFilepaths({ if ( stateTreeDepth !== pmStateTreeDepth || - messageTreeDepth !== pmMsgTreeDepth || - messageBatchDepth !== pmMsgBatchDepth || + messageBatchSize !== pmMsgBatchSize || voteOptionTreeDepth !== pmVoteOptionTreeDepth || stateTreeDepth !== tvStateTreeDepth || intStateTreeDepth !== tvIntStateTreeDepth || diff --git a/cli/ts/index.ts b/cli/ts/index.ts index 838b82b8ef..e5e398d4cc 100644 --- a/cli/ts/index.ts +++ b/cli/ts/index.ts @@ -16,7 +16,6 @@ import { showContracts, deployPoll, getPoll, - mergeMessages, publish, setVerifyingKeys, mergeSignups, @@ -93,9 +92,8 @@ program .option("-vk, --vk-contract ", "the VkRegistry contract address") .requiredOption("-s, --state-tree-depth ", "the state tree depth", parseInt) .requiredOption("-i, --int-state-tree-depth ", "the intermediate state tree depth", parseInt) - .requiredOption("-m, --msg-tree-depth ", "the message tree depth", parseInt) .requiredOption("-v, --vote-option-tree-depth ", "the vote option tree depth", parseInt) - .requiredOption("-b, --msg-batch-depth ", "the message batch depth", parseInt) + .requiredOption("-b, --msg-batch-size ", "the message batch size", parseInt) .requiredOption( "-p, --process-messages-zkey ", "the process messages zkey path (see different options for zkey files to use specific circuits https://maci.pse.dev/docs/trusted-setup, https://maci.pse.dev/docs/testing/#pre-compiled-artifacts-for-testing)", @@ -111,9 +109,8 @@ program await checkVerifyingKeys({ stateTreeDepth: cmdOptions.stateTreeDepth, intStateTreeDepth: cmdOptions.intStateTreeDepth, - messageTreeDepth: cmdOptions.msgTreeDepth, voteOptionTreeDepth: cmdOptions.voteOptionTreeDepth, - messageBatchDepth: cmdOptions.msgBatchDepth, + messageBatchSize: cmdOptions.msgBatchSize, processMessagesZkeyPath: cmdOptions.processMessagesZkey, tallyVotesZkeyPath: cmdOptions.tallyVotesZkey, vkRegistry: cmdOptions.vkContract, @@ -175,8 +172,7 @@ program .option("-vk, --vkRegistryAddress ", "the vk registry contract address") .requiredOption("-t, --duration ", "the poll duration", parseInt) .requiredOption("-i, --int-state-tree-depth ", "the int state tree depth", parseInt) - .requiredOption("-b, --msg-batch-depth ", "the message tree sub depth", parseInt) - .requiredOption("-m, --msg-tree-depth ", "the message tree depth", parseInt) + .requiredOption("-b, --msg-batch-size ", "the message batch size", parseInt) .requiredOption("-v, --vote-option-tree-depth ", "the vote option tree depth", parseInt) .requiredOption("-pk, --pubkey ", "the coordinator public key") .option( @@ -195,8 +191,7 @@ program await deployPoll({ pollDuration: cmdObj.duration, intStateTreeDepth: cmdObj.intStateTreeDepth, - messageTreeSubDepth: cmdObj.msgBatchDepth, - messageTreeDepth: cmdObj.msgTreeDepth, + messageBatchSize: cmdObj.msgBatchSize, voteOptionTreeDepth: cmdObj.voteOptionTreeDepth, coordinatorPubkey: cmdObj.pubkey, maciAddress: cmdObj.maciAddress, @@ -214,9 +209,8 @@ program .description("set the verifying keys") .requiredOption("-s, --state-tree-depth ", "the state tree depth", parseInt) .requiredOption("-i, --int-state-tree-depth ", "the intermediate state tree depth", parseInt) - .requiredOption("-m, --msg-tree-depth ", "the message tree depth", parseInt) .requiredOption("-v, --vote-option-tree-depth ", "the vote option tree depth", parseInt) - .requiredOption("-b, --msg-batch-depth ", "the message batch depth", parseInt) + .requiredOption("-b, --msg-batch-size ", "the message batch size", parseInt) .option( "-pqv, --process-messages-zkey-qv ", "the process messages qv zkey path (see different options for zkey files to use specific circuits https://maci.pse.dev/docs/trusted-setup, https://maci.pse.dev/docs/testing/#pre-compiled-artifacts-for-testing)", @@ -249,9 +243,8 @@ program await setVerifyingKeys({ stateTreeDepth: cmdObj.stateTreeDepth, intStateTreeDepth: cmdObj.intStateTreeDepth, - messageTreeDepth: cmdObj.msgTreeDepth, voteOptionTreeDepth: cmdObj.voteOptionTreeDepth, - messageBatchDepth: cmdObj.msgBatchDepth, + messageBatchSize: cmdObj.msgBatchSize, processMessagesZkeyPathQv: cmdObj.processMessagesZkeyQv, tallyVotesZkeyPathQv: cmdObj.tallyVotesZkeyQv, processMessagesZkeyPathNonQv: cmdObj.processMessagesZkeyNonQv, @@ -307,29 +300,7 @@ program program.error((error as Error).message, { exitCode: 1 }); } }); -program - .command("mergeMessages") - .description("merge the message accumulator queue") - .option("-q, --quiet ", "whether to print values to the console", (value) => value === "true", false) - .option("-r, --rpc-provider ", "the rpc provider URL") - .option("-x, --maci-address ", "the MACI contract address") - .requiredOption("-o, --poll-id ", "the poll id", BigInt) - .option("-n, --num-queue-ops ", "the number of queue operations", parseInt) - .action(async (cmdObj) => { - try { - const signer = await getSigner(); - await mergeMessages({ - pollId: cmdObj.pollId, - maciAddress: cmdObj.maciAddress, - numQueueOps: cmdObj.numQueueOps?.toString(), - quiet: cmdObj.quiet, - signer, - }); - } catch (error) { - program.error((error as Error).message, { exitCode: 1 }); - } - }); program .command("mergeSignups") .description("merge the signups accumulator queue") @@ -680,7 +651,6 @@ export { genKeyPair, genMaciPubKey, genProofs, - mergeMessages, mergeSignups, publish, publishBatch, @@ -701,7 +671,6 @@ export type { GenProofsArgs, PublishArgs, SignupArgs, - MergeMessagesArgs, MergeSignupsArgs, VerifyArgs, ProveOnChainArgs, diff --git a/cli/ts/sdk/index.ts b/cli/ts/sdk/index.ts index cf803bf35b..cdeaeb1875 100644 --- a/cli/ts/sdk/index.ts +++ b/cli/ts/sdk/index.ts @@ -1,7 +1,6 @@ import { extractVkToFile } from "../commands/extractVkToFile"; import { genKeyPair } from "../commands/genKeyPair"; import { genMaciPubKey } from "../commands/genPubKey"; -import { mergeMessages } from "../commands/mergeMessages"; import { mergeSignups } from "../commands/mergeSignups"; import { getPoll } from "../commands/poll"; import { publish, publishBatch } from "../commands/publish"; @@ -19,7 +18,6 @@ export { getPoll, extractVkToFile, mergeSignups, - mergeMessages, }; export type { ISnarkJSVerificationKey } from "maci-circuits"; diff --git a/cli/ts/utils/index.ts b/cli/ts/utils/index.ts index 90bd5388d6..79f9ee36c2 100644 --- a/cli/ts/utils/index.ts +++ b/cli/ts/utils/index.ts @@ -28,7 +28,6 @@ export type { SignupArgs, ISignupData, SetVerifyingKeysArgs, - MergeMessagesArgs, MergeSignupsArgs, ProveOnChainArgs, PublishArgs, diff --git a/cli/ts/utils/interfaces.ts b/cli/ts/utils/interfaces.ts index f4e6f41693..922bbd57e7 100644 --- a/cli/ts/utils/interfaces.ts +++ b/cli/ts/utils/interfaces.ts @@ -164,20 +164,15 @@ export interface CheckVerifyingKeysArgs { */ intStateTreeDepth: number; - /** - * The depth of the message tree - */ - messageTreeDepth: number; - /** * The depth of the vote option tree */ voteOptionTreeDepth: number; /** - * The depth of the message batch tree + * The size of the message batch */ - messageBatchDepth: number; + messageBatchSize: number; /** * The path to the process messages zkey @@ -280,14 +275,9 @@ export interface DeployPollArgs { intStateTreeDepth: number; /** - * The depth of the message tree sublevels + * The size of the message batch */ - messageTreeSubDepth: number; - - /** - * The depth of the message tree - */ - messageTreeDepth: number; + messageBatchSize: number; /** * The depth of the vote option tree @@ -516,36 +506,6 @@ export interface GenProofsArgs { tallyAddress?: string; } -/** - * Interface for the arguments to the mergeMessages command - */ -export interface MergeMessagesArgs { - /** - * The id of the poll - */ - pollId: bigint; - - /** - * A signer object - */ - signer: Signer; - - /** - * Whether to log the output - */ - quiet?: boolean; - - /** - * The address of the MACI contract - */ - maciAddress?: string; - - /** - * The number of queue operations to merge - */ - numQueueOps?: string; -} - /** * Interface for the arguments to the mergeSignups command */ @@ -755,20 +715,15 @@ export interface SetVerifyingKeysArgs { */ intStateTreeDepth: number; - /** - * The depth of the message tree - */ - messageTreeDepth: number; - /** * The depth of the vote option tree */ voteOptionTreeDepth: number; /** - * The depth of the message batch tree + * The size of message batch */ - messageBatchDepth: number; + messageBatchSize: number; /** * The path to the process messages qv zkey diff --git a/contracts/.nx/workspace-data/file-map.json b/contracts/.nx/workspace-data/file-map.json new file mode 100644 index 0000000000..c0b6b74852 --- /dev/null +++ b/contracts/.nx/workspace-data/file-map.json @@ -0,0 +1,572 @@ +{ + "version": "6.0", + "nxVersion": "19.2.0", + "deps": { + "@nomicfoundation/hardhat-ethers": "^3.0.6", + "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "@openzeppelin/contracts": "^5.0.2", + "@zk-kit/imt.sol": "2.0.0-beta.12", + "circomlibjs": "^0.1.7", + "ethers": "^6.13.1", + "hardhat": "^2.22.4", + "lowdb": "^1.0.0", + "maci-circuits": "2.0.0-alpha", + "maci-core": "2.0.0-alpha", + "maci-crypto": "2.0.0-alpha", + "maci-domainobjs": "2.0.0-alpha", + "solidity-docgen": "^0.6.0-beta.36", + "uuid": "^10.0.0", + "@types/chai": "^4.3.11", + "@types/circomlibjs": "^0.1.6", + "@types/lowdb": "^1.0.15", + "@types/mocha": "^10.0.7", + "@types/node": "^20.14.8", + "@types/snarkjs": "^0.7.8", + "@types/uuid": "^10.0.0", + "chai": "^4.3.10", + "dotenv": "^16.4.5", + "hardhat-artifactor": "^0.2.0", + "hardhat-contract-sizer": "^2.10.0", + "ts-node": "^10.9.1", + "typescript": "^5.5.2" + }, + "pathMappings": {}, + "nxJsonPlugins": [], + "fileMap": { + "projectFileMap": {}, + "nonProjectFiles": [ + { + "file": ".env.example", + "hash": "15632294391262897468" + }, + { + "file": ".eslintrc.js", + "hash": "13229915871480295193" + }, + { + "file": ".gitignore", + "hash": "13730679138274358620" + }, + { + "file": ".npmignore", + "hash": "3679978886324103548" + }, + { + "file": "CHANGELOG.md", + "hash": "10198965579070896133" + }, + { + "file": "README.md", + "hash": "6427624311883073094" + }, + { + "file": "contracts/.solhint.json", + "hash": "7625145626353633285" + }, + { + "file": "contracts/MACI.sol", + "hash": "6210968932685025002" + }, + { + "file": "contracts/MessageProcessor.sol", + "hash": "9369062046489049431" + }, + { + "file": "contracts/MessageProcessorFactory.sol", + "hash": "18315498813145404244" + }, + { + "file": "contracts/Poll.sol", + "hash": "2538654615536947155" + }, + { + "file": "contracts/PollFactory.sol", + "hash": "4285580269696869084" + }, + { + "file": "contracts/SignUpToken.sol", + "hash": "6889188637462174946" + }, + { + "file": "contracts/Tally.sol", + "hash": "6291586251160616969" + }, + { + "file": "contracts/TallyFactory.sol", + "hash": "10546984068022793417" + }, + { + "file": "contracts/VkRegistry.sol", + "hash": "7549118720052787520" + }, + { + "file": "contracts/benchmarks/HasherBenchmarks.sol", + "hash": "6351980648809979678" + }, + { + "file": "contracts/crypto/BabyJubJub.sol", + "hash": "16479100088697342506" + }, + { + "file": "contracts/crypto/Hasher.sol", + "hash": "8586852389276276484" + }, + { + "file": "contracts/crypto/MockVerifier.sol", + "hash": "1256558574282388530" + }, + { + "file": "contracts/crypto/Pairing.sol", + "hash": "14908328227781864442" + }, + { + "file": "contracts/crypto/PoseidonT3.sol", + "hash": "6837975831706217922" + }, + { + "file": "contracts/crypto/PoseidonT4.sol", + "hash": "13536397536923710046" + }, + { + "file": "contracts/crypto/PoseidonT5.sol", + "hash": "18228524023931370699" + }, + { + "file": "contracts/crypto/PoseidonT6.sol", + "hash": "17161833435682918862" + }, + { + "file": "contracts/crypto/SnarkCommon.sol", + "hash": "3239370262929372477" + }, + { + "file": "contracts/crypto/SnarkConstants.sol", + "hash": "16939489834635719258" + }, + { + "file": "contracts/crypto/Verifier.sol", + "hash": "11578394042197610763" + }, + { + "file": "contracts/gatekeepers/EASGatekeeper.sol", + "hash": "202955377193922685" + }, + { + "file": "contracts/gatekeepers/FreeForAllSignUpGatekeeper.sol", + "hash": "2426599184000019433" + }, + { + "file": "contracts/gatekeepers/GitcoinPassportGatekeeper.sol", + "hash": "2595698782440703658" + }, + { + "file": "contracts/gatekeepers/HatsGatekeeperBase.sol", + "hash": "1307832523932023318" + }, + { + "file": "contracts/gatekeepers/HatsGatekeeperMultiple.sol", + "hash": "12122649815869191635" + }, + { + "file": "contracts/gatekeepers/HatsGatekeeperSingle.sol", + "hash": "4436740644613888617" + }, + { + "file": "contracts/gatekeepers/SemaphoreGatekeeper.sol", + "hash": "8569912333473554871" + }, + { + "file": "contracts/gatekeepers/SignUpGatekeeper.sol", + "hash": "2756688768586414125" + }, + { + "file": "contracts/gatekeepers/SignUpTokenGatekeeper.sol", + "hash": "10422325540628748051" + }, + { + "file": "contracts/gatekeepers/zupass/ZupassGatekeeper.sol", + "hash": "7590440131265872327" + }, + { + "file": "contracts/gatekeepers/zupass/ZupassGroth16Verifier.sol", + "hash": "4665090691101751062" + }, + { + "file": "contracts/initialVoiceCreditProxy/ConstantInitialVoiceCreditProxy.sol", + "hash": "11645600634297997744" + }, + { + "file": "contracts/initialVoiceCreditProxy/InitialVoiceCreditProxy.sol", + "hash": "8560920400208569518" + }, + { + "file": "contracts/interfaces/IEAS.sol", + "hash": "5572590588822988615" + }, + { + "file": "contracts/interfaces/IGitcoinPassportDecoder.sol", + "hash": "18408472187152157116" + }, + { + "file": "contracts/interfaces/IHats.sol", + "hash": "10294009523475085300" + }, + { + "file": "contracts/interfaces/IMACI.sol", + "hash": "9665010136858305067" + }, + { + "file": "contracts/interfaces/IMPFactory.sol", + "hash": "14631848730732843106" + }, + { + "file": "contracts/interfaces/IMessageProcessor.sol", + "hash": "2358950118632904611" + }, + { + "file": "contracts/interfaces/IPoll.sol", + "hash": "17761025110426556290" + }, + { + "file": "contracts/interfaces/IPollFactory.sol", + "hash": "11237772357618328929" + }, + { + "file": "contracts/interfaces/ISemaphore.sol", + "hash": "9891090952629620910" + }, + { + "file": "contracts/interfaces/ITallyFactory.sol", + "hash": "10806728525515884474" + }, + { + "file": "contracts/interfaces/IVerifier.sol", + "hash": "16227622549440079985" + }, + { + "file": "contracts/interfaces/IVkRegistry.sol", + "hash": "3246138951708773250" + }, + { + "file": "contracts/mocks/MockEAS.sol", + "hash": "16458306502547071012" + }, + { + "file": "contracts/mocks/MockGitcoinPassportDecoder.sol", + "hash": "2761486377931249721" + }, + { + "file": "contracts/mocks/MockHatsProtocol.sol", + "hash": "2102570808766920644" + }, + { + "file": "contracts/mocks/MockSemaphore.sol", + "hash": "14893189542672929216" + }, + { + "file": "contracts/trees/EmptyBallotRoots.sol", + "hash": "17547908223677065624" + }, + { + "file": "contracts/trees/LazyIMT.sol", + "hash": "5477187387652728630" + }, + { + "file": "contracts/trees/zeros/MerkleBinary0.sol", + "hash": "11373316023425641265" + }, + { + "file": "contracts/trees/zeros/MerkleBinaryBlankSl.sol", + "hash": "14957882559993561893" + }, + { + "file": "contracts/trees/zeros/MerkleBinaryMaci.sol", + "hash": "8894600966776433421" + }, + { + "file": "contracts/trees/zeros/MerkleQuinary0.sol", + "hash": "11037575354755096344" + }, + { + "file": "contracts/trees/zeros/MerkleQuinaryBlankSl.sol", + "hash": "1912795270117728141" + }, + { + "file": "contracts/trees/zeros/MerkleQuinaryMaci.sol", + "hash": "16963409212452453928" + }, + { + "file": "contracts/trees/zeros/MerkleQuinaryMaciWithSha256.sol", + "hash": "13058965978682977076" + }, + { + "file": "contracts/utilities/CommonUtilities.sol", + "hash": "15388179638543681721" + }, + { + "file": "contracts/utilities/DomainObjs.sol", + "hash": "404271263832116330" + }, + { + "file": "contracts/utilities/Params.sol", + "hash": "10067832123624224330" + }, + { + "file": "contracts/utilities/Utilities.sol", + "hash": "18299056422434852206" + }, + { + "file": "deploy-config-example.json", + "hash": "10725617117977209513" + }, + { + "file": "hardhat.config.ts", + "hash": "16728432087225543716" + }, + { + "file": "package.json", + "hash": "9199392747160886147" + }, + { + "file": "scripts/compileSol.ts", + "hash": "18432679843284376413" + }, + { + "file": "tasks/deploy/index.ts", + "hash": "8878194502996518733" + }, + { + "file": "tasks/deploy/maci/01-constantInitialVoiceCreditProxy.ts", + "hash": "2321384985757935847" + }, + { + "file": "tasks/deploy/maci/02-gatekeepers.ts", + "hash": "6617365051591267238" + }, + { + "file": "tasks/deploy/maci/03-verifier.ts", + "hash": "10469946020617408869" + }, + { + "file": "tasks/deploy/maci/04-poseidon.ts", + "hash": "12358064199539432580" + }, + { + "file": "tasks/deploy/maci/05-pollFactory.ts", + "hash": "3180217303333545691" + }, + { + "file": "tasks/deploy/maci/06-messageProcessorFactory.ts", + "hash": "2217555742709570972" + }, + { + "file": "tasks/deploy/maci/07-tallyFactory.ts", + "hash": "7033381334718837179" + }, + { + "file": "tasks/deploy/maci/08-maci.ts", + "hash": "14111076116074128524" + }, + { + "file": "tasks/deploy/maci/09-vkRegistry.ts", + "hash": "9423054826338863646" + }, + { + "file": "tasks/deploy/poll/01-poll.ts", + "hash": "4305038256390380429" + }, + { + "file": "tasks/helpers/ContractStorage.ts", + "hash": "15804621985272389870" + }, + { + "file": "tasks/helpers/ContractVerifier.ts", + "hash": "10699087730230031451" + }, + { + "file": "tasks/helpers/Deployment.ts", + "hash": "12361243851567711303" + }, + { + "file": "tasks/helpers/ProofGenerator.ts", + "hash": "15464135712842161885" + }, + { + "file": "tasks/helpers/Prover.ts", + "hash": "867443952525058565" + }, + { + "file": "tasks/helpers/TreeMerger.ts", + "hash": "7030976863030650479" + }, + { + "file": "tasks/helpers/abi.ts", + "hash": "6697222876673072074" + }, + { + "file": "tasks/helpers/constants.ts", + "hash": "10955334027575229042" + }, + { + "file": "tasks/helpers/numericParser.ts", + "hash": "8734271717535435278" + }, + { + "file": "tasks/helpers/types.ts", + "hash": "1241345901428277330" + }, + { + "file": "tasks/runner/deployFull.ts", + "hash": "13570236853565155301" + }, + { + "file": "tasks/runner/deployPoll.ts", + "hash": "1111066950451691649" + }, + { + "file": "tasks/runner/merge.ts", + "hash": "14671043634079072696" + }, + { + "file": "tasks/runner/prove.ts", + "hash": "8221708548452868345" + }, + { + "file": "tasks/runner/verifyFull.ts", + "hash": "4479079775595966374" + }, + { + "file": "templates/EmptyBallotRoots.sol.template", + "hash": "17333860194677188776" + }, + { + "file": "templates/MerkleZeros.sol.template", + "hash": "6735062198728187226" + }, + { + "file": "testScriptLocalhost.sh", + "hash": "6242967841799401644" + }, + { + "file": "tests/EASGatekeeper.test.ts", + "hash": "4505966188207589614" + }, + { + "file": "tests/GitcoinPassportGatekeeper.test.ts", + "hash": "13009838034233779803" + }, + { + "file": "tests/Hasher.test.ts", + "hash": "17292202145139689641" + }, + { + "file": "tests/HasherBenchmarks.test.ts", + "hash": "16499734320521723587" + }, + { + "file": "tests/HatsGatekeeper.test.ts", + "hash": "373322220760058484" + }, + { + "file": "tests/MACI.test.ts", + "hash": "4985954350901860693" + }, + { + "file": "tests/MessageProcessor.test.ts", + "hash": "11851253915097933910" + }, + { + "file": "tests/Poll.test.ts", + "hash": "10118208690588060296" + }, + { + "file": "tests/PollFactory.test.ts", + "hash": "7644758437564609709" + }, + { + "file": "tests/SemaphoreGatekeeper.test.ts", + "hash": "7482152715119203200" + }, + { + "file": "tests/SignUpGatekeeper.test.ts", + "hash": "5755173139041425001" + }, + { + "file": "tests/Tally.test.ts", + "hash": "15761092877803508760" + }, + { + "file": "tests/TallyNonQv.test.ts", + "hash": "10975470947805554373" + }, + { + "file": "tests/Utilities.test.ts", + "hash": "3620856216299717518" + }, + { + "file": "tests/Verifier.test.ts", + "hash": "15461397013686255362" + }, + { + "file": "tests/VkRegistry.test.ts", + "hash": "5322885075462948979" + }, + { + "file": "tests/ZupassGatekeeper.test.ts", + "hash": "6596205249539536514" + }, + { + "file": "tests/constants.ts", + "hash": "3884455711734488573" + }, + { + "file": "tests/utils.ts", + "hash": "16260732813690167316" + }, + { + "file": "ts/buildPoseidon.ts", + "hash": "10897585445647714671" + }, + { + "file": "ts/constants.ts", + "hash": "5037459666093381282" + }, + { + "file": "ts/deploy.ts", + "hash": "11118223591559678865" + }, + { + "file": "ts/genEmptyBallotRootsContract.ts", + "hash": "9506593663971856640" + }, + { + "file": "ts/genMaciState.ts", + "hash": "11380461821382405809" + }, + { + "file": "ts/genZerosContract.ts", + "hash": "18339407776915141681" + }, + { + "file": "ts/index.ts", + "hash": "14990913341618370155" + }, + { + "file": "ts/types.ts", + "hash": "2205941998741399567" + }, + { + "file": "ts/utils.ts", + "hash": "12140194055989429663" + }, + { + "file": "tsconfig.build.json", + "hash": "7252134500199993931" + }, + { + "file": "tsconfig.json", + "hash": "14813452554169376286" + } + ] + } +} diff --git a/contracts/.nx/workspace-data/project-graph.json b/contracts/.nx/workspace-data/project-graph.json new file mode 100644 index 0000000000..20ecc9e6e8 --- /dev/null +++ b/contracts/.nx/workspace-data/project-graph.json @@ -0,0 +1,6 @@ +{ + "nodes": {}, + "externalNodes": {}, + "dependencies": {}, + "version": "6.0" +} diff --git a/contracts/contracts/MACI.sol b/contracts/contracts/MACI.sol index 758322f901..21bd4c1bc5 100644 --- a/contracts/contracts/MACI.sol +++ b/contracts/contracts/MACI.sol @@ -24,8 +24,8 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { /// if we change the state tree depth! uint8 public immutable stateTreeDepth; - uint8 internal constant TREE_ARITY = 2; - uint8 internal constant MESSAGE_TREE_ARITY = 5; + uint8 internal constant STATE_TREE_ARITY = 2; + uint8 internal constant VOTE_TREE_ARITY = 5; /// @notice The hash of a blank state leaf uint256 internal constant BLANK_STATE_LEAF_HASH = @@ -119,8 +119,7 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { /// @notice Allows any eligible user sign up. The sign-up gatekeeper should prevent /// double sign-ups or ineligible users from doing so. This function will - /// only succeed if the sign-up deadline has not passed. It also enqueues a - /// fresh state leaf into the state AccQueue. + /// only succeed if the sign-up deadline has not passed. /// @param _pubKey The user's desired public key. /// @param _signUpGatekeeperData Data to pass to the sign-up gatekeeper's /// register() function. For instance, the POAPGatekeeper or @@ -135,7 +134,7 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { bytes memory _initialVoiceCreditProxyData ) public virtual { // ensure we do not have more signups than what the circuits support - if (lazyIMTData.numberOfLeaves >= uint256(TREE_ARITY) ** uint256(stateTreeDepth)) revert TooManySignups(); + if (lazyIMTData.numberOfLeaves >= uint256(STATE_TREE_ARITY) ** uint256(stateTreeDepth)) revert TooManySignups(); // ensure that the public key is on the baby jubjub curve if (!CurveBabyJubJub.isOnCurve(_pubKey.x, _pubKey.y)) { @@ -161,6 +160,7 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { /// @notice Deploy a new Poll contract. /// @param _duration How long should the Poll last for /// @param _treeDepths The depth of the Merkle trees + /// @param _messageBatchSize The message batch size /// @param _coordinatorPubKey The coordinator's public key /// @param _verifier The Verifier Contract /// @param _vkRegistry The VkRegistry Contract @@ -169,6 +169,7 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { function deployPoll( uint256 _duration, TreeDepths memory _treeDepths, + uint8 _messageBatchSize, PubKey memory _coordinatorPubKey, address _verifier, address _vkRegistry, @@ -188,15 +189,19 @@ contract MACI is IMACI, DomainObjs, Params, Utilities { revert InvalidPubKey(); } - MaxValues memory maxValues = MaxValues({ - maxMessages: uint256(MESSAGE_TREE_ARITY) ** _treeDepths.messageTreeDepth, - maxVoteOptions: uint256(MESSAGE_TREE_ARITY) ** _treeDepths.voteOptionTreeDepth - }); + uint256 maxVoteOptions = VOTE_TREE_ARITY ** _treeDepths.voteOptionTreeDepth; // the owner of the message processor and tally contract will be the msg.sender address _msgSender = msg.sender; - address p = pollFactory.deploy(_duration, maxValues, _treeDepths, _coordinatorPubKey, address(this)); + address p = pollFactory.deploy( + _duration, + maxVoteOptions, + _treeDepths, + _messageBatchSize, + _coordinatorPubKey, + address(this) + ); address mp = messageProcessorFactory.deploy(_verifier, _vkRegistry, p, _msgSender, _mode); address tally = tallyFactory.deploy(_verifier, _vkRegistry, p, mp, _msgSender, _mode); diff --git a/contracts/contracts/MessageProcessor.sol b/contracts/contracts/MessageProcessor.sol index 1161139c8d..63e36b0bd7 100644 --- a/contracts/contracts/MessageProcessor.sol +++ b/contracts/contracts/MessageProcessor.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import { AccQueue } from "./trees/AccQueue.sol"; import { IMACI } from "./interfaces/IMACI.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IPoll } from "./interfaces/IPoll.sol"; @@ -21,7 +20,6 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes /// @notice custom errors error NoMoreMessages(); error StateNotMerged(); - error MessageAqNotMerged(); error InvalidProcessMessageProof(); error MaxVoteOptionsTooLarge(); error NumSignUpsTooLarge(); @@ -37,10 +35,8 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes /// @notice The number of batches processed uint256 public numBatchesProcessed; - /// @notice The current message batch index. When the coordinator runs - /// processMessages(), this action relates to messages - /// currentMessageBatchIndex to currentMessageBatchIndex + messageBatchSize. - uint256 public currentMessageBatchIndex; + /// @notice The current message batch index + uint256 public currentBatchIndex; /// @inheritdoc IMessageProcessor uint256 public sbCommitment; @@ -67,6 +63,7 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes vkRegistry = IVkRegistry(_vkRegistry); poll = IPoll(_poll); mode = _mode; + currentBatchIndex = 1; } /// @notice Update the Poll's currentSbCommitment if the proof is valid. @@ -82,51 +79,38 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes revert NoMoreMessages(); } - // The state AccQueue must be merged - if (!poll.stateMerged()) { - revert StateNotMerged(); - } - // Retrieve stored vals - (, uint8 messageTreeSubDepth, uint8 messageTreeDepth, uint8 voteOptionTreeDepth) = poll.treeDepths(); - // calculate the message batch size from the message tree subdepth - uint256 messageBatchSize = TREE_ARITY ** messageTreeSubDepth; - - (, AccQueue messageAq) = poll.extContracts(); - - // Require that the message queue has been merged - uint256 messageRoot = messageAq.getMainRoot(messageTreeDepth); - if (messageRoot == 0) { - revert MessageAqNotMerged(); - } + (, uint8 voteOptionTreeDepth) = poll.treeDepths(); + // Retrieve stored val + uint8 messageBatchSize = poll.messageBatchSize(); + uint256[] memory batchHashes; // Copy the state and ballot commitment and set the batch index if this // is the first batch to process if (numBatchesProcessed == 0) { uint256 currentSbCommitment = poll.currentSbCommitment(); sbCommitment = currentSbCommitment; - (, uint256 numMessages) = poll.numSignUpsAndMessages(); - uint256 r = numMessages % messageBatchSize; - currentMessageBatchIndex = numMessages; + poll.padLastBatch(); + batchHashes = poll.getBatchHashes(); + currentBatchIndex = batchHashes.length; - if (currentMessageBatchIndex > 0) { - if (r == 0) { - currentMessageBatchIndex -= messageBatchSize; - } else { - currentMessageBatchIndex -= r; - } + if (currentBatchIndex > 0) { + currentBatchIndex -= 1; } + } else { + batchHashes = poll.getBatchHashes(); } + uint256 outputBatchHash = batchHashes[currentBatchIndex]; + if ( !verifyProcessProof( - currentMessageBatchIndex, - messageRoot, + currentBatchIndex, + outputBatchHash, sbCommitment, _newSbCommitment, - messageTreeSubDepth, - messageTreeDepth, + messageBatchSize, voteOptionTreeDepth, _proof ) @@ -134,40 +118,26 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes revert InvalidProcessMessageProof(); } - { - (, uint256 numMessages) = poll.numSignUpsAndMessages(); - // Decrease the message batch start index to ensure that each - // message batch is processed in order - if (currentMessageBatchIndex > 0) { - currentMessageBatchIndex -= messageBatchSize; - } + (, uint256 numMessages) = poll.numSignUpsAndMessages(); - updateMessageProcessingData( - _newSbCommitment, - currentMessageBatchIndex, - numMessages <= messageBatchSize * (numBatchesProcessed + 1) - ); - } + updateMessageProcessingData(_newSbCommitment, numMessages <= messageBatchSize * (numBatchesProcessed + 1)); } /// @notice Verify the proof for processMessage /// @dev used to update the sbCommitment - /// @param _currentMessageBatchIndex The batch index of current message batch - /// @param _messageRoot The message tree root + /// @param _currentBatchIndex The batch index of current message batch /// @param _currentSbCommitment The current sbCommitment (state and ballot) /// @param _newSbCommitment The new sbCommitment after we update this message batch - /// @param _messageTreeSubDepth The message tree subdepth - /// @param _messageTreeDepth The message tree depth + /// @param _messageBatchSize The message batch size /// @param _voteOptionTreeDepth The vote option tree depth /// @param _proof The zk-SNARK proof /// @return isValid Whether the proof is valid function verifyProcessProof( - uint256 _currentMessageBatchIndex, - uint256 _messageRoot, + uint256 _currentBatchIndex, + uint256 _outputBatchHash, uint256 _currentSbCommitment, uint256 _newSbCommitment, - uint8 _messageTreeSubDepth, - uint8 _messageTreeDepth, + uint8 _messageBatchSize, uint8 _voteOptionTreeDepth, uint256[8] memory _proof ) internal view returns (bool isValid) { @@ -175,26 +145,25 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes // get the message batch size from the message tree subdepth // get the number of signups (uint256 numSignUps, uint256 numMessages) = poll.numSignUpsAndMessages(); - (IMACI maci, ) = poll.extContracts(); + IMACI maci = poll.extContracts(); // Calculate the public input hash (a SHA256 hash of several values) uint256 publicInputHash = genProcessMessagesPublicInputHash( - _currentMessageBatchIndex, - _messageRoot, + _currentBatchIndex, + _outputBatchHash, numSignUps, numMessages, _currentSbCommitment, _newSbCommitment, - _messageTreeSubDepth, + _messageBatchSize, _voteOptionTreeDepth ); // Get the verifying key from the VkRegistry VerifyingKey memory vk = vkRegistry.getProcessVk( maci.stateTreeDepth(), - _messageTreeDepth, _voteOptionTreeDepth, - TREE_ARITY ** _messageTreeSubDepth, + _messageBatchSize, mode ); @@ -208,21 +177,21 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes /// as a single public input and the preimage as private inputs, we reduce /// its verification gas cost though the number of constraints will be /// higher and proving time will be longer. - /// @param _currentMessageBatchIndex The batch index of current message batch + /// @param _currentBatchIndex The batch index of current message batch /// @param _numSignUps The number of users that signup /// @param _numMessages The number of messages /// @param _currentSbCommitment The current sbCommitment (state and ballot root) /// @param _newSbCommitment The new sbCommitment after we update this message batch - /// @param _messageTreeSubDepth The message tree subdepth + /// @param _messageBatchSize The message batch size /// @return inputHash Returns the SHA256 hash of the packed values function genProcessMessagesPublicInputHash( - uint256 _currentMessageBatchIndex, - uint256 _messageRoot, + uint256 _currentBatchIndex, + uint256 _outputBatchHash, uint256 _numSignUps, uint256 _numMessages, uint256 _currentSbCommitment, uint256 _newSbCommitment, - uint8 _messageTreeSubDepth, + uint8 _messageBatchSize, uint8 _voteOptionTreeDepth ) public view returns (uint256 inputHash) { uint256 coordinatorPubKeyHash = poll.coordinatorPubKeyHash(); @@ -231,24 +200,21 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes // pack the values uint256 packedVals = genProcessMessagesPackedVals( - _currentMessageBatchIndex, + _currentBatchIndex, _numSignUps, _numMessages, - _messageTreeSubDepth, + _messageBatchSize, _voteOptionTreeDepth ); - (uint256 deployTime, uint256 duration) = poll.getDeployTimeAndDuration(); - // generate the circuit only public input - uint256[] memory input = new uint256[](7); + uint256[] memory input = new uint256[](6); input[0] = packedVals; input[1] = coordinatorPubKeyHash; - input[2] = _messageRoot; + input[2] = _outputBatchHash; input[3] = _currentSbCommitment; input[4] = _newSbCommitment; - input[5] = deployTime + duration; - input[6] = actualStateTreeDepth; + input[5] = actualStateTreeDepth; inputHash = sha256Hash(input); } @@ -257,48 +223,40 @@ contract MessageProcessor is Ownable, SnarkCommon, Hasher, CommonUtilities, IMes /// 250-bit value, which consists of the maximum number of vote options, the /// number of signups, the current message batch index, and the end index of /// the current batch. - /// @param _currentMessageBatchIndex batch index of current message batch + /// @param _currentBatchIndex batch index of current message batch /// @param _numSignUps number of users that signup /// @param _numMessages number of messages - /// @param _messageTreeSubDepth message tree subdepth + /// @param _messageBatchSize The message batch size /// @param _voteOptionTreeDepth vote option tree depth /// @return result The packed value function genProcessMessagesPackedVals( - uint256 _currentMessageBatchIndex, + uint256 _currentBatchIndex, uint256 _numSignUps, uint256 _numMessages, - uint8 _messageTreeSubDepth, + uint8 _messageBatchSize, uint8 _voteOptionTreeDepth ) public pure returns (uint256 result) { uint256 maxVoteOptions = TREE_ARITY ** _voteOptionTreeDepth; - // calculate the message batch size from the message tree subdepth - uint256 messageBatchSize = TREE_ARITY ** _messageTreeSubDepth; - uint256 batchEndIndex = _currentMessageBatchIndex + messageBatchSize; + uint256 batchEndIndex = (_currentBatchIndex + 1) * _messageBatchSize; if (batchEndIndex > _numMessages) { batchEndIndex = _numMessages; } if (maxVoteOptions >= 2 ** 50) revert MaxVoteOptionsTooLarge(); if (_numSignUps >= 2 ** 50) revert NumSignUpsTooLarge(); - if (_currentMessageBatchIndex >= 2 ** 50) revert CurrentMessageBatchIndexTooLarge(); + if (_currentBatchIndex * _messageBatchSize >= 2 ** 50) revert CurrentMessageBatchIndexTooLarge(); if (batchEndIndex >= 2 ** 50) revert BatchEndIndexTooLarge(); - - result = maxVoteOptions + (_numSignUps << 50) + (_currentMessageBatchIndex << 100) + (batchEndIndex << 150); + result = maxVoteOptions + (_numSignUps << 50) + (_messageBatchSize << 100) + (batchEndIndex << 150); } /// @notice update message processing state variables /// @param _newSbCommitment sbCommitment to be updated - /// @param _currentMessageBatchIndex currentMessageBatchIndex to be updated /// @param _processingComplete update flag that indicate processing is finished or not - function updateMessageProcessingData( - uint256 _newSbCommitment, - uint256 _currentMessageBatchIndex, - bool _processingComplete - ) internal { + function updateMessageProcessingData(uint256 _newSbCommitment, bool _processingComplete) internal { sbCommitment = _newSbCommitment; processingComplete = _processingComplete; - currentMessageBatchIndex = _currentMessageBatchIndex; + currentBatchIndex -= 1; numBatchesProcessed++; } } diff --git a/contracts/contracts/Poll.sol b/contracts/contracts/Poll.sol index 4e5dd87851..3022419ed1 100644 --- a/contracts/contracts/Poll.sol +++ b/contracts/contracts/Poll.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; - import { Params } from "./utilities/Params.sol"; import { SnarkCommon } from "./crypto/SnarkCommon.sol"; import { EmptyBallotRoots } from "./trees/EmptyBallotRoots.sol"; @@ -57,15 +56,27 @@ contract Poll is Params, Utilities, SnarkCommon, EmptyBallotRoots, IPoll { /// to be used as public input for the circuit uint8 public actualStateTreeDepth; - /// @notice Max values for the poll - MaxValues public maxValues; + /// @notice Max vote options for the poll + uint256 public immutable maxVoteOptions; /// @notice Depths of the merkle trees TreeDepths public treeDepths; + /// @notice Message batch size for the poll + uint8 public immutable messageBatchSize; + /// @notice The contracts used by the Poll ExtContracts public extContracts; + /// @notice The array for chain hash checkpoints + uint256[] public batchHashes; + + /// @notice Current chain hash + uint256 public chainHash; + + /// @notice flag for batch padding + bool public isBatchHashesPadded; + error VotingPeriodOver(); error VotingPeriodNotOver(); error PollAlreadyInit(); @@ -73,23 +84,25 @@ contract Poll is Params, Utilities, SnarkCommon, EmptyBallotRoots, IPoll { error InvalidPubKey(); error StateAlreadyMerged(); error InvalidBatchLength(); + error BatchHashesAlreadyPadded(); event PublishMessage(Message _message, PubKey _encPubKey); event MergeMaciState(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. /// @param _duration The duration of the voting period, in seconds - /// @param _maxValues The maximum number of messages and vote options + /// @param _maxVoteOptions The maximum number of vote options /// @param _treeDepths The depths of the merkle trees + /// @param _messageBatchSize The message batch size /// @param _coordinatorPubKey The coordinator's public key /// @param _extContracts The external contracts + constructor( uint256 _duration, - MaxValues memory _maxValues, + uint256 _maxVoteOptions, TreeDepths memory _treeDepths, + uint8 _messageBatchSize, PubKey memory _coordinatorPubKey, ExtContracts memory _extContracts ) payable { @@ -106,8 +119,10 @@ contract Poll is Params, Utilities, SnarkCommon, EmptyBallotRoots, IPoll { extContracts = _extContracts; // store duration of the poll duration = _duration; - // store max values - maxValues = _maxValues; + // store max vote options + maxVoteOptions = _maxVoteOptions; + // store message batch size + messageBatchSize = _messageBatchSize; // store tree depth treeDepths = _treeDepths; // Record the current timestamp @@ -122,6 +137,11 @@ contract Poll is Params, Utilities, SnarkCommon, EmptyBallotRoots, IPoll { _; } + modifier isNotPadded() { + if (isBatchHashesPadded) revert BatchHashesAlreadyPadded(); + _; + } + /// @notice A modifier that causes the function to revert if the voting period is /// over modifier isWithinVotingDeadline() { @@ -132,7 +152,6 @@ contract Poll is Params, Utilities, SnarkCommon, EmptyBallotRoots, IPoll { /// @notice The initialization function. /// @dev Should be called immediately after Poll creation - /// and messageAq ownership transferred function init() public { if (isInit) revert PollAlreadyInit(); // set to true so it cannot be called again @@ -142,22 +161,26 @@ contract Poll is Params, Utilities, SnarkCommon, EmptyBallotRoots, IPoll { numMessages++; } - // init messageAq here by inserting placeholderLeaf + // init chainHash here by inserting placeholderLeaf uint256[2] memory dat; dat[0] = NOTHING_UP_MY_SLEEVE; dat[1] = 0; (Message memory _message, PubKey memory _padKey, uint256 placeholderLeaf) = padAndHashMessage(dat); - extContracts.messageAq.enqueue(placeholderLeaf); + chainHash = NOTHING_UP_MY_SLEEVE; + batchHashes.push(NOTHING_UP_MY_SLEEVE); + updateChainHash(placeholderLeaf); emit PublishMessage(_message, _padKey); } + // get all batch hash array elements + function getBatchHashes() external view returns (uint256[] memory) { + return batchHashes; + } + /// @inheritdoc IPoll function publishMessage(Message memory _message, PubKey calldata _encPubKey) public virtual isWithinVotingDeadline { - // we check that we do not exceed the max number of messages - if (numMessages >= maxValues.maxMessages) revert TooManyMessages(); - // check if the public key is on the curve if (!CurveBabyJubJub.isOnCurve(_encPubKey.x, _encPubKey.y)) { revert InvalidPubKey(); @@ -168,12 +191,33 @@ contract Poll is Params, Utilities, SnarkCommon, EmptyBallotRoots, IPoll { numMessages++; } - uint256 messageLeaf = hashMessageAndEncPubKey(_message, _encPubKey); - extContracts.messageAq.enqueue(messageLeaf); + // compute current message hash + uint256 messageHash = hashMessageAndEncPubKey(_message, _encPubKey); + + // update current message chain hash + updateChainHash(messageHash); emit PublishMessage(_message, _encPubKey); } + /// @notice compute and update current message chain hash + /// @param messageHash hash of the current message + function updateChainHash(uint256 messageHash) internal { + uint256 newChainHash = hash2([chainHash, messageHash]); + if (numMessages % messageBatchSize == 0) { + batchHashes.push(newChainHash); + } + chainHash = newChainHash; + } + + /// @notice pad last unclosed batch + function padLastBatch() external isAfterVotingDeadline isNotPadded { + if (numMessages % messageBatchSize != 0) { + batchHashes.push(chainHash); + } + isBatchHashesPadded = true; + } + /// @notice submit a message batch /// @dev Can only be submitted before the voting deadline /// @param _messages the messages @@ -228,18 +272,6 @@ contract Poll is Params, Utilities, SnarkCommon, EmptyBallotRoots, IPoll { emit MergeMaciState(mergedStateRoot, numSignups); } - /// @inheritdoc IPoll - function mergeMessageAqSubRoots(uint256 _numSrQueueOps) public isAfterVotingDeadline { - extContracts.messageAq.mergeSubRoots(_numSrQueueOps); - emit MergeMessageAqSubRoots(_numSrQueueOps); - } - - /// @inheritdoc IPoll - function mergeMessageAq() public isAfterVotingDeadline { - uint256 root = extContracts.messageAq.merge(treeDepths.messageTreeDepth); - emit MergeMessageAq(root); - } - /// @inheritdoc IPoll function getDeployTimeAndDuration() public view returns (uint256 pollDeployTime, uint256 pollDuration) { pollDeployTime = deployTime; diff --git a/contracts/contracts/PollFactory.sol b/contracts/contracts/PollFactory.sol index 31adc6a152..71a0f4894c 100644 --- a/contracts/contracts/PollFactory.sol +++ b/contracts/contracts/PollFactory.sol @@ -2,8 +2,6 @@ pragma solidity ^0.8.20; import { IMACI } from "./interfaces/IMACI.sol"; -import { AccQueue } from "./trees/AccQueue.sol"; -import { AccQueueQuinaryMaci } from "./trees/AccQueueQuinaryMaci.sol"; import { Params } from "./utilities/Params.sol"; import { DomainObjs } from "./utilities/DomainObjs.sol"; import { Poll } from "./Poll.sol"; @@ -14,7 +12,7 @@ import { IPollFactory } from "./interfaces/IPollFactory.sol"; /// size to stay within the limit set by EIP-170. contract PollFactory is Params, DomainObjs, IPollFactory { // custom error - error InvalidMaxValues(); + error InvalidMaxVoteOptions(); /// @notice The PollFactory constructor // solhint-disable-next-line no-empty-blocks @@ -23,31 +21,25 @@ contract PollFactory is Params, DomainObjs, IPollFactory { /// @inheritdoc IPollFactory function deploy( uint256 _duration, - MaxValues calldata _maxValues, + uint256 _maxVoteOptions, TreeDepths calldata _treeDepths, + uint8 _messageBatchSize, PubKey calldata _coordinatorPubKey, address _maci ) public virtual returns (address pollAddr) { - /// @notice Validate _maxValues + /// @notice Validate _maxVoteOptions /// maxVoteOptions must be less than 2 ** 50 due to circuit limitations; /// it will be packed as a 50-bit value along with other values as one /// of the inputs (aka packedVal) - if (_maxValues.maxVoteOptions >= (2 ** 50)) { - revert InvalidMaxValues(); + if (_maxVoteOptions >= (2 ** 50)) { + revert InvalidMaxVoteOptions(); } - /// @notice deploy a new AccQueue contract to store messages - AccQueue messageAq = new AccQueueQuinaryMaci(_treeDepths.messageTreeSubDepth); - /// @notice the smart contracts that a Poll would interact with - ExtContracts memory extContracts = ExtContracts({ maci: IMACI(_maci), messageAq: messageAq }); + ExtContracts memory extContracts = ExtContracts({ maci: IMACI(_maci) }); // deploy the poll - Poll poll = new Poll(_duration, _maxValues, _treeDepths, _coordinatorPubKey, extContracts); - - // Make the Poll contract own the messageAq contract, so only it can - // run enqueue/merge - messageAq.transferOwnership(address(poll)); + Poll poll = new Poll(_duration, _maxVoteOptions, _treeDepths, _messageBatchSize, _coordinatorPubKey, extContracts); // init Poll poll.init(); diff --git a/contracts/contracts/Tally.sol b/contracts/contracts/Tally.sol index a2c830d00d..97cd3a97e3 100644 --- a/contracts/contracts/Tally.sol +++ b/contracts/contracts/Tally.sol @@ -102,7 +102,7 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher, DomainObjs { /// @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(); + (uint8 intStateTreeDepth, ) = poll.treeDepths(); (uint256 numSignUps, ) = poll.numSignUpsAndMessages(); // Require that there are untallied ballots left @@ -150,7 +150,7 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher, DomainObjs { updateSbCommitment(); // get the batch size and start index - (uint8 intStateTreeDepth, , , ) = poll.treeDepths(); + (uint8 intStateTreeDepth, ) = poll.treeDepths(); uint256 tallyBatchSize = TREE_ARITY ** intStateTreeDepth; uint256 batchStartIndex = tallyBatchNum * tallyBatchSize; @@ -190,9 +190,9 @@ contract Tally is Ownable, SnarkCommon, CommonUtilities, Hasher, DomainObjs { uint256 _tallyBatchSize, uint256 _newTallyCommitment ) public view returns (bool isValid) { - (uint8 intStateTreeDepth, , , uint8 voteOptionTreeDepth) = poll.treeDepths(); + (uint8 intStateTreeDepth, uint8 voteOptionTreeDepth) = poll.treeDepths(); - (IMACI maci, ) = poll.extContracts(); + IMACI maci = poll.extContracts(); // Get the verifying key VerifyingKey memory vk = vkRegistry.getTallyVk(maci.stateTreeDepth(), intStateTreeDepth, voteOptionTreeDepth, mode); diff --git a/contracts/contracts/VkRegistry.sol b/contracts/contracts/VkRegistry.sol index f0b26d9e78..d69c17eb6c 100644 --- a/contracts/contracts/VkRegistry.sol +++ b/contracts/contracts/VkRegistry.sol @@ -49,16 +49,14 @@ contract VkRegistry is Ownable(msg.sender), DomainObjs, SnarkCommon, IVkRegistry /// @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 + uint8 _messageBatchSize ) public pure returns (uint256 sig) { - sig = (_messageBatchSize << 192) + (_stateTreeDepth << 128) + (_messageTreeDepth << 64) + _voteOptionTreeDepth; + sig = (_messageBatchSize << 128) + (_stateTreeDepth << 64) + _voteOptionTreeDepth; } /// @notice generate the signature for the tally verifying key @@ -78,7 +76,6 @@ contract VkRegistry is Ownable(msg.sender), DomainObjs, SnarkCommon, IVkRegistry /// of parameters and modes /// @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 _modes Array of QV or Non-QV modes (must have the same length as process and tally keys) @@ -87,9 +84,8 @@ contract VkRegistry is Ownable(msg.sender), DomainObjs, SnarkCommon, IVkRegistry function setVerifyingKeysBatch( uint256 _stateTreeDepth, uint256 _intStateTreeDepth, - uint256 _messageTreeDepth, uint256 _voteOptionTreeDepth, - uint256 _messageBatchSize, + uint8 _messageBatchSize, Mode[] calldata _modes, VerifyingKey[] calldata _processVks, VerifyingKey[] calldata _tallyVks @@ -104,7 +100,6 @@ contract VkRegistry is Ownable(msg.sender), DomainObjs, SnarkCommon, IVkRegistry setVerifyingKeys( _stateTreeDepth, _intStateTreeDepth, - _messageTreeDepth, _voteOptionTreeDepth, _messageBatchSize, _modes[index], @@ -122,7 +117,6 @@ contract VkRegistry is Ownable(msg.sender), DomainObjs, SnarkCommon, IVkRegistry /// 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 @@ -131,14 +125,13 @@ contract VkRegistry is Ownable(msg.sender), DomainObjs, SnarkCommon, IVkRegistry function setVerifyingKeys( uint256 _stateTreeDepth, uint256 _intStateTreeDepth, - uint256 _messageTreeDepth, uint256 _voteOptionTreeDepth, - uint256 _messageBatchSize, + uint8 _messageBatchSize, Mode _mode, VerifyingKey calldata _processVk, VerifyingKey calldata _tallyVk ) public onlyOwner { - uint256 processVkSig = genProcessVkSig(_stateTreeDepth, _messageTreeDepth, _voteOptionTreeDepth, _messageBatchSize); + uint256 processVkSig = genProcessVkSig(_stateTreeDepth, _voteOptionTreeDepth, _messageBatchSize); if (processVkSet[_mode][processVkSig]) revert ProcessVkAlreadySet(); @@ -186,19 +179,17 @@ contract VkRegistry is Ownable(msg.sender), DomainObjs, SnarkCommon, IVkRegistry /// @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, + uint8 _messageBatchSize, Mode _mode ) public view returns (bool isSet) { - uint256 sig = genProcessVkSig(_stateTreeDepth, _messageTreeDepth, _voteOptionTreeDepth, _messageBatchSize); + uint256 sig = genProcessVkSig(_stateTreeDepth, _voteOptionTreeDepth, _messageBatchSize); isSet = processVkSet[_mode][sig]; } @@ -215,12 +206,11 @@ contract VkRegistry is Ownable(msg.sender), DomainObjs, SnarkCommon, IVkRegistry /// @inheritdoc IVkRegistry function getProcessVk( uint256 _stateTreeDepth, - uint256 _messageTreeDepth, uint256 _voteOptionTreeDepth, - uint256 _messageBatchSize, + uint8 _messageBatchSize, Mode _mode ) public view returns (VerifyingKey memory vk) { - uint256 sig = genProcessVkSig(_stateTreeDepth, _messageTreeDepth, _voteOptionTreeDepth, _messageBatchSize); + uint256 sig = genProcessVkSig(_stateTreeDepth, _voteOptionTreeDepth, _messageBatchSize); vk = getProcessVkBySig(sig, _mode); } diff --git a/contracts/contracts/interfaces/IMACI.sol b/contracts/contracts/interfaces/IMACI.sol index bfd289040b..050c780503 100644 --- a/contracts/contracts/interfaces/IMACI.sol +++ b/contracts/contracts/interfaces/IMACI.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import { AccQueue } from "../trees/AccQueue.sol"; - /// @title IMACI /// @notice MACI interface interface IMACI { diff --git a/contracts/contracts/interfaces/IPoll.sol b/contracts/contracts/interfaces/IPoll.sol index 457b9a0e8c..6f4a1d7723 100644 --- a/contracts/contracts/interfaces/IPoll.sol +++ b/contracts/contracts/interfaces/IPoll.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.10; import { DomainObjs } from "../utilities/DomainObjs.sol"; import { IMACI } from "./IMACI.sol"; -import { AccQueue } from "../trees/AccQueue.sol"; /// @title IPoll /// @notice Poll interface @@ -13,6 +12,13 @@ interface IPoll { /// @return numMsgs The number of messages sent by voters function numSignUpsAndMessages() external view returns (uint256 numSignups, uint256 numMsgs); + /// @notice Get all message batch hashes + /// @return betchHashes array containing all batch hashes + function getBatchHashes() external view returns (uint256[] memory); + + /// @notice Pad last unclosed batch + function padLastBatch() external; + /// @notice Allows anyone to publish a message (an encrypted command and signature). /// This function also enqueues the message. /// @param _message The message to publish @@ -21,20 +27,11 @@ interface IPoll { /// to encrypt the message. function publishMessage(DomainObjs.Message memory _message, DomainObjs.PubKey calldata _encPubKey) external; - /// @notice The second step of merging the MACI state AccQueue. This allows the + /// @notice The second step of merging the MACI state. This allows the /// ProcessMessages circuit to access the latest state tree and ballots via /// currentSbCommitment. function mergeMaciState() external; - /// @notice The first step in merging the message AccQueue so that the - /// ProcessMessages circuit can access the message root. - /// @param _numSrQueueOps The number of subroot queue operations to perform - function mergeMessageAqSubRoots(uint256 _numSrQueueOps) external; - - /// @notice The second step in merging the message AccQueue so that the - /// ProcessMessages circuit can access the message root. - function mergeMessageAq() external; - /// @notice Returns the Poll's deploy time and duration /// @return _deployTime The deployment timestamp /// @return _duration The duration of the poll @@ -46,23 +43,16 @@ interface IPoll { /// @notice Get the depths of the merkle trees /// @return intStateTreeDepth The depth of the state tree - /// @return messageTreeSubDepth The subdepth of the message tree - /// @return messageTreeDepth The depth of the message tree /// @return voteOptionTreeDepth The subdepth of the vote option tree - function treeDepths() - external - view - returns (uint8 intStateTreeDepth, uint8 messageTreeSubDepth, uint8 messageTreeDepth, uint8 voteOptionTreeDepth); + function treeDepths() external view returns (uint8 intStateTreeDepth, uint8 voteOptionTreeDepth); - /// @notice Get the max values for the poll - /// @return maxMessages The maximum number of messages + /// @notice Get the max vote options for the poll /// @return maxVoteOptions The maximum number of vote options - function maxValues() external view returns (uint256 maxMessages, uint256 maxVoteOptions); + function maxVoteOptions() external view returns (uint256); - /// @notice Get the external contracts - /// @return maci The IMACI contract - /// @return messageAq The AccQueue contract - function extContracts() external view returns (IMACI maci, AccQueue messageAq); + /// @notice Get message batch size for the poll + /// @return The message batch size + function messageBatchSize() external view returns (uint8); /// @notice Get the hash of coordinator's public key /// @return _coordinatorPubKeyHash the hash of coordinator's public key @@ -81,4 +71,8 @@ interface IPoll { /// @notice Get the dynamic depth of the state tree at the time of poll /// finalization (based on the number of leaves inserted) function actualStateTreeDepth() external view returns (uint8); + + /// @notice Get the external contracts + /// @return maci The IMACI contract + function extContracts() external view returns (IMACI maci); } diff --git a/contracts/contracts/interfaces/IPollFactory.sol b/contracts/contracts/interfaces/IPollFactory.sol index 0587f88039..c8c38df650 100644 --- a/contracts/contracts/interfaces/IPollFactory.sol +++ b/contracts/contracts/interfaces/IPollFactory.sol @@ -7,17 +7,19 @@ import { DomainObjs } from "../utilities/DomainObjs.sol"; /// @title IPollFactory /// @notice PollFactory interface interface IPollFactory { - /// @notice Deploy a new Poll contract and AccQueue contract for messages. + /// @notice Deploy a new Poll contract /// @param _duration The duration of the poll - /// @param _maxValues The max values for the poll + /// @param _maxVoteOptions The max vote options for the poll /// @param _treeDepths The depths of the merkle trees + /// @param _messageBatchSize The size of message batch /// @param _coordinatorPubKey The coordinator's public key /// @param _maci The MACI contract interface reference /// @return The deployed Poll contract function deploy( uint256 _duration, - Params.MaxValues memory _maxValues, + uint256 _maxVoteOptions, Params.TreeDepths memory _treeDepths, + uint8 _messageBatchSize, DomainObjs.PubKey memory _coordinatorPubKey, address _maci ) external returns (address); diff --git a/contracts/contracts/interfaces/IVkRegistry.sol b/contracts/contracts/interfaces/IVkRegistry.sol index 3c2d22ec00..5d0437d972 100644 --- a/contracts/contracts/interfaces/IVkRegistry.sol +++ b/contracts/contracts/interfaces/IVkRegistry.sol @@ -22,16 +22,14 @@ interface IVkRegistry { /// @notice Get 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 /// @param _mode QV or Non-QV /// @return The verifying key function getProcessVk( uint256 _stateTreeDepth, - uint256 _messageTreeDepth, uint256 _voteOptionTreeDepth, - uint256 _messageBatchSize, + uint8 _messageBatchSize, DomainObjs.Mode _mode ) external view returns (SnarkCommon.VerifyingKey memory); } diff --git a/contracts/contracts/trees/AccQueue.sol b/contracts/contracts/trees/AccQueue.sol deleted file mode 100644 index ef6de8af03..0000000000 --- a/contracts/contracts/trees/AccQueue.sol +++ /dev/null @@ -1,467 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { Hasher } from "../crypto/Hasher.sol"; - -/// @title AccQueue -/// @notice This contract defines a Merkle tree where each leaf insertion only updates a -/// subtree. To obtain the main tree root, the contract owner must merge the -/// subtrees together. Merging subtrees requires at least 2 operations: -/// mergeSubRoots(), and merge(). To get around the gas limit, -/// the mergeSubRoots() can be performed in multiple transactions. -abstract contract AccQueue is Ownable(msg.sender), Hasher { - // The maximum tree depth - uint256 public constant MAX_DEPTH = 32; - - /// @notice A Queue is a 2D array of Merkle roots and indices which represents nodes - /// in a Merkle tree while it is progressively updated. - struct Queue { - /// @notice IMPORTANT: the following declares an array of b elements of type T: T[b] - /// And the following declares an array of b elements of type T[a]: T[a][b] - /// As such, the following declares an array of MAX_DEPTH+1 arrays of - /// uint256[4] arrays, **not the other way round**: - uint256[4][MAX_DEPTH + 1] levels; - uint256[MAX_DEPTH + 1] indices; - } - - // The depth of each subtree - uint256 internal immutable subDepth; - - // The number of elements per hash operation. Should be either 2 (for - // binary trees) or 5 (quinary trees). The limit is 5 because that is the - // maximum supported number of inputs for the EVM implementation of the - // Poseidon hash function - uint256 internal immutable hashLength; - - // hashLength ** subDepth - uint256 internal immutable subTreeCapacity; - - // True hashLength == 2, false if hashLength == 5 - bool internal isBinary; - - // The index of the current subtree. e.g. the first subtree has index 0, the - // second has 1, and so on - uint256 internal currentSubtreeIndex; - - // Tracks the current subtree. - Queue internal leafQueue; - - // Tracks the smallest tree of subroots - Queue internal subRootQueue; - - // Subtree roots - mapping(uint256 => uint256) internal subRoots; - - // Merged roots - uint256[MAX_DEPTH + 1] internal mainRoots; - - // Whether the subtrees have been merged - bool public subTreesMerged; - - // Whether entire merkle tree has been merged - bool public treeMerged; - - // The root of the shortest possible tree which fits all current subtree - // roots - uint256 internal smallSRTroot; - - // Tracks the next subroot to queue - uint256 internal nextSubRootIndex; - - // The number of leaves inserted across all subtrees so far - uint256 public numLeaves; - - /// @notice custom errors - error SubDepthCannotBeZero(); - error SubdepthTooLarge(uint256 _subDepth, uint256 max); - error InvalidHashLength(); - error DepthCannotBeZero(); - error SubTreesAlreadyMerged(); - error NothingToMerge(); - error SubTreesNotMerged(); - error DepthTooLarge(uint256 _depth, uint256 max); - error DepthTooSmall(uint256 _depth, uint256 min); - error InvalidIndex(uint256 _index); - error InvalidLevel(); - - /// @notice Create a new AccQueue - /// @param _subDepth The depth of each subtree. - /// @param _hashLength The number of leaves per node (2 or 5). - constructor(uint256 _subDepth, uint256 _hashLength) payable { - /// validation - if (_subDepth == 0) revert SubDepthCannotBeZero(); - if (_subDepth > MAX_DEPTH) revert SubdepthTooLarge(_subDepth, MAX_DEPTH); - if (_hashLength != 2 && _hashLength != 5) revert InvalidHashLength(); - - isBinary = _hashLength == 2; - subDepth = _subDepth; - hashLength = _hashLength; - subTreeCapacity = _hashLength ** _subDepth; - } - - /// @notice Hash the contents of the specified level and the specified leaf. - /// This is a virtual function as the hash function which the overriding - /// contract uses will be either hashLeftRight or hash5, which require - /// different input array lengths. - /// @param _level The level to hash. - /// @param _leaf The leaf include with the level. - /// @return _hash The hash of the level and leaf. - // solhint-disable-next-line no-empty-blocks - function hashLevel(uint256 _level, uint256 _leaf) internal virtual returns (uint256 _hash) {} - - /// @notice Hash the contents of the specified level and the specified leaf. - /// This is a virtual function as the hash function which the overriding - /// contract uses will be either hashLeftRight or hash5, which require - /// different input array lengths. - /// @param _level The level to hash. - /// @param _leaf The leaf include with the level. - /// @return _hash The hash of the level and leaf. - // solhint-disable-next-line no-empty-blocks - function hashLevelLeaf(uint256 _level, uint256 _leaf) public view virtual returns (uint256 _hash) {} - - /// @notice Returns the zero leaf at a specified level. - /// This is a virtual function as the hash function which the overriding - /// contract uses will be either hashLeftRight or hash5, which will produce - /// different zero values (e.g. hashLeftRight(0, 0) vs - /// hash5([0, 0, 0, 0, 0]). Moreover, the zero value may be a - /// nothing-up-my-sleeve value. - /// @param _level The level at which to return the zero leaf. - /// @return zero The zero leaf at the specified level. - // solhint-disable-next-line no-empty-blocks - function getZero(uint256 _level) internal virtual returns (uint256 zero) {} - - /// @notice Add a leaf to the queue for the current subtree. - /// @param _leaf The leaf to add. - /// @return leafIndex The index of the leaf in the queue. - function enqueue(uint256 _leaf) public onlyOwner returns (uint256 leafIndex) { - leafIndex = numLeaves; - // Recursively queue the leaf - _enqueue(_leaf, 0); - - // Update the leaf counter - numLeaves = leafIndex + 1; - - // Now that a new leaf has been added, mainRoots and smallSRTroot are - // obsolete - delete mainRoots; - delete smallSRTroot; - subTreesMerged = false; - - // If a subtree is full - if (numLeaves % subTreeCapacity == 0) { - // Store the subroot - subRoots[currentSubtreeIndex] = leafQueue.levels[subDepth][0]; - - // Increment the index - currentSubtreeIndex++; - - // Delete ancillary data - delete leafQueue.levels[subDepth][0]; - delete leafQueue.indices; - } - } - - /// @notice Updates the queue at a given level and hashes any subroots - /// that need to be hashed. - /// @param _leaf The leaf to add. - /// @param _level The level at which to queue the leaf. - function _enqueue(uint256 _leaf, uint256 _level) internal { - if (_level > subDepth) { - revert InvalidLevel(); - } - - while (true) { - uint256 n = leafQueue.indices[_level]; - - if (n != hashLength - 1) { - // Just store the leaf - leafQueue.levels[_level][n] = _leaf; - - if (_level != subDepth) { - // Update the index - leafQueue.indices[_level]++; - } - - return; - } - - // Hash the leaves to next level - _leaf = hashLevel(_level, _leaf); - - // Reset the index for this level - delete leafQueue.indices[_level]; - - // Queue the hash of the leaves into to the next level - _level++; - } - } - - /// @notice Fill any empty leaves of the current subtree with zeros and store the - /// resulting subroot. - function fill() public onlyOwner { - if (numLeaves % subTreeCapacity == 0) { - // If the subtree is completely empty, then the subroot is a - // precalculated zero value - subRoots[currentSubtreeIndex] = getZero(subDepth); - } else { - // Otherwise, fill the rest of the subtree with zeros - _fill(0); - - // Store the subroot - subRoots[currentSubtreeIndex] = leafQueue.levels[subDepth][0]; - - // Reset the subtree data - delete leafQueue.levels; - - // Reset the merged roots - delete mainRoots; - } - - // Increment the subtree index - uint256 curr = currentSubtreeIndex + 1; - currentSubtreeIndex = curr; - - // Update the number of leaves - numLeaves = curr * subTreeCapacity; - - // Reset the subroot tree root now that it is obsolete - delete smallSRTroot; - - subTreesMerged = false; - } - - /// @notice A function that queues zeros to the specified level, hashes, - /// the level, and enqueues the hash to the next level. - /// @param _level The level at which to queue zeros. - // solhint-disable-next-line no-empty-blocks - function _fill(uint256 _level) internal virtual {} - - /// Insert a subtree. Used for batch enqueues. - function insertSubTree(uint256 _subRoot) public onlyOwner { - subRoots[currentSubtreeIndex] = _subRoot; - - // Increment the subtree index - currentSubtreeIndex++; - - // Update the number of leaves - numLeaves += subTreeCapacity; - - // Reset the subroot tree root now that it is obsolete - delete smallSRTroot; - - subTreesMerged = false; - } - - /// @notice Calculate the lowest possible height of a tree with - /// all the subroots merged together. - /// @return depth The lowest possible height of a tree with all the - function calcMinHeight() public view returns (uint256 depth) { - depth = 1; - while (true) { - if (hashLength ** depth >= currentSubtreeIndex) { - break; - } - depth++; - } - } - - /// @notice Merge all subtrees to form the shortest possible tree. - /// This function can be called either once to merge all subtrees in a - /// single transaction, or multiple times to do the same in multiple - /// transactions. - /// @param _numSrQueueOps The number of times this function will call - /// queueSubRoot(), up to the maximum number of times - /// necessary. If it is set to 0, it will call - /// queueSubRoot() as many times as is necessary. Set - /// this to a low number and call this function - /// multiple times if there are many subroots to - /// merge, or a single transaction could run out of - /// gas. - function mergeSubRoots(uint256 _numSrQueueOps) public onlyOwner { - // This function can only be called once unless a new subtree is created - if (subTreesMerged) revert SubTreesAlreadyMerged(); - - // There must be subtrees to merge - if (numLeaves == 0) revert NothingToMerge(); - - // Fill any empty leaves in the current subtree with zeros only if the - // current subtree is not full - if (numLeaves % subTreeCapacity != 0) { - fill(); - } - - // If there is only 1 subtree, use its root - if (currentSubtreeIndex == 1) { - smallSRTroot = getSubRoot(0); - subTreesMerged = true; - return; - } - - uint256 depth = calcMinHeight(); - - uint256 queueOpsPerformed = 0; - for (uint256 i = nextSubRootIndex; i < currentSubtreeIndex; i++) { - if (_numSrQueueOps != 0 && queueOpsPerformed == _numSrQueueOps) { - // If the limit is not 0, stop if the limit has been reached - return; - } - - // Queue the next subroot - queueSubRoot(getSubRoot(nextSubRootIndex), 0, depth); - - // Increment the next subroot counter - nextSubRootIndex++; - - // Increment the ops counter - queueOpsPerformed++; - } - - // The height of the tree of subroots - uint256 m = hashLength ** depth; - - // Queue zeroes to fill out the SRT - if (nextSubRootIndex == currentSubtreeIndex) { - uint256 z = getZero(subDepth); - for (uint256 i = currentSubtreeIndex; i < m; i++) { - queueSubRoot(z, 0, depth); - } - } - - // Store the smallest main root - smallSRTroot = subRootQueue.levels[depth][0]; - subTreesMerged = true; - } - - /// @notice Queues a subroot into the subroot tree. - /// @param _leaf The value to queue. - /// @param _level The level at which to queue _leaf. - /// @param _maxDepth The depth of the tree. - function queueSubRoot(uint256 _leaf, uint256 _level, uint256 _maxDepth) internal { - if (_level > _maxDepth) { - return; - } - - uint256 n = subRootQueue.indices[_level]; - - if (n != hashLength - 1) { - // Just store the leaf - subRootQueue.levels[_level][n] = _leaf; - subRootQueue.indices[_level]++; - } else { - // Hash the elements in this level and queue it in the next level - uint256 hashed; - if (isBinary) { - uint256[2] memory inputs; - inputs[0] = subRootQueue.levels[_level][0]; - inputs[1] = _leaf; - hashed = hash2(inputs); - } else { - uint256[5] memory inputs; - for (uint8 i = 0; i < n; i++) { - inputs[i] = subRootQueue.levels[_level][i]; - } - inputs[n] = _leaf; - hashed = hash5(inputs); - } - - // TODO: change recursion to a while loop - // Recurse - delete subRootQueue.indices[_level]; - queueSubRoot(hashed, _level + 1, _maxDepth); - } - } - - /// @notice Merge all subtrees to form a main tree with a desired depth. - /// @param _depth The depth of the main tree. It must fit all the leaves or - /// this function will revert. - /// @return root The root of the main tree. - function merge(uint256 _depth) public onlyOwner returns (uint256 root) { - // The tree depth must be more than 0 - if (_depth == 0) revert DepthCannotBeZero(); - - // Ensure that the subtrees have been merged - if (!subTreesMerged) revert SubTreesNotMerged(); - - // Check the depth - if (_depth > MAX_DEPTH) revert DepthTooLarge(_depth, MAX_DEPTH); - - // Calculate the SRT depth - uint256 srtDepth = subDepth; - while (true) { - if (hashLength ** srtDepth >= numLeaves) { - break; - } - srtDepth++; - } - - if (_depth < srtDepth) revert DepthTooSmall(_depth, srtDepth); - - // If the depth is the same as the SRT depth, just use the SRT root - if (_depth == srtDepth) { - mainRoots[_depth] = smallSRTroot; - treeMerged = true; - return smallSRTroot; - } else { - root = smallSRTroot; - - // Calculate the main root - - for (uint256 i = srtDepth; i < _depth; i++) { - uint256 z = getZero(i); - - if (isBinary) { - uint256[2] memory inputs; - inputs[0] = root; - inputs[1] = z; - root = hash2(inputs); - } else { - uint256[5] memory inputs; - inputs[0] = root; - inputs[1] = z; - inputs[2] = z; - inputs[3] = z; - inputs[4] = z; - root = hash5(inputs); - } - } - - mainRoots[_depth] = root; - treeMerged = true; - } - } - - /// @notice Returns the subroot at the specified index. Reverts if the index refers - /// to a subtree which has not been filled yet. - /// @param _index The subroot index. - /// @return subRoot The subroot at the specified index. - function getSubRoot(uint256 _index) public view returns (uint256 subRoot) { - if (currentSubtreeIndex <= _index) revert InvalidIndex(_index); - subRoot = subRoots[_index]; - } - - /// @notice Returns the subroot tree (SRT) root. Its value must first be computed - /// using mergeSubRoots. - /// @return smallSubTreeRoot The SRT root. - function getSmallSRTroot() public view returns (uint256 smallSubTreeRoot) { - if (!subTreesMerged) revert SubTreesNotMerged(); - smallSubTreeRoot = smallSRTroot; - } - - /// @notice Return the merged Merkle root of all the leaves at a desired depth. - /// @dev merge() or merged(_depth) must be called first. - /// @param _depth The depth of the main tree. It must first be computed - /// using mergeSubRoots() and merge(). - /// @return mainRoot The root of the main tree. - function getMainRoot(uint256 _depth) public view returns (uint256 mainRoot) { - if (hashLength ** _depth < numLeaves) revert DepthTooSmall(_depth, numLeaves); - - mainRoot = mainRoots[_depth]; - } - - /// @notice Get the next subroot index and the current subtree index. - function getSrIndices() public view returns (uint256 next, uint256 current) { - next = nextSubRootIndex; - current = currentSubtreeIndex; - } -} diff --git a/contracts/contracts/trees/AccQueueBinary.sol b/contracts/contracts/trees/AccQueueBinary.sol deleted file mode 100644 index c652b18bd7..0000000000 --- a/contracts/contracts/trees/AccQueueBinary.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { AccQueue } from "./AccQueue.sol"; - -/// @title AccQueueBinary -/// @notice This contract defines a Merkle tree where each leaf insertion only updates a -/// subtree. To obtain the main tree root, the contract owner must merge the -/// subtrees together. Merging subtrees requires at least 2 operations: -/// mergeSubRoots(), and merge(). To get around the gas limit, -/// the mergeSubRoots() can be performed in multiple transactions. -/// @dev This contract is for a binary tree (2 leaves per node) -abstract contract AccQueueBinary is AccQueue { - /// @notice Create a new AccQueueBinary - constructor(uint256 _subDepth) AccQueue(_subDepth, 2) {} - - /// @notice Hash the contents of the specified level and the specified leaf. - /// @param _level The level to hash. - /// @param _leaf The leaf include with the level. - /// @return hashed The hash of the level and leaf. - function hashLevel(uint256 _level, uint256 _leaf) internal override returns (uint256 hashed) { - hashed = hashLeftRight(leafQueue.levels[_level][0], _leaf); - - // Free up storage slots to refund gas. - delete leafQueue.levels[_level][0]; - } - - /// @notice Hash the contents of the specified level and the specified leaf. - function hashLevelLeaf(uint256 _level, uint256 _leaf) public view override returns (uint256 hashed) { - hashed = hashLeftRight(leafQueue.levels[_level][0], _leaf); - } - - /// @notice An internal function which fills a subtree. - /// @param _level The level at which to fill the subtree. - function _fill(uint256 _level) internal override { - while (_level < subDepth) { - uint256 n = leafQueue.indices[_level]; - - if (n != 0) { - // Fill the subtree level with zeros and hash the level - uint256 hashed; - - uint256[2] memory inputs; - uint256 z = getZero(_level); - inputs[0] = leafQueue.levels[_level][0]; - inputs[1] = z; - hashed = hash2(inputs); - - // Update the subtree from the next level onwards with the new leaf - _enqueue(hashed, _level + 1); - } - - // Reset the current level - delete leafQueue.indices[_level]; - - _level++; - } - } -} diff --git a/contracts/contracts/trees/AccQueueBinary0.sol b/contracts/contracts/trees/AccQueueBinary0.sol deleted file mode 100644 index 33c5eeba14..0000000000 --- a/contracts/contracts/trees/AccQueueBinary0.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { MerkleZeros as MerkleBinary0 } from "./zeros/MerkleBinary0.sol"; -import { AccQueueBinary } from "./AccQueueBinary.sol"; - -/// @title AccQueueBinary0 -/// @notice This contract extends AccQueueBinary and MerkleBinary0 -/// @dev This contract is used for creating a -/// Merkle tree with binary (2 leaves per node) structure -contract AccQueueBinary0 is AccQueueBinary, MerkleBinary0 { - /// @notice Constructor for creating AccQueueBinary0 contract - /// @param _subDepth The depth of each subtree - constructor(uint256 _subDepth) AccQueueBinary(_subDepth) {} - - /// @notice Returns the zero leaf at a specified level - /// @param _level The level at which to return the zero leaf - /// @return zero The zero leaf at the specified level - function getZero(uint256 _level) internal view override returns (uint256 zero) { - zero = zeros[_level]; - } -} diff --git a/contracts/contracts/trees/AccQueueBinaryMaci.sol b/contracts/contracts/trees/AccQueueBinaryMaci.sol deleted file mode 100644 index 0291e9d322..0000000000 --- a/contracts/contracts/trees/AccQueueBinaryMaci.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { MerkleZeros as MerkleBinaryMaci } from "./zeros/MerkleBinaryMaci.sol"; -import { AccQueueBinary } from "./AccQueueBinary.sol"; - -/// @title AccQueueBinaryMaci -/// @notice This contract extends AccQueueBinary and MerkleBinaryMaci -/// @dev This contract is used for creating a -/// Merkle tree with binary (2 leaves per node) structure -contract AccQueueBinaryMaci is AccQueueBinary, MerkleBinaryMaci { - /// @notice Constructor for creating AccQueueBinaryMaci contract - /// @param _subDepth The depth of each subtree - constructor(uint256 _subDepth) AccQueueBinary(_subDepth) {} - - /// @notice Returns the zero leaf at a specified level - /// @param _level The level at which to return the zero leaf - function getZero(uint256 _level) internal view override returns (uint256 zero) { - zero = zeros[_level]; - } -} diff --git a/contracts/contracts/trees/AccQueueQuinary.sol b/contracts/contracts/trees/AccQueueQuinary.sol deleted file mode 100644 index c31d54dd2c..0000000000 --- a/contracts/contracts/trees/AccQueueQuinary.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { AccQueue } from "./AccQueue.sol"; - -/// @title AccQueueQuinary -/// @notice This contract defines a Merkle tree where each leaf insertion only updates a -/// subtree. To obtain the main tree root, the contract owner must merge the -/// subtrees together. Merging subtrees requires at least 2 operations: -/// mergeSubRoots(), and merge(). To get around the gas limit, -/// the mergeSubRoots() can be performed in multiple transactions. -/// @dev This contract is for a quinary tree (5 leaves per node) -abstract contract AccQueueQuinary is AccQueue { - /// @notice Create a new AccQueueQuinary instance - constructor(uint256 _subDepth) AccQueue(_subDepth, 5) {} - - /// @notice Hash the contents of the specified level and the specified leaf. - /// @dev it also frees up storage slots to refund gas. - /// @param _level The level to hash. - /// @param _leaf The leaf include with the level. - /// @return hashed The hash of the level and leaf. - function hashLevel(uint256 _level, uint256 _leaf) internal override returns (uint256 hashed) { - uint256[5] memory inputs; - inputs[0] = leafQueue.levels[_level][0]; - inputs[1] = leafQueue.levels[_level][1]; - inputs[2] = leafQueue.levels[_level][2]; - inputs[3] = leafQueue.levels[_level][3]; - inputs[4] = _leaf; - hashed = hash5(inputs); - - // Free up storage slots to refund gas. Note that using a loop here - // would result in lower gas savings. - delete leafQueue.levels[_level]; - } - - /// @notice Hash the contents of the specified level and the specified leaf. - /// @param _level The level to hash. - /// @param _leaf The leaf include with the level. - /// @return hashed The hash of the level and leaf. - function hashLevelLeaf(uint256 _level, uint256 _leaf) public view override returns (uint256 hashed) { - uint256[5] memory inputs; - inputs[0] = leafQueue.levels[_level][0]; - inputs[1] = leafQueue.levels[_level][1]; - inputs[2] = leafQueue.levels[_level][2]; - inputs[3] = leafQueue.levels[_level][3]; - inputs[4] = _leaf; - hashed = hash5(inputs); - } - - /// @notice An internal function which fills a subtree - /// @param _level The level at which to fill the subtree - function _fill(uint256 _level) internal override { - while (_level < subDepth) { - uint256 n = leafQueue.indices[_level]; - - if (n != 0) { - // Fill the subtree level with zeros and hash the level - uint256 hashed; - - uint256[5] memory inputs; - uint256 z = getZero(_level); - uint8 i = 0; - for (; i < n; i++) { - inputs[i] = leafQueue.levels[_level][i]; - } - - for (; i < hashLength; i++) { - inputs[i] = z; - } - hashed = hash5(inputs); - - // Update the subtree from the next level onwards with the new leaf - _enqueue(hashed, _level + 1); - } - - // Reset the current level - delete leafQueue.indices[_level]; - - _level++; - } - } -} diff --git a/contracts/contracts/trees/AccQueueQuinary0.sol b/contracts/contracts/trees/AccQueueQuinary0.sol deleted file mode 100644 index 9ac606a5c8..0000000000 --- a/contracts/contracts/trees/AccQueueQuinary0.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { MerkleZeros as MerkleQuinary0 } from "./zeros/MerkleQuinary0.sol"; -import { AccQueueQuinary } from "./AccQueueQuinary.sol"; - -/// @title AccQueueQuinary0 -/// @notice This contract extends AccQueueQuinary and MerkleQuinary0 -/// @dev This contract is used for creating a -/// Merkle tree with quinary (5 leaves per node) structure -contract AccQueueQuinary0 is AccQueueQuinary, MerkleQuinary0 { - /// @notice Constructor for creating AccQueueQuinary0 contract - /// @param _subDepth The depth of each subtree - constructor(uint256 _subDepth) AccQueueQuinary(_subDepth) {} - - /// @notice Returns the zero leaf at a specified level - /// @param _level The level at which to return the zero leaf - /// @return zero The zero leaf at the specified level - function getZero(uint256 _level) internal view override returns (uint256 zero) { - zero = zeros[_level]; - } -} diff --git a/contracts/contracts/trees/AccQueueQuinaryBlankSl.sol b/contracts/contracts/trees/AccQueueQuinaryBlankSl.sol deleted file mode 100644 index c629677cd8..0000000000 --- a/contracts/contracts/trees/AccQueueQuinaryBlankSl.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { MerkleZeros as MerkleQuinaryBlankSl } from "./zeros/MerkleQuinaryBlankSl.sol"; -import { AccQueueQuinary } from "./AccQueueQuinary.sol"; - -/// @title AccQueueQuinaryBlankSl -/// @notice This contract extends AccQueueQuinary and MerkleQuinaryBlankSl -/// @dev This contract is used for creating a -/// Merkle tree with quinary (5 leaves per node) structure -contract AccQueueQuinaryBlankSl is AccQueueQuinary, MerkleQuinaryBlankSl { - /// @notice Constructor for creating AccQueueQuinaryBlankSl contract - /// @param _subDepth The depth of each subtree - constructor(uint256 _subDepth) AccQueueQuinary(_subDepth) {} - - /// @notice Returns the zero leaf at a specified level - /// @param _level The level at which to return the zero leaf - /// @return zero The zero leaf at the specified level - function getZero(uint256 _level) internal view override returns (uint256 zero) { - zero = zeros[_level]; - } -} diff --git a/contracts/contracts/trees/AccQueueQuinaryMaci.sol b/contracts/contracts/trees/AccQueueQuinaryMaci.sol deleted file mode 100644 index cfb5533311..0000000000 --- a/contracts/contracts/trees/AccQueueQuinaryMaci.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { MerkleZeros as MerkleQuinaryMaci } from "./zeros/MerkleQuinaryMaci.sol"; -import { AccQueueQuinary } from "./AccQueueQuinary.sol"; - -/// @title AccQueueQuinaryMaci -/// @notice This contract extends AccQueueQuinary and MerkleQuinaryMaci -/// @dev This contract is used for creating a -/// Merkle tree with quinary (5 leaves per node) structure -contract AccQueueQuinaryMaci is AccQueueQuinary, MerkleQuinaryMaci { - /// @notice Constructor for creating AccQueueQuinaryMaci contract - /// @param _subDepth The depth of each subtree - constructor(uint256 _subDepth) AccQueueQuinary(_subDepth) {} - - /// @notice Returns the zero leaf at a specified level - /// @param _level The level at which to return the zero leaf - /// @return zero The zero leaf at the specified level - function getZero(uint256 _level) internal view override returns (uint256 zero) { - zero = zeros[_level]; - } -} diff --git a/contracts/contracts/utilities/Params.sol b/contracts/contracts/utilities/Params.sol index 06fb4bdd33..6a1d5fb603 100644 --- a/contracts/contracts/utilities/Params.sol +++ b/contracts/contracts/utilities/Params.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.20; import { IMACI } from "../interfaces/IMACI.sol"; -import { AccQueue } from "../trees/AccQueue.sol"; /// @title Params /// @notice This contracts contains a number of structures @@ -13,22 +12,13 @@ contract Params { /// @notice A struct holding the depths of the merkle trees struct TreeDepths { uint8 intStateTreeDepth; - uint8 messageTreeSubDepth; - uint8 messageTreeDepth; uint8 voteOptionTreeDepth; } - /// @notice A struct holding the max values for the poll - struct MaxValues { - uint256 maxMessages; - uint256 maxVoteOptions; - } - /// @notice A struct holding the external contracts /// that are to be passed to a Poll contract on /// deployment struct ExtContracts { IMACI maci; - AccQueue messageAq; } } diff --git a/contracts/deploy-config-example.json b/contracts/deploy-config-example.json index ca7d3090e6..2a319e9bc9 100644 --- a/contracts/deploy-config-example.json +++ b/contracts/deploy-config-example.json @@ -37,20 +37,19 @@ "VkRegistry": { "stateTreeDepth": 10, "intStateTreeDepth": 1, - "messageTreeDepth": 2, "voteOptionTreeDepth": 2, - "messageBatchDepth": 1, + "messageBatchSize": 20, "zkeys": { "qv": { - "processMessagesZkey": "../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test.0.zkey", + "processMessagesZkey": "../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test.0.zkey", "tallyVotesZkey": "../cli/zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test.0.zkey", - "processWasm": "../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test_js/ProcessMessages_10-2-1-2_test.wasm", + "processWasm": "../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test_js/ProcessMessages_10-20-2_test.wasm", "tallyWasm": "../cli/zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test_js/TallyVotes_10-1-2_test.wasm" }, "nonQv": { - "processMessagesZkey": "../cli/zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test.0.zkey", + "processMessagesZkey": "../cli/zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test.0.zkey", "tallyVotesZkey": "../cli/zkeys/TallyVotesNonQv_10-1-2_test/TallyVotesNonQv_10-1-2_test.0.zkey", - "processWasm": "../cli/zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test_js/ProcessMessagesNonQv_10-2-1-2_test.wasm", + "processWasm": "../cli/zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test_js/ProcessMessagesNonQv_10-20-2_test.wasm", "tallyWasm": "../cli/zkeys/TallyVotesNonQv_10-1-2_test/TallyVotesNonQv_10-1-2_test_js/TallyVotesNonQv_10-1-2_test.wasm" } } @@ -93,20 +92,19 @@ "VkRegistry": { "stateTreeDepth": 10, "intStateTreeDepth": 1, - "messageTreeDepth": 2, "voteOptionTreeDepth": 2, - "messageBatchDepth": 1, + "messageBatchSize": 20, "zkeys": { "qv": { - "processMessagesZkey": "../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test.0.zkey", + "processMessagesZkey": "../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test.0.zkey", "tallyVotesZkey": "../cli/zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test.0.zkey", - "processWasm": "../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test_js/ProcessMessages_10-2-1-2_test.wasm", + "processWasm": "../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test_js/ProcessMessages_10-20-2_test.wasm", "tallyWasm": "../cli/zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test_js/TallyVotes_10-1-2_test.wasm" }, "nonQv": { - "processMessagesZkey": "../cli/zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test.0.zkey", + "processMessagesZkey": "../cli/zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test.0.zkey", "tallyVotesZkey": "../cli/zkeys/TallyVotesNonQv_10-1-2_test/TallyVotesNonQv_10-1-2_test.0.zkey", - "processWasm": "../cli/zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test_js/ProcessMessagesNonQv_10-2-1-2_test.wasm", + "processWasm": "../cli/zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test_js/ProcessMessagesNonQv_10-20-2_test.wasm", "tallyWasm": "../cli/zkeys/TallyVotesNonQv_10-1-2_test/TallyVotesNonQv_10-1-2_test_js/TallyVotesNonQv_10-1-2_test.wasm" } } @@ -155,9 +153,8 @@ "VkRegistry": { "stateTreeDepth": 6, "intStateTreeDepth": 2, - "messageTreeDepth": 9, "voteOptionTreeDepth": 3, - "messageBatchDepth": 2, + "messageBatchSize": 20, "zkeys": { "qv": { "processMessagesZkey": "../cli/zkeys/ProcessMessages_6-9-2-3/processMessages_6-9-2-3.zkey", @@ -217,20 +214,19 @@ "VkRegistry": { "stateTreeDepth": 10, "intStateTreeDepth": 1, - "messageTreeDepth": 2, "voteOptionTreeDepth": 2, - "messageBatchDepth": 1, + "messageBatchSize": 20, "zkeys": { "qv": { - "processMessagesZkey": "../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test.0.zkey", + "processMessagesZkey": "../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test.0.zkey", "tallyVotesZkey": "../cli/zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test.0.zkey", - "processWasm": "../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test_js/ProcessMessages_10-2-1-2_test.wasm", + "processWasm": "../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test_js/ProcessMessages_10-20-2_test.wasm", "tallyWasm": "../cli/zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test_js/TallyVotes_10-1-2_test.wasm" }, "nonQv": { - "processMessagesZkey": "../cli/zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test.0.zkey", + "processMessagesZkey": "../cli/zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test.0.zkey", "tallyVotesZkey": "../cli/zkeys/TallyVotesNonQv_10-1-2_test/TallyVotesNonQv_10-1-2_test.0.zkey", - "processWasm": "../cli/zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test_js/ProcessMessagesNonQv_10-2-1-2_test.wasm", + "processWasm": "../cli/zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test_js/ProcessMessagesNonQv_10-20-2_test.wasm", "tallyWasm": "../cli/zkeys/TallyVotesNonQv_10-1-2_test/TallyVotesNonQv_10-1-2_test_js/TallyVotesNonQv_10-1-2_test.wasm" } } diff --git a/contracts/package.json b/contracts/package.json index 413d209846..3389c35f5f 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -42,8 +42,6 @@ "test:utilities": "pnpm run test ./tests/Utilities.test.ts", "test:signupGatekeeper": "pnpm run test ./tests/SignUpGatekeeper.test.ts", "test:verifier": "pnpm run test ./tests/Verifier.test.ts", - "test:accQueue": "pnpm run test ./tests/AccQueue.test.ts", - "test:accQueueBenchmark": "pnpm run test ./tests/AccQueueBenchmark.test.ts", "test:hasherBenchmarks": "pnpm run test ./tests/HasherBenchmarks.test.ts", "test:vkRegistry": "pnpm run test ./tests/VkRegistry.test.ts", "test:pollFactory": "pnpm run test ./tests/PollFactory.test.ts", diff --git a/contracts/tasks/deploy/maci/09-vkRegistry.ts b/contracts/tasks/deploy/maci/09-vkRegistry.ts index 4a04ddbab6..50bfc8d292 100644 --- a/contracts/tasks/deploy/maci/09-vkRegistry.ts +++ b/contracts/tasks/deploy/maci/09-vkRegistry.ts @@ -28,7 +28,6 @@ deployment.deployTask("full:deploy-vk-registry", "Deploy Vk Registry and set key const stateTreeDepth = deployment.getDeployConfigField(EContracts.VkRegistry, "stateTreeDepth"); const intStateTreeDepth = deployment.getDeployConfigField(EContracts.VkRegistry, "intStateTreeDepth"); - const messageTreeDepth = deployment.getDeployConfigField(EContracts.VkRegistry, "messageTreeDepth"); const messageBatchDepth = deployment.getDeployConfigField(EContracts.VkRegistry, "messageBatchDepth"); const voteOptionTreeDepth = deployment.getDeployConfigField(EContracts.VkRegistry, "voteOptionTreeDepth"); const processMessagesZkeyPathQv = deployment.getDeployConfigField( @@ -91,7 +90,6 @@ deployment.deployTask("full:deploy-vk-registry", "Deploy Vk Registry and set key .setVerifyingKeysBatch( stateTreeDepth, intStateTreeDepth, - messageTreeDepth, voteOptionTreeDepth, 5 ** messageBatchDepth, modes, diff --git a/contracts/tasks/deploy/poll/01-poll.ts b/contracts/tasks/deploy/poll/01-poll.ts index f830b95388..3bec805ecb 100644 --- a/contracts/tasks/deploy/poll/01-poll.ts +++ b/contracts/tasks/deploy/poll/01-poll.ts @@ -40,8 +40,7 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").then((task) => const coordinatorPubkey = deployment.getDeployConfigField(EContracts.Poll, "coordinatorPubkey"); const pollDuration = deployment.getDeployConfigField(EContracts.Poll, "pollDuration"); const intStateTreeDepth = deployment.getDeployConfigField(EContracts.VkRegistry, "intStateTreeDepth"); - const messageTreeSubDepth = deployment.getDeployConfigField(EContracts.VkRegistry, "messageBatchDepth"); - const messageTreeDepth = deployment.getDeployConfigField(EContracts.VkRegistry, "messageTreeDepth"); + const messageBatchSize = deployment.getDeployConfigField(EContracts.VkRegistry, "messageBatchSize"); const voteOptionTreeDepth = deployment.getDeployConfigField(EContracts.VkRegistry, "voteOptionTreeDepth"); const useQuadraticVoting = @@ -54,10 +53,9 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").then((task) => pollDuration, { intStateTreeDepth, - messageTreeSubDepth, - messageTreeDepth, voteOptionTreeDepth, }, + messageBatchSize, unserializedKey.asContractParam(), verifierContractAddress, vkRegistryContractAddress, @@ -68,10 +66,9 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").then((task) => pollDuration, { intStateTreeDepth, - messageTreeSubDepth, - messageTreeDepth, voteOptionTreeDepth, }, + messageBatchSize, unserializedKey.asContractParam(), verifierContractAddress, vkRegistryContractAddress, @@ -85,7 +82,10 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").then((task) => } const pollContract = await deployment.getContract({ name: EContracts.Poll, address: pollContractAddress }); - const [maxValues, extContracts] = await Promise.all([pollContract.maxValues(), pollContract.extContracts()]); + const [maxVoteOptions, extContracts] = await Promise.all([ + pollContract.maxVoteOptions(), + pollContract.extContracts(), + ]); const messageProcessorContract = await deployment.getContract({ name: EContracts.MessageProcessor, @@ -97,11 +97,6 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").then((task) => address: tallyContractAddress, }); - const messageAccQueueContract = await deployment.getContract({ - name: EContracts.AccQueueQuinaryMaci, - address: extContracts[1], - }); - await Promise.all([ storage.register({ id: EContracts.Poll, @@ -109,13 +104,14 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").then((task) => contract: pollContract, args: [ pollDuration, - maxValues.map((value) => value.toString()), + maxVoteOptions, { intStateTreeDepth, - messageTreeSubDepth, - messageTreeDepth, voteOptionTreeDepth, }, + { + messageBatchSize, + }, unserializedKey.asContractParam(), extContracts, ], @@ -143,15 +139,6 @@ deployment.deployTask("poll:deploy-poll", "Deploy poll").then((task) => ], network: hre.network.name, }), - - storage.register({ - id: EContracts.AccQueueQuinaryMaci, - key: `poll-${pollId}`, - name: "contracts/trees/AccQueueQuinaryMaci.sol:AccQueueQuinaryMaci", - contract: messageAccQueueContract, - args: [messageTreeSubDepth], - network: hre.network.name, - }), ]); }), ); diff --git a/contracts/tasks/helpers/ProofGenerator.ts b/contracts/tasks/helpers/ProofGenerator.ts index 97bdf9569b..656e57acb5 100644 --- a/contracts/tasks/helpers/ProofGenerator.ts +++ b/contracts/tasks/helpers/ProofGenerator.ts @@ -71,7 +71,6 @@ export class ProofGenerator { static async prepareState({ maciContract, pollContract, - messageAq, pollId, maciPrivateKey, coordinatorKeypair, @@ -92,26 +91,18 @@ export class ProofGenerator { } // build an off-chain representation of the MACI contract using data in the contract storage - const [defaultStartBlockSignup, defaultStartBlockPoll, { messageTreeDepth }, stateRoot, numSignups] = - await Promise.all([ - maciContract - .queryFilter(maciContract.filters.SignUp(), startBlock) - .then((events) => events[0]?.blockNumber ?? 0), - maciContract - .queryFilter(maciContract.filters.DeployPoll(), startBlock) - .then((events) => events[0]?.blockNumber ?? 0), - pollContract.treeDepths(), - maciContract.getStateTreeRoot(), - maciContract.numSignUps(), - ]); + const [defaultStartBlockSignup, defaultStartBlockPoll, stateRoot, numSignups] = await Promise.all([ + maciContract.queryFilter(maciContract.filters.SignUp(), startBlock).then((events) => events[0]?.blockNumber ?? 0), + maciContract + .queryFilter(maciContract.filters.DeployPoll(), startBlock) + .then((events) => events[0]?.blockNumber ?? 0), + maciContract.getStateTreeRoot(), + maciContract.numSignUps(), + ]); const defaultStartBlock = Math.min(defaultStartBlockPoll, defaultStartBlockSignup); let fromBlock = startBlock ? Number(startBlock) : defaultStartBlock; - const messageRoot = await messageAq.getMainRoot(messageTreeDepth); const defaultEndBlock = await Promise.all([ - pollContract - .queryFilter(pollContract.filters.MergeMessageAq(messageRoot), fromBlock) - .then((events) => events[events.length - 1]?.blockNumber), pollContract .queryFilter(pollContract.filters.MergeMaciState(stateRoot, numSignups), fromBlock) .then((events) => events[events.length - 1]?.blockNumber), diff --git a/contracts/tasks/helpers/Prover.ts b/contracts/tasks/helpers/Prover.ts index 8e2ffb494b..f811b82098 100644 --- a/contracts/tasks/helpers/Prover.ts +++ b/contracts/tasks/helpers/Prover.ts @@ -3,7 +3,7 @@ import { G1Point, G2Point, hashLeftRight } from "maci-crypto"; import { VerifyingKey } from "maci-domainobjs"; import type { IVerifyingKeyStruct, Proof } from "../../ts/types"; -import type { AccQueue, MACI, MessageProcessor, Poll, Tally, Verifier, VkRegistry } from "../../typechain-types"; +import type { MACI, MessageProcessor, Poll, Tally, Verifier, VkRegistry } from "../../typechain-types"; import type { BigNumberish } from "ethers"; import { formatProofForVerifierContract, asHex } from "../../ts/utils"; @@ -25,11 +25,6 @@ export class Prover { */ private mpContract: MessageProcessor; - /** - * AccQueue contract typechain wrapper (messages) - */ - private messageAqContract: AccQueue; - /** * MACI contract typechain wrapper */ @@ -58,7 +53,6 @@ export class Prover { constructor({ pollContract, mpContract, - messageAqContract, maciContract, vkRegistryContract, verifierContract, @@ -66,7 +60,6 @@ export class Prover { }: IProverParams) { this.pollContract = pollContract; this.mpContract = mpContract; - this.messageAqContract = messageAqContract; this.maciContract = maciContract; this.vkRegistryContract = vkRegistryContract; this.verifierContract = verifierContract; @@ -80,20 +73,29 @@ export class Prover { */ async proveMessageProcessing(proofs: Proof[]): Promise { // retrieve the values we need from the smart contracts - const [treeDepths, numSignUpsAndMessages, numBatchesProcessed, stateTreeDepth, dd, coordinatorPubKeyHash, mode] = - await Promise.all([ - this.pollContract.treeDepths(), - this.pollContract.numSignUpsAndMessages(), - this.mpContract.numBatchesProcessed().then(Number), - this.maciContract.stateTreeDepth().then(Number), - this.pollContract.getDeployTimeAndDuration(), - this.pollContract.coordinatorPubKeyHash(), - this.mpContract.mode(), - ]); + const [ + treeDepths, + msgBatchSize, + numSignUpsAndMessages, + numBatchesProcessed, + stateTreeDepth, + dd, + coordinatorPubKeyHash, + mode, + ] = await Promise.all([ + this.pollContract.treeDepths(), + this.pollContract.messageBatchSize(), + this.pollContract.numSignUpsAndMessages(), + this.mpContract.numBatchesProcessed().then(Number), + this.maciContract.stateTreeDepth().then(Number), + this.pollContract.getDeployTimeAndDuration(), + this.pollContract.coordinatorPubKeyHash(), + this.mpContract.mode(), + ]); const numSignUps = Number(numSignUpsAndMessages[0]); const numMessages = Number(numSignUpsAndMessages[1]); - const messageBatchSize = STATE_TREE_ARITY ** Number(treeDepths[1]); + const messageBatchSize = Number(msgBatchSize); let totalMessageBatches = numMessages <= messageBatchSize ? 1 : Math.floor(numMessages / messageBatchSize); let numberBatchesProcessed = numBatchesProcessed; @@ -101,16 +103,12 @@ export class Prover { totalMessageBatches += 1; } - const [messageRootOnChain, onChainProcessVk] = await Promise.all([ - this.messageAqContract.getMainRoot(Number(treeDepths[2])), - this.vkRegistryContract.getProcessVk( - stateTreeDepth, - treeDepths.messageTreeDepth, - treeDepths.voteOptionTreeDepth, - messageBatchSize, - mode, - ), - ]); + const onChainProcessVk = await this.vkRegistryContract.getProcessVk( + stateTreeDepth, + treeDepths.voteOptionTreeDepth, + messageBatchSize, + mode, + ); const pollEndTimestampOnChain = BigInt(dd[0]) + BigInt(dd[1]); @@ -145,7 +143,6 @@ export class Prover { // validation this.validatePollDuration(circuitInputs.pollEndTimestamp as BigNumberish, pollEndTimestampOnChain); - this.validateMessageRoot(circuitInputs.msgRoot as BigNumberish, messageRootOnChain); let currentSbCommitmentOnChain: bigint; @@ -172,7 +169,7 @@ export class Prover { currentMessageBatchIndex, numSignUps, numMessages, - treeDepths.messageTreeSubDepth, + messageBatchSize, treeDepths.voteOptionTreeDepth, ), ).toString(); @@ -180,15 +177,17 @@ export class Prover { const formattedProof = formatProofForVerifierContract(proof); + const batchHashes = await this.pollContract.getBatchHashes(); + const publicInputHashOnChain = BigInt( await this.mpContract.genProcessMessagesPublicInputHash( currentMessageBatchIndex, - messageRootOnChain.toString(), + batchHashes[currentMessageBatchIndex + 1].toString(), numSignUps, numMessages, circuitInputs.currentSbCommitment as BigNumberish, circuitInputs.newSbCommitment as BigNumberish, - treeDepths.messageTreeSubDepth, + messageBatchSize, treeDepths.voteOptionTreeDepth, ), ); @@ -333,19 +332,6 @@ export class Prover { } } - /** - * Validate message root - * - * @param messageRoot - off-chain message root - * @param messageRootOnChain - on-chain message root - * @throws error if roots don't match - */ - private validateMessageRoot(messageRoot: BigNumberish, messageRootOnChain: BigNumberish) { - if (BigInt(messageRoot).toString() !== messageRootOnChain.toString()) { - throw new Error("message root mismatch"); - } - } - /** * Validate commitment * diff --git a/contracts/tasks/helpers/TreeMerger.ts b/contracts/tasks/helpers/TreeMerger.ts index a28105751d..729ca6c81d 100644 --- a/contracts/tasks/helpers/TreeMerger.ts +++ b/contracts/tasks/helpers/TreeMerger.ts @@ -1,6 +1,6 @@ /* eslint-disable no-console */ import type { ITreeMergeParams } from "./types"; -import type { AccQueue, Poll } from "../../typechain-types"; +import type { Poll } from "../../typechain-types"; import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; /** @@ -8,11 +8,6 @@ import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signer * This class is using for merging signups and messages. */ export class TreeMerger { - /** - * User messages AccQueue contract - */ - private messageAccQueueContract: AccQueue; - /** * Poll contract */ @@ -28,9 +23,8 @@ export class TreeMerger { * * @param {ITreeMergeParams} params - contracts and signer */ - constructor({ deployer, messageAccQueueContract, pollContract }: ITreeMergeParams) { + constructor({ deployer, pollContract }: ITreeMergeParams) { this.pollContract = pollContract; - this.messageAccQueueContract = messageAccQueueContract; this.deployer = deployer; } @@ -73,69 +67,4 @@ export class TreeMerger { console.log("The state tree has already been merged."); } } - - /** - * Merge message subtrees - * - * @param queueOps - the number of queue operations to perform - */ - async mergeMessageSubtrees(queueOps: number): Promise { - let subTreesMerged = false; - - // infinite loop to merge the sub trees - while (!subTreesMerged) { - // eslint-disable-next-line no-await-in-loop - subTreesMerged = await this.messageAccQueueContract.subTreesMerged(); - - if (subTreesMerged) { - console.log("All message subtrees have been merged."); - } else { - // eslint-disable-next-line no-await-in-loop - await this.messageAccQueueContract.getSrIndices().then((indices) => { - console.log(`Merging message subroots ${indices[0] + 1n} / ${indices[1] + 1n}`); - }); - - // eslint-disable-next-line no-await-in-loop - const tx = await this.pollContract.mergeMessageAqSubRoots(queueOps); - // eslint-disable-next-line no-await-in-loop - const receipt = await tx.wait(); - - if (receipt?.status !== 1) { - throw new Error("Merge message subroots transaction failed"); - } - - console.log(`Executed mergeMessageAqSubRoots(); gas used: ${receipt.gasUsed.toString()}`); - - console.log(`Transaction hash: ${receipt.hash}`); - } - } - } - - /** - * Merge message queue - */ - async mergeMessages(): Promise { - // check if the message AQ has been fully merged - const messageTreeDepth = await this.pollContract.treeDepths().then((depths) => Number(depths[2])); - - // check if the main root was not already computed - const mainRoot = await this.messageAccQueueContract.getMainRoot(messageTreeDepth.toString()); - if (mainRoot.toString() === "0") { - // go and merge the message tree - - console.log("Merging subroots to a main message root..."); - const tx = await this.pollContract.mergeMessageAq(); - const receipt = await tx.wait(); - - if (receipt?.status !== 1) { - throw new Error("Merge messages transaction failed"); - } - - console.log(`Executed mergeMessageAq(); gas used: ${receipt.gasUsed.toString()}`); - console.log(`Transaction hash: ${receipt.hash}`); - console.log("The message tree has been merged."); - } else { - console.log("The message tree has already been merged."); - } - } } diff --git a/contracts/tasks/helpers/types.ts b/contracts/tasks/helpers/types.ts index 1417ccf150..ffdae49a69 100644 --- a/contracts/tasks/helpers/types.ts +++ b/contracts/tasks/helpers/types.ts @@ -1,4 +1,4 @@ -import type { AccQueue, MACI, MessageProcessor, Poll, Tally, Verifier, VkRegistry } from "../../typechain-types"; +import type { MACI, MessageProcessor, Poll, Tally, Verifier, VkRegistry } from "../../typechain-types"; import type { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; import type { BaseContract, @@ -233,11 +233,6 @@ export interface IPrepareStateParams { */ pollContract: Poll; - /** - * MessageAq contract - */ - messageAq: AccQueue; - /** * Poll id */ @@ -303,11 +298,6 @@ export interface IProverParams { */ mpContract: MessageProcessor; - /** - * AccQueue contract typechain wrapper (messages) - */ - messageAqContract: AccQueue; - /** * MACI contract typechain wrapper */ @@ -507,9 +497,6 @@ export enum EContracts { Poll = "Poll", Tally = "Tally", MessageProcessor = "MessageProcessor", - AccQueue = "AccQueue", - AccQueueQuinaryBlankSl = "AccQueueQuinaryBlankSl", - AccQueueQuinaryMaci = "AccQueueQuinaryMaci", } /** @@ -558,11 +545,6 @@ export interface ITreeMergeParams { * Poll contract */ pollContract: Poll; - - /** - * Message AccQueue contract - */ - messageAccQueueContract: AccQueue; } /** diff --git a/contracts/tasks/runner/merge.ts b/contracts/tasks/runner/merge.ts index 8b117beced..98e0182188 100644 --- a/contracts/tasks/runner/merge.ts +++ b/contracts/tasks/runner/merge.ts @@ -2,22 +2,19 @@ import { ZeroAddress } from "ethers"; import { task, types } from "hardhat/config"; -import type { AccQueue, MACI, Poll } from "../../typechain-types"; +import type { MACI, Poll } from "../../typechain-types"; import { Deployment } from "../helpers/Deployment"; import { TreeMerger } from "../helpers/TreeMerger"; import { EContracts, type IMergeParams } from "../helpers/types"; -const DEFAULT_SR_QUEUE_OPS = 4; - /** - * Command to merge signup and message queues of a MACI contract + * Command to merge signups of a MACI contract */ -task("merge", "Merge signups and messages") +task("merge", "Merge signups") .addParam("poll", "The poll id", undefined, types.string) - .addOptionalParam("queueOps", "The number of queue operations to perform", DEFAULT_SR_QUEUE_OPS, types.int) .addOptionalParam("prove", "Run prove command after merging", false, types.boolean) - .setAction(async ({ poll, prove, queueOps = DEFAULT_SR_QUEUE_OPS }: IMergeParams, hre) => { + .setAction(async ({ poll, prove }: IMergeParams, hre) => { const deployment = Deployment.getInstance(hre); deployment.setHre(hre); @@ -28,12 +25,6 @@ task("merge", "Merge signups and messages") const pollContractAddress = await maciContract.polls(poll); const pollContract = await deployment.getContract({ name: EContracts.Poll, address: pollContractAddress }); - const [, messageAccQueueContractAddress] = await pollContract.extContracts(); - - const messageAccQueueContract = await deployment.getContract({ - name: EContracts.AccQueue, - address: messageAccQueueContractAddress, - }); if (!pollContractAddress || pollContractAddress === ZeroAddress) { throw new Error(`No poll ${poll} found`); @@ -42,7 +33,6 @@ task("merge", "Merge signups and messages") const treeMerger = new TreeMerger({ deployer, pollContract, - messageAccQueueContract, }); const startBalance = await deployer.provider.getBalance(deployer); @@ -52,8 +42,6 @@ task("merge", "Merge signups and messages") await treeMerger.checkPollDuration(); await treeMerger.mergeSignups(); - await treeMerger.mergeMessageSubtrees(queueOps); - await treeMerger.mergeMessages(); const endBalance = await deployer.provider.getBalance(deployer); diff --git a/contracts/tasks/runner/prove.ts b/contracts/tasks/runner/prove.ts index 49259e3fce..d882ed4973 100644 --- a/contracts/tasks/runner/prove.ts +++ b/contracts/tasks/runner/prove.ts @@ -6,7 +6,7 @@ import { Keypair, PrivKey } from "maci-domainobjs"; import fs from "fs"; import type { Proof } from "../../ts/types"; -import type { VkRegistry, Verifier, MACI, Poll, AccQueue, MessageProcessor, Tally } from "../../typechain-types"; +import type { VkRegistry, Verifier, MACI, Poll, MessageProcessor, Tally } from "../../typechain-types"; import { ContractStorage } from "../helpers/ContractStorage"; import { Deployment } from "../helpers/Deployment"; @@ -74,17 +74,6 @@ task("prove", "Command to generate proof and prove the result of a poll on-chain const pollAddress = await maciContract.polls(poll); const pollContract = await deployment.getContract({ name: EContracts.Poll, address: pollAddress }); - const messageAqAddress = await pollContract.extContracts().then((contracts) => contracts.messageAq); - const messageAq = await deployment.getContract({ - name: EContracts.AccQueue, - address: messageAqAddress, - }); - - const [, messageAqContractAddress] = await pollContract.extContracts(); - const messageAqContract = await deployment.getContract({ - name: EContracts.AccQueue, - address: messageAqContractAddress, - }); const isStateAqMerged = await pollContract.stateMerged(); // Check that the state and message trees have been merged for at least the first poll @@ -92,19 +81,9 @@ task("prove", "Command to generate proof and prove the result of a poll on-chain throw new Error("The state tree has not been merged yet. Please use the mergeSignups subcommand to do so."); } - const messageTreeDepth = await pollContract.treeDepths().then((depths) => Number(depths[2])); - - // check that the main root is set - const mainRoot = await messageAqContract.getMainRoot(messageTreeDepth.toString()); - - if (mainRoot.toString() === "0") { - throw new Error("The message tree has not been merged yet. Please use the mergeMessages subcommand to do so."); - } - const maciState = await ProofGenerator.prepareState({ maciContract, pollContract, - messageAq, maciPrivateKey, coordinatorKeypair, pollId: poll, @@ -181,7 +160,6 @@ task("prove", "Command to generate proof and prove the result of a poll on-chain const prover = new Prover({ maciContract, - messageAqContract, mpContract, pollContract, vkRegistryContract, diff --git a/contracts/tests/AccQueue.test.ts b/contracts/tests/AccQueue.test.ts deleted file mode 100644 index 1929218b9e..0000000000 --- a/contracts/tests/AccQueue.test.ts +++ /dev/null @@ -1,496 +0,0 @@ -import { expect } from "chai"; -import { AccQueue, hashLeftRight, NOTHING_UP_MY_SLEEVE } from "maci-crypto"; - -import { - AccQueueBinary0__factory as AccQueueBinary0Factory, - AccQueueBinaryMaci__factory as AccQueueBinaryMaciFactory, - AccQueue as AccQueueContract, - AccQueueQuinary0__factory as AccQueueQuinary0Factory, - AccQueueQuinaryMaci__factory as AccQueueQuinaryMaciFactory, -} from "../typechain-types"; - -import { - deployTestAccQueues, - fillGasLimit, - enqueueGasLimit, - testEmptySubtree, - testEmptyUponDeployment, - testEnqueue, - testEnqueueAndInsertSubTree, - testFillForAllIncompletes, - testIncompleteSubtree, - testInsertSubTrees, - testMerge, - testMergeAgain, -} from "./utils"; - -describe("AccQueues", () => { - describe("Binary AccQueue enqueues", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - let aqContract: AccQueueContract; - - before(async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aqContract = r.aqContract as AccQueueContract; - }); - - it("should be empty upon deployment", async () => { - await testEmptyUponDeployment(aqContract); - }); - - it("should not be able to get a subroot that does not exist", async () => { - await expect(aqContract.getSubRoot(5)).to.be.revertedWithCustomError(aqContract, "InvalidIndex"); - }); - - it("should enqueue leaves", async () => { - await testEnqueue(aqContract, HASH_LENGTH, SUB_DEPTH, ZERO); - }); - }); - - describe("Quinary AccQueue enqueues", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 5; - const ZERO = BigInt(0); - let aqContract: AccQueueContract; - - before(async () => { - const r = await deployTestAccQueues(AccQueueQuinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aqContract = r.aqContract as AccQueueContract; - }); - - it("should be empty upon deployment", async () => { - await testEmptyUponDeployment(aqContract); - }); - - it("should not be able to get a subroot that does not exist", async () => { - await expect(aqContract.getSubRoot(5)).to.be.revertedWithCustomError(aqContract, "InvalidIndex"); - }); - - it("should enqueue leaves", async () => { - await testEnqueue(aqContract, HASH_LENGTH, SUB_DEPTH, ZERO); - }); - }); - - describe("Binary AccQueue0 fills", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - let aq: AccQueue; - let aqContract: AccQueueContract; - - before(async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aq = r.aq; - aqContract = r.aqContract as AccQueueContract; - }); - - it("should fill an empty subtree", async () => { - await testEmptySubtree(aq, aqContract, 0); - }); - - it("should fill an incomplete subtree", async () => { - await testIncompleteSubtree(aq, aqContract); - }); - - it("Filling an empty subtree again should create the correct subroot", async () => { - await testEmptySubtree(aq, aqContract, 2); - }); - - it("fill() should be correct for every number of leaves in an incomplete subtree", async () => { - await testFillForAllIncompletes(aq, aqContract, HASH_LENGTH); - }); - }); - - describe("Quinary AccQueue0 fills", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 5; - const ZERO = BigInt(0); - let aq: AccQueue; - let aqContract: AccQueueContract; - - before(async () => { - const r = await deployTestAccQueues(AccQueueQuinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aq = r.aq; - aqContract = r.aqContract as AccQueueContract; - }); - - it("should fill an empty subtree", async () => { - await testEmptySubtree(aq, aqContract, 0); - }); - - it("should fill an incomplete subtree", async () => { - await testIncompleteSubtree(aq, aqContract); - }); - - it("Filling an empty subtree again should create the correct subroot", async () => { - await testEmptySubtree(aq, aqContract, 2); - }); - - it("fill() should be correct for every number of leaves in an incomplete subtree", async () => { - await testFillForAllIncompletes(aq, aqContract, HASH_LENGTH); - }); - }); - - describe("Binary AccQueueMaci fills", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 2; - const ZERO = NOTHING_UP_MY_SLEEVE; - let aq: AccQueue; - let aqContract: AccQueueContract; - - before(async () => { - const r = await deployTestAccQueues(AccQueueBinaryMaciFactory, SUB_DEPTH, HASH_LENGTH, ZERO); - aq = r.aq; - aqContract = r.aqContract as AccQueueContract; - }); - - it("should fill an empty subtree", async () => { - await testEmptySubtree(aq, aqContract, 0); - }); - - it("should fill an incomplete subtree", async () => { - await testIncompleteSubtree(aq, aqContract); - }); - - it("Filling an empty subtree again should create the correct subroot", async () => { - await testEmptySubtree(aq, aqContract, 2); - }); - - it("fill() should be correct for every number of leaves in an incomplete subtree", async () => { - await testFillForAllIncompletes(aq, aqContract, HASH_LENGTH); - }); - }); - - describe("Quinary AccQueueMaci fills", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 5; - const ZERO = NOTHING_UP_MY_SLEEVE; - let aq: AccQueue; - let aqContract: AccQueueContract; - - before(async () => { - const r = await deployTestAccQueues(AccQueueQuinaryMaciFactory, SUB_DEPTH, HASH_LENGTH, ZERO); - aq = r.aq; - aqContract = r.aqContract as AccQueueContract; - }); - - it("should fill an empty subtree", async () => { - await testEmptySubtree(aq, aqContract, 0); - }); - - it("should fill an incomplete subtree", async () => { - await testIncompleteSubtree(aq, aqContract); - }); - - it("Filling an empty subtree again should create the correct subroot", async () => { - await testEmptySubtree(aq, aqContract, 2); - }); - - it("fill() should be correct for every number of leaves in an incomplete subtree", async () => { - await testFillForAllIncompletes(aq, aqContract, HASH_LENGTH); - }); - }); - - describe("Merge after enqueuing more leaves", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - const MAIN_DEPTH = 3; - - it("should produce the correct main roots", async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - await testMergeAgain(r.aq, r.aqContract as AccQueueContract, MAIN_DEPTH); - }); - }); - - describe("Edge cases", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - - it("should not be possible to merge into a tree of depth 0", async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - - const aqContract = r.aqContract as AccQueueContract; - await aqContract.enqueue(1).then((tx) => tx.wait()); - await aqContract.mergeSubRoots(0, { gasLimit: 1000000 }).then((tx) => tx.wait()); - await expect(aqContract.merge(0, { gasLimit: 1000000 })).to.revertedWithCustomError( - aqContract, - "DepthCannotBeZero", - ); - }); - - it("A small SRT of depth 1 should just have 2 leaves", async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, 1, HASH_LENGTH, ZERO); - - const aqContract = r.aqContract as AccQueueContract; - await aqContract.enqueue(0, enqueueGasLimit).then((tx) => tx.wait()); - await aqContract.mergeSubRoots(0, { gasLimit: 1000000 }).then((tx) => tx.wait()); - const srtRoot = await aqContract.getSmallSRTroot(); - const expectedRoot = hashLeftRight(BigInt(0), BigInt(0)); - expect(srtRoot.toString()).to.eq(expectedRoot.toString()); - }); - - it("should not be possible to merge subroots into a tree shorter than the SRT depth", async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, 1, HASH_LENGTH, ZERO); - const aqContract = r.aqContract as AccQueueContract; - for (let i = 0; i < 4; i += 1) { - // eslint-disable-next-line no-await-in-loop - await aqContract.fill(fillGasLimit).then((tx) => tx.wait()); - } - - await aqContract.mergeSubRoots(0, { gasLimit: 1000000 }).then((tx) => tx.wait()); - - await expect(aqContract.merge(1, { gasLimit: 1000000 })).to.be.revertedWithCustomError( - aqContract, - "DepthTooSmall", - ); - }); - - it("Merging without enqueuing new data should not change the root", async () => { - const MAIN_DEPTH = 5; - - const r = await deployTestAccQueues(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - - const { aq } = r; - const aqContract = r.aqContract as AccQueueContract; - // Merge once - await testMerge(aq, aqContract, 1, MAIN_DEPTH); - // Get the root - const expectedMainRoot = (await aqContract.getMainRoot(MAIN_DEPTH)).toString(); - // Merge again - await aqContract.merge(MAIN_DEPTH, { gasLimit: 8000000 }).then((tx) => tx.wait()); - // Get the root again - const root = (await aqContract.getMainRoot(MAIN_DEPTH)).toString(); - // Check that the roots match - expect(root).to.eq(expectedMainRoot); - }); - }); - - describe("Binary AccQueue0 one-shot merges", () => { - const SUB_DEPTH = 2; - const MAIN_DEPTH = 5; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - - const testParams = [1, 2, 3, 4]; - testParams.forEach((testParam) => { - it(`should merge ${testParam} subtrees`, async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - const { aq } = r; - const aqContract = r.aqContract as AccQueueContract; - await testMerge(aq, aqContract, testParam, MAIN_DEPTH); - }); - }); - }); - - describe("Quinary AccQueue0 one-shot merges", () => { - const SUB_DEPTH = 2; - const MAIN_DEPTH = 6; - const HASH_LENGTH = 5; - const ZERO = BigInt(0); - - const testParams = [1, 5, 26]; - testParams.forEach((testParam) => { - it(`should merge ${testParam} subtrees`, async () => { - const r = await deployTestAccQueues(AccQueueQuinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - const { aq } = r; - const aqContract = r.aqContract as AccQueueContract; - await testMerge(aq, aqContract, testParam, MAIN_DEPTH); - }); - }); - }); - - describe("Binary AccQueue0 subtree insertions", () => { - const SUB_DEPTH = 2; - const MAIN_DEPTH = 6; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - - it("Enqueued leaves and inserted subtrees should be in the right order", async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - const { aq } = r; - const aqContract = r.aqContract as AccQueueContract; - await testEnqueueAndInsertSubTree(aq, aqContract); - }); - - const testParams = [1, 2, 3, 9]; - testParams.forEach((testParam) => { - it(`should insert ${testParam} subtrees`, async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - const { aq } = r; - const aqContract = r.aqContract as AccQueueContract; - await testInsertSubTrees(aq, aqContract, testParam, MAIN_DEPTH); - }); - }); - }); - - describe("Quinary AccQueue0 subtree insertions", () => { - const SUB_DEPTH = 2; - const MAIN_DEPTH = 6; - const HASH_LENGTH = 5; - const ZERO = BigInt(0); - - it("Enqueued leaves and inserted subtrees should be in the right order", async () => { - const r = await deployTestAccQueues(AccQueueQuinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - const { aq } = r; - const aqContract = r.aqContract as AccQueueContract; - await testEnqueueAndInsertSubTree(aq, aqContract); - }); - - const testParams = [1, 4, 9, 26]; - testParams.forEach((testParam) => { - it(`should insert ${testParam} subtrees`, async () => { - const r = await deployTestAccQueues(AccQueueQuinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - const { aq } = r; - const aqContract = r.aqContract as AccQueueContract; - await testInsertSubTrees(aq, aqContract, testParam, MAIN_DEPTH); - }); - }); - }); - - describe("Binary AccQueue0 progressive merges", () => { - const SUB_DEPTH = 2; - const MAIN_DEPTH = 5; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - const NUM_SUBTREES = 5; - let aq: AccQueue; - let aqContract: AccQueueContract; - - before(async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aq = r.aq; - aqContract = r.aqContract as AccQueueContract; - }); - - it(`should progressively merge ${NUM_SUBTREES} subtrees`, async () => { - for (let i = 0; i < NUM_SUBTREES; i += 1) { - const leaf = BigInt(123); - // eslint-disable-next-line no-await-in-loop - await aqContract.enqueue(leaf.toString(), enqueueGasLimit).then((tx) => tx.wait()); - aq.enqueue(leaf); - - aq.fill(); - // eslint-disable-next-line no-await-in-loop - await aqContract.fill(fillGasLimit).then((tx) => tx.wait()); - } - - aq.mergeSubRoots(0); - const expectedSmallSRTroot = aq.getSmallSRTroot(); - - await expect(aqContract.getSmallSRTroot()).to.be.revertedWithCustomError(aqContract, "SubTreesNotMerged"); - - await aqContract.mergeSubRoots(2).then((tx) => tx.wait()); - await aqContract.mergeSubRoots(2).then((tx) => tx.wait()); - await aqContract.mergeSubRoots(1).then((tx) => tx.wait()); - - const contractSmallSRTroot = await aqContract.getSmallSRTroot(); - expect(expectedSmallSRTroot.toString()).to.eq(contractSmallSRTroot.toString()); - - aq.merge(MAIN_DEPTH); - await (await aqContract.merge(MAIN_DEPTH)).wait(); - - const expectedMainRoot = aq.getMainRoots()[MAIN_DEPTH]; - const contractMainRoot = await aqContract.getMainRoot(MAIN_DEPTH); - - expect(expectedMainRoot.toString()).to.eq(contractMainRoot.toString()); - }); - }); - - describe("Quinary AccQueue0 progressive merges", () => { - const SUB_DEPTH = 2; - const MAIN_DEPTH = 5; - const HASH_LENGTH = 5; - const ZERO = BigInt(0); - const NUM_SUBTREES = 6; - let aq: AccQueue; - let aqContract: AccQueueContract; - - before(async () => { - const r = await deployTestAccQueues(AccQueueQuinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aq = r.aq; - aqContract = r.aqContract as AccQueueContract; - }); - - it(`should progressively merge ${NUM_SUBTREES} subtrees`, async () => { - for (let i = 0; i < NUM_SUBTREES; i += 1) { - const leaf = BigInt(123); - // eslint-disable-next-line no-await-in-loop - await aqContract.enqueue(leaf.toString(), enqueueGasLimit).then((tx) => tx.wait()); - aq.enqueue(leaf); - - aq.fill(); - // eslint-disable-next-line no-await-in-loop - await aqContract.fill(fillGasLimit).then((tx) => tx.wait()); - } - - aq.mergeSubRoots(0); - const expectedSmallSRTroot = aq.getSmallSRTroot(); - - await expect(aqContract.getSmallSRTroot()).to.be.revertedWithCustomError(aqContract, "SubTreesNotMerged"); - - await (await aqContract.mergeSubRoots(2)).wait(); - await (await aqContract.mergeSubRoots(2)).wait(); - await (await aqContract.mergeSubRoots(2)).wait(); - - const contractSmallSRTroot = await aqContract.getSmallSRTroot(); - expect(expectedSmallSRTroot.toString()).to.eq(contractSmallSRTroot.toString()); - - aq.merge(MAIN_DEPTH); - await (await aqContract.merge(MAIN_DEPTH)).wait(); - - const expectedMainRoot = aq.getMainRoots()[MAIN_DEPTH]; - const contractMainRoot = await aqContract.getMainRoot(MAIN_DEPTH); - - expect(expectedMainRoot.toString()).to.eq(contractMainRoot.toString()); - }); - }); - - describe("Conditions that cause merge() to revert", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - const NUM_SUBTREES = 1; - let aqContract: AccQueueContract; - - before(async () => { - const r = await deployTestAccQueues(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aqContract = r.aqContract as AccQueueContract; - }); - - it("mergeSubRoots() should fail on an empty AccQueue", async () => { - await expect(aqContract.mergeSubRoots(0, { gasLimit: 1000000 })).to.be.revertedWithCustomError( - aqContract, - "NothingToMerge", - ); - }); - - it("merge() should revert on an empty AccQueue", async () => { - await expect(aqContract.merge(1, { gasLimit: 1000000 })).to.be.revertedWithCustomError( - aqContract, - "SubTreesNotMerged", - ); - }); - - it(`merge() should revert if there are unmerged subtrees`, async () => { - for (let i = 0; i < NUM_SUBTREES; i += 1) { - // eslint-disable-next-line no-await-in-loop - await aqContract.fill(fillGasLimit).then((tx) => tx.wait()); - } - - await expect(aqContract.merge(1)).to.be.revertedWithCustomError(aqContract, "SubTreesNotMerged"); - }); - - it(`merge() should revert if the desired depth is invalid`, async () => { - await aqContract.mergeSubRoots(0, { gasLimit: 1000000 }).then((tx) => tx.wait()); - - await expect(aqContract.merge(0, { gasLimit: 1000000 })).to.be.revertedWithCustomError( - aqContract, - "DepthCannotBeZero", - ); - }); - }); -}); diff --git a/contracts/tests/AccQueueBenchmark.test.ts b/contracts/tests/AccQueueBenchmark.test.ts deleted file mode 100644 index 981ea51769..0000000000 --- a/contracts/tests/AccQueueBenchmark.test.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { expect } from "chai"; -import { AccQueue, NOTHING_UP_MY_SLEEVE } from "maci-crypto"; - -import { linkPoseidonLibraries } from "../tasks/helpers/abi"; -import { deployPoseidonContracts, createContractFactory } from "../ts/deploy"; -import { getDefaultSigner } from "../ts/utils"; -import { - AccQueueBinary0__factory as AccQueueBinary0Factory, - AccQueue as AccQueueContract, - AccQueueQuinary0__factory as AccQueueQuinary0Factory, - AccQueueQuinaryMaci__factory as AccQueueQuinaryMaciFactory, -} from "../typechain-types"; - -let aqContract: AccQueueContract; - -const deploy = async ( - factory: typeof AccQueueBinary0Factory | typeof AccQueueQuinary0Factory | typeof AccQueueQuinaryMaciFactory, - SUB_DEPTH: number, - HASH_LENGTH: number, - ZERO: bigint, -) => { - const { PoseidonT3Contract, PoseidonT4Contract, PoseidonT5Contract, PoseidonT6Contract } = - await deployPoseidonContracts(await getDefaultSigner(), {}, true); - const [poseidonT3ContractAddress, poseidonT4ContractAddress, poseidonT5ContractAddress, poseidonT6ContractAddress] = - await Promise.all([ - PoseidonT3Contract.getAddress(), - PoseidonT4Contract.getAddress(), - PoseidonT5Contract.getAddress(), - PoseidonT6Contract.getAddress(), - ]); - - // Link Poseidon contracts - const AccQueueFactory = await createContractFactory( - factory.abi, - factory.linkBytecode( - linkPoseidonLibraries( - poseidonT3ContractAddress, - poseidonT4ContractAddress, - poseidonT5ContractAddress, - poseidonT6ContractAddress, - ), - ), - await getDefaultSigner(), - ); - - aqContract = (await AccQueueFactory.deploy(SUB_DEPTH)) as typeof aqContract; - - await aqContract.deploymentTransaction()?.wait(); - - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - return { aq, aqContract }; -}; - -const testMerge = async ( - aq: AccQueue, - contract: AccQueueContract, - NUM_SUBTREES: number, - MAIN_DEPTH: number, - NUM_MERGES: number, -) => { - for (let i = 0; i < NUM_SUBTREES; i += 1) { - const leaf = BigInt(123); - // eslint-disable-next-line no-await-in-loop - await contract.enqueue(leaf.toString(), { gasLimit: 200000 }).then((tx) => tx.wait()); - - aq.enqueue(leaf); - aq.fill(); - - // eslint-disable-next-line no-await-in-loop - await contract.fill({ gasLimit: 2000000 }).then((t) => t.wait()); - } - - if (NUM_MERGES === 0) { - aq.mergeSubRoots(NUM_MERGES); - const tx = await contract.mergeSubRoots(NUM_MERGES, { gasLimit: 8000000 }); - const receipt = await tx.wait(); - - expect(receipt).to.not.eq(null); - expect(receipt?.gasUsed.toString()).to.not.eq(""); - expect(receipt?.gasUsed.toString()).to.not.eq("0"); - } else { - for (let i = 0; i < NUM_MERGES; i += 1) { - const n = NUM_SUBTREES / NUM_MERGES; - aq.mergeSubRoots(n); - // eslint-disable-next-line no-await-in-loop - const receipt = await contract.mergeSubRoots(n, { gasLimit: 8000000 }).then((tx) => tx.wait()); - - expect(receipt).to.not.eq(null); - expect(receipt?.gasUsed.toString()).to.not.eq(""); - expect(receipt?.gasUsed.toString()).to.not.eq("0"); - } - } - - const expectedSmallSRTroot = aq.getSmallSRTroot(); - - const contractSmallSRTroot = await contract.getSmallSRTroot(); - - expect(expectedSmallSRTroot.toString()).to.eq(contractSmallSRTroot.toString()); - - aq.merge(MAIN_DEPTH); - const receipt = await contract.merge(MAIN_DEPTH, { gasLimit: 8000000 }).then((tx) => tx.wait()); - - expect(receipt).to.not.eq(null); - expect(receipt?.gasUsed.toString()).to.not.eq(""); - expect(receipt?.gasUsed.toString()).to.not.eq("0"); - - const expectedMainRoot = aq.getMainRoots()[MAIN_DEPTH]; - const contractMainRoot = await contract.getMainRoot(MAIN_DEPTH); - - expect(expectedMainRoot.toString()).to.eq(contractMainRoot.toString()); -}; - -const testOneShot = async (aq: AccQueue, contract: AccQueueContract, NUM_SUBTREES: number, MAIN_DEPTH: number) => { - await testMerge(aq, contract, NUM_SUBTREES, MAIN_DEPTH, 0); -}; - -const testMultiShot = async ( - aq: AccQueue, - contract: AccQueueContract, - NUM_SUBTREES: number, - MAIN_DEPTH: number, - NUM_MERGES: number, -) => { - await testMerge(aq, contract, NUM_SUBTREES, MAIN_DEPTH, NUM_MERGES); -}; - -describe("AccQueue gas benchmarks", () => { - describe("Binary enqueues", () => { - const SUB_DEPTH = 3; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - before(async () => { - const r = await deploy(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aqContract = r.aqContract; - }); - - it(`should enqueue to a subtree of depth ${SUB_DEPTH}`, async () => { - for (let i = 0; i < HASH_LENGTH ** SUB_DEPTH; i += 1) { - // eslint-disable-next-line no-await-in-loop - const receipt = await aqContract.enqueue(i, { gasLimit: 400000 }).then((tx) => tx.wait()); - - expect(receipt).to.not.eq(null); - expect(receipt?.gasUsed.toString()).to.not.eq(""); - expect(receipt?.gasUsed.toString()).to.not.eq("0"); - } - }); - }); - - describe("Binary fills", () => { - const SUB_DEPTH = 3; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - before(async () => { - const r = await deploy(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aqContract = r.aqContract; - }); - - it(`should fill to a subtree of depth ${SUB_DEPTH}`, async () => { - for (let i = 0; i < 2; i += 1) { - // eslint-disable-next-line no-await-in-loop - await aqContract.enqueue(i, { gasLimit: 800000 }).then((tx) => tx.wait()); - // eslint-disable-next-line no-await-in-loop - const receipt = await aqContract.fill({ gasLimit: 800000 }).then((tx) => tx.wait()); - - expect(receipt).to.not.eq(null); - expect(receipt?.gasUsed.toString()).to.not.eq(""); - expect(receipt?.gasUsed.toString()).to.not.eq("0"); - } - }); - }); - - describe("Quinary enqueues", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 5; - const ZERO = BigInt(0); - before(async () => { - const r = await deploy(AccQueueQuinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aqContract = r.aqContract; - }); - - it(`should enqueue to a subtree of depth ${SUB_DEPTH}`, async () => { - for (let i = 0; i < HASH_LENGTH ** SUB_DEPTH; i += 1) { - // eslint-disable-next-line no-await-in-loop - const receipt = await aqContract.enqueue(i, { gasLimit: 800000 }).then((tx) => tx.wait()); - - expect(receipt).to.not.eq(null); - expect(receipt?.gasUsed.toString()).to.not.eq(""); - expect(receipt?.gasUsed.toString()).to.not.eq("0"); - } - }); - }); - - describe("Quinary fills", () => { - const SUB_DEPTH = 2; - const HASH_LENGTH = 5; - const ZERO = NOTHING_UP_MY_SLEEVE; - before(async () => { - const r = await deploy(AccQueueQuinaryMaciFactory, SUB_DEPTH, HASH_LENGTH, ZERO); - aqContract = r.aqContract; - }); - - it(`should fill a subtree of depth ${SUB_DEPTH}`, async () => { - for (let i = 0; i < 2; i += 1) { - // eslint-disable-next-line no-await-in-loop - await aqContract.enqueue(i, { gasLimit: 800000 }).then((tx) => tx.wait()); - // eslint-disable-next-line no-await-in-loop - const receipt = await aqContract.fill({ gasLimit: 800000 }).then((tx) => tx.wait()); - - expect(receipt).to.not.eq(null); - expect(receipt?.gasUsed.toString()).to.not.eq(""); - expect(receipt?.gasUsed.toString()).to.not.eq("0"); - } - }); - }); - - describe("Binary AccQueue0 one-shot merge", () => { - const SUB_DEPTH = 4; - const MAIN_DEPTH = 32; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - const NUM_SUBTREES = 32; - let aq: AccQueue; - before(async () => { - const r = await deploy(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aq = r.aq; - aqContract = r.aqContract; - }); - - it(`should merge ${NUM_SUBTREES} subtrees`, async () => { - await testOneShot(aq, aqContract, NUM_SUBTREES, MAIN_DEPTH); - }); - }); - - describe("Binary AccQueue0 multi-shot merge", () => { - const SUB_DEPTH = 4; - const MAIN_DEPTH = 32; - const HASH_LENGTH = 2; - const ZERO = BigInt(0); - const NUM_SUBTREES = 32; - const NUM_MERGES = 4; - let aq: AccQueue; - before(async () => { - const r = await deploy(AccQueueBinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aq = r.aq; - aqContract = r.aqContract; - }); - - it(`should merge ${NUM_SUBTREES} subtrees in ${NUM_MERGES}`, async () => { - await testMultiShot(aq, aqContract, NUM_SUBTREES, MAIN_DEPTH, NUM_MERGES); - }); - }); - - describe("Quinary AccQueue0 one-shot merge", () => { - const SUB_DEPTH = 2; - const MAIN_DEPTH = 32; - const HASH_LENGTH = 5; - const ZERO = BigInt(0); - const NUM_SUBTREES = 25; - let aq: AccQueue; - before(async () => { - const r = await deploy(AccQueueQuinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aq = r.aq; - aqContract = r.aqContract; - }); - - it(`should merge ${NUM_SUBTREES} subtrees`, async () => { - await testOneShot(aq, aqContract, NUM_SUBTREES, MAIN_DEPTH); - }); - }); - - describe("Quinary AccQueue0 multi-shot merge", () => { - const SUB_DEPTH = 2; - const MAIN_DEPTH = 32; - const HASH_LENGTH = 5; - const ZERO = BigInt(0); - const NUM_SUBTREES = 20; - const NUM_MERGES = 4; - let aq: AccQueue; - - before(async () => { - const r = await deploy(AccQueueQuinary0Factory, SUB_DEPTH, HASH_LENGTH, ZERO); - aq = r.aq; - aqContract = r.aqContract; - }); - - it(`should merge ${NUM_SUBTREES} subtrees in ${NUM_MERGES}`, async () => { - await testMultiShot(aq, aqContract, NUM_SUBTREES, MAIN_DEPTH, NUM_MERGES); - }); - }); -}); diff --git a/contracts/tests/MACI.test.ts b/contracts/tests/MACI.test.ts index 0e175b2e51..928b12c8cb 100644 --- a/contracts/tests/MACI.test.ts +++ b/contracts/tests/MACI.test.ts @@ -14,7 +14,7 @@ import { STATE_TREE_DEPTH, duration, initialVoiceCreditBalance, - maxValues, + maxVoteOptions, messageBatchSize, treeDepths, } from "./constants"; @@ -219,6 +219,7 @@ describe("MACI", function test() { const tx = await maciContract.deployPoll( duration, treeDepths, + messageBatchSize, coordinator.pubKey.asContractParam() as { x: BigNumberish; y: BigNumberish }, verifierContract, vkRegistryContract, @@ -240,7 +241,7 @@ describe("MACI", function test() { const p = maciState.deployPoll( BigInt(deployTime + duration), - maxValues, + maxVoteOptions, treeDepths, messageBatchSize, coordinator, @@ -264,6 +265,7 @@ describe("MACI", function test() { const tx = await maciContract.deployPoll( duration, treeDepths, + messageBatchSize, coordinator.pubKey.asContractParam() as { x: BigNumberish; y: BigNumberish }, verifierContract, vkRegistryContract, @@ -282,6 +284,7 @@ describe("MACI", function test() { .deployPoll( duration, treeDepths, + messageBatchSize, users[0].pubKey.asContractParam() as { x: BigNumberish; y: BigNumberish }, verifierContract, vkRegistryContract, diff --git a/contracts/tests/MessageProcessor.test.ts b/contracts/tests/MessageProcessor.test.ts index c3e93155c9..1d4d11365f 100644 --- a/contracts/tests/MessageProcessor.test.ts +++ b/contracts/tests/MessageProcessor.test.ts @@ -13,8 +13,6 @@ import { MACI, MessageProcessor, MessageProcessor__factory as MessageProcessorFactory, - Poll as PollContract, - Poll__factory as PollFactory, Verifier, VkRegistry, } from "../typechain-types"; @@ -23,7 +21,7 @@ import { STATE_TREE_DEPTH, duration, initialVoiceCreditBalance, - maxValues, + maxVoteOptions, messageBatchSize, testProcessVk, testTallyVk, @@ -34,7 +32,6 @@ import { timeTravel, deployTestContracts } from "./utils"; describe("MessageProcessor", () => { // contracts let maciContract: MACI; - let pollContract: PollContract; let verifierContract: Verifier; let vkRegistryContract: VkRegistry; let mpContract: MessageProcessor; @@ -64,6 +61,7 @@ describe("MessageProcessor", () => { const tx = await maciContract.deployPoll( duration, treeDepths, + messageBatchSize, coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, @@ -90,24 +88,29 @@ describe("MessageProcessor", () => { }; pollId = event.args._pollId; - const pollContractAddress = await maciContract.getPoll(pollId); - pollContract = PollFactory.connect(pollContractAddress, signer); - mpContract = MessageProcessorFactory.connect(event.args.pollAddr.messageProcessor, signer); const block = await signer.provider!.getBlock(receipt!.blockHash); const deployTime = block!.timestamp; // deploy local poll - const p = maciState.deployPoll(BigInt(deployTime + duration), maxValues, treeDepths, messageBatchSize, coordinator); + const p = maciState.deployPoll( + BigInt(deployTime + duration), + maxVoteOptions, + treeDepths, + messageBatchSize, + coordinator, + ); expect(p.toString()).to.eq(pollId.toString()); - // publish the NOTHING_UP_MY_SLEEVE message - const messageData = [NOTHING_UP_MY_SLEEVE]; - for (let i = 1; i < 10; i += 1) { - messageData.push(BigInt(0)); + const messages = []; + for (let i = 0; i <= 24; i += 1) { + const messageData = [NOTHING_UP_MY_SLEEVE]; + for (let j = 1; j < 10; j += 1) { + messageData.push(BigInt(0)); + } + messages.push(new Message(messageData)); } - const message = new Message(messageData); const padKey = new PubKey([ BigInt("10457101036533406547632367118273992217979173478358440826365724437999023779287"), BigInt("19824078218392094440610104313265183977899662750282163392862422243483260492317"), @@ -115,7 +118,9 @@ describe("MessageProcessor", () => { poll = maciState.polls.get(pollId)!; - poll.publishMessage(message, padKey); + for (let i = 0; i <= 24; i += 1) { + poll.publishMessage(messages[i], padKey); + } // update the poll state poll.updatePoll(BigInt(maciState.stateLeaves.length)); @@ -126,7 +131,6 @@ describe("MessageProcessor", () => { vkRegistryContract.setVerifyingKeys( STATE_TREE_DEPTH, treeDepths.intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.QV, @@ -138,40 +142,19 @@ describe("MessageProcessor", () => { expect(receipt?.status).to.eq(1); }); - describe("before merging acc queues", () => { + describe("testing with more messages", () => { before(async () => { await timeTravel(signer.provider! as unknown as EthereumProvider, duration + 1); }); - it("processMessages() should fail if the state AQ has not been merged", async () => { - await expect(mpContract.processMessages(0, [0, 0, 0, 0, 0, 0, 0, 0])).to.be.revertedWithCustomError( - mpContract, - "StateNotMerged", - ); - }); - }); - - describe("after merging acc queues", () => { - before(async () => { - await pollContract.mergeMaciState(); - - await pollContract.mergeMessageAqSubRoots(0); - await pollContract.mergeMessageAq(); - }); - it("genProcessMessagesPackedVals() should generate the correct value", async () => { - const packedVals = packProcessMessageSmallVals( - BigInt(maxValues.maxVoteOptions), - BigInt(users.length), - 0, - poll.messages.length, - ); + const packedVals = packProcessMessageSmallVals(BigInt(maxVoteOptions), BigInt(users.length), 0, messageBatchSize); const onChainPackedVals = BigInt( await mpContract.genProcessMessagesPackedVals( 0, users.length, poll.messages.length, - treeDepths.messageTreeSubDepth, + messageBatchSize, treeDepths.voteOptionTreeDepth, ), ); diff --git a/contracts/tests/Poll.test.ts b/contracts/tests/Poll.test.ts index 006def07e2..c0fabce1bc 100644 --- a/contracts/tests/Poll.test.ts +++ b/contracts/tests/Poll.test.ts @@ -8,22 +8,13 @@ import { Keypair, Message, PCommand, PubKey } from "maci-domainobjs"; import { EMode } from "../ts/constants"; import { getDefaultSigner } from "../ts/utils"; -import { - AccQueue, - AccQueueQuinaryMaci__factory as AccQueueQuinaryMaciFactory, - Poll__factory as PollFactory, - MACI, - Poll as PollContract, - Verifier, - VkRegistry, -} from "../typechain-types"; +import { Poll__factory as PollFactory, MACI, Poll as PollContract, Verifier, VkRegistry } from "../typechain-types"; import { - MESSAGE_TREE_DEPTH, STATE_TREE_DEPTH, duration, initialVoiceCreditBalance, - maxValues, + maxVoteOptions, messageBatchSize, treeDepths, } from "./constants"; @@ -55,6 +46,7 @@ describe("Poll", () => { const tx = await maciContract.deployPoll( duration, treeDepths, + messageBatchSize, coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, @@ -82,7 +74,7 @@ describe("Poll", () => { // deploy local poll const p = maciState.deployPoll( BigInt(deployTime + duration), - maxValues, + maxVoteOptions, treeDepths, messageBatchSize, coordinator, @@ -120,17 +112,14 @@ describe("Poll", () => { }); it("should have the correct max values set", async () => { - const mv = await pollContract.maxValues(); - expect(mv[0].toString()).to.eq(maxValues.maxMessages.toString()); - expect(mv[1].toString()).to.eq(maxValues.maxVoteOptions.toString()); + const mvo = await pollContract.maxVoteOptions(); + expect(mvo.toString()).to.eq(maxVoteOptions.toString()); }); it("should have the correct tree depths set", async () => { const td = await pollContract.treeDepths(); expect(td[0].toString()).to.eq(treeDepths.intStateTreeDepth.toString()); - expect(td[1].toString()).to.eq(treeDepths.messageTreeSubDepth.toString()); - expect(td[2].toString()).to.eq(treeDepths.messageTreeDepth.toString()); - expect(td[3].toString()).to.eq(treeDepths.voteOptionTreeDepth.toString()); + expect(td[1].toString()).to.eq(treeDepths.voteOptionTreeDepth.toString()); }); it("should have numMessages set to 1 (blank message)", async () => { @@ -147,6 +136,7 @@ describe("Poll", () => { testMaciContract.deployPoll( duration, treeDepths, + messageBatchSize, { x: "100", y: "1", @@ -264,54 +254,21 @@ describe("Poll", () => { }); }); - describe("Merge messages", () => { - let messageAqContract: AccQueue; - - beforeEach(async () => { - const extContracts = await pollContract.extContracts(); - - const messageAqAddress = extContracts.messageAq; - messageAqContract = AccQueueQuinaryMaciFactory.connect(messageAqAddress, signer); - }); + describe("Message hash chain", () => { + it("should correctly compute chain hash and batch hashes array", async () => { + const currentChainHash = await pollContract.chainHash(); + const currentBatchHashes = await pollContract.getBatchHashes(); - it("should allow to merge the message AccQueue", async () => { - let tx = await pollContract.mergeMessageAqSubRoots(0, { - gasLimit: 3000000, - }); - let receipt = await tx.wait(); - expect(receipt?.status).to.eq(1); - - tx = await pollContract.mergeMessageAq({ gasLimit: 4000000 }); - receipt = await tx.wait(); - expect(receipt?.status).to.eq(1); + expect(currentChainHash).to.eq(maciState.polls.get(pollId)?.chainHash); + expect(currentBatchHashes).to.deep.equal(maciState.polls.get(pollId)?.batchHashes); }); - it("should have the correct message root set", async () => { - const onChainMessageRoot = await messageAqContract.getMainRoot(MESSAGE_TREE_DEPTH); - const offChainMessageRoot = maciState.polls.get(pollId)!.messageTree.root; - - expect(onChainMessageRoot.toString()).to.eq(offChainMessageRoot.toString()); - }); - - it("should prevent merging subroots again", async () => { - await expect(pollContract.mergeMessageAqSubRoots(0)).to.be.revertedWithCustomError( - messageAqContract, - "SubTreesAlreadyMerged", - ); - }); - - it("should not change the message root if merging a second time", async () => { - await pollContract.mergeMessageAq(); - const onChainMessageRoot = await messageAqContract.getMainRoot(MESSAGE_TREE_DEPTH); - const offChainMessageRoot = maciState.polls.get(pollId)!.messageTree.root; - - expect(onChainMessageRoot.toString()).to.eq(offChainMessageRoot.toString()); - }); + it("should correctly pad batch hash array with zeros", async () => { + await pollContract.padLastBatch(); + maciState.polls.get(pollId)?.padLastBatch(); - it("should emit an event with the same root when merging another time", async () => { - expect(await pollContract.mergeMessageAq()) - .to.emit(pollContract, "MergeMessageAq") - .withArgs(maciState.polls.get(pollId)!.messageTree.root); + expect(await pollContract.chainHash()).to.eq(maciState.polls.get(pollId)?.chainHash); + expect(await pollContract.getBatchHashes()).to.deep.eq(maciState.polls.get(pollId)?.batchHashes); }); }); }); diff --git a/contracts/tests/PollFactory.test.ts b/contracts/tests/PollFactory.test.ts index fac8b1939f..4faf55f01d 100644 --- a/contracts/tests/PollFactory.test.ts +++ b/contracts/tests/PollFactory.test.ts @@ -5,7 +5,7 @@ import { Keypair } from "maci-domainobjs"; import { deployPollFactory, getDefaultSigner } from "../ts"; import { PollFactory } from "../typechain-types"; -import { maxValues, treeDepths } from "./constants"; +import { messageBatchSize, maxVoteOptions, treeDepths } from "./constants"; describe("pollFactory", () => { let pollFactory: PollFactory; @@ -22,8 +22,9 @@ describe("pollFactory", () => { it("should allow anyone to deploy a new poll", async () => { const tx = await pollFactory.deploy( "100", - maxValues, + maxVoteOptions, treeDepths, + messageBatchSize, coordinatorPubKey.asContractParam(), ZeroAddress, ); @@ -31,19 +32,18 @@ describe("pollFactory", () => { expect(receipt?.status).to.eq(1); }); - it("should revert when called with an invalid param for max values", async () => { + it("should revert when called with an invalid param for max vote options", async () => { + const maxVoteOptionsInvalid = 2 ** 50; await expect( pollFactory.deploy( "100", - { - maxMessages: maxValues.maxMessages, - maxVoteOptions: 2 ** 50, - }, + maxVoteOptionsInvalid, treeDepths, + messageBatchSize, coordinatorPubKey.asContractParam(), ZeroAddress, ), - ).to.be.revertedWithCustomError(pollFactory, "InvalidMaxValues"); + ).to.be.revertedWithCustomError(pollFactory, "InvalidMaxVoteOptions"); }); }); }); diff --git a/contracts/tests/Tally.test.ts b/contracts/tests/Tally.test.ts index 37ce7d6007..217d40789f 100644 --- a/contracts/tests/Tally.test.ts +++ b/contracts/tests/Tally.test.ts @@ -31,7 +31,7 @@ import { STATE_TREE_DEPTH, duration, initialVoiceCreditBalance, - maxValues, + maxVoteOptions, messageBatchSize, tallyBatchSize, testProcessVk, @@ -75,6 +75,7 @@ describe("TallyVotes", () => { const tx = await maciContract.deployPoll( duration, treeDepths, + messageBatchSize, coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, @@ -112,7 +113,13 @@ describe("TallyVotes", () => { tallyContract = TallyFactory.connect(event.args.pollAddr.tally, signer); // deploy local poll - const p = maciState.deployPoll(BigInt(deployTime + duration), maxValues, treeDepths, messageBatchSize, coordinator); + const p = maciState.deployPoll( + BigInt(deployTime + duration), + maxVoteOptions, + treeDepths, + messageBatchSize, + coordinator, + ); expect(p.toString()).to.eq(pollId.toString()); // publish the NOTHING_UP_MY_SLEEVE message const messageData = [NOTHING_UP_MY_SLEEVE]; @@ -140,7 +147,6 @@ describe("TallyVotes", () => { await vkRegistryContract.setVerifyingKeys( STATE_TREE_DEPTH, treeDepths.intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.QV, @@ -180,13 +186,11 @@ describe("TallyVotes", () => { ); }); - describe("after merging acc queues", () => { + describe("after messages processing", () => { let tallyGeneratedInputs: ITallyCircuitInputs; before(async () => { await pollContract.mergeMaciState(); - await pollContract.mergeMessageAqSubRoots(0); - await pollContract.mergeMessageAq(); tallyGeneratedInputs = poll.tallyVotes(); }); @@ -256,6 +260,7 @@ describe("TallyVotes", () => { ...treeDepths, intStateTreeDepth, }, + messageBatchSize, coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, @@ -295,7 +300,7 @@ describe("TallyVotes", () => { // deploy local poll const p = maciState.deployPoll( BigInt(deployTime + updatedDuration), - maxValues, + maxVoteOptions, { ...treeDepths, intStateTreeDepth, @@ -327,7 +332,6 @@ describe("TallyVotes", () => { await vkRegistryContract.setVerifyingKeys( STATE_TREE_DEPTH, intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.QV, @@ -339,9 +343,6 @@ describe("TallyVotes", () => { await timeTravel(signer.provider! as unknown as EthereumProvider, updatedDuration); await pollContract.mergeMaciState(); - await pollContract.mergeMessageAqSubRoots(0); - await pollContract.mergeMessageAq(); - const processMessagesInputs = poll.processMessages(pollId); await mpContract.processMessages(processMessagesInputs.newSbCommitment, [0, 0, 0, 0, 0, 0, 0, 0]); }); @@ -401,6 +402,7 @@ describe("TallyVotes", () => { ...treeDepths, intStateTreeDepth, }, + messageBatchSize, coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, @@ -440,7 +442,7 @@ describe("TallyVotes", () => { // deploy local poll const p = maciState.deployPoll( BigInt(deployTime + updatedDuration), - maxValues, + maxVoteOptions, { ...treeDepths, intStateTreeDepth, @@ -472,7 +474,6 @@ describe("TallyVotes", () => { await vkRegistryContract.setVerifyingKeys( STATE_TREE_DEPTH, intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.QV, @@ -484,9 +485,6 @@ describe("TallyVotes", () => { await timeTravel(signer.provider! as unknown as EthereumProvider, updatedDuration); await pollContract.mergeMaciState(); - await pollContract.mergeMessageAqSubRoots(0); - await pollContract.mergeMessageAq(); - const processMessagesInputs = poll.processMessages(pollId); await mpContract.processMessages(processMessagesInputs.newSbCommitment, [0, 0, 0, 0, 0, 0, 0, 0]); }); diff --git a/contracts/tests/TallyNonQv.test.ts b/contracts/tests/TallyNonQv.test.ts index 8a9e6d2a49..c43686ccb5 100644 --- a/contracts/tests/TallyNonQv.test.ts +++ b/contracts/tests/TallyNonQv.test.ts @@ -30,7 +30,7 @@ import { import { STATE_TREE_DEPTH, duration, - maxValues, + maxVoteOptions, messageBatchSize, tallyBatchSize, testProcessVk, @@ -74,6 +74,7 @@ describe("TallyVotesNonQv", () => { const tx = await maciContract.deployPoll( duration, treeDepths, + messageBatchSize, coordinator.pubKey.asContractParam(), verifierContract, vkRegistryContract, @@ -111,7 +112,13 @@ describe("TallyVotesNonQv", () => { tallyContract = TallyFactory.connect(event.args.pollAddr.tally, signer); // deploy local poll - const p = maciState.deployPoll(BigInt(deployTime + duration), maxValues, treeDepths, messageBatchSize, coordinator); + const p = maciState.deployPoll( + BigInt(deployTime + duration), + maxVoteOptions, + treeDepths, + messageBatchSize, + coordinator, + ); expect(p.toString()).to.eq(pollId.toString()); // publish the NOTHING_UP_MY_SLEEVE message const messageData = [NOTHING_UP_MY_SLEEVE]; @@ -139,7 +146,6 @@ describe("TallyVotesNonQv", () => { await vkRegistryContract.setVerifyingKeys( STATE_TREE_DEPTH, treeDepths.intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.NON_QV, @@ -179,13 +185,11 @@ describe("TallyVotesNonQv", () => { ); }); - describe("after merging acc queues", () => { + describe("after messages processing", () => { let tallyGeneratedInputs: ITallyCircuitInputs; before(async () => { await pollContract.mergeMaciState(); - await pollContract.mergeMessageAqSubRoots(0); - await pollContract.mergeMessageAq(); tallyGeneratedInputs = poll.tallyVotes(); }); diff --git a/contracts/tests/VkRegistry.test.ts b/contracts/tests/VkRegistry.test.ts index ddb95923dc..8babb62b61 100644 --- a/contracts/tests/VkRegistry.test.ts +++ b/contracts/tests/VkRegistry.test.ts @@ -36,7 +36,6 @@ describe("VkRegistry", () => { const tx = await vkRegistryContract.setVerifyingKeys( stateTreeDepth, treeDepths.intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.QV, @@ -53,7 +52,6 @@ describe("VkRegistry", () => { vkRegistryContract.setVerifyingKeys( stateTreeDepth, treeDepths.intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.QV, @@ -68,7 +66,6 @@ describe("VkRegistry", () => { const tx = await vkRegistryContract.setVerifyingKeys( stateTreeDepth + 1, treeDepths.intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.QV, @@ -84,7 +81,6 @@ describe("VkRegistry", () => { const tx = await vkRegistryContract.setVerifyingKeys( stateTreeDepth + 1, treeDepths.intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.NON_QV, @@ -102,7 +98,6 @@ describe("VkRegistry", () => { const tx = await vkRegistryContract.setVerifyingKeysBatch( stateTreeDepth, treeDepths.intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, [EMode.NON_QV], @@ -120,7 +115,6 @@ describe("VkRegistry", () => { vkRegistryContract.setVerifyingKeysBatch( stateTreeDepth, treeDepths.intStateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, [EMode.QV], @@ -141,7 +135,6 @@ describe("VkRegistry", () => { expect( await vkRegistryContract.hasProcessVk( stateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.QV, @@ -153,7 +146,6 @@ describe("VkRegistry", () => { expect( await vkRegistryContract.hasProcessVk( stateTreeDepth + 2, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, EMode.QV, @@ -192,7 +184,6 @@ describe("VkRegistry", () => { it("should generate a valid signature", async () => { const sig = await vkRegistryContract.genProcessVkSig( stateTreeDepth, - treeDepths.messageTreeDepth, treeDepths.voteOptionTreeDepth, messageBatchSize, ); diff --git a/contracts/tests/constants.ts b/contracts/tests/constants.ts index 76960a201e..3758375f44 100644 --- a/contracts/tests/constants.ts +++ b/contracts/tests/constants.ts @@ -1,4 +1,4 @@ -import { MaxValues, TreeDepths, STATE_TREE_ARITY, MESSAGE_TREE_ARITY } from "maci-core"; +import { TreeDepths, STATE_TREE_ARITY } from "maci-core"; import { G1Point, G2Point } from "maci-crypto"; import { VerifyingKey } from "maci-domainobjs"; @@ -7,7 +7,7 @@ export const duration = 2_000; export const STATE_TREE_DEPTH = 10; export const MESSAGE_TREE_DEPTH = 2; export const MESSAGE_TREE_SUBDEPTH = 1; -export const messageBatchSize = MESSAGE_TREE_ARITY ** MESSAGE_TREE_SUBDEPTH; +export const messageBatchSize = 20; export const testProcessVk = new VerifyingKey( new G1Point(BigInt(0), BigInt(1)), @@ -42,15 +42,10 @@ export const testTallyVkNonQv = new VerifyingKey( ); export const initialVoiceCreditBalance = 100; -export const maxValues: MaxValues = { - maxMessages: MESSAGE_TREE_ARITY ** MESSAGE_TREE_DEPTH, - maxVoteOptions: 25, -}; +export const maxVoteOptions = 25; export const treeDepths: TreeDepths = { intStateTreeDepth: 1, - messageTreeDepth: MESSAGE_TREE_DEPTH, - messageTreeSubDepth: MESSAGE_TREE_SUBDEPTH, voteOptionTreeDepth: 2, }; diff --git a/contracts/tests/utils.ts b/contracts/tests/utils.ts index 1e8ef27d75..402d29e2cb 100644 --- a/contracts/tests/utils.ts +++ b/contracts/tests/utils.ts @@ -1,31 +1,19 @@ /* eslint-disable import/no-extraneous-dependencies */ import { expect } from "chai"; -import { BaseContract, Signer } from "ethers"; -import { IncrementalQuinTree, AccQueue, calcDepthFromNumLeaves, hash2, hash5 } from "maci-crypto"; +import { Signer } from "ethers"; import { IVkContractParams, VerifyingKey } from "maci-domainobjs"; import type { EthereumProvider } from "hardhat/types"; -import { linkPoseidonLibraries } from "../tasks/helpers/abi"; -import { getDefaultSigner } from "../ts"; import { deployConstantInitialVoiceCreditProxy, deployFreeForAllSignUpGatekeeper, deployMaci, deployMockVerifier, - deployPoseidonContracts, deployVkRegistry, - createContractFactory, } from "../ts/deploy"; import { IDeployedTestContracts } from "../ts/types"; -import { - AccQueueBinary0__factory as AccQueueBinary0Factory, - AccQueueBinaryMaci__factory as AccQueueBinaryMaciFactory, - AccQueue as AccQueueContract, - AccQueueQuinary0__factory as AccQueueQuinary0Factory, - AccQueueQuinaryMaci__factory as AccQueueQuinaryMaciFactory, - FreeForAllGatekeeper, -} from "../typechain-types"; +import { FreeForAllGatekeeper } from "../typechain-types"; export const insertSubTreeGasLimit = { gasLimit: 300000 }; export const enqueueGasLimit = { gasLimit: 500000 }; @@ -68,427 +56,6 @@ export const compareVks = (vk: VerifyingKey, vkOnChain: IVkContractParams): void expect(vk.gamma2.y[1].toString()).to.eq(vkOnChain.gamma2.y[1].toString()); }; -/** - * Deploy an AccQueue contract and setup a local TS instance of an AccQueue class - * @param contractName - the name of the contract to deploy - * @param SUB_DEPTH - the depth of the subtrees - * @param HASH_LENGTH - the number of leaves in each subtree - * @param ZERO - the zero value to be used as leaf - * @returns the AccQueue class instance and the AccQueue contract - */ -export const deployTestAccQueues = async ( - factory: - | typeof AccQueueBinary0Factory - | typeof AccQueueQuinary0Factory - | typeof AccQueueQuinaryMaciFactory - | typeof AccQueueBinaryMaciFactory, - SUB_DEPTH: number, - HASH_LENGTH: number, - ZERO: bigint, -): Promise<{ aq: AccQueue; aqContract: BaseContract }> => { - const { PoseidonT3Contract, PoseidonT4Contract, PoseidonT5Contract, PoseidonT6Contract } = - await deployPoseidonContracts(await getDefaultSigner(), {}, true); - - const [poseidonT3ContractAddress, poseidonT4ContractAddress, poseidonT5ContractAddress, poseidonT6ContractAddress] = - await Promise.all([ - PoseidonT3Contract.getAddress(), - PoseidonT4Contract.getAddress(), - PoseidonT5Contract.getAddress(), - PoseidonT6Contract.getAddress(), - ]); - // Link Poseidon contracts - const accQueueFactory = await createContractFactory( - factory.abi, - factory.linkBytecode( - linkPoseidonLibraries( - poseidonT3ContractAddress, - poseidonT4ContractAddress, - poseidonT5ContractAddress, - poseidonT6ContractAddress, - ), - ), - await getDefaultSigner(), - ); - - const aqContract = await accQueueFactory.deploy(SUB_DEPTH); - - await aqContract.deploymentTransaction()?.wait(); - - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - - return { aq, aqContract }; -}; - -/** - * Test whether fill() works for an empty subtree - * @param aq - the AccQueue class instance - * @param aqContract - the AccQueue contract - * @param index - the index of the subtree - */ -export const testEmptySubtree = async (aq: AccQueue, aqContract: AccQueueContract, index: number): Promise => { - aq.fill(); - const tx = await aqContract.fill(fillGasLimit); - await tx.wait(); - const subRoot = await aqContract.getSubRoot(index); - expect(subRoot.toString()).to.equal(aq.getSubRoot(index).toString()); -}; - -/** - * Insert one leaf and compute the subroot - * @param aq - the AccQueue class instance - * @param aqContract - the AccQueue contract - */ -export const testIncompleteSubtree = async (aq: AccQueue, aqContract: AccQueueContract): Promise => { - const leaf = BigInt(1); - - aq.enqueue(leaf); - await aqContract.enqueue(leaf.toString(), enqueueGasLimit).then((tx) => tx.wait()); - - aq.fill(); - await aqContract.fill(fillGasLimit).then((tx) => tx.wait()); - - const subRoot = await aqContract.getSubRoot(1); - expect(subRoot.toString()).to.equal(aq.getSubRoot(1).toString()); -}; - -/** - * Test whether fill() works for every number of leaves in an incomplete subtree - * @param aq - the AccQueue class instance - * @param aqContract - the AccQueue contract - * @param HASH_LENGTH - the number of leaves in each subtree - */ -export const testFillForAllIncompletes = async ( - aq: AccQueue, - aqContract: AccQueueContract, - HASH_LENGTH: number, -): Promise => { - for (let i = 0; i < HASH_LENGTH; i += 1) { - for (let j = 0; j < i; j += 1) { - const leaf = BigInt(i + 1); - aq.enqueue(leaf); - // eslint-disable-next-line no-await-in-loop - await aqContract.enqueue(leaf.toString(), enqueueGasLimit).then((tx) => tx.wait()); - } - aq.fill(); - // eslint-disable-next-line no-await-in-loop - await aqContract.fill(fillGasLimit).then((tx) => tx.wait()); - - // eslint-disable-next-line no-await-in-loop - const subRoot = await aqContract.getSubRoot(3 + i); - expect(subRoot.toString()).to.equal(aq.getSubRoot(3 + i).toString()); - } -}; - -/** - * Test whether the AccQueue is empty upon deployment - * @param aqContract - the AccQueue contract - */ -export const testEmptyUponDeployment = async (aqContract: AccQueueContract): Promise => { - const numLeaves = await aqContract.numLeaves(); - expect(numLeaves.toString()).to.equal("0"); - - await expect(aqContract.getSubRoot(0)).to.be.revertedWithCustomError(aqContract, "InvalidIndex"); -}; - -/** - * Enqueue leaves and check their subroots - * @param aqContract - the AccQueue contract - * @param HASH_LENGTH - the number of leaves in each subtree - * @param SUB_DEPTH - the depth of the subtrees - * @param ZERO - the zero value to be used as leaf - */ -export const testEnqueue = async ( - aqContract: AccQueueContract, - HASH_LENGTH: number, - SUB_DEPTH: number, - ZERO: bigint, -): Promise => { - const hashFunc = HASH_LENGTH === 5 ? hash5 : hash2; - const tree0 = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, hashFunc); - const subtreeCapacity = HASH_LENGTH ** SUB_DEPTH; - - // Insert up to a subtree - for (let i = 0; i < subtreeCapacity; i += 1) { - const leaf = BigInt(i + 1); - tree0.insert(leaf); - - // eslint-disable-next-line no-await-in-loop - await aqContract.enqueue(leaf.toString(), enqueueGasLimit).then((tx) => tx.wait()); - } - - let numLeaves = await aqContract.numLeaves(); - expect(numLeaves.toString()).to.eq(subtreeCapacity.toString()); - - const r = await aqContract.getSubRoot(0); - expect(r.toString()).to.eq(tree0.root.toString()); - - const tree1 = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, hashFunc); - - // Insert the other subtree - for (let i = 0; i < subtreeCapacity; i += 1) { - const leaf = BigInt(i + 2); - tree1.insert(leaf); - - // eslint-disable-next-line no-await-in-loop - await aqContract.enqueue(leaf.toString(), enqueueGasLimit).then((tx) => tx.wait()); - } - - numLeaves = await aqContract.numLeaves(); - expect(numLeaves.toString()).to.eq((subtreeCapacity * 2).toString()); - - const subroot1 = await aqContract.getSubRoot(1); - expect(subroot1.toString()).to.eq(tree1.root.toString()); -}; - -/** - * Insert subtrees directly - * @param aq - the AccQueue class instance - * @param aqContract - the AccQueue contract - * @param NUM_SUBTREES - the number of subtrees to insert - */ -export const testInsertSubTrees = async ( - aq: AccQueue, - aqContract: AccQueueContract, - NUM_SUBTREES: number, - MAIN_DEPTH: number, -): Promise => { - const leaves: bigint[] = []; - for (let i = 0; i < NUM_SUBTREES; i += 1) { - const subTree = new IncrementalQuinTree(aq.getSubDepth(), aq.getZeros()[0], aq.getHashLength(), aq.hashFunc); - const leaf = BigInt(i); - subTree.insert(leaf); - leaves.push(leaf); - - // insert the subtree root - aq.insertSubTree(subTree.root); - // eslint-disable-next-line no-await-in-loop - await aqContract.insertSubTree(subTree.root.toString(), insertSubTreeGasLimit).then((tx) => tx.wait()); - } - - let correctRoot: string; - if (NUM_SUBTREES === 1) { - correctRoot = aq.getSubRoots()[0].toString(); - } else { - const depth = calcDepthFromNumLeaves(aq.getHashLength(), aq.getSubRoots().length); - const tree = new IncrementalQuinTree(depth, aq.getZeros()[aq.getSubDepth()], aq.getHashLength(), aq.hashFunc); - - aq.getSubRoots().forEach((subRoot) => { - tree.insert(subRoot); - }); - - correctRoot = tree.root.toString(); - } - - // Check whether mergeSubRoots() works - aq.mergeSubRoots(0); - await aqContract.mergeSubRoots(0, { gasLimit: 8000000 }).then((tx) => tx.wait()); - - const expectedSmallSRTroot = aq.getSmallSRTroot().toString(); - - expect(correctRoot).to.eq(expectedSmallSRTroot); - - const contractSmallSRTroot = await aqContract.getSmallSRTroot(); - expect(expectedSmallSRTroot.toString()).to.eq(contractSmallSRTroot.toString()); - - // Check whether merge() works - aq.merge(MAIN_DEPTH); - await aqContract.merge(MAIN_DEPTH, { gasLimit: 8000000 }).then((tx) => tx.wait()); - - const expectedMainRoot = aq.getMainRoots()[MAIN_DEPTH]; - const contractMainRoot = await aqContract.getMainRoot(MAIN_DEPTH); - - expect(expectedMainRoot.toString()).to.eq(contractMainRoot.toString()); -}; - -/** - * The order of leaves when using enqueue() and insertSubTree() should be correct. - * @param aq - the AccQueue class instance - * @param aqContract - the AccQueue contract - */ -export const testEnqueueAndInsertSubTree = async (aq: AccQueue, aqContract: AccQueueContract): Promise => { - const [z] = aq.getZeros(); - const n = BigInt(1); - - const leaves: bigint[] = []; - - const subTree = new IncrementalQuinTree(aq.getSubDepth(), z, aq.getHashLength(), aq.hashFunc); - - for (let i = 0; i < aq.getHashLength() ** aq.getSubDepth(); i += 1) { - leaves.push(z); - } - - leaves.push(n); - // leaves is now [z, z, z, z..., n] - - const depth = calcDepthFromNumLeaves(aq.getHashLength(), leaves.length); - const tree = new IncrementalQuinTree(depth, z, aq.getHashLength(), aq.hashFunc); - - leaves.forEach((leaf) => { - tree.insert(leaf); - }); - - const expectedRoot = tree.root.toString(); - - aq.enqueue(n); - await aqContract.enqueue(n.toString(), enqueueGasLimit).then((tx) => tx.wait()); - - aq.insertSubTree(subTree.root); - await aqContract.insertSubTree(subTree.root.toString(), insertSubTreeGasLimit).then((tx) => tx.wait()); - - aq.fill(); - await aqContract.fill(fillGasLimit).then((tx) => tx.wait()); - - aq.mergeSubRoots(0); - await aqContract.mergeSubRoots(0, { gasLimit: 8000000 }).then((tx) => tx.wait()); - - expect(expectedRoot).to.eq(aq.getSmallSRTroot().toString()); - - const contractSmallSRTroot = await aqContract.getSmallSRTroot(); - expect(expectedRoot).to.eq(contractSmallSRTroot.toString()); -}; - -/** - * Insert a number of subtrees and merge them all into a main tree - * @param aq - the AccQueue class instance - * @param aqContract - the AccQueue contract - */ -export const testMerge = async ( - aq: AccQueue, - aqContract: AccQueueContract, - NUM_SUBTREES: number, - MAIN_DEPTH: number, -): Promise => { - // The raw leaves of the main tree - const leaves: bigint[] = []; - for (let i = 0; i < NUM_SUBTREES; i += 1) { - const leaf = BigInt(i); - - aq.enqueue(leaf); - aq.fill(); - // eslint-disable-next-line no-await-in-loop - await aqContract.enqueue(leaf.toString(), enqueueGasLimit).then((tx) => tx.wait()); - // eslint-disable-next-line no-await-in-loop - await aqContract.fill(fillGasLimit).then((tx) => tx.wait()); - - leaves.push(leaf); - - for (let j = 1; j < aq.getHashLength() ** aq.getSubDepth(); j += 1) { - leaves.push(aq.getZeros()[0]); - } - } - - // Insert leaves into a main tree - const tree = new IncrementalQuinTree(MAIN_DEPTH, aq.getZeros()[0], aq.getHashLength(), aq.hashFunc); - - leaves.forEach((leaf) => { - tree.insert(leaf); - }); - - // minHeight should be the small SRT height - const minHeight = await aqContract.calcMinHeight(); - const c = calcDepthFromNumLeaves(aq.getHashLength(), NUM_SUBTREES); - expect(minHeight.toString()).to.eq(c.toString()); - - // Check whether mergeSubRoots() works - aq.mergeSubRoots(0); - await (await aqContract.mergeSubRoots(0, { gasLimit: 8000000 })).wait(); - - const expectedSmallSRTroot = aq.getSmallSRTroot().toString(); - const contractSmallSRTroot = (await aqContract.getSmallSRTroot()).toString(); - - expect(expectedSmallSRTroot).to.eq(contractSmallSRTroot); - - if (NUM_SUBTREES === 1) { - expect(expectedSmallSRTroot).to.eq(aq.getSubRoots()[0].toString()); - } else { - // Check whether the small SRT root is correct - const srtHeight = calcDepthFromNumLeaves(aq.getHashLength(), NUM_SUBTREES); - const smallTree = new IncrementalQuinTree( - srtHeight, - aq.getZeros()[aq.getSubDepth()], - aq.getHashLength(), - aq.hashFunc, - ); - - aq.getSubRoots().forEach((subRoot) => { - smallTree.insert(subRoot); - }); - - expect(expectedSmallSRTroot).to.eq(smallTree.root.toString()); - } - - // Check whether mergeDirect() works - const aq2 = aq.copy(); - - aq2.mergeDirect(MAIN_DEPTH); - const directlyMergedRoot = aq2.getMainRoots()[MAIN_DEPTH].toString(); - expect(directlyMergedRoot.toString()).to.eq(tree.root.toString()); - - // Check whether off-chain merge() works - aq.merge(MAIN_DEPTH); - - const expectedMainRoot = aq.getMainRoots()[MAIN_DEPTH].toString(); - - expect(expectedMainRoot).to.eq(directlyMergedRoot); - - // Check whether on-chain merge() works - await (await aqContract.merge(MAIN_DEPTH, { gasLimit: 8000000 })).wait(); - const contractMainRoot = (await aqContract.getMainRoot(MAIN_DEPTH)).toString(); - expect(expectedMainRoot).to.eq(contractMainRoot); -}; - -/** - * Enqueue, merge, enqueue, and merge again - * @param aq - the AccQueue class instance - * @param aqContract - the AccQueue contract - */ -export const testMergeAgain = async (aq: AccQueue, aqContract: AccQueueContract, MAIN_DEPTH: number): Promise => { - const tree = new IncrementalQuinTree(MAIN_DEPTH, aq.getZeros()[0], aq.getHashLength(), aq.hashFunc); - const leaf = BigInt(123); - - // Enqueue - aq.enqueue(leaf); - await aqContract.enqueue(leaf.toString()).then((tx) => tx.wait()); - tree.insert(leaf); - - // Merge - aq.mergeDirect(MAIN_DEPTH); - await aqContract.mergeSubRoots(0, { gasLimit: 8000000 }).then((tx) => tx.wait()); - await aqContract.merge(MAIN_DEPTH, { gasLimit: 8000000 }).then((tx) => tx.wait()); - - for (let i = 1; i < aq.getHashLength() ** aq.getSubDepth(); i += 1) { - tree.insert(aq.getZeros()[0]); - } - - const mainRoot = (await aqContract.getMainRoot(MAIN_DEPTH)).toString(); - const expectedMainRoot = aq.getMainRoots()[MAIN_DEPTH].toString(); - expect(expectedMainRoot).to.eq(mainRoot); - expect(expectedMainRoot).to.eq(tree.root.toString()); - - const leaf2 = BigInt(456); - - // Enqueue - aq.enqueue(leaf2); - await aqContract.enqueue(leaf2.toString()).then((tx) => tx.wait()); - tree.insert(leaf2); - - // Merge - aq.mergeDirect(MAIN_DEPTH); - await aqContract.mergeSubRoots(0, { gasLimit: 8000000 }).then((tx) => tx.wait()); - await aqContract.merge(MAIN_DEPTH, { gasLimit: 8000000 }).then((tx) => tx.wait()); - - for (let i = 1; i < aq.getHashLength() ** aq.getSubDepth(); i += 1) { - tree.insert(aq.getZeros()[0]); - } - - const mainRoot2 = (await aqContract.getMainRoot(MAIN_DEPTH)).toString(); - const expectedMainRoot2 = aq.getMainRoots()[MAIN_DEPTH].toString(); - expect(expectedMainRoot2).to.eq(tree.root.toString()); - - expect(expectedMainRoot2).not.to.eq(expectedMainRoot); - expect(expectedMainRoot2).to.eq(mainRoot2); -}; - /** * Deploy a set of smart contracts that can be used for testing. * @param initialVoiceCreditBalance - the initial voice credit balance for each user diff --git a/contracts/ts/genMaciState.ts b/contracts/ts/genMaciState.ts index c6cf76d728..815da2f05a 100644 --- a/contracts/ts/genMaciState.ts +++ b/contracts/ts/genMaciState.ts @@ -1,6 +1,6 @@ /* eslint-disable no-underscore-dangle */ import { type Provider } from "ethers"; -import { MaciState, MESSAGE_TREE_ARITY, STATE_TREE_ARITY } from "maci-core"; +import { MaciState } from "maci-core"; import { type Keypair, PubKey, Message } from "maci-domainobjs"; import assert from "assert"; @@ -113,47 +113,33 @@ export const genMaciStateFromContract = async ( const pollContractAddress = pollContractAddresses.get(pollId)!; const pollContract = PollFactory.connect(pollContractAddress, provider); - const [coordinatorPubKeyOnChain, [deployTime, duration], onChainMaxValues, onChainTreeDepths] = await Promise.all([ - pollContract.coordinatorPubKey(), - pollContract.getDeployTimeAndDuration().then((values) => values.map(Number)), - pollContract.maxValues(), - pollContract.treeDepths(), - ]); + const [coordinatorPubKeyOnChain, [deployTime, duration], onChainMaxVoteOptions, onChainTreeDepths, msgBatchSize] = + await Promise.all([ + pollContract.coordinatorPubKey(), + pollContract.getDeployTimeAndDuration().then((values) => values.map(Number)), + pollContract.maxVoteOptions(), + pollContract.treeDepths(), + pollContract.messageBatchSize(), + ]); assert(coordinatorPubKeyOnChain[0].toString() === coordinatorKeypair.pubKey.rawPubKey[0].toString()); assert(coordinatorPubKeyOnChain[1].toString() === coordinatorKeypair.pubKey.rawPubKey[1].toString()); - const maxValues = { - maxMessages: Number(onChainMaxValues.maxMessages), - maxVoteOptions: Number(onChainMaxValues.maxVoteOptions), - }; + const maxVoteOptions = Number(onChainMaxVoteOptions); const treeDepths = { intStateTreeDepth: Number(onChainTreeDepths.intStateTreeDepth), - messageTreeDepth: Number(onChainTreeDepths.messageTreeDepth), - messageTreeSubDepth: Number(onChainTreeDepths.messageTreeSubDepth), voteOptionTreeDepth: Number(onChainTreeDepths.voteOptionTreeDepth), }; - const batchSizes = { - tallyBatchSize: STATE_TREE_ARITY ** Number(onChainTreeDepths.intStateTreeDepth), - messageBatchSize: MESSAGE_TREE_ARITY ** Number(onChainTreeDepths.messageTreeSubDepth), - }; + const messageBatchSize = Number(msgBatchSize); // fetch poll contract logs for (let i = fromBlock; i <= lastBlock; i += blocksPerRequest + 1) { const toBlock = i + blocksPerRequest >= lastBlock ? lastBlock : i + blocksPerRequest; - const [ - publishMessageLogs, - mergeMessageAqSubRootsLogs, - mergeMessageAqLogs, - // eslint-disable-next-line no-await-in-loop - ] = await Promise.all([ - pollContract.queryFilter(pollContract.filters.PublishMessage(), i, toBlock), - pollContract.queryFilter(pollContract.filters.MergeMessageAqSubRoots(), i, toBlock), - pollContract.queryFilter(pollContract.filters.MergeMessageAq(), i, toBlock), - ]); + // eslint-disable-next-line no-await-in-loop + const publishMessageLogs = await pollContract.queryFilter(pollContract.filters.PublishMessage(), i, toBlock); publishMessageLogs.forEach((event) => { assert(!!event); @@ -173,32 +159,6 @@ export const genMaciStateFromContract = async ( }); }); - mergeMessageAqSubRootsLogs.forEach((event) => { - assert(!!event); - - const numSrQueueOps = Number(event.args._numSrQueueOps); - actions.push({ - type: "MergeMessageAqSubRoots", - blockNumber: event.blockNumber, - transactionIndex: event.transactionIndex, - data: { - numSrQueueOps, - }, - }); - }); - - mergeMessageAqLogs.forEach((event) => { - assert(!!event); - - const messageRoot = BigInt((event.args as unknown as { _messageRoot: string })._messageRoot); - actions.push({ - type: "MergeMessageAq", - blockNumber: event.blockNumber, - transactionIndex: event.transactionIndex, - data: { messageRoot }, - }); - }); - if (sleepAmount) { // eslint-disable-next-line no-await-in-loop await sleep(sleepAmount); @@ -218,9 +178,9 @@ export const genMaciStateFromContract = async ( case action.type === "DeployPoll" && action.data.pollId?.toString() === pollId.toString(): { maciState.deployPoll( BigInt(deployTime + duration), - maxValues, + maxVoteOptions, treeDepths, - batchSizes.messageBatchSize, + messageBatchSize, coordinatorKeypair, ); break; @@ -237,12 +197,6 @@ export const genMaciStateFromContract = async ( break; } - // ensure that the message root is correct (i.e. all messages have been published offchain) - case action.type === "MergeMessageAq": { - assert(maciState.polls.get(pollId)?.messageTree.root.toString() === action.data.messageRoot?.toString()); - break; - } - default: break; } diff --git a/coordinator/.env.example b/coordinator/.env.example index 5b9172a83c..78e4007300 100644 --- a/coordinator/.env.example +++ b/coordinator/.env.example @@ -13,7 +13,7 @@ COORDINATOR_TALLY_ZKEY_NAME=TallyVotes_10-1-2_test # Make sure you have zkeys folder # https://maci.pse.dev/docs/trusted-setup -COORDINATOR_MESSAGE_PROCESS_ZKEY_NAME=ProcessMessages_10-2-1-2_test +COORDINATOR_MESSAGE_PROCESS_ZKEY_NAME=ProcessMessages_10-20-2_test # Rapidsnark executable path COORDINATOR_RAPIDSNARK_EXE= diff --git a/coordinator/tests/app.test.ts b/coordinator/tests/app.test.ts index 473d454e7f..e893e80846 100644 --- a/coordinator/tests/app.test.ts +++ b/coordinator/tests/app.test.ts @@ -12,7 +12,6 @@ import { signup, publish, timeTravel, - mergeMessages, mergeSignups, } from "maci-cli"; import { Keypair } from "maci-domainobjs"; @@ -30,9 +29,8 @@ import { FileModule } from "../ts/file/file.module"; const STATE_TREE_DEPTH = 10; const INT_STATE_TREE_DEPTH = 1; -const MSG_TREE_DEPTH = 2; const VOTE_OPTION_TREE_DEPTH = 2; -const MSG_BATCH_DEPTH = 1; +const MESSAGE_BATCH_SIZE = 20; describe("AppController (e2e)", () => { const coordinatorKeypair = new Keypair(); @@ -60,12 +58,11 @@ describe("AppController (e2e)", () => { quiet: true, stateTreeDepth: STATE_TREE_DEPTH, intStateTreeDepth: INT_STATE_TREE_DEPTH, - messageTreeDepth: MSG_TREE_DEPTH, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, - messageBatchDepth: MSG_BATCH_DEPTH, + messageBatchSize: MESSAGE_BATCH_SIZE, processMessagesZkeyPathNonQv: path.resolve( __dirname, - "../zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test.0.zkey", + "../zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test.0.zkey", ), tallyVotesZkeyPathNonQv: path.resolve( __dirname, @@ -80,8 +77,7 @@ describe("AppController (e2e)", () => { pollContracts = await deployPoll({ pollDuration: 30, intStateTreeDepth: INT_STATE_TREE_DEPTH, - messageTreeSubDepth: MSG_BATCH_DEPTH, - messageTreeDepth: MSG_TREE_DEPTH, + messageBatchSize: MESSAGE_BATCH_SIZE, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, coordinatorPubkey: coordinatorKeypair.pubKey.serialize(), useQuadraticVoting: false, @@ -290,35 +286,10 @@ describe("AppController (e2e)", () => { }); }); - test("should throw an error if messages are not merged", async () => { + test("should throw an error if coordinator key decryption is failed", async () => { await timeTravel({ seconds: 30, signer }); await mergeSignups({ pollId: 0n, signer }); - const publicKey = await fs.promises.readFile(process.env.COORDINATOR_PUBLIC_KEY_PATH!); - const encryptedCoordinatorPrivateKey = cryptoService.encrypt(publicKey, coordinatorKeypair.privKey.serialize()); - const encryptedHeader = await getAuthorizationHeader(); - - const result = await request(app.getHttpServer() as App) - .post("/v1/proof/generate") - .set("Authorization", encryptedHeader) - .send({ - poll: 0, - encryptedCoordinatorPrivateKey, - maciContractAddress: maciAddresses.maciAddress, - tallyContractAddress: pollContracts.tally, - useQuadraticVoting: false, - }) - .expect(400); - - expect(result.body).toStrictEqual({ - statusCode: HttpStatus.BAD_REQUEST, - message: ErrorCodes.NOT_MERGED_MESSAGE_TREE, - }); - }); - - test("should throw an error if coordinator key decryption is failed", async () => { - await mergeMessages({ pollId: 0n, signer }); - const encryptedHeader = await getAuthorizationHeader(); const result = await request(app.getHttpServer() as App) diff --git a/coordinator/ts/proof/__tests__/proof.service.test.ts b/coordinator/ts/proof/__tests__/proof.service.test.ts index 2a0bd95e93..a4e24ca866 100644 --- a/coordinator/ts/proof/__tests__/proof.service.test.ts +++ b/coordinator/ts/proof/__tests__/proof.service.test.ts @@ -129,14 +129,6 @@ describe("ProofGeneratorService", () => { await expect(service.generate(defaultArgs)).rejects.toThrow(ErrorCodes.PRIVATE_KEY_MISMATCH); }); - test("should throw error if there is no any poll", async () => { - mockContract.getMainRoot.mockResolvedValue(0n); - - const service = new ProofGeneratorService(defaultCryptoService, fileService); - - await expect(service.generate(defaultArgs)).rejects.toThrow(ErrorCodes.NOT_MERGED_MESSAGE_TREE); - }); - test("should throw error if poll is not found", async () => { const service = new ProofGeneratorService(defaultCryptoService, fileService); diff --git a/coordinator/ts/proof/proof.service.ts b/coordinator/ts/proof/proof.service.ts index a8d249ec53..ff8eb30558 100644 --- a/coordinator/ts/proof/proof.service.ts +++ b/coordinator/ts/proof/proof.service.ts @@ -1,7 +1,7 @@ import { Logger, Injectable } from "@nestjs/common"; import { ZeroAddress } from "ethers"; import hre from "hardhat"; -import { Deployment, EContracts, ProofGenerator, type Poll, type MACI, type AccQueue } from "maci-contracts"; +import { Deployment, EContracts, ProofGenerator, type Poll, type MACI } from "maci-contracts"; import { Keypair, PrivKey, PubKey } from "maci-domainobjs"; import path from "path"; @@ -70,14 +70,7 @@ export class ProofGeneratorService { } const pollContract = await this.deployment.getContract({ name: EContracts.Poll, address: pollAddress }); - const [{ messageAq: messageAqAddress }, coordinatorPublicKey] = await Promise.all([ - pollContract.extContracts(), - pollContract.coordinatorPubKey(), - ]); - const messageAq = await this.deployment.getContract({ - name: EContracts.AccQueue, - address: messageAqAddress, - }); + const [coordinatorPublicKey] = await Promise.all([pollContract.coordinatorPubKey()]); const isStateAqMerged = await pollContract.stateMerged(); @@ -86,15 +79,6 @@ export class ProofGeneratorService { throw new Error(ErrorCodes.NOT_MERGED_STATE_TREE); } - const messageTreeDepth = await pollContract.treeDepths().then((depths) => Number(depths[2])); - - const mainRoot = await messageAq.getMainRoot(messageTreeDepth.toString()); - - if (mainRoot.toString() === "0") { - this.logger.error(`Error: ${ErrorCodes.NOT_MERGED_MESSAGE_TREE}, message tree is not merged`); - throw new Error(ErrorCodes.NOT_MERGED_MESSAGE_TREE); - } - const { privateKey } = await this.fileService.getPrivateKey(); const maciPrivateKey = PrivKey.deserialize(this.cryptoService.decrypt(privateKey, encryptedCoordinatorPrivateKey)); const coordinatorKeypair = new Keypair(maciPrivateKey); @@ -111,7 +95,6 @@ export class ProofGeneratorService { const maciState = await ProofGenerator.prepareState({ maciContract, pollContract, - messageAq, maciPrivateKey, coordinatorKeypair, pollId: poll, diff --git a/core/ts/MaciState.ts b/core/ts/MaciState.ts index 62b9bbd7ec..e62e840656 100644 --- a/core/ts/MaciState.ts +++ b/core/ts/MaciState.ts @@ -1,6 +1,6 @@ import { type PubKey, type Keypair, StateLeaf, blankStateLeaf } from "maci-domainobjs"; -import type { IJsonMaciState, IJsonPoll, IMaciState, MaxValues, TreeDepths } from "./utils/types"; +import type { IJsonMaciState, IJsonPoll, IMaciState, TreeDepths } from "./utils/types"; import { Poll } from "./Poll"; import { STATE_TREE_ARITY } from "./utils/constants"; @@ -56,7 +56,7 @@ export class MaciState implements IMaciState { /** * Deploy a new poll with the given parameters. * @param pollEndTimestamp - The Unix timestamp at which the poll ends. - * @param maxValues - The maximum number of values for each vote option. + * @param maxVoteOptions - The maximum number of vote option. * @param treeDepths - The depths of the tree. * @param messageBatchSize - The batch size for processing messages. * @param coordinatorKeypair - The keypair of the MACI round coordinator. @@ -64,7 +64,7 @@ export class MaciState implements IMaciState { */ deployPoll( pollEndTimestamp: bigint, - maxValues: MaxValues, + maxVoteOptions: number, treeDepths: TreeDepths, messageBatchSize: number, coordinatorKeypair: Keypair, @@ -77,7 +77,7 @@ export class MaciState implements IMaciState { messageBatchSize, tallyBatchSize: STATE_TREE_ARITY ** treeDepths.intStateTreeDepth, }, - maxValues, + maxVoteOptions, this, ); diff --git a/core/ts/Poll.ts b/core/ts/Poll.ts index 330a24f448..9b94e5c55d 100644 --- a/core/ts/Poll.ts +++ b/core/ts/Poll.ts @@ -31,7 +31,6 @@ import type { MaciState } from "./MaciState"; import type { CircuitInputs, TreeDepths, - MaxValues, BatchSizes, IPoll, IJsonPoll, @@ -41,7 +40,7 @@ import type { } from "./utils/types"; import type { PathElements } from "maci-crypto"; -import { STATE_TREE_ARITY, MESSAGE_TREE_ARITY } from "./utils/constants"; +import { STATE_TREE_ARITY, VOTE_OPTION_TREE_ARITY } from "./utils/constants"; import { ProcessMessageErrors, ProcessMessageError } from "./utils/errors"; import { packTallyVotesSmallVals } from "./utils/utils"; @@ -57,7 +56,7 @@ export class Poll implements IPoll { batchSizes: BatchSizes; - maxValues: MaxValues; + maxVoteOptions: number; // the depth of the state tree stateTreeDepth: number; @@ -73,8 +72,6 @@ export class Poll implements IPoll { messages: Message[] = []; - messageTree: IncrementalQuinTree; - commands: PCommand[] = []; encPubKeys: PubKey[] = []; @@ -88,7 +85,7 @@ export class Poll implements IPoll { // For message processing numBatchesProcessed = 0; - currentMessageBatchIndex?: number; + currentMessageBatchIndex: number; maciStateRef: MaciState; @@ -117,6 +114,12 @@ export class Poll implements IPoll { emptyBallotHash?: bigint; + // message chain hash + chainHash = NOTHING_UP_MY_SLEEVE; + + // batch chain hashes + batchHashes = [NOTHING_UP_MY_SLEEVE]; + // how many users signed up private numSignups = 0n; @@ -126,7 +129,7 @@ export class Poll implements IPoll { * @param coordinatorKeypair - The keypair of the coordinator. * @param treeDepths - The depths of the trees used in the poll. * @param batchSizes - The sizes of the batches used in the poll. - * @param maxValues - The maximum values the MACI circuits can accept. + * @param maxVoteOptions - The maximum vote options the MACI circuits can accept. * @param maciStateRef - The reference to the MACI state. */ constructor( @@ -134,31 +137,25 @@ export class Poll implements IPoll { coordinatorKeypair: Keypair, treeDepths: TreeDepths, batchSizes: BatchSizes, - maxValues: MaxValues, + maxVoteOptions: number, maciStateRef: MaciState, ) { this.pollEndTimestamp = pollEndTimestamp; this.coordinatorKeypair = coordinatorKeypair; this.treeDepths = treeDepths; this.batchSizes = batchSizes; - this.maxValues = maxValues; + this.maxVoteOptions = maxVoteOptions; this.maciStateRef = maciStateRef; this.pollId = BigInt(maciStateRef.polls.size); this.stateTreeDepth = maciStateRef.stateTreeDepth; this.actualStateTreeDepth = maciStateRef.stateTreeDepth; + this.currentMessageBatchIndex = 0; - this.messageTree = new IncrementalQuinTree( - this.treeDepths.messageTreeDepth, - NOTHING_UP_MY_SLEEVE, - MESSAGE_TREE_ARITY, - hash5, - ); - - this.tallyResult = new Array(this.maxValues.maxVoteOptions).fill(0n) as bigint[]; - this.perVOSpentVoiceCredits = new Array(this.maxValues.maxVoteOptions).fill(0n) as bigint[]; + this.tallyResult = new Array(this.maxVoteOptions).fill(0n) as bigint[]; + this.perVOSpentVoiceCredits = new Array(this.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); + this.emptyBallot = Ballot.genBlankBallot(this.maxVoteOptions, treeDepths.voteOptionTreeDepth); this.ballots.push(this.emptyBallot); } @@ -169,8 +166,8 @@ export class Poll implements IPoll { */ updatePoll = (numSignups: bigint): void => { // there might be occasions where we fetch logs after new signups have been made - // logs are fetched (and MaciState/Poll created locally) after stateAq have been - // merged in. If someone signs up after that and we fetch that record + // logs are fetched (and MaciState/Poll created locally). + // If someone signs up after that and we fetch that record // then we won't be able to verify the processing on chain as the data will // not match. For this, we must only copy up to the number of signups @@ -245,7 +242,7 @@ export class Poll implements IPoll { } // If the vote option index is invalid, do nothing - if (command.voteOptionIndex < 0n || command.voteOptionIndex >= BigInt(this.maxValues.maxVoteOptions)) { + if (command.voteOptionIndex < 0n || command.voteOptionIndex >= BigInt(this.maxVoteOptions)) { throw new ProcessMessageError(ProcessMessageErrors.InvalidVoteOptionIndex); } @@ -294,7 +291,7 @@ export class Poll implements IPoll { const originalBallotPathElements = this.ballotTree?.genProof(Number(stateLeafIndex)).pathElements; // create a new quinary tree where we insert the votes of the origin (up until this message is processed) ballot - const vt = new IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, MESSAGE_TREE_ARITY, hash5); + const vt = new IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, VOTE_OPTION_TREE_ARITY, hash5); for (let i = 0; i < this.ballots[0].votes.length; i += 1) { vt.insert(ballot.votes[i]); } @@ -344,7 +341,9 @@ export class Poll implements IPoll { // store the message locally this.messages.push(message); // add the message hash to the message tree - this.messageTree.insert(message.hash(encPubKey)); + const messageHash = message.hash(encPubKey); + // update chain hash + this.updateChainHash(messageHash); // Decrypt the message and store the Command // step 1. we generate the shared key @@ -362,6 +361,28 @@ export class Poll implements IPoll { } }; + /** + * Updates message chain hash + * @param messageHash hash of message with encPubKey + */ + updateChainHash = (messageHash: bigint): void => { + this.chainHash = hash2([this.chainHash, messageHash]); + + if (this.messages.length % this.batchSizes.messageBatchSize === 0) { + this.batchHashes.push(this.chainHash); + this.currentMessageBatchIndex += 1; + } + }; + + /** + * Pad last unclosed batch + */ + padLastBatch = (): void => { + if (this.messages.length % this.batchSizes.messageBatchSize !== 0) { + this.batchHashes.push(this.chainHash); + } + }; + /** * This method checks if there are any unprocessed messages in the Poll instance. * @returns Returns true if the number of processed batches is @@ -399,45 +420,30 @@ export class Poll implements IPoll { const batchSize = this.batchSizes.messageBatchSize; if (this.numBatchesProcessed === 0) { - // The starting index of the batch of messages to process. - // Note that we process messages in reverse order. - // e.g if there are 8 messages and the batch size is 5, then - // the starting index should be 5. - assert( - this.currentMessageBatchIndex === undefined, - "The current message batch index should not be defined if this is the first batch", - ); // Prevent other polls from being processed until this poll has // been fully processed this.maciStateRef.pollBeingProcessed = true; this.maciStateRef.currentPollBeingProcessed = pollId; - } - - // Only allow one poll to be processed at a time - if (this.maciStateRef.pollBeingProcessed) { - assert(this.maciStateRef.currentPollBeingProcessed === pollId, "Another poll is currently being processed"); - } - if (this.numBatchesProcessed === 0) { - const r = this.messages.length % batchSize; + this.padLastBatch(); - this.currentMessageBatchIndex = this.messages.length; + this.currentMessageBatchIndex = this.batchHashes.length; // if there are messages if (this.currentMessageBatchIndex > 0) { - if (r === 0) { - this.currentMessageBatchIndex -= batchSize; - } else { - this.currentMessageBatchIndex -= r; - } + this.currentMessageBatchIndex -= 1; } this.sbSalts[this.currentMessageBatchIndex] = 0n; } + // Only allow one poll to be processed at a time + if (this.maciStateRef.pollBeingProcessed) { + assert(this.maciStateRef.currentPollBeingProcessed === pollId, "Another poll is currently being processed"); + } + // The starting index must be valid - assert(this.currentMessageBatchIndex! >= 0, "The starting index must be >= 0"); - assert(this.currentMessageBatchIndex! % batchSize === 0, "The starting index must be a multiple of the batch size"); + assert(this.currentMessageBatchIndex >= 0, "The starting index must be >= 0"); // ensure we copy the state from MACI when we start processing the // first batch @@ -447,7 +453,7 @@ export class Poll implements IPoll { // Generate circuit inputs const circuitInputs = stringifyBigInts( - this.genProcessMessagesCircuitInputsPartial(this.currentMessageBatchIndex!), + this.genProcessMessagesCircuitInputsPartial(this.currentMessageBatchIndex), ) as CircuitInputs; // we want to store the state leaves at this point in time @@ -468,7 +474,7 @@ export class Poll implements IPoll { // loop through the batch of messages for (let i = 0; i < batchSize; i += 1) { // we process the messages in reverse order - const idx = this.currentMessageBatchIndex! + batchSize - i - 1; + const idx = this.currentMessageBatchIndex * batchSize - i - 1; assert(idx >= 0, "The message index must be >= 0"); let message: Message; let encPubKey: PubKey; @@ -541,11 +547,16 @@ export class Poll implements IPoll { // this might be unnecessary but we do it to prevent a possible DoS attack // from voters who could potentially encrypt a message in such as way that // when decrypted it results in a valid state leaf index but an invalid vote option index - if (command.voteOptionIndex < this.maxValues.maxVoteOptions) { + if (command.voteOptionIndex < this.maxVoteOptions) { currentVoteWeights.unshift(ballot.votes[Number(command.voteOptionIndex)]); // create a new quinary tree and add all votes we have so far - const vt = new IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, MESSAGE_TREE_ARITY, hash5); + const vt = new IncrementalQuinTree( + this.treeDepths.voteOptionTreeDepth, + 0n, + VOTE_OPTION_TREE_ARITY, + hash5, + ); // fill the vote option tree with the votes we have so far for (let j = 0; j < this.ballots[0].votes.length; j += 1) { @@ -558,7 +569,12 @@ export class Poll implements IPoll { currentVoteWeights.unshift(ballot.votes[0]); // create a new quinary tree and add all votes we have so far - const vt = new IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, MESSAGE_TREE_ARITY, hash5); + const vt = new IncrementalQuinTree( + this.treeDepths.voteOptionTreeDepth, + 0n, + VOTE_OPTION_TREE_ARITY, + hash5, + ); // fill the vote option tree with the votes we have so far for (let j = 0; j < this.ballots[0].votes.length; j += 1) { @@ -579,7 +595,12 @@ export class Poll implements IPoll { currentVoteWeights.unshift(this.ballots[0].votes[0]); // create a new quinary tree and add an empty vote - const vt = new IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, MESSAGE_TREE_ARITY, hash5); + const vt = new IncrementalQuinTree( + this.treeDepths.voteOptionTreeDepth, + 0n, + VOTE_OPTION_TREE_ARITY, + hash5, + ); vt.insert(this.ballots[0].votes[0]); // get the path elements for this empty vote weight leaf currentVoteWeightsPathElements.unshift(vt.genProof(0).pathElements); @@ -600,7 +621,7 @@ export class Poll implements IPoll { currentVoteWeights.unshift(this.ballots[0].votes[0]); // create a new quinary tree and add an empty vote - const vt = new IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, MESSAGE_TREE_ARITY, hash5); + const vt = new IncrementalQuinTree(this.treeDepths.voteOptionTreeDepth, 0n, VOTE_OPTION_TREE_ARITY, hash5); vt.insert(this.ballots[0].votes[0]); // get the path elements for this empty vote weight leaf @@ -627,16 +648,16 @@ export class Poll implements IPoll { // record that we processed one batch this.numBatchesProcessed += 1; - if (this.currentMessageBatchIndex! > 0) { - this.currentMessageBatchIndex! -= batchSize; + if (this.currentMessageBatchIndex > 0) { + this.currentMessageBatchIndex -= 1; } // ensure newSbSalt differs from currentSbSalt let newSbSalt = genRandomSalt(); - while (this.sbSalts[this.currentMessageBatchIndex!] === newSbSalt) { + while (this.sbSalts[this.currentMessageBatchIndex] === newSbSalt) { newSbSalt = genRandomSalt(); } - this.sbSalts[this.currentMessageBatchIndex!] = newSbSalt; + this.sbSalts[this.currentMessageBatchIndex] = newSbSalt; // store the salt in the circuit inputs circuitInputs.newSbSalt = newSbSalt; @@ -654,10 +675,9 @@ export class Poll implements IPoll { circuitInputs.inputHash = sha256Hash([ circuitInputs.packedVals as bigint, coordPubKeyHash, - circuitInputs.msgRoot as bigint, + circuitInputs.outputBatchHash as bigint, circuitInputs.currentSbCommitment as bigint, circuitInputs.newSbCommitment, - this.pollEndTimestamp, BigInt(this.actualStateTreeDepth), ]); @@ -681,7 +701,6 @@ export class Poll implements IPoll { const { messageBatchSize } = this.batchSizes; assert(index <= this.messages.length, "The index must be <= the number of messages"); - assert(index % messageBatchSize === 0, "The index must be a multiple of the message batch size"); // fill the msgs array with a copy of the messages we have // plus empty messages to fill the batch @@ -707,26 +726,16 @@ export class Poll implements IPoll { while (msgs.length % messageBatchSize > 0) { msgs.push(msg.asCircuitInputs()); } - // we only take the messages we need for this batch - msgs = msgs.slice(index, index + messageBatchSize); - - // insert zero value in the message tree as padding - while (this.messageTree.nextIndex < index + messageBatchSize) { - this.messageTree.insert(this.messageTree.zeroValue); - } - - // generate the path to the subroot of the message tree for this batch - const messageSubrootPath = this.messageTree.genSubrootProof(index, index + messageBatchSize); - - // verify it - assert(this.messageTree.verifyProof(messageSubrootPath), "The message subroot path is invalid"); + // it slice msgs array from index of first message in current batch to + // index of last message in current batch + msgs = msgs.slice((index - 1) * messageBatchSize, index * messageBatchSize); // validate that the batch index is correct, if not fix it // this means that the end will be the last message - let batchEndIndex = index + messageBatchSize; + let batchEndIndex = index * messageBatchSize; if (batchEndIndex > this.messages.length) { - batchEndIndex = this.messages.length; + batchEndIndex = this.messages.length - (index - 1) * messageBatchSize; } // copy the public keys, pad the array with the last keys if needed @@ -735,11 +744,11 @@ export class Poll implements IPoll { // pad with the public key used to encrypt the message with state index 0 (padding) encPubKeys.push(key.pubKey.copy()); } + // then take the ones part of this batch - encPubKeys = encPubKeys.slice(index, index + messageBatchSize); + encPubKeys = encPubKeys.slice((index - 1) * messageBatchSize, index * messageBatchSize); // cache tree roots - const msgRoot = this.messageTree.root; const currentStateRoot = this.stateTree!.root; const currentBallotRoot = this.ballotTree!.root; // calculate the current state and ballot root @@ -748,31 +757,33 @@ export class Poll implements IPoll { const currentSbCommitment = hash3([ currentStateRoot, currentBallotRoot, - this.sbSalts[this.currentMessageBatchIndex!], + this.sbSalts[this.currentMessageBatchIndex], ]); + const inputBatchHash = this.batchHashes[index - 1]; + const outputBatchHash = this.batchHashes[index]; + // Generate a SHA256 hash of inputs which the contract provides /* eslint-disable no-bitwise */ const packedVals = - BigInt(this.maxValues.maxVoteOptions) + + BigInt(this.maxVoteOptions) + (BigInt(this.numSignups) << 50n) + - (BigInt(index) << 100n) + + (BigInt(0) << 100n) + (BigInt(batchEndIndex) << 150n); /* eslint-enable no-bitwise */ return stringifyBigInts({ - pollEndTimestamp: this.pollEndTimestamp, packedVals, - msgRoot, + inputBatchHash, + outputBatchHash, msgs, - msgSubrootPathElements: messageSubrootPath.pathElements, coordPrivKey: this.coordinatorKeypair.privKey.asCircuitInputs(), coordPubKey: this.coordinatorKeypair.pubKey.asCircuitInputs(), encPubKeys: encPubKeys.map((x) => x.asCircuitInputs()), currentStateRoot, currentBallotRoot, currentSbCommitment, - currentSbSalt: this.sbSalts[this.currentMessageBatchIndex!], + currentSbSalt: this.sbSalts[this.currentMessageBatchIndex], }) as CircuitInputs; }; @@ -806,7 +817,7 @@ export class Poll implements IPoll { */ tallyVotes = (): ITallyCircuitInputs => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (this.sbSalts[this.currentMessageBatchIndex!] === undefined) { + if (this.sbSalts[this.currentMessageBatchIndex] === undefined) { throw new Error("You must process the messages first"); } @@ -879,7 +890,7 @@ export class Poll implements IPoll { ballots.push(this.ballots[i]); // for each possible vote option we loop and calculate - for (let j = 0; j < this.maxValues.maxVoteOptions; j += 1) { + for (let j = 0; j < this.maxVoteOptions; j += 1) { const v = this.ballots[i].votes[j]; // the vote itself will be a quadratic vote (sqrt(voiceCredits)) @@ -893,7 +904,7 @@ export class Poll implements IPoll { } } - const emptyBallot = new Ballot(this.maxValues.maxVoteOptions, this.treeDepths.voteOptionTreeDepth); + const emptyBallot = new Ballot(this.maxVoteOptions, this.treeDepths.voteOptionTreeDepth); // pad the ballots array while (ballots.length < batchSize) { @@ -941,7 +952,7 @@ export class Poll implements IPoll { // cache vars const stateRoot = this.stateTree!.root; const ballotRoot = this.ballotTree!.root; - const sbSalt = this.sbSalts[this.currentMessageBatchIndex!]; + const sbSalt = this.sbSalts[this.currentMessageBatchIndex]; const sbCommitment = hash3([stateRoot, ballotRoot, sbSalt]); const packedVals = packTallyVotesSmallVals(batchStartIndex, batchSize, Number(this.numSignups)); @@ -981,7 +992,7 @@ export class Poll implements IPoll { tallyVotesNonQv = (): ITallyCircuitInputs => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (this.sbSalts[this.currentMessageBatchIndex!] === undefined) { + if (this.sbSalts[this.currentMessageBatchIndex] === undefined) { throw new Error("You must process the messages first"); } @@ -1036,7 +1047,7 @@ export class Poll implements IPoll { ballots.push(this.ballots[i]); // for each possible vote option we loop and calculate - for (let j = 0; j < this.maxValues.maxVoteOptions; j += 1) { + for (let j = 0; j < this.maxVoteOptions; j += 1) { const v = this.ballots[i].votes[j]; this.tallyResult[j] += v; @@ -1046,7 +1057,7 @@ export class Poll implements IPoll { } } - const emptyBallot = new Ballot(this.maxValues.maxVoteOptions, this.treeDepths.voteOptionTreeDepth); + const emptyBallot = new Ballot(this.maxVoteOptions, this.treeDepths.voteOptionTreeDepth); // pad the ballots array while (ballots.length < batchSize) { @@ -1081,7 +1092,7 @@ export class Poll implements IPoll { // cache vars const stateRoot = this.stateTree!.root; const ballotRoot = this.ballotTree!.root; - const sbSalt = this.sbSalts[this.currentMessageBatchIndex!]; + const sbSalt = this.sbSalts[this.currentMessageBatchIndex]; const sbCommitment = hash3([stateRoot, ballotRoot, sbSalt]); const packedVals = packTallyVotesSmallVals(batchStartIndex, batchSize, Number(this.numSignups)); @@ -1185,18 +1196,13 @@ export class Poll implements IPoll { this.coordinatorKeypair.copy(), { intStateTreeDepth: Number(this.treeDepths.intStateTreeDepth), - messageTreeDepth: Number(this.treeDepths.messageTreeDepth), - messageTreeSubDepth: Number(this.treeDepths.messageTreeSubDepth), voteOptionTreeDepth: Number(this.treeDepths.voteOptionTreeDepth), }, { tallyBatchSize: Number(this.batchSizes.tallyBatchSize.toString()), messageBatchSize: Number(this.batchSizes.messageBatchSize.toString()), }, - { - maxMessages: Number(this.maxValues.maxMessages.toString()), - maxVoteOptions: Number(this.maxValues.maxVoteOptions.toString()), - }, + this.maxVoteOptions, this.maciStateRef, ); @@ -1212,7 +1218,6 @@ export class Poll implements IPoll { copied.currentMessageBatchIndex = this.currentMessageBatchIndex; copied.maciStateRef = this.maciStateRef; - copied.messageTree = this.messageTree.copy(); copied.tallyResult = this.tallyResult.map((x: bigint) => BigInt(x.toString())); copied.perVOSpentVoiceCredits = this.perVOSpentVoiceCredits.map((x: bigint) => BigInt(x.toString())); @@ -1257,13 +1262,10 @@ export class Poll implements IPoll { const result = this.coordinatorKeypair.equals(p.coordinatorKeypair) && this.treeDepths.intStateTreeDepth === p.treeDepths.intStateTreeDepth && - this.treeDepths.messageTreeDepth === p.treeDepths.messageTreeDepth && - this.treeDepths.messageTreeSubDepth === p.treeDepths.messageTreeSubDepth && this.treeDepths.voteOptionTreeDepth === p.treeDepths.voteOptionTreeDepth && this.batchSizes.tallyBatchSize === p.batchSizes.tallyBatchSize && this.batchSizes.messageBatchSize === p.batchSizes.messageBatchSize && - this.maxValues.maxMessages === p.maxValues.maxMessages && - this.maxValues.maxVoteOptions === p.maxValues.maxVoteOptions && + this.maxVoteOptions === p.maxVoteOptions && this.messages.length === p.messages.length && this.encPubKeys.length === p.encPubKeys.length && this.numSignups === p.numSignups; @@ -1294,16 +1296,18 @@ export class Poll implements IPoll { pollEndTimestamp: this.pollEndTimestamp.toString(), treeDepths: this.treeDepths, batchSizes: this.batchSizes, - maxValues: this.maxValues, + maxVoteOptions: this.maxVoteOptions, messages: this.messages.map((message) => message.toJSON()), commands: this.commands.map((command) => command.toJSON()), ballots: this.ballots.map((ballot) => ballot.toJSON()), encPubKeys: this.encPubKeys.map((encPubKey) => encPubKey.serialize()), - currentMessageBatchIndex: this.currentMessageBatchIndex!, + currentMessageBatchIndex: this.currentMessageBatchIndex, stateLeaves: this.stateLeaves.map((leaf) => leaf.toJSON()), results: this.tallyResult.map((result) => result.toString()), numBatchesProcessed: this.numBatchesProcessed, numSignups: this.numSignups.toString(), + chainHash: this.chainHash.toString(), + batchHashes: this.batchHashes.map((batchHash) => batchHash.toString()), }; } @@ -1319,7 +1323,7 @@ export class Poll implements IPoll { new Keypair(), json.treeDepths, json.batchSizes, - json.maxValues, + json.maxVoteOptions, maciState, ); @@ -1331,12 +1335,8 @@ export class Poll implements IPoll { poll.tallyResult = json.results.map((result: string) => BigInt(result)); poll.currentMessageBatchIndex = json.currentMessageBatchIndex; poll.numBatchesProcessed = json.numBatchesProcessed; - - // fill the trees - for (let i = 0; i < poll.messages.length; i += 1) { - const messageLeaf = poll.messages[i].hash(poll.encPubKeys[i]); - poll.messageTree.insert(messageLeaf); - } + poll.chainHash = BigInt(json.chainHash); + poll.batchHashes = json.batchHashes.map((batchHash: string) => BigInt(batchHash)); // copy maci state poll.updatePoll(BigInt(json.numSignups)); diff --git a/core/ts/__benchmarks__/index.ts b/core/ts/__benchmarks__/index.ts index a53a6d7a1a..bd1c9bc4fe 100644 --- a/core/ts/__benchmarks__/index.ts +++ b/core/ts/__benchmarks__/index.ts @@ -35,7 +35,7 @@ export default function runCore(): void { const pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + DURATION), - MAX_VALUES, + MAX_VALUES.maxVoteOptions, TREE_DEPTHS, MESSAGE_BATCH_SIZE, COORDINATOR_KEYPAIR, diff --git a/core/ts/__benchmarks__/utils/constants.ts b/core/ts/__benchmarks__/utils/constants.ts index 04861ae48f..baba80f87c 100644 --- a/core/ts/__benchmarks__/utils/constants.ts +++ b/core/ts/__benchmarks__/utils/constants.ts @@ -7,13 +7,10 @@ export const COORDINATOR_KEYPAIR = new Keypair(); export const STATE_TREE_DEPTH = 10; export const MAX_VALUES = { maxUsers: 25, - maxMessages: 25, maxVoteOptions: 25, }; export const TREE_DEPTHS = { intStateTreeDepth: 2, - messageTreeDepth: 3, - messageTreeSubDepth: 2, voteOptionTreeDepth: 4, }; diff --git a/core/ts/__tests__/MaciState.test.ts b/core/ts/__tests__/MaciState.test.ts index 9539b66738..c14f6ed70c 100644 --- a/core/ts/__tests__/MaciState.test.ts +++ b/core/ts/__tests__/MaciState.test.ts @@ -36,7 +36,7 @@ describe("MaciState", function test() { m1.signUp(userKeypair.pubKey, voiceCreditBalance, BigInt(Math.floor(Date.now() / 1000))); pollId = m1.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -79,50 +79,35 @@ describe("MaciState", function test() { m9.polls.get(pollId)!.treeDepths.intStateTreeDepth += 1; expect(m1.equals(m9)).not.to.eq(true); - // modify poll.treeDepths.messageTreeDepth + // modify poll.treeDepths.voteOptionTreeDepth const m10 = m1.copy(); - m10.polls.get(pollId)!.treeDepths.messageTreeDepth += 1; + m10.polls.get(pollId)!.treeDepths.voteOptionTreeDepth += 1; expect(m1.equals(m10)).not.to.eq(true); - // modify poll.treeDepths.messageTreeSubDepth + // modify poll.batchSizes.tallyBatchSize const m11 = m1.copy(); - m11.polls.get(pollId)!.treeDepths.messageTreeSubDepth += 1; + m11.polls.get(pollId)!.batchSizes.tallyBatchSize += 1; expect(m1.equals(m11)).not.to.eq(true); - // modify poll.treeDepths.voteOptionTreeDepth + // modify poll.batchSizes.messageBatchSize const m12 = m1.copy(); - m12.polls.get(pollId)!.treeDepths.voteOptionTreeDepth += 1; + m12.polls.get(pollId)!.batchSizes.messageBatchSize += 1; expect(m1.equals(m12)).not.to.eq(true); - // modify poll.batchSizes.tallyBatchSize + // modify poll.maxVoteOptions const m13 = m1.copy(); - m13.polls.get(pollId)!.batchSizes.tallyBatchSize += 1; + m13.polls.get(pollId)!.maxVoteOptions += 1; expect(m1.equals(m13)).not.to.eq(true); - // modify poll.batchSizes.messageBatchSize + // modify poll.messages const m14 = m1.copy(); - m14.polls.get(pollId)!.batchSizes.messageBatchSize += 1; + m14.polls.get(pollId)!.messages[0].data[0] = BigInt(m14.polls.get(pollId)!.messages[0].data[0]) + 1n; expect(m1.equals(m14)).not.to.eq(true); - // modify poll.maxValues.maxMessages - const m16 = m1.copy(); - m16.polls.get(pollId)!.maxValues.maxMessages += 1; - expect(m1.equals(m16)).not.to.eq(true); - - // modify poll.maxValues.maxVoteOptions - const m17 = m1.copy(); - m17.polls.get(pollId)!.maxValues.maxVoteOptions += 1; - expect(m1.equals(m17)).not.to.eq(true); - - // modify poll.messages - const m20 = m1.copy(); - m20.polls.get(pollId)!.messages[0].data[0] = BigInt(m20.polls.get(pollId)!.messages[0].data[0]) + 1n; - expect(m1.equals(m20)).not.to.eq(true); - // modify poll.encPubKeys - const m21 = m1.copy(); - m21.polls.get(pollId)!.encPubKeys[0] = new Keypair().pubKey; - expect(m1.equals(m21)).not.to.eq(true); + const m15 = m1.copy(); + m15.polls.get(pollId)!.encPubKeys[0] = new Keypair().pubKey; + expect(m1.equals(m15)).not.to.eq(true); }); it("should create a JSON object from a MaciState object", () => { @@ -134,7 +119,6 @@ describe("MaciState", function test() { poll.setCoordinatorKeypair(coordinatorKeypair.privKey.serialize()); expect(poll.coordinatorKeypair.equals(coordinatorKeypair)).to.eq(true); }); - expect(state.equals(m1)).to.eq(true); }); }); diff --git a/core/ts/__tests__/Poll.test.ts b/core/ts/__tests__/Poll.test.ts index 7de35c2fc6..a48ee1bd63 100644 --- a/core/ts/__tests__/Poll.test.ts +++ b/core/ts/__tests__/Poll.test.ts @@ -21,7 +21,7 @@ describe("Poll", function test() { const maciState = new MaciState(STATE_TREE_DEPTH); const pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -241,7 +241,7 @@ describe("Poll", function test() { const maciState = new MaciState(STATE_TREE_DEPTH); const pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -258,30 +258,10 @@ describe("Poll", function test() { BigInt(Math.floor(Date.now() / 1000)), ); - it("should throw if this is the first batch and currentMessageBatchIndex is defined", () => { - const command = new PCommand(BigInt(user1StateIndex), user1Keypair.pubKey, 0n, 1n, 0n, 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); - - poll.publishMessage(message, ecdhKeypair.pubKey); - - // mock - poll.currentMessageBatchIndex = 0; - expect(() => poll.processMessages(pollId)).to.throw( - "The current message batch index should not be defined if this is the first batch", - ); - poll.currentMessageBatchIndex = undefined; - }); - it("should throw if the state has not been copied prior to calling processMessages", () => { const tmpPoll = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -334,7 +314,7 @@ describe("Poll", function test() { const maciState = new MaciState(STATE_TREE_DEPTH); const pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -422,7 +402,7 @@ describe("Poll", function test() { const maciState = new MaciState(STATE_TREE_DEPTH); const pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -492,7 +472,7 @@ describe("Poll", function test() { // deploy a second poll const secondPollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -541,7 +521,7 @@ describe("Poll", function test() { const maciState = new MaciState(STATE_TREE_DEPTH); const pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -560,7 +540,7 @@ describe("Poll", function test() { const maciState = new MaciState(STATE_TREE_DEPTH); const pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -586,7 +566,7 @@ describe("Poll", function test() { const maciState = new MaciState(STATE_TREE_DEPTH); const pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, diff --git a/core/ts/__tests__/e2e.test.ts b/core/ts/__tests__/e2e.test.ts index 45d526dc93..f4c253a860 100644 --- a/core/ts/__tests__/e2e.test.ts +++ b/core/ts/__tests__/e2e.test.ts @@ -1,10 +1,10 @@ import { expect } from "chai"; -import { hash5, NOTHING_UP_MY_SLEEVE, IncrementalQuinTree, AccQueue, hash2 } from "maci-crypto"; +import { hash5, IncrementalQuinTree, hash2 } from "maci-crypto"; import { PCommand, Keypair, StateLeaf, blankStateLeafHash } from "maci-domainobjs"; import { MaciState } from "../MaciState"; import { Poll } from "../Poll"; -import { STATE_TREE_DEPTH, STATE_TREE_ARITY, MESSAGE_TREE_ARITY } from "../utils/constants"; +import { STATE_TREE_DEPTH, STATE_TREE_ARITY } from "../utils/constants"; import { packProcessMessageSmallVals, unpackProcessMessageSmallVals } from "../utils/utils"; import { @@ -54,7 +54,7 @@ describe("MaciState/Poll e2e", function test() { // deploy a poll pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -157,7 +157,7 @@ describe("MaciState/Poll e2e", function test() { // deploy a poll pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -270,7 +270,7 @@ describe("MaciState/Poll e2e", function test() { // deploy a poll pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -366,7 +366,7 @@ describe("MaciState/Poll e2e", function test() { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -393,7 +393,21 @@ describe("MaciState/Poll e2e", function test() { expect(stateTree.root.toString()).to.eq(poll.stateTree?.root.toString()); }); - it("the message root should be correct", () => { + it("packProcessMessageSmallVals and unpackProcessMessageSmallVals", () => { + const maxVoteOptions = 1n; + const numUsers = 2n; + const batchStartIndex = 5; + const batchEndIndex = 10; + const packedVals = packProcessMessageSmallVals(maxVoteOptions, numUsers, batchStartIndex, batchEndIndex); + + const unpacked = unpackProcessMessageSmallVals(packedVals); + expect(unpacked.maxVoteOptions.toString()).to.eq(maxVoteOptions.toString()); + expect(unpacked.numUsers.toString()).to.eq(numUsers.toString()); + expect(unpacked.batchStartIndex.toString()).to.eq(batchStartIndex.toString()); + expect(unpacked.batchEndIndex.toString()).to.eq(batchEndIndex.toString()); + }); + + it("Process a batch of messages (though only 1 message is in the batch)", () => { const command = new PCommand( BigInt(stateIndex), userKeypair.pubKey, @@ -411,34 +425,6 @@ describe("MaciState/Poll e2e", function test() { poll.publishMessage(message, ecdhKeypair.pubKey); - // Use the accumulator queue to compare the root of the message tree - const accumulatorQueue: AccQueue = new AccQueue( - treeDepths.messageTreeSubDepth, - MESSAGE_TREE_ARITY, - NOTHING_UP_MY_SLEEVE, - ); - accumulatorQueue.enqueue(message.hash(ecdhKeypair.pubKey)); - accumulatorQueue.mergeSubRoots(0); - accumulatorQueue.merge(treeDepths.messageTreeDepth); - - expect(accumulatorQueue.getRoot(treeDepths.messageTreeDepth)?.toString()).to.eq(poll.messageTree.root.toString()); - }); - - it("packProcessMessageSmallVals and unpackProcessMessageSmallVals", () => { - const maxVoteOptions = 1n; - const numUsers = 2n; - const batchStartIndex = 5; - const batchEndIndex = 10; - const packedVals = packProcessMessageSmallVals(maxVoteOptions, numUsers, batchStartIndex, batchEndIndex); - - const unpacked = unpackProcessMessageSmallVals(packedVals); - expect(unpacked.maxVoteOptions.toString()).to.eq(maxVoteOptions.toString()); - expect(unpacked.numUsers.toString()).to.eq(numUsers.toString()); - expect(unpacked.batchStartIndex.toString()).to.eq(batchStartIndex.toString()); - expect(unpacked.batchEndIndex.toString()).to.eq(batchEndIndex.toString()); - }); - - it("Process a batch of messages (though only 1 message is in the batch)", () => { poll.processMessages(pollId); // Check the ballot @@ -462,7 +448,7 @@ describe("MaciState/Poll e2e", function test() { }); }); - describe(`Process and tally ${messageBatchSize * 2} messages from ${messageBatchSize} users`, () => { + describe(`Process and tally ${messageBatchSize * 2 - 2} messages from ${messageBatchSize - 1} users`, () => { let maciState: MaciState; let pollId: bigint; let poll: Poll; @@ -482,7 +468,7 @@ describe("MaciState/Poll e2e", function test() { pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -492,7 +478,7 @@ describe("MaciState/Poll e2e", function test() { }); it("should process votes correctly", () => { - // 24 valid votes + // 19 valid votes for (let i = 0; i < messageBatchSize - 1; i += 1) { const userKeypair = users[i]; @@ -515,9 +501,10 @@ describe("MaciState/Poll e2e", function test() { expect(poll.messages.length).to.eq(messageBatchSize - 1); - // 24 invalid votes + // 19 invalid votes for (let i = 0; i < messageBatchSize - 1; i += 1) { const userKeypair = users[i]; + const command = new PCommand( BigInt(i + 1), userKeypair.pubKey, @@ -535,18 +522,15 @@ describe("MaciState/Poll e2e", function test() { poll.publishMessage(message, ecdhKeypair.pubKey); } - // 48 messages in total + // 38 messages in total expect(poll.messages.length).to.eq(2 * (messageBatchSize - 1)); - expect(poll.currentMessageBatchIndex).to.eq(undefined); + expect(poll.currentMessageBatchIndex).to.eq(1); expect(poll.numBatchesProcessed).to.eq(0); // Process messages poll.processMessages(pollId); - // currentMessageBatchIndex is 0 because the current batch starts - // with index 0. - expect(poll.currentMessageBatchIndex).to.eq(0); expect(poll.numBatchesProcessed).to.eq(1); // Process messages @@ -591,7 +575,7 @@ describe("MaciState/Poll e2e", function test() { // Recall that each user `i` cast the same number of votes for // their option `i` - for (let i = 0; i < poll.tallyResult.length - 1; i += 1) { + for (let i = 1; i < messageBatchSize - 1; i += 1) { expect(poll.tallyResult[i].toString()).to.eq(voteWeight.toString()); } @@ -607,7 +591,6 @@ describe("MaciState/Poll e2e", function test() { let maciState: MaciState; let pollId: bigint; let poll: Poll; - let msgTree: IncrementalQuinTree; let stateTree: IncrementalQuinTree; const voteWeight = 9n; const voteOptionIndex = 0n; @@ -617,12 +600,11 @@ describe("MaciState/Poll e2e", function test() { before(() => { maciState = new MaciState(STATE_TREE_DEPTH); - msgTree = new IncrementalQuinTree(treeDepths.messageTreeDepth, NOTHING_UP_MY_SLEEVE, 5, hash5); stateTree = new IncrementalQuinTree(STATE_TREE_DEPTH, blankStateLeafHash, STATE_TREE_ARITY, hash5); pollId = maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -655,7 +637,6 @@ describe("MaciState/Poll e2e", function test() { const message = command.encrypt(signature, sharedKey); poll.publishMessage(message, ecdhKeypair.pubKey); - msgTree.insert(message.hash(ecdhKeypair.pubKey)); }); it("Process a batch of messages (though only 1 message is in the batch)", () => { diff --git a/core/ts/__tests__/utils.test.ts b/core/ts/__tests__/utils.test.ts index 964aa897e1..09be5b25f3 100644 --- a/core/ts/__tests__/utils.test.ts +++ b/core/ts/__tests__/utils.test.ts @@ -11,8 +11,8 @@ import { describe("Utils", () => { it("genProcessVkSig should work", () => { - const result = genProcessVkSig(1, 2, 3, 4); - expect(result).to.equal(25108406941546723055683440059751604127909689873435325366275n); + const result = genProcessVkSig(1, 2, 20); + expect(result).to.equal(6805647338418769269285938892709073780738n); }); it("genTallyVkSig should work", () => { diff --git a/core/ts/__tests__/utils/constants.ts b/core/ts/__tests__/utils/constants.ts index 1e6c2215a2..435f6d37c0 100644 --- a/core/ts/__tests__/utils/constants.ts +++ b/core/ts/__tests__/utils/constants.ts @@ -2,17 +2,14 @@ import { Keypair } from "maci-domainobjs"; export const voiceCreditBalance = 100n; export const duration = 30; -export const messageBatchSize = 25; +export const messageBatchSize = 20; export const coordinatorKeypair = new Keypair(); export const maxValues = { maxUsers: 25, - maxMessages: 25, maxVoteOptions: 25, }; export const treeDepths = { intStateTreeDepth: 2, - messageTreeDepth: 3, - messageTreeSubDepth: 2, voteOptionTreeDepth: 4, }; diff --git a/core/ts/__tests__/utils/utils.ts b/core/ts/__tests__/utils/utils.ts index 25f6452836..cb3cec6ae8 100644 --- a/core/ts/__tests__/utils/utils.ts +++ b/core/ts/__tests__/utils/utils.ts @@ -36,7 +36,7 @@ export class TestHarness { constructor() { this.pollId = this.maciState.deployPoll( BigInt(Math.floor(Date.now() / 1000) + duration), - maxValues, + maxValues.maxVoteOptions, treeDepths, messageBatchSize, this.coordinatorKeypair, diff --git a/core/ts/index.ts b/core/ts/index.ts index 54fbe47163..22148ddf91 100644 --- a/core/ts/index.ts +++ b/core/ts/index.ts @@ -21,4 +21,4 @@ export type { IJsonMaciState, } from "./utils/types"; -export { STATE_TREE_ARITY, MESSAGE_TREE_ARITY } from "./utils/constants"; +export { STATE_TREE_ARITY, MESSAGE_BATCH_SIZE } from "./utils/constants"; diff --git a/core/ts/utils/constants.ts b/core/ts/utils/constants.ts index a77a2b5375..ab5175128e 100644 --- a/core/ts/utils/constants.ts +++ b/core/ts/utils/constants.ts @@ -1,5 +1,5 @@ export const STATE_TREE_DEPTH = 10; export const STATE_TREE_ARITY = 2; export const STATE_TREE_SUBDEPTH = 2; -export const MESSAGE_TREE_ARITY = 5; export const VOTE_OPTION_TREE_ARITY = 5; +export const MESSAGE_BATCH_SIZE = 20; diff --git a/core/ts/utils/types.ts b/core/ts/utils/types.ts index 93647507fa..3e2e2e8d1f 100644 --- a/core/ts/utils/types.ts +++ b/core/ts/utils/types.ts @@ -21,14 +21,10 @@ export type CircuitInputs = Record - (BigInt(batchSize) << 192n) + - (BigInt(stateTreeDepth) << 128n) + - (BigInt(messageTreeDepth) << 64n) + - BigInt(voteOptionTreeDepth); +export const genProcessVkSig = (stateTreeDepth: number, voteOptionTreeDepth: number, batchSize: number): bigint => + (BigInt(batchSize) << 128n) + (BigInt(stateTreeDepth) << 64n) + BigInt(voteOptionTreeDepth); /** * This function generates the signature of a Tally Verifying Key(VK). diff --git a/crypto/package.json b/crypto/package.json index 4f4cd133ae..10d9fa9813 100644 --- a/crypto/package.json +++ b/crypto/package.json @@ -15,7 +15,6 @@ "types": "tsc -p tsconfig.json --noEmit", "test": "nyc ts-mocha --exit ts/__tests__/*.test.ts", "test:crypto": "ts-mocha --exit ts/__tests__/Crypto.test.ts", - "test:accQueue": "ts-mocha --exit ts/__tests__/AccQueue.test.ts", "test:utils": "ts-mocha --exit ts/__tests__/Utils.test.ts", "test:imt": "ts-mocha --exit ts/__tests__/IMT.test.ts", "docs": "typedoc --plugin typedoc-plugin-markdown --options ./typedoc.json" diff --git a/crypto/ts/AccQueue.ts b/crypto/ts/AccQueue.ts deleted file mode 100644 index 9d4159478e..0000000000 --- a/crypto/ts/AccQueue.ts +++ /dev/null @@ -1,627 +0,0 @@ -import assert from "assert"; - -import type { Leaf, Queue, StringifiedBigInts } from "./types"; - -import { deepCopyBigIntArray, stringifyBigInts, unstringifyBigInts } from "./bigIntUtils"; -import { sha256Hash, hashLeftRight, hash5 } from "./hashing"; -import { IncrementalQuinTree } from "./quinTree"; -import { calcDepthFromNumLeaves } from "./utils"; - -/** - * An Accumulator Queue which conforms to the implementation in AccQueue.sol. - * Each enqueue() operation updates a subtree, and a merge() operation combines - * all subtrees into a main tree. - * @notice It supports 2 or 5 elements per leaf. - */ -export class AccQueue { - private MAX_DEPTH = 32; - - // The depth per subtree - private subDepth: number; - - // The number of inputs per hash function - private hashLength: number; - - // The default value for empty leaves - private zeroValue: bigint; - - // The current subtree index. e.g. the first subtree has index 0, the - // second has 1, and so on - private currentSubtreeIndex = 0; - - // The number of leaves across all subtrees - private numLeaves = 0; - - // The current subtree - private leafQueue: Queue = { - levels: new Map(), - indices: [], - }; - - // For merging subtrees into the smallest tree - private nextSRindexToQueue = 0; - - private smallSRTroot = 0n; - - private subRootQueue: Queue = { - levels: new Map(), - indices: [], - }; - - // The root of each complete subtree - private subRoots: Leaf[] = []; - - // The root of merged subtrees - private mainRoots: Leaf[] = []; - - // The zero value per level. i.e. zeros[0] is zeroValue, - // zeros[1] is the hash of leavesPerNode zeros, and so on. - private zeros: bigint[] = []; - - // Whether the subtrees have been merged - private subTreesMerged = false; - - // The hash function to use for the subtrees - readonly subHashFunc: (leaves: Leaf[]) => bigint; - - // The hash function to use for rest of the tree (above the subroots) - readonly hashFunc: (leaves: Leaf[]) => bigint; - - /** - * Create a new instance of AccQueue - * @param subDepth - the depth of the subtrees - * @param hashLength - the number of leaves per node - * @param zeroValue - the default value for empty leaves - */ - constructor(subDepth: number, hashLength: number, zeroValue: bigint) { - // This class supports either 2 leaves per node, or 5 leaves per node. - // 5 is largest number of inputs which circomlib's Poseidon EVM hash - // function implementation supports. - - assert(hashLength === 2 || hashLength === 5); - assert(subDepth > 0); - - this.hashLength = hashLength; - this.subDepth = subDepth; - this.zeroValue = zeroValue; - - // Set this.hashFunc depending on the number of leaves per node - if (this.hashLength === 2) { - // Uses PoseidonT3 under the hood, which accepts 2 inputs - this.hashFunc = (inputs: bigint[]) => hashLeftRight(inputs[0], inputs[1]); - } else { - // Uses PoseidonT6 under the hood, which accepts up to 5 inputs - this.hashFunc = hash5; - } - - this.subHashFunc = sha256Hash; - - let hashed = this.zeroValue; - for (let i = 0; i < this.MAX_DEPTH; i += 1) { - this.zeros.push(hashed); - - let e: bigint[] = []; - if (this.hashLength === 2) { - e = [0n]; - hashed = this.hashFunc([hashed, hashed]); - } else { - e = [0n, 0n, 0n, 0n]; - hashed = this.hashFunc([hashed, hashed, hashed, hashed, hashed]); - } - - const levels = new Map(Object.entries(e).map(([key, value]) => [Number(key), value])); - - this.leafQueue.levels.set(this.leafQueue.levels.size, levels); - this.leafQueue.indices[i] = 0; - this.subRootQueue.levels.set(this.subRootQueue.levels.size, levels); - this.subRootQueue.indices[i] = 0; - } - } - - /** - * Get the small SRT root - * @returns small SRT root - */ - getSmallSRTroot(): bigint { - return this.smallSRTroot; - } - - /** - * Get the subroots - * @returns subroots - */ - getSubRoots(): Leaf[] { - return this.subRoots; - } - - /** - * Get the subdepth - * @returns subdepth - */ - getSubDepth(): number { - return this.subDepth; - } - - /** - * Get the root of merged subtrees - * @returns the root of merged subtrees - */ - getMainRoots(): Leaf[] { - return this.mainRoots; - } - - /** - * Get the zero values per level. i.e. zeros[0] is zeroValue, - * zeros[1] is the hash of leavesPerNode zeros, and so on. - * @returns zeros - */ - getZeros(): bigint[] { - return this.zeros; - } - - /** - * Get the subroot at a given index - * @param index - The index of the subroot - * @returns the subroot - */ - getSubRoot(index: number): Leaf { - return this.subRoots[index]; - } - - /** - * Get the number of inputs per hash function - * - * @returns the number of inputs - */ - getHashLength(): number { - return this.hashLength; - } - - /** - * Enqueue a leaf into the current subtree - * @param leaf The leaf to insert. - * @returns The index of the leaf - */ - enqueue(leaf: Leaf): number { - // validation - assert(this.numLeaves < this.hashLength ** this.MAX_DEPTH, "AccQueue is full"); - - this.enqueueOp(leaf, 0); - - // the index is the number of leaves (0-index) - const leafIndex = this.numLeaves; - - // increase the number of leaves - this.numLeaves += 1; - // we set merged false because there are new leaves - this.subTreesMerged = false; - // reset the smallSRTroot because it is obsolete - this.smallSRTroot = 0n; - - // @todo this can be moved in the constructor rather than computing every time - const subTreeCapacity = this.hashLength ** this.subDepth; - // If the current subtree is full - if (this.numLeaves % subTreeCapacity === 0) { - // store the subroot - const subRoot = this.leafQueue.levels.get(this.subDepth)?.get(0) ?? 0n; - - this.subRoots[this.currentSubtreeIndex] = subRoot; - this.currentSubtreeIndex += 1; - // reset the current subtree - this.leafQueue.levels.get(this.subDepth)?.set(0, 0n); - - for (let i = 0; i < this.MAX_DEPTH; i += 1) { - this.leafQueue.indices[i] = 0; - } - } - - return leafIndex; - } - - /** - * Private function that performs the actual enqueue operation - * @param leaf - The leaf to insert - * @param level - The level of the subtree - */ - private enqueueOp = (leaf: Leaf, level: number) => { - // small validation, do no throw - if (level > this.subDepth) { - return; - } - - // get the index to determine where to insert the next leaf - const n = this.leafQueue.indices[level]; - - // we check that the index is not the last one (1 or 4 depending on the hash length) - if (n !== this.hashLength - 1) { - // Just store the leaf - this.leafQueue.levels.get(level)?.set(n, leaf); - this.leafQueue.indices[level] += 1; - } else { - // if not we compute the root - let hashed: bigint; - if (this.hashLength === 2) { - const subRoot = this.leafQueue.levels.get(level)?.get(0) ?? 0n; - hashed = this.hashFunc([subRoot, leaf]); - this.leafQueue.levels.get(level)?.set(0, 0n); - } else { - const levelSlice = this.leafQueue.levels.get(level) ?? new Map(); - hashed = this.hashFunc(Array.from(levelSlice.values()).concat(leaf)); - - for (let i = 0; i < 4; i += 1) { - this.leafQueue.levels.get(level)?.set(i, 0n); - } - } - - this.leafQueue.indices[level] = 0; - - // Recurse - this.enqueueOp(hashed, level + 1); - } - }; - - /** - * Fill any empty leaves of the last subtree with zeros and store the - * resulting subroot. - */ - fill(): void { - // The total capacity of the subtree - const subTreeCapacity = this.hashLength ** this.subDepth; - - if (this.numLeaves % subTreeCapacity === 0) { - // If the subtree is completely empty, then the subroot is a - // precalculated zero value - this.subRoots[this.currentSubtreeIndex] = this.zeros[this.subDepth]; - } else { - this.fillOp(0); - - // Store the subroot - const subRoot = this.leafQueue.levels.get(this.subDepth)?.get(0) ?? 0n; - this.subRoots[this.currentSubtreeIndex] = subRoot; - - // Blank out the subtree data - for (let i = 0; i < this.subDepth + 1; i += 1) { - if (this.hashLength === 2) { - this.leafQueue.levels.get(i)?.set(0, 0n); - } else { - const levels = new Map(Object.entries([0n, 0n, 0n, 0n]).map(([key, value]) => [Number(key), value])); - this.leafQueue.levels.set(i, levels); - } - } - } - - // Update the subtree index - this.currentSubtreeIndex += 1; - - // Update the number of leaves - this.numLeaves = this.currentSubtreeIndex * subTreeCapacity; - - this.subTreesMerged = false; - this.smallSRTroot = 0n; - } - - /** - * Private function that performs the actual fill operation - * @param level - The level of the subtree - */ - private fillOp(level: number) { - if (level > this.subDepth) { - return; - } - - const n = this.leafQueue.indices[level]; - - if (n !== 0) { - // Fill the subtree level and hash it - let hashed: bigint; - if (this.hashLength === 2) { - hashed = this.hashFunc([this.leafQueue.levels.get(level)?.get(0) ?? 0n, this.zeros[level]]); - } else { - for (let i = n; i < this.hashLength; i += 1) { - this.leafQueue.levels.get(level)?.set(i, this.zeros[level]); - } - - const levelSlice = this.leafQueue.levels.get(level) ?? new Map(); - hashed = this.hashFunc(Array.from(levelSlice.values())); - } - - // Update the subtree from the next level onwards with the new leaf - this.enqueueOp(hashed, level + 1); - - // Reset the current level - this.leafQueue.indices[level] = 0; - } - - // Recurse - this.fillOp(level + 1); - } - - /** - * Calculate the depth of the smallest possible Merkle tree which fits all - * @returns the depth of the smallest possible Merkle tree which fits all - */ - calcSRTdepth(): number { - // Calculate the SRT depth - let srtDepth = this.subDepth; - const subTreeCapacity = this.hashLength ** this.subDepth; - while (this.hashLength ** srtDepth < this.subRoots.length * subTreeCapacity) { - srtDepth += 1; - } - - return srtDepth; - } - - /** - * Insert a subtree into the queue. This is used when the subtree is - * already computed. - * @param subRoot - The root of the subtree - */ - insertSubTree(subRoot: bigint): void { - // If the current subtree is not full, fill it. - const subTreeCapacity = this.hashLength ** this.subDepth; - - this.subRoots[this.currentSubtreeIndex] = subRoot; - - // Update the subtree index - this.currentSubtreeIndex += 1; - - // Update the number of leaves - this.numLeaves += subTreeCapacity; - - // Reset the subroot tree root now that it is obsolete - this.smallSRTroot = 0n; - - this.subTreesMerged = false; - } - - /** - * Merge all the subroots into a tree of a specified depth. - * It requires this.mergeSubRoots() to be run first. - */ - merge(depth: number): void { - assert(this.subTreesMerged); - assert(depth <= this.MAX_DEPTH); - - const srtDepth = this.calcSRTdepth(); - - assert(depth >= srtDepth); - - if (depth === srtDepth) { - this.mainRoots[depth] = this.smallSRTroot; - } else { - let root = this.smallSRTroot; - - // Calculate the main root - for (let i = srtDepth; i < depth; i += 1) { - const inputs: bigint[] = [root]; - const z = this.zeros[i]; - - for (let j = 1; j < this.hashLength; j += 1) { - inputs.push(z); - } - - root = this.hashFunc(inputs); - } - - this.mainRoots[depth] = root; - } - } - - /** - * Merge all the subroots into a tree of a specified depth. - * Uses an IncrementalQuinTree instead of the two-step method that - * AccQueue.sol uses. - */ - mergeDirect(depth: number): void { - // There must be subtrees to merge - assert(this.numLeaves > 0); - - const srtDepth = this.calcSRTdepth(); - - // The desired tree must be deep enough - assert(depth >= srtDepth); - - if (depth === this.subDepth) { - // If there is only 1 subtree, and the desired depth is the subtree - // depth, the subroot is the result - assert(this.numLeaves === this.hashLength ** this.subDepth); - const [subRoot] = this.subRoots; - this.mainRoots[depth] = subRoot; - this.subTreesMerged = true; - return; - } - - // The desired main tree must be deep enough to fit all leaves - assert(BigInt(depth ** this.hashLength) >= this.numLeaves); - - // Fill any empty leaves in the last subtree with zeros - if (this.numLeaves % this.hashLength ** this.subDepth > 0) { - this.fill(); - } - - const tree = new IncrementalQuinTree( - depth - this.subDepth, - this.zeros[this.subDepth], - this.hashLength, - this.hashFunc, - ); - - this.subRoots.forEach((subRoot) => { - tree.insert(subRoot); - }); - - this.mainRoots[depth] = tree.root; - } - - /** - * Merge all subroots into the smallest possible Merkle tree which fits - * them. e.g. if there are 5 subroots and hashLength == 2, the tree depth - * is 3 since 2 ** 3 = 8 which is the next power of 2. - * @param numSrQueueOps - The number of subroots to queue into the SRT - */ - mergeSubRoots(numSrQueueOps = 0): void { - // This function can only be called once unless a new subtree is created - assert(!this.subTreesMerged); - - // There must be subtrees to merge - assert(this.numLeaves > 0); - - // Fill any empty leaves in the last subtree with zeros - if (this.numLeaves % this.hashLength ** this.subDepth !== 0) { - this.fill(); - } - - // If there is only 1 subtree, use its root - if (this.currentSubtreeIndex === 1) { - this.smallSRTroot = this.getSubRoot(0); - this.subTreesMerged = true; - return; - } - - // Compute the depth and maximum capacity of the smallMainTreeRoot - const depth = calcDepthFromNumLeaves(this.hashLength, this.currentSubtreeIndex); - - let numQueueOps = 0; - - for (let i = this.nextSRindexToQueue; i < this.currentSubtreeIndex; i += 1) { - // Stop if the limit has been reached - if (numSrQueueOps !== 0 && numQueueOps === numSrQueueOps) { - return; - } - - // Queue the next subroot - const subRoot = this.getSubRoot(this.nextSRindexToQueue); - this.queueSubRoot(subRoot, 0, depth); - - // Increment the next subroot counter - this.nextSRindexToQueue += 1; - numQueueOps += 1; - } - - // Queue zeros to get the SRT. `m` is the number of leaves in the - // main tree, which already has `this.currentSubtreeIndex` leaves - const m = this.hashLength ** depth; - if (this.nextSRindexToQueue === this.currentSubtreeIndex) { - for (let i = this.currentSubtreeIndex; i < m; i += 1) { - const z = this.zeros[this.subDepth]; - this.queueSubRoot(z, 0, depth); - } - } - - // Store the root - const subRoot = this.subRootQueue.levels.get(depth)?.get(0) ?? 0n; - this.smallSRTroot = subRoot; - this.subTreesMerged = true; - } - - /** - * Queues the leaf (a subroot) into queuedSRTlevels - * @param leaf - The leaf to insert - * @param level - The level of the subtree - * @param maxDepth - The maximum depth of the tree - */ - private queueSubRoot(leaf: bigint, level: number, maxDepth: number) { - if (level > maxDepth) { - return; - } - - const n = this.subRootQueue.indices[level]; - - if (n !== this.hashLength - 1) { - // Just store the leaf - this.subRootQueue.levels.get(level)?.set(n, leaf); - this.subRootQueue.indices[level] += 1; - } else { - // Hash the elements in this level and queue it in the next level - const inputs: bigint[] = []; - for (let i = 0; i < this.hashLength - 1; i += 1) { - inputs.push(this.subRootQueue.levels.get(level)?.get(i) ?? 0n); - } - inputs.push(leaf); - const hashed = this.hashFunc(inputs); - - // Recurse - this.subRootQueue.indices[level] = 0; - this.queueSubRoot(hashed, level + 1, maxDepth); - } - } - - /** - * Get the root at a certain depth - * @param depth - The depth of the tree - * @returns the root - */ - getRoot(depth: number): bigint | null | undefined { - return this.mainRoots[depth]; - } - - /** - * Check if the root at a certain depth exists (subtree root) - * @param depth - the depth of the tree - * @returns whether the root exists - */ - hasRoot(depth: number): boolean { - const root = this.getRoot(depth); - return !(root === null || root === undefined); - } - - /** - * @notice Deep-copies this object - * @returns a deep copy of this object - */ - copy(): AccQueue { - const newAccQueue = new AccQueue(this.subDepth, this.hashLength, this.zeroValue); - newAccQueue.currentSubtreeIndex = JSON.parse(JSON.stringify(this.currentSubtreeIndex)) as number; - newAccQueue.numLeaves = JSON.parse(JSON.stringify(this.numLeaves)) as number; - - const arrayLeafLevels = unstringifyBigInts( - JSON.parse(JSON.stringify(stringifyBigInts(this.mapToArray(this.leafQueue.levels)))) as StringifiedBigInts, - ) as bigint[][]; - newAccQueue.leafQueue.levels = this.arrayToMap(arrayLeafLevels); - newAccQueue.leafQueue.indices = JSON.parse(JSON.stringify(this.leafQueue.indices)) as number[]; - newAccQueue.subRoots = deepCopyBigIntArray(this.subRoots); - newAccQueue.mainRoots = deepCopyBigIntArray(this.mainRoots); - newAccQueue.zeros = deepCopyBigIntArray(this.zeros); - newAccQueue.subTreesMerged = !!this.subTreesMerged; - newAccQueue.nextSRindexToQueue = Number(this.nextSRindexToQueue.toString()); - newAccQueue.smallSRTroot = BigInt(this.smallSRTroot.toString()); - newAccQueue.subRootQueue.indices = JSON.parse(JSON.stringify(this.subRootQueue.indices)) as number[]; - - const arraySubRootLevels = unstringifyBigInts( - JSON.parse(JSON.stringify(stringifyBigInts(this.mapToArray(this.subRootQueue.levels)))) as StringifiedBigInts, - ) as bigint[][]; - newAccQueue.subRootQueue.levels = this.arrayToMap(arraySubRootLevels); - - return newAccQueue; - } - - /** - * Convert map to 2D array - * - * @param map - map representation of 2D array - * @returns 2D array - */ - private mapToArray(map: Map>): bigint[][] { - return Array.from(map.values()).map((v) => Array.from(v.values())); - } - - /** - * Convert 2D array to its map representation - * - * @param array - 2D array - * @returns map representation of 2D array - */ - private arrayToMap(array: bigint[][]): Map> { - return new Map(array.map((level, i) => [i, new Map(level.map((leaf, j) => [j, leaf]))])); - } - - /** - * Hash an array of leaves - * @param leaves - The leaves to hash - * @returns the hash value of the leaves - */ - hash(leaves: bigint[]): bigint { - assert(leaves.length === this.hashLength); - return this.hashFunc(leaves); - } -} diff --git a/crypto/ts/__tests__/AccQueue.test.ts b/crypto/ts/__tests__/AccQueue.test.ts deleted file mode 100644 index 0e6585279d..0000000000 --- a/crypto/ts/__tests__/AccQueue.test.ts +++ /dev/null @@ -1,319 +0,0 @@ -import { expect } from "chai"; - -import { IncrementalQuinTree, AccQueue } from ".."; - -import { testMerge, testMergeExhaustive, testMergeShortest, testMergeShortestOne } from "./utils"; - -describe("AccQueue", function test() { - this.timeout(100000); - - describe("Enqueue", () => { - describe("Binary AccQueue", () => { - const HASH_LENGTH = 2; - const SUB_DEPTH = 2; - const ZERO = BigInt(0); - - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - - it("should enqueue leaves into a subtree", () => { - const tree0 = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - - const subtreeCapacity = HASH_LENGTH ** SUB_DEPTH; - for (let i = 0; i < subtreeCapacity; i += 1) { - const leaf = BigInt(i + 1); - tree0.insert(leaf); - aq.enqueue(leaf); - } - expect(aq.getSubRoot(0).toString()).to.eq(tree0.root.toString()); - }); - - it("should enqueue another subtree", () => { - const tree1 = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - - const subtreeCapacity = HASH_LENGTH ** SUB_DEPTH; - for (let i = 0; i < subtreeCapacity; i += 1) { - const leaf = BigInt(i + 1); - tree1.insert(leaf); - aq.enqueue(leaf); - } - expect(aq.getSubRoot(1).toString()).to.eq(tree1.root.toString()); - }); - }); - - describe("Quinary AccQueue", () => { - const HASH_LENGTH = 5; - const SUB_DEPTH = 2; - const ZERO = BigInt(0); - - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - - it("should enqueue leaves into a subtree", () => { - const tree0 = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - - const subtreeCapacity = HASH_LENGTH ** SUB_DEPTH; - for (let i = 0; i < subtreeCapacity; i += 1) { - const leaf = BigInt(i + 1); - tree0.insert(leaf); - aq.enqueue(leaf); - } - expect(aq.getSubRoot(0).toString()).to.eq(tree0.root.toString()); - - const tree1 = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - - for (let i = 0; i < subtreeCapacity; i += 1) { - const leaf = BigInt(i + 1); - tree1.insert(leaf); - aq.enqueue(leaf); - } - expect(aq.getSubRoot(1).toString()).to.eq(tree1.root.toString()); - }); - }); - }); - - describe("Fill", () => { - describe("Binary AccQueue", () => { - const HASH_LENGTH = 2; - const SUB_DEPTH = 2; - const ZERO = BigInt(0); - - it("Filling an empty subtree should create the correct subroot", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const tree = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - aq.fill(); - expect(aq.getSubRoot(0).toString()).to.eq(tree.root.toString()); - }); - - it("should fill an incomplete subtree", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const tree = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - - const leaf = BigInt(1); - aq.enqueue(leaf); - tree.insert(leaf); - - aq.fill(); - - expect(aq.getSubRoot(0).toString()).to.eq(tree.root.toString()); - }); - - it("Filling an empty subtree again should create the correct subroot", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const leaf = BigInt(1); - - // Create the first subtree with one leaf - aq.enqueue(leaf); - aq.fill(); - - // Fill the second subtree with zeros - aq.fill(); - const tree = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - expect(aq.getSubRoot(1).toString()).to.eq(tree.root.toString()); - }); - - it("fill() should be correct for every number of leaves in an incomplete subtree", () => { - for (let i = 0; i < 2; i += 1) { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const tree = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - for (let j = 0; j < i; j += 1) { - const leaf = BigInt(i + 1); - aq.enqueue(leaf); - tree.insert(leaf); - } - aq.fill(); - - expect(aq.getSubRoot(0).toString()).to.eq(tree.root.toString()); - } - }); - }); - - describe("Quinary AccQueue", () => { - const HASH_LENGTH = 5; - const SUB_DEPTH = 2; - const ZERO = BigInt(0); - - it("Filling an empty subtree should create the correct subroot", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const tree = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - aq.fill(); - expect(aq.getSubRoot(0).toString()).to.eq(tree.root.toString()); - }); - - it("should fill one incomplete subtree", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const tree = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - - const leaf = BigInt(1); - aq.enqueue(leaf); - tree.insert(leaf); - - aq.fill(); - - expect(aq.getSubRoot(0).toString()).to.eq(tree.root.toString()); - }); - - it("Filling an empty subtree again should create the correct subroot", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const leaf = BigInt(1); - - // Create the first subtree with one leaf - aq.enqueue(leaf); - aq.fill(); - - // Fill the second subtree with zeros - aq.fill(); - const tree = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - expect(aq.getSubRoot(1).toString()).to.eq(tree.root.toString()); - }); - - it("fill() should be correct for every number of leaves in an incomplete subtree", () => { - const capacity = HASH_LENGTH ** SUB_DEPTH; - for (let i = 1; i < capacity - 1; i += 1) { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const tree = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - for (let j = 0; j < i; j += 1) { - const leaf = BigInt(i + 1); - aq.enqueue(leaf); - tree.insert(leaf); - } - aq.fill(); - - expect(aq.getSubRoot(0).toString()).to.eq(tree.root.toString()); - } - }); - }); - }); - - describe("Merge", () => { - const SUB_DEPTH = 2; - const ZERO = BigInt(0); - const NUM_SUBTREES = 5; - const MAIN_DEPTH = 5; - - describe("Binary AccQueue", () => { - const HASH_LENGTH = 2; - - describe("merge()", () => { - it("should produce the correct main root", () => { - testMerge(SUB_DEPTH, HASH_LENGTH, ZERO, NUM_SUBTREES, MAIN_DEPTH); - }); - }); - - describe("mergeSubRoots()", () => { - it("should work progressively", () => { - testMergeShortest(SUB_DEPTH, HASH_LENGTH, ZERO, NUM_SUBTREES); - }); - - it("should fail if there are 0 leaves", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - expect(() => { - aq.mergeSubRoots(0); - }).to.throw(); - }); - - it("should a generate the same smallMainTreeRoot root from 1 subroot", () => { - testMergeShortestOne(SUB_DEPTH, HASH_LENGTH, ZERO); - }); - - it("Exhaustive test from 2 to 16 subtrees", () => { - const MAX = 16; - testMergeExhaustive(SUB_DEPTH, HASH_LENGTH, ZERO, MAX); - }); - }); - }); - - describe("Quinary AccQueue", () => { - const HASH_LENGTH = 5; - - describe("merge()", () => { - it("should produce the correct main root", () => { - testMerge(SUB_DEPTH, HASH_LENGTH, ZERO, NUM_SUBTREES, MAIN_DEPTH); - }); - }); - - describe("mergeSubRoots()", () => { - it("should work progressively", () => { - testMergeShortest(SUB_DEPTH, HASH_LENGTH, ZERO, NUM_SUBTREES); - }); - - it("should fail if there are 0 leaves", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - expect(() => { - aq.mergeSubRoots(0); - }).to.throw(); - }); - - it("should a generate the same smallMainTreeRoot root from 1 subroot", () => { - testMergeShortestOne(SUB_DEPTH, HASH_LENGTH, ZERO); - }); - - it("Exhaustive test from 2 to 16 subtrees", () => { - const MAX = 16; - testMergeExhaustive(SUB_DEPTH, HASH_LENGTH, ZERO, MAX); - }); - }); - }); - }); - - describe("InsertSubTree", () => { - describe("Binary AccQueue", () => { - const HASH_LENGTH = 2; - const SUB_DEPTH = 2; - const ZERO = BigInt(0); - - it("should insert a subtree root into the correct position", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const subRoot = BigInt(1); - expect(aq.getSubRoots().length).to.eq(0); - aq.insertSubTree(subRoot); - expect(aq.getSubRoots()[0].toString()).to.eq(subRoot.toString()); - }); - - it("should insert a subtree root when multiple subtrees exist", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const subRoot1 = BigInt(1); - const subRoot2 = BigInt(2); - aq.insertSubTree(subRoot1); - aq.insertSubTree(subRoot2); - expect(aq.getSubRoots()[0].toString()).to.eq(subRoot1.toString()); - expect(aq.getSubRoots()[1].toString()).to.eq(subRoot2.toString()); - }); - }); - }); - - describe("Copy", () => { - const HASH_LENGTH = 2; - const SUB_DEPTH = 2; - const ZERO = BigInt(0); - - it("should create a deep copy of the AccQueue", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - aq.enqueue(ZERO); - const copy = aq.copy(); - - expect(copy).to.be.an.instanceof(AccQueue); - expect(copy.getSubRoots().length).to.eq(aq.getSubRoots().length); - expect(copy.getSubRoots()).to.eql(aq.getSubRoots()); - expect(copy.getHashLength()).to.eq(aq.getHashLength()); - expect(copy.getSubDepth()).to.eql(aq.getSubDepth()); - expect(copy.getZeros()).to.eql(aq.getZeros()); - }); - - it("should not be the same object as the original", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const copy = aq.copy(); - expect(copy).not.eq(aq); - }); - - it("should not affect the original AccQueue when modifying the copy", () => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - - aq.enqueue(ZERO); - const copy = aq.copy(); - - copy.enqueue(ZERO); - copy.insertSubTree(ZERO); - - expect(aq.getSubRoots().length).to.eq(0); - }); - }); -}); diff --git a/crypto/ts/__tests__/utils.ts b/crypto/ts/__tests__/utils.ts deleted file mode 100644 index 75ccfa0f75..0000000000 --- a/crypto/ts/__tests__/utils.ts +++ /dev/null @@ -1,132 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -import { expect } from "chai"; - -import { AccQueue, IncrementalQuinTree, calcDepthFromNumLeaves } from ".."; - -/** - * Test a full merge - * @param SUB_DEPTH - * @param HASH_LENGTH - * @param ZERO - * @param NUM_SUBTREES - * @param MAIN_DEPTH - */ -export const testMerge = ( - SUB_DEPTH: number, - HASH_LENGTH: number, - ZERO: bigint, - NUM_SUBTREES: number, - MAIN_DEPTH: number, -): void => { - // const hashFunc = HASH_LENGTH === 5 ? hash5 : hash2 - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const aq2 = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const tree = new IncrementalQuinTree(MAIN_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - - for (let i = 0; i < NUM_SUBTREES; i += 1) { - for (let j = 0; j < HASH_LENGTH ** SUB_DEPTH; j += 1) { - const leaf = BigInt(j + 1); - tree.insert(leaf); - aq.enqueue(leaf); - aq2.enqueue(leaf); - } - } - - // The main root should not exist yet - expect(aq.hasRoot(MAIN_DEPTH)).to.eq(false); - expect(aq2.hasRoot(MAIN_DEPTH)).to.eq(false); - - aq2.mergeSubRoots(0); - aq2.merge(MAIN_DEPTH); - - // For reference only - aq.mergeDirect(MAIN_DEPTH); - - // merge and mergeDirect should produce the same root - expect(aq.hasRoot(MAIN_DEPTH)).to.eq(true); - expect(aq2.hasRoot(MAIN_DEPTH)).to.eq(true); - expect(aq.getRoot(MAIN_DEPTH)!.toString()).to.eq(aq2.getRoot(MAIN_DEPTH)!.toString()); - - // merge and mergeDirect should produce the correct root - expect(aq.getRoot(MAIN_DEPTH)!.toString()).to.eq(tree.root.toString()); -}; - -/** - * Test merging the shortest subtree - * @param SUB_DEPTH - * @param HASH_LENGTH - * @param ZERO - * @param NUM_SUBTREES - */ -export const testMergeShortest = (SUB_DEPTH: number, HASH_LENGTH: number, ZERO: bigint, NUM_SUBTREES: number): void => { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const aq2 = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - - for (let i = 0; i < NUM_SUBTREES; i += 1) { - for (let j = 0; j < HASH_LENGTH ** SUB_DEPTH; j += 1) { - const leaf = BigInt(j + 1); - aq.enqueue(leaf); - aq2.enqueue(leaf); - } - } - - // Merge all subroots in aq - aq.mergeSubRoots(0); - - // Merge all but one subroot in aq2 - aq2.mergeSubRoots(2); - expect(aq.getSmallSRTroot().toString()).not.to.eq(aq2.getSmallSRTroot().toString()); - aq2.mergeSubRoots(2); - expect(aq.getSmallSRTroot().toString()).not.to.eq(aq2.getSmallSRTroot().toString()); - - // Merge the last subroot in aq2 - aq2.mergeSubRoots(1); - - expect(aq.getSmallSRTroot().toString()).to.eq(aq2.getSmallSRTroot().toString()); -}; - -/** - * Insert one leaf, then run mergeSubRoots - */ -export const testMergeShortestOne = (SUB_DEPTH: number, HASH_LENGTH: number, ZERO: bigint): void => { - const leaf = BigInt(123); - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - const smallTree = new IncrementalQuinTree(SUB_DEPTH, ZERO, HASH_LENGTH, aq.hashFunc); - - aq.enqueue(leaf); - smallTree.insert(leaf); - - aq.mergeSubRoots(0); - - expect(aq.getSmallSRTroot().toString()).to.eq(smallTree.root.toString()); - expect(aq.getSubRoot(0).toString()).to.eq(smallTree.root.toString()); -}; - -/** - * Create a number of subtrees, and merge them all - */ -export const testMergeExhaustive = (SUB_DEPTH: number, HASH_LENGTH: number, ZERO: bigint, MAX: number): void => { - for (let numSubtrees = 2; numSubtrees <= MAX; numSubtrees += 1) { - const aq = new AccQueue(SUB_DEPTH, HASH_LENGTH, ZERO); - - // Create numSubtrees subtrees - for (let i = 0; i < numSubtrees; i += 1) { - for (let j = 0; j < HASH_LENGTH ** SUB_DEPTH; j += 1) { - const leaf = BigInt(j + 1); - aq.enqueue(leaf); - } - } - - // Merge subroots - aq.mergeSubRoots(0); - - const depth = calcDepthFromNumLeaves(HASH_LENGTH, numSubtrees); - const smallTree = new IncrementalQuinTree(depth, aq.getZeros()[aq.getSubDepth()], HASH_LENGTH, aq.hashFunc); - - aq.getSubRoots().forEach((subRoot) => { - smallTree.insert(subRoot); - }); - - expect(aq.getSmallSRTroot().toString()).to.eq(smallTree.root.toString()); - } -}; diff --git a/crypto/ts/index.ts b/crypto/ts/index.ts index 7da88db0e6..e2b4b19403 100644 --- a/crypto/ts/index.ts +++ b/crypto/ts/index.ts @@ -1,5 +1,3 @@ -export { AccQueue } from "./AccQueue"; - export { calcDepthFromNumLeaves, genTreeCommitment, genTreeProof } from "./utils"; export { IncrementalQuinTree } from "./quinTree"; diff --git a/integrationTests/ts/__tests__/data/suites.json b/integrationTests/ts/__tests__/data/suites.json index e76f98ba8e..e557df1640 100644 --- a/integrationTests/ts/__tests__/data/suites.json +++ b/integrationTests/ts/__tests__/data/suites.json @@ -11,7 +11,7 @@ }, { "name": "Happy path", - "description": "Full tree, 4 full batches, no bribers", + "description": "Full tree, 4 full batches, no bribers", "numVotesPerUser": 1, "numUsers": 16, "expectedTally": [16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], diff --git a/integrationTests/ts/__tests__/integration.test.ts b/integrationTests/ts/__tests__/integration.test.ts index a792b82ba9..cfaa0360b1 100644 --- a/integrationTests/ts/__tests__/integration.test.ts +++ b/integrationTests/ts/__tests__/integration.test.ts @@ -7,7 +7,6 @@ import { deployPoll, deployVkRegistryContract, genProofs, - mergeMessages, mergeSignups, proveOnChain, publish, @@ -19,7 +18,7 @@ import { PollContracts, } from "maci-cli"; import { getDefaultSigner } from "maci-contracts"; -import { MaciState, MaxValues, TreeDepths } from "maci-core"; +import { MaciState, TreeDepths } from "maci-core"; import { genPubKey, genRandomSalt } from "maci-crypto"; import { Keypair, PCommand, PrivKey, PubKey } from "maci-domainobjs"; @@ -29,8 +28,7 @@ import path from "path"; import { INT_STATE_TREE_DEPTH, - MSG_BATCH_DEPTH, - MSG_TREE_DEPTH, + MESSAGE_BATCH_SIZE, SG_DATA, STATE_TREE_DEPTH, VOTE_OPTION_TREE_DEPTH, @@ -38,7 +36,6 @@ import { initialVoiceCredits, ivcpData, maxMessages, - messageBatchDepth, } from "./utils/constants"; import { ITestSuite } from "./utils/interfaces"; import { expectTally, genTestUserCommands, isArm } from "./utils/utils"; @@ -75,12 +72,11 @@ describe("Integration tests", function test() { await setVerifyingKeys({ stateTreeDepth: STATE_TREE_DEPTH, intStateTreeDepth: INT_STATE_TREE_DEPTH, - messageTreeDepth: MSG_TREE_DEPTH, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, - messageBatchDepth: MSG_BATCH_DEPTH, + messageBatchSize: MESSAGE_BATCH_SIZE, processMessagesZkeyPathQv: path.resolve( __dirname, - "../../../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test.0.zkey", + "../../../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test.0.zkey", ), tallyVotesZkeyPathQv: path.resolve( __dirname, @@ -88,7 +84,7 @@ describe("Integration tests", function test() { ), processMessagesZkeyPathNonQv: path.resolve( __dirname, - "../../../cli/zkeys/ProcessMessagesNonQv_10-2-1-2_test/ProcessMessagesNonQv_10-2-1-2_test.0.zkey", + "../../../cli/zkeys/ProcessMessagesNonQv_10-20-2_test/ProcessMessagesNonQv_10-20-2_test.0.zkey", ), tallyVotesZkeyPathNonQv: path.resolve( __dirname, @@ -107,17 +103,13 @@ describe("Integration tests", function test() { // 3. deploy maci contracts = await deploy({ stateTreeDepth: STATE_TREE_DEPTH, initialVoiceCredits, signer }); - const maxValues: MaxValues = { - maxMessages: 25, - maxVoteOptions: 25, - }; + const maxVoteOptions = 25; // 4. create a poll pollContracts = await deployPoll({ pollDuration: duration, intStateTreeDepth: INT_STATE_TREE_DEPTH, - messageTreeSubDepth: MSG_BATCH_DEPTH, - messageTreeDepth: MSG_TREE_DEPTH, + messageBatchSize: MESSAGE_BATCH_SIZE, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, coordinatorPubkey: coordinatorKeypair.pubKey.serialize(), maciAddress: contracts.maciAddress, @@ -127,16 +119,14 @@ describe("Integration tests", function test() { const treeDepths: TreeDepths = { intStateTreeDepth: INT_STATE_TREE_DEPTH, - messageTreeDepth: MSG_TREE_DEPTH, - messageTreeSubDepth: MSG_BATCH_DEPTH, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, }; - const messageBatchSize = 5 ** messageBatchDepth; + const messageBatchSize = MESSAGE_BATCH_SIZE; pollId = maciState.deployPoll( BigInt(Date.now() + duration * 60000), - maxValues, + maxVoteOptions, treeDepths, messageBatchSize, coordinatorKeypair, @@ -246,11 +236,6 @@ describe("Integration tests", function test() { await timeTravel({ seconds: duration, signer }); - // merge messages - await expect( - mergeMessages({ pollId, maciAddress: contracts.maciAddress, signer }), - ).to.eventually.not.be.rejectedWith(); - // merge signups await expect( mergeSignups({ pollId, maciAddress: contracts.maciAddress, signer }), @@ -263,17 +248,17 @@ describe("Integration tests", function test() { tallyZkey: path.resolve(__dirname, "../../../cli/zkeys/TallyVotes_10-1-2_test/TallyVotes_10-1-2_test.0.zkey"), processZkey: path.resolve( __dirname, - "../../../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test.0.zkey", + "../../../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test.0.zkey", ), pollId, rapidsnark: `${homedir()}/rapidsnark/build/prover`, processWitgen: path.resolve( __dirname, - "../../../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test_cpp/ProcessMessages_10-2-1-2_test", + "../../../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test_cpp/ProcessMessages_10-20-2_test", ), processDatFile: path.resolve( __dirname, - "../../../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test_cpp/ProcessMessages_10-2-1-2_test.dat", + "../../../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test_cpp/ProcessMessages_10-20-2_test.dat", ), tallyWitgen: path.resolve( __dirname, @@ -287,7 +272,7 @@ describe("Integration tests", function test() { maciAddress: contracts.maciAddress, processWasm: path.resolve( __dirname, - "../../../cli/zkeys/ProcessMessages_10-2-1-2_test/ProcessMessages_10-2-1-2_test_js/ProcessMessages_10-2-1-2_test.wasm", + "../../../cli/zkeys/ProcessMessages_10-20-2_test/ProcessMessages_10-20-2_test_js/ProcessMessages_10-20-2_test.wasm", ), tallyWasm: path.resolve( __dirname, diff --git a/integrationTests/ts/__tests__/maci-keys.test.ts b/integrationTests/ts/__tests__/maci-keys.test.ts index 08260e7aeb..fe647eb754 100644 --- a/integrationTests/ts/__tests__/maci-keys.test.ts +++ b/integrationTests/ts/__tests__/maci-keys.test.ts @@ -11,8 +11,7 @@ import { VOTE_OPTION_TREE_DEPTH, duration, initialVoiceCredits, - messageBatchDepth, - messageTreeDepth, + MESSAGE_BATCH_SIZE, } from "./utils/constants"; import { deployTestContracts } from "./utils/utils"; @@ -85,10 +84,9 @@ describe("integration tests private/public/keypair", () => { BigInt(duration), { intStateTreeDepth: INT_STATE_TREE_DEPTH, - messageTreeDepth, - messageTreeSubDepth: messageBatchDepth, voteOptionTreeDepth: VOTE_OPTION_TREE_DEPTH, }, + MESSAGE_BATCH_SIZE, coordinatorKeypair.pubKey.asContractParam(), verifier, vkRegistry, diff --git a/integrationTests/ts/__tests__/utils/constants.ts b/integrationTests/ts/__tests__/utils/constants.ts index 2777805f2d..2583b36f16 100644 --- a/integrationTests/ts/__tests__/utils/constants.ts +++ b/integrationTests/ts/__tests__/utils/constants.ts @@ -28,16 +28,12 @@ export const signUpDuration = 120; export const votingDuration = 120; export const signUpDurationInSeconds = 3600; export const votingDurationInSeconds = 3600; -export const messageBatchSize = 4; export const tallyBatchSize = 4; export const quadVoteTallyBatchSize = 4; export const voteOptionsMaxLeafIndex = 3; export const duration = 300; export const intStateTreeDepth = 1; -export const messageTreeDepth = 2; -export const messageBatchDepth = 1; export const STATE_TREE_DEPTH = 10; export const INT_STATE_TREE_DEPTH = 1; -export const MSG_TREE_DEPTH = 2; export const VOTE_OPTION_TREE_DEPTH = 2; -export const MSG_BATCH_DEPTH = 1; +export const MESSAGE_BATCH_SIZE = 20; diff --git a/subgraph/schemas/schema.v1.graphql b/subgraph/schemas/schema.v1.graphql index c018b98466..743b4898a6 100644 --- a/subgraph/schemas/schema.v1.graphql +++ b/subgraph/schemas/schema.v1.graphql @@ -32,7 +32,6 @@ type Poll @entity { pollId: BigInt! # uint256 duration: BigInt! # uint256 treeDepth: BigInt! # uint8 - maxMessages: BigInt! maxVoteOption: BigInt! messageProcessor: Bytes! # address tally: Bytes! # address @@ -43,10 +42,6 @@ type Poll @entity { stateRoot: BigInt # uint256 numSignups: BigInt! # uint256 numMessages: BigInt! # uint256 - "merge message tree after ended" - numSrQueueOps: BigInt # uint256 - messageRoot: BigInt - "relations" owner: Bytes! maci: MACI! diff --git a/subgraph/src/maci.ts b/subgraph/src/maci.ts index 0511eb0c97..4400899927 100644 --- a/subgraph/src/maci.ts +++ b/subgraph/src/maci.ts @@ -14,15 +14,14 @@ export function handleDeployPoll(event: DeployPollEvent): void { const poll = new Poll(event.params.pollAddr.poll); const contract = PollContract.bind(event.params.pollAddr.poll); - const maxValues = contract.maxValues(); + const maxVoteOptions = contract.maxVoteOptions(); const treeDepths = contract.treeDepths(); const durations = contract.getDeployTimeAndDuration(); poll.pollId = event.params._pollId; poll.messageProcessor = event.params.pollAddr.messageProcessor; poll.tally = event.params.pollAddr.tally; - poll.maxMessages = maxValues.value0; - poll.maxVoteOption = maxValues.value1; + poll.maxVoteOption = maxVoteOptions; poll.treeDepth = GraphBN.fromI32(treeDepths.value0); poll.duration = durations.value1; poll.mode = GraphBN.fromI32(event.params._mode); diff --git a/subgraph/src/poll.ts b/subgraph/src/poll.ts index 7c9065d023..82117110c6 100644 --- a/subgraph/src/poll.ts +++ b/subgraph/src/poll.ts @@ -3,8 +3,6 @@ import { Poll, Vote, MACI } from "../generated/schema"; import { MergeMaciState as MergeMaciStateEvent, - MergeMessageAq as MergeMessageAqEvent, - MergeMessageAqSubRoots as MergeMessageAqSubRootsEvent, PublishMessage as PublishMessageEvent, } from "../generated/templates/Poll/Poll"; @@ -29,26 +27,6 @@ export function handleMergeMaciState(event: MergeMaciStateEvent): void { } } -export function handleMergeMessageAq(event: MergeMessageAqEvent): void { - const poll = Poll.load(event.address); - - if (poll) { - poll.messageRoot = event.params._messageRoot; - poll.updatedAt = event.block.timestamp; - poll.save(); - } -} - -export function handleMergeMessageAqSubRoots(event: MergeMessageAqSubRootsEvent): void { - const poll = Poll.load(event.address); - - if (poll) { - poll.numSrQueueOps = event.params._numSrQueueOps; - poll.updatedAt = event.block.timestamp; - poll.save(); - } -} - export function handlePublishMessage(event: PublishMessageEvent): void { const vote = new Vote(event.transaction.hash.concatI32(event.logIndex.toI32())); vote.data = event.params._message.data; diff --git a/subgraph/templates/subgraph.template.yaml b/subgraph/templates/subgraph.template.yaml index dbb6521eca..c7677e951b 100644 --- a/subgraph/templates/subgraph.template.yaml +++ b/subgraph/templates/subgraph.template.yaml @@ -56,10 +56,6 @@ templates: eventHandlers: - event: MergeMaciState(indexed uint256,indexed uint256) handler: handleMergeMaciState - - event: MergeMessageAq(indexed uint256) - handler: handleMergeMessageAq - - event: MergeMessageAqSubRoots(indexed uint256) - handler: handleMergeMessageAqSubRoots - event: PublishMessage((uint256[10]),(uint256,uint256)) handler: handlePublishMessage file: ./src/poll.ts diff --git a/subgraph/tests/common.ts b/subgraph/tests/common.ts index d3cec07095..e8aa030171 100644 --- a/subgraph/tests/common.ts +++ b/subgraph/tests/common.ts @@ -9,16 +9,13 @@ export const DEFAULT_MESSAGE_PROCESSOR_ADDRESS = Address.fromString("0x000000000 export const DEFAULT_TALLY_ADDRESS = Address.fromString("0x0000000000000000000000000000000000000003"); export function mockPollContract(): void { - createMockedFunction(DEFAULT_POLL_ADDRESS, "maxValues", "maxValues():(uint256,uint256)").returns([ - ethereum.Value.fromI32(10), + createMockedFunction(DEFAULT_POLL_ADDRESS, "maxVoteOptions", "maxVoteOptions():(uint256)").returns([ ethereum.Value.fromI32(20), ]); - createMockedFunction(DEFAULT_POLL_ADDRESS, "treeDepths", "treeDepths():(uint8,uint8,uint8,uint8)").returns([ + createMockedFunction(DEFAULT_POLL_ADDRESS, "treeDepths", "treeDepths():(uint8,uint8)").returns([ ethereum.Value.fromI32(1), ethereum.Value.fromI32(2), - ethereum.Value.fromI32(3), - ethereum.Value.fromI32(4), ]); createMockedFunction( diff --git a/subgraph/tests/poll/poll.test.ts b/subgraph/tests/poll/poll.test.ts index 2599545129..c6b550562f 100644 --- a/subgraph/tests/poll/poll.test.ts +++ b/subgraph/tests/poll/poll.test.ts @@ -4,12 +4,7 @@ import { test, describe, afterEach, clearStore, assert, beforeEach } from "match import { MACI, Poll } from "../../generated/schema"; import { handleDeployPoll } from "../../src/maci"; -import { - handleMergeMaciState, - handleMergeMessageAq, - handleMergeMessageAqSubRoots, - handlePublishMessage, -} from "../../src/poll"; +import { handleMergeMaciState, handlePublishMessage } from "../../src/poll"; import { DEFAULT_MESSAGE_PROCESSOR_ADDRESS, DEFAULT_POLL_ADDRESS, @@ -18,14 +13,9 @@ import { } from "../common"; import { createDeployPollEvent } from "../maci/utils"; -import { - createMergeMaciStateEvent, - createMergeMessageAqEvent, - createMergeMessageAqSubRootsEvent, - createPublishMessageEvent, -} from "./utils"; +import { createMergeMaciStateEvent, createPublishMessageEvent } from "./utils"; -export { handleMergeMaciState, handleMergeMessageAq, handleMergeMessageAqSubRoots, handlePublishMessage }; +export { handleMergeMaciState, handlePublishMessage }; describe("Poll", () => { beforeEach(() => { @@ -65,26 +55,6 @@ describe("Poll", () => { assert.assertTrue(maci.polls.load().length === 1); }); - test("should handle merge message queue properly", () => { - const event = createMergeMessageAqEvent(DEFAULT_POLL_ADDRESS, BigInt.fromI32(1)); - - handleMergeMessageAq(event); - - const poll = Poll.load(DEFAULT_POLL_ADDRESS)!; - - assert.fieldEquals("Poll", poll.id.toHex(), "messageRoot", "1"); - }); - - test("should handle merge message queue subroots properly", () => { - const event = createMergeMessageAqSubRootsEvent(DEFAULT_POLL_ADDRESS, BigInt.fromI32(1)); - - handleMergeMessageAqSubRoots(event); - - const poll = Poll.load(event.address)!; - - assert.fieldEquals("Poll", poll.id.toHex(), "numSrQueueOps", "1"); - }); - test("should handle publish message properly", () => { const event = createPublishMessageEvent( DEFAULT_POLL_ADDRESS, diff --git a/subgraph/tests/poll/utils.ts b/subgraph/tests/poll/utils.ts index 35ea5845dd..118618c0d3 100644 --- a/subgraph/tests/poll/utils.ts +++ b/subgraph/tests/poll/utils.ts @@ -2,12 +2,7 @@ import { Address, BigInt as GraphBN, ethereum } from "@graphprotocol/graph-ts"; // eslint-disable-next-line import/no-extraneous-dependencies import { newMockEvent } from "matchstick-as"; -import { - MergeMaciState, - MergeMessageAq, - MergeMessageAqSubRoots, - PublishMessage, -} from "../../generated/templates/Poll/Poll"; +import { MergeMaciState, PublishMessage } from "../../generated/templates/Poll/Poll"; export function createMergeMaciStateEvent(address: Address, stateRoot: GraphBN, numSignups: GraphBN): MergeMaciState { const event = changetype(newMockEvent()); @@ -19,24 +14,6 @@ export function createMergeMaciStateEvent(address: Address, stateRoot: GraphBN, return event; } -export function createMergeMessageAqEvent(address: Address, messageRoot: GraphBN): MergeMessageAq { - const event = changetype(newMockEvent()); - - event.parameters.push(new ethereum.EventParam("_messageRoot", ethereum.Value.fromUnsignedBigInt(messageRoot))); - event.address = address; - - return event; -} - -export function createMergeMessageAqSubRootsEvent(address: Address, numSrQueueOps: GraphBN): MergeMessageAqSubRoots { - const event = changetype(newMockEvent()); - - event.parameters.push(new ethereum.EventParam("_numSrQueueOps", ethereum.Value.fromUnsignedBigInt(numSrQueueOps))); - event.address = address; - - return event; -} - export function createPublishMessageEvent( address: Address, data: GraphBN[], diff --git a/website/static/img/completingAPoll.svg b/website/static/img/completingAPoll.svg index d9e14f2707..acc935bf0c 100644 --- a/website/static/img/completingAPoll.svg +++ b/website/static/img/completingAPoll.svg @@ -1,4 +1,4 @@ -
Coordinator
Coord...
Merge message acc queue
Merge message acc qu...
Merge state acc queue
Merge state acc queue
MACI.mergeStateAqSubRoots()
MACI.mergeStateAqSubRoots()
MACI.mergeStateAq()
MACI.mergeStateAq()
These need to be merged from a Poll contract
These need to be merged from...
Poll.mergeMaciStateAq()
Poll.mergeMaciStateAq()
Poll.mergeMaciStateAqSubRoots()
Poll.mergeMaciStateAqSubRoots()
Generate the state and ballot roots commitment
Generate the state a...
hash([stateRootHash, emptyBallotRoot, 0])
hash([stateRootHash, emptyBallo...
Poll.mergeMessageAqSubRoots()
Poll.mergeMessageAqSubRoots()
Poll.mergeMessageAq()
Poll.mergeMessageAq()
Merging the stateAq also results in
Merging the stateA...
Text is not SVG - cannot display
\ No newline at end of file +
Coordinator
Coord...
Merge message acc queue
Merge message acc qu...
Merge state acc queue
Merge state acc queue
MACI.mergeStateAqSubRoots()
MACI.mergeStateAqSubRoots()
MACI.mergeStateAq()
MACI.mergeStateAq()
These need to be merged from a Poll contract
These need to be merged from...
Poll.mergeMaciStateAq()
Poll.mergeMaciStateAq()
Poll.mergeMaciStateAqSubRoots()
Poll.mergeMaciStateAqSubRoots()
Generate the state and ballot roots commitment
Generate the state a...
hash([stateRootHash, emptyBallotRoot, 0])
hash([stateRootHash, emptyBallo...
Poll.mergeMessageAq()
Poll.mergeMessageAq()
Merging the stateAq also results in
Merging the stateA...
Text is not SVG - cannot display
\ No newline at end of file