Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/plonk #47

Merged
merged 17 commits into from
Nov 7, 2024
Merged
5 changes: 0 additions & 5 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ export const BYTES_IN_MB = 1048576;
export const MAX_PTAU_ID = 27;
export const PTAU_FILE_REG_EXP = /^(?:.+-|)(\d{1,2}).ptau$/;

export const PROVING_SYSTEM_CONSTRAINTS_MULTIPLIERS: { [key: string]: number } = {
groth16: 1,
plonk: 11,
};

export const CIRCOM_FILE_REG_EXP = /\w+\.circom/;

export const NODE_MODULES_REG_EXP = /^node_modules\//;
Expand Down
44 changes: 6 additions & 38 deletions src/core/compile/CompilationProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import path from "path";
import os from "os";
import fs from "fs";
import semver from "semver";
import fsExtra from "fs-extra";
import { v4 as uuid } from "uuid";
Expand All @@ -11,6 +10,8 @@ import { CircomCompilerFactory, createCircomCompilerFactory, getHighestVersion,
import { HardhatZKitError } from "../../errors";
import { CIRCUIT_ARTIFACT_VERSION, NODE_MODULES } from "../../constants";
import { Reporter } from "../../reporter";

import { getGroth16ConstraintsNumber } from "../../utils/constraints-utils";
import { getNormalizedFullPath, renameFilesRecursively, readDirRecursively } from "../../utils/path-utils";
mllwchrry marked this conversation as resolved.
Show resolved Hide resolved

import { ZKitConfig } from "../../types/zkit-config";
Expand Down Expand Up @@ -101,9 +102,10 @@ export class CompilationProcessor {

await this._compileCircuits(compiler, compilationInfoArr);

compilationInfoArr.forEach((info: CompilationInfo) => {
info.constraintsNumber = this._getConstraintsNumber(info);
});
for (const info of compilationInfoArr) {
const r1csFilePath = getNormalizedFullPath(info.tempArtifactsPath, `${info.circuitName}.r1cs`);
info.constraintsNumber = await getGroth16ConstraintsNumber(r1csFilePath);
}

await this._moveFromTempDirToArtifacts(compilationInfoArr);

Expand Down Expand Up @@ -258,40 +260,6 @@ export class CompilationProcessor {
return fileTypes;
}

private _getConstraintsNumber(compilationInfo: CompilationInfo): number {
const r1csFileName = `${compilationInfo.circuitName}.r1cs`;
const r1csFile = getNormalizedFullPath(compilationInfo.tempArtifactsPath, r1csFileName);
const r1csDescriptor = fs.openSync(r1csFile, "r");

const readBytes = (position: number, length: number): bigint => {
const buffer = Buffer.alloc(length);

fs.readSync(r1csDescriptor, buffer, { length, position });

return BigInt(`0x${buffer.reverse().toString("hex")}`);
};

// https://github.com/iden3/r1csfile/blob/d82959da1f88fbd06db0407051fde94afbf8824a/doc/r1cs_bin_format.md#format-of-the-file
const numberOfSections = readBytes(8, 4);
let sectionStart = 12;

for (let i = 0; i < numberOfSections; ++i) {
const sectionType = Number(readBytes(sectionStart, 4));
const sectionSize = Number(readBytes(sectionStart + 4, 8));

// Reading header section
if (sectionType == 1) {
const totalConstraintsOffset = 4 + 8 + 4 + 32 + 4 + 4 + 4 + 4 + 8;

return Number(readBytes(sectionStart + totalConstraintsOffset, 4));
}

sectionStart += 4 + 8 + sectionSize;
}

throw new HardhatZKitError(`Header section in ${r1csFileName} file is not found.`);
}

private _getLinkLibraries(): string[] {
return [this._nodeModulesPath];
}
Expand Down
24 changes: 12 additions & 12 deletions src/core/setup/SetupProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import * as snarkjs from "snarkjs";
import { ProvingSystemType } from "@solarity/zkit";

import { HardhatZKitError } from "../../errors";
import { PROVING_SYSTEM_CONSTRAINTS_MULTIPLIERS, PTAU_FILE_REG_EXP } from "../../constants";
import { BN128_CURVE_NAME, PTAU_FILE_REG_EXP } from "../../constants";
import { Reporter } from "../../reporter";
import { PtauDownloader } from "../utils/PtauDownloader";
import { terminateCurve } from "../../utils/utils";
import { getNormalizedFullPath } from "../../utils/path-utils";
import { getPlonkConstraintsNumber } from "../../utils/constraints-utils";

import { ICircuitArtifacts } from "../../types/artifacts/circuit-artifacts";
import { CircuitSetupInfo, SetupContributionSettings } from "../../types/core";
Expand Down Expand Up @@ -212,24 +213,23 @@ export class SetupProcessor {
circuitSetupInfoArr: CircuitSetupInfo[],
provingSystems: ProvingSystemType[],
): Promise<string> {
const usePlonk = provingSystems.includes("plonk");

const curve = await (snarkjs as any).curves.getCurveFromName(BN128_CURVE_NAME);

const circuitsConstraintsNumber: number[] = await Promise.all(
circuitSetupInfoArr.map(async (setupInfo: CircuitSetupInfo) => {
return setupInfo.circuitArtifact.baseCircuitInfo.constraintsNumber;
return usePlonk
? getPlonkConstraintsNumber(setupInfo.r1csSourcePath, curve.Fr)
: setupInfo.circuitArtifact.baseCircuitInfo.constraintsNumber;
}),
);

const maxConstraintsNumber = Math.max(...circuitsConstraintsNumber);
let provingSystemMultiplier = 1;

for (const provingSystem of provingSystems) {
const currentMultiplier = PROVING_SYSTEM_CONSTRAINTS_MULTIPLIERS[provingSystem];
await curve.terminate();

if (currentMultiplier && currentMultiplier > provingSystemMultiplier) {
provingSystemMultiplier = currentMultiplier;
}
}
const maxConstraintsNumber = Math.max(...circuitsConstraintsNumber);

const ptauId = Math.max(Math.ceil(Math.log2(maxConstraintsNumber * provingSystemMultiplier)), 8);
const ptauId = Math.max(Math.ceil(Math.log2(maxConstraintsNumber)), 8);

let entries: fsExtra.Dirent[] = [];

Expand Down
4 changes: 4 additions & 0 deletions src/types/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ export type ExecCallResult = {
stdout: string;
stderr: string;
};

export type LinearCombination = { [key: string]: bigint };

export type R1CSConstraint = [LinearCombination, LinearCombination, LinearCombination];
93 changes: 93 additions & 0 deletions src/utils/constraints-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as snarkjs from "snarkjs";
// @ts-expect-error: No type definitions available for the "r1csfile" package
import * as r1csfile from "r1csfile";
// @ts-expect-error: No type definitions available for the "@iden3/binfileutils" package
import * as binfileutils from "@iden3/binfileutils";
mllwchrry marked this conversation as resolved.
Show resolved Hide resolved

import { LinearCombination, R1CSConstraint } from "../../src/types/utils";

export async function getGroth16ConstraintsNumber(r1csFilePath: string): Promise<number> {
mllwchrry marked this conversation as resolved.
Show resolved Hide resolved
return (await snarkjs.r1cs.info(r1csFilePath)).nConstraints;
}

export async function getPlonkConstraintsNumber(r1csFilePath: string, Fr: any): Promise<number> {
const normalize = (lc: LinearCombination) => {
Object.keys(lc).forEach((key) => {
if (lc[key] == 0n) delete lc[key];
});
};

const join = (lc1: LinearCombination, k: bigint, lc2: LinearCombination) => {
const res = { ...lc1 };
Object.keys(lc2).forEach((s) => {
res[s] = res[s] ? Fr.add(res[s], Fr.mul(k, lc2[s])) : lc2[s];
});
normalize(res);
return res;
mllwchrry marked this conversation as resolved.
Show resolved Hide resolved
};

const reduceCoefs = (linearComb: LinearCombination, maxC: number) => {
let n = Object.keys(linearComb).filter((s) => s !== "0" && linearComb[s] != 0n).length;

while (n > maxC) {
plonkConstraintsCount++;
n--;
}
};

const addConstraintSum = (lc: LinearCombination) => {
reduceCoefs(lc, 3);
plonkConstraintsCount++;
};

const getLinearCombinationType = (lc: LinearCombination) => {
let k = Fr.zero;
let n = 0;

Object.keys(lc).forEach((key) => {
if (lc[key] !== 0n) {
if (key === "0") {
k = Fr.add(k, lc[key]);
} else {
n++;
}
}
mllwchrry marked this conversation as resolved.
Show resolved Hide resolved
});

return n > 0 ? n.toString() : k != Fr.zero ? "k" : "0";
};

const process = (lcA: LinearCombination, lcB: LinearCombination, lcC: LinearCombination) => {
const lctA = getLinearCombinationType(lcA);
const lctB = getLinearCombinationType(lcB);

if (lctA === "0" || lctB === "0") {
normalize(lcC);
addConstraintSum(lcC);
} else if (lctA === "k") {
addConstraintSum(join(lcB, lcA[0], lcC));
} else if (lctB === "k") {
addConstraintSum(join(lcA, lcB[0], lcC));
} else {
[lcA, lcB, lcC].forEach((lc) => reduceCoefs(lc, 1));
plonkConstraintsCount++;
}
};

const { fd: fdR1cs, sections: sectionsR1cs } = await binfileutils.readBinFile(
r1csFilePath,
"r1cs",
1,
1 << 22,
1 << 24,
);
const r1cs = await r1csfile.readR1csFd(fdR1cs, sectionsR1cs, { loadConstraints: true, loadCustomGates: true });

let plonkConstraintsCount = r1cs.nOutputs + r1cs.nPubInputs;

r1cs.constraints.forEach((constraint: R1CSConstraint) => process(...constraint));

await fdR1cs.fd.close();

return plonkConstraintsCount;
}
3 changes: 2 additions & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./path-utils";
export * from "./utils";
export * from "./path-utils";
export * from "./constraints-utils";
Loading