diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4cd7102..875f322 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -7,25 +7,6 @@ on:
jobs:
build:
- name: Build checks
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v2
-
- - name: Install Node.js
- uses: actions/setup-node@v1
- with:
- node-version: 18.x
-
- - name: Install dependencies
- run: yarn
-
- - name: Build everything
- run: yarn build
-
- style:
- name: Styling checks
runs-on: ubuntu-latest
steps:
@@ -44,3 +25,6 @@ jobs:
- name: Lint code
run: yarn lint
+
+ - name: Build everything
+ run: yarn build
diff --git a/.github/workflows/test.yml b/.github/workflows/tests.yml
similarity index 72%
rename from .github/workflows/test.yml
rename to .github/workflows/tests.yml
index 51aff4a..7aa2109 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/tests.yml
@@ -1,10 +1,9 @@
-name: test
+name: tests
on:
push:
branches:
- main
- - erhant/tests
jobs:
test:
@@ -22,6 +21,13 @@ jobs:
cd circom
cargo build --release
cargo install --path circom
+ cd ..
- - name: Print help
+ - name: Print Circom version
run: circom --help
+
+ - name: Install dependencies
+ run: yarn
+
+ - name: Run tests
+ run: yarn test
diff --git a/.gitignore b/.gitignore
index 79992c2..7d68eb5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -107,19 +107,12 @@ dist
build
dist
-# circuit-specific powers of tau are ignored
-*.ptau
-# universal ptaus not ignored
-!ptau/*
-# temporary ptaus are ignored
-tmp.ptau
-
# is this still a thing lol
.DS_Store
# ignore auto generated test circuits
circuits/test
-ptau
# for init tests
tmptest
+inputs/multiplier_3/test-input.json
diff --git a/README.md b/README.md
index 211f286..bb60ea1 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,9 @@
+
+
+
@@ -126,7 +129,7 @@ npx circomkit calldata circuit input
Circomkit with its default configuration follows an _opinionated file structure_, abstracting away the pathing and orientation behind the scenes. All of these can be customized by overriding the respective settings in `circomkit.json`.
-Here is an example structure, where we have a generic Sudoku proof-of-solution circuit, and we instantiate it for a 9x9 board:
+An example structure is shown below. Suppose there is a generic circuit for a Sudoku solution knowledge proof written under `circuits` folder. When instantiated, a `main` component for a 9x9 board is created under `circuits/main`. The solution along with it's puzzle is stored as a JSON object under `inputs/sudoku_9x9`. You can see the respective artifacts under `build` directory. In particular, we see `groth16` prefix on some files, indicating that Groth16 protocol was used to create them.
```sh
circomkit
@@ -159,8 +162,10 @@ circomkit
│
│── sudoku_9x9.r1cs
│── sudoku_9x9.sym
- │── prover_key.zkey
- └── verifier_key.json
+ │
+ │── groth16_pkey.zkey
+ │── groth16_vkey.json
+ └── groth16_verifier.sol
```
@@ -172,6 +177,8 @@ Run all tests via:
yarn test
```
+You can also use the CLI in the repo by `yarn cli` as if you are using `npx circomkit`. This is useful for hands-on testing stuff.
+
## Styling
Circomkit uses [Google TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html).
diff --git a/package.json b/package.json
index ce1ef7a..40cc2f3 100644
--- a/package.json
+++ b/package.json
@@ -37,27 +37,24 @@
"devDependencies": {
"@types/chai": "^4.3.4",
"@types/mocha": "^10.0.1",
+ "@types/mocha-each": "^2.0.0",
"@types/node": "^18.11.18",
"gts": "^3.1.1",
"mocha": "^10.2.0",
+ "mocha-each": "^2.0.1",
"rimraf": "^5.0.1",
"ts-node": "^10.9.1",
"typescript": "^4.9.5"
},
- "optionalDependencies": {
- "circomlib": "^2.0.5"
- },
"dependencies": {
"chai": "^4.3.7",
"circom_tester": "^0.0.19",
"loglevel": "^1.8.1",
- "snarkjs": "^0.6.0"
+ "snarkjs": "^0.7.0"
},
"keywords": [
"circom",
- "zk",
"zero knowledge",
- "zero-knowledge",
"snarkjs",
"typescript",
"cli",
diff --git a/ptau/powersOfTau28_hez_final_08.ptau b/ptau/powersOfTau28_hez_final_08.ptau
new file mode 100644
index 0000000..7fe5f5b
Binary files /dev/null and b/ptau/powersOfTau28_hez_final_08.ptau differ
diff --git a/src/bin/index.ts b/src/bin/index.ts
index 2c556ab..cb0f747 100644
--- a/src/bin/index.ts
+++ b/src/bin/index.ts
@@ -1,58 +1,11 @@
#!/usr/bin/env node
import {existsSync, mkdirSync, readFileSync, writeFileSync} from 'fs';
import {Circomkit} from '../circomkit';
-import {initFiles, postInitString} from '../utils/initFiles';
+import {initFiles, postInitString, usageString} from '../utils';
+import {prettyStringify} from '../utils';
const CONFIG_PATH = './circomkit.json';
const DEFAULT_INPUT = 'default';
-const USAGE = `Usage:
-
- Compile the circuit.
- > compile circuit
-
- Create main component.
- > instantiate circuit
-
- Print circuit information.
- > info circuit
-
- Clean build artifacts & main component.
- > clean circuit
-
- Export Solidity verifier.
- > contract circuit
-
- Export calldata for a verifier contract.
- > calldata circuit input
-
- Export JSON for a chosen file.
- > json r1cs circuit
- > json zkey circuit
- > json wtns circuit input
-
- Commence circuit-specific setup.
- > setup circuit
- > setup circuit ptau-path
-
- Download the PTAU file needed for the circuit.
- > ptau circuit
-
- Generate a proof.
- > prove circuit input
-
- Verify a proof.
- > verify circuit input
-
- Generate a witness.
- > witness circuit input
-
- Initialize a Circomkit project.
- > init # initializes in current folder
- > init project-name # initializes in a new folder
-
- Print configurations to console.
- > config
-`;
async function cli(): Promise {
// read user configs & override if there are any
@@ -91,12 +44,12 @@ async function cli(): Promise {
case 'json': {
titleLog('Exporting JSON file');
- const path = await circomkit.json(
+ const {json, path} = await circomkit.json(
process.argv[3] as 'r1cs' | 'zkey' | 'wtns',
process.argv[4],
- process.argv[5],
- true
+ process.argv[5]
);
+ writeFileSync(path, prettyStringify(json));
circomkit.log('Exported at: ' + path, 'success');
break;
}
@@ -163,8 +116,8 @@ async function cli(): Promise {
case 'setup': {
titleLog('Circuit-specific setup');
const paths = await circomkit.setup(process.argv[3]);
- circomkit.log('Prover key created: ' + paths.proverKey, 'success');
- circomkit.log('Verifier key created: ' + paths.verifierKey, 'success');
+ circomkit.log('Prover key created: ' + paths.proverKeyPath, 'success');
+ circomkit.log('Verifier key created: ' + paths.verifierKeyPath, 'success');
break;
}
@@ -196,7 +149,7 @@ async function cli(): Promise {
}
default:
- console.log(USAGE);
+ console.log(usageString);
return 1;
}
diff --git a/src/circomkit.ts b/src/circomkit.ts
index de359bb..1f5cf3d 100644
--- a/src/circomkit.ts
+++ b/src/circomkit.ts
@@ -4,7 +4,7 @@ import {writeFileSync, readFileSync, existsSync, mkdirSync, rmSync, renameSync}
import {readFile, rm, writeFile} from 'fs/promises';
import {instantiate} from './utils/instantiate';
import {downloadPtau, getPtauName} from './utils/ptau';
-import type {CircuitConfig, R1CSInfoType} from './types/circuit';
+import type {CircuitConfig, CircuitSignals, R1CSInfoType} from './types/circuit';
import {Logger, getLogger} from 'loglevel';
import type {
CircomkitConfig,
@@ -16,7 +16,7 @@ import {randomBytes} from 'crypto';
import {CircomWasmTester} from './types/circom_tester';
import WasmTester from './testers/wasmTester';
import ProofTester from './testers/proofTester';
-import {primeToCurveName} from './utils/curves';
+import {prettyStringify, primeToCurveName} from './utils';
import {defaultConfig, colors, CURVES, PROTOCOLS} from './utils/config';
/**
@@ -29,7 +29,7 @@ import {defaultConfig, colors, CURVES, PROTOCOLS} from './utils/config';
* const circomkit = new Circomkit()
* ```
*
- * It also provides a WasmTester and a ProofTester module which uses Chai assertions within.
+ * It also provides a **WasmTester** and a **ProofTester** module which uses Chai assertions within.
*
* ```ts
* const wasmTester = await circomkit.WasmTester(circuitName, circuitConfig)
@@ -65,11 +65,6 @@ export class Circomkit {
}
}
- /** Pretty-print for JSON stringify. */
- private prettyStringify(obj: unknown): string {
- return JSON.stringify(obj, undefined, 2);
- }
-
/** Parse circuit config from `circuits.json` */
private readCircuitConfig(circuit: string): CircuitConfig {
const circuits = JSON.parse(readFileSync(this.config.circuits, 'utf-8'));
@@ -92,18 +87,18 @@ export class Circomkit {
return dir;
case 'target':
return `${this.config.dirCircuits}/main/${circuit}.circom`;
- case 'pkey':
- return `${dir}/prover_key.zkey`;
- case 'vkey':
- return `${dir}/verifier_key.json`;
case 'r1cs':
return `${dir}/${circuit}.r1cs`;
case 'sym':
return `${dir}/${circuit}.sym`;
- case 'sol':
- return `${dir}/Verifier_${this.config.protocol}.sol`;
case 'wasm':
return `${dir}/${circuit}_js/${circuit}.wasm`;
+ case 'pkey':
+ return `${dir}/${this.config.protocol}_pkey.zkey`;
+ case 'vkey':
+ return `${dir}/${this.config.protocol}_vkey.json`;
+ case 'sol':
+ return `${dir}/${this.config.protocol}_verifier.sol`;
default:
throw new Error('Invalid type: ' + type);
}
@@ -166,7 +161,7 @@ export class Circomkit {
/** Information about circuit. */
async info(circuit: string): Promise {
- // we do not pass this._logger here in purpose
+ // we do not pass this._logger here on purpose
const r1csinfo = await snarkjs.r1cs.info(this.path(circuit, 'r1cs'), undefined);
return {
variables: r1csinfo.nVars,
@@ -250,7 +245,11 @@ export class Circomkit {
/** Export calldata to console.
* @returns calldata as a string
*/
- async calldata(circuit: string, input: string) {
+ async calldata(circuit: string, input: string): Promise {
+ // fflonk gives error (tested at snarkjs v0.7.0)
+ if (this.config.protocol === 'fflonk') {
+ throw new Error('Exporting calldata is not supported for fflonk yet.');
+ }
const [pubs, proof] = (
await Promise.all(
(['pubs', 'proof'] as const)
@@ -307,8 +306,8 @@ export class Circomkit {
const dir = this.pathWithInput(circuit, input, 'dir');
mkdirSync(dir, {recursive: true});
await Promise.all([
- writeFile(this.pathWithInput(circuit, input, 'pubs'), this.prettyStringify(fullProof.publicSignals)),
- writeFile(this.pathWithInput(circuit, input, 'proof'), this.prettyStringify(fullProof.proof)),
+ writeFile(this.pathWithInput(circuit, input, 'pubs'), prettyStringify(fullProof.publicSignals)),
+ writeFile(this.pathWithInput(circuit, input, 'proof'), prettyStringify(fullProof.proof)),
]);
return dir;
}
@@ -316,7 +315,7 @@ export class Circomkit {
/** Commence a circuit-specific setup.
* @returns path to verifier key and prover key
*/
- async setup(circuit: string, ptauPath?: string): Promise<{proverKey: string; verifierKey: string}> {
+ async setup(circuit: string, ptauPath?: string): Promise<{proverKeyPath: string; verifierKeyPath: string}> {
const r1csPath = this.path(circuit, 'r1cs');
const pkeyPath = this.path(circuit, 'pkey');
const vkeyPath = this.path(circuit, 'vkey');
@@ -377,8 +376,8 @@ export class Circomkit {
// export verification key
const vkey = await snarkjs.zKey.exportVerificationKey(pkeyPath, this._logger);
- writeFileSync(vkeyPath, this.prettyStringify(vkey));
- return {verifierKey: vkeyPath, proverKey: pkeyPath};
+ writeFileSync(vkeyPath, prettyStringify(vkey));
+ return {verifierKeyPath: vkeyPath, proverKeyPath: pkeyPath};
}
/** Verify a proof for some public signals.
@@ -412,20 +411,27 @@ export class Circomkit {
return wtnsPath;
}
+ /** Exports a JSON input file for some circuit with the given object.
+ * This is useful for testing real circuits, or creating an input programmatically.
+ * Overwrites an existing input.
+ * @returns path to created input file
+ */
+ input(circuit: string, input: string, data: CircuitSignals): string {
+ const inputPath = this.pathWithInput(circuit, input, 'in');
+ writeFileSync(inputPath, prettyStringify(data));
+ return inputPath;
+ }
+
/**
* Export a circuit artifact in JSON format. If the last argument `write` is true, it will write to
* file with the appropriate path, and return the path. Otheriwse, returns the JSON obejct.
* @param type type of file to export
* @returns a JSON object or the path that it would be exported to.
*/
- async json(
- type: 'r1cs' | 'zkey' | 'wtns',
- circuit: string,
- input?: string,
- write?: boolean
- ): Promise