diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 5de344b01f..0fa7bc028a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -62,13 +62,22 @@ jobs: wget -qO /home/runner/work/maci/.local/bin/circom https://github.com/iden3/circom/releases/download/v2.1.6/circom-linux-amd64 chmod +x /home/runner/work/maci/.local/bin/circom - - name: Generate zkeys + - name: Create zkeys folder run: | cd cli mkdir -p zkeys + + - name: Compile Circuits And Generate zkeys + run: | + cd circuits + export PATH=$PATH:/home/runner/work/maci/.local/bin + pnpm build-test-circuits-c + mv ./build/test/* ../cli/zkeys/ + cd ../cli wget -qO zkeys/powersOfTau28_hez_final_20.ptau https://maci-devops-zkeys.s3.ap-northeast-2.amazonaws.com/powersOfTau28_hez_final_20.ptau - pnpm exec zkey-manager compile -c ./zkeys.config.yml - pnpm exec zkey-manager genZkeys -c ./zkeys.config.yml + pnpm snarkjs groth16 setup ./zkeys/ProcessMessages_10-2-1-2_test.r1cs ./zkeys/powersOfTau28_hez_final_20.ptau ./zkeys/ProcessMessages_10-2-1-2_test.0.zkey + pnpm snarkjs groth16 setup ./zkeys/TallyVotes_10-1-2_test.r1cs ./zkeys/powersOfTau28_hez_final_20.ptau ./zkeys/TallyVotes_10-1-2_test.0.zkey + pnpm snarkjs groth16 setup ./zkeys/SubsidyPerBatch_10-1-2_test.r1cs ./zkeys/powersOfTau28_hez_final_20.ptau ./zkeys/SubsidyPerBatch_10-1-2_test.0.zkey - name: ${{ matrix.command }} run: pnpm run ${{ matrix.command }} diff --git a/.github/workflows/reusable-e2e.yml b/.github/workflows/reusable-e2e.yml index 86d4da6a3f..07e6372d6c 100644 --- a/.github/workflows/reusable-e2e.yml +++ b/.github/workflows/reusable-e2e.yml @@ -79,14 +79,24 @@ jobs: wget -qO /home/runner/work/maci/.local/bin/circom https://github.com/iden3/circom/releases/download/v2.1.6/circom-linux-amd64 chmod +x /home/runner/work/maci/.local/bin/circom - - name: Generate zkeys + - name: Create zkeys folder if: ${{ env.CHANGED == 'true' }} run: | cd cli mkdir -p zkeys + + - name: Compile Circuits And Generate zkeys + if: ${{ env.CHANGED == 'true' }} + run: | + cd circuits + export PATH=$PATH:/home/runner/work/maci/.local/bin + pnpm build-test-circuits-c + mv ./build/test/* ../cli/zkeys/ + cd ../cli wget -qO zkeys/powersOfTau28_hez_final_20.ptau https://maci-devops-zkeys.s3.ap-northeast-2.amazonaws.com/powersOfTau28_hez_final_20.ptau - pnpm exec zkey-manager compile -c ./zkeys.config.yml - pnpm exec zkey-manager genZkeys -c ./zkeys.config.yml + pnpm snarkjs groth16 setup ./zkeys/ProcessMessages_10-2-1-2_test.r1cs ./zkeys/powersOfTau28_hez_final_20.ptau ./zkeys/ProcessMessages_10-2-1-2_test.0.zkey + pnpm snarkjs groth16 setup ./zkeys/TallyVotes_10-1-2_test.r1cs ./zkeys/powersOfTau28_hez_final_20.ptau ./zkeys/TallyVotes_10-1-2_test.0.zkey + pnpm snarkjs groth16 setup ./zkeys/SubsidyPerBatch_10-1-2_test.r1cs ./zkeys/powersOfTau28_hez_final_20.ptau ./zkeys/SubsidyPerBatch_10-1-2_test.0.zkey - name: Download zkeys if: ${{ env.CHANGED == 'false' }} diff --git a/.gitignore b/.gitignore index 4ce43cd3b3..c796621e68 100644 --- a/.gitignore +++ b/.gitignore @@ -149,6 +149,8 @@ cli/tallyinput.json cli/contractAddresses.json cli/contractAddresses.old.json +circuits/circom/test + # mdBook rendered files publish diff --git a/circuits/circom/circuits.json b/circuits/circom/circuits.json new file mode 100644 index 0000000000..412f078e02 --- /dev/null +++ b/circuits/circom/circuits.json @@ -0,0 +1,32 @@ +{ + "processMessages": { + "file": "processMessages", + "template": "ProcessMessages", + "params": [6, 8, 2, 3], + "pubs": ["inputHash"] + }, + "ProcessMessages_10-2-1-2_test": { + "file": "processMessages", + "template": "ProcessMessages", + "params": [10, 2, 1, 2], + "pubs": ["inputHash"] + }, + "tallyVotes": { + "file": "tallyVotes", + "template": "TallyVotes", + "params": [6, 2, 3], + "pubs": ["inputHash"] + }, + "TallyVotes_10-1-2_test": { + "file": "tallyVotes", + "template": "TallyVotes", + "params": [10, 1, 2], + "pubs": ["inputHash"] + }, + "SubsidyPerBatch_10-1-2_test": { + "file": "subsidy", + "template": "SubsidyPerBatch", + "params": [10, 1, 2], + "pubs": ["inputHash"] + } +} diff --git a/circuits/circom/ecdh.circom b/circuits/circom/ecdh.circom index e1b7e11cfe..f9c6451430 100644 --- a/circuits/circom/ecdh.circom +++ b/circuits/circom/ecdh.circom @@ -1,6 +1,8 @@ pragma circom 2.0.0; -include "../node_modules/circomlib/circuits/bitify.circom"; -include "../node_modules/circomlib/circuits/escalarmulany.circom"; + +// circomlib imports +include "./bitify.circom"; +include "./escalarmulany.circom"; template Ecdh() { // Note: the private key needs to be hashed and pruned first diff --git a/circuits/circom/float.circom b/circuits/circom/float.circom index 3355db117f..f2d479fde7 100644 --- a/circuits/circom/float.circom +++ b/circuits/circom/float.circom @@ -1,7 +1,9 @@ pragma circom 2.0.0; -include "../node_modules/circomlib/circuits/bitify.circom"; -include "../node_modules/circomlib/circuits/comparators.circom"; -include "../node_modules/circomlib/circuits/mux1.circom"; + +// circomlib imports +include "./bitify.circom"; +include "./comparators.circom"; +include "./mux1.circom"; template msb(n) { // require in < 2**n diff --git a/circuits/circom/hasherPoseidon.circom b/circuits/circom/hasherPoseidon.circom index 1a158125a0..d40be2e686 100644 --- a/circuits/circom/hasherPoseidon.circom +++ b/circuits/circom/hasherPoseidon.circom @@ -1,4 +1,6 @@ pragma circom 2.0.0; + +// local imports include "./poseidon/poseidonHashT3.circom"; include "./poseidon/poseidonHashT4.circom"; include "./poseidon/poseidonHashT5.circom"; diff --git a/circuits/circom/hasherSha256.circom b/circuits/circom/hasherSha256.circom index d28a5c5cbd..644559ea2d 100644 --- a/circuits/circom/hasherSha256.circom +++ b/circuits/circom/hasherSha256.circom @@ -1,6 +1,8 @@ pragma circom 2.0.0; -include "../node_modules/circomlib/circuits/sha256/sha256.circom"; -include "../node_modules/circomlib/circuits/bitify.circom"; + +// circomlib imports +include "./sha256/sha256.circom"; +include "./bitify.circom"; template Sha256HashLeftRight() { signal input left; diff --git a/circuits/circom/messageHasher.circom b/circuits/circom/messageHasher.circom index 6431b8a125..b0459b128e 100644 --- a/circuits/circom/messageHasher.circom +++ b/circuits/circom/messageHasher.circom @@ -1,4 +1,6 @@ pragma circom 2.0.0; + +// local import include "./hasherPoseidon.circom"; // hash a MACI message together with the public key diff --git a/circuits/circom/messageToCommand.circom b/circuits/circom/messageToCommand.circom index a6972a0b5b..25ff4c916d 100644 --- a/circuits/circom/messageToCommand.circom +++ b/circuits/circom/messageToCommand.circom @@ -1,8 +1,13 @@ pragma circom 2.0.0; + +// circomlib import +include "./bitify.circom"; +// @zk-kit import +include "./poseidon-cipher.circom"; + +// local imports include "./ecdh.circom"; include "./unpackElement.circom"; -include "../node_modules/circomlib/circuits/bitify.circom"; -include "../node_modules/circomlib/circuits/poseidon.circom"; // template that converts a MACI message // to a command (decrypts it) diff --git a/circuits/circom/messageValidator.circom b/circuits/circom/messageValidator.circom index 520d74a14d..7de6251832 100644 --- a/circuits/circom/messageValidator.circom +++ b/circuits/circom/messageValidator.circom @@ -1,4 +1,6 @@ pragma circom 2.0.0; + +// local imports include "./verifySignature.circom"; include "./utils.circom"; diff --git a/circuits/circom/poseidon/poseidon.circom b/circuits/circom/poseidon/poseidon.circom new file mode 100644 index 0000000000..2b79c6b752 --- /dev/null +++ b/circuits/circom/poseidon/poseidon.circom @@ -0,0 +1,16 @@ +pragma circom 2.0.0; + +// https://github.com/weijiekoh/circomlib/blob/feat/poseidon-encryption/circuits/poseidon.circom +include "./poseidon-cipher.circom"; + +template Poseidon_OLD(nInputs) { + signal input inputs[nInputs]; + signal output out; + + component strategy = PoseidonPerm(nInputs + 1); + strategy.inputs[0] <== 0; + for (var i = 0; i < nInputs; i++) { + strategy.inputs[i + 1] <== inputs[i]; + } + out <== strategy.out[0]; +} diff --git a/circuits/circom/poseidon/poseidonHashT3.circom b/circuits/circom/poseidon/poseidonHashT3.circom index 1be08c826b..4d87444989 100644 --- a/circuits/circom/poseidon/poseidonHashT3.circom +++ b/circuits/circom/poseidon/poseidonHashT3.circom @@ -1,12 +1,13 @@ pragma circom 2.0.0; -include "../../node_modules/circomlib/circuits/poseidon.circom"; + +include "./poseidon.circom"; template PoseidonHashT3() { var nInputs = 2; signal input inputs[nInputs]; signal output out; - component hasher = Poseidon(nInputs); + component hasher = Poseidon_OLD(nInputs); for (var i = 0; i < nInputs; i ++) { hasher.inputs[i] <== inputs[i]; } diff --git a/circuits/circom/poseidon/poseidonHashT4.circom b/circuits/circom/poseidon/poseidonHashT4.circom index f181aad1b0..3697aca816 100644 --- a/circuits/circom/poseidon/poseidonHashT4.circom +++ b/circuits/circom/poseidon/poseidonHashT4.circom @@ -1,12 +1,13 @@ pragma circom 2.0.0; -include "../../node_modules/circomlib/circuits/poseidon.circom"; + +include "./poseidon.circom"; template PoseidonHashT4() { var nInputs = 3; signal input inputs[nInputs]; signal output out; - component hasher = Poseidon(nInputs); + component hasher = Poseidon_OLD(nInputs); for (var i = 0; i < nInputs; i ++) { hasher.inputs[i] <== inputs[i]; } diff --git a/circuits/circom/poseidon/poseidonHashT5.circom b/circuits/circom/poseidon/poseidonHashT5.circom index 653f4b6b62..7100fd9b3f 100644 --- a/circuits/circom/poseidon/poseidonHashT5.circom +++ b/circuits/circom/poseidon/poseidonHashT5.circom @@ -1,12 +1,13 @@ pragma circom 2.0.0; -include "../../node_modules/circomlib/circuits/poseidon.circom"; + +include "./poseidon.circom"; template PoseidonHashT5() { var nInputs = 4; signal input inputs[nInputs]; signal output out; - component hasher = Poseidon(nInputs); + component hasher = Poseidon_OLD(nInputs); for (var i = 0; i < nInputs; i ++) { hasher.inputs[i] <== inputs[i]; } diff --git a/circuits/circom/poseidon/poseidonHashT6.circom b/circuits/circom/poseidon/poseidonHashT6.circom index 23f148967c..a74dcdedbb 100644 --- a/circuits/circom/poseidon/poseidonHashT6.circom +++ b/circuits/circom/poseidon/poseidonHashT6.circom @@ -1,12 +1,13 @@ pragma circom 2.0.0; -include "../../node_modules/circomlib/circuits/poseidon.circom"; + +include "./poseidon.circom"; template PoseidonHashT6() { var nInputs = 5; signal input inputs[nInputs]; signal output out; - component hasher = Poseidon(nInputs); + component hasher = Poseidon_OLD(nInputs); for (var i = 0; i < nInputs; i ++) { hasher.inputs[i] <== inputs[i]; } diff --git a/circuits/circom/privToPubKey.circom b/circuits/circom/privToPubKey.circom index af2d577be8..4a8c3cade5 100644 --- a/circuits/circom/privToPubKey.circom +++ b/circuits/circom/privToPubKey.circom @@ -1,6 +1,8 @@ pragma circom 2.0.0; -include "../node_modules/circomlib/circuits/bitify.circom"; -include "../node_modules/circomlib/circuits/escalarmulfix.circom"; + +// circomlib imports +include "./bitify.circom"; +include "./escalarmulfix.circom"; // convert a private key to a public key // @note the basepoint is the base point of the baby jubjub curve diff --git a/circuits/circom/processMessages.circom b/circuits/circom/processMessages.circom index 1d5c145713..2be3bd4564 100644 --- a/circuits/circom/processMessages.circom +++ b/circuits/circom/processMessages.circom @@ -1,11 +1,15 @@ pragma circom 2.0.0; + +// circomlib import +include "./mux1.circom"; + +// local imports include "./hasherSha256.circom"; include "./messageHasher.circom"; include "./messageToCommand.circom"; include "./privToPubKey.circom"; include "./stateLeafAndBallotTransformer.circom"; include "./trees/incrementalQuinTree.circom"; -include "../node_modules/circomlib/circuits/mux1.circom"; include "./utils.circom"; // Proves the correctness of processing a batch of messages. diff --git a/circuits/circom/stateLeafAndBallotTransformer.circom b/circuits/circom/stateLeafAndBallotTransformer.circom index 0fa30a46ef..f2106db6bb 100644 --- a/circuits/circom/stateLeafAndBallotTransformer.circom +++ b/circuits/circom/stateLeafAndBallotTransformer.circom @@ -1,6 +1,10 @@ pragma circom 2.0.0; + +// circomlib import +include "./mux1.circom"; + +// local import include "./messageValidator.circom"; -include "../node_modules/circomlib/circuits/mux1.circom"; // Apply a command to a state leaf and ballot. template StateLeafAndBallotTransformer() { diff --git a/circuits/circom/subsidy.circom b/circuits/circom/subsidy.circom index 850e704302..2edc1d8eef 100644 --- a/circuits/circom/subsidy.circom +++ b/circuits/circom/subsidy.circom @@ -1,12 +1,15 @@ pragma circom 2.0.0; -include "../node_modules/circomlib/circuits/comparators.circom"; + +// circomlib import +include "./comparators.circom"; + +// local imports include "./trees/incrementalQuinTree.circom"; include "./trees/calculateTotal.circom"; include "./trees/checkRoot.circom"; include "./hasherSha256.circom"; include "./hasherPoseidon.circom"; include "./unpackElement.circom"; - include "./float.circom"; /* diff --git a/circuits/circom/tallyVotes.circom b/circuits/circom/tallyVotes.circom index e6c5bb7a8f..48b657c908 100644 --- a/circuits/circom/tallyVotes.circom +++ b/circuits/circom/tallyVotes.circom @@ -1,5 +1,9 @@ pragma circom 2.0.0; -include "../node_modules/circomlib/circuits/comparators.circom"; + +// circomlib import +include "./comparators.circom"; + +// local imports include "./trees/incrementalQuinTree.circom"; include "./trees/calculateTotal.circom"; include "./trees/checkRoot.circom"; diff --git a/circuits/circom/test/processMessages_test.circom b/circuits/circom/test/ProcessMessages_10-2-1-2_test.circom similarity index 99% rename from circuits/circom/test/processMessages_test.circom rename to circuits/circom/test/ProcessMessages_10-2-1-2_test.circom index dad203f86e..66f0afb572 100644 --- a/circuits/circom/test/processMessages_test.circom +++ b/circuits/circom/test/ProcessMessages_10-2-1-2_test.circom @@ -1,5 +1,7 @@ pragma circom 2.0.0; + include "../processMessages.circom"; + /* stateTreeDepth, msgTreeDepth, diff --git a/circuits/circom/test/SubsidyPerBatch_10-1-2_test.circom b/circuits/circom/test/SubsidyPerBatch_10-1-2_test.circom new file mode 100644 index 0000000000..e5f3123ea4 --- /dev/null +++ b/circuits/circom/test/SubsidyPerBatch_10-1-2_test.circom @@ -0,0 +1,5 @@ +pragma circom 2.0.0; + +include "../subsidy.circom"; + +component main {public [inputHash]} = SubsidyPerBatch(10, 1, 2); diff --git a/circuits/circom/test/tallyVotes_test.circom b/circuits/circom/test/TallyVotes_10-1-2_test.circom similarity index 62% rename from circuits/circom/test/tallyVotes_test.circom rename to circuits/circom/test/TallyVotes_10-1-2_test.circom index 1461ddbc1c..fdd5bfb555 100644 --- a/circuits/circom/test/tallyVotes_test.circom +++ b/circuits/circom/test/TallyVotes_10-1-2_test.circom @@ -1,7 +1,5 @@ pragma circom 2.0.0; + include "../tallyVotes.circom"; component main {public [inputHash]} = TallyVotes(10, 1, 2); -/*stateTreeDepth,*/ -/*intStateTreeDepth,*/ -/*voteOptionTreeDepth*/ diff --git a/circuits/circom/test/calculateTotal_test.circom b/circuits/circom/test/calculateTotal_test.circom deleted file mode 100644 index 4c97dd234b..0000000000 --- a/circuits/circom/test/calculateTotal_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../trees/calculateTotal.circom"; - -component main = CalculateTotal(6); diff --git a/circuits/circom/test/ceremonyParams/processMessages_test.circom b/circuits/circom/test/ceremonyParams/processMessages_test.circom deleted file mode 100644 index 5bec01aca8..0000000000 --- a/circuits/circom/test/ceremonyParams/processMessages_test.circom +++ /dev/null @@ -1,10 +0,0 @@ -pragma circom 2.0.0; -include "../../processMessages.circom"; -/* -stateTreeDepth, -msgTreeDepth, -msgSubTreeDepth -voteOptionTreeDepth, -*/ - -component main {public [inputHash]} = ProcessMessages(6, 8, 2, 3); diff --git a/circuits/circom/test/ceremonyParams/tallyVotes_test.circom b/circuits/circom/test/ceremonyParams/tallyVotes_test.circom deleted file mode 100644 index 4000c82cab..0000000000 --- a/circuits/circom/test/ceremonyParams/tallyVotes_test.circom +++ /dev/null @@ -1,7 +0,0 @@ -pragma circom 2.0.0; -include "../../tallyVotes.circom"; - -component main {public [inputHash]} = TallyVotes(6, 2, 3); -/*stateTreeDepth,*/ -/*intStateTreeDepth,*/ -/*voteOptionTreeDepth*/ diff --git a/circuits/circom/test/ecdh_test.circom b/circuits/circom/test/ecdh_test.circom deleted file mode 100644 index 858fd3b455..0000000000 --- a/circuits/circom/test/ecdh_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../ecdh.circom"; - -component main {public [pubKey]} = Ecdh(); diff --git a/circuits/circom/test/hasher13_test.circom b/circuits/circom/test/hasher13_test.circom deleted file mode 100644 index 0bc3658b12..0000000000 --- a/circuits/circom/test/hasher13_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../hasherPoseidon.circom"; - -component main = Hasher13(); diff --git a/circuits/circom/test/hasher3_test.circom b/circuits/circom/test/hasher3_test.circom deleted file mode 100644 index 430c0279af..0000000000 --- a/circuits/circom/test/hasher3_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../hasherPoseidon.circom"; - -component main = Hasher3(); diff --git a/circuits/circom/test/hasher4_test.circom b/circuits/circom/test/hasher4_test.circom deleted file mode 100644 index d8bfff7208..0000000000 --- a/circuits/circom/test/hasher4_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../hasherPoseidon.circom"; - -component main = Hasher4(); diff --git a/circuits/circom/test/hasher5_test.circom b/circuits/circom/test/hasher5_test.circom deleted file mode 100644 index 1ac0c022e8..0000000000 --- a/circuits/circom/test/hasher5_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../hasherPoseidon.circom"; - -component main = Hasher5(); diff --git a/circuits/circom/test/hashleftright_test.circom b/circuits/circom/test/hashleftright_test.circom deleted file mode 100644 index 2f04db2b81..0000000000 --- a/circuits/circom/test/hashleftright_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../hasherPoseidon.circom"; - -component main = HashLeftRight(); diff --git a/circuits/circom/test/merkleTreeCheckRoot_test.circom b/circuits/circom/test/merkleTreeCheckRoot_test.circom deleted file mode 100644 index beceb46e4a..0000000000 --- a/circuits/circom/test/merkleTreeCheckRoot_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../trees/incrementalMerkleTree.circom"; - -component main = CheckRoot(4); diff --git a/circuits/circom/test/merkleTreeInclusionProof_test.circom b/circuits/circom/test/merkleTreeInclusionProof_test.circom deleted file mode 100644 index 25ce1322e9..0000000000 --- a/circuits/circom/test/merkleTreeInclusionProof_test.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.0.0; -include "../trees/incrementalMerkleTree.circom"; - -component main = MerkleTreeInclusionProof(4); - diff --git a/circuits/circom/test/merkleTreeLeafExists_test.circom b/circuits/circom/test/merkleTreeLeafExists_test.circom deleted file mode 100644 index 8a36c79760..0000000000 --- a/circuits/circom/test/merkleTreeLeafExists_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../trees/incrementalMerkleTree.circom"; - -component main {public [leaf, root]} = LeafExists(4); diff --git a/circuits/circom/test/messageHasher_test.circom b/circuits/circom/test/messageHasher_test.circom deleted file mode 100644 index 2e3569f9d5..0000000000 --- a/circuits/circom/test/messageHasher_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../messageHasher.circom"; - -component main = MessageHasher(); diff --git a/circuits/circom/test/messageToCommand_test.circom b/circuits/circom/test/messageToCommand_test.circom deleted file mode 100644 index 1ac9e3e9d6..0000000000 --- a/circuits/circom/test/messageToCommand_test.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.0.0; -include "../messageToCommand.circom"; - -component main = MessageToCommand(); - diff --git a/circuits/circom/test/messageValidator_test.circom b/circuits/circom/test/messageValidator_test.circom deleted file mode 100644 index 3a70bc2ab8..0000000000 --- a/circuits/circom/test/messageValidator_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../messageValidator.circom"; - -component main = MessageValidator(); diff --git a/circuits/circom/test/privToPubKey_test.circom b/circuits/circom/test/privToPubKey_test.circom deleted file mode 100644 index ed8f58d45e..0000000000 --- a/circuits/circom/test/privToPubKey_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../privToPubKey.circom"; - -component main = PrivToPubKey(); diff --git a/circuits/circom/test/processMessagesInputHasher_test.circom b/circuits/circom/test/processMessagesInputHasher_test.circom deleted file mode 100644 index 63f8bafa06..0000000000 --- a/circuits/circom/test/processMessagesInputHasher_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../processMessages.circom"; - -component main = ProcessMessagesInputHasher(); diff --git a/circuits/circom/test/quinBatchLeavesExists_test.circom b/circuits/circom/test/quinBatchLeavesExists_test.circom deleted file mode 100644 index 8fbaba0db2..0000000000 --- a/circuits/circom/test/quinBatchLeavesExists_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../trees/incrementalQuinTree.circom"; - -component main = QuinBatchLeavesExists(4, 2); diff --git a/circuits/circom/test/quinGeneratePathIndices_test.circom b/circuits/circom/test/quinGeneratePathIndices_test.circom deleted file mode 100644 index f7d1a69935..0000000000 --- a/circuits/circom/test/quinGeneratePathIndices_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../trees/incrementalQuinTree.circom"; - -component main = QuinGeneratePathIndices(4); diff --git a/circuits/circom/test/quinSelector_test.circom b/circuits/circom/test/quinSelector_test.circom deleted file mode 100644 index 200a74f944..0000000000 --- a/circuits/circom/test/quinSelector_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../trees/incrementalQuinTree.circom"; - -component main = QuinSelector(5); diff --git a/circuits/circom/test/quinTreeCheckRoot_test.circom b/circuits/circom/test/quinTreeCheckRoot_test.circom deleted file mode 100644 index 40973b70dd..0000000000 --- a/circuits/circom/test/quinTreeCheckRoot_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../trees/checkRoot.circom"; - -component main = QuinCheckRoot(3); diff --git a/circuits/circom/test/quinTreeInclusionProof_test.circom b/circuits/circom/test/quinTreeInclusionProof_test.circom deleted file mode 100644 index 20087dab13..0000000000 --- a/circuits/circom/test/quinTreeInclusionProof_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../trees/incrementalQuinTree.circom"; - -component main = QuinTreeInclusionProof(3); diff --git a/circuits/circom/test/quinTreeLeafExists_test.circom b/circuits/circom/test/quinTreeLeafExists_test.circom deleted file mode 100644 index 865b363bd3..0000000000 --- a/circuits/circom/test/quinTreeLeafExists_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../trees/incrementalQuinTree.circom"; - -component main = QuinLeafExists(3); diff --git a/circuits/circom/test/sha256HashLeftRight_test.circom b/circuits/circom/test/sha256HashLeftRight_test.circom deleted file mode 100644 index c57ae5787e..0000000000 --- a/circuits/circom/test/sha256HashLeftRight_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../hasherSha256.circom"; - -component main = Sha256HashLeftRight(); diff --git a/circuits/circom/test/sha256Hasher4_test.circom b/circuits/circom/test/sha256Hasher4_test.circom deleted file mode 100644 index b5ac66e644..0000000000 --- a/circuits/circom/test/sha256Hasher4_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../hasherSha256.circom"; - -component main = Sha256Hasher4(); diff --git a/circuits/circom/test/sha256Hasher6_test.circom b/circuits/circom/test/sha256Hasher6_test.circom deleted file mode 100644 index 9a6b5e5edf..0000000000 --- a/circuits/circom/test/sha256Hasher6_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../hasherSha256.circom"; - -component main = Sha256Hasher6(); diff --git a/circuits/circom/test/splicer_test.circom b/circuits/circom/test/splicer_test.circom deleted file mode 100644 index eec0e8db7a..0000000000 --- a/circuits/circom/test/splicer_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../trees/incrementalQuinTree.circom"; - -component main = Splicer(4); diff --git a/circuits/circom/test/stateLeafAndBallotTransformer_test.circom b/circuits/circom/test/stateLeafAndBallotTransformer_test.circom deleted file mode 100644 index de9c691903..0000000000 --- a/circuits/circom/test/stateLeafAndBallotTransformer_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../stateLeafAndBallotTransformer.circom"; - -component main = StateLeafAndBallotTransformer(); diff --git a/circuits/circom/test/unpackElement4_test.circom b/circuits/circom/test/unpackElement4_test.circom deleted file mode 100644 index 075bf5262e..0000000000 --- a/circuits/circom/test/unpackElement4_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../unpackElement.circom"; - -component main = UnpackElement(4); diff --git a/circuits/circom/test/unpackElement_test.circom b/circuits/circom/test/unpackElement_test.circom deleted file mode 100644 index bcc19f9c3e..0000000000 --- a/circuits/circom/test/unpackElement_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../unpackElement.circom"; - -component main = UnpackElement(5); diff --git a/circuits/circom/test/verifySignature_test.circom b/circuits/circom/test/verifySignature_test.circom deleted file mode 100644 index e93aed87df..0000000000 --- a/circuits/circom/test/verifySignature_test.circom +++ /dev/null @@ -1,4 +0,0 @@ -pragma circom 2.0.0; -include "../verifySignature.circom"; - -component main {public [pubKey, R8, S]} = VerifySignature(); diff --git a/circuits/circom/trees/checkRoot.circom b/circuits/circom/trees/checkRoot.circom index 15c3ab1d4f..d11d640b89 100644 --- a/circuits/circom/trees/checkRoot.circom +++ b/circuits/circom/trees/checkRoot.circom @@ -1,4 +1,6 @@ pragma circom 2.0.0; + +// local import include "../hasherPoseidon.circom"; // Given a list of leaves, compute the root of the merkle tree diff --git a/circuits/circom/trees/incrementalMerkleTree.circom b/circuits/circom/trees/incrementalMerkleTree.circom index 206d7fbb52..df8069c7ba 100644 --- a/circuits/circom/trees/incrementalMerkleTree.circom +++ b/circuits/circom/trees/incrementalMerkleTree.circom @@ -1,9 +1,11 @@ pragma circom 2.0.0; + // Refer to: // https://github.com/peppersec/tornado-mixer/blob/master/circuits/merkleTree.circom // https://github.com/semaphore-protocol/semaphore/blob/audited/circuits/circom/semaphore-base.circom +include "./mux1.circom"; -include "../../node_modules/circomlib/circuits/mux1.circom"; +// local import include "../hasherPoseidon.circom"; // recompute a merkle root from a leaf and a path diff --git a/circuits/circom/trees/incrementalQuinTree.circom b/circuits/circom/trees/incrementalQuinTree.circom index e95e4255f3..606048efa0 100644 --- a/circuits/circom/trees/incrementalQuinTree.circom +++ b/circuits/circom/trees/incrementalQuinTree.circom @@ -1,9 +1,13 @@ pragma circom 2.0.0; -include "../../node_modules/circomlib/circuits/bitify.circom"; -include "../../node_modules/circomlib/circuits/mux1.circom"; -include "../hasherPoseidon.circom"; + +// circomlib imports +include "./bitify.circom"; +include "./mux1.circom"; + +// local imports include "./calculateTotal.circom"; include "./checkRoot.circom"; +include "../hasherPoseidon.circom"; include "../utils.circom"; // This file contains circuits for quintary Merkle tree verifcation. diff --git a/circuits/circom/unpackElement.circom b/circuits/circom/unpackElement.circom index 760139b514..a7482ce82a 100644 --- a/circuits/circom/unpackElement.circom +++ b/circuits/circom/unpackElement.circom @@ -1,5 +1,7 @@ pragma circom 2.0.0; -include "../node_modules/circomlib/circuits/bitify.circom"; + +// circomlib import +include "./bitify.circom"; // Converts a field element (253 bits) to n 50-bit output elements // where n <= 5 and n > 1 diff --git a/circuits/circom/utils.circom b/circuits/circom/utils.circom index 6289bad3f7..98edd84f14 100644 --- a/circuits/circom/utils.circom +++ b/circuits/circom/utils.circom @@ -1,5 +1,7 @@ pragma circom 2.0.0; -include "../node_modules/circomlib/circuits/bitify.circom"; + +// circomlib import +include "./bitify.circom"; // the implicit assumption of LessThan is both inputs are at most n bits // so we need add range check for both inputs diff --git a/circuits/circom/verifySignature.circom b/circuits/circom/verifySignature.circom index 14baed12c5..863bde048e 100644 --- a/circuits/circom/verifySignature.circom +++ b/circuits/circom/verifySignature.circom @@ -1,12 +1,16 @@ pragma circom 2.0.0; -include "../node_modules/circomlib/circuits/compconstant.circom"; -include "../node_modules/circomlib/circuits/comparators.circom"; -include "../node_modules/circomlib/circuits/pointbits.circom"; -include "./poseidon/poseidonHashT6.circom"; -include "../node_modules/circomlib/circuits/bitify.circom"; -include "../node_modules/circomlib/circuits/escalarmulany.circom"; -include "../node_modules/circomlib/circuits/escalarmulfix.circom"; + +// circomlib imports +include "./compconstant.circom"; +include "./comparators.circom"; +include "./pointbits.circom"; +include "./bitify.circom"; +include "./escalarmulany.circom"; +include "./escalarmulfix.circom"; + +// local imports include "./hasherPoseidon.circom"; +include "./poseidon/poseidonHashT6.circom"; template EdDSAPoseidonVerifier_patched() { signal input Ax; diff --git a/circuits/circomkit.json b/circuits/circomkit.json new file mode 100644 index 0000000000..2d02bd969f --- /dev/null +++ b/circuits/circomkit.json @@ -0,0 +1,18 @@ +{ + "protocol": "groth16", + "prime": "bn128", + "version": "2.1.6", + "circuits": "./circom/circuits.json", + "dirPtau": "./ptau", + "dirCircuits": "./circom", + "dirInputs": "./inputs", + "dirBuild": "./build", + "optimization": 2, + "inspect": false, + "include": ["./node_modules/circomlib/circuits", "./node_modules/@zk-kit/circuits/circom"], + "groth16numContributions": 0, + "groth16askForEntropy": false, + "logLevel": "INFO", + "verbose": true, + "cWitness": true +} diff --git a/circuits/package.json b/circuits/package.json index 76a54baf37..6c84e04acf 100644 --- a/circuits/package.json +++ b/circuits/package.json @@ -8,6 +8,8 @@ "build-test-circuits-wasm": "bash ./scripts/build_arm.sh", "watch": "tsc --watch", "build": "tsc -p tsconfig.build.json", + "circom:build": "NODE_OPTIONS=--max-old-space-size=4096 circomkit compile", + "circom:setup": "NODE_OPTIONS=--max-old-space-size=4096 circomkit setup", "types": "tsc -p tsconfig.json --noEmit", "test": "ts-mocha --exit ts/__tests__/*.test.ts", "test:hasher": "ts-mocha --exit ts/__tests__/Hasher.test.ts", @@ -27,7 +29,9 @@ "test:incrementalQuinTree": "ts-mocha --exit ts/__tests__/IncrementalQuinTree.test.ts" }, "dependencies": { - "circomlib": "https://github.com/weijiekoh/circomlib#ac85e82c1914d47789e2032fb11ceb2cfdd38a2b", + "@zk-kit/circuits": "^0.3.0", + "circomkit": "^0.0.20", + "circomlib": "^2.0.5", "maci-core": "^1.1.2", "maci-crypto": "^1.1.2", "maci-domainobjs": "^1.1.2", @@ -40,7 +44,6 @@ "@types/node": "^20.11.0", "chai": "^4.3.10", "chai-as-promised": "^7.1.1", - "circom_tester": "^0.0.20", "mocha": "^10.2.0", "ts-mocha": "^10.0.0", "typescript": "^5.3.3" diff --git a/circuits/scripts/build_arm.sh b/circuits/scripts/build_arm.sh index 5b389f9b27..551ca8928b 100644 --- a/circuits/scripts/build_arm.sh +++ b/circuits/scripts/build_arm.sh @@ -10,7 +10,7 @@ for circuit in "$CIRCUITS_PATH"/*.circom do # Compile circuit echo "#### Compile Circuit: "$circuit"" - circom --O0 --wasm --r1cs --sym --output "$OUTPUT_PATH" "$circuit" + circom --O2 --wasm --r1cs --sym --output "$OUTPUT_PATH" -l ./node_modules/circomlib/circuits -l ./node_modules/@zk-kit/circuits/circom "$circuit" cd "$CWD" done diff --git a/circuits/scripts/build_intel.sh b/circuits/scripts/build_intel.sh index d94ff3b421..7a60d60442 100644 --- a/circuits/scripts/build_intel.sh +++ b/circuits/scripts/build_intel.sh @@ -2,20 +2,23 @@ # Assuming this script file is located under circuits/circom/scripts and CWD=$(pwd) -OUTPUT_PATH="../build/test/" +OUTPUT_PATH="./build/test/" CIRCUITS_PATH="./circom/test" mkdir -p "$OUTPUT_PATH" for circuit in "$CIRCUITS_PATH"/*.circom do - # Compile circuit + # Compile circuit with circom directly as we need the c witness file echo "#### Compile Circuit: "$circuit"" - circom --O0 --wasm --r1cs --c --sym --output "$OUTPUT_PATH" "$circuit" - + circom --O2 --wasm --r1cs --c --sym --output "$OUTPUT_PATH" -l ./node_modules/circomlib/circuits -l ./node_modules/@zk-kit/circuits/circom "$circuit" # Generate executable that can compute the witness of this circuit filename=$(basename "$circuit" .circom) cd ""$OUTPUT_PATH""$filename"_cpp" make + # Move the witness files out of their folders + mv "$filename" "../" + mv "$filename.dat" "../" + cd "$CWD" done diff --git a/circuits/ts/__tests__/CalculateTotal.test.ts b/circuits/ts/__tests__/CalculateTotal.test.ts index 4d96c56fb9..7ed1beabbe 100644 --- a/circuits/ts/__tests__/CalculateTotal.test.ts +++ b/circuits/ts/__tests__/CalculateTotal.test.ts @@ -1,16 +1,16 @@ -import { expect } from "chai"; -import tester from "circom_tester"; +import { type WitnessTester } from "circomkit"; -import path from "path"; - -import { getSignal } from "./utils/utils"; +import { circomkitInstance } from "./utils/utils"; describe("CalculateTotal circuit", () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `calculateTotal_test.circom`); - let circuit: tester.WasmTester; + let circuit: WitnessTester<["nums"], ["sum"]>; before(async () => { - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("calculateTotal", { + file: "trees/calculateTotal", + template: "CalculateTotal", + params: [6], + }); }); it("should correctly sum a list of values", async () => { @@ -25,9 +25,6 @@ describe("CalculateTotal circuit", () => { nums, }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); - const result = await getSignal(circuit, witness, "sum"); - expect(result.toString()).to.be.eq(sum.toString()); + await circuit.expectPass(circuitInputs, { sum }); }); }); diff --git a/circuits/ts/__tests__/CeremonyParams.test.ts b/circuits/ts/__tests__/CeremonyParams.test.ts index 60fcec82ba..76e8cbf541 100644 --- a/circuits/ts/__tests__/CeremonyParams.test.ts +++ b/circuits/ts/__tests__/CeremonyParams.test.ts @@ -1,12 +1,12 @@ import { expect } from "chai"; -import tester from "circom_tester"; +import { type WitnessTester } from "circomkit"; import { MaciState, Poll, packProcessMessageSmallVals, STATE_TREE_ARITY } from "maci-core"; -import { hash5, IncrementalQuinTree, stringifyBigInts, NOTHING_UP_MY_SLEEVE, AccQueue } from "maci-crypto"; +import { hash5, IncrementalQuinTree, NOTHING_UP_MY_SLEEVE, AccQueue } from "maci-crypto"; import { PrivKey, Keypair, PCommand, Message, Ballot } from "maci-domainobjs"; -import path from "path"; +import { IProcessMessagesInputs, ITallyVotesInputs } from "../types"; -import { generateRandomIndex, getSignal } from "./utils/utils"; +import { generateRandomIndex, getSignal, circomkitInstance } from "./utils/utils"; describe("Ceremony param tests", () => { const params = { @@ -45,14 +45,48 @@ describe("Ceremony param tests", () => { describe("ProcessMessage circuit", function test() { this.timeout(900000); - let circuit: tester.WasmTester; - let hasherCircuit: tester.WasmTester; + let circuit: WitnessTester< + [ + "inputHash", + "packedVals", + "pollEndTimestamp", + "msgRoot", + "msgs", + "msgSubrootPathElements", + "coordPrivKey", + "coordPubKey", + "encPubKeys", + "currentStateRoot", + "currentStateLeaves", + "currentStateLeavesPathElements", + "currentSbCommitment", + "currentSbSalt", + "newSbCommitment", + "newSbSalt", + "currentBallotRoot", + "currentBallots", + "currentBallotsPathElements", + "currentVoteWeights", + "currentVoteWeightsPathElements", + ] + >; + + let hasherCircuit: WitnessTester< + ["packedVals", "coordPubKey", "msgRoot", "currentSbCommitment", "newSbCommitment", "pollEndTimestamp"], + ["maxVoteOptions", "numSignUps", "batchStartIndex", "batchEndIndex", "hash"] + >; before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test/ceremonyParams", `processMessages_test.circom`); - circuit = await tester.wasm(circuitPath); - const hasherCircuitPath = path.resolve(__dirname, "../../circom/test/", `processMessagesInputHasher_test.circom`); - hasherCircuit = await tester.wasm(hasherCircuitPath); + circuit = await circomkitInstance.WitnessTester("processMessages", { + file: "processMessages", + template: "ProcessMessages", + params: [6, 8, 2, 3], + }); + + hasherCircuit = await circomkitInstance.WitnessTester("processMessageInputHasher", { + file: "processMessages", + template: "ProcessMessagesInputHasher", + }); }); describe("1 user, 2 messages", () => { @@ -150,11 +184,11 @@ describe("Ceremony param tests", () => { const currentStateRoot = maciState.stateTree.root; const currentBallotRoot = ballotTree.root; - const generatedInputs = poll.processMessages(pollId); + const inputs = poll.processMessages(pollId) as unknown as IProcessMessagesInputs; // Calculate the witness - const witness = await circuit.calculateWitness(generatedInputs, true); - await circuit.checkConstraints(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 @@ -172,30 +206,56 @@ describe("Ceremony param tests", () => { ); // Test the ProcessMessagesInputHasher circuit - const hasherCircuitInputs = stringifyBigInts({ + const hasherCircuitInputs = { packedVals, - coordPubKey: generatedInputs.coordPubKey, - msgRoot: generatedInputs.msgRoot, - currentSbCommitment: generatedInputs.currentSbCommitment, - newSbCommitment: generatedInputs.newSbCommitment, - pollEndTimestamp: generatedInputs.pollEndTimestamp, - }); - - const hasherWitness = await hasherCircuit.calculateWitness(hasherCircuitInputs, true); - await hasherCircuit.checkConstraints(hasherWitness); + coordPubKey: inputs.coordPubKey, + msgRoot: inputs.msgRoot, + currentSbCommitment: inputs.currentSbCommitment, + newSbCommitment: inputs.newSbCommitment, + pollEndTimestamp: inputs.pollEndTimestamp, + }; + + const hasherWitness = await hasherCircuit.calculateWitness(hasherCircuitInputs); + await hasherCircuit.expectConstraintPass(hasherWitness); const hash = await getSignal(hasherCircuit, hasherWitness, "hash"); - expect(hash.toString()).to.be.eq(generatedInputs.inputHash.toString()); + expect(hash.toString()).to.be.eq(inputs.inputHash.toString()); }); }); describe("TallyVotes circuit", function test() { this.timeout(900000); - let testCircuit: tester.WasmTester; + let testCircuit: WitnessTester< + [ + "stateRoot", + "ballotRoot", + "sbSalt", + "packedVals", + "sbCommitment", + "currentTallyCommitment", + "newTallyCommitment", + "inputHash", + "ballots", + "ballotPathElements", + "votes", + "currentResults", + "currentResultsRootSalt", + "currentSpentVoiceCreditSubtotal", + "currentSpentVoiceCreditSubtotalSalt", + "currentPerVOSpentVoiceCredits", + "currentPerVOSpentVoiceCreditsRootSalt", + "newResultsRootSalt", + "newPerVOSpentVoiceCreditsRootSalt", + "newSpentVoiceCreditSubtotalSalt", + ] + >; before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test/ceremonyParams", `tallyVotes_test.circom`); - testCircuit = await tester.wasm(circuitPath); + testCircuit = await circomkitInstance.WitnessTester("tallyVotes", { + file: "tallyVotes", + template: "TallyVotes", + params: [6, 2, 3], + }); }); describe("1 user, 2 messages", () => { @@ -263,13 +323,13 @@ describe("Ceremony param tests", () => { }); it("should produce the correct result commitments", async () => { - const generatedInputs = poll.tallyVotes(); + const generatedInputs = poll.tallyVotes() as unknown as ITallyVotesInputs; const witness = await testCircuit.calculateWitness(generatedInputs); - await testCircuit.checkConstraints(witness); + await testCircuit.expectConstraintPass(witness); }); it("should produce the correct result if the inital tally is not zero", async () => { - const generatedInputs = poll.tallyVotes(); + const generatedInputs = poll.tallyVotes() as unknown as ITallyVotesInputs; // Start the tally from non-zero value let randIdx = generateRandomIndex(Object.keys(generatedInputs).length); @@ -277,9 +337,10 @@ describe("Ceremony param tests", () => { randIdx = generateRandomIndex(Object.keys(generatedInputs).length); } - generatedInputs.currentResults[randIdx] = "1"; + generatedInputs.currentResults[randIdx] = 1n; + const witness = await testCircuit.calculateWitness(generatedInputs); - await testCircuit.checkConstraints(witness); + await testCircuit.expectConstraintPass(witness); }); }); }); diff --git a/circuits/ts/__tests__/Ecdh.test.ts b/circuits/ts/__tests__/Ecdh.test.ts index 893149925e..3baa883900 100644 --- a/circuits/ts/__tests__/Ecdh.test.ts +++ b/circuits/ts/__tests__/Ecdh.test.ts @@ -1,21 +1,20 @@ import chai, { expect } from "chai"; import chaiAsPromised from "chai-as-promised"; -import tester from "circom_tester"; -import { stringifyBigInts } from "maci-crypto"; +import { type WitnessTester } from "circomkit"; import { Keypair } from "maci-domainobjs"; -import path from "path"; - -import { getSignal } from "./utils/utils"; +import { circomkitInstance } from "./utils/utils"; chai.use(chaiAsPromised); describe("Public key derivation circuit", () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `ecdh_test.circom`); - let circuit: tester.WasmTester; + let circuit: WitnessTester<["privKey", "pubKey"], ["sharedKey"]>; before(async () => { - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("ecdh", { + file: "ecdh", + template: "Ecdh", + }); }); it("correctly computes a public key", async () => { @@ -24,55 +23,44 @@ describe("Public key derivation circuit", () => { const ecdhSharedKey = Keypair.genEcdhSharedKey(keypair.privKey, keypair2.pubKey); - const circuitInputs = stringifyBigInts({ - privKey: keypair.privKey.asCircuitInputs(), - pubKey: keypair2.pubKey.asCircuitInputs(), - }); - - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const circuitInputs = { + privKey: BigInt(keypair.privKey.asCircuitInputs()), + pubKey: keypair2.pubKey.rawPubKey as [bigint, bigint], + }; - const circuitEcdhSharedKey0 = await getSignal(circuit, witness, "sharedKey[0]"); - const circuitEcdhSharedKey1 = await getSignal(circuit, witness, "sharedKey[1]"); - expect(circuitEcdhSharedKey0.toString()).to.be.eq(ecdhSharedKey[0].toString()); - expect(circuitEcdhSharedKey1.toString()).to.be.eq(ecdhSharedKey[1].toString()); + await circuit.expectPass(circuitInputs, { sharedKey: [ecdhSharedKey[0], ecdhSharedKey[1]] }); }); it("should generate the same ECDH key given the same inputs", async () => { const keypair = new Keypair(); const keypair2 = new Keypair(); - const circuitInputs = stringifyBigInts({ - privKey: keypair.privKey.asCircuitInputs(), - pubKey: keypair2.pubKey.asCircuitInputs(), - }); - - let witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const circuitInputs = { + privKey: BigInt(keypair.privKey.asCircuitInputs()), + pubKey: keypair2.pubKey.asCircuitInputs() as unknown as bigint[], + }; - const circuitEcdhSharedKey0 = await getSignal(circuit, witness, "sharedKey[0]"); - const circuitEcdhSharedKey1 = await getSignal(circuit, witness, "sharedKey[1]"); + // calculate first time witness and check contraints + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); - witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + // read out + const out = await circuit.readWitnessSignals(witness, ["sharedKey"]); - const circuitEcdhSharedKey02 = await getSignal(circuit, witness, "sharedKey[0]"); - const circuitEcdhSharedKey12 = await getSignal(circuit, witness, "sharedKey[1]"); - - expect(circuitEcdhSharedKey0.toString()).to.be.eq(circuitEcdhSharedKey02.toString()); - expect(circuitEcdhSharedKey1.toString()).to.be.eq(circuitEcdhSharedKey12.toString()); + // calculate again + await circuit.expectPass(circuitInputs, { sharedKey: out.sharedKey }); }); it("should throw when given invalid inputs (pubKey too short)", async () => { const keypair = new Keypair(); const keypair2 = new Keypair(); - const circuitInputs = stringifyBigInts({ - privKey: keypair.privKey.asCircuitInputs(), - pubKey: keypair2.pubKey.asCircuitInputs().slice(0, 1), - }); + const circuitInputs = { + privKey: BigInt(keypair.privKey.asCircuitInputs()), + pubKey: keypair2.pubKey.asCircuitInputs().slice(0, 1) as unknown as [bigint, bigint], + }; - await expect(circuit.calculateWitness(circuitInputs, true)).to.be.rejectedWith( + await expect(circuit.calculateWitness(circuitInputs)).to.be.rejectedWith( "Not enough values for input signal pubKey", ); }); diff --git a/circuits/ts/__tests__/Hasher.test.ts b/circuits/ts/__tests__/Hasher.test.ts index 80b25a5330..042b49457c 100644 --- a/circuits/ts/__tests__/Hasher.test.ts +++ b/circuits/ts/__tests__/Hasher.test.ts @@ -1,32 +1,32 @@ import { expect } from "chai"; -import tester from "circom_tester"; -import { stringifyBigInts, genRandomSalt, sha256Hash, hashLeftRight, hash13, hash5, hash4, hash3 } from "maci-crypto"; +import { type WitnessTester } from "circomkit"; +import { genRandomSalt, sha256Hash, hashLeftRight, hash13, hash5, hash4, hash3 } from "maci-crypto"; import { PCommand, Keypair } from "maci-domainobjs"; -import path from "path"; - -import { getSignal } from "./utils/utils"; +import { getSignal, circomkitInstance } from "./utils/utils"; describe("Poseidon hash circuits", function test() { this.timeout(30000); - let circuit: tester.WasmTester; - describe("SHA256", () => { describe("Sha256HashLeftRight", () => { + let circuit: WitnessTester<["left", "right"], ["hash"]>; + before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `sha256HashLeftRight_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("sha256HashLeftRight", { + file: "hasherSha256", + template: "Sha256HashLeftRight", + }); }); it("should correctly hash two random values", async () => { const left = genRandomSalt(); const right = genRandomSalt(); - const circuitInputs = stringifyBigInts({ left, right }); + const circuitInputs = { left, right }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output = await getSignal(circuit, witness, "hash"); const outputJS = sha256Hash([left, right]); @@ -36,9 +36,13 @@ describe("Poseidon hash circuits", function test() { }); describe("Sha256Hasher4", () => { + let circuit: WitnessTester<["in"], ["hash"]>; + before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `sha256Hasher4_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("sha256Hasher4", { + file: "hasherSha256", + template: "Sha256Hasher4", + }); }); it("should correctly hash 4 random values", async () => { @@ -47,12 +51,12 @@ describe("Poseidon hash circuits", function test() { preImages.push(genRandomSalt()); } - const circuitInputs = stringifyBigInts({ + const circuitInputs = { in: preImages, - }); + }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output = await getSignal(circuit, witness, "hash"); const outputJS = sha256Hash(preImages); @@ -62,9 +66,13 @@ describe("Poseidon hash circuits", function test() { }); describe("Sha256Hasher6", () => { + let circuit: WitnessTester<["in"], ["hash"]>; + before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `sha256Hasher6_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("sha256Hasher6", { + file: "hasherSha256", + template: "Sha256Hasher6", + }); }); it("should correctly hash 6 random values", async () => { @@ -73,12 +81,12 @@ describe("Poseidon hash circuits", function test() { preImages.push(genRandomSalt()); } - const circuitInputs = stringifyBigInts({ + const circuitInputs = { in: preImages, - }); + }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output = await getSignal(circuit, witness, "hash"); const outputJS = sha256Hash(preImages); @@ -90,9 +98,13 @@ describe("Poseidon hash circuits", function test() { describe("Poseidon", () => { describe("Hasher5", () => { + let circuit: WitnessTester<["in"], ["hash"]>; + before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `hasher5_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("hasher5", { + file: "hasherPoseidon", + template: "Hasher5", + }); }); it("correctly hashes 5 random values", async () => { @@ -101,12 +113,12 @@ describe("Poseidon hash circuits", function test() { preImages.push(genRandomSalt()); } - const circuitInputs = stringifyBigInts({ + const circuitInputs = { in: preImages, - }); + }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output = await getSignal(circuit, witness, "hash"); const outputJS = hash5(preImages); @@ -116,9 +128,13 @@ describe("Poseidon hash circuits", function test() { }); describe("Hasher4", () => { + let circuit: WitnessTester<["in"], ["hash"]>; + before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `hasher4_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("hasher4", { + file: "hasherPoseidon", + template: "Hasher4", + }); }); it("correctly hashes 4 random values", async () => { @@ -127,12 +143,12 @@ describe("Poseidon hash circuits", function test() { preImages.push(genRandomSalt()); } - const circuitInputs = stringifyBigInts({ + const circuitInputs = { in: preImages, - }); + }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output = await getSignal(circuit, witness, "hash"); const outputJS = hash4(preImages); @@ -142,9 +158,13 @@ describe("Poseidon hash circuits", function test() { }); describe("Hasher3", () => { + let circuit: WitnessTester<["in"], ["hash"]>; + before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `hasher3_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("hasher3", { + file: "hasherPoseidon", + template: "Hasher3", + }); }); it("correctly hashes 3 random values", async () => { @@ -153,12 +173,12 @@ describe("Poseidon hash circuits", function test() { preImages.push(genRandomSalt()); } - const circuitInputs = stringifyBigInts({ + const circuitInputs = { in: preImages, - }); + }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output = await getSignal(circuit, witness, "hash"); const outputJS = hash3(preImages); @@ -168,9 +188,13 @@ describe("Poseidon hash circuits", function test() { }); describe("Hasher13", () => { + let circuit: WitnessTester<["in"], ["hash"]>; + before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `hasher13_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("hasher13", { + file: "hasherPoseidon", + template: "Hasher13", + }); }); it("should correctly hash 13 random values", async () => { @@ -178,12 +202,12 @@ describe("Poseidon hash circuits", function test() { for (let i = 0; i < 13; i += 1) { preImages.push(genRandomSalt()); } - const circuitInputs = stringifyBigInts({ + const circuitInputs = { in: preImages, - }); + }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output = await getSignal(circuit, witness, "hash"); const outputJS = hash13(preImages); @@ -193,19 +217,23 @@ describe("Poseidon hash circuits", function test() { }); describe("HashLeftRight", () => { + let circuit: WitnessTester<["left", "right"], ["hash"]>; + before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `hashleftright_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("hashLeftRight", { + file: "hasherPoseidon", + template: "HashLeftRight", + }); }); it("should correctly hash two random values", async () => { const left = genRandomSalt(); const right = genRandomSalt(); - const circuitInputs = stringifyBigInts({ left, right }); + const circuitInputs = { left, right }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output = await getSignal(circuit, witness, "hash"); const outputJS = hashLeftRight(left, right); @@ -217,14 +245,14 @@ describe("Poseidon hash circuits", function test() { const left = genRandomSalt(); const right = genRandomSalt(); - const circuitInputs = stringifyBigInts({ left, right }); + const circuitInputs = { left, right }; - let witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + let witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output = await getSignal(circuit, witness, "hash"); - witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output2 = await getSignal(circuit, witness, "hash"); expect(output.toString()).to.be.eq(output2.toString()); @@ -233,9 +261,13 @@ describe("Poseidon hash circuits", function test() { }); describe("MessageHasher", () => { + let circuit: WitnessTester<["in", "encPubKey"], ["hash"]>; + before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `messageHasher_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("messageHasher", { + file: "messageHasher", + template: "MessageHasher", + }); }); it("should correctly hash a message", async () => { @@ -259,12 +291,12 @@ describe("Poseidon hash circuits", function test() { const signature = command.sign(privKey); const message = command.encrypt(signature, ecdhSharedKey); const messageHash = message.hash(k.pubKey); - const circuitInputs = stringifyBigInts({ + const circuitInputs = { in: message.asCircuitInputs(), - encPubKey: k.pubKey.asCircuitInputs(), - }); - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + encPubKey: k.pubKey.asCircuitInputs() as unknown as [bigint, bigint], + }; + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const output = await getSignal(circuit, witness, "hash"); expect(output.toString()).to.be.eq(messageHash.toString()); }); diff --git a/circuits/ts/__tests__/IncrementalQuinTree.test.ts b/circuits/ts/__tests__/IncrementalQuinTree.test.ts index 330e6a7faf..73b25bcb6f 100644 --- a/circuits/ts/__tests__/IncrementalQuinTree.test.ts +++ b/circuits/ts/__tests__/IncrementalQuinTree.test.ts @@ -1,11 +1,9 @@ import chai, { expect } from "chai"; import chaiAsPromised from "chai-as-promised"; -import tester from "circom_tester"; -import { IncrementalQuinTree, hash5, stringifyBigInts } from "maci-crypto"; +import { type WitnessTester } from "circomkit"; +import { IncrementalQuinTree, hash5 } from "maci-crypto"; -import path from "path"; - -import { getSignal } from "./utils/utils"; +import { getSignal, circomkitInstance } from "./utils/utils"; chai.use(chaiAsPromised); @@ -15,56 +13,71 @@ describe("IncrementalQuinTree circuit", function test() { const leavesPerNode = 5; const treeDepth = 3; - let circuitLeafExists: tester.WasmTester; - let circuitGeneratePathIndices: tester.WasmTester; - let circuitQuinSelector: tester.WasmTester; - let splicerCircuit: tester.WasmTester; + let circuitLeafExists: WitnessTester<["leaf", "path_elements", "path_index", "root"]>; + let circuitGeneratePathIndices: WitnessTester<["in"], ["out"]>; + let circuitQuinSelector: WitnessTester<["in", "index"], ["out"]>; + let splicerCircuit: WitnessTester<["in", "leaf", "index"], ["out"]>; before(async () => { - circuitLeafExists = await tester.wasm( - path.resolve(__dirname, "../../circom/test", `quinTreeLeafExists_test.circom`), - ); - circuitGeneratePathIndices = await tester.wasm( - path.resolve(__dirname, "../../circom/test", `quinGeneratePathIndices_test.circom`), - ); - circuitQuinSelector = await tester.wasm(path.resolve(__dirname, "../../circom/test", `quinSelector_test.circom`)); - splicerCircuit = await tester.wasm(path.resolve(__dirname, "../../circom/test", `splicer_test.circom`)); + circuitLeafExists = await circomkitInstance.WitnessTester("quinLeafExists", { + file: "./trees/incrementalQuinTree", + template: "QuinLeafExists", + params: [3], + }); + + circuitGeneratePathIndices = await circomkitInstance.WitnessTester("quinGeneratePathIndices", { + file: "./trees/incrementalQuinTree", + template: "QuinGeneratePathIndices", + params: [4], + }); + + circuitQuinSelector = await circomkitInstance.WitnessTester("quinSelector", { + file: "./trees/incrementalQuinTree", + template: "QuinSelector", + params: [5], + }); + + splicerCircuit = await circomkitInstance.WitnessTester("splicer", { + file: "./trees/incrementalQuinTree", + template: "Splicer", + params: [4], + }); }); describe("QuinSelector", () => { it("should return the correct value", async () => { - const circuitInputs = stringifyBigInts({ + const circuitInputs = { index: 0n, in: [1n, 2n, 3n, 4n, 5n], - }); + }; - const witness = await circuitQuinSelector.calculateWitness(circuitInputs, true); - await circuitQuinSelector.checkConstraints(witness); + const witness = await circuitQuinSelector.calculateWitness(circuitInputs); + await circuitQuinSelector.expectConstraintPass(witness); const out = await getSignal(circuitQuinSelector, witness, "out"); expect(out.toString()).to.be.eq("1"); }); it("should throw when the index is out of range", async () => { - const circuitInputs = stringifyBigInts({ + const circuitInputs = { index: 5n, in: [1n, 2n, 3n, 4n, 5n], - }); + }; - await expect(circuitQuinSelector.calculateWitness(circuitInputs, true)).to.be.rejectedWith("Assert Failed."); + await expect(circuitQuinSelector.calculateWitness(circuitInputs)).to.be.rejectedWith("Assert Failed."); }); }); describe("Splicer", () => { it("should insert a value at the correct index", async () => { - const circuitInputs = stringifyBigInts({ + const circuitInputs = { in: [5n, 3n, 20n, 44n], leaf: 0n, index: 2n, - }); + }; - const witness = await splicerCircuit.calculateWitness(circuitInputs, true); - await splicerCircuit.checkConstraints(witness); + const witness = await splicerCircuit.calculateWitness(circuitInputs); + await splicerCircuit.expectConstraintPass(witness); const out1 = await getSignal(splicerCircuit, witness, "out[0]"); const out2 = await getSignal(splicerCircuit, witness, "out[1]"); @@ -81,12 +94,12 @@ describe("IncrementalQuinTree circuit", function test() { describe("QuinGeneratePathIndices", () => { it("should generate the correct path indices", async () => { - const circuitInputs = stringifyBigInts({ + const circuitInputs = { in: 30n, - }); + }; - const witness = await circuitGeneratePathIndices.calculateWitness(circuitInputs, true); - await circuitGeneratePathIndices.checkConstraints(witness); + const witness = await circuitGeneratePathIndices.calculateWitness(circuitInputs); + await circuitGeneratePathIndices.expectConstraintPass(witness); const out1 = await getSignal(circuitGeneratePathIndices, witness, "out[0]"); const out2 = await getSignal(circuitGeneratePathIndices, witness, "out[1]"); @@ -110,19 +123,19 @@ describe("IncrementalQuinTree circuit", function test() { const proof = tree.genMerklePath(2); - const circuitInputs = stringifyBigInts({ + const circuitInputs = { root: tree.root, leaf: 3n, path_elements: proof.pathElements, - path_index: proof.indices.map((index) => index.toString()), - }); + path_index: proof.indices, + }; - const witness = await circuitLeafExists.calculateWitness(circuitInputs, true); - await circuitLeafExists.checkConstraints(witness); + const witness = await circuitLeafExists.calculateWitness(circuitInputs); + await circuitLeafExists.expectConstraintPass(witness); }); it("should throw when provided an incorrect leaf", async () => { - const circuitInputs = stringifyBigInts({ + const circuitInputs = { root: 30n, leaf: 0n, path_elements: [ @@ -131,9 +144,9 @@ describe("IncrementalQuinTree circuit", function test() { [1n, 1n, 1n, 0n], ], path_index: [0n, 1n, 1n], - }); + }; - await expect(circuitLeafExists.calculateWitness(circuitInputs, true)).to.be.rejectedWith("Assert Failed."); + await expect(circuitLeafExists.calculateWitness(circuitInputs)).to.be.rejectedWith("Assert Failed."); }); }); }); diff --git a/circuits/ts/__tests__/MessageToCommand.test.ts b/circuits/ts/__tests__/MessageToCommand.test.ts index be4973fe86..125de6e656 100644 --- a/circuits/ts/__tests__/MessageToCommand.test.ts +++ b/circuits/ts/__tests__/MessageToCommand.test.ts @@ -1,18 +1,32 @@ import { expect } from "chai"; -import tester from "circom_tester"; -import { stringifyBigInts, genRandomSalt, genPrivKey } from "maci-crypto"; +import { type WitnessTester } from "circomkit"; +import { genRandomSalt, genPrivKey } from "maci-crypto"; import { Keypair, PCommand, PrivKey } from "maci-domainobjs"; -import path from "path"; - -import { getSignal } from "./utils/utils"; +import { circomkitInstance, getSignal } from "./utils/utils"; describe("MessageToCommand circuit", () => { - let circuit: tester.WasmTester; + let circuit: WitnessTester< + ["message", "encPubKey", "encPubKey"], + [ + "stateIndex", + "newPubKey", + "voteOptionIndex", + "newVoteWeight", + "nonce", + "pollId", + "salt", + "sigR8", + "sigS", + "packedCommandOut", + ] + >; before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `messageToCommand_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("messageToCommand", { + file: "messageToCommand", + template: "MessageToCommand", + }); }); it("should decrypt a Message and output the fields of a Command", async () => { @@ -41,14 +55,14 @@ describe("MessageToCommand circuit", () => { const signature = command.sign(privKey); const message = command.encrypt(signature, ecdhSharedKey); - const circuitInputs = stringifyBigInts({ + const circuitInputs = { message: message.asCircuitInputs(), - encPrivKey: privKey.asCircuitInputs(), - encPubKey: pubKey1.asCircuitInputs(), - }); + encPrivKey: privKey.asCircuitInputs() as unknown as bigint, + encPubKey: pubKey1.asCircuitInputs() as unknown as bigint[], + }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const stateIndexOut = await getSignal(circuit, witness, "stateIndex"); expect(command.stateIndex.toString()).to.be.eq(stateIndexOut.toString()); @@ -110,14 +124,14 @@ describe("MessageToCommand circuit", () => { const signature = command.sign(privKey); const message = command.encrypt(signature, ecdhSharedKey); - const circuitInputs = stringifyBigInts({ + const circuitInputs = { message: message.asCircuitInputs(), // invalid private key - encPrivKey: new PrivKey(genPrivKey()).asCircuitInputs(), - encPubKey: pubKey1.asCircuitInputs(), - }); + encPrivKey: new PrivKey(genPrivKey()).asCircuitInputs() as unknown as bigint, + encPubKey: pubKey1.asCircuitInputs() as unknown as [bigint, bigint], + }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); }); }); diff --git a/circuits/ts/__tests__/MessageValidator.test.ts b/circuits/ts/__tests__/MessageValidator.test.ts index 436e3d4796..f678c14a4a 100644 --- a/circuits/ts/__tests__/MessageValidator.test.ts +++ b/circuits/ts/__tests__/MessageValidator.test.ts @@ -1,22 +1,43 @@ import { expect } from "chai"; -import tester from "circom_tester"; -import { stringifyBigInts, genRandomSalt } from "maci-crypto"; +import { type WitnessTester } from "circomkit"; +import { SignalValueType } from "circomkit/dist/types/circuit"; +import { genRandomSalt } from "maci-crypto"; import { PCommand, Keypair } from "maci-domainobjs"; -import path from "path"; - -import type { CircuitInputs } from "maci-core"; - -import { getSignal } from "./utils/utils"; +import { type IMessageValidatorCircuitInputs } from "./utils/types"; +import { getSignal, circomkitInstance } from "./utils/utils"; describe("MessageValidator circuit", function test() { this.timeout(90000); - let circuitInputs: CircuitInputs; - let circuit: tester.WasmTester; + + let circuitInputs: IMessageValidatorCircuitInputs; + + let circuit: WitnessTester< + [ + "stateTreeIndex", + "numSignUps", + "voteOptionIndex", + "maxVoteOptions", + "originalNonce", + "nonce", + "cmd", + "pubKey", + "sigR8", + "sigS", + "currentVoiceCreditBalance", + "currentVotesForOption", + "voteWeight", + "slTimestamp", + "pollEndTimestamp", + ], + ["isValid"] + >; before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `messageValidator_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("messageValidator", { + file: "messageValidator", + template: "MessageValidator", + }); }); before(() => { @@ -35,37 +56,37 @@ describe("MessageValidator circuit", function test() { const signature = command.sign(privKey); - circuitInputs = stringifyBigInts({ - stateTreeIndex: 0n, + circuitInputs = { + stateTreeIndex: 0n as SignalValueType, numSignUps: 1n, voteOptionIndex: 0n, maxVoteOptions: 1n, originalNonce: 1n, nonce: 2n, cmd: command.asCircuitInputs(), - pubKey: pubKey.asCircuitInputs(), - sigR8: signature.R8, - sigS: signature.S, + pubKey: pubKey.asCircuitInputs() as unknown as [bigint, bigint], + sigR8: signature.R8 as unknown as bigint, + sigS: signature.S as bigint, currentVoiceCreditBalance: 100n, currentVotesForOption: 0n, voteWeight: 9n, slTimestamp: 1n, pollEndTimestamp: 2n, - }) as CircuitInputs; + }; }); it("should pass if all inputs are valid", async () => { - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const isValid = await getSignal(circuit, witness, "isValid"); expect(isValid.toString()).to.be.eq("1"); }); it("should be invalid if the signature is invalid", async () => { const circuitInputs2 = circuitInputs; - circuitInputs2.sigS = "0"; - const witness = await circuit.calculateWitness(circuitInputs2, true); - await circuit.checkConstraints(witness); + circuitInputs2.sigS = 0n; + const witness = await circuit.calculateWitness(circuitInputs2); + await circuit.expectConstraintPass(witness); const isValid = await getSignal(circuit, witness, "isValid"); expect(isValid.toString()).to.be.eq("0"); }); @@ -73,8 +94,8 @@ describe("MessageValidator circuit", function test() { it("should be invalid if the pubkey is invalid", async () => { const circuitInputs2 = circuitInputs; circuitInputs2.pubKey = [0n, 1n]; - const witness = await circuit.calculateWitness(circuitInputs2, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs2); + await circuit.expectConstraintPass(witness); const isValid = await getSignal(circuit, witness, "isValid"); expect(isValid.toString()).to.be.eq("0"); }); @@ -82,8 +103,8 @@ describe("MessageValidator circuit", function test() { it("should be invalid if there are insufficient voice credits", async () => { const circuitInputs2 = circuitInputs; circuitInputs2.voteWeight = 11n; - const witness = await circuit.calculateWitness(circuitInputs2, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs2); + await circuit.expectConstraintPass(witness); const isValid = await getSignal(circuit, witness, "isValid"); expect(isValid.toString()).to.be.eq("0"); }); @@ -91,8 +112,8 @@ describe("MessageValidator circuit", function test() { it("should be invalid if the nonce is invalid", async () => { const circuitInputs2 = circuitInputs; circuitInputs2.nonce = 3n; - const witness = await circuit.calculateWitness(circuitInputs2, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs2); + await circuit.expectConstraintPass(witness); const isValid = await getSignal(circuit, witness, "isValid"); expect(isValid.toString()).to.be.eq("0"); }); @@ -100,8 +121,8 @@ describe("MessageValidator circuit", function test() { it("should be invalid if the state leaf index is invalid", async () => { const circuitInputs2 = circuitInputs; circuitInputs2.stateTreeIndex = 2n; - const witness = await circuit.calculateWitness(circuitInputs2, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs2); + await circuit.expectConstraintPass(witness); const isValid = await getSignal(circuit, witness, "isValid"); expect(isValid.toString()).to.be.eq("0"); }); @@ -109,26 +130,26 @@ describe("MessageValidator circuit", function test() { it("should be invalid if the vote option index is invalid", async () => { const circuitInputs2 = circuitInputs; circuitInputs2.voteOptionIndex = 1n; - const witness = await circuit.calculateWitness(circuitInputs2, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs2); + await circuit.expectConstraintPass(witness); const isValid = await getSignal(circuit, witness, "isValid"); expect(isValid.toString()).to.be.eq("0"); }); it("should be invalid if the vote option index is invalid", async () => { const circuitInputs2 = circuitInputs; - circuitInputs2.voteOptionIndex = "6049261729"; - const witness = await circuit.calculateWitness(circuitInputs2, true); - await circuit.checkConstraints(witness); + circuitInputs2.voteOptionIndex = 6049261729n; + const witness = await circuit.calculateWitness(circuitInputs2); + await circuit.expectConstraintPass(witness); 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 = "3"; - const witness = await circuit.calculateWitness(circuitInputs2, true); - await circuit.checkConstraints(witness); + 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__/PrivToPubKey.test.ts b/circuits/ts/__tests__/PrivToPubKey.test.ts index b73e76140a..ce33432a75 100644 --- a/circuits/ts/__tests__/PrivToPubKey.test.ts +++ b/circuits/ts/__tests__/PrivToPubKey.test.ts @@ -1,30 +1,31 @@ import { expect } from "chai"; -import tester from "circom_tester"; -import { SNARK_FIELD_SIZE, stringifyBigInts } from "maci-crypto"; +import { type WitnessTester } from "circomkit"; +import { SNARK_FIELD_SIZE } from "maci-crypto"; import { Keypair } from "maci-domainobjs"; -import path from "path"; - -import { getSignal } from "./utils/utils"; +import { circomkitInstance, getSignal } from "./utils/utils"; describe("Public key derivation circuit", function test() { this.timeout(90000); - let circuit: tester.WasmTester; + + let circuit: WitnessTester<["privKey"], ["pubKey"]>; before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `privToPubKey_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("privToPubKey", { + file: "privToPubKey", + template: "PrivToPubKey", + }); }); it("should correctly compute a public key", async () => { const keypair = new Keypair(); - const circuitInputs = stringifyBigInts({ - privKey: keypair.privKey.asCircuitInputs(), - }); + const circuitInputs = { + privKey: keypair.privKey.asCircuitInputs() as unknown as bigint, + }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const derivedPubkey0 = await getSignal(circuit, witness, "pubKey[0]"); const derivedPubkey1 = await getSignal(circuit, witness, "pubKey[1]"); @@ -35,12 +36,12 @@ describe("Public key derivation circuit", function test() { it("should produce an output that is within the baby jubjub curve", async () => { const keypair = new Keypair(); - const circuitInputs = stringifyBigInts({ - privKey: keypair.privKey.asCircuitInputs(), - }); + const circuitInputs = { + privKey: keypair.privKey.asCircuitInputs() as unknown as bigint, + }; - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const derivedPubkey0 = await getSignal(circuit, witness, "pubKey[0]"); const derivedPubkey1 = await getSignal(circuit, witness, "pubKey[1]"); diff --git a/circuits/ts/__tests__/ProcessMessages.test.ts b/circuits/ts/__tests__/ProcessMessages.test.ts index e9943d85b4..1899b45fc2 100644 --- a/circuits/ts/__tests__/ProcessMessages.test.ts +++ b/circuits/ts/__tests__/ProcessMessages.test.ts @@ -1,10 +1,10 @@ import { expect } from "chai"; -import tester from "circom_tester"; +import { type WitnessTester } from "circomkit"; import { MaciState, Poll, packProcessMessageSmallVals, STATE_TREE_ARITY } from "maci-core"; -import { hash5, IncrementalQuinTree, stringifyBigInts, NOTHING_UP_MY_SLEEVE, AccQueue } from "maci-crypto"; +import { hash5, IncrementalQuinTree, NOTHING_UP_MY_SLEEVE, AccQueue } from "maci-crypto"; import { PrivKey, Keypair, PCommand, Message, Ballot } from "maci-domainobjs"; -import path from "path"; +import { IProcessMessagesInputs } from "../types"; import { STATE_TREE_DEPTH, @@ -14,21 +14,55 @@ import { treeDepths, voiceCreditBalance, } from "./utils/constants"; -import { getSignal } from "./utils/utils"; +import { getSignal, circomkitInstance } from "./utils/utils"; describe("ProcessMessage circuit", function test() { this.timeout(900000); const coordinatorKeypair = new Keypair(); - let circuit: tester.WasmTester; - let hasherCircuit: tester.WasmTester; + let circuit: WitnessTester< + [ + "inputHash", + "packedVals", + "pollEndTimestamp", + "msgRoot", + "msgs", + "msgSubrootPathElements", + "coordPrivKey", + "coordPubKey", + "encPubKeys", + "currentStateRoot", + "currentStateLeaves", + "currentStateLeavesPathElements", + "currentSbCommitment", + "currentSbSalt", + "newSbCommitment", + "newSbSalt", + "currentBallotRoot", + "currentBallots", + "currentBallotsPathElements", + "currentVoteWeights", + "currentVoteWeightsPathElements", + ] + >; + + let hasherCircuit: WitnessTester< + ["packedVals", "coordPubKey", "msgRoot", "currentSbCommitment", "newSbCommitment", "pollEndTimestamp"], + ["maxVoteOptions", "numSignUps", "batchStartIndex", "batchEndIndex", "hash"] + >; before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `processMessages_test.circom`); - circuit = await tester.wasm(circuitPath); - const hasherCircuitPath = path.resolve(__dirname, "../../circom/test", `processMessagesInputHasher_test.circom`); - hasherCircuit = await tester.wasm(hasherCircuitPath); + circuit = await circomkitInstance.WitnessTester("processMessages", { + file: "processMessages", + template: "ProcessMessages", + params: [10, 2, 1, 2], + }); + + hasherCircuit = await circomkitInstance.WitnessTester("processMessageInputHasher", { + file: "processMessages", + template: "ProcessMessagesInputHasher", + }); }); describe("1 user, 2 messages", () => { @@ -126,11 +160,11 @@ describe("ProcessMessage circuit", function test() { const currentStateRoot = maciState.stateTree.root; const currentBallotRoot = ballotTree.root; - const generatedInputs = poll.processMessages(pollId); + const inputs = poll.processMessages(pollId) as unknown as IProcessMessagesInputs; // Calculate the witness - const witness = await circuit.calculateWitness(generatedInputs, true); - await circuit.checkConstraints(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 @@ -148,19 +182,19 @@ describe("ProcessMessage circuit", function test() { ); // Test the ProcessMessagesInputHasher circuit - const hasherCircuitInputs = stringifyBigInts({ + const hasherCircuitInputs = { packedVals, - coordPubKey: generatedInputs.coordPubKey, - msgRoot: generatedInputs.msgRoot, - currentSbCommitment: generatedInputs.currentSbCommitment, - newSbCommitment: generatedInputs.newSbCommitment, - pollEndTimestamp: generatedInputs.pollEndTimestamp, - }); - - const hasherWitness = await hasherCircuit.calculateWitness(hasherCircuitInputs, true); - await circuit.checkConstraints(witness); + coordPubKey: inputs.coordPubKey, + msgRoot: inputs.msgRoot, + currentSbCommitment: inputs.currentSbCommitment, + newSbCommitment: inputs.newSbCommitment, + pollEndTimestamp: inputs.pollEndTimestamp, + }; + + const hasherWitness = await hasherCircuit.calculateWitness(hasherCircuitInputs); + await hasherCircuit.expectConstraintPass(hasherWitness); const hash = await getSignal(hasherCircuit, hasherWitness, "hash"); - expect(hash.toString()).to.be.eq(generatedInputs.inputHash.toString()); + expect(hash.toString()).to.be.eq(inputs.inputHash.toString()); }); }); @@ -246,11 +280,10 @@ describe("ProcessMessage circuit", function test() { const currentStateRoot = maciState.stateTree.root; const currentBallotRoot = ballotTree.root; - const generatedInputs = poll.processMessages(pollId); - + const inputs = poll.processMessages(pollId) as unknown as IProcessMessagesInputs; // Calculate the witness - const witness = await circuit.calculateWitness(generatedInputs); - await circuit.checkConstraints(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 @@ -405,11 +438,11 @@ describe("ProcessMessage circuit", function test() { } for (let i = 0; i < NUM_BATCHES; i += 1) { - const generatedInputs = selectedPoll.processMessages(id); + const inputs = selectedPoll.processMessages(id) as unknown as IProcessMessagesInputs; // eslint-disable-next-line no-await-in-loop - const witness = await circuit.calculateWitness(generatedInputs, true); + const witness = await circuit.calculateWitness(inputs); // eslint-disable-next-line no-await-in-loop - await circuit.checkConstraints(witness); + await circuit.expectConstraintPass(witness); } }); }); @@ -479,9 +512,9 @@ describe("ProcessMessage circuit", function test() { poll.publishMessage(message, ecdhKeypair.pubKey); - const inputs = poll.processMessages(pollId); - const witness = await circuit.calculateWitness(inputs, true); - await circuit.checkConstraints(witness); + const inputs = poll.processMessages(pollId) as unknown as IProcessMessagesInputs; + const witness = await circuit.calculateWitness(inputs); + await circuit.expectConstraintPass(witness); }); }); }); diff --git a/circuits/ts/__tests__/QuinCheckRoot.test.ts b/circuits/ts/__tests__/QuinCheckRoot.test.ts index 589753fef2..5ec5cd719f 100644 --- a/circuits/ts/__tests__/QuinCheckRoot.test.ts +++ b/circuits/ts/__tests__/QuinCheckRoot.test.ts @@ -1,40 +1,42 @@ import chai, { expect } from "chai"; import chaiAsPromised from "chai-as-promised"; -import tester from "circom_tester"; -import { IncrementalQuinTree, hash5, stringifyBigInts } from "maci-crypto"; +import { type WitnessTester } from "circomkit"; +import { IncrementalQuinTree, hash5 } from "maci-crypto"; -import path from "path"; - -import { getSignal } from "./utils/utils"; +import { getSignal, circomkitInstance } from "./utils/utils"; chai.use(chaiAsPromised); describe("QuinCheckRoot circuit", function test() { this.timeout(50000); - const circuitPath = path.resolve(__dirname, "../../circom/test", `quinTreeCheckRoot_test.circom`); - let circuit: tester.WasmTester; const leavesPerNode = 5; const treeDepth = 3; + let circuit: WitnessTester<["leaves"], ["root"]>; + before(async () => { - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("checkRoot", { + file: "trees/checkRoot", + template: "QuinCheckRoot", + params: [3], + }); }); it("should compute the correct merkle root", async () => { const leaves = Array(leavesPerNode ** treeDepth).fill(5n); - const circuitInputs = stringifyBigInts({ + const circuitInputs = { leaves, - }); + }; const tree = new IncrementalQuinTree(3, 0n, 5, hash5); leaves.forEach((leaf) => { tree.insert(leaf); }); - const witness = await circuit.calculateWitness(circuitInputs, true); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); const circuitRoot = await getSignal(circuit, witness, "root"); expect(circuitRoot.toString()).to.be.eq(tree.root.toString()); @@ -43,11 +45,11 @@ describe("QuinCheckRoot circuit", function test() { it("should not accept less leaves than a full tree", async () => { const leaves = Array(leavesPerNode ** treeDepth - 1).fill(5n); - const circuitInputs = stringifyBigInts({ + const circuitInputs = { leaves, - }); + }; - await expect(circuit.calculateWitness(circuitInputs, true)).to.be.rejectedWith( + await expect(circuit.calculateWitness(circuitInputs)).to.be.rejectedWith( "Not enough values for input signal leaves", ); }); diff --git a/circuits/ts/__tests__/Splicer.test.ts b/circuits/ts/__tests__/Splicer.test.ts deleted file mode 100644 index 45a71647a7..0000000000 --- a/circuits/ts/__tests__/Splicer.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { expect } from "chai"; -import tester from "circom_tester"; -import { stringifyBigInts } from "maci-crypto"; - -import path from "path"; - -import { getSignal } from "./utils/utils"; - -describe("Splice circuit", () => { - let circuit: tester.WasmTester; - before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `splicer_test.circom`); - circuit = await tester.wasm(circuitPath); - }); - - it("should output the correct reconstructed level", async () => { - for (let index = 0; index < 5; index += 1) { - const items = [0n, 20n, 30n, 40n]; - const leaf = 10n; - const circuitInputs = stringifyBigInts({ in: items, leaf, index: BigInt(index) }); - - // eslint-disable-next-line no-await-in-loop - const witness = await circuit.calculateWitness(circuitInputs); - // eslint-disable-next-line no-await-in-loop - await circuit.checkConstraints(witness); - - const output: bigint[] = []; - for (let i = 0; i < items.length + 1; i += 1) { - // eslint-disable-next-line no-await-in-loop - const selected = await getSignal(circuit, witness, `out[${i}]`); - output.push(BigInt(selected)); - } - items.splice(index, 0, leaf); - - expect(JSON.stringify(stringifyBigInts(items.map(BigInt)))).to.be.eq( - JSON.stringify(stringifyBigInts(output.map(String))), - ); - } - }); -}); diff --git a/circuits/ts/__tests__/StateLeafAndBallotTransformer.test.ts b/circuits/ts/__tests__/StateLeafAndBallotTransformer.test.ts index 8cfd4b66c7..fafe0f034c 100644 --- a/circuits/ts/__tests__/StateLeafAndBallotTransformer.test.ts +++ b/circuits/ts/__tests__/StateLeafAndBallotTransformer.test.ts @@ -1,11 +1,9 @@ import { expect } from "chai"; -import tester from "circom_tester"; -import { stringifyBigInts, genRandomSalt } from "maci-crypto"; +import { type WitnessTester } from "circomkit"; +import { genRandomSalt } from "maci-crypto"; import { PCommand, Keypair } from "maci-domainobjs"; -import path from "path"; - -import { getSignal } from "./utils/utils"; +import { getSignal, circomkitInstance } from "./utils/utils"; describe("StateLeafAndBallotTransformer circuit", function test() { this.timeout(90000); @@ -35,37 +33,61 @@ describe("StateLeafAndBallotTransformer circuit", function test() { const signature = command.sign(slKeypair.privKey); - let circuit: tester.WasmTester; + let circuit: WitnessTester< + [ + "numSignUps", + "maxVoteOptions", + "slPubKey", + "slVoiceCreditBalance", + "slTimestamp", + "pollEndTimestamp", + "ballotNonce", + "ballotCurrentVotesForOption", + "cmdStateIndex", + "cmdNewPubKey", + "cmdVoteOptionIndex", + "cmdNewVoteWeight", + "cmdNonce", + "cmdPollId", + "cmdSalt", + "cmdSigR8", + "cmdSigS", + "packedCommand", + ], + ["newSlPubKey", "newBallotNonce", "isValid"] + >; before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `stateLeafAndBallotTransformer_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("stateLeafAndBallotTransformer", { + file: "stateLeafAndBallotTransformer", + template: "StateLeafAndBallotTransformer", + }); }); it("should output new state leaf and ballot values if the command is valid", async () => { - const circuitInputs = stringifyBigInts({ + const circuitInputs = { numSignUps, maxVoteOptions, - slPubKey: slPubKey.asCircuitInputs(), + slPubKey: slPubKey.asCircuitInputs() as unknown as [bigint, bigint], slVoiceCreditBalance, slTimestamp, pollEndTimestamp, ballotNonce, ballotCurrentVotesForOption, cmdStateIndex: command.stateIndex, - cmdNewPubKey: command.newPubKey.asCircuitInputs(), + cmdNewPubKey: command.newPubKey.asCircuitInputs() as unknown as [bigint, bigint], cmdVoteOptionIndex: command.voteOptionIndex, cmdNewVoteWeight: command.newVoteWeight, cmdNonce: command.nonce, cmdPollId: command.pollId, cmdSalt: command.salt, - cmdSigR8: signature.R8, - cmdSigS: signature.S, + cmdSigR8: signature.R8 as [bigint, bigint], + cmdSigS: signature.S as bigint, packedCommand: command.asCircuitInputs(), - }); + }; const witness = await circuit.calculateWitness(circuitInputs); - await circuit.checkConstraints(witness); + await circuit.expectConstraintPass(witness); const newSlPubKey0 = await getSignal(circuit, witness, "newSlPubKey[0]"); const newSlPubKey1 = await getSignal(circuit, witness, "newSlPubKey[1]"); @@ -80,29 +102,29 @@ describe("StateLeafAndBallotTransformer circuit", function test() { }); it("should output existing state leaf and ballot values if the command is invalid", async () => { - const circuitInputs = stringifyBigInts({ + const circuitInputs = { numSignUps, maxVoteOptions, - slPubKey: slPubKey.asCircuitInputs(), + slPubKey: slPubKey.asCircuitInputs() as unknown as [bigint, bigint], slVoiceCreditBalance, slTimestamp, pollEndTimestamp, ballotNonce, ballotCurrentVotesForOption, cmdStateIndex: command.stateIndex, - cmdNewPubKey: command.newPubKey.asCircuitInputs(), + cmdNewPubKey: command.newPubKey.asCircuitInputs() as unknown as [bigint, bigint], cmdVoteOptionIndex: command.voteOptionIndex, cmdNewVoteWeight: command.newVoteWeight, cmdNonce: 2n, // invalid cmdPollId: command.pollId, cmdSalt: command.salt, - cmdSigR8: signature.R8, - cmdSigS: signature.S, + cmdSigR8: signature.R8 as [bigint, bigint], + cmdSigS: signature.S as bigint, packedCommand: command.asCircuitInputs(), - }); + }; const witness = await circuit.calculateWitness(circuitInputs); - await circuit.checkConstraints(witness); + await circuit.expectConstraintPass(witness); const newSlPubKey0 = await getSignal(circuit, witness, "newSlPubKey[0]"); const newSlPubKey1 = await getSignal(circuit, witness, "newSlPubKey[1]"); diff --git a/circuits/ts/__tests__/TallyVotes.test.ts b/circuits/ts/__tests__/TallyVotes.test.ts index 056713fe21..847c645942 100644 --- a/circuits/ts/__tests__/TallyVotes.test.ts +++ b/circuits/ts/__tests__/TallyVotes.test.ts @@ -1,13 +1,13 @@ import { expect } from "chai"; -import tester from "circom_tester"; +import { type WitnessTester } from "circomkit"; import { MaciState, Poll, STATE_TREE_ARITY } from "maci-core"; import { AccQueue, NOTHING_UP_MY_SLEEVE } from "maci-crypto"; import { Keypair, PCommand, Message } from "maci-domainobjs"; -import path from "path"; +import { ITallyVotesInputs } from "../types"; import { STATE_TREE_DEPTH, duration, maxValues, messageBatchSize, voiceCreditBalance } from "./utils/constants"; -import { generateRandomIndex } from "./utils/utils"; +import { generateRandomIndex, circomkitInstance } from "./utils/utils"; describe("TallyVotes circuit", function test() { this.timeout(900000); @@ -21,11 +21,37 @@ describe("TallyVotes circuit", function test() { const coordinatorKeypair = new Keypair(); - let circuit: tester.WasmTester; + let circuit: WitnessTester< + [ + "stateRoot", + "ballotRoot", + "sbSalt", + "packedVals", + "sbCommitment", + "currentTallyCommitment", + "newTallyCommitment", + "inputHash", + "ballots", + "ballotPathElements", + "votes", + "currentResults", + "currentResultsRootSalt", + "currentSpentVoiceCreditSubtotal", + "currentSpentVoiceCreditSubtotalSalt", + "currentPerVOSpentVoiceCredits", + "currentPerVOSpentVoiceCreditsRootSalt", + "newResultsRootSalt", + "newPerVOSpentVoiceCreditsRootSalt", + "newSpentVoiceCreditSubtotalSalt", + ] + >; before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `tallyVotes_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("tallyVotes", { + file: "tallyVotes", + template: "TallyVotes", + params: [10, 1, 2], + }); }); describe("1 user, 2 messages", () => { @@ -93,13 +119,13 @@ describe("TallyVotes circuit", function test() { }); it("should produce the correct result commitments", async () => { - const generatedInputs = poll.tallyVotes(); + const generatedInputs = poll.tallyVotes() as unknown as ITallyVotesInputs; const witness = await circuit.calculateWitness(generatedInputs); - await circuit.checkConstraints(witness); + await circuit.expectConstraintPass(witness); }); it("should produce the correct result if the inital tally is not zero", async () => { - const generatedInputs = poll.tallyVotes(); + const generatedInputs = poll.tallyVotes() as unknown as ITallyVotesInputs; // Start the tally from non-zero value let randIdx = generateRandomIndex(Object.keys(generatedInputs).length); @@ -107,9 +133,9 @@ describe("TallyVotes circuit", function test() { randIdx = generateRandomIndex(Object.keys(generatedInputs).length); } - generatedInputs.currentResults[randIdx] = "1"; + generatedInputs.currentResults[randIdx] = 1n; const witness = await circuit.calculateWitness(generatedInputs); - await circuit.checkConstraints(witness); + await circuit.expectConstraintPass(witness); }); }); @@ -160,21 +186,21 @@ describe("TallyVotes circuit", function test() { } for (let i = 0; i < NUM_BATCHES; i += 1) { - const generatedInputs = poll.tallyVotes(); + const generatedInputs = poll.tallyVotes() as unknown as ITallyVotesInputs; // For the 0th batch, the circuit should ignore currentResults, // currentSpentVoiceCreditSubtotal, and // currentPerVOSpentVoiceCredits if (i === 0) { - generatedInputs.currentResults[0] = "123"; - generatedInputs.currentSpentVoiceCreditSubtotal = "456"; - generatedInputs.currentPerVOSpentVoiceCredits[0] = "789"; + generatedInputs.currentResults[0] = 123n; + generatedInputs.currentSpentVoiceCreditSubtotal = 456n; + generatedInputs.currentPerVOSpentVoiceCredits[0] = 789n; } // eslint-disable-next-line no-await-in-loop const witness = await circuit.calculateWitness(generatedInputs); // eslint-disable-next-line no-await-in-loop - await circuit.checkConstraints(witness); + await circuit.expectConstraintPass(witness); } }); }); diff --git a/circuits/ts/__tests__/UnpackElement.test.ts b/circuits/ts/__tests__/UnpackElement.test.ts index 02bbc6ea41..dff9cc8c2a 100644 --- a/circuits/ts/__tests__/UnpackElement.test.ts +++ b/circuits/ts/__tests__/UnpackElement.test.ts @@ -1,49 +1,51 @@ import { expect } from "chai"; -import tester from "circom_tester"; -import { genRandomSalt, stringifyBigInts } from "maci-crypto"; +import { type WitnessTester } from "circomkit"; +import { genRandomSalt } from "maci-crypto"; -import path from "path"; - -import { getSignal } from "./utils/utils"; +import { getSignal, circomkitInstance } from "./utils/utils"; describe("UnpackElement circuit", () => { - let circuit: tester.WasmTester; + let circuit: WitnessTester<["in"], ["out"]>; - describe("UnpackElement", () => { - before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `unpackElement_test.circom`); - circuit = await tester.wasm(circuitPath); + before(async () => { + circuit = await circomkitInstance.WitnessTester("unpackElement", { + file: "unpackElement", + template: "UnpackElement", + params: [5], }); + }); - it("should unpack a field element with 5 packed values correctly", async () => { - const elements: string[] = []; - for (let i = 0; i < 5; i += 1) { - let e = (BigInt(genRandomSalt().toString()) % BigInt(2 ** 50)).toString(2); - while (e.length < 50) { - e = `0${e}`; - } - elements.push(e); + it("should unpack a field element with 5 packed values correctly", async () => { + const elements: string[] = []; + for (let i = 0; i < 5; i += 1) { + let e = (BigInt(genRandomSalt().toString()) % BigInt(2 ** 50)).toString(2); + while (e.length < 50) { + e = `0${e}`; } + elements.push(e); + } - const circuitInputs = stringifyBigInts({ - in: BigInt(`0b${elements.join("")}`), - }); + const circuitInputs = { + in: BigInt(`0b${elements.join("")}`), + }; - const witness = await circuit.calculateWitness(circuitInputs); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(circuitInputs); + await circuit.expectConstraintPass(witness); - for (let i = 0; i < 5; i += 1) { - // eslint-disable-next-line no-await-in-loop - const out = await getSignal(circuit, witness, `out[${i}]`); - expect(BigInt(`0b${BigInt(out).toString(2)}`).toString()).to.be.eq(BigInt(`0b${elements[i]}`).toString()); - } - }); + for (let i = 0; i < 5; i += 1) { + // eslint-disable-next-line no-await-in-loop + const out = await getSignal(circuit, witness, `out[${i}]`); + expect(BigInt(`0b${BigInt(out).toString(2)}`).toString()).to.be.eq(BigInt(`0b${elements[i]}`).toString()); + } }); describe("unpackElement4", () => { before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `unpackElement4_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("unpackElement", { + file: "unpackElement", + template: "UnpackElement", + params: [4], + }); }); it("should unpack a field element with 4 packed values correctly", async () => { @@ -56,12 +58,12 @@ describe("UnpackElement circuit", () => { elements.push(e); } - const circuitInputs = stringifyBigInts({ + const circuitInputs = { in: BigInt(`0b${elements.join("")}`), - }); + }; const witness = await circuit.calculateWitness(circuitInputs); - await circuit.checkConstraints(witness); + await circuit.expectConstraintPass(witness); for (let i = 0; i < 4; i += 1) { // eslint-disable-next-line no-await-in-loop diff --git a/circuits/ts/__tests__/VerifySignature.test.ts b/circuits/ts/__tests__/VerifySignature.test.ts index 5aa56c1268..0514150298 100644 --- a/circuits/ts/__tests__/VerifySignature.test.ts +++ b/circuits/ts/__tests__/VerifySignature.test.ts @@ -1,19 +1,20 @@ import { expect } from "chai"; -import tester from "circom_tester"; -import { stringifyBigInts, verifySignature, hash4 } from "maci-crypto"; +import { type WitnessTester } from "circomkit"; +import { verifySignature, hash4 } from "maci-crypto"; import { Keypair, PCommand } from "maci-domainobjs"; -import path from "path"; - -import { getSignal } from "./utils/utils"; +import { getSignal, circomkitInstance } from "./utils/utils"; describe("Signature verification circuit", function test() { this.timeout(90000); - let circuit: tester.WasmTester; + let circuit: WitnessTester<["pubKey", "R8", "S", "preimage"], ["valid"]>; + before(async () => { - const circuitPath = path.resolve(__dirname, "../../circom/test", `verifySignature_test.circom`); - circuit = await tester.wasm(circuitPath); + circuit = await circomkitInstance.WitnessTester("verifySignature", { + file: "verifySignature", + template: "VerifySignature", + }); }); it("should verify a valid signature", async () => { @@ -26,15 +27,15 @@ describe("Signature verification circuit", function test() { expect(verifySignature(plaintext, sig, signer.pubKey.rawPubKey)).to.eq(true); - const circuitInputs = stringifyBigInts({ - pubKey: signer.pubKey.asCircuitInputs(), - R8: sig.R8, - S: sig.S, + const circuitInputs = { + pubKey: signer.pubKey.asCircuitInputs() as unknown as [bigint, bigint], + R8: sig.R8 as [bigint, bigint], + S: sig.S as bigint, preimage: command.asCircuitInputs(), - }); + }; const witness = await circuit.calculateWitness(circuitInputs); - await circuit.checkConstraints(witness); + await circuit.expectConstraintPass(witness); const isValid = await getSignal(circuit, witness, "valid"); expect(isValid.toString()).to.be.eq("1"); }); @@ -58,15 +59,15 @@ describe("Signature verification circuit", function test() { // The signature is not signed by `wrongSigner` expect(verifySignature(plaintext, sig, wrongSigner.pubKey.rawPubKey)).to.eq(false); - const circuitInputs = stringifyBigInts({ - pubKey: wrongSigner.pubKey.asCircuitInputs(), - R8: sig.R8, - S: sig.S, + const circuitInputs = { + pubKey: wrongSigner.pubKey.asCircuitInputs() as unknown as [bigint, bigint], + R8: sig.R8 as [bigint, bigint], + S: sig.S as bigint, preimage: command.asCircuitInputs(), - }); + }; const witness = await circuit.calculateWitness(circuitInputs); - await circuit.checkConstraints(witness); + await circuit.expectConstraintPass(witness); const isValid = await getSignal(circuit, witness, "valid"); expect(isValid.toString()).to.be.eq("0"); expect((await getSignal(circuit, witness, "verifier.isCcZero.out")).toString()).to.be.eq("1"); @@ -82,17 +83,17 @@ describe("Signature verification circuit", function test() { expect(verifySignature(plaintext, sig, signer.pubKey.rawPubKey)).to.eq(true); - const circuitInputs = stringifyBigInts({ - pubKey: signer.pubKey.asCircuitInputs(), - R8: sig.R8, + const circuitInputs = { + pubKey: signer.pubKey.asCircuitInputs() as unknown as [bigint, bigint], + R8: sig.R8 as [bigint, bigint], S: BigInt("2736030358979909402780800718157159386076813972158567259200215660948447373040") + BigInt(1), preimage: command.asCircuitInputs(), - }); + }; expect(verifySignature(plaintext, sig, signer.pubKey.rawPubKey)).to.eq(true); const witness = await circuit.calculateWitness(circuitInputs); - await circuit.checkConstraints(witness); + await circuit.expectConstraintPass(witness); const isValid = await getSignal(circuit, witness, "valid"); expect(isValid.toString()).to.be.eq("0"); expect((await getSignal(circuit, witness, "verifier.isCcZero.out")).toString()).to.be.eq("0"); diff --git a/circuits/ts/__tests__/utils/types.ts b/circuits/ts/__tests__/utils/types.ts new file mode 100644 index 0000000000..bacbeb6a55 --- /dev/null +++ b/circuits/ts/__tests__/utils/types.ts @@ -0,0 +1,22 @@ +import { type SignalValueType } from "circomkit/dist/types/circuit"; + +/** + * Circuit inputs for testing the MessageValidator circuit + */ +export interface IMessageValidatorCircuitInputs { + stateTreeIndex: SignalValueType; + numSignUps: SignalValueType; + voteOptionIndex: SignalValueType; + maxVoteOptions: SignalValueType; + originalNonce: SignalValueType; + nonce: SignalValueType; + cmd: SignalValueType; + pubKey: SignalValueType; + sigR8: SignalValueType; + sigS: SignalValueType; + currentVoiceCreditBalance: SignalValueType; + currentVotesForOption: SignalValueType; + voteWeight: SignalValueType; + slTimestamp: SignalValueType; + pollEndTimestamp: SignalValueType; +} diff --git a/circuits/ts/__tests__/utils/utils.ts b/circuits/ts/__tests__/utils/utils.ts index 6585002a12..d37dbfbff1 100644 --- a/circuits/ts/__tests__/utils/utils.ts +++ b/circuits/ts/__tests__/utils/utils.ts @@ -1,4 +1,15 @@ -import type { WasmTester } from "circom_tester"; +import { Circomkit, type WitnessTester, type CircomkitConfig } from "circomkit"; + +import fs from "fs"; +import path from "path"; + +const configFilePath = path.resolve(__dirname, "..", "..", "..", "circomkit.json"); +const config = JSON.parse(fs.readFileSync(configFilePath, "utf-8")) as CircomkitConfig; + +export const circomkitInstance = new Circomkit({ + ...config, + verbose: false, +}); /** * Convert a string to a bigint @@ -7,8 +18,15 @@ import type { WasmTester } from "circom_tester"; */ export const str2BigInt = (s: string): bigint => BigInt(parseInt(Buffer.from(s).toString("hex"), 16)); +/** + * Generate a random number within a certain threshold + * @param upper - the upper bound + * @returns the random index + */ +export const generateRandomIndex = (upper: number): number => Math.floor(Math.random() * (upper - 1)); + // @note thanks https://github.com/Rate-Limiting-Nullifier/circom-rln/blob/main/test/utils.ts -// for the code below +// for the code below (modified version) /** * Get a signal from the circuit * @param circuit - the circuit object @@ -16,25 +34,12 @@ export const str2BigInt = (s: string): bigint => BigInt(parseInt(Buffer.from(s). * @param name - the name of the signal * @returns the signal value */ -export const getSignal = async (wasmTester: WasmTester, witness: bigint[], name: string): Promise => { +export const getSignal = async (tester: WitnessTester, witness: bigint[], name: string): Promise => { const prefix = "main"; // E.g. the full name of the signal "root" is "main.root" // You can look up the signal names using `circuit.getDecoratedOutput(witness))` const signalFullName = `${prefix}.${name}`; - await wasmTester.loadSymbols(); - - // symbols[n] = { labelIdx: 1, varIdx: 1, componentIdx: 142 }, - const signalMeta = wasmTester.symbols[signalFullName]; - // Assigned value of the signal is located in the `varIdx`th position - // of the witness array - const indexInWitness = signalMeta.varIdx; - return BigInt(witness[indexInWitness]); + const out = await tester.readWitness(witness, [signalFullName]); + return BigInt(out[signalFullName]); }; - -/** - * Generate a random number within a certain threshold - * @param upper - the upper bound - * @returns the random index - */ -export const generateRandomIndex = (upper: number): number => Math.floor(Math.random() * (upper - 1)); diff --git a/circuits/ts/types.ts b/circuits/ts/types.ts index 620e58be77..161ec353fb 100644 --- a/circuits/ts/types.ts +++ b/circuits/ts/types.ts @@ -12,3 +12,56 @@ export interface IGenProofOptions { wasmPath?: string; silent?: boolean; } + +/** + * Inputs for circuit ProcessMessages + */ +export interface IProcessMessagesInputs { + inputHash: bigint; + packedVals: bigint; + pollEndTimestamp: bigint; + msgRoot: bigint; + msgs: bigint[]; + msgSubrootPathElements: bigint[][]; + coordPrivKey: bigint; + coordPubKey: [bigint, bigint]; + encPubKeys: bigint[]; + currentStateRoot: bigint; + currentStateLeaves: bigint[]; + currentStateLeavesPathElements: bigint[][]; + currentSbCommitment: bigint; + currentSbSalt: bigint; + newSbCommitment: bigint; + newSbSalt: bigint; + currentBallotRoot: bigint; + currentBallots: bigint[]; + currentBallotsPathElements: bigint[][]; + currentVoteWeights: bigint[]; + currentVoteWeightsPathElements: bigint[][]; +} + +/** + * Inputs for circuit TallyVotes + */ +export interface ITallyVotesInputs { + stateRoot: bigint; + ballotRoot: bigint; + sbSalt: bigint; + packedVals: bigint; + sbCommitment: bigint; + currentTallyCommitment: bigint; + newTallyCommitment: bigint; + inputHash: bigint; + ballots: bigint[]; + ballotPathElements: bigint[]; + votes: bigint[][]; + currentResults: bigint[]; + currentResultsRootSalt: bigint; + currentSpentVoiceCreditSubtotal: bigint; + currentSpentVoiceCreditSubtotalSalt: bigint; + currentPerVOSpentVoiceCredits: bigint[]; + currentPerVOSpentVoiceCreditsRootSalt: bigint; + newResultsRootSalt: bigint; + newPerVOSpentVoiceCreditsRootSalt: bigint; + newSpentVoiceCreditSubtotalSalt: bigint; +} diff --git a/circuits/ts/types/circom_tester.d.ts b/circuits/ts/types/circom_tester.d.ts deleted file mode 100644 index 2cc5fd0d64..0000000000 --- a/circuits/ts/types/circom_tester.d.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** Declaration file generated by dts-gen */ -declare module "circom_tester" { - interface Options { - include?: string | string[]; - sym?: boolean; - r1cs?: boolean; - json?: boolean; - output?: string; - prime?: string; - O?: 0 | 1; - verbose?: boolean; - inspect?: boolean; - recompile?: boolean; - compile?: boolean; - wasm?: boolean; - } - - interface CircuitSymbol { - labelIdx: number; - varIdx: number; - componentIdx: number; - } - - type Symbols = Record; - - interface WitnessCalculator { - calculateWitness(input: unknown, sanityCheck?: boolean): Promise; - } - - class WasmTester { - constructor(dir: string, baseName: string, witnessCalculator: WitnessCalculator); - - symbols: Symbols; - - calculateWitness(input: unknown, sanityCheck?: boolean): Promise; - - loadSymbols(): Promise; - - checkConstraints(witness: bigint[]): Promise; - - assertOut(actual: unknown, expected: unknown): Promise; - } - - export function wasm(circomInput: string, options?: Options): Promise; -} diff --git a/cli/testScript.sh b/cli/testScript.sh index 2f986faacd..21281319ea 100644 --- a/cli/testScript.sh +++ b/cli/testScript.sh @@ -47,7 +47,7 @@ HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js genProofs \ -w true HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js proveOnChain \ --poll-id 0 \ - --proof-dir proofs/ + --proof-dir proofs/ \ --subsidy-enabled false HARDHAT_CONFIG=./build/hardhat.config.js node build/ts/index.js verify \ --poll-id 0 \ diff --git a/cli/ts/commands/proveOnChain.ts b/cli/ts/commands/proveOnChain.ts index 47782073ba..4704cbce5c 100644 --- a/cli/ts/commands/proveOnChain.ts +++ b/cli/ts/commands/proveOnChain.ts @@ -77,7 +77,11 @@ export const proveOnChain = async ( const maciContractAddress = maciAddress || readContractAddress("MACI"); const messageProcessorContractAddress = messageProcessorAddress || readContractAddress(`MessageProcessor-${pollId}`); const tallyContractAddress = tallyAddress || readContractAddress(`Tally-${pollId}`); - const subsidyContractAddress = subsidyAddress || readContractAddress(`Subsidy-${pollId}`); + + let subsidyContractAddress; + if (subsidyEnabled) { + subsidyContractAddress = subsidyAddress || readContractAddress(`Subsidy-${pollId}`); + } // check contracts are deployed on chain if (!(await contractExists(signer.provider!, maciContractAddress))) { @@ -92,7 +96,7 @@ export const proveOnChain = async ( logError("Tally contract does not exist"); } - if (subsidyEnabled && !(await contractExists(signer.provider!, subsidyContractAddress))) { + if (subsidyEnabled && subsidyContractAddress && !(await contractExists(signer.provider!, subsidyContractAddress))) { logError("Subsidy contract does not exist"); } @@ -114,7 +118,10 @@ export const proveOnChain = async ( const tallyContract = new BaseContract(tallyContractAddress, parseArtifact("Tally")[0], signer) as Tally; - const subsidyContract = new BaseContract(subsidyContractAddress, parseArtifact("Subsidy")[0], signer) as Subsidy; + let subsidyContract: Subsidy | undefined; + if (subsidyEnabled && subsidyContractAddress) { + subsidyContract = new BaseContract(subsidyContractAddress, parseArtifact("Subsidy")[0], signer) as Subsidy; + } const messageAqContractAddress = (await pollContract.extContracts()).messageAq; @@ -356,9 +363,9 @@ export const proveOnChain = async ( } // subsidy calculations if any subsidy proofs are provided - if (subsidyEnabled && Object.keys(data.subsidyProofs).length !== 0) { - let rbi = Number(await subsidyContract.rbi()); - let cbi = Number(await subsidyContract.cbi()); + if (subsidyEnabled && subsidyContractAddress && Object.keys(data.subsidyProofs).length !== 0) { + let rbi = Number(await subsidyContract!.rbi()); + let cbi = Number(await subsidyContract!.cbi()); const num1DBatches = Math.ceil(numSignUps / subsidyBatchSize); let subsidyBatchNum = rbi * num1DBatches + cbi; const totalBatchNum = (num1DBatches * (num1DBatches + 1)) / 2; @@ -368,33 +375,33 @@ export const proveOnChain = async ( // process all batches for (let i = subsidyBatchNum; i < totalBatchNum; i += 1) { if (i === 0) { - await subsidyContract.updateSbCommitment(); + await subsidyContract!.updateSbCommitment(); } const { proof, circuitInputs, publicInputs } = data.subsidyProofs[i]; // ensure the commitment matches - const subsidyCommitmentOnChain = await subsidyContract.subsidyCommitment(); + const subsidyCommitmentOnChain = await subsidyContract!.subsidyCommitment(); if (subsidyCommitmentOnChain.toString() !== circuitInputs.currentSubsidyCommitment) { logError(`subsidycommitment mismatch`); } - const packedValsOnChain = BigInt(await subsidyContract.genSubsidyPackedVals(numSignUps)); + const packedValsOnChain = BigInt(await subsidyContract!.genSubsidyPackedVals(numSignUps)); if (circuitInputs.packedVals !== packedValsOnChain.toString()) { logError("subsidy packedVals mismatch."); } // ensure the state and ballot root commitment matches - const currentSbCommitmentOnChain = await subsidyContract.sbCommitment(); + const currentSbCommitmentOnChain = await subsidyContract!.sbCommitment(); if (currentSbCommitmentOnChain.toString() !== circuitInputs.sbCommitment) { logError("currentSbCommitment mismatch."); } - const publicInputHashOnChain = await subsidyContract.genSubsidyPublicInputHash( + const publicInputHashOnChain = await subsidyContract!.genSubsidyPublicInputHash( numSignUps, circuitInputs.newSubsidyCommitment as BigNumberish, ); @@ -409,7 +416,7 @@ export const proveOnChain = async ( try { // verify the proof on chain and set the new subsidy commitment - const tx = await subsidyContract.updateSubsidy( + const tx = await subsidyContract!.updateSubsidy( circuitInputs.newSubsidyCommitment as BigNumberish, formattedProof, ); @@ -424,8 +431,8 @@ export const proveOnChain = async ( logYellow(quiet, info(`Progress: ${subsidyBatchNum + 1} / ${totalBatchNum}`)); const [nrbi, ncbi] = await Promise.all([ - subsidyContract.rbi().then(Number), - subsidyContract.cbi().then(Number), + subsidyContract!.rbi().then(Number), + subsidyContract!.cbi().then(Number), ]); rbi = nrbi; diff --git a/package.json b/package.json index 2930818d2c..3003034ee7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "maci", "version": "1.1.1", - "description": "Minimal Anti-Collustion Infrastructure", + "description": "Minimal Anti-Collusion Infrastructure", "repository": "https://github.com/privacy-scaling-explorations/maci", "license": "MIT", "scripts": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 067935e771..e380d07dec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,9 +91,15 @@ importers: circuits: dependencies: + "@zk-kit/circuits": + specifier: ^0.3.0 + version: 0.3.0 + circomkit: + specifier: ^0.0.20 + version: 0.0.20 circomlib: - specifier: https://github.com/weijiekoh/circomlib#ac85e82c1914d47789e2032fb11ceb2cfdd38a2b - version: github.com/weijiekoh/circomlib/ac85e82c1914d47789e2032fb11ceb2cfdd38a2b + specifier: ^2.0.5 + version: 2.0.5 maci-core: specifier: ^1.1.2 version: link:../core @@ -125,9 +131,6 @@ importers: chai-as-promised: specifier: ^7.1.1 version: 7.1.1(chai@4.4.0) - circom_tester: - specifier: ^0.0.20 - version: 0.0.20 mocha: specifier: ^10.2.0 version: 10.2.0 @@ -5640,6 +5643,7 @@ packages: /@types/json5@0.0.29: resolution: { integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== } + requiresBuild: true dev: true /@types/katex@0.16.7: @@ -6213,6 +6217,13 @@ packages: "@zk-kit/utils": 0.1.0 dev: false + /@zk-kit/circuits@0.3.0: + resolution: + { integrity: sha512-v46KHC3sBRXUJbYi8d5PTAm3zCdBeArvWw3de+A2LcW/C9beYqBo8QJ/h6NWKZWOgpwqvCHzJa5HvyG6x3lIZQ== } + dependencies: + circomlib: 2.0.5 + dev: false + /@zk-kit/eddsa-poseidon@0.5.1: resolution: { integrity: sha512-sPyoyjwg9EZ+tHLGxOG+FDj9XJK1knVjm27nTMV4ZSiQIf0427QWnLhOk7b6zMvFmEpBWTG9gneJ5pr3jzJ4zg== } @@ -7635,6 +7646,11 @@ packages: parse5-htmlparser2-tree-adapter: 7.0.0 dev: false + /child_process@1.0.2: + resolution: + { integrity: sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g== } + dev: false + /chokidar@3.5.3: resolution: { integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== } @@ -7704,6 +7720,14 @@ packages: fnv-plus: 1.3.1 dev: true + /circom_runtime@0.1.21: + resolution: + { integrity: sha512-qTkud630B/GK8y76hnOaaS1aNuF6prfV0dTrkeRsiJKnlP1ryQbP2FWLgDOPqn6aKyaPlam+Z+DTbBhkEzh8dA== } + hasBin: true + dependencies: + ffjavascript: 0.2.56 + dev: false + /circom_runtime@0.1.24: resolution: { integrity: sha512-H7/7I2J/cBmRnZm9docOCGhfxzS61BEm4TMCWcrZGsWNBQhePNfQq88Oj2XpUfzmBTCd8pRvRb3Mvazt3TMrJw== } @@ -7711,18 +7735,31 @@ packages: dependencies: ffjavascript: 0.2.60 - /circom_tester@0.0.20: + /circom_tester@0.0.19: resolution: - { integrity: sha512-hhtqh3z1+/4RqhbAQxQTzekDvANFNd0M0+D8OdpxM1Ud4yQXoM+1n06AhJ7sULfCUD+LQrmnSjK5GD783KRSxg== } + { integrity: sha512-SNHaBsGxcBH6XsVWfsRbRPA7NF8m8AMKJI9dtJJCFGUtOTT2+zsoIqAwi50z6XCnO4TtjyXq7AeXa1PLHqT0tw== } dependencies: chai: 4.4.0 + child_process: 1.0.2 ffjavascript: 0.2.62 fnv-plus: 1.3.1 - r1csfile: 0.0.47 - snarkjs: 0.7.2 + r1csfile: 0.0.41 + snarkjs: 0.5.0 tmp-promise: 3.0.3 util: 0.12.5 - dev: true + dev: false + + /circomkit@0.0.20: + resolution: + { integrity: sha512-rzCJFV1WNshAhOrlSW1rnz3tTRoVjsQ3gkO4T0sNcrYa3mNRQxc02O4d7pUS7mKhAGDFgo2VpIkz3zfPcoLhrA== } + engines: { node: ">=12.0.0" } + hasBin: true + dependencies: + chai: 4.4.0 + circom_tester: 0.0.19 + loglevel: 1.8.1 + snarkjs: 0.7.2 + dev: false /circomlib@2.0.5: resolution: @@ -10502,6 +10539,15 @@ packages: web-worker: 1.3.0 dev: true + /ffjavascript@0.2.56: + resolution: + { integrity: sha512-em6G5Lrj7ucIqj4TYEgyoHs/j99Urwwqa4+YxEVY2hggnpRimVj+noX5pZQTxI1pvtiekZI4rG65JBf0xraXrg== } + dependencies: + wasmbuilder: 0.0.16 + wasmcurves: 0.2.0 + web-worker: 1.3.0 + dev: false + /ffjavascript@0.2.60: resolution: { integrity: sha512-T/9bnEL5xAZRDbQoEMf+pM9nrhK+C3JyZNmqiWub26EQorW7Jt+jR54gpqDhceA4Nj0YctPQwYnl8xa52/A26A== } @@ -10693,7 +10739,6 @@ packages: /fnv-plus@1.3.1: resolution: { integrity: sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw== } - dev: true /follow-redirects@1.15.4(debug@4.3.4): resolution: @@ -12563,6 +12608,7 @@ packages: dependencies: call-bind: 1.0.5 has-tostringtag: 1.0.0 + dev: false /is-array-buffer@3.0.2: resolution: @@ -13300,6 +13346,7 @@ packages: resolution: { integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== } hasBin: true + requiresBuild: true dependencies: minimist: 1.2.8 dev: true @@ -13860,6 +13907,12 @@ packages: wrap-ansi: 9.0.0 dev: true + /loglevel@1.8.1: + resolution: + { integrity: sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== } + engines: { node: ">= 0.6.0" } + dev: false + /logplease@1.2.15: resolution: { integrity: sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA== } @@ -17272,6 +17325,16 @@ packages: ffjavascript: 0.2.35 dev: true + /r1csfile@0.0.41: + resolution: + { integrity: sha512-Q1WDF3u1vYeAwjHo4YuddkA8Aq0TulbKjmGm99+Atn13Lf5fTsMZBnBV9T741w8iSyPFG6Uh6sapQby77sREqA== } + dependencies: + "@iden3/bigarray": 0.0.2 + "@iden3/binfileutils": 0.0.11 + fastfile: 0.0.20 + ffjavascript: 0.2.56 + dev: false + /r1csfile@0.0.47: resolution: { integrity: sha512-oI4mAwuh1WwuFg95eJDNDDL8hCaZkwnPuNZrQdLBWvDoRU7EG+L/MOHL7SwPW2Y+ZuYcTLpj3rBkgllBQZN/JA== } @@ -18711,6 +18774,23 @@ packages: readline: 1.3.0 dev: true + /snarkjs@0.5.0: + resolution: + { integrity: sha512-KWz8mZ2Y+6wvn6GGkQo6/ZlKwETdAGohd40Lzpwp5TUZCn6N6O4Az1SuX1rw/qREGL6Im+ycb19suCFE8/xaKA== } + hasBin: true + dependencies: + "@iden3/binfileutils": 0.0.11 + bfj: 7.1.0 + blake2b-wasm: 2.4.0 + circom_runtime: 0.1.21 + ejs: 3.1.9 + fastfile: 0.0.20 + ffjavascript: 0.2.56 + js-sha3: 0.8.0 + logplease: 1.2.15 + r1csfile: 0.0.41 + dev: false + /snarkjs@0.7.2: resolution: { integrity: sha512-A8yPFm9pRnZ7XYXfPSjSFnugEV1rsHGjb8W7c0Qk7nzXl5h3WANTkpVC5FYxakmw/GKWekz7wjjHaOFtPp823Q== } @@ -19247,6 +19327,7 @@ packages: resolution: { integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== } engines: { node: ">=4" } + requiresBuild: true dev: true /strip-bom@4.0.0: @@ -19642,7 +19723,7 @@ packages: { integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ== } dependencies: tmp: 0.2.1 - dev: true + dev: false /tmp@0.0.33: resolution: @@ -19657,7 +19738,6 @@ packages: engines: { node: ">=8.17.0" } dependencies: rimraf: 3.0.2 - dev: true /to-fast-properties@2.0.0: resolution: @@ -19807,6 +19887,7 @@ packages: /tsconfig-paths@3.15.0: resolution: { integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== } + requiresBuild: true dependencies: "@types/json5": 0.0.29 json5: 1.0.2 @@ -20367,6 +20448,7 @@ packages: is-generator-function: 1.0.10 is-typed-array: 1.1.12 which-typed-array: 1.1.13 + dev: false /utila@0.4.0: resolution: @@ -20530,6 +20612,13 @@ packages: blakejs: 1.2.1 dev: true + /wasmcurves@0.2.0: + resolution: + { integrity: sha512-3e2rbxdujOwaod657gxgmdhZNn+i1qKdHO3Y/bK+8E7bV8ttV/fu5FO4/WLBACF375cK0QDLOP+65Na63qYuWA== } + dependencies: + wasmbuilder: 0.0.16 + dev: false + /wasmcurves@0.2.2: resolution: { integrity: sha512-JRY908NkmKjFl4ytnTu5ED6AwPD+8VJ9oc94kdq7h5bIwbj0L4TDJ69mG+2aLs2SoCmGfqIesMWTEJjtYsoQXQ== } @@ -21561,10 +21650,3 @@ packages: /zwitch@2.0.4: resolution: { integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== } - - github.com/weijiekoh/circomlib/ac85e82c1914d47789e2032fb11ceb2cfdd38a2b: - resolution: - { tarball: https://codeload.github.com/weijiekoh/circomlib/tar.gz/ac85e82c1914d47789e2032fb11ceb2cfdd38a2b } - name: circomlib - version: 1.0.0 - dev: false