From 5adee591bf065f5cf7d329e13735c802c12e141e Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Thu, 26 Mar 2020 20:16:29 +0100 Subject: [PATCH] Adapted to circom 0.5 --- COPYING | 2 +- cli.js | 305 +++++++++++++++++++++++++-------------------- index.js | 3 - package-lock.json | 26 ++++ package.json | 2 + src/circuit.js | 2 +- src/loadsyms.js | 32 +++++ src/printr1cs.js | 37 ++++++ src/r1cs_parser.js | 23 ++-- test/setup_r1cs.js | 55 -------- 10 files changed, 284 insertions(+), 203 deletions(-) create mode 100644 src/loadsyms.js create mode 100644 src/printr1cs.js delete mode 100644 test/setup_r1cs.js 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