Plonk working
This commit is contained in:
parent
2ce529beba
commit
7ed350002d
70
.vscode/launch.json
vendored
Normal file
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
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
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
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
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
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
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
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.
17
test/plonk_circuit/circuit.circom
Normal file
17
test/plonk_circuit/circuit.circom
Normal file
@ -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();
|
BIN
test/plonk_circuit/circuit.r1cs
Normal file
BIN
test/plonk_circuit/circuit.r1cs
Normal file
Binary file not shown.
6
test/plonk_circuit/circuit.sym
Normal file
6
test/plonk_circuit/circuit.sym
Normal file
@ -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
|
BIN
test/plonk_circuit/circuit.wasm
Normal file
BIN
test/plonk_circuit/circuit.wasm
Normal file
Binary file not shown.
BIN
test/plonk_circuit/circuit.zkey
Normal file
BIN
test/plonk_circuit/circuit.zkey
Normal file
Binary file not shown.
1
test/plonk_circuit/input.json
Normal file
1
test/plonk_circuit/input.json
Normal file
@ -0,0 +1 @@
|
||||
{"a":1, "b":2}
|
BIN
test/plonk_circuit/powersOfTau15_0000.ptau
Normal file
BIN
test/plonk_circuit/powersOfTau15_0000.ptau
Normal file
Binary file not shown.
BIN
test/plonk_circuit/powersOfTau15_0001.ptau
Normal file
BIN
test/plonk_circuit/powersOfTau15_0001.ptau
Normal file
Binary file not shown.
BIN
test/plonk_circuit/powersOfTau15_final.ptau
Normal file
BIN
test/plonk_circuit/powersOfTau15_final.ptau
Normal file
Binary file not shown.
56
test/plonk_circuit/proof.json
Normal file
56
test/plonk_circuit/proof.json
Normal file
@ -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"
|
||||
}
|
3
test/plonk_circuit/public.json
Normal file
3
test/plonk_circuit/public.json
Normal file
@ -0,0 +1,3 @@
|
||||
[
|
||||
"7776"
|
||||
]
|
62
test/plonk_circuit/verification_key.json
Normal file
62
test/plonk_circuit/verification_key.json
Normal file
@ -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"
|
||||
]
|
||||
]
|
||||
}
|
BIN
test/plonk_circuit/witness.wtns
Normal file
BIN
test/plonk_circuit/witness.wtns
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user