Important optimization for the trusted setup

This commit is contained in:
Jordi Baylina 2018-09-23 09:20:01 +02:00
parent 3343981187
commit 16ff407765
No known key found for this signature in database
GPG Key ID: 7480C80C1BE43112
10 changed files with 1162118 additions and 204 deletions

@ -25,6 +25,7 @@
*/
const bigInt = require("./bigint.js");
const assert = require("assert");
class PolField {
constructor (F) {
@ -52,6 +53,35 @@ class PolField {
n--;
}
this.roots = [];
/* for (let i=0; i<16; i++) {
let r = this.F.one;
n = 1 << i;
const rootsi = new Array(n);
for (let j=0; j<n; j++) {
rootsi[j] = r;
r = this.F.mul(r, this.w[i]);
}
this.roots.push(rootsi);
}
*/
this._setRoots(15);
}
_setRoots(n) {
for (let i=n; (i>=0) && (!this.roots[i]); i--) {
let r = this.F.one;
const nroots = 1 << i;
const rootsi = new Array(nroots);
for (let j=0; j<nroots; j++) {
rootsi[j] = r;
r = this.F.mul(r, this.w[i]);
}
this.roots[i] = rootsi;
}
}
add(a, b) {
@ -117,12 +147,14 @@ class PolField {
mulFFT(a,b) {
const longestN = Math.max(a.length, b.length);
const bitsResult = log2(longestN-1)+2;
this._setRoots(bitsResult);
const m = 1 << bitsResult;
const ea = this.extend(a,m);
const eb = this.extend(b,m);
const ta = this._fft(ea, bitsResult, 0, 1, false);
const tb = this._fft(eb, bitsResult, 0, 1, false);
const ta = __fft(this, ea, bitsResult, 0, 1, false);
const tb = __fft(this, eb, bitsResult, 0, 1, false);
const tres = new Array(m);
@ -130,7 +162,7 @@ class PolField {
tres[i] = this.F.mul(ta[i], tb[i]);
}
const res = this._fft(tres, bitsResult, 0, 1, true);
const res = __fft(this, tres, bitsResult, 0, 1, true);
const twoinvm = this.F.inverse( this.F.mulScalar(this.F.one, m) );
const resn = new Array(m);
@ -159,7 +191,7 @@ class PolField {
}
}
eval(p, x) {
eval2(p, x) {
let v = this.F.zero;
let ix = this.F.one;
for (let i=0; i<p.length; i++) {
@ -169,6 +201,26 @@ class PolField {
return v;
}
eval(p,x) {
const F = this.F;
if (p.length == 0) return F.zero;
const m = this._next2Power(p.length);
const ep = this.extend(p, m);
return _eval(ep, x, 0, 1, m);
function _eval(p, x, offset, step, n) {
if (n==1) return p[offset];
const newX = F.square(x);
const res= F.add(
_eval(p, newX, offset, step << 1, n >> 1),
F.mul(
x,
_eval(p, newX, offset+step , step << 1, n >> 1)));
return res;
}
}
lagrange(points) {
let roots = [this.F.one];
for (let i=0; i<points.length; i++) {
@ -188,6 +240,38 @@ class PolField {
return sum;
}
fft(p) {
if (p.length <= 1) return p;
const bits = log2(p.length-1)+1;
this._setRoots(bits);
const m = 1 << bits;
const ep = this.extend(p, m);
const res = __fft(this, ep, bits, 0, 1);
return res;
}
ifft(p) {
if (p.length <= 1) return p;
const bits = log2(p.length-1)+1;
this._setRoots(bits);
const m = 1 << bits;
const ep = this.extend(p, m);
const res = __fft(this, ep, bits, 0, 1);
const twoinvm = this.F.inverse( this.F.mulScalar(this.F.one, m) );
const resn = new Array(m);
for (let i=0; i<m; i++) {
resn[i] = this.F.mul(res[(m-i)%m], twoinvm);
}
return resn;
}
_fft(pall, bits, offset, step) {
const n = 1 << bits;
@ -354,6 +438,62 @@ class PolField {
return q;
}
// returns the ith nth-root of one
oneRoot(n, i) {
let nbits = log2(n-1)+1;
let res = this.F.one;
let r = i;
assert(i<n);
assert(1<<nbits === n);
while (r>0) {
if (r & 1 == 1) {
res = this.F.mul(res, this.w[nbits]);
}
r = r >> 1;
nbits --;
}
return res;
}
computeVanishingPolinomial(bits, t) {
const m = 1 << bits;
return this.F.sub(this.F.exp(t, bigInt(m)), this.F.one);
}
evaluateLagrangePolynomials(bits, t) {
const m= 1 << bits;
const tm = this.F.exp(t, bigInt(m));
const u= new Array(m).fill(this.F.zero);
this._setRoots(bits);
const omega = this.w[bits];
if (this.F.equals(tm, this.F.one)) {
for (let i = 0; i < m; i++) {
if (this.F.equals(this.roots[bits][0],t)) { // i.e., t equals omega^i
u[i] = this.F.one;
return u;
}
}
}
const z = this.F.sub(tm, this.F.one);
// let l = this.F.mul(z, this.F.exp(this.F.twoinv, m));
let l = this.F.mul(z, this.F.inverse(bigInt(m)));
for (let i = 0; i < m; i++) {
u[i] = this.F.mul(l, this.F.inverse(this.F.sub(t,this.roots[bits][i])));
l = this.F.mul(l, omega);
}
return u;
}
log2(V) {
return log2(V);
}
}
function log2( V )
@ -361,4 +501,31 @@ 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 ) );
}
function __fft(PF, pall, bits, offset, step) {
const n = 1 << bits;
if (n==1) {
return [ pall[offset] ];
} else if (n==2) {
return [
PF.F.add(pall[offset], pall[offset + step]),
PF.F.sub(pall[offset], pall[offset + step])];
}
const ndiv2 = n >> 1;
const p1 = __fft(PF, pall, bits-1, offset, step*2);
const p2 = __fft(PF, pall, bits-1, offset+step, step*2);
const out = new Array(n);
for (let i=0; i<ndiv2; i++) {
out[i] = PF.F.add(p1[i], PF.F.mul(PF.roots[bits][i], p2[i]));
out[i+ndiv2] = PF.F.sub(p1[i], PF.F.mul(PF.roots[bits][i], p2[i]));
}
return out;
}
module.exports = PolField;

@ -67,6 +67,7 @@ module.exports = function genProof(vk_proof, witness) {
proof.pi_kp = G1.add( proof.pi_kp, G1.mulScalar( vk_proof.Kp[s], witness[s]));
}
/*
let polA = [];
let polB = [];
let polC = [];
@ -91,11 +92,15 @@ module.exports = function genProof(vk_proof, witness) {
[witness[s]] ));
}
let polFull = PolF.sub(PolF.mul( polA, polB), polC);
const h = PolF.div(polFull, vk_proof.polZ );
*/
// console.log(h.length + "/" + vk_proof.hExps.length);
const h = calculateH(vk_proof, witness);
console.log(h.length + "/" + vk_proof.hExps.length);
for (let i = 0; i < h.length; i++) {
proof.pi_h = G1.add( proof.pi_h, G1.mulScalar( vk_proof.hExps[i], h[i]));
@ -110,7 +115,56 @@ module.exports = function genProof(vk_proof, witness) {
proof.pi_kp = G1.affine(proof.pi_kp);
proof.pi_h = G1.affine(proof.pi_h);
proof.h=h;
const publicSignals = witness.slice(1, vk_proof.nPublic+1);
return {proof, publicSignals};
};
function calculateH(vk_proof, witness) {
const F = PolF.F;
const m = vk_proof.domainSize;
const polA_T = new Array(m).fill(PolF.F.zero);
const polB_T = new Array(m).fill(PolF.F.zero);
const polC_T = new Array(m).fill(PolF.F.zero);
for (let s=0; s<vk_proof.nVars; s++) {
for (let c in vk_proof.polsA[s]) {
polA_T[c] = F.add(polA_T[c], F.mul(witness[s], vk_proof.polsA[s][c]));
}
for (let c in vk_proof.polsB[s]) {
polB_T[c] = F.add(polB_T[c], F.mul(witness[s], vk_proof.polsB[s][c]));
}
for (let c in vk_proof.polsC[s]) {
polC_T[c] = F.add(polC_T[c], F.mul(witness[s], vk_proof.polsC[s][c]));
}
}
const polA_S = PolF.ifft(polA_T);
const polB_S = PolF.ifft(polB_T);
const polAB_S = PolF.mul(polA_S, polB_S);
const polC_S = PolF.ifft(polC_T);
const polABC_S = PolF.sub(polAB_S, polC_S);
const polZ_S = new Array(m+1).fill(F.zero);
polZ_S[m] = F.one;
polZ_S[0] = F.neg(F.one);
const H_S = PolF.div(polABC_S, polZ_S);
/*
const H2S = PolF.mul(H_S, polZ_S);
if (PolF.equals(H2S, polABC_S)) {
console.log("Is Divisible!");
} else {
console.log("ERROR: Not divisible!");
}
*/
return H_S;
}

@ -43,7 +43,11 @@ module.exports = function setup(circuit) {
toxic: {}
};
calculatePolynomials(setup, circuit);
setup.vk_proof.domainBits = PolF.log2(circuit.nConstraints + circuit.nPubInputs + circuit.nOutputs +1 -1) +1;
setup.vk_proof.domainSize = 1 << setup.vk_proof.domainBits;
calculatePolinomials(setup, circuit);
setup.toxic.t = F.random();
calculateEncriptedValuesAtT(setup, circuit);
calculateHexps(setup, circuit);
@ -51,128 +55,80 @@ module.exports = function setup(circuit) {
return setup;
};
function calculatePolynomials(setup, circuit) {
// Calculate the points that must cross each polynomial
/*
setup.toxic.aExtra = [];
setup.toxic.bExtra = [];
setup.toxic.cExtra = [];
const aPoints = [];
const bPoints = [];
const cPoints = [];
for (let s = 0; s<circuit.nVars; s++) {
aPoints[s] = [];
bPoints[s] = [];
cPoints[s] = [];
for (let c=0; c<circuit.nConstraints; c++) {
aPoints[s].push([[bigInt(c), F.one], [circuit.a(c, s), F.one]]);
bPoints[s].push([[bigInt(c), F.one], [circuit.b(c, s), F.one]]);
cPoints[s].push([[bigInt(c), F.one], [circuit.c(c, s), F.one]]);
}
// Add an extra point to avoid constant polinolials.
setup.toxic.aExtra[s] = F.random();
setup.toxic.bExtra[s] = F.random();
setup.toxic.cExtra[s] = F.random();
aPoints[s].push([[bigInt(circuit.nConstraints), F.one], [setup.toxic.aExtra[s], F.one]]);
bPoints[s].push([[bigInt(circuit.nConstraints), F.one], [setup.toxic.bExtra[s], F.one]]);
cPoints[s].push([[bigInt(circuit.nConstraints), F.one], [setup.toxic.cExtra[s], F.one]]);
function calculatePolinomials(setup, circuit) {
setup.vk_proof.polsA = new Array(circuit.nVars);
setup.vk_proof.polsB = new Array(circuit.nVars);
setup.vk_proof.polsC = new Array(circuit.nVars);
for (let i=0; i<circuit.nVars; i++) {
setup.vk_proof.polsA[i] = {};
setup.vk_proof.polsB[i] = {};
setup.vk_proof.polsC[i] = {};
}
// Calculate the polynomials using Lagrange
setup.vk_proof.polsA = [];
setup.vk_proof.polsB = [];
setup.vk_proof.polsC = [];
for (let s=0; s<circuit.nVars; s++) {
// console.log(`Caclcualte Pol ${s}/${circuit.nVars}`);
const pA = RatPolF.lagrange( aPoints[s] );
const pB = RatPolF.lagrange( bPoints[s] );
const pC = RatPolF.lagrange( cPoints[s] );
setup.vk_proof.polsA.push( unrat(pA) );
setup.vk_proof.polsB.push( unrat(pB) );
setup.vk_proof.polsC.push( unrat(pC) );
}
*/
setup.toxic.aExtra = [];
setup.toxic.bExtra = [];
setup.toxic.cExtra = [];
let allZerosPol = [bigInt(1)];
for (let c=0; c<=circuit.nConstraints; c++) {
allZerosPol = PolF.mul(allZerosPol, [F.neg(bigInt(c)), F.one]);
}
setup.vk_proof.polsA = [];
setup.vk_proof.polsB = [];
setup.vk_proof.polsC = [];
for (let s = 0; s<circuit.nVars; s++) {
setup.vk_proof.polsA.push([]);
setup.vk_proof.polsB.push([]);
setup.vk_proof.polsC.push([]);
}
for (let c=0; c<circuit.nConstraints; c++) {
const mpol = PolF.ruffini(allZerosPol, bigInt(c));
const normalizer = PolF.F.inverse(PolF.eval(mpol, bigInt(c)));
for (let s = 0; s<circuit.nVars; s++) {
const factorA = PolF.F.mul(normalizer, circuit.a(c, s));
const spolA = PolF.mulScalar(mpol, factorA);
setup.vk_proof.polsA[s] = PolF.add(setup.vk_proof.polsA[s], spolA);
const factorB = PolF.F.mul(normalizer, circuit.b(c, s));
const spolB = PolF.mulScalar(mpol, factorB);
setup.vk_proof.polsB[s] = PolF.add(setup.vk_proof.polsB[s], spolB);
const factorC = PolF.F.mul(normalizer, circuit.c(c, s));
const spolC = PolF.mulScalar(mpol, factorC);
setup.vk_proof.polsC[s] = PolF.add(setup.vk_proof.polsC[s], spolC);
for (let s in circuit.constraints[c][0]) {
setup.vk_proof.polsA[s][c] = bigInt(circuit.constraints[c][0][s]);
}
for (let s in circuit.constraints[c][1]) {
setup.vk_proof.polsB[s][c] = bigInt(circuit.constraints[c][1][s]);
}
for (let s in circuit.constraints[c][2]) {
setup.vk_proof.polsC[s][c] = bigInt(circuit.constraints[c][2][s]);
}
}
const mpol = PolF.ruffini(allZerosPol, bigInt(circuit.nConstraints));
const normalizer = PolF.F.inverse(PolF.eval(mpol, bigInt(circuit.nConstraints)));
for (let s = 0; s<circuit.nVars; s++) {
setup.toxic.aExtra[s] = F.random();
const factorA = PolF.F.mul(normalizer, setup.toxic.aExtra[s]);
const spolA = PolF.mulScalar(mpol, factorA);
setup.vk_proof.polsA[s] = PolF.add(setup.vk_proof.polsA[s], spolA);
setup.toxic.bExtra[s] = F.random();
const factorB = PolF.F.mul(normalizer, setup.toxic.bExtra[s]);
const spolB = PolF.mulScalar(mpol, factorB);
setup.vk_proof.polsB[s] = PolF.add(setup.vk_proof.polsB[s], spolB);
setup.toxic.cExtra[s] = F.random();
const factorC = PolF.F.mul(normalizer, setup.toxic.cExtra[s]);
const spolC = PolF.mulScalar(mpol, factorC);
setup.vk_proof.polsC[s] = PolF.add(setup.vk_proof.polsC[s], spolC);
}
// Calculate Z polynomial
// Z = 1
setup.vk_proof.polZ = [bigInt(1)];
for (let c=0; c<circuit.nConstraints; c++) {
// Z = Z * (x - p_c)
setup.vk_proof.polZ = PolF.mul(
setup.vk_proof.polZ,
[F.neg(bigInt(c)), bigInt(1)] );
/**
* add and process the constraints
* input_i * 0 = 0
* to ensure soundness of input consistency
*/
for (let i = 0; i < circuit.nPubInputs + circuit.nOutputs + 1; ++i)
{
setup.vk_proof.polsA[i][circuit.nConstraints + i] = F.one;
}
}
function calculateValuesAtT(setup, circuit) {
const z_t = PolF.computeVanishingPolinomial(setup.vk_proof.domainBits, setup.toxic.t);
const u = PolF.evaluateLagrangePolynomials(setup.vk_proof.domainBits, setup.toxic.t);
const a_t = new Array(circuit.nVars).fill(F.zero);
const b_t = new Array(circuit.nVars).fill(F.zero);
const c_t = new Array(circuit.nVars).fill(F.zero);
// TODO: substitute setup.polsA for coeficients
for (let s=0; s<circuit.nVars; s++) {
for (let c in setup.vk_proof.polsA[s]) {
a_t[s] = F.add(a_t[s], F.mul(u[c], setup.vk_proof.polsA[s][c]));
}
for (let c in setup.vk_proof.polsB[s]) {
b_t[s] = F.add(b_t[s], F.mul(u[c], setup.vk_proof.polsB[s][c]));
}
for (let c in setup.vk_proof.polsC[s]) {
c_t[s] = F.add(c_t[s], F.mul(u[c], setup.vk_proof.polsC[s][c]));
}
}
return {a_t, b_t, c_t, z_t};
}
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 = [];
const v = calculateValuesAtT(setup, circuit);
setup.vk_proof.A = new Array(circuit.nVars);
setup.vk_proof.B = new Array(circuit.nVars);
setup.vk_proof.C = new Array(circuit.nVars);
setup.vk_proof.Ap = new Array(circuit.nVars);
setup.vk_proof.Bp = new Array(circuit.nVars);
setup.vk_proof.Cp = new Array(circuit.nVars);
setup.vk_proof.Kp = new Array(circuit.nVars);
setup.vk_verifier.A = new Array(circuit.nVars);
setup.toxic.ka = F.random();
setup.toxic.kb = F.random();
@ -192,37 +148,34 @@ function calculateEncriptedValuesAtT(setup, circuit) {
for (let s=0; s<circuit.nVars; s++) {
// A[i] = G1 * polA(t)
const at = F.affine(PolF.eval(setup.vk_proof.polsA[s], setup.toxic.t));
const A = G1.affine(G1.mulScalar(G1.g, at));
const A = G1.affine(G1.mulScalar(G1.g, v.a_t[s]));
setup.vk_proof.A.push(A);
setup.vk_proof.A[s] = A;
if (s <= setup.vk_proof.nPublic) {
setup.vk_verifier.A.push(A);
setup.vk_verifier.A[s]=A;
}
// B1[i] = G1 * polB(t)
const bt = F.affine(PolF.eval(setup.vk_proof.polsB[s], setup.toxic.t));
const B1 = G1.affine(G1.mulScalar(G1.g, bt));
const B1 = G1.affine(G1.mulScalar(G1.g, v.b_t[s]));
// B2[i] = G2 * polB(t)
const B2 = G2.affine(G2.mulScalar(G2.g, bt));
const B2 = G2.affine(G2.mulScalar(G2.g, v.b_t[s]));
setup.vk_proof.B.push(B2);
setup.vk_proof.B[s]=B2;
// C[i] = G1 * polC(t)
const ct = F.affine(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);
const C = G1.affine(G1.mulScalar( G1.g, v.c_t[s]));
setup.vk_proof.C[s] =C;
// K = G1 * (A+B+C)
const kt = F.affine(F.add(F.add(at, bt), ct));
const kt = F.affine(F.add(F.add(v.a_t[s], v.b_t[s]), v.c_t[s]));
const K = G1.affine(G1.mulScalar( G1.g, kt));
// Comment this lines to improve the process
const Ktest = G1.affine(G1.add(G1.add(A, B1), C));
if (!G1.equals(K, Ktest)) {
@ -231,30 +184,20 @@ function calculateEncriptedValuesAtT(setup, circuit) {
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_proof.Ap[s] = G1.affine(G1.mulScalar(A, setup.toxic.ka));
setup.vk_proof.Bp[s] = G1.affine(G1.mulScalar(B1, setup.toxic.kb));
setup.vk_proof.Cp[s] = G1.affine(G1.mulScalar(C, setup.toxic.kc));
setup.vk_proof.Kp[s] = G1.affine(G1.mulScalar(K, setup.toxic.kbeta));
}
setup.vk_verifier.vk_z = G2.affine(G2.mulScalar(
G2.g,
PolF.eval(setup.vk_proof.polZ, setup.toxic.t)));
v.z_t));
}
function calculateHexps(setup, circuit) {
let maxA = 0;
let maxB = 0;
let maxC = 0;
for (let s=0; s<circuit.nVars; 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);
const maxH = maxFull - setup.vk_proof.polZ.length + 1;
const maxH = setup.vk_proof.domainSize;
setup.vk_proof.hExps = new Array(maxH);
setup.vk_proof.hExps[0] = G1.g;
@ -264,12 +207,4 @@ function calculateHexps(setup, circuit) {
eT = F.mul(eT, setup.toxic.t);
}
}
/*
function unrat(p) {
const res = new Array(p.length);
for (let i=0; i<p.length; i++) {
res[i] = RatPolF.F.toF(p[i]);
}
return res;
}
*/

353
src/setup_old.js Normal file

@ -0,0 +1,353 @@
/*
Copyright 2018 0kims association.
This file is part of zksnark JavaScript library.
zksnark JavaScript library is a free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your option)
any later version.
zksnark JavaScript library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
zksnark JavaScript library. If not, see <https://www.gnu.org/licenses/>.
*/
const bigInt = require("./bigint.js");
const BN128 = require("./bn128.js");
const PolField = require("./polfield.js");
const ZqField = require("./zqfield.js");
const RatField = require("./ratfield.js");
const bn128 = new BN128();
const G1 = bn128.G1;
const G2 = bn128.G2;
const PolF = new PolField(new ZqField(bn128.r));
const RatPolF = new PolField(new RatField(new ZqField(bn128.r)));
const F = new ZqField(bn128.r);
module.exports = function setup(circuit) {
const setup = {
vk_proof : {
nVars: circuit.nVars,
nPublic: circuit.nPubInputs + circuit.nOutputs
},
vk_verifier: {
nPublic: circuit.nPubInputs + circuit.nOutputs
},
toxic: {}
};
calculatePolynomials2(setup, circuit);
setup.toxic.t = F.random();
calculateEncriptedValuesAtT(setup, circuit);
calculateHexps(setup, circuit);
return setup;
};
function calculatePolynomials(setup, circuit) {
// Calculate the points that must cross each polynomial
/*
setup.toxic.aExtra = [];
setup.toxic.bExtra = [];
setup.toxic.cExtra = [];
const aPoints = [];
const bPoints = [];
const cPoints = [];
for (let s = 0; s<circuit.nVars; s++) {
aPoints[s] = [];
bPoints[s] = [];
cPoints[s] = [];
for (let c=0; c<circuit.nConstraints; c++) {
aPoints[s].push([[bigInt(c), F.one], [circuit.a(c, s), F.one]]);
bPoints[s].push([[bigInt(c), F.one], [circuit.b(c, s), F.one]]);
cPoints[s].push([[bigInt(c), F.one], [circuit.c(c, s), F.one]]);
}
// Add an extra point to avoid constant polinolials.
setup.toxic.aExtra[s] = F.random();
setup.toxic.bExtra[s] = F.random();
setup.toxic.cExtra[s] = F.random();
aPoints[s].push([[bigInt(circuit.nConstraints), F.one], [setup.toxic.aExtra[s], F.one]]);
bPoints[s].push([[bigInt(circuit.nConstraints), F.one], [setup.toxic.bExtra[s], F.one]]);
cPoints[s].push([[bigInt(circuit.nConstraints), F.one], [setup.toxic.cExtra[s], F.one]]);
}
// Calculate the polynomials using Lagrange
setup.vk_proof.polsA = [];
setup.vk_proof.polsB = [];
setup.vk_proof.polsC = [];
for (let s=0; s<circuit.nVars; s++) {
// console.log(`Caclcualte Pol ${s}/${circuit.nVars}`);
const pA = RatPolF.lagrange( aPoints[s] );
const pB = RatPolF.lagrange( bPoints[s] );
const pC = RatPolF.lagrange( cPoints[s] );
setup.vk_proof.polsA.push( unrat(pA) );
setup.vk_proof.polsB.push( unrat(pB) );
setup.vk_proof.polsC.push( unrat(pC) );
}
*/
setup.toxic.aExtra = [];
setup.toxic.bExtra = [];
setup.toxic.cExtra = [];
let allZerosPol = [bigInt(1)];
for (let c=0; c<=circuit.nConstraints; c++) {
allZerosPol = PolF.mul(allZerosPol, [F.neg(bigInt(c)), F.one]);
}
setup.vk_proof.polsA = [];
setup.vk_proof.polsB = [];
setup.vk_proof.polsC = [];
for (let s = 0; s<circuit.nVars; s++) {
setup.vk_proof.polsA.push([]);
setup.vk_proof.polsB.push([]);
setup.vk_proof.polsC.push([]);
}
for (let c=0; c<circuit.nConstraints; c++) {
console.log("Pol: "+ c);
const mpol = PolF.ruffini(allZerosPol, bigInt(c));
const normalizer = PolF.F.inverse(PolF.eval(mpol, bigInt(c)));
for (let s = 0; s<circuit.nVars; s++) {
if (!circuit.a(c, s).isZero()) {
const factorA = PolF.F.mul(normalizer, circuit.a(c, s));
const spolA = PolF.mulScalar(mpol, factorA);
setup.vk_proof.polsA[s] = PolF.add(setup.vk_proof.polsA[s], spolA);
}
if (!circuit.b(c, s).isZero()) {
const factorB = PolF.F.mul(normalizer, circuit.b(c, s));
const spolB = PolF.mulScalar(mpol, factorB);
setup.vk_proof.polsB[s] = PolF.add(setup.vk_proof.polsB[s], spolB);
}
if (!circuit.c(c, s).isZero()) {
const factorC = PolF.F.mul(normalizer, circuit.c(c, s));
const spolC = PolF.mulScalar(mpol, factorC);
setup.vk_proof.polsC[s] = PolF.add(setup.vk_proof.polsC[s], spolC);
}
}
if (global.gc) {
if (c%100 == 0) global.gc();
}
}
const mpol = PolF.ruffini(allZerosPol, bigInt(circuit.nConstraints));
const normalizer = PolF.F.inverse(PolF.eval(mpol, bigInt(circuit.nConstraints)));
for (let s = 0; s<circuit.nVars; s++) {
setup.toxic.aExtra[s] = F.random();
const factorA = PolF.F.mul(normalizer, setup.toxic.aExtra[s]);
const spolA = PolF.mulScalar(mpol, factorA);
setup.vk_proof.polsA[s] = PolF.add(setup.vk_proof.polsA[s], spolA);
setup.toxic.bExtra[s] = F.random();
const factorB = PolF.F.mul(normalizer, setup.toxic.bExtra[s]);
const spolB = PolF.mulScalar(mpol, factorB);
setup.vk_proof.polsB[s] = PolF.add(setup.vk_proof.polsB[s], spolB);
setup.toxic.cExtra[s] = F.random();
const factorC = PolF.F.mul(normalizer, setup.toxic.cExtra[s]);
const spolC = PolF.mulScalar(mpol, factorC);
setup.vk_proof.polsC[s] = PolF.add(setup.vk_proof.polsC[s], spolC);
}
// Calculate Z polynomial
// Z = 1
setup.vk_proof.polZ = [bigInt(1)];
for (let c=0; c<circuit.nConstraints; c++) {
// Z = Z * (x - p_c)
setup.vk_proof.polZ = PolF.mul(
setup.vk_proof.polZ,
[F.neg(bigInt(c)), bigInt(1)] );
}
}
function calculatePolynomials2(setup, circuit) {
setup.toxic.aExtra = [];
setup.toxic.bExtra = [];
setup.toxic.cExtra = [];
setup.vk_proof.polsA = [];
setup.vk_proof.polsB = [];
setup.vk_proof.polsC = [];
const aPoints = new Array(circuit.nConstraints+1);
const bPoints = new Array(circuit.nConstraints+1);
const cPoints = new Array(circuit.nConstraints+1);
for (let s = 0; s<circuit.nVars; s++) {
for (let c=0; c<circuit.nConstraints; c++) {
aPoints[c] = circuit.a(c, s);
bPoints[c] = circuit.b(c, s);
cPoints[c] = circuit.c(c, s);
}
// Add an extra point to avoid constant polinolials.
setup.toxic.aExtra[s] = F.random();
setup.toxic.bExtra[s] = F.random();
setup.toxic.cExtra[s] = F.random();
aPoints[circuit.nConstraints] = setup.toxic.aExtra[s];
bPoints[circuit.nConstraints] = setup.toxic.bExtra[s];
cPoints[circuit.nConstraints] = setup.toxic.cExtra[s];
const pA = PolF.ifft( aPoints );
const pB = PolF.ifft( bPoints );
const pC = PolF.ifft( cPoints );
setup.vk_proof.polsA.push( PolF.affine(pA) );
setup.vk_proof.polsB.push( PolF.affine(pB) );
setup.vk_proof.polsC.push( PolF.affine(pC) );
console.log(s);
if (global.gc) {
if (s%100 == 0) global.gc();
}
}
setup.polsLen=PolF._next2Power(circuit.nConstraints+1);
// Calculate Z polynomial
// Z = 1
const bits= log2(circuit.nConstraints)+1;
const rt1 = PolF.w[bits];
let rt = bigInt(1);
setup.vk_proof.polZ = [F.one];
for (let c=0; c<circuit.nConstraints; c++) {
// Z = Z * (x - p_c)
setup.vk_proof.polZ = PolF.mul(
setup.vk_proof.polZ,
[F.neg(rt), F.one] );
rt = F.mul(rt, rt1);
}
setup.vk_proof.polZ = PolF.affine(setup.vk_proof.polZ);
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 ) );
}
}
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.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.nVars; s++) {
// A[i] = G1 * polA(t)
const at = F.affine(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);
if (s <= setup.vk_proof.nPublic) {
setup.vk_verifier.A.push(A);
}
// B1[i] = G1 * polB(t)
const bt = F.affine(PolF.eval(setup.vk_proof.polsB[s], setup.toxic.t));
const B1 = G1.affine(G1.mulScalar(G1.g, bt));
// B2[i] = G2 * polB(t)
const B2 = G2.affine(G2.mulScalar(G2.g, bt));
setup.vk_proof.B.push(B2);
// C[i] = G1 * polC(t)
const ct = F.affine(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);
// K = G1 * (A+B+C)
const kt = F.affine(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.affine(G2.mulScalar(
G2.g,
PolF.eval(setup.vk_proof.polZ, setup.toxic.t)));
}
function calculateHexps(setup, circuit) {
let maxA = 0;
let maxB = 0;
let maxC = 0;
for (let s=0; s<circuit.nVars; 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);
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.affine(G1.mulScalar(G1.g, eT));
eT = F.mul(eT, setup.toxic.t);
}
}
/*
function unrat(p) {
const res = new Array(p.length);
for (let i=0; i<p.length; i++) {
res[i] = RatPolF.F.toF(p[i]);
}
return res;
}
*/

@ -49,14 +49,6 @@ module.exports = function isValid(vk_verifier, proof, publicSignals) {
bn128.pairing( proof.pi_cp , G2.g )))
return false;
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;
if (! bn128.F12.equals(
bn128.F12.mul(
bn128.pairing( G1.add(full_pi_a, proof.pi_c) , vk_verifier.vk_gb_2 ),
@ -65,6 +57,15 @@ module.exports = function isValid(vk_verifier, proof, publicSignals) {
bn128.pairing( proof.pi_kp , vk_verifier.vk_g )))
return false;
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 true;

1161222
test/circuit/sha256_2.json Normal file

File diff suppressed because one or more lines are too long

@ -128,6 +128,12 @@ describe("Polynomial field", () => {
const v = PF.eval(p, bigInt(2));
assert(PF.F.equals(v, bigInt(0)));
});
it("Should evaluate bigger number", () => {
const PF = new PolField(new ZqField(r));
const p = [bigInt(1), bigInt(2), bigInt(3)];
const v = PF.eval(p, bigInt(2));
assert(PF.F.equals(v, bigInt(17)));
});
it("Should create lagrange polynomial minmal", () => {
const PF = new PolField(new ZqField(r));
@ -168,5 +174,44 @@ describe("Polynomial field", () => {
assert(PF.equals(a, c));
});
it("Should test roots", () => {
const PF = new PolField(new ZqField(r));
let rt;
rt = PF.oneRoot(256, 16);
for (let i=0; i<8; i++) {
rt = PF.F.mul(rt, rt);
}
assert(rt.equals(PF.F.one));
rt = PF.oneRoot(256, 15);
for (let i=0; i<8; i++) {
rt = PF.F.mul(rt, rt);
}
assert(rt.equals(PF.F.one));
rt = PF.oneRoot(8, 3);
for (let i=0; i<3; i++) {
rt = PF.F.mul(rt, rt);
}
assert(rt.equals(PF.F.one));
rt = PF.oneRoot(8, 0);
assert(rt.equals(PF.F.one));
});
it("Should create a polynomial with values at roots with fft", () => {
const PF = new PolField(new ZqField(r));
const a = [bigInt(1), bigInt(2), bigInt(3), bigInt(4), bigInt(5),bigInt(6), bigInt(7)];
const p = PF.ifft(a);
for (let i=0; i<a.length; i++) {
const s = PF.F.affine(PF.eval(p, PF.oneRoot(8,i)));
assert(s.equals(a[i]));
}
});
});

@ -24,6 +24,13 @@ const bigInt = require("../src/bigint.js");
const Circuit = require("../src/circuit.js");
const zkSnark = require("../index.js");
const BN128 = require("../src/bn128.js");
const PolField = require("../src/polfield.js");
const ZqField = require("../src/zqfield.js");
const bn128 = new BN128();
const PolF = new PolField(new ZqField(bn128.r));
const G1 = bn128.G1;
const G2 = bn128.G2;
const assert = chai.assert;
@ -60,9 +67,11 @@ function unstringifyBigInts(o) {
}
}
describe("zkSnark", () => {
it("Load a circuit, create trusted setup, create a proof and validate it", () => {
const cirDef = JSON.parse(fs.readFileSync(path.join(__dirname, "circuit", "sum.json"), "utf8"));
const cir = new Circuit(cirDef);
@ -71,17 +80,145 @@ describe("zkSnark", () => {
fs.writeFileSync("vk_proof.json", JSON.stringify(strSetup.vk_proof), "utf-8");
fs.writeFileSync("vk_verifier.json", JSON.stringify(strSetup.vk_verifier), "utf-8");
function polT2S(p) {
const p_T = new Array(setup.vk_proof.domainSize).fill(bigInt(0));
for (let c in p) {
p_T[c] = p[c];
}
return PolF.ifft(p_T);
}
/*
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"});
const {proof, publicSignals} = zkSnark.genProof(setup.vk_proof, witness);
/*
const polA = new Array(cir.nVars);
const polB = new Array(cir.nVars);
const polC = new Array(cir.nVars);
for (let i=0; i<cir.nVars; i++) {
polA[i] = polT2S(setup.vk_proof.polsA[i]);
polB[i] = polT2S(setup.vk_proof.polsB[i]);
polC[i] = polT2S(setup.vk_proof.polsC[i]);
}
PolF._setRoots(setup.vk_proof.domainBits);
for (let c=0; c<setup.vk_proof.domainSize; c++) {
let A = bigInt(0);
let B = bigInt(0);
let C = bigInt(0);
for (let s=0; s<cir.nVars; s++) {
A = PolF.F.add(A, PolF.F.mul(PolF.eval(polA[s], PolF.roots[setup.vk_proof.domainBits][c]), witness[s]));
B = PolF.F.add(B, PolF.F.mul(PolF.eval(polB[s], PolF.roots[setup.vk_proof.domainBits][c]), witness[s]));
C = PolF.F.add(C, PolF.F.mul(PolF.eval(polC[s], PolF.roots[setup.vk_proof.domainBits][c]), witness[s]));
}
assert(PolF.F.equals(PolF.F.mul(A,B), C));
}
let A = bigInt(0);
let B = bigInt(0);
let C = bigInt(0);
for (let s=0; s<cir.nVars; s++) {
A = PolF.F.add(A, PolF.F.mul(PolF.eval(polA[s], setup.toxic.t), witness[s]));
B = PolF.F.add(B, PolF.F.mul(PolF.eval(polB[s], setup.toxic.t), witness[s]));
C = PolF.F.add(C, PolF.F.mul(PolF.eval(polC[s], setup.toxic.t), witness[s]));
}
let A2 = bigInt(0);
let B2 = bigInt(0);
let C2 = bigInt(0);
const u = PolF.evaluateLagrangePolynomials(setup.vk_proof.domainBits, setup.toxic.t);
for (let s=0; s<cir.nVars; s++) {
let at = PolF.F.zero;
for (let c in setup.vk_proof.polsA[s]) {
at = PolF.F.add(at, PolF.F.mul(u[c], setup.vk_proof.polsA[s][c]));
}
A2 = PolF.F.add(A2, PolF.F.mul(at, witness[s]));
let bt = PolF.F.zero;
for (let c in setup.vk_proof.polsB[s]) {
bt = PolF.F.add(bt, PolF.F.mul(u[c], setup.vk_proof.polsB[s][c]));
}
B2 = PolF.F.add(B2, PolF.F.mul(bt, witness[s]));
let ct = PolF.F.zero;
for (let c in setup.vk_proof.polsC[s]) {
ct = PolF.F.add(ct, PolF.F.mul(u[c], setup.vk_proof.polsC[s][c]));
}
C2 = PolF.F.add(C2, PolF.F.mul(ct, witness[s]));
}
A=PolF.F.affine(A);
B=PolF.F.affine(B);
C=PolF.F.affine(C);
A2=PolF.F.affine(A2);
B2=PolF.F.affine(B2);
C2=PolF.F.affine(C2);
assert(PolF.F.equals(C,C2));
assert(PolF.F.equals(B,B2));
assert(PolF.F.equals(A,A2));
const ABC = PolF.F.affine(PolF.F.sub(PolF.F.mul(A,B), C));
assert.equal(witness[cir.getSignalIdx("main.out")].toString(), "67");
const H = PolF.eval(proof.h, setup.toxic.t).affine();
const Z = PolF.F.sub(PolF.F.exp(setup.toxic.t, setup.vk_proof.domainSize), bigInt(1));
const HZ = PolF.F.affine(PolF.F.mul(H,Z));
assert(PolF.F.equals(ABC, HZ));
const gH = G1.affine(G1.mulScalar( G1.g, H));
const gZ = G2.affine(G2.mulScalar( G2.g, Z));
const gA = G1.affine(G1.mulScalar( G1.g, A));
const gB = G2.affine(G2.mulScalar( G2.g, B));
const gC = G1.affine(G1.mulScalar( G1.g, C));
assert(G1.equals(gH, proof.pi_h));
assert(G2.equals(gZ, setup.vk_verifier.vk_z));
assert(G2.equals(gB, proof.pi_b));
assert(G1.equals(gC, proof.pi_c));
// assert(G1.equals(gA, proof.pi_a));
*/
assert( zkSnark.isValid(setup.vk_verifier, proof, publicSignals));
}).timeout(10000000);
it("validate sha256_2", () => {
const cirDef = JSON.parse(fs.readFileSync(path.join(__dirname, "circuit", "sha256_2.json"), "utf8"));
const cir = new Circuit(cirDef);
console.log("Start setup: "+Date().toString());
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"});
const witness = cir.calculateWitness({"a": "1", "b": "2"});
assert.equal(witness[cir.getSignalIdx("main.out")].toString(), "67");
// assert.equal(witness[cir.getSignalIdx("main.out")].toString(), "67");
console.log("Start calculating the proof: "+Date().toString());
const {proof, publicSignals} = zkSnark.genProof(setup.vk_proof, witness);
console.log("Start verifiying: "+ Date().toString());
assert( zkSnark.isValid(setup.vk_verifier, proof, publicSignals));
}).timeout(10000000);
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long