Pinocchio was the first practical implementation of a zero-knowledge proving system; for instance, zCash implemented it to deliver their original shielded transaction protocol.
[ Disclaimer: this reading makes sense only if you’re already familiar with the Pinocchio proving system. You can find an overview of that protocol in this blog, pls see the Blog Index ].
In 2016, Jens Groth published a paper where he formalized a proving system that significantly improved performance compared to Pinocchio. In particular, for arithmetic circuits, proofs consist of only 2 elements in G1 and 1 in G2, compared to Pinocchio’s 7 elements in G1, and 1 in G2. Furthermore, verification is faster, with just 3 pairings instead of 12. The CRS is also shorter.
Everything looks the same up to QAP reduction, where we end up with polynomials A1(X), A2(X), A3(X), … and also B1(X), B2(X), B3(X), … and C1(X), C2(X), C3(X), …
So, we need as usual a good base field Fq, a nice pairing-friendly elliptic curve, the generators g1 and g2, and all the good Pinocchio's building blocks. With all that available, let's look at what's different.
CRS
At set-up, we’ll sample the following random field elements (all toxic waste):
α, β, γ, δ, τ.
Given:
- A computation with m variables, of which l as public input
- n constraints
- Z(X) as defined in our QAP post, i.e Z(X)=(X-1)*(X-2)*(X-3)*…
- Li(X) = β *Ai(X) + α * Bi(X) + Ci(X)
Our proving key will be made of:
- G1 elements (all the elements in bold are scalar “multiplied” for the generator point g1):
(α, δ, 1, τ, τ2, τ3, ..., τn-1, Ll+1(τ)/δ, Ll+2(τ)/δ, ..., Lm(τ)/δ, Z(τ)/δ, τ*Z(τ)/δ, τ2*Z(τ)/δ, ..., τn-2*Z(τ)/δ)1
- G2 elements (all the elements in bold are scalar “multiplied” for the generator point g2):
(β, δ, 1, τ, τ2, τ3, ..., τn-1)2
The proving key will also include the circuit, i.e. the coefficients of the A1(X), A2(X), A3(X), …, B1(X), B2(X), B3(X), … and C1(X), C2(X), C3(X),… polynomials, and the Z(X) polynomial.
The verification key will be made of:
- G1 elements: (1, L0(τ)/γ, L1(τ)/γ, L2(τ)/γ, ..., Ll(τ)/γ)1
- G2 elements: (1, γ, δ)2
- A Gt element (precomputed pairing): (α1 * β2)t
Proof Generation
Given a witness vector w = (1, w1, w2, w3, …, wm ), and two random field elements r and s, a proof is made of three points A, B and C.
A is a point in G1:
A1 = α1 + w0*A0(τ)1 + w1*A1(τ)1 + w2*A2(τ)1 + w3*A3(τ)1 + … + wm*Am(τ)1 + r*δ1
All the elements of this sum are G1 elements, so for instance:
- α1 = α*g1
- Ao(τ)1 = Ao(τ)*g1
- … and so on
Ai(τ)*g1 can be calculated from the coefficients of Ai(X), by multiplying each of them for the corresponding term g1, τ*g1, τ2*g1, … , that are points made available by the CRS.
B is a point in G2:
B2 = β2 + w0*B0(τ)2 + w1*B1(τ)2 + w2*B2(τ)2 + w2*B3(τ)2 + … + wm*Bm(τ)2 + s*δ2
and C is again a point in G1:
C1 = wl+1*(Ll+1(τ)/δ)1 + … + wm*(Lm(τ)/δ)1 + H(τ)*(Z(τ)/δ)1 + s*A1 + r*B1 - r*s* δ1
H(τ)*Z(τ)/δ * g1 can be calculated from the coefficients of H(X), by multiplying each of them for the corresponding term (Z(τ)/δ) * g1, (τ*Z(τ)/δ) * g1,( τ2*Z(τ)/δ) * g1, …, that are points made available by the CRS.
The polynomial H(X) can be calculated by calculating the polynomial A(X), B(X) and C(X) using the witness vector w on the polynomials A1(X), A2(X), A3(X), …, B1(X), B2(X), B3(X), … and C1(X), C2(X), C3(X),… provided by the CRS. As we recall from the QAP post, H(X) = [A(X) * B(X) – C(X)] / Z(X)
We also need to calculate B in G1: B1 = β1 + w0*B0(τ)1 + w1*B1(τ)1 + … + wm*Bm(τ)1 + s*δ1
Verification
To verify a proof, we’ll just need to check the following equality:
A1 * B2 = α1 * β2 + (w0*L0(τ)/y + w1*L1(τ)/y + … + wl*Ll(τ)/y)1 * y2 + C1 * δ2
That requires just three pairings, considering that α1 * β2 is already available in the verifying key.
We can easily see that the left term A * B is:
[α + A(τ) + r*δ] * [β + B(τ) + s*δ]
= α*β + α*B(τ) + s*α*δ + A(τ)*β + A(τ)*B(τ) + s*A(τ)*δ + r*δ *β + r*δ*B(τ) + s*r*δ*δ
= A(τ)*B(τ) + α*β + α*B(τ) + β*A(τ) + s*α*δ + s*A(τ)*δ + r*β*δ + r*B(τ)*δ + s*r*δ*δ
While the right term is:
α*β + L(τ) + H(τ)*Z(τ) + s*α*δ + s*A(τ)*δ + s*r*δ *δ + r*β*δ + r*B(τ)* δ + s*r*δ*δ - r*s*δ*δ
= α*β + β*A(τ) + α*B(τ) + C(τ) + H(τ)*Z(τ) + s*α*δ + s*A(τ)*δ + s*r*δ *δ + r*β*δ + r*B(τ)* δ
= C(τ) + H(τ)*Z(τ) + α*β + α *B(τ) + β*A(τ) + s*α*δ + s*A(τ)*δ + r*β*δ + r*B(τ)* δ + s*r*δ *δ
And so if the above equality hold, it means that:
A(τ)*B(τ) = C(τ) + H(τ)*Z(τ)
Mission accomplished!
As we can see, Groth16 does not use the “knowledge of coefficient” (that requires in the proof two group elements for each polynomial) , but uses the secret field elements α, β to force A, B and C to use the same vector w. The other two secret field elements γ, δ are used to make the public input independent from the other witness components.
The real-world instantiations of this proving system, extend the CRS to include some pre-computed points that make proof calculation faster, such as the hidings of the polynomial components evaluated in τ:
A0(τ)*g1, A1(τ)*g1, A2(τ)*g1, … , B0(τ)*g1, B1(τ)*g1, …, C0(τ)*g1, C1(τ)*g1, …
and
B0(τ)*g2, B1(τ)*g2, …