added main comp generator, some rfks

This commit is contained in:
Erhan Tezcan
2023-03-30 00:32:34 +03:00
parent 0ff445fef3
commit 1e4aa1daa0
22 changed files with 168 additions and 34 deletions

View File

@@ -10,21 +10,23 @@ The repository follows an _opinionated file structure_ shown below, abstracting
```sh
circom-ts-starter
├── circuits # where you write templates
│ ├── main # where you instantiate components
├── circuit.config.cjs # configs for circuit main components
├── .cli.env # environment variables for cli
├── circuits # where you write templates
│ ├── main # where you instantiate components
│ │ │── foo-main.circom
│ │ └── ...
│ │── foo.circom
│ └── ...
├── inputs # where you write JSON inputs per circuit
├── inputs # where you write JSON inputs per circuit
│ ├── foo
│ │ ├── input-name.json
│ │ └── ...
│ └── ...
├── ptau # universal phase-1 setups
├── ptau # universal phase-1 setups
│ ├── powersOfTau28_hez_final_12.ptau
│ └── ...
└── build # artifacts, .gitignore'd
└── build # artifacts, .gitignore'd
│── foo-main
│ │── foo-main_js # artifacts of compilation
│ │ │── generate_witness.js
@@ -42,14 +44,22 @@ circom-ts-starter
└── ...
```
Write your circuits under `circuits` folder. The circuit code itself should be templates only. You should only create the main component under `circuits/main` folder.
Write your circuits under `circuits` folder; the circuit code itself should be templates only. The main component itself is created automatically via a [script](./scripts/instantiate.js) which uses a simple EJS [template](./circuits/main/_template.circom) to create the main component. The target circuits are defined under the [circuit configs](./circuit.config.cjs) file, such as:
```js
multiplier3: {
file: 'multiplier',
template: 'Multiplier',
publicInputs: [],
templateInputs: [3],
}
```
Use the [CLI](./scripts/cli.sh), or its wrapper scripts in [package.json](./package.json) to do stuff with your circuits.
```bash
yarn compile -c circuit-name
yarn clean -c circuit-name
yarn type -c circuit-name
yarn ptau -c circuit-name -n num-contribs -p phase1-ptau-path
yarn prove -c circuit-name -i input-name
yarn verify -c circuit-name -i input-name

30
circuit.config.cjs Normal file
View File

@@ -0,0 +1,30 @@
module.exports = {
// multiplication of 3 numbers
multiplier3: {
file: 'multiplier',
template: 'Multiplier',
publicInputs: [],
templateInputs: [3],
},
// A 9x9 sudoku board
sudoku9: {
file: 'sudoku',
template: 'Sudoku',
publicInputs: ['puzzle'],
templateInputs: [3],
},
// 64-bit floating point, 11-bit exponent and 52-bit mantissa
fp64: {
file: 'float_add',
template: 'FloatAdd',
publicInputs: [],
templateInputs: [11, 52],
},
// 32-bit floating point, 8-bit exponent and 23-bit mantissa
fp32: {
file: 'float_add',
template: 'FloatAdd',
publicInputs: [],
templateInputs: [8, 23],
},
};

18
circuits/fibonacci.circom Normal file
View File

@@ -0,0 +1,18 @@
pragma circom 2.0.0;
// Fibonacci with custom starting numbers
template Fibonacci(n) {
assert(n >= 2);
signal input in[2];
signal output out;
// compute the sequence
signal f[n+1];
fib[0] <== in[0];
fib[1] <== in[1];
for (var i = 2; i <= n; i++) {
fib[i] <== fib[i-2] + fib[i-1];
}
out <== fib[n];
}

View File

@@ -0,0 +1,12 @@
<%#'this file is an EJS template to generate main component for circuits'-%>
<%#'configuration is read from config.js in the project root directory'-%>
// auto-generated by instantiate.js
pragma circom 2.0.0;
include "../../circuits/<%= file %>.circom";
component main<%=
publicInputs.length == 0 ?
'' :
' {public[' + publicInputs.join(", ") + ']}'
%> = <%= template %>(<%= templateInputs.join(", ") %>);

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
// auto-generated by instantiate.js
pragma circom 2.0.0;
include "../../circuits/multiplier.circom";
// Multiply 3 numbers
component main = Multiplier(3);

View File

@@ -2,5 +2,5 @@ pragma circom 2.0.0;
include "../../circuits/sudoku.circom";
// Circuit for 3^2 * 3^2 sudoku
// Circuit for 3^2 x 3^2 sudoku
component main {public[puzzle]} = Sudoku(3);

View File

@@ -27,6 +27,5 @@ template Multiplier(N){
comp[i+1].in1 <== comp[i].out;
comp[i+1].in2 <== in[i+2];
}
out <== comp[N-2].out;
}

View File

@@ -22,6 +22,7 @@
"circom_tester": "^0.0.19",
"circomlib": "^2.0.5",
"circomlibjs": "^0.1.7",
"ejs": "^3.1.9",
"gts": "^3.1.1",
"mocha": "^10.2.0",
"snarkjs": "^0.5.0",

View File

@@ -78,9 +78,6 @@ case $FUNC in
verify)
verify $CIRCUIT $INPUT
;;
keygen)
compile $CIRCUIT && ptau $CIRCUIT $NUM_CONTRIBS
;;
*)
echo "Usage:"
echo " -f <function>"

View File

@@ -5,13 +5,14 @@ compile() {
local CIRCOM_IN=./circuits/main/$CIRCUIT.circom
local CIRCOM_OUT=./build/$CIRCUIT
echo "Compiler: $CLIENV_COMPILER_ARGS"
# generate the circuit main component
node ./scripts/instantiate.js $CIRCUIT
# create build dir if not exists already
mkdir -p $CIRCOM_OUT
# compile with circom
circom $CIRCOM_IN -o $CIRCOM_OUT --r1cs --sym --wasm
circom $CIRCOM_IN -o $CIRCOM_OUT --r1cs --wasm --sym
echo -e "${CLIENV_COLOR_LOG}Built artifacts under $CIRCOM_OUT${CLIENV_COLOR_RESET}"
}

View File

@@ -1,11 +1,18 @@
## Parse the symbols file and generate types for TypeScript
## Parse the template circuit that you are using for your main component
## and generate TypeScript interfaces for it
## TODO: not sure i need this yet
type() {
echo -e "\n${CLIENV_COLOR_TITLE}=== Generating types ===${CLIENV_COLOR_RESET}"
CIRCUIT=$1
SYM=./build/$CIRCUIT/$CIRCUIT.sym
set -e
# choose lines with 1 dot only (these are the signals in main file), extract their names
cat $SYM | grep -E '^.+main[^.]*\.[^.]*$'
echo -e "\n${CLIENV_COLOR_TITLE}=== Generating types ===${CLIENV_COLOR_RESET}"
local CIRCUIT=$1
local SYM=./build/$CIRCUIT/$CIRCUIT.sym
local TMP=./scripts/utils.tmp.txt
# choose lines with 1 dot only (these are the signals of the main component), extract their names
cat $SYM | awk -F '.' '{print $2}'
echo -e "\n${CLIENV_COLOR_LOG}Types generated!${CLIENV_COLOR_RESET}"
}
# cat ./build/multiplier3/multiplier3.sym | awk -F '.' 'NF==2{print $2}'

17
scripts/instantiate.js Normal file
View File

@@ -0,0 +1,17 @@
const ejs = require('ejs');
const {writeFileSync, readFileSync} = require('fs');
const config = require('../circuit.config.cjs');
// read circuit from config
const target = process.argv[2];
if (!(target in config)) {
throw new Error(`Target ${target} not found in config.`);
}
// generate the main component code
let circuit = ejs.render(readFileSync('./circuits/main/_template.circom').toString(), config[target]);
// output to file
const targetPath = `./circuits/main/${target}.circom`;
writeFileSync(targetPath, circuit);
console.log(`Main component created at: ${targetPath}\n`);

View File

@@ -1,3 +0,0 @@
for (let arg of process.argv.slice(2)) {
console.log(arg);
}

View File

@@ -1,5 +1,5 @@
import {compileCircuit} from '../utils';
import {Circuit} from '../utils/circuit';
import {ProofTester} from '../utils/proofTester';
import type {CircuitSignals, FullProof} from '../types/circuit';
import type {WasmTester} from '../types/wasmTester';
import {assert, expect} from 'chai';
@@ -10,7 +10,7 @@ const CIRCUIT_NAME = 'multiplier3';
describe(CIRCUIT_NAME, () => {
const INPUT: CircuitSignals = input80;
describe('functionality', () => {
describe('witness computation', () => {
let circuit: WasmTester;
before(async () => {
@@ -57,10 +57,10 @@ describe(CIRCUIT_NAME, () => {
});
});
describe('validation', () => {
describe('proof verification', () => {
let fullProof: FullProof;
const circuit = new Circuit(CIRCUIT_NAME);
const circuit = new ProofTester(CIRCUIT_NAME);
before(async () => {
fullProof = await circuit.prove(INPUT);

View File

@@ -1,5 +1,4 @@
import {compileCircuit} from '../utils';
import {Circuit} from '../utils/circuit';
import type {CircuitSignals, FullProof} from '../types/circuit';
import type {WasmTester} from '../types/wasmTester';
import {assert, expect} from 'chai';
@@ -10,7 +9,7 @@ const CIRCUIT_NAME = 'sudoku9';
describe(CIRCUIT_NAME, () => {
const INPUT: CircuitSignals = inputfoo;
describe('functionality', () => {
describe('witness computation', () => {
let circuit: WasmTester;
before(async () => {

View File

@@ -5,7 +5,8 @@
export type CircuitSignals = {[signalName: string]: any};
/**
* A witness is just an array of strings.
* A witness is an array of bigints, corresponding to the values of each wire in
* the evaluation of the circuit.
*/
export type WitnessType = bigint[];

0
types/proofTester.ts Normal file
View File

View File

@@ -7,10 +7,15 @@ import {WitnessType, CircuitSignals} from './circuit';
export type WasmTester = {
/**
* Assert that constraints are valid.
* @param witness
* @param witness witness
*/
checkConstraints: (witness: WitnessType) => Promise<void>;
/**
* Cleanup directory, should probably be called upon test completion
*/
release(): Promise<void>;
/**
* Assert the output of a given witness.
* @param actualOut expected output signals
@@ -32,7 +37,28 @@ export type WasmTester = {
/**
* List of constraints, must call `loadConstraints` before
* accessing this key.
* accessing this key
*/
constraints: any[] | undefined;
/**
* Loads the symbols in a dictionary at `this.symbols`
* Symbols are stored under the .sym file
* Each line has 4 comma-separated values:
* 0: label index
* 1: variable index
* 2: component index
*/
loadSymbols(): Promise<void>;
/**
* A dictionary of symbols
*/
symbols: object;
/**
* @deprecated this is buggy right now
* @param witness witness
*/
getDecoratedOutput(witness: WitnessType): Promise<string>;
};

View File

@@ -6,7 +6,7 @@ import {CircuitSignals, FullProof} from '../types/circuit';
* A more extensive Circuit class, able to generate proofs & verify them.
* Assumes that prover key and verifier key have been computed.
*/
export class Circuit {
export class ProofTester {
private readonly wasmPath: string;
private readonly proverKeyPath: string;
private readonly verificationKey: object;

View File

@@ -1083,6 +1083,13 @@ ejs@^3.1.6:
dependencies:
jake "^10.8.5"
ejs@^3.1.9:
version "3.1.9"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361"
integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==
dependencies:
jake "^10.8.5"
elliptic@6.5.4:
version "6.5.4"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"