snarkjs
Version:
zkSNARKs implementation in JavaScript
188 lines (134 loc) • 7.08 kB
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 of the output
// Hash of the last contribution 64 Bytes
// 2^N*2-1 TauG1 Points (uncompressed)
// 2^N TauG2 Points (uncompressed)
// 2^N AlphaTauG1 Points (uncompressed)
// 2^N BetaTauG1 Points (uncompressed)
import Blake2b from "blake2b-wasm";
import * as utils from "./powersoftau_utils.js";
import * as keyPair from "./keypair.js";
import * as binFileUtils from "@iden3/binfileutils";
import * as misc from "./misc.js";
export default async function contribute(oldPtauFilename, newPTauFilename, name, entropy, logger) {
await Blake2b.ready();
const {fd: fdOld, sections} = await binFileUtils.readBinFile(oldPtauFilename, "ptau", 1);
const {curve, power, ceremonyPower} = await utils.readPTauHeader(fdOld, sections);
if (power != ceremonyPower) {
if (logger) logger.error("This file has been reduced. You cannot contribute into a reduced file.");
throw new Error("This file has been reduced. You cannot contribute into a reduced file.");
}
if (sections[12]) {
if (logger) logger.warn("WARNING: Contributing into a file that has phase2 calculated. You will have to prepare phase2 again.");
}
const contributions = await utils.readContributions(fdOld, curve, sections);
const curContribution = {
name: name,
type: 0, // Beacon
};
let lastChallengeHash;
const rng = await misc.getRandomRng(entropy);
if (contributions.length>0) {
lastChallengeHash = contributions[contributions.length-1].nextChallenge;
} else {
lastChallengeHash = utils.calculateFirstChallengeHash(curve, power, logger);
}
// Generate a random key
curContribution.key = keyPair.createPTauKey(curve, lastChallengeHash, rng);
const responseHasher = new Blake2b(64);
responseHasher.update(lastChallengeHash);
const fdNew = await binFileUtils.createBinFile(newPTauFilename, "ptau", 1, 7);
await utils.writePTauHeader(fdNew, curve, power);
const startSections = [];
let firstPoints;
firstPoints = await processSection(2, "G1", (2 ** power) * 2 -1, curve.Fr.e(1), curContribution.key.tau.prvKey, "tauG1" );
curContribution.tauG1 = firstPoints[1];
firstPoints = await processSection(3, "G2", (2 ** power) , curve.Fr.e(1), curContribution.key.tau.prvKey, "tauG2" );
curContribution.tauG2 = firstPoints[1];
firstPoints = await processSection(4, "G1", (2 ** power) , curContribution.key.alpha.prvKey, curContribution.key.tau.prvKey, "alphaTauG1" );
curContribution.alphaG1 = firstPoints[0];
firstPoints = await processSection(5, "G1", (2 ** power) , curContribution.key.beta.prvKey, curContribution.key.tau.prvKey, "betaTauG1" );
curContribution.betaG1 = firstPoints[0];
firstPoints = await processSection(6, "G2", 1, curContribution.key.beta.prvKey, curContribution.key.tau.prvKey, "betaTauG2" );
curContribution.betaG2 = firstPoints[0];
curContribution.partialHash = responseHasher.getPartialHash();
const buffKey = new Uint8Array(curve.F1.n8*2*6+curve.F2.n8*2*3);
utils.toPtauPubKeyRpr(buffKey, 0, curve, curContribution.key, false);
responseHasher.update(new Uint8Array(buffKey));
const hashResponse = responseHasher.digest();
if (logger) logger.info(misc.formatHash(hashResponse, "Contribution Response Hash imported: "));
const nextChallengeHasher = new Blake2b(64);
nextChallengeHasher.update(hashResponse);
await hashSection(fdNew, "G1", 2, (2 ** power) * 2 -1, "tauG1");
await hashSection(fdNew, "G2", 3, (2 ** power) , "tauG2");
await hashSection(fdNew, "G1", 4, (2 ** power) , "alphaTauG1");
await hashSection(fdNew, "G1", 5, (2 ** power) , "betaTauG1");
await hashSection(fdNew, "G2", 6, 1 , "betaG2");
curContribution.nextChallenge = nextChallengeHasher.digest();
if (logger) logger.info(misc.formatHash(curContribution.nextChallenge, "Next Challenge Hash: "));
contributions.push(curContribution);
await utils.writeContributions(fdNew, curve, contributions);
await fdOld.close();
await fdNew.close();
return hashResponse;
async function processSection(sectionId, groupName, NPoints, first, inc, sectionName) {
const res = [];
fdOld.pos = sections[sectionId][0].p;
await binFileUtils.startWriteSection(fdNew, sectionId);
startSections[sectionId] = fdNew.pos;
const G = curve[groupName];
const sG = G.F.n8*2;
const chunkSize = Math.floor((1<<20) / sG); // 128Mb chunks
let t = first;
for (let i=0 ; i<NPoints ; i+= chunkSize) {
if (logger) logger.debug(`processing: ${sectionName}: ${i}/${NPoints}`);
const n= Math.min(NPoints-i, chunkSize );
const buffIn = await fdOld.read(n * sG);
const buffOutLEM = await G.batchApplyKey(buffIn, t, inc);
/* Code to test the case where we don't have the 2^m-2 component
if (sectionName== "tauG1") {
const bz = new Uint8Array(64);
buffOutLEM.set(bz, 64*((2 ** power) - 1 ));
}
*/
const promiseWrite = fdNew.write(buffOutLEM);
const buffOutC = await G.batchLEMtoC(buffOutLEM);
responseHasher.update(buffOutC);
await promiseWrite;
if (i==0) // Return the 2 first points.
for (let j=0; j<Math.min(2, NPoints); j++)
res.push(G.fromRprLEM(buffOutLEM, j*sG));
t = curve.Fr.mul(t, curve.Fr.exp(inc, n));
}
await binFileUtils.endWriteSection(fdNew);
return res;
}
async function hashSection(fdTo, groupName, sectionId, nPoints, sectionName) {
const G = curve[groupName];
const sG = G.F.n8*2;
const nPointsChunk = Math.floor((1<<24)/sG);
const oldPos = fdTo.pos;
fdTo.pos = startSections[sectionId];
for (let i=0; i< nPoints; i += nPointsChunk) {
if ((logger)&&i) logger.debug(`Hashing ${sectionName}: ` + i);
const n = Math.min(nPoints-i, nPointsChunk);
const buffLEM = await fdTo.read(n * sG);
const buffU = await G.batchLEMtoU(buffLEM);
nextChallengeHasher.update(buffU);
}
fdTo.pos = oldPos;
}
}