Skip to content

Commit

Permalink
Reimplement keygen_vk for fe-be split as keygen_vk_v2
Browse files Browse the repository at this point in the history
  • Loading branch information
ed255 committed Dec 13, 2023
1 parent 6fc6d7c commit eb5d1aa
Show file tree
Hide file tree
Showing 3 changed files with 293 additions and 3 deletions.
59 changes: 59 additions & 0 deletions halo2_proofs/src/plonk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,

Check warning on line 50 in halo2_proofs/src/plonk.rs

View workflow job for this annotation

GitHub Actions / Clippy (beta)

fields `domain`, `fixed_commitments`, `permutation`, `cs` and `cs_degree` are never read

warning: fields `domain`, `fixed_commitments`, `permutation`, `cs` and `cs_degree` are never read --> halo2_proofs/src/plonk.rs:50:5 | 49 | pub struct VerifyingKeyV2<C: CurveAffine> { | -------------- fields in this struct 50 | domain: EvaluationDomain<C::Scalar>, | ^^^^^^ 51 | fixed_commitments: Vec<C>, | ^^^^^^^^^^^^^^^^^ 52 | permutation: permutation::VerifyingKey<C>, | ^^^^^^^^^^^ 53 | cs: ConstraintSystemV2Backend<C::Scalar>, | ^^ 54 | /// Cached maximum degree of `cs` (which doesn't change after construction). 55 | cs_degree: usize, | ^^^^^^^^^ | = note: `VerifyingKeyV2` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis = note: `#[warn(dead_code)]` on by default

Check warning on line 50 in halo2_proofs/src/plonk.rs

View workflow job for this annotation

GitHub Actions / Clippy (beta)

fields `domain`, `fixed_commitments`, `permutation`, `cs` and `cs_degree` are never read

warning: fields `domain`, `fixed_commitments`, `permutation`, `cs` and `cs_degree` are never read --> halo2_proofs/src/plonk.rs:50:5 | 49 | pub struct VerifyingKeyV2<C: CurveAffine> { | -------------- fields in this struct 50 | domain: EvaluationDomain<C::Scalar>, | ^^^^^^ 51 | fixed_commitments: Vec<C>, | ^^^^^^^^^^^^^^^^^ 52 | permutation: permutation::VerifyingKey<C>, | ^^^^^^^^^^^ 53 | cs: ConstraintSystemV2Backend<C::Scalar>, | ^^ 54 | /// Cached maximum degree of `cs` (which doesn't change after construction). 55 | cs_degree: usize, | ^^^^^^^^^ | = note: `VerifyingKeyV2` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis = note: `#[warn(dead_code)]` on by default
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)]
Expand Down
157 changes: 156 additions & 1 deletion halo2_proofs/src/plonk/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,

Check warning on line 1569 in halo2_proofs/src/plonk/circuit.rs

View workflow job for this annotation

GitHub Actions / Clippy (beta)

multiple fields are never read

warning: multiple fields are never read --> halo2_proofs/src/plonk/circuit.rs:1569:16 | 1568 | pub struct ConstraintSystemV2Backend<F: Field> { | ------------------------- fields in this struct 1569 | pub(crate) num_fixed_columns: usize, | ^^^^^^^^^^^^^^^^^ 1570 | pub(crate) num_advice_columns: usize, | ^^^^^^^^^^^^^^^^^^ 1571 | pub(crate) num_instance_columns: usize, | ^^^^^^^^^^^^^^^^^^^^ 1572 | // pub(crate) num_selectors: usize, 1573 | pub(crate) num_challenges: usize, | ^^^^^^^^^^^^^^ ... 1576 | pub(crate) unblinded_advice_columns: Vec<usize>, | ^^^^^^^^^^^^^^^^^^^^^^^^ ... 1579 | pub(crate) advice_column_phase: Vec<sealed::Phase>, | ^^^^^^^^^^^^^^^^^^^ 1580 | /// Contains the phase for each challenge. Should have same length as num_challenges. 1581 | pub(crate) challenge_phase: Vec<sealed::Phase>, | ^^^^^^^^^^^^^^^ ... 1608 | pub(crate) general_column_annotations: HashMap<metadata::Column, String>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `ConstraintSystemV2Backend` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis

Check warning on line 1569 in halo2_proofs/src/plonk/circuit.rs

View workflow job for this annotation

GitHub Actions / Clippy (beta)

multiple fields are never read

warning: multiple fields are never read --> halo2_proofs/src/plonk/circuit.rs:1569:16 | 1568 | pub struct ConstraintSystemV2Backend<F: Field> { | ------------------------- fields in this struct 1569 | pub(crate) num_fixed_columns: usize, | ^^^^^^^^^^^^^^^^^ 1570 | pub(crate) num_advice_columns: usize, | ^^^^^^^^^^^^^^^^^^ 1571 | pub(crate) num_instance_columns: usize, | ^^^^^^^^^^^^^^^^^^^^ 1572 | // pub(crate) num_selectors: usize, 1573 | pub(crate) num_challenges: usize, | ^^^^^^^^^^^^^^ ... 1576 | pub(crate) unblinded_advice_columns: Vec<usize>, | ^^^^^^^^^^^^^^^^^^^^^^^^ ... 1579 | pub(crate) advice_column_phase: Vec<sealed::Phase>, | ^^^^^^^^^^^^^^^^^^^ 1580 | /// Contains the phase for each challenge. Should have same length as num_challenges. 1581 | pub(crate) challenge_phase: Vec<sealed::Phase>, | ^^^^^^^^^^^^^^^ ... 1608 | pub(crate) general_column_annotations: HashMap<metadata::Column, String>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `ConstraintSystemV2Backend` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
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)]
Expand Down
80 changes: 78 additions & 2 deletions halo2_proofs/src/plonk/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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>(
Expand Down

0 comments on commit eb5d1aa

Please sign in to comment.