diff --git a/COPYING b/COPYING
index 9cecc1d..9dc9bca 100644
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,7 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2020 0Kims Association
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
diff --git a/cli.js b/cli.js
index 2beb0a2..f55c428 100755
--- a/cli.js
+++ b/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
@@ -41,13 +46,11 @@ setup command
Runs a setup for a circuit generating the proving and the verification key.
- -c or --circuit
+ -r or --r1cs
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
@@ -74,11 +77,11 @@ calculate witness command
Calculate the witness of a circuit given an input.
- -c or --circuit
+ --ws --wasm
Filename of the compiled circuit file generated by circom.
- Default: circuit.json
+ Default: circuit.r1cs
-i or --input
@@ -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
- -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
+ -r or --r1cs
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
+ -r or --r1cs
Filename of the compiled circuit file generated by circom.
- Default: circuit.json
+ Default: circuit.r1cs
+
+ -s or --sym
+
+ 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,139 +290,159 @@ function p256(n) {
return nstr;
}
-try {
- if (argv._[0].toUpperCase() == "INFO") {
- const cirDef = JSON.parse(fs.readFileSync(circuitName, "utf8"));
- const cir = new zkSnark.Circuit(cirDef);
+async function run() {
+ try {
+ if (argv._[0].toUpperCase() == "INFO") {
+ const cir = await loadR1cs(r1csName);
- console.log(`# Wires: ${cir.nVars}`);
- console.log(`# Constraints: ${cir.nConstraints}`);
- console.log(`# Private Inputs: ${cir.nPrvInputs}`);
- console.log(`# Public Inputs: ${cir.nPubInputs}`);
- console.log(`# Outputs: ${cir.nOutputs}`);
+ console.log(`# Wires: ${cir.nVars}`);
+ console.log(`# Constraints: ${cir.nConstraints}`);
+ console.log(`# Private Inputs: ${cir.nPrvInputs}`);
+ console.log(`# Public Inputs: ${cir.nPubInputs}`);
+ 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);
+ } else if (argv._[0].toUpperCase() == "PRINTCONSTRAINTS") {
+ const cir = await loadR1cs(r1csName, true, true);
- cir.printConstraints();
+ const sym = await loadSyms(symName);
- } else if (argv._[0].toUpperCase() == "SETUP") {
- const cirExtension = circuitName.split(".").pop();
+ printR1cs(cir, sym);
+ } else if (argv._[0].toUpperCase() == "SETUP") {
+ 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");
+ const setup = zkSnark[protocol].setup(cir);
- if (!zkSnark[protocol]) throw new Error("Invalid protocol");
- const setup = zkSnark[protocol].setup(cir);
+ fs.writeFileSync(provingKeyName, JSON.stringify(stringifyBigInts(setup.vk_proof), null, 1), "utf-8");
+ fs.writeFileSync(verificationKeyName, JSON.stringify(stringifyBigInts(setup.vk_verifier), null, 1), "utf-8");
+ process.exit(0);
+ } else if (argv._[0].toUpperCase() == "CALCULATEWITNESS") {
+ const wasm = await fs.promises.readFile(wasmName);
+ const input = unstringifyBigInts(JSON.parse(await fs.promises.readFile(inputName, "utf8")));
- fs.writeFileSync(provingKeyName, JSON.stringify(stringifyBigInts(setup.vk_proof), null, 1), "utf-8");
- 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 witness = cir.calculateWitness(input, {
- logOutput: argv.logoutput,
- logSet: argv.logset,
- logGet: argv.logget,
- logTrigger: argv.logtrigger
- });
+ 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]);
+ };
+ }
+ }
- fs.writeFileSync(witnessName, JSON.stringify(stringifyBigInts(witness), null, 1), "utf-8");
- process.exit(0);
- } else if (argv._[0].toUpperCase() == "PROOF") {
- const witness = unstringifyBigInts(JSON.parse(fs.readFileSync(witnessName, "utf8")));
- const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync(provingKeyName, "utf8")));
+ const wc = await WitnessCalculatorBuilder(wasm, options);
- const protocol = provingKey.protocol;
- if (!zkSnark[protocol]) throw new Error("Invalid protocol");
- const {proof, publicSignals} = zkSnark[protocol].genProof(provingKey, witness);
+ const w = await wc.calculateWitness(input);
- fs.writeFileSync(proofName, JSON.stringify(stringifyBigInts(proof), null, 1), "utf-8");
- fs.writeFileSync(publicName, JSON.stringify(stringifyBigInts(publicSignals), null, 1), "utf-8");
- process.exit(0);
- } else if (argv._[0].toUpperCase() == "VERIFY") {
- const public = unstringifyBigInts(JSON.parse(fs.readFileSync(publicName, "utf8")));
- const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync(verificationKeyName, "utf8")));
- const proof = unstringifyBigInts(JSON.parse(fs.readFileSync(proofName, "utf8")));
+ await fs.promises.writeFile(witnessName, JSON.stringify(stringifyBigInts(w), null, 1));
- const protocol = verificationKey.protocol;
- if (!zkSnark[protocol]) throw new Error("Invalid protocol");
+ } else if (argv._[0].toUpperCase() == "PROOF") {
+ const witness = unstringifyBigInts(JSON.parse(fs.readFileSync(witnessName, "utf8")));
+ const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync(provingKeyName, "utf8")));
- const isValid = zkSnark[protocol].isValid(verificationKey, proof, public);
+ const protocol = provingKey.protocol;
+ if (!zkSnark[protocol]) throw new Error("Invalid protocol");
+ const {proof, publicSignals} = zkSnark[protocol].genProof(provingKey, witness);
- if (isValid) {
- console.log("OK");
+ fs.writeFileSync(proofName, JSON.stringify(stringifyBigInts(proof), null, 1), "utf-8");
+ fs.writeFileSync(publicName, JSON.stringify(stringifyBigInts(publicSignals), null, 1), "utf-8");
+ process.exit(0);
+ } else if (argv._[0].toUpperCase() == "VERIFY") {
+ const public = unstringifyBigInts(JSON.parse(fs.readFileSync(publicName, "utf8")));
+ const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync(verificationKeyName, "utf8")));
+ const proof = unstringifyBigInts(JSON.parse(fs.readFileSync(proofName, "utf8")));
+
+ const protocol = verificationKey.protocol;
+ if (!zkSnark[protocol]) throw new Error("Invalid protocol");
+
+ const isValid = zkSnark[protocol].isValid(verificationKey, proof, public);
+
+ if (isValid) {
+ console.log("OK");
+ process.exit(0);
+ } else {
+ console.log("INVALID");
+ process.exit(1);
+ }
+ } else if (argv._[0].toUpperCase() == "GENERATEVERIFIER") {
+
+ const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync(verificationKeyName, "utf8")));
+
+ let verifierCode;
+ if (verificationKey.protocol == "original") {
+ verifierCode = generateVerifier_original(verificationKey);
+ } else if (verificationKey.protocol == "groth") {
+ verifierCode = generateVerifier_groth(verificationKey);
+ } else if (verificationKey.protocol == "kimleeoh") {
+ verifierCode = generateVerifier_kimleeoh(verificationKey);
+ } else {
+ throw new Error("InvalidProof");
+ }
+
+ fs.writeFileSync(verifierName, verifierCode, "utf-8");
+ process.exit(0);
+
+ } else if (argv._[0].toUpperCase() == "GENERATECALL") {
+
+ const public = unstringifyBigInts(JSON.parse(fs.readFileSync(publicName, "utf8")));
+ const proof = unstringifyBigInts(JSON.parse(fs.readFileSync(proofName, "utf8")));
+
+ let inputs = "";
+ for (let i=0; i {
+ 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);
+ }
+
+};
diff --git a/src/r1cs_parser.js b/src/r1cs_parser.js
index 719bb2b..8a18d29 100644
--- a/src/r1cs_parser.js
+++ b/src/r1cs_parser.js
@@ -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 {
-
- 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);
- });
-});
\ No newline at end of file