From e31b66991da7855f0057ccb51e5ed0057abf03d0 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 11 Sep 2025 00:46:53 +0100 Subject: [PATCH 01/11] Remove multiExp chunking - it works faster without it. Pass array buffers to worker threads instead of arrays (make it compatible with SharedArrayBuffer) --- src/engine_multiexp.js | 48 +++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/engine_multiexp.js b/src/engine_multiexp.js index 38d813f..002c828 100644 --- a/src/engine_multiexp.js +++ b/src/engine_multiexp.js @@ -53,11 +53,15 @@ export default function buildMultiexp(curve, groupName) { const bitChunkSize = pTSizes[log2(nPoints)]; const nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1; + console.log("buffBases len", buffBases.byteLength, "sGIn", sGIn, "nPoints", nPoints); + console.log("buffScalars len", buffScalars.byteLength, "sScalar", sScalar); + console.log("bitChunkSize", bitChunkSize, "nChunks", nChunks); + const opPromises = []; for (let i=0; iMAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; - if (chunkSizeMAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; + // if (chunkSize { + // if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); + // return r; + // })); + // } const opPromises = []; - for (let i=0; i { - if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); - return r; - })); - } + opPromises.push(_multiExpChunk(buffBases, buffScalars, inType, logger, logText).then( (r) => { + if (logger) logger.debug(`Multiexp end: ${logText}: ${nPoints}`); + return r; + })); const result = await Promise.all(opPromises); From 1d086533f3f7e6c6e636d3fb14722355a95155cd Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 11 Sep 2025 00:49:04 +0100 Subject: [PATCH 02/11] Create SharedArrayBuffers inside BigBuffer --- src/bigbuffer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bigbuffer.js b/src/bigbuffer.js index 1064a02..6900d57 100644 --- a/src/bigbuffer.js +++ b/src/bigbuffer.js @@ -8,7 +8,8 @@ export default class BigBuffer { this.byteLength = size; for (let i=0; i Date: Sat, 13 Sep 2025 01:59:10 +0100 Subject: [PATCH 03/11] Increase max size for underlying buffer of BigBuffer when run in nodejs --- src/bigbuffer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bigbuffer.js b/src/bigbuffer.js index 6900d57..e2247e3 100644 --- a/src/bigbuffer.js +++ b/src/bigbuffer.js @@ -1,5 +1,5 @@ -const PAGE_SIZE = 1<<30; +const PAGE_SIZE = ( typeof Buffer !== "undefined" && Buffer.constants && Buffer.constants.MAX_LENGTH ) ? Buffer.constants.MAX_LENGTH : (1 << 30); export default class BigBuffer { From eb5ffe10e3990c89ac1a5e6e88e5b8229aa9af96 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Sat, 13 Sep 2025 02:02:36 +0100 Subject: [PATCH 04/11] Fallback to old slicing logic when buffers are not SharedArrayBuffers --- src/engine_multiexp.js | 57 +++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/src/engine_multiexp.js b/src/engine_multiexp.js index 002c828..243e57b 100644 --- a/src/engine_multiexp.js +++ b/src/engine_multiexp.js @@ -120,31 +120,42 @@ export default function buildMultiexp(curve, groupName) { throw new Error("Scalar size does not match"); } - // const bitChunkSize = pTSizes[log2(nPoints)]; - // const nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1; - // - // let chunkSize; - // chunkSize = Math.floor(nPoints / (tm.concurrency /nChunks)); - // if (chunkSize>MAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; - // if (chunkSize { - // if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); - // return r; - // })); - // } + console.log("buffBases.buffer instanceof SharedArrayBuffer", buffBases.buffer instanceof SharedArrayBuffer); + console.log("buffScalars.buffer instanceof SharedArrayBuffer", buffScalars.buffer instanceof SharedArrayBuffer); const opPromises = []; - opPromises.push(_multiExpChunk(buffBases, buffScalars, inType, logger, logText).then( (r) => { - if (logger) logger.debug(`Multiexp end: ${logText}: ${nPoints}`); - return r; - })); + if (buffBases.buffer + && (buffBases.buffer instanceof SharedArrayBuffer) + && buffScalars.buffer + && (buffScalars.buffer instanceof SharedArrayBuffer) + ) + { + // If we are working with SharedArrayBuffers, we can do it in one chunk because memory is shared between threads + if (logger) logger.debug(`Multiexp start: ${logText}: ${nPoints}`); + opPromises.push(_multiExpChunk(buffBases, buffScalars, inType, logger, logText).then( (r) => { + if (logger) logger.debug(`Multiexp end: ${logText}: ${nPoints}`); + return r; + })); + } else { + const bitChunkSize = pTSizes[log2(nPoints)]; + const nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1; + + let chunkSize; + chunkSize = Math.floor(nPoints / (tm.concurrency /nChunks)); + if (chunkSize>MAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; + if (chunkSize { + if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); + return r; + })); + } + } const result = await Promise.all(opPromises); From ee037e62b3d65bc3fd106117cd3cb4b7e5614da3 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Mon, 22 Sep 2025 02:41:22 +0100 Subject: [PATCH 05/11] Error handling in workers. Terminate on worker error. Linter fixes --- src/threadman.js | 19 ++++++++++++++----- src/threadman_thread.js | 25 +++++++++++++++---------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/threadman.js b/src/threadman.js index f80901b..6f1cc43 100644 --- a/src/threadman.js +++ b/src/threadman.js @@ -110,7 +110,7 @@ export default async function buildThreadManager(wasm, singleThread) { concurrency = os.cpus().length; } - if(concurrency == 0){ + if(concurrency === 0){ concurrency = 2; } @@ -151,6 +151,15 @@ export default async function buildThreadManager(wasm, singleThread) { data = e; } + // handle errors + if (data.error) { + console.log("Worker error", data.error); + + tm.working[i]=false; + tm.pendingDeferreds[i].reject(data.error); + throw new Error(data.error); + } + tm.working[i]=false; tm.pendingDeferreds[i].resolve(data); tm.processWorks(); @@ -166,19 +175,19 @@ export class ThreadManager { } startSyncOp() { - if (this.oldPFree != 0) throw new Error("Sync operation in progress"); + if (this.oldPFree !== 0) throw new Error("Sync operation in progress"); this.oldPFree = this.u32[0]; } endSyncOp() { - if (this.oldPFree == 0) throw new Error("No sync operation in progress"); + 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) { if (this.working[workerId]) { - throw new Error("Posting a job t a working worker"); + throw new Error("Posting a job to a working worker"); } this.working[workerId] = true; @@ -190,7 +199,7 @@ export class ThreadManager { processWorks() { for (let i=0; (i 0); i++) { - if (this.working[i] == false) { + if (this.working[i] === false) { const work = this.actionQueue.shift(); this.postAction(i, work.data, work.transfers, work.deferred); } diff --git a/src/threadman_thread.js b/src/threadman_thread.js index 09951ba..726dbe9 100644 --- a/src/threadman_thread.js +++ b/src/threadman_thread.js @@ -14,15 +14,20 @@ export default function thread(self) { data = e; } - if (data[0].cmd == "INIT") { - init(data[0]).then(function() { - self.postMessage(data.result); - }); - } else if (data[0].cmd == "TERMINATE") { - self.close(); - } else { - const res = runTask(data); - self.postMessage(res); + try { + if (data[0].cmd === "INIT") { + init(data[0]).then(function() { + self.postMessage(data.result); + }); + } else if (data[0].cmd === "TERMINATE") { + self.close(); + } else { + const res = runTask(data); + self.postMessage(res); + } + } catch (err) { + // Catch any error and send it back to main thread + self.postMessage({error: err.message}); } }; } @@ -72,7 +77,7 @@ export default function thread(self) { } function runTask(task) { - if (task[0].cmd == "INIT") { + if (task[0].cmd === "INIT") { return init(task[0]); } const ctx = { From 8c51af04e233f312ec480d2c7a0b46868943358f Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Mon, 22 Sep 2025 02:52:25 +0100 Subject: [PATCH 06/11] Remove passing shared array buffers in one chunk. Fix nChunks calculation - drastically improve memory usage. Increase min chunk size to 1<<15 (32k) - speed improvement on smaller circuits. Serial chunk processing - better mem usage. Linter fixes --- src/engine_multiexp.js | 109 ++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 57 deletions(-) diff --git a/src/engine_multiexp.js b/src/engine_multiexp.js index 243e57b..c29591f 100644 --- a/src/engine_multiexp.js +++ b/src/engine_multiexp.js @@ -23,16 +23,16 @@ export default function buildMultiexp(curve, groupName) { let sGIn; let fnName; - if (groupName == "G1") { - if (inType == "affine") { + if (groupName === "G1") { + if (inType === "affine") { fnName = "g1m_multiexpAffine_chunk"; sGIn = G.F.n8*2; } else { fnName = "g1m_multiexp_chunk"; sGIn = G.F.n8*3; } - } else if (groupName == "G2") { - if (inType == "affine") { + } else if (groupName === "G2") { + if (inType === "affine") { fnName = "g2m_multiexpAffine_chunk"; sGIn = G.F.n8*2; } else { @@ -44,33 +44,30 @@ export default function buildMultiexp(curve, groupName) { } const nPoints = Math.floor(buffBases.byteLength / sGIn); - if (nPoints == 0) return G.zero; + if (nPoints === 0) return G.zero; const sScalar = Math.floor(buffScalars.byteLength / nPoints); - if( sScalar * nPoints != buffScalars.byteLength) { + 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; - console.log("buffBases len", buffBases.byteLength, "sGIn", sGIn, "nPoints", nPoints); - console.log("buffScalars len", buffScalars.byteLength, "sScalar", sScalar); - console.log("bitChunkSize", bitChunkSize, "nChunks", nChunks); const opPromises = []; for (let i=0; i { - if (logger) logger.debug(`Multiexp end: ${logText}: ${nPoints}`); - return r; - })); - } else { - const bitChunkSize = pTSizes[log2(nPoints)]; - const nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1; - - let chunkSize; - chunkSize = Math.floor(nPoints / (tm.concurrency /nChunks)); - if (chunkSize>MAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; - if (chunkSize { - if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); - return r; - })); - } + const bitChunkSize = pTSizes[log2(nPoints)]; + let nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1; + + if (groupName === "G2") { + // G2 has bigger points, so we reduce chunk size to optimize memory usage + nChunks *= 2; } - const result = await Promise.all(opPromises); + let chunkSize; + //chunkSize = Math.floor(nPoints / (tm.concurrency /nChunks)); + chunkSize = Math.floor(nPoints / nChunks); + if (chunkSize>MAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; + if (chunkSize { + // if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); + // return r; + // })); + const r = await _multiExpChunk(buffBasesChunk, buffScalarsChunk, inType, logger, logText); + if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); + result.push(r); + } + + //result = await Promise.all(opPromises); let res = G.zero; for (let i=result.length-1; i>=0; i--) { From af3b168ed24372b832876a28209a9b9aaf8bb381 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 25 Sep 2025 01:32:11 +0100 Subject: [PATCH 07/11] MultiExp: - remove chunking of chunks (removes unneeded copying of the same data to different worker jobs), - make nChunks multiple of tm.concurrency for optimal load balancing - switch back to promises from awaits (allows parallel execution of chunks) - rollback min chunk size - transfer buffer ownership to worker threads (removes memory copying for large arrays!!!) --- src/engine_multiexp.js | 72 ++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/src/engine_multiexp.js b/src/engine_multiexp.js index c29591f..498e3cd 100644 --- a/src/engine_multiexp.js +++ b/src/engine_multiexp.js @@ -10,6 +10,7 @@ const pTSizes = [ export default function buildMultiexp(curve, groupName) { const G = curve[groupName]; const tm = G.tm; + async function _multiExpChunk(buffBases, buffScalars, inType, logger, logText) { if ( ! (buffBases instanceof Uint8Array) ) { if (logger) logger.error(`${logText} _multiExpChunk buffBases is not Uint8Array`); @@ -25,18 +26,18 @@ export default function buildMultiexp(curve, groupName) { let fnName; if (groupName === "G1") { if (inType === "affine") { - fnName = "g1m_multiexpAffine_chunk"; + fnName = "g1m_multiexpAffine"; sGIn = G.F.n8*2; } else { - fnName = "g1m_multiexp_chunk"; + fnName = "g1m_multiexp"; sGIn = G.F.n8*3; } } else if (groupName === "G2") { if (inType === "affine") { - fnName = "g2m_multiexpAffine_chunk"; + fnName = "g2m_multiexpAffine"; sGIn = G.F.n8*2; } else { - fnName = "g2m_multiexp_chunk"; + fnName = "g2m_multiexp"; sGIn = G.F.n8*3; } } else { @@ -51,30 +52,26 @@ export default function buildMultiexp(curve, groupName) { } const bitChunkSize = pTSizes[log2(nPoints)]; - const nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1; - const opPromises = []; - for (let i=0; iMAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; if (chunkSize { - // if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); - // return r; - // })); - const r = await _multiExpChunk(buffBasesChunk, buffScalarsChunk, inType, logger, logText); - if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); - result.push(r); + opPromises.push(_multiExpChunk(buffBasesChunk, buffScalarsChunk, inType, logger, logText).then((r) => { + if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); + return r; + })); } - //result = await Promise.all(opPromises); + result = await Promise.all(opPromises); let res = G.zero; for (let i=result.length-1; i>=0; i--) { From 9ca91e44ab8f06c12a61d2325aa47a48d36f3b67 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Thu, 25 Sep 2025 01:34:58 +0100 Subject: [PATCH 08/11] Better error handling in threadman --- src/threadman.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/threadman.js b/src/threadman.js index 6f1cc43..38f46fc 100644 --- a/src/threadman.js +++ b/src/threadman.js @@ -153,11 +153,9 @@ export default async function buildThreadManager(wasm, singleThread) { // handle errors if (data.error) { - console.log("Worker error", data.error); - tm.working[i]=false; - tm.pendingDeferreds[i].reject(data.error); - throw new Error(data.error); + tm.pendingDeferreds[i].reject("Worker error: " + data.error); + throw new Error("Worker error: " + data.error); } tm.working[i]=false; @@ -198,6 +196,9 @@ export class ThreadManager { } processWorks() { + if (this.workers.length === 0 && this.actionQueue.length > 0) { + throw new Error("No workers initialized"); + } for (let i=0; (i 0); i++) { if (this.working[i] === false) { const work = this.actionQueue.shift(); From 6b39fc12e77cff1b069e1dd7d95afa4018ae67af Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Fri, 26 Sep 2025 14:41:11 +0100 Subject: [PATCH 09/11] Transfer buffer ownership to worker threads everywhere else --- src/engine_applykey.js | 6 ++++-- src/engine_batchconvert.js | 2 +- src/engine_fft.js | 14 +++++++------- src/engine_pairing.js | 2 +- src/wasm_field1.js | 2 +- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/engine_applykey.js b/src/engine_applykey.js index 5857d8c..294c9ff 100644 --- a/src/engine_applykey.js +++ b/src/engine_applykey.js @@ -64,10 +64,12 @@ export default function buildBatchApplyKey(curve, groupName) { const task = []; + const b = buff.slice(i*pointsPerChunk*sGin, i*pointsPerChunk*sGin + n*sGin); + task.push({ cmd: "ALLOCSET", var: 0, - buff: buff.slice(i*pointsPerChunk*sGin, i*pointsPerChunk*sGin + n*sGin) + buff: b }); task.push({cmd: "ALLOCSET", var: 1, buff: t}); task.push({cmd: "ALLOCSET", var: 2, buff: inc}); @@ -96,7 +98,7 @@ export default function buildBatchApplyKey(curve, groupName) { } task.push({cmd: "GET", out: 0, var: 3, len: n*sGout}); - opPromises.push(tm.queueAction(task)); + opPromises.push(tm.queueAction(task, [b.buffer])); t = Fr.mul(t, Fr.exp(inc, n)); } diff --git a/src/engine_batchconvert.js b/src/engine_batchconvert.js index 0acf524..1adb3bf 100644 --- a/src/engine_batchconvert.js +++ b/src/engine_batchconvert.js @@ -29,7 +29,7 @@ export default function buildBatchConvert(tm, fnName, sIn, sOut) { {cmd: "GET", out: 0, var: 1, len:sOut * n}, ]; opPromises.push( - tm.queueAction(task) + tm.queueAction(task, [buffChunk.buffer]) ); } diff --git a/src/engine_fft.js b/src/engine_fft.js index 5ae955b..2dae88d 100644 --- a/src/engine_fft.js +++ b/src/engine_fft.js @@ -146,7 +146,7 @@ export default function buildFFT(curve, groupName) { } else { task.push({cmd: "GET", out:0, var: 0, len: sMid*pointsInChunk}); } - promises.push(tm.queueAction(task).then( (r) => { + promises.push(tm.queueAction(task, [buffChunk.buffer]).then( (r) => { if (logger) logger.debug(`${loggerTxt}: fft ${bits} mix end: ${i}/${nChunks}`); return r; })); @@ -203,7 +203,7 @@ export default function buildFFT(curve, groupName) { task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sMid}); task.push({cmd: "GET", out: 1, var: 1, len: pointsInChunk*sMid}); } - opPromises.push(tm.queueAction(task).then( (r) => { + opPromises.push(tm.queueAction(task, [chunks[o1].buffer, chunks[o2].buffer, first.buffer ]).then( (r) => { if (logger) logger.debug(`${loggerTxt}: fft ${bits} join ${i}/${bits} ${j+1}/${nGroups} ${k}/${nChunksPerGroup/2}`); return r; })); @@ -402,7 +402,7 @@ export default function buildFFT(curve, groupName) { task.push({cmd: "GET", out: 0, var: 0, len: n*sOut}); task.push({cmd: "GET", out: 1, var: 1, len: n*sOut}); opPromises.push( - tm.queueAction(task).then( (r) => { + tm.queueAction(task, [b1.buffer, b2.buffer, firstChunk.buffer]).then((r) => { if (logger) logger.debug(`${loggerTxt}: fftJoinExt End: ${i}/${nPoints}`); return r; }) @@ -550,7 +550,7 @@ export default function buildFFT(curve, groupName) { } task.push({cmd: "GET", out: 0, var: 0, len: pointsPerChunk*sG}); opPromises.push( - tm.queueAction(task) + tm.queueAction(task, [b.buffer]) ); } @@ -585,7 +585,7 @@ export default function buildFFT(curve, groupName) { ]}); task.push({cmd: "GET", out: 0, var: 0, len: pointsPerChunk*sG}); task.push({cmd: "GET", out: 1, var: 1, len: pointsPerChunk*sG}); - opPromises.push(tm.queueAction(task)); + opPromises.push(tm.queueAction(task, [chunks[o1].buffer, chunks[o2].buffer, first.buffer])); } } @@ -664,7 +664,7 @@ export default function buildFFT(curve, groupName) { task.push({cmd: "GET", out: 0, var: 0, len: pointsPerChunk*sG}); task.push({cmd: "GET", out: 1, var: 1, len: pointsPerChunk*sG}); opPromises.push( - tm.queueAction(task) + tm.queueAction(task, [b1.buffer, b2.buffer, firstChunk.buffer]) ); } @@ -740,7 +740,7 @@ export default function buildFFT(curve, groupName) { ]}); task.push({cmd: "GET", out: 0, var: 0, len: n*sGout}); opPromises.push( - tm.queueAction(task) + tm.queueAction(task, [b.buffer]) ); } diff --git a/src/engine_pairing.js b/src/engine_pairing.js index 5a802ce..39095d2 100644 --- a/src/engine_pairing.js +++ b/src/engine_pairing.js @@ -60,7 +60,7 @@ export default function buildPairing(curve) { task.push({cmd: "GET", out: 0, var: 4, len: curve.Gt.n8}); opPromises.push( - tm.queueAction(task) + tm.queueAction(task, [g1Buff.buffer, g2Buff.buffer]) ); } diff --git a/src/wasm_field1.js b/src/wasm_field1.js index e5c3b64..7dbd8bb 100644 --- a/src/wasm_field1.js +++ b/src/wasm_field1.js @@ -282,7 +282,7 @@ export default class WasmField1 { {cmd: "GET", out: 0, var: 1, len:sOut * n}, ]; opPromises.push( - this.tm.queueAction(task) + this.tm.queueAction(task, [buffChunk.buffer]) ); } From b645924295f195cf0744158642e6c810c3ef33b4 Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Wed, 3 Dec 2025 00:08:25 +0000 Subject: [PATCH 10/11] Rollback usage of SharedArrayBuffer. Change var to let in tests. --- src/bigbuffer.js | 3 +-- src/engine_multiexp.js | 3 --- test/utils.js | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/bigbuffer.js b/src/bigbuffer.js index e2247e3..47c1a7c 100644 --- a/src/bigbuffer.js +++ b/src/bigbuffer.js @@ -8,8 +8,7 @@ export default class BigBuffer { this.byteLength = size; for (let i=0; i { it("Should generate buffer little-endian without trailing non-zero element", () => { for (let i = 1; i < 33; i++) { - var buff = utilsN.leInt2Buff(BigInt(42), i); + let buff = utilsN.leInt2Buff(BigInt(42), i); for (let t = 1; t < buff.length; t++){ assert(buff[t] === 0, true); } @@ -39,7 +39,7 @@ describe("Utils native", () => { it("Should generate buffer big-endian without trailing non-zero element", () => { for (let i = 1; i < 33; i++) { - var buff = utilsN.beInt2Buff(BigInt(42), i); + let buff = utilsN.beInt2Buff(BigInt(42), i); for (let t = 0; t < buff.length - 1; t++){ assert(buff[t] === 0, true); } From 399c45a7428322f02bbb73b63588b7fb24baeb1a Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Tue, 10 Feb 2026 12:13:05 +0000 Subject: [PATCH 11/11] Switch to dynamic import for prebuilt and gzip packed wasm from wasmcurves --- build/browser.esm.js | 23914 +++++++++++++++++---------------- build/main.cjs | 548 +- package-lock.json | 24 +- package.json | 2 +- rollup.browser.esm.config.js | 1 + src/bls12381.js | 5 +- src/bn128.js | 93 +- src/engine_fft.js | 45 +- src/engine_multiexp.js | 11 +- src/threadman.js | 195 +- src/threadman_thread.js | 99 +- 11 files changed, 12908 insertions(+), 12029 deletions(-) diff --git a/build/browser.esm.js b/build/browser.esm.js index 09713f3..92aa0d0 100644 --- a/build/browser.esm.js +++ b/build/browser.esm.js @@ -2757,3501 +2757,3526 @@ class EC { } -var utils$6 = {}; - -/* - Copyright 2019 0KIMS association. - - This file is part of wasmsnark (Web Assembly zkSnark Prover). - - wasmsnark 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. - - wasmsnark 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 wasmsnark. If not, see . -*/ +/* global BigInt */ -utils$6.bigInt2BytesLE = function bigInt2BytesLE(_a, len) { - const b = Array(len); - let v = BigInt(_a); - for (let i=0; i> 8n; +function stringifyBigInts(o) { + if (typeof o == "bigint" || o.eq !== undefined) { + return o.toString(10); + } else if (o instanceof Uint8Array) { + return fromRprLE(o, 0); + } else if (Array.isArray(o)) { + return o.map(stringifyBigInts); + } else if (typeof o == "object") { + const res = {}; + const keys = Object.keys(o); + keys.forEach((k) => { + res[k] = stringifyBigInts(o[k]); + }); + return res; + } else { + return o; } - return b; -}; +} -utils$6.bigInt2U32LE = function bigInt2BytesLE(_a, len) { - const b = Array(len); - let v = BigInt(_a); - for (let i=0; i> 32n; +function unstringifyBigInts(o) { + if (typeof o == "string" && /^[0-9]+$/.test(o)) { + return BigInt(o); + } else if (typeof o == "string" && /^0x[0-9a-fA-F]+$/.test(o)) { + return BigInt(o); + } else if (Array.isArray(o)) { + return o.map(unstringifyBigInts); + } else if (typeof o == "object") { + if (o === null) return null; + const res = {}; + const keys = Object.keys(o); + keys.forEach((k) => { + res[k] = unstringifyBigInts(o[k]); + }); + return res; + } else { + return o; } - return b; -}; - -utils$6.isOcamNum = function(a) { - if (!Array.isArray(a)) return false; - if (a.length != 3) return false; - if (typeof a[0] !== "number") return false; - if (typeof a[1] !== "number") return false; - if (!Array.isArray(a[2])) return false; - return true; -}; - -/* - Copyright 2019 0KIMS association. - - This file is part of wasmsnark (Web Assembly zkSnark Prover). - - wasmsnark 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. - - wasmsnark 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 wasmsnark. If not, see . -*/ - -var build_int = function buildInt(module, n64, _prefix) { - - const prefix = _prefix || "int"; - if (module.modules[prefix]) return prefix; // already builded - module.modules[prefix] = {}; - - const n32 = n64*2; - const n8 = n64*8; - - function buildCopy() { - const f = module.addFunction(prefix+"_copy"); - f.addParam("px", "i32"); - f.addParam("pr", "i32"); - - const c = f.getCodeBuilder(); +} - for (let i=0; i 0) { + if (i >= 4) { + i -= 4; + res += BigInt(buffV.getUint32(i)) << BigInt(offset * 8); + offset += 4; + } else if (i >= 2) { + i -= 2; + res += BigInt(buffV.getUint16(i)) << BigInt(offset * 8); + offset += 2; + } else { + i -= 1; + res += BigInt(buffV.getUint8(i)) << BigInt(offset * 8); + offset += 1; } } + return res; +} - function buildZero() { - const f = module.addFunction(prefix+"_zero"); - f.addParam("pr", "i32"); - - const c = f.getCodeBuilder(); - - for (let i=0; i 0) { + if (o - 4 >= 0) { + o -= 4; + buffV.setUint32(o, Number(r & BigInt(0xffffffff))); + r = r >> BigInt(32); + } else if (o - 2 >= 0) { + o -= 2; + buffV.setUint16(o, Number(r & BigInt(0xffff))); + r = r >> BigInt(16); + } else { + o -= 1; + buffV.setUint8(o, Number(r & BigInt(0xff))); + r = r >> BigInt(8); } } - - function buildOne() { - const f = module.addFunction(prefix+"_one"); - f.addParam("pr", "i32"); - - const c = f.getCodeBuilder(); - - f.addCode( - c.i64_store( - c.getLocal("pr"), - 0, - c.i64_const(1) - ) - ); - for (let i=1; i> BigInt(32); + } else if (o + 2 <= len) { + buffV.setUint16(o, Number(r & BigInt(0xffff)), true); + o += 2; + r = r >> BigInt(16); + } else { + buffV.setUint8(o, Number(r & BigInt(0xff)), true); + o += 1; + r = r >> BigInt(8); } + } + if (r) { + throw new Error("Number does not fit in this length"); + } + return buff; +} - f.addCode(getCompCode(n64-1)); - f.addCode(c.ret(c.i32_const(0))); +function stringifyFElements(F, o) { + if (typeof o == "bigint" || o.eq !== undefined) { + return o.toString(10); + } else if (o instanceof Uint8Array) { + return F.toString(F.e(o)); + } else if (Array.isArray(o)) { + return o.map(stringifyFElements.bind(this, F)); + } else if (typeof o == "object") { + const res = {}; + const keys = Object.keys(o); + keys.forEach((k) => { + res[k] = stringifyFElements(F, o[k]); + }); + return res; + } else { + return o; } +} +function unstringifyFElements(F, o) { + if (typeof o == "string" && /^[0-9]+$/.test(o)) { + return F.e(o); + } else if (typeof o == "string" && /^0x[0-9a-fA-F]+$/.test(o)) { + return F.e(o); + } else if (Array.isArray(o)) { + return o.map(unstringifyFElements.bind(this, F)); + } else if (typeof o == "object") { + if (o === null) return null; + const res = {}; + const keys = Object.keys(o); + keys.forEach((k) => { + res[k] = unstringifyFElements(F, o[k]); + }); + return res; + } else { + return o; + } +} +const _revTable = []; +for (let i = 0; i < 256; i++) { + _revTable[i] = _revSlow(i, 8); +} - function buildGte() { - const f = module.addFunction(prefix+"_gte"); - f.addParam("px", "i32"); - f.addParam("py", "i32"); - f.setReturnType("i32"); +function _revSlow(idx, bits) { + let res = 0; + let a = idx; + for (let i = 0; i < bits; i++) { + res <<= 1; + res = res | (a & 1); + a >>= 1; + } + return res; +} - const c = f.getCodeBuilder(); +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 getCompCode(n) { - if (n==0) { - return c.ret(c.i64_ge_u( - c.i64_load(c.getLocal("px")), - c.i64_load(c.getLocal("py")) - )); - } - return c.if( - c.i64_lt_u( - c.i64_load(c.getLocal("px"), n*8 ), - c.i64_load(c.getLocal("py"), n*8 ) - ), - c.ret(c.i32_const(0)), - c.if( - c.i64_gt_u( - c.i64_load(c.getLocal("px"), n*8 ), - c.i64_load(c.getLocal("py"), n*8 ) - ), - c.ret(c.i32_const(1)), - getCompCode(n-1) - ) - ); - } +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) + ); +} - f.addCode(getCompCode(n64-1)); - f.addCode(c.ret(c.i32_const(0))); +function buffReverseBits(buff, eSize) { + const n = buff.byteLength / eSize; + const bits = 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); + 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); + } + } +} +function array2buffer(arr, sG) { + const buff = new Uint8Array(sG * arr.length); + for (let i = 0; i < arr.length; i++) { + buff.set(arr[i], i * sG); + } - function buildAdd() { - - const f = module.addFunction(prefix+"_add"); - f.addParam("x", "i32"); - f.addParam("y", "i32"); - f.addParam("r", "i32"); - f.setReturnType("i32"); - f.addLocal("c", "i64"); + return buff; +} - const c = f.getCodeBuilder(); +function buffer2array(buff, sG) { + const n = buff.byteLength / sG; + const arr = new Array(n); + for (let i = 0; i < n; i++) { + arr[i] = buff.slice(i * sG, i * sG + sG); + } + return arr; +} - f.addCode(c.setLocal( - "c", - c.i64_add( - c.i64_load32_u(c.getLocal("x")), - c.i64_load32_u(c.getLocal("y")) - ) - )); +var _utils = /*#__PURE__*/Object.freeze({ + __proto__: null, + array2buffer: array2buffer, + beBuff2int: beBuff2int, + beInt2Buff: beInt2Buff, + bitReverse: bitReverse, + buffReverseBits: buffReverseBits, + buffer2array: buffer2array, + leBuff2int: leBuff2int, + leInt2Buff: leInt2Buff, + log2: log2, + stringifyBigInts: stringifyBigInts, + stringifyFElements: stringifyFElements, + unstringifyBigInts: unstringifyBigInts, + unstringifyFElements: unstringifyFElements +}); - f.addCode(c.i64_store32( - c.getLocal("r"), - c.getLocal("c"), - )); +const PAGE_SIZE = ( typeof Buffer !== "undefined" && Buffer.constants && Buffer.constants.MAX_LENGTH ) ? Buffer.constants.MAX_LENGTH : (1 << 30); - for (let i=1; i0) { + // bytes to copy from this page + const l = (o+r > PAGE_SIZE) ? (PAGE_SIZE -o) : r; + const srcView = new Uint8Array(this.buffers[p].buffer, this.buffers[p].byteOffset+o, l); + if (l == len) return srcView.slice(); + if (!buff) { + if (len <= PAGE_SIZE) { + buff = new Uint8Array(len); + } else { + buff = new BigBuffer(len); + } + } + buff.set(srcView, len-r); + r = r-l; + p ++; + o = 0; } - f.addCode(c.i32_wrap_i64 ( c.i64_shr_s (c.getLocal("c"), c.i64_const(32)))); + return buff; } + set(buff, offset) { + if (offset === undefined) offset = 0; - function buildMul() { + const len = buff.byteLength; - const f = module.addFunction(prefix+"_mul"); - f.addParam("x", "i32"); - f.addParam("y", "i32"); - f.addParam("r", "i32"); - f.addLocal("c0", "i64"); - f.addLocal("c1", "i64"); + if (len==0) return; + const firstPage = Math.floor(offset / PAGE_SIZE); + const lastPage = Math.floor((offset+len-1) / PAGE_SIZE); + + if (firstPage == lastPage) { + if ((buff instanceof BigBuffer)&&(buff.buffers.length==1)) { + return this.buffers[firstPage].set(buff.buffers[0], offset % PAGE_SIZE); + } else { + return this.buffers[firstPage].set(buff, offset % PAGE_SIZE); + } - for (let i=0;i0) { + const l = (o+r > PAGE_SIZE) ? (PAGE_SIZE -o) : r; + const srcView = buff.slice( len -r, len -r+l); + const dstView = new Uint8Array(this.buffers[p].buffer, this.buffers[p].byteOffset + o, l); + dstView.set(srcView); + r = r-l; + p ++; + o = 0; + } + + } +} + +function buildBatchConvert(tm, fnName, sIn, sOut) { + return async function batchConvert(buffIn) { + const nPoints = Math.floor(buffIn.byteLength / sIn); + 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=0; i--) { + this.w[i] = this.square(this.w[i+1]); + } - for (let i=Math.max(0, k-n32+1); (i<((k+1)>>1) )&&(i>1, k>>1) - ) - ) - ); + op1(opName, a) { + this.tm.setBuff(this.pOp1, a); + this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); + return this.tm.getBuff(this.pOp3, this.n8); + } - f.addCode( - c.setLocal(c1, - c.i64_add( - c.getLocal(c1), - c.i64_shr_u( - c.getLocal(c0), - c.i64_const(32) - ) - ) - ) - ); - } + op1Bool(opName, a) { + this.tm.setBuff(this.pOp1, a); + return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); + } - // Add the old carry + add(a,b) { + return this.op2("_add", a, b); + } - if (k>0) { - f.addCode( - c.setLocal(c0, - c.i64_add( - c.i64_and( - c.getLocal(c0), - c.i64_const(0xFFFFFFFF) - ), - c.i64_and( - c.getLocal(c0_old), - c.i64_const(0xFFFFFFFF) - ), - ) - ) - ); - f.addCode( - c.setLocal(c1, - c.i64_add( - c.i64_add( - c.getLocal(c1), - c.i64_shr_u( - c.getLocal(c0), - c.i64_const(32) - ) - ), - c.getLocal(c1_old) - ) - ) - ); - } + eq(a,b) { + return this.op2Bool("_eq", a, b); + } - f.addCode( - c.i64_store32( - c.getLocal("r"), - k*4, - c.getLocal(c0) - ) - ); + isZero(a) { + return this.op1Bool("_isZero", a); + } - f.addCode( - c.setLocal( - c0_old, - c.getLocal(c1) - ), - c.setLocal( - c1_old, - c.i64_shr_u( - c.getLocal(c0_old), - c.i64_const(32) - ) - ) - ); + sub(a,b) { + return this.op2("_sub", a, b); + } - } - f.addCode( - c.i64_store32( - c.getLocal("r"), - n32*4*2-4, - c.getLocal(c0_old) - ) - ); + neg(a) { + return this.op1("_neg", a); + } + inv(a) { + return this.op1("_inverse", a); } + toMontgomery(a) { + return this.op1("_toMontgomery", a); + } - function buildSquareOld() { - const f = module.addFunction(prefix+"_squareOld"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + fromMontgomery(a) { + return this.op1("_fromMontgomery", a); + } - const c = f.getCodeBuilder(); + mul(a,b) { + return this.op2("_mul", a, b); + } - f.addCode(c.call(prefix + "_mul", c.getLocal("x"), c.getLocal("x"), c.getLocal("r"))); + 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); } - function _buildMul1() { - const f = module.addFunction(prefix+"__mul1"); - f.addParam("px", "i32"); - f.addParam("y", "i64"); - f.addParam("pr", "i32"); - f.addLocal("c", "i64"); + square(a) { + return this.op1("_square", a); + } - const c = f.getCodeBuilder(); + isSquare(a) { + return this.op1Bool("_isSquare", a); + } - f.addCode(c.setLocal( - "c", - c.i64_mul( - c.i64_load32_u(c.getLocal("px"), 0, 0), - c.getLocal("y") - ) - )); + sqrt(a) { + return this.op1("_sqrt", a); + } - f.addCode(c.i64_store32( - c.getLocal("pr"), - 0, - 0, - c.getLocal("c"), - )); + exp(a, b) { + if (!(b instanceof Uint8Array)) { + b = toLEBuff(e(b)); + } + this.tm.setBuff(this.pOp1, a); + this.tm.setBuff(this.pOp2, b); + this.tm.instance.exports[this.prefix + "_exp"](this.pOp1, this.pOp2, b.byteLength, this.pOp3); + return this.tm.getBuff(this.pOp3, this.n8); + } - for (let i=1; i3)&&(Y[eY]==0) ey--; - f.addCode(c.block(c.loop( - c.br_if( - 1, - c.i32_or( - c.i32_load8_u( - c.i32_add(Y , c.getLocal("eY")), - 0, - 0 - ), - c.i32_eq( - c.getLocal("eY"), - c.i32_const(3) - ) - ) - ), - c.setLocal("eY", c.i32_sub(c.getLocal("eY"), c.i32_const(1))), - c.br(0) - ))); + this.pOp1 = tm.alloc(F.n8*2); + this.pOp2 = tm.alloc(F.n8*2); + this.pOp3 = tm.alloc(F.n8*2); + this.tm.instance.exports[prefix + "_zero"](this.pOp1); + this.zero = tm.getBuff(this.pOp1, this.n8); + this.tm.instance.exports[prefix + "_one"](this.pOp1); + this.one = tm.getBuff(this.pOp1, this.n8); - f.addCode( - c.setLocal( - "sy", - c.i64_add( - c.i64_load32_u( - c.i32_sub( - c.i32_add( Y, c.getLocal("eY")), - c.i32_const(3) - ), - 0, - 0 - ), - c.i64_const(1) - ) - ) - ); + this.negone = this.neg(this.one); + this.two = this.add(this.one, this.one); - // Force a divide by 0 if quotien is 0 - f.addCode( - c.if( - c.i64_eq( - c.getLocal("sy"), - c.i64_const(1) - ), - c.drop(c.i64_div_u(c.i64_const(0), c.i64_const(0))) - ) - ); + } - f.addCode(c.block(c.loop( + op2(opName, a, 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.tm.getBuff(this.pOp3, this.n8); + } - // while (eX>7)&&(Y[eX]==0) ex--; - c.block(c.loop( - c.br_if( - 1, - c.i32_or( - c.i32_load8_u( - c.i32_add(R , c.getLocal("eX")), - 0, - 0 - ), - c.i32_eq( - c.getLocal("eX"), - c.i32_const(7) - ) - ) - ), - c.setLocal("eX", c.i32_sub(c.getLocal("eX"), c.i32_const(1))), - c.br(0) - )), + op2Bool(opName, a, 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); + } - c.setLocal( - "sx", - c.i64_load( - c.i32_sub( - c.i32_add( R, c.getLocal("eX")), - c.i32_const(7) - ), - 0, - 0 - ) - ), + op1(opName, a) { + this.tm.setBuff(this.pOp1, a); + this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); + return this.tm.getBuff(this.pOp3, this.n8); + } - c.setLocal( - "sx", - c.i64_div_u( - c.getLocal("sx"), - c.getLocal("sy") - ) - ), - c.setLocal( - "ec", - c.i32_sub( - c.i32_sub( - c.getLocal("eX"), - c.getLocal("eY") - ), - c.i32_const(4) - ) - ), + op1Bool(opName, a) { + this.tm.setBuff(this.pOp1, a); + return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); + } - // While greater than 32 bits or ec is neg, shr and inc exp - c.block(c.loop( - c.br_if( - 1, - c.i32_and( - c.i64_eqz( - c.i64_and( - c.getLocal("sx"), - c.i64_const("0xFFFFFFFF00000000") - ) - ), - c.i32_ge_s( - c.getLocal("ec"), - c.i32_const(0) - ) - ) - ), + add(a,b) { + return this.op2("_add", a, b); + } - c.setLocal( - "sx", - c.i64_shr_u( - c.getLocal("sx"), - c.i64_const(8) - ) - ), + eq(a,b) { + return this.op2Bool("_eq", a, b); + } - c.setLocal( - "ec", - c.i32_add( - c.getLocal("ec"), - c.i32_const(1) - ) - ), - c.br(0) - )), + isZero(a) { + return this.op1Bool("_isZero", a); + } - c.if( - c.i64_eqz(c.getLocal("sx")), - [ - ...c.br_if( - 2, - c.i32_eqz(c.call(prefix + "_gte", R, Y)) - ), - ...c.setLocal("sx", c.i64_const(1)), - ...c.setLocal("ec", c.i32_const(0)) - ] - ), + sub(a,b) { + return this.op2("_sub", a, b); + } - c.call(prefix + "__mul1", Y, c.getLocal("sx"), R2), - c.drop(c.call( - prefix + "_sub", - R, - c.i32_sub(R2, c.getLocal("ec")), - R - )), - c.call( - prefix + "__add1", - c.i32_add(C, c.getLocal("ec")), - c.getLocal("sx") - ), - c.br(0) - ))); + neg(a) { + return this.op1("_neg", a); } - function buildInverseMod() { + inv(a) { + return this.op1("_inverse", a); + } - const f = module.addFunction(prefix+"_inverseMod"); - f.addParam("px", "i32"); - f.addParam("pm", "i32"); - f.addParam("pr", "i32"); - f.addLocal("t", "i32"); - f.addLocal("newt", "i32"); - f.addLocal("r", "i32"); - f.addLocal("qq", "i32"); - f.addLocal("qr", "i32"); - f.addLocal("newr", "i32"); - f.addLocal("swp", "i32"); - f.addLocal("x", "i32"); - f.addLocal("signt", "i32"); - f.addLocal("signnewt", "i32"); - f.addLocal("signx", "i32"); + isNegative(a) { + return this.op1Bool("_isNegative", a); + } - const c = f.getCodeBuilder(); + toMontgomery(a) { + return this.op1("_toMontgomery", a); + } - const aux1 = c.i32_const(module.alloc(n8)); - const aux2 = c.i32_const(module.alloc(n8)); - const aux3 = c.i32_const(module.alloc(n8)); - const aux4 = c.i32_const(module.alloc(n8)); - const aux5 = c.i32_const(module.alloc(n8)); - const aux6 = c.i32_const(module.alloc(n8)); - const mulBuff = c.i32_const(module.alloc(n8*2)); - const aux7 = c.i32_const(module.alloc(n8)); + fromMontgomery(a) { + return this.op1("_fromMontgomery", a); + } - f.addCode( - c.setLocal("t", aux1), - c.call(prefix + "_zero", aux1), - c.setLocal("signt", c.i32_const(0)), - ); + mul(a,b) { + return this.op2("_mul", a, b); + } - f.addCode( - c.setLocal("r", aux2), - c.call(prefix + "_copy", c.getLocal("pm"), aux2) - ); - - f.addCode( - c.setLocal("newt", aux3), - c.call(prefix + "_one", aux3), - c.setLocal("signnewt", c.i32_const(0)), - ); - - f.addCode( - c.setLocal("newr", aux4), - c.call(prefix + "_copy", c.getLocal("px"), aux4) - ); + mul1(a,b) { + return this.op2("_mul1", 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); + } + isSquare(a) { + return this.op1Bool("_isSquare", a); + } - f.addCode(c.setLocal("qq", aux5)); - f.addCode(c.setLocal("qr", aux6)); - f.addCode(c.setLocal("x", aux7)); + sqrt(a) { + return this.op1("_sqrt", a); + } - f.addCode(c.block(c.loop( - c.br_if( - 1, - c.call(prefix + "_isZero", c.getLocal("newr") ) - ), - c.call(prefix + "_div", c.getLocal("r"), c.getLocal("newr"), c.getLocal("qq"), c.getLocal("qr")), + exp(a, b) { + if (!(b instanceof Uint8Array)) { + b = toLEBuff(e(b)); + } + this.tm.setBuff(this.pOp1, a); + this.tm.setBuff(this.pOp2, b); + this.tm.instance.exports[this.prefix + "_exp"](this.pOp1, this.pOp2, b.byteLength, this.pOp3); + return this.tm.getBuff(this.pOp3, this.n8); + } - c.call(prefix + "_mul", c.getLocal("qq"), c.getLocal("newt"), mulBuff), + e(a, b) { + if (a instanceof Uint8Array) return a; + if ((Array.isArray(a)) && (a.length == 2)) { + const c1 = this.F.e(a[0], b); + const c2 = this.F.e(a[1], b); + const res = new Uint8Array(this.F.n8*2); + res.set(c1); + res.set(c2, this.F.n8*2); + return res; + } else { + throw new Error("invalid F2"); + } + } - c.if( - c.getLocal("signt"), - c.if( - c.getLocal("signnewt"), - c.if ( - c.call(prefix + "_gte", mulBuff, c.getLocal("t")), - [ - ...c.drop(c.call(prefix + "_sub", mulBuff, c.getLocal("t"), c.getLocal("x"))), - ...c.setLocal("signx", c.i32_const(0)) - ], - [ - ...c.drop(c.call(prefix + "_sub", c.getLocal("t"), mulBuff, c.getLocal("x"))), - ...c.setLocal("signx", c.i32_const(1)) - ], - ), - [ - ...c.drop(c.call(prefix + "_add", mulBuff, c.getLocal("t"), c.getLocal("x"))), - ...c.setLocal("signx", c.i32_const(1)) - ] - ), - c.if( - c.getLocal("signnewt"), - [ - ...c.drop(c.call(prefix + "_add", mulBuff, c.getLocal("t"), c.getLocal("x"))), - ...c.setLocal("signx", c.i32_const(0)) - ], - c.if ( - c.call(prefix + "_gte", c.getLocal("t"), mulBuff), - [ - ...c.drop(c.call(prefix + "_sub", c.getLocal("t"), mulBuff, c.getLocal("x"))), - ...c.setLocal("signx", c.i32_const(0)) - ], - [ - ...c.drop(c.call(prefix + "_sub", mulBuff, c.getLocal("t"), c.getLocal("x"))), - ...c.setLocal("signx", c.i32_const(1)) - ] - ) - ) - ), + toString(a, radix) { + const s1 = this.F.toString(a.slice(0, this.F.n8), radix); + const s2 = this.F.toString(a.slice(this.F.n8), radix); + return `[${s1}, ${s2}]`; + } - c.setLocal("swp", c.getLocal("t")), - c.setLocal("t", c.getLocal("newt")), - c.setLocal("newt", c.getLocal("x")), - c.setLocal("x", c.getLocal("swp")), + fromRng(rng) { + const c1 = this.F.fromRng(rng); + const c2 = this.F.fromRng(rng); + const res = new Uint8Array(this.F.n8*2); + res.set(c1); + res.set(c2, this.F.n8); + return res; + } - c.setLocal("signt", c.getLocal("signnewt")), - c.setLocal("signnewt", c.getLocal("signx")), + random() { + return this.fromRng(getThreadRng()); + } - c.setLocal("swp", c.getLocal("r")), - c.setLocal("r", c.getLocal("newr")), - c.setLocal("newr", c.getLocal("qr")), - c.setLocal("qr", c.getLocal("swp")), + toObject(a) { + const c1 = this.F.toObject(a.slice(0, this.F.n8)); + const c2 = this.F.toObject(a.slice(this.F.n8, this.F.n8*2)); + return [c1, c2]; + } - c.br(0) - ))); + fromObject(a) { + const buff = new Uint8Array(this.F.n8*2); + const b1 = this.F.fromObject(a[0]); + const b2 = this.F.fromObject(a[1]); + buff.set(b1); + buff.set(b2, this.F.n8); + return buff; + } - f.addCode(c.if( - c.getLocal("signt"), - c.drop(c.call(prefix + "_sub", c.getLocal("pm"), c.getLocal("t"), c.getLocal("pr"))), - c.call(prefix + "_copy", c.getLocal("t"), c.getLocal("pr")) - )); + c1(a) { + return a.slice(0, this.F.n8); } + c2(a) { + return a.slice(this.F.n8); + } - buildCopy(); - buildZero(); - buildIsZero(); - buildOne(); - buildEq(); - buildGte(); - buildAdd(); - buildSub(); - buildMul(); - buildSquare(); - buildSquareOld(); - buildDiv(); - buildInverseMod(); - module.exportFunction(prefix+"_copy"); - module.exportFunction(prefix+"_zero"); - module.exportFunction(prefix+"_one"); - module.exportFunction(prefix+"_isZero"); - module.exportFunction(prefix+"_eq"); - module.exportFunction(prefix+"_gte"); - module.exportFunction(prefix+"_add"); - module.exportFunction(prefix+"_sub"); - module.exportFunction(prefix+"_mul"); - module.exportFunction(prefix+"_square"); - module.exportFunction(prefix+"_squareOld"); - module.exportFunction(prefix+"_div"); - module.exportFunction(prefix+"_inverseMod"); +} - return prefix; -}; +class WasmField3 { -/* - Copyright 2019 0KIMS association. + constructor(tm, prefix, F) { + this.tm = tm; + this.prefix = prefix; - This file is part of wasmsnark (Web Assembly zkSnark Prover). + this.F = F; + this.type = "F3"; + this.m = F.m * 3; + this.n8 = this.F.n8*3; + this.n32 = this.F.n32*3; + this.n64 = this.F.n64*3; - wasmsnark 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. + this.pOp1 = tm.alloc(F.n8*3); + this.pOp2 = tm.alloc(F.n8*3); + this.pOp3 = tm.alloc(F.n8*3); + this.tm.instance.exports[prefix + "_zero"](this.pOp1); + this.zero = tm.getBuff(this.pOp1, this.n8); + this.tm.instance.exports[prefix + "_one"](this.pOp1); + this.one = tm.getBuff(this.pOp1, this.n8); - wasmsnark 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. + this.negone = this.neg(this.one); + this.two = this.add(this.one, this.one); - You should have received a copy of the GNU General Public License - along with wasmsnark. If not, see . -*/ + } -var build_timesscalar = function buildTimesScalar(module, fnName, elementLen, opAB, opAA, opCopy, opInit) { + op2(opName, a, 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.tm.getBuff(this.pOp3, this.n8); + } - const f = module.addFunction(fnName); - f.addParam("base", "i32"); - f.addParam("scalar", "i32"); - f.addParam("scalarLength", "i32"); - f.addParam("r", "i32"); - f.addLocal("i", "i32"); - f.addLocal("b", "i32"); + op2Bool(opName, a, 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); + } - const c = f.getCodeBuilder(); + op1(opName, a) { + this.tm.setBuff(this.pOp1, a); + this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); + return this.tm.getBuff(this.pOp3, this.n8); + } - const aux = c.i32_const(module.alloc(elementLen)); + op1Bool(opName, a) { + this.tm.setBuff(this.pOp1, a); + return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); + } - f.addCode( - c.if( - c.i32_eqz(c.getLocal("scalarLength")), - [ - ...c.call(opInit, c.getLocal("r")), - ...c.ret([]) - ] - ) - ); - f.addCode(c.call(opCopy, c.getLocal("base"), aux)); - f.addCode(c.call(opInit, c.getLocal("r"))); - f.addCode(c.setLocal("i", c.getLocal("scalarLength"))); - f.addCode(c.block(c.loop( - c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), - c.setLocal( - "b", - c.i32_load8_u( - c.i32_add( - c.getLocal("scalar"), - c.getLocal("i") - ) - ) - ), - ...innerLoop(), - c.br_if(1, c.i32_eqz ( c.getLocal("i") )), - c.br(0) - ))); + eq(a,b) { + return this.op2Bool("_eq", a, b); + } + isZero(a) { + return this.op1Bool("_isZero", a); + } - function innerLoop() { - const code = []; - for (let i=0; i<8; i++) { - code.push( - ...c.call(opAA, c.getLocal("r"), c.getLocal("r")), - ...c.if( - c.i32_ge_u( c.getLocal("b"), c.i32_const(0x80 >> i)), - [ - ...c.setLocal( - "b", - c.i32_sub( - c.getLocal("b"), - c.i32_const(0x80 >> i) - ) - ), - ...c.call(opAB, c.getLocal("r"),aux, c.getLocal("r")) - ] - ) - ); - } - return code; + add(a,b) { + return this.op2("_add", a, b); } -}; + sub(a,b) { + return this.op2("_sub", a, b); + } -var build_batchinverse = buildBatchInverse$3; + neg(a) { + return this.op1("_neg", a); + } -function buildBatchInverse$3(module, prefix) { + inv(a) { + return this.op1("_inverse", a); + } + isNegative(a) { + return this.op1Bool("_isNegative", a); + } - const n8 = module.modules[prefix].n64*8; + toMontgomery(a) { + return this.op1("_toMontgomery", a); + } - const f = module.addFunction(prefix+"_batchInverse"); - f.addParam("pIn", "i32"); - f.addParam("inStep", "i32"); - f.addParam("n", "i32"); - f.addParam("pOut", "i32"); - f.addParam("outStep", "i32"); - f.addLocal("itAux", "i32"); - f.addLocal("itIn", "i32"); - f.addLocal("itOut","i32"); - f.addLocal("i","i32"); + fromMontgomery(a) { + return this.op1("_fromMontgomery", a); + } - const c = f.getCodeBuilder(); + mul(a,b) { + return this.op2("_mul", a, b); + } - const AUX = c.i32_const(module.alloc(n8)); + 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); + } - // Alloc Working space for accumulated umltiplications - f.addCode( - c.setLocal("itAux", c.i32_load( c.i32_const(0) )), - c.i32_store( - c.i32_const(0), - c.i32_add( - c.getLocal("itAux"), - c.i32_mul( - c.i32_add( - c.getLocal("n"), - c.i32_const(1) - ), - c.i32_const(n8) - ) - ) - ) - ); + isSquare(a) { + return this.op1Bool("_isSquare", a); + } - f.addCode( + sqrt(a) { + return this.op1("_sqrt", a); + } - // aux[0] = a; - c.call(prefix+"_one", c.getLocal("itAux")), - // for (i=0;i b ? 1 : -1; -} - -function square$1(n) { - return n * n; -} + timesFr(a, s) { + let fnName; + if (a.byteLength == this.F.n8*3) { + fnName = this.prefix + "_timesFr"; + } else if (a.byteLength == this.F.n8*2) { + fnName = this.prefix + "_timesFrAffine"; + } else { + throw new Error("invalid point size"); + } + this.tm.setBuff(this.pOp1, a); + this.tm.setBuff(this.pOp2, s); + this.tm.instance.exports[fnName](this.pOp1, this.pOp2, this.pOp3); + return this.tm.getBuff(this.pOp3, this.F.n8*3); + } -function isOdd$4(n) { - return n % 2n !== 0n; -} + eq(a,b) { + if (a.byteLength == this.F.n8*3) { + if (b.byteLength == this.F.n8*3) { + return this.op2bool("_eq", a, b); + } else if (b.byteLength == this.F.n8*2) { + return this.op2bool("_eqMixed", a, b); + } else { + throw new Error("invalid point size"); + } + } else if (a.byteLength == this.F.n8*2) { + if (b.byteLength == this.F.n8*3) { + return this.op2bool("_eqMixed", b, a); + } else if (b.byteLength == this.F.n8*2) { + return this.op2bool("_eqAffine", a, b); + } else { + throw new Error("invalid point size"); + } + } else { + throw new Error("invalid point size"); + } + } -function isEven(n) { - return n % 2n === 0n; -} + toAffine(a) { + if (a.byteLength == this.F.n8*3) { + return this.op1Affine("_toAffine", a); + } else if (a.byteLength == this.F.n8*2) { + return a; + } else { + throw new Error("invalid point size"); + } + } -function isNegative$3(n) { - return n < 0n; -} + toJacobian(a) { + if (a.byteLength == this.F.n8*3) { + return a; + } else if (a.byteLength == this.F.n8*2) { + return this.op1("_toJacobian", a); + } else { + throw new Error("invalid point size"); + } + } -function isPositive(n) { - return n > 0n; -} + toRprUncompressed(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 + "_LEMtoU"](this.pOp1, this.pOp1); + const res = this.tm.getBuff(this.pOp1, this.F.n8*2); + arr.set(res, offset); + } -function bitLength$5(n) { - if (isNegative$3(n)) { - return n.toString(2).length - 1; // discard the - sign - } else { - return n.toString(2).length; + fromRprUncompressed(arr, offset) { + const buff = arr.slice(offset, offset + this.F.n8*2); + this.tm.setBuff(this.pOp1, buff); + this.tm.instance.exports[this.prefix + "_UtoLEM"](this.pOp1, this.pOp1); + return this.tm.getBuff(this.pOp1, this.F.n8*2); } -} -function abs(n) { - return n < 0n ? -n : n; -} + 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); + } -function isUnit(n) { - return abs(n) === 1n; -} + 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); + } -function modInv$3(a, n) { - var t = 0n, newT = 1n, r = n, newR = abs(a), q, lastT, lastR; - while (newR !== 0n) { - q = r / newR; - lastT = t; - lastR = r; - t = newT; - r = newR; - newT = lastT - (q * newT); - newR = lastR - (q * newR); + toUncompressed(a) { + const buff = new Uint8Array(this.F.n8*2); + this.toRprUncompressed(buff, 0, a); + return buff; } - if (!isUnit(r)) throw new Error(a.toString() + " and " + n.toString() + " are not co-prime"); - if (compare(t, 0n) === -1) { - t = t + n; + + toRprLEM(arr, offset, a) { + if (a.byteLength == this.F.n8*2) { + arr.set(a, offset); + return; + } else if (a.byteLength == this.F.n8*3) { + this.tm.setBuff(this.pOp1, a); + this.tm.instance.exports[this.prefix + "_toAffine"](this.pOp1, this.pOp1); + const res = this.tm.getBuff(this.pOp1, this.F.n8*2); + arr.set(res, offset); + } else { + throw new Error("invalid point size"); + } } - if (isNegative$3(a)) { - return -t; + + fromRprLEM(arr, offset) { + offset = offset || 0; + return arr.slice(offset, offset+this.F.n8*2); } - return t; -} -function modPow$2(n, exp, mod) { - if (mod === 0n) throw new Error("Cannot take modPow with modulus 0"); - var r = 1n, - base = n % mod; - if (isNegative$3(exp)) { - exp = exp * -1n; - base = modInv$3(base, mod); + toString(a, radix) { + if (a.byteLength == this.F.n8*3) { + const x = this.F.toString(a.slice(0, this.F.n8), radix); + const y = this.F.toString(a.slice(this.F.n8, this.F.n8*2), radix); + const z = this.F.toString(a.slice(this.F.n8*2), radix); + return `[ ${x}, ${y}, ${z} ]`; + } else if (a.byteLength == this.F.n8*2) { + const x = this.F.toString(a.slice(0, this.F.n8), radix); + const y = this.F.toString(a.slice(this.F.n8), radix); + return `[ ${x}, ${y} ]`; + } else { + throw new Error("invalid point size"); + } } - while (isPositive(exp)) { - if (base === 0n) return 0n; - if (isOdd$4(exp)) r = r * base % mod; - exp = exp / 2n; - base = square$1(base) % mod; + + isValid(a) { + if (this.isZero(a)) return true; + const F = this.F; + const aa = this.toAffine(a); + const x = aa.slice(0, this.F.n8); + const y = aa.slice(this.F.n8, this.F.n8*2); + const x3b = F.add(F.mul(F.square(x),x), this.b); + const y2 = F.square(y); + return F.eq(x3b, y2); } - return r; -} -function compareAbs(a, b) { - a = a >= 0n ? a : -a; - b = b >= 0n ? b : -b; - return a === b ? 0 : a > b ? 1 : -1; -} + fromRng(rng) { + const F = this.F; + let P = []; + let greatest; + let x3b; + do { + P[0] = F.fromRng(rng); + greatest = rng.nextBool(); + x3b = F.add(F.mul(F.square(P[0]), P[0]), this.b); + } while (!F.isSquare(x3b)); -function isDivisibleBy(a, n) { - if (n === 0n) return false; - if (isUnit(n)) return true; - if (compareAbs(n, 2n) === 0) return isEven(a); - return a % n === 0n; -} + P[1] = F.sqrt(x3b); -function isBasicPrime(v) { - var n = abs(v); - if (isUnit(n)) return false; - if (n === 2n || n === 3n || n === 5n) return true; - if (isEven(n) || isDivisibleBy(n, 3n) || isDivisibleBy(n, 5n)) return false; - if (n < 49n) return true; - // we don't know if it's prime: let the other functions figure it out -} + const s = F.isNegative(P[1]); + if (greatest ^ s) P[1] = F.neg(P[1]); -function prev(n) { - return n - 1n; -} + let Pbuff = new Uint8Array(this.F.n8*2); + Pbuff.set(P[0]); + Pbuff.set(P[1], this.F.n8); -function millerRabinTest(n, a) { - var nPrev = prev(n), - b = nPrev, - r = 0, - d, i, x; - while (isEven(b)) b = b / 2n, r++; - next: for (i = 0; i < a.length; i++) { - if (n < a[i]) continue; - x = modPow$2(BigInt(a[i]), b, n); - if (isUnit(x) || x === nPrev) continue; - for (d = r - 1; d != 0; d--) { - x = square$1(x) % n; - if (isUnit(x)) return false; - if (x === nPrev) continue next; + if (this.cofactor) { + Pbuff = this.timesScalar(Pbuff, this.cofactor); } - return false; - } - return true; -} -function isPrime$1(p) { - var isPrime = isBasicPrime(p); - if (isPrime !== undefined) return isPrime; - var n = abs(p); - var bits = bitLength$5(n); - if (bits <= 64) - return millerRabinTest(n, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]); - var logN = Math.log(2) * Number(bits); - var t = Math.ceil(logN); - for (var a = [], i = 0; i < t; i++) { - a.push(BigInt(i + 2)); + return Pbuff; } - return millerRabinTest(n, a); -} - -bigint.bitLength = bitLength$5; -bigint.isOdd = isOdd$4; -bigint.isNegative = isNegative$3; -bigint.abs = abs; -bigint.isUnit = isUnit; -bigint.compare = compare; -bigint.modInv = modInv$3; -bigint.modPow = modPow$2; -bigint.isPrime = isPrime$1; -bigint.square = square$1; -/* - Copyright 2019 0KIMS association. - This file is part of wasmsnark (Web Assembly zkSnark Prover). - wasmsnark 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. + toObject(a) { + if (this.isZero(a)) { + return [ + this.F.toObject(this.F.zero), + this.F.toObject(this.F.one), + this.F.toObject(this.F.zero), + ]; + } + const x = this.F.toObject(a.slice(0, this.F.n8)); + const y = this.F.toObject(a.slice(this.F.n8, this.F.n8*2)); + let z; + if (a.byteLength == this.F.n8*3) { + z = this.F.toObject(a.slice(this.F.n8*2, this.F.n8*3)); + } else { + z = this.F.toObject(this.F.one); + } + return [x, y, z]; + } - wasmsnark 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. + fromObject(a) { + const x = this.F.fromObject(a[0]); + const y = this.F.fromObject(a[1]); + let z; + if (a.length==3) { + z = this.F.fromObject(a[2]); + } else { + z = this.F.one; + } + if (this.F.isZero(z, this.F.one)) { + return this.zeroAffine; + } else if (this.F.eq(z, this.F.one)) { + const buff = new Uint8Array(this.F.n8*2); + buff.set(x); + buff.set(y, this.F.n8); + return buff; + } else { + const buff = new Uint8Array(this.F.n8*3); + buff.set(x); + buff.set(y, this.F.n8); + buff.set(z, this.F.n8*2); + return buff; + } + } - You should have received a copy of the GNU General Public License - along with wasmsnark. If not, see . -*/ + e(a) { + if (a instanceof Uint8Array) return a; + return this.fromObject(a); + } -const buildInt = build_int; -const utils$5 = utils$6; -const buildExp$2 = build_timesscalar; -const buildBatchInverse$2 = build_batchinverse; -const buildBatchConvertion$1 = build_batchconvertion; -const buildBatchOp = build_batchop; -const { bitLength: bitLength$4, modInv: modInv$2, modPow: modPow$1, isPrime, isOdd: isOdd$3, square } = bigint; + x(a) { + const tmp = this.toAffine(a); + return tmp.slice(0, this.F.n8); + } -var build_f1m = function buildF1m(module, _q, _prefix, _intPrefix) { - const q = BigInt(_q); - const n64 = Math.floor((bitLength$4(q - 1n) - 1)/64) +1; - const n32 = n64*2; - const n8 = n64*8; + y(a) { + const tmp = this.toAffine(a); + return tmp.slice(this.F.n8); + } - const prefix = _prefix || "f1m"; - if (module.modules[prefix]) return prefix; // already builded +} - const intPrefix = buildInt(module, n64, _intPrefix); - const pq = module.alloc(n8, utils$5.bigInt2BytesLE(q, n8)); +/* global WebAssembly */ - const pR2 = module.alloc(utils$5.bigInt2BytesLE(square(1n << BigInt(n64*64)) % q, n8)); - const pOne = module.alloc(utils$5.bigInt2BytesLE((1n << BigInt(n64*64)) % q, n8)); - const pZero = module.alloc(utils$5.bigInt2BytesLE(0n, n8)); - const _minusOne = q - 1n; - const _e = _minusOne >> 1n; // e = (p-1)/2 - const pe = module.alloc(n8, utils$5.bigInt2BytesLE(_e, n8)); +function thread(self) { + const MAXMEM = 32767; + let instance; + let memory; + let terminationTimeout = 500; // milliseconds + let terminationTimer; - const _ePlusOne = _e + 1n; // e = (p-1)/2 - const pePlusOne = module.alloc(n8, utils$5.bigInt2BytesLE(_ePlusOne, n8)); + if (self) { + self.onmessage = function(e) { + let data; + if (e.data) { + data = e.data; + } else { + data = e; + } - module.modules[prefix] = { - pq: pq, - pR2: pR2, - n64: n64, - q: q, - pOne: pOne, - pZero: pZero, - pePlusOne: pePlusOne - }; + try { + if (data[0].cmd === "INIT") { + init(data[0]).then(function() { + console.log("INIT DONE"); + self.postMessage({status: "initialized"}); + }); + } else if (data[0].cmd === "TERMINATE") { + terminate(); + } else { + if (data[data.length-1].cmd === "TERMINATE") { + data.pop(); + //terminationTimeout = 1; + } + const res = runTask(data); + //self.postMessage(res); + let transfers = []; + for (let i=0; i memory.buffer.byteLength) { + const currentPages = memory.buffer.byteLength / 0x10000; + let requiredPages = Math.floor((u32[0] + length) / 0x10000)+1; + if (requiredPages>MAXMEM) requiredPages=MAXMEM; + memory.grow(requiredPages-currentPages); + console.log("Growing memory to", memory.buffer.byteLength / 1024 / 1024, "MB"); + } + return res; } - function buildNeg() { - const f = module.addFunction(prefix+"_neg"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); - - const c = f.getCodeBuilder(); - - f.addCode( - c.call(prefix + "_sub", c.i32_const(pZero), c.getLocal("x"), c.getLocal("r")) - ); + function allocBuffer(buffer) { + const p = alloc(buffer.byteLength); + setBuffer(p, buffer); + return p; } + function getBuffer(pointer, length) { + // const u8 = new Uint8Array(memory.buffer); + // return new Uint8Array(u8.buffer, u8.byteOffset + pointer, length); + return new Uint8Array(memory.buffer, pointer, length); + } - function buildIsNegative() { - const f = module.addFunction(prefix+"_isNegative"); - f.addParam("x", "i32"); - f.setReturnType("i32"); + function setBuffer(pointer, buffer) { + const u8 = new Uint8Array(memory.buffer); + u8.set(new Uint8Array(buffer), pointer); + } - const c = f.getCodeBuilder(); + function runTask(task) { + clearTimeout(terminationTimer); + if (task[0].cmd === "INIT") { + return init(task[0]); + } + const ctx = { + vars: [], + out: [] + }; + const u32a = new Uint32Array(memory.buffer, 0, 1); + const oldAlloc = u32a[0]; + for (let i=0; i0) { + terminationTimer = setTimeout( () => { + console.log("Shutting down thread due to inactivity"); + terminate(); + }, terminationTimeout); + } } + function terminate() { + clearTimeout(terminationTimer); + instance = null; + memory = null; + if (self) { + console.log("TERMINATE"); + self.postMessage({status: "terminated"}); + self.close(); + } + } - function buildMReduct() { - const carries = module.alloc(n32*n32*8); - - const f = module.addFunction(prefix+"_mReduct"); - f.addParam("t", "i32"); - f.addParam("r", "i32"); - f.addLocal("np32", "i64"); - f.addLocal("c", "i64"); - f.addLocal("m", "i64"); - - const c = f.getCodeBuilder(); + return runTask; +} - const np32 = Number(0x100000000n - modInv$2(q, 0x100000000n)); +/* + Copyright 2019 0KIMS association. - f.addCode(c.setLocal("np32", c.i64_const(np32))); + This file is part of wasmsnark (Web Assembly zkSnark Prover). - for (let i=0; i. +*/ - f.addCode( - c.setLocal("c", - c.i64_add( - c.i64_add( - c.i64_load32_u(c.getLocal("t"), (i+j)*4), - c.i64_shr_u(c.getLocal("c"), c.i64_const(32)) - ), - c.i64_mul( - c.i64_load32_u(c.i32_const(pq), j*4), - c.getLocal("m") - ) - ) - ) - ); +// const MEM_SIZE = 1000; // Memory size in 64K Pakes (512Mb) +const MEM_SIZE = 25; // Memory size in 64K Pakes (1600Kb) - f.addCode( - c.i64_store32( - c.getLocal("t"), - (i+j)*4, - c.getLocal("c") - ) - ); - } +class Deferred { + constructor() { + this.promise = new Promise((resolve, reject)=> { + this.reject = reject; + this.resolve = resolve; + }); + } +} - f.addCode( - c.i64_store32( - c.i32_const(carries), - i*4, - c.i64_shr_u(c.getLocal("c"), c.i64_const(32)) - ) - ); - } +let workerSource; - f.addCode( - c.call( - prefix+"_add", - c.i32_const(carries), - c.i32_add( - c.getLocal("t"), - c.i32_const(n32*4) - ), - c.getLocal("r") - ) - ); +const threadStr = `(${"function thread(self) {\n const MAXMEM = 32767;\n let instance;\n let memory;\n let terminationTimeout = 500; // milliseconds\n let terminationTimer;\n\n if (self) {\n self.onmessage = function(e) {\n let data;\n if (e.data) {\n data = e.data;\n } else {\n data = e;\n }\n\n try {\n if (data[0].cmd === \"INIT\") {\n init(data[0]).then(function() {\n console.log(\"INIT DONE\");\n self.postMessage({status: \"initialized\"});\n });\n } else if (data[0].cmd === \"TERMINATE\") {\n terminate();\n } else {\n let terminateAfterTask = false;\n if (data[data.length-1].cmd === \"TERMINATE\") {\n terminateAfterTask = true;\n data.pop();\n //terminationTimeout = 1;\n }\n const res = runTask(data);\n //self.postMessage(res);\n let transfers = [];\n for (let i=0; i memory.buffer.byteLength) {\n const currentPages = memory.buffer.byteLength / 0x10000;\n let requiredPages = Math.floor((u32[0] + length) / 0x10000)+1;\n if (requiredPages>MAXMEM) requiredPages=MAXMEM;\n memory.grow(requiredPages-currentPages);\n console.log(\"Growing memory to\", memory.buffer.byteLength / 1024 / 1024, \"MB\");\n }\n return res;\n }\n\n function allocBuffer(buffer) {\n const p = alloc(buffer.byteLength);\n setBuffer(p, buffer);\n return p;\n }\n\n function getBuffer(pointer, length) {\n // const u8 = new Uint8Array(memory.buffer);\n // return new Uint8Array(u8.buffer, u8.byteOffset + pointer, length);\n return new Uint8Array(memory.buffer, pointer, length);\n }\n\n function setBuffer(pointer, buffer) {\n const u8 = new Uint8Array(memory.buffer);\n u8.set(new Uint8Array(buffer), pointer);\n }\n\n function runTask(task) {\n clearTimeout(terminationTimer);\n if (task[0].cmd === \"INIT\") {\n return init(task[0]);\n }\n const ctx = {\n vars: [],\n out: []\n };\n const u32a = new Uint32Array(memory.buffer, 0, 1);\n const oldAlloc = u32a[0];\n for (let i=0; i0) {\n terminationTimer = setTimeout( () => {\n console.log(\"Shutting down thread due to inactivity\");\n terminate();\n }, terminationTimeout);\n }\n }\n\n function terminate() {\n clearTimeout(terminationTimer);\n instance = null;\n memory = null;\n if (self) {\n console.log(\"TERMINATE\");\n self.postMessage({status: \"terminated\"});\n self.close();\n }\n }\n\n return runTask;\n}"})(self)`; +{ + if(globalThis?.Blob) { + const threadBytes= new TextEncoder().encode(threadStr); + const workerBlob = new Blob([threadBytes], { type: "application/javascript" }) ; + workerSource = URL.createObjectURL(workerBlob); + } else { + workerSource = "data:application/javascript;base64," + globalThis.btoa(threadStr); } +} - function buildMul() { +async function buildThreadManager(wasm, singleThread) { + const tm = new ThreadManager(); - const f = module.addFunction(prefix+"_mul"); - f.addParam("x", "i32"); - f.addParam("y", "i32"); - f.addParam("r", "i32"); - f.addLocal("c0", "i64"); - f.addLocal("c1", "i64"); - f.addLocal("np32", "i64"); + 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); - for (let i=0;i64) concurrency=64; + tm.concurrency = concurrency; - let c0 = "c0"; - let c1 = "c1"; + // for (let i = 0; i<1; i++) { + // + // tm.workers[i] = new Worker(workerSource); + // + // tm.workers[i].addEventListener("message", getOnMsg(i)); + // //tm.workers[i].addEventListener("error", getOnError(i)); + // + // tm.working[i]=false; + // } + // + // const initPromises = []; + // for (let i=0; i=n32) { - f.addCode( - c.i64_store32( - c.getLocal("r"), - (k-n32)*4, - c.getLocal(c0) - ) - ); + // handle status messages + if (data.status) { + if (data.status === "initialized") { + // Initialization successful message + tm.initializing[i]=false; + tm.initialized[i]=true; + } else if (data.status === "terminated") { + // Termination successful message + tm.initialized[i]=false; + tm.initializing[i]=false; + tm.workers[i]=null; + } } - [c0, c1] = [c1, c0]; - f.addCode( - c.setLocal(c1, - c.i64_shr_u( - c.getLocal(c0), - c.i64_const(32) - ) - ) - ); - } - f.addCode( - c.i64_store32( - c.getLocal("r"), - n32*4-4, - c.getLocal(c0) - ) - ); - f.addCode( - c.if( - c.i32_wrap_i64(c.getLocal(c1)), - c.drop(c.call(intPrefix+"_sub", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))), - c.if( - c.call(intPrefix+"_gte", c.getLocal("r"), c.i32_const(pq) ), - c.drop(c.call(intPrefix+"_sub", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))), - ) - ) - ); + tm.working[i]=false; + tm.pendingDeferreds[i].resolve(data); + tm.processWorks(); + }; } + getOnError(i) { + const tm = this; + return function(e) { + tm.working[i]=false; + tm.pendingDeferreds[i].reject(e.message); + throw new Error("Worker error: " + e.message); + }; + } - function buildSquare() { + startWorker(i){ + this.workers[i] = new Worker(workerSource); - const f = module.addFunction(prefix+"_square"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); - f.addLocal("c0", "i64"); - f.addLocal("c1", "i64"); - f.addLocal("c0_old", "i64"); - f.addLocal("c1_old", "i64"); - f.addLocal("np32", "i64"); + this.workers[i].addEventListener("message", this.getOnMsg(i)); + //tm.workers[i].addEventListener("error", this.getOnError(i)); + //this.working[i]=true; + this.initializing[i] = true; - for (let i=0;i { + this.initialized[i] = true; + }); + } - const np32 = Number(0x100000000n - modInv$2(q, 0x100000000n)); + startSyncOp() { + if (this.oldPFree !== 0) throw new Error("Sync operation in progress"); + this.oldPFree = this.u32[0]; + } - f.addCode(c.setLocal("np32", c.i64_const(np32))); + endSyncOp() { + if (this.oldPFree === 0) throw new Error("No sync operation in progress"); + this.u32[0] = this.oldPFree; + this.oldPFree = 0; + } + async postAction(workerId, e, transfers, _deferred) { + if (this.working[workerId]) { + throw new Error("Posting a job to a working worker"); + } + this.working[workerId] = true; - const loadX = []; - const loadQ = []; - function mulij(i, j) { - let X,Y; - if (!loadX[i]) { - X = c.teeLocal("x"+i, c.i64_load32_u( c.getLocal("x"), i*4)); - loadX[i] = true; - } else { - X = c.getLocal("x"+i); - } - if (!loadX[j]) { - Y = c.teeLocal("x"+j, c.i64_load32_u( c.getLocal("x"), j*4)); - loadX[j] = true; - } else { - Y = c.getLocal("x"+j); - } + this.pendingDeferreds[workerId] = _deferred ? _deferred : new Deferred(); + await this.workers[workerId].postMessage(e, transfers); - return c.i64_mul( X, Y ); - } + return this.pendingDeferreds[workerId].promise; + } - function mulqm(i, j) { - let Q,M; - if (!loadQ[i]) { - Q = c.teeLocal("q"+i, c.i64_load32_u(c.i32_const(0), pq+i*4 )); - loadQ[i] = true; - } else { - Q = c.getLocal("q"+i); + async processWorks() { + for (let i=0; (i 0); i++) { + if (this.workers[i] && this.initialized[i] && !this.working[i]) { + const work = this.actionQueue.shift(); + this.postAction(i, work.data, work.transfers, work.deferred); } - M = c.getLocal("m"+j); + } - return c.i64_mul( Q, M ); + // Initialize more workers if needed + if (this.actionQueue.length > 0) { + // Find a worker that is not initialized yet + let initializingCount = 0; + for (let i=0; i= this.actionQueue.length) break; + + // Initialize this worker + console.log(`Worker ${i} not initialized yet. Initializing...`); + initializingCount++; + await this.startWorker(i); + //this.startWorker(i); + } } + } + queueAction(actionData, transfers) { + const d = new Deferred(); - let c0 = "c0"; - let c1 = "c1"; - let c0_old = "c0_old"; - let c1_old = "c1_old"; + if (this.singleThread) { + const res = this.taskManager(actionData); + d.resolve(res); + } else { + this.actionQueue.push({ + data: actionData, + transfers: transfers, + deferred: d + }); + this.processWorks(); + } + return d.promise; + } - for (let k=0; k>1) )&&(i>1, k>>1) - ) - ) - ); + async terminate() { + console.log("terminate!!!"); + for (let i=0; i0) { - f.addCode( - c.setLocal(c0, - c.i64_add( - c.i64_and( - c.getLocal(c0), - c.i64_const(0xFFFFFFFF) - ), - c.i64_and( - c.getLocal(c0_old), - c.i64_const(0xFFFFFFFF) - ), - ) - ) - ); + const b = buff.slice(i*pointsPerChunk*sGin, i*pointsPerChunk*sGin + n*sGin); - f.addCode( - c.setLocal(c1, - c.i64_add( - c.i64_add( - c.getLocal(c1), - c.i64_shr_u( - c.getLocal(c0), - c.i64_const(32) - ) - ), - c.getLocal(c1_old) - ) - ) - ); + task.push({ + cmd: "ALLOCSET", + var: 0, + buff: b + }); + task.push({cmd: "ALLOCSET", var: 1, buff: t}); + task.push({cmd: "ALLOCSET", var: 2, buff: inc}); + task.push({cmd: "ALLOC", var: 3, len: n*Math.max(sGmid, sGout)}); + task.push({ + cmd: "CALL", + fnName: fnName, + params: [ + {var: 0}, + {val: n}, + {var: 1}, + {var: 2}, + {var:3} + ] + }); + if (fnAffine) { + task.push({ + cmd: "CALL", + fnName: fnAffine, + params: [ + {var: 3}, + {val: n}, + {var: 3}, + ] + }); } + task.push({cmd: "GET", out: 0, var: 3, len: n*sGout}); + opPromises.push(tm.queueAction(task, [b.buffer])); + t = Fr.mul(t, Fr.exp(inc, n)); + } - for (let i=Math.max(1, k-n32+1); (i<=k)&&(i=n32) { - f.addCode( - c.i64_store32( - c.getLocal("r"), - (k-n32)*4, - c.getLocal(c0) - ) - ); - } - f.addCode( - c.setLocal( - c0_old, - c.getLocal(c1) - ), - c.setLocal( - c1_old, - c.i64_shr_u( - c.getLocal(c0_old), - c.i64_const(32) - ) - ) - ); - } - f.addCode( - c.i64_store32( - c.getLocal("r"), - n32*4-4, - c.getLocal(c0_old) - ) - ); + const res = tm.getBuff(pRes, curve.Gt.n8); - f.addCode( - c.if( - c.i32_wrap_i64(c.getLocal(c1_old)), - c.drop(c.call(intPrefix+"_sub", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))), - c.if( - c.call(intPrefix+"_gte", c.getLocal("r"), c.i32_const(pq) ), - c.drop(c.call(intPrefix+"_sub", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))), - ) - ) - ); - } + tm.endSyncOp(); + return res; + }; + curve.pairingEq = async function pairingEq() { + let buffCt; + let nEqs; + if ((arguments.length % 2) == 1) { + buffCt = arguments[arguments.length-1]; + nEqs = (arguments.length -1) /2; + } else { + buffCt = curve.Gt.one; + nEqs = arguments.length /2; + } - function buildSquareOld() { - const f = module.addFunction(prefix+"_squareOld"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + const opPromises = []; + for (let i=0; i> 1n; - } - const pt = module.alloc(n8, utils$5.bigInt2BytesLE(_t, n8)); + tm.endSyncOp(); - const _nqrToT = modPow$1(_nqr, _t, q); - const pNqrToT = module.alloc(utils$5.bigInt2BytesLE((_nqrToT << BigInt(n64*64)) % q, n8)); + return r; + }; - const _tPlusOneOver2 = (_t + 1n) >> 1n; - const ptPlusOneOver2 = module.alloc(n8, utils$5.bigInt2BytesLE(_tPlusOneOver2, n8)); + 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; + }; - function buildSqrt() { + 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; + }; - const f = module.addFunction(prefix+ "_sqrt"); - f.addParam("n", "i32"); - f.addParam("r", "i32"); - f.addLocal("m", "i32"); - f.addLocal("i", "i32"); - f.addLocal("j", "i32"); + 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; + }; - const c = f.getCodeBuilder(); + 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; + }; - const ONE = c.i32_const(pOne); - const C = c.i32_const(module.alloc(n8)); - const T = c.i32_const(module.alloc(n8)); - const R = c.i32_const(module.alloc(n8)); - const SQ = c.i32_const(module.alloc(n8)); - const B = c.i32_const(module.alloc(n8)); +} - f.addCode( +/* eslint-disable indent */ - // If (n==0) return 0 - c.if( - c.call(prefix + "_isZero", c.getLocal("n")), - c.ret( - c.call(prefix + "_zero", c.getLocal("r")) - ) - ), +const pTSizes = [ + 1 , 1, 1, 1, 2, 3, 4, 5, + 6 , 7, 7, 8, 9, 10, 11, 12, + 13, 13, 14, 15, 16, 16, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17 +]; - c.setLocal("m", c.i32_const(s2)), - c.call(prefix + "_copy", c.i32_const(pNqrToT), C), - c.call(prefix + "_exp", c.getLocal("n"), c.i32_const(pt), c.i32_const(n8), T), - c.call(prefix + "_exp", c.getLocal("n"), c.i32_const(ptPlusOneOver2), c.i32_const(n8), R), +function buildMultiexp$1(curve, groupName) { + const G = curve[groupName]; + const tm = G.tm; - c.block(c.loop( - c.br_if(1, c.call(prefix + "_eq", T, ONE)), + async function _multiExpChunk(buffBases, buffScalars, inType, logger, logText) { + if ( ! (buffBases instanceof Uint8Array) ) { + if (logger) logger.error(`${logText} _multiExpChunk buffBases is not Uint8Array`); + throw new Error(`${logText} _multiExpChunk buffBases is not Uint8Array`); + } + if ( ! (buffScalars instanceof Uint8Array) ) { + if (logger) logger.error(`${logText} _multiExpChunk buffScalars is not Uint8Array`); + throw new Error(`${logText} _multiExpChunk buffScalars is not Uint8Array`); + } + inType = inType || "affine"; - c.call(prefix + "_square", T, SQ), - c.setLocal("i", c.i32_const(1)), - c.block(c.loop( - c.br_if(1, c.call(prefix + "_eq", SQ, ONE)), - c.call(prefix + "_square", SQ, SQ), - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )), + let sGIn; + let fnName; + if (groupName === "G1") { + if (inType === "affine") { + fnName = "g1m_multiexpAffine"; + sGIn = G.F.n8*2; + } else { + fnName = "g1m_multiexp"; + sGIn = G.F.n8*3; + } + } else if (groupName === "G2") { + if (inType === "affine") { + fnName = "g2m_multiexpAffine"; + sGIn = G.F.n8*2; + } else { + fnName = "g2m_multiexp"; + sGIn = G.F.n8*3; + } + } else { + throw new Error("Invalid group"); + } + const nPoints = Math.floor(buffBases.byteLength / sGIn); - c.call(prefix + "_copy", C, B), - c.setLocal("j", c.i32_sub(c.i32_sub( c.getLocal("m"), c.getLocal("i")), c.i32_const(1)) ), - c.block(c.loop( - c.br_if(1, c.i32_eqz(c.getLocal("j"))), - c.call(prefix + "_square", B, B), - c.setLocal("j", c.i32_sub(c.getLocal("j"), c.i32_const(1))), - c.br(0) - )), + if (nPoints === 0) return G.zero; + const sScalar = Math.floor(buffScalars.byteLength / nPoints); + if( sScalar * nPoints !== buffScalars.byteLength) { + throw new Error("Scalar size does not match"); + } - c.setLocal("m", c.getLocal("i")), - c.call(prefix + "_square", B, C), - c.call(prefix + "_mul", T, C, T), - c.call(prefix + "_mul", R, B, R), + const bitChunkSize = pTSizes[log2(nPoints)]; - c.br(0) - )), + const opPromises = []; - c.if( - c.call(prefix + "_isNegative", R), - c.call(prefix + "_neg", R, c.getLocal("r")), - c.call(prefix + "_copy", R, c.getLocal("r")), - ) + const task = [ + {cmd: "ALLOCSET", var: 0, buff: buffBases}, + {cmd: "ALLOCSET", var: 1, buff: buffScalars}, + {cmd: "ALLOC", var: 2, len: G.F.n8*3}, + {cmd: "CALL", fnName: fnName, params: [ + {var: 0}, //pBases + {var: 1}, // pScalars + {val: sScalar}, // scalarSize + {val: nPoints}, // nPoints + {var: 2} // pr + ]}, + {cmd: "GET", out: 0, var: 2, len: G.F.n8*3} + ]; + opPromises.push( + // transfer ownership of the buffers to the worker thread + G.tm.queueAction(task, [buffBases.buffer, buffScalars.buffer]) ); + + const result = await Promise.all(opPromises); + + let res = G.zero; + for (let i=result.length-1; i>=0; i--) { + if (!G.isZero(res)) { + for (let j=0; jMAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; + if (chunkSize { + if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); + return r; + })); + } - const c = f.getCodeBuilder(); + let result = await Promise.all(opPromises); - const AUX = c.i32_const(module.alloc(n8)); + let res = G.zero; + for (let i=result.length-1; i>=0; i--) { + res = G.add(res, result[i]); + } - f.addCode( - c.call(prefix + "_load", c.getLocal("scalar"), c.getLocal("scalarLen"), AUX), - c.call(prefix + "_toMontgomery", AUX, AUX), - c.call(prefix + "_mul", c.getLocal("x"), AUX, c.getLocal("r")), - ); + return res; } - function buildIsOne() { - const f = module.addFunction(prefix+"_isOne"); - f.addParam("x", "i32"); - f.setReturnType("i32"); + G.multiExp = async function multiExpAffine(buffBases, buffScalars, logger, logText) { + return await _multiExp(buffBases, buffScalars, "jacobian", logger, logText); + }; + G.multiExpAffine = async function multiExpAffine(buffBases, buffScalars, logger, logText) { + return await _multiExp(buffBases, buffScalars, "affine", logger, logText); + }; +} - const c = f.getCodeBuilder(); - f.addCode( - c.ret(c.call(intPrefix + "_eq", c.getLocal("x"), c.i32_const(pOne))) - ); - } +function buildFFT$2(curve, groupName) { + const G = curve[groupName]; + const Fr = curve.Fr; + const tm = G.tm; + async function _fft(buff, inverse, inType, outType, logger, loggerTxt) { + inType = inType || "affine"; + outType = outType || "affine"; + const MAX_BITS_THREAD = 14; - module.exportFunction(intPrefix + "_copy", prefix+"_copy"); - module.exportFunction(intPrefix + "_zero", prefix+"_zero"); - module.exportFunction(intPrefix + "_isZero", prefix+"_isZero"); - module.exportFunction(intPrefix + "_eq", prefix+"_eq"); + let sIn, sMid, sOut, fnIn2Mid, fnMid2Out, fnFFTMix, fnFFTJoin, fnFFTFinal, fnReversePermutation; + if (groupName == "G1") { + if (inType == "affine") { + sIn = G.F.n8*2; + fnIn2Mid = "g1m_batchToJacobian"; + } else { + sIn = G.F.n8*3; + } + sMid = G.F.n8*3; + if (inverse) { + fnFFTFinal = "g1m_fftFinal"; + } + fnFFTJoin = "g1m_fftJoin"; + fnFFTMix = "g1m_fftMix"; + fnReversePermutation = "g1m_reversePermutation"; - buildIsOne(); - buildAdd(); - buildSub(); - buildNeg(); - buildMReduct(); - buildMul(); - buildSquare(); - buildSquareOld(); - buildToMontgomery(); - buildFromMontgomery(); - buildIsNegative(); - buildSign(); - buildInverse(); - buildOne(); - buildLoad(); - buildTimesScalar(); - buildBatchInverse$2(module, prefix); - buildBatchConvertion$1(module, prefix + "_batchToMontgomery", prefix + "_toMontgomery", n8, n8); - buildBatchConvertion$1(module, prefix + "_batchFromMontgomery", prefix + "_fromMontgomery", n8, n8); - buildBatchConvertion$1(module, prefix + "_batchNeg", prefix + "_neg", n8, n8); - buildBatchOp(module, prefix + "_batchAdd", prefix + "_add", n8, n8); - buildBatchOp(module, prefix + "_batchSub", prefix + "_sub", n8, n8); - buildBatchOp(module, prefix + "_batchMul", prefix + "_mul", n8, n8); - - module.exportFunction(prefix + "_add"); - module.exportFunction(prefix + "_sub"); - module.exportFunction(prefix + "_neg"); - module.exportFunction(prefix + "_isNegative"); - module.exportFunction(prefix + "_isOne"); - module.exportFunction(prefix + "_sign"); - module.exportFunction(prefix + "_mReduct"); - module.exportFunction(prefix + "_mul"); - module.exportFunction(prefix + "_square"); - module.exportFunction(prefix + "_squareOld"); - module.exportFunction(prefix + "_fromMontgomery"); - module.exportFunction(prefix + "_toMontgomery"); - module.exportFunction(prefix + "_inverse"); - module.exportFunction(prefix + "_one"); - module.exportFunction(prefix + "_load"); - module.exportFunction(prefix + "_timesScalar"); - buildExp$2( - module, - prefix + "_exp", - n8, - prefix + "_mul", - prefix + "_square", - intPrefix + "_copy", - prefix + "_one", - ); - module.exportFunction(prefix + "_exp"); - module.exportFunction(prefix + "_batchInverse"); - if (isPrime(q)) { - buildSqrt(); - buildIsSquare(); - module.exportFunction(prefix + "_sqrt"); - module.exportFunction(prefix + "_isSquare"); - } - module.exportFunction(prefix + "_batchToMontgomery"); - module.exportFunction(prefix + "_batchFromMontgomery"); - // console.log(module.functionIdxByName); - - return prefix; -}; - -/* - Copyright 2019 0KIMS association. - - This file is part of wasmsnark (Web Assembly zkSnark Prover). - - wasmsnark 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. + if (outType == "affine") { + sOut = G.F.n8*2; + fnMid2Out = "g1m_batchToAffine"; + } else { + sOut = G.F.n8*3; + } - wasmsnark 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. + } else if (groupName == "G2") { + if (inType == "affine") { + sIn = G.F.n8*2; + fnIn2Mid = "g2m_batchToJacobian"; + } else { + sIn = G.F.n8*3; + } + sMid = G.F.n8*3; + if (inverse) { + fnFFTFinal = "g2m_fftFinal"; + } + fnFFTJoin = "g2m_fftJoin"; + fnFFTMix = "g2m_fftMix"; + fnReversePermutation = "g2m_reversePermutation"; + if (outType == "affine") { + sOut = G.F.n8*2; + fnMid2Out = "g2m_batchToAffine"; + } else { + sOut = G.F.n8*3; + } + } else if (groupName == "Fr") { + sIn = G.n8; + sMid = G.n8; + sOut = G.n8; + if (inverse) { + fnFFTFinal = "frm_fftFinal"; + } + fnFFTMix = "frm_fftMix"; + fnFFTJoin = "frm_fftJoin"; + fnReversePermutation = "frm_fftReversePermutation"; + } - You should have received a copy of the GNU General Public License - along with wasmsnark. If not, see . -*/ -const buildF1m$2 =build_f1m; -const { bitLength: bitLength$3 } = bigint; + let returnArray = false; + if (Array.isArray(buff)) { + buff = array2buffer(buff, sIn); + returnArray = true; + } else { + buff = buff.slice(0, buff.byteLength); + } -var build_f1 = function buildF1(module, _q, _prefix, _f1mPrefix, _intPrefix) { + console.log("FFT input size:", buff.byteLength, " bytes"); - const q = BigInt(_q); - const n64 = Math.floor((bitLength$3(q - 1n) - 1)/64) +1; - const n8 = n64*8; + const nPoints = buff.byteLength / sIn; + const bits = log2(nPoints); - const prefix = _prefix || "f1"; - if (module.modules[prefix]) return prefix; // already builded - module.modules[prefix] = { - n64: n64 - }; + console.log("FFT points:", nPoints, " bits:", bits); - const intPrefix = _intPrefix || "int"; - const f1mPrefix = buildF1m$2(module, q, _f1mPrefix, intPrefix); + if ((1 << bits) != nPoints) { + throw new Error("fft must be multiple of 2" ); + } + if (bits == Fr.s +1) { + let buffOut; - const pR2 = module.modules[f1mPrefix].pR2; - const pq = module.modules[f1mPrefix].pq; - const pePlusOne = module.modules[f1mPrefix].pePlusOne; + if (inverse) { + buffOut = await _fftExtInv(buff, inType, outType, logger, loggerTxt); + } else { + buffOut = await _fftExt(buff, inType, outType, logger, loggerTxt); + } - function buildMul() { - const pAux1 = module.alloc(n8); + if (returnArray) { + return buffer2array(buffOut, sOut); + } else { + return buffOut; + } + } - const f = module.addFunction(prefix+ "_mul"); - f.addParam("x", "i32"); - f.addParam("y", "i32"); - f.addParam("r", "i32"); + let inv; + if (inverse) { + inv = Fr.inv(Fr.e(nPoints)); + } - const c = f.getCodeBuilder(); - f.addCode(c.call(f1mPrefix + "_mul", c.getLocal("x"), c.getLocal("y"), c.i32_const(pAux1))); - f.addCode(c.call(f1mPrefix + "_mul", c.i32_const(pAux1), c.i32_const(pR2), c.getLocal("r"))); - } + let buffOut; - function buildSquare() { - const f = module.addFunction(prefix+"_square"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + // TODO: optimize. Move to wasm? + //buffReverseBits(buff, sIn); - const c = f.getCodeBuilder(); + console.log("fnReversePermutation:", fnReversePermutation); - f.addCode(c.call(prefix + "_mul", c.getLocal("x"), c.getLocal("x"), c.getLocal("r"))); - } + const task = []; + task.push({cmd: "ALLOC", var: 0, len: buff.byteLength}); + task.push({cmd: "SET", var: 0, buff: buff}); + task.push({cmd: "CALL", fnName: fnReversePermutation, params: [{var:0}, {val: bits}, {var: 0}]}); + task.push({cmd: "GET", out:0, var: 0, len: buff.byteLength}); + const res = await tm.queueAction(task, [buff.buffer]); + buff.set(res[0]); - function buildInverse() { + let chunks; + let pointsInChunk = Math.min(1 << MAX_BITS_THREAD, nPoints); + let nChunks = nPoints / pointsInChunk; - const f = module.addFunction(prefix+ "_inverse"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + while ((nChunks < tm.concurrency)&&(pointsInChunk>=16)) { + nChunks *= 2; + pointsInChunk /= 2; + } - const c = f.getCodeBuilder(); - f.addCode(c.call(intPrefix + "_inverseMod", c.getLocal("x"), c.i32_const(pq), c.getLocal("r"))); - } + const l2Chunk = log2(pointsInChunk); - function buildIsNegative() { - const f = module.addFunction(prefix+"_isNegative"); - f.addParam("x", "i32"); - f.setReturnType("i32"); + const promises = []; + for (let i = 0; i< nChunks; i++) { + if (logger) logger.debug(`${loggerTxt}: fft ${bits} mix start: ${i}/${nChunks}`); + 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}]}); + } + for (let j=1; j<=l2Chunk;j++) { + task.push({cmd: "CALL", fnName:fnFFTMix, params: [{var:0}, {val: pointsInChunk}, {val: j}]}); + } - const c = f.getCodeBuilder(); + if (l2Chunk==bits) { + if (fnFFTFinal) { + task.push({cmd: "ALLOCSET", var: 1, buff: inv}); + task.push({cmd: "CALL", fnName: fnFFTFinal, params:[ + {var: 0}, + {val: pointsInChunk}, + {var: 1}, + ]}); + } + if (fnMid2Out) { + task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:0}, {val: pointsInChunk}, {var: 0}]}); + } + task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sOut}); + } else { + task.push({cmd: "GET", out:0, var: 0, len: sMid*pointsInChunk}); + } + promises.push(tm.queueAction(task, [buffChunk.buffer]).then( (r) => { + if (logger) logger.debug(`${loggerTxt}: fft ${bits} mix end: ${i}/${nChunks}`); + return r; + })); + } - f.addCode( - c.call(intPrefix + "_gte", c.getLocal("x"), c.i32_const(pePlusOne) ) - ); - } + chunks = await Promise.all(promises); + for (let i = 0; i< nChunks; i++) chunks[i] = chunks[i][0]; + for (let i = l2Chunk+1; i<=bits; i++) { + if (logger) logger.debug(`${loggerTxt}: fft ${bits} join: ${i}/${bits}`); + const nGroups = 1 << (bits - i); + const nChunksPerGroup = nChunks / nGroups; + const opPromises = []; + for (let j=0; j { + if (logger) logger.debug(`${loggerTxt}: fft ${bits} join ${i}/${bits} ${j+1}/${nGroups} ${k}/${nChunksPerGroup/2}`); + return r; + })); + } + } - return prefix; -}; + const res = await Promise.all(opPromises); + for (let j=0; j0; i--) { + buffOut.set(chunks[i], p); + p += pointsInChunk*sOut; + delete chunks[i]; // Liberate mem + } + buffOut.set(chunks[0].slice(0, (pointsInChunk-1)*sOut), p); + delete chunks[0]; + } else { + for (let i=0; i. -*/ + [b1, b2] = await _fftJoinExt(b1, b2, "fftJoinExt", Fr.one, Fr.shift, inType, "jacobian", logger, loggerTxt); -const buildExp$1 = build_timesscalar; -const buildBatchInverse$1 = build_batchinverse; -const utils$4 = utils$6; + promises.push( _fft(b1, false, "jacobian", outType, logger, loggerTxt)); + promises.push( _fft(b2, false, "jacobian", outType, logger, loggerTxt)); -var build_f2m = function buildF2m(module, mulNonResidueFn, prefix, f1mPrefix) { + const res1 = await Promise.all(promises); - if (module.modules[prefix]) return prefix; // already builded + let buffOut; + if (res1[0].byteLength > (1<<28)) { + buffOut = new BigBuffer(res1[0].byteLength*2); + } else { + buffOut = new Uint8Array(res1[0].byteLength*2); + } - const f1n8 = module.modules[f1mPrefix].n64*8; - const q = module.modules[f1mPrefix].q; + buffOut.set(res1[0]); + buffOut.set(res1[1], res1[0].byteLength); - module.modules[prefix] = { - n64: module.modules[f1mPrefix].n64*2 - }; + return buffOut; + } - function buildAdd() { - const f = module.addFunction(prefix+"_add"); - f.addParam("x", "i32"); - f.addParam("y", "i32"); - f.addParam("r", "i32"); + async function _fftExtInv(buff, inType, outType, logger, loggerTxt) { + let b1, b2; + b1 = buff.slice( 0 , buff.byteLength/2); + b2 = buff.slice( buff.byteLength/2, buff.byteLength); - const c = f.getCodeBuilder(); + const promises = []; - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - const y0 = c.getLocal("y"); - const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8)); - const r0 = c.getLocal("r"); - const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + promises.push( _fft(b1, true, inType, "jacobian", logger, loggerTxt)); + promises.push( _fft(b2, true, inType, "jacobian", logger, loggerTxt)); - f.addCode( - c.call(f1mPrefix+"_add", x0, y0, r0), - c.call(f1mPrefix+"_add", x1, y1, r1), - ); - } + [b1, b2] = await Promise.all(promises); - function buildTimesScalar() { - const f = module.addFunction(prefix+"_timesScalar"); - f.addParam("x", "i32"); - f.addParam("scalar", "i32"); - f.addParam("scalarLen", "i32"); - f.addParam("r", "i32"); + const res1 = await _fftJoinExt(b1, b2, "fftJoinExtInv", Fr.one, Fr.shiftInv, "jacobian", outType, logger, loggerTxt); - const c = f.getCodeBuilder(); + let buffOut; + if (res1[0].byteLength > (1<<28)) { + buffOut = new BigBuffer(res1[0].byteLength*2); + } else { + buffOut = new Uint8Array(res1[0].byteLength*2); + } - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - const r0 = c.getLocal("r"); - const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + buffOut.set(res1[0]); + buffOut.set(res1[1], res1[0].byteLength); - f.addCode( - c.call(f1mPrefix+"_timesScalar", x0, c.getLocal("scalar"), c.getLocal("scalarLen"), r0), - c.call(f1mPrefix+"_timesScalar", x1, c.getLocal("scalar"), c.getLocal("scalarLen"), r1), - ); + return buffOut; } - function buildSub() { - const f = module.addFunction(prefix+"_sub"); - f.addParam("x", "i32"); - f.addParam("y", "i32"); - f.addParam("r", "i32"); - const c = f.getCodeBuilder(); + async function _fftJoinExt(buff1, buff2, fn, first, inc, inType, outType, logger, loggerTxt) { + const MAX_CHUNK_SIZE = 1<<16; + const MIN_CHUNK_SIZE = 1<<4; - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - const y0 = c.getLocal("y"); - const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8)); - const r0 = c.getLocal("r"); - const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + let fnName; + let fnIn2Mid, fnMid2Out; + let sOut, sIn, sMid; - f.addCode( - c.call(f1mPrefix+"_sub", x0, y0, r0), - c.call(f1mPrefix+"_sub", x1, y1, r1), - ); - } + if (groupName == "G1") { + if (inType == "affine") { + sIn = G.F.n8*2; + fnIn2Mid = "g1m_batchToJacobian"; + } else { + sIn = G.F.n8*3; + } + sMid = G.F.n8*3; + fnName = "g1m_"+fn; + if (outType == "affine") { + fnMid2Out = "g1m_batchToAffine"; + sOut = G.F.n8*2; + } else { + sOut = G.F.n8*3; + } + } else if (groupName == "G2") { + if (inType == "affine") { + sIn = G.F.n8*2; + fnIn2Mid = "g2m_batchToJacobian"; + } else { + sIn = G.F.n8*3; + } + fnName = "g2m_"+fn; + sMid = G.F.n8*3; + if (outType == "affine") { + fnMid2Out = "g2m_batchToAffine"; + sOut = G.F.n8*2; + } else { + sOut = G.F.n8*3; + } + } else if (groupName == "Fr") { + sIn = Fr.n8; + sOut = Fr.n8; + sMid = Fr.n8; + fnName = "frm_" + fn; + } else { + throw new Error("Invalid group"); + } - function buildNeg() { - const f = module.addFunction(prefix+"_neg"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + if (buff1.byteLength != buff2.byteLength) { + throw new Error("Invalid buffer size"); + } + const nPoints = Math.floor(buff1.byteLength / sIn); + if (nPoints != 1 << log2(nPoints)) { + throw new Error("Invalid number of points"); + } - const c = f.getCodeBuilder(); + let chunkSize = Math.floor(nPoints /tm.concurrency); + if (chunkSize < MIN_CHUNK_SIZE) chunkSize = MIN_CHUNK_SIZE; + if (chunkSize > MAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - const r0 = c.getLocal("r"); - const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + const opPromises = []; - f.addCode( - c.call(f1mPrefix+"_neg", x0, r0), - c.call(f1mPrefix+"_neg", x1, r1), - ); - } - - function buildConjugate() { - const f = module.addFunction(prefix+"_conjugate"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); - - const c = f.getCodeBuilder(); + for (let i=0; i { + if (logger) logger.debug(`${loggerTxt}: fftJoinExt End: ${i}/${nPoints}`); + return r; + }) + ); + } - function buildIsNegative() { - const f = module.addFunction(prefix+"_isNegative"); - f.addParam("x", "i32"); - f.setReturnType("i32"); + const result = await Promise.all(opPromises); - const c = f.getCodeBuilder(); + let fullBuffOut1; + let fullBuffOut2; + if (nPoints * sOut > 1<<28) { + fullBuffOut1 = new BigBuffer(nPoints*sOut); + fullBuffOut2 = new BigBuffer(nPoints*sOut); + } else { + fullBuffOut1 = new Uint8Array(nPoints*sOut); + fullBuffOut2 = new Uint8Array(nPoints*sOut); + } - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + let p =0; + for (let i=0; i Fr.s+1) { + if (logger) logger.error("lagrangeEvaluations input too big"); + throw new Error("lagrangeEvaluations input too big"); + } - f.addCode( - c.call(f1mPrefix + "_mul", x0, y, r0), // A = x0*y - c.call(f1mPrefix + "_mul", x1, y, r1), // B = x1*y - ); - } + let t0 = buff.slice(0, buff.byteLength/2); + let t1 = buff.slice(buff.byteLength/2, buff.byteLength); - function buildSquare() { - const f = module.addFunction(prefix+"_square"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); - const c = f.getCodeBuilder(); + const shiftToSmallM = Fr.exp(Fr.shift, nPoints/2); + const sConst = Fr.inv( Fr.sub(Fr.one, shiftToSmallM)); - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - const r0 = c.getLocal("r"); - const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + [t0, t1] = await _fftJoinExt(t0, t1, "prepareLagrangeEvaluation", sConst, Fr.shiftInv, inType, "jacobian", logger, loggerTxt + " prep"); - const AB = c.i32_const(module.alloc(f1n8)); - const APB = c.i32_const(module.alloc(f1n8)); - const APNB = c.i32_const(module.alloc(f1n8)); - const ABPNAB = c.i32_const(module.alloc(f1n8)); + const promises = []; + promises.push( _fft(t0, true, "jacobian", outType, logger, loggerTxt + " t0")); + promises.push( _fft(t1, true, "jacobian", outType, logger, loggerTxt + " t1")); - f.addCode( - // AB = x0*y1 - c.call(f1mPrefix + "_mul", x0, x1, AB), + [t0, t1] = await Promise.all(promises); - // APB = x0+y1 - c.call(f1mPrefix + "_add", x0, x1, APB), + let buffOut; + if (t0.byteLength > (1<<28)) { + buffOut = new BigBuffer(t0.byteLength*2); + } else { + buffOut = new Uint8Array(t0.byteLength*2); + } - // APBN0 = x0 + nr*x1 - c.call(mulNonResidueFn, x1, APNB), - c.call(f1mPrefix + "_add", x0, APNB, APNB), + buffOut.set(t0); + buffOut.set(t1, t0.byteLength); - // ABPNAB = ab + nr*ab - c.call(mulNonResidueFn, AB, ABPNAB), - c.call(f1mPrefix + "_add", ABPNAB, AB, ABPNAB), + return buffOut; + }; - // r0 = APB * APNB - ABPNAB - c.call(f1mPrefix + "_mul", APB, APNB, r0), - c.call(f1mPrefix + "_sub", r0, ABPNAB, r0), + G.fftMix = async function fftMix(buff) { + const sG = G.F.n8*3; + let fnName, fnFFTJoin; + if (groupName == "G1") { + fnName = "g1m_fftMix"; + fnFFTJoin = "g1m_fftJoin"; + } else if (groupName == "G2") { + fnName = "g2m_fftMix"; + fnFFTJoin = "g2m_fftJoin"; + } else if (groupName == "Fr") { + fnName = "frm_fftMix"; + fnFFTJoin = "frm_fftJoin"; + } else { + throw new Error("Invalid group"); + } - // r1 = AB + AB - c.call(f1mPrefix + "_add", AB, AB, r1), - ); + const nPoints = Math.floor(buff.byteLength / sG); + const power = log2(nPoints); - } + let nChunks = 1 << log2(tm.concurrency); + if (nPoints <= nChunks*2) nChunks = 1; - function buildToMontgomery() { - const f = module.addFunction(prefix+"_toMontgomery"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + const pointsPerChunk = nPoints / nChunks; - const c = f.getCodeBuilder(); + const powerChunk = log2(pointsPerChunk); - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - const r0 = c.getLocal("r"); - const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); - - f.addCode( - c.call(f1mPrefix+"_toMontgomery", x0, r0), - c.call(f1mPrefix+"_toMontgomery", x1, r1) - ); - } - - function buildFromMontgomery() { - const f = module.addFunction(prefix+"_fromMontgomery"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); - - const c = f.getCodeBuilder(); + const opPromises = []; + for (let i=0; i=0; i--) { + fullBuffOut.set(result[i][0], p); + p+=result[i][0].byteLength; + } - const c = f.getCodeBuilder(); + return fullBuffOut; + }; +} - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); +async function buildEngine(params) { - f.addCode( - c.setLocal("s" , c.call( f1mPrefix + "_sign", x1)), - c.if( - c.getLocal("s"), - c.ret(c.getLocal("s")) - ), - c.ret(c.call( f1mPrefix + "_sign", x0)) - ); - } + const tm = await buildThreadManager(params.wasm, params.singleThread); - function buildIsOne() { - const f = module.addFunction(prefix+"_isOne"); - f.addParam("x", "i32"); - f.setReturnType("i32"); - const c = f.getCodeBuilder(); + const curve = {}; - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + curve.q = e(params.wasm.q.toString()); + curve.r = e(params.wasm.r.toString()); + curve.name = params.name; + curve.tm = tm; + curve.prePSize = params.wasm.prePSize; + curve.preQSize = params.wasm.preQSize; + curve.Fr = new WasmField1(tm, "frm", params.n8r, params.r); + curve.F1 = new WasmField1(tm, "f1m", params.n8q, params.q); + curve.F2 = new WasmField2(tm, "f2m", curve.F1); + curve.G1 = new WasmCurve(tm, "g1m", curve.F1, params.wasm.pG1gen, params.wasm.pG1b, params.cofactorG1); + curve.G2 = new WasmCurve(tm, "g2m", curve.F2, params.wasm.pG2gen, params.wasm.pG2b, params.cofactorG2); + curve.F6 = new WasmField3(tm, "f6m", curve.F2); + curve.F12 = new WasmField2(tm, "ftm", curve.F6); - f.addCode( - c.ret(c.i32_and( - c.call(f1mPrefix + "_isOne", x0), - c.call(f1mPrefix + "_isZero", x1), - )) - ); - } + curve.Gt = curve.F12; + buildBatchApplyKey(curve, "G1"); + buildBatchApplyKey(curve, "G2"); + buildBatchApplyKey(curve, "Fr"); - // Check here: https://eprint.iacr.org/2012/685.pdf - // Alg 9adj - function buildSqrt() { + buildMultiexp$1(curve, "G1"); + buildMultiexp$1(curve, "G2"); - const f = module.addFunction(prefix+"_sqrt"); - f.addParam("a", "i32"); - f.addParam("pr", "i32"); + buildFFT$2(curve, "G1"); + buildFFT$2(curve, "G2"); + buildFFT$2(curve, "Fr"); - const c = f.getCodeBuilder(); + buildPairing(curve); - // BigInt can't take `undefined` so we use `|| 0` - const e34 = c.i32_const(module.alloc(utils$4.bigInt2BytesLE((BigInt(q || 0) - 3n) / 4n, f1n8 ))); - // BigInt can't take `undefined` so we use `|| 0` - const e12 = c.i32_const(module.alloc(utils$4.bigInt2BytesLE((BigInt(q || 0) - 1n) / 2n, f1n8 ))); + curve.array2buffer = function(arr, sG) { + const buff = new Uint8Array(sG*arr.length); - const a = c.getLocal("a"); - const a1 = c.i32_const(module.alloc(f1n8*2)); - const alpha = c.i32_const(module.alloc(f1n8*2)); - const a0 = c.i32_const(module.alloc(f1n8*2)); - const pn1 = module.alloc(f1n8*2); - const n1 = c.i32_const(pn1); - const n1a = c.i32_const(pn1); - const n1b = c.i32_const(pn1+f1n8); - const x0 = c.i32_const(module.alloc(f1n8*2)); - const b = c.i32_const(module.alloc(f1n8*2)); + for (let i=0; i= 0) { + curve = await buildBn128$1(singleThread, plugins); + } else if (["BLS12381"].indexOf(normName) >= 0) { + curve = await buildBls12381$1(singleThread, plugins); + } else { + throw new Error(`Curve not supported: ${name}`); + } + return curve; + + function normalizeName(n) { + return n.toUpperCase().match(/[A-Za-z0-9]+/g).join(""); + } + +} + +const Scalar=_Scalar; +const utils$6 = _utils; + +var bn128_wasm_gzip$1 = {}; + +bn128_wasm_gzip$1.gzipCode = "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"; + bn128_wasm_gzip$1.pq = 488; + bn128_wasm_gzip$1.pr = 1768; + bn128_wasm_gzip$1.pG1gen = 31432; + bn128_wasm_gzip$1.pG1zero = 31528; + bn128_wasm_gzip$1.pG1b = 3080; + bn128_wasm_gzip$1.pG2gen = 31624; + bn128_wasm_gzip$1.pG2zero = 31816; + bn128_wasm_gzip$1.pG2b = 12456; + bn128_wasm_gzip$1.pOneT = 32008; + bn128_wasm_gzip$1.prePSize = 192; + bn128_wasm_gzip$1.preQSize = 19776; + bn128_wasm_gzip$1.n8q = 32; + bn128_wasm_gzip$1.n8r = 32; + bn128_wasm_gzip$1.q = "21888242871839275222246405745257275088696311157297823662689037894645226208583"; + bn128_wasm_gzip$1.r = "21888242871839275222246405745257275088548364400416034343698204186575808495617"; + +var utils$5 = {}; /* Copyright 2019 0KIMS association. @@ -6272,1276 +6297,1124 @@ var build_f2m = function buildF2m(module, mulNonResidueFn, prefix, f1mPrefix) { along with wasmsnark. If not, see . */ -const buildExp = build_timesscalar; -const buildBatchInverse = build_batchinverse; +utils$5.bigInt2BytesLE = function bigInt2BytesLE(_a, len) { + const b = Array(len); + let v = BigInt(_a); + for (let i=0; i> 8n; + } + return b; +}; -var build_f3m = function buildF3m(module, mulNonResidueFn, prefix, f1mPrefix) { +utils$5.bigInt2U32LE = function bigInt2BytesLE(_a, len) { + const b = Array(len); + let v = BigInt(_a); + for (let i=0; i> 32n; + } + return b; +}; + +utils$5.isOcamNum = function(a) { + if (!Array.isArray(a)) return false; + if (a.length != 3) return false; + if (typeof a[0] !== "number") return false; + if (typeof a[1] !== "number") return false; + if (!Array.isArray(a[2])) return false; + return true; +}; + +/* + Copyright 2019 0KIMS association. + + This file is part of wasmsnark (Web Assembly zkSnark Prover). + + wasmsnark 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. + + wasmsnark 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 wasmsnark. If not, see . +*/ + +var build_int = function buildInt(module, n64, _prefix) { + const prefix = _prefix || "int"; if (module.modules[prefix]) return prefix; // already builded + module.modules[prefix] = {}; - const f1n8 = module.modules[f1mPrefix].n64*8; - module.modules[prefix] = { - n64: module.modules[f1mPrefix].n64*3 - }; + const n32 = n64*2; + const n8 = n64*8; - function buildAdd() { - const f = module.addFunction(prefix+"_add"); - f.addParam("x", "i32"); - f.addParam("y", "i32"); - f.addParam("r", "i32"); + function buildCopy() { + const f = module.addFunction(prefix+"_copy"); + f.addParam("px", "i32"); + f.addParam("pr", "i32"); const c = f.getCodeBuilder(); - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); - const y0 = c.getLocal("y"); - const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8)); - const y2 = c.i32_add(c.getLocal("y"), c.i32_const(2*f1n8)); - const r0 = c.getLocal("r"); - const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); - const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8)); - - f.addCode( - c.call(f1mPrefix+"_add", x0, y0, r0), - c.call(f1mPrefix+"_add", x1, y1, r1), - c.call(f1mPrefix+"_add", x2, y2, r2), - ); + for (let i=0; i>1) )&&(i>1, k>>1) + ) + ) + ); - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + f.addCode( + c.setLocal(c1, + c.i64_add( + c.getLocal(c1), + c.i64_shr_u( + c.getLocal(c0), + c.i64_const(32) + ) + ) + ) + ); + } - f.addCode( - c.setLocal("s" , c.call( f1mPrefix + "_sign", x2)), - c.if( - c.getLocal("s"), - c.ret(c.getLocal("s")) - ), - c.setLocal("s" , c.call( f1mPrefix + "_sign", x1)), - c.if( - c.getLocal("s"), - c.ret(c.getLocal("s")) - ), - c.ret(c.call( f1mPrefix + "_sign", x0)) - ); - } + // Add the old carry - function buildIsOne() { - const f = module.addFunction(prefix+"_isOne"); - f.addParam("x", "i32"); - f.setReturnType("i32"); + if (k>0) { + f.addCode( + c.setLocal(c0, + c.i64_add( + c.i64_and( + c.getLocal(c0), + c.i64_const(0xFFFFFFFF) + ), + c.i64_and( + c.getLocal(c0_old), + c.i64_const(0xFFFFFFFF) + ), + ) + ) + ); - const c = f.getCodeBuilder(); + f.addCode( + c.setLocal(c1, + c.i64_add( + c.i64_add( + c.getLocal(c1), + c.i64_shr_u( + c.getLocal(c0), + c.i64_const(32) + ) + ), + c.getLocal(c1_old) + ) + ) + ); + } - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - const x2 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8*2)); + f.addCode( + c.i64_store32( + c.getLocal("r"), + k*4, + c.getLocal(c0) + ) + ); - f.addCode( - c.ret( - c.i32_and( - c.i32_and( - c.call(f1mPrefix + "_isOne", x0), - c.call(f1mPrefix + "_isZero", x1) - ), - c.call(f1mPrefix + "_isZero", x2) + f.addCode( + c.setLocal( + c0_old, + c.getLocal(c1) + ), + c.setLocal( + c1_old, + c.i64_shr_u( + c.getLocal(c0_old), + c.i64_const(32) + ) ) + ); + + } + f.addCode( + c.i64_store32( + c.getLocal("r"), + n32*4*2-4, + c.getLocal(c0_old) ) ); + } - buildIsZero(); - buildIsOne(); - buildZero(); - buildOne(); - buildCopy(); - buildMul(); - buildSquare(); - buildAdd(); - buildSub(); - buildNeg(); - buildSign(); - buildToMontgomery(); - buildFromMontgomery(); - buildEq(); - buildInverse(); - buildTimesScalar(); - buildIsNegative(); - module.exportFunction(prefix + "_isZero"); - module.exportFunction(prefix + "_isOne"); - module.exportFunction(prefix + "_zero"); - module.exportFunction(prefix + "_one"); - module.exportFunction(prefix + "_copy"); - module.exportFunction(prefix + "_mul"); - module.exportFunction(prefix + "_square"); - module.exportFunction(prefix + "_add"); - module.exportFunction(prefix + "_sub"); - module.exportFunction(prefix + "_neg"); - module.exportFunction(prefix + "_sign"); - module.exportFunction(prefix + "_fromMontgomery"); - module.exportFunction(prefix + "_toMontgomery"); - module.exportFunction(prefix + "_eq"); - module.exportFunction(prefix + "_inverse"); - buildBatchInverse(module, prefix); - buildExp( - module, - prefix + "_exp", - f1n8*3, - prefix + "_mul", - prefix + "_square", - prefix + "_copy", - prefix + "_one" - ); - module.exportFunction(prefix + "_exp"); - module.exportFunction(prefix + "_timesScalar"); - module.exportFunction(prefix + "_batchInverse"); - module.exportFunction(prefix + "_isNegative"); + function buildSquareOld() { + const f = module.addFunction(prefix+"_squareOld"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); - return prefix; -}; + const c = f.getCodeBuilder(); -/* - Copyright 2019 0KIMS association. + f.addCode(c.call(prefix + "_mul", c.getLocal("x"), c.getLocal("x"), c.getLocal("r"))); + } - This file is part of wasmsnark (Web Assembly zkSnark Prover). + function _buildMul1() { + const f = module.addFunction(prefix+"__mul1"); + f.addParam("px", "i32"); + f.addParam("y", "i64"); + f.addParam("pr", "i32"); + f.addLocal("c", "i64"); - wasmsnark 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. + const c = f.getCodeBuilder(); - wasmsnark 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. + f.addCode(c.setLocal( + "c", + c.i64_mul( + c.i64_load32_u(c.getLocal("px"), 0, 0), + c.getLocal("y") + ) + )); - You should have received a copy of the GNU General Public License - along with wasmsnark. If not, see . -*/ + f.addCode(c.i64_store32( + c.getLocal("pr"), + 0, + 0, + c.getLocal("c"), + )); -var build_timesscalarnaf = function buildTimesScalarNAF(module, fnName, elementLen, opAB, opAA, opAmB, opCopy, opInit) { + for (let i=1; i3)&&(Y[eY]==0) ey--; + f.addCode(c.block(c.loop( + c.br_if( + 1, + c.i32_or( + c.i32_load8_u( + c.i32_add(Y , c.getLocal("eY")), + 0, + 0 + ), + c.i32_eq( + c.getLocal("eY"), + c.i32_const(3) + ) ) ), - - c.br_if(1, c.i32_eq( c.getLocal("old0"), c.getLocal("p"))), - c.setLocal("p", c.i32_sub(c.getLocal("p"), c.i32_const(1))), + c.setLocal("eY", c.i32_sub(c.getLocal("eY"), c.i32_const(1))), c.br(0) - - )), - - c.i32_store( c.i32_const(0), c.getLocal("old0")) - - ); - -}; - -/* - Copyright 2019 0KIMS association. - - This file is part of wasmsnark (Web Assembly zkSnark Prover). - - wasmsnark 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. - - wasmsnark 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 wasmsnark. If not, see . -*/ - -var build_multiexp = function buildMultiexp(module, prefix, fnName, opAdd, n8b) { - - const n64g = module.modules[prefix].n64; - const n8g = n64g*8; - - function buildGetChunk() { - const f = module.addFunction(fnName + "_getChunk"); - f.addParam("pScalar", "i32"); - f.addParam("scalarSize", "i32"); // Number of bytes of the scalar - f.addParam("startBit", "i32"); // Bit to start extract - f.addParam("chunkSize", "i32"); // Chunk size in bits - f.addLocal("bitsToEnd", "i32"); - f.addLocal("mask", "i32"); - f.setReturnType("i32"); - - const c = f.getCodeBuilder(); + ))); f.addCode( - c.setLocal("bitsToEnd", - c.i32_sub( - c.i32_mul( - c.getLocal("scalarSize"), - c.i32_const(8) + c.setLocal( + "sy", + c.i64_add( + c.i64_load32_u( + c.i32_sub( + c.i32_add( Y, c.getLocal("eY")), + c.i32_const(3) + ), + 0, + 0 ), - c.getLocal("startBit") + c.i64_const(1) ) - ), + ) + ); + + // Force a divide by 0 if quotien is 0 + f.addCode( c.if( - c.i32_gt_s( - c.getLocal("chunkSize"), - c.getLocal("bitsToEnd") + c.i64_eq( + c.getLocal("sy"), + c.i64_const(1) ), - c.setLocal( - "mask", - c.i32_sub( - c.i32_shl( - c.i32_const(1), - c.getLocal("bitsToEnd") + c.drop(c.i64_div_u(c.i64_const(0), c.i64_const(0))) + ) + ); + + f.addCode(c.block(c.loop( + + // while (eX>7)&&(Y[eX]==0) ex--; + c.block(c.loop( + c.br_if( + 1, + c.i32_or( + c.i32_load8_u( + c.i32_add(R , c.getLocal("eX")), + 0, + 0 ), - c.i32_const(1) + c.i32_eq( + c.getLocal("eX"), + c.i32_const(7) + ) ) ), - c.setLocal( - "mask", + c.setLocal("eX", c.i32_sub(c.getLocal("eX"), c.i32_const(1))), + c.br(0) + )), + + c.setLocal( + "sx", + c.i64_load( c.i32_sub( - c.i32_shl( - c.i32_const(1), - c.getLocal("chunkSize") - ), - c.i32_const(1) - ) + c.i32_add( R, c.getLocal("eX")), + c.i32_const(7) + ), + 0, + 0 ) ), - c.i32_and( - c.i32_shr_u( - c.i32_load( - c.i32_add( - c.getLocal("pScalar"), - c.i32_shr_u( - c.getLocal("startBit"), - c.i32_const(3) - ) - ), - 0, // offset - 0 // align to byte. - ), - c.i32_and( - c.getLocal("startBit"), - c.i32_const(0x7) - ) - ), - c.getLocal("mask") - ) - ); - } - - function buildMutiexpChunk() { - const f = module.addFunction(fnName + "_chunk"); - f.addParam("pBases", "i32"); - f.addParam("pScalars", "i32"); - f.addParam("scalarSize", "i32"); // Number of points - f.addParam("n", "i32"); // Number of points - f.addParam("startBit", "i32"); // bit where it starts the chunk - f.addParam("chunkSize", "i32"); // bit where it starts the chunk - f.addParam("pr", "i32"); - f.addLocal("nChunks", "i32"); - f.addLocal("itScalar", "i32"); - f.addLocal("endScalar", "i32"); - f.addLocal("itBase", "i32"); - f.addLocal("i", "i32"); - f.addLocal("j", "i32"); - f.addLocal("nTable", "i32"); - f.addLocal("pTable", "i32"); - f.addLocal("idx", "i32"); - f.addLocal("pIdxTable", "i32"); - - const c = f.getCodeBuilder(); - - f.addCode( - c.if( - c.i32_eqz(c.getLocal("n")), - [ - ...c.call(prefix + "_zero", c.getLocal("pr")), - ...c.ret([]) - ] - ), - - // Allocate memory c.setLocal( - "nTable", - c.i32_shl( - c.i32_const(1), - c.getLocal("chunkSize") + "sx", + c.i64_div_u( + c.getLocal("sx"), + c.getLocal("sy") ) ), - c.setLocal("pTable", c.i32_load( c.i32_const(0) )), - c.i32_store( - c.i32_const(0), - c.i32_add( - c.getLocal("pTable"), - c.i32_mul( - c.getLocal("nTable"), - c.i32_const(n8g) - ) + c.setLocal( + "ec", + c.i32_sub( + c.i32_sub( + c.getLocal("eX"), + c.getLocal("eY") + ), + c.i32_const(4) ) ), - // Reset Table - c.setLocal("j", c.i32_const(0)), + // While greater than 32 bits or ec is neg, shr and inc exp c.block(c.loop( c.br_if( 1, - c.i32_eq( - c.getLocal("j"), - c.getLocal("nTable") - ) - ), - - c.call( - prefix + "_zero", - c.i32_add( - c.getLocal("pTable"), - c.i32_mul( - c.getLocal("j"), - c.i32_const(n8g) + c.i32_and( + c.i64_eqz( + c.i64_and( + c.getLocal("sx"), + c.i64_const("0xFFFFFFFF00000000") + ) + ), + c.i32_ge_s( + c.getLocal("ec"), + c.i32_const(0) ) ) ), - c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), - c.br(0) - )), - - // Distribute elements - c.setLocal("itBase", c.getLocal("pBases")), - c.setLocal("itScalar", c.getLocal("pScalars")), - c.setLocal("endScalar", - c.i32_add( - c.getLocal("pScalars"), - c.i32_mul( - c.getLocal("n"), - c.getLocal("scalarSize") - ) - ) - ), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("itScalar"), - c.getLocal("endScalar") + c.setLocal( + "sx", + c.i64_shr_u( + c.getLocal("sx"), + c.i64_const(8) ) ), c.setLocal( - "idx", - c.call(fnName + "_getChunk", - c.getLocal("itScalar"), - c.getLocal("scalarSize"), - c.getLocal("startBit"), - c.getLocal("chunkSize") + "ec", + c.i32_add( + c.getLocal("ec"), + c.i32_const(1) ) ), - - c.if( - c.getLocal("idx"), - [ - ...c.setLocal( - "pIdxTable", - c.i32_add( - c.getLocal("pTable"), - c.i32_mul( - c.i32_sub( - c.getLocal("idx"), - c.i32_const(1) - ), - c.i32_const(n8g) - ) - ) - ), - ...c.call( - opAdd, - c.getLocal("pIdxTable"), - c.getLocal("itBase"), - c.getLocal("pIdxTable"), - ) - ] - ), - - c.setLocal("itScalar", c.i32_add(c.getLocal("itScalar"), c.getLocal("scalarSize"))), - c.setLocal("itBase", c.i32_add(c.getLocal("itBase"), c.i32_const(n8b))), c.br(0) )), - c.call(fnName + "_reduceTable", c.getLocal("pTable"), c.getLocal("chunkSize")), - c.call( - prefix + "_copy", - c.getLocal("pTable"), - c.getLocal("pr") + c.if( + c.i64_eqz(c.getLocal("sx")), + [ + ...c.br_if( + 2, + c.i32_eqz(c.call(prefix + "_gte", R, Y)) + ), + ...c.setLocal("sx", c.i64_const(1)), + ...c.setLocal("ec", c.i32_const(0)) + ] ), - - c.i32_store( - c.i32_const(0), - c.getLocal("pTable") - ) - - ); + c.call(prefix + "__mul1", Y, c.getLocal("sx"), R2), + c.drop(c.call( + prefix + "_sub", + R, + c.i32_sub(R2, c.getLocal("ec")), + R + )), + c.call( + prefix + "__add1", + c.i32_add(C, c.getLocal("ec")), + c.getLocal("sx") + ), + c.br(0) + ))); } - function buildMultiexp() { - const f = module.addFunction(fnName); - f.addParam("pBases", "i32"); - f.addParam("pScalars", "i32"); - f.addParam("scalarSize", "i32"); // Number of points - f.addParam("n", "i32"); // Number of points + function buildInverseMod() { + + const f = module.addFunction(prefix+"_inverseMod"); + f.addParam("px", "i32"); + f.addParam("pm", "i32"); f.addParam("pr", "i32"); - f.addLocal("chunkSize", "i32"); - f.addLocal("nChunks", "i32"); - f.addLocal("itScalar", "i32"); - f.addLocal("endScalar", "i32"); - f.addLocal("itBase", "i32"); - f.addLocal("itBit", "i32"); - f.addLocal("i", "i32"); - f.addLocal("j", "i32"); - f.addLocal("nTable", "i32"); - f.addLocal("pTable", "i32"); - f.addLocal("idx", "i32"); - f.addLocal("pIdxTable", "i32"); + f.addLocal("t", "i32"); + f.addLocal("newt", "i32"); + f.addLocal("r", "i32"); + f.addLocal("qq", "i32"); + f.addLocal("qr", "i32"); + f.addLocal("newr", "i32"); + f.addLocal("swp", "i32"); + f.addLocal("x", "i32"); + f.addLocal("signt", "i32"); + f.addLocal("signnewt", "i32"); + f.addLocal("signx", "i32"); const c = f.getCodeBuilder(); - const aux = c.i32_const(module.alloc(n8g)); - - const pTSizes = module.alloc([ - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 16, 16, 15, 14, 13, 13, - 12, 11, 10, 9, 8, 7, 7, 6, - 5 , 4, 3, 2, 1, 1, 1, 1 - ]); + const aux1 = c.i32_const(module.alloc(n8)); + const aux2 = c.i32_const(module.alloc(n8)); + const aux3 = c.i32_const(module.alloc(n8)); + const aux4 = c.i32_const(module.alloc(n8)); + const aux5 = c.i32_const(module.alloc(n8)); + const aux6 = c.i32_const(module.alloc(n8)); + const mulBuff = c.i32_const(module.alloc(n8*2)); + const aux7 = c.i32_const(module.alloc(n8)); f.addCode( - c.call(prefix + "_zero", c.getLocal("pr")), - c.if( - c.i32_eqz(c.getLocal("n")), - c.ret([]) - ), - c.setLocal("chunkSize", c.i32_load8_u( c.i32_clz(c.getLocal("n")), pTSizes )), - c.setLocal( - "nChunks", - c.i32_add( - c.i32_div_u( - c.i32_sub( - c.i32_shl( - c.getLocal("scalarSize"), - c.i32_const(3) - ), - c.i32_const(1) - ), - c.getLocal("chunkSize") - ), - c.i32_const(1) - ) - ), - + c.setLocal("t", aux1), + c.call(prefix + "_zero", aux1), + c.setLocal("signt", c.i32_const(0)), + ); - // Allocate memory + f.addCode( + c.setLocal("r", aux2), + c.call(prefix + "_copy", c.getLocal("pm"), aux2) + ); - c.setLocal( - "itBit", - c.i32_mul( - c.i32_sub( - c.getLocal("nChunks"), - c.i32_const(1) - ), - c.getLocal("chunkSize") - ) - ), - c.block(c.loop( - c.br_if( - 1, - c.i32_lt_s( - c.getLocal("itBit"), - c.i32_const(0) - ) - ), + f.addCode( + c.setLocal("newt", aux3), + c.call(prefix + "_one", aux3), + c.setLocal("signnewt", c.i32_const(0)), + ); - // Double nChunk times - c.if( - c.i32_eqz(c.call(prefix + "_isZero", c.getLocal("pr"))), - [ - ...c.setLocal("j", c.i32_const(0)), - ...c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("j"), - c.getLocal("chunkSize") - ) - ), + f.addCode( + c.setLocal("newr", aux4), + c.call(prefix + "_copy", c.getLocal("px"), aux4) + ); - c.call(prefix + "_double", c.getLocal("pr"), c.getLocal("pr")), - c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), - c.br(0) - )) - ] - ), - c.call( - fnName + "_chunk", - c.getLocal("pBases"), - c.getLocal("pScalars"), - c.getLocal("scalarSize"), - c.getLocal("n"), - c.getLocal("itBit"), - c.getLocal("chunkSize"), - aux - ), - c.call( - prefix + "_add", - c.getLocal("pr"), - aux, - c.getLocal("pr") - ), - c.setLocal("itBit", c.i32_sub(c.getLocal("itBit"), c.getLocal("chunkSize"))), - c.br(0) - )) - ); - } + f.addCode(c.setLocal("qq", aux5)); + f.addCode(c.setLocal("qr", aux6)); + f.addCode(c.setLocal("x", aux7)); - function buildReduceTable() { - const f = module.addFunction(fnName + "_reduceTable"); - f.addParam("pTable", "i32"); - f.addParam("p", "i32"); // Number of bits of the table - f.addLocal("half", "i32"); - f.addLocal("it1", "i32"); - f.addLocal("it2", "i32"); - f.addLocal("pAcc", "i32"); + f.addCode(c.block(c.loop( + c.br_if( + 1, + c.call(prefix + "_isZero", c.getLocal("newr") ) + ), + c.call(prefix + "_div", c.getLocal("r"), c.getLocal("newr"), c.getLocal("qq"), c.getLocal("qr")), - const c = f.getCodeBuilder(); + c.call(prefix + "_mul", c.getLocal("qq"), c.getLocal("newt"), mulBuff), - f.addCode( c.if( - c.i32_eq(c.getLocal("p"), c.i32_const(1)), - c.ret([]) - ), - c.setLocal( - "half", - c.i32_shl( - c.i32_const(1), - c.i32_sub( - c.getLocal("p"), - c.i32_const(1) - ) - ) - ), - - c.setLocal("it1", c.getLocal("pTable")), - c.setLocal( - "it2", - c.i32_add( - c.getLocal("pTable"), - c.i32_mul( - c.getLocal("half"), - c.i32_const(n8g) - ) - ) - ), - c.setLocal("pAcc", - c.i32_sub( - c.getLocal("it2"), - c.i32_const(n8g) - ) - ), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("it1"), - c.getLocal("pAcc") - ) - ), - c.call( - prefix + "_add", - c.getLocal("it1"), - c.getLocal("it2"), - c.getLocal("it1") - ), - c.call( - prefix + "_add", - c.getLocal("pAcc"), - c.getLocal("it2"), - c.getLocal("pAcc") + c.getLocal("signt"), + c.if( + c.getLocal("signnewt"), + c.if ( + c.call(prefix + "_gte", mulBuff, c.getLocal("t")), + [ + ...c.drop(c.call(prefix + "_sub", mulBuff, c.getLocal("t"), c.getLocal("x"))), + ...c.setLocal("signx", c.i32_const(0)) + ], + [ + ...c.drop(c.call(prefix + "_sub", c.getLocal("t"), mulBuff, c.getLocal("x"))), + ...c.setLocal("signx", c.i32_const(1)) + ], + ), + [ + ...c.drop(c.call(prefix + "_add", mulBuff, c.getLocal("t"), c.getLocal("x"))), + ...c.setLocal("signx", c.i32_const(1)) + ] ), - c.setLocal("it1", c.i32_add(c.getLocal("it1"), c.i32_const(n8g))), - c.setLocal("it2", c.i32_add(c.getLocal("it2"), c.i32_const(n8g))), - c.br(0) - )), - - c.call( - fnName + "_reduceTable", - c.getLocal("pTable"), - c.i32_sub( - c.getLocal("p"), - c.i32_const(1) + c.if( + c.getLocal("signnewt"), + [ + ...c.drop(c.call(prefix + "_add", mulBuff, c.getLocal("t"), c.getLocal("x"))), + ...c.setLocal("signx", c.i32_const(0)) + ], + c.if ( + c.call(prefix + "_gte", c.getLocal("t"), mulBuff), + [ + ...c.drop(c.call(prefix + "_sub", c.getLocal("t"), mulBuff, c.getLocal("x"))), + ...c.setLocal("signx", c.i32_const(0)) + ], + [ + ...c.drop(c.call(prefix + "_sub", mulBuff, c.getLocal("t"), c.getLocal("x"))), + ...c.setLocal("signx", c.i32_const(1)) + ] + ) ) ), - c.setLocal("p", c.i32_sub(c.getLocal("p"), c.i32_const(1))), - c.block(c.loop( - c.br_if(1, c.i32_eqz(c.getLocal("p"))), - c.call(prefix + "_double", c.getLocal("pAcc"), c.getLocal("pAcc")), - c.setLocal("p", c.i32_sub(c.getLocal("p"), c.i32_const(1))), - c.br(0) - )), - - c.call(prefix + "_add", c.getLocal("pTable"), c.getLocal("pAcc"), c.getLocal("pTable")) - ); - } - - buildGetChunk(); - buildReduceTable(); - buildMutiexpChunk(); - buildMultiexp(); + c.setLocal("swp", c.getLocal("t")), + c.setLocal("t", c.getLocal("newt")), + c.setLocal("newt", c.getLocal("x")), + c.setLocal("x", c.getLocal("swp")), - module.exportFunction(fnName); - module.exportFunction(fnName +"_chunk"); + c.setLocal("signt", c.getLocal("signnewt")), + c.setLocal("signnewt", c.getLocal("signx")), + c.setLocal("swp", c.getLocal("r")), + c.setLocal("r", c.getLocal("newr")), + c.setLocal("newr", c.getLocal("qr")), + c.setLocal("qr", c.getLocal("swp")), -}; + c.br(0) + ))); -/* - Copyright 2019 0KIMS association. + f.addCode(c.if( + c.getLocal("signt"), + c.drop(c.call(prefix + "_sub", c.getLocal("pm"), c.getLocal("t"), c.getLocal("pr"))), + c.call(prefix + "_copy", c.getLocal("t"), c.getLocal("pr")) + )); + } - This file is part of wasmsnark (Web Assembly zkSnark Prover). - wasmsnark is a free software: you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by + buildCopy(); + buildZero(); + buildIsZero(); + buildOne(); + buildEq(); + buildGte(); + buildAdd(); + buildSub(); + buildMul(); + buildSquare(); + buildSquareOld(); + buildDiv(); + buildInverseMod(); + module.exportFunction(prefix+"_copy"); + module.exportFunction(prefix+"_zero"); + module.exportFunction(prefix+"_one"); + module.exportFunction(prefix+"_isZero"); + module.exportFunction(prefix+"_eq"); + module.exportFunction(prefix+"_gte"); + module.exportFunction(prefix+"_add"); + module.exportFunction(prefix+"_sub"); + module.exportFunction(prefix+"_mul"); + module.exportFunction(prefix+"_square"); + module.exportFunction(prefix+"_squareOld"); + module.exportFunction(prefix+"_div"); + module.exportFunction(prefix+"_inverseMod"); + + return prefix; +}; + +/* + Copyright 2019 0KIMS association. + + This file is part of wasmsnark (Web Assembly zkSnark Prover). + + wasmsnark 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. @@ -7554,2865 +7427,2924 @@ var build_multiexp = function buildMultiexp(module, prefix, fnName, opAdd, n8b) along with wasmsnark. If not, see . */ -const buildTimesScalarNAF = build_timesscalarnaf; -//const buildTimesScalar = require("./build_timesscalar"); -const buildBatchConvertion = build_batchconvertion; -const buildMultiexp$1 = build_multiexp; - -var build_curve_jacobian_a0 = function buildCurve(module, prefix, prefixField, pB) { - - - const n64 = module.modules[prefixField].n64; - const n8 = n64*8; - - if (module.modules[prefix]) return prefix; // already builded - module.modules[prefix] = { - n64: n64*3 - }; +var build_timesscalar = function buildTimesScalar(module, fnName, elementLen, opAB, opAA, opCopy, opInit) { - function buildIsZero() { - const f = module.addFunction(prefix + "_isZero"); - f.addParam("p1", "i32"); - f.setReturnType("i32"); + const f = module.addFunction(fnName); + f.addParam("base", "i32"); + f.addParam("scalar", "i32"); + f.addParam("scalarLength", "i32"); + f.addParam("r", "i32"); + f.addLocal("i", "i32"); + f.addLocal("b", "i32"); - const c = f.getCodeBuilder(); + const c = f.getCodeBuilder(); - f.addCode(c.call( - prefixField + "_isZero", - c.i32_add( - c.getLocal("p1"), - c.i32_const(n8*2) - ) - )); - } - function buildIsZeroAffine() { - const f = module.addFunction(prefix + "_isZeroAffine"); - f.addParam("p1", "i32"); - f.setReturnType("i32"); + const aux = c.i32_const(module.alloc(elementLen)); - const c = f.getCodeBuilder(); + f.addCode( + c.if( + c.i32_eqz(c.getLocal("scalarLength")), + [ + ...c.call(opInit, c.getLocal("r")), + ...c.ret([]) + ] + ) + ); + f.addCode(c.call(opCopy, c.getLocal("base"), aux)); + f.addCode(c.call(opInit, c.getLocal("r"))); + f.addCode(c.setLocal("i", c.getLocal("scalarLength"))); + f.addCode(c.block(c.loop( + c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), - f.addCode( - c.i32_and( - c.call( - prefixField + "_isZero", - c.getLocal("p1") - ), - c.call( - prefixField + "_isZero", - c.i32_add( - c.getLocal("p1"), - c.i32_const(n8) - ) + c.setLocal( + "b", + c.i32_load8_u( + c.i32_add( + c.getLocal("scalar"), + c.getLocal("i") ) ) - ); - } - - function buildCopy() { - const f = module.addFunction(prefix + "_copy"); - f.addParam("ps", "i32"); - f.addParam("pd", "i32"); + ), + ...innerLoop(), + c.br_if(1, c.i32_eqz ( c.getLocal("i") )), + c.br(0) + ))); - const c = f.getCodeBuilder(); - for (let i=0; i> i)), + [ + ...c.setLocal( + "b", + c.i32_sub( + c.getLocal("b"), + c.i32_const(0x80 >> i) + ) + ), + ...c.call(opAB, c.getLocal("r"),aux, c.getLocal("r")) + ] ) ); } + return code; } +}; - function buildCopyAffine() { - const f = module.addFunction(prefix + "_copyAffine"); - f.addParam("ps", "i32"); - f.addParam("pd", "i32"); - - const c = f.getCodeBuilder(); +var build_batchinverse = buildBatchInverse$3; - for (let i=0; i b ? 1 : -1; +} - const XX = c.i32_const(module.alloc(n8)); - const YY = c.i32_const(module.alloc(n8)); - const YYYY = c.i32_const(module.alloc(n8)); - const S = c.i32_const(module.alloc(n8)); - const M = c.i32_const(module.alloc(n8)); - const eightYYYY = c.i32_const(module.alloc(n8)); +function square$1(n) { + return n * n; +} - f.addCode( - c.if( - c.call(prefix + "_isZeroAffine", c.getLocal("p1")), - [ - ...c.call(prefix + "_toJacobian", c.getLocal("p1"), c.getLocal("pr")), - ...c.ret([]) - ] - ), +function isOdd$4(n) { + return n % 2n !== 0n; +} - // XX = X1^2 - c.call(prefixField + "_square", x, XX), +function isEven(n) { + return n % 2n === 0n; +} - // YY = Y1^2 - c.call(prefixField + "_square", y, YY), +function isNegative$3(n) { + return n < 0n; +} - // YYYY = YY^2 - c.call(prefixField + "_square", YY, YYYY), +function isPositive(n) { + return n > 0n; +} - // S = 2*((X1+YY)^2-XX-YYYY) - c.call(prefixField + "_add", x, YY, S), - c.call(prefixField + "_square", S, S), - c.call(prefixField + "_sub", S, XX, S), - c.call(prefixField + "_sub", S, YYYY, S), - c.call(prefixField + "_add", S, S, S), +function bitLength$5(n) { + if (isNegative$3(n)) { + return n.toString(2).length - 1; // discard the - sign + } else { + return n.toString(2).length; + } +} - // M = 3*XX+a (Hera a=0) - c.call(prefixField + "_add", XX, XX, M), - c.call(prefixField + "_add", M, XX, M), +function abs(n) { + return n < 0n ? -n : n; +} - // Z3 = 2*Y1 - c.call(prefixField + "_add", y, y, z3), +function isUnit(n) { + return abs(n) === 1n; +} - // T = M^2-2*S - // X3 = T - c.call(prefixField + "_square", M, x3), - c.call(prefixField + "_sub", x3, S, x3), - c.call(prefixField + "_sub", x3, S, x3), +function modInv$3(a, n) { + var t = 0n, newT = 1n, r = n, newR = abs(a), q, lastT, lastR; + while (newR !== 0n) { + q = r / newR; + lastT = t; + lastR = r; + t = newT; + r = newR; + newT = lastT - (q * newT); + newR = lastR - (q * newR); + } + if (!isUnit(r)) throw new Error(a.toString() + " and " + n.toString() + " are not co-prime"); + if (compare(t, 0n) === -1) { + t = t + n; + } + if (isNegative$3(a)) { + return -t; + } + return t; +} - // Y3 = M*(S-T)-8*YYYY - c.call(prefixField + "_add", YYYY, YYYY, eightYYYY), - c.call(prefixField + "_add", eightYYYY, eightYYYY, eightYYYY), - c.call(prefixField + "_add", eightYYYY, eightYYYY, eightYYYY), - c.call(prefixField + "_sub", S, x3, y3), - c.call(prefixField + "_mul", y3, M, y3), - c.call(prefixField + "_sub", y3, eightYYYY, y3), - ); +function modPow$2(n, exp, mod) { + if (mod === 0n) throw new Error("Cannot take modPow with modulus 0"); + var r = 1n, + base = n % mod; + if (isNegative$3(exp)) { + exp = exp * -1n; + base = modInv$3(base, mod); + } + while (isPositive(exp)) { + if (base === 0n) return 0n; + if (isOdd$4(exp)) r = r * base % mod; + exp = exp / 2n; + base = square$1(base) % mod; } + return r; +} +function compareAbs(a, b) { + a = a >= 0n ? a : -a; + b = b >= 0n ? b : -b; + return a === b ? 0 : a > b ? 1 : -1; +} - function buildEqAffine() { - const f = module.addFunction(prefix + "_eqAffine"); - f.addParam("p1", "i32"); - f.addParam("p2", "i32"); - f.setReturnType("i32"); - f.addLocal("z1", "i32"); +function isDivisibleBy(a, n) { + if (n === 0n) return false; + if (isUnit(n)) return true; + if (compareAbs(n, 2n) === 0) return isEven(a); + return a % n === 0n; +} - const c = f.getCodeBuilder(); +function isBasicPrime(v) { + var n = abs(v); + if (isUnit(n)) return false; + if (n === 2n || n === 3n || n === 5n) return true; + if (isEven(n) || isDivisibleBy(n, 3n) || isDivisibleBy(n, 5n)) return false; + if (n < 49n) return true; + // we don't know if it's prime: let the other functions figure it out +} - f.addCode( - c.ret(c.i32_and( - c.call( - prefixField + "_eq", - c.getLocal("p1"), - c.getLocal("p2") - ), - c.call( - prefixField + "_eq", - c.i32_add(c.getLocal("p1"), c.i32_const(n8)), - c.i32_add(c.getLocal("p2"), c.i32_const(n8)) - ) - )) - ); +function prev(n) { + return n - 1n; +} + +function millerRabinTest(n, a) { + var nPrev = prev(n), + b = nPrev, + r = 0, + d, i, x; + while (isEven(b)) b = b / 2n, r++; + next: for (i = 0; i < a.length; i++) { + if (n < a[i]) continue; + x = modPow$2(BigInt(a[i]), b, n); + if (isUnit(x) || x === nPrev) continue; + for (d = r - 1; d != 0; d--) { + x = square$1(x) % n; + if (isUnit(x)) return false; + if (x === nPrev) continue next; + } + return false; } + return true; +} - function buildToMontgomery() { - const f = module.addFunction(prefix + "_toMontgomery"); - f.addParam("p1", "i32"); +function isPrime$1(p) { + var isPrime = isBasicPrime(p); + if (isPrime !== undefined) return isPrime; + var n = abs(p); + var bits = bitLength$5(n); + if (bits <= 64) + return millerRabinTest(n, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]); + var logN = Math.log(2) * Number(bits); + var t = Math.ceil(logN); + for (var a = [], i = 0; i < t; i++) { + a.push(BigInt(i + 2)); + } + return millerRabinTest(n, a); +} + +bigint.bitLength = bitLength$5; +bigint.isOdd = isOdd$4; +bigint.isNegative = isNegative$3; +bigint.abs = abs; +bigint.isUnit = isUnit; +bigint.compare = compare; +bigint.modInv = modInv$3; +bigint.modPow = modPow$2; +bigint.isPrime = isPrime$1; +bigint.square = square$1; + +/* + Copyright 2019 0KIMS association. + + This file is part of wasmsnark (Web Assembly zkSnark Prover). + + wasmsnark 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. + + wasmsnark 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 wasmsnark. If not, see . +*/ + +const buildInt = build_int; +const utils$4 = utils$5; +const buildExp$2 = build_timesscalar; +const buildBatchInverse$2 = build_batchinverse; +const buildBatchConvertion$1 = build_batchconvertion; +const buildBatchOp = build_batchop; +const { bitLength: bitLength$4, modInv: modInv$2, modPow: modPow$1, isPrime, isOdd: isOdd$3, square } = bigint; + +var build_f1m = function buildF1m(module, _q, _prefix, _intPrefix) { + const q = BigInt(_q); + const n64 = Math.floor((bitLength$4(q - 1n) - 1)/64) +1; + const n32 = n64*2; + const n8 = n64*8; + + const prefix = _prefix || "f1m"; + if (module.modules[prefix]) return prefix; // already builded + + const intPrefix = buildInt(module, n64, _intPrefix); + const pq = module.alloc(n8, utils$4.bigInt2BytesLE(q, n8)); + + const pR2 = module.alloc(utils$4.bigInt2BytesLE(square(1n << BigInt(n64*64)) % q, n8)); + const pOne = module.alloc(utils$4.bigInt2BytesLE((1n << BigInt(n64*64)) % q, n8)); + const pZero = module.alloc(utils$4.bigInt2BytesLE(0n, n8)); + const _minusOne = q - 1n; + const _e = _minusOne >> 1n; // e = (p-1)/2 + const pe = module.alloc(n8, utils$4.bigInt2BytesLE(_e, n8)); + + const _ePlusOne = _e + 1n; // e = (p-1)/2 + const pePlusOne = module.alloc(n8, utils$4.bigInt2BytesLE(_ePlusOne, n8)); + + module.modules[prefix] = { + pq: pq, + pR2: pR2, + n64: n64, + q: q, + pOne: pOne, + pZero: pZero, + pePlusOne: pePlusOne + }; + + function buildOne() { + const f = module.addFunction(prefix+"_one"); f.addParam("pr", "i32"); const c = f.getCodeBuilder(); - f.addCode(c.call( - prefixField + "_toMontgomery", - c.getLocal("p1"), - c.getLocal("pr") - )); - for (let i=1; i<3; i++) { - f.addCode(c.call( - prefixField + "_toMontgomery", - c.i32_add(c.getLocal("p1"), c.i32_const(i*n8)), - c.i32_add(c.getLocal("pr"), c.i32_const(i*n8)) - )); - } + f.addCode(c.call(intPrefix + "_copy", c.i32_const(pOne), c.getLocal("pr"))); } - function buildToMontgomeryAffine() { - const f = module.addFunction(prefix + "_toMontgomeryAffine"); - f.addParam("p1", "i32"); - f.addParam("pr", "i32"); + function buildAdd() { + const f = module.addFunction(prefix+"_add"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); - f.addCode(c.call( - prefixField + "_toMontgomery", - c.getLocal("p1"), - c.getLocal("pr") - )); - for (let i=1; i<2; i++) { - f.addCode(c.call( - prefixField + "_toMontgomery", - c.i32_add(c.getLocal("p1"), c.i32_const(i*n8)), - c.i32_add(c.getLocal("pr"), c.i32_const(i*n8)) - )); - } + f.addCode( + c.if( + c.call(intPrefix+"_add", c.getLocal("x"), c.getLocal("y"), c.getLocal("r")), + c.drop(c.call(intPrefix+"_sub", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))), + c.if( + c.call(intPrefix+"_gte", c.getLocal("r"), c.i32_const(pq) ), + c.drop(c.call(intPrefix+"_sub", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))), + ) + ) + ); } - function buildFromMontgomery() { - const f = module.addFunction(prefix + "_fromMontgomery"); - f.addParam("p1", "i32"); - f.addParam("pr", "i32"); + function buildSub() { + const f = module.addFunction(prefix+"_sub"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); - f.addCode(c.call( - prefixField + "_fromMontgomery", - c.getLocal("p1"), - c.getLocal("pr") - )); - for (let i=1; i<3; i++) { - f.addCode(c.call( - prefixField + "_fromMontgomery", - c.i32_add(c.getLocal("p1"), c.i32_const(i*n8)), - c.i32_add(c.getLocal("pr"), c.i32_const(i*n8)) - )); - } + f.addCode( + c.if( + c.call(intPrefix+"_sub", c.getLocal("x"), c.getLocal("y"), c.getLocal("r")), + c.drop(c.call(intPrefix+"_add", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))) + ) + ); } - - function buildFromMontgomeryAffine() { - const f = module.addFunction(prefix + "_fromMontgomeryAffine"); - f.addParam("p1", "i32"); - f.addParam("pr", "i32"); + function buildNeg() { + const f = module.addFunction(prefix+"_neg"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); - f.addCode(c.call( - prefixField + "_fromMontgomery", - c.getLocal("p1"), - c.getLocal("pr") - )); - for (let i=1; i<2; i++) { - f.addCode(c.call( - prefixField + "_fromMontgomery", - c.i32_add(c.getLocal("p1"), c.i32_const(i*n8)), - c.i32_add(c.getLocal("pr"), c.i32_const(i*n8)) - )); - } + f.addCode( + c.call(prefix + "_sub", c.i32_const(pZero), c.getLocal("x"), c.getLocal("r")) + ); } - function buildAdd() { - const f = module.addFunction(prefix + "_add"); - f.addParam("p1", "i32"); - f.addParam("p2", "i32"); - f.addParam("pr", "i32"); - f.addLocal("z1", "i32"); - f.addLocal("z2", "i32"); + function buildIsNegative() { + const f = module.addFunction(prefix+"_isNegative"); + f.addParam("x", "i32"); + f.setReturnType("i32"); const c = f.getCodeBuilder(); - const x1 = c.getLocal("p1"); - const y1 = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); - f.addCode(c.setLocal("z1", c.i32_add(c.getLocal("p1"), c.i32_const(n8*2)))); - const z1 = c.getLocal("z1"); - const x2 = c.getLocal("p2"); - const y2 = c.i32_add(c.getLocal("p2"), c.i32_const(n8)); - f.addCode(c.setLocal("z2", c.i32_add(c.getLocal("p2"), c.i32_const(n8*2)))); - const z2 = c.getLocal("z2"); - const x3 = c.getLocal("pr"); - const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); - const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2)); - - const Z1Z1 = c.i32_const(module.alloc(n8)); - const Z2Z2 = c.i32_const(module.alloc(n8)); - const U1 = c.i32_const(module.alloc(n8)); - const U2 = c.i32_const(module.alloc(n8)); - const Z1_cubed = c.i32_const(module.alloc(n8)); - const Z2_cubed = c.i32_const(module.alloc(n8)); - const S1 = c.i32_const(module.alloc(n8)); - const S2 = c.i32_const(module.alloc(n8)); - const H = c.i32_const(module.alloc(n8)); - const S2_minus_S1 = c.i32_const(module.alloc(n8)); - const I = c.i32_const(module.alloc(n8)); - const J = c.i32_const(module.alloc(n8)); - const r = c.i32_const(module.alloc(n8)); - const r2 = c.i32_const(module.alloc(n8)); - const V = c.i32_const(module.alloc(n8)); - const V2 = c.i32_const(module.alloc(n8)); - const S1_J2 = c.i32_const(module.alloc(n8)); + const AUX = c.i32_const(module.alloc(n8)); f.addCode( - c.if( - c.call(prefix + "_isZero", c.getLocal("p1")), - [ - ...c.call(prefix + "_copy", c.getLocal("p2"), c.getLocal("pr")), - ...c.ret([]) - ] - ), - c.if( - c.call(prefix + "_isZero", c.getLocal("p2")), - [ - ...c.call(prefix + "_copy", c.getLocal("p1"), c.getLocal("pr")), - ...c.ret([]) - ] - ), - c.if( - c.call(prefixField + "_isOne", z1), - [ - ...c.call(prefix + "_addMixed", x2, x1, x3), - ...c.ret([]) - ] - ), - c.if( - c.call(prefixField + "_isOne", z2), - [ - ...c.call(prefix + "_addMixed", x1, x2, x3), - ...c.ret([]) - ] - ), - c.call(prefixField + "_square", z1, Z1Z1), - c.call(prefixField + "_square", z2, Z2Z2), - c.call(prefixField + "_mul", x1, Z2Z2, U1), - c.call(prefixField + "_mul", x2, Z1Z1, U2), - c.call(prefixField + "_mul", z1, Z1Z1, Z1_cubed), - c.call(prefixField + "_mul", z2, Z2Z2, Z2_cubed), - c.call(prefixField + "_mul", y1, Z2_cubed, S1), - c.call(prefixField + "_mul", y2, Z1_cubed, S2), - - c.if( - c.call(prefixField + "_eq", U1, U2), - c.if( - c.call(prefixField + "_eq", S1, S2), - [ - ...c.call(prefix + "_double", c.getLocal("p1"), c.getLocal("pr")), - ...c.ret([]) - ] - ) - ), - - c.call(prefixField + "_sub", U2, U1, H), - c.call(prefixField + "_sub", S2, S1, S2_minus_S1), - c.call(prefixField + "_add", H, H, I), - c.call(prefixField + "_square", I, I), - c.call(prefixField + "_mul", H, I, J), - c.call(prefixField + "_add", S2_minus_S1, S2_minus_S1, r), - c.call(prefixField + "_mul", U1, I, V), - c.call(prefixField + "_square", r, r2), - c.call(prefixField + "_add", V, V, V2), + c.call(prefix + "_fromMontgomery", c.getLocal("x"), AUX), + c.call(intPrefix + "_gte", AUX, c.i32_const(pePlusOne) ) + ); + } - c.call(prefixField + "_sub", r2, J, x3), - c.call(prefixField + "_sub", x3, V2, x3), + function buildSign() { + const f = module.addFunction(prefix+"_sign"); + f.addParam("x", "i32"); + f.setReturnType("i32"); - c.call(prefixField + "_mul", S1, J, S1_J2), - c.call(prefixField + "_add", S1_J2, S1_J2, S1_J2), + const c = f.getCodeBuilder(); - c.call(prefixField + "_sub", V, x3, y3), - c.call(prefixField + "_mul", y3, r, y3), - c.call(prefixField + "_sub", y3, S1_J2, y3), + const AUX = c.i32_const(module.alloc(n8)); - c.call(prefixField + "_add", z1, z2, z3), - c.call(prefixField + "_square", z3, z3), - c.call(prefixField + "_sub", z3, Z1Z1, z3), - c.call(prefixField + "_sub", z3, Z2Z2, z3), - c.call(prefixField + "_mul", z3, H, z3), + f.addCode( + c.if ( + c.call(intPrefix + "_isZero", c.getLocal("x")), + c.ret(c.i32_const(0)) + ), + c.call(prefix + "_fromMontgomery", c.getLocal("x"), AUX), + c.if( + c.call(intPrefix + "_gte", AUX, c.i32_const(pePlusOne)), + c.ret(c.i32_const(-1)) + ), + c.ret(c.i32_const(1)) ); - } - function buildAddMixed() { + function buildMReduct() { + const carries = module.alloc(n32*n32*8); - const f = module.addFunction(prefix + "_addMixed"); - f.addParam("p1", "i32"); - f.addParam("p2", "i32"); - f.addParam("pr", "i32"); - f.addLocal("z1", "i32"); + const f = module.addFunction(prefix+"_mReduct"); + f.addParam("t", "i32"); + f.addParam("r", "i32"); + f.addLocal("np32", "i64"); + f.addLocal("c", "i64"); + f.addLocal("m", "i64"); const c = f.getCodeBuilder(); - const x1 = c.getLocal("p1"); - const y1 = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); - f.addCode(c.setLocal("z1", c.i32_add(c.getLocal("p1"), c.i32_const(n8*2)))); - const z1 = c.getLocal("z1"); - const x2 = c.getLocal("p2"); - const y2 = c.i32_add(c.getLocal("p2"), c.i32_const(n8)); - const x3 = c.getLocal("pr"); - const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); - const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2)); + const np32 = Number(0x100000000n - modInv$2(q, 0x100000000n)); - const Z1Z1 = c.i32_const(module.alloc(n8)); - const U2 = c.i32_const(module.alloc(n8)); - const Z1_cubed = c.i32_const(module.alloc(n8)); - const S2 = c.i32_const(module.alloc(n8)); - const H = c.i32_const(module.alloc(n8)); - const HH = c.i32_const(module.alloc(n8)); - const S2_minus_y1 = c.i32_const(module.alloc(n8)); - const I = c.i32_const(module.alloc(n8)); - const J = c.i32_const(module.alloc(n8)); - const r = c.i32_const(module.alloc(n8)); - const r2 = c.i32_const(module.alloc(n8)); - const V = c.i32_const(module.alloc(n8)); - const V2 = c.i32_const(module.alloc(n8)); - const y1_J2 = c.i32_const(module.alloc(n8)); + f.addCode(c.setLocal("np32", c.i64_const(np32))); - f.addCode( - c.if( - c.call(prefix + "_isZero", c.getLocal("p1")), - [ - ...c.call(prefix + "_copyAffine", c.getLocal("p2"), c.getLocal("pr")), - ...c.call(prefixField + "_one", c.i32_add(c.getLocal("pr") , c.i32_const(n8*2))), - ...c.ret([]) - ] - ), - c.if( - c.call(prefix + "_isZeroAffine", c.getLocal("p2")), - [ - ...c.call(prefix + "_copy", c.getLocal("p1"), c.getLocal("pr")), - ...c.ret([]) - ] - ), - c.if( - c.call(prefixField + "_isOne", z1), - [ - ...c.call(prefix + "_addAffine", x1, x2, x3), - ...c.ret([]) - ] - ), - c.call(prefixField + "_square", z1, Z1Z1), - c.call(prefixField + "_mul", x2, Z1Z1, U2), - c.call(prefixField + "_mul", z1, Z1Z1, Z1_cubed), - c.call(prefixField + "_mul", y2, Z1_cubed, S2), + for (let i=0; i=n32) { + f.addCode( + c.i64_store32( + c.getLocal("r"), + (k-n32)*4, + c.getLocal(c0) + ) + ); + } + [c0, c1] = [c1, c0]; + f.addCode( + c.setLocal(c1, + c.i64_shr_u( + c.getLocal(c0), + c.i64_const(32) + ) + ) + ); + } f.addCode( - c.call(prefix + "_negAffine", c.getLocal("p2"), AUX), - c.call(prefix + "_addMixed", c.getLocal("p1"), AUX, c.getLocal("pr")), + c.i64_store32( + c.getLocal("r"), + n32*4-4, + c.getLocal(c0) + ) ); - } - - - function buildSubAffine() { - const f = module.addFunction(prefix + "_subAffine"); - f.addParam("p1", "i32"); - f.addParam("p2", "i32"); - f.addParam("pr", "i32"); - - const c = f.getCodeBuilder(); - - const AUX = c.i32_const(module.alloc(n8*3)); f.addCode( - c.call(prefix + "_negAffine", c.getLocal("p2"), AUX), - c.call(prefix + "_addAffine", c.getLocal("p1"), AUX, c.getLocal("pr")), + c.if( + c.i32_wrap_i64(c.getLocal(c1)), + c.drop(c.call(intPrefix+"_sub", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))), + c.if( + c.call(intPrefix+"_gte", c.getLocal("r"), c.i32_const(pq) ), + c.drop(c.call(intPrefix+"_sub", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))), + ) + ) ); } - // This sets Z to One - function buildNormalize() { - const f = module.addFunction(prefix + "_normalize"); - f.addParam("p1", "i32"); - f.addParam("pr", "i32"); - - const c = f.getCodeBuilder(); - - const x = c.getLocal("p1"); - const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); - const z = c.i32_add(c.getLocal("p1"), c.i32_const(n8*2)); - const x3 = c.getLocal("pr"); - const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); - const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2)); + function buildSquare() { - const Z_inv = c.i32_const(module.alloc(n8)); - const Z2_inv = c.i32_const(module.alloc(n8)); - const Z3_inv = c.i32_const(module.alloc(n8)); - - f.addCode( - c.if( - c.call(prefix + "_isZero", c.getLocal("p1")), - c.call(prefix + "_zero", c.getLocal("pr")), - [ - ...c.call(prefixField + "_inverse", z, Z_inv), - ...c.call(prefixField + "_square", Z_inv, Z2_inv), - ...c.call(prefixField + "_mul", Z_inv, Z2_inv, Z3_inv), - ...c.call(prefixField + "_mul", x, Z2_inv, x3), - ...c.call(prefixField + "_mul", y, Z3_inv, y3), - ...c.call(prefixField + "_one", z3), - ] - ) - ); - } + const f = module.addFunction(prefix+"_square"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + f.addLocal("c0", "i64"); + f.addLocal("c1", "i64"); + f.addLocal("c0_old", "i64"); + f.addLocal("c1_old", "i64"); + f.addLocal("np32", "i64"); - // Does not set Z. - function buildToAffine() { - const f = module.addFunction(prefix + "_toAffine"); - f.addParam("p1", "i32"); - f.addParam("pr", "i32"); + for (let i=0;i>1) )&&(i>1, k>>1) + ) + ) + ); - c.if( - c.call(prefixField + "_isZero", c.getLocal("itAux")), - [ - ...c.call(prefixField + "_zero", c.getLocal("itOut")), - ...c.call(prefixField + "_zero", c.i32_add(c.getLocal("itOut"), c.i32_const(n8))) - ], - [ - ...c.call( - prefixField+"_mul", - c.getLocal("itAux"), - c.i32_add(c.getLocal("itIn"), c.i32_const(n8)), - tmp, - ), - ...c.call( - prefixField+"_square", - c.getLocal("itAux"), - c.getLocal("itAux") - ), - ...c.call( - prefixField+"_mul", - c.getLocal("itAux"), - c.getLocal("itIn"), - c.getLocal("itOut"), - ), - ...c.call( - prefixField+"_mul", - c.getLocal("itAux"), - tmp, - c.i32_add(c.getLocal("itOut"), c.i32_const(n8)), - ), - ] + f.addCode( + c.setLocal(c1, + c.i64_add( + c.getLocal(c1), + c.i64_shr_u( + c.getLocal(c0), + c.i64_const(32) + ) + ) + ) + ); + } + + // Add the old carry + + if (k>0) { + f.addCode( + c.setLocal(c0, + c.i64_add( + c.i64_and( + c.getLocal(c0), + c.i64_const(0xFFFFFFFF) + ), + c.i64_and( + c.getLocal(c0_old), + c.i64_const(0xFFFFFFFF) + ), + ) + ) + ); + + f.addCode( + c.setLocal(c1, + c.i64_add( + c.i64_add( + c.getLocal(c1), + c.i64_shr_u( + c.getLocal(c0), + c.i64_const(32) + ) + ), + c.getLocal(c1_old) + ) + ) + ); + } + + + for (let i=Math.max(1, k-n32+1); (i<=k)&&(i=n32) { + f.addCode( + c.i64_store32( + c.getLocal("r"), + (k-n32)*4, + c.getLocal(c0) + ) + ); + } + f.addCode( + c.setLocal( + c0_old, + c.getLocal(c1) ), + c.setLocal( + c1_old, + c.i64_shr_u( + c.getLocal(c0_old), + c.i64_const(32) + ) + ) + ); + } + f.addCode( + c.i64_store32( + c.getLocal("r"), + n32*4-4, + c.getLocal(c0_old) + ) + ); - c.setLocal("itIn", c.i32_add(c.getLocal("itIn"), c.i32_const(n8*3))), - c.setLocal("itOut", c.i32_add(c.getLocal("itOut"), c.i32_const(n8*2))), - c.setLocal("itAux", c.i32_add(c.getLocal("itAux"), c.i32_const(n8))), - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )), - c.i32_store( - c.i32_const(0), - c.getLocal("pAux") + f.addCode( + c.if( + c.i32_wrap_i64(c.getLocal(c1_old)), + c.drop(c.call(intPrefix+"_sub", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))), + c.if( + c.call(intPrefix+"_gte", c.getLocal("r"), c.i32_const(pq) ), + c.drop(c.call(intPrefix+"_sub", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))), + ) ) ); } - // This function is private and does not allow to OVERLAP buffers. - function buildReverseBytes() { - const f = module.addFunction(prefix + "__reverseBytes"); - f.addParam("pIn", "i32"); + function buildSquareOld() { + const f = module.addFunction(prefix+"_squareOld"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + + const c = f.getCodeBuilder(); + + f.addCode(c.call(prefix + "_mul", c.getLocal("x"), c.getLocal("x"), c.getLocal("r"))); + } + + function buildToMontgomery() { + const f = module.addFunction(prefix+"_toMontgomery"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + + const c = f.getCodeBuilder(); + f.addCode(c.call(prefix+"_mul", c.getLocal("x"), c.i32_const(pR2), c.getLocal("r"))); + } + + function buildFromMontgomery() { + + const pAux2 = module.alloc(n8*2); + + const f = module.addFunction(prefix+"_fromMontgomery"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + + const c = f.getCodeBuilder(); + f.addCode(c.call(intPrefix + "_copy", c.getLocal("x"), c.i32_const(pAux2) )); + f.addCode(c.call(intPrefix + "_zero", c.i32_const(pAux2 + n8) )); + f.addCode(c.call(prefix+"_mReduct", c.i32_const(pAux2), c.getLocal("r"))); + } + + function buildInverse() { + + const f = module.addFunction(prefix+ "_inverse"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + + const c = f.getCodeBuilder(); + f.addCode(c.call(prefix + "_fromMontgomery", c.getLocal("x"), c.getLocal("r"))); + f.addCode(c.call(intPrefix + "_inverseMod", c.getLocal("r"), c.i32_const(pq), c.getLocal("r"))); + f.addCode(c.call(prefix + "_toMontgomery", c.getLocal("r"), c.getLocal("r"))); + } + + // Calculate various valuse needed for sqrt + + + let _nqr = 2n; + if (isPrime(q)) { + while (modPow$1(_nqr, _e, q) !== _minusOne) _nqr = _nqr + 1n; + } + + let s2 = 0; + let _t = _minusOne; + + while ((!isOdd$3(_t))&&(_t !== 0n)) { + s2++; + _t = _t >> 1n; + } + const pt = module.alloc(n8, utils$4.bigInt2BytesLE(_t, n8)); + + const _nqrToT = modPow$1(_nqr, _t, q); + const pNqrToT = module.alloc(utils$4.bigInt2BytesLE((_nqrToT << BigInt(n64*64)) % q, n8)); + + const _tPlusOneOver2 = (_t + 1n) >> 1n; + const ptPlusOneOver2 = module.alloc(n8, utils$4.bigInt2BytesLE(_tPlusOneOver2, n8)); + + function buildSqrt() { + + const f = module.addFunction(prefix+ "_sqrt"); f.addParam("n", "i32"); - f.addParam("pOut", "i32"); - f.addLocal("itOut", "i32"); - f.addLocal("itIn", "i32"); + f.addParam("r", "i32"); + f.addLocal("m", "i32"); + f.addLocal("i", "i32"); + f.addLocal("j", "i32"); const c = f.getCodeBuilder(); + const ONE = c.i32_const(pOne); + const C = c.i32_const(module.alloc(n8)); + const T = c.i32_const(module.alloc(n8)); + const R = c.i32_const(module.alloc(n8)); + const SQ = c.i32_const(module.alloc(n8)); + const B = c.i32_const(module.alloc(n8)); + f.addCode( - c.setLocal( - "itOut", - c.i32_sub( - c.i32_add( - c.getLocal("pOut"), - c.getLocal("n") - ), - c.i32_const(1) + + // If (n==0) return 0 + c.if( + c.call(prefix + "_isZero", c.getLocal("n")), + c.ret( + c.call(prefix + "_zero", c.getLocal("r")) ) ), - c.setLocal( - "itIn", - c.getLocal("pIn") + + c.setLocal("m", c.i32_const(s2)), + c.call(prefix + "_copy", c.i32_const(pNqrToT), C), + c.call(prefix + "_exp", c.getLocal("n"), c.i32_const(pt), c.i32_const(n8), T), + c.call(prefix + "_exp", c.getLocal("n"), c.i32_const(ptPlusOneOver2), c.i32_const(n8), R), + + c.block(c.loop( + c.br_if(1, c.call(prefix + "_eq", T, ONE)), + + c.call(prefix + "_square", T, SQ), + c.setLocal("i", c.i32_const(1)), + c.block(c.loop( + c.br_if(1, c.call(prefix + "_eq", SQ, ONE)), + c.call(prefix + "_square", SQ, SQ), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )), + + c.call(prefix + "_copy", C, B), + c.setLocal("j", c.i32_sub(c.i32_sub( c.getLocal("m"), c.getLocal("i")), c.i32_const(1)) ), + c.block(c.loop( + c.br_if(1, c.i32_eqz(c.getLocal("j"))), + c.call(prefix + "_square", B, B), + c.setLocal("j", c.i32_sub(c.getLocal("j"), c.i32_const(1))), + c.br(0) + )), + + c.setLocal("m", c.getLocal("i")), + c.call(prefix + "_square", B, C), + c.call(prefix + "_mul", T, C, T), + c.call(prefix + "_mul", R, B, R), + + c.br(0) + )), + + c.if( + c.call(prefix + "_isNegative", R), + c.call(prefix + "_neg", R, c.getLocal("r")), + c.call(prefix + "_copy", R, c.getLocal("r")), + ) + ); + } + + function buildIsSquare() { + const f = module.addFunction(prefix+"_isSquare"); + f.addParam("n", "i32"); + f.setReturnType("i32"); + + const c = f.getCodeBuilder(); + + const ONE = c.i32_const(pOne); + const AUX = c.i32_const(module.alloc(n8)); + + f.addCode( + c.if( + c.call(prefix + "_isZero", c.getLocal("n")), + c.ret(c.i32_const(1)) ), + c.call(prefix + "_exp", c.getLocal("n"), c.i32_const(pe), c.i32_const(n8), AUX), + c.call(prefix + "_eq", AUX, ONE) + ); + } + + + function buildLoad() { + const f = module.addFunction(prefix+"_load"); + f.addParam("scalar", "i32"); + f.addParam("scalarLen", "i32"); + f.addParam("r", "i32"); + f.addLocal("p", "i32"); + f.addLocal("l", "i32"); + f.addLocal("i", "i32"); + f.addLocal("j", "i32"); + const c = f.getCodeBuilder(); + + const R = c.i32_const(module.alloc(n8)); + const pAux = module.alloc(n8); + const AUX = c.i32_const(pAux); + + f.addCode( + c.call(intPrefix + "_zero", c.getLocal("r")), + c.setLocal("i", c.i32_const(n8)), + c.setLocal("p", c.getLocal("scalar")), c.block(c.loop( - c.br_if(1, c.i32_lt_s( c.getLocal("itOut"), c.getLocal("pOut") )), + c.br_if(1, c.i32_gt_u(c.getLocal("i"), c.getLocal("scalarLen"))), + + c.if( + c.i32_eq(c.getLocal("i"), c.i32_const(n8)), + c.call(prefix + "_one", R), + c.call(prefix + "_mul", R, c.i32_const(pR2), R) + ), + c.call(prefix + "_mul", c.getLocal("p"), R, AUX), + c.call(prefix + "_add", c.getLocal("r"), AUX, c.getLocal("r")), + + c.setLocal("p", c.i32_add(c.getLocal("p"), c.i32_const(n8))), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(n8))), + c.br(0) + )), + + c.setLocal("l", c.i32_rem_u( c.getLocal("scalarLen"), c.i32_const(n8))), + c.if(c.i32_eqz(c.getLocal("l")), c.ret([])), + c.call(intPrefix + "_zero", AUX), + c.setLocal("j", c.i32_const(0)), + c.block(c.loop( + c.br_if(1, c.i32_eq(c.getLocal("j"), c.getLocal("l"))), + c.i32_store8( - c.getLocal("itOut"), - c.i32_load8_u(c.getLocal("itIn")), + c.getLocal("j"), + pAux, + c.i32_load8_u(c.getLocal("p")), ), - c.setLocal("itOut", c.i32_sub(c.getLocal("itOut"), c.i32_const(1))), - c.setLocal("itIn", c.i32_add(c.getLocal("itIn"), c.i32_const(1))), + c.setLocal("p", c.i32_add(c.getLocal("p"), c.i32_const(1))), + c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), c.br(0) )), - ); + c.if( + c.i32_eq(c.getLocal("i"), c.i32_const(n8)), + c.call(prefix + "_one", R), + c.call(prefix + "_mul", R, c.i32_const(pR2), R) + ), + c.call(prefix + "_mul", AUX, R, AUX), + c.call(prefix + "_add", c.getLocal("r"), AUX, c.getLocal("r")), + ); } - function buildLEMtoC() { - const f = module.addFunction(prefix + "_LEMtoC"); - f.addParam("pIn", "i32"); - f.addParam("pOut", "i32"); + function buildTimesScalar() { + const f = module.addFunction(prefix+"_timesScalar"); + f.addParam("x", "i32"); + f.addParam("scalar", "i32"); + f.addParam("scalarLen", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); - const tmp = c.i32_const(module.alloc(n8)); + const AUX = c.i32_const(module.alloc(n8)); f.addCode( - c.if( - c.call(prefix + "_isZeroAffine", c.getLocal("pIn")), - [ - ...c.call(prefixField + "_zero", c.getLocal("pOut")), - ...c.i32_store8( - c.getLocal("pOut"), - c.i32_const(0x40) - ), - ...c.ret([]) - ] - ), - c.call(prefixField + "_fromMontgomery", c.getLocal("pIn"), tmp), - c.call(prefix + "__reverseBytes", tmp, c.i32_const(n8), c.getLocal("pOut")), - c.if( - c.i32_eq( - c.call(prefixField + "_sign", c.i32_add(c.getLocal("pIn"), c.i32_const(n8))), - c.i32_const(-1) - ), - c.i32_store8( - c.getLocal("pOut"), - c.i32_or( - c.i32_load8_u(c.getLocal("pOut")), - c.i32_const(0x80) - ) - ) - ), + c.call(prefix + "_load", c.getLocal("scalar"), c.getLocal("scalarLen"), AUX), + c.call(prefix + "_toMontgomery", AUX, AUX), + c.call(prefix + "_mul", c.getLocal("x"), AUX, c.getLocal("r")), ); } - function buildLEMtoU() { - const f = module.addFunction(prefix + "_LEMtoU"); - f.addParam("pIn", "i32"); - f.addParam("pOut", "i32"); + function buildIsOne() { + const f = module.addFunction(prefix+"_isOne"); + f.addParam("x", "i32"); + f.setReturnType("i32"); const c = f.getCodeBuilder(); - - const pTmp = module.alloc(n8*2); - const tmp = c.i32_const(pTmp); - const tmpX = c.i32_const(pTmp); - const tmpY = c.i32_const(pTmp + n8); - f.addCode( - c.if( - c.call(prefix + "_isZeroAffine", c.getLocal("pIn")), - [ - ...c.call(prefix + "_zeroAffine", c.getLocal("pOut")), - ...c.ret([]) - ] - ), - - c.call(prefix + "_fromMontgomeryAffine", c.getLocal("pIn"), tmp), - - c.call(prefix + "__reverseBytes", tmpX, c.i32_const(n8), c.getLocal("pOut")), - c.call(prefix + "__reverseBytes", tmpY, c.i32_const(n8), c.i32_add(c.getLocal("pOut"), c.i32_const(n8))), + c.ret(c.call(intPrefix + "_eq", c.getLocal("x"), c.i32_const(pOne))) ); } - function buildUtoLEM() { - const f = module.addFunction(prefix + "_UtoLEM"); - f.addParam("pIn", "i32"); - f.addParam("pOut", "i32"); - const c = f.getCodeBuilder(); + module.exportFunction(intPrefix + "_copy", prefix+"_copy"); + module.exportFunction(intPrefix + "_zero", prefix+"_zero"); + module.exportFunction(intPrefix + "_isZero", prefix+"_isZero"); + module.exportFunction(intPrefix + "_eq", prefix+"_eq"); - const pTmp = module.alloc(n8*2); - const tmp = c.i32_const(pTmp); - const tmpX = c.i32_const(pTmp); - const tmpY = c.i32_const(pTmp + n8); + buildIsOne(); + buildAdd(); + buildSub(); + buildNeg(); + buildMReduct(); + buildMul(); + buildSquare(); + buildSquareOld(); + buildToMontgomery(); + buildFromMontgomery(); + buildIsNegative(); + buildSign(); + buildInverse(); + buildOne(); + buildLoad(); + buildTimesScalar(); + buildBatchInverse$2(module, prefix); + buildBatchConvertion$1(module, prefix + "_batchToMontgomery", prefix + "_toMontgomery", n8, n8); + buildBatchConvertion$1(module, prefix + "_batchFromMontgomery", prefix + "_fromMontgomery", n8, n8); + buildBatchConvertion$1(module, prefix + "_batchNeg", prefix + "_neg", n8, n8); + buildBatchOp(module, prefix + "_batchAdd", prefix + "_add", n8, n8); + buildBatchOp(module, prefix + "_batchSub", prefix + "_sub", n8, n8); + buildBatchOp(module, prefix + "_batchMul", prefix + "_mul", n8, n8); - f.addCode( - c.if( - c.i32_and(c.i32_load8_u(c.getLocal("pIn")), c.i32_const(0x40)), - [ - ...c.call(prefix + "_zeroAffine", c.getLocal("pOut")), - ...c.ret([]) - ] - ), - c.call(prefix + "__reverseBytes", c.getLocal("pIn"), c.i32_const(n8), tmpX), - c.call(prefix + "__reverseBytes", c.i32_add(c.getLocal("pIn"), c.i32_const(n8)), c.i32_const(n8), tmpY), - c.call(prefix + "_toMontgomeryAffine", tmp, c.getLocal("pOut")) - ); + module.exportFunction(prefix + "_add"); + module.exportFunction(prefix + "_sub"); + module.exportFunction(prefix + "_neg"); + module.exportFunction(prefix + "_isNegative"); + module.exportFunction(prefix + "_isOne"); + module.exportFunction(prefix + "_sign"); + module.exportFunction(prefix + "_mReduct"); + module.exportFunction(prefix + "_mul"); + module.exportFunction(prefix + "_square"); + module.exportFunction(prefix + "_squareOld"); + module.exportFunction(prefix + "_fromMontgomery"); + module.exportFunction(prefix + "_toMontgomery"); + module.exportFunction(prefix + "_inverse"); + module.exportFunction(prefix + "_one"); + module.exportFunction(prefix + "_load"); + module.exportFunction(prefix + "_timesScalar"); + buildExp$2( + module, + prefix + "_exp", + n8, + prefix + "_mul", + prefix + "_square", + intPrefix + "_copy", + prefix + "_one", + ); + module.exportFunction(prefix + "_exp"); + module.exportFunction(prefix + "_batchInverse"); + if (isPrime(q)) { + buildSqrt(); + buildIsSquare(); + module.exportFunction(prefix + "_sqrt"); + module.exportFunction(prefix + "_isSquare"); } + module.exportFunction(prefix + "_batchToMontgomery"); + module.exportFunction(prefix + "_batchFromMontgomery"); + // console.log(module.functionIdxByName); - function buildCtoLEM() { - const f = module.addFunction(prefix + "_CtoLEM"); - f.addParam("pIn", "i32"); - f.addParam("pOut", "i32"); - f.addLocal("firstByte", "i32"); - f.addLocal("greatest", "i32"); + return prefix; +}; - const c = f.getCodeBuilder(); +/* + Copyright 2019 0KIMS association. - const pTmp = module.alloc(n8*2); - const tmpX = c.i32_const(pTmp); - const tmpY = c.i32_const(pTmp + n8); + This file is part of wasmsnark (Web Assembly zkSnark Prover). - f.addCode( - c.setLocal("firstByte", c.i32_load8_u(c.getLocal("pIn"))), - c.if( - c.i32_and( - c.getLocal("firstByte"), - c.i32_const(0x40) - ), - [ - ...c.call(prefix + "_zeroAffine", c.getLocal("pOut")), - ...c.ret([]) - ] - ), - c.setLocal( - "greatest", - c.i32_and( - c.getLocal("firstByte"), - c.i32_const(0x80) - ) - ), + wasmsnark 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. - c.call(prefixField + "_copy", c.getLocal("pIn"), tmpY), - c.i32_store8(tmpY, c.i32_and(c.getLocal("firstByte"), c.i32_const(0x3F))), - c.call(prefix + "__reverseBytes", tmpY, c.i32_const(n8), tmpX), - c.call(prefixField + "_toMontgomery", tmpX, c.getLocal("pOut")), + wasmsnark 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. - c.call(prefixField + "_square", c.getLocal("pOut"), tmpY), - c.call(prefixField + "_mul", c.getLocal("pOut"), tmpY, tmpY), - c.call(prefixField + "_add", tmpY, c.i32_const(pB), tmpY), + You should have received a copy of the GNU General Public License + along with wasmsnark. If not, see . +*/ - c.call(prefixField + "_sqrt", tmpY, tmpY), - c.call(prefixField + "_neg", tmpY, tmpX), +const buildF1m$2 =build_f1m; +const { bitLength: bitLength$3 } = bigint; - c.if( - c.i32_eq( - c.call(prefixField + "_sign", tmpY), - c.i32_const(-1) - ), - c.if( - c.getLocal("greatest"), - c.call(prefixField + "_copy", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8))), - c.call(prefixField + "_neg", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8))) - ), - c.if( - c.getLocal("greatest"), - c.call(prefixField + "_neg", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8))), - c.call(prefixField + "_copy", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8))) - ), - ) +var build_f1 = function buildF1(module, _q, _prefix, _f1mPrefix, _intPrefix) { + + const q = BigInt(_q); + const n64 = Math.floor((bitLength$3(q - 1n) - 1)/64) +1; + const n8 = n64*8; + + const prefix = _prefix || "f1"; + if (module.modules[prefix]) return prefix; // already builded + module.modules[prefix] = { + n64: n64 + }; + + const intPrefix = _intPrefix || "int"; + const f1mPrefix = buildF1m$2(module, q, _f1mPrefix, intPrefix); - ); + + const pR2 = module.modules[f1mPrefix].pR2; + const pq = module.modules[f1mPrefix].pq; + const pePlusOne = module.modules[f1mPrefix].pePlusOne; + + function buildMul() { + const pAux1 = module.alloc(n8); + + const f = module.addFunction(prefix+ "_mul"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.addParam("r", "i32"); + + const c = f.getCodeBuilder(); + f.addCode(c.call(f1mPrefix + "_mul", c.getLocal("x"), c.getLocal("y"), c.i32_const(pAux1))); + f.addCode(c.call(f1mPrefix + "_mul", c.i32_const(pAux1), c.i32_const(pR2), c.getLocal("r"))); } - function buildInCurveAffine() { - const f = module.addFunction(prefix + "_inCurveAffine"); - f.addParam("pIn", "i32"); - f.setReturnType("i32"); + function buildSquare() { + const f = module.addFunction(prefix+"_square"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); - const x = c.getLocal("pIn"); - const y = c.i32_add(c.getLocal("pIn"), c.i32_const(n8)); + f.addCode(c.call(prefix + "_mul", c.getLocal("x"), c.getLocal("x"), c.getLocal("r"))); + } - const y2 = c.i32_const(module.alloc(n8)); - const x3b = c.i32_const(module.alloc(n8)); - f.addCode( - c.call(prefixField + "_square", y, y2), - c.call(prefixField + "_square", x, x3b), - c.call(prefixField + "_mul", x, x3b, x3b), - c.call(prefixField + "_add", x3b, c.i32_const(pB), x3b), + function buildInverse() { - c.ret( - c.call(prefixField + "_eq", y2, x3b) - ) - ); + const f = module.addFunction(prefix+ "_inverse"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + + const c = f.getCodeBuilder(); + f.addCode(c.call(intPrefix + "_inverseMod", c.getLocal("x"), c.i32_const(pq), c.getLocal("r"))); } - function buildInCurve() { - const f = module.addFunction(prefix + "_inCurve"); - f.addParam("pIn", "i32"); + function buildIsNegative() { + const f = module.addFunction(prefix+"_isNegative"); + f.addParam("x", "i32"); f.setReturnType("i32"); const c = f.getCodeBuilder(); - const aux = c.i32_const(module.alloc(n8*2)); - f.addCode( - c.call(prefix + "_toAffine", c.getLocal("pIn"), aux), - - c.ret( - c.call(prefix + "_inCurveAffine", aux), - ) + c.call(intPrefix + "_gte", c.getLocal("x"), c.i32_const(pePlusOne) ) ); } - buildIsZeroAffine(); - buildIsZero(); - buildZeroAffine(); - buildZero(); - buildCopyAffine(); - buildCopy(); - buildToJacobian(); - buildEqAffine(); - buildEqMixed(); - buildEq(); - buildDoubleAffine(); - buildDouble(); - buildAddAffine(); - buildAddMixed(); - buildAdd(); - buildNegAffine(); - buildNeg(); - buildSubAffine(); - buildSubMixed(); - buildSub(); - buildFromMontgomeryAffine(); - buildFromMontgomery(); - buildToMontgomeryAffine(); - buildToMontgomery(); - buildToAffine(); - buildInCurveAffine(); - buildInCurve(); - buildBatchToAffine(); + buildMul(); + buildSquare(); + buildInverse(); + buildIsNegative(); + module.exportFunction(f1mPrefix + "_add", prefix + "_add"); + module.exportFunction(f1mPrefix + "_sub", prefix + "_sub"); + module.exportFunction(f1mPrefix + "_neg", prefix + "_neg"); + module.exportFunction(prefix + "_mul"); + module.exportFunction(prefix + "_square"); + module.exportFunction(prefix + "_inverse"); + module.exportFunction(prefix + "_isNegative"); + module.exportFunction(f1mPrefix + "_copy", prefix+"_copy"); + module.exportFunction(f1mPrefix + "_zero", prefix+"_zero"); + module.exportFunction(f1mPrefix + "_one", prefix+"_one"); + module.exportFunction(f1mPrefix + "_isZero", prefix+"_isZero"); + module.exportFunction(f1mPrefix + "_eq", prefix+"_eq"); - buildNormalize(); + return prefix; +}; +/* + Copyright 2019 0KIMS association. - buildReverseBytes(); + This file is part of wasmsnark (Web Assembly zkSnark Prover). - buildLEMtoU(); - buildLEMtoC(); - buildUtoLEM(); - buildCtoLEM(); + wasmsnark 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. - buildBatchConvertion(module, prefix + "_batchLEMtoU", prefix + "_LEMtoU", n8*2, n8*2); - buildBatchConvertion(module, prefix + "_batchLEMtoC", prefix + "_LEMtoC", n8*2, n8); - buildBatchConvertion(module, prefix + "_batchUtoLEM", prefix + "_UtoLEM", n8*2, n8*2); - buildBatchConvertion(module, prefix + "_batchCtoLEM", prefix + "_CtoLEM", n8, n8*2, true); + wasmsnark 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. - buildBatchConvertion(module, prefix + "_batchToJacobian", prefix + "_toJacobian", n8*2, n8*3, true); + You should have received a copy of the GNU General Public License + along with wasmsnark. If not, see . +*/ - buildMultiexp$1(module, prefix, prefix + "_multiexp", prefix + "_add", n8*3); - buildMultiexp$1(module, prefix, prefix + "_multiexpAffine", prefix + "_addMixed", n8*2); +const buildExp$1 = build_timesscalar; +const buildBatchInverse$1 = build_batchinverse; +const utils$3 = utils$5; - /* - buildTimesScalar( - module, - prefix + "_timesScalarOld", - n8*3, - prefix + "_add", - prefix + "_double", - prefix + "_copy", - prefix + "_zero", - ); - */ - buildTimesScalarNAF( - module, - prefix + "_timesScalar", - n8*3, - prefix + "_add", - prefix + "_double", - prefix + "_sub", - prefix + "_copy", - prefix + "_zero" - ); +var build_f2m = function buildF2m(module, mulNonResidueFn, prefix, f1mPrefix) { - buildTimesScalarNAF( - module, - prefix + "_timesScalarAffine", - n8*2, - prefix + "_addMixed", - prefix + "_double", - prefix + "_subMixed", - prefix + "_copyAffine", - prefix + "_zero" - ); + if (module.modules[prefix]) return prefix; // already builded - module.exportFunction(prefix + "_isZero"); - module.exportFunction(prefix + "_isZeroAffine"); + const f1n8 = module.modules[f1mPrefix].n64*8; + const q = module.modules[f1mPrefix].q; - module.exportFunction(prefix + "_eq"); - module.exportFunction(prefix + "_eqMixed"); - module.exportFunction(prefix + "_eqAffine"); + module.modules[prefix] = { + n64: module.modules[f1mPrefix].n64*2 + }; - module.exportFunction(prefix + "_copy"); - module.exportFunction(prefix + "_copyAffine"); + function buildAdd() { + const f = module.addFunction(prefix+"_add"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.addParam("r", "i32"); - module.exportFunction(prefix + "_zero"); - module.exportFunction(prefix + "_zeroAffine"); + const c = f.getCodeBuilder(); - module.exportFunction(prefix + "_double"); - module.exportFunction(prefix + "_doubleAffine"); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const y0 = c.getLocal("y"); + const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); - module.exportFunction(prefix + "_add"); - module.exportFunction(prefix + "_addMixed"); - module.exportFunction(prefix + "_addAffine"); + f.addCode( + c.call(f1mPrefix+"_add", x0, y0, r0), + c.call(f1mPrefix+"_add", x1, y1, r1), + ); + } + + function buildTimesScalar() { + const f = module.addFunction(prefix+"_timesScalar"); + f.addParam("x", "i32"); + f.addParam("scalar", "i32"); + f.addParam("scalarLen", "i32"); + f.addParam("r", "i32"); + + const c = f.getCodeBuilder(); + + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + + f.addCode( + c.call(f1mPrefix+"_timesScalar", x0, c.getLocal("scalar"), c.getLocal("scalarLen"), r0), + c.call(f1mPrefix+"_timesScalar", x1, c.getLocal("scalar"), c.getLocal("scalarLen"), r1), + ); + } + + function buildSub() { + const f = module.addFunction(prefix+"_sub"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.addParam("r", "i32"); + + const c = f.getCodeBuilder(); + + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const y0 = c.getLocal("y"); + const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + + f.addCode( + c.call(f1mPrefix+"_sub", x0, y0, r0), + c.call(f1mPrefix+"_sub", x1, y1, r1), + ); + } + + function buildNeg() { + const f = module.addFunction(prefix+"_neg"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + + const c = f.getCodeBuilder(); + + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); - module.exportFunction(prefix + "_neg"); - module.exportFunction(prefix + "_negAffine"); + f.addCode( + c.call(f1mPrefix+"_neg", x0, r0), + c.call(f1mPrefix+"_neg", x1, r1), + ); + } - module.exportFunction(prefix + "_sub"); - module.exportFunction(prefix + "_subMixed"); - module.exportFunction(prefix + "_subAffine"); + function buildConjugate() { + const f = module.addFunction(prefix+"_conjugate"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); - module.exportFunction(prefix + "_fromMontgomery"); - module.exportFunction(prefix + "_fromMontgomeryAffine"); + const c = f.getCodeBuilder(); - module.exportFunction(prefix + "_toMontgomery"); - module.exportFunction(prefix + "_toMontgomeryAffine"); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); - module.exportFunction(prefix + "_timesScalar"); - module.exportFunction(prefix + "_timesScalarAffine"); + f.addCode( + c.call(f1mPrefix+"_copy", x0, r0), + c.call(f1mPrefix+"_neg", x1, r1), + ); + } - module.exportFunction(prefix + "_normalize"); - // Convertion functions - module.exportFunction(prefix + "_LEMtoU"); - module.exportFunction(prefix + "_LEMtoC"); - module.exportFunction(prefix + "_UtoLEM"); - module.exportFunction(prefix + "_CtoLEM"); + function buildIsNegative() { + const f = module.addFunction(prefix+"_isNegative"); + f.addParam("x", "i32"); + f.setReturnType("i32"); - module.exportFunction(prefix + "_batchLEMtoU"); - module.exportFunction(prefix + "_batchLEMtoC"); - module.exportFunction(prefix + "_batchUtoLEM"); - module.exportFunction(prefix + "_batchCtoLEM"); + const c = f.getCodeBuilder(); - module.exportFunction(prefix + "_toAffine"); - module.exportFunction(prefix + "_toJacobian"); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - module.exportFunction(prefix + "_batchToAffine"); - module.exportFunction(prefix + "_batchToJacobian"); + f.addCode( + c.if( + c.call(f1mPrefix+"_isZero", x1), + c.ret(c.call(f1mPrefix+"_isNegative", x0)) + ), + c.ret(c.call(f1mPrefix+"_isNegative", x1)) + ); + } - module.exportFunction(prefix + "_inCurve"); - module.exportFunction(prefix + "_inCurveAffine"); + function buildMul() { + const f = module.addFunction(prefix+"_mul"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.addParam("r", "i32"); - /* - buildG1MulScalar(module, zq); - module.exportFunction("g1MulScalar"); - */ + const c = f.getCodeBuilder(); - return prefix; -}; + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const y0 = c.getLocal("y"); + const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); -/* - Copyright 2019 0KIMS association. + const A = c.i32_const(module.alloc(f1n8)); + const B = c.i32_const(module.alloc(f1n8)); + const C = c.i32_const(module.alloc(f1n8)); + const D = c.i32_const(module.alloc(f1n8)); - This file is part of wasmsnark (Web Assembly zkSnark Prover). - wasmsnark 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. + f.addCode( + c.call(f1mPrefix + "_mul", x0, y0, A), // A = x0*y0 + c.call(f1mPrefix + "_mul", x1, y1, B), // B = x1*y1 - wasmsnark 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. + c.call(f1mPrefix + "_add", x0, x1, C), // C = x0 + x1 + c.call(f1mPrefix + "_add", y0, y1, D), // D = y0 + y1 + c.call(f1mPrefix + "_mul", C, D, C), // C = (x0 + x1)*(y0 + y1) = x0*y0+x0*y1+x1*y0+x1*y1 - You should have received a copy of the GNU General Public License - along with wasmsnark. If not, see . -*/ + // c.call(f1mPrefix + "_mul", B, c.i32_const(pNonResidue), r0), // r0 = nr*(x1*y1) + c.call(mulNonResidueFn, B, r0), // r0 = nr*(x1*y1) + c.call(f1mPrefix + "_add", A, r0, r0), // r0 = x0*y0 + nr*(x1*y1) + c.call(f1mPrefix + "_add", A, B, r1), // r1 = x0*y0+x1*y1 + c.call(f1mPrefix + "_sub", C, r1, r1) // r1 = x0*y0+x0*y1+x1*y0+x1*y1 - x0*y0+x1*y1 = x0*y1+x1*y0 + ); -const { isOdd: isOdd$2, modInv: modInv$1, modPow } = bigint; -const utils$3 = utils$6; + } -var build_fft = function buildFFT(module, prefix, gPrefix, fPrefix, opGtimesF) { + function buildMul1() { + const f = module.addFunction(prefix+"_mul1"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.addParam("r", "i32"); - const n64f = module.modules[fPrefix].n64; - const n8f = n64f*8; + const c = f.getCodeBuilder(); - const n64g = module.modules[gPrefix].n64; - const n8g = n64g*8; + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const y = c.getLocal("y"); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); - const q = module.modules[fPrefix].q; - let rem = q - 1n; - let maxBits = 0; - while (!isOdd$2(rem)) { - maxBits ++; - rem = rem >> 1n; + f.addCode( + c.call(f1mPrefix + "_mul", x0, y, r0), // A = x0*y + c.call(f1mPrefix + "_mul", x1, y, r1), // B = x1*y + ); } - let nr = 2n; + function buildSquare() { + const f = module.addFunction(prefix+"_square"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); - while ( modPow(nr, q >> 1n, q) === 1n ) nr = nr + 1n; + const c = f.getCodeBuilder(); - // console.log(nr); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); - const w = new Array(maxBits+1); - w[maxBits] = modPow(nr, rem, q); + const AB = c.i32_const(module.alloc(f1n8)); + const APB = c.i32_const(module.alloc(f1n8)); + const APNB = c.i32_const(module.alloc(f1n8)); + const ABPNAB = c.i32_const(module.alloc(f1n8)); - let n=maxBits-1; - while (n>=0) { - w[n] = modPow(w[n+1], 2n, q); - n--; - } - const bytes = []; - const R = (1n << BigInt(n8f*8)) % q; + f.addCode( + // AB = x0*y1 + c.call(f1mPrefix + "_mul", x0, x1, AB), - for (let i=0; i> i); - } - } - return r; - } + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); - const rtable = Array(256); - for (let i=0; i<256; i++) { - rtable[i] = rev(i); + f.addCode( + c.call(f1mPrefix+"_toMontgomery", x0, r0), + c.call(f1mPrefix+"_toMontgomery", x1, r1) + ); } - const REVTABLE = module.alloc(rtable); + function buildFromMontgomery() { + const f = module.addFunction(prefix+"_fromMontgomery"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + const c = f.getCodeBuilder(); - function buildLog2() { - const f = module.addFunction(prefix+"__log2"); - f.addParam("n", "i32"); - f.setReturnType("i32"); - f.addLocal("bits", "i32"); - f.addLocal("aux", "i32"); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + + f.addCode( + c.call(f1mPrefix+"_fromMontgomery", x0, r0), + c.call(f1mPrefix+"_fromMontgomery", x1, r1) + ); + } + + function buildCopy() { + const f = module.addFunction(prefix+"_copy"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + f.addCode( - c.setLocal( - "aux", - c.i32_shr_u( - c.getLocal("n"), - c.i32_const(1) - ) - ) + c.call(f1mPrefix+"_copy", x0, r0), + c.call(f1mPrefix+"_copy", x1, r1) ); - f.addCode(c.setLocal("bits", c.i32_const(0))); + } - f.addCode(c.block(c.loop( - c.br_if( - 1, - c.i32_eqz(c.getLocal("aux")) - ), + function buildZero() { + const f = module.addFunction(prefix+"_zero"); + f.addParam("x", "i32"); - c.setLocal( - "aux", - c.i32_shr_u( - c.getLocal("aux"), - c.i32_const(1) - ) - ), + const c = f.getCodeBuilder(); - c.setLocal( - "bits", - c.i32_add( - c.getLocal("bits"), - c.i32_const(1) - ) - ), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - c.br(0) - ))); + f.addCode( + c.call(f1mPrefix+"_zero", x0), + c.call(f1mPrefix+"_zero", x1) + ); + } - f.addCode(c.if( - c.i32_ne( - c.getLocal("n"), - c.i32_shl( - c.i32_const(1), - c.getLocal("bits") - ) - ), - c.unreachable() - )); + function buildOne() { + const f = module.addFunction(prefix+"_one"); + f.addParam("x", "i32"); - f.addCode(c.if( - c.i32_gt_u( - c.getLocal("bits"), - c.i32_const(maxBits) - ), - c.unreachable() - )); + const c = f.getCodeBuilder(); - f.addCode(c.getLocal("bits")); - } + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - function buildFFT() { - const f = module.addFunction(prefix+"_fft"); - f.addParam("px", "i32"); - f.addParam("n", "i32"); + f.addCode( + c.call(f1mPrefix+"_one", x0), + c.call(f1mPrefix+"_zero", x1) + ); + } - f.addLocal("bits", "i32"); + function buildEq() { + const f = module.addFunction(prefix+"_eq"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.setReturnType("i32"); const c = f.getCodeBuilder(); - const One = c.i32_const(module.alloc(n8f)); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const y0 = c.getLocal("y"); + const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8)); f.addCode( - c.setLocal( - "bits", - c.call( - prefix + "__log2", - c.getLocal("n") - ) - ), - c.call(fPrefix + "_one", One), - c.call( - prefix+"_rawfft", - c.getLocal("px"), - c.getLocal("bits"), - c.i32_const(0), - One + c.i32_and( + c.call(f1mPrefix+"_eq", x0, y0), + c.call(f1mPrefix+"_eq", x1, y1) ) ); - } - function buildIFFT() { - const f = module.addFunction(prefix+"_ifft"); - f.addParam("px", "i32"); - f.addParam("n", "i32"); - f.addLocal("bits", "i32"); - f.addLocal("pInv2", "i32"); + function buildIsZero() { + const f = module.addFunction(prefix+"_isZero"); + f.addParam("x", "i32"); + f.setReturnType("i32"); const c = f.getCodeBuilder(); - f.addCode( - c.setLocal( - "bits", - c.call( - prefix + "__log2", - c.getLocal("n") - ) - ), - c.setLocal( - "pInv2", - c.i32_add( - c.i32_const(INV2), - c.i32_mul( - c.getLocal("bits"), - c.i32_const(n8f) - ) - ) - ), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - c.call( - prefix+"_rawfft", - c.getLocal("px"), - c.getLocal("bits"), - c.i32_const(1), - c.getLocal("pInv2") - ), + f.addCode( + c.i32_and( + c.call(f1mPrefix+"_isZero", x0), + c.call(f1mPrefix+"_isZero", x1) + ) ); } - function buildRawFFT() { - const f = module.addFunction(prefix+"_rawfft"); - f.addParam("px", "i32"); - f.addParam("bits", "i32"); // 2 power - f.addParam("reverse", "i32"); - f.addParam("mulFactor", "i32"); - - f.addLocal("s", "i32"); - f.addLocal("k", "i32"); - f.addLocal("j", "i32"); - f.addLocal("m", "i32"); - f.addLocal("mdiv2", "i32"); - f.addLocal("n", "i32"); - f.addLocal("pwm", "i32"); - f.addLocal("idx1", "i32"); - f.addLocal("idx2", "i32"); + function buildInverse() { + const f = module.addFunction(prefix+"_inverse"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); - const W = c.i32_const(module.alloc(n8f)); - const T = c.i32_const(module.alloc(n8g)); - const U = c.i32_const(module.alloc(n8g)); - - f.addCode( - c.call(prefix + "__reversePermutation", c.getLocal("px"), c.getLocal("bits")), - c.setLocal("n", c.i32_shl(c.i32_const(1), c.getLocal("bits"))), - c.setLocal("s", c.i32_const(1)), - c.block(c.loop( - c.br_if( - 1, - c.i32_gt_u( - c.getLocal("s"), - c.getLocal("bits") - ) - ), - c.setLocal("m", c.i32_shl(c.i32_const(1), c.getLocal("s"))), - c.setLocal("pwm", - c.i32_add( - c.i32_const(ROOTs), - c.i32_mul( - c.getLocal("s"), - c.i32_const(n8f) - ) - ) - ), - c.setLocal("k", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_ge_u( - c.getLocal("k"), - c.getLocal("n") - ) - ), - - c.call(fPrefix + "_one", W), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); - c.setLocal("mdiv2", c.i32_shr_u(c.getLocal("m"), c.i32_const(1)) ), - c.setLocal("j", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_ge_u( - c.getLocal("j"), - c.getLocal("mdiv2") - ) - ), + const t0 = c.i32_const(module.alloc(f1n8)); + const t1 = c.i32_const(module.alloc(f1n8)); + const t2 = c.i32_const(module.alloc(f1n8)); + const t3 = c.i32_const(module.alloc(f1n8)); - c.setLocal( - "idx1", - c.i32_add( - c.getLocal("px"), - c.i32_mul( - c.i32_add( - c.getLocal("k"), - c.getLocal("j") - ), - c.i32_const(n8g) - ) - ) - ), + f.addCode( + c.call(f1mPrefix+"_square", x0, t0), + c.call(f1mPrefix+"_square", x1, t1), + // c.call(f1mPrefix+"_mul", t1, c.i32_const(pNonResidue), t2), + c.call(mulNonResidueFn, t1, t2), - c.setLocal( - "idx2", - c.i32_add( - c.getLocal("idx1"), - c.i32_mul( - c.getLocal("mdiv2"), - c.i32_const(n8g) - ) - ) - ), + c.call(f1mPrefix+"_sub", t0, t2, t2), + c.call(f1mPrefix+"_inverse", t2, t3), - c.call( - opGtimesF, - c.getLocal("idx2"), - W, - T - ), + c.call(f1mPrefix+"_mul", x0, t3, r0), + c.call(f1mPrefix+"_mul", x1, t3, r1), + c.call(f1mPrefix+"_neg", r1, r1), + ); + } - c.call( - gPrefix + "_copy", - c.getLocal("idx1"), - U - ), - c.call( - gPrefix + "_add", - U, - T, - c.getLocal("idx1"), - ), + function buildSign() { + const f = module.addFunction(prefix+"_sign"); + f.addParam("x", "i32"); + f.addLocal("s", "i32"); + f.setReturnType("i32"); - c.call( - gPrefix + "_sub", - U, - T, - c.getLocal("idx2"), - ), + const c = f.getCodeBuilder(); - c.call( - fPrefix + "_mul", - W, - c.getLocal("pwm"), - W, - ), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); - c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), - c.br(0) - )), + f.addCode( + c.setLocal("s" , c.call( f1mPrefix + "_sign", x1)), + c.if( + c.getLocal("s"), + c.ret(c.getLocal("s")) + ), + c.ret(c.call( f1mPrefix + "_sign", x0)) + ); + } - c.setLocal("k", c.i32_add(c.getLocal("k"), c.getLocal("m"))), - c.br(0) - )), + function buildIsOne() { + const f = module.addFunction(prefix+"_isOne"); + f.addParam("x", "i32"); + f.setReturnType("i32"); - c.setLocal("s", c.i32_add(c.getLocal("s"), c.i32_const(1))), - c.br(0) - )), - c.call( - prefix + "__fftFinal", - c.getLocal("px"), - c.getLocal("bits"), - c.getLocal("reverse"), - c.getLocal("mulFactor") - ) + const c = f.getCodeBuilder(); + + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + + f.addCode( + c.ret(c.i32_and( + c.call(f1mPrefix + "_isOne", x0), + c.call(f1mPrefix + "_isZero", x1), + )) ); } - function buildFinalInverse() { - const f = module.addFunction(prefix+"__fftFinal"); - f.addParam("px", "i32"); - f.addParam("bits", "i32"); - f.addParam("reverse", "i32"); - f.addParam("mulFactor", "i32"); - f.addLocal("n", "i32"); - f.addLocal("ndiv2", "i32"); - f.addLocal("pInv2", "i32"); - f.addLocal("i", "i32"); - f.addLocal("mask", "i32"); - f.addLocal("idx1", "i32"); - f.addLocal("idx2", "i32"); + // Check here: https://eprint.iacr.org/2012/685.pdf + // Alg 9adj + function buildSqrt() { + + const f = module.addFunction(prefix+"_sqrt"); + f.addParam("a", "i32"); + f.addParam("pr", "i32"); const c = f.getCodeBuilder(); - const T = c.i32_const(module.alloc(n8g)); + // BigInt can't take `undefined` so we use `|| 0` + const e34 = c.i32_const(module.alloc(utils$3.bigInt2BytesLE((BigInt(q || 0) - 3n) / 4n, f1n8 ))); + // BigInt can't take `undefined` so we use `|| 0` + const e12 = c.i32_const(module.alloc(utils$3.bigInt2BytesLE((BigInt(q || 0) - 1n) / 2n, f1n8 ))); + + const a = c.getLocal("a"); + const a1 = c.i32_const(module.alloc(f1n8*2)); + const alpha = c.i32_const(module.alloc(f1n8*2)); + const a0 = c.i32_const(module.alloc(f1n8*2)); + const pn1 = module.alloc(f1n8*2); + const n1 = c.i32_const(pn1); + const n1a = c.i32_const(pn1); + const n1b = c.i32_const(pn1+f1n8); + const x0 = c.i32_const(module.alloc(f1n8*2)); + const b = c.i32_const(module.alloc(f1n8*2)); f.addCode( - c.if( - c.i32_and( - c.i32_eqz(c.getLocal("reverse")), - c.call(fPrefix + "_isOne", c.getLocal("mulFactor")) - ), - c.ret([]) - ), - c.setLocal("n", c.i32_shl( c.i32_const(1), c.getLocal("bits"))), - c.setLocal("mask", c.i32_sub( c.getLocal("n") , c.i32_const(1))), - c.setLocal("i", c.i32_const(1)), - c.setLocal( - "ndiv2", - c.i32_shr_u( - c.getLocal("n"), - c.i32_const(1) - ) - ), - c.block(c.loop( - c.br_if( - 1, - c.i32_ge_u( - c.getLocal("i"), - c.getLocal("ndiv2") - ) - ), + c.call(prefix + "_one", n1), + c.call(prefix + "_neg", n1, n1), - c.setLocal("idx1", - c.i32_add( - c.getLocal("px"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), + // const a1 = F.pow(a, F.sqrt_e34); + c.call(prefix + "_exp", a, e34, c.i32_const(f1n8), a1), - c.setLocal("idx2", - c.i32_add( - c.getLocal("px"), - c.i32_mul( - c.i32_sub( - c.getLocal("n"), - c.getLocal("i") - ), - c.i32_const(n8g) - ) - ) - ), + // const a1 = F.pow(a, F.sqrt_e34); + c.call(prefix + "_square", a1, alpha), + c.call(prefix + "_mul", a, alpha, alpha), - c.if( - c.getLocal("reverse"), - c.if( - c.call(fPrefix + "_isOne", c.getLocal("mulFactor")), - [ - ...c.call(gPrefix + "_copy", c.getLocal("idx1"), T), - ...c.call(gPrefix + "_copy", c.getLocal("idx2") , c.getLocal("idx1") ), - ...c.call(gPrefix + "_copy", T , c.getLocal("idx2")), - ], - [ - ...c.call(gPrefix + "_copy", c.getLocal("idx1"), T), - ...c.call(opGtimesF , c.getLocal("idx2") , c.getLocal("mulFactor"), c.getLocal("idx1") ), - ...c.call(opGtimesF , T , c.getLocal("mulFactor"), c.getLocal("idx2")), - ] - ), - c.if( - c.call(fPrefix + "_isOne", c.getLocal("mulFactor")), - [ - // Do nothing (It should not be here) - ], - [ - ...c.call(opGtimesF , c.getLocal("idx1") , c.getLocal("mulFactor"), c.getLocal("idx1") ), - ...c.call(opGtimesF , c.getLocal("idx2") , c.getLocal("mulFactor"), c.getLocal("idx2")), - ] - ) - ), - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + // const a0 = F.mul(F.frobenius(1, alfa), alfa); + c.call(prefix + "_conjugate", alpha, a0), + c.call(prefix + "_mul", a0, alpha, a0), - c.br(0) - )), + // if (F.eq(a0, F.negone)) return null; + c.if(c.call(prefix + "_eq",a0,n1), c.unreachable() ), + + // const x0 = F.mul(a1, a); + c.call(prefix + "_mul", a1, a, x0), + // if (F.eq(alfa, F.negone)) { c.if( - c.call(fPrefix + "_isOne", c.getLocal("mulFactor")), + c.call(prefix + "_eq", alpha, n1), [ - // Do nothing (It should not be here) + // x = F.mul(x0, [F.F.zero, F.F.one]); + ...c.call(f1mPrefix + "_zero", n1a), + ...c.call(f1mPrefix + "_one", n1b), + ...c.call(prefix + "_mul", n1, x0, c.getLocal("pr")), ], [ - ...c.call(opGtimesF, c.getLocal("px") , c.getLocal("mulFactor"), c.getLocal("px")), - ...c.setLocal("idx2", - c.i32_add( - c.getLocal("px"), - c.i32_mul( - c.getLocal("ndiv2"), - c.i32_const(n8g) - ) - ) - ), - ...c.call(opGtimesF, c.getLocal("idx2"),c.getLocal("mulFactor"), c.getLocal("idx2")) + // const b = F.pow(F.add(F.one, alfa), F.sqrt_e12); + ...c.call(prefix + "_one", b), + ...c.call(prefix + "_add", b, alpha, b), + ...c.call(prefix + "_exp", b, e12, c.i32_const(f1n8), b), + + // x = F.mul(b, x0); + ...c.call(prefix + "_mul", b, x0, c.getLocal("pr")), ] ) ); + + } + + + function buildIsSquare() { + + const f = module.addFunction(prefix+"_isSquare"); + f.addParam("a", "i32"); + f.setReturnType("i32"); + + const c = f.getCodeBuilder(); + + // BigInt can't take `undefined` so we use `|| 0` + const e34 = c.i32_const(module.alloc(utils$3.bigInt2BytesLE((BigInt(q || 0) - 3n) / 4n, f1n8 ))); + + const a = c.getLocal("a"); + const a1 = c.i32_const(module.alloc(f1n8*2)); + const alpha = c.i32_const(module.alloc(f1n8*2)); + const a0 = c.i32_const(module.alloc(f1n8*2)); + const pn1 = module.alloc(f1n8*2); + const n1 = c.i32_const(pn1); + + f.addCode( + + c.call(prefix + "_one", n1), + c.call(prefix + "_neg", n1, n1), + + // const a1 = F.pow(a, F.sqrt_e34); + c.call(prefix + "_exp", a, e34, c.i32_const(f1n8), a1), + + // const a1 = F.pow(a, F.sqrt_e34); + c.call(prefix + "_square", a1, alpha), + c.call(prefix + "_mul", a, alpha, alpha), + + // const a0 = F.mul(F.frobenius(1, alfa), alfa); + c.call(prefix + "_conjugate", alpha, a0), + c.call(prefix + "_mul", a0, alpha, a0), + + // if (F.eq(a0, F.negone)) return null; + c.if( + c.call( + prefix + "_eq", + a0, + n1 + ), + c.ret(c.i32_const(0)) + ), + c.ret(c.i32_const(1)) + ); + } - function buildReversePermutation() { - const f = module.addFunction(prefix+"__reversePermutation"); - f.addParam("px", "i32"); - f.addParam("bits", "i32"); - f.addLocal("n", "i32"); - f.addLocal("i", "i32"); - f.addLocal("ri", "i32"); - f.addLocal("idx1", "i32"); - f.addLocal("idx2", "i32"); - const c = f.getCodeBuilder(); + buildIsZero(); + buildIsOne(); + buildZero(); + buildOne(); + buildCopy(); + buildMul(); + buildMul1(); + buildSquare(); + buildAdd(); + buildSub(); + buildNeg(); + buildConjugate(); + buildToMontgomery(); + buildFromMontgomery(); + buildEq(); + buildInverse(); + buildTimesScalar(); + buildSign(); + buildIsNegative(); - const T = c.i32_const(module.alloc(n8g)); + module.exportFunction(prefix + "_isZero"); + module.exportFunction(prefix + "_isOne"); + module.exportFunction(prefix + "_zero"); + module.exportFunction(prefix + "_one"); + module.exportFunction(prefix + "_copy"); + module.exportFunction(prefix + "_mul"); + module.exportFunction(prefix + "_mul1"); + module.exportFunction(prefix + "_square"); + module.exportFunction(prefix + "_add"); + module.exportFunction(prefix + "_sub"); + module.exportFunction(prefix + "_neg"); + module.exportFunction(prefix + "_sign"); + module.exportFunction(prefix + "_conjugate"); + module.exportFunction(prefix + "_fromMontgomery"); + module.exportFunction(prefix + "_toMontgomery"); + module.exportFunction(prefix + "_eq"); + module.exportFunction(prefix + "_inverse"); + buildBatchInverse$1(module, prefix); + buildExp$1( + module, + prefix + "_exp", + f1n8*2, + prefix + "_mul", + prefix + "_square", + prefix + "_copy", + prefix + "_one", + ); + buildSqrt(); + buildIsSquare(); - f.addCode( - c.setLocal("n", c.i32_shl( c.i32_const(1), c.getLocal("bits"))), - c.setLocal("i", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("i"), - c.getLocal("n") - ) - ), + module.exportFunction(prefix + "_exp"); + module.exportFunction(prefix + "_timesScalar"); + module.exportFunction(prefix + "_batchInverse"); + module.exportFunction(prefix + "_sqrt"); + module.exportFunction(prefix + "_isSquare"); + module.exportFunction(prefix + "_isNegative"); - c.setLocal("idx1", - c.i32_add( - c.getLocal("px"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), - c.setLocal("ri", c.call(prefix + "__rev", c.getLocal("i"), c.getLocal("bits"))), + return prefix; +}; - c.setLocal("idx2", - c.i32_add( - c.getLocal("px"), - c.i32_mul( - c.getLocal("ri"), - c.i32_const(n8g) - ) - ) - ), +/* + Copyright 2019 0KIMS association. - c.if( - c.i32_lt_u( - c.getLocal("i"), - c.getLocal("ri") - ), - [ - ...c.call(gPrefix + "_copy", c.getLocal("idx1"), T), - ...c.call(gPrefix + "_copy", c.getLocal("idx2") , c.getLocal("idx1")), - ...c.call(gPrefix + "_copy", T , c.getLocal("idx2")) - ] - ), + This file is part of wasmsnark (Web Assembly zkSnark Prover). - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + wasmsnark 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. - c.br(0) - )) - ); - } + wasmsnark 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. - function buildRev() { - const f = module.addFunction(prefix+"__rev"); + You should have received a copy of the GNU General Public License + along with wasmsnark. If not, see . +*/ + +const buildExp = build_timesscalar; +const buildBatchInverse = build_batchinverse; + +var build_f3m = function buildF3m(module, mulNonResidueFn, prefix, f1mPrefix) { + + if (module.modules[prefix]) return prefix; // already builded + + const f1n8 = module.modules[f1mPrefix].n64*8; + module.modules[prefix] = { + n64: module.modules[f1mPrefix].n64*3 + }; + + function buildAdd() { + const f = module.addFunction(prefix+"_add"); f.addParam("x", "i32"); - f.addParam("bits", "i32"); - f.setReturnType("i32"); + f.addParam("y", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + const y0 = c.getLocal("y"); + const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8)); + const y2 = c.i32_add(c.getLocal("y"), c.i32_const(2*f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8)); + f.addCode( - c.i32_rotl( - c.i32_add( - c.i32_add( - c.i32_shl( - c.i32_load8_u( - c.i32_and( - c.getLocal("x"), - c.i32_const(0xFF) - ), - REVTABLE, - 0 - ), - c.i32_const(24) - ), - c.i32_shl( - c.i32_load8_u( - c.i32_and( - c.i32_shr_u( - c.getLocal("x"), - c.i32_const(8) - ), - c.i32_const(0xFF) - ), - REVTABLE, - 0 - ), - c.i32_const(16) - ), - ), - c.i32_add( - c.i32_shl( - c.i32_load8_u( - c.i32_and( - c.i32_shr_u( - c.getLocal("x"), - c.i32_const(16) - ), - c.i32_const(0xFF) - ), - REVTABLE, - 0 - ), - c.i32_const(8) - ), - c.i32_load8_u( - c.i32_and( - c.i32_shr_u( - c.getLocal("x"), - c.i32_const(24) - ), - c.i32_const(0xFF) - ), - REVTABLE, - 0 - ), - ) - ), - c.getLocal("bits") - ) + c.call(f1mPrefix+"_add", x0, y0, r0), + c.call(f1mPrefix+"_add", x1, y1, r1), + c.call(f1mPrefix+"_add", x2, y2, r2), ); } - - function buildFFTJoin() { - const f = module.addFunction(prefix+"_fftJoin"); - f.addParam("pBuff1", "i32"); - f.addParam("pBuff2", "i32"); - f.addParam("n", "i32"); - f.addParam("first", "i32"); - f.addParam("inc", "i32"); - f.addLocal("idx1", "i32"); - f.addLocal("idx2", "i32"); - f.addLocal("i", "i32"); + function buildTimesScalar() { + const f = module.addFunction(prefix+"_timesScalar"); + f.addParam("x", "i32"); + f.addParam("scalar", "i32"); + f.addParam("scalarLen", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); - const W = c.i32_const(module.alloc(n8f)); - const T = c.i32_const(module.alloc(n8g)); - const U = c.i32_const(module.alloc(n8g)); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8)); f.addCode( - c.call( fPrefix + "_copy", c.getLocal("first"), W), - c.setLocal("i", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("i"), - c.getLocal("n") - ) - ), + c.call(f1mPrefix+"_timesScalar", x0, c.getLocal("scalar"), c.getLocal("scalarLen"), r0), + c.call(f1mPrefix+"_timesScalar", x1, c.getLocal("scalar"), c.getLocal("scalarLen"), r1), + c.call(f1mPrefix+"_timesScalar", x2, c.getLocal("scalar"), c.getLocal("scalarLen"), r2), + ); + } - c.setLocal( - "idx1", - c.i32_add( - c.getLocal("pBuff1"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), - c.setLocal( - "idx2", - c.i32_add( - c.getLocal("pBuff2"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), + function buildSub() { + const f = module.addFunction(prefix+"_sub"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.addParam("r", "i32"); - c.call( - opGtimesF, - c.getLocal("idx2"), - W, - T - ), + const c = f.getCodeBuilder(); - c.call( - gPrefix + "_copy", - c.getLocal("idx1"), - U - ), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + const y0 = c.getLocal("y"); + const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8)); + const y2 = c.i32_add(c.getLocal("y"), c.i32_const(2*f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8)); - c.call( - gPrefix + "_add", - U, - T, - c.getLocal("idx1"), - ), + f.addCode( + c.call(f1mPrefix+"_sub", x0, y0, r0), + c.call(f1mPrefix+"_sub", x1, y1, r1), + c.call(f1mPrefix+"_sub", x2, y2, r2), + ); + } - c.call( - gPrefix + "_sub", - U, - T, - c.getLocal("idx2"), - ), + function buildNeg() { + const f = module.addFunction(prefix+"_neg"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); - c.call( - fPrefix + "_mul", - W, - c.getLocal("inc"), - W, - ), + const c = f.getCodeBuilder(); - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8)); + + f.addCode( + c.call(f1mPrefix+"_neg", x0, r0), + c.call(f1mPrefix+"_neg", x1, r1), + c.call(f1mPrefix+"_neg", x2, r2), ); } - - function buildFFTJoinExt() { - const f = module.addFunction(prefix+"_fftJoinExt"); - f.addParam("pBuff1", "i32"); - f.addParam("pBuff2", "i32"); - f.addParam("n", "i32"); - f.addParam("first", "i32"); - f.addParam("inc", "i32"); - f.addParam("totalBits", "i32"); - f.addLocal("idx1", "i32"); - f.addLocal("idx2", "i32"); - f.addLocal("i", "i32"); - f.addLocal("pShiftToM", "i32"); + function buildIsNegative() { + const f = module.addFunction(prefix+"_isNegative"); + f.addParam("x", "i32"); + f.setReturnType("i32"); const c = f.getCodeBuilder(); - const W = c.i32_const(module.alloc(n8f)); - const U = c.i32_const(module.alloc(n8g)); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); f.addCode( - - c.setLocal("pShiftToM", - c.i32_add( - c.i32_const(SHIFT_TO_M), - c.i32_mul( - c.getLocal("totalBits"), - c.i32_const(n8f) - ) + c.if( + c.call(f1mPrefix+"_isZero", x2), + c.if( + c.call(f1mPrefix+"_isZero", x1), + c.ret(c.call(f1mPrefix+"_isNegative", x0)), + c.ret(c.call(f1mPrefix+"_isNegative", x1)) ) ), + c.ret(c.call(f1mPrefix+"_isNegative", x2)) + ); + } - c.call( fPrefix + "_copy", c.getLocal("first"), W), - c.setLocal("i", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("i"), - c.getLocal("n") - ) - ), + function buildMul() { + const f = module.addFunction(prefix+"_mul"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.addParam("r", "i32"); - c.setLocal( - "idx1", - c.i32_add( - c.getLocal("pBuff1"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), + const cd = f.getCodeBuilder(); - c.setLocal( - "idx2", - c.i32_add( - c.getLocal("pBuff2"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), + const a = cd.getLocal("x"); + const b = cd.i32_add(cd.getLocal("x"), cd.i32_const(f1n8)); + const c = cd.i32_add(cd.getLocal("x"), cd.i32_const(2*f1n8)); + const A = cd.getLocal("y"); + const B = cd.i32_add(cd.getLocal("y"), cd.i32_const(f1n8)); + const C = cd.i32_add(cd.getLocal("y"), cd.i32_const(2*f1n8)); + const r0 = cd.getLocal("r"); + const r1 = cd.i32_add(cd.getLocal("r"), cd.i32_const(f1n8)); + const r2 = cd.i32_add(cd.getLocal("r"), cd.i32_const(2*f1n8)); - c.call( - gPrefix + "_add", - c.getLocal("idx1"), - c.getLocal("idx2"), - U - ), + const aA = cd.i32_const(module.alloc(f1n8)); + const bB = cd.i32_const(module.alloc(f1n8)); + const cC = cd.i32_const(module.alloc(f1n8)); + const a_b = cd.i32_const(module.alloc(f1n8)); + const A_B = cd.i32_const(module.alloc(f1n8)); + const a_c = cd.i32_const(module.alloc(f1n8)); + const A_C = cd.i32_const(module.alloc(f1n8)); + const b_c = cd.i32_const(module.alloc(f1n8)); + const B_C = cd.i32_const(module.alloc(f1n8)); + const aA_bB = cd.i32_const(module.alloc(f1n8)); + const aA_cC = cd.i32_const(module.alloc(f1n8)); + const bB_cC = cd.i32_const(module.alloc(f1n8)); + const AUX = cd.i32_const(module.alloc(f1n8)); - c.call( - opGtimesF, - c.getLocal("idx2"), - c.getLocal("pShiftToM"), - c.getLocal("idx2") - ), - c.call( - gPrefix + "_add", - c.getLocal("idx1"), - c.getLocal("idx2"), - c.getLocal("idx2") - ), + f.addCode( + cd.call(f1mPrefix + "_mul", a, A, aA), + cd.call(f1mPrefix + "_mul", b, B, bB), + cd.call(f1mPrefix + "_mul", c, C, cC), - c.call( - opGtimesF, - c.getLocal("idx2"), - W, - c.getLocal("idx2"), - ), + cd.call(f1mPrefix + "_add", a, b, a_b), + cd.call(f1mPrefix + "_add", A, B, A_B), + cd.call(f1mPrefix + "_add", a, c, a_c), + cd.call(f1mPrefix + "_add", A, C, A_C), + cd.call(f1mPrefix + "_add", b, c, b_c), + cd.call(f1mPrefix + "_add", B, C, B_C), - c.call( - gPrefix + "_copy", - U, - c.getLocal("idx1") - ), + cd.call(f1mPrefix + "_add", aA, bB, aA_bB), + cd.call(f1mPrefix + "_add", aA, cC, aA_cC), + cd.call(f1mPrefix + "_add", bB, cC, bB_cC), - c.call( - fPrefix + "_mul", - W, - c.getLocal("inc"), - W - ), + cd.call(f1mPrefix + "_mul", b_c, B_C, r0), + cd.call(f1mPrefix + "_sub", r0, bB_cC, r0), + cd.call(mulNonResidueFn, r0, r0), + cd.call(f1mPrefix + "_add", aA, r0, r0), - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) + cd.call(f1mPrefix + "_mul", a_b, A_B, r1), + cd.call(f1mPrefix + "_sub", r1, aA_bB, r1), + cd.call(mulNonResidueFn, cC, AUX), + cd.call(f1mPrefix + "_add", r1, AUX, r1), + + cd.call(f1mPrefix + "_mul", a_c, A_C, r2), + cd.call(f1mPrefix + "_sub", r2, aA_cC, r2), + cd.call(f1mPrefix + "_add", r2, bB, r2), ); + } - function buildFFTJoinExtInv() { - const f = module.addFunction(prefix+"_fftJoinExtInv"); - f.addParam("pBuff1", "i32"); - f.addParam("pBuff2", "i32"); - f.addParam("n", "i32"); - f.addParam("first", "i32"); - f.addParam("inc", "i32"); - f.addParam("totalBits", "i32"); - f.addLocal("idx1", "i32"); - f.addLocal("idx2", "i32"); - f.addLocal("i", "i32"); - f.addLocal("pShiftToM", "i32"); - f.addLocal("pSConst", "i32"); + function buildSquare() { + const f = module.addFunction(prefix+"_square"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); - const W = c.i32_const(module.alloc(n8f)); - const U = c.i32_const(module.alloc(n8g)); + const A = c.getLocal("x"); + const B = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const C = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8)); + + const s0 = c.i32_const(module.alloc(f1n8)); + const ab = c.i32_const(module.alloc(f1n8)); + const s1 = c.i32_const(module.alloc(f1n8)); + const s2 = c.i32_const(module.alloc(f1n8)); + const bc = c.i32_const(module.alloc(f1n8)); + const s3 = c.i32_const(module.alloc(f1n8)); + const s4 = c.i32_const(module.alloc(f1n8)); + f.addCode( - c.setLocal("pShiftToM", - c.i32_add( - c.i32_const(SHIFT_TO_M), - c.i32_mul( - c.getLocal("totalBits"), - c.i32_const(n8f) - ) - ) - ), - c.setLocal("pSConst", - c.i32_add( - c.i32_const(SCONST), - c.i32_mul( - c.getLocal("totalBits"), - c.i32_const(n8f) - ) - ) - ), + c.call(f1mPrefix + "_square", A, s0), + c.call(f1mPrefix + "_mul", A, B, ab), + c.call(f1mPrefix + "_add", ab, ab, s1), + c.call(f1mPrefix + "_sub", A, B, s2), + c.call(f1mPrefix + "_add", s2, C, s2), + c.call(f1mPrefix + "_square", s2, s2), - c.call( fPrefix + "_copy", c.getLocal("first"), W), - c.setLocal("i", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("i"), - c.getLocal("n") - ) - ), + c.call(f1mPrefix + "_mul", B, C, bc), + c.call(f1mPrefix + "_add", bc, bc, s3), - c.setLocal( - "idx1", - c.i32_add( - c.getLocal("pBuff1"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), + c.call(f1mPrefix + "_square", C, s4), - c.setLocal( - "idx2", - c.i32_add( - c.getLocal("pBuff2"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), + c.call(mulNonResidueFn, s3, r0), + c.call(f1mPrefix + "_add", s0, r0, r0), - c.call( - opGtimesF, - c.getLocal("idx2"), - W, - U - ), + c.call(mulNonResidueFn, s4, r1), + c.call(f1mPrefix + "_add", s1, r1, r1), - c.call( - gPrefix + "_sub", - c.getLocal("idx1"), - U, - c.getLocal("idx2"), - ), + c.call(f1mPrefix + "_add", s0, s4, r2), + c.call(f1mPrefix + "_sub", s3, r2, r2), + c.call(f1mPrefix + "_add", s2, r2, r2), + c.call(f1mPrefix + "_add", s1, r2, r2), + ); - c.call( - opGtimesF, - c.getLocal("idx2"), - c.getLocal("pSConst"), - c.getLocal("idx2") - ), + } - c.call( - opGtimesF, - c.getLocal("idx1"), - c.getLocal("pShiftToM"), - c.getLocal("idx1") - ), - c.call( - gPrefix + "_sub", - U, - c.getLocal("idx1"), - c.getLocal("idx1") - ), + function buildToMontgomery() { + const f = module.addFunction(prefix+"_toMontgomery"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); - c.call( - opGtimesF, - c.getLocal("idx1"), - c.getLocal("pSConst"), - c.getLocal("idx1") - ), + const c = f.getCodeBuilder(); - c.call( - fPrefix + "_mul", - W, - c.getLocal("inc"), - W - ), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8)); - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) + f.addCode( + c.call(f1mPrefix+"_toMontgomery", x0, r0), + c.call(f1mPrefix+"_toMontgomery", x1, r1), + c.call(f1mPrefix+"_toMontgomery", x2, r2) ); } + function buildFromMontgomery() { + const f = module.addFunction(prefix+"_fromMontgomery"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + const c = f.getCodeBuilder(); - function buildPrepareLagrangeEvaluation() { - const f = module.addFunction(prefix+"_prepareLagrangeEvaluation"); - f.addParam("pBuff1", "i32"); - f.addParam("pBuff2", "i32"); - f.addParam("n", "i32"); - f.addParam("first", "i32"); - f.addParam("inc", "i32"); - f.addParam("totalBits", "i32"); - f.addLocal("idx1", "i32"); - f.addLocal("idx2", "i32"); - f.addLocal("i", "i32"); - f.addLocal("pShiftToM", "i32"); - f.addLocal("pSConst", "i32"); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8)); + + f.addCode( + c.call(f1mPrefix+"_fromMontgomery", x0, r0), + c.call(f1mPrefix+"_fromMontgomery", x1, r1), + c.call(f1mPrefix+"_fromMontgomery", x2, r2) + ); + } + + function buildCopy() { + const f = module.addFunction(prefix+"_copy"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); const c = f.getCodeBuilder(); - const W = c.i32_const(module.alloc(n8f)); - const U = c.i32_const(module.alloc(n8g)); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8)); f.addCode( + c.call(f1mPrefix+"_copy", x0, r0), + c.call(f1mPrefix+"_copy", x1, r1), + c.call(f1mPrefix+"_copy", x2, r2), + ); + } - c.setLocal("pShiftToM", - c.i32_add( - c.i32_const(SHIFT_TO_M), - c.i32_mul( - c.getLocal("totalBits"), - c.i32_const(n8f) - ) - ) - ), - c.setLocal("pSConst", - c.i32_add( - c.i32_const(SCONST), - c.i32_mul( - c.getLocal("totalBits"), - c.i32_const(n8f) - ) - ) - ), + function buildZero() { + const f = module.addFunction(prefix+"_zero"); + f.addParam("x", "i32"); + const c = f.getCodeBuilder(); - c.call( fPrefix + "_copy", c.getLocal("first"), W), - c.setLocal("i", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("i"), - c.getLocal("n") - ) - ), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); - c.setLocal( - "idx1", - c.i32_add( - c.getLocal("pBuff1"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), + f.addCode( + c.call(f1mPrefix+"_zero", x0), + c.call(f1mPrefix+"_zero", x1), + c.call(f1mPrefix+"_zero", x2), + ); + } - c.setLocal( - "idx2", - c.i32_add( - c.getLocal("pBuff2"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), + function buildOne() { + const f = module.addFunction(prefix+"_one"); + f.addParam("x", "i32"); + const c = f.getCodeBuilder(); - c.call( - opGtimesF, - c.getLocal("idx1"), - c.getLocal("pShiftToM"), - U - ), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); - c.call( - gPrefix + "_sub", - c.getLocal("idx2"), - U, - U - ), + f.addCode( + c.call(f1mPrefix+"_one", x0), + c.call(f1mPrefix+"_zero", x1), + c.call(f1mPrefix+"_zero", x2), + ); + } - c.call( - gPrefix + "_sub", - c.getLocal("idx1"), - c.getLocal("idx2"), - c.getLocal("idx2"), - ), + function buildEq() { + const f = module.addFunction(prefix+"_eq"); + f.addParam("x", "i32"); + f.addParam("y", "i32"); + f.setReturnType("i32"); - c.call( - opGtimesF, - U, - c.getLocal("pSConst"), - c.getLocal("idx1"), - ), + const c = f.getCodeBuilder(); - c.call( - opGtimesF, - c.getLocal("idx2"), - W, - c.getLocal("idx2"), - ), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + const y0 = c.getLocal("y"); + const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8)); + const y2 = c.i32_add(c.getLocal("y"), c.i32_const(2*f1n8)); - c.call( - fPrefix + "_mul", - W, - c.getLocal("inc"), - W + f.addCode( + c.i32_and( + c.i32_and( + c.call(f1mPrefix+"_eq", x0, y0), + c.call(f1mPrefix+"_eq", x1, y1), ), - - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) + c.call(f1mPrefix+"_eq", x2, y2) + ) ); } - function buildFFTMix() { - const f = module.addFunction(prefix+"_fftMix"); - f.addParam("pBuff", "i32"); - f.addParam("n", "i32"); - f.addParam("exp", "i32"); - f.addLocal("nGroups", "i32"); - f.addLocal("nPerGroup", "i32"); - f.addLocal("nPerGroupDiv2", "i32"); - f.addLocal("pairOffset", "i32"); - f.addLocal("idx1", "i32"); - f.addLocal("idx2", "i32"); - f.addLocal("i", "i32"); - f.addLocal("j", "i32"); - f.addLocal("pwm", "i32"); + function buildIsZero() { + const f = module.addFunction(prefix+"_isZero"); + f.addParam("x", "i32"); + f.setReturnType("i32"); const c = f.getCodeBuilder(); - const W = c.i32_const(module.alloc(n8f)); - const T = c.i32_const(module.alloc(n8g)); - const U = c.i32_const(module.alloc(n8g)); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); f.addCode( - c.setLocal("nPerGroup", c.i32_shl(c.i32_const(1), c.getLocal("exp"))), - c.setLocal("nPerGroupDiv2", c.i32_shr_u(c.getLocal("nPerGroup"), c.i32_const(1))), - c.setLocal("nGroups", c.i32_shr_u(c.getLocal("n"), c.getLocal("exp"))), - c.setLocal("pairOffset", c.i32_mul(c.getLocal("nPerGroupDiv2"), c.i32_const(n8g))), - c.setLocal("pwm", - c.i32_add( - c.i32_const(ROOTs), - c.i32_mul( - c.getLocal("exp"), - c.i32_const(n8f) - ) - ) - ), - c.setLocal("i", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("i"), - c.getLocal("nGroups") - ) + c.i32_and( + c.i32_and( + c.call(f1mPrefix+"_isZero", x0), + c.call(f1mPrefix+"_isZero", x1) ), - c.call( fPrefix + "_one", W), - c.setLocal("j", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("j"), - c.getLocal("nPerGroupDiv2") - ) - ), + c.call(f1mPrefix+"_isZero", x2) + ) + ); + } - c.setLocal( - "idx1", - c.i32_add( - c.getLocal("pBuff"), - c.i32_mul( - c.i32_add( - c.i32_mul( - c.getLocal("i"), - c.getLocal("nPerGroup") - ), - c.getLocal("j") - ), - c.i32_const(n8g) - ) - ) - ), + function buildInverse() { + const f = module.addFunction(prefix+"_inverse"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); - c.setLocal( - "idx2", - c.i32_add( - c.getLocal("idx1"), - c.getLocal("pairOffset") - ) - ), + const c = f.getCodeBuilder(); - c.call( - opGtimesF, - c.getLocal("idx2"), - W, - T - ), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); + const r0 = c.getLocal("r"); + const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8)); + const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8)); - c.call( - gPrefix + "_copy", - c.getLocal("idx1"), - U - ), + const t0 = c.i32_const(module.alloc(f1n8)); + const t1 = c.i32_const(module.alloc(f1n8)); + const t2 = c.i32_const(module.alloc(f1n8)); + const t3 = c.i32_const(module.alloc(f1n8)); + const t4 = c.i32_const(module.alloc(f1n8)); + const t5 = c.i32_const(module.alloc(f1n8)); + const c0 = c.i32_const(module.alloc(f1n8)); + const c1 = c.i32_const(module.alloc(f1n8)); + const c2 = c.i32_const(module.alloc(f1n8)); + const t6 = c.i32_const(module.alloc(f1n8)); + const AUX = c.i32_const(module.alloc(f1n8)); - c.call( - gPrefix + "_add", - U, - T, - c.getLocal("idx1"), - ), + f.addCode( + c.call(f1mPrefix+"_square", x0, t0), + c.call(f1mPrefix+"_square", x1, t1), + c.call(f1mPrefix+"_square", x2, t2), + c.call(f1mPrefix+"_mul", x0, x1, t3), + c.call(f1mPrefix+"_mul", x0, x2, t4), + c.call(f1mPrefix+"_mul", x1, x2, t5), + + c.call(mulNonResidueFn, t5, c0), + c.call(f1mPrefix+"_sub", t0, c0, c0), + + c.call(mulNonResidueFn, t2, c1), + c.call(f1mPrefix+"_sub", c1, t3, c1), + + c.call(f1mPrefix+"_sub", t1, t4, c2), + + c.call(f1mPrefix+"_mul", x2, c1, t6), + c.call(f1mPrefix+"_mul", x1, c2, AUX), + c.call(f1mPrefix+"_add", t6, AUX, t6), + c.call(mulNonResidueFn, t6, t6), + c.call(f1mPrefix+"_mul", x0, c0, AUX), + c.call(f1mPrefix+"_add", AUX, t6, t6), - c.call( - gPrefix + "_sub", - U, - T, - c.getLocal("idx2"), - ), + c.call(f1mPrefix+"_inverse", t6, t6), - c.call( - fPrefix + "_mul", - W, - c.getLocal("pwm"), - W, - ), - c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), - c.br(0) - )), - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) + c.call(f1mPrefix+"_mul", t6, c0, r0), + c.call(f1mPrefix+"_mul", t6, c1, r1), + c.call(f1mPrefix+"_mul", t6, c2, r2) ); } - // Reverse all and multiply by factor - function buildFFTFinal() { - const f = module.addFunction(prefix+"_fftFinal"); - f.addParam("pBuff", "i32"); - f.addParam("n", "i32"); - f.addParam("factor", "i32"); - f.addLocal("idx1", "i32"); - f.addLocal("idx2", "i32"); - f.addLocal("i", "i32"); - f.addLocal("ndiv2", "i32"); + function buildSign() { + const f = module.addFunction(prefix+"_sign"); + f.addParam("x", "i32"); + f.addLocal("s", "i32"); + f.setReturnType("i32"); const c = f.getCodeBuilder(); - const T = c.i32_const(module.alloc(n8g)); + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8)); f.addCode( - c.setLocal("ndiv2", c.i32_shr_u(c.getLocal("n"), c.i32_const(1))), + c.setLocal("s" , c.call( f1mPrefix + "_sign", x2)), c.if( - c.i32_and( - c.getLocal("n"), - c.i32_const(1) - ), - c.call( - opGtimesF, - c.i32_add( - c.getLocal("pBuff"), - c.i32_mul( - c.getLocal("ndiv2"), - c.i32_const(n8g) - ) - ), - c.getLocal("factor"), - c.i32_add( - c.getLocal("pBuff"), - c.i32_mul( - c.getLocal("ndiv2"), - c.i32_const(n8g) - ) - ), - ), + c.getLocal("s"), + c.ret(c.getLocal("s")) ), - c.setLocal("i", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_ge_u( - c.getLocal("i"), - c.getLocal("ndiv2") - ) - ), - - c.setLocal( - "idx1", - c.i32_add( - c.getLocal("pBuff"), - c.i32_mul( - c.getLocal("i"), - c.i32_const(n8g) - ) - ) - ), - - c.setLocal( - "idx2", - c.i32_add( - c.getLocal("pBuff"), - c.i32_mul( - c.i32_sub( - c.i32_sub( - c.getLocal("n"), - c.i32_const(1) - ), - c.getLocal("i") - ), - c.i32_const(n8g) - ) - ) - ), + c.setLocal("s" , c.call( f1mPrefix + "_sign", x1)), + c.if( + c.getLocal("s"), + c.ret(c.getLocal("s")) + ), + c.ret(c.call( f1mPrefix + "_sign", x0)) + ); + } - c.call( - opGtimesF, - c.getLocal("idx2"), - c.getLocal("factor"), - T - ), + function buildIsOne() { + const f = module.addFunction(prefix+"_isOne"); + f.addParam("x", "i32"); + f.setReturnType("i32"); - c.call( - opGtimesF, - c.getLocal("idx1"), - c.getLocal("factor"), - c.getLocal("idx2"), - ), + const c = f.getCodeBuilder(); - c.call( - gPrefix + "_copy", - T, - c.getLocal("idx1"), - ), + const x0 = c.getLocal("x"); + const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8)); + const x2 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8*2)); - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) + f.addCode( + c.ret( + c.i32_and( + c.i32_and( + c.call(f1mPrefix + "_isOne", x0), + c.call(f1mPrefix + "_isZero", x1) + ), + c.call(f1mPrefix + "_isZero", x2) + ) + ) ); } - buildRev(); - buildReversePermutation(); - buildFinalInverse(); - buildRawFFT(); - buildLog2(); - buildFFT(); - buildIFFT(); - buildFFTJoin(); - buildFFTJoinExt(); - buildFFTJoinExtInv(); - buildFFTMix(); - buildFFTFinal(); - buildPrepareLagrangeEvaluation(); + buildIsZero(); + buildIsOne(); + buildZero(); + buildOne(); + buildCopy(); + buildMul(); + buildSquare(); + buildAdd(); + buildSub(); + buildNeg(); + buildSign(); + buildToMontgomery(); + buildFromMontgomery(); + buildEq(); + buildInverse(); + buildTimesScalar(); + buildIsNegative(); - module.exportFunction(prefix+"_fft"); - module.exportFunction(prefix+"_ifft"); - module.exportFunction(prefix+"_rawfft"); - module.exportFunction(prefix+"_fftJoin"); - module.exportFunction(prefix+"_fftJoinExt"); - module.exportFunction(prefix+"_fftJoinExtInv"); - module.exportFunction(prefix+"_fftMix"); - module.exportFunction(prefix+"_fftFinal"); - module.exportFunction(prefix+"_prepareLagrangeEvaluation"); + module.exportFunction(prefix + "_isZero"); + module.exportFunction(prefix + "_isOne"); + module.exportFunction(prefix + "_zero"); + module.exportFunction(prefix + "_one"); + module.exportFunction(prefix + "_copy"); + module.exportFunction(prefix + "_mul"); + module.exportFunction(prefix + "_square"); + module.exportFunction(prefix + "_add"); + module.exportFunction(prefix + "_sub"); + module.exportFunction(prefix + "_neg"); + module.exportFunction(prefix + "_sign"); + module.exportFunction(prefix + "_fromMontgomery"); + module.exportFunction(prefix + "_toMontgomery"); + module.exportFunction(prefix + "_eq"); + module.exportFunction(prefix + "_inverse"); + buildBatchInverse(module, prefix); + buildExp( + module, + prefix + "_exp", + f1n8*3, + prefix + "_mul", + prefix + "_square", + prefix + "_copy", + prefix + "_one" + ); + module.exportFunction(prefix + "_exp"); + module.exportFunction(prefix + "_timesScalar"); + module.exportFunction(prefix + "_batchInverse"); + module.exportFunction(prefix + "_isNegative"); + return prefix; }; /* @@ -10434,335 +10366,403 @@ var build_fft = function buildFFT(module, prefix, gPrefix, fPrefix, opGtimesF) { along with wasmsnark. If not, see . */ -var build_pol = function buildPol(module, prefix, prefixField) { - - const n64 = module.modules[prefixField].n64; - const n8 = n64*8; +var build_timesscalarnaf = function buildTimesScalarNAF(module, fnName, elementLen, opAB, opAA, opAmB, opCopy, opInit) { + const f = module.addFunction(fnName); + f.addParam("base", "i32"); + f.addParam("scalar", "i32"); + f.addParam("scalarLength", "i32"); + f.addParam("r", "i32"); + f.addLocal("old0", "i32"); + f.addLocal("nbits", "i32"); + f.addLocal("i", "i32"); + f.addLocal("last", "i32"); + f.addLocal("cur", "i32"); + f.addLocal("carry", "i32"); + f.addLocal("p", "i32"); - function buildZero() { - const f = module.addFunction(prefix+"_zero"); - f.addParam("px", "i32"); - f.addParam("n", "i32"); - f.addLocal("lastp", "i32"); - f.addLocal("p", "i32"); + const c = f.getCodeBuilder(); - const c = f.getCodeBuilder(); + const aux = c.i32_const(module.alloc(elementLen)); - f.addCode( - c.setLocal("p", c.getLocal("px")), - c.setLocal( - "lastp", - c.i32_add( - c.getLocal("px"), - c.i32_mul( - c.getLocal("n"), - c.i32_const(n8) + function getBit(IDX) { + return c.i32_and( + c.i32_shr_u( + c.i32_load( + c.i32_add( + c.getLocal("scalar"), + c.i32_and( + c.i32_shr_u( + IDX, + c.i32_const(3) + ), + c.i32_const(0xFFFFFFFC) + ) ) + ), + c.i32_and( + IDX, + c.i32_const(0x1F) ) ), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("p"), - c.getLocal("lastp") - ) - ), - c.call(prefixField + "_zero", c.getLocal("p")), - c.setLocal("p", c.i32_add(c.getLocal("p"), c.i32_const(n8))), - c.br(0) - )) + c.i32_const(1) ); } - function buildConstructLC() { - const f = module.addFunction(prefix+"_constructLC"); - f.addParam("ppolynomials", "i32"); - f.addParam("psignals", "i32"); - f.addParam("nSignals", "i32"); - f.addParam("pres", "i32"); - f.addLocal("i", "i32"); - f.addLocal("j", "i32"); - f.addLocal("pp", "i32"); - f.addLocal("ps", "i32"); - f.addLocal("pd", "i32"); - f.addLocal("ncoefs", "i32"); + function pushBit(b) { + return [ + ...c.i32_store8( + c.getLocal("p"), + c.i32_const(b) + ), + ...c.setLocal( + "p", + c.i32_add( + c.getLocal("p"), + c.i32_const(1) + ) + ) + ]; + } - const c = f.getCodeBuilder(); + f.addCode( + c.if( + c.i32_eqz(c.getLocal("scalarLength")), + [ + ...c.call(opInit, c.getLocal("r")), + ...c.ret([]) + ] + ), + c.setLocal("nbits", c.i32_shl(c.getLocal("scalarLength"), c.i32_const(3))), + c.setLocal("old0", c.i32_load(c.i32_const(0))), + c.setLocal("p", c.getLocal("old0")), + c.i32_store( + c.i32_const(0), + c.i32_and( + c.i32_add( + c.i32_add( + c.getLocal("old0"), + c.i32_const(32) + ), + c.getLocal("nbits") + ), + c.i32_const(0xFFFFFFF8) + ) + ), + c.setLocal("i", c.i32_const(1)), - const aux = c.i32_const(module.alloc(n8)); + c.setLocal("last",getBit(c.i32_const(0))), + c.setLocal("carry",c.i32_const(0)), - f.addCode( - c.setLocal("i", c.i32_const(0)), - c.setLocal("pp", c.getLocal("ppolynomials")), - c.setLocal("ps", c.getLocal("psignals")), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("i"), - c.getLocal("nSignals") - ) + c.block(c.loop( + c.br_if(1, c.i32_eq( c.getLocal("i"), c.getLocal("nbits"))), + + c.setLocal("cur", getBit(c.getLocal("i"))), + c.if( c.getLocal("last"), + c.if( c.getLocal("cur"), + c.if(c.getLocal("carry"), + [ + ...c.setLocal("last", c.i32_const(0)), + ...c.setLocal("carry", c.i32_const(1)), + ...pushBit(1) + ] + , + [ + ...c.setLocal("last", c.i32_const(0)), + ...c.setLocal("carry", c.i32_const(1)), + ...pushBit(255) + ], + ), + c.if(c.getLocal("carry"), + [ + ...c.setLocal("last", c.i32_const(0)), + ...c.setLocal("carry", c.i32_const(1)), + ...pushBit(255) + ] + , + [ + ...c.setLocal("last", c.i32_const(0)), + ...c.setLocal("carry", c.i32_const(0)), + ...pushBit(1) + ], + ), ), + c.if( c.getLocal("cur"), + c.if(c.getLocal("carry"), + [ + ...c.setLocal("last", c.i32_const(0)), + ...c.setLocal("carry", c.i32_const(1)), + ...pushBit(0) + ] + , + [ + ...c.setLocal("last", c.i32_const(1)), + ...c.setLocal("carry", c.i32_const(0)), + ...pushBit(0) + ], + ), + c.if(c.getLocal("carry"), + [ + ...c.setLocal("last", c.i32_const(1)), + ...c.setLocal("carry", c.i32_const(0)), + ...pushBit(0) + ] + , + [ + ...c.setLocal("last", c.i32_const(0)), + ...c.setLocal("carry", c.i32_const(0)), + ...pushBit(0) + ], + ), + ) + ), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )), - c.setLocal("ncoefs", c.i32_load(c.getLocal("pp"))), - c.setLocal("pp", c.i32_add(c.getLocal("pp"), c.i32_const(4))), + c.if( c.getLocal("last"), + c.if(c.getLocal("carry"), + [ + ...pushBit(255), + ...pushBit(0), + ...pushBit(1) + ] + , + [ + ...pushBit(1) + ], + ), + c.if(c.getLocal("carry"), + [ + ...pushBit(0), + ...pushBit(1) + ] + ), + ), - c.setLocal("j", c.i32_const(0)), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("j"), - c.getLocal("ncoefs") - ) - ), + c.setLocal("p", c.i32_sub(c.getLocal("p"), c.i32_const(1))), - c.setLocal( - "pd", - c.i32_add( - c.getLocal("pres"), - c.i32_mul( - c.i32_load(c.getLocal("pp")), - c.i32_const(n8) - ) - ) - ), + // p already points to the last bit - c.setLocal("pp", c.i32_add(c.getLocal("pp"), c.i32_const(4))), + c.call(opCopy, c.getLocal("base"), aux), + c.call(opInit, c.getLocal("r")), - c.call( - prefixField + "_mul", - c.getLocal("ps"), - c.getLocal("pp"), - aux - ), + c.block(c.loop( - c.call( - prefixField + "_add", - aux, - c.getLocal("pd"), - c.getLocal("pd") - ), - c.setLocal("pp", c.i32_add(c.getLocal("pp"), c.i32_const(n8))), - c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), - c.br(0) - )), + c.call(opAA, c.getLocal("r"), c.getLocal("r")), - c.setLocal("ps", c.i32_add(c.getLocal("ps"), c.i32_const(n8))), - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) - ); - } + c.setLocal("cur", + c.i32_load8_u( + c.getLocal("p") + ) + ), - buildZero(); - buildConstructLC(); + c.if( + c.getLocal("cur"), + c.if( + c.i32_eq(c.getLocal("cur"), c.i32_const(1)), + c.call(opAB, c.getLocal("r"), aux, c.getLocal("r")), + c.call(opAmB, c.getLocal("r"), aux, c.getLocal("r")), + ) + ), + c.br_if(1, c.i32_eq( c.getLocal("old0"), c.getLocal("p"))), + c.setLocal("p", c.i32_sub(c.getLocal("p"), c.i32_const(1))), + c.br(0) - module.exportFunction(prefix + "_zero"); - module.exportFunction(prefix + "_constructLC"); + )), - return prefix; + c.i32_store( c.i32_const(0), c.getLocal("old0")) + + ); + +}; +/* + Copyright 2019 0KIMS association. + This file is part of wasmsnark (Web Assembly zkSnark Prover). + wasmsnark 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. -}; + wasmsnark 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. -var build_qap = function buildQAP(module, prefix, prefixField) { + You should have received a copy of the GNU General Public License + along with wasmsnark. If not, see . +*/ - const n64 = module.modules[prefixField].n64; - const n8 = n64*8; +var build_multiexp = function buildMultiexp(module, prefix, fnName, opAdd, n8b) { + const n64g = module.modules[prefix].n64; + const n8g = n64g*8; - function buildBuildABC() { - const f = module.addFunction(prefix+"_buildABC"); - f.addParam("pCoefs", "i32"); - f.addParam("nCoefs", "i32"); - f.addParam("pWitness", "i32"); - f.addParam("pA", "i32"); - f.addParam("pB", "i32"); - f.addParam("pC", "i32"); - f.addParam("offsetOut", "i32"); - f.addParam("nOut", "i32"); - f.addParam("offsetWitness", "i32"); - f.addParam("nWitness", "i32"); - f.addLocal("it", "i32"); - f.addLocal("ita", "i32"); - f.addLocal("itb", "i32"); - f.addLocal("last", "i32"); - f.addLocal("m", "i32"); - f.addLocal("c", "i32"); - f.addLocal("s", "i32"); - f.addLocal("pOut", "i32"); + function buildGetChunk() { + const f = module.addFunction(fnName + "_getChunk"); + f.addParam("pScalar", "i32"); + f.addParam("scalarSize", "i32"); // Number of bytes of the scalar + f.addParam("startBit", "i32"); // Bit to start extract + f.addParam("chunkSize", "i32"); // Chunk size in bits + f.addLocal("bitsToEnd", "i32"); + f.addLocal("mask", "i32"); + f.setReturnType("i32"); const c = f.getCodeBuilder(); - const aux = c.i32_const(module.alloc(n8)); - f.addCode( - - // Set output a and b to 0 - c.setLocal("ita", c.getLocal("pA")), - c.setLocal("itb", c.getLocal("pB")), - c.setLocal( - "last", - c.i32_add( - c.getLocal("pA"), + c.setLocal("bitsToEnd", + c.i32_sub( c.i32_mul( - c.getLocal("nOut"), - c.i32_const(n8) + c.getLocal("scalarSize"), + c.i32_const(8) + ), + c.getLocal("startBit") + ) + ), + c.if( + c.i32_gt_s( + c.getLocal("chunkSize"), + c.getLocal("bitsToEnd") + ), + c.setLocal( + "mask", + c.i32_sub( + c.i32_shl( + c.i32_const(1), + c.getLocal("bitsToEnd") + ), + c.i32_const(1) + ) + ), + c.setLocal( + "mask", + c.i32_sub( + c.i32_shl( + c.i32_const(1), + c.getLocal("chunkSize") + ), + c.i32_const(1) ) ) ), - c.block(c.loop( - c.br_if( - 1, - c.i32_eq( - c.getLocal("ita"), - c.getLocal("last") + c.i32_and( + c.i32_shr_u( + c.i32_load( + c.i32_add( + c.getLocal("pScalar"), + c.i32_shr_u( + c.getLocal("startBit"), + c.i32_const(3) + ) + ), + 0, // offset + 0 // align to byte. + ), + c.i32_and( + c.getLocal("startBit"), + c.i32_const(0x7) ) ), - c.call(prefixField + "_zero", c.getLocal("ita")), - c.call(prefixField + "_zero", c.getLocal("itb")), - c.setLocal("ita", c.i32_add(c.getLocal("ita"), c.i32_const(n8))), - c.setLocal("itb", c.i32_add(c.getLocal("itb"), c.i32_const(n8))), - c.br(0) - )), + c.getLocal("mask") + ) + ); + } + + function buildMutiexpChunk() { + const f = module.addFunction(fnName + "_chunk"); + f.addParam("pBases", "i32"); + f.addParam("pScalars", "i32"); + f.addParam("scalarSize", "i32"); // Number of points + f.addParam("n", "i32"); // Number of points + f.addParam("startBit", "i32"); // bit where it starts the chunk + f.addParam("chunkSize", "i32"); // bit where it starts the chunk + f.addParam("pr", "i32"); + f.addLocal("nChunks", "i32"); + f.addLocal("itScalar", "i32"); + f.addLocal("endScalar", "i32"); + f.addLocal("itBase", "i32"); + f.addLocal("i", "i32"); + f.addLocal("j", "i32"); + f.addLocal("nTable", "i32"); + f.addLocal("pTable", "i32"); + f.addLocal("idx", "i32"); + f.addLocal("pIdxTable", "i32"); + const c = f.getCodeBuilder(); + + f.addCode( + c.if( + c.i32_eqz(c.getLocal("n")), + [ + ...c.call(prefix + "_zero", c.getLocal("pr")), + ...c.ret([]) + ] + ), + + // Allocate memory - c.setLocal("it", c.getLocal("pCoefs")), c.setLocal( - "last", + "nTable", + c.i32_shl( + c.i32_const(1), + c.getLocal("chunkSize") + ) + ), + c.setLocal("pTable", c.i32_load( c.i32_const(0) )), + c.i32_store( + c.i32_const(0), c.i32_add( - c.getLocal("pCoefs"), + c.getLocal("pTable"), c.i32_mul( - c.getLocal("nCoefs"), - c.i32_const(n8+12) + c.getLocal("nTable"), + c.i32_const(n8g) ) ) ), + + // Reset Table + c.setLocal("j", c.i32_const(0)), c.block(c.loop( c.br_if( 1, - c.i32_eq( - c.getLocal("it"), - c.getLocal("last") - ) - ), - c.setLocal( - "s", - c.i32_load(c.getLocal("it"), 8) - ), - c.if( - c.i32_or( - c.i32_lt_u( - c.getLocal("s"), - c.getLocal("offsetWitness"), - ), - c.i32_ge_u( - c.getLocal("s"), - c.i32_add( - c.getLocal("offsetWitness"), - c.getLocal("nWitness"), - ) - ) - ), - [ - ...c.setLocal("it", c.i32_add(c.getLocal("it"), c.i32_const(n8+12))), - ...c.br(1) - ] - ), - - c.setLocal( - "m", - c.i32_load(c.getLocal("it")) - ), - c.if( - c.i32_eq(c.getLocal("m"), c.i32_const(0)), - c.setLocal("pOut", c.getLocal("pA")), - c.if( - c.i32_eq(c.getLocal("m"), c.i32_const(1)), - c.setLocal("pOut", c.getLocal("pB")), - [ - ...c.setLocal("it", c.i32_add(c.getLocal("it"), c.i32_const(n8+12))), - ...c.br(1) - ] - ) - ), - c.setLocal( - "c", - c.i32_load(c.getLocal("it"), 4) - ), - c.if( - c.i32_or( - c.i32_lt_u( - c.getLocal("c"), - c.getLocal("offsetOut"), - ), - c.i32_ge_u( - c.getLocal("c"), - c.i32_add( - c.getLocal("offsetOut"), - c.getLocal("nOut"), - ) - ) - ), - [ - ...c.setLocal("it", c.i32_add(c.getLocal("it"), c.i32_const(n8+12))), - ...c.br(1) - ] - ), - c.setLocal( - "pOut", - c.i32_add( - c.getLocal("pOut"), - c.i32_mul( - c.i32_sub( - c.getLocal("c"), - c.getLocal("offsetOut") - ), - c.i32_const(n8) - ) + c.i32_eq( + c.getLocal("j"), + c.getLocal("nTable") ) ), + c.call( - prefixField + "_mul", + prefix + "_zero", c.i32_add( - c.getLocal("pWitness"), + c.getLocal("pTable"), c.i32_mul( - c.i32_sub(c.getLocal("s"), c.getLocal("offsetWitness")), - c.i32_const(n8) + c.getLocal("j"), + c.i32_const(n8g) ) - ), - c.i32_add( c.getLocal("it"), c.i32_const(12)), - aux - ), - c.call( - prefixField + "_add", - c.getLocal("pOut"), - aux, - c.getLocal("pOut"), + ) ), - c.setLocal("it", c.i32_add(c.getLocal("it"), c.i32_const(n8+12))), + + c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), c.br(0) )), - c.setLocal("ita", c.getLocal("pA")), - c.setLocal("itb", c.getLocal("pB")), - c.setLocal("it", c.getLocal("pC")), - c.setLocal( - "last", + // Distribute elements + c.setLocal("itBase", c.getLocal("pBases")), + c.setLocal("itScalar", c.getLocal("pScalars")), + c.setLocal("endScalar", c.i32_add( - c.getLocal("pA"), + c.getLocal("pScalars"), c.i32_mul( - c.getLocal("nOut"), - c.i32_const(n8) + c.getLocal("n"), + c.getLocal("scalarSize") ) ) ), @@ -10770,144 +10770,287 @@ var build_qap = function buildQAP(module, prefix, prefixField) { c.br_if( 1, c.i32_eq( - c.getLocal("ita"), - c.getLocal("last") + c.getLocal("itScalar"), + c.getLocal("endScalar") ) ), - c.call( - prefixField + "_mul", - c.getLocal("ita"), - c.getLocal("itb"), - c.getLocal("it") + + c.setLocal( + "idx", + c.call(fnName + "_getChunk", + c.getLocal("itScalar"), + c.getLocal("scalarSize"), + c.getLocal("startBit"), + c.getLocal("chunkSize") + ) ), - c.setLocal("ita", c.i32_add(c.getLocal("ita"), c.i32_const(n8))), - c.setLocal("itb", c.i32_add(c.getLocal("itb"), c.i32_const(n8))), - c.setLocal("it", c.i32_add(c.getLocal("it"), c.i32_const(n8))), + + c.if( + c.getLocal("idx"), + [ + ...c.setLocal( + "pIdxTable", + c.i32_add( + c.getLocal("pTable"), + c.i32_mul( + c.i32_sub( + c.getLocal("idx"), + c.i32_const(1) + ), + c.i32_const(n8g) + ) + ) + ), + ...c.call( + opAdd, + c.getLocal("pIdxTable"), + c.getLocal("itBase"), + c.getLocal("pIdxTable"), + ) + ] + ), + + c.setLocal("itScalar", c.i32_add(c.getLocal("itScalar"), c.getLocal("scalarSize"))), + c.setLocal("itBase", c.i32_add(c.getLocal("itBase"), c.i32_const(n8b))), c.br(0) )), + c.call(fnName + "_reduceTable", c.getLocal("pTable"), c.getLocal("chunkSize")), + c.call( + prefix + "_copy", + c.getLocal("pTable"), + c.getLocal("pr") + ), + + + c.i32_store( + c.i32_const(0), + c.getLocal("pTable") + ) + ); } - function buildJoinABC() { - const f = module.addFunction(prefix+"_joinABC"); - f.addParam("pA", "i32"); - f.addParam("pB", "i32"); - f.addParam("pC", "i32"); - f.addParam("n", "i32"); - f.addParam("pP", "i32"); - f.addLocal("ita", "i32"); - f.addLocal("itb", "i32"); - f.addLocal("itc", "i32"); - f.addLocal("itp", "i32"); - f.addLocal("last", "i32"); + function buildMultiexp() { + const f = module.addFunction(fnName); + f.addParam("pBases", "i32"); + f.addParam("pScalars", "i32"); + f.addParam("scalarSize", "i32"); // Number of points + f.addParam("n", "i32"); // Number of points + f.addParam("pr", "i32"); + f.addLocal("chunkSize", "i32"); + f.addLocal("nChunks", "i32"); + f.addLocal("itScalar", "i32"); + f.addLocal("endScalar", "i32"); + f.addLocal("itBase", "i32"); + f.addLocal("itBit", "i32"); + f.addLocal("i", "i32"); + f.addLocal("j", "i32"); + f.addLocal("nTable", "i32"); + f.addLocal("pTable", "i32"); + f.addLocal("idx", "i32"); + f.addLocal("pIdxTable", "i32"); const c = f.getCodeBuilder(); - const aux = c.i32_const(module.alloc(n8)); + const aux = c.i32_const(module.alloc(n8g)); + + const pTSizes = module.alloc([ + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 16, 16, 15, 14, 13, 13, + 12, 11, 10, 9, 8, 7, 7, 6, + 5 , 4, 3, 2, 1, 1, 1, 1 + ]); f.addCode( - c.setLocal("ita", c.getLocal("pA")), - c.setLocal("itb", c.getLocal("pB")), - c.setLocal("itc", c.getLocal("pC")), - c.setLocal("itp", c.getLocal("pP")), + c.call(prefix + "_zero", c.getLocal("pr")), + c.if( + c.i32_eqz(c.getLocal("n")), + c.ret([]) + ), + c.setLocal("chunkSize", c.i32_load8_u( c.i32_clz(c.getLocal("n")), pTSizes )), c.setLocal( - "last", + "nChunks", c.i32_add( - c.getLocal("pA"), - c.i32_mul( - c.getLocal("n"), - c.i32_const(n8) - ) + c.i32_div_u( + c.i32_sub( + c.i32_shl( + c.getLocal("scalarSize"), + c.i32_const(3) + ), + c.i32_const(1) + ), + c.getLocal("chunkSize") + ), + c.i32_const(1) + ) + ), + + + // Allocate memory + + c.setLocal( + "itBit", + c.i32_mul( + c.i32_sub( + c.getLocal("nChunks"), + c.i32_const(1) + ), + c.getLocal("chunkSize") ) ), c.block(c.loop( c.br_if( 1, - c.i32_eq( - c.getLocal("ita"), - c.getLocal("last") + c.i32_lt_s( + c.getLocal("itBit"), + c.i32_const(0) ) ), + + // Double nChunk times + c.if( + c.i32_eqz(c.call(prefix + "_isZero", c.getLocal("pr"))), + [ + ...c.setLocal("j", c.i32_const(0)), + ...c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("j"), + c.getLocal("chunkSize") + ) + ), + + c.call(prefix + "_double", c.getLocal("pr"), c.getLocal("pr")), + + c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), + c.br(0) + )) + ] + ), + c.call( - prefixField + "_mul", - c.getLocal("ita"), - c.getLocal("itb"), + fnName + "_chunk", + c.getLocal("pBases"), + c.getLocal("pScalars"), + c.getLocal("scalarSize"), + c.getLocal("n"), + c.getLocal("itBit"), + c.getLocal("chunkSize"), aux ), + c.call( - prefixField + "_sub", + prefix + "_add", + c.getLocal("pr"), aux, - c.getLocal("itc"), - c.getLocal("itp"), + c.getLocal("pr") ), - c.setLocal("ita", c.i32_add(c.getLocal("ita"), c.i32_const(n8))), - c.setLocal("itb", c.i32_add(c.getLocal("itb"), c.i32_const(n8))), - c.setLocal("itc", c.i32_add(c.getLocal("itc"), c.i32_const(n8))), - c.setLocal("itp", c.i32_add(c.getLocal("itp"), c.i32_const(n8))), + c.setLocal("itBit", c.i32_sub(c.getLocal("itBit"), c.getLocal("chunkSize"))), c.br(0) )) ); } - function buildBatchAdd() { - const f = module.addFunction(prefix+"_batchAdd"); - f.addParam("pa", "i32"); - f.addParam("pb", "i32"); - f.addParam("n", "i32"); - f.addParam("pr", "i32"); - f.addLocal("ita", "i32"); - f.addLocal("itb", "i32"); - f.addLocal("itr", "i32"); - f.addLocal("last", "i32"); + function buildReduceTable() { + const f = module.addFunction(fnName + "_reduceTable"); + f.addParam("pTable", "i32"); + f.addParam("p", "i32"); // Number of bits of the table + f.addLocal("half", "i32"); + f.addLocal("it1", "i32"); + f.addLocal("it2", "i32"); + f.addLocal("pAcc", "i32"); const c = f.getCodeBuilder(); - f.addCode( - c.setLocal("ita", c.getLocal("pa")), - c.setLocal("itb", c.getLocal("pb")), - c.setLocal("itr", c.getLocal("pr")), + f.addCode( + c.if( + c.i32_eq(c.getLocal("p"), c.i32_const(1)), + c.ret([]) + ), + c.setLocal( + "half", + c.i32_shl( + c.i32_const(1), + c.i32_sub( + c.getLocal("p"), + c.i32_const(1) + ) + ) + ), + + c.setLocal("it1", c.getLocal("pTable")), c.setLocal( - "last", + "it2", c.i32_add( - c.getLocal("pa"), + c.getLocal("pTable"), c.i32_mul( - c.getLocal("n"), - c.i32_const(n8) + c.getLocal("half"), + c.i32_const(n8g) ) ) ), + c.setLocal("pAcc", + c.i32_sub( + c.getLocal("it2"), + c.i32_const(n8g) + ) + ), c.block(c.loop( c.br_if( 1, c.i32_eq( - c.getLocal("ita"), - c.getLocal("last") + c.getLocal("it1"), + c.getLocal("pAcc") ) ), c.call( - prefixField + "_add", - c.getLocal("ita"), - c.getLocal("itb"), - c.getLocal("itr"), + prefix + "_add", + c.getLocal("it1"), + c.getLocal("it2"), + c.getLocal("it1") ), - c.setLocal("ita", c.i32_add(c.getLocal("ita"), c.i32_const(n8))), - c.setLocal("itb", c.i32_add(c.getLocal("itb"), c.i32_const(n8))), - c.setLocal("itr", c.i32_add(c.getLocal("itr"), c.i32_const(n8))), + c.call( + prefix + "_add", + c.getLocal("pAcc"), + c.getLocal("it2"), + c.getLocal("pAcc") + ), + c.setLocal("it1", c.i32_add(c.getLocal("it1"), c.i32_const(n8g))), + c.setLocal("it2", c.i32_add(c.getLocal("it2"), c.i32_const(n8g))), c.br(0) - )) + )), + + c.call( + fnName + "_reduceTable", + c.getLocal("pTable"), + c.i32_sub( + c.getLocal("p"), + c.i32_const(1) + ) + ), + + c.setLocal("p", c.i32_sub(c.getLocal("p"), c.i32_const(1))), + c.block(c.loop( + c.br_if(1, c.i32_eqz(c.getLocal("p"))), + c.call(prefix + "_double", c.getLocal("pAcc"), c.getLocal("pAcc")), + c.setLocal("p", c.i32_sub(c.getLocal("p"), c.i32_const(1))), + c.br(0) + )), + + c.call(prefix + "_add", c.getLocal("pTable"), c.getLocal("pAcc"), c.getLocal("pTable")) ); } - buildBuildABC(); - buildJoinABC(); - buildBatchAdd(); + buildGetChunk(); + buildReduceTable(); + buildMutiexpChunk(); + buildMultiexp(); - module.exportFunction(prefix + "_buildABC"); - module.exportFunction(prefix + "_joinABC"); - module.exportFunction(prefix + "_batchAdd"); + module.exportFunction(fnName); + module.exportFunction(fnName +"_chunk"); - return prefix; }; @@ -10930,6181 +11073,6501 @@ var build_qap = function buildQAP(module, prefix, prefixField) { along with wasmsnark. If not, see . */ -var build_applykey = function buildApplyKey(module, fnName, gPrefix, frPrefix, sizeGIn, sizeGOut, sizeF, opGtimesF) { +const buildTimesScalarNAF = build_timesscalarnaf; +//const buildTimesScalar = require("./build_timesscalar"); +const buildBatchConvertion = build_batchconvertion; +const buildMultiexp = build_multiexp; - const f = module.addFunction(fnName); - f.addParam("pIn", "i32"); - f.addParam("n", "i32"); - f.addParam("pFirst", "i32"); - f.addParam("pInc", "i32"); - f.addParam("pOut", "i32"); - f.addLocal("pOldFree", "i32"); - f.addLocal("i", "i32"); - f.addLocal("pFrom", "i32"); - f.addLocal("pTo", "i32"); +var build_curve_jacobian_a0 = function buildCurve(module, prefix, prefixField, pB) { - const c = f.getCodeBuilder(); - const t = c.i32_const(module.alloc(sizeF)); + const n64 = module.modules[prefixField].n64; + const n8 = n64*8; - f.addCode( - c.setLocal("pFrom", c.getLocal("pIn")), - c.setLocal("pTo", c.getLocal("pOut")), - ); + if (module.modules[prefix]) return prefix; // already builded + module.modules[prefix] = { + n64: n64*3 + }; - // t = first - f.addCode( - c.call( - frPrefix + "_copy", - c.getLocal("pFirst"), - t - ) - ); - f.addCode( - c.setLocal("i", c.i32_const(0)), - c.block(c.loop( - c.br_if(1, c.i32_eq ( c.getLocal("i"), c.getLocal("n") )), + function buildIsZero() { + const f = module.addFunction(prefix + "_isZero"); + f.addParam("p1", "i32"); + f.setReturnType("i32"); - c.call( - opGtimesF, - c.getLocal("pFrom"), - t, - c.getLocal("pTo") - ), - c.setLocal("pFrom", c.i32_add(c.getLocal("pFrom"), c.i32_const(sizeGIn))), - c.setLocal("pTo", c.i32_add(c.getLocal("pTo"), c.i32_const(sizeGOut))), + const c = f.getCodeBuilder(); - // t = t* inc - c.call( - frPrefix + "_mul", - t, - c.getLocal("pInc"), - t - ), - c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) - ); + f.addCode(c.call( + prefixField + "_isZero", + c.i32_add( + c.getLocal("p1"), + c.i32_const(n8*2) + ) + )); + } + function buildIsZeroAffine() { + const f = module.addFunction(prefix + "_isZeroAffine"); + f.addParam("p1", "i32"); + f.setReturnType("i32"); + + const c = f.getCodeBuilder(); + + f.addCode( + c.i32_and( + c.call( + prefixField + "_isZero", + c.getLocal("p1") + ), + c.call( + prefixField + "_isZero", + c.i32_add( + c.getLocal("p1"), + c.i32_const(n8) + ) + ) + ) + ); + } + + function buildCopy() { + const f = module.addFunction(prefix + "_copy"); + f.addParam("ps", "i32"); + f.addParam("pd", "i32"); + + const c = f.getCodeBuilder(); + + for (let i=0; i acc + ( b!=0 ? 1 : 0) ,0); - const ateNCoefs = ateNAddCoefs + ateNDblCoefs + 1; - const prePSize = 3*2*n8; - const preQSize = 3*n8*2 + ateNCoefs*ateCoefSize; + function buildFromMontgomery() { + const f = module.addFunction(prefix + "_fromMontgomery"); + f.addParam("p1", "i32"); + f.addParam("pr", "i32"); + const c = f.getCodeBuilder(); - module.modules[prefix] = { - n64: n64, - pG1gen: pG1gen, - pG1zero: pG1zero, - pG1b: pG1b, - pG2gen: pG2gen, - pG2zero: pG2zero, - pG2b: pG2b, - pq: module.modules["f1m"].pq, - pr: pr, - pOneT: pOneT, - prePSize: prePSize, - preQSize: preQSize, - r: r.toString(), - q: q.toString() - }; + f.addCode(c.call( + prefixField + "_fromMontgomery", + c.getLocal("p1"), + c.getLocal("pr") + )); + for (let i=1; i<3; i++) { + f.addCode(c.call( + prefixField + "_fromMontgomery", + c.i32_add(c.getLocal("p1"), c.i32_const(i*n8)), + c.i32_add(c.getLocal("pr"), c.i32_const(i*n8)) + )); + } + } - // console.log("PrePSize: " +prePSize); - // console.log("PreQSize: " +preQSize); - const finalExpZ = 4965661367192848881n; + function buildFromMontgomeryAffine() { + const f = module.addFunction(prefix + "_fromMontgomeryAffine"); + f.addParam("p1", "i32"); + f.addParam("pr", "i32"); - function naf(n) { - let E = n; - const res = []; - while (E > 0n) { - if (isOdd$1(E)) { - const z = 2 - Number(E % 4n); - res.push( z ); - E = E - BigInt(z); - } else { - res.push( 0 ); - } - E = E >> 1n; + const c = f.getCodeBuilder(); + + f.addCode(c.call( + prefixField + "_fromMontgomery", + c.getLocal("p1"), + c.getLocal("pr") + )); + for (let i=1; i<2; i++) { + f.addCode(c.call( + prefixField + "_fromMontgomery", + c.i32_add(c.getLocal("p1"), c.i32_const(i*n8)), + c.i32_add(c.getLocal("pr"), c.i32_const(i*n8)) + )); } - return res; } - function bits(n) { - let E = n; - const res = []; - while (E > 0n) { - if (isOdd$1(E)) { - res.push( 1 ); - } else { - res.push( 0 ); - } - E = E >> 1n; - } - return res; - } + function buildAdd() { - function buildPrepareG1() { - const f = module.addFunction(prefix+ "_prepareG1"); - f.addParam("pP", "i32"); - f.addParam("ppreP", "i32"); + const f = module.addFunction(prefix + "_add"); + f.addParam("p1", "i32"); + f.addParam("p2", "i32"); + f.addParam("pr", "i32"); + f.addLocal("z1", "i32"); + f.addLocal("z2", "i32"); const c = f.getCodeBuilder(); - f.addCode( - c.call(g1mPrefix + "_normalize", c.getLocal("pP"), c.getLocal("ppreP")), // TODO Remove if already in affine - ); - } + const x1 = c.getLocal("p1"); + const y1 = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); + f.addCode(c.setLocal("z1", c.i32_add(c.getLocal("p1"), c.i32_const(n8*2)))); + const z1 = c.getLocal("z1"); + const x2 = c.getLocal("p2"); + const y2 = c.i32_add(c.getLocal("p2"), c.i32_const(n8)); + f.addCode(c.setLocal("z2", c.i32_add(c.getLocal("p2"), c.i32_const(n8*2)))); + const z2 = c.getLocal("z2"); + const x3 = c.getLocal("pr"); + const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); + const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2)); - function buildPrepAddStep() { - const f = module.addFunction(prefix+ "_prepAddStep"); - f.addParam("pQ", "i32"); - f.addParam("pR", "i32"); - f.addParam("pCoef", "i32"); + const Z1Z1 = c.i32_const(module.alloc(n8)); + const Z2Z2 = c.i32_const(module.alloc(n8)); + const U1 = c.i32_const(module.alloc(n8)); + const U2 = c.i32_const(module.alloc(n8)); + const Z1_cubed = c.i32_const(module.alloc(n8)); + const Z2_cubed = c.i32_const(module.alloc(n8)); + const S1 = c.i32_const(module.alloc(n8)); + const S2 = c.i32_const(module.alloc(n8)); + const H = c.i32_const(module.alloc(n8)); + const S2_minus_S1 = c.i32_const(module.alloc(n8)); + const I = c.i32_const(module.alloc(n8)); + const J = c.i32_const(module.alloc(n8)); + const r = c.i32_const(module.alloc(n8)); + const r2 = c.i32_const(module.alloc(n8)); + const V = c.i32_const(module.alloc(n8)); + const V2 = c.i32_const(module.alloc(n8)); + const S1_J2 = c.i32_const(module.alloc(n8)); - const c = f.getCodeBuilder(); + f.addCode( + c.if( + c.call(prefix + "_isZero", c.getLocal("p1")), + [ + ...c.call(prefix + "_copy", c.getLocal("p2"), c.getLocal("pr")), + ...c.ret([]) + ] + ), + c.if( + c.call(prefix + "_isZero", c.getLocal("p2")), + [ + ...c.call(prefix + "_copy", c.getLocal("p1"), c.getLocal("pr")), + ...c.ret([]) + ] + ), + c.if( + c.call(prefixField + "_isOne", z1), + [ + ...c.call(prefix + "_addMixed", x2, x1, x3), + ...c.ret([]) + ] + ), + c.if( + c.call(prefixField + "_isOne", z2), + [ + ...c.call(prefix + "_addMixed", x1, x2, x3), + ...c.ret([]) + ] + ), + c.call(prefixField + "_square", z1, Z1Z1), + c.call(prefixField + "_square", z2, Z2Z2), + c.call(prefixField + "_mul", x1, Z2Z2, U1), + c.call(prefixField + "_mul", x2, Z1Z1, U2), + c.call(prefixField + "_mul", z1, Z1Z1, Z1_cubed), + c.call(prefixField + "_mul", z2, Z2Z2, Z2_cubed), + c.call(prefixField + "_mul", y1, Z2_cubed, S1), + c.call(prefixField + "_mul", y2, Z1_cubed, S2), - const X2 = c.getLocal("pQ"); - const Y2 = c.i32_add(c.getLocal("pQ"), c.i32_const(f2size)); + c.if( + c.call(prefixField + "_eq", U1, U2), + c.if( + c.call(prefixField + "_eq", S1, S2), + [ + ...c.call(prefix + "_double", c.getLocal("p1"), c.getLocal("pr")), + ...c.ret([]) + ] + ) + ), - const X1 = c.getLocal("pR"); - const Y1 = c.i32_add(c.getLocal("pR"), c.i32_const(f2size)); - const Z1 = c.i32_add(c.getLocal("pR"), c.i32_const(2*f2size)); + c.call(prefixField + "_sub", U2, U1, H), + c.call(prefixField + "_sub", S2, S1, S2_minus_S1), + c.call(prefixField + "_add", H, H, I), + c.call(prefixField + "_square", I, I), + c.call(prefixField + "_mul", H, I, J), + c.call(prefixField + "_add", S2_minus_S1, S2_minus_S1, r), + c.call(prefixField + "_mul", U1, I, V), + c.call(prefixField + "_square", r, r2), + c.call(prefixField + "_add", V, V, V2), - const ELL_0 = c.getLocal("pCoef"); - const ELL_VW = c.i32_add(c.getLocal("pCoef"), c.i32_const(f2size)); - const ELL_VV = c.i32_add(c.getLocal("pCoef"), c.i32_const(2*f2size)); + c.call(prefixField + "_sub", r2, J, x3), + c.call(prefixField + "_sub", x3, V2, x3), - const D = ELL_VW; - const E = c.i32_const(module.alloc(f2size)); - const F = c.i32_const(module.alloc(f2size)); - const G = c.i32_const(module.alloc(f2size)); - const H = c.i32_const(module.alloc(f2size)); - const I = c.i32_const(module.alloc(f2size)); - const J = c.i32_const(module.alloc(f2size)); - const AUX = c.i32_const(module.alloc(f2size)); + c.call(prefixField + "_mul", S1, J, S1_J2), + c.call(prefixField + "_add", S1_J2, S1_J2, S1_J2), - f.addCode( - // D = X1 - X2*Z1 - c.call(f2mPrefix + "_mul", X2, Z1, D), - c.call(f2mPrefix + "_sub", X1, D, D), + c.call(prefixField + "_sub", V, x3, y3), + c.call(prefixField + "_mul", y3, r, y3), + c.call(prefixField + "_sub", y3, S1_J2, y3), - // E = Y1 - Y2*Z1 - c.call(f2mPrefix + "_mul", Y2, Z1, E), - c.call(f2mPrefix + "_sub", Y1, E, E), + c.call(prefixField + "_add", z1, z2, z3), + c.call(prefixField + "_square", z3, z3), + c.call(prefixField + "_sub", z3, Z1Z1, z3), + c.call(prefixField + "_sub", z3, Z2Z2, z3), + c.call(prefixField + "_mul", z3, H, z3), + ); - // F = D^2 - c.call(f2mPrefix + "_square", D, F), + } - // G = E^2 - c.call(f2mPrefix + "_square", E, G), - // H = D*F - c.call(f2mPrefix + "_mul", D, F, H), + function buildAddMixed() { - // I = X1 * F - c.call(f2mPrefix + "_mul", X1, F, I), + const f = module.addFunction(prefix + "_addMixed"); + f.addParam("p1", "i32"); + f.addParam("p2", "i32"); + f.addParam("pr", "i32"); + f.addLocal("z1", "i32"); - // J = H + Z1*G - (I+I) - c.call(f2mPrefix + "_add", I, I, AUX), - c.call(f2mPrefix + "_mul", Z1, G, J), - c.call(f2mPrefix + "_add", H, J, J), - c.call(f2mPrefix + "_sub", J, AUX, J), + const c = f.getCodeBuilder(); + const x1 = c.getLocal("p1"); + const y1 = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); + f.addCode(c.setLocal("z1", c.i32_add(c.getLocal("p1"), c.i32_const(n8*2)))); + const z1 = c.getLocal("z1"); + const x2 = c.getLocal("p2"); + const y2 = c.i32_add(c.getLocal("p2"), c.i32_const(n8)); + const x3 = c.getLocal("pr"); + const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); + const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2)); - // X3 (X1) = D*J - c.call(f2mPrefix + "_mul", D, J, X1), + const Z1Z1 = c.i32_const(module.alloc(n8)); + const U2 = c.i32_const(module.alloc(n8)); + const Z1_cubed = c.i32_const(module.alloc(n8)); + const S2 = c.i32_const(module.alloc(n8)); + const H = c.i32_const(module.alloc(n8)); + const HH = c.i32_const(module.alloc(n8)); + const S2_minus_y1 = c.i32_const(module.alloc(n8)); + const I = c.i32_const(module.alloc(n8)); + const J = c.i32_const(module.alloc(n8)); + const r = c.i32_const(module.alloc(n8)); + const r2 = c.i32_const(module.alloc(n8)); + const V = c.i32_const(module.alloc(n8)); + const V2 = c.i32_const(module.alloc(n8)); + const y1_J2 = c.i32_const(module.alloc(n8)); - // Y3 (Y1) = E*(I-J)-(H*Y1) - c.call(f2mPrefix + "_mul", H, Y1, Y1), - c.call(f2mPrefix + "_sub", I, J, AUX), - c.call(f2mPrefix + "_mul", E, AUX, AUX), - c.call(f2mPrefix + "_sub", AUX, Y1, Y1), + f.addCode( + c.if( + c.call(prefix + "_isZero", c.getLocal("p1")), + [ + ...c.call(prefix + "_copyAffine", c.getLocal("p2"), c.getLocal("pr")), + ...c.call(prefixField + "_one", c.i32_add(c.getLocal("pr") , c.i32_const(n8*2))), + ...c.ret([]) + ] + ), + c.if( + c.call(prefix + "_isZeroAffine", c.getLocal("p2")), + [ + ...c.call(prefix + "_copy", c.getLocal("p1"), c.getLocal("pr")), + ...c.ret([]) + ] + ), + c.if( + c.call(prefixField + "_isOne", z1), + [ + ...c.call(prefix + "_addAffine", x1, x2, x3), + ...c.ret([]) + ] + ), + c.call(prefixField + "_square", z1, Z1Z1), + c.call(prefixField + "_mul", x2, Z1Z1, U2), + c.call(prefixField + "_mul", z1, Z1Z1, Z1_cubed), + c.call(prefixField + "_mul", y2, Z1_cubed, S2), - // Z3 (Z1) = Z1*H - c.call(f2mPrefix + "_mul", Z1, H, Z1), + c.if( + c.call(prefixField + "_eq", x1, U2), + c.if( + c.call(prefixField + "_eq", y1, S2), + [ + ...c.call(prefix + "_doubleAffine", c.getLocal("p2"), c.getLocal("pr")), + ...c.ret([]) + ] + ) + ), - // ell_0 = xi * (E * X2 - D * Y2) - c.call(f2mPrefix + "_mul", D, Y2, AUX), - c.call(f2mPrefix + "_mul", E, X2, ELL_0), - c.call(f2mPrefix + "_sub", ELL_0, AUX, ELL_0), - c.call(f2mPrefix + "_mul", ELL_0, c.i32_const(pAltBn128Twist), ELL_0), + c.call(prefixField + "_sub", U2, x1, H), + c.call(prefixField + "_sub", S2, y1, S2_minus_y1), + c.call(prefixField + "_square", H, HH), + c.call(prefixField + "_add", HH , HH, I), + c.call(prefixField + "_add", I , I, I), + c.call(prefixField + "_mul", H, I, J), + c.call(prefixField + "_add", S2_minus_y1, S2_minus_y1, r), + c.call(prefixField + "_mul", x1, I, V), + c.call(prefixField + "_square", r, r2), + c.call(prefixField + "_add", V, V, V2), + c.call(prefixField + "_sub", r2, J, x3), + c.call(prefixField + "_sub", x3, V2, x3), - // ell_VV = - E (later: * xP) - c.call(f2mPrefix + "_neg", E, ELL_VV), + c.call(prefixField + "_mul", y1, J, y1_J2), + c.call(prefixField + "_add", y1_J2, y1_J2, y1_J2), - // ell_VW = D (later: * yP ) - // Already assigned + c.call(prefixField + "_sub", V, x3, y3), + c.call(prefixField + "_mul", y3, r, y3), + c.call(prefixField + "_sub", y3, y1_J2, y3), + c.call(prefixField + "_add", z1, H, z3), + c.call(prefixField + "_square", z3, z3), + c.call(prefixField + "_sub", z3, Z1Z1, z3), + c.call(prefixField + "_sub", z3, HH, z3), ); } + function buildAddAffine() { - function buildPrepDoubleStep() { - const f = module.addFunction(prefix+ "_prepDblStep"); - f.addParam("pR", "i32"); - f.addParam("pCoef", "i32"); + const f = module.addFunction(prefix + "_addAffine"); + f.addParam("p1", "i32"); + f.addParam("p2", "i32"); + f.addParam("pr", "i32"); + f.addLocal("z1", "i32"); const c = f.getCodeBuilder(); - const X1 = c.getLocal("pR"); - const Y1 = c.i32_add(c.getLocal("pR"), c.i32_const(f2size)); - const Z1 = c.i32_add(c.getLocal("pR"), c.i32_const(2*f2size)); - - const ELL_0 = c.getLocal("pCoef"); - const ELL_VW = c.i32_add(c.getLocal("pCoef"), c.i32_const(f2size)); - const ELL_VV = c.i32_add(c.getLocal("pCoef"), c.i32_const(2*f2size)); + const x1 = c.getLocal("p1"); + const y1 = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); + f.addCode(c.setLocal("z1", c.i32_add(c.getLocal("p1"), c.i32_const(n8*2)))); + const x2 = c.getLocal("p2"); + const y2 = c.i32_add(c.getLocal("p2"), c.i32_const(n8)); + const x3 = c.getLocal("pr"); + const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); + const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2)); - const A = c.i32_const(module.alloc(f2size)); - const B = c.i32_const(module.alloc(f2size)); - const C = c.i32_const(module.alloc(f2size)); - const D = c.i32_const(module.alloc(f2size)); - const E = c.i32_const(module.alloc(f2size)); - const F = c.i32_const(module.alloc(f2size)); - const G = c.i32_const(module.alloc(f2size)); - const H = c.i32_const(module.alloc(f2size)); - const I = c.i32_const(module.alloc(f2size)); - const J = c.i32_const(module.alloc(f2size)); - const E2 = c.i32_const(module.alloc(f2size)); - const AUX = c.i32_const(module.alloc(f2size)); + const H = c.i32_const(module.alloc(n8)); + const HH = c.i32_const(module.alloc(n8)); + const y2_minus_y1 = c.i32_const(module.alloc(n8)); + const I = c.i32_const(module.alloc(n8)); + const J = c.i32_const(module.alloc(n8)); + const r = c.i32_const(module.alloc(n8)); + const r2 = c.i32_const(module.alloc(n8)); + const V = c.i32_const(module.alloc(n8)); + const V2 = c.i32_const(module.alloc(n8)); + const y1_J2 = c.i32_const(module.alloc(n8)); f.addCode( + c.if( + c.call(prefix + "_isZeroAffine", c.getLocal("p1")), + [ + ...c.call(prefix + "_copyAffine", c.getLocal("p2"), c.getLocal("pr")), + ...c.call(prefixField + "_one", c.i32_add(c.getLocal("pr") , c.i32_const(n8*2))), + ...c.ret([]) + ] + ), + c.if( + c.call(prefix + "_isZeroAffine", c.getLocal("p2")), + [ + ...c.call(prefix + "_copyAffine", c.getLocal("p1"), c.getLocal("pr")), + ...c.call(prefixField + "_one", c.i32_add(c.getLocal("pr") , c.i32_const(n8*2))), + ...c.ret([]) + ] + ), - // A = X1 * Y1 / 2 - c.call(f2mPrefix + "_mul", Y1, c.i32_const(pTwoInv), A), - c.call(f2mPrefix + "_mul", X1, A, A), - - // B = Y1^2 - c.call(f2mPrefix + "_square", Y1, B), - - // C = Z1^2 - c.call(f2mPrefix + "_square", Z1, C), - - // D = 3 * C - c.call(f2mPrefix + "_add", C, C, D), - c.call(f2mPrefix + "_add", D, C, D), - - // E = twist_b * D - c.call(f2mPrefix + "_mul", c.i32_const(pTwistCoefB), D, E), - - // F = 3 * E - c.call(f2mPrefix + "_add", E, E, F), - c.call(f2mPrefix + "_add", E, F, F), - - // G = (B+F)/2 - c.call(f2mPrefix + "_add", B, F, G), - c.call(f2mPrefix + "_mul", G, c.i32_const(pTwoInv), G), - - // H = (Y1+Z1)^2-(B+C) - c.call(f2mPrefix + "_add", B, C, AUX), - c.call(f2mPrefix + "_add", Y1, Z1, H), - c.call(f2mPrefix + "_square", H, H), - c.call(f2mPrefix + "_sub", H, AUX, H), - - // I = E-B - c.call(f2mPrefix + "_sub", E, B, I), - - // J = X1^2 - c.call(f2mPrefix + "_square", X1, J), - - // E_squared = E^2 - c.call(f2mPrefix + "_square", E, E2), - - // X3 (X1) = A * (B-F) - c.call(f2mPrefix + "_sub", B, F, AUX), - c.call(f2mPrefix + "_mul", A, AUX, X1), - // Y3 (Y1) = G^2 - 3*E^2 - c.call(f2mPrefix + "_add", E2, E2, AUX), - c.call(f2mPrefix + "_add", E2, AUX, AUX), - c.call(f2mPrefix + "_square", G, Y1), - c.call(f2mPrefix + "_sub", Y1, AUX, Y1), + c.if( + c.call(prefixField + "_eq", x1, x2), + c.if( + c.call(prefixField + "_eq", y1, y2), + [ + ...c.call(prefix + "_doubleAffine", c.getLocal("p2"), c.getLocal("pr")), + ...c.ret([]) + ] + ) + ), - // Z3 (Z1) = B * H - c.call(f2mPrefix + "_mul", B, H, Z1), + c.call(prefixField + "_sub", x2, x1, H), + c.call(prefixField + "_sub", y2, y1, y2_minus_y1), + c.call(prefixField + "_square", H, HH), + c.call(prefixField + "_add", HH , HH, I), + c.call(prefixField + "_add", I , I, I), + c.call(prefixField + "_mul", H, I, J), + c.call(prefixField + "_add", y2_minus_y1, y2_minus_y1, r), + c.call(prefixField + "_mul", x1, I, V), + c.call(prefixField + "_square", r, r2), + c.call(prefixField + "_add", V, V, V2), - // ell_0 = xi * I - c.call(f2mPrefix + "_mul", c.i32_const(pAltBn128Twist), I, ELL_0), + c.call(prefixField + "_sub", r2, J, x3), + c.call(prefixField + "_sub", x3, V2, x3), - // ell_VW = - H (later: * yP) - c.call(f2mPrefix + "_neg", H, ELL_VW), + c.call(prefixField + "_mul", y1, J, y1_J2), + c.call(prefixField + "_add", y1_J2, y1_J2, y1_J2), - // ell_VV = 3*J (later: * xP) - c.call(f2mPrefix + "_add", J, J, ELL_VV), - c.call(f2mPrefix + "_add", J, ELL_VV, ELL_VV), + c.call(prefixField + "_sub", V, x3, y3), + c.call(prefixField + "_mul", y3, r, y3), + c.call(prefixField + "_sub", y3, y1_J2, y3), + c.call(prefixField + "_add", H, H, z3), ); } - function buildMulByQ() { - const f = module.addFunction(prefix + "_mulByQ"); + function buildNeg() { + const f = module.addFunction(prefix + "_neg"); f.addParam("p1", "i32"); f.addParam("pr", "i32"); const c = f.getCodeBuilder(); const x = c.getLocal("p1"); - const y = c.i32_add(c.getLocal("p1"), c.i32_const(f2size)); - const z = c.i32_add(c.getLocal("p1"), c.i32_const(f2size*2)); + const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); + const z = c.i32_add(c.getLocal("p1"), c.i32_const(n8*2)); const x3 = c.getLocal("pr"); - const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(f2size)); - const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(f2size*2)); - - const MulByQX = c.i32_const(module.alloc([ - ...utils$2.bigInt2BytesLE( toMontgomery("21575463638280843010398324269430826099269044274347216827212613867836435027261"), f1size ), - ...utils$2.bigInt2BytesLE( toMontgomery("10307601595873709700152284273816112264069230130616436755625194854815875713954"), f1size ), - ])); - - const MulByQY = c.i32_const(module.alloc([ - ...utils$2.bigInt2BytesLE( toMontgomery("2821565182194536844548159561693502659359617185244120367078079554186484126554"), f1size ), - ...utils$2.bigInt2BytesLE( toMontgomery("3505843767911556378687030309984248845540243509899259641013678093033130930403"), f1size ), - ])); + const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); + const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2)); f.addCode( - // The frobeniusMap(1) in this field, is the conjugate - c.call(f2mPrefix + "_conjugate", x, x3), - c.call(f2mPrefix + "_mul", MulByQX, x3, x3), - c.call(f2mPrefix + "_conjugate", y, y3), - c.call(f2mPrefix + "_mul", MulByQY, y3, y3), - c.call(f2mPrefix + "_conjugate", z, z3), + c.call(prefixField + "_copy", x, x3), + c.call(prefixField + "_neg", y, y3), + c.call(prefixField + "_copy", z, z3) ); } - function buildPrepareG2() { - buildMulByQ(); - const f = module.addFunction(prefix+ "_prepareG2"); - f.addParam("pQ", "i32"); - f.addParam("ppreQ", "i32"); - f.addLocal("pCoef", "i32"); - f.addLocal("i", "i32"); + function buildNegAffine() { + const f = module.addFunction(prefix + "_negAffine"); + f.addParam("p1", "i32"); + f.addParam("pr", "i32"); const c = f.getCodeBuilder(); - const QX = c.getLocal("pQ"); - - const pR = module.alloc(f2size*3); - const R = c.i32_const(pR); - const RX = c.i32_const(pR); - const RY = c.i32_const(pR+f2size); - const RZ = c.i32_const(pR+2*f2size); - - const cQX = c.i32_add( c.getLocal("ppreQ"), c.i32_const(0)); - const cQY = c.i32_add( c.getLocal("ppreQ"), c.i32_const(f2size)); - - const pQ1 = module.alloc(f2size*3); - const Q1 = c.i32_const(pQ1); - - const pQ2 = module.alloc(f2size*3); - const Q2 = c.i32_const(pQ2); - const Q2Y = c.i32_const(pQ2 + f2size); + const x = c.getLocal("p1"); + const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); + const x3 = c.getLocal("pr"); + const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); f.addCode( - c.call(g2mPrefix + "_normalize", QX, cQX), // TODO Remove if already in affine - c.call(f2mPrefix + "_copy", cQX, RX), - c.call(f2mPrefix + "_copy", cQY, RY), - c.call(f2mPrefix + "_one", RZ), + c.call(prefixField + "_copy", x, x3), + c.call(prefixField + "_neg", y, y3), ); + } - f.addCode( - c.setLocal("pCoef", c.i32_add( c.getLocal("ppreQ"), c.i32_const(f2size*3))), - c.setLocal("i", c.i32_const(ateLoopBitBytes.length-2)), - c.block(c.loop( - - c.call(prefix + "_prepDblStep", R, c.getLocal("pCoef")), - c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), - c.if( - c.i32_load8_s(c.getLocal("i"), pAteLoopBitBytes), - [ - ...c.call(prefix + "_prepAddStep", cQX, R, c.getLocal("pCoef")), - ...c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), - ] - ), - c.br_if(1, c.i32_eqz ( c.getLocal("i") )), - c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) - ); + function buildSub() { + const f = module.addFunction(prefix + "_sub"); + f.addParam("p1", "i32"); + f.addParam("p2", "i32"); + f.addParam("pr", "i32"); + + const c = f.getCodeBuilder(); + + const AUX = c.i32_const(module.alloc(n8*3)); f.addCode( - c.call(prefix + "_mulByQ", cQX, Q1), - c.call(prefix + "_mulByQ", Q1, Q2) + c.call(prefix + "_neg", c.getLocal("p2"), AUX), + c.call(prefix + "_add", c.getLocal("p1"), AUX, c.getLocal("pr")), ); + } - f.addCode( - c.call(f2mPrefix + "_neg", Q2Y, Q2Y), + function buildSubMixed() { + const f = module.addFunction(prefix + "_subMixed"); + f.addParam("p1", "i32"); + f.addParam("p2", "i32"); + f.addParam("pr", "i32"); - c.call(prefix + "_prepAddStep", Q1, R, c.getLocal("pCoef")), - c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + const c = f.getCodeBuilder(); - c.call(prefix + "_prepAddStep", Q2, R, c.getLocal("pCoef")), - c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + const AUX = c.i32_const(module.alloc(n8*3)); + + f.addCode( + c.call(prefix + "_negAffine", c.getLocal("p2"), AUX), + c.call(prefix + "_addMixed", c.getLocal("p1"), AUX, c.getLocal("pr")), ); } - function buildMulBy024Old() { - const f = module.addFunction(prefix+ "__mulBy024Old"); - f.addParam("pEll0", "i32"); - f.addParam("pEllVW", "i32"); - f.addParam("pEllVV", "i32"); - f.addParam("pR", "i32"); // Result in F12 - - const c = f.getCodeBuilder(); - const x0 = c.getLocal("pEll0"); - const x2 = c.getLocal("pEllVV"); - const x4 = c.getLocal("pEllVW"); + function buildSubAffine() { + const f = module.addFunction(prefix + "_subAffine"); + f.addParam("p1", "i32"); + f.addParam("p2", "i32"); + f.addParam("pr", "i32"); - const z0 = c.getLocal("pR"); + const c = f.getCodeBuilder(); - const pAUX12 = module.alloc(ftsize); - const AUX12 = c.i32_const(pAUX12); - const AUX12_0 = c.i32_const(pAUX12); - const AUX12_2 = c.i32_const(pAUX12+f2size); - const AUX12_4 = c.i32_const(pAUX12+f2size*2); - const AUX12_6 = c.i32_const(pAUX12+f2size*3); - const AUX12_8 = c.i32_const(pAUX12+f2size*4); - const AUX12_10 = c.i32_const(pAUX12+f2size*5); + const AUX = c.i32_const(module.alloc(n8*3)); f.addCode( - - c.call(f2mPrefix + "_copy", x0, AUX12_0), - c.call(f2mPrefix + "_zero", AUX12_2), - c.call(f2mPrefix + "_copy", x2, AUX12_4), - c.call(f2mPrefix + "_zero", AUX12_6), - c.call(f2mPrefix + "_copy", x4, AUX12_8), - c.call(f2mPrefix + "_zero", AUX12_10), - c.call(ftmPrefix + "_mul", AUX12, z0, z0), + c.call(prefix + "_negAffine", c.getLocal("p2"), AUX), + c.call(prefix + "_addAffine", c.getLocal("p1"), AUX, c.getLocal("pr")), ); } - function buildMulBy024() { - const f = module.addFunction(prefix+ "__mulBy024"); - f.addParam("pEll0", "i32"); - f.addParam("pEllVW", "i32"); - f.addParam("pEllVV", "i32"); - f.addParam("pR", "i32"); // Result in F12 + // This sets Z to One + function buildNormalize() { + const f = module.addFunction(prefix + "_normalize"); + f.addParam("p1", "i32"); + f.addParam("pr", "i32"); const c = f.getCodeBuilder(); - const x0 = c.getLocal("pEll0"); - const x2 = c.getLocal("pEllVV"); - const x4 = c.getLocal("pEllVW"); + const x = c.getLocal("p1"); + const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); + const z = c.i32_add(c.getLocal("p1"), c.i32_const(n8*2)); + const x3 = c.getLocal("pr"); + const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); + const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2)); - const z0 = c.getLocal("pR"); - const z1 = c.i32_add(c.getLocal("pR"), c.i32_const(2*n8)); - const z2 = c.i32_add(c.getLocal("pR"), c.i32_const(4*n8)); - const z3 = c.i32_add(c.getLocal("pR"), c.i32_const(6*n8)); - const z4 = c.i32_add(c.getLocal("pR"), c.i32_const(8*n8)); - const z5 = c.i32_add(c.getLocal("pR"), c.i32_const(10*n8)); - const t0 = c.i32_const(module.alloc(f2size)); - const t1 = c.i32_const(module.alloc(f2size)); - const t2 = c.i32_const(module.alloc(f2size)); - const s0 = c.i32_const(module.alloc(f2size)); - const T3 = c.i32_const(module.alloc(f2size)); - const T4 = c.i32_const(module.alloc(f2size)); - const D0 = c.i32_const(module.alloc(f2size)); - const D2 = c.i32_const(module.alloc(f2size)); - const D4 = c.i32_const(module.alloc(f2size)); - const S1 = c.i32_const(module.alloc(f2size)); - const AUX = c.i32_const(module.alloc(f2size)); + const Z_inv = c.i32_const(module.alloc(n8)); + const Z2_inv = c.i32_const(module.alloc(n8)); + const Z3_inv = c.i32_const(module.alloc(n8)); f.addCode( + c.if( + c.call(prefix + "_isZero", c.getLocal("p1")), + c.call(prefix + "_zero", c.getLocal("pr")), + [ + ...c.call(prefixField + "_inverse", z, Z_inv), + ...c.call(prefixField + "_square", Z_inv, Z2_inv), + ...c.call(prefixField + "_mul", Z_inv, Z2_inv, Z3_inv), + ...c.call(prefixField + "_mul", x, Z2_inv, x3), + ...c.call(prefixField + "_mul", y, Z3_inv, y3), + ...c.call(prefixField + "_one", z3), + ] + ) + ); + } - // D0 = z0 * x0; - c.call(f2mPrefix + "_mul", z0, x0, D0), - // D2 = z2 * x2; - c.call(f2mPrefix + "_mul", z2, x2, D2), - // D4 = z4 * x4; - c.call(f2mPrefix + "_mul", z4, x4, D4), - // t2 = z0 + z4; - c.call(f2mPrefix + "_add", z0, z4, t2), - // t1 = z0 + z2; - c.call(f2mPrefix + "_add", z0, z2, t1), - // s0 = z1 + z3 + z5; - c.call(f2mPrefix + "_add", z1, z3, s0), - c.call(f2mPrefix + "_add", s0, z5, s0), + // Does not set Z. + function buildToAffine() { + const f = module.addFunction(prefix + "_toAffine"); + f.addParam("p1", "i32"); + f.addParam("pr", "i32"); - // For z.a_.a_ = z0. - // S1 = z1 * x2; - c.call(f2mPrefix + "_mul", z1, x2, S1), - // T3 = S1 + D4; - c.call(f2mPrefix + "_add", S1, D4, T3), - // T4 = my_Fp6::non_residue * T3 + D0; - c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), T3, T4), - c.call(f2mPrefix + "_add", T4, D0, z0), - // z0 = T4; + const c = f.getCodeBuilder(); - // For z.a_.b_ = z1 - // T3 = z5 * x4; - c.call(f2mPrefix + "_mul", z5, x4, T3), - // S1 = S1 + T3; - c.call(f2mPrefix + "_add", S1, T3, S1), - // T3 = T3 + D2; - c.call(f2mPrefix + "_add", T3, D2, T3), - // T4 = my_Fp6::non_residue * T3; - c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), T3, T4), - // T3 = z1 * x0; - c.call(f2mPrefix + "_mul", z1, x0, T3), - // S1 = S1 + T3; - c.call(f2mPrefix + "_add", S1, T3, S1), - // T4 = T4 + T3; - c.call(f2mPrefix + "_add", T4, T3, z1), - // z1 = T4; + const x = c.getLocal("p1"); + const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); + const z = c.i32_add(c.getLocal("p1"), c.i32_const(n8*2)); + const x3 = c.getLocal("pr"); + const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); + const Z_inv = c.i32_const(module.alloc(n8)); + const Z2_inv = c.i32_const(module.alloc(n8)); + const Z3_inv = c.i32_const(module.alloc(n8)); - // For z.a_.c_ = z2 - // t0 = x0 + x2; - c.call(f2mPrefix + "_add", x0, x2, t0), - // T3 = t1 * t0 - D0 - D2; - c.call(f2mPrefix + "_mul", t1, t0, T3), - c.call(f2mPrefix + "_add", D0, D2, AUX), - c.call(f2mPrefix + "_sub", T3, AUX, T3), - // T4 = z3 * x4; - c.call(f2mPrefix + "_mul", z3, x4, T4), - // S1 = S1 + T4; - c.call(f2mPrefix + "_add", S1, T4, S1), + f.addCode( + c.if( + c.call(prefix + "_isZero", c.getLocal("p1")), + [ + ...c.call(prefixField + "_zero", x3), + ...c.call(prefixField + "_zero", y3), + ], + [ + ...c.call(prefixField + "_inverse", z, Z_inv), + ...c.call(prefixField + "_square", Z_inv, Z2_inv), + ...c.call(prefixField + "_mul", Z_inv, Z2_inv, Z3_inv), + ...c.call(prefixField + "_mul", x, Z2_inv, x3), + ...c.call(prefixField + "_mul", y, Z3_inv, y3), + ] + ) + ); + } - // For z.b_.a_ = z3 (z3 needs z2) - // t0 = z2 + z4; - c.call(f2mPrefix + "_add", z2, z4, t0), - // T3 = T3 + T4; - // z2 = T3; - c.call(f2mPrefix + "_add", T3, T4, z2), - // t1 = x2 + x4; - c.call(f2mPrefix + "_add", x2, x4, t1), - // T3 = t0 * t1 - D2 - D4; - c.call(f2mPrefix + "_mul", t1, t0, T3), - c.call(f2mPrefix + "_add", D2, D4, AUX), - c.call(f2mPrefix + "_sub", T3, AUX, T3), - // T4 = my_Fp6::non_residue * T3; - c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), T3, T4), - // T3 = z3 * x0; - c.call(f2mPrefix + "_mul", z3, x0, T3), - // S1 = S1 + T3; - c.call(f2mPrefix + "_add", S1, T3, S1), - // T4 = T4 + T3; - c.call(f2mPrefix + "_add", T4, T3, z3), - // z3 = T4; + function buildToJacobian() { + const f = module.addFunction(prefix + "_toJacobian"); + f.addParam("p1", "i32"); + f.addParam("pr", "i32"); - // For z.b_.b_ = z4 - // T3 = z5 * x2; - c.call(f2mPrefix + "_mul", z5, x2, T3), - // S1 = S1 + T3; - c.call(f2mPrefix + "_add", S1, T3, S1), - // T4 = my_Fp6::non_residue * T3; - c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), T3, T4), - // t0 = x0 + x4; - c.call(f2mPrefix + "_add", x0, x4, t0), - // T3 = t2 * t0 - D0 - D4; - c.call(f2mPrefix + "_mul", t2, t0, T3), - c.call(f2mPrefix + "_add", D0, D4, AUX), - c.call(f2mPrefix + "_sub", T3, AUX, T3), - // T4 = T4 + T3; - c.call(f2mPrefix + "_add", T4, T3, z4), - // z4 = T4; + const c = f.getCodeBuilder(); - // For z.b_.c_ = z5. - // t0 = x0 + x2 + x4; - c.call(f2mPrefix + "_add", x0, x2, t0), - c.call(f2mPrefix + "_add", t0, x4, t0), - // T3 = s0 * t0 - S1; - c.call(f2mPrefix + "_mul", s0, t0, T3), - c.call(f2mPrefix + "_sub", T3, S1, z5), - // z5 = T3; + const x = c.getLocal("p1"); + const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8)); + const x3 = c.getLocal("pr"); + const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8)); + const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2)); + f.addCode( + c.if( + c.call(prefix + "_isZeroAffine", c.getLocal("p1")), + c.call(prefix + "_zero", c.getLocal("pr")), + [ + ...c.call(prefixField + "_one", z3), + ...c.call(prefixField + "_copy", y, y3), + ...c.call(prefixField + "_copy", x, x3) + ] + ) ); } - - function buildMillerLoop() { - const f = module.addFunction(prefix+ "_millerLoop"); - f.addParam("ppreP", "i32"); - f.addParam("ppreQ", "i32"); - f.addParam("r", "i32"); - f.addLocal("pCoef", "i32"); + function buildBatchToAffine() { + const f = module.addFunction(prefix + "_batchToAffine"); + f.addParam("pIn", "i32"); + f.addParam("n", "i32"); + f.addParam("pOut", "i32"); + f.addLocal("pAux", "i32"); + f.addLocal("itIn", "i32"); + f.addLocal("itAux", "i32"); + f.addLocal("itOut", "i32"); f.addLocal("i", "i32"); const c = f.getCodeBuilder(); - const preP_PX = c.getLocal("ppreP"); - const preP_PY = c.i32_add(c.getLocal("ppreP"), c.i32_const(f1size)); - - const ELL_0 = c.getLocal("pCoef"); - const ELL_VW = c.i32_add(c.getLocal("pCoef"), c.i32_const(f2size)); - const ELL_VV = c.i32_add(c.getLocal("pCoef"), c.i32_const(2*f2size)); - - - const pVW = module.alloc(f2size); - const VW = c.i32_const(pVW); - const pVV = module.alloc(f2size); - const VV = c.i32_const(pVV); - - const F = c.getLocal("r"); - + const tmp = c.i32_const(module.alloc(n8)); f.addCode( - c.call(ftmPrefix + "_one", F), + c.setLocal("pAux", c.i32_load( c.i32_const(0) )), + c.i32_store( + c.i32_const(0), + c.i32_add( + c.getLocal("pAux"), + c.i32_mul(c.getLocal("n"), c.i32_const(n8)) + ) + ), - c.setLocal("pCoef", c.i32_add( c.getLocal("ppreQ"), c.i32_const(f2size*3))), + c.call( + prefixField + "_batchInverse", + c.i32_add(c.getLocal("pIn"), c.i32_const(n8*2)), + c.i32_const(n8*3), + c.getLocal("n"), + c.getLocal("pAux"), + c.i32_const(n8) + ), - c.setLocal("i", c.i32_const(ateLoopBitBytes.length-2)), + c.setLocal("itIn", c.getLocal("pIn")), + c.setLocal("itAux", c.getLocal("pAux")), + c.setLocal("itOut", c.getLocal("pOut")), + c.setLocal("i", c.i32_const(0)), c.block(c.loop( - - - c.call(ftmPrefix + "_square", F, F), - - c.call(f2mPrefix + "_mul1", ELL_VW,preP_PY, VW), - c.call(f2mPrefix + "_mul1", ELL_VV, preP_PX, VV), - c.call(prefix + "__mulBy024", ELL_0, VW, VV, F), - c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + c.br_if(1, c.i32_eq ( c.getLocal("i"), c.getLocal("n") )), c.if( - c.i32_load8_s(c.getLocal("i"), pAteLoopBitBytes), + c.call(prefixField + "_isZero", c.getLocal("itAux")), [ - ...c.call(f2mPrefix + "_mul1", ELL_VW, preP_PY, VW), - ...c.call(f2mPrefix + "_mul1", ELL_VV, preP_PX, VV), - - ...c.call(prefix + "__mulBy024", ELL_0, VW, VV, F), - ...c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), - + ...c.call(prefixField + "_zero", c.getLocal("itOut")), + ...c.call(prefixField + "_zero", c.i32_add(c.getLocal("itOut"), c.i32_const(n8))) + ], + [ + ...c.call( + prefixField+"_mul", + c.getLocal("itAux"), + c.i32_add(c.getLocal("itIn"), c.i32_const(n8)), + tmp, + ), + ...c.call( + prefixField+"_square", + c.getLocal("itAux"), + c.getLocal("itAux") + ), + ...c.call( + prefixField+"_mul", + c.getLocal("itAux"), + c.getLocal("itIn"), + c.getLocal("itOut"), + ), + ...c.call( + prefixField+"_mul", + c.getLocal("itAux"), + tmp, + c.i32_add(c.getLocal("itOut"), c.i32_const(n8)), + ), ] ), - c.br_if(1, c.i32_eqz ( c.getLocal("i") )), - c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) - - ); - - f.addCode( - c.call(f2mPrefix + "_mul1", ELL_VW, preP_PY, VW), - c.call(f2mPrefix + "_mul1", ELL_VV, preP_PX, VV), - c.call(prefix + "__mulBy024", ELL_0, VW, VV, F), - c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), - - c.call(f2mPrefix + "_mul1", ELL_VW, preP_PY, VW), - c.call(f2mPrefix + "_mul1", ELL_VV, preP_PX, VV), - c.call(prefix + "__mulBy024", ELL_0, VW, VV, F), - c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + c.setLocal("itIn", c.i32_add(c.getLocal("itIn"), c.i32_const(n8*3))), + c.setLocal("itOut", c.i32_add(c.getLocal("itOut"), c.i32_const(n8*2))), + c.setLocal("itAux", c.i32_add(c.getLocal("itAux"), c.i32_const(n8))), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )), + c.i32_store( + c.i32_const(0), + c.getLocal("pAux") + ) ); - } - function buildFrobeniusMap(n) { - const F12 = [ - [ - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - ], - [ - [1n, 0n], - [8376118865763821496583973867626364092589906065868298776909617916018768340080n, 16469823323077808223889137241176536799009286646108169935659301613961712198316n], - [21888242871839275220042445260109153167277707414472061641714758635765020556617n, 0n], - [11697423496358154304825782922584725312912383441159505038794027105778954184319n, 303847389135065887422783454877609941456349188919719272345083954437860409601n], - [21888242871839275220042445260109153167277707414472061641714758635765020556616n, 0n], - [3321304630594332808241809054958361220322477375291206261884409189760185844239n, 5722266937896532885780051958958348231143373700109372999374820235121374419868n], - [21888242871839275222246405745257275088696311157297823662689037894645226208582n, 0n], - [13512124006075453725662431877630910996106405091429524885779419978626457868503n, 5418419548761466998357268504080738289687024511189653727029736280683514010267n], - [2203960485148121921418603742825762020974279258880205651966n, 0n], - [10190819375481120917420622822672549775783927716138318623895010788866272024264n, 21584395482704209334823622290379665147239961968378104390343953940207365798982n], - [2203960485148121921418603742825762020974279258880205651967n, 0n], - [18566938241244942414004596690298913868373833782006617400804628704885040364344n, 16165975933942742336466353786298926857552937457188450663314217659523851788715n], - ] - ]; - - const F6 = [ - [ - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - ], - [ - [1n, 0n], - [21575463638280843010398324269430826099269044274347216827212613867836435027261n, 10307601595873709700152284273816112264069230130616436755625194854815875713954n], - [21888242871839275220042445260109153167277707414472061641714758635765020556616n, 0n], - [3772000881919853776433695186713858239009073593817195771773381919316419345261n, 2236595495967245188281701248203181795121068902605861227855261137820944008926n], - [2203960485148121921418603742825762020974279258880205651966n, 0n], - [18429021223477853657660792034369865839114504446431234726392080002137598044644n, 9344045779998320333812420223237981029506012124075525679208581902008406485703n], - ], - [ - [1n, 0n], - [2581911344467009335267311115468803099551665605076196740867805258568234346338n, 19937756971775647987995932169929341994314640652964949448313374472400716661030n], - [2203960485148121921418603742825762020974279258880205651966n, 0n], - [5324479202449903542726783395506214481928257762400643279780343368557297135718n, 16208900380737693084919495127334387981393726419856888799917914180988844123039n], - [21888242871839275220042445260109153167277707414472061641714758635765020556616n, 0n], - [13981852324922362344252311234282257507216387789820983642040889267519694726527n, 7629828391165209371577384193250820201684255241773809077146787135900891633097n], - ] - ]; - - const f = module.addFunction(prefix+ "__frobeniusMap"+n); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + // This function is private and does not allow to OVERLAP buffers. + function buildReverseBytes() { + const f = module.addFunction(prefix + "__reverseBytes"); + f.addParam("pIn", "i32"); + f.addParam("n", "i32"); + f.addParam("pOut", "i32"); + f.addLocal("itOut", "i32"); + f.addLocal("itIn", "i32"); const c = f.getCodeBuilder(); - for (let i=0; i<6; i++) { - const X = (i==0) ? c.getLocal("x") : c.i32_add(c.getLocal("x"), c.i32_const(i*f2size)); - const Xc0 = X; - const Xc1 = c.i32_add(c.getLocal("x"), c.i32_const(i*f2size + f1size)); - const R = (i==0) ? c.getLocal("r") : c.i32_add(c.getLocal("r"), c.i32_const(i*f2size)); - const Rc0 = R; - const Rc1 = c.i32_add(c.getLocal("r"), c.i32_const(i*f2size + f1size)); - const coef = mul2(F12[Math.floor(i/3)][n%12] , F6[i%3][n%6]); - const pCoef = module.alloc([ - ...utils$2.bigInt2BytesLE(toMontgomery(coef[0]), 32), - ...utils$2.bigInt2BytesLE(toMontgomery(coef[1]), 32), - ]); - if (n%2 == 1) { - f.addCode( - c.call(f1mPrefix + "_copy", Xc0, Rc0), - c.call(f1mPrefix + "_neg", Xc1, Rc1), - c.call(f2mPrefix + "_mul", R, c.i32_const(pCoef), R), - ); - } else { - f.addCode(c.call(f2mPrefix + "_mul", X, c.i32_const(pCoef), R)); - } - } - - function mul2(a, b) { - const ac0 = BigInt(a[0]); - const ac1 = BigInt(a[1]); - const bc0 = BigInt(b[0]); - const bc1 = BigInt(b[1]); - const res = [ - (ac0 * bc0 - ( ac1 * bc1) ) % q, - (ac0 * bc1 + ( ac1 * bc0) ) % q, - ]; - if (isNegative$2(res[0])) res[0] = res[0] + q; - return res; - } + f.addCode( + c.setLocal( + "itOut", + c.i32_sub( + c.i32_add( + c.getLocal("pOut"), + c.getLocal("n") + ), + c.i32_const(1) + ) + ), + c.setLocal( + "itIn", + c.getLocal("pIn") + ), + c.block(c.loop( + c.br_if(1, c.i32_lt_s( c.getLocal("itOut"), c.getLocal("pOut") )), + c.i32_store8( + c.getLocal("itOut"), + c.i32_load8_u(c.getLocal("itIn")), + ), + c.setLocal("itOut", c.i32_sub(c.getLocal("itOut"), c.i32_const(1))), + c.setLocal("itIn", c.i32_add(c.getLocal("itIn"), c.i32_const(1))), + c.br(0) + )), + ); } + function buildLEMtoC() { + const f = module.addFunction(prefix + "_LEMtoC"); + f.addParam("pIn", "i32"); + f.addParam("pOut", "i32"); + const c = f.getCodeBuilder(); - function buildFinalExponentiationFirstChunk() { + const tmp = c.i32_const(module.alloc(n8)); - const f = module.addFunction(prefix+ "__finalExponentiationFirstChunk"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + f.addCode( + c.if( + c.call(prefix + "_isZeroAffine", c.getLocal("pIn")), + [ + ...c.call(prefixField + "_zero", c.getLocal("pOut")), + ...c.i32_store8( + c.getLocal("pOut"), + c.i32_const(0x40) + ), + ...c.ret([]) + ] + ), + c.call(prefixField + "_fromMontgomery", c.getLocal("pIn"), tmp), + c.call(prefix + "__reverseBytes", tmp, c.i32_const(n8), c.getLocal("pOut")), + c.if( + c.i32_eq( + c.call(prefixField + "_sign", c.i32_add(c.getLocal("pIn"), c.i32_const(n8))), + c.i32_const(-1) + ), + c.i32_store8( + c.getLocal("pOut"), + c.i32_or( + c.i32_load8_u(c.getLocal("pOut")), + c.i32_const(0x80) + ) + ) + ), + ); + } + + function buildLEMtoU() { + const f = module.addFunction(prefix + "_LEMtoU"); + f.addParam("pIn", "i32"); + f.addParam("pOut", "i32"); const c = f.getCodeBuilder(); - const elt = c.getLocal("x"); - const eltC0 = elt; - const eltC1 = c.i32_add(elt, c.i32_const(n8*6)); - const r = c.getLocal("r"); - const pA = module.alloc(ftsize); - const A = c.i32_const(pA); - const Ac0 = A; - const Ac1 = c.i32_const(pA + n8*6); - const B = c.i32_const(module.alloc(ftsize)); - const C = c.i32_const(module.alloc(ftsize)); - const D = c.i32_const(module.alloc(ftsize)); + const pTmp = module.alloc(n8*2); + const tmp = c.i32_const(pTmp); + const tmpX = c.i32_const(pTmp); + const tmpY = c.i32_const(pTmp + n8); f.addCode( - // const alt_bn128_Fq12 A = alt_bn128_Fq12(elt.c0,-elt.c1); - c.call(f6mPrefix + "_copy", eltC0, Ac0), - c.call(f6mPrefix + "_neg", eltC1, Ac1), + c.if( + c.call(prefix + "_isZeroAffine", c.getLocal("pIn")), + [ + ...c.call(prefix + "_zeroAffine", c.getLocal("pOut")), + ...c.ret([]) + ] + ), - // const alt_bn128_Fq12 B = elt.inverse(); - c.call(ftmPrefix + "_inverse", elt, B), + c.call(prefix + "_fromMontgomeryAffine", c.getLocal("pIn"), tmp), - // const alt_bn128_Fq12 C = A * B; - c.call(ftmPrefix + "_mul", A, B, C), - // const alt_bn128_Fq12 D = C.Frobenius_map(2); - c.call(prefix + "__frobeniusMap2", C, D), - // const alt_bn128_Fq12 result = D * C; - c.call(ftmPrefix + "_mul", C, D, r), + c.call(prefix + "__reverseBytes", tmpX, c.i32_const(n8), c.getLocal("pOut")), + c.call(prefix + "__reverseBytes", tmpY, c.i32_const(n8), c.i32_add(c.getLocal("pOut"), c.i32_const(n8))), ); } - function buildCyclotomicSquare() { - const f = module.addFunction(prefix+ "__cyclotomicSquare"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + function buildUtoLEM() { + const f = module.addFunction(prefix + "_UtoLEM"); + f.addParam("pIn", "i32"); + f.addParam("pOut", "i32"); const c = f.getCodeBuilder(); - const x0 = c.getLocal("x"); - const x4 = c.i32_add(c.getLocal("x"), c.i32_const(f2size)); - const x3 = c.i32_add(c.getLocal("x"), c.i32_const(2*f2size)); - const x2 = c.i32_add(c.getLocal("x"), c.i32_const(3*f2size)); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(4*f2size)); - const x5 = c.i32_add(c.getLocal("x"), c.i32_const(5*f2size)); + const pTmp = module.alloc(n8*2); + const tmp = c.i32_const(pTmp); + const tmpX = c.i32_const(pTmp); + const tmpY = c.i32_const(pTmp + n8); - const r0 = c.getLocal("r"); - const r4 = c.i32_add(c.getLocal("r"), c.i32_const(f2size)); - const r3 = c.i32_add(c.getLocal("r"), c.i32_const(2*f2size)); - const r2 = c.i32_add(c.getLocal("r"), c.i32_const(3*f2size)); - const r1 = c.i32_add(c.getLocal("r"), c.i32_const(4*f2size)); - const r5 = c.i32_add(c.getLocal("r"), c.i32_const(5*f2size)); + f.addCode( + c.if( + c.i32_and(c.i32_load8_u(c.getLocal("pIn")), c.i32_const(0x40)), + [ + ...c.call(prefix + "_zeroAffine", c.getLocal("pOut")), + ...c.ret([]) + ] + ), + c.call(prefix + "__reverseBytes", c.getLocal("pIn"), c.i32_const(n8), tmpX), + c.call(prefix + "__reverseBytes", c.i32_add(c.getLocal("pIn"), c.i32_const(n8)), c.i32_const(n8), tmpY), + c.call(prefix + "_toMontgomeryAffine", tmp, c.getLocal("pOut")) + ); + } - const t0 = c.i32_const(module.alloc(f2size)); - const t1 = c.i32_const(module.alloc(f2size)); - const t2 = c.i32_const(module.alloc(f2size)); - const t3 = c.i32_const(module.alloc(f2size)); - const t4 = c.i32_const(module.alloc(f2size)); - const t5 = c.i32_const(module.alloc(f2size)); - const tmp = c.i32_const(module.alloc(f2size)); - const AUX = c.i32_const(module.alloc(f2size)); + function buildCtoLEM() { + const f = module.addFunction(prefix + "_CtoLEM"); + f.addParam("pIn", "i32"); + f.addParam("pOut", "i32"); + f.addLocal("firstByte", "i32"); + f.addLocal("greatest", "i32"); + const c = f.getCodeBuilder(); - f.addCode( - // // t0 + t1*y = (z0 + z1*y)^2 = a^2 - // tmp = z0 * z1; - // t0 = (z0 + z1) * (z0 + my_Fp6::non_residue * z1) - tmp - my_Fp6::non_residue * tmp; - // t1 = tmp + tmp; - c.call(f2mPrefix + "_mul", x0, x1, tmp), - c.call(f2mPrefix + "_mul", x1, c.i32_const(pNonResidueF6), t0), - c.call(f2mPrefix + "_add", x0, t0, t0), - c.call(f2mPrefix + "_add", x0, x1, AUX), - c.call(f2mPrefix + "_mul", AUX, t0, t0), - c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), tmp, AUX), - c.call(f2mPrefix + "_add", tmp, AUX, AUX), - c.call(f2mPrefix + "_sub", t0, AUX, t0), - c.call(f2mPrefix + "_add", tmp, tmp, t1), + const pTmp = module.alloc(n8*2); + const tmpX = c.i32_const(pTmp); + const tmpY = c.i32_const(pTmp + n8); - // // t2 + t3*y = (z2 + z3*y)^2 = b^2 - // tmp = z2 * z3; - // t2 = (z2 + z3) * (z2 + my_Fp6::non_residue * z3) - tmp - my_Fp6::non_residue * tmp; - // t3 = tmp + tmp; - c.call(f2mPrefix + "_mul", x2, x3, tmp), - c.call(f2mPrefix + "_mul", x3, c.i32_const(pNonResidueF6), t2), - c.call(f2mPrefix + "_add", x2, t2, t2), - c.call(f2mPrefix + "_add", x2, x3, AUX), - c.call(f2mPrefix + "_mul", AUX, t2, t2), - c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), tmp, AUX), - c.call(f2mPrefix + "_add", tmp, AUX, AUX), - c.call(f2mPrefix + "_sub", t2, AUX, t2), - c.call(f2mPrefix + "_add", tmp, tmp, t3), + f.addCode( + c.setLocal("firstByte", c.i32_load8_u(c.getLocal("pIn"))), + c.if( + c.i32_and( + c.getLocal("firstByte"), + c.i32_const(0x40) + ), + [ + ...c.call(prefix + "_zeroAffine", c.getLocal("pOut")), + ...c.ret([]) + ] + ), + c.setLocal( + "greatest", + c.i32_and( + c.getLocal("firstByte"), + c.i32_const(0x80) + ) + ), - // // t4 + t5*y = (z4 + z5*y)^2 = c^2 - // tmp = z4 * z5; - // t4 = (z4 + z5) * (z4 + my_Fp6::non_residue * z5) - tmp - my_Fp6::non_residue * tmp; - // t5 = tmp + tmp; - c.call(f2mPrefix + "_mul", x4, x5, tmp), - c.call(f2mPrefix + "_mul", x5, c.i32_const(pNonResidueF6), t4), - c.call(f2mPrefix + "_add", x4, t4, t4), - c.call(f2mPrefix + "_add", x4, x5, AUX), - c.call(f2mPrefix + "_mul", AUX, t4, t4), - c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), tmp, AUX), - c.call(f2mPrefix + "_add", tmp, AUX, AUX), - c.call(f2mPrefix + "_sub", t4, AUX, t4), - c.call(f2mPrefix + "_add", tmp, tmp, t5), + c.call(prefixField + "_copy", c.getLocal("pIn"), tmpY), + c.i32_store8(tmpY, c.i32_and(c.getLocal("firstByte"), c.i32_const(0x3F))), + c.call(prefix + "__reverseBytes", tmpY, c.i32_const(n8), tmpX), + c.call(prefixField + "_toMontgomery", tmpX, c.getLocal("pOut")), - // For A - // z0 = 3 * t0 - 2 * z0 - c.call(f2mPrefix + "_sub", t0, x0, r0), - c.call(f2mPrefix + "_add", r0, r0, r0), - c.call(f2mPrefix + "_add", t0, r0, r0), - // z1 = 3 * t1 + 2 * z1 - c.call(f2mPrefix + "_add", t1, x1, r1), - c.call(f2mPrefix + "_add", r1, r1, r1), - c.call(f2mPrefix + "_add", t1, r1, r1), + c.call(prefixField + "_square", c.getLocal("pOut"), tmpY), + c.call(prefixField + "_mul", c.getLocal("pOut"), tmpY, tmpY), + c.call(prefixField + "_add", tmpY, c.i32_const(pB), tmpY), - // For B - // z2 = 3 * (xi * t5) + 2 * z2 - c.call(f2mPrefix + "_mul", t5, c.i32_const(pAltBn128Twist), AUX), - c.call(f2mPrefix + "_add", AUX, x2, r2), - c.call(f2mPrefix + "_add", r2, r2, r2), - c.call(f2mPrefix + "_add", AUX, r2, r2), - // z3 = 3 * t4 - 2 * z3 - c.call(f2mPrefix + "_sub", t4, x3, r3), - c.call(f2mPrefix + "_add", r3, r3, r3), - c.call(f2mPrefix + "_add", t4, r3, r3), + c.call(prefixField + "_sqrt", tmpY, tmpY), + c.call(prefixField + "_neg", tmpY, tmpX), - // For C - // z4 = 3 * t2 - 2 * z4 - c.call(f2mPrefix + "_sub", t2, x4, r4), - c.call(f2mPrefix + "_add", r4, r4, r4), - c.call(f2mPrefix + "_add", t2, r4, r4), - // z5 = 3 * t3 + 2 * z5 - c.call(f2mPrefix + "_add", t3, x5, r5), - c.call(f2mPrefix + "_add", r5, r5, r5), - c.call(f2mPrefix + "_add", t3, r5, r5), + c.if( + c.i32_eq( + c.call(prefixField + "_sign", tmpY), + c.i32_const(-1) + ), + c.if( + c.getLocal("greatest"), + c.call(prefixField + "_copy", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8))), + c.call(prefixField + "_neg", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8))) + ), + c.if( + c.getLocal("greatest"), + c.call(prefixField + "_neg", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8))), + c.call(prefixField + "_copy", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8))) + ), + ) ); } + function buildInCurveAffine() { + const f = module.addFunction(prefix + "_inCurveAffine"); + f.addParam("pIn", "i32"); + f.setReturnType("i32"); - function buildCyclotomicExp(exponent, fnName) { - const exponentNafBytes = naf(exponent).map( (b) => (b==-1 ? 0xFF: b) ); - const pExponentNafBytes = module.alloc(exponentNafBytes); + const c = f.getCodeBuilder(); - const f = module.addFunction(prefix+ "__cyclotomicExp_"+fnName); - f.addParam("x", "i32"); - f.addParam("r", "i32"); - f.addLocal("bit", "i32"); - f.addLocal("i", "i32"); + const x = c.getLocal("pIn"); + const y = c.i32_add(c.getLocal("pIn"), c.i32_const(n8)); - const c = f.getCodeBuilder(); + const y2 = c.i32_const(module.alloc(n8)); + const x3b = c.i32_const(module.alloc(n8)); - const x = c.getLocal("x"); + f.addCode( + c.call(prefixField + "_square", y, y2), + c.call(prefixField + "_square", x, x3b), + c.call(prefixField + "_mul", x, x3b, x3b), + c.call(prefixField + "_add", x3b, c.i32_const(pB), x3b), - const res = c.getLocal("r"); + c.ret( + c.call(prefixField + "_eq", y2, x3b) + ) + ); + } - const inverse = c.i32_const(module.alloc(ftsize)); + function buildInCurve() { + const f = module.addFunction(prefix + "_inCurve"); + f.addParam("pIn", "i32"); + f.setReturnType("i32"); + const c = f.getCodeBuilder(); - f.addCode( - c.call(ftmPrefix + "_conjugate", x, inverse), - c.call(ftmPrefix + "_one", res), + const aux = c.i32_const(module.alloc(n8*2)); - c.if( - c.teeLocal("bit", c.i32_load8_s(c.i32_const(exponentNafBytes.length-1), pExponentNafBytes)), - c.if( - c.i32_eq( - c.getLocal("bit"), - c.i32_const(1) - ), - c.call(ftmPrefix + "_mul", res, x, res), - c.call(ftmPrefix + "_mul", res, inverse, res), - ) - ), + f.addCode( + c.call(prefix + "_toAffine", c.getLocal("pIn"), aux), - c.setLocal("i", c.i32_const(exponentNafBytes.length-2)), - c.block(c.loop( - c.call(prefix + "__cyclotomicSquare", res, res), - c.if( - c.teeLocal("bit", c.i32_load8_s(c.getLocal("i"), pExponentNafBytes)), - c.if( - c.i32_eq( - c.getLocal("bit"), - c.i32_const(1) - ), - c.call(ftmPrefix + "_mul", res, x, res), - c.call(ftmPrefix + "_mul", res, inverse, res), - ) - ), - c.br_if(1, c.i32_eqz ( c.getLocal("i") )), - c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) + c.ret( + c.call(prefix + "_inCurveAffine", aux), + ) ); } + buildIsZeroAffine(); + buildIsZero(); + buildZeroAffine(); + buildZero(); + buildCopyAffine(); + buildCopy(); + buildToJacobian(); + buildEqAffine(); + buildEqMixed(); + buildEq(); + buildDoubleAffine(); + buildDouble(); + buildAddAffine(); + buildAddMixed(); + buildAdd(); + buildNegAffine(); + buildNeg(); + buildSubAffine(); + buildSubMixed(); + buildSub(); + buildFromMontgomeryAffine(); + buildFromMontgomery(); + buildToMontgomeryAffine(); + buildToMontgomery(); + buildToAffine(); + buildInCurveAffine(); + buildInCurve(); + buildBatchToAffine(); - function buildFinalExponentiationLastChunk() { - buildCyclotomicSquare(); - buildCyclotomicExp(finalExpZ, "w0"); - - const f = module.addFunction(prefix+ "__finalExponentiationLastChunk"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); - - const c = f.getCodeBuilder(); + buildNormalize(); - const elt = c.getLocal("x"); - const result = c.getLocal("r"); - const A = c.i32_const(module.alloc(ftsize)); - const B = c.i32_const(module.alloc(ftsize)); - const C = c.i32_const(module.alloc(ftsize)); - const D = c.i32_const(module.alloc(ftsize)); - const E = c.i32_const(module.alloc(ftsize)); - const F = c.i32_const(module.alloc(ftsize)); - const G = c.i32_const(module.alloc(ftsize)); - const H = c.i32_const(module.alloc(ftsize)); - const I = c.i32_const(module.alloc(ftsize)); - const J = c.i32_const(module.alloc(ftsize)); - const K = c.i32_const(module.alloc(ftsize)); - const L = c.i32_const(module.alloc(ftsize)); - const M = c.i32_const(module.alloc(ftsize)); - const N = c.i32_const(module.alloc(ftsize)); - const O = c.i32_const(module.alloc(ftsize)); - const P = c.i32_const(module.alloc(ftsize)); - const Q = c.i32_const(module.alloc(ftsize)); - const R = c.i32_const(module.alloc(ftsize)); - const S = c.i32_const(module.alloc(ftsize)); - const T = c.i32_const(module.alloc(ftsize)); - const U = c.i32_const(module.alloc(ftsize)); - f.addCode( + buildReverseBytes(); + buildLEMtoU(); + buildLEMtoC(); + buildUtoLEM(); + buildCtoLEM(); - // A = exp_by_neg_z(elt) // = elt^(-z) - c.call(prefix + "__cyclotomicExp_w0", elt, A), - c.call(ftmPrefix + "_conjugate", A, A), - // B = A^2 // = elt^(-2*z) - c.call(prefix + "__cyclotomicSquare", A, B), - // C = B^2 // = elt^(-4*z) - c.call(prefix + "__cyclotomicSquare", B, C), - // D = C * B // = elt^(-6*z) - c.call(ftmPrefix + "_mul", C, B, D), - // E = exp_by_neg_z(D) // = elt^(6*z^2) - c.call(prefix + "__cyclotomicExp_w0", D, E), - c.call(ftmPrefix + "_conjugate", E, E), - // F = E^2 // = elt^(12*z^2) - c.call(prefix + "__cyclotomicSquare", E, F), - // G = epx_by_neg_z(F) // = elt^(-12*z^3) - c.call(prefix + "__cyclotomicExp_w0", F, G), - c.call(ftmPrefix + "_conjugate", G, G), - // H = conj(D) // = elt^(6*z) - c.call(ftmPrefix + "_conjugate", D, H), - // I = conj(G) // = elt^(12*z^3) - c.call(ftmPrefix + "_conjugate", G, I), - // J = I * E // = elt^(12*z^3 + 6*z^2) - c.call(ftmPrefix + "_mul", I, E, J), - // K = J * H // = elt^(12*z^3 + 6*z^2 + 6*z) - c.call(ftmPrefix + "_mul", J, H, K), - // L = K * B // = elt^(12*z^3 + 6*z^2 + 4*z) - c.call(ftmPrefix + "_mul", K, B, L), - // M = K * E // = elt^(12*z^3 + 12*z^2 + 6*z) - c.call(ftmPrefix + "_mul", K, E, M), + buildBatchConvertion(module, prefix + "_batchLEMtoU", prefix + "_LEMtoU", n8*2, n8*2); + buildBatchConvertion(module, prefix + "_batchLEMtoC", prefix + "_LEMtoC", n8*2, n8); + buildBatchConvertion(module, prefix + "_batchUtoLEM", prefix + "_UtoLEM", n8*2, n8*2); + buildBatchConvertion(module, prefix + "_batchCtoLEM", prefix + "_CtoLEM", n8, n8*2, true); - // N = M * elt // = elt^(12*z^3 + 12*z^2 + 6*z + 1) - c.call(ftmPrefix + "_mul", M, elt, N), + buildBatchConvertion(module, prefix + "_batchToJacobian", prefix + "_toJacobian", n8*2, n8*3, true); - // O = L.Frobenius_map(1) // = elt^(q*(12*z^3 + 6*z^2 + 4*z)) - c.call(prefix + "__frobeniusMap1", L, O), - // P = O * N // = elt^(q*(12*z^3 + 6*z^2 + 4*z) * (12*z^3 + 12*z^2 + 6*z + 1)) - c.call(ftmPrefix + "_mul", O, N, P), - // Q = K.Frobenius_map(2) // = elt^(q^2 * (12*z^3 + 6*z^2 + 6*z)) - c.call(prefix + "__frobeniusMap2", K, Q), - // R = Q * P // = elt^(q^2 * (12*z^3 + 6*z^2 + 6*z) + q*(12*z^3 + 6*z^2 + 4*z) * (12*z^3 + 12*z^2 + 6*z + 1)) - c.call(ftmPrefix + "_mul", Q, P, R), - // S = conj(elt) // = elt^(-1) - c.call(ftmPrefix + "_conjugate", elt, S), - // T = S * L // = elt^(12*z^3 + 6*z^2 + 4*z - 1) - c.call(ftmPrefix + "_mul", S, L, T), - // U = T.Frobenius_map(3) // = elt^(q^3(12*z^3 + 6*z^2 + 4*z - 1)) - c.call(prefix + "__frobeniusMap3", T, U), - // V = U * R // = elt^(q^3(12*z^3 + 6*z^2 + 4*z - 1) + q^2 * (12*z^3 + 6*z^2 + 6*z) + q*(12*z^3 + 6*z^2 + 4*z) * (12*z^3 + 12*z^2 + 6*z + 1)) - c.call(ftmPrefix + "_mul", U, R, result), - // result = V - ); - } + buildMultiexp(module, prefix, prefix + "_multiexp", prefix + "_add", n8*3); + buildMultiexp(module, prefix, prefix + "_multiexpAffine", prefix + "_addMixed", n8*2); + /* + buildTimesScalar( + module, + prefix + "_timesScalarOld", + n8*3, + prefix + "_add", + prefix + "_double", + prefix + "_copy", + prefix + "_zero", + ); + */ + buildTimesScalarNAF( + module, + prefix + "_timesScalar", + n8*3, + prefix + "_add", + prefix + "_double", + prefix + "_sub", + prefix + "_copy", + prefix + "_zero" + ); - function buildFinalExponentiation() { - buildFinalExponentiationFirstChunk(); - buildFinalExponentiationLastChunk(); - const f = module.addFunction(prefix+ "_finalExponentiation"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + buildTimesScalarNAF( + module, + prefix + "_timesScalarAffine", + n8*2, + prefix + "_addMixed", + prefix + "_double", + prefix + "_subMixed", + prefix + "_copyAffine", + prefix + "_zero" + ); - const c = f.getCodeBuilder(); + module.exportFunction(prefix + "_isZero"); + module.exportFunction(prefix + "_isZeroAffine"); - const elt = c.getLocal("x"); - const result = c.getLocal("r"); - const eltToFirstChunk = c.i32_const(module.alloc(ftsize)); + module.exportFunction(prefix + "_eq"); + module.exportFunction(prefix + "_eqMixed"); + module.exportFunction(prefix + "_eqAffine"); - f.addCode( - c.call(prefix + "__finalExponentiationFirstChunk", elt, eltToFirstChunk ), - c.call(prefix + "__finalExponentiationLastChunk", eltToFirstChunk, result ) - ); - } + module.exportFunction(prefix + "_copy"); + module.exportFunction(prefix + "_copyAffine"); + module.exportFunction(prefix + "_zero"); + module.exportFunction(prefix + "_zeroAffine"); - function buildFinalExponentiationOld() { - const f = module.addFunction(prefix+ "_finalExponentiationOld"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + module.exportFunction(prefix + "_double"); + module.exportFunction(prefix + "_doubleAffine"); - const exponent = 552484233613224096312617126783173147097382103762957654188882734314196910839907541213974502761540629817009608548654680343627701153829446747810907373256841551006201639677726139946029199968412598804882391702273019083653272047566316584365559776493027495458238373902875937659943504873220554161550525926302303331747463515644711876653177129578303191095900909191624817826566688241804408081892785725967931714097716709526092261278071952560171111444072049229123565057483750161460024353346284167282452756217662335528813519139808291170539072125381230815729071544861602750936964829313608137325426383735122175229541155376346436093930287402089517426973178917569713384748081827255472576937471496195752727188261435633271238710131736096299798168852925540549342330775279877006784354801422249722573783561685179618816480037695005515426162362431072245638324744480n; + module.exportFunction(prefix + "_add"); + module.exportFunction(prefix + "_addMixed"); + module.exportFunction(prefix + "_addAffine"); - const pExponent = module.alloc(utils$2.bigInt2BytesLE( exponent, 352 )); + module.exportFunction(prefix + "_neg"); + module.exportFunction(prefix + "_negAffine"); - const c = f.getCodeBuilder(); + module.exportFunction(prefix + "_sub"); + module.exportFunction(prefix + "_subMixed"); + module.exportFunction(prefix + "_subAffine"); - f.addCode( - c.call(ftmPrefix + "_exp", c.getLocal("x"), c.i32_const(pExponent), c.i32_const(352), c.getLocal("r")), - ); - } + module.exportFunction(prefix + "_fromMontgomery"); + module.exportFunction(prefix + "_fromMontgomeryAffine"); + module.exportFunction(prefix + "_toMontgomery"); + module.exportFunction(prefix + "_toMontgomeryAffine"); + module.exportFunction(prefix + "_timesScalar"); + module.exportFunction(prefix + "_timesScalarAffine"); + module.exportFunction(prefix + "_normalize"); - const pPreP = module.alloc(prePSize); - const pPreQ = module.alloc(preQSize); + // Convertion functions + module.exportFunction(prefix + "_LEMtoU"); + module.exportFunction(prefix + "_LEMtoC"); + module.exportFunction(prefix + "_UtoLEM"); + module.exportFunction(prefix + "_CtoLEM"); - function buildPairingEquation(nPairings) { + module.exportFunction(prefix + "_batchLEMtoU"); + module.exportFunction(prefix + "_batchLEMtoC"); + module.exportFunction(prefix + "_batchUtoLEM"); + module.exportFunction(prefix + "_batchCtoLEM"); - const f = module.addFunction(prefix+ "_pairingEq"+nPairings); - for (let i=0; i. +*/ +const { isOdd: isOdd$2, modInv: modInv$1, modPow } = bigint; +const utils$2 = utils$5; - function buildPairing() { +var build_fft = function buildFFT(module, prefix, gPrefix, fPrefix, opGtimesF) { - const f = module.addFunction(prefix+ "_pairing"); - f.addParam("p", "i32"); - f.addParam("q", "i32"); - f.addParam("r", "i32"); + const n64f = module.modules[fPrefix].n64; + const n8f = n64f*8; - const c = f.getCodeBuilder(); + const n64g = module.modules[gPrefix].n64; + const n8g = n64g*8; - const resT = c.i32_const(module.alloc(ftsize)); + const q = module.modules[fPrefix].q; - f.addCode(c.call(prefix + "_prepareG1", c.getLocal("p"), c.i32_const(pPreP) )); - f.addCode(c.call(prefix + "_prepareG2", c.getLocal("q"), c.i32_const(pPreQ) )); - f.addCode(c.call(prefix + "_millerLoop", c.i32_const(pPreP), c.i32_const(pPreQ), resT )); - f.addCode(c.call(prefix + "_finalExponentiation", resT, c.getLocal("r") )); + let rem = q - 1n; + let maxBits = 0; + while (!isOdd$2(rem)) { + maxBits ++; + rem = rem >> 1n; } + let nr = 2n; - buildPrepAddStep(); - buildPrepDoubleStep(); - - buildPrepareG1(); - buildPrepareG2(); + while ( modPow(nr, q >> 1n, q) === 1n ) nr = nr + 1n; - buildMulBy024(); - buildMulBy024Old(); - buildMillerLoop(); + // console.log(nr); + const w = new Array(maxBits+1); + w[maxBits] = modPow(nr, rem, q); - for (let i=0; i<10; i++) { - buildFrobeniusMap(i); - module.exportFunction(prefix + "__frobeniusMap"+i); + let n=maxBits-1; + while (n>=0) { + w[n] = modPow(w[n+1], 2n, q); + n--; } - buildFinalExponentiationOld(); - buildFinalExponentiation(); + const bytes = []; + const R = (1n << BigInt(n8f*8)) % q; - for (let i=1; i<=5; i++) { - buildPairingEquation(i); - module.exportFunction(prefix + "_pairingEq"+i); + for (let i=0; i> i); + } + } + return r; + } -var build_bls12381 = function buildBLS12381(module, _prefix) { + const rtable = Array(256); + for (let i=0; i<256; i++) { + rtable[i] = rev(i); + } - const prefix = _prefix || "bls12381"; + const REVTABLE = module.alloc(rtable); - if (module.modules[prefix]) return prefix; // already builded - const q = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn; - const r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n; + function buildLog2() { + const f = module.addFunction(prefix+"__log2"); + f.addParam("n", "i32"); + f.setReturnType("i32"); + f.addLocal("bits", "i32"); + f.addLocal("aux", "i32"); - const n64q = Math.floor((bitLength$1(q - 1n) - 1)/64) +1; - const n8q = n64q*8; - const f1size = n8q; - const f2size = f1size * 2; - const ftsize = f1size * 12; + const c = f.getCodeBuilder(); - const n64r = Math.floor((bitLength$1(r - 1n) - 1)/64) +1; - const n8r = n64r*8; - const frsize = n8r; + f.addCode( + c.setLocal( + "aux", + c.i32_shr_u( + c.getLocal("n"), + c.i32_const(1) + ) + ) + ); + f.addCode(c.setLocal("bits", c.i32_const(0))); + + f.addCode(c.block(c.loop( + c.br_if( + 1, + c.i32_eqz(c.getLocal("aux")) + ), + c.setLocal( + "aux", + c.i32_shr_u( + c.getLocal("aux"), + c.i32_const(1) + ) + ), - const pr = module.alloc(utils$1.bigInt2BytesLE( r, frsize )); + c.setLocal( + "bits", + c.i32_add( + c.getLocal("bits"), + c.i32_const(1) + ) + ), - const f1mPrefix = buildF1m(module, q, "f1m", "intq"); - buildF1(module, r, "fr", "frm", "intr"); - const pG1b = module.alloc(utils$1.bigInt2BytesLE( toMontgomery(4n), f1size )); - const g1mPrefix = buildCurve(module, "g1m", "f1m", pG1b); + c.br(0) + ))); - buildFFT$1(module, "frm", "frm", "frm", "frm_mul"); + f.addCode(c.if( + c.i32_ne( + c.getLocal("n"), + c.i32_shl( + c.i32_const(1), + c.getLocal("bits") + ) + ), + c.unreachable() + )); - buildPol(module, "pol", "frm"); - buildQAP(module, "qap", "frm"); + f.addCode(c.if( + c.i32_gt_u( + c.getLocal("bits"), + c.i32_const(maxBits) + ), + c.unreachable() + )); - const f2mPrefix = buildF2m(module, "f1m_neg", "f2m", "f1m"); - const pG2b = module.alloc([ - ...utils$1.bigInt2BytesLE( toMontgomery(4n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(4n), f1size ) - ]); - const g2mPrefix = buildCurve(module, "g2m", "f2m", pG2b); + f.addCode(c.getLocal("bits")); + } + function buildFFT() { + const f = module.addFunction(prefix+"_fft"); + f.addParam("px", "i32"); + f.addParam("n", "i32"); - function buildGTimesFr(fnName, opMul) { - const f = module.addFunction(fnName); - f.addParam("pG", "i32"); - f.addParam("pFr", "i32"); - f.addParam("pr", "i32"); + f.addLocal("bits", "i32"); const c = f.getCodeBuilder(); - const AUX = c.i32_const(module.alloc(n8r)); + const One = c.i32_const(module.alloc(n8f)); f.addCode( - c.call("frm_fromMontgomery", c.getLocal("pFr"), AUX), + c.setLocal( + "bits", + c.call( + prefix + "__log2", + c.getLocal("n") + ) + ), + c.call(fPrefix + "_one", One), c.call( - opMul, - c.getLocal("pG"), - AUX, - c.i32_const(n8r), - c.getLocal("pr") + prefix+"_rawfft", + c.getLocal("px"), + c.getLocal("bits"), + c.i32_const(0), + One ) ); - module.exportFunction(fnName); } - buildGTimesFr("g1m_timesFr", "g1m_timesScalar"); - buildFFT$1(module, "g1m", "g1m", "frm", "g1m_timesFr"); - - buildGTimesFr("g2m_timesFr", "g2m_timesScalar"); - buildFFT$1(module, "g2m", "g2m", "frm", "g2m_timesFr"); - buildGTimesFr("g1m_timesFrAffine", "g1m_timesScalarAffine"); - buildGTimesFr("g2m_timesFrAffine", "g2m_timesScalarAffine"); + function buildIFFT() { + const f = module.addFunction(prefix+"_ifft"); + f.addParam("px", "i32"); + f.addParam("n", "i32"); + f.addLocal("bits", "i32"); + f.addLocal("pInv2", "i32"); - buildApplyKey(module, "frm_batchApplyKey", "fmr", "frm", n8r, n8r, n8r, "frm_mul"); - buildApplyKey(module, "g1m_batchApplyKey", "g1m", "frm", n8q*3, n8q*3, n8r, "g1m_timesFr"); - buildApplyKey(module, "g1m_batchApplyKeyMixed", "g1m", "frm", n8q*2, n8q*3, n8r, "g1m_timesFrAffine"); - buildApplyKey(module, "g2m_batchApplyKey", "g2m", "frm", n8q*2*3, n8q*3*2, n8r, "g2m_timesFr"); - buildApplyKey(module, "g2m_batchApplyKeyMixed", "g2m", "frm", n8q*2*2, n8q*3*2, n8r, "g2m_timesFrAffine"); + const c = f.getCodeBuilder(); + f.addCode( + c.setLocal( + "bits", + c.call( + prefix + "__log2", + c.getLocal("n") + ) + ), + c.setLocal( + "pInv2", + c.i32_add( + c.i32_const(INV2), + c.i32_mul( + c.getLocal("bits"), + c.i32_const(n8f) + ) + ) + ), - function toMontgomery(a) { - return BigInt(a) * (1n << BigInt(f1size*8)) % q; + c.call( + prefix+"_rawfft", + c.getLocal("px"), + c.getLocal("bits"), + c.i32_const(1), + c.getLocal("pInv2") + ), + ); } - const G1gen = [ - 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507n, - 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569n, - 1n - ]; + function buildRawFFT() { + const f = module.addFunction(prefix+"_rawfft"); + f.addParam("px", "i32"); + f.addParam("bits", "i32"); // 2 power + f.addParam("reverse", "i32"); + f.addParam("mulFactor", "i32"); - const pG1gen = module.alloc( - [ - ...utils$1.bigInt2BytesLE( toMontgomery(G1gen[0]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G1gen[1]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G1gen[2]), f1size ), - ] - ); + f.addLocal("s", "i32"); + f.addLocal("k", "i32"); + f.addLocal("j", "i32"); + f.addLocal("m", "i32"); + f.addLocal("mdiv2", "i32"); + f.addLocal("n", "i32"); + f.addLocal("pwm", "i32"); + f.addLocal("idx1", "i32"); + f.addLocal("idx2", "i32"); - const G1zero = [ - 0n, - 1n, - 0n - ]; + const c = f.getCodeBuilder(); - const pG1zero = module.alloc( - [ - ...utils$1.bigInt2BytesLE( toMontgomery(G1zero[0]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G1zero[1]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G1zero[2]), f1size ) - ] - ); + const W = c.i32_const(module.alloc(n8f)); + const T = c.i32_const(module.alloc(n8g)); + const U = c.i32_const(module.alloc(n8g)); - const G2gen = [ - [ - 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160n, - 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758n, - ],[ - 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905n, - 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582n, - ],[ - 1n, - 0n, - ] - ]; + f.addCode( + c.call(prefix + "__reversePermutation", c.getLocal("px"), c.getLocal("bits")), + c.setLocal("n", c.i32_shl(c.i32_const(1), c.getLocal("bits"))), + c.setLocal("s", c.i32_const(1)), + c.block(c.loop( + c.br_if( + 1, + c.i32_gt_u( + c.getLocal("s"), + c.getLocal("bits") + ) + ), + c.setLocal("m", c.i32_shl(c.i32_const(1), c.getLocal("s"))), + c.setLocal("pwm", + c.i32_add( + c.i32_const(ROOTs), + c.i32_mul( + c.getLocal("s"), + c.i32_const(n8f) + ) + ) + ), + c.setLocal("k", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_ge_u( + c.getLocal("k"), + c.getLocal("n") + ) + ), - const pG2gen = module.alloc( - [ - ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[0][0]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[0][1]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[1][0]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[1][1]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[2][0]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[2][1]), f1size ), - ] - ); + c.call(fPrefix + "_one", W), - const G2zero = [ - [ - 0n, - 0n, - ],[ - 1n, - 0n, - ],[ - 0n, - 0n, - ] - ]; + c.setLocal("mdiv2", c.i32_shr_u(c.getLocal("m"), c.i32_const(1)) ), + c.setLocal("j", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_ge_u( + c.getLocal("j"), + c.getLocal("mdiv2") + ) + ), - const pG2zero = module.alloc( - [ - ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[0][0]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[0][1]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[1][0]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[1][1]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[2][0]), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[2][1]), f1size ), - ] - ); + c.setLocal( + "idx1", + c.i32_add( + c.getLocal("px"), + c.i32_mul( + c.i32_add( + c.getLocal("k"), + c.getLocal("j") + ), + c.i32_const(n8g) + ) + ) + ), + + c.setLocal( + "idx2", + c.i32_add( + c.getLocal("idx1"), + c.i32_mul( + c.getLocal("mdiv2"), + c.i32_const(n8g) + ) + ) + ), + + c.call( + opGtimesF, + c.getLocal("idx2"), + W, + T + ), - const pOneT = module.alloc([ - ...utils$1.bigInt2BytesLE( toMontgomery(1n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(0n), f1size ), - ]); + c.call( + gPrefix + "_copy", + c.getLocal("idx1"), + U + ), - const pBls12381Twist = module.alloc([ - ...utils$1.bigInt2BytesLE( toMontgomery(1n), f1size ), - ...utils$1.bigInt2BytesLE( toMontgomery(1n), f1size ), - ]); + c.call( + gPrefix + "_add", + U, + T, + c.getLocal("idx1"), + ), - function build_mulNR2() { - const f = module.addFunction(f2mPrefix + "_mulNR"); - f.addParam("x", "i32"); - f.addParam("pr", "i32"); + c.call( + gPrefix + "_sub", + U, + T, + c.getLocal("idx2"), + ), - const c = f.getCodeBuilder(); + c.call( + fPrefix + "_mul", + W, + c.getLocal("pwm"), + W, + ), - const x0c = c.i32_const(module.alloc(f1size)); - const x0 = c.getLocal("x"); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1size)); - const r0 = c.getLocal("pr"); - const r1 = c.i32_add(c.getLocal("pr"), c.i32_const(f1size)); + c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), + c.br(0) + )), - f.addCode( - c.call(f1mPrefix+"_copy", x0, x0c), - c.call(f1mPrefix+"_sub", x0, x1, r0), - c.call(f1mPrefix+"_add", x0c, x1, r1), + c.setLocal("k", c.i32_add(c.getLocal("k"), c.getLocal("m"))), + c.br(0) + )), + + c.setLocal("s", c.i32_add(c.getLocal("s"), c.i32_const(1))), + c.br(0) + )), + c.call( + prefix + "__fftFinal", + c.getLocal("px"), + c.getLocal("bits"), + c.getLocal("reverse"), + c.getLocal("mulFactor") + ) ); } - build_mulNR2(); - const f6mPrefix = buildF3m(module, f2mPrefix+"_mulNR", "f6m", "f2m"); - function build_mulNR6() { - const f = module.addFunction(f6mPrefix + "_mulNR"); - f.addParam("x", "i32"); - f.addParam("pr", "i32"); + function buildFinalInverse() { + const f = module.addFunction(prefix+"__fftFinal"); + f.addParam("px", "i32"); + f.addParam("bits", "i32"); + f.addParam("reverse", "i32"); + f.addParam("mulFactor", "i32"); + f.addLocal("n", "i32"); + f.addLocal("ndiv2", "i32"); + f.addLocal("pInv2", "i32"); + f.addLocal("i", "i32"); + f.addLocal("mask", "i32"); + f.addLocal("idx1", "i32"); + f.addLocal("idx2", "i32"); const c = f.getCodeBuilder(); - const c0copy = c.i32_const(module.alloc(f1size*2)); + const T = c.i32_const(module.alloc(n8g)); f.addCode( - c.call( - f2mPrefix + "_copy", - c.getLocal("x"), - c0copy - ), - c.call( - f2mPrefix + "_mulNR", - c.i32_add(c.getLocal("x"), c.i32_const(n8q*4)), - c.getLocal("pr") - ), - c.call( - f2mPrefix + "_copy", - c.i32_add(c.getLocal("x"), c.i32_const(n8q*2)), - c.i32_add(c.getLocal("pr"), c.i32_const(n8q*4)), + c.if( + c.i32_and( + c.i32_eqz(c.getLocal("reverse")), + c.call(fPrefix + "_isOne", c.getLocal("mulFactor")) + ), + c.ret([]) ), - c.call( - f2mPrefix + "_copy", - c0copy, - c.i32_add(c.getLocal("pr"), c.i32_const(n8q*2)), + c.setLocal("n", c.i32_shl( c.i32_const(1), c.getLocal("bits"))), + + c.setLocal("mask", c.i32_sub( c.getLocal("n") , c.i32_const(1))), + c.setLocal("i", c.i32_const(1)), + c.setLocal( + "ndiv2", + c.i32_shr_u( + c.getLocal("n"), + c.i32_const(1) + ) ), + c.block(c.loop( + c.br_if( + 1, + c.i32_ge_u( + c.getLocal("i"), + c.getLocal("ndiv2") + ) + ), + + c.setLocal("idx1", + c.i32_add( + c.getLocal("px"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), + + c.setLocal("idx2", + c.i32_add( + c.getLocal("px"), + c.i32_mul( + c.i32_sub( + c.getLocal("n"), + c.getLocal("i") + ), + c.i32_const(n8g) + ) + ) + ), + + c.if( + c.getLocal("reverse"), + c.if( + c.call(fPrefix + "_isOne", c.getLocal("mulFactor")), + [ + ...c.call(gPrefix + "_copy", c.getLocal("idx1"), T), + ...c.call(gPrefix + "_copy", c.getLocal("idx2") , c.getLocal("idx1") ), + ...c.call(gPrefix + "_copy", T , c.getLocal("idx2")), + ], + [ + ...c.call(gPrefix + "_copy", c.getLocal("idx1"), T), + ...c.call(opGtimesF , c.getLocal("idx2") , c.getLocal("mulFactor"), c.getLocal("idx1") ), + ...c.call(opGtimesF , T , c.getLocal("mulFactor"), c.getLocal("idx2")), + ] + ), + c.if( + c.call(fPrefix + "_isOne", c.getLocal("mulFactor")), + [ + // Do nothing (It should not be here) + ], + [ + ...c.call(opGtimesF , c.getLocal("idx1") , c.getLocal("mulFactor"), c.getLocal("idx1") ), + ...c.call(opGtimesF , c.getLocal("idx2") , c.getLocal("mulFactor"), c.getLocal("idx2")), + ] + ) + ), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + + c.br(0) + )), + + c.if( + c.call(fPrefix + "_isOne", c.getLocal("mulFactor")), + [ + // Do nothing (It should not be here) + ], + [ + ...c.call(opGtimesF, c.getLocal("px") , c.getLocal("mulFactor"), c.getLocal("px")), + ...c.setLocal("idx2", + c.i32_add( + c.getLocal("px"), + c.i32_mul( + c.getLocal("ndiv2"), + c.i32_const(n8g) + ) + ) + ), + ...c.call(opGtimesF, c.getLocal("idx2"),c.getLocal("mulFactor"), c.getLocal("idx2")) + ] + ) ); } - build_mulNR6(); - const ftmPrefix = buildF2m(module, f6mPrefix+"_mulNR", "ftm", f6mPrefix); - - const ateLoopCount = 0xd201000000010000n; - const ateLoopBitBytes = bits(ateLoopCount); - const pAteLoopBitBytes = module.alloc(ateLoopBitBytes); + function buildReversePermutation() { + const f = module.addFunction(prefix+"__reversePermutation"); + f.addParam("px", "i32"); + f.addParam("bits", "i32"); + f.addLocal("n", "i32"); + f.addLocal("i", "i32"); + f.addLocal("ri", "i32"); + f.addLocal("idx1", "i32"); + f.addLocal("idx2", "i32"); - const ateCoefSize = 3 * f2size; - const ateNDblCoefs = ateLoopBitBytes.length-1; - const ateNAddCoefs = ateLoopBitBytes.reduce((acc, b) => acc + ( b!=0 ? 1 : 0) ,0); - const ateNCoefs = ateNAddCoefs + ateNDblCoefs + 1; - const prePSize = 3*2*n8q; - const preQSize = 3*n8q*2 + ateNCoefs*ateCoefSize; - const finalExpIsNegative = true; + const c = f.getCodeBuilder(); - const finalExpZ = 15132376222941642752n; + const T = c.i32_const(module.alloc(n8g)); + f.addCode( + c.setLocal("n", c.i32_shl( c.i32_const(1), c.getLocal("bits"))), + c.setLocal("i", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("i"), + c.getLocal("n") + ) + ), - module.modules[prefix] = { - n64q: n64q, - n64r: n64r, - n8q: n8q, - n8r: n8r, - pG1gen: pG1gen, - pG1zero: pG1zero, - pG1b: pG1b, - pG2gen: pG2gen, - pG2zero: pG2zero, - pG2b: pG2b, - pq: module.modules["f1m"].pq, - pr: pr, - pOneT: pOneT, - r: r, - q: q, - prePSize: prePSize, - preQSize: preQSize - }; + c.setLocal("idx1", + c.i32_add( + c.getLocal("px"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), + c.setLocal("ri", c.call(prefix + "__rev", c.getLocal("i"), c.getLocal("bits"))), - function naf(n) { - let E = n; - const res = []; - while (E > 0n) { - if (isOdd(E)) { - const z = 2 - Number(E % 4n); - res.push( z ); - E = E - BigInt(z); - } else { - res.push( 0 ); - } - E = E >> 1n; - } - return res; - } + c.setLocal("idx2", + c.i32_add( + c.getLocal("px"), + c.i32_mul( + c.getLocal("ri"), + c.i32_const(n8g) + ) + ) + ), - function bits(n) { - let E = n; - const res = []; - while (E > 0n) { - if (isOdd(E)) { - res.push( 1 ); - } else { - res.push( 0 ); - } - E = E >> 1n; - } - return res; + c.if( + c.i32_lt_u( + c.getLocal("i"), + c.getLocal("ri") + ), + [ + ...c.call(gPrefix + "_copy", c.getLocal("idx1"), T), + ...c.call(gPrefix + "_copy", c.getLocal("idx2") , c.getLocal("idx1")), + ...c.call(gPrefix + "_copy", T , c.getLocal("idx2")) + ] + ), + + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + + c.br(0) + )) + ); } - function buildPrepareG1() { - const f = module.addFunction(prefix+ "_prepareG1"); - f.addParam("pP", "i32"); - f.addParam("ppreP", "i32"); + function buildRev() { + const f = module.addFunction(prefix+"__rev"); + f.addParam("x", "i32"); + f.addParam("bits", "i32"); + f.setReturnType("i32"); const c = f.getCodeBuilder(); f.addCode( - c.call(g1mPrefix + "_normalize", c.getLocal("pP"), c.getLocal("ppreP")), // TODO Remove if already in affine + c.i32_rotl( + c.i32_add( + c.i32_add( + c.i32_shl( + c.i32_load8_u( + c.i32_and( + c.getLocal("x"), + c.i32_const(0xFF) + ), + REVTABLE, + 0 + ), + c.i32_const(24) + ), + c.i32_shl( + c.i32_load8_u( + c.i32_and( + c.i32_shr_u( + c.getLocal("x"), + c.i32_const(8) + ), + c.i32_const(0xFF) + ), + REVTABLE, + 0 + ), + c.i32_const(16) + ), + ), + c.i32_add( + c.i32_shl( + c.i32_load8_u( + c.i32_and( + c.i32_shr_u( + c.getLocal("x"), + c.i32_const(16) + ), + c.i32_const(0xFF) + ), + REVTABLE, + 0 + ), + c.i32_const(8) + ), + c.i32_load8_u( + c.i32_and( + c.i32_shr_u( + c.getLocal("x"), + c.i32_const(24) + ), + c.i32_const(0xFF) + ), + REVTABLE, + 0 + ), + ) + ), + c.getLocal("bits") + ) ); } - - function buildPrepDoubleStep() { - const f = module.addFunction(prefix+ "_prepDblStep"); - f.addParam("R", "i32"); - f.addParam("r", "i32"); + function buildFFTJoin() { + const f = module.addFunction(prefix+"_fftJoin"); + f.addParam("pBuff1", "i32"); + f.addParam("pBuff2", "i32"); + f.addParam("n", "i32"); + f.addParam("first", "i32"); + f.addParam("inc", "i32"); + f.addLocal("idx1", "i32"); + f.addLocal("idx2", "i32"); + f.addLocal("i", "i32"); const c = f.getCodeBuilder(); - const Rx = c.getLocal("R"); - const Ry = c.i32_add(c.getLocal("R"), c.i32_const(2*n8q)); - const Rz = c.i32_add(c.getLocal("R"), c.i32_const(4*n8q)); - - const t0 = c.getLocal("r"); - const t3 = c.i32_add(c.getLocal("r"), c.i32_const(2*n8q)); - const t6 = c.i32_add(c.getLocal("r"), c.i32_const(4*n8q)); - - - const zsquared = c.i32_const(module.alloc(f2size)); - const t1 = c.i32_const(module.alloc(f2size)); - const t2 = c.i32_const(module.alloc(f2size)); - const t4 = c.i32_const(module.alloc(f2size)); - const t5 = c.i32_const(module.alloc(f2size)); + const W = c.i32_const(module.alloc(n8f)); + const T = c.i32_const(module.alloc(n8g)); + const U = c.i32_const(module.alloc(n8g)); f.addCode( + c.call( fPrefix + "_copy", c.getLocal("first"), W), + c.setLocal("i", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("i"), + c.getLocal("n") + ) + ), - // tmp0 = r.x.square(); - c.call(f2mPrefix + "_square", Rx, t0), - - // tmp1 = r.y.square(); - c.call(f2mPrefix + "_square", Ry, t1), + c.setLocal( + "idx1", + c.i32_add( + c.getLocal("pBuff1"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), - // tmp2 = tmp1.square(); - c.call(f2mPrefix + "_square", t1, t2), + c.setLocal( + "idx2", + c.i32_add( + c.getLocal("pBuff2"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), - // tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2; - c.call(f2mPrefix + "_add", t1, Rx, t3), - c.call(f2mPrefix + "_square", t3, t3), - c.call(f2mPrefix + "_sub", t3, t0, t3), - c.call(f2mPrefix + "_sub", t3, t2, t3), + c.call( + opGtimesF, + c.getLocal("idx2"), + W, + T + ), - // tmp3 = tmp3 + tmp3; - c.call(f2mPrefix + "_add", t3, t3, t3), + c.call( + gPrefix + "_copy", + c.getLocal("idx1"), + U + ), - // tmp4 = tmp0 + tmp0 + tmp0; - c.call(f2mPrefix + "_add", t0, t0, t4), - c.call(f2mPrefix + "_add", t4, t0, t4), + c.call( + gPrefix + "_add", + U, + T, + c.getLocal("idx1"), + ), - // tmp6 = r.x + tmp4; - c.call(f2mPrefix + "_add", Rx, t4, t6), + c.call( + gPrefix + "_sub", + U, + T, + c.getLocal("idx2"), + ), - // tmp5 = tmp4.square(); - c.call(f2mPrefix + "_square", t4, t5), + c.call( + fPrefix + "_mul", + W, + c.getLocal("inc"), + W, + ), - // zsquared = r.z.square(); - c.call(f2mPrefix + "_square", Rz, zsquared), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) + ); + } - // r.x = tmp5 - tmp3 - tmp3; - c.call(f2mPrefix + "_sub", t5, t3, Rx), - c.call(f2mPrefix + "_sub", Rx, t3, Rx), - // r.z = (r.z + r.y).square() - tmp1 - zsquared; - c.call(f2mPrefix + "_add", Rz, Ry, Rz), - c.call(f2mPrefix + "_square", Rz, Rz), - c.call(f2mPrefix + "_sub", Rz, t1, Rz), - c.call(f2mPrefix + "_sub", Rz, zsquared, Rz), + function buildFFTJoinExt() { + const f = module.addFunction(prefix+"_fftJoinExt"); + f.addParam("pBuff1", "i32"); + f.addParam("pBuff2", "i32"); + f.addParam("n", "i32"); + f.addParam("first", "i32"); + f.addParam("inc", "i32"); + f.addParam("totalBits", "i32"); + f.addLocal("idx1", "i32"); + f.addLocal("idx2", "i32"); + f.addLocal("i", "i32"); + f.addLocal("pShiftToM", "i32"); - // r.y = (tmp3 - r.x) * tmp4; - c.call(f2mPrefix + "_sub", t3, Rx, Ry), - c.call(f2mPrefix + "_mul", Ry, t4, Ry), + const c = f.getCodeBuilder(); - // tmp2 = tmp2 + tmp2; - c.call(f2mPrefix + "_add", t2, t2, t2), + const W = c.i32_const(module.alloc(n8f)); + const U = c.i32_const(module.alloc(n8g)); - // tmp2 = tmp2 + tmp2; - c.call(f2mPrefix + "_add", t2, t2, t2), + f.addCode( - // tmp2 = tmp2 + tmp2; - c.call(f2mPrefix + "_add", t2, t2, t2), + c.setLocal("pShiftToM", + c.i32_add( + c.i32_const(SHIFT_TO_M), + c.i32_mul( + c.getLocal("totalBits"), + c.i32_const(n8f) + ) + ) + ), - // r.y -= tmp2; - c.call(f2mPrefix + "_sub", Ry, t2, Ry), - // tmp3 = tmp4 * zsquared; - c.call(f2mPrefix + "_mul", t4, zsquared, t3), + c.call( fPrefix + "_copy", c.getLocal("first"), W), + c.setLocal("i", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("i"), + c.getLocal("n") + ) + ), - // tmp3 = tmp3 + tmp3; - c.call(f2mPrefix + "_add", t3, t3, t3), + c.setLocal( + "idx1", + c.i32_add( + c.getLocal("pBuff1"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), - // tmp3 = -tmp3; - c.call(f2mPrefix + "_neg", t3, t3), + c.setLocal( + "idx2", + c.i32_add( + c.getLocal("pBuff2"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), - // tmp6 = tmp6.square() - tmp0 - tmp5; - c.call(f2mPrefix + "_square", t6, t6), - c.call(f2mPrefix + "_sub", t6, t0, t6), - c.call(f2mPrefix + "_sub", t6, t5, t6), + c.call( + gPrefix + "_add", + c.getLocal("idx1"), + c.getLocal("idx2"), + U + ), - // tmp1 = tmp1 + tmp1; - c.call(f2mPrefix + "_add", t1, t1, t1), + c.call( + opGtimesF, + c.getLocal("idx2"), + c.getLocal("pShiftToM"), + c.getLocal("idx2") + ), - // tmp1 = tmp1 + tmp1; - c.call(f2mPrefix + "_add", t1, t1, t1), + c.call( + gPrefix + "_add", + c.getLocal("idx1"), + c.getLocal("idx2"), + c.getLocal("idx2") + ), - // tmp6 = tmp6 - tmp1; - c.call(f2mPrefix + "_sub", t6, t1, t6), + c.call( + opGtimesF, + c.getLocal("idx2"), + W, + c.getLocal("idx2"), + ), - // tmp0 = r.z * zsquared; - c.call(f2mPrefix + "_mul", Rz, zsquared, t0), + c.call( + gPrefix + "_copy", + U, + c.getLocal("idx1") + ), - // tmp0 = tmp0 + tmp0; - c.call(f2mPrefix + "_add", t0, t0, t0), + c.call( + fPrefix + "_mul", + W, + c.getLocal("inc"), + W + ), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) ); } - function buildPrepAddStep() { - const f = module.addFunction(prefix+ "_prepAddStep"); - f.addParam("R", "i32"); - f.addParam("Q", "i32"); - f.addParam("r", "i32"); + function buildFFTJoinExtInv() { + const f = module.addFunction(prefix+"_fftJoinExtInv"); + f.addParam("pBuff1", "i32"); + f.addParam("pBuff2", "i32"); + f.addParam("n", "i32"); + f.addParam("first", "i32"); + f.addParam("inc", "i32"); + f.addParam("totalBits", "i32"); + f.addLocal("idx1", "i32"); + f.addLocal("idx2", "i32"); + f.addLocal("i", "i32"); + f.addLocal("pShiftToM", "i32"); + f.addLocal("pSConst", "i32"); const c = f.getCodeBuilder(); - const Rx = c.getLocal("R"); - const Ry = c.i32_add(c.getLocal("R"), c.i32_const(2*n8q)); - const Rz = c.i32_add(c.getLocal("R"), c.i32_const(4*n8q)); + const W = c.i32_const(module.alloc(n8f)); + const U = c.i32_const(module.alloc(n8g)); - const Qx = c.getLocal("Q"); - const Qy = c.i32_add(c.getLocal("Q"), c.i32_const(2*n8q)); + f.addCode( - const t10 = c.getLocal("r"); - const t1 = c.i32_add(c.getLocal("r"), c.i32_const(2*n8q)); - const t9 = c.i32_add(c.getLocal("r"), c.i32_const(4*n8q)); + c.setLocal("pShiftToM", + c.i32_add( + c.i32_const(SHIFT_TO_M), + c.i32_mul( + c.getLocal("totalBits"), + c.i32_const(n8f) + ) + ) + ), + c.setLocal("pSConst", + c.i32_add( + c.i32_const(SCONST), + c.i32_mul( + c.getLocal("totalBits"), + c.i32_const(n8f) + ) + ) + ), - const zsquared = c.i32_const(module.alloc(f2size)); - const ysquared = c.i32_const(module.alloc(f2size)); - const ztsquared = c.i32_const(module.alloc(f2size)); - const t0 = c.i32_const(module.alloc(f2size)); - const t2 = c.i32_const(module.alloc(f2size)); - const t3 = c.i32_const(module.alloc(f2size)); - const t4 = c.i32_const(module.alloc(f2size)); - const t5 = c.i32_const(module.alloc(f2size)); - const t6 = c.i32_const(module.alloc(f2size)); - const t7 = c.i32_const(module.alloc(f2size)); - const t8 = c.i32_const(module.alloc(f2size)); - f.addCode( + c.call( fPrefix + "_copy", c.getLocal("first"), W), + c.setLocal("i", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("i"), + c.getLocal("n") + ) + ), - // zsquared = r.z.square(); - c.call(f2mPrefix + "_square", Rz, zsquared), + c.setLocal( + "idx1", + c.i32_add( + c.getLocal("pBuff1"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), - // ysquared = q.y.square(); - c.call(f2mPrefix + "_square", Qy, ysquared), + c.setLocal( + "idx2", + c.i32_add( + c.getLocal("pBuff2"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), - // t0 = zsquared * q.x; - c.call(f2mPrefix + "_mul", zsquared, Qx, t0), + c.call( + opGtimesF, + c.getLocal("idx2"), + W, + U + ), - // t1 = ((q.y + r.z).square() - ysquared - zsquared) * zsquared; - c.call(f2mPrefix + "_add", Qy, Rz, t1), - c.call(f2mPrefix + "_square", t1, t1), - c.call(f2mPrefix + "_sub", t1, ysquared, t1), - c.call(f2mPrefix + "_sub", t1, zsquared, t1), - c.call(f2mPrefix + "_mul", t1, zsquared, t1), + c.call( + gPrefix + "_sub", + c.getLocal("idx1"), + U, + c.getLocal("idx2"), + ), - // t2 = t0 - r.x; - c.call(f2mPrefix + "_sub", t0, Rx, t2), + c.call( + opGtimesF, + c.getLocal("idx2"), + c.getLocal("pSConst"), + c.getLocal("idx2") + ), + + c.call( + opGtimesF, + c.getLocal("idx1"), + c.getLocal("pShiftToM"), + c.getLocal("idx1") + ), + + c.call( + gPrefix + "_sub", + U, + c.getLocal("idx1"), + c.getLocal("idx1") + ), - // t3 = t2.square(); - c.call(f2mPrefix + "_square", t2, t3), + c.call( + opGtimesF, + c.getLocal("idx1"), + c.getLocal("pSConst"), + c.getLocal("idx1") + ), - // t4 = t3 + t3; - c.call(f2mPrefix + "_add", t3, t3, t4), + c.call( + fPrefix + "_mul", + W, + c.getLocal("inc"), + W + ), - // t4 = t4 + t4; - c.call(f2mPrefix + "_add", t4, t4, t4), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) + ); + } - // t5 = t4 * t2; - c.call(f2mPrefix + "_mul", t4, t2, t5), - // t6 = t1 - r.y - r.y; - c.call(f2mPrefix + "_sub", t1, Ry, t6), - c.call(f2mPrefix + "_sub", t6, Ry, t6), - // t9 = t6 * q.x; - c.call(f2mPrefix + "_mul", t6, Qx, t9), + function buildPrepareLagrangeEvaluation() { + const f = module.addFunction(prefix+"_prepareLagrangeEvaluation"); + f.addParam("pBuff1", "i32"); + f.addParam("pBuff2", "i32"); + f.addParam("n", "i32"); + f.addParam("first", "i32"); + f.addParam("inc", "i32"); + f.addParam("totalBits", "i32"); + f.addLocal("idx1", "i32"); + f.addLocal("idx2", "i32"); + f.addLocal("i", "i32"); + f.addLocal("pShiftToM", "i32"); + f.addLocal("pSConst", "i32"); - // t7 = t4 * r.x; - c.call(f2mPrefix + "_mul", t4, Rx, t7), + const c = f.getCodeBuilder(); - // r.x = t6.square() - t5 - t7 - t7; - c.call(f2mPrefix + "_square", t6, Rx), - c.call(f2mPrefix + "_sub", Rx, t5, Rx), - c.call(f2mPrefix + "_sub", Rx, t7, Rx), - c.call(f2mPrefix + "_sub", Rx, t7, Rx), + const W = c.i32_const(module.alloc(n8f)); + const U = c.i32_const(module.alloc(n8g)); - // r.z = (r.z + t2).square() - zsquared - t3; - c.call(f2mPrefix + "_add", Rz, t2, Rz), - c.call(f2mPrefix + "_square", Rz, Rz), - c.call(f2mPrefix + "_sub", Rz, zsquared, Rz), - c.call(f2mPrefix + "_sub", Rz, t3, Rz), + f.addCode( - // t10 = q.y + r.z; - c.call(f2mPrefix + "_add", Qy, Rz, t10), + c.setLocal("pShiftToM", + c.i32_add( + c.i32_const(SHIFT_TO_M), + c.i32_mul( + c.getLocal("totalBits"), + c.i32_const(n8f) + ) + ) + ), + c.setLocal("pSConst", + c.i32_add( + c.i32_const(SCONST), + c.i32_mul( + c.getLocal("totalBits"), + c.i32_const(n8f) + ) + ) + ), - // t8 = (t7 - r.x) * t6; - c.call(f2mPrefix + "_sub", t7, Rx, t8), - c.call(f2mPrefix + "_mul", t8, t6, t8), - // t0 = r.y * t5; - c.call(f2mPrefix + "_mul", Ry, t5, t0), + c.call( fPrefix + "_copy", c.getLocal("first"), W), + c.setLocal("i", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("i"), + c.getLocal("n") + ) + ), - // t0 = t0 + t0; - c.call(f2mPrefix + "_add", t0, t0, t0), + c.setLocal( + "idx1", + c.i32_add( + c.getLocal("pBuff1"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), - // r.y = t8 - t0; - c.call(f2mPrefix + "_sub", t8, t0, Ry), + c.setLocal( + "idx2", + c.i32_add( + c.getLocal("pBuff2"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), - // t10 = t10.square() - ysquared; - c.call(f2mPrefix + "_square", t10, t10), - c.call(f2mPrefix + "_sub", t10, ysquared, t10), - // ztsquared = r.z.square(); - c.call(f2mPrefix + "_square", Rz, ztsquared), + c.call( + opGtimesF, + c.getLocal("idx1"), + c.getLocal("pShiftToM"), + U + ), - // t10 = t10 - ztsquared; - c.call(f2mPrefix + "_sub", t10, ztsquared, t10), + c.call( + gPrefix + "_sub", + c.getLocal("idx2"), + U, + U + ), - // t9 = t9 + t9 - t10; - c.call(f2mPrefix + "_add", t9, t9, t9), - c.call(f2mPrefix + "_sub", t9, t10, t9), + c.call( + gPrefix + "_sub", + c.getLocal("idx1"), + c.getLocal("idx2"), + c.getLocal("idx2"), + ), - // t10 = r.z + r.z; - c.call(f2mPrefix + "_add", Rz, Rz, t10), + c.call( + opGtimesF, + U, + c.getLocal("pSConst"), + c.getLocal("idx1"), + ), - // t6 = -t6; - c.call(f2mPrefix + "_neg", t6, t6), + c.call( + opGtimesF, + c.getLocal("idx2"), + W, + c.getLocal("idx2"), + ), - // t1 = t6 + t6; - c.call(f2mPrefix + "_add", t6, t6, t1), + c.call( + fPrefix + "_mul", + W, + c.getLocal("inc"), + W + ), + + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) ); } - - function buildPrepareG2() { - const f = module.addFunction(prefix+ "_prepareG2"); - f.addParam("pQ", "i32"); - f.addParam("ppreQ", "i32"); - f.addLocal("pCoef", "i32"); + function buildFFTMix() { + const f = module.addFunction(prefix+"_fftMix"); + f.addParam("pBuff", "i32"); + f.addParam("n", "i32"); + f.addParam("exp", "i32"); + f.addLocal("nGroups", "i32"); + f.addLocal("nPerGroup", "i32"); + f.addLocal("nPerGroupDiv2", "i32"); + f.addLocal("pairOffset", "i32"); + f.addLocal("idx1", "i32"); + f.addLocal("idx2", "i32"); f.addLocal("i", "i32"); + f.addLocal("j", "i32"); + f.addLocal("pwm", "i32"); const c = f.getCodeBuilder(); + const W = c.i32_const(module.alloc(n8f)); + const T = c.i32_const(module.alloc(n8g)); + const U = c.i32_const(module.alloc(n8g)); - const Q = c.getLocal("pQ"); + f.addCode( + c.setLocal("nPerGroup", c.i32_shl(c.i32_const(1), c.getLocal("exp"))), + c.setLocal("nPerGroupDiv2", c.i32_shr_u(c.getLocal("nPerGroup"), c.i32_const(1))), + c.setLocal("nGroups", c.i32_shr_u(c.getLocal("n"), c.getLocal("exp"))), + c.setLocal("pairOffset", c.i32_mul(c.getLocal("nPerGroupDiv2"), c.i32_const(n8g))), + c.setLocal("pwm", + c.i32_add( + c.i32_const(ROOTs), + c.i32_mul( + c.getLocal("exp"), + c.i32_const(n8f) + ) + ) + ), + c.setLocal("i", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("i"), + c.getLocal("nGroups") + ) + ), + c.call( fPrefix + "_one", W), + c.setLocal("j", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("j"), + c.getLocal("nPerGroupDiv2") + ) + ), - const pR = module.alloc(f2size*3); - const R = c.i32_const(pR); + c.setLocal( + "idx1", + c.i32_add( + c.getLocal("pBuff"), + c.i32_mul( + c.i32_add( + c.i32_mul( + c.getLocal("i"), + c.getLocal("nPerGroup") + ), + c.getLocal("j") + ), + c.i32_const(n8g) + ) + ) + ), + + c.setLocal( + "idx2", + c.i32_add( + c.getLocal("idx1"), + c.getLocal("pairOffset") + ) + ), - const base = c.getLocal("ppreQ"); + c.call( + opGtimesF, + c.getLocal("idx2"), + W, + T + ), - f.addCode( - c.call(g2mPrefix + "_normalize", Q, base), - c.if( - c.call(g2mPrefix + "_isZero", base), - c.ret([]) - ), - c.call(g2mPrefix + "_copy", base, R), - c.setLocal("pCoef", c.i32_add(c.getLocal("ppreQ"), c.i32_const(f2size*3))), - ); + c.call( + gPrefix + "_copy", + c.getLocal("idx1"), + U + ), - f.addCode( - c.setLocal("i", c.i32_const(ateLoopBitBytes.length-2)), - c.block(c.loop( + c.call( + gPrefix + "_add", + U, + T, + c.getLocal("idx1"), + ), - c.call(prefix + "_prepDblStep", R, c.getLocal("pCoef")), - c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + c.call( + gPrefix + "_sub", + U, + T, + c.getLocal("idx2"), + ), - c.if( - c.i32_load8_s(c.getLocal("i"), pAteLoopBitBytes), - [ - ...c.call(prefix + "_prepAddStep", R, base, c.getLocal("pCoef")), - ...c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), - ] - ), - c.br_if(1, c.i32_eqz ( c.getLocal("i") )), - c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), + c.call( + fPrefix + "_mul", + W, + c.getLocal("pwm"), + W, + ), + c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), + c.br(0) + )), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), c.br(0) )) ); } - function buildF6Mul1() { - const f = module.addFunction(f6mPrefix+ "_mul1"); - f.addParam("pA", "i32"); // F6 - f.addParam("pC1", "i32"); // F2 - f.addParam("pR", "i32"); // F6 + // Reverse all and multiply by factor + function buildFFTFinal() { + const f = module.addFunction(prefix+"_fftFinal"); + f.addParam("pBuff", "i32"); + f.addParam("n", "i32"); + f.addParam("factor", "i32"); + f.addLocal("idx1", "i32"); + f.addLocal("idx2", "i32"); + f.addLocal("i", "i32"); + f.addLocal("ndiv2", "i32"); const c = f.getCodeBuilder(); - const A_c0 = c.getLocal("pA"); - const A_c1 = c.i32_add(c.getLocal("pA"), c.i32_const(f1size*2)); - const A_c2 = c.i32_add(c.getLocal("pA"), c.i32_const(f1size*4)); - - const c1 = c.getLocal("pC1"); - - const t1 = c.getLocal("pR"); - const t2 = c.i32_add(c.getLocal("pR"), c.i32_const(f1size*2)); - const b_b = c.i32_add(c.getLocal("pR"), c.i32_const(f1size*4)); - - const Ac0_Ac1 = c.i32_const(module.alloc(f1size*2)); - const Ac1_Ac2 = c.i32_const(module.alloc(f1size*2)); + const T = c.i32_const(module.alloc(n8g)); f.addCode( + c.setLocal("ndiv2", c.i32_shr_u(c.getLocal("n"), c.i32_const(1))), + c.if( + c.i32_and( + c.getLocal("n"), + c.i32_const(1) + ), + c.call( + opGtimesF, + c.i32_add( + c.getLocal("pBuff"), + c.i32_mul( + c.getLocal("ndiv2"), + c.i32_const(n8g) + ) + ), + c.getLocal("factor"), + c.i32_add( + c.getLocal("pBuff"), + c.i32_mul( + c.getLocal("ndiv2"), + c.i32_const(n8g) + ) + ), + ), + ), + c.setLocal("i", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_ge_u( + c.getLocal("i"), + c.getLocal("ndiv2") + ) + ), - c.call(f2mPrefix + "_add", A_c0, A_c1, Ac0_Ac1), - c.call(f2mPrefix + "_add", A_c1, A_c2, Ac1_Ac2), + c.setLocal( + "idx1", + c.i32_add( + c.getLocal("pBuff"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(n8g) + ) + ) + ), - // let b_b = self.c1 * c1; - c.call(f2mPrefix + "_mul", A_c1, c1, b_b), + c.setLocal( + "idx2", + c.i32_add( + c.getLocal("pBuff"), + c.i32_mul( + c.i32_sub( + c.i32_sub( + c.getLocal("n"), + c.i32_const(1) + ), + c.getLocal("i") + ), + c.i32_const(n8g) + ) + ) + ), - // let t1 = (self.c1 + self.c2) * c1 - b_b; - c.call(f2mPrefix + "_mul", Ac1_Ac2, c1, t1), - c.call(f2mPrefix + "_sub", t1, b_b, t1), + c.call( + opGtimesF, + c.getLocal("idx2"), + c.getLocal("factor"), + T + ), - // let t1 = t1.mul_by_nonresidue(); - c.call(f2mPrefix + "_mulNR", t1, t1), + c.call( + opGtimesF, + c.getLocal("idx1"), + c.getLocal("factor"), + c.getLocal("idx2"), + ), - // let t2 = (self.c0 + self.c1) * c1 - b_b; - c.call(f2mPrefix + "_mul", Ac0_Ac1, c1, t2), - c.call(f2mPrefix + "_sub", t2, b_b, t2), + c.call( + gPrefix + "_copy", + T, + c.getLocal("idx1"), + ), + + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) ); } - buildF6Mul1(); - - function buildF6Mul01() { - const f = module.addFunction(f6mPrefix+ "_mul01"); - f.addParam("pA", "i32"); // F6 - f.addParam("pC0", "i32"); // F2 - f.addParam("pC1", "i32"); // F2 - f.addParam("pR", "i32"); // F6 - - const c = f.getCodeBuilder(); - - const A_c0 = c.getLocal("pA"); - const A_c1 = c.i32_add(c.getLocal("pA"), c.i32_const(f1size*2)); - const A_c2 = c.i32_add(c.getLocal("pA"), c.i32_const(f1size*4)); - const c0 = c.getLocal("pC0"); - const c1 = c.getLocal("pC1"); - - const t1 = c.getLocal("pR"); - const t2 = c.i32_add(c.getLocal("pR"), c.i32_const(f1size*2)); - const t3 = c.i32_add(c.getLocal("pR"), c.i32_const(f1size*4)); - - const a_a = c.i32_const(module.alloc(f1size*2)); - const b_b = c.i32_const(module.alloc(f1size*2)); - const Ac0_Ac1 = c.i32_const(module.alloc(f1size*2)); - const Ac0_Ac2 = c.i32_const(module.alloc(f1size*2)); - - f.addCode( - // let a_a = self.c0 * c0; - c.call(f2mPrefix + "_mul", A_c0, c0, a_a), - - // let b_b = self.c1 * c1; - c.call(f2mPrefix + "_mul", A_c1, c1, b_b), - - - c.call(f2mPrefix + "_add", A_c0, A_c1, Ac0_Ac1), - c.call(f2mPrefix + "_add", A_c0, A_c2, Ac0_Ac2), - - // let t1 = (self.c1 + self.c2) * c1 - b_b; - c.call(f2mPrefix + "_add", A_c1, A_c2, t1), - c.call(f2mPrefix + "_mul", t1, c1, t1), - c.call(f2mPrefix + "_sub", t1, b_b, t1), - - // let t1 = t1.mul_by_nonresidue() + a_a; - c.call(f2mPrefix + "_mulNR", t1, t1), - c.call(f2mPrefix + "_add", t1, a_a, t1), - - // let t2 = (c0 + c1) * (self.c0 + self.c1) - a_a - b_b; - c.call(f2mPrefix + "_add", c0, c1, t2), - c.call(f2mPrefix + "_mul", t2, Ac0_Ac1, t2), - c.call(f2mPrefix + "_sub", t2, a_a, t2), - c.call(f2mPrefix + "_sub", t2, b_b, t2), + buildRev(); + buildReversePermutation(); + buildFinalInverse(); + buildRawFFT(); + buildLog2(); + buildFFT(); + buildIFFT(); + buildFFTJoin(); + buildFFTJoinExt(); + buildFFTJoinExtInv(); + buildFFTMix(); + buildFFTFinal(); + buildPrepareLagrangeEvaluation(); - // let t3 = (self.c0 + self.c2) * c0 - a_a + b_b; - c.call(f2mPrefix + "_mul", Ac0_Ac2, c0, t3), - c.call(f2mPrefix + "_sub", t3, a_a, t3), - c.call(f2mPrefix + "_add", t3, b_b, t3), + module.exportFunction(prefix+"__reversePermutation"); + module.exportFunction(prefix+"_fft"); + module.exportFunction(prefix+"_ifft"); + module.exportFunction(prefix+"_rawfft"); + module.exportFunction(prefix+"_fftJoin"); + module.exportFunction(prefix+"_fftJoinExt"); + module.exportFunction(prefix+"_fftJoinExtInv"); + module.exportFunction(prefix+"_fftMix"); + module.exportFunction(prefix+"_fftFinal"); + module.exportFunction(prefix+"_prepareLagrangeEvaluation"); +}; - ); - } - buildF6Mul01(); +/* + Copyright 2019 0KIMS association. + This file is part of wasmsnark (Web Assembly zkSnark Prover). - function buildF12Mul014() { + wasmsnark 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. - const f = module.addFunction(ftmPrefix+ "_mul014"); - f.addParam("pA", "i32"); // F12 - f.addParam("pC0", "i32"); // F2 - f.addParam("pC1", "i32"); // F2 - f.addParam("pC4", "i32"); // F2 - f.addParam("pR", "i32"); // F12 + wasmsnark 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. - const c = f.getCodeBuilder(); + You should have received a copy of the GNU General Public License + along with wasmsnark. If not, see . +*/ +var build_pol = function buildPol(module, prefix, prefixField) { - const A_c0 = c.getLocal("pA"); - const A_c1 = c.i32_add(c.getLocal("pA"), c.i32_const(f1size*6)); + const n64 = module.modules[prefixField].n64; + const n8 = n64*8; - const c0 = c.getLocal("pC0"); - const c1 = c.getLocal("pC1"); - const c4 = c.getLocal("pC4"); - const aa = c.i32_const(module.alloc(f1size*6)); - const bb = c.i32_const(module.alloc(f1size*6)); - const o = c.i32_const(module.alloc(f1size*2)); + function buildZero() { + const f = module.addFunction(prefix+"_zero"); + f.addParam("px", "i32"); + f.addParam("n", "i32"); + f.addLocal("lastp", "i32"); + f.addLocal("p", "i32"); - const R_c0 = c.getLocal("pR"); - const R_c1 = c.i32_add(c.getLocal("pR"), c.i32_const(f1size*6)); + const c = f.getCodeBuilder(); f.addCode( - // let aa = self.c0.mul_by_01(c0, c1); - c.call(f6mPrefix + "_mul01", A_c0, c0, c1, aa), - - // let bb = self.c1.mul_by_1(c4); - c.call(f6mPrefix + "_mul1", A_c1, c4, bb), + c.setLocal("p", c.getLocal("px")), + c.setLocal( + "lastp", + c.i32_add( + c.getLocal("px"), + c.i32_mul( + c.getLocal("n"), + c.i32_const(n8) + ) + ) + ), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("p"), + c.getLocal("lastp") + ) + ), + c.call(prefixField + "_zero", c.getLocal("p")), + c.setLocal("p", c.i32_add(c.getLocal("p"), c.i32_const(n8))), + c.br(0) + )) + ); + } - // let o = c1 + c4; - c.call(f2mPrefix + "_add", c1, c4, o), + function buildConstructLC() { + const f = module.addFunction(prefix+"_constructLC"); + f.addParam("ppolynomials", "i32"); + f.addParam("psignals", "i32"); + f.addParam("nSignals", "i32"); + f.addParam("pres", "i32"); + f.addLocal("i", "i32"); + f.addLocal("j", "i32"); + f.addLocal("pp", "i32"); + f.addLocal("ps", "i32"); + f.addLocal("pd", "i32"); + f.addLocal("ncoefs", "i32"); - // let c1 = self.c1 + self.c0; - c.call(f6mPrefix + "_add", A_c1, A_c0, R_c1), + const c = f.getCodeBuilder(); - // let c1 = c1.mul_by_01(c0, &o); - c.call(f6mPrefix + "_mul01", R_c1, c0, o, R_c1), + const aux = c.i32_const(module.alloc(n8)); - // let c1 = c1 - aa - bb; - c.call(f6mPrefix + "_sub", R_c1, aa, R_c1), - c.call(f6mPrefix + "_sub", R_c1, bb, R_c1), + f.addCode( + c.setLocal("i", c.i32_const(0)), + c.setLocal("pp", c.getLocal("ppolynomials")), + c.setLocal("ps", c.getLocal("psignals")), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("i"), + c.getLocal("nSignals") + ) + ), - // let c0 = bb; - c.call(f6mPrefix + "_copy", bb, R_c0), + c.setLocal("ncoefs", c.i32_load(c.getLocal("pp"))), + c.setLocal("pp", c.i32_add(c.getLocal("pp"), c.i32_const(4))), - // let c0 = c0.mul_by_nonresidue(); - c.call(f6mPrefix + "_mulNR", R_c0, R_c0), + c.setLocal("j", c.i32_const(0)), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("j"), + c.getLocal("ncoefs") + ) + ), - // let c0 = c0 + aa; - c.call(f6mPrefix + "_add", R_c0, aa, R_c0), - ); - } - buildF12Mul014(); + c.setLocal( + "pd", + c.i32_add( + c.getLocal("pres"), + c.i32_mul( + c.i32_load(c.getLocal("pp")), + c.i32_const(n8) + ) + ) + ), + c.setLocal("pp", c.i32_add(c.getLocal("pp"), c.i32_const(4))), - function buildELL() { - const f = module.addFunction(prefix+ "_ell"); - f.addParam("pP", "i32"); - f.addParam("pCoefs", "i32"); - f.addParam("pF", "i32"); - const c = f.getCodeBuilder(); + c.call( + prefixField + "_mul", + c.getLocal("ps"), + c.getLocal("pp"), + aux + ), - const Px = c.getLocal("pP"); - const Py = c.i32_add(c.getLocal("pP"), c.i32_const(n8q)); + c.call( + prefixField + "_add", + aux, + c.getLocal("pd"), + c.getLocal("pd") + ), - const F = c.getLocal("pF"); + c.setLocal("pp", c.i32_add(c.getLocal("pp"), c.i32_const(n8))), + c.setLocal("j", c.i32_add(c.getLocal("j"), c.i32_const(1))), + c.br(0) + )), - const coef0_0 = c.getLocal("pCoefs"); - const coef0_1 = c.i32_add(c.getLocal("pCoefs"), c.i32_const(f1size)); - const coef1_0 = c.i32_add(c.getLocal("pCoefs"), c.i32_const(f1size*2)); - const coef1_1 = c.i32_add(c.getLocal("pCoefs"), c.i32_const(f1size*3)); - const coef2 = c.i32_add(c.getLocal("pCoefs"), c.i32_const(f1size*4)); + c.setLocal("ps", c.i32_add(c.getLocal("ps"), c.i32_const(n8))), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) + ); - const pc0 = module.alloc(f1size*2); - const c0 = c.i32_const(pc0); - const c0_c0 = c.i32_const(pc0); - const c0_c1 = c.i32_const(pc0+f1size); + } - const pc1 = module.alloc(f1size*2); - const c1 = c.i32_const(pc1); - const c1_c0 = c.i32_const(pc1); - const c1_c1 = c.i32_const(pc1+f1size); - f.addCode( - // let mut c0 = coeffs.0; - // let mut c1 = coeffs.1; - // - // c0.c0 *= p.y; - // c0.c1 *= p.y; - // - // c1.c0 *= p.x; - // c1.c1 *= p.x; - // - // f.mul_by_014(&coeffs.2, &c1, &c0) + buildZero(); + buildConstructLC(); - c.call(f1mPrefix + "_mul", coef0_0, Py, c0_c0), - c.call(f1mPrefix + "_mul", coef0_1, Py, c0_c1), - c.call(f1mPrefix + "_mul", coef1_0, Px, c1_c0), - c.call(f1mPrefix + "_mul", coef1_1, Px, c1_c1), - c.call(ftmPrefix + "_mul014", F, coef2, c1, c0, F), + module.exportFunction(prefix + "_zero"); + module.exportFunction(prefix + "_constructLC"); - ); + return prefix; - } - buildELL(); - function buildMillerLoop() { - const f = module.addFunction(prefix+ "_millerLoop"); - f.addParam("ppreP", "i32"); - f.addParam("ppreQ", "i32"); - f.addParam("r", "i32"); - f.addLocal("pCoef", "i32"); - f.addLocal("i", "i32"); - const c = f.getCodeBuilder(); - const preP = c.getLocal("ppreP"); +}; - const coefs = c.getLocal("pCoef"); +var build_qap = function buildQAP(module, prefix, prefixField) { - const F = c.getLocal("r"); + const n64 = module.modules[prefixField].n64; + const n8 = n64*8; - f.addCode( - c.call(ftmPrefix + "_one", F), + function buildBuildABC() { + const f = module.addFunction(prefix+"_buildABC"); + f.addParam("pCoefs", "i32"); + f.addParam("nCoefs", "i32"); + f.addParam("pWitness", "i32"); + f.addParam("pA", "i32"); + f.addParam("pB", "i32"); + f.addParam("pC", "i32"); + f.addParam("offsetOut", "i32"); + f.addParam("nOut", "i32"); + f.addParam("offsetWitness", "i32"); + f.addParam("nWitness", "i32"); + f.addLocal("it", "i32"); + f.addLocal("ita", "i32"); + f.addLocal("itb", "i32"); + f.addLocal("last", "i32"); + f.addLocal("m", "i32"); + f.addLocal("c", "i32"); + f.addLocal("s", "i32"); + f.addLocal("pOut", "i32"); - c.if( - c.call(g1mPrefix + "_isZero", preP), - c.ret([]) - ), - c.if( - c.call(g1mPrefix + "_isZero", c.getLocal("ppreQ")), - c.ret([]) - ), - c.setLocal("pCoef", c.i32_add( c.getLocal("ppreQ"), c.i32_const(f2size*3))), + const c = f.getCodeBuilder(); - c.setLocal("i", c.i32_const(ateLoopBitBytes.length-2)), + const aux = c.i32_const(module.alloc(n8)); + + f.addCode( + + // Set output a and b to 0 + c.setLocal("ita", c.getLocal("pA")), + c.setLocal("itb", c.getLocal("pB")), + c.setLocal( + "last", + c.i32_add( + c.getLocal("pA"), + c.i32_mul( + c.getLocal("nOut"), + c.i32_const(n8) + ) + ) + ), c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("ita"), + c.getLocal("last") + ) + ), + c.call(prefixField + "_zero", c.getLocal("ita")), + c.call(prefixField + "_zero", c.getLocal("itb")), + c.setLocal("ita", c.i32_add(c.getLocal("ita"), c.i32_const(n8))), + c.setLocal("itb", c.i32_add(c.getLocal("itb"), c.i32_const(n8))), + c.br(0) + )), - c.call(prefix + "_ell", preP, coefs, F), - c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + c.setLocal("it", c.getLocal("pCoefs")), + c.setLocal( + "last", + c.i32_add( + c.getLocal("pCoefs"), + c.i32_mul( + c.getLocal("nCoefs"), + c.i32_const(n8+12) + ) + ) + ), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("it"), + c.getLocal("last") + ) + ), + c.setLocal( + "s", + c.i32_load(c.getLocal("it"), 8) + ), + c.if( + c.i32_or( + c.i32_lt_u( + c.getLocal("s"), + c.getLocal("offsetWitness"), + ), + c.i32_ge_u( + c.getLocal("s"), + c.i32_add( + c.getLocal("offsetWitness"), + c.getLocal("nWitness"), + ) + ) + ), + [ + ...c.setLocal("it", c.i32_add(c.getLocal("it"), c.i32_const(n8+12))), + ...c.br(1) + ] + ), + c.setLocal( + "m", + c.i32_load(c.getLocal("it")) + ), c.if( - c.i32_load8_s(c.getLocal("i"), pAteLoopBitBytes), + c.i32_eq(c.getLocal("m"), c.i32_const(0)), + c.setLocal("pOut", c.getLocal("pA")), + c.if( + c.i32_eq(c.getLocal("m"), c.i32_const(1)), + c.setLocal("pOut", c.getLocal("pB")), + [ + ...c.setLocal("it", c.i32_add(c.getLocal("it"), c.i32_const(n8+12))), + ...c.br(1) + ] + ) + ), + c.setLocal( + "c", + c.i32_load(c.getLocal("it"), 4) + ), + c.if( + c.i32_or( + c.i32_lt_u( + c.getLocal("c"), + c.getLocal("offsetOut"), + ), + c.i32_ge_u( + c.getLocal("c"), + c.i32_add( + c.getLocal("offsetOut"), + c.getLocal("nOut"), + ) + ) + ), [ - ...c.call(prefix + "_ell", preP, coefs, F), - ...c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + ...c.setLocal("it", c.i32_add(c.getLocal("it"), c.i32_const(n8+12))), + ...c.br(1) ] ), - c.call(ftmPrefix + "_square", F, F), + c.setLocal( + "pOut", + c.i32_add( + c.getLocal("pOut"), + c.i32_mul( + c.i32_sub( + c.getLocal("c"), + c.getLocal("offsetOut") + ), + c.i32_const(n8) + ) + ) + ), + c.call( + prefixField + "_mul", + c.i32_add( + c.getLocal("pWitness"), + c.i32_mul( + c.i32_sub(c.getLocal("s"), c.getLocal("offsetWitness")), + c.i32_const(n8) + ) + ), + c.i32_add( c.getLocal("it"), c.i32_const(12)), + aux + ), + c.call( + prefixField + "_add", + c.getLocal("pOut"), + aux, + c.getLocal("pOut"), + ), + c.setLocal("it", c.i32_add(c.getLocal("it"), c.i32_const(n8+12))), + c.br(0) + )), - c.br_if(1, c.i32_eq ( c.getLocal("i"), c.i32_const(1) )), - c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), + c.setLocal("ita", c.getLocal("pA")), + c.setLocal("itb", c.getLocal("pB")), + c.setLocal("it", c.getLocal("pC")), + c.setLocal( + "last", + c.i32_add( + c.getLocal("pA"), + c.i32_mul( + c.getLocal("nOut"), + c.i32_const(n8) + ) + ) + ), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("ita"), + c.getLocal("last") + ) + ), + c.call( + prefixField + "_mul", + c.getLocal("ita"), + c.getLocal("itb"), + c.getLocal("it") + ), + c.setLocal("ita", c.i32_add(c.getLocal("ita"), c.i32_const(n8))), + c.setLocal("itb", c.i32_add(c.getLocal("itb"), c.i32_const(n8))), + c.setLocal("it", c.i32_add(c.getLocal("it"), c.i32_const(n8))), c.br(0) )), - c.call(prefix + "_ell", preP, coefs, F), ); - - - { - f.addCode( - c.call(ftmPrefix + "_conjugate", F, F), - ); - } } - - function buildFrobeniusMap(n) { - const F12 = [ - [ - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - ], - [ - [1n, 0n], - [3850754370037169011952147076051364057158807420970682438676050522613628423219637725072182697113062777891589506424760n, 151655185184498381465642749684540099398075398968325446656007613510403227271200139370504932015952886146304766135027n], - [793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620351n, 0n], - [2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530n, 1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257n], - [793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350n, 0n], - [3125332594171059424908108096204648978570118281977575435832422631601824034463382777937621250592425535493320683825557n, 877076961050607968509681729531255177986764537961432449499635504522207616027455086505066378536590128544573588734230n], - [4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559786n, 0n], - [151655185184498381465642749684540099398075398968325446656007613510403227271200139370504932015952886146304766135027n, 3850754370037169011952147076051364057158807420970682438676050522613628423219637725072182697113062777891589506424760n], - [4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436n, 0n], - [1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257n, 2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530n], - [4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939437n, 0n], - [877076961050607968509681729531255177986764537961432449499635504522207616027455086505066378536590128544573588734230n, 3125332594171059424908108096204648978570118281977575435832422631601824034463382777937621250592425535493320683825557n], - ] - ]; - - const F6 = [ - [ - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - [1n, 0n], - ], - [ - [1n, 0n], - [0n, 4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436n], - [793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350n, 0n], - [0n, 1n], - [4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436n, 0n], - [0n, 793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350n], - ], - [ - [1n, 0n], - [4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939437n, 0n], - [4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436n, 0n], - [4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559786n, 0n], - [793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350n, 0n], - [793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620351n, 0n], - ] - ]; - - const f = module.addFunction(ftmPrefix + "_frobeniusMap"+n); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + function buildJoinABC() { + const f = module.addFunction(prefix+"_joinABC"); + f.addParam("pA", "i32"); + f.addParam("pB", "i32"); + f.addParam("pC", "i32"); + f.addParam("n", "i32"); + f.addParam("pP", "i32"); + f.addLocal("ita", "i32"); + f.addLocal("itb", "i32"); + f.addLocal("itc", "i32"); + f.addLocal("itp", "i32"); + f.addLocal("last", "i32"); const c = f.getCodeBuilder(); - for (let i=0; i<6; i++) { - const X = (i==0) ? c.getLocal("x") : c.i32_add(c.getLocal("x"), c.i32_const(i*f2size)); - const Xc0 = X; - const Xc1 = c.i32_add(c.getLocal("x"), c.i32_const(i*f2size + f1size)); - const R = (i==0) ? c.getLocal("r") : c.i32_add(c.getLocal("r"), c.i32_const(i*f2size)); - const Rc0 = R; - const Rc1 = c.i32_add(c.getLocal("r"), c.i32_const(i*f2size + f1size)); - const coef = mul2(F12[Math.floor(i/3)][n%12] , F6[i%3][n%6]); - const pCoef = module.alloc([ - ...utils$1.bigInt2BytesLE(toMontgomery(coef[0]), n8q), - ...utils$1.bigInt2BytesLE(toMontgomery(coef[1]), n8q), - ]); - if (n%2 == 1) { - f.addCode( - c.call(f1mPrefix + "_copy", Xc0, Rc0), - c.call(f1mPrefix + "_neg", Xc1, Rc1), - c.call(f2mPrefix + "_mul", R, c.i32_const(pCoef), R), - ); - } else { - f.addCode(c.call(f2mPrefix + "_mul", X, c.i32_const(pCoef), R)); - } - } - - function mul2(a, b) { - const ac0 = a[0]; - const ac1 = a[1]; - const bc0 = b[0]; - const bc1 = b[1]; - const res = [ - (ac0 * bc0 - (ac1 * bc1)) % q, - (ac0 * bc1 + (ac1 * bc0)) % q, - ]; - if (isNegative$1(res[0])) res[0] = res[0] + q; - return res; - } + const aux = c.i32_const(module.alloc(n8)); + f.addCode( + c.setLocal("ita", c.getLocal("pA")), + c.setLocal("itb", c.getLocal("pB")), + c.setLocal("itc", c.getLocal("pC")), + c.setLocal("itp", c.getLocal("pP")), + c.setLocal( + "last", + c.i32_add( + c.getLocal("pA"), + c.i32_mul( + c.getLocal("n"), + c.i32_const(n8) + ) + ) + ), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("ita"), + c.getLocal("last") + ) + ), + c.call( + prefixField + "_mul", + c.getLocal("ita"), + c.getLocal("itb"), + aux + ), + c.call( + prefixField + "_sub", + aux, + c.getLocal("itc"), + c.getLocal("itp"), + ), + c.setLocal("ita", c.i32_add(c.getLocal("ita"), c.i32_const(n8))), + c.setLocal("itb", c.i32_add(c.getLocal("itb"), c.i32_const(n8))), + c.setLocal("itc", c.i32_add(c.getLocal("itc"), c.i32_const(n8))), + c.setLocal("itp", c.i32_add(c.getLocal("itp"), c.i32_const(n8))), + c.br(0) + )) + ); } - - function buildCyclotomicSquare() { - const f = module.addFunction(prefix+ "__cyclotomicSquare"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + function buildBatchAdd() { + const f = module.addFunction(prefix+"_batchAdd"); + f.addParam("pa", "i32"); + f.addParam("pb", "i32"); + f.addParam("n", "i32"); + f.addParam("pr", "i32"); + f.addLocal("ita", "i32"); + f.addLocal("itb", "i32"); + f.addLocal("itr", "i32"); + f.addLocal("last", "i32"); const c = f.getCodeBuilder(); - const x0 = c.getLocal("x"); - const x4 = c.i32_add(c.getLocal("x"), c.i32_const(f2size)); - const x3 = c.i32_add(c.getLocal("x"), c.i32_const(2*f2size)); - const x2 = c.i32_add(c.getLocal("x"), c.i32_const(3*f2size)); - const x1 = c.i32_add(c.getLocal("x"), c.i32_const(4*f2size)); - const x5 = c.i32_add(c.getLocal("x"), c.i32_const(5*f2size)); + f.addCode( + c.setLocal("ita", c.getLocal("pa")), + c.setLocal("itb", c.getLocal("pb")), + c.setLocal("itr", c.getLocal("pr")), + c.setLocal( + "last", + c.i32_add( + c.getLocal("pa"), + c.i32_mul( + c.getLocal("n"), + c.i32_const(n8) + ) + ) + ), + c.block(c.loop( + c.br_if( + 1, + c.i32_eq( + c.getLocal("ita"), + c.getLocal("last") + ) + ), + c.call( + prefixField + "_add", + c.getLocal("ita"), + c.getLocal("itb"), + c.getLocal("itr"), + ), + c.setLocal("ita", c.i32_add(c.getLocal("ita"), c.i32_const(n8))), + c.setLocal("itb", c.i32_add(c.getLocal("itb"), c.i32_const(n8))), + c.setLocal("itr", c.i32_add(c.getLocal("itr"), c.i32_const(n8))), + c.br(0) + )) + ); + } - const r0 = c.getLocal("r"); - const r4 = c.i32_add(c.getLocal("r"), c.i32_const(f2size)); - const r3 = c.i32_add(c.getLocal("r"), c.i32_const(2*f2size)); - const r2 = c.i32_add(c.getLocal("r"), c.i32_const(3*f2size)); - const r1 = c.i32_add(c.getLocal("r"), c.i32_const(4*f2size)); - const r5 = c.i32_add(c.getLocal("r"), c.i32_const(5*f2size)); + buildBuildABC(); + buildJoinABC(); + buildBatchAdd(); - const t0 = c.i32_const(module.alloc(f2size)); - const t1 = c.i32_const(module.alloc(f2size)); - const t2 = c.i32_const(module.alloc(f2size)); - const t3 = c.i32_const(module.alloc(f2size)); - const t4 = c.i32_const(module.alloc(f2size)); - const t5 = c.i32_const(module.alloc(f2size)); - const tmp = c.i32_const(module.alloc(f2size)); - const AUX = c.i32_const(module.alloc(f2size)); + module.exportFunction(prefix + "_buildABC"); + module.exportFunction(prefix + "_joinABC"); + module.exportFunction(prefix + "_batchAdd"); + return prefix; - f.addCode( - // // t0 + t1*y = (z0 + z1*y)^2 = a^2 - // tmp = z0 * z1; - // t0 = (z0 + z1) * (z0 + my_Fp6::non_residue * z1) - tmp - my_Fp6::non_residue * tmp; - // t1 = tmp + tmp; - c.call(f2mPrefix + "_mul", x0, x1, tmp), - c.call(f2mPrefix + "_mulNR", x1, t0), - c.call(f2mPrefix + "_add", x0, t0, t0), - c.call(f2mPrefix + "_add", x0, x1, AUX), - c.call(f2mPrefix + "_mul", AUX, t0, t0), - c.call(f2mPrefix + "_mulNR", tmp, AUX), - c.call(f2mPrefix + "_add", tmp, AUX, AUX), - c.call(f2mPrefix + "_sub", t0, AUX, t0), - c.call(f2mPrefix + "_add", tmp, tmp, t1), +}; - // // t2 + t3*y = (z2 + z3*y)^2 = b^2 - // tmp = z2 * z3; - // t2 = (z2 + z3) * (z2 + my_Fp6::non_residue * z3) - tmp - my_Fp6::non_residue * tmp; - // t3 = tmp + tmp; - c.call(f2mPrefix + "_mul", x2, x3, tmp), - c.call(f2mPrefix + "_mulNR", x3, t2), - c.call(f2mPrefix + "_add", x2, t2, t2), - c.call(f2mPrefix + "_add", x2, x3, AUX), - c.call(f2mPrefix + "_mul", AUX, t2, t2), - c.call(f2mPrefix + "_mulNR", tmp, AUX), - c.call(f2mPrefix + "_add", tmp, AUX, AUX), - c.call(f2mPrefix + "_sub", t2, AUX, t2), - c.call(f2mPrefix + "_add", tmp, tmp, t3), +/* + Copyright 2019 0KIMS association. - // // t4 + t5*y = (z4 + z5*y)^2 = c^2 - // tmp = z4 * z5; - // t4 = (z4 + z5) * (z4 + my_Fp6::non_residue * z5) - tmp - my_Fp6::non_residue * tmp; - // t5 = tmp + tmp; - c.call(f2mPrefix + "_mul", x4, x5, tmp), - c.call(f2mPrefix + "_mulNR", x5, t4), - c.call(f2mPrefix + "_add", x4, t4, t4), - c.call(f2mPrefix + "_add", x4, x5, AUX), - c.call(f2mPrefix + "_mul", AUX, t4, t4), - c.call(f2mPrefix + "_mulNR", tmp, AUX), - c.call(f2mPrefix + "_add", tmp, AUX, AUX), - c.call(f2mPrefix + "_sub", t4, AUX, t4), - c.call(f2mPrefix + "_add", tmp, tmp, t5), + This file is part of wasmsnark (Web Assembly zkSnark Prover). - // For A - // z0 = 3 * t0 - 2 * z0 - c.call(f2mPrefix + "_sub", t0, x0, r0), - c.call(f2mPrefix + "_add", r0, r0, r0), - c.call(f2mPrefix + "_add", t0, r0, r0), - // z1 = 3 * t1 + 2 * z1 - c.call(f2mPrefix + "_add", t1, x1, r1), - c.call(f2mPrefix + "_add", r1, r1, r1), - c.call(f2mPrefix + "_add", t1, r1, r1), + wasmsnark 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. - // For B - // z2 = 3 * (xi * t5) + 2 * z2 - c.call(f2mPrefix + "_mul", t5, c.i32_const(pBls12381Twist), AUX), - c.call(f2mPrefix + "_add", AUX, x2, r2), - c.call(f2mPrefix + "_add", r2, r2, r2), - c.call(f2mPrefix + "_add", AUX, r2, r2), - // z3 = 3 * t4 - 2 * z3 - c.call(f2mPrefix + "_sub", t4, x3, r3), - c.call(f2mPrefix + "_add", r3, r3, r3), - c.call(f2mPrefix + "_add", t4, r3, r3), + wasmsnark 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. - // For C - // z4 = 3 * t2 - 2 * z4 - c.call(f2mPrefix + "_sub", t2, x4, r4), - c.call(f2mPrefix + "_add", r4, r4, r4), - c.call(f2mPrefix + "_add", t2, r4, r4), - // z5 = 3 * t3 + 2 * z5 - c.call(f2mPrefix + "_add", t3, x5, r5), - c.call(f2mPrefix + "_add", r5, r5, r5), - c.call(f2mPrefix + "_add", t3, r5, r5), + You should have received a copy of the GNU General Public License + along with wasmsnark. If not, see . +*/ + +var build_applykey = function buildApplyKey(module, fnName, gPrefix, frPrefix, sizeGIn, sizeGOut, sizeF, opGtimesF) { + + const f = module.addFunction(fnName); + f.addParam("pIn", "i32"); + f.addParam("n", "i32"); + f.addParam("pFirst", "i32"); + f.addParam("pInc", "i32"); + f.addParam("pOut", "i32"); + f.addLocal("pOldFree", "i32"); + f.addLocal("i", "i32"); + f.addLocal("pFrom", "i32"); + f.addLocal("pTo", "i32"); - ); - } + const c = f.getCodeBuilder(); + const t = c.i32_const(module.alloc(sizeF)); - function buildCyclotomicExp(exponent, isExpNegative, fnName) { - const exponentNafBytes = naf(exponent).map( (b) => (b==-1 ? 0xFF: b) ); - const pExponentNafBytes = module.alloc(exponentNafBytes); - // const pExponent = module.alloc(utils.bigInt2BytesLE(exponent, n8)); + f.addCode( + c.setLocal("pFrom", c.getLocal("pIn")), + c.setLocal("pTo", c.getLocal("pOut")), + ); - const f = module.addFunction(prefix+ "__cyclotomicExp_"+fnName); - f.addParam("x", "i32"); - f.addParam("r", "i32"); - f.addLocal("bit", "i32"); - f.addLocal("i", "i32"); + // t = first + f.addCode( + c.call( + frPrefix + "_copy", + c.getLocal("pFirst"), + t + ) + ); + f.addCode( + c.setLocal("i", c.i32_const(0)), + c.block(c.loop( + c.br_if(1, c.i32_eq ( c.getLocal("i"), c.getLocal("n") )), - const c = f.getCodeBuilder(); + c.call( + opGtimesF, + c.getLocal("pFrom"), + t, + c.getLocal("pTo") + ), + c.setLocal("pFrom", c.i32_add(c.getLocal("pFrom"), c.i32_const(sizeGIn))), + c.setLocal("pTo", c.i32_add(c.getLocal("pTo"), c.i32_const(sizeGOut))), - const x = c.getLocal("x"); + // t = t* inc + c.call( + frPrefix + "_mul", + t, + c.getLocal("pInc"), + t + ), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) + ); - const res = c.getLocal("r"); + module.exportFunction(fnName); - const inverse = c.i32_const(module.alloc(ftsize)); +}; +const utils$1 = utils$5; - f.addCode( - c.call(ftmPrefix + "_conjugate", x, inverse), - c.call(ftmPrefix + "_one", res), +const buildF1m$1 =build_f1m; +const buildF1$1 =build_f1; +const buildF2m$1 =build_f2m; +const buildF3m$1 =build_f3m; +const buildCurve$1 =build_curve_jacobian_a0; +const buildFFT$1 = build_fft; +const buildPol$1 = build_pol; +const buildQAP$1 = build_qap; +const buildApplyKey$1 = build_applykey; +const { bitLength: bitLength$2, modInv, isOdd: isOdd$1, isNegative: isNegative$2 } = bigint; - c.if( - c.teeLocal("bit", c.i32_load8_s(c.i32_const(exponentNafBytes.length-1), pExponentNafBytes)), - c.if( - c.i32_eq( - c.getLocal("bit"), - c.i32_const(1) - ), - c.call(ftmPrefix + "_mul", res, x, res), - c.call(ftmPrefix + "_mul", res, inverse, res), - ) - ), +var build_bn128 = function buildBN128(module, _prefix) { - c.setLocal("i", c.i32_const(exponentNafBytes.length-2)), - c.block(c.loop( - c.call(prefix + "__cyclotomicSquare", res, res), - c.if( - c.teeLocal("bit", c.i32_load8_s(c.getLocal("i"), pExponentNafBytes)), - c.if( - c.i32_eq( - c.getLocal("bit"), - c.i32_const(1) - ), - c.call(ftmPrefix + "_mul", res, x, res), - c.call(ftmPrefix + "_mul", res, inverse, res), - ) - ), - c.br_if(1, c.i32_eqz ( c.getLocal("i") )), - c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), - c.br(0) - )) - ); + const prefix = _prefix || "bn128"; - if (isExpNegative) { - f.addCode( - c.call(ftmPrefix + "_conjugate", res, res), - ); - } + if (module.modules[prefix]) return prefix; // already builded - } + const q = 21888242871839275222246405745257275088696311157297823662689037894645226208583n; + const r = 21888242871839275222246405745257275088548364400416034343698204186575808495617n; - function buildFinalExponentiation() { - buildCyclotomicSquare(); - buildCyclotomicExp(finalExpZ, finalExpIsNegative, "w0"); - const f = module.addFunction(prefix+ "_finalExponentiation"); - f.addParam("x", "i32"); - f.addParam("r", "i32"); + const n64 = Math.floor((bitLength$2(q - 1n) - 1)/64) +1; + const n8 = n64*8; + const frsize = n8; + const f1size = n8; + const f2size = f1size * 2; + const ftsize = f1size * 12; - const c = f.getCodeBuilder(); + const pr = module.alloc(utils$1.bigInt2BytesLE( r, frsize )); - const elt = c.getLocal("x"); - const res = c.getLocal("r"); - const t0 = c.i32_const(module.alloc(ftsize)); - const t1 = c.i32_const(module.alloc(ftsize)); - const t2 = c.i32_const(module.alloc(ftsize)); - const t3 = c.i32_const(module.alloc(ftsize)); - const t4 = c.i32_const(module.alloc(ftsize)); - const t5 = c.i32_const(module.alloc(ftsize)); - const t6 = c.i32_const(module.alloc(ftsize)); + const f1mPrefix = buildF1m$1(module, q, "f1m"); + buildF1$1(module, r, "fr", "frm"); - f.addCode( + const pG1b = module.alloc(utils$1.bigInt2BytesLE( toMontgomery(3n), f1size )); + const g1mPrefix = buildCurve$1(module, "g1m", "f1m", pG1b); - // let mut t0 = f.frobenius_map(6) - c.call(ftmPrefix + "_frobeniusMap6", elt, t0), + buildFFT$1(module, "frm", "frm", "frm", "frm_mul"); - // let t1 = f.invert() - c.call(ftmPrefix + "_inverse", elt, t1), + buildPol$1(module, "pol", "frm"); + buildQAP$1(module, "qap", "frm"); - // let mut t2 = t0 * t1; - c.call(ftmPrefix + "_mul", t0, t1, t2), + const f2mPrefix = buildF2m$1(module, "f1m_neg", "f2m", "f1m"); + const pG2b = module.alloc([ + ...utils$1.bigInt2BytesLE( toMontgomery(19485874751759354771024239261021720505790618469301721065564631296452457478373n), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(266929791119991161246907387137283842545076965332900288569378510910307636690n), f1size ) + ]); + const g2mPrefix = buildCurve$1(module, "g2m", "f2m", pG2b); - // t1 = t2.clone(); - c.call(ftmPrefix + "_copy", t2, t1), - // t2 = t2.frobenius_map().frobenius_map(); - c.call(ftmPrefix + "_frobeniusMap2", t2, t2), + function buildGTimesFr(fnName, opMul) { + const f = module.addFunction(fnName); + f.addParam("pG", "i32"); + f.addParam("pFr", "i32"); + f.addParam("pr", "i32"); - // t2 *= t1; - c.call(ftmPrefix + "_mul", t2, t1, t2), + const c = f.getCodeBuilder(); + const AUX = c.i32_const(module.alloc(n8)); - // t1 = cyclotomic_square(t2).conjugate(); - c.call(prefix + "__cyclotomicSquare", t2, t1), - c.call(ftmPrefix + "_conjugate", t1, t1), + f.addCode( + c.call("frm_fromMontgomery", c.getLocal("pFr"), AUX), + c.call( + opMul, + c.getLocal("pG"), + AUX, + c.i32_const(n8), + c.getLocal("pr") + ) + ); - // let mut t3 = cycolotomic_exp(t2); - c.call(prefix + "__cyclotomicExp_w0", t2, t3), + module.exportFunction(fnName); + } + buildGTimesFr("g1m_timesFr", "g1m_timesScalar"); + buildFFT$1(module, "g1m", "g1m", "frm", "g1m_timesFr"); - // let mut t4 = cyclotomic_square(t3); - c.call(prefix + "__cyclotomicSquare", t3, t4), + buildGTimesFr("g2m_timesFr", "g2m_timesScalar"); + buildFFT$1(module, "g2m", "g2m", "frm", "g2m_timesFr"); - // let mut t5 = t1 * t3; - c.call(ftmPrefix + "_mul", t1, t3, t5), + buildGTimesFr("g1m_timesFrAffine", "g1m_timesScalarAffine"); + buildGTimesFr("g2m_timesFrAffine", "g2m_timesScalarAffine"); - // t1 = cycolotomic_exp(t5); - c.call(prefix + "__cyclotomicExp_w0", t5, t1), + buildApplyKey$1(module, "frm_batchApplyKey", "fmr", "frm", n8, n8, n8, "frm_mul"); + buildApplyKey$1(module, "g1m_batchApplyKey", "g1m", "frm", n8*3, n8*3, n8, "g1m_timesFr"); + buildApplyKey$1(module, "g1m_batchApplyKeyMixed", "g1m", "frm", n8*2, n8*3, n8, "g1m_timesFrAffine"); + buildApplyKey$1(module, "g2m_batchApplyKey", "g2m", "frm", n8*2*3, n8*3*2, n8, "g2m_timesFr"); + buildApplyKey$1(module, "g2m_batchApplyKeyMixed", "g2m", "frm", n8*2*2, n8*3*2, n8, "g2m_timesFrAffine"); - // t0 = cycolotomic_exp(t1); - c.call(prefix + "__cyclotomicExp_w0", t1, t0), + function toMontgomery(a) { + return BigInt(a) * ( 1n << BigInt(f1size*8)) % q; + } - // let mut t6 = cycolotomic_exp(t0); - c.call(prefix + "__cyclotomicExp_w0", t0, t6), + const G1gen = [ + 1n, + 2n, + 1n + ]; - // t6 *= t4; - c.call(ftmPrefix + "_mul", t6, t4, t6), + const pG1gen = module.alloc( + [ + ...utils$1.bigInt2BytesLE( toMontgomery(G1gen[0]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G1gen[1]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G1gen[2]), f1size ), + ] + ); - // t4 = cycolotomic_exp(t6); - c.call(prefix + "__cyclotomicExp_w0", t6, t4), + const G1zero = [ + 0n, + 1n, + 0n + ]; - // t5 = t5.conjugate(); - c.call(ftmPrefix + "_conjugate", t5, t5), + const pG1zero = module.alloc( + [ + ...utils$1.bigInt2BytesLE( toMontgomery(G1zero[0]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G1zero[1]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G1zero[2]), f1size ) + ] + ); - // t4 *= t5 * t2; - c.call(ftmPrefix + "_mul", t4, t5, t4), - c.call(ftmPrefix + "_mul", t4, t2, t4), + const G2gen = [ + [ + 10857046999023057135944570762232829481370756359578518086990519993285655852781n, + 11559732032986387107991004021392285783925812861821192530917403151452391805634n, + ],[ + 8495653923123431417604973247489272438418190587263600148770280649306958101930n, + 4082367875863433681332203403145435568316851327593401208105741076214120093531n, + ],[ + 1n, + 0n, + ] + ]; - // t5 = t2.conjugate(); - c.call(ftmPrefix + "_conjugate", t2, t5), + const pG2gen = module.alloc( + [ + ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[0][0]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[0][1]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[1][0]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[1][1]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[2][0]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G2gen[2][1]), f1size ), + ] + ); - // t1 *= t2; - c.call(ftmPrefix + "_mul", t1, t2, t1), + const G2zero = [ + [ + 0n, + 0n, + ],[ + 1n, + 0n, + ],[ + 0n, + 0n, + ] + ]; - // t1 = t1.frobenius_map().frobenius_map().frobenius_map(); - c.call(ftmPrefix + "_frobeniusMap3", t1, t1), + const pG2zero = module.alloc( + [ + ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[0][0]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[0][1]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[1][0]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[1][1]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[2][0]), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(G2zero[2][1]), f1size ), + ] + ); - // t6 *= t5; - c.call(ftmPrefix + "_mul", t6, t5, t6), + const pOneT = module.alloc([ + ...utils$1.bigInt2BytesLE( toMontgomery(1), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(0), f1size ), + ]); - // t6 = t6.frobenius_map(); - c.call(ftmPrefix + "_frobeniusMap1", t6, t6), + const pNonResidueF6 = module.alloc([ + ...utils$1.bigInt2BytesLE( toMontgomery(9), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(1), f1size ), + ]); - // t3 *= t0; - c.call(ftmPrefix + "_mul", t3, t0, t3), + const pTwoInv = module.alloc([ + ...utils$1.bigInt2BytesLE( toMontgomery( modInv(2n, q)), f1size ), + ...utils$1.bigInt2BytesLE( 0n, f1size ) + ]); - // t3 = t3.frobenius_map().frobenius_map(); - c.call(ftmPrefix + "_frobeniusMap2", t3, t3), + const pAltBn128Twist = pNonResidueF6; - // t3 *= t1; - c.call(ftmPrefix + "_mul", t3, t1, t3), + const pTwistCoefB = module.alloc([ + ...utils$1.bigInt2BytesLE( toMontgomery(19485874751759354771024239261021720505790618469301721065564631296452457478373n), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery(266929791119991161246907387137283842545076965332900288569378510910307636690n), f1size ), + ]); - // t3 *= t6; - c.call(ftmPrefix + "_mul", t3, t6, t3), + function build_mulNR6() { + const f = module.addFunction(prefix + "_mulNR6"); + f.addParam("x", "i32"); + f.addParam("pr", "i32"); - // f = t3 * t4; - c.call(ftmPrefix + "_mul", t3, t4, res), + const c = f.getCodeBuilder(); + f.addCode( + c.call( + f2mPrefix + "_mul", + c.i32_const(pNonResidueF6), + c.getLocal("x"), + c.getLocal("pr") + ) ); } + build_mulNR6(); + const f6mPrefix = buildF3m$1(module, prefix+"_mulNR6", "f6m", "f2m"); - function buildFinalExponentiationOld() { - const f = module.addFunction(prefix+ "_finalExponentiationOld"); + function build_mulNR12() { + const f = module.addFunction(prefix + "_mulNR12"); f.addParam("x", "i32"); - f.addParam("r", "i32"); - - const exponent = 322277361516934140462891564586510139908379969514828494218366688025288661041104682794998680497580008899973249814104447692778988208376779573819485263026159588510513834876303014016798809919343532899164848730280942609956670917565618115867287399623286813270357901731510188149934363360381614501334086825442271920079363289954510565375378443704372994881406797882676971082200626541916413184642520269678897559532260949334760604962086348898118982248842634379637598665468817769075878555493752214492790122785850202957575200176084204422751485957336465472324810982833638490904279282696134323072515220044451592646885410572234451732790590013479358343841220074174848221722017083597872017638514103174122784843925578370430843522959600095676285723737049438346544753168912974976791528535276317256904336520179281145394686565050419250614107803233314658825463117900250701199181529205942363159325765991819433914303908860460720581408201373164047773794825411011922305820065611121544561808414055302212057471395719432072209245600258134364584636810093520285711072578721435517884103526483832733289802426157301542744476740008494780363354305116978805620671467071400711358839553375340724899735460480144599782014906586543813292157922220645089192130209334926661588737007768565838519456601560804957985667880395221049249803753582637708560n; - - const pExponent = module.alloc(utils$1.bigInt2BytesLE( exponent, 544 )); + f.addParam("pr", "i32"); const c = f.getCodeBuilder(); f.addCode( - c.call(ftmPrefix + "_exp", c.getLocal("x"), c.i32_const(pExponent), c.i32_const(544), c.getLocal("r")), + c.call( + f2mPrefix + "_mul", + c.i32_const(pNonResidueF6), + c.i32_add(c.getLocal("x"), c.i32_const(n8*4)), + c.getLocal("pr") + ), + c.call( + f2mPrefix + "_copy", + c.getLocal("x"), + c.i32_add(c.getLocal("pr"), c.i32_const(n8*2)), + ), + c.call( + f2mPrefix + "_copy", + c.i32_add(c.getLocal("x"), c.i32_const(n8*2)), + c.i32_add(c.getLocal("pr"), c.i32_const(n8*4)), + ) ); } + build_mulNR12(); + const ftmPrefix = buildF2m$1(module, prefix+"_mulNR12", "ftm", f6mPrefix); - const pPreP = module.alloc(prePSize); - const pPreQ = module.alloc(preQSize); - - function buildPairingEquation(nPairings) { - - const f = module.addFunction(prefix+ "_pairingEq"+nPairings); - for (let i=0; i acc + ( b!=0 ? 1 : 0) ,0); + const ateNCoefs = ateNAddCoefs + ateNDblCoefs + 1; + const prePSize = 3*2*n8; + const preQSize = 3*n8*2 + ateNCoefs*ateCoefSize; - for (let i=0; i 0n) { + if (isOdd$1(E)) { + const z = 2 - Number(E % 4n); + res.push( z ); + E = E - BigInt(z); + } else { + res.push( 0 ); + } + E = E >> 1n; } - - f.addCode(c.call(prefix + "_finalExponentiation", resT, resT )); - - f.addCode(c.call(ftmPrefix + "_eq", resT, c.getLocal("c"))); + return res; } - - function buildPairing() { - - const f = module.addFunction(prefix+ "_pairing"); - f.addParam("p", "i32"); - f.addParam("q", "i32"); - f.addParam("r", "i32"); - - const c = f.getCodeBuilder(); - - const resT = c.i32_const(module.alloc(ftsize)); - - f.addCode(c.call(prefix + "_prepareG1", c.getLocal("p"), c.i32_const(pPreP) )); - f.addCode(c.call(prefix + "_prepareG2", c.getLocal("q"), c.i32_const(pPreQ) )); - f.addCode(c.call(prefix + "_millerLoop", c.i32_const(pPreP), c.i32_const(pPreQ), resT )); - f.addCode(c.call(prefix + "_finalExponentiation", resT, c.getLocal("r") )); + function bits(n) { + let E = n; + const res = []; + while (E > 0n) { + if (isOdd$1(E)) { + res.push( 1 ); + } else { + res.push( 0 ); + } + E = E >> 1n; + } + return res; } - - function buildInGroupG2() { - const f = module.addFunction(g2mPrefix+ "_inGroupAffine"); - f.addParam("p", "i32"); - f.setReturnType("i32"); + function buildPrepareG1() { + const f = module.addFunction(prefix+ "_prepareG1"); + f.addParam("pP", "i32"); + f.addParam("ppreP", "i32"); const c = f.getCodeBuilder(); - const WINV = [ - 2001204777610833696708894912867952078278441409969503942666029068062015825245418932221343814564507832018947136279894n, - 2001204777610833696708894912867952078278441409969503942666029068062015825245418932221343814564507832018947136279893n - ]; - - const FROB2X = 4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436n; - const FROB3Y = [ - 2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530n, - 2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530n - ]; - - const wInv = c.i32_const(module.alloc([ - ...utils$1.bigInt2BytesLE(toMontgomery(WINV[0]), n8q), - ...utils$1.bigInt2BytesLE(toMontgomery(WINV[1]), n8q), - ])); - - const frob2X = c.i32_const(module.alloc(utils$1.bigInt2BytesLE(toMontgomery(FROB2X), n8q))); - const frob3Y = c.i32_const(module.alloc([ - ...utils$1.bigInt2BytesLE(toMontgomery(FROB3Y[0]), n8q), - ...utils$1.bigInt2BytesLE(toMontgomery(FROB3Y[1]), n8q), - ])); - - const z = c.i32_const(module.alloc(utils$1.bigInt2BytesLE(finalExpZ, 8))); - - const px = c.getLocal("p"); - const py = c.i32_add(c.getLocal("p"), c.i32_const(f2size)); - - const aux = c.i32_const(module.alloc(f1size)); + f.addCode( + c.call(g1mPrefix + "_normalize", c.getLocal("pP"), c.getLocal("ppreP")), // TODO Remove if already in affine + ); + } - const x_winv = c.i32_const(module.alloc(f2size)); - const y_winv = c.i32_const(module.alloc(f2size)); - const pf2 = module.alloc(f2size*2); - const f2 = c.i32_const(pf2); - const f2x = c.i32_const(pf2); - const f2x_c1 = c.i32_const(pf2); - const f2x_c2 = c.i32_const(pf2+f1size); - const f2y = c.i32_const(pf2+f2size); - const f2y_c1 = c.i32_const(pf2+f2size); - const f2y_c2 = c.i32_const(pf2+f2size+f1size); - const pf3 = module.alloc(f2size*3); - const f3 = c.i32_const(pf3); - const f3x = c.i32_const(pf3); - const f3x_c1 = c.i32_const(pf3); - const f3x_c2 = c.i32_const(pf3+f1size); - const f3y = c.i32_const(pf3+f2size); - const f3y_c1 = c.i32_const(pf3+f2size); - const f3y_c2 = c.i32_const(pf3+f2size+f1size); - const f3z = c.i32_const(pf3+f2size*2); + function buildPrepAddStep() { + const f = module.addFunction(prefix+ "_prepAddStep"); + f.addParam("pQ", "i32"); + f.addParam("pR", "i32"); + f.addParam("pCoef", "i32"); + + const c = f.getCodeBuilder(); + + const X2 = c.getLocal("pQ"); + const Y2 = c.i32_add(c.getLocal("pQ"), c.i32_const(f2size)); + + const X1 = c.getLocal("pR"); + const Y1 = c.i32_add(c.getLocal("pR"), c.i32_const(f2size)); + const Z1 = c.i32_add(c.getLocal("pR"), c.i32_const(2*f2size)); + + const ELL_0 = c.getLocal("pCoef"); + const ELL_VW = c.i32_add(c.getLocal("pCoef"), c.i32_const(f2size)); + const ELL_VV = c.i32_add(c.getLocal("pCoef"), c.i32_const(2*f2size)); + const D = ELL_VW; + const E = c.i32_const(module.alloc(f2size)); + const F = c.i32_const(module.alloc(f2size)); + const G = c.i32_const(module.alloc(f2size)); + const H = c.i32_const(module.alloc(f2size)); + const I = c.i32_const(module.alloc(f2size)); + const J = c.i32_const(module.alloc(f2size)); + const AUX = c.i32_const(module.alloc(f2size)); f.addCode( - c.if( - c.call(g2mPrefix + "_isZeroAffine", c.getLocal("p")), - c.ret( c.i32_const(1)), - ), - c.if( - c.i32_eqz(c.call(g2mPrefix + "_inCurveAffine", c.getLocal("p"))), - c.ret( c.i32_const(0)), - ), - c.call(f2mPrefix + "_mul", px, wInv, x_winv), - c.call(f2mPrefix + "_mul", py, wInv, y_winv), + // D = X1 - X2*Z1 + c.call(f2mPrefix + "_mul", X2, Z1, D), + c.call(f2mPrefix + "_sub", X1, D, D), - c.call(f2mPrefix + "_mul1", x_winv, frob2X, f2x), - c.call(f2mPrefix + "_neg", y_winv, f2y), + // E = Y1 - Y2*Z1 + c.call(f2mPrefix + "_mul", Y2, Z1, E), + c.call(f2mPrefix + "_sub", Y1, E, E), - c.call(f2mPrefix + "_neg", x_winv, f3x), - c.call(f2mPrefix + "_mul", y_winv, frob3Y, f3y), + // F = D^2 + c.call(f2mPrefix + "_square", D, F), - c.call(f1mPrefix + "_sub", f2x_c1, f2x_c2, aux), - c.call(f1mPrefix + "_add", f2x_c1, f2x_c2, f2x_c2), - c.call(f1mPrefix + "_copy", aux, f2x_c1), + // G = E^2 + c.call(f2mPrefix + "_square", E, G), - c.call(f1mPrefix + "_sub", f2y_c1, f2y_c2, aux), - c.call(f1mPrefix + "_add", f2y_c1, f2y_c2, f2y_c2), - c.call(f1mPrefix + "_copy", aux, f2y_c1), + // H = D*F + c.call(f2mPrefix + "_mul", D, F, H), - c.call(f1mPrefix + "_add", f3x_c1, f3x_c2, aux), - c.call(f1mPrefix + "_sub", f3x_c1, f3x_c2, f3x_c2), - c.call(f1mPrefix + "_copy", aux, f3x_c1), + // I = X1 * F + c.call(f2mPrefix + "_mul", X1, F, I), - c.call(f1mPrefix + "_sub", f3y_c2, f3y_c1, aux), - c.call(f1mPrefix + "_add", f3y_c1, f3y_c2, f3y_c2), - c.call(f1mPrefix + "_copy", aux, f3y_c1), + // J = H + Z1*G - (I+I) + c.call(f2mPrefix + "_add", I, I, AUX), + c.call(f2mPrefix + "_mul", Z1, G, J), + c.call(f2mPrefix + "_add", H, J, J), + c.call(f2mPrefix + "_sub", J, AUX, J), - c.call(f2mPrefix + "_one", f3z), - c.call(g2mPrefix + "_timesScalar", f3, z, c.i32_const(8), f3), - c.call(g2mPrefix + "_addMixed", f3, f2, f3), + // X3 (X1) = D*J + c.call(f2mPrefix + "_mul", D, J, X1), - c.ret( - c.call(g2mPrefix + "_eqMixed", f3, c.getLocal("p")) - ) - ); + // Y3 (Y1) = E*(I-J)-(H*Y1) + c.call(f2mPrefix + "_mul", H, Y1, Y1), + c.call(f2mPrefix + "_sub", I, J, AUX), + c.call(f2mPrefix + "_mul", E, AUX, AUX), + c.call(f2mPrefix + "_sub", AUX, Y1, Y1), - const fInGroup = module.addFunction(g2mPrefix + "_inGroup"); - fInGroup.addParam("pIn", "i32"); - fInGroup.setReturnType("i32"); + // Z3 (Z1) = Z1*H + c.call(f2mPrefix + "_mul", Z1, H, Z1), - const c2 = fInGroup.getCodeBuilder(); + // ell_0 = xi * (E * X2 - D * Y2) + c.call(f2mPrefix + "_mul", D, Y2, AUX), + c.call(f2mPrefix + "_mul", E, X2, ELL_0), + c.call(f2mPrefix + "_sub", ELL_0, AUX, ELL_0), + c.call(f2mPrefix + "_mul", ELL_0, c.i32_const(pAltBn128Twist), ELL_0), - const aux2 = c2.i32_const(module.alloc(f2size*2)); - fInGroup.addCode( - c2.call(g2mPrefix + "_toAffine", c2.getLocal("pIn"), aux2), + // ell_VV = - E (later: * xP) + c.call(f2mPrefix + "_neg", E, ELL_VV), - c2.ret( - c2.call(g2mPrefix + "_inGroupAffine", aux2), - ) - ); + // ell_VW = D (later: * yP ) + // Already assigned + ); } - function buildInGroupG1() { - const f = module.addFunction(g1mPrefix+ "_inGroupAffine"); - f.addParam("p", "i32"); - f.setReturnType("i32"); + + + function buildPrepDoubleStep() { + const f = module.addFunction(prefix+ "_prepDblStep"); + f.addParam("pR", "i32"); + f.addParam("pCoef", "i32"); const c = f.getCodeBuilder(); - const BETA = 4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436n; - const BETA2 = 793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350n; - const Z2M1D3 = (finalExpZ * finalExpZ - 1n) / 3n; + const X1 = c.getLocal("pR"); + const Y1 = c.i32_add(c.getLocal("pR"), c.i32_const(f2size)); + const Z1 = c.i32_add(c.getLocal("pR"), c.i32_const(2*f2size)); + + const ELL_0 = c.getLocal("pCoef"); + const ELL_VW = c.i32_add(c.getLocal("pCoef"), c.i32_const(f2size)); + const ELL_VV = c.i32_add(c.getLocal("pCoef"), c.i32_const(2*f2size)); + + const A = c.i32_const(module.alloc(f2size)); + const B = c.i32_const(module.alloc(f2size)); + const C = c.i32_const(module.alloc(f2size)); + const D = c.i32_const(module.alloc(f2size)); + const E = c.i32_const(module.alloc(f2size)); + const F = c.i32_const(module.alloc(f2size)); + const G = c.i32_const(module.alloc(f2size)); + const H = c.i32_const(module.alloc(f2size)); + const I = c.i32_const(module.alloc(f2size)); + const J = c.i32_const(module.alloc(f2size)); + const E2 = c.i32_const(module.alloc(f2size)); + const AUX = c.i32_const(module.alloc(f2size)); - const beta = c.i32_const(module.alloc(utils$1.bigInt2BytesLE(toMontgomery(BETA), n8q))); - const beta2 = c.i32_const(module.alloc(utils$1.bigInt2BytesLE(toMontgomery(BETA2), n8q))); + f.addCode( - const z2m1d3 = c.i32_const(module.alloc(utils$1.bigInt2BytesLE(Z2M1D3, 16))); + // A = X1 * Y1 / 2 + c.call(f2mPrefix + "_mul", Y1, c.i32_const(pTwoInv), A), + c.call(f2mPrefix + "_mul", X1, A, A), + // B = Y1^2 + c.call(f2mPrefix + "_square", Y1, B), - const px = c.getLocal("p"); - const py = c.i32_add(c.getLocal("p"), c.i32_const(f1size)); + // C = Z1^2 + c.call(f2mPrefix + "_square", Z1, C), - const psp = module.alloc(f1size*3); - const sp = c.i32_const(psp); - const spx = c.i32_const(psp); - const spy = c.i32_const(psp+f1size); + // D = 3 * C + c.call(f2mPrefix + "_add", C, C, D), + c.call(f2mPrefix + "_add", D, C, D), - const ps2p = module.alloc(f1size*2); - const s2p = c.i32_const(ps2p); - const s2px = c.i32_const(ps2p); - const s2py = c.i32_const(ps2p+f1size); + // E = twist_b * D + c.call(f2mPrefix + "_mul", c.i32_const(pTwistCoefB), D, E), - f.addCode( - c.if( - c.call(g1mPrefix + "_isZeroAffine", c.getLocal("p")), - c.ret( c.i32_const(1)), - ), - c.if( - c.i32_eqz(c.call(g1mPrefix + "_inCurveAffine", c.getLocal("p"))), - c.ret( c.i32_const(0)), - ), + // F = 3 * E + c.call(f2mPrefix + "_add", E, E, F), + c.call(f2mPrefix + "_add", E, F, F), - c.call(f1mPrefix + "_mul", px, beta, spx), - c.call(f1mPrefix + "_copy", py, spy), + // G = (B+F)/2 + c.call(f2mPrefix + "_add", B, F, G), + c.call(f2mPrefix + "_mul", G, c.i32_const(pTwoInv), G), - c.call(f1mPrefix + "_mul", px, beta2, s2px), - c.call(f1mPrefix + "_copy", py, s2py), + // H = (Y1+Z1)^2-(B+C) + c.call(f2mPrefix + "_add", B, C, AUX), + c.call(f2mPrefix + "_add", Y1, Z1, H), + c.call(f2mPrefix + "_square", H, H), + c.call(f2mPrefix + "_sub", H, AUX, H), + // I = E-B + c.call(f2mPrefix + "_sub", E, B, I), - c.call(g1mPrefix + "_doubleAffine", sp, sp), - c.call(g1mPrefix + "_subMixed", sp, c.getLocal("p"), sp), - c.call(g1mPrefix + "_subMixed", sp, s2p, sp), + // J = X1^2 + c.call(f2mPrefix + "_square", X1, J), - c.call(g1mPrefix + "_timesScalar", sp, z2m1d3, c.i32_const(16), sp), + // E_squared = E^2 + c.call(f2mPrefix + "_square", E, E2), - c.ret( - c.call(g1mPrefix + "_eqMixed", sp, s2p) - ) + // X3 (X1) = A * (B-F) + c.call(f2mPrefix + "_sub", B, F, AUX), + c.call(f2mPrefix + "_mul", A, AUX, X1), - ); + // Y3 (Y1) = G^2 - 3*E^2 + c.call(f2mPrefix + "_add", E2, E2, AUX), + c.call(f2mPrefix + "_add", E2, AUX, AUX), + c.call(f2mPrefix + "_square", G, Y1), + c.call(f2mPrefix + "_sub", Y1, AUX, Y1), - const fInGroup = module.addFunction(g1mPrefix + "_inGroup"); - fInGroup.addParam("pIn", "i32"); - fInGroup.setReturnType("i32"); + // Z3 (Z1) = B * H + c.call(f2mPrefix + "_mul", B, H, Z1), - const c2 = fInGroup.getCodeBuilder(); + // ell_0 = xi * I + c.call(f2mPrefix + "_mul", c.i32_const(pAltBn128Twist), I, ELL_0), - const aux2 = c2.i32_const(module.alloc(f1size*2)); + // ell_VW = - H (later: * yP) + c.call(f2mPrefix + "_neg", H, ELL_VW), - fInGroup.addCode( - c2.call(g1mPrefix + "_toAffine", c2.getLocal("pIn"), aux2), + // ell_VV = 3*J (later: * xP) + c.call(f2mPrefix + "_add", J, J, ELL_VV), + c.call(f2mPrefix + "_add", J, ELL_VV, ELL_VV), - c2.ret( - c2.call(g1mPrefix + "_inGroupAffine", aux2), - ) ); } - for (let i=0; i<10; i++) { - buildFrobeniusMap(i); - module.exportFunction(ftmPrefix + "_frobeniusMap"+i); - } - - - buildInGroupG1(); - buildInGroupG2(); + function buildMulByQ() { + const f = module.addFunction(prefix + "_mulByQ"); + f.addParam("p1", "i32"); + f.addParam("pr", "i32"); - buildPrepAddStep(); - buildPrepDoubleStep(); + const c = f.getCodeBuilder(); - buildPrepareG1(); - buildPrepareG2(); + const x = c.getLocal("p1"); + const y = c.i32_add(c.getLocal("p1"), c.i32_const(f2size)); + const z = c.i32_add(c.getLocal("p1"), c.i32_const(f2size*2)); + const x3 = c.getLocal("pr"); + const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(f2size)); + const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(f2size*2)); - buildMillerLoop(); + const MulByQX = c.i32_const(module.alloc([ + ...utils$1.bigInt2BytesLE( toMontgomery("21575463638280843010398324269430826099269044274347216827212613867836435027261"), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery("10307601595873709700152284273816112264069230130616436755625194854815875713954"), f1size ), + ])); - buildFinalExponentiationOld(); - buildFinalExponentiation(); + const MulByQY = c.i32_const(module.alloc([ + ...utils$1.bigInt2BytesLE( toMontgomery("2821565182194536844548159561693502659359617185244120367078079554186484126554"), f1size ), + ...utils$1.bigInt2BytesLE( toMontgomery("3505843767911556378687030309984248845540243509899259641013678093033130930403"), f1size ), + ])); - for (let i=1; i<=5; i++) { - buildPairingEquation(i); - module.exportFunction(prefix + "_pairingEq"+i); + f.addCode( + // The frobeniusMap(1) in this field, is the conjugate + c.call(f2mPrefix + "_conjugate", x, x3), + c.call(f2mPrefix + "_mul", MulByQX, x3, x3), + c.call(f2mPrefix + "_conjugate", y, y3), + c.call(f2mPrefix + "_mul", MulByQY, y3, y3), + c.call(f2mPrefix + "_conjugate", z, z3), + ); } - buildPairing(); - module.exportFunction(prefix + "_pairing"); + function buildPrepareG2() { + buildMulByQ(); + const f = module.addFunction(prefix+ "_prepareG2"); + f.addParam("pQ", "i32"); + f.addParam("ppreQ", "i32"); + f.addLocal("pCoef", "i32"); + f.addLocal("i", "i32"); + const c = f.getCodeBuilder(); - module.exportFunction(prefix + "_prepareG1"); - module.exportFunction(prefix + "_prepareG2"); - module.exportFunction(prefix + "_millerLoop"); - module.exportFunction(prefix + "_finalExponentiation"); - module.exportFunction(prefix + "_finalExponentiationOld"); - module.exportFunction(prefix + "__cyclotomicSquare"); - module.exportFunction(prefix + "__cyclotomicExp_w0"); + const QX = c.getLocal("pQ"); - module.exportFunction(f6mPrefix + "_mul1"); - module.exportFunction(f6mPrefix + "_mul01"); - module.exportFunction(ftmPrefix + "_mul014"); + const pR = module.alloc(f2size*3); + const R = c.i32_const(pR); + const RX = c.i32_const(pR); + const RY = c.i32_const(pR+f2size); + const RZ = c.i32_const(pR+2*f2size); - module.exportFunction(g1mPrefix + "_inGroupAffine"); - module.exportFunction(g1mPrefix + "_inGroup"); - module.exportFunction(g2mPrefix + "_inGroupAffine"); - module.exportFunction(g2mPrefix + "_inGroup"); + const cQX = c.i32_add( c.getLocal("ppreQ"), c.i32_const(0)); + const cQY = c.i32_add( c.getLocal("ppreQ"), c.i32_const(f2size)); - // console.log(module.functionIdxByName); -}; + const pQ1 = module.alloc(f2size*3); + const Q1 = c.i32_const(pQ1); -/* - Copyright 2019 0KIMS association. + const pQ2 = module.alloc(f2size*3); + const Q2 = c.i32_const(pQ2); + const Q2Y = c.i32_const(pQ2 + f2size); - This file is part of wasmsnark (Web Assembly zkSnark Prover). + f.addCode( + c.call(g2mPrefix + "_normalize", QX, cQX), // TODO Remove if already in affine + c.call(f2mPrefix + "_copy", cQX, RX), + c.call(f2mPrefix + "_copy", cQY, RY), + c.call(f2mPrefix + "_one", RZ), + ); - wasmsnark 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. + f.addCode( + c.setLocal("pCoef", c.i32_add( c.getLocal("ppreQ"), c.i32_const(f2size*3))), + c.setLocal("i", c.i32_const(ateLoopBitBytes.length-2)), + c.block(c.loop( - wasmsnark 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. + c.call(prefix + "_prepDblStep", R, c.getLocal("pCoef")), + c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), - You should have received a copy of the GNU General Public License - along with wasmsnark. If not, see . -*/ + c.if( + c.i32_load8_s(c.getLocal("i"), pAteLoopBitBytes), + [ + ...c.call(prefix + "_prepAddStep", cQX, R, c.getLocal("pCoef")), + ...c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + ] + ), + c.br_if(1, c.i32_eqz ( c.getLocal("i") )), + c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) + ); -// module.exports.bn128_wasm = require("./build/bn128_wasm.js"); -// module.exports.bls12381_wasm = require("./build/bls12381_wasm.js"); -// module.exports.mnt6753_wasm = require("./build/mnt6753_wasm.js"); + f.addCode( + c.call(prefix + "_mulByQ", cQX, Q1), + c.call(prefix + "_mulByQ", Q1, Q2) + ); -var buildBn128$1 = build_bn128; -var buildBls12381$1 = build_bls12381; + f.addCode( + c.call(f2mPrefix + "_neg", Q2Y, Q2Y), -/* global BigInt */ + c.call(prefix + "_prepAddStep", Q1, R, c.getLocal("pCoef")), + c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), -function stringifyBigInts(o) { - if (typeof o == "bigint" || o.eq !== undefined) { - return o.toString(10); - } else if (o instanceof Uint8Array) { - return fromRprLE(o, 0); - } else if (Array.isArray(o)) { - return o.map(stringifyBigInts); - } else if (typeof o == "object") { - const res = {}; - const keys = Object.keys(o); - keys.forEach((k) => { - res[k] = stringifyBigInts(o[k]); - }); - return res; - } else { - return o; + c.call(prefix + "_prepAddStep", Q2, R, c.getLocal("pCoef")), + c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + ); } -} -function unstringifyBigInts(o) { - if (typeof o == "string" && /^[0-9]+$/.test(o)) { - return BigInt(o); - } else if (typeof o == "string" && /^0x[0-9a-fA-F]+$/.test(o)) { - return BigInt(o); - } else if (Array.isArray(o)) { - return o.map(unstringifyBigInts); - } else if (typeof o == "object") { - if (o === null) return null; - const res = {}; - const keys = Object.keys(o); - keys.forEach((k) => { - res[k] = unstringifyBigInts(o[k]); - }); - return res; - } else { - return o; - } -} + function buildMulBy024Old() { + const f = module.addFunction(prefix+ "__mulBy024Old"); + f.addParam("pEll0", "i32"); + f.addParam("pEllVW", "i32"); + f.addParam("pEllVV", "i32"); + f.addParam("pR", "i32"); // Result in F12 -function beBuff2int(buff) { - let res = BigInt(0); - let i = buff.length; - let offset = 0; - const buffV = new DataView(buff.buffer, buff.byteOffset, buff.byteLength); - while (i > 0) { - if (i >= 4) { - i -= 4; - res += BigInt(buffV.getUint32(i)) << BigInt(offset * 8); - offset += 4; - } else if (i >= 2) { - i -= 2; - res += BigInt(buffV.getUint16(i)) << BigInt(offset * 8); - offset += 2; - } else { - i -= 1; - res += BigInt(buffV.getUint8(i)) << BigInt(offset * 8); - offset += 1; - } - } - return res; -} + const c = f.getCodeBuilder(); -function beInt2Buff(n, len) { - let r = n; - const buff = new Uint8Array(len); - const buffV = new DataView(buff.buffer); - let o = len; - while (o > 0) { - if (o - 4 >= 0) { - o -= 4; - buffV.setUint32(o, Number(r & BigInt(0xffffffff))); - r = r >> BigInt(32); - } else if (o - 2 >= 0) { - o -= 2; - buffV.setUint16(o, Number(r & BigInt(0xffff))); - r = r >> BigInt(16); - } else { - o -= 1; - buffV.setUint8(o, Number(r & BigInt(0xff))); - r = r >> BigInt(8); - } - } - if (r) { - throw new Error("Number does not fit in this length"); - } - return buff; -} + const x0 = c.getLocal("pEll0"); + const x2 = c.getLocal("pEllVV"); + const x4 = c.getLocal("pEllVW"); -function leBuff2int(buff) { - let res = BigInt(0); - let i = 0; - const buffV = new DataView(buff.buffer, buff.byteOffset, buff.byteLength); - while (i < buff.length) { - if (i + 4 <= buff.length) { - res += BigInt(buffV.getUint32(i, true)) << BigInt(i * 8); - i += 4; - } else if (i + 2 <= buff.length) { - res += BigInt(buffV.getUint16(i, true)) << BigInt(i * 8); - i += 2; - } else { - res += BigInt(buffV.getUint8(i, true)) << BigInt(i * 8); - i += 1; - } - } - return res; -} + const z0 = c.getLocal("pR"); -function leInt2Buff(n, len) { - let r = n; - if (typeof len === "undefined") { - len = Math.floor((bitLength$6(n) - 1) / 8) + 1; - if (len == 0) len = 1; - } - const buff = new Uint8Array(len); - const buffV = new DataView(buff.buffer); - let o = 0; - while (o < len) { - if (o + 4 <= len) { - buffV.setUint32(o, Number(r & BigInt(0xffffffff)), true); - o += 4; - r = r >> BigInt(32); - } else if (o + 2 <= len) { - buffV.setUint16(o, Number(r & BigInt(0xffff)), true); - o += 2; - r = r >> BigInt(16); - } else { - buffV.setUint8(o, Number(r & BigInt(0xff)), true); - o += 1; - r = r >> BigInt(8); - } - } - if (r) { - throw new Error("Number does not fit in this length"); - } - return buff; -} + const pAUX12 = module.alloc(ftsize); + const AUX12 = c.i32_const(pAUX12); + const AUX12_0 = c.i32_const(pAUX12); + const AUX12_2 = c.i32_const(pAUX12+f2size); + const AUX12_4 = c.i32_const(pAUX12+f2size*2); + const AUX12_6 = c.i32_const(pAUX12+f2size*3); + const AUX12_8 = c.i32_const(pAUX12+f2size*4); + const AUX12_10 = c.i32_const(pAUX12+f2size*5); + + f.addCode( -function stringifyFElements(F, o) { - if (typeof o == "bigint" || o.eq !== undefined) { - return o.toString(10); - } else if (o instanceof Uint8Array) { - return F.toString(F.e(o)); - } else if (Array.isArray(o)) { - return o.map(stringifyFElements.bind(this, F)); - } else if (typeof o == "object") { - const res = {}; - const keys = Object.keys(o); - keys.forEach((k) => { - res[k] = stringifyFElements(F, o[k]); - }); - return res; - } else { - return o; + c.call(f2mPrefix + "_copy", x0, AUX12_0), + c.call(f2mPrefix + "_zero", AUX12_2), + c.call(f2mPrefix + "_copy", x2, AUX12_4), + c.call(f2mPrefix + "_zero", AUX12_6), + c.call(f2mPrefix + "_copy", x4, AUX12_8), + c.call(f2mPrefix + "_zero", AUX12_10), + c.call(ftmPrefix + "_mul", AUX12, z0, z0), + ); } -} -function unstringifyFElements(F, o) { - if (typeof o == "string" && /^[0-9]+$/.test(o)) { - return F.e(o); - } else if (typeof o == "string" && /^0x[0-9a-fA-F]+$/.test(o)) { - return F.e(o); - } else if (Array.isArray(o)) { - return o.map(unstringifyFElements.bind(this, F)); - } else if (typeof o == "object") { - if (o === null) return null; - const res = {}; - const keys = Object.keys(o); - keys.forEach((k) => { - res[k] = unstringifyFElements(F, o[k]); - }); - return res; - } else { - return o; - } -} + function buildMulBy024() { + const f = module.addFunction(prefix+ "__mulBy024"); + f.addParam("pEll0", "i32"); + f.addParam("pEllVW", "i32"); + f.addParam("pEllVV", "i32"); + f.addParam("pR", "i32"); // Result in F12 -const _revTable = []; -for (let i = 0; i < 256; i++) { - _revTable[i] = _revSlow(i, 8); -} + const c = f.getCodeBuilder(); -function _revSlow(idx, bits) { - let res = 0; - let a = idx; - for (let i = 0; i < bits; i++) { - res <<= 1; - res = res | (a & 1); - a >>= 1; - } - return res; -} + const x0 = c.getLocal("pEll0"); + const x2 = c.getLocal("pEllVV"); + const x4 = c.getLocal("pEllVW"); -function bitReverse(idx, bits) { - return ( - (_revTable[idx >>> 24] | - (_revTable[(idx >>> 16) & 0xff] << 8) | - (_revTable[(idx >>> 8) & 0xff] << 16) | - (_revTable[idx & 0xff] << 24)) >>> - (32 - bits) - ); -} + const z0 = c.getLocal("pR"); + const z1 = c.i32_add(c.getLocal("pR"), c.i32_const(2*n8)); + const z2 = c.i32_add(c.getLocal("pR"), c.i32_const(4*n8)); + const z3 = c.i32_add(c.getLocal("pR"), c.i32_const(6*n8)); + const z4 = c.i32_add(c.getLocal("pR"), c.i32_const(8*n8)); + const z5 = c.i32_add(c.getLocal("pR"), c.i32_const(10*n8)); -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) - ); -} + const t0 = c.i32_const(module.alloc(f2size)); + const t1 = c.i32_const(module.alloc(f2size)); + const t2 = c.i32_const(module.alloc(f2size)); + const s0 = c.i32_const(module.alloc(f2size)); + const T3 = c.i32_const(module.alloc(f2size)); + const T4 = c.i32_const(module.alloc(f2size)); + const D0 = c.i32_const(module.alloc(f2size)); + const D2 = c.i32_const(module.alloc(f2size)); + const D4 = c.i32_const(module.alloc(f2size)); + const S1 = c.i32_const(module.alloc(f2size)); + const AUX = c.i32_const(module.alloc(f2size)); -function buffReverseBits(buff, eSize) { - const n = buff.byteLength / eSize; - const bits = 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); - 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); - } - } -} + f.addCode( -function array2buffer(arr, sG) { - const buff = new Uint8Array(sG * arr.length); + // D0 = z0 * x0; + c.call(f2mPrefix + "_mul", z0, x0, D0), + // D2 = z2 * x2; + c.call(f2mPrefix + "_mul", z2, x2, D2), + // D4 = z4 * x4; + c.call(f2mPrefix + "_mul", z4, x4, D4), + // t2 = z0 + z4; + c.call(f2mPrefix + "_add", z0, z4, t2), + // t1 = z0 + z2; + c.call(f2mPrefix + "_add", z0, z2, t1), + // s0 = z1 + z3 + z5; + c.call(f2mPrefix + "_add", z1, z3, s0), + c.call(f2mPrefix + "_add", s0, z5, s0), - for (let i = 0; i < arr.length; i++) { - buff.set(arr[i], i * sG); - } - return buff; -} + // For z.a_.a_ = z0. + // S1 = z1 * x2; + c.call(f2mPrefix + "_mul", z1, x2, S1), + // T3 = S1 + D4; + c.call(f2mPrefix + "_add", S1, D4, T3), + // T4 = my_Fp6::non_residue * T3 + D0; + c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), T3, T4), + c.call(f2mPrefix + "_add", T4, D0, z0), + // z0 = T4; -function buffer2array(buff, sG) { - const n = buff.byteLength / sG; - const arr = new Array(n); - for (let i = 0; i < n; i++) { - arr[i] = buff.slice(i * sG, i * sG + sG); + // For z.a_.b_ = z1 + // T3 = z5 * x4; + c.call(f2mPrefix + "_mul", z5, x4, T3), + // S1 = S1 + T3; + c.call(f2mPrefix + "_add", S1, T3, S1), + // T3 = T3 + D2; + c.call(f2mPrefix + "_add", T3, D2, T3), + // T4 = my_Fp6::non_residue * T3; + c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), T3, T4), + // T3 = z1 * x0; + c.call(f2mPrefix + "_mul", z1, x0, T3), + // S1 = S1 + T3; + c.call(f2mPrefix + "_add", S1, T3, S1), + // T4 = T4 + T3; + c.call(f2mPrefix + "_add", T4, T3, z1), + // z1 = T4; + + + + // For z.a_.c_ = z2 + // t0 = x0 + x2; + c.call(f2mPrefix + "_add", x0, x2, t0), + // T3 = t1 * t0 - D0 - D2; + c.call(f2mPrefix + "_mul", t1, t0, T3), + c.call(f2mPrefix + "_add", D0, D2, AUX), + c.call(f2mPrefix + "_sub", T3, AUX, T3), + // T4 = z3 * x4; + c.call(f2mPrefix + "_mul", z3, x4, T4), + // S1 = S1 + T4; + c.call(f2mPrefix + "_add", S1, T4, S1), + + + // For z.b_.a_ = z3 (z3 needs z2) + // t0 = z2 + z4; + c.call(f2mPrefix + "_add", z2, z4, t0), + // T3 = T3 + T4; + // z2 = T3; + c.call(f2mPrefix + "_add", T3, T4, z2), + // t1 = x2 + x4; + c.call(f2mPrefix + "_add", x2, x4, t1), + // T3 = t0 * t1 - D2 - D4; + c.call(f2mPrefix + "_mul", t1, t0, T3), + c.call(f2mPrefix + "_add", D2, D4, AUX), + c.call(f2mPrefix + "_sub", T3, AUX, T3), + // T4 = my_Fp6::non_residue * T3; + c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), T3, T4), + // T3 = z3 * x0; + c.call(f2mPrefix + "_mul", z3, x0, T3), + // S1 = S1 + T3; + c.call(f2mPrefix + "_add", S1, T3, S1), + // T4 = T4 + T3; + c.call(f2mPrefix + "_add", T4, T3, z3), + // z3 = T4; + + // For z.b_.b_ = z4 + // T3 = z5 * x2; + c.call(f2mPrefix + "_mul", z5, x2, T3), + // S1 = S1 + T3; + c.call(f2mPrefix + "_add", S1, T3, S1), + // T4 = my_Fp6::non_residue * T3; + c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), T3, T4), + // t0 = x0 + x4; + c.call(f2mPrefix + "_add", x0, x4, t0), + // T3 = t2 * t0 - D0 - D4; + c.call(f2mPrefix + "_mul", t2, t0, T3), + c.call(f2mPrefix + "_add", D0, D4, AUX), + c.call(f2mPrefix + "_sub", T3, AUX, T3), + // T4 = T4 + T3; + c.call(f2mPrefix + "_add", T4, T3, z4), + // z4 = T4; + + // For z.b_.c_ = z5. + // t0 = x0 + x2 + x4; + c.call(f2mPrefix + "_add", x0, x2, t0), + c.call(f2mPrefix + "_add", t0, x4, t0), + // T3 = s0 * t0 - S1; + c.call(f2mPrefix + "_mul", s0, t0, T3), + c.call(f2mPrefix + "_sub", T3, S1, z5), + // z5 = T3; + + ); } - return arr; -} -var _utils = /*#__PURE__*/Object.freeze({ - __proto__: null, - array2buffer: array2buffer, - beBuff2int: beBuff2int, - beInt2Buff: beInt2Buff, - bitReverse: bitReverse, - buffReverseBits: buffReverseBits, - buffer2array: buffer2array, - leBuff2int: leBuff2int, - leInt2Buff: leInt2Buff, - log2: log2, - stringifyBigInts: stringifyBigInts, - stringifyFElements: stringifyFElements, - unstringifyBigInts: unstringifyBigInts, - unstringifyFElements: unstringifyFElements -}); -const PAGE_SIZE = 1<<30; + function buildMillerLoop() { + const f = module.addFunction(prefix+ "_millerLoop"); + f.addParam("ppreP", "i32"); + f.addParam("ppreQ", "i32"); + f.addParam("r", "i32"); + f.addLocal("pCoef", "i32"); + f.addLocal("i", "i32"); + + const c = f.getCodeBuilder(); -class BigBuffer { + const preP_PX = c.getLocal("ppreP"); + const preP_PY = c.i32_add(c.getLocal("ppreP"), c.i32_const(f1size)); - constructor(size) { - this.buffers = []; - this.byteLength = size; - for (let i=0; i0) { - // bytes to copy from this page - const l = (o+r > PAGE_SIZE) ? (PAGE_SIZE -o) : r; - const srcView = new Uint8Array(this.buffers[p].buffer, this.buffers[p].byteOffset+o, l); - if (l == len) return srcView.slice(); - if (!buff) { - if (len <= PAGE_SIZE) { - buff = new Uint8Array(len); - } else { - buff = new BigBuffer(len); - } - } - buff.set(srcView, len-r); - r = r-l; - p ++; - o = 0; - } + c.setLocal("pCoef", c.i32_add( c.getLocal("ppreQ"), c.i32_const(f2size*3))), - return buff; - } + c.setLocal("i", c.i32_const(ateLoopBitBytes.length-2)), + c.block(c.loop( - set(buff, offset) { - if (offset === undefined) offset = 0; - const len = buff.byteLength; + c.call(ftmPrefix + "_square", F, F), - if (len==0) return; + c.call(f2mPrefix + "_mul1", ELL_VW,preP_PY, VW), + c.call(f2mPrefix + "_mul1", ELL_VV, preP_PX, VV), + c.call(prefix + "__mulBy024", ELL_0, VW, VV, F), + c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), - const firstPage = Math.floor(offset / PAGE_SIZE); - const lastPage = Math.floor((offset+len-1) / PAGE_SIZE); + c.if( + c.i32_load8_s(c.getLocal("i"), pAteLoopBitBytes), + [ + ...c.call(f2mPrefix + "_mul1", ELL_VW, preP_PY, VW), + ...c.call(f2mPrefix + "_mul1", ELL_VV, preP_PX, VV), - if (firstPage == lastPage) { - if ((buff instanceof BigBuffer)&&(buff.buffers.length==1)) { - return this.buffers[firstPage].set(buff.buffers[0], offset % PAGE_SIZE); - } else { - return this.buffers[firstPage].set(buff, offset % PAGE_SIZE); - } + ...c.call(prefix + "__mulBy024", ELL_0, VW, VV, F), + ...c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), - } + ] + ), + c.br_if(1, c.i32_eqz ( c.getLocal("i") )), + c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) + ); - let p = firstPage; - let o = offset % PAGE_SIZE; - let r = len; - while (r>0) { - const l = (o+r > PAGE_SIZE) ? (PAGE_SIZE -o) : r; - const srcView = buff.slice( len -r, len -r+l); - const dstView = new Uint8Array(this.buffers[p].buffer, this.buffers[p].byteOffset + o, l); - dstView.set(srcView); - r = r-l; - p ++; - o = 0; - } + f.addCode( + c.call(f2mPrefix + "_mul1", ELL_VW, preP_PY, VW), + c.call(f2mPrefix + "_mul1", ELL_VV, preP_PX, VV), + c.call(prefix + "__mulBy024", ELL_0, VW, VV, F), + c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + + c.call(f2mPrefix + "_mul1", ELL_VW, preP_PY, VW), + c.call(f2mPrefix + "_mul1", ELL_VV, preP_PX, VV), + c.call(prefix + "__mulBy024", ELL_0, VW, VV, F), + c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))), + + ); } -} -function buildBatchConvert(tm, fnName, sIn, sOut) { - return async function batchConvert(buffIn) { - const nPoints = Math.floor(buffIn.byteLength / sIn); - 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=0; i--) { - this.w[i] = this.square(this.w[i+1]); - } + // // t2 + t3*y = (z2 + z3*y)^2 = b^2 + // tmp = z2 * z3; + // t2 = (z2 + z3) * (z2 + my_Fp6::non_residue * z3) - tmp - my_Fp6::non_residue * tmp; + // t3 = tmp + tmp; + c.call(f2mPrefix + "_mul", x2, x3, tmp), + c.call(f2mPrefix + "_mul", x3, c.i32_const(pNonResidueF6), t2), + c.call(f2mPrefix + "_add", x2, t2, t2), + c.call(f2mPrefix + "_add", x2, x3, AUX), + c.call(f2mPrefix + "_mul", AUX, t2, t2), + c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), tmp, AUX), + c.call(f2mPrefix + "_add", tmp, AUX, AUX), + c.call(f2mPrefix + "_sub", t2, AUX, t2), + c.call(f2mPrefix + "_add", tmp, tmp, t3), - if (!this.eq(this.w[0], this.one)) { - throw new Error("Error calculating roots of unity"); - } + // // t4 + t5*y = (z4 + z5*y)^2 = c^2 + // tmp = z4 * z5; + // t4 = (z4 + z5) * (z4 + my_Fp6::non_residue * z5) - tmp - my_Fp6::non_residue * tmp; + // t5 = tmp + tmp; + c.call(f2mPrefix + "_mul", x4, x5, tmp), + c.call(f2mPrefix + "_mul", x5, c.i32_const(pNonResidueF6), t4), + c.call(f2mPrefix + "_add", x4, t4, t4), + c.call(f2mPrefix + "_add", x4, x5, AUX), + c.call(f2mPrefix + "_mul", AUX, t4, t4), + c.call(f2mPrefix + "_mul", c.i32_const(pNonResidueF6), tmp, AUX), + c.call(f2mPrefix + "_add", tmp, AUX, AUX), + c.call(f2mPrefix + "_sub", t4, AUX, t4), + c.call(f2mPrefix + "_add", tmp, tmp, t5), - this.batchToMontgomery = buildBatchConvert(tm, prefix + "_batchToMontgomery", this.n8, this.n8); - this.batchFromMontgomery = buildBatchConvert(tm, prefix + "_batchFromMontgomery", this.n8, this.n8); - } + // For A + // z0 = 3 * t0 - 2 * z0 + c.call(f2mPrefix + "_sub", t0, x0, r0), + c.call(f2mPrefix + "_add", r0, r0, r0), + c.call(f2mPrefix + "_add", t0, r0, r0), + // z1 = 3 * t1 + 2 * z1 + c.call(f2mPrefix + "_add", t1, x1, r1), + c.call(f2mPrefix + "_add", r1, r1, r1), + c.call(f2mPrefix + "_add", t1, r1, r1), + // For B + // z2 = 3 * (xi * t5) + 2 * z2 + c.call(f2mPrefix + "_mul", t5, c.i32_const(pAltBn128Twist), AUX), + c.call(f2mPrefix + "_add", AUX, x2, r2), + c.call(f2mPrefix + "_add", r2, r2, r2), + c.call(f2mPrefix + "_add", AUX, r2, r2), + // z3 = 3 * t4 - 2 * z3 + c.call(f2mPrefix + "_sub", t4, x3, r3), + c.call(f2mPrefix + "_add", r3, r3, r3), + c.call(f2mPrefix + "_add", t4, r3, r3), - op2(opName, a, 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.tm.getBuff(this.pOp3, this.n8); - } + // For C + // z4 = 3 * t2 - 2 * z4 + c.call(f2mPrefix + "_sub", t2, x4, r4), + c.call(f2mPrefix + "_add", r4, r4, r4), + c.call(f2mPrefix + "_add", t2, r4, r4), + // z5 = 3 * t3 + 2 * z5 + c.call(f2mPrefix + "_add", t3, x5, r5), + c.call(f2mPrefix + "_add", r5, r5, r5), + c.call(f2mPrefix + "_add", t3, r5, r5), - op2Bool(opName, a, 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.tm.setBuff(this.pOp1, a); - this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); - return this.tm.getBuff(this.pOp3, this.n8); - } - op1Bool(opName, a) { - this.tm.setBuff(this.pOp1, a); - return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); - } + function buildCyclotomicExp(exponent, fnName) { + const exponentNafBytes = naf(exponent).map( (b) => (b==-1 ? 0xFF: b) ); + const pExponentNafBytes = module.alloc(exponentNafBytes); - add(a,b) { - return this.op2("_add", a, b); - } + const f = module.addFunction(prefix+ "__cyclotomicExp_"+fnName); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + f.addLocal("bit", "i32"); + f.addLocal("i", "i32"); + const c = f.getCodeBuilder(); - eq(a,b) { - return this.op2Bool("_eq", a, b); - } + const x = c.getLocal("x"); - isZero(a) { - return this.op1Bool("_isZero", a); - } + const res = c.getLocal("r"); - sub(a,b) { - return this.op2("_sub", a, b); - } + const inverse = c.i32_const(module.alloc(ftsize)); - neg(a) { - return this.op1("_neg", a); - } - inv(a) { - return this.op1("_inverse", a); - } + f.addCode( + c.call(ftmPrefix + "_conjugate", x, inverse), + c.call(ftmPrefix + "_one", res), - toMontgomery(a) { - return this.op1("_toMontgomery", a); - } + c.if( + c.teeLocal("bit", c.i32_load8_s(c.i32_const(exponentNafBytes.length-1), pExponentNafBytes)), + c.if( + c.i32_eq( + c.getLocal("bit"), + c.i32_const(1) + ), + c.call(ftmPrefix + "_mul", res, x, res), + c.call(ftmPrefix + "_mul", res, inverse, res), + ) + ), - fromMontgomery(a) { - return this.op1("_fromMontgomery", a); + c.setLocal("i", c.i32_const(exponentNafBytes.length-2)), + c.block(c.loop( + c.call(prefix + "__cyclotomicSquare", res, res), + c.if( + c.teeLocal("bit", c.i32_load8_s(c.getLocal("i"), pExponentNafBytes)), + c.if( + c.i32_eq( + c.getLocal("bit"), + c.i32_const(1) + ), + c.call(ftmPrefix + "_mul", res, x, res), + c.call(ftmPrefix + "_mul", res, inverse, res), + ) + ), + c.br_if(1, c.i32_eqz ( c.getLocal("i") )), + c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) + ); } - mul(a,b) { - 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); - } + function buildFinalExponentiationLastChunk() { + buildCyclotomicSquare(); + buildCyclotomicExp(finalExpZ, "w0"); - isSquare(a) { - return this.op1Bool("_isSquare", a); - } + const f = module.addFunction(prefix+ "__finalExponentiationLastChunk"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); - sqrt(a) { - return this.op1("_sqrt", a); - } + const c = f.getCodeBuilder(); - exp(a, b) { - if (!(b instanceof Uint8Array)) { - b = toLEBuff(e(b)); - } - this.tm.setBuff(this.pOp1, a); - this.tm.setBuff(this.pOp2, b); - this.tm.instance.exports[this.prefix + "_exp"](this.pOp1, this.pOp2, b.byteLength, this.pOp3); - return this.tm.getBuff(this.pOp3, this.n8); - } + const elt = c.getLocal("x"); + const result = c.getLocal("r"); + const A = c.i32_const(module.alloc(ftsize)); + const B = c.i32_const(module.alloc(ftsize)); + const C = c.i32_const(module.alloc(ftsize)); + const D = c.i32_const(module.alloc(ftsize)); + const E = c.i32_const(module.alloc(ftsize)); + const F = c.i32_const(module.alloc(ftsize)); + const G = c.i32_const(module.alloc(ftsize)); + const H = c.i32_const(module.alloc(ftsize)); + const I = c.i32_const(module.alloc(ftsize)); + const J = c.i32_const(module.alloc(ftsize)); + const K = c.i32_const(module.alloc(ftsize)); + const L = c.i32_const(module.alloc(ftsize)); + const M = c.i32_const(module.alloc(ftsize)); + const N = c.i32_const(module.alloc(ftsize)); + const O = c.i32_const(module.alloc(ftsize)); + const P = c.i32_const(module.alloc(ftsize)); + const Q = c.i32_const(module.alloc(ftsize)); + const R = c.i32_const(module.alloc(ftsize)); + const S = c.i32_const(module.alloc(ftsize)); + const T = c.i32_const(module.alloc(ftsize)); + const U = c.i32_const(module.alloc(ftsize)); - isNegative(a) { - return this.op1Bool("_isNegative", a); - } + f.addCode( - e(a, b) { - if (a instanceof Uint8Array) return a; - let ra = e(a, b); - if (isNegative$4(ra)) { - ra = neg(ra); - if (gt(ra, this.p)) { - ra = mod(ra, this.p); - } - ra = sub(this.p, ra); - } else { - if (gt(ra, this.p)) { - ra = mod(ra, this.p); - } - } - const buff = leInt2Buff(ra, this.n8); - return this.toMontgomery(buff); - } - toString(a, radix) { - const an = this.fromMontgomery(a); - const s = fromRprLE(an, 0); - return toString(s, radix); - } + // A = exp_by_neg_z(elt) // = elt^(-z) + c.call(prefix + "__cyclotomicExp_w0", elt, A), + c.call(ftmPrefix + "_conjugate", A, A), + // B = A^2 // = elt^(-2*z) + c.call(prefix + "__cyclotomicSquare", A, B), + // C = B^2 // = elt^(-4*z) + c.call(prefix + "__cyclotomicSquare", B, C), + // D = C * B // = elt^(-6*z) + c.call(ftmPrefix + "_mul", C, B, D), + // E = exp_by_neg_z(D) // = elt^(6*z^2) + c.call(prefix + "__cyclotomicExp_w0", D, E), + c.call(ftmPrefix + "_conjugate", E, E), + // F = E^2 // = elt^(12*z^2) + c.call(prefix + "__cyclotomicSquare", E, F), + // G = epx_by_neg_z(F) // = elt^(-12*z^3) + c.call(prefix + "__cyclotomicExp_w0", F, G), + c.call(ftmPrefix + "_conjugate", G, G), + // H = conj(D) // = elt^(6*z) + c.call(ftmPrefix + "_conjugate", D, H), + // I = conj(G) // = elt^(12*z^3) + c.call(ftmPrefix + "_conjugate", G, I), + // J = I * E // = elt^(12*z^3 + 6*z^2) + c.call(ftmPrefix + "_mul", I, E, J), + // K = J * H // = elt^(12*z^3 + 6*z^2 + 6*z) + c.call(ftmPrefix + "_mul", J, H, K), + // L = K * B // = elt^(12*z^3 + 6*z^2 + 4*z) + c.call(ftmPrefix + "_mul", K, B, L), + // M = K * E // = elt^(12*z^3 + 12*z^2 + 6*z) + c.call(ftmPrefix + "_mul", K, E, M), - fromRng(rng) { - let v; - const buff = new Uint8Array(this.n8); - do { - v = zero; - for (let i=0; i acc + ( b!=0 ? 1 : 0) ,0); + const ateNCoefs = ateNAddCoefs + ateNDblCoefs + 1; + const prePSize = 3*2*n8q; + const preQSize = 3*n8q*2 + ateNCoefs*ateCoefSize; + const finalExpIsNegative = true; - op1Bool(opName, a) { - this.tm.setBuff(this.pOp1, a); - return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3); - } + const finalExpZ = 15132376222941642752n; - add(a,b) { - if (a.byteLength == this.F.n8*3) { - if (b.byteLength == this.F.n8*3) { - return this.op2("_add", a, b); - } else if (b.byteLength == this.F.n8*2) { - return this.op2("_addMixed", a, b); - } else { - throw new Error("invalid point size"); - } - } else if (a.byteLength == this.F.n8*2) { - if (b.byteLength == this.F.n8*3) { - return this.op2("_addMixed", b, a); - } else if (b.byteLength == this.F.n8*2) { - return this.op2("_addAffine", a, b); + + module.modules[prefix] = { + n64q: n64q, + n64r: n64r, + n8q: n8q, + n8r: n8r, + pG1gen: pG1gen, + pG1zero: pG1zero, + pG1b: pG1b, + pG2gen: pG2gen, + pG2zero: pG2zero, + pG2b: pG2b, + pq: module.modules["f1m"].pq, + pr: pr, + pOneT: pOneT, + r: r, + q: q, + prePSize: prePSize, + preQSize: preQSize + }; + + + function naf(n) { + let E = n; + const res = []; + while (E > 0n) { + if (isOdd(E)) { + const z = 2 - Number(E % 4n); + res.push( z ); + E = E - BigInt(z); } else { - throw new Error("invalid point size"); + res.push( 0 ); } - } else { - throw new Error("invalid point size"); + E = E >> 1n; } + return res; } - sub(a,b) { - if (a.byteLength == this.F.n8*3) { - if (b.byteLength == this.F.n8*3) { - return this.op2("_sub", a, b); - } else if (b.byteLength == this.F.n8*2) { - return this.op2("_subMixed", a, b); - } else { - throw new Error("invalid point size"); - } - } else if (a.byteLength == this.F.n8*2) { - if (b.byteLength == this.F.n8*3) { - return this.op2("_subMixed", b, a); - } else if (b.byteLength == this.F.n8*2) { - return this.op2("_subAffine", a, b); + function bits(n) { + let E = n; + const res = []; + while (E > 0n) { + if (isOdd(E)) { + res.push( 1 ); } else { - throw new Error("invalid point size"); + res.push( 0 ); } - } else { - throw new Error("invalid point size"); + E = E >> 1n; } + return res; } - neg(a) { - if (a.byteLength == this.F.n8*3) { - return this.op1("_neg", a); - } else if (a.byteLength == this.F.n8*2) { - return this.op1Affine("_negAffine", a); - } else { - throw new Error("invalid point size"); - } - } + function buildPrepareG1() { + const f = module.addFunction(prefix+ "_prepareG1"); + f.addParam("pP", "i32"); + f.addParam("ppreP", "i32"); - double(a) { - if (a.byteLength == this.F.n8*3) { - return this.op1("_double", a); - } else if (a.byteLength == this.F.n8*2) { - return this.op1("_doubleAffine", a); - } else { - throw new Error("invalid point size"); - } - } + const c = f.getCodeBuilder(); - isZero(a) { - if (a.byteLength == this.F.n8*3) { - return this.op1Bool("_isZero", a); - } else if (a.byteLength == this.F.n8*2) { - return this.op1Bool("_isZeroAffine", a); - } else { - throw new Error("invalid point size"); - } + f.addCode( + c.call(g1mPrefix + "_normalize", c.getLocal("pP"), c.getLocal("ppreP")), // TODO Remove if already in affine + ); } - timesScalar(a, s) { - if (!(s instanceof Uint8Array)) { - s = toLEBuff(e(s)); - } - let fnName; - if (a.byteLength == this.F.n8*3) { - fnName = this.prefix + "_timesScalar"; - } else if (a.byteLength == this.F.n8*2) { - fnName = this.prefix + "_timesScalarAffine"; - } else { - throw new Error("invalid point size"); - } - this.tm.setBuff(this.pOp1, a); - this.tm.setBuff(this.pOp2, s); - this.tm.instance.exports[fnName](this.pOp1, this.pOp2, s.byteLength, this.pOp3); - return this.tm.getBuff(this.pOp3, this.F.n8*3); - } - timesFr(a, s) { - let fnName; - if (a.byteLength == this.F.n8*3) { - fnName = this.prefix + "_timesFr"; - } else if (a.byteLength == this.F.n8*2) { - fnName = this.prefix + "_timesFrAffine"; - } else { - throw new Error("invalid point size"); - } - this.tm.setBuff(this.pOp1, a); - this.tm.setBuff(this.pOp2, s); - this.tm.instance.exports[fnName](this.pOp1, this.pOp2, this.pOp3); - return this.tm.getBuff(this.pOp3, this.F.n8*3); - } - eq(a,b) { - if (a.byteLength == this.F.n8*3) { - if (b.byteLength == this.F.n8*3) { - return this.op2bool("_eq", a, b); - } else if (b.byteLength == this.F.n8*2) { - return this.op2bool("_eqMixed", a, b); - } else { - throw new Error("invalid point size"); - } - } else if (a.byteLength == this.F.n8*2) { - if (b.byteLength == this.F.n8*3) { - return this.op2bool("_eqMixed", b, a); - } else if (b.byteLength == this.F.n8*2) { - return this.op2bool("_eqAffine", a, b); - } else { - throw new Error("invalid point size"); - } - } else { - throw new Error("invalid point size"); - } - } + function buildPrepDoubleStep() { + const f = module.addFunction(prefix+ "_prepDblStep"); + f.addParam("R", "i32"); + f.addParam("r", "i32"); - toAffine(a) { - if (a.byteLength == this.F.n8*3) { - return this.op1Affine("_toAffine", a); - } else if (a.byteLength == this.F.n8*2) { - return a; - } else { - throw new Error("invalid point size"); - } - } + const c = f.getCodeBuilder(); + + const Rx = c.getLocal("R"); + const Ry = c.i32_add(c.getLocal("R"), c.i32_const(2*n8q)); + const Rz = c.i32_add(c.getLocal("R"), c.i32_const(4*n8q)); - toJacobian(a) { - if (a.byteLength == this.F.n8*3) { - return a; - } else if (a.byteLength == this.F.n8*2) { - return this.op1("_toJacobian", a); - } else { - throw new Error("invalid point size"); - } - } + const t0 = c.getLocal("r"); + const t3 = c.i32_add(c.getLocal("r"), c.i32_const(2*n8q)); + const t6 = c.i32_add(c.getLocal("r"), c.i32_const(4*n8q)); - toRprUncompressed(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 + "_LEMtoU"](this.pOp1, this.pOp1); - const res = this.tm.getBuff(this.pOp1, this.F.n8*2); - arr.set(res, offset); - } - fromRprUncompressed(arr, offset) { - const buff = arr.slice(offset, offset + this.F.n8*2); - this.tm.setBuff(this.pOp1, buff); - this.tm.instance.exports[this.prefix + "_UtoLEM"](this.pOp1, this.pOp1); - return this.tm.getBuff(this.pOp1, this.F.n8*2); - } + const zsquared = c.i32_const(module.alloc(f2size)); + const t1 = c.i32_const(module.alloc(f2size)); + const t2 = c.i32_const(module.alloc(f2size)); + const t4 = c.i32_const(module.alloc(f2size)); + const t5 = c.i32_const(module.alloc(f2size)); - 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); - } + f.addCode( - 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); - } + // tmp0 = r.x.square(); + c.call(f2mPrefix + "_square", Rx, t0), - toUncompressed(a) { - const buff = new Uint8Array(this.F.n8*2); - this.toRprUncompressed(buff, 0, a); - return buff; - } + // tmp1 = r.y.square(); + c.call(f2mPrefix + "_square", Ry, t1), - toRprLEM(arr, offset, a) { - if (a.byteLength == this.F.n8*2) { - arr.set(a, offset); - return; - } else if (a.byteLength == this.F.n8*3) { - this.tm.setBuff(this.pOp1, a); - this.tm.instance.exports[this.prefix + "_toAffine"](this.pOp1, this.pOp1); - const res = this.tm.getBuff(this.pOp1, this.F.n8*2); - arr.set(res, offset); - } else { - throw new Error("invalid point size"); - } - } + // tmp2 = tmp1.square(); + c.call(f2mPrefix + "_square", t1, t2), - fromRprLEM(arr, offset) { - offset = offset || 0; - return arr.slice(offset, offset+this.F.n8*2); - } + // tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2; + c.call(f2mPrefix + "_add", t1, Rx, t3), + c.call(f2mPrefix + "_square", t3, t3), + c.call(f2mPrefix + "_sub", t3, t0, t3), + c.call(f2mPrefix + "_sub", t3, t2, t3), - toString(a, radix) { - if (a.byteLength == this.F.n8*3) { - const x = this.F.toString(a.slice(0, this.F.n8), radix); - const y = this.F.toString(a.slice(this.F.n8, this.F.n8*2), radix); - const z = this.F.toString(a.slice(this.F.n8*2), radix); - return `[ ${x}, ${y}, ${z} ]`; - } else if (a.byteLength == this.F.n8*2) { - const x = this.F.toString(a.slice(0, this.F.n8), radix); - const y = this.F.toString(a.slice(this.F.n8), radix); - return `[ ${x}, ${y} ]`; - } else { - throw new Error("invalid point size"); - } - } + // tmp3 = tmp3 + tmp3; + c.call(f2mPrefix + "_add", t3, t3, t3), - isValid(a) { - if (this.isZero(a)) return true; - const F = this.F; - const aa = this.toAffine(a); - const x = aa.slice(0, this.F.n8); - const y = aa.slice(this.F.n8, this.F.n8*2); - const x3b = F.add(F.mul(F.square(x),x), this.b); - const y2 = F.square(y); - return F.eq(x3b, y2); - } + // tmp4 = tmp0 + tmp0 + tmp0; + c.call(f2mPrefix + "_add", t0, t0, t4), + c.call(f2mPrefix + "_add", t4, t0, t4), - fromRng(rng) { - const F = this.F; - let P = []; - let greatest; - let x3b; - do { - P[0] = F.fromRng(rng); - greatest = rng.nextBool(); - x3b = F.add(F.mul(F.square(P[0]), P[0]), this.b); - } while (!F.isSquare(x3b)); + // tmp6 = r.x + tmp4; + c.call(f2mPrefix + "_add", Rx, t4, t6), - P[1] = F.sqrt(x3b); + // tmp5 = tmp4.square(); + c.call(f2mPrefix + "_square", t4, t5), - const s = F.isNegative(P[1]); - if (greatest ^ s) P[1] = F.neg(P[1]); + // zsquared = r.z.square(); + c.call(f2mPrefix + "_square", Rz, zsquared), - let Pbuff = new Uint8Array(this.F.n8*2); - Pbuff.set(P[0]); - Pbuff.set(P[1], this.F.n8); + // r.x = tmp5 - tmp3 - tmp3; + c.call(f2mPrefix + "_sub", t5, t3, Rx), + c.call(f2mPrefix + "_sub", Rx, t3, Rx), - if (this.cofactor) { - Pbuff = this.timesScalar(Pbuff, this.cofactor); - } + // r.z = (r.z + r.y).square() - tmp1 - zsquared; + c.call(f2mPrefix + "_add", Rz, Ry, Rz), + c.call(f2mPrefix + "_square", Rz, Rz), + c.call(f2mPrefix + "_sub", Rz, t1, Rz), + c.call(f2mPrefix + "_sub", Rz, zsquared, Rz), - return Pbuff; - } + // r.y = (tmp3 - r.x) * tmp4; + c.call(f2mPrefix + "_sub", t3, Rx, Ry), + c.call(f2mPrefix + "_mul", Ry, t4, Ry), + // tmp2 = tmp2 + tmp2; + c.call(f2mPrefix + "_add", t2, t2, t2), + // tmp2 = tmp2 + tmp2; + c.call(f2mPrefix + "_add", t2, t2, t2), - toObject(a) { - if (this.isZero(a)) { - return [ - this.F.toObject(this.F.zero), - this.F.toObject(this.F.one), - this.F.toObject(this.F.zero), - ]; - } - const x = this.F.toObject(a.slice(0, this.F.n8)); - const y = this.F.toObject(a.slice(this.F.n8, this.F.n8*2)); - let z; - if (a.byteLength == this.F.n8*3) { - z = this.F.toObject(a.slice(this.F.n8*2, this.F.n8*3)); - } else { - z = this.F.toObject(this.F.one); - } - return [x, y, z]; - } + // tmp2 = tmp2 + tmp2; + c.call(f2mPrefix + "_add", t2, t2, t2), - fromObject(a) { - const x = this.F.fromObject(a[0]); - const y = this.F.fromObject(a[1]); - let z; - if (a.length==3) { - z = this.F.fromObject(a[2]); - } else { - z = this.F.one; - } - if (this.F.isZero(z, this.F.one)) { - return this.zeroAffine; - } else if (this.F.eq(z, this.F.one)) { - const buff = new Uint8Array(this.F.n8*2); - buff.set(x); - buff.set(y, this.F.n8); - return buff; - } else { - const buff = new Uint8Array(this.F.n8*3); - buff.set(x); - buff.set(y, this.F.n8); - buff.set(z, this.F.n8*2); - return buff; - } - } + // r.y -= tmp2; + c.call(f2mPrefix + "_sub", Ry, t2, Ry), - e(a) { - if (a instanceof Uint8Array) return a; - return this.fromObject(a); - } + // tmp3 = tmp4 * zsquared; + c.call(f2mPrefix + "_mul", t4, zsquared, t3), - x(a) { - const tmp = this.toAffine(a); - return tmp.slice(0, this.F.n8); - } + // tmp3 = tmp3 + tmp3; + c.call(f2mPrefix + "_add", t3, t3, t3), - y(a) { - const tmp = this.toAffine(a); - return tmp.slice(this.F.n8); - } + // tmp3 = -tmp3; + c.call(f2mPrefix + "_neg", t3, t3), -} + // tmp6 = tmp6.square() - tmp0 - tmp5; + c.call(f2mPrefix + "_square", t6, t6), + c.call(f2mPrefix + "_sub", t6, t0, t6), + c.call(f2mPrefix + "_sub", t6, t5, t6), -/* global WebAssembly */ + // tmp1 = tmp1 + tmp1; + c.call(f2mPrefix + "_add", t1, t1, t1), -function thread(self) { - const MAXMEM = 32767; - let instance; - let memory; + // tmp1 = tmp1 + tmp1; + c.call(f2mPrefix + "_add", t1, t1, t1), - if (self) { - self.onmessage = function(e) { - let data; - if (e.data) { - data = e.data; - } else { - data = e; - } + // tmp6 = tmp6 - tmp1; + c.call(f2mPrefix + "_sub", t6, t1, t6), - if (data[0].cmd == "INIT") { - init(data[0]).then(function() { - self.postMessage(data.result); - }); - } else if (data[0].cmd == "TERMINATE") { - self.close(); - } else { - const res = runTask(data); - self.postMessage(res); - } - }; + // tmp0 = r.z * zsquared; + c.call(f2mPrefix + "_mul", Rz, zsquared, t0), + + // tmp0 = tmp0 + tmp0; + c.call(f2mPrefix + "_add", t0, t0, t0), + + ); } - async function init(data) { - const code = new Uint8Array(data.code); - const wasmModule = await WebAssembly.compile(code); - memory = new WebAssembly.Memory({initial:data.init, maximum: MAXMEM}); + function buildPrepAddStep() { + const f = module.addFunction(prefix+ "_prepAddStep"); + f.addParam("R", "i32"); + f.addParam("Q", "i32"); + f.addParam("r", "i32"); - instance = await WebAssembly.instantiate(wasmModule, { - env: { - "memory": memory - } - }); - } + const c = f.getCodeBuilder(); + const Rx = c.getLocal("R"); + const Ry = c.i32_add(c.getLocal("R"), c.i32_const(2*n8q)); + const Rz = c.i32_add(c.getLocal("R"), c.i32_const(4*n8q)); + const Qx = c.getLocal("Q"); + const Qy = c.i32_add(c.getLocal("Q"), c.i32_const(2*n8q)); - function alloc(length) { - const u32 = new Uint32Array(memory.buffer, 0, 1); - while (u32[0] & 3) u32[0]++; // Return always aligned pointers - const res = u32[0]; - u32[0] += length; - if (u32[0] + length > memory.buffer.byteLength) { - const currentPages = memory.buffer.byteLength / 0x10000; - let requiredPages = Math.floor((u32[0] + length) / 0x10000)+1; - if (requiredPages>MAXMEM) requiredPages=MAXMEM; - memory.grow(requiredPages-currentPages); - } - return res; - } + const t10 = c.getLocal("r"); + const t1 = c.i32_add(c.getLocal("r"), c.i32_const(2*n8q)); + const t9 = c.i32_add(c.getLocal("r"), c.i32_const(4*n8q)); - function allocBuffer(buffer) { - const p = alloc(buffer.byteLength); - setBuffer(p, buffer); - return p; - } + const zsquared = c.i32_const(module.alloc(f2size)); + const ysquared = c.i32_const(module.alloc(f2size)); + const ztsquared = c.i32_const(module.alloc(f2size)); + const t0 = c.i32_const(module.alloc(f2size)); + const t2 = c.i32_const(module.alloc(f2size)); + const t3 = c.i32_const(module.alloc(f2size)); + const t4 = c.i32_const(module.alloc(f2size)); + const t5 = c.i32_const(module.alloc(f2size)); + const t6 = c.i32_const(module.alloc(f2size)); + const t7 = c.i32_const(module.alloc(f2size)); + const t8 = c.i32_const(module.alloc(f2size)); - function getBuffer(pointer, length) { - const u8 = new Uint8Array(memory.buffer); - return new Uint8Array(u8.buffer, u8.byteOffset + pointer, length); - } + f.addCode( - function setBuffer(pointer, buffer) { - const u8 = new Uint8Array(memory.buffer); - u8.set(new Uint8Array(buffer), pointer); - } + // zsquared = r.z.square(); + c.call(f2mPrefix + "_square", Rz, zsquared), - function runTask(task) { - if (task[0].cmd == "INIT") { - return init(task[0]); - } - const ctx = { - vars: [], - out: [] - }; - const u32a = new Uint32Array(memory.buffer, 0, 1); - const oldAlloc = u32a[0]; - for (let i=0; i. -*/ + // t5 = t4 * t2; + c.call(f2mPrefix + "_mul", t4, t2, t5), -// const MEM_SIZE = 1000; // Memory size in 64K Pakes (512Mb) -const MEM_SIZE = 25; // Memory size in 64K Pakes (1600Kb) + // t6 = t1 - r.y - r.y; + c.call(f2mPrefix + "_sub", t1, Ry, t6), + c.call(f2mPrefix + "_sub", t6, Ry, t6), -class Deferred { - constructor() { - this.promise = new Promise((resolve, reject)=> { - this.reject = reject; - this.resolve = resolve; - }); - } -} + // t9 = t6 * q.x; + c.call(f2mPrefix + "_mul", t6, Qx, t9), -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} + // t7 = t4 * r.x; + c.call(f2mPrefix + "_mul", t4, Rx, t7), -let workerSource; + // r.x = t6.square() - t5 - t7 - t7; + c.call(f2mPrefix + "_square", t6, Rx), + c.call(f2mPrefix + "_sub", Rx, t5, Rx), + c.call(f2mPrefix + "_sub", Rx, t7, Rx), + c.call(f2mPrefix + "_sub", Rx, t7, Rx), -const threadStr = `(${"function thread(self) {\n const MAXMEM = 32767;\n let instance;\n let memory;\n\n if (self) {\n self.onmessage = function(e) {\n let data;\n if (e.data) {\n data = e.data;\n } else {\n data = e;\n }\n\n if (data[0].cmd == \"INIT\") {\n init(data[0]).then(function() {\n self.postMessage(data.result);\n });\n } else if (data[0].cmd == \"TERMINATE\") {\n self.close();\n } else {\n const res = runTask(data);\n self.postMessage(res);\n }\n };\n }\n\n async function init(data) {\n const code = new Uint8Array(data.code);\n const wasmModule = await WebAssembly.compile(code);\n memory = new WebAssembly.Memory({initial:data.init, maximum: MAXMEM});\n\n instance = await WebAssembly.instantiate(wasmModule, {\n env: {\n \"memory\": memory\n }\n });\n }\n\n\n\n function alloc(length) {\n const u32 = new Uint32Array(memory.buffer, 0, 1);\n while (u32[0] & 3) u32[0]++; // Return always aligned pointers\n const res = u32[0];\n u32[0] += length;\n if (u32[0] + length > memory.buffer.byteLength) {\n const currentPages = memory.buffer.byteLength / 0x10000;\n let requiredPages = Math.floor((u32[0] + length) / 0x10000)+1;\n if (requiredPages>MAXMEM) requiredPages=MAXMEM;\n memory.grow(requiredPages-currentPages);\n }\n return res;\n }\n\n function allocBuffer(buffer) {\n const p = alloc(buffer.byteLength);\n setBuffer(p, buffer);\n return p;\n }\n\n function getBuffer(pointer, length) {\n const u8 = new Uint8Array(memory.buffer);\n return new Uint8Array(u8.buffer, u8.byteOffset + pointer, length);\n }\n\n function setBuffer(pointer, buffer) {\n const u8 = new Uint8Array(memory.buffer);\n u8.set(new Uint8Array(buffer), pointer);\n }\n\n function runTask(task) {\n if (task[0].cmd == \"INIT\") {\n return init(task[0]);\n }\n const ctx = {\n vars: [],\n out: []\n };\n const u32a = new Uint32Array(memory.buffer, 0, 1);\n const oldAlloc = u32a[0];\n for (let i=0; i64) concurrency=64; - tm.concurrency = concurrency; + function buildPrepareG2() { + const f = module.addFunction(prefix+ "_prepareG2"); + f.addParam("pQ", "i32"); + f.addParam("ppreQ", "i32"); + f.addLocal("pCoef", "i32"); + f.addLocal("i", "i32"); - for (let i = 0; i 0); i++) { - if (this.working[i] == false) { - const work = this.actionQueue.shift(); - this.postAction(i, work.data, work.transfers, work.deferred); - } - } - } + f.addCode( - queueAction(actionData, transfers) { - const d = new Deferred(); + c.call(f2mPrefix + "_add", A_c0, A_c1, Ac0_Ac1), + c.call(f2mPrefix + "_add", A_c1, A_c2, Ac1_Ac2), - if (this.singleThread) { - const res = this.taskManager(actionData); - d.resolve(res); - } else { - this.actionQueue.push({ - data: actionData, - transfers: transfers, - deferred: d - }); - this.processWorks(); - } - return d.promise; - } + // let b_b = self.c1 * c1; + c.call(f2mPrefix + "_mul", A_c1, c1, b_b), - resetMemory() { - this.u32[0] = this.initalPFree; - } + // let t1 = (self.c1 + self.c2) * c1 - b_b; + c.call(f2mPrefix + "_mul", Ac1_Ac2, c1, t1), + c.call(f2mPrefix + "_sub", t1, b_b, t1), - allocBuff(buff) { - const pointer = this.alloc(buff.byteLength); - this.setBuff(pointer, buff); - return pointer; - } + // let t1 = t1.mul_by_nonresidue(); + c.call(f2mPrefix + "_mulNR", t1, t1), - getBuff(pointer, length) { - return this.u8.slice(pointer, pointer+ length); + // let t2 = (self.c0 + self.c1) * c1 - b_b; + c.call(f2mPrefix + "_mul", Ac0_Ac1, c1, t2), + c.call(f2mPrefix + "_sub", t2, b_b, t2), + ); } + buildF6Mul1(); - setBuff(pointer, buffer) { - this.u8.set(new Uint8Array(buffer), pointer); - } + function buildF6Mul01() { + const f = module.addFunction(f6mPrefix+ "_mul01"); + f.addParam("pA", "i32"); // F6 + f.addParam("pC0", "i32"); // F2 + f.addParam("pC1", "i32"); // F2 + f.addParam("pR", "i32"); // F6 - alloc(length) { - while (this.u32[0] & 3) this.u32[0]++; // Return always aligned pointers - const res = this.u32[0]; - this.u32[0] += length; - return res; - } + const c = f.getCodeBuilder(); - async terminate() { - for (let i=0; i=0; i--) { - if (!G.isZero(res)) { - for (let j=0; jMAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; - if (chunkSize { - if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); - return r; - })); - } + // For A + // z0 = 3 * t0 - 2 * z0 + c.call(f2mPrefix + "_sub", t0, x0, r0), + c.call(f2mPrefix + "_add", r0, r0, r0), + c.call(f2mPrefix + "_add", t0, r0, r0), + // z1 = 3 * t1 + 2 * z1 + c.call(f2mPrefix + "_add", t1, x1, r1), + c.call(f2mPrefix + "_add", r1, r1, r1), + c.call(f2mPrefix + "_add", t1, r1, r1), - const result = await Promise.all(opPromises); + // For B + // z2 = 3 * (xi * t5) + 2 * z2 + c.call(f2mPrefix + "_mul", t5, c.i32_const(pBls12381Twist), AUX), + c.call(f2mPrefix + "_add", AUX, x2, r2), + c.call(f2mPrefix + "_add", r2, r2, r2), + c.call(f2mPrefix + "_add", AUX, r2, r2), + // z3 = 3 * t4 - 2 * z3 + c.call(f2mPrefix + "_sub", t4, x3, r3), + c.call(f2mPrefix + "_add", r3, r3, r3), + c.call(f2mPrefix + "_add", t4, r3, r3), - let res = G.zero; - for (let i=result.length-1; i>=0; i--) { - res = G.add(res, result[i]); - } + // For C + // z4 = 3 * t2 - 2 * z4 + c.call(f2mPrefix + "_sub", t2, x4, r4), + c.call(f2mPrefix + "_add", r4, r4, r4), + c.call(f2mPrefix + "_add", t2, r4, r4), + // z5 = 3 * t3 + 2 * z5 + c.call(f2mPrefix + "_add", t3, x5, r5), + c.call(f2mPrefix + "_add", r5, r5, r5), + c.call(f2mPrefix + "_add", t3, r5, r5), - return res; + ); } - G.multiExp = async function multiExpAffine(buffBases, buffScalars, logger, logText) { - return await _multiExp(buffBases, buffScalars, "jacobian", logger, logText); - }; - G.multiExpAffine = async function multiExpAffine(buffBases, buffScalars, logger, logText) { - return await _multiExp(buffBases, buffScalars, "affine", logger, logText); - }; -} - -function buildFFT(curve, groupName) { - const G = curve[groupName]; - const Fr = curve.Fr; - const tm = G.tm; - async function _fft(buff, inverse, inType, outType, logger, loggerTxt) { - inType = inType || "affine"; - outType = outType || "affine"; - const MAX_BITS_THREAD = 14; + function buildCyclotomicExp(exponent, isExpNegative, fnName) { + const exponentNafBytes = naf(exponent).map( (b) => (b==-1 ? 0xFF: b) ); + const pExponentNafBytes = module.alloc(exponentNafBytes); + // const pExponent = module.alloc(utils.bigInt2BytesLE(exponent, n8)); - let sIn, sMid, sOut, fnIn2Mid, fnMid2Out, fnFFTMix, fnFFTJoin, fnFFTFinal; - if (groupName == "G1") { - if (inType == "affine") { - sIn = G.F.n8*2; - fnIn2Mid = "g1m_batchToJacobian"; - } else { - sIn = G.F.n8*3; - } - sMid = G.F.n8*3; - if (inverse) { - fnFFTFinal = "g1m_fftFinal"; - } - fnFFTJoin = "g1m_fftJoin"; - fnFFTMix = "g1m_fftMix"; + const f = module.addFunction(prefix+ "__cyclotomicExp_"+fnName); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + f.addLocal("bit", "i32"); + f.addLocal("i", "i32"); - if (outType == "affine") { - sOut = G.F.n8*2; - fnMid2Out = "g1m_batchToAffine"; - } else { - sOut = G.F.n8*3; - } + const c = f.getCodeBuilder(); - } else if (groupName == "G2") { - if (inType == "affine") { - sIn = G.F.n8*2; - fnIn2Mid = "g2m_batchToJacobian"; - } else { - sIn = G.F.n8*3; - } - sMid = G.F.n8*3; - if (inverse) { - fnFFTFinal = "g2m_fftFinal"; - } - fnFFTJoin = "g2m_fftJoin"; - fnFFTMix = "g2m_fftMix"; - if (outType == "affine") { - sOut = G.F.n8*2; - fnMid2Out = "g2m_batchToAffine"; - } else { - sOut = G.F.n8*3; - } - } else if (groupName == "Fr") { - sIn = G.n8; - sMid = G.n8; - sOut = G.n8; - if (inverse) { - fnFFTFinal = "frm_fftFinal"; - } - fnFFTMix = "frm_fftMix"; - fnFFTJoin = "frm_fftJoin"; - } + const x = c.getLocal("x"); + const res = c.getLocal("r"); - let returnArray = false; - if (Array.isArray(buff)) { - buff = array2buffer(buff, sIn); - returnArray = true; - } else { - buff = buff.slice(0, buff.byteLength); - } + const inverse = c.i32_const(module.alloc(ftsize)); - const nPoints = buff.byteLength / sIn; - const bits = log2(nPoints); - if ((1 << bits) != nPoints) { - throw new Error("fft must be multiple of 2" ); - } + f.addCode( + c.call(ftmPrefix + "_conjugate", x, inverse), + c.call(ftmPrefix + "_one", res), - if (bits == Fr.s +1) { - let buffOut; + c.if( + c.teeLocal("bit", c.i32_load8_s(c.i32_const(exponentNafBytes.length-1), pExponentNafBytes)), + c.if( + c.i32_eq( + c.getLocal("bit"), + c.i32_const(1) + ), + c.call(ftmPrefix + "_mul", res, x, res), + c.call(ftmPrefix + "_mul", res, inverse, res), + ) + ), - if (inverse) { - buffOut = await _fftExtInv(buff, inType, outType, logger, loggerTxt); - } else { - buffOut = await _fftExt(buff, inType, outType, logger, loggerTxt); - } + c.setLocal("i", c.i32_const(exponentNafBytes.length-2)), + c.block(c.loop( + c.call(prefix + "__cyclotomicSquare", res, res), + c.if( + c.teeLocal("bit", c.i32_load8_s(c.getLocal("i"), pExponentNafBytes)), + c.if( + c.i32_eq( + c.getLocal("bit"), + c.i32_const(1) + ), + c.call(ftmPrefix + "_mul", res, x, res), + c.call(ftmPrefix + "_mul", res, inverse, res), + ) + ), + c.br_if(1, c.i32_eqz ( c.getLocal("i") )), + c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) + ); - if (returnArray) { - return buffer2array(buffOut, sOut); - } else { - return buffOut; - } + if (isExpNegative) { + f.addCode( + c.call(ftmPrefix + "_conjugate", res, res), + ); } - let inv; - if (inverse) { - inv = Fr.inv(Fr.e(nPoints)); - } + } - let buffOut; + function buildFinalExponentiation() { + buildCyclotomicSquare(); + buildCyclotomicExp(finalExpZ, finalExpIsNegative, "w0"); - buffReverseBits(buff, sIn); + const f = module.addFunction(prefix+ "_finalExponentiation"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); - let chunks; - let pointsInChunk = Math.min(1 << MAX_BITS_THREAD, nPoints); - let nChunks = nPoints / pointsInChunk; + const c = f.getCodeBuilder(); - while ((nChunks < tm.concurrency)&&(pointsInChunk>=16)) { - nChunks *= 2; - pointsInChunk /= 2; - } + const elt = c.getLocal("x"); + const res = c.getLocal("r"); + const t0 = c.i32_const(module.alloc(ftsize)); + const t1 = c.i32_const(module.alloc(ftsize)); + const t2 = c.i32_const(module.alloc(ftsize)); + const t3 = c.i32_const(module.alloc(ftsize)); + const t4 = c.i32_const(module.alloc(ftsize)); + const t5 = c.i32_const(module.alloc(ftsize)); + const t6 = c.i32_const(module.alloc(ftsize)); - const l2Chunk = log2(pointsInChunk); + f.addCode( - const promises = []; - for (let i = 0; i< nChunks; i++) { - if (logger) logger.debug(`${loggerTxt}: fft ${bits} mix start: ${i}/${nChunks}`); - 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}]}); - } - for (let j=1; j<=l2Chunk;j++) { - task.push({cmd: "CALL", fnName:fnFFTMix, params: [{var:0}, {val: pointsInChunk}, {val: j}]}); - } + // let mut t0 = f.frobenius_map(6) + c.call(ftmPrefix + "_frobeniusMap6", elt, t0), + + // let t1 = f.invert() + c.call(ftmPrefix + "_inverse", elt, t1), - if (l2Chunk==bits) { - if (fnFFTFinal) { - task.push({cmd: "ALLOCSET", var: 1, buff: inv}); - task.push({cmd: "CALL", fnName: fnFFTFinal, params:[ - {var: 0}, - {val: pointsInChunk}, - {var: 1}, - ]}); - } - if (fnMid2Out) { - task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:0}, {val: pointsInChunk}, {var: 0}]}); - } - task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sOut}); - } else { - task.push({cmd: "GET", out:0, var: 0, len: sMid*pointsInChunk}); - } - promises.push(tm.queueAction(task).then( (r) => { - if (logger) logger.debug(`${loggerTxt}: fft ${bits} mix end: ${i}/${nChunks}`); - return r; - })); - } + // let mut t2 = t0 * t1; + c.call(ftmPrefix + "_mul", t0, t1, t2), - chunks = await Promise.all(promises); - for (let i = 0; i< nChunks; i++) chunks[i] = chunks[i][0]; + // t1 = t2.clone(); + c.call(ftmPrefix + "_copy", t2, t1), - for (let i = l2Chunk+1; i<=bits; i++) { - if (logger) logger.debug(`${loggerTxt}: fft ${bits} join: ${i}/${bits}`); - const nGroups = 1 << (bits - i); - const nChunksPerGroup = nChunks / nGroups; - const opPromises = []; - for (let j=0; j { - if (logger) logger.debug(`${loggerTxt}: fft ${bits} join ${i}/${bits} ${j+1}/${nGroups} ${k}/${nChunksPerGroup/2}`); - return r; - })); - } - } + // t2 *= t1; + c.call(ftmPrefix + "_mul", t2, t1, t2), - const res = await Promise.all(opPromises); - for (let j=0; j0; i--) { - buffOut.set(chunks[i], p); - p += pointsInChunk*sOut; - delete chunks[i]; // Liberate mem - } - buffOut.set(chunks[0].slice(0, (pointsInChunk-1)*sOut), p); - delete chunks[0]; - } else { - for (let i=0; i (1<<28)) { - buffOut = new BigBuffer(res1[0].byteLength*2); - } else { - buffOut = new Uint8Array(res1[0].byteLength*2); - } + // t6 *= t4; + c.call(ftmPrefix + "_mul", t6, t4, t6), - buffOut.set(res1[0]); - buffOut.set(res1[1], res1[0].byteLength); + // t4 = cycolotomic_exp(t6); + c.call(prefix + "__cyclotomicExp_w0", t6, t4), - return buffOut; - } + // t5 = t5.conjugate(); + c.call(ftmPrefix + "_conjugate", t5, t5), - async function _fftExtInv(buff, inType, outType, logger, loggerTxt) { - let b1, b2; - b1 = buff.slice( 0 , buff.byteLength/2); - b2 = buff.slice( buff.byteLength/2, buff.byteLength); + // t4 *= t5 * t2; + c.call(ftmPrefix + "_mul", t4, t5, t4), + c.call(ftmPrefix + "_mul", t4, t2, t4), - const promises = []; + // t5 = t2.conjugate(); + c.call(ftmPrefix + "_conjugate", t2, t5), - promises.push( _fft(b1, true, inType, "jacobian", logger, loggerTxt)); - promises.push( _fft(b2, true, inType, "jacobian", logger, loggerTxt)); + // t1 *= t2; + c.call(ftmPrefix + "_mul", t1, t2, t1), - [b1, b2] = await Promise.all(promises); + // t1 = t1.frobenius_map().frobenius_map().frobenius_map(); + c.call(ftmPrefix + "_frobeniusMap3", t1, t1), - const res1 = await _fftJoinExt(b1, b2, "fftJoinExtInv", Fr.one, Fr.shiftInv, "jacobian", outType, logger, loggerTxt); + // t6 *= t5; + c.call(ftmPrefix + "_mul", t6, t5, t6), - let buffOut; - if (res1[0].byteLength > (1<<28)) { - buffOut = new BigBuffer(res1[0].byteLength*2); - } else { - buffOut = new Uint8Array(res1[0].byteLength*2); - } + // t6 = t6.frobenius_map(); + c.call(ftmPrefix + "_frobeniusMap1", t6, t6), - buffOut.set(res1[0]); - buffOut.set(res1[1], res1[0].byteLength); + // t3 *= t0; + c.call(ftmPrefix + "_mul", t3, t0, t3), - return buffOut; - } + // t3 = t3.frobenius_map().frobenius_map(); + c.call(ftmPrefix + "_frobeniusMap2", t3, t3), + // t3 *= t1; + c.call(ftmPrefix + "_mul", t3, t1, t3), - async function _fftJoinExt(buff1, buff2, fn, first, inc, inType, outType, logger, loggerTxt) { - const MAX_CHUNK_SIZE = 1<<16; - const MIN_CHUNK_SIZE = 1<<4; + // t3 *= t6; + c.call(ftmPrefix + "_mul", t3, t6, t3), - let fnName; - let fnIn2Mid, fnMid2Out; - let sOut, sIn, sMid; + // f = t3 * t4; + c.call(ftmPrefix + "_mul", t3, t4, res), - if (groupName == "G1") { - if (inType == "affine") { - sIn = G.F.n8*2; - fnIn2Mid = "g1m_batchToJacobian"; - } else { - sIn = G.F.n8*3; - } - sMid = G.F.n8*3; - fnName = "g1m_"+fn; - if (outType == "affine") { - fnMid2Out = "g1m_batchToAffine"; - sOut = G.F.n8*2; - } else { - sOut = G.F.n8*3; - } - } else if (groupName == "G2") { - if (inType == "affine") { - sIn = G.F.n8*2; - fnIn2Mid = "g2m_batchToJacobian"; - } else { - sIn = G.F.n8*3; - } - fnName = "g2m_"+fn; - sMid = G.F.n8*3; - if (outType == "affine") { - fnMid2Out = "g2m_batchToAffine"; - sOut = G.F.n8*2; - } else { - sOut = G.F.n8*3; - } - } else if (groupName == "Fr") { - sIn = Fr.n8; - sOut = Fr.n8; - sMid = Fr.n8; - fnName = "frm_" + fn; - } else { - throw new Error("Invalid group"); - } + ); + } + + + function buildFinalExponentiationOld() { + const f = module.addFunction(prefix+ "_finalExponentiationOld"); + f.addParam("x", "i32"); + f.addParam("r", "i32"); + + const exponent = 322277361516934140462891564586510139908379969514828494218366688025288661041104682794998680497580008899973249814104447692778988208376779573819485263026159588510513834876303014016798809919343532899164848730280942609956670917565618115867287399623286813270357901731510188149934363360381614501334086825442271920079363289954510565375378443704372994881406797882676971082200626541916413184642520269678897559532260949334760604962086348898118982248842634379637598665468817769075878555493752214492790122785850202957575200176084204422751485957336465472324810982833638490904279282696134323072515220044451592646885410572234451732790590013479358343841220074174848221722017083597872017638514103174122784843925578370430843522959600095676285723737049438346544753168912974976791528535276317256904336520179281145394686565050419250614107803233314658825463117900250701199181529205942363159325765991819433914303908860460720581408201373164047773794825411011922305820065611121544561808414055302212057471395719432072209245600258134364584636810093520285711072578721435517884103526483832733289802426157301542744476740008494780363354305116978805620671467071400711358839553375340724899735460480144599782014906586543813292157922220645089192130209334926661588737007768565838519456601560804957985667880395221049249803753582637708560n; - if (buff1.byteLength != buff2.byteLength) { - throw new Error("Invalid buffer size"); - } - const nPoints = Math.floor(buff1.byteLength / sIn); - if (nPoints != 1 << log2(nPoints)) { - throw new Error("Invalid number of points"); + const pExponent = module.alloc(utils.bigInt2BytesLE( exponent, 544 )); + + const c = f.getCodeBuilder(); + + f.addCode( + c.call(ftmPrefix + "_exp", c.getLocal("x"), c.i32_const(pExponent), c.i32_const(544), c.getLocal("r")), + ); + } + + + const pPreP = module.alloc(prePSize); + const pPreQ = module.alloc(preQSize); + + function buildPairingEquation(nPairings) { + + const f = module.addFunction(prefix+ "_pairingEq"+nPairings); + for (let i=0; i MAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; - const opPromises = []; + const c = f.getCodeBuilder(); - for (let i=0; i { - if (logger) logger.debug(`${loggerTxt}: fftJoinExt End: ${i}/${nPoints}`); - return r; - }) + f.addCode(c.call(prefix + "_prepareG1", c.getLocal("p_"+i), c.i32_const(pPreP) )); + f.addCode(c.call(prefix + "_prepareG2", c.getLocal("q_"+i), c.i32_const(pPreQ) )); + + // Checks + f.addCode( + c.if( + c.i32_eqz(c.call(g1mPrefix + "_inGroupAffine", c.i32_const(pPreP))), + c.ret(c.i32_const(0)) + ), + c.if( + c.i32_eqz(c.call(g2mPrefix + "_inGroupAffine", c.i32_const(pPreQ))), + c.ret(c.i32_const(0)) + ) ); - } - const result = await Promise.all(opPromises); + f.addCode(c.call(prefix + "_millerLoop", c.i32_const(pPreP), c.i32_const(pPreQ), auxT )); - let fullBuffOut1; - let fullBuffOut2; - if (nPoints * sOut > 1<<28) { - fullBuffOut1 = new BigBuffer(nPoints*sOut); - fullBuffOut2 = new BigBuffer(nPoints*sOut); - } else { - fullBuffOut1 = new Uint8Array(nPoints*sOut); - fullBuffOut2 = new Uint8Array(nPoints*sOut); + f.addCode(c.call(ftmPrefix + "_mul", resT, auxT, resT )); } - let p =0; - for (let i=0; i Fr.s+1) { - if (logger) logger.error("lagrangeEvaluations input too big"); - throw new Error("lagrangeEvaluations input too big"); - } + const c = f.getCodeBuilder(); - let t0 = buff.slice(0, buff.byteLength/2); - let t1 = buff.slice(buff.byteLength/2, buff.byteLength); + const WINV = [ + 2001204777610833696708894912867952078278441409969503942666029068062015825245418932221343814564507832018947136279894n, + 2001204777610833696708894912867952078278441409969503942666029068062015825245418932221343814564507832018947136279893n + ]; + const FROB2X = 4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436n; + const FROB3Y = [ + 2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530n, + 2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530n + ]; - const shiftToSmallM = Fr.exp(Fr.shift, nPoints/2); - const sConst = Fr.inv( Fr.sub(Fr.one, shiftToSmallM)); + const wInv = c.i32_const(module.alloc([ + ...utils.bigInt2BytesLE(toMontgomery(WINV[0]), n8q), + ...utils.bigInt2BytesLE(toMontgomery(WINV[1]), n8q), + ])); - [t0, t1] = await _fftJoinExt(t0, t1, "prepareLagrangeEvaluation", sConst, Fr.shiftInv, inType, "jacobian", logger, loggerTxt + " prep"); + const frob2X = c.i32_const(module.alloc(utils.bigInt2BytesLE(toMontgomery(FROB2X), n8q))); + const frob3Y = c.i32_const(module.alloc([ + ...utils.bigInt2BytesLE(toMontgomery(FROB3Y[0]), n8q), + ...utils.bigInt2BytesLE(toMontgomery(FROB3Y[1]), n8q), + ])); - const promises = []; + const z = c.i32_const(module.alloc(utils.bigInt2BytesLE(finalExpZ, 8))); - promises.push( _fft(t0, true, "jacobian", outType, logger, loggerTxt + " t0")); - promises.push( _fft(t1, true, "jacobian", outType, logger, loggerTxt + " t1")); + const px = c.getLocal("p"); + const py = c.i32_add(c.getLocal("p"), c.i32_const(f2size)); - [t0, t1] = await Promise.all(promises); + const aux = c.i32_const(module.alloc(f1size)); - let buffOut; - if (t0.byteLength > (1<<28)) { - buffOut = new BigBuffer(t0.byteLength*2); - } else { - buffOut = new Uint8Array(t0.byteLength*2); - } + const x_winv = c.i32_const(module.alloc(f2size)); + const y_winv = c.i32_const(module.alloc(f2size)); + const pf2 = module.alloc(f2size*2); + const f2 = c.i32_const(pf2); + const f2x = c.i32_const(pf2); + const f2x_c1 = c.i32_const(pf2); + const f2x_c2 = c.i32_const(pf2+f1size); + const f2y = c.i32_const(pf2+f2size); + const f2y_c1 = c.i32_const(pf2+f2size); + const f2y_c2 = c.i32_const(pf2+f2size+f1size); + const pf3 = module.alloc(f2size*3); + const f3 = c.i32_const(pf3); + const f3x = c.i32_const(pf3); + const f3x_c1 = c.i32_const(pf3); + const f3x_c2 = c.i32_const(pf3+f1size); + const f3y = c.i32_const(pf3+f2size); + const f3y_c1 = c.i32_const(pf3+f2size); + const f3y_c2 = c.i32_const(pf3+f2size+f1size); + const f3z = c.i32_const(pf3+f2size*2); - buffOut.set(t0); - buffOut.set(t1, t0.byteLength); - return buffOut; - }; + f.addCode( + c.if( + c.call(g2mPrefix + "_isZeroAffine", c.getLocal("p")), + c.ret( c.i32_const(1)), + ), + c.if( + c.i32_eqz(c.call(g2mPrefix + "_inCurveAffine", c.getLocal("p"))), + c.ret( c.i32_const(0)), + ), + c.call(f2mPrefix + "_mul", px, wInv, x_winv), + c.call(f2mPrefix + "_mul", py, wInv, y_winv), - G.fftMix = async function fftMix(buff) { - const sG = G.F.n8*3; - let fnName, fnFFTJoin; - if (groupName == "G1") { - fnName = "g1m_fftMix"; - fnFFTJoin = "g1m_fftJoin"; - } else if (groupName == "G2") { - fnName = "g2m_fftMix"; - fnFFTJoin = "g2m_fftJoin"; - } else if (groupName == "Fr") { - fnName = "frm_fftMix"; - fnFFTJoin = "frm_fftJoin"; - } else { - throw new Error("Invalid group"); - } + c.call(f2mPrefix + "_mul1", x_winv, frob2X, f2x), + c.call(f2mPrefix + "_neg", y_winv, f2y), + + c.call(f2mPrefix + "_neg", x_winv, f3x), + c.call(f2mPrefix + "_mul", y_winv, frob3Y, f3y), + + c.call(f1mPrefix + "_sub", f2x_c1, f2x_c2, aux), + c.call(f1mPrefix + "_add", f2x_c1, f2x_c2, f2x_c2), + c.call(f1mPrefix + "_copy", aux, f2x_c1), + + c.call(f1mPrefix + "_sub", f2y_c1, f2y_c2, aux), + c.call(f1mPrefix + "_add", f2y_c1, f2y_c2, f2y_c2), + c.call(f1mPrefix + "_copy", aux, f2y_c1), + + c.call(f1mPrefix + "_add", f3x_c1, f3x_c2, aux), + c.call(f1mPrefix + "_sub", f3x_c1, f3x_c2, f3x_c2), + c.call(f1mPrefix + "_copy", aux, f3x_c1), - const nPoints = Math.floor(buff.byteLength / sG); - const power = log2(nPoints); + c.call(f1mPrefix + "_sub", f3y_c2, f3y_c1, aux), + c.call(f1mPrefix + "_add", f3y_c1, f3y_c2, f3y_c2), + c.call(f1mPrefix + "_copy", aux, f3y_c1), - let nChunks = 1 << log2(tm.concurrency); + c.call(f2mPrefix + "_one", f3z), - if (nPoints <= nChunks*2) nChunks = 1; + c.call(g2mPrefix + "_timesScalar", f3, z, c.i32_const(8), f3), + c.call(g2mPrefix + "_addMixed", f3, f2, f3), - const pointsPerChunk = nPoints / nChunks; + c.ret( + c.call(g2mPrefix + "_eqMixed", f3, c.getLocal("p")) + ) + ); - const powerChunk = log2(pointsPerChunk); + const fInGroup = module.addFunction(g2mPrefix + "_inGroup"); + fInGroup.addParam("pIn", "i32"); + fInGroup.setReturnType("i32"); - const opPromises = []; - for (let i=0; i=0; i--) { - fullBuffOut.set(result[i][0], p); - p+=result[i][0].byteLength; - } + buildPrepareG1(); + buildPrepareG2(); - return fullBuffOut; - }; -} + buildMillerLoop(); -async function buildEngine(params) { + buildFinalExponentiationOld(); + buildFinalExponentiation(); - const tm = await buildThreadManager(params.wasm, params.singleThread); + for (let i=1; i<=5; i++) { + buildPairingEquation(i); + module.exportFunction(prefix + "_pairingEq"+i); + } + buildPairing(); - const curve = {}; + module.exportFunction(prefix + "_pairing"); - curve.q = e(params.wasm.q.toString()); - curve.r = e(params.wasm.r.toString()); - curve.name = params.name; - curve.tm = tm; - curve.prePSize = params.wasm.prePSize; - curve.preQSize = params.wasm.preQSize; - curve.Fr = new WasmField1(tm, "frm", params.n8r, params.r); - curve.F1 = new WasmField1(tm, "f1m", params.n8q, params.q); - curve.F2 = new WasmField2(tm, "f2m", curve.F1); - curve.G1 = new WasmCurve(tm, "g1m", curve.F1, params.wasm.pG1gen, params.wasm.pG1b, params.cofactorG1); - curve.G2 = new WasmCurve(tm, "g2m", curve.F2, params.wasm.pG2gen, params.wasm.pG2b, params.cofactorG2); - curve.F6 = new WasmField3(tm, "f6m", curve.F2); - curve.F12 = new WasmField2(tm, "ftm", curve.F6); - curve.Gt = curve.F12; + module.exportFunction(prefix + "_prepareG1"); + module.exportFunction(prefix + "_prepareG2"); + module.exportFunction(prefix + "_millerLoop"); + module.exportFunction(prefix + "_finalExponentiation"); + module.exportFunction(prefix + "_finalExponentiationOld"); + module.exportFunction(prefix + "__cyclotomicSquare"); + module.exportFunction(prefix + "__cyclotomicExp_w0"); - buildBatchApplyKey(curve, "G1"); - buildBatchApplyKey(curve, "G2"); - buildBatchApplyKey(curve, "Fr"); + module.exportFunction(f6mPrefix + "_mul1"); + module.exportFunction(f6mPrefix + "_mul01"); + module.exportFunction(ftmPrefix + "_mul014"); - buildMultiexp(curve, "G1"); - buildMultiexp(curve, "G2"); + module.exportFunction(g1mPrefix + "_inGroupAffine"); + module.exportFunction(g1mPrefix + "_inGroup"); + module.exportFunction(g2mPrefix + "_inGroupAffine"); + module.exportFunction(g2mPrefix + "_inGroup"); - buildFFT(curve, "G1"); - buildFFT(curve, "G2"); - buildFFT(curve, "Fr"); + // console.log(module.functionIdxByName); +}; - buildPairing(curve); +/* + Copyright 2019 0KIMS association. - curve.array2buffer = function(arr, sG) { - const buff = new Uint8Array(sG*arr.length); + This file is part of wasmsnark (Web Assembly zkSnark Prover). - for (let i=0; i. +*/ - return curve; -} +// module.exports.bn128_wasm = require("./build/bn128_wasm.js"); +var bn128_wasm_gzip = bn128_wasm_gzip$1; +// module.exports.bls12381_wasm = require("./build/bls12381_wasm.js"); +// module.exports.mnt6753_wasm = require("./build/mnt6753_wasm.js"); + +var buildBn128 = build_bn128; +var buildBls12381 = build_bls12381; + +var index = /*#__PURE__*/Object.freeze({ + __proto__: null, + bn128_wasm_gzip: bn128_wasm_gzip, + buildBls12381: buildBls12381, + buildBn128: buildBn128 +}); /* Copyright 2019 0KIMS association. @@ -18026,169 +18489,28 @@ class ModuleBuilder { } -globalThis.curve_bn128 = null; - -async function buildBn128(singleThread, plugins) { - if ((!singleThread) && (globalThis.curve_bn128)) return globalThis.curve_bn128; - - const moduleBuilder = new ModuleBuilder(); - moduleBuilder.setMemory(25); - buildBn128$1(moduleBuilder); - - if (plugins) plugins(moduleBuilder); - - const bn128wasm = {}; - - bn128wasm.code = moduleBuilder.build(); - bn128wasm.pq = moduleBuilder.modules.f1m.pq; - bn128wasm.pr = moduleBuilder.modules.frm.pq; - bn128wasm.pG1gen = moduleBuilder.modules.bn128.pG1gen; - bn128wasm.pG1zero = moduleBuilder.modules.bn128.pG1zero; - bn128wasm.pG1b = moduleBuilder.modules.bn128.pG1b; - bn128wasm.pG2gen = moduleBuilder.modules.bn128.pG2gen; - bn128wasm.pG2zero = moduleBuilder.modules.bn128.pG2zero; - bn128wasm.pG2b = moduleBuilder.modules.bn128.pG2b; - bn128wasm.pOneT = moduleBuilder.modules.bn128.pOneT; - bn128wasm.prePSize = moduleBuilder.modules.bn128.prePSize; - bn128wasm.preQSize = moduleBuilder.modules.bn128.preQSize; - bn128wasm.n8q = 32; - bn128wasm.n8r = 32; - bn128wasm.q = moduleBuilder.modules.bn128.q; - bn128wasm.r = moduleBuilder.modules.bn128.r; - - const params = { - name: "bn128", - wasm: bn128wasm, - q: e("21888242871839275222246405745257275088696311157297823662689037894645226208583"), - r: e("21888242871839275222246405745257275088548364400416034343698204186575808495617"), - n8q: 32, - n8r: 32, - cofactorG2: e("30644e72e131a029b85045b68181585e06ceecda572a2489345f2299c0f9fa8d", 16), - singleThread: singleThread ? true : false - }; - - const curve = await buildEngine(params); - curve.terminate = async function () { - if (!params.singleThread) { - globalThis.curve_bn128 = null; - await this.tm.terminate(); - } - }; - - if (!singleThread) { - globalThis.curve_bn128 = curve; - } - - return curve; -} - -globalThis.curve_bls12381 = null; - -async function buildBls12381(singleThread, plugins) { - if ((!singleThread) && (globalThis.curve_bls12381)) return globalThis.curve_bls12381; - - const moduleBuilder = new ModuleBuilder(); - moduleBuilder.setMemory(25); - buildBls12381$1(moduleBuilder); - - if (plugins) plugins(moduleBuilder); - - const bls12381wasm = {}; - - bls12381wasm.code = moduleBuilder.build(); - bls12381wasm.pq = moduleBuilder.modules.f1m.pq; - bls12381wasm.pr = moduleBuilder.modules.frm.pq; - bls12381wasm.pG1gen = moduleBuilder.modules.bls12381.pG1gen; - bls12381wasm.pG1zero = moduleBuilder.modules.bls12381.pG1zero; - bls12381wasm.pG1b = moduleBuilder.modules.bls12381.pG1b; - bls12381wasm.pG2gen = moduleBuilder.modules.bls12381.pG2gen; - bls12381wasm.pG2zero = moduleBuilder.modules.bls12381.pG2zero; - bls12381wasm.pG2b = moduleBuilder.modules.bls12381.pG2b; - bls12381wasm.pOneT = moduleBuilder.modules.bls12381.pOneT; - bls12381wasm.prePSize = moduleBuilder.modules.bls12381.prePSize; - bls12381wasm.preQSize = moduleBuilder.modules.bls12381.preQSize; - bls12381wasm.n8q = 48; - bls12381wasm.n8r = 32; - bls12381wasm.q = moduleBuilder.modules.bls12381.q; - bls12381wasm.r = moduleBuilder.modules.bls12381.r; - - - const params = { - name: "bls12381", - wasm: bls12381wasm, - q: e("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", 16), - r: e("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", 16), - n8q: 48, - n8r: 32, - cofactorG1: e("0x396c8c005555e1568c00aaab0000aaab", 16), - cofactorG2: e("0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5", 16), - singleThread: singleThread ? true : false - }; - - const curve = await buildEngine(params); - curve.terminate = async function () { - if (!params.singleThread) { - globalThis.curve_bls12381 = null; - await this.tm.terminate(); - } - }; - - if (!singleThread) { - globalThis.curve_bls12381 = curve; - } - - return curve; -} - -const bls12381r = e("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", 16); -const bn128r = e("21888242871839275222246405745257275088548364400416034343698204186575808495617"); - -const bls12381q = e("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", 16); -const bn128q = e("21888242871839275222246405745257275088696311157297823662689037894645226208583"); - -async function getCurveFromR(r, singleThread, plugins) { - let curve; - if (eq(r, bn128r)) { - curve = await buildBn128(singleThread, plugins); - } else if (eq(r, bls12381r)) { - curve = await buildBls12381(singleThread, plugins); - } else { - throw new Error(`Curve not supported: ${toString(r)}`); - } - return curve; -} +/* + Copyright 2019 0KIMS association. -async function getCurveFromQ(q, singleThread, plugins) { - let curve; - if (eq(q, bn128q)) { - curve = await buildBn128(singleThread, plugins); - } else if (eq(q, bls12381q)) { - curve = await buildBls12381(singleThread, plugins); - } else { - throw new Error(`Curve not supported: ${toString(q, 16)}`); - } - return curve; -} + This file is part of wasmbuilder -async function getCurveFromName(name, singleThread, plugins) { - let curve; - const normName = normalizeName(name); - if (["BN128", "BN254", "ALTBN128"].indexOf(normName) >= 0) { - curve = await buildBn128(singleThread, plugins); - } else if (["BLS12381"].indexOf(normName) >= 0) { - curve = await buildBls12381(singleThread, plugins); - } else { - throw new Error(`Curve not supported: ${name}`); - } - return curve; + wasmbuilder 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. - function normalizeName(n) { - return n.toUpperCase().match(/[A-Za-z0-9]+/g).join(""); - } + wasmbuilder 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 wasmbuilder. If not, see . +*/ -const Scalar=_Scalar; -const utils = _utils; +var main = /*#__PURE__*/Object.freeze({ + __proto__: null, + ModuleBuilder: ModuleBuilder +}); -export { BigBuffer, ChaCha, EC, ZqField as F1Field, F2Field, F3Field, PolField, Scalar, ZqField, buildBls12381, buildBn128, getCurveFromName, getCurveFromQ, getCurveFromR, utils }; +export { BigBuffer, ChaCha, EC, ZqField as F1Field, F2Field, F3Field, PolField, Scalar, ZqField, buildBls12381$1 as buildBls12381, buildBn128$1 as buildBn128, getCurveFromName, getCurveFromQ, getCurveFromR, utils$6 as utils }; diff --git a/build/main.cjs b/build/main.cjs index 5bb247b..4448871 100644 --- a/build/main.cjs +++ b/build/main.cjs @@ -1,10 +1,8 @@ 'use strict'; var crypto = require('crypto'); -var wasmcurves = require('wasmcurves'); var os = require('os'); var Worker = require('web-worker'); -var wasmbuilder = require('wasmbuilder'); /* global BigInt */ const hexLen = [ 0, 1, 2, 2, 3, 3, 3, 3, 4 ,4 ,4 ,4 ,4 ,4 ,4 ,4]; @@ -3034,7 +3032,7 @@ var _utils = /*#__PURE__*/Object.freeze({ unstringifyFElements: unstringifyFElements }); -const PAGE_SIZE = 1<<30; +const PAGE_SIZE = ( typeof Buffer !== "undefined" && Buffer.constants && Buffer.constants.MAX_LENGTH ) ? Buffer.constants.MAX_LENGTH : (1 << 30); class BigBuffer { @@ -3044,7 +3042,11 @@ class BigBuffer { for (let i=0; iMAXMEM) requiredPages=MAXMEM; memory.grow(requiredPages-currentPages); + console.log("Growing memory to", memory.buffer.byteLength / 1024 / 1024, "MB"); } return res; } @@ -4310,8 +4367,9 @@ function thread(self) { } function getBuffer(pointer, length) { - const u8 = new Uint8Array(memory.buffer); - return new Uint8Array(u8.buffer, u8.byteOffset + pointer, length); + // const u8 = new Uint8Array(memory.buffer); + // return new Uint8Array(u8.buffer, u8.byteOffset + pointer, length); + return new Uint8Array(memory.buffer, pointer, length); } function setBuffer(pointer, buffer) { @@ -4320,7 +4378,8 @@ function thread(self) { } function runTask(task) { - if (task[0].cmd == "INIT") { + clearTimeout(terminationTimer); + if (task[0].cmd === "INIT") { return init(task[0]); } const ctx = { @@ -4362,9 +4421,31 @@ function thread(self) { } const u32b = new Uint32Array(memory.buffer, 0, 1); u32b[0] = oldAlloc; + + //console.log(ctx.out); + return ctx.out; } + function scheduleTermination() { + if (terminationTimeout>0) { + terminationTimer = setTimeout( () => { + console.log("Shutting down thread due to inactivity"); + terminate(); + }, terminationTimeout); + } + } + + function terminate() { + clearTimeout(terminationTimer); + instance = null; + memory = null; + if (self) { + console.log("TERMINATE"); + self.postMessage({status: "terminated"}); + self.close(); + } + } return runTask; } @@ -4400,10 +4481,6 @@ class Deferred { } } -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - let workerSource; const threadStr = `(${thread.toString()})(self)`; @@ -4435,7 +4512,7 @@ async function buildThreadManager(wasm, singleThread) { "memory": tm.memory } }); - + if(process.browser && !globalThis?.Worker) { singleThread = true; } @@ -4450,11 +4527,13 @@ async function buildThreadManager(wasm, singleThread) { tm.pG2zero = wasm.pG2zero; tm.pOneT = wasm.pOneT; + tm.code = wasm.code; + tm.wasmModule = wasmModule; + // 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.taskManager = thread(); await tm.taskManager([{ cmd: "INIT", @@ -4466,6 +4545,8 @@ async function buildThreadManager(wasm, singleThread) { tm.workers = []; tm.pendingDeferreds = []; tm.working = []; + tm.initialized = []; + tm.initializing = []; let concurrency = 2; if (process.browser) { @@ -4476,39 +4557,69 @@ async function buildThreadManager(wasm, singleThread) { concurrency = os.cpus().length; } - if(concurrency == 0){ + if(concurrency === 0){ concurrency = 2; } + //concurrency = 10; // For testing + // Limit to 64 threads for memory reasons. if (concurrency>64) concurrency=64; tm.concurrency = concurrency; - for (let i = 0; i { + this.initialized[i] = true; + }); } startSyncOp() { - if (this.oldPFree != 0) throw new Error("Sync operation in progress"); + if (this.oldPFree !== 0) throw new Error("Sync operation in progress"); this.oldPFree = this.u32[0]; } endSyncOp() { - if (this.oldPFree == 0) throw new Error("No sync operation in progress"); + 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) { + async postAction(workerId, e, transfers, _deferred) { if (this.working[workerId]) { - throw new Error("Posting a job t a working worker"); + throw new Error("Posting a job to a working worker"); } this.working[workerId] = true; this.pendingDeferreds[workerId] = _deferred ? _deferred : new Deferred(); - this.workers[workerId].postMessage(e, transfers); + await this.workers[workerId].postMessage(e, transfers); return this.pendingDeferreds[workerId].promise; } - processWorks() { - for (let i=0; (i 0); i++) { - if (this.working[i] == false) { + async processWorks() { + for (let i=0; (i 0); i++) { + if (this.workers[i] && this.initialized[i] && !this.working[i]) { const work = this.actionQueue.shift(); this.postAction(i, work.data, work.transfers, work.deferred); } } + + // Initialize more workers if needed + if (this.actionQueue.length > 0) { + // Find a worker that is not initialized yet + let initializingCount = 0; + for (let i=0; i= this.actionQueue.length) break; + + // Initialize this worker + console.log(`Worker ${i} not initialized yet. Initializing...`); + initializingCount++; + await this.startWorker(i); + //this.startWorker(i); + } + } } queueAction(actionData, transfers) { @@ -4606,10 +4789,12 @@ class ThreadManager { } async terminate() { + console.log("terminate!!!"); for (let i=0; iMAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; if (chunkSize { + + opPromises.push(_multiExpChunk(buffBasesChunk, buffScalarsChunk, inType, logger, logText).then((r) => { if (logger) logger.debug(`Multiexp end: ${logText}: ${i}/${nPoints}`); return r; })); } - const result = await Promise.all(opPromises); + let result = await Promise.all(opPromises); let res = G.zero; for (let i=result.length-1; i>=0; i--) { @@ -5027,7 +5235,7 @@ function buildFFT(curve, groupName) { outType = outType || "affine"; const MAX_BITS_THREAD = 14; - let sIn, sMid, sOut, fnIn2Mid, fnMid2Out, fnFFTMix, fnFFTJoin, fnFFTFinal; + let sIn, sMid, sOut, fnIn2Mid, fnMid2Out, fnFFTMix, fnFFTJoin, fnFFTFinal, fnReversePermutation; if (groupName == "G1") { if (inType == "affine") { sIn = G.F.n8*2; @@ -5041,6 +5249,7 @@ function buildFFT(curve, groupName) { } fnFFTJoin = "g1m_fftJoin"; fnFFTMix = "g1m_fftMix"; + fnReversePermutation = "g1m_reversePermutation"; if (outType == "affine") { sOut = G.F.n8*2; @@ -5062,6 +5271,7 @@ function buildFFT(curve, groupName) { } fnFFTJoin = "g2m_fftJoin"; fnFFTMix = "g2m_fftMix"; + fnReversePermutation = "g2m_reversePermutation"; if (outType == "affine") { sOut = G.F.n8*2; fnMid2Out = "g2m_batchToAffine"; @@ -5077,6 +5287,7 @@ function buildFFT(curve, groupName) { } fnFFTMix = "frm_fftMix"; fnFFTJoin = "frm_fftJoin"; + fnReversePermutation = "frm_fftReversePermutation"; } @@ -5088,9 +5299,13 @@ function buildFFT(curve, groupName) { buff = buff.slice(0, buff.byteLength); } + console.log("FFT input size:", buff.byteLength, " bytes"); + const nPoints = buff.byteLength / sIn; const bits = log2(nPoints); + console.log("FFT points:", nPoints, " bits:", bits); + if ((1 << bits) != nPoints) { throw new Error("fft must be multiple of 2" ); } @@ -5118,7 +5333,19 @@ function buildFFT(curve, groupName) { let buffOut; - buffReverseBits(buff, sIn); + // TODO: optimize. Move to wasm? + //buffReverseBits(buff, sIn); + + console.log("fnReversePermutation:", fnReversePermutation); + + const task = []; + task.push({cmd: "ALLOC", var: 0, len: buff.byteLength}); + task.push({cmd: "SET", var: 0, buff: buff}); + task.push({cmd: "CALL", fnName: fnReversePermutation, params: [{var:0}, {val: bits}, {var: 0}]}); + task.push({cmd: "GET", out:0, var: 0, len: buff.byteLength}); + const res = await tm.queueAction(task, [buff.buffer]); + + buff.set(res[0]); let chunks; let pointsInChunk = Math.min(1 << MAX_BITS_THREAD, nPoints); @@ -5161,7 +5388,7 @@ function buildFFT(curve, groupName) { } else { task.push({cmd: "GET", out:0, var: 0, len: sMid*pointsInChunk}); } - promises.push(tm.queueAction(task).then( (r) => { + promises.push(tm.queueAction(task, [buffChunk.buffer]).then( (r) => { if (logger) logger.debug(`${loggerTxt}: fft ${bits} mix end: ${i}/${nChunks}`); return r; })); @@ -5218,7 +5445,7 @@ function buildFFT(curve, groupName) { task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sMid}); task.push({cmd: "GET", out: 1, var: 1, len: pointsInChunk*sMid}); } - opPromises.push(tm.queueAction(task).then( (r) => { + opPromises.push(tm.queueAction(task, [chunks[o1].buffer, chunks[o2].buffer, first.buffer ]).then( (r) => { if (logger) logger.debug(`${loggerTxt}: fft ${bits} join ${i}/${bits} ${j+1}/${nGroups} ${k}/${nChunksPerGroup/2}`); return r; })); @@ -5417,7 +5644,7 @@ function buildFFT(curve, groupName) { task.push({cmd: "GET", out: 0, var: 0, len: n*sOut}); task.push({cmd: "GET", out: 1, var: 1, len: n*sOut}); opPromises.push( - tm.queueAction(task).then( (r) => { + tm.queueAction(task, [b1.buffer, b2.buffer, firstChunk.buffer]).then((r) => { if (logger) logger.debug(`${loggerTxt}: fftJoinExt End: ${i}/${nPoints}`); return r; }) @@ -5565,7 +5792,7 @@ function buildFFT(curve, groupName) { } task.push({cmd: "GET", out: 0, var: 0, len: pointsPerChunk*sG}); opPromises.push( - tm.queueAction(task) + tm.queueAction(task, [b.buffer]) ); } @@ -5600,7 +5827,7 @@ function buildFFT(curve, groupName) { ]}); task.push({cmd: "GET", out: 0, var: 0, len: pointsPerChunk*sG}); task.push({cmd: "GET", out: 1, var: 1, len: pointsPerChunk*sG}); - opPromises.push(tm.queueAction(task)); + opPromises.push(tm.queueAction(task, [chunks[o1].buffer, chunks[o2].buffer, first.buffer])); } } @@ -5679,7 +5906,7 @@ function buildFFT(curve, groupName) { task.push({cmd: "GET", out: 0, var: 0, len: pointsPerChunk*sG}); task.push({cmd: "GET", out: 1, var: 1, len: pointsPerChunk*sG}); opPromises.push( - tm.queueAction(task) + tm.queueAction(task, [b1.buffer, b2.buffer, firstChunk.buffer]) ); } @@ -5755,7 +5982,7 @@ function buildFFT(curve, groupName) { ]}); task.push({cmd: "GET", out: 0, var: 0, len: n*sGout}); opPromises.push( - tm.queueAction(task) + tm.queueAction(task, [b.buffer]) ); } @@ -5837,35 +6064,79 @@ async function buildEngine(params) { return curve; } +//import { bn128_wasm_gzip as bn128wasmPrebuilt } from "wasmcurves"; + globalThis.curve_bn128 = null; async function buildBn128(singleThread, plugins) { if ((!singleThread) && (globalThis.curve_bn128)) return globalThis.curve_bn128; - const moduleBuilder = new wasmbuilder.ModuleBuilder(); - moduleBuilder.setMemory(25); - wasmcurves.buildBn128(moduleBuilder); + let bn128wasm = {}; - if (plugins) plugins(moduleBuilder); + if (!plugins) { + + console.log("Using prebuilt bn128 wasm"); - const bn128wasm = {}; - - bn128wasm.code = moduleBuilder.build(); - bn128wasm.pq = moduleBuilder.modules.f1m.pq; - bn128wasm.pr = moduleBuilder.modules.frm.pq; - bn128wasm.pG1gen = moduleBuilder.modules.bn128.pG1gen; - bn128wasm.pG1zero = moduleBuilder.modules.bn128.pG1zero; - bn128wasm.pG1b = moduleBuilder.modules.bn128.pG1b; - bn128wasm.pG2gen = moduleBuilder.modules.bn128.pG2gen; - bn128wasm.pG2zero = moduleBuilder.modules.bn128.pG2zero; - bn128wasm.pG2b = moduleBuilder.modules.bn128.pG2b; - bn128wasm.pOneT = moduleBuilder.modules.bn128.pOneT; - bn128wasm.prePSize = moduleBuilder.modules.bn128.prePSize; - bn128wasm.preQSize = moduleBuilder.modules.bn128.preQSize; - bn128wasm.n8q = 32; - bn128wasm.n8r = 32; - bn128wasm.q = moduleBuilder.modules.bn128.q; - bn128wasm.r = moduleBuilder.modules.bn128.r; + //import { bn128_wasm_gzip as bn128wasmPrebuilt } from "wasmcurves"; + const { bn128_wasm_gzip: bn128wasmPrebuilt } = await import('wasmcurves'); + //const { default: bn128wasmPrebuilt } = await import("wasmcurves/build/bn128_wasm_gzip.js"); + + //console.log(bn128wasmPrebuilt); + bn128wasm.pq = bn128wasmPrebuilt.pq; + bn128wasm.pr = bn128wasmPrebuilt.pq; + bn128wasm.pG1gen = bn128wasmPrebuilt.pG1gen; + bn128wasm.pG1zero = bn128wasmPrebuilt.pG1zero; + bn128wasm.pG1b = bn128wasmPrebuilt.pG1b; + bn128wasm.pG2gen = bn128wasmPrebuilt.pG2gen; + bn128wasm.pG2zero = bn128wasmPrebuilt.pG2zero; + bn128wasm.pG2b = bn128wasmPrebuilt.pG2b; + bn128wasm.pOneT = bn128wasmPrebuilt.pOneT; + bn128wasm.prePSize = bn128wasmPrebuilt.prePSize; + bn128wasm.preQSize = bn128wasmPrebuilt.preQSize; + bn128wasm.n8q = 32; + bn128wasm.n8r = 32; + bn128wasm.q = bn128wasmPrebuilt.q; + bn128wasm.r = bn128wasmPrebuilt.r; + + const compressedCode = new Uint8Array(Buffer.from(bn128wasmPrebuilt.gzipCode, "base64")); + const blob = new Blob([compressedCode]); + + const ds = new DecompressionStream("gzip"); + const decompressedStream = blob.stream().pipeThrough(ds); + + bn128wasm.code = await new Response(decompressedStream).bytes(); + } else { + + //import { ModuleBuilder } from "wasmbuilder"; + //import { buildBn128 as buildBn128wasm } from "wasmcurves"; + const { ModuleBuilder } = await import('wasmbuilder'); + const { buildBn128: buildBn128wasm } = await import('wasmcurves'); + + const moduleBuilder = new ModuleBuilder(); + moduleBuilder.setMemory(25); + buildBn128wasm(moduleBuilder); + + if (plugins) plugins(moduleBuilder); + + bn128wasm.code = moduleBuilder.build(); + bn128wasm.pq = moduleBuilder.modules.f1m.pq; + bn128wasm.pr = moduleBuilder.modules.frm.pq; + bn128wasm.pG1gen = moduleBuilder.modules.bn128.pG1gen; + bn128wasm.pG1zero = moduleBuilder.modules.bn128.pG1zero; + bn128wasm.pG1b = moduleBuilder.modules.bn128.pG1b; + bn128wasm.pG2gen = moduleBuilder.modules.bn128.pG2gen; + bn128wasm.pG2zero = moduleBuilder.modules.bn128.pG2zero; + bn128wasm.pG2b = moduleBuilder.modules.bn128.pG2b; + bn128wasm.pOneT = moduleBuilder.modules.bn128.pOneT; + bn128wasm.prePSize = moduleBuilder.modules.bn128.prePSize; + bn128wasm.preQSize = moduleBuilder.modules.bn128.preQSize; + bn128wasm.n8q = 32; + bn128wasm.n8r = 32; + bn128wasm.q = moduleBuilder.modules.bn128.q; + bn128wasm.r = moduleBuilder.modules.bn128.r; + } + + //console.log("bn128wasm:", bn128wasm); const params = { name: "bn128", @@ -5898,9 +6169,12 @@ globalThis.curve_bls12381 = null; async function buildBls12381(singleThread, plugins) { if ((!singleThread) && (globalThis.curve_bls12381)) return globalThis.curve_bls12381; - const moduleBuilder = new wasmbuilder.ModuleBuilder(); + const { ModuleBuilder } = await import('wasmbuilder'); + const { buildBls12381: buildBls12381wasm } = await import('wasmcurves'); + + const moduleBuilder = new ModuleBuilder(); moduleBuilder.setMemory(25); - wasmcurves.buildBls12381(moduleBuilder); + buildBls12381wasm(moduleBuilder); if (plugins) plugins(moduleBuilder); diff --git a/package-lock.json b/package-lock.json index 5880f3d..977eaa4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "GPL-3.0", "dependencies": { "wasmbuilder": "0.0.16", - "wasmcurves": "0.2.2", + "wasmcurves": "file:../wasmcurves", "web-worker": "1.5.0" }, "devDependencies": { @@ -23,6 +23,17 @@ "rollup": "^3.29.4" } }, + "../wasmcurves": { + "version": "0.2.2", + "license": "GPL-3.0", + "dependencies": { + "wasmbuilder": "0.0.16" + }, + "devDependencies": { + "eslint": "^8.17.0", + "mocha": "^10.0.0" + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -329,6 +340,7 @@ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -740,6 +752,7 @@ "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", @@ -1734,6 +1747,7 @@ "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -1906,12 +1920,8 @@ "integrity": "sha512-Qx3lEFqaVvp1cEYW7Bfi+ebRJrOiwz2Ieu7ZG2l7YyeSJIok/reEQCQCuicj/Y32ITIJuGIM9xZQppGx5LrQdA==" }, "node_modules/wasmcurves": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/wasmcurves/-/wasmcurves-0.2.2.tgz", - "integrity": "sha512-JRY908NkmKjFl4ytnTu5ED6AwPD+8VJ9oc94kdq7h5bIwbj0L4TDJ69mG+2aLs2SoCmGfqIesMWTEJjtYsoQXQ==", - "dependencies": { - "wasmbuilder": "0.0.16" - } + "resolved": "../wasmcurves", + "link": true }, "node_modules/web-worker": { "version": "1.5.0", diff --git a/package.json b/package.json index a3ad475..358c4e9 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "homepage": "https://github.com/iden3/ffjavascript", "dependencies": { "wasmbuilder": "0.0.16", - "wasmcurves": "0.2.2", + "wasmcurves": "file:../wasmcurves", "web-worker": "1.5.0" }, "devDependencies": { diff --git a/rollup.browser.esm.config.js b/rollup.browser.esm.config.js index 104a1e1..bc07d5d 100644 --- a/rollup.browser.esm.config.js +++ b/rollup.browser.esm.config.js @@ -10,6 +10,7 @@ export default [ { format: "es", file: "build/browser.esm.js", + inlineDynamicImports: true, }, ], plugins: [ diff --git a/src/bls12381.js b/src/bls12381.js index ce012b8..737f6a2 100644 --- a/src/bls12381.js +++ b/src/bls12381.js @@ -1,13 +1,14 @@ -import { buildBls12381 as buildBls12381wasm } from "wasmcurves"; import buildEngine from "./engine.js"; import * as Scalar from "./scalar.js"; -import { ModuleBuilder } from "wasmbuilder"; globalThis.curve_bls12381 = null; export default async function buildBls12381(singleThread, plugins) { if ((!singleThread) && (globalThis.curve_bls12381)) return globalThis.curve_bls12381; + const { ModuleBuilder } = await import("wasmbuilder"); + const { buildBls12381: buildBls12381wasm } = await import("wasmcurves"); + const moduleBuilder = new ModuleBuilder(); moduleBuilder.setMemory(25); buildBls12381wasm(moduleBuilder); diff --git a/src/bn128.js b/src/bn128.js index 7a421c1..371d286 100644 --- a/src/bn128.js +++ b/src/bn128.js @@ -1,37 +1,78 @@ -import { buildBn128 as buildBn128wasm } from "wasmcurves"; +//import { bn128_wasm_gzip as bn128wasmPrebuilt } from "wasmcurves"; import buildEngine from "./engine.js"; import * as Scalar from "./scalar.js"; -import { ModuleBuilder } from "wasmbuilder"; globalThis.curve_bn128 = null; export default async function buildBn128(singleThread, plugins) { if ((!singleThread) && (globalThis.curve_bn128)) return globalThis.curve_bn128; - const moduleBuilder = new ModuleBuilder(); - moduleBuilder.setMemory(25); - buildBn128wasm(moduleBuilder); - - if (plugins) plugins(moduleBuilder); - - const bn128wasm = {}; - - bn128wasm.code = moduleBuilder.build(); - bn128wasm.pq = moduleBuilder.modules.f1m.pq; - bn128wasm.pr = moduleBuilder.modules.frm.pq; - bn128wasm.pG1gen = moduleBuilder.modules.bn128.pG1gen; - bn128wasm.pG1zero = moduleBuilder.modules.bn128.pG1zero; - bn128wasm.pG1b = moduleBuilder.modules.bn128.pG1b; - bn128wasm.pG2gen = moduleBuilder.modules.bn128.pG2gen; - bn128wasm.pG2zero = moduleBuilder.modules.bn128.pG2zero; - bn128wasm.pG2b = moduleBuilder.modules.bn128.pG2b; - bn128wasm.pOneT = moduleBuilder.modules.bn128.pOneT; - bn128wasm.prePSize = moduleBuilder.modules.bn128.prePSize; - bn128wasm.preQSize = moduleBuilder.modules.bn128.preQSize; - bn128wasm.n8q = 32; - bn128wasm.n8r = 32; - bn128wasm.q = moduleBuilder.modules.bn128.q; - bn128wasm.r = moduleBuilder.modules.bn128.r; + let bn128wasm = {}; + + if (!plugins) { + + console.log("Using prebuilt bn128 wasm"); + + //import { bn128_wasm_gzip as bn128wasmPrebuilt } from "wasmcurves"; + //const { bn128_wasm_gzip: bn128wasmPrebuilt } = await import("wasmcurves"); + const { default: bn128wasmPrebuilt } = await import("wasmcurves/build/bn128_wasm_gzip.js"); + + //console.log(bn128wasmPrebuilt); + bn128wasm.pq = bn128wasmPrebuilt.pq; + bn128wasm.pr = bn128wasmPrebuilt.pq; + bn128wasm.pG1gen = bn128wasmPrebuilt.pG1gen; + bn128wasm.pG1zero = bn128wasmPrebuilt.pG1zero; + bn128wasm.pG1b = bn128wasmPrebuilt.pG1b; + bn128wasm.pG2gen = bn128wasmPrebuilt.pG2gen; + bn128wasm.pG2zero = bn128wasmPrebuilt.pG2zero; + bn128wasm.pG2b = bn128wasmPrebuilt.pG2b; + bn128wasm.pOneT = bn128wasmPrebuilt.pOneT; + bn128wasm.prePSize = bn128wasmPrebuilt.prePSize; + bn128wasm.preQSize = bn128wasmPrebuilt.preQSize; + bn128wasm.n8q = 32; + bn128wasm.n8r = 32; + bn128wasm.q = bn128wasmPrebuilt.q; + bn128wasm.r = bn128wasmPrebuilt.r; + + const compressedCode = new Uint8Array(Buffer.from(bn128wasmPrebuilt.gzipCode, "base64")); + const blob = new Blob([compressedCode]); + + const ds = new DecompressionStream("gzip"); + const decompressedStream = blob.stream().pipeThrough(ds); + + bn128wasm.code = await new Response(decompressedStream).bytes(); + } else { + + //import { ModuleBuilder } from "wasmbuilder"; + //import { buildBn128 as buildBn128wasm } from "wasmcurves"; + const { ModuleBuilder } = await import("wasmbuilder"); + const { buildBn128: buildBn128wasm } = await import("wasmcurves"); + + const moduleBuilder = new ModuleBuilder(); + moduleBuilder.setMemory(25); + buildBn128wasm(moduleBuilder); + + if (plugins) plugins(moduleBuilder); + + bn128wasm.code = moduleBuilder.build(); + bn128wasm.pq = moduleBuilder.modules.f1m.pq; + bn128wasm.pr = moduleBuilder.modules.frm.pq; + bn128wasm.pG1gen = moduleBuilder.modules.bn128.pG1gen; + bn128wasm.pG1zero = moduleBuilder.modules.bn128.pG1zero; + bn128wasm.pG1b = moduleBuilder.modules.bn128.pG1b; + bn128wasm.pG2gen = moduleBuilder.modules.bn128.pG2gen; + bn128wasm.pG2zero = moduleBuilder.modules.bn128.pG2zero; + bn128wasm.pG2b = moduleBuilder.modules.bn128.pG2b; + bn128wasm.pOneT = moduleBuilder.modules.bn128.pOneT; + bn128wasm.prePSize = moduleBuilder.modules.bn128.prePSize; + bn128wasm.preQSize = moduleBuilder.modules.bn128.preQSize; + bn128wasm.n8q = 32; + bn128wasm.n8r = 32; + bn128wasm.q = moduleBuilder.modules.bn128.q; + bn128wasm.r = moduleBuilder.modules.bn128.r; + } + + //console.log("bn128wasm:", bn128wasm); const params = { name: "bn128", diff --git a/src/engine_fft.js b/src/engine_fft.js index 2dae88d..2a862df 100644 --- a/src/engine_fft.js +++ b/src/engine_fft.js @@ -12,7 +12,7 @@ export default function buildFFT(curve, groupName) { outType = outType || "affine"; const MAX_BITS_THREAD = 14; - let sIn, sMid, sOut, fnIn2Mid, fnMid2Out, fnFFTMix, fnFFTJoin, fnFFTFinal; + let sIn, sMid, sOut, fnIn2Mid, fnMid2Out, fnFFTMix, fnFFTJoin, fnFFTFinal, fnReversePermutation; if (groupName == "G1") { if (inType == "affine") { sIn = G.F.n8*2; @@ -26,6 +26,7 @@ export default function buildFFT(curve, groupName) { } fnFFTJoin = "g1m_fftJoin"; fnFFTMix = "g1m_fftMix"; + fnReversePermutation = "g1m__reversePermutation"; if (outType == "affine") { sOut = G.F.n8*2; @@ -47,6 +48,7 @@ export default function buildFFT(curve, groupName) { } fnFFTJoin = "g2m_fftJoin"; fnFFTMix = "g2m_fftMix"; + fnReversePermutation = "g2m__reversePermutation"; if (outType == "affine") { sOut = G.F.n8*2; fnMid2Out = "g2m_batchToAffine"; @@ -62,6 +64,7 @@ export default function buildFFT(curve, groupName) { } fnFFTMix = "frm_fftMix"; fnFFTJoin = "frm_fftJoin"; + fnReversePermutation = "frm__reversePermutation"; } @@ -73,9 +76,13 @@ export default function buildFFT(curve, groupName) { buff = buff.slice(0, buff.byteLength); } + console.log("FFT input size:", buff.byteLength, " bytes"); + const nPoints = buff.byteLength / sIn; const bits = log2(nPoints); + console.log("FFT points:", nPoints, " bits:", bits); + if ((1 << bits) != nPoints) { throw new Error("fft must be multiple of 2" ); } @@ -103,7 +110,26 @@ export default function buildFFT(curve, groupName) { let buffOut; - buffReverseBits(buff, sIn); + // TODO: optimize. Move to wasm + // buffReverseBits(buff, sIn); + + console.log("fnReversePermutation:", fnReversePermutation); + + // TODO: try to do reversing for each batch separately and inside of the task + const task = []; + task.push({cmd: "ALLOCSET", var: 0, buff: buff}); + task.push({cmd: "CALL", fnName: fnReversePermutation, params: [{var:0}, {val: bits}]}); + task.push({cmd: "GET", out:0, var: 0, len: nPoints*sOut}); + const reversedBuff = await tm.queueAction(task, [buff.buffer]); + //const reversedBuff = await tm.queueAction(task, []); + + //console.log("wasm buffReverseBits:", reversedBuff[0]); + //buffReverseBits(buff, sIn); + //console.log("js buffReverseBits:", buff); + + //exit(1); + + buff = reversedBuff[0]; let chunks; let pointsInChunk = Math.min(1 << MAX_BITS_THREAD, nPoints); @@ -117,8 +143,8 @@ export default function buildFFT(curve, groupName) { const l2Chunk = log2(pointsInChunk); const promises = []; + if (logger) logger.debug(`${loggerTxt}: fft ${bits} mix start: ${nChunks}`); for (let i = 0; i< nChunks; i++) { - if (logger) logger.debug(`${loggerTxt}: fft ${bits} mix start: ${i}/${nChunks}`); const task = []; task.push({cmd: "ALLOC", var: 0, len: sMid*pointsInChunk}); const buffChunk = buff.slice( (pointsInChunk * i)*sIn, (pointsInChunk * (i+1))*sIn); @@ -146,17 +172,15 @@ export default function buildFFT(curve, groupName) { } else { task.push({cmd: "GET", out:0, var: 0, len: sMid*pointsInChunk}); } - promises.push(tm.queueAction(task, [buffChunk.buffer]).then( (r) => { - if (logger) logger.debug(`${loggerTxt}: fft ${bits} mix end: ${i}/${nChunks}`); - return r; - })); + promises.push(tm.queueAction(task, [buffChunk.buffer])); } chunks = await Promise.all(promises); + if (logger) logger.debug(`${loggerTxt}: fft ${bits} mix end: ${nChunks}`); for (let i = 0; i< nChunks; i++) chunks[i] = chunks[i][0]; for (let i = l2Chunk+1; i<=bits; i++) { - if (logger) logger.debug(`${loggerTxt}: fft ${bits} join: ${i}/${bits}`); + if (logger) logger.debug(`${loggerTxt}: fft ${bits} join: ${i}/${bits}`); const nGroups = 1 << (bits - i); const nChunksPerGroup = nChunks / nGroups; const opPromises = []; @@ -203,10 +227,7 @@ export default function buildFFT(curve, groupName) { task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sMid}); task.push({cmd: "GET", out: 1, var: 1, len: pointsInChunk*sMid}); } - opPromises.push(tm.queueAction(task, [chunks[o1].buffer, chunks[o2].buffer, first.buffer ]).then( (r) => { - if (logger) logger.debug(`${loggerTxt}: fft ${bits} join ${i}/${bits} ${j+1}/${nGroups} ${k}/${nChunksPerGroup/2}`); - return r; - })); + opPromises.push(tm.queueAction(task, [chunks[o1].buffer, chunks[o2].buffer, first.buffer ])); } } diff --git a/src/engine_multiexp.js b/src/engine_multiexp.js index 5bd27b4..96d52f4 100644 --- a/src/engine_multiexp.js +++ b/src/engine_multiexp.js @@ -88,7 +88,7 @@ export default function buildMultiexp(curve, groupName) { async function _multiExp(buffBases, buffScalars, inType, logger, logText) { const MAX_CHUNK_SIZE = 1 << 22; - const MIN_CHUNK_SIZE = 1 << 10; + const MIN_CHUNK_SIZE = 1 << 12; let sGIn; if (groupName === "G1") { @@ -127,6 +127,8 @@ export default function buildMultiexp(curve, groupName) { let chunkSize; //chunkSize = Math.floor(nPoints / tm.concurrency) + 1; + console.log("nChunks_0", nChunks); + // make nChunks multiple of tm.concurrency for optimal load balancing nChunks = (Math.floor((nChunks-1) / tm.concurrency) + 1) * tm.concurrency; chunkSize = Math.floor(nPoints / nChunks) + 1; @@ -134,6 +136,9 @@ export default function buildMultiexp(curve, groupName) { if (chunkSize>MAX_CHUNK_SIZE) chunkSize = MAX_CHUNK_SIZE; if (chunkSize64) concurrency=64; tm.concurrency = concurrency; - for (let i = 0; i { + this.initialized[i] = true; + }); } startSyncOp() { @@ -183,43 +278,65 @@ export class ThreadManager { this.oldPFree = 0; } - postAction(workerId, e, transfers, _deferred) { + async postAction(workerId, e, transfers, _deferred) { if (this.working[workerId]) { throw new Error("Posting a job to a working worker"); } this.working[workerId] = true; this.pendingDeferreds[workerId] = _deferred ? _deferred : new Deferred(); - this.workers[workerId].postMessage(e, transfers); + await this.workers[workerId].postMessage(e, transfers); return this.pendingDeferreds[workerId].promise; } - processWorks() { - if (this.workers.length === 0 && this.actionQueue.length > 0) { - throw new Error("No workers initialized"); - } - for (let i=0; (i 0); i++) { - if (this.working[i] === false) { + async processWorks() { + + //console.log("this.actionQueue.length:", this.actionQueue.length); + + for (let i=0; (i 0); i++) { + if (this.workers[i] && this.initialized[i] && !this.working[i]) { const work = this.actionQueue.shift(); - this.postAction(i, work.data, work.transfers, work.deferred); + await this.postAction(i, work.data, work.transfers, work.deferred); + } + } + + // Initialize more workers if needed + if (this.actionQueue.length > 0) { + // Find a worker that is not initialized yet + let initializingCount = 0; + for (let i=0; i= this.actionQueue.length) break; + + // Initialize this worker + console.log(`Worker ${i} not initialized yet. Initializing...`); + initializingCount++; + await this.startWorker(i); + //this.startWorker(i); } } } - queueAction(actionData, transfers) { + async queueAction(actionData, transfers) { const d = new Deferred(); if (this.singleThread) { const res = this.taskManager(actionData); d.resolve(res); } else { + // Wait if queue is too large + // while (this.actionQueue.length >= this.concurrency * 2) { + // await sleep(10); + // } this.actionQueue.push({ data: actionData, transfers: transfers, deferred: d }); - this.processWorks(); + await this.processWorks(); } return d.promise; } @@ -250,10 +367,12 @@ export class ThreadManager { } async terminate() { + //console.log("terminate!!!"); for (let i=0; iMAXMEM) requiredPages=MAXMEM; memory.grow(requiredPages-currentPages); + console.log("Growing memory to", memory.buffer.byteLength / 1024 / 1024, "MB"); } return res; } @@ -67,8 +119,9 @@ export default function thread(self) { } function getBuffer(pointer, length) { - const u8 = new Uint8Array(memory.buffer); - return new Uint8Array(u8.buffer, u8.byteOffset + pointer, length); + // const u8 = new Uint8Array(memory.buffer); + // return new Uint8Array(u8.buffer, u8.byteOffset + pointer, length); + return new Uint8Array(memory.buffer, pointer, length); } function setBuffer(pointer, buffer) { @@ -77,6 +130,7 @@ export default function thread(self) { } function runTask(task) { + clearTimeout(terminationTimer); if (task[0].cmd === "INIT") { return init(task[0]); } @@ -89,9 +143,17 @@ export default function thread(self) { for (let i=0; i 25) { + console.log("tasks", task); + console.trace(); + } ctx.vars[task[i].var] = allocBuffer(task[i].buff); break; case "ALLOC": + if (task[i].len / 1024 / 1024 > 25) { + console.log("tasks", task); + console.trace(); + } ctx.vars[task[i].var] = alloc(task[i].len); break; case "SET": @@ -119,9 +181,32 @@ export default function thread(self) { } const u32b = new Uint32Array(memory.buffer, 0, 1); u32b[0] = oldAlloc; + + //console.log(ctx.out); + return ctx.out; } + function scheduleTermination() { + clearTimeout(terminationTimer); + if (terminationTimeout>0) { + terminationTimer = setTimeout( () => { + console.log("Shutting down thread due to inactivity"); + terminate(); + }, terminationTimeout); + } + } + + function terminate() { + clearTimeout(terminationTimer); + //instance = null; + //memory = null; + if (self) { + console.log("TERMINATE"); + self.postMessage({status: "terminated"}); + self.close(); + } + } return runTask; }