Plonk working

This commit is contained in:
Jordi Baylina 2021-05-18 22:26:22 +02:00
parent 2ce529beba
commit 7ed350002d
No known key found for this signature in database
GPG Key ID: 7480C80C1BE43112
33 changed files with 2090 additions and 25 deletions

70
.vscode/launch.json vendored Normal file

@ -0,0 +1,70 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "plonk setup",
"skipFiles": [
"<node_internals>/**"
],
"program": "cli.js",
"args": [
"pks",
"test/plonk_circuit/circuit.r1cs",
"test/plonk_circuit/powersOfTau15_final.ptau",
"test/plonk_circuit/circuit.zkey"
]
},
{
"type": "pwa-node",
"request": "launch",
"name": "plonk prove",
"skipFiles": [
"<node_internals>/**"
],
"program": "cli.js",
"args": [
"pkp",
"test/plonk_circuit/circuit.zkey",
"test/plonk_circuit/witness.wtns",
"test/plonk_circuit/proof.json",
"test/plonk_circuit/public.json",
"-v"
]
},
{
"type": "pwa-node",
"request": "launch",
"name": "plonk export vk",
"skipFiles": [
"<node_internals>/**"
],
"program": "cli.js",
"args": [
"zkev",
"test/plonk_circuit/circuit.zkey",
"test/plonk_circuit/verification_key.json",
]
},
{
"type": "pwa-node",
"request": "launch",
"name": "plonk verify",
"skipFiles": [
"<node_internals>/**"
],
"program": "cli.js",
"args": [
"pkv",
"test/plonk_circuit/verification_key.json",
"test/plonk_circuit/public.json",
"test/plonk_circuit/proof.json",
"-v"
]
}
]
}

123
cli.js

@ -35,6 +35,7 @@ const {stringifyBigInts, unstringifyBigInts} = utils;
import * as zkey from "./src/zkey.js";
import * as groth16 from "./src/groth16.js";
import * as plonk from "./src/plonk.js";
import * as wtns from "./src/wtns.js";
import * as curves from "./src/curves.js";
import path from "path";
@ -267,7 +268,34 @@ const commands = [
alias: ["g16v", "verify -vk|verificationkey -pub|public -p|proof"],
action: groth16Verify
},
{
cmd: "plonk setup [circuit.r1cs] [powersoftau.ptau] [circuit.zkey]",
description: "Creates an initial PLONK pkey ",
alias: ["pks"],
options: "-verbose|v",
action: plonkSetup
},
{
cmd: "plonk prove [circuit.zkey] [witness.wtns] [proof.json] [public.json]",
description: "Generates a PLONK Proof from witness",
alias: ["pkp"],
options: "-verbose|v -protocol",
action: plonkProve
},
{
cmd: "plonk fullprove [input.json] [circuit.wasm] [circuit.zkey] [proof.json] [public.json]",
description: "Generates a PLONK Proof from input",
alias: ["pkf"],
options: "-verbose|v -protocol",
action: plonkFullProve
},
{
cmd: "plonk verify [verification_key.json] [public.json] [proof.json]",
description: "Verify a PLONK Proof",
alias: ["pkv"],
options: "-verbose|v",
action: plonkVerify
}
];
@ -986,3 +1014,96 @@ async function zkeyBellmanContribute(params, options) {
return zkey.bellmanContribute(curve, challengeName, responseName, options.entropy, logger);
}
// plonk setup <circuit.r1cs> <powersoftau.ptau> <circuit.zkey>
async function plonkSetup(params, options) {
let r1csName;
let ptauName;
let zkeyName;
if (params.length < 1) {
r1csName = "circuit.r1cs";
} else {
r1csName = params[0];
}
if (params.length < 2) {
ptauName = "powersoftau.ptau";
} else {
ptauName = params[1];
}
if (params.length < 3) {
zkeyName = "circuit.zkey";
} else {
zkeyName = params[2];
}
if (options.verbose) Logger.setLogLevel("DEBUG");
return plonk.setup(r1csName, ptauName, zkeyName, logger);
}
// plonk prove [circuit.zkey] [witness.wtns] [proof.json] [public.json]
async function plonkProve(params, options) {
const zkeyName = params[0] || "circuit.zkey";
const witnessName = params[1] || "witness.wtns";
const proofName = params[2] || "proof.json";
const publicName = params[3] || "public.json";
if (options.verbose) Logger.setLogLevel("DEBUG");
const {proof, publicSignals} = await plonk.prove(zkeyName, witnessName, logger);
await fs.promises.writeFile(proofName, JSON.stringify(stringifyBigInts(proof), null, 1), "utf-8");
await fs.promises.writeFile(publicName, JSON.stringify(stringifyBigInts(publicSignals), null, 1), "utf-8");
return 0;
}
// plonk fullprove [input.json] [circuit.wasm] [circuit.zkey] [proof.json] [public.json]
async function plonkFullProve(params, options) {
const inputName = params[0] || "input.json";
const wasmName = params[1] || "circuit.wasm";
const zkeyName = params[2] || "circuit.zkey";
const proofName = params[3] || "proof.json";
const publicName = params[4] || "public.json";
if (options.verbose) Logger.setLogLevel("DEBUG");
const input = unstringifyBigInts(JSON.parse(await fs.promises.readFile(inputName, "utf8")));
const {proof, publicSignals} = await plonk.fullProve(input, wasmName, zkeyName, logger);
await fs.promises.writeFile(proofName, JSON.stringify(stringifyBigInts(proof), null, 1), "utf-8");
await fs.promises.writeFile(publicName, JSON.stringify(stringifyBigInts(publicSignals), null, 1), "utf-8");
return 0;
}
// plonk verify [verification_key.json] [public.json] [proof.json]
async function plonkVerify(params, options) {
const verificationKeyName = params[0] || "verification_key.json";
const publicName = params[1] || "public.json";
const proofName = params[2] || "proof.json";
const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync(verificationKeyName, "utf8")));
const pub = unstringifyBigInts(JSON.parse(fs.readFileSync(publicName, "utf8")));
const proof = unstringifyBigInts(JSON.parse(fs.readFileSync(proofName, "utf8")));
if (options.verbose) Logger.setLogLevel("DEBUG");
const isValid = await plonk.verify(verificationKey, pub, proof, logger);
if (isValid) {
return 0;
} else {
return 1;
}
}

11
package-lock.json generated

@ -13,6 +13,7 @@
"circom_runtime": "0.1.13",
"fastfile": "0.0.19",
"ffjavascript": "0.2.35",
"js-sha3": "^0.8.0",
"logplease": "^1.2.15",
"r1csfile": "0.0.32",
"readline": "^1.3.0"
@ -1670,6 +1671,11 @@
"node": ">= 10.13.0"
}
},
"node_modules/js-sha3": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
"integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -4601,6 +4607,11 @@
"supports-color": "^7.0.0"
}
},
"js-sha3": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
"integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",

@ -43,6 +43,7 @@
"circom_runtime": "0.1.13",
"fastfile": "0.0.19",
"ffjavascript": "0.2.35",
"js-sha3": "^0.8.0",
"logplease": "^1.2.15",
"r1csfile": "0.0.32",
"readline": "^1.3.0"

@ -13,7 +13,11 @@ export default async function groth16Prove(zkeyFileName, witnessFileName, logger
const {fd: fdZKey, sections: sectionsZKey} = await binFileUtils.readBinFile(zkeyFileName, "zkey", 2, 1<<25, 1<<23);
const zkey = await zkeyUtils.readHeader(fdZKey, sectionsZKey, "groth16");
const zkey = await zkeyUtils.readHeader(fdZKey, sectionsZKey);
if (zkey.protocol != "groth16") {
throw new Error("zkey file is not groth16");
}
if (!Scalar.eq(zkey.r, wtns.q)) {
throw new Error("Curve of the witness does not match the curve of the proving key");
@ -162,7 +166,7 @@ async function buldABC1(curve, zkey, witness, coeffs, logger) {
}
/*
async function buldABC(curve, zkey, witness, coeffs, logger) {
const concurrency = curve.tm.concurrency;
const sCoef = 4*3 + zkey.n8r;
@ -291,7 +295,7 @@ async function buldABC(curve, zkey, witness, coeffs, logger) {
return 4 + m*sCoef;
}
}
*/
async function joinABC(curve, zkey, a, b, c, logger) {
const MAX_CHUNK_SIZE = 1 << 22;

@ -74,4 +74,4 @@ export default async function groth16Verify(vk_verifier, publicSignals, proof, l
if (logger) logger.info("OK!");
return true;
};
}

4
src/plonk.js Normal file

@ -0,0 +1,4 @@
export {default as setup} from "./plonk_setup.js";
export {default as fullProve} from "./plonk_fullprove.js";
export {default as prove} from "./plonk_prove.js";
export {default as verify} from "./plonk_verify.js";

10
src/plonk_fullprove.js Normal file

@ -0,0 +1,10 @@
import plonk_prove from "./plonk_prove.js";
import wtns_calculate from "./wtns_calculate.js";
export default async function plonkFullProve(input, wasmFile, zkeyFileName, logger) {
const wtns= {
type: "mem"
};
await wtns_calculate(input, wasmFile, wtns);
return await plonk_prove(zkeyFileName, wtns, logger);
}

717
src/plonk_prove.js Normal file

@ -0,0 +1,717 @@
/*
Copyright 2021 0kims association.
This file is part of snarkjs.
snarkjs is a free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your option)
any later version.
snarkjs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
snarkjs. If not, see <https://www.gnu.org/licenses/>.
*/
/* Implementation of this paper: https://eprint.iacr.org/2019/953.pdf section 8.4 */
import * as binFileUtils from "@iden3/binfileutils";
import * as zkeyUtils from "./zkey_utils.js";
import * as wtnsUtils from "./wtns_utils.js";
import { getCurveFromQ as getCurve } from "./curves.js";
import { Scalar, utils, BigBuffer } from "ffjavascript";
const {stringifyBigInts} = utils;
import jsSha3 from "js-sha3";
const { keccak256 } = jsSha3;
export default async function plonk16Prove(zkeyFileName, witnessFileName, logger) {
const {fd: fdWtns, sections: sectionsWtns} = await binFileUtils.readBinFile(witnessFileName, "wtns", 2, 1<<25, 1<<23);
const wtns = await wtnsUtils.readHeader(fdWtns, sectionsWtns);
const {fd: fdZKey, sections: sectionsZKey} = await binFileUtils.readBinFile(zkeyFileName, "zkey", 2, 1<<25, 1<<23);
const zkey = await zkeyUtils.readHeader(fdZKey, sectionsZKey);
if (zkey.protocol != "plonk") {
throw new Error("zkey file is not groth16");
}
if (!Scalar.eq(zkey.r, wtns.q)) {
throw new Error("Curve of the witness does not match the curve of the proving key");
}
if (wtns.nWitness != zkey.nVars -zkey.nAdditions) {
throw new Error(`Invalid witness length. Circuit: ${zkey.nVars}, witness: ${wtns.nWitness}, ${zkey.nAdditions}`);
}
const curve = await getCurve(zkey.q);
const Fr = curve.Fr;
const G1 = curve.G1;
const n8r = curve.Fr.n8;
if (logger) logger.debug("Reading Wtns");
const buffWitness = await binFileUtils.readSection(fdWtns, sectionsWtns, 2);
// First element in plonk is not used and can be any value. (But always the same).
// We set it to zero to go faster in the exponentiations.
buffWitness.set(Fr.zero, 0);
const buffInternalWitness = new BigBuffer(n8r*zkey.nAdditions);
await calculateAdditions();
let A,B,C,Z;
let pol_a,pol_b,pol_c, pol_z, pol_t, pol_r;
let proof = {};
if (logger) logger.debug("Reading L Points");
const lagrangeBases = await binFileUtils.readSection(fdZKey, sectionsZKey, 14);
const sigmaBuff = new BigBuffer(zkey.domainSize*n8r*4*3);
let o = sectionsZKey[12][0].p + zkey.domainSize*n8r;
await fdZKey.readToBuffer(sigmaBuff, 0 , zkey.domainSize*n8r*4, o);
o += zkey.domainSize*n8r*5;
await fdZKey.readToBuffer(sigmaBuff, zkey.domainSize*n8r*4 , zkey.domainSize*n8r*4, o);
o += zkey.domainSize*n8r*5;
await fdZKey.readToBuffer(sigmaBuff, zkey.domainSize*n8r*8 , zkey.domainSize*n8r*4, o);
const pol_s1 = new BigBuffer(zkey.domainSize*n8r);
await fdZKey.readToBuffer(pol_s1, 0 , zkey.domainSize*n8r, sectionsZKey[12][0].p);
const pol_s2 = new BigBuffer(zkey.domainSize*n8r);
await fdZKey.readToBuffer(pol_s2, 0 , zkey.domainSize*n8r, sectionsZKey[12][0].p + 5*zkey.domainSize*n8r);
const PTau = await binFileUtils.readSection(fdZKey, sectionsZKey, 15);
let alpha, beta, gamma, xi;
let xim;
const b=[];
await round1();
await round2();
await round3();
await round4();
await round5();
///////////////////////
// Final adjustments //
///////////////////////
proof.protocol = "plonk";
await fdZKey.close();
await fdWtns.close();
let publicSignals = [];
for (let i=1; i<= zkey.nPublic; i++) {
const pub = buffWitness.slice(i*Fr.n8, i*Fr.n8+Fr.n8);
publicSignals.push(Scalar.fromRprLE(pub));
}
proof.A = G1.toObject(G1.toAffine(proof.A));
proof.B = G1.toObject(G1.toAffine(proof.B));
proof.C = G1.toObject(G1.toAffine(proof.C));
proof.Z = G1.toObject(G1.toAffine(proof.Z));
proof.T1 = G1.toObject(G1.toAffine(proof.T1));
proof.T2 = G1.toObject(G1.toAffine(proof.T2));
proof.T3 = G1.toObject(G1.toAffine(proof.T3));
proof.eval_a = Fr.toObject(proof.eval_a);
proof.eval_b = Fr.toObject(proof.eval_b);
proof.eval_c = Fr.toObject(proof.eval_c);
proof.eval_s1 = Fr.toObject(proof.eval_s1);
proof.eval_s2 = Fr.toObject(proof.eval_s2);
proof.eval_zw = Fr.toObject(proof.eval_zw);
proof.eval_t = Fr.toObject(proof.eval_t);
proof.eval_r = Fr.toObject(proof.eval_r);
proof.Wxi = G1.toObject(G1.toAffine(proof.Wxi));
proof.Wxiw = G1.toObject(G1.toAffine(proof.Wxiw));
proof = stringifyBigInts(proof);
publicSignals = stringifyBigInts(publicSignals);
return {proof, publicSignals};
async function calculateAdditions() {
const additionsBuff = await binFileUtils.readSection(fdZKey, sectionsZKey, 3);
const sSum = 8+curve.Fr.n8*2;
for (let i=0; i<zkey.nAdditions; i++) {
const ai= readUInt32(additionsBuff, i*sSum);
const bi= readUInt32(additionsBuff, i*sSum+4);
const ac= additionsBuff.slice(i*sSum+8, i*sSum+8+n8r);
const bc= additionsBuff.slice(i*sSum+8+n8r, i*sSum+8+n8r*2);
const aw= getWitness(ai);
const bw= getWitness(bi);
const r = curve.Fr.add(
curve.Fr.mul(ac, aw),
curve.Fr.mul(bc, bw)
);
buffInternalWitness.set(r, n8r*i);
}
}
async function buildABC() {
const A = new BigBuffer(zkey.domainSize * n8r);
const B = new BigBuffer(zkey.domainSize * n8r);
const C = new BigBuffer(zkey.domainSize * n8r);
const aMap = await binFileUtils.readSection(fdZKey, sectionsZKey, 4);
const bMap = await binFileUtils.readSection(fdZKey, sectionsZKey, 5);
const cMap = await binFileUtils.readSection(fdZKey, sectionsZKey, 6);
for (let i=0; i<zkey.nConstrains; i++) {
const iA = readUInt32(aMap, i*4);
A.set(getWitness(iA), i*n8r);
const iB = readUInt32(bMap, i*4);
B.set(getWitness(iB), i*n8r);
const iC = readUInt32(cMap, i*4);
C.set(getWitness(iC), i*n8r);
}
return [A,B,C];
}
function readUInt32(b, o) {
const buff = b.slice(o, o+4);
const buffV = new DataView(buff.buffer, buff.byteOffset, buff.byteLength);
return buffV.getUint32(0, true);
}
function getWitness(idx) {
if (idx < zkey.nVars-zkey.nAdditions) {
return buffWitness.slice(idx*n8r, idx*n8r+n8r);
} else if (idx < zkey.nVars) {
return buffInternalWitness.slice((idx - (zkey.nVars-zkey.nAdditions))*n8r, (idx-(zkey.nVars-zkey.nAdditions))*n8r + n8r);
} else {
return curve.Fr.zero;
}
}
async function round1() {
for (let i=1; i<=9; i++) {
b[i] = curve.Fr.random();
b[i] = curve.Fr.e(i);
// b[i] = curve.Fr.zero;
}
[A, B, C] = await buildABC();
proof.A = await curve.G1.multiExpAffine(lagrangeBases, A, logger, "multiexp A");
proof.B = await curve.G1.multiExpAffine(lagrangeBases, B, logger, "multiexp B");
proof.C = await curve.G1.multiExpAffine(lagrangeBases, C, logger, "multiexp C");
proof.A = G1.add(proof.A, G1.timesFr(zkey.XtoMplus1, b[1]));
proof.A = G1.sub(proof.A, G1.timesFr(zkey.X, b[1]));
proof.A = G1.add(proof.A, G1.timesFr(zkey.XtoM, b[2]));
proof.A = G1.sub(proof.A, G1.timesFr(G1.one, b[2]));
proof.B = G1.add(proof.B, G1.timesFr(zkey.XtoMplus1, b[3]));
proof.B = G1.sub(proof.B, G1.timesFr(zkey.X, b[3]));
proof.B = G1.add(proof.B, G1.timesFr(zkey.XtoM, b[4]));
proof.B = G1.sub(proof.B, G1.timesFr(G1.one, b[4]));
proof.C = G1.add(proof.C, G1.timesFr(zkey.XtoMplus1, b[5]));
proof.C = G1.sub(proof.C, G1.timesFr(zkey.X, b[5]));
proof.C = G1.add(proof.C, G1.timesFr(zkey.XtoM, b[6]));
proof.C = G1.sub(proof.C, G1.timesFr(G1.one, b[6]));
}
async function round2() {
const transcript1 = new Uint8Array(G1.F.n8*2*3);
G1.toRprUncompressed(transcript1, 0, proof.A);
G1.toRprUncompressed(transcript1, G1.F.n8*2, proof.B);
G1.toRprUncompressed(transcript1, G1.F.n8*4, proof.C);
beta = hashToFr(transcript1);
if (logger) logger.debug("beta: " + Fr.toString(beta));
const transcript2 = new Uint8Array(n8r);
Fr.toRprBE(transcript2, 0, beta);
gamma = hashToFr(transcript2);
if (logger) logger.debug("gamma: " + Fr.toString(gamma));
A = await Fr.batchToMontgomery(A);
B = await Fr.batchToMontgomery(B);
C = await Fr.batchToMontgomery(C);
let numArr = new BigBuffer(Fr.n8*zkey.domainSize);
let denArr = new BigBuffer(Fr.n8*zkey.domainSize);
numArr.set(Fr.one, 0);
denArr.set(Fr.one, 0);
let w = Fr.one;
for (let i=0; i<zkey.domainSize; i++) {
let n1 = A.slice(i*n8r, (i+1)*n8r);
n1 = Fr.add( n1, Fr.mul(beta, w) );
n1 = Fr.add( n1, gamma );
let n2 = B.slice(i*n8r, (i+1)*n8r);
n2 = Fr.add( n2, Fr.mul(zkey.k1, Fr.mul(beta, w) ));
n2 = Fr.add( n2, gamma );
let n3 = C.slice(i*n8r, (i+1)*n8r);
n3 = Fr.add( n3, Fr.mul(zkey.k2, Fr.mul(beta, w) ));
n3 = Fr.add( n3, gamma );
const num = Fr.mul(n1, Fr.mul(n2, n3));
let d1 = A.slice(i*n8r, (i+1)*n8r);
d1 = Fr.add(d1, Fr.mul( sigmaBuff.slice(i*n8r*4, i*n8r*4 + n8r) , beta));
d1 = Fr.add(d1, gamma);
let d2 = B.slice(i*n8r, (i+1)*n8r);
d2 = Fr.add(d2, Fr.mul( sigmaBuff.slice((zkey.domainSize + i)*4*n8r, (zkey.domainSize + i)*4*n8r+n8r) , beta));
d2 = Fr.add(d2, gamma);
let d3 = C.slice(i*n8r, (i+1)*n8r);
d3 = Fr.add(d3, Fr.mul( sigmaBuff.slice((zkey.domainSize*2 + i)*4*n8r, (zkey.domainSize*2 + i)*4*n8r + n8r) , beta));
d3 = Fr.add(d3, gamma);
const den = Fr.mul(d1, Fr.mul(d2, d3));
numArr.set(
Fr.mul(
numArr.slice(i*n8r,(i+1)*n8r) ,
num
),
((i+1)%zkey.domainSize)*n8r
);
denArr.set(
Fr.mul(
denArr.slice(i*n8r,(i+1)*n8r) ,
den
),
((i+1)%zkey.domainSize)*n8r
);
w = Fr.mul(w, Fr.w[zkey.power]);
}
denArr = await Fr.batchInverse(denArr);
// TODO: Do it in assembly and in parallel
for (let i=0; i<zkey.domainSize; i++) {
numArr.set( Fr.mul( numArr.slice(i*n8r, (i+1)*n8r), denArr.slice(i*n8r, (i+1)*n8r) ) ,i*n8r);
}
if (!Fr.eq(numArr.slice(0, n8r), Fr.one)) {
throw new Error("Copy constraints does not match");
}
Z = numArr.slice(0, zkey.domainSize*n8r);
numArr = await Fr.batchFromMontgomery(numArr);
proof.Z = await curve.G1.multiExpAffine(lagrangeBases, numArr, logger, "multiexp Z");
proof.Z = G1.add(proof.Z, G1.timesFr(zkey.XtoMplus2, b[7]));
proof.Z = G1.sub(proof.Z, G1.timesFr(zkey.Xto2, b[7]));
proof.Z = G1.add(proof.Z, G1.timesFr(zkey.XtoMplus1, b[8]));
proof.Z = G1.sub(proof.Z, G1.timesFr(zkey.X, b[8]));
proof.Z = G1.add(proof.Z, G1.timesFr(zkey.XtoM, b[9]));
proof.Z = G1.sub(proof.Z, G1.timesFr(G1.one, b[9]));
}
async function round3() {
async function checkDegree(P) {
const p = await curve.Fr.ifft(P);
let deg = (P.byteLength/n8r)-1;
while ((deg>0)&&(Fr.isZero(p.slice(deg*n8r, deg*n8r+n8r)))) deg--;
return deg;
}
function printPol(P) {
const n=(P.byteLength/n8r);
console.log("[");
for (let i=0; i<n; i++) {
console.log(Fr.toString(P.slice(i*n8r, i*n8r+n8r)));
}
console.log("]");
}
const QM4 = new BigBuffer(zkey.domainSize*4*n8r);
await fdZKey.readToBuffer(QM4, 0 , zkey.domainSize*n8r*4, sectionsZKey[7][0].p + zkey.domainSize*n8r);
const QL4 = new BigBuffer(zkey.domainSize*4*n8r);
await fdZKey.readToBuffer(QL4, 0 , zkey.domainSize*n8r*4, sectionsZKey[8][0].p + zkey.domainSize*n8r);
const QR4 = new BigBuffer(zkey.domainSize*4*n8r);
await fdZKey.readToBuffer(QR4, 0 , zkey.domainSize*n8r*4, sectionsZKey[9][0].p + zkey.domainSize*n8r);
const QO4 = new BigBuffer(zkey.domainSize*4*n8r);
await fdZKey.readToBuffer(QO4, 0 , zkey.domainSize*n8r*4, sectionsZKey[10][0].p + zkey.domainSize*n8r);
const QC4 = new BigBuffer(zkey.domainSize*4*n8r);
await fdZKey.readToBuffer(QC4, 0 , zkey.domainSize*n8r*4, sectionsZKey[11][0].p + zkey.domainSize*n8r);
const lPols = await binFileUtils.readSection(fdZKey, sectionsZKey, 13);
const transcript3 = new Uint8Array(G1.F.n8*2);
G1.toRprUncompressed(transcript3, 0, proof.Z);
alpha = hashToFr(transcript3);
if (logger) logger.debug("alpha: " + Fr.toString(alpha));
let A4; [pol_a, A4] = await to4T(A);
let B4; [pol_b, B4] = await to4T(B);
let C4; [pol_c, C4] = await to4T(C);
let Z4; [pol_z, Z4] = await to4T(Z);
/*
const Zw = new BigBuffer(zkey.domainSize*4*n8r);
for (let i=0; i<zkey.domainSize*4; i++) {
Zw.set(
Z4.slice(((i+zkey.domainSize*4+4)%(zkey.domainSize*4)) *n8r, ((i+zkey.domainSize*4+4)%(zkey.domainSize*4)) *n8r +n8r),
i*n8r
);
}
const degZw = await checkDegree(Zw);
printPol(Zw);
console.log("degZw: " + degZw);
*/
const T = new BigBuffer(zkey.domainSize*4*n8r);
let w = Fr.one;
for (let i=0; i<zkey.domainSize*4; i++) {
const a = A4.slice(i*n8r, i*n8r+n8r);
const b = B4.slice(i*n8r, i*n8r+n8r);
const c = C4.slice(i*n8r, i*n8r+n8r);
const z = Z4.slice(i*n8r, i*n8r+n8r);
const zw = Z4.slice(((i+zkey.domainSize*4+4)%(zkey.domainSize*4)) *n8r, ((i+zkey.domainSize*4+4)%(zkey.domainSize*4)) *n8r +n8r);
const qm = QM4.slice(i*n8r, i*n8r+n8r);
const ql = QL4.slice(i*n8r, i*n8r+n8r);
const qr = QR4.slice(i*n8r, i*n8r+n8r);
const qo = QO4.slice(i*n8r, i*n8r+n8r);
const qc = QC4.slice(i*n8r, i*n8r+n8r);
const s1 = sigmaBuff.slice(i*n8r, i*n8r+n8r);
const s2 = sigmaBuff.slice((i+zkey.domainSize*4)*n8r, (i+zkey.domainSize*4)*n8r+n8r);
const s3 = sigmaBuff.slice((i+zkey.domainSize*8)*n8r, (i+zkey.domainSize*8)*n8r+n8r);
let pl = Fr.zero;
for (let j=0; j<zkey.nPublic; j++) {
pl = Fr.sub(pl, Fr.mul(
lPols.slice( (j*5*zkey.domainSize+ zkey.domainSize+ i)*n8r, (j*5*zkey.domainSize+ zkey.domainSize + i+1)*n8r),
A.slice(j*n8r, (j+1)*n8r)
));
}
let e1 = Fr.mul(Fr.mul(a, b), qm);
e1 = Fr.add(e1, Fr.mul(a, ql));
e1 = Fr.add(e1, Fr.mul(b, qr));
e1 = Fr.add(e1, Fr.mul(c, qo));
e1 = Fr.add(e1, pl);
e1 = Fr.add(e1, qc);
const betaw = Fr.mul(beta, w);
let e2a =a;
e2a = Fr.add(e2a, betaw);
e2a = Fr.add(e2a, gamma);
let e2b =b;
e2b = Fr.add(e2b, Fr.mul(betaw, zkey.k1));
e2b = Fr.add(e2b, gamma);
let e2c =c;
e2c = Fr.add(e2c, Fr.mul(betaw, zkey.k2));
e2c = Fr.add(e2c, gamma);
let e2 = Fr.mul(Fr.mul(e2a, e2b), e2c);
e2 = Fr.mul(e2, z);
e2 = Fr.mul(e2, alpha);
let e3a = a;
e3a = Fr.add(e3a, Fr.mul(beta, s1));
e3a = Fr.add(e3a, gamma);
let e3b = b;
e3b = Fr.add(e3b, Fr.mul(beta,s2));
e3b = Fr.add(e3b, gamma);
let e3c = c;
e3c = Fr.add(e3c, Fr.mul(beta,s3));
e3c = Fr.add(e3c, gamma);
let e3 = Fr.mul(Fr.mul(e3a, e3b), e3c);
e3 = Fr.mul(e3, zw);
e3 = Fr.mul(e3, alpha);
let e4 = Fr.sub(z, Fr.one);
e4 = Fr.mul(e4, lPols.slice( (zkey.domainSize + i)*n8r, (zkey.domainSize+i+1)*n8r));
e4 = Fr.mul(e4, Fr.mul(alpha, alpha));
let e = Fr.add(Fr.sub(Fr.add(e1, e2), e3), e4);
T.set(e, i*n8r);
w = Fr.mul(w, Fr.w[zkey.power+2]);
}
printPol(T);
let t = await Fr.ifft(T);
for (let i=0; i<zkey.domainSize; i++) {
t.set(Fr.neg(t.slice(i*n8r, i*n8r+n8r)), i*n8r);
}
for (let i=zkey.domainSize; i<zkey.domainSize*4; i++) {
const a = Fr.sub(
t.slice((i-zkey.domainSize)*n8r, (i-zkey.domainSize)*n8r + n8r),
t.slice(i*n8r, i*n8r+n8r)
);
t.set(a, i*n8r);
if (i > (zkey.domainSize*3 -4) ) {
if (!Fr.isZero(a)) {
throw new Error("T Polynomial is not divisible");
}
}
}
pol_t = t.slice(0, (zkey.domainSize*3)*n8r);
t = await Fr.batchFromMontgomery(t);
proof.T1 = await curve.G1.multiExpAffine(PTau, t.slice(0, zkey.domainSize*n8r), logger, "multiexp T1");
proof.T2 = await curve.G1.multiExpAffine(PTau, t.slice(zkey.domainSize*n8r, zkey.domainSize*2*n8r), logger, "multiexp T2");
proof.T3 = await curve.G1.multiExpAffine(PTau, t.slice(zkey.domainSize*2*n8r, (zkey.domainSize*3)*n8r), logger, "multiexp T3");
async function to4T(A) {
const a = await Fr.ifft(A);
const a4 = new BigBuffer(n8r*zkey.domainSize*4);
a4.set(a, 0);
const A4 = await Fr.fft(a4);
return [a, A4];
}
}
async function round4() {
const pol_qm = new BigBuffer(zkey.domainSize*n8r);
await fdZKey.readToBuffer(pol_qm, 0 , zkey.domainSize*n8r, sectionsZKey[7][0].p);
const pol_ql = new BigBuffer(zkey.domainSize*n8r);
await fdZKey.readToBuffer(pol_ql, 0 , zkey.domainSize*n8r, sectionsZKey[8][0].p);
const pol_qr = new BigBuffer(zkey.domainSize*n8r);
await fdZKey.readToBuffer(pol_qr, 0 , zkey.domainSize*n8r, sectionsZKey[9][0].p);
const pol_qo = new BigBuffer(zkey.domainSize*n8r);
await fdZKey.readToBuffer(pol_qo, 0 , zkey.domainSize*n8r, sectionsZKey[10][0].p);
const pol_qc = new BigBuffer(zkey.domainSize*n8r);
await fdZKey.readToBuffer(pol_qc, 0 , zkey.domainSize*n8r, sectionsZKey[11][0].p);
const pol_s3 = new BigBuffer(zkey.domainSize*n8r);
await fdZKey.readToBuffer(pol_s3, 0 , zkey.domainSize*n8r, sectionsZKey[12][0].p + 10*zkey.domainSize*n8r);
const transcript4 = new Uint8Array(G1.F.n8*2*3);
G1.toRprUncompressed(transcript4, 0, proof.T1);
G1.toRprUncompressed(transcript4, G1.F.n8*2, proof.T2);
G1.toRprUncompressed(transcript4, G1.F.n8*4, proof.T3);
xi = hashToFr(transcript4);
if (logger) logger.debug("xi: " + Fr.toString(xi));
proof.eval_a = evalPol(pol_a, xi);
proof.eval_b = evalPol(pol_b, xi);
proof.eval_c = evalPol(pol_c, xi);
proof.eval_s1 = evalPol(pol_s1, xi);
proof.eval_s2 = evalPol(pol_s2, xi);
proof.eval_t = evalPol(pol_t, xi);
proof.eval_zw = evalPol(pol_z, Fr.mul(xi, Fr.w[zkey.power]));
const coef_ab = Fr.mul(proof.eval_a, proof.eval_b);
let e2a = proof.eval_a;
const betaxi = Fr.mul(beta, xi);
e2a = Fr.add( e2a, betaxi);
e2a = Fr.add( e2a, gamma);
let e2b = proof.eval_b;
e2b = Fr.add( e2b, Fr.mul(betaxi, zkey.k1));
e2b = Fr.add( e2b, gamma);
let e2c = proof.eval_c;
e2c = Fr.add( e2c, Fr.mul(betaxi, zkey.k2));
e2c = Fr.add( e2c, gamma);
const e2 = Fr.mul(Fr.mul(Fr.mul(e2a, e2b), e2c), alpha);
let e3a = proof.eval_a;
e3a = Fr.add( e3a, Fr.mul(beta, proof.eval_s1));
e3a = Fr.add( e3a, gamma);
let e3b = proof.eval_b;
e3b = Fr.add( e3b, Fr.mul(beta, proof.eval_s2));
e3b = Fr.add( e3b, gamma);
let e3 = Fr.mul(e3a, e3b);
e3 = Fr.mul(e3, beta);
e3 = Fr.mul(e3, proof.eval_zw);
e3 = Fr.mul(e3, alpha);
xim= xi;
for (let i=0; i<zkey.power; i++) xim = Fr.mul(xim, xim);
const eval_l1 = Fr.div(
Fr.sub(xim, Fr.one),
Fr.mul(Fr.sub(xi, Fr.one), Fr.e(zkey.domainSize))
);
const e4 = Fr.mul(eval_l1, Fr.mul(alpha, alpha));
const coefs3 = e3;
const coefz = Fr.add(e2, e4);
pol_r = new BigBuffer(zkey.domainSize*n8r);
for (let i = 0; i<zkey.domainSize; i++) {
let v = Fr.mul(coef_ab, pol_qm.slice(i*n8r,(i+1)*n8r));
v = Fr.add(v, Fr.mul(proof.eval_a, pol_ql.slice(i*n8r,(i+1)*n8r)));
v = Fr.add(v, Fr.mul(proof.eval_b, pol_qr.slice(i*n8r,(i+1)*n8r)));
v = Fr.add(v, Fr.mul(proof.eval_c, pol_qo.slice(i*n8r,(i+1)*n8r)));
v = Fr.add(v, pol_qc.slice(i*n8r,(i+1)*n8r));
v = Fr.add(v, Fr.mul(coefz, pol_z.slice(i*n8r,(i+1)*n8r)));
v = Fr.sub(v, Fr.mul(coefs3, pol_s3.slice(i*n8r,(i+1)*n8r)));
pol_r.set(v, i*n8r);
}
proof.eval_r = evalPol(pol_r, xi);
}
async function round5() {
const transcript5 = new Uint8Array(n8r*8);
Fr.toRprBE(transcript5, 0, proof.eval_a);
Fr.toRprBE(transcript5, n8r, proof.eval_b);
Fr.toRprBE(transcript5, n8r*2, proof.eval_c);
Fr.toRprBE(transcript5, n8r*3, proof.eval_s1);
Fr.toRprBE(transcript5, n8r*4, proof.eval_s2);
Fr.toRprBE(transcript5, n8r*5, proof.eval_zw);
Fr.toRprBE(transcript5, n8r*6, proof.eval_t);
Fr.toRprBE(transcript5, n8r*7, proof.eval_r);
const v = [];
v[1] = hashToFr(transcript5);
if (logger) logger.debug("v: " + Fr.toString(v[1]));
for (let i=2; i<=6; i++ ) v[i] = Fr.mul(v[i-1], v[1]);
// TODO DELETE
// v[1] = Fr.zero;
// v[2] = Fr.zero;
// v[3] = Fr.zero;
// v[4] = Fr.zero;
// v[5] = Fr.zero;
// v[6] = Fr.zero;
let pol_wxi = new BigBuffer(zkey.domainSize*n8r);
const xi2m = Fr.mul(xim, xim);
for (let i=0; i<zkey.domainSize; i++) {
let w = Fr.zero;
w = Fr.add(w, pol_t.slice(i*n8r, (i+1)*n8r));
w = Fr.add(w, Fr.mul(xim, pol_t.slice( (zkey.domainSize+i)*n8r, (zkey.domainSize+i+1)*n8r )));
w = Fr.add(w, Fr.mul(xi2m, pol_t.slice( (zkey.domainSize*2+i)*n8r, (zkey.domainSize*2+i+1)*n8r )));
w = Fr.add(w, Fr.mul(v[1], pol_r.slice(i*n8r, (i+1)*n8r)));
w = Fr.add(w, Fr.mul(v[2], pol_a.slice(i*n8r, (i+1)*n8r)));
w = Fr.add(w, Fr.mul(v[3], pol_b.slice(i*n8r, (i+1)*n8r)));
w = Fr.add(w, Fr.mul(v[4], pol_c.slice(i*n8r, (i+1)*n8r)));
w = Fr.add(w, Fr.mul(v[5], pol_s1.slice(i*n8r, (i+1)*n8r)));
w = Fr.add(w, Fr.mul(v[6], pol_s2.slice(i*n8r, (i+1)*n8r)));
pol_wxi.set(w, i*n8r);
}
let w0 = pol_wxi.slice(0, n8r);
w0 = Fr.sub(w0, proof.eval_t);
w0 = Fr.sub(w0, Fr.mul(v[1], proof.eval_r));
w0 = Fr.sub(w0, Fr.mul(v[2], proof.eval_a));
w0 = Fr.sub(w0, Fr.mul(v[3], proof.eval_b));
w0 = Fr.sub(w0, Fr.mul(v[4], proof.eval_c));
w0 = Fr.sub(w0, Fr.mul(v[5], proof.eval_s1));
w0 = Fr.sub(w0, Fr.mul(v[6], proof.eval_s2));
pol_wxi.set(w0, 0);
pol_wxi= divPol1(pol_wxi, xi);
pol_wxi = await Fr.batchFromMontgomery(pol_wxi);
proof.Wxi = await curve.G1.multiExpAffine(PTau, pol_wxi, logger, "multiexp Wxi");
let pol_wxiw = new BigBuffer(zkey.domainSize*n8r);
for (let i=0; i<zkey.domainSize; i++) {
const w = pol_z.slice(i*n8r, (i+1)*n8r);
pol_wxiw.set(w, i*n8r);
}
w0 = pol_wxiw.slice(0, n8r);
w0 = Fr.sub(w0, proof.eval_zw);
pol_wxiw.set(w0, 0);
pol_wxiw= divPol1(pol_wxiw, Fr.mul(xi, Fr.w[zkey.power]));
pol_wxiw = await Fr.batchFromMontgomery(pol_wxiw);
proof.Wxiw = await curve.G1.multiExpAffine(PTau, pol_wxiw, logger, "multiexp Wxiw");
}
function hashToFr(transcript) {
const v = Scalar.fromRprBE(new Uint8Array(keccak256.arrayBuffer(transcript)));
return Fr.e(v);
}
function evalPol(P, x) {
const n = P.byteLength / n8r;
if (n == 0) return Fr.zero;
let res = P.slice((n-1)*n8r, n*n8r);
for (let i=n-2; i>=0; i--) {
res = Fr.add(Fr.mul(res, x), P.slice(i*n8r, (i+1)*n8r));
}
return res;
}
function divPol1(P, d) {
const n = P.byteLength/n8r;
const res = new BigBuffer(n*n8r);
res.set(Fr.zero, (n-1) *n8r);
res.set(P.slice((n-1)*n8r, n*n8r), (n-2)*n8r);
for (let i=n-3; i>=0; i--) {
res.set(
Fr.add(
P.slice((i+1)*n8r, (i+2)*n8r),
Fr.mul(
d,
res.slice((i+1)*n8r, (i+2)*n8r)
)
),
i*n8r
);
}
if (!Fr.eq(
P.slice(0, n8r),
Fr.mul(
Fr.neg(d),
res.slice(0, n8r)
)
)) {
throw new Error("Polinomial does not divide");
}
return res;
}
}

448
src/plonk_setup.js Normal file

@ -0,0 +1,448 @@
/*
Copyright 2021 0kims association.
This file is part of snarkjs.
snarkjs is a free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your option)
any later version.
snarkjs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
snarkjs. If not, see <https://www.gnu.org/licenses/>.
*/
/* Implementation of this paper: https://eprint.iacr.org/2019/953.pdf */
import {readR1csHeader} from "r1csfile";
import * as utils from "./powersoftau_utils.js";
import {
readBinFile,
createBinFile,
readSection,
writeBigInt,
startWriteSection,
endWriteSection,
} from "@iden3/binfileutils";
import { log2 } from "./misc.js";
import { Scalar, BigBuffer } from "ffjavascript";
import Blake2b from "blake2b-wasm";
import BigArray from "./bigarray.js";
export default async function plonkSetup(r1csName, ptauName, zkeyName, logger) {
await Blake2b.ready();
const {fd: fdPTau, sections: sectionsPTau} = await readBinFile(ptauName, "ptau", 1, 1<<22, 1<<24);
const {curve, power} = await utils.readPTauHeader(fdPTau, sectionsPTau);
const {fd: fdR1cs, sections: sectionsR1cs} = await readBinFile(r1csName, "r1cs", 1, 1<<22, 1<<24);
const r1cs = await readR1csHeader(fdR1cs, sectionsR1cs, false);
const sG1 = curve.G1.F.n8*2;
const G1 = curve.G1;
const sG2 = curve.G2.F.n8*2;
const Fr = curve.Fr;
const n8r = curve.Fr.n8;
if (logger) logger.info("Reading r1cs");
let sR1cs = await readSection(fdR1cs, sectionsR1cs, 2);
const plonkConstraints = new BigArray();
const plonkAdditions = new BigArray();
let plonkNVars = r1cs.nVars;
const nPublic = r1cs.nOutputs + r1cs.nPubInputs;
await processConstraints();
const fdZKey = await createBinFile(zkeyName, "zkey", 1, 15, 1<<22, 1<<24);
if (r1cs.prime != curve.r) {
if (logger) logger.error("r1cs curve does not match powers of tau ceremony curve");
return -1;
}
const cirPower = log2(plonkConstraints.length -1) +1;
const domainSize = 2 ** cirPower;
if (cirPower > power) {
if (logger) logger.error(`circuit too big for this power of tau ceremony. ${r1cs.nConstraints}*2 > 2**${power}`);
return -1;
}
if (!sectionsPTau[12]) {
if (logger) logger.error("Powers of tau is not prepared.");
return -1;
}
const LPoints = new BigBuffer(domainSize*sG1);
const o = sectionsPTau[12][0].p + ((2 ** (cirPower)) -1)*sG1;
await fdPTau.readToBuffer(LPoints, 0, domainSize*sG1, o);
const [k1, k2] = getK1K2();
const vk = {};
await writeAdditions(3, "Additions");
await writeWitnessMap(4, 0, "Amap");
await writeWitnessMap(5, 1, "Bmap");
await writeWitnessMap(6, 2, "Cmap");
await writeQMap(7, 3, "Qm");
await writeQMap(8, 4, "Ql");
await writeQMap(9, 5, "Qr");
await writeQMap(10, 6, "Qo");
await writeQMap(11, 7, "Qc");
await writeSigma(12, "sigma");
await writeLs(13, "lagrange polynomials");
// Write Lagrange Points Section
///////////
await startWriteSection(fdZKey, 14);
await fdZKey.write(LPoints);
await endWriteSection(fdZKey);
// Write PTau points
////////////
await startWriteSection(fdZKey, 15);
const buffOut = new BigBuffer(domainSize*sG1);
await fdPTau.readToBuffer(buffOut, 0, domainSize*sG1, sectionsPTau[2][0].p);
await fdZKey.write(buffOut);
await endWriteSection(fdZKey);
await writeHeaders();
await fdZKey.close();
await fdR1cs.close();
await fdPTau.close();
if (logger) logger.info("Setup Finished");
return ;
async function processConstraints() {
let r1csPos = 0;
function r1cs_readULE32() {
const buff = sR1cs.slice(r1csPos, r1csPos+4);
r1csPos += 4;
const buffV = new DataView(buff.buffer);
return buffV.getUint32(0, true);
}
function r1cs_readCoef() {
const res = Fr.fromRprLE(sR1cs.slice(r1csPos, r1csPos+curve.Fr.n8));
r1csPos += curve.Fr.n8;
return res;
}
function r1cs_readCoefs() {
const coefs = [];
const res = {
k: curve.Fr.zero
};
const nA = r1cs_readULE32();
for (let i=0; i<nA; i++) {
const s = r1cs_readULE32();
const coefp = r1cs_readCoef();
if (s==0) {
res.k = coefp;
} else {
coefs.push([s, coefp]);
}
}
const resCoef = reduceCoef(coefs);
res.s = resCoef[0];
res.coef = resCoef[1];
return res;
}
function reduceCoef(coefs) {
if (coefs.length == 0) {
return [0, curve.Fr.zero];
}
if (coefs.length == 1) {
return coefs[0];
}
const arr1 = coefs.slice(0, coefs.length >> 1);
const arr2 = coefs.slice(coefs.length >> 1);
const coef1 = reduceCoef(arr1);
const coef2 = reduceCoef(arr2);
const sl = coef1[0];
const sr = coef2[0];
const so = plonkNVars++;
const qm = curve.Fr.zero;
const ql = Fr.neg(coef1[1]);
const qr = Fr.neg(coef2[1]);
const qo = curve.Fr.one;
const qc = curve.Fr.zero;
plonkConstraints.push([sl, sr, so, qm, ql, qr, qo, qc]);
plonkAdditions.push([sl, sr, coef1[1], coef2[1]]);
return [so, curve.Fr.one];
}
for (let s = 1; s <= nPublic ; s++) {
const sl = s;
const sr = 0;
const so = 0;
const qm = curve.Fr.zero;
const ql = curve.Fr.one;
const qr = curve.Fr.zero;
const qo = curve.Fr.zero;
const qc = curve.Fr.zero;
plonkConstraints.push([sl, sr, so, qm, ql, qr, qo, qc]);
}
for (let c=0; c<r1cs.nConstraints; c++) {
if ((logger)&&(c%10000 == 0)) logger.debug(`processing constraints: ${c}/${r1cs.nConstraints}`);
const A = r1cs_readCoefs();
const B = r1cs_readCoefs();
const C = r1cs_readCoefs();
const sl = A.s;
const sr = B.s;
const so = C.s;
const qm = curve.Fr.mul(A.coef, B.coef);
const ql = curve.Fr.mul(A.coef, B.k);
const qr = curve.Fr.mul(A.k, B.coef);
const qo = curve.Fr.neg(C.coef);
const qc = curve.Fr.neg(C.k);
plonkConstraints.push([sl, sr, so, qm, ql, qr, qo, qc]);
}
}
async function writeWitnessMap(sectionNum, posConstraint, name) {
await startWriteSection(fdZKey, sectionNum);
for (let i=0; i<plonkConstraints.length; i++) {
await fdZKey.writeULE32(plonkConstraints[i][posConstraint]);
if ((logger)&&(i%1000000 == 0)) logger.debug(`writing ${name}: ${i}/${plonkConstraints.length}`);
}
await endWriteSection(fdZKey);
}
async function writeQMap(sectionNum, posConstraint, name) {
let Q = new BigBuffer(domainSize*n8r);
for (let i=0; i<plonkConstraints.length; i++) {
Q.set(plonkConstraints[i][posConstraint], i*n8r);
if ((logger)&&(i%1000000 == 0)) logger.debug(`writing ${name}: ${i}/${plonkConstraints.length}`);
}
await startWriteSection(fdZKey, sectionNum);
await writeP4(Q);
await endWriteSection(fdZKey);
Q = await Fr.batchFromMontgomery(Q);
vk[name]= await curve.G1.multiExpAffine(LPoints, Q, logger, "multiexp "+name);
}
async function writeP4(buff) {
const q = await Fr.ifft(buff);
const q4 = new BigBuffer(domainSize*n8r*4);
q4.set(q, 0);
const Q4 = await Fr.fft(q4);
await fdZKey.write(q);
await fdZKey.write(Q4);
}
async function writeAdditions(sectionNum, name) {
await startWriteSection(fdZKey, sectionNum);
const buffOut = new Uint8Array((2*4+2*n8r));
const buffOutV = new DataView(buffOut.buffer);
for (let i=0; i<plonkAdditions.length; i++) {
const addition=plonkAdditions[i];
let o=0;
buffOutV.setUint32(o, addition[0], true); o+=4;
buffOutV.setUint32(o, addition[1], true); o+=4;
// The value is storen in Montgomery. stored = v*R
// so when montgomery multiplicated by the witness it result = v*R*w/R = v*w
buffOut.set(addition[2], o); o+= n8r;
buffOut.set(addition[3], o); o+= n8r;
await fdZKey.write(buffOut);
if ((logger)&&(i%1000000 == 0)) logger.debug(`writing ${name}: ${i}/${plonkAdditions.length}`);
}
await endWriteSection(fdZKey);
}
async function writeSigma(sectionNum, name) {
const sigma = new BigBuffer(n8r*domainSize*3);
const lastAparence = new BigArray(plonkNVars);
const firstPos = new BigArray(plonkNVars);
let w = Fr.one;
for (let i=0; i<domainSize;i++) {
if (i<plonkConstraints.length) {
buildSigma(plonkConstraints[i][0], i);
buildSigma(plonkConstraints[i][1], domainSize + i);
buildSigma(plonkConstraints[i][2], domainSize*2 + i);
} else {
buildSigma(0, i);
buildSigma(0, domainSize + i);
buildSigma(0, domainSize*2 + i);
}
w = Fr.mul(w, Fr.w[cirPower]);
if ((logger)&&(i%1000000 == 0)) logger.debug(`writing ${name} phase1: ${i}/${plonkConstraints.length}`);
}
for (let s=0; s<plonkNVars; s++) {
if (typeof firstPos[s] !== "undefined") {
sigma.set(lastAparence[s], firstPos[s]*n8r);
} else {
throw new Error("Variable not used");
}
if ((logger)&&(s%1000000 == 0)) logger.debug(`writing ${name} phase2: ${s}/${plonkNVars}`);
}
await startWriteSection(fdZKey, sectionNum);
let S1 = sigma.slice(0, domainSize*n8r);
await writeP4(S1);
let S2 = sigma.slice(domainSize*n8r, domainSize*n8r*2);
await writeP4(S2);
let S3 = sigma.slice(domainSize*n8r*2, domainSize*n8r*3);
await writeP4(S3);
await endWriteSection(fdZKey);
S1 = await Fr.batchFromMontgomery(S1);
S2 = await Fr.batchFromMontgomery(S2);
S3 = await Fr.batchFromMontgomery(S3);
vk.S1= await curve.G1.multiExpAffine(LPoints, S1, logger, "multiexp S1");
vk.S2= await curve.G1.multiExpAffine(LPoints, S2, logger, "multiexp S2");
vk.S3= await curve.G1.multiExpAffine(LPoints, S3, logger, "multiexp S3");
function buildSigma(s, p) {
if (typeof lastAparence[s] === "undefined") {
firstPos[s] = p;
} else {
sigma.set(lastAparence[s], p*n8r);
}
let v;
if (p<domainSize) {
v = w;
} else if (p<2*domainSize) {
v = Fr.mul(w, k1);
} else {
v = Fr.mul(w, k2);
}
lastAparence[s]=v;
}
}
async function writeLs(sectionNum, name) {
await startWriteSection(fdZKey, sectionNum);
const l=Math.max(nPublic, 1);
for (let i=0; i<l; i++) {
let buff = new BigBuffer(domainSize*n8r);
buff.set(Fr.one, i*n8r);
await writeP4(buff);
if (logger) logger.debug(`writing ${name} ${i}/${l}`);
}
await endWriteSection(fdZKey);
}
async function writeHeaders() {
// Write the header
///////////
await startWriteSection(fdZKey, 1);
await fdZKey.writeULE32(2); // Plonk
await endWriteSection(fdZKey);
// Write the Plonk header section
///////////
await startWriteSection(fdZKey, 2);
const primeQ = curve.q;
const n8q = (Math.floor( (Scalar.bitLength(primeQ) - 1) / 64) +1)*8;
const primeR = curve.r;
const n8r = (Math.floor( (Scalar.bitLength(primeR) - 1) / 64) +1)*8;
await fdZKey.writeULE32(n8q);
await writeBigInt(fdZKey, primeQ, n8q);
await fdZKey.writeULE32(n8r);
await writeBigInt(fdZKey, primeR, n8r);
await fdZKey.writeULE32(plonkNVars); // Total number of bars
await fdZKey.writeULE32(nPublic); // Total number of public vars (not including ONE)
await fdZKey.writeULE32(domainSize); // domainSize
await fdZKey.writeULE32(plonkAdditions.length); // domainSize
await fdZKey.writeULE32(plonkConstraints.length);
await fdZKey.write(k1);
await fdZKey.write(k2);
let bX;
bX = await fdPTau.read(sG1, sectionsPTau[2][0].p + sG1);
await fdZKey.write(bX);
let bX2;
bX2 = await fdPTau.read(sG1, sectionsPTau[2][0].p + sG1*2);
await fdZKey.write(bX2);
let bXtoM;
bXtoM = await fdPTau.read(sG1, sectionsPTau[2][0].p + sG1*domainSize);
await fdZKey.write(bXtoM);
let bXtoMplus1;
bXtoMplus1 = await fdPTau.read(sG1, sectionsPTau[2][0].p + sG1*(domainSize+1));
await fdZKey.write(bXtoMplus1);
let bXtoMplus2;
bXtoMplus2 = await fdPTau.read(sG1, sectionsPTau[2][0].p + sG1*(domainSize+2));
await fdZKey.write(bXtoMplus2);
await fdZKey.write(G1.toAffine(vk.Qm));
await fdZKey.write(G1.toAffine(vk.Ql));
await fdZKey.write(G1.toAffine(vk.Qr));
await fdZKey.write(G1.toAffine(vk.Qo));
await fdZKey.write(G1.toAffine(vk.Qc));
await fdZKey.write(G1.toAffine(vk.S1));
await fdZKey.write(G1.toAffine(vk.S2));
await fdZKey.write(G1.toAffine(vk.S3));
let bX_2;
bX_2 = await fdPTau.read(sG2, sectionsPTau[3][0].p + sG2);
await fdZKey.write(bX_2);
await endWriteSection(fdZKey);
}
function getK1K2() {
let k1 = Fr.two;
while (isIncluded(k1, [], cirPower)) Fr.add(k1, Fr.one);
let k2 = Fr.add(k1, Fr.one);
while (isIncluded(k2, [k1], cirPower)) Fr.add(k2, Fr.one);
return [k1, k2];
function isIncluded(k, kArr, pow) {
const domainSize= 2**pow;
let w = Fr.one;
for (let i=0; i<domainSize; i++) {
if (Fr.eq(k, w)) return true;
for (let j=0; j<kArr.length; j++) {
if (Fr.eq(k, Fr.mul(kArr[j], w))) return true;
}
w = Fr.mul(w, Fr.w[pow]);
}
return false;
}
}
}

417
src/plonk_verify.js Normal file

@ -0,0 +1,417 @@
/*
Copyright 2021 0kims association.
This file is part of snarkjs.
snarkjs is a free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your option)
any later version.
snarkjs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
snarkjs. If not, see <https://www.gnu.org/licenses/>.
*/
/* Implementation of this paper: https://eprint.iacr.org/2019/953.pdf */
import { Scalar } from "ffjavascript";
import * as curves from "./curves.js";
import { utils } from "ffjavascript";
const {unstringifyBigInts} = utils;
import jsSha3 from "js-sha3";
const { keccak256 } = jsSha3;
export default async function plonkVerify(vk_verifier, publicSignals, proof, logger) {
vk_verifier = unstringifyBigInts(vk_verifier);
proof = unstringifyBigInts(proof);
publicSignals = unstringifyBigInts(publicSignals);
const curve = await curves.getCurveFromName(vk_verifier.curve);
const Fr = curve.Fr;
const G1 = curve.G1;
proof = fromObjectProof(curve,proof);
vk_verifier = fromObjectVk(curve, vk_verifier);
if (!isWellConstructed(curve, proof)) {
logger.error("Proof is not well constructed");
return false;
}
const challanges = calculateChallanges(curve, proof);
if (logger) {
logger.debug("beta: " + Fr.toString(challanges.beta));
logger.debug("gamma: " + Fr.toString(challanges.gamma));
logger.debug("alpha: " + Fr.toString(challanges.alpha));
logger.debug("xi: " + Fr.toString(challanges.xi));
logger.debug("v: " + Fr.toString(challanges.v));
logger.debug("u: " + Fr.toString(challanges.u));
}
const L = calculateLagrangeEvaluations(curve, challanges, vk_verifier);
if (logger) {
logger.debug("Lagrange Evaluations: ");
for (let i=1; i<L.length; i++) {
logger.debug(`L${i}(xi)=` + Fr.toString(L[i]));
}
}
if (publicSignals.length != vk_verifier.nPublic) {
logger.error("Number of public signals does not match with vk");
return false;
}
const pl = calculatePl(curve, publicSignals, L);
if (logger) {
logger.debug("Pl: " + Fr.toString(pl));
}
const t = calculateT(curve, proof, challanges, pl, L[1]);
if (logger) {
logger.debug("t: " + Fr.toString(t));
}
const D = calculateD(curve, proof, challanges, vk_verifier, L[1]);
if (logger) {
logger.debug("D: " + G1.toString(D));
}
const F = calculateF(curve, proof, challanges, vk_verifier, D);
if (logger) {
logger.debug("F: " + G1.toString(F));
}
const E = calculateE(curve, proof, challanges, vk_verifier, t);
if (logger) {
logger.debug("E: " + G1.toString(E));
}
const res = await isValidPairing(curve, proof, challanges, vk_verifier, E, F);
if (logger) {
if (res) {
logger.info("OK!");
} else {
logger.warn("Invalid Proof");
}
}
return res;
}
function fromObjectProof(curve, proof) {
const G1 = curve.G1;
const Fr = curve.Fr;
const res = {};
res.A = G1.fromObject(proof.A);
res.B = G1.fromObject(proof.B);
res.C = G1.fromObject(proof.C);
res.Z = G1.fromObject(proof.Z);
res.T1 = G1.fromObject(proof.T1);
res.T2 = G1.fromObject(proof.T2);
res.T3 = G1.fromObject(proof.T3);
res.eval_a = Fr.fromObject(proof.eval_a);
res.eval_b = Fr.fromObject(proof.eval_b);
res.eval_c = Fr.fromObject(proof.eval_c);
res.eval_zw = Fr.fromObject(proof.eval_zw);
res.eval_s1 = Fr.fromObject(proof.eval_s1);
res.eval_s2 = Fr.fromObject(proof.eval_s2);
res.eval_t = Fr.fromObject(proof.eval_t);
res.eval_r = Fr.fromObject(proof.eval_r);
res.Wxi = G1.fromObject(proof.Wxi);
res.Wxiw = G1.fromObject(proof.Wxiw);
return res;
}
function fromObjectVk(curve, vk) {
const G1 = curve.G1;
const G2 = curve.G2;
const Fr = curve.Fr;
const res = vk;
res.Qm = G1.fromObject(vk.Qm);
res.Ql = G1.fromObject(vk.Ql);
res.Qr = G1.fromObject(vk.Qr);
res.Qo = G1.fromObject(vk.Qo);
res.Qc = G1.fromObject(vk.Qc);
res.S1 = G1.fromObject(vk.S1);
res.S2 = G1.fromObject(vk.S2);
res.S3 = G1.fromObject(vk.S3);
res.k1 = Fr.fromObject(vk.k1);
res.k2 = Fr.fromObject(vk.k2);
res.X_2 = G2.fromObject(vk.X_2);
return res;
}
function isWellConstructed(curve, proof) {
const G1 = curve.G1;
if (!G1.isValid(proof.A)) return false;
if (!G1.isValid(proof.B)) return false;
if (!G1.isValid(proof.C)) return false;
if (!G1.isValid(proof.Z)) return false;
if (!G1.isValid(proof.T1)) return false;
if (!G1.isValid(proof.T2)) return false;
if (!G1.isValid(proof.T3)) return false;
if (!G1.isValid(proof.Wxi)) return false;
if (!G1.isValid(proof.Wxiw)) return false;
return true;
}
function calculateChallanges(curve, proof) {
const G1 = curve.G1;
const Fr = curve.Fr;
const n8r = curve.Fr.n8;
const res = {};
const transcript1 = new Uint8Array(G1.F.n8*2*3);
G1.toRprUncompressed(transcript1, 0, proof.A);
G1.toRprUncompressed(transcript1, G1.F.n8*2, proof.B);
G1.toRprUncompressed(transcript1, G1.F.n8*4, proof.C);
res.beta = hashToFr(curve, transcript1);
const transcript2 = new Uint8Array(n8r);
Fr.toRprBE(transcript2, 0, res.beta);
res.gamma = hashToFr(curve, transcript2);
const transcript3 = new Uint8Array(G1.F.n8*2);
G1.toRprUncompressed(transcript3, 0, proof.Z);
res.alpha = hashToFr(curve, transcript3);
console.log("Alpha: ", res.alpha);
const transcript4 = new Uint8Array(G1.F.n8*2*3);
G1.toRprUncompressed(transcript4, 0, proof.T1);
G1.toRprUncompressed(transcript4, G1.F.n8*2, proof.T2);
G1.toRprUncompressed(transcript4, G1.F.n8*4, proof.T3);
res.xi = hashToFr(curve, transcript4);
const transcript5 = new Uint8Array(n8r*8);
Fr.toRprBE(transcript5, 0, proof.eval_a);
Fr.toRprBE(transcript5, n8r, proof.eval_b);
Fr.toRprBE(transcript5, n8r*2, proof.eval_c);
Fr.toRprBE(transcript5, n8r*3, proof.eval_s1);
Fr.toRprBE(transcript5, n8r*4, proof.eval_s2);
Fr.toRprBE(transcript5, n8r*5, proof.eval_zw);
Fr.toRprBE(transcript5, n8r*6, proof.eval_t);
Fr.toRprBE(transcript5, n8r*7, proof.eval_r);
res.v = [];
res.v[1] = hashToFr(curve, transcript5);
for (let i=2; i<=6; i++ ) res.v[i] = Fr.mul(res.v[i-1], res.v[1]);
const transcript6 = new Uint8Array(G1.F.n8*2*2);
G1.toRprUncompressed(transcript6, 0, proof.Wxi);
G1.toRprUncompressed(transcript6, G1.F.n8*2, proof.Wxiw);
res.u = hashToFr(curve, transcript6);
// TODO: remove
// res.v[1] = Fr.zero;
// res.v[2] = Fr.zero;
// res.v[3] = Fr.zero;
// res.v[4] = Fr.zero;
// res.v[5] = Fr.zero;
// res.v[6] = Fr.zero;
// res.u = Fr.zero;
return res;
}
function calculateLagrangeEvaluations(curve, challanges, vk) {
const Fr = curve.Fr;
let xin = challanges.xi;
let domainSize = 1;
for (let i=0; i<vk.power; i++) {
xin = Fr.square(xin);
domainSize *= 2;
}
challanges.xin = xin;
challanges.zh = Fr.sub(xin, Fr.one);
const L = [];
const n = Fr.e(domainSize);
let w = Fr.one;
for (let i=1; i<=Math.max(1, vk.nPublic); i++) {
L[i] = Fr.div(Fr.mul(w, challanges.zh), Fr.mul(n, Fr.sub(challanges.xi, w)));
w = Fr.mul(w, Fr.w[vk.power]);
}
return L;
}
function hashToFr(curve, transcript) {
const v = Scalar.fromRprBE(new Uint8Array(keccak256.arrayBuffer(transcript)));
return curve.Fr.e(v);
}
function calculatePl(curve, publicSignals, L) {
const Fr = curve.Fr;
let pl = Fr.zero;
for (let i=0; i<publicSignals.length; i++) {
const w = Fr.e(publicSignals[i]);
pl = Fr.sub(pl, Fr.mul(w, L[i+1]));
}
return pl;
}
function calculateT(curve, proof, challanges, pl, l1) {
const Fr = curve.Fr;
let num = proof.eval_r;
num = Fr.add(num, pl);
let e1 = proof.eval_a;
e1 = Fr.add(e1, Fr.mul(challanges.beta, proof.eval_s1));
e1 = Fr.add(e1, challanges.gamma);
let e2 = proof.eval_b;
e2 = Fr.add(e2, Fr.mul(challanges.beta, proof.eval_s2));
e2 = Fr.add(e2, challanges.gamma);
let e3 = proof.eval_c;
e3 = Fr.add(e3, challanges.gamma);
let e = Fr.mul(Fr.mul(e1, e2), e3);
e = Fr.mul(e, proof.eval_zw);
e = Fr.mul(e, challanges.alpha);
num = Fr.sub(num, e);
num = Fr.sub(num, Fr.mul(l1, Fr.square(challanges.alpha)));
const t = Fr.div(num, challanges.zh);
if (!Fr.eq(t, proof.eval_t)) {
console.log("t DOES NOT MATCH");
} else {
console.log("t OK");
}
console.log(Fr.toString(proof.eval_t));
console.log(Fr.toString(t));
return t;
}
function calculateD(curve, proof, challanges, vk, l1) {
const G1 = curve.G1;
const Fr = curve.Fr;
let s1 = Fr.mul(Fr.mul(proof.eval_a, proof.eval_b), challanges.v[1]);
let res = G1.timesFr(vk.Qm, s1);
let s2 = Fr.mul(proof.eval_a, challanges.v[1]);
res = G1.add(res, G1.timesFr(vk.Ql, s2));
let s3 = Fr.mul(proof.eval_b, challanges.v[1]);
res = G1.add(res, G1.timesFr(vk.Qr, s3));
let s4 = Fr.mul(proof.eval_c, challanges.v[1]);
res = G1.add(res, G1.timesFr(vk.Qo, s4));
res = G1.add(res, G1.timesFr(vk.Qc, challanges.v[1]));
const betaxi = Fr.mul(challanges.beta, challanges.xi);
let s6a = proof.eval_a;
s6a = Fr.add(s6a, betaxi);
s6a = Fr.add(s6a, challanges.gamma);
let s6b = proof.eval_b;
s6b = Fr.add(s6b, Fr.mul(betaxi, vk.k1));
s6b = Fr.add(s6b, challanges.gamma);
let s6c = proof.eval_c;
s6c = Fr.add(s6c, Fr.mul(betaxi, vk.k2));
s6c = Fr.add(s6c, challanges.gamma);
let s6 = Fr.mul(Fr.mul(s6a, s6b), s6c);
s6 = Fr.mul(s6, Fr.mul(challanges.alpha, challanges.v[1]));
let s6d = Fr.mul(Fr.mul(l1, Fr.square(challanges.alpha)), challanges.v[1]);
s6 = Fr.add(s6, s6d);
s6 = Fr.add(s6, challanges.u);
res = G1.add(res, G1.timesFr(proof.Z, s6));
let s7a = proof.eval_a;
s7a = Fr.add(s7a, Fr.mul(challanges.beta, proof.eval_s1));
s7a = Fr.add(s7a, challanges.gamma);
let s7b = proof.eval_b;
s7b = Fr.add(s7b, Fr.mul(challanges.beta, proof.eval_s2));
s7b = Fr.add(s7b, challanges.gamma);
let s7 = Fr.mul(s7a, s7b);
s7 = Fr.mul(s7, challanges.alpha);
s7 = Fr.mul(s7, challanges.v[1]);
s7 = Fr.mul(s7, challanges.beta);
s7 = Fr.mul(s7, proof.eval_zw);
res = G1.sub(res, G1.timesFr(vk.S3, s7));
return res;
}
function calculateF(curve, proof, challanges, vk, D) {
const G1 = curve.G1;
const Fr = curve.Fr;
let res = proof.T1;
res = G1.add(res, G1.timesFr(proof.T2, challanges.xin));
res = G1.add(res, G1.timesFr(proof.T3, Fr.square(challanges.xin)));
res = G1.add(res, D);
res = G1.add(res, G1.timesFr(proof.A, challanges.v[2]));
res = G1.add(res, G1.timesFr(proof.B, challanges.v[3]));
res = G1.add(res, G1.timesFr(proof.C, challanges.v[4]));
res = G1.add(res, G1.timesFr(vk.S1, challanges.v[5]));
res = G1.add(res, G1.timesFr(vk.S2, challanges.v[6]));
return res;
}
function calculateE(curve, proof, challanges, vk, t) {
const G1 = curve.G1;
const Fr = curve.Fr;
let s = t;
s = Fr.add(s, Fr.mul(challanges.v[1], proof.eval_r));
s = Fr.add(s, Fr.mul(challanges.v[2], proof.eval_a));
s = Fr.add(s, Fr.mul(challanges.v[3], proof.eval_b));
s = Fr.add(s, Fr.mul(challanges.v[4], proof.eval_c));
s = Fr.add(s, Fr.mul(challanges.v[5], proof.eval_s1));
s = Fr.add(s, Fr.mul(challanges.v[6], proof.eval_s2));
s = Fr.add(s, Fr.mul(challanges.u, proof.eval_zw));
const res = G1.timesFr(G1.one, s);
return res;
}
async function isValidPairing(curve, proof, challanges, vk, E, F) {
const G1 = curve.G1;
const Fr = curve.Fr;
let A1 = proof.Wxi;
A1 = G1.add(A1, G1.timesFr(proof.Wxiw, challanges.u));
let B1 = G1.timesFr(proof.Wxi, challanges.xi);
const s = Fr.mul(Fr.mul(challanges.u, challanges.xi), Fr.w[vk.power]);
B1 = G1.add(B1, G1.timesFr(proof.Wxiw, s));
B1 = G1.add(B1, F);
B1 = G1.sub(B1, E);
const res = await curve.pairingEq(
G1.neg(A1) , vk.X_2,
B1 , curve.G2.one
);
return res;
}

@ -31,7 +31,12 @@ export default async function beacon(zkeyNameOld, zkeyNameNew, name, beaconHashS
const {fd: fdOld, sections: sections} = await binFileUtils.readBinFile(zkeyNameOld, "zkey", 2);
const zkey = await zkeyUtils.readHeader(fdOld, sections, "groth16");
const zkey = await zkeyUtils.readHeader(fdOld, sections);
if (zkey.protocol != "groth16") {
throw new Error("zkey file is not groth16");
}
const curve = await getCurve(zkey.q);

@ -12,7 +12,10 @@ export default async function phase2contribute(zkeyNameOld, zkeyNameNew, name, e
await Blake2b.ready();
const {fd: fdOld, sections: sections} = await binFileUtils.readBinFile(zkeyNameOld, "zkey", 2);
const zkey = await zkeyUtils.readHeader(fdOld, sections, "groth16");
const zkey = await zkeyUtils.readHeader(fdOld, sections);
if (zkey.protocol != "groth16") {
throw new Error("zkey file is not groth16");
}
const curve = await getCurve(zkey.q);

@ -7,7 +7,10 @@ import { getCurveFromQ as getCurve } from "./curves.js";
export default async function phase2exportMPCParams(zkeyName, mpcparamsName, logger) {
const {fd: fdZKey, sections: sectionsZKey} = await binFileUtils.readBinFile(zkeyName, "zkey", 2);
const zkey = await zkeyUtils.readHeader(fdZKey, sectionsZKey, "groth16");
const zkey = await zkeyUtils.readHeader(fdZKey, sectionsZKey);
if (zkey.protocol != "groth16") {
throw new Error("zkey file is not groth16");
}
const curve = await getCurve(zkey.q);
const sG1 = curve.G1.F.n8*2;
@ -133,4 +136,4 @@ export default async function phase2exportMPCParams(zkeyName, mpcparamsName, log
};
}

@ -1,6 +1,6 @@
import { readZKey as readZKey } from "./zkey_utils.js";
export default async function zkeyExportJson(zkeyFileName, verbose) {
export default async function zkeyExportJson(zkeyFileName) {
const zKey = await readZKey(zkeyFileName, true);

@ -5,11 +5,27 @@ import { utils } from "ffjavascript";
const {stringifyBigInts} = utils;
export default async function zkeyExportVerificationKey(zkeyName, logger) {
export default async function zkeyExportVerificationKey(zkeyName, /* logger */ ) {
const {fd, sections} = await binFileUtils.readBinFile(zkeyName, "zkey", 2);
const zkey = await zkeyUtils.readHeader(fd, sections, "groth16");
const zkey = await zkeyUtils.readHeader(fd, sections);
let res;
if (zkey.protocol == "groth16") {
res = await groth16Vk(zkey, fd, sections);
} else if (zkey.protocol == "plonk") {
res = await plonkVk(zkey);
} else {
throw new Error("zkey file is not groth16");
}
await fd.close();
return res;
}
async function groth16Vk(zkey, fd, sections) {
const curve = await getCurve(zkey.q);
const sG1 = curve.G1.F.n8*2;
@ -42,7 +58,35 @@ export default async function zkeyExportVerificationKey(zkeyName, logger) {
vKey = stringifyBigInts(vKey);
await fd.close();
return vKey;
}
async function plonkVk(zkey) {
const curve = await getCurve(zkey.q);
let vKey = {
protocol: zkey.protocol,
curve: curve.name,
nPublic: zkey.nPublic,
power: zkey.power,
k1: curve.Fr.toObject(zkey.k1),
k2: curve.Fr.toObject(zkey.k2),
Qm: curve.G1.toObject(zkey.Qm),
Ql: curve.G1.toObject(zkey.Ql),
Qr: curve.G1.toObject(zkey.Qr),
Qo: curve.G1.toObject(zkey.Qo),
Qc: curve.G1.toObject(zkey.Qc),
S1: curve.G1.toObject(zkey.S1),
S2: curve.G1.toObject(zkey.S2),
S3: curve.G1.toObject(zkey.S3),
X_2: curve.G2.toObject(zkey.X_2)
};
vKey = stringifyBigInts(vKey);
return vKey;
}

@ -185,20 +185,30 @@ async function readG2(fd, curve, toObject) {
}
export async function readHeader(fd, sections, protocol, toObject) {
if (protocol != "groth16") throw new Error("Protocol not supported: "+protocol);
const zkey = {};
export async function readHeader(fd, sections, toObject) {
// Read Header
/////////////////////
await binFileUtils.startReadUniqueSection(fd, sections, 1);
const protocolId = await fd.readULE32();
if (protocolId != 1) throw new Error("File is not groth");
zkey.protocol = "groth16";
await binFileUtils.endReadSection(fd);
if (protocolId == 1) {
return await readHeaderGroth16(fd, sections, toObject);
} else if (protocolId == 2) {
return await readHeaderPlonk(fd, sections, toObject);
} else {
throw new Error("Protocol not supported: ");
}
}
async function readHeaderGroth16(fd, sections, toObject) {
const zkey = {};
zkey.protocol = "groth16";
// Read Groth Header
/////////////////////
await binFileUtils.startReadUniqueSection(fd, sections, 2);
@ -228,6 +238,57 @@ export async function readHeader(fd, sections, protocol, toObject) {
}
async function readHeaderPlonk(fd, sections, protocol, toObject) {
const zkey = {};
zkey.protocol = "plonk";
// Read Plonk Header
/////////////////////
await binFileUtils.startReadUniqueSection(fd, sections, 2);
const n8q = await fd.readULE32();
zkey.n8q = n8q;
zkey.q = await binFileUtils.readBigInt(fd, n8q);
const n8r = await fd.readULE32();
zkey.n8r = n8r;
zkey.r = await binFileUtils.readBigInt(fd, n8r);
let curve = await getCurve(zkey.q);
zkey.nVars = await fd.readULE32();
zkey.nPublic = await fd.readULE32();
zkey.domainSize = await fd.readULE32();
zkey.power = log2(zkey.domainSize);
zkey.nAdditions = await fd.readULE32();
zkey.nConstrains = await fd.readULE32();
zkey.k1 = await fd.read(n8r);
zkey.k2 = await fd.read(n8r);
zkey.X = await readG1(fd, curve, toObject);
zkey.Xto2 = await readG1(fd, curve, toObject);
zkey.XtoM = await readG1(fd, curve, toObject);
zkey.XtoMplus1 = await readG1(fd, curve, toObject);
zkey.XtoMplus2 = await readG1(fd, curve, toObject);
zkey.Qm = await readG1(fd, curve, toObject);
zkey.Ql = await readG1(fd, curve, toObject);
zkey.Qr = await readG1(fd, curve, toObject);
zkey.Qo = await readG1(fd, curve, toObject);
zkey.Qc = await readG1(fd, curve, toObject);
zkey.S1 = await readG1(fd, curve, toObject);
zkey.S2 = await readG1(fd, curve, toObject);
zkey.S3 = await readG1(fd, curve, toObject);
zkey.X_2 = await readG2(fd, curve, toObject);
await binFileUtils.endReadSection(fd);
return zkey;
}
export async function readZKey(fileName, toObject) {
const {fd, sections} = await binFileUtils.readBinFile(fileName, "zkey", 1);
@ -330,7 +391,7 @@ export async function readZKey(fileName, toObject) {
return zkey;
async function readFr2(toObject) {
async function readFr2(/* toObject */) {
const n = await binFileUtils.readBigInt(fd, zkey.n8r);
return Fr.mul(n, Rri2);
}

@ -6,7 +6,6 @@ import * as misc from "./misc.js";
import { hashToG2 as hashToG2 } from "./keypair.js";
const sameRatio = misc.sameRatio;
import crypto from "crypto";
import newZKey from "./zkey_new.js";
import {hashG1, hashPubKey} from "./zkey_utils.js";
import { Scalar, ChaCha, BigBuffer } from "ffjavascript";
@ -18,11 +17,13 @@ export default async function phase2verifyFromInit(initFileName, pTauFileName, z
await Blake2b.ready();
const {fd, sections} = await binFileUtils.readBinFile(zkeyFileName, "zkey", 2);
const zkey = await zkeyUtils.readHeader(fd, sections, "groth16");
const zkey = await zkeyUtils.readHeader(fd, sections);
if (zkey.protocol != "groth16") {
throw new Error("zkey file is not groth16");
}
const curve = await getCurve(zkey.q);
const sG1 = curve.G1.F.n8*2;
const sG2 = curve.G2.F.n8*2;
const mpcParams = await zkeyUtils.readMPCParams(fd, curve, sections);

Binary file not shown.

Binary file not shown.

@ -0,0 +1,17 @@
template TestPlonk() {
signal private input a;
signal private input b;
signal output c;
signal i1;
signal i2;
signal i4;
i1 <== a+b+3;
i2 <== i1*i1;
i4 <== i2*i2;
c <== i1*i4;
}
component main = TestPlonk();

Binary file not shown.

@ -0,0 +1,6 @@
1,2,0,main.a
2,3,0,main.b
3,1,0,main.c
4,4,0,main.i1
5,5,0,main.i2
6,6,0,main.i4

Binary file not shown.

Binary file not shown.

@ -0,0 +1 @@
{"a":1, "b":2}

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,56 @@
{
"A": [
"15743209584538732831984522947865045943191698736126835167107429821444692635700",
"15304499734695470270217999401312954213615077608391938626547048969059391815770",
"1"
],
"B": [
"3011437941647082010337336879820112189520787585056958119182126144516922325699",
"14293021774310121677905381158509930628442641616993815871854561496752240048132",
"1"
],
"C": [
"17023913191405186346489422835812580810015512803033000441904349097055703180515",
"11228876974248468319926028828930973923072635393190630092554386478523728646602",
"1"
],
"Z": [
"21787485149860139414155053903444419266452989172629957435807448984274998525743",
"2537376736563984101921166856341942675021784850064711527768725712244516210693",
"1"
],
"T1": [
"9982560943557310174756809994489647193274022193173772342571072376360853826159",
"2193374861650642053413940227444385900509799625539732490553578296834818806014",
"1"
],
"T2": [
"1935051902238074558682471125149626614935744241552612922092977226651069265819",
"15728245714470497509947599396234528395359156386509927514699449257668858437022",
"1"
],
"T3": [
"18565050802152066143639524874000298798967497331659460012952465075688981418065",
"18124252316135049195270348857308050282660839058495893905635826459440493046467",
"1"
],
"eval_a": "18560625457154932877354134783576059795528977185011370741206750467310685514420",
"eval_b": "2968882576386486226736180104429680319564132166278962733938101279863400669397",
"eval_c": "14065677607789115870968882944412413440929947283607968973583982627560459103752",
"eval_s1": "17673322793248695230976965373575045530576580704252056363216368719625468122334",
"eval_s2": "831541334231628590558915969572942800627020663197326186193303197959043192699",
"eval_t": "8179926370148644196892921681282394306069033457557483827929857756544255424016",
"eval_zw": "7342206155372820933732681194284651788067168695182187676476085111006912772156",
"eval_r": "11261053049659093980937042550692833529286331232997878682705148472953954402090",
"Wxi": [
"6125149678611859223282479698302269532216717966631378380250981750098717210374",
"13207465019087327267137821712205429411984497896597119936649478058261276282566",
"1"
],
"Wxiw": [
"13714076431412195084554498803436795524319005402640663193692974043979445012470",
"4405441109329384737176946029242914012963938939164670332792981962524535185412",
"1"
],
"protocol": "plonk"
}

@ -0,0 +1,3 @@
[
"7776"
]

@ -0,0 +1,62 @@
{
"protocol": "plonk",
"curve": "bn128",
"nPublic": 1,
"power": 3,
"k1": "2",
"k2": "3",
"Qm": [
"17168609912386190999462867062592844491706144727020886602745470454037632312807",
"4824893185019084254715538827477609000309081525483856078561591934996168045584",
"1"
],
"Ql": [
"15450133393628414971876261621748138496338605666826023846615859108791492332989",
"1184681025187442459325266097207314011772232923873671071468501257625715473389",
"1"
],
"Qr": [
"16752106819266013412663211551583756351765466845186589867767132691506607495492",
"14299683507274194476312714964546495843580088611903841363880636607454602216010",
"1"
],
"Qo": [
"7984966467262973095497162697022203877199031573073309221047701766279201883804",
"18404871471444263234086942561943030263846986803222776461621204864269731897231",
"1"
],
"Qc": [
"16135959299033384762614902229352182078283157726424477475533552688440953520084",
"349204288781237561964913896195664733659754516098184691353357621586316325634",
"1"
],
"S1": [
"1910989172493900079102803565308322125332203370089591628568145817380913373724",
"9337220904228568088603689620174503369357413191014667225917702935803917493067",
"1"
],
"S2": [
"371255004847147127096466409492899314753458794130581205813923179247808061393",
"2961742946983985442978214878084168395967577369032613662121930483289108288759",
"1"
],
"S3": [
"12610570891683582603162481901590486845060916419456929897556794111704166680394",
"886409796777648048370939111979503186274941933388434245426850564817301004445",
"1"
],
"X_2": [
[
"18029695676650738226693292988307914797657423701064905010927197838374790804409",
"14583779054894525174450323658765874724019480979794335525732096752006891875705"
],
[
"2140229616977736810657479771656733941598412651537078903776637920509952744750",
"11474861747383700316476719153975578001603231366361248090558603872215261634898"
],
[
"1",
"0"
]
]
}

Binary file not shown.