mirror of
https://github.com/erhant/circomkit.git
synced 2026-05-05 03:00:37 -04:00
init added
This commit is contained in:
10
.npmignore
Normal file
10
.npmignore
Normal file
@@ -0,0 +1,10 @@
|
||||
ptau
|
||||
node_modules
|
||||
scripts
|
||||
circuits
|
||||
build
|
||||
.github
|
||||
.vscode
|
||||
inputs
|
||||
src
|
||||
tests
|
||||
@@ -1,5 +1,3 @@
|
||||
> Work in progress...
|
||||
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
Circomkit
|
||||
@@ -10,6 +8,9 @@
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT" target="_blank">
|
||||
<img src="https://img.shields.io/badge/license-MIT-yellow.svg">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/circomkit" target="_blank">
|
||||
<img alt="NPM" src="https://img.shields.io/npm/v/circomkit?logo=npm&color=CB3837">
|
||||
</a>
|
||||
<a href="./.github/workflows/styles.yml" target="_blank">
|
||||
<img alt="Workflow: Styles" src="https://github.com/erhant/circomkit/actions/workflows/styles.yml/badge.svg?branch=main">
|
||||
|
||||
19
package.json
19
package.json
@@ -13,7 +13,7 @@
|
||||
"bin": "dist/bin/index.js",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"homepage": "https://github.com/erhant/circomkit",
|
||||
"homepage": "https://github.com/erhant/circomkit#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/erhant/circomkit.git"
|
||||
@@ -66,7 +66,18 @@
|
||||
"ejs": "^3.1.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"chai": "^4.3.7",
|
||||
"snarkjs": "^0.6.0"
|
||||
}
|
||||
"snarkjs": "^0.6.0",
|
||||
"chai": "^4.3.7"
|
||||
},
|
||||
"keywords": [
|
||||
"circom",
|
||||
"zk",
|
||||
"zero knowledge",
|
||||
"snarkjs",
|
||||
"typescript",
|
||||
"cli",
|
||||
"tooling",
|
||||
"blockchain",
|
||||
"ethereum"
|
||||
]
|
||||
}
|
||||
|
||||
130
src/bin/index.ts
130
src/bin/index.ts
@@ -1,44 +1,134 @@
|
||||
#!/usr/bin/env node
|
||||
import {existsSync, mkdirSync, readFileSync, writeFileSync} from 'fs';
|
||||
import {Circomkit} from '../circomkit';
|
||||
import {initFiles} from '../utils/init';
|
||||
|
||||
const CONFIG_PATH = './circomkit.json';
|
||||
const DEFAULT_INPUT = 'default';
|
||||
const DEFAULT_PTAU = './ptau/powersOfTau28_hez_final_12.ptau';
|
||||
const USAGE = `Usage:
|
||||
|
||||
Compile the circuit.
|
||||
compile circuit
|
||||
|
||||
Create main component.
|
||||
instantiate circuit
|
||||
|
||||
Clean build artifacts & main.
|
||||
clean circuit
|
||||
|
||||
Export Solidity verifier.
|
||||
contract circuit
|
||||
|
||||
Generate a proof.
|
||||
prove circuit input
|
||||
|
||||
Verify a proof.
|
||||
verify circuit input
|
||||
|
||||
Generate a witness.
|
||||
witness circuit input
|
||||
|
||||
Circuit-specific setup.
|
||||
setup circuit input
|
||||
`;
|
||||
|
||||
async function cli(): Promise<number> {
|
||||
const circomkit = new Circomkit();
|
||||
switch (process.argv[2] as unknown as keyof Circomkit) {
|
||||
case 'compile':
|
||||
await circomkit.compile(process.argv[3]);
|
||||
// read user configs
|
||||
let config = {};
|
||||
if (existsSync(CONFIG_PATH)) {
|
||||
config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
||||
}
|
||||
const circomkit = new Circomkit(config);
|
||||
|
||||
// execute command
|
||||
switch (process.argv[2] as unknown as keyof Circomkit | 'init') {
|
||||
case 'compile': {
|
||||
circomkit.log('\n=== Compiling the circuit ===', 'title');
|
||||
const path = await circomkit.compile(process.argv[3]);
|
||||
circomkit.log('Built at: ' + path);
|
||||
break;
|
||||
case 'instantiate':
|
||||
circomkit.instantiate(process.argv[3]);
|
||||
}
|
||||
|
||||
case 'instantiate': {
|
||||
circomkit.log('\n=== Creating main component ===', 'title');
|
||||
const path = circomkit.instantiate(process.argv[3]);
|
||||
circomkit.log('Created at: ' + path);
|
||||
break;
|
||||
case 'clean':
|
||||
}
|
||||
|
||||
case 'clean': {
|
||||
circomkit.log('\n=== Cleaning artifacts ===', 'title');
|
||||
await circomkit.clean(process.argv[3]);
|
||||
circomkit.log('Cleaned.');
|
||||
break;
|
||||
// case 'type':
|
||||
}
|
||||
|
||||
// case 'type': // TODO
|
||||
// await circomkit.type(process.argv[3]);
|
||||
// break;
|
||||
case 'contract':
|
||||
await circomkit.contract(process.argv[3]);
|
||||
|
||||
case 'contract': {
|
||||
circomkit.log('=== Generating verifier contract ===', 'title');
|
||||
const path = await circomkit.contract(process.argv[3]);
|
||||
circomkit.log('Created at: ' + path);
|
||||
break;
|
||||
// case 'calldata':
|
||||
}
|
||||
|
||||
// case 'calldata': // // TODO
|
||||
// await circomkit.calldata(process.argv[3]);
|
||||
// break;
|
||||
case 'prove':
|
||||
await circomkit.prove(process.argv[3], process.argv[4] || DEFAULT_INPUT);
|
||||
|
||||
case 'prove': {
|
||||
circomkit.log('=== Generating proof ===', 'title');
|
||||
const path = await circomkit.prove(process.argv[3], process.argv[4] || DEFAULT_INPUT);
|
||||
circomkit.log('Generated under: ' + path);
|
||||
break;
|
||||
case 'verify':
|
||||
await circomkit.verify(process.argv[3], process.argv[4] || DEFAULT_INPUT);
|
||||
}
|
||||
|
||||
case 'verify': {
|
||||
circomkit.log('=== Verifying proof ===', 'title');
|
||||
const result = await circomkit.verify(process.argv[3], process.argv[4] || DEFAULT_INPUT);
|
||||
if (result) {
|
||||
circomkit.log('Verification successful.', 'log');
|
||||
} else {
|
||||
circomkit.log('Verification failed!', 'error');
|
||||
}
|
||||
break;
|
||||
case 'witness':
|
||||
await circomkit.witness(process.argv[3], process.argv[4] || DEFAULT_INPUT);
|
||||
}
|
||||
|
||||
case 'witness': {
|
||||
circomkit.log('=== Calculating witness ===', 'title');
|
||||
const path = await circomkit.witness(process.argv[3], process.argv[4] || DEFAULT_INPUT);
|
||||
circomkit.log('Witness created: ' + path);
|
||||
break;
|
||||
case 'setup':
|
||||
await circomkit.setup(process.argv[3], process.argv[4] || DEFAULT_PTAU);
|
||||
}
|
||||
|
||||
case 'setup': {
|
||||
circomkit.log('=== Circuit-specific setup ===', 'title');
|
||||
const path = await circomkit.setup(process.argv[3], process.argv[4] || DEFAULT_PTAU);
|
||||
circomkit.log('Prover key created: ' + path);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'init': {
|
||||
await Promise.all(
|
||||
[initFiles.circuit, initFiles.circuits, initFiles.input, initFiles.tests].map(item => {
|
||||
const path = `${item.dir}/${item.name}`;
|
||||
if (!existsSync(path)) {
|
||||
mkdirSync(item.dir, {recursive: true});
|
||||
writeFileSync(path, item.content);
|
||||
} else {
|
||||
circomkit.log(path + ' exists, skipping.', 'error');
|
||||
}
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
// setup project structure
|
||||
|
||||
default:
|
||||
console.error('Invalid command.');
|
||||
console.log(USAGE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
113
src/circomkit.ts
113
src/circomkit.ts
@@ -1,11 +1,31 @@
|
||||
const snarkjs = require('snarkjs');
|
||||
const wasm_tester = require('circom_tester').wasm;
|
||||
import {type CircomkitConfig, defaultConfig} from './config';
|
||||
import {writeFileSync, readFileSync, existsSync} from 'fs';
|
||||
import {mkdirSync} from 'fs';
|
||||
import {readFile, rm} from 'fs/promises';
|
||||
import instantiate from './utils/instantiate';
|
||||
import type {CircuitConfig} from './types/circuit';
|
||||
import type {CircomkitConfig, CircuitInputPathBuilders, CircuitPathBuilders} from './types/circomkit';
|
||||
|
||||
/** Default configurations */
|
||||
const defaultConfig: Readonly<CircomkitConfig> = {
|
||||
proofSystem: 'plonk',
|
||||
curve: 'bn128',
|
||||
version: '2.1.0',
|
||||
silent: false,
|
||||
ptauDir: './ptau',
|
||||
compiler: {
|
||||
optimization: 0,
|
||||
verbose: false,
|
||||
json: false,
|
||||
include: ['./node_modules'],
|
||||
},
|
||||
colors: {
|
||||
title: '\x1b[0;34m', // blue
|
||||
log: '\x1b[2;37m', // gray
|
||||
error: '\x1b[0;31m', // red
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Circomkit is an opinionated wrapper around a few
|
||||
@@ -18,20 +38,22 @@ import type {CircuitConfig} from './types/circuit';
|
||||
*/
|
||||
export class Circomkit {
|
||||
readonly config: CircomkitConfig;
|
||||
private readonly resetColor = '\x1b[0m';
|
||||
|
||||
constructor(overrides: Partial<CircomkitConfig> = {}) {
|
||||
// override default options, if any
|
||||
this.config = {
|
||||
const config: CircomkitConfig = {
|
||||
...defaultConfig,
|
||||
...overrides,
|
||||
};
|
||||
|
||||
// sanitize
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/** Colorful logging based on config. */
|
||||
private log(message: string, type: keyof CircomkitConfig['colors'] = 'log') {
|
||||
/** Colorful logging based on config, used by CLI. */
|
||||
log(message: string, type: keyof CircomkitConfig['colors'] = 'log') {
|
||||
if (!this.config.silent) {
|
||||
console.log(`${this.config.colors[type]}${message}${this.resetColor}`);
|
||||
console.log(`${this.config.colors[type]}${message}'\x1b[0m'`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,13 +62,22 @@ export class Circomkit {
|
||||
return JSON.stringify(obj, undefined, 2);
|
||||
}
|
||||
|
||||
/** Parse circuit config from `circuits.json` */
|
||||
private readCircuitConfig(circuit: string): CircuitConfig {
|
||||
const circuits = JSON.parse(readFileSync('./circuits.json', 'utf-8'));
|
||||
if (!(circuit in circuits)) {
|
||||
throw new Error('No such circuit in circuits.json');
|
||||
}
|
||||
return circuits[circuit] as CircuitConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a path that requires a circuit name.
|
||||
* @param circuit circuit name
|
||||
* @param type path type
|
||||
* @returns path
|
||||
*/
|
||||
private path(circuit: string, type: 'target' | 'sym' | 'pkey' | 'vkey' | 'wasm' | 'sol' | 'dir' | 'r1cs'): string {
|
||||
private path(circuit: string, type: CircuitPathBuilders): string {
|
||||
const dir = `./build/${circuit}`;
|
||||
switch (type) {
|
||||
case 'dir':
|
||||
@@ -62,7 +93,7 @@ export class Circomkit {
|
||||
case 'sym':
|
||||
return `${dir}/${circuit}.sym`;
|
||||
case 'sol':
|
||||
return `${dir}/Verifier.sol`;
|
||||
return `${dir}/Verifier_${this.config.proofSystem}.sol`;
|
||||
case 'wasm':
|
||||
return `${dir}/${circuit}_js/${circuit}.wasm`;
|
||||
default:
|
||||
@@ -77,7 +108,7 @@ export class Circomkit {
|
||||
* @param type path type
|
||||
* @returns path
|
||||
*/
|
||||
private path2(circuit: string, input: string, type: 'pubs' | 'proof' | 'wtns' | 'in' | 'dir'): string {
|
||||
private path2(circuit: string, input: string, type: CircuitInputPathBuilders): string {
|
||||
const dir = `./build/${circuit}/${input}`;
|
||||
switch (type) {
|
||||
case 'dir':
|
||||
@@ -97,28 +128,28 @@ export class Circomkit {
|
||||
|
||||
/** Clean build files and the main component. */
|
||||
async clean(circuit: string) {
|
||||
this.log('\n=== Cleaning artifacts ===', 'title');
|
||||
await Promise.all([
|
||||
rm(this.path(circuit, 'dir'), {recursive: true, force: true}),
|
||||
rm(this.path(circuit, 'target'), {force: true}),
|
||||
]);
|
||||
this.log('Cleaned.');
|
||||
}
|
||||
|
||||
/** Compile the circuit.
|
||||
* This function uses [wasm tester](../../node_modules/circom_tester/wasm/tester.js)
|
||||
* in the background.
|
||||
*
|
||||
* @returns path to build files
|
||||
*/
|
||||
async compile(circuit: string) {
|
||||
this.log('\n=== Compiling the circuit ===', 'title');
|
||||
const outDir = this.path(circuit, 'dir');
|
||||
const targetPath = this.path(circuit, 'target');
|
||||
|
||||
if (!existsSync(this.path(circuit, 'target'))) {
|
||||
if (!existsSync(targetPath)) {
|
||||
this.log('Main component does not exist, creating it now.');
|
||||
this.instantiate(circuit);
|
||||
}
|
||||
|
||||
const outDir = this.path(circuit, 'dir');
|
||||
await wasm_tester(this.path(circuit, 'target'), {
|
||||
await wasm_tester(targetPath, {
|
||||
output: outDir,
|
||||
prime: this.config.curve,
|
||||
verbose: this.config.compiler.verbose,
|
||||
@@ -129,22 +160,26 @@ export class Circomkit {
|
||||
sym: true,
|
||||
recompile: true,
|
||||
});
|
||||
this.log('Built at: ' + outDir);
|
||||
|
||||
// TODO: add C output
|
||||
|
||||
return outDir;
|
||||
}
|
||||
|
||||
/** Exports a solidity contract for the verifier. */
|
||||
async contract(circuit: string) {
|
||||
this.log('=== Generating verifier contract ===', 'title');
|
||||
|
||||
const pkey = this.path(circuit, 'pkey');
|
||||
const verifierCode = await snarkjs.zKey.exportSolidityVerifier(pkey, {
|
||||
groth16: readFileSync('./node_modules/snarkjs/templates/verifier_groth16.sol.ejs', 'utf-8'),
|
||||
plonk: readFileSync('./node_modules/snarkjs/templates/verifier_plonk.sol.ejs', 'utf-8'),
|
||||
const template = readFileSync(
|
||||
`./node_modules/snarkjs/templates/verifier_${this.config.proofSystem}.sol.ejs`,
|
||||
'utf-8'
|
||||
);
|
||||
const contractCode = await snarkjs.zKey.exportSolidityVerifier(pkey, {
|
||||
[this.config.proofSystem]: template,
|
||||
});
|
||||
|
||||
const sol = this.path(circuit, 'sol');
|
||||
writeFileSync(sol, verifierCode);
|
||||
this.log('Contract created at: ' + sol);
|
||||
const contractPath = this.path(circuit, 'sol');
|
||||
writeFileSync(contractPath, contractCode);
|
||||
return contractPath;
|
||||
}
|
||||
|
||||
/** Export calldata to console. */
|
||||
@@ -154,23 +189,17 @@ export class Circomkit {
|
||||
|
||||
/** Instantiate the `main` component. */
|
||||
instantiate(circuit: string) {
|
||||
this.log('\n=== Creating main component ===', 'title');
|
||||
const circuits = JSON.parse(readFileSync('./circuits.json', 'utf-8'));
|
||||
if (!(circuit in circuits)) {
|
||||
throw new Error('No such circuit in circuits.json');
|
||||
}
|
||||
const circuitConfig = circuits[circuit] as CircuitConfig;
|
||||
instantiate(circuit, {
|
||||
const circuitConfig = this.readCircuitConfig(circuit);
|
||||
const target = instantiate(circuit, {
|
||||
...circuitConfig,
|
||||
dir: 'main',
|
||||
version: this.config.version,
|
||||
});
|
||||
this.log('Done!');
|
||||
return target;
|
||||
}
|
||||
|
||||
/** Generate a proof. */
|
||||
async prove(circuit: string, input: string) {
|
||||
this.log('=== Generating proof ===', 'title');
|
||||
const jsonInput = JSON.parse(readFileSync(this.path2(circuit, input, 'in'), 'utf-8'));
|
||||
const fullProof = await snarkjs[this.config.proofSystem].fullProve(
|
||||
jsonInput,
|
||||
@@ -182,13 +211,11 @@ export class Circomkit {
|
||||
mkdirSync(dir, {recursive: true});
|
||||
writeFileSync(this.path2(circuit, input, 'pubs'), this.prettyStringify(fullProof.publicSignals));
|
||||
writeFileSync(this.path2(circuit, input, 'proof'), this.prettyStringify(fullProof.proof));
|
||||
this.log('Generated under: ' + dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
/** Commence a circuit-specific setup. */
|
||||
async setup(circuit: string, ptau: string) {
|
||||
this.log('=== Circuit-specific setup ===', 'title');
|
||||
|
||||
const r1csPath = this.path(circuit, 'r1cs');
|
||||
const pkeyPath = this.path(circuit, 'pkey');
|
||||
const vkeyPath = this.path(circuit, 'vkey');
|
||||
@@ -209,7 +236,7 @@ export class Circomkit {
|
||||
// export verification key
|
||||
const vkey = await snarkjs.zKey.exportVerificationKey(pkeyPath);
|
||||
writeFileSync(vkeyPath, this.prettyStringify(vkey));
|
||||
this.log('Prover key created: ' + vkeyPath);
|
||||
return vkeyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,8 +249,6 @@ export class Circomkit {
|
||||
|
||||
/** Verify a proof for some public signals. */
|
||||
async verify(circuit: string, input: string) {
|
||||
this.log('=== Verifying proof ===', 'title');
|
||||
|
||||
const [vkey, pubs, proof] = (
|
||||
await Promise.all(
|
||||
[this.path(circuit, 'vkey'), this.path2(circuit, input, 'pubs'), this.path2(circuit, input, 'proof')].map(
|
||||
@@ -232,17 +257,11 @@ export class Circomkit {
|
||||
)
|
||||
).map(content => JSON.parse(content));
|
||||
|
||||
const result = await snarkjs[this.config.proofSystem].verify(vkey, pubs, proof);
|
||||
if (result) {
|
||||
this.log('Verification successful.', 'log');
|
||||
} else {
|
||||
this.log('Verification failed!', 'error');
|
||||
}
|
||||
return await snarkjs[this.config.proofSystem].verify(vkey, pubs, proof);
|
||||
}
|
||||
|
||||
/** Calculates the witness for the given circuit and input. */
|
||||
async witness(circuit: string, input: string) {
|
||||
this.log('=== Calculating witness ===', 'title');
|
||||
const wasmPath = this.path(circuit, 'wasm');
|
||||
const wtnsPath = this.path2(circuit, input, 'wtns');
|
||||
const outDir = this.path2(circuit, input, 'dir');
|
||||
@@ -250,6 +269,6 @@ export class Circomkit {
|
||||
|
||||
mkdirSync(outDir, {recursive: true});
|
||||
await snarkjs.wtns.calculate(jsonInput, wasmPath, wtnsPath);
|
||||
this.log('Created under: ' + wtnsPath);
|
||||
return wtnsPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import ProofTester from './testers/proofTester';
|
||||
import WasmTester from './testers/wasmTester';
|
||||
import instantiate from './utils/instantiate';
|
||||
import {Circomkit} from './circomkit';
|
||||
export * from './types/circuit';
|
||||
|
||||
export {ProofTester, WasmTester, instantiate};
|
||||
export {ProofTester, WasmTester, instantiate, Circomkit};
|
||||
|
||||
@@ -6,6 +6,8 @@ export type CircomkitConfig = {
|
||||
proofSystem: 'groth16' | 'plonk';
|
||||
/** Curve to be used, which defines the underlying prime field. */
|
||||
curve: 'bn128' | 'bls12381' | 'goldilocks';
|
||||
/** Directory to download PTAU files. */
|
||||
ptauDir: string;
|
||||
/** Version number for main components. */
|
||||
version: VersionType;
|
||||
/** Hide Circomkit logs */
|
||||
@@ -33,20 +35,5 @@ export type CircomkitConfig = {
|
||||
};
|
||||
};
|
||||
|
||||
export const defaultConfig: Readonly<CircomkitConfig> = {
|
||||
proofSystem: 'plonk',
|
||||
curve: 'bn128',
|
||||
version: '2.1.0',
|
||||
silent: false,
|
||||
colors: {
|
||||
title: '\x1b[0;34m', // blue
|
||||
log: '\x1b[2;37m', // gray
|
||||
error: '\x1b[0;31m', // red
|
||||
},
|
||||
compiler: {
|
||||
optimization: 0,
|
||||
verbose: false,
|
||||
json: false,
|
||||
include: ['./node_modules'],
|
||||
},
|
||||
};
|
||||
export type CircuitPathBuilders = 'target' | 'sym' | 'pkey' | 'vkey' | 'wasm' | 'sol' | 'dir' | 'r1cs';
|
||||
export type CircuitInputPathBuilders = 'pubs' | 'proof' | 'wtns' | 'in' | 'dir';
|
||||
83
src/utils/init.ts
Normal file
83
src/utils/init.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Initializes a development environment. This function is
|
||||
* most likely to be called from the CLI.
|
||||
*
|
||||
* It creates the following:
|
||||
*
|
||||
* - `circuits/multiplier.circom`
|
||||
* - `inputs/multiplier/default.json`
|
||||
* - `circuits.json`
|
||||
*/
|
||||
|
||||
import {existsSync, mkdirSync, writeFileSync, writeSync} from 'fs';
|
||||
|
||||
export const initFiles = {
|
||||
circuit: {
|
||||
dir: './circuits',
|
||||
name: 'multiplier.circom',
|
||||
content: `pragma circom 2.0.0;
|
||||
template Multiplier(N) {
|
||||
assert(N > 1);
|
||||
signal input in[N];
|
||||
signal output out;
|
||||
|
||||
signal inner[N-1];
|
||||
inner[0] <== in[0] * in[1];
|
||||
for(var i = 2; i < N; i++) {
|
||||
inner[i-1] <== inner[i-2] * in[i];
|
||||
}
|
||||
out <== inner[N-2];
|
||||
}`,
|
||||
},
|
||||
input: {
|
||||
dir: './inputs/multiplier_3',
|
||||
name: '80.json',
|
||||
content: `{
|
||||
"in": [2, 4, 10]
|
||||
}
|
||||
`,
|
||||
},
|
||||
circuits: {
|
||||
dir: '.',
|
||||
name: 'circuits.json',
|
||||
content: `{
|
||||
"multiplier_3": {
|
||||
"file": "multiplier",
|
||||
"template": "Multiplier",
|
||||
"params": [3]
|
||||
},
|
||||
}
|
||||
`,
|
||||
},
|
||||
tests: {
|
||||
dir: './tests',
|
||||
name: 'multipier.test.ts',
|
||||
content: `import { WasmTester } from "circomkit";
|
||||
|
||||
// exercise: make this test work for all numbers, not just 3
|
||||
describe("multiplier", () => {
|
||||
let circuit: WasmTester<["in"], ["out"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await WasmTester.new('multiplier_3', {
|
||||
file: "multiplier",
|
||||
template: "Multiplier",
|
||||
params: [3],
|
||||
});
|
||||
await circuit.checkConstraintCount(2);
|
||||
});
|
||||
|
||||
it("should multiply correctly", async () => {
|
||||
await circuit.expectCorrectAssert({ in: [3, 8, 20] }, { out: 480 });
|
||||
});
|
||||
});
|
||||
|
||||
`,
|
||||
},
|
||||
} satisfies {
|
||||
[key: string]: {
|
||||
dir: string;
|
||||
name: string;
|
||||
content: string;
|
||||
};
|
||||
};
|
||||
49
src/utils/ptau.ts
Normal file
49
src/utils/ptau.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import {createWriteStream} from 'fs';
|
||||
import {get} from 'https';
|
||||
|
||||
const PTAU_DIR = './ptau';
|
||||
const PTAU_URL_BASE = 'https://hermez.s3-eu-west-1.amazonaws.com';
|
||||
|
||||
/**
|
||||
* Returns the URL of PTAU file for a given power.
|
||||
*
|
||||
* - If power is larger than 27,
|
||||
* @see {@link https://github.com/iden3/snarkjs#7-prepare-phase-2}
|
||||
* @param p a number such that numConstraints <= 2^p
|
||||
* @returns
|
||||
*/
|
||||
function getPtauName(p: number): string {
|
||||
let id = ''; // default for large values
|
||||
if (p < 8) {
|
||||
id = '_08';
|
||||
} else if (p < 10) {
|
||||
id = `_0${p}`;
|
||||
} else if (p < 28) {
|
||||
id = `_${p}`;
|
||||
} else if (p === 28) {
|
||||
id = '';
|
||||
} else {
|
||||
throw new Error('No PTAU for power level ' + p);
|
||||
}
|
||||
return `powersOfTau28_hez_final${id}.ptau`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads phase-1 powers of tau from Polygon Hermez.
|
||||
* @param numConstraints number of constraints in the circuit
|
||||
* @returns path to ptau
|
||||
*/
|
||||
export function downloadPtau(numConstraints: number): Promise<string> {
|
||||
const ptauName = getPtauName(Math.floor(Math.log2(numConstraints)));
|
||||
const ptauPath = `${PTAU_DIR}/${ptauName}`;
|
||||
const file = createWriteStream(ptauPath);
|
||||
return new Promise<string>(resolve => {
|
||||
get(`${PTAU_URL_BASE}/${ptauName}`, response => {
|
||||
response.pipe(file);
|
||||
file.on('finish', () => {
|
||||
file.close();
|
||||
resolve(ptauPath);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user