diff --git a/.github/scripts/ceremony-param-tests-c-witness.sh b/.github/scripts/ceremony-param-tests-c-witness.sh deleted file mode 100755 index f3d00157f8..0000000000 --- a/.github/scripts/ceremony-param-tests-c-witness.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -set -e - -cd "$(dirname "$0")" -cd ../../cli - -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js deployVkRegistry -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js setVerifyingKeys \ - --state-tree-depth 6 \ - --int-state-tree-depth 2 \ - --msg-tree-depth 8 \ - --vote-option-tree-depth 3 \ - --msg-batch-depth 2 \ - --process-messages-zkey ./zkeys/processMessages_6-8-2-3.zkey \ - --tally-votes-zkey ./zkeys/tallyVotes_6-2-3.zkey \ - -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js create -s 6 -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js deployPoll \ - -pk macipk.ea638a3366ed91f2e955110888573861f7c0fc0bb5fb8b8dca9cd7a08d7d6b93 \ - --duration 300 \ - --max-messages 390625 \ - --max-vote-options 125 \ - --int-state-tree-depth 2 \ - --msg-tree-depth 8 \ - --msg-batch-depth 2 \ - --vote-option-tree-depth 3 \ - -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js signup \ - --pubkey macipk.e743ffb5298ef0f5c1f63b6464a48fea19ea7ee5a885c67ae1b24a1d04f03f07 \ - -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js publish \ - --pubkey macipk.e743ffb5298ef0f5c1f63b6464a48fea19ea7ee5a885c67ae1b24a1d04f03f07 \ - --privkey macisk.0ab0281365e01cff60afc62310daec765e590487bf989a7c4986ebc3fd49895e \ - --state-index 1 \ - --vote-option-index 0 \ - --new-vote-weight 9 \ - --nonce 1 \ - --poll-id 0 \ - -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js publish \ - --pubkey macipk.e743ffb5298ef0f5c1f63b6464a48fea19ea7ee5a885c67ae1b24a1d04f03f07 \ - --privkey macisk.0ab0281365e01cff60afc62310daec765e590487bf989a7c4986ebc3fd49895e \ - --state-index 1 \ - --vote-option-index 1 \ - --new-vote-weight 9 \ - --nonce 2 \ - --poll-id 0 \ - -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js timeTravel -s 300 -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js mergeSignups --poll-id 0 -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js mergeMessages --poll-id 0 -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js genProofs \ - --privkey macisk.1751146b59d32e3c0d7426de411218172428263f93b2fc4d981c036047a4d8c0 \ - --poll-id 0 \ - --rapidsnark ~/rapidsnark/build/prover \ - --process-zkey ./zkeys/processMessages_6-8-2-3.zkey \ - --tally-zkey ./zkeys/tallyVotes_6-2-3.zkey \ - --tally-file tally.json \ - --output proofs/ \ - --tally-witnessgen ./zkeys/tallyVotes_6-2-3 \ - --process-witnessgen ./zkeys/processMessages_6-8-2-3 \ - -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js proveOnChain \ - --poll-id 0 \ - --proof-dir proofs/ \ - -q true -HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js verify \ - --poll-id 0 \ - --tally-file tally.json \ - -q true diff --git a/.github/workflows/nightly-ceremony.yml b/.github/workflows/nightly-ceremony.yml index 3e5267d246..9747439d99 100644 --- a/.github/workflows/nightly-ceremony.yml +++ b/.github/workflows/nightly-ceremony.yml @@ -1,8 +1,9 @@ name: Nightly Ceremony on: - schedule: - - cron: 0 0 * * * + push: + branches: + - test/stress-test env: STATE_TREE_DEPTH: ${{ vars.STATE_TREE_DEPTH }} @@ -57,8 +58,8 @@ jobs: - name: Download ceremony artifacts run: ./.github/scripts/download-ceremony-artifacts.sh - - name: Run e2e tests - run: ./.github/scripts/ceremony-param-tests-c-witness.sh + - name: Run stress tests + run: pnpm run test:stress - name: Stop Hardhat if: always() diff --git a/cli/package.json b/cli/package.json index 65db33b830..fb4749c55a 100644 --- a/cli/package.json +++ b/cli/package.json @@ -14,7 +14,7 @@ "build": "tsc -p tsconfig.build.json", "postbuild": "cp package.json ./build", "types": "tsc -p tsconfig.json --noEmit", - "test": "nyc ts-mocha --exit tests/**/*.test.ts", + "test": "nyc ts-mocha --exit tests/e2e/*.test.ts tests/unit/*.test.ts", "test:e2e": "ts-mocha --exit tests/e2e.test.ts", "test:e2e-subsidy": "ts-mocha --exit tests/e2e.subsidy.test.ts", "test:keyChange": "ts-mocha --exit tests/keyChange.test.ts", @@ -24,7 +24,8 @@ "test:genKeypair": "ts-mocha --exit tests/unit/genKeyPair.test.ts", "test:timeTravel": "ts-mocha --exit tests/unit/timeTravel.test.ts", "test:fundWallet": "ts-mocha --exit tests/unit/fundWallet.test.ts", - "test:utils": "ts-mocha --exit tests/unit/utils.test.ts" + "test:utils": "ts-mocha --exit tests/unit/utils.test.ts", + "test:stress": "ts-mocha --exit tests/stress/*.test.ts" }, "dependencies": { "@commander-js/extra-typings": "^11.1.0", diff --git a/cli/tests/stress/stress.test.ts b/cli/tests/stress/stress.test.ts new file mode 100644 index 0000000000..fbdf4d9b2f --- /dev/null +++ b/cli/tests/stress/stress.test.ts @@ -0,0 +1,412 @@ +import { genRandomSalt } from "maci-crypto"; +import { Keypair } from "maci-domainobjs"; + +import { + deploy, + deployPoll, + deployVkRegistryContract, + genProofs, + mergeMessages, + mergeSignups, + proveOnChain, + publish, + setVerifyingKeys, + signup, + timeTravel, + verify, +} from "../../ts/commands"; +import { DeployedContracts, PollContracts } from "../../ts/utils"; +import { + coordinatorPrivKey, + coordinatorPubKey, + maxMessages, + maxVoteOptions, + processMessageTestZkeyPath, + tallyVotesTestZkeyPath, + testProcessMessagesWasmPath, + testProcessMessagesWitnessPath, + testProofsDirPath, + testRapidsnarkPath, + testTallyFilePath, + testTallyVotesWasmPath, + testTallyVotesWitnessPath, +} from "../constants"; +import { cleanVanilla, isArm } from "../utils"; + +describe("stress tests", function test() { + const messageTreeDepth = 8; + const stateTreeDepth = 6; + const voteOptionTreeDepth = 3; + const messageBatchDepth = 2; + const intStateTreeDepth = 2; + + const pollDuration = 600; + + const useWasm = isArm(); + this.timeout(900000); + + let maciAddresses: DeployedContracts; + let pollAddresses: PollContracts; + + // before all tests we deploy the vk registry contract and set the verifying keys + before(async () => { + // we deploy the vk registry contract + await deployVkRegistryContract(true); + // we set the verifying keys + await setVerifyingKeys( + stateTreeDepth, + intStateTreeDepth, + messageTreeDepth, + voteOptionTreeDepth, + messageBatchDepth, + processMessageTestZkeyPath, + tallyVotesTestZkeyPath, + ); + }); + + const users = Array(100).fill(new Keypair()); + + describe("1 user, 2 messages", () => { + after(() => { + cleanVanilla(); + }); + + before(async () => { + // deploy the smart contracts + maciAddresses = await deploy(stateTreeDepth); + // deploy a poll contract + pollAddresses = await deployPoll( + pollDuration, + maxMessages, + maxVoteOptions, + intStateTreeDepth, + messageBatchDepth, + messageBatchDepth, + voteOptionTreeDepth, + coordinatorPubKey, + ); + }); + + it("should signup 1 user", async () => { + await signup(users[0].pubKey.serialize()); + }); + + it("should publish 2 messages", async () => { + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (let i = 0; i < 2; i += 1) { + const randomVoteOption = Math.floor(Math.random() * 126); + const randomVoteWeight = Math.floor(Math.random() * 10); + // eslint-disable-next-line no-await-in-loop + await publish( + users[0].pubKey.serialize(), + i, + randomVoteOption, + 1, + 0, + randomVoteWeight, + maciAddresses.maciAddress, + genRandomSalt().toString(), + users[0].privKey.serialize(), + ); + } + }); + + it("should generate zk-SNARK proofs and verify them", async () => { + await timeTravel(pollDuration); + await mergeMessages(0, maciAddresses.maciAddress); + await mergeSignups(0, maciAddresses.maciAddress); + const tallyFileData = await genProofs( + testProofsDirPath, + testTallyFilePath, + tallyVotesTestZkeyPath, + processMessageTestZkeyPath, + 0, + undefined, + undefined, + testRapidsnarkPath, + testProcessMessagesWitnessPath, + testTallyVotesWitnessPath, + undefined, + coordinatorPrivKey, + maciAddresses.maciAddress, + undefined, + testProcessMessagesWasmPath, + testTallyVotesWasmPath, + undefined, + useWasm, + ); + await proveOnChain("0", testProofsDirPath); + await verify( + "0", + testTallyFilePath, + tallyFileData, + maciAddresses.maciAddress, + pollAddresses.tally, + pollAddresses.subsidy, + ); + }); + }); + + describe("100 signups, 100 messages", () => { + after(() => { + cleanVanilla(); + }); + + before(async () => { + // deploy the smart contracts + maciAddresses = await deploy(stateTreeDepth); + // deploy a poll contract + pollAddresses = await deployPoll( + pollDuration, + maxMessages, + maxVoteOptions, + intStateTreeDepth, + messageBatchDepth, + messageBatchDepth, + voteOptionTreeDepth, + coordinatorPubKey, + ); + }); + + it("should signup 100 users", async () => { + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (let i = 0; i < users.length; i += 1) { + // eslint-disable-next-line no-await-in-loop + await signup(users[i].pubKey.serialize()); + } + }); + + it("should publish 100 messages", async () => { + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (let i = 0; i < users.length; i += 1) { + const randomVoteOption = Math.floor(Math.random() * 126); + const randomVoteWeight = Math.floor(Math.random() * 10); + // eslint-disable-next-line no-await-in-loop + await publish( + users[i].pubKey.serialize(), + i, + randomVoteOption, + 1, + 0, + randomVoteWeight, + maciAddresses.maciAddress, + genRandomSalt().toString(), + users[i].privKey.serialize(), + ); + } + }); + + it("should generate zk-SNARK proofs and verify them", async () => { + await timeTravel(pollDuration); + await mergeMessages(0, maciAddresses.maciAddress); + await mergeSignups(0, maciAddresses.maciAddress); + const tallyFileData = await genProofs( + testProofsDirPath, + testTallyFilePath, + tallyVotesTestZkeyPath, + processMessageTestZkeyPath, + 0, + undefined, + undefined, + testRapidsnarkPath, + testProcessMessagesWitnessPath, + testTallyVotesWitnessPath, + undefined, + coordinatorPrivKey, + maciAddresses.maciAddress, + undefined, + testProcessMessagesWasmPath, + testTallyVotesWasmPath, + undefined, + useWasm, + ); + await proveOnChain("0", testProofsDirPath); + await verify( + "0", + testTallyFilePath, + tallyFileData, + maciAddresses.maciAddress, + pollAddresses.tally, + pollAddresses.subsidy, + ); + }); + }); + + describe("50 signups, 200 messages", () => { + after(() => { + cleanVanilla(); + }); + + before(async () => { + // deploy the smart contracts + maciAddresses = await deploy(stateTreeDepth); + // deploy a poll contract + pollAddresses = await deployPoll( + pollDuration, + maxMessages, + maxVoteOptions, + intStateTreeDepth, + messageBatchDepth, + messageBatchDepth, + voteOptionTreeDepth, + coordinatorPubKey, + ); + }); + + it("should signup 50 users", async () => { + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (let i = 0; i < 50; i += 1) { + // eslint-disable-next-line no-await-in-loop + await signup(users[i].pubKey.serialize()); + } + }); + + it("should publish 200 messages (some for non signed up users)", async () => { + for (let i = 0; i < 2; i += 1) { + const jitter = Math.floor(Math.random() * 10); + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (let j = 0; j < users.length; j += 1) { + const randomVoteOption = Math.floor(Math.random() * 126); + const randomVoteWeight = Math.floor(Math.random() * 10); + // eslint-disable-next-line no-await-in-loop + await publish( + users[j].pubKey.serialize(), + j, + randomVoteOption * jitter, + 1, + 0, + randomVoteWeight * jitter, + maciAddresses.maciAddress, + genRandomSalt().toString(), + users[j].privKey.serialize(), + ); + } + } + }); + + it("should generate zk-SNARK proofs and verify them", async () => { + await timeTravel(pollDuration); + await mergeMessages(0, maciAddresses.maciAddress); + await mergeSignups(0, maciAddresses.maciAddress); + const tallyFileData = await genProofs( + testProofsDirPath, + testTallyFilePath, + tallyVotesTestZkeyPath, + processMessageTestZkeyPath, + 0, + undefined, + undefined, + testRapidsnarkPath, + testProcessMessagesWitnessPath, + testTallyVotesWitnessPath, + undefined, + coordinatorPrivKey, + maciAddresses.maciAddress, + undefined, + testProcessMessagesWasmPath, + testTallyVotesWasmPath, + undefined, + useWasm, + ); + await proveOnChain("0", testProofsDirPath); + await verify( + "0", + testTallyFilePath, + tallyFileData, + maciAddresses.maciAddress, + pollAddresses.tally, + pollAddresses.subsidy, + ); + }); + }); + + describe("1000 signups, 5000 messages", () => { + const thousandUsers = Array(1000).fill(new Keypair()); + + after(() => { + cleanVanilla(); + }); + + before(async () => { + // deploy the smart contracts + maciAddresses = await deploy(stateTreeDepth); + // deploy a poll contract + pollAddresses = await deployPoll( + pollDuration, + maxMessages, + maxVoteOptions, + intStateTreeDepth, + messageBatchDepth, + messageBatchDepth, + voteOptionTreeDepth, + coordinatorPubKey, + ); + }); + + it("should signup 1000 users", async () => { + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (let i = 0; i < thousandUsers.length; i += 1) { + // eslint-disable-next-line no-await-in-loop + await signup(thousandUsers[i].pubKey.serialize()); + } + }); + + it("should publish 5000 messages (some for non signed up users)", async () => { + for (let i = 0; i < 5; i += 1) { + const jitter = Math.floor(Math.random() * 10); + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (let j = 0; j < thousandUsers.length; j += 1) { + const randomVoteOption = Math.floor(Math.random() * 126); + const randomVoteWeight = Math.floor(Math.random() * 10); + // eslint-disable-next-line no-await-in-loop + await publish( + thousandUsers[j].pubKey.serialize(), + j, + randomVoteOption * jitter, + 1, + 0, + randomVoteWeight * jitter, + maciAddresses.maciAddress, + genRandomSalt().toString(), + thousandUsers[j].privKey.serialize(), + ); + } + } + }); + + it("should generate zk-SNARK proofs and verify them", async () => { + await timeTravel(pollDuration); + await mergeMessages(0, maciAddresses.maciAddress); + await mergeSignups(0, maciAddresses.maciAddress); + const tallyFileData = await genProofs( + testProofsDirPath, + testTallyFilePath, + tallyVotesTestZkeyPath, + processMessageTestZkeyPath, + 0, + undefined, + undefined, + testRapidsnarkPath, + testProcessMessagesWitnessPath, + testTallyVotesWitnessPath, + undefined, + coordinatorPrivKey, + maciAddresses.maciAddress, + undefined, + testProcessMessagesWasmPath, + testTallyVotesWasmPath, + undefined, + useWasm, + ); + await proveOnChain("0", testProofsDirPath); + await verify( + "0", + testTallyFilePath, + tallyFileData, + maciAddresses.maciAddress, + pollAddresses.tally, + pollAddresses.subsidy, + ); + }); + }); +}); diff --git a/package.json b/package.json index 2930818d2c..ab9e386d3b 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "test:cli": "lerna run test --scope \"maci-cli\"", "test:integration": "lerna run test --scope \"maci-integrationtests\"", "test": "lerna run test --ignore maci-integrationtests --ignore maci-cli", + "test:stress": "lerna run test:stress --scope \"maci-cli\"", "types": "lerna run types", "typedoc": "typedoc --plugin typedoc-plugin-markdown --options typedoc.json", "prepare": "is-ci || husky install"