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 = "atlas"))]
10pub mod versioned {
11 pub use crate::{
12 addition::{
13 alt_bn128_versioned_g1_addition, VersionedG1Addition, ALT_BN128_ADDITION_INPUT_SIZE,
14 ALT_BN128_ADDITION_OUTPUT_SIZE, ALT_BN128_G1_ADD_BE, ALT_BN128_G1_ADD_LE,
15 ALT_BN128_G1_SUB_BE, ALT_BN128_G1_SUB_LE,
16 },
17 multiplication::{
18 alt_bn128_versioned_g1_multiplication, VersionedG1Multiplication, ALT_BN128_G1_MUL_BE,
19 ALT_BN128_G1_MUL_LE, ALT_BN128_MULTIPLICATION_INPUT_SIZE,
20 ALT_BN128_MULTIPLICATION_OUTPUT_SIZE,
21 },
22 pairing::{
23 alt_bn128_versioned_pairing, VersionedPairing, ALT_BN128_PAIRING_BE,
24 ALT_BN128_PAIRING_ELEMENT_SIZE, ALT_BN128_PAIRING_LE, ALT_BN128_PAIRING_OUTPUT_SIZE,
25 },
26 target_arch::Endianness,
27 };
28 #[allow(deprecated)]
29 pub use crate::{
30 addition::{
31 ALT_BN128_ADD, ALT_BN128_ADDITION_INPUT_LEN, ALT_BN128_ADDITION_OUTPUT_LEN,
32 ALT_BN128_SUB,
33 },
34 multiplication::{
35 ALT_BN128_MUL, ALT_BN128_MULTIPLICATION_INPUT_LEN, ALT_BN128_MULTIPLICATION_OUTPUT_LEN,
36 },
37 pairing::{ALT_BN128_PAIRING, ALT_BN128_PAIRING_ELEMENT_LEN, ALT_BN128_PAIRING_OUTPUT_LEN},
38 };
39}
40
41pub mod prelude {
43 #[allow(deprecated)]
44 #[cfg(not(target_os = "atlas"))]
45 pub use crate::multiplication::alt_bn128_multiplication_128; #[allow(deprecated)]
47 pub use crate::{
48 addition::{
49 alt_bn128_addition, ALT_BN128_ADD, ALT_BN128_ADDITION_INPUT_LEN,
50 ALT_BN128_ADDITION_OUTPUT_LEN, ALT_BN128_SUB,
51 },
52 multiplication::{
53 alt_bn128_multiplication, ALT_BN128_MUL, ALT_BN128_MULTIPLICATION_INPUT_LEN,
54 ALT_BN128_MULTIPLICATION_OUTPUT_LEN,
55 },
56 pairing::{
57 alt_bn128_pairing, ALT_BN128_PAIRING, ALT_BN128_PAIRING_ELEMENT_LEN,
58 ALT_BN128_PAIRING_OUTPUT_LEN,
59 },
60 };
61 pub use crate::{
62 addition::{
63 alt_bn128_g1_addition_be, alt_bn128_g1_addition_le, ALT_BN128_ADDITION_INPUT_SIZE,
64 ALT_BN128_ADDITION_OUTPUT_SIZE, ALT_BN128_G1_ADD_BE, ALT_BN128_G1_ADD_LE,
65 ALT_BN128_G1_SUB_BE, ALT_BN128_G1_SUB_LE,
66 },
67 consts::*,
68 multiplication::{
69 alt_bn128_g1_multiplication_be, alt_bn128_g1_multiplication_le, ALT_BN128_G1_MUL_BE,
70 ALT_BN128_G1_MUL_LE, ALT_BN128_MULTIPLICATION_INPUT_SIZE,
71 ALT_BN128_MULTIPLICATION_OUTPUT_SIZE,
72 },
73 pairing::{
74 alt_bn128_pairing_be, alt_bn128_pairing_le, ALT_BN128_PAIRING_BE,
75 ALT_BN128_PAIRING_ELEMENT_SIZE, ALT_BN128_PAIRING_LE, ALT_BN128_PAIRING_OUTPUT_SIZE,
76 },
77 AltBn128Error,
78 };
79}
80
81#[cfg(not(target_os = "atlas"))]
82use bytemuck::{Pod, Zeroable};
83use thiserror::Error;
84
85mod consts {
86 pub const ALT_BN128_FIELD_SIZE: usize = 32;
88
89 pub const ALT_BN128_G1_POINT_SIZE: usize = ALT_BN128_FIELD_SIZE * 2;
92
93 #[deprecated(since = "3.1.0", note = "Please use `ALT_BN128_G1_POINT_SIZE` instead")]
94 pub const ALT_BN128_POINT_SIZE: usize = ALT_BN128_G1_POINT_SIZE;
95
96 pub const ALT_BN128_G2_POINT_SIZE: usize = ALT_BN128_FIELD_SIZE * 4;
98}
99
100#[derive(Debug, Error, Clone, PartialEq, Eq)]
103pub enum AltBn128Error {
104 #[error("The input data is invalid")]
105 InvalidInputData,
106 #[error("Invalid group data")]
107 GroupError,
108 #[error("Slice data is going out of input data bounds")]
109 SliceOutOfBounds,
110 #[error("Unexpected error")]
111 UnexpectedError,
112 #[error("Failed to convert a byte slice into a vector {0:?}")]
113 TryIntoVecError(Vec<u8>),
114 #[error("Failed to convert projective to affine g1")]
115 ProjectiveToG1Failed,
116}
117
118impl From<u64> for AltBn128Error {
119 fn from(v: u64) -> AltBn128Error {
120 match v {
121 1 => AltBn128Error::InvalidInputData,
122 2 => AltBn128Error::GroupError,
123 3 => AltBn128Error::SliceOutOfBounds,
124 4 => AltBn128Error::TryIntoVecError(Vec::new()),
125 5 => AltBn128Error::ProjectiveToG1Failed,
126 _ => AltBn128Error::UnexpectedError,
127 }
128 }
129}
130
131impl From<AltBn128Error> for u64 {
132 fn from(v: AltBn128Error) -> u64 {
133 match v {
135 AltBn128Error::InvalidInputData => 1,
136 AltBn128Error::GroupError => 2,
137 AltBn128Error::SliceOutOfBounds => 3,
138 AltBn128Error::TryIntoVecError(_) => 4,
139 AltBn128Error::ProjectiveToG1Failed => 5,
140 AltBn128Error::UnexpectedError => 6,
141 }
142 }
143}
144
145#[cfg(not(target_os = "atlas"))]
146use consts::{
147 ALT_BN128_FIELD_SIZE as FIELD_SIZE, ALT_BN128_G1_POINT_SIZE as G1_POINT_SIZE,
148 ALT_BN128_G2_POINT_SIZE as G2_POINT_SIZE,
149};
150
151pub(crate) const LE_FLAG: u64 = 0x80;
153
154#[cfg(not(target_os = "atlas"))]
166#[derive(Clone, Copy, Debug, PartialEq, Eq, Pod, Zeroable)]
167#[repr(transparent)]
168pub struct PodG1(pub [u8; G1_POINT_SIZE]);
169
170#[cfg(not(target_os = "atlas"))]
187#[derive(Clone, Copy, Debug, PartialEq, Eq, Pod, Zeroable)]
188#[repr(transparent)]
189pub struct PodG2(pub [u8; G2_POINT_SIZE]);
190
191#[cfg(not(target_os = "atlas"))]
192mod target_arch {
193 use {
194 super::*,
195 ark_ec::{self, AffineRepr},
196 ark_serialize::{CanonicalDeserialize, Compress, Validate},
197 };
198
199 pub(crate) type G1 = ark_bn254::g1::G1Affine;
200 pub(crate) type G2 = ark_bn254::g2::G2Affine;
201
202 impl PodG1 {
203 pub(crate) fn from_be_bytes(be_bytes: &[u8]) -> Result<Self, AltBn128Error> {
206 if be_bytes.len() != G1_POINT_SIZE {
207 return Err(AltBn128Error::SliceOutOfBounds);
208 }
209 let mut pod_bytes = [0u8; G1_POINT_SIZE];
210 reverse_copy(&be_bytes[..FIELD_SIZE], &mut pod_bytes[..FIELD_SIZE])?;
211 reverse_copy(&be_bytes[FIELD_SIZE..], &mut pod_bytes[FIELD_SIZE..])?;
212 Ok(Self(pod_bytes))
213 }
214
215 #[inline(always)]
218 pub(crate) fn from_le_bytes(le_bytes: &[u8]) -> Result<Self, AltBn128Error> {
219 Ok(Self(
220 le_bytes
221 .try_into()
222 .map_err(|_| AltBn128Error::SliceOutOfBounds)?,
223 ))
224 }
225 }
226
227 impl PodG2 {
228 pub(crate) fn from_be_bytes(be_bytes: &[u8]) -> Result<Self, AltBn128Error> {
232 if be_bytes.len() != G2_POINT_SIZE {
233 return Err(AltBn128Error::SliceOutOfBounds);
234 }
235 const SOURCE_X1_INDEX: usize = 0;
237 const SOURCE_X0_INDEX: usize = SOURCE_X1_INDEX.saturating_add(FIELD_SIZE);
238 const SOURCE_Y1_INDEX: usize = SOURCE_X0_INDEX.saturating_add(FIELD_SIZE);
239 const SOURCE_Y0_INDEX: usize = SOURCE_Y1_INDEX.saturating_add(FIELD_SIZE);
240
241 const TARGET_X0_INDEX: usize = 0;
242 const TARGET_X1_INDEX: usize = TARGET_X0_INDEX.saturating_add(FIELD_SIZE);
243 const TARGET_Y0_INDEX: usize = TARGET_X1_INDEX.saturating_add(FIELD_SIZE);
244 const TARGET_Y1_INDEX: usize = TARGET_Y0_INDEX.saturating_add(FIELD_SIZE);
245
246 let mut pod_bytes = [0u8; G2_POINT_SIZE];
247 reverse_copy(
248 &be_bytes[SOURCE_X1_INDEX..SOURCE_X1_INDEX.saturating_add(FIELD_SIZE)],
249 &mut pod_bytes[TARGET_X1_INDEX..TARGET_X1_INDEX.saturating_add(FIELD_SIZE)],
250 )?;
251 reverse_copy(
252 &be_bytes[SOURCE_X0_INDEX..SOURCE_X0_INDEX.saturating_add(FIELD_SIZE)],
253 &mut pod_bytes[TARGET_X0_INDEX..TARGET_X0_INDEX.saturating_add(FIELD_SIZE)],
254 )?;
255 reverse_copy(
256 &be_bytes[SOURCE_Y1_INDEX..SOURCE_Y1_INDEX.saturating_add(FIELD_SIZE)],
257 &mut pod_bytes[TARGET_Y1_INDEX..TARGET_Y1_INDEX.saturating_add(FIELD_SIZE)],
258 )?;
259 reverse_copy(
260 &be_bytes[SOURCE_Y0_INDEX..SOURCE_Y0_INDEX.saturating_add(FIELD_SIZE)],
261 &mut pod_bytes[TARGET_Y0_INDEX..TARGET_Y0_INDEX.saturating_add(FIELD_SIZE)],
262 )?;
263 Ok(Self(pod_bytes))
264 }
265
266 #[inline(always)]
269 pub(crate) fn from_le_bytes(le_bytes: &[u8]) -> Result<Self, AltBn128Error> {
270 Ok(Self(
271 le_bytes
272 .try_into()
273 .map_err(|_| AltBn128Error::SliceOutOfBounds)?,
274 ))
275 }
276 }
277
278 impl TryFrom<PodG1> for G1 {
279 type Error = AltBn128Error;
280
281 fn try_from(bytes: PodG1) -> Result<Self, Self::Error> {
282 if bytes.0 == [0u8; 64] {
283 return Ok(G1::zero());
284 }
285 let g1 = Self::deserialize_with_mode(
286 &*[&bytes.0[..], &[0u8][..]].concat(),
287 Compress::No,
288 Validate::Yes,
289 );
290
291 match g1 {
292 Ok(g1) => {
293 if !g1.is_on_curve() {
294 Err(AltBn128Error::GroupError)
295 } else {
296 Ok(g1)
297 }
298 }
299 Err(_) => Err(AltBn128Error::InvalidInputData),
300 }
301 }
302 }
303
304 impl TryFrom<PodG2> for G2 {
305 type Error = AltBn128Error;
306
307 fn try_from(bytes: PodG2) -> Result<Self, Self::Error> {
308 if bytes.0 == [0u8; 128] {
309 return Ok(G2::zero());
310 }
311 let g2 = Self::deserialize_with_mode(
312 &*[&bytes.0[..], &[0u8][..]].concat(),
313 Compress::No,
314 Validate::Yes,
315 );
316
317 match g2 {
318 Ok(g2) => {
319 if !g2.is_on_curve() {
320 Err(AltBn128Error::GroupError)
321 } else {
322 Ok(g2)
323 }
324 }
325 Err(_) => Err(AltBn128Error::InvalidInputData),
326 }
327 }
328 }
329
330 pub enum Endianness {
331 BE,
332 LE,
333 }
334
335 pub(crate) fn convert_endianness_64(bytes: &[u8]) -> Vec<u8> {
336 bytes
337 .chunks(32)
338 .flat_map(|b| b.iter().copied().rev().collect::<Vec<u8>>())
339 .collect::<Vec<u8>>()
340 }
341
342 pub(crate) fn reverse_copy(source: &[u8], destination: &mut [u8]) -> Result<(), AltBn128Error> {
344 if source.len() != destination.len() {
345 return Err(AltBn128Error::SliceOutOfBounds);
346 }
347 for (source_index, destination_index) in source.iter().rev().zip(destination.iter_mut()) {
348 *destination_index = *source_index;
349 }
350 Ok(())
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use {
357 crate::{prelude::*, PodG1},
358 ark_bn254::g1::G1Affine,
359 ark_ec::AffineRepr,
360 ark_serialize::{CanonicalSerialize, Compress},
361 };
362
363 #[test]
364 fn zero_serialization_test() {
365 let zero = G1Affine::zero();
366 let mut result_point_data = [0u8; 64];
367 zero.x
368 .serialize_with_mode(&mut result_point_data[..32], Compress::No)
369 .map_err(|_| AltBn128Error::InvalidInputData)
370 .unwrap();
371 zero.y
372 .serialize_with_mode(&mut result_point_data[32..], Compress::No)
373 .map_err(|_| AltBn128Error::InvalidInputData)
374 .unwrap();
375 assert_eq!(result_point_data, [0u8; 64]);
376
377 let p: G1Affine = PodG1(result_point_data[..64].try_into().unwrap())
378 .try_into()
379 .unwrap();
380 assert_eq!(p, zero);
381 }
382}