1#![cfg_attr(docsrs, feature(doc_cfg))]
2pub(crate) mod addition;
3pub mod compression;
4pub(crate) mod multiplication;
5pub(crate) mod pairing;
6
7#[cfg(not(target_os = "solana"))]
10pub mod versioned {
11 pub use crate::{
12 addition::{
13 alt_bn128_versioned_g1_addition, alt_bn128_versioned_g2_addition, VersionedG1Addition,
14 VersionedG2Addition, ALT_BN128_G1_ADDITION_INPUT_SIZE, ALT_BN128_G1_ADD_BE,
15 ALT_BN128_G1_ADD_LE, ALT_BN128_G1_SUB_BE, ALT_BN128_G1_SUB_LE,
16 ALT_BN128_G2_ADDITION_INPUT_SIZE, ALT_BN128_G2_ADD_BE, ALT_BN128_G2_ADD_LE,
17 ALT_BN128_G2_SUB_BE, ALT_BN128_G2_SUB_LE,
18 },
19 consts::*,
20 multiplication::{
21 alt_bn128_versioned_g1_multiplication, alt_bn128_versioned_g2_multiplication,
22 VersionedG1Multiplication, VersionedG2Multiplication,
23 ALT_BN128_G1_MULTIPLICATION_INPUT_SIZE, ALT_BN128_G1_MUL_BE, ALT_BN128_G1_MUL_LE,
24 ALT_BN128_G2_MULTIPLICATION_INPUT_SIZE, ALT_BN128_G2_MUL_BE, ALT_BN128_G2_MUL_LE,
25 },
26 pairing::{
27 alt_bn128_versioned_pairing, VersionedPairing, ALT_BN128_PAIRING_BE,
28 ALT_BN128_PAIRING_ELEMENT_SIZE, ALT_BN128_PAIRING_LE, ALT_BN128_PAIRING_OUTPUT_SIZE,
29 },
30 target_arch::Endianness,
31 };
32 #[allow(deprecated)]
33 pub use crate::{
34 addition::{
35 ALT_BN128_ADD, ALT_BN128_ADDITION_INPUT_LEN, ALT_BN128_ADDITION_INPUT_SIZE,
36 ALT_BN128_ADDITION_OUTPUT_LEN, ALT_BN128_ADDITION_OUTPUT_SIZE, ALT_BN128_SUB,
37 },
38 multiplication::{
39 ALT_BN128_MUL, ALT_BN128_MULTIPLICATION_INPUT_LEN, ALT_BN128_MULTIPLICATION_INPUT_SIZE,
40 ALT_BN128_MULTIPLICATION_OUTPUT_LEN, ALT_BN128_MULTIPLICATION_OUTPUT_SIZE,
41 },
42 pairing::{ALT_BN128_PAIRING, ALT_BN128_PAIRING_ELEMENT_LEN, ALT_BN128_PAIRING_OUTPUT_LEN},
43 };
44}
45
46pub mod prelude {
48 #[allow(deprecated)]
49 #[cfg(not(target_os = "solana"))]
50 pub use crate::multiplication::alt_bn128_multiplication_128; #[allow(deprecated)]
52 pub use crate::{
53 addition::{
54 alt_bn128_addition, ALT_BN128_ADD, ALT_BN128_ADDITION_INPUT_LEN,
55 ALT_BN128_ADDITION_INPUT_SIZE, ALT_BN128_ADDITION_OUTPUT_LEN,
56 ALT_BN128_ADDITION_OUTPUT_SIZE, ALT_BN128_SUB,
57 },
58 multiplication::{
59 alt_bn128_multiplication, ALT_BN128_MUL, ALT_BN128_MULTIPLICATION_INPUT_LEN,
60 ALT_BN128_MULTIPLICATION_INPUT_SIZE, ALT_BN128_MULTIPLICATION_OUTPUT_LEN,
61 ALT_BN128_MULTIPLICATION_OUTPUT_SIZE,
62 },
63 pairing::{
64 alt_bn128_pairing, ALT_BN128_PAIRING, ALT_BN128_PAIRING_ELEMENT_LEN,
65 ALT_BN128_PAIRING_OUTPUT_LEN,
66 },
67 };
68 pub use crate::{
69 addition::{
70 alt_bn128_g1_addition_be, alt_bn128_g1_addition_le, alt_bn128_g2_addition_be,
71 alt_bn128_g2_addition_le, ALT_BN128_G1_ADDITION_INPUT_SIZE, ALT_BN128_G1_ADD_BE,
72 ALT_BN128_G1_ADD_LE, ALT_BN128_G1_SUB_BE, ALT_BN128_G1_SUB_LE,
73 ALT_BN128_G2_ADDITION_INPUT_SIZE, ALT_BN128_G2_ADD_BE, ALT_BN128_G2_ADD_LE,
74 ALT_BN128_G2_SUB_BE, ALT_BN128_G2_SUB_LE,
75 },
76 consts::*,
77 multiplication::{
78 alt_bn128_g1_multiplication_be, alt_bn128_g1_multiplication_le,
79 alt_bn128_g2_multiplication_be, alt_bn128_g2_multiplication_le,
80 ALT_BN128_G1_MULTIPLICATION_INPUT_SIZE, ALT_BN128_G1_MUL_BE, ALT_BN128_G1_MUL_LE,
81 ALT_BN128_G2_MULTIPLICATION_INPUT_SIZE, ALT_BN128_G2_MUL_BE, ALT_BN128_G2_MUL_LE,
82 },
83 pairing::{
84 alt_bn128_pairing_be, alt_bn128_pairing_le, ALT_BN128_PAIRING_BE,
85 ALT_BN128_PAIRING_ELEMENT_SIZE, ALT_BN128_PAIRING_LE, ALT_BN128_PAIRING_OUTPUT_SIZE,
86 },
87 AltBn128Error,
88 };
89}
90
91#[cfg(not(target_os = "solana"))]
92use bytemuck::{Pod, Zeroable};
93use thiserror::Error;
94
95mod consts {
96 pub const ALT_BN128_FIELD_SIZE: usize = 32;
98
99 pub const ALT_BN128_FQ2_SIZE: usize = ALT_BN128_FIELD_SIZE * 2;
101
102 pub const ALT_BN128_G1_POINT_SIZE: usize = ALT_BN128_FIELD_SIZE * 2;
105
106 #[deprecated(since = "3.1.0", note = "Please use `ALT_BN128_G1_POINT_SIZE` instead")]
107 pub const ALT_BN128_POINT_SIZE: usize = ALT_BN128_G1_POINT_SIZE;
108
109 pub const ALT_BN128_G2_POINT_SIZE: usize = ALT_BN128_FQ2_SIZE * 2;
111}
112
113#[derive(Debug, Error, Clone, PartialEq, Eq)]
116pub enum AltBn128Error {
117 #[error("The input data is invalid")]
118 InvalidInputData,
119 #[error("Invalid group data")]
120 GroupError,
121 #[error("Slice data is going out of input data bounds")]
122 SliceOutOfBounds,
123 #[error("Unexpected error")]
124 UnexpectedError,
125 #[error("Failed to convert a byte slice into a vector {0:?}")]
126 TryIntoVecError(Vec<u8>),
127 #[error("Failed to convert projective to affine g1")]
128 ProjectiveToG1Failed,
129}
130
131impl From<u64> for AltBn128Error {
132 fn from(v: u64) -> AltBn128Error {
133 match v {
134 1 => AltBn128Error::InvalidInputData,
135 2 => AltBn128Error::GroupError,
136 3 => AltBn128Error::SliceOutOfBounds,
137 4 => AltBn128Error::TryIntoVecError(Vec::new()),
138 5 => AltBn128Error::ProjectiveToG1Failed,
139 _ => AltBn128Error::UnexpectedError,
140 }
141 }
142}
143
144impl From<AltBn128Error> for u64 {
145 fn from(v: AltBn128Error) -> u64 {
146 match v {
148 AltBn128Error::InvalidInputData => 1,
149 AltBn128Error::GroupError => 2,
150 AltBn128Error::SliceOutOfBounds => 3,
151 AltBn128Error::TryIntoVecError(_) => 4,
152 AltBn128Error::ProjectiveToG1Failed => 5,
153 AltBn128Error::UnexpectedError => 6,
154 }
155 }
156}
157
158#[cfg(not(target_os = "solana"))]
159use consts::{
160 ALT_BN128_FIELD_SIZE as FIELD_SIZE, ALT_BN128_FQ2_SIZE as FQ2_SIZE,
161 ALT_BN128_G1_POINT_SIZE as G1_POINT_SIZE, ALT_BN128_G2_POINT_SIZE as G2_POINT_SIZE,
162};
163
164pub(crate) const LE_FLAG: u64 = 0x80;
166
167#[cfg(not(target_os = "solana"))]
179#[derive(Clone, Copy, Debug, PartialEq, Eq, Pod, Zeroable)]
180#[repr(transparent)]
181pub struct PodG1(pub [u8; G1_POINT_SIZE]);
182
183#[cfg(not(target_os = "solana"))]
200#[derive(Clone, Copy, Debug, PartialEq, Eq, Pod, Zeroable)]
201#[repr(transparent)]
202pub struct PodG2(pub [u8; G2_POINT_SIZE]);
203
204#[cfg(not(target_os = "solana"))]
205mod target_arch {
206 use {
207 super::*,
208 ark_ec::{self, AffineRepr},
209 ark_serialize::{CanonicalDeserialize, Compress, Validate},
210 };
211
212 pub(crate) type G1 = ark_bn254::g1::G1Affine;
213 pub(crate) type G2 = ark_bn254::g2::G2Affine;
214
215 impl PodG1 {
216 pub(crate) fn from_be_bytes(be_bytes: &[u8]) -> Result<Self, AltBn128Error> {
219 let pod_bytes = convert_endianness::<FIELD_SIZE, G1_POINT_SIZE>(
220 be_bytes
221 .try_into()
222 .map_err(|_| AltBn128Error::SliceOutOfBounds)?,
223 );
224 Ok(Self(pod_bytes))
225 }
226
227 #[inline(always)]
230 pub(crate) fn from_le_bytes(le_bytes: &[u8]) -> Result<Self, AltBn128Error> {
231 Ok(Self(
232 le_bytes
233 .try_into()
234 .map_err(|_| AltBn128Error::SliceOutOfBounds)?,
235 ))
236 }
237 }
238
239 impl PodG2 {
240 pub(crate) fn into_affine_unchecked(self) -> Result<G2, AltBn128Error> {
243 if self.0 == [0u8; 128] {
244 return Ok(G2::zero());
245 }
246
247 let g2 = G2::deserialize_with_mode(
249 &*[&self.0[..], &[0u8][..]].concat(),
250 Compress::No,
251 Validate::No,
252 )
253 .map_err(|_| AltBn128Error::InvalidInputData)?;
254
255 if !g2.is_on_curve() {
257 return Err(AltBn128Error::GroupError);
258 }
259
260 Ok(g2)
261 }
262
263 pub(crate) fn from_be_bytes(be_bytes: &[u8]) -> Result<Self, AltBn128Error> {
267 let pod_bytes = convert_endianness::<FQ2_SIZE, G2_POINT_SIZE>(
268 be_bytes
269 .try_into()
270 .map_err(|_| AltBn128Error::SliceOutOfBounds)?,
271 );
272 Ok(Self(pod_bytes))
273 }
274
275 #[inline(always)]
278 pub(crate) fn from_le_bytes(le_bytes: &[u8]) -> Result<Self, AltBn128Error> {
279 Ok(Self(
280 le_bytes
281 .try_into()
282 .map_err(|_| AltBn128Error::SliceOutOfBounds)?,
283 ))
284 }
285 }
286
287 impl TryFrom<PodG1> for G1 {
288 type Error = AltBn128Error;
289
290 fn try_from(bytes: PodG1) -> Result<Self, Self::Error> {
291 if bytes.0 == [0u8; 64] {
292 return Ok(G1::zero());
293 }
294 let g1 = Self::deserialize_with_mode(
295 &*[&bytes.0[..], &[0u8][..]].concat(),
296 Compress::No,
297 Validate::Yes,
298 );
299
300 match g1 {
301 Ok(g1) => {
302 if !g1.is_on_curve() {
303 Err(AltBn128Error::GroupError)
304 } else {
305 Ok(g1)
306 }
307 }
308 Err(_) => Err(AltBn128Error::InvalidInputData),
309 }
310 }
311 }
312
313 impl TryFrom<PodG2> for G2 {
314 type Error = AltBn128Error;
315
316 fn try_from(bytes: PodG2) -> Result<Self, Self::Error> {
317 if bytes.0 == [0u8; 128] {
318 return Ok(G2::zero());
319 }
320 let g2 = Self::deserialize_with_mode(
321 &*[&bytes.0[..], &[0u8][..]].concat(),
322 Compress::No,
323 Validate::Yes,
324 );
325
326 match g2 {
327 Ok(g2) => {
328 if !g2.is_on_curve() {
329 Err(AltBn128Error::GroupError)
330 } else {
331 Ok(g2)
332 }
333 }
334 Err(_) => Err(AltBn128Error::InvalidInputData),
335 }
336 }
337 }
338
339 pub enum Endianness {
340 BE,
341 LE,
342 }
343
344 pub fn convert_endianness<const CHUNK_SIZE: usize, const ARRAY_SIZE: usize>(
352 bytes: &[u8; ARRAY_SIZE],
353 ) -> [u8; ARRAY_SIZE] {
354 let reversed: [_; ARRAY_SIZE] = bytes
355 .chunks_exact(CHUNK_SIZE)
356 .flat_map(|chunk| chunk.iter().rev().copied())
357 .enumerate()
358 .fold([0u8; ARRAY_SIZE], |mut acc, (i, v)| {
359 acc[i] = v;
360 acc
361 });
362 reversed
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use {
369 crate::{prelude::*, PodG1},
370 ark_bn254::g1::G1Affine,
371 ark_ec::AffineRepr,
372 ark_serialize::{CanonicalSerialize, Compress},
373 };
374
375 #[test]
376 fn zero_serialization_test() {
377 let zero = G1Affine::zero();
378 let mut result_point_data = [0u8; 64];
379 zero.x
380 .serialize_with_mode(&mut result_point_data[..32], Compress::No)
381 .map_err(|_| AltBn128Error::InvalidInputData)
382 .unwrap();
383 zero.y
384 .serialize_with_mode(&mut result_point_data[32..], Compress::No)
385 .map_err(|_| AltBn128Error::InvalidInputData)
386 .unwrap();
387 assert_eq!(result_point_data, [0u8; 64]);
388
389 let p: G1Affine = PodG1(result_point_data[..64].try_into().unwrap())
390 .try_into()
391 .unwrap();
392 assert_eq!(p, zero);
393 }
394}