revm_precompile/bls12_381/
map_fp_to_g1.rs

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