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}