revm_precompile/bls12_381/
g1_mul.rs

1use super::{
2    g1::{encode_g1_point, extract_g1_input, G1_INPUT_ITEM_LENGTH},
3    utils::{extract_scalar_input, NBITS},
4};
5use crate::{u64_to_address, PrecompileWithAddress};
6use blst::{blst_p1, blst_p1_affine, blst_p1_from_affine, blst_p1_mult, blst_p1_to_affine};
7use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult};
8
9/// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G1MUL precompile.
10pub const PRECOMPILE: PrecompileWithAddress =
11    PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(g1_mul));
12/// BLS12_G1MUL precompile address.
13pub const ADDRESS: u64 = 0x0c;
14/// Base gas fee for BLS12-381 g1_mul operation.
15pub(super) const BASE_GAS_FEE: u64 = 12000;
16
17/// Input length of g1_mul operation.
18pub(super) const INPUT_LENGTH: usize = 160;
19
20/// G1 multiplication call expects `160` bytes as an input that is interpreted as
21/// byte concatenation of encoding of G1 point (`128` bytes) and encoding of a
22/// scalar value (`32` bytes).
23/// Output is an encoding of multiplication operation result - single G1 point
24/// (`128` bytes).
25/// See also: <https://eips.ethereum.org/EIPS/eip-2537#abi-for-g1-multiplication>
26pub(super) fn g1_mul(input: &Bytes, gas_limit: u64) -> PrecompileResult {
27    if BASE_GAS_FEE > gas_limit {
28        return Err(PrecompileError::OutOfGas.into());
29    }
30    if input.len() != INPUT_LENGTH {
31        return Err(PrecompileError::Other(format!(
32            "G1MUL input should be {INPUT_LENGTH} bytes, was {}",
33            input.len()
34        ))
35        .into());
36    }
37
38    // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check.
39    //
40    // So we set the subgroup_check flag to `true`
41    let slice = &input[..G1_INPUT_ITEM_LENGTH];
42    let p0_aff = &extract_g1_input(slice, true)?;
43
44    let mut p0 = blst_p1::default();
45
46    // SAFETY: p0 and p0_aff are blst values.
47    unsafe { blst_p1_from_affine(&mut p0, p0_aff) };
48
49    let input_scalar0 = extract_scalar_input(&input[G1_INPUT_ITEM_LENGTH..])?;
50
51    let mut p = blst_p1::default();
52    // SAFETY: input_scalar0.b has fixed size, p and p0 are blst values.
53    unsafe { blst_p1_mult(&mut p, &p0, input_scalar0.b.as_ptr(), NBITS) };
54    let mut p_aff = blst_p1_affine::default();
55    // SAFETY: p_aff and p are blst values.
56    unsafe { blst_p1_to_affine(&mut p_aff, &p) };
57
58    let out = encode_g1_point(&p_aff);
59    Ok(PrecompileOutput::new(BASE_GAS_FEE, out))
60}