diff --git a/halo2_proofs/src/helpers.rs b/halo2_proofs/src/helpers.rs index 2158a369..e56758fb 100644 --- a/halo2_proofs/src/helpers.rs +++ b/halo2_proofs/src/helpers.rs @@ -14,3 +14,22 @@ pub(crate) trait CurveRead: CurveAffine { } impl CurveRead for C {} + +/// Converts a slice of `bool` into a `u8`. +/// +/// Panics if the slice has length greater than 8. +pub fn pack(bits: &[bool]) -> u8 { + let mut value = 0u8; + assert!(bits.len() <= 8); + for (bit_index, bit) in bits.iter().enumerate() { + value |= (*bit as u8) << bit_index; + } + value +} + +/// Writes the first `bits.len()` bits of a `u8` into `bits`. +pub fn unpack(byte: u8, bits: &mut [bool]) { + for (bit_index, bit) in bits.iter_mut().enumerate() { + *bit = (byte >> bit_index) & 1 == 1; + } +} diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index e96cf3fe..3557517a 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -9,9 +9,10 @@ use blake2b_simd::Params as Blake2bParams; use group::ff::{Field, FromUniformBytes, PrimeField}; use crate::arithmetic::CurveAffine; +use crate::helpers::{pack, unpack, CurveRead}; use crate::poly::{ - Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, PinnedEvaluationDomain, - Polynomial, + commitment::Params, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, + PinnedEvaluationDomain, Polynomial, }; use crate::transcript::{ChallengeScalar, EncodedChallenge, Transcript}; @@ -47,17 +48,131 @@ pub struct VerifyingKey { cs_degree: usize, /// The representative of this `VerifyingKey` in transcripts. transcript_repr: C::Scalar, + selectors: Vec>, } impl VerifyingKey where C::Scalar: FromUniformBytes<64>, { + /// Writes a verifying key to a buffer. + pub fn write(&self, writer: &mut W) -> io::Result<()> { + // Version byte that will be checked on read. + writer.write_all(&[0x01])?; + + writer.write_all(&(u32::try_from(self.fixed_commitments.len()).unwrap()).to_le_bytes())?; + for commitment in &self.fixed_commitments { + writer.write_all(commitment.to_bytes().as_ref())?; + } + self.permutation.write(writer)?; + + // write self.selectors + writer.write_all(&(u32::try_from(self.selectors.len()).unwrap()).to_le_bytes())?; + for selector in &self.selectors { + // since `selector` is filled with `bool`, we pack them 8 at a time into bytes and then write + for bits in selector.chunks(8) { + writer.write_all(&[pack(bits)])?; + } + } + + Ok(()) + } + + /// Reads a verifying key from a buffer. + pub fn read>( + reader: &mut R, + params: &Params, + ) -> io::Result { + let (domain, cs, _) = keygen::create_domain::(params); + + let mut version_byte = [0u8; 1]; + reader.read_exact(&mut version_byte)?; + if 0x01 != version_byte[0] { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "unexpected version byte", + )); + } + + let mut num_fixed_columns_le_bytes = [0u8; 4]; + reader.read_exact(&mut num_fixed_columns_le_bytes)?; + let num_fixed_columns = u32::from_le_bytes(num_fixed_columns_le_bytes); + + let fixed_commitments: Vec<_> = (0..num_fixed_columns) + .map(|_| C::read(reader)) + .collect::>()?; + + let permutation = permutation::VerifyingKey::read(reader, &cs.permutation)?; + + // read selectors + let mut num_selectors_le_bytes = [0u8; 4]; + reader.read_exact(&mut num_selectors_le_bytes)?; + let num_selectors = u32::from_le_bytes(num_selectors_le_bytes); + if cs.num_selectors != num_selectors.try_into().unwrap() { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "unexpected number of selectors", + )); + } + let selectors: Vec> = vec![vec![false; params.n as usize]; cs.num_selectors] + .into_iter() + .map(|mut selector| { + let mut selector_bytes = vec![0u8; (selector.len() + 7) / 8]; + reader.read_exact(&mut selector_bytes)?; + for (bits, byte) in selector.chunks_mut(8).zip(selector_bytes) { + unpack(byte, bits); + } + Ok(selector) + }) + .collect::>()?; + + let (cs, _) = cs.compress_selectors(selectors.clone()); + + Ok(Self::from_parts( + domain, + fixed_commitments, + permutation, + cs, + selectors, + )) + } + + /// Writes a verifying key to a vector of bytes. + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::::with_capacity(self.bytes_length()); + self.write(&mut bytes) + .expect("Writing to vector should not fail"); + bytes + } + + /// Reads a verifying key from a slice of bytes. + pub fn from_bytes>( + mut bytes: &[u8], + params: &Params, + ) -> io::Result { + Self::read::<_, ConcreteCircuit>(&mut bytes, params) + } + + /// Gets the total number of bytes in the serialization of `self`. + fn bytes_length(&self) -> usize { + 1 + 4 + + self.fixed_commitments.len() * C::default().to_bytes().as_ref().len() + + self.permutation.bytes_length() + + 4 + + self.selectors.len() + * self + .selectors + .get(0) + .map(|selector| (selector.len() + 7) / 8) + .unwrap_or(0) + } + fn from_parts( domain: EvaluationDomain, fixed_commitments: Vec, permutation: permutation::VerifyingKey, cs: ConstraintSystem, + selectors: Vec>, ) -> Self { // Compute cached values. let cs_degree = cs.degree(); @@ -70,6 +185,7 @@ where cs_degree, // Temporary, this is not pinned. transcript_repr: C::Scalar::ZERO, + selectors, }; let mut hasher = Blake2bParams::new() diff --git a/halo2_proofs/src/plonk/keygen.rs b/halo2_proofs/src/plonk/keygen.rs index bb26d5a6..1b2be6d0 100644 --- a/halo2_proofs/src/plonk/keygen.rs +++ b/halo2_proofs/src/plonk/keygen.rs @@ -219,7 +219,7 @@ where )?; let mut fixed = batch_invert_assigned(assembly.fixed); - let (cs, selector_polys) = cs.compress_selectors(assembly.selectors); + let (cs, selector_polys) = cs.compress_selectors(assembly.selectors.clone()); fixed.extend( selector_polys .into_iter() @@ -240,6 +240,7 @@ where fixed_commitments, permutation_vk, cs, + assembly.selectors, )) } diff --git a/halo2_proofs/src/plonk/permutation.rs b/halo2_proofs/src/plonk/permutation.rs index c81de276..2f001715 100644 --- a/halo2_proofs/src/plonk/permutation.rs +++ b/halo2_proofs/src/plonk/permutation.rs @@ -8,6 +8,9 @@ pub(crate) mod keygen; pub(crate) mod prover; pub(crate) mod verifier; +use crate::helpers::CurveRead; +use std::io; + /// A permutation argument. #[derive(Debug, Clone)] pub(crate) struct Argument { @@ -75,6 +78,37 @@ pub(crate) struct VerifyingKey { commitments: Vec, } +impl VerifyingKey { + pub(crate) fn write(&self, writer: &mut W) -> io::Result<()> { + writer.write_all(&(u32::try_from(self.commitments.len()).unwrap()).to_le_bytes())?; + for commitment in &self.commitments { + writer.write_all(commitment.to_bytes().as_ref())?; + } + + Ok(()) + } + + pub(crate) fn read(reader: &mut R, argument: &Argument) -> io::Result { + let mut num_commitments_le_bytes = [0u8; 4]; + reader.read_exact(&mut num_commitments_le_bytes)?; + let num_commitments = u32::from_le_bytes(num_commitments_le_bytes); + if argument.columns.len() != num_commitments.try_into().unwrap() { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "unexpected number of column commitments", + )); + } + let commitments: Vec<_> = (0..argument.columns.len()) + .map(|_| C::read(reader)) + .collect::>()?; + Ok(VerifyingKey { commitments }) + } + + pub(crate) fn bytes_length(&self) -> usize { + 4 + self.commitments.len() * C::default().to_bytes().as_ref().len() + } +} + /// The proving key for a single permutation argument. #[derive(Clone, Debug)] pub(crate) struct ProvingKey {