Browse Source

zkSnarks working

master
Jordi Baylina 6 years ago
parent
commit
2910b7cf7d
No known key found for this signature in database GPG Key ID: 7480C80C1BE43112
25 changed files with 945 additions and 180 deletions
  1. +10
    -5
      README.md
  2. +3
    -3
      file%3a/Users/jbaylina/git/personal/zksnark/src/polfield.js
  3. +4
    -4
      index.js
  4. +68
    -2
      src/bigint.js
  5. +12
    -12
      src/bn128.js
  6. +209
    -0
      src/calculateWitness.js
  7. +100
    -0
      src/circuit.js
  8. +1
    -1
      src/constants.js
  9. +2
    -2
      src/f2field.js
  10. +2
    -2
      src/f3field.js
  11. +4
    -4
      src/futils.js
  12. +10
    -2
      src/gcurve.js
  13. +54
    -13
      src/polfield.js
  14. +37
    -29
      src/prover.js
  15. +108
    -0
      src/ratzqfield.js
  16. +57
    -46
      src/setup.js
  17. +30
    -30
      src/verifier.js
  18. +14
    -2
      src/zqfield.js
  19. +12
    -12
      test/algebra.js
  20. +20
    -0
      test/calculatewitness.js
  21. +49
    -11
      test/pols.js
  22. +70
    -0
      test/ratzqfield.js
  23. +67
    -0
      test/zksnark.js
  24. +1
    -0
      vk_proof.json
  25. +1
    -0
      vk_verifier.json

+ 10
- 5
README.md

@ -39,7 +39,7 @@ const circuit = new zkSnark.Circuit(circuitDef);
circuit.nPublic; // number of public signals (nOutputs + nPublicInputs) circuit.nPublic; // number of public signals (nOutputs + nPublicInputs)
// The array of signals is always sorted in this order: // The array of signals is always sorted in this order:
// [ outputs, publicInputs, 1, privedInputs, internalSignals, constants]
// [ 1, outputs, publicInputs, privedInputs, internalSignals, constants]
// returns a,b and c coeficients of the `signalId` on a given `constrain` // returns a,b and c coeficients of the `signalId` on a given `constrain`
circuit.a(constrain, signalId) circuit.a(constrain, signalId)
@ -47,14 +47,19 @@ const circuit = new zkSnark.Circuit(circuitDef);
circuit.c(constrain, signalId) circuit.c(constrain, signalId)
circuit.nOutputs // number of public outputs circuit.nOutputs // number of public outputs
circuit.nPublicInputs // number of public inputs
circuit.nPrivateInputs // number of private inputs
circuit.pubInputs // number of public inputs
circuit.nPrvInputs // number of private inputs
circuit.nInputs // number of inputs ( nPublicInputs + nPrivateInputs) circuit.nInputs // number of inputs ( nPublicInputs + nPrivateInputs)
circuit.nVars // number of variables ( not including constants (one is a variable) )
circuit.nSignals // number of signals ( including constants )
circuit.outputIdx(i) // returns the index of the i'th output circuit.outputIdx(i) // returns the index of the i'th output
circuit.inputIdx(i) // returns the index of the i'th input 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
circuit.pubInputIdx(i) // returns the index of the i'th public input
circuit.prvInputIdx(i) // returns the index of the i'th private input
circuit.varIdx(i) // returns the index of the i'th variable
circuit.constantIdx(i) // returns the index of the i'th constant
circuit.signalIdx(i) // returns the index of the i'th signal
// returns signal Idx given a signalId // returns signal Idx given a signalId
// if the idx >= n , it is a constant // if the idx >= n , it is a constant

+ 3
- 3
file%3a/Users/jbaylina/git/personal/zksnark/src/polfield.js

@ -55,7 +55,7 @@ class PolFieldZq {
return this.reduce(res); return this.reduce(res);
} }
mulEscalar(a, b) {
mulScalar(a, b) {
if (this.F.isZero(b)) return []; if (this.F.isZero(b)) return [];
const res = new Array(a.length); const res = new Array(a.length);
for (let i=0; i<a.length; i++) { for (let i=0; i<a.length; i++) {
@ -67,8 +67,8 @@ class PolFieldZq {
mul(a, b) { mul(a, b) {
if (a.length == 0) return []; if (a.length == 0) return [];
if (b.length == 0) return []; if (b.length == 0) return [];
if (a.length == 1) return this.mulEscalar(b, a[0]);
if (b.length == 1) return this.mulEscalar(a, b[0]);
if (a.length == 1) return this.mulScalar(b, a[0]);
if (b.length == 1) return this.mulScalar(a, b[0]);
const longestN = Math.max(a.length, b.length); const longestN = Math.max(a.length, b.length);
const bitsResult = log2(longestN-1)+2; const bitsResult = log2(longestN-1)+2;

+ 4
- 4
index.js

@ -1,4 +1,4 @@
exports.Circuit = require "./src/circuit.js";
exports.setup = require "./src/setup.js";
exports.genProof = require "./src/prover.js";
exports.isValid = require "./src/verifier.js";
exports.Circuit = require("./src/circuit.js");
exports.setup = require("./src/setup.js");
exports.genProof = require("./src/prover.js");
exports.isValid = require("./src/verifier.js");

+ 68
- 2
src/bigint.js

@ -85,6 +85,20 @@ if (typeof(BigInt) != "undefined") {
} }
}; };
// Shr
wBigInt.genShr = () => {
return (a,b) => a >> wBigInt(b);
};
// Shl
wBigInt.genShl = (q) => {
if (q) {
return (a,b) => (a << wBigInt(b)) % q;
} else {
return (a,b) => a << wBigInt(b);
}
};
// Equals // Equals
wBigInt.genEquals = (q) => { wBigInt.genEquals = (q) => {
if (q) { if (q) {
@ -132,18 +146,40 @@ if (typeof(BigInt) != "undefined") {
return this < wBigInt.zero; return this < wBigInt.zero;
}; };
wBigInt.prototype.shiftRight = function(f) {
return this >> wBigInt(f);
wBigInt.prototype.and = function(m) {
return this & m;
};
wBigInt.prototype.mod = function(c) {
return this % c;
};
wBigInt.prototype.modPow = function(e, m) {
return this ** e % m;
}; };
wBigInt.prototype.greaterOrEquals = function(b) { wBigInt.prototype.greaterOrEquals = function(b) {
return this >= b; return this >= b;
}; };
wBigInt.prototype.greater = function(b) {
return this > b;
};
wBigInt.prototype.gt = wBigInt.prototype.greater;
wBigInt.prototype.lesserOrEquals = function(b) { wBigInt.prototype.lesserOrEquals = function(b) {
return this <= b; return this <= b;
}; };
wBigInt.prototype.lesser = function(b) {
return this < b;
};
wBigInt.prototype.lt = wBigInt.prototype.lesser;
wBigInt.prototype.equals = function(b) {
return this.valueOf == b.valueOf;
};
wBigInt.prototype.eq = wBigInt.prototype.equals;
} else { } else {
@ -214,6 +250,20 @@ if (typeof(BigInt) != "undefined") {
} }
}; };
// Shr
wBigInt.genShr = () => {
return (a,b) => a.shiftRight(wBigInt(b).value);
};
// Shr
wBigInt.genShl = (q) => {
if (q) {
return (a,b) => a.shiftLeft(wBigInt(b).value).mod(q);
} else {
return (a,b) => a.shiftLeft(wBigInt(b).value);
}
};
// Square // Square
wBigInt.genSquare = (q) => { wBigInt.genSquare = (q) => {
if (q) { if (q) {
@ -301,6 +351,22 @@ wBigInt.prototype.mul = function (a, q) {
return wBigInt.genMul(q)(this, a); return wBigInt.genMul(q)(this, a);
}; };
wBigInt.shr = function(a, b, q) {
return wBigInt.genShr(q)(a,b);
};
wBigInt.prototype.shr = function (a, q) {
return wBigInt.genShr(q)(this, a);
};
wBigInt.shl = function(a, b, q) {
return wBigInt.genShl(q)(a,b);
};
wBigInt.prototype.shl = function (a, q) {
return wBigInt.genShl(q)(this, a);
};
wBigInt.equals = function(a, b, q) { wBigInt.equals = function(a, b, q) {
return wBigInt.genEquals(q)(a,b); return wBigInt.genEquals(q)(a,b);
}; };

+ 12
- 12
src/bn128.js

@ -58,14 +58,14 @@ class BN128 {
this.loop_count_bits = []; // Constant this.loop_count_bits = []; // Constant
while (!lc.isZero()) { while (!lc.isZero()) {
this.loop_count_bits.push( lc.isOdd() ); this.loop_count_bits.push( lc.isOdd() );
lc = lc.shiftRight(1);
lc = lc.shr(1);
} }
this.two_inv = this.F1.inverse(bigInt(2)); this.two_inv = this.F1.inverse(bigInt(2));
this.coef_b = bigInt(3); this.coef_b = bigInt(3);
this.twist = [bigInt(9) , bigInt(1)]; this.twist = [bigInt(9) , bigInt(1)];
this.twist_coeff_b = this.F2.mulEscalar( this.F2.inverse(this.twist), this.coef_b );
this.twist_coeff_b = this.F2.mulScalar( this.F2.inverse(this.twist), this.coef_b );
this.frobenius_coeffs_c1_1 = bigInt("21888242871839275222246405745257275088696311157297823662689037894645226208582"); this.frobenius_coeffs_c1_1 = bigInt("21888242871839275222246405745257275088696311157297823662689037894645226208582");
this.twist_mul_by_q_X = this.twist_mul_by_q_X =
@ -179,8 +179,8 @@ class BN128 {
f = this._mul_by_024( f = this._mul_by_024(
f, f,
c.ell_0, c.ell_0,
this.F2.mulEscalar(c.ell_VW , pre1.PY),
this.F2.mulEscalar(c.ell_VV , pre1.PX, ));
this.F2.mulScalar(c.ell_VW , pre1.PY),
this.F2.mulScalar(c.ell_VV , pre1.PX, ));
if (bit) if (bit)
{ {
@ -188,8 +188,8 @@ class BN128 {
f = this._mul_by_024( f = this._mul_by_024(
f, f,
c.ell_0, c.ell_0,
this.F2.mulEscalar(c.ell_VW, pre1.PY, ),
this.F2.mulEscalar(c.ell_VV, pre1.PX, ));
this.F2.mulScalar(c.ell_VW, pre1.PY, ),
this.F2.mulScalar(c.ell_VV, pre1.PX, ));
} }
} }
@ -203,15 +203,15 @@ class BN128 {
f = this._mul_by_024( f = this._mul_by_024(
f, f,
c.ell_0, c.ell_0,
this.F2.mulEscalar(c.ell_VW, pre1.PY),
this.F2.mulEscalar(c.ell_VV, pre1.PX));
this.F2.mulScalar(c.ell_VW, pre1.PY),
this.F2.mulScalar(c.ell_VV, pre1.PX));
c = pre2.coeffs[idx++]; c = pre2.coeffs[idx++];
f = this._mul_by_024( f = this._mul_by_024(
f, f,
c.ell_0, c.ell_0,
this.F2.mulEscalar(c.ell_VW, pre1.PY, ),
this.F2.mulEscalar(c.ell_VV, pre1.PX));
this.F2.mulScalar(c.ell_VW, pre1.PY, ),
this.F2.mulScalar(c.ell_VV, pre1.PX));
return f; return f;
} }
@ -229,14 +229,14 @@ class BN128 {
const Y = current.Y; const Y = current.Y;
const Z = current.Z; const Z = current.Z;
const A = this.F2.mulEscalar(this.F2.mul(X,Y), this.two_inv); // A = X1 * Y1 / 2
const A = this.F2.mulScalar(this.F2.mul(X,Y), this.two_inv); // A = X1 * Y1 / 2
const B = this.F2.square(Y); // B = Y1^2 const B = this.F2.square(Y); // B = Y1^2
const C = this.F2.square(Z); // C = Z1^2 const C = this.F2.square(Z); // C = Z1^2
const D = this.F2.add(C, this.F2.add(C,C)); // D = 3 * C const D = this.F2.add(C, this.F2.add(C,C)); // D = 3 * C
const E = this.F2.mul(this.twist_coeff_b, D); // E = twist_b * D const E = this.F2.mul(this.twist_coeff_b, D); // E = twist_b * D
const F = this.F2.add(E, this.F2.add(E,E)); // F = 3 * E const F = this.F2.add(E, this.F2.add(E,E)); // F = 3 * E
const G = const G =
this.F2.mulEscalar(
this.F2.mulScalar(
this.F2.add( B , F ), this.F2.add( B , F ),
this.two_inv); // G = (B+F)/2 this.two_inv); // G = (B+F)/2
const H = const H =

+ 209
- 0
src/calculateWitness.js

@ -0,0 +1,209 @@
const bigInt = require("./bigInt");
module.exports = calculateWitness;
function calculateWitness(circuit, inputSignals, log) {
log = log || (() => {});
const ctx = new RTCtx(circuit, log);
function iterateSelector(values, sels, cb) {
if (!Array.isArray(values)) {
return cb(sels, values);
}
for (let i=0; i<values.length; i++) {
sels.push(i);
iterateSelector(values[i], sels, cb);
sels.pop(i);
}
}
ctx.setSignal("one", [], bigInt(1));
for (let c in ctx.notInitSignals) {
if (ctx.notInitSignals[c] == 0) ctx.triggerComponent(c);
}
for (let s in inputSignals) {
ctx.currentComponent = "main";
iterateSelector(inputSignals[s], [], function(selector, value) {
ctx.setSignal(s, selector, bigInt(value));
});
}
for (let i=0; i<circuit.nInputs; i++) {
const idx = circuit.inputIdx(i);
if (typeof(ctx.witness[idx]) == "undefined") {
throw new Error("Input Signal not assigned: " + circuit.signalNames(i));
}
}
for (let i=0; i<ctx.witness.length; i++) {
if (typeof(ctx.witness[i]) == "undefined") {
throw new Error("Signal not assigned: " + circuit.signalNames(i));
}
log(circuit.signalNames(i) + " --> " + ctx.witness[i].toString());
}
return ctx.witness.slice(0, circuit.nVars);
}
class RTCtx {
constructor(circuit, log) {
this.log = log || function() {};
this.scopes = [];
this.circuit = circuit;
this.witness = new Array(circuit.nSignals);
this.notInitSignals = {};
for (let c in this.circuit.components) {
this.notInitSignals[c] = this.circuit.components[c].inputSignals;
}
}
_sels2str(sels) {
let res = "";
for (let i=0; i<sels.length; i++) {
res += `[${sels[i]}]`;
}
return res;
}
setPin(componentName, componentSels, signalName, signalSels, value) {
let fullName = componentName=="one" ? "one" : this.currentComponent + "." + componentName;
fullName += this._sels2str(componentSels) +
"."+
signalName+
this._sels2str(signalSels);
this.setSignalFullName(fullName, value);
}
setSignal(name, sels, value) {
let fullName = this.currentComponent ? this.currentComponent + "." + name : name;
fullName += this._sels2str(sels);
this.setSignalFullName(fullName, value);
}
triggerComponent(c) {
this.log("Component Treiggered: " + c);
// Set notInitSignals to -1 to not initialize again
this.notInitSignals[c] --;
const oldComponent = this.currentComponent;
this.currentComponent = this.circuit.components[c].name;
const template = this.circuit.components[c].template;
const newScope = {};
for (let p in this.circuit.components[c].params) {
newScope[p] = this.circuit.components[c].params[p];
}
const oldScope = this.scopes;
this.scopes = [ this.scopes[0], newScope ];
// TODO set params.
this.circuit.templates[template](this);
this.scopes = oldScope;
this.currentComponent = oldComponent;
}
callFunction(functionName, params) {
const newScope = {};
for (let p=0; p<this.circuit.functions[functionName].params.length; p++) {
const paramName = this.circuit.functions[functionName].params[p];
newScope[paramName] = params[p];
}
const oldScope = this.scopes;
this.scopes = [ this.scopes[0], newScope ];
// TODO set params.
const res = this.circuit.functions[functionName].func(this);
this.scopes = oldScope;
return res;
}
setSignalFullName(fullName, value) {
this.log("set " + fullName + " <-- " + value.toString());
const sId = this.circuit.getSignalIdx(fullName);
let firstInit =false;
if (typeof(this.witness[sId]) == "undefined") {
firstInit = true;
}
this.witness[sId] = value;
const callComponents = [];
for (let i=0; i<this.circuit.signals[sId].triggerComponents.length; i++) {
var idCmp = this.circuit.signals[sId].triggerComponents[i];
if (firstInit) this.notInitSignals[idCmp] --;
callComponents.push(idCmp);
}
callComponents.map( (c) => {
if (this.notInitSignals[c] == 0) this.triggerComponent(c);
});
return value;
}
setVar(name, sels, value) {
function setVarArray(a, sels2, value) {
if (sels2.length == 1) {
a[sels2[0]] = value;
} else {
if (typeof(a[sels2[0]]) == "undefined") a[sels2[0]] = [];
setVarArray(a[sels2[0]], sels2.slice(1), value);
}
}
const scope = this.scopes[this.scopes.length-1];
if (sels.length == 0) {
scope[name] = value;
} else {
if (typeof(scope[name]) == "undefined") scope[name] = [];
setVarArray(scope[name], sels, value);
}
return value;
}
getVar(name, sels) {
function select(a, sels2) {
return (sels2.length == 0) ? a : select(a[sels2[0]], sels2.slice(1));
}
for (let i=this.scopes.length-1; i>=0; i--) {
if (typeof(this.scopes[i][name]) != "undefined") return select(this.scopes[i][name], sels);
}
throw new Error("Variable not defined: " + name);
}
getSignal(name, sels) {
let fullName = name=="one" ? "one" : this.currentComponent + "." + name;
fullName += this._sels2str(sels);
return this.getSignalFullName(fullName);
}
getPin(componentName, componentSels, signalName, signalSels) {
let fullName = componentName=="one" ? "one" : this.currentComponent + "." + componentName;
fullName += this._sels2str(componentSels) +
"."+
signalName+
this._sels2str(signalSels);
return this.getSignalFullName(fullName);
}
getSignalFullName(fullName) {
const sId = this.circuit.getSignalIdx(fullName);
if (typeof(this.witness[sId]) == "undefined") {
throw new Error("Signal not initialized: "+fullName);
}
this.log("get --->" + fullName + " = " + this.witness[sId].toString() );
return this.witness[sId];
}
assert(a,b) {
const ba = bigInt(a);
const bb = bigInt(b);
if (!ba.equals(bb)) {
throw new Error("Constrain doesn't match: " + ba.toString() + " != " + bb.toString());
}
}
}

+ 100
- 0
src/circuit.js

@ -1,6 +1,106 @@
const bigInt = require("./bigint.js");
const __P__ = bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const __MASK__ = bigInt("28948022309329048855892746252171976963317496166410141009864396001978282409983"); // 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
const calculateWitness = require("./calculateWitness.js");
module.exports = class Circuit { module.exports = class Circuit {
constructor(circuitDef) { constructor(circuitDef) {
this.nPubInputs = circuitDef.nPubInputs;
this.nPrvInputs = circuitDef.nPrvInputs;
this.nInputs = circuitDef.nInputs;
this.nOutputs = circuitDef.nOutputs;
this.nVars = circuitDef.nVars;
this.nSignals = circuitDef.nSignals;
this.nConstants = circuitDef.nConstants;
this.nConstrains = circuitDef.constrains.length;
this.signalName2Idx = circuitDef.signalName2Idx;
this.components = circuitDef.components;
this.componentName2Idx = circuitDef.componentName2Idx;
this.signals = circuitDef.signals;
this.constrains = circuitDef.constrains;
this.templates = {};
for (let t in circuitDef.templates) {
this.templates[t] = eval(" const __f= " +circuitDef.templates[t] + "\n__f");
}
this.functions = {};
for (let f in circuitDef.functions) {
this.functions[f] = {
params: circuitDef.functions[f].params,
func: eval(" const __f= " +circuitDef.functions[f].func + "\n__f;")
};
}
}
calculateWitness(input) {
return calculateWitness(this, input);
}
getSignalIdx(name) {
if (typeof(this.signalName2Idx[name]) != "undefined") return this.signalName2Idx[name];
if (!isNaN(name)) return Number(name);
throw new Error("Invalid signal identifier: ", name);
}
// returns the index of the i'th output
outputIdx(i) {
if (i>=this.nOutputs) throw new Error("Accessing an invalid output: "+i);
return i+1;
}
// returns the index of the i'th input
inputIdx(i) {
if (i>=this.nInputs) throw new Error("Accessing an invalid input: "+i);
return this.nOutputs + 1 + i;
}
// returns the index of the i'th public input
pubInputIdx(i) {
if (i>=this.nPubInputs) throw new Error("Accessing an invalid pubInput: "+i);
return this.inputIdx(i);
}
// returns the index of the i'th private input
prvInputIdx(i) {
if (i>=this.nPrvInputs) throw new Error("Accessing an invalid prvInput: "+i);
return this.inputIdx(this.nPubInputs + i);
}
// returns the index of the i'th variable
varIdx(i) {
if (i>=this.nVars) throw new Error("Accessing an invalid variable: "+i);
return i;
}
// returns the index of the i'th constant
constantIdx(i) {
if (i>=this.nConstants) throw new Error("Accessing an invalid constant: "+i);
return this.nVars + i;
}
// returns the index of the i'th signal
signalIdx(i) {
if (i>=this.nSignls) throw new Error("Accessing an invalid signal: "+i);
return i;
}
signalNames(i) {
return this.signals[ this.getSignalIdx(i) ].names.join(", ");
}
a(constrain, signalIdx) {
return bigInt(this.constrains[constrain][0][signalIdx] || 0 );
}
b(constrain, signalIdx) {
return bigInt(this.constrains[constrain][1][signalIdx] || 0);
}
c(constrain, signalIdx) {
return bigInt(this.constrains[constrain][2][signalIdx] || 0);
} }
}; };

+ 1
- 1
src/constants.js

@ -37,7 +37,7 @@ C.two_inv= F1.inverse(bigInt(2));
C.coef_b = bigInt(3); C.coef_b = bigInt(3);
C.twist = [bigInt(9) , bigInt(1)]; C.twist = [bigInt(9) , bigInt(1)];
C.twist_coeff_b = F2.mulEscalar( F2.inverse(C.twist), C.coef_b );
C.twist_coeff_b = F2.mulScalar( F2.inverse(C.twist), C.coef_b );
module.exports = C; module.exports = C;

+ 2
- 2
src/f2field.js

@ -101,8 +101,8 @@ class F2Field {
return [this.F.affine(a[0]), this.F.affine(a[1])]; return [this.F.affine(a[0]), this.F.affine(a[1])];
} }
mulEscalar(base, e) {
return fUtils.mulEscalar(this, base, e);
mulScalar(base, e) {
return fUtils.mulScalar(this, base, e);
} }
exp(base, e) { exp(base, e) {

+ 2
- 2
src/f3field.js

@ -141,8 +141,8 @@ class F3Field {
return [this.F.affine(a[0]), this.F.affine(a[1]), this.F.affine(a[2])]; return [this.F.affine(a[0]), this.F.affine(a[1]), this.F.affine(a[2])];
} }
mulEscalar(base, e) {
return fUtils.mulEscalar(this, base, e);
mulScalar(base, e) {
return fUtils.mulScalar(this, base, e);
} }
exp(base, e) { exp(base, e) {

+ 4
- 4
src/futils.js

@ -1,6 +1,6 @@
const bigInt = require("big-integer");
const bigInt = require("./bigint.js");
exports.mulEscalar = (F, base, e) =>{
exports.mulScalar = (F, base, e) =>{
let res = F.zero; let res = F.zero;
let rem = bigInt(e); let rem = bigInt(e);
let exp = base; let exp = base;
@ -10,7 +10,7 @@ exports.mulEscalar = (F, base, e) =>{
res = F.add(res, exp); res = F.add(res, exp);
} }
exp = F.double(exp); exp = F.double(exp);
rem = rem.shiftRight(1);
rem = rem.shr(1);
} }
return res; return res;
@ -27,7 +27,7 @@ exports.exp = (F, base, e) =>{
res = F.mul(res, exp); res = F.mul(res, exp);
} }
exp = F.square(exp); exp = F.square(exp);
rem = rem.shiftRight(1);
rem = rem.shr(1);
} }
return res; return res;

+ 10
- 2
src/gcurve.js

@ -68,6 +68,14 @@ class GCurve {
return res; return res;
} }
neg(p) {
return [p[0], this.F.neg(p[1]), p[2]];
}
sub(a, b) {
return this.add(a, this.neg(b));
}
double(p) { double(p) {
const res = new Array(3); const res = new Array(3);
@ -105,8 +113,8 @@ class GCurve {
return res; return res;
} }
mulEscalar(base, e) {
return fUtils.mulEscalar(this, base, e);
mulScalar(base, e) {
return fUtils.mulScalar(this, base, e);
} }
affine(p) { affine(p) {

+ 54
- 13
src/polfield.js

@ -6,22 +6,24 @@
*/ */
const bigInt = require("./bigInt"); const bigInt = require("./bigInt");
const ZqField = require("./zqfield");
class PolFieldZq { class PolFieldZq {
constructor (q) {
this.F = new ZqField(q);
constructor (F) {
this.F = F;
let rem = q.sub(bigInt(1));
const q = this.F.q;
let rem = q.sub(this.F.one);
let s = 0; let s = 0;
while (!rem.isOdd()) { while (!rem.isOdd()) {
s ++; s ++;
rem = rem.shiftRight(1);
rem = rem.shr(1);
} }
const five = this.F.add(this.F.add(this.F.two, this.F.two), this.F.one);
this.w = new Array(s+1); this.w = new Array(s+1);
this.wi = new Array(s+1); this.wi = new Array(s+1);
this.w[s] = this.F.exp(bigInt(5), rem);
this.w[s] = this.F.exp(five, rem);
this.wi[s] = this.F.inverse(this.w[s]); this.wi[s] = this.F.inverse(this.w[s]);
let n=s-1; let n=s-1;
@ -55,8 +57,9 @@ class PolFieldZq {
return this.reduce(res); return this.reduce(res);
} }
mulEscalar(a, b) {
mulScalar(a, b) {
if (this.F.isZero(b)) return []; if (this.F.isZero(b)) return [];
if (this.F.equals(b, this.F.one)) return a;
const res = new Array(a.length); const res = new Array(a.length);
for (let i=0; i<a.length; i++) { for (let i=0; i<a.length; i++) {
res[i] = this.F.mul(a[i], b); res[i] = this.F.mul(a[i], b);
@ -64,12 +67,35 @@ class PolFieldZq {
return res; return res;
} }
mul(a, b) { mul(a, b) {
if (a.length == 0) return []; if (a.length == 0) return [];
if (b.length == 0) return []; if (b.length == 0) return [];
if (a.length == 1) return this.mulEscalar(b, a[0]);
if (b.length == 1) return this.mulEscalar(a, b[0]);
if (a.length == 1) return this.mulScalar(b, a[0]);
if (b.length == 1) return this.mulScalar(a, b[0]);
if (b.length > a.length) {
[b, a] = [a, b];
}
if (b.length < log2(a.length)) {
return this.mulNormal(a,b);
} else {
return this.mulFFT(a,b);
}
}
mulNormal(a, b) {
let res = [];
b = this.affine(b);
for (let i=0; i<b.length; i++) {
res = this.add(res, this.scaleX(this.mulScalar(a, b[i]), i) );
}
return res;
}
mulFFT(a,b) {
const longestN = Math.max(a.length, b.length); const longestN = Math.max(a.length, b.length);
const bitsResult = log2(longestN-1)+2; const bitsResult = log2(longestN-1)+2;
const m = 1 << bitsResult; const m = 1 << bitsResult;
@ -87,7 +113,7 @@ class PolFieldZq {
const res = this._fft(tres, bitsResult, 0, 1, true); const res = this._fft(tres, bitsResult, 0, 1, true);
const twoinvm = this.F.inverse(bigInt(m));
const twoinvm = this.F.inverse( this.F.mulScalar(this.F.one, m) );
const resn = new Array(m); const resn = new Array(m);
for (let i=0; i<m; i++) { for (let i=0; i<m; i++) {
resn[i] = this.F.mul(res[(m-i)%m], twoinvm); resn[i] = this.F.mul(res[(m-i)%m], twoinvm);
@ -125,7 +151,22 @@ class PolFieldZq {
} }
lagrange(points) { lagrange(points) {
throw new Error("Not Implementted");
let sum = [];
for (let i=0; i<points.length; i++) {
let mpol = [this.F.one];
for (let j=0;j<points.length;j++) {
if (i!=j) {
mpol = this.mul(mpol, [this.F.neg(points[j][0]), this.F.one]);
}
}
const factor =
this.F.mul(
this.F.inverse(this.eval(mpol, points[i][0])),
points[i][1]);
mpol = this.mulScalar(mpol, factor);
sum = this.add(sum, mpol);
}
return sum;
} }
_fft(pall, bits, offset, step) { _fft(pall, bits, offset, step) {
@ -141,7 +182,7 @@ class PolFieldZq {
const out = new Array(n); const out = new Array(n);
let m= bigInt(1);
let m= this.F.one;
for (let i=0; i<ndiv2; i++) { for (let i=0; i<ndiv2; i++) {
out[i] = this.F.add(p1[i], this.F.mul(m, p2[i])); out[i] = this.F.add(p1[i], this.F.mul(m, p2[i]));
out[i+ndiv2] = this.F.sub(p1[i], this.F.mul(m, p2[i])); out[i+ndiv2] = this.F.sub(p1[i], this.F.mul(m, p2[i]));
@ -262,7 +303,7 @@ class PolFieldZq {
const s = this._reciprocal(v, kbits); const s = this._reciprocal(v, kbits);
let t; let t;
if (m>2*n) { if (m>2*n) {
t = this.sub(this.scaleX([bigInt(1)], 2*n), this.mul(s, v));
t = this.sub(this.scaleX([this.F.one], 2*n), this.mul(s, v));
} }
let q = []; let q = [];

+ 37
- 29
src/prover.js

@ -1,62 +1,58 @@
const bigInt = require("big-integer");
const ZnField = require("./znfield.js");
const G1Curve = require("./g1curve");
const G2Curve = require("./g2curve");
const BN128 = require("./BN128.js");
const PolField = require("./polfield.js"); const PolField = require("./polfield.js");
const ZqField = require("./zqfield.js");
const F = new ZnField(bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
const G1 = new G1Curve();
const G2 = new G2Curve();
const PolF = new PolField(F);
const bn128 = new BN128();
const PolF = new PolField(new ZqField(bn128.r));
const G1 = bn128.G1;
const G2 = bn128.G2;
module.exports = function genProof(vk_proof, witness) { module.exports = function genProof(vk_proof, witness) {
const proof = {}; 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();
proof.pi_a = G1.zero;
proof.pi_ap = G1.zero;
proof.pi_b = G2.zero;
proof.pi_bp = G1.zero;
proof.pi_c = G1.zero;
proof.pi_cp = G1.zero;
proof.pi_kp = G1.zero;
proof.pi_h = G1.zero;
// Skip public entries and the "1" signal that are forced by the verifier // Skip public entries and the "1" signal that are forced by the verifier
for (let s= vk_proof.nPublic+1; s< vk_proof.nSignals; s++) {
for (let s= vk_proof.nPublic+1; s< vk_proof.nVars; s++) {
// pi_a = pi_a + A[s] * witness[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]));
proof.pi_a = G1.add( proof.pi_a, G1.mulScalar( vk_proof.A[s], witness[s]));
// pi_ap = pi_ap + Ap[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]));
proof.pi_ap = G1.add( proof.pi_ap, G1.mulScalar( vk_proof.Ap[s], witness[s]));
} }
for (let s= 0; s< vk_proof.nSignals; s++) {
for (let s= 0; s< vk_proof.nVars; s++) {
// pi_a = pi_a + A[s] * witness[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]));
proof.pi_b = G2.add( proof.pi_b, G2.mulScalar( vk_proof.B[s], witness[s]));
// pi_ap = pi_ap + Ap[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]));
proof.pi_bp = G1.add( proof.pi_bp, G1.mulScalar( vk_proof.Bp[s], witness[s]));
// pi_a = pi_a + A[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]));
proof.pi_c = G1.add( proof.pi_c, G1.mulScalar( vk_proof.C[s], witness[s]));
// pi_ap = pi_ap + Ap[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]));
proof.pi_cp = G1.add( proof.pi_cp, G1.mulScalar( vk_proof.Cp[s], witness[s]));
// pi_ap = pi_ap + Ap[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]));
proof.pi_kp = G1.add( proof.pi_kp, G1.mulScalar( vk_proof.Kp[s], witness[s]));
} }
let polA = []; let polA = [];
let polB = []; let polB = [];
let polC = []; let polC = [];
for (let s= 0; s< vk_proof.nSignals; s++) {
for (let s= 0; s< vk_proof.nVars; s++) {
polA = PolF.add( polA = PolF.add(
polA, polA,
PolF.mul( PolF.mul(
@ -81,7 +77,19 @@ module.exports = function genProof(vk_proof, witness) {
const h = PolF.div(polFull, vk_proof.polZ ); const h = PolF.div(polFull, vk_proof.polZ );
for (let i = 0; i < h.length; i++) { for (let i = 0; i < h.length; i++) {
proof.pi_h = G1.add( proof.pi_h, G1.mulEscalar( vk_proof.hExps[i], h[i]));
proof.pi_h = G1.add( proof.pi_h, G1.mulScalar( vk_proof.hExps[i], h[i]));
} }
proof.pi_a = G1.affine(proof.pi_a);
proof.pi_b = G2.affine(proof.pi_b);
proof.pi_c = G1.affine(proof.pi_c);
proof.pi_ap = G1.affine(proof.pi_ap);
proof.pi_bp = G1.affine(proof.pi_bp);
proof.pi_cp = G1.affine(proof.pi_cp);
proof.pi_kp = G1.affine(proof.pi_kp);
proof.pi_h = G1.affine(proof.pi_h);
const publicSignals = witness.slice(1, vk_proof.nPublic+1);
return {proof, publicSignals};
}; };

+ 108
- 0
src/ratzqfield.js

@ -0,0 +1,108 @@
const fUtils = require("./futils.js");
class RatZqField {
constructor(F) {
this.F = F;
this.zero = [F.zero, F.one];
this.one = [F.one, F.one];
this.two = [F.two, F.one];
this.twoinv = [F.one, F.two];
this.q = F.q;
}
add(a,b) {
return [
this.F.add(
this.F.mul(a[0], b[1]),
this.F.mul(a[1], b[0])),
this.F.mul(a[1], b[1])];
}
double(a) {
return [this.F.add(a[0], a[0]), a[1]];
}
sub(a,b) {
return [
this.F.sub(
this.F.mul(a[0], b[1]),
this.F.mul(a[1], b[0])),
this.F.mul(a[1], b[1])];
}
neg(a) {
return [this.F.neg(a[0]), a[1]];
}
mul(a,b) {
return [
this.F.mul(a[0], b[0]),
this.F.mul(a[1], b[1]),
];
}
copy(a) {
return [a[0], a[1]];
}
div(a, b) {
return [
this.F.mul(a[0], b[1]),
this.F.mul(a[1], b[0]),
];
}
inverse(a) {
return [a[1], a[0]];
}
square(a) {
return [
this.F.square(a[0]),
this.F.square(a[1])
];
}
mulScalar(base, e) {
return [this.F.mulScalar(base[0], e) , base[1]];
}
exp(base, e) {
return fUtils.exp(this, base, e);
}
equals(a, b) {
return this.F.equals(
this.F.mul(a[0], b[1]),
this.F.mul(a[1], b[0])
);
}
isZero(a) {
return this.F.isZero(a[0]);
}
affine(a) {
return [this.F.div(a[0], a[1]), this.F.one];
}
toString(a) {
const ca = this.affine(a);
return `"0x${ca[0].toString(16)}"`;
}
random() {
return [this.F.random(), this.F.one];
}
fromF(a) {
return [a, this.F.one];
}
toF(a) {
return this.affine(a)[0];
}
}
module.exports = RatZqField;

+ 57
- 46
src/setup.js

@ -1,23 +1,23 @@
const bigInt = require("big-integer");
const bigInt = require("./bigint.js");
const ZnField = require("./znfield.js");
const BN128 = require("./BN128.js");
const PolField = require("./polfield.js"); const PolField = require("./polfield.js");
const G1Curve = require("./g1curve");
const G2Curve = require("./g2curve");
const ZqField = require("./zqfield.js");
const F = new ZnField(bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
const PolF = new PolField(F);
const G1 = new G1Curve();
const G2 = new G2Curve();
const bn128 = new BN128();
const G1 = bn128.G1;
const G2 = bn128.G2;
const PolF = new PolField(new ZqField(bn128.r));
const F = new ZqField(bn128.r);
module.exports = function setup(circuit) { module.exports = function setup(circuit) {
const setup = { const setup = {
vk_proof : { vk_proof : {
nSignals: circuit.nSignals,
nPublic: circuit.nPublic
nVars: circuit.nVars,
nPublic: circuit.nPubInputs + circuit.nOutputs
}, },
vk_verifier: { vk_verifier: {
nPublic: circuit.nPublic
nPublic: circuit.nPubInputs + circuit.nOutputs
}, },
toxic: {} toxic: {}
}; };
@ -26,6 +26,8 @@ module.exports = function setup(circuit) {
setup.toxic.t = F.random(); setup.toxic.t = F.random();
calculateEncriptedValuesAtT(setup, circuit); calculateEncriptedValuesAtT(setup, circuit);
calculateHexps(setup, circuit); calculateHexps(setup, circuit);
return setup;
}; };
function calculatePolinomials(setup, circuit) { function calculatePolinomials(setup, circuit) {
@ -33,7 +35,7 @@ function calculatePolinomials(setup, circuit) {
const aPoints = []; const aPoints = [];
const bPoints = []; const bPoints = [];
const cPoints = []; const cPoints = [];
for (let s = 0; circuit.nSignals; s++) {
for (let s = 0; s<circuit.nSignals; s++) {
aPoints[s] = []; aPoints[s] = [];
bPoints[s] = []; bPoints[s] = [];
cPoints[s] = []; cPoints[s] = [];
@ -49,6 +51,7 @@ function calculatePolinomials(setup, circuit) {
setup.vk_proof.polsB = []; setup.vk_proof.polsB = [];
setup.vk_proof.polsC = []; setup.vk_proof.polsC = [];
for (let s=0; s<circuit.nSignals; s++) { for (let s=0; s<circuit.nSignals; s++) {
console.log(`Caclcualte Pol ${s}/${circuit.nSignals}`);
setup.vk_proof.polsA.push(PolF.lagrange( aPoints[s] )); setup.vk_proof.polsA.push(PolF.lagrange( aPoints[s] ));
setup.vk_proof.polsB.push(PolF.lagrange( bPoints[s] )); setup.vk_proof.polsB.push(PolF.lagrange( bPoints[s] ));
setup.vk_proof.polsC.push(PolF.lagrange( cPoints[s] )); setup.vk_proof.polsC.push(PolF.lagrange( cPoints[s] ));
@ -83,56 +86,64 @@ function calculateEncriptedValuesAtT(setup, circuit) {
const gb = F.mul(setup.toxic.kbeta, setup.toxic.kgamma); 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);
setup.vk_verifier.vk_a = G2.affine(G2.mulScalar( G2.g, setup.toxic.ka));
setup.vk_verifier.vk_b = G1.affine(G1.mulScalar( G1.g, setup.toxic.kb));
setup.vk_verifier.vk_c = G2.affine(G2.mulScalar( G2.g, setup.toxic.kc));
setup.vk_verifier.vk_gb_1 = G1.affine(G1.mulScalar( G1.g, gb));
setup.vk_verifier.vk_gb_2 = G2.affine(G2.mulScalar( G2.g, gb));
setup.vk_verifier.vk_g = G2.affine(G2.mulScalar( G2.g, setup.toxic.kgamma));
for (let s=0; s<circuit.nSignals; s++) { for (let s=0; s<circuit.nSignals; s++) {
// A[i] = G1 * polA(t) // A[i] = G1 * polA(t)
const A = G1.mulEscalar(
G1.g,
PolF.eval(setup.vk_proof.polsA[s], setup.vk_proof.t));
const at = PolF.eval(setup.vk_proof.polsA[s], setup.toxic.t);
const A = G1.affine(G1.mulScalar(G1.g, at));
setup.vk_proof.A.push(A); setup.vk_proof.A.push(A);
if (s < circuit.nPublicSignals) {
setup.vk_verifier.A.pusj(A);
if (s <= setup.vk_proof.nPublic) {
setup.vk_verifier.A.push(A);
} }
// B1[i] = G1 * polB(t) // B1[i] = G1 * polB(t)
const B1 = G1.mulEscalar(
G1.g,
PolF.eval(setup.vk_proof.polsB[s], setup.vk_proof.t));
const bt = PolF.eval(setup.vk_proof.polsB[s], setup.toxic.t);
const B1 = G1.affine(G1.mulScalar(G1.g, bt));
// B2[i] = G2 * polB(t) // B2[i] = G2 * polB(t)
const B2 = G2.mulEscalar(
G2.g,
PolF.eval(setup.vk_proof.polsB[s], setup.vk_proof.t));
const B2 = G2.affine(G2.mulScalar(G2.g, bt));
setup.vk_proof.B.push(B2); setup.vk_proof.B.push(B2);
// C[i] = G1 * polC(t) // C[i] = G1 * polC(t)
const C = G1.mulEscalar(
G1.g,
PolF.eval(setup.vk_proof.polsC[s], setup.vk_proof.t));
const ct = PolF.eval(setup.vk_proof.polsC[s], setup.toxic.t);
const C = G1.affine(G1.mulScalar( G1.g, ct));
setup.vk_proof.C.push (C); setup.vk_proof.C.push (C);
// K = G1 * (A+B+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));
const kt = F.add(F.add(at, bt), ct);
const K = G1.affine(G1.mulScalar( G1.g, kt));
const Ktest = G1.affine(G1.add(G1.add(A, B1), C));
if (!G1.equals(K, Ktest)) {
console.log ("=====FAIL======");
}
setup.vk_proof.Ap.push(G1.affine(G1.mulScalar(A, setup.toxic.ka)));
setup.vk_proof.Bp.push(G1.affine(G1.mulScalar(B1, setup.toxic.kb)));
setup.vk_proof.Cp.push(G1.affine(G1.mulScalar(C, setup.toxic.kc)));
setup.vk_proof.Kp.push(G1.affine(G1.mulScalar(K, setup.toxic.kbeta)));
} }
setup.vk_verifier.vk_z = G2.mulEscalar(
setup.vk_verifier.vk_z = G2.affine(G2.mulScalar(
G2.g, G2.g,
PolF.eval(setup.vk_proof.polZ, setup.vk_proof.t));
PolF.eval(setup.vk_proof.polZ, setup.toxic.t)));
} }
function calculateHexps(setup, circuit) { function calculateHexps(setup, circuit) {
@ -140,9 +151,9 @@ function calculateHexps(setup, circuit) {
let maxB = 0; let maxB = 0;
let maxC = 0; let maxC = 0;
for (let s=0; s<circuit.nSignals; s++) { 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]);
maxA = Math.max(maxA, setup.vk_proof.polsA[s].length);
maxB = Math.max(maxB, setup.vk_proof.polsB[s].length);
maxC = Math.max(maxC, setup.vk_proof.polsC[s].length);
} }
let maxFull = Math.max(maxA * maxB - 1, maxC); let maxFull = Math.max(maxA * maxB - 1, maxC);
@ -153,7 +164,7 @@ function calculateHexps(setup, circuit) {
setup.vk_proof.hExps[0] = G1.g; setup.vk_proof.hExps[0] = G1.g;
let eT = setup.toxic.t; let eT = setup.toxic.t;
for (let i=1; i<maxH; i++) { for (let i=1; i<maxH; i++) {
setup.vk_proof.hExps[i] = G1.mulEscalar(G1.g, eT);
setup.vk_proof.hExps[i] = G1.affine(G1.mulScalar(G1.g, eT));
eT = F.mul(eT, setup.toxic.t); eT = F.mul(eT, setup.toxic.t);
} }
} }

+ 30
- 30
src/verifier.js

@ -1,52 +1,52 @@
const G1Curve = require("./g1curve");
const G2Curve = require("./g2curve");
const GT = require("./gt");
const BN128 = require("./BN128.js");
const G1 = new G1Curve();
const G2 = new G2Curve();
const Gt = new GT();
const bn128 = new BN128();
const G1 = bn128.G1;
const G2 = bn128.G2;
const pairing = require("./pairing");
const pairing = bn128.pairing;
module.exports = function isValid(vk_verifier, proof, publicSignals) { module.exports = function isValid(vk_verifier, proof, publicSignals) {
let full_pi_a = vk_verifier.A[0];
for (let s= 0; s< vk_verifier.nPublic; s++) { 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]));
full_pi_a = G1.add( full_pi_a, G1.mulScalar( vk_verifier.A[s+1], publicSignals[s]));
} }
let full_pi_a = G1.add(proof.pi_a, vk_verifier.A[vk_verifier.nPublic]);
full_pi_a = G1.add( full_pi_a, proof.pi_a);
if (! Gt.equal(
pairing( proof.pi_a , vk_verifier.vk_a ),
pairing( proof.pi_ap , G2.g )))
if (! bn128.F12.equals(
bn128.pairing( proof.pi_a , vk_verifier.vk_a ),
bn128.pairing( proof.pi_ap , G2.g )))
return false; return false;
if (! Gt.equal(
pairing( vk_verifier.vk_b, proof.pi_b ),
pairing( proof.pi_ap , G2.g )))
if (! bn128.F12.equals(
bn128.pairing( vk_verifier.vk_b, proof.pi_b ),
bn128.pairing( proof.pi_bp , G2.g )))
return false; return false;
if (! Gt.equal(
pairing( proof.pi_c , vk_verifier.vk_c ),
pairing( proof.pi_cp , G2.g )))
if (! bn128.F12.equals(
bn128.pairing( proof.pi_c , vk_verifier.vk_c ),
bn128.pairing( proof.pi_cp , G2.g )))
return false; 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 )))
if (! bn128.F12.equals(
bn128.pairing( full_pi_a , proof.pi_b ),
bn128.F12.mul(
bn128.pairing( proof.pi_h , vk_verifier.vk_z ),
bn128.pairing( proof.pi_c , G2.g ),
)))
return false; 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 ),
if (! bn128.F12.equals(
bn128.F12.mul(
bn128.pairing( G1.add(full_pi_a, proof.pi_c) , vk_verifier.vk_gb_2 ),
bn128.pairing( vk_verifier.vk_gb_1 , proof.pi_b ),
), ),
pairing( proof.pi_kp , vk_verifier.vk_g )))
bn128.pairing( proof.pi_kp , vk_verifier.vk_g )))
return false; return false;
return true; return true;
}; };

+ 14
- 2
src/zqfield.js

@ -1,3 +1,5 @@
const crypto = require("crypto");
const bigInt = require("./bigint"); const bigInt = require("./bigint");
const fUtils = require("./futils.js"); const fUtils = require("./futils.js");
@ -28,8 +30,8 @@ class ZqField {
return this.mul(a, this.inverse(b)); return this.mul(a, this.inverse(b));
} }
mulEscalar(base, e) {
return fUtils.mulEscalar(this, base, e);
mulScalar(base, e) {
return this.mul(base, bigInt(e));
} }
exp(base, e) { exp(base, e) {
@ -40,6 +42,16 @@ class ZqField {
const ca = this.affine(a); const ca = this.affine(a);
return `"0x${ca.toString(16)}"`; return `"0x${ca.toString(16)}"`;
} }
random() {
let res = bigInt(0);
let n = bigInt(this.q);
while (!n.isZero()) {
res = res.shl(8).add(bigInt(crypto.randomBytes(1)[0]));
n = n.shr(8);
}
return res;
}
} }

+ 12
- 12
test/algebra.js

@ -31,7 +31,7 @@ describe("Curve G1 Test", () => {
it("r*one == 0", () => { it("r*one == 0", () => {
const bn128 = new BN128(); const bn128 = new BN128();
const res = bn128.G1.mulEscalar(bn128.G1.g, bn128.r);
const res = bn128.G1.mulScalar(bn128.G1.g, bn128.r);
assert(bn128.G1.equals(res, bn128.G1.zero), "G1 does not have range r"); assert(bn128.G1.equals(res, bn128.G1.zero), "G1 does not have range r");
}); });
@ -43,12 +43,12 @@ describe("Curve G1 Test", () => {
const r1 = bigInt(33); const r1 = bigInt(33);
const r2 = bigInt(44); const r2 = bigInt(44);
const gr1 = bn128.G1.mulEscalar(bn128.G1.g, r1);
const gr2 = bn128.G1.mulEscalar(bn128.G1.g, r2);
const gr1 = bn128.G1.mulScalar(bn128.G1.g, r1);
const gr2 = bn128.G1.mulScalar(bn128.G1.g, r2);
const grsum1 = bn128.G1.add(gr1, gr2); const grsum1 = bn128.G1.add(gr1, gr2);
const grsum2 = bn128.G1.mulEscalar(bn128.G1.g, r1.add(r2));
const grsum2 = bn128.G1.mulScalar(bn128.G1.g, r1.add(r2));
assert(bn128.G1.equals(grsum1, grsum2)); assert(bn128.G1.equals(grsum1, grsum2));
}); });
@ -58,7 +58,7 @@ describe("Curve G2 Test", () => {
it ("r*one == 0", () => { it ("r*one == 0", () => {
const bn128 = new BN128(); const bn128 = new BN128();
const res = bn128.G2.mulEscalar(bn128.G2.g, bn128.r);
const res = bn128.G2.mulScalar(bn128.G2.g, bn128.r);
assert(bn128.G2.equals(res, bn128.G2.zero), "G2 does not have range r"); assert(bn128.G2.equals(res, bn128.G2.zero), "G2 does not have range r");
}); });
@ -69,12 +69,12 @@ describe("Curve G2 Test", () => {
const r1 = bigInt(33); const r1 = bigInt(33);
const r2 = bigInt(44); const r2 = bigInt(44);
const gr1 = bn128.G2.mulEscalar(bn128.G2.g, r1);
const gr2 = bn128.G2.mulEscalar(bn128.G2.g, r2);
const gr1 = bn128.G2.mulScalar(bn128.G2.g, r1);
const gr2 = bn128.G2.mulScalar(bn128.G2.g, r2);
const grsum1 = bn128.G2.add(gr1, gr2); const grsum1 = bn128.G2.add(gr1, gr2);
const grsum2 = bn128.G2.mulEscalar(bn128.G2.g, r1.add(r2));
const grsum2 = bn128.G2.mulScalar(bn128.G2.g, r1.add(r2));
/* /*
console.log(G2.toString(grsum1)); console.log(G2.toString(grsum1));
@ -148,11 +148,11 @@ describe("Pairing", () => {
for (let i=0; i<1; i++) { for (let i=0; i<1; i++) {
const bn128 = new BN128(); const bn128 = new BN128();
const g1a = bn128.G1.mulEscalar(bn128.G1.g, 25);
const g2a = bn128.G2.mulEscalar(bn128.G2.g, 30);
const g1a = bn128.G1.mulScalar(bn128.G1.g, 25);
const g2a = bn128.G2.mulScalar(bn128.G2.g, 30);
const g1b = bn128.G1.mulEscalar(bn128.G1.g, 30);
const g2b = bn128.G2.mulEscalar(bn128.G2.g, 25);
const g1b = bn128.G1.mulScalar(bn128.G1.g, 30);
const g2b = bn128.G2.mulScalar(bn128.G2.g, 25);
const pre1a = bn128.precomputeG1(g1a); const pre1a = bn128.precomputeG1(g1a);
const pre2a = bn128.precomputeG2(g2a); const pre2a = bn128.precomputeG2(g2a);

+ 20
- 0
test/calculatewitness.js

@ -0,0 +1,20 @@
const chai = require("chai");
const fs = require("fs");
const Circuit = require("../src/circuit.js");
const BN128 = require("../src/BN128.js");
const F1Field = require("../src/zqfield.js");
const assert = chai.assert;
describe("Calculate witness", () => {
it("Should calculate the witness of a sum circuit", () => {
const cirDef = JSON.parse(fs.readFileSync("../jaz/sum.json", "utf8"));
const cir = new Circuit(cirDef);
const witness = cir.calculateWitness({"a": "33", "b": "34"});
assert.equal(witness[cir.getSignalIdx("main.out")].toString(), "67");
});
});

+ 49
- 11
test/pols.js

@ -2,6 +2,7 @@ const chai = require("chai");
const bigInt = require("../src/bigint.js"); const bigInt = require("../src/bigint.js");
const PolField = require("../src/polfield.js"); const PolField = require("../src/polfield.js");
const ZqField = require("../src/zqfield");
const assert = chai.assert; const assert = chai.assert;
@ -9,7 +10,7 @@ const r = bigInt("2188824287183927522224640574525727508854836440041603434369820
describe("Polinomial field", () => { describe("Polinomial field", () => {
it("Should compute a multiplication", () => { it("Should compute a multiplication", () => {
const PF = new PolField(r);
const PF = new PolField(new ZqField(r));
const a = [bigInt(1), bigInt(2), bigInt(3)]; const a = [bigInt(1), bigInt(2), bigInt(3)];
const b = [bigInt(1), bigInt(2), bigInt(3)]; const b = [bigInt(1), bigInt(2), bigInt(3)];
@ -18,7 +19,7 @@ describe("Polinomial field", () => {
assert(PF.equals(res, [bigInt(1), bigInt(4), bigInt(10), bigInt(12), bigInt(9)])); assert(PF.equals(res, [bigInt(1), bigInt(4), bigInt(10), bigInt(12), bigInt(9)]));
}); });
it("Should compute a multiplication 2", () => { it("Should compute a multiplication 2", () => {
const PF = new PolField(r);
const PF = new PolField(new ZqField(r));
const a = [bigInt(5), bigInt(1)]; const a = [bigInt(5), bigInt(1)];
const b = [bigInt(-5), bigInt(1)]; const b = [bigInt(-5), bigInt(1)];
@ -27,7 +28,7 @@ describe("Polinomial field", () => {
assert(PF.equals(res, [bigInt(-25), bigInt(0), bigInt(1)])); assert(PF.equals(res, [bigInt(-25), bigInt(0), bigInt(1)]));
}); });
it("Should compute an addition", () => { it("Should compute an addition", () => {
const PF = new PolField(r);
const PF = new PolField(new ZqField(r));
const a = [bigInt(5), bigInt(1)]; const a = [bigInt(5), bigInt(1)];
const b = [bigInt(-5), bigInt(1)]; const b = [bigInt(-5), bigInt(1)];
@ -36,7 +37,7 @@ describe("Polinomial field", () => {
assert(PF.equals(res, [bigInt(0), bigInt(2)])); assert(PF.equals(res, [bigInt(0), bigInt(2)]));
}); });
it("Should compute a substraction", () => { it("Should compute a substraction", () => {
const PF = new PolField(r);
const PF = new PolField(new ZqField(r));
const a = [bigInt(5), bigInt(3), bigInt(4)]; const a = [bigInt(5), bigInt(3), bigInt(4)];
const b = [bigInt(5), bigInt(1)]; const b = [bigInt(5), bigInt(1)];
@ -45,7 +46,7 @@ describe("Polinomial field", () => {
assert(PF.equals(res, [bigInt(0), bigInt(2), bigInt(4)])); assert(PF.equals(res, [bigInt(0), bigInt(2), bigInt(4)]));
}); });
it("Should compute reciprocal", () => { it("Should compute reciprocal", () => {
const PF = new PolField(r);
const PF = new PolField(new ZqField(r));
const a = [bigInt(4), bigInt(1), bigInt(-3), bigInt(-1), bigInt(2),bigInt(1), bigInt(-1), bigInt(1)]; const a = [bigInt(4), bigInt(1), bigInt(-3), bigInt(-1), bigInt(2),bigInt(1), bigInt(-1), bigInt(1)];
const res = PF._reciprocal(a, 3, 0); const res = PF._reciprocal(a, 3, 0);
@ -53,7 +54,7 @@ describe("Polinomial field", () => {
assert(PF.equals(res, [bigInt(12), bigInt(15), bigInt(3), bigInt(-4), bigInt(-3), bigInt(0), bigInt(1), bigInt(1)])); assert(PF.equals(res, [bigInt(12), bigInt(15), bigInt(3), bigInt(-4), bigInt(-3), bigInt(0), bigInt(1), bigInt(1)]));
}); });
it("Should div2", () => { it("Should div2", () => {
const PF = new PolField(r);
const PF = new PolField(new ZqField(r));
// x^6 // x^6
const a = [bigInt(0), bigInt(0), bigInt(0), bigInt(0), bigInt(0),bigInt(0), bigInt(1)]; const a = [bigInt(0), bigInt(0), bigInt(0), bigInt(0), bigInt(0),bigInt(0), bigInt(1)];
@ -67,7 +68,7 @@ describe("Polinomial field", () => {
assert(PF.equals(res2, [bigInt(0), bigInt(1)])); assert(PF.equals(res2, [bigInt(0), bigInt(1)]));
}); });
it("Should div", () => { it("Should div", () => {
const PF = new PolField(r);
const PF = new PolField(new ZqField(r));
const a = [bigInt(1), bigInt(2), bigInt(3), bigInt(4), bigInt(5),bigInt(6), bigInt(7)]; const a = [bigInt(1), bigInt(2), bigInt(3), bigInt(4), bigInt(5),bigInt(6), bigInt(7)];
const b = [bigInt(8), bigInt(9), bigInt(10), bigInt(11), bigInt(12), bigInt(13)]; const b = [bigInt(8), bigInt(9), bigInt(10), bigInt(11), bigInt(12), bigInt(13)];
@ -79,7 +80,7 @@ describe("Polinomial field", () => {
}); });
it("Should div big/small", () => { it("Should div big/small", () => {
const PF = new PolField(r);
const PF = new PolField(new ZqField(r));
const a = [bigInt(1), bigInt(2), bigInt(3), bigInt(4), bigInt(5),bigInt(6), bigInt(7)]; const a = [bigInt(1), bigInt(2), bigInt(3), bigInt(4), bigInt(5),bigInt(6), bigInt(7)];
const b = [bigInt(8), bigInt(9)]; const b = [bigInt(8), bigInt(9)];
@ -90,18 +91,55 @@ describe("Polinomial field", () => {
assert(PF.equals(a, d)); assert(PF.equals(a, d));
}); });
it("Should div random big", () => { it("Should div random big", () => {
const PF = new PolField(r);
const PF = new PolField(new ZqField(r));
const a = []; const a = [];
const b = []; const b = [];
for (let i=0; i<1000; i++) a.push(bigInt(Math.floor(Math.random()*100000) -500000)); for (let i=0; i<1000; i++) a.push(bigInt(Math.floor(Math.random()*100000) -500000));
for (let i=0; i<300; i++) b.push(bigInt(Math.floor(Math.random()*100000) -500000));
for (let i=0; i<900; i++) b.push(bigInt(Math.floor(Math.random()*100000) -500000));
const c = PF.mul(a,b); const c = PF.mul(a,b);
const d = PF.div(c,b); const d = PF.div(c,b);
assert(PF.equals(a, d)); assert(PF.equals(a, d));
}).timeout(10000000);
}).timeout(10000);
it("Should evaluate and zero", () => {
const PF = new PolField(new ZqField(r));
const p = [PF.F.neg(bigInt(2)), bigInt(1)];
const v = PF.eval(p, bigInt(2));
assert(PF.F.equals(v, bigInt(0)));
});
it("Should create lagrange polynomial minmal", () => {
const PF = new PolField(new ZqField(r));
const points=[];
points.push([bigInt(1), bigInt(1)]);
points.push([bigInt(2), bigInt(2)]);
points.push([bigInt(3), bigInt(5)]);
const p=PF.lagrange(points);
for (let i=0; i<points.length; i++) {
const v = PF.eval(p, points[i][0]);
assert(PF.F.equals(v, points[i][1]));
}
});
it("Should create lagrange polynomial", () => {
const PF = new PolField(new ZqField(r));
const points=[];
points.push([bigInt(1), bigInt(2)]);
points.push([bigInt(2), bigInt(-2)]);
points.push([bigInt(3), bigInt(0)]);
points.push([bigInt(4), bigInt(453345)]);
const p=PF.lagrange(points);
for (let i=0; i<points.length; i++) {
const v = PF.eval(p, points[i][0]);
assert(PF.F.equals(v, points[i][1]));
}
});
}); });

+ 70
- 0
test/ratzqfield.js

@ -0,0 +1,70 @@
const chai = require("chai");
const bigInt = require("../src/bigint.js");
const ZqField = require("../src/zqfield.js");
const RatZqField = require("../src/ratzqfield.js");
const q = bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const Z = new ZqField(q);
const R = new RatZqField(Z);
const assert = chai.assert;
function r(a,b) {
return [bigInt(a), bigInt(b)];
}
describe("Rational zq Field", () => {
it("Should compare correctly", () => {
assert( R.equals(r(3,5), r(6,10)));
assert(!R.equals(r(3,5), r(6,11)));
});
it("Should add correctly", () => {
const a = r(7,4);
const b = r(5,12);
assert(R.equals( R.add(a,b), r(13, 6)));
});
it("Should substract", () => {
const a = r(7,4);
const b = r(5,12);
assert(R.equals( R.sub(a,b), r(4, 3)));
});
it("Should multiply", () => {
const a = r(7,4);
const b = r(5,12);
assert(R.equals( R.mul(a,b), r(35, 48)));
});
it("Should div", () => {
const a = r(7,4);
const b = r(5,12);
assert(R.equals( R.div(a,b), r(7*12, 5*4)));
});
it("Should square", () => {
const a = r(7,4);
assert(R.equals( R.square(a), r(49, 16)));
});
it("Should affine", () => {
const a = r(12,4);
const aa = R.affine(a);
assert(Z.equals( aa[0], bigInt(3)));
assert(Z.equals( aa[1], Z.one));
});
it("Should convert from Z to R", () => {
const vz = bigInt(34);
const vr = R.fromF(vz);
assert(R.equals( vr, r(34,1)));
});
it("Should convert from R to Z", () => {
const vr = r(32, 2);
const vz = R.toF(vr);
assert(Z.equals( vz, bigInt(16)));
});
});

+ 67
- 0
test/zksnark.js

@ -0,0 +1,67 @@
const chai = require("chai");
const fs = require("fs");
const bigInt = require("../src/bigint.js");
const Circuit = require("../src/circuit.js");
const zkSnark = require("../index.js");
const assert = chai.assert;
function stringifyBigInts(o) {
if ((typeof(o) == "bigint") || (o instanceof bigInt)) {
return o.toString(10);
} else if (Array.isArray(o)) {
return o.map(stringifyBigInts);
} else if (typeof o == "object") {
const res = {};
for (let k in o) {
res[k] = stringifyBigInts(o[k]);
}
return res;
} else {
return o;
}
}
function unstringifyBigInts(o) {
if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) {
return bigInt(o);
} else if (Array.isArray(o)) {
return o.map(unstringifyBigInts);
} else if (typeof o == "object") {
const res = {};
for (let k in o) {
res[k] = unstringifyBigInts(o[k]);
}
return res;
} else {
return o;
}
}
describe("zkSnark", () => {
it("Load a circuit, create trusted setup, create a proof and validate", () => {
const cirDef = JSON.parse(fs.readFileSync("../jaz/sum.json", "utf8"));
const cir = new Circuit(cirDef);
const setup = zkSnark.setup(cir);
const strSetup = stringifyBigInts(setup);
fs.writeFileSync("vk_proof.json", JSON.stringify(strSetup.vk_proof), "utf-8");
fs.writeFileSync("vk_verifier.json", JSON.stringify(strSetup.vk_verifier), "utf-8");
/*
const setup = {};
setup.vk_proof = unstringifyBigInts(JSON.parse(fs.readFileSync("vk_proof.json", "utf8")));
setup.vk_verifier = unstringifyBigInts(JSON.parse(fs.readFileSync("vk_verifier.json", "utf8")));
*/
const witness = cir.calculateWitness({"a": "33", "b": "34"});
assert.equal(witness[cir.getSignalIdx("main.out")].toString(), "67");
const {proof, publicSignals} = zkSnark.genProof(setup.vk_proof, witness);
assert( zkSnark.isValid(setup.vk_verifier, proof, publicSignals));
}).timeout(10000000);
});

+ 1
- 0
vk_proof.json
File diff suppressed because it is too large
View File


+ 1
- 0
vk_verifier.json

@ -0,0 +1 @@
{"nPublic":2,"A":[["0","1","0"],["0","1","0"],["0","1","0"]],"vk_a":[["10910302893854256300335313159192947444388132687074523434871198422054676751347","7128911999163755080071576365381312078199150810378083348330952764137111826309"],["18631850238317279011886579705479432506796416675676074259923090354119752710032","11613125271011748641588689311610150724463404115865535933368983811438235418330"],["1","0"]],"vk_b":["14021005419725675778621050747090735283429719877068464218769467811976154477903","13400327218680122582347155709033494654797979408389561950304209881074672567956","1"],"vk_c":[["3096051994409557504719773510360462097114704303646108617318273214347944304327","21060178932557065464613441158524747948458716806166605021180362439829514495363"],["13711469505507426929293229841903695489923719718740047084134989818704335937579","16689306268247690470861364441748198782838298216373469479699367433528545900642"],["1","0"]],"vk_gb_1":["6323076061844787963801699179984388237278671131058334789970950232032140773376","1289442623160734848462556712373448525459100416297212719395950333399658173112","1"],"vk_gb_2":[["14738403669696391106523308033989669531459082697567002658683173458264070358472","7115351592298265627577071367133297887460251153742186717064165044604072075208"],["5684452168257595880393745672144928966665179424697978770339950828964351397925","16005652883870491274891326398599328665806046577140362628567012925774147115231"],["1","0"]],"vk_g":[["13057967944664071939530697379611124014113572609236714070685841838860689079766","4921285704318374954421284662839198492272478322335189504342768676294475453825"],["16821731243154385058674189668904278548308574642868092417486261078313511500423","14329710484989081757569399730710287771777297111484092478685760712065088557056"],["1","0"]],"vk_z":[["9830782349217318574531840014041507435528173882006141768847850813321843762209","908701408738093737682928637200323566556898851221230193697915803980061312337"],["18420854262130852746249857908832658786725598484918874058527841900342812415992","11905336095877065527602013373361492354917686666454749599264218906638347950295"],["1","0"]]}

Loading…
Cancel
Save