Skip to content

Commit

Permalink
Port verify_proof to fe-be split
Browse files Browse the repository at this point in the history
  • Loading branch information
ed255 committed Dec 15, 2023
1 parent 98759c3 commit 9ee0ced
Show file tree
Hide file tree
Showing 13 changed files with 930 additions and 178 deletions.
107 changes: 106 additions & 1 deletion halo2_proofs/src/plonk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::helpers::{
};
use crate::poly::{
Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, PinnedEvaluationDomain,
Polynomial,
Polynomial, Rotation,
};
use crate::transcript::{ChallengeScalar, EncodedChallenge, Transcript};
use crate::SerdeFormat;
Expand Down Expand Up @@ -43,6 +43,108 @@ pub use verifier::*;
use evaluation::Evaluator;
use std::io;

/// List of queries (columns and rotations) used by a circuit
#[derive(Debug, Clone)]
pub struct Queries {
/// List of unique advice queries
pub advice: Vec<(Column<Advice>, Rotation)>,
/// List of unique instance queries
pub instance: Vec<(Column<Instance>, Rotation)>,
/// List of unique fixed queries
pub fixed: Vec<(Column<Fixed>, Rotation)>,
/// Contains an integer for each advice column
/// identifying how many distinct queries it has
/// so far; should be same length as cs.num_advice_columns.
pub num_advice_queries: Vec<usize>,
}

impl Queries {
/// 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
}

pub(crate) fn get_advice_query_index(&self, column: Column<Advice>, at: Rotation) -> usize {
for (index, advice_query) in self.advice.iter().enumerate() {
if advice_query == &(column, at) {
return index;
}
}

panic!("get_advice_query_index called for non-existent query");
}

pub(crate) fn get_fixed_query_index(&self, column: Column<Fixed>, at: Rotation) -> usize {
for (index, fixed_query) in self.fixed.iter().enumerate() {
if fixed_query == &(column, at) {
return index;
}
}

panic!("get_fixed_query_index called for non-existent query");
}

pub(crate) fn get_instance_query_index(&self, column: Column<Instance>, at: Rotation) -> usize {
for (index, instance_query) in self.instance.iter().enumerate() {
if instance_query == &(column, at) {
return index;
}
}

panic!("get_instance_query_index called for non-existent query");
}

pub(crate) fn get_any_query_index(&self, column: Column<Any>, at: Rotation) -> usize {
match column.column_type() {
Any::Advice(_) => {
self.get_advice_query_index(Column::<Advice>::try_from(column).unwrap(), at)
}
Any::Fixed => {
self.get_fixed_query_index(Column::<Fixed>::try_from(column).unwrap(), at)
}
Any::Instance => {
self.get_instance_query_index(Column::<Instance>::try_from(column).unwrap(), at)
}
}
}
}

/// This is a verifying key which allows for the verification of proofs for a
/// particular circuit.
#[derive(Clone, Debug)]
Expand All @@ -51,6 +153,7 @@ pub struct VerifyingKeyV2<C: CurveAffine> {
fixed_commitments: Vec<C>,
permutation: permutation::VerifyingKey<C>,
cs: ConstraintSystemV2Backend<C::Scalar>,
queries: Queries,
/// Cached maximum degree of `cs` (which doesn't change after construction).
cs_degree: usize,
/// The representative of this `VerifyingKey` in transcripts.
Expand All @@ -69,12 +172,14 @@ impl<C: CurveAffine> VerifyingKeyV2<C> {
{
// Compute cached values.
let cs_degree = cs.degree();
let queries = cs.collect_queries();

let mut vk = Self {
domain,
fixed_commitments,
permutation,
cs,
queries,
cs_degree,
// Temporary, this is not pinned.
transcript_repr: C::Scalar::ZERO,
Expand Down
134 changes: 88 additions & 46 deletions halo2_proofs/src/plonk/circuit.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{lookup, permutation, shuffle, Assigned, Error};
use super::{lookup, permutation, shuffle, Assigned, Error, Queries};
use crate::circuit::layouter::SyncDeps;
use crate::dev::metadata;
use crate::{
Expand All @@ -9,6 +9,7 @@ use core::cmp::max;
use core::ops::{Add, Mul};
use ff::Field;
use sealed::SealedPhase;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::fmt::Debug;
use std::iter::{Product, Sum};
Expand Down Expand Up @@ -1587,6 +1588,48 @@ pub struct CompiledCircuitV2<F: Field> {
pub(crate) cs: ConstraintSystemV2Backend<F>,
}

struct QueriesSet {
advice: BTreeSet<(Column<Advice>, Rotation)>,
instance: BTreeSet<(Column<Instance>, Rotation)>,
fixed: BTreeSet<(Column<Fixed>, Rotation)>,
}

fn collect_queries<F: Field>(expr: &Expression<F>, queries: &mut QueriesSet) {
match expr {
Expression::Constant(_) => (),
Expression::Selector(_selector) => {
panic!("no Selector should arrive to the Backend");
}
Expression::Fixed(query) => {
queries
.fixed
.insert((Column::new(query.column_index, Fixed), query.rotation));
}
Expression::Advice(query) => {
queries.advice.insert((
Column::new(query.column_index, Advice { phase: query.phase }),
query.rotation,
));
}
Expression::Instance(query) => {
queries
.instance
.insert((Column::new(query.column_index, Instance), query.rotation));
}
Expression::Challenge(_) => (),
Expression::Negated(a) => collect_queries(a, queries),
Expression::Sum(a, b) => {
collect_queries(a, queries);
collect_queries(b, queries);
}
Expression::Product(a, b) => {
collect_queries(a, queries);
collect_queries(b, queries);
}
Expression::Scaled(a, _) => collect_queries(a, queries),
};
}

/// This is a description of the circuit environment, such as the gate, column and
/// permutation arrangements.
#[derive(Debug, Clone)]
Expand All @@ -1611,10 +1654,6 @@ pub struct ConstraintSystemV2Backend<F: Field> {
// pub(crate) selector_map: Vec<Column<Fixed>>,
pub(crate) gates: Vec<GateV2Backend<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)>,

Expand Down Expand Up @@ -1683,47 +1722,6 @@ impl<F: Field> ConstraintSystemV2Backend<F> {
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
}

pub(crate) fn phases(&self) -> Vec<u8> {
let max_phase = self
.advice_column_phase
Expand All @@ -1733,6 +1731,50 @@ impl<F: Field> ConstraintSystemV2Backend<F> {
.unwrap_or_default();
(0..=max_phase).collect()
}

pub(crate) fn collect_queries(&self) -> Queries {
let mut queries = QueriesSet {
advice: BTreeSet::new(),
instance: BTreeSet::new(),
fixed: BTreeSet::new(),
};
let mut num_advice_queries = vec![0; self.num_advice_columns];

for gate in &self.gates {
for expr in gate.polynomials() {
collect_queries(expr, &mut queries);
}
}
for lookup in &self.lookups {
for expr in lookup
.input_expressions
.iter()
.chain(lookup.table_expressions.iter())
{
collect_queries(expr, &mut queries);
}
}
for shuffle in &self.shuffles {
for expr in shuffle
.input_expressions
.iter()
.chain(shuffle.shuffle_expressions.iter())
{
collect_queries(expr, &mut queries);
}
}

for (column, _) in queries.advice.iter() {
num_advice_queries[column.index()] += 1;
}

Queries {
advice: queries.advice.into_iter().collect(),
instance: queries.instance.into_iter().collect(),
fixed: queries.fixed.into_iter().collect(),
num_advice_queries,
}
}
}

/// This is a description of the circuit environment, such as the gate, column and
Expand Down
4 changes: 3 additions & 1 deletion halo2_proofs/src/plonk/evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ impl<C: CurveAffine> Evaluator<C> {
}

/// Creates a new evaluation structure
// TODO: Remove
pub fn new(cs: &ConstraintSystem<C::ScalarExt>) -> Self {
let mut ev = Evaluator::default();

Expand Down Expand Up @@ -475,7 +476,7 @@ impl<C: CurveAffine> Evaluator<C> {
// Permutations
let sets = &permutation.sets;
if !sets.is_empty() {
let blinding_factors = pk.vk.cs.blinding_factors();
let blinding_factors = pk.vk.queries.blinding_factors();
let last_rotation = Rotation(-((blinding_factors + 1) as i32));
let chunk_len = pk.vk.cs.degree() - 2;
let delta_start = beta * &C::Scalar::ZETA;
Expand Down Expand Up @@ -695,6 +696,7 @@ impl<C: CurveAffine> Evaluator<C> {
}

/// Evaluate h poly
// TODO: Remove
#[allow(clippy::too_many_arguments)]
pub(in crate::plonk) fn evaluate_h(
&self,
Expand Down
13 changes: 9 additions & 4 deletions halo2_proofs/src/plonk/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,10 @@ where
C::Scalar: FromUniformBytes<64>,
{
let cs = &circuit.cs;
let queries = cs.collect_queries();
let domain = EvaluationDomain::new(cs.degree() as u32, params.k());

if (params.n() as usize) < cs.minimum_rows() {
if (params.n() as usize) < queries.minimum_rows() {
return Err(Error::not_enough_rows_available(params.k()));
}

Expand Down Expand Up @@ -343,7 +344,7 @@ where
{
let cs = &circuit.cs;

if (params.n() as usize) < cs.minimum_rows() {
if (params.n() as usize) < vk.queries.minimum_rows() {
return Err(Error::not_enough_rows_available(params.k()));
}

Expand Down Expand Up @@ -376,7 +377,11 @@ where
// Compute l_blind(X) which evaluates to 1 for each blinding factor row
// and 0 otherwise over the domain.
let mut l_blind = vk.domain.empty_lagrange();
for evaluation in l_blind[..].iter_mut().rev().take(cs.blinding_factors()) {
for evaluation in l_blind[..]
.iter_mut()
.rev()
.take(vk.queries.blinding_factors())
{
*evaluation = C::Scalar::ONE;
}
let l_blind = vk.domain.lagrange_to_coeff(l_blind);
Expand All @@ -385,7 +390,7 @@ 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;
l_last[params.n() as usize - vk.queries.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 Down
Loading

0 comments on commit 9ee0ced

Please sign in to comment.