What is Circom, what is Noir and why are you trying to get me to learn MORE languages?
Circom and Noir are programming languages designed for building privacy-preserving applications using zero-knowledge proofs (ZKPs).
Circom and SnarkJS have been utilized in numerous production-ready projects within the Ethereum network, such as Tornado, Polygon Hermez, Dark Forest, and various others.
Aztec Protocol is the team behind Noir. Here is a list of projects using Noir aside from the Aztec team.
TL;DR
Circom and Noir enable the creation of efficient and scalable zero-knowledge circuits.
By learning Circom and/or Noir, you can have access to two powerful tools for building privacy-preserving applications. While it may seem daunting to learn multiple programming languages, each language has its own strengths and use cases. Circom is particularly useful for building efficient and scalable circuits, while Noir is great for building complex applications that require advanced cryptographic operations.
Zero-Knowledge Proofs (ZKPs) are a type of cryptographic protocol that can be used to prove the validity of computations without revealing the input values or intermediate computations.
Learning Circom and Noir can greatly enhance your skills as a developer in the field of ZKP development, and provide you with a wider range of tools to build powerful privacy-preserving applications.
Have fun reading this post, but read the docs <3
The Circom and Noir docs are quite thorough and well laid out. Reading through them eventually is a very good idea. This blog post is meant to help as a supporting resource for "getting started" and will not cover more complex interactions with the languages; for another post I suppose.
What one might learn from going through this post?
A zero-knowledge proof is a protocol that enables one party, called the prover, to convince another, the verifier, that a statement is true without revealing any information beyond the veracity of the statement.
Non-interactive zero-knowledge proofs are a particular type of zero-knowledge proofs in which the prover can generate the proof without interaction with the verifier. NIZK protocols are very suitable for blockchain applications, because they allow a smart contract to act as a verifier.
zk-SNARKs require the computational statement to be modeled with an arithmetic circuit. Although it may not always be obvious how to do this conversion, most computational problems we care about can easily be converted into arithmetic circuits.
An F_p
-arithmetic circuit is a circuit consisting of set of wires that carry values from the field F_p
and connect them to addition and multiplication gates modulo p
.
p
, the finite field F_p
consists of the set of numbers {0,...,p-1}
on which we can add and multiply these numbers modulo p
.
F_7
consists of the set of numbers {0,...,6}
on which we can add and multiply numbers modulo 7
.15 modulo 7 = 1
, since 15 = 7 + 7 + 1
7 modulo 7 = 0
4*3 modulo 7 = 5
, since 4*3 = 12 = 7 + 5
An arithmetic circuit takes some input signals that are values between
0,...,p-1
and performs additions and multiplications between them modulo the primep
. The output of every addition and multiplication gate is considered an intermediate signal, except for the last gate of the circuit, the output of which, is the output signal of the circuit.
The set of constraints describing the circuit is called rank-1 constraint system
.
(a_1*s_1 + ... + a_n*s_n) + (b_1*s_1 + ... + b_n*s_n) + (c_1*s_1 + ... + c_n*s_n) = 0
d = a*b modulo 7
out = d+c modulo 7
out = a*b + c modulo 7
An assignment of the signals is called a witness.
a
is a private key and the input b
is the corresponding public key. If we define a
as a private input, b
, c
as public inputs and out
as a public output, with zero-knowledge we are able to prove, without revealing its value, that we know a private input a
such that, for certain public values b
, c
and out
, the equation a*b + c = out mod 7
holds.
a
by isolating it from the other signals. It is important to design circuits that keep the privacy of the private inputs and prevent deducing them from the R1CS.Circom is a language for building arithmetic circuits that can be compiled into zero-knowledge proofs. It allows programmers to define the constraints that define the arithmetic circuit using linear combinations of signals.
In this post, we will explore how to build and verify zero-knowledge proofs for arithmetic circuits using Circom and SnarkJS. We'll start by introducing the concept of zero-knowledge proofs and their importance in preserving privacy. Then, we'll dive into Circom, a language for building arithmetic circuits, and walk through the process of defining constraints, compiling circuits, computing witnesses, and generating proofs. Finally, we'll show how to verify proofs both locally and on the Ethereum blockchain using SnarkJS and Solidity. By the end of this post, you should have a solid understanding of how to build and verify zero-knowledge proofs for arithmetic circuits using Circom and SnarkJS.
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
git clone https://github.com/iden3/circom.git
cargo build --release
cargo install --path circom
npm install -g snarkjs
A*B + C = 0
where A
, B
, and C
are linear combinations of signals.Multiplier2
.a
, b
, and c
.Example Circuit:
// Sets the compiler version to 2.0.0
pragma circom 2.0.0;
// Defines a new template called Multiplier2
template Multiplier2 () {
// Declares input signals a and b, and output signal c
signal input a;
signal input b;
signal output c;
// Sets the value of c to the result of multiplying a and b
c <== a * b;
}
a
and b
, and one output signal, c
.<==
is used to set the value of c
to the result of multiplying a
and b
.
==>
can be used for the same purpose.For more complex circuit examples: https://docs.circom.io/more-circuits/more-basic-circuits/
multiplier2.circom
.circom --help
circom multiplier2.circom --r1cs --wasm --sym --c
multiplier2.r1cs
: contains the R1CS (Rank-1 Constraint System) of the circuit in binary format.multiplier2_js
: contains the Wasm (WebAssembly) code and other files needed to generate the witness.multiplier2.sym
: a symbols file required for debugging or for printing the constraint system in an annotated mode.multiplier2_cpp
: contains several files (multiplier2.cpp
, multiplier2.dat
, and other common files for every compiled program like main.cpp
, MakeFile
, etc.) needed to compile the C code to generate the witness.What is a witness?
Before creating the proof, we need to calculate all the signals of the circuit that match all the constraints of the circuit.
input.json
that containing the inputs written in the standard JSON format.
{"a": "3", "b": "11"}
Computing the witness with WebAssembly
multiplier2.circom
we can find a multiplier2_js
folder that contains the Wasm code in multiplier2.wasm
and all the needed JavaScript files.multiplier2_js
, add the input in a file input.json and execute:node generate_witness.js multiplier2.wasm input.json witness.wtns
Computing the witness with C++
--c
make
./multiplier2 input.json witness.wtns
Note. To compile the C++ source, we rely on some libraries that you need to have installed in your system. In particular, we use
nlohmann-json3-dev
,libgmp-dev
andnasm
.
ẁitness.wtns
file. This file is encoded in a binary format compatible with snarkjs
, which is the tool that we use to create the actual proofs.snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="First contribution" -v
pot12_0001.ptau
and we can proceed with the Phase 2.snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau -v
.zkey
file that will contain the proving and verification keys together with all phase 2 contributions: snarkjs groth16 setup multiplier2.r1cs pot12_final.ptau multiplier2_0000.zkey
snarkjs zkey contribute multiplier2_0000.zkey multiplier2_0001.zkey --name="1st Contributor Name" -v
snarkjs zkey export verificationkey multiplier2_0001.zkey verification_key.json
Once the witness is computed and the trusted setup is already executed, we can generate a zk-proof associated to the circuit and the witness
snarkjs groth16 prove multiplier2_0001.zkey witness.wtns proof.json public.json
proof.json
: it contains the proof.public.json
: it contains the values of the public inputs and outputs.A valid proof not only proves that we know a set of signals that satisfy the circuit, but also that the public inputs and outputs that we use match the ones described in the public.json file.
snarkjs groth16 verify verification_key.json public.json proof.json
verification_key.json
we exported earlier, proof.json
, and public.json
to check if the proof is valid. (OK)It is also possible to generate a Solidity verifier that allows verifying proofs on Ethereum blockchain.
snarkjs zkey export solidityverifier multiplier2_0001.zkey verifier.sol
multiplier2_0001.zkey
and outputs Solidity code in a file named verifier.sol
.
verifyProof
that returns TRUE
if and only if the proof and the inputs are valid.
snarkJS
to generate the parameters of the call by typing: snarkjs generatecall
verifyProof
method in Remix.
TRUE
.FALSE
.Noir is a programming language that was specifically designed for building privacy-preserving applications using zero-knowledge proofs. It is a high-level language that makes it easy for developers to express complex cryptographic operations using simple and intuitive syntax. The language is built on top of the Rust programming language, which provides fast execution speeds and strong type safety guarantees. Noir also comes with a suite of powerful tools and libraries that make it easy to build, prove, and verify zero-knowledge circuits. Overall, Noir is a powerful tool for developers who want to build privacy-preserving applications without sacrificing performance or security.
This post provides an overview of Noir, including how to install it and use its suite of tools and libraries to build, prove, and verify zero-knowledge circuits. A basic example is also provided to demonstrate how to create and verify a proof in a Noir program.
git clone git@github.com:noir-lang/noir.git
cd noir
cargo install --locked --path=crates/nargo --no-default-features --features plonk_bn254_wasm
nargo --help
nargo
command (same a nargo help
/ nargo -h
/ nargo --help
) brings up the menu.nargo new
nargo check
Prover.toml
and Verifier.toml files for specifying prover and verifier.nargo excecute
nargo prove
nargo verify
nargo codegen-verifier
nargo compile
Noir is a domain specific language for creating and verifying proofs. It's design choices are influenced heavily by Rust.
nargo
is a command line tool for interacting with Noir programs (e.g. compiling, proving, verifying and more).
nargo new hello_world
circuits
for better identifiability when sitting alongside other folders in the codebase (e.g. contracts
, scripts
, test
).
fn main(x : Field, y : pub Field) {
constrain x != y;
}
x
), but can be labeled public using the keyword pub
(e.g. y
).cd hello_world
& cargo check
Prover.toml
file.
constrain x != y;
nargo prove
<proof_name>nargo verify
<proof_name>We did it! We created and verified a proof in a Noir program!
What might one want to read next?
After going through the basics of Circom and Noir, you might want to explore more advanced topics and real-world use cases for both languages. Here are some suggestions for what to read next:
Circom:
Noir:
Others
By diving into more advanced topics and real-world examples, you'll gain a deeper understanding of how to build and verify zero-knowledge proofs for arithmetic circuits using Circom and Noir, as well as how to apply these techniques in various scenarios such as privacy-preserving applications and decentralized systems.