From 9f15dd2e29bb782c35d602922d87c6d1e7a0a771 Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Thu, 26 Nov 2020 07:22:59 +0100 Subject: [PATCH] Allows pass signal arrays to a function --- package-lock.json | 6 ++-- package.json | 2 +- ports/c/builder.js | 7 +++-- ports/wasm/build_runtime.js | 38 +++++++++++++++++++++++++ ports/wasm/builder.js | 10 ++++--- src/construction_phase.js | 3 +- src/gencode.js | 55 ++++++++++++++++++++++++++++++------ test/basiccases.js | 2 +- test/basiccases.json | 11 ++++++++ test/circuits/fnarray.circom | 21 ++++++++++++++ 10 files changed, 133 insertions(+), 22 deletions(-) create mode 100644 test/circuits/fnarray.circom diff --git a/package-lock.json b/package-lock.json index 6d05780..6252b43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -208,9 +208,9 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" }, "circom_runtime": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/circom_runtime/-/circom_runtime-0.1.5.tgz", - "integrity": "sha512-BT3d9VCrH/rBRbThDXG731JwezKyskxyE46nACO6Tt/jaorn27LDxFDORdAAjyD0RAoBt+6FpaTp3qlYSx7Pjg==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/circom_runtime/-/circom_runtime-0.1.8.tgz", + "integrity": "sha512-5ZmzCyidkNPb1zZsJGRXTuWcJ6kW6+gRBtHgf2tFqTh5dUyWVVPH0Zg7AsU2ijPr1AmYZUlme0yORUZK5HrjOA==", "requires": { "ffjavascript": "0.2.10", "fnv-plus": "^1.3.1" diff --git a/package.json b/package.json index f5f038e..c7c8d44 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "chai": "^4.2.0", - "circom_runtime": "0.1.5", + "circom_runtime": "0.1.8", "fastfile": "0.0.18", "ffiasm": "0.1.1", "ffjavascript": "0.2.22", diff --git a/ports/c/builder.js b/ports/c/builder.js index 99eaf79..b1a86f7 100644 --- a/ports/c/builder.js +++ b/ports/c/builder.js @@ -59,8 +59,9 @@ class CodeBuilderC { this.ops.push({op: "SETSIGNAL", component, signal, value}); } - getSignal(dLabel, component, signal) { - this.ops.push({op: "GETSIGNAL", dLabel, component, signal}); + getSignal(dLabel, component, signal, n) { + if (typeof(n) == "undefined") n=1; + this.ops.push({op: "GETSIGNAL", dLabel, component, signal, n}); } copyN(dLabel, offset, src, n) { @@ -181,7 +182,7 @@ class CodeBuilderC { } 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});`); + code.push(`ctx->multiGetSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${o.dLabel}, ${o.n});`); } else if (o.op == "COPYN") { const oS = ref2src(o.offset); const dLabel = (oS != "0") ? (o.dLabel + "+" + oS) : o.dLabel; diff --git a/ports/wasm/build_runtime.js b/ports/wasm/build_runtime.js index a299986..863bd9a 100644 --- a/ports/wasm/build_runtime.js +++ b/ports/wasm/build_runtime.js @@ -412,6 +412,42 @@ module.exports = function buildRuntime(module, builder) { } + function buildMultiGetSignal() { + const f = module.addFunction("multiGetSignal"); + f.addParam("cIdx", "i32"); + f.addParam("pR", "i32"); + f.addParam("component", "i32"); + f.addParam("signal", "i32"); + f.addParam("n", "i32"); + f.addLocal("i", "i32"); + + const c = f.getCodeBuilder(); + + 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( + "getSignal", + c.getLocal("cIdx"), + c.i32_add( + c.getLocal("pR"), + c.i32_mul( + c.getLocal("i"), + c.i32_const(builder.sizeFr) + ) + ), + c.getLocal("component"), + c.i32_add( + c.getLocal("signal"), + c.getLocal("i") + ) + ), + c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))), + c.br(0) + )) + ); + } function buildSetSignal() { const f = module.addFunction("setSignal"); @@ -847,6 +883,7 @@ module.exports = function buildRuntime(module, builder) { buildGetSignal(); buildSetSignal(); + buildMultiGetSignal(); buildComponentStarted(); buildComponentFinished(); @@ -867,6 +904,7 @@ module.exports = function buildRuntime(module, builder) { module.exportFunction("getFrLen"); module.exportFunction("getSignalOffset32"); module.exportFunction("setSignal"); + module.exportFunction("multiGetSignal"); module.exportFunction("getPWitness"); module.exportFunction("Fr_toInt"); module.exportFunction("getPRawPrime"); diff --git a/ports/wasm/builder.js b/ports/wasm/builder.js index 424504e..2a4c08d 100644 --- a/ports/wasm/builder.js +++ b/ports/wasm/builder.js @@ -62,8 +62,9 @@ class CodeBuilderWasm { this.ops.push({op: "SETSIGNAL", component, signal, value}); } - getSignal(dLabel, component, signal) { - this.ops.push({op: "GETSIGNAL", dLabel, component, signal}); + getSignal(dLabel, component, signal, n) { + if (typeof n == "undefined") n=1; + this.ops.push({op: "GETSIGNAL", dLabel, component, signal, n}); } copyN(dLabel, offset, src, n) { @@ -251,11 +252,12 @@ class CodeBuilderWasm { } else if (o.op == "GETSIGNAL") { code.push( c.call( - "getSignal", + "multiGetSignal", c.getLocal("cIdx"), this.fnBuilder._getPtr(c, o.dLabel), this.fnBuilder._deRefInt(c, o.component), - this.fnBuilder._deRefInt(c, o.signal) + this.fnBuilder._deRefInt(c, o.signal), + c.i32_const(o.n) ) ); } else if (o.op == "COPYN") { diff --git a/src/construction_phase.js b/src/construction_phase.js index 5765a84..14780f1 100644 --- a/src/construction_phase.js +++ b/src/construction_phase.js @@ -888,7 +888,8 @@ function execOpOp(ctx, ast, op, lr) { right = {t:"N", v: ctx.F.one}; } - if (!right) return ctx.throwError(ast, "adding a no number"); + if (!right) return ctx.throwError(ast, "right operand is not initialized"); + if (!resBefore) return ctx.throwError(ast, "left operand is not initialized"); const resAfter = ctx.lc[op](resBefore, right); left.v[o] = resAfter; diff --git a/src/gencode.js b/src/gencode.js index bd120df..27b2f81 100644 --- a/src/gencode.js +++ b/src/gencode.js @@ -284,6 +284,32 @@ function genDeclareComponent(ctx, ast) { } function genDeclareSignal(ctx, ast) { + + const v = ctx.refs[ast.refId]; + + let sizes; + if (ast.name.selectors.length>0) { + sizes=[]; + for (let i=0; i< ast.name.selectors.length; i++) { + const sizeRef = gen(ctx, ast.name.selectors[i]); + const size = ctx.refs[sizeRef]; + if (size.sizes[0] != 1) return ctx.throwError(ast, "A selector cannot be an array"); + if (size.used) return ctx.throwError(ast, "Signal size variables not allowed"); + sizes.push(Scalar.toNumber(size.value[0])); + } + sizes = utils.accSizes(sizes); + } else { + sizes = [1,0]; + } + + if ((!v.sizes)&&(sizes)) { + v.sizes = sizes; + } + + if (v.sizes) { + if (!utils.sameSizes(v.sizes, sizes)) return ctx.throwError(ast, "Redefined a signal with different sized"); + } + return ast.refId; } @@ -414,12 +440,13 @@ function genVariable(ctx, ast) { vOffset = genGetSignalOffset(ctx, -1, ast.name); } - const resRef = newRef(ctx, "BIGINT", "_sigValue"); + const sizes = v.sizes.slice(l); + + const resRef = newRef(ctx, "BIGINT", "_sigValue", null, sizes); const res = ctx.refs[resRef]; instantiateRef(ctx, resRef); - ctx.codeBuilder.getSignal(res.label, ["CC"], toRefA_Int1(ctx, ast, vOffset)); + ctx.codeBuilder.getSignal(res.label, ["CC"], toRefA_Int1(ctx, ast, vOffset), sizes[0]); return resRef; - } else if (v.type == "BIGINT") { const refOffset = genGetOffset(ctx, 0, v.sizes, ast.selectors ); const offset = ctx.refs[refOffset]; @@ -486,13 +513,18 @@ function genPin(ctx, ast) { vsIdx = genGetSignalOffset(ctx, vcIdx, ast.pin.name); } - const resRef = newRef(ctx, "BIGINT", "_sigValue"); + const l = ast.selectors ? ast.selectors.length : 0; + + const sizes = [1,0]; // TODO, Treat array + + const resRef = newRef(ctx, "BIGINT", "_sigValue", null, sizes); const res = ctx.refs[resRef]; instantiateRef(ctx, resRef); ctx.codeBuilder.getSignal( res.label, toRefA_Int1(ctx, ast.component, vcIdx), - toRefA_Int1(ctx, ast.pin, vsIdx) + toRefA_Int1(ctx, ast.pin, vsIdx), + sizes[0] ); return resRef; } @@ -951,7 +983,7 @@ function genLoop(ctx, ast) { if (ctx.error) return; const cond = ctx.refs[condRef]; - if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array"); + if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "genLoop: Operation cannot be done on an array"); if (cond.used) { inLoop = true; @@ -1028,7 +1060,7 @@ function genIf(ctx, ast) { const condRef = gen(ctx, ast.condition); if (ctx.error) return; const cond = ctx.refs[condRef]; - if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array"); + if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "genIf: Operation cannot be done on an array"); if (cond.used) { let thenCode, elseCode; @@ -1148,7 +1180,12 @@ function genOp(ctx, ast, op, nOps, adjustBool) { valRefs.push(ref); vals.push(v); - if (!utils.sameSizes(v.sizes, [1,0])) return ctx.throwError(ast, "Operation cannot be done on an array"); + if (!utils.sameSizes(v.sizes, [1,0])) { + console.log("xx"); + console.log(i); + console.log(v); + return ctx.throwError(ast, "genOp2: Operation cannot be done on an array"); + } if ( (!v.used) &&( (!utils.isDefined(v.value)) ||(!utils.isDefined(v.value[0])))) @@ -1184,7 +1221,7 @@ function genTerCon(ctx, ast) { const condRef = gen(ctx, ast.values[0]); if (ctx.error) return; const cond = ctx.refs[condRef]; - if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array"); + if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "genTer: Operation cannot be done on an array"); if (cond.used) { let thenCode, elseCode; diff --git a/test/basiccases.js b/test/basiccases.js index 185fc93..c27db00 100644 --- a/test/basiccases.js +++ b/test/basiccases.js @@ -46,7 +46,7 @@ async function doTest(tester, circuit, testVectors) { describe("basic cases", function () { this.timeout(100000); - for (let i=0; i { await doTest(c_tester, basicCases[i].circuit, basicCases[i].tv); }); diff --git a/test/basiccases.json b/test/basiccases.json index 65abf1d..4ac30ef 100644 --- a/test/basiccases.json +++ b/test/basiccases.json @@ -14,6 +14,17 @@ }] ] }, + { + "name": "fnarray", + "circuit": "fnarray.circom", + "tv": [ + [{ + "in": [1, 8, 25] + }, { + "out": [2, 16, 50] + }] + ] + }, { "name": "add", "circuit": "add.circom", diff --git a/test/circuits/fnarray.circom b/test/circuits/fnarray.circom new file mode 100644 index 0000000..4172d0e --- /dev/null +++ b/test/circuits/fnarray.circom @@ -0,0 +1,21 @@ + + + + +function calc(h) { + var res[3]; + for (var i=0; i<3; i++) res[i] = h[i]*2; + return res; +} + +template Test() { + signal input in[3]; + signal output out[3]; + + var cout[3] = calc(in); + for (var i=0; i<3; i++) out[i] <-- cout[i]; + + for (var i=0; i<3; i++) out[i] === in[i]*2; +} + +component main = Test();