Skip to content

Commit

Permalink
stub out verifier and pull out test
Browse files Browse the repository at this point in the history
  • Loading branch information
nalinbhardwaj committed Jan 28, 2023
1 parent 53fa440 commit 6012b18
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 183 deletions.
25 changes: 22 additions & 3 deletions test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pickle
from compiler.program import Program
from curve import G1Point
from poly import Basis, Polynomial
Expand Down Expand Up @@ -88,16 +89,25 @@ def prover_test(setup):
return proof


def verifier_test(setup, proof):
def verifier_test_unoptimized(setup, proof):
print("Beginning verifier test")
program = Program(["e public", "c <== a * b", "e <== c * d"], 8)
public = [60]
vk = setup.verification_key(program.common_preprocessed_input())
assert vk.verify_proof(8, proof, public)
assert vk.verify_proof_unoptimized(8, proof, public)
print("Verifier test success")


def verifier_test_full(setup, proof):
print("Beginning verifier test")
program = Program(["e public", "c <== a * b", "e <== c * d"], 8)
public = [60]
vk = setup.verification_key(program.common_preprocessed_input())
assert vk.verify_proof_unoptimized(8, proof, public)
assert vk.verify_proof(8, proof, public)
print("Verifier test success")


def factorization_test(setup):
print("Beginning test: prove you know small integers that multiply to 91")
program = Program.from_str(
Expand Down Expand Up @@ -186,11 +196,20 @@ def poseidon_test(setup):


if __name__ == "__main__":
# Step 1: Pass setup test
setup_test()

# Step 2: Pass verifier test
setup = basic_test()
with open("test/proof.pickle", "rb") as f:
proof = pickle.load(f)
verifier_test_unoptimized(setup, proof)
verifier_test_full(setup, proof)

# Step 3: Pass prover and end-to-end test
ab_plus_a_test(setup)
one_public_input_test(setup)
proof = prover_test(setup)
verifier_test(setup, proof)
verifier_test_full(setup, proof)
factorization_test(setup)
poseidon_test(setup)
Binary file added test/proof.pickle
Binary file not shown.
187 changes: 7 additions & 180 deletions verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,98 +38,20 @@ class VerificationKey:
# efficiently batch them
def verify_proof(self, group_order: int, pf, public=[]) -> bool:
# 4. Compute challenges
beta, gamma, alpha, zeta, v, u = self.compute_challenges(pf)
proof = pf.flatten()

# 5. Compute zero polynomial evaluation Z_H(ζ) = ζ^n - 1
root_of_unity = Scalar.root_of_unity(group_order)
ZH_ev = zeta**group_order - 1

# 6. Compute Lagrange polynomial evaluation L_0(ζ)
L0_ev = ZH_ev / (group_order * (zeta - 1))

# 7. Compute public input polynomial evaluation PI(ζ).
PI = Polynomial(
[Scalar(-x) for x in public]
+ [Scalar(0) for _ in range(group_order - len(public))],
Basis.LAGRANGE,
)
PI_ev = PI.barycentric_eval(zeta)

# Compute the constant term of R. This is not literally the degree-0
# term of the R polynomial; rather, it's the portion of R that can
# be computed directly, without resorting to elliptic cutve commitments
r0 = (
PI_ev
- L0_ev * alpha**2
- (
alpha
* (proof["a_eval"] + beta * proof["s1_eval"] + gamma)
* (proof["b_eval"] + beta * proof["s2_eval"] + gamma)
* (proof["c_eval"] + gamma)
* proof["z_shifted_eval"]
)
)

# D = (R - r0) + u * Z
D_pt = ec_lincomb(
[
(self.Qm, proof["a_eval"] * proof["b_eval"]),
(self.Ql, proof["a_eval"]),
(self.Qr, proof["b_eval"]),
(self.Qo, proof["c_eval"]),
(self.Qc, 1),
(
proof["z_1"],
(
(proof["a_eval"] + beta * zeta + gamma)
* (proof["b_eval"] + beta * 2 * zeta + gamma)
* (proof["c_eval"] + beta * 3 * zeta + gamma)
* alpha
+ L0_ev * alpha**2
+ u
),
),
(
self.S3,
(
-(proof["a_eval"] + beta * proof["s1_eval"] + gamma)
* (proof["b_eval"] + beta * proof["s2_eval"] + gamma)
* alpha
* beta
* proof["z_shifted_eval"]
),
),
(proof["t_lo_1"], -ZH_ev),
(proof["t_mid_1"], -ZH_ev * zeta**group_order),
(proof["t_hi_1"], -ZH_ev * zeta ** (group_order * 2)),
]
)

F_pt = ec_lincomb(
[
(D_pt, 1),
(proof["a_1"], v),
(proof["b_1"], v**2),
(proof["c_1"], v**3),
(self.S1, v**4),
(self.S2, v**5),
]
)

E_pt = ec_mul(
b.G1,
(
-r0
+ v * proof["a_eval"]
+ v**2 * proof["b_eval"]
+ v**3 * proof["c_eval"]
+ v**4 * proof["s1_eval"]
+ v**5 * proof["s2_eval"]
+ u * proof["z_shifted_eval"]
),
)
# Compute D = (R - r0) + u * Z, and E and F

# Run one pairing check to verify the last two checks.
# What's going on here is a clever re-arrangement of terms to check
# the same equations that are being checked in the basic version,
# but in a way that minimizes the number of EC muls and even
Expand All @@ -145,123 +67,28 @@ def verify_proof(self, group_order: int, pf, public=[]) -> bool:
#
# so at this point we can take a random linear combination of the two
# checks, and verify it with only one pairing.
assert b.pairing(
self.X_2, ec_lincomb([(proof["W_z_1"], 1), (proof["W_zw_1"], u)])
) == b.pairing(
b.G2,
ec_lincomb(
[
(proof["W_z_1"], zeta),
(proof["W_zw_1"], u * zeta * root_of_unity),
(F_pt, 1),
(E_pt, -1),
]
),
)

print("done combined check")
return True

return False

# Basic, easier-to-understand version of what's going on
def verify_proof_unoptimized(self, group_order: int, pf, public=[]) -> bool:
# 4. Compute challenges
beta, gamma, alpha, zeta, v, _ = self.compute_challenges(pf)
proof = pf.flatten()

# 5. Compute zero polynomial evaluation Z_H(ζ) = ζ^n - 1
root_of_unity = Scalar.root_of_unity(group_order)
ZH_ev = zeta**group_order - 1

# 6. Compute Lagrange polynomial evaluation L_0(ζ)
L0_ev = ZH_ev / (group_order * (zeta - 1))

# 7. Compute public input polynomial evaluation PI(ζ).
PI = Polynomial(
[Scalar(-x) for x in public]
+ [Scalar(0) for _ in range(group_order - len(public))],
Basis.LAGRANGE,
)
PI_ev = PI.barycentric_eval(zeta)

# Recover the commitment to the linearization polynomial R,
# exactly the same as what was created by the prover
R_pt = ec_lincomb(
[
(self.Qm, proof["a_eval"] * proof["b_eval"]),
(self.Ql, proof["a_eval"]),
(self.Qr, proof["b_eval"]),
(self.Qo, proof["c_eval"]),
(b.G1, PI_ev),
(self.Qc, 1),
(
proof["z_1"],
(
(proof["a_eval"] + beta * zeta + gamma)
* (proof["b_eval"] + beta * 2 * zeta + gamma)
* (proof["c_eval"] + beta * 3 * zeta + gamma)
* alpha
),
),
(
self.S3,
(
-(proof["a_eval"] + beta * proof["s1_eval"] + gamma)
* (proof["b_eval"] + beta * proof["s2_eval"] + gamma)
* beta
* alpha
* proof["z_shifted_eval"]
),
),
(
b.G1,
(
-(proof["a_eval"] + beta * proof["s1_eval"] + gamma)
* (proof["b_eval"] + beta * proof["s2_eval"] + gamma)
* (proof["c_eval"] + gamma)
* alpha
* proof["z_shifted_eval"]
),
),
(proof["z_1"], L0_ev * alpha**2),
(b.G1, -L0_ev * alpha**2),
(proof["t_lo_1"], -ZH_ev),
(proof["t_mid_1"], -ZH_ev * zeta**group_order),
(proof["t_hi_1"], -ZH_ev * zeta ** (group_order * 2)),
]
)

print("verifier R_pt", R_pt)


# Verify that R(z) = 0 and the prover-provided evaluations
# A(z), B(z), C(z), S1(z), S2(z) are all correct
assert b.pairing(
b.G2,
ec_lincomb(
[
(R_pt, 1),
(proof["a_1"], v),
(b.G1, -v * proof["a_eval"]),
(proof["b_1"], v**2),
(b.G1, -(v**2) * proof["b_eval"]),
(proof["c_1"], v**3),
(b.G1, -(v**3) * proof["c_eval"]),
(self.S1, v**4),
(b.G1, -(v**4) * proof["s1_eval"]),
(self.S2, v**5),
(b.G1, -(v**5) * proof["s2_eval"]),
]
),
) == b.pairing(b.add(self.X_2, ec_mul(b.G2, -zeta)), proof["W_z_1"])
print("done check 1")

# Verify that the provided value of Z(zeta*w) is correct
assert b.pairing(
b.G2, ec_lincomb([(proof["z_1"], 1), (b.G1, -proof["z_shifted_eval"])])
) == b.pairing(
b.add(self.X_2, ec_mul(b.G2, -zeta * root_of_unity)), proof["W_zw_1"]
)
print("done check 2")
return True

return False

# Compute challenges (should be same as those computed by prover)
def compute_challenges(
Expand Down

0 comments on commit 6012b18

Please sign in to comment.