revm_precompile/bls12_381/
map_fp_to_g1.rs

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