1pub mod compression;
2pub mod prelude {
3 pub use crate::{consts::*, target_arch::*, AltBn128Error};
4}
5
6use {
7 bytemuck::{Pod, Zeroable},
8 consts::*,
9 thiserror::Error,
10};
11
12mod consts {
13 pub const ALT_BN128_ADDITION_INPUT_LEN: usize = 128;
15
16 pub const ALT_BN128_MULTIPLICATION_INPUT_LEN: usize = 96;
18
19 pub const ALT_BN128_PAIRING_ELEMENT_LEN: usize = 192;
21
22 pub const ALT_BN128_ADDITION_OUTPUT_LEN: usize = 64;
24
25 pub const ALT_BN128_MULTIPLICATION_OUTPUT_LEN: usize = 64;
27
28 pub const ALT_BN128_PAIRING_OUTPUT_LEN: usize = 32;
30
31 pub const ALT_BN128_FIELD_SIZE: usize = 32;
33
34 pub const ALT_BN128_POINT_SIZE: usize = 64;
37
38 pub const ALT_BN128_ADD: u64 = 0;
39 pub const ALT_BN128_SUB: u64 = 1;
40 pub const ALT_BN128_MUL: u64 = 2;
41 pub const ALT_BN128_PAIRING: u64 = 3;
42}
43
44#[derive(Debug, Error, Clone, PartialEq, Eq)]
47pub enum AltBn128Error {
48 #[error("The input data is invalid")]
49 InvalidInputData,
50 #[error("Invalid group data")]
51 GroupError,
52 #[error("Slice data is going out of input data bounds")]
53 SliceOutOfBounds,
54 #[error("Unexpected error")]
55 UnexpectedError,
56 #[error("Failed to convert a byte slice into a vector {0:?}")]
57 TryIntoVecError(Vec<u8>),
58 #[error("Failed to convert projective to affine g1")]
59 ProjectiveToG1Failed,
60}
61
62impl From<u64> for AltBn128Error {
63 fn from(v: u64) -> AltBn128Error {
64 match v {
65 1 => AltBn128Error::InvalidInputData,
66 2 => AltBn128Error::GroupError,
67 3 => AltBn128Error::SliceOutOfBounds,
68 4 => AltBn128Error::TryIntoVecError(Vec::new()),
69 5 => AltBn128Error::ProjectiveToG1Failed,
70 _ => AltBn128Error::UnexpectedError,
71 }
72 }
73}
74
75impl From<AltBn128Error> for u64 {
76 fn from(v: AltBn128Error) -> u64 {
77 match v {
79 AltBn128Error::InvalidInputData => 1,
80 AltBn128Error::GroupError => 2,
81 AltBn128Error::SliceOutOfBounds => 3,
82 AltBn128Error::TryIntoVecError(_) => 4,
83 AltBn128Error::ProjectiveToG1Failed => 5,
84 AltBn128Error::UnexpectedError => 6,
85 }
86 }
87}
88
89use consts::{ALT_BN128_FIELD_SIZE as FIELD_SIZE, ALT_BN128_POINT_SIZE as G1_POINT_SIZE};
90
91#[derive(Clone, Copy, Debug, PartialEq, Eq, Pod, Zeroable)]
104#[repr(transparent)]
105pub struct PodG1(pub [u8; G1_POINT_SIZE]);
106
107const G2_POINT_SIZE: usize = FIELD_SIZE * 4;
108
109#[derive(Clone, Copy, Debug, PartialEq, Eq, Pod, Zeroable)]
127#[repr(transparent)]
128pub struct PodG2(pub [u8; G2_POINT_SIZE]);
129
130#[cfg(not(target_os = "trezoa"))]
131mod target_arch {
132 use {
133 super::*,
134 ark_bn254::{self, Config},
135 ark_ec::{self, models::bn::Bn, pairing::Pairing, AffineRepr},
136 ark_ff::{BigInteger, BigInteger256, One},
137 ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate},
138 };
139
140 type G1 = ark_bn254::g1::G1Affine;
141 type G2 = ark_bn254::g2::G2Affine;
142
143 impl PodG1 {
144 fn from_be_bytes(be_bytes: &[u8]) -> Result<Self, AltBn128Error> {
147 if be_bytes.len() != G1_POINT_SIZE {
148 return Err(AltBn128Error::SliceOutOfBounds);
149 }
150 let mut pod_bytes = [0u8; G1_POINT_SIZE];
151 reverse_copy(&be_bytes[..FIELD_SIZE], &mut pod_bytes[..FIELD_SIZE])?;
152 reverse_copy(&be_bytes[FIELD_SIZE..], &mut pod_bytes[FIELD_SIZE..])?;
153 Ok(Self(pod_bytes))
154 }
155 }
156
157 impl PodG2 {
158 fn from_be_bytes(be_bytes: &[u8]) -> Result<Self, AltBn128Error> {
162 if be_bytes.len() != G2_POINT_SIZE {
163 return Err(AltBn128Error::SliceOutOfBounds);
164 }
165 const SOURCE_X1_INDEX: usize = 0;
167 const SOURCE_X0_INDEX: usize = SOURCE_X1_INDEX.saturating_add(FIELD_SIZE);
168 const SOURCE_Y1_INDEX: usize = SOURCE_X0_INDEX.saturating_add(FIELD_SIZE);
169 const SOURCE_Y0_INDEX: usize = SOURCE_Y1_INDEX.saturating_add(FIELD_SIZE);
170
171 const TARGET_X0_INDEX: usize = 0;
172 const TARGET_X1_INDEX: usize = TARGET_X0_INDEX.saturating_add(FIELD_SIZE);
173 const TARGET_Y0_INDEX: usize = TARGET_X1_INDEX.saturating_add(FIELD_SIZE);
174 const TARGET_Y1_INDEX: usize = TARGET_Y0_INDEX.saturating_add(FIELD_SIZE);
175
176 let mut pod_bytes = [0u8; G2_POINT_SIZE];
177 reverse_copy(
178 &be_bytes[SOURCE_X1_INDEX..SOURCE_X1_INDEX.saturating_add(FIELD_SIZE)],
179 &mut pod_bytes[TARGET_X1_INDEX..TARGET_X1_INDEX.saturating_add(FIELD_SIZE)],
180 )?;
181 reverse_copy(
182 &be_bytes[SOURCE_X0_INDEX..SOURCE_X0_INDEX.saturating_add(FIELD_SIZE)],
183 &mut pod_bytes[TARGET_X0_INDEX..TARGET_X0_INDEX.saturating_add(FIELD_SIZE)],
184 )?;
185 reverse_copy(
186 &be_bytes[SOURCE_Y1_INDEX..SOURCE_Y1_INDEX.saturating_add(FIELD_SIZE)],
187 &mut pod_bytes[TARGET_Y1_INDEX..TARGET_Y1_INDEX.saturating_add(FIELD_SIZE)],
188 )?;
189 reverse_copy(
190 &be_bytes[SOURCE_Y0_INDEX..SOURCE_Y0_INDEX.saturating_add(FIELD_SIZE)],
191 &mut pod_bytes[TARGET_Y0_INDEX..TARGET_Y0_INDEX.saturating_add(FIELD_SIZE)],
192 )?;
193 Ok(Self(pod_bytes))
194 }
195 }
196
197 impl TryFrom<PodG1> for G1 {
198 type Error = AltBn128Error;
199
200 fn try_from(bytes: PodG1) -> Result<Self, Self::Error> {
201 if bytes.0 == [0u8; 64] {
202 return Ok(G1::zero());
203 }
204 let g1 = Self::deserialize_with_mode(
205 &*[&bytes.0[..], &[0u8][..]].concat(),
206 Compress::No,
207 Validate::Yes,
208 );
209
210 match g1 {
211 Ok(g1) => {
212 if !g1.is_on_curve() {
213 Err(AltBn128Error::GroupError)
214 } else {
215 Ok(g1)
216 }
217 }
218 Err(_) => Err(AltBn128Error::InvalidInputData),
219 }
220 }
221 }
222
223 impl TryFrom<PodG2> for G2 {
224 type Error = AltBn128Error;
225
226 fn try_from(bytes: PodG2) -> Result<Self, Self::Error> {
227 if bytes.0 == [0u8; 128] {
228 return Ok(G2::zero());
229 }
230 let g2 = Self::deserialize_with_mode(
231 &*[&bytes.0[..], &[0u8][..]].concat(),
232 Compress::No,
233 Validate::Yes,
234 );
235
236 match g2 {
237 Ok(g2) => {
238 if !g2.is_on_curve() {
239 Err(AltBn128Error::GroupError)
240 } else {
241 Ok(g2)
242 }
243 }
244 Err(_) => Err(AltBn128Error::InvalidInputData),
245 }
246 }
247 }
248
249 pub fn alt_bn128_addition(input: &[u8]) -> Result<Vec<u8>, AltBn128Error> {
250 if input.len() > ALT_BN128_ADDITION_INPUT_LEN {
251 return Err(AltBn128Error::InvalidInputData);
252 }
253
254 let mut input = input.to_vec();
255 input.resize(ALT_BN128_ADDITION_INPUT_LEN, 0);
256
257 let p: G1 = PodG1::from_be_bytes(&input[..64])?.try_into()?;
258 let q: G1 = PodG1::from_be_bytes(&input[64..ALT_BN128_ADDITION_INPUT_LEN])?.try_into()?;
259
260 #[allow(clippy::arithmetic_side_effects)]
261 let result_point = p + q;
262
263 let mut result_point_data = [0u8; ALT_BN128_ADDITION_OUTPUT_LEN];
264 let result_point_affine: G1 = result_point.into();
265 result_point_affine
266 .x
267 .serialize_with_mode(&mut result_point_data[..32], Compress::No)
268 .map_err(|_| AltBn128Error::InvalidInputData)?;
269 result_point_affine
270 .y
271 .serialize_with_mode(&mut result_point_data[32..], Compress::No)
272 .map_err(|_| AltBn128Error::InvalidInputData)?;
273
274 Ok(convert_endianness_64(&result_point_data[..]))
275 }
276
277 pub fn alt_bn128_multiplication(input: &[u8]) -> Result<Vec<u8>, AltBn128Error> {
278 alt_bn128_apply_multiplication(input, ALT_BN128_MULTIPLICATION_INPUT_LEN)
279 }
280
281 pub fn alt_bn128_multiplication_128(input: &[u8]) -> Result<Vec<u8>, AltBn128Error> {
282 alt_bn128_apply_multiplication(input, 128) }
284
285 fn alt_bn128_apply_multiplication(
286 input: &[u8],
287 expected_length: usize,
288 ) -> Result<Vec<u8>, AltBn128Error> {
289 if input.len() > expected_length {
290 return Err(AltBn128Error::InvalidInputData);
291 }
292
293 let mut input = input.to_vec();
294 input.resize(expected_length, 0);
295
296 let p: G1 = PodG1::from_be_bytes(&input[..64])?.try_into()?;
297 let mut fr_bytes = [0u8; 32];
298 reverse_copy(&input[64..96], &mut fr_bytes)?;
299 let fr = BigInteger256::deserialize_uncompressed_unchecked(fr_bytes.as_slice())
300 .map_err(|_| AltBn128Error::InvalidInputData)?;
301
302 let result_point: G1 = p.mul_bigint(fr).into();
303
304 let mut result_point_data = [0u8; ALT_BN128_MULTIPLICATION_OUTPUT_LEN];
305
306 result_point
307 .x
308 .serialize_with_mode(&mut result_point_data[..32], Compress::No)
309 .map_err(|_| AltBn128Error::InvalidInputData)?;
310 result_point
311 .y
312 .serialize_with_mode(&mut result_point_data[32..], Compress::No)
313 .map_err(|_| AltBn128Error::InvalidInputData)?;
314
315 Ok(convert_endianness_64(
316 &result_point_data[..ALT_BN128_MULTIPLICATION_OUTPUT_LEN],
317 ))
318 }
319
320 pub fn alt_bn128_pairing(input: &[u8]) -> Result<Vec<u8>, AltBn128Error> {
321 if input
322 .len()
323 .checked_rem(consts::ALT_BN128_PAIRING_ELEMENT_LEN)
324 .is_none()
325 {
326 return Err(AltBn128Error::InvalidInputData);
327 }
328
329 let ele_len = input.len().saturating_div(ALT_BN128_PAIRING_ELEMENT_LEN);
330
331 let mut vec_pairs: Vec<(G1, G2)> = Vec::with_capacity(ele_len);
332 for chunk in input.chunks(ALT_BN128_PAIRING_ELEMENT_LEN).take(ele_len) {
333 let (p_bytes, q_bytes) = chunk.split_at(G1_POINT_SIZE);
334
335 let g1 = PodG1::from_be_bytes(p_bytes)?.try_into()?;
336 let g2 = PodG2::from_be_bytes(q_bytes)?.try_into()?;
337
338 vec_pairs.push((g1, g2));
339 }
340
341 let mut result = BigInteger256::from(0u64);
342 let res = <Bn<Config> as Pairing>::multi_pairing(
343 vec_pairs.iter().map(|pair| pair.0),
344 vec_pairs.iter().map(|pair| pair.1),
345 );
346
347 if res.0 == ark_bn254::Fq12::one() {
348 result = BigInteger256::from(1u64);
349 }
350
351 let output = result.to_bytes_be();
352 Ok(output)
353 }
354
355 fn convert_endianness_64(bytes: &[u8]) -> Vec<u8> {
356 bytes
357 .chunks(32)
358 .flat_map(|b| b.iter().copied().rev().collect::<Vec<u8>>())
359 .collect::<Vec<u8>>()
360 }
361
362 fn reverse_copy(source: &[u8], destination: &mut [u8]) -> Result<(), AltBn128Error> {
364 if source.len() != destination.len() {
365 return Err(AltBn128Error::SliceOutOfBounds);
366 }
367 for (source_index, destination_index) in source.iter().rev().zip(destination.iter_mut()) {
368 *destination_index = *source_index;
369 }
370 Ok(())
371 }
372}
373
374#[cfg(target_os = "trezoa")]
375mod target_arch {
376 use {super::*, trezoa_define_syscall::definitions as syscalls};
377
378 pub fn alt_bn128_addition(input: &[u8]) -> Result<Vec<u8>, AltBn128Error> {
379 if input.len() > ALT_BN128_ADDITION_INPUT_LEN {
380 return Err(AltBn128Error::InvalidInputData);
381 }
382 let mut result_buffer = [0; ALT_BN128_ADDITION_OUTPUT_LEN];
383 let result = unsafe {
384 syscalls::trz_alt_bn128_group_op(
385 ALT_BN128_ADD,
386 input as *const _ as *const u8,
387 input.len() as u64,
388 &mut result_buffer as *mut _ as *mut u8,
389 )
390 };
391
392 match result {
393 0 => Ok(result_buffer.to_vec()),
394 _ => Err(AltBn128Error::UnexpectedError),
395 }
396 }
397
398 pub fn alt_bn128_multiplication(input: &[u8]) -> Result<Vec<u8>, AltBn128Error> {
399 if input.len() > ALT_BN128_MULTIPLICATION_INPUT_LEN {
400 return Err(AltBn128Error::InvalidInputData);
401 }
402 let mut result_buffer = [0u8; ALT_BN128_POINT_SIZE];
403 let result = unsafe {
404 syscalls::trz_alt_bn128_group_op(
405 ALT_BN128_MUL,
406 input as *const _ as *const u8,
407 input.len() as u64,
408 &mut result_buffer as *mut _ as *mut u8,
409 )
410 };
411
412 match result {
413 0 => Ok(result_buffer.to_vec()),
414 _ => Err(AltBn128Error::UnexpectedError),
415 }
416 }
417
418 pub fn alt_bn128_pairing(input: &[u8]) -> Result<Vec<u8>, AltBn128Error> {
419 if input
420 .len()
421 .checked_rem(consts::ALT_BN128_PAIRING_ELEMENT_LEN)
422 .is_none()
423 {
424 return Err(AltBn128Error::InvalidInputData);
425 }
426 let mut result_buffer = [0u8; 32];
427 let result = unsafe {
428 syscalls::trz_alt_bn128_group_op(
429 ALT_BN128_PAIRING,
430 input as *const _ as *const u8,
431 input.len() as u64,
432 &mut result_buffer as *mut _ as *mut u8,
433 )
434 };
435
436 match result {
437 0 => Ok(result_buffer.to_vec()),
438 _ => Err(AltBn128Error::UnexpectedError),
439 }
440 }
441}
442
443#[cfg(test)]
444mod tests {
445 use {
446 crate::{prelude::*, PodG1},
447 ark_bn254::g1::G1Affine,
448 ark_ec::AffineRepr,
449 ark_serialize::{CanonicalSerialize, Compress},
450 };
451
452 #[test]
453 fn zero_serialization_test() {
454 let zero = G1Affine::zero();
455 let mut result_point_data = [0u8; 64];
456 zero.x
457 .serialize_with_mode(&mut result_point_data[..32], Compress::No)
458 .map_err(|_| AltBn128Error::InvalidInputData)
459 .unwrap();
460 zero.y
461 .serialize_with_mode(&mut result_point_data[32..], Compress::No)
462 .map_err(|_| AltBn128Error::InvalidInputData)
463 .unwrap();
464 assert_eq!(result_point_data, [0u8; 64]);
465
466 let p: G1Affine = PodG1(result_point_data[..64].try_into().unwrap())
467 .try_into()
468 .unwrap();
469 assert_eq!(p, zero);
470 }
471
472 #[test]
473 fn alt_bn128_pairing_invalid_length() {
474 use ark_ff::{BigInteger, BigInteger256};
475
476 let input = [0; 193];
477 let result = alt_bn128_pairing(&input);
478 assert!(result.is_ok());
479 let expected = BigInteger256::from(1u64).to_bytes_be();
480 assert_eq!(result.unwrap(), expected);
481 }
482}