revm_precompile/bls12_381/
utils.rs

1//! BLS12-381 utilities for padding and unpadding of input.
2use crate::{
3    bls12_381_const::{
4        FP_LENGTH, FP_PAD_BY, G1_LENGTH, PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH,
5    },
6    PrecompileError,
7};
8
9/// Removes zeros with which the precompile inputs are left padded to 64 bytes.
10pub(super) fn remove_fp_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], PrecompileError> {
11    if input.len() != PADDED_FP_LENGTH {
12        return Err(PrecompileError::Bls12381FpPaddingLength);
13    }
14    let (padding, unpadded) = input.split_at(FP_PAD_BY);
15    if !padding.iter().all(|&x| x == 0) {
16        return Err(PrecompileError::Bls12381FpPaddingInvalid);
17    }
18    Ok(unpadded.try_into().unwrap())
19}
20/// remove_g1_padding removes the padding applied to the Fp elements that constitute the
21/// encoded G1 element.
22pub(super) fn remove_g1_padding(input: &[u8]) -> Result<[&[u8; FP_LENGTH]; 2], PrecompileError> {
23    if input.len() != PADDED_G1_LENGTH {
24        return Err(PrecompileError::Bls12381G1PaddingLength);
25    }
26
27    let x = remove_fp_padding(&input[..PADDED_FP_LENGTH])?;
28    let y = remove_fp_padding(&input[PADDED_FP_LENGTH..PADDED_G1_LENGTH])?;
29    Ok([x, y])
30}
31
32/// remove_g2_padding removes the padding applied to the Fp elements that constitute the
33/// encoded G2 element.
34pub(super) fn remove_g2_padding(input: &[u8]) -> Result<[&[u8; FP_LENGTH]; 4], PrecompileError> {
35    if input.len() != PADDED_G2_LENGTH {
36        return Err(PrecompileError::Bls12381G2PaddingLength);
37    }
38
39    let mut input_fps = [&[0; FP_LENGTH]; 4];
40    for i in 0..4 {
41        input_fps[i] = remove_fp_padding(&input[i * PADDED_FP_LENGTH..(i + 1) * PADDED_FP_LENGTH])?;
42    }
43    Ok(input_fps)
44}
45
46/// Pads an unpadded G1 point (96 bytes) to the EVM-compatible format (128 bytes).
47///
48/// Takes a G1 point with 2 field elements of 48 bytes each and adds 16 bytes of
49/// zero padding before each field element.
50pub(super) fn pad_g1_point(unpadded: &[u8]) -> [u8; PADDED_G1_LENGTH] {
51    assert_eq!(
52        unpadded.len(),
53        G1_LENGTH,
54        "Invalid unpadded G1 point length"
55    );
56
57    let mut padded = [0u8; PADDED_G1_LENGTH];
58
59    // Copy each field element (x, y) with padding
60    for i in 0..2 {
61        padded[i * PADDED_FP_LENGTH + FP_PAD_BY..(i + 1) * PADDED_FP_LENGTH]
62            .copy_from_slice(&unpadded[i * FP_LENGTH..(i + 1) * FP_LENGTH]);
63    }
64
65    padded
66}
67
68/// Pads an unpadded G2 point (192 bytes) to the EVM-compatible format (256 bytes).
69///
70/// Takes a G2 point with 4 field elements of 48 bytes each and adds 16 bytes of
71/// zero padding before each field element.
72pub(super) fn pad_g2_point(unpadded: &[u8]) -> [u8; PADDED_G2_LENGTH] {
73    assert_eq!(
74        unpadded.len(),
75        4 * FP_LENGTH,
76        "Invalid unpadded G2 point length"
77    );
78
79    let mut padded = [0u8; PADDED_G2_LENGTH];
80
81    // Copy each field element (x.c0, x.c1, y.c0, y.c1) with padding
82    for i in 0..4 {
83        padded[i * PADDED_FP_LENGTH + FP_PAD_BY..(i + 1) * PADDED_FP_LENGTH]
84            .copy_from_slice(&unpadded[i * FP_LENGTH..(i + 1) * FP_LENGTH]);
85    }
86
87    padded
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_pad_g1_point_roundtrip() {
96        // Create test data
97        let mut unpadded = [0u8; G1_LENGTH];
98        for (i, byte) in unpadded.iter_mut().enumerate() {
99            *byte = (i * 2 + 1) as u8;
100        }
101
102        // Pad the point
103        let padded = pad_g1_point(&unpadded);
104
105        // Remove padding
106        let result = remove_g1_padding(&padded).unwrap();
107
108        // Verify roundtrip
109        assert_eq!(result[0], &unpadded[0..FP_LENGTH]);
110        assert_eq!(result[1], &unpadded[FP_LENGTH..G1_LENGTH]);
111    }
112
113    #[test]
114    fn test_pad_g2_point_roundtrip() {
115        // Create test data for G2 point (192 bytes = 4 * 48)
116        let mut unpadded = [0u8; 4 * FP_LENGTH];
117        for (i, byte) in unpadded.iter_mut().enumerate() {
118            *byte = (i * 2 + 1) as u8;
119        }
120
121        // Pad the point
122        let padded = pad_g2_point(&unpadded);
123
124        // Remove padding
125        let result = remove_g2_padding(&padded).unwrap();
126
127        // Verify roundtrip - G2 has 4 field elements
128        assert_eq!(result[0], &unpadded[0..FP_LENGTH]);
129        assert_eq!(result[1], &unpadded[FP_LENGTH..2 * FP_LENGTH]);
130        assert_eq!(result[2], &unpadded[2 * FP_LENGTH..3 * FP_LENGTH]);
131        assert_eq!(result[3], &unpadded[3 * FP_LENGTH..4 * FP_LENGTH]);
132    }
133}