How it works

The scheme this site’s demo actually runs

This is Schnorr identification, made non-interactive with the Fiat–Shamir transform — see what a zk-SNARK is for exactly which of SNARK’s four properties this scheme has (three of four) and which it deliberately doesn’t attempt (Succinctness).

Setup. A group where the discrete logarithm problem is hard — this site uses the elliptic curve secp256k1, the same curve already wired up on pedersen.foundation (and, via BIP-340, the curve real Schnorr signatures use in production). GG is the curve’s base point.

Given: a secret scalar xx, and a public value y=xGy = x \cdot G (additive elliptic-curve notation).

Prove — the prover picks a fresh random kk each time and computes:

R=kGR = k \cdot G c=Hash(G,y,R)c = \text{Hash}(G, y, R) s=k+cx(modq)s = k + c \cdot x \pmod{q}

where qq is the group’s order. The proof is the pair (R,s)(R, s).

Verify — the verifier recomputes the same hash themselves (this is the whole trick behind Fiat–Shamir: nobody has to send a challenge, because the challenge is just a hash of the transcript so far) and checks one equation:

c=Hash(G,y,R)c' = \text{Hash}(G, y, R) sG=?R+cys \cdot G \stackrel{?}{=} R + c' \cdot y

Why it’s correct. Substituting the prover’s actual values:

sG=(k+cx)G=kG+c(xG)=R+cys \cdot G = (k + c \cdot x) \cdot G = k \cdot G + c \cdot (x \cdot G) = R + c \cdot y

— which is exactly the verifier’s check. This holds precisely when the prover used the real xx that produced yy; try to substitute any other xx' and the two sides diverge (see Interactive, where this is tested directly, including on purpose-tampered proofs).

Why it’s zero-knowledge. RR is a uniformly random group element (since kk is fresh and random each time) and ss is a value that a simulator — someone who doesn’t know xx — could produce too: pick ss and cc first, then derive R=sGcyR = s \cdot G - c \cdot y. A transcript built this way is distributed identically to a real one, so watching a proof teaches a verifier nothing about xx beyond “the prover knows it.”

Why it’s non-interactive. In the original, interactive version of this protocol, the verifier picks cc at random after seeing RR, specifically so the prover can’t cheat by choosing RR to fit a cc they guessed in advance. Fiat–Shamir’s insight: if cc is instead computed by hashing RR (and the rest of the public transcript), the prover can’t predict it before committing to RR either — a hash function stands in for an honest random verifier. See History for the 1986 paper.

How this conceptually scales up to a full zk-SNARK

This section is conceptual, not implemented — nothing on this page or site builds the machinery described here in code. It’s here so the jump from “a proof of knowing one discrete logarithm” to “a proof about an arbitrary computation with millions of steps” doesn’t feel like magic.

  1. Arithmetization. The computation being proven (a program, a circuit, a batch of transactions) first gets converted into a system of polynomial equations — a standard target is a Quadratic Arithmetic Program (QAP). The claim “this computation was executed correctly” becomes the claim “these specific polynomials satisfy this specific algebraic relationship.” See Further reading for Vitalik Buterin’s widely-used walkthrough of this step.
  2. Polynomial commitments. Instead of sending the (potentially huge) polynomials themselves, the prover sends a short cryptographic commitment to each one — a fixed-size value that can later be “opened” at any single point without revealing the whole polynomial. This is the piece that actually delivers Succinctness: the commitment is small and constant size no matter how large the polynomial (and therefore the original computation) was.
  3. Pairings. Constructions like Groth16 use bilinear pairings on elliptic curves — a special operation that lets a verifier check an algebraic relationship between committed values without ever seeing the values themselves, using a small, fixed number of pairing operations. This is what makes the final verification step both succinct and zero-knowledge at once.

Chained together — arithmetize the computation, commit to the resulting polynomials, use pairings to check the committed relationship holds — this is, at a sketch level, how a Groth16 or PLONK proof gets from “I ran this program correctly” down to the 3 group elements mentioned on the homepage. Each of those three steps is its own substantial area of cryptographic engineering; Further reading links to the original papers and a good next-step explainer for each.