19829 lines
633 KiB
JavaScript
Vendored
19829 lines
633 KiB
JavaScript
Vendored
'use strict';
|
|
|
|
var threads = require('worker_threads');
|
|
var crypto = require('crypto');
|
|
var os = require('os');
|
|
var URL = require('url');
|
|
var VM = require('vm');
|
|
|
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
|
|
function getDefaultExportFromCjs (x) {
|
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
}
|
|
|
|
var lib = {};
|
|
|
|
var FixedMerkleTree = {};
|
|
|
|
var simpleHash$1 = {};
|
|
|
|
Object.defineProperty(simpleHash$1, "__esModule", { value: true });
|
|
simpleHash$1.simpleHash = void 0;
|
|
/***
|
|
* This is insecure hash function, just for example only
|
|
* @param data
|
|
* @param seed
|
|
* @param hashLength
|
|
*/
|
|
function simpleHash(data, seed, hashLength = 40) {
|
|
const str = data.join('');
|
|
let i, l, hval = seed !== null && seed !== void 0 ? seed : 0x811c9dcc5;
|
|
for (i = 0, l = str.length; i < l; i++) {
|
|
hval ^= str.charCodeAt(i);
|
|
hval += (hval << 1) + (hval << 4) + (hval << 6) + (hval << 8) + (hval << 24);
|
|
}
|
|
const hash = (hval >>> 0).toString(16);
|
|
return BigInt('0x' + hash.padEnd(hashLength - (hash.length - 1), '0')).toString(10);
|
|
}
|
|
simpleHash$1.simpleHash = simpleHash;
|
|
simpleHash$1.default = (left, right) => simpleHash([left, right]);
|
|
|
|
var BaseTree$1 = {};
|
|
|
|
Object.defineProperty(BaseTree$1, "__esModule", { value: true });
|
|
BaseTree$1.BaseTree = void 0;
|
|
class BaseTree {
|
|
get capacity() {
|
|
return 2 ** this.levels;
|
|
}
|
|
get layers() {
|
|
return this._layers.slice();
|
|
}
|
|
get zeros() {
|
|
return this._zeros.slice();
|
|
}
|
|
get elements() {
|
|
return this._layers[0].slice();
|
|
}
|
|
get root() {
|
|
var _a;
|
|
return (_a = this._layers[this.levels][0]) !== null && _a !== void 0 ? _a : this._zeros[this.levels];
|
|
}
|
|
/**
|
|
* Find an element in the tree
|
|
* @param elements elements of tree
|
|
* @param element An element to find
|
|
* @param comparator A function that checks leaf value equality
|
|
* @param fromIndex The index to start the search at. If the index is greater than or equal to the array's length, -1 is returned
|
|
* @returns {number} Index if element is found, otherwise -1
|
|
*/
|
|
static indexOf(elements, element, fromIndex, comparator) {
|
|
if (comparator) {
|
|
return elements.findIndex((el) => comparator(element, el));
|
|
}
|
|
else {
|
|
return elements.indexOf(element, fromIndex);
|
|
}
|
|
}
|
|
/**
|
|
* Insert new element into the tree
|
|
* @param element Element to insert
|
|
*/
|
|
insert(element) {
|
|
if (this._layers[0].length >= this.capacity) {
|
|
throw new Error('Tree is full');
|
|
}
|
|
this.update(this._layers[0].length, element);
|
|
}
|
|
/*
|
|
* Insert multiple elements into the tree.
|
|
* @param {Array} elements Elements to insert
|
|
*/
|
|
bulkInsert(elements) {
|
|
if (!elements.length) {
|
|
return;
|
|
}
|
|
if (this._layers[0].length + elements.length > this.capacity) {
|
|
throw new Error('Tree is full');
|
|
}
|
|
// First we insert all elements except the last one
|
|
// updating only full subtree hashes (all layers where inserted element has odd index)
|
|
// the last element will update the full path to the root making the tree consistent again
|
|
for (let i = 0; i < elements.length - 1; i++) {
|
|
this._layers[0].push(elements[i]);
|
|
let level = 0;
|
|
let index = this._layers[0].length - 1;
|
|
while (index % 2 === 1) {
|
|
level++;
|
|
index >>= 1;
|
|
const left = this._layers[level - 1][index * 2];
|
|
const right = this._layers[level - 1][index * 2 + 1];
|
|
this._layers[level][index] = this._hashFn(left, right);
|
|
}
|
|
}
|
|
this.insert(elements[elements.length - 1]);
|
|
}
|
|
/**
|
|
* Change an element in the tree
|
|
* @param {number} index Index of element to change
|
|
* @param element Updated element value
|
|
*/
|
|
update(index, element) {
|
|
if (isNaN(Number(index)) || index < 0 || index > this._layers[0].length || index >= this.capacity) {
|
|
throw new Error('Insert index out of bounds: ' + index);
|
|
}
|
|
this._layers[0][index] = element;
|
|
this._processUpdate(index);
|
|
}
|
|
/**
|
|
* Get merkle path to a leaf
|
|
* @param {number} index Leaf index to generate path for
|
|
* @returns {{pathElements: Object[], pathIndex: number[]}} An object containing adjacent elements and left-right index
|
|
*/
|
|
path(index) {
|
|
if (isNaN(Number(index)) || index < 0 || index >= this._layers[0].length) {
|
|
throw new Error('Index out of bounds: ' + index);
|
|
}
|
|
let elIndex = +index;
|
|
const pathElements = [];
|
|
const pathIndices = [];
|
|
const pathPositions = [];
|
|
for (let level = 0; level < this.levels; level++) {
|
|
pathIndices[level] = elIndex % 2;
|
|
const leafIndex = elIndex ^ 1;
|
|
if (leafIndex < this._layers[level].length) {
|
|
pathElements[level] = this._layers[level][leafIndex];
|
|
pathPositions[level] = leafIndex;
|
|
}
|
|
else {
|
|
pathElements[level] = this._zeros[level];
|
|
pathPositions[level] = 0;
|
|
}
|
|
elIndex >>= 1;
|
|
}
|
|
return {
|
|
pathElements,
|
|
pathIndices,
|
|
pathPositions,
|
|
pathRoot: this.root,
|
|
};
|
|
}
|
|
_buildZeros() {
|
|
this._zeros = [this.zeroElement];
|
|
for (let i = 1; i <= this.levels; i++) {
|
|
this._zeros[i] = this._hashFn(this._zeros[i - 1], this._zeros[i - 1]);
|
|
}
|
|
}
|
|
_processNodes(nodes, layerIndex) {
|
|
const length = nodes.length;
|
|
let currentLength = Math.ceil(length / 2);
|
|
const currentLayer = new Array(currentLength);
|
|
currentLength--;
|
|
const starFrom = length - ((length % 2) ^ 1);
|
|
let j = 0;
|
|
for (let i = starFrom; i >= 0; i -= 2) {
|
|
if (nodes[i - 1] === undefined)
|
|
break;
|
|
const left = nodes[i - 1];
|
|
const right = (i === starFrom && length % 2 === 1) ? this._zeros[layerIndex - 1] : nodes[i];
|
|
currentLayer[currentLength - j] = this._hashFn(left, right);
|
|
j++;
|
|
}
|
|
return currentLayer;
|
|
}
|
|
_processUpdate(index) {
|
|
for (let level = 1; level <= this.levels; level++) {
|
|
index >>= 1;
|
|
const left = this._layers[level - 1][index * 2];
|
|
const right = index * 2 + 1 < this._layers[level - 1].length
|
|
? this._layers[level - 1][index * 2 + 1]
|
|
: this._zeros[level - 1];
|
|
this._layers[level][index] = this._hashFn(left, right);
|
|
}
|
|
}
|
|
}
|
|
BaseTree$1.BaseTree = BaseTree;
|
|
|
|
var __importDefault$1 = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(FixedMerkleTree, "__esModule", { value: true });
|
|
const simpleHash_1$1 = __importDefault$1(simpleHash$1);
|
|
const BaseTree_1$1 = BaseTree$1;
|
|
class MerkleTree extends BaseTree_1$1.BaseTree {
|
|
constructor(levels, elements = [], { hashFunction = simpleHash_1$1.default, zeroElement = 0, } = {}) {
|
|
super();
|
|
this.levels = levels;
|
|
if (elements.length > this.capacity) {
|
|
throw new Error('Tree is full');
|
|
}
|
|
this._hashFn = hashFunction;
|
|
this.zeroElement = zeroElement;
|
|
this._layers = [];
|
|
const leaves = elements.slice();
|
|
this._layers = [leaves];
|
|
this._buildZeros();
|
|
this._buildHashes();
|
|
}
|
|
_buildHashes() {
|
|
for (let layerIndex = 1; layerIndex <= this.levels; layerIndex++) {
|
|
const nodes = this._layers[layerIndex - 1];
|
|
this._layers[layerIndex] = this._processNodes(nodes, layerIndex);
|
|
}
|
|
}
|
|
/**
|
|
* Insert multiple elements into the tree.
|
|
* @param {Array} elements Elements to insert
|
|
*/
|
|
bulkInsert(elements) {
|
|
if (!elements.length) {
|
|
return;
|
|
}
|
|
if (this._layers[0].length + elements.length > this.capacity) {
|
|
throw new Error('Tree is full');
|
|
}
|
|
// First we insert all elements except the last one
|
|
// updating only full subtree hashes (all layers where inserted element has odd index)
|
|
// the last element will update the full path to the root making the tree consistent again
|
|
for (let i = 0; i < elements.length - 1; i++) {
|
|
this._layers[0].push(elements[i]);
|
|
let level = 0;
|
|
let index = this._layers[0].length - 1;
|
|
while (index % 2 === 1) {
|
|
level++;
|
|
index >>= 1;
|
|
this._layers[level][index] = this._hashFn(this._layers[level - 1][index * 2], this._layers[level - 1][index * 2 + 1]);
|
|
}
|
|
}
|
|
this.insert(elements[elements.length - 1]);
|
|
}
|
|
indexOf(element, comparator) {
|
|
return BaseTree_1$1.BaseTree.indexOf(this._layers[0], element, 0, comparator);
|
|
}
|
|
proof(element) {
|
|
const index = this.indexOf(element);
|
|
return this.path(index);
|
|
}
|
|
getTreeEdge(edgeIndex) {
|
|
const edgeElement = this._layers[0][edgeIndex];
|
|
if (edgeElement === undefined) {
|
|
throw new Error('Element not found');
|
|
}
|
|
const edgePath = this.path(edgeIndex);
|
|
return { edgePath, edgeElement, edgeIndex, edgeElementsCount: this._layers[0].length };
|
|
}
|
|
/**
|
|
* 🪓
|
|
* @param count
|
|
*/
|
|
getTreeSlices(count = 4) {
|
|
const length = this._layers[0].length;
|
|
let size = Math.ceil(length / count);
|
|
if (size % 2)
|
|
size++;
|
|
const slices = [];
|
|
for (let i = 0; i < length; i += size) {
|
|
const edgeLeft = i;
|
|
const edgeRight = i + size;
|
|
slices.push({ edge: this.getTreeEdge(edgeLeft), elements: this.elements.slice(edgeLeft, edgeRight) });
|
|
}
|
|
return slices;
|
|
}
|
|
/**
|
|
* Serialize entire tree state including intermediate layers into a plain object
|
|
* Deserializing it back will not require to recompute any hashes
|
|
* Elements are not converted to a plain type, this is responsibility of the caller
|
|
*/
|
|
serialize() {
|
|
return {
|
|
levels: this.levels,
|
|
_zeros: this._zeros,
|
|
_layers: this._layers,
|
|
};
|
|
}
|
|
/**
|
|
* Deserialize data into a MerkleTree instance
|
|
* Make sure to provide the same hashFunction as was used in the source tree,
|
|
* otherwise the tree state will be invalid
|
|
*/
|
|
static deserialize(data, hashFunction) {
|
|
const instance = Object.assign(Object.create(this.prototype), data);
|
|
instance._hashFn = hashFunction || simpleHash_1$1.default;
|
|
instance.zeroElement = instance._zeros[0];
|
|
return instance;
|
|
}
|
|
toString() {
|
|
return JSON.stringify(this.serialize());
|
|
}
|
|
}
|
|
FixedMerkleTree.default = MerkleTree;
|
|
|
|
var PartialMerkleTree$1 = {};
|
|
|
|
var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(PartialMerkleTree$1, "__esModule", { value: true });
|
|
PartialMerkleTree$1.PartialMerkleTree = void 0;
|
|
const simpleHash_1 = __importDefault(simpleHash$1);
|
|
const BaseTree_1 = BaseTree$1;
|
|
class PartialMerkleTree extends BaseTree_1.BaseTree {
|
|
constructor(levels, { edgePath, edgeElement, edgeIndex, edgeElementsCount, }, leaves, { hashFunction, zeroElement } = {}) {
|
|
super();
|
|
if (edgeIndex + leaves.length !== edgeElementsCount)
|
|
throw new Error('Invalid number of elements');
|
|
this._edgeLeafProof = edgePath;
|
|
this._initialRoot = edgePath.pathRoot;
|
|
this.zeroElement = zeroElement !== null && zeroElement !== void 0 ? zeroElement : 0;
|
|
this._edgeLeaf = { data: edgeElement, index: edgeIndex };
|
|
this._leavesAfterEdge = leaves;
|
|
this.levels = levels;
|
|
this._hashFn = hashFunction || simpleHash_1.default;
|
|
this._createProofMap();
|
|
this._buildTree();
|
|
}
|
|
get edgeIndex() {
|
|
return this._edgeLeaf.index;
|
|
}
|
|
get edgeElement() {
|
|
return this._edgeLeaf.data;
|
|
}
|
|
get edgeLeafProof() {
|
|
return this._edgeLeafProof;
|
|
}
|
|
_createProofMap() {
|
|
this._proofMap = this.edgeLeafProof.pathPositions.reduce((p, c, i) => {
|
|
p.set(i, [c, this.edgeLeafProof.pathElements[i]]);
|
|
return p;
|
|
}, new Map());
|
|
this._proofMap.set(this.levels, [0, this.edgeLeafProof.pathRoot]);
|
|
}
|
|
_buildTree() {
|
|
const edgeLeafIndex = this._edgeLeaf.index;
|
|
this._leaves = Array(edgeLeafIndex).concat(this._leavesAfterEdge);
|
|
if (this._proofMap.has(0)) {
|
|
const [proofPos, proofEl] = this._proofMap.get(0);
|
|
this._leaves[proofPos] = proofEl;
|
|
}
|
|
this._layers = [this._leaves];
|
|
this._buildZeros();
|
|
this._buildHashes();
|
|
}
|
|
_buildHashes() {
|
|
for (let layerIndex = 1; layerIndex <= this.levels; layerIndex++) {
|
|
const nodes = this._layers[layerIndex - 1];
|
|
const currentLayer = this._processNodes(nodes, layerIndex);
|
|
if (this._proofMap.has(layerIndex)) {
|
|
const [proofPos, proofEl] = this._proofMap.get(layerIndex);
|
|
if (!currentLayer[proofPos])
|
|
currentLayer[proofPos] = proofEl;
|
|
}
|
|
this._layers[layerIndex] = currentLayer;
|
|
}
|
|
}
|
|
/**
|
|
* Change an element in the tree
|
|
* @param {number} index Index of element to change
|
|
* @param element Updated element value
|
|
*/
|
|
update(index, element) {
|
|
if (isNaN(Number(index)) || index < 0 || index > this._layers[0].length || index >= this.capacity) {
|
|
throw new Error('Insert index out of bounds: ' + index);
|
|
}
|
|
if (index < this._edgeLeaf.index) {
|
|
throw new Error(`Index ${index} is below the edge: ${this._edgeLeaf.index}`);
|
|
}
|
|
this._layers[0][index] = element;
|
|
this._processUpdate(index);
|
|
}
|
|
path(index) {
|
|
var _a;
|
|
if (isNaN(Number(index)) || index < 0 || index >= this._layers[0].length) {
|
|
throw new Error('Index out of bounds: ' + index);
|
|
}
|
|
if (index < this._edgeLeaf.index) {
|
|
throw new Error(`Index ${index} is below the edge: ${this._edgeLeaf.index}`);
|
|
}
|
|
let elIndex = Number(index);
|
|
const pathElements = [];
|
|
const pathIndices = [];
|
|
const pathPositions = [];
|
|
for (let level = 0; level < this.levels; level++) {
|
|
pathIndices[level] = elIndex % 2;
|
|
const leafIndex = elIndex ^ 1;
|
|
if (leafIndex < this._layers[level].length) {
|
|
pathElements[level] = this._layers[level][leafIndex];
|
|
pathPositions[level] = leafIndex;
|
|
}
|
|
else {
|
|
pathElements[level] = this._zeros[level];
|
|
pathPositions[level] = 0;
|
|
}
|
|
const [proofPos, proofEl] = this._proofMap.get(level);
|
|
pathElements[level] = (_a = pathElements[level]) !== null && _a !== void 0 ? _a : (proofPos === leafIndex ? proofEl : this._zeros[level]);
|
|
elIndex >>= 1;
|
|
}
|
|
return {
|
|
pathElements,
|
|
pathIndices,
|
|
pathPositions,
|
|
pathRoot: this.root,
|
|
};
|
|
}
|
|
indexOf(element, comparator) {
|
|
return BaseTree_1.BaseTree.indexOf(this._layers[0], element, this.edgeIndex, comparator);
|
|
}
|
|
proof(element) {
|
|
const index = this.indexOf(element);
|
|
return this.path(index);
|
|
}
|
|
/**
|
|
* Shifts edge of tree to left
|
|
* @param edge new TreeEdge below current edge
|
|
* @param elements leaves between old and new edge
|
|
*/
|
|
shiftEdge(edge, elements) {
|
|
if (this._edgeLeaf.index <= edge.edgeIndex) {
|
|
throw new Error(`New edgeIndex should be smaller then ${this._edgeLeaf.index}`);
|
|
}
|
|
if (elements.length !== (this._edgeLeaf.index - edge.edgeIndex)) {
|
|
throw new Error(`Elements length should be ${this._edgeLeaf.index - edge.edgeIndex}`);
|
|
}
|
|
this._edgeLeafProof = edge.edgePath;
|
|
this._edgeLeaf = { index: edge.edgeIndex, data: edge.edgeElement };
|
|
this._leavesAfterEdge = [...elements, ...this._leavesAfterEdge];
|
|
this._createProofMap();
|
|
this._buildTree();
|
|
}
|
|
serialize() {
|
|
return {
|
|
_edgeLeafProof: this._edgeLeafProof,
|
|
_edgeLeaf: this._edgeLeaf,
|
|
_layers: this._layers,
|
|
_zeros: this._zeros,
|
|
levels: this.levels,
|
|
};
|
|
}
|
|
static deserialize(data, hashFunction) {
|
|
const instance = Object.assign(Object.create(this.prototype), data);
|
|
instance._hashFn = hashFunction || simpleHash_1.default;
|
|
instance._initialRoot = data._edgeLeafProof.pathRoot;
|
|
instance.zeroElement = instance._zeros[0];
|
|
instance._leavesAfterEdge = instance._layers[0].slice(data._edgeLeaf.index);
|
|
instance._createProofMap();
|
|
return instance;
|
|
}
|
|
toString() {
|
|
return JSON.stringify(this.serialize());
|
|
}
|
|
}
|
|
PartialMerkleTree$1.PartialMerkleTree = PartialMerkleTree;
|
|
|
|
(function (exports) {
|
|
var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.MerkleTree = exports.simpleHash = exports.PartialMerkleTree = void 0;
|
|
const FixedMerkleTree_1 = __importDefault(FixedMerkleTree);
|
|
Object.defineProperty(exports, "MerkleTree", { enumerable: true, get: function () { return FixedMerkleTree_1.default; } });
|
|
var PartialMerkleTree_1 = PartialMerkleTree$1;
|
|
Object.defineProperty(exports, "PartialMerkleTree", { enumerable: true, get: function () { return PartialMerkleTree_1.PartialMerkleTree; } });
|
|
var simpleHash_1 = simpleHash$1;
|
|
Object.defineProperty(exports, "simpleHash", { enumerable: true, get: function () { return simpleHash_1.simpleHash; } });
|
|
exports.default = FixedMerkleTree_1.default;
|
|
} (lib));
|
|
|
|
/* global BigInt */
|
|
const hexLen = [ 0, 1, 2, 2, 3, 3, 3, 3, 4 ,4 ,4 ,4 ,4 ,4 ,4 ,4];
|
|
|
|
function fromString$2(s, radix) {
|
|
if ((!radix)||(radix==10)) {
|
|
return BigInt(s);
|
|
} else if (radix==16) {
|
|
if (s.slice(0,2) == "0x") {
|
|
return BigInt(s);
|
|
} else {
|
|
return BigInt("0x"+s);
|
|
}
|
|
}
|
|
}
|
|
|
|
const e$2 = fromString$2;
|
|
|
|
function fromArray$2(a, radix) {
|
|
let acc =BigInt(0);
|
|
radix = BigInt(radix);
|
|
for (let i=0; i<a.length; i++) {
|
|
acc = acc*radix + BigInt(a[i]);
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
function bitLength$2(a) {
|
|
const aS =a.toString(16);
|
|
return (aS.length-1)*4 +hexLen[parseInt(aS[0], 16)];
|
|
}
|
|
|
|
function isNegative$2(a) {
|
|
return BigInt(a) < BigInt(0);
|
|
}
|
|
|
|
function isZero$2(a) {
|
|
return !a;
|
|
}
|
|
|
|
function shiftLeft$2(a, n) {
|
|
return BigInt(a) << BigInt(n);
|
|
}
|
|
|
|
function shiftRight$2(a, n) {
|
|
return BigInt(a) >> BigInt(n);
|
|
}
|
|
|
|
const shl$2 = shiftLeft$2;
|
|
const shr$2 = shiftRight$2;
|
|
|
|
function isOdd$2(a) {
|
|
return (BigInt(a) & BigInt(1)) == BigInt(1);
|
|
}
|
|
|
|
|
|
function naf$2(n) {
|
|
let E = BigInt(n);
|
|
const res = [];
|
|
while (E) {
|
|
if (E & BigInt(1)) {
|
|
const z = 2 - Number(E % BigInt(4));
|
|
res.push( z );
|
|
E = E - BigInt(z);
|
|
} else {
|
|
res.push( 0 );
|
|
}
|
|
E = E >> BigInt(1);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
function bits$2(n) {
|
|
let E = BigInt(n);
|
|
const res = [];
|
|
while (E) {
|
|
if (E & BigInt(1)) {
|
|
res.push(1);
|
|
} else {
|
|
res.push( 0 );
|
|
}
|
|
E = E >> BigInt(1);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function toNumber$3(s) {
|
|
if (s>BigInt(Number.MAX_SAFE_INTEGER )) {
|
|
throw new Error("Number too big");
|
|
}
|
|
return Number(s);
|
|
}
|
|
|
|
function toArray$2(s, radix) {
|
|
const res = [];
|
|
let rem = BigInt(s);
|
|
radix = BigInt(radix);
|
|
while (rem) {
|
|
res.unshift( Number(rem % radix));
|
|
rem = rem / radix;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
function add$2(a, b) {
|
|
return BigInt(a) + BigInt(b);
|
|
}
|
|
|
|
function sub$2(a, b) {
|
|
return BigInt(a) - BigInt(b);
|
|
}
|
|
|
|
function neg$2(a) {
|
|
return -BigInt(a);
|
|
}
|
|
|
|
function mul$2(a, b) {
|
|
return BigInt(a) * BigInt(b);
|
|
}
|
|
|
|
function square$2(a) {
|
|
return BigInt(a) * BigInt(a);
|
|
}
|
|
|
|
function pow$2(a, b) {
|
|
return BigInt(a) ** BigInt(b);
|
|
}
|
|
|
|
function exp$2(a, b) {
|
|
return BigInt(a) ** BigInt(b);
|
|
}
|
|
|
|
function abs$2(a) {
|
|
return BigInt(a) >= 0 ? BigInt(a) : -BigInt(a);
|
|
}
|
|
|
|
function div$2(a, b) {
|
|
return BigInt(a) / BigInt(b);
|
|
}
|
|
|
|
function mod$2(a, b) {
|
|
return BigInt(a) % BigInt(b);
|
|
}
|
|
|
|
function eq$2(a, b) {
|
|
return BigInt(a) == BigInt(b);
|
|
}
|
|
|
|
function neq$2(a, b) {
|
|
return BigInt(a) != BigInt(b);
|
|
}
|
|
|
|
function lt$2(a, b) {
|
|
return BigInt(a) < BigInt(b);
|
|
}
|
|
|
|
function gt$2(a, b) {
|
|
return BigInt(a) > BigInt(b);
|
|
}
|
|
|
|
function leq$2(a, b) {
|
|
return BigInt(a) <= BigInt(b);
|
|
}
|
|
|
|
function geq$2(a, b) {
|
|
return BigInt(a) >= BigInt(b);
|
|
}
|
|
|
|
function band$2(a, b) {
|
|
return BigInt(a) & BigInt(b);
|
|
}
|
|
|
|
function bor$2(a, b) {
|
|
return BigInt(a) | BigInt(b);
|
|
}
|
|
|
|
function bxor$2(a, b) {
|
|
return BigInt(a) ^ BigInt(b);
|
|
}
|
|
|
|
function land$2(a, b) {
|
|
return BigInt(a) && BigInt(b);
|
|
}
|
|
|
|
function lor$2(a, b) {
|
|
return BigInt(a) || BigInt(b);
|
|
}
|
|
|
|
function lnot$2(a) {
|
|
return !BigInt(a);
|
|
}
|
|
|
|
var Scalar_native = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
abs: abs$2,
|
|
add: add$2,
|
|
band: band$2,
|
|
bitLength: bitLength$2,
|
|
bits: bits$2,
|
|
bor: bor$2,
|
|
bxor: bxor$2,
|
|
div: div$2,
|
|
e: e$2,
|
|
eq: eq$2,
|
|
exp: exp$2,
|
|
fromArray: fromArray$2,
|
|
fromString: fromString$2,
|
|
geq: geq$2,
|
|
gt: gt$2,
|
|
isNegative: isNegative$2,
|
|
isOdd: isOdd$2,
|
|
isZero: isZero$2,
|
|
land: land$2,
|
|
leq: leq$2,
|
|
lnot: lnot$2,
|
|
lor: lor$2,
|
|
lt: lt$2,
|
|
mod: mod$2,
|
|
mul: mul$2,
|
|
naf: naf$2,
|
|
neg: neg$2,
|
|
neq: neq$2,
|
|
pow: pow$2,
|
|
shiftLeft: shiftLeft$2,
|
|
shiftRight: shiftRight$2,
|
|
shl: shl$2,
|
|
shr: shr$2,
|
|
square: square$2,
|
|
sub: sub$2,
|
|
toArray: toArray$2,
|
|
toNumber: toNumber$3
|
|
});
|
|
|
|
var BigInteger = {exports: {}};
|
|
|
|
(function (module) {
|
|
var bigInt = (function (undefined$1) {
|
|
|
|
var BASE = 1e7,
|
|
LOG_BASE = 7,
|
|
MAX_INT = 9007199254740992,
|
|
MAX_INT_ARR = smallToArray(MAX_INT),
|
|
DEFAULT_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
|
|
var supportsNativeBigInt = typeof BigInt === "function";
|
|
|
|
function Integer(v, radix, alphabet, caseSensitive) {
|
|
if (typeof v === "undefined") return Integer[0];
|
|
if (typeof radix !== "undefined") return +radix === 10 && !alphabet ? parseValue(v) : parseBase(v, radix, alphabet, caseSensitive);
|
|
return parseValue(v);
|
|
}
|
|
|
|
function BigInteger(value, sign) {
|
|
this.value = value;
|
|
this.sign = sign;
|
|
this.isSmall = false;
|
|
}
|
|
BigInteger.prototype = Object.create(Integer.prototype);
|
|
|
|
function SmallInteger(value) {
|
|
this.value = value;
|
|
this.sign = value < 0;
|
|
this.isSmall = true;
|
|
}
|
|
SmallInteger.prototype = Object.create(Integer.prototype);
|
|
|
|
function NativeBigInt(value) {
|
|
this.value = value;
|
|
}
|
|
NativeBigInt.prototype = Object.create(Integer.prototype);
|
|
|
|
function isPrecise(n) {
|
|
return -MAX_INT < n && n < MAX_INT;
|
|
}
|
|
|
|
function smallToArray(n) { // For performance reasons doesn't reference BASE, need to change this function if BASE changes
|
|
if (n < 1e7)
|
|
return [n];
|
|
if (n < 1e14)
|
|
return [n % 1e7, Math.floor(n / 1e7)];
|
|
return [n % 1e7, Math.floor(n / 1e7) % 1e7, Math.floor(n / 1e14)];
|
|
}
|
|
|
|
function arrayToSmall(arr) { // If BASE changes this function may need to change
|
|
trim(arr);
|
|
var length = arr.length;
|
|
if (length < 4 && compareAbs(arr, MAX_INT_ARR) < 0) {
|
|
switch (length) {
|
|
case 0: return 0;
|
|
case 1: return arr[0];
|
|
case 2: return arr[0] + arr[1] * BASE;
|
|
default: return arr[0] + (arr[1] + arr[2] * BASE) * BASE;
|
|
}
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
function trim(v) {
|
|
var i = v.length;
|
|
while (v[--i] === 0);
|
|
v.length = i + 1;
|
|
}
|
|
|
|
function createArray(length) { // function shamelessly stolen from Yaffle's library https://github.com/Yaffle/BigInteger
|
|
var x = new Array(length);
|
|
var i = -1;
|
|
while (++i < length) {
|
|
x[i] = 0;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
function truncate(n) {
|
|
if (n > 0) return Math.floor(n);
|
|
return Math.ceil(n);
|
|
}
|
|
|
|
function add(a, b) { // assumes a and b are arrays with a.length >= b.length
|
|
var l_a = a.length,
|
|
l_b = b.length,
|
|
r = new Array(l_a),
|
|
carry = 0,
|
|
base = BASE,
|
|
sum, i;
|
|
for (i = 0; i < l_b; i++) {
|
|
sum = a[i] + b[i] + carry;
|
|
carry = sum >= base ? 1 : 0;
|
|
r[i] = sum - carry * base;
|
|
}
|
|
while (i < l_a) {
|
|
sum = a[i] + carry;
|
|
carry = sum === base ? 1 : 0;
|
|
r[i++] = sum - carry * base;
|
|
}
|
|
if (carry > 0) r.push(carry);
|
|
return r;
|
|
}
|
|
|
|
function addAny(a, b) {
|
|
if (a.length >= b.length) return add(a, b);
|
|
return add(b, a);
|
|
}
|
|
|
|
function addSmall(a, carry) { // assumes a is array, carry is number with 0 <= carry < MAX_INT
|
|
var l = a.length,
|
|
r = new Array(l),
|
|
base = BASE,
|
|
sum, i;
|
|
for (i = 0; i < l; i++) {
|
|
sum = a[i] - base + carry;
|
|
carry = Math.floor(sum / base);
|
|
r[i] = sum - carry * base;
|
|
carry += 1;
|
|
}
|
|
while (carry > 0) {
|
|
r[i++] = carry % base;
|
|
carry = Math.floor(carry / base);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
BigInteger.prototype.add = function (v) {
|
|
var n = parseValue(v);
|
|
if (this.sign !== n.sign) {
|
|
return this.subtract(n.negate());
|
|
}
|
|
var a = this.value, b = n.value;
|
|
if (n.isSmall) {
|
|
return new BigInteger(addSmall(a, Math.abs(b)), this.sign);
|
|
}
|
|
return new BigInteger(addAny(a, b), this.sign);
|
|
};
|
|
BigInteger.prototype.plus = BigInteger.prototype.add;
|
|
|
|
SmallInteger.prototype.add = function (v) {
|
|
var n = parseValue(v);
|
|
var a = this.value;
|
|
if (a < 0 !== n.sign) {
|
|
return this.subtract(n.negate());
|
|
}
|
|
var b = n.value;
|
|
if (n.isSmall) {
|
|
if (isPrecise(a + b)) return new SmallInteger(a + b);
|
|
b = smallToArray(Math.abs(b));
|
|
}
|
|
return new BigInteger(addSmall(b, Math.abs(a)), a < 0);
|
|
};
|
|
SmallInteger.prototype.plus = SmallInteger.prototype.add;
|
|
|
|
NativeBigInt.prototype.add = function (v) {
|
|
return new NativeBigInt(this.value + parseValue(v).value);
|
|
};
|
|
NativeBigInt.prototype.plus = NativeBigInt.prototype.add;
|
|
|
|
function subtract(a, b) { // assumes a and b are arrays with a >= b
|
|
var a_l = a.length,
|
|
b_l = b.length,
|
|
r = new Array(a_l),
|
|
borrow = 0,
|
|
base = BASE,
|
|
i, difference;
|
|
for (i = 0; i < b_l; i++) {
|
|
difference = a[i] - borrow - b[i];
|
|
if (difference < 0) {
|
|
difference += base;
|
|
borrow = 1;
|
|
} else borrow = 0;
|
|
r[i] = difference;
|
|
}
|
|
for (i = b_l; i < a_l; i++) {
|
|
difference = a[i] - borrow;
|
|
if (difference < 0) difference += base;
|
|
else {
|
|
r[i++] = difference;
|
|
break;
|
|
}
|
|
r[i] = difference;
|
|
}
|
|
for (; i < a_l; i++) {
|
|
r[i] = a[i];
|
|
}
|
|
trim(r);
|
|
return r;
|
|
}
|
|
|
|
function subtractAny(a, b, sign) {
|
|
var value;
|
|
if (compareAbs(a, b) >= 0) {
|
|
value = subtract(a, b);
|
|
} else {
|
|
value = subtract(b, a);
|
|
sign = !sign;
|
|
}
|
|
value = arrayToSmall(value);
|
|
if (typeof value === "number") {
|
|
if (sign) value = -value;
|
|
return new SmallInteger(value);
|
|
}
|
|
return new BigInteger(value, sign);
|
|
}
|
|
|
|
function subtractSmall(a, b, sign) { // assumes a is array, b is number with 0 <= b < MAX_INT
|
|
var l = a.length,
|
|
r = new Array(l),
|
|
carry = -b,
|
|
base = BASE,
|
|
i, difference;
|
|
for (i = 0; i < l; i++) {
|
|
difference = a[i] + carry;
|
|
carry = Math.floor(difference / base);
|
|
difference %= base;
|
|
r[i] = difference < 0 ? difference + base : difference;
|
|
}
|
|
r = arrayToSmall(r);
|
|
if (typeof r === "number") {
|
|
if (sign) r = -r;
|
|
return new SmallInteger(r);
|
|
} return new BigInteger(r, sign);
|
|
}
|
|
|
|
BigInteger.prototype.subtract = function (v) {
|
|
var n = parseValue(v);
|
|
if (this.sign !== n.sign) {
|
|
return this.add(n.negate());
|
|
}
|
|
var a = this.value, b = n.value;
|
|
if (n.isSmall)
|
|
return subtractSmall(a, Math.abs(b), this.sign);
|
|
return subtractAny(a, b, this.sign);
|
|
};
|
|
BigInteger.prototype.minus = BigInteger.prototype.subtract;
|
|
|
|
SmallInteger.prototype.subtract = function (v) {
|
|
var n = parseValue(v);
|
|
var a = this.value;
|
|
if (a < 0 !== n.sign) {
|
|
return this.add(n.negate());
|
|
}
|
|
var b = n.value;
|
|
if (n.isSmall) {
|
|
return new SmallInteger(a - b);
|
|
}
|
|
return subtractSmall(b, Math.abs(a), a >= 0);
|
|
};
|
|
SmallInteger.prototype.minus = SmallInteger.prototype.subtract;
|
|
|
|
NativeBigInt.prototype.subtract = function (v) {
|
|
return new NativeBigInt(this.value - parseValue(v).value);
|
|
};
|
|
NativeBigInt.prototype.minus = NativeBigInt.prototype.subtract;
|
|
|
|
BigInteger.prototype.negate = function () {
|
|
return new BigInteger(this.value, !this.sign);
|
|
};
|
|
SmallInteger.prototype.negate = function () {
|
|
var sign = this.sign;
|
|
var small = new SmallInteger(-this.value);
|
|
small.sign = !sign;
|
|
return small;
|
|
};
|
|
NativeBigInt.prototype.negate = function () {
|
|
return new NativeBigInt(-this.value);
|
|
};
|
|
|
|
BigInteger.prototype.abs = function () {
|
|
return new BigInteger(this.value, false);
|
|
};
|
|
SmallInteger.prototype.abs = function () {
|
|
return new SmallInteger(Math.abs(this.value));
|
|
};
|
|
NativeBigInt.prototype.abs = function () {
|
|
return new NativeBigInt(this.value >= 0 ? this.value : -this.value);
|
|
};
|
|
|
|
|
|
function multiplyLong(a, b) {
|
|
var a_l = a.length,
|
|
b_l = b.length,
|
|
l = a_l + b_l,
|
|
r = createArray(l),
|
|
base = BASE,
|
|
product, carry, i, a_i, b_j;
|
|
for (i = 0; i < a_l; ++i) {
|
|
a_i = a[i];
|
|
for (var j = 0; j < b_l; ++j) {
|
|
b_j = b[j];
|
|
product = a_i * b_j + r[i + j];
|
|
carry = Math.floor(product / base);
|
|
r[i + j] = product - carry * base;
|
|
r[i + j + 1] += carry;
|
|
}
|
|
}
|
|
trim(r);
|
|
return r;
|
|
}
|
|
|
|
function multiplySmall(a, b) { // assumes a is array, b is number with |b| < BASE
|
|
var l = a.length,
|
|
r = new Array(l),
|
|
base = BASE,
|
|
carry = 0,
|
|
product, i;
|
|
for (i = 0; i < l; i++) {
|
|
product = a[i] * b + carry;
|
|
carry = Math.floor(product / base);
|
|
r[i] = product - carry * base;
|
|
}
|
|
while (carry > 0) {
|
|
r[i++] = carry % base;
|
|
carry = Math.floor(carry / base);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
function shiftLeft(x, n) {
|
|
var r = [];
|
|
while (n-- > 0) r.push(0);
|
|
return r.concat(x);
|
|
}
|
|
|
|
function multiplyKaratsuba(x, y) {
|
|
var n = Math.max(x.length, y.length);
|
|
|
|
if (n <= 30) return multiplyLong(x, y);
|
|
n = Math.ceil(n / 2);
|
|
|
|
var b = x.slice(n),
|
|
a = x.slice(0, n),
|
|
d = y.slice(n),
|
|
c = y.slice(0, n);
|
|
|
|
var ac = multiplyKaratsuba(a, c),
|
|
bd = multiplyKaratsuba(b, d),
|
|
abcd = multiplyKaratsuba(addAny(a, b), addAny(c, d));
|
|
|
|
var product = addAny(addAny(ac, shiftLeft(subtract(subtract(abcd, ac), bd), n)), shiftLeft(bd, 2 * n));
|
|
trim(product);
|
|
return product;
|
|
}
|
|
|
|
// The following function is derived from a surface fit of a graph plotting the performance difference
|
|
// between long multiplication and karatsuba multiplication versus the lengths of the two arrays.
|
|
function useKaratsuba(l1, l2) {
|
|
return -0.012 * l1 - 0.012 * l2 + 0.000015 * l1 * l2 > 0;
|
|
}
|
|
|
|
BigInteger.prototype.multiply = function (v) {
|
|
var n = parseValue(v),
|
|
a = this.value, b = n.value,
|
|
sign = this.sign !== n.sign,
|
|
abs;
|
|
if (n.isSmall) {
|
|
if (b === 0) return Integer[0];
|
|
if (b === 1) return this;
|
|
if (b === -1) return this.negate();
|
|
abs = Math.abs(b);
|
|
if (abs < BASE) {
|
|
return new BigInteger(multiplySmall(a, abs), sign);
|
|
}
|
|
b = smallToArray(abs);
|
|
}
|
|
if (useKaratsuba(a.length, b.length)) // Karatsuba is only faster for certain array sizes
|
|
return new BigInteger(multiplyKaratsuba(a, b), sign);
|
|
return new BigInteger(multiplyLong(a, b), sign);
|
|
};
|
|
|
|
BigInteger.prototype.times = BigInteger.prototype.multiply;
|
|
|
|
function multiplySmallAndArray(a, b, sign) { // a >= 0
|
|
if (a < BASE) {
|
|
return new BigInteger(multiplySmall(b, a), sign);
|
|
}
|
|
return new BigInteger(multiplyLong(b, smallToArray(a)), sign);
|
|
}
|
|
SmallInteger.prototype._multiplyBySmall = function (a) {
|
|
if (isPrecise(a.value * this.value)) {
|
|
return new SmallInteger(a.value * this.value);
|
|
}
|
|
return multiplySmallAndArray(Math.abs(a.value), smallToArray(Math.abs(this.value)), this.sign !== a.sign);
|
|
};
|
|
BigInteger.prototype._multiplyBySmall = function (a) {
|
|
if (a.value === 0) return Integer[0];
|
|
if (a.value === 1) return this;
|
|
if (a.value === -1) return this.negate();
|
|
return multiplySmallAndArray(Math.abs(a.value), this.value, this.sign !== a.sign);
|
|
};
|
|
SmallInteger.prototype.multiply = function (v) {
|
|
return parseValue(v)._multiplyBySmall(this);
|
|
};
|
|
SmallInteger.prototype.times = SmallInteger.prototype.multiply;
|
|
|
|
NativeBigInt.prototype.multiply = function (v) {
|
|
return new NativeBigInt(this.value * parseValue(v).value);
|
|
};
|
|
NativeBigInt.prototype.times = NativeBigInt.prototype.multiply;
|
|
|
|
function square(a) {
|
|
//console.assert(2 * BASE * BASE < MAX_INT);
|
|
var l = a.length,
|
|
r = createArray(l + l),
|
|
base = BASE,
|
|
product, carry, i, a_i, a_j;
|
|
for (i = 0; i < l; i++) {
|
|
a_i = a[i];
|
|
carry = 0 - a_i * a_i;
|
|
for (var j = i; j < l; j++) {
|
|
a_j = a[j];
|
|
product = 2 * (a_i * a_j) + r[i + j] + carry;
|
|
carry = Math.floor(product / base);
|
|
r[i + j] = product - carry * base;
|
|
}
|
|
r[i + l] = carry;
|
|
}
|
|
trim(r);
|
|
return r;
|
|
}
|
|
|
|
BigInteger.prototype.square = function () {
|
|
return new BigInteger(square(this.value), false);
|
|
};
|
|
|
|
SmallInteger.prototype.square = function () {
|
|
var value = this.value * this.value;
|
|
if (isPrecise(value)) return new SmallInteger(value);
|
|
return new BigInteger(square(smallToArray(Math.abs(this.value))), false);
|
|
};
|
|
|
|
NativeBigInt.prototype.square = function (v) {
|
|
return new NativeBigInt(this.value * this.value);
|
|
};
|
|
|
|
function divMod1(a, b) { // Left over from previous version. Performs faster than divMod2 on smaller input sizes.
|
|
var a_l = a.length,
|
|
b_l = b.length,
|
|
base = BASE,
|
|
result = createArray(b.length),
|
|
divisorMostSignificantDigit = b[b_l - 1],
|
|
// normalization
|
|
lambda = Math.ceil(base / (2 * divisorMostSignificantDigit)),
|
|
remainder = multiplySmall(a, lambda),
|
|
divisor = multiplySmall(b, lambda),
|
|
quotientDigit, shift, carry, borrow, i, l, q;
|
|
if (remainder.length <= a_l) remainder.push(0);
|
|
divisor.push(0);
|
|
divisorMostSignificantDigit = divisor[b_l - 1];
|
|
for (shift = a_l - b_l; shift >= 0; shift--) {
|
|
quotientDigit = base - 1;
|
|
if (remainder[shift + b_l] !== divisorMostSignificantDigit) {
|
|
quotientDigit = Math.floor((remainder[shift + b_l] * base + remainder[shift + b_l - 1]) / divisorMostSignificantDigit);
|
|
}
|
|
// quotientDigit <= base - 1
|
|
carry = 0;
|
|
borrow = 0;
|
|
l = divisor.length;
|
|
for (i = 0; i < l; i++) {
|
|
carry += quotientDigit * divisor[i];
|
|
q = Math.floor(carry / base);
|
|
borrow += remainder[shift + i] - (carry - q * base);
|
|
carry = q;
|
|
if (borrow < 0) {
|
|
remainder[shift + i] = borrow + base;
|
|
borrow = -1;
|
|
} else {
|
|
remainder[shift + i] = borrow;
|
|
borrow = 0;
|
|
}
|
|
}
|
|
while (borrow !== 0) {
|
|
quotientDigit -= 1;
|
|
carry = 0;
|
|
for (i = 0; i < l; i++) {
|
|
carry += remainder[shift + i] - base + divisor[i];
|
|
if (carry < 0) {
|
|
remainder[shift + i] = carry + base;
|
|
carry = 0;
|
|
} else {
|
|
remainder[shift + i] = carry;
|
|
carry = 1;
|
|
}
|
|
}
|
|
borrow += carry;
|
|
}
|
|
result[shift] = quotientDigit;
|
|
}
|
|
// denormalization
|
|
remainder = divModSmall(remainder, lambda)[0];
|
|
return [arrayToSmall(result), arrayToSmall(remainder)];
|
|
}
|
|
|
|
function divMod2(a, b) { // Implementation idea shamelessly stolen from Silent Matt's library http://silentmatt.com/biginteger/
|
|
// Performs faster than divMod1 on larger input sizes.
|
|
var a_l = a.length,
|
|
b_l = b.length,
|
|
result = [],
|
|
part = [],
|
|
base = BASE,
|
|
guess, xlen, highx, highy, check;
|
|
while (a_l) {
|
|
part.unshift(a[--a_l]);
|
|
trim(part);
|
|
if (compareAbs(part, b) < 0) {
|
|
result.push(0);
|
|
continue;
|
|
}
|
|
xlen = part.length;
|
|
highx = part[xlen - 1] * base + part[xlen - 2];
|
|
highy = b[b_l - 1] * base + b[b_l - 2];
|
|
if (xlen > b_l) {
|
|
highx = (highx + 1) * base;
|
|
}
|
|
guess = Math.ceil(highx / highy);
|
|
do {
|
|
check = multiplySmall(b, guess);
|
|
if (compareAbs(check, part) <= 0) break;
|
|
guess--;
|
|
} while (guess);
|
|
result.push(guess);
|
|
part = subtract(part, check);
|
|
}
|
|
result.reverse();
|
|
return [arrayToSmall(result), arrayToSmall(part)];
|
|
}
|
|
|
|
function divModSmall(value, lambda) {
|
|
var length = value.length,
|
|
quotient = createArray(length),
|
|
base = BASE,
|
|
i, q, remainder, divisor;
|
|
remainder = 0;
|
|
for (i = length - 1; i >= 0; --i) {
|
|
divisor = remainder * base + value[i];
|
|
q = truncate(divisor / lambda);
|
|
remainder = divisor - q * lambda;
|
|
quotient[i] = q | 0;
|
|
}
|
|
return [quotient, remainder | 0];
|
|
}
|
|
|
|
function divModAny(self, v) {
|
|
var value, n = parseValue(v);
|
|
if (supportsNativeBigInt) {
|
|
return [new NativeBigInt(self.value / n.value), new NativeBigInt(self.value % n.value)];
|
|
}
|
|
var a = self.value, b = n.value;
|
|
var quotient;
|
|
if (b === 0) throw new Error("Cannot divide by zero");
|
|
if (self.isSmall) {
|
|
if (n.isSmall) {
|
|
return [new SmallInteger(truncate(a / b)), new SmallInteger(a % b)];
|
|
}
|
|
return [Integer[0], self];
|
|
}
|
|
if (n.isSmall) {
|
|
if (b === 1) return [self, Integer[0]];
|
|
if (b == -1) return [self.negate(), Integer[0]];
|
|
var abs = Math.abs(b);
|
|
if (abs < BASE) {
|
|
value = divModSmall(a, abs);
|
|
quotient = arrayToSmall(value[0]);
|
|
var remainder = value[1];
|
|
if (self.sign) remainder = -remainder;
|
|
if (typeof quotient === "number") {
|
|
if (self.sign !== n.sign) quotient = -quotient;
|
|
return [new SmallInteger(quotient), new SmallInteger(remainder)];
|
|
}
|
|
return [new BigInteger(quotient, self.sign !== n.sign), new SmallInteger(remainder)];
|
|
}
|
|
b = smallToArray(abs);
|
|
}
|
|
var comparison = compareAbs(a, b);
|
|
if (comparison === -1) return [Integer[0], self];
|
|
if (comparison === 0) return [Integer[self.sign === n.sign ? 1 : -1], Integer[0]];
|
|
|
|
// divMod1 is faster on smaller input sizes
|
|
if (a.length + b.length <= 200)
|
|
value = divMod1(a, b);
|
|
else value = divMod2(a, b);
|
|
|
|
quotient = value[0];
|
|
var qSign = self.sign !== n.sign,
|
|
mod = value[1],
|
|
mSign = self.sign;
|
|
if (typeof quotient === "number") {
|
|
if (qSign) quotient = -quotient;
|
|
quotient = new SmallInteger(quotient);
|
|
} else quotient = new BigInteger(quotient, qSign);
|
|
if (typeof mod === "number") {
|
|
if (mSign) mod = -mod;
|
|
mod = new SmallInteger(mod);
|
|
} else mod = new BigInteger(mod, mSign);
|
|
return [quotient, mod];
|
|
}
|
|
|
|
BigInteger.prototype.divmod = function (v) {
|
|
var result = divModAny(this, v);
|
|
return {
|
|
quotient: result[0],
|
|
remainder: result[1]
|
|
};
|
|
};
|
|
NativeBigInt.prototype.divmod = SmallInteger.prototype.divmod = BigInteger.prototype.divmod;
|
|
|
|
|
|
BigInteger.prototype.divide = function (v) {
|
|
return divModAny(this, v)[0];
|
|
};
|
|
NativeBigInt.prototype.over = NativeBigInt.prototype.divide = function (v) {
|
|
return new NativeBigInt(this.value / parseValue(v).value);
|
|
};
|
|
SmallInteger.prototype.over = SmallInteger.prototype.divide = BigInteger.prototype.over = BigInteger.prototype.divide;
|
|
|
|
BigInteger.prototype.mod = function (v) {
|
|
return divModAny(this, v)[1];
|
|
};
|
|
NativeBigInt.prototype.mod = NativeBigInt.prototype.remainder = function (v) {
|
|
return new NativeBigInt(this.value % parseValue(v).value);
|
|
};
|
|
SmallInteger.prototype.remainder = SmallInteger.prototype.mod = BigInteger.prototype.remainder = BigInteger.prototype.mod;
|
|
|
|
BigInteger.prototype.pow = function (v) {
|
|
var n = parseValue(v),
|
|
a = this.value,
|
|
b = n.value,
|
|
value, x, y;
|
|
if (b === 0) return Integer[1];
|
|
if (a === 0) return Integer[0];
|
|
if (a === 1) return Integer[1];
|
|
if (a === -1) return n.isEven() ? Integer[1] : Integer[-1];
|
|
if (n.sign) {
|
|
return Integer[0];
|
|
}
|
|
if (!n.isSmall) throw new Error("The exponent " + n.toString() + " is too large.");
|
|
if (this.isSmall) {
|
|
if (isPrecise(value = Math.pow(a, b)))
|
|
return new SmallInteger(truncate(value));
|
|
}
|
|
x = this;
|
|
y = Integer[1];
|
|
while (true) {
|
|
if (b & 1 === 1) {
|
|
y = y.times(x);
|
|
--b;
|
|
}
|
|
if (b === 0) break;
|
|
b /= 2;
|
|
x = x.square();
|
|
}
|
|
return y;
|
|
};
|
|
SmallInteger.prototype.pow = BigInteger.prototype.pow;
|
|
|
|
NativeBigInt.prototype.pow = function (v) {
|
|
var n = parseValue(v);
|
|
var a = this.value, b = n.value;
|
|
var _0 = BigInt(0), _1 = BigInt(1), _2 = BigInt(2);
|
|
if (b === _0) return Integer[1];
|
|
if (a === _0) return Integer[0];
|
|
if (a === _1) return Integer[1];
|
|
if (a === BigInt(-1)) return n.isEven() ? Integer[1] : Integer[-1];
|
|
if (n.isNegative()) return new NativeBigInt(_0);
|
|
var x = this;
|
|
var y = Integer[1];
|
|
while (true) {
|
|
if ((b & _1) === _1) {
|
|
y = y.times(x);
|
|
--b;
|
|
}
|
|
if (b === _0) break;
|
|
b /= _2;
|
|
x = x.square();
|
|
}
|
|
return y;
|
|
};
|
|
|
|
BigInteger.prototype.modPow = function (exp, mod) {
|
|
exp = parseValue(exp);
|
|
mod = parseValue(mod);
|
|
if (mod.isZero()) throw new Error("Cannot take modPow with modulus 0");
|
|
var r = Integer[1],
|
|
base = this.mod(mod);
|
|
if (exp.isNegative()) {
|
|
exp = exp.multiply(Integer[-1]);
|
|
base = base.modInv(mod);
|
|
}
|
|
while (exp.isPositive()) {
|
|
if (base.isZero()) return Integer[0];
|
|
if (exp.isOdd()) r = r.multiply(base).mod(mod);
|
|
exp = exp.divide(2);
|
|
base = base.square().mod(mod);
|
|
}
|
|
return r;
|
|
};
|
|
NativeBigInt.prototype.modPow = SmallInteger.prototype.modPow = BigInteger.prototype.modPow;
|
|
|
|
function compareAbs(a, b) {
|
|
if (a.length !== b.length) {
|
|
return a.length > b.length ? 1 : -1;
|
|
}
|
|
for (var i = a.length - 1; i >= 0; i--) {
|
|
if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
BigInteger.prototype.compareAbs = function (v) {
|
|
var n = parseValue(v),
|
|
a = this.value,
|
|
b = n.value;
|
|
if (n.isSmall) return 1;
|
|
return compareAbs(a, b);
|
|
};
|
|
SmallInteger.prototype.compareAbs = function (v) {
|
|
var n = parseValue(v),
|
|
a = Math.abs(this.value),
|
|
b = n.value;
|
|
if (n.isSmall) {
|
|
b = Math.abs(b);
|
|
return a === b ? 0 : a > b ? 1 : -1;
|
|
}
|
|
return -1;
|
|
};
|
|
NativeBigInt.prototype.compareAbs = function (v) {
|
|
var a = this.value;
|
|
var b = parseValue(v).value;
|
|
a = a >= 0 ? a : -a;
|
|
b = b >= 0 ? b : -b;
|
|
return a === b ? 0 : a > b ? 1 : -1;
|
|
};
|
|
|
|
BigInteger.prototype.compare = function (v) {
|
|
// See discussion about comparison with Infinity:
|
|
// https://github.com/peterolson/BigInteger.js/issues/61
|
|
if (v === Infinity) {
|
|
return -1;
|
|
}
|
|
if (v === -Infinity) {
|
|
return 1;
|
|
}
|
|
|
|
var n = parseValue(v),
|
|
a = this.value,
|
|
b = n.value;
|
|
if (this.sign !== n.sign) {
|
|
return n.sign ? 1 : -1;
|
|
}
|
|
if (n.isSmall) {
|
|
return this.sign ? -1 : 1;
|
|
}
|
|
return compareAbs(a, b) * (this.sign ? -1 : 1);
|
|
};
|
|
BigInteger.prototype.compareTo = BigInteger.prototype.compare;
|
|
|
|
SmallInteger.prototype.compare = function (v) {
|
|
if (v === Infinity) {
|
|
return -1;
|
|
}
|
|
if (v === -Infinity) {
|
|
return 1;
|
|
}
|
|
|
|
var n = parseValue(v),
|
|
a = this.value,
|
|
b = n.value;
|
|
if (n.isSmall) {
|
|
return a == b ? 0 : a > b ? 1 : -1;
|
|
}
|
|
if (a < 0 !== n.sign) {
|
|
return a < 0 ? -1 : 1;
|
|
}
|
|
return a < 0 ? 1 : -1;
|
|
};
|
|
SmallInteger.prototype.compareTo = SmallInteger.prototype.compare;
|
|
|
|
NativeBigInt.prototype.compare = function (v) {
|
|
if (v === Infinity) {
|
|
return -1;
|
|
}
|
|
if (v === -Infinity) {
|
|
return 1;
|
|
}
|
|
var a = this.value;
|
|
var b = parseValue(v).value;
|
|
return a === b ? 0 : a > b ? 1 : -1;
|
|
};
|
|
NativeBigInt.prototype.compareTo = NativeBigInt.prototype.compare;
|
|
|
|
BigInteger.prototype.equals = function (v) {
|
|
return this.compare(v) === 0;
|
|
};
|
|
NativeBigInt.prototype.eq = NativeBigInt.prototype.equals = SmallInteger.prototype.eq = SmallInteger.prototype.equals = BigInteger.prototype.eq = BigInteger.prototype.equals;
|
|
|
|
BigInteger.prototype.notEquals = function (v) {
|
|
return this.compare(v) !== 0;
|
|
};
|
|
NativeBigInt.prototype.neq = NativeBigInt.prototype.notEquals = SmallInteger.prototype.neq = SmallInteger.prototype.notEquals = BigInteger.prototype.neq = BigInteger.prototype.notEquals;
|
|
|
|
BigInteger.prototype.greater = function (v) {
|
|
return this.compare(v) > 0;
|
|
};
|
|
NativeBigInt.prototype.gt = NativeBigInt.prototype.greater = SmallInteger.prototype.gt = SmallInteger.prototype.greater = BigInteger.prototype.gt = BigInteger.prototype.greater;
|
|
|
|
BigInteger.prototype.lesser = function (v) {
|
|
return this.compare(v) < 0;
|
|
};
|
|
NativeBigInt.prototype.lt = NativeBigInt.prototype.lesser = SmallInteger.prototype.lt = SmallInteger.prototype.lesser = BigInteger.prototype.lt = BigInteger.prototype.lesser;
|
|
|
|
BigInteger.prototype.greaterOrEquals = function (v) {
|
|
return this.compare(v) >= 0;
|
|
};
|
|
NativeBigInt.prototype.geq = NativeBigInt.prototype.greaterOrEquals = SmallInteger.prototype.geq = SmallInteger.prototype.greaterOrEquals = BigInteger.prototype.geq = BigInteger.prototype.greaterOrEquals;
|
|
|
|
BigInteger.prototype.lesserOrEquals = function (v) {
|
|
return this.compare(v) <= 0;
|
|
};
|
|
NativeBigInt.prototype.leq = NativeBigInt.prototype.lesserOrEquals = SmallInteger.prototype.leq = SmallInteger.prototype.lesserOrEquals = BigInteger.prototype.leq = BigInteger.prototype.lesserOrEquals;
|
|
|
|
BigInteger.prototype.isEven = function () {
|
|
return (this.value[0] & 1) === 0;
|
|
};
|
|
SmallInteger.prototype.isEven = function () {
|
|
return (this.value & 1) === 0;
|
|
};
|
|
NativeBigInt.prototype.isEven = function () {
|
|
return (this.value & BigInt(1)) === BigInt(0);
|
|
};
|
|
|
|
BigInteger.prototype.isOdd = function () {
|
|
return (this.value[0] & 1) === 1;
|
|
};
|
|
SmallInteger.prototype.isOdd = function () {
|
|
return (this.value & 1) === 1;
|
|
};
|
|
NativeBigInt.prototype.isOdd = function () {
|
|
return (this.value & BigInt(1)) === BigInt(1);
|
|
};
|
|
|
|
BigInteger.prototype.isPositive = function () {
|
|
return !this.sign;
|
|
};
|
|
SmallInteger.prototype.isPositive = function () {
|
|
return this.value > 0;
|
|
};
|
|
NativeBigInt.prototype.isPositive = SmallInteger.prototype.isPositive;
|
|
|
|
BigInteger.prototype.isNegative = function () {
|
|
return this.sign;
|
|
};
|
|
SmallInteger.prototype.isNegative = function () {
|
|
return this.value < 0;
|
|
};
|
|
NativeBigInt.prototype.isNegative = SmallInteger.prototype.isNegative;
|
|
|
|
BigInteger.prototype.isUnit = function () {
|
|
return false;
|
|
};
|
|
SmallInteger.prototype.isUnit = function () {
|
|
return Math.abs(this.value) === 1;
|
|
};
|
|
NativeBigInt.prototype.isUnit = function () {
|
|
return this.abs().value === BigInt(1);
|
|
};
|
|
|
|
BigInteger.prototype.isZero = function () {
|
|
return false;
|
|
};
|
|
SmallInteger.prototype.isZero = function () {
|
|
return this.value === 0;
|
|
};
|
|
NativeBigInt.prototype.isZero = function () {
|
|
return this.value === BigInt(0);
|
|
};
|
|
|
|
BigInteger.prototype.isDivisibleBy = function (v) {
|
|
var n = parseValue(v);
|
|
if (n.isZero()) return false;
|
|
if (n.isUnit()) return true;
|
|
if (n.compareAbs(2) === 0) return this.isEven();
|
|
return this.mod(n).isZero();
|
|
};
|
|
NativeBigInt.prototype.isDivisibleBy = SmallInteger.prototype.isDivisibleBy = BigInteger.prototype.isDivisibleBy;
|
|
|
|
function isBasicPrime(v) {
|
|
var n = v.abs();
|
|
if (n.isUnit()) return false;
|
|
if (n.equals(2) || n.equals(3) || n.equals(5)) return true;
|
|
if (n.isEven() || n.isDivisibleBy(3) || n.isDivisibleBy(5)) return false;
|
|
if (n.lesser(49)) return true;
|
|
// we don't know if it's prime: let the other functions figure it out
|
|
}
|
|
|
|
function millerRabinTest(n, a) {
|
|
var nPrev = n.prev(),
|
|
b = nPrev,
|
|
r = 0,
|
|
d, i, x;
|
|
while (b.isEven()) b = b.divide(2), r++;
|
|
next: for (i = 0; i < a.length; i++) {
|
|
if (n.lesser(a[i])) continue;
|
|
x = bigInt(a[i]).modPow(b, n);
|
|
if (x.isUnit() || x.equals(nPrev)) continue;
|
|
for (d = r - 1; d != 0; d--) {
|
|
x = x.square().mod(n);
|
|
if (x.isUnit()) return false;
|
|
if (x.equals(nPrev)) continue next;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Set "strict" to true to force GRH-supported lower bound of 2*log(N)^2
|
|
BigInteger.prototype.isPrime = function (strict) {
|
|
var isPrime = isBasicPrime(this);
|
|
if (isPrime !== undefined$1) return isPrime;
|
|
var n = this.abs();
|
|
var bits = n.bitLength();
|
|
if (bits <= 64)
|
|
return millerRabinTest(n, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]);
|
|
var logN = Math.log(2) * bits.toJSNumber();
|
|
var t = Math.ceil((strict === true) ? (2 * Math.pow(logN, 2)) : logN);
|
|
for (var a = [], i = 0; i < t; i++) {
|
|
a.push(bigInt(i + 2));
|
|
}
|
|
return millerRabinTest(n, a);
|
|
};
|
|
NativeBigInt.prototype.isPrime = SmallInteger.prototype.isPrime = BigInteger.prototype.isPrime;
|
|
|
|
BigInteger.prototype.isProbablePrime = function (iterations, rng) {
|
|
var isPrime = isBasicPrime(this);
|
|
if (isPrime !== undefined$1) return isPrime;
|
|
var n = this.abs();
|
|
var t = iterations === undefined$1 ? 5 : iterations;
|
|
for (var a = [], i = 0; i < t; i++) {
|
|
a.push(bigInt.randBetween(2, n.minus(2), rng));
|
|
}
|
|
return millerRabinTest(n, a);
|
|
};
|
|
NativeBigInt.prototype.isProbablePrime = SmallInteger.prototype.isProbablePrime = BigInteger.prototype.isProbablePrime;
|
|
|
|
BigInteger.prototype.modInv = function (n) {
|
|
var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR;
|
|
while (!newR.isZero()) {
|
|
q = r.divide(newR);
|
|
lastT = t;
|
|
lastR = r;
|
|
t = newT;
|
|
r = newR;
|
|
newT = lastT.subtract(q.multiply(newT));
|
|
newR = lastR.subtract(q.multiply(newR));
|
|
}
|
|
if (!r.isUnit()) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime");
|
|
if (t.compare(0) === -1) {
|
|
t = t.add(n);
|
|
}
|
|
if (this.isNegative()) {
|
|
return t.negate();
|
|
}
|
|
return t;
|
|
};
|
|
|
|
NativeBigInt.prototype.modInv = SmallInteger.prototype.modInv = BigInteger.prototype.modInv;
|
|
|
|
BigInteger.prototype.next = function () {
|
|
var value = this.value;
|
|
if (this.sign) {
|
|
return subtractSmall(value, 1, this.sign);
|
|
}
|
|
return new BigInteger(addSmall(value, 1), this.sign);
|
|
};
|
|
SmallInteger.prototype.next = function () {
|
|
var value = this.value;
|
|
if (value + 1 < MAX_INT) return new SmallInteger(value + 1);
|
|
return new BigInteger(MAX_INT_ARR, false);
|
|
};
|
|
NativeBigInt.prototype.next = function () {
|
|
return new NativeBigInt(this.value + BigInt(1));
|
|
};
|
|
|
|
BigInteger.prototype.prev = function () {
|
|
var value = this.value;
|
|
if (this.sign) {
|
|
return new BigInteger(addSmall(value, 1), true);
|
|
}
|
|
return subtractSmall(value, 1, this.sign);
|
|
};
|
|
SmallInteger.prototype.prev = function () {
|
|
var value = this.value;
|
|
if (value - 1 > -MAX_INT) return new SmallInteger(value - 1);
|
|
return new BigInteger(MAX_INT_ARR, true);
|
|
};
|
|
NativeBigInt.prototype.prev = function () {
|
|
return new NativeBigInt(this.value - BigInt(1));
|
|
};
|
|
|
|
var powersOfTwo = [1];
|
|
while (2 * powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]);
|
|
var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1];
|
|
|
|
function shift_isSmall(n) {
|
|
return Math.abs(n) <= BASE;
|
|
}
|
|
|
|
BigInteger.prototype.shiftLeft = function (v) {
|
|
var n = parseValue(v).toJSNumber();
|
|
if (!shift_isSmall(n)) {
|
|
throw new Error(String(n) + " is too large for shifting.");
|
|
}
|
|
if (n < 0) return this.shiftRight(-n);
|
|
var result = this;
|
|
if (result.isZero()) return result;
|
|
while (n >= powers2Length) {
|
|
result = result.multiply(highestPower2);
|
|
n -= powers2Length - 1;
|
|
}
|
|
return result.multiply(powersOfTwo[n]);
|
|
};
|
|
NativeBigInt.prototype.shiftLeft = SmallInteger.prototype.shiftLeft = BigInteger.prototype.shiftLeft;
|
|
|
|
BigInteger.prototype.shiftRight = function (v) {
|
|
var remQuo;
|
|
var n = parseValue(v).toJSNumber();
|
|
if (!shift_isSmall(n)) {
|
|
throw new Error(String(n) + " is too large for shifting.");
|
|
}
|
|
if (n < 0) return this.shiftLeft(-n);
|
|
var result = this;
|
|
while (n >= powers2Length) {
|
|
if (result.isZero() || (result.isNegative() && result.isUnit())) return result;
|
|
remQuo = divModAny(result, highestPower2);
|
|
result = remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0];
|
|
n -= powers2Length - 1;
|
|
}
|
|
remQuo = divModAny(result, powersOfTwo[n]);
|
|
return remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0];
|
|
};
|
|
NativeBigInt.prototype.shiftRight = SmallInteger.prototype.shiftRight = BigInteger.prototype.shiftRight;
|
|
|
|
function bitwise(x, y, fn) {
|
|
y = parseValue(y);
|
|
var xSign = x.isNegative(), ySign = y.isNegative();
|
|
var xRem = xSign ? x.not() : x,
|
|
yRem = ySign ? y.not() : y;
|
|
var xDigit = 0, yDigit = 0;
|
|
var xDivMod = null, yDivMod = null;
|
|
var result = [];
|
|
while (!xRem.isZero() || !yRem.isZero()) {
|
|
xDivMod = divModAny(xRem, highestPower2);
|
|
xDigit = xDivMod[1].toJSNumber();
|
|
if (xSign) {
|
|
xDigit = highestPower2 - 1 - xDigit; // two's complement for negative numbers
|
|
}
|
|
|
|
yDivMod = divModAny(yRem, highestPower2);
|
|
yDigit = yDivMod[1].toJSNumber();
|
|
if (ySign) {
|
|
yDigit = highestPower2 - 1 - yDigit; // two's complement for negative numbers
|
|
}
|
|
|
|
xRem = xDivMod[0];
|
|
yRem = yDivMod[0];
|
|
result.push(fn(xDigit, yDigit));
|
|
}
|
|
var sum = fn(xSign ? 1 : 0, ySign ? 1 : 0) !== 0 ? bigInt(-1) : bigInt(0);
|
|
for (var i = result.length - 1; i >= 0; i -= 1) {
|
|
sum = sum.multiply(highestPower2).add(bigInt(result[i]));
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
BigInteger.prototype.not = function () {
|
|
return this.negate().prev();
|
|
};
|
|
NativeBigInt.prototype.not = SmallInteger.prototype.not = BigInteger.prototype.not;
|
|
|
|
BigInteger.prototype.and = function (n) {
|
|
return bitwise(this, n, function (a, b) { return a & b; });
|
|
};
|
|
NativeBigInt.prototype.and = SmallInteger.prototype.and = BigInteger.prototype.and;
|
|
|
|
BigInteger.prototype.or = function (n) {
|
|
return bitwise(this, n, function (a, b) { return a | b; });
|
|
};
|
|
NativeBigInt.prototype.or = SmallInteger.prototype.or = BigInteger.prototype.or;
|
|
|
|
BigInteger.prototype.xor = function (n) {
|
|
return bitwise(this, n, function (a, b) { return a ^ b; });
|
|
};
|
|
NativeBigInt.prototype.xor = SmallInteger.prototype.xor = BigInteger.prototype.xor;
|
|
|
|
var LOBMASK_I = 1 << 30, LOBMASK_BI = (BASE & -BASE) * (BASE & -BASE) | LOBMASK_I;
|
|
function roughLOB(n) { // get lowestOneBit (rough)
|
|
// SmallInteger: return Min(lowestOneBit(n), 1 << 30)
|
|
// BigInteger: return Min(lowestOneBit(n), 1 << 14) [BASE=1e7]
|
|
var v = n.value,
|
|
x = typeof v === "number" ? v | LOBMASK_I :
|
|
typeof v === "bigint" ? v | BigInt(LOBMASK_I) :
|
|
v[0] + v[1] * BASE | LOBMASK_BI;
|
|
return x & -x;
|
|
}
|
|
|
|
function integerLogarithm(value, base) {
|
|
if (base.compareTo(value) <= 0) {
|
|
var tmp = integerLogarithm(value, base.square(base));
|
|
var p = tmp.p;
|
|
var e = tmp.e;
|
|
var t = p.multiply(base);
|
|
return t.compareTo(value) <= 0 ? { p: t, e: e * 2 + 1 } : { p: p, e: e * 2 };
|
|
}
|
|
return { p: bigInt(1), e: 0 };
|
|
}
|
|
|
|
BigInteger.prototype.bitLength = function () {
|
|
var n = this;
|
|
if (n.compareTo(bigInt(0)) < 0) {
|
|
n = n.negate().subtract(bigInt(1));
|
|
}
|
|
if (n.compareTo(bigInt(0)) === 0) {
|
|
return bigInt(0);
|
|
}
|
|
return bigInt(integerLogarithm(n, bigInt(2)).e).add(bigInt(1));
|
|
};
|
|
NativeBigInt.prototype.bitLength = SmallInteger.prototype.bitLength = BigInteger.prototype.bitLength;
|
|
|
|
function max(a, b) {
|
|
a = parseValue(a);
|
|
b = parseValue(b);
|
|
return a.greater(b) ? a : b;
|
|
}
|
|
function min(a, b) {
|
|
a = parseValue(a);
|
|
b = parseValue(b);
|
|
return a.lesser(b) ? a : b;
|
|
}
|
|
function gcd(a, b) {
|
|
a = parseValue(a).abs();
|
|
b = parseValue(b).abs();
|
|
if (a.equals(b)) return a;
|
|
if (a.isZero()) return b;
|
|
if (b.isZero()) return a;
|
|
var c = Integer[1], d, t;
|
|
while (a.isEven() && b.isEven()) {
|
|
d = min(roughLOB(a), roughLOB(b));
|
|
a = a.divide(d);
|
|
b = b.divide(d);
|
|
c = c.multiply(d);
|
|
}
|
|
while (a.isEven()) {
|
|
a = a.divide(roughLOB(a));
|
|
}
|
|
do {
|
|
while (b.isEven()) {
|
|
b = b.divide(roughLOB(b));
|
|
}
|
|
if (a.greater(b)) {
|
|
t = b; b = a; a = t;
|
|
}
|
|
b = b.subtract(a);
|
|
} while (!b.isZero());
|
|
return c.isUnit() ? a : a.multiply(c);
|
|
}
|
|
function lcm(a, b) {
|
|
a = parseValue(a).abs();
|
|
b = parseValue(b).abs();
|
|
return a.divide(gcd(a, b)).multiply(b);
|
|
}
|
|
function randBetween(a, b, rng) {
|
|
a = parseValue(a);
|
|
b = parseValue(b);
|
|
var usedRNG = rng || Math.random;
|
|
var low = min(a, b), high = max(a, b);
|
|
var range = high.subtract(low).add(1);
|
|
if (range.isSmall) return low.add(Math.floor(usedRNG() * range));
|
|
var digits = toBase(range, BASE).value;
|
|
var result = [], restricted = true;
|
|
for (var i = 0; i < digits.length; i++) {
|
|
var top = restricted ? digits[i] + (i + 1 < digits.length ? digits[i + 1] / BASE : 0) : BASE;
|
|
var digit = truncate(usedRNG() * top);
|
|
result.push(digit);
|
|
if (digit < digits[i]) restricted = false;
|
|
}
|
|
return low.add(Integer.fromArray(result, BASE, false));
|
|
}
|
|
|
|
var parseBase = function (text, base, alphabet, caseSensitive) {
|
|
alphabet = alphabet || DEFAULT_ALPHABET;
|
|
text = String(text);
|
|
if (!caseSensitive) {
|
|
text = text.toLowerCase();
|
|
alphabet = alphabet.toLowerCase();
|
|
}
|
|
var length = text.length;
|
|
var i;
|
|
var absBase = Math.abs(base);
|
|
var alphabetValues = {};
|
|
for (i = 0; i < alphabet.length; i++) {
|
|
alphabetValues[alphabet[i]] = i;
|
|
}
|
|
for (i = 0; i < length; i++) {
|
|
var c = text[i];
|
|
if (c === "-") continue;
|
|
if (c in alphabetValues) {
|
|
if (alphabetValues[c] >= absBase) {
|
|
if (c === "1" && absBase === 1) continue;
|
|
throw new Error(c + " is not a valid digit in base " + base + ".");
|
|
}
|
|
}
|
|
}
|
|
base = parseValue(base);
|
|
var digits = [];
|
|
var isNegative = text[0] === "-";
|
|
for (i = isNegative ? 1 : 0; i < text.length; i++) {
|
|
var c = text[i];
|
|
if (c in alphabetValues) digits.push(parseValue(alphabetValues[c]));
|
|
else if (c === "<") {
|
|
var start = i;
|
|
do { i++; } while (text[i] !== ">" && i < text.length);
|
|
digits.push(parseValue(text.slice(start + 1, i)));
|
|
}
|
|
else throw new Error(c + " is not a valid character");
|
|
}
|
|
return parseBaseFromArray(digits, base, isNegative);
|
|
};
|
|
|
|
function parseBaseFromArray(digits, base, isNegative) {
|
|
var val = Integer[0], pow = Integer[1], i;
|
|
for (i = digits.length - 1; i >= 0; i--) {
|
|
val = val.add(digits[i].times(pow));
|
|
pow = pow.times(base);
|
|
}
|
|
return isNegative ? val.negate() : val;
|
|
}
|
|
|
|
function stringify(digit, alphabet) {
|
|
alphabet = alphabet || DEFAULT_ALPHABET;
|
|
if (digit < alphabet.length) {
|
|
return alphabet[digit];
|
|
}
|
|
return "<" + digit + ">";
|
|
}
|
|
|
|
function toBase(n, base) {
|
|
base = bigInt(base);
|
|
if (base.isZero()) {
|
|
if (n.isZero()) return { value: [0], isNegative: false };
|
|
throw new Error("Cannot convert nonzero numbers to base 0.");
|
|
}
|
|
if (base.equals(-1)) {
|
|
if (n.isZero()) return { value: [0], isNegative: false };
|
|
if (n.isNegative())
|
|
return {
|
|
value: [].concat.apply([], Array.apply(null, Array(-n.toJSNumber()))
|
|
.map(Array.prototype.valueOf, [1, 0])
|
|
),
|
|
isNegative: false
|
|
};
|
|
|
|
var arr = Array.apply(null, Array(n.toJSNumber() - 1))
|
|
.map(Array.prototype.valueOf, [0, 1]);
|
|
arr.unshift([1]);
|
|
return {
|
|
value: [].concat.apply([], arr),
|
|
isNegative: false
|
|
};
|
|
}
|
|
|
|
var neg = false;
|
|
if (n.isNegative() && base.isPositive()) {
|
|
neg = true;
|
|
n = n.abs();
|
|
}
|
|
if (base.isUnit()) {
|
|
if (n.isZero()) return { value: [0], isNegative: false };
|
|
|
|
return {
|
|
value: Array.apply(null, Array(n.toJSNumber()))
|
|
.map(Number.prototype.valueOf, 1),
|
|
isNegative: neg
|
|
};
|
|
}
|
|
var out = [];
|
|
var left = n, divmod;
|
|
while (left.isNegative() || left.compareAbs(base) >= 0) {
|
|
divmod = left.divmod(base);
|
|
left = divmod.quotient;
|
|
var digit = divmod.remainder;
|
|
if (digit.isNegative()) {
|
|
digit = base.minus(digit).abs();
|
|
left = left.next();
|
|
}
|
|
out.push(digit.toJSNumber());
|
|
}
|
|
out.push(left.toJSNumber());
|
|
return { value: out.reverse(), isNegative: neg };
|
|
}
|
|
|
|
function toBaseString(n, base, alphabet) {
|
|
var arr = toBase(n, base);
|
|
return (arr.isNegative ? "-" : "") + arr.value.map(function (x) {
|
|
return stringify(x, alphabet);
|
|
}).join('');
|
|
}
|
|
|
|
BigInteger.prototype.toArray = function (radix) {
|
|
return toBase(this, radix);
|
|
};
|
|
|
|
SmallInteger.prototype.toArray = function (radix) {
|
|
return toBase(this, radix);
|
|
};
|
|
|
|
NativeBigInt.prototype.toArray = function (radix) {
|
|
return toBase(this, radix);
|
|
};
|
|
|
|
BigInteger.prototype.toString = function (radix, alphabet) {
|
|
if (radix === undefined$1) radix = 10;
|
|
if (radix !== 10 || alphabet) return toBaseString(this, radix, alphabet);
|
|
var v = this.value, l = v.length, str = String(v[--l]), zeros = "0000000", digit;
|
|
while (--l >= 0) {
|
|
digit = String(v[l]);
|
|
str += zeros.slice(digit.length) + digit;
|
|
}
|
|
var sign = this.sign ? "-" : "";
|
|
return sign + str;
|
|
};
|
|
|
|
SmallInteger.prototype.toString = function (radix, alphabet) {
|
|
if (radix === undefined$1) radix = 10;
|
|
if (radix != 10 || alphabet) return toBaseString(this, radix, alphabet);
|
|
return String(this.value);
|
|
};
|
|
|
|
NativeBigInt.prototype.toString = SmallInteger.prototype.toString;
|
|
|
|
NativeBigInt.prototype.toJSON = BigInteger.prototype.toJSON = SmallInteger.prototype.toJSON = function () { return this.toString(); };
|
|
|
|
BigInteger.prototype.valueOf = function () {
|
|
return parseInt(this.toString(), 10);
|
|
};
|
|
BigInteger.prototype.toJSNumber = BigInteger.prototype.valueOf;
|
|
|
|
SmallInteger.prototype.valueOf = function () {
|
|
return this.value;
|
|
};
|
|
SmallInteger.prototype.toJSNumber = SmallInteger.prototype.valueOf;
|
|
NativeBigInt.prototype.valueOf = NativeBigInt.prototype.toJSNumber = function () {
|
|
return parseInt(this.toString(), 10);
|
|
};
|
|
|
|
function parseStringValue(v) {
|
|
if (isPrecise(+v)) {
|
|
var x = +v;
|
|
if (x === truncate(x))
|
|
return supportsNativeBigInt ? new NativeBigInt(BigInt(x)) : new SmallInteger(x);
|
|
throw new Error("Invalid integer: " + v);
|
|
}
|
|
var sign = v[0] === "-";
|
|
if (sign) v = v.slice(1);
|
|
var split = v.split(/e/i);
|
|
if (split.length > 2) throw new Error("Invalid integer: " + split.join("e"));
|
|
if (split.length === 2) {
|
|
var exp = split[1];
|
|
if (exp[0] === "+") exp = exp.slice(1);
|
|
exp = +exp;
|
|
if (exp !== truncate(exp) || !isPrecise(exp)) throw new Error("Invalid integer: " + exp + " is not a valid exponent.");
|
|
var text = split[0];
|
|
var decimalPlace = text.indexOf(".");
|
|
if (decimalPlace >= 0) {
|
|
exp -= text.length - decimalPlace - 1;
|
|
text = text.slice(0, decimalPlace) + text.slice(decimalPlace + 1);
|
|
}
|
|
if (exp < 0) throw new Error("Cannot include negative exponent part for integers");
|
|
text += (new Array(exp + 1)).join("0");
|
|
v = text;
|
|
}
|
|
var isValid = /^([0-9][0-9]*)$/.test(v);
|
|
if (!isValid) throw new Error("Invalid integer: " + v);
|
|
if (supportsNativeBigInt) {
|
|
return new NativeBigInt(BigInt(sign ? "-" + v : v));
|
|
}
|
|
var r = [], max = v.length, l = LOG_BASE, min = max - l;
|
|
while (max > 0) {
|
|
r.push(+v.slice(min, max));
|
|
min -= l;
|
|
if (min < 0) min = 0;
|
|
max -= l;
|
|
}
|
|
trim(r);
|
|
return new BigInteger(r, sign);
|
|
}
|
|
|
|
function parseNumberValue(v) {
|
|
if (supportsNativeBigInt) {
|
|
return new NativeBigInt(BigInt(v));
|
|
}
|
|
if (isPrecise(v)) {
|
|
if (v !== truncate(v)) throw new Error(v + " is not an integer.");
|
|
return new SmallInteger(v);
|
|
}
|
|
return parseStringValue(v.toString());
|
|
}
|
|
|
|
function parseValue(v) {
|
|
if (typeof v === "number") {
|
|
return parseNumberValue(v);
|
|
}
|
|
if (typeof v === "string") {
|
|
return parseStringValue(v);
|
|
}
|
|
if (typeof v === "bigint") {
|
|
return new NativeBigInt(v);
|
|
}
|
|
return v;
|
|
}
|
|
// Pre-define numbers in range [-999,999]
|
|
for (var i = 0; i < 1000; i++) {
|
|
Integer[i] = parseValue(i);
|
|
if (i > 0) Integer[-i] = parseValue(-i);
|
|
}
|
|
// Backwards compatibility
|
|
Integer.one = Integer[1];
|
|
Integer.zero = Integer[0];
|
|
Integer.minusOne = Integer[-1];
|
|
Integer.max = max;
|
|
Integer.min = min;
|
|
Integer.gcd = gcd;
|
|
Integer.lcm = lcm;
|
|
Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger || x instanceof NativeBigInt; };
|
|
Integer.randBetween = randBetween;
|
|
|
|
Integer.fromArray = function (digits, base, isNegative) {
|
|
return parseBaseFromArray(digits.map(parseValue), parseValue(base || 10), isNegative);
|
|
};
|
|
|
|
return Integer;
|
|
})();
|
|
|
|
// Node.js check
|
|
if (module.hasOwnProperty("exports")) {
|
|
module.exports = bigInt;
|
|
}
|
|
} (BigInteger));
|
|
|
|
var BigIntegerExports = BigInteger.exports;
|
|
var bigInt$8 = /*@__PURE__*/getDefaultExportFromCjs(BigIntegerExports);
|
|
|
|
function fromString$1(s, radix) {
|
|
if (typeof s == "string") {
|
|
if (s.slice(0,2) == "0x") {
|
|
return bigInt$8(s.slice(2), 16);
|
|
} else {
|
|
return bigInt$8(s,radix);
|
|
}
|
|
} else {
|
|
return bigInt$8(s, radix);
|
|
}
|
|
}
|
|
|
|
const e$1 = fromString$1;
|
|
|
|
function fromArray$1(a, radix) {
|
|
return bigInt$8.fromArray(a, radix);
|
|
}
|
|
|
|
function bitLength$1(a) {
|
|
return bigInt$8(a).bitLength();
|
|
}
|
|
|
|
function isNegative$1(a) {
|
|
return bigInt$8(a).isNegative();
|
|
}
|
|
|
|
function isZero$1(a) {
|
|
return bigInt$8(a).isZero();
|
|
}
|
|
|
|
function shiftLeft$1(a, n) {
|
|
return bigInt$8(a).shiftLeft(n);
|
|
}
|
|
|
|
function shiftRight$1(a, n) {
|
|
return bigInt$8(a).shiftRight(n);
|
|
}
|
|
|
|
const shl$1 = shiftLeft$1;
|
|
const shr$1 = shiftRight$1;
|
|
|
|
function isOdd$1(a) {
|
|
return bigInt$8(a).isOdd();
|
|
}
|
|
|
|
|
|
function naf$1(n) {
|
|
let E = bigInt$8(n);
|
|
const res = [];
|
|
while (E.gt(bigInt$8.zero)) {
|
|
if (E.isOdd()) {
|
|
const z = 2 - E.mod(4).toJSNumber();
|
|
res.push( z );
|
|
E = E.minus(z);
|
|
} else {
|
|
res.push( 0 );
|
|
}
|
|
E = E.shiftRight(1);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function bits$1(n) {
|
|
let E = bigInt$8(n);
|
|
const res = [];
|
|
while (E.gt(bigInt$8.zero)) {
|
|
if (E.isOdd()) {
|
|
res.push(1);
|
|
} else {
|
|
res.push( 0 );
|
|
}
|
|
E = E.shiftRight(1);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function toNumber$2(s) {
|
|
if (!s.lt(bigInt$8("9007199254740992", 10))) {
|
|
throw new Error("Number too big");
|
|
}
|
|
return s.toJSNumber();
|
|
}
|
|
|
|
function toArray$1(s, radix) {
|
|
return bigInt$8(s).toArray(radix);
|
|
}
|
|
|
|
function add$1(a, b) {
|
|
return bigInt$8(a).add(bigInt$8(b));
|
|
}
|
|
|
|
function sub$1(a, b) {
|
|
return bigInt$8(a).minus(bigInt$8(b));
|
|
}
|
|
|
|
function neg$1(a) {
|
|
return bigInt$8.zero.minus(bigInt$8(a));
|
|
}
|
|
|
|
function mul$1(a, b) {
|
|
return bigInt$8(a).times(bigInt$8(b));
|
|
}
|
|
|
|
function square$1(a) {
|
|
return bigInt$8(a).square();
|
|
}
|
|
|
|
function pow$1(a, b) {
|
|
return bigInt$8(a).pow(bigInt$8(b));
|
|
}
|
|
|
|
function exp$1(a, b) {
|
|
return bigInt$8(a).pow(bigInt$8(b));
|
|
}
|
|
|
|
function abs$1(a) {
|
|
return bigInt$8(a).abs();
|
|
}
|
|
|
|
function div$1(a, b) {
|
|
return bigInt$8(a).divide(bigInt$8(b));
|
|
}
|
|
|
|
function mod$1(a, b) {
|
|
return bigInt$8(a).mod(bigInt$8(b));
|
|
}
|
|
|
|
function eq$1(a, b) {
|
|
return bigInt$8(a).eq(bigInt$8(b));
|
|
}
|
|
|
|
function neq$1(a, b) {
|
|
return bigInt$8(a).neq(bigInt$8(b));
|
|
}
|
|
|
|
function lt$1(a, b) {
|
|
return bigInt$8(a).lt(bigInt$8(b));
|
|
}
|
|
|
|
function gt$1(a, b) {
|
|
return bigInt$8(a).gt(bigInt$8(b));
|
|
}
|
|
|
|
function leq$1(a, b) {
|
|
return bigInt$8(a).leq(bigInt$8(b));
|
|
}
|
|
|
|
function geq$1(a, b) {
|
|
return bigInt$8(a).geq(bigInt$8(b));
|
|
}
|
|
|
|
function band$1(a, b) {
|
|
return bigInt$8(a).and(bigInt$8(b));
|
|
}
|
|
|
|
function bor$1(a, b) {
|
|
return bigInt$8(a).or(bigInt$8(b));
|
|
}
|
|
|
|
function bxor$1(a, b) {
|
|
return bigInt$8(a).xor(bigInt$8(b));
|
|
}
|
|
|
|
function land$1(a, b) {
|
|
return (!bigInt$8(a).isZero()) && (!bigInt$8(b).isZero());
|
|
}
|
|
|
|
function lor$1(a, b) {
|
|
return (!bigInt$8(a).isZero()) || (!bigInt$8(b).isZero());
|
|
}
|
|
|
|
function lnot$1(a) {
|
|
return bigInt$8(a).isZero();
|
|
}
|
|
|
|
var Scalar_bigint = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
abs: abs$1,
|
|
add: add$1,
|
|
band: band$1,
|
|
bitLength: bitLength$1,
|
|
bits: bits$1,
|
|
bor: bor$1,
|
|
bxor: bxor$1,
|
|
div: div$1,
|
|
e: e$1,
|
|
eq: eq$1,
|
|
exp: exp$1,
|
|
fromArray: fromArray$1,
|
|
fromString: fromString$1,
|
|
geq: geq$1,
|
|
gt: gt$1,
|
|
isNegative: isNegative$1,
|
|
isOdd: isOdd$1,
|
|
isZero: isZero$1,
|
|
land: land$1,
|
|
leq: leq$1,
|
|
lnot: lnot$1,
|
|
lor: lor$1,
|
|
lt: lt$1,
|
|
mod: mod$1,
|
|
mul: mul$1,
|
|
naf: naf$1,
|
|
neg: neg$1,
|
|
neq: neq$1,
|
|
pow: pow$1,
|
|
shiftLeft: shiftLeft$1,
|
|
shiftRight: shiftRight$1,
|
|
shl: shl$1,
|
|
shr: shr$1,
|
|
square: square$1,
|
|
sub: sub$1,
|
|
toArray: toArray$1,
|
|
toNumber: toNumber$2
|
|
});
|
|
|
|
const supportsNativeBigInt$1 = typeof BigInt === "function";
|
|
|
|
let Scalar$1 = {};
|
|
if (supportsNativeBigInt$1) {
|
|
Object.assign(Scalar$1, Scalar_native);
|
|
} else {
|
|
Object.assign(Scalar$1, Scalar_bigint);
|
|
}
|
|
|
|
|
|
// Returns a buffer with Little Endian Representation
|
|
Scalar$1.toRprLE = function rprBE(buff, o, e, n8) {
|
|
const s = "0000000" + e.toString(16);
|
|
const v = new Uint32Array(buff.buffer, o, n8/4);
|
|
const l = (((s.length-7)*4 - 1) >> 5)+1; // Number of 32bit words;
|
|
for (let i=0; i<l; i++) v[i] = parseInt(s.substring(s.length-8*i-8, s.length-8*i), 16);
|
|
for (let i=l; i<v.length; i++) v[i] = 0;
|
|
for (let i=v.length*4; i<n8; i++) buff[i] = Scalar$1.toNumber(Scalar$1.band(Scalar$1.shiftRight(e, i*8), 0xFF));
|
|
};
|
|
|
|
// Returns a buffer with Big Endian Representation
|
|
Scalar$1.toRprBE = function rprLEM(buff, o, e, n8) {
|
|
const s = "0000000" + e.toString(16);
|
|
const v = new DataView(buff.buffer, buff.byteOffset + o, n8);
|
|
const l = (((s.length-7)*4 - 1) >> 5)+1; // Number of 32bit words;
|
|
for (let i=0; i<l; i++) v.setUint32(n8-i*4 -4, parseInt(s.substring(s.length-8*i-8, s.length-8*i), 16), false);
|
|
for (let i=0; i<n8/4-l; i++) v[i] = 0;
|
|
};
|
|
|
|
// Pases a buffer with Little Endian Representation
|
|
Scalar$1.fromRprLE = function rprLEM(buff, o, n8) {
|
|
n8 = n8 || buff.byteLength;
|
|
o = o || 0;
|
|
const v = new Uint32Array(buff.buffer, o, n8/4);
|
|
const a = new Array(n8/4);
|
|
v.forEach( (ch,i) => a[a.length-i-1] = ch.toString(16).padStart(8,"0") );
|
|
return Scalar$1.fromString(a.join(""), 16);
|
|
};
|
|
|
|
// Pases a buffer with Big Endian Representation
|
|
Scalar$1.fromRprBE = function rprLEM(buff, o, n8) {
|
|
n8 = n8 || buff.byteLength;
|
|
o = o || 0;
|
|
const v = new DataView(buff.buffer, buff.byteOffset + o, n8);
|
|
const a = new Array(n8/4);
|
|
for (let i=0; i<n8/4; i++) {
|
|
a[i] = v.getUint32(i*4, false).toString(16).padStart(8, "0");
|
|
}
|
|
return Scalar$1.fromString(a.join(""), 16);
|
|
};
|
|
|
|
Scalar$1.toString = function toString(a, radix) {
|
|
return a.toString(radix);
|
|
};
|
|
|
|
Scalar$1.toLEBuff = function toLEBuff(a) {
|
|
const buff = new Uint8Array(Math.floor((Scalar$1.bitLength(a) - 1) / 8) +1);
|
|
Scalar$1.toRprLE(buff, 0, a, buff.byteLength);
|
|
return buff;
|
|
};
|
|
|
|
|
|
Scalar$1.zero = Scalar$1.e(0);
|
|
Scalar$1.one = Scalar$1.e(1);
|
|
|
|
let {
|
|
toRprLE,
|
|
toRprBE,
|
|
fromRprLE,
|
|
fromRprBE,
|
|
toString,
|
|
toLEBuff,
|
|
zero,
|
|
one,
|
|
fromString,
|
|
e,
|
|
fromArray,
|
|
bitLength,
|
|
isNegative,
|
|
isZero,
|
|
shiftLeft,
|
|
shiftRight,
|
|
shl,
|
|
shr,
|
|
isOdd,
|
|
naf,
|
|
bits,
|
|
toNumber: toNumber$1,
|
|
toArray,
|
|
add,
|
|
sub,
|
|
neg,
|
|
mul,
|
|
square,
|
|
pow,
|
|
exp,
|
|
abs,
|
|
div,
|
|
mod,
|
|
eq,
|
|
neq,
|
|
lt,
|
|
gt,
|
|
leq,
|
|
geq,
|
|
band,
|
|
bor,
|
|
bxor,
|
|
land,
|
|
lor,
|
|
lnot,
|
|
} = Scalar$1;
|
|
|
|
var _Scalar = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
abs: abs,
|
|
add: add,
|
|
band: band,
|
|
bitLength: bitLength,
|
|
bits: bits,
|
|
bor: bor,
|
|
bxor: bxor,
|
|
div: div,
|
|
e: e,
|
|
eq: eq,
|
|
exp: exp,
|
|
fromArray: fromArray,
|
|
fromRprBE: fromRprBE,
|
|
fromRprLE: fromRprLE,
|
|
fromString: fromString,
|
|
geq: geq,
|
|
gt: gt,
|
|
isNegative: isNegative,
|
|
isOdd: isOdd,
|
|
isZero: isZero,
|
|
land: land,
|
|
leq: leq,
|
|
lnot: lnot,
|
|
lor: lor,
|
|
lt: lt,
|
|
mod: mod,
|
|
mul: mul,
|
|
naf: naf,
|
|
neg: neg,
|
|
neq: neq,
|
|
one: one,
|
|
pow: pow,
|
|
shiftLeft: shiftLeft,
|
|
shiftRight: shiftRight,
|
|
shl: shl,
|
|
shr: shr,
|
|
square: square,
|
|
sub: sub,
|
|
toArray: toArray,
|
|
toLEBuff: toLEBuff,
|
|
toNumber: toNumber$1,
|
|
toRprBE: toRprBE,
|
|
toRprLE: toRprLE,
|
|
toString: toString,
|
|
zero: zero
|
|
});
|
|
|
|
function quarterRound(st, a, b, c, d) {
|
|
|
|
st[a] = (st[a] + st[b]) >>> 0;
|
|
st[d] = (st[d] ^ st[a]) >>> 0;
|
|
st[d] = ((st[d] << 16) | ((st[d]>>>16) & 0xFFFF)) >>> 0;
|
|
|
|
st[c] = (st[c] + st[d]) >>> 0;
|
|
st[b] = (st[b] ^ st[c]) >>> 0;
|
|
st[b] = ((st[b] << 12) | ((st[b]>>>20) & 0xFFF)) >>> 0;
|
|
|
|
st[a] = (st[a] + st[b]) >>> 0;
|
|
st[d] = (st[d] ^ st[a]) >>> 0;
|
|
st[d] = ((st[d] << 8) | ((st[d]>>>24) & 0xFF)) >>> 0;
|
|
|
|
st[c] = (st[c] + st[d]) >>> 0;
|
|
st[b] = (st[b] ^ st[c]) >>> 0;
|
|
st[b] = ((st[b] << 7) | ((st[b]>>>25) & 0x7F)) >>> 0;
|
|
}
|
|
|
|
function doubleRound(st) {
|
|
quarterRound(st, 0, 4, 8,12);
|
|
quarterRound(st, 1, 5, 9,13);
|
|
quarterRound(st, 2, 6,10,14);
|
|
quarterRound(st, 3, 7,11,15);
|
|
|
|
quarterRound(st, 0, 5,10,15);
|
|
quarterRound(st, 1, 6,11,12);
|
|
quarterRound(st, 2, 7, 8,13);
|
|
quarterRound(st, 3, 4, 9,14);
|
|
}
|
|
|
|
class ChaCha {
|
|
|
|
constructor(seed) {
|
|
seed = seed || [0,0,0,0,0,0,0,0];
|
|
this.state = [
|
|
0x61707865,
|
|
0x3320646E,
|
|
0x79622D32,
|
|
0x6B206574,
|
|
seed[0],
|
|
seed[1],
|
|
seed[2],
|
|
seed[3],
|
|
seed[4],
|
|
seed[5],
|
|
seed[6],
|
|
seed[7],
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
];
|
|
this.idx = 16;
|
|
this.buff = new Array(16);
|
|
}
|
|
|
|
nextU32() {
|
|
if (this.idx == 16) this.update();
|
|
return this.buff[this.idx++];
|
|
}
|
|
|
|
nextU64() {
|
|
return add(mul(this.nextU32(), 0x100000000), this.nextU32());
|
|
}
|
|
|
|
nextBool() {
|
|
return (this.nextU32() & 1) == 1;
|
|
}
|
|
|
|
update() {
|
|
// Copy the state
|
|
for (let i=0; i<16; i++) this.buff[i] = this.state[i];
|
|
|
|
// Apply the rounds
|
|
for (let i=0; i<10; i++) doubleRound(this.buff);
|
|
|
|
// Add to the initial
|
|
for (let i=0; i<16; i++) this.buff[i] = (this.buff[i] + this.state[i]) >>> 0;
|
|
|
|
this.idx = 0;
|
|
|
|
this.state[12] = (this.state[12] + 1) >>> 0;
|
|
if (this.state[12] != 0) return;
|
|
this.state[13] = (this.state[13] + 1) >>> 0;
|
|
if (this.state[13] != 0) return;
|
|
this.state[14] = (this.state[14] + 1) >>> 0;
|
|
if (this.state[14] != 0) return;
|
|
this.state[15] = (this.state[15] + 1) >>> 0;
|
|
}
|
|
}
|
|
|
|
function getRandomBytes(n) {
|
|
let array = new Uint8Array(n);
|
|
if (process.browser) { // Browser
|
|
if (typeof globalThis.crypto !== "undefined") { // Supported
|
|
globalThis.crypto.getRandomValues(array);
|
|
} else { // fallback
|
|
for (let i=0; i<n; i++) {
|
|
array[i] = (Math.random()*4294967296)>>>0;
|
|
}
|
|
}
|
|
}
|
|
else { // NodeJS
|
|
crypto.randomFillSync(array);
|
|
}
|
|
return array;
|
|
}
|
|
|
|
function getRandomSeed() {
|
|
const arr = getRandomBytes(32);
|
|
const arrV = new Uint32Array(arr.buffer);
|
|
const seed = [];
|
|
for (let i=0; i<8; i++) {
|
|
seed.push(arrV[i]);
|
|
}
|
|
return seed;
|
|
}
|
|
|
|
let threadRng = null;
|
|
|
|
function getThreadRng() {
|
|
if (threadRng) return threadRng;
|
|
threadRng = new ChaCha(getRandomSeed());
|
|
return threadRng;
|
|
}
|
|
|
|
var utils$b = {};
|
|
|
|
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const bigInt$7 = BigIntegerExports;
|
|
|
|
utils$b.bigInt2BytesLE = function bigInt2BytesLE(_a, len) {
|
|
const b = Array(len);
|
|
let v = bigInt$7(_a);
|
|
for (let i=0; i<len; i++) {
|
|
b[i] = v.and(0xFF).toJSNumber();
|
|
v = v.shiftRight(8);
|
|
}
|
|
return b;
|
|
};
|
|
|
|
utils$b.bigInt2U32LE = function bigInt2BytesLE(_a, len) {
|
|
const b = Array(len);
|
|
let v = bigInt$7(_a);
|
|
for (let i=0; i<len; i++) {
|
|
b[i] = v.and(0xFFFFFFFF).toJSNumber();
|
|
v = v.shiftRight(32);
|
|
}
|
|
return b;
|
|
};
|
|
|
|
utils$b.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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const utils$a = utils$b;
|
|
|
|
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;
|
|
|
|
module.alloc(n8, utils$a.bigInt2BytesLE(1, n8));
|
|
|
|
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<n64; i++) {
|
|
f.addCode(
|
|
c.i64_store(
|
|
c.getLocal("pr"),
|
|
i*8,
|
|
c.i64_load(
|
|
c.getLocal("px"),
|
|
i*8
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
function buildZero() {
|
|
const f = module.addFunction(prefix+"_zero");
|
|
f.addParam("pr", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
for (let i=0; i<n64; i++) {
|
|
f.addCode(
|
|
c.i64_store(
|
|
c.getLocal("pr"),
|
|
i*8,
|
|
c.i64_const(0)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
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<n64; i++) {
|
|
f.addCode(
|
|
c.i64_store(
|
|
c.getLocal("pr"),
|
|
i*8,
|
|
c.i64_const(0)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
function buildIsZero() {
|
|
const f = module.addFunction(prefix+"_isZero");
|
|
f.addParam("px", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
function getCompCode(n) {
|
|
if (n==0) {
|
|
return c.ret(c.i64_eqz(
|
|
c.i64_load(c.getLocal("px"))
|
|
));
|
|
}
|
|
return c.if(
|
|
c.i64_eqz(
|
|
c.i64_load(c.getLocal("px"), n*8 )
|
|
),
|
|
getCompCode(n-1),
|
|
c.ret(c.i32_const(0))
|
|
);
|
|
}
|
|
|
|
f.addCode(getCompCode(n64-1));
|
|
f.addCode(c.ret(c.i32_const(0)));
|
|
}
|
|
|
|
function buildEq() {
|
|
const f = module.addFunction(prefix+"_eq");
|
|
f.addParam("px", "i32");
|
|
f.addParam("py", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
function getCompCode(n) {
|
|
if (n==0) {
|
|
return c.ret(c.i64_eq(
|
|
c.i64_load(c.getLocal("px")),
|
|
c.i64_load(c.getLocal("py"))
|
|
));
|
|
}
|
|
return c.if(
|
|
c.i64_eq(
|
|
c.i64_load(c.getLocal("px"), n*8 ),
|
|
c.i64_load(c.getLocal("py"), n*8 )
|
|
),
|
|
getCompCode(n-1),
|
|
c.ret(c.i32_const(0))
|
|
);
|
|
}
|
|
|
|
f.addCode(getCompCode(n64-1));
|
|
f.addCode(c.ret(c.i32_const(0)));
|
|
}
|
|
|
|
|
|
|
|
function buildGte() {
|
|
const f = module.addFunction(prefix+"_gte");
|
|
f.addParam("px", "i32");
|
|
f.addParam("py", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
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)
|
|
)
|
|
);
|
|
}
|
|
|
|
f.addCode(getCompCode(n64-1));
|
|
f.addCode(c.ret(c.i32_const(0)));
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(c.setLocal(
|
|
"c",
|
|
c.i64_add(
|
|
c.i64_load32_u(c.getLocal("x")),
|
|
c.i64_load32_u(c.getLocal("y"))
|
|
)
|
|
));
|
|
|
|
f.addCode(c.i64_store32(
|
|
c.getLocal("r"),
|
|
c.getLocal("c"),
|
|
));
|
|
|
|
for (let i=1; i<n32; i++) {
|
|
f.addCode(c.setLocal( "c",
|
|
c.i64_add(
|
|
c.i64_add(
|
|
c.i64_load32_u(c.getLocal("x"), 4*i),
|
|
c.i64_load32_u(c.getLocal("y"), 4*i)
|
|
),
|
|
c.i64_shr_u (c.getLocal("c"), c.i64_const(32))
|
|
)
|
|
));
|
|
|
|
f.addCode(c.i64_store32(
|
|
c.getLocal("r"),
|
|
i*4,
|
|
c.getLocal("c")
|
|
));
|
|
}
|
|
|
|
f.addCode(c.i32_wrap_i64(c.i64_shr_u (c.getLocal("c"), c.i64_const(32))));
|
|
}
|
|
|
|
|
|
function buildSub() {
|
|
|
|
const f = module.addFunction(prefix+"_sub");
|
|
f.addParam("x", "i32");
|
|
f.addParam("y", "i32");
|
|
f.addParam("r", "i32");
|
|
f.setReturnType("i32");
|
|
f.addLocal("c", "i64");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(c.setLocal(
|
|
"c",
|
|
c.i64_sub(
|
|
c.i64_load32_u(c.getLocal("x")),
|
|
c.i64_load32_u(c.getLocal("y"))
|
|
)
|
|
));
|
|
|
|
f.addCode(c.i64_store32(
|
|
c.getLocal("r"),
|
|
c.i64_and(
|
|
c.getLocal("c"),
|
|
c.i64_const("0xFFFFFFFF")
|
|
)
|
|
));
|
|
|
|
for (let i=1; i<n32; i++) {
|
|
f.addCode(c.setLocal( "c",
|
|
c.i64_add(
|
|
c.i64_sub(
|
|
c.i64_load32_u(c.getLocal("x"), 4*i),
|
|
c.i64_load32_u(c.getLocal("y"), 4*i)
|
|
),
|
|
c.i64_shr_s (c.getLocal("c"), c.i64_const(32))
|
|
)
|
|
));
|
|
|
|
f.addCode(c.i64_store32(
|
|
c.getLocal("r"),
|
|
i*4,
|
|
c.i64_and( c.getLocal("c"), c.i64_const("0xFFFFFFFF"))
|
|
));
|
|
}
|
|
|
|
f.addCode(c.i32_wrap_i64 ( c.i64_shr_s (c.getLocal("c"), c.i64_const(32))));
|
|
}
|
|
|
|
|
|
function buildMul() {
|
|
|
|
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");
|
|
|
|
|
|
for (let i=0;i<n32; i++) {
|
|
f.addLocal("x"+i, "i64");
|
|
f.addLocal("y"+i, "i64");
|
|
}
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const loadX = [];
|
|
const loadY = [];
|
|
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 (!loadY[j]) {
|
|
Y = c.teeLocal("y"+j, c.i64_load32_u( c.getLocal("y"), j*4));
|
|
loadY[j] = true;
|
|
} else {
|
|
Y = c.getLocal("y"+j);
|
|
}
|
|
|
|
return c.i64_mul( X, Y );
|
|
}
|
|
|
|
let c0 = "c0";
|
|
let c1 = "c1";
|
|
|
|
for (let k=0; k<n32*2-1; k++) {
|
|
for (let i=Math.max(0, k-n32+1); (i<=k)&&(i<n32); i++) {
|
|
const j= k-i;
|
|
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_add(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
mulij(i,j)
|
|
)
|
|
)
|
|
);
|
|
|
|
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.i64_store32(
|
|
c.getLocal("r"),
|
|
k*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.i64_store32(
|
|
c.getLocal("r"),
|
|
n32*4*2-4,
|
|
c.getLocal(c0)
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function buildSquare() {
|
|
|
|
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");
|
|
|
|
|
|
for (let i=0;i<n32; i++) {
|
|
f.addLocal("x"+i, "i64");
|
|
}
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const loadX = [];
|
|
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);
|
|
}
|
|
|
|
return c.i64_mul( X, Y );
|
|
}
|
|
|
|
let c0 = "c0";
|
|
let c1 = "c1";
|
|
let c0_old = "c0_old";
|
|
let c1_old = "c1_old";
|
|
|
|
for (let k=0; k<n32*2-1; k++) {
|
|
f.addCode(
|
|
c.setLocal(c0, c.i64_const(0)),
|
|
c.setLocal(c1, c.i64_const(0)),
|
|
);
|
|
|
|
for (let i=Math.max(0, k-n32+1); (i<((k+1)>>1) )&&(i<n32); i++) {
|
|
const j= k-i;
|
|
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_add(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
mulij(i,j)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
c.setLocal(c1,
|
|
c.i64_add(
|
|
c.getLocal(c1),
|
|
c.i64_shr_u(
|
|
c.getLocal(c0),
|
|
c.i64_const(32)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
// Multiply by 2
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_shl(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
c.i64_const(1)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
c.setLocal(c1,
|
|
c.i64_add(
|
|
c.i64_shl(
|
|
c.getLocal(c1),
|
|
c.i64_const(1)
|
|
),
|
|
c.i64_shr_u(
|
|
c.getLocal(c0),
|
|
c.i64_const(32)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
if (k%2 == 0) {
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_add(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
mulij(k>>1, k>>1)
|
|
)
|
|
)
|
|
);
|
|
|
|
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)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
f.addCode(
|
|
c.i64_store32(
|
|
c.getLocal("r"),
|
|
k*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*2-4,
|
|
c.getLocal(c0_old)
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
|
|
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 _buildMul1() {
|
|
const f = module.addFunction(prefix+"__mul1");
|
|
f.addParam("px", "i32");
|
|
f.addParam("y", "i64");
|
|
f.addParam("pr", "i32");
|
|
f.addLocal("c", "i64");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(c.setLocal(
|
|
"c",
|
|
c.i64_mul(
|
|
c.i64_load32_u(c.getLocal("px"), 0, 0),
|
|
c.getLocal("y")
|
|
)
|
|
));
|
|
|
|
f.addCode(c.i64_store32(
|
|
c.getLocal("pr"),
|
|
0,
|
|
0,
|
|
c.getLocal("c"),
|
|
));
|
|
|
|
for (let i=1; i<n32; i++) {
|
|
f.addCode(c.setLocal( "c",
|
|
c.i64_add(
|
|
c.i64_mul(
|
|
c.i64_load32_u(c.getLocal("px"), 4*i, 0),
|
|
c.getLocal("y")
|
|
),
|
|
c.i64_shr_u (c.getLocal("c"), c.i64_const(32))
|
|
)
|
|
));
|
|
|
|
f.addCode(c.i64_store32(
|
|
c.getLocal("pr"),
|
|
i*4,
|
|
0,
|
|
c.getLocal("c")
|
|
));
|
|
}
|
|
}
|
|
|
|
function _buildAdd1() {
|
|
const f = module.addFunction(prefix+"__add1");
|
|
f.addParam("x", "i32");
|
|
f.addParam("y", "i64");
|
|
f.addLocal("c", "i64");
|
|
f.addLocal("px", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(c.setLocal("px", c.getLocal("x")));
|
|
|
|
f.addCode(c.setLocal(
|
|
"c",
|
|
c.i64_add(
|
|
c.i64_load32_u(c.getLocal("px"), 0, 0),
|
|
c.getLocal("y")
|
|
)
|
|
));
|
|
|
|
f.addCode(c.i64_store32(
|
|
c.getLocal("px"),
|
|
0,
|
|
0,
|
|
c.getLocal("c"),
|
|
));
|
|
|
|
f.addCode(c.setLocal(
|
|
"c",
|
|
c.i64_shr_u(
|
|
c.getLocal("c"),
|
|
c.i64_const(32)
|
|
)
|
|
));
|
|
|
|
f.addCode(c.block(c.loop(
|
|
c.br_if(
|
|
1,
|
|
c.i64_eqz(c.getLocal("c"))
|
|
),
|
|
c.setLocal(
|
|
"px",
|
|
c.i32_add(
|
|
c.getLocal("px"),
|
|
c.i32_const(4)
|
|
)
|
|
),
|
|
|
|
c.setLocal(
|
|
"c",
|
|
c.i64_add(
|
|
c.i64_load32_u(c.getLocal("px"), 0, 0),
|
|
c.getLocal("c")
|
|
)
|
|
),
|
|
|
|
c.i64_store32(
|
|
c.getLocal("px"),
|
|
0,
|
|
0,
|
|
c.getLocal("c"),
|
|
),
|
|
|
|
c.setLocal(
|
|
"c",
|
|
c.i64_shr_u(
|
|
c.getLocal("c"),
|
|
c.i64_const(32)
|
|
)
|
|
),
|
|
|
|
c.br(0)
|
|
)));
|
|
}
|
|
|
|
|
|
function buildDiv() {
|
|
_buildMul1();
|
|
_buildAdd1();
|
|
|
|
const f = module.addFunction(prefix+"_div");
|
|
f.addParam("x", "i32");
|
|
f.addParam("y", "i32");
|
|
f.addParam("c", "i32");
|
|
f.addParam("r", "i32");
|
|
f.addLocal("rr", "i32");
|
|
f.addLocal("cc", "i32");
|
|
f.addLocal("eX", "i32");
|
|
f.addLocal("eY", "i32");
|
|
f.addLocal("sy", "i64");
|
|
f.addLocal("sx", "i64");
|
|
f.addLocal("ec", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const Y = c.i32_const(module.alloc(n8));
|
|
const Caux = c.i32_const(module.alloc(n8));
|
|
const Raux = c.i32_const(module.alloc(n8));
|
|
const C = c.getLocal("cc");
|
|
const R = c.getLocal("rr");
|
|
const pr1 = module.alloc(n8*2);
|
|
const R1 = c.i32_const(pr1);
|
|
const R2 = c.i32_const(pr1+n8);
|
|
|
|
// Ic c is 0 then store it in an auxiliary buffer
|
|
f.addCode(c.if(
|
|
c.getLocal("c"),
|
|
c.setLocal("cc", c.getLocal("c")),
|
|
c.setLocal("cc", Caux)
|
|
));
|
|
|
|
// Ic r is 0 then store it in an auxiliary buffer
|
|
f.addCode(c.if(
|
|
c.getLocal("r"),
|
|
c.setLocal("rr", c.getLocal("r")),
|
|
c.setLocal("rr", Raux)
|
|
));
|
|
|
|
// Copy
|
|
f.addCode(c.call(prefix + "_copy", c.getLocal("x"), R));
|
|
f.addCode(c.call(prefix + "_copy", c.getLocal("y"), Y));
|
|
f.addCode(c.call(prefix + "_zero", C));
|
|
f.addCode(c.call(prefix + "_zero", R1));
|
|
|
|
|
|
f.addCode(c.setLocal("eX", c.i32_const(n8-1)));
|
|
f.addCode(c.setLocal("eY", c.i32_const(n8-1)));
|
|
|
|
// while (eY>3)&&(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)
|
|
)));
|
|
|
|
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)
|
|
)
|
|
)
|
|
);
|
|
|
|
// 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(
|
|
|
|
// 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)
|
|
)),
|
|
|
|
c.setLocal(
|
|
"sx",
|
|
c.i64_load(
|
|
c.i32_sub(
|
|
c.i32_add( R, c.getLocal("eX")),
|
|
c.i32_const(7)
|
|
),
|
|
0,
|
|
0
|
|
)
|
|
),
|
|
|
|
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)
|
|
)
|
|
),
|
|
|
|
// 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)
|
|
)
|
|
)
|
|
),
|
|
|
|
c.setLocal(
|
|
"sx",
|
|
c.i64_shr_u(
|
|
c.getLocal("sx"),
|
|
c.i64_const(8)
|
|
)
|
|
),
|
|
|
|
c.setLocal(
|
|
"ec",
|
|
c.i32_add(
|
|
c.getLocal("ec"),
|
|
c.i32_const(1)
|
|
)
|
|
),
|
|
c.br(0)
|
|
)),
|
|
|
|
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.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 buildInverseMod() {
|
|
|
|
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");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
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.setLocal("t", aux1),
|
|
c.call(prefix + "_zero", aux1),
|
|
c.setLocal("signt", c.i32_const(0)),
|
|
);
|
|
|
|
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)
|
|
);
|
|
|
|
|
|
|
|
|
|
f.addCode(c.setLocal("qq", aux5));
|
|
f.addCode(c.setLocal("qr", aux6));
|
|
f.addCode(c.setLocal("x", aux7));
|
|
|
|
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")),
|
|
|
|
c.call(prefix + "_mul", c.getLocal("qq"), c.getLocal("newt"), mulBuff),
|
|
|
|
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))
|
|
]
|
|
)
|
|
)
|
|
),
|
|
|
|
c.setLocal("swp", c.getLocal("t")),
|
|
c.setLocal("t", c.getLocal("newt")),
|
|
c.setLocal("newt", c.getLocal("x")),
|
|
c.setLocal("x", c.getLocal("swp")),
|
|
|
|
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)
|
|
)));
|
|
|
|
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"))
|
|
));
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
var build_timesscalar = function buildTimesScalar(module, fnName, elementLen, opAB, opAA, 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("i", "i32");
|
|
f.addLocal("b", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const aux = c.i32_const(module.alloc(elementLen));
|
|
|
|
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)
|
|
)));
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
};
|
|
|
|
var build_batchinverse = buildBatchInverse$3;
|
|
|
|
function buildBatchInverse$3(module, prefix) {
|
|
|
|
|
|
const n8 = module.modules[prefix].n64*8;
|
|
|
|
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");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const AUX = c.i32_const(module.alloc(n8));
|
|
|
|
|
|
// 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)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
|
|
// aux[0] = a;
|
|
c.call(prefix+"_one", c.getLocal("itAux")),
|
|
// for (i=0;i<n;i++) aux[i] = aux[i-1]*in[i]
|
|
c.setLocal("itIn", c.getLocal("pIn")),
|
|
c.setLocal("itAux", c.i32_add(c.getLocal("itAux"), c.i32_const(n8))),
|
|
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.if(
|
|
c.call(prefix+"_isZero", c.getLocal("itIn")),
|
|
c.call(
|
|
prefix + "_copy",
|
|
c.i32_sub(c.getLocal("itAux"), c.i32_const(n8)),
|
|
c.getLocal("itAux")
|
|
),
|
|
c.call(
|
|
prefix+"_mul",
|
|
c.getLocal("itIn"),
|
|
c.i32_sub(c.getLocal("itAux"), c.i32_const(n8)),
|
|
c.getLocal("itAux")
|
|
)
|
|
),
|
|
c.setLocal("itIn", c.i32_add(c.getLocal("itIn"), c.getLocal("inStep"))),
|
|
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)
|
|
)),
|
|
|
|
// point to the last
|
|
c.setLocal("itIn", c.i32_sub(c.getLocal("itIn"), c.getLocal("inStep"))),
|
|
c.setLocal("itAux", c.i32_sub(c.getLocal("itAux"), c.i32_const(n8))),
|
|
// itOut = pOut + (n-1)*stepOut // Point to the last
|
|
c.setLocal(
|
|
"itOut",
|
|
c.i32_add(
|
|
c.getLocal("pOut"),
|
|
c.i32_mul(
|
|
c.i32_sub(c.getLocal("n"), c.i32_const(1)),
|
|
c.getLocal("outStep"),
|
|
)
|
|
)
|
|
),
|
|
|
|
// aux[n-1] = 1/aux[n-1]
|
|
c.call(prefix+"_inverse", c.getLocal("itAux"), c.getLocal("itAux") ),
|
|
|
|
c.block(c.loop(
|
|
c.br_if(1, c.i32_eqz( c.getLocal("i"))),
|
|
c.if(
|
|
c.call(prefix+"_isZero", c.getLocal("itIn")),
|
|
[
|
|
...c.call(
|
|
prefix + "_copy",
|
|
c.getLocal("itAux"),
|
|
c.i32_sub(c.getLocal("itAux"), c.i32_const(n8)),
|
|
),
|
|
...c.call(
|
|
prefix + "_zero",
|
|
c.getLocal("itOut")
|
|
)
|
|
],[
|
|
...c.call(prefix + "_copy", c.i32_sub(c.getLocal("itAux"), c.i32_const(n8)), AUX),
|
|
...c.call(
|
|
prefix+"_mul",
|
|
c.getLocal("itAux"),
|
|
c.getLocal("itIn"),
|
|
c.i32_sub(c.getLocal("itAux"), c.i32_const(n8)),
|
|
),
|
|
...c.call(
|
|
prefix+"_mul",
|
|
c.getLocal("itAux"),
|
|
AUX,
|
|
c.getLocal("itOut")
|
|
)
|
|
]
|
|
),
|
|
c.setLocal("itIn", c.i32_sub(c.getLocal("itIn"), c.getLocal("inStep"))),
|
|
c.setLocal("itOut", c.i32_sub(c.getLocal("itOut"), c.getLocal("outStep"))),
|
|
c.setLocal("itAux", c.i32_sub(c.getLocal("itAux"), c.i32_const(n8))),
|
|
c.setLocal("i", c.i32_sub(c.getLocal("i"), c.i32_const(1))),
|
|
c.br(0)
|
|
))
|
|
|
|
);
|
|
|
|
|
|
// Recover Old memory
|
|
f.addCode(
|
|
c.i32_store(
|
|
c.i32_const(0),
|
|
c.getLocal("itAux")
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
var build_batchconvertion = buildBatchConvertion$3;
|
|
|
|
function buildBatchConvertion$3(module, fnName, internalFnName, sizeIn, sizeOut, reverse) {
|
|
if (typeof reverse === "undefined") {
|
|
// Set the reverse in a way that allows to use the same buffer as in/out.
|
|
if (sizeIn < sizeOut) {
|
|
reverse = true;
|
|
} else {
|
|
reverse = false;
|
|
}
|
|
}
|
|
|
|
const f = module.addFunction(fnName);
|
|
f.addParam("pIn", "i32");
|
|
f.addParam("n", "i32");
|
|
f.addParam("pOut", "i32");
|
|
f.addLocal("i", "i32");
|
|
f.addLocal("itIn", "i32");
|
|
f.addLocal("itOut", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
if (reverse) {
|
|
f.addCode(
|
|
c.setLocal("itIn",
|
|
c.i32_add(
|
|
c.getLocal("pIn"),
|
|
c.i32_mul(
|
|
c.i32_sub(
|
|
c.getLocal("n"),
|
|
c.i32_const(1)
|
|
),
|
|
c.i32_const(sizeIn)
|
|
)
|
|
)
|
|
),
|
|
c.setLocal("itOut",
|
|
c.i32_add(
|
|
c.getLocal("pOut"),
|
|
c.i32_mul(
|
|
c.i32_sub(
|
|
c.getLocal("n"),
|
|
c.i32_const(1)
|
|
),
|
|
c.i32_const(sizeOut)
|
|
)
|
|
)
|
|
),
|
|
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(internalFnName, c.getLocal("itIn"), c.getLocal("itOut")),
|
|
|
|
c.setLocal("itIn", c.i32_sub(c.getLocal("itIn"), c.i32_const(sizeIn))),
|
|
c.setLocal("itOut", c.i32_sub(c.getLocal("itOut"), c.i32_const(sizeOut))),
|
|
c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))),
|
|
c.br(0)
|
|
)),
|
|
);
|
|
} else {
|
|
f.addCode(
|
|
c.setLocal("itIn", c.getLocal("pIn")),
|
|
c.setLocal("itOut", c.getLocal("pOut")),
|
|
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(internalFnName, c.getLocal("itIn"), c.getLocal("itOut")),
|
|
|
|
c.setLocal("itIn", c.i32_add(c.getLocal("itIn"), c.i32_const(sizeIn))),
|
|
c.setLocal("itOut", c.i32_add(c.getLocal("itOut"), c.i32_const(sizeOut))),
|
|
c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))),
|
|
c.br(0)
|
|
)),
|
|
);
|
|
}
|
|
}
|
|
|
|
var build_batchop = buildBatchConvertion$2;
|
|
|
|
function buildBatchConvertion$2(module, fnName, internalFnName, sizeIn, sizeOut, reverse) {
|
|
if (typeof reverse === "undefined") {
|
|
// Set the reverse in a way that allows to use the same buffer as in/out.
|
|
if (sizeIn < sizeOut) {
|
|
reverse = true;
|
|
} else {
|
|
reverse = false;
|
|
}
|
|
}
|
|
|
|
const f = module.addFunction(fnName);
|
|
f.addParam("pIn1", "i32");
|
|
f.addParam("pIn2", "i32");
|
|
f.addParam("n", "i32");
|
|
f.addParam("pOut", "i32");
|
|
f.addLocal("i", "i32");
|
|
f.addLocal("itIn1", "i32");
|
|
f.addLocal("itIn2", "i32");
|
|
f.addLocal("itOut", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
if (reverse) {
|
|
f.addCode(
|
|
c.setLocal("itIn1",
|
|
c.i32_add(
|
|
c.getLocal("pIn1"),
|
|
c.i32_mul(
|
|
c.i32_sub(
|
|
c.getLocal("n"),
|
|
c.i32_const(1)
|
|
),
|
|
c.i32_const(sizeIn)
|
|
)
|
|
)
|
|
),
|
|
c.setLocal("itIn2",
|
|
c.i32_add(
|
|
c.getLocal("pIn2"),
|
|
c.i32_mul(
|
|
c.i32_sub(
|
|
c.getLocal("n"),
|
|
c.i32_const(1)
|
|
),
|
|
c.i32_const(sizeIn)
|
|
)
|
|
)
|
|
),
|
|
c.setLocal("itOut",
|
|
c.i32_add(
|
|
c.getLocal("pOut"),
|
|
c.i32_mul(
|
|
c.i32_sub(
|
|
c.getLocal("n"),
|
|
c.i32_const(1)
|
|
),
|
|
c.i32_const(sizeOut)
|
|
)
|
|
)
|
|
),
|
|
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(internalFnName, c.getLocal("itIn1"), c.getLocal("itIn2"), c.getLocal("itOut")),
|
|
|
|
c.setLocal("itIn1", c.i32_sub(c.getLocal("itIn1"), c.i32_const(sizeIn))),
|
|
c.setLocal("itIn2", c.i32_sub(c.getLocal("itIn2"), c.i32_const(sizeIn))),
|
|
c.setLocal("itOut", c.i32_sub(c.getLocal("itOut"), c.i32_const(sizeOut))),
|
|
c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))),
|
|
c.br(0)
|
|
)),
|
|
);
|
|
} else {
|
|
f.addCode(
|
|
c.setLocal("itIn1", c.getLocal("pIn1")),
|
|
c.setLocal("itIn2", c.getLocal("pIn2")),
|
|
c.setLocal("itOut", c.getLocal("pOut")),
|
|
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(internalFnName, c.getLocal("itIn1"), c.getLocal("itIn2"), c.getLocal("itOut")),
|
|
|
|
c.setLocal("itIn1", c.i32_add(c.getLocal("itIn1"), c.i32_const(sizeIn))),
|
|
c.setLocal("itIn2", c.i32_add(c.getLocal("itIn2"), c.i32_const(sizeIn))),
|
|
c.setLocal("itOut", c.i32_add(c.getLocal("itOut"), c.i32_const(sizeOut))),
|
|
c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))),
|
|
c.br(0)
|
|
)),
|
|
);
|
|
}
|
|
}
|
|
|
|
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const bigInt$6 = BigIntegerExports;
|
|
const buildInt = build_int;
|
|
const utils$9 = utils$b;
|
|
const buildExp$2 = build_timesscalar;
|
|
const buildBatchInverse$2 = build_batchinverse;
|
|
const buildBatchConvertion$1 = build_batchconvertion;
|
|
const buildBatchOp = build_batchop;
|
|
|
|
var build_f1m = function buildF1m(module, _q, _prefix, _intPrefix) {
|
|
const q = bigInt$6(_q);
|
|
const n64 = Math.floor((q.minus(1).bitLength() - 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$9.bigInt2BytesLE(q, n8));
|
|
|
|
module.alloc(utils$9.bigInt2BytesLE(bigInt$6.one.shiftLeft(n64*64).mod(q), n8));
|
|
const pR2 = module.alloc(utils$9.bigInt2BytesLE(bigInt$6.one.shiftLeft(n64*64).square().mod(q), n8));
|
|
const pOne = module.alloc(utils$9.bigInt2BytesLE(bigInt$6.one.shiftLeft(n64*64).mod(q), n8));
|
|
const pZero = module.alloc(utils$9.bigInt2BytesLE(bigInt$6.zero, n8));
|
|
const _minusOne = q.minus(bigInt$6.one);
|
|
const _e = _minusOne.shiftRight(1); // e = (p-1)/2
|
|
const pe = module.alloc(n8, utils$9.bigInt2BytesLE(_e, n8));
|
|
|
|
const _ePlusOne = _e.add(bigInt$6.one); // e = (p-1)/2
|
|
const pePlusOne = module.alloc(n8, utils$9.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(intPrefix + "_copy", c.i32_const(pOne), c.getLocal("pr")));
|
|
}
|
|
|
|
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.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 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.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 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 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));
|
|
|
|
f.addCode(
|
|
c.call(prefix + "_fromMontgomery", c.getLocal("x"), AUX),
|
|
c.call(intPrefix + "_gte", AUX, c.i32_const(pePlusOne) )
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
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));
|
|
|
|
f.addCode(
|
|
c.call(prefix + "_fromMontgomery", c.getLocal("x"), AUX),
|
|
c.i32_and(
|
|
c.i32_load(AUX),
|
|
c.i32_const(1)
|
|
)
|
|
);
|
|
}
|
|
*/
|
|
|
|
function buildSign() {
|
|
const f = module.addFunction(prefix+"_sign");
|
|
f.addParam("x", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const AUX = c.i32_const(module.alloc(n8));
|
|
|
|
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 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();
|
|
|
|
const np32 = bigInt$6("100000000",16).minus( q.modInv(bigInt$6("100000000",16))).toJSNumber();
|
|
|
|
f.addCode(c.setLocal("np32", c.i64_const(np32)));
|
|
|
|
for (let i=0; i<n32; i++) {
|
|
f.addCode(c.setLocal("c", c.i64_const(0)));
|
|
|
|
f.addCode(
|
|
c.setLocal(
|
|
"m",
|
|
c.i64_and(
|
|
c.i64_mul(
|
|
c.i64_load32_u(c.getLocal("t"), i*4),
|
|
c.getLocal("np32")
|
|
),
|
|
c.i64_const("0xFFFFFFFF")
|
|
)
|
|
)
|
|
);
|
|
|
|
for (let j=0; j<n32; j++) {
|
|
|
|
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")
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
c.i64_store32(
|
|
c.getLocal("t"),
|
|
(i+j)*4,
|
|
c.getLocal("c")
|
|
)
|
|
);
|
|
}
|
|
|
|
f.addCode(
|
|
c.i64_store32(
|
|
c.i32_const(carries),
|
|
i*4,
|
|
c.i64_shr_u(c.getLocal("c"), c.i64_const(32))
|
|
)
|
|
);
|
|
}
|
|
|
|
f.addCode(
|
|
c.call(
|
|
prefix+"_add",
|
|
c.i32_const(carries),
|
|
c.i32_add(
|
|
c.getLocal("t"),
|
|
c.i32_const(n32*4)
|
|
),
|
|
c.getLocal("r")
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
|
|
function buildMul() {
|
|
|
|
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");
|
|
|
|
|
|
for (let i=0;i<n32; i++) {
|
|
f.addLocal("x"+i, "i64");
|
|
f.addLocal("y"+i, "i64");
|
|
f.addLocal("m"+i, "i64");
|
|
f.addLocal("q"+i, "i64");
|
|
}
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const np32 = bigInt$6("100000000",16).minus( q.modInv(bigInt$6("100000000",16))).toJSNumber();
|
|
|
|
f.addCode(c.setLocal("np32", c.i64_const(np32)));
|
|
|
|
|
|
const loadX = [];
|
|
const loadY = [];
|
|
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 (!loadY[j]) {
|
|
Y = c.teeLocal("y"+j, c.i64_load32_u( c.getLocal("y"), j*4));
|
|
loadY[j] = true;
|
|
} else {
|
|
Y = c.getLocal("y"+j);
|
|
}
|
|
|
|
return c.i64_mul( X, Y );
|
|
}
|
|
|
|
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);
|
|
}
|
|
M = c.getLocal("m"+j);
|
|
|
|
return c.i64_mul( Q, M );
|
|
}
|
|
|
|
|
|
let c0 = "c0";
|
|
let c1 = "c1";
|
|
|
|
for (let k=0; k<n32*2-1; k++) {
|
|
for (let i=Math.max(0, k-n32+1); (i<=k)&&(i<n32); i++) {
|
|
const j= k-i;
|
|
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_add(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
mulij(i,j)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
c.setLocal(c1,
|
|
c.i64_add(
|
|
c.getLocal(c1),
|
|
c.i64_shr_u(
|
|
c.getLocal(c0),
|
|
c.i64_const(32)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
for (let i=Math.max(1, k-n32+1); (i<=k)&&(i<n32); i++) {
|
|
const j= k-i;
|
|
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_add(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
mulqm(i,j)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
c.setLocal(c1,
|
|
c.i64_add(
|
|
c.getLocal(c1),
|
|
c.i64_shr_u(
|
|
c.getLocal(c0),
|
|
c.i64_const(32)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
if (k<n32) {
|
|
f.addCode(
|
|
c.setLocal(
|
|
"m"+k,
|
|
c.i64_and(
|
|
c.i64_mul(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
c.getLocal("np32")
|
|
),
|
|
c.i64_const("0xFFFFFFFF")
|
|
)
|
|
)
|
|
);
|
|
|
|
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_add(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
mulqm(0,k)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
c.setLocal(c1,
|
|
c.i64_add(
|
|
c.getLocal(c1),
|
|
c.i64_shr_u(
|
|
c.getLocal(c0),
|
|
c.i64_const(32)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
if (k>=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.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"))),
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
function buildSquare() {
|
|
|
|
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");
|
|
|
|
|
|
for (let i=0;i<n32; i++) {
|
|
f.addLocal("x"+i, "i64");
|
|
f.addLocal("m"+i, "i64");
|
|
f.addLocal("q"+i, "i64");
|
|
}
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const np32 = bigInt$6("100000000",16).minus( q.modInv(bigInt$6("100000000",16))).toJSNumber();
|
|
|
|
f.addCode(c.setLocal("np32", c.i64_const(np32)));
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
return c.i64_mul( X, Y );
|
|
}
|
|
|
|
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);
|
|
}
|
|
M = c.getLocal("m"+j);
|
|
|
|
return c.i64_mul( Q, M );
|
|
}
|
|
|
|
|
|
let c0 = "c0";
|
|
let c1 = "c1";
|
|
let c0_old = "c0_old";
|
|
let c1_old = "c1_old";
|
|
|
|
for (let k=0; k<n32*2-1; k++) {
|
|
f.addCode(
|
|
c.setLocal(c0, c.i64_const(0)),
|
|
c.setLocal(c1, c.i64_const(0)),
|
|
);
|
|
for (let i=Math.max(0, k-n32+1); (i<((k+1)>>1) )&&(i<n32); i++) {
|
|
const j= k-i;
|
|
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_add(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
mulij(i,j)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
c.setLocal(c1,
|
|
c.i64_add(
|
|
c.getLocal(c1),
|
|
c.i64_shr_u(
|
|
c.getLocal(c0),
|
|
c.i64_const(32)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
// Multiply by 2
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_shl(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
c.i64_const(1)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
c.setLocal(c1,
|
|
c.i64_add(
|
|
c.i64_shl(
|
|
c.getLocal(c1),
|
|
c.i64_const(1)
|
|
),
|
|
c.i64_shr_u(
|
|
c.getLocal(c0),
|
|
c.i64_const(32)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
if (k%2 == 0) {
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_add(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
mulij(k>>1, k>>1)
|
|
)
|
|
)
|
|
);
|
|
|
|
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); i++) {
|
|
const j= k-i;
|
|
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_add(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
mulqm(i,j)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
c.setLocal(c1,
|
|
c.i64_add(
|
|
c.getLocal(c1),
|
|
c.i64_shr_u(
|
|
c.getLocal(c0),
|
|
c.i64_const(32)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
if (k<n32) {
|
|
f.addCode(
|
|
c.setLocal(
|
|
"m"+k,
|
|
c.i64_and(
|
|
c.i64_mul(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
c.getLocal("np32")
|
|
),
|
|
c.i64_const("0xFFFFFFFF")
|
|
)
|
|
)
|
|
);
|
|
|
|
|
|
f.addCode(
|
|
c.setLocal(c0,
|
|
c.i64_add(
|
|
c.i64_and(
|
|
c.getLocal(c0),
|
|
c.i64_const(0xFFFFFFFF)
|
|
),
|
|
mulqm(0,k)
|
|
)
|
|
)
|
|
);
|
|
|
|
f.addCode(
|
|
c.setLocal(c1,
|
|
c.i64_add(
|
|
c.getLocal(c1),
|
|
c.i64_shr_u(
|
|
c.getLocal(c0),
|
|
c.i64_const(32)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
if (k>=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)
|
|
)
|
|
);
|
|
|
|
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"))),
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
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 = bigInt$6(2);
|
|
if (q.isPrime()) {
|
|
while (!_nqr.modPow(_e, q).equals(_minusOne)) _nqr = _nqr.add(bigInt$6.one);
|
|
}
|
|
|
|
module.alloc(utils$9.bigInt2BytesLE(_nqr.shiftLeft(n64*64).mod(q), n8));
|
|
|
|
let s2 = 0;
|
|
let _t = _minusOne;
|
|
|
|
while ((!_t.isOdd())&&(!_t.isZero())) {
|
|
s2++;
|
|
_t = _t.shiftRight(1);
|
|
}
|
|
const pt = module.alloc(n8, utils$9.bigInt2BytesLE(_t, n8));
|
|
|
|
const _nqrToT = _nqr.modPow(_t, q);
|
|
const pNqrToT = module.alloc(utils$9.bigInt2BytesLE(_nqrToT.shiftLeft(n64*64).mod(q), n8));
|
|
|
|
const _tPlusOneOver2 = _t.add(1).shiftRight(1);
|
|
const ptPlusOneOver2 = module.alloc(n8, utils$9.bigInt2BytesLE(_tPlusOneOver2, n8));
|
|
|
|
function buildSqrt() {
|
|
|
|
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");
|
|
|
|
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(
|
|
|
|
// 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("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_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("j"),
|
|
pAux,
|
|
c.i32_load8_u(c.getLocal("p")),
|
|
),
|
|
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 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 AUX = c.i32_const(module.alloc(n8));
|
|
|
|
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")),
|
|
);
|
|
}
|
|
|
|
function buildIsOne() {
|
|
const f = module.addFunction(prefix+"_isOne");
|
|
f.addParam("x", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
f.addCode(
|
|
c.ret(c.call(intPrefix + "_eq", c.getLocal("x"), c.i32_const(pOne)))
|
|
);
|
|
}
|
|
|
|
|
|
module.exportFunction(intPrefix + "_copy", prefix+"_copy");
|
|
module.exportFunction(intPrefix + "_zero", prefix+"_zero");
|
|
module.exportFunction(intPrefix + "_isZero", prefix+"_isZero");
|
|
module.exportFunction(intPrefix + "_eq", prefix+"_eq");
|
|
|
|
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 (q.isPrime()) {
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const bigInt$5 = BigIntegerExports;
|
|
|
|
const buildF1m$2 =build_f1m;
|
|
|
|
var build_f1 = function buildF1(module, _q, _prefix, _f1mPrefix, _intPrefix) {
|
|
|
|
const q = bigInt$5(_q);
|
|
const n64 = Math.floor((q.minus(1).bitLength() - 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 buildSquare() {
|
|
const f = module.addFunction(prefix+"_square");
|
|
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 buildInverse() {
|
|
|
|
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 buildIsNegative() {
|
|
const f = module.addFunction(prefix+"_isNegative");
|
|
f.addParam("x", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(
|
|
c.call(intPrefix + "_gte", c.getLocal("x"), c.i32_const(pePlusOne) )
|
|
);
|
|
}
|
|
|
|
|
|
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");
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const buildExp$1 = build_timesscalar;
|
|
const buildBatchInverse$1 = build_batchinverse;
|
|
const bigInt$4 = BigIntegerExports;
|
|
const utils$8 = utils$b;
|
|
|
|
var build_f2m = function buildF2m(module, mulNonResidueFn, prefix, f1mPrefix) {
|
|
|
|
if (module.modules[prefix]) return prefix; // already builded
|
|
|
|
const f1n8 = module.modules[f1mPrefix].n64*8;
|
|
const q = module.modules[f1mPrefix].q;
|
|
|
|
module.modules[prefix] = {
|
|
n64: module.modules[f1mPrefix].n64*2
|
|
};
|
|
|
|
function buildAdd() {
|
|
const f = module.addFunction(prefix+"_add");
|
|
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+"_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));
|
|
|
|
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();
|
|
|
|
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+"_copy", x0, r0),
|
|
c.call(f1mPrefix+"_neg", x1, r1),
|
|
);
|
|
}
|
|
|
|
|
|
function buildIsNegative() {
|
|
const f = module.addFunction(prefix+"_isNegative");
|
|
f.addParam("x", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const x0 = c.getLocal("x");
|
|
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
|
|
|
|
f.addCode(
|
|
c.if(
|
|
c.call(f1mPrefix+"_isZero", x1),
|
|
c.ret(c.call(f1mPrefix+"_isNegative", x0))
|
|
),
|
|
c.ret(c.call(f1mPrefix+"_isNegative", x1))
|
|
);
|
|
}
|
|
|
|
function buildMul() {
|
|
const f = module.addFunction(prefix+"_mul");
|
|
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));
|
|
|
|
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));
|
|
|
|
|
|
f.addCode(
|
|
c.call(f1mPrefix + "_mul", x0, y0, A), // A = x0*y0
|
|
c.call(f1mPrefix + "_mul", x1, y1, B), // B = x1*y1
|
|
|
|
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
|
|
|
|
// 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
|
|
);
|
|
|
|
}
|
|
|
|
function buildMul1() {
|
|
const f = module.addFunction(prefix+"_mul1");
|
|
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 y = c.getLocal("y");
|
|
const r0 = c.getLocal("r");
|
|
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
|
|
|
|
|
|
f.addCode(
|
|
c.call(f1mPrefix + "_mul", x0, y, r0), // A = x0*y
|
|
c.call(f1mPrefix + "_mul", x1, y, r1), // B = x1*y
|
|
);
|
|
}
|
|
|
|
function buildSquare() {
|
|
const f = module.addFunction(prefix+"_square");
|
|
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));
|
|
|
|
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));
|
|
|
|
|
|
f.addCode(
|
|
// AB = x0*y1
|
|
c.call(f1mPrefix + "_mul", x0, x1, AB),
|
|
|
|
// APB = x0+y1
|
|
c.call(f1mPrefix + "_add", x0, x1, APB),
|
|
|
|
// APBN0 = x0 + nr*x1
|
|
c.call(mulNonResidueFn, x1, APNB),
|
|
c.call(f1mPrefix + "_add", x0, APNB, APNB),
|
|
|
|
// ABPNAB = ab + nr*ab
|
|
c.call(mulNonResidueFn, AB, ABPNAB),
|
|
c.call(f1mPrefix + "_add", ABPNAB, AB, ABPNAB),
|
|
|
|
// r0 = APB * APNB - ABPNAB
|
|
c.call(f1mPrefix + "_mul", APB, APNB, r0),
|
|
c.call(f1mPrefix + "_sub", r0, ABPNAB, r0),
|
|
|
|
// r1 = AB + AB
|
|
c.call(f1mPrefix + "_add", AB, AB, r1),
|
|
);
|
|
|
|
}
|
|
|
|
|
|
function buildToMontgomery() {
|
|
const f = module.addFunction(prefix+"_toMontgomery");
|
|
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.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 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.call(f1mPrefix+"_copy", x0, r0),
|
|
c.call(f1mPrefix+"_copy", x1, r1)
|
|
);
|
|
}
|
|
|
|
function buildZero() {
|
|
const f = module.addFunction(prefix+"_zero");
|
|
f.addParam("x", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const x0 = c.getLocal("x");
|
|
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
|
|
|
|
f.addCode(
|
|
c.call(f1mPrefix+"_zero", x0),
|
|
c.call(f1mPrefix+"_zero", x1)
|
|
);
|
|
}
|
|
|
|
function buildOne() {
|
|
const f = module.addFunction(prefix+"_one");
|
|
f.addParam("x", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const x0 = c.getLocal("x");
|
|
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
|
|
|
|
f.addCode(
|
|
c.call(f1mPrefix+"_one", x0),
|
|
c.call(f1mPrefix+"_zero", x1)
|
|
);
|
|
}
|
|
|
|
function buildEq() {
|
|
const f = module.addFunction(prefix+"_eq");
|
|
f.addParam("x", "i32");
|
|
f.addParam("y", "i32");
|
|
f.setReturnType("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));
|
|
|
|
f.addCode(
|
|
c.i32_and(
|
|
c.call(f1mPrefix+"_eq", x0, y0),
|
|
c.call(f1mPrefix+"_eq", x1, y1)
|
|
)
|
|
);
|
|
}
|
|
|
|
function buildIsZero() {
|
|
const f = module.addFunction(prefix+"_isZero");
|
|
f.addParam("x", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const x0 = c.getLocal("x");
|
|
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
|
|
|
|
f.addCode(
|
|
c.i32_and(
|
|
c.call(f1mPrefix+"_isZero", x0),
|
|
c.call(f1mPrefix+"_isZero", x1)
|
|
)
|
|
);
|
|
}
|
|
|
|
function buildInverse() {
|
|
const f = module.addFunction(prefix+"_inverse");
|
|
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));
|
|
|
|
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));
|
|
|
|
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.call(f1mPrefix+"_sub", t0, t2, t2),
|
|
c.call(f1mPrefix+"_inverse", t2, t3),
|
|
|
|
c.call(f1mPrefix+"_mul", x0, t3, r0),
|
|
c.call(f1mPrefix+"_mul", x1, t3, r1),
|
|
c.call(f1mPrefix+"_neg", r1, r1),
|
|
);
|
|
}
|
|
|
|
|
|
function buildSign() {
|
|
const f = module.addFunction(prefix+"_sign");
|
|
f.addParam("x", "i32");
|
|
f.addLocal("s", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const x0 = c.getLocal("x");
|
|
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
|
|
|
|
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))
|
|
);
|
|
}
|
|
|
|
function buildIsOne() {
|
|
const f = module.addFunction(prefix+"_isOne");
|
|
f.addParam("x", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
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),
|
|
))
|
|
);
|
|
}
|
|
|
|
|
|
// 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 e34 = c.i32_const(module.alloc(utils$8.bigInt2BytesLE(bigInt$4(q).minus(bigInt$4(3)).divide(4), f1n8 )));
|
|
const e12 = c.i32_const(module.alloc(utils$8.bigInt2BytesLE(bigInt$4(q).minus(bigInt$4(1)).divide(2), 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.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.unreachable() ),
|
|
|
|
// const x0 = F.mul(a1, a);
|
|
c.call(prefix + "_mul", a1, a, x0),
|
|
|
|
// if (F.eq(alfa, F.negone)) {
|
|
c.if(
|
|
c.call(prefix + "_eq", alpha, n1),
|
|
[
|
|
// 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")),
|
|
],
|
|
[
|
|
// 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();
|
|
|
|
const e34 = c.i32_const(module.alloc(utils$8.bigInt2BytesLE(bigInt$4(q).minus(bigInt$4(3)).divide(4), 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))
|
|
);
|
|
|
|
}
|
|
|
|
|
|
buildIsZero();
|
|
buildIsOne();
|
|
buildZero();
|
|
buildOne();
|
|
buildCopy();
|
|
buildMul();
|
|
buildMul1();
|
|
buildSquare();
|
|
buildAdd();
|
|
buildSub();
|
|
buildNeg();
|
|
buildConjugate();
|
|
buildToMontgomery();
|
|
buildFromMontgomery();
|
|
buildEq();
|
|
buildInverse();
|
|
buildTimesScalar();
|
|
buildSign();
|
|
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 + "_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();
|
|
|
|
module.exportFunction(prefix + "_exp");
|
|
module.exportFunction(prefix + "_timesScalar");
|
|
module.exportFunction(prefix + "_batchInverse");
|
|
module.exportFunction(prefix + "_sqrt");
|
|
module.exportFunction(prefix + "_isSquare");
|
|
module.exportFunction(prefix + "_isNegative");
|
|
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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("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.call(f1mPrefix+"_add", x0, y0, r0),
|
|
c.call(f1mPrefix+"_add", x1, y1, r1),
|
|
c.call(f1mPrefix+"_add", x2, y2, r2),
|
|
);
|
|
}
|
|
|
|
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 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+"_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),
|
|
);
|
|
}
|
|
|
|
|
|
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 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+"_sub", x0, y0, r0),
|
|
c.call(f1mPrefix+"_sub", x1, y1, r1),
|
|
c.call(f1mPrefix+"_sub", x2, y2, r2),
|
|
);
|
|
}
|
|
|
|
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 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 buildIsNegative() {
|
|
const f = module.addFunction(prefix+"_isNegative");
|
|
f.addParam("x", "i32");
|
|
f.setReturnType("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));
|
|
|
|
f.addCode(
|
|
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))
|
|
);
|
|
}
|
|
|
|
|
|
function buildMul() {
|
|
const f = module.addFunction(prefix+"_mul");
|
|
f.addParam("x", "i32");
|
|
f.addParam("y", "i32");
|
|
f.addParam("r", "i32");
|
|
|
|
const cd = f.getCodeBuilder();
|
|
|
|
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));
|
|
|
|
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));
|
|
|
|
|
|
f.addCode(
|
|
cd.call(f1mPrefix + "_mul", a, A, aA),
|
|
cd.call(f1mPrefix + "_mul", b, B, bB),
|
|
cd.call(f1mPrefix + "_mul", c, C, cC),
|
|
|
|
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),
|
|
|
|
cd.call(f1mPrefix + "_add", aA, bB, aA_bB),
|
|
cd.call(f1mPrefix + "_add", aA, cC, aA_cC),
|
|
cd.call(f1mPrefix + "_add", bB, cC, bB_cC),
|
|
|
|
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),
|
|
|
|
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 buildSquare() {
|
|
const f = module.addFunction(prefix+"_square");
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
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.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(f1mPrefix + "_mul", B, C, bc),
|
|
c.call(f1mPrefix + "_add", bc, bc, s3),
|
|
|
|
c.call(f1mPrefix + "_square", C, s4),
|
|
|
|
c.call(mulNonResidueFn, s3, r0),
|
|
c.call(f1mPrefix + "_add", s0, r0, r0),
|
|
|
|
c.call(mulNonResidueFn, s4, r1),
|
|
c.call(f1mPrefix + "_add", s1, r1, r1),
|
|
|
|
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),
|
|
);
|
|
|
|
}
|
|
|
|
|
|
function buildToMontgomery() {
|
|
const f = module.addFunction(prefix+"_toMontgomery");
|
|
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 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+"_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();
|
|
|
|
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 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),
|
|
);
|
|
}
|
|
|
|
function buildZero() {
|
|
const f = module.addFunction(prefix+"_zero");
|
|
f.addParam("x", "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));
|
|
|
|
f.addCode(
|
|
c.call(f1mPrefix+"_zero", x0),
|
|
c.call(f1mPrefix+"_zero", x1),
|
|
c.call(f1mPrefix+"_zero", x2),
|
|
);
|
|
}
|
|
|
|
function buildOne() {
|
|
const f = module.addFunction(prefix+"_one");
|
|
f.addParam("x", "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));
|
|
|
|
f.addCode(
|
|
c.call(f1mPrefix+"_one", x0),
|
|
c.call(f1mPrefix+"_zero", x1),
|
|
c.call(f1mPrefix+"_zero", x2),
|
|
);
|
|
}
|
|
|
|
function buildEq() {
|
|
const f = module.addFunction(prefix+"_eq");
|
|
f.addParam("x", "i32");
|
|
f.addParam("y", "i32");
|
|
f.setReturnType("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));
|
|
|
|
f.addCode(
|
|
c.i32_and(
|
|
c.i32_and(
|
|
c.call(f1mPrefix+"_eq", x0, y0),
|
|
c.call(f1mPrefix+"_eq", x1, y1),
|
|
),
|
|
c.call(f1mPrefix+"_eq", x2, y2)
|
|
)
|
|
);
|
|
}
|
|
|
|
function buildIsZero() {
|
|
const f = module.addFunction(prefix+"_isZero");
|
|
f.addParam("x", "i32");
|
|
f.setReturnType("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));
|
|
|
|
f.addCode(
|
|
c.i32_and(
|
|
c.i32_and(
|
|
c.call(f1mPrefix+"_isZero", x0),
|
|
c.call(f1mPrefix+"_isZero", x1)
|
|
),
|
|
c.call(f1mPrefix+"_isZero", x2)
|
|
)
|
|
);
|
|
}
|
|
|
|
function buildInverse() {
|
|
const f = module.addFunction(prefix+"_inverse");
|
|
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 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));
|
|
|
|
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));
|
|
|
|
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(f1mPrefix+"_inverse", t6, t6),
|
|
|
|
c.call(f1mPrefix+"_mul", t6, c0, r0),
|
|
c.call(f1mPrefix+"_mul", t6, c1, r1),
|
|
c.call(f1mPrefix+"_mul", t6, c2, r2)
|
|
);
|
|
}
|
|
|
|
|
|
function buildSign() {
|
|
const f = module.addFunction(prefix+"_sign");
|
|
f.addParam("x", "i32");
|
|
f.addLocal("s", "i32");
|
|
f.setReturnType("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));
|
|
|
|
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))
|
|
);
|
|
}
|
|
|
|
function buildIsOne() {
|
|
const f = module.addFunction(prefix+"_isOne");
|
|
f.addParam("x", "i32");
|
|
f.setReturnType("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(f1n8*2));
|
|
|
|
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)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
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");
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const aux = c.i32_const(module.alloc(elementLen));
|
|
|
|
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.i32_const(1)
|
|
);
|
|
}
|
|
|
|
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)
|
|
)
|
|
)
|
|
];
|
|
}
|
|
|
|
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)),
|
|
|
|
c.setLocal("last",getBit(c.i32_const(0))),
|
|
c.setLocal("carry",c.i32_const(0)),
|
|
|
|
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.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("p", c.i32_sub(c.getLocal("p"), c.i32_const(1))),
|
|
|
|
// p already points to the last bit
|
|
|
|
c.call(opCopy, c.getLocal("base"), aux),
|
|
|
|
c.call(opInit, c.getLocal("r")),
|
|
|
|
c.block(c.loop(
|
|
|
|
|
|
c.call(opAA, c.getLocal("r"), c.getLocal("r")),
|
|
|
|
|
|
c.setLocal("cur",
|
|
c.i32_load8_u(
|
|
c.getLocal("p")
|
|
)
|
|
),
|
|
|
|
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)
|
|
|
|
)),
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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.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.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")
|
|
)
|
|
),
|
|
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)
|
|
)
|
|
)
|
|
),
|
|
|
|
// Reset Table
|
|
c.setLocal("j", c.i32_const(0)),
|
|
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.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(
|
|
"idx",
|
|
c.call(fnName + "_getChunk",
|
|
c.getLocal("itScalar"),
|
|
c.getLocal("scalarSize"),
|
|
c.getLocal("startBit"),
|
|
c.getLocal("chunkSize")
|
|
)
|
|
),
|
|
|
|
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 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(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.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)
|
|
)
|
|
),
|
|
|
|
|
|
// 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_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(
|
|
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)
|
|
))
|
|
);
|
|
}
|
|
|
|
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.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.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"))
|
|
);
|
|
}
|
|
|
|
buildGetChunk();
|
|
buildReduceTable();
|
|
buildMutiexpChunk();
|
|
buildMultiexp();
|
|
|
|
module.exportFunction(fnName);
|
|
module.exportFunction(fnName +"_chunk");
|
|
|
|
|
|
};
|
|
|
|
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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
|
|
};
|
|
|
|
function buildIsZero() {
|
|
const f = module.addFunction(prefix + "_isZero");
|
|
f.addParam("p1", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
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 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<n64*3; i++) {
|
|
f.addCode(
|
|
c.i64_store(
|
|
c.getLocal("pd"),
|
|
i*8,
|
|
c.i64_load(
|
|
c.getLocal("ps"),
|
|
i*8
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
function buildCopyAffine() {
|
|
const f = module.addFunction(prefix + "_copyAffine");
|
|
f.addParam("ps", "i32");
|
|
f.addParam("pd", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
for (let i=0; i<n64*2; i++) {
|
|
f.addCode(
|
|
c.i64_store(
|
|
c.getLocal("pd"),
|
|
i*8,
|
|
c.i64_load(
|
|
c.getLocal("ps"),
|
|
i*8
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
function buildZero() {
|
|
const f = module.addFunction(prefix + "_zero");
|
|
f.addParam("pr", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(c.call(
|
|
prefixField + "_zero",
|
|
c.getLocal("pr")
|
|
));
|
|
|
|
f.addCode(c.call(
|
|
prefixField + "_one",
|
|
c.i32_add(
|
|
c.getLocal("pr"),
|
|
c.i32_const(n8)
|
|
)
|
|
));
|
|
|
|
f.addCode(c.call(
|
|
prefixField + "_zero",
|
|
c.i32_add(
|
|
c.getLocal("pr"),
|
|
c.i32_const(n8*2)
|
|
)
|
|
));
|
|
}
|
|
|
|
|
|
function buildZeroAffine() {
|
|
const f = module.addFunction(prefix + "_zeroAffine");
|
|
f.addParam("pr", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(c.call(
|
|
prefixField + "_zero",
|
|
c.getLocal("pr")
|
|
));
|
|
|
|
f.addCode(c.call(
|
|
prefixField + "_zero",
|
|
c.i32_add(
|
|
c.getLocal("pr"),
|
|
c.i32_const(n8)
|
|
)
|
|
));
|
|
}
|
|
|
|
function buildEq() {
|
|
const f = module.addFunction(prefix + "_eq");
|
|
f.addParam("p1", "i32");
|
|
f.addParam("p2", "i32");
|
|
f.setReturnType("i32");
|
|
f.addLocal("z1", "i32");
|
|
f.addLocal("z2", "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 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));
|
|
|
|
|
|
f.addCode(
|
|
c.if(
|
|
c.call(prefix + "_isZero", c.getLocal("p1")),
|
|
c.ret( c.call(prefix + "_isZero", c.getLocal("p2"))),
|
|
),
|
|
c.if(
|
|
c.call(prefix + "_isZero", c.getLocal("p2")),
|
|
c.ret(c.i32_const(0))
|
|
),
|
|
c.if(
|
|
c.call(prefixField + "_isOne", z1),
|
|
c.ret(c.call(prefix + "_eqMixed", c.getLocal("p2"), c.getLocal("p1")))
|
|
),
|
|
c.if(
|
|
c.call(prefixField + "_isOne", z2),
|
|
c.ret(c.call(prefix + "_eqMixed", c.getLocal("p1"), c.getLocal("p2")))
|
|
),
|
|
|
|
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.ret(c.i32_const(1))
|
|
)
|
|
),
|
|
c.ret(c.i32_const(0))
|
|
);
|
|
}
|
|
|
|
|
|
function buildEqMixed() {
|
|
const f = module.addFunction(prefix + "_eqMixed");
|
|
f.addParam("p1", "i32");
|
|
f.addParam("p2", "i32");
|
|
f.setReturnType("i32");
|
|
f.addLocal("z1", "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));
|
|
|
|
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));
|
|
|
|
f.addCode(
|
|
c.if(
|
|
c.call(prefix + "_isZero", c.getLocal("p1")),
|
|
c.ret( c.call(prefix + "_isZeroAffine", c.getLocal("p2"))),
|
|
),
|
|
c.if(
|
|
c.call(prefix + "_isZeroAffine", c.getLocal("p2")),
|
|
c.ret(c.i32_const(0))
|
|
),
|
|
c.if(
|
|
c.call(prefixField + "_isOne", z1),
|
|
c.ret(c.call(prefix + "_eqAffine", c.getLocal("p1"), c.getLocal("p2")))
|
|
),
|
|
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),
|
|
|
|
c.if(
|
|
c.call(prefixField + "_eq", x1, U2),
|
|
c.if(
|
|
c.call(prefixField + "_eq", y1, S2),
|
|
c.ret(c.i32_const(1))
|
|
)
|
|
),
|
|
c.ret(c.i32_const(0))
|
|
);
|
|
}
|
|
|
|
function buildDouble() {
|
|
const f = module.addFunction(prefix + "_double");
|
|
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));
|
|
|
|
const A = c.i32_const(module.alloc(n8));
|
|
const B = c.i32_const(module.alloc(n8));
|
|
const C = c.i32_const(module.alloc(n8));
|
|
const D = c.i32_const(module.alloc(n8));
|
|
const E = c.i32_const(module.alloc(n8));
|
|
const F = c.i32_const(module.alloc(n8));
|
|
const G = c.i32_const(module.alloc(n8));
|
|
const eightC = c.i32_const(module.alloc(n8));
|
|
|
|
f.addCode(
|
|
c.if(
|
|
c.call(prefix + "_isZero", c.getLocal("p1")),
|
|
[
|
|
...c.call(prefix + "_copy", c.getLocal("p1"), c.getLocal("pr")),
|
|
...c.ret([])
|
|
]
|
|
),
|
|
c.if(
|
|
c.call(prefixField + "_isOne", z),
|
|
[
|
|
...c.ret(c.call(prefix + "_doubleAffine", c.getLocal("p1"), c.getLocal("pr"))),
|
|
...c.ret([])
|
|
]
|
|
),
|
|
|
|
c.call(prefixField + "_square", x, A),
|
|
c.call(prefixField + "_square", y, B),
|
|
c.call(prefixField + "_square", B, C),
|
|
|
|
c.call(prefixField + "_add", x, B, D),
|
|
c.call(prefixField + "_square", D, D),
|
|
c.call(prefixField + "_sub", D, A, D),
|
|
c.call(prefixField + "_sub", D, C, D),
|
|
c.call(prefixField + "_add", D, D, D),
|
|
|
|
c.call(prefixField + "_add", A, A, E),
|
|
c.call(prefixField + "_add", E, A, E),
|
|
c.call(prefixField + "_square", E, F),
|
|
|
|
c.call(prefixField + "_mul", y, z, G),
|
|
|
|
c.call(prefixField + "_add", D, D, x3),
|
|
c.call(prefixField + "_sub", F, x3, x3),
|
|
|
|
c.call(prefixField + "_add", C, C, eightC),
|
|
c.call(prefixField + "_add", eightC, eightC, eightC),
|
|
c.call(prefixField + "_add", eightC, eightC, eightC),
|
|
|
|
c.call(prefixField + "_sub", D, x3, y3),
|
|
c.call(prefixField + "_mul", y3, E, y3),
|
|
c.call(prefixField + "_sub", y3, eightC, y3),
|
|
|
|
c.call(prefixField + "_add", G, G, z3),
|
|
);
|
|
}
|
|
|
|
|
|
function buildDoubleAffine() {
|
|
const f = module.addFunction(prefix + "_doubleAffine");
|
|
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 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 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));
|
|
|
|
f.addCode(
|
|
c.if(
|
|
c.call(prefix + "_isZeroAffine", c.getLocal("p1")),
|
|
[
|
|
...c.call(prefix + "_toJacobian", c.getLocal("p1"), c.getLocal("pr")),
|
|
...c.ret([])
|
|
]
|
|
),
|
|
|
|
// XX = X1^2
|
|
c.call(prefixField + "_square", x, XX),
|
|
|
|
// YY = Y1^2
|
|
c.call(prefixField + "_square", y, YY),
|
|
|
|
// YYYY = YY^2
|
|
c.call(prefixField + "_square", YY, YYYY),
|
|
|
|
// 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),
|
|
|
|
// M = 3*XX+a (Hera a=0)
|
|
c.call(prefixField + "_add", XX, XX, M),
|
|
c.call(prefixField + "_add", M, XX, M),
|
|
|
|
// Z3 = 2*Y1
|
|
c.call(prefixField + "_add", y, y, z3),
|
|
|
|
// 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),
|
|
|
|
// 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 buildEqAffine() {
|
|
const f = module.addFunction(prefix + "_eqAffine");
|
|
f.addParam("p1", "i32");
|
|
f.addParam("p2", "i32");
|
|
f.setReturnType("i32");
|
|
f.addLocal("z1", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
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 buildToMontgomery() {
|
|
const f = module.addFunction(prefix + "_toMontgomery");
|
|
f.addParam("p1", "i32");
|
|
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))
|
|
));
|
|
}
|
|
}
|
|
|
|
function buildToMontgomeryAffine() {
|
|
const f = module.addFunction(prefix + "_toMontgomeryAffine");
|
|
f.addParam("p1", "i32");
|
|
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<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))
|
|
));
|
|
}
|
|
}
|
|
|
|
function buildFromMontgomery() {
|
|
const f = module.addFunction(prefix + "_fromMontgomery");
|
|
f.addParam("p1", "i32");
|
|
f.addParam("pr", "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))
|
|
));
|
|
}
|
|
}
|
|
|
|
|
|
function buildFromMontgomeryAffine() {
|
|
const f = module.addFunction(prefix + "_fromMontgomeryAffine");
|
|
f.addParam("p1", "i32");
|
|
f.addParam("pr", "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))
|
|
));
|
|
}
|
|
}
|
|
|
|
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");
|
|
|
|
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));
|
|
|
|
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(prefixField + "_sub", r2, J, x3),
|
|
c.call(prefixField + "_sub", x3, V2, x3),
|
|
|
|
c.call(prefixField + "_mul", S1, J, S1_J2),
|
|
c.call(prefixField + "_add", S1_J2, S1_J2, S1_J2),
|
|
|
|
c.call(prefixField + "_sub", V, x3, y3),
|
|
c.call(prefixField + "_mul", y3, r, y3),
|
|
c.call(prefixField + "_sub", y3, S1_J2, y3),
|
|
|
|
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),
|
|
);
|
|
|
|
}
|
|
|
|
|
|
function buildAddMixed() {
|
|
|
|
const f = module.addFunction(prefix + "_addMixed");
|
|
f.addParam("p1", "i32");
|
|
f.addParam("p2", "i32");
|
|
f.addParam("pr", "i32");
|
|
f.addLocal("z1", "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));
|
|
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 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.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),
|
|
|
|
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([])
|
|
]
|
|
)
|
|
),
|
|
|
|
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),
|
|
|
|
c.call(prefixField + "_mul", y1, J, y1_J2),
|
|
c.call(prefixField + "_add", y1_J2, y1_J2, y1_J2),
|
|
|
|
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() {
|
|
|
|
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("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 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([])
|
|
]
|
|
),
|
|
|
|
|
|
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([])
|
|
]
|
|
)
|
|
),
|
|
|
|
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),
|
|
|
|
c.call(prefixField + "_sub", r2, J, x3),
|
|
c.call(prefixField + "_sub", x3, V2, x3),
|
|
|
|
c.call(prefixField + "_mul", y1, J, y1_J2),
|
|
c.call(prefixField + "_add", y1_J2, y1_J2, y1_J2),
|
|
|
|
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 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(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));
|
|
|
|
f.addCode(
|
|
c.call(prefixField + "_copy", x, x3),
|
|
c.call(prefixField + "_neg", y, y3),
|
|
c.call(prefixField + "_copy", z, z3)
|
|
);
|
|
}
|
|
|
|
|
|
function buildNegAffine() {
|
|
const f = module.addFunction(prefix + "_negAffine");
|
|
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 x3 = c.getLocal("pr");
|
|
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
|
|
|
|
f.addCode(
|
|
c.call(prefixField + "_copy", x, x3),
|
|
c.call(prefixField + "_neg", y, y3),
|
|
);
|
|
}
|
|
|
|
|
|
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 + "_neg", c.getLocal("p2"), AUX),
|
|
c.call(prefix + "_add", c.getLocal("p1"), AUX, c.getLocal("pr")),
|
|
);
|
|
}
|
|
|
|
function buildSubMixed() {
|
|
const f = module.addFunction(prefix + "_subMixed");
|
|
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 + "_addMixed", c.getLocal("p1"), AUX, c.getLocal("pr")),
|
|
);
|
|
}
|
|
|
|
|
|
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")),
|
|
);
|
|
}
|
|
|
|
// 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));
|
|
|
|
|
|
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),
|
|
]
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
// Does not set Z.
|
|
function buildToAffine() {
|
|
const f = module.addFunction(prefix + "_toAffine");
|
|
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 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(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),
|
|
]
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
function buildToJacobian() {
|
|
const f = module.addFunction(prefix + "_toJacobian");
|
|
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 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 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 tmp = c.i32_const(module.alloc(n8));
|
|
|
|
f.addCode(
|
|
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.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("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.br_if(1, c.i32_eq ( c.getLocal("i"), c.getLocal("n") )),
|
|
|
|
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)),
|
|
),
|
|
]
|
|
),
|
|
|
|
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")
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
// 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();
|
|
|
|
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();
|
|
|
|
const tmp = c.i32_const(module.alloc(n8));
|
|
|
|
f.addCode(
|
|
c.if(
|
|
c.call(prefix + "_isZero", 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 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.i32_store8(
|
|
c.getLocal("pOut"),
|
|
c.i32_const(0x40)
|
|
),
|
|
...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))),
|
|
);
|
|
}
|
|
|
|
function buildUtoLEM() {
|
|
const f = module.addFunction(prefix + "_UtoLEM");
|
|
f.addParam("pIn", "i32");
|
|
f.addParam("pOut", "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.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"))
|
|
);
|
|
}
|
|
|
|
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();
|
|
|
|
const pTmp = module.alloc(n8*2);
|
|
const tmpX = c.i32_const(pTmp);
|
|
const tmpY = c.i32_const(pTmp + n8);
|
|
|
|
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)
|
|
)
|
|
),
|
|
|
|
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")),
|
|
|
|
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),
|
|
|
|
c.call(prefixField + "_sqrt", tmpY, tmpY),
|
|
c.call(prefixField + "_neg", tmpY, tmpX),
|
|
|
|
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");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const x = c.getLocal("pIn");
|
|
const y = c.i32_add(c.getLocal("pIn"), n8);
|
|
|
|
const y2 = module.alloc(n8*2);
|
|
const x3b = module.alloc(n8*2);
|
|
|
|
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),
|
|
|
|
c.ret(
|
|
c.call(prefixField + "_eq", y2, x3b)
|
|
)
|
|
);
|
|
}
|
|
|
|
function buildInCurveAffine() {
|
|
const f = module.addFunction(prefix + "_inCurveAffine");
|
|
f.addParam("pIn", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const x = c.getLocal("pIn");
|
|
const y = c.i32_add(c.getLocal("pIn"), c.i32_const(n8));
|
|
|
|
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),
|
|
|
|
c.ret(
|
|
c.call(prefixField + "_eq", y2, x3b)
|
|
)
|
|
);
|
|
}
|
|
|
|
function buildInCurve() {
|
|
const f = module.addFunction(prefix + "_inCurve");
|
|
f.addParam("pIn", "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),
|
|
)
|
|
);
|
|
}
|
|
|
|
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();
|
|
|
|
buildNormalize();
|
|
|
|
|
|
buildReverseBytes();
|
|
|
|
buildLEMtoU();
|
|
buildLEMtoC();
|
|
buildUtoLEM();
|
|
buildCtoLEM();
|
|
|
|
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);
|
|
|
|
buildBatchConvertion(module, prefix + "_batchToJacobian", prefix + "_toJacobian", n8*2, n8*3, true);
|
|
|
|
buildMultiexp$1(module, prefix, prefix + "_multiexp", prefix + "_add", n8*3);
|
|
buildMultiexp$1(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"
|
|
);
|
|
|
|
buildTimesScalarNAF(
|
|
module,
|
|
prefix + "_timesScalarAffine",
|
|
n8*2,
|
|
prefix + "_addMixed",
|
|
prefix + "_double",
|
|
prefix + "_subMixed",
|
|
prefix + "_copyAffine",
|
|
prefix + "_zero"
|
|
);
|
|
|
|
module.exportFunction(prefix + "_isZero");
|
|
module.exportFunction(prefix + "_isZeroAffine");
|
|
|
|
module.exportFunction(prefix + "_eq");
|
|
module.exportFunction(prefix + "_eqMixed");
|
|
module.exportFunction(prefix + "_eqAffine");
|
|
|
|
module.exportFunction(prefix + "_copy");
|
|
module.exportFunction(prefix + "_copyAffine");
|
|
|
|
module.exportFunction(prefix + "_zero");
|
|
module.exportFunction(prefix + "_zeroAffine");
|
|
|
|
module.exportFunction(prefix + "_double");
|
|
module.exportFunction(prefix + "_doubleAffine");
|
|
|
|
module.exportFunction(prefix + "_add");
|
|
module.exportFunction(prefix + "_addMixed");
|
|
module.exportFunction(prefix + "_addAffine");
|
|
|
|
module.exportFunction(prefix + "_neg");
|
|
module.exportFunction(prefix + "_negAffine");
|
|
|
|
module.exportFunction(prefix + "_sub");
|
|
module.exportFunction(prefix + "_subMixed");
|
|
module.exportFunction(prefix + "_subAffine");
|
|
|
|
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");
|
|
|
|
// Convertion functions
|
|
module.exportFunction(prefix + "_LEMtoU");
|
|
module.exportFunction(prefix + "_LEMtoC");
|
|
module.exportFunction(prefix + "_UtoLEM");
|
|
module.exportFunction(prefix + "_CtoLEM");
|
|
|
|
module.exportFunction(prefix + "_batchLEMtoU");
|
|
module.exportFunction(prefix + "_batchLEMtoC");
|
|
module.exportFunction(prefix + "_batchUtoLEM");
|
|
module.exportFunction(prefix + "_batchCtoLEM");
|
|
|
|
module.exportFunction(prefix + "_toAffine");
|
|
module.exportFunction(prefix + "_toJacobian");
|
|
|
|
module.exportFunction(prefix + "_batchToAffine");
|
|
module.exportFunction(prefix + "_batchToJacobian");
|
|
|
|
module.exportFunction(prefix + "_inCurve");
|
|
module.exportFunction(prefix + "_inCurveAffine");
|
|
|
|
/*
|
|
buildG1MulScalar(module, zq);
|
|
module.exportFunction("g1MulScalar");
|
|
*/
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const bigInt$3 = BigIntegerExports;
|
|
const utils$7 = utils$b;
|
|
|
|
var build_fft = function buildFFT(module, prefix, gPrefix, fPrefix, opGtimesF) {
|
|
|
|
const n64f = module.modules[fPrefix].n64;
|
|
const n8f = n64f*8;
|
|
|
|
const n64g = module.modules[gPrefix].n64;
|
|
const n8g = n64g*8;
|
|
|
|
const q = module.modules[fPrefix].q;
|
|
|
|
let rem = q.minus(bigInt$3(1));
|
|
let maxBits = 0;
|
|
while (!rem.isOdd()) {
|
|
maxBits ++;
|
|
rem = rem.shiftRight(1);
|
|
}
|
|
|
|
let nr = bigInt$3(2);
|
|
|
|
while ( nr.modPow(q.shiftRight(1), q).equals(1) ) nr = nr.add(1);
|
|
|
|
// console.log(nr);
|
|
|
|
const w = new Array(maxBits+1);
|
|
w[maxBits] = nr.modPow(rem, q);
|
|
|
|
let n=maxBits-1;
|
|
while (n>=0) {
|
|
w[n] = w[n+1].modPow(2, q);
|
|
n--;
|
|
}
|
|
|
|
const bytes = [];
|
|
const R = bigInt$3(1).shiftLeft(n8f*8).mod(q);
|
|
|
|
for (let i=0; i<w.length; i++) {
|
|
const m = w[i].times(R).mod(q);
|
|
bytes.push(...utils$7.bigInt2BytesLE(m, n8f));
|
|
}
|
|
|
|
const ROOTs = module.alloc(bytes);
|
|
|
|
const i2 = new Array(maxBits+1);
|
|
i2[0] = bigInt$3(1);
|
|
|
|
for (let i=1; i<=maxBits; i++) {
|
|
i2[i] = i2[i-1].times(2);
|
|
}
|
|
|
|
const bytesi2 =[];
|
|
for (let i=0; i<=maxBits; i++) {
|
|
const m = i2[i].modInv(q).times(R).mod(q);
|
|
bytesi2.push(...utils$7.bigInt2BytesLE(m, n8f));
|
|
}
|
|
|
|
const INV2 = module.alloc(bytesi2);
|
|
|
|
const shift = nr.modPow(2, q);
|
|
const bytesShiftToSmallM =[];
|
|
const bytesSConst =[];
|
|
for (let i=0; i<=maxBits; i++) {
|
|
const shiftToSmallM = shift.modPow(bigInt$3(2).pow(i), q);
|
|
const sConst = q.add(bigInt$3.one).minus(shiftToSmallM).modInv(q);
|
|
bytesShiftToSmallM.push(...utils$7.bigInt2BytesLE(shiftToSmallM.times(R).mod(q), n8f));
|
|
bytesSConst.push(...utils$7.bigInt2BytesLE(sConst.times(R).mod(q), n8f));
|
|
}
|
|
|
|
const SHIFT_TO_M = module.alloc( bytesShiftToSmallM );
|
|
const SCONST = module.alloc( bytesSConst );
|
|
|
|
function rev(x) {
|
|
let r=0;
|
|
for (let i=0; i<8; i++) {
|
|
if (x & (1 << i)) {
|
|
r = r | (0x80 >> i);
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
const rtable = Array(256);
|
|
for (let i=0; i<256; i++) {
|
|
rtable[i] = rev(i);
|
|
}
|
|
|
|
const REVTABLE = module.alloc(rtable);
|
|
|
|
|
|
function buildLog2() {
|
|
const f = module.addFunction(prefix+"__log2");
|
|
f.addParam("n", "i32");
|
|
f.setReturnType("i32");
|
|
f.addLocal("bits", "i32");
|
|
f.addLocal("aux", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
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)
|
|
)
|
|
),
|
|
|
|
c.setLocal(
|
|
"bits",
|
|
c.i32_add(
|
|
c.getLocal("bits"),
|
|
c.i32_const(1)
|
|
)
|
|
),
|
|
|
|
c.br(0)
|
|
)));
|
|
|
|
f.addCode(c.if(
|
|
c.i32_ne(
|
|
c.getLocal("n"),
|
|
c.i32_shl(
|
|
c.i32_const(1),
|
|
c.getLocal("bits")
|
|
)
|
|
),
|
|
c.unreachable()
|
|
));
|
|
|
|
f.addCode(c.if(
|
|
c.i32_gt_u(
|
|
c.getLocal("bits"),
|
|
c.i32_const(maxBits)
|
|
),
|
|
c.unreachable()
|
|
));
|
|
|
|
f.addCode(c.getLocal("bits"));
|
|
}
|
|
|
|
function buildFFT() {
|
|
const f = module.addFunction(prefix+"_fft");
|
|
f.addParam("px", "i32");
|
|
f.addParam("n", "i32");
|
|
|
|
f.addLocal("bits", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const One = c.i32_const(module.alloc(n8f));
|
|
|
|
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
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
function buildIFFT() {
|
|
const f = module.addFunction(prefix+"_ifft");
|
|
f.addParam("px", "i32");
|
|
f.addParam("n", "i32");
|
|
f.addLocal("bits", "i32");
|
|
f.addLocal("pInv2", "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)
|
|
)
|
|
)
|
|
),
|
|
|
|
c.call(
|
|
prefix+"_rawfft",
|
|
c.getLocal("px"),
|
|
c.getLocal("bits"),
|
|
c.i32_const(1),
|
|
c.getLocal("pInv2")
|
|
),
|
|
);
|
|
}
|
|
|
|
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");
|
|
|
|
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),
|
|
|
|
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")
|
|
)
|
|
),
|
|
|
|
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
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_copy",
|
|
c.getLocal("idx1"),
|
|
U
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_add",
|
|
U,
|
|
T,
|
|
c.getLocal("idx1"),
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_sub",
|
|
U,
|
|
T,
|
|
c.getLocal("idx2"),
|
|
),
|
|
|
|
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("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")
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
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 T = c.i32_const(module.alloc(n8g));
|
|
|
|
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.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"))
|
|
]
|
|
)
|
|
);
|
|
}
|
|
|
|
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();
|
|
|
|
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")
|
|
)
|
|
),
|
|
|
|
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"))),
|
|
|
|
c.setLocal("idx2",
|
|
c.i32_add(
|
|
c.getLocal("px"),
|
|
c.i32_mul(
|
|
c.getLocal("ri"),
|
|
c.i32_const(n8g)
|
|
)
|
|
)
|
|
),
|
|
|
|
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 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.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 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 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")
|
|
)
|
|
),
|
|
|
|
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)
|
|
)
|
|
)
|
|
),
|
|
|
|
c.call(
|
|
opGtimesF,
|
|
c.getLocal("idx2"),
|
|
W,
|
|
T
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_copy",
|
|
c.getLocal("idx1"),
|
|
U
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_add",
|
|
U,
|
|
T,
|
|
c.getLocal("idx1"),
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_sub",
|
|
U,
|
|
T,
|
|
c.getLocal("idx2"),
|
|
),
|
|
|
|
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 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");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const W = c.i32_const(module.alloc(n8f));
|
|
const U = c.i32_const(module.alloc(n8g));
|
|
|
|
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.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.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)
|
|
)
|
|
)
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_add",
|
|
c.getLocal("idx1"),
|
|
c.getLocal("idx2"),
|
|
U
|
|
),
|
|
|
|
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")
|
|
),
|
|
|
|
c.call(
|
|
opGtimesF,
|
|
c.getLocal("idx2"),
|
|
W,
|
|
c.getLocal("idx2"),
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_copy",
|
|
U,
|
|
c.getLocal("idx1")
|
|
),
|
|
|
|
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 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 W = c.i32_const(module.alloc(n8f));
|
|
const U = c.i32_const(module.alloc(n8g));
|
|
|
|
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( 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.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)
|
|
)
|
|
)
|
|
),
|
|
|
|
c.call(
|
|
opGtimesF,
|
|
c.getLocal("idx2"),
|
|
W,
|
|
U
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_sub",
|
|
c.getLocal("idx1"),
|
|
U,
|
|
c.getLocal("idx2"),
|
|
),
|
|
|
|
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")
|
|
),
|
|
|
|
c.call(
|
|
opGtimesF,
|
|
c.getLocal("idx1"),
|
|
c.getLocal("pSConst"),
|
|
c.getLocal("idx1")
|
|
),
|
|
|
|
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 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 c = f.getCodeBuilder();
|
|
|
|
const W = c.i32_const(module.alloc(n8f));
|
|
const U = c.i32_const(module.alloc(n8g));
|
|
|
|
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( 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.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)
|
|
)
|
|
)
|
|
),
|
|
|
|
|
|
c.call(
|
|
opGtimesF,
|
|
c.getLocal("idx1"),
|
|
c.getLocal("pShiftToM"),
|
|
U
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_sub",
|
|
c.getLocal("idx2"),
|
|
U,
|
|
U
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_sub",
|
|
c.getLocal("idx1"),
|
|
c.getLocal("idx2"),
|
|
c.getLocal("idx2"),
|
|
),
|
|
|
|
c.call(
|
|
opGtimesF,
|
|
U,
|
|
c.getLocal("pSConst"),
|
|
c.getLocal("idx1"),
|
|
),
|
|
|
|
c.call(
|
|
opGtimesF,
|
|
c.getLocal("idx2"),
|
|
W,
|
|
c.getLocal("idx2"),
|
|
),
|
|
|
|
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 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));
|
|
|
|
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")
|
|
)
|
|
),
|
|
|
|
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")
|
|
)
|
|
),
|
|
|
|
c.call(
|
|
opGtimesF,
|
|
c.getLocal("idx2"),
|
|
W,
|
|
T
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_copy",
|
|
c.getLocal("idx1"),
|
|
U
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_add",
|
|
U,
|
|
T,
|
|
c.getLocal("idx1"),
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_sub",
|
|
U,
|
|
T,
|
|
c.getLocal("idx2"),
|
|
),
|
|
|
|
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)
|
|
))
|
|
);
|
|
}
|
|
|
|
|
|
// 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 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.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.call(
|
|
opGtimesF,
|
|
c.getLocal("idx2"),
|
|
c.getLocal("factor"),
|
|
T
|
|
),
|
|
|
|
c.call(
|
|
opGtimesF,
|
|
c.getLocal("idx1"),
|
|
c.getLocal("factor"),
|
|
c.getLocal("idx2"),
|
|
),
|
|
|
|
c.call(
|
|
gPrefix + "_copy",
|
|
T,
|
|
c.getLocal("idx1"),
|
|
),
|
|
|
|
c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))),
|
|
c.br(0)
|
|
))
|
|
);
|
|
}
|
|
|
|
buildRev();
|
|
buildReversePermutation();
|
|
buildFinalInverse();
|
|
buildRawFFT();
|
|
buildLog2();
|
|
buildFFT();
|
|
buildIFFT();
|
|
buildFFTJoin();
|
|
buildFFTJoinExt();
|
|
buildFFTJoinExtInv();
|
|
buildFFTMix();
|
|
buildFFTFinal();
|
|
buildPrepareLagrangeEvaluation();
|
|
|
|
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");
|
|
|
|
};
|
|
|
|
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
var build_pol = function buildPol(module, prefix, prefixField) {
|
|
|
|
const n64 = module.modules[prefixField].n64;
|
|
const n8 = n64*8;
|
|
|
|
|
|
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();
|
|
|
|
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)
|
|
)
|
|
)
|
|
),
|
|
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)
|
|
))
|
|
);
|
|
}
|
|
|
|
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");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const aux = c.i32_const(module.alloc(n8));
|
|
|
|
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.setLocal("ncoefs", c.i32_load(c.getLocal("pp"))),
|
|
c.setLocal("pp", c.i32_add(c.getLocal("pp"), c.i32_const(4))),
|
|
|
|
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(
|
|
"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))),
|
|
|
|
|
|
c.call(
|
|
prefixField + "_mul",
|
|
c.getLocal("ps"),
|
|
c.getLocal("pp"),
|
|
aux
|
|
),
|
|
|
|
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.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)
|
|
))
|
|
);
|
|
|
|
}
|
|
|
|
buildZero();
|
|
buildConstructLC();
|
|
|
|
|
|
module.exportFunction(prefix + "_zero");
|
|
module.exportFunction(prefix + "_constructLC");
|
|
|
|
return prefix;
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
var build_qap = function buildQAP(module, prefix, prefixField) {
|
|
|
|
const n64 = module.modules[prefixField].n64;
|
|
const n8 = n64*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");
|
|
|
|
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.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.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_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.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.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)
|
|
)),
|
|
|
|
);
|
|
}
|
|
|
|
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();
|
|
|
|
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 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();
|
|
|
|
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)
|
|
))
|
|
);
|
|
}
|
|
|
|
buildBuildABC();
|
|
buildJoinABC();
|
|
buildBatchAdd();
|
|
|
|
module.exportFunction(prefix + "_buildABC");
|
|
module.exportFunction(prefix + "_joinABC");
|
|
module.exportFunction(prefix + "_batchAdd");
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
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));
|
|
|
|
f.addCode(
|
|
c.setLocal("pFrom", c.getLocal("pIn")),
|
|
c.setLocal("pTo", c.getLocal("pOut")),
|
|
);
|
|
|
|
// 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") )),
|
|
|
|
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))),
|
|
|
|
// 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)
|
|
))
|
|
);
|
|
|
|
module.exportFunction(fnName);
|
|
|
|
};
|
|
|
|
const bigInt$2 = BigIntegerExports;
|
|
const utils$6 = utils$b;
|
|
|
|
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$2 = build_fft;
|
|
const buildPol$1 = build_pol;
|
|
const buildQAP$1 = build_qap;
|
|
const buildApplyKey$1 = build_applykey;
|
|
|
|
var build_bn128 = function buildBN128(module, _prefix) {
|
|
|
|
const prefix = _prefix || "bn128";
|
|
|
|
if (module.modules[prefix]) return prefix; // already builded
|
|
|
|
const q = bigInt$2("21888242871839275222246405745257275088696311157297823662689037894645226208583");
|
|
const r = bigInt$2("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
|
|
|
|
|
const n64 = Math.floor((q.minus(1).bitLength() - 1)/64) +1;
|
|
const n8 = n64*8;
|
|
const frsize = n8;
|
|
const f1size = n8;
|
|
const f2size = f1size * 2;
|
|
const ftsize = f1size * 12;
|
|
|
|
const pr = module.alloc(utils$6.bigInt2BytesLE( r, frsize ));
|
|
|
|
const f1mPrefix = buildF1m$1(module, q, "f1m");
|
|
buildF1$1(module, r, "fr", "frm");
|
|
|
|
const pG1b = module.alloc(utils$6.bigInt2BytesLE( toMontgomery(bigInt$2(3)), f1size ));
|
|
const g1mPrefix = buildCurve$1(module, "g1m", "f1m", pG1b);
|
|
|
|
buildFFT$2(module, "frm", "frm", "frm", "frm_mul");
|
|
|
|
buildPol$1(module, "pol", "frm");
|
|
buildQAP$1(module, "qap", "frm");
|
|
|
|
const f2mPrefix = buildF2m$1(module, "f1m_neg", "f2m", "f1m");
|
|
const pG2b = module.alloc([
|
|
...utils$6.bigInt2BytesLE( toMontgomery(bigInt$2("19485874751759354771024239261021720505790618469301721065564631296452457478373")), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(bigInt$2("266929791119991161246907387137283842545076965332900288569378510910307636690")), f1size )
|
|
]);
|
|
const g2mPrefix = buildCurve$1(module, "g2m", "f2m", pG2b);
|
|
|
|
|
|
function buildGTimesFr(fnName, opMul) {
|
|
const f = module.addFunction(fnName);
|
|
f.addParam("pG", "i32");
|
|
f.addParam("pFr", "i32");
|
|
f.addParam("pr", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const AUX = c.i32_const(module.alloc(n8));
|
|
|
|
f.addCode(
|
|
c.call("frm_fromMontgomery", c.getLocal("pFr"), AUX),
|
|
c.call(
|
|
opMul,
|
|
c.getLocal("pG"),
|
|
AUX,
|
|
c.i32_const(n8),
|
|
c.getLocal("pr")
|
|
)
|
|
);
|
|
|
|
module.exportFunction(fnName);
|
|
}
|
|
buildGTimesFr("g1m_timesFr", "g1m_timesScalar");
|
|
buildFFT$2(module, "g1m", "g1m", "frm", "g1m_timesFr");
|
|
|
|
buildGTimesFr("g2m_timesFr", "g2m_timesScalar");
|
|
buildFFT$2(module, "g2m", "g2m", "frm", "g2m_timesFr");
|
|
|
|
buildGTimesFr("g1m_timesFrAffine", "g1m_timesScalarAffine");
|
|
buildGTimesFr("g2m_timesFrAffine", "g2m_timesScalarAffine");
|
|
|
|
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");
|
|
|
|
function toMontgomery(a) {
|
|
return bigInt$2(a).times( bigInt$2.one.shiftLeft(f1size*8)).mod(q);
|
|
}
|
|
|
|
const G1gen = [
|
|
bigInt$2("1"),
|
|
bigInt$2("2"),
|
|
bigInt$2.one
|
|
];
|
|
|
|
const pG1gen = module.alloc(
|
|
[
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G1gen[0]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G1gen[1]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G1gen[2]), f1size ),
|
|
]
|
|
);
|
|
|
|
const G1zero = [
|
|
bigInt$2.zero,
|
|
bigInt$2.one,
|
|
bigInt$2.zero
|
|
];
|
|
|
|
const pG1zero = module.alloc(
|
|
[
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G1zero[0]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G1zero[1]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G1zero[2]), f1size )
|
|
]
|
|
);
|
|
|
|
const G2gen = [
|
|
[
|
|
bigInt$2("10857046999023057135944570762232829481370756359578518086990519993285655852781"),
|
|
bigInt$2("11559732032986387107991004021392285783925812861821192530917403151452391805634"),
|
|
],[
|
|
bigInt$2("8495653923123431417604973247489272438418190587263600148770280649306958101930"),
|
|
bigInt$2("4082367875863433681332203403145435568316851327593401208105741076214120093531"),
|
|
],[
|
|
bigInt$2.one,
|
|
bigInt$2.zero,
|
|
]
|
|
];
|
|
|
|
const pG2gen = module.alloc(
|
|
[
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2gen[0][0]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2gen[0][1]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2gen[1][0]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2gen[1][1]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2gen[2][0]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2gen[2][1]), f1size ),
|
|
]
|
|
);
|
|
|
|
const G2zero = [
|
|
[
|
|
bigInt$2.zero,
|
|
bigInt$2.zero,
|
|
],[
|
|
bigInt$2.one,
|
|
bigInt$2.zero,
|
|
],[
|
|
bigInt$2.zero,
|
|
bigInt$2.zero,
|
|
]
|
|
];
|
|
|
|
const pG2zero = module.alloc(
|
|
[
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2zero[0][0]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2zero[0][1]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2zero[1][0]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2zero[1][1]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2zero[2][0]), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(G2zero[2][1]), f1size ),
|
|
]
|
|
);
|
|
|
|
const pOneT = module.alloc([
|
|
...utils$6.bigInt2BytesLE( toMontgomery(1), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
]);
|
|
|
|
const pNonResidueF6 = module.alloc([
|
|
...utils$6.bigInt2BytesLE( toMontgomery(9), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery(1), f1size ),
|
|
]);
|
|
|
|
const pTwoInv = module.alloc([
|
|
...utils$6.bigInt2BytesLE( toMontgomery( bigInt$2(2).modInv(q)), f1size ),
|
|
...utils$6.bigInt2BytesLE( bigInt$2(0), f1size )
|
|
]);
|
|
|
|
const pAltBn128Twist = pNonResidueF6;
|
|
|
|
const pTwistCoefB = module.alloc([
|
|
...utils$6.bigInt2BytesLE( toMontgomery("19485874751759354771024239261021720505790618469301721065564631296452457478373"), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery("266929791119991161246907387137283842545076965332900288569378510910307636690"), f1size ),
|
|
]);
|
|
|
|
function build_mulNR6() {
|
|
const f = module.addFunction(prefix + "_mulNR6");
|
|
f.addParam("x", "i32");
|
|
f.addParam("pr", "i32");
|
|
|
|
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 build_mulNR12() {
|
|
const f = module.addFunction(prefix + "_mulNR12");
|
|
f.addParam("x", "i32");
|
|
f.addParam("pr", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(
|
|
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 ateLoopCount = bigInt$2("29793968203157093288");
|
|
const ateLoopBitBytes = bits(ateLoopCount);
|
|
const pAteLoopBitBytes = module.alloc(ateLoopBitBytes);
|
|
|
|
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*n8;
|
|
const preQSize = 3*n8*2 + ateNCoefs*ateCoefSize;
|
|
|
|
|
|
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()
|
|
};
|
|
|
|
// console.log("PrePSize: " +prePSize);
|
|
// console.log("PreQSize: " +preQSize);
|
|
|
|
const finalExpZ = bigInt$2("4965661367192848881");
|
|
|
|
function naf(n) {
|
|
let E = n;
|
|
const res = [];
|
|
while (E.gt(bigInt$2.zero)) {
|
|
if (E.isOdd()) {
|
|
const z = 2 - E.mod(4).toJSNumber();
|
|
res.push( z );
|
|
E = E.minus(z);
|
|
} else {
|
|
res.push( 0 );
|
|
}
|
|
E = E.shiftRight(1);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function bits(n) {
|
|
let E = n;
|
|
const res = [];
|
|
while (E.gt(bigInt$2.zero)) {
|
|
if (E.isOdd()) {
|
|
res.push( 1 );
|
|
} else {
|
|
res.push( 0 );
|
|
}
|
|
E = E.shiftRight(1);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function buildPrepareG1() {
|
|
const f = module.addFunction(prefix+ "_prepareG1");
|
|
f.addParam("pP", "i32");
|
|
f.addParam("ppreP", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(
|
|
c.call(g1mPrefix + "_normalize", c.getLocal("pP"), c.getLocal("ppreP")), // TODO Remove if already in affine
|
|
);
|
|
}
|
|
|
|
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(
|
|
// D = X1 - X2*Z1
|
|
c.call(f2mPrefix + "_mul", X2, Z1, D),
|
|
c.call(f2mPrefix + "_sub", X1, D, D),
|
|
|
|
// E = Y1 - Y2*Z1
|
|
c.call(f2mPrefix + "_mul", Y2, Z1, E),
|
|
c.call(f2mPrefix + "_sub", Y1, E, E),
|
|
|
|
// 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),
|
|
|
|
// I = X1 * F
|
|
c.call(f2mPrefix + "_mul", X1, F, I),
|
|
|
|
// 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),
|
|
|
|
|
|
// X3 (X1) = D*J
|
|
c.call(f2mPrefix + "_mul", D, J, X1),
|
|
|
|
// 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),
|
|
|
|
// Z3 (Z1) = Z1*H
|
|
c.call(f2mPrefix + "_mul", Z1, H, Z1),
|
|
|
|
// 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),
|
|
|
|
|
|
// ell_VV = - E (later: * xP)
|
|
c.call(f2mPrefix + "_neg", E, ELL_VV),
|
|
|
|
// ell_VW = D (later: * yP )
|
|
// Already assigned
|
|
|
|
);
|
|
}
|
|
|
|
|
|
|
|
function buildPrepDoubleStep() {
|
|
const f = module.addFunction(prefix+ "_prepDblStep");
|
|
f.addParam("pR", "i32");
|
|
f.addParam("pCoef", "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 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));
|
|
|
|
f.addCode(
|
|
|
|
// 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),
|
|
|
|
// Z3 (Z1) = B * H
|
|
c.call(f2mPrefix + "_mul", B, H, Z1),
|
|
|
|
// ell_0 = xi * I
|
|
c.call(f2mPrefix + "_mul", c.i32_const(pAltBn128Twist), I, ELL_0),
|
|
|
|
// ell_VW = - H (later: * yP)
|
|
c.call(f2mPrefix + "_neg", H, ELL_VW),
|
|
|
|
// ell_VV = 3*J (later: * xP)
|
|
c.call(f2mPrefix + "_add", J, J, ELL_VV),
|
|
c.call(f2mPrefix + "_add", J, ELL_VV, ELL_VV),
|
|
|
|
);
|
|
}
|
|
|
|
function buildMulByQ() {
|
|
const f = module.addFunction(prefix + "_mulByQ");
|
|
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 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$6.bigInt2BytesLE( toMontgomery("21575463638280843010398324269430826099269044274347216827212613867836435027261"), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery("10307601595873709700152284273816112264069230130616436755625194854815875713954"), f1size ),
|
|
]));
|
|
|
|
const MulByQY = c.i32_const(module.alloc([
|
|
...utils$6.bigInt2BytesLE( toMontgomery("2821565182194536844548159561693502659359617185244120367078079554186484126554"), f1size ),
|
|
...utils$6.bigInt2BytesLE( toMontgomery("3505843767911556378687030309984248845540243509899259641013678093033130930403"), f1size ),
|
|
]));
|
|
|
|
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),
|
|
);
|
|
}
|
|
|
|
|
|
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();
|
|
|
|
const QX = c.getLocal("pQ");
|
|
c.i32_add( c.getLocal("pQ"), c.i32_const(f2size));
|
|
c.i32_add( c.getLocal("pQ"), c.i32_const(f2size*2));
|
|
|
|
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));
|
|
c.i32_add( c.getLocal("ppreQ"), c.i32_const(f2size*2));
|
|
|
|
const pQ1 = module.alloc(f2size*3);
|
|
const Q1 = c.i32_const(pQ1);
|
|
|
|
const pQ2 = module.alloc(f2size*3);
|
|
const Q2 = c.i32_const(pQ2);
|
|
c.i32_const(pQ2);
|
|
const Q2Y = c.i32_const(pQ2 + f2size);
|
|
c.i32_const(pQ2 + f2size*2);
|
|
|
|
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),
|
|
);
|
|
|
|
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)
|
|
))
|
|
);
|
|
|
|
f.addCode(
|
|
c.call(prefix + "_mulByQ", cQX, Q1),
|
|
c.call(prefix + "_mulByQ", Q1, Q2)
|
|
);
|
|
|
|
f.addCode(
|
|
c.call(f2mPrefix + "_neg", Q2Y, Q2Y),
|
|
|
|
c.call(prefix + "_prepAddStep", Q1, R, c.getLocal("pCoef")),
|
|
c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))),
|
|
|
|
c.call(prefix + "_prepAddStep", Q2, R, c.getLocal("pCoef")),
|
|
c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))),
|
|
);
|
|
}
|
|
|
|
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");
|
|
|
|
const z0 = c.getLocal("pR");
|
|
|
|
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(
|
|
|
|
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 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 c = f.getCodeBuilder();
|
|
|
|
const x0 = c.getLocal("pEll0");
|
|
const x2 = c.getLocal("pEllVV");
|
|
const x4 = c.getLocal("pEllVW");
|
|
|
|
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));
|
|
|
|
f.addCode(
|
|
|
|
// 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 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;
|
|
|
|
// 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;
|
|
|
|
);
|
|
}
|
|
|
|
|
|
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_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");
|
|
|
|
|
|
f.addCode(
|
|
c.call(ftmPrefix + "_one", F),
|
|
|
|
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(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.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),
|
|
|
|
...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)
|
|
))
|
|
|
|
);
|
|
|
|
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 buildFrobeniusMap(n) {
|
|
const F12 = [
|
|
[
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
],
|
|
[
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("8376118865763821496583973867626364092589906065868298776909617916018768340080"), bigInt$2("16469823323077808223889137241176536799009286646108169935659301613961712198316")],
|
|
[bigInt$2("21888242871839275220042445260109153167277707414472061641714758635765020556617"), bigInt$2("0")],
|
|
[bigInt$2("11697423496358154304825782922584725312912383441159505038794027105778954184319"), bigInt$2("303847389135065887422783454877609941456349188919719272345083954437860409601")],
|
|
[bigInt$2("21888242871839275220042445260109153167277707414472061641714758635765020556616"), bigInt$2("0")],
|
|
[bigInt$2("3321304630594332808241809054958361220322477375291206261884409189760185844239"), bigInt$2("5722266937896532885780051958958348231143373700109372999374820235121374419868")],
|
|
[bigInt$2("21888242871839275222246405745257275088696311157297823662689037894645226208582"), bigInt$2("0")],
|
|
[bigInt$2("13512124006075453725662431877630910996106405091429524885779419978626457868503"), bigInt$2("5418419548761466998357268504080738289687024511189653727029736280683514010267")],
|
|
[bigInt$2("2203960485148121921418603742825762020974279258880205651966"), bigInt$2("0")],
|
|
[bigInt$2("10190819375481120917420622822672549775783927716138318623895010788866272024264"), bigInt$2("21584395482704209334823622290379665147239961968378104390343953940207365798982")],
|
|
[bigInt$2("2203960485148121921418603742825762020974279258880205651967"), bigInt$2("0")],
|
|
[bigInt$2("18566938241244942414004596690298913868373833782006617400804628704885040364344"), bigInt$2("16165975933942742336466353786298926857552937457188450663314217659523851788715")],
|
|
]
|
|
];
|
|
|
|
const F6 = [
|
|
[
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
],
|
|
[
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("21575463638280843010398324269430826099269044274347216827212613867836435027261"), bigInt$2("10307601595873709700152284273816112264069230130616436755625194854815875713954")],
|
|
[bigInt$2("21888242871839275220042445260109153167277707414472061641714758635765020556616"), bigInt$2("0")],
|
|
[bigInt$2("3772000881919853776433695186713858239009073593817195771773381919316419345261"), bigInt$2("2236595495967245188281701248203181795121068902605861227855261137820944008926")],
|
|
[bigInt$2("2203960485148121921418603742825762020974279258880205651966"), bigInt$2("0")],
|
|
[bigInt$2("18429021223477853657660792034369865839114504446431234726392080002137598044644"), bigInt$2("9344045779998320333812420223237981029506012124075525679208581902008406485703")],
|
|
],
|
|
[
|
|
[bigInt$2("1"), bigInt$2("0")],
|
|
[bigInt$2("2581911344467009335267311115468803099551665605076196740867805258568234346338"), bigInt$2("19937756971775647987995932169929341994314640652964949448313374472400716661030")],
|
|
[bigInt$2("2203960485148121921418603742825762020974279258880205651966"), bigInt$2("0")],
|
|
[bigInt$2("5324479202449903542726783395506214481928257762400643279780343368557297135718"), bigInt$2("16208900380737693084919495127334387981393726419856888799917914180988844123039")],
|
|
[bigInt$2("21888242871839275220042445260109153167277707414472061641714758635765020556616"), bigInt$2("0")],
|
|
[bigInt$2("13981852324922362344252311234282257507216387789820983642040889267519694726527"), bigInt$2("7629828391165209371577384193250820201684255241773809077146787135900891633097")],
|
|
]
|
|
];
|
|
|
|
const f = module.addFunction(prefix+ "__frobeniusMap"+n);
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "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$6.bigInt2BytesLE(toMontgomery(coef[0]), 32),
|
|
...utils$6.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$2(a[0]);
|
|
const ac1 = bigInt$2(a[1]);
|
|
const bc0 = bigInt$2(b[0]);
|
|
const bc1 = bigInt$2(b[1]);
|
|
const res = [
|
|
ac0.times(bc0).minus( ac1.times(bc1) ).mod(q),
|
|
ac0.times(bc1).add( ac1.times(bc0) ).mod(q),
|
|
];
|
|
if (res[0].isNegative()) res[0] = res[0].add(q);
|
|
return res;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function buildFinalExponentiationFirstChunk() {
|
|
|
|
const f = module.addFunction(prefix+ "__finalExponentiationFirstChunk");
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "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));
|
|
|
|
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),
|
|
|
|
// const alt_bn128_Fq12 B = elt.inverse();
|
|
c.call(ftmPrefix + "_inverse", elt, B),
|
|
|
|
// 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),
|
|
);
|
|
}
|
|
|
|
function buildCyclotomicSquare() {
|
|
const f = module.addFunction(prefix+ "__cyclotomicSquare");
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "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 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));
|
|
|
|
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));
|
|
|
|
|
|
f.addCode(
|
|
|
|
// c.call(ftmPrefix + "_square", x0, r0),
|
|
|
|
// // 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),
|
|
|
|
// // 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),
|
|
|
|
// // 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),
|
|
|
|
// 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),
|
|
|
|
// 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),
|
|
|
|
);
|
|
}
|
|
|
|
|
|
function buildCyclotomicExp(exponent, fnName) {
|
|
const exponentNafBytes = naf(exponent).map( (b) => (b==-1 ? 0xFF: b) );
|
|
const pExponentNafBytes = module.alloc(exponentNafBytes);
|
|
module.alloc(utils$6.bigInt2BytesLE(exponent, 32));
|
|
|
|
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();
|
|
|
|
const x = c.getLocal("x");
|
|
|
|
const res = c.getLocal("r");
|
|
|
|
const inverse = c.i32_const(module.alloc(ftsize));
|
|
|
|
|
|
f.addCode(
|
|
// c.call(ftmPrefix + "_exp", x, c.i32_const(pExponent), c.i32_const(32), res),
|
|
|
|
c.call(ftmPrefix + "_conjugate", x, inverse),
|
|
c.call(ftmPrefix + "_one", res),
|
|
|
|
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),
|
|
)
|
|
),
|
|
|
|
c.setLocal("i", c.i32_const(exponentNafBytes.length-2)),
|
|
c.block(c.loop(
|
|
// c.call(ftmPrefix + "_square", res, res),
|
|
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)
|
|
))
|
|
);
|
|
}
|
|
|
|
|
|
|
|
function buildFinalExponentiationLastChunk() {
|
|
buildCyclotomicSquare();
|
|
buildCyclotomicExp(finalExpZ, "w0");
|
|
|
|
const f = module.addFunction(prefix+ "__finalExponentiationLastChunk");
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
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(
|
|
|
|
|
|
// 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),
|
|
|
|
// N = M * elt // = elt^(12*z^3 + 12*z^2 + 6*z + 1)
|
|
c.call(ftmPrefix + "_mul", M, elt, N),
|
|
|
|
// 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
|
|
);
|
|
}
|
|
|
|
|
|
function buildFinalExponentiation() {
|
|
buildFinalExponentiationFirstChunk();
|
|
buildFinalExponentiationLastChunk();
|
|
const f = module.addFunction(prefix+ "_finalExponentiation");
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const elt = c.getLocal("x");
|
|
const result = c.getLocal("r");
|
|
const eltToFirstChunk = c.i32_const(module.alloc(ftsize));
|
|
|
|
f.addCode(
|
|
c.call(prefix + "__finalExponentiationFirstChunk", elt, eltToFirstChunk ),
|
|
c.call(prefix + "__finalExponentiationLastChunk", eltToFirstChunk, result )
|
|
);
|
|
}
|
|
|
|
|
|
function buildFinalExponentiationOld() {
|
|
const f = module.addFunction(prefix+ "_finalExponentiationOld");
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "i32");
|
|
|
|
const exponent = bigInt$2("552484233613224096312617126783173147097382103762957654188882734314196910839907541213974502761540629817009608548654680343627701153829446747810907373256841551006201639677726139946029199968412598804882391702273019083653272047566316584365559776493027495458238373902875937659943504873220554161550525926302303331747463515644711876653177129578303191095900909191624817826566688241804408081892785725967931714097716709526092261278071952560171111444072049229123565057483750161460024353346284167282452756217662335528813519139808291170539072125381230815729071544861602750936964829313608137325426383735122175229541155376346436093930287402089517426973178917569713384748081827255472576937471496195752727188261435633271238710131736096299798168852925540549342330775279877006784354801422249722573783561685179618816480037695005515426162362431072245638324744480");
|
|
|
|
const pExponent = module.alloc(utils$6.bigInt2BytesLE( exponent, 352 ));
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(
|
|
c.call(ftmPrefix + "_exp", c.getLocal("x"), c.i32_const(pExponent), c.i32_const(352), 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<nPairings; i++) {
|
|
f.addParam("p_"+i, "i32");
|
|
f.addParam("q_"+i, "i32");
|
|
}
|
|
f.addParam("c", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const resT = c.i32_const(module.alloc(ftsize));
|
|
const auxT = c.i32_const(module.alloc(ftsize));
|
|
|
|
f.addCode(c.call(ftmPrefix + "_one", resT ));
|
|
|
|
for (let i=0; i<nPairings; i++) {
|
|
|
|
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) ));
|
|
f.addCode(c.call(prefix + "_millerLoop", c.i32_const(pPreP), c.i32_const(pPreQ), auxT ));
|
|
|
|
f.addCode(c.call(ftmPrefix + "_mul", resT, auxT, resT ));
|
|
}
|
|
|
|
f.addCode(c.call(prefix + "_finalExponentiation", resT, resT ));
|
|
|
|
f.addCode(c.call(ftmPrefix + "_eq", resT, c.getLocal("c")));
|
|
}
|
|
|
|
|
|
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") ));
|
|
}
|
|
|
|
|
|
buildPrepAddStep();
|
|
buildPrepDoubleStep();
|
|
|
|
buildPrepareG1();
|
|
buildPrepareG2();
|
|
|
|
buildMulBy024();
|
|
buildMulBy024Old();
|
|
buildMillerLoop();
|
|
|
|
|
|
for (let i=0; i<10; i++) {
|
|
buildFrobeniusMap(i);
|
|
module.exportFunction(prefix + "__frobeniusMap"+i);
|
|
}
|
|
|
|
buildFinalExponentiationOld();
|
|
buildFinalExponentiation();
|
|
|
|
for (let i=1; i<=5; i++) {
|
|
buildPairingEquation(i);
|
|
module.exportFunction(prefix + "_pairingEq"+i);
|
|
}
|
|
|
|
buildPairing();
|
|
|
|
module.exportFunction(prefix + "_pairing");
|
|
|
|
module.exportFunction(prefix + "_prepareG1");
|
|
module.exportFunction(prefix + "_prepareG2");
|
|
module.exportFunction(prefix + "_millerLoop");
|
|
module.exportFunction(prefix + "_finalExponentiation");
|
|
module.exportFunction(prefix + "_finalExponentiationOld");
|
|
module.exportFunction(prefix + "__mulBy024");
|
|
module.exportFunction(prefix + "__mulBy024Old");
|
|
module.exportFunction(prefix + "__cyclotomicSquare");
|
|
module.exportFunction(prefix + "__cyclotomicExp_w0");
|
|
|
|
// console.log(module.functionIdxByName);
|
|
|
|
};
|
|
|
|
const bigInt$1 = BigIntegerExports;
|
|
const utils$5 = utils$b;
|
|
|
|
const buildF1m =build_f1m;
|
|
const buildF1 =build_f1;
|
|
const buildF2m =build_f2m;
|
|
const buildF3m =build_f3m;
|
|
const buildCurve =build_curve_jacobian_a0;
|
|
const buildFFT$1 = build_fft;
|
|
const buildPol = build_pol;
|
|
const buildQAP = build_qap;
|
|
const buildApplyKey = build_applykey;
|
|
|
|
// Definition here: https://electriccoin.co/blog/new-snark-curve/
|
|
|
|
var build_bls12381 = function buildBLS12381(module, _prefix) {
|
|
|
|
const prefix = _prefix || "bls12381";
|
|
|
|
if (module.modules[prefix]) return prefix; // already builded
|
|
|
|
const q = bigInt$1("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", 16);
|
|
const r = bigInt$1("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", 16);
|
|
|
|
const n64q = Math.floor((q.minus(1).bitLength() - 1)/64) +1;
|
|
const n8q = n64q*8;
|
|
const f1size = n8q;
|
|
const f2size = f1size * 2;
|
|
const ftsize = f1size * 12;
|
|
|
|
const n64r = Math.floor((r.minus(1).bitLength() - 1)/64) +1;
|
|
const n8r = n64r*8;
|
|
const frsize = n8r;
|
|
|
|
|
|
const pr = module.alloc(utils$5.bigInt2BytesLE( r, frsize ));
|
|
|
|
const f1mPrefix = buildF1m(module, q, "f1m", "intq");
|
|
buildF1(module, r, "fr", "frm", "intr");
|
|
const pG1b = module.alloc(utils$5.bigInt2BytesLE( toMontgomery(bigInt$1(4)), f1size ));
|
|
const g1mPrefix = buildCurve(module, "g1m", "f1m", pG1b);
|
|
|
|
buildFFT$1(module, "frm", "frm", "frm", "frm_mul");
|
|
|
|
buildPol(module, "pol", "frm");
|
|
buildQAP(module, "qap", "frm");
|
|
|
|
const f2mPrefix = buildF2m(module, "f1m_neg", "f2m", "f1m");
|
|
const pG2b = module.alloc([
|
|
...utils$5.bigInt2BytesLE( toMontgomery(bigInt$1("4")), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(bigInt$1("4")), f1size )
|
|
]);
|
|
const g2mPrefix = buildCurve(module, "g2m", "f2m", pG2b);
|
|
|
|
|
|
function buildGTimesFr(fnName, opMul) {
|
|
const f = module.addFunction(fnName);
|
|
f.addParam("pG", "i32");
|
|
f.addParam("pFr", "i32");
|
|
f.addParam("pr", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const AUX = c.i32_const(module.alloc(n8r));
|
|
|
|
f.addCode(
|
|
c.call("frm_fromMontgomery", c.getLocal("pFr"), AUX),
|
|
c.call(
|
|
opMul,
|
|
c.getLocal("pG"),
|
|
AUX,
|
|
c.i32_const(n8r),
|
|
c.getLocal("pr")
|
|
)
|
|
);
|
|
|
|
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");
|
|
|
|
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");
|
|
|
|
|
|
function toMontgomery(a) {
|
|
return bigInt$1(a).times( bigInt$1.one.shiftLeft(f1size*8)).mod(q);
|
|
}
|
|
|
|
const G1gen = [
|
|
bigInt$1("3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507"),
|
|
bigInt$1("1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569"),
|
|
bigInt$1.one
|
|
];
|
|
|
|
const pG1gen = module.alloc(
|
|
[
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G1gen[0]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G1gen[1]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G1gen[2]), f1size ),
|
|
]
|
|
);
|
|
|
|
const G1zero = [
|
|
bigInt$1.zero,
|
|
bigInt$1.one,
|
|
bigInt$1.zero
|
|
];
|
|
|
|
const pG1zero = module.alloc(
|
|
[
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G1zero[0]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G1zero[1]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G1zero[2]), f1size )
|
|
]
|
|
);
|
|
|
|
const G2gen = [
|
|
[
|
|
bigInt$1("352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160"),
|
|
bigInt$1("3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758"),
|
|
],[
|
|
bigInt$1("1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905"),
|
|
bigInt$1("927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582"),
|
|
],[
|
|
bigInt$1.one,
|
|
bigInt$1.zero,
|
|
]
|
|
];
|
|
|
|
const pG2gen = module.alloc(
|
|
[
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2gen[0][0]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2gen[0][1]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2gen[1][0]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2gen[1][1]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2gen[2][0]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2gen[2][1]), f1size ),
|
|
]
|
|
);
|
|
|
|
const G2zero = [
|
|
[
|
|
bigInt$1.zero,
|
|
bigInt$1.zero,
|
|
],[
|
|
bigInt$1.one,
|
|
bigInt$1.zero,
|
|
],[
|
|
bigInt$1.zero,
|
|
bigInt$1.zero,
|
|
]
|
|
];
|
|
|
|
const pG2zero = module.alloc(
|
|
[
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2zero[0][0]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2zero[0][1]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2zero[1][0]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2zero[1][1]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2zero[2][0]), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(G2zero[2][1]), f1size ),
|
|
]
|
|
);
|
|
|
|
const pOneT = module.alloc([
|
|
...utils$5.bigInt2BytesLE( toMontgomery(1), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(0), f1size ),
|
|
]);
|
|
|
|
module.alloc([
|
|
...utils$5.bigInt2BytesLE( toMontgomery( bigInt$1(2).modInv(q)), f1size ),
|
|
...utils$5.bigInt2BytesLE( bigInt$1(0), f1size )
|
|
]);
|
|
|
|
const pBls12381Twist = module.alloc([
|
|
...utils$5.bigInt2BytesLE( toMontgomery(1), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery(1), f1size ),
|
|
]);
|
|
|
|
module.alloc([
|
|
...utils$5.bigInt2BytesLE( toMontgomery("4"), f1size ),
|
|
...utils$5.bigInt2BytesLE( toMontgomery("4"), f1size ),
|
|
]);
|
|
|
|
function build_mulNR2() {
|
|
const f = module.addFunction(f2mPrefix + "_mulNR");
|
|
f.addParam("x", "i32");
|
|
f.addParam("pr", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
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));
|
|
|
|
f.addCode(
|
|
c.call(f1mPrefix+"_copy", x0, x0c),
|
|
c.call(f1mPrefix+"_sub", x0, x1, r0),
|
|
c.call(f1mPrefix+"_add", x0c, x1, r1),
|
|
);
|
|
}
|
|
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");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const c0copy = c.i32_const(module.alloc(f1size*2));
|
|
|
|
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.call(
|
|
f2mPrefix + "_copy",
|
|
c0copy,
|
|
c.i32_add(c.getLocal("pr"), c.i32_const(n8q*2)),
|
|
),
|
|
);
|
|
}
|
|
build_mulNR6();
|
|
|
|
const ftmPrefix = buildF2m(module, f6mPrefix+"_mulNR", "ftm", f6mPrefix);
|
|
|
|
const ateLoopCount = bigInt$1("d201000000010000", 16);
|
|
const ateLoopBitBytes = bits(ateLoopCount);
|
|
const pAteLoopBitBytes = module.alloc(ateLoopBitBytes);
|
|
|
|
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 finalExpZ = bigInt$1("15132376222941642752");
|
|
|
|
|
|
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.gt(bigInt$1.zero)) {
|
|
if (E.isOdd()) {
|
|
const z = 2 - E.mod(4).toJSNumber();
|
|
res.push( z );
|
|
E = E.minus(z);
|
|
} else {
|
|
res.push( 0 );
|
|
}
|
|
E = E.shiftRight(1);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function bits(n) {
|
|
let E = n;
|
|
const res = [];
|
|
while (E.gt(bigInt$1.zero)) {
|
|
if (E.isOdd()) {
|
|
res.push( 1 );
|
|
} else {
|
|
res.push( 0 );
|
|
}
|
|
E = E.shiftRight(1);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function buildPrepareG1() {
|
|
const f = module.addFunction(prefix+ "_prepareG1");
|
|
f.addParam("pP", "i32");
|
|
f.addParam("ppreP", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(
|
|
c.call(g1mPrefix + "_normalize", c.getLocal("pP"), c.getLocal("ppreP")), // TODO Remove if already in affine
|
|
);
|
|
}
|
|
|
|
|
|
|
|
function buildPrepDoubleStep() {
|
|
const f = module.addFunction(prefix+ "_prepDblStep");
|
|
f.addParam("R", "i32");
|
|
f.addParam("r", "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));
|
|
|
|
f.addCode(
|
|
|
|
// tmp0 = r.x.square();
|
|
c.call(f2mPrefix + "_square", Rx, t0),
|
|
|
|
// tmp1 = r.y.square();
|
|
c.call(f2mPrefix + "_square", Ry, t1),
|
|
|
|
// tmp2 = tmp1.square();
|
|
c.call(f2mPrefix + "_square", t1, t2),
|
|
|
|
// 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),
|
|
|
|
// tmp3 = tmp3 + tmp3;
|
|
c.call(f2mPrefix + "_add", t3, t3, t3),
|
|
|
|
// tmp4 = tmp0 + tmp0 + tmp0;
|
|
c.call(f2mPrefix + "_add", t0, t0, t4),
|
|
c.call(f2mPrefix + "_add", t4, t0, t4),
|
|
|
|
// tmp6 = r.x + tmp4;
|
|
c.call(f2mPrefix + "_add", Rx, t4, t6),
|
|
|
|
// tmp5 = tmp4.square();
|
|
c.call(f2mPrefix + "_square", t4, t5),
|
|
|
|
// zsquared = r.z.square();
|
|
c.call(f2mPrefix + "_square", Rz, zsquared),
|
|
|
|
// 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),
|
|
|
|
// 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),
|
|
|
|
// tmp2 = tmp2 + tmp2;
|
|
c.call(f2mPrefix + "_add", t2, t2, t2),
|
|
|
|
// r.y -= tmp2;
|
|
c.call(f2mPrefix + "_sub", Ry, t2, Ry),
|
|
|
|
// tmp3 = tmp4 * zsquared;
|
|
c.call(f2mPrefix + "_mul", t4, zsquared, t3),
|
|
|
|
// tmp3 = tmp3 + tmp3;
|
|
c.call(f2mPrefix + "_add", t3, t3, t3),
|
|
|
|
// 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),
|
|
|
|
// tmp1 = tmp1 + tmp1;
|
|
c.call(f2mPrefix + "_add", t1, t1, t1),
|
|
|
|
// tmp1 = tmp1 + tmp1;
|
|
c.call(f2mPrefix + "_add", t1, t1, t1),
|
|
|
|
// tmp6 = tmp6 - tmp1;
|
|
c.call(f2mPrefix + "_sub", t6, t1, t6),
|
|
|
|
// tmp0 = r.z * zsquared;
|
|
c.call(f2mPrefix + "_mul", Rz, zsquared, t0),
|
|
|
|
// tmp0 = tmp0 + tmp0;
|
|
c.call(f2mPrefix + "_add", t0, t0, t0),
|
|
|
|
);
|
|
}
|
|
|
|
function buildPrepAddStep() {
|
|
const f = module.addFunction(prefix+ "_prepAddStep");
|
|
f.addParam("R", "i32");
|
|
f.addParam("Q", "i32");
|
|
f.addParam("r", "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 Qx = c.getLocal("Q");
|
|
const Qy = c.i32_add(c.getLocal("Q"), c.i32_const(2*n8q));
|
|
|
|
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));
|
|
|
|
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(
|
|
|
|
// zsquared = r.z.square();
|
|
c.call(f2mPrefix + "_square", Rz, zsquared),
|
|
|
|
// ysquared = q.y.square();
|
|
c.call(f2mPrefix + "_square", Qy, ysquared),
|
|
|
|
// t0 = zsquared * q.x;
|
|
c.call(f2mPrefix + "_mul", zsquared, Qx, t0),
|
|
|
|
// 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),
|
|
|
|
// t2 = t0 - r.x;
|
|
c.call(f2mPrefix + "_sub", t0, Rx, t2),
|
|
|
|
// t3 = t2.square();
|
|
c.call(f2mPrefix + "_square", t2, t3),
|
|
|
|
// t4 = t3 + t3;
|
|
c.call(f2mPrefix + "_add", t3, t3, t4),
|
|
|
|
// t4 = t4 + t4;
|
|
c.call(f2mPrefix + "_add", t4, t4, t4),
|
|
|
|
// 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),
|
|
|
|
// t7 = t4 * r.x;
|
|
c.call(f2mPrefix + "_mul", t4, Rx, t7),
|
|
|
|
// 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),
|
|
|
|
// 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),
|
|
|
|
// t10 = q.y + r.z;
|
|
c.call(f2mPrefix + "_add", Qy, Rz, t10),
|
|
|
|
// 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),
|
|
|
|
// t0 = t0 + t0;
|
|
c.call(f2mPrefix + "_add", t0, t0, t0),
|
|
|
|
// r.y = t8 - t0;
|
|
c.call(f2mPrefix + "_sub", t8, t0, Ry),
|
|
|
|
// 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),
|
|
|
|
// t10 = t10 - ztsquared;
|
|
c.call(f2mPrefix + "_sub", t10, ztsquared, t10),
|
|
|
|
// t9 = t9 + t9 - t10;
|
|
c.call(f2mPrefix + "_add", t9, t9, t9),
|
|
c.call(f2mPrefix + "_sub", t9, t10, t9),
|
|
|
|
// t10 = r.z + r.z;
|
|
c.call(f2mPrefix + "_add", Rz, Rz, t10),
|
|
|
|
// t6 = -t6;
|
|
c.call(f2mPrefix + "_neg", t6, t6),
|
|
|
|
// t1 = t6 + t6;
|
|
c.call(f2mPrefix + "_add", t6, t6, t1),
|
|
);
|
|
}
|
|
|
|
|
|
function buildPrepareG2() {
|
|
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();
|
|
|
|
|
|
const Q = c.getLocal("pQ");
|
|
|
|
const pR = module.alloc(f2size*3);
|
|
const R = c.i32_const(pR);
|
|
|
|
const base = c.getLocal("ppreQ");
|
|
|
|
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))),
|
|
);
|
|
|
|
f.addCode(
|
|
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", 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.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
|
|
|
|
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));
|
|
|
|
f.addCode(
|
|
|
|
c.call(f2mPrefix + "_add", A_c0, A_c1, Ac0_Ac1),
|
|
c.call(f2mPrefix + "_add", A_c1, A_c2, Ac1_Ac2),
|
|
|
|
// let b_b = self.c1 * c1;
|
|
c.call(f2mPrefix + "_mul", A_c1, c1, b_b),
|
|
|
|
// 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),
|
|
|
|
// let t1 = t1.mul_by_nonresidue();
|
|
c.call(f2mPrefix + "_mulNR", t1, t1),
|
|
|
|
// 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();
|
|
|
|
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),
|
|
|
|
// 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),
|
|
|
|
|
|
);
|
|
}
|
|
buildF6Mul01();
|
|
|
|
|
|
function buildF12Mul014() {
|
|
|
|
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
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
|
|
const A_c0 = c.getLocal("pA");
|
|
const A_c1 = c.i32_add(c.getLocal("pA"), c.i32_const(f1size*6));
|
|
|
|
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));
|
|
|
|
const R_c0 = c.getLocal("pR");
|
|
const R_c1 = c.i32_add(c.getLocal("pR"), c.i32_const(f1size*6));
|
|
|
|
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),
|
|
|
|
// let o = c1 + c4;
|
|
c.call(f2mPrefix + "_add", c1, c4, o),
|
|
|
|
// let c1 = self.c1 + self.c0;
|
|
c.call(f6mPrefix + "_add", A_c1, A_c0, R_c1),
|
|
|
|
// let c1 = c1.mul_by_01(c0, &o);
|
|
c.call(f6mPrefix + "_mul01", R_c1, c0, o, R_c1),
|
|
|
|
// let c1 = c1 - aa - bb;
|
|
c.call(f6mPrefix + "_sub", R_c1, aa, R_c1),
|
|
c.call(f6mPrefix + "_sub", R_c1, bb, R_c1),
|
|
|
|
// let c0 = bb;
|
|
c.call(f6mPrefix + "_copy", bb, R_c0),
|
|
|
|
// let c0 = c0.mul_by_nonresidue();
|
|
c.call(f6mPrefix + "_mulNR", R_c0, R_c0),
|
|
|
|
// let c0 = c0 + aa;
|
|
c.call(f6mPrefix + "_add", R_c0, aa, R_c0),
|
|
);
|
|
}
|
|
buildF12Mul014();
|
|
|
|
|
|
function buildELL() {
|
|
const f = module.addFunction(prefix+ "_ell");
|
|
f.addParam("pP", "i32");
|
|
f.addParam("pCoefs", "i32");
|
|
f.addParam("pF", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const Px = c.getLocal("pP");
|
|
const Py = c.i32_add(c.getLocal("pP"), c.i32_const(n8q));
|
|
|
|
const F = c.getLocal("pF");
|
|
|
|
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));
|
|
|
|
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)
|
|
|
|
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),
|
|
|
|
);
|
|
|
|
}
|
|
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");
|
|
c.getLocal("ppreQ");
|
|
|
|
const coefs = c.getLocal("pCoef");
|
|
|
|
const F = c.getLocal("r");
|
|
|
|
|
|
f.addCode(
|
|
c.call(ftmPrefix + "_one", F),
|
|
|
|
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))),
|
|
|
|
c.setLocal("i", c.i32_const(ateLoopBitBytes.length-2)),
|
|
c.block(c.loop(
|
|
|
|
|
|
c.call(prefix + "_ell", preP, coefs, F),
|
|
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 + "_ell", preP, coefs, F),
|
|
...c.setLocal("pCoef", c.i32_add(c.getLocal("pCoef"), c.i32_const(ateCoefSize))),
|
|
]
|
|
),
|
|
c.call(ftmPrefix + "_square", F, F),
|
|
|
|
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.br(0)
|
|
)),
|
|
c.call(prefix + "_ell", preP, coefs, F),
|
|
|
|
);
|
|
|
|
|
|
{
|
|
f.addCode(
|
|
c.call(ftmPrefix + "_conjugate", F, F),
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
function buildFrobeniusMap(n) {
|
|
const F12 = [
|
|
[
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
],
|
|
[
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("3850754370037169011952147076051364057158807420970682438676050522613628423219637725072182697113062777891589506424760"), bigInt$1("151655185184498381465642749684540099398075398968325446656007613510403227271200139370504932015952886146304766135027")],
|
|
[bigInt$1("793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620351"), bigInt$1("0")],
|
|
[bigInt$1("2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530"), bigInt$1("1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257")],
|
|
[bigInt$1("793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350"), bigInt$1("0")],
|
|
[bigInt$1("3125332594171059424908108096204648978570118281977575435832422631601824034463382777937621250592425535493320683825557"), bigInt$1("877076961050607968509681729531255177986764537961432449499635504522207616027455086505066378536590128544573588734230")],
|
|
[bigInt$1("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559786"), bigInt$1("0")],
|
|
[bigInt$1("151655185184498381465642749684540099398075398968325446656007613510403227271200139370504932015952886146304766135027"), bigInt$1("3850754370037169011952147076051364057158807420970682438676050522613628423219637725072182697113062777891589506424760")],
|
|
[bigInt$1("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436"), bigInt$1("0")],
|
|
[bigInt$1("1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257"), bigInt$1("2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530")],
|
|
[bigInt$1("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939437"), bigInt$1("0")],
|
|
[bigInt$1("877076961050607968509681729531255177986764537961432449499635504522207616027455086505066378536590128544573588734230"), bigInt$1("3125332594171059424908108096204648978570118281977575435832422631601824034463382777937621250592425535493320683825557")],
|
|
]
|
|
];
|
|
|
|
const F6 = [
|
|
[
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
],
|
|
[
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("0"), bigInt$1("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436")],
|
|
[bigInt$1("793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350"), bigInt$1("0")],
|
|
[bigInt$1("0"), bigInt$1("1")],
|
|
[bigInt$1("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436"), bigInt$1("0")],
|
|
[bigInt$1("0"), bigInt$1("793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350")],
|
|
],
|
|
[
|
|
[bigInt$1("1"), bigInt$1("0")],
|
|
[bigInt$1("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939437"), bigInt$1("0")],
|
|
[bigInt$1("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436"), bigInt$1("0")],
|
|
[bigInt$1("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559786"), bigInt$1("0")],
|
|
[bigInt$1("793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350"), bigInt$1("0")],
|
|
[bigInt$1("793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620351"), bigInt$1("0")],
|
|
]
|
|
];
|
|
|
|
const f = module.addFunction(ftmPrefix + "_frobeniusMap"+n);
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "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$5.bigInt2BytesLE(toMontgomery(coef[0]), n8q),
|
|
...utils$5.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 = bigInt$1(a[0]);
|
|
const ac1 = bigInt$1(a[1]);
|
|
const bc0 = bigInt$1(b[0]);
|
|
const bc1 = bigInt$1(b[1]);
|
|
const res = [
|
|
ac0.times(bc0).minus( ac1.times(bc1) ).mod(q),
|
|
ac0.times(bc1).add( ac1.times(bc0) ).mod(q),
|
|
];
|
|
if (res[0].isNegative()) res[0] = res[0].add(q);
|
|
return res;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
function buildCyclotomicSquare() {
|
|
const f = module.addFunction(prefix+ "__cyclotomicSquare");
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "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 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));
|
|
|
|
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));
|
|
|
|
|
|
f.addCode(
|
|
|
|
// c.call(ftmPrefix + "_square", x0, r0),
|
|
|
|
// // 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),
|
|
|
|
// // 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),
|
|
|
|
// 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(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),
|
|
|
|
// 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),
|
|
|
|
);
|
|
}
|
|
|
|
|
|
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));
|
|
|
|
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();
|
|
|
|
const x = c.getLocal("x");
|
|
|
|
const res = c.getLocal("r");
|
|
|
|
const inverse = c.i32_const(module.alloc(ftsize));
|
|
|
|
|
|
f.addCode(
|
|
// c.call(ftmPrefix + "_exp", x, c.i32_const(pExponent), c.i32_const(32), res),
|
|
|
|
c.call(ftmPrefix + "_conjugate", x, inverse),
|
|
c.call(ftmPrefix + "_one", res),
|
|
|
|
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),
|
|
)
|
|
),
|
|
|
|
c.setLocal("i", c.i32_const(exponentNafBytes.length-2)),
|
|
c.block(c.loop(
|
|
// c.call(ftmPrefix + "_square", res, res),
|
|
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 (isExpNegative) {
|
|
f.addCode(
|
|
c.call(ftmPrefix + "_conjugate", res, res),
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
function buildFinalExponentiation() {
|
|
buildCyclotomicSquare();
|
|
buildCyclotomicExp(finalExpZ, finalExpIsNegative, "w0");
|
|
|
|
const f = module.addFunction(prefix+ "_finalExponentiation");
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
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));
|
|
|
|
f.addCode(
|
|
|
|
// let mut t0 = f.frobenius_map(6)
|
|
c.call(ftmPrefix + "_frobeniusMap6", elt, t0),
|
|
|
|
// let t1 = f.invert()
|
|
c.call(ftmPrefix + "_inverse", elt, t1),
|
|
|
|
// let mut t2 = t0 * t1;
|
|
c.call(ftmPrefix + "_mul", t0, t1, t2),
|
|
|
|
// t1 = t2.clone();
|
|
c.call(ftmPrefix + "_copy", t2, t1),
|
|
|
|
// t2 = t2.frobenius_map().frobenius_map();
|
|
c.call(ftmPrefix + "_frobeniusMap2", t2, t2),
|
|
|
|
// t2 *= t1;
|
|
c.call(ftmPrefix + "_mul", t2, t1, t2),
|
|
|
|
|
|
// t1 = cyclotomic_square(t2).conjugate();
|
|
c.call(prefix + "__cyclotomicSquare", t2, t1),
|
|
c.call(ftmPrefix + "_conjugate", t1, t1),
|
|
|
|
// let mut t3 = cycolotomic_exp(t2);
|
|
c.call(prefix + "__cyclotomicExp_w0", t2, t3),
|
|
|
|
// let mut t4 = cyclotomic_square(t3);
|
|
c.call(prefix + "__cyclotomicSquare", t3, t4),
|
|
|
|
// let mut t5 = t1 * t3;
|
|
c.call(ftmPrefix + "_mul", t1, t3, t5),
|
|
|
|
// t1 = cycolotomic_exp(t5);
|
|
c.call(prefix + "__cyclotomicExp_w0", t5, t1),
|
|
|
|
// t0 = cycolotomic_exp(t1);
|
|
c.call(prefix + "__cyclotomicExp_w0", t1, t0),
|
|
|
|
// let mut t6 = cycolotomic_exp(t0);
|
|
c.call(prefix + "__cyclotomicExp_w0", t0, t6),
|
|
|
|
// t6 *= t4;
|
|
c.call(ftmPrefix + "_mul", t6, t4, t6),
|
|
|
|
// t4 = cycolotomic_exp(t6);
|
|
c.call(prefix + "__cyclotomicExp_w0", t6, t4),
|
|
|
|
// t5 = t5.conjugate();
|
|
c.call(ftmPrefix + "_conjugate", t5, t5),
|
|
|
|
// t4 *= t5 * t2;
|
|
c.call(ftmPrefix + "_mul", t4, t5, t4),
|
|
c.call(ftmPrefix + "_mul", t4, t2, t4),
|
|
|
|
// t5 = t2.conjugate();
|
|
c.call(ftmPrefix + "_conjugate", t2, t5),
|
|
|
|
// t1 *= t2;
|
|
c.call(ftmPrefix + "_mul", t1, t2, t1),
|
|
|
|
// t1 = t1.frobenius_map().frobenius_map().frobenius_map();
|
|
c.call(ftmPrefix + "_frobeniusMap3", t1, t1),
|
|
|
|
// t6 *= t5;
|
|
c.call(ftmPrefix + "_mul", t6, t5, t6),
|
|
|
|
// t6 = t6.frobenius_map();
|
|
c.call(ftmPrefix + "_frobeniusMap1", t6, t6),
|
|
|
|
// t3 *= t0;
|
|
c.call(ftmPrefix + "_mul", t3, t0, t3),
|
|
|
|
// t3 = t3.frobenius_map().frobenius_map();
|
|
c.call(ftmPrefix + "_frobeniusMap2", t3, t3),
|
|
|
|
// t3 *= t1;
|
|
c.call(ftmPrefix + "_mul", t3, t1, t3),
|
|
|
|
// t3 *= t6;
|
|
c.call(ftmPrefix + "_mul", t3, t6, t3),
|
|
|
|
// f = t3 * t4;
|
|
c.call(ftmPrefix + "_mul", t3, t4, res),
|
|
|
|
);
|
|
}
|
|
|
|
|
|
function buildFinalExponentiationOld() {
|
|
const f = module.addFunction(prefix+ "_finalExponentiationOld");
|
|
f.addParam("x", "i32");
|
|
f.addParam("r", "i32");
|
|
|
|
const exponent = bigInt$1("322277361516934140462891564586510139908379969514828494218366688025288661041104682794998680497580008899973249814104447692778988208376779573819485263026159588510513834876303014016798809919343532899164848730280942609956670917565618115867287399623286813270357901731510188149934363360381614501334086825442271920079363289954510565375378443704372994881406797882676971082200626541916413184642520269678897559532260949334760604962086348898118982248842634379637598665468817769075878555493752214492790122785850202957575200176084204422751485957336465472324810982833638490904279282696134323072515220044451592646885410572234451732790590013479358343841220074174848221722017083597872017638514103174122784843925578370430843522959600095676285723737049438346544753168912974976791528535276317256904336520179281145394686565050419250614107803233314658825463117900250701199181529205942363159325765991819433914303908860460720581408201373164047773794825411011922305820065611121544561808414055302212057471395719432072209245600258134364584636810093520285711072578721435517884103526483832733289802426157301542744476740008494780363354305116978805620671467071400711358839553375340724899735460480144599782014906586543813292157922220645089192130209334926661588737007768565838519456601560804957985667880395221049249803753582637708560");
|
|
|
|
const pExponent = module.alloc(utils$5.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<nPairings; i++) {
|
|
f.addParam("p_"+i, "i32");
|
|
f.addParam("q_"+i, "i32");
|
|
}
|
|
f.addParam("c", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const resT = c.i32_const(module.alloc(ftsize));
|
|
const auxT = c.i32_const(module.alloc(ftsize));
|
|
|
|
f.addCode(c.call(ftmPrefix + "_one", resT ));
|
|
|
|
for (let i=0; i<nPairings; i++) {
|
|
|
|
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))
|
|
)
|
|
);
|
|
|
|
f.addCode(c.call(prefix + "_millerLoop", c.i32_const(pPreP), c.i32_const(pPreQ), auxT ));
|
|
|
|
f.addCode(c.call(ftmPrefix + "_mul", resT, auxT, resT ));
|
|
}
|
|
|
|
f.addCode(c.call(prefix + "_finalExponentiation", resT, resT ));
|
|
|
|
f.addCode(c.call(ftmPrefix + "_eq", resT, c.getLocal("c")));
|
|
}
|
|
|
|
|
|
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 buildInGroupG2() {
|
|
const f = module.addFunction(g2mPrefix+ "_inGroupAffine");
|
|
f.addParam("p", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const WINV = [
|
|
bigInt$1("2001204777610833696708894912867952078278441409969503942666029068062015825245418932221343814564507832018947136279894"),
|
|
bigInt$1("2001204777610833696708894912867952078278441409969503942666029068062015825245418932221343814564507832018947136279893")
|
|
];
|
|
|
|
const FROB2X = bigInt$1("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436");
|
|
const FROB3Y = [
|
|
bigInt$1("2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530"),
|
|
bigInt$1("2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530")
|
|
];
|
|
|
|
const wInv = c.i32_const(module.alloc([
|
|
...utils$5.bigInt2BytesLE(toMontgomery(WINV[0]), n8q),
|
|
...utils$5.bigInt2BytesLE(toMontgomery(WINV[1]), n8q),
|
|
]));
|
|
|
|
const frob2X = c.i32_const(module.alloc(utils$5.bigInt2BytesLE(toMontgomery(FROB2X), n8q)));
|
|
const frob3Y = c.i32_const(module.alloc([
|
|
...utils$5.bigInt2BytesLE(toMontgomery(FROB3Y[0]), n8q),
|
|
...utils$5.bigInt2BytesLE(toMontgomery(FROB3Y[1]), n8q),
|
|
]));
|
|
|
|
const z = c.i32_const(module.alloc(utils$5.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));
|
|
|
|
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);
|
|
|
|
|
|
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),
|
|
|
|
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),
|
|
|
|
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),
|
|
|
|
c.call(f2mPrefix + "_one", f3z),
|
|
|
|
c.call(g2mPrefix + "_timesScalar", f3, z, c.i32_const(8), f3),
|
|
c.call(g2mPrefix + "_addMixed", f3, f2, f3),
|
|
|
|
c.ret(
|
|
c.call(g2mPrefix + "_eqMixed", f3, c.getLocal("p"))
|
|
)
|
|
);
|
|
|
|
const fInGroup = module.addFunction(g2mPrefix + "_inGroup");
|
|
fInGroup.addParam("pIn", "i32");
|
|
fInGroup.setReturnType("i32");
|
|
|
|
const c2 = fInGroup.getCodeBuilder();
|
|
|
|
const aux2 = c2.i32_const(module.alloc(f2size*2));
|
|
|
|
fInGroup.addCode(
|
|
c2.call(g2mPrefix + "_toAffine", c2.getLocal("pIn"), aux2),
|
|
|
|
c2.ret(
|
|
c2.call(g2mPrefix + "_inGroupAffine", aux2),
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
function buildInGroupG1() {
|
|
const f = module.addFunction(g1mPrefix+ "_inGroupAffine");
|
|
f.addParam("p", "i32");
|
|
f.setReturnType("i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
const BETA = bigInt$1("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436");
|
|
const BETA2 = bigInt$1("793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350");
|
|
const Z2M1D3 = finalExpZ.times(finalExpZ).minus(bigInt$1.one).divide(bigInt$1(3));
|
|
|
|
const beta = c.i32_const(module.alloc(utils$5.bigInt2BytesLE(toMontgomery(BETA), n8q)));
|
|
const beta2 = c.i32_const(module.alloc(utils$5.bigInt2BytesLE(toMontgomery(BETA2), n8q)));
|
|
|
|
const z2m1d3 = c.i32_const(module.alloc(utils$5.bigInt2BytesLE(Z2M1D3, 16)));
|
|
|
|
|
|
const px = c.getLocal("p");
|
|
const py = c.i32_add(c.getLocal("p"), c.i32_const(f1size));
|
|
|
|
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);
|
|
c.i32_const(psp+2*f1size);
|
|
|
|
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);
|
|
|
|
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)),
|
|
),
|
|
|
|
c.call(f1mPrefix + "_mul", px, beta, spx),
|
|
c.call(f1mPrefix + "_copy", py, spy),
|
|
|
|
c.call(f1mPrefix + "_mul", px, beta2, s2px),
|
|
c.call(f1mPrefix + "_copy", py, s2py),
|
|
|
|
|
|
c.call(g1mPrefix + "_doubleAffine", sp, sp),
|
|
c.call(g1mPrefix + "_subMixed", sp, c.getLocal("p"), sp),
|
|
c.call(g1mPrefix + "_subMixed", sp, s2p, sp),
|
|
|
|
c.call(g1mPrefix + "_timesScalar", sp, z2m1d3, c.i32_const(16), sp),
|
|
|
|
c.ret(
|
|
c.call(g1mPrefix + "_eqMixed", sp, s2p)
|
|
)
|
|
|
|
);
|
|
|
|
const fInGroup = module.addFunction(g1mPrefix + "_inGroup");
|
|
fInGroup.addParam("pIn", "i32");
|
|
fInGroup.setReturnType("i32");
|
|
|
|
const c2 = fInGroup.getCodeBuilder();
|
|
|
|
const aux2 = c2.i32_const(module.alloc(f1size*2));
|
|
|
|
fInGroup.addCode(
|
|
c2.call(g1mPrefix + "_toAffine", c2.getLocal("pIn"), aux2),
|
|
|
|
c2.ret(
|
|
c2.call(g1mPrefix + "_inGroupAffine", aux2),
|
|
)
|
|
);
|
|
}
|
|
|
|
for (let i=0; i<10; i++) {
|
|
buildFrobeniusMap(i);
|
|
module.exportFunction(ftmPrefix + "_frobeniusMap"+i);
|
|
}
|
|
|
|
|
|
buildInGroupG1();
|
|
buildInGroupG2();
|
|
|
|
buildPrepAddStep();
|
|
buildPrepDoubleStep();
|
|
|
|
buildPrepareG1();
|
|
buildPrepareG2();
|
|
|
|
buildMillerLoop();
|
|
|
|
buildFinalExponentiationOld();
|
|
buildFinalExponentiation();
|
|
|
|
for (let i=1; i<=5; i++) {
|
|
buildPairingEquation(i);
|
|
module.exportFunction(prefix + "_pairingEq"+i);
|
|
}
|
|
|
|
buildPairing();
|
|
|
|
module.exportFunction(prefix + "_pairing");
|
|
|
|
|
|
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");
|
|
|
|
module.exportFunction(f6mPrefix + "_mul1");
|
|
module.exportFunction(f6mPrefix + "_mul01");
|
|
module.exportFunction(ftmPrefix + "_mul014");
|
|
|
|
module.exportFunction(g1mPrefix + "_inGroupAffine");
|
|
module.exportFunction(g1mPrefix + "_inGroup");
|
|
module.exportFunction(g2mPrefix + "_inGroupAffine");
|
|
module.exportFunction(g2mPrefix + "_inGroup");
|
|
|
|
// console.log(module.functionIdxByName);
|
|
};
|
|
|
|
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// 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");
|
|
|
|
var buildBn128$1 = build_bn128;
|
|
var buildBls12381$1 = build_bls12381;
|
|
|
|
/* global BigInt */
|
|
|
|
function stringifyBigInts$2(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$2);
|
|
} else if (typeof o == "object") {
|
|
const res = {};
|
|
const keys = Object.keys(o);
|
|
keys.forEach( (k) => {
|
|
res[k] = stringifyBigInts$2(o[k]);
|
|
});
|
|
return res;
|
|
} else {
|
|
return o;
|
|
}
|
|
}
|
|
|
|
function unstringifyBigInts$2(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$2);
|
|
} else if (typeof o == "object") {
|
|
if (o===null) return null;
|
|
const res = {};
|
|
const keys = Object.keys(o);
|
|
keys.forEach( (k) => {
|
|
res[k] = unstringifyBigInts$2(o[k]);
|
|
});
|
|
return res;
|
|
} else {
|
|
return o;
|
|
}
|
|
}
|
|
|
|
function beBuff2int$2(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;
|
|
}
|
|
|
|
function beInt2Buff$2(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;
|
|
}
|
|
|
|
|
|
function leBuff2int$2(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 + 4 <= 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;
|
|
}
|
|
|
|
function leInt2Buff$2(n, len) {
|
|
let r = n;
|
|
if (typeof len === "undefined") {
|
|
len = Math.floor((bitLength(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(Number(o, r & BigInt(0xFFFF)), true );
|
|
o += 2;
|
|
r = r >> BigInt(16);
|
|
} else {
|
|
buffV.setUint8(Number(o, r & BigInt(0xFF)), true );
|
|
o += 1;
|
|
r = r >> BigInt(8);
|
|
}
|
|
}
|
|
if (r) {
|
|
throw new Error("Number does not fit in this length");
|
|
}
|
|
return buff;
|
|
}
|
|
|
|
|
|
function stringifyFElements$1(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$1.bind(this,F));
|
|
} else if (typeof o == "object") {
|
|
const res = {};
|
|
const keys = Object.keys(o);
|
|
keys.forEach( (k) => {
|
|
res[k] = stringifyFElements$1(F, o[k]);
|
|
});
|
|
return res;
|
|
} else {
|
|
return o;
|
|
}
|
|
}
|
|
|
|
|
|
function unstringifyFElements$1(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$1.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$1(F, o[k]);
|
|
});
|
|
return res;
|
|
} else {
|
|
return o;
|
|
}
|
|
}
|
|
|
|
var utils_native = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
beBuff2int: beBuff2int$2,
|
|
beInt2Buff: beInt2Buff$2,
|
|
leBuff2int: leBuff2int$2,
|
|
leInt2Buff: leInt2Buff$2,
|
|
stringifyBigInts: stringifyBigInts$2,
|
|
stringifyFElements: stringifyFElements$1,
|
|
unstringifyBigInts: unstringifyBigInts$2,
|
|
unstringifyFElements: unstringifyFElements$1
|
|
});
|
|
|
|
function stringifyBigInts$1(o) {
|
|
if ((typeof(o) == "bigint") || o.eq !== undefined) {
|
|
return o.toString(10);
|
|
} else if (Array.isArray(o)) {
|
|
return o.map(stringifyBigInts$1);
|
|
} else if (typeof o == "object") {
|
|
const res = {};
|
|
const keys = Object.keys(o);
|
|
keys.forEach( (k) => {
|
|
res[k] = stringifyBigInts$1(o[k]);
|
|
});
|
|
return res;
|
|
} else {
|
|
return o;
|
|
}
|
|
}
|
|
|
|
function unstringifyBigInts$1(o) {
|
|
if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) {
|
|
return bigInt$8(o);
|
|
} else if ((typeof(o) == "string") && (/^0x[0-9a-fA-F]+$/.test(o) )) {
|
|
return bigInt$8(o);
|
|
} else if (Array.isArray(o)) {
|
|
return o.map(unstringifyBigInts$1);
|
|
} else if (typeof o == "object") {
|
|
const res = {};
|
|
const keys = Object.keys(o);
|
|
keys.forEach( (k) => {
|
|
res[k] = unstringifyBigInts$1(o[k]);
|
|
});
|
|
return res;
|
|
} else {
|
|
return o;
|
|
}
|
|
}
|
|
|
|
function beBuff2int$1(buff) {
|
|
let res = bigInt$8.zero;
|
|
for (let i=0; i<buff.length; i++) {
|
|
const n = bigInt$8(buff[buff.length - i - 1]);
|
|
res = res.add(n.shiftLeft(i*8));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function beInt2Buff$1(n, len) {
|
|
let r = n;
|
|
let o =len-1;
|
|
const buff = new Uint8Array(len);
|
|
while ((r.gt(bigInt$8.zero))&&(o>=0)) {
|
|
let c = Number(r.and(bigInt$8("255")));
|
|
buff[o] = c;
|
|
o--;
|
|
r = r.shiftRight(8);
|
|
}
|
|
if (!r.eq(bigInt$8.zero)) {
|
|
throw new Error("Number does not fit in this length");
|
|
}
|
|
return buff;
|
|
}
|
|
|
|
|
|
function leBuff2int$1 (buff) {
|
|
let res = bigInt$8.zero;
|
|
for (let i=0; i<buff.length; i++) {
|
|
const n = bigInt$8(buff[i]);
|
|
res = res.add(n.shiftLeft(i*8));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function leInt2Buff$1(n, len) {
|
|
let r = n;
|
|
let o =0;
|
|
const buff = new Uint8Array(len);
|
|
while ((r.gt(bigInt$8.zero))&&(o<buff.length)) {
|
|
let c = Number(r.and(bigInt$8(255)));
|
|
buff[o] = c;
|
|
o++;
|
|
r = r.shiftRight(8);
|
|
}
|
|
if (!r.eq(bigInt$8.zero)) {
|
|
throw new Error("Number does not fit in this length");
|
|
}
|
|
return buff;
|
|
}
|
|
|
|
var utils_bigint = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
beBuff2int: beBuff2int$1,
|
|
beInt2Buff: beInt2Buff$1,
|
|
leBuff2int: leBuff2int$1,
|
|
leInt2Buff: leInt2Buff$1,
|
|
stringifyBigInts: stringifyBigInts$1,
|
|
unstringifyBigInts: unstringifyBigInts$1
|
|
});
|
|
|
|
let utils$4 = {};
|
|
|
|
const supportsNativeBigInt = typeof BigInt === "function";
|
|
if (supportsNativeBigInt) {
|
|
Object.assign(utils$4, utils_native);
|
|
} else {
|
|
Object.assign(utils$4, utils_bigint);
|
|
}
|
|
|
|
|
|
const _revTable = [];
|
|
for (let i=0; i<256; i++) {
|
|
_revTable[i] = _revSlow(i, 8);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
utils$4.bitReverse = function bitReverse(idx, bits) {
|
|
return (
|
|
_revTable[idx >>> 24] |
|
|
(_revTable[(idx >>> 16) & 0xFF] << 8) |
|
|
(_revTable[(idx >>> 8) & 0xFF] << 16) |
|
|
(_revTable[idx & 0xFF] << 24)
|
|
) >>> (32-bits);
|
|
};
|
|
|
|
|
|
utils$4.log2 = function log2( V )
|
|
{
|
|
return( ( ( V & 0xFFFF0000 ) !== 0 ? ( V &= 0xFFFF0000, 16 ) : 0 ) | ( ( V & 0xFF00FF00 ) !== 0 ? ( V &= 0xFF00FF00, 8 ) : 0 ) | ( ( V & 0xF0F0F0F0 ) !== 0 ? ( V &= 0xF0F0F0F0, 4 ) : 0 ) | ( ( V & 0xCCCCCCCC ) !== 0 ? ( V &= 0xCCCCCCCC, 2 ) : 0 ) | ( ( V & 0xAAAAAAAA ) !== 0 ) );
|
|
};
|
|
|
|
utils$4.buffReverseBits = function buffReverseBits(buff, eSize) {
|
|
const n = buff.byteLength /eSize;
|
|
const bits = utils$4.log2(n);
|
|
if (n != (1 << bits)) {
|
|
throw new Error("Invalid number of pointers");
|
|
}
|
|
for (let i=0; i<n; i++) {
|
|
const r = utils$4.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);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
utils$4.array2buffer = function(arr, sG) {
|
|
const buff = new Uint8Array(sG*arr.length);
|
|
|
|
for (let i=0; i<arr.length; i++) {
|
|
buff.set(arr[i], i*sG);
|
|
}
|
|
|
|
return buff;
|
|
};
|
|
|
|
utils$4.buffer2array = function(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;
|
|
};
|
|
|
|
let {
|
|
bitReverse,
|
|
log2,
|
|
buffReverseBits,
|
|
stringifyBigInts,
|
|
unstringifyBigInts,
|
|
beBuff2int,
|
|
beInt2Buff,
|
|
leBuff2int,
|
|
leInt2Buff,
|
|
array2buffer,
|
|
buffer2array,
|
|
stringifyFElements,
|
|
unstringifyFElements
|
|
} = utils$4;
|
|
|
|
const PAGE_SIZE = 1<<30;
|
|
|
|
class BigBuffer {
|
|
|
|
constructor(size) {
|
|
this.buffers = [];
|
|
this.byteLength = size;
|
|
for (let i=0; i<size; i+= PAGE_SIZE) {
|
|
const n = Math.min(size-i, PAGE_SIZE);
|
|
this.buffers.push(new Uint8Array(n));
|
|
}
|
|
|
|
}
|
|
|
|
slice(fr, to) {
|
|
if ( to === undefined ) to = this.byteLength;
|
|
if ( fr === undefined ) fr = 0;
|
|
const len = to-fr;
|
|
|
|
const firstPage = Math.floor(fr / PAGE_SIZE);
|
|
const lastPage = Math.floor((fr+len-1) / PAGE_SIZE);
|
|
|
|
if ((firstPage == lastPage)||(len==0))
|
|
return this.buffers[firstPage].slice(fr%PAGE_SIZE, fr%PAGE_SIZE + len);
|
|
|
|
let buff;
|
|
|
|
let p = firstPage;
|
|
let o = fr % PAGE_SIZE;
|
|
// Remaining bytes to read
|
|
let r = len;
|
|
while (r>0) {
|
|
// 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;
|
|
}
|
|
|
|
return buff;
|
|
}
|
|
|
|
set(buff, offset) {
|
|
if (offset === undefined) offset = 0;
|
|
|
|
const len = buff.byteLength;
|
|
|
|
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);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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<tm.concurrency; i++) {
|
|
let n;
|
|
if (i< tm.concurrency-1) {
|
|
n = pointsPerChunk;
|
|
} else {
|
|
n = nPoints - i*pointsPerChunk;
|
|
}
|
|
if (n==0) continue;
|
|
|
|
const buffChunk = buffIn.slice(i*pointsPerChunk*sIn, i*pointsPerChunk*sIn + n*sIn);
|
|
const task = [
|
|
{cmd: "ALLOCSET", var: 0, buff:buffChunk},
|
|
{cmd: "ALLOC", var: 1, len:sOut * n},
|
|
{cmd: "CALL", fnName: fnName, params: [
|
|
{var: 0},
|
|
{val: n},
|
|
{var: 1}
|
|
]},
|
|
{cmd: "GET", out: 0, var: 1, len:sOut * n},
|
|
];
|
|
opPromises.push(
|
|
tm.queueAction(task)
|
|
);
|
|
}
|
|
|
|
const result = await Promise.all(opPromises);
|
|
|
|
let fullBuffOut;
|
|
if (buffIn instanceof BigBuffer) {
|
|
fullBuffOut = new BigBuffer(nPoints*sOut);
|
|
} else {
|
|
fullBuffOut = new Uint8Array(nPoints*sOut);
|
|
}
|
|
|
|
let p =0;
|
|
for (let i=0; i<result.length; i++) {
|
|
fullBuffOut.set(result[i][0], p);
|
|
p+=result[i][0].byteLength;
|
|
}
|
|
|
|
return fullBuffOut;
|
|
};
|
|
}
|
|
|
|
class WasmField1 {
|
|
|
|
constructor(tm, prefix, n8, p) {
|
|
this.tm = tm;
|
|
this.prefix = prefix;
|
|
|
|
this.p = p;
|
|
this.n8 = n8;
|
|
this.type = "F1";
|
|
this.m = 1;
|
|
|
|
this.half = shiftRight(p, one);
|
|
this.bitLength = bitLength(p);
|
|
this.mask = sub(shiftLeft(one, this.bitLength), one);
|
|
|
|
this.pOp1 = tm.alloc(n8);
|
|
this.pOp2 = tm.alloc(n8);
|
|
this.pOp3 = tm.alloc(n8);
|
|
this.tm.instance.exports[prefix + "_zero"](this.pOp1);
|
|
this.zero = this.tm.getBuff(this.pOp1, this.n8);
|
|
this.tm.instance.exports[prefix + "_one"](this.pOp1);
|
|
this.one = this.tm.getBuff(this.pOp1, this.n8);
|
|
|
|
this.negone = this.neg(this.one);
|
|
this.two = this.add(this.one, this.one);
|
|
|
|
this.n64 = Math.floor(n8/8);
|
|
this.n32 = Math.floor(n8/4);
|
|
|
|
if(this.n64*8 != this.n8) {
|
|
throw new Error("n8 must be a multiple of 8");
|
|
}
|
|
|
|
this.half = shiftRight(this.p, one);
|
|
this.nqr = this.two;
|
|
let r = this.exp(this.nqr, this.half);
|
|
while (!this.eq(r, this.negone)) {
|
|
this.nqr = this.add(this.nqr, this.one);
|
|
r = this.exp(this.nqr, this.half);
|
|
}
|
|
|
|
this.shift = this.mul(this.nqr, this.nqr);
|
|
this.shiftInv = this.inv(this.shift);
|
|
|
|
this.s = 0;
|
|
let t = sub(this.p, one);
|
|
|
|
while ( !isOdd(t) ) {
|
|
this.s = this.s + 1;
|
|
t = shiftRight(t, one);
|
|
}
|
|
|
|
this.w = [];
|
|
this.w[this.s] = this.exp(this.nqr, t);
|
|
|
|
for (let i= this.s-1; i>=0; i--) {
|
|
this.w[i] = this.square(this.w[i+1]);
|
|
}
|
|
|
|
if (!this.eq(this.w[0], this.one)) {
|
|
throw new Error("Error calculating roots of unity");
|
|
}
|
|
|
|
this.batchToMontgomery = buildBatchConvert(tm, prefix + "_batchToMontgomery", this.n8, this.n8);
|
|
this.batchFromMontgomery = buildBatchConvert(tm, prefix + "_batchFromMontgomery", this.n8, this.n8);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
add(a,b) {
|
|
return this.op2("_add", a, b);
|
|
}
|
|
|
|
|
|
eq(a,b) {
|
|
return this.op2Bool("_eq", a, b);
|
|
}
|
|
|
|
isZero(a) {
|
|
return this.op1Bool("_isZero", a);
|
|
}
|
|
|
|
sub(a,b) {
|
|
return this.op2("_sub", a, b);
|
|
}
|
|
|
|
neg(a) {
|
|
return this.op1("_neg", a);
|
|
}
|
|
|
|
inv(a) {
|
|
return this.op1("_inverse", a);
|
|
}
|
|
|
|
toMontgomery(a) {
|
|
return this.op1("_toMontgomery", a);
|
|
}
|
|
|
|
fromMontgomery(a) {
|
|
return this.op1("_fromMontgomery", a);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
isSquare(a) {
|
|
return this.op1Bool("_isSquare", a);
|
|
}
|
|
|
|
sqrt(a) {
|
|
return this.op1("_sqrt", a);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
isNegative(a) {
|
|
return this.op1Bool("_isNegative", a);
|
|
}
|
|
|
|
e(a, b) {
|
|
if (a instanceof Uint8Array) return a;
|
|
let ra = e(a, b);
|
|
if (isNegative(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);
|
|
}
|
|
|
|
fromRng(rng) {
|
|
let v;
|
|
const buff = new Uint8Array(this.n8);
|
|
do {
|
|
v = zero;
|
|
for (let i=0; i<this.n64; i++) {
|
|
v = add(v, shiftLeft(rng.nextU64(), 64*i));
|
|
}
|
|
v = band(v, this.mask);
|
|
} while (geq(v, this.p));
|
|
toRprLE(buff, 0, v, this.n8);
|
|
return buff;
|
|
}
|
|
|
|
random() {
|
|
return this.fromRng(getThreadRng());
|
|
}
|
|
|
|
toObject(a) {
|
|
const an = this.fromMontgomery(a);
|
|
return fromRprLE(an, 0);
|
|
}
|
|
|
|
fromObject(a) {
|
|
const buff = new Uint8Array(this.n8);
|
|
toRprLE(buff, 0, a, this.n8);
|
|
return this.toMontgomery(buff);
|
|
}
|
|
|
|
toRprLE(buff, offset, a) {
|
|
buff.set(this.fromMontgomery(a), offset);
|
|
}
|
|
|
|
toRprBE(buff, offset, a) {
|
|
const buff2 = this.fromMontgomery(a);
|
|
for (let i=0; i<this.n8/2; i++) {
|
|
const aux = buff2[i];
|
|
buff2[i] = buff2[this.n8-1-i];
|
|
buff2[this.n8-1-i] = aux;
|
|
}
|
|
buff.set(buff2, offset);
|
|
}
|
|
|
|
fromRprLE(buff, offset) {
|
|
offset = offset || 0;
|
|
const res = buff.slice(offset, offset + this.n8);
|
|
return this.toMontgomery(res);
|
|
}
|
|
|
|
async batchInverse(buffIn) {
|
|
let returnArray = false;
|
|
const sIn = this.n8;
|
|
const sOut = this.n8;
|
|
|
|
if (Array.isArray(buffIn)) {
|
|
buffIn = array2buffer(buffIn, sIn );
|
|
returnArray = true;
|
|
} else {
|
|
buffIn = buffIn.slice(0, buffIn.byteLength);
|
|
}
|
|
|
|
const nPoints = Math.floor(buffIn.byteLength / sIn);
|
|
if ( nPoints * sIn !== buffIn.byteLength) {
|
|
throw new Error("Invalid buffer size");
|
|
}
|
|
const pointsPerChunk = Math.floor(nPoints/this.tm.concurrency);
|
|
const opPromises = [];
|
|
for (let i=0; i<this.tm.concurrency; i++) {
|
|
let n;
|
|
if (i< this.tm.concurrency-1) {
|
|
n = pointsPerChunk;
|
|
} else {
|
|
n = nPoints - i*pointsPerChunk;
|
|
}
|
|
if (n==0) continue;
|
|
|
|
const buffChunk = buffIn.slice(i*pointsPerChunk*sIn, i*pointsPerChunk*sIn + n*sIn);
|
|
const task = [
|
|
{cmd: "ALLOCSET", var: 0, buff:buffChunk},
|
|
{cmd: "ALLOC", var: 1, len:sOut * n},
|
|
{cmd: "CALL", fnName: this.prefix + "_batchInverse", params: [
|
|
{var: 0},
|
|
{val: sIn},
|
|
{val: n},
|
|
{var: 1},
|
|
{val: sOut},
|
|
]},
|
|
{cmd: "GET", out: 0, var: 1, len:sOut * n},
|
|
];
|
|
opPromises.push(
|
|
this.tm.queueAction(task)
|
|
);
|
|
}
|
|
|
|
const result = await Promise.all(opPromises);
|
|
|
|
let fullBuffOut;
|
|
if (buffIn instanceof BigBuffer) {
|
|
fullBuffOut = new BigBuffer(nPoints*sOut);
|
|
} else {
|
|
fullBuffOut = new Uint8Array(nPoints*sOut);
|
|
}
|
|
|
|
let p =0;
|
|
for (let i=0; i<result.length; i++) {
|
|
fullBuffOut.set(result[i][0], p);
|
|
p+=result[i][0].byteLength;
|
|
}
|
|
|
|
if (returnArray) {
|
|
return buffer2array(fullBuffOut, sOut);
|
|
} else {
|
|
return fullBuffOut;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class WasmField2 {
|
|
|
|
constructor(tm, prefix, F) {
|
|
this.tm = tm;
|
|
this.prefix = prefix;
|
|
|
|
this.F = F;
|
|
this.type = "F2";
|
|
this.m = F.m * 2;
|
|
this.n8 = this.F.n8*2;
|
|
this.n32 = this.F.n32*2;
|
|
this.n64 = this.F.n64*2;
|
|
|
|
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);
|
|
|
|
this.negone = this.neg(this.one);
|
|
this.two = this.add(this.one, this.one);
|
|
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
add(a,b) {
|
|
return this.op2("_add", a, b);
|
|
}
|
|
|
|
eq(a,b) {
|
|
return this.op2Bool("_eq", a, b);
|
|
}
|
|
|
|
isZero(a) {
|
|
return this.op1Bool("_isZero", a);
|
|
}
|
|
|
|
sub(a,b) {
|
|
return this.op2("_sub", a, b);
|
|
}
|
|
|
|
neg(a) {
|
|
return this.op1("_neg", a);
|
|
}
|
|
|
|
inv(a) {
|
|
return this.op1("_inverse", a);
|
|
}
|
|
|
|
isNegative(a) {
|
|
return this.op1Bool("_isNegative", a);
|
|
}
|
|
|
|
toMontgomery(a) {
|
|
return this.op1("_toMontgomery", a);
|
|
}
|
|
|
|
fromMontgomery(a) {
|
|
return this.op1("_fromMontgomery", a);
|
|
}
|
|
|
|
mul(a,b) {
|
|
return this.op2("_mul", a, b);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
sqrt(a) {
|
|
return this.op1("_sqrt", a);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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}]`;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
random() {
|
|
return this.fromRng(getThreadRng());
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
c1(a) {
|
|
return a.slice(0, this.F.n8);
|
|
}
|
|
|
|
c2(a) {
|
|
return a.slice(this.F.n8);
|
|
}
|
|
|
|
}
|
|
|
|
class WasmField3 {
|
|
|
|
constructor(tm, prefix, F) {
|
|
this.tm = tm;
|
|
this.prefix = prefix;
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
this.negone = this.neg(this.one);
|
|
this.two = this.add(this.one, this.one);
|
|
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
eq(a,b) {
|
|
return this.op2Bool("_eq", a, b);
|
|
}
|
|
|
|
isZero(a) {
|
|
return this.op1Bool("_isZero", a);
|
|
}
|
|
|
|
add(a,b) {
|
|
return this.op2("_add", a, b);
|
|
}
|
|
|
|
sub(a,b) {
|
|
return this.op2("_sub", a, b);
|
|
}
|
|
|
|
neg(a) {
|
|
return this.op1("_neg", a);
|
|
}
|
|
|
|
inv(a) {
|
|
return this.op1("_inverse", a);
|
|
}
|
|
|
|
isNegative(a) {
|
|
return this.op1Bool("_isNegative", a);
|
|
}
|
|
|
|
toMontgomery(a) {
|
|
return this.op1("_toMontgomery", a);
|
|
}
|
|
|
|
fromMontgomery(a) {
|
|
return this.op1("_fromMontgomery", a);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
isSquare(a) {
|
|
return this.op1Bool("_isSquare", a);
|
|
}
|
|
|
|
sqrt(a) {
|
|
return this.op1("_sqrt", a);
|
|
}
|
|
|
|
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.getBuff(this.pOp3, this.n8);
|
|
}
|
|
|
|
e(a, b) {
|
|
if (a instanceof Uint8Array) return a;
|
|
if ((Array.isArray(a)) && (a.length == 3)) {
|
|
const c1 = this.F.e(a[0], b);
|
|
const c2 = this.F.e(a[1], b);
|
|
const c3 = this.F.e(a[2], b);
|
|
const res = new Uint8Array(this.F.n8*3);
|
|
res.set(c1);
|
|
res.set(c2, this.F.n8);
|
|
res.set(c3, this.F.n8*2);
|
|
return res;
|
|
} else {
|
|
throw new Error("invalid F3");
|
|
}
|
|
}
|
|
|
|
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, this.F.n8*2), radix);
|
|
const s3 = this.F.toString(a.slice(this.F.n8*2), radix);
|
|
return `[${s1}, ${s2}, ${s3}]`;
|
|
}
|
|
|
|
fromRng(rng) {
|
|
const c1 = this.F.fromRng(rng);
|
|
const c2 = this.F.fromRng(rng);
|
|
const c3 = this.F.fromRng(rng);
|
|
const res = new Uint8Array(this.F.n8*3);
|
|
res.set(c1);
|
|
res.set(c2, this.F.n8);
|
|
res.set(c3, this.F.n8*2);
|
|
return res;
|
|
}
|
|
|
|
random() {
|
|
return this.fromRng(getThreadRng());
|
|
}
|
|
|
|
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));
|
|
const c3 = this.F.toObject(a.slice(this.F.n8*2, this.F.n8*3));
|
|
return [c1, c2, c3];
|
|
}
|
|
|
|
fromObject(a) {
|
|
const buff = new Uint8Array(this.F.n8*3);
|
|
const b1 = this.F.fromObject(a[0]);
|
|
const b2 = this.F.fromObject(a[1]);
|
|
const b3 = this.F.fromObject(a[2]);
|
|
buff.set(b1);
|
|
buff.set(b2, this.F.n8);
|
|
buff.set(b3, this.F.n8*2);
|
|
return buff;
|
|
}
|
|
|
|
c1(a) {
|
|
return a.slice(0, this.F.n8);
|
|
}
|
|
|
|
c2(a) {
|
|
return a.slice(this.F.n8, this.F.n8*2);
|
|
}
|
|
|
|
c3(a) {
|
|
return a.slice(this.F.n8*2);
|
|
}
|
|
|
|
}
|
|
|
|
class WasmCurve {
|
|
|
|
constructor(tm, prefix, F, pGen, pGb, cofactor) {
|
|
this.tm = tm;
|
|
this.prefix = prefix;
|
|
this.F = F;
|
|
|
|
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 = this.tm.getBuff(this.pOp1, F.n8*3);
|
|
this.tm.instance.exports[prefix + "_zeroAffine"](this.pOp1);
|
|
this.zeroAffine = this.tm.getBuff(this.pOp1, F.n8*2);
|
|
this.one = this.tm.getBuff(pGen, F.n8*3);
|
|
this.g = this.one;
|
|
this.oneAffine = this.tm.getBuff(pGen, F.n8*2);
|
|
this.gAffine = this.oneAffine;
|
|
this.b = this.tm.getBuff(pGb, F.n8);
|
|
|
|
if (cofactor) {
|
|
this.cofactor = toLEBuff(cofactor);
|
|
}
|
|
|
|
this.negone = this.neg(this.one);
|
|
this.two = this.add(this.one, this.one);
|
|
|
|
this.batchLEMtoC = buildBatchConvert(tm, prefix + "_batchLEMtoC", F.n8*2, F.n8);
|
|
this.batchLEMtoU = buildBatchConvert(tm, prefix + "_batchLEMtoU", F.n8*2, F.n8*2);
|
|
this.batchCtoLEM = buildBatchConvert(tm, prefix + "_batchCtoLEM", F.n8, F.n8*2);
|
|
this.batchUtoLEM = buildBatchConvert(tm, prefix + "_batchUtoLEM", F.n8*2, F.n8*2);
|
|
this.batchToJacobian = buildBatchConvert(tm, prefix + "_batchToJacobian", F.n8*2, F.n8*3);
|
|
this.batchToAffine = buildBatchConvert(tm, prefix + "_batchToAffine", F.n8*3, F.n8*2);
|
|
}
|
|
|
|
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.F.n8*3);
|
|
}
|
|
|
|
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, this.pOp3);
|
|
}
|
|
|
|
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.F.n8*3);
|
|
}
|
|
|
|
op1Affine(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.F.n8*2);
|
|
}
|
|
|
|
op1Bool(opName, a) {
|
|
this.tm.setBuff(this.pOp1, a);
|
|
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
|
|
}
|
|
|
|
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);
|
|
} else {
|
|
throw new Error("invalid point size");
|
|
}
|
|
} else {
|
|
throw new Error("invalid point size");
|
|
}
|
|
}
|
|
|
|
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);
|
|
} else {
|
|
throw new Error("invalid point size");
|
|
}
|
|
} else {
|
|
throw new Error("invalid point size");
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
toRprCompressed(arr, offset, a) {
|
|
this.tm.setBuff(this.pOp1, a);
|
|
if (a.byteLength == this.F.n8*3) {
|
|
this.tm.instance.exports[this.prefix + "_toAffine"](this.pOp1, this.pOp1);
|
|
} else if (a.byteLength != this.F.n8*2) {
|
|
throw new Error("invalid point size");
|
|
}
|
|
this.tm.instance.exports[this.prefix + "_LEMtoC"](this.pOp1, this.pOp1);
|
|
const res = this.tm.getBuff(this.pOp1, this.F.n8);
|
|
arr.set(res, offset);
|
|
}
|
|
|
|
fromRprCompressed(arr, offset) {
|
|
const buff = arr.slice(offset, offset + this.F.n8);
|
|
this.tm.setBuff(this.pOp1, buff);
|
|
this.tm.instance.exports[this.prefix + "_CtoLEM"](this.pOp1, this.pOp2);
|
|
return this.tm.getBuff(this.pOp2, this.F.n8*2);
|
|
}
|
|
|
|
toUncompressed(a) {
|
|
const buff = new Uint8Array(this.F.n8*2);
|
|
this.toRprUncompressed(buff, 0, a);
|
|
return buff;
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
fromRprLEM(arr, offset) {
|
|
offset = offset || 0;
|
|
return arr.slice(offset, offset+this.F.n8*2);
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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));
|
|
|
|
P[1] = F.sqrt(x3b);
|
|
|
|
const s = F.isNegative(P[1]);
|
|
if (greatest ^ s) P[1] = F.neg(P[1]);
|
|
|
|
let Pbuff = new Uint8Array(this.F.n8*2);
|
|
Pbuff.set(P[0]);
|
|
Pbuff.set(P[1], this.F.n8);
|
|
|
|
if (this.cofactor) {
|
|
Pbuff = this.timesScalar(Pbuff, this.cofactor);
|
|
}
|
|
|
|
return Pbuff;
|
|
}
|
|
|
|
|
|
|
|
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];
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
e(a) {
|
|
if (a instanceof Uint8Array) return a;
|
|
return this.fromObject(a);
|
|
}
|
|
|
|
x(a) {
|
|
const tmp = this.toAffine(a);
|
|
return tmp.slice(0, this.F.n8);
|
|
}
|
|
|
|
y(a) {
|
|
const tmp = this.toAffine(a);
|
|
return tmp.slice(this.F.n8);
|
|
}
|
|
|
|
}
|
|
|
|
/* global WebAssembly */
|
|
|
|
function thread(self) {
|
|
const MAXMEM = 32767;
|
|
let instance;
|
|
let memory;
|
|
|
|
if (self) {
|
|
self.onmessage = function(e) {
|
|
let data;
|
|
if (e.data) {
|
|
data = e.data;
|
|
} else {
|
|
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);
|
|
}
|
|
};
|
|
}
|
|
|
|
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});
|
|
|
|
instance = await WebAssembly.instantiate(wasmModule, {
|
|
env: {
|
|
"memory": memory
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
function setBuffer(pointer, buffer) {
|
|
const u8 = new Uint8Array(memory.buffer);
|
|
u8.set(new Uint8Array(buffer), pointer);
|
|
}
|
|
|
|
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<task.length; i++) {
|
|
switch (task[i].cmd) {
|
|
case "ALLOCSET":
|
|
ctx.vars[task[i].var] = allocBuffer(task[i].buff);
|
|
break;
|
|
case "ALLOC":
|
|
ctx.vars[task[i].var] = alloc(task[i].len);
|
|
break;
|
|
case "SET":
|
|
setBuffer(ctx.vars[task[i].var], task[i].buff);
|
|
break;
|
|
case "CALL": {
|
|
const params = [];
|
|
for (let j=0; j<task[i].params.length; j++) {
|
|
const p = task[i].params[j];
|
|
if (typeof p.var !== "undefined") {
|
|
params.push(ctx.vars[p.var] + (p.offset || 0));
|
|
} else if (typeof p.val != "undefined") {
|
|
params.push(p.val);
|
|
}
|
|
}
|
|
instance.exports[task[i].fnName](...params);
|
|
break;
|
|
}
|
|
case "GET":
|
|
ctx.out[task[i].out] = getBuffer(ctx.vars[task[i].var], task[i].len).slice();
|
|
break;
|
|
default:
|
|
throw new Error("Invalid cmd");
|
|
}
|
|
}
|
|
const u32b = new Uint32Array(memory.buffer, 0, 1);
|
|
u32b[0] = oldAlloc;
|
|
return ctx.out;
|
|
}
|
|
|
|
|
|
return runTask;
|
|
}
|
|
|
|
/**
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
|
|
const WORKER = Symbol.for('worker');
|
|
const EVENTS = Symbol.for('events');
|
|
|
|
class EventTarget {
|
|
constructor() {
|
|
Object.defineProperty(this, EVENTS, {
|
|
value: new Map()
|
|
});
|
|
}
|
|
dispatchEvent(event) {
|
|
event.target = event.currentTarget = this;
|
|
if (this['on'+event.type]) {
|
|
try {
|
|
this['on'+event.type](event);
|
|
}
|
|
catch (err) {
|
|
console.error(err);
|
|
}
|
|
}
|
|
const list = this[EVENTS].get(event.type);
|
|
if (list == null) return;
|
|
list.forEach(handler => {
|
|
try {
|
|
handler.call(this, event);
|
|
}
|
|
catch (err) {
|
|
console.error(err);
|
|
}
|
|
});
|
|
}
|
|
addEventListener(type, fn) {
|
|
let events = this[EVENTS].get(type);
|
|
if (!events) this[EVENTS].set(type, events = []);
|
|
events.push(fn);
|
|
}
|
|
removeEventListener(type, fn) {
|
|
let events = this[EVENTS].get(type);
|
|
if (events) {
|
|
const index = events.indexOf(fn);
|
|
if (index !== -1) events.splice(index, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
function Event(type, target) {
|
|
this.type = type;
|
|
this.timeStamp = Date.now();
|
|
this.target = this.currentTarget = this.data = null;
|
|
}
|
|
|
|
// this module is used self-referentially on both sides of the
|
|
// thread boundary, but behaves differently in each context.
|
|
var Worker = threads.isMainThread ? mainThread() : workerThread();
|
|
|
|
const baseUrl = URL.pathToFileURL(process.cwd() + '/');
|
|
|
|
function mainThread() {
|
|
|
|
/**
|
|
* A web-compatible Worker implementation atop Node's worker_threads.
|
|
* - uses DOM-style events (Event.data, Event.type, etc)
|
|
* - supports event handler properties (worker.onmessage)
|
|
* - Worker() constructor accepts a module URL
|
|
* - accepts the {type:'module'} option
|
|
* - emulates WorkerGlobalScope within the worker
|
|
* @param {string} url The URL or module specifier to load
|
|
* @param {object} [options] Worker construction options
|
|
* @param {string} [options.name] Available as `self.name` within the Worker
|
|
* @param {string} [options.type="classic"] Pass "module" to create a Module Worker.
|
|
*/
|
|
class Worker extends EventTarget {
|
|
constructor(url, options) {
|
|
super();
|
|
const { name, type } = options || {};
|
|
url += '';
|
|
let mod;
|
|
if (/^data:/.test(url)) {
|
|
mod = url;
|
|
}
|
|
else {
|
|
mod = URL.fileURLToPath(new URL.URL(url, baseUrl));
|
|
}
|
|
const worker = new threads.Worker(
|
|
__filename,
|
|
{ workerData: { mod, name, type } }
|
|
);
|
|
Object.defineProperty(this, WORKER, {
|
|
value: worker
|
|
});
|
|
worker.on('message', data => {
|
|
const event = new Event('message');
|
|
event.data = data;
|
|
this.dispatchEvent(event);
|
|
});
|
|
worker.on('error', error => {
|
|
error.type = 'error';
|
|
this.dispatchEvent(error);
|
|
});
|
|
worker.on('exit', () => {
|
|
this.dispatchEvent(new Event('close'));
|
|
});
|
|
}
|
|
postMessage(data, transferList) {
|
|
this[WORKER].postMessage(data, transferList);
|
|
}
|
|
terminate() {
|
|
this[WORKER].terminate();
|
|
}
|
|
}
|
|
Worker.prototype.onmessage = Worker.prototype.onerror = Worker.prototype.onclose = null;
|
|
return Worker;
|
|
}
|
|
|
|
function workerThread() {
|
|
let { mod, name, type } = threads.workerData;
|
|
if (!mod) return mainThread();
|
|
|
|
// turn global into a mock WorkerGlobalScope
|
|
const self = global.self = global;
|
|
|
|
// enqueue messages to dispatch after modules are loaded
|
|
let q = [];
|
|
function flush() {
|
|
const buffered = q;
|
|
q = null;
|
|
buffered.forEach(event => { self.dispatchEvent(event); });
|
|
}
|
|
threads.parentPort.on('message', data => {
|
|
const event = new Event('message');
|
|
event.data = data;
|
|
if (q == null) self.dispatchEvent(event);
|
|
else q.push(event);
|
|
});
|
|
threads.parentPort.on('error', err => {
|
|
err.type = 'Error';
|
|
self.dispatchEvent(err);
|
|
});
|
|
|
|
class WorkerGlobalScope extends EventTarget {
|
|
postMessage(data, transferList) {
|
|
threads.parentPort.postMessage(data, transferList);
|
|
}
|
|
// Emulates https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/close
|
|
close() {
|
|
process.exit();
|
|
}
|
|
}
|
|
let proto = Object.getPrototypeOf(global);
|
|
delete proto.constructor;
|
|
Object.defineProperties(WorkerGlobalScope.prototype, proto);
|
|
proto = Object.setPrototypeOf(global, new WorkerGlobalScope());
|
|
['postMessage', 'addEventListener', 'removeEventListener', 'dispatchEvent'].forEach(fn => {
|
|
proto[fn] = proto[fn].bind(global);
|
|
});
|
|
global.name = name;
|
|
|
|
const isDataUrl = /^data:/.test(mod);
|
|
if (type === 'module') {
|
|
import(mod)
|
|
.catch(err => {
|
|
if (isDataUrl && err.message === 'Not supported') {
|
|
console.warn('Worker(): Importing data: URLs requires Node 12.10+. Falling back to classic worker.');
|
|
return evaluateDataUrl(mod, name);
|
|
}
|
|
console.error(err);
|
|
})
|
|
.then(flush);
|
|
}
|
|
else {
|
|
try {
|
|
if (/^data:/.test(mod)) {
|
|
evaluateDataUrl(mod, name);
|
|
}
|
|
else {
|
|
require(mod);
|
|
}
|
|
}
|
|
catch (err) {
|
|
console.error(err);
|
|
}
|
|
Promise.resolve().then(flush);
|
|
}
|
|
}
|
|
|
|
function evaluateDataUrl(url, name) {
|
|
const { data } = parseDataUrl(url);
|
|
return VM.runInThisContext(data, {
|
|
filename: 'worker.<'+(name || 'data:')+'>'
|
|
});
|
|
}
|
|
|
|
function parseDataUrl(url) {
|
|
let [m, type, encoding, data] = url.match(/^data: *([^;,]*)(?: *; *([^,]*))? *,(.*)$/) || [];
|
|
if (!m) throw Error('Invalid Data URL.');
|
|
if (encoding) switch (encoding.toLowerCase()) {
|
|
case 'base64':
|
|
data = Buffer.from(data, 'base64').toString();
|
|
break;
|
|
default:
|
|
throw Error('Unknown Data URL encoding "' + encoding + '"');
|
|
}
|
|
return { type, data };
|
|
}
|
|
|
|
/* global navigator, WebAssembly */
|
|
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// const MEM_SIZE = 1000; // Memory size in 64K Pakes (512Mb)
|
|
const MEM_SIZE = 25; // Memory size in 64K Pakes (1600Kb)
|
|
|
|
class Deferred {
|
|
constructor() {
|
|
this.promise = new Promise((resolve, reject)=> {
|
|
this.reject = reject;
|
|
this.resolve = resolve;
|
|
});
|
|
}
|
|
}
|
|
|
|
function sleep(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
function stringToBase64(str) {
|
|
if (process.browser) {
|
|
return globalThis.btoa(str);
|
|
} else {
|
|
return Buffer.from(str).toString("base64");
|
|
}
|
|
}
|
|
|
|
const threadSource = stringToBase64("(" + thread.toString() + ")(self)");
|
|
const workerSource = "data:application/javascript;base64," + threadSource;
|
|
|
|
|
|
|
|
async function buildThreadManager(wasm, singleThread) {
|
|
const tm = new ThreadManager();
|
|
|
|
tm.memory = new WebAssembly.Memory({initial:MEM_SIZE});
|
|
tm.u8 = new Uint8Array(tm.memory.buffer);
|
|
tm.u32 = new Uint32Array(tm.memory.buffer);
|
|
|
|
const wasmModule = await WebAssembly.compile(wasm.code);
|
|
|
|
tm.instance = await WebAssembly.instantiate(wasmModule, {
|
|
env: {
|
|
"memory": tm.memory
|
|
}
|
|
});
|
|
|
|
tm.singleThread = singleThread;
|
|
tm.initalPFree = tm.u32[0]; // Save the Pointer to free space.
|
|
tm.pq = wasm.pq;
|
|
tm.pr = wasm.pr;
|
|
tm.pG1gen = wasm.pG1gen;
|
|
tm.pG1zero = wasm.pG1zero;
|
|
tm.pG2gen = wasm.pG2gen;
|
|
tm.pG2zero = wasm.pG2zero;
|
|
tm.pOneT = wasm.pOneT;
|
|
|
|
// tm.pTmp0 = tm.alloc(curve.G2.F.n8*3);
|
|
// tm.pTmp1 = tm.alloc(curve.G2.F.n8*3);
|
|
|
|
|
|
if (singleThread) {
|
|
tm.code = wasm.code;
|
|
tm.taskManager = thread();
|
|
await tm.taskManager([{
|
|
cmd: "INIT",
|
|
init: MEM_SIZE,
|
|
code: tm.code.slice()
|
|
}]);
|
|
tm.concurrency = 1;
|
|
} else {
|
|
tm.workers = [];
|
|
tm.pendingDeferreds = [];
|
|
tm.working = [];
|
|
|
|
let concurrency;
|
|
|
|
if ((typeof(navigator) === "object") && navigator.hardwareConcurrency) {
|
|
concurrency = navigator.hardwareConcurrency;
|
|
} else {
|
|
concurrency = os.cpus().length;
|
|
}
|
|
|
|
if(concurrency == 0){
|
|
concurrency = 2;
|
|
}
|
|
|
|
// Limit to 64 threads for memory reasons.
|
|
if (concurrency>64) concurrency=64;
|
|
tm.concurrency = concurrency;
|
|
|
|
for (let i = 0; i<concurrency; i++) {
|
|
|
|
tm.workers[i] = new Worker(workerSource);
|
|
|
|
tm.workers[i].addEventListener("message", getOnMsg(i));
|
|
|
|
tm.working[i]=false;
|
|
}
|
|
|
|
const initPromises = [];
|
|
for (let i=0; i<tm.workers.length;i++) {
|
|
const copyCode = wasm.code.slice();
|
|
initPromises.push(tm.postAction(i, [{
|
|
cmd: "INIT",
|
|
init: MEM_SIZE,
|
|
code: copyCode
|
|
}], [copyCode.buffer]));
|
|
}
|
|
|
|
await Promise.all(initPromises);
|
|
|
|
}
|
|
return tm;
|
|
|
|
function getOnMsg(i) {
|
|
return function(e) {
|
|
let data;
|
|
if ((e)&&(e.data)) {
|
|
data = e.data;
|
|
} else {
|
|
data = e;
|
|
}
|
|
|
|
tm.working[i]=false;
|
|
tm.pendingDeferreds[i].resolve(data);
|
|
tm.processWorks();
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
class ThreadManager {
|
|
constructor() {
|
|
this.actionQueue = [];
|
|
this.oldPFree = 0;
|
|
}
|
|
|
|
startSyncOp() {
|
|
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");
|
|
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");
|
|
}
|
|
this.working[workerId] = true;
|
|
|
|
this.pendingDeferreds[workerId] = _deferred ? _deferred : new Deferred();
|
|
this.workers[workerId].postMessage(e, transfers);
|
|
|
|
return this.pendingDeferreds[workerId].promise;
|
|
}
|
|
|
|
processWorks() {
|
|
for (let i=0; (i<this.workers.length)&&(this.actionQueue.length > 0); i++) {
|
|
if (this.working[i] == false) {
|
|
const work = this.actionQueue.shift();
|
|
this.postAction(i, work.data, work.transfers, work.deferred);
|
|
}
|
|
}
|
|
}
|
|
|
|
queueAction(actionData, transfers) {
|
|
const d = new Deferred();
|
|
|
|
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;
|
|
}
|
|
|
|
resetMemory() {
|
|
this.u32[0] = this.initalPFree;
|
|
}
|
|
|
|
allocBuff(buff) {
|
|
const pointer = this.alloc(buff.byteLength);
|
|
this.setBuff(pointer, buff);
|
|
return pointer;
|
|
}
|
|
|
|
getBuff(pointer, length) {
|
|
return this.u8.slice(pointer, pointer+ length);
|
|
}
|
|
|
|
setBuff(pointer, buffer) {
|
|
this.u8.set(new Uint8Array(buffer), pointer);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
async terminate() {
|
|
for (let i=0; i<this.workers.length; i++) {
|
|
this.workers[i].postMessage([{cmd: "TERMINATE"}]);
|
|
}
|
|
await sleep(200);
|
|
}
|
|
|
|
}
|
|
|
|
function buildBatchApplyKey(curve, groupName) {
|
|
const G = curve[groupName];
|
|
const Fr = curve.Fr;
|
|
const tm = curve.tm;
|
|
|
|
curve[groupName].batchApplyKey = async function(buff, first, inc, inType, outType) {
|
|
inType = inType || "affine";
|
|
outType = outType || "affine";
|
|
let fnName, fnAffine;
|
|
let sGin, sGmid, sGout;
|
|
if (groupName == "G1") {
|
|
if (inType == "jacobian") {
|
|
sGin = G.F.n8*3;
|
|
fnName = "g1m_batchApplyKey";
|
|
} else {
|
|
sGin = G.F.n8*2;
|
|
fnName = "g1m_batchApplyKeyMixed";
|
|
}
|
|
sGmid = G.F.n8*3;
|
|
if (outType == "jacobian") {
|
|
sGout = G.F.n8*3;
|
|
} else {
|
|
fnAffine = "g1m_batchToAffine";
|
|
sGout = G.F.n8*2;
|
|
}
|
|
} else if (groupName == "G2") {
|
|
if (inType == "jacobian") {
|
|
sGin = G.F.n8*3;
|
|
fnName = "g2m_batchApplyKey";
|
|
} else {
|
|
sGin = G.F.n8*2;
|
|
fnName = "g2m_batchApplyKeyMixed";
|
|
}
|
|
sGmid = G.F.n8*3;
|
|
if (outType == "jacobian") {
|
|
sGout = G.F.n8*3;
|
|
} else {
|
|
fnAffine = "g2m_batchToAffine";
|
|
sGout = G.F.n8*2;
|
|
}
|
|
} else if (groupName == "Fr") {
|
|
fnName = "frm_batchApplyKey";
|
|
sGin = G.n8;
|
|
sGmid = G.n8;
|
|
sGout = G.n8;
|
|
} else {
|
|
throw new Error("Invalid group: " + groupName);
|
|
}
|
|
const nPoints = Math.floor(buff.byteLength / sGin);
|
|
const pointsPerChunk = Math.floor(nPoints/tm.concurrency);
|
|
const opPromises = [];
|
|
inc = Fr.e(inc);
|
|
let t = Fr.e(first);
|
|
for (let i=0; i<tm.concurrency; i++) {
|
|
let n;
|
|
if (i< tm.concurrency-1) {
|
|
n = pointsPerChunk;
|
|
} else {
|
|
n = nPoints - i*pointsPerChunk;
|
|
}
|
|
if (n==0) continue;
|
|
|
|
const task = [];
|
|
|
|
task.push({
|
|
cmd: "ALLOCSET",
|
|
var: 0,
|
|
buff: buff.slice(i*pointsPerChunk*sGin, i*pointsPerChunk*sGin + n*sGin)
|
|
});
|
|
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));
|
|
t = Fr.mul(t, Fr.exp(inc, n));
|
|
}
|
|
|
|
const result = await Promise.all(opPromises);
|
|
|
|
let outBuff;
|
|
if (buff instanceof BigBuffer) {
|
|
outBuff = new BigBuffer(nPoints*sGout);
|
|
} else {
|
|
outBuff = new Uint8Array(nPoints*sGout);
|
|
}
|
|
|
|
let p=0;
|
|
for (let i=0; i<result.length; i++) {
|
|
outBuff.set(result[i][0], p);
|
|
p += result[i][0].byteLength;
|
|
}
|
|
|
|
return outBuff;
|
|
};
|
|
}
|
|
|
|
function buildPairing(curve) {
|
|
const tm = curve.tm;
|
|
curve.pairing = function pairing(a, b) {
|
|
|
|
tm.startSyncOp();
|
|
const pA = tm.allocBuff(curve.G1.toJacobian(a));
|
|
const pB = tm.allocBuff(curve.G2.toJacobian(b));
|
|
const pRes = tm.alloc(curve.Gt.n8);
|
|
tm.instance.exports[curve.name + "_pairing"](pA, pB, pRes);
|
|
|
|
const res = tm.getBuff(pRes, curve.Gt.n8);
|
|
|
|
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;
|
|
}
|
|
|
|
const opPromises = [];
|
|
for (let i=0; i<nEqs; i++) {
|
|
|
|
const task = [];
|
|
|
|
const g1Buff = curve.G1.toJacobian(arguments[i*2]);
|
|
task.push({cmd: "ALLOCSET", var: 0, buff: g1Buff});
|
|
task.push({cmd: "ALLOC", var: 1, len: curve.prePSize});
|
|
|
|
const g2Buff = curve.G2.toJacobian(arguments[i*2 +1]);
|
|
task.push({cmd: "ALLOCSET", var: 2, buff: g2Buff});
|
|
task.push({cmd: "ALLOC", var: 3, len: curve.preQSize});
|
|
|
|
task.push({cmd: "ALLOC", var: 4, len: curve.Gt.n8});
|
|
|
|
task.push({cmd: "CALL", fnName: curve.name + "_prepareG1", params: [
|
|
{var: 0},
|
|
{var: 1}
|
|
]});
|
|
|
|
task.push({cmd: "CALL", fnName: curve.name + "_prepareG2", params: [
|
|
{var: 2},
|
|
{var: 3}
|
|
]});
|
|
|
|
task.push({cmd: "CALL", fnName: curve.name + "_millerLoop", params: [
|
|
{var: 1},
|
|
{var: 3},
|
|
{var: 4}
|
|
]});
|
|
|
|
task.push({cmd: "GET", out: 0, var: 4, len: curve.Gt.n8});
|
|
|
|
opPromises.push(
|
|
tm.queueAction(task)
|
|
);
|
|
}
|
|
|
|
|
|
const result = await Promise.all(opPromises);
|
|
|
|
tm.startSyncOp();
|
|
const pRes = tm.alloc(curve.Gt.n8);
|
|
tm.instance.exports.ftm_one(pRes);
|
|
|
|
for (let i=0; i<result.length; i++) {
|
|
const pMR = tm.allocBuff(result[i][0]);
|
|
tm.instance.exports.ftm_mul(pRes, pMR, pRes);
|
|
}
|
|
tm.instance.exports[curve.name + "_finalExponentiation"](pRes, pRes);
|
|
|
|
const pCt = tm.allocBuff(buffCt);
|
|
|
|
const r = !!tm.instance.exports.ftm_eq(pRes, pCt);
|
|
|
|
tm.endSyncOp();
|
|
|
|
return r;
|
|
};
|
|
|
|
curve.prepareG1 = function(p) {
|
|
this.tm.startSyncOp();
|
|
const pP = this.tm.allocBuff(p);
|
|
const pPrepP = this.tm.alloc(this.prePSize);
|
|
this.tm.instance.exports[this.name + "_prepareG1"](pP, pPrepP);
|
|
const res = this.tm.getBuff(pPrepP, this.prePSize);
|
|
this.tm.endSyncOp();
|
|
return res;
|
|
};
|
|
|
|
curve.prepareG2 = function(q) {
|
|
this.tm.startSyncOp();
|
|
const pQ = this.tm.allocBuff(q);
|
|
const pPrepQ = this.tm.alloc(this.preQSize);
|
|
this.tm.instance.exports[this.name + "_prepareG2"](pQ, pPrepQ);
|
|
const res = this.tm.getBuff(pPrepQ, this.preQSize);
|
|
this.tm.endSyncOp();
|
|
return res;
|
|
};
|
|
|
|
curve.millerLoop = function(preP, preQ) {
|
|
this.tm.startSyncOp();
|
|
const pPreP = this.tm.allocBuff(preP);
|
|
const pPreQ = this.tm.allocBuff(preQ);
|
|
const pRes = this.tm.alloc(this.Gt.n8);
|
|
this.tm.instance.exports[this.name + "_millerLoop"](pPreP, pPreQ, pRes);
|
|
const res = this.tm.getBuff(pRes, this.Gt.n8);
|
|
this.tm.endSyncOp();
|
|
return res;
|
|
};
|
|
|
|
curve.finalExponentiation = function(a) {
|
|
this.tm.startSyncOp();
|
|
const pA = this.tm.allocBuff(a);
|
|
const pRes = this.tm.alloc(this.Gt.n8);
|
|
this.tm.instance.exports[this.name + "_finalExponentiation"](pA, pRes);
|
|
const res = this.tm.getBuff(pRes, this.Gt.n8);
|
|
this.tm.endSyncOp();
|
|
return res;
|
|
};
|
|
|
|
}
|
|
|
|
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
|
|
];
|
|
|
|
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);
|
|
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);
|
|
};
|
|
}
|
|
|
|
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;
|
|
|
|
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";
|
|
|
|
if (outType == "affine") {
|
|
sOut = G.F.n8*2;
|
|
fnMid2Out = "g1m_batchToAffine";
|
|
} 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;
|
|
}
|
|
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";
|
|
}
|
|
|
|
|
|
let returnArray = false;
|
|
if (Array.isArray(buff)) {
|
|
buff = array2buffer(buff, sIn);
|
|
returnArray = true;
|
|
} else {
|
|
buff = buff.slice(0, buff.byteLength);
|
|
}
|
|
|
|
const nPoints = buff.byteLength / sIn;
|
|
const bits = log2(nPoints);
|
|
|
|
if ((1 << bits) != nPoints) {
|
|
throw new Error("fft must be multiple of 2" );
|
|
}
|
|
|
|
if (bits == Fr.s +1) {
|
|
let buffOut;
|
|
|
|
if (inverse) {
|
|
buffOut = await _fftExtInv(buff, inType, outType, logger, loggerTxt);
|
|
} else {
|
|
buffOut = await _fftExt(buff, inType, outType, logger, loggerTxt);
|
|
}
|
|
|
|
if (returnArray) {
|
|
return buffer2array(buffOut, sOut);
|
|
} else {
|
|
return buffOut;
|
|
}
|
|
}
|
|
|
|
let inv;
|
|
if (inverse) {
|
|
inv = Fr.inv(Fr.e(nPoints));
|
|
}
|
|
|
|
let buffOut;
|
|
|
|
buffReverseBits(buff, sIn);
|
|
|
|
let chunks;
|
|
let pointsInChunk = Math.min(1 << MAX_BITS_THREAD, nPoints);
|
|
let nChunks = nPoints / pointsInChunk;
|
|
|
|
while ((nChunks < tm.concurrency)&&(pointsInChunk>=16)) {
|
|
nChunks *= 2;
|
|
pointsInChunk /= 2;
|
|
}
|
|
|
|
const l2Chunk = log2(pointsInChunk);
|
|
|
|
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}]});
|
|
}
|
|
|
|
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;
|
|
}));
|
|
}
|
|
|
|
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<nGroups; j++) {
|
|
for (let k=0; k <nChunksPerGroup/2; k++) {
|
|
const first = Fr.exp( Fr.w[i], k*pointsInChunk);
|
|
const inc = Fr.w[i];
|
|
const o1 = j*nChunksPerGroup + k;
|
|
const o2 = j*nChunksPerGroup + k + nChunksPerGroup/2;
|
|
|
|
const task = [];
|
|
task.push({cmd: "ALLOCSET", var: 0, buff: chunks[o1]});
|
|
task.push({cmd: "ALLOCSET", var: 1, buff: chunks[o2]});
|
|
task.push({cmd: "ALLOCSET", var: 2, buff: first});
|
|
task.push({cmd: "ALLOCSET", var: 3, buff: inc});
|
|
task.push({cmd: "CALL", fnName: fnFFTJoin, params:[
|
|
{var: 0},
|
|
{var: 1},
|
|
{val: pointsInChunk},
|
|
{var: 2},
|
|
{var: 3}
|
|
]});
|
|
if (i==bits) {
|
|
if (fnFFTFinal) {
|
|
task.push({cmd: "ALLOCSET", var: 4, buff: inv});
|
|
task.push({cmd: "CALL", fnName: fnFFTFinal, params:[
|
|
{var: 0},
|
|
{val: pointsInChunk},
|
|
{var: 4},
|
|
]});
|
|
task.push({cmd: "CALL", fnName: fnFFTFinal, params:[
|
|
{var: 1},
|
|
{val: pointsInChunk},
|
|
{var: 4},
|
|
]});
|
|
}
|
|
if (fnMid2Out) {
|
|
task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:0}, {val: pointsInChunk}, {var: 0}]});
|
|
task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:1}, {val: pointsInChunk}, {var: 1}]});
|
|
}
|
|
task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sOut});
|
|
task.push({cmd: "GET", out: 1, var: 1, len: pointsInChunk*sOut});
|
|
} else {
|
|
task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sMid});
|
|
task.push({cmd: "GET", out: 1, var: 1, len: pointsInChunk*sMid});
|
|
}
|
|
opPromises.push(tm.queueAction(task).then( (r) => {
|
|
if (logger) logger.debug(`${loggerTxt}: fft ${bits} join ${i}/${bits} ${j+1}/${nGroups} ${k}/${nChunksPerGroup/2}`);
|
|
return r;
|
|
}));
|
|
}
|
|
}
|
|
|
|
const res = await Promise.all(opPromises);
|
|
for (let j=0; j<nGroups; j++) {
|
|
for (let k=0; k <nChunksPerGroup/2; k++) {
|
|
const o1 = j*nChunksPerGroup + k;
|
|
const o2 = j*nChunksPerGroup + k + nChunksPerGroup/2;
|
|
const resChunk = res.shift();
|
|
chunks[o1] = resChunk[0];
|
|
chunks[o2] = resChunk[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (buff instanceof BigBuffer) {
|
|
buffOut = new BigBuffer(nPoints*sOut);
|
|
} else {
|
|
buffOut = new Uint8Array(nPoints*sOut);
|
|
}
|
|
if (inverse) {
|
|
buffOut.set(chunks[0].slice((pointsInChunk-1)*sOut));
|
|
let p= sOut;
|
|
for (let i=nChunks-1; i>0; i--) {
|
|
buffOut.set(chunks[i], p);
|
|
p += 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<nChunks; i++) {
|
|
buffOut.set(chunks[i], pointsInChunk*sOut*i);
|
|
delete chunks[i];
|
|
}
|
|
}
|
|
|
|
if (returnArray) {
|
|
return buffer2array(buffOut, sOut);
|
|
} else {
|
|
return buffOut;
|
|
}
|
|
}
|
|
|
|
async function _fftExt(buff, inType, outType, logger, loggerTxt) {
|
|
let b1, b2;
|
|
b1 = buff.slice( 0 , buff.byteLength/2);
|
|
b2 = buff.slice( buff.byteLength/2, buff.byteLength);
|
|
|
|
const promises = [];
|
|
|
|
[b1, b2] = await _fftJoinExt(b1, b2, "fftJoinExt", Fr.one, Fr.shift, inType, "jacobian", logger, loggerTxt);
|
|
|
|
promises.push( _fft(b1, false, "jacobian", outType, logger, loggerTxt));
|
|
promises.push( _fft(b2, false, "jacobian", outType, logger, loggerTxt));
|
|
|
|
const res1 = await Promise.all(promises);
|
|
|
|
let buffOut;
|
|
if (res1[0].byteLength > (1<<28)) {
|
|
buffOut = new BigBuffer(res1[0].byteLength*2);
|
|
} else {
|
|
buffOut = new Uint8Array(res1[0].byteLength*2);
|
|
}
|
|
|
|
buffOut.set(res1[0]);
|
|
buffOut.set(res1[1], res1[0].byteLength);
|
|
|
|
return buffOut;
|
|
}
|
|
|
|
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 promises = [];
|
|
|
|
promises.push( _fft(b1, true, inType, "jacobian", logger, loggerTxt));
|
|
promises.push( _fft(b2, true, inType, "jacobian", logger, loggerTxt));
|
|
|
|
[b1, b2] = await Promise.all(promises);
|
|
|
|
const res1 = await _fftJoinExt(b1, b2, "fftJoinExtInv", Fr.one, Fr.shiftInv, "jacobian", outType, logger, loggerTxt);
|
|
|
|
let buffOut;
|
|
if (res1[0].byteLength > (1<<28)) {
|
|
buffOut = new BigBuffer(res1[0].byteLength*2);
|
|
} else {
|
|
buffOut = new Uint8Array(res1[0].byteLength*2);
|
|
}
|
|
|
|
buffOut.set(res1[0]);
|
|
buffOut.set(res1[1], res1[0].byteLength);
|
|
|
|
return buffOut;
|
|
}
|
|
|
|
|
|
async function _fftJoinExt(buff1, buff2, fn, first, inc, inType, outType, logger, loggerTxt) {
|
|
const MAX_CHUNK_SIZE = 1<<16;
|
|
const MIN_CHUNK_SIZE = 1<<4;
|
|
|
|
let fnName;
|
|
let fnIn2Mid, fnMid2Out;
|
|
let sOut, sIn, sMid;
|
|
|
|
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");
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
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 opPromises = [];
|
|
|
|
for (let i=0; i<nPoints; i += chunkSize) {
|
|
if (logger) logger.debug(`${loggerTxt}: fftJoinExt Start: ${i}/${nPoints}`);
|
|
const n= Math.min(nPoints - i, chunkSize);
|
|
|
|
const firstChunk = Fr.mul(first, Fr.exp( inc, i));
|
|
const task = [];
|
|
|
|
const b1 = buff1.slice(i*sIn, (i+n)*sIn);
|
|
const b2 = buff2.slice(i*sIn, (i+n)*sIn);
|
|
|
|
task.push({cmd: "ALLOC", var: 0, len: sMid*n});
|
|
task.push({cmd: "SET", var: 0, buff: b1});
|
|
task.push({cmd: "ALLOC", var: 1, len: sMid*n});
|
|
task.push({cmd: "SET", var: 1, buff: b2});
|
|
task.push({cmd: "ALLOCSET", var: 2, buff: firstChunk});
|
|
task.push({cmd: "ALLOCSET", var: 3, buff: inc});
|
|
if (fnIn2Mid) {
|
|
task.push({cmd: "CALL", fnName:fnIn2Mid, params: [{var:0}, {val: n}, {var: 0}]});
|
|
task.push({cmd: "CALL", fnName:fnIn2Mid, params: [{var:1}, {val: n}, {var: 1}]});
|
|
}
|
|
task.push({cmd: "CALL", fnName: fnName, params: [
|
|
{var: 0},
|
|
{var: 1},
|
|
{val: n},
|
|
{var: 2},
|
|
{var: 3},
|
|
{val: Fr.s},
|
|
]});
|
|
if (fnMid2Out) {
|
|
task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:0}, {val: n}, {var: 0}]});
|
|
task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:1}, {val: n}, {var: 1}]});
|
|
}
|
|
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) => {
|
|
if (logger) logger.debug(`${loggerTxt}: fftJoinExt End: ${i}/${nPoints}`);
|
|
return r;
|
|
})
|
|
);
|
|
}
|
|
|
|
const result = await Promise.all(opPromises);
|
|
|
|
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);
|
|
}
|
|
|
|
let p =0;
|
|
for (let i=0; i<result.length; i++) {
|
|
fullBuffOut1.set(result[i][0], p);
|
|
fullBuffOut2.set(result[i][1], p);
|
|
p+=result[i][0].byteLength;
|
|
}
|
|
|
|
return [fullBuffOut1, fullBuffOut2];
|
|
}
|
|
|
|
|
|
G.fft = async function(buff, inType, outType, logger, loggerTxt) {
|
|
return await _fft(buff, false, inType, outType, logger, loggerTxt);
|
|
};
|
|
|
|
G.ifft = async function(buff, inType, outType, logger, loggerTxt) {
|
|
return await _fft(buff, true, inType, outType, logger, loggerTxt);
|
|
};
|
|
|
|
G.lagrangeEvaluations = async function (buff, inType, outType, logger, loggerTxt) {
|
|
inType = inType || "affine";
|
|
outType = outType || "affine";
|
|
|
|
let sIn;
|
|
if (groupName == "G1") {
|
|
if (inType == "affine") {
|
|
sIn = G.F.n8*2;
|
|
} else {
|
|
sIn = G.F.n8*3;
|
|
}
|
|
} else if (groupName == "G2") {
|
|
if (inType == "affine") {
|
|
sIn = G.F.n8*2;
|
|
} else {
|
|
sIn = G.F.n8*3;
|
|
}
|
|
} else if (groupName == "Fr") {
|
|
sIn = Fr.n8;
|
|
} else {
|
|
throw new Error("Invalid group");
|
|
}
|
|
|
|
const nPoints = buff.byteLength /sIn;
|
|
const bits = log2(nPoints);
|
|
|
|
if ((2 ** bits)*sIn != buff.byteLength) {
|
|
if (logger) logger.error("lagrangeEvaluations iinvalid input size");
|
|
throw new Error("lagrangeEvaluations invalid Input size");
|
|
}
|
|
|
|
if (bits <= Fr.s) {
|
|
return await G.ifft(buff, inType, outType, logger, loggerTxt);
|
|
}
|
|
|
|
if (bits > Fr.s+1) {
|
|
if (logger) logger.error("lagrangeEvaluations input too big");
|
|
throw new Error("lagrangeEvaluations input too big");
|
|
}
|
|
|
|
let t0 = buff.slice(0, buff.byteLength/2);
|
|
let t1 = buff.slice(buff.byteLength/2, buff.byteLength);
|
|
|
|
|
|
const shiftToSmallM = Fr.exp(Fr.shift, nPoints/2);
|
|
const sConst = Fr.inv( Fr.sub(Fr.one, shiftToSmallM));
|
|
|
|
[t0, t1] = await _fftJoinExt(t0, t1, "prepareLagrangeEvaluation", sConst, Fr.shiftInv, inType, "jacobian", logger, loggerTxt + " prep");
|
|
|
|
const promises = [];
|
|
|
|
promises.push( _fft(t0, true, "jacobian", outType, logger, loggerTxt + " t0"));
|
|
promises.push( _fft(t1, true, "jacobian", outType, logger, loggerTxt + " t1"));
|
|
|
|
[t0, t1] = await Promise.all(promises);
|
|
|
|
let buffOut;
|
|
if (t0.byteLength > (1<<28)) {
|
|
buffOut = new BigBuffer(t0.byteLength*2);
|
|
} else {
|
|
buffOut = new Uint8Array(t0.byteLength*2);
|
|
}
|
|
|
|
buffOut.set(t0);
|
|
buffOut.set(t1, t0.byteLength);
|
|
|
|
return buffOut;
|
|
};
|
|
|
|
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");
|
|
}
|
|
|
|
const nPoints = Math.floor(buff.byteLength / sG);
|
|
const power = log2(nPoints);
|
|
|
|
let nChunks = 1 << log2(tm.concurrency);
|
|
|
|
if (nPoints <= nChunks*2) nChunks = 1;
|
|
|
|
const pointsPerChunk = nPoints / nChunks;
|
|
|
|
const powerChunk = log2(pointsPerChunk);
|
|
|
|
const opPromises = [];
|
|
for (let i=0; i<nChunks; i++) {
|
|
const task = [];
|
|
const b = buff.slice((i* pointsPerChunk)*sG, ((i+1)* pointsPerChunk)*sG);
|
|
task.push({cmd: "ALLOCSET", var: 0, buff: b});
|
|
for (let j=1; j<=powerChunk; j++) {
|
|
task.push({cmd: "CALL", fnName: fnName, params: [
|
|
{var: 0},
|
|
{val: pointsPerChunk},
|
|
{val: j}
|
|
]});
|
|
}
|
|
task.push({cmd: "GET", out: 0, var: 0, len: pointsPerChunk*sG});
|
|
opPromises.push(
|
|
tm.queueAction(task)
|
|
);
|
|
}
|
|
|
|
const result = await Promise.all(opPromises);
|
|
|
|
const chunks = [];
|
|
for (let i=0; i<result.length; i++) chunks[i] = result[i][0];
|
|
|
|
|
|
for (let i = powerChunk+1; i<=power; i++) {
|
|
const nGroups = 1 << (power - i);
|
|
const nChunksPerGroup = nChunks / nGroups;
|
|
const opPromises = [];
|
|
for (let j=0; j<nGroups; j++) {
|
|
for (let k=0; k <nChunksPerGroup/2; k++) {
|
|
const first = Fr.exp( Fr.w[i], k*pointsPerChunk);
|
|
const inc = Fr.w[i];
|
|
const o1 = j*nChunksPerGroup + k;
|
|
const o2 = j*nChunksPerGroup + k + nChunksPerGroup/2;
|
|
|
|
const task = [];
|
|
task.push({cmd: "ALLOCSET", var: 0, buff: chunks[o1]});
|
|
task.push({cmd: "ALLOCSET", var: 1, buff: chunks[o2]});
|
|
task.push({cmd: "ALLOCSET", var: 2, buff: first});
|
|
task.push({cmd: "ALLOCSET", var: 3, buff: inc});
|
|
task.push({cmd: "CALL", fnName: fnFFTJoin, params:[
|
|
{var: 0},
|
|
{var: 1},
|
|
{val: pointsPerChunk},
|
|
{var: 2},
|
|
{var: 3}
|
|
]});
|
|
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));
|
|
}
|
|
}
|
|
|
|
const res = await Promise.all(opPromises);
|
|
for (let j=0; j<nGroups; j++) {
|
|
for (let k=0; k <nChunksPerGroup/2; k++) {
|
|
const o1 = j*nChunksPerGroup + k;
|
|
const o2 = j*nChunksPerGroup + k + nChunksPerGroup/2;
|
|
const resChunk = res.shift();
|
|
chunks[o1] = resChunk[0];
|
|
chunks[o2] = resChunk[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
let fullBuffOut;
|
|
if (buff instanceof BigBuffer) {
|
|
fullBuffOut = new BigBuffer(nPoints*sG);
|
|
} else {
|
|
fullBuffOut = new Uint8Array(nPoints*sG);
|
|
}
|
|
let p =0;
|
|
for (let i=0; i<nChunks; i++) {
|
|
fullBuffOut.set(chunks[i], p);
|
|
p+=chunks[i].byteLength;
|
|
}
|
|
|
|
return fullBuffOut;
|
|
};
|
|
|
|
G.fftJoin = async function fftJoin(buff1, buff2, first, inc) {
|
|
const sG = G.F.n8*3;
|
|
let fnName;
|
|
if (groupName == "G1") {
|
|
fnName = "g1m_fftJoin";
|
|
} else if (groupName == "G2") {
|
|
fnName = "g2m_fftJoin";
|
|
} else if (groupName == "Fr") {
|
|
fnName = "frm_fftJoin";
|
|
} else {
|
|
throw new Error("Invalid group");
|
|
}
|
|
|
|
if (buff1.byteLength != buff2.byteLength) {
|
|
throw new Error("Invalid buffer size");
|
|
}
|
|
const nPoints = Math.floor(buff1.byteLength / sG);
|
|
if (nPoints != 1 << log2(nPoints)) {
|
|
throw new Error("Invalid number of points");
|
|
}
|
|
|
|
let nChunks = 1 << log2(tm.concurrency);
|
|
if (nPoints <= nChunks*2) nChunks = 1;
|
|
|
|
const pointsPerChunk = nPoints / nChunks;
|
|
|
|
|
|
const opPromises = [];
|
|
for (let i=0; i<nChunks; i++) {
|
|
const task = [];
|
|
|
|
const firstChunk = Fr.mul(first, Fr.exp(inc, i*pointsPerChunk));
|
|
const b1 = buff1.slice((i* pointsPerChunk)*sG, ((i+1)* pointsPerChunk)*sG);
|
|
const b2 = buff2.slice((i* pointsPerChunk)*sG, ((i+1)* pointsPerChunk)*sG);
|
|
task.push({cmd: "ALLOCSET", var: 0, buff: b1});
|
|
task.push({cmd: "ALLOCSET", var: 1, buff: b2});
|
|
task.push({cmd: "ALLOCSET", var: 2, buff: firstChunk});
|
|
task.push({cmd: "ALLOCSET", var: 3, buff: inc});
|
|
task.push({cmd: "CALL", fnName: fnName, params: [
|
|
{var: 0},
|
|
{var: 1},
|
|
{val: pointsPerChunk},
|
|
{var: 2},
|
|
{var: 3}
|
|
]});
|
|
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)
|
|
);
|
|
|
|
}
|
|
|
|
|
|
const result = await Promise.all(opPromises);
|
|
|
|
let fullBuffOut1;
|
|
let fullBuffOut2;
|
|
if (buff1 instanceof BigBuffer) {
|
|
fullBuffOut1 = new BigBuffer(nPoints*sG);
|
|
fullBuffOut2 = new BigBuffer(nPoints*sG);
|
|
} else {
|
|
fullBuffOut1 = new Uint8Array(nPoints*sG);
|
|
fullBuffOut2 = new Uint8Array(nPoints*sG);
|
|
}
|
|
|
|
let p =0;
|
|
for (let i=0; i<result.length; i++) {
|
|
fullBuffOut1.set(result[i][0], p);
|
|
fullBuffOut2.set(result[i][1], p);
|
|
p+=result[i][0].byteLength;
|
|
}
|
|
|
|
return [fullBuffOut1, fullBuffOut2];
|
|
};
|
|
|
|
|
|
|
|
G.fftFinal = async function fftFinal(buff, factor) {
|
|
const sG = G.F.n8*3;
|
|
const sGout = G.F.n8*2;
|
|
let fnName, fnToAffine;
|
|
if (groupName == "G1") {
|
|
fnName = "g1m_fftFinal";
|
|
fnToAffine = "g1m_batchToAffine";
|
|
} else if (groupName == "G2") {
|
|
fnName = "g2m_fftFinal";
|
|
fnToAffine = "g2m_batchToAffine";
|
|
} else {
|
|
throw new Error("Invalid group");
|
|
}
|
|
|
|
const nPoints = Math.floor(buff.byteLength / sG);
|
|
if (nPoints != 1 << log2(nPoints)) {
|
|
throw new Error("Invalid number of points");
|
|
}
|
|
|
|
const pointsPerChunk = Math.floor(nPoints / tm.concurrency);
|
|
|
|
const opPromises = [];
|
|
for (let i=0; i<tm.concurrency; i++) {
|
|
let n;
|
|
if (i< tm.concurrency-1) {
|
|
n = pointsPerChunk;
|
|
} else {
|
|
n = nPoints - i*pointsPerChunk;
|
|
}
|
|
if (n==0) continue;
|
|
const task = [];
|
|
const b = buff.slice((i* pointsPerChunk)*sG, (i*pointsPerChunk+n)*sG);
|
|
task.push({cmd: "ALLOCSET", var: 0, buff: b});
|
|
task.push({cmd: "ALLOCSET", var: 1, buff: factor});
|
|
task.push({cmd: "CALL", fnName: fnName, params: [
|
|
{var: 0},
|
|
{val: n},
|
|
{var: 1},
|
|
]});
|
|
task.push({cmd: "CALL", fnName: fnToAffine, params: [
|
|
{var: 0},
|
|
{val: n},
|
|
{var: 0},
|
|
]});
|
|
task.push({cmd: "GET", out: 0, var: 0, len: n*sGout});
|
|
opPromises.push(
|
|
tm.queueAction(task)
|
|
);
|
|
|
|
}
|
|
|
|
const result = await Promise.all(opPromises);
|
|
|
|
let fullBuffOut;
|
|
if (buff instanceof BigBuffer) {
|
|
fullBuffOut = new BigBuffer(nPoints*sGout);
|
|
} else {
|
|
fullBuffOut = new Uint8Array(nPoints*sGout);
|
|
}
|
|
|
|
let p =0;
|
|
for (let i=result.length-1; i>=0; i--) {
|
|
fullBuffOut.set(result[i][0], p);
|
|
p+=result[i][0].byteLength;
|
|
}
|
|
|
|
return fullBuffOut;
|
|
};
|
|
}
|
|
|
|
async function buildEngine(params) {
|
|
|
|
const tm = await buildThreadManager(params.wasm, params.singleThread);
|
|
|
|
|
|
const curve = {};
|
|
|
|
curve.q = e(params.wasm.q);
|
|
curve.r = e(params.wasm.r);
|
|
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;
|
|
|
|
buildBatchApplyKey(curve, "G1");
|
|
buildBatchApplyKey(curve, "G2");
|
|
buildBatchApplyKey(curve, "Fr");
|
|
|
|
buildMultiexp(curve, "G1");
|
|
buildMultiexp(curve, "G2");
|
|
|
|
buildFFT(curve, "G1");
|
|
buildFFT(curve, "G2");
|
|
buildFFT(curve, "Fr");
|
|
|
|
buildPairing(curve);
|
|
|
|
curve.array2buffer = function(arr, sG) {
|
|
const buff = new Uint8Array(sG*arr.length);
|
|
|
|
for (let i=0; i<arr.length; i++) {
|
|
buff.set(arr[i], i*sG);
|
|
}
|
|
|
|
return buff;
|
|
};
|
|
|
|
curve.buffer2array = function(buff , sG) {
|
|
const n= buff.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;
|
|
};
|
|
|
|
return curve;
|
|
}
|
|
|
|
var utils$3 = {};
|
|
|
|
/*
|
|
Copyright 2019 0KIMS association.
|
|
|
|
This file is part of wasmbuilder
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const bigInt = BigIntegerExports;
|
|
|
|
function toNumber(n) {
|
|
let v;
|
|
if (typeof n=="string") {
|
|
if (n.slice(0,2).toLowerCase() == "0x") {
|
|
v = bigInt(n.slice(2),16);
|
|
} else {
|
|
v = bigInt(n);
|
|
}
|
|
} else {
|
|
v = bigInt(n);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
function u32(n) {
|
|
const b = [];
|
|
const v = toNumber(n);
|
|
b.push(v.and(0xFF).toJSNumber());
|
|
b.push(v.shiftRight(8).and(0xFF).toJSNumber());
|
|
b.push(v.shiftRight(16).and(0xFF).toJSNumber());
|
|
b.push(v.shiftRight(24).and(0xFF).toJSNumber());
|
|
return b;
|
|
}
|
|
|
|
function u64(n) {
|
|
const b = [];
|
|
const v = toNumber(n);
|
|
b.push(v.and(0xFF).toJSNumber());
|
|
b.push(v.shiftRight(8).and(0xFF).toJSNumber());
|
|
b.push(v.shiftRight(16).and(0xFF).toJSNumber());
|
|
b.push(v.shiftRight(24).and(0xFF).toJSNumber());
|
|
b.push(v.shiftRight(32).and(0xFF).toJSNumber());
|
|
b.push(v.shiftRight(40).and(0xFF).toJSNumber());
|
|
b.push(v.shiftRight(48).and(0xFF).toJSNumber());
|
|
b.push(v.shiftRight(56).and(0xFF).toJSNumber());
|
|
return b;
|
|
}
|
|
|
|
function toUTF8Array(str) {
|
|
var utf8 = [];
|
|
for (var i=0; i < str.length; i++) {
|
|
var charcode = str.charCodeAt(i);
|
|
if (charcode < 0x80) utf8.push(charcode);
|
|
else if (charcode < 0x800) {
|
|
utf8.push(0xc0 | (charcode >> 6),
|
|
0x80 | (charcode & 0x3f));
|
|
}
|
|
else if (charcode < 0xd800 || charcode >= 0xe000) {
|
|
utf8.push(0xe0 | (charcode >> 12),
|
|
0x80 | ((charcode>>6) & 0x3f),
|
|
0x80 | (charcode & 0x3f));
|
|
}
|
|
// surrogate pair
|
|
else {
|
|
i++;
|
|
// UTF-16 encodes 0x10000-0x10FFFF by
|
|
// subtracting 0x10000 and splitting the
|
|
// 20 bits of 0x0-0xFFFFF into two halves
|
|
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
|
|
| (str.charCodeAt(i) & 0x3ff));
|
|
utf8.push(0xf0 | (charcode >>18),
|
|
0x80 | ((charcode>>12) & 0x3f),
|
|
0x80 | ((charcode>>6) & 0x3f),
|
|
0x80 | (charcode & 0x3f));
|
|
}
|
|
}
|
|
return utf8;
|
|
}
|
|
|
|
function string(str) {
|
|
const bytes = toUTF8Array(str);
|
|
return [ ...varuint32(bytes.length), ...bytes ];
|
|
}
|
|
|
|
function varuint(n) {
|
|
const code = [];
|
|
let v = toNumber(n);
|
|
if (v.isNegative()) throw new Error("Number cannot be negative");
|
|
while (!v.isZero()) {
|
|
code.push(v.and(0x7F).toJSNumber());
|
|
v = v.shiftRight(7);
|
|
}
|
|
if (code.length==0) code.push(0);
|
|
for (let i=0; i<code.length-1; i++) {
|
|
code[i] = code[i] | 0x80;
|
|
}
|
|
return code;
|
|
}
|
|
|
|
function varint(_n) {
|
|
let n, sign;
|
|
const bits = _n.bitLength().toJSNumber();
|
|
if (_n<0) {
|
|
sign = true;
|
|
n = bigInt.one.shiftLeft(bits).add(_n);
|
|
} else {
|
|
sign = false;
|
|
n = toNumber(_n);
|
|
}
|
|
const paddingBits = 7 - (bits % 7);
|
|
|
|
const padding = bigInt.one.shiftLeft(paddingBits).minus(1).shiftLeft(bits);
|
|
const paddingMask = ((1 << (7 - paddingBits))-1) | 0x80;
|
|
|
|
const code = varuint(n.add(padding));
|
|
|
|
if (!sign) {
|
|
code[code.length-1] = code[code.length-1] & paddingMask;
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
function varint32(n) {
|
|
let v = toNumber(n);
|
|
if (v.greater(bigInt("FFFFFFFF", 16))) throw new Error("Number too big");
|
|
if (v.greater(bigInt("7FFFFFFF", 16))) v = v.minus(bigInt("100000000",16));
|
|
if (v.lesser(bigInt("-80000000", 16))) throw new Error("Number too small");
|
|
return varint(v);
|
|
}
|
|
|
|
function varint64(n) {
|
|
let v = toNumber(n);
|
|
if (v.greater(bigInt("FFFFFFFFFFFFFFFF", 16))) throw new Error("Number too big");
|
|
if (v.greater(bigInt("7FFFFFFFFFFFFFFF", 16))) v = v.minus(bigInt("10000000000000000",16));
|
|
if (v.lesser(bigInt("-8000000000000000", 16))) throw new Error("Number too small");
|
|
return varint(v);
|
|
}
|
|
|
|
function varuint32(n) {
|
|
let v = toNumber(n);
|
|
if (v.greater(bigInt("FFFFFFFF", 16))) throw new Error("Number too big");
|
|
return varuint(v);
|
|
}
|
|
|
|
function varuint64(n) {
|
|
let v = toNumber(n);
|
|
if (v.greater(bigInt("FFFFFFFFFFFFFFFF", 16))) throw new Error("Number too big");
|
|
return varuint(v);
|
|
}
|
|
|
|
function toHexString(byteArray) {
|
|
return Array.from(byteArray, function(byte) {
|
|
return ("0" + (byte & 0xFF).toString(16)).slice(-2);
|
|
}).join("");
|
|
}
|
|
|
|
function ident(text) {
|
|
if (typeof text === "string") {
|
|
let lines = text.split("\n");
|
|
for (let i=0; i<lines.length; i++) {
|
|
if (lines[i]) lines[i] = " "+lines[i];
|
|
}
|
|
return lines.join("\n");
|
|
} else if (Array.isArray(text)) {
|
|
for (let i=0; i<text.length; i++ ) {
|
|
text[i] = ident(text[i]);
|
|
}
|
|
return text;
|
|
}
|
|
}
|
|
|
|
utils$3.toNumber = toNumber;
|
|
utils$3.u32 = u32;
|
|
utils$3.u64 = u64;
|
|
utils$3.varuint32 = varuint32;
|
|
utils$3.varuint64 = varuint64;
|
|
utils$3.varint32 = varint32;
|
|
utils$3.varint64 = varint64;
|
|
utils$3.string = string;
|
|
utils$3.toHexString = toHexString;
|
|
utils$3.ident = ident;
|
|
|
|
/*
|
|
Copyright 2019 0KIMS association.
|
|
|
|
This file is part of wasmbuilder
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const utils$2 = utils$3;
|
|
|
|
let CodeBuilder$1 = class CodeBuilder {
|
|
constructor(func) {
|
|
this.func = func;
|
|
this.functionName = func.functionName;
|
|
this.module = func.module;
|
|
}
|
|
|
|
setLocal(localName, valCode) {
|
|
const idx = this.func.localIdxByName[localName];
|
|
if (idx === undefined)
|
|
throw new Error(`Local Variable not defined: Function: ${this.functionName} local: ${localName} `);
|
|
return [...valCode, 0x21, ...utils$2.varuint32( idx )];
|
|
}
|
|
|
|
teeLocal(localName, valCode) {
|
|
const idx = this.func.localIdxByName[localName];
|
|
if (idx === undefined)
|
|
throw new Error(`Local Variable not defined: Function: ${this.functionName} local: ${localName} `);
|
|
return [...valCode, 0x22, ...utils$2.varuint32( idx )];
|
|
}
|
|
|
|
getLocal(localName) {
|
|
const idx = this.func.localIdxByName[localName];
|
|
if (idx === undefined)
|
|
throw new Error(`Local Variable not defined: Function: ${this.functionName} local: ${localName} `);
|
|
return [0x20, ...utils$2.varuint32( idx )];
|
|
}
|
|
|
|
i64_load8_s(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 0 : _align; // 8 bits alignment by default
|
|
return [...idxCode, 0x30, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i64_load8_u(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 0 : _align; // 8 bits alignment by default
|
|
return [...idxCode, 0x31, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i64_load16_s(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 1 : _align; // 16 bits alignment by default
|
|
return [...idxCode, 0x32, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i64_load16_u(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 1 : _align; // 16 bits alignment by default
|
|
return [...idxCode, 0x33, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i64_load32_s(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 2 : _align; // 32 bits alignment by default
|
|
return [...idxCode, 0x34, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i64_load32_u(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 2 : _align; // 32 bits alignment by default
|
|
return [...idxCode, 0x35, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i64_load(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 3 : _align; // 64 bits alignment by default
|
|
return [...idxCode, 0x29, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
|
|
i64_store(idxCode, _offset, _align, _codeVal) {
|
|
let offset, align, codeVal;
|
|
if (Array.isArray(_offset)) {
|
|
offset = 0;
|
|
align = 3;
|
|
codeVal = _offset;
|
|
} else if (Array.isArray(_align)) {
|
|
offset = _offset;
|
|
align = 3;
|
|
codeVal = _align;
|
|
} else if (Array.isArray(_codeVal)) {
|
|
offset = _offset;
|
|
align = _align;
|
|
codeVal = _codeVal;
|
|
}
|
|
return [...idxCode, ...codeVal, 0x37, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i64_store32(idxCode, _offset, _align, _codeVal) {
|
|
let offset, align, codeVal;
|
|
if (Array.isArray(_offset)) {
|
|
offset = 0;
|
|
align = 2;
|
|
codeVal = _offset;
|
|
} else if (Array.isArray(_align)) {
|
|
offset = _offset;
|
|
align = 2;
|
|
codeVal = _align;
|
|
} else if (Array.isArray(_codeVal)) {
|
|
offset = _offset;
|
|
align = _align;
|
|
codeVal = _codeVal;
|
|
}
|
|
return [...idxCode, ...codeVal, 0x3e, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
|
|
i64_store16(idxCode, _offset, _align, _codeVal) {
|
|
let offset, align, codeVal;
|
|
if (Array.isArray(_offset)) {
|
|
offset = 0;
|
|
align = 1;
|
|
codeVal = _offset;
|
|
} else if (Array.isArray(_align)) {
|
|
offset = _offset;
|
|
align = 1;
|
|
codeVal = _align;
|
|
} else if (Array.isArray(_codeVal)) {
|
|
offset = _offset;
|
|
align = _align;
|
|
codeVal = _codeVal;
|
|
}
|
|
return [...idxCode, ...codeVal, 0x3d, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
|
|
i64_store8(idxCode, _offset, _align, _codeVal) {
|
|
let offset, align, codeVal;
|
|
if (Array.isArray(_offset)) {
|
|
offset = 0;
|
|
align = 0;
|
|
codeVal = _offset;
|
|
} else if (Array.isArray(_align)) {
|
|
offset = _offset;
|
|
align = 0;
|
|
codeVal = _align;
|
|
} else if (Array.isArray(_codeVal)) {
|
|
offset = _offset;
|
|
align = _align;
|
|
codeVal = _codeVal;
|
|
}
|
|
return [...idxCode, ...codeVal, 0x3c, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i32_load8_s(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 0 : _align; // 32 bits alignment by default
|
|
return [...idxCode, 0x2c, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i32_load8_u(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 0 : _align; // 32 bits alignment by default
|
|
return [...idxCode, 0x2d, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i32_load16_s(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 1 : _align; // 32 bits alignment by default
|
|
return [...idxCode, 0x2e, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i32_load16_u(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 1 : _align; // 32 bits alignment by default
|
|
return [...idxCode, 0x2f, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i32_load(idxCode, _offset, _align) {
|
|
const offset = _offset || 0;
|
|
const align = (_align === undefined) ? 2 : _align; // 32 bits alignment by default
|
|
return [...idxCode, 0x28, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i32_store(idxCode, _offset, _align, _codeVal) {
|
|
let offset, align, codeVal;
|
|
if (Array.isArray(_offset)) {
|
|
offset = 0;
|
|
align = 2;
|
|
codeVal = _offset;
|
|
} else if (Array.isArray(_align)) {
|
|
offset = _offset;
|
|
align = 2;
|
|
codeVal = _align;
|
|
} else if (Array.isArray(_codeVal)) {
|
|
offset = _offset;
|
|
align = _align;
|
|
codeVal = _codeVal;
|
|
}
|
|
return [...idxCode, ...codeVal, 0x36, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
|
|
i32_store16(idxCode, _offset, _align, _codeVal) {
|
|
let offset, align, codeVal;
|
|
if (Array.isArray(_offset)) {
|
|
offset = 0;
|
|
align = 1;
|
|
codeVal = _offset;
|
|
} else if (Array.isArray(_align)) {
|
|
offset = _offset;
|
|
align = 1;
|
|
codeVal = _align;
|
|
} else if (Array.isArray(_codeVal)) {
|
|
offset = _offset;
|
|
align = _align;
|
|
codeVal = _codeVal;
|
|
}
|
|
return [...idxCode, ...codeVal, 0x3b, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
i32_store8(idxCode, _offset, _align, _codeVal) {
|
|
let offset, align, codeVal;
|
|
if (Array.isArray(_offset)) {
|
|
offset = 0;
|
|
align = 0;
|
|
codeVal = _offset;
|
|
} else if (Array.isArray(_align)) {
|
|
offset = _offset;
|
|
align = 0;
|
|
codeVal = _align;
|
|
} else if (Array.isArray(_codeVal)) {
|
|
offset = _offset;
|
|
align = _align;
|
|
codeVal = _codeVal;
|
|
}
|
|
return [...idxCode, ...codeVal, 0x3a, align, ...utils$2.varuint32(offset)];
|
|
}
|
|
|
|
call(fnName, ...args) {
|
|
const idx = this.module.functionIdxByName[fnName];
|
|
if (idx === undefined)
|
|
throw new Error(`Function not defined: Function: ${fnName}`);
|
|
return [...[].concat(...args), 0x10, ...utils$2.varuint32(idx)];
|
|
}
|
|
|
|
call_indirect(fnIdx, ...args) {
|
|
return [...[].concat(...args), ...fnIdx, 0x11, 0, 0];
|
|
}
|
|
|
|
if(condCode, thenCode, elseCode) {
|
|
if (elseCode) {
|
|
return [...condCode, 0x04, 0x40, ...thenCode, 0x05, ...elseCode, 0x0b];
|
|
} else {
|
|
return [...condCode, 0x04, 0x40, ...thenCode, 0x0b];
|
|
}
|
|
}
|
|
|
|
block(bCode) { return [0x02, 0x40, ...bCode, 0x0b]; }
|
|
loop(...args) {
|
|
return [0x03, 0x40, ...[].concat(...[...args]), 0x0b];
|
|
}
|
|
br_if(relPath, condCode) { return [...condCode, 0x0d, ...utils$2.varuint32(relPath)]; }
|
|
br(relPath) { return [0x0c, ...utils$2.varuint32(relPath)]; }
|
|
ret(rCode) { return [...rCode, 0x0f]; }
|
|
drop(dCode) { return [...dCode, 0x1a]; }
|
|
|
|
i64_const(num) { return [0x42, ...utils$2.varint64(num)]; }
|
|
i32_const(num) { return [0x41, ...utils$2.varint32(num)]; }
|
|
|
|
|
|
i64_eqz(opcode) { return [...opcode, 0x50]; }
|
|
i64_eq(op1code, op2code) { return [...op1code, ...op2code, 0x51]; }
|
|
i64_ne(op1code, op2code) { return [...op1code, ...op2code, 0x52]; }
|
|
i64_lt_s(op1code, op2code) { return [...op1code, ...op2code, 0x53]; }
|
|
i64_lt_u(op1code, op2code) { return [...op1code, ...op2code, 0x54]; }
|
|
i64_gt_s(op1code, op2code) { return [...op1code, ...op2code, 0x55]; }
|
|
i64_gt_u(op1code, op2code) { return [...op1code, ...op2code, 0x56]; }
|
|
i64_le_s(op1code, op2code) { return [...op1code, ...op2code, 0x57]; }
|
|
i64_le_u(op1code, op2code) { return [...op1code, ...op2code, 0x58]; }
|
|
i64_ge_s(op1code, op2code) { return [...op1code, ...op2code, 0x59]; }
|
|
i64_ge_u(op1code, op2code) { return [...op1code, ...op2code, 0x5a]; }
|
|
i64_add(op1code, op2code) { return [...op1code, ...op2code, 0x7c]; }
|
|
i64_sub(op1code, op2code) { return [...op1code, ...op2code, 0x7d]; }
|
|
i64_mul(op1code, op2code) { return [...op1code, ...op2code, 0x7e]; }
|
|
i64_div_s(op1code, op2code) { return [...op1code, ...op2code, 0x7f]; }
|
|
i64_div_u(op1code, op2code) { return [...op1code, ...op2code, 0x80]; }
|
|
i64_rem_s(op1code, op2code) { return [...op1code, ...op2code, 0x81]; }
|
|
i64_rem_u(op1code, op2code) { return [...op1code, ...op2code, 0x82]; }
|
|
i64_and(op1code, op2code) { return [...op1code, ...op2code, 0x83]; }
|
|
i64_or(op1code, op2code) { return [...op1code, ...op2code, 0x84]; }
|
|
i64_xor(op1code, op2code) { return [...op1code, ...op2code, 0x85]; }
|
|
i64_shl(op1code, op2code) { return [...op1code, ...op2code, 0x86]; }
|
|
i64_shr_s(op1code, op2code) { return [...op1code, ...op2code, 0x87]; }
|
|
i64_shr_u(op1code, op2code) { return [...op1code, ...op2code, 0x88]; }
|
|
i64_extend_i32_s(op1code) { return [...op1code, 0xac]; }
|
|
i64_extend_i32_u(op1code) { return [...op1code, 0xad]; }
|
|
i64_clz(op1code) { return [...op1code, 0x79]; }
|
|
i64_ctz(op1code) { return [...op1code, 0x7a]; }
|
|
|
|
i32_eqz(op1code) { return [...op1code, 0x45]; }
|
|
i32_eq(op1code, op2code) { return [...op1code, ...op2code, 0x46]; }
|
|
i32_ne(op1code, op2code) { return [...op1code, ...op2code, 0x47]; }
|
|
i32_lt_s(op1code, op2code) { return [...op1code, ...op2code, 0x48]; }
|
|
i32_lt_u(op1code, op2code) { return [...op1code, ...op2code, 0x49]; }
|
|
i32_gt_s(op1code, op2code) { return [...op1code, ...op2code, 0x4a]; }
|
|
i32_gt_u(op1code, op2code) { return [...op1code, ...op2code, 0x4b]; }
|
|
i32_le_s(op1code, op2code) { return [...op1code, ...op2code, 0x4c]; }
|
|
i32_le_u(op1code, op2code) { return [...op1code, ...op2code, 0x4d]; }
|
|
i32_ge_s(op1code, op2code) { return [...op1code, ...op2code, 0x4e]; }
|
|
i32_ge_u(op1code, op2code) { return [...op1code, ...op2code, 0x4f]; }
|
|
i32_add(op1code, op2code) { return [...op1code, ...op2code, 0x6a]; }
|
|
i32_sub(op1code, op2code) { return [...op1code, ...op2code, 0x6b]; }
|
|
i32_mul(op1code, op2code) { return [...op1code, ...op2code, 0x6c]; }
|
|
i32_div_s(op1code, op2code) { return [...op1code, ...op2code, 0x6d]; }
|
|
i32_div_u(op1code, op2code) { return [...op1code, ...op2code, 0x6e]; }
|
|
i32_rem_s(op1code, op2code) { return [...op1code, ...op2code, 0x6f]; }
|
|
i32_rem_u(op1code, op2code) { return [...op1code, ...op2code, 0x70]; }
|
|
i32_and(op1code, op2code) { return [...op1code, ...op2code, 0x71]; }
|
|
i32_or(op1code, op2code) { return [...op1code, ...op2code, 0x72]; }
|
|
i32_xor(op1code, op2code) { return [...op1code, ...op2code, 0x73]; }
|
|
i32_shl(op1code, op2code) { return [...op1code, ...op2code, 0x74]; }
|
|
i32_shr_s(op1code, op2code) { return [...op1code, ...op2code, 0x75]; }
|
|
i32_shr_u(op1code, op2code) { return [...op1code, ...op2code, 0x76]; }
|
|
i32_rotl(op1code, op2code) { return [...op1code, ...op2code, 0x77]; }
|
|
i32_rotr(op1code, op2code) { return [...op1code, ...op2code, 0x78]; }
|
|
i32_wrap_i64(op1code) { return [...op1code, 0xa7]; }
|
|
i32_clz(op1code) { return [...op1code, 0x67]; }
|
|
i32_ctz(op1code) { return [...op1code, 0x68]; }
|
|
|
|
unreachable() { return [ 0x0 ]; }
|
|
|
|
current_memory() { return [ 0x3f, 0]; }
|
|
|
|
comment() { return []; }
|
|
};
|
|
|
|
var codebuilder = CodeBuilder$1;
|
|
|
|
/*
|
|
Copyright 2019 0KIMS association.
|
|
|
|
This file is part of wasmbuilder
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const CodeBuilder = codebuilder;
|
|
const utils$1 = utils$3;
|
|
|
|
const typeCodes = {
|
|
"i32": 0x7f,
|
|
"i64": 0x7e,
|
|
"f32": 0x7d,
|
|
"f64": 0x7c,
|
|
"anyfunc": 0x70,
|
|
"func": 0x60,
|
|
"emptyblock": 0x40
|
|
};
|
|
|
|
|
|
let FunctionBuilder$1 = class FunctionBuilder {
|
|
|
|
constructor (module, fnName, fnType, moduleName, fieldName) {
|
|
if (fnType == "import") {
|
|
this.fnType = "import";
|
|
this.moduleName = moduleName;
|
|
this.fieldName = fieldName;
|
|
} else if (fnType == "internal") {
|
|
this.fnType = "internal";
|
|
} else {
|
|
throw new Error("Invalid function fnType: " + fnType);
|
|
}
|
|
this.module = module;
|
|
this.fnName = fnName;
|
|
this.params = [];
|
|
this.locals = [];
|
|
this.localIdxByName = {};
|
|
this.code = [];
|
|
this.returnType = null;
|
|
this.nextLocal =0;
|
|
}
|
|
|
|
addParam(paramName, paramType) {
|
|
if (this.localIdxByName[paramName])
|
|
throw new Error(`param already exists. Function: ${this.fnName}, Param: ${paramName} `);
|
|
const idx = this.nextLocal++;
|
|
this.localIdxByName[paramName] = idx;
|
|
this.params.push({
|
|
type: paramType
|
|
});
|
|
}
|
|
|
|
addLocal(localName, localType, _length) {
|
|
const length = _length || 1;
|
|
if (this.localIdxByName[localName])
|
|
throw new Error(`local already exists. Function: ${this.fnName}, Param: ${localName} `);
|
|
const idx = this.nextLocal++;
|
|
this.localIdxByName[localName] = idx;
|
|
this.locals.push({
|
|
type: localType,
|
|
length: length
|
|
});
|
|
}
|
|
|
|
setReturnType(returnType) {
|
|
if (this.returnType)
|
|
throw new Error(`returnType already defined. Function: ${this.fnName}`);
|
|
this.returnType = returnType;
|
|
}
|
|
|
|
getSignature() {
|
|
const params = [...utils$1.varuint32(this.params.length), ...this.params.map((p) => typeCodes[p.type])];
|
|
const returns = this.returnType ? [0x01, typeCodes[this.returnType]] : [0];
|
|
return [0x60, ...params, ...returns];
|
|
}
|
|
|
|
getBody() {
|
|
const locals = this.locals.map((l) => [
|
|
...utils$1.varuint32(l.length),
|
|
typeCodes[l.type]
|
|
]);
|
|
|
|
const body = [
|
|
...utils$1.varuint32(this.locals.length),
|
|
...[].concat(...locals),
|
|
...this.code,
|
|
0x0b
|
|
];
|
|
return [
|
|
...utils$1.varuint32(body.length),
|
|
...body
|
|
];
|
|
}
|
|
|
|
addCode(...code) {
|
|
this.code.push(...[].concat(...[...code]));
|
|
}
|
|
|
|
getCodeBuilder() {
|
|
return new CodeBuilder(this);
|
|
}
|
|
};
|
|
|
|
var functionbuilder = FunctionBuilder$1;
|
|
|
|
/*
|
|
Copyright 2019 0KIMS association.
|
|
|
|
This file is part of wasmbuilder
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const FunctionBuilder = functionbuilder;
|
|
const utils = utils$3;
|
|
|
|
let ModuleBuilder$1 = class ModuleBuilder {
|
|
|
|
constructor() {
|
|
this.functions = [];
|
|
this.functionIdxByName = {};
|
|
this.nImportFunctions = 0;
|
|
this.nInternalFunctions =0;
|
|
this.memory = {
|
|
pagesSize: 1,
|
|
moduleName: "env",
|
|
fieldName: "memory"
|
|
};
|
|
this.free = 8;
|
|
this.datas = [];
|
|
this.modules = {};
|
|
this.exports = [];
|
|
this.functionsTable = [];
|
|
}
|
|
|
|
build() {
|
|
this._setSignatures();
|
|
return new Uint8Array([
|
|
...utils.u32(0x6d736100),
|
|
...utils.u32(1),
|
|
...this._buildType(),
|
|
...this._buildImport(),
|
|
...this._buildFunctionDeclarations(),
|
|
...this._buildFunctionsTable(),
|
|
...this._buildExports(),
|
|
...this._buildElements(),
|
|
...this._buildCode(),
|
|
...this._buildData()
|
|
]);
|
|
}
|
|
|
|
addFunction(fnName) {
|
|
if (typeof(this.functionIdxByName[fnName]) !== "undefined")
|
|
throw new Error(`Function already defined: ${fnName}`);
|
|
|
|
const idx = this.functions.length;
|
|
this.functionIdxByName[fnName] = idx;
|
|
|
|
this.functions.push(new FunctionBuilder(this, fnName, "internal"));
|
|
|
|
this.nInternalFunctions++;
|
|
return this.functions[idx];
|
|
}
|
|
|
|
addIimportFunction(fnName, moduleName, _fieldName) {
|
|
if (typeof(this.functionIdxByName[fnName]) !== "undefined")
|
|
throw new Error(`Function already defined: ${fnName}`);
|
|
|
|
if ( (this.functions.length>0)
|
|
&&(this.functions[this.functions.length-1].type == "internal"))
|
|
throw new Error(`Import functions must be declared before internal: ${fnName}`);
|
|
|
|
let fieldName = _fieldName || fnName;
|
|
|
|
const idx = this.functions.length;
|
|
this.functionIdxByName[fnName] = idx;
|
|
|
|
this.functions.push(new FunctionBuilder(this, fnName, "import", moduleName, fieldName));
|
|
|
|
this.nImportFunctions ++;
|
|
return this.functions[idx];
|
|
}
|
|
|
|
setMemory(pagesSize, moduleName, fieldName) {
|
|
this.memory = {
|
|
pagesSize: pagesSize,
|
|
moduleName: moduleName || "env",
|
|
fieldName: fieldName || "memory"
|
|
};
|
|
}
|
|
|
|
exportFunction(fnName, _exportName) {
|
|
const exportName = _exportName || fnName;
|
|
if (typeof(this.functionIdxByName[fnName]) === "undefined")
|
|
throw new Error(`Function not defined: ${fnName}`);
|
|
const idx = this.functionIdxByName[fnName];
|
|
if (exportName != fnName) {
|
|
this.functionIdxByName[exportName] = idx;
|
|
}
|
|
this.exports.push({
|
|
exportName: exportName,
|
|
idx: idx
|
|
});
|
|
}
|
|
|
|
addFunctionToTable(fnName) {
|
|
const idx = this.functionIdxByName[fnName];
|
|
this.functionsTable.push(idx);
|
|
}
|
|
|
|
addData(offset, bytes) {
|
|
this.datas.push({
|
|
offset: offset,
|
|
bytes: bytes
|
|
});
|
|
}
|
|
|
|
alloc(a, b) {
|
|
let size;
|
|
let bytes;
|
|
if ((Array.isArray(a) || ArrayBuffer.isView(a)) && (typeof(b) === "undefined")) {
|
|
size = a.length;
|
|
bytes = a;
|
|
} else {
|
|
size = a;
|
|
bytes = b;
|
|
}
|
|
size = (((size-1)>>3) +1)<<3; // Align to 64 bits.
|
|
const p = this.free;
|
|
this.free += size;
|
|
if (bytes) {
|
|
this.addData(p, bytes);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
allocString(s) {
|
|
const encoder = new globalThis.TextEncoder();
|
|
const uint8array = encoder.encode(s);
|
|
return this.alloc([...uint8array, 0]);
|
|
}
|
|
|
|
_setSignatures() {
|
|
this.signatures = [];
|
|
const signatureIdxByName = {};
|
|
if (this.functionsTable.length>0) {
|
|
const signature = this.functions[this.functionsTable[0]].getSignature();
|
|
const signatureName = "s_"+utils.toHexString(signature);
|
|
signatureIdxByName[signatureName] = 0;
|
|
this.signatures.push(signature);
|
|
}
|
|
for (let i=0; i<this.functions.length; i++) {
|
|
const signature = this.functions[i].getSignature();
|
|
const signatureName = "s_"+utils.toHexString(signature);
|
|
if (typeof(signatureIdxByName[signatureName]) === "undefined") {
|
|
signatureIdxByName[signatureName] = this.signatures.length;
|
|
this.signatures.push(signature);
|
|
}
|
|
|
|
this.functions[i].signatureIdx = signatureIdxByName[signatureName];
|
|
}
|
|
|
|
}
|
|
|
|
_buildSection(sectionType, section) {
|
|
return [sectionType, ...utils.varuint32(section.length), ...section];
|
|
}
|
|
|
|
_buildType() {
|
|
return this._buildSection(
|
|
0x01,
|
|
[
|
|
...utils.varuint32(this.signatures.length),
|
|
...[].concat(...this.signatures)
|
|
]
|
|
);
|
|
}
|
|
|
|
_buildImport() {
|
|
const entries = [];
|
|
entries.push([
|
|
...utils.string(this.memory.moduleName),
|
|
...utils.string(this.memory.fieldName),
|
|
0x02,
|
|
0x00, //Flags no init valua
|
|
...utils.varuint32(this.memory.pagesSize)
|
|
]);
|
|
for (let i=0; i< this.nImportFunctions; i++) {
|
|
entries.push([
|
|
...utils.string(this.functions[i].moduleName),
|
|
...utils.string(this.functions[i].fieldName),
|
|
0x00,
|
|
...utils.varuint32(this.functions[i].signatureIdx)
|
|
]);
|
|
}
|
|
return this._buildSection(
|
|
0x02,
|
|
utils.varuint32(entries.length).concat(...entries)
|
|
);
|
|
}
|
|
|
|
_buildFunctionDeclarations() {
|
|
const entries = [];
|
|
for (let i=this.nImportFunctions; i< this.nImportFunctions + this.nInternalFunctions; i++) {
|
|
entries.push(...utils.varuint32(this.functions[i].signatureIdx));
|
|
}
|
|
return this._buildSection(
|
|
0x03,
|
|
[
|
|
...utils.varuint32(entries.length),
|
|
...[...entries]
|
|
]
|
|
);
|
|
}
|
|
|
|
_buildFunctionsTable() {
|
|
if (this.functionsTable.length == 0) return [];
|
|
return this._buildSection(
|
|
0x04,
|
|
[
|
|
...utils.varuint32(1),
|
|
0x70, 0, ...utils.varuint32(this.functionsTable.length)
|
|
]
|
|
);
|
|
}
|
|
|
|
_buildElements() {
|
|
if (this.functionsTable.length == 0) return [];
|
|
const entries = [];
|
|
for (let i=0; i<this.functionsTable.length; i++) {
|
|
entries.push(...utils.varuint32(this.functionsTable[i]));
|
|
}
|
|
return this._buildSection(
|
|
0x09,
|
|
[
|
|
...utils.varuint32(1), // 1 entry
|
|
...utils.varuint32(0), // Table (0 in MVP)
|
|
0x41, // offset 0
|
|
...utils.varint32(0),
|
|
0x0b,
|
|
...utils.varuint32(this.functionsTable.length), // Number of elements
|
|
...[...entries]
|
|
]
|
|
);
|
|
}
|
|
|
|
_buildExports() {
|
|
const entries = [];
|
|
for (let i=0; i< this.exports.length; i++) {
|
|
entries.push([
|
|
...utils.string(this.exports[i].exportName),
|
|
0x00,
|
|
...utils.varuint32(this.exports[i].idx)
|
|
]);
|
|
}
|
|
return this._buildSection(
|
|
0x07,
|
|
utils.varuint32(entries.length).concat(...entries)
|
|
);
|
|
}
|
|
|
|
_buildCode() {
|
|
const entries = [];
|
|
for (let i=this.nImportFunctions; i< this.nImportFunctions + this.nInternalFunctions; i++) {
|
|
entries.push(this.functions[i].getBody());
|
|
}
|
|
return this._buildSection(
|
|
0x0a,
|
|
utils.varuint32(entries.length).concat(...entries)
|
|
);
|
|
}
|
|
|
|
_buildData() {
|
|
const entries = [];
|
|
entries.push([
|
|
0x00,
|
|
0x41,
|
|
0x00,
|
|
0x0b,
|
|
0x04,
|
|
...utils.u32(this.free)
|
|
]);
|
|
for (let i=0; i< this.datas.length; i++) {
|
|
entries.push([
|
|
0x00,
|
|
0x41,
|
|
...utils.varint32(this.datas[i].offset),
|
|
0x0b,
|
|
...utils.varuint32(this.datas[i].bytes.length),
|
|
...this.datas[i].bytes,
|
|
]);
|
|
}
|
|
return this._buildSection(
|
|
0x0b,
|
|
utils.varuint32(entries.length).concat(...entries)
|
|
);
|
|
}
|
|
|
|
};
|
|
|
|
var modulebuilder = ModuleBuilder$1;
|
|
|
|
/*
|
|
Copyright 2019 0KIMS association.
|
|
|
|
This file is part of wasmbuilder
|
|
|
|
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.
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
var ModuleBuilder = modulebuilder;
|
|
|
|
globalThis.curve_bn128 = null;
|
|
|
|
async function buildBn128(singleThread, plugins) {
|
|
|
|
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;
|
|
|
|
if ((!singleThread) && (globalThis.curve_bn128)) return globalThis.curve_bn128;
|
|
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) {
|
|
|
|
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.bn128.q;
|
|
bls12381wasm.r = moduleBuilder.modules.bn128.r;
|
|
|
|
|
|
if ((!singleThread) && (globalThis.curve_bls12381)) return globalThis.curve_bls12381;
|
|
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;
|
|
}
|
|
|
|
e("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", 16);
|
|
e("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
|
|
|
e("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", 16);
|
|
e("21888242871839275222246405745257275088696311157297823662689037894645226208583");
|
|
|
|
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;
|
|
|
|
function normalizeName(n) {
|
|
return n.toUpperCase().match(/[A-Za-z0-9]+/g).join("");
|
|
}
|
|
|
|
}
|
|
|
|
const Scalar=_Scalar;
|
|
|
|
const version$2 = "logger/5.7.0";
|
|
|
|
let _permanentCensorErrors = false;
|
|
let _censorErrors = false;
|
|
const LogLevels = { debug: 1, "default": 2, info: 2, warning: 3, error: 4, off: 5 };
|
|
let _logLevel = LogLevels["default"];
|
|
let _globalLogger = null;
|
|
function _checkNormalize() {
|
|
try {
|
|
const missing = [];
|
|
// Make sure all forms of normalization are supported
|
|
["NFD", "NFC", "NFKD", "NFKC"].forEach((form) => {
|
|
try {
|
|
if ("test".normalize(form) !== "test") {
|
|
throw new Error("bad normalize");
|
|
}
|
|
}
|
|
catch (error) {
|
|
missing.push(form);
|
|
}
|
|
});
|
|
if (missing.length) {
|
|
throw new Error("missing " + missing.join(", "));
|
|
}
|
|
if (String.fromCharCode(0xe9).normalize("NFD") !== String.fromCharCode(0x65, 0x0301)) {
|
|
throw new Error("broken implementation");
|
|
}
|
|
}
|
|
catch (error) {
|
|
return error.message;
|
|
}
|
|
return null;
|
|
}
|
|
const _normalizeError = _checkNormalize();
|
|
var LogLevel;
|
|
(function (LogLevel) {
|
|
LogLevel["DEBUG"] = "DEBUG";
|
|
LogLevel["INFO"] = "INFO";
|
|
LogLevel["WARNING"] = "WARNING";
|
|
LogLevel["ERROR"] = "ERROR";
|
|
LogLevel["OFF"] = "OFF";
|
|
})(LogLevel || (LogLevel = {}));
|
|
var ErrorCode;
|
|
(function (ErrorCode) {
|
|
///////////////////
|
|
// Generic Errors
|
|
// Unknown Error
|
|
ErrorCode["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
// Not Implemented
|
|
ErrorCode["NOT_IMPLEMENTED"] = "NOT_IMPLEMENTED";
|
|
// Unsupported Operation
|
|
// - operation
|
|
ErrorCode["UNSUPPORTED_OPERATION"] = "UNSUPPORTED_OPERATION";
|
|
// Network Error (i.e. Ethereum Network, such as an invalid chain ID)
|
|
// - event ("noNetwork" is not re-thrown in provider.ready; otherwise thrown)
|
|
ErrorCode["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
// Some sort of bad response from the server
|
|
ErrorCode["SERVER_ERROR"] = "SERVER_ERROR";
|
|
// Timeout
|
|
ErrorCode["TIMEOUT"] = "TIMEOUT";
|
|
///////////////////
|
|
// Operational Errors
|
|
// Buffer Overrun
|
|
ErrorCode["BUFFER_OVERRUN"] = "BUFFER_OVERRUN";
|
|
// Numeric Fault
|
|
// - operation: the operation being executed
|
|
// - fault: the reason this faulted
|
|
ErrorCode["NUMERIC_FAULT"] = "NUMERIC_FAULT";
|
|
///////////////////
|
|
// Argument Errors
|
|
// Missing new operator to an object
|
|
// - name: The name of the class
|
|
ErrorCode["MISSING_NEW"] = "MISSING_NEW";
|
|
// Invalid argument (e.g. value is incompatible with type) to a function:
|
|
// - argument: The argument name that was invalid
|
|
// - value: The value of the argument
|
|
ErrorCode["INVALID_ARGUMENT"] = "INVALID_ARGUMENT";
|
|
// Missing argument to a function:
|
|
// - count: The number of arguments received
|
|
// - expectedCount: The number of arguments expected
|
|
ErrorCode["MISSING_ARGUMENT"] = "MISSING_ARGUMENT";
|
|
// Too many arguments
|
|
// - count: The number of arguments received
|
|
// - expectedCount: The number of arguments expected
|
|
ErrorCode["UNEXPECTED_ARGUMENT"] = "UNEXPECTED_ARGUMENT";
|
|
///////////////////
|
|
// Blockchain Errors
|
|
// Call exception
|
|
// - transaction: the transaction
|
|
// - address?: the contract address
|
|
// - args?: The arguments passed into the function
|
|
// - method?: The Solidity method signature
|
|
// - errorSignature?: The EIP848 error signature
|
|
// - errorArgs?: The EIP848 error parameters
|
|
// - reason: The reason (only for EIP848 "Error(string)")
|
|
ErrorCode["CALL_EXCEPTION"] = "CALL_EXCEPTION";
|
|
// Insufficient funds (< value + gasLimit * gasPrice)
|
|
// - transaction: the transaction attempted
|
|
ErrorCode["INSUFFICIENT_FUNDS"] = "INSUFFICIENT_FUNDS";
|
|
// Nonce has already been used
|
|
// - transaction: the transaction attempted
|
|
ErrorCode["NONCE_EXPIRED"] = "NONCE_EXPIRED";
|
|
// The replacement fee for the transaction is too low
|
|
// - transaction: the transaction attempted
|
|
ErrorCode["REPLACEMENT_UNDERPRICED"] = "REPLACEMENT_UNDERPRICED";
|
|
// The gas limit could not be estimated
|
|
// - transaction: the transaction passed to estimateGas
|
|
ErrorCode["UNPREDICTABLE_GAS_LIMIT"] = "UNPREDICTABLE_GAS_LIMIT";
|
|
// The transaction was replaced by one with a higher gas price
|
|
// - reason: "cancelled", "replaced" or "repriced"
|
|
// - cancelled: true if reason == "cancelled" or reason == "replaced")
|
|
// - hash: original transaction hash
|
|
// - replacement: the full TransactionsResponse for the replacement
|
|
// - receipt: the receipt of the replacement
|
|
ErrorCode["TRANSACTION_REPLACED"] = "TRANSACTION_REPLACED";
|
|
///////////////////
|
|
// Interaction Errors
|
|
// The user rejected the action, such as signing a message or sending
|
|
// a transaction
|
|
ErrorCode["ACTION_REJECTED"] = "ACTION_REJECTED";
|
|
})(ErrorCode || (ErrorCode = {}));
|
|
const HEX = "0123456789abcdef";
|
|
class Logger {
|
|
constructor(version) {
|
|
Object.defineProperty(this, "version", {
|
|
enumerable: true,
|
|
value: version,
|
|
writable: false
|
|
});
|
|
}
|
|
_log(logLevel, args) {
|
|
const level = logLevel.toLowerCase();
|
|
if (LogLevels[level] == null) {
|
|
this.throwArgumentError("invalid log level name", "logLevel", logLevel);
|
|
}
|
|
if (_logLevel > LogLevels[level]) {
|
|
return;
|
|
}
|
|
console.log.apply(console, args);
|
|
}
|
|
debug(...args) {
|
|
this._log(Logger.levels.DEBUG, args);
|
|
}
|
|
info(...args) {
|
|
this._log(Logger.levels.INFO, args);
|
|
}
|
|
warn(...args) {
|
|
this._log(Logger.levels.WARNING, args);
|
|
}
|
|
makeError(message, code, params) {
|
|
// Errors are being censored
|
|
if (_censorErrors) {
|
|
return this.makeError("censored error", code, {});
|
|
}
|
|
if (!code) {
|
|
code = Logger.errors.UNKNOWN_ERROR;
|
|
}
|
|
if (!params) {
|
|
params = {};
|
|
}
|
|
const messageDetails = [];
|
|
Object.keys(params).forEach((key) => {
|
|
const value = params[key];
|
|
try {
|
|
if (value instanceof Uint8Array) {
|
|
let hex = "";
|
|
for (let i = 0; i < value.length; i++) {
|
|
hex += HEX[value[i] >> 4];
|
|
hex += HEX[value[i] & 0x0f];
|
|
}
|
|
messageDetails.push(key + "=Uint8Array(0x" + hex + ")");
|
|
}
|
|
else {
|
|
messageDetails.push(key + "=" + JSON.stringify(value));
|
|
}
|
|
}
|
|
catch (error) {
|
|
messageDetails.push(key + "=" + JSON.stringify(params[key].toString()));
|
|
}
|
|
});
|
|
messageDetails.push(`code=${code}`);
|
|
messageDetails.push(`version=${this.version}`);
|
|
const reason = message;
|
|
let url = "";
|
|
switch (code) {
|
|
case ErrorCode.NUMERIC_FAULT: {
|
|
url = "NUMERIC_FAULT";
|
|
const fault = message;
|
|
switch (fault) {
|
|
case "overflow":
|
|
case "underflow":
|
|
case "division-by-zero":
|
|
url += "-" + fault;
|
|
break;
|
|
case "negative-power":
|
|
case "negative-width":
|
|
url += "-unsupported";
|
|
break;
|
|
case "unbound-bitwise-result":
|
|
url += "-unbound-result";
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case ErrorCode.CALL_EXCEPTION:
|
|
case ErrorCode.INSUFFICIENT_FUNDS:
|
|
case ErrorCode.MISSING_NEW:
|
|
case ErrorCode.NONCE_EXPIRED:
|
|
case ErrorCode.REPLACEMENT_UNDERPRICED:
|
|
case ErrorCode.TRANSACTION_REPLACED:
|
|
case ErrorCode.UNPREDICTABLE_GAS_LIMIT:
|
|
url = code;
|
|
break;
|
|
}
|
|
if (url) {
|
|
message += " [ See: https:/\/links.ethers.org/v5-errors-" + url + " ]";
|
|
}
|
|
if (messageDetails.length) {
|
|
message += " (" + messageDetails.join(", ") + ")";
|
|
}
|
|
// @TODO: Any??
|
|
const error = new Error(message);
|
|
error.reason = reason;
|
|
error.code = code;
|
|
Object.keys(params).forEach(function (key) {
|
|
error[key] = params[key];
|
|
});
|
|
return error;
|
|
}
|
|
throwError(message, code, params) {
|
|
throw this.makeError(message, code, params);
|
|
}
|
|
throwArgumentError(message, name, value) {
|
|
return this.throwError(message, Logger.errors.INVALID_ARGUMENT, {
|
|
argument: name,
|
|
value: value
|
|
});
|
|
}
|
|
assert(condition, message, code, params) {
|
|
if (!!condition) {
|
|
return;
|
|
}
|
|
this.throwError(message, code, params);
|
|
}
|
|
assertArgument(condition, message, name, value) {
|
|
if (!!condition) {
|
|
return;
|
|
}
|
|
this.throwArgumentError(message, name, value);
|
|
}
|
|
checkNormalize(message) {
|
|
if (_normalizeError) {
|
|
this.throwError("platform missing String.prototype.normalize", Logger.errors.UNSUPPORTED_OPERATION, {
|
|
operation: "String.prototype.normalize", form: _normalizeError
|
|
});
|
|
}
|
|
}
|
|
checkSafeUint53(value, message) {
|
|
if (typeof (value) !== "number") {
|
|
return;
|
|
}
|
|
if (message == null) {
|
|
message = "value not safe";
|
|
}
|
|
if (value < 0 || value >= 0x1fffffffffffff) {
|
|
this.throwError(message, Logger.errors.NUMERIC_FAULT, {
|
|
operation: "checkSafeInteger",
|
|
fault: "out-of-safe-range",
|
|
value: value
|
|
});
|
|
}
|
|
if (value % 1) {
|
|
this.throwError(message, Logger.errors.NUMERIC_FAULT, {
|
|
operation: "checkSafeInteger",
|
|
fault: "non-integer",
|
|
value: value
|
|
});
|
|
}
|
|
}
|
|
checkArgumentCount(count, expectedCount, message) {
|
|
if (message) {
|
|
message = ": " + message;
|
|
}
|
|
else {
|
|
message = "";
|
|
}
|
|
if (count < expectedCount) {
|
|
this.throwError("missing argument" + message, Logger.errors.MISSING_ARGUMENT, {
|
|
count: count,
|
|
expectedCount: expectedCount
|
|
});
|
|
}
|
|
if (count > expectedCount) {
|
|
this.throwError("too many arguments" + message, Logger.errors.UNEXPECTED_ARGUMENT, {
|
|
count: count,
|
|
expectedCount: expectedCount
|
|
});
|
|
}
|
|
}
|
|
checkNew(target, kind) {
|
|
if (target === Object || target == null) {
|
|
this.throwError("missing new", Logger.errors.MISSING_NEW, { name: kind.name });
|
|
}
|
|
}
|
|
checkAbstract(target, kind) {
|
|
if (target === kind) {
|
|
this.throwError("cannot instantiate abstract class " + JSON.stringify(kind.name) + " directly; use a sub-class", Logger.errors.UNSUPPORTED_OPERATION, { name: target.name, operation: "new" });
|
|
}
|
|
else if (target === Object || target == null) {
|
|
this.throwError("missing new", Logger.errors.MISSING_NEW, { name: kind.name });
|
|
}
|
|
}
|
|
static globalLogger() {
|
|
if (!_globalLogger) {
|
|
_globalLogger = new Logger(version$2);
|
|
}
|
|
return _globalLogger;
|
|
}
|
|
static setCensorship(censorship, permanent) {
|
|
if (!censorship && permanent) {
|
|
this.globalLogger().throwError("cannot permanently disable censorship", Logger.errors.UNSUPPORTED_OPERATION, {
|
|
operation: "setCensorship"
|
|
});
|
|
}
|
|
if (_permanentCensorErrors) {
|
|
if (!censorship) {
|
|
return;
|
|
}
|
|
this.globalLogger().throwError("error censorship permanent", Logger.errors.UNSUPPORTED_OPERATION, {
|
|
operation: "setCensorship"
|
|
});
|
|
}
|
|
_censorErrors = !!censorship;
|
|
_permanentCensorErrors = !!permanent;
|
|
}
|
|
static setLogLevel(logLevel) {
|
|
const level = LogLevels[logLevel.toLowerCase()];
|
|
if (level == null) {
|
|
Logger.globalLogger().warn("invalid log level - " + logLevel);
|
|
return;
|
|
}
|
|
_logLevel = level;
|
|
}
|
|
static from(version) {
|
|
return new Logger(version);
|
|
}
|
|
}
|
|
Logger.errors = ErrorCode;
|
|
Logger.levels = LogLevel;
|
|
|
|
const version$1 = "bytes/5.7.0";
|
|
|
|
const logger$1 = new Logger(version$1);
|
|
///////////////////////////////
|
|
function isHexable(value) {
|
|
return !!(value.toHexString);
|
|
}
|
|
function addSlice(array) {
|
|
if (array.slice) {
|
|
return array;
|
|
}
|
|
array.slice = function () {
|
|
const args = Array.prototype.slice.call(arguments);
|
|
return addSlice(new Uint8Array(Array.prototype.slice.apply(array, args)));
|
|
};
|
|
return array;
|
|
}
|
|
function isInteger(value) {
|
|
return (typeof (value) === "number" && value == value && (value % 1) === 0);
|
|
}
|
|
function isBytes(value) {
|
|
if (value == null) {
|
|
return false;
|
|
}
|
|
if (value.constructor === Uint8Array) {
|
|
return true;
|
|
}
|
|
if (typeof (value) === "string") {
|
|
return false;
|
|
}
|
|
if (!isInteger(value.length) || value.length < 0) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < value.length; i++) {
|
|
const v = value[i];
|
|
if (!isInteger(v) || v < 0 || v >= 256) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
function arrayify(value, options) {
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
if (typeof (value) === "number") {
|
|
logger$1.checkSafeUint53(value, "invalid arrayify value");
|
|
const result = [];
|
|
while (value) {
|
|
result.unshift(value & 0xff);
|
|
value = parseInt(String(value / 256));
|
|
}
|
|
if (result.length === 0) {
|
|
result.push(0);
|
|
}
|
|
return addSlice(new Uint8Array(result));
|
|
}
|
|
if (options.allowMissingPrefix && typeof (value) === "string" && value.substring(0, 2) !== "0x") {
|
|
value = "0x" + value;
|
|
}
|
|
if (isHexable(value)) {
|
|
value = value.toHexString();
|
|
}
|
|
if (isHexString(value)) {
|
|
let hex = value.substring(2);
|
|
if (hex.length % 2) {
|
|
if (options.hexPad === "left") {
|
|
hex = "0" + hex;
|
|
}
|
|
else if (options.hexPad === "right") {
|
|
hex += "0";
|
|
}
|
|
else {
|
|
logger$1.throwArgumentError("hex data is odd-length", "value", value);
|
|
}
|
|
}
|
|
const result = [];
|
|
for (let i = 0; i < hex.length; i += 2) {
|
|
result.push(parseInt(hex.substring(i, i + 2), 16));
|
|
}
|
|
return addSlice(new Uint8Array(result));
|
|
}
|
|
if (isBytes(value)) {
|
|
return addSlice(new Uint8Array(value));
|
|
}
|
|
return logger$1.throwArgumentError("invalid arrayify value", "value", value);
|
|
}
|
|
function isHexString(value, length) {
|
|
if (typeof (value) !== "string" || !value.match(/^0x[0-9A-Fa-f]*$/)) {
|
|
return false;
|
|
}
|
|
if (length && value.length !== 2 + 2 * length) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
var sha3$1 = {exports: {}};
|
|
|
|
/**
|
|
* [js-sha3]{@link https://github.com/emn178/js-sha3}
|
|
*
|
|
* @version 0.8.0
|
|
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
|
* @copyright Chen, Yi-Cyuan 2015-2018
|
|
* @license MIT
|
|
*/
|
|
|
|
(function (module) {
|
|
/*jslint bitwise: true */
|
|
(function () {
|
|
|
|
var INPUT_ERROR = 'input is invalid type';
|
|
var FINALIZE_ERROR = 'finalize already called';
|
|
var WINDOW = typeof window === 'object';
|
|
var root = WINDOW ? window : {};
|
|
if (root.JS_SHA3_NO_WINDOW) {
|
|
WINDOW = false;
|
|
}
|
|
var WEB_WORKER = !WINDOW && typeof self === 'object';
|
|
var NODE_JS = !root.JS_SHA3_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
|
|
if (NODE_JS) {
|
|
root = commonjsGlobal;
|
|
} else if (WEB_WORKER) {
|
|
root = self;
|
|
}
|
|
var COMMON_JS = !root.JS_SHA3_NO_COMMON_JS && 'object' === 'object' && module.exports;
|
|
var ARRAY_BUFFER = !root.JS_SHA3_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
|
|
var HEX_CHARS = '0123456789abcdef'.split('');
|
|
var SHAKE_PADDING = [31, 7936, 2031616, 520093696];
|
|
var CSHAKE_PADDING = [4, 1024, 262144, 67108864];
|
|
var KECCAK_PADDING = [1, 256, 65536, 16777216];
|
|
var PADDING = [6, 1536, 393216, 100663296];
|
|
var SHIFT = [0, 8, 16, 24];
|
|
var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
|
|
0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
|
|
2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
|
|
2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
|
|
2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
|
|
var BITS = [224, 256, 384, 512];
|
|
var SHAKE_BITS = [128, 256];
|
|
var OUTPUT_TYPES = ['hex', 'buffer', 'arrayBuffer', 'array', 'digest'];
|
|
var CSHAKE_BYTEPAD = {
|
|
'128': 168,
|
|
'256': 136
|
|
};
|
|
|
|
if (root.JS_SHA3_NO_NODE_JS || !Array.isArray) {
|
|
Array.isArray = function (obj) {
|
|
return Object.prototype.toString.call(obj) === '[object Array]';
|
|
};
|
|
}
|
|
|
|
if (ARRAY_BUFFER && (root.JS_SHA3_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
|
|
ArrayBuffer.isView = function (obj) {
|
|
return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
|
|
};
|
|
}
|
|
|
|
var createOutputMethod = function (bits, padding, outputType) {
|
|
return function (message) {
|
|
return new Keccak(bits, padding, bits).update(message)[outputType]();
|
|
};
|
|
};
|
|
|
|
var createShakeOutputMethod = function (bits, padding, outputType) {
|
|
return function (message, outputBits) {
|
|
return new Keccak(bits, padding, outputBits).update(message)[outputType]();
|
|
};
|
|
};
|
|
|
|
var createCshakeOutputMethod = function (bits, padding, outputType) {
|
|
return function (message, outputBits, n, s) {
|
|
return methods['cshake' + bits].update(message, outputBits, n, s)[outputType]();
|
|
};
|
|
};
|
|
|
|
var createKmacOutputMethod = function (bits, padding, outputType) {
|
|
return function (key, message, outputBits, s) {
|
|
return methods['kmac' + bits].update(key, message, outputBits, s)[outputType]();
|
|
};
|
|
};
|
|
|
|
var createOutputMethods = function (method, createMethod, bits, padding) {
|
|
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
|
|
var type = OUTPUT_TYPES[i];
|
|
method[type] = createMethod(bits, padding, type);
|
|
}
|
|
return method;
|
|
};
|
|
|
|
var createMethod = function (bits, padding) {
|
|
var method = createOutputMethod(bits, padding, 'hex');
|
|
method.create = function () {
|
|
return new Keccak(bits, padding, bits);
|
|
};
|
|
method.update = function (message) {
|
|
return method.create().update(message);
|
|
};
|
|
return createOutputMethods(method, createOutputMethod, bits, padding);
|
|
};
|
|
|
|
var createShakeMethod = function (bits, padding) {
|
|
var method = createShakeOutputMethod(bits, padding, 'hex');
|
|
method.create = function (outputBits) {
|
|
return new Keccak(bits, padding, outputBits);
|
|
};
|
|
method.update = function (message, outputBits) {
|
|
return method.create(outputBits).update(message);
|
|
};
|
|
return createOutputMethods(method, createShakeOutputMethod, bits, padding);
|
|
};
|
|
|
|
var createCshakeMethod = function (bits, padding) {
|
|
var w = CSHAKE_BYTEPAD[bits];
|
|
var method = createCshakeOutputMethod(bits, padding, 'hex');
|
|
method.create = function (outputBits, n, s) {
|
|
if (!n && !s) {
|
|
return methods['shake' + bits].create(outputBits);
|
|
} else {
|
|
return new Keccak(bits, padding, outputBits).bytepad([n, s], w);
|
|
}
|
|
};
|
|
method.update = function (message, outputBits, n, s) {
|
|
return method.create(outputBits, n, s).update(message);
|
|
};
|
|
return createOutputMethods(method, createCshakeOutputMethod, bits, padding);
|
|
};
|
|
|
|
var createKmacMethod = function (bits, padding) {
|
|
var w = CSHAKE_BYTEPAD[bits];
|
|
var method = createKmacOutputMethod(bits, padding, 'hex');
|
|
method.create = function (key, outputBits, s) {
|
|
return new Kmac(bits, padding, outputBits).bytepad(['KMAC', s], w).bytepad([key], w);
|
|
};
|
|
method.update = function (key, message, outputBits, s) {
|
|
return method.create(key, outputBits, s).update(message);
|
|
};
|
|
return createOutputMethods(method, createKmacOutputMethod, bits, padding);
|
|
};
|
|
|
|
var algorithms = [
|
|
{ name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod },
|
|
{ name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod },
|
|
{ name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod },
|
|
{ name: 'cshake', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createCshakeMethod },
|
|
{ name: 'kmac', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createKmacMethod }
|
|
];
|
|
|
|
var methods = {}, methodNames = [];
|
|
|
|
for (var i = 0; i < algorithms.length; ++i) {
|
|
var algorithm = algorithms[i];
|
|
var bits = algorithm.bits;
|
|
for (var j = 0; j < bits.length; ++j) {
|
|
var methodName = algorithm.name + '_' + bits[j];
|
|
methodNames.push(methodName);
|
|
methods[methodName] = algorithm.createMethod(bits[j], algorithm.padding);
|
|
if (algorithm.name !== 'sha3') {
|
|
var newMethodName = algorithm.name + bits[j];
|
|
methodNames.push(newMethodName);
|
|
methods[newMethodName] = methods[methodName];
|
|
}
|
|
}
|
|
}
|
|
|
|
function Keccak(bits, padding, outputBits) {
|
|
this.blocks = [];
|
|
this.s = [];
|
|
this.padding = padding;
|
|
this.outputBits = outputBits;
|
|
this.reset = true;
|
|
this.finalized = false;
|
|
this.block = 0;
|
|
this.start = 0;
|
|
this.blockCount = (1600 - (bits << 1)) >> 5;
|
|
this.byteCount = this.blockCount << 2;
|
|
this.outputBlocks = outputBits >> 5;
|
|
this.extraBytes = (outputBits & 31) >> 3;
|
|
|
|
for (var i = 0; i < 50; ++i) {
|
|
this.s[i] = 0;
|
|
}
|
|
}
|
|
|
|
Keccak.prototype.update = function (message) {
|
|
if (this.finalized) {
|
|
throw new Error(FINALIZE_ERROR);
|
|
}
|
|
var notString, type = typeof message;
|
|
if (type !== 'string') {
|
|
if (type === 'object') {
|
|
if (message === null) {
|
|
throw new Error(INPUT_ERROR);
|
|
} else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
|
|
message = new Uint8Array(message);
|
|
} else if (!Array.isArray(message)) {
|
|
if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
|
|
throw new Error(INPUT_ERROR);
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error(INPUT_ERROR);
|
|
}
|
|
notString = true;
|
|
}
|
|
var blocks = this.blocks, byteCount = this.byteCount, length = message.length,
|
|
blockCount = this.blockCount, index = 0, s = this.s, i, code;
|
|
|
|
while (index < length) {
|
|
if (this.reset) {
|
|
this.reset = false;
|
|
blocks[0] = this.block;
|
|
for (i = 1; i < blockCount + 1; ++i) {
|
|
blocks[i] = 0;
|
|
}
|
|
}
|
|
if (notString) {
|
|
for (i = this.start; index < length && i < byteCount; ++index) {
|
|
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
|
|
}
|
|
} else {
|
|
for (i = this.start; index < length && i < byteCount; ++index) {
|
|
code = message.charCodeAt(index);
|
|
if (code < 0x80) {
|
|
blocks[i >> 2] |= code << SHIFT[i++ & 3];
|
|
} else if (code < 0x800) {
|
|
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
|
} else if (code < 0xd800 || code >= 0xe000) {
|
|
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
|
} else {
|
|
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
|
|
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
|
|
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
|
}
|
|
}
|
|
}
|
|
this.lastByteIndex = i;
|
|
if (i >= byteCount) {
|
|
this.start = i - byteCount;
|
|
this.block = blocks[blockCount];
|
|
for (i = 0; i < blockCount; ++i) {
|
|
s[i] ^= blocks[i];
|
|
}
|
|
f(s);
|
|
this.reset = true;
|
|
} else {
|
|
this.start = i;
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
|
|
Keccak.prototype.encode = function (x, right) {
|
|
var o = x & 255, n = 1;
|
|
var bytes = [o];
|
|
x = x >> 8;
|
|
o = x & 255;
|
|
while (o > 0) {
|
|
bytes.unshift(o);
|
|
x = x >> 8;
|
|
o = x & 255;
|
|
++n;
|
|
}
|
|
if (right) {
|
|
bytes.push(n);
|
|
} else {
|
|
bytes.unshift(n);
|
|
}
|
|
this.update(bytes);
|
|
return bytes.length;
|
|
};
|
|
|
|
Keccak.prototype.encodeString = function (str) {
|
|
var notString, type = typeof str;
|
|
if (type !== 'string') {
|
|
if (type === 'object') {
|
|
if (str === null) {
|
|
throw new Error(INPUT_ERROR);
|
|
} else if (ARRAY_BUFFER && str.constructor === ArrayBuffer) {
|
|
str = new Uint8Array(str);
|
|
} else if (!Array.isArray(str)) {
|
|
if (!ARRAY_BUFFER || !ArrayBuffer.isView(str)) {
|
|
throw new Error(INPUT_ERROR);
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error(INPUT_ERROR);
|
|
}
|
|
notString = true;
|
|
}
|
|
var bytes = 0, length = str.length;
|
|
if (notString) {
|
|
bytes = length;
|
|
} else {
|
|
for (var i = 0; i < str.length; ++i) {
|
|
var code = str.charCodeAt(i);
|
|
if (code < 0x80) {
|
|
bytes += 1;
|
|
} else if (code < 0x800) {
|
|
bytes += 2;
|
|
} else if (code < 0xd800 || code >= 0xe000) {
|
|
bytes += 3;
|
|
} else {
|
|
code = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(++i) & 0x3ff));
|
|
bytes += 4;
|
|
}
|
|
}
|
|
}
|
|
bytes += this.encode(bytes * 8);
|
|
this.update(str);
|
|
return bytes;
|
|
};
|
|
|
|
Keccak.prototype.bytepad = function (strs, w) {
|
|
var bytes = this.encode(w);
|
|
for (var i = 0; i < strs.length; ++i) {
|
|
bytes += this.encodeString(strs[i]);
|
|
}
|
|
var paddingBytes = w - bytes % w;
|
|
var zeros = [];
|
|
zeros.length = paddingBytes;
|
|
this.update(zeros);
|
|
return this;
|
|
};
|
|
|
|
Keccak.prototype.finalize = function () {
|
|
if (this.finalized) {
|
|
return;
|
|
}
|
|
this.finalized = true;
|
|
var blocks = this.blocks, i = this.lastByteIndex, blockCount = this.blockCount, s = this.s;
|
|
blocks[i >> 2] |= this.padding[i & 3];
|
|
if (this.lastByteIndex === this.byteCount) {
|
|
blocks[0] = blocks[blockCount];
|
|
for (i = 1; i < blockCount + 1; ++i) {
|
|
blocks[i] = 0;
|
|
}
|
|
}
|
|
blocks[blockCount - 1] |= 0x80000000;
|
|
for (i = 0; i < blockCount; ++i) {
|
|
s[i] ^= blocks[i];
|
|
}
|
|
f(s);
|
|
};
|
|
|
|
Keccak.prototype.toString = Keccak.prototype.hex = function () {
|
|
this.finalize();
|
|
|
|
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
|
extraBytes = this.extraBytes, i = 0, j = 0;
|
|
var hex = '', block;
|
|
while (j < outputBlocks) {
|
|
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
|
|
block = s[i];
|
|
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F] +
|
|
HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F] +
|
|
HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F] +
|
|
HEX_CHARS[(block >> 28) & 0x0F] + HEX_CHARS[(block >> 24) & 0x0F];
|
|
}
|
|
if (j % blockCount === 0) {
|
|
f(s);
|
|
i = 0;
|
|
}
|
|
}
|
|
if (extraBytes) {
|
|
block = s[i];
|
|
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F];
|
|
if (extraBytes > 1) {
|
|
hex += HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F];
|
|
}
|
|
if (extraBytes > 2) {
|
|
hex += HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F];
|
|
}
|
|
}
|
|
return hex;
|
|
};
|
|
|
|
Keccak.prototype.arrayBuffer = function () {
|
|
this.finalize();
|
|
|
|
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
|
extraBytes = this.extraBytes, i = 0, j = 0;
|
|
var bytes = this.outputBits >> 3;
|
|
var buffer;
|
|
if (extraBytes) {
|
|
buffer = new ArrayBuffer((outputBlocks + 1) << 2);
|
|
} else {
|
|
buffer = new ArrayBuffer(bytes);
|
|
}
|
|
var array = new Uint32Array(buffer);
|
|
while (j < outputBlocks) {
|
|
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
|
|
array[j] = s[i];
|
|
}
|
|
if (j % blockCount === 0) {
|
|
f(s);
|
|
}
|
|
}
|
|
if (extraBytes) {
|
|
array[i] = s[i];
|
|
buffer = buffer.slice(0, bytes);
|
|
}
|
|
return buffer;
|
|
};
|
|
|
|
Keccak.prototype.buffer = Keccak.prototype.arrayBuffer;
|
|
|
|
Keccak.prototype.digest = Keccak.prototype.array = function () {
|
|
this.finalize();
|
|
|
|
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
|
extraBytes = this.extraBytes, i = 0, j = 0;
|
|
var array = [], offset, block;
|
|
while (j < outputBlocks) {
|
|
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
|
|
offset = j << 2;
|
|
block = s[i];
|
|
array[offset] = block & 0xFF;
|
|
array[offset + 1] = (block >> 8) & 0xFF;
|
|
array[offset + 2] = (block >> 16) & 0xFF;
|
|
array[offset + 3] = (block >> 24) & 0xFF;
|
|
}
|
|
if (j % blockCount === 0) {
|
|
f(s);
|
|
}
|
|
}
|
|
if (extraBytes) {
|
|
offset = j << 2;
|
|
block = s[i];
|
|
array[offset] = block & 0xFF;
|
|
if (extraBytes > 1) {
|
|
array[offset + 1] = (block >> 8) & 0xFF;
|
|
}
|
|
if (extraBytes > 2) {
|
|
array[offset + 2] = (block >> 16) & 0xFF;
|
|
}
|
|
}
|
|
return array;
|
|
};
|
|
|
|
function Kmac(bits, padding, outputBits) {
|
|
Keccak.call(this, bits, padding, outputBits);
|
|
}
|
|
|
|
Kmac.prototype = new Keccak();
|
|
|
|
Kmac.prototype.finalize = function () {
|
|
this.encode(this.outputBits, true);
|
|
return Keccak.prototype.finalize.call(this);
|
|
};
|
|
|
|
var f = function (s) {
|
|
var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
|
|
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
|
|
b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
|
|
b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
|
|
for (n = 0; n < 48; n += 2) {
|
|
c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
|
|
c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
|
|
c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
|
|
c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
|
|
c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
|
|
c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
|
|
c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
|
|
c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
|
|
c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
|
|
c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
|
|
|
|
h = c8 ^ ((c2 << 1) | (c3 >>> 31));
|
|
l = c9 ^ ((c3 << 1) | (c2 >>> 31));
|
|
s[0] ^= h;
|
|
s[1] ^= l;
|
|
s[10] ^= h;
|
|
s[11] ^= l;
|
|
s[20] ^= h;
|
|
s[21] ^= l;
|
|
s[30] ^= h;
|
|
s[31] ^= l;
|
|
s[40] ^= h;
|
|
s[41] ^= l;
|
|
h = c0 ^ ((c4 << 1) | (c5 >>> 31));
|
|
l = c1 ^ ((c5 << 1) | (c4 >>> 31));
|
|
s[2] ^= h;
|
|
s[3] ^= l;
|
|
s[12] ^= h;
|
|
s[13] ^= l;
|
|
s[22] ^= h;
|
|
s[23] ^= l;
|
|
s[32] ^= h;
|
|
s[33] ^= l;
|
|
s[42] ^= h;
|
|
s[43] ^= l;
|
|
h = c2 ^ ((c6 << 1) | (c7 >>> 31));
|
|
l = c3 ^ ((c7 << 1) | (c6 >>> 31));
|
|
s[4] ^= h;
|
|
s[5] ^= l;
|
|
s[14] ^= h;
|
|
s[15] ^= l;
|
|
s[24] ^= h;
|
|
s[25] ^= l;
|
|
s[34] ^= h;
|
|
s[35] ^= l;
|
|
s[44] ^= h;
|
|
s[45] ^= l;
|
|
h = c4 ^ ((c8 << 1) | (c9 >>> 31));
|
|
l = c5 ^ ((c9 << 1) | (c8 >>> 31));
|
|
s[6] ^= h;
|
|
s[7] ^= l;
|
|
s[16] ^= h;
|
|
s[17] ^= l;
|
|
s[26] ^= h;
|
|
s[27] ^= l;
|
|
s[36] ^= h;
|
|
s[37] ^= l;
|
|
s[46] ^= h;
|
|
s[47] ^= l;
|
|
h = c6 ^ ((c0 << 1) | (c1 >>> 31));
|
|
l = c7 ^ ((c1 << 1) | (c0 >>> 31));
|
|
s[8] ^= h;
|
|
s[9] ^= l;
|
|
s[18] ^= h;
|
|
s[19] ^= l;
|
|
s[28] ^= h;
|
|
s[29] ^= l;
|
|
s[38] ^= h;
|
|
s[39] ^= l;
|
|
s[48] ^= h;
|
|
s[49] ^= l;
|
|
|
|
b0 = s[0];
|
|
b1 = s[1];
|
|
b32 = (s[11] << 4) | (s[10] >>> 28);
|
|
b33 = (s[10] << 4) | (s[11] >>> 28);
|
|
b14 = (s[20] << 3) | (s[21] >>> 29);
|
|
b15 = (s[21] << 3) | (s[20] >>> 29);
|
|
b46 = (s[31] << 9) | (s[30] >>> 23);
|
|
b47 = (s[30] << 9) | (s[31] >>> 23);
|
|
b28 = (s[40] << 18) | (s[41] >>> 14);
|
|
b29 = (s[41] << 18) | (s[40] >>> 14);
|
|
b20 = (s[2] << 1) | (s[3] >>> 31);
|
|
b21 = (s[3] << 1) | (s[2] >>> 31);
|
|
b2 = (s[13] << 12) | (s[12] >>> 20);
|
|
b3 = (s[12] << 12) | (s[13] >>> 20);
|
|
b34 = (s[22] << 10) | (s[23] >>> 22);
|
|
b35 = (s[23] << 10) | (s[22] >>> 22);
|
|
b16 = (s[33] << 13) | (s[32] >>> 19);
|
|
b17 = (s[32] << 13) | (s[33] >>> 19);
|
|
b48 = (s[42] << 2) | (s[43] >>> 30);
|
|
b49 = (s[43] << 2) | (s[42] >>> 30);
|
|
b40 = (s[5] << 30) | (s[4] >>> 2);
|
|
b41 = (s[4] << 30) | (s[5] >>> 2);
|
|
b22 = (s[14] << 6) | (s[15] >>> 26);
|
|
b23 = (s[15] << 6) | (s[14] >>> 26);
|
|
b4 = (s[25] << 11) | (s[24] >>> 21);
|
|
b5 = (s[24] << 11) | (s[25] >>> 21);
|
|
b36 = (s[34] << 15) | (s[35] >>> 17);
|
|
b37 = (s[35] << 15) | (s[34] >>> 17);
|
|
b18 = (s[45] << 29) | (s[44] >>> 3);
|
|
b19 = (s[44] << 29) | (s[45] >>> 3);
|
|
b10 = (s[6] << 28) | (s[7] >>> 4);
|
|
b11 = (s[7] << 28) | (s[6] >>> 4);
|
|
b42 = (s[17] << 23) | (s[16] >>> 9);
|
|
b43 = (s[16] << 23) | (s[17] >>> 9);
|
|
b24 = (s[26] << 25) | (s[27] >>> 7);
|
|
b25 = (s[27] << 25) | (s[26] >>> 7);
|
|
b6 = (s[36] << 21) | (s[37] >>> 11);
|
|
b7 = (s[37] << 21) | (s[36] >>> 11);
|
|
b38 = (s[47] << 24) | (s[46] >>> 8);
|
|
b39 = (s[46] << 24) | (s[47] >>> 8);
|
|
b30 = (s[8] << 27) | (s[9] >>> 5);
|
|
b31 = (s[9] << 27) | (s[8] >>> 5);
|
|
b12 = (s[18] << 20) | (s[19] >>> 12);
|
|
b13 = (s[19] << 20) | (s[18] >>> 12);
|
|
b44 = (s[29] << 7) | (s[28] >>> 25);
|
|
b45 = (s[28] << 7) | (s[29] >>> 25);
|
|
b26 = (s[38] << 8) | (s[39] >>> 24);
|
|
b27 = (s[39] << 8) | (s[38] >>> 24);
|
|
b8 = (s[48] << 14) | (s[49] >>> 18);
|
|
b9 = (s[49] << 14) | (s[48] >>> 18);
|
|
|
|
s[0] = b0 ^ (~b2 & b4);
|
|
s[1] = b1 ^ (~b3 & b5);
|
|
s[10] = b10 ^ (~b12 & b14);
|
|
s[11] = b11 ^ (~b13 & b15);
|
|
s[20] = b20 ^ (~b22 & b24);
|
|
s[21] = b21 ^ (~b23 & b25);
|
|
s[30] = b30 ^ (~b32 & b34);
|
|
s[31] = b31 ^ (~b33 & b35);
|
|
s[40] = b40 ^ (~b42 & b44);
|
|
s[41] = b41 ^ (~b43 & b45);
|
|
s[2] = b2 ^ (~b4 & b6);
|
|
s[3] = b3 ^ (~b5 & b7);
|
|
s[12] = b12 ^ (~b14 & b16);
|
|
s[13] = b13 ^ (~b15 & b17);
|
|
s[22] = b22 ^ (~b24 & b26);
|
|
s[23] = b23 ^ (~b25 & b27);
|
|
s[32] = b32 ^ (~b34 & b36);
|
|
s[33] = b33 ^ (~b35 & b37);
|
|
s[42] = b42 ^ (~b44 & b46);
|
|
s[43] = b43 ^ (~b45 & b47);
|
|
s[4] = b4 ^ (~b6 & b8);
|
|
s[5] = b5 ^ (~b7 & b9);
|
|
s[14] = b14 ^ (~b16 & b18);
|
|
s[15] = b15 ^ (~b17 & b19);
|
|
s[24] = b24 ^ (~b26 & b28);
|
|
s[25] = b25 ^ (~b27 & b29);
|
|
s[34] = b34 ^ (~b36 & b38);
|
|
s[35] = b35 ^ (~b37 & b39);
|
|
s[44] = b44 ^ (~b46 & b48);
|
|
s[45] = b45 ^ (~b47 & b49);
|
|
s[6] = b6 ^ (~b8 & b0);
|
|
s[7] = b7 ^ (~b9 & b1);
|
|
s[16] = b16 ^ (~b18 & b10);
|
|
s[17] = b17 ^ (~b19 & b11);
|
|
s[26] = b26 ^ (~b28 & b20);
|
|
s[27] = b27 ^ (~b29 & b21);
|
|
s[36] = b36 ^ (~b38 & b30);
|
|
s[37] = b37 ^ (~b39 & b31);
|
|
s[46] = b46 ^ (~b48 & b40);
|
|
s[47] = b47 ^ (~b49 & b41);
|
|
s[8] = b8 ^ (~b0 & b2);
|
|
s[9] = b9 ^ (~b1 & b3);
|
|
s[18] = b18 ^ (~b10 & b12);
|
|
s[19] = b19 ^ (~b11 & b13);
|
|
s[28] = b28 ^ (~b20 & b22);
|
|
s[29] = b29 ^ (~b21 & b23);
|
|
s[38] = b38 ^ (~b30 & b32);
|
|
s[39] = b39 ^ (~b31 & b33);
|
|
s[48] = b48 ^ (~b40 & b42);
|
|
s[49] = b49 ^ (~b41 & b43);
|
|
|
|
s[0] ^= RC[n];
|
|
s[1] ^= RC[n + 1];
|
|
}
|
|
};
|
|
|
|
if (COMMON_JS) {
|
|
module.exports = methods;
|
|
} else {
|
|
for (i = 0; i < methodNames.length; ++i) {
|
|
root[methodNames[i]] = methods[methodNames[i]];
|
|
}
|
|
}
|
|
})();
|
|
} (sha3$1));
|
|
|
|
var sha3Exports = sha3$1.exports;
|
|
var sha3 = /*@__PURE__*/getDefaultExportFromCjs(sha3Exports);
|
|
|
|
function keccak256(data) {
|
|
return '0x' + sha3.keccak_256(arrayify(data));
|
|
}
|
|
|
|
const version = "strings/5.7.0";
|
|
|
|
const logger = new Logger(version);
|
|
///////////////////////////////
|
|
var UnicodeNormalizationForm;
|
|
(function (UnicodeNormalizationForm) {
|
|
UnicodeNormalizationForm["current"] = "";
|
|
UnicodeNormalizationForm["NFC"] = "NFC";
|
|
UnicodeNormalizationForm["NFD"] = "NFD";
|
|
UnicodeNormalizationForm["NFKC"] = "NFKC";
|
|
UnicodeNormalizationForm["NFKD"] = "NFKD";
|
|
})(UnicodeNormalizationForm || (UnicodeNormalizationForm = {}));
|
|
var Utf8ErrorReason;
|
|
(function (Utf8ErrorReason) {
|
|
// A continuation byte was present where there was nothing to continue
|
|
// - offset = the index the codepoint began in
|
|
Utf8ErrorReason["UNEXPECTED_CONTINUE"] = "unexpected continuation byte";
|
|
// An invalid (non-continuation) byte to start a UTF-8 codepoint was found
|
|
// - offset = the index the codepoint began in
|
|
Utf8ErrorReason["BAD_PREFIX"] = "bad codepoint prefix";
|
|
// The string is too short to process the expected codepoint
|
|
// - offset = the index the codepoint began in
|
|
Utf8ErrorReason["OVERRUN"] = "string overrun";
|
|
// A missing continuation byte was expected but not found
|
|
// - offset = the index the continuation byte was expected at
|
|
Utf8ErrorReason["MISSING_CONTINUE"] = "missing continuation byte";
|
|
// The computed code point is outside the range for UTF-8
|
|
// - offset = start of this codepoint
|
|
// - badCodepoint = the computed codepoint; outside the UTF-8 range
|
|
Utf8ErrorReason["OUT_OF_RANGE"] = "out of UTF-8 range";
|
|
// UTF-8 strings may not contain UTF-16 surrogate pairs
|
|
// - offset = start of this codepoint
|
|
// - badCodepoint = the computed codepoint; inside the UTF-16 surrogate range
|
|
Utf8ErrorReason["UTF16_SURROGATE"] = "UTF-16 surrogate";
|
|
// The string is an overlong representation
|
|
// - offset = start of this codepoint
|
|
// - badCodepoint = the computed codepoint; already bounds checked
|
|
Utf8ErrorReason["OVERLONG"] = "overlong representation";
|
|
})(Utf8ErrorReason || (Utf8ErrorReason = {}));
|
|
// http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array
|
|
function toUtf8Bytes(str, form = UnicodeNormalizationForm.current) {
|
|
if (form != UnicodeNormalizationForm.current) {
|
|
logger.checkNormalize();
|
|
str = str.normalize(form);
|
|
}
|
|
let result = [];
|
|
for (let i = 0; i < str.length; i++) {
|
|
const c = str.charCodeAt(i);
|
|
if (c < 0x80) {
|
|
result.push(c);
|
|
}
|
|
else if (c < 0x800) {
|
|
result.push((c >> 6) | 0xc0);
|
|
result.push((c & 0x3f) | 0x80);
|
|
}
|
|
else if ((c & 0xfc00) == 0xd800) {
|
|
i++;
|
|
const c2 = str.charCodeAt(i);
|
|
if (i >= str.length || (c2 & 0xfc00) !== 0xdc00) {
|
|
throw new Error("invalid utf-8 string");
|
|
}
|
|
// Surrogate Pair
|
|
const pair = 0x10000 + ((c & 0x03ff) << 10) + (c2 & 0x03ff);
|
|
result.push((pair >> 18) | 0xf0);
|
|
result.push(((pair >> 12) & 0x3f) | 0x80);
|
|
result.push(((pair >> 6) & 0x3f) | 0x80);
|
|
result.push((pair & 0x3f) | 0x80);
|
|
}
|
|
else {
|
|
result.push((c >> 12) | 0xe0);
|
|
result.push(((c >> 6) & 0x3f) | 0x80);
|
|
result.push((c & 0x3f) | 0x80);
|
|
}
|
|
}
|
|
return arrayify(result);
|
|
}
|
|
|
|
const SEED = "mimcsponge";
|
|
const NROUNDS = 220;
|
|
|
|
async function buildMimcSponge() {
|
|
const bn128 = await getCurveFromName("bn128", true);
|
|
return new MimcSponge(bn128.Fr);
|
|
}
|
|
|
|
class MimcSponge {
|
|
constructor (F) {
|
|
this.F = F;
|
|
this.cts = this.getConstants(SEED, NROUNDS);
|
|
}
|
|
|
|
getIV (seed) {
|
|
const F = this.F;
|
|
if (typeof seed === "undefined") seed = SEED;
|
|
const c = keccak256(toUtf8Bytes(seed+"_iv"));
|
|
const cn = Scalar.e(c);
|
|
const iv = cn.mod(F.p);
|
|
return iv;
|
|
};
|
|
|
|
getConstants (seed, nRounds) {
|
|
const F = this.F;
|
|
if (typeof nRounds === "undefined") nRounds = NROUNDS;
|
|
const cts = new Array(nRounds);
|
|
let c = keccak256(toUtf8Bytes(SEED)); for (let i=1; i<nRounds; i++) {
|
|
c = keccak256(c);
|
|
|
|
cts[i] = F.e(c);
|
|
}
|
|
cts[0] = F.e(0);
|
|
cts[cts.length - 1] = F.e(0);
|
|
return cts;
|
|
};
|
|
|
|
|
|
hash(_xL_in, _xR_in, _k) {
|
|
const F = this.F;
|
|
let xL = F.e(_xL_in);
|
|
let xR = F.e(_xR_in);
|
|
const k = F.e(_k);
|
|
for (let i=0; i<NROUNDS; i++) {
|
|
const c = this.cts[i];
|
|
const t = (i==0) ? F.add(xL, k) : F.add(F.add(xL, k), c);
|
|
const t2 = F.square(t);
|
|
const t4 = F.square(t2);
|
|
const t5 = F.mul(t4, t);
|
|
const xR_tmp = F.e(xR);
|
|
if (i < (NROUNDS - 1)) {
|
|
xR = xL;
|
|
xL = F.add(xR_tmp, t5);
|
|
} else {
|
|
xR = F.add(xR_tmp, t5);
|
|
}
|
|
}
|
|
return {
|
|
xL: xL,
|
|
xR: xR
|
|
};
|
|
}
|
|
|
|
multiHash(arr, key, numOutputs) {
|
|
const F = this.F;
|
|
if (typeof(numOutputs) === "undefined") {
|
|
numOutputs = 1;
|
|
}
|
|
if (typeof(key) === "undefined") {
|
|
key = F.zero;
|
|
}
|
|
|
|
let R = F.zero;
|
|
let C = F.zero;
|
|
|
|
for (let i=0; i<arr.length; i++) {
|
|
R = F.add(R, F.e(arr[i]));
|
|
const S = this.hash(R, C, key);
|
|
R = S.xL;
|
|
C = S.xR;
|
|
}
|
|
let outputs = [R];
|
|
for (let i=1; i < numOutputs; i++) {
|
|
const S = this.hash(R, C, key);
|
|
R = S.xL;
|
|
C = S.xR;
|
|
outputs.push(R);
|
|
}
|
|
if (numOutputs == 1) {
|
|
return outputs[0];
|
|
} else {
|
|
return outputs;
|
|
}
|
|
}
|
|
}
|
|
|
|
var __async$1 = (__this, __arguments, generator) => {
|
|
return new Promise((resolve, reject) => {
|
|
var fulfilled = (value) => {
|
|
try {
|
|
step(generator.next(value));
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
};
|
|
var rejected = (value) => {
|
|
try {
|
|
step(generator.throw(value));
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
};
|
|
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
step((generator = generator.apply(__this, __arguments)).next());
|
|
});
|
|
};
|
|
class Mimc {
|
|
constructor() {
|
|
this.mimcPromise = this.initMimc();
|
|
}
|
|
initMimc() {
|
|
return __async$1(this, null, function* () {
|
|
this.sponge = yield buildMimcSponge();
|
|
this.hash = (left, right) => {
|
|
var _a, _b;
|
|
return (_b = this.sponge) == null ? void 0 : _b.F.toString((_a = this.sponge) == null ? void 0 : _a.multiHash([BigInt(left), BigInt(right)]));
|
|
};
|
|
});
|
|
}
|
|
getHash() {
|
|
return __async$1(this, null, function* () {
|
|
yield this.mimcPromise;
|
|
return {
|
|
sponge: this.sponge,
|
|
hash: this.hash
|
|
};
|
|
});
|
|
}
|
|
}
|
|
const mimc = new Mimc();
|
|
|
|
BigInt.prototype.toJSON = function() {
|
|
return this.toString();
|
|
};
|
|
const isNode = !process.browser && typeof globalThis.window === "undefined";
|
|
|
|
var __async = (__this, __arguments, generator) => {
|
|
return new Promise((resolve, reject) => {
|
|
var fulfilled = (value) => {
|
|
try {
|
|
step(generator.next(value));
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
};
|
|
var rejected = (value) => {
|
|
try {
|
|
step(generator.throw(value));
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
};
|
|
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
step((generator = generator.apply(__this, __arguments)).next());
|
|
});
|
|
};
|
|
function nodePostWork() {
|
|
return __async(this, null, function* () {
|
|
const { hash: hashFunction } = yield mimc.getHash();
|
|
const { merkleTreeHeight, edge, elements, zeroElement } = threads.workerData;
|
|
if (edge) {
|
|
const merkleTree2 = new lib.PartialMerkleTree(merkleTreeHeight, edge, elements, {
|
|
zeroElement,
|
|
hashFunction
|
|
});
|
|
threads.parentPort.postMessage(merkleTree2.toString());
|
|
return;
|
|
}
|
|
const merkleTree = new lib.MerkleTree(merkleTreeHeight, elements, {
|
|
zeroElement,
|
|
hashFunction
|
|
});
|
|
threads.parentPort.postMessage(merkleTree.toString());
|
|
});
|
|
}
|
|
if (isNode && threads) {
|
|
nodePostWork();
|
|
} else if (!isNode && typeof addEventListener === "function" && typeof postMessage === "function") {
|
|
addEventListener("message", (e) => __async(undefined, null, function* () {
|
|
let data;
|
|
if (e.data) {
|
|
data = e.data;
|
|
} else {
|
|
data = e;
|
|
}
|
|
const { hash: hashFunction } = yield mimc.getHash();
|
|
const { merkleTreeHeight, edge, elements, zeroElement } = data;
|
|
if (edge) {
|
|
const merkleTree2 = new lib.PartialMerkleTree(merkleTreeHeight, edge, elements, {
|
|
zeroElement,
|
|
hashFunction
|
|
});
|
|
postMessage(merkleTree2.toString());
|
|
return;
|
|
}
|
|
const merkleTree = new lib.MerkleTree(merkleTreeHeight, elements, {
|
|
zeroElement,
|
|
hashFunction
|
|
});
|
|
postMessage(merkleTree.toString());
|
|
}));
|
|
} else {
|
|
throw new Error("This browser / environment does not support workers!");
|
|
}
|