Skip to main content

Intro

The purpose of this document is to give the reader mathematical, cryptographic, and programming context sufficient to become an effective practitioner of zero-knowledge proofs and ZK-SNARKs specifically.

Some fundamental mathematical objects

In this section we'll discuss the fundamental objects used in the construction of most ZK-SNARKs in use today. These objects are used extensively and without ceremony, so it's important to understand them very well.

If you find you don't understand something at first, don't worry: practice is often the best teacher and in using these objects you will find they will become like the bicycle you've had for years: like an extension of yourself that you use without a thought.

Fields

A field is a generalization of the concept of a number. To be precise, a field is a set FF equipped with

  • An element 0F0 \in F

  • An element 1F1 \in F

  • A function mul ⁣:F×FF\mathsf{mul} \colon F \times F \to F

  • A function add ⁣:F×FF\mathsf{add} \colon F \times F \to F

  • A function sub ⁣:F×FF\mathsf{sub} \colon F \times F \to F

  • A function div ⁣:F×(F{0})F\mathsf{div} \colon F \times (F \setminus \{ 0 \}) \to F

Note that the second argument to div\mathsf{div} cannot be 00. We write these functions in the traditional infix notation writing

  • xyxy or xyx \cdot y for mul(x,y)\mathsf{mul}(x, y)

  • x+yx + y for add(x,y)\mathsf{add}(x, y)

  • xyx - y for sub(x,y)\mathsf{sub}(x, y)

  • xy\frac{x}{y} for div(x,y)\mathsf{div}(x, y)

and we also write x1x^{-1} for div(1,x)\mathsf{div}(1, x) and x-x for sub(0,x)\mathsf{sub}(0, x).

Moreover all these elements and functions must obey all the usual laws of arithmetic, such as

  • x+(y+z)=(x+y)+zx + (y + z) = (x + y) + z

  • x+y=y+xx + y = y + x

  • x+(x)=0x + (- x) = 0

  • x+0=xx + 0 = x

  • x(yz)=(xy)zx (yz) = (x y)z

  • xy=yxx y = y x

  • xy=z\frac{x}{y} = z if and only if x=zyx = z y, assuming y0y \neq 0.

  • 1x=x1 \cdot x = x

  • x(y+z)=xy+xzx (y + z) = xy + xz

In short, FF should be an abelian group over ++ with 00 as identity and sub(0,)\mathsf{sub}(0, -) as inverse, F{0}F \setminus \{0 \} should be an abelian group over \cdot with 11 as identity and div(1,)\mathsf{div}(1, -) as inverse, and addition should distribute over multiplication. If you don't know what an abelian group is, don't worry, we will cover it later.

The point of defining a field is that we can algebraically manipulate elements of a field the same way we do with ordinary numbers, adding, multiplying, subtracting, and dividing them without worrying about rounding, underflows, overflows, etc.

In Rust, we use the trait Field to represent types that are fields. So, if we have T : Field then values of type T can be multiplied, subtracted, divided, etc.

Examples

The most familiar examples of fields are the real numbers R\mathbb{R} and the rational numbers Q\mathbb{Q} (ratios of integers). Some readers may also be friends with the complex numbers C\mathbb{C} which are also a field.

The fields that we use in ZKPs, however, are different. They are finite fields. A finite field is a field with finitely many elements. This is in distinction to the fields Q,R\mathbb{Q}, \mathbb{R}, and C\mathbb{C}, which all have infinitely many elements.

What are finite fields like?

In this section we'll try to figure out from first principles what a finite field should look like. If you just want to know the answer, feel free to skip to the next section.

Let's explore what a finite field can possibly be like. Say FF is a finite field. If nn is a natural number like 44 or 8787 we can imagine it as an element of FF by writing 1++1n\underbrace{1 + \dots + 1}_{n}.

Since FF is finite it must be the case that we can find two distinct natural numbers n<mn < m which are the same when interpreted as elements of FF.

Then, mn=0m - n = 0, which means the FF element 1++1mn\underbrace{1 + \dots + 1}_{m - n} is 00. Now that we have established that if you add 11 to itself enough times you get 00, let pp be the least natural number such that if you add 11 to itself pp times you get 00.

Now let's show that pp is prime. Suppose it's not, and p=abp = a b. Then since p=0p = 0 in FF, ab=0ab = 0. It is a fact about fields (exercise) that if ab=0a b = 0 then either aa is 0 or bb is 0. Either way, we would then get a natural number smaller than pp which is equal to 00 in FF, which is not possible since pp is the smallest such number. So we have demonstrated that pp is prime.

The smallest number of times you have to add 11 to itself to get 0 is called the characteristic of the field FF. So, we have established that every finite field has a characteristic and it is prime.

This gives us a big hint as to what finite fields should look like.

Prime order fields

With the above in hand, we are ready to define the simplest finite fields, which are fields of prime order (also called prime order fields).

Let pp be a prime. Then Fp\mathbb{F}_p (pronounced "eff pee" or "the field of order p") is defined as the field whose elements are the set of natural numbers {0,1,,p1}\{ 0, 1, \dots, p - 1\}.

  • 00 is defined to be 00

  • 11 is defined to be 11

  • add(x,y)=(x+y)modp\mathsf{add}(x, y) = (x + y) \mod p

  • sub(x,y)=(xy)modp\mathsf{sub}(x, y) = (x - y) \mod p

  • mul(x,y)=(xy)modp\mathsf{mul}(x, y) = (x \cdot y) \mod p

  • div(x,y)=(xyp2)modp\mathsf{div}(x, y) = (x \cdot y^{p - 2}) \mod p

Basically, you just do arithmetic operations normally, except you take the remainder after dividing by pp. This is with the exception of division which has a funny definition. Actually, a more efficient algorithm is used in practice to calculate division, but the above is the most succinct way of writing it down.

If you want, you can try to prove that the above definition of division makes the required equations hold, but we will not do that here.

Let's work out a few examples.

2 is a prime, so there is a field F2\mathbb{F}_2 whose elements are {0,1}\{ 0, 1 \}. The only surprising equation we have is

  • 1+1=01 + 1 = 0

Addition is XOR and multiplication is AND.

Let's do a more interesting example. 5 is a prime, so there is a field whose elements are {0,1,2,3,4}\{0,1,2,3,4\}. We have

12=1252=23=8=3\begin{aligned} \frac{1}{2} &= 1 \cdot 2^{5 - 2} \\ &= 2^3 \\ &= 8 \\ &= 3 \end{aligned}

where the last equality follows because everything is mod 5.

We can confirm that 3 is in fact 12\frac{1}{2} by multiplying 3 and 2 and checking the result is 1.

122=32=6=1\frac{1}{2} \cdot 2 = 3 \cdot 2 = 6 = 1

so that checks out.

In cryptography, we typically work with much larger finite fields. There are two ways to get a large finite field.

  1. Pick a large prime pp and take the field Fp\mathbb{F}_p. This is what we do in Mina, where we use fields in which pp is on the order of 22552^{255}.

  2. Take an extension of a small finite field. We may expand this document to talk about field extensions later, but it does not now.

Algorithmics of prime order fields

For a finite field Fp\mathbb{F}_p where pp fits in nn bits (i.e., p<2np < 2^n) we have

  • Addition, subtraction: O(n)O(n)

  • Multiplication O(n2)O(n^2)

  • Division O(n2)O(n^2) I believe, in practice significantly slower than multiplication.

Actually, on a practical level, it's more accurate to model the complexity in terms of the number of limbs rather than the number of bits where a limb is 64 bits. Asymptotically it makes no difference but concretely it's better to think about the number of limbs for the most part.

As a result you can see it's the smaller nn is the better, especially with respect to multiplication, which dominates performance considerations for implementations of zk-SNARKs, since they are dominated by elliptic curve operations that consist of field operations.

While still in development, Mina used to use a field of 753 bits or 12 limbs and now uses a field of 255 bits or 4 limbs. As a result, field multiplication became automatically sped up by a factor of 122/42=912^2 / 4^2 = 9, so you can see it's very useful to try to shrink the field size.