todo test & dir refactors

This commit is contained in:
Erhan Tezcan
2023-04-01 16:22:02 +03:00
parent 5114061283
commit e5b68f122c
14 changed files with 226 additions and 115 deletions

View File

@@ -38,7 +38,7 @@ const config = {
templateParams: [11],
},
// checks that a number fits to given bit count
checkBitLength: {
cbl_3: {
file: 'float_add',
template: 'CheckBitLength',
publicInputs: [],

View File

@@ -2,12 +2,12 @@ pragma circom 2.0.0;
// this code is taken from ZKP MOOC 2023 lab, and then modified
include "circomlib/circuits/comparators.sol";
include "circomlib/circuits/switcher.sol";
include "circomlib/circuits/gates.sol";
include "circomlib/circuits/bitify.sol";
include "circomlib/circuits/comparators.circom";
include "circomlib/circuits/switcher.circom";
include "circomlib/circuits/gates.circom";
include "circomlib/circuits/bitify.circom";
include "./math/bits.sol";
include "functions/bits.circom";
/*
* Basically `out = cond ? ifTrue : ifFalse`

View File

@@ -1,17 +1,37 @@
pragma circom 2.0.0;
include "circomlib/circuits/bitify.circom";
include "./functions/bits.circom";
include "functions/bits.circom";
// Ensures that number is representable by b-bits
template CheckBitLength(b) {
assert(b < 254);
signal input in;
// compute b-bit representation of the number
signal bits[b];
var sum_of_bits = 0;
for (var i = 0; i < b; i++) {
bits[i] <-- (in >> i) & 1;
bits[i] * (1 - bits[i]) === 0;
sum_of_bits += (2 ** i) * bits[i];
}
// check if sum is equal to number itself
in === sum_of_bits;
}
// Assert that two elements are not equal
template NonEqual() {
signal input in[2];
signal output out;
signal inv;
// we check if (in[0] - in[1] != 0)
// because 1/0 results in 0, so the constraint won't hold
inv <-- 1 / (in[1] - in[0]);
inv * (in[1] - in[0]) === 1;
0 * inv === 0; // silence error
out <== inv * (in[1] - in[0]);
}
// Assert that all given values are unique
@@ -22,6 +42,7 @@ template Distinct(n) {
for(var j = 0; j < i; j++){
nonEqual[i][j] = NonEqual();
nonEqual[i][j].in <== [in[i], in[j]];
nonEqual[i][j].out === 1;
}
}
}
@@ -31,8 +52,8 @@ template InRange(MIN, MAX) {
signal input in;
var b = numOfBits(MAX);
component lowerBound = Num2Bits(b);
component upperBound = Num2Bits(b);
component lowerBound = CheckBitLength(b);
component upperBound = CheckBitLength(b);
lowerBound.in <== in - MIN; // e.g. 1 - 1 = 0 (for 0 <= in)
upperBound.in <== in + (2 ** b) - MAX - 1; // e.g. 9 + 6 = 15 (for in <= 15)
}

View File

@@ -0,0 +1,6 @@
// auto-generated by instantiate.js
pragma circom 2.0.0;
include "../float_add.circom";
component main = CheckBitLength(3);

View File

@@ -17,7 +17,6 @@
"author": "erhant",
"devDependencies": {
"@types/chai": "^4.3.4",
"@types/chai-as-promised": "^7.1.5",
"@types/mocha": "^10.0.1",
"@types/node": "^18.11.18",
"chai": "^4.3.7",

View File

@@ -17,12 +17,17 @@ source ./scripts/functions/compile.sh
# get arguments
NUM_CONTRIBS=1 # default value
while getopts "f:c:n:i:p:" opt; do
COMPILE_DIR="main" # default dir
while getopts "f:c:n:i:p:d:" opt; do
case $opt in
# function to call
f)
FUNC="$OPTARG"
;;
# director for compilation (default: main)
d)
COMPILE_DIR="$OPTARG"
;;
# circuit name
c)
CIRCUIT="$OPTARG"
@@ -61,7 +66,7 @@ case $FUNC in
clean $CIRCUIT
;;
compile)
compile $CIRCUIT
compile $CIRCUIT $COMPILE_DIR
;;
type)
type $CIRCUIT
@@ -89,6 +94,7 @@ case $FUNC in
echo " prove Prove an input"
echo " verify Verify a proof & public signals"
echo " -c <circuit-name>"
echo " -d <directory-name>"
echo " -n <num-contributions> (default: 1)"
echo " -i <input-name>"
echo " -p <phase1-ptau-path>"

View File

@@ -2,11 +2,13 @@
compile() {
echo -e "\n${CLIENV_COLOR_TITLE}=== Compiling the circuit ===${CLIENV_COLOR_RESET}"
local CIRCUIT=$1
local DIR=$2
local CIRCOM_IN=./circuits/$DIR/$CIRCUIT.circom
local CIRCOM_OUT=./build/$CIRCUIT
# generate the circuit main component
node ./scripts/instantiate.js $CIRCUIT
local CIRCOM_IN=./circuits/main/$CIRCUIT.circom
local CIRCOM_OUT=./build/$CIRCUIT
mkdir -p ./circuits/$DIR
node ./scripts/instantiate.js $CIRCUIT $DIR
# create build dir if not exists already
mkdir -p $CIRCOM_OUT

View File

@@ -1,28 +1,19 @@
const ejs = require('ejs');
const {writeFileSync, readFileSync, existsSync, mkdirSync} = require('fs');
const {writeFileSync, readFileSync} = require('fs');
const config = require('../circuit.config.cjs');
// read circuit from config
const target = process.argv[2];
const dir = process.argv[3];
if (!(target in config)) {
throw new Error(`Target ${target} not found in config.`);
}
// generate the main component code
const templatePath = './circuits/ejs/template.circom';
let circuit = ejs.render(readFileSync(templatePath).toString(), config[target]);
const ejsPath = './circuits/ejs/template.circom';
let circuit = ejs.render(readFileSync(ejsPath).toString(), config[target]);
// output to file
const dirName = config[target].dir ? config[target].dir : 'main';
if (typeof dirName !== 'string') {
throw new Error(`Bad target type.`);
}
const dir = `./circuits/${dirName}`;
if (!existsSync(dir)) {
mkdirSync(dir, {recursive: true});
}
const targetPath = `${dir}/${target}.circom`;
const targetPath = `./circuits/${dir}/${target}.circom`;
writeFileSync(targetPath, circuit);
console.log(`Main component created at: ${targetPath}\n`);

View File

@@ -21,25 +21,23 @@ describe(CIRCUIT_NAME, () => {
in: [1, 1],
};
describe('witness computation', () => {
let circuit: Awaited<ReturnType<typeof createWasmTester>>;
let circuit: Awaited<ReturnType<typeof createWasmTester>>;
before(async () => {
circuit = await createWasmTester(CIRCUIT_NAME);
});
before(async () => {
circuit = await createWasmTester(CIRCUIT_NAME);
});
it('should compute correctly', async () => {
// compute witness
const witness = await circuit.calculateWitness(INPUT, true);
it('should compute correctly', async () => {
// compute witness
const witness = await circuit.calculateWitness(INPUT, true);
// witness should have valid constraints
await circuit.checkConstraints(witness);
// witness should have valid constraints
await circuit.checkConstraints(witness);
// witness should have correct output
const output = {
out: fibonacci(INPUT.in, 11),
};
await circuit.assertOut(witness, output);
});
// witness should have correct output
const output = {
out: fibonacci(INPUT.in, 11),
};
await circuit.assertOut(witness, output);
});
});

27
tests/float_add.test.ts Normal file
View File

@@ -0,0 +1,27 @@
import {createWasmTester} from '../utils/wasmTester';
import {ProofTester} from '../utils/proofTester';
import type {CircuitSignals, FullProof} from '../types/circuit';
import {assert, expect} from 'chai';
// TODO: write tests
const CIRCUIT_NAME = 'cbl_32';
describe('utils', () => {
let circuit: Awaited<ReturnType<typeof createWasmTester>>;
before(async () => {
circuit = await createWasmTester(CIRCUIT_NAME, 'test');
});
it('should compute correctly', async () => {
const witness = await circuit.calculateWitness(
{
in: 3,
},
true
);
await circuit.checkConstraints(witness);
await circuit.assertOut(witness, {
out: 1,
});
});
});

View File

@@ -0,0 +1,26 @@
import {ProofTester} from '../utils/proofTester';
import type {CircuitSignals, FullProof} from '../types/circuit';
import {assert, expect} from 'chai';
// read inputs from file
import input80 from '../inputs/multiplier3/80.json';
const CIRCUIT_NAME = 'multiplier3';
describe(CIRCUIT_NAME + ' (proofs)', () => {
const INPUT: CircuitSignals = input80;
let fullProof: FullProof;
const circuit = new ProofTester(CIRCUIT_NAME);
before(async () => {
fullProof = await circuit.prove(INPUT);
});
it('should verify', async () => {
expect(await circuit.verify(fullProof.proof, fullProof.publicSignals)).to.be.true;
});
it('should NOT verify a wrong multiplication', async () => {
// just give a prime number, assuming there are no factors of 1
expect(await circuit.verify(fullProof.proof, ['13'])).to.be.false;
});
});

View File

@@ -9,56 +9,36 @@ const CIRCUIT_NAME = 'multiplier3';
describe(CIRCUIT_NAME, () => {
const INPUT: CircuitSignals = input80;
describe('witness computation', () => {
let circuit: Awaited<ReturnType<typeof createWasmTester>>;
let circuit: Awaited<ReturnType<typeof createWasmTester>>;
before(async () => {
circuit = await createWasmTester(CIRCUIT_NAME);
});
it('should compute correctly', async () => {
const witness = await circuit.calculateWitness(INPUT, true);
// witness should have valid constraints
await circuit.checkConstraints(witness);
// witness should have correct output
const output = {
out: BigInt(INPUT.in.reduce((prev: bigint, acc: bigint) => acc * prev)),
};
await circuit.assertOut(witness, output);
});
it('should NOT compute with wrong number of inputs', async () => {
const fewInputs = INPUT.in.slice(1);
await circuit.calculateWitness({in: fewInputs}, true).then(
() => assert.fail('expected to fail on fewer inputs'),
err => expect(err.message).to.eq('Not enough values for input signal in\n')
);
const manyInputs = [2n, ...INPUT.in];
await circuit.calculateWitness({in: manyInputs}, true).then(
() => assert.fail('expected to fail on too many inputs'),
err => expect(err.message).to.eq('Too many values for input signal in\n')
);
});
before(async () => {
circuit = await createWasmTester(CIRCUIT_NAME);
});
describe('proof verification', () => {
let fullProof: FullProof;
const circuit = new ProofTester(CIRCUIT_NAME);
it('should compute correctly', async () => {
const witness = await circuit.calculateWitness(INPUT, true);
before(async () => {
fullProof = await circuit.prove(INPUT);
});
// witness should have valid constraints
await circuit.checkConstraints(witness);
it('should verify', async () => {
expect(await circuit.verify(fullProof.proof, fullProof.publicSignals)).to.be.true;
});
// witness should have correct output
const output = {
out: BigInt(INPUT.in.reduce((prev: bigint, acc: bigint) => acc * prev)),
};
await circuit.assertOut(witness, output);
});
it('should NOT verify a wrong multiplication', async () => {
// just give a prime number, assuming there are no factors of 1
expect(await circuit.verify(fullProof.proof, ['13'])).to.be.false;
});
it('should NOT compute with wrong number of inputs', async () => {
const fewInputs = INPUT.in.slice(1);
await circuit.calculateWitness({in: fewInputs}, true).then(
() => assert.fail(),
err => expect(err.message).to.eq('Not enough values for input signal in\n')
);
const manyInputs = [2n, ...INPUT.in];
await circuit.calculateWitness({in: manyInputs}, true).then(
() => assert.fail(),
err => expect(err.message).to.eq('Too many values for input signal in\n')
);
});
});

View File

@@ -1,26 +1,88 @@
import {createWasmTester} from '../utils/wasmTester';
import type {CircuitSignals, FullProof} from '../types/circuit';
import {assert, expect} from 'chai';
// read inputs from file
import inputfoo from '../inputs/sudoku_9x9/example.json';
const CIRCUIT_NAME = 'sudoku9';
const CIRCUIT_NAME = 'sudoku_9x9';
describe(CIRCUIT_NAME, () => {
const INPUT: CircuitSignals = inputfoo;
const INPUT = {
solution: [
[1, 9, 4, 8, 6, 5, 2, 3, 7],
[7, 3, 5, 4, 1, 2, 9, 6, 8],
[8, 6, 2, 3, 9, 7, 1, 4, 5],
[9, 2, 1, 7, 4, 8, 3, 5, 6],
[6, 7, 8, 5, 3, 1, 4, 2, 9],
[4, 5, 3, 9, 2, 6, 8, 7, 1],
[3, 8, 9, 6, 5, 4, 7, 1, 2],
[2, 4, 6, 1, 7, 9, 5, 8, 3],
[5, 1, 7, 2, 8, 3, 6, 9, 4],
],
puzzle: [
[0, 0, 0, 8, 6, 0, 2, 3, 0],
[7, 0, 5, 0, 0, 0, 9, 0, 8],
[0, 6, 0, 3, 0, 7, 0, 4, 0],
[0, 2, 0, 7, 0, 8, 0, 5, 0],
[0, 7, 8, 5, 0, 0, 0, 0, 0],
[4, 0, 0, 9, 0, 6, 0, 7, 0],
[3, 0, 9, 0, 5, 0, 7, 0, 2],
[0, 4, 0, 1, 0, 9, 0, 8, 0],
[5, 0, 7, 0, 8, 0, 0, 9, 4],
],
};
describe('witness computation', () => {
let circuit: Awaited<ReturnType<typeof createWasmTester>>;
let circuit: Awaited<ReturnType<typeof createWasmTester>>;
before(async () => {
circuit = await createWasmTester(CIRCUIT_NAME);
});
before(async () => {
circuit = await createWasmTester(CIRCUIT_NAME);
});
it('should compute correctly', async () => {
// compute witness
const witness = await circuit.calculateWitness(INPUT, true);
it('should compute correctly', async () => {
// compute witness
const witness = await circuit.calculateWitness(INPUT, true);
// witness should have valid constraints
await circuit.checkConstraints(witness);
});
// witness should have valid constraints
await circuit.checkConstraints(witness);
});
it('should NOT accept non-distinct rows', async () => {
const badInput = JSON.parse(JSON.stringify(INPUT));
badInput.solution[0][0] = badInput.solution[0][1];
console.log(badInput.solution[0], badInput.solution[1]);
await circuit.calculateWitness(INPUT, true).then(
() => assert.fail(),
err => expect(err.message.slice(0, 21)).to.eq('Error: Assert Failed.')
);
});
it('should NOT accept non-distinct columns', async () => {
const badInput = JSON.parse(JSON.stringify(INPUT));
badInput.solution[0][0] = badInput.solution[1][0];
console.log(badInput.solution[0], badInput.solution[1]);
await circuit.calculateWitness(INPUT, true).then(
() => assert.fail(),
err => expect(err.message.slice(0, 21)).to.eq('Error: Assert Failed.')
);
});
it('should NOT accept non-distinct square', async () => {
const badInput: typeof INPUT = JSON.parse(JSON.stringify(INPUT));
badInput.solution[0][0] = badInput.solution[1][1];
console.log(badInput.solution[0], badInput.solution[1]);
await circuit.calculateWitness(INPUT, true).then(
() => assert.fail(),
err => expect(err.message.slice(0, 21)).to.eq('Error: Assert Failed.')
);
});
it('should NOT accept empty value in solution', async () => {
const badInput = JSON.parse(JSON.stringify(INPUT));
badInput.solution[0][0] = 0;
console.log(badInput.solution[0], badInput.solution[1]);
await circuit.calculateWitness(badInput, true).then(
() => assert.fail(),
err => expect(err.message.slice(0, 21)).to.eq('Error: Assert Failed.')
);
});
});

View File

@@ -480,14 +480,7 @@
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
"@types/chai-as-promised@^7.1.5":
version "7.1.5"
resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz#6e016811f6c7a64f2eed823191c3a6955094e255"
integrity sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==
dependencies:
"@types/chai" "*"
"@types/chai@*", "@types/chai@^4.3.4":
"@types/chai@^4.3.4":
version "4.3.4"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4"
integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==