Plonk By Hand

Reference

plonk by hand (part1)

plonk by hand (part2)

plonk by hand (part3)

Codes

All the codes below are python with SageMath.

K = GF(101)
# y^2=x^3+3
E = EllipticCurve(K,[0,0,0,0,3])
G = E(1,2) # <G> is subgroup of order 17, 17*G=inf
for i in range(1,18):
    print(i*G)
​​​​(1 : 2 : 1)
​​​​(68 : 74 : 1)
​​​​(26 : 45 : 1)
​​​​(65 : 98 : 1)
​​​​(12 : 32 : 1)
​​​​(32 : 42 : 1)
​​​​(91 : 35 : 1)
​​​​(18 : 49 : 1)
​​​​(18 : 52 : 1)
​​​​(91 : 66 : 1)
​​​​(32 : 59 : 1)
​​​​(12 : 69 : 1)
​​​​(65 : 3 : 1)
​​​​(26 : 56 : 1)
​​​​(68 : 27 : 1)
​​​​(1 : 99 : 1)
​​​​(0 : 1 : 0)

embedding degree

The degree of the extension field of characteristic

p that contains every point of order
r
on the elliptic curve is the embedding degree. It has a simple formula: the embedding degree is the smallest number
k
such that
r|pk1
. Since
60017=(10121)/17
, the embedding degree for
E(F101)
is
2
.

If r is relatively prime to the charactersitc p of the field, then there will be exactly

r2 points of order r in
Fpk
. Thus, there are total of
172
points on the elliptic curve
E(F1012)

extension field

Find an irreducible polynomial of degree

2 on
F101
, e.g.
x2+2
. Let
u
be a solution, then all the points in
F1012
can be written as
a+bu,a,bF101

# use the twisted curve technique from pairing for beginner
# to get another generator of order 17 (G2)
# (36, 31*u) 
mod(36^3,101)+3  == mod(31^2,101)*(-2)  # x^3+3 = y^2 mod 101
​​​​True
# define field extention F_{101^2} over F_101 with irreducible poly x^2+2
del(x)
x=var("x")
K2.<u> = GF(101^2, modulus=x^2 + 2,name='x')
# elliptic curve over F_{101^2}
E2=EllipticCurve(K2,[0,0,0,0,3])
P=E2(36,31*u)
2*P
​​​​(90 : 82*u : 1)

trusted setup

a circuit with

n gates requires an SRS with at least
n+5
elements as below
G1,sG1,,sn+2G1,G2,sG2

suppose secret random number

s=2, and
n=4
we have srs:
(1,2),(68,74),(65,98),(18,49),(1,99),(68,27),(65,3),(31,36u),(90,82u)

Statement

(Pythagorean Triangle) Find

(x1,x2,x3) such that
x12+x22=x32
.

General form of plonk circuit is

qLa+qRb+qOc+qMab+qC=0
The Pythagorean circuit becomes
0a1+0b1+(1)c1+1a1b1+0=00a2+0b2+(1)c2+1a2b2+0=00a3+0b3+(1)c3+1a3b3+0=01a4+1b4+(1)c4+0a4b4+0=0

In matrix form we have:

a b c q_L q_R q_O q_M q_C
3 3 9 0 0 -1 1 0
4 4 16 0 0 -1 1 0
5 5 25 0 0 -1 1 0
9 16 25 1 1 -1 0 0

where

a,b,c are advice columns in halo2, and the rest five columns are selectors (fixed column)

polynomial interpolation

We need subgroup of order

4 to interpolate each column, and luckily, the scalar field
F17
has root of unity
ω=4
. Thus the subgroup is
H
, and we can split the non-zero elements into cosets.
H=(ω0,ω1,ω2,ω4)=(1,4,16,13)k1H=2H=(2,8,15,9)k2H=3H=(3,12,14,5)

For polynomial

f(x)=a+bx+cx2+dx3,
f(ωi)=(1,ωi,ω2i,ω3i)(a,b,c,d)T
. Define matrix
Ω=(ωij)i,j
. By inverse FFT, we have
(a,b,c,d)=Ω1(f(ω0),f(ω1),f(ω2),f(ω3))T=14(ωij)i,j(f(ω0),f(ω1),f(ω2),f(ω3))T

Ω1=14[1111113164116116141613]=[13131313131641134134131416]
Notice
(ωij)i,j
is "conjugate transpose" of
Ω
, not transpose.

omega=matrix(GF(17),[[1,1,1,1],[1,4,16,13],[1,16,1,16],[1,13,16,4]])
omega.inverse()
​​​​[13 13 13 13]
​​​​[13 16  4  1]
​​​​[13  4 13  4]
​​​​[13  1  4 16]

Thus all the polynomials for each row are:

fa=1+13x+3x2+3x3fb=7+3x+14x2+13x3fc=6+5x+11x2+4x3qL=13+x+4x2+16x3qR=13+x+4x2+16x3qO=16qM=5+16x+13x2+x3qC=0

copy constraint

From

x12+x22=x33, we have
6
constraints:
a1=b1=x1a2=b2=x2a3=b3=x3a4=c1b4=c2c4=c3(a1,b1),(a2,b2),(a3,b3),(a4,c1),(b4,c2),(c4,c3)

encode permutation from copy constraint as polynomial

If we use

H=[1,4,16,13] to define the values on cell
a
,
k1H=[2,8,15,9]
to define values on cell
b
,
k2H=[3,12,14,5]
to label values on cell
c
. The only requirement here is that these values are unique. Then, the
6
copy constraints defined permutaion on these values:
(1,2)(4,8)(16,15)(13,3)(9,12)(14,5)
. And we can encode them as polynomials:
Sσ1(x)=7+13x+10x2+6x3Sσ2(x)=4+13x2+x3Sσ3(x)=6+7x+3x2+14x3

# fine the polynomial by apply inverse FFT
H=[1,4,16,13] # values of S_{sigma1}(x) before permutation
H1=[2,8,15,9] # values of S_{sigma2}(x) before permutation
H2=[3,12,14,5] 
perm = Permutation('(1,2)(16,15)(4,8)(13,3)(9,12)(14,5)') # permutation on the evaluation domain of polynomials
val = [[perm(i)] for i in H] # values of S_{sigma1}(x) after permutation
val1 = [[perm(i)] for i in H1] # values of S_{sigma2}(x) after permutation
val2 = [[perm(i)] for i in H2]
p=omega.inverse()*matrix(GF(17),val)
p1=omega.inverse()*matrix(GF(17),val1)
p2=omega.inverse()*matrix(GF(17),val2)
print(p.transpose())
print(p1.transpose())
print(p2.transpose())
​​​​[ 7 13 10  6]
​​​​[ 4  0 13  1]
​​​​[ 6  7  3 14]
# check the polynomial evaluation on [1,omega,omega^2,omega^3]
perm = Permutation('(1,2)(16,15)(4,8)(13,3)(9,12)(14,5)')
H=[1,4,16,13]
H1=[2,8,15,9]
H2=[3,12,14,5] 
R.<t>=PolynomialRing(GF(17))

p1=7+13*t+10*t^2+6*t^3
l1=[int(p1(x)) for x in H]
print(l1,[perm(i) for i in H]) # should equal

p2=4+13*t^2+t^3
l2=[int(p2(x)) for x in H]
print(l2,[perm(i) for i in H1]) # should equal

p3=6+7*t+3*t^2+14*t^3
l3=[int(p3(x)) for x in H]
print(l3,[perm(i) for i in H2])
​​​​[2, 8, 15, 3] [2, 8, 15, 3]
​​​​[1, 4, 16, 12] [1, 4, 16, 12]
​​​​[13, 9, 5, 14] [13, 9, 5, 14]

Round 1

commit a,b,c
With

ZH(x)=x41, we have:
a(x)=(b1x+b2)ZH(x)+fa(x)b(x)=(b3x+b4)ZH(x)+fb(x)c(x)=(b5x+b6)ZH(x)+fc(x)

Generating random numbers in

F17, suppose we got
(b1,b2,b3,b4,b5,b6)=(7,4,11,12,16,2)
. Then
a(x)=14+6x+3x2+3x3+4x4+7x5b(x)=12+9x+14x2+13x3+12x4+11x5c(x)=4+6x+11x2+4x3+2x4+16x5

Finally, we commit them with SRS
[a(x)]=[a(x)](1,2)=(91,66)[b(x)]=(26,45)[c(x)]=(91,35)

def commit_to_curve(coeff, base):
    commitment = 0*base;
    s = 2 # secret value
    for i in range(len(coeff)):
        commitment += coeff[i]*(s^i*base)
    return commitment
a=[14,6,3,3,4,7]
b=[12,9,14,13,12,11]
c=[4,6,11,4,2,16]        
comm_a = commit_to_curve(a,G)
comm_b = commit_to_curve(b,G)
comm_c = commit_to_curve(c,G)
print(comm_a,comm_b,comm_c)
​​​​(91 : 66 : 1) (26 : 45 : 1) (91 : 35 : 1)

Round 2

Generate

(b7,b8,b9)=(14,11,7)F17, get challenge
(β,γ)=(12,13)F17
, compute
z(x)
:
z(x)=(b7x2+b8x+b9)ZH(x)+acc(x)acc0=1acci=acci1(ai+βωi1+γ)(bi+βk1ωi1+γ)(ci+βk2ωi1+γ)(ai+βSσ1(ωi1)+γ)(bi+βSσ2(ωi1)+γ)(ci+βSσ3(ωi1)+γ)acc1=1(3+121+13)(3+1221+13)(9+1231+13)(3+122+13)(3+121+13)(9+1213+13)=11676118=3acc2=3(4+124+13)(4+1224+13)(16+1234+13)(4+128+13)(4+124+13)(16+129+13)=31411311141=9acc2=9(5+1216+13)(5+12216+13)(25+12316+13)(5+1215+13)(5+1216+13)(25+125+13)=9611211613=4acc(x)=16x+5x2+14x3z(x)=(14x2+11x+7)(x41)+16x+5x2+14x3=10+5x+8x2+14x3+7x4+11x5+14x6[z(x)]1=[z(x)](1,2)=(32,59)

# commitment of z(x)
omega=matrix(GF(17),[[1,1,1,1],[1,4,16,13],[1,16,1,16],[1,13,16,4]])
acc=omega.inverse()*matrix(GF(17),[[1],[3],[9],[4]])
coeff_z = [10,5,8,14,7,11,14]
commit_to_curve(coeff_z,G)
​​​​(32 : 59 : 1)

Round 3

Compute quotient challenge

α=15F17, compute quotient polynomial
t(x)=(a(x)b(x)qM(x)+a(x)qL(x)+b(x)qR(x)+c(x)qO(x)+PI(x)+qC(x))1ZH(x)+(a(x)+βx+γ)(b(x)+βk1x+γ)(c(x)+βk2x+γ)z(x)αZH(x)(a(x)+βSσ1(x)+γ)(b(x)+βSσ2(x)+γ)(c(x)+βSσ2(x)+γ)z(ωx)αZH(x)+(z(x)1)L1(x)α2ZH(x)

Then split into degree
<n+2
polynomials
tlo(x),tmid(x),thi(x)
where
t(x)=tlo(x)+tmid(x)+thi(x)

Output
[tlo(x)],[tmid(x)],[thi(x)]
.

We have

t(x)=11x17+7x16+2x15+16x14+6x13+15x12+x11+10x10+2x9+x8+8x7+13x6+13x5+9x3+13x2+16x+11tlo=11+16x+13x2+9x3+13x5tmid=13+8x+x2+2x3+10x4+x5thi=15+6x+16x2+2x3+7x4+11x5

And the commitment

[tlo]1=(12,32)[tmid]1=(26,45)[thi]1=(91,66)

omega=matrix(GF(17),[[1,1,1,1],[1,4,16,13],[1,16,1,16],[1,13,16,4]])
R.<x>=PolynomialRing(GF(17))
# Lagrange polynomial L_1(x)
L_1=omega.inverse()*matrix(GF(17),[[1],[0],[0],[0]])
l1=13+13*x+13*x^2+13*x^3
a = 14+6*x+3*x^2+3*x^3+4*x^4+7*x^5
b =12+9*x+14*x^2+13*x^3+12*x^4+11*x^5
c=4+6*x+11*x^2+4*x^3+2*x^4+16*x^5
q_L=13+x+4*x^2+16*x^3
q_R =13+x+4*x^2+16*x^3 
q_O = 16 
q_M=5+16*x+13*x^2+x^3
z = 10+5*x+8*x^2+14*x^3+7*x^4+11*x^5+14*x^6
zomega = 10+3*x+9*x^2+12*x^3+7*x^4+10*x^5+3*x^6
sig1=7+13*x+10*x^2+6*x^3
sig2=4+13*x^2+x^3
sig3=6+7*x+3*x^2+14*x^3
t1=(a*b*q_M + a*q_L +b*q_R+c*q_O)
t2=(a+12*x+13)*(b+12*2*x+13)*(c+12*3*x+13)*z*15
t3=(a+12*sig1+13)*(b+12*sig2+13)*(c+12*sig3+13)*zomega*15
t4=(z-1)*l1*15*15
final=t1+t2-t3+t4
t=final/(x^4-1)
t
​​​​11*x^17 + 7*x^16 + 2*x^15 + 16*x^14 + 6*x^13 + 15*x^12 + x^11 + 10*x^10 + 2*x^9 + x^8 + 8*x^7 + 13*x^6 + 13*x^5 + 9*x^3 + 13*x^2 + 16*x + 11
tlow=[11,16,13,9,0,13]
tmid=[13,8,1,2,10,1]
thi=[15,6,16,2,7,11]
tlow=commit_to_curve(tlow,G)
tmid=commit_to_curve(tmid,G)
thi=commit_to_curve(thi,G)
print(tlow,tmid,thi)
​​​​(12 : 32 : 1) (26 : 45 : 1) (91 : 66 : 1)

Round 4

Linearization. Compute evaluation challenge

ζ=5F19.

Compute opening evaluations

a¯=a(ζ),b¯=b(ζ),c¯=c(ζ)Sσ1¯=Sσ1(ζ),Sσ2¯=Sσ2(ζ)t¯=t(ζ)zω¯=z(ωζ)
Compute linearization polynomial (linear w.r.p commitment values):
r(x)=a¯b¯qM(x)+a¯qL(x)+b¯qR(x)+c¯qO(x)+qC(x)+α(a¯+βζ+γ)(b¯+βk1ζ+γ)(c¯+βk2ζ+γ)z(x)α(a¯+βSσ1¯+γ)(b¯+βSσ2¯+γ)βzω¯Sσ3(x)+α2z(x)L1(ζ)

The definition of
r(x)
is not same as in plonk paper, where we removed all the constant terms when we do linearization. This can save one verifier scalar multiplication.

Compute linearization evaluation

r¯=r(ζ). And output
a¯,b¯,c¯,Sσ1¯,Sσ2¯,zω¯,t¯,r¯

Supose

ζ=5, we have (I skip the calculation here, just copy from the blog):
a¯=a(5)=15,b¯=13,c¯=5,Sσ1¯=1,Sσ2¯=12,t¯=1,zω¯=15r(x)=16x+9x2+13x3+8x4+15x5+16x6r¯=15

Output
a¯=15,b¯=13,c¯=5Sσ1¯=1,Sσ2¯=12t¯=1zω¯=15r¯=15

Round 5

Compute opening challenge

vF19

Compute opening proof polynomial

Wζ(x):
Wζ(x)=1xζ[tlo(x)+ζn+2tmid(x)+ζ2n+4thi(x)t¯+v(r(x)r¯)+v2(a(x)a¯)+v3(b(x)b¯)+v4(c(x)c¯)+v5(Sσ1(x)Sσ1¯)+v6(Sσ2(x)Sσ2¯)]

Compute opening proof polynomial
Wζω(x)
:
Wζω(x)=z(x)zω¯xζω

Output
[Wζ(x)]1,[Wζω(x)]1

Given

ζ=5, we have
[Wζ(x)]1=(91,35),[Wζω(x)]1=(65,98)

FF=GF(17)
omega=FF(4);zeta=FF(5)
v=FF(12);tbar=FF(1);abar=FF(15);bbar=FF(13);cbar=FF(5);
sig1bar=FF(1);sig2bar=FF(12);zomega=FF(15);rbar=FF(15)
R.<x>=PolynomialRing(GF(17))
tlow=11+16*x+13*x^2+9*x^3+13*x^5
tmid=13+8*x+x^2+2*x^3+10*x^4+x^5
thi=15+6*x+16*x^2+2*x^3+7*x^4+11*x^5
r=16*x+9*x^2+13*x^3+8*x^4+15*x^5+16*x^6
sig1=7+13*x+10*x^2+6*x^3
sig2=4+13*x^2+x^3
z = 10+5*x+8*x^2+14*x^3+7*x^4+11*x^5+14*x^6
term1=tlow+zeta^6*tmid+zeta^12*thi-tbar
term2=v*(r-rbar)
term3=v^2*(a-abar)+v^3*(b-bbar)+v^4*(c-cbar)
term4=v^5*(sig1-sig1bar)+v^6*(sig2-sig2bar)
Wz=(term1+term2+term3+term4)/(x-zeta)
Wzw=(z-zomega)/(x-zeta*omega)
wz=[16,13,2,9,3,5]
wzw=[13,14,2,13,2,14]
c1=commit_to_curve(wz,G)
c2=commit_to_curve(wzw,G)
print(c1,c2)
​​​​(91 : 35 : 1) (65 : 98 : 1)

Proof

π=([a],[b],[c],[z],[tlo],[tmid],[thi],[Wζ],[Wζω]a¯,b¯,c¯,Sσ1¯,Sσ2¯,zω¯,r¯)
In our case,
π=((91,66),(26,45),(91,35),(32,59),(12,32),(26,45),(91,66),(91,35),(65,98)15,13,5,1,12,15,15)

Verify Preprocessing

[qM]=(12,69)[qL]=(32,42)[qR]=(32,42)[qO]=(1,99)[qC]=[sσ1]=(68,74)[sσ2]=(65,3)[sσ3]=(18,49)

R.<x>=PolynomialRing(GF(17))
omega=matrix(GF(17),[[1,1,1,1],[1,4,16,13],[1,16,1,16],[1,13,16,4]])
qm=omega.inverse()*matrix(GF(17),[[1],[1],[1],[0]]) #[ 5 16 13  1]
commit_to_curve([ 5, 16, 13,  1],G) # [q_M]=(12,69)
​​​​(12 : 69 : 1)

Verifier Algorithm

  1. Validate
    [a],[b],[c],[z],[tlo],[tmid],[thi],[Wζ],[Wζω]G1
  2. Validate
    a¯,b¯,c¯,Sσ1¯,Sσ2¯,zω¯,r¯F17
    .
  3. Validate
    wi[l]F17
    (public inputs)
  4. Compute zero polynomial evaluation:
    ZH(ζ)=ζn1
    .
  5. Compute
    L1(ζ)=ζn1n(ζ1)
  6. Compute public input polynomial evaluation:
    PI(ζ)=i[l]wiLi(ζ)
  7. Compute quotient polynomial evaluation:
    t¯=r¯+PI(ζ)(a¯+βSσ1¯+γ)(b¯+βSσ2¯+γ)(c¯+γ)αL1(ζ)α2ZH(ζ)
  8. Compute first part of batched polynomial commitment. Define
    [D]=v[r(x)]+u[z]
    :
    [D]=a¯b¯v[qM]+a¯v[qL]+b¯v[qR]+c¯v[qO]+v[qC]+((a¯+βζ+γ)(b¯+βk1ζ+γ)(c¯+βk2ζ+γ)αv+L1(ζ)α2v+u)[z](a¯+βSσ1¯+γ)(b¯+βSσ2+γ)αvβzω¯[Sσ3]
  9. Compute full batched polynomial commitment
    [F]=[tlo]+ζn+2[tmid]+ζ2n+4[thi]+[D]+v2[a]+v3[b]+v4[c]+v5[Sσ1]+v6[Sσ2]
  10. Compute group encoded batch evaluation
    [E]
    :
    [E]=(t¯+vr¯+v2a¯+v3b¯+v4c¯+v5Sσ1¯+v6Sσ2¯+uzω¯)[1]
  11. Batch validate all evaluations:
    e([Wζ]+u[Wζω],[s]2)=e(ζ[Wζ]+uζω[Wζω]+[F][E],[1]2)

Calculation

Let's check steps

911 for each of the polynomials. For example the terms with
u

e(us[Wζω],1)=?e(uζω[Wζω]+u[z]uz(ωζ),1)e(u(sζω)[Wζω],1)=?e(u(z(s)z(ωζ),1)(sζω)z(s)z(ζω)sζω=?z(s)z(ωζ)

Let's check
r(x)
, here we define
[Wζ]=v(r(s)r(ζ))sζ
for simplicity:
e(s[Wζ],1)=?e(ζ[Wζ]+vr(s)vr¯,1)e((sζ)v(r(s)r(ζ))sζ,1)=?e(v(r(s)r(ζ)),1)

Let's check
t(x)
, here we define
[Wζ]=tlo(s)+ζn+2tmid(s)+ζ2n+4thi(s)t(ζ)sζ
for simplicity
e((sζ)[Wζ],1)=?e([tlo]+ζn+2[tmid]+ζ2n+4[thi]t¯,1)e(tlo(s)+ζn+2tmid(s)+ζ2n+4thi(s)t(ζ),1)=?e(tlo(s)+ζn+2tmid(s)+ζ2n+4thi(s)t(ζ),1)

The other terms are easy to check as well.

Verify with concret example

  1. the blog only checks the points are on the curve, not check if in the subgroup
    G1
    . However, in our case, we have the table of all elements of
    G1
    , which is easy to compare the commitments with table.
  2. just check the values are less than
    17
    .
  3. skip, no public inputs.
  4. ZH(ζ)=12
  5. L1(ζ)=ζn1n(ζ1)=5
  6. skip, no public inputs.
  7. t¯=1
  8. [D]=inf
  9. [F]=(68,27)
  10. [E]=(1,2)
  11. e((32,42),(36,31u))=?e((12,69),(90,82u))
FF=GF(17)
zeta=FF(5)
#4.
zh=zeta^4-1
#5.
l1=(zeta^4-1)/(4*(zeta-1)) #l1=5
#7.
alpha=FF(15);beta=FF(12);gamma=FF(13)
[a,b,c,sig1,sig2,zw,r]=[FF(x) for x in [15,13,5,1,12,15,15]]
t=(r-(a+beta*sig1+gamma)*(b+beta*sig2+gamma)*(c+gamma)*alpha-l1*alpha^2)/zh
#8
zeta=5;alpha=15;beta=12;gamma=13;l1=5
[a,b,c,sig1,sig2,zw,r] = [15,13,5,1,12,15,15]
k1=2;k2=3;v=12;u=4;qm=E(12,69);ql=E(32,42);qr=E(32,42);qo=E(1,99)
ssig1=E(68,74);ssig2=E(65,3);ssig3=E(18,49)
z=E(32,59)
dn1=a*b*v*qm+a*v*ql+b*v*qr+c*v*qo
dn2a=(a+beta*zeta+gamma)*(b+beta*k1*zeta+gamma)*(c+beta*k2*zeta+gamma)*alpha*v+l1*alpha^2*v+u
dn2=dn2a*z
dn3a=(a+beta*sig1+gamma)*(b+beta*sig2+gamma)*a*v*beta*zw
dn3=dn3a*ssig3
D=dn1+dn2+dn3 # (0:1:0)=inf
#9.
n=4
[ca,cb,cc,cz,ctlo,ctmid,cthi,cwz,cwzw]=[E(91,66),E(26,45),E(91,35),E(32,59),E(12,32),E(26,45),E(91,66),E(91,35),E(65,98)]
F=ctlo+zeta^(n+2)*ctmid+zeta^(2*n+4)*cthi+D+v^2*ca+v^3*cb+v^4*cc+v^5*ssig1+v^6*ssig2
#10.
t=1
Es=t+v*r+v^2*a+v^3*b+v^4*c+v^5*sig1+v^6*sig2+u*zw
E0=Es*G
#11.
left1=cwz+u*cwzw
left2=2*G
w=4
right1=zeta*cwz+u*zeta*w*cwzw+F-E0
right2=G
# 11.
K = GF(101)
# y^2=x^3+3
E = EllipticCurve(K,[0,0,0,0,3])
# define field extention F_{101^2} over F_101 with irreducible poly x^2+2
# x is poluted so we need to reset them before define K2
del(x)
x=var("x")
K2.<u> = GF(101^2, modulus=x^2 + 2,name='x')
# elliptic curve over F_{101^2}
E2=EllipticCurve(K2,[0,0,0,0,3])
P=E2(36,31*u)
2*P
tags: public plonk zkp