SHPlonk

Presentation: https://www.youtube.com/watch?v=yc7sEg1QikU

Introduction

The KZG commitment was introduced in KZG10. The time required for commitment is proportional to the degree of the polynomial, and the commitment consists of a single G1\mathbb{G}_1 element, which is C=[P(s)]1C = [P(s)]_1 for given P(X)P(X) .

The opening proof WW for a point xx is defined as:

W=[P(s)ysx]1W = \left[\frac{P(s) - y}{s - x} \right]_1

Verification is performed using the following equation:

e(W,[s]2[x]2)e(C[y]1,[1]2)=?1e(W, [s]_2 -[x]_2) \cdot e(C - [y]_1,[1]_2) \stackrel{?}= 1

However, when proving over AIR, multiple polynomials need to be opened at xx and ωx\omega \cdot x. In PLONK-based schemes that support custom gates, such as Halo2, polynomials must be opened at various opening points in different combinations. One of the efficient methods for proving in batch settings is SHPlonk, which was introduced in BDFG20.

Background

We'll now see how GWC19 opens multiple polynomials at multiple points. Let's start with simple cases.

Opening Multiple Polynomials at a Single Point

Suppose two polynomials, P0(X)P_0(X) and P1(X)P_1(X), are opened at xx with corresponding values y0y_0 and y1y_1.

  1. The verifier provides αF\alpha \leftarrow \mathbb{F} to the prover.

  2. The prover generates the following opening proof WW:

W=[P(s)ysx]1W = \left[ \frac{P(s) - y}{s- x} \right]_1

Where P(X):=P0(X)+αP1(X)P(X) := P_0(X) + \alpha \cdot P_1(X) and y:=y0+αy1y := y_0 + \alpha \cdot y_1.

Verification is performed using the following equation:

e(W,[s]2[x]2)e(C[y]1,[1]2)=?1e(W, [s]_2 - [x]_2) \cdot e(C- [y]_1, [-1]_2)\stackrel{?}=1

Where C:=C0+αC1C := C_0 + \alpha \cdot C_1 and C0,C1C_0, C_1 are commitments of P0(X),P1(X)P_0(X), P_1(X) respectively.

This approach is almost identical to opening a single polynomial at a single point, with the only difference being the random linear combination applied to the polynomials before constructing the proof.

Opening a Single Polynomial at Multiple Points

Suppose a polynomial P(X)P(X) is opened at two points, x0x_0 and x1x_1, with corresponding values y0y_0 and y1y_1.

The prover generates the following opening proof WW:

W=[P(s)r(s)(sx0)(sx1)]1W = \left[ \frac{P(s) - r(s)}{(s- x_0)(s - x_1)} \right]_1

Where r(X)r(X) is defined as:

r(X):={P(x0) if X=x0P(x1) if X=x1r(X) := \begin{cases} P(x_0) & \text{ if } X = x_0 \\ P(x_1) & \text{ if } X = x_1 \end{cases}

Verification is performed using the following equation:

e(W,[(sx0)(sx1)]2)e(C[r(s)]1,[1]2)=?1e(W, [(s- x_0)(s - x_1)]_2) \cdot e(C - [r(s)]_1, [-1]_2) \stackrel{?}= 1

Where CC is the commitment of P(X)P(X).

There are two key issues with this naive approach:

  1. Increasing G2\mathbb{G}_2 ​ parameters: As the number of opening points increases, the required G2\mathbb{G}_2 parameters also increase. For instance, in this case, [s2]2[s^2]_2 is required.

  2. Expensive group operation in G2\mathbb{G}_2: Operations in G2\mathbb{G}_2 are computationally expensive, leading to higher gas consumption in on-chain, and inefficient in recursive proof generation.

To solve these issues, we modify the protocol as follows:

The prover generates 2 opening proofs, W0W_0 and W1W_1:

W0=[Q0(s)sx0],W1=[Q1(s)sx1]W_0 = \left[ \frac{Q_0(s)}{s - x_0} \right], \quad W_1 = \left[ \frac{Q_1(s)}{s - x_1} \right]

Where Q0(X):=P(X)y0Q_0(X) := P(X) - y_0 and Q1(X):=P(X)y1Q_1(X) := P(X) - y_1.

The verifier uses αF\alpha \leftarrow \mathbb{F} to verify the following equation:

e([Q0(s)]1+α[Q1(s)]1+x0W0+αx1W1,[1]2)e(W0αW1,[s]2)=?1e([Q_0(s)]_1 + \alpha \cdot [Q_1(s)]_1 + x_0 \cdot W_0 + \alpha \cdot x_1 \cdot W_1, [1]_2) \cdot e(-W_0 - \alpha \cdot W_1, [s]_2) \stackrel{?}= 1

The verifier can obtain [Q0(s)]1,[Q1(s)]1[Q_0(s)]_1, [Q_1(s)]_1 by computing C[y0]1C - [y_0]_1 and C[y1]1C - [y_1]_1 respectively.

Proof.

By extracting the exponent components, it can be confirmed that the verification equation holds.

Q0(s)+αQ1(s)+(x0s)Q0(s)sx0+α(x1s)Q1(s)sx1=Q0(s)+αQ1(s)Q0(s)αQ1(s)=0\begin{align*} &Q_0(s) + \alpha \cdot Q_1(s) + (x_0 - s) \cdot \frac{Q_0(s)}{s - x_0} + \alpha \cdot (x_1 - s) \cdot \frac{Q_1(s)}{s - x_1} \\ = &Q_0(s) + \alpha \cdot Q_1(s) - Q_0(s) - \alpha \cdot Q_1(s) \\ = &0 \end{align*}

Now the number of required G2\mathbb{G}_2 parameters is now constant, and the group operations in G2\mathbb{G}_2 are eliminated.

Opening Multiple Polynomials at Multiple Points

In GWC19, a method that combines the previous techniques is proposed. Consider the following scenario:

  • P0(X)P_0(X) is opened at x0x_0 with value y0y_0.

  • P1(X)P_1(X) is opened at x0,x1x_0, x_1 with values y1,y2y_1, y_2.

  • P2(X)P_2(X) is opened at x0,x1x_0, x_1 with values y3,y4y_3, y_4.

  1. The verifier provides αF\alpha \leftarrow \mathbb{F} to the prover.

  2. The prover generates 2 opening proofs, W0W_0 and W1W_1:

W0=[Q0(s)sx0]1,W1=[Q1(s)sx1]1W_0 = \left[\frac{Q_0(s)}{s - x_0} \right]_1, \quad W_1 = \left[ \frac{Q_1(s)}{s - x_1} \right]_1

Where Q0(X):=P0(X)y0+α(P1(X)y1)+α2(P2(X)y3)Q_0(X) := P_0(X) - y_0 + \alpha(P_1(X) - y_1) + \alpha^2(P_2(X) - y_3) and Q1(X):=P1(X)y2+α(P2(X)y4)Q_1(X) := P_1(X) - y_2 + \alpha(P_2(X) - y_4) .

Verification is performed with βF\beta \leftarrow \mathbb{F} using the following equation:

e([Q0(s)]1+β[Q1(s)]1+x0W0+βx1W1,[1]2)e(W0βW1,[s]2)=?1e([Q_0(s)]_1 + \beta \cdot [Q_1(s)]_1 + x_0 \cdot W_0 + \beta \cdot x_1 \cdot W_1, [1]_2) \cdot e(-W_0 - \beta \cdot W_1, [s]_2) \stackrel{?}= 1

The verifier can obtain [Q0(s)]1,[Q1(s)]1[Q_0(s)]_1, [Q_1(s)]_1 by computing α(α(C2[y3]1)+C1[y1]1)+C0[y0]1\alpha(\alpha(C_2 - [y_3]_1) + C_1 - [y_1]_1) + C_0 - [y_0]_1 and α(C2[y4]1)+C1[y2]1\alpha(C_2 - [y_4]_1) + C_1 - [y_2]_1 respectively, where C0,C1,C2C_0, C_1, C_2 are the commitments of P0(X),P1(X),P2(X)P_0(X), P_1(X), P_2(X).

If there are nn polynomials of degree dd, and each polynomial is opened at tt points, the following holds:

  • srs\mathsf{srs} size: (d+1)G1,2G2(d + 1) \mathbb{G}_1, 2\mathbb{G}_2

    • srs\mathsf{srs} stands for "structure reference string".

  • Prover work: O(dt)G1,O(tdlogd)FO( d \cdot t)\mathbb{G}_1, O(t \cdot d \log d) \mathbb{F}

    • To compute each division in Qi(X)Xxi\frac{Q_i(X)}{X - x_i}, it takes O(dlogd)FO(d \log d) \mathbb{F} operations.

    • To compute each proof WiW_i, it takes O(d)G1O(d) \mathbb{G}_1 operations.

  • Proof length: tG1t\mathbb{G}_1

  • Verifier work: O(nt)G1,2PO(n \cdot t) \mathbb{G}_1, 2 \mathsf{P}

    • To compute all of [Qi(s)]1[Q_i(s)]_1, it takes O(nt)G1O(n \cdot t) \mathbb{G}_1 operations.

Here, Gi\mathbb{G}_i represents scalar multiplication in Gi\mathbb{G}_i​, F\mathbb{F} represents addition or multiplication in F\mathbb{F}, and P\mathsf{P} denotes a pairing operation.

SHPlonk enables a smaller proof length and faster verifier work for this case.

Protocol Explanation

Version 1

To illustrate this scheme, let's use the same example as before. If we group the opening points, we can form two sets: S0={x0}S_0 = \{x_0\} and S1={x0,x1}S_1 = \{x_0, x_1\}. The complete set of opening points is then T={x0,x1}T = \{x_0, x_1\}.

The zero polynomial ZS(X)Z_{S}(X) on S={x0,,xn1}S = \{x_0, \dots, x_{n-1}\} is defined as:

ZS(X):=(Xx0)(Xxn1)Z_S(X) := (X - x_0) \cdots (X - x_{n-1})

The opening proof WW is constructed as follows:

  1. The verifier provides α,βF\alpha, \beta \leftarrow \mathbb{F} to the prover.

  2. The prover computes W=[h(s)]1W = [h(s)]_1 as follows:

h(X):=Q0(X)ZS0(X)+βQ1(X)ZS1(X)h(X) := \frac{Q_0(X)}{Z_{S_0}(X)} + \beta \cdot \frac{Q_1(X)}{Z_{S_1}(X)}

Where Q0(X):=P0(X)y0,Q1(X):=P1(X)r0(X)+α(P2(X)r1(X)),r0(X),Q_0(X) := P_0(X) - y_0, Q_1(X) := P_1(X) - r_0(X) + \alpha(P_2(X) - r_1(X)), r_0(X),r0(X)r_0(X) and r1(X)r_1(X) are defined as follows:

r0(X):={P1(x0) if X=x0P1(x1) if X=x1,r1(X):={P2(x0) if X=x0P2(x1) if X=x1r_0(X) := \begin{cases} P_1(x_0) & \text{ if } X = x_0 \\ P_1(x_1) & \text{ if } X = x_1 \end{cases}, \quad r_1(X) := \begin{cases} P_2(x_0) & \text{ if } X = x_0 \\ P_2(x_1) & \text{ if } X = x_1 \end{cases}

To verify the opening proof WW, the following steps are performed:

  1. Compute Z0Z_0 and Z1Z_1 using Zi:=[ZTSi(s)]2Z_i := [Z_{T \setminus S_i}(s)]_2:

Z0=[s]2[x1]2,Z1=[1]2Z_0 = [s]_2 - [x_1]_2, \quad Z_1 =[1]_2
  1. Verify using the following equation:

e([Q0(s)]1,Z0)e(β[Q1(s)]1,Z1)=?e(W,[ZT(s)]2)e([Q_0(s)]_1, Z_0)\cdot e(\beta \cdot [Q_1(s)]_1, Z_1) \stackrel{?}= e(W, [Z_T(s)]_2)

The verifier can obtain [Q0(s)]1,[Q1(s)]1[Q_0(s)]_1, [Q_1(s)]_1 by computing C0[y0]1C_0 - [y_0]_1 and α(C2[r1(s)]1)+C1[r0(s)]1\alpha(C_2 - [r_1(s)]_1) + C_1 - [r_0(s)]_1 respectively, where C0,C1,C2C_0, C_1, C_2 are commitments of P0(X),P1(X),P2(X)P_0(X), P_1(X), P_2(X).

Proof.

Extracting the exponent components from the above equation, we obtain:

Q0(s)ZTS0(s)+βQ1(s)ZTS1(s)=(Q0(s)ZS0(s)+βQ1(s)ZS1(s))ZT(s)=h(s)ZT(s)\begin{align*} &Q_0(s) \cdot Z_{T \setminus S_0}(s) + \beta \cdot Q_1(s) \cdot Z_{T \setminus S_1}(s) \\ =&\left(\frac{Q_0(s)}{Z_{S_0}(s)} + \beta \cdot \frac{Q_1(s)}{Z_{S_1}(s)}\right) \cdot Z_T(s) \\ =&h(s) \cdot Z_T(s) \end{align*}

Thus, the verification equation holds.

If there are nn polynomials of degree dd, and each polynomial is opened at tt points, the computational costs are as follows:

  • The number of opening sets ss: 1sn1 \le s \le n

  • srs\mathsf{srs} size: (d+1)G1,(t+1)G2(d + 1) \mathbb{G}_1, (t + 1)\mathbb{G}_2

  • Prover work: O(d)G1,O(dn+sdlogd)FO( d )\mathbb{G}_1, O(d \cdot n + s \cdot d \log d) \mathbb{F}

    • This doesn't account for computation of each ri(X)r_i(X), since they are normally small operations.

    • To compute all of Qi(X)Q_i(X), it takes O(dn)FO(d \cdot n) \mathbb{F} operations.

    • To compute each Qi(X)ZSi(X)\frac{Q_i(X)}{Z_{S_i}(X)}, it takes O(dlogd)FO(d \log d) \mathbb{F} operations.

    • To compute a proof WW, it takes O(d)G1O(d) \mathbb{G}_1 operations.

  • Proof length: 1G11\mathbb{G}_1

  • Verifier work: O(n)G1,O(st)G2,(s+1)PO(n ) \mathbb{G}_1, O(s \cdot t) \mathbb{G}_2, (s+1 )\mathsf{P}

    • To compute all of [Qi(s)]1[Q_i(s)]_1, it takes O(n)G1O(n)\mathbb{G}_1 operations.

    • To compute [ZT(s)]2[Z_T(s)]_2, it takes O(t)G2O(t)\mathbb{G}_2 operations.

    • To compute each ZiZ_i, it takes O(t)G2O(t)\mathbb{G}_2 operations.

Here, Gi\mathbb{G}_i represents scalar multiplication in Gi\mathbb{G}_i​, F\mathbb{F} represents addition or multiplication in F\mathbb{F}, and P\mathsf{P} denotes a pairing operation.

Now the same issues mentioned in Opening a Single Polynomial at Multiple Points arise again. Since [ZT(s)]2[Z_T(s)]_2 is the primary cause of these issues, we need to devise a more efficient approach—one that treats the opening as if it were occurring at a single point rather than tt points.

During the presentation, BATZORIG ZORIGOO asked a great question: "What happens if all polynomials are opened at TT?" Since this simplifies ss to 1, both prover and verifier work can be reduced. However, there are two main issues:

  1. Prover Complexity: The prover must evaluate the polynomials at every point in TT, which increases the number of opening points to ntn \cdot t.

  2. ZK Proof Size: The ZK proof size increases because it must include all the opened values.

It's important to distinguish between two types of proofs here. The Proof mentioned earlier refers specifically to proving polynomial openings. However, in this context, the ZK Proof not only includes the opening proof but also contains commitments and the opened values themselves.

Version 2

Version 2 addresses the issues from the previous approach by introducing one additional proof.

The opening proofs W,WW , W' are constructed as follows:

  1. The verifier provides α,βF\alpha, \beta \leftarrow \mathbb{F} to the prover.

  2. The prover computes W=[h(s)]1W = [h(s)]_1 as follows:

h(X):=f(X)ZT(X)h(X) := \frac{f(X)}{Z_T(X)}

Where f(X)f(X) is defined as:

f(X):=ZTS0(X)Q0(X)+βZTS1(X)Q1(X)f(X) := Z_{T \setminus S_0}(X) \cdot Q_0(X) + \beta \cdot Z_{T \setminus S_1}(X) \cdot Q_1(X)

where Q0(X):=P0(X)y0,Q1(X):=P1(X)r0(X)+α(P2(X)r1(X)),r0(X),Q_0(X) := P_0(X) - y_0, Q_1(X) := P_1(X) - r_0(X) + \alpha(P_2(X) - r_1(X)), r_0(X),r0(X)r_0(X) and r1(X)r_1(X) are defined as follows:

r0(X):={P1(x0) if X=x0P1(x1) if X=x1,r1(X):={P2(x0) if X=x0P2(x1) if X=x1r_0(X) := \begin{cases} P_1(x_0) & \text{ if } X = x_0 \\ P_1(x_1) & \text{ if } X = x_1 \end{cases}, \quad r_1(X) := \begin{cases} P_2(x_0) & \text{ if } X = x_0 \\ P_2(x_1) & \text{ if } X = x_1 \end{cases}
  1. The verifier provides zFz \leftarrow \mathbb{F} to the prover.

  2. The prover computes WW' as follows:

W=[L(s)sz]1W' = \left[ \frac{L(s)}{s- z} \right]_1

Where L(X)L(X) is defined as:

L(X):=fz(X)ZT(z)h(X)L(X) := f_z(X) - Z_T(z) \cdot h(X)

Here, fz(X)f_z(X) is defined as:

fz(X):=ZTS0(z)(P0(X)y0)+βZTS1(z)(P1(X)r0(z)+α(P2(X)r1(z)))f_z(X) := Z_{T \setminus S_0}(z)(P_0(X) - y_0) + \beta \cdot Z_{T \setminus S_1}(z) (P_1(X) - r_0(z) + \alpha(P_2(X) - r_1(z)))

The polynomial L(X)L(X) is crucial in solving the issues from the previous approach. Unlike Qi(X)Q_i(X), which vanishes at multiple points, this L(X)L(X) now vanishes at single point at zz because:

L(z)=f(z)ZT(z)h(z)=0L(z) = f(z) - Z_T(z) \cdot h(z) = 0

To verify the opening proofs, the verifier checks the following equation:

e([fz(s)]1ZT(z)W,[1]2)=?e(W,[s]2[z]2)e([f_z(s)]_1 - Z_T(z) \cdot W, [1]_2) \stackrel{?}= e(W', [s]_2 - [z]_2)

The verifier can obtain [fz(s)]1[f_z(s)]_1 as follows:

[fz(s)]1=βZTS1(z)(α(C2[r1(z)]1)+C1[r0(z)]1)+ZTS0(z)(C0[y0]1)[f_z(s)]_1 = \beta \cdot Z_{T \setminus S_1}(z) (\alpha(C_2 - [r_1(z)]_1) + C_1 - [r_0(z)]_1) + Z_{T \setminus S_0}(z)(C_0 - [y_0]_1)

Where C0,C1,C2C_0, C_1, C_2 are commitments of P0(X),P1(X),P2(X)P_0(X), P_1(X), P_2(X).

Proof.

Extracting the exponent terms from the above equation, we obtain:

fz(s)ZT(z)h(s)=L(s)=L(s)sz(sz)\begin{align*} &f_z(s) - Z_T(z) \cdot h(s) \\ =& L(s) \\ =&\frac{L(s)}{s-z} \cdot (s - z) \end{align*}

Thus, the verification equation holds.

If there are nn polynomials of degree dd, and each polynomial is opened at most tt points, the computational costs are as follows:

  • The number of opening sets ss: 1sn1 \le s \le n

  • srs\mathsf{srs} size: (d+1)G1,2G2(d+1) \mathbb{G}_1, 2\mathbb{G}_2

  • Prover work: O(d)G1,O(dn+sdlogd)FO( d )\mathbb{G}_1, O(d \cdot n + s \cdot d \log d) \mathbb{F}

    • To compute all of Qi(X)Q_i(X), it takes O(dn)FO(d \cdot n) \mathbb{F} operations.

    • To compute each ZTSiQi(X)Z_{T\setminus S_{i}} \cdot Q_i(X), it takes O(dlogd)FO(d \log d)\mathbb{F} operations.

    • To compute f(X)ZT(X)\frac{f(X)}{Z_T(X)} and L(X)Xz\frac{L(X)}{X - z}, it takes O(dlogd)FO(d \log d) \mathbb{F} operations.

    • To compute L(X)L(X), it takes O(dn)O(d \cdot n) operations.

    • To compute a proof W,WW, W', it takes O(d)G1O(d) \mathbb{G}_1 operations.

  • Proof length: 2G12\mathbb{G}_1

  • Verifier work: O(n)G1,O(1)G2,2PO(n ) \mathbb{G}_1, O(1)\mathbb{G}_2,2 \mathsf{P}

    • To compute all of [f(z)]1[f(z)]_1, it takes O(n)G1O(n)\mathbb{G}_1 operations.

Here, Gi\mathbb{G}_i represents scalar multiplication in Gi\mathbb{G}_i​, F\mathbb{F} represents addition or multiplication in F\mathbb{F}, and P\mathsf{P} denotes a pairing operation.

Optimization on Version 2.

When constructing WW', it can be "normalized", to save one of the scalar multiplications like below.

W=[L(s)ZTS0(z)(sz)]1W' = \left[ \frac{L(s)}{Z_{T \setminus S_0}(z) \cdot (s- z)} \right]_1

Then, the verification logic is changed accordingly:

e([fz(s)ZTS0(z)]1ZT(z)ZTS0(z)W,[1]2)=?e(W,[s]2[z]2)e([\frac{f_z(s)}{Z_{T \setminus S_0}(z)}]_1 - \frac{Z_T(z)}{Z_{T \setminus S_0}(z)} \cdot W, [1]_2) \stackrel{?}= e(W', [s]_2 - [z]_2)

The verifier can obtain [fz(s)]1ZTS0(z)\frac{[f_z(s)]_1}{Z_{T \setminus S_0}(z)} as follows:

[fz(s)]1ZTS0(z)=βZTS1(z)ZTS0(z)(α(C2[r1(z)]1)+C1[r0(z)]1)+(C0[y0]1)\frac{[f_z(s)]_1}{Z_{T \setminus S_0}(z)} = \beta \cdot \frac{Z_{T \setminus S_1}(z)}{Z_{T \setminus S_0}(z)} (\alpha(C_2 - [r_1(z)]_1) + C_1 - [r_0(z)]_1) +(C_0 - [y_0]_1)

Notice that the rightmost part of the equation doesn't have scalar multiplication any more.

Conclusion

By summarizing the key metrics in the following table, we can see that SHPlonk demonstrates superior efficiency compared to GWC19.

SRS size
Prover work
Prover length
Verifier work

GWC19

(d+1)G1,2G2(d + 1) \mathbb{G}_1, 2\mathbb{G}_2

O(dt)G1,O(tdlogd)FO(d \cdot t)\mathbb{G}_1, O(t \cdot d \log d) \mathbb{F}

tG1t\mathbb{G}_1

O(nt)G1,2PO(n \cdot t) \mathbb{G}_1, 2 \mathsf{P}

SHPlonk version 1.

(d+1)G1,(t+1)G2(d + 1) \mathbb{G}_1,(t + 1)\mathbb{G}_2

O(d)G1,O(dn+sdlogd)FO( d )\mathbb{G}_1, O(d \cdot n + s \cdot d \log d) \mathbb{F}

1G11\mathbb{G}_1

O(n)G1,O(st)G2,(s+1)PO(n ) \mathbb{G}_1, O(s \cdot t) \mathbb{G}_2, (s+1) \mathsf{P}

SHPlonk version 2.

(d+1)G1,2G2(d+1) \mathbb{G}_1, 2\mathbb{G}_2

O(d)G1,O(dn+sdlogd)FO( d )\mathbb{G}_1, O(d \cdot n + s \cdot d \log d) \mathbb{F}

2G12\mathbb{G}_1

O(n)G1,O(1)G2,2PO(n ) \mathbb{G}_1, O(1)\mathbb{G}_2, \\2 \mathsf{P}

SHPlonk offers a more efficient proof system, balancing prover and verifier costs while keeping proof sizes minimal. Depending on the use case, Version 1 is preferable for minimizing proof length, whereas Version 2 provides a more scalable solution with constant G2\mathbb{G}_2 parameters and a fixed number of pairing operations while removing scalar multiplications in G2\mathbb{G}_2.

References

Written by ryan Kim from A41

Last updated