snarkjs/src/zkey_utils.js
2021-05-31 13:21:07 +02:00

529 lines
15 KiB
JavaScript

/*
Copyright 2018 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/>.
*/
// Format
// ======
// Header(1)
// Prover Type 1 Groth
// HeaderGroth(2)
// n8q
// q
// n8r
// r
// NVars
// NPub
// DomainSize (multiple of 2
// alpha1
// beta1
// delta1
// beta2
// gamma2
// delta2
// IC(3)
// Coefs(4)
// PointsA(5)
// PointsB1(6)
// PointsB2(7)
// PointsC(8)
// PointsH(9)
// Contributions(10)
import { Scalar, F1Field } from "ffjavascript";
import * as binFileUtils from "@iden3/binfileutils";
import { getCurveFromQ as getCurve } from "./curves.js";
import { log2 } from "./misc.js";
export async function writeHeader(fd, zkey) {
// Write the header
///////////
await binFileUtils.startWriteSection(fd, 1);
await fd.writeULE32(1); // Groth
await binFileUtils.endWriteSection(fd);
// Write the Groth header section
///////////
const curve = await getCurve(zkey.q);
await binFileUtils.startWriteSection(fd, 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 fd.writeULE32(n8q);
await binFileUtils.writeBigInt(fd, primeQ, n8q);
await fd.writeULE32(n8r);
await binFileUtils.writeBigInt(fd, primeR, n8r);
await fd.writeULE32(zkey.nVars); // Total number of bars
await fd.writeULE32(zkey.nPublic); // Total number of public vars (not including ONE)
await fd.writeULE32(zkey.domainSize); // domainSize
await writeG1(fd, curve, zkey.vk_alpha_1);
await writeG1(fd, curve, zkey.vk_beta_1);
await writeG2(fd, curve, zkey.vk_beta_2);
await writeG2(fd, curve, zkey.vk_gamma_2);
await writeG1(fd, curve, zkey.vk_delta_1);
await writeG2(fd, curve, zkey.vk_delta_2);
await binFileUtils.endWriteSection(fd);
}
export async function writeZKey(fileName, zkey) {
let curve = getCurve(zkey.q);
const fd = await binFileUtils.createBinFile(fileName,"zkey", 1, 9);
await writeHeader(fd, zkey);
const n8r = (Math.floor( (Scalar.bitLength(zkey.r) - 1) / 64) +1)*8;
const Rr = Scalar.mod(Scalar.shl(1, n8r*8), zkey.r);
const R2r = Scalar.mod(Scalar.mul(Rr,Rr), zkey.r);
// Write Pols (A and B (C can be ommited))
///////////
zkey.ccoefs = zkey.ccoefs.filter(c => c.matrix<2);
zkey.ccoefs.sort( (a,b) => a.constraint - b.constraint );
await binFileUtils.startWriteSection(fd, 4);
await fd.writeULE32(zkey.ccoefs.length);
for (let i=0; i<zkey.ccoefs.length; i++) {
const coef = zkey.ccoefs[i];
await fd.writeULE32(coef.matrix);
await fd.writeULE32(coef.constraint);
await fd.writeULE32(coef.signal);
await writeFr2(coef.value);
}
await binFileUtils.endWriteSection(fd);
// Write IC Section
///////////
await binFileUtils.startWriteSection(fd, 3);
for (let i=0; i<= zkey.nPublic; i++) {
await writeG1(fd, curve, zkey.IC[i] );
}
await binFileUtils.endWriteSection(fd);
// Write A
///////////
await binFileUtils.startWriteSection(fd, 5);
for (let i=0; i<zkey.nVars; i++) {
await writeG1(fd, curve, zkey.A[i]);
}
await binFileUtils.endWriteSection(fd);
// Write B1
///////////
await binFileUtils.startWriteSection(fd, 6);
for (let i=0; i<zkey.nVars; i++) {
await writeG1(fd, curve, zkey.B1[i]);
}
await binFileUtils.endWriteSection(fd);
// Write B2
///////////
await binFileUtils.startWriteSection(fd, 7);
for (let i=0; i<zkey.nVars; i++) {
await writeG2(fd, curve, zkey.B2[i]);
}
await binFileUtils.endWriteSection(fd);
// Write C
///////////
await binFileUtils.startWriteSection(fd, 8);
for (let i=zkey.nPublic+1; i<zkey.nVars; i++) {
await writeG1(fd, curve, zkey.C[i]);
}
await binFileUtils.endWriteSection(fd);
// Write H points
///////////
await binFileUtils.startWriteSection(fd, 9);
for (let i=0; i<zkey.domainSize; i++) {
await writeG1(fd, curve, zkey.hExps[i]);
}
await binFileUtils.endWriteSection(fd);
await fd.close();
async function writeFr2(n) {
// Convert to montgomery
n = Scalar.mod( Scalar.mul(n, R2r), zkey.r);
await binFileUtils.writeBigInt(fd, n, n8r);
}
}
async function writeG1(fd, curve, p) {
const buff = new Uint8Array(curve.G1.F.n8*2);
curve.G1.toRprLEM(buff, 0, p);
await fd.write(buff);
}
async function writeG2(fd, curve, p) {
const buff = new Uint8Array(curve.G2.F.n8*2);
curve.G2.toRprLEM(buff, 0, p);
await fd.write(buff);
}
async function readG1(fd, curve, toObject) {
const buff = await fd.read(curve.G1.F.n8*2);
const res = curve.G1.fromRprLEM(buff, 0);
return toObject ? curve.G1.toObject(res) : res;
}
async function readG2(fd, curve, toObject) {
const buff = await fd.read(curve.G2.F.n8*2);
const res = curve.G2.fromRprLEM(buff, 0);
return toObject ? curve.G2.toObject(res) : res;
}
export async function readHeader(fd, sections, toObject) {
// Read Header
/////////////////////
await binFileUtils.startReadUniqueSection(fd, sections, 1);
const protocolId = await fd.readULE32();
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);
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.vk_alpha_1 = await readG1(fd, curve, toObject);
zkey.vk_beta_1 = await readG1(fd, curve, toObject);
zkey.vk_beta_2 = await readG2(fd, curve, toObject);
zkey.vk_gamma_2 = await readG2(fd, curve, toObject);
zkey.vk_delta_1 = await readG1(fd, curve, toObject);
zkey.vk_delta_2 = await readG2(fd, curve, toObject);
await binFileUtils.endReadSection(fd);
return zkey;
}
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.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);
const zkey = await readHeader(fd, sections, "groth16", toObject);
const Fr = new F1Field(zkey.r);
const Rr = Scalar.mod(Scalar.shl(1, zkey.n8r*8), zkey.r);
const Rri = Fr.inv(Rr);
const Rri2 = Fr.mul(Rri, Rri);
let curve = await getCurve(zkey.q);
// Read IC Section
///////////
await binFileUtils.startReadUniqueSection(fd, sections, 3);
zkey.IC = [];
for (let i=0; i<= zkey.nPublic; i++) {
const P = await readG1(fd, curve, toObject);
zkey.IC.push(P);
}
await binFileUtils.endReadSection(fd);
// Read Coefs
///////////
await binFileUtils.startReadUniqueSection(fd, sections, 4);
const nCCoefs = await fd.readULE32();
zkey.ccoefs = [];
for (let i=0; i<nCCoefs; i++) {
const m = await fd.readULE32();
const c = await fd.readULE32();
const s = await fd.readULE32();
const v = await readFr2(toObject);
zkey.ccoefs.push({
matrix: m,
constraint: c,
signal: s,
value: v
});
}
await binFileUtils.endReadSection(fd);
// Read A points
///////////
await binFileUtils.startReadUniqueSection(fd, sections, 5);
zkey.A = [];
for (let i=0; i<zkey.nVars; i++) {
const A = await readG1(fd, curve, toObject);
zkey.A[i] = A;
}
await binFileUtils.endReadSection(fd);
// Read B1
///////////
await binFileUtils.startReadUniqueSection(fd, sections, 6);
zkey.B1 = [];
for (let i=0; i<zkey.nVars; i++) {
const B1 = await readG1(fd, curve, toObject);
zkey.B1[i] = B1;
}
await binFileUtils.endReadSection(fd);
// Read B2 points
///////////
await binFileUtils.startReadUniqueSection(fd, sections, 7);
zkey.B2 = [];
for (let i=0; i<zkey.nVars; i++) {
const B2 = await readG2(fd, curve, toObject);
zkey.B2[i] = B2;
}
await binFileUtils.endReadSection(fd);
// Read C points
///////////
await binFileUtils.startReadUniqueSection(fd, sections, 8);
zkey.C = [];
for (let i=zkey.nPublic+1; i<zkey.nVars; i++) {
const C = await readG1(fd, curve, toObject);
zkey.C[i] = C;
}
await binFileUtils.endReadSection(fd);
// Read H points
///////////
await binFileUtils.startReadUniqueSection(fd, sections, 9);
zkey.hExps = [];
for (let i=0; i<zkey.domainSize; i++) {
const H = await readG1(fd, curve, toObject);
zkey.hExps.push(H);
}
await binFileUtils.endReadSection(fd);
await fd.close();
return zkey;
async function readFr2(/* toObject */) {
const n = await binFileUtils.readBigInt(fd, zkey.n8r);
return Fr.mul(n, Rri2);
}
}
async function readContribution(fd, curve, toObject) {
const c = {delta:{}};
c.deltaAfter = await readG1(fd, curve, toObject);
c.delta.g1_s = await readG1(fd, curve, toObject);
c.delta.g1_sx = await readG1(fd, curve, toObject);
c.delta.g2_spx = await readG2(fd, curve, toObject);
c.transcript = await fd.read(64);
c.type = await fd.readULE32();
const paramLength = await fd.readULE32();
const curPos = fd.pos;
let lastType =0;
while (fd.pos-curPos < paramLength) {
const buffType = await fd.read(1);
if (buffType[0]<= lastType) throw new Error("Parameters in the contribution must be sorted");
lastType = buffType[0];
if (buffType[0]==1) { // Name
const buffLen = await fd.read(1);
const buffStr = await fd.read(buffLen[0]);
c.name = new TextDecoder().decode(buffStr);
} else if (buffType[0]==2) {
const buffExp = await fd.read(1);
c.numIterationsExp = buffExp[0];
} else if (buffType[0]==3) {
const buffLen = await fd.read(1);
c.beaconHash = await fd.read(buffLen[0]);
} else {
throw new Error("Parameter not recognized");
}
}
if (fd.pos != curPos + paramLength) {
throw new Error("Parametes do not match");
}
return c;
}
export async function readMPCParams(fd, curve, sections) {
await binFileUtils.startReadUniqueSection(fd, sections, 10);
const res = { contributions: []};
res.csHash = await fd.read(64);
const n = await fd.readULE32();
for (let i=0; i<n; i++) {
const c = await readContribution(fd, curve);
res.contributions.push(c);
}
await binFileUtils.endReadSection(fd);
return res;
}
async function writeContribution(fd, curve, c) {
await writeG1(fd, curve, c.deltaAfter);
await writeG1(fd, curve, c.delta.g1_s);
await writeG1(fd, curve, c.delta.g1_sx);
await writeG2(fd, curve, c.delta.g2_spx);
await fd.write(c.transcript);
await fd.writeULE32(c.type || 0);
const params = [];
if (c.name) {
params.push(1); // Param Name
const nameData = new TextEncoder("utf-8").encode(c.name.substring(0,64));
params.push(nameData.byteLength);
for (let i=0; i<nameData.byteLength; i++) params.push(nameData[i]);
}
if (c.type == 1) {
params.push(2); // Param numIterationsExp
params.push(c.numIterationsExp);
params.push(3); // Beacon Hash
params.push(c.beaconHash.byteLength);
for (let i=0; i<c.beaconHash.byteLength; i++) params.push(c.beaconHash[i]);
}
if (params.length>0) {
const paramsBuff = new Uint8Array(params);
await fd.writeULE32(paramsBuff.byteLength);
await fd.write(paramsBuff);
} else {
await fd.writeULE32(0);
}
}
export async function writeMPCParams(fd, curve, mpcParams) {
await binFileUtils.startWriteSection(fd, 10);
await fd.write(mpcParams.csHash);
await fd.writeULE32(mpcParams.contributions.length);
for (let i=0; i<mpcParams.contributions.length; i++) {
await writeContribution(fd, curve,mpcParams.contributions[i]);
}
await binFileUtils.endWriteSection(fd);
}
export function hashG1(hasher, curve, p) {
const buff = new Uint8Array(curve.G1.F.n8*2);
curve.G1.toRprUncompressed(buff, 0, p);
hasher.update(buff);
}
export function hashG2(hasher,curve, p) {
const buff = new Uint8Array(curve.G2.F.n8*2);
curve.G2.toRprUncompressed(buff, 0, p);
hasher.update(buff);
}
export function hashPubKey(hasher, curve, c) {
hashG1(hasher, curve, c.deltaAfter);
hashG1(hasher, curve, c.delta.g1_s);
hashG1(hasher, curve, c.delta.g1_sx);
hashG2(hasher, curve, c.delta.g2_spx);
hasher.update(c.transcript);
}