From aa09e106e573a30ca9262c357619a6f2b75cdcbf Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Wed, 21 Dec 2022 12:16:56 -0500 Subject: [PATCH 1/4] feat: read `VerifyingKey` and `ProvingKey` does not require `params` as long as we serialize `params.k()` --- halo2_proofs/Cargo.toml | 6 ++- halo2_proofs/src/helpers.rs | 83 +++++++++++++++++++++++++++++++++---- halo2_proofs/src/plonk.rs | 70 +++++++++++++++++-------------- halo2_proofs/src/poly.rs | 8 ++-- 4 files changed, 121 insertions(+), 46 deletions(-) diff --git a/halo2_proofs/Cargo.toml b/halo2_proofs/Cargo.toml index abf200061a..8231074902 100644 --- a/halo2_proofs/Cargo.toml +++ b/halo2_proofs/Cargo.toml @@ -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/jonathanpwang/halo2curves', branch = 'feat/serde-field' } rand_core = { version = "0.6", default-features = false } tracing = "0.1" blake2b_simd = "1" @@ -68,11 +68,13 @@ rand_core = { version = "0.6", default-features = false, features = ["getrandom" getrandom = { version = "0.2", features = ["js"] } [features] -default = ["batch"] +default = ["batch", "serde-raw"] dev-graph = ["plotters", "tabbycat"] gadget-traces = ["backtrace"] sanity-checks = [] batch = ["rand_core/getrandom"] +serde-raw = [] +raw-unchecked = ["serde-raw"] [lib] bench = false diff --git a/halo2_proofs/src/helpers.rs b/halo2_proofs/src/helpers.rs index f601eab984..eb42d0787e 100644 --- a/halo2_proofs/src/helpers.rs +++ b/halo2_proofs/src/helpers.rs @@ -1,9 +1,12 @@ use crate::poly::Polynomial; use ff::PrimeField; +#[cfg(feature = "serde-raw")] +use halo2curves::serde::SerdeObject; use halo2curves::CurveAffine; use std::io; -pub(crate) trait CurveRead: CurveAffine { +#[cfg(not(feature = "serde-raw"))] +pub(crate) trait SerdeCurveAffine: CurveAffine { /// Reads a compressed element from the buffer and attempts to parse it /// using `from_bytes`. fn read(reader: &mut R) -> io::Result { @@ -12,10 +15,45 @@ pub(crate) trait CurveRead: CurveAffine { Option::from(Self::from_bytes(&compressed)) .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Invalid point encoding in proof")) } + /// Writes a curve element as a compressed affine point in bytes. + fn write(&self, writer: &mut W) -> io::Result<()> { + writer.write_all(self.to_bytes().as_ref()) + } } +#[cfg(not(feature = "serde-raw"))] +impl SerdeCurveAffine for C {} -impl CurveRead for C {} +#[cfg(feature = "serde-raw")] +pub(crate) trait SerdeCurveAffine: CurveAffine + SerdeObject { + /// Reads a curve element from raw bytes. + /// The curve element is stored exactly as it is in memory (two field elements in Montgomery representation). + fn read(reader: &mut R) -> io::Result { + #[cfg(feature = "raw-unchecked")] + { + Ok(Self::read_raw_unchecked(reader)) + } + #[cfg(not(feature = "raw-unchecked"))] + { + Self::read_raw(reader) + } + } + /// Writes a curve element into raw bytes. + /// The curve element is stored exactly as it is in memory (two field elements in Montgomery representation). + fn write(&self, writer: &mut W) -> io::Result<()> { + #[cfg(feature = "raw-unchecked")] + { + Ok(self.write_raw_unchecked(writer)) + } + #[cfg(not(feature = "raw-unchecked"))] + { + self.write_raw(writer) + } + } +} +#[cfg(feature = "serde-raw")] +impl SerdeCurveAffine for C {} +#[cfg(not(feature = "serde-raw"))] pub(crate) trait SerdePrimeField: PrimeField { /// Reads a field element as bytes from the buffer using `from_repr`. /// Endianness is specified by `PrimeField` implementation. @@ -33,9 +71,38 @@ pub(crate) trait SerdePrimeField: PrimeField { writer.write_all(self.to_repr().as_ref()) } } - +#[cfg(not(feature = "serde-raw"))] impl SerdePrimeField for F {} +#[cfg(feature = "serde-raw")] +pub(crate) trait SerdePrimeField: PrimeField + SerdeObject { + /// Reads a field element from raw bytes in its internal Montgomery representation. + fn read(reader: &mut R) -> io::Result { + #[cfg(feature = "raw-unchecked")] + { + Ok(Self::read_raw_unchecked(reader)) + } + #[cfg(not(feature = "raw-unchecked"))] + { + Self::read_raw(reader) + } + } + /// Writes a field element into raw bytes in its internal Montgomery representation, + /// WITHOUT performing the expensive Montgomery reduction. + fn write(&self, writer: &mut W) -> io::Result<()> { + #[cfg(feature = "raw-unchecked")] + { + Ok(self.write_raw_unchecked(writer)) + } + #[cfg(not(feature = "raw-unchecked"))] + { + self.write_raw(writer) + } + } +} +#[cfg(feature = "serde-raw")] +impl SerdePrimeField for F {} + /// Convert a slice of `bool` into a `u8`. /// /// Panics if the slice has length greater than 8. @@ -56,12 +123,12 @@ pub fn unpack(byte: u8, bits: &mut [bool]) { } /// Reads a vector of polynomials from buffer -pub(crate) fn read_polynomial_vec( +pub(crate) fn read_polynomial_vec( reader: &mut R, ) -> io::Result>> { - 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::::read(reader)) @@ -69,7 +136,7 @@ pub(crate) fn read_polynomial_vec( } /// Writes a slice of polynomials to buffer -pub(crate) fn write_polynomial_slice( +pub(crate) fn write_polynomial_slice( slice: &[Polynomial], writer: &mut W, ) -> io::Result<()> { diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 70ae557eed..a2d9cf2af2 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -11,7 +11,7 @@ use group::ff::Field; use crate::arithmetic::{CurveAffine, FieldExt}; use crate::helpers::{ - polynomial_slice_byte_length, read_polynomial_vec, write_polynomial_slice, CurveRead, + polynomial_slice_byte_length, read_polynomial_vec, write_polynomial_slice, SerdeCurveAffine, SerdePrimeField, }; use crate::poly::{ @@ -57,9 +57,13 @@ pub struct VerifyingKey { selectors: Vec>, } -impl VerifyingKey { +impl VerifyingKey +where + C::Scalar: SerdePrimeField, +{ /// Writes a verifying key to a buffer. pub fn write(&self, writer: &mut W) -> io::Result<()> { + writer.write_all(&self.domain.k().to_be_bytes())?; writer.write_all(&(self.fixed_commitments.len() as u32).to_be_bytes())?; for commitment in &self.fixed_commitments { writer.write_all(commitment.to_bytes().as_ref())?; @@ -77,11 +81,13 @@ impl VerifyingKey { } /// Reads a verification key from a buffer. - pub fn read<'params, R: io::Read, ConcreteCircuit: Circuit>( + pub fn read>( reader: &mut R, - params: &impl Params<'params, C>, ) -> io::Result { - let (domain, cs, _) = keygen::create_domain::(params.k()); + let mut k = [0u8; 4]; + reader.read_exact(&mut k)?; + let k = u32::from_be_bytes(k); + let (domain, cs, _) = keygen::create_domain::(k); let mut num_fixed_columns_be_bytes = [0u8; 4]; reader.read_exact(&mut num_fixed_columns_be_bytes)?; let num_fixed_columns = u32::from_be_bytes(num_fixed_columns_be_bytes); @@ -93,7 +99,7 @@ impl VerifyingKey { let permutation = permutation::VerifyingKey::read(reader, &cs.permutation)?; // read selectors - let selectors: Vec> = vec![vec![false; params.n() as usize]; cs.num_selectors] + let selectors: Vec> = vec![vec![false; 1 << k]; cs.num_selectors] .into_iter() .map(|mut selector| { let mut selector_bytes = vec![0u8; (selector.len() + 7) / 8]; @@ -124,15 +130,14 @@ impl VerifyingKey { } /// Reads a verification key from a slice of bytes. - pub fn from_bytes<'params, ConcreteCircuit: Circuit>( - mut bytes: &[u8], - params: &impl Params<'params, C>, - ) -> io::Result { - Self::read::<_, ConcreteCircuit>(&mut bytes, params) + pub fn from_bytes>(mut bytes: &[u8]) -> io::Result { + Self::read::<_, ConcreteCircuit>(&mut bytes) } +} +impl VerifyingKey { fn bytes_length(&self) -> usize { - 4 + (self.fixed_commitments.len() * C::default().to_bytes().as_ref().len()) + 8 + (self.fixed_commitments.len() * C::default().to_bytes().as_ref().len()) + self.permutation.bytes_length() + self.selectors.len() * (self @@ -251,6 +256,23 @@ impl ProvingKey { &self.vk } + /// Gets the total number of bytes in the serialization of `self` + fn bytes_length(&self) -> usize { + let scalar_len = C::Scalar::default().to_repr().as_ref().len(); + self.vk.bytes_length() + + 12 + + scalar_len * (self.l0.len() + self.l_last.len() + self.l_active_row.len()) + + polynomial_slice_byte_length(&self.fixed_values) + + polynomial_slice_byte_length(&self.fixed_polys) + + polynomial_slice_byte_length(&self.fixed_cosets) + + self.permutation.bytes_length() + } +} + +impl ProvingKey +where + C::Scalar: SerdePrimeField, +{ /// Writes a proving key to a buffer. /// Does so by first writing the verifying key and then serializing the rest of the data (in the form of field polynomials) pub fn write(&self, writer: &mut W) -> io::Result<()> { @@ -267,11 +289,10 @@ impl ProvingKey { /// Reads a proving key from a buffer. /// Does so by reading verification key first, and then deserializing the rest of the file into the remaining proving key data. - pub fn read<'params, R: io::Read, ConcreteCircuit: Circuit>( + pub fn read>( reader: &mut R, - params: &impl Params<'params, C>, ) -> io::Result { - let vk = VerifyingKey::::read::(reader, params)?; + let vk = VerifyingKey::::read::(reader)?; let l0 = Polynomial::read(reader)?; let l_last = Polynomial::read(reader)?; let l_active_row = Polynomial::read(reader)?; @@ -301,23 +322,8 @@ impl ProvingKey { } /// Reads a proving key from a slice of bytes. - pub fn from_bytes<'params, ConcreteCircuit: Circuit>( - mut bytes: &[u8], - params: &impl Params<'params, C>, - ) -> io::Result { - Self::read::<_, ConcreteCircuit>(&mut bytes, params) - } - - /// Gets the total number of bytes in the serialization of `self` - fn bytes_length(&self) -> usize { - let scalar_len = C::Scalar::default().to_repr().as_ref().len(); - self.vk.bytes_length() - + 12 - + scalar_len * (self.l0.len() + self.l_last.len() + self.l_active_row.len()) - + polynomial_slice_byte_length(&self.fixed_values) - + polynomial_slice_byte_length(&self.fixed_polys) - + polynomial_slice_byte_length(&self.fixed_cosets) - + self.permutation.bytes_length() + pub fn from_bytes>(mut bytes: &[u8]) -> io::Result { + Self::read::<_, ConcreteCircuit>(&mut bytes) } } diff --git a/halo2_proofs/src/poly.rs b/halo2_proofs/src/poly.rs index 1e2ac6366c..d5c05ee866 100644 --- a/halo2_proofs/src/poly.rs +++ b/halo2_proofs/src/poly.rs @@ -146,12 +146,12 @@ impl Polynomial { } } -impl Polynomial { +impl Polynomial { /// Reads polynomial from buffer using `SerdePrimeField::read`. pub(crate) fn read(reader: &mut R) -> io::Result { - let mut poly_len_be_bytes = [0u8; 4]; - reader.read_exact(&mut poly_len_be_bytes)?; - let poly_len = u32::from_be_bytes(poly_len_be_bytes); + let mut poly_len = [0u8; 4]; + reader.read_exact(&mut poly_len)?; + let poly_len = u32::from_be_bytes(poly_len); (0..poly_len) .map(|_| F::read(reader)) From db67f1174af4682841a72cecd4ed32635afeb5de Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Wed, 21 Dec 2022 14:31:44 -0500 Subject: [PATCH 2/4] feat: add features "serde-raw" and "raw-unchecked" to serialize/deserialize KZG params, verifying key, and proving key directly into raw bytes in internal memory format. So field elements are stored in Montgomery form `a * R (mod p)` and curve points are stored without compression. --- halo2_proofs/examples/serialization.rs | 2 +- halo2_proofs/src/helpers.rs | 40 +++--- halo2_proofs/src/plonk.rs | 11 +- halo2_proofs/src/plonk/permutation.rs | 23 ++- halo2_proofs/src/poly/kzg/commitment.rs | 134 ++++++++++++------ .../src/poly/kzg/multiopen/gwc/prover.rs | 8 +- .../src/poly/kzg/multiopen/gwc/verifier.rs | 9 +- .../src/poly/kzg/multiopen/shplonk/prover.rs | 5 +- .../poly/kzg/multiopen/shplonk/verifier.rs | 8 +- halo2_proofs/src/poly/kzg/strategy.rs | 14 +- 10 files changed, 164 insertions(+), 90 deletions(-) diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index fbd19a89b4..1fe70feb13 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -139,7 +139,7 @@ fn main() { let f = File::open("serialization-test.pk").unwrap(); let mut reader = BufReader::new(f); - let pk = ProvingKey::::read::<_, StandardPlonk>(&mut reader, ¶ms).unwrap(); + let pk = ProvingKey::::read::<_, StandardPlonk>(&mut reader).unwrap(); std::fs::remove_file("serialization-test.pk").unwrap(); diff --git a/halo2_proofs/src/helpers.rs b/halo2_proofs/src/helpers.rs index eb42d0787e..206725162f 100644 --- a/halo2_proofs/src/helpers.rs +++ b/halo2_proofs/src/helpers.rs @@ -2,11 +2,11 @@ use crate::poly::Polynomial; use ff::PrimeField; #[cfg(feature = "serde-raw")] use halo2curves::serde::SerdeObject; -use halo2curves::CurveAffine; +use halo2curves::{pairing::Engine, CurveAffine}; use std::io; -#[cfg(not(feature = "serde-raw"))] -pub(crate) trait SerdeCurveAffine: CurveAffine { +// 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`. fn read(reader: &mut R) -> io::Result { @@ -15,6 +15,16 @@ pub(crate) trait SerdeCurveAffine: CurveAffine { Option::from(Self::from_bytes(&compressed)) .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Invalid point encoding in proof")) } +} +impl CurveRead for C {} + +#[cfg(not(feature = "serde-raw"))] +pub trait SerdeCurveAffine: CurveAffine { + /// Reads a compressed element from the buffer and attempts to parse it + /// using `from_bytes`. + fn read(reader: &mut R) -> io::Result { + ::read(reader) + } /// Writes a curve element as a compressed affine point in bytes. fn write(&self, writer: &mut W) -> io::Result<()> { writer.write_all(self.to_bytes().as_ref()) @@ -24,7 +34,7 @@ pub(crate) trait SerdeCurveAffine: CurveAffine { impl SerdeCurveAffine for C {} #[cfg(feature = "serde-raw")] -pub(crate) trait SerdeCurveAffine: CurveAffine + SerdeObject { +pub trait SerdeCurveAffine: CurveAffine + SerdeObject { /// Reads a curve element from raw bytes. /// The curve element is stored exactly as it is in memory (two field elements in Montgomery representation). fn read(reader: &mut R) -> io::Result { @@ -40,21 +50,14 @@ pub(crate) trait SerdeCurveAffine: CurveAffine + SerdeObject { /// Writes a curve element into raw bytes. /// The curve element is stored exactly as it is in memory (two field elements in Montgomery representation). fn write(&self, writer: &mut W) -> io::Result<()> { - #[cfg(feature = "raw-unchecked")] - { - Ok(self.write_raw_unchecked(writer)) - } - #[cfg(not(feature = "raw-unchecked"))] - { - self.write_raw(writer) - } + self.write_raw(writer) } } #[cfg(feature = "serde-raw")] impl SerdeCurveAffine for C {} #[cfg(not(feature = "serde-raw"))] -pub(crate) trait SerdePrimeField: PrimeField { +pub trait SerdePrimeField: PrimeField { /// Reads a field element as bytes from the buffer using `from_repr`. /// Endianness is specified by `PrimeField` implementation. fn read(reader: &mut R) -> io::Result { @@ -75,7 +78,7 @@ pub(crate) trait SerdePrimeField: PrimeField { impl SerdePrimeField for F {} #[cfg(feature = "serde-raw")] -pub(crate) trait SerdePrimeField: PrimeField + SerdeObject { +pub trait SerdePrimeField: PrimeField + SerdeObject { /// Reads a field element from raw bytes in its internal Montgomery representation. fn read(reader: &mut R) -> io::Result { #[cfg(feature = "raw-unchecked")] @@ -90,14 +93,7 @@ pub(crate) trait SerdePrimeField: PrimeField + SerdeObject { /// Writes a field element into raw bytes in its internal Montgomery representation, /// WITHOUT performing the expensive Montgomery reduction. fn write(&self, writer: &mut W) -> io::Result<()> { - #[cfg(feature = "raw-unchecked")] - { - Ok(self.write_raw_unchecked(writer)) - } - #[cfg(not(feature = "raw-unchecked"))] - { - self.write_raw(writer) - } + self.write_raw(writer) } } #[cfg(feature = "serde-raw")] diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index a2d9cf2af2..38c4078571 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -66,7 +66,7 @@ where writer.write_all(&self.domain.k().to_be_bytes())?; writer.write_all(&(self.fixed_commitments.len() as u32).to_be_bytes())?; for commitment in &self.fixed_commitments { - writer.write_all(commitment.to_bytes().as_ref())?; + commitment.write(writer)?; } self.permutation.write(writer)?; @@ -88,9 +88,9 @@ where reader.read_exact(&mut k)?; let k = u32::from_be_bytes(k); let (domain, cs, _) = keygen::create_domain::(k); - let mut num_fixed_columns_be_bytes = [0u8; 4]; - reader.read_exact(&mut num_fixed_columns_be_bytes)?; - let num_fixed_columns = u32::from_be_bytes(num_fixed_columns_be_bytes); + let mut num_fixed_columns = [0u8; 4]; + reader.read_exact(&mut num_fixed_columns)?; + let num_fixed_columns = u32::from_be_bytes(num_fixed_columns); let fixed_commitments: Vec<_> = (0..num_fixed_columns) .map(|_| C::read(reader)) @@ -109,8 +109,7 @@ where } Ok(selector) }) - .collect::>>>() - .unwrap(); + .collect::>()?; let (cs, _) = cs.compress_selectors(selectors.clone()); Ok(Self::from_parts( diff --git a/halo2_proofs/src/plonk/permutation.rs b/halo2_proofs/src/plonk/permutation.rs index 7d0d8cc774..c0dc26dddd 100644 --- a/halo2_proofs/src/plonk/permutation.rs +++ b/halo2_proofs/src/plonk/permutation.rs @@ -2,7 +2,8 @@ use super::circuit::{Any, Column}; use crate::{ arithmetic::CurveAffine, helpers::{ - polynomial_slice_byte_length, read_polynomial_vec, write_polynomial_slice, CurveRead, + polynomial_slice_byte_length, read_polynomial_vec, write_polynomial_slice, + SerdeCurveAffine, SerdePrimeField, }, poly::{Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial}, }; @@ -87,15 +88,20 @@ impl VerifyingKey { &self.commitments } - pub(crate) fn write(&self, writer: &mut W) -> io::Result<()> { + pub(crate) fn write(&self, writer: &mut W) -> io::Result<()> + where + C: SerdeCurveAffine, + { for commitment in &self.commitments { - writer.write_all(commitment.to_bytes().as_ref())?; + commitment.write(writer)?; } - Ok(()) } - pub(crate) fn read(reader: &mut R, argument: &Argument) -> io::Result { + pub(crate) fn read(reader: &mut R, argument: &Argument) -> io::Result + where + C: SerdeCurveAffine, + { let commitments = (0..argument.columns.len()) .map(|_| C::read(reader)) .collect::, _>>()?; @@ -115,7 +121,10 @@ pub(crate) struct ProvingKey { pub(super) cosets: Vec>, } -impl ProvingKey { +impl ProvingKey +where + C::Scalar: SerdePrimeField, +{ /// Reads proving key for a single permutation argument from buffer using `Polynomial::read`. pub(super) fn read(reader: &mut R) -> io::Result { let permutations = read_polynomial_vec(reader)?; @@ -135,7 +144,9 @@ impl ProvingKey { write_polynomial_slice(&self.cosets, writer)?; Ok(()) } +} +impl ProvingKey { /// Gets the total number of bytes in the serialization of `self` pub(super) fn bytes_length(&self) -> usize { polynomial_slice_byte_length(&self.permutations) diff --git a/halo2_proofs/src/poly/kzg/commitment.rs b/halo2_proofs/src/poly/kzg/commitment.rs index 3e8cce6d09..c801d9401b 100644 --- a/halo2_proofs/src/poly/kzg/commitment.rs +++ b/halo2_proofs/src/poly/kzg/commitment.rs @@ -1,7 +1,7 @@ use crate::arithmetic::{ best_fft, best_multiexp, g_to_lagrange, parallelize, CurveAffine, CurveExt, FieldExt, Group, }; -use crate::helpers::CurveRead; +use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::{Blind, CommitmentScheme, Params, ParamsProver, ParamsVerifier, MSM}; use crate::poly::{Coeff, LagrangeCoeff, Polynomial}; @@ -34,7 +34,11 @@ pub struct KZGCommitmentScheme { _marker: PhantomData, } -impl CommitmentScheme for KZGCommitmentScheme { +impl CommitmentScheme for KZGCommitmentScheme +where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, +{ type Scalar = E::Scalar; type Curve = E::G1Affine; @@ -139,7 +143,11 @@ impl ParamsKZG { /// KZG multi-open verification parameters pub type ParamsVerifierKZG = ParamsKZG; -impl<'params, E: Engine + Debug> Params<'params, E::G1Affine> for ParamsKZG { +impl<'params, E: Engine + Debug> Params<'params, E::G1Affine> for ParamsKZG +where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, +{ type MSM = MSMKZG; fn k(&self) -> u32 { @@ -179,61 +187,88 @@ impl<'params, E: Engine + Debug> Params<'params, E::G1Affine> for ParamsKZG { /// Writes params to a buffer. fn write(&self, writer: &mut W) -> io::Result<()> { - use group::GroupEncoding; writer.write_all(&self.k.to_le_bytes())?; for el in self.g.iter() { - writer.write_all(el.to_bytes().as_ref())?; + el.write(writer)?; } for el in self.g_lagrange.iter() { - writer.write_all(el.to_bytes().as_ref())?; + el.write(writer)?; } - writer.write_all(self.g2.to_bytes().as_ref())?; - writer.write_all(self.s_g2.to_bytes().as_ref())?; + self.g2.write(writer)?; + self.s_g2.write(writer)?; Ok(()) } /// Reads params from a buffer. fn read(reader: &mut R) -> io::Result { - use group::GroupEncoding; - let mut k = [0u8; 4]; reader.read_exact(&mut k[..])?; let k = u32::from_le_bytes(k); let n = 1 << k; - let load_points_from_file_parallelly = - |reader: &mut R| -> io::Result>> { - let mut points_compressed = - vec![<::G1Affine as GroupEncoding>::Repr::default(); n]; - for points_compressed in points_compressed.iter_mut() { - reader.read_exact((*points_compressed).as_mut())?; - } - - let mut points = vec![Option::::None; n]; - parallelize(&mut points, |points, chunks| { - for (i, point) in points.iter_mut().enumerate() { - *point = - Option::from(E::G1Affine::from_bytes(&points_compressed[chunks + i])); + #[cfg(not(feature = "serde-raw"))] + let (g, g_lagrange) = { + use group::GroupEncoding; + let load_points_from_file_parallelly = + |reader: &mut R| -> io::Result>> { + let mut points_compressed = + vec![<::G1Affine as GroupEncoding>::Repr::default(); n]; + for points_compressed in points_compressed.iter_mut() { + reader.read_exact((*points_compressed).as_mut())?; } - }); - Ok(points) - }; - - let g = load_points_from_file_parallelly(reader)?; - let g: Vec<::G1Affine> = g - .iter() - .map(|point| { - point.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point encoding")) - }) - .collect::>()?; - - let g_lagrange = load_points_from_file_parallelly(reader)?; - let g_lagrange: Vec<::G1Affine> = g_lagrange - .iter() - .map(|point| { - point.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point encoding")) - }) - .collect::>()?; + + let mut points = vec![Option::::None; n]; + parallelize(&mut points, |points, chunks| { + for (i, point) in points.iter_mut().enumerate() { + *point = Option::from(E::G1Affine::from_bytes( + &points_compressed[chunks + i], + )); + } + }); + Ok(points) + }; + + let g = load_points_from_file_parallelly(reader)?; + let g: Vec<::G1Affine> = g + .iter() + .map(|point| { + point.ok_or_else(|| { + io::Error::new(io::ErrorKind::Other, "invalid point encoding") + }) + }) + .collect::>()?; + let g_lagrange = load_points_from_file_parallelly(reader)?; + let g_lagrange: Vec<::G1Affine> = g_lagrange + .iter() + .map(|point| { + point.ok_or_else(|| { + io::Error::new(io::ErrorKind::Other, "invalid point encoding") + }) + }) + .collect::>()?; + (g, g_lagrange) + }; + #[cfg(all(feature = "serde-raw", not(feature = "raw-unchecked")))] + let (g, g_lagrange) = { + let g = (0..n) + .map(|_| ::read(reader)) + .collect::, _>>()?; + let g_lagrange = (0..n) + .map(|_| ::read(reader)) + .collect::, _>>()?; + (g, g_lagrange) + }; + #[cfg(all(feature = "serde-raw", feature = "raw-unchecked"))] + let (g, g_lagrange) = { + // avoid try branching for performance + let g = (0..n) + .map(|_| ::read(reader).unwrap()) + .collect::>(); + let g_lagrange = (0..n) + .map(|_| ::read(reader).unwrap()) + .collect::>(); + (g, g_lagrange) + }; let g2 = E::G2Affine::read(reader)?; let s_g2 = E::G2Affine::read(reader)?; @@ -249,9 +284,18 @@ impl<'params, E: Engine + Debug> Params<'params, E::G1Affine> for ParamsKZG { } } -impl<'params, E: Engine + Debug> ParamsVerifier<'params, E::G1Affine> for ParamsKZG {} +impl<'params, E: Engine + Debug> ParamsVerifier<'params, E::G1Affine> for ParamsKZG +where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, +{ +} -impl<'params, E: Engine + Debug> ParamsProver<'params, E::G1Affine> for ParamsKZG { +impl<'params, E: Engine + Debug> ParamsProver<'params, E::G1Affine> for ParamsKZG +where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, +{ type ParamsVerifier = ParamsVerifierKZG; fn verifier_params(&'params self) -> &'params Self::ParamsVerifier { @@ -278,11 +322,9 @@ impl<'params, E: Engine + Debug> ParamsProver<'params, E::G1Affine> for ParamsKZ #[cfg(test)] mod test { - use crate::arithmetic::{ best_fft, best_multiexp, parallelize, CurveAffine, CurveExt, FieldExt, Group, }; - use crate::helpers::CurveRead; use crate::poly::commitment::ParamsProver; use crate::poly::commitment::{Blind, CommitmentScheme, Params, MSM}; use crate::poly::kzg::commitment::{ParamsKZG, ParamsVerifierKZG}; diff --git a/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs b/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs index e5077b1b58..e7bff84ade 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs @@ -1,6 +1,6 @@ use super::{construct_intermediate_sets, ChallengeV, Query}; use crate::arithmetic::{eval_polynomial, kate_division, powers, CurveAffine, FieldExt}; - +use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::ParamsProver; use crate::poly::commitment::Prover; use crate::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; @@ -27,7 +27,11 @@ pub struct ProverGWC<'params, E: Engine> { } /// Create a multi-opening proof -impl<'params, E: Engine + Debug> Prover<'params, KZGCommitmentScheme> for ProverGWC<'params, E> { +impl<'params, E: Engine + Debug> Prover<'params, KZGCommitmentScheme> for ProverGWC<'params, E> +where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, +{ const QUERY_INSTANCE: bool = false; fn new(params: &'params ParamsKZG) -> Self { diff --git a/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs b/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs index 8f9d348942..1ec003d638 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; use super::{construct_intermediate_sets, ChallengeU, ChallengeV}; use crate::arithmetic::{eval_polynomial, lagrange_interpolate, powers, CurveAffine, FieldExt}; - +use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::Verifier; use crate::poly::commitment::MSM; use crate::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; @@ -30,8 +30,11 @@ pub struct VerifierGWC<'params, E: Engine> { params: &'params ParamsKZG, } -impl<'params, E: MultiMillerLoop + Debug> Verifier<'params, KZGCommitmentScheme> - for VerifierGWC<'params, E> +impl<'params, E> Verifier<'params, KZGCommitmentScheme> for VerifierGWC<'params, E> +where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, { type Guard = GuardKZG<'params, E>; type MSMAccumulator = DualMSM<'params, E>; diff --git a/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs b/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs index 2585d9ab69..8b144d85b6 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs @@ -5,7 +5,7 @@ use crate::arithmetic::{ eval_polynomial, evaluate_vanishing_polynomial, kate_division, lagrange_interpolate, parallelize, powers, CurveAffine, FieldExt, }; - +use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::{Blind, ParamsProver, Prover}; use crate::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; use crate::poly::query::{PolynomialPointer, ProverQuery}; @@ -103,6 +103,9 @@ impl<'a, E: Engine> ProverSHPLONK<'a, E> { /// Create a multi-opening proof impl<'params, E: Engine + Debug> Prover<'params, KZGCommitmentScheme> for ProverSHPLONK<'params, E> +where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, { const QUERY_INSTANCE: bool = false; diff --git a/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs b/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs index 75626d74c6..11ffa880ef 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs @@ -7,6 +7,7 @@ use crate::arithmetic::{ eval_polynomial, evaluate_vanishing_polynomial, lagrange_interpolate, powers, CurveAffine, FieldExt, }; +use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::Verifier; use crate::poly::commitment::MSM; use crate::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; @@ -33,8 +34,11 @@ pub struct VerifierSHPLONK<'params, E: Engine> { params: &'params ParamsKZG, } -impl<'params, E: MultiMillerLoop + Debug> Verifier<'params, KZGCommitmentScheme> - for VerifierSHPLONK<'params, E> +impl<'params, E> Verifier<'params, KZGCommitmentScheme> for VerifierSHPLONK<'params, E> +where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, { type Guard = GuardKZG<'params, E>; type MSMAccumulator = DualMSM<'params, E>; diff --git a/halo2_proofs/src/poly/kzg/strategy.rs b/halo2_proofs/src/poly/kzg/strategy.rs index 896760067d..ca4b4fb18a 100644 --- a/halo2_proofs/src/poly/kzg/strategy.rs +++ b/halo2_proofs/src/poly/kzg/strategy.rs @@ -6,6 +6,7 @@ use super::{ multiopen::VerifierGWC, }; use crate::{ + helpers::SerdeCurveAffine, plonk::Error, poly::{ commitment::{Verifier, MSM}, @@ -29,7 +30,12 @@ pub struct GuardKZG<'params, E: MultiMillerLoop + Debug> { } /// Define accumulator type as `DualMSM` -impl<'params, E: MultiMillerLoop + Debug> Guard> for GuardKZG<'params, E> { +impl<'params, E> Guard> for GuardKZG<'params, E> +where + E: MultiMillerLoop + Debug, + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, +{ type MSMAccumulator = DualMSM<'params, E>; } @@ -85,6 +91,9 @@ impl< Guard = GuardKZG<'params, E>, >, > VerificationStrategy<'params, KZGCommitmentScheme, V> for AccumulatorStrategy<'params, E> +where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, { type Output = Self; @@ -120,6 +129,9 @@ impl< Guard = GuardKZG<'params, E>, >, > VerificationStrategy<'params, KZGCommitmentScheme, V> for SingleStrategy<'params, E> +where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, { type Output = (); From 7a444d3384ae1860781a96a656682a7b8ad645e3 Mon Sep 17 00:00:00 2001 From: Jonathan Wang Date: Thu, 5 Jan 2023 16:31:47 -0500 Subject: [PATCH 3/4] chore: switch to halo2curves 0.3.1 tag --- halo2_proofs/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/halo2_proofs/Cargo.toml b/halo2_proofs/Cargo.toml index 8231074902..de0ed10f8e 100644 --- a/halo2_proofs/Cargo.toml +++ b/halo2_proofs/Cargo.toml @@ -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/jonathanpwang/halo2curves', branch = 'feat/serde-field' } +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" From 607c3a4eb25d5ae07f710accf1ec6c604f76903f Mon Sep 17 00:00:00 2001 From: Jonathan Wang Date: Sun, 15 Jan 2023 17:45:59 -0800 Subject: [PATCH 4/4] feat: add enum `SerdeFormat` for user to select serialization/deserialization format of curve and field elements --- halo2_proofs/Cargo.toml | 4 +- halo2_proofs/examples/serialization.rs | 6 +- halo2_proofs/src/helpers.rs | 138 ++++++++--------- halo2_proofs/src/lib.rs | 1 + halo2_proofs/src/plonk.rs | 111 +++++++++---- halo2_proofs/src/plonk/permutation.rs | 33 ++-- halo2_proofs/src/poly.rs | 13 +- halo2_proofs/src/poly/kzg/commitment.rs | 198 +++++++++++++----------- 8 files changed, 288 insertions(+), 216 deletions(-) diff --git a/halo2_proofs/Cargo.toml b/halo2_proofs/Cargo.toml index de0ed10f8e..b611531f80 100644 --- a/halo2_proofs/Cargo.toml +++ b/halo2_proofs/Cargo.toml @@ -68,13 +68,11 @@ rand_core = { version = "0.6", default-features = false, features = ["getrandom" getrandom = { version = "0.2", features = ["js"] } [features] -default = ["batch", "serde-raw"] +default = ["batch"] dev-graph = ["plotters", "tabbycat"] gadget-traces = ["backtrace"] sanity-checks = [] batch = ["rand_core/getrandom"] -serde-raw = [] -raw-unchecked = ["serde-raw"] [lib] bench = false diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index 1fe70feb13..91ed5464e4 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -21,6 +21,7 @@ use halo2_proofs::{ transcript::{ Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, }, + SerdeFormat, }; use halo2curves::bn256::{Bn256, Fr, G1Affine}; use rand_core::OsRng; @@ -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::::read::<_, StandardPlonk>(&mut reader).unwrap(); + let pk = ProvingKey::::read::<_, StandardPlonk>(&mut reader, SerdeFormat::RawBytes) + .unwrap(); std::fs::remove_file("serialization-test.pk").unwrap(); diff --git a/halo2_proofs/src/helpers.rs b/halo2_proofs/src/helpers.rs index 206725162f..41fc2e8d1e 100644 --- a/halo2_proofs/src/helpers.rs +++ b/halo2_proofs/src/helpers.rs @@ -1,10 +1,24 @@ use crate::poly::Polynomial; use ff::PrimeField; -#[cfg(feature = "serde-raw")] -use halo2curves::serde::SerdeObject; -use halo2curves::{pairing::Engine, 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 @@ -18,85 +32,65 @@ pub(crate) trait CurveRead: CurveAffine { } impl CurveRead for C {} -#[cfg(not(feature = "serde-raw"))] -pub trait SerdeCurveAffine: CurveAffine { - /// Reads a compressed element from the buffer and attempts to parse it - /// using `from_bytes`. - fn read(reader: &mut R) -> io::Result { - ::read(reader) - } - /// Writes a curve element as a compressed affine point in bytes. - fn write(&self, writer: &mut W) -> io::Result<()> { - writer.write_all(self.to_bytes().as_ref()) - } -} -#[cfg(not(feature = "serde-raw"))] -impl SerdeCurveAffine for C {} - -#[cfg(feature = "serde-raw")] pub trait SerdeCurveAffine: CurveAffine + SerdeObject { - /// Reads a curve element from raw bytes. - /// The curve element is stored exactly as it is in memory (two field elements in Montgomery representation). - fn read(reader: &mut R) -> io::Result { - #[cfg(feature = "raw-unchecked")] - { - Ok(Self::read_raw_unchecked(reader)) - } - #[cfg(not(feature = "raw-unchecked"))] - { - Self::read_raw(reader) + /// 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(reader: &mut R, format: SerdeFormat) -> io::Result { + match format { + SerdeFormat::Processed => ::read(reader), + SerdeFormat::RawBytes => ::read_raw(reader), + SerdeFormat::RawBytesUnchecked => Ok(::read_raw_unchecked(reader)), } } - /// Writes a curve element into raw bytes. - /// The curve element is stored exactly as it is in memory (two field elements in Montgomery representation). - fn write(&self, writer: &mut W) -> io::Result<()> { - self.write_raw(writer) + /// 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(&self, writer: &mut W, format: SerdeFormat) -> io::Result<()> { + match format { + SerdeFormat::Processed => writer.write_all(self.to_bytes().as_ref()), + _ => self.write_raw(writer), + } } } -#[cfg(feature = "serde-raw")] impl SerdeCurveAffine for C {} -#[cfg(not(feature = "serde-raw"))] -pub trait SerdePrimeField: PrimeField { - /// Reads a field element as bytes from the buffer using `from_repr`. - /// Endianness is specified by `PrimeField` implementation. - fn read(reader: &mut R) -> io::Result { - 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") - }) - } - - /// Writes a field element as bytes to the buffer using `to_repr`. - /// Endianness is specified by `PrimeField` implementation. - fn write(&self, writer: &mut W) -> io::Result<()> { - writer.write_all(self.to_repr().as_ref()) - } -} -#[cfg(not(feature = "serde-raw"))] -impl SerdePrimeField for F {} - -#[cfg(feature = "serde-raw")] pub trait SerdePrimeField: PrimeField + SerdeObject { - /// Reads a field element from raw bytes in its internal Montgomery representation. - fn read(reader: &mut R) -> io::Result { - #[cfg(feature = "raw-unchecked")] - { - Ok(Self::read_raw_unchecked(reader)) - } - #[cfg(not(feature = "raw-unchecked"))] - { - Self::read_raw(reader) + /// 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(reader: &mut R, format: SerdeFormat) -> io::Result { + 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 => ::read_raw(reader), + SerdeFormat::RawBytesUnchecked => Ok(::read_raw_unchecked(reader)), } } - /// Writes a field element into raw bytes in its internal Montgomery representation, + + /// 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(&self, writer: &mut W) -> io::Result<()> { - self.write_raw(writer) + fn 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), + } } } -#[cfg(feature = "serde-raw")] impl SerdePrimeField for F {} /// Convert a slice of `bool` into a `u8`. @@ -121,13 +115,14 @@ pub fn unpack(byte: u8, bits: &mut [bool]) { /// Reads a vector of polynomials from buffer pub(crate) fn read_polynomial_vec( reader: &mut R, + format: SerdeFormat, ) -> io::Result>> { let mut len = [0u8; 4]; reader.read_exact(&mut len)?; let len = u32::from_be_bytes(len); (0..len) - .map(|_| Polynomial::::read(reader)) + .map(|_| Polynomial::::read(reader, format)) .collect::>>() } @@ -135,10 +130,11 @@ pub(crate) fn read_polynomial_vec( pub(crate) fn write_polynomial_slice( slice: &[Polynomial], 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(()) } diff --git a/halo2_proofs/src/lib.rs b/halo2_proofs/src/lib.rs index c84e482675..52676ddb19 100644 --- a/halo2_proofs/src/lib.rs +++ b/halo2_proofs/src/lib.rs @@ -35,3 +35,4 @@ pub mod transcript; pub mod dev; mod helpers; +pub use helpers::SerdeFormat; diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 38c4078571..00f2c51586 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -19,6 +19,7 @@ use crate::poly::{ PinnedEvaluationDomain, Polynomial, }; use crate::transcript::{ChallengeScalar, EncodedChallenge, Transcript}; +use crate::SerdeFormat; mod assigned; mod circuit; @@ -62,13 +63,21 @@ where C::Scalar: SerdePrimeField, { /// Writes a verifying key to a buffer. - pub fn write(&self, writer: &mut W) -> io::Result<()> { + /// + /// Writes a curve element according to `format`: + /// - `Processed`: Writes a compressed curve element with coordinates in standard form. + /// Writes a field element in standard form, with endianness specified by the + /// `PrimeField` implementation. + /// - Otherwise: Writes an uncompressed curve element with coordinates in Montgomery form + /// Writes a field element into raw bytes in its internal Montgomery representation, + /// WITHOUT performing the expensive Montgomery reduction. + pub fn write(&self, writer: &mut W, format: SerdeFormat) -> io::Result<()> { writer.write_all(&self.domain.k().to_be_bytes())?; writer.write_all(&(self.fixed_commitments.len() as u32).to_be_bytes())?; for commitment in &self.fixed_commitments { - commitment.write(writer)?; + commitment.write(writer, format)?; } - self.permutation.write(writer)?; + self.permutation.write(writer, format)?; // write self.selectors for selector in &self.selectors { @@ -81,8 +90,18 @@ where } /// Reads a verification key from a buffer. + /// + /// Reads a curve element from the buffer and parses it according to the `format`: + /// - `Processed`: Reads a compressed curve element and decompresses it. + /// 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 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 pub fn read>( reader: &mut R, + format: SerdeFormat, ) -> io::Result { let mut k = [0u8; 4]; reader.read_exact(&mut k)?; @@ -93,10 +112,10 @@ where let num_fixed_columns = u32::from_be_bytes(num_fixed_columns); let fixed_commitments: Vec<_> = (0..num_fixed_columns) - .map(|_| C::read(reader)) + .map(|_| C::read(reader, format)) .collect::>()?; - let permutation = permutation::VerifyingKey::read(reader, &cs.permutation)?; + let permutation = permutation::VerifyingKey::read(reader, &cs.permutation, format)?; // read selectors let selectors: Vec> = vec![vec![false; 1 << k]; cs.num_selectors] @@ -121,16 +140,19 @@ where )) } - /// Writes a verifying key to a vector of bytes. - pub fn to_bytes(&self) -> Vec { + /// Writes a verifying key to a vector of bytes using [`Self::write`]. + pub fn to_bytes(&self, format: SerdeFormat) -> Vec { let mut bytes = Vec::::with_capacity(self.bytes_length()); - Self::write(self, &mut bytes).expect("Writing to vector should not fail"); + Self::write(self, &mut bytes, format).expect("Writing to vector should not fail"); bytes } - /// Reads a verification key from a slice of bytes. - pub fn from_bytes>(mut bytes: &[u8]) -> io::Result { - Self::read::<_, ConcreteCircuit>(&mut bytes) + /// Reads a verification key from a slice of bytes using [`Self::read`]. + pub fn from_bytes>( + mut bytes: &[u8], + format: SerdeFormat, + ) -> io::Result { + Self::read::<_, ConcreteCircuit>(&mut bytes, format) } } @@ -273,32 +295,50 @@ where C::Scalar: SerdePrimeField, { /// Writes a proving key to a buffer. + /// + /// Writes a curve element according to `format`: + /// - `Processed`: Writes a compressed curve element with coordinates in standard form. + /// Writes a field element in standard form, with endianness specified by the + /// `PrimeField` implementation. + /// - Otherwise: Writes an uncompressed curve element with coordinates in Montgomery form + /// Writes a field element into raw bytes in its internal Montgomery representation, + /// WITHOUT performing the expensive Montgomery reduction. /// Does so by first writing the verifying key and then serializing the rest of the data (in the form of field polynomials) - pub fn write(&self, writer: &mut W) -> io::Result<()> { - self.vk.write(writer)?; - self.l0.write(writer)?; - self.l_last.write(writer)?; - self.l_active_row.write(writer)?; - write_polynomial_slice(&self.fixed_values, writer)?; - write_polynomial_slice(&self.fixed_polys, writer)?; - write_polynomial_slice(&self.fixed_cosets, writer)?; - self.permutation.write(writer)?; + pub fn write(&self, writer: &mut W, format: SerdeFormat) -> io::Result<()> { + self.vk.write(writer, format)?; + self.l0.write(writer, format)?; + self.l_last.write(writer, format)?; + self.l_active_row.write(writer, format)?; + write_polynomial_slice(&self.fixed_values, writer, format)?; + write_polynomial_slice(&self.fixed_polys, writer, format)?; + write_polynomial_slice(&self.fixed_cosets, writer, format)?; + self.permutation.write(writer, format)?; Ok(()) } /// Reads a proving key from a buffer. /// Does so by reading verification key first, and then deserializing the rest of the file into the remaining proving key data. + /// + /// Reads a curve element from the buffer and parses it according to the `format`: + /// - `Processed`: Reads a compressed curve element and decompresses it. + /// 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 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 pub fn read>( reader: &mut R, + format: SerdeFormat, ) -> io::Result { - let vk = VerifyingKey::::read::(reader)?; - let l0 = Polynomial::read(reader)?; - let l_last = Polynomial::read(reader)?; - let l_active_row = Polynomial::read(reader)?; - let fixed_values = read_polynomial_vec(reader)?; - let fixed_polys = read_polynomial_vec(reader)?; - let fixed_cosets = read_polynomial_vec(reader)?; - let permutation = permutation::ProvingKey::read(reader)?; + let vk = VerifyingKey::::read::(reader, format)?; + let l0 = Polynomial::read(reader, format)?; + let l_last = Polynomial::read(reader, format)?; + let l_active_row = Polynomial::read(reader, format)?; + let fixed_values = read_polynomial_vec(reader, format)?; + let fixed_polys = read_polynomial_vec(reader, format)?; + let fixed_cosets = read_polynomial_vec(reader, format)?; + let permutation = permutation::ProvingKey::read(reader, format)?; let ev = Evaluator::new(vk.cs()); Ok(Self { vk, @@ -313,16 +353,19 @@ where }) } - /// Writes a proving key to a vector of bytes. - pub fn to_bytes(&self) -> Vec { + /// Writes a proving key to a vector of bytes using [`Self::write`]. + pub fn to_bytes(&self, format: SerdeFormat) -> Vec { let mut bytes = Vec::::with_capacity(self.bytes_length()); - Self::write(self, &mut bytes).expect("Writing to vector should not fail"); + Self::write(self, &mut bytes, format).expect("Writing to vector should not fail"); bytes } - /// Reads a proving key from a slice of bytes. - pub fn from_bytes>(mut bytes: &[u8]) -> io::Result { - Self::read::<_, ConcreteCircuit>(&mut bytes) + /// Reads a proving key from a slice of bytes using [`Self::read`]. + pub fn from_bytes>( + mut bytes: &[u8], + format: SerdeFormat, + ) -> io::Result { + Self::read::<_, ConcreteCircuit>(&mut bytes, format) } } diff --git a/halo2_proofs/src/plonk/permutation.rs b/halo2_proofs/src/plonk/permutation.rs index c0dc26dddd..05cae2d373 100644 --- a/halo2_proofs/src/plonk/permutation.rs +++ b/halo2_proofs/src/plonk/permutation.rs @@ -6,6 +6,7 @@ use crate::{ SerdeCurveAffine, SerdePrimeField, }, poly::{Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial}, + SerdeFormat, }; use ff::PrimeField; @@ -88,22 +89,26 @@ impl VerifyingKey { &self.commitments } - pub(crate) fn write(&self, writer: &mut W) -> io::Result<()> + pub(crate) fn write(&self, writer: &mut W, format: SerdeFormat) -> io::Result<()> where C: SerdeCurveAffine, { for commitment in &self.commitments { - commitment.write(writer)?; + commitment.write(writer, format)?; } Ok(()) } - pub(crate) fn read(reader: &mut R, argument: &Argument) -> io::Result + pub(crate) fn read( + reader: &mut R, + argument: &Argument, + format: SerdeFormat, + ) -> io::Result where C: SerdeCurveAffine, { let commitments = (0..argument.columns.len()) - .map(|_| C::read(reader)) + .map(|_| C::read(reader, format)) .collect::, _>>()?; Ok(VerifyingKey { commitments }) } @@ -126,10 +131,10 @@ where C::Scalar: SerdePrimeField, { /// Reads proving key for a single permutation argument from buffer using `Polynomial::read`. - pub(super) fn read(reader: &mut R) -> io::Result { - let permutations = read_polynomial_vec(reader)?; - let polys = read_polynomial_vec(reader)?; - let cosets = read_polynomial_vec(reader)?; + pub(super) fn read(reader: &mut R, format: SerdeFormat) -> io::Result { + let permutations = read_polynomial_vec(reader, format)?; + let polys = read_polynomial_vec(reader, format)?; + let cosets = read_polynomial_vec(reader, format)?; Ok(ProvingKey { permutations, polys, @@ -138,10 +143,14 @@ where } /// Writes proving key for a single permutation argument to buffer using `Polynomial::write`. - pub(super) fn write(&self, writer: &mut W) -> io::Result<()> { - write_polynomial_slice(&self.permutations, writer)?; - write_polynomial_slice(&self.polys, writer)?; - write_polynomial_slice(&self.cosets, writer)?; + pub(super) fn write( + &self, + writer: &mut W, + format: SerdeFormat, + ) -> io::Result<()> { + write_polynomial_slice(&self.permutations, writer, format)?; + write_polynomial_slice(&self.polys, writer, format)?; + write_polynomial_slice(&self.cosets, writer, format)?; Ok(()) } } diff --git a/halo2_proofs/src/poly.rs b/halo2_proofs/src/poly.rs index d5c05ee866..44cde43dd9 100644 --- a/halo2_proofs/src/poly.rs +++ b/halo2_proofs/src/poly.rs @@ -5,6 +5,7 @@ use crate::arithmetic::parallelize; use crate::helpers::SerdePrimeField; use crate::plonk::Assigned; +use crate::SerdeFormat; use ff::PrimeField; use group::ff::{BatchInvert, Field}; @@ -148,13 +149,13 @@ impl Polynomial { impl Polynomial { /// Reads polynomial from buffer using `SerdePrimeField::read`. - pub(crate) fn read(reader: &mut R) -> io::Result { + pub(crate) fn read(reader: &mut R, format: SerdeFormat) -> io::Result { let mut poly_len = [0u8; 4]; reader.read_exact(&mut poly_len)?; let poly_len = u32::from_be_bytes(poly_len); (0..poly_len) - .map(|_| F::read(reader)) + .map(|_| F::read(reader, format)) .collect::>>() .map(|values| Self { values, @@ -163,10 +164,14 @@ impl Polynomial { } /// Writes polynomial to buffer using `SerdePrimeField::write`. - pub(crate) fn write(&self, writer: &mut W) -> io::Result<()> { + pub(crate) fn write( + &self, + writer: &mut W, + format: SerdeFormat, + ) -> io::Result<()> { writer.write_all(&(self.values.len() as u32).to_be_bytes())?; for value in self.values.iter() { - value.write(writer)?; + value.write(writer, format)?; } Ok(()) } diff --git a/halo2_proofs/src/poly/kzg/commitment.rs b/halo2_proofs/src/poly/kzg/commitment.rs index c801d9401b..03f6340200 100644 --- a/halo2_proofs/src/poly/kzg/commitment.rs +++ b/halo2_proofs/src/poly/kzg/commitment.rs @@ -4,6 +4,7 @@ use crate::arithmetic::{ use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::{Blind, CommitmentScheme, Params, ParamsProver, ParamsVerifier, MSM}; use crate::poly::{Coeff, LagrangeCoeff, Polynomial}; +use crate::SerdeFormat; use ff::{Field, PrimeField}; use group::{prime::PrimeCurveAffine, Curve, Group as _}; @@ -136,6 +137,111 @@ impl ParamsKZG { pub fn s_g2(&self) -> E::G2Affine { self.s_g2 } + + /// Writes parameters to buffer + pub fn write_custom(&self, writer: &mut W, format: SerdeFormat) -> io::Result<()> + where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, + { + writer.write_all(&self.k.to_le_bytes())?; + for el in self.g.iter() { + el.write(writer, format)?; + } + for el in self.g_lagrange.iter() { + el.write(writer, format)?; + } + self.g2.write(writer, format)?; + self.s_g2.write(writer, format)?; + Ok(()) + } + + /// Reads params from a buffer. + pub fn read_custom(reader: &mut R, format: SerdeFormat) -> io::Result + where + E::G1Affine: SerdeCurveAffine, + E::G2Affine: SerdeCurveAffine, + { + let mut k = [0u8; 4]; + reader.read_exact(&mut k[..])?; + let k = u32::from_le_bytes(k); + let n = 1 << k; + + let (g, g_lagrange) = match format { + SerdeFormat::Processed => { + use group::GroupEncoding; + let load_points_from_file_parallelly = + |reader: &mut R| -> io::Result>> { + let mut points_compressed = + vec![<::G1Affine as GroupEncoding>::Repr::default(); n]; + for points_compressed in points_compressed.iter_mut() { + reader.read_exact((*points_compressed).as_mut())?; + } + + let mut points = vec![Option::::None; n]; + parallelize(&mut points, |points, chunks| { + for (i, point) in points.iter_mut().enumerate() { + *point = Option::from(E::G1Affine::from_bytes( + &points_compressed[chunks + i], + )); + } + }); + Ok(points) + }; + + let g = load_points_from_file_parallelly(reader)?; + let g: Vec<::G1Affine> = g + .iter() + .map(|point| { + point.ok_or_else(|| { + io::Error::new(io::ErrorKind::Other, "invalid point encoding") + }) + }) + .collect::>()?; + let g_lagrange = load_points_from_file_parallelly(reader)?; + let g_lagrange: Vec<::G1Affine> = g_lagrange + .iter() + .map(|point| { + point.ok_or_else(|| { + io::Error::new(io::ErrorKind::Other, "invalid point encoding") + }) + }) + .collect::>()?; + (g, g_lagrange) + } + SerdeFormat::RawBytes => { + let g = (0..n) + .map(|_| ::read(reader, format)) + .collect::, _>>()?; + let g_lagrange = (0..n) + .map(|_| ::read(reader, format)) + .collect::, _>>()?; + (g, g_lagrange) + } + SerdeFormat::RawBytesUnchecked => { + // avoid try branching for performance + let g = (0..n) + .map(|_| ::read(reader, format).unwrap()) + .collect::>(); + let g_lagrange = (0..n) + .map(|_| ::read(reader, format).unwrap()) + .collect::>(); + (g, g_lagrange) + } + }; + + let g2 = E::G2Affine::read(reader, format)?; + let s_g2 = E::G2Affine::read(reader, format)?; + + Ok(Self { + k, + n: n as u64, + g, + g_lagrange, + g2, + s_g2, + }) + } } // TODO: see the issue at https://github.com/appliedzkp/halo2/issues/45 @@ -187,100 +293,12 @@ where /// Writes params to a buffer. fn write(&self, writer: &mut W) -> io::Result<()> { - writer.write_all(&self.k.to_le_bytes())?; - for el in self.g.iter() { - el.write(writer)?; - } - for el in self.g_lagrange.iter() { - el.write(writer)?; - } - self.g2.write(writer)?; - self.s_g2.write(writer)?; - Ok(()) + self.write_custom(writer, SerdeFormat::RawBytes) } /// Reads params from a buffer. fn read(reader: &mut R) -> io::Result { - let mut k = [0u8; 4]; - reader.read_exact(&mut k[..])?; - let k = u32::from_le_bytes(k); - let n = 1 << k; - - #[cfg(not(feature = "serde-raw"))] - let (g, g_lagrange) = { - use group::GroupEncoding; - let load_points_from_file_parallelly = - |reader: &mut R| -> io::Result>> { - let mut points_compressed = - vec![<::G1Affine as GroupEncoding>::Repr::default(); n]; - for points_compressed in points_compressed.iter_mut() { - reader.read_exact((*points_compressed).as_mut())?; - } - - let mut points = vec![Option::::None; n]; - parallelize(&mut points, |points, chunks| { - for (i, point) in points.iter_mut().enumerate() { - *point = Option::from(E::G1Affine::from_bytes( - &points_compressed[chunks + i], - )); - } - }); - Ok(points) - }; - - let g = load_points_from_file_parallelly(reader)?; - let g: Vec<::G1Affine> = g - .iter() - .map(|point| { - point.ok_or_else(|| { - io::Error::new(io::ErrorKind::Other, "invalid point encoding") - }) - }) - .collect::>()?; - let g_lagrange = load_points_from_file_parallelly(reader)?; - let g_lagrange: Vec<::G1Affine> = g_lagrange - .iter() - .map(|point| { - point.ok_or_else(|| { - io::Error::new(io::ErrorKind::Other, "invalid point encoding") - }) - }) - .collect::>()?; - (g, g_lagrange) - }; - #[cfg(all(feature = "serde-raw", not(feature = "raw-unchecked")))] - let (g, g_lagrange) = { - let g = (0..n) - .map(|_| ::read(reader)) - .collect::, _>>()?; - let g_lagrange = (0..n) - .map(|_| ::read(reader)) - .collect::, _>>()?; - (g, g_lagrange) - }; - #[cfg(all(feature = "serde-raw", feature = "raw-unchecked"))] - let (g, g_lagrange) = { - // avoid try branching for performance - let g = (0..n) - .map(|_| ::read(reader).unwrap()) - .collect::>(); - let g_lagrange = (0..n) - .map(|_| ::read(reader).unwrap()) - .collect::>(); - (g, g_lagrange) - }; - - let g2 = E::G2Affine::read(reader)?; - let s_g2 = E::G2Affine::read(reader)?; - - Ok(Self { - k, - n: n as u64, - g, - g_lagrange, - g2, - s_g2, - }) + Self::read_custom(reader, SerdeFormat::RawBytes) } }