UNPKG

@kotevode/ffjavascript

Version:

Finite Field Library in Javascript

2,081 lines (1,675 loc) 170 kB
'use strict'; var crypto = require('crypto'); var wasmcurves = require('wasmcurves'); var os = require('os'); var Worker = require('@kotevode/web-worker'); var wasmbuilder = require('wasmbuilder'); /* global BigInt */ const hexLen = [ 0, 1, 2, 2, 3, 3, 3, 3, 4 ,4 ,4 ,4 ,4 ,4 ,4 ,4]; function fromString(s, radix) { if ((!radix)||(radix==10)) { return BigInt(s); } else if (radix==16) { if (s.slice(0,2) == "0x") { return BigInt(s); } else { return BigInt("0x"+s); } } } const e = fromString; function fromArray(a, radix) { let acc =BigInt(0); radix = BigInt(radix); for (let i=0; i<a.length; i++) { acc = acc*radix + BigInt(a[i]); } return acc; } function bitLength(a) { const aS =a.toString(16); return (aS.length-1)*4 +hexLen[parseInt(aS[0], 16)]; } function isNegative(a) { return BigInt(a) < BigInt(0); } function isZero(a) { return !a; } function shiftLeft(a, n) { return BigInt(a) << BigInt(n); } function shiftRight(a, n) { return BigInt(a) >> BigInt(n); } const shl = shiftLeft; const shr = shiftRight; function isOdd(a) { return (BigInt(a) & BigInt(1)) == BigInt(1); } function naf(n) { let E = BigInt(n); const res = []; while (E) { if (E & BigInt(1)) { const z = 2 - Number(E % BigInt(4)); res.push( z ); E = E - BigInt(z); } else { res.push( 0 ); } E = E >> BigInt(1); } return res; } function bits(n) { let E = BigInt(n); const res = []; while (E) { if (E & BigInt(1)) { res.push(1); } else { res.push( 0 ); } E = E >> BigInt(1); } return res; } function toNumber(s) { if (s>BigInt(Number.MAX_SAFE_INTEGER )) { throw new Error("Number too big"); } return Number(s); } function toArray(s, radix) { const res = []; let rem = BigInt(s); radix = BigInt(radix); while (rem) { res.unshift( Number(rem % radix)); rem = rem / radix; } return res; } function add(a, b) { return BigInt(a) + BigInt(b); } function sub(a, b) { return BigInt(a) - BigInt(b); } function neg(a) { return -BigInt(a); } function mul(a, b) { return BigInt(a) * BigInt(b); } function square(a) { return BigInt(a) * BigInt(a); } function pow(a, b) { return BigInt(a) ** BigInt(b); } function exp$1(a, b) { return BigInt(a) ** BigInt(b); } function abs(a) { return BigInt(a) >= 0 ? BigInt(a) : -BigInt(a); } function div(a, b) { return BigInt(a) / BigInt(b); } function mod(a, b) { return BigInt(a) % BigInt(b); } function eq(a, b) { return BigInt(a) == BigInt(b); } function neq(a, b) { return BigInt(a) != BigInt(b); } function lt(a, b) { return BigInt(a) < BigInt(b); } function gt(a, b) { return BigInt(a) > BigInt(b); } function leq(a, b) { return BigInt(a) <= BigInt(b); } function geq(a, b) { return BigInt(a) >= BigInt(b); } function band(a, b) { return BigInt(a) & BigInt(b); } function bor(a, b) { return BigInt(a) | BigInt(b); } function bxor(a, b) { return BigInt(a) ^ BigInt(b); } function land(a, b) { return BigInt(a) && BigInt(b); } function lor(a, b) { return BigInt(a) || BigInt(b); } function lnot(a) { return !BigInt(a); } // Returns a buffer with Little Endian Representation function toRprLE(buff, o, e, n8) { const s = "0000000" + e.toString(16); const v = new Uint32Array(buff.buffer, buff.byteOffset + o, n8/4); const l = (((s.length-7)*4 - 1) >> 5)+1; // Number of 32bit words; for (let i=0; i<l; i++) v[i] = parseInt(s.substring(s.length-8*i-8, s.length-8*i), 16); for (let i=l; i<v.length; i++) v[i] = 0; for (let i=v.length*4; i<n8; i++) buff[i] = toNumber(band(shiftRight(e, i*8), 0xFF)); } // Returns a buffer with Big Endian Representation function toRprBE(buff, o, e, n8) { const s = "0000000" + e.toString(16); const v = new DataView(buff.buffer, buff.byteOffset + o, n8); const l = (((s.length-7)*4 - 1) >> 5)+1; // Number of 32bit words; for (let i=0; i<l; i++) v.setUint32(n8-i*4 -4, parseInt(s.substring(s.length-8*i-8, s.length-8*i), 16), false); for (let i=0; i<n8/4-l; i++) v[i] = 0; } // Pases a buffer with Little Endian Representation function fromRprLE(buff, o, n8) { n8 = n8 || buff.byteLength; o = o || 0; const v = new Uint32Array(buff.buffer, buff.byteOffset + o, n8/4); const a = new Array(n8/4); v.forEach( (ch,i) => a[a.length-i-1] = ch.toString(16).padStart(8,"0") ); return fromString(a.join(""), 16); } // Pases a buffer with Big Endian Representation function fromRprBE(buff, o, n8) { n8 = n8 || buff.byteLength; o = o || 0; const v = new DataView(buff.buffer, buff.byteOffset + o, n8); const a = new Array(n8/4); for (let i=0; i<n8/4; i++) { a[i] = v.getUint32(i*4, false).toString(16).padStart(8, "0"); } return fromString(a.join(""), 16); } function toString(a, radix) { return a.toString(radix); } function toLEBuff(a) { const buff = new Uint8Array(Math.floor((bitLength(a) - 1) / 8) +1); toRprLE(buff, 0, a, buff.byteLength); return buff; } const zero = e(0); const one = e(1); var _Scalar = /*#__PURE__*/Object.freeze({ __proto__: null, abs: abs, add: add, band: band, bitLength: bitLength, bits: bits, bor: bor, bxor: bxor, div: div, e: e, eq: eq, exp: exp$1, fromArray: fromArray, fromRprBE: fromRprBE, fromRprLE: fromRprLE, fromString: fromString, geq: geq, gt: gt, isNegative: isNegative, isOdd: isOdd, isZero: isZero, land: land, leq: leq, lnot: lnot, lor: lor, lt: lt, mod: mod, mul: mul, naf: naf, neg: neg, neq: neq, one: one, pow: pow, shiftLeft: shiftLeft, shiftRight: shiftRight, shl: shl, shr: shr, square: square, sub: sub, toArray: toArray, toLEBuff: toLEBuff, toNumber: toNumber, toRprBE: toRprBE, toRprLE: toRprLE, toString: toString, zero: zero }); /* Copyright 2018 0kims association. This file is part of snarkjs. snarkjs 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. snarkjs 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 snarkjs. If not, see <https://www.gnu.org/licenses/>. */ /* This library does operations on polynomials with coefficients in a field F. A polynomial P(x) = p0 + p1 * x + p2 * x^2 + ... + pn * x^n is represented by the array [ p0, p1, p2, ... , pn ]. */ class PolField { constructor (F) { this.F = F; let rem = F.sqrt_t; let s = F.sqrt_s; const five = this.F.add(this.F.add(this.F.two, this.F.two), this.F.one); this.w = new Array(s+1); this.wi = new Array(s+1); this.w[s] = this.F.pow(five, rem); this.wi[s] = this.F.inv(this.w[s]); let n=s-1; while (n>=0) { this.w[n] = this.F.square(this.w[n+1]); this.wi[n] = this.F.square(this.wi[n+1]); 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) { if (n > this.F.sqrt_s) n = this.s; 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) { const m = Math.max(a.length, b.length); const res = new Array(m); for (let i=0; i<m; i++) { res[i] = this.F.add(a[i] || this.F.zero, b[i] || this.F.zero); } return this.reduce(res); } double(a) { return this.add(a,a); } sub(a, b) { const m = Math.max(a.length, b.length); const res = new Array(m); for (let i=0; i<m; i++) { res[i] = this.F.sub(a[i] || this.F.zero, b[i] || this.F.zero); } return this.reduce(res); } mulScalar(p, b) { if (this.F.eq(b, this.F.zero)) return []; if (this.F.eq(b, this.F.one)) return p; const res = new Array(p.length); for (let i=0; i<p.length; i++) { res[i] = this.F.mul(p[i], b); } return res; } mul(a, b) { if (a.length == 0) return []; if (b.length == 0) return []; 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 <= 2) || (b.length < log2$2(a.length))) { return this.mulNormal(a,b); } else { return this.mulFFT(a,b); } } mulNormal(a, b) { let res = []; 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 bitsResult = log2$2(longestN-1)+2; this._setRoots(bitsResult); const m = 1 << bitsResult; const ea = this.extend(a,m); const eb = this.extend(b,m); const ta = __fft$1(this, ea, bitsResult, 0, 1); const tb = __fft$1(this, eb, bitsResult, 0, 1); const tres = new Array(m); for (let i=0; i<m; i++) { tres[i] = this.F.mul(ta[i], tb[i]); } const res = __fft$1(this, tres, bitsResult, 0, 1); const twoinvm = this.F.inv( 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 this.reduce(resn); } square(a) { return this.mul(a,a); } scaleX(p, n) { if (n==0) { return p; } else if (n>0) { const z = new Array(n).fill(this.F.zero); return z.concat(p); } else { if (-n >= p.length) return []; return p.slice(-n); } } eval2(p, x) { let v = this.F.zero; let ix = this.F.one; for (let i=0; i<p.length; i++) { v = this.F.add(v, this.F.mul(p[i], ix)); ix = this.F.mul(ix, x); } return v; } evaluate(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++) { roots = this.mul(roots, [this.F.neg(points[i][0]), this.F.one]); } let sum = []; for (let i=0; i<points.length; i++) { let mpol = this.ruffini(roots, points[i][0]); const factor = this.F.mul( this.F.inv(this.evaluate(mpol, points[i][0])), points[i][1]); mpol = this.mulScalar(mpol, factor); sum = this.add(sum, mpol); } return sum; } fft(p) { if (p.length <= 1) return p; const bits = log2$2(p.length-1)+1; this._setRoots(bits); const m = 1 << bits; const ep = this.extend(p, m); const res = __fft$1(this, ep, bits, 0, 1); return res; } fft2(p) { if (p.length <= 1) return p; const bits = log2$2(p.length-1)+1; this._setRoots(bits); const m = 1 << bits; const ep = this.extend(p, m); __bitReverse(ep, bits); const res = __fft2(this, ep, bits); return res; } ifft(p) { if (p.length <= 1) return p; const bits = log2$2(p.length-1)+1; this._setRoots(bits); const m = 1 << bits; const ep = this.extend(p, m); const res = __fft$1(this, ep, bits, 0, 1); const twoinvm = this.F.inv( 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; } ifft2(p) { if (p.length <= 1) return p; const bits = log2$2(p.length-1)+1; this._setRoots(bits); const m = 1 << bits; const ep = this.extend(p, m); __bitReverse(ep, bits); const res = __fft2(this, ep, bits); const twoinvm = this.F.inv( 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; if (n==1) { return [ pall[offset] ]; } const ndiv2 = n >> 1; const p1 = this._fft(pall, bits-1, offset, step*2); const p2 = this._fft(pall, bits-1, offset+step, step*2); const out = new Array(n); let m= this.F.one; for (let i=0; i<ndiv2; 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])); m = this.F.mul(m, this.w[bits]); } return out; } extend(p, e) { if (e == p.length) return p; const z = new Array(e-p.length).fill(this.F.zero); return p.concat(z); } reduce(p) { if (p.length == 0) return p; if (! this.F.eq(p[p.length-1], this.F.zero) ) return p; let i=p.length-1; while( i>0 && this.F.eq(p[i], this.F.zero) ) i--; return p.slice(0, i+1); } eq(a, b) { const pa = this.reduce(a); const pb = this.reduce(b); if (pa.length != pb.length) return false; for (let i=0; i<pb.length; i++) { if (!this.F.eq(pa[i], pb[i])) return false; } return true; } ruffini(p, r) { const res = new Array(p.length-1); res[res.length-1] = p[p.length-1]; for (let i = res.length-2; i>=0; i--) { res[i] = this.F.add(this.F.mul(res[i+1], r), p[i+1]); } return res; } _next2Power(v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } toString(p) { const ap = this.normalize(p); let S = ""; for (let i=ap.length-1; i>=0; i--) { if (!this.F.eq(p[i], this.F.zero)) { if (S!="") S += " + "; S = S + p[i].toString(10); if (i>0) { S = S + "x"; if (i>1) { S = S + "^" +i; } } } } return S; } normalize(p) { const res = new Array(p.length); for (let i=0; i<p.length; i++) { res[i] = this.F.normalize(p[i]); } return res; } _reciprocal(p, bits) { const k = 1 << bits; if (k==1) { return [ this.F.inv(p[0]) ]; } const np = this.scaleX(p, -k/2); const q = this._reciprocal(np, bits-1); const a = this.scaleX(this.double(q), 3*k/2-2); const b = this.mul( this.square(q), p); return this.scaleX(this.sub(a,b), -(k-2)); } // divides x^m / v _div2(m, v) { const kbits = log2$2(v.length-1)+1; const k = 1 << kbits; const scaleV = k - v.length; // rec = x^(k - 2) / v* x^scaleV => // rec = x^(k-2-scaleV)/ v // // res = x^m/v = x^(m + (2*k-2 - scaleV) - (2*k-2 - scaleV)) /v => // res = rec * x^(m - (2*k-2 - scaleV)) => // res = rec * x^(m - 2*k + 2 + scaleV) const rec = this._reciprocal(this.scaleX(v, scaleV), kbits); const res = this.scaleX(rec, m - 2*k + 2 + scaleV); return res; } div(_u, _v) { if (_u.length < _v.length) return []; const kbits = log2$2(_v.length-1)+1; const k = 1 << kbits; const u = this.scaleX(_u, k-_v.length); const v = this.scaleX(_v, k-_v.length); const n = v.length-1; let m = u.length-1; const s = this._reciprocal(v, kbits); let t; if (m>2*n) { t = this.sub(this.scaleX([this.F.one], 2*n), this.mul(s, v)); } let q = []; let rem = u; let us, ut; let finish = false; while (!finish) { us = this.mul(rem, s); q = this.add(q, this.scaleX(us, -2*n)); if ( m > 2*n ) { ut = this.mul(rem, t); rem = this.scaleX(ut, -2*n); m = rem.length-1; } else { finish = true; } } return q; } // returns the ith nth-root of one oneRoot(n, i) { let nbits = log2$2(n-1)+1; let res = this.F.one; let r = i; if(i>=n) { throw new Error("Given 'i' should be lower than 'n'"); } else if (1<<nbits !== n) { throw new Error(`Internal errlr: ${n} should equal ${1<<nbits}`); } 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.pow(t, m), this.F.one); } evaluateLagrangePolynomials(bits, t) { const m= 1 << bits; const tm = this.F.pow(t, m); const u= new Array(m).fill(this.F.zero); this._setRoots(bits); const omega = this.w[bits]; if (this.F.eq(tm, this.F.one)) { for (let i = 0; i < m; i++) { if (this.F.eq(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.pow(this.F.twoinv, m)); let l = this.F.mul(z, this.F.inv(this.F.e(m))); for (let i = 0; i < m; i++) { u[i] = this.F.mul(l, this.F.inv(this.F.sub(t,this.roots[bits][i]))); l = this.F.mul(l, omega); } return u; } log2(V) { return log2$2(V); } } function log2$2( 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$1(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$1(PF, pall, bits-1, offset, step*2); const p2 = __fft$1(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; } function __fft2(PF, pall, bits) { const n = 1 << bits; if (n==1) { return [ pall[0] ]; } const ndiv2 = n >> 1; const p1 = __fft2(PF, pall.slice(0, ndiv2), bits-1); const p2 = __fft2(PF, pall.slice(ndiv2), bits-1); 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; } const _revTable$1 = []; for (let i=0; i<256; i++) { _revTable$1[i] = _revSlow$1(i, 8); } function _revSlow$1(idx, bits) { let res =0; let a = idx; for (let i=0; i<bits; i++) { res <<= 1; res = res | (a &1); a >>=1; } return res; } function rev(idx, bits) { return ( _revTable$1[idx >>> 24] | (_revTable$1[(idx >>> 16) & 0xFF] << 8) | (_revTable$1[(idx >>> 8) & 0xFF] << 16) | (_revTable$1[idx & 0xFF] << 24) ) >>> (32-bits); } function __bitReverse(p, bits) { for (let k=0; k<p.length; k++) { const r = rev(k, bits); if (r>k) { const tmp= p[k]; p[k] = p[r]; p[r] = tmp; } } } /* Copyright 2018 0kims association. This file is part of snarkjs. snarkjs 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. snarkjs 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 snarkjs. If not, see <https://www.gnu.org/licenses/>. */ function mulScalar(F, base, e) { let res; if (isZero(e)) return F.zero; const n = naf(e); if (n[n.length-1] == 1) { res = base; } else if (n[n.length-1] == -1) { res = F.neg(base); } else { throw new Error("invlaud NAF"); } for (let i=n.length-2; i>=0; i--) { res = F.double(res); if (n[i] == 1) { res = F.add(res, base); } else if (n[i] == -1) { res = F.sub(res, base); } } return res; } /* exports.mulScalar = (F, base, e) =>{ let res = F.zero; let rem = bigInt(e); let exp = base; while (! rem.eq(bigInt.zero)) { if (rem.and(bigInt.one).eq(bigInt.one)) { res = F.add(res, exp); } exp = F.double(exp); rem = rem.shiftRight(1); } return res; }; */ function exp(F, base, e) { if (isZero(e)) return F.one; const n = bits(e); if (n.length==0) return F.one; let res = base; for (let i=n.length-2; i>=0; i--) { res = F.square(res); if (n[i]) { res = F.mul(res, base); } } return res; } // Check here: https://eprint.iacr.org/2012/685.pdf function buildSqrt (F) { if ((F.m % 2) == 1) { if (eq(mod(F.p, 4), 1 )) { if (eq(mod(F.p, 8), 1 )) { if (eq(mod(F.p, 16), 1 )) { // alg7_muller(F); alg5_tonelliShanks(F); } else if (eq(mod(F.p, 16), 9 )) { alg4_kong(F); } else { throw new Error("Field withot sqrt"); } } else if (eq(mod(F.p, 8), 5 )) { alg3_atkin(F); } else { throw new Error("Field withot sqrt"); } } else if (eq(mod(F.p, 4), 3 )) { alg2_shanks(F); } } else { const pm2mod4 = mod(pow(F.p, F.m/2), 4); if (pm2mod4 == 1) { alg10_adj(F); } else if (pm2mod4 == 3) { alg9_adj(F); } else { alg8_complex(F); } } } function alg5_tonelliShanks(F) { F.sqrt_q = pow(F.p, F.m); F.sqrt_s = 0; F.sqrt_t = sub(F.sqrt_q, 1); while (!isOdd(F.sqrt_t)) { F.sqrt_s = F.sqrt_s + 1; F.sqrt_t = div(F.sqrt_t, 2); } let c0 = F.one; while (F.eq(c0, F.one)) { const c = F.random(); F.sqrt_z = F.pow(c, F.sqrt_t); c0 = F.pow(F.sqrt_z, 2 ** (F.sqrt_s-1) ); } F.sqrt_tm1d2 = div(sub(F.sqrt_t, 1),2); F.sqrt = function(a) { const F=this; if (F.isZero(a)) return F.zero; let w = F.pow(a, F.sqrt_tm1d2); const a0 = F.pow( F.mul(F.square(w), a), 2 ** (F.sqrt_s-1) ); if (F.eq(a0, F.negone)) return null; let v = F.sqrt_s; let x = F.mul(a, w); let b = F.mul(x, w); let z = F.sqrt_z; while (!F.eq(b, F.one)) { let b2k = F.square(b); let k=1; while (!F.eq(b2k, F.one)) { b2k = F.square(b2k); k++; } w = z; for (let i=0; i<v-k-1; i++) { w = F.square(w); } z = F.square(w); b = F.mul(b, z); x = F.mul(x, w); v = k; } return F.geq(x, F.zero) ? x : F.neg(x); }; } function alg4_kong(F) { F.sqrt = function() { throw new Error("Sqrt alg 4 not implemented"); }; } function alg3_atkin(F) { F.sqrt = function() { throw new Error("Sqrt alg 3 not implemented"); }; } function alg2_shanks(F) { F.sqrt_q = pow(F.p, F.m); F.sqrt_e1 = div( sub(F.sqrt_q, 3) , 4); F.sqrt = function(a) { if (this.isZero(a)) return this.zero; // Test that have solution const a1 = this.pow(a, this.sqrt_e1); const a0 = this.mul(this.square(a1), a); if ( this.eq(a0, this.negone) ) return null; const x = this.mul(a1, a); return F.geq(x, F.zero) ? x : F.neg(x); }; } function alg10_adj(F) { F.sqrt = function() { throw new Error("Sqrt alg 10 not implemented"); }; } function alg9_adj(F) { F.sqrt_q = pow(F.p, F.m/2); F.sqrt_e34 = div( sub(F.sqrt_q, 3) , 4); F.sqrt_e12 = div( sub(F.sqrt_q, 1) , 2); F.frobenius = function(n, x) { if ((n%2) == 1) { return F.conjugate(x); } else { return x; } }; F.sqrt = function(a) { const F = this; const a1 = F.pow(a, F.sqrt_e34); const alfa = F.mul(F.square(a1), a); const a0 = F.mul(F.frobenius(1, alfa), alfa); if (F.eq(a0, F.negone)) return null; const x0 = F.mul(a1, a); let x; if (F.eq(alfa, F.negone)) { x = F.mul(x0, [F.F.zero, F.F.one]); } else { const b = F.pow(F.add(F.one, alfa), F.sqrt_e12); x = F.mul(b, x0); } return F.geq(x, F.zero) ? x : F.neg(x); }; } function alg8_complex(F) { F.sqrt = function() { throw new Error("Sqrt alg 8 not implemented"); }; } function quarterRound(st, a, b, c, d) { st[a] = (st[a] + st[b]) >>> 0; st[d] = (st[d] ^ st[a]) >>> 0; st[d] = ((st[d] << 16) | ((st[d]>>>16) & 0xFFFF)) >>> 0; st[c] = (st[c] + st[d]) >>> 0; st[b] = (st[b] ^ st[c]) >>> 0; st[b] = ((st[b] << 12) | ((st[b]>>>20) & 0xFFF)) >>> 0; st[a] = (st[a] + st[b]) >>> 0; st[d] = (st[d] ^ st[a]) >>> 0; st[d] = ((st[d] << 8) | ((st[d]>>>24) & 0xFF)) >>> 0; st[c] = (st[c] + st[d]) >>> 0; st[b] = (st[b] ^ st[c]) >>> 0; st[b] = ((st[b] << 7) | ((st[b]>>>25) & 0x7F)) >>> 0; } function doubleRound(st) { quarterRound(st, 0, 4, 8,12); quarterRound(st, 1, 5, 9,13); quarterRound(st, 2, 6,10,14); quarterRound(st, 3, 7,11,15); quarterRound(st, 0, 5,10,15); quarterRound(st, 1, 6,11,12); quarterRound(st, 2, 7, 8,13); quarterRound(st, 3, 4, 9,14); } class ChaCha { constructor(seed) { seed = seed || [0,0,0,0,0,0,0,0]; this.state = [ 0x61707865, 0x3320646E, 0x79622D32, 0x6B206574, seed[0], seed[1], seed[2], seed[3], seed[4], seed[5], seed[6], seed[7], 0, 0, 0, 0 ]; this.idx = 16; this.buff = new Array(16); } nextU32() { if (this.idx == 16) this.update(); return this.buff[this.idx++]; } nextU64() { return add(mul(this.nextU32(), 0x100000000), this.nextU32()); } nextBool() { return (this.nextU32() & 1) == 1; } update() { // Copy the state for (let i=0; i<16; i++) this.buff[i] = this.state[i]; // Apply the rounds for (let i=0; i<10; i++) doubleRound(this.buff); // Add to the initial for (let i=0; i<16; i++) this.buff[i] = (this.buff[i] + this.state[i]) >>> 0; this.idx = 0; this.state[12] = (this.state[12] + 1) >>> 0; if (this.state[12] != 0) return; this.state[13] = (this.state[13] + 1) >>> 0; if (this.state[13] != 0) return; this.state[14] = (this.state[14] + 1) >>> 0; if (this.state[14] != 0) return; this.state[15] = (this.state[15] + 1) >>> 0; } } function getRandomBytes(n) { let array = new Uint8Array(n); if (process.browser) { // Browser if (typeof globalThis.crypto !== "undefined") { // Supported globalThis.crypto.getRandomValues(array); } else { // fallback for (let i=0; i<n; i++) { array[i] = (Math.random()*4294967296)>>>0; } } } else { // NodeJS crypto.randomFillSync(array); } return array; } function getRandomSeed() { const arr = getRandomBytes(32); const arrV = new Uint32Array(arr.buffer); const seed = []; for (let i=0; i<8; i++) { seed.push(arrV[i]); } return seed; } let threadRng = null; function getThreadRng() { if (threadRng) return threadRng; threadRng = new ChaCha(getRandomSeed()); return threadRng; } /* Copyright 2018 0kims association. This file is part of snarkjs. snarkjs 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. snarkjs 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 snarkjs. If not, see <https://www.gnu.org/licenses/>. */ /* This library does operations on polynomials with coefficients in a field F. A polynomial P(x) = p0 + p1 * x + p2 * x^2 + ... + pn * x^n is represented by the array [ p0, p1, p2, ... , pn ]. */ class FFT { constructor (G, F, opMulGF) { this.F = F; this.G = G; this.opMulGF = opMulGF; let rem = F.sqrt_t || F.t; let s = F.sqrt_s || F.s; let nqr = F.one; while (F.eq(F.pow(nqr, F.half), F.one)) nqr = F.add(nqr, F.one); this.w = new Array(s+1); this.wi = new Array(s+1); this.w[s] = this.F.pow(nqr, rem); this.wi[s] = this.F.inv(this.w[s]); let n=s-1; while (n>=0) { this.w[n] = this.F.square(this.w[n+1]); this.wi[n] = this.F.square(this.wi[n+1]); 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(Math.min(s, 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; } } fft(p) { if (p.length <= 1) return p; const bits = log2$1(p.length-1)+1; this._setRoots(bits); const m = 1 << bits; if (p.length != m) { throw new Error("Size must be multiple of 2"); } const res = __fft(this, p, bits, 0, 1); return res; } ifft(p) { if (p.length <= 1) return p; const bits = log2$1(p.length-1)+1; this._setRoots(bits); const m = 1 << bits; if (p.length != m) { throw new Error("Size must be multiple of 2"); } const res = __fft(this, p, bits, 0, 1); const twoinvm = this.F.inv( this.F.mulScalar(this.F.one, m) ); const resn = new Array(m); for (let i=0; i<m; i++) { resn[i] = this.opMulGF(res[(m-i)%m], twoinvm); } return resn; } } function log2$1( 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.G.add(pall[offset], pall[offset + step]), PF.G.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.G.add(p1[i], PF.opMulGF(p2[i], PF.roots[bits][i])); out[i+ndiv2] = PF.G.sub(p1[i], PF.opMulGF(p2[i], PF.roots[bits][i])); } return out; } /* global BigInt */ class ZqField { constructor(p) { this.type="F1"; this.one = BigInt(1); this.zero = BigInt(0); this.p = BigInt(p); this.m = 1; this.negone = this.p-this.one; this.two = BigInt(2); this.half = this.p >> this.one; this.bitLength = bitLength(this.p); this.mask = (this.one << BigInt(this.bitLength)) - this.one; this.n64 = Math.floor((this.bitLength - 1) / 64)+1; this.n32 = this.n64*2; this.n8 = this.n64*8; this.R = this.e(this.one << BigInt(this.n64*64)); this.Ri = this.inv(this.R); const e = this.negone >> this.one; this.nqr = this.two; let r = this.pow(this.nqr, e); while (!this.eq(r, this.negone)) { this.nqr = this.nqr + this.one; r = this.pow(this.nqr, e); } this.s = 0; this.t = this.negone; while ((this.t & this.one) == this.zero) { this.s = this.s + 1; this.t = this.t >> this.one; } this.nqr_to_t = this.pow(this.nqr, this.t); buildSqrt(this); this.FFT = new FFT(this, this, this.mul.bind(this)); this.fft = this.FFT.fft.bind(this.FFT); this.ifft = this.FFT.ifft.bind(this.FFT); this.w = this.FFT.w; this.wi = this.FFT.wi; this.shift = this.square(this.nqr); this.k = this.exp(this.nqr, 2**this.s); } e(a,b) { let res; if (!b) { res = BigInt(a); } else if (b==16) { res = BigInt("0x"+a); } if (res < 0) { let nres = -res; if (nres >= this.p) nres = nres % this.p; return this.p - nres; } else { return (res>= this.p) ? res%this.p : res; } } add(a, b) { const res = a + b; return res >= this.p ? res-this.p : res; } sub(a, b) { return (a >= b) ? a-b : this.p-b+a; } neg(a) { return a ? this.p-a : a; } mul(a, b) { return (a*b)%this.p; } mulScalar(base, s) { return (base * this.e(s)) % this.p; } square(a) { return (a*a) % this.p; } eq(a, b) { return a==b; } neq(a, b) { return a!=b; } lt(a, b) { const aa = (a > this.half) ? a - this.p : a; const bb = (b > this.half) ? b - this.p : b; return aa < bb; } gt(a, b) { const aa = (a > this.half) ? a - this.p : a; const bb = (b > this.half) ? b - this.p : b; return aa > bb; } leq(a, b) { const aa = (a > this.half) ? a - this.p : a; const bb = (b > this.half) ? b - this.p : b; return aa <= bb; } geq(a, b) { const aa = (a > this.half) ? a - this.p : a; const bb = (b > this.half) ? b - this.p : b; return aa >= bb; } div(a, b) { return this.mul(a, this.inv(b)); } idiv(a, b) { if (!b) throw new Error("Division by zero"); return a / b; } inv(a) { if (!a) throw new Error("Division by zero"); let t = this.zero; let r = this.p; let newt = this.one; let newr = a % this.p; while (newr) { let q = r/newr; [t, newt] = [newt, t-q*newt]; [r, newr] = [newr, r-q*newr]; } if (t<this.zero) t += this.p; return t; } mod(a, b) { return a % b; } pow(b, e) { return exp(this, b, e); } exp(b, e) { return exp(this, b, e); } band(a, b) { const res = ((a & b) & this.mask); return res >= this.p ? res-this.p : res; } bor(a, b) { const res = ((a | b) & this.mask); return res >= this.p ? res-this.p : res; } bxor(a, b) { const res = ((a ^ b) & this.mask); return res >= this.p ? res-this.p : res; } bnot(a) { const res = a ^ this.mask; return res >= this.p ? res-this.p : res; } shl(a, b) { if (Number(b) < this.bitLength) { const res = (a << b) & this.mask; return res >= this.p ? res-this.p : res; } else { const nb = this.p - b; if (Number(nb) < this.bitLength) { return a >> nb; } else { return this.zero; } } } shr(a, b) { if (Number(b) < this.bitLength) { return a >> b; } else { const nb = this.p - b; if (Number(nb) < this.bitLength) { const res = (a << nb) & this.mask; return res >= this.p ? res-this.p : res; } else { return 0; } } } land(a, b) { return (a && b) ? this.one : this.zero; } lor(a, b) { return (a || b) ? this.one : this.zero; } lnot(a) { return (a) ? this.zero : this.one; } sqrt_old(n) { if (n == this.zero) return this.zero; // Test that have solution const res = this.pow(n, this.negone >> this.one); if ( res != this.one ) return null; let m = this.s; let c = this.nqr_to_t; let t = this.pow(n, this.t); let r = this.pow(n, this.add(this.t, this.one) >> this.one ); while ( t != this.one ) { let sq = this.square(t); let i = 1; while (sq != this.one ) { i++; sq = this.square(sq); } // b = c ^ m-i-1 let b = c; for (let j=0; j< m-i-1; j ++) b = this.square(b); m = i; c = this.square(b); t = this.mul(t, c); r = this.mul(r, b); } if (r > (this.p >> this.one)) { r = this.neg(r); } return r; } normalize(a, b) { a = BigInt(a,b); if (a < 0) { let na = -a; if (na >= this.p) na = na % this.p; return this.p - na; } else { return (a>= this.p) ? a%this.p : a; } } random() { const nBytes = (this.bitLength*2 / 8); let res =this.zero; for (let i=0; i<nBytes; i++) { res = (res << BigInt(8)) + BigInt(getRandomBytes(1)[0]); } return res % this.p; } toString(a, base) { base = base || 10; let vs; if ((a > this.half)&&(base == 10)) { const v = this.p-a; vs = "-"+v.toString(base); } else { vs = a.toString(base); } return vs; } isZero(a) { return a == this.zero; } fromRng(rng) { let v; do { v=this.zero; for (let i=0; i<this.n64; i++) { v += rng.nextU64() << BigInt(64 *i); } v &= this.mask; } while (v >= this.p); v = (v * this.Ri) % this.p; // Convert from montgomery return v; } fft(a) { return this.FFT.fft(a); } ifft(a) { return this.FFT.ifft(a); } // Returns a buffer with Little Endian Representation toRprLE(buff, o, e) { toRprLE(buff, o, e, this.n64*8); } // Returns a buffer with Big Endian Representation toRprBE(buff, o, e) { toRprBE(buff, o, e, this.n64*8); } // Returns a buffer with Big Endian Montgomery Representation toRprBEM(buff, o, e) { return this.toRprBE(buff, o, this.mul(this.R, e)); } toRprLEM(buff, o, e) { return this.toRprLE(buff, o, this.mul(this.R, e)); } // Pases a buffer with Little Endian Representation fromRprLE(buff, o) { return fromRprLE(buff, o, this.n8); } // Pases a buffer with Big Endian Representation fromRprBE(buff, o) { return fromRprBE(buff, o, this.n8); } fromRprLEM(buff, o) { return this.mul(this.fromRprLE(buff, o), this.Ri); } fromRprBEM(buff, o) { return this.mul(this.fromRprBE(buff, o), this.Ri); } toObject(a) { return a; } } /* Copyright 2018 0kims association. This file is part of snarkjs. snarkjs 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. snarkjs 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 snarkjs. If not, see <https://www.gnu.org/licenses/>. */ class F2Field { constructor(F, nonResidue) { this.type="F2"; this.F = F; this.zero = [this.F.zero, this.F.zero]; this.one = [this.F.one, this.F.zero]; this.negone = this.neg(this.one); this.nonResidue = nonResidue; this.m = F.m*2; this.p = F.p; this.n64 = F.n64*2; this.n32 = this.n64*2; this.n8 = this.n64*8; buildSqrt(this); } _mulByNonResidue(a) { return this.F.mul(this.nonResidue, a); } copy(a) { return [this.F.copy(a[0]), this.F.copy(a[1])]; } add(a, b) { return [ this.F.add(a[0], b[0]), this.F.add(a[1], b[1]) ]; } double(a) { return this.add(a,a); } sub(a, b) { return [ this.F.sub(a[0], b[0]), this.F.sub(a[1], b[1]) ]; } neg(a) { return this.sub(this.zero, a); } conjugate(a) { return [ a[0], this.F.neg(a[1]) ]; } mul(a, b) { const aA = this.F.mul(a[0] , b[0]); const bB = this.F.mul(a[1] , b[1]); return [ this.F.add( aA , this._mulByNonResidue(bB)), this.F.sub( this.F.mul( this.F.add(a[0], a[1]), this.F.add(b[0], b[1])), this.F.add(aA, bB))]; } inv(a) { const t0 = this.F.square(a[0]); const t1 = this.F.square(a[1]); const t2 = this.F.sub(t0, this._mulByNonResidue(t1)); const t3 = this.F.inv(t2); return [ this.F.mul(a[0], t3), this.F.neg(this.F.mul( a[1], t3)) ]; } div(a, b) { return this.mul(a, this.inv(b)); } square(a) { const ab = this.F.mul(a[0] , a[1]); /* [ (a + b) * (a + non_residue * b) - ab - non_residue * ab, ab + ab ]; */ return [ this.F.sub( this.F.mul( this.F.add(a[0], a[1]) , this.F.add( a[0] , this._mulByNonResidue(a[1]))), this.F.add( ab, this._mulByNonResidue(ab))), this.F.add(ab, ab) ]; } isZero(a) { return this.F.isZero(a[0]) && this.F.isZero(a[1]); } eq(a, b) { return this.F.eq(a[0], b[0]) && this.F.eq(a[1], b[1]); } mulScalar(base, e) { return mulScalar(this, base, e); } pow(base, e) { return exp(this, base, e); } exp(base, e) { return exp(this, base, e); } toString(a) { return `[ ${this.F.toString(a[0])} , ${this.F.toString(a[1])} ]`; } fromRng(rng) { const c0 = this.F.fromRng(rng); const c1 = this.F.fromRng(rng); return [c0, c1]; } gt(a, b) { if (this.F.gt(a[0], b[0])) return true; if (this.F.gt(b[0], a[0])) return false; if (this.F.gt(a[1], b[1])) return true; return false; } geq(a, b) { return this.gt(a, b) || this.eq(a, b); } lt(a, b) { return !this.geq(a,b); } leq(a, b) { return !this.gt(a,b); } neq(a, b) { return !this.eq(a,b); } random() { return [this.F.random(), this.F.random()]; } toRprLE(buff, o, e) { this.F.toRprLE(buff, o, e[0]); this.F.toRprLE(buff, o+this.F.n8, e[1]); } toRprBE(buff, o, e) { this.F.toRprBE(buff, o, e[1]); this.F.toRprBE(buff, o+this.F.n8, e[0]); } toRprLEM(buff, o, e) { this.F.toRprLEM(buff, o, e[0]); this.F.toRprLEM(buff, o+this.F.n8, e[1]); } toRprBEM(buff, o, e) { this.F.toRprBEM(buff, o, e[1]); this.F.toRprBEM(buff, o+this.F.n8, e[0]); } fromRprLE(buff, o) { o = o || 0; const c0 = this.F.fromRprLE(buff, o); const c1 = this.F.fromRprLE(buff, o+this.F.n8); return [c0, c1]; } fromRprBE(buff, o) { o = o || 0; const c1 = this.F.fromRprBE(buff, o); const c0 = this.F.fromRprBE(buff, o+this.F.n8); return [c0, c1]; } fromRprLEM(buff, o) { o = o || 0; const c0 = this.F.fromRprLEM(buff, o); const c1 = this.F.fromRprLEM(buff, o+this.F.n8); return [c0, c1]; } fromRprBEM(buff, o) { o = o || 0; const c1 = this.F.fromRprBEM(buff, o); const c0 = this.F.fromRprBEM(buff, o+this.F.n8); return [c0, c1]; } toObject(a) { return a; } } /* Copyright 2018 0kims association. This file is part of snarkjs. snarkjs 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. snarkjs 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 snarkjs. If not, see <https://www.gnu.org/licenses/>. */ class F3Field { constructor(F, nonResidue) { this.type="F3"; this.F = F; this.zero = [this.F.zero, this.F.zero, this.F.zero]; this.one = [this.F.one, this.F.zero, this.F.zero];