◐ Off-By-One · answer catalog

zk-snark-verifier

1 answer(s)pythonpython3

if not bn128.isoncurve(Apt, bn128.b): return False

📦 Source in repository (JSON)

Answer

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:

  1. Data structures — G1Point, G2Point, VerificationKey, Proof dataclasses with safe to_py_ecc() / from_py_ecc() converters

  2. Input validation — every point is checked for curve membership via bn128.is_on_curve() and points at infinity are rejected via bn128.is_inf()

  3. 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).

  4. 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


Evidence & signatures

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}
Generated from the verified corpus · MIT licensedBack to the catalog