1use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
90use ff::PrimeField;
91use halo2curves::CurveAffine;
92use num_bigint::BigUint;
93use std::{
94 fs::File,
95 io::{self, Read, Seek, SeekFrom, Write},
96 path::Path,
97 str::{from_utf8, Utf8Error},
98};
99
100#[derive(thiserror::Error, Debug)]
102pub enum PtauFileError {
103 #[error("Invalid magic string")]
105 InvalidHead,
106
107 #[error("Unsupported version")]
109 UnsupportedVersion(u32),
110
111 #[error("Invalid number of sections")]
113 InvalidNumSections(u32),
114
115 #[error("Invalid base prime")]
117 InvalidPrime(BigUint),
118
119 #[error("Insufficient power for G1")]
121 InsufficientPowerForG1 {
122 power: u32,
124 required: usize,
126 },
127
128 #[error("Insufficient power for G2")]
130 InsufficientPowerForG2 {
131 power: u32,
133 required: usize,
135 },
136
137 #[error(transparent)]
139 IoError(#[from] io::Error),
140 #[error(transparent)]
142 Utf8Error(#[from] Utf8Error),
143}
144
145#[derive(Debug)]
146struct MetaData {
147 pos_header: u64,
148 pos_tau_g1: u64,
149 pos_tau_g2: u64,
150}
151
152pub const PTAU_VERSION: u32 = 1;
154pub const NUM_SECTIONS_FULL: u32 = 11;
156pub const NUM_SECTIONS_PRUNED: u32 = 3;
158
159pub const MAX_PPOT_POWER: u32 = 28;
161
162fn write_header<Base: PrimeField>(
163 writer: &mut impl Write,
164 power: u32,
165) -> Result<(), PtauFileError> {
166 const N8: usize = 32;
167
168 writer.write_all(b"ptau")?;
169
170 writer.write_u32::<LittleEndian>(PTAU_VERSION)?;
171 writer.write_u32::<LittleEndian>(NUM_SECTIONS_FULL)?;
172
173 writer.write_u32::<LittleEndian>(1)?;
175 writer.write_i64::<LittleEndian>(4 + N8 as i64 + 4)?;
176
177 writer.write_u32::<LittleEndian>(N8 as u32)?;
178
179 let modulus = BigUint::parse_bytes(&Base::MODULUS.as_bytes()[2..], 16).unwrap();
180 let mut bytes = [0u8; N8];
181 bytes.copy_from_slice(&modulus.to_bytes_le());
182 writer.write_all(&bytes)?;
183
184 writer.write_u32::<LittleEndian>(power)?;
185
186 Ok(())
187}
188
189pub(crate) fn write_points<G>(
190 mut writer: &mut impl Write,
191 points: Vec<G>,
192) -> Result<(), PtauFileError>
193where
194 G: halo2curves::serde::SerdeObject + CurveAffine,
195{
196 for point in points {
197 point.write_raw(&mut writer)?;
198 }
199 Ok(())
200}
201
202pub fn write_ptau<G1, G2>(
204 mut writer: &mut (impl Write + Seek),
205 g1_points: Vec<G1>,
206 g2_points: Vec<G2>,
207 power: u32,
208) -> Result<(), PtauFileError>
209where
210 G1: halo2curves::serde::SerdeObject + CurveAffine,
211 G2: halo2curves::serde::SerdeObject + CurveAffine,
212{
213 write_header::<G1::Base>(&mut writer, power)?;
214
215 writer.write_u32::<LittleEndian>(0)?;
216 writer.write_i64::<LittleEndian>(0)?;
217
218 for id in 4..NUM_SECTIONS_FULL {
219 writer.write_u32::<LittleEndian>(id)?;
220 writer.write_i64::<LittleEndian>(0)?;
221 }
222
223 {
224 writer.write_u32::<LittleEndian>(2)?;
225 let pos = writer.stream_position()?;
226
227 writer.write_i64::<LittleEndian>(0)?;
228 let start = writer.stream_position()?;
229
230 write_points(writer, g1_points)?;
231
232 let size = writer.stream_position()? - start;
233
234 writer.seek(SeekFrom::Start(pos))?;
235 writer.write_i64::<LittleEndian>(size as i64)?;
236
237 writer.seek(SeekFrom::Current(size as i64))?;
238 }
239
240 {
241 writer.write_u32::<LittleEndian>(3)?;
242 let pos = writer.stream_position()?;
243
244 writer.write_i64::<LittleEndian>(0)?;
245 let start = writer.stream_position()?;
246
247 write_points(writer, g2_points)?;
248
249 let size = writer.stream_position()? - start;
250
251 writer.seek(SeekFrom::Start(pos))?;
252 writer.write_i64::<LittleEndian>(size as i64)?;
253 }
254 Ok(())
255}
256
257fn read_meta_data(reader: &mut (impl Read + Seek)) -> Result<MetaData, PtauFileError> {
258 {
259 let mut buf = [0u8; 4];
260 reader.read_exact(&mut buf)?;
261 if from_utf8(&buf)? != "ptau" {
262 return Err(PtauFileError::InvalidHead);
263 }
264 }
265 {
266 let version = reader.read_u32::<LittleEndian>()?;
267 if version != PTAU_VERSION {
268 return Err(PtauFileError::UnsupportedVersion(version));
269 }
270 }
271 let num_sections = {
272 let num_sections = reader.read_u32::<LittleEndian>()?;
273 if num_sections != NUM_SECTIONS_FULL && num_sections != NUM_SECTIONS_PRUNED {
275 return Err(PtauFileError::InvalidNumSections(num_sections));
276 }
277 num_sections
278 };
279 let mut pos_header = 0;
280 let mut pos_tau_g1 = 0;
281 let mut pos_tau_g2 = 0;
282
283 for _ in 0..num_sections {
284 let id = reader.read_u32::<LittleEndian>()?;
285 let size = reader.read_i64::<LittleEndian>()?;
286
287 let pos = reader.stream_position()?;
288
289 match id {
290 1 => {
291 pos_header = pos;
292 }
293 2 => {
294 pos_tau_g1 = pos;
295 }
296 3 => {
297 pos_tau_g2 = pos;
298 }
299 _ => {}
300 };
301 reader.seek(SeekFrom::Current(size))?;
302 }
303
304 assert_ne!(pos_header, 0);
305 assert_ne!(pos_tau_g1, 0);
306 assert_ne!(pos_tau_g2, 0);
307
308 Ok(MetaData {
309 pos_header,
310 pos_tau_g1,
311 pos_tau_g2,
312 })
313}
314
315fn read_header<Base: PrimeField>(
316 reader: &mut impl Read,
317 num_g1: usize,
318 num_g2: usize,
319) -> Result<(), PtauFileError> {
320 let n8 = reader.read_u32::<LittleEndian>()?;
322
323 {
325 let mut buf = vec![0u8; n8 as usize];
326 reader.read_exact(&mut buf)?;
327
328 let modulus = BigUint::from_bytes_le(&buf);
329
330 let modulus_expected = BigUint::parse_bytes(&Base::MODULUS.as_bytes()[2..], 16).unwrap();
331
332 if modulus != modulus_expected {
333 return Err(PtauFileError::InvalidPrime(modulus));
334 }
335 }
336
337 let power = reader.read_u32::<LittleEndian>()?;
339
340 let max_num_g2 = 1 << power;
341 let max_num_g1 = max_num_g2 * 2 - 1;
342 if num_g1 > max_num_g1 {
343 return Err(PtauFileError::InsufficientPowerForG1 {
344 power,
345 required: max_num_g1,
346 });
347 }
348 if num_g2 > max_num_g2 {
349 return Err(PtauFileError::InsufficientPowerForG2 {
350 power,
351 required: max_num_g2,
352 });
353 }
354
355 Ok(())
356}
357
358pub(crate) fn read_points<G>(
359 mut reader: &mut impl Read,
360 num: usize,
361) -> Result<Vec<G>, PtauFileError>
362where
363 G: halo2curves::serde::SerdeObject + CurveAffine,
364{
365 let mut res = Vec::with_capacity(num);
366 for _ in 0..num {
367 res.push(G::read_raw(&mut reader)?);
368 }
369 Ok(res)
370}
371
372pub fn read_ptau<G1, G2>(
374 mut reader: &mut (impl Read + Seek),
375 num_g1: usize,
376 num_g2: usize,
377) -> Result<(Vec<G1>, Vec<G2>), PtauFileError>
378where
379 G1: halo2curves::serde::SerdeObject + CurveAffine,
380 G2: halo2curves::serde::SerdeObject + CurveAffine,
381{
382 let metadata = read_meta_data(&mut reader)?;
383
384 reader.seek(SeekFrom::Start(metadata.pos_header))?;
385 read_header::<G1::Base>(reader, num_g1, num_g2)?;
386
387 reader.seek(SeekFrom::Start(metadata.pos_tau_g1))?;
388 let g1_points = read_points::<G1>(&mut reader, num_g1)?;
389
390 reader.seek(SeekFrom::Start(metadata.pos_tau_g2))?;
391 let g2_points = read_points::<G2>(&mut reader, num_g2)?;
392
393 Ok((g1_points, g2_points))
394}
395
396pub fn check_sanity_of_ptau_file<G1>(
398 path: impl AsRef<Path>,
399 num_g1: usize,
400 num_g2: usize,
401) -> Result<(), PtauFileError>
402where
403 G1: halo2curves::serde::SerdeObject + CurveAffine,
404{
405 let mut reader = File::open(path)?;
406
407 let metadata = read_meta_data(&mut reader)?;
408
409 reader.seek(SeekFrom::Start(metadata.pos_header))?;
410 read_header::<G1::Base>(&mut reader, num_g1, num_g2)
411}