Seyed Masoud Hosseini · Overview · Study log · Weekly summaries · Ideas · Search · Transcript · RSS feed
Zero Knowledge Proofs · Lecture 3 of 14 · 1:41:38
Lecture 3: Programming Zero Knowledge Proofs
Study guide
What this lecture covers
This lecture answers a practical question left open by the theory: once you have an idea for a zero knowledge application, how do you actually implement it? Pratyush Mishra and Alex Ozdemir walk through the process of turning a high-level predicate into an arithmetic circuit and then into rank-1 constraint systems (R1CS), the format most SNARK provers consume.
The lecture covers three concrete approaches with live tutorials: a hardware description language (Circom), a Rust library (arkworks), and a dedicated compiled language (Zokrates), building a partial Sudoku checker in each. After watching, you should understand what R1CS is, how each tool represents constraints and computes witnesses, and the tradeoffs between the three styles.
Key ideas
- Arithmetic circuit: a directed acyclic graph of addition and multiplication gates over a prime field, used to express the predicate a zero knowledge proof will prove.
- Rank-1 constraint system (R1CS): a set of constraints of the form
alpha * beta = gamma, where each ofalpha,beta,gammais an affine (linear plus constant) combination of variables; equivalently, three matricesA,B,Csuch that(Az) * (Bz) = Cz. - Circom: a hardware description language where signals are R1CS variables and constraints are created explicitly with the
===(or<==) operator; witness values are set separately with<--. - arkworks: a Rust library where a constraint system object tracks variables and constraints, and Rust's type system and operator overloading let developers build higher-level abstractions like a
Booleantype on top of raw constraints. - Zokrates: a standalone programming language that compiles high-level code (functions, loops, arrays,
assertstatements) directly into R1CS, hiding the constraint-by-constraint bookkeeping. - Witness computation vs. constraining: setting a signal's value (so a prover can compute it) is a separate step from constraining that value to satisfy an equation; HDLs like Circom expose this distinction explicitly.
- Tradeoff pattern: HDLs give the most direct control over constraints but are hard to learn and poor at abstraction; libraries add a host language's expressiveness at the cost of needing to know that language; compiled DSLs are the easiest to learn but give up explicit control over how witnesses are computed.
Walkthrough
From idea to circuit (0:01)
The lecture opens with the gap between a high-level idea (private payments, private voting) and the arithmetic circuit or constraint system format that zero knowledge proof systems actually consume. It previews the three approaches to be covered: hardware description languages, library-based approaches, and dedicated compiled languages, plus a closing comparison.
Arithmetic circuits and R1CS (5:09)
Mishra explains arithmetic circuits as prime-field analogues of Boolean circuits, expressible either as a system of field equations or as a directed acyclic graph of gates. Ozdemir then introduces R1CS formally, with worked examples of valid and invalid constraints, the matrix formulation Az * Bz = Cz, and a step-by-step algorithm for converting any arithmetic circuit into one R1CS constraint per gate.
Circom tutorial (21:29)
Mishra introduces Circom as an HDL where wires are signals and gates are constraints, contrasting it with general-purpose programming languages. Through live coding, the tutorial builds up templates for multiplication, repeated squaring (using template arguments and compile-time loops), a non-zero check (via multiplicative inverse), and sub-circuits, then assembles a partial Sudoku checker that verifies row uniqueness, range constraints, and agreement between puzzle and solution.
arkworks tutorial (52:06)
Ozdemir explains the constraint-system-object pattern common to library-based approaches: adding variables, building linear combinations, and enforcing constraints, plus how Rust abstractions like structs and operator overloading reduce boilerplate (illustrated with a Boolean AND gadget). He then implements the same Sudoku row-uniqueness and range/match checks using the arkworks R1CS standard library, running the code to confirm it accepts valid solutions and rejects invalid ones.
Zokrates tutorial (1:18:22)
Ozdemir introduces Zokrates as a standalone language with high-level features (structs, generics, for loops, assert) that compiles directly to R1CS, noting that it cannot compute intermediate witnesses explicitly the way Circom and arkworks can. The tutorial reimplements the Sudoku checks in Zokrates and walks through the command-line pipeline: compile, setup, compute witness, generate proof, and verify, demonstrating both an accepted and a rejected solution.
Comparing the approaches (1:31:31)
The lecture closes by classifying the three tools along two axes (circuit-describing vs. program-describing, and standalone syntax vs. embedded in a host language), then summarizes each tool's pros and cons: Circom's clarity but weak abstractions, arkworks' expressiveness but need to know Rust, and Zokrates' ease of use but reduced control over witness generation. It briefly surveys other tools (gadgetlib, Bellman, Snarky, Noir, Cairo) and notes the common techniques (booleans, fixed-width integers, control flow) shared across all of them.
Before you watch
- Watch Lecture 2 first for the definitions of arithmetic circuits, SNARKs, and R1CS that this lecture builds on directly.
- Basic familiarity with a systems language like Rust helps with the arkworks section, though the lecture explains the relevant syntax as it goes.
- No prior exposure to Circom, arkworks, or Zokrates is assumed.
Check your understanding
- What is the difference between setting a signal's value and constraining it, and why does Circom separate these into
<--and===? - Why can't a single R1CS constraint contain a product of three variables, and how does the lecture's example work around this?
- Compare the three tools on how much explicit control they give a developer over witness computation.
- Why does the arkworks approach require knowledge of Rust, while Zokrates does not require knowledge of any host language?
- What common techniques does the lecture suggest are shared across Circom, arkworks, and Zokrates despite their different syntax?
Vocabulary
- predicate (noun)
- A condition or statement that is either true or false about some inputs.
The zero knowledge system needs to prove a specific predicate is true. - R1CS (rank-1 constraint system) (noun)
- A standard format of equations, each multiplying two combinations of variables, used by most SNARK provers.
The compiler turns the circuit into R1CS constraints. - signal (noun)
- A variable inside a circuit description that carries a value.
In Circom, each wire in the circuit is called a signal. - constraint (noun)
- A rule that a valid set of values must satisfy.
Each multiplication gate becomes one constraint in R1CS. - hardware description language (noun)
- A language used to describe circuits directly, wire by wire.
Circom is a hardware description language for zero knowledge circuits. - operator overloading (noun)
- Letting normal symbols like + or * work on custom, user-defined objects.
Operator overloading in Rust lets code look like normal math on circuit variables. - compile (verb)
- To translate code written in one form into another, more basic form.
Zokrates code is compiled directly into R1CS. - witness computation (noun)
- The step of calculating the actual values that satisfy a circuit's constraints.
Witness computation is separate from writing the constraints themselves. - assert (noun)
- A statement in code that checks a condition and fails if it isn't true.
An assert statement enforces a constraint in the Zokrates language. - abstraction (noun)
- A simplified way of representing something complex, hiding lower-level details.
Zokrates gives more abstraction than raw constraint writing. - boilerplate (noun)
- Repeated, standard code that has to be written every time for a routine task.
Rust's type system reduces boilerplate when defining new circuit gadgets. - sub-circuit (noun)
- A smaller circuit reused as a building block inside a larger one.
The Sudoku checker is built from several reusable sub-circuits. - range constraint (noun)
- A rule that limits a value to fall between a minimum and maximum.
A range constraint checks that each Sudoku cell holds a number from 1 to 9. - trade-off (noun)
- A balance between two good things where gaining one means losing some of the other.
There's a trade-off between ease of use and fine control over the circuit. - template (noun)
- A reusable pattern of code that can be filled in with different specific values.
Circom uses templates to define reusable circuit components. - modular inverse (noun)
- The value that, multiplied by a number under modular arithmetic, gives one.
A modular inverse is used to check whether a value is non-zero. - standalone (adjective)
- Working independently, without needing another host system or language.
Zokrates is a standalone language, not embedded in Rust or another host. - domain-specific language (noun)
- A programming language designed for one specific type of task.
Circom is a domain-specific language for describing circuits. - generic (adjective)
- Written to work with many different types or sizes, not just one specific case.
Zokrates supports generic functions that work for different input sizes. - compile-time (adjective)
- Happening while code is being compiled, before the program actually runs.
Circom supports compile-time loops for repeated squaring. - gadget (noun)
- A small reusable circuit building block for a specific check.
A Boolean AND gadget is built from a few simple constraints.
From the YouTube description
Guest Lecturers: Pratyush Mishra & Alex Ozdemir
Zero Knowledge Proofs MOOC Spring '23
← Lecture 2: Overview of Modern SNARK Constructions · Lecture 4: Interactive Proofs and the Sum-Check Protocol →
