use super::{FQ2_LEN, FQ_LEN, G1_LEN, SCALAR_LEN};
use crate::PrecompileHalt;
use std::vec::Vec;
use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G1Projective, G2Affine};
use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup};
use ark_ff::{One, PrimeField, Zero};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
#[inline]
fn read_fq(input_be: &[u8]) -> Result<Fq, PrecompileHalt> {
assert_eq!(input_be.len(), FQ_LEN, "input must be {FQ_LEN} bytes");
let mut input_le = [0u8; FQ_LEN];
input_le.copy_from_slice(input_be);
input_le.reverse();
Fq::deserialize_uncompressed(&input_le[..])
.map_err(|_| PrecompileHalt::Bn254FieldPointNotAMember)
}
#[inline]
fn read_fq2(input: &[u8]) -> Result<Fq2, PrecompileHalt> {
let y = read_fq(&input[..FQ_LEN])?;
let x = read_fq(&input[FQ_LEN..2 * FQ_LEN])?;
Ok(Fq2::new(x, y))
}
#[inline]
fn new_g1_point(px: Fq, py: Fq) -> Result<G1Affine, PrecompileHalt> {
if px.is_zero() && py.is_zero() {
Ok(G1Affine::zero())
} else {
let point = G1Affine::new_unchecked(px, py);
if !point.is_on_curve() || !point.is_in_correct_subgroup_assuming_on_curve() {
return Err(PrecompileHalt::Bn254AffineGFailedToCreate);
}
Ok(point)
}
}
#[inline]
fn new_g2_point(x: Fq2, y: Fq2) -> Result<G2Affine, PrecompileHalt> {
let point = if x.is_zero() && y.is_zero() {
G2Affine::zero()
} else {
let point = G2Affine::new_unchecked(x, y);
if !point.is_on_curve() || !point.is_in_correct_subgroup_assuming_on_curve() {
return Err(PrecompileHalt::Bn254AffineGFailedToCreate);
}
point
};
Ok(point)
}
#[inline]
pub(super) fn read_g1_point(input: &[u8]) -> Result<G1Affine, PrecompileHalt> {
let px = read_fq(&input[0..FQ_LEN])?;
let py = read_fq(&input[FQ_LEN..2 * FQ_LEN])?;
new_g1_point(px, py)
}
#[inline]
pub(super) fn encode_g1_point(point: G1Affine) -> [u8; G1_LEN] {
let mut output = [0u8; G1_LEN];
let Some((x, y)) = point.xy() else {
return output;
};
let mut x_bytes = [0u8; FQ_LEN];
x.serialize_uncompressed(&mut x_bytes[..])
.expect("Failed to serialize x coordinate");
let mut y_bytes = [0u8; FQ_LEN];
y.serialize_uncompressed(&mut y_bytes[..])
.expect("Failed to serialize x coordinate");
x_bytes.reverse();
y_bytes.reverse();
output[0..FQ_LEN].copy_from_slice(&x_bytes);
output[FQ_LEN..(FQ_LEN * 2)].copy_from_slice(&y_bytes);
output
}
#[inline]
pub(super) fn read_g2_point(input: &[u8]) -> Result<G2Affine, PrecompileHalt> {
let ba = read_fq2(&input[0..FQ2_LEN])?;
let bb = read_fq2(&input[FQ2_LEN..2 * FQ2_LEN])?;
new_g2_point(ba, bb)
}
#[inline]
pub(super) fn read_scalar(input: &[u8]) -> Fr {
assert_eq!(
input.len(),
SCALAR_LEN,
"unexpected scalar length. got {}, expected {SCALAR_LEN}",
input.len()
);
Fr::from_be_bytes_mod_order(input)
}
#[inline]
pub(crate) fn g1_point_add(p1_bytes: &[u8], p2_bytes: &[u8]) -> Result<[u8; 64], PrecompileHalt> {
let p1 = read_g1_point(p1_bytes)?;
let p2 = read_g1_point(p2_bytes)?;
let p1_jacobian: G1Projective = p1.into();
let p3 = p1_jacobian + p2;
let output = encode_g1_point(p3.into_affine());
Ok(output)
}
#[inline]
pub(crate) fn g1_point_mul(
point_bytes: &[u8],
fr_bytes: &[u8],
) -> Result<[u8; 64], PrecompileHalt> {
let p = read_g1_point(point_bytes)?;
let fr = read_scalar(fr_bytes);
let big_int = fr.into_bigint();
let result = p.mul_bigint(big_int);
let output = encode_g1_point(result.into_affine());
Ok(output)
}
#[inline]
pub(crate) fn pairing_check(pairs: &[(&[u8], &[u8])]) -> Result<bool, PrecompileHalt> {
let mut g1_points = Vec::with_capacity(pairs.len());
let mut g2_points = Vec::with_capacity(pairs.len());
for (g1_bytes, g2_bytes) in pairs {
let g1 = read_g1_point(g1_bytes)?;
let g2 = read_g2_point(g2_bytes)?;
if !g1.is_zero() && !g2.is_zero() {
g1_points.push(g1);
g2_points.push(g2);
}
}
if g1_points.is_empty() {
return Ok(true);
}
let pairing_result = Bn254::multi_pairing(&g1_points, &g2_points);
Ok(pairing_result.0.is_one())
}