Super Simple Circuit
Part 1: Setting up your Docker environment
This tutorial assumes you have Docker installed on your machine. The root folder of this repo contains a Dockerfile that creates an environment with Rust (needed for the Circom compiler) and Node (needed for snarkjs).
First, build a docker image called circom-playground
:
or pull the pre-built docker image if you don't want to wait for the circom compiler to build:
Next, fire up an interactive environment with this new image:
this will start a bash terminal in the docker environment. Then change directories into your mounted volume:
Warning The above command uses the
-v
flag to mount the repository into the container environment so you can edit your code without stopping, rebuilding, then restarting your container. Be sure to change/home/todd/code/circom-playground
to the correct path on your machine or you won't see the files you need in the working directory.
Now you're all set up to start using Circom and snarkjs.
Part 2: Compile a simple multiplication circuit
We're going to start with a boringly simple problem that multiplies N numbers together and proves we did it right. If you wanted to do something like this in practice, you'd probably want to publish a hashed commitment of the two numbers your are multiplying together so that your proof is a little more useful. That way the prover would know you multiplied the numbers that you committed to at a certain time stamp. But this first example is just to go through the motions.
In order to compile the circuit, we need the circom compiler (already built for you in the container environment).
The compilation command produced a .wasm
file that can run in the browser (notice how we used a node
runtime to compute the witness). The .wasm
file is your fully compiled circuit and is used, along with an variable input file, to generate a witness file for your specific circuit.
So, in practice the .wasm
file will be a dependency of your client-side application. It will run on your end user's device and it will be called every time a proof needs to be generated (which could be often).
Inspect the R1CS file created by the compilation step:
Print the constraints:
Part 3: Perform the trusted setup ceremony
Now that your circuit is compiled and you have it in the form of a R1CS, its time to generate the common reference string (aka, magic numbers). We are going to use snarkjs for this. The first phase is often called the Powers of Tau and is only ever done once; it has no dependency on the computational graph of your circuit.
The second phase of depends on your specific circuit, specifically its R1CS. The quantity of magic numbers required to construct a zkSNARK is proportional to the size of your circuit, so if you change the computation, you need to redo phase 2 of the trusted setup.
In practice, if your application is proving the same kind of statement over and over again with different private inputs (this will most likely be the case), then both phase I and II need only be done a single time before you officially launch your application into production. If you have a situation where you don't know the circuits ahead of time, you'll need to run phase II once for each new circuit introduced (because we are using Groth16).
Part 4: Generate a Proof
Now that we have compiled our circuit and successfully completed the trusted setup ceremony, a prover has everything it needs to start generating proofs.
First, use the .wasm
file you compiled along with an input file containing all the public and private inputs to output a special witness file.
Then, use the publicly available proving key from the trusted setup along with the witness to output a json file containing your proof as well as a separate file that contains just the public information that the verifier will need.
Part 5: Verify the Proof
Now that the proof and public inputs have been delivered to the verifier, use the publicly available verification key to verify their contents:
Proofs generated for zkSNARKS can also be verified on smart contract platforms like Ethereum. The Ethereum Virtual Machine introduced special routines (EIP196 and EIP197) in an attempt to make zkSNARK proof verification as efficient (and thus cheap) as possible. This is what makes token mixers like Tornado Cash feasible to implement and use on the Etheruem blockchain.
Note Try changing something small in
proof.json
orpublic.json
file so that the proof doesn't verify.
Last updated