@zk-email/snarkjs
Version:
zkSNARKs implementation in JavaScript with chunked zkeys
856 lines (663 loc) • 29.9 kB
JavaScript
/*
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 A4, B4, C4, Z4;
let pol_a,pol_b,pol_c, pol_z, pol_t, pol_r;
let proof = {};
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, 14);
const ch = {};
await round1();
await round2();
await round3();
await round4();
await round5();
///////////////////////
// Final adjustments //
///////////////////////
proof.protocol = "plonk";
proof.curve = curve.name;
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(proof.A);
proof.B = G1.toObject(proof.B);
proof.C = G1.toObject(proof.C);
proof.Z = G1.toObject(proof.Z);
proof.T1 = G1.toObject(proof.T1);
proof.T2 = G1.toObject(proof.T2);
proof.T3 = G1.toObject(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(proof.Wxi);
proof.Wxiw = G1.toObject(proof.Wxiw);
delete proof.eval_t;
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() {
let A = new BigBuffer(zkey.domainSize * n8r);
let B = new BigBuffer(zkey.domainSize * n8r);
let 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);
}
A = await Fr.batchToMontgomery(A);
B = await Fr.batchToMontgomery(B);
C = await Fr.batchToMontgomery(C);
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() {
ch.b = [];
for (let i=1; i<=9; i++) {
ch.b[i] = curve.Fr.random();
}
[A, B, C] = await buildABC();
[pol_a, A4] = await to4T(A, [ch.b[2], ch.b[1]]);
[pol_b, B4] = await to4T(B, [ch.b[4], ch.b[3]]);
[pol_c, C4] = await to4T(C, [ch.b[6], ch.b[5]]);
proof.A = await expTau(pol_a, "multiexp A");
proof.B = await expTau(pol_b, "multiexp B");
proof.C = await expTau(pol_c, "multiexp C");
}
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);
ch.beta = hashToFr(transcript1);
if (logger) logger.debug("beta: " + Fr.toString(ch.beta));
const transcript2 = new Uint8Array(n8r);
Fr.toRprBE(transcript2, 0, ch.beta);
ch.gamma = hashToFr(transcript2);
if (logger) logger.debug("gamma: " + Fr.toString(ch.gamma));
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(ch.beta, w) );
n1 = Fr.add( n1, ch.gamma );
let n2 = B.slice(i*n8r, (i+1)*n8r);
n2 = Fr.add( n2, Fr.mul(zkey.k1, Fr.mul(ch.beta, w) ));
n2 = Fr.add( n2, ch.gamma );
let n3 = C.slice(i*n8r, (i+1)*n8r);
n3 = Fr.add( n3, Fr.mul(zkey.k2, Fr.mul(ch.beta, w) ));
n3 = Fr.add( n3, ch.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) , ch.beta));
d1 = Fr.add(d1, ch.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) , ch.beta));
d2 = Fr.add(d2, ch.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) , ch.beta));
d3 = Fr.add(d3, ch.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;
[pol_z, Z4] = await to4T(Z, [ch.b[9], ch.b[8], ch.b[7]]);
proof.Z = await expTau( pol_z, "multiexp Z");
}
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("]");
}
*/
if (logger) logger.debug("phse3: Reading QM4");
const QM4 = new BigBuffer(zkey.domainSize*4*n8r);
await fdZKey.readToBuffer(QM4, 0 , zkey.domainSize*n8r*4, sectionsZKey[7][0].p + zkey.domainSize*n8r);
if (logger) logger.debug("phse3: Reading QL4");
const QL4 = new BigBuffer(zkey.domainSize*4*n8r);
await fdZKey.readToBuffer(QL4, 0 , zkey.domainSize*n8r*4, sectionsZKey[8][0].p + zkey.domainSize*n8r);
if (logger) logger.debug("phse3: Reading QR4");
const QR4 = new BigBuffer(zkey.domainSize*4*n8r);
await fdZKey.readToBuffer(QR4, 0 , zkey.domainSize*n8r*4, sectionsZKey[9][0].p + zkey.domainSize*n8r);
if (logger) logger.debug("phse3: Reading QO4");
const QO4 = new BigBuffer(zkey.domainSize*4*n8r);
await fdZKey.readToBuffer(QO4, 0 , zkey.domainSize*n8r*4, sectionsZKey[10][0].p + zkey.domainSize*n8r);
if (logger) logger.debug("phse3: Reading QC4");
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);
ch.alpha = hashToFr(transcript3);
if (logger) logger.debug("alpha: " + Fr.toString(ch.alpha));
const Z1 = [
Fr.zero,
Fr.add(Fr.e(-1), Fr.w[2]),
Fr.e(-2),
Fr.sub(Fr.e(-1), Fr.w[2]),
];
const Z2 = [
Fr.zero,
Fr.add(Fr.zero, Fr.mul(Fr.e(-2), Fr.w[2])),
Fr.e(4),
Fr.sub(Fr.zero, Fr.mul(Fr.e(-2), Fr.w[2])),
];
const Z3 = [
Fr.zero,
Fr.add(Fr.e(2), Fr.mul(Fr.e(2), Fr.w[2])),
Fr.e(-8),
Fr.sub(Fr.e(2), Fr.mul(Fr.e(2), Fr.w[2])),
];
const T = new BigBuffer(zkey.domainSize*4*n8r);
const Tz = new BigBuffer(zkey.domainSize*4*n8r);
let w = Fr.one;
for (let i=0; i<zkey.domainSize*4; i++) {
if ((i%4096 == 0)&&(logger)) logger.debug(`calculating t ${i}/${zkey.domainSize*4}`);
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);
const ap = Fr.add(ch.b[2], Fr.mul(ch.b[1], w));
const bp = Fr.add(ch.b[4], Fr.mul(ch.b[3], w));
const cp = Fr.add(ch.b[6], Fr.mul(ch.b[5], w));
const w2 = Fr.square(w);
const zp = Fr.add(Fr.add(Fr.mul(ch.b[7], w2), Fr.mul(ch.b[8], w)), ch.b[9]);
const wW = Fr.mul(w, Fr.w[zkey.power]);
const wW2 = Fr.square(wW);
const zWp = Fr.add(Fr.add(Fr.mul(ch.b[7], wW2), Fr.mul(ch.b[8], wW)), ch.b[9]);
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, e1z] = mul2(a, b, ap, bp, i%4);
e1 = Fr.mul(e1, qm);
e1z = Fr.mul(e1z, qm);
e1 = Fr.add(e1, Fr.mul(a, ql));
e1z = Fr.add(e1z, Fr.mul(ap, ql));
e1 = Fr.add(e1, Fr.mul(b, qr));
e1z = Fr.add(e1z, Fr.mul(bp, qr));
e1 = Fr.add(e1, Fr.mul(c, qo));
e1z = Fr.add(e1z, Fr.mul(cp, qo));
e1 = Fr.add(e1, pl);
e1 = Fr.add(e1, qc);
const betaw = Fr.mul(ch.beta, w);
let e2a =a;
e2a = Fr.add(e2a, betaw);
e2a = Fr.add(e2a, ch.gamma);
let e2b =b;
e2b = Fr.add(e2b, Fr.mul(betaw, zkey.k1));
e2b = Fr.add(e2b, ch.gamma);
let e2c =c;
e2c = Fr.add(e2c, Fr.mul(betaw, zkey.k2));
e2c = Fr.add(e2c, ch.gamma);
let e2d = z;
let [e2, e2z] = mul4(e2a, e2b, e2c, e2d, ap, bp, cp, zp, i%4);
e2 = Fr.mul(e2, ch.alpha);
e2z = Fr.mul(e2z, ch.alpha);
let e3a = a;
e3a = Fr.add(e3a, Fr.mul(ch.beta, s1));
e3a = Fr.add(e3a, ch.gamma);
let e3b = b;
e3b = Fr.add(e3b, Fr.mul(ch.beta,s2));
e3b = Fr.add(e3b, ch.gamma);
let e3c = c;
e3c = Fr.add(e3c, Fr.mul(ch.beta,s3));
e3c = Fr.add(e3c, ch.gamma);
let e3d = zw;
let [e3, e3z] = mul4(e3a, e3b, e3c, e3d, ap, bp, cp, zWp, i%4);
e3 = Fr.mul(e3, ch.alpha);
e3z = Fr.mul(e3z, ch.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(ch.alpha, ch.alpha));
let e4z = Fr.mul(zp, lPols.slice( (zkey.domainSize + i)*n8r, (zkey.domainSize+i+1)*n8r));
e4z = Fr.mul(e4z, Fr.mul(ch.alpha, ch.alpha));
let e = Fr.add(Fr.sub(Fr.add(e1, e2), e3), e4);
let ez = Fr.add(Fr.sub(Fr.add(e1z, e2z), e3z), e4z);
T.set(e, i*n8r);
Tz.set(ez, i*n8r);
w = Fr.mul(w, Fr.w[zkey.power+2]);
}
if (logger) logger.debug("ifft T");
let t = await Fr.ifft(T);
if (logger) logger.debug("dividing T/Z");
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");
}
}
}
if (logger) logger.debug("ifft Tz");
const tz = await Fr.ifft(Tz);
for (let i=0; i<zkey.domainSize*4; i++) {
const a = tz.slice(i*n8r, (i+1)*n8r);
if (i > (zkey.domainSize*3 +5) ) {
if (!Fr.isZero(a)) {
throw new Error("Tz Polynomial is not well calculated");
}
} else {
t.set(
Fr.add(
t.slice(i*n8r, (i+1)*n8r),
a
),
i*n8r
);
}
}
pol_t = t.slice(0, (zkey.domainSize*3+6)*n8r);
proof.T1 = await expTau( t.slice(0, zkey.domainSize*n8r) , "multiexp T1");
proof.T2 = await expTau( t.slice(zkey.domainSize*n8r, zkey.domainSize*2*n8r) , "multiexp T2");
proof.T3 = await expTau( t.slice(zkey.domainSize*2*n8r, (zkey.domainSize*3+6)*n8r) , "multiexp T3");
function mul2(a,b, ap, bp, p) {
let r, rz;
const a_b = Fr.mul(a,b);
const a_bp = Fr.mul(a,bp);
const ap_b = Fr.mul(ap,b);
const ap_bp = Fr.mul(ap,bp);
r = a_b;
let a0 = Fr.add(a_bp, ap_b);
let a1 = ap_bp;
rz = a0;
if (p) {
rz = Fr.add(rz, Fr.mul(Z1[p], a1));
}
return [r, rz];
}
function mul4(a,b,c,d, ap, bp, cp, dp, p) {
let r, rz;
const a_b = Fr.mul(a,b);
const a_bp = Fr.mul(a,bp);
const ap_b = Fr.mul(ap,b);
const ap_bp = Fr.mul(ap,bp);
const c_d = Fr.mul(c,d);
const c_dp = Fr.mul(c,dp);
const cp_d = Fr.mul(cp,d);
const cp_dp = Fr.mul(cp,dp);
r = Fr.mul(a_b, c_d);
let a0 = Fr.mul(ap_b, c_d);
a0 = Fr.add(a0, Fr.mul(a_bp, c_d));
a0 = Fr.add(a0, Fr.mul(a_b, cp_d));
a0 = Fr.add(a0, Fr.mul(a_b, c_dp));
let a1 = Fr.mul(ap_bp, c_d);
a1 = Fr.add(a1, Fr.mul(ap_b, cp_d));
a1 = Fr.add(a1, Fr.mul(ap_b, c_dp));
a1 = Fr.add(a1, Fr.mul(a_bp, cp_d));
a1 = Fr.add(a1, Fr.mul(a_bp, c_dp));
a1 = Fr.add(a1, Fr.mul(a_b, cp_dp));
let a2 = Fr.mul(a_bp, cp_dp);
a2 = Fr.add(a2, Fr.mul(ap_b, cp_dp));
a2 = Fr.add(a2, Fr.mul(ap_bp, c_dp));
a2 = Fr.add(a2, Fr.mul(ap_bp, cp_d));
let a3 = Fr.mul(ap_bp, cp_dp);
rz = a0;
if (p) {
rz = Fr.add(rz, Fr.mul(Z1[p], a1));
rz = Fr.add(rz, Fr.mul(Z2[p], a2));
rz = Fr.add(rz, Fr.mul(Z3[p], a3));
}
return [r, rz];
}
}
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);
ch.xi = hashToFr(transcript4);
if (logger) logger.debug("xi: " + Fr.toString(ch.xi));
proof.eval_a = evalPol(pol_a, ch.xi);
proof.eval_b = evalPol(pol_b, ch.xi);
proof.eval_c = evalPol(pol_c, ch.xi);
proof.eval_s1 = evalPol(pol_s1, ch.xi);
proof.eval_s2 = evalPol(pol_s2, ch.xi);
proof.eval_t = evalPol(pol_t, ch.xi);
proof.eval_zw = evalPol(pol_z, Fr.mul(ch.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(ch.beta, ch.xi);
e2a = Fr.add( e2a, betaxi);
e2a = Fr.add( e2a, ch.gamma);
let e2b = proof.eval_b;
e2b = Fr.add( e2b, Fr.mul(betaxi, zkey.k1));
e2b = Fr.add( e2b, ch.gamma);
let e2c = proof.eval_c;
e2c = Fr.add( e2c, Fr.mul(betaxi, zkey.k2));
e2c = Fr.add( e2c, ch.gamma);
const e2 = Fr.mul(Fr.mul(Fr.mul(e2a, e2b), e2c), ch.alpha);
let e3a = proof.eval_a;
e3a = Fr.add( e3a, Fr.mul(ch.beta, proof.eval_s1));
e3a = Fr.add( e3a, ch.gamma);
let e3b = proof.eval_b;
e3b = Fr.add( e3b, Fr.mul(ch.beta, proof.eval_s2));
e3b = Fr.add( e3b, ch.gamma);
let e3 = Fr.mul(e3a, e3b);
e3 = Fr.mul(e3, ch.beta);
e3 = Fr.mul(e3, proof.eval_zw);
e3 = Fr.mul(e3, ch.alpha);
ch.xim= ch.xi;
for (let i=0; i<zkey.power; i++) ch.xim = Fr.mul(ch.xim, ch.xim);
const eval_l1 = Fr.div(
Fr.sub(ch.xim, Fr.one),
Fr.mul(Fr.sub(ch.xi, Fr.one), Fr.e(zkey.domainSize))
);
const e4 = Fr.mul(eval_l1, Fr.mul(ch.alpha, ch.alpha));
const coefs3 = e3;
const coefz = Fr.add(e2, e4);
pol_r = new BigBuffer((zkey.domainSize+3)*n8r);
for (let i = 0; i<zkey.domainSize+3; i++) {
let v = Fr.mul(coefz, pol_z.slice(i*n8r,(i+1)*n8r));
if (i<zkey.domainSize) {
v = Fr.add(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.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, ch.xi);
}
async function round5() {
const transcript5 = new Uint8Array(n8r*7);
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_r);
ch.v = [];
ch.v[1] = hashToFr(transcript5);
if (logger) logger.debug("v: " + Fr.toString(ch.v[1]));
for (let i=2; i<=6; i++ ) ch.v[i] = Fr.mul(ch.v[i-1], ch.v[1]);
let pol_wxi = new BigBuffer((zkey.domainSize+6)*n8r);
const xi2m = Fr.mul(ch.xim, ch.xim);
for (let i=0; i<zkey.domainSize+6; i++) {
let w = Fr.zero;
w = Fr.add(w, Fr.mul(xi2m, pol_t.slice( (zkey.domainSize*2+i)*n8r, (zkey.domainSize*2+i+1)*n8r )));
if (i<zkey.domainSize+3) {
w = Fr.add(w, Fr.mul(ch.v[1], pol_r.slice(i*n8r, (i+1)*n8r)));
}
if (i<zkey.domainSize+2) {
w = Fr.add(w, Fr.mul(ch.v[2], pol_a.slice(i*n8r, (i+1)*n8r)));
w = Fr.add(w, Fr.mul(ch.v[3], pol_b.slice(i*n8r, (i+1)*n8r)));
w = Fr.add(w, Fr.mul(ch.v[4], pol_c.slice(i*n8r, (i+1)*n8r)));
}
if (i<zkey.domainSize) {
w = Fr.add(w, pol_t.slice(i*n8r, (i+1)*n8r));
w = Fr.add(w, Fr.mul(ch.xim, pol_t.slice( (zkey.domainSize+i)*n8r, (zkey.domainSize+i+1)*n8r )));
w = Fr.add(w, Fr.mul(ch.v[5], pol_s1.slice(i*n8r, (i+1)*n8r)));
w = Fr.add(w, Fr.mul(ch.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(ch.v[1], proof.eval_r));
w0 = Fr.sub(w0, Fr.mul(ch.v[2], proof.eval_a));
w0 = Fr.sub(w0, Fr.mul(ch.v[3], proof.eval_b));
w0 = Fr.sub(w0, Fr.mul(ch.v[4], proof.eval_c));
w0 = Fr.sub(w0, Fr.mul(ch.v[5], proof.eval_s1));
w0 = Fr.sub(w0, Fr.mul(ch.v[6], proof.eval_s2));
pol_wxi.set(w0, 0);
pol_wxi= divPol1(pol_wxi, ch.xi);
proof.Wxi = await expTau(pol_wxi, "multiexp Wxi");
let pol_wxiw = new BigBuffer((zkey.domainSize+3)*n8r);
for (let i=0; i<zkey.domainSize+3; 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(ch.xi, Fr.w[zkey.power]));
proof.Wxiw = await expTau(pol_wxiw, "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;
}
async function expTau(b, name) {
const n = b.byteLength/n8r;
const PTauN = PTau.slice(0, n*curve.G1.F.n8*2);
const bm = await curve.Fr.batchFromMontgomery(b);
let res = await curve.G1.multiExpAffine(PTauN, bm, logger, name);
res = curve.G1.toAffine(res);
return res;
}
async function to4T(A, pz) {
pz = pz || [];
let a = await Fr.ifft(A);
const a4 = new BigBuffer(n8r*zkey.domainSize*4);
a4.set(a, 0);
const a1 = new BigBuffer(n8r*(zkey.domainSize + pz.length));
a1.set(a, 0);
for (let i= 0; i<pz.length; i++) {
a1.set(
Fr.add(
a1.slice((zkey.domainSize+i)*n8r, (zkey.domainSize+i+1)*n8r),
pz[i]
),
(zkey.domainSize+i)*n8r
);
a1.set(
Fr.sub(
a1.slice(i*n8r, (i+1)*n8r),
pz[i]
),
i*n8r
);
}
const A4 = await Fr.fft(a4);
return [a1, A4];
}
}