Skip to content

Commit

Permalink
refactor(setup): remove initPhase2 and add extract vKey method (#51)
Browse files Browse the repository at this point in the history
* refactor(setup): remove initPhase2 and add extract vKey method

* do not remove the downloaded ptau

---------

Co-authored-by: erhant <[email protected]>
  • Loading branch information
ctrlc03 and erhant authored Jan 24, 2024
1 parent d1834f1 commit 5d82bfc
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 11 deletions.
7 changes: 7 additions & 0 deletions src/bin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ async function cli(): Promise<number> {
break;
}

case 'vkey': {
titleLog('Extracting verification key');
const path = await circomkit.vkey(process.argv[3], process.argv[4]);
circomkit.log('Created at: ' + path, 'success');
break;
}

case 'prove': {
titleLog('Generating proof');
const path = await circomkit.prove(process.argv[3], process.argv[4] || DEFAULT_INPUT);
Expand Down
41 changes: 33 additions & 8 deletions src/circomkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,26 @@ export class Circomkit {
]);
}

/** Export a verification key (vKey) from a zKey */
async vkey(circuit: string, pkeyPath?: string): Promise<string> {
const vkeyPath = this.path(circuit, 'vkey');

// check if it exists
if (pkeyPath === undefined) {
pkeyPath = this.path(circuit, 'pkey');
}

if (!existsSync(pkeyPath)) {
throw new Error('There must be a prover key for this circuit to extract a verification key.');
}

// extract it
const vkey = await snarkjs.zKey.exportVerificationKey(pkeyPath, this.snarkjsLogger);
writeFileSync(vkeyPath, prettyStringify(vkey));

return vkeyPath;
}

/** Information about circuit. */
async info(circuit: string): Promise<R1CSInfoType> {
// we do not pass `this.snarkjsLogger` here on purpose
Expand Down Expand Up @@ -182,6 +202,8 @@ export class Circomkit {
if (this.config.prime !== 'bn128') {
throw new Error('Auto-downloading PTAU only allowed for bn128 at the moment.');
}

// @todo check for performance gains when larger PTAUs are found instead of the target PTAU
const {constraints} = await this.info(circuit);
const ptauName = getPtauName(constraints);

Expand Down Expand Up @@ -400,26 +422,29 @@ export class Circomkit {
}

// get ptau path
this.log('Checking for PTAU file...', 'debug');

if (ptauPath === undefined) {
if (this.config.prime !== 'bn128') {
throw new Error('Please provide PTAU file when using a prime field other than bn128');
}
this.log('Checking for PTAU file...', 'debug');
ptauPath = await this.ptau(circuit);
} else {
// if the provided path does not exist, we can just download it
if (!existsSync(ptauPath)) {
// we can download it
ptauPath = await this.ptau(circuit);
}
}

// circuit specific setup
this.log('Beginning setup phase!', 'info');
if (this.config.protocol === 'groth16') {
// Groth16 needs a specific setup with its own PTAU ceremony
// initialize phase 2
const ptau2Init = this.pathPtau(`${circuit}_init.zkey`);
await snarkjs.powersOfTau.preparePhase2(ptauPath, ptau2Init, this.snarkjsLogger);
// Groth16 needs a circuit specific setup

// start PTAU generation
// generate genesis zKey
let curZkey = this.pathZkey(circuit, 0);
await snarkjs.zKey.newZKey(r1csPath, ptau2Init, curZkey, this.snarkjsLogger);
rmSync(ptau2Init);
await snarkjs.zKey.newZKey(r1csPath, ptauPath, curZkey, this.snarkjsLogger);

// make contributions
for (let contrib = 1; contrib <= this.config.groth16numContributions; contrib++) {
Expand Down
3 changes: 3 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export const usageString = `Usage:
Clean build artifacts & main component.
> clean circuit
Export Verification Key (vKey.json).
> vkey circuit zKeyPath
Export Solidity verifier.
> contract circuit
Expand Down
2 changes: 1 addition & 1 deletion src/utils/ptau.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {createWriteStream} from 'fs';
import {get} from 'https';

/** Base PTAU URL as seen in [SnarkJS docs](https://github.com/iden3/snarkjs#7-prepare-phase-2). */
const PTAU_URL_BASE = 'https://hermez.s3-eu-west-1.amazonaws.com';
const PTAU_URL_BASE = 'https://storage.googleapis.com/zkevm/ptau';

/**
* Returns the name of PTAU file for a given number of constraints.
Expand Down
39 changes: 39 additions & 0 deletions tests/circomkit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,24 @@ forEach(PROTOCOLS).describe('protocol: %s', (protocol: (typeof PROTOCOLS)[number
await circomkit.compile(CIRCUIT_NAME);
});

it('should compile the circuits and not generate the c witness calculator files', async () => {
const outPath = await circomkit.compile(CIRCUIT_NAME);
expect(existsSync(`${outPath}/${CIRCUIT_NAME}_cpp`)).to.be.false;
});

it('should compile circuit and generate the c witness calculator files', async () => {
const circomKitCWitness = new Circomkit({
protocol,
verbose: false,
logLevel: 'silent',
cWitness: true,
});
const outPath = await circomKitCWitness.compile(CIRCUIT_NAME);
expect(existsSync(`${outPath}/${CIRCUIT_NAME}_cpp`)).to.be.true;
// remove it to prevent the next proof system run to fail
rmSync(`${outPath}/${CIRCUIT_NAME}_cpp`, {recursive: true});
});

it('should export circuit information', async () => {
await circomkit.info(CIRCUIT_NAME);
});
Expand All @@ -37,6 +55,27 @@ forEach(PROTOCOLS).describe('protocol: %s', (protocol: (typeof PROTOCOLS)[number
expect(existsSync(verifierKeyPath)).to.be.true;
});

it('should export a verification key given a circuit name and a prover key path', async () => {
const {proverKeyPath} = await circomkit.setup(CIRCUIT_NAME, PTAU_PATH);
const vkeyPath = await circomkit.vkey(CIRCUIT_NAME, proverKeyPath);
expect(existsSync(vkeyPath)).to.be.true;
});

it('should export a verification key given a circuit name', async () => {
const vkeyPath = await circomkit.vkey(CIRCUIT_NAME);
expect(existsSync(vkeyPath)).to.be.true;
});

it('should throw when exporting a verification key given an invalid prover key path', async () => {
try {
await circomkit.vkey(CIRCUIT_NAME, 'non-existent-path');
} catch (err) {
expect((err as Error).message).to.eq(
'There must be a prover key for this circuit to extract a verification key.'
);
}
});

it('should create an input', async () => {
const path = circomkit.input(CIRCUIT_NAME, INPUT_NAME, INPUT);
expect(existsSync(path)).to.be.true;
Expand Down
4 changes: 2 additions & 2 deletions tests/testers.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Circomkit, ProofTester, WitnessTester} from '../src';
import {BAD_INPUT, CIRCUIT_CONFIG, CIRCUIT_NAME, INPUT, N, OUTPUT} from './common';
import {BAD_INPUT, CIRCUIT_CONFIG, CIRCUIT_NAME, INPUT, N, OUTPUT, PTAU_PATH} from './common';
import {expect} from 'chai';

describe('witness tester', () => {
Expand Down Expand Up @@ -69,7 +69,7 @@ describe('proof tester', () => {
protocol: 'plonk',
});
circomkit.instantiate(CIRCUIT_NAME, CIRCUIT_CONFIG);
await circomkit.setup(CIRCUIT_NAME, './ptau/powersOfTau28_hez_final_08.ptau');
await circomkit.setup(CIRCUIT_NAME, PTAU_PATH);
circuit = await circomkit.ProofTester(CIRCUIT_NAME);
});

Expand Down

0 comments on commit 5d82bfc

Please sign in to comment.