Skip to content

Commit eb99844

Browse files
committed
added getPlonkConstraintsNumber function
1 parent 970a761 commit eb99844

File tree

6 files changed

+117
-56
lines changed

6 files changed

+117
-56
lines changed

src/constants.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@ export const BYTES_IN_MB = 1048576;
1818
export const MAX_PTAU_ID = 27;
1919
export const PTAU_FILE_REG_EXP = /^(?:.+-|)(\d{1,2}).ptau$/;
2020

21-
export const PROVING_SYSTEM_CONSTRAINTS_MULTIPLIERS: { [key: string]: number } = {
22-
groth16: 1,
23-
plonk: 11,
24-
};
25-
2621
export const CIRCOM_FILE_REG_EXP = /\w+\.circom/;
2722

2823
export const NODE_MODULES_REG_EXP = /^node_modules\//;

src/core/compile/CompilationProcessor.ts

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import path from "path";
22
import os from "os";
3-
import fs from "fs";
43
import semver from "semver";
54
import fsExtra from "fs-extra";
65
import { v4 as uuid } from "uuid";
@@ -11,6 +10,8 @@ import { CircomCompilerFactory, createCircomCompilerFactory, getHighestVersion,
1110
import { HardhatZKitError } from "../../errors";
1211
import { CIRCUIT_ARTIFACT_VERSION, NODE_MODULES } from "../../constants";
1312
import { Reporter } from "../../reporter";
13+
14+
import { getGroth16ConstraintsNumber } from "../../utils/constraints-utils";
1415
import { getNormalizedFullPath, renameFilesRecursively, readDirRecursively } from "../../utils/path-utils";
1516

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

102103
await this._compileCircuits(compiler, compilationInfoArr);
103104

104-
compilationInfoArr.forEach((info: CompilationInfo) => {
105-
info.constraintsNumber = this._getConstraintsNumber(info);
106-
});
105+
for (const info of compilationInfoArr) {
106+
const r1csFilePath = getNormalizedFullPath(info.tempArtifactsPath, `${info.circuitName}.r1cs`);
107+
info.constraintsNumber = await getGroth16ConstraintsNumber(r1csFilePath);
108+
}
107109

108110
await this._moveFromTempDirToArtifacts(compilationInfoArr);
109111

@@ -258,40 +260,6 @@ export class CompilationProcessor {
258260
return fileTypes;
259261
}
260262

261-
private _getConstraintsNumber(compilationInfo: CompilationInfo): number {
262-
const r1csFileName = `${compilationInfo.circuitName}.r1cs`;
263-
const r1csFile = getNormalizedFullPath(compilationInfo.tempArtifactsPath, r1csFileName);
264-
const r1csDescriptor = fs.openSync(r1csFile, "r");
265-
266-
const readBytes = (position: number, length: number): bigint => {
267-
const buffer = Buffer.alloc(length);
268-
269-
fs.readSync(r1csDescriptor, buffer, { length, position });
270-
271-
return BigInt(`0x${buffer.reverse().toString("hex")}`);
272-
};
273-
274-
// https://github.com/iden3/r1csfile/blob/d82959da1f88fbd06db0407051fde94afbf8824a/doc/r1cs_bin_format.md#format-of-the-file
275-
const numberOfSections = readBytes(8, 4);
276-
let sectionStart = 12;
277-
278-
for (let i = 0; i < numberOfSections; ++i) {
279-
const sectionType = Number(readBytes(sectionStart, 4));
280-
const sectionSize = Number(readBytes(sectionStart + 4, 8));
281-
282-
// Reading header section
283-
if (sectionType == 1) {
284-
const totalConstraintsOffset = 4 + 8 + 4 + 32 + 4 + 4 + 4 + 4 + 8;
285-
286-
return Number(readBytes(sectionStart + totalConstraintsOffset, 4));
287-
}
288-
289-
sectionStart += 4 + 8 + sectionSize;
290-
}
291-
292-
throw new HardhatZKitError(`Header section in ${r1csFileName} file is not found.`);
293-
}
294-
295263
private _getLinkLibraries(): string[] {
296264
return [this._nodeModulesPath];
297265
}

src/core/setup/SetupProcessor.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import * as snarkjs from "snarkjs";
88
import { ProvingSystemType } from "@solarity/zkit";
99

1010
import { HardhatZKitError } from "../../errors";
11-
import { PROVING_SYSTEM_CONSTRAINTS_MULTIPLIERS, PTAU_FILE_REG_EXP } from "../../constants";
11+
import { BN128_CURVE_NAME, PTAU_FILE_REG_EXP } from "../../constants";
1212
import { Reporter } from "../../reporter";
1313
import { PtauDownloader } from "../utils/PtauDownloader";
1414
import { terminateCurve } from "../../utils/utils";
1515
import { getNormalizedFullPath } from "../../utils/path-utils";
16+
import { getPlonkConstraintsNumber } from "../../utils/constraints-utils";
1617

1718
import { ICircuitArtifacts } from "../../types/artifacts/circuit-artifacts";
1819
import { CircuitSetupInfo, SetupContributionSettings } from "../../types/core";
@@ -212,24 +213,23 @@ export class SetupProcessor {
212213
circuitSetupInfoArr: CircuitSetupInfo[],
213214
provingSystems: ProvingSystemType[],
214215
): Promise<string> {
216+
const usePlonk = provingSystems.includes("plonk");
217+
218+
const curve = await (snarkjs as any).curves.getCurveFromName(BN128_CURVE_NAME);
219+
215220
const circuitsConstraintsNumber: number[] = await Promise.all(
216221
circuitSetupInfoArr.map(async (setupInfo: CircuitSetupInfo) => {
217-
return setupInfo.circuitArtifact.baseCircuitInfo.constraintsNumber;
222+
return usePlonk
223+
? getPlonkConstraintsNumber(setupInfo.r1csSourcePath, curve.Fr)
224+
: setupInfo.circuitArtifact.baseCircuitInfo.constraintsNumber;
218225
}),
219226
);
220227

221-
const maxConstraintsNumber = Math.max(...circuitsConstraintsNumber);
222-
let provingSystemMultiplier = 1;
223-
224-
for (const provingSystem of provingSystems) {
225-
const currentMultiplier = PROVING_SYSTEM_CONSTRAINTS_MULTIPLIERS[provingSystem];
228+
await curve.terminate();
226229

227-
if (currentMultiplier && currentMultiplier > provingSystemMultiplier) {
228-
provingSystemMultiplier = currentMultiplier;
229-
}
230-
}
230+
const maxConstraintsNumber = Math.max(...circuitsConstraintsNumber);
231231

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

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

src/types/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ export type ExecCallResult = {
66
stdout: string;
77
stderr: string;
88
};
9+
10+
export type LinearCombination = { [key: string]: bigint };
11+
12+
export type R1CSConstraint = [LinearCombination, LinearCombination, LinearCombination];

src/utils/constraints-utils.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import * as snarkjs from "snarkjs";
2+
// @ts-expect-error: No type definitions available for the "r1csfile" package
3+
import * as r1csfile from "r1csfile";
4+
// @ts-expect-error: No type definitions available for the "@iden3/binfileutils" package
5+
import * as binfileutils from "@iden3/binfileutils";
6+
7+
import { LinearCombination, R1CSConstraint } from "../../src/types/utils";
8+
9+
export async function getGroth16ConstraintsNumber(r1csFilePath: string): Promise<number> {
10+
return (await snarkjs.r1cs.info(r1csFilePath)).nConstraints;
11+
}
12+
13+
export async function getPlonkConstraintsNumber(r1csFilePath: string, Fr: any): Promise<number> {
14+
const normalize = (lc: LinearCombination) => {
15+
Object.keys(lc).forEach((key) => {
16+
if (lc[key] == 0n) delete lc[key];
17+
});
18+
};
19+
20+
const join = (lc1: LinearCombination, k: bigint, lc2: LinearCombination) => {
21+
const res = { ...lc1 };
22+
Object.keys(lc2).forEach((s) => {
23+
res[s] = res[s] ? Fr.add(res[s], Fr.mul(k, lc2[s])) : lc2[s];
24+
});
25+
normalize(res);
26+
return res;
27+
};
28+
29+
const reduceCoefs = (linearComb: LinearCombination, maxC: number) => {
30+
let n = Object.keys(linearComb).filter((s) => s !== "0" && linearComb[s] != 0n).length;
31+
32+
while (n > maxC) {
33+
plonkConstraintsCount++;
34+
n--;
35+
}
36+
};
37+
38+
const addConstraintSum = (lc: LinearCombination) => {
39+
reduceCoefs(lc, 3);
40+
plonkConstraintsCount++;
41+
};
42+
43+
const getLinearCombinationType = (lc: LinearCombination) => {
44+
let k = Fr.zero;
45+
let n = 0;
46+
47+
Object.keys(lc).forEach((key) => {
48+
if (lc[key] !== 0n) {
49+
if (key === "0") {
50+
k = Fr.add(k, lc[key]);
51+
} else {
52+
n++;
53+
}
54+
}
55+
});
56+
57+
return n > 0 ? n.toString() : k != Fr.zero ? "k" : "0";
58+
};
59+
60+
const process = (lcA: LinearCombination, lcB: LinearCombination, lcC: LinearCombination) => {
61+
const lctA = getLinearCombinationType(lcA);
62+
const lctB = getLinearCombinationType(lcB);
63+
64+
if (lctA === "0" || lctB === "0") {
65+
normalize(lcC);
66+
addConstraintSum(lcC);
67+
} else if (lctA === "k") {
68+
addConstraintSum(join(lcB, lcA[0], lcC));
69+
} else if (lctB === "k") {
70+
addConstraintSum(join(lcA, lcB[0], lcC));
71+
} else {
72+
[lcA, lcB, lcC].forEach((lc) => reduceCoefs(lc, 1));
73+
plonkConstraintsCount++;
74+
}
75+
};
76+
77+
const { fd: fdR1cs, sections: sectionsR1cs } = await binfileutils.readBinFile(
78+
r1csFilePath,
79+
"r1cs",
80+
1,
81+
1 << 22,
82+
1 << 24,
83+
);
84+
const r1cs = await r1csfile.readR1csFd(fdR1cs, sectionsR1cs, { loadConstraints: true, loadCustomGates: true });
85+
86+
let plonkConstraintsCount = r1cs.nOutputs + r1cs.nPubInputs;
87+
88+
r1cs.constraints.forEach((constraint: R1CSConstraint) => process(...constraint));
89+
90+
await fdR1cs.fd.close();
91+
92+
return plonkConstraintsCount;
93+
}

src/utils/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
export * from "./path-utils";
21
export * from "./utils";
2+
export * from "./path-utils";
3+
export * from "./constraints-utils";

0 commit comments

Comments
 (0)