Skip to main content

BN254

zisklib functions for BN254 field arithmetic (Fp, Fp2, Fr), G1 curve operations, G2 / twist operations, the Fp6 / Fp12 tower, Miller loop, final exponentiation, cyclotomic subgroup operations, and full pairing computations.

Overview

BN254 (alt_bn128) is a ~100-bit Barreto-Naehrig pairing-friendly curve, powering Ethereum's 0x06 / 0x07 / 0x08 precompiles, Groth16, and EVM-flavored Plonk. It's y² = x³ + 3 over Fp (~254 bits), embedding degree 12, with the tower Fp ⊂ Fp2 = Fp[u]/(u² + 1) ⊂ Fp6 = Fp2[v]/(v³ − (u+9)) ⊂ Fp12 = Fp6[w]/(w² − v). G1 lives over Fp, G2 on a sextic twist over Fp2, and GT in Fp12; the scalar field Fr is also ~254 bits. Pairings are computed in two stages: a Miller loop turns a (G1, G2) pair into an Fp12 element, then a final exponentiation raises that value to (p¹² − 1)/r, projecting it into the cyclotomic subgroup of Fp12, the small-order subgroup where compressed squaring and exponentiation by the BN parameter x give the main speedups.

The page is organized bottom-up:

SectionProvidesDepends on
Base field (Fp)Arithmetic for every coordinate computation.
Extension field (Fp2)Quadratic extension Fp[u]/(u² + 1); host of G2 coordinates.Fp
Scalar field (Fr)Arithmetic for scalar multiplication and exponents.
UtilitiesByte/limb conversions bridging external encodings.
G1 curvePoint operations on y² = x³ + 3 over Fp.Fp
PairingHigh-level pairing_bn254, pairing_batch_bn254, pairing_check_bn254.G1, G2/twist, Fp12
G2 / twistPoint operations on the sextic twist over Fp2.Fp2
Fp6 towerCubic extension Fp2[v]/(v³ − (u+9)).Fp2
Fp12 towerQuadratic extension Fp6[w]/(w² − v); host of GT.Fp6
Miller loopMaps a (G1, G2) pair to an Fp12 element — the first half of pairing.G1, G2/twist, Fp12
Final exponentiationRaises an Fp12 element to (p¹² − 1)/r, projecting into the cyclotomic subgroup.Fp12
Cyclotomic subgroupCompressed squaring and exponentiation by x in the order-Φ₆(p²) subgroup of Fp12.Fp12

Base field (Fp)

The BN254 base field Fp, where p is a ~254-bit Barreto-Naehrig prime. Elements are encoded as [u64; 4] little-endian limbs (256 bits). Fp is the foundation of everything else on this page: G1 coordinates live in Fp, and the Fp2 / Fp6 / Fp12 tower is built on top of it.

Addition in the BN254 base field Fp.

pub fn add_fp_bn254(x: &[u64; 4], y: &[u64; 4]) -> [u64; 4]

Parameters

NameTypeDescription
x&[u64; 4]First Fp operand as 4 LE limbs.
y&[u64; 4]Second Fp operand as 4 LE limbs.

Returns

TypeDescription
[u64; 4]x + y mod p in the BN254 base field.

Example

let sum = zisklib::add_fp_bn254(&x_limbs, &y_limbs);

Extension field (Fp2)

The quadratic extension Fp2 = Fp[u] / (u² + 1). Elements are pairs (re, im) of Fp values, encoded as [u64; 8] (real part ++ imaginary part, 4 LE limbs each). G2 coordinates are Fp2 elements, and Fp6 and Fp12 are towers built on top of Fp2, so this section feeds both the G2 twist and the pairing target.

Addition in the BN254 extension field Fp2. Elements are [u64; 8] (real ++ imaginary, 4 LE limbs each).

pub fn add_fp2_bn254(a: &[u64; 8], b: &[u64; 8]) -> [u64; 8]

Parameters

NameTypeDescription
a&[u64; 8]First Fp2 element as 8 LE limbs (real ++ imaginary).
b&[u64; 8]Second Fp2 element as 8 LE limbs.

Returns

TypeDescription
[u64; 8]a + b in BN254 Fp2.

Example

let sum = zisklib::add_fp2_bn254(&a, &b);

Scalar field (Fr)

The BN254 scalar field Fr, of prime order r — the order of both G1 and G2. Fr is what scalars (e.g. private keys, Groth16 witness elements, pairing-equation exponents) live in. Elements are [u64; 4] LE limbs, matching the Fp layout but reduced modulo r rather than p.

Reduces a 256-bit integer modulo the BN254 scalar field order.

pub fn reduce_fr_bn254(x: &[u64; 4]) -> [u64; 4]

Parameters

NameTypeDescription
x&[u64; 4]256-bit integer as 4 LE limbs.

Returns

TypeDescription
[u64; 4]x mod r in the BN254 scalar field.

Example

let reduced = zisklib::reduce_fr_bn254(&scalar);

Utilities

Encoding helpers that bridge between external byte-oriented representations (such as the big-endian byte strings used by Ethereum precompiles and most BN254 serialization formats) and the internal little-endian limb layout used by the rest of this module.

Converts a big-endian 32-byte scalar to 4 little-endian u64 limbs.

pub fn scalar_bytes_be_to_u64_le_bn254(bytes: &[u8; 32]) -> [u64; 4]

Parameters

NameTypeDescription
bytes&[u8; 32]Big-endian 32-byte scalar representation.

Returns

TypeDescription
[u64; 4]The same value as 4 little-endian u64 limbs.

Example

let limbs = zisklib::scalar_bytes_be_to_u64_le_bn254(&be_bytes);

G1 curve

The BN254 G1 group: the order-r subgroup of the curve y² = x³ + 3 over Fp. Affine points are encoded as [u64; 8] (x ++ y). This is the "main" curve most users interact with — Groth16 proof elements, EVM precompiles 0x06 (add) and 0x07 (scalar mul), and the first argument of any pairing all live here. For users who only need G1 arithmetic plus a pairing check, this section together with Pairing covers the full surface.

Returns true if the G1 point p satisfies y² = x³ + 3.

pub fn is_on_curve_bn254(p: &[u64; 8]) -> bool

Parameters

NameTypeDescription
p&[u64; 8]Affine G1 point encoded as x ++ y, each 4 LE limbs.

Returns

TypeDescription
booltrue if p satisfies the BN254 G1 curve equation, false otherwise.

Example

assert!(zisklib::is_on_curve_bn254(&point));

Pairing

The high-level optimal Ate pairing e: G1 × G2 → GT ⊂ Fp12 on BN254. These entry points wrap the full pipeline (Miller loop followed by final exponentiation) so callers do not have to assemble it manually. Use pairing_bn254 for a single pair, pairing_batch_bn254 to multiply several pairings into one GT element, and pairing_check_bn254 for the EVM-style "product equals 1" verification used by Groth16 and most pairing-based SNARKs.

Optimal Ate pairing e(p, q) on BN254. Returns the GT element as [u64; 48] (Fp12). If either input is the identity, returns the Fp12 identity 1.

pub fn pairing_bn254(p: &[u64; 8], q: &[u64; 16]) -> [u64; 48]

Parameters

NameTypeDescription
p&[u64; 8]G1 point as x ++ y limbs.
q&[u64; 16]G2 point as Fp2 x ++ Fp2 y limbs.

Returns

TypeDescription
[u64; 48]The pairing result e(p, q) as an Fp12 GT element.

Example

let gt = zisklib::pairing_bn254(&g1_point, &g2_point);

G2 / twist

The BN254 G2 group, realized on the sextic twist E': y² = x³ + 3/(9 + u) defined over Fp2. Working on the twist lets G2 coordinates live in Fp2 ([u64; 16] per point) rather than Fp12, which is what makes pairing implementation tractable in practice. This section covers twist arithmetic plus the subgroup check and the untwist-Frobenius-twist endomorphism 𝜓 used by both the subgroup check and the Miller loop.

Converts a G2 (twist) point in Jacobian coordinates (x, y, z) over Fp2 to affine coordinates. Returns the G2 identity if z = 0, or the (x, y) limbs unchanged if z = 1.

pub fn jacobian_to_affine_twist_bn254(p: &[u64; 24]) -> [u64; 16]

Parameters

NameTypeDescription
p&[u64; 24]Jacobian G2 point, encoded as Fp2 x ++ Fp2 y ++ Fp2 z (8 LE limbs each).

Returns

TypeDescription
[u64; 16]The affine twist point encoded as Fp2 x ++ Fp2 y.

Example

let affine = zisklib::jacobian_to_affine_twist_bn254(&jac_q);

Fp6 tower

The cubic extension Fp6 = Fp2[v] / (v³ - (u + 9)). Elements are three Fp2 coefficients packed as [u64; 24] (a1 + a2·v + a3·v²). Fp6 is the middle layer of the tower: it is built on top of Fp2 and is itself the ground field for Fp12. The "sparse" multiplications here exist because the Miller loop and line evaluations produce Fp6/Fp12 operands with several coefficients equal to zero, and exploiting that sparsity is a significant pairing speedup.

Addition in the BN254 Fp6 extension. Elements are [u64; 24] (three Fp2 coefficients).

pub fn add_fp6_bn254(a: &[u64; 24], b: &[u64; 24]) -> [u64; 24]

Parameters

NameTypeDescription
a&[u64; 24]First Fp6 element.
b&[u64; 24]Second Fp6 element.

Returns

TypeDescription
[u64; 24]a + b in Fp6.

Fp12 tower

The quadratic extension Fp12 = Fp6[w] / (w² - v), the top of the tower and the home of the pairing target GT. Elements are two Fp6 coefficients packed as [u64; 48] (a1 + a2·w). Alongside basic arithmetic, this section provides the Frobenius operators a^p, a^(p²), a^(p³), conjugation, and a generic 64-bit exponentiation — all building blocks used by the Miller loop and the final exponentiation.

Full multiplication in the BN254 Fp12 extension. Elements are [u64; 48] (two Fp6 coefficients a1 + a2·w).

pub fn mul_fp12_bn254(a: &[u64; 48], b: &[u64; 48]) -> [u64; 48]

Parameters

NameTypeDescription
a&[u64; 48]First Fp12 operand.
b&[u64; 48]Second Fp12 operand.

Returns

TypeDescription
[u64; 48]a · b in Fp12.

Miller loop

The Miller loop is the first half of the optimal Ate pairing: it walks a fixed addition chain derived from the BN parameter x, accumulating G2 line evaluations into a running Fp12 value. The output is an Fp12 element that is not yet in GT — it must be passed through final_exp_bn254 to land in the cyclotomic subgroup. Use the batch variant to amortize the loop iterations and final squarings across several G1/G2 pairs.

Computes the Miller loop for the optimal Ate pairing of a non-zero G1 point p and a non-zero G2 point q over BN254. The result must be combined with final_exp_bn254 to obtain a pairing.

pub fn miller_loop_bn254(p: &[u64; 8], q: &[u64; 16]) -> [u64; 48]

Parameters

NameTypeDescription
p&[u64; 8]Non-zero G1 point.
q&[u64; 16]Non-zero G2 point.

Returns

TypeDescription
[u64; 48]The Miller loop value as an Fp12 element.

Final exponentiation

The second half of the pairing pipeline: raises a Miller loop output f ∈ Fp12 to the power (p¹² - 1)/r, projecting it into the order-r target group GT. The implementation splits this exponent into an "easy part" ((p⁶ - 1)(p² + 1), done with conjugation, inversion, and Frobenius) and a "hard part" ((p⁴ - p² + 1)/r, done with cyclotomic squarings and exponentiation by the BN parameter x — see Cyclotomic subgroup).

Final exponentiation f^((p¹² - 1)/r) for BN254 pairings. Combines an "easy part" (exponent (p⁶ - 1)(p² + 1)) using conjugation, inversion, and the second Frobenius, and a "hard part" ((p⁴ - p² + 1)/r) using cyclotomic exponentiation by the BN curve parameter x.

pub fn final_exp_bn254(f: &[u64; 48]) -> [u64; 48]

Parameters

NameTypeDescription
f&[u64; 48]Fp12 element (typically the output of a Miller loop).

Returns

TypeDescription
[u64; 48]f^((p¹² - 1)/r) in Fp12, an element of GT.

Cyclotomic subgroup

The cyclotomic subgroup GΦ₆(p²) ⊂ Fp12 is where elements live after the easy part of the final exponentiation, and it is where GT sits. In this subgroup, Karabina compression represents an Fp12 element using just four Fp2 coefficients ([u64; 32] instead of [u64; 48]), and squaring can be performed directly on the compressed form. These operations are the inner loop of the final exponentiation's hard part and of any pairing-product accumulation in GT.

Karabina compression of a cyclotomic-subgroup element of Fp12. Returns four Fp2 coefficients [a2, a3, a4, a5] packed as [u64; 32].

pub fn compress_cyclo_bn254(a: &[u64; 48]) -> [u64; 32]
warning

If the input does not belong to the cyclotomic subgroup GΦ₆(p²), the compression / decompression is not well defined — decompress_cyclo_bn254(compress_cyclo_bn254(a)) != a in general.

Parameters

NameTypeDescription
a&[u64; 48]Fp12 element in the cyclotomic subgroup.

Returns

TypeDescription
[u64; 32]Compressed form [a2, a3, a4, a5].