revm_precompile/bls12_381/
map_fp_to_g1.rs

1//! BLS12-381 map fp to g1 precompile. More details in [`map_fp_to_g1`]
2use super::utils::{pad_g1_point, remove_fp_padding};
3use crate::bls12_381_const::{MAP_FP_TO_G1_ADDRESS, MAP_FP_TO_G1_BASE_GAS_FEE, PADDED_FP_LENGTH};
4use crate::{crypto, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress};
5
6/// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_MAP_FP_TO_G1 precompile.
7pub const PRECOMPILE: PrecompileWithAddress =
8    PrecompileWithAddress(MAP_FP_TO_G1_ADDRESS, map_fp_to_g1);
9
10/// Field-to-curve call expects 64 bytes as an input that is interpreted as an
11/// element of Fp. Output of this call is 128 bytes and is an encoded G1 point.
12/// See also: <https://eips.ethereum.org/EIPS/eip-2537#abi-for-mapping-fp-element-to-g1-point>
13pub fn map_fp_to_g1(input: &[u8], gas_limit: u64) -> PrecompileResult {
14    if MAP_FP_TO_G1_BASE_GAS_FEE > gas_limit {
15        return Err(PrecompileError::OutOfGas);
16    }
17
18    if input.len() != PADDED_FP_LENGTH {
19        return Err(PrecompileError::Other(format!(
20            "MAP_FP_TO_G1 input should be {PADDED_FP_LENGTH} bytes, was {}",
21            input.len()
22        )));
23    }
24
25    let input_p0 = remove_fp_padding(input)?;
26
27    let unpadded_result = crypto().bls12_381_fp_to_g1(input_p0)?;
28
29    // Pad the result for EVM compatibility
30    let padded_result = pad_g1_point(&unpadded_result);
31
32    Ok(PrecompileOutput::new(
33        MAP_FP_TO_G1_BASE_GAS_FEE,
34        padded_result.into(),
35    ))
36}
37
38#[cfg(test)]
39mod test {
40    use super::*;
41    use primitives::{hex, Bytes};
42
43    #[test]
44    fn sanity_test() {
45        let input = Bytes::from(hex!("000000000000000000000000000000006900000000000000636f6e7472616374595a603f343061cd305a03f40239f5ffff31818185c136bc2595f2aa18e08f17"));
46        let fail = map_fp_to_g1(&input, MAP_FP_TO_G1_BASE_GAS_FEE);
47        assert_eq!(
48            fail,
49            Err(PrecompileError::Other("non-canonical fp value".to_string()))
50        );
51    }
52}