diff --git a/cli.js b/cli.js index 698a80e..7e06a8b 100755 --- a/cli.js +++ b/cli.js @@ -38,11 +38,13 @@ const printR1cs = require("./src/printr1cs"); const clProcessor = require("./src/clprocessor"); -const powersOfTaw = require("./src/powersoftaw"); +const powersOfTaw = require("./src/powersoftau"); const bn128 = require("ffjavascript").bn128; const solidityGenerator = require("./src/soliditygenerator.js"); +const phase2 = require("./src/phase2"); + const commands = [ { cmd: "r1cs info [circuit.r1cs]", @@ -103,62 +105,69 @@ const commands = [ action: solidityGenCall }, { - cmd: "powersoftaw new [powersoftaw_0000.ptaw]", - description: "Starts a powers of taw ceremony", + cmd: "powersoftau new [powersoftau_0000.ptau]", + description: "Starts a powers of tau ceremony", alias: ["ptn"], options: "-verbose|v", action: powersOfTawNew }, { - cmd: "powersoftaw export challange [challange]", + cmd: "powersoftau export challange [challange]", description: "Creates a challange", alias: ["pte"], options: "-verbose|v", action: powersOfTawExportChallange }, { - cmd: "powersoftaw challange contribute [response]", + cmd: "powersoftau challange contribute [response]", description: "Contribute to a challange", alias: ["ptcc"], options: "-verbose|v -entropy|e", action: powersOfTawChallangeContribute }, { - cmd: "powersoftaw import <", - description: "import a response to a ptaw file", + cmd: "powersoftau import <", + description: "import a response to a ptau file", alias: ["pti"], options: "-verbose|v -nopoints -nocheck -description|d -name|n", action: powersOfTawImport }, { - cmd: "powersoftaw verify ", + cmd: "powersoftau verify ", description: "verifies a powers of tau file", alias: ["ptv"], options: "-verbose|v", action: powersOfTawVerify }, { - cmd: "powersoftaw beacon ", + cmd: "powersoftau beacon ", description: "adds a beacon", alias: ["ptb"], options: "-verbose|v -name|n", action: powersOfTawBeacon }, { - cmd: "powersoftaw contribute ", + cmd: "powersoftau contribute ", description: "verifies a powers of tau file", alias: ["ptc"], options: "-verbose|v -name|n -entropy|e", action: powersOfTawContribute }, { - cmd: "powersoftaw prepare phase2 ", + cmd: "powersoftau prepare phase2 ", description: "Prepares phase 2. ", longDescription: " This process calculates the evaluation of the Lagrange polinomials at tau for alpha*tau and beta tau", alias: ["pt2"], options: "-verbose|v", action: powersOfTawPreparePhase2 }, + { + cmd: "phase2 new [circuit.r1cs] [powersoftau.ptau] [circuit.zkey]", + description: "Creates an initial pkey file with zero contributions ", + alias: ["p2n"], + options: "-verbose|v", + action: phase2new + }, ]; @@ -504,7 +513,7 @@ async function solidityGenCall(params, options) { async function powersOfTawNew(params, options) { let power; - let ptawName; + let ptauName; power = parseInt(params[0]); if ((power<1) || (power>28)) { @@ -512,19 +521,19 @@ async function powersOfTawNew(params, options) { } if (params.length < 2) { - ptawName = "powersOfTaw" + power + "_0000.ptaw"; + ptauName = "powersOfTaw" + power + "_0000.ptau"; } else { - ptawName = params[1]; + ptauName = params[1]; } - return await powersOfTaw.newAccumulator(bn128, power, ptawName, options.verbose); + return await powersOfTaw.newAccumulator(bn128, power, ptauName, options.verbose); } async function powersOfTawExportChallange(params, options) { - let ptawName; + let ptauName; let challangeName; - ptawName = params[0]; + ptauName = params[0]; if (params.length < 2) { challangeName = "challange"; @@ -532,7 +541,7 @@ async function powersOfTawExportChallange(params, options) { challangeName = params[1]; } - return await powersOfTaw.exportChallange(ptawName, challangeName, options.verbose); + return await powersOfTaw.exportChallange(ptauName, challangeName, options.verbose); } @@ -623,3 +632,30 @@ async function powersOfTawPreparePhase2(params, options) { return await powersOfTaw.preparePhase2(oldPtauName, newPtauName, options.verbose); } + +// phase2 new +async function phase2new(params, options) { + let r1csName; + let ptauName; + let zkeyName; + + if (params.length < 1) { + r1csName = "circuit.r1cs"; + } else { + r1csName = params[0]; + } + + if (params.length < 2) { + ptauName = "powersoftau.ptau"; + } else { + ptauName = params[1]; + } + + if (params.length < 2) { + zkeyName = "circuit.zkey"; + } else { + zkeyName = params[2]; + } + + return phase2.new(r1csName, ptauName, zkeyName, options.verbose); +} diff --git a/src/binfileutils.js b/src/binfileutils.js index c4db735..16a7c72 100644 --- a/src/binfileutils.js +++ b/src/binfileutils.js @@ -88,7 +88,7 @@ async function endReadSection(fd, noCheck) { async function writeBigInt(fd, n, n8) { const buff = new Uint8Array(n8); - Scalar.toRprLE(buff, 0, n); + Scalar.toRprLE(buff, 0, n, n8); await fd.write(buff); } diff --git a/src/phase2.js b/src/phase2.js new file mode 100644 index 0000000..3bd56ab --- /dev/null +++ b/src/phase2.js @@ -0,0 +1,3 @@ + + +module.exports.new = require("./phase2_new.js"); diff --git a/src/phase2_new.js b/src/phase2_new.js new file mode 100644 index 0000000..b4ca2cc --- /dev/null +++ b/src/phase2_new.js @@ -0,0 +1,191 @@ + +const loadR1cs = require("r1csfile").load; +const utils = require("./powersoftau_utils"); +const binFileUtils = require("./binfileutils"); +const writeZKey = require("./zkeyfile").write; +const assert = require("assert"); + + +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 ) ); +} + + +module.exports = async function phase2new(r1csName, ptauName, zkeyName) { + + const r1cs = await loadR1cs(r1csName, true); + + const {fd: ptauFd, sections} = await binFileUtils.readBinFile(ptauName, "ptau", 1); + const {curve, power} = await utils.readPTauHeader(ptauFd, sections); + + if (r1cs.prime != curve.r) { + console.log("r1cs curve does not match powers of tau ceremony curve"); + return -1; + } + + const cirPower = log2(r1cs.nConstraints + r1cs.nPubInputs + r1cs.nOutputs +1 -1) +1; + + if (cirPower > power) { + console.log(`circuit too big for this power of tau ceremony. ${r1cs.nConstraints} > 2**${power}`); + return -1; + } + + if (!sections[12]) { + console.log("Powers of tau is not prepared."); + return -1; + } + + const zKey = { + + nPublic: r1cs.nOutputs + r1cs.nPubInputs, + nVars: r1cs.nVars, + q: curve.q, + r: curve.r, + domainBits: cirPower, + domainSize: 1 << cirPower + + }; + + const linc = 1 << (power - cirPower); + + calculatePolinomials(curve, zKey,r1cs); + + zKey.A = new Array(r1cs.nVars); + zKey.B1 = new Array(r1cs.nVars); + zKey.B2 = new Array(r1cs.nVars); + zKey.C = new Array(r1cs.nVars); + zKey.IC = new Array(zKey.nPublic+1); + for (let i=0; izKey.nPublic) { + zKey.C[i] = curve.G1.zero; + } else { + zKey.IC[i] = curve.G1.zero; + } + } + for (let i=0; i { + zKey.ccoefs.push({ + matrix: m, + constraint: c, + signal: s, + value: r1cs.constraints[c][m][s] + }); + }); + } + } + + /** + * add and process the constraints + * input_i * 0 = 0 + * to ensure soundness of input consistency + */ + for (let i = 0; i < r1cs.nPubInputs + r1cs.nOutputs + 1; ++i) + { + zKey.ccoefs.push({ + matrix: 0, + constraint: r1cs.nConstraints + i, + signal: i, + value: curve.Fr.one + }); + } +} diff --git a/src/powersoftaw.js b/src/powersoftau.js similarity index 100% rename from src/powersoftaw.js rename to src/powersoftau.js diff --git a/src/powersoftau_verify.js b/src/powersoftau_verify.js index 470d07f..f60a6d6 100644 --- a/src/powersoftau_verify.js +++ b/src/powersoftau_verify.js @@ -3,20 +3,21 @@ const utils = require("./powersoftau_utils"); const keyPair = require("./keypair"); const assert = require("assert"); const crypto = require("crypto"); -const buildTaskManager = require("./taskmanager"); const binFileUtils = require("./binfileutils"); const ChaCha = require("ffjavascript").ChaCha; -function sameRatio(curve, g1s, g1sx, g2s, g2sx) { +async function sameRatio(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)); + // 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 verifyContribution(curve, cur, prev) { - +async function verifyContribution(curve, cur, prev) { + let sr; if (cur.type == 1) { // Verify the beacon. const beaconKey = utils.keyFromBeacon(curve, prev.nextChallange, cur.beaconHash, cur.numIterationsExp); @@ -64,42 +65,50 @@ function verifyContribution(curve, cur, prev) { cur.key.alpha.g2_sp = keyPair.getG2sp(1, prev.nextChallange, cur.key.alpha.g1_s, cur.key.alpha.g1_sx); cur.key.beta.g2_sp = keyPair.getG2sp(2, prev.nextChallange, cur.key.beta.g1_s, cur.key.beta.g1_sx); - if (!sameRatio(curve, cur.key.tau.g1_s, cur.key.tau.g1_sx, cur.key.tau.g2_sp, cur.key.tau.g2_spx)) { + sr = await sameRatio(curve, cur.key.tau.g1_s, cur.key.tau.g1_sx, cur.key.tau.g2_sp, cur.key.tau.g2_spx); + if (sr !== true) { console.log("INVALID key (tau) in challange #"+cur.id); return false; } - if (!sameRatio(curve, cur.key.alpha.g1_s, cur.key.alpha.g1_sx, cur.key.alpha.g2_sp, cur.key.alpha.g2_spx)) { + sr = await sameRatio(curve, cur.key.alpha.g1_s, cur.key.alpha.g1_sx, cur.key.alpha.g2_sp, cur.key.alpha.g2_spx); + if (sr !== true) { console.log("INVALID key (alpha) in challange #"+cur.id); return false; } - if (!sameRatio(curve, cur.key.beta.g1_s, cur.key.beta.g1_sx, cur.key.beta.g2_sp, cur.key.beta.g2_spx)) { + sr = await sameRatio(curve, cur.key.beta.g1_s, cur.key.beta.g1_sx, cur.key.beta.g2_sp, cur.key.beta.g2_spx); + if (sr !== true) { console.log("INVALID key (beta) in challange #"+cur.id); return false; } - if (!sameRatio(curve, prev.tauG1, cur.tauG1, cur.key.tau.g2_sp, cur.key.tau.g2_spx)) { + sr = await sameRatio(curve, prev.tauG1, cur.tauG1, cur.key.tau.g2_sp, cur.key.tau.g2_spx); + if (sr !== true) { console.log("INVALID tau*G1. challange #"+cur.id+" It does not follow the previous contribution"); return false; } - if (!sameRatio(curve, cur.key.tau.g1_s, cur.key.tau.g1_sx, prev.tauG2, cur.tauG2,)) { + sr = await sameRatio(curve, cur.key.tau.g1_s, cur.key.tau.g1_sx, prev.tauG2, cur.tauG2); + if (sr !== true) { console.log("INVALID tau*G2. challange #"+cur.id+" It does not follow the previous contribution"); return false; } - if (!sameRatio(curve, prev.alphaG1, cur.alphaG1, cur.key.alpha.g2_sp, cur.key.alpha.g2_spx)) { + sr = await sameRatio(curve, prev.alphaG1, cur.alphaG1, cur.key.alpha.g2_sp, cur.key.alpha.g2_spx); + if (sr !== true) { console.log("INVALID alpha*G1. challange #"+cur.id+" It does not follow the previous contribution"); return false; } - if (!sameRatio(curve, prev.betaG1, cur.betaG1, cur.key.beta.g2_sp, cur.key.beta.g2_spx)) { + sr = await sameRatio(curve, prev.betaG1, cur.betaG1, cur.key.beta.g2_sp, cur.key.beta.g2_spx); + if (sr !== true) { console.log("INVALID beta*G1. challange #"+cur.id+" It does not follow the previous contribution"); return false; } - if (!sameRatio(curve, cur.key.beta.g1_s, cur.key.beta.g1_sx, prev.betaG2, cur.betaG2,)) { + sr = await sameRatio(curve, cur.key.beta.g1_s, cur.key.beta.g1_sx, prev.betaG2, cur.betaG2); + if (sr !== true) { console.log("INVALID beta*G2. challange #"+cur.id+"It does not follow the previous contribution"); return false; } @@ -108,6 +117,7 @@ function verifyContribution(curve, cur, prev) { } async function verify(tauFilename, verbose) { + let sr; await Blake2b.ready(); const {fd, sections} = await binFileUtils.readBinFile(tauFilename, "ptau", 1); @@ -141,7 +151,7 @@ async function verify(tauFilename, verbose) { } const curContr = contrs[contrs.length-1]; if (verbose) console.log("Validating contribution #"+contrs[contrs.length-1].id); - const res = verifyContribution(curve, curContr,prevContr, verbose); + const res = await verifyContribution(curve, curContr,prevContr, verbose); if (!res) return false; @@ -156,7 +166,8 @@ async function verify(tauFilename, verbose) { // Verify Section tau*G1 if (verbose) console.log("Verifying powers in tau*G1 section"); const rTau1 = await processSection(2, "G1", "tauG1", (1 << power)*2-1, [0, 1]); - if (!sameRatio(curve, rTau1.R1, rTau1.R2, curve.G2.g, curContr.tauG2)) { + sr = await sameRatio(curve, rTau1.R1, rTau1.R2, curve.G2.g, curContr.tauG2); + if (sr !== true) { console.log("tauG1 section. Powers do not match"); return false; } @@ -174,7 +185,8 @@ async function verify(tauFilename, verbose) { // Verify Section tau*G2 if (verbose) console.log("Verifying powers in tau*G2 section"); const rTau2 = await processSection(3, "G2", "tauG2", 1 << power, [0, 1]); - if (!sameRatio(curve, curve.G1.g, curContr.tauG1, rTau2.R1, rTau2.R2)) { + sr = await sameRatio(curve, curve.G1.g, curContr.tauG1, rTau2.R1, rTau2.R2); + if (sr !== true) { console.log("tauG2 section. Powers do not match"); return false; } @@ -190,7 +202,8 @@ async function verify(tauFilename, verbose) { // Verify Section alpha*tau*G1 if (verbose) console.log("Verifying powers in alpha*tau*G1 section"); const rAlphaTauG1 = await processSection(4, "G1", "alphatauG1", 1 << power, [0]); - if (!sameRatio(curve, rAlphaTauG1.R1, rAlphaTauG1.R2, curve.G2.g, curContr.tauG2)) { + sr = await sameRatio(curve, rAlphaTauG1.R1, rAlphaTauG1.R2, curve.G2.g, curContr.tauG2); + if (sr !== true) { console.log("alphaTauG1 section. Powers do not match"); return false; } @@ -202,7 +215,8 @@ async function verify(tauFilename, verbose) { // Verify Section beta*tau*G1 if (verbose) console.log("Verifying powers in beta*tau*G1 section"); const rBetaTauG1 = await processSection(5, "G1", "betatauG1", 1 << power, [0]); - if (!sameRatio(curve, rBetaTauG1.R1, rBetaTauG1.R2, curve.G2.g, curContr.tauG2)) { + sr = await sameRatio(curve, rBetaTauG1.R1, rBetaTauG1.R2, curve.G2.g, curContr.tauG2); + if (sr !== true) { console.log("betaTauG1 section. Powers do not match"); return false; } @@ -238,7 +252,7 @@ async function verify(tauFilename, verbose) { for (let i = contrs.length-2; i>=0; i--) { const curContr = contrs[i]; const prevContr = (curContr>0) ? contrs[i-1] : initialContribution; - verifyContribution(curve, curContr, prevContr); + await verifyContribution(curve, curContr, prevContr); printContribution(curContr, prevContr); } console.log("-----------------------------------------------------"); diff --git a/src/zkeyfile.js b/src/zkeyfile.js index cae1144..2192024 100644 --- a/src/zkeyfile.js +++ b/src/zkeyfile.js @@ -32,10 +32,18 @@ const Scalar = require("ffjavascript").Scalar; const F1Field = require("ffjavascript").F1Field; const assert = require("assert"); const binFileUtils = require("./binfileutils"); +const bn128 = require("ffjavascript").bn128; module.exports.write = async function writeZKey(fileName, zkey) { - const fd = await binFileUtils.createOverride(fileName,"zkey", 6, 1); + let curve; + if (Scalar.eq(zkey.q, bn128.q)) { + curve = bn128; + } else { + assert(false, fd.fileName +": Curve not supported"); + } + + const fd = await binFileUtils.createBinFile(fileName,"zkey", 1, 9); // Write the header /////////// @@ -48,20 +56,17 @@ module.exports.write = async function writeZKey(fileName, zkey) { await binFileUtils.startWriteSection(fd, 2); const primeQ = zkey.q; - const Fq = new F1Field(zkey.q); const n8q = (Math.floor( (Scalar.bitLength(primeQ) - 1) / 64) +1)*8; - const Rq = Scalar.mod(Scalar.shl(1, n8q*8), primeQ); const primeR = zkey.r; - const Fr = new F1Field(zkey.r); const n8r = (Math.floor( (Scalar.bitLength(primeR) - 1) / 64) +1)*8; const Rr = Scalar.mod(Scalar.shl(1, n8r*8), primeR); const R2r = Scalar.mod(Scalar.mul(Rr,Rr), primeR); await fd.writeULE32(n8q); - await binFileUtils.writeBigInt(primeQ, n8q); + await binFileUtils.writeBigInt(fd, primeQ, n8q); await fd.writeULE32(n8r); - await binFileUtils.writeBigInt(primeR, 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 @@ -98,24 +103,43 @@ module.exports.write = async function writeZKey(fileName, zkey) { await binFileUtils.endWriteSection(fd); - // Write A B1 B2 C points + + // Write A /////////// await binFileUtils.startWriteSection(fd, 5); for (let i=0; i