Use native big int

This commit is contained in:
Jordi Baylina
2020-04-18 20:53:13 +02:00
parent f25842f67c
commit 767ca60008
18 changed files with 276 additions and 518 deletions

View File

@@ -1,7 +1,8 @@
const streamFromMultiArray = require("../../src/streamfromarray_txt.js");
const bigInt = require("big-integer");
const utils = require("../../src/utils");
const assert = require("assert");
const Scalar = require("ffjavascript").Scalar;
const F1Field = require("ffjavascript").F1Field;
function ref2src(c) {
if ((c[0] == "R")||(c[0] == "RI")) {
@@ -340,7 +341,9 @@ class FunctionBuilderC {
}
class BuilderC {
constructor() {
constructor(p) {
this.F = new F1Field(p);
this.hashMaps={};
this.componentEntriesTables={};
this.sizes ={};
@@ -348,7 +351,6 @@ class BuilderC {
this.functions = [];
this.components = [];
this.usedConstants = {};
}
setHeader(header) {
@@ -369,9 +371,9 @@ class BuilderC {
}
addConstant(c) {
c = bigInt(c);
c = this.F.e(c);
const cS = c.toString();
if (this.usedConstants[cS]) return this.usedConstants[cS];
if (typeof this.usedConstants[cS] != "undefined") return this.usedConstants[cS];
this.constants.push(c);
this.usedConstants[cS] = this.constants.length - 1;
return this.constants.length - 1;
@@ -471,8 +473,6 @@ class BuilderC {
_buildConstants(code) {
const self = this;
const n64 = Math.floor((self.header.P.bitLength() - 1) / 64)+1;
const R = bigInt.one.shiftLeft(n64*64);
code.push("// Constants");
code.push(`FrElement _constants[${self.constants.length}] = {`);
@@ -482,12 +482,19 @@ class BuilderC {
code.push("};");
function number2Code(n) {
if (n.lt(bigInt("80000000", 16)) ) {
return addShortMontgomeryPositive(n);
}
if (n.geq(self.header.P.minus(bigInt("80000000", 16))) ) {
return addShortMontgomeryNegative(n);
const minShort = self.F.neg(self.F.e("80000000"));
const maxShort = self.F.e("7FFFFFFF", 16);
if ( (self.F.geq(n, minShort))
&&(self.F.leq(n, maxShort)))
{
if (self.F.geq(n, self.F.zero)) {
return addShortMontgomeryPositive(n);
} else {
return addShortMontgomeryNegative(n);
}
}
return addLongMontgomery(n);
@@ -506,25 +513,31 @@ class BuilderC {
}
function getLongString(a) {
let r = bigInt(a);
let S = "";
let i = 0;
while (!r.isZero()) {
if (S!= "") S = S+",";
S += "0x" + r.and(bigInt("FFFFFFFFFFFFFFFF", 16)).toString(16) + "LL";
i++;
r = r.shiftRight(64);
}
while (i<n64) {
if (S!= "") S = S+",";
S += "0LL";
i++;
const arr = Scalar.toArray(a, 0x100000000);
for (let i=0; i<self.F.n64*2; i+=2) {
const idx = arr.length-2-i;
if (i>0) S = S + ",";
if ( idx >=0) {
let msb = arr[idx].toString(16);
while (msb.length<8) msb = "0" + msb;
let lsb = arr[idx+1].toString(16);
while (lsb.length<8) lsb = "0" + lsb;
S += "0x" + msb + lsb + "LL";
} else {
S += "0LL";
}
}
return S;
}
function toMontgomery(a) {
return a.times(R).mod(self.header.P);
return self.F.mul(a, self.F.R);
}
}

View File

@@ -8,12 +8,14 @@ const compiler = require("../../src/compiler");
const util = require("util");
const exec = util.promisify(require("child_process").exec);
const bigInt = require("big-integer");
const Scalar = require("ffjavascript").Scalar;
const utils = require("../../src/utils");
const loadR1cs = require("r1csfile").load;
const ZqField = require("ffjavascript").ZqField;
const buildZqField = require("ffiasm").buildZqField;
const {stringifyBigInts, unstringifyBigInts } = require("ffjavascript").utils;
module.exports = c_tester;
@@ -31,7 +33,7 @@ async function c_tester(circomFile, _options) {
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
options.r1csFileName = path.join(dir.path, baseName + ".r1cs");
options.p = options.p || bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
options.p = options.p || Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
await compiler(circomFile, options);
const source = await buildZqField(options.p, "Fr");
@@ -87,7 +89,7 @@ class CTester {
async calculateWitness(input) {
await fs.promises.writeFile(
path.join(this.dir.path, "in.json"),
JSON.stringify(utils.stringifyBigInts(input), null, 1)
JSON.stringify(stringifyBigInts(input), null, 1)
);
const r = await exec(`${path.join(this.dir.path, this.baseName)}` +
` ${path.join(this.dir.path, "in.json")}` +
@@ -100,7 +102,7 @@ class CTester {
path.join(this.dir.path, "out.json")
);
const res = utils.unstringifyBigInts(JSON.parse(resStr));
const res = unstringifyBigInts(JSON.parse(resStr));
return res;
}
@@ -127,7 +129,7 @@ class CTester {
const self = this;
if (this.constraints) return;
const r1cs = await loadR1cs(path.join(this.dir.path, this.baseName + ".r1cs"),true, false);
self.field = new ZqField(r1cs.prime);
self.F = new ZqField(r1cs.prime);
self.nVars = r1cs.nVars;
self.constraints = r1cs.constraints;
}
@@ -152,8 +154,8 @@ class CTester {
if (typeof self.symbols[prefix] == "undefined") {
assert(false, "Output variable not defined: "+ prefix);
}
const ba = bigInt(actualOut[self.symbols[prefix].varIdx]).toString();
const be = bigInt(eOut).toString();
const ba = actualOut[self.symbols[prefix].varIdx].toString();
const be = eOut.toString();
assert.strictEqual(ba, be, prefix);
}
}
@@ -183,7 +185,7 @@ class CTester {
}
function checkConstraint(constraint) {
const F = self.field;
const F = self.F;
const a = evalLC(constraint[0]);
const b = evalLC(constraint[1]);
const c = evalLC(constraint[2]);
@@ -192,7 +194,7 @@ class CTester {
}
function evalLC(lc) {
const F = self.field;
const F = self.F;
let v = F.zero;
for (let w in lc) {
v = F.add(

View File

@@ -1,10 +1,11 @@
const streamFromArrayTxt = require("../../src/streamfromarray_txt");
const streamFromArrayBin = require("../../src/streamfromarray_bin");
const bigInt = require("big-integer");
const assert = require("assert");
const ModuleBuilder = require("wasmbuilder").ModuleBuilder;
const ModuleBuilderWat = require("wasmbuilder").ModuleBuilderWat;
const buildRuntime = require("./build_runtime");
const Scalar = require("ffjavascript").Scalar;
const F1Field = require("ffjavascript").F1Field;
const errs = require("./errs");
@@ -689,7 +690,8 @@ class FunctionBuilderWasm {
}
class BuilderWasm {
constructor() {
constructor(p) {
this.F = new F1Field(p);
this.hashMaps={};
this.componentEntriesTables={};
this.sizes ={};
@@ -701,8 +703,8 @@ class BuilderWasm {
this.TYPE_SIGNAL = 1;
this.TYPE_COMPONENT = 2;
this.addConstant(bigInt(0)); // constants[0] = 0;
this.addConstant(bigInt(1)); // constants[1] = 1;
this.addConstant(Scalar.fromString("0")); // constants[0] = 0;
this.addConstant(Scalar.fromString("1")); // constants[1] = 1;
this.offsetComponentNInputSignals = 12;
this.sizeofComponent = 20;
@@ -710,7 +712,8 @@ class BuilderWasm {
setHeader(header) {
this.header=header;
this.n64 = Math.floor((this.header.P.bitLength() - 1) / 64)+1;
this.n64 = Math.floor((Scalar.bitLength(this.header.P) - 1) / 64)+1;
this.sizeFr = this.n64*8 + 8;
}
@@ -728,9 +731,9 @@ class BuilderWasm {
}
addConstant(c) {
c = bigInt(c);
c = this.F.e(c);
const cS = c.toString();
if (this.usedConstants[cS]) return this.usedConstants[cS];
if (typeof this.usedConstants[cS] != "undefined") return this.usedConstants[cS];
this.constants.push(c);
this.usedConstants[cS] = this.constants.length - 1;
return this.constants.length - 1;
@@ -842,7 +845,6 @@ class BuilderWasm {
_buildConstants(module) {
const self = this;
const R = bigInt.one.shiftLeft(this.n64*64);
const bytes = [];
for (let i=0; i<self.constants.length; i++) {
@@ -852,19 +854,27 @@ class BuilderWasm {
const fBytes = [].concat(...bytes);
this.pConstants = module.alloc(fBytes);
function Fr2Bytes(n) {
if (n.lt(bigInt("80000000", 16)) ) {
return shortMontgomeryPositive(n);
}
if (n.geq(self.header.P.minus(bigInt("80000000", 16))) ) {
return shortMontgomeryNegative(n);
const minShort = self.F.neg(self.F.e("80000000"));
const maxShort = self.F.e("7FFFFFFF", 16);
if ( (self.F.geq(n, minShort))
&&(self.F.leq(n, maxShort)))
{
if (self.F.geq(n, self.F.zero)) {
return shortMontgomeryPositive(n);
} else {
return shortMontgomeryNegative(n);
}
}
return longMontgomery(n);
function shortMontgomeryPositive(a) {
return [
...intToBytes32(parseInt(a)),
...intToBytes32(Scalar.toNumber(a)),
...intToBytes32(0x40000000),
...long(toMontgomery(a))
];
@@ -872,9 +882,9 @@ class BuilderWasm {
function shortMontgomeryNegative(a) {
const b = a.minus(self.header.P);
const b = -Scalar.toNumber(self.F.neg(a));
return [
...intToBytes32(parseInt(b)),
...intToBytes32(b),
...intToBytes32(0x40000000),
...long(toMontgomery(a))
];
@@ -889,27 +899,24 @@ class BuilderWasm {
}
function long(a) {
const bytes = [];
let r = bigInt(a);
let S = "";
let i = 0;
while (!r.isZero()) {
S = ("0000000000000000" + r.and(bigInt("FFFFFFFFFFFFFFFF", 16)).toString(16)).substr(-16);
bytes.push(hexToBytesR(S));
i++;
r = r.shiftRight(64);
}
while (i<self.n64) {
bytes.push(hexToBytesR("0000000000000000"));
i++;
const arr = Scalar.toArray(a, 0x100000000);
for (let i=0; i<self.F.n64*2; i++) {
const idx = arr.length-1-i;
if ( idx >=0) {
bytes.push(...intToBytes32(arr[idx]));
} else {
bytes.push(...intToBytes32(0));
}
}
const fBytes = [].concat(...bytes);
return fBytes;
return bytes;
}
function toMontgomery(a) {
return a.times(R).mod(self.header.P);
return self.F.mul(a, self.F.R);
}
}

View File

@@ -6,7 +6,6 @@ var tmp = require("tmp-promise");
const path = require("path");
const compiler = require("../../src/compiler");
const bigInt = require("big-integer");
const utils = require("../../src/utils");
const loadR1cs = require("r1csfile").load;
const ZqField = require("ffjavascript").ZqField;
@@ -82,7 +81,7 @@ class WasmTester {
const self = this;
if (this.constraints) return;
const r1cs = await loadR1cs(path.join(this.dir.path, this.baseName + ".r1cs"),true, false);
self.field = new ZqField(r1cs.prime);
self.F = new ZqField(r1cs.prime);
self.nVars = r1cs.nVars;
self.constraints = r1cs.constraints;
}
@@ -107,8 +106,8 @@ class WasmTester {
if (typeof self.symbols[prefix] == "undefined") {
assert(false, "Output variable not defined: "+ prefix);
}
const ba = bigInt(actualOut[self.symbols[prefix].varIdx]).toString();
const be = bigInt(eOut).toString();
const ba = actualOut[self.symbols[prefix].varIdx].toString();
const be = eOut.toString();
assert.strictEqual(ba, be, prefix);
}
}
@@ -138,16 +137,16 @@ class WasmTester {
}
function checkConstraint(constraint) {
const F = self.field;
const F = self.F;
const a = evalLC(constraint[0]);
const b = evalLC(constraint[1]);
const c = evalLC(constraint[2]);
assert (F.sub(F.mul(a,b), c).isZero(), "Constraint doesn't match");
assert (F.isZero(F.sub(F.mul(a,b), c)), "Constraint doesn't match");
}
function evalLC(lc) {
const F = self.field;
const F = self.F;
let v = F.zero;
for (let w in lc) {
v = F.add(