diff --git a/c/tester.js b/c/tester.js new file mode 100644 index 0000000..fbca5aa --- /dev/null +++ b/c/tester.js @@ -0,0 +1,239 @@ +const chai = require("chai"); +const assert = chai.assert; + +const fs = require("fs"); +var tmp = require("tmp-promise"); +const path = require("path"); + +const util = require("util"); +const exec = util.promisify(require("child_process").exec); + +const loadR1cs = require("r1csfile").load; +const ZqField = require("ffjavascript").ZqField; + +module.exports = wasm_tester; + +async function wasm_tester(circomInput, _options) { + + assert(await compiler_above_version("2.0.0"),"Wrong compiler version. Must be at least 2.0.0"); + + tmp.setGracefulCleanup(); + + const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true }); + + // console.log(dir.path); + + const baseName = path.basename(circomInput, ".circom"); + const options = Object.assign({}, _options); + + options.wasm = true; + + options.sym = true; + options.json = options.json || false; // costraints in json format + //options.r1cs = options.r1cs || false; // costraints in r1cs format + //if (!options.json) options.r1cs = true; // r1cs if not json + options.r1cs = true; + options.output = dir.path; + + await compile(baseName, circomInput, options); + + return new WasmTester(dir, baseName, run); +} + +async function compile (baseName, fileName, options) { + var flags = "--c "; + if (options.sym) flags += "--sym "; + if (options.r1cs) flags += "--r1cs "; + if (options.json) flags += "--json "; + if (options.output) flags += "--output " + options.output + " "; + + b = await exec("circom " + flags + fileName); + assert(b.stderr == "", + "circom compiler error \n" + b.stderr); + + const c_folder = path.join(options.output, baseName+"_cpp/") + b = await exec("make -C "+c_folder); + assert(b.stderr == "", + "error building the executable C program\n" + b.stderr); +} + +class WasmTester { + + constructor(dir, baseName, witnessCalculator) { + this.dir=dir; + this.baseName = baseName; + this.witnessCalculator = witnessCalculator; + } + + async release() { + await this.dir.cleanup(); + } + + async calculateWitness(input) { + const inputjson = JSON.stringify(input); + const inputFile = path.join(this.dir.path, this.baseName+"_cpp/" + this.baseName + ".json"); + const wtnsFile = path.join(this.dir.path, this.baseName+"_cpp/" + this.baseName + ".wtns"); + const runc = path.join(this.dir.path, this.baseName+"_cpp/" + this.baseName); + fs.writeFile(inputFile, inputjson, function(err) { + if (err) throw err; + }); + await exec("ls " + path.join(this.dir.path, this.baseName+"_cpp/")); + await exec(runc + " " + inputFile + " " + wtnsFile); + return readBinaryFile(wtnsFile); + } + + 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 ${v}`); + } + return lines.join("\n"); + } + + async checkConstraints(witness) { + const self = this; + if (!self.constraints) await self.loadConstraints(); + for (let i=0; i v2[i]) return true; + if (v1[i] < v2[i]) return false; + } + return true; +} + +async function compiler_above_version(v) { + let output = await exec('circom --version').toString(); + let compiler_version = version_to_list(output.slice(output.search(/\d/),-1)); + vlist = version_to_list(v); + return check_versions ( compiler_version, vlist ); +} + +function readBinaryFile(fileName) { + const buff = fs.readFileSync(fileName); + const n32 = fromArray8ToUint(buff.slice(24,27)); + var pos = 28+n32; + const ws = fromArray8ToUint(buff.slice(pos,pos+3)); + pos += 16; + const w = []; + for (let i=0; i=0; i--) { + res = res*radix + BigInt(arr[i]); + } + return res; +} + +function fromArray8ToUint(arr) { //returns a BigInt + var res = 0; + const radix = 8; + for (let i = arr.length-1 ; i>=0; i--) { + res = res*radix + arr[i]; + } + return res; +} diff --git a/index.js b/index.js index 3199ce4..6eca9cc 100644 --- a/index.js +++ b/index.js @@ -1,2 +1,2 @@ exports.wasm = require("./wasm/tester"); -//exports.c = require("./c/tester"); +exports.c = require("./c/tester"); diff --git a/test/multiplier2.js b/test/multiplier2.js index 2c57141..62413c2 100644 --- a/test/multiplier2.js +++ b/test/multiplier2.js @@ -1,6 +1,7 @@ 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; @@ -12,11 +13,17 @@ const assert = chai.assert; describe("Simple test", function () { this.timeout(100000); - it("Checking the compilation of simple circuit", async () => { + it("Checking the compilation of a simple circuit generating wasm", async () => { const circuit = await wasm_tester(path.join(__dirname, "Multiplier2.circom")); const w = await circuit.calculateWitness({a: 2, b: 4}); + await circuit.checkConstraints(w); + }); + it("Checking the compilation of a simple circuit generating C", async () => { + + const circuit = await c_tester(path.join(__dirname, "Multiplier2.circom")); + const w = await circuit.calculateWitness({a: 2, b: 4}); await circuit.checkConstraints(w); });