revm_precompile/bls12_381/
map_fp_to_g1.rs

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