revm_precompile/bls12_381/
pairing.rs

1use super::crypto_backend::{pairing_check, read_g1, read_g2};
2use super::utils::{remove_g1_padding, remove_g2_padding};
3use crate::bls12_381_const::{
4    PADDED_G1_LENGTH, PADDED_G2_LENGTH, PAIRING_ADDRESS, PAIRING_INPUT_LENGTH,
5    PAIRING_MULTIPLIER_BASE, PAIRING_OFFSET_BASE,
6};
7use crate::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress};
8use primitives::{Bytes, B256};
9use std::vec::Vec;
10
11/// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_PAIRING precompile.
12pub const PRECOMPILE: PrecompileWithAddress = PrecompileWithAddress(PAIRING_ADDRESS, pairing);
13
14/// Pairing call expects 384*k (k being a positive integer) bytes as an inputs
15/// that is interpreted as byte concatenation of k slices. Each slice has the
16/// following structure:
17///    * 128 bytes of G1 point encoding
18///    * 256 bytes of G2 point encoding
19///
20/// Each point is expected to be in the subgroup of order q.
21/// Output is 32 bytes where first 31 bytes are equal to 0x00 and the last byte
22/// is 0x01 if pairing result is equal to the multiplicative identity in a pairing
23/// target field and 0x00 otherwise.
24///
25/// See also: <https://eips.ethereum.org/EIPS/eip-2537#abi-for-pairing>
26pub(super) fn pairing(input: &Bytes, gas_limit: u64) -> PrecompileResult {
27    let input_len = input.len();
28    if input_len == 0 || input_len % PAIRING_INPUT_LENGTH != 0 {
29        return Err(PrecompileError::Other(format!(
30            "Pairing input length should be multiple of {PAIRING_INPUT_LENGTH}, was {input_len}"
31        )));
32    }
33
34    let k = input_len / PAIRING_INPUT_LENGTH;
35    let required_gas: u64 = PAIRING_MULTIPLIER_BASE * k as u64 + PAIRING_OFFSET_BASE;
36    if required_gas > gas_limit {
37        return Err(PrecompileError::OutOfGas);
38    }
39
40    // Collect pairs of points for the pairing check
41    let mut pairs = Vec::with_capacity(k);
42    for i in 0..k {
43        let encoded_g1_element =
44            &input[i * PAIRING_INPUT_LENGTH..i * PAIRING_INPUT_LENGTH + PADDED_G1_LENGTH];
45        let encoded_g2_element = &input[i * PAIRING_INPUT_LENGTH + PADDED_G1_LENGTH
46            ..i * PAIRING_INPUT_LENGTH + PADDED_G1_LENGTH + PADDED_G2_LENGTH];
47
48        // If either the G1 or G2 element is the encoded representation
49        // of the point at infinity, then these two points are no-ops
50        // in the pairing computation.
51        //
52        // Note: we do not skip the validation of these two elements even if
53        // one of them is the point at infinity because we could have G1 be
54        // the point at infinity and G2 be an invalid element or vice versa.
55        // In that case, the precompile should error because one of the elements
56        // was invalid.
57        let g1_is_zero = encoded_g1_element.iter().all(|i| *i == 0);
58        let g2_is_zero = encoded_g2_element.iter().all(|i| *i == 0);
59
60        let [a_x, a_y] = remove_g1_padding(encoded_g1_element)?;
61        let [b_x_0, b_x_1, b_y_0, b_y_1] = remove_g2_padding(encoded_g2_element)?;
62
63        // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check.
64        // extract_g1_input and extract_g2_input perform the necessary checks
65        let p1_aff = read_g1(a_x, a_y)?;
66        let p2_aff = read_g2(b_x_0, b_x_1, b_y_0, b_y_1)?;
67
68        if !g1_is_zero & !g2_is_zero {
69            pairs.push((p1_aff, p2_aff));
70        }
71    }
72    let result = if pairing_check(&pairs) { 1 } else { 0 };
73
74    Ok(PrecompileOutput::new(
75        required_gas,
76        B256::with_last_byte(result).into(),
77    ))
78}