We talked about SNARKs, BabySNARK (do do doo) in particular. We further went into field extensions.
A SNARK is a "Succinct Non-interactive Arugment of Knowledge". Note that an "argument" is different than a "proof".
Consider a language
We usually formalize this relation using an arithmetic circuit
graph LR
x1[x1]; x2[x2]
m((x)); a((+))
x1 & x2 --> m
2 & m --> a
a --> 0
What we want from a SNARK is that it should have:
-
fast computation: something like
$\mathcal{O}(n \log n)$ or$\mathcal{O}(\sqrt{n})$ (e.g. Binius). -
low communication: sending the entire witness
$w$ would be the naive way, but we would like to be much efficient than doing that.
A problem is in the NP class if given a solution, we can verify it in polynomial time. For example, the problem of finding a Hamiltonian cycle in a graph is in NP class.
NP-complete problems are such problems that any NP problem can be reduce to an NP-complete problem. Some examples of NP-complete problems are:
- Boolean circuit satisfiability
- Arithmetic circuit satisfiability
- Three-coloring problem
In the context of SNARKS, the idea is the following reduction:
BabySNARK is a minimal implementation that has the SNARK "magic", and is fairly digestible to understand as a beginner. It is originally based on this paper, and the repository can be found at initc3/babySNARK.
A square span program is a program that takes a vector
In short:
We can describe boolean circuits using square span programs! The vector
For convenience, we usually write this as
Example: Consider the XOR operation
-
$a$ must be a bit, so$a(1 - a) = 0$ which we can write as a Square Span:$(2a - 1)^2 = 1$ -
$b$ must be a bit, so$b(1 - b) = 0$ which we can write as a Square Span:$(2b - 1)^2 = 1$ -
$c$ must be a bit, so$c(1 - c) = 0$ which we can write as a Square Span:$(2c - 1)^2 = 1$ -
$c = a + b - 2ab$ corresponds to XOR operation, but this is hard to write as a Square Span. Instead, notice that$a+b+c-1 \in {-1, 1}$ , so we can actually write the Square Span:$(a+b+c-1)^2 = 1$ .
So we have the system of equations:
Now lets write this as a matrix equation:
Square Span problems are "modular" in the sense that we can "connect" them together to form a larger Square Span problem. For example, continuing from the XOR above, if we wanted to compute another XOR of
Notice that the first 4 rows are the same as the previous matrix, and the last rows are for the new XOR operation. Further notice that 3rd and 5th rows are the same, making the constraint two times gains us nothing, we could optimize the constraints there if we wanted to.
What we often have is that people write "gadgets", such as boolean gates, field operations and such. Then, these "modular" gadgets can be connected together to form a larger problem. Then, a higher-level tool takes in a program that describes the problem and converts it into a SNARK-friendly format.
- Above, we wrote a problem in Square Span format. Square Span is not really efficient, but there are other mentioned formats are quite useful, also known as "SNARK-friendly" formats.
- In Groth16, you write the program in R1CS (Rank-1 Constraint System) format.
- In Plonk, you write the problem in Plonkish format.
- In zkVMs, you often write the problem in AIR (Algebraic Intermediate Representation) format.
Some tools that convert a problem into a SNARK-friendly format are:
So to recap Square Span, we have
I want to show that I know
We can make use of univariate polynomials here. Consider a polynomial
We will view the columns as evaluations of the polynomial at different points. Consider the matrix:
So for example at
The trick about our result polynomial
If this is indeed true, then
We have chosen our points carefully, i.e. they are in the subgroup of order
Also remember that we can use FFT to evaluate polynomials really efficiently, and now not only that but we also compute the vanishing polynomial efficiently as well!
As the last step, a random point
In other words
Warning
This part may be incomplete.
Within our protocol, we have made several MSMs. For this, we need a trusted setup specific to our circuit, We will have two "keys":
Verifying Key is used to verify a proof, it has:
-
$u_0(s)g_1, u_1(s)g_1, \ldots, u_n(s)g_1$ which are$n+1$ points as$P_0, P_1, \ldots, P_n$ . Thanks to these points, we can compute$V_u(s) = \sum u_k P_k$ (MSM) -
$u_0(s)g_2, u_1(s)g_2, \ldots, u_n(s)g_2$ which are$n+1$ points, but using the generator$g_2$ instead. $Z(s) g_2$ - a constant
$e(g_1, g_2)^{-1}$ $\beta \gamma g_1$ $\gamma g_2$
Proving Key is used to generate a proof, it has:
-
$g_1, s g_1, s^2 g_1, \ldots, s^n g_1$ which are$n+1$ points as$Q_0, Q_1, \ldots, Q_n$ . Thanks to these points, we can compute$h(s) = \sum s^k g$ (MSM) -
$u_{n+1}(s)g_1, u_{n+2}(s)g_1, \ldots, u_{n+m}(s)g_1$ which are$m$ points -
$u_{n+1}(s)g_2, u_{n+1}(s)g_2, \ldots, u_{n+1}(s)g_2$ which are$m$ points, but using the generator$g_2$ instead. -
$\beta u_{n+1}(s)g_1, \beta u_{n+2}(s)g_1, \ldots, \beta u_{n+m}(s)g_1$ which are$m$ points similar to step 2, but multiplied with a constant$\beta$ as well.
The secret values here are
-
Compute the commitment
$\boxed{V_w}_1 = V_w(s)g_1$ and$\boxed{V_w}_2 = V_w(s)g_2$ and$\boxed{B_w}_1 = B_w(s)g_1 = \beta V_w(s)g_1$ -
Compute
$p(x) / Z(x) = h(x)$ . This part can be done efficiently using FFT. We could get evaluations of$p$ and$Z$ on some random domain, and divide the evaluations, and then interpolate the result to obtain$h$ . This is faster than long-division of polynomials. -
Commit to the result
$\boxed{h}_1 = h(s)g_1$ . -
Output proof
$\pi = (\boxed{h}_1, \boxed{V_w}_1, \boxed{V_w}_2, \boxed{B_w}_1)$ .
The proof used 4 MSMs, and just outputs 4 curve elements. Not only this is much more efficient than sending the entire witness, but this proof size is constant as well! It does not matter how large your circuit is.
-
Parse proof to obtain
$\pi \to (\boxed{h}_1, \boxed{V_w}_1, \boxed{V_w}_2, \boxed{B_w}_1)$ . -
Check the pairing
$e(\boxed{V_w}_1, g_2) = e(g_1, \boxed{V_w}_2)$ to ensure that prover has used the same input for both commitments. -
Check
$e(\boxed{B_w}_1, \beta g_2) = e(\beta \gamma g_1, \boxed{V_w}_2)$ to ensure that prover did not cheat (thanks to$\beta$ ) -
Compute
$V_u(s)g_1 = \boxed{V_u}_1$ and$V_u(s)g_2 = \boxed{V_u}_2$ from the public inputs. -
Check
$e(\boxed{V_u}_1 + \boxed{V_w}_1, \boxed{V_u}_2 + \boxed{V_w}_2) e(g_1, g_2)^{-1} = e(\boxed{h}_1, \boxed{Z}_2)$ where$\boxed{Z}_2 = Z(s)g_2$ .
If all checks pass, the proof is valid!
BabySNARK is implemented in LambdaWorks! See https://github.com/lambdaclass/lambdaworks/tree/main/examples/baby-snark.
Recall that we had fields defined over integers modulo some prime
Consider a ring of polynomials over real numbers
What happens in this example is that, whenever the degree of polynomial in
When working with polynomials, we can define addition and multiplication laws as well. For the example above:
$(a_0 + a_1x) + (b_0 + b_1x) = (a_0 + b_0) + (a_1 + b_1)x$ $(a_0 + a_1x) + (b_0 + b_1x) = a_0b_0 + (a_0b_1 + a_1b_0) + a_1b_1x^2$
Now, that multiplication above has a term
In this case,
$a_0b_0 + (a_0b_1 + a_1b_0) + a_1b_1x^2 \bmod{x^2 + 1}$ results in$(a_0b_0 - a_1b_1) + (a_0b_1 + a_1b_0)x$ . This is actually equivalent to the Complex number multiplication! In other words,$x^2$ became$-1$ .
An irredicuble polynomial can not be factorized into smaller polynomials, doing that would imply that there is a root, in other words:
Another interesting fact about field extensions is that we still have the definitions for a multiplicative inverse! That is, for
In this example, we have used
$I(x) = x^2 + 1$ . We call our field extension a "degree-2 extension", or a "quadratic extension". When$(x, y) \in \mathbb{R}$ , we can view the field extension as a "$\mathbb{R}$-vector space of dimension 2".
A question one may ask here, why
There are some more efficient multiplication methods in field extensions, such as Karatsuba and Toom-Cook. The funny thing is, uou can even use FFT if you really want and it has the "best asymptotic complexity" for multiplication; but it requires a 1729 dimensional FFT! This result comes from the Schönhage-Strassen algorithm. This is not practical in real life usage, making this algorithm a Galactic algorithm.
Consider the finite field
-
$0 + 0 = 0$ ,$0 + 1 = 1$ ,$1 + 0 = 1$ ,$1 + 1 = 0$ . This is just our ordinary XOR operation. -
$0 \times 0 = 0$ ,$0 \times 1 = 0$ ,$1 \times 0 = 0$ ,$1 \times 1 = 1$ . This is just our ordinary AND operation.
So things are pretty simple as we can see, and quite efficient as we can use bitwise operations.
A binary field extension of degree
Instead, we can pick
0 + 0*x = (0,0) = 00
0 + 1*x = (0,1) = 01
1 + 0*x = (1,0) = 10
1 + 1*x = (1,1) = 11
Lets look at the multiplications of these elements:
* 00 10 01 11
00 00 00 00 00
10 00 10 01 11
01 00 01 11 10
11 00 11 10 01
This is the type of multiplication that Vitalik did in his post.
We can have much higher degrees of extensions as well, we just need an irreducible polynomial. For example,
Suppose you have the extension
The elements of this field extension would be
Suppose that you want to build a degree 12 extension over
-
Find an irreducible polynomial over
$\mathbb{F}_p$ of degree 12.
For example, in BN254 elliptic curve we have
$I(x) = x^{12} - 18x^6 + 82$ with which we can build the extension.
- Build extensions towers to obtain the desired degree.
For example, given
$\mathbb{F}_p$ how can we obtain an extension of degree 12?
$\mathbb{F}_p \to \mathbb{F}_{p^2}$ using$I(x) = x^2 + 1$ .$\mathbb{F}_{p^2} \to \mathbb{F}_{p^6}$ using$I(y) = y^3 - (9 + x)$ .$\mathbb{F}_{p^6} \to \mathbb{F}_{p^{12}}$ using$I(z) = z^2 - y$ .
Using any of these methods, the resulting extensions will be isomorphic!
Binius works over binary field extensions, and they use more tricks than the ones above to build their extension tower.
Recall that we talked about type-3 pairings
The trick of finding these extensions come from the embedding degree
For example, in BN254
$k = 12$ . This means that we can build a degree 12 extension over$\mathbb{F}_p$ and obtain a sub-group of order$r$ , because the multiplicative order of that group is$p^{12} - 1$ . In fact:
$G_T$ in the pairing for BN254 is a subgroup of$\mathbb{F}_{p^{12}}$ .$G_1$ will be points on the curve$(x, y) \ in \mathbb{F}_p \times \mathbb{F}_p$ , essentially using the group itself.$G_2$ will be points on the curve $(x, y) \ in \mathbb{F}{p^2} \times \mathbb{F}{p^2}$.
In a pairing operation, we have two parts:
-
Miller Loop: takes in a pair of points
$(P_1 \in G_1, P_2 \in G_2)$ and output a point in$F_{p^{k}}$ . -
Cofactor Clearing: takes the resulting point from Miller loop and map it to the subgroup of order
$r$ by exponentiating it to$(p^{k} - 1) / r$ .
The cost of exponentiation (i.e. cofactor clearing) is fixed, bu the Miller Loop has a variable cost.
In STARKs, we may work with small fields such as Baby Goldilocks, but take our "samples" from larger extensions of this field to achieve cryptographic security.
This is a really good library of cryptographic implementations: https://github.com/mratsim/constantine.