From d46b5fae908e920c5a46b2669fda8182b27f8aee Mon Sep 17 00:00:00 2001 From: Erhan Tezcan Date: Fri, 31 Mar 2023 00:10:49 +0300 Subject: [PATCH] todo: compile should take dirname --- .vscode/settings.json | 3 +- README.md | 37 +++++++++-------- circuit.config.cjs | 19 ++++++++- .../_template.circom => ejs/template.circom} | 2 +- circuits/fibonacci.circom | 2 +- circuits/main/fibonacci_11.circom | 6 +++ .../{sudoku9.circom => sudoku_9x9.circom} | 0 circuits/multiplier.circom | 2 +- circuits/util/checkBitLength.circom | 6 +++ package.json | 15 +++---- scripts/instantiate.js | 17 ++++++-- tests/fibonacci.test.ts | 40 +++++++++++++++++++ tests/multiplier.test.ts | 3 +- tests/sudoku.test.ts | 2 +- utils/wasmTester.ts | 12 ++++-- 15 files changed, 126 insertions(+), 40 deletions(-) rename circuits/{main/_template.circom => ejs/template.circom} (89%) create mode 100644 circuits/main/fibonacci_11.circom rename circuits/main/{sudoku9.circom => sudoku_9x9.circom} (100%) create mode 100644 circuits/util/checkBitLength.circom create mode 100644 tests/fibonacci.test.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 3be7537..95d849e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,8 @@ "main": "Controller", "inputs": "Json", "ptau": "Secure", - "zkey": "Functions" + "zkey": "Functions", + "ejs": "JavaScript" }, // https://raw.githubusercontent.com/PKief/vscode-material-icon-theme/main/images/fileIcons.png "material-icon-theme.files.associations": { diff --git a/README.md b/README.md index 75fa070..54b11fb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Circom Starter -> A template repository to write arithmetic circuits. +> An opinionated Circom circuit development environment. ## Usage @@ -13,32 +13,32 @@ circom-ts-starter ├── 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 +│ ├── main # auto-generated main components +│ │ │── sudoku_9x9.circom # e.g. a 9x9 sudoku board │ │ └── ... -│ │── foo.circom +│ │── sudoku.circom # a generic sudoku template │ └── ... ├── inputs # where you write JSON inputs per circuit -│ ├── foo -│ │ ├── input-name.json +│ ├── sudoku_9x9 # each main template has its own folder +│ │ ├── input-1.json # e.g. a solution & its puzzle │ │ └── ... │ └── ... ├── ptau # universal phase-1 setups │ ├── powersOfTau28_hez_final_12.ptau │ └── ... -└── build # artifacts, .gitignore'd - │── foo-main - │ │── foo-main_js # artifacts of compilation +└── build # build artifacts, these are .gitignore'd + │── sudoku_9x9 # each main template has its own folder + │ │── sudoku_9x9_js # artifacts of compilation │ │ │── generate_witness.js │ │ │── witness_calculator.js - │ │ └── foo-main.wasm + │ │ └── sudoku_9x9.wasm │ │── input-name # artifacts of witness & proof generation │ │ │── proof.json # proof object │ │ │── public.json # public signals │ │ └── witness.wtns │ │── ... # folders for other inputs - │ │── foo-main.r1cs - │ │── foo-main.sym + │ │── sudoku_9x9.r1cs + │ │── sudoku_9x9.sym │ │── prover_key.zkey │ └── verification_key.json └── ... @@ -58,11 +58,14 @@ multiplier3: { 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 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 +# first argument is ALWAYS the circuit name +yarn compile circuit-name +yarn clean circuit-name +yarn ptau circuit-name -n num-contribs -p phase1-ptau-path +yarn prove circuit-name -i input-name +yarn verify circuit-name -i input-name +yarn test circuit-name +yarn test:all ``` There are some environment variables that the CLI can make use of, they are written under [.cli.env](./.cli.env) file. diff --git a/circuit.config.cjs b/circuit.config.cjs index 1a10a4a..41404d6 100644 --- a/circuit.config.cjs +++ b/circuit.config.cjs @@ -7,11 +7,11 @@ module.exports = { templateInputs: [3], }, // A 9x9 sudoku board - sudoku9: { + sudoku_9x9: { file: 'sudoku', template: 'Sudoku', publicInputs: ['puzzle'], - templateInputs: [3], + templateInputs: [Math.sqrt(9)], }, // 64-bit floating point, 11-bit exponent and 52-bit mantissa fp64: { @@ -27,4 +27,19 @@ module.exports = { publicInputs: [], templateInputs: [8, 23], }, + // 11-th Fibonacci number + fibonacci_11: { + file: 'fibonacci', + template: 'Fibonacci', + publicInputs: [], + templateInputs: [11], + }, + // checks that a number fits to given bit count + checkBitLength: { + file: 'float_add', + template: 'CheckBitLength', + publicInputs: [], + templateInputs: [3], + dir: 'util', + }, }; diff --git a/circuits/main/_template.circom b/circuits/ejs/template.circom similarity index 89% rename from circuits/main/_template.circom rename to circuits/ejs/template.circom index 29b4897..a136764 100644 --- a/circuits/main/_template.circom +++ b/circuits/ejs/template.circom @@ -3,7 +3,7 @@ // auto-generated by instantiate.js pragma circom 2.0.0; -include "../../circuits/<%= file %>.circom"; +include "../<%= file %>.circom"; component main<%= publicInputs.length == 0 ? diff --git a/circuits/fibonacci.circom b/circuits/fibonacci.circom index c89cac8..2b2f6e4 100644 --- a/circuits/fibonacci.circom +++ b/circuits/fibonacci.circom @@ -7,7 +7,7 @@ template Fibonacci(n) { signal output out; // compute the sequence - signal f[n+1]; + signal fib[n+1]; fib[0] <== in[0]; fib[1] <== in[1]; for (var i = 2; i <= n; i++) { diff --git a/circuits/main/fibonacci_11.circom b/circuits/main/fibonacci_11.circom new file mode 100644 index 0000000..5ae5949 --- /dev/null +++ b/circuits/main/fibonacci_11.circom @@ -0,0 +1,6 @@ +// auto-generated by instantiate.js +pragma circom 2.0.0; + +include "../../circuits/fibonacci.circom"; + +component main = Fibonacci(11); diff --git a/circuits/main/sudoku9.circom b/circuits/main/sudoku_9x9.circom similarity index 100% rename from circuits/main/sudoku9.circom rename to circuits/main/sudoku_9x9.circom diff --git a/circuits/multiplier.circom b/circuits/multiplier.circom index fec5b77..d2f0f8a 100644 --- a/circuits/multiplier.circom +++ b/circuits/multiplier.circom @@ -10,7 +10,7 @@ template Multiplier2() { } // Multiplication of N numbers -template Multiplier(N){ +template Multiplier(N) { signal input in[N]; signal output out; component comp[N-1]; diff --git a/circuits/util/checkBitLength.circom b/circuits/util/checkBitLength.circom new file mode 100644 index 0000000..5743c1a --- /dev/null +++ b/circuits/util/checkBitLength.circom @@ -0,0 +1,6 @@ +// auto-generated by instantiate.js +pragma circom 2.0.0; + +include "../float_add.circom"; + +component main = CheckBitLength(3); diff --git a/package.json b/package.json index 0326598..b8a9ba6 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,14 @@ "description": "A Circom development environment", "license": "MIT", "scripts": { - "test": "npx mocha", - "type": "./scripts/cli.sh -f type", - "compile": "./scripts/cli.sh -f compile", - "clean": "./scripts/cli.sh -f clean", - "ptau": "./scripts/cli.sh -f ptau", - "prove": "./scripts/cli.sh -f prove", - "verify": "./scripts/cli.sh -f verify", + "test:all": "npx mocha", + "test": "npx mocha --grep", + "type": "./scripts/cli.sh -f type -c", + "compile": "./scripts/cli.sh -f compile -c", + "clean": "./scripts/cli.sh -f clean -c", + "ptau": "./scripts/cli.sh -f ptau -c", + "prove": "./scripts/cli.sh -f prove -c", + "verify": "./scripts/cli.sh -f verify -c", "_help": "./scripts/cli.sh -f help" }, "author": "erhant", diff --git a/scripts/instantiate.js b/scripts/instantiate.js index 0026ad1..cce681e 100644 --- a/scripts/instantiate.js +++ b/scripts/instantiate.js @@ -1,5 +1,5 @@ const ejs = require('ejs'); -const {writeFileSync, readFileSync} = require('fs'); +const {writeFileSync, readFileSync, existsSync, mkdirSync} = require('fs'); const config = require('../circuit.config.cjs'); // read circuit from config @@ -9,9 +9,20 @@ if (!(target in config)) { } // generate the main component code -let circuit = ejs.render(readFileSync('./circuits/main/_template.circom').toString(), config[target]); +const templatePath = './circuits/ejs/template.circom'; +let circuit = ejs.render(readFileSync(templatePath).toString(), config[target]); // output to file -const targetPath = `./circuits/main/${target}.circom`; +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`; writeFileSync(targetPath, circuit); console.log(`Main component created at: ${targetPath}\n`); diff --git a/tests/fibonacci.test.ts b/tests/fibonacci.test.ts new file mode 100644 index 0000000..cdfc555 --- /dev/null +++ b/tests/fibonacci.test.ts @@ -0,0 +1,40 @@ +import {createWasmTester} from '../utils/wasmTester'; +import type {CircuitSignals} from '../types/circuit'; + +function fibonacci(init: [number, number], n: number): number { + let [a, b] = init; + for (let i = 2; i <= n; i++) { + b = a + b; + a = b - a; + } + return b; +} + +const CIRCUIT_NAME = 'fibonacci_11'; +describe(CIRCUIT_NAME, () => { + const INPUT: CircuitSignals = { + in: [1, 1], + }; + + describe('witness computation', () => { + let circuit: Awaited>; + + before(async () => { + circuit = await createWasmTester(CIRCUIT_NAME); + }); + + 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 correct output + const output = { + out: fibonacci(INPUT.in, 11), + }; + await circuit.assertOut(witness, output); + }); + }); +}); diff --git a/tests/multiplier.test.ts b/tests/multiplier.test.ts index cfd2ca8..d519ae2 100644 --- a/tests/multiplier.test.ts +++ b/tests/multiplier.test.ts @@ -13,7 +13,7 @@ describe(CIRCUIT_NAME, () => { let circuit: Awaited>; before(async () => { - circuit = await createWasmTester('./circuits/main/' + CIRCUIT_NAME + '.circom', true); + circuit = await createWasmTester(CIRCUIT_NAME); }); it('should compute correctly', async () => { @@ -55,7 +55,6 @@ describe(CIRCUIT_NAME, () => { describe('proof verification', () => { let fullProof: FullProof; - const circuit = new ProofTester(CIRCUIT_NAME); before(async () => { diff --git a/tests/sudoku.test.ts b/tests/sudoku.test.ts index 2b393c4..7ad1439 100644 --- a/tests/sudoku.test.ts +++ b/tests/sudoku.test.ts @@ -12,7 +12,7 @@ describe(CIRCUIT_NAME, () => { let circuit: Awaited>; before(async () => { - circuit = await createWasmTester('./circuits/main/' + CIRCUIT_NAME + '.circom', true); + circuit = await createWasmTester(CIRCUIT_NAME); }); it('should compute correctly', async () => { diff --git a/utils/wasmTester.ts b/utils/wasmTester.ts index 7f6afd1..6d839df 100644 --- a/utils/wasmTester.ts +++ b/utils/wasmTester.ts @@ -38,8 +38,7 @@ type WasmTester = { loadConstraints(): Promise; /** - * List of constraints, must call `loadConstraints` before - * accessing this key + * List of constraints, must call `loadConstraints` before accessing this key */ constraints: any[] | undefined; @@ -68,11 +67,16 @@ type WasmTester = { /** * Compiles and reutrns a circuit via `circom_tester`'s `wasm_tester`. * @param circuit name of circuit + * @param dir directory to read the circuit from, defaults to `main` * @param showNumConstraints print number of constraints, defualts to `false` * @returns a `wasm_tester` object */ -export async function createWasmTester(path: string, showNumConstraints: boolean = false): Promise { - const circuit = await wasm_tester(path, { +export async function createWasmTester( + circuitName: string, + dir: string = 'main', + showNumConstraints: boolean = false +): Promise { + const circuit = await wasm_tester(`./circuits/${dir}/${circuitName}.circom`, { include: 'node_modules', // will link circomlib circuits });