revm_precompile/bls12_381/
g2.rs

1use super::utils::{fp_from_bendian, fp_to_bytes, remove_padding, FP_LENGTH, PADDED_FP_LENGTH};
2use crate::primitives::{Bytes, PrecompileError};
3use blst::{blst_fp2, blst_p2_affine, blst_p2_affine_in_g2, blst_p2_affine_on_curve};
4
5/// Length of each of the elements in a g2 operation input.
6pub(super) const G2_INPUT_ITEM_LENGTH: usize = 256;
7
8/// Output length of a g2 operation.
9const G2_OUTPUT_LENGTH: usize = 256;
10
11/// Encodes a G2 point in affine format into byte slice with padded elements.
12pub(super) fn encode_g2_point(input: &blst_p2_affine) -> Bytes {
13    let mut out = vec![0u8; G2_OUTPUT_LENGTH];
14    fp_to_bytes(&mut out[..PADDED_FP_LENGTH], &input.x.fp[0]);
15    fp_to_bytes(
16        &mut out[PADDED_FP_LENGTH..2 * PADDED_FP_LENGTH],
17        &input.x.fp[1],
18    );
19    fp_to_bytes(
20        &mut out[2 * PADDED_FP_LENGTH..3 * PADDED_FP_LENGTH],
21        &input.y.fp[0],
22    );
23    fp_to_bytes(
24        &mut out[3 * PADDED_FP_LENGTH..4 * PADDED_FP_LENGTH],
25        &input.y.fp[1],
26    );
27    out.into()
28}
29
30/// Convert the following field elements from byte slices into a `blst_p2_affine` point.
31pub(super) fn decode_and_check_g2(
32    x1: &[u8; 48],
33    x2: &[u8; 48],
34    y1: &[u8; 48],
35    y2: &[u8; 48],
36) -> Result<blst_p2_affine, PrecompileError> {
37    Ok(blst_p2_affine {
38        x: check_canonical_fp2(x1, x2)?,
39        y: check_canonical_fp2(y1, y2)?,
40    })
41}
42
43/// Checks whether or not the input represents a canonical fp2 field element, returning the field
44/// element if successful.
45pub(super) fn check_canonical_fp2(
46    input_1: &[u8; 48],
47    input_2: &[u8; 48],
48) -> Result<blst_fp2, PrecompileError> {
49    let fp_1 = fp_from_bendian(input_1)?;
50    let fp_2 = fp_from_bendian(input_2)?;
51
52    let fp2 = blst_fp2 { fp: [fp_1, fp_2] };
53
54    Ok(fp2)
55}
56
57/// Extracts a G2 point in Affine format from a 256 byte slice representation.
58///
59/// NOTE: This function will perform a G2 subgroup check if `subgroup_check` is set to `true`.
60pub(super) fn extract_g2_input(
61    input: &[u8],
62    subgroup_check: bool,
63) -> Result<blst_p2_affine, PrecompileError> {
64    if input.len() != G2_INPUT_ITEM_LENGTH {
65        return Err(PrecompileError::Other(format!(
66            "Input should be {G2_INPUT_ITEM_LENGTH} bytes, was {}",
67            input.len()
68        )));
69    }
70
71    let mut input_fps = [&[0; FP_LENGTH]; 4];
72    for i in 0..4 {
73        input_fps[i] = remove_padding(&input[i * PADDED_FP_LENGTH..(i + 1) * PADDED_FP_LENGTH])?;
74    }
75
76    let out = decode_and_check_g2(input_fps[0], input_fps[1], input_fps[2], input_fps[3])?;
77
78    if subgroup_check {
79        // NB: Subgroup checks
80        //
81        // Scalar multiplications, MSMs and pairings MUST perform a subgroup check.
82        //
83        // Implementations SHOULD use the optimized subgroup check method:
84        //
85        // https://eips.ethereum.org/assets/eip-2537/fast_subgroup_checks
86        //
87        // On any input that fail the subgroup check, the precompile MUST return an error.
88        //
89        // As endomorphism acceleration requires input on the correct subgroup, implementers MAY
90        // use endomorphism acceleration.
91        if unsafe { !blst_p2_affine_in_g2(&out) } {
92            return Err(PrecompileError::Other("Element not in G2".to_string()));
93        }
94    } else {
95        // From EIP-2537:
96        //
97        // Error cases:
98        //
99        // * An input is neither a point on the G2 elliptic curve nor the infinity point
100        //
101        // NB: There is no subgroup check for the G2 addition precompile.
102        //
103        // We use blst_p2_affine_on_curve instead of blst_p2_affine_in_g2 because the latter performs
104        // the subgroup check.
105        //
106        // SAFETY: out is a blst value.
107        if unsafe { !blst_p2_affine_on_curve(&out) } {
108            return Err(PrecompileError::Other(
109                "Element not on G2 curve".to_string(),
110            ));
111        }
112    }
113
114    Ok(out)
115}