# Understanding PLONK [PLONK](https://eprint.iacr.org/2019/953.pdf) 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](https://aztec.network/) version coming soon). This is the scheme that we use at [Mina](https://minaprotocol.com/) 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. ## How PLONK works, the short version Eventually, the idea of PLONK is to prove that some polynomial $f(x)$ vanishes on some domain $H \subset \mathbb{F}$ (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: - Proving the previous statement is equivalent to proving that the polynomial is divisible by $Z_H(x)$, the polynomial that has all the elements of $H$ as roots (also called vanishing polynomial). - Which is equivalent to proving the following identity (for some quotient polynomial $t$): $$f(x) = t(x) \cdot Z_H(x) \; \; \; \forall x \in \mathbb{F}$$ - Which is equivalent to proving the identity on some random point $z$ (thanks to the [Schwartz-Zippel lemma](https://www.cryptologie.net/article/507/the-missing-explanation-of-zk-snarks-part-1/)): $$f(z) = t(z) \cdot Z_H(z)$$ To prove the last statement, the prover uses of polynomial commitment scheme (specifically, the [KZG scheme](https://cryptologie.net/article/525/pairing-based-polynomial-commitments-and-kate-polynomial-commitments/)) to commit to the polynomial $f$ and $t$. The prover then sends the commitments to the verifier. At that point, the verifier has to check that for some random point $z$ $$ f(z) = t(z) \cdot Z_H(z) $$ This is done by sending a random point $z$ to the prover and doing an "opening" of the commitments at this point: the prover sends the values $f(z)$ and $t(z)$ as well as a proof that these are the correct evaluations. ```sequence Prover->Verifier: com(f), com(t) Note right of Verifier: generates random z Verifier->Prover: z Prover->Verifier: f(z), t(z) Prover->Verifier: proofs of opening Note right of Verifier: checks that \n sum f(z) = t(z)z_H(z) ``` This is in essence the PLONK protocol, except that this is not really what happens in the paper... ## More reductions The newer PLONK actually does one more reduction of the last statement: - As per the previous section: we want to prove that $$f(z) = t(z) \cdot Z_H(z)$$ - Which is equivalent to proving that $z$ is a root of the polynomial $$f(x) - t(x) \cdot Z_H(x)$$ - Since the verifier already knows one of the polynomial ($Z_H$), they can evaluate it in advance. So the previous statement is equivalent to proving that $z$ is a root of $$r(x) = f(x) - t(x) \cdot Z_H(z)$$ The last two steps is an optimization (called [Maller's optimization](https://www.cryptologie.net/article/526/maller-optimization-to-reduce-proof-size/)) that removes the need for the prover to send $t(z)$, as the verifier can use the commitment to $t$ to produce a commitment to $r$ (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**. ```sequence Prover->Verifier: com(f), com(t) Note right of Verifier: generates random z Verifier->Prover: z Prover->Verifier: f(z), r(z) = 0 Prover->Verifier: proofs of opening Note right of Verifier: reconstruct r(x) and \n validate opening proofs ``` <details> <summary>Click to see how it looks in round 5 in the paper. </summary> ![](https://i.imgur.com/wGkWziy.png) If we remove the other openings that have been aggregated here it is indeed an opening proof of $r(z) = 0$: $$\frac{r(X) - 0}{X - z}$$ </details> <br> To verify the opening of $r$ for $x = z$, the verifier will have to reconstruct a commitment to $r$ first. That's easy, it is: $$com(r) = com(f) - com(t) \cdot Z_H(z)$$ which will use: - the commitment to $f$ received during the protocol - the commitment to $t$ received during the protocol - the evaluation of $Z_H(x)$ at $x=z$ which they can do by themselves ## Not so fast... t is too large If you've read PLONK, you've noticed that the prover actually doesn't send a commitment to $t$ directly, because $t$ is too large and polynomial commitment schemes have an upperbound fixed during the trusted setup. (By the way, $t$ 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 $t$ is split into three smaller polynomials $t_{lo}, t_{mid}, t_{hi}$ such that: $$ t(x) = t_{lo}(x) + x^n \cdot t_{mid}(x) + x^{2n} \cdot t_{hi}(x) $$ This means that in our previous protocol, we can't prove directly that $z$ is a root of $$r(x) = f(x) - t(x) \cdot Z_H(z)$$ instead we have to prove the equivalent that $z$ is a root of $$r(x) = f(x) - [t_{lo}(x) + x^n \cdot t_{mid}(x) + x^{2n} \cdot t_{hi}(x)] \cdot Z_H(z)$$ This is not great, as the prover cannot produce a commitment to $r$ anymore. The reason is that $x^n$ and $x^{2n}$ 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 $z$ and ask instead for a proof that: $$r(x) = f(x) - [t_{lo}(x) + z^n \cdot t_{mid}(x) + z^{2n} \cdot t_{hi}(x)] \cdot Z_H(z)$$ which is a fine request, as the verifier can produce the commitment of $r$ needed to verify the opening proof: $$ com(r) = com(f) - [com(t_{lo}) + z^n \cdot com(t_{mid}) + z^{2n} \cdot com(t_{hi})] \cdot Z_H(z) $$ At this point, the protocol looks more like this: ```sequence Prover->Verifier: com(f) Prover->Verifier: com(t_lo), com(t_mid), com(to_hi) Note right of Verifier: generates random z Verifier->Prover: z Prover->Verifier: f(z), r(z) = 0 Prover->Verifier: proofs of opening Note right of Verifier: reconstruct r(x) and \n validate opening proofs ``` ## Uh-oh, what about f? The big proof in PLONK really boils down to two things: 1. The permutation argument, which links the wires in our circuit. I ignore this proof in the post. 2. the main polynomial $f$, which is our circuit. Since the polynomial $f$ needs to be constructed such that: * it does not leak any non-public information to the verifier * it does not allow the prover to change fixed parts of the circuit the prover and the verifier perform a "_polynomial dance_" to construct the polynomial together. The end product sorts of looks like this: $$ f(x) = a(x) q_L(x) + b(x) q_R(x) + q_M(x) a(x) b(x) + q_O(x) c(x) + q_C(x) $$ where $a, b, c$ are private polynomials that the prover constructs, commits, and sends to the verifier; and $q_L, q_R, q_M, q_O, q_C$ 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: ```sequence Prover->Verifier: com(a), com(b), com(c) Prover->Verifier: com(t_lo), com(t_mid), com(to_hi) Note right of Verifier: generates random z Verifier->Prover: z Prover->Verifier: a(z), b(z), c(z), r(z) = 0 Prover->Verifier: proofs of opening Note right of Verifier: reconstruct r(x) and \n validate opening proofs ``` And as in the previous section, the verifier needs to reconstruct a commitment to $r$ before being able to ask for an opening, which is now impossible as we're dealing with multiplication of commitments $$ \begin{align} r(x) = \; &a(x) q_L(x) + b(x) q_R(x) + a(x) b(x) q_M(x) + c(x) q_O(x) + q_C(x) \\ & - [t_{lo}(x) + x^n \cdot t_{mid}(x) + x^{2n} \cdot t_{hi}(x)] \cdot Z_H(z) \end{align} $$ but since the prover sends the evaluations of $a, b, c$ at $z$ (with proofs), the verifier can use that to simplify the polynomial $r$ to: $$ \begin{align} r(x) = \; &a(z) q_L(x) + b(z) q_R(x) + a(z) b(z) q_M(x) + c(z) q_O(x) + q_C(x) \\ & - [t_{lo}(x) + x^n \cdot t_{mid}(x) + x^{2n} \cdot t_{hi}(x)] \cdot Z_H(z) \end{align} $$ Finally, the verifier can produce the commitment of $r$ as: $$ \begin{align} com(r) = \; &a(z) com(q_L) + b(z) com(q_R) + a(z) b(z) com(q_M) + c(z) com(q_O) + com(q_C) \\ & - [com(t_{lo}) + z^n \cdot com(t_{mid}) + z^{2n} \cdot com(t_{hi})] \cdot Z_H(z) \end{align} $$ 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 :)