Groth16 zkSNARK

Groth16 is a zkSNARK protocol introduced by Jens Groth in 2016 & it saw an early application in ZCash. Its proof size is among the smallest (consisting of only three elliptic curve elements) and it is also the fastest to verify

Pre-requisite Topics

Polynomial Commitment Schemes & Elliptic Curve Pairings

Though Groth16 doesn’t exactly use KZG Commitments, it uses something very similar. If you are not familiar with this type of Polynomial Commitment Schemes, you can go through this post on KZG Polynomial Commitment Scheme. It will introduce you to both Polynomial Commitment Schemes & Elliptic Curve Pairings.

We will use an Asymmetric Elliptic Curve Pairing setup

$e : \mathbb G_1 \times \mathbb G_2 \mapsto \mathbb G_T$

$G_1$ & $G_2$ are generators of cyclic groups $\mathbb G_1$ & $\mathbb G_2$ respectively.

The core properties of Elliptic Curve Pairings which we use here are the following where $a$ & $b$ are scalars

  • $e(a G_1, b G_2) = e(b G_1, a G_2) = e(ab G_1, G_2) = e(G_1, ab G_2) = e(G_1, a G_2)^{b} = e(G_1, G_2)^{ab}$

If $m = a\cdot b + c \cdot d$, then

$e(mG_1,G_2)=e((ab+cd)G_1, G_2)$

$\qquad\qquad = e(G_1,G_2)^{ab+cd}$

$\qquad\qquad = e(G_1,G_2)^{ab}\cdot e(G_1,G_2)^{cd}$

$\qquad\qquad = e(aG_1, bG_2)\cdot e(cG_1,dG_2)$

So, if we want to prove $m = a\cdot b + c \cdot d$, we can do so by proving

$e(mG_1,G_2) = e(G_1,G_2)^{ab}\cdot e(G_1,G_2)^{cd}$

The Basic Idea

The Prover wants to prove to the Verifier that she knows the solution to a particular equation without revealing the solution itself. In order for her to do this, the equation has to be first converted to the Rank-1 Constraint System (R1CS) & then to the Quadratic Arithmetic Program (QAP) form. I have covered the conversion here - R1CS and QAP - From Zero to Hero.

The current post covers what happens after the conversion.

We have polynomials $L, R, O, H$ known only to the Prover & $Z$ known to both the Prover & Verifier - all these are in the Polynomial Ring $\mathbb F_{641}[x]$

$L(x) = 529x^3 + 359x^2 + 354x + 43$

$R(x) = 428x^3 + 636x^2 + 224x + 638$

$O(x) = 537x^3 + 296x^2 + 499x + 600$

$H(x) = 139x^2 + 480x + 210$

$Z(x) = (x−1)(x−2)(x−3)(x−4)$

Let $Q = O + H\cdot Z$

The Prover has to prove $L\cdot R = Q$

For a polynomial $F(x)$, we will denote its commitment in the Group $\mathbb G_n$ as $[F]_n$. (In sagemath code, we will denote it as CFn)

Prover computes commitments $[L]_1, [R]_2$ & $[Q]_1$ & sends it to Verifier.

Verifier can then verify if $L\cdot R \stackrel {?}{=} Q$ by checking if the following equality holds

$e([L]_1,[R]_2) \stackrel {?}{=} e([Q]_1, G2)$

Powers of Tau

We create the toy curve & the generators we use for our example in Sage

q = 7691
Fq = GF(q)
Eq = EllipticCurve(Fq, [0,1])
Fqr.<r> = Fq[]
Fq_2.<v> = GF(q^2, name = 'v', modulus = Fqr(r^2 + 1))
ExtEq = EllipticCurve(Fq_2, [0,1])
G1 = ExtEq([2693, 4312])
G2 = ExtEq(633*v + 6145, 7372*v + 109)
G1.order()
641
G2.order()
641

For commitments, we need a trusted setup. A random value $\tau \in \mathbb F_{641}$ is sampled - $\tau = 266$. This is like the $a$ in the KZG Trusted Setup.

Our CRS will look like $CRS_i = \lbrace \tau^0 G_i, \tau^1 G_i, \tau^2 G_i, … \rbrace$. We need to have as many powers as the degree of the highest polynomial we want to commit. The term $H\cdot Z$ (part of the polynomial $C$) has a degree $2n-2$ & for $G_1$, we would need powers up to $\tau^{2n-2} G_1$. For $G_2$, we only need up to $\tau^{n-1}\cdot G_2$ because we commit only $R$ which is of degree $n-1$

#Randomly Sampled tau
t = Fp(266)

CRS1 = [t^0 * G1, t^1 * G1, t^2 * G1, t^3 * G1, t^4 * G1, t^5 * G1, t^6 * G1]

CRS2 = [t^0 * G2, t^1 * G2, t^2 * G2, t^3 * G2]

$H\cdot Z = 139x^6 + 372x^5 + 275x^4 + 58x^3 + 147x^2 + 379x + 553$

In sagemath, we calculate commitments $[L]_1, [R]_2, [O]_1, [HZ]_1 $. These commitments are additively homomorphic & so $[Q]_1 = [O]_1 + [HZ]_1$

CL1 = 529 * CRS1[3] + 359 * CRS1[2] + 354 * CRS1[1] + 43 * CRS1[0]

(626 : 4218 : 1)

### Likewise compute commitments CR2 & CQ1

### Display the 3 commitments 

CL1, CR2, CQ1
((626 : 4218 : 1), (2557*v + 3335 : 6647*v + 6264 : 1), (1297 : 4026 : 1))

Prover sends these commitments to the Verifier

Verifier checks if $e([L]_1,[R]_2) \stackrel {?}{=} e([D]_1, G2)$ using Pairings in sagemath.

## Left Hand Side
CL1.weil_pairing(CR2, p)
7480*v + 2304

## Right Hand Side
CQ1.weil_pairing(G2,p)
7480*v + 2304

As you can see, the check verifies. This can be followed by opening the commitments at a random point.

Problems

$1)$ The above proof is Complete, but it’s not Sound. A dishonest Prover can easily generate fake commitments which satisfy the above test.

Prover generates two random numbers $k_1$ & $k_2$ & generates fake commitments which pass the pairing test

k1 = 23
k2 = 41
L1 = k1*G1
R2 = k2*G2
C1 = k1 * k2 * g1

L1.weil_pairing(R1,p)
1732*v + 3636
C1.weil_pairing(G2, p)
1732*v + 3636

This works because of one of the properties of pairings which we listed earlier

$e(a G_1, b G_2) = e(ab G_1, G_2)$

$2)$ We created the $L, R$ & $O$ polynomials by multiplying the QAP matrix with the Solution Vector $S$

$S = [1, out, x, var1, var2, var3]$

The values of first 2 elements of the Solution vector are known to both Prover & Verifier - i.e. they are public inputs - the set of these elements is called Instance. The other 4 elements are known only to Prover & not to the verifier - they are private inputs - called Witness.

Even assuming that the Prover didn’t generate the proof using random values, the Verifier cannot verify that these were generated using the Public Inputs/Instance which is known to him. How does the Verifier know that the value of $out$ used was $35$?

$3)$ The $QAP$ & the Instance is known to both the prover & the verifier. The proof is a composition of them along with the witness. Just like the verifier cannot know if the Prover used the Instance, he also cannot know if the Prover used the known $QAP$.

We will modify the proof to fix the above & more problems.

Separating Public & Private Inputs

We created each of the polynomials by multiplying the corresponding Matrix by the solution vector - for e.g. $L(x)$ was created by multiplying the Solution Vector by $L_m$ (or $PolyM[0]$). We then constructed the equation $L\cdot = O + H\cdot Z$ which we proved.

We will do it a little different now.

Our $L_m$ or $PolyM[0]$ from the QAP

\[L_m = PolyM[0] = \begin{pmatrix} 636&116&636&535 \\ 0&0&0&0 \\ 8&416&5&213 \\ 635&330&637&321 \\ 4&634&324&320 \\ 640&536&640&107 \end{pmatrix}\]

We consider each row as a Polynomial - i.e. the first row here represents the Polynomial $535x^3 + 636x^2 + 116x + 636$

We can form the polynomials from Matrix $L_m$ this way

Pl = []
for i in range(PolyM[0].nrows()):
     Pl.append(Rp(list(PolyM[0][i])))

If we print out $P_l$ array, we get

[535*x^3 + 636*x^2 + 116*x + 636,
 0,
 213*x^3 + 5*x^2 + 416*x + 8,
 321*x^3 + 637*x^2 + 330*x + 635,
 320*x^3 + 324*x^2 + 634*x + 4,
 107*x^3 + 640*x^2 + 536*x + 640]

We can multiply each of the 6 polynomials in the $P_l$ array & multiply it with the corresponding element in the solution vector & add all the 6 outputs together we get the following

ls = 0
for i in range(len(Pl)):
     ls = ls + S[i] * Pl[i]
print(ls)
529*x^3 + 359*x^2 + 354*x + 43

If you check $ls$, it’s the same as our original $L(x)$

For ease of reading, let’s indicate the array elements of the solution vector as $S_i$ instead of $S[i]$ & the Left Polynomials as $L_i$ instead $P_r[i]$

So $L(x)$ can be expressed as

So $L = \sum\limits_{i = 0}^{m-1}S_iL_i$

where $m=6$ is the number of elements in the Solution Vector.

Expressing $L$ this way means, we can process the public & private parts of the Solution vector separately.

$l=2$ is number of public elements.

$L = \sum\limits_{i = 0}^{l-1}S_iL_i + \sum\limits_{i = l}^{m-1}S_iL_i$

Let,

$L_v = \sum\limits_{i = 0}^{l-1}S_iL_i$

$L_p = \sum\limits_{i = l}^{m-1}S_iL_i$

The subscript $p$ in $L_p$ is to denote that this part of each polynomial is known only to Prover, while subscript $v$ is to denote its Public Input & known to the Verifier also.

Fixing Problems

During the Trusted Setup, 4 random elements are sampled - $\alpha, \beta, \gamma, \delta$. None of these are known to Prover or Verifier. These along with $\tau$ are known as the Simulation Trapdoor.

And when the prover generates the proof, she samples 2 more random elements $r_1$ & $r_2$ for randomising the proof.

We create new Polynomials

$A = L + \alpha + r_1\delta$

$B = R + \beta + r_2\delta$

Multiplying,

$AB = \alpha\beta + \alpha R + \alpha r_2\delta + \beta L + LR + r_2\delta L +r_1\beta\delta + r_1\delta R + r_1r_2\delta^2$

Substituting in the above the following

$\qquad LR = O + HZ$

$\qquad L = L_v + L_p$

$\qquad R = R_v + R_p$

$\qquad O = O_v + O_p$

$AB = \alpha\beta + \alpha(R_v + R_v) + r2\alpha\delta + \beta(L_p + L_v) + (O_v + O_p) + HZ + r_2\delta L +r_1\beta\delta + r_1\delta R + r_1r_2\delta^2$

Rearranging,

$AB = \alpha\beta + (\beta Lv + \alpha Rv + O_v) + (\beta Lp + \alpha Rp + O_p + HZ) + r_2\alpha\delta + r_2\delta L +r_1\beta\delta + r_1\delta R + r_1r_2\delta^2$

Let $I = \frac {\beta Lv + \alpha Rv + O_v}{\gamma}$ and

Let $J = \frac{\beta Lp + \alpha Rp + O_p + HZ}{\delta}$

$AB = \alpha\beta + I\cdot \gamma + (J + r_2\alpha + r_2 L +r_1\beta + r_1 R + r_1r_2\delta)\cdot\delta$

$AB = \alpha\beta + I\cdot \gamma + (J + r_2(L + \alpha) +r_1(R +\beta) + r_1r_2\delta)\cdot\delta$

Substituting in the above,

$\qquad L + \alpha = A - r_1\delta$

$\qquad R + \beta = B - r_2\delta$

$AB = \alpha\beta + I\cdot \gamma + (J + r_2(A - r_1\delta) +r_1(B -r_2\delta) + r_1r_2\delta)\cdot\delta$

$AB = \alpha\beta + I\cdot \gamma + (J + r_2A - r_1r_2\delta +r_1 B -r_1r_2\delta + r_1r_2\delta)\cdot\delta$

$AB = \alpha\beta + I\cdot \gamma + (J + r_2A +r_1 B -r_1r_2\delta)\cdot\delta$

Let $C = J + r_2A +r_1 B -r_1r_2\delta$

So, our final equation to prove is

\[\\ AB = \alpha\beta + I\cdot \gamma + C\cdot\delta \\\]

where,

$I = \frac {\beta Lv + \alpha Rv + O_v}{\gamma}$ and

$C = \frac{\beta Lp + \alpha Rp + O_p + HZ}{\delta} + r_2A +r_1 B -r_1r_2\delta$

Prover computes 3 commitments $[A]_1, [B]_1, [C]_1$ & sends them to the verifier.

Verifier computes the commitments $[I]_1,[\alpha]_1, [\beta]_2, [\gamma]_2, [\delta]_2$

Using the $m = ab + cd$ property of pairings we introduced in the pre-requisites section, verifier can verify if

$A B \stackrel {?}{=} \alpha\beta + I \gamma + C\delta$

by checking if

$e([A]_1, [B]_2) \stackrel {?}{=} e([\alpha]_1, [\beta]_2) \cdot e(I, [\gamma]_2) \cdot e([C]_1, [\delta]_2)$

Later in the post, we discuss how these changes fix the problems.

Trusted Setup

To accommodate all the above changes, we have to add more elements to the trusted setup.

Phase 1:

We already saw the powers of $\tau$ in $CRS_1$ & $CRS_2$ right at the beginning.

CRS1
[(2693 : 4312 : 1),
 (5445 : 1084 : 1),
 (4704 : 5111 : 1),
 (2636 : 806 : 1),
 (760 : 3470 : 1),
 (7111 : 4864 : 1),
 (7124 : 4829 : 1)]

CRS2
[(633*v + 6145 : 7372*v + 109 : 1),
 (6256*v + 1837 : 4186*v + 2463 : 1),
 (6858*v + 4226 : 2462*v + 4671 : 1),
 (5012*v + 2956 : 1090*v + 2261 : 1)]

Groth16 Trusted Setup has 2 phases - the above is Phase 1 or Universal Setup. If you create a Phase 1 setup for a circuit of $n$ gates, it can be reused for any Circuit which has $n$ or less gates. $\tau$ is destroyed after Phase 1 is over.

Phase 2:

The 2nd Phase is circuit specific since it uses the $QAP$ of the circuit.

$-$ Trapdoor elements

The setup samples 4 Random elements $\in \mathbb F_{641}$ and computes their commitments

## Random 
a = Fp(177) # alpha
b = Fp(274) # beta
g = Fp(502) # gamma
d = Fp(138) # delta

CRSTrap1 = [a*G1, b*G1, d*G1]
CRSTrap2 = [b*G2, g*G2, d*G2]

CRSTrap1
[(7111 : 2827 : 1), (836 : 3029 : 1), (1941 : 3313 : 1)]

CRSTrap2
[(7669*v + 1046 : 7678*v + 6427 : 1),
 (4004*v + 353 : 7567*v + 6151 : 1),
 (488*v + 5968 : 7624*v + 4698 : 1)]

$-$ CRS of Public & Private Part of the Polynomials

$CRSpub = [{\lbrace}\frac{\beta L_i + \alpha R_i + O_i}{\gamma}\cdot G_1\rbrace_{i=0}^{l-1}]$

$CRSprv = [\lbrace\frac{\beta L_i + \alpha R_i + O_i}{\gamma}\cdot G_1\rbrace_{i=l}^{m-1}]$

We compute these using the $P_l, P_r, P_o$ we computed earlier.

def commit(F, CRS):
    cof = F.list()
    d = F.degree()
    com = ExtEq([0,1,0])
    for i in range(d+1):
        com = com + cof[i]*CRS[i]
    return com

CRSPub = []
CRSPrv = []

# We use Pl, Pr, Po computed earlier from the QAP
for i in range(l):
    CRSPub.append(commit(Rp((b * Pl[i] + a*Pr[i] + Po[i])/g),1))
    
for i in range(l,m)):
    CRSprv.append(commit(Rp((b * Pl[i] + a*Pr[i] + Po[i])/d), CRS1))

CRSPub
[(516 : 6246 : 1), (5397 : 7026 : 1)]

CRSPrv
[(1604 : 6439 : 1), (2473 : 5808 : 1), (1576 : 4715 : 1), (5416 : 1062 : 1)]

$-$ CRS for computing $HZ$

$CRShz = [\lbrace\frac {\tau^i Z(\tau)}{\delta} G_1\rbrace_{i=0}^{n-2} ]$

We don’t know $\tau$ or $Z(\tau)$, so how do we compute this list?

Let $R(x,i) = \frac {x^i\cdot Z(x)}{\delta}$. Each element of $CRShz$ is $R((x=\tau),i)$ multiplied by $G_1$ for increasing values of $i$ - i.e. each element is the Commitment of $R(x)$ for increasing values of $i$

$Z(x)$ is of degree $n$ & the max degree of $x$ we multiply it by is $n-2$, so we require $\tau$ powers up to $2n-2$ in $CRS1$ to compute $CRShz$.

CRShz=[]
for i in range(n-2):
    CRShz.append(commit(((x^i)*Zx)/d,CRS1))

CRShz
[(5282 : 2807 : 1), (728 : 800 : 1), (1202 : 4268 : 1)]

So having $CRS1$ up to $2n-2$ enables us to split the Trusted Setup into 2 parts - Phase 1 which is the Universal Setup & Phase 2 which is the Circuit Specific Setup. Phase 1 can be reused for any other circuit as long it has $n$ or less gates.

$\alpha, \beta, \gamma$ & $\delta$ are destroyed after Phase 2 is over.

Prover Phase

## Prover samples 2 random numbers
r1 = Fp(244)
r2 = Fp(250)

Prover computes the follow three commitments

$1)\space[A]_1$ where $A = L + \alpha + r_1\delta$

(We have already computed the commitment of $L$ earlier directly from the polynomial $L$ )

CA1 = CL1 + CRSTrap1[1] + r2*CRSTrap1[2]

CA1
(4720 : 3593 : 1)

$2)\space[B]_2$ where $B = R + \beta + r_2\delta$

CB2 = CR2 + CRSTrap2[0] + r2*CRSTrap2[2]

CB2
(4544*v + 1851 : 7516*v + 2019 : 1)

$3)\space[C]_1$ where $C = \frac {\beta L_p + \alpha L_p + O_p + HZ} {\delta} + L_1 r_2 + R_1 r_1 - r_1 r_2 \delta$

There are 4 terms in the Right Hand Side, we compute each one & add all of them together.

com1 = ExtEq([0,1,0])

for i in range(len(CRSPrv)):
    com1 = com1 + S[i+2]*CRSPrv[i]

## Commitment of (H(x)T(x))/d
com1 = com1 + commit(Hx,CRShz)

com2 = r2*P1

## Remember R = Rx + b + r2*d

com3 = r1*(commit(Rx,CRS1) + CRSTrap1[1] + r2*CRSTrap1[2])

com4 = r1*r2*CRSTrap1[2]

CC1 = com1 + com2 + com3 - com4

CC1
(7474 : 3237 : 1)

The only thing to explain in the above is possibly the commitment of $H\cdot Z$

When we have 2 polynomials $H$ & $Z$ & we need to commit their product, then there are 2 ways this can be done (both will obviously result in the same commitment)

$1)$ Prover multiplies $H$ & $Z$ & gets $P = H\cdot Z$ - the degree of $P$ is the degree of $H$ + degree of $Z$

Prover computes the commitment of $P$

$2)$ First, we evaluate $Z$ at $x = \tau$ which gives us a value $z = Z(\tau)$

Now we multiply $H$ by the value $Z(\tau)$ we get a new polynomial whose degree is same as that of $H$.

In our case ($H(x) = 139x^2+480x+210$) the new polynomial

$Z(\tau)\cdot H(x) = Z(\tau)\cdot 139x^2 + Z(\tau)\cdot480x + Z(\tau)\cdot210$

The commitment of $Z(\tau)\cdot H(x)$ is

$[HZ]_1 = Z(\tau)\cdot 139\tau^2\cdot G1 + Z(\tau)\cdot480\cdot \tau\cdot G1 + Z(\tau)\cdot210\cdot G1$

Rearranging,

$[HZ]_1 = 139\cdot(\tau^2\cdot Z(\tau)\cdot G1) + 480\cdot (\tau^1\cdot Z(\tau)\cdot G1) + 210\cdot (\tau^0\cdot Z(\tau)\cdot\ G1)$

The terms in the brackets are exactly what we have in $CRShz$

So, $[HZ]_1 = 139\cdot CRShz[2] + 480\cdot CRShz[1] + 210\cdot CRShz[0]$

This is the same as the commitment of $H$ using $CRShz$ as the Powers of $\tau$ instead of using $CRS1$ as the Powers of $\tau$.

I will explain a little later why we compute the commitment of $HZ$ this way instead of the other simpler way.

Prover sends the proof $\pi = ([A]_1, [B]_2, [C]_1)$ to the verifier

Verification Phase

Verifier needs the remaining commitments $[I]_1,[\alpha]_1, [\beta]_2, [\gamma]_2 [\delta]_2$ to verify the proof.

Verifier computes $[I]_1$ where $I = (\frac {\beta L_v + \alpha L_v + O_v}{\gamma})$

CI1 = ExtEq([0,1,0])
for i in range(2):
    CI1 = CI1 + S[i]*CRSPub[i]

CI1
(1657 : 1925 : 1)

The remaining are already in the CRS

Ca1 = CRSTrap1[0]
Cb2 = CRSTrap2[0]
Cg2 = CRSTrap2[1]
Cd2 = CRSTrap2[2]

Verifier checks the equality using Pairings

$e([A]_1, [B]_2) \stackrel {?}{=} e([\alpha]_1, [\beta]_2) \cdot e(I, [\gamma]_2) \cdot e([C]_1, [\delta]_2)$

## Left Pairing
CA1.weil_pairing(CB2, p)
2416*v + 1193

## Right Pairings
Ca1.weil_pairing(Cb2, p) * CI1.weil_pairing(Cg1, p) * CC1.weil_pairing(Cd2,p)
2416*v + 1193

As you can see, the check verifies

Soundness & Zero Knowledge

The problems we identified earlier in here were fixed by the changes we made here. Below I do some handwaving which tries to explain the intuition behind how these changes fixed the problems.

In our original proof, we had 5 polynomials $L, R, O, H, Z$ & their commitments were all computed by the Prover.

  • We had no way of confirming if she used the QAP & the Solution vector at all in coming up with polynomials & the computing their commitments. She could have used the $k_1, k_2$ trick we showed in the problems section.

    Fix: Now some of the commitments are computed by the Prover & some by the Verifier & they have common elements - for e.g. the commitment of $I = (\frac {\beta L_v + \alpha L_v + O_v}{\gamma})$ is computed by the Verifier & $I$ includes public parts of $L, R$ & $O$ (i.e. $L_v, R_v$ & $O_v$) - so, if the Prover tried the $k_1, k_2$ the parts computed by the verifier equation won’t match & the check will fail

  • Polynomials $L, R$ & $O$ are formed by multiplying the 3 $QAP$ matrices with the Solution Vector $S$. We have no way for confirming if she used consistent values from the Witness part of $S$ in computing $L$, $R$ & $O$

    Fix: The shifting of $L$ & $R$ with $\alpha$ & $\beta$ ensures that $L, R$ & $O$ are consistent in using the same $S$ The product $\alpha\cdot\beta$ in the verification equation computed by the verifier guarantees that $A$ and $B$ involve non-trivial $\alpha$ and $\beta$ components. The product $A\cdot B$ now involves a linear dependence on $\alpha$ and $\beta$, and the Groth16 paper presents a proof of how this means this linear dependence can only be balanced out by a $C$ with a consistent choice of $S$ in all three of $L, R$ & $C$ used to compute $A, B$ & $C$.

  • We had no way of confirming that the solution vector used by the Prover had the same Public Input which we expect (i.e. the $out$)

    Fix: Our modified equation has the public input divided by $\gamma$ ($\gamma$ is not known to the Prover). So, if the Prover used different public inputs in computing $L, R$ or $O$ then it won’t be balanced out by the part computed by the Verifier.

  • We had no way of confirming that the Prover used the right polynomial $Z$ computing the commitment of $H\cdot Z$

    Fix: The term $C$ contains $\frac {H\cdot Z}{\delta}$ - however, the prover doesn’t know the value of $\delta$. So, if the prover has to compute the commitment using $CRShz$ which has the $\frac{\tau^i\cdot Z(\tau)}{\delta}$ commitments. Any commitment of $C$ which is sent by the Prover won’t balance out the remaining terms (parts of which are computed by the verifier), unless she computed it using $CRShz$

  • Zero Knowledge: Adding $\alpha, \beta, \gamma, \delta$ ensures that the Prover uses the QAP with the Instance & the Witness. However, now it’s no longer zero knowledge because someone can try to brute force it - i.e. do an exhaustive search of witness permutations combined with the public parts & the $QAP$ & compute each of their commitments & check if the commitments match with the commitments sent by the Prover.

    Fix: The 2 random values sampled & used by the Prover $r_1$ and $r_2$ randomizes the proof to get zero-knowledge.

In the Groth16 protocols, the commitments are never opened at some random point given by the Verifier - the proof doesn’t depend on that - it is sound even without the openings.

This ends the post.

Note: If you are interested in a python implementation, you can check out Crypto Fairy’s excellent series of Blog Posts on Groth16

Hits

Written on July 24, 2024