265 lines
8.3 KiB
JavaScript
265 lines
8.3 KiB
JavaScript
/*
|
|
Copyright 2019 0KIMS association.
|
|
|
|
This file is part of websnark (Web Assembly zkSnark Prover).
|
|
|
|
websnark 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.
|
|
|
|
websnark 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 websnark. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
const bigInt = require("big-integer");
|
|
const buildInt = require("./build_int.js");
|
|
const utils = require("./utils.js");
|
|
|
|
module.exports = function buildF1m(module, _q, _prefix, _intPrefix) {
|
|
const q = bigInt(_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.bigInt2BytesLE(q, n8));
|
|
|
|
const pR2 = module.alloc(utils.bigInt2BytesLE(bigInt.one.shiftLeft(n64*64).square().mod(q), n8));
|
|
const pOne = module.alloc(utils.bigInt2BytesLE(bigInt.one.shiftLeft(n64*64).mod(q), n8));
|
|
const pZero = module.alloc(utils.bigInt2BytesLE(bigInt.zero, n8));
|
|
module.modules[prefix] = {
|
|
pq: pq,
|
|
pR2: pR2,
|
|
n64: n64,
|
|
q: q,
|
|
pOne: pOne,
|
|
pZero: pZero
|
|
};
|
|
|
|
function buildOne() {
|
|
const f = module.addFunction(prefix+"_one");
|
|
f.addParam("pr", "i32");
|
|
|
|
const c = f.getCodeBuilder();
|
|
|
|
f.addCode(c.call(prefix + "_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.if(
|
|
c.i32_eqz( c.call(intPrefix + "_isZero", c.getLocal("x"))),
|
|
c.drop(c.call(intPrefix+"_sub", c.i32_const(pq), c.getLocal("x"), c.getLocal("r"))),
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
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("100000000",16).minus( q.modInv(bigInt("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 pAux2 = module.alloc(n8*2);
|
|
|
|
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(intPrefix + "_mul", c.getLocal("x"), c.getLocal("y"), c.i32_const(pAux2) ));
|
|
f.addCode(c.call(prefix + "_mReduct", c.i32_const(pAux2), 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")));
|
|
}
|
|
|
|
buildAdd();
|
|
buildSub();
|
|
buildNeg();
|
|
buildMReduct();
|
|
buildMul();
|
|
buildToMontgomery();
|
|
buildFromMontgomery();
|
|
buildInverse();
|
|
module.exportFunction(prefix + "_add");
|
|
module.exportFunction(prefix + "_sub");
|
|
module.exportFunction(prefix + "_neg");
|
|
module.exportFunction(prefix + "_mReduct");
|
|
module.exportFunction(prefix + "_mul");
|
|
module.exportFunction(prefix + "_fromMontgomery");
|
|
module.exportFunction(prefix + "_toMontgomery");
|
|
module.exportFunction(prefix + "_inverse");
|
|
module.exportFunction(intPrefix + "_copy", prefix+"_copy");
|
|
module.exportFunction(intPrefix + "_zero", prefix+"_zero");
|
|
module.exportFunction(intPrefix + "_isZero", prefix+"_isZero");
|
|
module.exportFunction(intPrefix + "_eq", prefix+"_eq");
|
|
buildOne();
|
|
module.exportFunction(prefix + "_one");
|
|
|
|
return prefix;
|
|
};
|