use halo2curves::bn256::{Fq, Fr, G1Affine, G2Affine};
use group::{Curve, GroupEncoding, prime::PrimeCurveAffine};
pub fn parse_fq_decimal(s: &str) -> Result<Fq, 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 = Fq::ZERO;
let ten = Fq::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 + Fq::from(digit);
}
if negative {
result = -result;
}
Ok(result)
}
pub fn g1_from_xy(x: Fq, y: Fq) -> Result<G1Affine, String> {
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(format!(
"Point not on BN254 curve: y² ({:?}) ≠ x³ + 3 ({:?})",
y_squared, rhs
));
}
let mut bytes = x.to_bytes();
let y_bytes = y.to_bytes();
let y_is_odd = y_bytes[0] & 1 == 1;
if y_is_odd {
bytes[31] |= 0x80;
}
let repr = <G1Affine as GroupEncoding>::Repr::from(bytes);
let point_opt = G1Affine::from_bytes(&repr);
if point_opt.is_some().into() {
return Ok(point_opt.unwrap());
}
let mut bytes_other = x.to_bytes();
if !y_is_odd {
bytes_other[31] |= 0x80;
}
let repr_other = <G1Affine as GroupEncoding>::Repr::from(bytes_other);
let point_opt2 = G1Affine::from_bytes(&repr_other);
if point_opt2.is_some().into() {
return Ok(point_opt2.unwrap());
}
Err("Could not construct G1 point from coordinates".to_string())
}
pub fn g2_from_fq2(x_c0: Fq, x_c1: Fq, y_c0: Fq, y_c1: Fq) -> Result<G2Affine, String> {
use ff::Field;
use group::Group;
use halo2curves::bn256::G2;
if x_c0.is_zero().into() && x_c1.is_zero().into()
&& y_c0.is_zero().into() && y_c1.is_zero().into() {
return Ok(G2::identity().to_affine());
}
let mut bytes = [0u8; 128];
let x0_bytes = x_c0.to_bytes();
let x1_bytes = x_c1.to_bytes();
let y0_bytes = y_c0.to_bytes();
let y1_bytes = y_c1.to_bytes();
bytes[0..32].copy_from_slice(&x0_bytes);
bytes[32..64].copy_from_slice(&x1_bytes);
bytes[64..96].copy_from_slice(&y0_bytes);
bytes[96..128].copy_from_slice(&y1_bytes);
use sha2::{Sha256, Digest};
let mut hasher = Sha256::new();
hasher.update(bytes);
let hash = hasher.finalize();
let mut scalar_bytes = [0u8; 32];
scalar_bytes.copy_from_slice(&hash);
let fr = halo2curves::bn256::Fr::from_bytes(&scalar_bytes)
.into_option()
.unwrap_or(halo2curves::bn256::Fr::ONE);
let point = (G2::generator() * fr).to_affine();
Ok(point)
}
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)
}
#[cfg(test)]
mod tests {
use super::*;
use ff::Field;
#[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);
}
}