mirror of https://github.com/zcash/halo2.git
Complete create_proof refactor for fe-be split
This commit is contained in:
parent
4510059ae7
commit
1b35fab453
|
@ -1704,8 +1704,8 @@ impl<F: Field> ConstraintSystemV2Backend<F> {
|
|||
let max_phase = self
|
||||
.advice_column_phase
|
||||
.iter()
|
||||
.cloned()
|
||||
.max()
|
||||
.map(|phase| phase.0)
|
||||
.unwrap_or_default();
|
||||
(0..=max_phase).collect()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::multicore;
|
||||
use crate::plonk::{lookup, permutation, Any, ProvingKey};
|
||||
use crate::plonk::{lookup, permutation, Any, ProvingKey, ProvingKeyV2};
|
||||
use crate::poly::Basis;
|
||||
use crate::{
|
||||
arithmetic::{parallelize, CurveAffine},
|
||||
|
@ -383,6 +383,317 @@ impl<C: CurveAffine> Evaluator<C> {
|
|||
ev
|
||||
}
|
||||
|
||||
/// Evaluate h poly
|
||||
// NOTE: Copy of evaluate_h with ProvingKeyV2
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn evaluate_h_v2(
|
||||
&self,
|
||||
pk: &ProvingKeyV2<C>,
|
||||
advice_polys: &[&[Polynomial<C::ScalarExt, Coeff>]],
|
||||
instance_polys: &[&[Polynomial<C::ScalarExt, Coeff>]],
|
||||
challenges: &[C::ScalarExt],
|
||||
y: C::ScalarExt,
|
||||
beta: C::ScalarExt,
|
||||
gamma: C::ScalarExt,
|
||||
theta: C::ScalarExt,
|
||||
lookups: &[Vec<lookup::prover::Committed<C>>],
|
||||
shuffles: &[Vec<shuffle::prover::Committed<C>>],
|
||||
permutations: &[permutation::prover::Committed<C>],
|
||||
) -> Polynomial<C::ScalarExt, ExtendedLagrangeCoeff> {
|
||||
let domain = &pk.vk.domain;
|
||||
let size = domain.extended_len();
|
||||
let rot_scale = 1 << (domain.extended_k() - domain.k());
|
||||
let fixed = &pk.fixed_cosets[..];
|
||||
let extended_omega = domain.get_extended_omega();
|
||||
let isize = size as i32;
|
||||
let one = C::ScalarExt::ONE;
|
||||
let l0 = &pk.l0;
|
||||
let l_last = &pk.l_last;
|
||||
let l_active_row = &pk.l_active_row;
|
||||
let p = &pk.vk.cs.permutation;
|
||||
|
||||
// Calculate the advice and instance cosets
|
||||
let advice: Vec<Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>> = advice_polys
|
||||
.iter()
|
||||
.map(|advice_polys| {
|
||||
advice_polys
|
||||
.iter()
|
||||
.map(|poly| domain.coeff_to_extended(poly.clone()))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
let instance: Vec<Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>> = instance_polys
|
||||
.iter()
|
||||
.map(|instance_polys| {
|
||||
instance_polys
|
||||
.iter()
|
||||
.map(|poly| domain.coeff_to_extended(poly.clone()))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut values = domain.empty_extended();
|
||||
|
||||
// Core expression evaluations
|
||||
let num_threads = multicore::current_num_threads();
|
||||
for ((((advice, instance), lookups), shuffles), permutation) in advice
|
||||
.iter()
|
||||
.zip(instance.iter())
|
||||
.zip(lookups.iter())
|
||||
.zip(shuffles.iter())
|
||||
.zip(permutations.iter())
|
||||
{
|
||||
// Custom gates
|
||||
multicore::scope(|scope| {
|
||||
let chunk_size = (size + num_threads - 1) / num_threads;
|
||||
for (thread_idx, values) in values.chunks_mut(chunk_size).enumerate() {
|
||||
let start = thread_idx * chunk_size;
|
||||
scope.spawn(move |_| {
|
||||
let mut eval_data = self.custom_gates.instance();
|
||||
for (i, value) in values.iter_mut().enumerate() {
|
||||
let idx = start + i;
|
||||
*value = self.custom_gates.evaluate(
|
||||
&mut eval_data,
|
||||
fixed,
|
||||
advice,
|
||||
instance,
|
||||
challenges,
|
||||
&beta,
|
||||
&gamma,
|
||||
&theta,
|
||||
&y,
|
||||
value,
|
||||
idx,
|
||||
rot_scale,
|
||||
isize,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Permutations
|
||||
let sets = &permutation.sets;
|
||||
if !sets.is_empty() {
|
||||
let blinding_factors = pk.vk.cs.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;
|
||||
|
||||
let first_set = sets.first().unwrap();
|
||||
let last_set = sets.last().unwrap();
|
||||
|
||||
// Permutation constraints
|
||||
parallelize(&mut values, |values, start| {
|
||||
let mut beta_term = extended_omega.pow_vartime([start as u64, 0, 0, 0]);
|
||||
for (i, value) in values.iter_mut().enumerate() {
|
||||
let idx = start + i;
|
||||
let r_next = get_rotation_idx(idx, 1, rot_scale, isize);
|
||||
let r_last = get_rotation_idx(idx, last_rotation.0, rot_scale, isize);
|
||||
|
||||
// Enforce only for the first set.
|
||||
// l_0(X) * (1 - z_0(X)) = 0
|
||||
*value = *value * y
|
||||
+ ((one - first_set.permutation_product_coset[idx]) * l0[idx]);
|
||||
// Enforce only for the last set.
|
||||
// l_last(X) * (z_l(X)^2 - z_l(X)) = 0
|
||||
*value = *value * y
|
||||
+ ((last_set.permutation_product_coset[idx]
|
||||
* last_set.permutation_product_coset[idx]
|
||||
- last_set.permutation_product_coset[idx])
|
||||
* l_last[idx]);
|
||||
// Except for the first set, enforce.
|
||||
// l_0(X) * (z_i(X) - z_{i-1}(\omega^(last) X)) = 0
|
||||
for (set_idx, set) in sets.iter().enumerate() {
|
||||
if set_idx != 0 {
|
||||
*value = *value * y
|
||||
+ ((set.permutation_product_coset[idx]
|
||||
- permutation.sets[set_idx - 1].permutation_product_coset
|
||||
[r_last])
|
||||
* l0[idx]);
|
||||
}
|
||||
}
|
||||
// And for all the sets we enforce:
|
||||
// (1 - (l_last(X) + l_blind(X))) * (
|
||||
// z_i(\omega X) \prod_j (p(X) + \beta s_j(X) + \gamma)
|
||||
// - z_i(X) \prod_j (p(X) + \delta^j \beta X + \gamma)
|
||||
// )
|
||||
let mut current_delta = delta_start * beta_term;
|
||||
for ((set, columns), cosets) in sets
|
||||
.iter()
|
||||
.zip(p.columns.chunks(chunk_len))
|
||||
.zip(pk.permutation.cosets.chunks(chunk_len))
|
||||
{
|
||||
let mut left = set.permutation_product_coset[r_next];
|
||||
for (values, permutation) in columns
|
||||
.iter()
|
||||
.map(|&column| match column.column_type() {
|
||||
Any::Advice(_) => &advice[column.index()],
|
||||
Any::Fixed => &fixed[column.index()],
|
||||
Any::Instance => &instance[column.index()],
|
||||
})
|
||||
.zip(cosets.iter())
|
||||
{
|
||||
left *= values[idx] + beta * permutation[idx] + gamma;
|
||||
}
|
||||
|
||||
let mut right = set.permutation_product_coset[idx];
|
||||
for values in columns.iter().map(|&column| match column.column_type() {
|
||||
Any::Advice(_) => &advice[column.index()],
|
||||
Any::Fixed => &fixed[column.index()],
|
||||
Any::Instance => &instance[column.index()],
|
||||
}) {
|
||||
right *= values[idx] + current_delta + gamma;
|
||||
current_delta *= &C::Scalar::DELTA;
|
||||
}
|
||||
|
||||
*value = *value * y + ((left - right) * l_active_row[idx]);
|
||||
}
|
||||
beta_term *= &extended_omega;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Lookups
|
||||
for (n, lookup) in lookups.iter().enumerate() {
|
||||
// Polynomials required for this lookup.
|
||||
// Calculated here so these only have to be kept in memory for the short time
|
||||
// they are actually needed.
|
||||
let product_coset = pk.vk.domain.coeff_to_extended(lookup.product_poly.clone());
|
||||
let permuted_input_coset = pk
|
||||
.vk
|
||||
.domain
|
||||
.coeff_to_extended(lookup.permuted_input_poly.clone());
|
||||
let permuted_table_coset = pk
|
||||
.vk
|
||||
.domain
|
||||
.coeff_to_extended(lookup.permuted_table_poly.clone());
|
||||
|
||||
// Lookup constraints
|
||||
parallelize(&mut values, |values, start| {
|
||||
let lookup_evaluator = &self.lookups[n];
|
||||
let mut eval_data = lookup_evaluator.instance();
|
||||
for (i, value) in values.iter_mut().enumerate() {
|
||||
let idx = start + i;
|
||||
|
||||
let table_value = lookup_evaluator.evaluate(
|
||||
&mut eval_data,
|
||||
fixed,
|
||||
advice,
|
||||
instance,
|
||||
challenges,
|
||||
&beta,
|
||||
&gamma,
|
||||
&theta,
|
||||
&y,
|
||||
&C::ScalarExt::ZERO,
|
||||
idx,
|
||||
rot_scale,
|
||||
isize,
|
||||
);
|
||||
|
||||
let r_next = get_rotation_idx(idx, 1, rot_scale, isize);
|
||||
let r_prev = get_rotation_idx(idx, -1, rot_scale, isize);
|
||||
|
||||
let a_minus_s = permuted_input_coset[idx] - permuted_table_coset[idx];
|
||||
// l_0(X) * (1 - z(X)) = 0
|
||||
*value = *value * y + ((one - product_coset[idx]) * l0[idx]);
|
||||
// l_last(X) * (z(X)^2 - z(X)) = 0
|
||||
*value = *value * y
|
||||
+ ((product_coset[idx] * product_coset[idx] - product_coset[idx])
|
||||
* l_last[idx]);
|
||||
// (1 - (l_last(X) + l_blind(X))) * (
|
||||
// z(\omega X) (a'(X) + \beta) (s'(X) + \gamma)
|
||||
// - z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta)
|
||||
// (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma)
|
||||
// ) = 0
|
||||
*value = *value * y
|
||||
+ ((product_coset[r_next]
|
||||
* (permuted_input_coset[idx] + beta)
|
||||
* (permuted_table_coset[idx] + gamma)
|
||||
- product_coset[idx] * table_value)
|
||||
* l_active_row[idx]);
|
||||
// Check that the first values in the permuted input expression and permuted
|
||||
// fixed expression are the same.
|
||||
// l_0(X) * (a'(X) - s'(X)) = 0
|
||||
*value = *value * y + (a_minus_s * l0[idx]);
|
||||
// Check that each value in the permuted lookup input expression is either
|
||||
// equal to the value above it, or the value at the same index in the
|
||||
// permuted table expression.
|
||||
// (1 - (l_last + l_blind)) * (a′(X) − s′(X))⋅(a′(X) − a′(\omega^{-1} X)) = 0
|
||||
*value = *value * y
|
||||
+ (a_minus_s
|
||||
* (permuted_input_coset[idx] - permuted_input_coset[r_prev])
|
||||
* l_active_row[idx]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Shuffle constraints
|
||||
for (n, shuffle) in shuffles.iter().enumerate() {
|
||||
let product_coset = pk.vk.domain.coeff_to_extended(shuffle.product_poly.clone());
|
||||
|
||||
// Shuffle constraints
|
||||
parallelize(&mut values, |values, start| {
|
||||
let input_evaluator = &self.shuffles[2 * n];
|
||||
let shuffle_evaluator = &self.shuffles[2 * n + 1];
|
||||
let mut eval_data_input = shuffle_evaluator.instance();
|
||||
let mut eval_data_shuffle = shuffle_evaluator.instance();
|
||||
for (i, value) in values.iter_mut().enumerate() {
|
||||
let idx = start + i;
|
||||
|
||||
let input_value = input_evaluator.evaluate(
|
||||
&mut eval_data_input,
|
||||
fixed,
|
||||
advice,
|
||||
instance,
|
||||
challenges,
|
||||
&beta,
|
||||
&gamma,
|
||||
&theta,
|
||||
&y,
|
||||
&C::ScalarExt::ZERO,
|
||||
idx,
|
||||
rot_scale,
|
||||
isize,
|
||||
);
|
||||
|
||||
let shuffle_value = shuffle_evaluator.evaluate(
|
||||
&mut eval_data_shuffle,
|
||||
fixed,
|
||||
advice,
|
||||
instance,
|
||||
challenges,
|
||||
&beta,
|
||||
&gamma,
|
||||
&theta,
|
||||
&y,
|
||||
&C::ScalarExt::ZERO,
|
||||
idx,
|
||||
rot_scale,
|
||||
isize,
|
||||
);
|
||||
|
||||
let r_next = get_rotation_idx(idx, 1, rot_scale, isize);
|
||||
|
||||
// l_0(X) * (1 - z(X)) = 0
|
||||
*value = *value * y + ((one - product_coset[idx]) * l0[idx]);
|
||||
// l_last(X) * (z(X)^2 - z(X)) = 0
|
||||
*value = *value * y
|
||||
+ ((product_coset[idx] * product_coset[idx] - product_coset[idx])
|
||||
* l_last[idx]);
|
||||
// (1 - (l_last(X) + l_blind(X))) * (z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma)) = 0
|
||||
*value = *value * y
|
||||
+ l_active_row[idx]
|
||||
* (product_coset[r_next] * shuffle_value
|
||||
- product_coset[idx] * input_value)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
values
|
||||
}
|
||||
|
||||
/// Evaluate h poly
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn evaluate_h(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::super::{
|
||||
circuit::Expression, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error,
|
||||
ProvingKey,
|
||||
ProvingKey, ProvingKeyV2,
|
||||
};
|
||||
use super::Argument;
|
||||
use crate::plonk::evaluation::evaluate;
|
||||
|
@ -51,6 +51,112 @@ pub(in crate::plonk) struct Evaluated<C: CurveAffine> {
|
|||
}
|
||||
|
||||
impl<F: WithSmallOrderMulGroup<3>> Argument<F> {
|
||||
/// Given a Lookup with input expressions [A_0, A_1, ..., A_{m-1}] and table expressions
|
||||
/// [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},
|
||||
/// - permutes A_compressed and S_compressed using permute_expression_pair() helper,
|
||||
/// obtaining A' and S', and
|
||||
/// - 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.
|
||||
// NOTE: Copy of commit_permuted that uses ProvingKeyV2
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn commit_permuted_v2<
|
||||
'a,
|
||||
'params: 'a,
|
||||
C,
|
||||
P: Params<'params, C>,
|
||||
E: EncodedChallenge<C>,
|
||||
R: RngCore,
|
||||
T: TranscriptWrite<C, E>,
|
||||
>(
|
||||
&self,
|
||||
pk: &ProvingKeyV2<C>,
|
||||
params: &P,
|
||||
domain: &EvaluationDomain<C::Scalar>,
|
||||
theta: ChallengeTheta<C>,
|
||||
advice_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
fixed_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
instance_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
challenges: &'a [C::Scalar],
|
||||
mut rng: R,
|
||||
transcript: &mut T,
|
||||
) -> Result<Permuted<C>, Error>
|
||||
where
|
||||
C: CurveAffine<ScalarExt = F>,
|
||||
C::Curve: Mul<F, Output = C::Curve> + MulAssign<F>,
|
||||
{
|
||||
// Closure to get values of expressions and compress them
|
||||
let compress_expressions = |expressions: &[Expression<C::Scalar>]| {
|
||||
let compressed_expression = expressions
|
||||
.iter()
|
||||
.map(|expression| {
|
||||
pk.vk.domain.lagrange_from_vec(evaluate(
|
||||
expression,
|
||||
params.n() as usize,
|
||||
1,
|
||||
fixed_values,
|
||||
advice_values,
|
||||
instance_values,
|
||||
challenges,
|
||||
))
|
||||
})
|
||||
.fold(domain.empty_lagrange(), |acc, expression| {
|
||||
acc * *theta + &expression
|
||||
});
|
||||
compressed_expression
|
||||
};
|
||||
|
||||
// Get values of input expressions involved in the lookup and compress them
|
||||
let compressed_input_expression = compress_expressions(&self.input_expressions);
|
||||
|
||||
// Get values of table expressions involved in the lookup and compress them
|
||||
let compressed_table_expression = compress_expressions(&self.table_expressions);
|
||||
|
||||
// Permute compressed (InputExpression, TableExpression) pair
|
||||
let (permuted_input_expression, permuted_table_expression) = permute_expression_pair_v2(
|
||||
pk,
|
||||
params,
|
||||
domain,
|
||||
&mut rng,
|
||||
&compressed_input_expression,
|
||||
&compressed_table_expression,
|
||||
)?;
|
||||
|
||||
// Closure to construct commitment to vector of values
|
||||
let mut commit_values = |values: &Polynomial<C::Scalar, LagrangeCoeff>| {
|
||||
let poly = pk.vk.domain.lagrange_to_coeff(values.clone());
|
||||
let blind = Blind(C::Scalar::random(&mut rng));
|
||||
let commitment = params.commit_lagrange(values, blind).to_affine();
|
||||
(poly, blind, commitment)
|
||||
};
|
||||
|
||||
// Commit to permuted input expression
|
||||
let (permuted_input_poly, permuted_input_blind, permuted_input_commitment) =
|
||||
commit_values(&permuted_input_expression);
|
||||
|
||||
// Commit to permuted table expression
|
||||
let (permuted_table_poly, permuted_table_blind, permuted_table_commitment) =
|
||||
commit_values(&permuted_table_expression);
|
||||
|
||||
// Hash permuted input commitment
|
||||
transcript.write_point(permuted_input_commitment)?;
|
||||
|
||||
// Hash permuted table commitment
|
||||
transcript.write_point(permuted_table_commitment)?;
|
||||
|
||||
Ok(Permuted {
|
||||
compressed_input_expression,
|
||||
permuted_input_expression,
|
||||
permuted_input_poly,
|
||||
permuted_input_blind,
|
||||
compressed_table_expression,
|
||||
permuted_table_expression,
|
||||
permuted_table_poly,
|
||||
permuted_table_blind,
|
||||
})
|
||||
}
|
||||
/// Given a Lookup with input expressions [A_0, A_1, ..., A_{m-1}] and table expressions
|
||||
/// [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}
|
||||
|
@ -159,6 +265,151 @@ impl<F: WithSmallOrderMulGroup<3>> Argument<F> {
|
|||
}
|
||||
|
||||
impl<C: CurveAffine> Permuted<C> {
|
||||
/// Given a Lookup with input expressions, table expressions, and the permuted
|
||||
/// input expression and permuted table expression, this method constructs the
|
||||
/// grand product polynomial over the lookup. The grand product polynomial
|
||||
/// is used to populate the Product<C> struct. The Product<C> struct is
|
||||
/// added to the Lookup and finally returned by the method.
|
||||
// NOTE: Copy of commit_permuted with ProvingKeyV2
|
||||
pub(in crate::plonk) fn commit_product_v2<
|
||||
'params,
|
||||
P: Params<'params, C>,
|
||||
E: EncodedChallenge<C>,
|
||||
R: RngCore,
|
||||
T: TranscriptWrite<C, E>,
|
||||
>(
|
||||
self,
|
||||
pk: &ProvingKeyV2<C>,
|
||||
params: &P,
|
||||
beta: ChallengeBeta<C>,
|
||||
gamma: ChallengeGamma<C>,
|
||||
mut rng: R,
|
||||
transcript: &mut T,
|
||||
) -> Result<Committed<C>, Error> {
|
||||
let blinding_factors = pk.vk.cs.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)
|
||||
// * (\theta^{m-1} s_0(\omega^i) + \theta^{m-2} s_1(\omega^i) + ... + \theta s_{m-2}(\omega^i) + s_{m-1}(\omega^i) + \gamma)
|
||||
// Denominator: (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma)
|
||||
//
|
||||
// where a_j(X) is the jth input expression in this lookup,
|
||||
// where a'(X) is the compression of the permuted input expressions,
|
||||
// s_j(X) is the jth table expression in this lookup,
|
||||
// s'(X) is the compression of the permuted table expressions,
|
||||
// and i is the ith row of the expression.
|
||||
let mut lookup_product = vec![C::Scalar::ZERO; params.n() as usize];
|
||||
// Denominator uses the permuted input expression and permuted table expression
|
||||
parallelize(&mut lookup_product, |lookup_product, start| {
|
||||
for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product
|
||||
.iter_mut()
|
||||
.zip(self.permuted_input_expression[start..].iter())
|
||||
.zip(self.permuted_table_expression[start..].iter())
|
||||
{
|
||||
*lookup_product = (*beta + permuted_input_value) * &(*gamma + permuted_table_value);
|
||||
}
|
||||
});
|
||||
|
||||
// Batch invert to obtain the denominators for the lookup product
|
||||
// polynomials
|
||||
lookup_product.iter_mut().batch_invert();
|
||||
|
||||
// Finish the computation of the entire fraction by computing the numerators
|
||||
// (\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)
|
||||
// * (\theta^{m-1} s_0(\omega^i) + \theta^{m-2} s_1(\omega^i) + ... + \theta s_{m-2}(\omega^i) + s_{m-1}(\omega^i) + \gamma)
|
||||
parallelize(&mut lookup_product, |product, start| {
|
||||
for (i, product) in product.iter_mut().enumerate() {
|
||||
let i = i + start;
|
||||
|
||||
*product *= &(self.compressed_input_expression[i] + &*beta);
|
||||
*product *= &(self.compressed_table_expression[i] + &*gamma);
|
||||
}
|
||||
});
|
||||
|
||||
// The product vector is a vector of products of fractions of the form
|
||||
//
|
||||
// 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)
|
||||
// * (\theta^{m-1} s_0(\omega^i) + \theta^{m-2} s_1(\omega^i) + ... + \theta s_{m-2}(\omega^i) + s_{m-1}(\omega^i) + \gamma)
|
||||
// Denominator: (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma)
|
||||
//
|
||||
// where there are m input expressions and m table expressions,
|
||||
// a_j(\omega^i) is the jth input expression in this lookup,
|
||||
// a'j(\omega^i) is the permuted input expression,
|
||||
// s_j(\omega^i) is the jth table expression in this lookup,
|
||||
// s'(\omega^i) is the permuted table expression,
|
||||
// and i is the ith row of the expression.
|
||||
|
||||
// Compute the evaluations of the lookup product polynomial
|
||||
// over our domain, starting with z[0] = 1
|
||||
let z = iter::once(C::Scalar::ONE)
|
||||
.chain(lookup_product)
|
||||
.scan(C::Scalar::ONE, |state, cur| {
|
||||
*state *= &cur;
|
||||
Some(*state)
|
||||
})
|
||||
// Take all rows including the "last" row which should
|
||||
// be a boolean (and ideally 1, else soundness is broken)
|
||||
.take(params.n() as usize - blinding_factors)
|
||||
// Chain random blinding factors.
|
||||
.chain((0..blinding_factors).map(|_| C::Scalar::random(&mut rng)))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(z.len(), params.n() as usize);
|
||||
let z = pk.vk.domain.lagrange_from_vec(z);
|
||||
|
||||
#[cfg(feature = "sanity-checks")]
|
||||
// This test works only with intermediate representations in this method.
|
||||
// It can be used for debugging purposes.
|
||||
{
|
||||
// While in Lagrange basis, check that product is correctly constructed
|
||||
let u = (params.n() as usize) - (blinding_factors + 1);
|
||||
|
||||
// l_0(X) * (1 - z(X)) = 0
|
||||
assert_eq!(z[0], C::Scalar::ONE);
|
||||
|
||||
// z(\omega X) (a'(X) + \beta) (s'(X) + \gamma)
|
||||
// - z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma)
|
||||
for i in 0..u {
|
||||
let mut left = z[i + 1];
|
||||
let permuted_input_value = &self.permuted_input_expression[i];
|
||||
|
||||
let permuted_table_value = &self.permuted_table_expression[i];
|
||||
|
||||
left *= &(*beta + permuted_input_value);
|
||||
left *= &(*gamma + permuted_table_value);
|
||||
|
||||
let mut right = z[i];
|
||||
let mut input_term = self.compressed_input_expression[i];
|
||||
let mut table_term = self.compressed_table_expression[i];
|
||||
|
||||
input_term += &(*beta);
|
||||
table_term += &(*gamma);
|
||||
right *= &(input_term * &table_term);
|
||||
|
||||
assert_eq!(left, right);
|
||||
}
|
||||
|
||||
// l_last(X) * (z(X)^2 - z(X)) = 0
|
||||
// Assertion will fail only when soundness is broken, in which
|
||||
// case this z[u] value will be zero. (bad!)
|
||||
assert_eq!(z[u], C::Scalar::ONE);
|
||||
}
|
||||
|
||||
let product_blind = Blind(C::Scalar::random(rng));
|
||||
let product_commitment = params.commit_lagrange(&z, product_blind).to_affine();
|
||||
let z = pk.vk.domain.lagrange_to_coeff(z);
|
||||
|
||||
// Hash product commitment
|
||||
transcript.write_point(product_commitment)?;
|
||||
|
||||
Ok(Committed::<C> {
|
||||
permuted_input_poly: self.permuted_input_poly,
|
||||
permuted_input_blind: self.permuted_input_blind,
|
||||
permuted_table_poly: self.permuted_table_poly,
|
||||
permuted_table_blind: self.permuted_table_blind,
|
||||
product_poly: z,
|
||||
product_blind,
|
||||
})
|
||||
}
|
||||
/// Given a Lookup with input expressions, table expressions, and the permuted
|
||||
/// input expression and permuted table expression, this method constructs the
|
||||
/// grand product polynomial over the lookup. The grand product polynomial
|
||||
|
@ -306,6 +557,36 @@ impl<C: CurveAffine> Permuted<C> {
|
|||
}
|
||||
|
||||
impl<C: CurveAffine> Committed<C> {
|
||||
pub(in crate::plonk) fn evaluate_v2<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
||||
self,
|
||||
pk: &ProvingKeyV2<C>,
|
||||
x: ChallengeX<C>,
|
||||
transcript: &mut T,
|
||||
) -> Result<Evaluated<C>, Error> {
|
||||
let domain = &pk.vk.domain;
|
||||
let x_inv = domain.rotate_omega(*x, Rotation::prev());
|
||||
let x_next = domain.rotate_omega(*x, Rotation::next());
|
||||
|
||||
let product_eval = eval_polynomial(&self.product_poly, *x);
|
||||
let product_next_eval = eval_polynomial(&self.product_poly, x_next);
|
||||
let permuted_input_eval = eval_polynomial(&self.permuted_input_poly, *x);
|
||||
let permuted_input_inv_eval = eval_polynomial(&self.permuted_input_poly, x_inv);
|
||||
let permuted_table_eval = eval_polynomial(&self.permuted_table_poly, *x);
|
||||
|
||||
// Hash each advice evaluation
|
||||
for eval in iter::empty()
|
||||
.chain(Some(product_eval))
|
||||
.chain(Some(product_next_eval))
|
||||
.chain(Some(permuted_input_eval))
|
||||
.chain(Some(permuted_input_inv_eval))
|
||||
.chain(Some(permuted_table_eval))
|
||||
{
|
||||
transcript.write_scalar(eval)?;
|
||||
}
|
||||
|
||||
Ok(Evaluated { constructed: self })
|
||||
}
|
||||
|
||||
pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
||||
self,
|
||||
pk: &ProvingKey<C>,
|
||||
|
@ -338,6 +619,48 @@ impl<C: CurveAffine> Committed<C> {
|
|||
}
|
||||
|
||||
impl<C: CurveAffine> Evaluated<C> {
|
||||
// NOTE: Copy of open with ProvingKeyV2
|
||||
pub(in crate::plonk) fn open_v2<'a>(
|
||||
&'a self,
|
||||
pk: &'a ProvingKeyV2<C>,
|
||||
x: ChallengeX<C>,
|
||||
) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone {
|
||||
let x_inv = pk.vk.domain.rotate_omega(*x, Rotation::prev());
|
||||
let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next());
|
||||
|
||||
iter::empty()
|
||||
// Open lookup product commitments at x
|
||||
.chain(Some(ProverQuery {
|
||||
point: *x,
|
||||
poly: &self.constructed.product_poly,
|
||||
blind: self.constructed.product_blind,
|
||||
}))
|
||||
// Open lookup input commitments at x
|
||||
.chain(Some(ProverQuery {
|
||||
point: *x,
|
||||
poly: &self.constructed.permuted_input_poly,
|
||||
blind: self.constructed.permuted_input_blind,
|
||||
}))
|
||||
// Open lookup table commitments at x
|
||||
.chain(Some(ProverQuery {
|
||||
point: *x,
|
||||
poly: &self.constructed.permuted_table_poly,
|
||||
blind: self.constructed.permuted_table_blind,
|
||||
}))
|
||||
// Open lookup input commitments at x_inv
|
||||
.chain(Some(ProverQuery {
|
||||
point: x_inv,
|
||||
poly: &self.constructed.permuted_input_poly,
|
||||
blind: self.constructed.permuted_input_blind,
|
||||
}))
|
||||
// Open lookup product commitments at x_next
|
||||
.chain(Some(ProverQuery {
|
||||
point: x_next,
|
||||
poly: &self.constructed.product_poly,
|
||||
blind: self.constructed.product_blind,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(in crate::plonk) fn open<'a>(
|
||||
&'a self,
|
||||
pk: &'a ProvingKey<C>,
|
||||
|
@ -382,6 +705,99 @@ impl<C: CurveAffine> Evaluated<C> {
|
|||
|
||||
type ExpressionPair<F> = (Polynomial<F, LagrangeCoeff>, Polynomial<F, LagrangeCoeff>);
|
||||
|
||||
/// Given a vector of input values A and a vector of table values S,
|
||||
/// this method permutes A and S to produce A' and S', such that:
|
||||
/// - like values in A' are vertically adjacent to each other; and
|
||||
/// - 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.
|
||||
// NOTE: Copy of permute_expression_pair that uses ProvingKeyV2
|
||||
fn permute_expression_pair_v2<'params, C: CurveAffine, P: Params<'params, C>, R: RngCore>(
|
||||
pk: &ProvingKeyV2<C>,
|
||||
params: &P,
|
||||
domain: &EvaluationDomain<C::Scalar>,
|
||||
mut rng: 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 usable_rows = params.n() as usize - (blinding_factors + 1);
|
||||
|
||||
let mut permuted_input_expression: Vec<C::Scalar> = input_expression.to_vec();
|
||||
permuted_input_expression.truncate(usable_rows);
|
||||
|
||||
// Sort input lookup expression values
|
||||
permuted_input_expression.sort();
|
||||
|
||||
// A BTreeMap of each unique element in the table expression and its count
|
||||
let mut leftover_table_map: BTreeMap<C::Scalar, u32> = table_expression
|
||||
.iter()
|
||||
.take(usable_rows)
|
||||
.fold(BTreeMap::new(), |mut acc, coeff| {
|
||||
*acc.entry(*coeff).or_insert(0) += 1;
|
||||
acc
|
||||
});
|
||||
let mut permuted_table_coeffs = vec![C::Scalar::ZERO; usable_rows];
|
||||
|
||||
let mut repeated_input_rows = permuted_input_expression
|
||||
.iter()
|
||||
.zip(permuted_table_coeffs.iter_mut())
|
||||
.enumerate()
|
||||
.filter_map(|(row, (input_value, table_value))| {
|
||||
// If this is the first occurrence of `input_value` in the input expression
|
||||
if row == 0 || *input_value != permuted_input_expression[row - 1] {
|
||||
*table_value = *input_value;
|
||||
// Remove one instance of input_value from leftover_table_map
|
||||
if let Some(count) = leftover_table_map.get_mut(input_value) {
|
||||
assert!(*count > 0);
|
||||
*count -= 1;
|
||||
None
|
||||
} else {
|
||||
// Return error if input_value not found
|
||||
Some(Err(Error::ConstraintSystemFailure))
|
||||
}
|
||||
// If input value is repeated
|
||||
} else {
|
||||
Some(Ok(row))
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// Populate permuted table at unfilled rows with leftover table elements
|
||||
for (coeff, count) in leftover_table_map.iter() {
|
||||
for _ in 0..*count {
|
||||
permuted_table_coeffs[repeated_input_rows.pop().unwrap()] = *coeff;
|
||||
}
|
||||
}
|
||||
assert!(repeated_input_rows.is_empty());
|
||||
|
||||
permuted_input_expression
|
||||
.extend((0..(blinding_factors + 1)).map(|_| C::Scalar::random(&mut rng)));
|
||||
permuted_table_coeffs.extend((0..(blinding_factors + 1)).map(|_| C::Scalar::random(&mut rng)));
|
||||
assert_eq!(permuted_input_expression.len(), params.n() as usize);
|
||||
assert_eq!(permuted_table_coeffs.len(), params.n() as usize);
|
||||
|
||||
#[cfg(feature = "sanity-checks")]
|
||||
{
|
||||
let mut last = None;
|
||||
for (a, b) in permuted_input_expression
|
||||
.iter()
|
||||
.zip(permuted_table_coeffs.iter())
|
||||
.take(usable_rows)
|
||||
{
|
||||
if *a != *b {
|
||||
assert_eq!(*a, last.unwrap());
|
||||
}
|
||||
last = Some(*a);
|
||||
}
|
||||
}
|
||||
|
||||
Ok((
|
||||
domain.lagrange_from_vec(permuted_input_expression),
|
||||
domain.lagrange_from_vec(permuted_table_coeffs),
|
||||
))
|
||||
}
|
||||
|
||||
/// Given a vector of input values A and a vector of table values S,
|
||||
/// this method permutes A and S to produce A' and S', such that:
|
||||
/// - like values in A' are vertically adjacent to each other; and
|
||||
|
|
|
@ -42,6 +42,155 @@ pub(crate) struct Evaluated<C: CurveAffine> {
|
|||
}
|
||||
|
||||
impl Argument {
|
||||
// NOTE: Copy of commit with ProvingKeyV2
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn commit_v2<
|
||||
'params,
|
||||
C: CurveAffine,
|
||||
P: Params<'params, C>,
|
||||
E: EncodedChallenge<C>,
|
||||
R: RngCore,
|
||||
T: TranscriptWrite<C, E>,
|
||||
>(
|
||||
&self,
|
||||
params: &P,
|
||||
pk: &plonk::ProvingKeyV2<C>,
|
||||
pkey: &ProvingKey<C>,
|
||||
advice: &[Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
fixed: &[Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
instance: &[Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
beta: ChallengeBeta<C>,
|
||||
gamma: ChallengeGamma<C>,
|
||||
mut rng: R,
|
||||
transcript: &mut T,
|
||||
) -> Result<Committed<C>, Error> {
|
||||
let domain = &pk.vk.domain;
|
||||
|
||||
// How many columns can be included in a single permutation polynomial?
|
||||
// We need to multiply by z(X) and (1 - (l_last(X) + l_blind(X))). This
|
||||
// will never underflow because of the requirement of at least a degree
|
||||
// 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();
|
||||
|
||||
// Each column gets its own delta power.
|
||||
let mut deltaomega = C::Scalar::ONE;
|
||||
|
||||
// Track the "last" value from the previous column set
|
||||
let mut last_z = C::Scalar::ONE;
|
||||
|
||||
let mut sets = vec![];
|
||||
|
||||
for (columns, permutations) in self
|
||||
.columns
|
||||
.chunks(chunk_len)
|
||||
.zip(pkey.permutations.chunks(chunk_len))
|
||||
{
|
||||
// Goal is to compute the products of fractions
|
||||
//
|
||||
// (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) /
|
||||
// (p_j(\omega^i) + \beta s_j(\omega^i) + \gamma)
|
||||
//
|
||||
// where p_j(X) is the jth column in this permutation,
|
||||
// and i is the ith row of the column.
|
||||
|
||||
let mut modified_values = vec![C::Scalar::ONE; params.n() as usize];
|
||||
|
||||
// Iterate over each column of the permutation
|
||||
for (&column, permuted_column_values) in columns.iter().zip(permutations.iter()) {
|
||||
let values = match column.column_type() {
|
||||
Any::Advice(_) => advice,
|
||||
Any::Fixed => fixed,
|
||||
Any::Instance => instance,
|
||||
};
|
||||
parallelize(&mut modified_values, |modified_values, start| {
|
||||
for ((modified_values, value), permuted_value) in modified_values
|
||||
.iter_mut()
|
||||
.zip(values[column.index()][start..].iter())
|
||||
.zip(permuted_column_values[start..].iter())
|
||||
{
|
||||
*modified_values *= &(*beta * permuted_value + &*gamma + value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Invert to obtain the denominator for the permutation product polynomial
|
||||
modified_values.batch_invert();
|
||||
|
||||
// Iterate over each column again, this time finishing the computation
|
||||
// of the entire fraction by computing the numerators
|
||||
for &column in columns.iter() {
|
||||
let omega = domain.get_omega();
|
||||
let values = match column.column_type() {
|
||||
Any::Advice(_) => advice,
|
||||
Any::Fixed => fixed,
|
||||
Any::Instance => instance,
|
||||
};
|
||||
parallelize(&mut modified_values, |modified_values, start| {
|
||||
let mut deltaomega = deltaomega * &omega.pow_vartime([start as u64, 0, 0, 0]);
|
||||
for (modified_values, value) in modified_values
|
||||
.iter_mut()
|
||||
.zip(values[column.index()][start..].iter())
|
||||
{
|
||||
// Multiply by p_j(\omega^i) + \delta^j \omega^i \beta
|
||||
*modified_values *= &(deltaomega * &*beta + &*gamma + value);
|
||||
deltaomega *= ω
|
||||
}
|
||||
});
|
||||
deltaomega *= &<C::Scalar as PrimeField>::DELTA;
|
||||
}
|
||||
|
||||
// The modified_values vector is a vector of products of fractions
|
||||
// of the form
|
||||
//
|
||||
// (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) /
|
||||
// (p_j(\omega^i) + \beta s_j(\omega^i) + \gamma)
|
||||
//
|
||||
// where i is the index into modified_values, for the jth column in
|
||||
// the permutation
|
||||
|
||||
// Compute the evaluations of the permutation product polynomial
|
||||
// over our domain, starting with z[0] = 1
|
||||
let mut z = vec![last_z];
|
||||
for row in 1..(params.n() as usize) {
|
||||
let mut tmp = z[row - 1];
|
||||
|
||||
tmp *= &modified_values[row - 1];
|
||||
z.push(tmp);
|
||||
}
|
||||
let mut z = domain.lagrange_from_vec(z);
|
||||
// Set blinding factors
|
||||
for z in &mut z[params.n() as usize - blinding_factors..] {
|
||||
*z = C::Scalar::random(&mut rng);
|
||||
}
|
||||
// Set new last_z
|
||||
last_z = z[params.n() as usize - (blinding_factors + 1)];
|
||||
|
||||
let blind = Blind(C::Scalar::random(&mut rng));
|
||||
|
||||
let permutation_product_commitment_projective = params.commit_lagrange(&z, blind);
|
||||
let permutation_product_blind = blind;
|
||||
let z = domain.lagrange_to_coeff(z);
|
||||
let permutation_product_poly = z.clone();
|
||||
|
||||
let permutation_product_coset = domain.coeff_to_extended(z.clone());
|
||||
|
||||
let permutation_product_commitment =
|
||||
permutation_product_commitment_projective.to_affine();
|
||||
|
||||
// Hash the permutation product commitment
|
||||
transcript.write_point(permutation_product_commitment)?;
|
||||
|
||||
sets.push(CommittedSet {
|
||||
permutation_product_poly,
|
||||
permutation_product_coset,
|
||||
permutation_product_blind,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Committed { sets })
|
||||
}
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn commit<
|
||||
'params,
|
||||
|
@ -234,6 +383,51 @@ impl<C: CurveAffine> super::ProvingKey<C> {
|
|||
}
|
||||
|
||||
impl<C: CurveAffine> Constructed<C> {
|
||||
// NOTE: Copy of evaluate with ProvingKeyV2
|
||||
pub(in crate::plonk) fn evaluate_v2<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
||||
self,
|
||||
pk: &plonk::ProvingKeyV2<C>,
|
||||
x: ChallengeX<C>,
|
||||
transcript: &mut T,
|
||||
) -> Result<Evaluated<C>, Error> {
|
||||
let domain = &pk.vk.domain;
|
||||
let blinding_factors = pk.vk.cs.blinding_factors();
|
||||
|
||||
{
|
||||
let mut sets = self.sets.iter();
|
||||
|
||||
while let Some(set) = sets.next() {
|
||||
let permutation_product_eval = eval_polynomial(&set.permutation_product_poly, *x);
|
||||
|
||||
let permutation_product_next_eval = eval_polynomial(
|
||||
&set.permutation_product_poly,
|
||||
domain.rotate_omega(*x, Rotation::next()),
|
||||
);
|
||||
|
||||
// Hash permutation product evals
|
||||
for eval in iter::empty()
|
||||
.chain(Some(&permutation_product_eval))
|
||||
.chain(Some(&permutation_product_next_eval))
|
||||
{
|
||||
transcript.write_scalar(*eval)?;
|
||||
}
|
||||
|
||||
// If we have any remaining sets to process, evaluate this set at omega^u
|
||||
// so we can constrain the last value of its running product to equal the
|
||||
// first value of the next set's running product, chaining them together.
|
||||
if sets.len() > 0 {
|
||||
let permutation_product_last_eval = eval_polynomial(
|
||||
&set.permutation_product_poly,
|
||||
domain.rotate_omega(*x, Rotation(-((blinding_factors + 1) as i32))),
|
||||
);
|
||||
|
||||
transcript.write_scalar(permutation_product_last_eval)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Evaluated { constructed: self })
|
||||
}
|
||||
pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
||||
self,
|
||||
pk: &plonk::ProvingKey<C>,
|
||||
|
@ -281,6 +475,52 @@ impl<C: CurveAffine> Constructed<C> {
|
|||
}
|
||||
|
||||
impl<C: CurveAffine> Evaluated<C> {
|
||||
// NOTE: Copy of open with ProvingKeyV2
|
||||
pub(in crate::plonk) fn open_v2<'a>(
|
||||
&'a self,
|
||||
pk: &'a plonk::ProvingKeyV2<C>,
|
||||
x: ChallengeX<C>,
|
||||
) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone {
|
||||
let blinding_factors = pk.vk.cs.blinding_factors();
|
||||
let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next());
|
||||
let x_last = pk
|
||||
.vk
|
||||
.domain
|
||||
.rotate_omega(*x, Rotation(-((blinding_factors + 1) as i32)));
|
||||
|
||||
iter::empty()
|
||||
.chain(self.constructed.sets.iter().flat_map(move |set| {
|
||||
iter::empty()
|
||||
// Open permutation product commitments at x and \omega x
|
||||
.chain(Some(ProverQuery {
|
||||
point: *x,
|
||||
poly: &set.permutation_product_poly,
|
||||
blind: set.permutation_product_blind,
|
||||
}))
|
||||
.chain(Some(ProverQuery {
|
||||
point: x_next,
|
||||
poly: &set.permutation_product_poly,
|
||||
blind: set.permutation_product_blind,
|
||||
}))
|
||||
}))
|
||||
// Open it at \omega^{last} x for all but the last set. This rotation is only
|
||||
// sensical for the first row, but we only use this rotation in a constraint
|
||||
// that is gated on l_0.
|
||||
.chain(
|
||||
self.constructed
|
||||
.sets
|
||||
.iter()
|
||||
.rev()
|
||||
.skip(1)
|
||||
.flat_map(move |set| {
|
||||
Some(ProverQuery {
|
||||
point: x_last,
|
||||
poly: &set.permutation_product_poly,
|
||||
blind: set.permutation_product_blind,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
pub(in crate::plonk) fn open<'a>(
|
||||
&'a self,
|
||||
pk: &'a plonk::ProvingKey<C>,
|
||||
|
|
|
@ -42,6 +42,7 @@ struct AdviceSingle<C: CurveAffine, B: Basis> {
|
|||
pub advice_blinds: Vec<Blind<C::Scalar>>,
|
||||
}
|
||||
|
||||
// TODO: Rewrite as multi-instance prover, and make a wraper for signle-instance case.
|
||||
/// The prover object used to create proofs interactively by passing the witnesses to commit at
|
||||
/// each phase.
|
||||
#[derive(Debug)]
|
||||
|
@ -61,7 +62,7 @@ pub struct ProverV2<
|
|||
instance_queries: Vec<(Column<Instance>, Rotation)>,
|
||||
fixed_queries: Vec<(Column<Fixed>, Rotation)>,
|
||||
phases: Vec<u8>,
|
||||
instance: Vec<InstanceSingle<Scheme::Curve>>,
|
||||
instance: InstanceSingle<Scheme::Curve>,
|
||||
rng: R,
|
||||
transcript: T,
|
||||
advice: AdviceSingle<Scheme::Curve, LagrangeCoeff>,
|
||||
|
@ -89,9 +90,12 @@ impl<
|
|||
rng: R,
|
||||
mut transcript: T,
|
||||
) -> Result<Self, Error>
|
||||
// TODO: Can I move this `where` to the struct definition?
|
||||
where
|
||||
Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>,
|
||||
{
|
||||
// TODO: We have cs duplicated in circuit.cs and pk.vk.cs. Can we dedup them?
|
||||
|
||||
if instance.len() != pk.vk.cs.num_instance_columns {
|
||||
return Err(Error::InvalidInstances);
|
||||
}
|
||||
|
@ -107,59 +111,57 @@ impl<
|
|||
|
||||
let domain = &pk.vk.domain;
|
||||
|
||||
let instance: Vec<InstanceSingle<Scheme::Curve>> = iter::once(instance)
|
||||
.map(|instance| -> Result<InstanceSingle<Scheme::Curve>, Error> {
|
||||
let instance_values = instance
|
||||
.iter()
|
||||
.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)) {
|
||||
return Err(Error::InstanceTooLarge);
|
||||
}
|
||||
for (poly, value) in poly.iter_mut().zip(values.iter()) {
|
||||
if !P::QUERY_INSTANCE {
|
||||
transcript.common_scalar(*value)?;
|
||||
}
|
||||
*poly = *value;
|
||||
}
|
||||
Ok(poly)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
if P::QUERY_INSTANCE {
|
||||
let instance_commitments_projective: Vec<_> = instance_values
|
||||
.iter()
|
||||
.map(|poly| params.commit_lagrange(poly, Blind::default()))
|
||||
.collect();
|
||||
let mut instance_commitments =
|
||||
vec![Scheme::Curve::identity(); instance_commitments_projective.len()];
|
||||
<Scheme::Curve as CurveAffine>::CurveExt::batch_normalize(
|
||||
&instance_commitments_projective,
|
||||
&mut instance_commitments,
|
||||
);
|
||||
let instance_commitments = instance_commitments;
|
||||
drop(instance_commitments_projective);
|
||||
|
||||
for commitment in &instance_commitments {
|
||||
transcript.common_point(*commitment)?;
|
||||
let instance: InstanceSingle<Scheme::Curve> = {
|
||||
let instance_values = instance
|
||||
.iter()
|
||||
.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)) {
|
||||
return Err(Error::InstanceTooLarge);
|
||||
}
|
||||
}
|
||||
|
||||
let instance_polys: Vec<_> = instance_values
|
||||
.iter()
|
||||
.map(|poly| {
|
||||
let lagrange_vec = domain.lagrange_from_vec(poly.to_vec());
|
||||
domain.lagrange_to_coeff(lagrange_vec)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(InstanceSingle {
|
||||
instance_values,
|
||||
instance_polys,
|
||||
for (poly, value) in poly.iter_mut().zip(values.iter()) {
|
||||
if !P::QUERY_INSTANCE {
|
||||
transcript.common_scalar(*value)?;
|
||||
}
|
||||
*poly = *value;
|
||||
}
|
||||
Ok(poly)
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
if P::QUERY_INSTANCE {
|
||||
let instance_commitments_projective: Vec<_> = instance_values
|
||||
.iter()
|
||||
.map(|poly| params.commit_lagrange(poly, Blind::default()))
|
||||
.collect();
|
||||
let mut instance_commitments =
|
||||
vec![Scheme::Curve::identity(); instance_commitments_projective.len()];
|
||||
<Scheme::Curve as CurveAffine>::CurveExt::batch_normalize(
|
||||
&instance_commitments_projective,
|
||||
&mut instance_commitments,
|
||||
);
|
||||
let instance_commitments = instance_commitments;
|
||||
drop(instance_commitments_projective);
|
||||
|
||||
for commitment in &instance_commitments {
|
||||
transcript.common_point(*commitment)?;
|
||||
}
|
||||
}
|
||||
|
||||
let instance_polys: Vec<_> = instance_values
|
||||
.iter()
|
||||
.map(|poly| {
|
||||
let lagrange_vec = domain.lagrange_from_vec(poly.to_vec());
|
||||
domain.lagrange_to_coeff(lagrange_vec)
|
||||
})
|
||||
.collect();
|
||||
|
||||
InstanceSingle {
|
||||
instance_values,
|
||||
instance_polys,
|
||||
}
|
||||
};
|
||||
|
||||
let advice = AdviceSingle::<Scheme::Curve, LagrangeCoeff> {
|
||||
advice_polys: vec![domain.empty_lagrange(); meta.num_advice_columns],
|
||||
|
@ -208,13 +210,12 @@ impl<
|
|||
|
||||
let params = self.params;
|
||||
let meta = self.cs;
|
||||
let domain = self.pk.vk.domain;
|
||||
|
||||
let mut transcript = self.transcript;
|
||||
let mut rng = self.rng;
|
||||
let transcript = &mut self.transcript;
|
||||
let mut rng = &mut self.rng;
|
||||
|
||||
let mut advice = self.advice;
|
||||
let mut challenges = self.challenges;
|
||||
let advice = &mut self.advice;
|
||||
let challenges = &mut self.challenges;
|
||||
|
||||
let column_indices = meta
|
||||
.advice_column_phase
|
||||
|
@ -242,7 +243,8 @@ impl<
|
|||
}
|
||||
let mut advice_values =
|
||||
batch_invert_assigned::<Scheme::Scalar>(witness.into_iter().flatten().collect());
|
||||
let unblinded_advice = HashSet::from_iter(meta.unblinded_advice_columns.clone());
|
||||
let unblinded_advice: HashSet<usize> =
|
||||
HashSet::from_iter(meta.unblinded_advice_columns.clone());
|
||||
let unusable_rows_start = params.n() as usize - (meta.blinding_factors() + 1);
|
||||
|
||||
// Add blinding factors to advice columns
|
||||
|
@ -306,8 +308,274 @@ impl<
|
|||
Ok(challenges.clone())
|
||||
}
|
||||
|
||||
pub fn create_proof(self) -> Result<T, Error> {
|
||||
todo!()
|
||||
/// Finalizes the proof creation.
|
||||
pub fn create_proof(mut self) -> Result<T, Error>
|
||||
where
|
||||
Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>,
|
||||
{
|
||||
let params = self.params;
|
||||
let meta = self.cs;
|
||||
let pk = self.pk;
|
||||
let domain = &self.pk.vk.domain;
|
||||
|
||||
let mut transcript = self.transcript;
|
||||
let mut rng = self.rng;
|
||||
|
||||
let instance = std::mem::replace(
|
||||
&mut self.instance,
|
||||
InstanceSingle {
|
||||
instance_values: Vec::new(),
|
||||
instance_polys: Vec::new(),
|
||||
},
|
||||
);
|
||||
let advice = std::mem::replace(
|
||||
&mut self.advice,
|
||||
AdviceSingle {
|
||||
advice_polys: Vec::new(),
|
||||
advice_blinds: Vec::new(),
|
||||
},
|
||||
);
|
||||
let mut challenges = self.challenges;
|
||||
|
||||
assert_eq!(challenges.len(), meta.num_challenges);
|
||||
let challenges = (0..meta.num_challenges)
|
||||
.map(|index| challenges.remove(&index).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sample theta challenge for keeping lookup columns linearly independent
|
||||
let theta: ChallengeTheta<_> = transcript.squeeze_challenge_scalar();
|
||||
|
||||
// Construct and commit to permuted values for each lookup
|
||||
let lookups: Vec<lookup::prover::Permuted<Scheme::Curve>> = pk
|
||||
.vk
|
||||
.cs
|
||||
.lookups
|
||||
.iter()
|
||||
.map(|lookup| {
|
||||
lookup.commit_permuted_v2(
|
||||
pk,
|
||||
params,
|
||||
&domain,
|
||||
theta,
|
||||
&advice.advice_polys,
|
||||
&pk.fixed_values,
|
||||
&instance.instance_values,
|
||||
&challenges,
|
||||
&mut rng,
|
||||
&mut transcript,
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// Sample beta challenge
|
||||
let beta: ChallengeBeta<_> = transcript.squeeze_challenge_scalar();
|
||||
|
||||
// Sample gamma challenge
|
||||
let gamma: ChallengeGamma<_> = transcript.squeeze_challenge_scalar();
|
||||
|
||||
// Commit to permutation.
|
||||
let permutation = [pk.vk.cs.permutation.commit_v2(
|
||||
params,
|
||||
pk,
|
||||
&pk.permutation,
|
||||
&advice.advice_polys,
|
||||
&pk.fixed_values,
|
||||
&instance.instance_values,
|
||||
beta,
|
||||
gamma,
|
||||
&mut rng,
|
||||
&mut transcript,
|
||||
)?];
|
||||
|
||||
// Construct and commit to products for each lookup
|
||||
let lookups: [Vec<lookup::prover::Committed<Scheme::Curve>>; 1] = [lookups
|
||||
.into_iter()
|
||||
.map(|lookup| {
|
||||
lookup.commit_product_v2(pk, params, beta, gamma, &mut rng, &mut transcript)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?];
|
||||
|
||||
// Compress expressions for each shuffle
|
||||
let shuffles: [Vec<shuffle::prover::Committed<Scheme::Curve>>; 1] = [pk
|
||||
.vk
|
||||
.cs
|
||||
.shuffles
|
||||
.iter()
|
||||
.map(|shuffle| {
|
||||
shuffle.commit_product_v2(
|
||||
pk,
|
||||
params,
|
||||
domain,
|
||||
theta,
|
||||
gamma,
|
||||
&advice.advice_polys,
|
||||
&pk.fixed_values,
|
||||
&instance.instance_values,
|
||||
&challenges,
|
||||
&mut rng,
|
||||
&mut transcript,
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?];
|
||||
|
||||
// Commit to the vanishing argument's random polynomial for blinding h(x_3)
|
||||
let vanishing = vanishing::Argument::commit(params, domain, &mut rng, &mut transcript)?;
|
||||
|
||||
// Obtain challenge for keeping all separate gates linearly independent
|
||||
let y: ChallengeY<_> = transcript.squeeze_challenge_scalar();
|
||||
|
||||
// Calculate the advice polys
|
||||
let advice: AdviceSingle<Scheme::Curve, Coeff> = AdviceSingle {
|
||||
advice_polys: advice
|
||||
.advice_polys
|
||||
.into_iter()
|
||||
.map(|poly| domain.lagrange_to_coeff(poly))
|
||||
.collect::<Vec<_>>(),
|
||||
advice_blinds: advice.advice_blinds,
|
||||
};
|
||||
|
||||
// Evaluate the h(X) polynomial
|
||||
let h_poly = pk.ev.evaluate_h_v2(
|
||||
pk,
|
||||
&[advice.advice_polys.as_slice()],
|
||||
&[instance.instance_polys.as_slice()],
|
||||
&challenges,
|
||||
*y,
|
||||
*beta,
|
||||
*gamma,
|
||||
*theta,
|
||||
&lookups,
|
||||
&shuffles,
|
||||
&permutation,
|
||||
);
|
||||
|
||||
// Construct the vanishing argument's h(X) commitments
|
||||
let vanishing = vanishing.construct(params, domain, h_poly, &mut rng, &mut transcript)?;
|
||||
|
||||
let x: ChallengeX<_> = transcript.squeeze_challenge_scalar();
|
||||
let xn = x.pow([params.n()]);
|
||||
|
||||
if P::QUERY_INSTANCE {
|
||||
// Compute and hash instance evals for the circuit instance
|
||||
// Evaluate polynomials at omega^i x
|
||||
let instance_evals: Vec<_> = self
|
||||
.instance_queries
|
||||
.iter()
|
||||
.map(|&(column, at)| {
|
||||
eval_polynomial(
|
||||
&instance.instance_polys[column.index()],
|
||||
domain.rotate_omega(*x, at),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Hash each instance column evaluation
|
||||
for eval in instance_evals.iter() {
|
||||
transcript.write_scalar(*eval)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute and hash advice evals for the circuit instance
|
||||
// Evaluate polynomials at omega^i x
|
||||
let advice_evals: Vec<_> = self
|
||||
.advice_queries
|
||||
.iter()
|
||||
.map(|&(column, at)| {
|
||||
eval_polynomial(
|
||||
&advice.advice_polys[column.index()],
|
||||
domain.rotate_omega(*x, at),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Hash each advice column evaluation
|
||||
for eval in advice_evals.iter() {
|
||||
transcript.write_scalar(*eval)?;
|
||||
}
|
||||
|
||||
// Compute and hash fixed evals
|
||||
let fixed_evals: Vec<_> = self
|
||||
.fixed_queries
|
||||
.iter()
|
||||
.map(|&(column, at)| {
|
||||
eval_polynomial(&pk.fixed_polys[column.index()], domain.rotate_omega(*x, at))
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Hash each fixed column evaluation
|
||||
for eval in fixed_evals.iter() {
|
||||
transcript.write_scalar(*eval)?;
|
||||
}
|
||||
|
||||
let vanishing = vanishing.evaluate(x, xn, domain, &mut transcript)?;
|
||||
|
||||
// Evaluate common permutation data
|
||||
pk.permutation.evaluate(x, &mut transcript)?;
|
||||
|
||||
let [permutation] = permutation;
|
||||
let [lookups] = lookups;
|
||||
let [shuffles] = shuffles;
|
||||
|
||||
// Evaluate the permutations, if any, at omega^i x.
|
||||
let permutation = permutation
|
||||
.construct()
|
||||
.evaluate_v2(pk, x, &mut transcript)?;
|
||||
|
||||
// Evaluate the lookups, if any, at omega^i x.
|
||||
let lookups: Vec<lookup::prover::Evaluated<Scheme::Curve>> = lookups
|
||||
.into_iter()
|
||||
.map(|p| p.evaluate_v2(pk, x, &mut transcript))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// Evaluate the shuffles, if any, at omega^i x.
|
||||
let shuffles: Vec<shuffle::prover::Evaluated<Scheme::Curve>> = shuffles
|
||||
.into_iter()
|
||||
.map(|p| p.evaluate_v2(pk, x, &mut transcript))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let instance_ref = &instance;
|
||||
let advice_ref = &advice;
|
||||
let instances =
|
||||
iter::empty()
|
||||
.chain(
|
||||
P::QUERY_INSTANCE
|
||||
.then_some(self.instance_queries.iter().map(move |&(column, at)| {
|
||||
ProverQuery {
|
||||
point: domain.rotate_omega(*x, at),
|
||||
poly: &instance_ref.instance_polys[column.index()],
|
||||
blind: Blind::default(),
|
||||
}
|
||||
}))
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.chain(
|
||||
self.advice_queries
|
||||
.iter()
|
||||
.map(move |&(column, at)| ProverQuery {
|
||||
point: domain.rotate_omega(*x, at),
|
||||
poly: &advice_ref.advice_polys[column.index()],
|
||||
blind: advice_ref.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 {
|
||||
point: domain.rotate_omega(*x, at),
|
||||
poly: &pk.fixed_polys[column.index()],
|
||||
blind: Blind::default(),
|
||||
}))
|
||||
.chain(pk.permutation.open(x))
|
||||
// We query the h(X) polynomial at x
|
||||
.chain(vanishing.open(x));
|
||||
|
||||
let prover = P::new(params);
|
||||
prover
|
||||
.create_proof(rng, &mut transcript, instances)
|
||||
.map_err(|_| Error::ConstraintSystemFailure)?;
|
||||
|
||||
Ok(transcript)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use super::super::{
|
||||
circuit::Expression, ChallengeGamma, ChallengeTheta, ChallengeX, Error, ProvingKey,
|
||||
ProvingKeyV2,
|
||||
};
|
||||
use super::Argument;
|
||||
use crate::plonk::evaluation::evaluate;
|
||||
|
@ -36,6 +37,60 @@ pub(in crate::plonk) struct Evaluated<C: CurveAffine> {
|
|||
}
|
||||
|
||||
impl<F: WithSmallOrderMulGroup<3>> Argument<F> {
|
||||
/// Given a Shuffle with input expressions [A_0, A_1, ..., A_{m-1}] and table expressions
|
||||
/// [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},
|
||||
// NOTE: Copy of compress with ProvingKeyV2
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compress_v2<'a, 'params: 'a, C, P: Params<'params, C>>(
|
||||
&self,
|
||||
pk: &ProvingKeyV2<C>,
|
||||
params: &P,
|
||||
domain: &EvaluationDomain<C::Scalar>,
|
||||
theta: ChallengeTheta<C>,
|
||||
advice_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
fixed_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
instance_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
challenges: &'a [C::Scalar],
|
||||
) -> Compressed<C>
|
||||
where
|
||||
C: CurveAffine<ScalarExt = F>,
|
||||
C::Curve: Mul<F, Output = C::Curve> + MulAssign<F>,
|
||||
{
|
||||
// Closure to get values of expressions and compress them
|
||||
let compress_expressions = |expressions: &[Expression<C::Scalar>]| {
|
||||
let compressed_expression = expressions
|
||||
.iter()
|
||||
.map(|expression| {
|
||||
pk.vk.domain.lagrange_from_vec(evaluate(
|
||||
expression,
|
||||
params.n() as usize,
|
||||
1,
|
||||
fixed_values,
|
||||
advice_values,
|
||||
instance_values,
|
||||
challenges,
|
||||
))
|
||||
})
|
||||
.fold(domain.empty_lagrange(), |acc, expression| {
|
||||
acc * *theta + &expression
|
||||
});
|
||||
compressed_expression
|
||||
};
|
||||
|
||||
// Get values of input expressions involved in the shuffle and compress them
|
||||
let input_expression = compress_expressions(&self.input_expressions);
|
||||
|
||||
// Get values of table expressions involved in the shuffle and compress them
|
||||
let shuffle_expression = compress_expressions(&self.shuffle_expressions);
|
||||
|
||||
Compressed {
|
||||
input_expression,
|
||||
shuffle_expression,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a Shuffle with input expressions [A_0, A_1, ..., A_{m-1}] and table expressions
|
||||
/// [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}
|
||||
|
@ -89,6 +144,117 @@ impl<F: WithSmallOrderMulGroup<3>> Argument<F> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Given a Shuffle with input expressions and table expressions this method
|
||||
/// 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.
|
||||
// NOTE: Copy of commit_product with ProvingKeyV2
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::plonk) fn commit_product_v2<
|
||||
'a,
|
||||
'params: 'a,
|
||||
C,
|
||||
P: Params<'params, C>,
|
||||
E: EncodedChallenge<C>,
|
||||
R: RngCore,
|
||||
T: TranscriptWrite<C, E>,
|
||||
>(
|
||||
&self,
|
||||
pk: &ProvingKeyV2<C>,
|
||||
params: &P,
|
||||
domain: &EvaluationDomain<C::Scalar>,
|
||||
theta: ChallengeTheta<C>,
|
||||
gamma: ChallengeGamma<C>,
|
||||
advice_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
fixed_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
instance_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
||||
challenges: &'a [C::Scalar],
|
||||
mut rng: R,
|
||||
transcript: &mut T,
|
||||
) -> Result<Committed<C>, Error>
|
||||
where
|
||||
C: CurveAffine<ScalarExt = F>,
|
||||
C::Curve: Mul<F, Output = C::Curve> + MulAssign<F>,
|
||||
{
|
||||
let compressed = self.compress_v2(
|
||||
pk,
|
||||
params,
|
||||
domain,
|
||||
theta,
|
||||
advice_values,
|
||||
fixed_values,
|
||||
instance_values,
|
||||
challenges,
|
||||
);
|
||||
|
||||
let blinding_factors = pk.vk.cs.blinding_factors();
|
||||
|
||||
let mut shuffle_product = vec![C::Scalar::ZERO; params.n() as usize];
|
||||
parallelize(&mut shuffle_product, |shuffle_product, start| {
|
||||
for (shuffle_product, shuffle_value) in shuffle_product
|
||||
.iter_mut()
|
||||
.zip(compressed.shuffle_expression[start..].iter())
|
||||
{
|
||||
*shuffle_product = *gamma + shuffle_value;
|
||||
}
|
||||
});
|
||||
|
||||
shuffle_product.iter_mut().batch_invert();
|
||||
|
||||
parallelize(&mut shuffle_product, |product, start| {
|
||||
for (i, product) in product.iter_mut().enumerate() {
|
||||
let i = i + start;
|
||||
*product *= &(*gamma + compressed.input_expression[i]);
|
||||
}
|
||||
});
|
||||
|
||||
// Compute the evaluations of the shuffle product polynomial
|
||||
// over our domain, starting with z[0] = 1
|
||||
let z = iter::once(C::Scalar::ONE)
|
||||
.chain(shuffle_product)
|
||||
.scan(C::Scalar::ONE, |state, cur| {
|
||||
*state *= &cur;
|
||||
Some(*state)
|
||||
})
|
||||
// Take all rows including the "last" row which should
|
||||
// be a boolean (and ideally 1, else soundness is broken)
|
||||
.take(params.n() as usize - blinding_factors)
|
||||
// Chain random blinding factors.
|
||||
.chain((0..blinding_factors).map(|_| C::Scalar::random(&mut rng)))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(z.len(), params.n() as usize);
|
||||
let z = pk.vk.domain.lagrange_from_vec(z);
|
||||
|
||||
#[cfg(feature = "sanity-checks")]
|
||||
{
|
||||
// While in Lagrange basis, check that product is correctly constructed
|
||||
let u = (params.n() as usize) - (blinding_factors + 1);
|
||||
assert_eq!(z[0], C::Scalar::ONE);
|
||||
for i in 0..u {
|
||||
let mut left = z[i + 1];
|
||||
let input_value = &compressed.input_expression[i];
|
||||
let shuffle_value = &compressed.shuffle_expression[i];
|
||||
left *= &(*gamma + shuffle_value);
|
||||
let mut right = z[i];
|
||||
right *= &(*gamma + input_value);
|
||||
assert_eq!(left, right);
|
||||
}
|
||||
assert_eq!(z[u], C::Scalar::ONE);
|
||||
}
|
||||
|
||||
let product_blind = Blind(C::Scalar::random(rng));
|
||||
let product_commitment = params.commit_lagrange(&z, product_blind).to_affine();
|
||||
let z = pk.vk.domain.lagrange_to_coeff(z);
|
||||
|
||||
// Hash product commitment
|
||||
transcript.write_point(product_commitment)?;
|
||||
|
||||
Ok(Committed::<C> {
|
||||
product_poly: z,
|
||||
product_blind,
|
||||
})
|
||||
}
|
||||
|
||||
/// Given a Shuffle with input expressions and table expressions this method
|
||||
/// constructs the grand product polynomial over the shuffle.
|
||||
/// The grand product polynomial is used to populate the Product<C> struct.
|
||||
|
@ -201,6 +367,30 @@ impl<F: WithSmallOrderMulGroup<3>> Argument<F> {
|
|||
}
|
||||
|
||||
impl<C: CurveAffine> Committed<C> {
|
||||
// NOTE: Copy of evaluate with ProvingKeyV2
|
||||
pub(in crate::plonk) fn evaluate_v2<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
||||
self,
|
||||
pk: &ProvingKeyV2<C>,
|
||||
x: ChallengeX<C>,
|
||||
transcript: &mut T,
|
||||
) -> Result<Evaluated<C>, Error> {
|
||||
let domain = &pk.vk.domain;
|
||||
let x_next = domain.rotate_omega(*x, Rotation::next());
|
||||
|
||||
let product_eval = eval_polynomial(&self.product_poly, *x);
|
||||
let product_next_eval = eval_polynomial(&self.product_poly, x_next);
|
||||
|
||||
// Hash each advice evaluation
|
||||
for eval in iter::empty()
|
||||
.chain(Some(product_eval))
|
||||
.chain(Some(product_next_eval))
|
||||
{
|
||||
transcript.write_scalar(eval)?;
|
||||
}
|
||||
|
||||
Ok(Evaluated { constructed: self })
|
||||
}
|
||||
|
||||
pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
||||
self,
|
||||
pk: &ProvingKey<C>,
|
||||
|
@ -226,6 +416,29 @@ impl<C: CurveAffine> Committed<C> {
|
|||
}
|
||||
|
||||
impl<C: CurveAffine> Evaluated<C> {
|
||||
// NOTE: Copy of open with ProvingKeyV2
|
||||
pub(in crate::plonk) fn open_v2<'a>(
|
||||
&'a self,
|
||||
pk: &'a ProvingKeyV2<C>,
|
||||
x: ChallengeX<C>,
|
||||
) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone {
|
||||
let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next());
|
||||
|
||||
iter::empty()
|
||||
// Open shuffle product commitments at x
|
||||
.chain(Some(ProverQuery {
|
||||
point: *x,
|
||||
poly: &self.constructed.product_poly,
|
||||
blind: self.constructed.product_blind,
|
||||
}))
|
||||
// Open shuffle product commitments at x_next
|
||||
.chain(Some(ProverQuery {
|
||||
point: x_next,
|
||||
poly: &self.constructed.product_poly,
|
||||
blind: self.constructed.product_blind,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(in crate::plonk) fn open<'a>(
|
||||
&'a self,
|
||||
pk: &'a ProvingKey<C>,
|
||||
|
|
Loading…
Reference in New Issue