Works in browser

This commit is contained in:
Jordi Baylina
2020-07-07 19:21:41 +02:00
parent a061ead2ef
commit e67eda93ee
50 changed files with 16789 additions and 956 deletions

View File

@@ -5,7 +5,8 @@ module.exports = {
"mocha": true
},
"parserOptions": {
"ecmaVersion": 2020
"ecmaVersion": 2020,
"sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {

7320
build/main.cjs Normal file

File diff suppressed because one or more lines are too long

7320
build/main.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,18 +0,0 @@
exports.Scalar = require("./src/scalar");
exports.PolField = require("./src/polfield.js");
exports.F1Field = require("./src/f1field");
exports.F2Field = require("./src/f2field");
exports.F3Field = require("./src/f3field");
exports.ZqField = exports.F1Field;
exports.EC = require("./src/ec");
exports.buildBn128 = require("./src/bn128.js");
exports.buildBls12381 = require("./src/bls12381.js");
exports.utils = require("./src/utils");
exports.ChaCha = require("./src/chacha");

20
main.js Normal file
View File

@@ -0,0 +1,20 @@
import * as _Scalar from "./src/scalar.js";
export const Scalar=_Scalar;
export {default as PolField} from "./src/polfield.js";
export {default as F1Field} from "./src/f1field.js";
export {default as F2Field} from "./src/f2field.js";
export {default as F3Field} from "./src/f3field.js";
export {default as ZqField} from "./src/f1field.js";
export {default as EC} from "./src/ec.js";
export {default as buildBn128} from "./src/bn128.js";
export {default as buildBls12381} from "./src/bls12381.js";
import * as _utils from "./src/utils.js";
export const utils = _utils;
export {default as ChaCha} from "./src/chacha.js";

1286
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,17 @@
{
"name": "ffjavascript",
"type": "module",
"version": "0.1.3",
"description": "Finite Field Library in Javascript",
"main": "index.js",
"main": "./build/main.cjs",
"module": "./main.js",
"exports": {
"import": "./main.js",
"require": "./build/main.cjs"
},
"scripts": {
"test": "mocha"
"test": "mocha",
"build": "rollup -c rollup.cjs.config.js"
},
"repository": {
"type": "git",
@@ -26,11 +33,18 @@
"homepage": "https://github.com/iden3/ffjs#readme",
"dependencies": {
"big-integer": "^1.6.48",
"wasmsnark": "0.0.10"
"wasmcurves": "0.0.3",
"worker-threads": "^1.0.0"
},
"devDependencies": {
"blake2b": "^2.1.3",
"chai": "^4.2.0",
"eslint": "^6.8.0"
"eslint": "^6.8.0",
"esm": "^3.2.25",
"rollup": "^2.19.0",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-resolve": "^5.2.0"
}
}

18
rollup.cjs.config.js Normal file
View File

@@ -0,0 +1,18 @@
import resolve from "rollup-plugin-node-resolve";
import commonJS from "rollup-plugin-commonjs";
export default {
input: "main.js",
output: {
file: "build/main.js",
format: "cjs",
},
external: ["crypto", "os", "worker_threads"],
plugins: [
resolve({ preferBuiltins: true }),
commonJS({
preserveSymlinks: true
}),
]
};

View File

@@ -1,15 +1,15 @@
const bls12381_wasm = require("wasmsnark").bls12381_wasm;
const buildEngine = require("./engine");
const Scalar = require("./scalar");
import wasmcurves from "wasmcurves";
import buildEngine from "./engine.js";
import * as Scalar from "./scalar.js";
let curve;
module.exports = async function buildBls12381() {
export default async function buildBls12381() {
if (curve) return curve;
const params = {
name: "bls12381",
wasm: bls12381_wasm,
wasm: wasmcurves.bls12381_wasm,
q: Scalar.e("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", 16),
r: Scalar.e("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", 16),
n8q: 48,
@@ -20,6 +20,11 @@ module.exports = async function buildBls12381() {
};
curve = await buildEngine(params);
return curve;
};
curve.terminate = function() {
this.tm.terminate();
curve = null;
};
return curve;
}

View File

@@ -1,15 +1,15 @@
const bn128_wasm = require("wasmsnark").bn128_wasm;
const buildEngine = require("./engine");
const Scalar = require("./scalar");
import wasmcurves from "wasmcurves";
import buildEngine from "./engine.js";
import * as Scalar from "./scalar.js";
let curve;
module.exports = async function buildBn128() {
export default async function buildBn128() {
if (curve) return curve;
const params = {
name: "bn128",
wasm: bn128_wasm,
wasm: wasmcurves.bn128_wasm,
q: Scalar.e("21888242871839275222246405745257275088696311157297823662689037894645226208583"),
r: Scalar.e("21888242871839275222246405745257275088548364400416034343698204186575808495617"),
n8q: 32,
@@ -19,6 +19,11 @@ module.exports = async function buildBn128() {
};
curve = await buildEngine(params);
return curve;
};
curve.terminate = function() {
this.tm.terminate();
curve = null;
};
return curve;
}

View File

@@ -1,6 +1,6 @@
const Scalar = require("./scalar");
import * as Scalar from "./scalar.js";
function quarterRound(st, a, b, c, d) {
@@ -34,7 +34,7 @@ function doubleRound(st) {
quarterRound(st, 3, 4, 9,14);
}
module.exports = class ChaCha {
export default class ChaCha {
constructor(seed) {
seed = seed || [0,0,0,0,0,0,0,0];
@@ -93,4 +93,4 @@ module.exports = class ChaCha {
if (this.state[14] != 0) return;
this.state[15] = (this.state[15] + 1) >>> 0;
}
};
}

View File

@@ -19,9 +19,8 @@
const fUtils = require("./futils.js");
const Scalar = require("./scalar");
const assert = require("assert");
import * as fUtils from "./futils.js";
import * as Scalar from "./scalar.js";
function isGreatest(F, a) {
@@ -39,7 +38,7 @@ function isGreatest(F, a) {
}
class EC {
export default class EC {
constructor(F, g) {
this.F = F;
@@ -152,6 +151,10 @@ class EC {
return res;
}
timesScalar(base, e) {
return fUtils.mulScalar(this, base, e);
}
mulScalar(base, e) {
return fUtils.mulScalar(this, base, e);
}
@@ -388,7 +391,7 @@ class EC {
P[1] = F.sqrt(x3b);
if (P[1] === null) {
assert(false, "Invalid Point!");
throw new Error("Invalid Point!");
}
const s = isGreatest(F, P[1]);
@@ -431,5 +434,4 @@ class EC {
}
module.exports = EC;

View File

@@ -1,20 +1,19 @@
const WasmField1 =require("./wasm_field1");
const WasmField2 =require("./wasm_field2");
const WasmField3 =require("./wasm_field3");
const WasmCurve = require("./wasm_curve");
const buildThreadManager = require("./threadman");
const Scalar = require("./scalar");
const buildBatchApplyKey = require("./engine_applykey");
const buildPairing = require("./engine_pairing");
const buildMultiExp = require("./engine_multiexp");
const buildFFT = require("./engine_fft");
import WasmField1 from "./wasm_field1.js";
import WasmField2 from "./wasm_field2.js";
import WasmField3 from "./wasm_field3.js";
import WasmCurve from "./wasm_curve.js";
import buildThreadManager from "./threadman.js";
import * as Scalar from "./scalar.js";
import buildBatchApplyKey from "./engine_applykey.js";
import buildPairing from "./engine_pairing.js";
import buildMultiExp from "./engine_multiexp.js";
import buildFFT from "./engine_fft.js";
module.exports = buildEngine;
async function buildEngine(params) {
export default async function buildEngine(params) {
const tm = await buildThreadManager(params.wasm, params.singleThread);
const curve = {};
curve.q = Scalar.e(params.wasm.q);
@@ -46,6 +45,25 @@ async function buildEngine(params) {
buildPairing(curve);
curve.array2buffer = function(arr, sG) {
const buff = new Uint8Array(sG*arr.length);
for (let i=0; i<arr.length; i++) {
buff.set(arr[i], i*sG);
}
return buff;
};
curve.buffer2array = function(buff , sG) {
const n= buff.length / sG;
const arr = new Array(n);
for (let i=0; i<n; i++) {
arr[i] = buff.slice(i*sG, i*sG+sG);
}
return arr;
};
return curve;
}

View File

@@ -1,5 +1,5 @@
module.exports = function buildBatchApplyKey(curve, groupName) {
export default function buildBatchApplyKey(curve, groupName) {
const G = curve[groupName];
const Fr = curve.Fr;
const tm = curve.tm;
@@ -110,4 +110,4 @@ module.exports = function buildBatchApplyKey(curve, groupName) {
return outBuff;
};
};
}

View File

@@ -1,9 +1,10 @@
const assert = require("assert");
module.exports = function buildBatchConvert(tm, fnName, sIn, sOut) {
export default function buildBatchConvert(tm, fnName, sIn, sOut) {
return async function batchConvert(buffIn) {
const nPoints = Math.floor(buffIn.byteLength / sIn);
assert( nPoints * sIn === buffIn.byteLength);
if ( nPoints * sIn !== buffIn.byteLength) {
throw new Error("Invalid buffer size");
}
const pointsPerChunk = Math.floor(nPoints/tm.concurrency);
const opPromises = [];
for (let i=0; i<tm.concurrency; i++) {
@@ -42,4 +43,4 @@ module.exports = function buildBatchConvert(tm, fnName, sIn, sOut) {
return fullBuffOut;
};
};
}

View File

@@ -1,11 +1,11 @@
const assert = require("assert");
const {log2, buffReverseBits} = require("./utils");
import {log2, buffReverseBits} from "./utils.js";
module.exports = function buildFFT(curve, groupName) {
export default function buildFFT(curve, groupName) {
const G = curve[groupName];
const Fr = curve.Fr;
const tm = G.tm;
async function _fft(buff, inverse, inType, outType, log) {
inType = inType || "affine";
outType = outType || "affine";
const MAX_BITS_THREAD = 12;
@@ -72,11 +72,18 @@ module.exports = function buildFFT(curve, groupName) {
}
let returnArray = false;
if (Array.isArray(buff)) {
buff = curve.array2buffer(buff, sIn);
returnArray = true;
}
const nPoints = buff.byteLength / sIn;
const bits = log2(nPoints);
assert( (1 << bits) == nPoints, "fft must be multiple of 2" );
if ((1 << bits) != nPoints) {
throw new Error("fft must be multiple of 2" );
}
let inv;
if (inverse) {
@@ -216,7 +223,11 @@ module.exports = function buildFFT(curve, groupName) {
}
}
return buffOut;
if (returnArray) {
return curve.buffer2array(buffOut, sOut);
} else {
return buffOut;
}
}
G.fft = async function(buff, inType, outType, log) {
@@ -240,7 +251,7 @@ module.exports = function buildFFT(curve, groupName) {
fnName = "frm_fftMix";
fnFFTJoin = "frm_fftJoin";
} else {
assert(false);
throw new Error("Invalid group");
}
const nPoints = Math.floor(buff.byteLength / sG);
@@ -338,12 +349,16 @@ module.exports = function buildFFT(curve, groupName) {
} else if (groupName == "G2") {
fnName = "g2m_fftJoin";
} else {
assert(false);
throw new Error("Invalid group");
}
assert (buff1.byteLength == buff2.byteLength);
if (buff1.byteLength != buff2.byteLength) {
throw new Error("Invalid buffer size");
}
const nPoints = Math.floor(buff1.byteLength / sG);
assert (nPoints == 1 << log2(nPoints));
if (nPoints != 1 << log2(nPoints)) {
throw new Error("Invalid number of points");
}
let nChunks = 1 << log2(tm.concurrency);
if (nPoints <= nChunks*2) nChunks = 1;
@@ -403,11 +418,13 @@ module.exports = function buildFFT(curve, groupName) {
fnName = "g2m_fftFinal";
fnToAffine = "g2m_batchToAffine";
} else {
assert(false);
throw new Error("Invalid group");
}
const nPoints = Math.floor(buff.byteLength / sG);
assert (nPoints == 1 << log2(nPoints));
if (nPoints == 1 << log2(nPoints)) {
throw new Error("Invalid number of points");
}
const pointsPerChunk = Math.floor(nPoints / tm.concurrency);
@@ -452,4 +469,4 @@ module.exports = function buildFFT(curve, groupName) {
return fullBuffOut;
};
};
}

View File

@@ -1,5 +1,4 @@
const assert = require("assert");
const {log2} = require("./utils");
import { log2 } from "./utils.js";
const pTSizes = [
1 , 1, 1, 1, 2, 3, 4, 5,
@@ -8,7 +7,7 @@ const pTSizes = [
17, 17, 17, 17, 17, 17, 17, 17
];
module.exports = function buildMultiexp(curve, groupName) {
export default function buildMultiexp(curve, groupName) {
const G = curve[groupName];
async function _multiExp(buffBases, buffScalars, inType) {
inType = inType || "affine";
@@ -32,11 +31,13 @@ module.exports = function buildMultiexp(curve, groupName) {
sGIn = G.F.n8*3;
}
} else {
assert(false, "Invalid group");
throw new Error("Invalid group");
}
const nPoints = Math.floor(buffBases.byteLength / sGIn);
const sScalar = Math.floor(buffScalars.byteLength / nPoints);
assert( sScalar * nPoints == buffScalars.byteLength, "Scalar size does not match");
if( sScalar * nPoints != buffScalars.byteLength) {
throw new Error("Scalar size does not match");
}
const bitChunkSize = pTSizes[log2(nPoints)];
const nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1;
@@ -82,4 +83,4 @@ module.exports = function buildMultiexp(curve, groupName) {
G.multiExpAffine = async function multiExpAffine(buffBases, buffScalars) {
return await _multiExp(buffBases, buffScalars, "affine");
};
};
}

View File

@@ -1,5 +1,5 @@
module.exports = function buildPairing(curve) {
export default function buildPairing(curve) {
const tm = curve.tm;
curve.pairing = function pairing(a, b) {
@@ -85,4 +85,46 @@ module.exports = function buildPairing(curve) {
return r;
};
};
curve.prepareG1 = function(p) {
this.tm.startSyncOp();
const pP = this.tm.allocBuff(p);
const pPrepP = this.tm.alloc(this.prePSize);
this.tm.instance.exports[this.name + "_prepareG1"](pP, pPrepP);
const res = this.tm.getBuff(pPrepP, this.prePSize);
this.tm.endSyncOp();
return res;
};
curve.prepareG2 = function(q) {
this.tm.startSyncOp();
const pQ = this.tm.allocBuff(q);
const pPrepQ = this.tm.alloc(this.preQSize);
this.tm.instance.exports[this.name + "_prepareG2"](pQ, pPrepQ);
const res = this.tm.getBuff(pPrepQ, this.preQSize);
this.tm.endSyncOp();
return res;
};
curve.millerLoop = function(preP, preQ) {
this.tm.startSyncOp();
const pPreP = this.tm.allocBuff(preP);
const pPreQ = this.tm.allocBuff(preQ);
const pRes = this.tm.alloc(this.Gt.n8);
this.tm.instance.exports[this.name + "_millerLoop"](pPreP, pPreQ, pRes);
const res = this.tm.getBuff(pRes, this.Gt.n8);
this.tm.endSyncOp();
return res;
};
curve.finalExponentiation = function(a) {
this.tm.startSyncOp();
const pA = this.tm.allocBuff(a);
const pRes = this.tm.alloc(this.Gt.n8);
this.tm.instance.exports[this.name + "_finalExponentiation"](pA, pRes);
const res = this.tm.getBuff(pRes, this.Gt.n8);
this.tm.endSyncOp();
return res;
};
}

View File

@@ -1,53 +1,55 @@
const Scalar = require("./scalar");
const assert = require("assert");
import * as Scalar from "./scalar.js";
import F1Field_native from "./f1field_native.js";
import F1Field_bigint from "./f1field_bigint.js";
const supportsNativeBigInt = typeof BigInt === "function";
let F1Field;
let _F1Field;
if (supportsNativeBigInt) {
F1Field = require("./f1field_native");
_F1Field = F1Field_native;
} else {
F1Field = require("./f1field_bigint");
_F1Field = F1Field_bigint;
}
export default class F1Field extends _F1Field {
// Returns a buffer with Little Endian Representation
F1Field.prototype.toRprLE = function toRprLE(buff, o, e) {
Scalar.toRprLE(buff, o, e, this.n64*8);
};
// Returns a buffer with Little Endian Representation
toRprLE(buff, o, e) {
Scalar.toRprLE(buff, o, e, this.n64*8);
}
// Returns a buffer with Big Endian Representation
F1Field.prototype.toRprBE = function toRprBE(buff, o, e) {
Scalar.toRprBE(buff, o, e, this.n64*8);
};
// Returns a buffer with Big Endian Representation
toRprBE(buff, o, e) {
Scalar.toRprBE(buff, o, e, this.n64*8);
}
// Returns a buffer with Big Endian Montgomery Representation
F1Field.prototype.toRprBEM = function toRprBEM(buff, o, e) {
return this.toRprBE(buff, o, this.mul(this.R, e));
};
// Returns a buffer with Big Endian Montgomery Representation
toRprBEM(buff, o, e) {
return this.toRprBE(buff, o, this.mul(this.R, e));
}
F1Field.prototype.toRprLEM = function toRprLEM(buff, o, e) {
return this.toRprLE(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
F1Field.prototype.fromRprLE = function fromRprLE(buff, o) {
return Scalar.fromRprLE(buff, o, this.n8);
};
// Pases a buffer with Little Endian Representation
fromRprLE(buff, o) {
return Scalar.fromRprLE(buff, o, this.n8);
}
// Pases a buffer with Big Endian Representation
F1Field.prototype.fromRprBE = function fromRprBE(buff, o) {
return Scalar.fromRprBE(buff, o, this.n8);
};
// Pases a buffer with Big Endian Representation
fromRprBE(buff, o) {
return Scalar.fromRprBE(buff, o, this.n8);
}
F1Field.prototype.fromRprLEM = function fromRprLEM(buff, o) {
return this.mul(this.fromRprLE(buff, o), this.Ri);
};
fromRprLEM(buff, o) {
return this.mul(this.fromRprLE(buff, o), this.Ri);
}
F1Field.prototype.fromRprBEM = function fromRprBEM(buff, o) {
return this.mul(this.fromRprBE(buff, o), this.Ri);
};
fromRprBEM(buff, o) {
return this.mul(this.fromRprBE(buff, o), this.Ri);
}
}
module.exports = F1Field;

View File

@@ -1,24 +1,8 @@
const bigInt = require("big-integer");
const assert = require("assert");
const buildSqrt = require("./fsqrt");
import bigInt from "big-integer";
import buildSqrt from "./fsqrt.js";
import {getRandomBytes} from "./random.js";
function getRandomByte() {
if (typeof window !== "undefined") { // Browser
if (typeof window.crypto !== "undefined") { // Supported
let array = new Uint8Array(1);
window.crypto.getRandomValues(array);
return array[0];
}
else { // fallback
return Math.floor(Math.random() * 256);
}
}
else { // NodeJS
return module.require("crypto").randomBytes(1)[0];
}
}
module.exports = class ZqField {
export default class ZqField {
constructor(p) {
this.type="F1";
this.one = bigInt.one;
@@ -132,17 +116,17 @@ module.exports = class ZqField {
}
div(a, b) {
assert(!b.isZero(), "Division by zero");
if (b.isZero()) throw new Error("Division by zero");
return a.times(b.modInv(this.p)).mod(this.p);
}
idiv(a, b) {
assert(!b.isZero(), "Division by zero");
if (b.isZero()) throw new Error("Division by zero");
return a.divide(b);
}
inv(a) {
assert(!a.isZero(), "Division by zero");
if (a.isZero()) throw new Error("Division by zero");
return a.modInv(this.p);
}
@@ -154,6 +138,10 @@ module.exports = class ZqField {
return a.modPow(b, this.p);
}
exp(a, b) {
return a.modPow(b, this.p);
}
band(a, b) {
return a.and(b).and(this.mask).mod(this.p);
}
@@ -259,7 +247,7 @@ module.exports = class ZqField {
let res = bigInt(0);
let n = bigInt(this.p.square());
while (!n.isZero()) {
res = res.shiftLeft(8).add(bigInt(getRandomByte()));
res = res.shiftLeft(8).add(bigInt(getRandomBytes(1)[0]));
n = n.shiftRight(8);
}
return res.mod(this.p);
@@ -295,5 +283,5 @@ module.exports = class ZqField {
}
};
}

View File

@@ -1,26 +1,10 @@
/* global BigInt */
const assert = require("assert");
const Scalar = require("./scalar");
const futils = require("./futils");
const buildSqrt = require("./fsqrt");
import * as Scalar from "./scalar.js";
import * as futils from "./futils.js";
import buildSqrt from "./fsqrt.js";
import {getRandomBytes} from "./random.js";
function getRandomByte() {
if (typeof window !== "undefined") { // Browser
if (typeof window.crypto !== "undefined") { // Supported
let array = new Uint8Array(1);
window.crypto.getRandomValues(array);
return array[0];
}
else { // fallback
return Math.floor(Math.random() * 256);
}
}
else { // NodeJS
return module.require("crypto").randomBytes(1)[0];
}
}
module.exports = class ZqField {
export default class ZqField {
constructor(p) {
this.type="F1";
this.one = 1n;
@@ -140,12 +124,12 @@ module.exports = class ZqField {
}
idiv(a, b) {
assert(b, "Division by zero");
if (!b) throw new Error("Division by zero");
return a / b;
}
inv(a) {
assert(a, "Division by zero");
if (!a) throw new Error("Division by zero");
let t = 0n;
let r = this.p;
@@ -168,6 +152,10 @@ module.exports = class ZqField {
return futils.exp(this, b, e);
}
exp(b, e) {
return futils.exp(this, b, e);
}
band(a, b) {
const res = ((a & b) & this.mask);
return res >= this.p ? res-this.p : res;
@@ -281,7 +269,7 @@ module.exports = class ZqField {
const nBytes = (this.bitLength*2 / 8);
let res =0n;
for (let i=0; i<nBytes; i++) {
res = (res << 8n) + BigInt(getRandomByte());
res = (res << 8n) + BigInt(getRandomBytes(1)[0]);
}
return res % this.p;
}

View File

@@ -17,10 +17,10 @@
snarkjs. If not, see <https://www.gnu.org/licenses/>.
*/
const fUtils = require("./futils.js");
const buildSqrt = require("./fsqrt");
import * as fUtils from "./futils.js";
import buildSqrt from "./fsqrt.js";
class F2Field {
export default class F2Field {
constructor(F, nonResidue) {
this.type="F2";
this.F = F;
@@ -141,6 +141,10 @@ class F2Field {
return fUtils.exp(this, base, e);
}
exp(base, e) {
return fUtils.exp(this, base, e);
}
toString(a) {
return `[ ${this.F.toString(a[0])} , ${this.F.toString(a[1])} ]`;
}
@@ -230,4 +234,3 @@ class F2Field {
}
module.exports = F2Field;

View File

@@ -17,9 +17,9 @@
snarkjs. If not, see <https://www.gnu.org/licenses/>.
*/
const fUtils = require("./futils.js");
import * as fUtils from "./futils.js";
class F3Field {
export default class F3Field {
constructor(F, nonResidue) {
this.type="F3";
this.F = F;
@@ -171,6 +171,10 @@ class F3Field {
return fUtils.mulScalar(this, base, e);
}
pow(base, e) {
return fUtils.exp(this, base, e);
}
exp(base, e) {
return fUtils.exp(this, base, e);
}
@@ -275,5 +279,3 @@ class F3Field {
}
}
module.exports = F3Field;

View File

@@ -24,8 +24,7 @@
by the array [ p0, p1, p2, ... , pn ].
*/
const assert = require("assert");
class FFT {
export default class FFT {
constructor (G, F, opMulGF) {
this.F = F;
this.G = G;
@@ -51,7 +50,8 @@ class FFT {
this.roots = [];
/* for (let i=0; i<16; i++) {
/*
for (let i=0; i<16; i++) {
let r = this.F.one;
n = 1 << i;
const rootsi = new Array(n);
@@ -62,7 +62,7 @@ class FFT {
this.roots.push(rootsi);
}
*/
*/
this._setRoots(15);
}
@@ -86,7 +86,9 @@ class FFT {
this._setRoots(bits);
const m = 1 << bits;
assert (p.length == m);
if (p.length != m) {
throw new Error("Size must be multiple of 2");
}
const res = __fft(this, p, bits, 0, 1);
return res;
}
@@ -97,8 +99,10 @@ class FFT {
const bits = log2(p.length-1)+1;
this._setRoots(bits);
const m = 1 << bits;
assert (p.length == m);
const res = __ffti(this, p, bits, 0, 1);
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++) {
@@ -142,5 +146,3 @@ function __fft(PF, pall, bits, offset, step) {
return out;
}
module.exports = FFT;

View File

@@ -1,28 +1,25 @@
const Scalar = require("./scalar");
const assert = require("assert");
import * as Scalar from "./scalar.js";
// Check here: https://eprint.iacr.org/2012/685.pdf
module.exports = function buildSqrt (F) {
export default function buildSqrt (F) {
if ((F.m % 2) == 1) {
if (Scalar.eq(Scalar.mod(F.p, 4), 1 )) {
if (Scalar.eq(Scalar.mod(F.p, 8), 1 )) {
if (Scalar.eq(Scalar.mod(F.p, 16), 1 )) {
// alg7_muller(F);
alg5_tonelliShanks(F);
} else if (Scalar.eq(Scalar.mod(F.p, 16), 1 )) {
} else if (Scalar.eq(Scalar.mod(F.p, 16), 9 )) {
alg4_kong(F);
} else {
assert(false);
throw new Error("Field withot sqrt");
}
} else if (Scalar.eq(Scalar.mod(F.p, 8), 5 )) {
alg3_atkin(F);
} else {
assert(false);
throw new Error("Field withot sqrt");
}
} else if (Scalar.eq(Scalar.mod(F.p, 4), 3 )) {
alg2_shanks(F);
} else {
assert(false);
}
} else {
const pm2mod4 = Scalar.mod(Scalar.pow(F.p, F.m/2), 4);
@@ -35,96 +32,9 @@ module.exports = function buildSqrt (F) {
}
}
};
function alg7_muller(F) {
F.sqrt_q = Scalar.pow(F.p, F.m);
F.sqrt_2 = F.add(F.one, F.one);
F.sqrt_4 = F.add(F.sqrt_2, F.sqrt_2);
F.sqrt_e = Scalar.div( Scalar.sub(F.sqrt_q, 1) , 2);
F.sqrt_bits = Scalar.bits(Scalar.div( Scalar.sub(F.sqrt_q, 1) , 4));
F.sqrt_v = function(alfa) {
const d = [];
d[0] = alfa;
d[1] = this.sub(this.square(alfa), this.sqrt_2);
for (let j=F.sqrt_bits.length-2; j>0; j--) {
const d0 =
this.sub(
this.mul(d[0], d[1]),
alfa
);
const d1 =
this.sub(
this.square( d[ 1 - F.sqrt_bits[j] ]),
this.sqrt_2
);
d[ 1 - F.sqrt_bits[j] ] = d0;
d[ F.sqrt_bits[j] ] = d1;
/*
d[ 1 - F.sqrt_bits[j] ] =
this.sub(
this.mul(d[0], d[1]),
alfa
);
d[ F.sqrt_bits[j] ] =
this.sub(
this.square( d[ 1 - F.sqrt_bits[j] ]),
this.sqrt_2
);
*/
}
if (F.sqrt_bits[0] == 1) {
return this.sub(
this.mul(d[0], d[1]),
alfa
);
} else {
return this.sub(
this.square(d[0]),
this.sqrt_2
);
}
};
F.sqrt = function(a) {
if (this.isZero(a)) return this.zero;
if (this.eq(a, this.sqrt_4)) return this.sqrt_2;
let t = this.one;
let a1 = this.pow( this.sub(a, F.sqrt_4), F.sqrt_e);
while (this.eq(a1, this.one)) {
t = this.random();
while (this.isZero(t) || this.eq(t, this.one)) {
t = this.random();
}
const b = this.sub(this.mul(a, this.square(t)), this.sqrt_4);
if (this.isZero(b)) {
return this.mul(this.sqrt_2, this.inv(t));
}
a1 = this.pow(b, F.sqrt_e);
}
const alfa = this.sub(this.mul(a, this.square(t)), this.sqrt_2);
const x = this.div( this.sqrt_v(alfa), t);
const a0 = this.sub(this.square(x), a);
if (!this.isZero(a0)) return null;
return x;
};
}
function alg5_tonelliShanks(F) {
F.sqrt_q = Scalar.pow(F.p, F.m);
@@ -180,13 +90,13 @@ function alg5_tonelliShanks(F) {
function alg4_kong(F) {
F.sqrt = function() {
assert(false, "Not implemented");
throw new Error("Sqrt alg 4 not implemented");
};
}
function alg3_atkin(F) {
F.sqrt = function() {
assert(false, "Not implemented");
throw new Error("Sqrt alg 3 not implemented");
};
}
@@ -213,7 +123,7 @@ function alg2_shanks(F) {
function alg10_adj(F) {
F.sqrt = function() {
assert(false, "Not implemented");
throw new Error("Sqrt alg 10 not implemented");
};
}
@@ -251,6 +161,6 @@ function alg9_adj(F) {
function alg8_complex(F) {
F.sqrt = function() {
assert(false, "Not implemented");
throw new Error("Sqrt alg 8 not implemented");
};
}

View File

@@ -17,11 +17,10 @@
snarkjs. If not, see <https://www.gnu.org/licenses/>.
*/
const Scalar = require("./scalar.js");
const assert = require("assert");
import * as Scalar from "./scalar.js";
exports.mulScalar = (F, base, e) => {
export function mulScalar(F, base, e) {
let res;
if (Scalar.isZero(e)) return F.zero;
@@ -33,7 +32,7 @@ exports.mulScalar = (F, base, e) => {
} else if (n[n.length-1] == -1) {
res = F.neg(base);
} else {
assert(false);
throw new Error("invlaud NAF");
}
for (let i=n.length-2; i>=0; i--) {
@@ -48,7 +47,7 @@ exports.mulScalar = (F, base, e) => {
}
return res;
};
}
/*
@@ -70,7 +69,7 @@ exports.mulScalar = (F, base, e) =>{
*/
exports.exp = (F, base, e) => {
export function exp(F, base, e) {
if (Scalar.isZero(e)) return F.one;
@@ -90,6 +89,6 @@ exports.exp = (F, base, e) => {
}
return res;
};
}

View File

View File

@@ -24,7 +24,7 @@
by the array [ p0, p1, p2, ... , pn ].
*/
class PolField {
export default class PolField {
constructor (F) {
this.F = F;
@@ -509,7 +509,7 @@ class PolField {
}
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.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])));
@@ -615,4 +615,3 @@ function __bitReverse(p, bits) {
}
module.exports = PolField;

View File

@@ -1,11 +1,8 @@
/* global window */
const ChaCha = require("./chacha");
import ChaCha from "./chacha.js";
import crypto from "crypto";
module.exports.getRandomBytes = getRandomBytes;
module.exports.getRandomSeed = getRandomSeed;
module.exports.getThreadRng = getThreadRng;
function getRandomBytes(n) {
export function getRandomBytes(n) {
let array = new Uint8Array(n);
if (typeof window !== "undefined") { // Browser
if (typeof window.crypto !== "undefined") { // Supported
@@ -17,12 +14,12 @@ function getRandomBytes(n) {
}
}
else { // NodeJS
module.require("crypto").randomFillSync(array);
crypto.randomFillSync(array);
}
return array;
}
function getRandomSeed() {
export function getRandomSeed() {
const arr = getRandomBytes(32);
const arrV = new Uint32Array(arr.buffer);
const seed = [];
@@ -34,7 +31,7 @@ function getRandomSeed() {
let threadRng = null;
function getThreadRng() {
export function getThreadRng() {
if (threadRng) return threadRng;
threadRng = new ChaCha(getRandomSeed());
return threadRng;

View File

@@ -17,9 +17,9 @@
snarkjs. If not, see <https://www.gnu.org/licenses/>.
*/
const fUtils = require("./futils.js");
import * as fUtils from "./futils.js";
class RatField {
export default class RatField {
constructor(F) {
this.F = F;
this.zero = [F.zero, F.one];
@@ -123,5 +123,3 @@ class RatField {
}
}
module.exports = RatField;

View File

@@ -1,18 +1,19 @@
const assert = require("assert");
import * as Scalar_native from "./scalar_native.js";
import * as Scalar_bigint from "./scalar_bigint.js";
const supportsNativeBigInt = typeof BigInt === "function";
let Scalar;
let Scalar = {};
if (supportsNativeBigInt) {
Scalar = require("./scalar_native.js");
Object.assign(Scalar, Scalar_native);
} else {
Scalar = require("./scalar_bigint.js");
Object.assign(Scalar, Scalar_bigint);
}
// Returns a buffer with Little Endian Representation
Scalar.__proto__.toRprLE = function rprBE(buff, o, e, n8) {
Scalar.toRprLE = function rprBE(buff, o, e, n8) {
const s = "0000000" + e.toString(16);
const v = new Uint32Array(buff.buffer, o, n8/4);
const l = (((s.length-7)*4 - 1) >> 5)+1; // Number of 32bit words;
@@ -22,7 +23,7 @@ Scalar.__proto__.toRprLE = function rprBE(buff, o, e, n8) {
};
// Returns a buffer with Big Endian Representation
Scalar.__proto__.toRprBE = function rprLEM(buff, o, e, n8) {
Scalar.toRprBE = function rprLEM(buff, o, e, n8) {
const s = "0000000" + e.toString(16);
const v = new DataView(buff.buffer, o, n8);
const l = (((s.length-7)*4 - 1) >> 5)+1; // Number of 32bit words;
@@ -31,7 +32,7 @@ Scalar.__proto__.toRprBE = function rprLEM(buff, o, e, n8) {
};
// Pases a buffer with Little Endian Representation
Scalar.__proto__.fromRprLE = function rprLEM(buff, o, n8) {
Scalar.fromRprLE = function rprLEM(buff, o, n8) {
n8 = n8 || buff.byteLength;
const v = new Uint32Array(buff.buffer, o, n8/4);
const a = new Array(n8/4);
@@ -40,7 +41,7 @@ Scalar.__proto__.fromRprLE = function rprLEM(buff, o, n8) {
};
// Pases a buffer with Big Endian Representation
Scalar.__proto__.fromRprBE = function rprLEM(buff, o, n8) {
Scalar.fromRprBE = function rprLEM(buff, o, n8) {
n8 = n8 || buff.byteLength;
const v = new DataView(buff.buffer, o, n8);
const a = new Array(n8/4);
@@ -50,22 +51,69 @@ Scalar.__proto__.fromRprBE = function rprLEM(buff, o, n8) {
return Scalar.fromString(a.join(""), 16);
};
Scalar.__proto__.toString = function toString(a, radix) {
Scalar.toString = function toString(a, radix) {
return a.toString(radix);
};
Scalar.__proto__.toLEBuff = function toLEBuff(a) {
Scalar.toLEBuff = function toLEBuff(a) {
const buff = new Uint8Array(Math.floor((Scalar.bitLength(a) - 1) / 8) +1);
Scalar.toRprLE(buff, 0, a, buff.byteLength);
return buff;
};
Scalar.zero = Scalar.e(0);
Scalar.one = Scalar.e(1);
module.exports = Scalar;
export let {
toRprLE,
toRprBE,
fromRprLE,
fromRprBE,
toString,
toLEBuff,
zero,
one,
fromString,
e,
fromArray,
bitLength,
isNegative,
isZero,
shiftLeft,
shiftRight,
shl,
shr,
isOdd,
naf,
bits,
toNumber,
toArray,
add,
sub,
neg,
mul,
square,
pow,
exp,
abs,
div,
mod,
eq,
neq,
lt,
gt,
leq,
geq,
band,
bor,
bxor,
land,
lor,
lnot,
} = Scalar;

View File

@@ -1,7 +1,6 @@
const bigInt = require("big-integer");
const assert = require("assert");
import bigInt from "big-integer";
module.exports.fromString = function fromString(s, radix) {
export function fromString(s, radix) {
if (typeof s == "string") {
if (s.slice(0,2) == "0x") {
return bigInt(s.slice(2), 16);
@@ -11,43 +10,43 @@ module.exports.fromString = function fromString(s, radix) {
} else {
return bigInt(s, radix);
}
};
}
module.exports.e = module.exports.fromString;
export const e = fromString;
module.exports.fromArray = function fromArray(a, radix) {
export function fromArray(a, radix) {
return bigInt.fromArray(a, radix);
};
}
module.exports.bitLength = function (a) {
export function bitLength(a) {
return bigInt(a).bitLength();
};
}
module.exports.isNegative = function (a) {
export function isNegative(a) {
return bigInt(a).isNegative();
};
}
module.exports.isZero = function (a) {
export function isZero(a) {
return bigInt(a).isZero();
};
}
module.exports.shiftLeft = function (a, n) {
export function shiftLeft(a, n) {
return bigInt(a).shiftLeft(n);
};
}
module.exports.shiftRight = function (a, n) {
export function shiftRight(a, n) {
return bigInt(a).shiftRight(n);
};
}
module.exports.shl = module.exports.shiftLeft;
module.exports.shr = module.exports.shiftRight;
export const shl = shiftLeft;
export const shr = shiftRight;
module.exports.isOdd = function (a) {
export function isOdd(a) {
return bigInt(a).isOdd();
};
}
module.exports.naf = function naf(n) {
export function naf(n) {
let E = bigInt(n);
const res = [];
while (E.gt(bigInt.zero)) {
@@ -61,10 +60,9 @@ module.exports.naf = function naf(n) {
E = E.shiftRight(1);
}
return res;
};
}
module.exports.bits = function naf(n) {
export function bits(n) {
let E = bigInt(n);
const res = [];
while (E.gt(bigInt.zero)) {
@@ -76,99 +74,105 @@ module.exports.bits = function naf(n) {
E = E.shiftRight(1);
}
return res;
};
}
module.exports.toNumber = function(s) {
assert(s.lt(bigInt("9007199254740992", 10)));
export function toNumber(s) {
if (!s.lt(bigInt("9007199254740992", 10))) {
throw new Error("Number too big");
}
return s.toJSNumber();
};
}
module.exports.toArray = function(s, radix) {
export function toArray(s, radix) {
return bigInt(s).toArray(radix);
};
}
module.exports.add = function(a, b) {
export function add(a, b) {
return bigInt(a).add(bigInt(b));
};
}
module.exports.sub = function(a, b) {
export function sub(a, b) {
return bigInt(a).minus(bigInt(b));
};
}
module.exports.neg = function(a) {
export function neg(a) {
return bigInt.zero.minus(bigInt(a));
};
}
module.exports.mul = function(a, b) {
export function mul(a, b) {
return bigInt(a).times(bigInt(b));
};
}
module.exports.square = function(a) {
export function square(a) {
return bigInt(a).square();
};
}
module.exports.pow = function(a, b) {
export function pow(a, b) {
return bigInt(a).pow(bigInt(b));
};
}
module.exports.abs = function(a) {
export function exp(a, b) {
return bigInt(a).pow(bigInt(b));
}
export function abs(a) {
return bigInt(a).abs();
};
}
module.exports.div = function(a, b) {
export function div(a, b) {
return bigInt(a).divide(bigInt(b));
};
}
module.exports.mod = function(a, b) {
export function mod(a, b) {
return bigInt(a).mod(bigInt(b));
};
}
module.exports.eq = function(a, b) {
export function eq(a, b) {
return bigInt(a).eq(bigInt(b));
};
}
module.exports.neq = function(a, b) {
export function neq(a, b) {
return bigInt(a).neq(bigInt(b));
};
}
module.exports.lt = function(a, b) {
export function lt(a, b) {
return bigInt(a).lt(bigInt(b));
};
}
module.exports.gt = function(a, b) {
export function gt(a, b) {
return bigInt(a).gt(bigInt(b));
};
}
module.exports.leq = function(a, b) {
export function leq(a, b) {
return bigInt(a).leq(bigInt(b));
};
}
module.exports.geq = function(a, b) {
export function geq(a, b) {
return bigInt(a).geq(bigInt(b));
};
}
module.exports.band = function(a, b) {
export function band(a, b) {
return bigInt(a).and(bigInt(b));
};
}
module.exports.bor = function(a, b) {
export function bor(a, b) {
return bigInt(a).or(bigInt(b));
};
}
module.exports.bxor = function(a, b) {
export function bxor(a, b) {
return bigInt(a).xor(bigInt(b));
};
}
module.exports.band = function(a, b) {
export function land(a, b) {
return (!bigInt(a).isZero()) && (!bigInt(b).isZero());
};
}
module.exports.bor = function(a, b) {
export function lor(a, b) {
return (!bigInt(a).isZero()) || (!bigInt(b).isZero());
};
}
module.exports.bnot = function(a) {
export function lnot(a) {
return bigInt(a).isZero();
};
}

View File

@@ -1,8 +1,7 @@
/* global BigInt */
const assert = require("assert");
const hexLen = [ 0, 1, 2, 2, 3, 3, 3, 3, 4 ,4 ,4 ,4 ,4 ,4 ,4 ,4];
module.exports.fromString = function fromString(s, radix) {
export function fromString(s, radix) {
if ((!radix)||(radix==10)) {
return BigInt(s);
} else if (radix==16) {
@@ -12,49 +11,49 @@ module.exports.fromString = function fromString(s, radix) {
return BigInt("0x"+s);
}
}
};
}
module.exports.e = module.exports.fromString;
export const e = fromString;
module.exports.fromArray = function fromArray(a, radix) {
export function fromArray(a, radix) {
let acc =0n;
radix = BigInt(radix);
for (let i=0; i<a.length; i++) {
acc = acc*radix + BigInt(a[i]);
}
return acc;
};
}
module.exports.bitLength = function (a) {
export function bitLength(a) {
const aS =a.toString(16);
return (aS.length-1)*4 +hexLen[parseInt(aS[0], 16)];
};
}
module.exports.isNegative = function (a) {
export function isNegative(a) {
return BigInt(a) < 0n;
};
}
module.exports.isZero = function (a) {
export function isZero(a) {
return !a;
};
}
module.exports.shiftLeft = function (a, n) {
export function shiftLeft(a, n) {
return BigInt(a) << BigInt(n);
};
}
module.exports.shiftRight = function (a, n) {
export function shiftRight(a, n) {
return BigInt(a) >> BigInt(n);
};
}
module.exports.shl = module.exports.shiftLeft;
module.exports.shr = module.exports.shiftRight;
export const shl = shiftLeft;
export const shr = shiftRight;
module.exports.isOdd = function (a) {
export function isOdd(a) {
return (BigInt(a) & 1n) == 1n;
};
}
module.exports.naf = function naf(n) {
export function naf(n) {
let E = BigInt(n);
const res = [];
while (E) {
@@ -68,10 +67,10 @@ module.exports.naf = function naf(n) {
E = E >> 1n;
}
return res;
};
}
module.exports.bits = function naf(n) {
export function bits(n) {
let E = BigInt(n);
const res = [];
while (E) {
@@ -83,14 +82,16 @@ module.exports.bits = function naf(n) {
E = E >> 1n;
}
return res;
};
}
module.exports.toNumber = function(s) {
assert(s<BigInt(Number.MAX_SAFE_INTEGER + 1));
export function toNumber(s) {
if (s>BigInt(Number.MAX_SAFE_INTEGER )) {
throw new Error("Number too big");
}
return Number(s);
};
}
module.exports.toArray = function(s, radix) {
export function toArray(s, radix) {
const res = [];
let rem = BigInt(s);
radix = BigInt(radix);
@@ -99,90 +100,94 @@ module.exports.toArray = function(s, radix) {
rem = rem / radix;
}
return res;
};
}
module.exports.add = function(a, b) {
export function add(a, b) {
return BigInt(a) + BigInt(b);
};
}
module.exports.sub = function(a, b) {
export function sub(a, b) {
return BigInt(a) - BigInt(b);
};
}
module.exports.neg = function(a) {
export function neg(a) {
return -BigInt(a);
};
}
module.exports.mul = function(a, b) {
export function mul(a, b) {
return BigInt(a) * BigInt(b);
};
}
module.exports.square = function(a) {
export function square(a) {
return BigInt(a) * BigInt(a);
};
}
module.exports.pow = function(a, b) {
export function pow(a, b) {
return BigInt(a) ** BigInt(b);
};
}
module.exports.abs = function(a) {
export function exp(a, b) {
return BigInt(a) ** BigInt(b);
}
export function abs(a) {
return BigInt(a) >= 0 ? BigInt(a) : -BigInt(a);
};
}
module.exports.div = function(a, b) {
export function div(a, b) {
return BigInt(a) / BigInt(b);
};
}
module.exports.mod = function(a, b) {
export function mod(a, b) {
return BigInt(a) % BigInt(b);
};
}
module.exports.eq = function(a, b) {
export function eq(a, b) {
return BigInt(a) == BigInt(b);
};
}
module.exports.neq = function(a, b) {
export function neq(a, b) {
return BigInt(a) != BigInt(b);
};
}
module.exports.lt = function(a, b) {
export function lt(a, b) {
return BigInt(a) < BigInt(b);
};
}
module.exports.gt = function(a, b) {
export function gt(a, b) {
return BigInt(a) > BigInt(b);
};
}
module.exports.leq = function(a, b) {
export function leq(a, b) {
return BigInt(a) <= BigInt(b);
};
}
module.exports.geq = function(a, b) {
export function geq(a, b) {
return BigInt(a) >= BigInt(b);
};
}
module.exports.band = function(a, b) {
export function band(a, b) {
return BigInt(a) & BigInt(b);
};
}
module.exports.bor = function(a, b) {
export function bor(a, b) {
return BigInt(a) | BigInt(b);
};
}
module.exports.bxor = function(a, b) {
export function bxor(a, b) {
return BigInt(a) ^ BigInt(b);
};
}
module.exports.land = function(a, b) {
export function land(a, b) {
return BigInt(a) && BigInt(b);
};
}
module.exports.lor = function(a, b) {
export function lor(a, b) {
return BigInt(a) || BigInt(b);
};
}
module.exports.lnot = function(a) {
export function lnot(a) {
return !BigInt(a);
};
}

View File

@@ -1,4 +1,4 @@
/* global navigator, Blob, Worker, WebAssembly */
/* global window, navigator, Blob, Worker, WebAssembly */
/*
Copyright 2019 0KIMS association.
@@ -21,17 +21,14 @@
const MEM_SIZE = 4096; // Memory size in 64K Pakes (256Mb)
const assert = require("assert");
const thread = require("./threadman_thread");
const FFT = require("./fft");
const {log2, buffReverseBits} = require("./utils");
import thread from "./threadman_thread.js";
import os from "os";
import NodeWorker_mod from "worker_threads";
const inBrowser = (typeof window !== "undefined");
let NodeWorker;
let NodeCrypto;
if (!inBrowser) {
NodeWorker = require("worker_threads").Worker;
NodeCrypto = require("crypto");
NodeWorker = NodeWorker_mod.Worker;
}
class Deferred {
@@ -43,17 +40,31 @@ class Deferred {
}
}
function base64ToArrayBuffer(base64) {
if (process.browser) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes;
} else {
return new Uint8Array(Buffer.from(base64, "base64"));
}
}
async function buildThreadManager(wasm, singleThread) {
export default async function buildThreadManager(wasm, singleThread) {
const tm = new ThreadManager();
tm.memory = new WebAssembly.Memory({initial:MEM_SIZE});
tm.u8 = new Uint8Array(tm.memory.buffer);
tm.u32 = new Uint32Array(tm.memory.buffer);
const wasmModule = await WebAssembly.compile(wasm.code);
const wasmModule = await WebAssembly.compile(base64ToArrayBuffer(wasm.code));
tm.instance = await WebAssembly.instantiate(wasmModule, {
env: {
@@ -71,17 +82,17 @@ async function buildThreadManager(wasm, singleThread) {
tm.pG2zero = wasm.pG2zero;
tm.pOneT = wasm.pOneT;
// tm.pTmp0 = tm.alloc(curve.G2.F.n8*3);
// tm.pTmp1 = tm.alloc(curve.G2.F.n8*3);
// tm.pTmp0 = tm.alloc(curve.G2.F.n8*3);
// tm.pTmp1 = tm.alloc(curve.G2.F.n8*3);
if (singleThread) {
tm.code = wasm.code;
tm.code = base64ToArrayBuffer(wasm.code);
tm.taskManager = thread();
await tm.taskManager([{
cmd: "INIT",
init: MEM_SIZE,
code: wasm.code
code: tm.code.slice()
}]);
tm.concurrency = 1;
} else {
@@ -94,7 +105,6 @@ async function buildThreadManager(wasm, singleThread) {
if ((typeof(navigator) === "object") && navigator.hardwareConcurrency) {
concurrency = navigator.hardwareConcurrency;
} else {
const os = require("os");
concurrency = os.cpus().length;
}
tm.concurrency = concurrency;
@@ -120,12 +130,12 @@ async function buildThreadManager(wasm, singleThread) {
const initPromises = [];
for (let i=0; i<tm.workers.length;i++) {
const copyCode = wasm.code.buffer.slice(0);
const copyCode = base64ToArrayBuffer(wasm.code).slice();
initPromises.push(tm.postAction(i, [{
cmd: "INIT",
init: MEM_SIZE,
code: copyCode
}], [copyCode]));
}], [copyCode.buffer]));
}
await Promise.all(initPromises);
@@ -157,18 +167,20 @@ class ThreadManager {
}
startSyncOp() {
assert(this.oldPFree == 0);
if (this.oldPFree != 0) throw new Error("Sync operation in progress");
this.oldPFree = this.u32[0];
}
endSyncOp() {
assert(this.oldPFree != 0);
if (this.oldPFree == 0) throw new Error("No sync operation in progress");
this.u32[0] = this.oldPFree;
this.oldPFree = 0;
}
postAction(workerId, e, transfers, _deferred) {
assert(this.working[workerId] == false);
if (this.working[workerId]) {
throw new Error("Posting a job t a working worker");
}
this.working[workerId] = true;
this.pendingDeferreds[workerId] = _deferred ? _deferred : new Deferred();
@@ -236,7 +248,3 @@ class ThreadManager {
}
module.exports = buildThreadManager;

View File

@@ -1,6 +1,6 @@
/* global WebAssembly */
module.exports = function thread(self) {
export default function thread(self) {
let instance;
let memory;
let u32;
@@ -69,7 +69,6 @@ module.exports = function thread(self) {
}
function runTask(task) {
const self=this;
if (task[0].cmd == "INIT") {
return init(task[0]);
}
@@ -113,145 +112,6 @@ module.exports = function thread(self) {
return ctx.out;
}
function batchApplyKey(task) {
const outBuffLen = task.buff.byteLength;
const oldAlloc = u32[0];
const pBufIn = allocBuffer(task.buff);
const pFirst = allocBuffer(task.first);
const pInc = allocBuffer(task.inc);
const pBuffOut = alloc(outBuffLen);
if (task.Gs == "G1") {
instance.exports.g1m_batchApplyKey(pBufIn, task.n, pFirst, pInc, pBuffOut);
} else {
instance.exports.g2m_batchApplyKey(pBufIn, task.n, pFirst, pInc, pBuffOut);
}
const outBuff = getBuffer(pBuffOut, outBuffLen).slice();
u32[0] = oldAlloc;
return [ outBuff, outBuff.buffer];
}
function batchConvert(task) {
const oldAlloc = u32[0];
const outBuffLen = task.n*task.sOut;
const pBufIn = allocBuffer(task.buff);
const pBuffOut = alloc(outBuffLen);
instance.exports[task.fnName](pBufIn, task.n, pBuffOut);
const outBuff = getBuffer(pBuffOut, outBuffLen).slice();
u32[0] = oldAlloc;
return [ outBuff, outBuff.buffer];
}
function batchConvertOld(task) {
const oldAlloc = u32[0];
const outBuffLen = task.n*task.sGin;
const pBufIn = allocBuffer(task.buff);
const pBuffOut = alloc(outBuffLen);
if (task.Gs == "G1") {
if (task.fr=="LEM") {
if (task.to=="U") {
instance.exports.g1m_batchLEMtoU(pBufIn, task.n, pBuffOut);
} else if (task.to=="C") {
instance.exports.g1m_batchLEMtoC(pBufIn, task.n, pBuffOut);
} else {
throw new Error("Invalid to: "+task.to);
}
} else {
throw new Error("Invalid fr: "+task.fr);
}
} else if (task.Gs == "G2") {
if (task.fr=="LEM") {
if (task.to=="U") {
instance.exports.g2m_batchLEMtoU(pBufIn, task.n, pBuffOut);
} else if (task.to=="C") {
instance.exports.g2m_batchLEMtoC(pBufIn, task.n, pBuffOut);
} else {
throw new Error("Invalid to: "+task.to);
}
} else {
throw new Error("Invalid fr: "+task.fr);
}
} else {
throw new Error("Invalid group: "+task.gs);
}
const outBuff = getBuffer(pBuffOut, outBuffLen).slice();
u32[0] = oldAlloc;
return [ outBuff, outBuff.buffer];
}
function fft(task) {
const oldAlloc = u32[0];
const maxBuffLen = task.n*task.sGin*3/2;
const pBuff = alloc(maxBuffLen);
setBuffer(pBuff, task.buff);
if (task.Gs == "G1") {
instance.exports.g1m_batchToJacobian(pBuff, task.n, pBuff);
if (task.inverse) {
instance.exports.g1m_ifft(pBuff, task.n);
} else {
instance.exports.g1m_fft(pBuff, task.n);
}
instance.exports.g1m_batchToAffine(pBuff, task.n, pBuff);
} else if (task.Gs == "G2") {
instance.exports.g2m_batchToJacobian(pBuff, task.n, pBuff);
if (task.inverse) {
instance.exports.g2m_ifft(pBuff, task.n);
} else {
instance.exports.g2m_fft(pBuff, task.n);
}
instance.exports.g2m_batchToAffine(pBuff, task.n, pBuff);
} else if (task.Gs == "Fr") {
if (task.inverse) {
instance.exports.frm_ifft(pBuff, task.n);
} else {
instance.exports.frm_fft(pBuff, task.n);
}
} else {
throw new Error("Invalid group: "+task.gs);
}
const outBuff = getBuffer(pBuff, task.n*task.sGin).slice();
u32[0] = oldAlloc;
return [ outBuff, outBuff.buffer];
}
function multiexp(task) {
const oldAlloc = u32[0];
const pBuffBases = allocBuffer(task.buffBases);
const pBuffScalars = allocBuffer(task.buffScalars);
const pOut = alloc(task.sOut);
instance.exports[task.fnName](pBuffBases, pBuffScalars, task.sScalar, task.n, pOut);
const outBuff = getBuffer(pOut, task.sOut).slice();
u32[0] = oldAlloc;
return [ outBuff, outBuff.buffer];
}
function taskManager(task) {
if (task.command == "INIT") {
return init(task);
} else if (task.command == "BATCH_APPLY_KEY") {
return batchApplyKey(task);
} else if (task.command == "BATCH_CONVERT") {
return batchConvert(task);
} else if (task.command == "FFT") {
return fft(task);
} else if (task.command == "MULTIEXP") {
return multiexp(task);
} else {
console.log("Invalid task", task);
throw new Error("Invalid task");
}
}
return runTask;
};
}

View File

@@ -1,10 +1,13 @@
const assert = require("assert");
import * as utils_native from "./utils_native.js";
import * as utils_bigint from "./utils_bigint.js";
let utils = {};
const supportsNativeBigInt = typeof BigInt === "function";
if (supportsNativeBigInt) {
module.exports = require("./utils_native.js");
Object.assign(utils, utils_native);
} else {
module.exports = require("./utils_bigint.js");
Object.assign(utils, utils_bigint);
}
@@ -24,36 +27,46 @@ function _revSlow(idx, bits) {
return res;
}
function bitReverse(idx, bits) {
utils.bitReverse = function bitReverse(idx, bits) {
return (
_revTable[idx >>> 24] |
(_revTable[(idx >>> 16) & 0xFF] << 8) |
(_revTable[(idx >>> 8) & 0xFF] << 16) |
(_revTable[idx & 0xFF] << 24)
) >>> (32-bits);
}
};
function log2( V )
utils.log2 = 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 buffReverseBits(buff, eSize) {
utils.buffReverseBits = function buffReverseBits(buff, eSize) {
const n = buff.byteLength /eSize;
const bits = log2(n);
assert (n == (1 << bits));
const bits = utils.log2(n);
if (n != (1 << bits)) {
throw new Error("Invalid number of pointers");
}
for (let i=0; i<n; i++) {
const r = bitReverse(i,bits);
const r = utils.bitReverse(i,bits);
if (i>r) {
const tmp = buff.slice(i*eSize, (i+1)*eSize);
buff.set( buff.slice(r*eSize, (r+1)*eSize), i*eSize);
buff.set(tmp, r*eSize);
}
}
}
};
module.exports.bitReverse = bitReverse;
module.exports.log2 = log2;
module.exports.buffReverseBits = buffReverseBits;
export let {
bitReverse,
log2,
buffReverseBits,
stringifyBigInts,
unstringifyBigInts,
beBuff2int,
beInt2Buff,
leBuff2int,
leInt2Buff,
} = utils;

View File

@@ -1,7 +1,6 @@
const assert = require("assert");
const bigInt = require("big-integer");
import bigInt from "big-integer";
module.exports.stringifyBigInts = function stringifyBigInts(o) {
export function stringifyBigInts(o) {
if ((typeof(o) == "bigint") || o.eq !== undefined) {
return o.toString(10);
} else if (Array.isArray(o)) {
@@ -16,9 +15,9 @@ module.exports.stringifyBigInts = function stringifyBigInts(o) {
} else {
return o;
}
};
}
module.exports.unstringifyBigInts = function unstringifyBigInts(o) {
export function unstringifyBigInts(o) {
if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) {
return bigInt(o);
} else if (Array.isArray(o)) {
@@ -33,51 +32,55 @@ module.exports.unstringifyBigInts = function unstringifyBigInts(o) {
} else {
return o;
}
};
}
module.exports.beBuff2int = function beBuff2int(buff) {
export function beBuff2int(buff) {
let res = bigInt.zero;
for (let i=0; i<buff.length; i++) {
const n = bigInt(buff[buff.length - i - 1]);
res = res.add(n.shiftLeft(i*8));
}
return res;
};
}
module.exports.beInt2Buff = function beInt2Buff(n, len) {
export function beInt2Buff(n, len) {
let r = n;
let o =len-1;
const buff = Buffer.alloc(len);
const buff = new Uint8Array(len);
while ((r.gt(bigInt.zero))&&(o>=0)) {
let c = Number(r.and(bigInt("255")));
buff[o] = c;
o--;
r = r.shiftRight(8);
}
assert(r.eq(bigInt.zero));
if (!r.eq(bigInt.zero)) {
throw new Error("Number does not fit in this length");
}
return buff;
};
}
module.exports.leBuff2int = function leBuff2int (buff) {
export function leBuff2int (buff) {
let res = bigInt.zero;
for (let i=0; i<buff.length; i++) {
const n = bigInt(buff[i]);
res = res.add(n.shiftLeft(i*8));
}
return res;
};
}
module.exports.leInt2Buff = function leInt2Buff(n, len) {
export function leInt2Buff(n, len) {
let r = n;
let o =0;
const buff = Buffer.alloc(len);
const buff = new Uint8Array(len);
while ((r.gt(bigInt.zero))&&(o<buff.length)) {
let c = Number(r.and(bigInt(255)));
buff[o] = c;
o++;
r = r.shiftRight(8);
}
assert(r.eq(bigInt.zero));
if (!r.eq(bigInt.zero)) {
throw new Error("Number does not fit in this length");
}
return buff;
};
}

View File

@@ -1,8 +1,7 @@
/* global BigInt */
const assert = require("assert");
const Scalar =require("./scalar");
import * as Scalar from "./scalar.js";
module.exports.stringifyBigInts = function stringifyBigInts(o) {
export function stringifyBigInts(o) {
if ((typeof(o) == "bigint") || o.eq !== undefined) {
return o.toString(10);
} else if (Array.isArray(o)) {
@@ -17,9 +16,9 @@ module.exports.stringifyBigInts = function stringifyBigInts(o) {
} else {
return o;
}
};
}
module.exports.unstringifyBigInts = function unstringifyBigInts(o) {
export function unstringifyBigInts(o) {
if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) {
return BigInt(o);
} else if (Array.isArray(o)) {
@@ -35,95 +34,103 @@ module.exports.unstringifyBigInts = function unstringifyBigInts(o) {
} else {
return o;
}
};
}
module.exports.beBuff2int = function beBuff2int(buff) {
export function beBuff2int(buff) {
let res = 0n;
let i = buff.length;
let offset = 0;
const buffV = new DataView(buff.buffer);
while (i>0) {
if (i >= 4) {
i -= 4;
res += BigInt(buff.readUInt32BE(i)) << BigInt(offset*8);
res += BigInt(buffV.getUint32(i)) << BigInt(offset*8);
offset += 4;
} else if (i >= 2) {
i -= 2;
res += BigInt(buff.readUInt16BE(i)) << BigInt(offset*8);
res += BigInt(buffV.getUint16(i)) << BigInt(offset*8);
offset += 2;
} else {
i -= 1;
res += BigInt(buff.readUInt8(i)) << BigInt(offset*8);
res += BigInt(buffV.getUint8(i)) << BigInt(offset*8);
offset += 1;
}
}
return res;
};
}
module.exports.beInt2Buff = function beInt2Buff(n, len) {
export function beInt2Buff(n, len) {
let r = n;
const buff = Buffer.alloc(len);
const buff = new Uint8Array(len);
const buffV = new DataView(buff.buffer);
let o = len;
while (o > 0) {
if (o-4 >= 0) {
o -= 4;
buff.writeUInt32BE(Number(r & 0xFFFFFFFFn), o);
buffV.setUint32(o, Number(r & 0xFFFFFFFFn));
r = r >> 32n;
} else if (o-2 >= 0) {
o -= 2;
buff.writeUInt16BE(Number(r & 0xFFFFn), o);
buffV.setUint16(o, Number(r & 0xFFFFn));
r = r >> 16n;
} else {
o -= 1;
buff.writeUInt8(Number(r & 0xFFn),o );
buffV.setUint8(o, Number(r & 0xFFn));
r = r >> 8n;
}
}
assert(r == 0n);
if (r) {
throw new Error("Number does not fit in this length");
}
return buff;
};
}
module.exports.leBuff2int = function leBuff2int(buff) {
export function leBuff2int(buff) {
let res = 0n;
let i = 0;
const buffV = new DataView(buff.buffer);
while (i<buff.length) {
if (i + 4 <= buff.length) {
res += BigInt(buff.readUInt32LE(i)) << BigInt( i*8);
res += BigInt(buffV.getUint32(i, true)) << BigInt( i*8);
i += 4;
} else if (i + 4 <= buff.length) {
res += BigInt(buff.readUInt16LE(i)) << BigInt( i*8);
res += BigInt(buffV.getUint16(i, true)) << BigInt( i*8);
i += 2;
} else {
res += BigInt(buff.readUInt8(i)) << BigInt( i*8);
res += BigInt(buffV.getUint8(i, true)) << BigInt( i*8);
i += 1;
}
}
return res;
};
}
module.exports.leInt2Buff = function leInt2Buff(n, len) {
export function leInt2Buff(n, len) {
let r = n;
if (typeof len === "undefined") {
len = Math.floor((Scalar.bitLength(n) - 1) / 8) +1;
if (len==0) len = 1;
}
const buff = Buffer.alloc(len);
const buff = new Uint8Array(len);
const buffV = new DataView(buff.buffer);
let o = 0;
while (o < len) {
if (o+4 <= len) {
buff.writeUInt32LE(Number(r & 0xFFFFFFFFn), o );
buffV.setUint32(o, Number(r & 0xFFFFFFFFn), true );
o += 4;
r = r >> 32n;
} else if (o+2 <= len) {
buff.writeUInt16LE(Number(r & 0xFFFFn), o );
buff.setUint16(Number(o, r & 0xFFFFn), true );
o += 2;
r = r >> 16n;
} else {
buff.writeUInt8(Number(r & 0xFFn), o );
buff.setUint8(Number(o, r & 0xFFn), true );
o += 1;
r = r >> 8n;
}
}
assert(r == 0n);
if (r) {
throw new Error("Number does not fit in this length");
}
return buff;
};
}

View File

@@ -1,11 +1,9 @@
const assert = require("assert");
const Scalar = require("./scalar");
const utils = require("./utils");
const buildBatchConvert = require("./engine_batchconvert");
import * as Scalar from "./scalar.js";
import buildBatchConvert from "./engine_batchconvert.js";
class WasmCurve {
export default class WasmCurve {
constructor(tm, prefix, F, pGen, pGb, cofactor) {
this.tm = tm;
@@ -77,7 +75,7 @@ class WasmCurve {
} else if (b.byteLength == this.F.n8*2) {
return this.op2("_addMixed", a, b);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
} else if (a.byteLength == this.F.n8*2) {
if (b.byteLength == this.F.n8*3) {
@@ -85,10 +83,10 @@ class WasmCurve {
} else if (b.byteLength == this.F.n8*2) {
return this.op2("_addAffine", a, b);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
}
@@ -99,7 +97,7 @@ class WasmCurve {
} else if (b.byteLength == this.F.n8*2) {
return this.op2("_subMixed", a, b);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
} else if (a.byteLength == this.F.n8*2) {
if (b.byteLength == this.F.n8*3) {
@@ -107,10 +105,10 @@ class WasmCurve {
} else if (b.byteLength == this.F.n8*2) {
return this.op2("_subAffine", a, b);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
}
@@ -120,7 +118,7 @@ class WasmCurve {
} else if (a.byteLength == this.F.n8*2) {
return this.op1Affine("_negAffine", a);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
}
@@ -130,7 +128,7 @@ class WasmCurve {
} else if (a.byteLength == this.F.n8*2) {
return this.op1("_doubleAffine", a);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
}
@@ -140,7 +138,7 @@ class WasmCurve {
} else if (a.byteLength == this.F.n8*2) {
return this.op1Bool("_isZeroAffine", a);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
}
@@ -154,7 +152,7 @@ class WasmCurve {
} else if (a.byteLength == this.F.n8*2) {
fnName = this.prefix + "_timesScalarAffine";
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
this.tm.setBuff(this.pOp1, a);
this.tm.setBuff(this.pOp2, s);
@@ -169,7 +167,7 @@ class WasmCurve {
} else if (a.byteLength == this.F.n8*2) {
fnName = this.prefix + "_timesFrAffine";
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
this.tm.setBuff(this.pOp1, a);
this.tm.setBuff(this.pOp2, s);
@@ -184,7 +182,7 @@ class WasmCurve {
} else if (b.byteLength == this.F.n8*2) {
return this.op2bool("_eqMixed", a, b);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
} else if (a.byteLength == this.F.n8*2) {
if (b.byteLength == this.F.n8*3) {
@@ -192,10 +190,10 @@ class WasmCurve {
} else if (b.byteLength == this.F.n8*2) {
return this.op2bool("_eqAffine", a, b);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
}
@@ -205,7 +203,7 @@ class WasmCurve {
} else if (a.byteLength == this.F.n8*2) {
return a;
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
}
@@ -215,7 +213,7 @@ class WasmCurve {
} else if (a.byteLength == this.F.n8*2) {
return this.op1("_toJacobian", a);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
}
@@ -224,7 +222,7 @@ class WasmCurve {
if (a.byteLength == this.F.n8*3) {
this.tm.instance.exports[this.prefix + "_toAffine"](this.pOp1, this.pOp1);
} else if (a.byteLength != this.F.n8*2) {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
this.tm.instance.exports[this.prefix + "_LEMtoU"](this.pOp1, this.pOp1);
const res = this.tm.getBuff(this.pOp1, this.F.n8*2);
@@ -238,6 +236,25 @@ class WasmCurve {
return this.tm.getBuff(this.pOp1, this.F.n8*2);
}
toRprCompressed(arr, offset, a) {
this.tm.setBuff(this.pOp1, a);
if (a.byteLength == this.F.n8*3) {
this.tm.instance.exports[this.prefix + "_toAffine"](this.pOp1, this.pOp1);
} else if (a.byteLength != this.F.n8*2) {
throw new Error("invalid point size");
}
this.tm.instance.exports[this.prefix + "_LEMtoC"](this.pOp1, this.pOp1);
const res = this.tm.getBuff(this.pOp1, this.F.n8);
arr.set(res, offset);
}
fromRprCompressed(arr, offset) {
const buff = arr.slice(offset, offset + this.F.n8);
this.tm.setBuff(this.pOp1, buff);
this.tm.instance.exports[this.prefix + "_CtoLEM"](this.pOp1, this.pOp2);
return this.tm.getBuff(this.pOp2, this.F.n8*2);
}
toUncompressed(a) {
const buff = new Uint8Array(this.F.n8*2);
this.toRprUncompressed(buff, 0, a);
@@ -254,7 +271,7 @@ class WasmCurve {
const res = this.tm.getBuff(this.pOp1, this.F.n8*2);
arr.set(res, offset);
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
}
@@ -274,7 +291,7 @@ class WasmCurve {
const y = this.F.toString(a.slice(this.F.n8), radix);
return `[ ${x}, ${y} ]`;
} else {
assert(false, "invalid point size");
throw new Error("invalid point size");
}
}
@@ -352,8 +369,11 @@ class WasmCurve {
}
}
e(a) {
if (a instanceof Uint8Array) return a;
return this.fromObject(a);
}
}
module.exports = WasmCurve;

View File

@@ -1,13 +1,12 @@
const assert = require("assert");
const Scalar = require("./scalar");
const utils = require("./utils");
const {getThreadRng} = require("./random");
const buildBatchConvert = require("./engine_batchconvert");
import * as Scalar from "./scalar.js";
import * as utils from "./utils.js";
import { getThreadRng } from "./random.js";
import buildBatchConvert from "./engine_batchconvert.js";
class WasmField1 {
export default class WasmField1 {
constructor(tm, prefix, n8, p) {
this.tm = tm;
@@ -26,9 +25,9 @@ class WasmField1 {
this.pOp2 = tm.alloc(n8);
this.pOp3 = tm.alloc(n8);
this.tm.instance.exports[prefix + "_zero"](this.pOp1);
this.zero = this.getBuff(this.pOp1);
this.zero = this.tm.getBuff(this.pOp1, this.n8);
this.tm.instance.exports[prefix + "_one"](this.pOp1);
this.one = this.getBuff(this.pOp1);
this.one = this.tm.getBuff(this.pOp1, this.n8);
this.negone = this.neg(this.one);
this.two = this.add(this.one, this.one);
@@ -36,7 +35,9 @@ class WasmField1 {
this.n64 = Math.floor(n8/8);
this.n32 = Math.floor(n8/4);
assert(this.n64*8 == this.n8, "n8 must be a multiple of 8");
if(this.n64*8 != this.n8) {
throw new Error("n8 must be a multiple of 8");
}
this.half = Scalar.shiftRight(this.p, Scalar.one);
this.nqr = this.two;
@@ -62,42 +63,36 @@ class WasmField1 {
this.w[i] = this.square(this.w[i+1]);
}
assert(this.eq(this.w[0], this.one));
if (!this.eq(this.w[0], this.one)) {
throw new Error("Error calculating roots of unity");
}
this.batchToMontgomery = buildBatchConvert(tm, prefix + "_batchToMontgomery", this.n8, this.n8);
this.batchFromMontgomery = buildBatchConvert(tm, prefix + "_batchFromMontgomery", this.n8, this.n8);
}
getBuff(p) {
return this.tm.u8.slice(p, p+this.n8);
}
setBuff(p, buff) {
return this.tm.u8.set(buff, p);
}
op2(opName, a, b) {
this.setBuff(this.pOp1, a);
this.setBuff(this.pOp2, b);
this.tm.setBuff(this.pOp1, a);
this.tm.setBuff(this.pOp2, b);
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2, this.pOp3);
return this.getBuff(this.pOp3);
return this.tm.getBuff(this.pOp3, this.n8);
}
op2Bool(opName, a, b) {
this.setBuff(this.pOp1, a);
this.setBuff(this.pOp2, b);
this.tm.setBuff(this.pOp1, a);
this.tm.setBuff(this.pOp2, b);
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2);
}
op1(opName, a) {
this.setBuff(this.pOp1, a);
this.tm.setBuff(this.pOp1, a);
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
return this.getBuff(this.pOp3);
return this.tm.getBuff(this.pOp3, this.n8);
}
op1Bool(opName, a) {
this.setBuff(this.pOp1, a);
this.tm.setBuff(this.pOp1, a);
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
}
@@ -138,6 +133,14 @@ class WasmField1 {
return this.op2("_mul", a, b);
}
div(a, b) {
this.tm.setBuff(this.pOp1, a);
this.tm.setBuff(this.pOp2, b);
this.tm.instance.exports[this.prefix + "_inverse"](this.pOp2, this.pOp2);
this.tm.instance.exports[this.prefix + "_mul"](this.pOp1, this.pOp2, this.pOp3);
return this.tm.getBuff(this.pOp3, this.n8);
}
square(a) {
return this.op1("_square", a);
}
@@ -227,5 +230,4 @@ class WasmField1 {
}
module.exports = WasmField1;

View File

@@ -1,11 +1,8 @@
import { getThreadRng } from "./random.js";
import * as Scalar from "./scalar.js";
const assert = require("assert");
const {getThreadRng} = require("./random");
const Scalar = require("./scalar");
class WasmField2 {
export default class WasmField2 {
constructor(tm, prefix, F) {
this.tm = tm;
@@ -95,6 +92,14 @@ class WasmField2 {
return this.op2("_mul", a, b);
}
div(a, b) {
this.tm.setBuff(this.pOp1, a);
this.tm.setBuff(this.pOp2, b);
this.tm.instance.exports[this.prefix + "_inverse"](this.pOp2, this.pOp2);
this.tm.instance.exports[this.prefix + "_mul"](this.pOp1, this.pOp2, this.pOp3);
return this.tm.getBuff(this.pOp3, this.n8);
}
square(a) {
return this.op1("_square", a);
}
@@ -127,7 +132,7 @@ class WasmField2 {
res.set(c2, this.F.n8*2);
return res;
} else {
assert(false, "invalid F2");
throw new Error("invalid F2");
}
}
@@ -167,5 +172,3 @@ class WasmField2 {
}
module.exports = WasmField2;

View File

@@ -1,11 +1,10 @@
const assert = require("assert");
const {getThreadRng} = require("./random");
const Scalar = require("./scalar");
import { getThreadRng } from "./random.js";
import * as Scalar from "./scalar.js";
class WasmField3 {
export default class WasmField3 {
constructor(tm, prefix, F) {
this.tm = tm;
@@ -96,6 +95,14 @@ class WasmField3 {
return this.op2("_mul", a, b);
}
div(a, b) {
this.tm.setBuff(this.pOp1, a);
this.tm.setBuff(this.pOp2, b);
this.tm.instance.exports[this.prefix + "_inverse"](this.pOp2, this.pOp2);
this.tm.instance.exports[this.prefix + "_mul"](this.pOp1, this.pOp2, this.pOp3);
return this.tm.getBuff(this.pOp3, this.n8);
}
square(a) {
return this.op1("_square", a);
}
@@ -130,7 +137,7 @@ class WasmField3 {
res.set(c3, this.F.n8*2);
return res;
} else {
assert(false, "invalid F3");
throw new Error("invalid F3");
}
}
@@ -164,7 +171,7 @@ class WasmField3 {
}
fromObject(a) {
const buff = new Uint8Array(this.F.n8*2);
const buff = new Uint8Array(this.F.n8*3);
const b1 = this.F.fromObject(a[0]);
const b2 = this.F.fromObject(a[1]);
const b3 = this.F.fromObject(a[2]);
@@ -176,5 +183,4 @@ class WasmField3 {
}
module.exports = WasmField3;

View File

@@ -17,16 +17,24 @@
zksnark JavaScript library. If not, see <https://www.gnu.org/licenses/>.
*/
const chai = require("chai");
import chai from "chai";
const Scalar = require("../src/scalar.js");
const bn128 = require("../src/bn128.js");
const F1Field = require("../src/f1field.js");
import * as Scalar from "../src/scalar.js";
import buildBn128 from "../src/bn128.js";
import F1Field from "../src/f1field.js";
const assert = chai.assert;
describe("F1 testing", () => {
let bn128;
before( async() => {
bn128 = await buildBn128();
});
after( async() => {
bn128.terminate();
});
it("Should compute euclidean", () => {
const F = new F1Field(Scalar.fromString("7"));
const res = F.inv(F.e(4));
@@ -65,8 +73,16 @@ describe("F1 testing", () => {
});
describe("Curve G1 Test", () => {
let bn128;
before( async() => {
bn128 = await buildBn128();
});
after( async() => {
bn128.terminate();
});
it("r*one == 0", () => {
const res = bn128.G1.mulScalar(bn128.G1.g, bn128.r);
const res = bn128.G1.timesScalar(bn128.G1.g, bn128.r);
assert(bn128.G1.eq(res, bn128.G1.zero), "G1 does not have range r");
});
@@ -76,20 +92,28 @@ describe("Curve G1 Test", () => {
const r1 = bn128.Fr.e(33);
const r2 = bn128.Fr.e(44);
const gr1 = bn128.G1.mulScalar(bn128.G1.g, r1);
const gr2 = bn128.G1.mulScalar(bn128.G1.g, r2);
const gr1 = bn128.G1.timesFr(bn128.G1.g, r1);
const gr2 = bn128.G1.timesFr(bn128.G1.g, r2);
const grsum1 = bn128.G1.add(gr1, gr2);
const grsum2 = bn128.G1.mulScalar(bn128.G1.g, bn128.Fr.add(r1, r2));
const grsum2 = bn128.G1.timesFr(bn128.G1.g, bn128.Fr.add(r1, r2));
assert(bn128.G1.eq(grsum1, grsum2));
});
});
describe("Curve G2 Test", () => {
let bn128;
before( async() => {
bn128 = await buildBn128();
});
after( async() => {
bn128.terminate();
});
it ("r*one == 0", () => {
const res = bn128.G2.mulScalar(bn128.G2.g, bn128.r);
const res = bn128.G2.timesScalar(bn128.G2.g, bn128.r);
assert(bn128.G2.eq(res, bn128.G2.zero), "G2 does not have range r");
});
@@ -98,12 +122,12 @@ describe("Curve G2 Test", () => {
const r1 = bn128.Fr.e(33);
const r2 = bn128.Fr.e(44);
const gr1 = bn128.G2.mulScalar(bn128.G2.g, r1);
const gr2 = bn128.G2.mulScalar(bn128.G2.g, r2);
const gr1 = bn128.G2.timesFr(bn128.G2.g, r1);
const gr2 = bn128.G2.timesFr(bn128.G2.g, r2);
const grsum1 = bn128.G2.add(gr1, gr2);
const grsum2 = bn128.G2.mulScalar(bn128.G2.g, bn128.Fr.add(r1, r2));
const grsum2 = bn128.G2.timesFr(bn128.G2.g, bn128.Fr.add(r1, r2));
/*
console.log(G2.toString(grsum1));
@@ -115,20 +139,26 @@ describe("Curve G2 Test", () => {
});
describe("F6 testing", () => {
let bn128;
before( async() => {
bn128 = await buildBn128();
});
after( async() => {
bn128.terminate();
});
it("Should multiply and divide in F6", () => {
const a =
[
[bn128.F1.e("1"), bn128.F1.e("2")],
[bn128.F1.e("3"), bn128.F1.e("4")],
[bn128.F1.e("5"), bn128.F1.e("6")]
];
const b =
[
[bn128.F1.e("12"), bn128.F1.e("11")],
[bn128.F1.e("10"), bn128.F1.e("9")],
[bn128.F1.e("8"), bn128.F1.e("7")]
];
const a = bn128.F6.fromObject([
[Scalar.e("1"), Scalar.e("2")],
[Scalar.e("3"), Scalar.e("4")],
[Scalar.e("5"), Scalar.e("6")]
]);
const b = bn128.F6.fromObject([
[Scalar.e("12"), Scalar.e("11")],
[Scalar.e("10"), Scalar.e("9")],
[Scalar.e("8"), Scalar.e("7")]
]);
const c = bn128.F6.mul(a,b);
const d = bn128.F6.div(c,b);
@@ -137,33 +167,39 @@ describe("F6 testing", () => {
});
describe("F12 testing", () => {
let bn128;
before( async() => {
bn128 = await buildBn128();
});
after( async() => {
bn128.terminate();
});
it("Should multiply and divide in F12", () => {
const a =
[
const a = bn128.Gt.fromObject([
[
[bn128.F1.e("1"), bn128.F1.e("2")],
[bn128.F1.e("3"), bn128.F1.e("4")],
[bn128.F1.e("5"), bn128.F1.e("6")]
[Scalar.e("1"), Scalar.e("2")],
[Scalar.e("3"), Scalar.e("4")],
[Scalar.e("5"), Scalar.e("6")]
],
[
[bn128.F1.e("7"), bn128.F1.e("8")],
[bn128.F1.e("9"), bn128.F1.e("10")],
[bn128.F1.e("11"), bn128.F1.e("12")]
[Scalar.e("7"), Scalar.e("8")],
[Scalar.e("9"), Scalar.e("10")],
[Scalar.e("11"), Scalar.e("12")]
]
];
const b =
[
]);
const b = bn128.Gt.fromObject([
[
[bn128.F1.e("12"), bn128.F1.e("11")],
[bn128.F1.e("10"), bn128.F1.e("9")],
[bn128.F1.e("8"), bn128.F1.e("7")]
[Scalar.e("12"), Scalar.e("11")],
[Scalar.e("10"), Scalar.e("9")],
[Scalar.e("8"), Scalar.e("7")]
],
[
[bn128.F1.e("6"), bn128.F1.e("5")],
[bn128.F1.e("4"), bn128.F1.e("3")],
[bn128.F1.e("2"), bn128.F1.e("1")]
[Scalar.e("6"), Scalar.e("5")],
[Scalar.e("4"), Scalar.e("3")],
[Scalar.e("2"), Scalar.e("1")]
]
];
]);
const c = bn128.F12.mul(a,b);
const d = bn128.F12.div(c,b);
@@ -172,7 +208,15 @@ describe("F12 testing", () => {
});
describe("Pairing", () => {
/*
let bn128;
before( async() => {
bn128 = await buildBn128();
});
after( async() => {
bn128.terminate();
});
/*
it("Should match pairing", () => {
for (let i=0; i<1; i++) {
const bn128 = new BN128();
@@ -183,10 +227,10 @@ describe("Pairing", () => {
const g1b = bn128.G1.mulScalar(bn128.G1.g, 30);
const g2b = bn128.G2.mulScalar(bn128.G2.g, 25);
const pre1a = bn128.precomputeG1(g1a);
const pre2a = bn128.precomputeG2(g2a);
const pre1b = bn128.precomputeG1(g1b);
const pre2b = bn128.precomputeG2(g2b);
const pre1a = bn128.prepareG1(g1a);
const pre2a = bn128.prepareG2(g2a);
const pre1b = bn128.prepareG1(g1b);
const pre2b = bn128.prepareG2(g2b);
const r1 = bn128.millerLoop(pre1a, pre2a);
const r2 = bn128.millerLoop(pre1b, pre2b);
@@ -198,19 +242,19 @@ describe("Pairing", () => {
assert(bn128.F12.eq(res, bn128.F12.one));
}
}).timeout(10000);
*/
*/
it("Should generate another pairing pairing", () => {
for (let i=0; i<1; i++) {
const g1a = bn128.G1.mulScalar(bn128.G1.g, 10);
const g2a = bn128.G2.mulScalar(bn128.G2.g, 1);
const g1a = bn128.G1.timesScalar(bn128.G1.g, 10);
const g2a = bn128.G2.timesScalar(bn128.G2.g, 1);
const g1b = bn128.G1.mulScalar(bn128.G1.g, 1);
const g2b = bn128.G2.mulScalar(bn128.G2.g, 10);
const g1b = bn128.G1.timesScalar(bn128.G1.g, 1);
const g2b = bn128.G2.timesScalar(bn128.G2.g, 10);
const pre1a = bn128.precomputeG1(g1a);
const pre2a = bn128.precomputeG2(g2a);
const pre1b = bn128.precomputeG1(g1b);
const pre2b = bn128.precomputeG2(g2b);
const pre1a = bn128.prepareG1(g1a);
const pre2a = bn128.prepareG2(g2a);
const pre1b = bn128.prepareG1(g1b);
const pre2b = bn128.prepareG2(g2b);
const r1 = bn128.millerLoop(pre1a, pre2a);
const r2 = bn128.finalExponentiation(r1);
@@ -219,46 +263,62 @@ describe("Pairing", () => {
const r4 = bn128.finalExponentiation(r3);
/*
console.log("ML1: " ,bn128.F12.toString(r1));
console.log("FE1: " ,bn128.F12.toString(r2));
console.log("ML2: " ,bn128.F12.toString(r3));
console.log("FE2: " ,bn128.F12.toString(r4));
*/
assert(bn128.F12.eq(r2, r4));
/* const r2 = bn128.millerLoop(pre1b, pre2b);
/*
const r2 = bn128.millerLoop(pre1b, pre2b);
const rbe = bn128.F12.mul(r1, bn128.F12.inverse(r2));
const res = bn128.finalExponentiation(rbe);
assert(bn128.F12.eq(res, bn128.F12.one)); */
assert(bn128.F12.eq(res, bn128.F12.one));
*/
}
}).timeout(10000);
it("Should test rpr of F2", () => {
const P1 = [
});
describe("Compressed Form", () => {
let bn128;
before( async() => {
bn128 = await buildBn128();
});
after( async() => {
bn128.terminate();
});
it("Should test rpr of G2", () => {
const P1 = bn128.G2.fromObject([
[
bn128.F1.e("1b2327ce7815d3358fe89fd8e5695305ed23682db29569f549ab8f48cae1f1c4",16),
bn128.F1.e("1ed41ca6b3edc06237af648f845c270ff83bcde333f17863c1b71a43b271b46d",16)
Scalar.e("1b2327ce7815d3358fe89fd8e5695305ed23682db29569f549ab8f48cae1f1c4",16),
Scalar.e("1ed41ca6b3edc06237af648f845c270ff83bcde333f17863c1b71a43b271b46d",16)
],
[
bn128.F1.e("122057912ab892abcf2e729f0f342baea3fe1b484840eb97c7d78cd7530f4ab5",16),
bn128.F1.e("2cb317fd40d56eeb17b0c1ff9443661a42ec00cea060012873b3f643f1a5bff8",16)
Scalar.e("122057912ab892abcf2e729f0f342baea3fe1b484840eb97c7d78cd7530f4ab5",16),
Scalar.e("2cb317fd40d56eeb17b0c1ff9443661a42ec00cea060012873b3f643f1a5bff8",16)
],
[
bn128.F1.one,
bn128.F1.zero
Scalar.one,
Scalar.zero
]
];
]);
const buff = new Uint8Array(64);
bn128.G2.toRprCompressed(buff, 0, P1);
const P2 = bn128.G2.fromRprCompressed(buff, 0);
console.log(P1[1][0].toString(16));
console.log(P2[1][0].toString(16));
/*
console.log(bn128.G2.toString(P1, 16));
console.log(bn128.G2.toString(P2, 16));
*/
assert(bn128.G2.eq(P1,P2));
}).timeout(10000);

View File

@@ -1,11 +1,19 @@
const assert = require("assert");
import assert from "assert";
import buildBn128 from "../src/bn128.js";
describe("FFT in G1", async function () {
let bn128;
before( async() => {
bn128 = await buildBn128();
});
after( async() => {
bn128.terminate();
});
describe("bn128 tester", async function () {
this.timeout(100000);
it("It shoud do an inverse FFT in G1", async () => {
const bn128 = require("../index").bn128;
const Fr = bn128.Fr;
const G1 = bn128.G1;
@@ -13,21 +21,19 @@ describe("bn128 tester", async function () {
for (let i=0; i<8; i++) a[i] = Fr.e(i+1);
const aG_expected = [];
for (let i=0; i<8; i++) aG_expected[i] = G1.mulScalar(G1.g, a[i]);
for (let i=0; i<8; i++) aG_expected[i] = G1.timesFr(G1.g, a[i]);
const A = await bn128.PFr.fft(a);
const A = await bn128.Fr.fft(a);
const AG = [];
for (let i=0; i<8; i++) AG[i] = G1.mulScalar(G1.g, A[i]);
for (let i=0; i<8; i++) AG[i] = G1.timesFr(G1.g, A[i]);
const aG_calculated = await G1.ifft(AG);
const aG_calculated = await G1.ifft(AG, "jacobian", "jacobian");
for (let i=0; i<8; i++) {
assert(G1.eq(aG_calculated[i], aG_expected[i]));
}
bn128.engine.terminate();
});
});

View File

@@ -17,11 +17,11 @@
zksnark JavaScript library. If not, see <https://www.gnu.org/licenses/>.
*/
const chai = require("chai");
import chai from "chai";
const Scalar = require("../src/scalar");
const PolField = require("../src/polfield.js");
const ZqField = require("../src/f1field");
import * as Scalar from "../src/scalar.js";
import PolField from "../src/polfield.js";
import ZqField from "../src/f1field.js";
const assert = chai.assert;

View File

@@ -17,11 +17,11 @@
zksnark JavaScript library. If not, see <https://www.gnu.org/licenses/>.
*/
const chai = require("chai");
import chai from "chai";
const Scalar = require("../src/scalar.js");
const ZqField = require("../src/f1field.js");
const RatField = require("../src/ratfield.js");
import * as Scalar from "../src/scalar.js";
import ZqField from "../src/f1field.js";
import RatField from "../src/ratfield.js";
const q = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const Z = new ZqField(q);

View File

@@ -1,7 +1,7 @@
const assert = require("assert");
import assert from "assert";
const ScalarN = require("../src/scalar_native.js");
const ScalarB = require("../src/scalar_bigint.js");
import * as ScalarN from "../src/scalar_native.js";
import * as ScalarB from "../src/scalar_bigint.js";
describe("Basic scalar convertions", () => {
it("Should convertion Native", () => {

View File

@@ -17,16 +17,24 @@
zksnark JavaScript library. If not, see <https://www.gnu.org/licenses/>.
*/
const chai = require("chai");
import chai from "chai";
const Scalar = require("../src/scalar.js");
const bn128 = require("../src/bn128.js");
const F1Field = require("../src/f1field.js");
import * as Scalar from "../src/scalar.js";
import buildBn128 from "../src/bn128.js";
import F1Field from "../src/f1field.js";
const assert = chai.assert;
describe("Sqrt testing", () => {
let bn128;
before( async() => {
bn128 = await buildBn128();
});
after( async() => {
bn128.terminate();
});
/*
it("Should compute sqrts", () => {
const F = new F1Field(bn128.r);
@@ -71,13 +79,13 @@ describe("Sqrt testing", () => {
});
it("Should compute sqrt m=2 p%4 = 3", () => {
const F = bn128.F2;
const e = Scalar.div(Scalar.pow(F.p, F.m), 2);
const e = Scalar.div(Scalar.exp(F.F.p, F.m), 2);
for (let i=0; i<100; i++) {
const x2 = F.random();
const x = F.sqrt(x2);
if (x==null) {
assert(F.eq( F.pow(x2, e), F.negone));
if (!F.isSquare(x2)) {
assert(F.eq( F.exp(x2, e), F.negone));
} else {
const x = F.sqrt(x2);
assert(F.eq(F.square(x), x2));
}
}

View File

@@ -1,10 +1,10 @@
const assert = require("assert");
import assert from "assert";
const ScalarN = require("../src/scalar_native.js");
const ScalarB = require("../src/scalar_bigint.js");
import * as ScalarN from "../src/scalar_native.js";
import * as ScalarB from "../src/scalar_bigint.js";
const utilsN = require("../src/utils_native.js");
const utilsB = require("../src/utils_bigint.js");
import * as utilsN from "../src/utils_native.js";
import * as utilsB from "../src/utils_bigint.js";
describe("Utils native", () => {
const num = ScalarN.e(21888242871839275222246405745257275088614511777268538073601725287587578984328);
@@ -12,21 +12,21 @@ describe("Utils native", () => {
it("Should convert integer to buffer little-endian", () => {
const buff = utilsN.leInt2Buff(num, 32);
const numFromBuff = utilsN.leBuff2int(buff);
assert(ScalarN.eq(num, numFromBuff), true);
});
it("Should convert integer to buffer big-endian", () => {
const buff = utilsN.beInt2Buff(num, 32);
const numFromBuff = utilsN.beBuff2int(buff);
assert(ScalarN.eq(num, numFromBuff), true);
});
it("Should stringify bigInt", () => {
const str = utilsN.stringifyBigInts(num);
const numFromStr = utilsN.unstringifyBigInts(str);
assert(ScalarN.eq(num, numFromStr), true);
});
});
@@ -37,21 +37,21 @@ describe("Utils bigInt", () => {
it("Should convert integer to buffer little-endian", () => {
const buff = utilsB.leInt2Buff(num, 32);
const numFromBuff = utilsB.leBuff2int(buff);
assert(ScalarB.eq(num, numFromBuff), true);
});
it("Should convert integer to buffer big-endian", () => {
const buff = utilsB.beInt2Buff(num, 32);
const numFromBuff = utilsB.beBuff2int(buff);
assert(ScalarB.eq(num, numFromBuff), true);
});
it("Should stringify bigInt", () => {
const str = utilsB.stringifyBigInts(num);
const numFromStr = utilsB.unstringifyBigInts(str);
assert(ScalarB.eq(num, numFromStr), true);
});
});
});

View File

@@ -1,6 +1,6 @@
const ZqField = require("../src/f1field");
const Scalar = require("../src/scalar");
const assert = require("assert");
import ZqField from "../src/f1field.js";
import * as Scalar from "../src/scalar.js";
import assert from "assert";
const q = Scalar.fromString("21888242871839275222246405745257275088696311157297823662689037894645226208583");
const r = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");