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 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007 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 Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.

127
cli.js

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

@ -42,6 +42,3 @@ exports.unstringifyBigInts = require("./src/stringifybigint.js").unstringifyBigI
const Bn128 = require("./src/bn128.js"); const Bn128 = require("./src/bn128.js");
exports.bn128 = new Bn128(); 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", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" "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": { "cli-cursor": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "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", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz",
"integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==" "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": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "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", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" "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": { "ramda": {
"version": "0.26.1", "version": "0.26.1",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz",

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

@ -107,7 +107,7 @@ module.exports = class Circuit {
vs = ""; vs = "";
} }
if (vs!="1") { 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 res = {};
const fd = fs.openSync(fileName, "r"); const fd = fs.openSync(fileName, "r");
@ -167,11 +167,9 @@ function loadR1csSync(fileName) {
let pConstraints; let pConstraints;
let headerSize; let headerSize;
let constraintsSize; let constraintsSize;
let pMap;
let mapSize;
for (let i=0; i<nSections; i++) { for (let i=0; i<nSections; i++) {
let ht = readU32(); let ht = readU32();
let hl = readDouble64(); let hl = readU64();
if (ht == 1) { if (ht == 1) {
if (typeof pHeader != "undefined") assert(false, "File has two headder sections"); if (typeof pHeader != "undefined") assert(false, "File has two headder sections");
pHeader = p; pHeader = p;
@ -180,17 +178,26 @@ function loadR1csSync(fileName) {
if (typeof pConstraints != "undefined") assert(false, "File has two constraints sections"); if (typeof pConstraints != "undefined") assert(false, "File has two constraints sections");
pConstraints = p; pConstraints = p;
constraintsSize = hl; constraintsSize = hl;
} else if (ht==3) {
pMap = p;
mapSize = hl;
} }
p += 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 // Read Header
p = pHeader; 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 fieldDefSize = readU32();
const pFieldDef = p; 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);
});
});