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:
| Section | Provides | Depends 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. | — |
| Utilities | Byte/limb conversions bridging external encodings. | — |
| G1 curve | Point operations on y² = x³ + 3 over Fp. | Fp |
| Pairing | High-level pairing_bn254, pairing_batch_bn254, pairing_check_bn254. | G1, G2/twist, Fp12 |
| G2 / twist | Point operations on the sextic twist over Fp2. | Fp2 |
| Fp6 tower | Cubic extension Fp2[v]/(v³ − (u+9)). | Fp2 |
| Fp12 tower | Quadratic extension Fp6[w]/(w² − v); host of GT. | Fp6 |
| Miller loop | Maps a (G1, G2) pair to an Fp12 element — the first half of pairing. | G1, G2/twist, Fp12 |
| Final exponentiation | Raises an Fp12 element to (p¹² − 1)/r, projecting into the cyclotomic subgroup. | Fp12 |
| Cyclotomic subgroup | Compressed 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
| Name | Type | Description |
|---|---|---|
x | &[u64; 4] | First Fp operand as 4 LE limbs. |
y | &[u64; 4] | Second Fp operand as 4 LE limbs. |
Returns
| Type | Description |
|---|---|
[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
| Name | Type | Description |
|---|---|---|
a | &[u64; 8] | First Fp2 element as 8 LE limbs (real ++ imaginary). |
b | &[u64; 8] | Second Fp2 element as 8 LE limbs. |
Returns
| Type | Description |
|---|---|
[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
| Name | Type | Description |
|---|---|---|
x | &[u64; 4] | 256-bit integer as 4 LE limbs. |
Returns
| Type | Description |
|---|---|
[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
| Name | Type | Description |
|---|---|---|
bytes | &[u8; 32] | Big-endian 32-byte scalar representation. |
Returns
| Type | Description |
|---|---|
[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
| Name | Type | Description |
|---|---|---|
p | &[u64; 8] | Affine G1 point encoded as x ++ y, each 4 LE limbs. |
Returns
| Type | Description |
|---|---|
bool | true 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
| Name | Type | Description |
|---|---|---|
p | &[u64; 8] | G1 point as x ++ y limbs. |
q | &[u64; 16] | G2 point as Fp2 x ++ Fp2 y limbs. |
Returns
| Type | Description |
|---|---|
[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
| Name | Type | Description |
|---|---|---|
p | &[u64; 24] | Jacobian G2 point, encoded as Fp2 x ++ Fp2 y ++ Fp2 z (8 LE limbs each). |
Returns
| Type | Description |
|---|---|
[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.
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
| Name | Type | Description |
|---|---|---|
a | &[u64; 48] | First Fp12 operand. |
b | &[u64; 48] | Second Fp12 operand. |
Returns
| Type | Description |
|---|---|
[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
| Name | Type | Description |
|---|---|---|
p | &[u64; 8] | Non-zero G1 point. |
q | &[u64; 16] | Non-zero G2 point. |
Returns
| Type | Description |
|---|---|
[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
| Name | Type | Description |
|---|---|---|
f | &[u64; 48] | Fp12 element (typically the output of a Miller loop). |
Returns
| Type | Description |
|---|---|
[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]
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
| Name | Type | Description |
|---|---|---|
a | &[u64; 48] | Fp12 element in the cyclotomic subgroup. |
Returns
| Type | Description |
|---|---|
[u64; 32] | Compressed form [a2, a3, a4, a5]. |