skip to content
Skill Issue Dev | Dax the Dev
search

Why BN254, and when to switch off it

Print view

Sections

Every production ZK system you can name in 2026 — Zcash Sapling, Aleo, Mina, Filecoin’s PoRep, Solana’s alt_bn128_* syscalls, the Ethereum precompile at 0x06/0x07/0x08, zera-sdk — runs Groth16 over the same curve. BN254. A Barreto–Naehrig curve with a 254-bit base field, embedding degree 12, and a pairing that has been the default in pairing-based cryptography for nearly two decades.

It is also the curve that has lost the most bits of advertised security in the last ten years.

This post is the long answer to two questions that come up in every cryptography review I run with a serious team: why is BN254 still the default in 2026, and when do we get off it.

The minimum cryptography you need

A pairing is a bilinear map

e:G1×G2GTe : \mathbb{G}_1 \times \mathbb{G}_2 \to \mathbb{G}_T

with G1,G2\mathbb{G}_1, \mathbb{G}_2 cyclic groups of prime order rr on an elliptic curve EE, and GT\mathbb{G}_T a multiplicative subgroup of an extension field Fpk\mathbb{F}_{p^k}. Bilinear means

e(aP,bQ)=e(P,Q)abe(a P, b Q) = e(P, Q)^{ab}

for any a,bZra, b \in \mathbb{Z}_r and generators P,QP, Q. That single equation is the entire reason pairing-based cryptography exists — it lets you “multiply in the exponent” across two different groups, which is exactly what Groth16’s verification equation needs.

Two parameters drive everything. The embedding degree kk is the smallest integer with rpk1r \mid p^k - 1; it sets the size of the target field Fpk\mathbb{F}_{p^k}. The base field characteristic pp sets the cost of every operation in G1\mathbb{G}_1. The security of the pairing rests on:

  • The discrete log problem (DLP) in G1\mathbb{G}_1 and G2\mathbb{G}_2 — protected by Pollard’s rho, cost r\sqrt{r}, so we want r2256r \approx 2^{256} for 128-bit security.
  • The DLP in Fpk\mathbb{F}_{p^k}^* — protected by the number field sieve, cost subexponential in pkp^k, so we want pkp^k large enough that NFS is no easier than r\sqrt{r}.

The trick of pairing-friendly curve design is to find (p,r,k)(p, r, k) where both DLPs are hard and pp is small enough that field operations don’t dominate. BN curves use the parameterisation

p(t)=36t4+36t3+24t2+6t+1,p(t) = 36 t^4 + 36 t^3 + 24 t^2 + 6 t + 1, r(t)=36t4+36t3+18t2+6t+1,r(t) = 36 t^4 + 36 t^3 + 18 t^2 + 6 t + 1,

with E:y2=x3+3E: y^2 = x^3 + 3 defined over Fp\mathbb{F}_p and embedding degree k=12k = 12. Pick tt such that pp and rr are both prime, and you get a curve. BN254 is the choice t=4965661367192848881t = 4965661367192848881 — an integer carefully chosen so pp has 254 bits and rr has 254 bits, and so the resulting field arithmetic is reasonably efficient.

Where the 128 bits went

When BN254 was deployed in 2010-2015, the security argument was: p1223048p^{12} \approx 2^{3048}, the NFS algorithm at the time required 2128\approx 2^{128} field operations to break the DLP in Fp12\mathbb{F}_{p^{12}}^*, and Pollard’s rho on G1\mathbb{G}_1 required 2127\approx 2^{127}. Both legs landed at 128-bit security. Done.

Then Kim and Barbulescu (2016) introduced exTNFS, an extended Tower NFS variant that exploits the structure of Fpk\mathbb{F}_{p^k} when kk has a non-trivial factorisation (which k=12=43k = 12 = 4 \cdot 3 does). The complexity of NFS dropped, and the Barbulescu-Duquesne (2019) update re-estimated the security of BN254 at roughly 100-110 bits — depending on which constant in the NFS asymptotic you trust.

That is the gap. The curve is not broken. The pairing still works. But “BN254 = 128-bit security” was the marketing line, and after 2016 it should have been “BN254 ≈ 100 bits.”

The honest table:

OptionCostLatencyBlast radiusNotes
BN254 ~254-bit base field; cheapest pairing in production Best verifier perf; ~3ms Groth16 verify on Solana ~100-110 bits actual security after exTNFS Default in Ethereum precompile, Solana, Zcash Sprout, zera-sdk. Fine for 2026; not a forever choice.
BLS12-381 ~381-bit base field; ~50% slower pairings Verifier ~5-7ms typical ~120-126 bits actual security Ethereum 2.0 BLS signatures, Zcash Sapling, Filecoin. The realistic upgrade target.
BLS12-446 ~446-bit base field; ~80% slower than BN254 Verifier ~7-9ms ~128 bits with margin Theoretically clean 128-bit choice; minimal deployment as of 2026.
BLS24-509 Larger base, embedding degree 24, very fast G_T arithmetic Verifier ~6-8ms ~128 bits, more conservative NFS margin Niche; competes with BLS12-381 on perf, more on paper than in production.
Post-quantum (lattice / hash / code) No pairings; structurally different STARK-style verification, larger proofs Quantum-secure (still active research) When pairing-based cryptography retires, this is what replaces it. Not 2026, probably 2030+.

The blast-radius column is the load-bearing one. BN254 is not broken in 2026. A 100-bit security level still costs an attacker 2100\sim 2^{100} field operations, which is not within the budget of any actor we model. But it is also not a curve you start a fresh decade-long deployment on.

The migration hierarchy

The pairing-friendly curve landscape, drawn as a hierarchy of “what would I deploy next”:

The bottom row is what kills pairing-based cryptography eventually. Shor’s algorithm runs in polynomial time on a sufficiently large quantum computer, the discrete log breaks, and every curve in the diagram above goes to zero overnight. The realistic time horizon for that is not 2026 — the largest credible quantum factorisation as of last year is still toy-scale — but it is the reason you build a hash function migration story into your protocol from day one. We did this in zera-sdk by isolating the curve choice to a single crates/zera-sdk-core/src/curve.rs module. A future migration to BLS12-381 is one type alias and a regenerated .zkey. A migration to a lattice-based scheme is a bigger lift but the seam is clean.

Why the IETF still hasn’t picked one

The IETF CFRG has been running a pairing-friendly curves working group since 2018. As of draft 11, the recommendation lists BLS12-381 and BN462 as the two curves with 128-bit security after exTNFS. BN254 is explicitly not recommended for new deployments — the draft notes:

The BN curves with smaller parameters such as BN254 should not be used for applications requiring 128-bit security level due to the recent improvements of the number field sieve algorithm. Implementations targeting the 128-bit security level SHOULD use BLS12-381 or BN462.

IETF CFRG, pairing-friendly-curves draft ↗ source

The reason BN254 is still the production default in 2026 despite this is one part path-dependence (the Ethereum precompile is BN254 and rewriting that is a hard fork) and one part cost (BLS12-381 is roughly 50% slower per pairing and 50% larger per group element). For a privacy pool that is already paying tens of milliseconds per proof, the trade-off is real.

The clean argument: BN254 today, BLS12-381 next, lattice-based when the quantum threat becomes credible. That ordering is what every serious protocol designer I’ve talked to in the last year converges on.

Pairing arithmetic, by hand

The pairing itself is a Miller-loop algorithm followed by a final exponentiation. It is unreasonable to derive in a blog post — go read Barreto, Galbraith, Ó hÉigeartaigh, Scott (2007) for the optimal-Ate construction — but the bilinearity check is one line and worth seeing:

e([a]P,[b]Q)=e(P,Q)abe([a]P, [b]Q) = e(P, Q)^{ab}

The toy below verifies this property over a tiny pairing-friendly toy curve. It uses a synthetic group and a synthetic pairing — not BN254, because computing a real BN254 pairing in 60 lines of TypeScript is not honest pedagogy. The shape of the relations is real. The numbers are not.

sandbox [ vanilla-ts ]
run

The Rust shape of a real pairing — using arkworks — is much closer to a one-liner once the curve is in scope:

bilinearity (skeleton) [ rust ]
open

The real implementation lives in arkworks-rs/algebra and supranational/blst. The latter is what production Ethereum and Solana ZK code links against — blst is the BLS12-381 pairing library written by Pierre-Yves Strub and Sergey Vasilyev, audited, and with constant-time multi-scalar multiplication that beats anything else in the open source.

What changes if we move to BLS12-381

The migration cost is not the curve. The migration cost is everything that touches the curve.

  1. Re-run the trusted setup. Groth16 needs a per-circuit setup. Migrating to BLS12-381 means a fresh ceremony for every circuit. That is non-trivial — a Powers-of-Tau ceremony runs for months — but it is also not blocked on cryptography.
  2. Regenerate the verifying keys. Every on-chain verifier ships a verifying key (a few KB of curve points). Those have to be regenerated and re-deployed. On Solana, that’s a program upgrade. On Ethereum, that’s a fresh contract deploy.
  3. Update every prover. snarkjs, rapidsnark, the Rust prover in zera-sdk-core — all of them. The ff and pairing crate ecosystem in Rust is curve-generic, so this is an ark-bn254ark-bls12-381 swap and a recompile. The TypeScript side is harder because circomlibjs is BN254-pinned in places.
  4. Eat the verifier-cost hit. A BLS12-381 pairing is roughly 50% more expensive than BN254. On Solana that’s 1500 extra compute units per pairing-based verification. Multiplied by the four pairings in a typical multi-input transfer proof, that’s 6000 CUs — meaningful but absorbable.

We’ve scoped this work for zera-sdk v2 but not yet committed to a date. The bet is: BN254 carries us through 2027 deployments comfortably, and the BLS12-381 migration is a clean lift the moment the broader Solana ecosystem standardises on it (the alt_bls12_381_* syscalls have been in cargo-audit feature flags since 2025).

Where this lands in the stack

In crates/zera-sdk-core/src/curve.rs the curve is a single type alias:

// Currently:
pub type Curve = ark_bn254::Bn254;
pub type Fr = ark_bn254::Fr;
pub type G1 = ark_bn254::G1Affine;
pub type G2 = ark_bn254::G2Affine;

// Future:
// pub type Curve = ark_bls12_381::Bls12_381;
// pub type Fr = ark_bls12_381::Fr;
// ...etc

The whole SDK reads from Curve, Fr, G1, G2. The migration is a four-line swap and a re-ceremony. The cleanliness is on purpose — see Building the Zera SDK: Day One for why we boxed the curve choice the way we did.

What I tell people who ask “should I deploy on BN254 or BLS12-381?”: deploy on BN254 if you need to compose with the Ethereum precompile or the Solana alt_bn128_* syscall today, deploy on BLS12-381 if you don’t and you want the security headroom. The math is the math. The deployment surface is what makes the call.

Further reading

Hire me — book a 30-min call $ book →