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.

258 lines
8.1 KiB

5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. # circom and snarkjs tutorial
  2. This tutorial will guide you in creating your first zero-knowledge SNARK circuit. It will take you through the various techniques to write circuits and show you how to create and verify proofs off-chain and on-chain on Ethereum.
  3. ## 1. Installing the tools
  4. ### 1.1 Pre-requisites
  5. If you don't have it installed yet, you need to install `Node.js`.
  6. You should install at least version 10 of node. It's important to note here that the latests versions of javascript, includes big integer support and web assembly compilers that make the code run fast.
  7. ### 1.2 Install **circom** and **snarkjs**
  8. Run:
  9. ```sh
  10. npm install -g circom
  11. npm install -g snarkjs
  12. ```
  13. ## 2. Working with a circuit
  14. Let's create a circuit that will allow you to prove that you are able to factor a number!
  15. ### 2.1 Create a circuit in a new directory
  16. 1. Create an empty directory called `factor` where you will put all the files that you will use in this tutorial.
  17. ```
  18. mkdir factor
  19. cd factor
  20. ```
  21. > In a real circuit, you will probably want to create a `git` repository with a `circuits` directory and a `test` directory with all your tests, and the needed scripts to build all the circuits.
  22. 2. Create a new file named `circuit.circom` with the following content:
  23. ```
  24. template Multiplier() {
  25. signal private input a;
  26. signal private input b;
  27. signal output c;
  28. c <== a*b;
  29. }
  30. component main = Multiplier();
  31. ```
  32. This circuit has 2 private input signals named `a` and `b` and one output named `c`.
  33. The only thing that the circuit does is forcing the signal `c` to be the value of `a*b`
  34. After declaring the `Multiplier` template, we instantiate it with a component named`main`.
  35. Note: When compiling a circuit, a component named `main` must always exist.
  36. ### 2.2 Compile the circuit
  37. We are now ready to compile the circuit. Run the following command:
  38. ```sh
  39. circom circuit.circom --r1cs --wasm --sym
  40. ```
  41. The `--r1cs` option will generate `circuit.r1cs` (the r1cs constraint system of the circuit in binary format).
  42. The `--wasm` option will generate `circuit.wasm` (the wasm code to generate the witness).
  43. The `--sym` option will generate `circuit.sym` (a symbols file required for debugging or if you want to print the constraint system in an annotated mode).
  44. ## 3. Taking the compiled circuit to *snarkjs*
  45. Now that the circuit is compiled, we will continue with `snarkjs`.
  46. Please note that you can always access the help of `snarkjs` by typing:
  47. ```sh
  48. snarkjs --help
  49. ```
  50. ### 3.1 View information and stats regarding a circuit
  51. To show general statistics of this circuit, you can run:
  52. ```sh
  53. snarkjs info -r circuit.r1cs
  54. ```
  55. You can also print the constraints of the circuit by running:
  56. ```sh
  57. snarkjs printconstraints -r circuit.r1cs -s circuit.sym
  58. ```
  59. ### 3.2 Setting up using *snarkjs*
  60. Ok, let's run a setup for our circuit:
  61. ```sh
  62. snarkjs setup
  63. ```
  64. > By default `snarkjs` will look for and use `circuit.r1cs`. You can always specify a different circuit file by adding `-r <circuit R1CS file name>`.
  65. The output of the setup will be in the form of 2 files: `proving_key.json` and `verification_key.json`.
  66. ### 3.3. Calculating a witness
  67. Before creating any proof, we need to calculate all the signals of the circuit that match (all) the constraints of the circuit.
  68. `circom` generates a wasm module that calculates those for you. You need to provide a file with the inputs and it will execute the circuit and calculate all the intermediate signals and the output. This set of signals is the *witness*.
  69. The zero-knowledge proofs prove that you know a set of signals (witness) that match all the constraints without revealing any of the signals except the public inputs and the outputs.
  70. For example, imagine you want to prove you are able to factor the number 33. It means that you know two numbers `a` and `b` that when you multiply them, it results in 33.
  71. > Of course you can always use the number one and the same number as `a` or `b`. We will deal with this problem later.
  72. So you want to prove that you know 3 and 11.
  73. Let's create a file named `input.json`
  74. ```json
  75. {"a": 3, "b": 11}
  76. ```
  77. Now let's calculate the witness:
  78. ```sh
  79. snarkjs calculatewitness --wasm circuit.wasm --input input.json --witness witness.json
  80. ```
  81. You may want to take a look at `witness.json` file with all the signals.
  82. ### Create the proof
  83. Now that we have the witness generated, we can create the proof.
  84. ```sh
  85. snarkjs proof
  86. ```
  87. This command will use the `proving_key.json` and the `witness.json` files by default to generate `proof.json` and `public.json`
  88. The `proof.json` file will contain the actual proof and the `public.json` file will contain just the values of the public inputs and the outputs.
  89. ### Verifying the proof
  90. To verify the proof run:
  91. ```sh
  92. snarkjs verify
  93. ```
  94. This command will use `verification_key.json`, `proof.json` and `public.json` to verify that is valid.
  95. Here we are verifying that we know a witness that the public inputs and the outputs matches the ones in the `public.json` file.
  96. If the proof is ok, you will see `OK` or `INVALID` if not ok.
  97. ### Generate the solidity verifier
  98. ```sh
  99. snarkjs generateverifier
  100. ```
  101. This command will take the `verification_key.json` and generate solidity code in `verifier.sol` file.
  102. You can take the code in `verifier.sol` and cut and paste it in remix.
  103. This code contains two contracts: Pairings and Verifier. You only need to deploy the `Verifier` contract.
  104. > You may want to use a test net like Rinkeby, Kovan or Ropsten. You can also use the Javascript VM, but in some browsers the verification takes long and it may hang the page.
  105. ### Verifying the proof on-chain
  106. The verifier contract deployed in the last step has a `view` function called `verifyProof`.
  107. This function will return true if the proof and the inputs are valid.
  108. To facilitate the call, you can use `snarkjs` to generate the parameters of the call by typing:
  109. ```sh
  110. snarkjs generatecall
  111. ```
  112. Just cut and paste the output to the parameters field of the `verifyProof` method in Remix.
  113. If every thing works ok, this method should return true.
  114. If you change any bit in the parameters, the result will be verifiably false.
  115. ## Bonus track
  116. We can fix the circuit to not accept the number 1 as any of the input values by adding some extra constraints.
  117. Here, the trick is that we use the property that 0 has no inverse. So `(a-1)` should not have an inverse.
  118. That means that `(a-1)*inv = 1` will be inpossible to match if `a` is 1.
  119. We just calculate inv by `1/(a-1)`.
  120. So, let's modify the circuit:
  121. ```
  122. template Multiplier() {
  123. signal private input a;
  124. signal private input b;
  125. signal output c;
  126. signal inva;
  127. signal invb;
  128. inva <-- 1/(a-1);
  129. (a-1)*inva === 1;
  130. invb <-- 1/(b-1);
  131. (b-1)*invb === 1;
  132. c <== a*b;
  133. }
  134. component main = Multiplier();
  135. ```
  136. A nice thing of the circom language is that you can split a `<==` into two independent actions: `<--` and `===`.
  137. The `<--` and `-->` operators assign a value to a signal without creating any constraints.
  138. The `===` operator adds a constraint without assigning any value to a signal.
  139. The circuit also has another problem: the operation works in `Z_r`, so we need to guarantee the multiplication does not overflow. This can be done by converting the inputs to binary and checking the ranges, but we will reserve it for future tutorials.
  140. ## Where to go from here
  141. You may want to read the [README](https://github.com/iden3/circom) to learn more features about `circom`.
  142. You can also check a library with many basic circuits lib binarizations, comparators, eddsa, hashes, merkle trees etc [here](https://github.com/iden3/circomlib) (Work in progress).
  143. Or a exponentiation in the Baby Jubjub curve [here](https://github.com/iden3/circomlib) (Work in progress).
  144. # Final note
  145. There is nothing worse for a dev than working with a buggy compiler. This is a very early stage of the compiler, so there are many bugs and lots of work needs to be done. Please have it present if you are doing anything serious with it.
  146. And please contact us for any isue you have. In general, a github issue with a small piece of code with the bug is very useful to us.
  147. Enjoy zero-knowledge proving!