Skip to content

Commit

Permalink
Add cherry picked VerifyingKey serialization
Browse files Browse the repository at this point in the history
Here's the original PR by nalinbhardwaj:
zcash#661

It was decided not to merge this PR, but at the moment this is the only way to serialize Halo2 VerifyingKey, since PSE's fork of Halo2 only supports serializing keys derived for KZG based proofs which were incompatible with the Poseidon gadget.

This branch should not be merged and is simple used as a temporary solution until halo2 implements proper serialization.
  • Loading branch information
ThrasherLT committed Aug 20, 2024
1 parent 7df93fd commit cbb5c89
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 7 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# halo2

This is a temporary fork until Halo2 implements proper VerifyingKey serialziation.
Based on the PR by nalinbhardwaj, which isn't going to be merged, but so far is the only way to serialize the VerifyingKey:
https://github.com/zcash/halo2/pull/661
This fork is being used in my other personal project for now.

## Usage

This repository contains the [halo2_proofs](halo2_proofs/README.md) and
Expand Down
6 changes: 3 additions & 3 deletions halo2_proofs/src/dev/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ impl Layout {
total_rows: 0,
total_advice_rows: 0,
total_fixed_rows: 0,
/// Any cells assigned outside of a region.
// Any cells assigned outside of a region.
loose_cells: vec![],
/// Pairs of cells between which we have equality constraints.
// Pairs of cells between which we have equality constraints.
equality: vec![],
/// Selector assignments used for optimization pass
// Selector assignments used for optimization pass
selectors: vec![vec![false; n]; num_selectors],
}
}
Expand Down
19 changes: 19 additions & 0 deletions halo2_proofs/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,22 @@ pub(crate) trait CurveRead: CurveAffine {
}

impl<C: CurveAffine> 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;
}
}
2 changes: 1 addition & 1 deletion halo2_proofs/src/multicore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ compile_error!(

pub use maybe_rayon::{
iter::{IntoParallelIterator, ParallelIterator},
join, scope, Scope,
join, scope
};

#[cfg(feature = "multicore")]
Expand Down
121 changes: 119 additions & 2 deletions halo2_proofs/src/plonk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
use blake2b_simd::Params as Blake2bParams;
use group::ff::{Field, FromUniformBytes, PrimeField};

use crate::helpers::{pack, unpack, CurveRead};
use crate::arithmetic::CurveAffine;
use crate::poly::{
Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, PinnedEvaluationDomain,
Polynomial,
commitment::Params, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff,
PinnedEvaluationDomain, Polynomial,
};
use crate::transcript::{ChallengeScalar, EncodedChallenge, Transcript};

Expand Down Expand Up @@ -47,17 +48,132 @@ pub struct VerifyingKey<C: CurveAffine> {
cs_degree: usize,
/// The representative of this `VerifyingKey` in transcripts.
transcript_repr: C::Scalar,

selectors: Vec<Vec<bool>>,
}

impl<C: CurveAffine> VerifyingKey<C>
where
C::Scalar: FromUniformBytes<64>,
{
/// Writes a verifying key to a buffer.
pub fn write<W: io::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<R: io::Read, ConcreteCircuit: Circuit<C::Scalar>>(
reader: &mut R,
params: &Params<C>,
) -> io::Result<Self> {
let (domain, cs, _) = keygen::create_domain::<C, ConcreteCircuit>(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::<io::Result<_>>()?;

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<bool>> = 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::<io::Result<_>>()?;

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<u8> {
let mut bytes = Vec::<u8>::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<ConcreteCircuit: Circuit<C::Scalar>>(
mut bytes: &[u8],
params: &Params<C>,
) -> io::Result<Self> {
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<C::Scalar>,
fixed_commitments: Vec<C>,
permutation: permutation::VerifyingKey<C>,
cs: ConstraintSystem<C::Scalar>,
selectors: Vec<Vec<bool>>,
) -> Self {
// Compute cached values.
let cs_degree = cs.degree();
Expand All @@ -70,6 +186,7 @@ where
cs_degree,
// Temporary, this is not pinned.
transcript_repr: C::Scalar::ZERO,
selectors,
};

let mut hasher = Blake2bParams::new()
Expand Down
1 change: 1 addition & 0 deletions halo2_proofs/src/plonk/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@ impl<F: Field> Mul<F> for Expression<F> {

/// Represents an index into a vector where each entry corresponds to a distinct
/// point that polynomials are queried at.
#[allow(dead_code)]
#[derive(Copy, Clone, Debug)]
pub(crate) struct PointIndex(pub usize);

Expand Down
3 changes: 2 additions & 1 deletion halo2_proofs/src/plonk/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -240,6 +240,7 @@ where
fixed_commitments,
permutation_vk,
cs,
assembly.selectors,
))
}

Expand Down
33 changes: 33 additions & 0 deletions halo2_proofs/src/plonk/permutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use crate::{
arithmetic::CurveAffine,
poly::{Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial},
};
use std::io;
use crate::helpers::CurveRead;

pub(crate) mod keygen;
pub(crate) mod prover;
Expand Down Expand Up @@ -75,6 +77,37 @@ pub(crate) struct VerifyingKey<C: CurveAffine> {
commitments: Vec<C>,
}

impl<C: CurveAffine> VerifyingKey<C> {
pub(crate) fn write<W: io::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<R: io::Read>(reader: &mut R, argument: &Argument) -> io::Result<Self> {
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::<io::Result<_>>()?;
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<C: CurveAffine> {
Expand Down

0 comments on commit cbb5c89

Please sign in to comment.