arnaucube
arnaucube/snarkjsPublic Self-hosted repository
snarks part done
Jordi Baylina jordi@baylina.cat
Thu, 09 Aug 2018 15:31:16 +0200
commit

c0312e5d83502022572d88b5a087d5878a251a6b

parent

e03abd7cf9e474bc5b325c12e8f4cb476097691b

A README

@@ -0,0 +1,107 @@

+# javascript implementation of zkSnark + +This is a javascript implementation of zkSnarks. + +This library allows to do the trusted setup, generate proofs and verify the proofs. + +This library uses the compiled circuits generated by the jaz compiler. + +## Install + +``` +npm install zkSnark +``` + +## Usage + +### import + +``` +const zkSnark = require("zksnark"); +``` + +### Load a circuit. + +``` +// "myCircuit.cir" is the output of the jaz compiler + +const circuitDef = JSON.parse(fs.readFileSync("myCircuit.cir", "utf8")); +const circuit = new zkSnark.Circuit(circuitDef); +``` + +### Inspect the circuit. + +``` + // `signalId` can always be a number or an alias string + + circuit.m; // number of constrains + circuit.n; // number of signals + circuit.p; // number of public signals (nPublicInputs + nOutputs) + + // The array of signals is always sorted in this order: + // [ outputs, publicInputs, privedInputs, internalSignals, constants] + + // returns a,b and c coeficients of the `signalId` on a given `constrain` + circuit.a(constrain, signalId) + circuit.b(constrain, signalId) + circuit.c(constrain, signalId) + + circuit.nOutputs // number of public outputs + circuit.nPublicInputs // number of public inputs + circuit.nPrivateInputs // number of private inputs + circuit.nInputs // number of inputs ( nPublicInputs + nPrivateInputs) + + circuit.outputIdx(i) // returns the index of the i'th output + circuit.inputIdx(i) // returns the index of the i'th input + circuit.inputPublicIdx(i) // returns the index of the i'th public input + circuit.inputPrivateIdx(i) // returns the index of the i'th private input + + // returns signal Idx given a signalId + // if the idx >= n , it is a constant + // if the idx == -1, the signal does not exist + circuit.signalId2idx(signalId); + + // returns an array aliases names for a given signalId + circuit.signalNames(signalId) + + // input is a key value object where keys are the signal names + // of all the inputs (public and private) + // returns an array of values that represent the witness + circuit.generateWitness(input) +``` + +### Trusted setup + +``` +const setup = zkSnark.setup(circuit); +fs.writeFileSink("myCircuit.vk_proof", JSON.stringify(setup.vk_proof), "utf8"); +fs.writeFileSink("myCircuit.vk_verifier", JSON.stringify(setup.vk_verifier), "utf8"); +setup.toxic // Must be discarded. +``` + +### Generate proof + +``` +const circuitDef = JSON.parse(fs.readFileSync("myCircuit.cir", "utf8")); +const circuit = new zkSnark.Circuit(circuitDef); +const input = { + "main.pubIn1": "123", + "main.out1": "456" +} +const witness = circuit.generateWitness(input); +const vk_proof = JSON.parse(fs.readFileSync("myCircuit.vk_proof", "utf8")); + +const {proof, publicSignals} = zkSnark.genProof(vk_proof, witness); +``` + +### Verifier + +``` +const vk_verifier = JSON.parse(fs.readFileSync("myCircuit.vk_verifier", "utf8")); + +if (zkSnark.isValid(vk_verifier, proof, publicSignals)) { + console.log("The proof is valid"); +} else { + console.log("The proof is not valid"); +} +```
M README.md → README.md

@@ -34,9 +34,9 @@

``` // `signalId` can always be a number or an alias string - circuit.m; // number of constrains - circuit.n; // number of signals - circuit.p; // number of public signals (nPublicInputs + nOutputs) + circuit.nConstrains; // number of constrains + circuit.nSignals; // number of signals + circuit.nPublic; // number of public signals (nOutputs + nPublicInputs) // The array of signals is always sorted in this order: // [ outputs, publicInputs, privedInputs, internalSignals, constants]
A src/gt.js

@@ -0,0 +1,20 @@

+const bigInt = require("big-integer"); +const ZnField = require("./znfield.js"); + +module.eports = class Gt { + + constructor() { + // TODO + throw new Error("Not Implementted"); + } + + mul(p1, p2) { + // TODO + throw new Error("Not Implementted"); + } + + equal(p1, p2) { + // TODO + throw new Error("Not Implementted"); + } +};
M src/pairing.js → src/pairing.js

@@ -5,6 +5,8 @@

const bigInt = require("big-integer"); module.exports = function pairing(p1, p2) { + + // TODO throw new Error("Not Implementted"); };
M src/polinomial.js → src/polfield.js

@@ -37,6 +37,10 @@ div(a, b) {

throw new Error("Not Implementted"); } + eval(p, x) { + throw new Error("Not Implementted"); + } + lagrange(points) { throw new Error("Not Implementted"); }
M src/prover.js → src/prover.js

@@ -1,5 +1,86 @@

+const bigInt = require("big-integer"); + +const ZnField = require("./znfield.js"); +const G1Curve = require("./g1curve"); +const G2Curve = require("./g2curve"); +const PolField = require("./polfield.js"); + +const F = new ZnField(bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617")); +const G1 = new G1Curve(); +const G2 = new G2Curve(); +const PolF = new PolField(F); module.exports = function genProof(vk_proof, witness) { -} + const proof = {}; + + proof.pi_a = G1.zero(); + proof.pi_ap = G1.zero(); + proof.pi_b = G2.zero(); + proof.pi_bp = G2.zero(); + proof.pi_c = G1.zero(); + proof.pi_cp = G1.zero(); + proof.pi_kp = G1.zero(); + proof.pi_h = G1.zero(); + + + for (let s= vk_proof.nPublic; s< vk_proof.nSignals; s++) { + + // pi_a = pi_a + A[s] * witness[s]; + proof.pi_a = G1.add( proof.pi_a, G1.mulEscalar( vk_proof.A[s], witness[s])); + + // pi_ap = pi_ap + Ap[s] * witness[s]; + proof.pi_ap = G1.add( proof.pi_ap, G1.mulEscalar( vk_proof.Ap[s], witness[s])); + } + + for (let s= 0; s< vk_proof.nSignals; s++) { + // pi_a = pi_a + A[s] * witness[s]; + proof.pi_b = G2.add( proof.pi_b, G1.mulEscalar( vk_proof.B[s], witness[s])); + + // pi_ap = pi_ap + Ap[s] * witness[s]; + proof.pi_bp = G1.add( proof.pi_bp, G1.mulEscalar( vk_proof.Bp[s], witness[s])); + + // pi_a = pi_a + A[s] * witness[s]; + proof.pi_c = G1.add( proof.pi_c, G1.mulEscalar( vk_proof.C[s], witness[s])); + + // pi_ap = pi_ap + Ap[s] * witness[s]; + proof.pi_cp = G1.add( proof.pi_cp, G1.mulEscalar( vk_proof.Cp[s], witness[s])); + + // pi_ap = pi_ap + Ap[s] * witness[s]; + proof.pi_kp = G1.add( proof.pi_kp, G1.mulEscalar( vk_proof.Kp[s], witness[s])); + } + + let polA = []; + let polB = []; + let polC = []; + + for (let s= 0; s< vk_proof.nSignals; s++) { + polA = PolF.add( + polA, + PolF.mul( + vk_proof.polsA[s], + [witness[s]] )); + + polB = PolF.add( + polB, + PolF.mul( + vk_proof.polsB[s], + [witness[s]] )); + + polC = PolF.add( + polC, + PolF.mul( + vk_proof.polsC[s], + [witness[s]] )); + } + + let polFull = PolF.sub(PolF.mul( polA, polB), polC); + + const h = PolF.div(polFull, vk_proof.polZ ); + + for (let i = 0; i < h.length; i++) { + proof.pi_h = G1.add( proof.pi_h, G1.mulEscalar( vk_proof.hExps[i], h[i])); + } + +};
M src/setup.js → src/setup.js

@@ -1,5 +1,160 @@

+const bigInt = require("big-integer"); +const ZnField = require("./znfield.js"); +const PolField = require("./polfield.js"); +const G1Curve = require("./g1curve"); +const G2Curve = require("./g2curve"); + +const F = new ZnField(bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617")); +const PolF = new PolField(F); +const G1 = new G1Curve(); +const G2 = new G2Curve(); module.exports = function setup(circuit) { + const setup = { + vk_proof : { + nSignals: circuit.nSignals, + nPublic: circuit.nPublic + }, + vk_verifier: { + nPublic: circuit.nPublic + }, + toxic: {} + }; + calculatePolinomials(setup, circuit); + setup.toxic.t = F.random(); + calculateEncriptedValuesAtT(setup, circuit); + calculateHexps(setup, circuit); +}; + +function calculatePolinomials(setup, circuit) { + // Calculate the points that must cross each polinomial + const aPoints = []; + const bPoints = []; + const cPoints = []; + for (let s = 0; circuit.nSignals; s++) { + aPoints[s] = []; + bPoints[s] = []; + cPoints[s] = []; + for (let c=0; c<circuit.nConstrains; c++) { + aPoints[s].push([bigInt(c), circuit.a(c, s)]); + bPoints[s].push([bigInt(c), circuit.b(c, s)]); + cPoints[s].push([bigInt(c), circuit.c(c, s)]); + } + } + + // Calculate the polinomials using Lagrange + setup.vk_proof.polsA = []; + setup.vk_proof.polsB = []; + setup.vk_proof.polsC = []; + for (let s=0; s<circuit.nSignals; s++) { + setup.vk_proof.polsA.push(PolF.lagrange( aPoints[s] )); + setup.vk_proof.polsB.push(PolF.lagrange( bPoints[s] )); + setup.vk_proof.polsC.push(PolF.lagrange( cPoints[s] )); + } + + // Calculate Z polinomial + // Z = 1 + setup.vk_proof.polZ = [bigInt(1)]; + for (let c=0; c<circuit.nConstrains; c++) { + // Z = Z * (x - p_c) + setup.vk_proof.polZ = PolF.mul( + setup.vk_proof.polZ, + [F.neg(bigInt(c)), bigInt(1)] ); + } } + +function calculateEncriptedValuesAtT(setup, circuit) { + setup.vk_proof.A = []; + setup.vk_proof.B = []; + setup.vk_proof.C = []; + setup.vk_proof.Ap = []; + setup.vk_proof.Bp = []; + setup.vk_proof.Cp = []; + setup.vk_proof.Kp = []; + setup.vk_verifier.A = []; + + setup.toxic.ka = F.random(); + setup.toxic.kb = F.random(); + setup.toxic.kc = F.random(); + setup.toxic.kbeta = F.random(); + setup.toxic.kgamma = F.random(); + + const gb = F.mul(setup.toxic.kbeta, setup.toxic.kgamma); + + setup.vk_verifier.vk_a = G2.mulEscalar( G2.g, setup.toxic.ka); + setup.vk_verifier.vk_b = G1.mulEscalar( G1.g, setup.toxic.kb); + setup.vk_verifier.vk_c = G2.mulEscalar( G2.g, setup.toxic.kc); + setup.vk_verifier.vk_gb_1 = G1.mulEscalar( G1.g, gb); + setup.vk_verifier.vk_gb_2 = G2.mulEscalar( G2.g, gb); + setup.vk_verifier.vk_g = G2.mulEscalar( G2.g, setup.toxic.kgamma); + + for (let s=0; s<circuit.nSignals; s++) { + + // A[i] = G1 * polA(t) + const A = G1.mulEscalar( + G1.g, + PolF.eval(setup.vk_proof.polsA[s], setup.vk_proof.t)); + setup.vk_proof.A.push(A); + + if (s < circuit.nPublicSignals) { + setup.vk_verifier.A.pusj(A); + } + + // B1[i] = G1 * polB(t) + const B1 = G1.mulEscalar( + G1.g, + PolF.eval(setup.vk_proof.polsB[s], setup.vk_proof.t)); + + // B2[i] = G2 * polB(t) + const B2 = G2.mulEscalar( + G2.g, + PolF.eval(setup.vk_proof.polsB[s], setup.vk_proof.t)); + setup.vk_proof.B.push(B2); + + // C[i] = G1 * polC(t) + const C = G1.mulEscalar( + G1.g, + PolF.eval(setup.vk_proof.polsC[s], setup.vk_proof.t)); + setup.vk_proof.C.push (C); + + // K = G1 * (A+B+C) + const K = G1.mulEscalar( + G1.g, + G1.add(G1.add(A, B1), C)); + + setup.vk_proof.Ap.push(G1.mulEscalar(A, setup.toxic.ka)); + setup.vk_proof.Bp.push(G1.mulEscalar(B1, setup.toxic.kb)); + setup.vk_proof.Cp.push(G1.mulEscalar(C, setup.toxic.kc)); + setup.vk_proof.Kp.push(G1.mulEscalar(K, setup.toxic.beta)); + } + + setup.vk_verifier.vk_z = G2.mulEscalar( + G2.g, + PolF.eval(setup.vk_proof.polZ, setup.vk_proof.t)); +} + +function calculateHexps(setup, circuit) { + let maxA = 0; + let maxB = 0; + let maxC = 0; + for (let s=0; s<circuit.nSignals; s++) { + maxA = Math.max(maxA, setup.vk_proof.polsA[s]); + maxB = Math.max(maxB, setup.vk_proof.polsB[s]); + maxC = Math.max(maxC, setup.vk_proof.polsC[s]); + } + + let maxFull = Math.max(maxA * maxB - 1, maxC); + + const maxH = maxFull - setup.vk_proof.polZ.length + 1; + + setup.vk_proof.hExps = new Array(maxH); + setup.vk_proof.hExps[0] = G1.g; + let eT = setup.toxic.t; + for (let i=1; i<maxH; i++) { + setup.vk_proof.hExps[i] = G1.mulEscalar(G1.g, eT); + eT = F.mul(eT, setup.toxic.t); + } +} +
M src/verifier.js → src/verifier.js

@@ -1,4 +1,52 @@

+const G1Curve = require("./g1curve"); +const G2Curve = require("./g2curve"); +const GT = require("./gt"); + +const G1 = new G1Curve(); +const G2 = new G2Curve(); +const Gt = new GT(); + +const pairing = require("./pairing"); module.exports = function isValid(vk_verifier, proof, publicSignals) { -} + let full_pi_a = proof.pi_a; + for (let s= 0; s< vk_verifier.nPublic; s++) { + + full_pi_a = G1.add( full_pi_a, G1.mulEscalar( vk_verifier.A[s], publicSignals[s])); + } + + if (! Gt.equal( + pairing( proof.pi_a , vk_verifier.vk_a ), + pairing( proof.pi_ap , G2.g ))) + return false; + + if (! Gt.equal( + pairing( vk_verifier.vk_b, proof.pi_b ), + pairing( proof.pi_ap , G2.g ))) + return false; + + if (! Gt.equal( + pairing( proof.pi_c , vk_verifier.vk_c ), + pairing( proof.pi_cp , G2.g ))) + return false; + + if (! Gt.equal( + pairing( full_pi_a , proof.pi_b ), + Gt.mul( + pairing( proof.pi_h , vk_verifier.vk_z ), + pairing( proof.pi_b , G2.g ), + ), + pairing( proof.pi_kp , vk_verifier.vk_g ))) + return false; + + if (! Gt.equal( + Gt.mul( + pairing( G1.add(full_pi_a, proof.pi_c) , vk_verifier.vk_gb_2 ), + pairing( vk_verifier.vk_gb_1 , proof.pi_b ), + ), + pairing( proof.pi_kp , vk_verifier.vk_g ))) + return false; + + return true; +};
M src/znfield.js → src/znfield.js

@@ -19,6 +19,11 @@ // TODO

throw new Error("Not Implementted"); } + neg(a) { + // TODO + throw new Error("Not Implementted"); + } + mul(a, b) { // TODO throw new Error("Not Implementted");

@@ -30,6 +35,16 @@ throw new Error("Not Implementted");

} div(a, b) { + // TODO + throw new Error("Not Implementted"); + } + + isZero(a) { + // TODO + throw new Error("Not Implementted"); + } + + random() { // TODO throw new Error("Not Implementted"); }