mirror of https://github.com/zcash/halo2.git
Port verify_proof to fe-be split
This commit is contained in:
parent
98759c33cd
commit
9ee0ced481
|
@ -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;
|
||||
|
@ -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)]
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
|
|
@ -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::{
|
||||
|
@ -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};
|
||||
|
@ -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)]
|
||||
|
@ -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)>,
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -166,6 +166,7 @@ impl<F: WithSmallOrderMulGroup<3>> Argument<F> {
|
|||
/// - constructs Permuted<C> struct using permuted_input_value = A', and
|
||||
/// permuted_table_expression = S'.
|
||||
/// The Permuted<C> struct is used to update the Lookup, and is then returned.
|
||||
// TODO: Remove
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn commit_permuted<
|
||||
'a,
|
||||
|
@ -286,7 +287,7 @@ impl<C: CurveAffine> Permuted<C> {
|
|||
mut rng: R,
|
||||
transcript: &mut T,
|
||||
) -> Result<Committed<C>, Error> {
|
||||
let blinding_factors = pk.vk.cs.blinding_factors();
|
||||
let blinding_factors = pk.vk.queries.blinding_factors();
|
||||
// Goal is to compute the products of fractions
|
||||
//
|
||||
// Numerator: (\theta^{m-1} a_0(\omega^i) + \theta^{m-2} a_1(\omega^i) + ... + \theta a_{m-2}(\omega^i) + a_{m-1}(\omega^i) + \beta)
|
||||
|
@ -587,6 +588,7 @@ impl<C: CurveAffine> Committed<C> {
|
|||
Ok(Evaluated { constructed: self })
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
||||
self,
|
||||
pk: &ProvingKey<C>,
|
||||
|
@ -661,6 +663,7 @@ impl<C: CurveAffine> Evaluated<C> {
|
|||
}))
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
pub(in crate::plonk) fn open<'a>(
|
||||
&'a self,
|
||||
pk: &'a ProvingKey<C>,
|
||||
|
@ -720,7 +723,7 @@ fn permute_expression_pair_v2<'params, C: CurveAffine, P: Params<'params, C>, R:
|
|||
input_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
|
||||
table_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
|
||||
) -> Result<ExpressionPair<C::Scalar>, Error> {
|
||||
let blinding_factors = pk.vk.cs.blinding_factors();
|
||||
let blinding_factors = pk.vk.queries.blinding_factors();
|
||||
let usable_rows = params.n() as usize - (blinding_factors + 1);
|
||||
|
||||
let mut permuted_input_expression: Vec<C::Scalar> = input_expression.to_vec();
|
||||
|
@ -804,6 +807,7 @@ fn permute_expression_pair_v2<'params, C: CurveAffine, P: Params<'params, C>, R:
|
|||
/// - the first row in a sequence of like values in A' is the row
|
||||
/// that has the corresponding value in S'.
|
||||
/// This method returns (A', S') if no errors are encountered.
|
||||
// TODO: Remove
|
||||
fn permute_expression_pair<'params, C: CurveAffine, P: Params<'params, C>, R: RngCore>(
|
||||
pk: &ProvingKey<C>,
|
||||
params: &P,
|
||||
|
|
|
@ -6,7 +6,7 @@ use super::super::{
|
|||
use super::Argument;
|
||||
use crate::{
|
||||
arithmetic::CurveAffine,
|
||||
plonk::{Error, VerifyingKey},
|
||||
plonk::{Error, VerifyingKey, VerifyingKeyV2},
|
||||
poly::{commitment::MSM, Rotation, VerifierQuery},
|
||||
transcript::{EncodedChallenge, TranscriptRead},
|
||||
};
|
||||
|
@ -168,6 +168,49 @@ impl<C: CurveAffine> Evaluated<C> {
|
|||
))
|
||||
}
|
||||
|
||||
// NOTE: Copy of queries with VerifyingKeyV2
|
||||
pub(in crate::plonk) fn queries_v2<'r, M: MSM<C> + 'r>(
|
||||
&'r self,
|
||||
vk: &'r VerifyingKeyV2<C>,
|
||||
x: ChallengeX<C>,
|
||||
) -> impl Iterator<Item = VerifierQuery<'r, C, M>> + Clone {
|
||||
let x_inv = vk.domain.rotate_omega(*x, Rotation::prev());
|
||||
let x_next = vk.domain.rotate_omega(*x, Rotation::next());
|
||||
|
||||
iter::empty()
|
||||
// Open lookup product commitment at x
|
||||
.chain(Some(VerifierQuery::new_commitment(
|
||||
&self.committed.product_commitment,
|
||||
*x,
|
||||
self.product_eval,
|
||||
)))
|
||||
// Open lookup input commitments at x
|
||||
.chain(Some(VerifierQuery::new_commitment(
|
||||
&self.committed.permuted.permuted_input_commitment,
|
||||
*x,
|
||||
self.permuted_input_eval,
|
||||
)))
|
||||
// Open lookup table commitments at x
|
||||
.chain(Some(VerifierQuery::new_commitment(
|
||||
&self.committed.permuted.permuted_table_commitment,
|
||||
*x,
|
||||
self.permuted_table_eval,
|
||||
)))
|
||||
// Open lookup input commitments at \omega^{-1} x
|
||||
.chain(Some(VerifierQuery::new_commitment(
|
||||
&self.committed.permuted.permuted_input_commitment,
|
||||
x_inv,
|
||||
self.permuted_input_inv_eval,
|
||||
)))
|
||||
// Open lookup product commitment at \omega x
|
||||
.chain(Some(VerifierQuery::new_commitment(
|
||||
&self.committed.product_commitment,
|
||||
x_next,
|
||||
self.product_next_eval,
|
||||
)))
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
pub(in crate::plonk) fn queries<'r, M: MSM<C> + 'r>(
|
||||
&'r self,
|
||||
vk: &'r VerifyingKey<C>,
|
||||
|
|
|
@ -72,7 +72,7 @@ impl Argument {
|
|||
// 3 circuit for the permutation argument.
|
||||
assert!(pk.vk.cs_degree >= 3);
|
||||
let chunk_len = pk.vk.cs_degree - 2;
|
||||
let blinding_factors = pk.vk.cs.blinding_factors();
|
||||
let blinding_factors = pk.vk.queries.blinding_factors();
|
||||
|
||||
// Each column gets its own delta power.
|
||||
let mut deltaomega = C::Scalar::ONE;
|
||||
|
@ -191,6 +191,8 @@ impl Argument {
|
|||
|
||||
Ok(Committed { sets })
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn commit<
|
||||
'params,
|
||||
|
@ -391,7 +393,7 @@ impl<C: CurveAffine> Constructed<C> {
|
|||
transcript: &mut T,
|
||||
) -> Result<Evaluated<C>, Error> {
|
||||
let domain = &pk.vk.domain;
|
||||
let blinding_factors = pk.vk.cs.blinding_factors();
|
||||
let blinding_factors = pk.vk.queries.blinding_factors();
|
||||
|
||||
{
|
||||
let mut sets = self.sets.iter();
|
||||
|
@ -428,6 +430,8 @@ impl<C: CurveAffine> Constructed<C> {
|
|||
|
||||
Ok(Evaluated { constructed: self })
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
||||
self,
|
||||
pk: &plonk::ProvingKey<C>,
|
||||
|
@ -481,7 +485,7 @@ impl<C: CurveAffine> Evaluated<C> {
|
|||
pk: &'a plonk::ProvingKeyV2<C>,
|
||||
x: ChallengeX<C>,
|
||||
) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone {
|
||||
let blinding_factors = pk.vk.cs.blinding_factors();
|
||||
let blinding_factors = pk.vk.queries.blinding_factors();
|
||||
let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next());
|
||||
let x_last = pk
|
||||
.vk
|
||||
|
@ -521,6 +525,8 @@ impl<C: CurveAffine> Evaluated<C> {
|
|||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
pub(in crate::plonk) fn open<'a>(
|
||||
&'a self,
|
||||
pk: &'a plonk::ProvingKey<C>,
|
||||
|
|
|
@ -30,6 +30,29 @@ pub struct Evaluated<C: CurveAffine> {
|
|||
}
|
||||
|
||||
impl Argument {
|
||||
pub(crate) fn read_product_commitments_v2<
|
||||
C: CurveAffine,
|
||||
E: EncodedChallenge<C>,
|
||||
T: TranscriptRead<C, E>,
|
||||
>(
|
||||
&self,
|
||||
vk: &plonk::VerifyingKeyV2<C>,
|
||||
transcript: &mut T,
|
||||
) -> Result<Committed<C>, Error> {
|
||||
let chunk_len = vk.cs_degree - 2;
|
||||
|
||||
let permutation_product_commitments = self
|
||||
.columns
|
||||
.chunks(chunk_len)
|
||||
.map(|_| transcript.read_point())
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(Committed {
|
||||
permutation_product_commitments,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
pub(crate) fn read_product_commitments<
|
||||
C: CurveAffine,
|
||||
E: EncodedChallenge<C>,
|
||||
|
@ -99,6 +122,114 @@ impl<C: CurveAffine> Committed<C> {
|
|||
}
|
||||
|
||||
impl<C: CurveAffine> Evaluated<C> {
|
||||
// NOTE: Copy of expressions with VerifyingKeyV2
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn expressions_v2<'a>(
|
||||
&'a self,
|
||||
vk: &'a plonk::VerifyingKeyV2<C>,
|
||||
p: &'a Argument,
|
||||
common: &'a CommonEvaluated<C>,
|
||||
advice_evals: &'a [C::Scalar],
|
||||
fixed_evals: &'a [C::Scalar],
|
||||
instance_evals: &'a [C::Scalar],
|
||||
l_0: C::Scalar,
|
||||
l_last: C::Scalar,
|
||||
l_blind: C::Scalar,
|
||||
beta: ChallengeBeta<C>,
|
||||
gamma: ChallengeGamma<C>,
|
||||
x: ChallengeX<C>,
|
||||
) -> impl Iterator<Item = C::Scalar> + 'a {
|
||||
let chunk_len = vk.cs_degree - 2;
|
||||
iter::empty()
|
||||
// Enforce only for the first set.
|
||||
// l_0(X) * (1 - z_0(X)) = 0
|
||||
.chain(
|
||||
self.sets
|
||||
.first()
|
||||
.map(|first_set| l_0 * &(C::Scalar::ONE - &first_set.permutation_product_eval)),
|
||||
)
|
||||
// Enforce only for the last set.
|
||||
// l_last(X) * (z_l(X)^2 - z_l(X)) = 0
|
||||
.chain(self.sets.last().map(|last_set| {
|
||||
(last_set.permutation_product_eval.square() - &last_set.permutation_product_eval)
|
||||
* &l_last
|
||||
}))
|
||||
// Except for the first set, enforce.
|
||||
// l_0(X) * (z_i(X) - z_{i-1}(\omega^(last) X)) = 0
|
||||
.chain(
|
||||
self.sets
|
||||
.iter()
|
||||
.skip(1)
|
||||
.zip(self.sets.iter())
|
||||
.map(|(set, last_set)| {
|
||||
(
|
||||
set.permutation_product_eval,
|
||||
last_set.permutation_product_last_eval.unwrap(),
|
||||
)
|
||||
})
|
||||
.map(move |(set, prev_last)| (set - &prev_last) * &l_0),
|
||||
)
|
||||
// And for all the sets we enforce:
|
||||
// (1 - (l_last(X) + l_blind(X))) * (
|
||||
// z_i(\omega X) \prod (p(X) + \beta s_i(X) + \gamma)
|
||||
// - z_i(X) \prod (p(X) + \delta^i \beta X + \gamma)
|
||||
// )
|
||||
.chain(
|
||||
self.sets
|
||||
.iter()
|
||||
.zip(p.columns.chunks(chunk_len))
|
||||
.zip(common.permutation_evals.chunks(chunk_len))
|
||||
.enumerate()
|
||||
.map(move |(chunk_index, ((set, columns), permutation_evals))| {
|
||||
let mut left = set.permutation_product_next_eval;
|
||||
for (eval, permutation_eval) in columns
|
||||
.iter()
|
||||
.map(|&column| match column.column_type() {
|
||||
Any::Advice(_) => {
|
||||
advice_evals
|
||||
[vk.queries.get_any_query_index(column, Rotation::cur())]
|
||||
}
|
||||
Any::Fixed => {
|
||||
fixed_evals
|
||||
[vk.queries.get_any_query_index(column, Rotation::cur())]
|
||||
}
|
||||
Any::Instance => {
|
||||
instance_evals
|
||||
[vk.queries.get_any_query_index(column, Rotation::cur())]
|
||||
}
|
||||
})
|
||||
.zip(permutation_evals.iter())
|
||||
{
|
||||
left *= &(eval + &(*beta * permutation_eval) + &*gamma);
|
||||
}
|
||||
|
||||
let mut right = set.permutation_product_eval;
|
||||
let mut current_delta = (*beta * &*x)
|
||||
* &(<C::Scalar as PrimeField>::DELTA
|
||||
.pow_vartime([(chunk_index * chunk_len) as u64]));
|
||||
for eval in columns.iter().map(|&column| match column.column_type() {
|
||||
Any::Advice(_) => {
|
||||
advice_evals
|
||||
[vk.queries.get_any_query_index(column, Rotation::cur())]
|
||||
}
|
||||
Any::Fixed => {
|
||||
fixed_evals[vk.queries.get_any_query_index(column, Rotation::cur())]
|
||||
}
|
||||
Any::Instance => {
|
||||
instance_evals
|
||||
[vk.queries.get_any_query_index(column, Rotation::cur())]
|
||||
}
|
||||
}) {
|
||||
right *= &(eval + ¤t_delta + &*gamma);
|
||||
current_delta *= &C::Scalar::DELTA;
|
||||
}
|
||||
|
||||
(left - &right) * (C::Scalar::ONE - &(l_last + &l_blind))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn expressions<'a>(
|
||||
&'a self,
|
||||
|
@ -201,6 +332,45 @@ impl<C: CurveAffine> Evaluated<C> {
|
|||
)
|
||||
}
|
||||
|
||||
// NOTE: Copy of queries with VerifyingKeyV2
|
||||
pub(in crate::plonk) fn queries_v2<'r, M: MSM<C> + 'r>(
|
||||
&'r self,
|
||||
vk: &'r plonk::VerifyingKeyV2<C>,
|
||||
x: ChallengeX<C>,
|
||||
) -> impl Iterator<Item = VerifierQuery<'r, C, M>> + Clone {
|
||||
let blinding_factors = vk.queries.blinding_factors();
|
||||
let x_next = vk.domain.rotate_omega(*x, Rotation::next());
|
||||
let x_last = vk
|
||||
.domain
|
||||
.rotate_omega(*x, Rotation(-((blinding_factors + 1) as i32)));
|
||||
|
||||
iter::empty()
|
||||
.chain(self.sets.iter().flat_map(move |set| {
|
||||
iter::empty()
|
||||
// Open permutation product commitments at x and \omega^{-1} x
|
||||
// Open permutation product commitments at x and \omega x
|
||||
.chain(Some(VerifierQuery::new_commitment(
|
||||
&set.permutation_product_commitment,
|
||||
*x,
|
||||
set.permutation_product_eval,
|
||||
)))
|
||||
.chain(Some(VerifierQuery::new_commitment(
|
||||
&set.permutation_product_commitment,
|
||||
x_next,
|
||||
set.permutation_product_next_eval,
|
||||
)))
|
||||
}))
|
||||
// Open it at \omega^{last} x for all but the last set
|
||||
.chain(self.sets.iter().rev().skip(1).flat_map(move |set| {
|
||||
Some(VerifierQuery::new_commitment(
|
||||
&set.permutation_product_commitment,
|
||||
x_last,
|
||||
set.permutation_product_last_eval.unwrap(),
|
||||
))
|
||||
}))
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
pub(in crate::plonk) fn queries<'r, M: MSM<C> + 'r>(
|
||||
&'r self,
|
||||
vk: &'r plonk::VerifyingKey<C>,
|
||||
|
|
|
@ -8,8 +8,8 @@ use std::{collections::HashMap, iter};
|
|||
use super::{
|
||||
circuit::{
|
||||
sealed::{self},
|
||||
Advice, Any, Assignment, Challenge, Circuit, Column, ConstraintSystem,
|
||||
ConstraintSystemV2Backend, Expression, Fixed, FloorPlanner, Instance, Selector,
|
||||
Advice, Any, Assignment, Challenge, Circuit, Column, ConstraintSystem, Fixed, FloorPlanner,
|
||||
Instance, Selector,
|
||||
},
|
||||
lookup, permutation, shuffle, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta,
|
||||
ChallengeX, ChallengeY, Error, ProvingKey, ProvingKeyV2,
|
||||
|
@ -21,7 +21,7 @@ use crate::{
|
|||
plonk::Assigned,
|
||||
poly::{
|
||||
commitment::{Blind, CommitmentScheme, Params, Prover},
|
||||
Basis, Coeff, LagrangeCoeff, Polynomial, ProverQuery, Rotation,
|
||||
Basis, Coeff, LagrangeCoeff, Polynomial, ProverQuery,
|
||||
},
|
||||
};
|
||||
use crate::{
|
||||
|
@ -58,9 +58,9 @@ pub struct ProverV2<
|
|||
// Circuit and setup fields
|
||||
params: &'params Scheme::ParamsProver,
|
||||
pk: &'a ProvingKeyV2<Scheme::Curve>,
|
||||
advice_queries: Vec<(Column<Advice>, Rotation)>,
|
||||
instance_queries: Vec<(Column<Instance>, Rotation)>,
|
||||
fixed_queries: Vec<(Column<Fixed>, Rotation)>,
|
||||
// advice_queries: Vec<(Column<Advice>, Rotation)>,
|
||||
// instance_queries: Vec<(Column<Instance>, Rotation)>,
|
||||
// fixed_queries: Vec<(Column<Fixed>, Rotation)>,
|
||||
phases: Vec<u8>,
|
||||
// State
|
||||
instance: Vec<InstanceSingle<Scheme::Curve>>,
|
||||
|
@ -72,92 +72,6 @@ pub struct ProverV2<
|
|||
_marker: std::marker::PhantomData<(P, E)>,
|
||||
}
|
||||
|
||||
struct Queries {
|
||||
advice: Vec<(Column<Advice>, Rotation)>,
|
||||
instance: Vec<(Column<Instance>, Rotation)>,
|
||||
fixed: Vec<(Column<Fixed>, Rotation)>,
|
||||
}
|
||||
|
||||
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),
|
||||
};
|
||||
}
|
||||
|
||||
fn get_all_queries<F: Field>(cs: &ConstraintSystemV2Backend<F>) -> Queries {
|
||||
let mut queries = QueriesSet {
|
||||
advice: BTreeSet::new(),
|
||||
instance: BTreeSet::new(),
|
||||
fixed: BTreeSet::new(),
|
||||
};
|
||||
|
||||
for gate in &cs.gates {
|
||||
for expr in gate.polynomials() {
|
||||
collect_queries(expr, &mut queries);
|
||||
}
|
||||
}
|
||||
for lookup in &cs.lookups {
|
||||
for expr in lookup
|
||||
.input_expressions
|
||||
.iter()
|
||||
.chain(lookup.table_expressions.iter())
|
||||
{
|
||||
collect_queries(expr, &mut queries);
|
||||
}
|
||||
}
|
||||
for shuffle in &cs.shuffles {
|
||||
for expr in shuffle
|
||||
.input_expressions
|
||||
.iter()
|
||||
.chain(shuffle.shuffle_expressions.iter())
|
||||
{
|
||||
collect_queries(expr, &mut queries);
|
||||
}
|
||||
}
|
||||
|
||||
Queries {
|
||||
advice: queries.advice.into_iter().collect(),
|
||||
instance: queries.instance.into_iter().collect(),
|
||||
fixed: queries.fixed.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
'params,
|
||||
|
@ -186,12 +100,11 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
let queries = get_all_queries(&pk.vk.cs);
|
||||
|
||||
// Hash verification key into transcript
|
||||
pk.vk.hash_into(&mut transcript)?;
|
||||
|
||||
let meta = &pk.vk.cs;
|
||||
let queries = &pk.vk.queries;
|
||||
let phases = meta.phases();
|
||||
|
||||
let domain = &pk.vk.domain;
|
||||
|
@ -204,7 +117,7 @@ impl<
|
|||
.map(|values| {
|
||||
let mut poly = domain.empty_lagrange();
|
||||
assert_eq!(poly.len(), params.n() as usize);
|
||||
if values.len() > (poly.len() - (meta.blinding_factors() + 1)) {
|
||||
if values.len() > (poly.len() - (queries.blinding_factors() + 1)) {
|
||||
return Err(Error::InstanceTooLarge);
|
||||
}
|
||||
for (poly, value) in poly.iter_mut().zip(values.iter()) {
|
||||
|
@ -266,9 +179,6 @@ impl<
|
|||
Ok(ProverV2 {
|
||||
params,
|
||||
pk,
|
||||
advice_queries: queries.advice,
|
||||
instance_queries: queries.instance,
|
||||
fixed_queries: queries.fixed,
|
||||
phases,
|
||||
instance,
|
||||
rng,
|
||||
|
@ -303,6 +213,7 @@ impl<
|
|||
|
||||
let params = self.params;
|
||||
let meta = &self.pk.vk.cs;
|
||||
let queries = &self.pk.vk.queries;
|
||||
|
||||
let transcript = &mut self.transcript;
|
||||
let mut rng = &mut self.rng;
|
||||
|
@ -348,7 +259,7 @@ impl<
|
|||
Option<Polynomial<Assigned<Scheme::Scalar>, LagrangeCoeff>>,
|
||||
>|
|
||||
-> Result<(), Error> {
|
||||
let unusable_rows_start = params.n() as usize - (meta.blinding_factors() + 1);
|
||||
let unusable_rows_start = params.n() as usize - (queries.blinding_factors() + 1);
|
||||
let mut advice_values =
|
||||
batch_invert_assigned::<Scheme::Scalar>(witness.into_iter().flatten().collect());
|
||||
let unblinded_advice: HashSet<usize> =
|
||||
|
@ -428,6 +339,7 @@ impl<
|
|||
{
|
||||
let params = self.params;
|
||||
let meta = &self.pk.vk.cs;
|
||||
let queries = &self.pk.vk.queries;
|
||||
let pk = self.pk;
|
||||
let domain = &self.pk.vk.domain;
|
||||
|
||||
|
@ -598,8 +510,8 @@ impl<
|
|||
// Compute and hash instance evals for the circuit instance
|
||||
for instance in instance.iter() {
|
||||
// Evaluate polynomials at omega^i x
|
||||
let instance_evals: Vec<_> = self
|
||||
.instance_queries
|
||||
let instance_evals: Vec<_> = queries
|
||||
.instance
|
||||
.iter()
|
||||
.map(|&(column, at)| {
|
||||
eval_polynomial(
|
||||
|
@ -619,8 +531,8 @@ impl<
|
|||
// Compute and hash advice evals for the circuit instance
|
||||
for advice in advice.iter() {
|
||||
// Evaluate polynomials at omega^i x
|
||||
let advice_evals: Vec<_> = self
|
||||
.advice_queries
|
||||
let advice_evals: Vec<_> = queries
|
||||
.advice
|
||||
.iter()
|
||||
.map(|&(column, at)| {
|
||||
eval_polynomial(
|
||||
|
@ -637,8 +549,8 @@ impl<
|
|||
}
|
||||
|
||||
// Compute and hash fixed evals
|
||||
let fixed_evals: Vec<_> = self
|
||||
.fixed_queries
|
||||
let fixed_evals: Vec<_> = queries
|
||||
.fixed
|
||||
.iter()
|
||||
.map(|&(column, at)| {
|
||||
eval_polynomial(&pk.fixed_polys[column.index()], domain.rotate_omega(*x, at))
|
||||
|
@ -695,7 +607,7 @@ impl<
|
|||
iter::empty()
|
||||
.chain(
|
||||
P::QUERY_INSTANCE
|
||||
.then_some(self.instance_queries.iter().map(move |&(column, at)| {
|
||||
.then_some(queries.instance.iter().map(move |&(column, at)| {
|
||||
ProverQuery {
|
||||
point: domain.rotate_omega(*x, at),
|
||||
poly: &instance.instance_polys[column.index()],
|
||||
|
@ -705,20 +617,16 @@ impl<
|
|||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.chain(
|
||||
self.advice_queries
|
||||
.iter()
|
||||
.map(move |&(column, at)| ProverQuery {
|
||||
point: domain.rotate_omega(*x, at),
|
||||
poly: &advice.advice_polys[column.index()],
|
||||
blind: advice.advice_blinds[column.index()],
|
||||
}),
|
||||
)
|
||||
.chain(queries.advice.iter().map(move |&(column, at)| ProverQuery {
|
||||
point: domain.rotate_omega(*x, at),
|
||||
poly: &advice.advice_polys[column.index()],
|
||||
blind: advice.advice_blinds[column.index()],
|
||||
}))
|
||||
.chain(permutation.open_v2(pk, x))
|
||||
.chain(lookups.iter().flat_map(move |p| p.open_v2(pk, x)))
|
||||
.chain(shuffles.iter().flat_map(move |p| p.open_v2(pk, x)))
|
||||
})
|
||||
.chain(self.fixed_queries.iter().map(|&(column, at)| ProverQuery {
|
||||
.chain(queries.fixed.iter().map(|&(column, at)| ProverQuery {
|
||||
point: domain.rotate_omega(*x, at),
|
||||
poly: &pk.fixed_polys[column.index()],
|
||||
blind: Blind::default(),
|
||||
|
@ -740,6 +648,7 @@ impl<
|
|||
/// parameters `params` and the proving key [`ProvingKey`] that was
|
||||
/// generated previously for the same circuit. The provided `instances`
|
||||
/// are zero-padded internally.
|
||||
// TODO: Remove
|
||||
pub fn create_proof<
|
||||
'params,
|
||||
Scheme: CommitmentScheme,
|
||||
|
|
|
@ -95,6 +95,7 @@ impl<F: WithSmallOrderMulGroup<3>> Argument<F> {
|
|||
/// [S_0, S_1, ..., S_{m-1}], this method
|
||||
/// - constructs A_compressed = \theta^{m-1} A_0 + theta^{m-2} A_1 + ... + \theta A_{m-2} + A_{m-1}
|
||||
/// and S_compressed = \theta^{m-1} S_0 + theta^{m-2} S_1 + ... + \theta S_{m-2} + S_{m-1},
|
||||
// TODO: Remove
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compress<'a, 'params: 'a, C, P: Params<'params, C>>(
|
||||
&self,
|
||||
|
@ -187,7 +188,7 @@ impl<F: WithSmallOrderMulGroup<3>> Argument<F> {
|
|||
challenges,
|
||||
);
|
||||
|
||||
let blinding_factors = pk.vk.cs.blinding_factors();
|
||||
let blinding_factors = pk.vk.queries.blinding_factors();
|
||||
|
||||
let mut shuffle_product = vec![C::Scalar::ZERO; params.n() as usize];
|
||||
parallelize(&mut shuffle_product, |shuffle_product, start| {
|
||||
|
@ -259,6 +260,7 @@ impl<F: WithSmallOrderMulGroup<3>> Argument<F> {
|
|||
/// constructs the grand product polynomial over the shuffle.
|
||||
/// The grand product polynomial is used to populate the Product<C> struct.
|
||||
/// The Product<C> struct is added to the Shuffle and finally returned by the method.
|
||||
// TODO: Remove
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn commit_product<
|
||||
'a,
|
||||
|
@ -391,6 +393,7 @@ impl<C: CurveAffine> Committed<C> {
|
|||
Ok(Evaluated { constructed: self })
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
||||
self,
|
||||
pk: &ProvingKey<C>,
|
||||
|
@ -439,6 +442,7 @@ impl<C: CurveAffine> Evaluated<C> {
|
|||
}))
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
pub(in crate::plonk) fn open<'a>(
|
||||
&'a self,
|
||||
pk: &'a ProvingKey<C>,
|
||||
|
|
|
@ -4,7 +4,7 @@ use super::super::{circuit::Expression, ChallengeGamma, ChallengeTheta, Challeng
|
|||
use super::Argument;
|
||||
use crate::{
|
||||
arithmetic::CurveAffine,
|
||||
plonk::{Error, VerifyingKey},
|
||||
plonk::{Error, VerifyingKey, VerifyingKeyV2},
|
||||
poly::{commitment::MSM, Rotation, VerifierQuery},
|
||||
transcript::{EncodedChallenge, TranscriptRead},
|
||||
};
|
||||
|
@ -114,6 +114,30 @@ impl<C: CurveAffine> Evaluated<C> {
|
|||
)
|
||||
}
|
||||
|
||||
// NOTE: Copy of queries with VerifyingKeyV2
|
||||
pub(in crate::plonk) fn queries_v2<'r, M: MSM<C> + 'r>(
|
||||
&'r self,
|
||||
vk: &'r VerifyingKeyV2<C>,
|
||||
x: ChallengeX<C>,
|
||||
) -> impl Iterator<Item = VerifierQuery<'r, C, M>> + Clone {
|
||||
let x_next = vk.domain.rotate_omega(*x, Rotation::next());
|
||||
|
||||
iter::empty()
|
||||
// Open shuffle product commitment at x
|
||||
.chain(Some(VerifierQuery::new_commitment(
|
||||
&self.committed.product_commitment,
|
||||
*x,
|
||||
self.product_eval,
|
||||
)))
|
||||
// Open shuffle product commitment at \omega x
|
||||
.chain(Some(VerifierQuery::new_commitment(
|
||||
&self.committed.product_commitment,
|
||||
x_next,
|
||||
self.product_next_eval,
|
||||
)))
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
pub(in crate::plonk) fn queries<'r, M: MSM<C> + 'r>(
|
||||
&'r self,
|
||||
vk: &'r VerifyingKey<C>,
|
||||
|
|
|
@ -4,7 +4,7 @@ use ff::Field;
|
|||
|
||||
use crate::{
|
||||
arithmetic::CurveAffine,
|
||||
plonk::{Error, VerifyingKey},
|
||||
plonk::{Error, VerifyingKey, VerifyingKeyV2},
|
||||
poly::{
|
||||
commitment::{Params, MSM},
|
||||
VerifierQuery,
|
||||
|
@ -53,6 +53,22 @@ impl<C: CurveAffine> Argument<C> {
|
|||
}
|
||||
|
||||
impl<C: CurveAffine> Committed<C> {
|
||||
pub(in crate::plonk) fn read_commitments_after_y_v2<
|
||||
E: EncodedChallenge<C>,
|
||||
T: TranscriptRead<C, E>,
|
||||
>(
|
||||
self,
|
||||
vk: &VerifyingKeyV2<C>,
|
||||
transcript: &mut T,
|
||||
) -> Result<Constructed<C>, Error> {
|
||||
// Obtain a commitment to h(X) in the form of multiple pieces of degree n - 1
|
||||
let h_commitments = read_n_points(transcript, vk.domain.get_quotient_poly_degree())?;
|
||||
|
||||
Ok(Constructed {
|
||||
h_commitments,
|
||||
random_poly_commitment: self.random_poly_commitment,
|
||||
})
|
||||
}
|
||||
pub(in crate::plonk) fn read_commitments_after_y<
|
||||
E: EncodedChallenge<C>,
|
||||
T: TranscriptRead<C, E>,
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::iter;
|
|||
|
||||
use super::{
|
||||
vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error,
|
||||
VerifyingKey,
|
||||
VerifyingKey, VerifyingKeyV2,
|
||||
};
|
||||
use crate::arithmetic::compute_inner_product;
|
||||
use crate::poly::commitment::{CommitmentScheme, Verifier};
|
||||
|
@ -20,6 +20,428 @@ mod batch;
|
|||
#[cfg(feature = "batch")]
|
||||
pub use batch::BatchVerifier;
|
||||
|
||||
/// Returns a boolean indicating whether or not the proof is valid
|
||||
pub fn verify_proof_v2<
|
||||
'params,
|
||||
Scheme: CommitmentScheme,
|
||||
V: Verifier<'params, Scheme>,
|
||||
E: EncodedChallenge<Scheme::Curve>,
|
||||
T: TranscriptRead<Scheme::Curve, E>,
|
||||
Strategy: VerificationStrategy<'params, Scheme, V>,
|
||||
>(
|
||||
params: &'params Scheme::ParamsVerifier,
|
||||
vk: &VerifyingKeyV2<Scheme::Curve>,
|
||||
strategy: Strategy,
|
||||
instances: &[&[&[Scheme::Scalar]]],
|
||||
transcript: &mut T,
|
||||
) -> Result<Strategy::Output, Error>
|
||||
where
|
||||
Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>,
|
||||
{
|
||||
// Check that instances matches the expected number of instance columns
|
||||
for instances in instances.iter() {
|
||||
if instances.len() != vk.cs.num_instance_columns {
|
||||
return Err(Error::InvalidInstances);
|
||||
}
|
||||
}
|
||||
|
||||
let instance_commitments = if V::QUERY_INSTANCE {
|
||||
instances
|
||||
.iter()
|
||||
.map(|instance| {
|
||||
instance
|
||||
.iter()
|
||||
.map(|instance| {
|
||||
if instance.len()
|
||||
> params.n() as usize - (vk.queries.blinding_factors() + 1)
|
||||
{
|
||||
return Err(Error::InstanceTooLarge);
|
||||
}
|
||||
let mut poly = instance.to_vec();
|
||||
poly.resize(params.n() as usize, Scheme::Scalar::ZERO);
|
||||
let poly = vk.domain.lagrange_from_vec(poly);
|
||||
|
||||
Ok(params.commit_lagrange(&poly, Blind::default()).to_affine())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
} else {
|
||||
vec![vec![]; instances.len()]
|
||||
};
|
||||
|
||||
let num_proofs = instance_commitments.len();
|
||||
|
||||
// Hash verification key into transcript
|
||||
vk.hash_into(transcript)?;
|
||||
|
||||
if V::QUERY_INSTANCE {
|
||||
for instance_commitments in instance_commitments.iter() {
|
||||
// Hash the instance (external) commitments into the transcript
|
||||
for commitment in instance_commitments {
|
||||
transcript.common_point(*commitment)?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for instance in instances.iter() {
|
||||
for instance in instance.iter() {
|
||||
for value in instance.iter() {
|
||||
transcript.common_scalar(*value)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hash the prover's advice commitments into the transcript and squeeze challenges
|
||||
let (advice_commitments, challenges) = {
|
||||
let mut advice_commitments =
|
||||
vec![vec![Scheme::Curve::default(); vk.cs.num_advice_columns]; num_proofs];
|
||||
let mut challenges = vec![Scheme::Scalar::ZERO; vk.cs.num_challenges];
|
||||
|
||||
for current_phase in vk.cs.phases() {
|
||||
for advice_commitments in advice_commitments.iter_mut() {
|
||||
for (phase, commitment) in vk
|
||||
.cs
|
||||
.advice_column_phase
|
||||
.iter()
|
||||
.zip(advice_commitments.iter_mut())
|
||||
{
|
||||
if current_phase == *phase {
|
||||
*commitment = transcript.read_point()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (phase, challenge) in vk.cs.challenge_phase.iter().zip(challenges.iter_mut()) {
|
||||
if current_phase == *phase {
|
||||
*challenge = *transcript.squeeze_challenge_scalar::<()>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(advice_commitments, challenges)
|
||||
};
|
||||
|
||||
// Sample theta challenge for keeping lookup columns linearly independent
|
||||
let theta: ChallengeTheta<_> = transcript.squeeze_challenge_scalar();
|
||||
|
||||
let lookups_permuted = (0..num_proofs)
|
||||
.map(|_| -> Result<Vec<_>, _> {
|
||||
// Hash each lookup permuted commitment
|
||||
vk.cs
|
||||
.lookups
|
||||
.iter()
|
||||
.map(|argument| argument.read_permuted_commitments(transcript))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// Sample beta challenge
|
||||
let beta: ChallengeBeta<_> = transcript.squeeze_challenge_scalar();
|
||||
|
||||
// Sample gamma challenge
|
||||
let gamma: ChallengeGamma<_> = transcript.squeeze_challenge_scalar();
|
||||
|
||||
let permutations_committed = (0..num_proofs)
|
||||
.map(|_| {
|
||||
// Hash each permutation product commitment
|
||||
vk.cs
|
||||
.permutation
|
||||
.read_product_commitments_v2(vk, transcript)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let lookups_committed = lookups_permuted
|
||||
.into_iter()
|
||||
.map(|lookups| {
|
||||
// Hash each lookup product commitment
|
||||
lookups
|
||||
.into_iter()
|
||||
.map(|lookup| lookup.read_product_commitment(transcript))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let shuffles_committed = (0..num_proofs)
|
||||
.map(|_| -> Result<Vec<_>, _> {
|
||||
// Hash each shuffle product commitment
|
||||
vk.cs
|
||||
.shuffles
|
||||
.iter()
|
||||
.map(|argument| argument.read_product_commitment(transcript))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let vanishing = vanishing::Argument::read_commitments_before_y(transcript)?;
|
||||
|
||||
// Sample y challenge, which keeps the gates linearly independent.
|
||||
let y: ChallengeY<_> = transcript.squeeze_challenge_scalar();
|
||||
|
||||
let vanishing = vanishing.read_commitments_after_y_v2(vk, transcript)?;
|
||||
|
||||
// Sample x challenge, which is used to ensure the circuit is
|
||||
// satisfied with high probability.
|
||||
let x: ChallengeX<_> = transcript.squeeze_challenge_scalar();
|
||||
let instance_evals = if V::QUERY_INSTANCE {
|
||||
(0..num_proofs)
|
||||
.map(|_| -> Result<Vec<_>, _> { read_n_scalars(transcript, vk.queries.instance.len()) })
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
} else {
|
||||
let xn = x.pow([params.n()]);
|
||||
let (min_rotation, max_rotation) =
|
||||
vk.queries
|
||||
.instance
|
||||
.iter()
|
||||
.fold((0, 0), |(min, max), (_, rotation)| {
|
||||
if rotation.0 < min {
|
||||
(rotation.0, max)
|
||||
} else if rotation.0 > max {
|
||||
(min, rotation.0)
|
||||
} else {
|
||||
(min, max)
|
||||
}
|
||||
});
|
||||
let max_instance_len = instances
|
||||
.iter()
|
||||
.flat_map(|instance| instance.iter().map(|instance| instance.len()))
|
||||
.max_by(Ord::cmp)
|
||||
.unwrap_or_default();
|
||||
let l_i_s = &vk.domain.l_i_range(
|
||||
*x,
|
||||
xn,
|
||||
-max_rotation..max_instance_len as i32 + min_rotation.abs(),
|
||||
);
|
||||
instances
|
||||
.iter()
|
||||
.map(|instances| {
|
||||
vk.queries
|
||||
.instance
|
||||
.iter()
|
||||
.map(|(column, rotation)| {
|
||||
let instances = instances[column.index()];
|
||||
let offset = (max_rotation - rotation.0) as usize;
|
||||
compute_inner_product(instances, &l_i_s[offset..offset + instances.len()])
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let advice_evals = (0..num_proofs)
|
||||
.map(|_| -> Result<Vec<_>, _> { read_n_scalars(transcript, vk.queries.advice.len()) })
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let fixed_evals = read_n_scalars(transcript, vk.queries.fixed.len())?;
|
||||
|
||||
let vanishing = vanishing.evaluate_after_x(transcript)?;
|
||||
|
||||
let permutations_common = vk.permutation.evaluate(transcript)?;
|
||||
|
||||
let permutations_evaluated = permutations_committed
|
||||
.into_iter()
|
||||
.map(|permutation| permutation.evaluate(transcript))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let lookups_evaluated = lookups_committed
|
||||
.into_iter()
|
||||
.map(|lookups| -> Result<Vec<_>, _> {
|
||||
lookups
|
||||
.into_iter()
|
||||
.map(|lookup| lookup.evaluate(transcript))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let shuffles_evaluated = shuffles_committed
|
||||
.into_iter()
|
||||
.map(|shuffles| -> Result<Vec<_>, _> {
|
||||
shuffles
|
||||
.into_iter()
|
||||
.map(|shuffle| shuffle.evaluate(transcript))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// This check ensures the circuit is satisfied so long as the polynomial
|
||||
// commitments open to the correct values.
|
||||
let vanishing = {
|
||||
// x^n
|
||||
let xn = x.pow([params.n()]);
|
||||
|
||||
let blinding_factors = vk.queries.blinding_factors();
|
||||
let l_evals = vk
|
||||
.domain
|
||||
.l_i_range(*x, xn, (-((blinding_factors + 1) as i32))..=0);
|
||||
assert_eq!(l_evals.len(), 2 + blinding_factors);
|
||||
let l_last = l_evals[0];
|
||||
let l_blind: Scheme::Scalar = l_evals[1..(1 + blinding_factors)]
|
||||
.iter()
|
||||
.fold(Scheme::Scalar::ZERO, |acc, eval| acc + eval);
|
||||
let l_0 = l_evals[1 + blinding_factors];
|
||||
|
||||
// Compute the expected value of h(x)
|
||||
let expressions = advice_evals
|
||||
.iter()
|
||||
.zip(instance_evals.iter())
|
||||
.zip(permutations_evaluated.iter())
|
||||
.zip(lookups_evaluated.iter())
|
||||
.zip(shuffles_evaluated.iter())
|
||||
.flat_map(
|
||||
|((((advice_evals, instance_evals), permutation), lookups), shuffles)| {
|
||||
let challenges = &challenges;
|
||||
let fixed_evals = &fixed_evals;
|
||||
std::iter::empty()
|
||||
// Evaluate the circuit using the custom gates provided
|
||||
.chain(vk.cs.gates.iter().flat_map(move |gate| {
|
||||
gate.polynomials().iter().map(move |poly| {
|
||||
poly.evaluate(
|
||||
&|scalar| scalar,
|
||||
&|_| {
|
||||
panic!("virtual selectors are removed during optimization")
|
||||
},
|
||||
&|query| fixed_evals[query.index.unwrap()],
|
||||
&|query| advice_evals[query.index.unwrap()],
|
||||
&|query| instance_evals[query.index.unwrap()],
|
||||
&|challenge| challenges[challenge.index()],
|
||||
&|a| -a,
|
||||
&|a, b| a + &b,
|
||||
&|a, b| a * &b,
|
||||
&|a, scalar| a * &scalar,
|
||||
)
|
||||
})
|
||||
}))
|
||||
.chain(permutation.expressions_v2(
|
||||
vk,
|
||||
&vk.cs.permutation,
|
||||
&permutations_common,
|
||||
advice_evals,
|
||||
fixed_evals,
|
||||
instance_evals,
|
||||
l_0,
|
||||
l_last,
|
||||
l_blind,
|
||||
beta,
|
||||
gamma,
|
||||
x,
|
||||
))
|
||||
.chain(lookups.iter().zip(vk.cs.lookups.iter()).flat_map(
|
||||
move |(p, argument)| {
|
||||
p.expressions(
|
||||
l_0,
|
||||
l_last,
|
||||
l_blind,
|
||||
argument,
|
||||
theta,
|
||||
beta,
|
||||
gamma,
|
||||
advice_evals,
|
||||
fixed_evals,
|
||||
instance_evals,
|
||||
challenges,
|
||||
)
|
||||
},
|
||||
))
|
||||
.chain(shuffles.iter().zip(vk.cs.shuffles.iter()).flat_map(
|
||||
move |(p, argument)| {
|
||||
p.expressions(
|
||||
l_0,
|
||||
l_last,
|
||||
l_blind,
|
||||
argument,
|
||||
theta,
|
||||
gamma,
|
||||
advice_evals,
|
||||
fixed_evals,
|
||||
instance_evals,
|
||||
challenges,
|
||||
)
|
||||
},
|
||||
))
|
||||
},
|
||||
);
|
||||
|
||||
vanishing.verify(params, expressions, y, xn)
|
||||
};
|
||||
|
||||
let queries = instance_commitments
|
||||
.iter()
|
||||
.zip(instance_evals.iter())
|
||||
.zip(advice_commitments.iter())
|
||||
.zip(advice_evals.iter())
|
||||
.zip(permutations_evaluated.iter())
|
||||
.zip(lookups_evaluated.iter())
|
||||
.zip(shuffles_evaluated.iter())
|
||||
.flat_map(
|
||||
|(
|
||||
(
|
||||
(
|
||||
(
|
||||
((instance_commitments, instance_evals), advice_commitments),
|
||||
advice_evals,
|
||||
),
|
||||
permutation,
|
||||
),
|
||||
lookups,
|
||||
),
|
||||
shuffles,
|
||||
)| {
|
||||
iter::empty()
|
||||
.chain(
|
||||
V::QUERY_INSTANCE
|
||||
.then_some(vk.queries.instance.iter().enumerate().map(
|
||||
move |(query_index, &(column, at))| {
|
||||
VerifierQuery::new_commitment(
|
||||
&instance_commitments[column.index()],
|
||||
vk.domain.rotate_omega(*x, at),
|
||||
instance_evals[query_index],
|
||||
)
|
||||
},
|
||||
))
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.chain(vk.queries.advice.iter().enumerate().map(
|
||||
move |(query_index, &(column, at))| {
|
||||
VerifierQuery::new_commitment(
|
||||
&advice_commitments[column.index()],
|
||||
vk.domain.rotate_omega(*x, at),
|
||||
advice_evals[query_index],
|
||||
)
|
||||
},
|
||||
))
|
||||
.chain(permutation.queries_v2(vk, x))
|
||||
.chain(lookups.iter().flat_map(move |p| p.queries_v2(vk, x)))
|
||||
.chain(shuffles.iter().flat_map(move |p| p.queries_v2(vk, x)))
|
||||
},
|
||||
)
|
||||
.chain(
|
||||
vk.queries
|
||||
.fixed
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(query_index, &(column, at))| {
|
||||
VerifierQuery::new_commitment(
|
||||
&vk.fixed_commitments[column.index()],
|
||||
vk.domain.rotate_omega(*x, at),
|
||||
fixed_evals[query_index],
|
||||
)
|
||||
}),
|
||||
)
|
||||
.chain(permutations_common.queries(&vk.permutation, x))
|
||||
.chain(vanishing.queries(x));
|
||||
|
||||
// We are now convinced the circuit is satisfied so long as the
|
||||
// polynomial commitments open to the correct values.
|
||||
|
||||
let verifier = V::new(params);
|
||||
strategy.process(|msm| {
|
||||
verifier
|
||||
.verify_proof(transcript, queries, msm)
|
||||
.map_err(|_| Error::Opening)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
/// Returns a boolean indicating whether or not the proof is valid
|
||||
pub fn verify_proof<
|
||||
'params,
|
||||
|
|
Loading…
Reference in New Issue