revm_precompile/bn128/
arkworks.rs

1use super::{FQ2_LEN, FQ_LEN, G1_LEN, SCALAR_LEN};
2use crate::PrecompileError;
3use std::vec::Vec;
4
5use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G1Projective, G2Affine};
6use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup};
7use ark_ff::{One, PrimeField, Zero};
8use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
9
10/// Reads a single `Fq` field element from the input slice.
11///
12/// Takes a byte slice and attempts to interpret the first 32 bytes as an
13/// elliptic curve field element. Returns an error if the bytes do not form
14/// a valid field element.
15///
16/// # Panics
17///
18/// Panics if the input is not at least 32 bytes long.
19#[inline]
20fn read_fq(input_be: &[u8]) -> Result<Fq, PrecompileError> {
21    assert_eq!(input_be.len(), FQ_LEN, "input must be {FQ_LEN} bytes");
22
23    let mut input_le = [0u8; FQ_LEN];
24    input_le.copy_from_slice(input_be);
25
26    // Reverse in-place to convert from big-endian to little-endian.
27    input_le.reverse();
28
29    Fq::deserialize_uncompressed(&input_le[..])
30        .map_err(|_| PrecompileError::Bn128FieldPointNotAMember)
31}
32/// Reads a Fq2 (quadratic extension field element) from the input slice.
33///
34/// Parses two consecutive Fq field elements as the real and imaginary parts
35/// of an Fq2 element.
36/// The second component is parsed before the first, ie if a we represent an
37/// element in Fq2 as (x,y) -- `y` is parsed before `x`
38///
39/// # Panics
40///
41/// Panics if the input is not at least 64 bytes long.
42#[inline]
43fn read_fq2(input: &[u8]) -> Result<Fq2, PrecompileError> {
44    let y = read_fq(&input[..FQ_LEN])?;
45    let x = read_fq(&input[FQ_LEN..2 * FQ_LEN])?;
46
47    Ok(Fq2::new(x, y))
48}
49
50/// Creates a new `G1` point from the given `x` and `y` coordinates.
51///
52/// Constructs a point on the G1 curve from its affine coordinates.
53///
54/// Note: The point at infinity which is represented as (0,0) is
55/// handled specifically because `AffineG1` is not capable of
56/// representing such a point.
57/// In particular, when we convert from `AffineG1` to `G1`, the point
58/// will be (0,0,1) instead of (0,1,0)
59#[inline]
60fn new_g1_point(px: Fq, py: Fq) -> Result<G1Affine, PrecompileError> {
61    if px.is_zero() && py.is_zero() {
62        Ok(G1Affine::zero())
63    } else {
64        // We cannot use `G1Affine::new` because that triggers an assert if the point is not on the curve.
65        let point = G1Affine::new_unchecked(px, py);
66        if !point.is_on_curve() || !point.is_in_correct_subgroup_assuming_on_curve() {
67            return Err(PrecompileError::Bn128AffineGFailedToCreate);
68        }
69        Ok(point)
70    }
71}
72
73/// Creates a new `G2` point from the given Fq2 coordinates.
74///
75/// G2 points in BN128 are defined over a quadratic extension field Fq2.
76/// This function takes two Fq2 elements representing the x and y coordinates
77/// and creates a G2 point.
78///
79/// Note: The point at infinity which is represented as (0,0) is
80/// handled specifically because `AffineG2` is not capable of
81/// representing such a point.
82/// In particular, when we convert from `AffineG2` to `G2`, the point
83/// will be (0,0,1) instead of (0,1,0)
84#[inline]
85fn new_g2_point(x: Fq2, y: Fq2) -> Result<G2Affine, PrecompileError> {
86    let point = if x.is_zero() && y.is_zero() {
87        G2Affine::zero()
88    } else {
89        // We cannot use `G1Affine::new` because that triggers an assert if the point is not on the curve.
90        let point = G2Affine::new_unchecked(x, y);
91        if !point.is_on_curve() || !point.is_in_correct_subgroup_assuming_on_curve() {
92            return Err(PrecompileError::Bn128AffineGFailedToCreate);
93        }
94        point
95    };
96
97    Ok(point)
98}
99
100/// Reads a G1 point from the input slice.
101///
102/// Parses a G1 point from a byte slice by reading two consecutive field elements
103/// representing the x and y coordinates.
104///
105/// # Panics
106///
107/// Panics if the input is not at least 64 bytes long.
108#[inline]
109pub(super) fn read_g1_point(input: &[u8]) -> Result<G1Affine, PrecompileError> {
110    let px = read_fq(&input[0..FQ_LEN])?;
111    let py = read_fq(&input[FQ_LEN..2 * FQ_LEN])?;
112    new_g1_point(px, py)
113}
114
115/// Encodes a G1 point into a byte array.
116///
117/// Converts a G1 point in Jacobian coordinates to affine coordinates and
118/// serializes the x and y coordinates as big-endian byte arrays.
119///
120/// Note: If the point is the point at infinity, this function returns
121/// all zeroes.
122#[inline]
123pub(super) fn encode_g1_point(point: G1Affine) -> [u8; G1_LEN] {
124    let mut output = [0u8; G1_LEN];
125    let Some((x, y)) = point.xy() else {
126        return output;
127    };
128
129    let mut x_bytes = [0u8; FQ_LEN];
130    x.serialize_uncompressed(&mut x_bytes[..])
131        .expect("Failed to serialize x coordinate");
132
133    let mut y_bytes = [0u8; FQ_LEN];
134    y.serialize_uncompressed(&mut y_bytes[..])
135        .expect("Failed to serialize x coordinate");
136
137    // Convert to big endian by reversing the bytes.
138    x_bytes.reverse();
139    y_bytes.reverse();
140
141    // Place x in the first half, y in the second half.
142    output[0..FQ_LEN].copy_from_slice(&x_bytes);
143    output[FQ_LEN..(FQ_LEN * 2)].copy_from_slice(&y_bytes);
144
145    output
146}
147
148/// Reads a G2 point from the input slice.
149///
150/// Parses a G2 point from a byte slice by reading four consecutive Fq field elements
151/// representing the two Fq2 coordinates (x and y) of the G2 point.
152///
153/// # Panics
154///
155/// Panics if the input is not at least 128 bytes long.
156#[inline]
157pub(super) fn read_g2_point(input: &[u8]) -> Result<G2Affine, PrecompileError> {
158    let ba = read_fq2(&input[0..FQ2_LEN])?;
159    let bb = read_fq2(&input[FQ2_LEN..2 * FQ2_LEN])?;
160    new_g2_point(ba, bb)
161}
162
163/// Reads a scalar from the input slice
164///
165/// Note: The scalar does not need to be canonical.
166///
167/// # Panics
168///
169/// If `input.len()` is not equal to [`SCALAR_LEN`].
170#[inline]
171pub(super) fn read_scalar(input: &[u8]) -> Fr {
172    assert_eq!(
173        input.len(),
174        SCALAR_LEN,
175        "unexpected scalar length. got {}, expected {SCALAR_LEN}",
176        input.len()
177    );
178    Fr::from_be_bytes_mod_order(input)
179}
180
181/// Performs point addition on two G1 points.
182#[inline]
183pub(super) fn g1_point_add(p1: G1Affine, p2: G1Affine) -> G1Affine {
184    let p1_jacobian: G1Projective = p1.into();
185
186    let p3 = p1_jacobian + p2;
187
188    p3.into_affine()
189}
190
191/// Performs a G1 scalar multiplication.
192#[inline]
193pub(super) fn g1_point_mul(p: G1Affine, fr: Fr) -> G1Affine {
194    let big_int = fr.into_bigint();
195    let result = p.mul_bigint(big_int);
196
197    result.into_affine()
198}
199
200/// pairing_check performs a pairing check on a list of G1 and G2 point pairs and
201/// returns true if the result is equal to the identity element.
202///
203/// Note: If the input is empty, this function returns true.
204/// This is different to EIP2537 which disallows the empty input.
205#[inline]
206pub(super) fn pairing_check(pairs: &[(G1Affine, G2Affine)]) -> bool {
207    if pairs.is_empty() {
208        return true;
209    }
210
211    let (g1_points, g2_points): (Vec<G1Affine>, Vec<G2Affine>) = pairs.iter().copied().unzip();
212
213    let pairing_result = Bn254::multi_pairing(&g1_points, &g2_points);
214    pairing_result.0.is_one()
215}