Adapted to circom 0.5

This commit is contained in:
Jordi Baylina 2020-03-26 20:16:29 +01:00
parent 762fbe19a4
commit 5adee591bf
No known key found for this signature in database
GPG Key ID: 7480C80C1BE43112
10 changed files with 284 additions and 203 deletions

@ -1,7 +1,7 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2020 0Kims Association <https://0kims.org>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

123
cli.js

@ -27,9 +27,14 @@ const path = require("path");
const zkSnark = require("./index.js");
const {stringifyBigInts, unstringifyBigInts} = require("./src/stringifybigint.js");
const loadR1cs = require("r1csfile").load;
const WitnessCalculatorBuilder = require("circom_runtime").WitnessCalculatorBuilder;
const version = require("./package").version;
const loadSyms = require("./src/loadsyms");
const printR1cs = require("./src/printr1cs");
const argv = require("yargs")
.version(version)
.usage(`snarkjs <command> <options>
@ -41,13 +46,11 @@ setup command
Runs a setup for a circuit generating the proving and the verification key.
-c or --circuit <circuitFile>
-r or --r1cs <r1csFile>
Filename of the compiled circuit file generated by circom.
Filename could have extension .json or .r1cs
Default: circuit.json
Default: circuit.r1cs
--pk or --provingkey <provingKeyFile>
@ -74,11 +77,11 @@ calculate witness command
Calculate the witness of a circuit given an input.
-c or --circuit <circuitFile>
--ws --wasm <wasmFile>
Filename of the compiled circuit file generated by circom.
Default: circuit.json
Default: circuit.r1cs
-i or --input <inputFile>
@ -90,16 +93,12 @@ calculate witness command
{"a": "22", "b": "33"}
-w or --witness
--wt --witness
Output filename with the generated witness.
Default: witness.json
--lo or --logoutput
Output all the Output signals
--lg or --logget
Output GET access to the signals
@ -112,13 +111,15 @@ calculate witness command
Output when a subcomponent is triggered and when finished
--s or --sanitycheck
generate a proof command
========================
snarkjs proof <options>
-w or --witness
--wt or --witness
Input filename used to calculate the proof.
@ -217,11 +218,11 @@ circuit info
Print statistics of a circuit
-c or --circuit <circuitFile>
-r or --r1cs <r1csFile>
Filename of the compiled circuit file generated by circom.
Default: circuit.json
Default: circuit.r1cs
print constraints
=================
@ -230,16 +231,24 @@ print constraints
Print all the constraints of a given circuit
-c or --circuit <circuitFile>
-r or --r1cs <r1csFile>
Filename of the compiled circuit file generated by circom.
Default: circuit.json
Default: circuit.r1cs
-s or --sym <symFile>
Filename of the debuging symbols file generated by circom.
Default: circuit.sym
`)
.alias("c", "circuit")
.alias("r", "r1cs")
.alias("s", "sym")
.alias("pk", "provingkey")
.alias("vk", "verificationkey")
.alias("w", "witness")
.alias("wt", "witness")
.alias("ws", "wasm")
.alias("p", "proof")
.alias("i", "input")
.alias("pub", "public")
@ -258,15 +267,21 @@ print constraints
repo directory at https://github.com/iden3/circom `)
.argv;
const circuitName = (argv.circuit) ? argv.circuit : "circuit.json";
const r1csName = (argv.r1cs) ? argv.r1cs : "circuit.r1cs";
const symName = (argv.sym) ? argv.sym : "circuit.sym";
const provingKeyName = (argv.provingkey) ? argv.provingkey : "proving_key.json";
const verificationKeyName = (argv.verificationkey) ? argv.verificationkey : "verification_key.json";
const inputName = (argv.input) ? argv.input : "input.json";
const wasmName = (argv.wasm) ? argv.wasm : "circuit.wasm";
const witnessName = (argv.witness) ? argv.witness : "witness.json";
const proofName = (argv.proof) ? argv.proof : "proof.json";
const publicName = (argv.public) ? argv.public : "public.json";
const verifierName = (argv.verifier) ? argv.verifier : "verifier.sol";
const protocol = (argv.protocol) ? argv.protocol : "original";
const protocol = (argv.protocol) ? argv.protocol : "groth";
run().then(() => {
process.exit();
});
function p256(n) {
let nstr = n.toString(16);
@ -275,10 +290,10 @@ function p256(n) {
return nstr;
}
async function run() {
try {
if (argv._[0].toUpperCase() == "INFO") {
const cirDef = JSON.parse(fs.readFileSync(circuitName, "utf8"));
const cir = new zkSnark.Circuit(cirDef);
const cir = await loadR1cs(r1csName);
console.log(`# Wires: ${cir.nVars}`);
console.log(`# Constraints: ${cir.nConstraints}`);
@ -287,20 +302,13 @@ try {
console.log(`# Outputs: ${cir.nOutputs}`);
} else if (argv._[0].toUpperCase() == "PRINTCONSTRAINTS") {
const cirDef = JSON.parse(fs.readFileSync(circuitName, "utf8"));
const cir = new zkSnark.Circuit(cirDef);
const cir = await loadR1cs(r1csName, true, true);
cir.printConstraints();
const sym = await loadSyms(symName);
printR1cs(cir, sym);
} else if (argv._[0].toUpperCase() == "SETUP") {
const cirExtension = circuitName.split(".").pop();
let cir;
if (cirExtension == "json"){
const cirDefJSON = JSON.parse(fs.readFileSync(circuitName, "utf8"));
cir = new zkSnark.Circuit(cirDefJSON);
} else if (cirExtension == "r1cs")
cir = zkSnark.parseR1csSync(circuitName);
const cir = await loadR1cs(r1csName, true);
if (!zkSnark[protocol]) throw new Error("Invalid protocol");
const setup = zkSnark[protocol].setup(cir);
@ -309,19 +317,45 @@ try {
fs.writeFileSync(verificationKeyName, JSON.stringify(stringifyBigInts(setup.vk_verifier), null, 1), "utf-8");
process.exit(0);
} else if (argv._[0].toUpperCase() == "CALCULATEWITNESS") {
const cirDef = JSON.parse(fs.readFileSync(circuitName, "utf8"));
const cir = new zkSnark.Circuit(cirDef);
const input = unstringifyBigInts(JSON.parse(fs.readFileSync(inputName, "utf8")));
const wasm = await fs.promises.readFile(wasmName);
const input = unstringifyBigInts(JSON.parse(await fs.promises.readFile(inputName, "utf8")));
const witness = cir.calculateWitness(input, {
logOutput: argv.logoutput,
logSet: argv.logset,
logGet: argv.logget,
logTrigger: argv.logtrigger
});
fs.writeFileSync(witnessName, JSON.stringify(stringifyBigInts(witness), null, 1), "utf-8");
process.exit(0);
let options;
let sym;
if (argv.logset || argv.logget || argv.logtrigger || argv.sanitycheck) {
options = {
sanityCheck: true
};
if (argv.logset) {
if (!sym) sym = await loadSyms(symName);
options.logSetSignal= function(labelIdx, value) {
console.log("SET " + sym.labelIdx2Name[labelIdx] + " <-- " + value.toString());
};
}
if (argv.logget) {
if (!sym) sym = await loadSyms(symName);
options.logGetSignal= function(varIdx, value) {
console.log("GET " + sym.labelIdx2Name[varIdx] + " --> " + value.toString());
};
}
if (argv.logtrigger) {
if (!sym) sym = await loadSyms(symName);
options.logStartComponent= function(cIdx) {
console.log("START: " + sym.componentIdx2Name[cIdx]);
};
options.logFinishComponent= function(cIdx) {
console.log("FINISH: " + sym.componentIdx2Name[cIdx]);
};
}
}
const wc = await WitnessCalculatorBuilder(wasm, options);
const w = await wc.calculateWitness(input);
await fs.promises.writeFile(witnessName, JSON.stringify(stringifyBigInts(w), null, 1));
} else if (argv._[0].toUpperCase() == "PROOF") {
const witness = unstringifyBigInts(JSON.parse(fs.readFileSync(witnessName, "utf8")));
const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync(provingKeyName, "utf8")));
@ -409,6 +443,7 @@ try {
console.log("ERROR: " + err);
process.exit(1);
}
}
function generateVerifier_original(verificationKey) {

@ -42,6 +42,3 @@ exports.unstringifyBigInts = require("./src/stringifybigint.js").unstringifyBigI
const Bn128 = require("./src/bn128.js");
exports.bn128 = new Bn128();
exports.parseR1cs = require("./src/r1cs_parser.js").loadR1cs;
exports.parseR1csSync = require("./src/r1cs_parser.js").loadR1csSynch;

26
package-lock.json generated

@ -155,6 +155,22 @@
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
},
"circom_runtime": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/circom_runtime/-/circom_runtime-0.0.1.tgz",
"integrity": "sha512-8j/UBzGHhCyGfAaxKz2kFFEUwmfbp1iC10js382t3hy4RvCyrqQhssFVUXP3GNGKwu7W9ecdHbyODcg5MgffsA==",
"requires": {
"big-integer": "^1.6.48",
"fnv-plus": "^1.3.1"
},
"dependencies": {
"big-integer": {
"version": "1.6.48",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
"integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w=="
}
}
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@ -491,6 +507,11 @@
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz",
"integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg=="
},
"fnv-plus": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/fnv-plus/-/fnv-plus-1.3.1.tgz",
"integrity": "sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw=="
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -978,6 +999,11 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"r1csfile": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/r1csfile/-/r1csfile-0.0.1.tgz",
"integrity": "sha512-1mUgD7XXpU/EAf4xWiIt1jaQbQuMBDKAiEJ2eZYsN9rHOJtBWZqLYDkAmC4WJhCwK3O3NZKhvRMaNBM5dBpp1Q=="
},
"ramda": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz",

@ -30,9 +30,11 @@
"dependencies": {
"big-integer": "^1.6.43",
"chai": "^4.2.0",
"circom_runtime": "0.0.1",
"escape-string-regexp": "^1.0.5",
"eslint": "^5.16.0",
"keccak": "^2.0.0",
"r1csfile": "0.0.1",
"yargs": "^12.0.5"
},
"devDependencies": {

@ -107,7 +107,7 @@ module.exports = class Circuit {
vs = "";
}
if (vs!="1") {
vs = vs + v.toString();;
vs = vs + v.toString();
}
}

32
src/loadsyms.js Normal file

@ -0,0 +1,32 @@
const fs = require("fs");
module.exports = async function loadSymbols(symFileName) {
const sym = {
labelIdx2Name: [ "one" ],
varIdx2Name: [ "one" ],
componentIdx2Name: []
};
const symsStr = await fs.promises.readFile(symFileName, "utf8");
const lines = symsStr.split("\n");
for (let i=0; i<lines.length; i++) {
const arr = lines[i].split(",");
if (arr.length!=4) continue;
if (sym.varIdx2Name[arr[1]]) {
sym.varIdx2Name[arr[1]] += "|" + arr[3];
} else {
sym.varIdx2Name[arr[1]] = arr[3];
}
sym.labelIdx2Name[arr[0]] = arr[3];
if (!sym.componentIdx2Name[arr[2]]) {
sym.componentIdx2Name[arr[2]] = extractComponent(arr[3]);
}
}
return sym;
function extractComponent(name) {
const arr = name.split(".");
arr.pop(); // Remove the lasr element
return arr.join(".");
}
};

37
src/printr1cs.js Normal file

@ -0,0 +1,37 @@
const bigInt = require("big-integer");
module.exports = function printR1cs(r1cs, syms) {
for (let i=0; i<r1cs.constraints.length; i++) {
printCostraint(r1cs.constraints[i]);
}
function printCostraint(c) {
const lc2str = (lc) => {
let S = "";
for (let k in lc) {
let name = syms[k];
if (name == "one") name = "";
let v = bigInt(lc[k]);
let vs;
if (!v.lesserOrEquals(r1cs.prime.shiftRight(bigInt(1)))) {
v = r1cs.prime.minus(v);
vs = "-"+v.toString();
} else {
if (S!="") {
vs = "+"+v.toString();
} else {
vs = "";
}
if (vs!="1") {
vs = vs + v.toString();
}
}
S= S + " " + vs + name;
}
return S;
};
const S = `[ ${lc2str(c[0])} ] * [ ${lc2str(c[1])} ] - [ ${lc2str(c[2])} ] = 0`;
console.log(S);
}
};

@ -147,7 +147,7 @@ async function loadR1cs(fileName) {
}
}
function loadR1csSync(fileName) {
async function loadR1cs(fileName) {
const res = {};
const fd = fs.openSync(fileName, "r");
@ -167,11 +167,9 @@ function loadR1csSync(fileName) {
let pConstraints;
let headerSize;
let constraintsSize;
let pMap;
let mapSize;
for (let i=0; i<nSections; i++) {
let ht = readU32();
let hl = readDouble64();
let hl = readU64();
if (ht == 1) {
if (typeof pHeader != "undefined") assert(false, "File has two headder sections");
pHeader = p;
@ -180,17 +178,26 @@ function loadR1csSync(fileName) {
if (typeof pConstraints != "undefined") assert(false, "File has two constraints sections");
pConstraints = p;
constraintsSize = hl;
} else if (ht==3) {
pMap = p;
mapSize = hl;
}
p += hl;
}
if (typeof pHeader == "undefined") assert(false, "File has two header");
if (typeof pHeader == "undefined") assert(false, "File has no header");
// Read Header
p = pHeader;
const n8 = await readU32();
res.prime = await readBigInt();
res.nWires = await readU32();
res.nPubOuts = await readU32();
res.nPubIns = await readU32();
res.nPrvIns = await readU32();
res.nLabels = await readU64();
res.nConstraints = await readU32();
const fieldDefSize = readU32();
const pFieldDef = p;

@ -1,55 +0,0 @@
const chai = require("chai");
const fs = require("fs");
const path = require("path");
const lodash = require("lodash");
const zkSnark = require("../index.js");
const assert = chai.assert;
describe("R1CS", () => {
it("parser", () => {
// Load circuit with .json file
const cirDefJSON = JSON.parse(fs.readFileSync(path.join(__dirname, "r1cs", "circuit.json"), "utf8"));
const cirJSON = new zkSnark.Circuit(cirDefJSON);
// Load circuit with .r1cs file (async)
zkSnark.parseR1cs(path.join(__dirname, "r1cs", "circuit.r1cs"))
.then( cirDefR1cs => {
assert(cirJSON.nVars == cirDefR1cs.nVars);
assert(cirJSON.nPubInputs == cirDefR1cs.nPubInputs);
assert(cirJSON.nOutputs == cirDefR1cs.nOutputs);
assert(cirJSON.constraints.length == cirDefR1cs.nConstraints);
for (let i = 0; i < cirDefR1cs.nConstraints; i++){
const constraintJSON = cirJSON.constraints[i];
const constraintR1CS = cirDefR1cs.constraints[i];
assert(constraintJSON.length, constraintR1CS.length);
for (let j = 0; j < constraintJSON.length; j++)
assert(lodash.isEqual(constraintJSON[j], constraintR1CS[j]));
}
});
});
it("check setup", () => {
// load JSON circuit
const cirDef = JSON.parse(fs.readFileSync(path.join(__dirname, "r1cs", "circuit.json"), "utf8"));
const cir = new zkSnark.Circuit(cirDef);
// load .r1cs circuit (sync)
const cirDefR1cs = zkSnark.parseR1csSync(path.join(__dirname, "r1cs", "circuit.r1cs"));
// calculate prover and verifier from R1CS circuit
const setupR1cs = zkSnark["groth"].setup(cirDefR1cs);
// calculate witness from regular circuit
const witness = cir.calculateWitness({"a": "1", "b": "1", "c": "5", "d": "5", "e": "1", "f": "25"});
// generate proof
const { proof, publicSignals } = zkSnark["groth"].genProof(setupR1cs.vk_proof, witness);
// check proof
const isValid = zkSnark["groth"].isValid(setupR1cs.vk_verifier, proof, publicSignals);
assert(isValid);
});
});