solana_bn254/
compression.rs

1pub mod prelude {
2    pub use crate::compression::{
3        alt_bn128_compression_size::*, consts::*, target_arch::*, AltBn128CompressionError,
4    };
5}
6
7use thiserror::Error;
8
9mod consts {
10    pub const ALT_BN128_G1_COMPRESS: u64 = 0;
11    pub const ALT_BN128_G1_DECOMPRESS: u64 = 1;
12    pub const ALT_BN128_G2_COMPRESS: u64 = 2;
13    pub const ALT_BN128_G2_DECOMPRESS: u64 = 3;
14}
15
16mod alt_bn128_compression_size {
17    pub const G1: usize = 64;
18    pub const G2: usize = 128;
19    pub const G1_COMPRESSED: usize = 32;
20    pub const G2_COMPRESSED: usize = 64;
21}
22
23// AltBn128CompressionError must be removed once the
24// simplify_alt_bn128_syscall_error_codes feature gets activated
25#[derive(Debug, Error, Clone, PartialEq, Eq)]
26pub enum AltBn128CompressionError {
27    #[error("Unexpected error")]
28    UnexpectedError,
29    #[error("Failed to decompress g1")]
30    G1DecompressionFailed,
31    #[error("Failed to decompress g2")]
32    G2DecompressionFailed,
33    #[error("Failed to compress affine g1")]
34    G1CompressionFailed,
35    #[error("Failed to compress affine g2")]
36    G2CompressionFailed,
37    #[error("Invalid input size")]
38    InvalidInputSize,
39}
40
41impl From<u64> for AltBn128CompressionError {
42    fn from(v: u64) -> AltBn128CompressionError {
43        match v {
44            1 => AltBn128CompressionError::G1DecompressionFailed,
45            2 => AltBn128CompressionError::G2DecompressionFailed,
46            3 => AltBn128CompressionError::G1CompressionFailed,
47            4 => AltBn128CompressionError::G2CompressionFailed,
48            5 => AltBn128CompressionError::InvalidInputSize,
49            _ => AltBn128CompressionError::UnexpectedError,
50        }
51    }
52}
53
54impl From<AltBn128CompressionError> for u64 {
55    fn from(v: AltBn128CompressionError) -> u64 {
56        // note: should never return 0, as it risks to be confused with syscall success
57        match v {
58            AltBn128CompressionError::G1DecompressionFailed => 1,
59            AltBn128CompressionError::G2DecompressionFailed => 2,
60            AltBn128CompressionError::G1CompressionFailed => 3,
61            AltBn128CompressionError::G2CompressionFailed => 4,
62            AltBn128CompressionError::InvalidInputSize => 5,
63            AltBn128CompressionError::UnexpectedError => 6,
64        }
65    }
66}
67
68#[cfg(not(target_os = "solana"))]
69mod target_arch {
70
71    use {
72        super::*,
73        crate::compression::alt_bn128_compression_size,
74        ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate},
75    };
76
77    type G1 = ark_bn254::g1::G1Affine;
78    type G2 = ark_bn254::g2::G2Affine;
79
80    pub fn alt_bn128_g1_decompress(
81        g1_bytes: &[u8],
82    ) -> Result<[u8; alt_bn128_compression_size::G1], AltBn128CompressionError> {
83        let g1_bytes: [u8; alt_bn128_compression_size::G1_COMPRESSED] = g1_bytes
84            .try_into()
85            .map_err(|_| AltBn128CompressionError::InvalidInputSize)?;
86        if g1_bytes == [0u8; alt_bn128_compression_size::G1_COMPRESSED] {
87            return Ok([0u8; alt_bn128_compression_size::G1]);
88        }
89        let decompressed_g1 = G1::deserialize_with_mode(
90            convert_endianness::<32, 32>(&g1_bytes).as_slice(),
91            Compress::Yes,
92            Validate::No,
93        )
94        .map_err(|_| AltBn128CompressionError::G1DecompressionFailed)?;
95        let mut decompressed_g1_bytes = [0u8; alt_bn128_compression_size::G1];
96        decompressed_g1
97            .x
98            .serialize_with_mode(&mut decompressed_g1_bytes[..32], Compress::No)
99            .map_err(|_| AltBn128CompressionError::G1DecompressionFailed)?;
100        decompressed_g1
101            .y
102            .serialize_with_mode(&mut decompressed_g1_bytes[32..], Compress::No)
103            .map_err(|_| AltBn128CompressionError::G1DecompressionFailed)?;
104        Ok(convert_endianness::<32, 64>(&decompressed_g1_bytes))
105    }
106
107    pub fn alt_bn128_g1_compress(
108        g1_bytes: &[u8],
109    ) -> Result<[u8; alt_bn128_compression_size::G1_COMPRESSED], AltBn128CompressionError> {
110        let g1_bytes: [u8; alt_bn128_compression_size::G1] = g1_bytes
111            .try_into()
112            .map_err(|_| AltBn128CompressionError::InvalidInputSize)?;
113        if g1_bytes == [0u8; alt_bn128_compression_size::G1] {
114            return Ok([0u8; alt_bn128_compression_size::G1_COMPRESSED]);
115        }
116        let g1 = G1::deserialize_with_mode(
117            convert_endianness::<32, 64>(&g1_bytes).as_slice(),
118            Compress::No,
119            Validate::No,
120        )
121        .map_err(|_| AltBn128CompressionError::G1CompressionFailed)?;
122        let mut g1_bytes = [0u8; alt_bn128_compression_size::G1_COMPRESSED];
123        G1::serialize_compressed(&g1, g1_bytes.as_mut_slice())
124            .map_err(|_| AltBn128CompressionError::G1CompressionFailed)?;
125        Ok(convert_endianness::<32, 32>(&g1_bytes))
126    }
127
128    pub fn alt_bn128_g2_decompress(
129        g2_bytes: &[u8],
130    ) -> Result<[u8; alt_bn128_compression_size::G2], AltBn128CompressionError> {
131        let g2_bytes: [u8; alt_bn128_compression_size::G2_COMPRESSED] = g2_bytes
132            .try_into()
133            .map_err(|_| AltBn128CompressionError::InvalidInputSize)?;
134        if g2_bytes == [0u8; alt_bn128_compression_size::G2_COMPRESSED] {
135            return Ok([0u8; alt_bn128_compression_size::G2]);
136        }
137        let decompressed_g2 = G2::deserialize_with_mode(
138            convert_endianness::<64, 64>(&g2_bytes).as_slice(),
139            Compress::Yes,
140            Validate::No,
141        )
142        .map_err(|_| AltBn128CompressionError::G2DecompressionFailed)?;
143        let mut decompressed_g2_bytes = [0u8; alt_bn128_compression_size::G2];
144        decompressed_g2
145            .x
146            .serialize_with_mode(&mut decompressed_g2_bytes[..64], Compress::No)
147            .map_err(|_| AltBn128CompressionError::G2DecompressionFailed)?;
148        decompressed_g2
149            .y
150            .serialize_with_mode(&mut decompressed_g2_bytes[64..128], Compress::No)
151            .map_err(|_| AltBn128CompressionError::G2DecompressionFailed)?;
152        Ok(convert_endianness::<64, 128>(&decompressed_g2_bytes))
153    }
154
155    pub fn alt_bn128_g2_compress(
156        g2_bytes: &[u8],
157    ) -> Result<[u8; alt_bn128_compression_size::G2_COMPRESSED], AltBn128CompressionError> {
158        let g2_bytes: [u8; alt_bn128_compression_size::G2] = g2_bytes
159            .try_into()
160            .map_err(|_| AltBn128CompressionError::InvalidInputSize)?;
161        if g2_bytes == [0u8; alt_bn128_compression_size::G2] {
162            return Ok([0u8; alt_bn128_compression_size::G2_COMPRESSED]);
163        }
164        let g2 = G2::deserialize_with_mode(
165            convert_endianness::<64, 128>(&g2_bytes).as_slice(),
166            Compress::No,
167            Validate::No,
168        )
169        .map_err(|_| AltBn128CompressionError::G2CompressionFailed)?;
170        let mut g2_bytes = [0u8; alt_bn128_compression_size::G2_COMPRESSED];
171        G2::serialize_compressed(&g2, g2_bytes.as_mut_slice())
172            .map_err(|_| AltBn128CompressionError::G2CompressionFailed)?;
173        Ok(convert_endianness::<64, 64>(&g2_bytes))
174    }
175
176    pub fn convert_endianness<const CHUNK_SIZE: usize, const ARRAY_SIZE: usize>(
177        bytes: &[u8; ARRAY_SIZE],
178    ) -> [u8; ARRAY_SIZE] {
179        let reversed: [_; ARRAY_SIZE] = bytes
180            .chunks_exact(CHUNK_SIZE)
181            .flat_map(|chunk| chunk.iter().rev().copied())
182            .enumerate()
183            .fold([0u8; ARRAY_SIZE], |mut acc, (i, v)| {
184                acc[i] = v;
185                acc
186            });
187        reversed
188    }
189}
190
191#[cfg(target_os = "solana")]
192mod target_arch {
193    use {
194        super::*,
195        alt_bn128_compression_size::{G1, G1_COMPRESSED, G2, G2_COMPRESSED},
196        prelude::*,
197        solana_program::syscalls,
198    };
199
200    pub fn alt_bn128_g1_compress(
201        input: &[u8],
202    ) -> Result<[u8; G1_COMPRESSED], AltBn128CompressionError> {
203        let mut result_buffer = [0; G1_COMPRESSED];
204        let result = unsafe {
205            syscalls::sol_alt_bn128_compression(
206                ALT_BN128_G1_COMPRESS,
207                input as *const _ as *const u8,
208                input.len() as u64,
209                &mut result_buffer as *mut _ as *mut u8,
210            )
211        };
212
213        match result {
214            0 => Ok(result_buffer),
215            _ => Err(AltBn128CompressionError::UnexpectedError),
216        }
217    }
218
219    pub fn alt_bn128_g1_decompress(input: &[u8]) -> Result<[u8; G1], AltBn128CompressionError> {
220        let mut result_buffer = [0; G1];
221        let result = unsafe {
222            syscalls::sol_alt_bn128_compression(
223                ALT_BN128_G1_DECOMPRESS,
224                input as *const _ as *const u8,
225                input.len() as u64,
226                &mut result_buffer as *mut _ as *mut u8,
227            )
228        };
229
230        match result {
231            0 => Ok(result_buffer),
232            _ => Err(AltBn128CompressionError::UnexpectedError),
233        }
234    }
235
236    pub fn alt_bn128_g2_compress(
237        input: &[u8],
238    ) -> Result<[u8; G2_COMPRESSED], AltBn128CompressionError> {
239        let mut result_buffer = [0; G2_COMPRESSED];
240        let result = unsafe {
241            syscalls::sol_alt_bn128_compression(
242                ALT_BN128_G2_COMPRESS,
243                input as *const _ as *const u8,
244                input.len() as u64,
245                &mut result_buffer as *mut _ as *mut u8,
246            )
247        };
248
249        match result {
250            0 => Ok(result_buffer),
251            _ => Err(AltBn128CompressionError::UnexpectedError),
252        }
253    }
254
255    pub fn alt_bn128_g2_decompress(
256        input: &[u8; G2_COMPRESSED],
257    ) -> Result<[u8; G2], AltBn128CompressionError> {
258        let mut result_buffer = [0; G2];
259        let result = unsafe {
260            syscalls::sol_alt_bn128_compression(
261                ALT_BN128_G2_DECOMPRESS,
262                input as *const _ as *const u8,
263                input.len() as u64,
264                &mut result_buffer as *mut _ as *mut u8,
265            )
266        };
267
268        match result {
269            0 => Ok(result_buffer),
270            _ => Err(AltBn128CompressionError::UnexpectedError),
271        }
272    }
273}
274
275#[cfg(test)]
276mod tests {
277    use {
278        super::*,
279        crate::compression::target_arch::convert_endianness,
280        ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate},
281        std::ops::Neg,
282        target_arch::{
283            alt_bn128_g1_compress, alt_bn128_g1_decompress, alt_bn128_g2_compress,
284            alt_bn128_g2_decompress,
285        },
286    };
287    type G1 = ark_bn254::g1::G1Affine;
288    type G2 = ark_bn254::g2::G2Affine;
289
290    #[test]
291    fn alt_bn128_g1_compression() {
292        let g1_be = [
293            45, 206, 255, 166, 152, 55, 128, 138, 79, 217, 145, 164, 25, 74, 120, 234, 234, 217,
294            68, 149, 162, 44, 133, 120, 184, 205, 12, 44, 175, 98, 168, 172, 20, 24, 216, 15, 209,
295            175, 106, 75, 147, 236, 90, 101, 123, 219, 245, 151, 209, 202, 218, 104, 148, 8, 32,
296            254, 243, 191, 218, 122, 42, 81, 193, 84,
297        ];
298        let g1_le = convert_endianness::<32, 64>(&g1_be);
299        let g1: G1 =
300            G1::deserialize_with_mode(g1_le.as_slice(), Compress::No, Validate::No).unwrap();
301
302        let g1_neg = g1.neg();
303        let mut g1_neg_be = [0u8; 64];
304        g1_neg
305            .x
306            .serialize_with_mode(&mut g1_neg_be[..32], Compress::No)
307            .unwrap();
308        g1_neg
309            .y
310            .serialize_with_mode(&mut g1_neg_be[32..64], Compress::No)
311            .unwrap();
312        let g1_neg_be: [u8; 64] = convert_endianness::<32, 64>(&g1_neg_be);
313
314        let points = [(g1, g1_be), (g1_neg, g1_neg_be)];
315
316        for (point, g1_be) in &points {
317            let mut compressed_ref = [0u8; 32];
318            G1::serialize_with_mode(point, compressed_ref.as_mut_slice(), Compress::Yes).unwrap();
319            let compressed_ref: [u8; 32] = convert_endianness::<32, 32>(&compressed_ref);
320
321            let decompressed = alt_bn128_g1_decompress(compressed_ref.as_slice()).unwrap();
322
323            assert_eq!(
324                alt_bn128_g1_compress(&decompressed).unwrap(),
325                compressed_ref
326            );
327            assert_eq!(decompressed, *g1_be);
328        }
329    }
330
331    #[test]
332    fn alt_bn128_g2_compression() {
333        let g2_be = [
334            40, 57, 233, 205, 180, 46, 35, 111, 215, 5, 23, 93, 12, 71, 118, 225, 7, 46, 247, 147,
335            47, 130, 106, 189, 184, 80, 146, 103, 141, 52, 242, 25, 0, 203, 124, 176, 110, 34, 151,
336            212, 66, 180, 238, 151, 236, 189, 133, 209, 17, 137, 205, 183, 168, 196, 92, 159, 75,
337            174, 81, 168, 18, 86, 176, 56, 16, 26, 210, 20, 18, 81, 122, 142, 104, 62, 251, 169,
338            98, 141, 21, 253, 50, 130, 182, 15, 33, 109, 228, 31, 79, 183, 88, 147, 174, 108, 4,
339            22, 14, 129, 168, 6, 80, 246, 254, 100, 218, 131, 94, 49, 247, 211, 3, 245, 22, 200,
340            177, 91, 60, 144, 147, 174, 90, 17, 19, 189, 62, 147, 152, 18,
341        ];
342        let g2_le = convert_endianness::<64, 128>(&g2_be);
343        let g2: G2 =
344            G2::deserialize_with_mode(g2_le.as_slice(), Compress::No, Validate::No).unwrap();
345
346        let g2_neg = g2.neg();
347        let mut g2_neg_be = [0u8; 128];
348        g2_neg
349            .x
350            .serialize_with_mode(&mut g2_neg_be[..64], Compress::No)
351            .unwrap();
352        g2_neg
353            .y
354            .serialize_with_mode(&mut g2_neg_be[64..128], Compress::No)
355            .unwrap();
356        let g2_neg_be: [u8; 128] = convert_endianness::<64, 128>(&g2_neg_be);
357
358        let points = [(g2, g2_be), (g2_neg, g2_neg_be)];
359
360        for (point, g2_be) in &points {
361            let mut compressed_ref = [0u8; 64];
362            G2::serialize_with_mode(point, compressed_ref.as_mut_slice(), Compress::Yes).unwrap();
363            let compressed_ref: [u8; 64] = convert_endianness::<64, 64>(&compressed_ref);
364
365            let decompressed = alt_bn128_g2_decompress(compressed_ref.as_slice()).unwrap();
366
367            assert_eq!(
368                alt_bn128_g2_compress(&decompressed).unwrap(),
369                compressed_ref
370            );
371            assert_eq!(decompressed, *g2_be);
372        }
373    }
374
375    #[test]
376    fn alt_bn128_compression_g1_point_of_infitity() {
377        let g1_bytes = vec![0u8; 64];
378        let g1_compressed = alt_bn128_g1_compress(&g1_bytes).unwrap();
379        let g1_decompressed = alt_bn128_g1_decompress(&g1_compressed).unwrap();
380        assert_eq!(g1_bytes, g1_decompressed);
381    }
382
383    #[test]
384    fn alt_bn128_compression_g2_point_of_infitity() {
385        let g1_bytes = vec![0u8; 128];
386        let g1_compressed = alt_bn128_g2_compress(&g1_bytes).unwrap();
387        let g1_decompressed = alt_bn128_g2_decompress(&g1_compressed).unwrap();
388        assert_eq!(g1_bytes, g1_decompressed);
389    }
390    #[test]
391    fn alt_bn128_compression_pairing_test_input() {
392        use serde_derive::Deserialize;
393
394        let test_data = r#"[
395        {
396            "Input": "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
397            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
398            "Name": "jeff1",
399            "Gas": 113000,
400            "NoBenchmark": false
401        },{
402            "Input": "2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc0203d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db841213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f06967a1237ebfeca9aaae0d6d0bab8e28c198c5a339ef8a2407e31cdac516db922160fa257a5fd5b280642ff47b65eca77e626cb685c84fa6d3b6882a283ddd1198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
403            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
404            "Name": "jeff2",
405            "Gas": 113000,
406            "NoBenchmark": false
407        },{
408            "Input": "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb314a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee245901b9e027bd5cfc2cb5db82d4dc9677ac795ec500ecd47deee3b5da006d6d049b811d7511c78158de484232fc68daf8a45cf217d1c2fae693ff5871e8752d73b21198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
409            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
410            "Name": "jeff3",
411            "Gas": 113000,
412            "NoBenchmark": false
413        },{
414            "Input": "2f2ea0b3da1e8ef11914acf8b2e1b32d99df51f5f4f206fc6b947eae860eddb6068134ddb33dc888ef446b648d72338684d678d2eb2371c61a50734d78da4b7225f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb122acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf6806d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb186bac5188a98c45e6016873d107f5cd131f3a3e339d0375e58bd6219347b008122ae2b09e539e152ec5364e7e2204b03d11d3caa038bfc7cd499f8176aacbee1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd415794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f211b88da1679b0b64a63b7e0e7bfe52aae524f73a55be7fe70c7e9bfc94b4cf0da1213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f",
415            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
416            "Name": "jeff4",
417            "Gas": 147000,
418            "NoBenchmark": false
419        },{
420            "Input": "20a754d2071d4d53903e3b31a7e98ad6882d58aec240ef981fdf0a9d22c5926a29c853fcea789887315916bbeb89ca37edb355b4f980c9a12a94f30deeed30211213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f1abb4a25eb9379ae96c84fff9f0540abcfc0a0d11aeda02d4f37e4baf74cb0c11073b3ff2cdbb38755f8691ea59e9606696b3ff278acfc098fa8226470d03869217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac290a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a98552fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d70f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2198a1f162a73261f112401aa2db79c7dab1533c9935c77290a6ce3b191f2318d198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
421            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
422            "Name": "jeff5",
423            "Gas": 147000,
424            "NoBenchmark": false
425        },{
426            "Input": "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c103188585e2364128fe25c70558f1560f4f9350baf3959e603cc91486e110936198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
427            "Expected": "0000000000000000000000000000000000000000000000000000000000000000",
428            "Name": "jeff6",
429            "Gas": 113000,
430            "NoBenchmark": false
431        },{
432            "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
433            "Expected": "0000000000000000000000000000000000000000000000000000000000000000",
434            "Name": "one_point",
435            "Gas": 79000,
436            "NoBenchmark": false
437        },{
438            "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d",
439            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
440            "Name": "two_point_match_2",
441            "Gas": 113000,
442            "NoBenchmark": false
443        },{
444            "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
445            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
446            "Name": "two_point_match_3",
447            "Gas": 113000,
448            "NoBenchmark": false
449        },{
450            "Input": "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75",
451            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
452            "Name": "two_point_match_4",
453            "Gas": 113000,
454            "NoBenchmark": false
455        },{
456            "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d",
457            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
458            "Name": "ten_point_match_1",
459            "Gas": 385000,
460            "NoBenchmark": false
461        },{
462            "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
463            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
464            "Name": "ten_point_match_2",
465            "Gas": 385000,
466            "NoBenchmark": false
467        },{
468            "Input": "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75",
469            "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
470            "Name": "ten_point_match_3",
471            "Gas": 113000,
472            "NoBenchmark": false
473        }
474        ]"#;
475
476        #[derive(Deserialize)]
477        #[serde(rename_all = "PascalCase")]
478        struct TestCase {
479            input: String,
480        }
481
482        let test_cases: Vec<TestCase> = serde_json::from_str(test_data).unwrap();
483
484        test_cases.iter().for_each(|test| {
485            let input = array_bytes::hex2bytes_unchecked(&test.input);
486            let g1 = input[0..64].to_vec();
487            let g1_compressed = alt_bn128_g1_compress(&g1).unwrap();
488            assert_eq!(g1, alt_bn128_g1_decompress(&g1_compressed).unwrap());
489            let g2 = input[64..192].to_vec();
490            let g2_compressed = alt_bn128_g2_compress(&g2).unwrap();
491            assert_eq!(g2, alt_bn128_g2_decompress(&g2_compressed).unwrap());
492        });
493    }
494}