Building a SNARK

There are various paradigms on building SNARKs, but the general paradigm is two step:

  1. A functional commitment scheme, where most of cryptography takes place
  2. A suitable interactive oracle proof (IOP), where most of the information theory takes place

Functional Commitment Scheme

Well, first, what is a commitment scheme? A cryptographic commitment is like a physical-world envelope. For instance, Bob can put a data in an envelope, and when Alice receives this envelope she can be sure that Bob has committed to whatever value is in it. Alice can later reveal that value.

The commitment scheme has two algorithms:

  • for some randomly chosen

The scheme must have the following properties:

  • Binding: cannot produce two valid openings for
  • Hiding: reveals nothing about the committed data

There is a standard construction using hash functions. Fix a hash function where

Committing to a function

Choose a family of functions . What does it really mean to commit to a function? Well, consider the following interaction:

sequenceDiagram
	actor P as Prover
	actor V as Verifier

	note over P: f ∈ F
	P ->> V: com_f := commit(f, r)
	V ->> P: x ∈ X
	P ->> V: y ∈ Y, proof π
	note over V: Accept or Reject

Here, the proof is to show that and .

More formally, a functional commitment scheme for :

  • is public parameters
  • is commitment to with
    • this should be a binding scheme
    • optionally, it can be hiding, which is good for zk-SNARK
  • with a prover and verifier , where for a given and :
    • , which is a SNARK for the relation: and and .
    • Basically, the system is a SNARK

Function Families

There are 3 very important functional commitment types:

  • Polynomial Commitments: Committing to a univariate polynomial where that fancy notation stands for the set of all univariate polynomials of degree at most .
  • Multilinear Commitments: Committing to a multilinear polynomial in which is the set of all the multilinear polynomials in at most variables.
    • A multilinear polynomial is when all the variables have degree at most 1. Here is an example: .
  • Linear Commitments: Committing to a linear function which is just the dot product of two vectors.

Different SNARK systems may use different commitments. Note that linear commitments can be transformed into multilinear commitments, and those can be transformed into polynomial commitments. A good exercise!

From here on we will talk about Polynomial Commitments.

Polynomial Commitment Scheme (PCS)

A PCS is a functional commitment for the family . The prover commits to a univariate polynomial , later, they can prove that for some public . As this is a SNARK, the proof size and verifier time should be .

  • Using basic elliptic curves: Bulletproofs
  • Using bilinear groups: KZG (trusted setup) (2010), Dory (transparent) (2020)
  • Using groups of unknown order: Dark (20)
  • Using hash functions only: based on FRI

We will focus on KZG, as it is much simpler and commonly used.

KZG PCS

The name stands for Kate-Zaverucha-Goldberg. It operates on a cyclic group of order where is the generator. Note that in such a setting, .

Setup

The setup phase works as follows:

  1. Sample random
  2. Delete or you get in trouble! (trusted setup)

Note that you can't do something like because division is not defined for these guys!

Commitment

The commitment phase is as follows:

  • Compute , but wait we don't have so what do we do?
  • We have a univariate polynomial
  • We use the public parameters to compute
  • If you expand in there, you will notice that the entire thing is equal to .
  • This is a binding commitment, but it is not hiding for now.

Evaluation

We have where at this point. The evaluation phase will work as follows:

  • Prover has the goal of proving and generate some proof .
  • Verifier will verify that proof.

The trick is to see that if then is a root of . It is because . There is a very well known result of this, that divides . This also means that there is quotient polynomial such that .

With this, the Prover will calculate the quotient polynomial and will commit to it to find . This will be the proof . The verifier will accept the proof only if .

Note that verifier is using here, even though it was secret. The truth is, it is not actually using but instead uses a pairing, and the only thing verifier needs to know for that is and , which are part of public parameters .

Computing is pretty expensive for large , and this part takes most of the computational time of the entire algorithm.

Remarks

KZG has been generalized for committing to -variate polynomials (PST 2013). There are many more generalizations of it. KZG also provides batch proofs, where a prover can prove a bunch of commitments in a single computation.

The difficulty with KZG is that it requires a trusted setup for , and size is linear in .

Polynomial Interactive Oracle Proof

Now we are at the second part of a SNARK. Let be some arithmetic circuit. Let . Poly-IOP is a proof system that proves as follows:

  1. We preprocess the circuit as similar to previous steps. where .
  2. An interactive proof is played as shown below:
sequenceDiagram
	actor P as Prover P(S_p, x, w)
	actor V as Verifier V(S_v, x)

	loop i = 1, 2, ..., (t-1)
	P ->> V: f_i ∈ F
	note over V: r_i ← F_p
	V ->> P: r_i
	end

	P ->> V: f_t ∈ F
	note over V: verify^(f_{-s}, ..., f_t)(x, r_1, ..., r_(t-1))

Here the in the end is just an efficient function that can evaluate at any point in . Also note that is randomly chosen only after the prover has committed to .

The Verifier here is just creating random numbers. Using Fiat-Shamir transform, such an interactive proof can be made non-interactive!

As usual, we expect the following properties:

  • Complete: if then verifier always accepts.
  • Knowledge Sound: Let . For every that convinces the verifier with some non-negligible probability , there is an efficient extractor such that for some negligible :

  • Zero-Knowledge: This is optional, but will be required for a zk-SNARK.

The Resulting SNARK

The resulting SNARK will look the following: there will be number of polynomials committed, and number of evaluation queries (points) for the verification. This is parameterized as POLY-IOP.

For the SNARK:

  • Prover send polynomial commitments
  • During Poly-IOP verify, the PCS is run times.
  • Note that is made non-interactive via Fiat-Shamir transform.

The length of the SNARK proof is polynomial commitments + evaluation proofs.

  • Prover Time:
  • Verifier Time:

Usually, both so these times are really short, in fact, in constant time w.r.t the polynomial count which is awesome.


We will build a Poly-IOP called Plonk. Plonk + PCS will make up a SNARK, and optionally can be extended to a zk-SNARK.

Key Observations

We can do zero test and equality test on committed polynomials, and these are used on almost all SNARK constructions.

Zero Test

A really key fact for (for some random non-zero polynomial with degree at most ) is as follows:

  • for it holds that

We know that has at most roots. is chosen at random from a size , do the probability that "hits" a root value is easy to see that .

Suppose that and . Then, is negligible! So it is really unlikely that a randomly chosen field element will be the root for .

With this in mind, if you do get for then is identically zero with very high probability. This gives you a simple zero test for a committed polynomial!

This condition holds even for multi-variate polynomials!

Equality Test

Here is a related observation from the Zero Test.

Let . For , if then with very high probability! This comes from the observation above (think of ).

Useful Proof Gadgets

Let be a primitive -th root of unity (meaning that . Set . Let and where .

There are efficient poly-IOPs for the following tasks:

  1. Zero Test: Prove that is identically zero on
  2. Sum Check: Prove that where the verifier has .
  3. Product Check: Prove that where the verifier has .

We will only look at zero test.

Zero Test on

There is a cute lemma that will be used here: is zero on if and only if is divisible by .

sequenceDiagram
	actor P as Prover
	actor V as Verifier

	note over P: q(X) ← f(X) / (X^k - 1)
  	P ->> V: q
	note over V: r ← F_p
	V ->> P: r
	note over P: evaluate q(r) and f(r)
	P ->> V: q(r), f(r)
	note over V: accept if f(r) = q(r) * (r^k - 1)

This protocol is complete and sound, assuming is negligible.

PLONK

PLONK is a Poly-IOP for a general circuit .

Step 1: Compile Circuit to a Computation Circuit

Consider the following circuit (gate fan-in: 2, meaning that gates can take 2 inputs):

flowchart LR
	x1((x1)) --> +1[+ g0]
	x2((x2)) --> +1
	x2 --> +2[+ g1]
	w1((w1)) --> +2
	+1 --> x[x g2]
	+2 --> x
	x --> r(( ))

There are 3 gates here (namely ), 2 statement inputs and a witness input . The circuit outputs . Consider giving . Here is what that computation would look like:

flowchart LR
	x1((x1)) -- 5 --> +1[+ g0]
	x2((x2)) -- 6 --> +1
	x2 -- 6 --> +2[+ g1]
	w1((w1)) -- 1 --> +2
	+1 -- 11 --> x[x g2]
	+2 -- 7 --> x
	x -- 77 --> r((77))

We would like to obtain a computation trace of this circuit evaluation. A computation trace is simply a table that shows the inputs, and the state of each gate (input1, input2, output). The output of the circuit is the output of the last gate in the circuit. Here is the computation trace for the circuit evaluation above:

Inputs561
Gate 05611
Gate 1617
Gate 211777

At this point, we can forget about the circuit and focus on proving that a computation trace is valid. Note that input count does not have to be equal to number of inputs & output of a gate.

Step 1.5: Encoding the Trace as a Polynomial

First, some notation:

  • is the total number of gates in circuit
  • is equal to which is total number of inputs to
  • Let which gives us the number of entries in the computation trace. In the example, that is .

The plan: the prover will interpolate a polynomial that encodes the entire trace, such that:

  1. encodes all inputs: for .
  2. encodes all wires: :
    1. gives the left input to gate #
    2. gives the right input to gate #
    3. gives the output of gate #

Prover uses FFT (Fast Fourier Transform) to compute the coefficients of in time , almost in linear time!

Step 2: Proving validity of

Prover needs to prove that is a correct computation trace, which means the following:

  1. encodes the correct inputs
  2. Every gate is evaluated correctly
  3. The "wiring" is implemented correctly
  4. The output of last gate is 0

(1) encodes the correct inputs

Remember both prover and verifier has the statement . They will interpolate a polynomial that encodes the -inputs to the circuit:

  • for

Constructing takes time proportional to the size of input .

In our example: so is quadratic.

Next, they will agree on the points encoding the input:

Prover will prove (1) by using a zero-test on to prove that:

  • for all

(2): Every gate is evaluated correctly

The idea here is to encode gate types using a selector polynomial . Remember that in our example we encoded the two gate inputs and an output as to the power for some gate . Now, we will encode the "types" of these gates.

Define such that :

  • if gate is an addition gate +
  • if gate is a multiplication gate x

In our example, , so is a quadratic polynomial.

Now, we make a really nice observation: it should hold that:

Here, are left input, right input and output respectively.

Prover will use a zero-test on the set to prove that :

(3) The wiring is correct

What do we mean by wiring? Well, if you look at the circuit (or the table) you will notice that some outputs become inputs on other gates. For example, the input 6 is a right input for gate 0, and a left input for gate 1 and such. Prover will have to prove that this wiring has been done correctly.

For that, the wires of are encoded with respect to their constraints. In our example:

Define a polynomial that implements a rotation:

Why we do this fantastic thing is due to a lemma; if then the wire constraints are satisfied.

However, there is a problem: has degree but we want prover to work in linear time only! PLONK uses a very nice trick: use product check proof to reduce this to a constraint of linear degree. This trick is called the "PLONK Permutation" trick.

(4) Output of last gate is 0

Proving the last one is easy, just show .

Final PLONK Poly-IOP

In the setup phase:

  • where

The prover has and the verifier has .

  • The prover will build and give the commitment to the verifier.
  • The verifier will then build

Finally, the prover will prove the four things described before:

  • Inputs

  • Gates

  • Wires

  • Output

There is a theorem that shows this PLONK Poly-IOP is knowledge sound and complete! The resulting proof is around 400 bytes, and verification takes around 6ms. PLONK can actually handle circuits with more general gates than + and x, although we only used those operations in our gate constraints. The resulting SNARK can be made into a zk-SNARK, though it is not covered here.

There is also something called PLOOKUP: efficient Poly-IOP for circuits with lookup tables, which is very very useful.