Skip to main content

revm_precompile/bls12_381/
blst.rs

1// This module contains a safe wrapper around the blst library.
2
3use super::{G1Point, G2Point, PairingPair};
4use crate::{
5    bls12_381::{G1PointScalar, G2PointScalar},
6    bls12_381_const::{FP_LENGTH, G1_LENGTH, G2_LENGTH, SCALAR_LENGTH, SCALAR_LENGTH_BITS},
7    PrecompileError,
8};
9use blst::{
10    blst_bendian_from_fp, blst_final_exp, blst_fp, blst_fp12, blst_fp12_is_one, blst_fp12_mul,
11    blst_fp2, blst_fp_from_bendian, blst_map_to_g1, blst_map_to_g2, blst_miller_loop, blst_p1,
12    blst_p1_add_or_double_affine, blst_p1_affine, blst_p1_affine_in_g1, blst_p1_affine_on_curve,
13    blst_p1_from_affine, blst_p1_mult, blst_p1_to_affine, blst_p2, blst_p2_add_or_double_affine,
14    blst_p2_affine, blst_p2_affine_in_g2, blst_p2_affine_on_curve, blst_p2_from_affine,
15    blst_p2_mult, blst_p2_to_affine, blst_scalar, blst_scalar_from_bendian, MultiPoint,
16};
17use std::vec::Vec;
18
19// Big-endian non-Montgomery form.
20const MODULUS_REPR: [u8; 48] = [
21    0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7,
22    0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24,
23    0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab,
24];
25
26#[inline]
27pub(crate) fn p1_to_affine(p: &blst_p1) -> blst_p1_affine {
28    let mut p_affine = blst_p1_affine::default();
29    // SAFETY: both inputs are valid blst types
30    unsafe { blst_p1_to_affine(&mut p_affine, p) };
31    p_affine
32}
33
34#[inline]
35pub(crate) fn p1_from_affine(p_affine: &blst_p1_affine) -> blst_p1 {
36    let mut p = blst_p1::default();
37    // SAFETY: both inputs are valid blst types
38    unsafe { blst_p1_from_affine(&mut p, p_affine) };
39    p
40}
41
42#[inline]
43pub(crate) fn p1_add_or_double(p: &blst_p1, p_affine: &blst_p1_affine) -> blst_p1 {
44    let mut result = blst_p1::default();
45    // SAFETY: all inputs are valid blst types
46    unsafe { blst_p1_add_or_double_affine(&mut result, p, p_affine) };
47    result
48}
49
50#[inline]
51pub(crate) fn p2_to_affine(p: &blst_p2) -> blst_p2_affine {
52    let mut p_affine = blst_p2_affine::default();
53    // SAFETY: both inputs are valid blst types
54    unsafe { blst_p2_to_affine(&mut p_affine, p) };
55    p_affine
56}
57
58#[inline]
59pub(crate) fn p2_from_affine(p_affine: &blst_p2_affine) -> blst_p2 {
60    let mut p = blst_p2::default();
61    // SAFETY: both inputs are valid blst types
62    unsafe { blst_p2_from_affine(&mut p, p_affine) };
63    p
64}
65
66#[inline]
67pub(crate) fn p2_add_or_double(p: &blst_p2, p_affine: &blst_p2_affine) -> blst_p2 {
68    let mut result = blst_p2::default();
69    // SAFETY: all inputs are valid blst types
70    unsafe { blst_p2_add_or_double_affine(&mut result, p, p_affine) };
71    result
72}
73
74/// p1_add_affine adds two G1 points in affine form, returning the result in affine form
75///
76/// Note: `a` and `b` can be the same, ie this method is safe to call if one wants
77/// to essentially double a point
78#[inline]
79fn p1_add_affine(a: &blst_p1_affine, b: &blst_p1_affine) -> blst_p1_affine {
80    // Convert first point to Jacobian coordinates
81    let a_jacobian = p1_from_affine(a);
82
83    // Add second point (in affine) to first point (in Jacobian)
84    let sum_jacobian = p1_add_or_double(&a_jacobian, b);
85
86    // Convert result back to affine coordinates
87    p1_to_affine(&sum_jacobian)
88}
89
90/// Add two G2 points in affine form, returning the result in affine form
91#[inline]
92fn p2_add_affine(a: &blst_p2_affine, b: &blst_p2_affine) -> blst_p2_affine {
93    // Convert first point to Jacobian coordinates
94    let a_jacobian = p2_from_affine(a);
95
96    // Add second point (in affine) to first point (in Jacobian)
97    let sum_jacobian = p2_add_or_double(&a_jacobian, b);
98
99    // Convert result back to affine coordinates
100    p2_to_affine(&sum_jacobian)
101}
102
103/// Performs a G1 scalar multiplication
104///
105/// Takes a G1 point in affine form and a scalar, and returns the result
106/// of the scalar multiplication in affine form
107///
108/// Note: The scalar is expected to be in Big Endian format.
109#[inline]
110pub(crate) fn p1_scalar_mul(p: &blst_p1_affine, scalar: &blst_scalar) -> blst_p1_affine {
111    // Convert point to Jacobian coordinates
112    let p_jacobian = p1_from_affine(p);
113
114    let mut result = blst_p1::default();
115
116    // SAFETY: all inputs are valid blst types
117    unsafe {
118        blst_p1_mult(
119            &mut result,
120            &p_jacobian,
121            scalar.b.as_ptr(),
122            scalar.b.len() * 8,
123        )
124    };
125
126    // Convert result back to affine coordinates
127    p1_to_affine(&result)
128}
129
130/// Performs a G2 scalar multiplication
131///
132/// Takes a G2 point in affine form and a scalar, and returns the result
133/// of the scalar multiplication in affine form
134///
135/// Note: The scalar is expected to be in Big Endian format.
136#[inline]
137pub(crate) fn p2_scalar_mul(p: &blst_p2_affine, scalar: &blst_scalar) -> blst_p2_affine {
138    // Convert point to Jacobian coordinates
139    let p_jacobian = p2_from_affine(p);
140
141    let mut result = blst_p2::default();
142    // SAFETY: all inputs are valid blst types
143    unsafe {
144        blst_p2_mult(
145            &mut result,
146            &p_jacobian,
147            scalar.b.as_ptr(),
148            scalar.b.len() * 8,
149        )
150    };
151
152    // Convert result back to affine coordinates
153    p2_to_affine(&result)
154}
155
156/// Performs multi-scalar multiplication (MSM) for G1 points
157///
158/// Takes a vector of G1 points and corresponding scalars, and returns their weighted sum
159///
160/// Note: This method assumes that `g1_points` does not contain any points at infinity.
161#[inline]
162fn p1_msm(g1_points: Vec<blst_p1_affine>, scalars: Vec<blst_scalar>) -> blst_p1_affine {
163    assert_eq!(
164        g1_points.len(),
165        scalars.len(),
166        "number of scalars should equal the number of g1 points"
167    );
168
169    // When no inputs are given, we return the point at infinity.
170    // This case can only trigger, if the initial MSM pairs
171    // all had, either a zero scalar or the point at infinity.
172    //
173    // The precompile will return an error, if the initial input
174    // was empty, in accordance with EIP-2537.
175    if g1_points.is_empty() {
176        return blst_p1_affine::default();
177    }
178
179    // When there is only a single point, we use a simpler scalar multiplication
180    // procedure
181    if g1_points.len() == 1 {
182        return p1_scalar_mul(&g1_points[0], &scalars[0]);
183    }
184
185    // SAFETY: blst_scalar is repr(C) with a single `b: [u8; 32]` field.
186    let scalars_bytes =
187        unsafe { core::slice::from_raw_parts(scalars.as_ptr() as *const u8, scalars.len() * 32) };
188    // Perform multi-scalar multiplication
189    let multiexp = g1_points.mult(scalars_bytes, SCALAR_LENGTH_BITS);
190
191    // Convert result back to affine coordinates
192    p1_to_affine(&multiexp)
193}
194
195/// Performs multi-scalar multiplication (MSM) for G2 points
196///
197/// Takes a vector of G2 points and corresponding scalars, and returns their weighted sum
198///
199/// Note: Scalars are expected to be in Big Endian format.
200/// This method assumes that `g2_points` does not contain any points at infinity.
201#[inline]
202fn p2_msm(g2_points: Vec<blst_p2_affine>, scalars: Vec<blst_scalar>) -> blst_p2_affine {
203    assert_eq!(
204        g2_points.len(),
205        scalars.len(),
206        "number of scalars should equal the number of g2 points"
207    );
208
209    // When no inputs are given, we return the point at infinity.
210    // This case can only trigger, if the initial MSM pairs
211    // all had, either a zero scalar or the point at infinity.
212    //
213    // The precompile will return an error, if the initial input
214    // was empty, in accordance with EIP-2537.
215    if g2_points.is_empty() {
216        return blst_p2_affine::default();
217    }
218
219    // When there is only a single point, we use a simpler scalar multiplication
220    // procedure
221    if g2_points.len() == 1 {
222        return p2_scalar_mul(&g2_points[0], &scalars[0]);
223    }
224
225    // SAFETY: blst_scalar is repr(C) with a single `b: [u8; 32]` field.
226    let scalars_bytes =
227        unsafe { core::slice::from_raw_parts(scalars.as_ptr() as *const u8, scalars.len() * 32) };
228
229    // Perform multi-scalar multiplication
230    let multiexp = g2_points.mult(scalars_bytes, SCALAR_LENGTH_BITS);
231
232    // Convert result back to affine coordinates
233    p2_to_affine(&multiexp)
234}
235
236/// Maps a field element to a G1 point
237///
238/// Takes a field element (blst_fp) and returns the corresponding G1 point in affine form
239#[inline]
240fn map_fp_to_g1(fp: &blst_fp) -> blst_p1_affine {
241    // Create a new G1 point in Jacobian coordinates
242    let mut p = blst_p1::default();
243
244    // Map the field element to a point on the curve
245    // SAFETY: `p` and `fp` are blst values
246    // Third argument is unused if null
247    unsafe { blst_map_to_g1(&mut p, fp, core::ptr::null()) };
248
249    // Convert to affine coordinates
250    p1_to_affine(&p)
251}
252
253/// Maps a field element to a G2 point
254///
255/// Takes a field element (blst_fp2) and returns the corresponding G2 point in affine form
256#[inline]
257fn map_fp2_to_g2(fp2: &blst_fp2) -> blst_p2_affine {
258    // Create a new G2 point in Jacobian coordinates
259    let mut p = blst_p2::default();
260
261    // Map the field element to a point on the curve
262    // SAFETY: `p` and `fp2` are blst values
263    // Third argument is unused if null
264    unsafe { blst_map_to_g2(&mut p, fp2, core::ptr::null()) };
265
266    // Convert to affine coordinates
267    p2_to_affine(&p)
268}
269
270/// Computes a single miller loop for a given G1, G2 pair
271#[inline]
272fn compute_miller_loop(g1: &blst_p1_affine, g2: &blst_p2_affine) -> blst_fp12 {
273    let mut result = blst_fp12::default();
274
275    // SAFETY: All arguments are valid blst types
276    unsafe { blst_miller_loop(&mut result, g2, g1) }
277
278    result
279}
280
281/// multiply_fp12 multiplies two fp12 elements
282#[inline]
283fn multiply_fp12(a: &blst_fp12, b: &blst_fp12) -> blst_fp12 {
284    let mut result = blst_fp12::default();
285
286    // SAFETY: All arguments are valid blst types
287    unsafe { blst_fp12_mul(&mut result, a, b) }
288
289    result
290}
291
292/// final_exp computes the final exponentiation on an fp12 element
293#[inline]
294fn final_exp(f: &blst_fp12) -> blst_fp12 {
295    let mut result = blst_fp12::default();
296
297    // SAFETY: All arguments are valid blst types
298    unsafe { blst_final_exp(&mut result, f) }
299
300    result
301}
302
303/// is_fp12_one checks if an fp12 element equals
304/// multiplicative identity element, one
305#[inline]
306fn is_fp12_one(f: &blst_fp12) -> bool {
307    // SAFETY: argument is a valid blst type
308    unsafe { blst_fp12_is_one(f) }
309}
310
311/// pairing_check performs a pairing check on a list of G1 and G2 point pairs and
312/// returns true if the result is equal to the identity element.
313#[inline]
314pub(crate) fn pairing_check(pairs: &[(blst_p1_affine, blst_p2_affine)]) -> bool {
315    // When no inputs are given, we return true
316    // This case can only trigger, if the initial pairing components
317    // all had, either the G1 element as the point at infinity
318    // or the G2 element as the point at infinity.
319    //
320    // The precompile will return an error, if the initial input
321    // was empty, in accordance with EIP-2537.
322    if pairs.is_empty() {
323        return true;
324    }
325    // Compute the miller loop for the first pair
326    let (first_g1, first_g2) = &pairs[0];
327    let mut acc = compute_miller_loop(first_g1, first_g2);
328
329    // For the remaining pairs, compute miller loop and multiply with the accumulated result
330    for (g1, g2) in pairs.iter().skip(1) {
331        let ml = compute_miller_loop(g1, g2);
332        acc = multiply_fp12(&acc, &ml);
333    }
334
335    // Apply final exponentiation and check if result is 1
336    let final_result = final_exp(&acc);
337
338    // Check if the result is one (identity element)
339    is_fp12_one(&final_result)
340}
341
342/// Encodes a G1 point in affine format into byte slice.
343///
344/// Note: The encoded bytes are in Big Endian format.
345fn encode_g1_point(input: &blst_p1_affine) -> [u8; G1_LENGTH] {
346    let mut out = [0u8; G1_LENGTH];
347    fp_to_bytes(&mut out[..FP_LENGTH], &input.x);
348    fp_to_bytes(&mut out[FP_LENGTH..], &input.y);
349    out
350}
351
352/// Encodes a single finite field element into byte slice.
353///
354/// Note: The encoded bytes are in Big Endian format.
355fn fp_to_bytes(out: &mut [u8], input: &blst_fp) {
356    if out.len() != FP_LENGTH {
357        return;
358    }
359    // SAFETY: Out length is checked previously, `input` is a blst value.
360    unsafe { blst_bendian_from_fp(out.as_mut_ptr(), input) };
361}
362
363/// Returns a `blst_p1_affine` from the provided byte slices, which represent the x and y
364/// affine coordinates of the point.
365///
366/// Note: Coordinates are expected to be in Big Endian format.
367///
368/// - If the x or y coordinate do not represent a canonical field element, an error is returned.
369///   See [read_fp] for more information.
370/// - If the point is not on the curve, an error is returned.
371fn decode_g1_on_curve(
372    p0_x: &[u8; FP_LENGTH],
373    p0_y: &[u8; FP_LENGTH],
374) -> Result<blst_p1_affine, PrecompileError> {
375    let out = blst_p1_affine {
376        x: read_fp(p0_x)?,
377        y: read_fp(p0_y)?,
378    };
379
380    // From EIP-2537:
381    //
382    // Error cases:
383    //
384    // * An input is neither a point on the G1 elliptic curve nor the infinity point
385    //
386    // SAFETY: Out is a blst value.
387    if unsafe { !blst_p1_affine_on_curve(&out) } {
388        return Err(PrecompileError::Bls12381G1NotOnCurve);
389    }
390
391    Ok(out)
392}
393
394/// Extracts a G1 point in Affine format from the x and y coordinates.
395///
396/// Note: Coordinates are expected to be in Big Endian format.
397/// By default, subgroup checks are performed.
398fn read_g1(x: &[u8; FP_LENGTH], y: &[u8; FP_LENGTH]) -> Result<blst_p1_affine, PrecompileError> {
399    _extract_g1_input(x, y, true)
400}
401/// Extracts a G1 point in Affine format from the x and y coordinates
402/// without performing a subgroup check.
403///
404/// Note: Coordinates are expected to be in Big Endian format.
405/// Skipping subgroup checks can introduce security issues.
406/// This method should only be called if:
407///     - The EIP specifies that no subgroup check should be performed
408///     - One can be certain that the point is in the correct subgroup.
409fn read_g1_no_subgroup_check(
410    x: &[u8; FP_LENGTH],
411    y: &[u8; FP_LENGTH],
412) -> Result<blst_p1_affine, PrecompileError> {
413    _extract_g1_input(x, y, false)
414}
415/// Extracts a G1 point in Affine format from the x and y coordinates.
416///
417/// Note: Coordinates are expected to be in Big Endian format.
418/// This function will perform a G1 subgroup check if `subgroup_check` is set to `true`.
419fn _extract_g1_input(
420    x: &[u8; FP_LENGTH],
421    y: &[u8; FP_LENGTH],
422    subgroup_check: bool,
423) -> Result<blst_p1_affine, PrecompileError> {
424    let out = decode_g1_on_curve(x, y)?;
425
426    if subgroup_check {
427        // NB: Subgroup checks
428        //
429        // Scalar multiplications, MSMs and pairings MUST perform a subgroup check.
430        //
431        // Implementations SHOULD use the optimized subgroup check method:
432        //
433        // https://eips.ethereum.org/assets/eip-2537/fast_subgroup_checks
434        //
435        // On any input that fail the subgroup check, the precompile MUST return an error.
436        //
437        // As endomorphism acceleration requires input on the correct subgroup, implementers MAY
438        // use endomorphism acceleration.
439        if unsafe { !blst_p1_affine_in_g1(&out) } {
440            return Err(PrecompileError::Bls12381G1NotInSubgroup);
441        }
442    }
443    Ok(out)
444}
445
446/// Encodes a G2 point in affine format into byte slice.
447///
448/// Note: The encoded bytes are in Big Endian format.
449fn encode_g2_point(input: &blst_p2_affine) -> [u8; G2_LENGTH] {
450    let mut out = [0u8; G2_LENGTH];
451    fp_to_bytes(&mut out[..FP_LENGTH], &input.x.fp[0]);
452    fp_to_bytes(&mut out[FP_LENGTH..2 * FP_LENGTH], &input.x.fp[1]);
453    fp_to_bytes(&mut out[2 * FP_LENGTH..3 * FP_LENGTH], &input.y.fp[0]);
454    fp_to_bytes(&mut out[3 * FP_LENGTH..4 * FP_LENGTH], &input.y.fp[1]);
455    out
456}
457
458/// Returns a `blst_p2_affine` from the provided byte slices, which represent the x and y
459/// affine coordinates of the point.
460///
461/// Note: Coordinates are expected to be in Big Endian format.
462///
463/// - If the x or y coordinate do not represent a canonical field element, an error is returned.
464///   See [read_fp2] for more information.
465/// - If the point is not on the curve, an error is returned.
466fn decode_g2_on_curve(
467    x1: &[u8; FP_LENGTH],
468    x2: &[u8; FP_LENGTH],
469    y1: &[u8; FP_LENGTH],
470    y2: &[u8; FP_LENGTH],
471) -> Result<blst_p2_affine, PrecompileError> {
472    let out = blst_p2_affine {
473        x: read_fp2(x1, x2)?,
474        y: read_fp2(y1, y2)?,
475    };
476
477    // From EIP-2537:
478    //
479    // Error cases:
480    //
481    // * An input is neither a point on the G2 elliptic curve nor the infinity point
482    //
483    // SAFETY: Out is a blst value.
484    if unsafe { !blst_p2_affine_on_curve(&out) } {
485        return Err(PrecompileError::Bls12381G2NotOnCurve);
486    }
487
488    Ok(out)
489}
490
491/// Creates a blst_fp2 element from two field elements.
492///
493/// Field elements are expected to be in Big Endian format.
494/// Returns an error if either of the input field elements is not canonical.
495fn read_fp2(
496    input_1: &[u8; FP_LENGTH],
497    input_2: &[u8; FP_LENGTH],
498) -> Result<blst_fp2, PrecompileError> {
499    let fp_1 = read_fp(input_1)?;
500    let fp_2 = read_fp(input_2)?;
501
502    let fp2 = blst_fp2 { fp: [fp_1, fp_2] };
503
504    Ok(fp2)
505}
506/// Extracts a G2 point in Affine format from the x and y coordinates.
507///
508/// Note: Coordinates are expected to be in Big Endian format.
509/// By default, subgroup checks are performed.
510fn read_g2(
511    a_x_0: &[u8; FP_LENGTH],
512    a_x_1: &[u8; FP_LENGTH],
513    a_y_0: &[u8; FP_LENGTH],
514    a_y_1: &[u8; FP_LENGTH],
515) -> Result<blst_p2_affine, PrecompileError> {
516    _extract_g2_input(a_x_0, a_x_1, a_y_0, a_y_1, true)
517}
518/// Extracts a G2 point in Affine format from the x and y coordinates
519/// without performing a subgroup check.
520///
521/// Note: Coordinates are expected to be in Big Endian format.
522/// Skipping subgroup checks can introduce security issues.
523/// This method should only be called if:
524///     - The EIP specifies that no subgroup check should be performed
525///     - One can be certain that the point is in the correct subgroup.
526fn read_g2_no_subgroup_check(
527    a_x_0: &[u8; FP_LENGTH],
528    a_x_1: &[u8; FP_LENGTH],
529    a_y_0: &[u8; FP_LENGTH],
530    a_y_1: &[u8; FP_LENGTH],
531) -> Result<blst_p2_affine, PrecompileError> {
532    _extract_g2_input(a_x_0, a_x_1, a_y_0, a_y_1, false)
533}
534/// Extracts a G2 point in Affine format from the x and y coordinates.
535///
536/// Note: Coordinates are expected to be in Big Endian format.
537/// This function will perform a G2 subgroup check if `subgroup_check` is set to `true`.
538fn _extract_g2_input(
539    a_x_0: &[u8; FP_LENGTH],
540    a_x_1: &[u8; FP_LENGTH],
541    a_y_0: &[u8; FP_LENGTH],
542    a_y_1: &[u8; FP_LENGTH],
543    subgroup_check: bool,
544) -> Result<blst_p2_affine, PrecompileError> {
545    let out = decode_g2_on_curve(a_x_0, a_x_1, a_y_0, a_y_1)?;
546
547    if subgroup_check {
548        // NB: Subgroup checks
549        //
550        // Scalar multiplications, MSMs and pairings MUST perform a subgroup check.
551        //
552        // Implementations SHOULD use the optimized subgroup check method:
553        //
554        // https://eips.ethereum.org/assets/eip-2537/fast_subgroup_checks
555        //
556        // On any input that fail the subgroup check, the precompile MUST return an error.
557        //
558        // As endomorphism acceleration requires input on the correct subgroup, implementers MAY
559        // use endomorphism acceleration.
560        if unsafe { !blst_p2_affine_in_g2(&out) } {
561            return Err(PrecompileError::Bls12381G2NotInSubgroup);
562        }
563    }
564    Ok(out)
565}
566
567/// Checks whether or not the input represents a canonical field element
568/// returning the field element if successful.
569///
570/// Note: The field element is expected to be in big endian format.
571fn read_fp(input: &[u8; FP_LENGTH]) -> Result<blst_fp, PrecompileError> {
572    if !is_valid_be(input) {
573        return Err(PrecompileError::NonCanonicalFp);
574    }
575    let mut fp = blst_fp::default();
576    // SAFETY: `input` has fixed length, and `fp` is a blst value.
577    unsafe {
578        // This performs the check for canonical field elements
579        blst_fp_from_bendian(&mut fp, input.as_ptr());
580    }
581
582    Ok(fp)
583}
584
585/// Extracts a scalar from a 32 byte slice representation, decoding the input as a Big Endian
586/// unsigned integer. If the input is not exactly 32 bytes long, an error is returned.
587///
588/// From [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537):
589/// * A scalar for the multiplication operation is encoded as 32 bytes by performing BigEndian
590///   encoding of the corresponding (unsigned) integer.
591///
592/// We do not check that the scalar is a canonical Fr element, because the EIP specifies:
593/// * The corresponding integer is not required to be less than or equal than main subgroup order
594///   `q`.
595fn read_scalar(input: &[u8]) -> Result<blst_scalar, PrecompileError> {
596    if input.len() != SCALAR_LENGTH {
597        return Err(PrecompileError::Bls12381ScalarInputLength);
598    }
599
600    let mut out = blst_scalar::default();
601    // SAFETY: `input` length is checked previously, out is a blst value.
602    unsafe {
603        // Note: We do not use `blst_scalar_fr_check` here because, from EIP-2537:
604        //
605        // * The corresponding integer is not required to be less than or equal than main subgroup
606        // order `q`.
607        blst_scalar_from_bendian(&mut out, input.as_ptr())
608    };
609
610    Ok(out)
611}
612
613/// Checks if the input is a valid big-endian representation of a field element.
614fn is_valid_be(input: &[u8; 48]) -> bool {
615    *input < MODULUS_REPR
616}
617
618// Byte-oriented versions of the functions for external API compatibility
619
620/// Performs point addition on two G1 points taking byte coordinates.
621#[inline]
622pub(crate) fn p1_add_affine_bytes(
623    a: G1Point,
624    b: G1Point,
625) -> Result<[u8; G1_LENGTH], crate::PrecompileError> {
626    let (a_x, a_y) = a;
627    let (b_x, b_y) = b;
628    // Parse first point
629    let p1 = read_g1_no_subgroup_check(&a_x, &a_y)?;
630
631    // Parse second point
632    let p2 = read_g1_no_subgroup_check(&b_x, &b_y)?;
633
634    // Perform addition
635    let result = p1_add_affine(&p1, &p2);
636
637    // Encode result
638    Ok(encode_g1_point(&result))
639}
640
641/// Performs point addition on two G2 points taking byte coordinates.
642#[inline]
643pub(crate) fn p2_add_affine_bytes(
644    a: G2Point,
645    b: G2Point,
646) -> Result<[u8; G2_LENGTH], crate::PrecompileError> {
647    let (a_x_0, a_x_1, a_y_0, a_y_1) = a;
648    let (b_x_0, b_x_1, b_y_0, b_y_1) = b;
649    // Parse first point
650    let p1 = read_g2_no_subgroup_check(&a_x_0, &a_x_1, &a_y_0, &a_y_1)?;
651
652    // Parse second point
653    let p2 = read_g2_no_subgroup_check(&b_x_0, &b_x_1, &b_y_0, &b_y_1)?;
654
655    // Perform addition
656    let result = p2_add_affine(&p1, &p2);
657
658    // Encode result
659    Ok(encode_g2_point(&result))
660}
661
662/// Maps a field element to a G1 point from bytes
663#[inline]
664pub(crate) fn map_fp_to_g1_bytes(
665    fp_bytes: &[u8; FP_LENGTH],
666) -> Result<[u8; G1_LENGTH], crate::PrecompileError> {
667    let fp = read_fp(fp_bytes)?;
668    let result = map_fp_to_g1(&fp);
669    Ok(encode_g1_point(&result))
670}
671
672/// Maps field elements to a G2 point from bytes
673#[inline]
674pub(crate) fn map_fp2_to_g2_bytes(
675    fp2_x: &[u8; FP_LENGTH],
676    fp2_y: &[u8; FP_LENGTH],
677) -> Result<[u8; G2_LENGTH], crate::PrecompileError> {
678    let fp2 = read_fp2(fp2_x, fp2_y)?;
679    let result = map_fp2_to_g2(&fp2);
680    Ok(encode_g2_point(&result))
681}
682
683/// Performs multi-scalar multiplication (MSM) for G1 points taking byte inputs.
684#[inline]
685pub(crate) fn p1_msm_bytes(
686    point_scalar_pairs: impl Iterator<Item = Result<G1PointScalar, crate::PrecompileError>>,
687) -> Result<[u8; G1_LENGTH], crate::PrecompileError> {
688    let (lower, _) = point_scalar_pairs.size_hint();
689    let mut g1_points = Vec::with_capacity(lower);
690    let mut scalars = Vec::with_capacity(lower);
691
692    // Parse all points and scalars
693    for pair_result in point_scalar_pairs {
694        let ((x, y), scalar_bytes) = pair_result?;
695
696        // NB: MSM requires subgroup check
697        let point = read_g1(&x, &y)?;
698
699        // Skip zero scalars after validating the point
700        if scalar_bytes.iter().all(|&b| b == 0) {
701            continue;
702        }
703
704        let scalar = read_scalar(&scalar_bytes)?;
705        g1_points.push(point);
706        scalars.push(scalar);
707    }
708
709    // Return point at infinity if no pairs were provided or all scalars were zero
710    if g1_points.is_empty() {
711        return Ok([0u8; G1_LENGTH]);
712    }
713
714    // Perform MSM
715    let result = p1_msm(g1_points, scalars);
716
717    // Encode result
718    Ok(encode_g1_point(&result))
719}
720
721/// Performs multi-scalar multiplication (MSM) for G2 points taking byte inputs.
722#[inline]
723pub(crate) fn p2_msm_bytes(
724    point_scalar_pairs: impl Iterator<Item = Result<G2PointScalar, crate::PrecompileError>>,
725) -> Result<[u8; G2_LENGTH], crate::PrecompileError> {
726    let (lower, _) = point_scalar_pairs.size_hint();
727    let mut g2_points = Vec::with_capacity(lower);
728    let mut scalars = Vec::with_capacity(lower);
729
730    // Parse all points and scalars
731    for pair_result in point_scalar_pairs {
732        let ((x_0, x_1, y_0, y_1), scalar_bytes) = pair_result?;
733
734        // NB: MSM requires subgroup check
735        let point = read_g2(&x_0, &x_1, &y_0, &y_1)?;
736
737        // Skip zero scalars after validating the point
738        if scalar_bytes.iter().all(|&b| b == 0) {
739            continue;
740        }
741
742        let scalar = read_scalar(&scalar_bytes)?;
743        g2_points.push(point);
744        scalars.push(scalar);
745    }
746
747    // Return point at infinity if no pairs were provided or all scalars were zero
748    if g2_points.is_empty() {
749        return Ok([0u8; G2_LENGTH]);
750    }
751
752    // Perform MSM
753    let result = p2_msm(g2_points, scalars);
754
755    // Encode result
756    Ok(encode_g2_point(&result))
757}
758
759/// pairing_check_bytes performs a pairing check on a list of G1 and G2 point pairs taking byte inputs.
760#[inline]
761pub(crate) fn pairing_check_bytes(pairs: &[PairingPair]) -> Result<bool, crate::PrecompileError> {
762    super::pairing_common::pairing_check_bytes_generic(pairs, read_g1, read_g2, pairing_check)
763}