revm_precompile/bls12_381/
g1.rs

1use super::utils::{fp_from_bendian, fp_to_bytes, remove_padding, PADDED_FP_LENGTH};
2use crate::primitives::{Bytes, PrecompileError};
3use blst::{blst_p1_affine, blst_p1_affine_in_g1, blst_p1_affine_on_curve};
4
5/// Length of each of the elements in a g1 operation input.
6pub(super) const G1_INPUT_ITEM_LENGTH: usize = 128;
7
8/// Output length of a g1 operation.
9const G1_OUTPUT_LENGTH: usize = 128;
10
11/// Encodes a G1 point in affine format into byte slice with padded elements.
12pub(super) fn encode_g1_point(input: *const blst_p1_affine) -> Bytes {
13    let mut out = vec![0u8; G1_OUTPUT_LENGTH];
14    // SAFETY: out comes from fixed length array, input is a blst value.
15    unsafe {
16        fp_to_bytes(&mut out[..PADDED_FP_LENGTH], &(*input).x);
17        fp_to_bytes(&mut out[PADDED_FP_LENGTH..], &(*input).y);
18    }
19    out.into()
20}
21
22/// Returns a `blst_p1_affine` from the provided byte slices, which represent the x and y
23/// affine coordinates of the point.
24///
25/// If the x or y coordinate do not represent a canonical field element, an error is returned.
26///
27/// See [fp_from_bendian] for more information.
28pub(super) fn decode_and_check_g1(
29    p0_x: &[u8; 48],
30    p0_y: &[u8; 48],
31) -> Result<blst_p1_affine, PrecompileError> {
32    let out = blst_p1_affine {
33        x: fp_from_bendian(p0_x)?,
34        y: fp_from_bendian(p0_y)?,
35    };
36
37    Ok(out)
38}
39
40/// Extracts a G1 point in Affine format from a 128 byte slice representation.
41///
42/// NOTE: This function will perform a G1 subgroup check if `subgroup_check` is set to `true`.
43pub(super) fn extract_g1_input(
44    input: &[u8],
45    subgroup_check: bool,
46) -> Result<blst_p1_affine, PrecompileError> {
47    if input.len() != G1_INPUT_ITEM_LENGTH {
48        return Err(PrecompileError::Other(format!(
49            "Input should be {G1_INPUT_ITEM_LENGTH} bytes, was {}",
50            input.len()
51        )));
52    }
53
54    let input_p0_x = remove_padding(&input[..PADDED_FP_LENGTH])?;
55    let input_p0_y = remove_padding(&input[PADDED_FP_LENGTH..G1_INPUT_ITEM_LENGTH])?;
56    let out = decode_and_check_g1(input_p0_x, input_p0_y)?;
57
58    if subgroup_check {
59        // NB: Subgroup checks
60        //
61        // Scalar multiplications, MSMs and pairings MUST perform a subgroup check.
62        //
63        // Implementations SHOULD use the optimized subgroup check method:
64        //
65        // https://eips.ethereum.org/assets/eip-2537/fast_subgroup_checks
66        //
67        // On any input that fail the subgroup check, the precompile MUST return an error.
68        //
69        // As endomorphism acceleration requires input on the correct subgroup, implementers MAY
70        // use endomorphism acceleration.
71        if unsafe { !blst_p1_affine_in_g1(&out) } {
72            return Err(PrecompileError::Other("Element not in G1".to_string()));
73        }
74    } else {
75        // From EIP-2537:
76        //
77        // Error cases:
78        //
79        // * An input is neither a point on the G1 elliptic curve nor the infinity point
80        //
81        // NB: There is no subgroup check for the G1 addition precompile.
82        //
83        // We use blst_p1_affine_on_curve instead of blst_p1_affine_in_g1 because the latter performs
84        // the subgroup check.
85        //
86        // SAFETY: out is a blst value.
87        if unsafe { !blst_p1_affine_on_curve(&out) } {
88            return Err(PrecompileError::Other(
89                "Element not on G1 curve".to_string(),
90            ));
91        }
92    }
93
94    Ok(out)
95}