UNPKG

circom-helper

Version:

A convenient way for developers to compile, cache, and execute circom circuits, as well as to generate proofs.

339 lines 16.6 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; exports.__esModule = true; exports.callGetSignalByName = exports.callGenWitness = exports.run = void 0; var betterSqlite3 = require('better-sqlite3'); var lineByLine = require('n-readlines'); var argparse = __importStar(require("argparse")); var fs = __importStar(require("fs")); var path = __importStar(require("path")); var shelljs = __importStar(require("shelljs")); var server_1 = require("./server"); var utils_1 = require("./utils"); exports.callGenWitness = utils_1.genWitness; exports.callGetSignalByName = utils_1.getSignalByName; var read_num_inputs_1 = require("./read_num_inputs"); var CIRCOM_FILE_EXTENSION = '.circom'; var CACHE_DIRNAME = 'cache'; var version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString()).version; var compile = function (circomPath, filepath, buildDir, noClobber, quiet, skip) { if (skip === void 0) { skip = false; } var log = function (s) { if (!quiet) { console.log(s); } }; var filename = path.basename(filepath); var withoutExtension = filename.slice(0, filename.length - CIRCOM_FILE_EXTENSION.length); var r1csFilepath = path.join(path.resolve(buildDir), withoutExtension + '.r1cs'); var cFilepath = path.join(path.resolve(buildDir), withoutExtension + '_cpp'); var symFilepath = path.join(path.resolve(buildDir), withoutExtension + '.sym'); var witnessGenFilepath = path.join(path.resolve(buildDir), withoutExtension); var buildDirContents = fs.readdirSync(buildDir); if (noClobber) { var skip_1 = true; for (var _i = 0, _a = [ r1csFilepath, cFilepath, symFilepath, witnessGenFilepath, ]; _i < _a.length; _i++) { var f = _a[_i]; if (buildDirContents.indexOf(path.basename(f)) === -1) { skip_1 = false; break; } } if (skip_1) { log("Skipping " + filepath); return { r1csFilepath: r1csFilepath, symFilepath: symFilepath, witnessGenFilepath: witnessGenFilepath }; } } if (!skip) { log("Compiling " + filepath); var cmd = circomPath + " --O0 --r1cs --c --sym -o " + path.resolve(buildDir) + " " + filepath; console.log(cmd); var compileOut = shelljs.exec(cmd, { silent: true }); if (compileOut.stderr || compileOut.code === 1) { console.error(compileOut.stderr); throw new Error('Could not compile ' + circomPath); } var srcs = path.join(path.resolve(buildDir), 'main.cpp') + ' ' + path.join(path.resolve(buildDir), 'calcwit.cpp') + ' ' + path.join(path.resolve(buildDir), 'utils.cpp') + ' ' + path.join(path.resolve(buildDir), 'fr.cpp') + ' ' + path.join(path.resolve(buildDir), 'fr.o'); cmd = "g++ -pthread " + srcs + " " + (cFilepath + " -o " + witnessGenFilepath + " ") + "-lgmp -std=c++11 -O3 -fopenmp -DSANITY_CHECK"; shelljs.cd(path.join(path.resolve(buildDir), withoutExtension + '_cpp')); var makeCmdOut = shelljs.exec('make', { silent: true }); if (makeCmdOut.stderr) { console.error('Error running the make command. Please check if all ' + 'dependencies are present.'); console.error(cmd); console.error(makeCmdOut.stderr); } shelljs.mv(path.join(path.resolve(buildDir), withoutExtension + '_cpp', withoutExtension), path.resolve(buildDir)); shelljs.mv(path.join(path.resolve(buildDir), withoutExtension + '_cpp', withoutExtension + '.dat'), path.resolve(buildDir)); } return { r1csFilepath: r1csFilepath, symFilepath: symFilepath, witnessGenFilepath: witnessGenFilepath }; }; var run = function (circomPath, snarkjsPath, circuitDirs, buildDir, port, noClobber, quiet, compileOnly, skipAll) { if (compileOnly === void 0) { compileOnly = false; } if (skipAll === void 0) { skipAll = false; } return __awaiter(void 0, void 0, void 0, function () { var log, filesToCompile, d, numInputsPerCircuit, _i, circuitDirs_1, c, _a, _b, file, fp, files, symFilepaths, _c, filesToCompile_1, f, start, filepaths, n, numInputs, bn, circuitName, end, duration, db, stmt, _d, _e, file, circuit, symFile, liner, line, lineNumber, index, vals, idx, name_1, circuitBasename, witnessGeneratorExes, _f, files_1, f, baseName, state; return __generator(this, function (_g) { switch (_g.label) { case 0: log = function (s) { if (!quiet) { console.log(s); } }; filesToCompile = []; d = {}; numInputsPerCircuit = {}; for (_i = 0, circuitDirs_1 = circuitDirs; _i < circuitDirs_1.length; _i++) { c = circuitDirs_1[_i]; for (_a = 0, _b = fs.readdirSync(c); _a < _b.length; _a++) { file = _b[_a]; if (file.endsWith(CIRCOM_FILE_EXTENSION)) { fp = path.join(path.resolve(c), file); filesToCompile.push(fp); if (d[file]) { // Emit an error if there are any filename collisions // e.g. a/test.circom collides with b.circom console.error("Error: there is more than one circuit " + ("file named " + file)); process.exit(1); } else { d[file] = true; } } } } files = []; symFilepaths = []; _c = 0, filesToCompile_1 = filesToCompile; _g.label = 1; case 1: if (!(_c < filesToCompile_1.length)) return [3 /*break*/, 4]; f = filesToCompile_1[_c]; start = Date.now(); filepaths = compile(circomPath, f, buildDir, noClobber, quiet, skipAll); symFilepaths.push(filepaths.symFilepath); return [4 /*yield*/, read_num_inputs_1.readNumInputs(filepaths.r1csFilepath)]; case 2: n = _g.sent(); numInputs = n.numPubInputs + n.numPrivInputs; bn = path.basename(f); circuitName = bn.slice(0, bn.length - 7); numInputsPerCircuit[circuitName] = numInputs; end = Date.now(); duration = Math.round((end - start) / 1000); log("Took " + duration + " seconds"); files.push(filepaths); _g.label = 3; case 3: _c++; return [3 /*break*/, 1]; case 4: // Iterate through the .sym files and save signals in a DB console.log('Caching circuit symbols and indices...'); db = new betterSqlite3(':memory:'); db.exec('CREATE TABLE symbols (circuit TEXT, idx INTEGER, name TEXT)'); stmt = db.prepare('INSERT INTO symbols (circuit, idx, name) VALUES (?, ?, ?)'); for (_d = 0, _e = fs.readdirSync(buildDir); _d < _e.length; _d++) { file = _e[_d]; if (file.endsWith('.sym')) { circuit = file.slice(0, file.length - '.sym'.length); symFile = path.join(buildDir, file); if (symFilepaths.indexOf(symFile) === -1) { continue; } liner = new lineByLine(symFile); line = void 0; lineNumber = 0; index = void 0; while (line = liner.next()) { line = line.toString(); vals = line.split(','); if (vals.length > 2) { idx = Number(vals[1]); name_1 = vals[3]; stmt.run(circuit, idx, name_1); } } } } if (compileOnly) { return [2 /*return*/]; } circuitBasename = function (f) { var l = f.r1csFilepath.length; return path.basename(f.r1csFilepath.slice(0, l - '.r1cs'.length)); }; witnessGeneratorExes = {}; for (_f = 0, files_1 = files; _f < files_1.length; _f++) { f = files_1[_f]; baseName = circuitBasename(f); witnessGeneratorExes[baseName] = baseName; } // Launch the server log("Launched JSON-RPC server at port " + port); state = { snarkjsPath: snarkjsPath, numInputsPerCircuit: numInputsPerCircuit, buildDir: buildDir, witnessGeneratorExes: witnessGeneratorExes, db: db }; return [2 /*return*/, server_1.launchServer(port, state)]; } }); }); }; exports.run = run; var main = function () { return __awaiter(void 0, void 0, void 0, function () { var parser, args, port, configFilepath, config, buildDirPath, resolveCircuitDirpath, circomPath, snarkjsPath; return __generator(this, function (_a) { parser = new argparse.ArgumentParser({ description: 'A convenient way for developers to compile, cache, ' + 'and execute circom circuits, as well as to generate proofs.' }); parser.add_argument('-v', '--version', { action: 'version', version: version }); parser.add_argument('-c', '--config', { required: true, action: 'store', type: String, help: 'The circom-helper config file' }); parser.add_argument('-nc', '--no-clobber', { required: false, action: 'store_true', help: 'Skip compilation if compiled files exist' }); parser.add_argument('-q', '--quiet', { required: false, action: 'store_true', help: 'Do not display logs' }); parser.add_argument('-b', '--build_dir', { required: true, action: 'store', type: String, help: 'The output directory for compiled files' }); parser.add_argument('-y', '--compile-only', { required: false, action: 'store_true', help: 'Only compile circuits and do not launch the JSON-RPC server' }); parser.add_argument('-k', '--skip-all', { required: false, action: 'store_true', help: 'Launch the JSON-RPC server without compilation or checking if any circuits exist' }); parser.add_argument('-p', '--port', { required: true, action: 'store', type: Number, help: 'The desired port number' }); args = parser.parse_args(); port = args.port; configFilepath = path.resolve(args.config); if (!fs.existsSync(configFilepath)) { console.error('Error: no such config file'); process.exit(1); } config = JSON.parse(fs.readFileSync(configFilepath).toString()); if (config.circuitDirs == null) { console.error('Error: circuitDirs not specified in the config file'); process.exit(1); } buildDirPath = path.resolve(args.build_dir); fs.mkdirSync(buildDirPath, { recursive: true }); resolveCircuitDirpath = function (c) { var baseDir = path.dirname(configFilepath); return path.join(baseDir, c); }; circomPath = path.join(path.dirname(configFilepath), config.circom); snarkjsPath = path.join(path.dirname(configFilepath), config.snarkjs); run(circomPath, snarkjsPath, config.circuitDirs.map(resolveCircuitDirpath), buildDirPath, port, args.no_clobber, args.quiet, args.compile_only, args.skip_all); return [2 /*return*/]; }); }); }; if (require.main === module) { main(); } //# sourceMappingURL=index.js.map