if not bn128.isoncurve(Apt, bn128.b): return False
The solution implements a Groth16-style SNARK verifier over the BN128 (alt_bn128) pairing-friendly curve using the py_ecc library. The core verification equation is:
e(A, B) = e(α, β) · e(C, δ)
where:
- A, C ∈ G1 (points on the base field curve y² = x³ + 3)
- B ∈ G2 (points on the twisted curve over Fp²)
- α ∈ G1, β, δ ∈ G2 (verification key elements)
- e : G1 × G2 → GT is the optimal ate pairing
Key implementation details:
Data structures — G1Point, G2Point, VerificationKey, Proof dataclasses with safe to_py_ecc() / from_py_ecc() converters
Input validation — every point is checked for curve membership via bn128.is_on_curve() and points at infinity are rejected via bn128.is_inf()
Pairing computation — uses bn128.pairing(G2_point, G1_point) which computes the full reduced pairing (Miller loop + final exponentiation). The py_ecc API convention is pairing(g2, g1), so e(A, B) becomes pairing(B, A).
Equation check — computes e_AB, e_αβ, e_Cδ and checks bn128.eq(e_AB, e_αβ * e_Cδ) where * is GT group multiplication
Central verification function (52 lines):
def verify_groth16_proof(vk: VerificationKey, proof: Proof) -> bool:
# Convert to py_ecc internal representations
A_pt = proof.A.to_py_ecc()
B_pt = proof.B.to_py_ecc()
C_pt = proof.C.to_py_ecc()
alpha_pt = vk.alpha.to_py_ecc()
beta_pt = vk.beta.to_py_ecc()
delta_pt = vk.delta.to_py_ecc()
# --- Input validation ---
if not bn128.is_on_curve(A_pt, bn128.b): return False
if not bn128.is_on_curve(C_pt, bn128.b): return False
if not bn128.is_on_curve(alpha_pt, bn128.b): return False
if not bn128.is_on_curve(B_pt, bn128.b2): return False
if not bn128.is_on_curve(beta_pt, bn128.b2): return False
if not bn128.is_on_curve(delta_pt, bn128.b2): return False
if any(bn128.is_inf(p) for p in (A_pt, B_pt, C_pt, alpha_pt, beta_pt, delta_pt)):
return False
# --- Pairing computation ---
e_AB = bn128.pairing(B_pt, A_pt) # e(A, B)
e_alpha_beta = bn128.pairing(beta_pt, alpha_pt) # e(α, β)
e_C_delta = bn128.pairing(delta_pt, C_pt) # e(C, δ)
# --- Check equation ---
return bn128.eq(e_AB, e_alpha_beta * e_C_delta)
Proof generation for testing: A valid proof is constructed by:
1. Picking random toxic waste α, β, δ ∈ Fr
2. Picking random prover randomness r ∈ Fr
3. Setting A = r·G1, B = r·G2
4. Computing C = ((r² − α·β) / δ)·G1 so the pairing equation balances
All tests run via the **py_ecc** library in a Python 3.14 venv (py_ecc v8.0.0). The test suite covers: | # | Test | Result | |---|------|--------| | 1 | **Valid proof** — correctly constructed proof verifies | ✅ | | 2 | **Tampered proof** — perturbed A.x coordinate fails | ✅ | | 3 | **Wrong VK** — proof verified against different α,β,δ fails | ✅ | | 4 | **Bad G1 point** — non-curve point in VK rejected | ✅ | | 5–9 | **Multiple random proofs** — 5 additional valid proofs with fresh randomness all verify | ✅ | | 10 | **Bilinearity** — `e(a·G1, b·G2) = e(G1, G2)^(a·b)` | ✅ | | 11 | **GT homomorphism** — `e^a × e^b = e^(a+b)` in GT | ✅ | | 12 | **Large scalars** — scalars near `CURVE_ORDER - 1` work correctly | ✅ | **Edge cases explicitly handled**: - Points **off the curve** → rejected before any pairing computation - **Point at infinity** (`Z1`/`Z2`) → rejected - Scalars above `CURVE_ORDER` → reduced modulo `CURVE_ORDER` - **Zero scalar** → raises `ValueError` (avoids creating infinity point silently) ---
{"model": "deepseek-v4-flash", "problem_class": "zk-snark-verifier", "result": "passed", "tests": 12}