Introduction to ZK
For anyone who wants to learn how zero knowledge proofs work
A zero knowledge proof (ZKP or just ZK) is a proof of computation satisfying:
Succinctness: the size of the proof is constant (or in some cases logarithmic) in the size of the computation
Zero knowledge: certain inputs or intermediate variables of the computation can be hidden from the verifier of the proof
Contrary to the name, the succinctness property is more central to current applications of ZK, whereas the zero knowledge aspect is often not used and can be turned off. Succinctness allows us to compress expensive computations into ZKPs that are computationally cheap to verify:
The general dynamic of a zero knowledge proof is that a Prover generates a ZKP of an expensive computation and sends the (constant sized) ZKP to a Verifier. Because the ZKP is constant sized, the Verifier can run a cheap constant time verification algorithm on the ZKP: if it passes, then the Verifier is assured that the Prover executed the claimed computation truthfully. Most importantly, the Verifier has this assurance without trusting the Prover. The precise assurance is of the form
under certain cryptographic assumptions, ___ is true with probability .
How does it all work?
There are now developer toolkits with varying levels of abstraction (Rust/Go/Javascript API libraries, domain-specific languages) to translate arbitrary NP computations to ZK circuits. Given computation-specific inputs, a ZK circuit generates a ZK proof to be submitted to the Verifier.
This step of translation from familiar computation to ZK circuit is tricky: at their core, ZK "circuits" are closer in spirit to circuits in hardware chips than normal computer programs (in CS lingo: ZK circuits are not specified in a Turing complete language). There are still many improvements to be made in ZK circuit design. This is where we need brilliant minds (that's you!) to join us and innovate.
Circuit Design: Arithmetizations
In a ZK circuit, a computation is represented as a collection of vectors together with imposed constraints (aka relations) between certain entries in the vectors. For example, computing 1 + 1 = 2
could be saying we have a vector (a, b, c)
and constraints
One way to think about constraints is that in a trustless environment, all entries of the vector can be adversarially selected, and your only safeguards are the constraints you impose on these numbers. Continuing the example, if we didn't have the last constraint a + b == c
, then I could supply c = 0
and all our constraints would still be satisfied!
There are different ways to translate a standard computation into such a collection of vectors and constraints - these are called arithmetizations. Nowadays there are developer "front-ends" with different levels of abstraction and customizability for translating a ZK circuit into an arithmetization.
The choice of an arithmetization and a front-end for writing in that arithmetization is the closest thing to choosing a programming language in ZK.
Prover-Verifier Dynamic
Once we have an arithmetization (vectors + constraints), the prover-verifier dynamic goes as follows:
The Prover sends the Verifier some commitment to the vectors and further commitments (details omitted) to the constraints.
The Verifier sends the Prover some random numbers
Prover uses the previous commitment, along with some cryptography, to give a proof that the supplied vectors actually satisfies the claimed constraints (aka, the computation did what it claimed to do).
Polynomial Commitments
Use elliptic curve cryptography (not quantum-secure, additional assumptions for security, slower runtime)
In the real world, after you have designed your [hardware] circuit its performance is governed by the laws of physics. In ZK, circuit performance is governed by the laws of math, and polynomial commitment schemes specify which laws apply.
Summary
To choose a ZK proving stack and start building:
Choose what polynomial commitment scheme to prover/verifier will use. Often this choice is baked into the choice of arithmetization.
Find an existing library that generates proofs from a given arithmetization. (Not recommended: rolling your own.)
The Axiom Stack
How does it all work on Ethereum?
Everything is largely the same. The main difference is that the Verifier is now a smart contract. This means that a smart contract runs the algorithm to verify a ZK SNARK supplied by the Prover. This enables powerful modularity and composability: the smart contract can programmatically perform further actions depending on whether the SNARK is verified correctly or not. For more details about how to produce SNARK verifier smart contracts, see this page.
Challenges
Why is ZK not used more prevalently if it can compress any computation? One reason is that only recently have the developer tooling and cryptographic systems become expressive and stable enough for people to actual build on top of them.
There are also some intrinsic challenges related to the nature of ZK circuits:
Overhead
While proof size and proof verification time are constant, the runtime to generate a proof is far from it. Right now, the estimated overhead of generating a proof for a particular computation is around 100-1000x. This is an active engineering problem with many facets for improvement:
Improving circuit design - this involves finding the optimal way to translate a computation into an arithmetization.
General performance engineering - some of the open source code used for proof generation was developed for other purposes, and serious performance optimization has not been applied yet.
Choice of proving system: the combination of arithmetization and polynomial commitment scheme forms a proving system. New research in this area can lead to step change improvements in performance.
Hardware: many core algorithms (Fast Fourier Transform, Elliptic Curve Multiscalar Multiplication) involved in the polynomial commitments can be parallelized or otherwise optimized using GPUs, FPGAs, ASICs.
VM / Turing completeness
in a circuit, we need to compute both f(b)
and g(b)
and then return a * f(b) + (1 - a) * g(b)
(assuming that a
is either 0
or 1
).
Numerical architecture
This difference means that:
Operations that are cheap for bits (bit shifts,
AND
,XOR
,OR
) are expensive to implement in ZK.Since ZK circuits still need to be implemented in traditional architectures, the implementation of things like finite field arithmetic adds another layer of overhead to all computations.
Closing Remarks
The potential of things ZK can build is tremendous and only increasing exponentially. We believe that a key ingredient in successfully building new ZK applications is to have a good understanding of the current features and limitations of ZKPs.
Further Reading
We haven't gone into the weeds of the math involved with ZK.
And of course, contact us to ask questions, give suggestions, and discuss further!
Last updated