use super::{FQ2_LEN, FQ_LEN, G1_LEN, SCALAR_LEN};
use crate::PrecompileHalt;
use bn::{AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2};
use std::vec::Vec;
#[inline]
fn read_fq(input: &[u8]) -> Result<Fq, PrecompileHalt> {
Fq::from_slice(&input[..FQ_LEN]).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<G1, PrecompileHalt> {
if px == Fq::zero() && py == Fq::zero() {
Ok(G1::zero())
} else {
AffineG1::new(px, py)
.map(Into::into)
.map_err(|_| PrecompileHalt::Bn254AffineGFailedToCreate)
}
}
#[inline]
fn new_g2_point(x: Fq2, y: Fq2) -> Result<G2, PrecompileHalt> {
let point = if x.is_zero() && y.is_zero() {
G2::zero()
} else {
G2::from(AffineG2::new(x, y).map_err(|_| PrecompileHalt::Bn254AffineGFailedToCreate)?)
};
Ok(point)
}
#[inline]
pub(super) fn read_g1_point(input: &[u8]) -> Result<G1, 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: G1) -> [u8; G1_LEN] {
let mut output = [0u8; G1_LEN];
if let Some(point_affine) = AffineG1::from_jacobian(point) {
point_affine
.x()
.to_big_endian(&mut output[..FQ_LEN])
.unwrap();
point_affine
.y()
.to_big_endian(&mut output[FQ_LEN..])
.unwrap();
}
output
}
#[inline]
pub(super) fn read_g2_point(input: &[u8]) -> Result<G2, 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]) -> bn::Fr {
assert_eq!(
input.len(),
SCALAR_LEN,
"unexpected scalar length. got {}, expected {SCALAR_LEN}",
input.len()
);
bn::Fr::from_slice(input).unwrap()
}
#[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 result = p1 + p2;
Ok(encode_g1_point(result))
}
#[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 result = p * fr;
Ok(encode_g1_point(result))
}
#[inline]
pub(crate) fn pairing_check(pairs: &[(&[u8], &[u8])]) -> Result<bool, PrecompileHalt> {
let mut parsed_pairs = 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() {
parsed_pairs.push((g1, g2));
}
}
if parsed_pairs.is_empty() {
return Ok(true);
}
Ok(bn::pairing_batch(&parsed_pairs) == Gt::one())
}