revm_precompile/bls12_381/
g2_msm.rsuse super::{
g2::{encode_g2_point, extract_g2_input, G2_INPUT_ITEM_LENGTH},
g2_mul,
msm::msm_required_gas,
utils::{extract_scalar_input, NBITS, SCALAR_LENGTH},
};
use crate::{u64_to_address, PrecompileWithAddress};
use blst::{blst_p2, blst_p2_affine, blst_p2_from_affine, blst_p2_to_affine, p2_affines};
use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult};
pub const PRECOMPILE: PrecompileWithAddress =
PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(g2_msm));
pub const ADDRESS: u64 = 0x10;
pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult {
let input_len = input.len();
if input_len == 0 || input_len % g2_mul::INPUT_LENGTH != 0 {
return Err(PrecompileError::Other(format!(
"G2MSM input length should be multiple of {}, was {}",
g2_mul::INPUT_LENGTH,
input_len
))
.into());
}
let k = input_len / g2_mul::INPUT_LENGTH;
let required_gas = msm_required_gas(k, g2_mul::BASE_GAS_FEE);
if required_gas > gas_limit {
return Err(PrecompileError::OutOfGas.into());
}
let mut g2_points: Vec<blst_p2> = Vec::with_capacity(k);
let mut scalars: Vec<u8> = Vec::with_capacity(k * SCALAR_LENGTH);
for i in 0..k {
let slice =
&input[i * g2_mul::INPUT_LENGTH..i * g2_mul::INPUT_LENGTH + G2_INPUT_ITEM_LENGTH];
if slice.iter().all(|i| *i == 0) {
continue;
}
let p0_aff = &extract_g2_input(slice, true)?;
let mut p0 = blst_p2::default();
unsafe { blst_p2_from_affine(&mut p0, p0_aff) };
g2_points.push(p0);
scalars.extend_from_slice(
&extract_scalar_input(
&input[i * g2_mul::INPUT_LENGTH + G2_INPUT_ITEM_LENGTH
..i * g2_mul::INPUT_LENGTH + G2_INPUT_ITEM_LENGTH + SCALAR_LENGTH],
)?
.b,
);
}
if g2_points.is_empty() {
return Ok(PrecompileOutput::new(required_gas, [0; 256].into()));
}
let points = p2_affines::from(&g2_points);
let multiexp = points.mult(&scalars, NBITS);
let mut multiexp_aff = blst_p2_affine::default();
unsafe { blst_p2_to_affine(&mut multiexp_aff, &multiexp) };
let out = encode_g2_point(&multiexp_aff);
Ok(PrecompileOutput::new(required_gas, out))
}