You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

394 lines
13 KiB

  1. #!/usr/bin/env node
  2. /*
  3. Copyright 2018 0KIMS association.
  4. This file is part of jaz (Zero Knowledge Circuit Compiler).
  5. jaz is a free software: you can redistribute it and/or modify it
  6. under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. jaz is distributed in the hope that it will be useful, but WITHOUT
  10. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11. or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  12. License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with jaz. If not, see <https://www.gnu.org/licenses/>.
  15. */
  16. /* eslint-disable no-console */
  17. const fs = require("fs");
  18. const path = require("path");
  19. const zkSnark = require("./index.js");
  20. const {stringifyBigInts, unstringifyBigInts} = require("./src/stringifybigint.js");
  21. const version = require("./package").version;
  22. const argv = require("yargs")
  23. .version(version)
  24. .usage(`snarkjs <command> <options>
  25. setup command
  26. =============
  27. snarkjs setup <option>
  28. Runs a setup for a circuit generating the proving and the verification key.
  29. -c or --circuit <circuitFile>
  30. Filename of the compiled circuit file generated by circom.
  31. Default: circuit.json
  32. --pk or --provingkey <provingKeyFile>
  33. Output filename where the proving key will be stored.
  34. Default: proving_key.json
  35. --vk or --verificationkey <verificationKeyFile>
  36. Output Filename where the verification key will be stored.
  37. Default: verification_key.json
  38. calculate witness command
  39. =========================
  40. snarkjs calculatewitness <options>
  41. Calculate the witness of a circuit given an input.
  42. -c or --circuit <circuitFile>
  43. Filename of the compiled circuit file generated by circom.
  44. Default: circuit.json
  45. -i or --input <inputFile>
  46. JSON file with the inputs of the circuit.
  47. Default: input.json
  48. Example of for a circuit with tow inputs a and b:
  49. {"a": "22", "b": "33"}
  50. -w or --witness
  51. Output filename with the generated witness.
  52. Default: witness.json
  53. generate a proof command
  54. ========================
  55. snarkjs proof <options>
  56. -w or --witness
  57. Input filename used to calculate the proof.
  58. Default: witness.json
  59. --pk or --provingkey <provingKeyFile>
  60. Input filename with the proving key (generated during the setup).
  61. Default: proving_key.json
  62. -p or --proof
  63. Output filenam with the zero knowlage proof.
  64. Default: proof.json
  65. --pub or --public <publicFilename>
  66. Output filename with the value of the public wires/signals.
  67. This info will be needed to verify the proof.
  68. Default: public.json
  69. verify command
  70. ==============
  71. snarkjs verify <options>
  72. The command returns "OK" if the proof is valid
  73. and "INVALID" in case it is not a valid proof.
  74. --vk or --verificationkey <verificationKeyFile>
  75. Input Filename with the verification key (generated during the setup).
  76. Default: verification_key.json
  77. -p or --proof
  78. Input filenam with the zero knowlage proof you want to verify
  79. Default: proof.json
  80. --pub or --public <publicFilename>
  81. Input filename with the public wires/signals.
  82. Default: public.json
  83. generate solidity verifier command
  84. ==================================
  85. snarkjs generateverifier <options>
  86. Generates a solidity smart contract that verifies the zero knowlage proof.
  87. --vk or --verificationkey <verificationKeyFile>
  88. Input Filename with the verification key (generated during the setup).
  89. Default: verification_key.json
  90. -v or --verifier
  91. Output file with a solidity smart contract that verifies a zero knowlage proof.
  92. Default: verifier.sol
  93. generate call parameters
  94. ========================
  95. snarkjs generatecall <options>
  96. Outputs into the console the raw parameters to be used in 'verifyProof'
  97. method of the solidity verifier function.
  98. -p or --proof
  99. Input filename with the zero knowlage proof you want to use
  100. Default: proof.json
  101. --pub or --public <publicFilename>
  102. Input filename with the public wires/signals.
  103. Default: public.json
  104. circuit info
  105. ============
  106. snarkjs info <options>
  107. Print statistics of a circuit
  108. -c or --circuit <circuitFile>
  109. Filename of the compiled circuit file generated by circom.
  110. Default: circuit.json
  111. print constraints
  112. =================
  113. snarkjs printconstraints <options>
  114. Print all the constraints of a given circuit
  115. -c or --circuit <circuitFile>
  116. Filename of the compiled circuit file generated by circom.
  117. Default: circuit.json
  118. `)
  119. .alias("c", "circuit")
  120. .alias("pk", "provingkey")
  121. .alias("vk", "verificationkey")
  122. .alias("w", "witness")
  123. .alias("p", "proof")
  124. .alias("i", "input")
  125. .alias("pub", "public")
  126. .alias("v", "verifier")
  127. .help("h")
  128. .alias("h", "help")
  129. .epilogue(`Copyright (C) 2018 0kims association
  130. This program comes with ABSOLUTELY NO WARRANTY;
  131. This is free software, and you are welcome to redistribute it
  132. under certain conditions; see the COPYING file in the official
  133. repo directory at https://github.com/iden3/circom `)
  134. .argv;
  135. const circuitName = (argv.circuit) ? argv.circuit : "circuit.json";
  136. const provingKeyName = (argv.provingkey) ? argv.provingkey : "proving_key.json";
  137. const verificationKeyName = (argv.verificationkey) ? argv.verificationkey : "verification_key.json";
  138. const inputName = (argv.input) ? argv.input : "input.json";
  139. const witnessName = (argv.witness) ? argv.witness : "witness.json";
  140. const proofName = (argv.proof) ? argv.proof : "proof.json";
  141. const publicName = (argv.public) ? argv.public : "public.json";
  142. const verifierName = (argv.public) ? argv.public : "verifier.sol";
  143. function p256(n) {
  144. let nstr = n.toString(16);
  145. while (nstr.length < 64) nstr = "0"+nstr;
  146. nstr = `"0x${nstr}"`;
  147. return nstr;
  148. }
  149. try {
  150. if (argv._[0].toUpperCase() == "INFO") {
  151. const cirDef = JSON.parse(fs.readFileSync(circuitName, "utf8"));
  152. const cir = new zkSnark.Circuit(cirDef);
  153. console.log(`# Wires: ${cir.nVars}`);
  154. console.log(`# Constraints: ${cir.nConstraints}`);
  155. console.log(`# Private Inputs: ${cir.nPrvInputs}`);
  156. console.log(`# Public Inputs: ${cir.nPubInputs}`);
  157. console.log(`# Outputs: ${cir.nOutputs}`);
  158. } else if (argv._[0].toUpperCase() == "PRINTCONSTRAINTS") {
  159. const cirDef = JSON.parse(fs.readFileSync(circuitName, "utf8"));
  160. const cir = new zkSnark.Circuit(cirDef);
  161. cir.printConstraints();
  162. } else if (argv._[0].toUpperCase() == "SETUP") {
  163. const cirDef = JSON.parse(fs.readFileSync(circuitName, "utf8"));
  164. const cir = new zkSnark.Circuit(cirDef);
  165. const setup = zkSnark.setup(cir);
  166. fs.writeFileSync(provingKeyName, JSON.stringify(stringifyBigInts(setup.vk_proof), null, 1), "utf-8");
  167. fs.writeFileSync(verificationKeyName, JSON.stringify(stringifyBigInts(setup.vk_verifier), null, 1), "utf-8");
  168. process.exit(0);
  169. } else if (argv._[0].toUpperCase() == "CALCULATEWITNESS") {
  170. const cirDef = JSON.parse(fs.readFileSync(circuitName, "utf8"));
  171. const cir = new zkSnark.Circuit(cirDef);
  172. const input = unstringifyBigInts(JSON.parse(fs.readFileSync(inputName, "utf8")));
  173. const witness = cir.calculateWitness(input);
  174. fs.writeFileSync(witnessName, JSON.stringify(stringifyBigInts(witness), null, 1), "utf-8");
  175. process.exit(0);
  176. } else if (argv._[0].toUpperCase() == "PROOF") {
  177. const witness = unstringifyBigInts(JSON.parse(fs.readFileSync(witnessName, "utf8")));
  178. const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync(provingKeyName, "utf8")));
  179. const {proof, publicSignals} = zkSnark.genProof(provingKey, witness);
  180. fs.writeFileSync(proofName, JSON.stringify(stringifyBigInts(proof), null, 1), "utf-8");
  181. fs.writeFileSync(publicName, JSON.stringify(stringifyBigInts(publicSignals), null, 1), "utf-8");
  182. process.exit(0);
  183. } else if (argv._[0].toUpperCase() == "VERIFY") {
  184. const public = unstringifyBigInts(JSON.parse(fs.readFileSync(publicName, "utf8")));
  185. const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync(verificationKeyName, "utf8")));
  186. const proof = unstringifyBigInts(JSON.parse(fs.readFileSync(proofName, "utf8")));
  187. const isValid = zkSnark.isValid(verificationKey, proof, public);
  188. if (isValid) {
  189. console.log("OK");
  190. process.exit(0);
  191. } else {
  192. console.log("INVALID");
  193. process.exit(1);
  194. }
  195. } else if (argv._[0].toUpperCase() == "GENERATEVERIFIER") {
  196. const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync(verificationKeyName, "utf8")));
  197. let template = fs.readFileSync(path.join( __dirname, "templates", "verifier.sol"), "utf-8");
  198. const vka_str = `[${verificationKey.vk_a[0][1].toString()},`+
  199. `${verificationKey.vk_a[0][0].toString()}], `+
  200. `[${verificationKey.vk_a[1][1].toString()},` +
  201. `${verificationKey.vk_a[1][0].toString()}]`;
  202. template = template.replace("<%vk_a%>", vka_str);
  203. const vkb_str = `${verificationKey.vk_b[0].toString()},`+
  204. `${verificationKey.vk_b[1].toString()}`;
  205. template = template.replace("<%vk_b%>", vkb_str);
  206. const vkc_str = `[${verificationKey.vk_c[0][1].toString()},`+
  207. `${verificationKey.vk_c[0][0].toString()}], `+
  208. `[${verificationKey.vk_c[1][1].toString()},` +
  209. `${verificationKey.vk_c[1][0].toString()}]`;
  210. template = template.replace("<%vk_c%>", vkc_str);
  211. const vkg_str = `[${verificationKey.vk_g[0][1].toString()},`+
  212. `${verificationKey.vk_g[0][0].toString()}], `+
  213. `[${verificationKey.vk_g[1][1].toString()},` +
  214. `${verificationKey.vk_g[1][0].toString()}]`;
  215. template = template.replace("<%vk_g%>", vkg_str);
  216. const vkgb1_str = `${verificationKey.vk_gb_1[0].toString()},`+
  217. `${verificationKey.vk_gb_1[1].toString()}`;
  218. template = template.replace("<%vk_gb1%>", vkgb1_str);
  219. const vkgb2_str = `[${verificationKey.vk_gb_2[0][1].toString()},`+
  220. `${verificationKey.vk_gb_2[0][0].toString()}], `+
  221. `[${verificationKey.vk_gb_2[1][1].toString()},` +
  222. `${verificationKey.vk_gb_2[1][0].toString()}]`;
  223. template = template.replace("<%vk_gb2%>", vkgb2_str);
  224. const vkz_str = `[${verificationKey.vk_z[0][1].toString()},`+
  225. `${verificationKey.vk_z[0][0].toString()}], `+
  226. `[${verificationKey.vk_z[1][1].toString()},` +
  227. `${verificationKey.vk_z[1][0].toString()}]`;
  228. template = template.replace("<%vk_z%>", vkz_str);
  229. // The points
  230. template = template.replace("<%vk_input_length%>", (verificationKey.A.length-1).toString());
  231. template = template.replace("<%vk_ic_length%>", verificationKey.A.length.toString());
  232. let vi = "";
  233. for (let i=0; i<verificationKey.A.length; i++) {
  234. if (vi != "") vi = vi + " ";
  235. vi = vi + `vk.IC[${i}] = Pairing.G1Point(${verificationKey.A[i][0].toString()},`+
  236. `${verificationKey.A[i][1].toString()});\n`;
  237. }
  238. template = template.replace("<%vk_ic_pts%>", vi);
  239. fs.writeFileSync(verifierName, template, "utf-8");
  240. process.exit(0);
  241. } else if (argv._[0].toUpperCase() == "GENERATECALL") {
  242. const public = unstringifyBigInts(JSON.parse(fs.readFileSync(publicName, "utf8")));
  243. const proof = unstringifyBigInts(JSON.parse(fs.readFileSync(proofName, "utf8")));
  244. let inputs = "";
  245. for (let i=0; i<public.length; i++) {
  246. if (inputs != "") inputs = inputs + ",";
  247. inputs = inputs + p256(public[i]);
  248. }
  249. const S=`[${p256(proof.pi_a[0])}, ${p256(proof.pi_a[1])}],` +
  250. `[${p256(proof.pi_ap[0])}, ${p256(proof.pi_ap[1])}],` +
  251. `[[${p256(proof.pi_b[0][1])}, ${p256(proof.pi_b[0][0])}],[${p256(proof.pi_b[1][1])}, ${p256(proof.pi_b[1][0])}]],` +
  252. `[${p256(proof.pi_bp[0])}, ${p256(proof.pi_bp[1])}],` +
  253. `[${p256(proof.pi_c[0])}, ${p256(proof.pi_c[1])}],` +
  254. `[${p256(proof.pi_cp[0])}, ${p256(proof.pi_cp[1])}],` +
  255. `[${p256(proof.pi_h[0])}, ${p256(proof.pi_h[1])}],` +
  256. `[${p256(proof.pi_kp[0])}, ${p256(proof.pi_kp[1])}],` +
  257. `[${inputs}]` ;
  258. console.log(S);
  259. process.exit(0);
  260. } else {
  261. throw new Error("Invalid Command");
  262. }
  263. } catch(err) {
  264. console.log("ERROR: " + err);
  265. process.exit(1);
  266. }