Reimplement keygen_vk for fe-be split as keygen_vk_v2

This commit is contained in:
Eduard S. 2023-12-13 17:37:04 +01:00
parent 6fc6d7ca01
commit eb5d1aadbe
3 changed files with 293 additions and 3 deletions

View File

@ -43,6 +43,65 @@ pub use verifier::*;
use evaluation::Evaluator;
use std::io;
/// This is a verifying key which allows for the verification of proofs for a
/// particular circuit.
#[derive(Clone, Debug)]
pub struct VerifyingKeyV2<C: CurveAffine> {
domain: EvaluationDomain<C::Scalar>,
fixed_commitments: Vec<C>,
permutation: permutation::VerifyingKey<C>,
cs: ConstraintSystemV2Backend<C::Scalar>,
/// Cached maximum degree of `cs` (which doesn't change after construction).
cs_degree: usize,
/// The representative of this `VerifyingKey` in transcripts.
transcript_repr: C::Scalar,
}
impl<C: CurveAffine> VerifyingKeyV2<C> {
fn from_parts(
domain: EvaluationDomain<C::Scalar>,
fixed_commitments: Vec<C>,
permutation: permutation::VerifyingKey<C>,
cs: ConstraintSystemV2Backend<C::Scalar>,
) -> Self
where
C::ScalarExt: FromUniformBytes<64>,
{
// Compute cached values.
let cs_degree = cs.degree();
let mut vk = Self {
domain,
fixed_commitments,
permutation,
cs,
cs_degree,
// Temporary, this is not pinned.
transcript_repr: C::Scalar::ZERO,
};
let mut hasher = Blake2bParams::new()
.hash_length(64)
.personal(b"Halo2-Verify-Key")
.to_state();
// let s = format!("{:?}", vk.pinned());
// TODO(Edu): Is it Ok to not use the pinned Vk here? We removed a lot of stuff from Vk
// and Cs, so maybe we already have the same as in PinnedVerificationKey?
// TODO(Edu): We removed queries information from the ConstraintSystem, so this output will
// definitely be a breaking change.
let s = format!("{:?}", vk);
hasher.update(&(s.len() as u64).to_le_bytes());
hasher.update(s.as_bytes());
// Hash in final Blake2bState
vk.transcript_repr = C::Scalar::from_uniform_bytes(hasher.finalize().as_array());
vk
}
}
/// This is a verifying key which allows for the verification of proofs for a
/// particular circuit.
#[derive(Clone, Debug)]

View File

@ -3,7 +3,7 @@ use crate::circuit::layouter::SyncDeps;
use crate::dev::metadata;
use crate::{
circuit::{Layouter, Region, Value},
poly::Rotation,
poly::{LagrangeCoeff, Polynomial, Rotation},
};
use core::cmp::max;
use core::ops::{Add, Mul};
@ -1545,6 +1545,161 @@ impl<F: Field> Gate<F> {
}
}
/// Data that needs to be preprocessed from a circuit
#[derive(Debug, Clone)]
pub struct PreprocessingV2<F: Field> {
// TODO(Edu): Can we replace this by a simpler structure?
pub(crate) permutation: permutation::keygen::Assembly,
// TODO(Edu): Replace this by Vec<Vec<F>>
pub(crate) fixed: Vec<Polynomial<F, LagrangeCoeff>>,
}
/// This is a description of a low level Plonkish compiled circuit. Contains the Constraint System
/// as well as the fixed columns and copy constraints information.
#[derive(Debug, Clone)]
pub struct CompiledCircuitV2<F: Field> {
pub(crate) preprocessing: PreprocessingV2<F>,
pub(crate) cs: ConstraintSystemV2Backend<F>,
}
/// This is a description of the circuit environment, such as the gate, column and
/// permutation arrangements.
#[derive(Debug, Clone)]
pub struct ConstraintSystemV2Backend<F: Field> {
pub(crate) num_fixed_columns: usize,
pub(crate) num_advice_columns: usize,
pub(crate) num_instance_columns: usize,
// pub(crate) num_selectors: usize,
pub(crate) num_challenges: usize,
/// Contains the index of each advice column that is left unblinded.
pub(crate) unblinded_advice_columns: Vec<usize>,
/// Contains the phase for each advice column. Should have same length as num_advice_columns.
pub(crate) advice_column_phase: Vec<sealed::Phase>,
/// Contains the phase for each challenge. Should have same length as num_challenges.
pub(crate) challenge_phase: Vec<sealed::Phase>,
/// This is a cached vector that maps virtual selectors to the concrete
/// fixed column that they were compressed into. This is just used by dev
/// tooling right now.
// pub(crate) selector_map: Vec<Column<Fixed>>,
pub(crate) gates: Vec<Gate<F>>,
// pub(crate) advice_queries: Vec<(Column<Advice>, Rotation)>,
// Contains an integer for each advice column
// identifying how many distinct queries it has
// so far; should be same length as num_advice_columns.
num_advice_queries: Vec<usize>,
// pub(crate) instance_queries: Vec<(Column<Instance>, Rotation)>,
// pub(crate) fixed_queries: Vec<(Column<Fixed>, Rotation)>,
// Permutation argument for performing equality constraints
pub(crate) permutation: permutation::Argument,
// Vector of lookup arguments, where each corresponds to a sequence of
// input expressions and a sequence of table expressions involved in the lookup.
pub(crate) lookups: Vec<lookup::Argument<F>>,
// Vector of shuffle arguments, where each corresponds to a sequence of
// input expressions and a sequence of shuffle expressions involved in the shuffle.
pub(crate) shuffles: Vec<shuffle::Argument<F>>,
// List of indexes of Fixed columns which are associated to a circuit-general Column tied to their annotation.
pub(crate) general_column_annotations: HashMap<metadata::Column, String>,
// Vector of fixed columns, which can be used to store constant values
// that are copied into advice columns.
// pub(crate) constants: Vec<Column<Fixed>>,
// pub(crate) minimum_degree: Option<usize>,
}
impl<F: Field> ConstraintSystemV2Backend<F> {
/// Compute the degree of the constraint system (the maximum degree of all
/// constraints).
pub fn degree(&self) -> usize {
// The permutation argument will serve alongside the gates, so must be
// accounted for.
let mut degree = self.permutation.required_degree();
// The lookup argument also serves alongside the gates and must be accounted
// for.
degree = std::cmp::max(
degree,
self.lookups
.iter()
.map(|l| l.required_degree())
.max()
.unwrap_or(1),
);
// The lookup argument also serves alongside the gates and must be accounted
// for.
degree = std::cmp::max(
degree,
self.shuffles
.iter()
.map(|l| l.required_degree())
.max()
.unwrap_or(1),
);
// Account for each gate to ensure our quotient polynomial is the
// correct degree and that our extended domain is the right size.
degree = std::cmp::max(
degree,
self.gates
.iter()
.flat_map(|gate| gate.polynomials().iter().map(|poly| poly.degree()))
.max()
.unwrap_or(0),
);
// std::cmp::max(degree, self.minimum_degree.unwrap_or(1))
degree
}
/// Returns the minimum necessary rows that need to exist in order to
/// account for e.g. blinding factors.
pub fn minimum_rows(&self) -> usize {
self.blinding_factors() // m blinding factors
+ 1 // for l_{-(m + 1)} (l_last)
+ 1 // for l_0 (just for extra breathing room for the permutation
// argument, to essentially force a separation in the
// permutation polynomial between the roles of l_last, l_0
// and the interstitial values.)
+ 1 // for at least one row
}
/// Compute the number of blinding factors necessary to perfectly blind
/// each of the prover's witness polynomials.
pub fn blinding_factors(&self) -> usize {
// All of the prover's advice columns are evaluated at no more than
let factors = *self.num_advice_queries.iter().max().unwrap_or(&1);
// distinct points during gate checks.
// - The permutation argument witness polynomials are evaluated at most 3 times.
// - Each lookup argument has independent witness polynomials, and they are
// evaluated at most 2 times.
let factors = std::cmp::max(3, factors);
// Each polynomial is evaluated at most an additional time during
// multiopen (at x_3 to produce q_evals):
let factors = factors + 1;
// h(x) is derived by the other evaluations so it does not reveal
// anything; in fact it does not even appear in the proof.
// h(x_3) is also not revealed; the verifier only learns a single
// evaluation of a polynomial in x_1 which has h(x_3) and another random
// polynomial evaluated at x_3 as coefficients -- this random polynomial
// is "random_poly" in the vanishing argument.
// Add an additional blinding factor as a slight defense against
// off-by-one errors.
factors + 1
}
}
/// This is a description of the circuit environment, such as the gate, column and
/// permutation arrangements.
#[derive(Debug, Clone)]

View File

@ -7,11 +7,12 @@ use group::Curve;
use super::{
circuit::{
Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, FloorPlanner, Instance,
Selector,
Advice, Any, Assignment, Circuit, Column, CompiledCircuitV2, ConstraintSystem, Fixed,
FloorPlanner, Instance, Selector,
},
evaluation::Evaluator,
permutation, Assigned, Challenge, Error, LagrangeCoeff, Polynomial, ProvingKey, VerifyingKey,
VerifyingKeyV2,
};
use crate::{
arithmetic::{parallelize, CurveAffine},
@ -202,6 +203,81 @@ impl<F: Field> Assignment<F> for Assembly<F> {
}
}
/// Generate a `VerifyingKey` from an instance of `CompiledCircuit`.
pub fn keygen_vk_v2<'params, C, P>(
params: &P,
circuit: &CompiledCircuitV2<C::Scalar>,
) -> Result<VerifyingKeyV2<C>, Error>
where
C: CurveAffine,
P: Params<'params, C>,
C::Scalar: FromUniformBytes<64>,
{
let cs = &circuit.cs;
let domain = EvaluationDomain::new(cs.degree() as u32, params.k());
// let (domain, cs, config) = create_domain::<C, ConcreteCircuit>(
// params.k(),
// #[cfg(feature = "circuit-params")]
// circuit.params(),
// );
if (params.n() as usize) < cs.minimum_rows() {
return Err(Error::not_enough_rows_available(params.k()));
}
// let mut assembly: Assembly<C::Scalar> = Assembly {
// k: params.k(),
// fixed: vec![domain.empty_lagrange_assigned(); cs.num_fixed_columns],
// permutation: permutation::keygen::Assembly::new(params.n() as usize, &cs.permutation),
// // selectors: vec![vec![false; params.n() as usize]; cs.num_selectors],
// usable_rows: 0..params.n() as usize - (cs.blinding_factors() + 1),
// _marker: std::marker::PhantomData,
// };
// Synthesize the circuit to obtain URS
// ConcreteCircuit::FloorPlanner::synthesize(
// &mut assembly,
// circuit,
// config,
// cs.constants.clone(),
// )?;
// let mut fixed = batch_invert_assigned(assembly.fixed);
// let (cs, selector_polys) = if compress_selectors {
// cs.compress_selectors(assembly.selectors.clone())
// } else {
// // After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways.
// let selectors = std::mem::take(&mut assembly.selectors);
// cs.directly_convert_selectors_to_fixed(selectors)
// };
// fixed.extend(
// selector_polys
// .into_iter()
// .map(|poly| domain.lagrange_from_vec(poly)),
// );
let permutation_vk =
circuit
.preprocessing
.permutation
.clone()
.build_vk(params, &domain, &cs.permutation);
let fixed_commitments = circuit
.preprocessing
.fixed
.iter()
.map(|poly| params.commit_lagrange(poly, Blind::default()).to_affine())
.collect();
Ok(VerifyingKeyV2::from_parts(
domain,
fixed_commitments,
permutation_vk,
cs.clone(),
))
}
/// Generate a `VerifyingKey` from an instance of `Circuit`.
/// By default, selector compression is turned **off**.
pub fn keygen_vk<'params, C, P, ConcreteCircuit>(