UNPKG

snarkjs

Version:

zkSNARKs implementation in JavaScript

1,482 lines (1,188 loc) 458 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var binFileUtils = require('@iden3/binfileutils'); var ffjavascript = require('ffjavascript'); var Blake2b = require('blake2b-wasm'); var readline = require('readline'); var crypto = require('crypto'); var fastFile = require('fastfile'); var circom_runtime = require('circom_runtime'); var r1csfile = require('r1csfile'); var ejs = require('ejs'); var jsSha3 = require('js-sha3'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } var binFileUtils__namespace = /*#__PURE__*/_interopNamespace(binFileUtils); var Blake2b__default = /*#__PURE__*/_interopDefaultLegacy(Blake2b); var readline__default = /*#__PURE__*/_interopDefaultLegacy(readline); var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto); var fastFile__namespace = /*#__PURE__*/_interopNamespace(fastFile); var ejs__default = /*#__PURE__*/_interopDefaultLegacy(ejs); var jsSha3__default = /*#__PURE__*/_interopDefaultLegacy(jsSha3); const bls12381r$1 = ffjavascript.Scalar.e("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", 16); const bn128r$1 = ffjavascript.Scalar.e("21888242871839275222246405745257275088548364400416034343698204186575808495617"); const bls12381q = ffjavascript.Scalar.e("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", 16); const bn128q = ffjavascript.Scalar.e("21888242871839275222246405745257275088696311157297823662689037894645226208583"); async function getCurveFromR(r, options) { let curve; // check that options param is defined and that options.singleThread is defined let singleThread = options && options.singleThread; if (ffjavascript.Scalar.eq(r, bn128r$1)) { curve = await ffjavascript.buildBn128(singleThread); } else if (ffjavascript.Scalar.eq(r, bls12381r$1)) { curve = await ffjavascript.buildBls12381(singleThread); } else { throw new Error(`Curve not supported: ${ffjavascript.Scalar.toString(r)}`); } return curve; } async function getCurveFromQ(q, options) { let curve; let singleThread = options && options.singleThread; if (ffjavascript.Scalar.eq(q, bn128q)) { curve = await ffjavascript.buildBn128(singleThread); } else if (ffjavascript.Scalar.eq(q, bls12381q)) { curve = await ffjavascript.buildBls12381(singleThread); } else { throw new Error(`Curve not supported: ${ffjavascript.Scalar.toString(q)}`); } return curve; } async function getCurveFromName(name, options) { let curve; let singleThread = options && options.singleThread; const normName = normalizeName(name); if (["BN128", "BN254", "ALTBN128"].indexOf(normName) >= 0) { curve = await ffjavascript.buildBn128(singleThread); } else if (["BLS12381"].indexOf(normName) >= 0) { curve = await ffjavascript.buildBls12381(singleThread); } else { throw new Error(`Curve not supported: ${name}`); } return curve; function normalizeName(n) { return n.toUpperCase().match(/[A-Za-z0-9]+/g).join(""); } } var curves = /*#__PURE__*/Object.freeze({ __proto__: null, getCurveFromR: getCurveFromR, getCurveFromQ: getCurveFromQ, getCurveFromName: getCurveFromName }); /* 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/>. */ function log2( V ) { return( ( ( V & 0xFFFF0000 ) !== 0 ? ( V &= 0xFFFF0000, 16 ) : 0 ) | ( ( V & 0xFF00FF00 ) !== 0 ? ( V &= 0xFF00FF00, 8 ) : 0 ) | ( ( V & 0xF0F0F0F0 ) !== 0 ? ( V &= 0xF0F0F0F0, 4 ) : 0 ) | ( ( V & 0xCCCCCCCC ) !== 0 ? ( V &= 0xCCCCCCCC, 2 ) : 0 ) | ( ( V & 0xAAAAAAAA ) !== 0 ) ); } function formatHash(b, title) { const a = new DataView(b.buffer, b.byteOffset, b.byteLength); let S = ""; for (let i=0; i<4; i++) { if (i>0) S += "\n"; S += "\t\t"; for (let j=0; j<4; j++) { if (j>0) S += " "; S += a.getUint32(i*16+j*4).toString(16).padStart(8, "0"); } } if (title) S = title + "\n" + S; return S; } function hashIsEqual(h1, h2) { if (h1.byteLength != h2.byteLength) return false; var dv1 = new Int8Array(h1); var dv2 = new Int8Array(h2); for (var i = 0 ; i != h1.byteLength ; i++) { if (dv1[i] != dv2[i]) return false; } return true; } function cloneHasher(h) { const ph = h.getPartialHash(); const res = Blake2b__default["default"](64); res.setPartialHash(ph); return res; } async function sameRatio$2(curve, g1s, g1sx, g2s, g2sx) { if (curve.G1.isZero(g1s)) return false; if (curve.G1.isZero(g1sx)) return false; if (curve.G2.isZero(g2s)) return false; if (curve.G2.isZero(g2sx)) return false; // return curve.F12.eq(curve.pairing(g1s, g2sx), curve.pairing(g1sx, g2s)); const res = await curve.pairingEq(g1s, g2sx, curve.G1.neg(g1sx), g2s); return res; } function askEntropy() { if (process.browser) { return window.prompt("Enter a random text. (Entropy): ", ""); } else { const rl = readline__default["default"].createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question("Enter a random text. (Entropy): ", (input) => resolve(input) ); }); } } function getRandomBytes(n) { let array = new Uint8Array(n); if (process.browser) { // Supported globalThis.crypto.getRandomValues(array); } else { // NodeJS crypto__default["default"].randomFillSync(array); } return array; } async function sha256digest(data) { if (process.browser) { // Supported const buffer = await globalThis.crypto.subtle.digest("SHA-256", data.buffer); return new Uint8Array(buffer); } else { // NodeJS return crypto__default["default"].createHash("sha256").update(data).digest(); } } /** * @param {Uint8Array} data * @param {number} offset */ function readUInt32BE(data, offset) { return new DataView(data.buffer).getUint32(offset, false); } async function getRandomRng(entropy) { // Generate a random Rng while (!entropy) { entropy = await askEntropy(); } const hasher = Blake2b__default["default"](64); hasher.update(getRandomBytes(64)); const enc = new TextEncoder(); // always utf-8 hasher.update(enc.encode(entropy)); const hash = hasher.digest(); const seed = []; for (let i=0;i<8;i++) { seed[i] = readUInt32BE(hash, i*4); } const rng = new ffjavascript.ChaCha(seed); return rng; } async function rngFromBeaconParams(beaconHash, numIterationsExp) { let nIterationsInner; let nIterationsOuter; if (numIterationsExp<32) { nIterationsInner = (1 << numIterationsExp) >>> 0; nIterationsOuter = 1; } else { nIterationsInner = 0x100000000; nIterationsOuter = (1 << (numIterationsExp-32)) >>> 0; } let curHash = beaconHash; for (let i=0; i<nIterationsOuter; i++) { for (let j=0; j<nIterationsInner; j++) { curHash = await sha256digest(curHash); } } const curHashV = new DataView(curHash.buffer, curHash.byteOffset, curHash.byteLength); const seed = []; for (let i=0; i<8; i++) { seed[i] = curHashV.getUint32(i*4, false); } const rng = new ffjavascript.ChaCha(seed); return rng; } function hex2ByteArray(s) { if (s instanceof Uint8Array) return s; if (s.slice(0,2) == "0x") s= s.slice(2); return new Uint8Array(s.match(/[\da-f]{2}/gi).map(function (h) { return parseInt(h, 16); })); } function byteArray2hex(byteArray) { return Array.prototype.map.call(byteArray, function(byte) { return ("0" + (byte & 0xFF).toString(16)).slice(-2); }).join(""); } function stringifyBigIntsWithField(Fr, o) { if (o instanceof Uint8Array) { return Fr.toString(o); } else if (Array.isArray(o)) { return o.map(stringifyBigIntsWithField.bind(null, Fr)); } else if (typeof o == "object") { const res = {}; const keys = Object.keys(o); keys.forEach( (k) => { res[k] = stringifyBigIntsWithField(Fr, o[k]); }); return res; } else if ((typeof(o) == "bigint") || o.eq !== undefined) { return o.toString(10); } else { return o; } } const HEADER_ZKEY_SECTION = 1; const GROTH16_PROTOCOL_ID = 1; const PLONK_PROTOCOL_ID = 2; const FFLONK_PROTOCOL_ID = 10; /* Copyright 2022 iden3 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/>. */ // FFlonk constants const FF_T_POL_DEG_MIN = 3; // ZKEY constants const ZKEY_FF_NSECTIONS = 17; const ZKEY_FF_HEADER_SECTION = 2; const ZKEY_FF_ADDITIONS_SECTION = 3; const ZKEY_FF_A_MAP_SECTION = 4; const ZKEY_FF_B_MAP_SECTION = 5; const ZKEY_FF_C_MAP_SECTION = 6; const ZKEY_FF_QL_SECTION = 7; const ZKEY_FF_QR_SECTION = 8; const ZKEY_FF_QM_SECTION = 9; const ZKEY_FF_QO_SECTION = 10; const ZKEY_FF_QC_SECTION = 11; const ZKEY_FF_SIGMA1_SECTION = 12; const ZKEY_FF_SIGMA2_SECTION = 13; const ZKEY_FF_SIGMA3_SECTION = 14; const ZKEY_FF_LAGRANGE_SECTION = 15; const ZKEY_FF_PTAU_SECTION = 16; const ZKEY_FF_C0_SECTION = 17; /* 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/>. */ async function writeHeader(fd, zkey) { // Write the header /////////// await binFileUtils__namespace.startWriteSection(fd, 1); await fd.writeULE32(1); // Groth await binFileUtils__namespace.endWriteSection(fd); // Write the Groth header section /////////// const curve = await getCurveFromQ(zkey.q); await binFileUtils__namespace.startWriteSection(fd, 2); const primeQ = curve.q; const n8q = (Math.floor( (ffjavascript.Scalar.bitLength(primeQ) - 1) / 64) +1)*8; const primeR = curve.r; const n8r = (Math.floor( (ffjavascript.Scalar.bitLength(primeR) - 1) / 64) +1)*8; await fd.writeULE32(n8q); await binFileUtils__namespace.writeBigInt(fd, primeQ, n8q); await fd.writeULE32(n8r); await binFileUtils__namespace.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__namespace.endWriteSection(fd); } 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; } async function readHeader$1(fd, sections, toObject, options) { // Read Header ///////////////////// await binFileUtils__namespace.startReadUniqueSection(fd, sections, 1); const protocolId = await fd.readULE32(); await binFileUtils__namespace.endReadSection(fd); if (protocolId === GROTH16_PROTOCOL_ID) { return await readHeaderGroth16(fd, sections, toObject, options); } else if (protocolId === PLONK_PROTOCOL_ID) { return await readHeaderPlonk(fd, sections, toObject, options); } else if (protocolId === FFLONK_PROTOCOL_ID) { return await readHeaderFFlonk(fd, sections, toObject, options); } else { throw new Error("Protocol not supported: "); } } async function readHeaderGroth16(fd, sections, toObject, options) { const zkey = {}; zkey.protocol = "groth16"; // Read Groth Header ///////////////////// await binFileUtils__namespace.startReadUniqueSection(fd, sections, 2); const n8q = await fd.readULE32(); zkey.n8q = n8q; zkey.q = await binFileUtils__namespace.readBigInt(fd, n8q); const n8r = await fd.readULE32(); zkey.n8r = n8r; zkey.r = await binFileUtils__namespace.readBigInt(fd, n8r); zkey.curve = await getCurveFromQ(zkey.q, options); 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, zkey.curve, toObject); zkey.vk_beta_1 = await readG1(fd, zkey.curve, toObject); zkey.vk_beta_2 = await readG2(fd, zkey.curve, toObject); zkey.vk_gamma_2 = await readG2(fd, zkey.curve, toObject); zkey.vk_delta_1 = await readG1(fd, zkey.curve, toObject); zkey.vk_delta_2 = await readG2(fd, zkey.curve, toObject); await binFileUtils__namespace.endReadSection(fd); return zkey; } async function readHeaderPlonk(fd, sections, toObject, options) { const zkey = {}; zkey.protocol = "plonk"; // Read Plonk Header ///////////////////// await binFileUtils__namespace.startReadUniqueSection(fd, sections, 2); const n8q = await fd.readULE32(); zkey.n8q = n8q; zkey.q = await binFileUtils__namespace.readBigInt(fd, n8q); const n8r = await fd.readULE32(); zkey.n8r = n8r; zkey.r = await binFileUtils__namespace.readBigInt(fd, n8r); zkey.curve = await getCurveFromQ(zkey.q, options); 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.nConstraints = await fd.readULE32(); zkey.k1 = await fd.read(n8r); zkey.k2 = await fd.read(n8r); zkey.Qm = await readG1(fd, zkey.curve, toObject); zkey.Ql = await readG1(fd, zkey.curve, toObject); zkey.Qr = await readG1(fd, zkey.curve, toObject); zkey.Qo = await readG1(fd, zkey.curve, toObject); zkey.Qc = await readG1(fd, zkey.curve, toObject); zkey.S1 = await readG1(fd, zkey.curve, toObject); zkey.S2 = await readG1(fd, zkey.curve, toObject); zkey.S3 = await readG1(fd, zkey.curve, toObject); zkey.X_2 = await readG2(fd, zkey.curve, toObject); await binFileUtils__namespace.endReadSection(fd); return zkey; } async function readHeaderFFlonk(fd, sections, toObject, options) { const zkey = {}; zkey.protocol = "fflonk"; zkey.protocolId = FFLONK_PROTOCOL_ID; await binFileUtils__namespace.startReadUniqueSection(fd, sections, ZKEY_FF_HEADER_SECTION); const n8q = await fd.readULE32(); zkey.n8q = n8q; zkey.q = await binFileUtils__namespace.readBigInt(fd, n8q); zkey.curve = await getCurveFromQ(zkey.q, options); const n8r = await fd.readULE32(); zkey.n8r = n8r; zkey.r = await binFileUtils__namespace.readBigInt(fd, n8r); 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.nConstraints = await fd.readULE32(); zkey.k1 = await fd.read(n8r); zkey.k2 = await fd.read(n8r); zkey.w3 = await fd.read(n8r); zkey.w4 = await fd.read(n8r); zkey.w8 = await fd.read(n8r); zkey.wr = await fd.read(n8r); zkey.X_2 = await readG2(fd, zkey.curve, toObject); zkey.C0 = await readG1(fd, zkey.curve, toObject); await binFileUtils__namespace.endReadSection(fd); return zkey; } async function readZKey(fileName, toObject) { const {fd, sections} = await binFileUtils__namespace.readBinFile(fileName, "zkey", 1); const zkey = await readHeader$1(fd, sections, toObject); const Fr = new ffjavascript.F1Field(zkey.r); const Rr = ffjavascript.Scalar.mod(ffjavascript.Scalar.shl(1, zkey.n8r*8), zkey.r); const Rri = Fr.inv(Rr); const Rri2 = Fr.mul(Rri, Rri); let curve = await getCurveFromQ(zkey.q); // Read IC Section /////////// await binFileUtils__namespace.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__namespace.endReadSection(fd); // Read Coefs /////////// await binFileUtils__namespace.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(); zkey.ccoefs.push({ matrix: m, constraint: c, signal: s, value: v }); } await binFileUtils__namespace.endReadSection(fd); // Read A points /////////// await binFileUtils__namespace.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__namespace.endReadSection(fd); // Read B1 /////////// await binFileUtils__namespace.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__namespace.endReadSection(fd); // Read B2 points /////////// await binFileUtils__namespace.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__namespace.endReadSection(fd); // Read C points /////////// await binFileUtils__namespace.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__namespace.endReadSection(fd); // Read H points /////////// await binFileUtils__namespace.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__namespace.endReadSection(fd); await fd.close(); return zkey; async function readFr2(/* toObject */) { const n = await binFileUtils__namespace.readBigInt(fd, zkey.n8r); return Fr.mul(n, Rri2); } } async function readContribution$1(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("Parameters do not match"); } return c; } async function readMPCParams(fd, curve, sections) { await binFileUtils__namespace.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$1(fd, curve); res.contributions.push(c); } await binFileUtils__namespace.endReadSection(fd); return res; } async function writeContribution$1(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); } } async function writeMPCParams(fd, curve, mpcParams) { await binFileUtils__namespace.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$1(fd, curve,mpcParams.contributions[i]); } await binFileUtils__namespace.endWriteSection(fd); } function hashG1(hasher, curve, p) { const buff = new Uint8Array(curve.G1.F.n8*2); curve.G1.toRprUncompressed(buff, 0, p); hasher.update(buff); } function hashG2(hasher,curve, p) { const buff = new Uint8Array(curve.G2.F.n8*2); curve.G2.toRprUncompressed(buff, 0, p); hasher.update(buff); } 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); } /* 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/>. */ async function write(fd, witness, prime) { await binFileUtils__namespace.startWriteSection(fd, 1); const n8 = (Math.floor( (ffjavascript.Scalar.bitLength(prime) - 1) / 64) +1)*8; await fd.writeULE32(n8); await binFileUtils__namespace.writeBigInt(fd, prime, n8); await fd.writeULE32(witness.length); await binFileUtils__namespace.endWriteSection(fd); await binFileUtils__namespace.startWriteSection(fd, 2); for (let i=0; i<witness.length; i++) { await binFileUtils__namespace.writeBigInt(fd, witness[i], n8); } await binFileUtils__namespace.endWriteSection(fd, 2); } async function writeBin(fd, witnessBin, prime) { await binFileUtils__namespace.startWriteSection(fd, 1); const n8 = (Math.floor( (ffjavascript.Scalar.bitLength(prime) - 1) / 64) +1)*8; await fd.writeULE32(n8); await binFileUtils__namespace.writeBigInt(fd, prime, n8); if (witnessBin.byteLength % n8 != 0) { throw new Error("Invalid witness length"); } await fd.writeULE32(witnessBin.byteLength / n8); await binFileUtils__namespace.endWriteSection(fd); await binFileUtils__namespace.startWriteSection(fd, 2); await fd.write(witnessBin); await binFileUtils__namespace.endWriteSection(fd); } async function readHeader(fd, sections) { await binFileUtils__namespace.startReadUniqueSection(fd, sections, 1); const n8 = await fd.readULE32(); const q = await binFileUtils__namespace.readBigInt(fd, n8); const nWitness = await fd.readULE32(); await binFileUtils__namespace.endReadSection(fd); return {n8, q, nWitness}; } async function read(fileName) { const {fd, sections} = await binFileUtils__namespace.readBinFile(fileName, "wtns", 2); const {n8, nWitness} = await readHeader(fd, sections); await binFileUtils__namespace.startReadUniqueSection(fd, sections, 2); const res = []; for (let i=0; i<nWitness; i++) { const v = await binFileUtils__namespace.readBigInt(fd, n8); res.push(v); } await binFileUtils__namespace.endReadSection(fd); await fd.close(); return res; } /* 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/>. */ const {stringifyBigInts: stringifyBigInts$4} = ffjavascript.utils; async function groth16Prove(zkeyFileName, witnessFileName, logger, options) { const {fd: fdWtns, sections: sectionsWtns} = await binFileUtils__namespace.readBinFile(witnessFileName, "wtns", 2, 1<<25, 1<<23); const wtns = await readHeader(fdWtns, sectionsWtns); const {fd: fdZKey, sections: sectionsZKey} = await binFileUtils__namespace.readBinFile(zkeyFileName, "zkey", 2, 1<<25, 1<<23); const zkey = await readHeader$1(fdZKey, sectionsZKey, undefined, options); if (zkey.protocol != "groth16") { throw new Error("zkey file is not groth16"); } if (!ffjavascript.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) { throw new Error(`Invalid witness length. Circuit: ${zkey.nVars}, witness: ${wtns.nWitness}`); } const curve = zkey.curve; const Fr = curve.Fr; const G1 = curve.G1; const G2 = curve.G2; const power = log2(zkey.domainSize); if (logger) logger.debug("Reading Wtns"); const buffWitness = await binFileUtils__namespace.readSection(fdWtns, sectionsWtns, 2); if (logger) logger.debug("Reading Coeffs"); const buffCoeffs = await binFileUtils__namespace.readSection(fdZKey, sectionsZKey, 4); if (logger) logger.debug("Building ABC"); const [buffA_T, buffB_T, buffC_T] = await buildABC1(curve, zkey, buffWitness, buffCoeffs, logger); const inc = power == Fr.s ? curve.Fr.shift : curve.Fr.w[power+1]; const buffA = await Fr.ifft(buffA_T, "", "", logger, "IFFT_A"); const buffAodd = await Fr.batchApplyKey(buffA, Fr.e(1), inc); const buffAodd_T = await Fr.fft(buffAodd, "", "", logger, "FFT_A"); const buffB = await Fr.ifft(buffB_T, "", "", logger, "IFFT_B"); const buffBodd = await Fr.batchApplyKey(buffB, Fr.e(1), inc); const buffBodd_T = await Fr.fft(buffBodd, "", "", logger, "FFT_B"); const buffC = await Fr.ifft(buffC_T, "", "", logger, "IFFT_C"); const buffCodd = await Fr.batchApplyKey(buffC, Fr.e(1), inc); const buffCodd_T = await Fr.fft(buffCodd, "", "", logger, "FFT_C"); if (logger) logger.debug("Join ABC"); const buffPodd_T = await joinABC(curve, zkey, buffAodd_T, buffBodd_T, buffCodd_T, logger); let proof = {}; if (logger) logger.debug("Reading A Points"); const buffBasesA = await binFileUtils__namespace.readSection(fdZKey, sectionsZKey, 5); proof.pi_a = await curve.G1.multiExpAffine(buffBasesA, buffWitness, logger, "multiexp A"); if (logger) logger.debug("Reading B1 Points"); const buffBasesB1 = await binFileUtils__namespace.readSection(fdZKey, sectionsZKey, 6); let pib1 = await curve.G1.multiExpAffine(buffBasesB1, buffWitness, logger, "multiexp B1"); if (logger) logger.debug("Reading B2 Points"); const buffBasesB2 = await binFileUtils__namespace.readSection(fdZKey, sectionsZKey, 7); proof.pi_b = await curve.G2.multiExpAffine(buffBasesB2, buffWitness, logger, "multiexp B2"); if (logger) logger.debug("Reading C Points"); const buffBasesC = await binFileUtils__namespace.readSection(fdZKey, sectionsZKey, 8); proof.pi_c = await curve.G1.multiExpAffine(buffBasesC, buffWitness.slice((zkey.nPublic+1)*curve.Fr.n8), logger, "multiexp C"); if (logger) logger.debug("Reading H Points"); const buffBasesH = await binFileUtils__namespace.readSection(fdZKey, sectionsZKey, 9); const resH = await curve.G1.multiExpAffine(buffBasesH, buffPodd_T, logger, "multiexp H"); const r = curve.Fr.random(); const s = curve.Fr.random(); proof.pi_a = G1.add( proof.pi_a, zkey.vk_alpha_1 ); proof.pi_a = G1.add( proof.pi_a, G1.timesFr( zkey.vk_delta_1, r )); proof.pi_b = G2.add( proof.pi_b, zkey.vk_beta_2 ); proof.pi_b = G2.add( proof.pi_b, G2.timesFr( zkey.vk_delta_2, s )); pib1 = G1.add( pib1, zkey.vk_beta_1 ); pib1 = G1.add( pib1, G1.timesFr( zkey.vk_delta_1, s )); proof.pi_c = G1.add(proof.pi_c, resH); proof.pi_c = G1.add( proof.pi_c, G1.timesFr( proof.pi_a, s )); proof.pi_c = G1.add( proof.pi_c, G1.timesFr( pib1, r )); proof.pi_c = G1.add( proof.pi_c, G1.timesFr( zkey.vk_delta_1, Fr.neg(Fr.mul(r,s) ))); let publicSignals = []; for (let i=1; i<= zkey.nPublic; i++) { const b = buffWitness.slice(i*Fr.n8, i*Fr.n8+Fr.n8); publicSignals.push(ffjavascript.Scalar.fromRprLE(b)); } proof.pi_a = G1.toObject(G1.toAffine(proof.pi_a)); proof.pi_b = G2.toObject(G2.toAffine(proof.pi_b)); proof.pi_c = G1.toObject(G1.toAffine(proof.pi_c)); proof.protocol = "groth16"; proof.curve = curve.name; await fdZKey.close(); await fdWtns.close(); proof = stringifyBigInts$4(proof); publicSignals = stringifyBigInts$4(publicSignals); return {proof, publicSignals}; } async function buildABC1(curve, zkey, witness, coeffs, logger) { const n8 = curve.Fr.n8; const sCoef = 4*3 + zkey.n8r; const nCoef = (coeffs.byteLength-4) / sCoef; const outBuffA = new ffjavascript.BigBuffer(zkey.domainSize * n8); const outBuffB = new ffjavascript.BigBuffer(zkey.domainSize * n8); const outBuffC = new ffjavascript.BigBuffer(zkey.domainSize * n8); const outBuf = [ outBuffA, outBuffB ]; for (let i=0; i<nCoef; i++) { if ((logger)&&(i%1000000 == 0)) logger.debug(`QAP AB: ${i}/${nCoef}`); const buffCoef = coeffs.slice(4+i*sCoef, 4+i*sCoef+sCoef); const buffCoefV = new DataView(buffCoef.buffer); const m= buffCoefV.getUint32(0, true); const c= buffCoefV.getUint32(4, true); const s= buffCoefV.getUint32(8, true); const coef = buffCoef.slice(12, 12+n8); outBuf[m].set( curve.Fr.add( outBuf[m].slice(c*n8, c*n8+n8), curve.Fr.mul(coef, witness.slice(s*n8, s*n8+n8)) ), c*n8 ); } for (let i=0; i<zkey.domainSize; i++) { if ((logger)&&(i%1000000 == 0)) logger.debug(`QAP C: ${i}/${zkey.domainSize}`); outBuffC.set( curve.Fr.mul( outBuffA.slice(i*n8, i*n8+n8), outBuffB.slice(i*n8, i*n8+n8), ), i*n8 ); } return [outBuffA, outBuffB, outBuffC]; } /* async function buildABC(curve, zkey, witness, coeffs, logger) { const concurrency = curve.tm.concurrency; const sCoef = 4*3 + zkey.n8r; let getUint32; if (coeffs instanceof BigBuffer) { const coeffsDV = []; const PAGE_LEN = coeffs.buffers[0].length; for (let i=0; i< coeffs.buffers.length; i++) { coeffsDV.push(new DataView(coeffs.buffers[i].buffer)); } getUint32 = function (pos) { return coeffsDV[Math.floor(pos/PAGE_LEN)].getUint32(pos % PAGE_LEN, true); }; } else { const coeffsDV = new DataView(coeffs.buffer, coeffs.byteOffset, coeffs.byteLength); getUint32 = function (pos) { return coeffsDV.getUint32(pos, true); }; } const elementsPerChunk = Math.floor(zkey.domainSize/concurrency); const promises = []; const cutPoints = []; for (let i=0; i<concurrency; i++) { cutPoints.push( getCutPoint( Math.floor(i*elementsPerChunk) )); } cutPoints.push(coeffs.byteLength); const chunkSize = 2**26; for (let s=0 ; s<zkey.nVars ; s+= chunkSize) { if (logger) logger.debug(`QAP ${s}: ${s}/${zkey.nVars}`); const ns= Math.min(zkey.nVars-s, chunkSize ); for (let i=0; i<concurrency; i++) { let n; if (i< concurrency-1) { n = elementsPerChunk; } else { n = zkey.domainSize - i*elementsPerChunk; } if (n==0) continue; const task = []; task.push({cmd: "ALLOCSET", var: 0, buff: coeffs.slice(cutPoints[i], cutPoints[i+1])}); task.push({cmd: "ALLOCSET", var: 1, buff: witness.slice(s*curve.Fr.n8, (s+ns)*curve.Fr.n8)}); task.push({cmd: "ALLOC", var: 2, len: n*curve.Fr.n8}); task.push({cmd: "ALLOC", var: 3, len: n*curve.Fr.n8}); task.push({cmd: "ALLOC", var: 4, len: n*curve.Fr.n8}); task.push({cmd: "CALL", fnName: "qap_buildABC", params:[ {var: 0}, {val: (cutPoints[i+1] - cutPoints[i])/sCoef}, {var: 1}, {var: 2}, {var: 3}, {var: 4}, {val: i*elementsPerChunk}, {val: n}, {val: s}, {val: ns} ]}); task.push({cmd: "GET", out: 0, var: 2, len: n*curve.Fr.n8}); task.push({cmd: "GET", out: 1, var: 3, len: n*curve.Fr.n8}); task.push({cmd: "GET", out: 2, var: 4, len: n*curve.Fr.n8}); promises.push(curve.tm.queueAction(task)); } } let result = await Promise.all(promises); const nGroups = result.length / concurrency; if (nGroups>1) { const promises2 = []; for (let i=0; i<concurrency; i++) { const task=[]; task.push({cmd: "ALLOC", var: 0, len: result[i][0].byteLength}); task.push({cmd: "ALLOC", var: 1, len: result[i][0].byteLength}); for (let m=0; m<3; m++) { task.push({cmd: "SET", var: 0, buff: result[i][m]}); for (let s=1; s<nGroups; s++) { task.push({cmd: "SET", var: 1, buff: result[s*concurrency + i][m]}); task.push({cmd: "CALL", fnName: "qap_batchAdd", params:[ {var: 0}, {var: 1}, {val: result[i][m].length/curve.Fr.n8}, {var: 0} ]}); } task.push({cmd: "GET", out: m, var: 0, len: result[i][m].length}); } promises2.push(curve.tm.queueAction(task)); } result = await Promise.all(promises2); } const outBuffA = new BigBuffer(zkey.domainSize * curve.Fr.n8); const outBuffB = new BigBuffer(zkey.domainSize * curve.Fr.n8); const outBuffC = new BigBuffer(zkey.domainSize * curve.Fr.n8); let p=0; for (let i=0; i<result.length; i++) { outBuffA.set(result[i][0], p); outBuffB.set(result[i][1], p); outBuffC.set(result[i][2], p); p += result[i][0].byteLength; } return [outBuffA, outBuffB, outBuffC]; function getCutPoint(v) { let m = 0; let n = getUint32(0); while (m < n) { var k = Math.floor((n + m) / 2); const va = getUint32(4 + k*sCoef + 4); if (va > v) { n = k - 1; } else if (va < v) { m = k + 1; } else { n = k; } } return 4 + m*sCoef; } } */ async function joinABC(curve, zkey, a, b, c, logger) { const MAX_CHUNK_SIZE = 1 << 22; const n8 = curve.Fr.n8; const nElements = Math.floor(a.byteLength / curve.Fr.n8); const promises = []; for (let i=0; i<nElements; i += MAX_CHUNK_SIZE) { if (logger) logger.debug(`JoinABC: ${i}/${nElements}`); const n= Math.min(nElements - i, MAX_CHUNK_SIZE); const task = []; const aChunk = a.slice(i*n8, (i + n)*n8 ); const bChunk = b.slice(i*n8, (i + n)*n8 ); const cChunk = c.slice(i*n8, (i + n)*n8 ); task.push({cmd: "ALLOCSET", var: 0, buff: aChunk}); task.push({cmd: "ALLOCSET", var: 1, buff: bChunk}); task.push({cmd: "ALLOCSET", var: 2, buff: cChunk}); task.push({cmd: "ALLOC", var: 3, len: n*n8}); task.push({cmd: "CALL", fnName: "qap_joinABC", params:[ {var: 0}, {var: 1}, {var: 2}, {val: n}, {var: 3}, ]}); task.push({cmd: "CALL", fnName: "frm_batchFromMontgomery", params:[ {var: 3}, {val: n}, {var: 3} ]}); task.push({cmd: "GET", out: 0, var: 3, len: n*n8}); promises.push(curve.tm.queueAction(task)); } const result = await Promise.all(promises); let outBuff; if (a instanceof ffjavascript.BigBuffer) { outBuff = new ffjavascript.BigBuffer(a.byteLength); } else { outBuff = new Uint8Array(a.byteLength); } let p=0; for (let i=0; i<result.length; i++) { outBuff.set(result[i][0], p); p += result[i][0].byteLength; } return outBuff; } /* 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/>. */ const { unstringifyBigInts: unstringifyBigInts$b} = ffjavascript.utils; async function wtnsCalculate(_input, wasmFileName, wtnsFileName, options) { const input = unstringifyBigInts$b(_input); const fdWasm = await fastFile__namespace.readExisting(wasmFileName); const wasm = await fdWasm.read(fdWasm.totalSize); await fdWasm.close(); const wc = await circom_runtime.WitnessCalculatorBuilder(wasm, options); if (wc.circom_version() === 1) { const w = await wc.calculateBinWitness(input); const fdWtns = await binFileUtils__namespace.createBinFile(wtnsFileName, "wtns", 2, 2); await writeBin(fdWtns, w, wc.prime); await fdWtns.close(); } else { const fdWtns = await fastFile__namespace.createOverride(wtnsFileName); const w = await wc.calculateWTNSBin(input); await fdWtns.write(w); await fdWtns.close(); } } /* 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/>. */ const {unstringifyBigInts: unstringifyBigInts$a} = ffjavascript.utils; async function groth16FullProve(_input, wasmFile, zkeyFileName, logger, wtnsCalcOptions, proverOptions) { const input = unstringifyBigInts$a(_input); const wtns= { type: "mem" }; await wtnsCalculate(input, wasmFile, wtns, wtnsCalcOptions); return await groth16Prove(zkeyFileName, wtns, logger, proverOptions); } /* 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/>. */ const {unstringifyBigInts: unstringifyBigInts$9} = ffjavascript.utils; async function groth16Verify(_vk_verifier, _publicSignals, _proof, logger) { /* let cpub = vk_verifier.IC[0]; for (let s= 0; s< vk_verifier.nPublic; s++) { cpub = G1.add( cpub, G1.timesScalar( vk_verifier.IC[s+1], publicSignals[s])); } */ const vk_verifier = unstringifyBigInts$9(_vk_verifier); const proof = unstringifyBigInts$9(_proof); const publicSignals = unstringifyBigInts$9(_publicSignals); const curve = await getCurveFromName(vk_verifier.curve); const IC0 = curve.G1.fromObject(vk_verifier.IC[0]); const IC = new Uint8Array(curve.G1.F.n8*2 * publicSignals.length); const w = new Uint8Array(curve.Fr.n8 * publicSignals.length); if (!publicInputsAreValid$1(curve, publicSignals)) { if (logger) logger.error("Public inputs are not valid."); return false; } for (let i=0; i<publicSignals.length; i++) { const buffP = curve.G1.fromObject(vk_verifier.IC[i+1]); IC.set(buffP, i*curve.G1.F.n8*2); ffjavascript.Scalar.toRprLE(w, curve.Fr.n8*i, publicSignals[i], curve.Fr.n8); } let cpub = await curve.G1.multiExpAffine(IC, w); cpub = curve.G1.add(cpub, IC0); const pi_a = curve.G1.fromObject(proof.pi_a); const pi_b = curve.G2.fromObject(proof.pi_b); const pi_c = curve.G1.fromObject(proof.pi_c); if (!isWellConstructed$1(curve, {pi_a, pi_b, pi_c})) { if(logger) logger.error("Proof commitments are not valid."); return false; } const vk_gamma_2 = curve.G2.fromObject(vk_verifier.vk_gamma_2); const vk_delta_2 = curve.G2.fromObject(vk_verifier.vk_delta_2); const vk_alpha_1 = curve.G1.fromObject(vk_verifier.vk_alpha_1); const vk_beta_2 = curve.G2.fromObject(vk_verifier.vk_beta_2); const res = await curve.pairingEq( curve.G1.neg(pi_a) , pi_b, cpub , vk_gamma_2, pi_c , vk_delta_2, vk_alpha_1, vk_beta_2 ); if (! res) { if (logger) logger.error("Invalid proof"); return false; } if (logger) logger.info("OK!"); return true; } function isWellConstructed$1(curve, proof) { const G1 = curve.G1; const G2 = curve.G2; return G1.isValid(proof.pi_a) && G2.isValid(proof.pi_b) && G1.isValid(proof.pi_c); } function publicInputsAreValid$1(curve, publicInputs) { for(let i = 0; i < publicInputs.length; i++) { if(!ffjavascript.Scalar.lt(publicInputs[i], curve.r)) { return false; } } return true; } /* 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/>. */ const { unstringifyBigInts: unstringifyBigInts$8} = ffjavascript.utils; function p256$2(n) { let nstr = n.toString(16); while (nstr.length < 64) nstr = "0"+nstr; nstr = `"0x${nstr}"`; return nstr; } async function groth16ExportSolidityCallData(_proof, _pub) { const proof = unstringifyBigInts$8(_proof); const pub = unstringifyBigInts$8(_pub); let inputs = ""; for (let i=0; i<pub.length; i++) { if (inputs != "") inputs = inputs + ","; inputs = inputs + p256$2(pub[i]); } let S; S=`[${p256$2(proof.pi_a[0])}, ${p256$2(proof.pi_a[1])}],` + `[[${p256$2(proof.pi_b[0][1])}, ${p256$2(proof.pi_b[0][0])}],[${p256$2(proof.pi_b[1][1])}, ${p256$2(proof.pi_b[1][0])}]],` + `[${p256$2(proof.pi_c[0])}, ${p256$2(proof.pi_c[1])}],` + `[${inputs}]`; return S; } /* 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/>. */ var groth16 =