mirror of
https://github.com/CryptKeeperZK/ffjavascript.git
synced 2026-05-03 03:00:11 -04:00
link with wasmsnark
This commit is contained in:
123
src/bn128.js
123
src/bn128.js
@@ -21,6 +21,7 @@ const Scalar = require("./scalar");
|
||||
const F1Field = require("./f1field");
|
||||
const F2Field = require("./f2field");
|
||||
const F3Field = require("./f3field");
|
||||
const PolField = require("./polfield");
|
||||
const EC = require("./ec.js");
|
||||
const buildEngine = require("./engine");
|
||||
const bn128_wasm = require("wasmsnark").bn128_wasm;
|
||||
@@ -69,7 +70,7 @@ class BN128 {
|
||||
this.F6 = new F3Field(this.F2, this.nonResidueF6);
|
||||
this.F12 = new F2Field(this.F6, this.nonResidueF6);
|
||||
this.Fr = new F1Field(this.r);
|
||||
|
||||
this.PFr = new PolField(this.Fr);
|
||||
|
||||
|
||||
const self = this;
|
||||
@@ -85,11 +86,27 @@ class BN128 {
|
||||
this.G2.batchLEMtoU = this.batchLEMtoUG2.bind(this);
|
||||
this.G1.batchLEMtoC = this.batchLEMtoCG1.bind(this);
|
||||
this.G2.batchLEMtoC = this.batchLEMtoCG2.bind(this);
|
||||
this.G1.batchUtoLEM = this.batchUtoLEMG1.bind(this);
|
||||
this.G2.batchUtoLEM = this.batchUtoLEMG2.bind(this);
|
||||
this.G1.batchCtoLEM = this.batchCtoLEMG1.bind(this);
|
||||
this.G2.batchCtoLEM = this.batchCtoLEMG2.bind(this);
|
||||
this.G1.ifft = this.ifftG1.bind(this);
|
||||
this.G1.fft = this.fftG1.bind(this);
|
||||
this.G2.ifft = this.ifftG2.bind(this);
|
||||
this.G2.fft = this.fftG2.bind(this);
|
||||
|
||||
this.Fr.batchToMontgomery = this.batchToMontgomeryFr.bind(this);
|
||||
this.Fr.batchFromMontgomery = this.batchFromMontgomeryFr.bind(this);
|
||||
this.Fr.fft = this.fftFr.bind(this);
|
||||
this.Fr.ifft = this.ifftFr.bind(this);
|
||||
|
||||
this.G1.multiExpAffine = this.multiExpAffineG1.bind(this);
|
||||
this.G2.multiExpAffine = this.multiExpAffineG2.bind(this);
|
||||
}
|
||||
|
||||
async loadEngine() {
|
||||
if (!engine) {
|
||||
engine = await buildEngine(this, bn128_wasm, true);
|
||||
engine = await buildEngine(this, bn128_wasm /* , true */ ); // Set single Trherad tot true to debug
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,27 +124,121 @@ class BN128 {
|
||||
|
||||
async batchLEMtoUG1(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("G1", "LEM", "U", buff );
|
||||
// const res = await engine.batchConvert("G1", "LEM", "U", buff );
|
||||
const res = await engine.batchConvert("g1m_batchLEMtoU", buff, this.F1.n8*2, this.F1.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoUG2(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("G2", "LEM", "U",buff);
|
||||
// const res = await engine.batchConvert("G2", "LEM", "U",buff);
|
||||
const res = await engine.batchConvert("g2m_batchLEMtoU", buff, this.F2.n8*2, this.F2.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoCG1(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("G1", "LEM", "C", buff);
|
||||
// const res = await engine.batchConvert("G1", "LEM", "C", buff);
|
||||
const res = await engine.batchConvert("g1m_batchLEMtoC", buff, this.F1.n8*2, this.F1.n8);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoCG2(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("G2", "LEM", "C", buff);
|
||||
// const res = await engine.batchConvert("G2", "LEM", "C", buff);
|
||||
const res = await engine.batchConvert("g2m_batchLEMtoC", buff, this.F2.n8*2, this.F2.n8);
|
||||
return res;
|
||||
}
|
||||
/////
|
||||
|
||||
async batchUtoLEMG1(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G1", "LEM", "U", buff );
|
||||
const res = await engine.batchConvert("g1m_batchUtoLEM", buff, this.F1.n8*2, this.F1.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchUtoLEMG2(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G2", "LEM", "U",buff);
|
||||
const res = await engine.batchConvert("g2m_batchUtoLEM", buff, this.F2.n8*2, this.F2.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchCtoLEMG1(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G1", "LEM", "C", buff);
|
||||
const res = await engine.batchConvert("g1m_batchCtoLEM", buff, this.F1.n8, this.F1.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchCtoLEMG2(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G2", "LEM", "C", buff);
|
||||
const res = await engine.batchConvert("g2m_batchCtoLEM", buff, this.F2.n8, this.F2.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchToMontgomeryFr(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("frm_batchToMontgomery", buff, this.Fr.n8, this.Fr.n8);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchFromMontgomeryFr(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("frm_batchFromMontgomery", buff, this.Fr.n8, this.Fr.n8);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftG1(buff, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("G1", buff, false, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async ifftG1(buff, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("G1", buff, true, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftG2(buff, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("G2", buff, false, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async ifftG2(buff, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("G2", buff, true, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftFr(buff, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("Fr", buff, false, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async ifftFr(buff, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("Fr", buff, true, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async multiExpAffineG1(buffBases, buffScalars) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.multiExpAffine("G1", buffBases, buffScalars);
|
||||
return res;
|
||||
}
|
||||
|
||||
async multiExpAffineG2(buffBases, buffScalars) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.multiExpAffine("G2", buffBases, buffScalars);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
_preparePairing() {
|
||||
this.loopCount = Scalar.fromString("29793968203157093288");// CONSTANT
|
||||
|
||||
15
src/ec.js
15
src/ec.js
@@ -340,6 +340,17 @@ class EC {
|
||||
return [x, y, this.F.one];
|
||||
}
|
||||
|
||||
fromRprLEJM(buff, o) {
|
||||
o = o || 0;
|
||||
const x = this.F.fromRprLEM(buff, o);
|
||||
const y = this.F.fromRprLEM(buff, o+this.F.n8);
|
||||
const z = this.F.fromRprLEM(buff, o+this.F.n8*2);
|
||||
if (this.F.isZero(x) && this.F.isZero(y)) {
|
||||
return this.zero;
|
||||
}
|
||||
return [x, y, z];
|
||||
}
|
||||
|
||||
fromRprBEM(buff, o) {
|
||||
o = o || 0;
|
||||
const x = this.F.fromRprBEM(buff, o);
|
||||
@@ -352,7 +363,7 @@ class EC {
|
||||
|
||||
fromRprCompressed(buff, o) {
|
||||
const F = this.F;
|
||||
const v = new Uint8Array(buff, o, F.n8);
|
||||
const v = new Uint8Array(buff.buffer, o, F.n8);
|
||||
if (v[0] & 0x40) return this.zero;
|
||||
const P = new Array(3);
|
||||
|
||||
@@ -377,7 +388,7 @@ class EC {
|
||||
|
||||
toRprCompressed(buff, o, p) {
|
||||
p = this.affine(p);
|
||||
const v = new Uint8Array(buff, o, this.F.n8);
|
||||
const v = new Uint8Array(buff.buffer, o, this.F.n8);
|
||||
if (this.isZero(p)) {
|
||||
v.fill(0);
|
||||
v[0] = 0x40;
|
||||
|
||||
421
src/engine.js
421
src/engine.js
@@ -23,6 +23,7 @@ const MEM_SIZE = 4096; // Memory size in 64K Pakes (256Mb)
|
||||
|
||||
const assert = require("assert");
|
||||
const thread = require("./engine_thread");
|
||||
const FFT = require("./fft");
|
||||
|
||||
const inBrowser = (typeof window !== "undefined");
|
||||
let NodeWorker;
|
||||
@@ -76,11 +77,11 @@ async function buildEngine(curve, wasm, singleThread) {
|
||||
if (singleThread) {
|
||||
engine.code = wasm.code;
|
||||
engine.taskManager = thread();
|
||||
await engine.taskManager({
|
||||
command: "INIT",
|
||||
await engine.taskManager([{
|
||||
cmd: "INIT",
|
||||
init: MEM_SIZE,
|
||||
code: wasm.code
|
||||
});
|
||||
}]);
|
||||
engine.concurrency = 1;
|
||||
} else {
|
||||
engine.workers = [];
|
||||
@@ -94,6 +95,7 @@ async function buildEngine(curve, wasm, singleThread) {
|
||||
} else {
|
||||
concurrency = 8;
|
||||
}
|
||||
engine.concurrency = concurrency;
|
||||
|
||||
for (let i = 0; i<concurrency; i++) {
|
||||
|
||||
@@ -117,12 +119,11 @@ async function buildEngine(curve, wasm, singleThread) {
|
||||
const initPromises = [];
|
||||
for (let i=0; i<engine.workers.length;i++) {
|
||||
const copyCode = wasm.code.buffer.slice(0);
|
||||
initPromises.push(engine.postAction(i, {
|
||||
command: "INIT",
|
||||
initPromises.push(engine.postAction(i, [{
|
||||
cmd: "INIT",
|
||||
init: MEM_SIZE,
|
||||
code: copyCode
|
||||
|
||||
}, [copyCode]));
|
||||
}], [copyCode]));
|
||||
}
|
||||
|
||||
await Promise.all(initPromises);
|
||||
@@ -174,7 +175,7 @@ class Engine {
|
||||
queueAction(actionData, transfers) {
|
||||
if (this.singleThread) {
|
||||
const res = this.taskManager(actionData);
|
||||
return res[0];
|
||||
return res;
|
||||
} else {
|
||||
const d = new Deferred();
|
||||
this.actionQueue.push({
|
||||
@@ -294,37 +295,52 @@ class Engine {
|
||||
|
||||
terminate() {
|
||||
for (let i=0; i<this.workers.length; i++) {
|
||||
this.workers[i].postMessage({command: "TERMINATE"});
|
||||
this.workers[i].postMessage([{cmd: "TERMINATE"}]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async batchApplyKey(Gs, buff, first, inc) {
|
||||
async batchApplyKey(groupName, buff, first, inc) {
|
||||
const self = this;
|
||||
const G = self.curve[Gs];
|
||||
const G = self.curve[groupName];
|
||||
const Fr = self.curve.Fr;
|
||||
const sG = G.F.n8*2;
|
||||
let fnName;
|
||||
if (groupName == "G1") {
|
||||
fnName = "g1m_batchApplyKey";
|
||||
} else if (groupName == "G2") {
|
||||
fnName = "g2m_batchApplyKey";
|
||||
} else {
|
||||
throw new Error("Invalid group: " + groupName);
|
||||
}
|
||||
const nPoints = Math.floor(buff.byteLength / sG);
|
||||
const pointsPerChunk = Math.floor(nPoints/self.concurrency);
|
||||
const opPromises = [];
|
||||
const bInc = new Uint8Array(Fr.n8);
|
||||
Fr.toRprLEM(bInc.buffer, 0, inc);
|
||||
Fr.toRprLEM(bInc, 0, inc);
|
||||
let t = Fr.e(first);
|
||||
for (let i=0; i<self.concurrency; i++) {
|
||||
const n = Math.min(nPoints - i, pointsPerChunk);
|
||||
let n;
|
||||
if (i< self.concurrency-1) {
|
||||
n = pointsPerChunk;
|
||||
} else {
|
||||
n = nPoints - i*pointsPerChunk;
|
||||
}
|
||||
if (n==0) continue;
|
||||
const bFirst = new Uint8Array(Fr.n8);
|
||||
Fr.toRprLEM(bFirst.buffer, 0, t);
|
||||
Fr.toRprLEM(bFirst, 0, t);
|
||||
|
||||
opPromises.push(
|
||||
self.queueAction({
|
||||
command: "BATCH_APPLY_KEY",
|
||||
Gs: Gs,
|
||||
buff: buff.slice(i*pointsPerChunk*sG, i*pointsPerChunk*sG + n*sG),
|
||||
first: bFirst,
|
||||
inc: bInc,
|
||||
n: n
|
||||
})
|
||||
self.queueAction([
|
||||
{cmd: "ALLOCSET", var: 0, buff: buff.slice(i*pointsPerChunk*sG, i*pointsPerChunk*sG + n*sG)},
|
||||
{cmd: "ALLOCSET", var: 1, buff: bFirst},
|
||||
{cmd: "ALLOCSET", var: 2, buff: bInc},
|
||||
{cmd: "ALLOC", var: 3, len: n*sG},
|
||||
{cmd: "CALL", fnName: fnName, params: [
|
||||
{var: 0}, {val: n}, {var: 1}, {var: 2}, {var:3}
|
||||
]},
|
||||
{cmd: "GET", out: 0, var: 3, len: n*sG},
|
||||
])
|
||||
);
|
||||
t = Fr.mul(t, Fr.pow(inc, n));
|
||||
}
|
||||
@@ -332,67 +348,358 @@ class Engine {
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
const outBuff = new Uint8Array(buff.byteLength);
|
||||
for (let i=0; i<self.concurrency; i++) {
|
||||
const n = Math.min(nPoints - i, pointsPerChunk);
|
||||
if (n==0) continue;
|
||||
outBuff.set(result[i], i*pointsPerChunk*sG);
|
||||
let p=0;
|
||||
for (let i=0; i<result.length; i++) {
|
||||
outBuff.set(result[i][0], p);
|
||||
p += result[i][0].byteLength;
|
||||
}
|
||||
|
||||
return outBuff;
|
||||
}
|
||||
|
||||
|
||||
async batchConvert(Gs, fr, to, fullBuffIn) {
|
||||
array2bufferG(Gs, arr) {
|
||||
const self = this;
|
||||
const G = self.curve[Gs];
|
||||
let sGin;
|
||||
if (["LEM", "U"].indexOf(fr)>=0) {
|
||||
sGin = G.F.n8*2;
|
||||
} else if (["C"].indexOf(fr)>=0) {
|
||||
sGin = G.F.n8;
|
||||
} else {
|
||||
throw new Error("Invaid convertion format: "+fr);
|
||||
const sG = G.F.n8*2;
|
||||
|
||||
const buff = new Uint8Array(sG*arr.length);
|
||||
|
||||
for (let i=0; i<arr.length; i++) {
|
||||
G.toRprLEM(buff, i*sG, arr[i]);
|
||||
}
|
||||
let sGout;
|
||||
if (["LEM", "U"].indexOf(fr)>=0) {
|
||||
sGout = G.F.n8*2;
|
||||
} else if (["C"].indexOf(fr)>=0) {
|
||||
sGout = G.F.n8;
|
||||
} else {
|
||||
throw new Error("Invaid convertion format: "+fr);
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
buffer2arrayG(Gs, buff) {
|
||||
const self = this;
|
||||
const G = self.curve[Gs];
|
||||
const sG = G.F.n8*2;
|
||||
|
||||
const n= buff.length / sG;
|
||||
const arr = new Array(n);
|
||||
for (let i=0; i<n; i++) {
|
||||
const P = G.fromRprLEM(buff, i*sG);
|
||||
arr[i] = P;
|
||||
}
|
||||
const nPoints = Math.floor(fullBuffIn.byteLength / sGin);
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
async fft(groupName, buff, inverse, log) {
|
||||
const self = this;
|
||||
const MAX_BITS_THREAD = 12;
|
||||
const G = self.curve[groupName];
|
||||
const Fr = self.curve.Fr;
|
||||
const PFr = new FFT(G, self.curve.Fr, G.mulScalar.bind(G));
|
||||
|
||||
let sIn, sMid, sOut, fnIn2Mid, fnMid2Out, fnName, fnFFT, fnFFTJoin, fnFFTFinal;
|
||||
if (groupName == "G1") {
|
||||
sIn = G.F.n8*2;
|
||||
sMid = G.F.n8*3;
|
||||
sOut = G.F.n8*2;
|
||||
fnIn2Mid = "g1m_batchToJacobian";
|
||||
if (inverse) {
|
||||
fnName = "g1m_ifft";
|
||||
fnFFTFinal = "g1m_fftFinal";
|
||||
} else {
|
||||
fnName = "g1m_fft";
|
||||
}
|
||||
fnFFT = "g1m_fft";
|
||||
fnFFTJoin = "g1m_fftJoin";
|
||||
fnMid2Out = "g1m_batchToAffine";
|
||||
} else if (groupName == "G2") {
|
||||
sIn = G.F.n8*2;
|
||||
sMid = G.F.n8*3;
|
||||
sOut = G.F.n8*2;
|
||||
fnIn2Mid = "g2m_batchToJacobian";
|
||||
if (inverse) {
|
||||
fnName = "g2m_ifft";
|
||||
fnFFTFinal = "g2m_fftFinal";
|
||||
} else {
|
||||
fnName = "g2m_fft";
|
||||
}
|
||||
fnFFT = "g2m_fft";
|
||||
fnFFTJoin = "g2m_fftJoin";
|
||||
fnMid2Out = "g2m_batchToAffine";
|
||||
} else if (groupName == "Fr") {
|
||||
sIn = G.n8;
|
||||
sMid = G.n8;
|
||||
sOut = G.n8;
|
||||
if (inverse) {
|
||||
fnName = "frm_ifft";
|
||||
fnFFTFinal = "frm_fftFinal";
|
||||
} else {
|
||||
fnName = "frm_fft";
|
||||
}
|
||||
fnFFT = "frm_fft";
|
||||
fnFFTJoin = "frm_fftJoin";
|
||||
}
|
||||
|
||||
|
||||
|
||||
let returnArray = false;
|
||||
if (Array.isArray(buff)) {
|
||||
buff = self.array2bufferG(groupName, buff);
|
||||
returnArray = true;
|
||||
}
|
||||
|
||||
const nPoints = buff.byteLength / sIn;
|
||||
const bits = log2(nPoints);
|
||||
|
||||
assert( (1 << bits) == nPoints, "fft must be ultiple of 2" );
|
||||
|
||||
let bInv;
|
||||
if (inverse) {
|
||||
bInv = new Uint8Array(Fr.n8);
|
||||
Fr.toRprLEM(bInv, 0, Fr.inv(Fr.e(nPoints)));
|
||||
}
|
||||
|
||||
|
||||
let buffOut;
|
||||
|
||||
if (nPoints <= (1 << MAX_BITS_THREAD)) {
|
||||
const task = [];
|
||||
task.push({cmd: "ALLOC", var: 0, len: sMid*nPoints});
|
||||
task.push({cmd: "SET", var: 0, buff: buff});
|
||||
if (fnIn2Mid) {
|
||||
task.push({cmd: "CALL", fnName:fnIn2Mid, params: [{var:0}, {val: nPoints}, {var: 0}]});
|
||||
}
|
||||
task.push({cmd: "CALL", fnName:fnName, params: [{var:0}, {val: nPoints}]});
|
||||
if (fnMid2Out) {
|
||||
task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:0}, {val: nPoints}, {var: 0}]});
|
||||
}
|
||||
task.push({cmd: "GET", out: 0, var: 0, len: sOut*nPoints});
|
||||
|
||||
const res = await self.queueAction(task);
|
||||
buffOut = res[0];
|
||||
} else {
|
||||
|
||||
let chunks;
|
||||
const pointsInChunk = 1 << MAX_BITS_THREAD;
|
||||
const chunkSize = pointsInChunk * sIn;
|
||||
const nChunks = nPoints / pointsInChunk;
|
||||
|
||||
const promises = [];
|
||||
for (let i = 0; i< nChunks; i++) {
|
||||
const task = [];
|
||||
task.push({cmd: "ALLOC", var: 0, len: sMid*pointsInChunk});
|
||||
const buffChunk = buff.slice( (pointsInChunk * i)*sIn, (pointsInChunk * (i+1))*sIn);
|
||||
task.push({cmd: "SET", var: 0, buff: buffChunk});
|
||||
if (fnIn2Mid) {
|
||||
task.push({cmd: "CALL", fnName:fnIn2Mid, params: [{var:0}, {val: pointsInChunk}, {var: 0}]});
|
||||
}
|
||||
task.push({cmd: "CALL", fnName:fnFFT, params: [{var:0}, {val: pointsInChunk}]});
|
||||
task.push({cmd: "GET", out:0, var: 0, len: sMid*pointsInChunk});
|
||||
promises.push(self.queueAction(task).then( (r) => {
|
||||
if (log) log(`fft: ${i}/${nChunks}`);
|
||||
return r;
|
||||
}));
|
||||
}
|
||||
|
||||
chunks = await Promise.all(promises);
|
||||
for (let i = 0; i< nChunks; i++) chunks[i] = chunks[i][0];
|
||||
|
||||
for (let i = MAX_BITS_THREAD; i<bits; i++) {
|
||||
if (log) log(`${i}/${bits}`);
|
||||
const nGroups = 1 << (bits - i -1);
|
||||
const nChunksPerGroup = nChunks / nGroups;
|
||||
const opPromises = [];
|
||||
for (let j=0; j<nGroups; j++) {
|
||||
for (let k=0; k <nChunksPerGroup/2; k++) {
|
||||
const bFirst = new Uint8Array(Fr.n8);
|
||||
Fr.toRprLEM(bFirst, 0, Fr.pow( PFr.w[i+1], k*pointsInChunk));
|
||||
const bInc = new Uint8Array(Fr.n8);
|
||||
Fr.toRprLEM(bInc, 0, PFr.w[i+1]);
|
||||
const o1 = j*nChunksPerGroup + k;
|
||||
const o2 = j*nChunksPerGroup + k + nChunksPerGroup/2;
|
||||
|
||||
const task = [];
|
||||
task.push({cmd: "ALLOCSET", var: 0, buff: chunks[o1]});
|
||||
task.push({cmd: "ALLOCSET", var: 1, buff: chunks[o2]});
|
||||
task.push({cmd: "ALLOCSET", var: 2, buff: bFirst});
|
||||
task.push({cmd: "ALLOCSET", var: 3, buff: bInc});
|
||||
task.push({cmd: "CALL", fnName: fnFFTJoin, params:[
|
||||
{var: 0},
|
||||
{var: 1},
|
||||
{val: pointsInChunk},
|
||||
{var: 2},
|
||||
{var: 3}
|
||||
]});
|
||||
if (i==bits-1) {
|
||||
if (fnFFTFinal) {
|
||||
task.push({cmd: "ALLOCSET", var: 4, buff: bInv});
|
||||
task.push({cmd: "CALL", fnName: fnFFTFinal, params:[
|
||||
{var: 0},
|
||||
{val: pointsInChunk},
|
||||
{var: 4},
|
||||
]});
|
||||
task.push({cmd: "CALL", fnName: fnFFTFinal, params:[
|
||||
{var: 1},
|
||||
{val: pointsInChunk},
|
||||
{var: 4},
|
||||
]});
|
||||
}
|
||||
if (fnMid2Out) {
|
||||
task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:0}, {val: pointsInChunk}, {var: 0}]});
|
||||
task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:1}, {val: pointsInChunk}, {var: 1}]});
|
||||
}
|
||||
task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sOut});
|
||||
task.push({cmd: "GET", out: 1, var: 1, len: pointsInChunk*sOut});
|
||||
} else {
|
||||
task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sMid});
|
||||
task.push({cmd: "GET", out: 1, var: 1, len: pointsInChunk*sMid});
|
||||
}
|
||||
opPromises.push(self.queueAction(task));
|
||||
}
|
||||
}
|
||||
|
||||
const res = await Promise.all(opPromises);
|
||||
for (let j=0; j<nGroups; j++) {
|
||||
for (let k=0; k <nChunksPerGroup/2; k++) {
|
||||
const o1 = j*nChunksPerGroup + k;
|
||||
const o2 = j*nChunksPerGroup + k + nChunksPerGroup/2;
|
||||
const resChunk = res.shift();
|
||||
chunks[o1] = resChunk[0];
|
||||
chunks[o2] = resChunk[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffOut = new Uint8Array(nPoints * sOut);
|
||||
if (inverse) {
|
||||
buffOut.set(chunks[0].slice((pointsInChunk-1)*sOut));
|
||||
let p= sOut;
|
||||
for (let i=nChunks-1; i>0; i--) {
|
||||
buffOut.set(chunks[i], p);
|
||||
p += chunkSize;
|
||||
delete chunks[i]; // Liberate mem
|
||||
}
|
||||
buffOut.set(chunks[0].slice(0, (pointsInChunk-1)*sOut), p);
|
||||
delete chunks[nChunks-1];
|
||||
} else {
|
||||
for (let i=0; i<nChunks; i++) {
|
||||
buffOut.set(chunks[i], pointsInChunk*sOut*i);
|
||||
delete chunks[i];
|
||||
}
|
||||
return buffOut;
|
||||
}
|
||||
}
|
||||
|
||||
if (returnArray) {
|
||||
const arr = self.buffer2arrayG(groupName, buffOut);
|
||||
return arr;
|
||||
} else {
|
||||
return buffOut;
|
||||
}
|
||||
|
||||
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 ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async multiExpAffine(groupName, buffBases, buffScalars) {
|
||||
const self = this;
|
||||
const G = self.curve[groupName];
|
||||
const sBase = G.F.n8*2;
|
||||
const sScalar = self.curve.Fr.n8;
|
||||
const nPoints = Math.floor(buffBases.byteLength / sBase);
|
||||
assert( nPoints * sBase === buffBases.byteLength);
|
||||
assert( nPoints * sScalar === buffScalars.byteLength);
|
||||
const pointsPerChunk = Math.floor(nPoints/self.concurrency);
|
||||
const opPromises = [];
|
||||
let fnName;
|
||||
if (groupName == "G1") {
|
||||
fnName = "g1m_multiexpAffine";
|
||||
} else if (groupName == "G2") {
|
||||
fnName = "g2m_multiexpAffine";
|
||||
}
|
||||
for (let i=0; i<self.concurrency; i++) {
|
||||
const n = Math.min(nPoints - i, pointsPerChunk);
|
||||
let n;
|
||||
if (i< self.concurrency-1) {
|
||||
n = pointsPerChunk;
|
||||
} else {
|
||||
n = nPoints - i*pointsPerChunk;
|
||||
}
|
||||
if (n==0) continue;
|
||||
|
||||
const buffBasesChunk = buffBases.slice(i*pointsPerChunk*sBase, i*pointsPerChunk*sBase + n*sBase);
|
||||
const buffScalarsChunk = buffScalars.slice(i*pointsPerChunk*sScalar, i*pointsPerChunk*sScalar + n*sScalar);
|
||||
const task = [
|
||||
{cmd: "ALLOCSET", var: 0, buff: buffBasesChunk},
|
||||
{cmd: "ALLOCSET", var: 1, buff: buffScalarsChunk},
|
||||
{cmd: "ALLOC", var: 2, len: G.F.n8*3},
|
||||
{cmd: "CALL", fnName: fnName, params: [
|
||||
{var: 0},
|
||||
{var: 1},
|
||||
{val: sScalar},
|
||||
{val: nPoints},
|
||||
{var: 2}
|
||||
]},
|
||||
{cmd: "GET", out: 0, var: 2, len: G.F.n8*3}
|
||||
];
|
||||
opPromises.push(
|
||||
self.queueAction({
|
||||
command: "BATCH_CONVERT",
|
||||
Gs: Gs,
|
||||
fr: fr,
|
||||
sGin: sGin,
|
||||
sGout: sGout,
|
||||
to: to,
|
||||
buff: fullBuffIn.slice(i*pointsPerChunk*sGin, i*pointsPerChunk*sGin + n*sGin),
|
||||
n: n
|
||||
})
|
||||
self.queueAction(task)
|
||||
);
|
||||
}
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
const fullBuffOut = new Uint8Array(nPoints*sGout);
|
||||
let res = G.zero;
|
||||
for (let i=0; i<result.length; i++) {
|
||||
const P = G.fromRprLEJM(result[i][0], 0);
|
||||
res = G.add(res, P);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchConvert(fnName, buffIn, sIn, sOut) {
|
||||
const self = this;
|
||||
const nPoints = Math.floor(buffIn.byteLength / sIn);
|
||||
assert( nPoints * sIn === buffIn.byteLength);
|
||||
const pointsPerChunk = Math.floor(nPoints/self.concurrency);
|
||||
const opPromises = [];
|
||||
for (let i=0; i<self.concurrency; i++) {
|
||||
const n = Math.min(nPoints - i, pointsPerChunk);
|
||||
let n;
|
||||
if (i< self.concurrency-1) {
|
||||
n = pointsPerChunk;
|
||||
} else {
|
||||
n = nPoints - i*pointsPerChunk;
|
||||
}
|
||||
if (n==0) continue;
|
||||
fullBuffOut.set(result[i], i*pointsPerChunk*sGout);
|
||||
|
||||
const buffChunk = buffIn.slice(i*pointsPerChunk*sIn, i*pointsPerChunk*sIn + n*sIn);
|
||||
const task = [
|
||||
{cmd: "ALLOCSET", var: 0, buff:buffChunk},
|
||||
{cmd: "ALLOC", var: 1, len:sOut * n},
|
||||
{cmd: "CALL", fnName: fnName, params: [
|
||||
{var: 0},
|
||||
{val: n},
|
||||
{var: 1}
|
||||
]},
|
||||
{cmd: "GET", out: 0, var: 1, len:sOut * n},
|
||||
];
|
||||
opPromises.push(
|
||||
self.queueAction(task)
|
||||
);
|
||||
}
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
const fullBuffOut = new Uint8Array(nPoints*sOut);
|
||||
let p =0;
|
||||
for (let i=0; i<result.length; i++) {
|
||||
fullBuffOut.set(result[i][0], p);
|
||||
p+=result[i][0].byteLength;
|
||||
}
|
||||
|
||||
return fullBuffOut;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,15 +15,15 @@ module.exports = function thread(self) {
|
||||
data = e;
|
||||
}
|
||||
|
||||
if (data.command == "INIT") {
|
||||
init(data).then(function() {
|
||||
if (data[0].cmd == "INIT") {
|
||||
init(data[0]).then(function() {
|
||||
self.postMessage(data.result);
|
||||
});
|
||||
} else if (data.command == "TERMINATE") {
|
||||
} else if (data[0].cmd == "TERMINATE") {
|
||||
process.exit();
|
||||
} else {
|
||||
const res = taskManager(data);
|
||||
self.postMessage(res[0], [res[1]]);
|
||||
const res = runTask(data);
|
||||
self.postMessage(res);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -68,6 +68,51 @@ module.exports = function thread(self) {
|
||||
u8.set(new Uint8Array(buffer), pointer);
|
||||
}
|
||||
|
||||
function runTask(task) {
|
||||
const self=this;
|
||||
if (task[0].cmd == "INIT") {
|
||||
return init(task[0]);
|
||||
}
|
||||
const ctx = {
|
||||
vars: [],
|
||||
out: []
|
||||
};
|
||||
const oldAlloc = u32[0];
|
||||
for (let i=0; i<task.length; i++) {
|
||||
switch (task[i].cmd) {
|
||||
case "ALLOCSET":
|
||||
ctx.vars[task[i].var] = allocBuffer(task[i].buff);
|
||||
break;
|
||||
case "ALLOC":
|
||||
ctx.vars[task[i].var] = alloc(task[i].len);
|
||||
break;
|
||||
case "SET":
|
||||
setBuffer(ctx.vars[task[i].var], task[i].buff);
|
||||
break;
|
||||
case "CALL": {
|
||||
const params = [];
|
||||
for (let j=0; j<task[i].params.length; j++) {
|
||||
const p = task[i].params[j];
|
||||
if (typeof p.var !== "undefined") {
|
||||
params.push(ctx.vars[p.var]);
|
||||
} else if (typeof p.val != "undefined") {
|
||||
params.push(p.val);
|
||||
}
|
||||
}
|
||||
instance.exports[task[i].fnName](...params);
|
||||
break;
|
||||
}
|
||||
case "GET":
|
||||
ctx.out[task[i].out] = getBuffer(ctx.vars[task[i].var], task[i].len).slice();
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid cmd");
|
||||
}
|
||||
}
|
||||
u32[0] = oldAlloc;
|
||||
return ctx.out;
|
||||
}
|
||||
|
||||
function batchApplyKey(task) {
|
||||
const outBuffLen = task.buff.byteLength;
|
||||
const oldAlloc = u32[0];
|
||||
@@ -83,12 +128,25 @@ module.exports = function thread(self) {
|
||||
|
||||
const outBuff = getBuffer(pBuffOut, outBuffLen).slice();
|
||||
u32[0] = oldAlloc;
|
||||
return [ outBuff, outBuff];
|
||||
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);
|
||||
@@ -122,9 +180,62 @@ module.exports = function thread(self) {
|
||||
|
||||
const outBuff = getBuffer(pBuffOut, outBuffLen).slice();
|
||||
u32[0] = oldAlloc;
|
||||
return [ outBuff, outBuff];
|
||||
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);
|
||||
@@ -132,11 +243,15 @@ module.exports = function thread(self) {
|
||||
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 taskManager;
|
||||
return runTask;
|
||||
};
|
||||
|
||||
146
src/fft.js
Normal file
146
src/fft.js
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
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 ].
|
||||
*/
|
||||
|
||||
const assert = require("assert");
|
||||
class FFT {
|
||||
constructor (G, F, opMulGF) {
|
||||
this.F = F;
|
||||
this.G = G;
|
||||
this.opMulGF = opMulGF;
|
||||
|
||||
let rem = F.sqrt_t;
|
||||
let s = F.sqrt_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(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(p.length-1)+1;
|
||||
this._setRoots(bits);
|
||||
|
||||
const m = 1 << bits;
|
||||
assert (p.length == m);
|
||||
const res = __fft(this, p, bits, 0, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
ifft(p) {
|
||||
|
||||
if (p.length <= 1) return p;
|
||||
const bits = log2(p.length-1)+1;
|
||||
this._setRoots(bits);
|
||||
const m = 1 << bits;
|
||||
assert (p.length == m);
|
||||
const res = __ffti(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( 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;
|
||||
}
|
||||
|
||||
|
||||
module.exports = FFT;
|
||||
@@ -71,7 +71,6 @@ class PolField {
|
||||
rootsi[j] = r;
|
||||
r = this.F.mul(r, this.w[i]);
|
||||
}
|
||||
|
||||
this.roots[i] = rootsi;
|
||||
}
|
||||
}
|
||||
@@ -262,7 +261,6 @@ class PolField {
|
||||
|
||||
}
|
||||
|
||||
|
||||
_fft(pall, bits, offset, step) {
|
||||
|
||||
const n = 1 << bits;
|
||||
@@ -523,5 +521,4 @@ function __fft(PF, pall, bits, offset, step) {
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
module.exports = PolField;
|
||||
|
||||
@@ -14,7 +14,7 @@ if (supportsNativeBigInt) {
|
||||
// Returns a buffer with Little Endian Representation
|
||||
Scalar.__proto__.toRprLE = function rprBE(buff, o, e, n8) {
|
||||
const s = "0000000" + e.toString(16);
|
||||
const v = new Uint32Array(buff, o, n8/4);
|
||||
const v = new Uint32Array(buff.buffer, 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;
|
||||
@@ -23,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) {
|
||||
const s = "0000000" + e.toString(16);
|
||||
const v = new DataView(buff, o, n8);
|
||||
const v = new DataView(buff.buffer, 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;
|
||||
@@ -32,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) {
|
||||
n8 = n8 || buff.byteLength;
|
||||
const v = new Uint32Array(buff, o, n8/4);
|
||||
const v = new Uint32Array(buff.buffer, 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 Scalar.fromString(a.join(""), 16);
|
||||
@@ -41,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) {
|
||||
n8 = n8 || buff.byteLength;
|
||||
const v = new DataView(buff, o, n8);
|
||||
const v = new DataView(buff.buffer, 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");
|
||||
|
||||
@@ -8,9 +8,10 @@ module.exports.stringifyBigInts = function stringifyBigInts(o) {
|
||||
return o.map(stringifyBigInts);
|
||||
} else if (typeof o == "object") {
|
||||
const res = {};
|
||||
for (let k in o) {
|
||||
const keys = Object.keys(o);
|
||||
keys.forEach( (k) => {
|
||||
res[k] = stringifyBigInts(o[k]);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
} else {
|
||||
return o;
|
||||
@@ -24,9 +25,10 @@ module.exports.unstringifyBigInts = function unstringifyBigInts(o) {
|
||||
return o.map(unstringifyBigInts);
|
||||
} else if (typeof o == "object") {
|
||||
const res = {};
|
||||
for (let k in o) {
|
||||
const keys = Object.keys(o);
|
||||
keys.forEach( (k) => {
|
||||
res[k] = unstringifyBigInts(o[k]);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
} else {
|
||||
return o;
|
||||
|
||||
@@ -8,9 +8,10 @@ module.exports.stringifyBigInts = function stringifyBigInts(o) {
|
||||
return o.map(stringifyBigInts);
|
||||
} else if (typeof o == "object") {
|
||||
const res = {};
|
||||
for (let k in o) {
|
||||
const keys = Object.keys(o);
|
||||
keys.forEach( (k) => {
|
||||
res[k] = stringifyBigInts(o[k]);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
} else {
|
||||
return o;
|
||||
@@ -24,9 +25,10 @@ module.exports.unstringifyBigInts = function unstringifyBigInts(o) {
|
||||
return o.map(unstringifyBigInts);
|
||||
} else if (typeof o == "object") {
|
||||
const res = {};
|
||||
for (let k in o) {
|
||||
const keys = Object.keys(o);
|
||||
keys.forEach( (k) => {
|
||||
res[k] = unstringifyBigInts(o[k]);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
} else {
|
||||
return o;
|
||||
|
||||
30
test/bn128.js
Normal file
30
test/bn128.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const assert = require("assert");
|
||||
|
||||
describe("bn128 tester", 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;
|
||||
|
||||
const a = [];
|
||||
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]);
|
||||
|
||||
const A = bn128.PFr.fft(a);
|
||||
const AG = [];
|
||||
for (let i=0; i<8; i++) AG[i] = G1.mulScalar(G1.g, A[i]);
|
||||
|
||||
const aG_calculated = await G1.ifft(AG);
|
||||
|
||||
for (let i=0; i<8; i++) {
|
||||
assert(G1.eq(aG_calculated[i], aG_expected[i]));
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user