mirror of
https://github.com/erhant/circomkit.git
synced 2026-05-05 03:00:37 -04:00
todo test & dir refactors
This commit is contained in:
@@ -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: [],
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
6
circuits/test/cbl_3.circom
Normal file
6
circuits/test/cbl_3.circom
Normal file
@@ -0,0 +1,6 @@
|
||||
// auto-generated by instantiate.js
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "../float_add.circom";
|
||||
|
||||
component main = CheckBitLength(3);
|
||||
@@ -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",
|
||||
|
||||
@@ -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>"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`);
|
||||
|
||||
@@ -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
27
tests/float_add.test.ts
Normal 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,
|
||||
});
|
||||
});
|
||||
});
|
||||
26
tests/multiplier.proofs.test.ts
Normal file
26
tests/multiplier.proofs.test.ts
Normal 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;
|
||||
});
|
||||
});
|
||||
@@ -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')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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.')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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==
|
||||
|
||||
Reference in New Issue
Block a user