use std::fs::File;
use std::collections::BTreeMap;
use std::io::{Read, Seek, SeekFrom};
use byteorder::{LittleEndian, ReadBytesExt};
use ark_bn254::{G1Affine, G2Affine, Fq, Fq2};
use ark_ff::fields::PrimeField;
use ark_ff::FromBytes;
use ark_ff::biginteger::BigInteger256;
#[derive(Debug, PartialEq)]
pub enum Error {
InvalidMagicString,
InvalidVersion,
InvalidPrimeOrder,
InvalidNumSections,
InvalidNumG1Points,
InvalidNumG2Points,
InvalidG1Point,
InvalidG2Point,
}
pub fn read(
ptau_file: &str,
num_g1_points: usize,
num_g2_points: usize,
) -> Result<(Vec<G1Affine>, Vec<G2Affine>), Error> {
let mut f = File::open(ptau_file).unwrap();
let mut magic_string_buf = [0u8; 4];
let _ = f.read_exact(&mut magic_string_buf);
if std::str::from_utf8(&magic_string_buf).unwrap() != "ptau" {
return Err(Error::InvalidMagicString);
}
let version = f.read_u32::<LittleEndian>().unwrap();
if version != 1 {
return Err(Error::InvalidVersion);
}
let num_sections = f.read_u32::<LittleEndian>().unwrap();
if num_sections != 11 {
return Err(Error::InvalidNumSections);
}
let mut sections = BTreeMap::<usize, u64>::new();
for _ in 0..num_sections {
let num = f.read_u32::<LittleEndian>().unwrap();
let size = f.read_i64::<LittleEndian>().unwrap();
let pos = f.stream_position().unwrap();
let _ = f.seek(SeekFrom::Current(size));
sections.insert(num as usize, pos);
}
let _ = f.seek(SeekFrom::Start(sections[&1]));
let n8 = f.read_u32::<LittleEndian>().unwrap();
let mut q_buf = vec![0u8; n8 as usize];
let _ = f.read_exact(&mut q_buf);
let mut num_zeroes = 0;
for b in q_buf.iter() {
if *b == 0u8 {
num_zeroes += 1;
}
}
if num_zeroes == 32 {
return Err(Error::InvalidPrimeOrder);
}
let q = Fq::from_le_bytes_mod_order(&q_buf);
if q != Fq::from(0) {
return Err(Error::InvalidPrimeOrder);
}
let power = f.read_u32::<LittleEndian>().unwrap();
let _ceremony_power = f.read_u32::<LittleEndian>().unwrap();
let max_g2_points = 1 << power;
let max_g1_points = max_g2_points * 2 - 1;
if num_g1_points > max_g1_points {
return Err(Error::InvalidNumG1Points)
}
if num_g2_points > max_g2_points {
return Err(Error::InvalidNumG2Points);
}
let mut g1_points = Vec::<G1Affine>::with_capacity(num_g1_points);
let _ = f.seek(SeekFrom::Start(sections[&2]));
for _ in 0..num_g1_points {
let mut x_buf = [0u8; 32];
let mut y_buf = [0u8; 32];
let _ = f.read_exact(&mut x_buf);
let _ = f.read_exact(&mut y_buf);
let x_bigint = BigInteger256::read(x_buf.as_slice()).unwrap();
let y_bigint = BigInteger256::read(y_buf.as_slice()).unwrap();
let x = Fq::new(x_bigint);
let y = Fq::new(y_bigint);
let g1 = G1Affine::new(x, y, false);
if !g1.is_on_curve() {
return Err(Error::InvalidG1Point);
}
g1_points.push(g1);
}
let _ = f.seek(SeekFrom::Start(sections[&3]));
let mut g2_points = Vec::<G2Affine>::with_capacity(num_g2_points);
for _ in 0..num_g2_points {
let mut x0_buf = [0u8; 32];
let mut x1_buf = [0u8; 32];
let mut y0_buf = [0u8; 32];
let mut y1_buf = [0u8; 32];
let _ = f.read_exact(&mut x0_buf);
let _ = f.read_exact(&mut x1_buf);
let _ = f.read_exact(&mut y0_buf);
let _ = f.read_exact(&mut y1_buf);
let x0_bigint = BigInteger256::read(x0_buf.as_slice()).unwrap();
let x1_bigint = BigInteger256::read(x1_buf.as_slice()).unwrap();
let y0_bigint = BigInteger256::read(y0_buf.as_slice()).unwrap();
let y1_bigint = BigInteger256::read(y1_buf.as_slice()).unwrap();
let x0 = Fq::new(x0_bigint);
let x1 = Fq::new(x1_bigint);
let y0 = Fq::new(y0_bigint);
let y1 = Fq::new(y1_bigint);
let x = Fq2::new(x0, x1);
let y = Fq2::new(y0, y1);
let g2 = G2Affine::new(x, y, false);
if !g2.is_on_curve() {
return Err(Error::InvalidG2Point);
}
g2_points.push(g2);
}
Ok((g1_points, g2_points))
}
#[cfg(test)]
mod tests {
use super::Error;
use ark_bn254::{G1Affine, G2Affine, Fq, Fq2};
use ark_ff::FromBytes;
fn hex_to_fq(val: &str) -> Fq {
assert_eq!(val.len(), 64);
let bytes_vec = hex::decode(val).unwrap();
let bytes_slice: &[u8] = bytes_vec.as_slice();
Fq::read(bytes_slice).unwrap()
}
#[test]
pub fn test_read() {
let num_g1_points = 511;
let num_g2_points = 256;
let ptau_file = "8.ptau";
let (g1_points, g2_points) = super::read(ptau_file, num_g1_points, num_g2_points).unwrap();
assert_eq!(g1_points.len(), num_g1_points);
assert_eq!(g2_points.len(), num_g2_points);
let point_g1_0_x = Fq::from(1);
let point_g1_0_y = Fq::from(2);
let point_g1_1_x = hex_to_fq("cf51b65ad54479e394aef90d4b0ec4e4a1a16bbb6865614a4b5b8a0959fdd32d");
let point_g1_1_y = hex_to_fq("f04eb3f6ef601be3d326c237feed3351de969ce6d634905a4304ba25350c6825");
assert_eq!(g1_points[0], G1Affine::new(point_g1_0_x, point_g1_0_y, false));
assert_eq!(g1_points[1], G1Affine::new(point_g1_1_x, point_g1_1_y, false));
let point_g2_0_x0 = hex_to_fq("edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018");
let point_g2_0_x1 = hex_to_fq("c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19");
let point_g2_0_y0 = hex_to_fq("aa7dfa6601cce64c7bd3430c69e7d1e38f40cb8d8071ab4aeb6d8cdba55ec812");
let point_g2_0_y1 = hex_to_fq("5b9722d1dcdaac55f38eb37033314bbc95330c69ad999eec75f05f58d0890609");
let point_g2_0 = G2Affine::new(
Fq2::new(point_g2_0_x0, point_g2_0_x1),
Fq2::new(point_g2_0_y0, point_g2_0_y1),
false
);
assert_eq!(g2_points[0], point_g2_0);
}
#[test]
pub fn test_read_too_few_g1() {
let num_g1_points = 512;
let num_g2_points = 256;
let ptau_file = "8.ptau";
let r = super::read(ptau_file, num_g1_points, num_g2_points);
assert_eq!(r.err().unwrap(), Error::InvalidNumG1Points);
}
#[test]
pub fn test_read_too_few_g2() {
let num_g1_points = 511;
let num_g2_points = 257;
let ptau_file = "8.ptau";
let r = super::read(ptau_file, num_g1_points, num_g2_points);
assert_eq!(r.err().unwrap(), Error::InvalidNumG2Points);
}
}