revm_precompile/bls12_381/
g1_msm.rs1use crate::bls12_381::utils::{pad_g1_point, remove_g1_padding};
3use crate::bls12_381::G1Point;
4use crate::bls12_381_const::{
5 DISCOUNT_TABLE_G1_MSM, G1_MSM_ADDRESS, G1_MSM_BASE_GAS_FEE, G1_MSM_INPUT_LENGTH,
6 PADDED_G1_LENGTH, SCALAR_LENGTH,
7};
8use crate::bls12_381_utils::msm_required_gas;
9use crate::{crypto, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress};
10
11pub const PRECOMPILE: PrecompileWithAddress = PrecompileWithAddress(G1_MSM_ADDRESS, g1_msm);
13
14pub fn g1_msm(input: &[u8], gas_limit: u64) -> PrecompileResult {
23 let input_len = input.len();
24 if input_len == 0 || !input_len.is_multiple_of(G1_MSM_INPUT_LENGTH) {
25 return Err(PrecompileError::Other(format!(
26 "G1MSM input length should be multiple of {G1_MSM_INPUT_LENGTH}, was {input_len}",
27 )));
28 }
29
30 let k = input_len / G1_MSM_INPUT_LENGTH;
31 let required_gas = msm_required_gas(k, &DISCOUNT_TABLE_G1_MSM, G1_MSM_BASE_GAS_FEE);
32 if required_gas > gas_limit {
33 return Err(PrecompileError::OutOfGas);
34 }
35
36 let mut valid_pairs_iter = (0..k).map(|i| {
37 let start = i * G1_MSM_INPUT_LENGTH;
38 let padded_g1 = &input[start..start + PADDED_G1_LENGTH];
39 let scalar_bytes = &input[start + PADDED_G1_LENGTH..start + G1_MSM_INPUT_LENGTH];
40
41 let [x, y] = remove_g1_padding(padded_g1)?;
43 let scalar_array: [u8; SCALAR_LENGTH] = scalar_bytes.try_into().unwrap();
44
45 let point: G1Point = (*x, *y);
46 Ok((point, scalar_array))
47 });
48
49 let unpadded_result = crypto().bls12_381_g1_msm(&mut valid_pairs_iter)?;
50
51 let padded_result = pad_g1_point(&unpadded_result);
53
54 Ok(PrecompileOutput::new(required_gas, padded_result.into()))
55}
56
57#[cfg(test)]
58mod test {
59 use super::*;
60 use primitives::{hex, Bytes};
61
62 #[test]
63 fn bls_g1multiexp_g1_not_on_curve_but_in_subgroup() {
64 let input = Bytes::from(hex!("000000000000000000000000000000000a2833e497b38ee3ca5c62828bf4887a9f940c9e426c7890a759c20f248c23a7210d2432f4c98a514e524b5184a0ddac00000000000000000000000000000000150772d56bf9509469f9ebcd6e47570429fd31b0e262b66d512e245c38ec37255529f2271fd70066473e393a8bead0c30000000000000000000000000000000000000000000000000000000000000000"));
65 let fail = g1_msm(&input, G1_MSM_BASE_GAS_FEE);
66 assert_eq!(
67 fail,
68 Err(PrecompileError::Other(
69 "Element not on G1 curve".to_string()
70 ))
71 );
72 }
73}