revm_precompile/bls12_381/
g1_msm.rs

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