revm_precompile/bls12_381/
utils.rs

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