Begining of wasm

This commit is contained in:
Jordi Baylina
2020-03-09 21:16:56 +01:00
parent 6c1a3e7687
commit 8f63d18ff4
70 changed files with 4135 additions and 1353 deletions

View File

@@ -34,7 +34,6 @@ function build(ctx) {
ctx.codes_sizes = [];
ctx.definedSizes = {};
ctx.addSizes = addSizes;
ctx.constantsMap = {};
ctx.addConstant = addConstant;
ctx.addConstant(bigInt.zero);
ctx.addConstant(bigInt.one);
@@ -287,12 +286,7 @@ function addSizes(_sizes) {
}
function addConstant(c) {
c = bigInt(c);
const s = c.toString();
if (typeof (this.constantsMap[s]) !== "undefined") return this.constantsMap[s];
const cIdx = this.builder.addConstant(c);
this.constantsMap[s] = cIdx;
return cIdx;
return this.builder.addConstant(c);
}
function buildFunction(name, paramValues) {
@@ -320,7 +314,7 @@ function buildFunction(name, paramValues) {
ctx.scopes = [{}];
ctx.refs = [];
ctx.conditionalCode = false;
ctx.fnBuilder = ctx.builder.newFunctionBuilder(`${name}_${h}`, instanceDef);
ctx.fnBuilder = ctx.builder.newFunctionBuilder(`${name}_${h}`, instanceDef, ctx.functions[name].params);
ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
ctx.uniqueNames = Object.assign({},ctx.globalNames);
ctx.returnValue = null;

View File

@@ -1,616 +0,0 @@
const streamFromMultiArray = require("./stream_from_multiarray");
const bigInt = require("big-integer");
const utils = require("./utils");
const assert = require("assert");
function ref2src(c) {
if (c[0] == "R") {
return c[1];
} else if (c[0] == "V") {
return c[1].toString();
} else if (c[0] == "C") {
return `(ctx->circuit->constants + ${c[1]})`;
} else if (c[0] == "CC") {
return "__cIdx";
} else {
assert(false);
}
}
class CodeBuilderC {
constructor() {
this.ops = [];
}
addComment(comment) {
this.ops.push({op: "COMMENT", comment});
}
addBlock(block) {
this.ops.push({op: "BLOCK", block});
}
calcOffset(dLabel, offsets) {
this.ops.push({op: "CALCOFFSETS", dLabel, offsets});
}
assign(dLabel, src, sOffset) {
this.ops.push({op: "ASSIGN", dLabel, src, sOffset});
}
getSubComponentOffset(dLabel, component, hash, hashLabel) {
this.ops.push({op: "GETSUBCOMPONENTOFFSET", dLabel, component, hash, hashLabel});
}
getSubComponentSizes(dLabel, component, hash, hashLabel) {
this.ops.push({op: "GETSUBCOMPONENTSIZES", dLabel, component, hash, hashLabel});
}
getSignalOffset(dLabel, component, hash, hashLabel) {
this.ops.push({op: "GETSIGNALOFFSET", dLabel, component, hash, hashLabel});
}
getSignalSizes(dLabel, component, hash, hashLabel) {
this.ops.push({op: "GETSIGNALSIZES", dLabel, component, hash, hashLabel});
}
setSignal(component, signal, value) {
this.ops.push({op: "SETSIGNAL", component, signal, value});
}
getSignal(dLabel, component, signal) {
this.ops.push({op: "GETSIGNAL", dLabel, component, signal});
}
copyN(dLabel, offset, src, n) {
this.ops.push({op: "COPYN", dLabel, offset, src, n});
}
copyNRet(src, n) {
this.ops.push({op: "COPYNRET", src, n});
}
fieldOp(dLabel, fOp, params) {
this.ops.push({op: "FOP", dLabel, fOp, params});
}
ret() {
this.ops.push({op: "RET"});
}
addLoop(condLabel, body) {
this.ops.push({op: "LOOP", condLabel, body});
}
addIf(condLabel, thenCode, elseCode) {
this.ops.push({op: "IF", condLabel, thenCode, elseCode});
}
fnCall(fnName, retLabel, params) {
this.ops.push({op: "FNCALL", fnName, retLabel, params});
}
checkConstraint(a, b, strErr) {
this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr});
}
concat(cb) {
this.ops.push(...cb.ops);
}
hasCode() {
for (let i=0; i<this.ops.length; i++) {
if (this.ops[i].op != "COMMENT") return true;
}
return false;
}
_buildOffset(offsets) {
let rN=0;
let S = "";
offsets.forEach((o) => {
if ((o[0][0] == "V") && (o[1][0]== "V")) {
rN += o[0][1]*o[1][1];
return;
}
let f="";
if (o[0][0] == "V") {
if (o[0][1]==0) return;
f += o[0][1];
} else if (o[0][0] == "RI") {
if (o[0][1]==0) return;
f += o[0][1];
} else if (o[0][0] == "R") {
f += `Fr_toInt(${o[0][1]})`;
} else {
assert(false);
}
if (o[1][0] == "V") {
if (o[1][1]==0) return;
if (o[1][1]>1) {
f += "*" + o[1][1];
}
} else if (o[1][0] == "RS") {
f += `*${o[1][1]}[${o[1][2]}]`;
} else {
assert(false);
}
if (S!="") S+= " + ";
S += f;
});
if (rN>0) {
S = `${rN} + ${S}`;
}
return S;
}
build(code) {
this.ops.forEach( (o) => {
if (o.op == "COMMENT") {
code.push(`/* ${o.comment} */`);
} else if (o.op == "BLOCK") {
const codeBlock=[];
o.block.build(codeBlock);
code.push(utils.ident(codeBlock));
} else if (o.op == "CALCOFFSETS") {
code.push(`${o.dLabel} = ${this._buildOffset(o.offsets)};`);
} else if (o.op == "ASSIGN") {
const oS = ref2src(o.sOffset);
if (oS != "0") {
code.push(`${o.dLabel} = ${ref2src(o.src)} + ${oS};`);
} else {
code.push(`${o.dLabel} = ${ref2src(o.src)};`);
}
} else if (o.op == "GETSUBCOMPONENTOFFSET") {
code.push(`${o.dLabel} = ctx->getSubComponentOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
} else if (o.op == "GETSUBCOMPONENTSIZES") {
code.push(`${o.dLabel} = ctx->getSubComponentSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
} else if (o.op == "GETSIGNALOFFSET") {
code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
} else if (o.op == "GETSIGNALSIZES") {
code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
} else if (o.op == "SETSIGNAL") {
code.push(`ctx->setSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${ref2src(o.value)});`);
} else if (o.op == "GETSIGNAL") {
code.push(`ctx->getSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${o.dLabel});`);
} else if (o.op == "COPYN") {
const oS = ref2src(o.offset);
const dLabel = (oS != "0") ? (o.dLabel + "+" + oS) : o.dLabel;
code.push(`Fr_copyn(${dLabel}, ${ref2src(o.src)}, ${o.n});`);
} else if (o.op == "COPYNRET") {
code.push(`Fr_copyn(__retValue, ${ref2src(o.src)}, ${o.n});`);
} else if (o.op == "RET") {
code.push("goto returnFunc;");
} else if (o.op == "FOP") {
let paramsS = "";
for (let i=0; i<o.params.length; i++) {
if (i>0) paramsS += ", ";
paramsS += ref2src(o.params[i]);
}
code.push(`Fr_${o.fOp}(${o.dLabel}, ${paramsS});`);
} else if (o.op == "LOOP") {
code.push(`while (Fr_isTrue(${o.condLabel})) {`);
const body = [];
o.body.build(body);
code.push(utils.ident(body));
code.push("}");
} else if (o.op == "IF") {
code.push(`if (Fr_isTrue(${o.condLabel})) {`);
const thenCode = [];
o.thenCode.build(thenCode);
code.push(utils.ident(thenCode));
if (o.elseCode) {
code.push("} else {");
const elseCode = [];
o.elseCode.build(elseCode);
code.push(utils.ident(elseCode));
}
code.push("}");
} else if (o.op == "FNCALL") {
code.push(`${o.fnName}(ctx, ${o.retLabel}, ${o.params.join(",")});`);
} else if (o.op == "CHECKCONSTRAINT") {
code.push(`ctx->checkConstraint(__cIdx, ${ref2src(o.a)}, ${ref2src(o.b)}, "${o.strErr}");`);
}
});
}
}
class FunctionBuilderC {
constructor(name, instanceDef, type) {
this.name = name;
this.instanceDef = instanceDef;
this.type = type; // "COMPONENT" or "FUNCTIOM"
this.definedFrElements = [];
this.definedIntElements = [];
this.definedSizeElements = [];
this.definedPFrElements = [];
this.initializedElements = [];
this.initializedSignalOffset = [];
this.initializedSignalSizes = [];
}
defineFrElements(dLabel, size) {
this.definedFrElements.push({dLabel, size});
}
defineIntElement(dLabel) {
this.definedIntElements.push({dLabel});
}
defineSizesElement(dLabel) {
this.definedSizeElements.push({dLabel});
}
definePFrElement(dLabel) {
this.definedPFrElements.push({dLabel});
}
initializeFrElement(dLabel, offset, idConstant) {
this.initializedElements.push({dLabel, offset, idConstant});
}
initializeSignalOffset(dLabel, component, hash, hashLabel) {
this.initializedSignalOffset.push({dLabel, component, hash, hashLabel});
}
initializeSignalSizes(dLabel, component, hash, hashLabel) {
this.initializedSignalSizes.push({dLabel, component, hash, hashLabel});
}
setParams(params) {
this.params = params;
}
_buildHeader(code) {
this.definedFrElements.forEach( (o) => {
code.push(`FrElement ${o.dLabel}[${o.size}];`);
});
this.definedIntElements.forEach( (o) => {
code.push(`int ${o.dLabel};`);
});
this.definedSizeElements.forEach( (o) => {
code.push(`Circom_Sizes ${o.dLabel};`);
});
this.definedPFrElements.forEach( (o) => {
code.push(`PFrElement ${o.dLabel};`);
});
this.initializedElements.forEach( (o) => {
code.push(`Fr_copy(&(${o.dLabel}[${o.offset}]), ctx->circuit->constants +${o.idConstant});`);
});
this.initializedSignalOffset.forEach( (o) => {
code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
});
this.initializedSignalSizes.forEach( (o) => {
code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`);
});
}
_buildFooter(code) {
}
newCodeBuilder() {
return new CodeBuilderC();
}
setBody(body) {
this.body = body;
}
build(code) {
code.push(
"/*",
this.instanceDef,
"*/"
);
if (this.type=="COMPONENT") {
code.push(`void ${this.name}(Circom_CalcWit *ctx, int __cIdx) {`);
} else if (this.type=="FUNCTION") {
let sParams = "";
for (let i=0;i<this.params.length;i++ ) sParams += `, PFrElement ${this.params[i]}`;
code.push(`void ${this.name}(Circom_CalcWit *ctx, PFrElement __retValue ${sParams}) {`);
} else {
assert(false);
}
const fnCode = [];
this._buildHeader(fnCode);
this.body.build(fnCode);
if (this.type=="COMPONENT") {
fnCode.push("ctx->finished(__cIdx);");
} else if (this.type=="FUNCTION") {
fnCode.push("returnFunc: ;");
} else {
assert(false);
}
this._buildFooter(fnCode);
code.push(utils.ident(fnCode));
code.push("}");
}
}
class BuilderC {
constructor() {
this.hashMaps={};
this.componentEntriesTables={};
this.sizes ={};
this.constants = [];
this.functions = [];
this.components = [];
}
setHeader(header) {
this.header=header;
}
// ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
addHashMap(name, hm) {
this.hashMaps[name] = hm;
}
addComponentEntriesTable(name, cet) {
this.componentEntriesTables[name] = cet;
}
addSizes(name, accSizes) {
this.sizes[name] = accSizes;
}
addConstant(c) {
this.constants.push(c);
return this.constants.length - 1;
}
addFunction(fnBuilder) {
this.functions.push(fnBuilder);
}
addComponent(component) {
this.components.push(component);
}
setMapIsInput(map) {
this.mapIsInput = map;
}
setWit2Sig(wit2sig) {
this.wit2sig = wit2sig;
}
newComponentFunctionBuilder(name, instanceDef) {
return new FunctionBuilderC(name, instanceDef, "COMPONENT");
}
newFunctionBuilder(name, instanceDef) {
return new FunctionBuilderC(name, instanceDef, "FUNCTION");
}
// Body functions
_buildHeader(code) {
code.push(
"#include \"circom.h\"",
"#include \"calcwit.h\"",
`#define NSignals ${this.header.NSignals}`,
`#define NComponents ${this.header.NComponents}`,
`#define NOutputs ${this.header.NOutputs}`,
`#define NInputs ${this.header.NInputs}`,
`#define NVars ${this.header.NVars}`,
`#define __P__ "${this.header.P.toString()}"`,
""
);
}
_buildHashMaps(code) {
code.push("// Hash Maps ");
for (let hmName in this.hashMaps ) {
const hm = this.hashMaps[hmName];
let c = `Circom_HashEntry ${hmName}[256] = {`;
for (let i=0; i<256; i++) {
c += i>0 ? "," : "";
if (hm[i]) {
c += `{0x${hm[i][0]}LL, ${hm[i][1]}} /* ${hm[i][2]} */`;
} else {
c += "{0,0}";
}
}
c += "};";
code.push(c);
}
}
_buildComponentEntriesTables(code) {
code.push("// Component Entry tables");
for (let cetName in this.componentEntriesTables) {
const cet = this.componentEntriesTables[cetName];
code.push(`Circom_ComponentEntry ${cetName}[${cet.length}] = {`);
for (let j=0; j<cet.length; j++) {
const ty = cet[j].type == "S" ? "_typeSignal" : "_typeComponent";
code.push(` ${j>0?",":" "}{${cet[j].offset},${cet[j].sizeName}, ${ty}}`);
}
code.push("};");
}
}
_buildSizes(code) {
code.push("// Sizes");
for (let sName in this.sizes) {
const accSizes = this.sizes[sName];
let c = `Circom_Size ${sName}[${accSizes.length}] = {`;
for (let i=0; i<accSizes.length; i++) {
if (i>0) c += ",";
c += accSizes[i];
}
c += "};";
code.push(c);
}
}
_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}] = {`);
for (let i=0; i<self.constants.length; i++) {
code.push((i>0 ? "," : " ") + "{" + number2Code(self.constants[i]) + "}");
}
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);
}
return addLongMontgomery(n);
function addShortMontgomeryPositive(a) {
return `${a.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`;
}
function addShortMontgomeryNegative(a) {
const b = a.minus(self.header.P);
return `${b.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`;
}
function addLongMontgomery(a) {
return `0, 0xC0000000, { ${getLongString(toMontgomery(a))} }`;
}
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++;
}
return S;
}
function toMontgomery(a) {
return a.times(R).mod(self.header.P);
}
}
}
_buildFunctions(code) {
for (let i=0; i<this.functions.length; i++) {
const cfb = this.functions[i];
cfb.build(code);
}
}
_buildComponents(code) {
code.push("// Components");
code.push(`Circom_Component _components[${this.components.length}] = {`);
for (let i=0; i<this.components.length; i++) {
const c = this.components[i];
const sep = i>0 ? " ," : " ";
code.push(`${sep}{${c.hashMapName}, ${c.entryTableName}, ${c.functionName}, ${c.nInSignals}, ${c.newThread}}`);
}
code.push("};");
}
_buildMapIsInput(code) {
code.push("// mapIsInput");
code.push(`u32 _mapIsInput[${this.mapIsInput.length}] = {`);
let line = "";
for (let i=0; i<this.mapIsInput.length; i++) {
line += i>0 ? ", " : " ";
line += toHex(this.mapIsInput[i]);
if (((i+1) % 64)==0) {
code.push(" "+line);
line = "";
}
}
if (line != "") code.push(" "+line);
code.push("};");
function toHex(number) {
if (number < 0) number = 0xFFFFFFFF + number + 1;
let S=number.toString(16).toUpperCase();
while (S.length<8) S = "0" + S;
return "0x"+S;
}
}
_buildWit2Sig(code) {
code.push("// Witness to Signal Table");
code.push(`int _wit2sig[${this.wit2sig.length}] = {`);
let line = "";
for (let i=0; i<this.wit2sig.length; i++) {
line += i>0 ? "," : " ";
line += this.wit2sig[i];
if (((i+1) % 64) == 0) {
code.push(" "+line);
line = "";
}
}
if (line != "") code.push(" "+line);
code.push("};");
}
_buildCircuitVar(code) {
code.push(
"// Circuit Variable",
"Circom_Circuit _circuit = {" ,
" NSignals,",
" NComponents,",
" NInputs,",
" NOutputs,",
" NVars,",
" _wit2sig,",
" _components,",
" _mapIsInput,",
" _constants,",
" __P__",
"};"
);
}
build() {
const code=[];
this._buildHeader(code);
this._buildSizes(code);
this._buildConstants(code);
this._buildHashMaps(code);
this._buildComponentEntriesTables(code);
this._buildFunctions(code);
this._buildComponents(code);
this._buildMapIsInput(code);
this._buildWit2Sig(code);
this._buildCircuitVar(code);
return streamFromMultiArray(code);
}
}
module.exports = BuilderC;

View File

@@ -1,28 +0,0 @@
class BuilderWasm {
constructor() {
}
setHeader(header) {
this.header=header;
}
// ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
addHashMap(name, ht) {
this.hashTables[name] = ht;
}
addComponentEntriesTable(name, cet) {
this.componentEntriesTables[name] = cet;
}
addSizes(name, accSizes) {
this.sizes[name] = accSizes;
}
build() {
}
}
module.exports = BuilderWasm;

156
src/buildsyms.js Normal file
View File

@@ -0,0 +1,156 @@
const Readable = require("stream").Readable;
module.exports = function buildSyms(ctx) {
const rs = Readable();
let it = new ComponentIt(ctx, 0, "main");
let counter = 0;
rs._read = function() {
const actual = it.current();
if (actual == null ) {
rs.push(null);
return;
}
let s=actual.offset;
while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
let wId = ctx.signals[s].id;
if (typeof(wId) == "undefined") wId=-1;
rs.push(`${actual.offset},${wId},${actual.name}\n`);
it.next();
counter ++;
if ((ctx.verbose)&&(counter%10000 == 0)) console.log("Symbols saved: "+counter);
};
return rs;
};
class SignalIt {
constructor (ctx, offset, prefix) {
this.ctx = ctx;
this.offset = offset;
this.prefix = prefix;
this.cur = 0;
}
next() {
this.cur = 1;
return this.current();
}
current() {
if (this.cur == 0) {
return {offset: this.offset, name: this.prefix};
}
}
}
class ArrayIt {
constructor (ctx, type, sizes, offset, prefix) {
if (sizes.length == 0) {
if (type == "S") {
return new SignalIt(ctx, offset, prefix);
} else {
return new ComponentIt(ctx, offset, prefix);
}
}
this.ctx = ctx;
this.type = type;
this.sizes = sizes;
this.offset = offset;
this.prefix = prefix;
this.subIt = null;
this.cur = 0;
this.subArrSize = 1;
for (let i=1; i<sizes.length; i++) {
this.subArrSize *= sizes[i];
}
this._loadSubIt();
}
_loadSubIt() {
if (this.cur < this.sizes[0]) {
this.subIt = new ArrayIt(this.ctx, this.type, this.sizes.slice(1), this.offset + this.cur*this.subArrSize, this.prefix + "[" + this.cur + "]");
}
}
next() {
if (this.subIt) {
const res = this.subIt.next();
if (res == null) {
this.subIt = null;
this.cur++;
this._loadSubIt();
}
}
return this.current();
}
current() {
if (this.subIt) {
return this.subIt.current();
} else {
return null;
}
}
}
class ComponentIt {
constructor (ctx, idxComponent, prefix) {
this.ctx = ctx;
this.idxComponent = idxComponent;
this.prefix = prefix;
this.names = Object.keys(ctx.components[idxComponent].names.o);
this.subIt = null;
this.cur = 0;
this._loadSubIt();
}
_loadSubIt() {
if (this.cur < this.names.length) {
const entrie = this.ctx.components[this.idxComponent].names.o[this.names[this.cur]];
this.subIt = new ArrayIt(this.ctx, entrie.type, entrie.sizes, entrie.offset, this.prefix + "." + this.names[this.cur]);
}
}
next() {
if (this.subIt) {
const res = this.subIt.next();
if (res == null) {
this.subIt = null;
this.cur++;
this._loadSubIt();
}
}
return this.current();
}
current() {
if (this.subIt) {
return this.subIt.current();
} else {
return null;
}
}
}

View File

@@ -1,52 +0,0 @@
const ModuleBuilder = require("wasmbuilder").ModuleBuilder;
const gen = require("./gencode");
module.exports = function buildWasm(ctx) {
const fDefined = {};
ctx.module = new ModuleBuilder();
for (let f in ctx.functions) {
ctx.f = ctx.module.addFunction(f);
ctx.c = ctx.f.getCodeBuilder();
ctx.scope = {};
for (let p in ctx.functions[f].params) {
const param = ctx.functions[f].params[p];
ctx.f.addParam(param.name, "i32");
ctx.scope[param.name] = {
type: "PARAM",
sels: param.sels,
getter: () => { return ctx.c.getLocal(param.name); },
setter: (v) => { return ctx.c.setLocal(param.name, v); }
};
}
gen(ctx, ctx.functions[f].block);
}
for (let i=0; i<ctx.components.length; i++) {
const h = hashComponentCall(ctx, i);
const fName = ctx.components[i].temlate+"_"+h;
if (!fDefined[fName]) {
ctx.f = ctx.module.addFunction(fName);
ctx.c = ctx.f.getCodeBuilder();
ctx.scope = {};
for (let p in ctx.components[i].params) {
ctx.scope[p] = createConstant(ctx, ctx.components[i].params[p]);
}
gen(ctx, ctx.templates[ctx.components[i].temlate].block);
}
ctx.components[i].f = fName;
}
};
function buildSetSignal(ctx) {
}

View File

@@ -1,186 +0,0 @@
const chai = require("chai");
const assert = chai.assert;
const fs = require("fs");
var tmp = require("tmp-promise");
const path = require("path");
const compiler = require("./compiler");
const util = require("util");
const exec = util.promisify(require("child_process").exec);
const stringifyBigInts = require("./utils").stringifyBigInts;
const unstringifyBigInts = require("./utils").unstringifyBigInts;
const bigInt = require("big-integer");
const utils = require("./utils");
const loadR1cs = require("./r1csfile").loadR1cs;
const ZqField = require("fflib").ZqField;
module.exports = c_tester;
async function c_tester(circomFile, _options) {
tmp.setGracefulCleanup();
const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true });
// console.log(dir.path);
const baseName = path.basename(circomFile, ".circom");
const options = Object.assign({}, _options);
options.cSourceWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".cpp"));
options.symWriteStream = fs.createWriteStream(path.join(dir.path, baseName + ".sym"));
options.r1csFileName = path.join(dir.path, baseName + ".r1cs");
await compiler(circomFile, options);
const cdir = path.join(__dirname, "..", "c");
await exec("cp" +
` ${path.join(dir.path, baseName + ".cpp")}` +
" /tmp/circuit.cpp"
);
await exec("g++" +
` ${path.join(cdir, "main.cpp")}` +
` ${path.join(cdir, "calcwit.cpp")}` +
` ${path.join(cdir, "utils.cpp")}` +
` ${path.join(cdir, "fr.c")}` +
` ${path.join(cdir, "fr.o")}` +
` ${path.join(dir.path, baseName + ".cpp")} ` +
` -o ${path.join(dir.path, baseName)}` +
` -I ${cdir}` +
" -lgmp -std=c++11 -DSANITY_CHECK"
);
// console.log(dir.path);
return new CTester(dir, baseName);
}
class CTester {
constructor(dir, baseName) {
this.dir=dir;
this.baseName = baseName;
}
async release() {
await this.dir.cleanup();
}
async calculateWitness(input) {
await fs.promises.writeFile(
path.join(this.dir.path, "in.json"),
JSON.stringify(stringifyBigInts(input), null, 1)
);
await exec(`${path.join(this.dir.path, this.baseName)}` +
` ${path.join(this.dir.path, "in.json")}` +
` ${path.join(this.dir.path, "out.json")}`
);
const resStr = await fs.promises.readFile(
path.join(this.dir.path, "out.json")
);
const res = unstringifyBigInts(JSON.parse(resStr));
return res;
}
async loadSymbols() {
if (this.symbols) return;
this.symbols = {};
const symsStr = await fs.promises.readFile(
path.join(this.dir.path, this.baseName + ".sym"),
"utf8"
);
const lines = symsStr.split("\n");
for (let i=0; i<lines.length; i++) {
const arr = lines[i].split(",");
if (arr.length!=3) continue;
this.symbols[arr[2]] = {
idx: Number(arr[0]),
idxWit: Number(arr[1])
};
}
}
async loadConstraints() {
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.nWires = r1cs.nWires;
self.constraints = r1cs.constraints;
}
async assertOut(actualOut, expectedOut) {
const self = this;
if (!self.symbols) await self.loadSymbols();
checkObject("main", expectedOut);
function checkObject(prefix, eOut) {
if (Array.isArray(eOut)) {
for (let i=0; i<eOut.length; i++) {
checkObject(prefix + "["+i+"]", eOut[i]);
}
} else if ((typeof eOut == "object")&&(eOut.constructor.name == "Object")) {
for (let k in eOut) {
checkObject(prefix + "."+k, eOut[k]);
}
} else {
if (typeof self.symbols[prefix] == "undefined") {
assert(false, "Output variable not defined: "+ prefix);
}
const ba = bigInt(actualOut[self.symbols[prefix].idxWit]).toString();
const be = bigInt(eOut).toString();
assert.strictEqual(ba, be, prefix);
}
}
}
async getDecoratedOutput(witness) {
const self = this;
const lines = [];
if (!self.symbols) await self.loadSymbols();
for (let n in self.symbols) {
let v;
if (utils.isDefined(witness[self.symbols[n].idxWit])) {
v = witness[self.symbols[n].idxWit].toString();
} else {
v = "undefined";
}
lines.push(`${n} --> ${v}`);
}
return lines.join("\n");
}
async checkConstraints(witness) {
const self = this;
if (!self.constraints) await self.loadConstraints();
for (let i=0; i<self.constraints.length; i++) {
checkConstraint(self.constraints[i]);
}
function checkConstraint(constraint) {
const F = self.field;
const a = evalLC(constraint.a);
const b = evalLC(constraint.b);
const c = evalLC(constraint.c);
assert (F.sub(F.mul(a,b), c).isZero(), "Constraint doesn't match");
}
function evalLC(lc) {
const F = self.field;
let v = F.zero;
for (let w in lc) {
v = F.add(
v,
F.mul( lc[w], witness[w] )
);
}
return v;
}
}
}

View File

@@ -21,14 +21,15 @@ const bigInt = require("big-integer");
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const sONE = 0;
const build = require("./build");
const BuilderC = require("./builder_c");
const BuilderWasm = require("./builder_wasm");
const BuilderC = require("../ports/c/builder.js");
const BuilderWasm = require("../ports/wasm/builder.js");
const constructionPhase = require("./construction_phase");
const Ctx = require("./ctx");
const ZqField = require("fflib").ZqField;
const utils = require("./utils");
const buildR1cs = require("./r1csfile").buildR1cs;
const BigArray = require("./bigarray");
const buildSyms = require("./buildsyms");
module.exports = compile;
@@ -92,11 +93,17 @@ async function compile(srcFile, options) {
// await new Promise(fulfill => options.cSourceWriteStream.on("finish", fulfill));
}
if (options.wasmWriteStream) {
if ((options.wasmWriteStream)||(options.watWriteStream)) {
ctx.builder = new BuilderWasm();
build(ctx);
const rdStream = ctx.builder.build();
rdStream.pipe(options.wasmWriteStream);
if (options.wasmWriteStream) {
const rdStream = ctx.builder.build("wasm");
rdStream.pipe(options.wasmWriteStream);
}
if (options.watWriteStream) {
const rdStream = ctx.builder.build("wat");
rdStream.pipe(options.watWriteStream);
}
// await new Promise(fulfill => options.wasmWriteStream.on("finish", fulfill));
}
@@ -109,7 +116,10 @@ async function compile(srcFile, options) {
}
if (options.symWriteStream) {
buildSyms(ctx, options.symWriteStream);
const rdStream = buildSyms(ctx);
rdStream.pipe(options.symWriteStream);
// await new Promise(fulfill => options.symWriteStream.on("finish", fulfill));
}
// const def = buildCircuitDef(ctx, mainCode);
@@ -504,6 +514,8 @@ function buildConstraints(ctx) {
return res;
}
*/
/*
function buildSyms(ctx, strm) {
let nSyms;
@@ -543,5 +555,5 @@ function buildSyms(ctx, strm) {
}
*/

View File

@@ -754,7 +754,13 @@ function toRefA_Int1(ctx, ast, aRef) {
const a = ctx.refs[aRef];
if (a.sizes[0] != 1) return ctx.throwError(ast, "Expected only one element");
if (a.used) {
return ["R", a.label];
if (a.type == "INT") {
return ["RI", a.label];
} else if (a.type == "BIGINT") {
return ["R", a.label];
} else {
assert(false);
}
} else {
return ["V", a.value[0]];
}
@@ -818,9 +824,7 @@ function genArray(ctx, ast) {
function genFunctionCall(ctx, ast) {
if (ast.name == "log") {
const vRef = gen(ctx, ast.params[0]);
const val = ctx.refs[vRef];
instantiateRef(ctx, vRef, val.value);
ctx.code+=`ctx->log(${val.label});`;
ctx.codeBuilder.log(toRefA_Fr1(ctx, ast.params[0], vRef));
return vRef;
}
const params = [];

View File

@@ -235,7 +235,7 @@ async function buildR1cs(ctx, fileName) {
}
for (let i=0; i<arr.length; i++) {
await writeU32(arr[i]);
if ((ctx.verbose)&&(i%100000)) console.log("writing label2wire map: ", i);
if ((ctx.verbose)&&(i%100000)) console.log("writing wire2label map: ", i);
}
const mapSize = p - pMapSize -4;

View File

@@ -0,0 +1,21 @@
const Readable = require("stream").Readable;
module.exports = function streamFromArrayBin(a) {
const rs = Readable();
let curIndex = 0;
rs._read = function(size) {
if (curIndex >= a.length) {
rs.push(null);
return;
}
const start = curIndex;
const end = Math.min(a.length, curIndex+size);
curIndex = end;
rs.push(a.slice(start, end));
};
return rs;
};

View File

@@ -1,7 +1,7 @@
const Readable = require("stream").Readable;
module.exports = function streamFromMultiarray(ma) {
module.exports = function streamFromArrayTxt(ma) {
const rs = Readable();
let curIndex = getFirstIdx(ma);
@@ -10,7 +10,7 @@ module.exports = function streamFromMultiarray(ma) {
let res;
res = objFromIdx(ma, curIndex);
curIndex = nextIdx(curIndex);
if (res!=null) {
if (res!==null) {
rs.push(res + "\n");
} else {
rs.push(null);