From fcbc5a096d2b5a91b4d35192aed00a253c9baf8a Mon Sep 17 00:00:00 2001 From: Vieloooo Date: Fri, 10 Nov 2023 14:26:17 +0800 Subject: [PATCH 1/6] feature: add getOutput function for wasm tester fix wasm_tester getDecoratedOutput "utils not found" bug --- .gitignore | 6 ++++- test/m2.circom | 14 ++++++++++++ test/test_output.js | 26 ++++++++++++++++++++++ wasm/tester.js | 53 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 test/m2.circom create mode 100644 test/test_output.js diff --git a/.gitignore b/.gitignore index 50d8b98..fa410ff 100644 --- a/.gitignore +++ b/.gitignore @@ -62,4 +62,8 @@ typings/ tmp -.DS_Store \ No newline at end of file +.DS_Store + +package-lock.json + +*.r1cs diff --git a/test/m2.circom b/test/m2.circom new file mode 100644 index 0000000..dd75cb1 --- /dev/null +++ b/test/m2.circom @@ -0,0 +1,14 @@ +pragma circom 2.0.0; + +template Multiplier2() { + signal input a; + signal input b; + signal output c; + signal output vec[10]; + c <== a*b; + for (var i = 0; i < 10; i++) { + vec[i] <== a*b; + } +} + +component main = Multiplier2(); diff --git a/test/test_output.js b/test/test_output.js new file mode 100644 index 0000000..07ecbd8 --- /dev/null +++ b/test/test_output.js @@ -0,0 +1,26 @@ +const chai = require("chai"); +const path = require("path"); +const wasm_tester = require("./../index").wasm; +const c_tester = require("./../index").c; + +const F1Field = require("ffjavascript").F1Field; +const Scalar = require("ffjavascript").Scalar; +exports.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); +const Fr = new F1Field(exports.p); + +const assert = chai.assert; + +describe("outputs tester", function () { + this.timeout(100000); + + it("Checking the compilation of a simple circuit generating C in a given folder without recompiling", async function () { + const circuit = await wasm_tester( + path.join(__dirname, "m2.circom"), + ); + const w = await circuit.calculateWitness({a: 6, b: 3}); + const o = await circuit.getOutput(w, ["c", "vec[10]"]); + console.log(o); + console.log("ok"); + }); + +}); diff --git a/wasm/tester.js b/wasm/tester.js index 8e19877..6bd34ec 100644 --- a/wasm/tester.js +++ b/wasm/tester.js @@ -178,7 +178,7 @@ class WasmTester { if (!self.symbols) await self.loadSymbols(); for (let n in self.symbols) { let v; - if (utils.isDefined(witness[self.symbols[n].varIdx])) { + if (witness[self.symbols[n].varIdx] !== undefined) { v = witness[self.symbols[n].varIdx].toString(); } else { v = "undefined"; @@ -187,6 +187,57 @@ class WasmTester { } return lines.join("\n"); } + async getOutput(witness, outputs) { + const outputs_iter = parse(outputs) + const self = this; + if (!self.symbols) await self.loadSymbols(); + // new a dictionary which map [name] to [value] + let res = {}; + for (let n of outputs_iter) { + let v; + // prefix n with "main." + n = "main." + n; + if (witness[self.symbols[n].varIdx] !== undefined) { + v = witness[self.symbols[n].varIdx].toString(); + } else { + assert(false, "Output variable not defined: " + n); + } + // add {name: value} to the dictionary + res[n] = v; + } + // parse "a, b[3]" to "a, b[0], b[1], b[2]" + function parse(inputArray) { + const outputArray = []; + for (const item of inputArray) { + // Check if the item matches the pattern with brackets and a number + const match = item.match(/^(.+)\[(\d+)\]$/); + + if (match) { + // If there's a match, expand the item + const base = match[1]; // The base string (e.g., "b" or "d") + const count = parseInt(match[2], 10); // The number inside the brackets + + for (let i = 0; i < count; i++) { + // Check for nested brackets + if (base.includes("[")) { + // Call the function recursively for nested brackets + const nestedResults = parse([`${base}[${i}]`]); + outputArray.push(...nestedResults); + } else { + // Otherwise, just add the expanded items to the output array + outputArray.push(`${base}[${i}]`); + } + } + } else { + // If there's no match, add the item as it is a singla varialbe + outputArray.push(item); + } + } + + return outputArray; + } + return res; + } async checkConstraints(witness) { const self = this; From 53fe19e9e4d514f70de0b6fcb0fc64369259a1e0 Mon Sep 17 00:00:00 2001 From: Vieloooo Date: Fri, 10 Nov 2023 14:44:20 +0800 Subject: [PATCH 2/6] fix: fix the duplicated code in parse --- wasm/tester.js | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/wasm/tester.js b/wasm/tester.js index 6bd34ec..da7f2d2 100644 --- a/wasm/tester.js +++ b/wasm/tester.js @@ -211,26 +211,23 @@ class WasmTester { for (const item of inputArray) { // Check if the item matches the pattern with brackets and a number const match = item.match(/^(.+)\[(\d+)\]$/); - if (match) { - // If there's a match, expand the item - const base = match[1]; // The base string (e.g., "b" or "d") - const count = parseInt(match[2], 10); // The number inside the brackets - - for (let i = 0; i < count; i++) { - // Check for nested brackets - if (base.includes("[")) { - // Call the function recursively for nested brackets - const nestedResults = parse([`${base}[${i}]`]); - outputArray.push(...nestedResults); - } else { - // Otherwise, just add the expanded items to the output array - outputArray.push(`${base}[${i}]`); + // If there's a match, expand the item + const base = match[1]; // The base string (e.g., "b" or "d") + const count = parseInt(match[2], 10); // The number inside the brackets + // Range all the element in the array, then add the expanded items to the output array + for (let i = 0; i < count; i++) { + // Check for nested brackets + if (base.includes("[")) { + assert(false, "Multi-dimension array is not supported"); + } else { + // Otherwise, just add the expanded items to the output array + outputArray.push(`${base}[${i}]`); + } } - } } else { - // If there's no match, add the item as it is a singla varialbe - outputArray.push(item); + // If there's no match, add the item as it is a singla varialbe + outputArray.push(item); } } From 55eecd684d95ec82f97740b991e30efdeca5e305 Mon Sep 17 00:00:00 2001 From: Vieloooo Date: Fri, 10 Nov 2023 14:51:50 +0800 Subject: [PATCH 3/6] trim for pr --- test/m2.circom | 14 -------------- test/test_output.js | 26 -------------------------- 2 files changed, 40 deletions(-) delete mode 100644 test/m2.circom delete mode 100644 test/test_output.js diff --git a/test/m2.circom b/test/m2.circom deleted file mode 100644 index dd75cb1..0000000 --- a/test/m2.circom +++ /dev/null @@ -1,14 +0,0 @@ -pragma circom 2.0.0; - -template Multiplier2() { - signal input a; - signal input b; - signal output c; - signal output vec[10]; - c <== a*b; - for (var i = 0; i < 10; i++) { - vec[i] <== a*b; - } -} - -component main = Multiplier2(); diff --git a/test/test_output.js b/test/test_output.js deleted file mode 100644 index 07ecbd8..0000000 --- a/test/test_output.js +++ /dev/null @@ -1,26 +0,0 @@ -const chai = require("chai"); -const path = require("path"); -const wasm_tester = require("./../index").wasm; -const c_tester = require("./../index").c; - -const F1Field = require("ffjavascript").F1Field; -const Scalar = require("ffjavascript").Scalar; -exports.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); -const Fr = new F1Field(exports.p); - -const assert = chai.assert; - -describe("outputs tester", function () { - this.timeout(100000); - - it("Checking the compilation of a simple circuit generating C in a given folder without recompiling", async function () { - const circuit = await wasm_tester( - path.join(__dirname, "m2.circom"), - ); - const w = await circuit.calculateWitness({a: 6, b: 3}); - const o = await circuit.getOutput(w, ["c", "vec[10]"]); - console.log(o); - console.log("ok"); - }); - -}); From b11edf62f3b08f46bf8afdb4b10f5a962fa2d204 Mon Sep 17 00:00:00 2001 From: Vieloooo Date: Tue, 28 Nov 2023 10:02:57 +0800 Subject: [PATCH 4/6] bugfix: make outputs more readable --- wasm/tester.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm/tester.js b/wasm/tester.js index da7f2d2..91f6303 100644 --- a/wasm/tester.js +++ b/wasm/tester.js @@ -196,8 +196,8 @@ class WasmTester { for (let n of outputs_iter) { let v; // prefix n with "main." - n = "main." + n; - if (witness[self.symbols[n].varIdx] !== undefined) { + let tmp_n = "main." + n; + if (witness[self.symbols[tmp_n].varIdx] !== undefined) { v = witness[self.symbols[n].varIdx].toString(); } else { assert(false, "Output variable not defined: " + n); From 48ee65b049ef7a643c743f4705b043578f00501f Mon Sep 17 00:00:00 2001 From: Vieloooo Date: Thu, 30 Nov 2023 21:30:58 +0800 Subject: [PATCH 5/6] fix: fix an important typo in `getOuput` --- wasm/tester.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wasm/tester.js b/wasm/tester.js index 91f6303..dea34d1 100644 --- a/wasm/tester.js +++ b/wasm/tester.js @@ -7,6 +7,7 @@ const path = require("path"); const util = require("util"); const {F1Field} = require("ffjavascript"); +const { c } = require(".."); const exec = util.promisify(require("child_process").exec); const readR1cs = require("r1csfile").readR1cs; @@ -188,7 +189,7 @@ class WasmTester { return lines.join("\n"); } async getOutput(witness, outputs) { - const outputs_iter = parse(outputs) + const outputs_iter = parse(outputs); const self = this; if (!self.symbols) await self.loadSymbols(); // new a dictionary which map [name] to [value] @@ -198,7 +199,7 @@ class WasmTester { // prefix n with "main." let tmp_n = "main." + n; if (witness[self.symbols[tmp_n].varIdx] !== undefined) { - v = witness[self.symbols[n].varIdx].toString(); + v = witness[self.symbols[tmp_n].varIdx].toString(); } else { assert(false, "Output variable not defined: " + n); } From cf7d7a60bddcffc2388f0acaf44f38bb00d6f2f6 Mon Sep 17 00:00:00 2001 From: Vieloooo Date: Thu, 30 Nov 2023 21:59:15 +0800 Subject: [PATCH 6/6] feature: add getOuput for c_tester --- c/tester.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/c/tester.js b/c/tester.js index 99c6269..0c39dfc 100644 --- a/c/tester.js +++ b/c/tester.js @@ -206,6 +206,54 @@ class CTester { } return lines.join("\n"); } + async getOutput(witness, outputs) { + const outputs_iter = parse(outputs); + const self = this; + if (!self.symbols) await self.loadSymbols(); + // new a dictionary which map [name] to [value] + let res = {}; + for (let n of outputs_iter) { + let v; + // prefix n with "main." + let tmp_n = "main." + n; + if (witness[self.symbols[tmp_n].varIdx] !== undefined) { + v = witness[self.symbols[tmp_n].varIdx].toString(); + } else { + assert(false, "Output variable not defined: " + n); + } + // add {name: value} to the dictionary + res[n] = v; + } + // parse "a, b[3]" to "a, b[0], b[1], b[2]" + function parse(inputArray) { + const outputArray = []; + for (const item of inputArray) { + // Check if the item matches the pattern with brackets and a number + const match = item.match(/^(.+)\[(\d+)\]$/); + if (match) { + // If there's a match, expand the item + const base = match[1]; // The base string (e.g., "b" or "d") + const count = parseInt(match[2], 10); // The number inside the brackets + // Range all the element in the array, then add the expanded items to the output array + for (let i = 0; i < count; i++) { + // Check for nested brackets + if (base.includes("[")) { + assert(false, "Multi-dimension array is not supported"); + } else { + // Otherwise, just add the expanded items to the output array + outputArray.push(`${base}[${i}]`); + } + } + } else { + // If there's no match, add the item as it is a singla varialbe + outputArray.push(item); + } + } + + return outputArray; + } + return res; + } async checkConstraints(witness) { const self = this;