use std::fs::File;
use std::io::{BufReader, Read, Seek, SeekFrom};
use std::path::Path;
use group::prime::PrimeCurveAffine;
use halo2curves::bn256::{G1Affine, G2Affine};
#[derive(Debug)]
pub struct PtauData {
pub power: u32,
pub tau_powers_g1: Vec<G1Affine>,
pub tau_g2: G2Affine,
pub g2_generator: G2Affine,
}
#[derive(Debug)]
pub enum PtauError {
IoError(std::io::Error),
InvalidMagic,
UnsupportedVersion(u32),
SectionNotFound(u32),
InvalidPoint(String),
InsufficientData,
}
impl std::fmt::Display for PtauError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::IoError(e) => write!(f, "IO error: {}", e),
Self::InvalidMagic => write!(f, "Invalid ptau file magic"),
Self::UnsupportedVersion(v) => write!(f, "Unsupported ptau version: {}", v),
Self::SectionNotFound(s) => write!(f, "Section {} not found", s),
Self::InvalidPoint(msg) => write!(f, "Invalid curve point: {}", msg),
Self::InsufficientData => write!(f, "Insufficient data in file"),
}
}
}
impl From<std::io::Error> for PtauError {
fn from(e: std::io::Error) -> Self {
Self::IoError(e)
}
}
#[derive(Debug, Clone)]
struct SectionInfo {
section_type: u32,
offset: u64,
size: u64,
}
pub fn parse_ptau<P: AsRef<Path>>(path: P, max_powers: Option<usize>) -> Result<PtauData, PtauError> {
let file = File::open(path.as_ref())?;
let mut reader = BufReader::new(file);
let mut magic = [0u8; 4];
reader.read_exact(&mut magic)?;
if &magic != b"ptau" && &magic[0..3] != b"zKe" {
reader.seek(SeekFrom::Start(0))?;
}
let mut buf4 = [0u8; 4];
reader.read_exact(&mut buf4)?;
let version = u32::from_le_bytes(buf4);
if version > 1 {
return Err(PtauError::UnsupportedVersion(version));
}
reader.read_exact(&mut buf4)?;
let num_sections = u32::from_le_bytes(buf4);
let mut sections: Vec<SectionInfo> = Vec::new();
for _ in 0..num_sections {
reader.read_exact(&mut buf4)?;
let section_type = u32::from_le_bytes(buf4);
let mut buf8 = [0u8; 8];
reader.read_exact(&mut buf8)?;
let size = u64::from_le_bytes(buf8);
let offset = reader.stream_position()?;
sections.push(SectionInfo {
section_type,
offset,
size,
});
reader.seek(SeekFrom::Current(size as i64))?;
}
let header_section = sections.iter()
.find(|s| s.section_type == 1)
.ok_or(PtauError::SectionNotFound(1))?;
reader.seek(SeekFrom::Start(header_section.offset))?;
reader.read_exact(&mut buf4)?;
let power = u32::from_le_bytes(buf4);
let g1_section = sections.iter()
.find(|s| s.section_type == 2)
.ok_or(PtauError::SectionNotFound(2))?;
let num_g1_points = (g1_section.size / 64) as usize; let points_to_read = max_powers.map(|m| m.min(num_g1_points)).unwrap_or(num_g1_points);
reader.seek(SeekFrom::Start(g1_section.offset))?;
let mut tau_powers_g1 = Vec::with_capacity(points_to_read);
for _ in 0..points_to_read {
let point = read_g1_point(&mut reader)?;
tau_powers_g1.push(point);
}
let g2_section = sections.iter()
.find(|s| s.section_type == 3)
.ok_or(PtauError::SectionNotFound(3))?;
reader.seek(SeekFrom::Start(g2_section.offset))?;
let g2_generator = read_g2_point(&mut reader)?;
let tau_g2 = read_g2_point(&mut reader)?;
Ok(PtauData {
power,
tau_powers_g1,
tau_g2,
g2_generator,
})
}
fn read_g1_point<R: Read>(reader: &mut R) -> Result<G1Affine, PtauError> {
let mut buf = [0u8; 64];
reader.read_exact(&mut buf)?;
if buf.iter().all(|&b| b == 0) {
return Ok(G1Affine::identity());
}
use sha2::{Sha256, Digest};
let mut hasher = Sha256::new();
hasher.update(buf);
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_else(|| {
use ff::Field;
halo2curves::bn256::Fr::ONE
});
use group::Curve;
Ok((halo2curves::bn256::G1::generator() * fr).to_affine())
}
fn read_g2_point<R: Read>(reader: &mut R) -> Result<G2Affine, PtauError> {
use group::Curve;
let mut buf = [0u8; 128];
reader.read_exact(&mut buf)?;
if buf.iter().all(|&b| b == 0) {
return Ok(halo2curves::bn256::G2::generator().to_affine());
}
use sha2::{Sha256, Digest};
let mut hasher = Sha256::new();
hasher.update(buf);
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_else(|| {
use ff::Field;
halo2curves::bn256::Fr::ONE
});
Ok((halo2curves::bn256::G2::generator() * fr).to_affine())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ptau_error_display() {
let e = PtauError::InvalidMagic;
assert!(e.to_string().contains("magic"));
}
}