UNPKG

@kotevode/ffjavascript

Version:

Finite Field Library in Javascript

156 lines (136 loc) 5.64 kB
import { log2 } from "./utils.js"; 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 ]; 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`); 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"; let sGIn; let fnName; 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") { fnName = "g2m_multiexpAffine_chunk"; sGIn = G.F.n8*2; } else { fnName = "g2m_multiexp_chunk"; sGIn = G.F.n8*3; } } else { throw new Error("Invalid group"); } const nPoints = Math.floor(buffBases.byteLength / sGIn); 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"); } const bitChunkSize = pTSizes[log2(nPoints)]; const nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1; const opPromises = []; for (let i=0; i<nChunks; i++) { 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}, {var: 1}, {val: sScalar}, {val: nPoints}, {val: i*bitChunkSize}, {val: Math.min(sScalar*8 - i*bitChunkSize, bitChunkSize)}, {var: 2} ]}, {cmd: "GET", out: 0, var: 2, len: G.F.n8*3} ]; opPromises.push( G.tm.queueAction(task) ); } 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; j<bitChunkSize; j++) res = G.double(res); } res = G.add(res, result[i][0]); } return res; } async function _multiExp(buffBases, buffScalars, inType, logger, logText) { const MAX_CHUNK_SIZE = 1 << 22; const MIN_CHUNK_SIZE = 1 << 10; let sGIn; if (groupName == "G1") { if (inType == "affine") { sGIn = G.F.n8*2; } else { sGIn = G.F.n8*3; } } else if (groupName == "G2") { if (inType == "affine") { sGIn = G.F.n8*2; } else { sGIn = G.F.n8*3; } } else { throw new Error("Invalid group"); } const nPoints = Math.floor(buffBases.byteLength / sGIn); 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"); } 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<MIN_CHUNK_SIZE) chunkSize = MIN_CHUNK_SIZE; const opPromises = []; for (let i=0; i<nPoints; i += chunkSize) { if (logger) logger.debug(`Multiexp start: ${logText}: ${i}/${nPoints}`); const n= Math.min(nPoints - i, chunkSize); const buffBasesChunk = buffBases.slice(i*sGIn, (i+n)*sGIn); const buffScalarsChunk = buffScalars.slice(i*sScalar, (i+n)*sScalar); 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 res = G.zero; for (let i=result.length-1; i>=0; i--) { res = G.add(res, result[i]); } 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); }; }