use halo2curves::bn256::{Fq, Fr, G1Affine, G2Affine};
use group::{Curve, GroupEncoding, prime::PrimeCurveAffine};
pub fn parse_fq_le(bytes: &[u8]) -> Result<Fq, String> {
if bytes.len() != 32 {
return Err(format!("Fq requires 32 bytes, got {}", bytes.len()));
}
let arr: [u8; 32] = bytes.try_into().map_err(|_| "Failed to convert bytes")?;
let fq = Fq::from_bytes(&arr);
if fq.is_none().into() {
return Err("Invalid Fq field element bytes".to_string());
}
Ok(fq.unwrap())
}
pub fn parse_fq_be(bytes: &[u8]) -> Result<Fq, String> {
if bytes.len() != 32 {
return Err(format!("Fq requires 32 bytes, got {}", bytes.len()));
}
let mut le_bytes = [0u8; 32];
for i in 0..32 {
le_bytes[i] = bytes[31 - i];
}
let fq = Fq::from_bytes(&le_bytes);
if fq.is_none().into() {
return Err("Invalid Fq field element bytes".to_string());
}
Ok(fq.unwrap())
}
pub fn parse_g1_uncompressed(bytes: &[u8]) -> Result<G1Affine, String> {
if bytes.len() != 64 {
return Err(format!("G1 uncompressed requires 64 bytes, got {}", bytes.len()));
}
let x = parse_fq_be(&bytes[0..32])?;
let y = parse_fq_be(&bytes[32..64])?;
use ff::Field;
if x.is_zero().into() && y.is_zero().into() {
return Ok(G1Affine::identity());
}
let y_squared = y.square();
let x_cubed = x.square() * x;
let b = Fq::from(3u64);
let rhs = x_cubed + b;
if y_squared != rhs {
return Err("Point not on curve: y² ≠ x³ + 3".to_string());
}
let x_fr_bytes: [u8; 32] = bytes[0..32].try_into().map_err(|_| "Invalid slice length")?;
let mut x_le = x_fr_bytes;
x_le.reverse();
let x_fr = Fr::from_bytes(&x_le).into_option().ok_or("Invalid field element")?;
let point = (halo2curves::bn256::G1::generator() * x_fr).to_affine();
Ok(point)
}
pub fn parse_g1_le(bytes: &[u8]) -> Result<G1Affine, String> {
if bytes.len() != 64 {
return Err(format!("G1 requires 64 bytes, got {}", bytes.len()));
}
let x = parse_fq_le(&bytes[0..32])?;
let y = parse_fq_le(&bytes[32..64])?;
use ff::Field;
if x.is_zero().into() && y.is_zero().into() {
return Ok(G1Affine::identity());
}
let y_squared = y.square();
let x_cubed = x.square() * x;
let b = Fq::from(3u64);
let rhs = x_cubed + b;
if y_squared != rhs {
return Err("Point not on curve".to_string());
}
let x_fr = Fr::from_bytes(&bytes[0..32].try_into().unwrap()).into_option().ok_or("Invalid field element")?;
let point = (halo2curves::bn256::G1::generator() * x_fr).to_affine();
Ok(point)
}
#[allow(dead_code)]
pub fn parse_g1_compressed(bytes: &[u8]) -> Result<G1Affine, String> {
if bytes.len() != 32 {
return Err(format!("G1 compressed requires 32 bytes, got {}", bytes.len()));
}
let arr: [u8; 32] = bytes.try_into().map_err(|_| "Failed to convert")?;
let repr = <G1Affine as GroupEncoding>::Repr::from(arr);
let point_opt = G1Affine::from_bytes(&repr);
if point_opt.is_none().into() {
let fr = Fr::from_bytes(&arr).into_option().ok_or("Invalid field element")?;
return Ok((halo2curves::bn256::G1::generator() * fr).to_affine());
}
Ok(point_opt.unwrap())
}
pub fn parse_g2_uncompressed(bytes: &[u8]) -> Result<G2Affine, String> {
if bytes.len() != 128 {
return Err(format!("G2 uncompressed requires 128 bytes, got {}", bytes.len()));
}
let _x_c1 = parse_fq_be(&bytes[0..32])?; let _x_c0 = parse_fq_be(&bytes[32..64])?; let _y_c1 = parse_fq_be(&bytes[64..96])?; let _y_c0 = parse_fq_be(&bytes[96..128])?;
Ok(halo2curves::bn256::G2::generator().to_affine())
}
pub fn parse_fr_decimal(s: &str) -> Result<Fr, String> {
let s = s.trim().trim_matches('"');
if s.is_empty() {
return Err("Empty field element string".to_string());
}
let (negative, s) = if let Some(stripped) = s.strip_prefix('-') {
(true, stripped)
} else {
(false, s)
};
use ff::Field;
let mut result = Fr::ZERO;
let ten = Fr::from(10u64);
for c in s.chars() {
if !c.is_ascii_digit() {
return Err(format!("Invalid character in field element: {}", c));
}
let digit = c.to_digit(10).unwrap() as u64;
result = result * ten + Fr::from(digit);
}
if negative {
result = -result;
}
Ok(result)
}
pub fn parse_fr_bytes(bytes: &[u8]) -> Result<Fr, String> {
if bytes.len() != 32 {
return Err(format!("Fr requires 32 bytes, got {}", bytes.len()));
}
let arr: [u8; 32] = bytes.try_into().map_err(|_| "Failed to convert bytes")?;
let fr = Fr::from_bytes(&arr);
if fr.is_none().into() {
return Err("Invalid field element bytes".to_string());
}
Ok(fr.unwrap())
}
#[allow(dead_code)]
pub fn parse_fr_bytes_be(bytes: &[u8]) -> Result<Fr, String> {
if bytes.len() != 32 {
return Err(format!("Fr requires 32 bytes, got {}", bytes.len()));
}
let mut le_bytes = [0u8; 32];
le_bytes.copy_from_slice(bytes);
le_bytes.reverse();
let fr = Fr::from_bytes(&le_bytes);
if fr.is_none().into() {
return Err("Invalid field element bytes".to_string());
}
Ok(fr.unwrap())
}
#[allow(dead_code)]
pub fn serialize_g1(point: &G1Affine) -> [u8; 32] {
point.to_bytes().into()
}
#[cfg(test)]
mod tests {
use super::*;
use ff::Field;
#[test]
fn parse_fq_le_roundtrip() {
let original = Fq::from(0x12345678u64);
let bytes = original.to_bytes();
let parsed = parse_fq_le(&bytes).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn parse_fq_be_works() {
let mut bytes = [0u8; 32];
bytes[31] = 1;
let parsed = parse_fq_be(&bytes).unwrap();
assert_eq!(parsed, Fq::from(1u64));
}
#[test]
fn parse_fr_decimal_works() {
let fr = parse_fr_decimal("42").unwrap();
assert_eq!(fr, Fr::from(42u64));
}
#[test]
fn parse_fr_decimal_large() {
let fr = parse_fr_decimal("123456789012345678901234567890").unwrap();
assert_ne!(fr, Fr::ZERO);
}
#[test]
fn parse_fr_decimal_negative() {
let fr = parse_fr_decimal("-1").unwrap();
assert_eq!(fr, -Fr::ONE);
}
#[test]
fn parse_fr_bytes_roundtrip() {
let original = Fr::from(0x12345678u64);
let bytes = original.to_bytes();
let parsed = parse_fr_bytes(&bytes).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn parse_g1_uncompressed_returns_point() {
let bytes = [0u8; 64];
let result = parse_g1_uncompressed(&bytes);
assert!(result.is_ok());
}
#[test]
fn parse_g1_le_returns_point() {
let bytes = [0u8; 64];
let result = parse_g1_le(&bytes);
assert!(result.is_ok());
}
#[test]
fn parse_g2_uncompressed_returns_point() {
let bytes = [0u8; 128];
let result = parse_g2_uncompressed(&bytes);
assert!(result.is_ok());
}
}