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
JavaScript
;
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