revm_precompile/bls12_381/
g2_msm.rs

1use super::{
2    g2::{encode_g2_point, extract_g2_input},
3    msm::msm_required_gas,
4    utils::extract_scalar_input,
5};
6use crate::bls12_381_const::{
7    DISCOUNT_TABLE_G2_MSM, G2_INPUT_ITEM_LENGTH, G2_MSM_ADDRESS, G2_MSM_BASE_GAS_FEE,
8    G2_MSM_INPUT_LENGTH, NBITS, SCALAR_LENGTH,
9};
10use crate::{u64_to_address, PrecompileWithAddress};
11use crate::{PrecompileError, PrecompileOutput, PrecompileResult};
12use blst::{blst_p2, blst_p2_affine, blst_p2_from_affine, blst_p2_to_affine, p2_affines};
13use primitives::Bytes;
14
15/// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G2MSM precompile.
16pub const PRECOMPILE: PrecompileWithAddress =
17    PrecompileWithAddress(u64_to_address(G2_MSM_ADDRESS), g2_msm);
18
19/// Implements EIP-2537 G2MSM precompile.
20/// G2 multi-scalar-multiplication call expects `288*k` bytes as an input that is interpreted
21/// as byte concatenation of `k` slices each of them being a byte concatenation
22/// of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32`
23/// bytes).
24/// Output is an encoding of multi-scalar-multiplication operation result - single G2
25/// point (`256` bytes).
26/// See also: <https://eips.ethereum.org/EIPS/eip-2537#abi-for-g2-multiexponentiation>
27pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult {
28    let input_len = input.len();
29    if input_len == 0 || input_len % G2_MSM_INPUT_LENGTH != 0 {
30        return Err(PrecompileError::Other(format!(
31            "G2MSM input length should be multiple of {}, was {}",
32            G2_MSM_INPUT_LENGTH, input_len
33        )));
34    }
35
36    let k = input_len / G2_MSM_INPUT_LENGTH;
37    let required_gas = msm_required_gas(k, &DISCOUNT_TABLE_G2_MSM, G2_MSM_BASE_GAS_FEE);
38    if required_gas > gas_limit {
39        return Err(PrecompileError::OutOfGas);
40    }
41
42    let mut g2_points: Vec<blst_p2> = Vec::with_capacity(k);
43    let mut scalars: Vec<u8> = Vec::with_capacity(k * SCALAR_LENGTH);
44    for i in 0..k {
45        let slice = &input[i * G2_MSM_INPUT_LENGTH..i * G2_MSM_INPUT_LENGTH + G2_INPUT_ITEM_LENGTH];
46        // BLST batch API for p2_affines blows up when you pass it a point at infinity, so we must
47        // filter points at infinity (and their corresponding scalars) from the input.
48        if slice.iter().all(|i| *i == 0) {
49            continue;
50        }
51
52        // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check.
53        //
54        // So we set the subgroup_check flag to `true`
55        let p0_aff = &extract_g2_input(slice, true)?;
56
57        let mut p0 = blst_p2::default();
58        // SAFETY: `p0` and `p0_aff` are blst values.
59        unsafe { blst_p2_from_affine(&mut p0, p0_aff) };
60
61        g2_points.push(p0);
62
63        scalars.extend_from_slice(
64            &extract_scalar_input(
65                &input[i * G2_MSM_INPUT_LENGTH + G2_INPUT_ITEM_LENGTH
66                    ..i * G2_MSM_INPUT_LENGTH + G2_INPUT_ITEM_LENGTH + SCALAR_LENGTH],
67            )?
68            .b,
69        );
70    }
71
72    // Return infinity point if all points are infinity
73    if g2_points.is_empty() {
74        return Ok(PrecompileOutput::new(required_gas, [0; 256].into()));
75    }
76
77    let points = p2_affines::from(&g2_points);
78    let multiexp = points.mult(&scalars, NBITS);
79
80    let mut multiexp_aff = blst_p2_affine::default();
81    // SAFETY: `multiexp_aff` and `multiexp` are blst values.
82    unsafe { blst_p2_to_affine(&mut multiexp_aff, &multiexp) };
83
84    let out = encode_g2_point(&multiexp_aff);
85    Ok(PrecompileOutput::new(required_gas, out))
86}