In this seventh video, I explain how we use our circuit polynomial in a protocol between a prover and a verifier to prove succinctly that vanishes on a number of specified points.
Stay tuned for part 9… Part 8 is here. Check the full series here.
cryptography, security, and random thoughts
Hey! I'm David, cofounder of zkSecurity, advisor at Archetype, and author of the Real-World Cryptography book. I was previously a cryptography architect of Mina at O(1) Labs, the security lead for Libra/Diem at Facebook, and a security engineer at the Cryptography Services of NCC Group. Welcome to my blog about cryptography, security, and other related topics.
In this seventh video, I explain how we use our circuit polynomial in a protocol between a prover and a verifier to prove succinctly that vanishes on a number of specified points.
Stay tuned for part 9… Part 8 is here. Check the full series here.
In this sixth video, I explain the compilation, or even compression, of a set of equations into a single polynomial. That polynomial represents all of our constraints, as long as it vanishes in an agreed set of points. With a polynomial in hand, we will be able to create a protocol with our polynomial-based proof system.
In this fifth video, I explain how we can “compile” an arithmetic circuit into something PLONK can understand: a constraint system. Specifically, a PLONK-flavored constraint system, which is a series of equations that must if equal to zero correctly describe our program (or circuit).
In this fourth video, I explain the “arithmetization” of our program into so-called arithmetic circuits. You can see this as “encoding” programs into math, so that we can use cryptography on them.
In this third video, I start by explaining what the protocol will use at the end: polynomials. It’ll give you a glimpse as to what direction we’ll be taking when we transform our program into something we can prove.
In this second video, I give some intuition on how to think about zero-knowledge proof systems, with the example of proving the solution of a sudoku, then I give an overview of what I’ll explain in this series of video.
I recently got into general-purpose zero-knowledge proof systems (cryptographic primitives that allow you to prove the execution of a program without revealing some of the inputs), specifically the state-of-the-art PLONK proof system. This is a series of video I made to explain what I understood and learned in the past few months. There might be some inaccuracies, so I apologize in advance for that. You can check all the videos via the playlist here: https://www.youtube.com/watch?v=RUZcam_jrz0&list=PLBJMt6zV1c7Gh9Utg-Vng2V6EYVidTFCC
In this first video, I simply explain what general-purpose zero-knowledge proofs are, specifically zk-SNARKs, and what PLONK is.
The inner product argument is the following construction: given the commitments (for now let’s say the hash) of two vectors and of size and with entries in some field , prove that their inner product is equal to .
There exist different variants of this inner product argument. In some versions, none of the values (, and ) are given, only commitments. In some other version, which is interesting to us and that I will explain here, only is unknown.
Inner products arguments are useful for several things, but what we’re using them for in Mina is polynomial commitments. The rest of this post won’t make too much sense if you don’t know what a polynomial commitment is, but briefly: it allows you to commit to a polynomial and then later prove its evaluation at some point . Check my post on Kate polynomial commitments for more on polynomial commitment schemes.
How does that translate to the inner product argument though? First, let’s see our polynomial as a vector of coefficients:
Then notice that
And here’s our inner product again.
The inner product argument protocol I’m about to explain was invented by Bootle et al. It was later optimized in the Bulletproof paper (hence why we unofficially call the first paper bootleproof), and then some more in the Halo paper. It’s the later optimization that I’ll explain here.
So before I get into the weeds, what’s the high-level? Well first, what’s a naive way to prove that we know the pre-image of a hash , the vector , such that ? We could just reveal and let anyone verify that indeed, hashing it gives out , and that it also verifies the equation .
Obliviously, we have to reveal itself, which is not great. But we’ll deal with that later, trust me. What we want to tackle first here is the proof size, which is the size of the vector . Can we do better?
The inner product argument reduces the opening proof by using an intermediate reduction proof:
Where the size of is half the size of , and as such the final opening proof () is half the size of our naive approach.
The reduction proof is where most of the magic happens, and this reduction can be applied many times ( times to be exact) to get a final opening proof of size 1. Of course the entire proof is not just the final opening proof of size 1, but all the elements involved in the reduction proofs. It can still be much smaller than the original proof of size .
So most of the proof size comes from the multiple reduction subproofs that you’ll end up creating on the way. Our proof is really a collection of miniproofs or subproofs.
To understand the protocol, you need to understand commitments. I’ve used hashing so far, but hashing with a hash function like SHA-3 is not great as it has no convenient mathematical structure. We need algebraic commitments, which will allow us to prove things on the committed value without revealing the value committed. Usually what we want is some homomorphic property that will allow us to either add commitments together or/and multiply them together.
For now, let’s see a simple non-hiding commitment: a Pedersen hash. To commit to a single value simply compute:
where the discrete logarithm of is unknown. To open the commitment, simply reveal the value .
We can also perform multi-commitments with Pedersen hashing. For a vector of values , compute:
where each is distinct and has an unknown discrete logarithm as well. I’ll often shorten the last formula as the inner product for and . To reveal a commitment, simply reveal the values .
Pedersen hashing allow commitents that are non-hiding, but binding, as you can’t open them to a different value than the originally comitted one. And as you can see, adding the commitment of and gives us the commitment of :
which will be handy in our inner product argument protocol
Here are the settings of our protocol. Known only to the prover, is the secret vector
The rest is known to both:
For the sake of simplicity, let’s pretend that this is our problem, and we just want to halve the size of our secret vector before revealing it. As such, we will only perform a single round of reduction. But you can also think of this step as being already the reduction of another problem twice as large.
We can picture the protocol as follows:
Does that make sense? Of course what’s interesting to us is the proof, and how the prover uses that random .
First, the prover cuts everything in half. Then they use to construct linear combinations of these cuts:
This is how the problem is reduced to .
At this point, the prover can send , , and and the verifier can check if indeed . But that wouldn’t make much sense would it? Here we also want:
The verifier can compute as they have everything they need to do so.
What about , the commitment of which uses the new basis. It should be the following value:
So to compute this new commitment, the verifier needs:
What about ? Recall:
So the new inner product should be:
Similarly to , the verifier can recompute from the previous value and two scalar values and which the prover needs to provide.
So in the end, the proof has becomes:
We can update our previous diagram:
In our example, the naive proof was to reveal which was 4 field elements. We are now revealing instead 2 + 2 + 2 = 6 field elements. This is not great, but if was much larger (let’s say 128), the reduction in half would still be of 64 + 2 + 2 = 68 field elements. Not bad no? We can do better though… Stay tuned for the next post.
PLONK is the state of the art when it comes to general-purpose proof system. While it was released in 2019, the paper has recently received some updates, and the scheme is still evolving (with Aztec announcing an UltraPLONK version coming soon). This is the scheme that we use at Mina to compress the size of the blockchain from gigabytes to a fixed size of a dozen kilobytes.
While I don’t think the core ideas are the hardest to understand, the scheme compresses a myriad of optimization which makes it hard to parse. In this post I hope to add some clarity to some aspects of the scheme. Note that I assume that you have some knowledge of how PLONK works.
Eventually, the idea of PLONK is to prove that some polynomial vanishes on some domain (and I will ignore the permutation argument, which is just another proof). To prove that, we reduce the problem to some other problem. Incrementaly, it looks like this:
To prove the last statement, the prover uses of polynomial commitment scheme (specifically, the KZG scheme) to commit to the polynomial and . The prover then sends the commitments to the verifier. At that point, the verifier has to check that for some random point
This is done by sending a random point to the prover and doing an “opening” of the commitments at this point: the prover sends the values and as well as a proof that these are the correct evaluations.
This is in essence the PLONK protocol, except that this is not really what happens in the paper…
The newer PLONK actually does one more reduction of the last statement:
The last two steps is an optimization (called Maller’s optimization) that removes the need for the prover to send , as the verifier can use the commitment to to produce a commitment to (to verify the opening proof).
These additional reductions moved us from a protocol in which the prover sends openings to let the verifier check an identity by themselves, to a protocol where the prover simply sends openings.
To verify the opening of for , the verifier will have to reconstruct a commitment to first. That’s easy, it is:
which will use:
If you’ve read PLONK, you’ve noticed that the prover actually doesn’t send a commitment to directly, because is too large and polynomial commitment schemes have an upperbound fixed during the trusted setup. (By the way, is too large because the permutation argument makes it three times as large due to the three witness polynomials.) To circumvent that limitation, the polynomial is split into three smaller polynomials such that:
This means that in our previous protocol, we can’t prove directly that is a root of
instead we have to prove the equivalent that is a root of
This is not great, as the prover cannot produce a commitment to anymore. The reason is that and cannot be committed as they’re larger than the upperbound of our polynomial commitment. Instead, notice that since the verifier already knows these values, so they can pre-evaluate them at and ask instead for a proof that:
which is a fine request, as the verifier can produce the commitment of needed to verify the opening proof:
At this point, the protocol looks more like this:
The big proof in PLONK really boils down to two things:
Since the polynomial needs to be constructed such that:
the prover and the verifier perform a “polynomial dance” to construct the polynomial together. The end product sorts of looks like this:
where are private polynomials that the prover constructs, commits, and sends to the verifier; and are public polynomials (the selector polynomials) that both the verifier and the prover can construct (and commit to if necessary).
So the end protocol looks more like this:
And as in the previous section, the verifier needs to reconstruct a commitment to before being able to ask for an opening, which is now impossible as we’re dealing with multiplication of commitments
but since the prover sends the evaluations of at (with proofs), the verifier can use that to simplify the polynomial to:
Finally, the verifier can produce the commitment of as:
There’s much more to PLONK. I’ve skipped the circuit part, the permutation argument, I’ve also ignored the big pairing equation at the end. These will be subjects for another post :)
In the PLONK paper, they make use of an optimization from Mary Maller in order to reduce the proof size. This is a note explaining this optimization. If you have no idea what these words are, you might want to skip reading this post :)
Maller’s optimization is used in the “polynomial dance” between the prover and the verifier to reduce the number of openings the prover send.
Recall that the polynomial dance is the process where the verifier and the prover form polynomials together so that:
In the dance, the prover can additionally perform some steps that will keep the same properties but with reduced communication.
Let’s see the protocol where Prover wants to prove to Verifier that
given commitments of .
A shorter proof exists. Essentially, if the verifier already has the opening h1(s), they can reduce the problem to showing that
given commitments of and evaluation of at a point .
Why couldn’t the prover open the polynomial directly?
By doing
The problem here is that you can’t multiply the commitments together without using a pairing (if you’re using a pairing-based polynomial commitment scheme), and you can only use that pairing once in the protocol.
If you’re using an inner-product-based commitment, you can’t even multiply commitments anyway.
https://eprint.iacr.org/2019/953.pdf

For completion, the lemma 4.7:
