diff --git a/package-lock.json b/package-lock.json index 50afb55..a6486b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "ffjavascript": "^0.2.38", "fnv-plus": "^1.3.1", "r1csfile": "0.0.16", - "tmp-promise": "^3.0.2", + "tmp-promise": "^3.0.3", "util": "^0.12.4" } }, @@ -711,9 +711,9 @@ } }, "node_modules/tmp-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.2.tgz", - "integrity": "sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", "dependencies": { "tmp": "^0.2.0" } @@ -1302,9 +1302,9 @@ } }, "tmp-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.2.tgz", - "integrity": "sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", "requires": { "tmp": "^0.2.0" } diff --git a/package.json b/package.json index 45c12cd..cd14af0 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "ffjavascript": "^0.2.38", "fnv-plus": "^1.3.1", "r1csfile": "0.0.16", - "tmp-promise": "^3.0.2", + "tmp-promise": "^3.0.3", "util": "^0.12.4" } } diff --git a/scripts/circom-docker.sh b/scripts/circom-docker.sh new file mode 100755 index 0000000..fcf0e62 --- /dev/null +++ b/scripts/circom-docker.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +set -o errexit + +[[ $1 =~ (-h|--help) ]] && { + echo "usage: ${0##*/} " + echo "e.g.: ${0##*/} circuits/hello.circom --r1cs --wasm --sym --c --output compiled/" + exit 1 +} + +path_this=$( cd $(dirname "$0"); pwd -L ) +if [ -L "$0" ]; then + path_this=$(dirname $(readlink ${path_this}/$(basename "$0"))) # path relatively to the symlink file + [[ "$path_this" != /* ]] && path_this=$(cd $(dirname "$0")/${path_this}; pwd -L) +fi + +test -t 1 && USE_TTY="-t" | USE_TTY="" + +docker run -i ${USE_TTY} --rm \ + -v "${path_this}/..":/data \ + -v /etc/passwd:/etc/passwd:ro \ + -v /etc/group:/etc/group:ro \ + --user $(id -u) \ + aspiers/circom circom $@ diff --git a/test/helpers/circomDocker.js b/test/helpers/circomDocker.js new file mode 100644 index 0000000..fe230f5 --- /dev/null +++ b/test/helpers/circomDocker.js @@ -0,0 +1,33 @@ +const { mkdtemp, rmdirSync } = require("fs"); +const { tmpdir, userInfo } = require("os"); +const { join, sep } = require("path"); + +module.exports = { + getOptions, +}; + +async function getOptions(projectRoot = "") { + const basedir = projectRoot || join(__dirname, "../../"); + const options = { + basedir, + }; + if (!!process.env.CIRCOM_DOCKER) { + const tmpDir = await new Promise((res, rej) => + mkdtemp(`${tmpdir()}${sep}`, (err, folder) => + err ? rej(err) : res(folder) + ) + ); + process.on("exit", () => rmdirSync(tmpDir, { recursive: true })); + options.tmpdir = tmpDir; + + options.compiler = `docker run -i --rm -v ${basedir}:/data -v ${tmpDir}:${tmpDir} `; + const { uid } = userInfo(); + if (!!uid) { + options.compiler += `-v /etc/passwd:/etc/passwd:ro `; + options.compiler += `-v /etc/group:/etc/group:ro `; + options.compiler += `--user ${uid} `; + } + options.compiler += "aspiers/circom circom"; + } + return Promise.resolve(options); +} diff --git a/test/multiplier2.js b/test/multiplier2.js index 62413c2..87bbc4b 100644 --- a/test/multiplier2.js +++ b/test/multiplier2.js @@ -15,7 +15,9 @@ describe("Simple test", function () { it("Checking the compilation of a simple circuit generating wasm", async () => { - const circuit = await wasm_tester(path.join(__dirname, "Multiplier2.circom")); + const input = path.join(__dirname, "Multiplier2.circom"); + const opts = await getOptions(__dirname); + const circuit = await wasm_tester(input, opts); const w = await circuit.calculateWitness({a: 2, b: 4}); await circuit.checkConstraints(w); }); diff --git a/wasm/tester.js b/wasm/tester.js index 8a41daf..9f51795 100644 --- a/wasm/tester.js +++ b/wasm/tester.js @@ -5,8 +5,7 @@ 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 { exec } = require("child_process"); const loadR1cs = require("r1csfile").load; const ZqField = require("ffjavascript").ZqField; @@ -15,27 +14,30 @@ 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 options = Object.assign( + // default + { compiler: "circom", json: false }, + // user-defined + _options, + // may not be redefined by user + { wasm: true, sym: true, r1cs: true, circomVersion: "2.0.0" }, + ); - const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true }); + assert(await compiler_above_version(options), "Wrong compiler version. Must be at least 2.0.0"); - //console.log(dir.path); + tmp.setGracefulCleanup(); - const baseName = path.basename(circomInput, ".circom"); - const options = Object.assign({}, _options); + const dir = await tmp.dir({prefix: "circom_", unsafeCleanup: true, tmpdir: options.tmpdir}); - options.wasm = true; + options.input = !!options.basedir ? path.relative(options.basedir, circomInput) : circomInput; + options.output = !!options.basedir ? path.relative(options.basedir, dir.path) : dir.path; + + //console.log(dir.path); - options.sym = true; - options.json = options.json || false; // costraints in json format - options.r1cs = true; - options.output = dir.path; + const baseName = path.basename(options.input, ".circom"); - await compile(circomInput, options); + await compile(options); - const utils = require("./utils"); const WitnessCalculator = require("./witness_calculator"); const wasm = await fs.promises.readFile(path.join(dir.path, baseName+"_js/"+ baseName + ".wasm")); @@ -45,8 +47,8 @@ async function wasm_tester(circomInput, _options) { return new WasmTester(dir, baseName, wc); } -async function compile (fileName, options) { - var flags = "--wasm "; +async function compile (options) { + var flags = " --wasm "; if (options.sym) flags += "--sym "; if (options.r1cs) flags += "--r1cs "; if (options.json) flags += "--json "; @@ -54,11 +56,10 @@ async function compile (fileName, options) { if (options.O === 0) flags += "--O0 " if (options.O === 1) flags += "--O1 " - b = await exec("circom " + flags + fileName); - assert(b.stderr == "", - "circom compiler error \n" + b.stderr); + b = await _exec(options.compiler + flags + options.input); + assert(b.err === null, "circom compiler error \n" + b.stderr || b.stdout); } - + class WasmTester { constructor(dir, baseName, witnessCalculator) { @@ -154,7 +155,7 @@ class WasmTester { } function checkConstraint(constraint) { - + const F = self.F; const a = evalLC(constraint[0]); const b = evalLC(constraint[1]); @@ -179,23 +180,34 @@ class WasmTester { function version_to_list ( v ) { return v.split(".").map(function(x) { - return parseInt(x, 10); + return parseInt(x, 10); }); } function check_versions ( v1, v2 ) { //check if v1 is newer than or equal to v2 for (let i = 0; i < v2.length; i++) { - if (v1[i] > v2[i]) return true; - if (v1[i] < v2[i]) return false; + if (v1[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(); +async function compiler_above_version(options) { + let output = (await _exec(options.compiler + ' --version')).stdout.toString(); let compiler_version = version_to_list(output.slice(output.search(/\d/),-1)); - vlist = version_to_list(v); - return check_versions ( compiler_version, vlist ); + vlist = version_to_list(options.circomVersion); + return check_versions(compiler_version, vlist); } +async function _exec(cmd) { + return new Promise((resolve) => { + const res = {}; + exec(cmd, (err, stdout, stderr) => { + res.err = err; + res.stdout = stdout; + res.stderr = stderr; + resolve(res); + }); + }); +}