revm_precompile/bls12_381/
pairing.rs

1use super::{
2    g1::{extract_g1_input, G1_INPUT_ITEM_LENGTH},
3    g2::{extract_g2_input, G2_INPUT_ITEM_LENGTH},
4};
5use crate::{u64_to_address, PrecompileWithAddress};
6use blst::{blst_final_exp, blst_fp12, blst_fp12_is_one, blst_fp12_mul, blst_miller_loop};
7use revm_primitives::{
8    Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult, B256,
9};
10
11/// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_PAIRING precompile.
12pub const PRECOMPILE: PrecompileWithAddress =
13    PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(pairing));
14/// BLS12_PAIRING precompile address.
15pub const ADDRESS: u64 = 0x11;
16
17/// Multiplier gas fee for BLS12-381 pairing operation.
18const PAIRING_MULTIPLIER_BASE: u64 = 43000;
19/// Offset gas fee for BLS12-381 pairing operation.
20const PAIRING_OFFSET_BASE: u64 = 65000;
21/// Input length of pairing operation.
22const INPUT_LENGTH: usize = 384;
23
24/// Pairing call expects 384*k (k being a positive integer) bytes as an inputs
25/// that is interpreted as byte concatenation of k slices. Each slice has the
26/// following structure:
27///    * 128 bytes of G1 point encoding
28///    * 256 bytes of G2 point encoding
29///
30/// Each point is expected to be in the subgroup of order q.
31/// Output is 32 bytes where first 31 bytes are equal to 0x00 and the last byte
32/// is 0x01 if pairing result is equal to the multiplicative identity in a pairing
33/// target field and 0x00 otherwise.
34///
35/// See also: <https://eips.ethereum.org/EIPS/eip-2537#abi-for-pairing>
36pub(super) fn pairing(input: &Bytes, gas_limit: u64) -> PrecompileResult {
37    let input_len = input.len();
38    if input_len == 0 || input_len % INPUT_LENGTH != 0 {
39        return Err(PrecompileError::Other(format!(
40            "Pairing input length should be multiple of {INPUT_LENGTH}, was {input_len}"
41        ))
42        .into());
43    }
44
45    let k = input_len / INPUT_LENGTH;
46    let required_gas: u64 = PAIRING_MULTIPLIER_BASE * k as u64 + PAIRING_OFFSET_BASE;
47    if required_gas > gas_limit {
48        return Err(PrecompileError::OutOfGas.into());
49    }
50
51    // Accumulator for the fp12 multiplications of the miller loops.
52    let mut acc = blst_fp12::default();
53    for i in 0..k {
54        // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check.
55        //
56        // So we set the subgroup_check flag to `true`
57        let p1_aff = &extract_g1_input(
58            &input[i * INPUT_LENGTH..i * INPUT_LENGTH + G1_INPUT_ITEM_LENGTH],
59            true,
60        )?;
61
62        // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check.
63        //
64        // So we set the subgroup_check flag to `true`
65        let p2_aff = &extract_g2_input(
66            &input[i * INPUT_LENGTH + G1_INPUT_ITEM_LENGTH
67                ..i * INPUT_LENGTH + G1_INPUT_ITEM_LENGTH + G2_INPUT_ITEM_LENGTH],
68            true,
69        )?;
70
71        if i > 0 {
72            // After the first slice (i>0) we use cur_ml to store the current
73            // miller loop and accumulate with the previous results using a fp12
74            // multiplication.
75            let mut cur_ml = blst_fp12::default();
76            let mut res = blst_fp12::default();
77            // SAFETY: res, acc, cur_ml, p1_aff and p2_aff are blst values.
78            unsafe {
79                blst_miller_loop(&mut cur_ml, p2_aff, p1_aff);
80                blst_fp12_mul(&mut res, &acc, &cur_ml);
81            }
82            acc = res;
83        } else {
84            // On the first slice (i==0) there is no previous results and no need
85            // to accumulate.
86            // SAFETY: acc, p1_aff and p2_aff are blst values.
87            unsafe {
88                blst_miller_loop(&mut acc, p2_aff, p1_aff);
89            }
90        }
91    }
92
93    // SAFETY: ret and acc are blst values.
94    let mut ret = blst_fp12::default();
95    unsafe {
96        blst_final_exp(&mut ret, &acc);
97    }
98
99    let mut result: u8 = 0;
100    // SAFETY: ret is a blst value.
101    unsafe {
102        if blst_fp12_is_one(&ret) {
103            result = 1;
104        }
105    }
106    Ok(PrecompileOutput::new(
107        required_gas,
108        B256::with_last_byte(result).into(),
109    ))
110}