revm_precompile/bls12_381/
g1_msm.rs1use super::crypto_backend::{encode_g1_point, p1_msm, read_g1, read_scalar};
3use crate::bls12_381::utils::remove_g1_padding;
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::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress};
10use primitives::Bytes;
11use std::vec::Vec;
12
13pub const PRECOMPILE: PrecompileWithAddress = PrecompileWithAddress(G1_MSM_ADDRESS, g1_msm);
15
16pub fn g1_msm(input: &[u8], gas_limit: u64) -> PrecompileResult {
25 let input_len = input.len();
26 if input_len == 0 || input_len % G1_MSM_INPUT_LENGTH != 0 {
27 return Err(PrecompileError::Other(format!(
28 "G1MSM input length should be multiple of {}, was {}",
29 G1_MSM_INPUT_LENGTH, input_len
30 )));
31 }
32
33 let k = input_len / G1_MSM_INPUT_LENGTH;
34 let required_gas = msm_required_gas(k, &DISCOUNT_TABLE_G1_MSM, G1_MSM_BASE_GAS_FEE);
35 if required_gas > gas_limit {
36 return Err(PrecompileError::OutOfGas);
37 }
38
39 let mut g1_points: Vec<_> = Vec::with_capacity(k);
40 let mut scalars = Vec::with_capacity(k);
41 for i in 0..k {
42 let encoded_g1_element =
43 &input[i * G1_MSM_INPUT_LENGTH..i * G1_MSM_INPUT_LENGTH + PADDED_G1_LENGTH];
44 let encoded_scalar = &input[i * G1_MSM_INPUT_LENGTH + PADDED_G1_LENGTH
45 ..i * G1_MSM_INPUT_LENGTH + PADDED_G1_LENGTH + SCALAR_LENGTH];
46
47 if encoded_g1_element.iter().all(|i| *i == 0) {
52 continue;
53 }
54
55 let [a_x, a_y] = remove_g1_padding(encoded_g1_element)?;
56
57 let p0_aff = read_g1(a_x, a_y)?;
59
60 if encoded_scalar.iter().all(|i| *i == 0) {
66 continue;
67 }
68
69 g1_points.push(p0_aff);
70 scalars.push(read_scalar(encoded_scalar)?);
71 }
72
73 const ENCODED_POINT_AT_INFINITY: [u8; PADDED_G1_LENGTH] = [0; PADDED_G1_LENGTH];
76 if g1_points.is_empty() {
77 return Ok(PrecompileOutput::new(
78 required_gas,
79 Bytes::from_static(&ENCODED_POINT_AT_INFINITY),
80 ));
81 }
82
83 let multiexp_aff = p1_msm(g1_points, scalars);
84
85 let out = encode_g1_point(&multiexp_aff);
86 Ok(PrecompileOutput::new(required_gas, out.into()))
87}
88
89#[cfg(test)]
90mod test {
91 use super::*;
92 use primitives::hex;
93
94 #[test]
95 fn bls_g1multiexp_g1_not_on_curve_but_in_subgroup() {
96 let input = Bytes::from(hex!("000000000000000000000000000000000a2833e497b38ee3ca5c62828bf4887a9f940c9e426c7890a759c20f248c23a7210d2432f4c98a514e524b5184a0ddac00000000000000000000000000000000150772d56bf9509469f9ebcd6e47570429fd31b0e262b66d512e245c38ec37255529f2271fd70066473e393a8bead0c30000000000000000000000000000000000000000000000000000000000000000"));
97 let fail = g1_msm(&input, G1_MSM_BASE_GAS_FEE);
98 assert_eq!(
99 fail,
100 Err(PrecompileError::Other(
101 "Element not on G1 curve".to_string()
102 ))
103 );
104 }
105}