1use blst::{
2 blst_bendian_from_fp, blst_fp, blst_fp_from_bendian, blst_scalar, blst_scalar_from_bendian,
3};
4use core::cmp::Ordering;
5use revm_primitives::PrecompileError;
67/// Number of bits used in the BLS12-381 curve finite field elements.
8pub(super) const NBITS: usize = 256;
9/// Finite field element input length.
10pub(super) const FP_LENGTH: usize = 48;
11/// Finite field element padded input length.
12pub(super) const PADDED_FP_LENGTH: usize = 64;
13/// Quadratic extension of finite field element input length.
14pub(super) const PADDED_FP2_LENGTH: usize = 128;
15/// Input elements padding length.
16pub(super) const PADDING_LENGTH: usize = 16;
17/// Scalar length.
18pub(super) const SCALAR_LENGTH: usize = 32;
19// Big-endian non-Montgomery form.
20pub(super) const MODULUS_REPR: [u8; 48] = [
210x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7,
220x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24,
230x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab,
24];
2526/// Encodes a single finite field element into byte slice with padding.
27pub(super) fn fp_to_bytes(out: &mut [u8], input: *const blst_fp) {
28if out.len() != PADDED_FP_LENGTH {
29return;
30 }
31let (padding, rest) = out.split_at_mut(PADDING_LENGTH);
32 padding.fill(0);
33// SAFETY: out length is checked previously, input is a blst value.
34unsafe { blst_bendian_from_fp(rest.as_mut_ptr(), input) };
35}
3637/// Removes zeros with which the precompile inputs are left padded to 64 bytes.
38pub(super) fn remove_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], PrecompileError> {
39if input.len() != PADDED_FP_LENGTH {
40return Err(PrecompileError::Other(format!(
41"Padded input should be {PADDED_FP_LENGTH} bytes, was {}",
42 input.len()
43 )));
44 }
45let (padding, unpadded) = input.split_at(PADDING_LENGTH);
46if !padding.iter().all(|&x| x == 0) {
47return Err(PrecompileError::Other(format!(
48"{PADDING_LENGTH} top bytes of input are not zero",
49 )));
50 }
51Ok(unpadded.try_into().unwrap())
52}
5354/// Extracts a scalar from a 32 byte slice representation, decoding the input as a big endian
55/// unsigned integer. If the input is not exactly 32 bytes long, an error is returned.
56///
57/// From [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537):
58/// * A scalar for the multiplication operation is encoded as 32 bytes by performing BigEndian
59/// encoding of the corresponding (unsigned) integer.
60///
61/// We do not check that the scalar is a canonical Fr element, because the EIP specifies:
62/// * The corresponding integer is not required to be less than or equal than main subgroup order
63/// `q`.
64pub(super) fn extract_scalar_input(input: &[u8]) -> Result<blst_scalar, PrecompileError> {
65if input.len() != SCALAR_LENGTH {
66return Err(PrecompileError::Other(format!(
67"Input should be {SCALAR_LENGTH} bytes, was {}",
68 input.len()
69 )));
70 }
7172let mut out = blst_scalar::default();
73// SAFETY: input length is checked previously, out is a blst value.
74unsafe {
75// NOTE: we do not use `blst_scalar_fr_check` here because, from EIP-2537:
76 //
77 // * The corresponding integer is not required to be less than or equal than main subgroup
78 // order `q`.
79blst_scalar_from_bendian(&mut out, input.as_ptr())
80 };
8182Ok(out)
83}
8485/// Checks if the input is a valid big-endian representation of a field element.
86fn is_valid_be(input: &[u8; 48]) -> bool {
87for (i, modul) in input.iter().zip(MODULUS_REPR.iter()) {
88match i.cmp(modul) {
89 Ordering::Greater => return false,
90 Ordering::Less => return true,
91 Ordering::Equal => continue,
92 }
93 }
94// false if matching the modulus
95false
96}
9798/// Checks whether or not the input represents a canonical field element, returning the field
99/// element if successful.
100pub(super) fn fp_from_bendian(input: &[u8; 48]) -> Result<blst_fp, PrecompileError> {
101if !is_valid_be(input) {
102return Err(PrecompileError::Other("non-canonical fp value".to_string()));
103 }
104let mut fp = blst_fp::default();
105// SAFETY: input has fixed length, and fp is a blst value.
106unsafe {
107// This performs the check for canonical field elements
108blst_fp_from_bendian(&mut fp, input.as_ptr());
109 }
110111Ok(fp)
112}