stub out verifier and pull out test

This commit is contained in:
Nalin Bhardwaj
2023-01-28 00:23:59 -05:00
parent 53fa440292
commit 6012b18884
3 changed files with 29 additions and 183 deletions

25
test.py
View File

@@ -1,3 +1,4 @@
import pickle
from compiler.program import Program
from curve import G1Point
from poly import Basis, Polynomial
@@ -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(
@@ -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)

BIN
test/proof.pickle Normal file

Binary file not shown.

View File

@@ -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
@@ -145,123 +67,28 @@ class VerificationKey:
#
# 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(