@ -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"); |
||||
|
} |
||||
|
``` |
@ -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"); |
||||
|
} |
||||
|
}; |
@ -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) { |
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])); |
||||
|
} |
||||
|
|
||||
|
}; |
@ -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) { |
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); |
||||
|
} |
||||
|
} |
||||
|
|
@ -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) { |
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; |
||||
|
}; |