snarkjs/src/powersoftau_utils.js

394 lines
11 KiB
JavaScript
Raw Normal View History

2020-05-10 11:10:42 +03:00
const fastFile = require("fastfile");
const assert = require("assert");
2020-05-11 21:23:04 +03:00
const Scalar = require("ffjavascript").Scalar;
const bn128 = require("ffjavascript").bn128;
const Blake2b = require("blake2b-wasm");
2020-05-15 22:30:37 +03:00
const ChaCha = require("ffjavascript").ChaCha;
const keyPair = require("./keypair");
const crypto = require("crypto");
2020-05-10 11:10:42 +03:00
2020-05-26 19:45:49 +03:00
async function writePTauHeader(fd, curve, power, ceremonyPower) {
2020-05-11 21:23:04 +03:00
// Write the header
///////////
2020-05-26 19:45:49 +03:00
if (typeof(ceremonyPower) === "undefined") ceremonyPower = power;
2020-05-11 21:23:04 +03:00
await fd.writeULE32(1); // Header type
const pHeaderSize = fd.pos;
await fd.writeULE64(0); // Temporally set to 0 length
await fd.writeULE32(curve.F1.n64*8);
2020-05-26 19:45:49 +03:00
const buff = new Uint8Array(curve.F1.n8);
2020-05-11 21:23:04 +03:00
Scalar.toRprLE(buff, 0, curve.q, curve.F1.n8);
await fd.write(buff);
await fd.writeULE32(power); // power
2020-05-26 19:45:49 +03:00
await fd.writeULE32(ceremonyPower); // power
2020-05-11 21:23:04 +03:00
const headerSize = fd.pos - pHeaderSize - 8;
const oldPos = fd.pos;
fd.writeULE64(headerSize, pHeaderSize);
fd.pos = oldPos;
}
async function readPTauHeader(fd, sections) {
if (!sections[1]) assert(false, fd.fileName + ": File has no header");
if (sections[1].length>1) assert(false, fd.fileName +": File has more than one header");
fd.pos = sections[1][0].p;
const n8 = await fd.readULE32();
const buff = await fd.read(n8);
const q = Scalar.fromRprLE(buff);
let curve;
if (Scalar.eq(q, bn128.q)) {
curve = bn128;
} else {
assert(false, fd.fileName +": Curve not supported");
}
assert(curve.F1.n64*8 == n8, fd.fileName +": Invalid size");
const power = await fd.readULE32();
2020-05-26 19:45:49 +03:00
const ceremonyPower = await fd.readULE32();
2020-05-11 21:23:04 +03:00
assert.equal(fd.pos-sections[1][0].p, sections[1][0].size);
2020-05-26 19:45:49 +03:00
return {curve, power, ceremonyPower};
2020-05-11 21:23:04 +03:00
}
async function readPtauPubKey(fd, curve, montgomery) {
const buff = await fd.read(curve.F1.n8*2*6 + curve.F2.n8*2*3);
return fromPtauPubKeyRpr(buff, 0, curve, montgomery);
}
function fromPtauPubKeyRpr(buff, pos, curve, montgomery) {
const key = {
tau: {},
alpha: {},
beta: {}
};
key.tau.g1_s = readG1();
key.tau.g1_sx = readG1();
key.alpha.g1_s = readG1();
key.alpha.g1_sx = readG1();
key.beta.g1_s = readG1();
key.beta.g1_sx = readG1();
key.tau.g2_spx = readG2();
key.alpha.g2_spx = readG2();
key.beta.g2_spx = readG2();
return key;
function readG1() {
let p;
if (montgomery) {
p = curve.G1.fromRprLEM( buff, pos );
} else {
p = curve.G1.fromRprBE( buff, pos );
}
pos += curve.G1.F.n8*2;
return p;
}
function readG2() {
let p;
if (montgomery) {
p = curve.G2.fromRprLEM( buff, pos );
} else {
p = curve.G2.fromRprBE( buff, pos );
}
pos += curve.G2.F.n8*2;
return p;
}
}
function toPtauPubKeyRpr(buff, pos, curve, key, montgomery) {
writeG1(key.tau.g1_s);
writeG1(key.tau.g1_sx);
writeG1(key.alpha.g1_s);
writeG1(key.alpha.g1_sx);
writeG1(key.beta.g1_s);
writeG1(key.beta.g1_sx);
writeG2(key.tau.g2_spx);
writeG2(key.alpha.g2_spx);
writeG2(key.beta.g2_spx);
async function writeG1(p) {
if (montgomery) {
curve.G1.toRprLEM(buff, pos, p);
} else {
curve.G1.toRprBE(buff, pos, p);
}
pos += curve.F1.n8*2;
}
async function writeG2(p) {
if (montgomery) {
curve.G2.toRprLEM(buff, pos, p);
} else {
curve.G2.toRprBE(buff, pos, p);
}
pos += curve.F2.n8*2;
}
return buff;
2020-05-10 11:10:42 +03:00
}
2020-05-11 21:23:04 +03:00
async function writePtauPubKey(fd, curve, key, montgomery) {
2020-05-26 19:45:49 +03:00
const buff = new Uint8Array(curve.F1.n8*2*6 + curve.F2.n8*2*3);
2020-05-11 21:23:04 +03:00
toPtauPubKeyRpr(buff, 0, curve, key, montgomery);
await fd.write(buff);
}
async function readContribution(fd, curve) {
const c = {};
c.tauG1 = await readG1();
c.tauG2 = await readG2();
c.alphaG1 = await readG1();
c.betaG1 = await readG1();
c.betaG2 = await readG2();
c.key = await readPtauPubKey(fd, curve, true);
2020-05-26 19:45:49 +03:00
c.partialHash = await fd.read(216);
c.nextChallange = await fd.read(64);
2020-05-15 22:30:37 +03:00
c.type = await fd.readULE32();
2020-05-26 19:45:49 +03:00
const buffV = new Uint8Array(curve.G1.F.n8*2*6+curve.G2.F.n8*2*3);
toPtauPubKeyRpr(buffV, 0, curve, c.key, false);
const responseHasher = Blake2b(64);
responseHasher.setPartialHash(c.partialHash);
responseHasher.update(buffV);
c.responseHash = responseHasher.digest();
2020-05-15 22:30:37 +03:00
const paramLength = await fd.readULE32();
const curPos = fd.pos;
let lastType =0;
while (fd.pos-curPos < paramLength) {
const buffType = await readDV(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 readDV(1);
const buffStr = await readDV(buffLen[0]);
c.name = new TextDecoder().decode(buffStr);
} else if (buffType[0]==2) {
const buffExp = await readDV(1);
c.numIterationsExp = buffExp[0];
} else if (buffType[0]==3) {
const buffLen = await readDV(1);
c.beaconHash = await readDV(buffLen[0]);
} else {
throw new Error("Parameter not recognized");
}
}
if (fd.pos != curPos + paramLength) {
throw new Error("Parametes do not match");
}
2020-05-11 21:23:04 +03:00
return c;
async function readG1() {
const pBuff = await fd.read(curve.F1.n8*2);
return curve.G1.fromRprLEM( pBuff );
}
async function readG2() {
const pBuff = await fd.read(curve.F2.n8*2);
return curve.G2.fromRprLEM( pBuff );
}
2020-05-15 22:30:37 +03:00
async function readDV(n) {
const b = await fd.read(n);
return new Uint8Array(b);
}
2020-05-11 21:23:04 +03:00
}
async function readContributions(fd, curve, sections) {
if (!sections[7]) assert(false, fd.fileName + ": File has no contributions");
if (sections[7][0].length>1) assert(false, fd.fileName +": File has more than one contributions section");
fd.pos = sections[7][0].p;
const nContributions = await fd.readULE32();
const contributions = [];
for (let i=0; i<nContributions; i++) {
const c = await readContribution(fd, curve);
c.id = i+1;
contributions.push(c);
}
assert.equal(fd.pos-sections[7][0].p, sections[7][0].size);
return contributions;
}
async function writeContribution(fd, curve, contribution) {
2020-05-26 19:45:49 +03:00
const buffG1 = new Uint8Array(curve.F1.n8*2);
const buffG2 = new Uint8Array(curve.F2.n8*2);
2020-05-11 21:23:04 +03:00
await writeG1(contribution.tauG1);
await writeG2(contribution.tauG2);
await writeG1(contribution.alphaG1);
await writeG1(contribution.betaG1);
await writeG2(contribution.betaG2);
await writePtauPubKey(fd, curve, contribution.key, true);
await fd.write(contribution.partialHash);
await fd.write(contribution.nextChallange);
2020-05-15 22:30:37 +03:00
await fd.writeULE32(contribution.type || 0);
const params = [];
if (contribution.name) {
params.push(1); // Param Name
const nameData = new TextEncoder("utf-8").encode(contribution.name.substring(0,64));
params.push(nameData.byteLength);
for (let i=0; i<nameData.byteLength; i++) params.push(nameData[i]);
}
if (contribution.type == 1) {
params.push(2); // Param numIterationsExp
params.push(contribution.numIterationsExp);
params.push(3); // Beacon Hash
params.push(contribution.beaconHash.byteLength);
for (let i=0; i<contribution.beaconHash.byteLength; i++) params.push(contribution.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);
}
2020-05-11 21:23:04 +03:00
async function writeG1(p) {
curve.G1.toRprLEM(buffG1, 0, p);
await fd.write(buffG1);
}
async function writeG2(p) {
curve.G2.toRprLEM(buffG2, 0, p);
await fd.write(buffG2);
}
}
async function writeContributions(fd, curve, contributions) {
await fd.writeULE32(7); // Header type
const pContributionsSize = fd.pos;
await fd.writeULE64(0); // Temporally set to 0 length
await fd.writeULE32(contributions.length);
for (let i=0; i< contributions.length; i++) {
await writeContribution(fd, curve, contributions[i]);
}
const contributionsSize = fd.pos - pContributionsSize - 8;
const oldPos = fd.pos;
fd.writeULE64(contributionsSize, pContributionsSize);
fd.pos = oldPos;
2020-05-10 11:10:42 +03:00
}
2020-05-11 21:23:04 +03:00
function formatHash(b) {
const a = new DataView(b.buffer);
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 += " ";
2020-05-15 22:30:37 +03:00
S += a.getUint32(i*16+j*4).toString(16).padStart(8, "0");
2020-05-11 21:23:04 +03:00
}
}
return S;
2020-05-10 11:10:42 +03:00
}
2020-05-11 21:23:04 +03:00
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 calculateFirstChallangeHash(curve, power) {
const hasher = new Blake2b(64);
2020-05-26 19:45:49 +03:00
const vG1 = new Uint8Array(curve.G1.F.n8*2);
const vG2 = new Uint8Array(curve.G2.F.n8*2);
curve.G1.toRprBE(vG1, 0, curve.G1.g);
curve.G2.toRprBE(vG2, 0, curve.G2.g);
2020-05-11 21:23:04 +03:00
const blankHasher = new Blake2b(64);
hasher.update(blankHasher.digest());
let n;
n=(1 << power)*2 -1;
for (let i=0; i<n; i++) hasher.update(vG1);
n= 1 << power;
for (let i=0; i<n; i++) hasher.update(vG2);
for (let i=0; i<n; i++) hasher.update(vG1);
for (let i=0; i<n; i++) hasher.update(vG1);
hasher.update(vG2);
return hasher.digest();
}
2020-05-15 22:30:37 +03:00
function keyFromBeacon(curve, challangeHash, 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 = crypto.createHash("sha256").update(curHash).digest();
}
}
const curHashV = new DataView(curHash.buffer);
const seed = [];
for (let i=0; i<8; i++) {
seed[i] = curHashV.getUint32(i*4, false);
}
const rng = new ChaCha(seed);
const key = keyPair.createPTauKey(curve, challangeHash, rng);
return key;
}
2020-05-11 21:23:04 +03:00
module.exports.readPTauHeader = readPTauHeader;
module.exports.writePTauHeader = writePTauHeader;
module.exports.readPtauPubKey = readPtauPubKey;
module.exports.writePtauPubKey = writePtauPubKey;
module.exports.formatHash = formatHash;
module.exports.readContributions = readContributions;
module.exports.writeContributions = writeContributions;
module.exports.hashIsEqual = hashIsEqual;
module.exports.calculateFirstChallangeHash = calculateFirstChallangeHash;
module.exports.toPtauPubKeyRpr = toPtauPubKeyRpr;
module.exports.fromPtauPubKeyRpr = fromPtauPubKeyRpr;
2020-05-15 22:30:37 +03:00
module.exports.keyFromBeacon = keyFromBeacon;
2020-05-11 21:23:04 +03:00