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

Reduce size of proving keys and verifying keys #6

Merged
merged 4 commits into from
Apr 26, 2024
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
27 changes: 12 additions & 15 deletions halo2_proofs/src/plonk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,13 +360,9 @@ where
where
C: SerdeCurveAffine,
{
let scalar_len = C::Scalar::default().to_repr().as_ref().len();
self.vk.bytes_length(format)
+ 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()
}
}
Expand All @@ -387,12 +383,7 @@ where
/// 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<W: io::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(())
}
Expand All @@ -419,13 +410,19 @@ where
#[cfg(feature = "circuit-params")]
params,
)?;
let l0 = Polynomial::read(reader, format)?;
let l_last = Polynomial::read(reader, format)?;
let l_active_row = Polynomial::read(reader, format)?;
let [l0, l_last, l_active_row] = compute_lagrange_polys(&vk, &vk.cs);
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 fixed_polys: Vec<_> = fixed_values
.iter()
.map(|poly| vk.domain.lagrange_to_coeff(poly.clone()))
.collect();

let fixed_cosets = fixed_polys
.iter()
.map(|poly| vk.domain.coeff_to_extended(poly.clone()))
.collect();
let permutation =
permutation::ProvingKey::read(reader, format, &vk.domain, &vk.cs.permutation)?;
let ev = Evaluator::new(vk.cs());
Ok(Self {
vk,
Expand Down
57 changes: 39 additions & 18 deletions halo2_proofs/src/plonk/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
poly::{
batch_invert_assigned,
commitment::{Blind, Params},
EvaluationDomain,
EvaluationDomain, ExtendedLagrangeCoeff,
},
};

Expand Down Expand Up @@ -206,7 +206,16 @@ where
ConcreteCircuit: Circuit<C::Scalar>,
C::Scalar: FromUniformBytes<64>,
{
keygen_vk_custom(params, circuit, true)
// We disable "complex selectors" for now, because they are not properly
// serialized. At the moment, they are stored as boolean vectors containing
// their value on every single row. This makes the verification key size
// linear when they are enabled.
// Furthermore, having complex selectors makes the cs structure variable
// depending on the selector instantiation. This is undesirable.
// In the future, if we want to enjoy the benefits of complex selectors
// (about a 10-20% improvement in proof size and verifying key payload size)
// we need to improve their serialization.
keygen_vk_custom(params, circuit, false)
}

/// Generate a `VerifyingKey` from an instance of `Circuit`.
Expand Down Expand Up @@ -349,6 +358,31 @@ where
.permutation
.build_pk(params, &vk.domain, &cs.permutation);

let [l0, l_last, l_active_row] = compute_lagrange_polys(&vk, &cs);

// Compute the optimized evaluation data structure
let ev = Evaluator::new(&vk.cs);

Ok(ProvingKey {
vk,
l0,
l_last,
l_active_row,
fixed_values: fixed,
fixed_polys,
fixed_cosets,
permutation: permutation_pk,
ev,
})
}

pub(crate) fn compute_lagrange_polys<C>(
vk: &VerifyingKey<C>,
cs: &ConstraintSystem<C::Scalar>,
) -> [Polynomial<C::Scalar, ExtendedLagrangeCoeff>; 3]
where
C: CurveAffine,
{
// Compute l_0(X)
// TODO: this can be done more efficiently
let mut l0 = vk.domain.empty_lagrange();
Expand All @@ -368,7 +402,8 @@ where
// Compute l_last(X) which evaluates to 1 on the first inactive row (just
// before the blinding factors) and 0 otherwise over the domain
let mut l_last = vk.domain.empty_lagrange();
l_last[params.n() as usize - cs.blinding_factors() - 1] = C::Scalar::ONE;
let n = l_last.len();
l_last[n - cs.blinding_factors() - 1] = C::Scalar::ONE;
let l_last = vk.domain.lagrange_to_coeff(l_last);
let l_last = vk.domain.coeff_to_extended(l_last);

Expand All @@ -381,19 +416,5 @@ where
*value = one - (l_last[idx] + l_blind[idx]);
}
});

// Compute the optimized evaluation data structure
let ev = Evaluator::new(&vk.cs);

Ok(ProvingKey {
vk,
l0,
l_last,
l_active_row,
fixed_values: fixed,
fixed_polys,
fixed_cosets,
permutation: permutation_pk,
ev,
})
[l0, l_last, l_active_row]
}
18 changes: 10 additions & 8 deletions halo2_proofs/src/plonk/permutation.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! Implementation of permutation argument.

use self::keygen::compute_polys_and_cosets;

use super::circuit::{Any, Column};
use crate::{
arithmetic::CurveAffine,
helpers::{
polynomial_slice_byte_length, read_polynomial_vec, write_polynomial_slice,
SerdeCurveAffine, SerdePrimeField,
},
poly::{Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial},
poly::{Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial},
SerdeFormat,
};

Expand Down Expand Up @@ -138,10 +140,14 @@ where
C::Scalar: SerdePrimeField,
{
/// Reads proving key for a single permutation argument from buffer using `Polynomial::read`.
pub(super) fn read<R: io::Read>(reader: &mut R, format: SerdeFormat) -> io::Result<Self> {
pub(super) fn read<R: io::Read>(
reader: &mut R,
format: SerdeFormat,
domain: &EvaluationDomain<C::Scalar>,
p: &Argument,
) -> io::Result<Self> {
let permutations = read_polynomial_vec(reader, format)?;
let polys = read_polynomial_vec(reader, format)?;
let cosets = read_polynomial_vec(reader, format)?;
let (polys, cosets) = compute_polys_and_cosets::<C>(domain, p, &permutations);
Ok(ProvingKey {
permutations,
polys,
Expand All @@ -156,8 +162,6 @@ where
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(())
}
}
Expand All @@ -166,7 +170,5 @@ impl<C: CurveAffine> ProvingKey<C> {
/// Gets the total number of bytes in the serialization of `self`
pub(super) fn bytes_length(&self) -> usize {
polynomial_slice_byte_length(&self.permutations)
+ polynomial_slice_byte_length(&self.polys)
+ polynomial_slice_byte_length(&self.cosets)
}
}
57 changes: 35 additions & 22 deletions halo2_proofs/src/plonk/permutation/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
plonk::{Any, Column, Error},
poly::{
commitment::{Blind, Params},
EvaluationDomain,
Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial,
},
};

Expand Down Expand Up @@ -368,27 +368,7 @@ pub(crate) fn build_pk<'params, C: CurveAffine, P: Params<'params, C>>(
});
}

let mut polys = vec![domain.empty_coeff(); p.columns.len()];
{
parallelize(&mut polys, |o, start| {
for (x, poly) in o.iter_mut().enumerate() {
let i = start + x;
let permutation_poly = permutations[i].clone();
*poly = domain.lagrange_to_coeff(permutation_poly);
}
});
}

let mut cosets = vec![domain.empty_extended(); p.columns.len()];
{
parallelize(&mut cosets, |o, start| {
for (x, coset) in o.iter_mut().enumerate() {
let i = start + x;
let poly = polys[i].clone();
*coset = domain.coeff_to_extended(poly);
}
});
}
let (polys, cosets) = compute_polys_and_cosets::<C>(domain, p, &permutations);

ProvingKey {
permutations,
Expand Down Expand Up @@ -458,3 +438,36 @@ pub(crate) fn build_vk<'params, C: CurveAffine, P: Params<'params, C>>(

VerifyingKey { commitments }
}

#[allow(clippy::type_complexity)]
pub(crate) fn compute_polys_and_cosets<C: CurveAffine>(
domain: &EvaluationDomain<C::Scalar>,
p: &Argument,
permutations: &[Polynomial<C::Scalar, LagrangeCoeff>],
) -> (
Vec<Polynomial<C::Scalar, Coeff>>,
Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
) {
let mut polys = vec![domain.empty_coeff(); p.columns.len()];
{
parallelize(&mut polys, |o, start| {
for (x, poly) in o.iter_mut().enumerate() {
let i = start + x;
let permutation_poly = permutations[i].clone();
*poly = domain.lagrange_to_coeff(permutation_poly);
}
});
}

let mut cosets = vec![domain.empty_extended(); p.columns.len()];
{
parallelize(&mut cosets, |o, start| {
for (x, coset) in o.iter_mut().enumerate() {
let i = start + x;
let poly = polys[i].clone();
*coset = domain.coeff_to_extended(poly);
}
});
}
(polys, cosets)
}
Loading