Skip to content

Commit

Permalink
test: fuzz testing for circuits
Browse files Browse the repository at this point in the history
- [x] Add fast-check for circuits packages
- [x] Add fuzz testing test templates
- [x] Code style fixes for circuits
  • Loading branch information
0xmad committed May 23, 2024
1 parent 321a0c9 commit 987b20d
Show file tree
Hide file tree
Showing 8 changed files with 372 additions and 180 deletions.
2 changes: 1 addition & 1 deletion circuits/circom/utils/calculateTotal.circom
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ template CalculateTotal(n) {
signal sums[n];
sums[0] <== nums[0];

for (var i=1; i < n; i++) {
for (var i = 1; i < n; i++) {
sums[i] <== sums[i - 1] + nums[i];
}

Expand Down
9 changes: 9 additions & 0 deletions circuits/circom/utils/privToPubKey.circom
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pragma circom 2.0.0;

// circomlib imports
include "./bitify.circom";
include "./comparators.circom";
include "./escalarmulfix.circom";

/**
Expand All @@ -15,9 +16,17 @@ template PrivToPubKey() {
16950150798460657717958625567821834550301663161624707787222815936182638968203
];

// Prime subgroup order 'l'.
var l = 2736030358979909402780800718157159386076813972158567259200215660948447373041;

signal input privKey;
signal output pubKey[2];

// Check if private key is in the prime subgroup order 'l'
component isLessThan = LessThan(251);
isLessThan.in <== [privKey, l];
isLessThan.out === 1;

// Convert the private key to bits.
var computedPrivBits[253] = Num2Bits(253)(privKey);

Expand Down
30 changes: 17 additions & 13 deletions circuits/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,24 @@
"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",
"test:slAndBallotTransformer": "ts-mocha --exit ts/__tests__/StateLeafAndBallotTransformer.test.ts",
"test:messageToCommand": "ts-mocha --exit ts/__tests__/MessageToCommand.test.ts",
"test:messageValidator": "ts-mocha --exit ts/__tests__/MessageValidator.test.ts",
"test:verifySignature": "ts-mocha --exit ts/__tests__/VerifySignature.test.ts",
"test:splicer": "ts-mocha --exit ts/__tests__/Splicer.test.ts",
"test:privToPubKey": "ts-mocha --exit ts/__tests__/PrivToPubKey.test.ts",
"test:calculateTotal": "ts-mocha --exit ts/__tests__/CalculateTotal.test.ts",
"test:processMessages": "NODE_OPTIONS=--max-old-space-size=4096 ts-mocha --exit ts/__tests__/ProcessMessages.test.ts",
"test:tallyVotes": "NODE_OPTIONS=--max-old-space-size=4096 ts-mocha --exit ts/__tests__/TallyVotes.test.ts",
"test:ceremonyParams": "ts-mocha --exit ts/__tests__/CeremonyParams.test.ts",
"test:incrementalQuinaryTree": "ts-mocha --exit ts/__tests__/IncrementalQuinaryTree.test.ts"
"mocha-test": "NODE_OPTIONS=--max-old-space-size=4096 ts-mocha --exit -g '^(?!.*\\[fuzz\\]).*$'",
"test": "pnpm run mocha-test ts/__tests__/*.test.ts",
"test:fuzz": "NODE_OPTIONS=--max-old-space-size=4096 ts-mocha --exit -g '\\[fuzz\\]' ./ts/__tests__/*.test.ts",
"test:hasher": "pnpm run mocha-test ts/__tests__/Hasher.test.ts",
"test:slAndBallotTransformer": "pnpm run mocha-test ts/__tests__/StateLeafAndBallotTransformer.test.ts",
"test:messageToCommand": "pnpm run mocha-test ts/__tests__/MessageToCommand.test.ts",
"test:messageValidator": "pnpm run mocha-test ts/__tests__/MessageValidator.test.ts",
"test:verifySignature": "pnpm run mocha-test ts/__tests__/VerifySignature.test.ts",
"test:splicer": "pnpm run mocha-test ts/__tests__/Splicer.test.ts",
"test:privToPubKey": "pnpm run mocha-test ts/__tests__/PrivToPubKey.test.ts",
"test:calculateTotal": "pnpm run mocha-test ts/__tests__/CalculateTotal.test.ts",
"test:processMessages": "pnpm run mocha-test ts/__tests__/ProcessMessages.test.ts",
"test:tallyVotes": "pnpm run mocha-test ts/__tests__/TallyVotes.test.ts",
"test:ceremonyParams": "pnpm run mocha-test ts/__tests__/CeremonyParams.test.ts",
"test:incrementalQuinaryTree": "pnpm run mocha-test ts/__tests__/IncrementalQuinaryTree.test.ts"
},
"dependencies": {
"@zk-kit/baby-jubjub": "^1.0.1",
"@zk-kit/circuits": "^0.4.0",
"circomkit": "^0.2.1",
"circomlib": "^2.0.5",
Expand All @@ -50,6 +53,7 @@
"@types/node": "^20.12.12",
"chai": "^4.3.10",
"chai-as-promised": "^7.1.2",
"fast-check": "^3.18.0",
"mocha": "^10.4.0",
"ts-mocha": "^10.0.0",
"ts-node": "^10.9.1",
Expand Down
49 changes: 47 additions & 2 deletions circuits/ts/__tests__/CalculateTotal.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { r } from "@zk-kit/baby-jubjub";
import { type WitnessTester } from "circomkit";
import fc from "fast-check";

import { circomkitInstance } from "./utils/utils";
import { circomkitInstance, getSignal } from "./utils/utils";

describe("CalculateTotal circuit", function test() {
this.timeout(900000);

describe("CalculateTotal circuit", () => {
let circuit: WitnessTester<["nums"], ["sum"]>;

before(async () => {
Expand All @@ -15,6 +19,7 @@ describe("CalculateTotal circuit", () => {

it("should correctly sum a list of values", async () => {
const nums: number[] = [];

for (let i = 0; i < 6; i += 1) {
nums.push(Math.floor(Math.random() * 100));
}
Expand All @@ -27,4 +32,44 @@ describe("CalculateTotal circuit", () => {

await circuit.expectPass(circuitInputs, { sum });
});

it("should sum max value and loop back", async () => {
const nums: bigint[] = [r, r, r, r, r, r];

await circuit.expectPass({ nums }, { sum: 0n });
});

it("should sum max negative value and loop back", async () => {
const nums: bigint[] = [-r, -r, -r, -r, -r, -r];

await circuit.expectPass({ nums }, { sum: 0n });
});

it("should sum max positive and negative values without looping", async () => {
const nums: bigint[] = [-r, r, -r, r, 1n, 2n];

await circuit.expectPass({ nums }, { sum: 3n });
});

it("should correctly sum a list of values [fuzz]", async () => {
await fc.assert(
fc.asyncProperty(fc.array(fc.bigInt({ min: 0n, max: r - 1n }), { minLength: 1 }), async (nums: bigint[]) => {
const sum = nums.reduce((a, b) => a + b, 0n);
fc.pre(sum <= r - 1n);

const testCircuit = await circomkitInstance.WitnessTester("calculateTotal", {
file: "./utils/calculateTotal",
template: "CalculateTotal",
params: [nums.length],
});

const witness = await testCircuit.calculateWitness({ nums });
await testCircuit.expectConstraintPass(witness);
const total = await getSignal(testCircuit, witness, "sum");

return total === sum;
}),
{ numRuns: 10_000 },
);
});
});
Loading

0 comments on commit 987b20d

Please sign in to comment.