Try   HackMD

SHPlonK explainer

There are a lot of low-level details in PlonK which typically get low attention (or are completely swept under the rug) in many popular expositions.

Today, I want to talk about a different (from canonical PlonK paper GWC19) opening scheme, suggested in the subsequent work BDFG21. I will not discuss the PlonK itself (though, a bit of familiarity will help a lot in understanding this text).

Notably, this approach is used in practice in many currently existing PlonK systems; but explanations tend to follow GWC19. So maybe it makes sense to do a some sort of writeup on this.

This explainer is first and foremost for me (best way of understanding something is trying to explain it!); though I hope it will be useful for the general audience.

Recall: PlonK

Plonkish protocols

Plonkish protocols, generally, look like this:

  1. Prover sends commitments to some polynomials
  2. Verifier responds with some challenges
  3. (they repeat this for some amount of rounds)
  4. Prover opens these polynomials in some points, and verifier checks some equations on these openings.

In fact, typically these equations are supposed to hold on the polynomials themselves, and their rotations, where

rotk(P)(x)=P(μkx), with
μ
being a primitive root of unity of order
n
.

Therefore, in general, to check these equations, in the last step, verifier sends a challenge point

x and prover opens the polynomials
Pi
in points
μkx
for
kSi
, where
Si
is set of all rotations of
Pi
present in the constraint.

I did not talk about quotient polynomial

H here, but it is naturally included in this framework, so this is a non-issue.

KZG commitment scheme

There are actually few closely related things called KZG commitment scheme, we will talk about one used in PlonK:

SRS:

(G,τG,τ2G,...,τn1G)VecG1;(H,τH)VecG2.

Here,

τ is a toxic waste element. Also, optionally we could have more powers of tau in the second group, but this is not necessary in what we are going to do (but useful in some other protocols, like proving that polynomial has some particular degree).

We will also denote

Gi=τiG,
Hi=τiH
. For a polynomial
P(t)
its KZG commitment is defined as:

[P(t)]1=P(τ)G=degPi=0piGi

Now, we would like to describe how to "open" a commitment - i.e. prove that a committed polynomial satisfies

P(x)=y for some field elements
x,y
.

This is done as follows: prover computes

Q(t)=P(t)ytx, and sends commitment to it (called "opening proof").

Verifier, then, checks:

[P]1[y]1,[1]2 =
[Q]1,[tx]2
.

Let's rewrite it in a bit more convenient way:

[P]1yG0,H0 =
[Q]1,H1xH0

And further, using bilinearity, open up the rhs and reorganize the check a bit:

[P]1yG0,H0 =
[Q]1,H1[Q]1,xH0

[P]1yG0,H0 =
[Q]1,H1x[Q]1,H0

[P]1yG0+x[Q]1,H0 =
[Q]1,H1

This is a very convenient way of representing the check, because you can notice that in these equations the right term in the pairing is always constant (

H0 and
H1
respectively), which means that we can, potentially, batch these equations together.

I.e., if we had multiple such equations, for multiple polynomials, we could add them up with random coefficients and only then check. Moreover, this addition would be only cheap

G1 operations.

Notably, any homomorphic polynomial commitment scheme admits batching for the opening in the same point - but here were can do it even for different points.

Therefore, final PlonK verifier costs 2 pairings, and an MSM (random linear combination) of size total amount of points opened.

Here, I will talk about SHPlonK, which is a way of reducing the size of this MSM using multi-opening arguments.

SHPlonK

Before we proceed, let's discuss how to open a polynomial in multiple points simultaneously.

Let's denote as

Y(t) a polynomial which satisfies
Y(xi)=yi
. We would like to prove that P(t) - Y(t) is divisible by
ZS(t)
, where
ZS=i(txi)
is a vanishing polynomial of a set
S={x1,...,xk}
.

This could be achieved, for example, if we had a more extended SRS (having more powers of tau in the second group) - we would just write something like:

[P]1[Y]1,[1]2=[Q(t)]1,[ZS(t)]2.

There are tricks in SHPlonK paper explaining how to batch these equations (but still inconvenient because they involve a lot of

G2 operations).

But there is also an alternative idea (which I will try to first explain as how I understand it, and then check against the paper).

So, we have commitments to polynomials:

[P]1,[Q]1. In order to check the required equation, let's just ask the verifier for a challenge point
z
, open
[P]1,[Q]1
there, and check that
P(z)Y(z)=Q(z)ZS(z)
!

This, of course, still requires computing

Y(z),ZS(z), but it is much faster than doing MSM (because it is just a computation over a field). The fact that we need to both normally open
P
and
Q
is a bit inconvenient, i.e. this only gives an advantage if our set
S
was
3
points or larger.

Now, armed with our "first impression" argument, let's take a look what the authors of SHPlonK actually do in their paper. Let's denote the union of

Si's as
T
.

  1. Prover: send commitments
    [P1]1,...,[Pn]1
    and openings
    Yi(t)
    in sets
    Si
    .
  2. Verifier: send random challenge
    γ
    for random linear combination.
  3. Prover: compute
    P(t)=iγiZTSi(t)(Pi(t)Yi(t))
    . // notice that we have multiplied differences by
    ZTS
    , which makes the whole thing divisible by
    ZT(t)
    .
    Denote
    H(t)=P(t)ZT(t)
    , and send commitment
    W=[H]1
    .
  4. Verifier: send random challenge
    z
    .
  5. Prover: compute
    Pz(t)=iγiZTSi(z)(Pi(t)Yi(z))
    (we are aiming to do the same thing I've described before, but for now evaluated it in
    z
    only partially).
    Define
    L(t)=Pz(t)ZT(z)H(t)
    . It is clearly vanishing in
    t=z
    , so let's send a commitment
    W=[L(t)tz]
    .
  6. Verifier: computes
    F=iγiZTSi(z)([Pi]1[Yi(z)]1)ZT(z)W
    , and checks:
    F,[1]2=W,[tz]2
    .

Let's take a deep breath; first of all, to see how this is related to what I've said before, let's imagine we only had a single polynomial, and a single set

S. Then,
H
has a role of
Q
in our previous naive scheme. Nonetheless, already in this case, the SHPlonK is a bit different. Let's spell it out:

  1. Prover: send commitments to
    P
    and
    Q=P(t)Y(t)ZS(t)
    .
  2. Verifier: send
    z
    .
  3. Prover: prepare proof
    W=[P(t)Y(z)ZS(z)Q(t)tz]1
    .
  4. Verifier: naturally, check
    W,[tz]2=[P]1[Y(z)]1ZS(z)[Q]1,[1]2
    .

That's similar to the naive protocol I've described, but we do not open

P and
Q
in
z
independently.

Let's abstract a bit: imagine we had some (committed) polynomials

P1,...,Pn, and low-degree public polynomials
f1,...,fn
, and we wanted to prove that
ifi(t)Pi(t)=0
. Then, we want to do something which (in a different context) is known as Mary Maller's optimization: after verifier sends us
z
, we just need to prove that
ifi(z)Pi(t)
vanishes in a point
z
. Using the fact that our
fi
-s our public, and commitments are homomorphic, we will now only need to open
ifi(z)[Pi]1
, which roughly ~ doubles our efficiency.

// Mary Maller's argument generally also deals with the situation where both

f and
P
are private, where it is less overpowered but still reduces the amount of openings we need to do. Good explanation of her argument can be found here.

This is exactly what happens in the general argument, too. We need to check that

iγiZTS(t)(Pi(t)Yi(t))H(t)ZT(t) = 0.

In order to do it, we get a variable

z from a verifier, and substitute it to all public polynomials (
ZT,ZTSi,Yi
), which, conveniently, gives us a linear combination of committed polynomials (that we can just add up using the fact that the commitment is homomorphic) - which can then be opened in
z
in one go.

The equations provided above should also be optimized in a same fashion that we have did for a single opening proof - i.e. rearranged in a form

A,H0=B,H1 - which is done trivially using the same technique as before. This allows us to batch the multipoint opening arguments in a same fashion as normal opening arguments.

Consequences

This approach is typically advantageous. Notably, verification is much cheaper provided we have a lot of different rotations of a same polynomial ("narrow" config). We also do not need to have a separate column for public inputs, like in normal PlonK - which is a notable difference. Instead, we can just use multipoint opening argument to ensure that some values of some polynomials in some cells are constrained to be public inputs.

As far as I understand, the FFlonK is also an extreme version of a similar idea, though I do not understand it well enough to judge yet.