Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature to serialize/deserialize KZG params, verifying key, and proving key into uncompressed Montgomery form #111

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion halo2_proofs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ backtrace = { version = "0.3", optional = true }
rayon = "1.5.1"
ff = "0.12"
group = "0.12"
halo2curves = { git = 'https://github.com/privacy-scaling-explorations/halo2curves', tag = '0.3.0' }
halo2curves = { git = 'https://github.com/privacy-scaling-explorations/halo2curves.git', tag = "0.3.1" }
rand_core = { version = "0.6", default-features = false }
tracing = "0.1"
blake2b_simd = "1"
Expand Down
6 changes: 4 additions & 2 deletions halo2_proofs/examples/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use halo2_proofs::{
transcript::{
Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer,
},
SerdeFormat,
};
use halo2curves::bn256::{Bn256, Fr, G1Affine};
use rand_core::OsRng;
Expand Down Expand Up @@ -134,12 +135,13 @@ fn main() {

let f = File::create("serialization-test.pk").unwrap();
let mut writer = BufWriter::new(f);
pk.write(&mut writer).unwrap();
pk.write(&mut writer, SerdeFormat::RawBytes).unwrap();
writer.flush().unwrap();

let f = File::open("serialization-test.pk").unwrap();
let mut reader = BufReader::new(f);
let pk = ProvingKey::<G1Affine>::read::<_, StandardPlonk>(&mut reader, &params).unwrap();
let pk = ProvingKey::<G1Affine>::read::<_, StandardPlonk>(&mut reader, SerdeFormat::RawBytes)
.unwrap();

std::fs::remove_file("serialization-test.pk").unwrap();

Expand Down
107 changes: 83 additions & 24 deletions halo2_proofs/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
use crate::poly::Polynomial;
use ff::PrimeField;
use halo2curves::CurveAffine;
use halo2curves::{pairing::Engine, serde::SerdeObject, CurveAffine};
use std::io;

/// This enum specifies how various types are serialized and deserialized.
#[derive(Clone, Copy, Debug)]
pub enum SerdeFormat {
/// Curve elements are serialized in compressed form.
/// Field elements are serialized in standard form, with endianness specified by the
/// `PrimeField` implementation.
Processed,
/// Curve elements are serialized in uncompressed form. Field elements are serialized
/// in their internal Montgomery representation.
/// When deserializing, checks are performed to ensure curve elements indeed lie on the curve and field elements
/// are less than modulus.
RawBytes,
/// Serialization is the same as `RawBytes`, but no checks are performed.
RawBytesUnchecked,
}

// Keep this trait for compatibility with IPA serialization
pub(crate) trait CurveRead: CurveAffine {
/// Reads a compressed element from the buffer and attempts to parse it
/// using `from_bytes`.
Expand All @@ -13,28 +30,68 @@ pub(crate) trait CurveRead: CurveAffine {
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Invalid point encoding in proof"))
}
}

impl<C: CurveAffine> CurveRead for C {}

pub(crate) trait SerdePrimeField: PrimeField {
/// Reads a field element as bytes from the buffer using `from_repr`.
/// Endianness is specified by `PrimeField` implementation.
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
let mut compressed = Self::Repr::default();
reader.read_exact(compressed.as_mut())?;
Option::from(Self::from_repr(compressed)).ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "Invalid prime field point encoding")
})
pub trait SerdeCurveAffine: CurveAffine + SerdeObject {
/// Reads an element from the buffer and parses it according to the `format`:
/// - `Processed`: Reads a compressed curve element and decompress it
/// - `RawBytes`: Reads an uncompressed curve element with coordinates in Montgomery form.
/// Checks that field elements are less than modulus, and then checks that the point is on the curve.
/// - `RawBytesUnchecked`: Reads an uncompressed curve element with coordinates in Montgomery form;
/// does not perform any checks
fn read<R: io::Read>(reader: &mut R, format: SerdeFormat) -> io::Result<Self> {
match format {
SerdeFormat::Processed => <Self as CurveRead>::read(reader),
SerdeFormat::RawBytes => <Self as SerdeObject>::read_raw(reader),
SerdeFormat::RawBytesUnchecked => Ok(<Self as SerdeObject>::read_raw_unchecked(reader)),
}
}

/// Writes a field element as bytes to the buffer using `to_repr`.
/// Endianness is specified by `PrimeField` implementation.
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(self.to_repr().as_ref())
/// Writes a curve element according to `format`:
/// - `Processed`: Writes a compressed curve element
/// - Otherwise: Writes an uncompressed curve element with coordinates in Montgomery form
fn write<W: io::Write>(&self, writer: &mut W, format: SerdeFormat) -> io::Result<()> {
match format {
SerdeFormat::Processed => writer.write_all(self.to_bytes().as_ref()),
_ => self.write_raw(writer),
}
}
}
impl<C: CurveAffine + SerdeObject> SerdeCurveAffine for C {}

pub trait SerdePrimeField: PrimeField + SerdeObject {
/// Reads a field element as bytes from the buffer according to the `format`:
/// - `Processed`: Reads a field element in standard form, with endianness specified by the
/// `PrimeField` implementation, and checks that the element is less than the modulus.
/// - `RawBytes`: Reads a field element from raw bytes in its internal Montgomery representations,
/// and checks that the element is less than the modulus.
/// - `RawBytesUnchecked`: Reads a field element in Montgomery form and performs no checks.
fn read<R: io::Read>(reader: &mut R, format: SerdeFormat) -> io::Result<Self> {
match format {
SerdeFormat::Processed => {
let mut compressed = Self::Repr::default();
reader.read_exact(compressed.as_mut())?;
Option::from(Self::from_repr(compressed)).ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "Invalid prime field point encoding")
})
}
SerdeFormat::RawBytes => <Self as SerdeObject>::read_raw(reader),
SerdeFormat::RawBytesUnchecked => Ok(<Self as SerdeObject>::read_raw_unchecked(reader)),
}
}

impl<F: PrimeField> SerdePrimeField for F {}
/// Writes a field element as bytes to the buffer according to the `format`:
/// - `Processed`: Writes a field element in standard form, with endianness specified by the
/// `PrimeField` implementation.
/// - Otherwise: Writes a field element into raw bytes in its internal Montgomery representation,
/// WITHOUT performing the expensive Montgomery reduction.
fn write<W: io::Write>(&self, writer: &mut W, format: SerdeFormat) -> io::Result<()> {
match format {
SerdeFormat::Processed => writer.write_all(self.to_repr().as_ref()),
_ => self.write_raw(writer),
}
}
}
impl<F: PrimeField + SerdeObject> SerdePrimeField for F {}

/// Convert a slice of `bool` into a `u8`.
///
Expand All @@ -56,26 +113,28 @@ pub fn unpack(byte: u8, bits: &mut [bool]) {
}

/// Reads a vector of polynomials from buffer
pub(crate) fn read_polynomial_vec<R: io::Read, F: PrimeField, B>(
pub(crate) fn read_polynomial_vec<R: io::Read, F: SerdePrimeField, B>(
reader: &mut R,
format: SerdeFormat,
) -> io::Result<Vec<Polynomial<F, B>>> {
let mut len_be_bytes = [0u8; 4];
reader.read_exact(&mut len_be_bytes)?;
let len = u32::from_be_bytes(len_be_bytes);
let mut len = [0u8; 4];
reader.read_exact(&mut len)?;
let len = u32::from_be_bytes(len);

(0..len)
.map(|_| Polynomial::<F, B>::read(reader))
.map(|_| Polynomial::<F, B>::read(reader, format))
.collect::<io::Result<Vec<_>>>()
}

/// Writes a slice of polynomials to buffer
pub(crate) fn write_polynomial_slice<W: io::Write, F: PrimeField, B>(
pub(crate) fn write_polynomial_slice<W: io::Write, F: SerdePrimeField, B>(
slice: &[Polynomial<F, B>],
writer: &mut W,
format: SerdeFormat,
) -> io::Result<()> {
writer.write_all(&(slice.len() as u32).to_be_bytes())?;
for poly in slice.iter() {
poly.write(writer)?;
poly.write(writer, format)?;
}
Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions halo2_proofs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ pub mod transcript;

pub mod dev;
mod helpers;
pub use helpers::SerdeFormat;
Loading