atlas_bn254/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2pub(crate) mod addition;
3pub mod compression;
4pub(crate) mod multiplication;
5pub(crate) mod pairing;
6
7/// This module contains the versioned syscall implementations and is intended for use
8/// primarily by validator code.
9#[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
41/// This module should be used by Atlas programs or other downstream projects.
42pub mod prelude {
43    #[allow(deprecated)]
44    #[cfg(not(target_os = "atlas"))]
45    pub use crate::multiplication::alt_bn128_multiplication_128; // to be removed in v4.0
46    #[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    /// Size of the EC point field, in bytes.
87    pub const ALT_BN128_FIELD_SIZE: usize = 32;
88
89    /// Size of the EC point. `alt_bn128` point contains
90    /// the consistently united x and y fields as 64 bytes.
91    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    /// Elements in G2 is represented by 2 field-extension elements `(x, y)`.
97    pub const ALT_BN128_G2_POINT_SIZE: usize = ALT_BN128_FIELD_SIZE * 4;
98}
99
100// AltBn128Error must be removed once the
101// simplify_alt_bn128_syscall_error_codes feature gets activated
102#[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        // note: should never return 0, as it risks to be confused with syscall success
134        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
151/// A bitmask used to indicate that an operation's input data is little-endian.
152pub(crate) const LE_FLAG: u64 = 0x80;
153
154/// The BN254 (BN128) group element in G1 as a POD type.
155///
156/// A group element in G1 consists of two field elements `(x, y)`. A `PodG1`
157/// type expects a group element to be encoded as `[le(x), le(y)]` where
158/// `le(..)` is the little-endian encoding of the input field element as used
159/// in the `ark-bn254` crate. Note that this differs from the EIP-197 standard,
160/// which specifies that the field elements are encoded as big-endian.
161///
162/// `PodG1` can be constructed from both big-endian (EIP-197) and little-endian
163/// (ark-bn254) encodings using `from_be_bytes` and `from_le_bytes` methods,
164/// respectively.
165#[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/// The BN254 (BN128) group element in G2 as a POD type.
171///
172/// Elements in G2 is represented by 2 field-extension elements `(x, y)`. Each
173/// field-extension element itself is a degree 1 polynomial `x = x0 + x1*X`,
174/// `y = y0 + y1*X`. The EIP-197 standard encodes a G2 element as
175/// `[be(x1), be(x0), be(y1), be(y0)]` where `be(..)` is the big-endian
176/// encoding of the input field element. The `ark-bn254` crate encodes a G2
177/// element as `[le(x0), le(x1), le(y0), le(y1)]` where `le(..)` is the
178/// little-endian encoding of the input field element. Notably, in addition to
179/// the differences in the big-endian vs. little-endian encodings of field
180/// elements, the order of the polynomial field coefficients `x0`, `x1`, `y0`,
181/// and `y1` are different.
182///
183/// `PodG2` can be constructed from both big-endian (EIP-197) and little-endian
184/// (ark-bn254) encodings using `from_be_bytes` and `from_le_bytes` methods,
185/// respectively.
186#[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        /// Takes in an EIP-197 (big-endian) byte encoding of a group element in G1 and constructs a
204        /// `PodG1` struct that encodes the same bytes in little-endian.
205        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        /// Takes in a little-endian byte encoding of a group element in G1 and constructs a
216        /// `PodG1` struct that encodes the same bytes internally.
217        #[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        /// Takes in an EIP-197 (big-endian) byte encoding of a group element in G2
229        /// and constructs a `PodG2` struct that encodes the same bytes in
230        /// little-endian.
231        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            // note the cross order
236            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        /// Takes in a little-endian byte encoding of a group element in G2 and constructs a
267        /// `PodG2` struct that encodes the same bytes internally.
268        #[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    /// Copies a `source` byte slice into a `destination` byte slice in reverse order.
343    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}