#![cfg_attr(docsrs, feature(doc_cfg))]
pub(crate) mod addition;
pub mod compression;
pub(crate) mod multiplication;
pub(crate) mod pairing;
#[cfg(not(target_os = "atlas"))]
pub mod versioned {
pub use crate::{
addition::{
alt_bn128_versioned_g1_addition, VersionedG1Addition, ALT_BN128_ADDITION_INPUT_SIZE,
ALT_BN128_ADDITION_OUTPUT_SIZE, ALT_BN128_G1_ADD_BE, ALT_BN128_G1_ADD_LE,
ALT_BN128_G1_SUB_BE, ALT_BN128_G1_SUB_LE,
},
multiplication::{
alt_bn128_versioned_g1_multiplication, VersionedG1Multiplication, ALT_BN128_G1_MUL_BE,
ALT_BN128_G1_MUL_LE, ALT_BN128_MULTIPLICATION_INPUT_SIZE,
ALT_BN128_MULTIPLICATION_OUTPUT_SIZE,
},
pairing::{
alt_bn128_versioned_pairing, VersionedPairing, ALT_BN128_PAIRING_BE,
ALT_BN128_PAIRING_ELEMENT_SIZE, ALT_BN128_PAIRING_LE, ALT_BN128_PAIRING_OUTPUT_SIZE,
},
target_arch::Endianness,
};
#[allow(deprecated)]
pub use crate::{
addition::{
ALT_BN128_ADD, ALT_BN128_ADDITION_INPUT_LEN, ALT_BN128_ADDITION_OUTPUT_LEN,
ALT_BN128_SUB,
},
multiplication::{
ALT_BN128_MUL, ALT_BN128_MULTIPLICATION_INPUT_LEN, ALT_BN128_MULTIPLICATION_OUTPUT_LEN,
},
pairing::{ALT_BN128_PAIRING, ALT_BN128_PAIRING_ELEMENT_LEN, ALT_BN128_PAIRING_OUTPUT_LEN},
};
}
pub mod prelude {
#[allow(deprecated)]
#[cfg(not(target_os = "atlas"))]
pub use crate::multiplication::alt_bn128_multiplication_128; #[allow(deprecated)]
pub use crate::{
addition::{
alt_bn128_addition, ALT_BN128_ADD, ALT_BN128_ADDITION_INPUT_LEN,
ALT_BN128_ADDITION_OUTPUT_LEN, ALT_BN128_SUB,
},
multiplication::{
alt_bn128_multiplication, ALT_BN128_MUL, ALT_BN128_MULTIPLICATION_INPUT_LEN,
ALT_BN128_MULTIPLICATION_OUTPUT_LEN,
},
pairing::{
alt_bn128_pairing, ALT_BN128_PAIRING, ALT_BN128_PAIRING_ELEMENT_LEN,
ALT_BN128_PAIRING_OUTPUT_LEN,
},
};
pub use crate::{
addition::{
alt_bn128_g1_addition_be, alt_bn128_g1_addition_le, ALT_BN128_ADDITION_INPUT_SIZE,
ALT_BN128_ADDITION_OUTPUT_SIZE, ALT_BN128_G1_ADD_BE, ALT_BN128_G1_ADD_LE,
ALT_BN128_G1_SUB_BE, ALT_BN128_G1_SUB_LE,
},
consts::*,
multiplication::{
alt_bn128_g1_multiplication_be, alt_bn128_g1_multiplication_le, ALT_BN128_G1_MUL_BE,
ALT_BN128_G1_MUL_LE, ALT_BN128_MULTIPLICATION_INPUT_SIZE,
ALT_BN128_MULTIPLICATION_OUTPUT_SIZE,
},
pairing::{
alt_bn128_pairing_be, alt_bn128_pairing_le, ALT_BN128_PAIRING_BE,
ALT_BN128_PAIRING_ELEMENT_SIZE, ALT_BN128_PAIRING_LE, ALT_BN128_PAIRING_OUTPUT_SIZE,
},
AltBn128Error,
};
}
#[cfg(not(target_os = "atlas"))]
use bytemuck::{Pod, Zeroable};
use thiserror::Error;
mod consts {
pub const ALT_BN128_FIELD_SIZE: usize = 32;
pub const ALT_BN128_G1_POINT_SIZE: usize = ALT_BN128_FIELD_SIZE * 2;
#[deprecated(since = "3.1.0", note = "Please use `ALT_BN128_G1_POINT_SIZE` instead")]
pub const ALT_BN128_POINT_SIZE: usize = ALT_BN128_G1_POINT_SIZE;
pub const ALT_BN128_G2_POINT_SIZE: usize = ALT_BN128_FIELD_SIZE * 4;
}
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum AltBn128Error {
#[error("The input data is invalid")]
InvalidInputData,
#[error("Invalid group data")]
GroupError,
#[error("Slice data is going out of input data bounds")]
SliceOutOfBounds,
#[error("Unexpected error")]
UnexpectedError,
#[error("Failed to convert a byte slice into a vector {0:?}")]
TryIntoVecError(Vec<u8>),
#[error("Failed to convert projective to affine g1")]
ProjectiveToG1Failed,
}
impl From<u64> for AltBn128Error {
fn from(v: u64) -> AltBn128Error {
match v {
1 => AltBn128Error::InvalidInputData,
2 => AltBn128Error::GroupError,
3 => AltBn128Error::SliceOutOfBounds,
4 => AltBn128Error::TryIntoVecError(Vec::new()),
5 => AltBn128Error::ProjectiveToG1Failed,
_ => AltBn128Error::UnexpectedError,
}
}
}
impl From<AltBn128Error> for u64 {
fn from(v: AltBn128Error) -> u64 {
match v {
AltBn128Error::InvalidInputData => 1,
AltBn128Error::GroupError => 2,
AltBn128Error::SliceOutOfBounds => 3,
AltBn128Error::TryIntoVecError(_) => 4,
AltBn128Error::ProjectiveToG1Failed => 5,
AltBn128Error::UnexpectedError => 6,
}
}
}
#[cfg(not(target_os = "atlas"))]
use consts::{
ALT_BN128_FIELD_SIZE as FIELD_SIZE, ALT_BN128_G1_POINT_SIZE as G1_POINT_SIZE,
ALT_BN128_G2_POINT_SIZE as G2_POINT_SIZE,
};
pub(crate) const LE_FLAG: u64 = 0x80;
#[cfg(not(target_os = "atlas"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodG1(pub [u8; G1_POINT_SIZE]);
#[cfg(not(target_os = "atlas"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Pod, Zeroable)]
#[repr(transparent)]
pub struct PodG2(pub [u8; G2_POINT_SIZE]);
#[cfg(not(target_os = "atlas"))]
mod target_arch {
use {
super::*,
ark_ec::{self, AffineRepr},
ark_serialize::{CanonicalDeserialize, Compress, Validate},
};
pub(crate) type G1 = ark_bn254::g1::G1Affine;
pub(crate) type G2 = ark_bn254::g2::G2Affine;
impl PodG1 {
pub(crate) fn from_be_bytes(be_bytes: &[u8]) -> Result<Self, AltBn128Error> {
if be_bytes.len() != G1_POINT_SIZE {
return Err(AltBn128Error::SliceOutOfBounds);
}
let mut pod_bytes = [0u8; G1_POINT_SIZE];
reverse_copy(&be_bytes[..FIELD_SIZE], &mut pod_bytes[..FIELD_SIZE])?;
reverse_copy(&be_bytes[FIELD_SIZE..], &mut pod_bytes[FIELD_SIZE..])?;
Ok(Self(pod_bytes))
}
#[inline(always)]
pub(crate) fn from_le_bytes(le_bytes: &[u8]) -> Result<Self, AltBn128Error> {
Ok(Self(
le_bytes
.try_into()
.map_err(|_| AltBn128Error::SliceOutOfBounds)?,
))
}
}
impl PodG2 {
pub(crate) fn from_be_bytes(be_bytes: &[u8]) -> Result<Self, AltBn128Error> {
if be_bytes.len() != G2_POINT_SIZE {
return Err(AltBn128Error::SliceOutOfBounds);
}
const SOURCE_X1_INDEX: usize = 0;
const SOURCE_X0_INDEX: usize = SOURCE_X1_INDEX.saturating_add(FIELD_SIZE);
const SOURCE_Y1_INDEX: usize = SOURCE_X0_INDEX.saturating_add(FIELD_SIZE);
const SOURCE_Y0_INDEX: usize = SOURCE_Y1_INDEX.saturating_add(FIELD_SIZE);
const TARGET_X0_INDEX: usize = 0;
const TARGET_X1_INDEX: usize = TARGET_X0_INDEX.saturating_add(FIELD_SIZE);
const TARGET_Y0_INDEX: usize = TARGET_X1_INDEX.saturating_add(FIELD_SIZE);
const TARGET_Y1_INDEX: usize = TARGET_Y0_INDEX.saturating_add(FIELD_SIZE);
let mut pod_bytes = [0u8; G2_POINT_SIZE];
reverse_copy(
&be_bytes[SOURCE_X1_INDEX..SOURCE_X1_INDEX.saturating_add(FIELD_SIZE)],
&mut pod_bytes[TARGET_X1_INDEX..TARGET_X1_INDEX.saturating_add(FIELD_SIZE)],
)?;
reverse_copy(
&be_bytes[SOURCE_X0_INDEX..SOURCE_X0_INDEX.saturating_add(FIELD_SIZE)],
&mut pod_bytes[TARGET_X0_INDEX..TARGET_X0_INDEX.saturating_add(FIELD_SIZE)],
)?;
reverse_copy(
&be_bytes[SOURCE_Y1_INDEX..SOURCE_Y1_INDEX.saturating_add(FIELD_SIZE)],
&mut pod_bytes[TARGET_Y1_INDEX..TARGET_Y1_INDEX.saturating_add(FIELD_SIZE)],
)?;
reverse_copy(
&be_bytes[SOURCE_Y0_INDEX..SOURCE_Y0_INDEX.saturating_add(FIELD_SIZE)],
&mut pod_bytes[TARGET_Y0_INDEX..TARGET_Y0_INDEX.saturating_add(FIELD_SIZE)],
)?;
Ok(Self(pod_bytes))
}
#[inline(always)]
pub(crate) fn from_le_bytes(le_bytes: &[u8]) -> Result<Self, AltBn128Error> {
Ok(Self(
le_bytes
.try_into()
.map_err(|_| AltBn128Error::SliceOutOfBounds)?,
))
}
}
impl TryFrom<PodG1> for G1 {
type Error = AltBn128Error;
fn try_from(bytes: PodG1) -> Result<Self, Self::Error> {
if bytes.0 == [0u8; 64] {
return Ok(G1::zero());
}
let g1 = Self::deserialize_with_mode(
&*[&bytes.0[..], &[0u8][..]].concat(),
Compress::No,
Validate::Yes,
);
match g1 {
Ok(g1) => {
if !g1.is_on_curve() {
Err(AltBn128Error::GroupError)
} else {
Ok(g1)
}
}
Err(_) => Err(AltBn128Error::InvalidInputData),
}
}
}
impl TryFrom<PodG2> for G2 {
type Error = AltBn128Error;
fn try_from(bytes: PodG2) -> Result<Self, Self::Error> {
if bytes.0 == [0u8; 128] {
return Ok(G2::zero());
}
let g2 = Self::deserialize_with_mode(
&*[&bytes.0[..], &[0u8][..]].concat(),
Compress::No,
Validate::Yes,
);
match g2 {
Ok(g2) => {
if !g2.is_on_curve() {
Err(AltBn128Error::GroupError)
} else {
Ok(g2)
}
}
Err(_) => Err(AltBn128Error::InvalidInputData),
}
}
}
pub enum Endianness {
BE,
LE,
}
pub(crate) fn convert_endianness_64(bytes: &[u8]) -> Vec<u8> {
bytes
.chunks(32)
.flat_map(|b| b.iter().copied().rev().collect::<Vec<u8>>())
.collect::<Vec<u8>>()
}
pub(crate) fn reverse_copy(source: &[u8], destination: &mut [u8]) -> Result<(), AltBn128Error> {
if source.len() != destination.len() {
return Err(AltBn128Error::SliceOutOfBounds);
}
for (source_index, destination_index) in source.iter().rev().zip(destination.iter_mut()) {
*destination_index = *source_index;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use {
crate::{prelude::*, PodG1},
ark_bn254::g1::G1Affine,
ark_ec::AffineRepr,
ark_serialize::{CanonicalSerialize, Compress},
};
#[test]
fn zero_serialization_test() {
let zero = G1Affine::zero();
let mut result_point_data = [0u8; 64];
zero.x
.serialize_with_mode(&mut result_point_data[..32], Compress::No)
.map_err(|_| AltBn128Error::InvalidInputData)
.unwrap();
zero.y
.serialize_with_mode(&mut result_point_data[32..], Compress::No)
.map_err(|_| AltBn128Error::InvalidInputData)
.unwrap();
assert_eq!(result_point_data, [0u8; 64]);
let p: G1Affine = PodG1(result_point_data[..64].try_into().unwrap())
.try_into()
.unwrap();
assert_eq!(p, zero);
}
}