2020-11-30 23:22:11 -08:00
|
|
|
|
use super::super::{
|
2021-02-10 06:57:08 -08:00
|
|
|
|
circuit::Expression, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error,
|
|
|
|
|
ProvingKey,
|
2020-11-30 23:22:11 -08:00
|
|
|
|
};
|
2020-12-23 12:03:31 -08:00
|
|
|
|
use super::Argument;
|
2020-11-30 23:22:11 -08:00
|
|
|
|
use crate::{
|
2021-02-22 11:02:53 -08:00
|
|
|
|
arithmetic::{eval_polynomial, parallelize, BatchInvert, CurveAffine, FieldExt},
|
2020-11-30 23:22:11 -08:00
|
|
|
|
poly::{
|
|
|
|
|
commitment::{Blind, Params},
|
2020-12-01 00:46:11 -08:00
|
|
|
|
multiopen::ProverQuery,
|
2020-11-30 23:22:11 -08:00
|
|
|
|
Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation,
|
|
|
|
|
},
|
2021-04-30 18:28:50 -07:00
|
|
|
|
transcript::{EncodedChallenge, TranscriptWrite},
|
2020-11-30 23:22:11 -08:00
|
|
|
|
};
|
|
|
|
|
use ff::Field;
|
2021-02-22 11:02:53 -08:00
|
|
|
|
use group::Curve;
|
2021-02-10 06:57:08 -08:00
|
|
|
|
use std::{
|
|
|
|
|
collections::BTreeMap,
|
|
|
|
|
iter,
|
|
|
|
|
ops::{Mul, MulAssign},
|
|
|
|
|
};
|
2020-11-30 22:53:20 -08:00
|
|
|
|
|
2020-12-02 19:56:22 -08:00
|
|
|
|
#[derive(Debug)]
|
2021-02-10 06:57:08 -08:00
|
|
|
|
pub(in crate::plonk) struct Permuted<C: CurveAffine> {
|
2021-02-11 18:24:55 -08:00
|
|
|
|
unpermuted_input_expressions: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
|
2021-02-10 06:57:08 -08:00
|
|
|
|
unpermuted_input_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
|
2021-02-11 18:24:55 -08:00
|
|
|
|
permuted_input_expression: Polynomial<C::Scalar, LagrangeCoeff>,
|
2020-11-30 22:53:20 -08:00
|
|
|
|
permuted_input_poly: Polynomial<C::Scalar, Coeff>,
|
|
|
|
|
permuted_input_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
|
|
|
|
|
permuted_input_blind: Blind<C::Scalar>,
|
|
|
|
|
permuted_input_commitment: C,
|
2021-02-11 18:24:55 -08:00
|
|
|
|
unpermuted_table_expressions: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
|
2021-02-10 06:57:08 -08:00
|
|
|
|
unpermuted_table_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
|
2021-02-11 18:24:55 -08:00
|
|
|
|
permuted_table_expression: Polynomial<C::Scalar, LagrangeCoeff>,
|
2020-11-30 22:53:20 -08:00
|
|
|
|
permuted_table_poly: Polynomial<C::Scalar, Coeff>,
|
|
|
|
|
permuted_table_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
|
|
|
|
|
permuted_table_blind: Blind<C::Scalar>,
|
|
|
|
|
permuted_table_commitment: C,
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-02 19:56:22 -08:00
|
|
|
|
#[derive(Debug)]
|
2021-02-10 06:57:08 -08:00
|
|
|
|
pub(in crate::plonk) struct Committed<C: CurveAffine> {
|
|
|
|
|
permuted: Permuted<C>,
|
2020-11-30 22:53:20 -08:00
|
|
|
|
product_poly: Polynomial<C::Scalar, Coeff>,
|
|
|
|
|
product_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
|
|
|
|
|
product_blind: Blind<C::Scalar>,
|
|
|
|
|
product_commitment: C,
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-03 17:18:28 -08:00
|
|
|
|
pub(in crate::plonk) struct Constructed<C: CurveAffine> {
|
2020-11-30 22:53:20 -08:00
|
|
|
|
permuted_input_poly: Polynomial<C::Scalar, Coeff>,
|
|
|
|
|
permuted_input_blind: Blind<C::Scalar>,
|
|
|
|
|
permuted_table_poly: Polynomial<C::Scalar, Coeff>,
|
|
|
|
|
permuted_table_blind: Blind<C::Scalar>,
|
|
|
|
|
product_poly: Polynomial<C::Scalar, Coeff>,
|
|
|
|
|
product_blind: Blind<C::Scalar>,
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-03 17:18:28 -08:00
|
|
|
|
pub(in crate::plonk) struct Evaluated<C: CurveAffine> {
|
2020-11-30 22:53:20 -08:00
|
|
|
|
constructed: Constructed<C>,
|
|
|
|
|
}
|
2020-11-30 23:22:11 -08:00
|
|
|
|
|
2021-02-10 03:36:25 -08:00
|
|
|
|
impl<F: FieldExt> Argument<F> {
|
2021-02-11 18:24:55 -08:00
|
|
|
|
/// Given a Lookup with input expressions [A_0, A_1, ..., A_{m-1}] and table expressions
|
2020-12-05 14:58:48 -08:00
|
|
|
|
/// [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},
|
2021-02-11 18:24:55 -08:00
|
|
|
|
/// - permutes A_compressed and S_compressed using permute_expression_pair() helper,
|
2020-11-30 23:22:11 -08:00
|
|
|
|
/// obtaining A' and S', and
|
|
|
|
|
/// - constructs Permuted<C> struct using permuted_input_value = A', and
|
2021-02-11 18:24:55 -08:00
|
|
|
|
/// permuted_table_expression = S'.
|
2020-11-30 23:22:11 -08:00
|
|
|
|
/// The Permuted<C> struct is used to update the Lookup, and is then returned.
|
2021-04-30 18:28:50 -07:00
|
|
|
|
pub(in crate::plonk) fn commit_permuted<
|
|
|
|
|
'a,
|
|
|
|
|
C,
|
2021-05-07 07:21:54 -07:00
|
|
|
|
E: EncodedChallenge<C>,
|
|
|
|
|
T: TranscriptWrite<C, E>,
|
2021-04-30 18:28:50 -07:00
|
|
|
|
>(
|
2020-11-30 23:22:11 -08:00
|
|
|
|
&self,
|
|
|
|
|
pk: &ProvingKey<C>,
|
|
|
|
|
params: &Params<C>,
|
|
|
|
|
domain: &EvaluationDomain<C::Scalar>,
|
2020-12-23 12:03:31 -08:00
|
|
|
|
theta: ChallengeTheta<C>,
|
2020-12-02 19:56:22 -08:00
|
|
|
|
advice_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
|
|
|
|
fixed_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
2021-02-14 09:30:36 -08:00
|
|
|
|
instance_values: &'a [Polynomial<C::Scalar, LagrangeCoeff>],
|
2020-12-02 19:56:22 -08:00
|
|
|
|
advice_cosets: &'a [Polynomial<C::Scalar, ExtendedLagrangeCoeff>],
|
|
|
|
|
fixed_cosets: &'a [Polynomial<C::Scalar, ExtendedLagrangeCoeff>],
|
2021-02-14 09:30:36 -08:00
|
|
|
|
instance_cosets: &'a [Polynomial<C::Scalar, ExtendedLagrangeCoeff>],
|
2020-12-23 12:03:31 -08:00
|
|
|
|
transcript: &mut T,
|
2021-02-10 06:57:08 -08:00
|
|
|
|
) -> Result<Permuted<C>, Error>
|
|
|
|
|
where
|
2021-02-22 11:02:53 -08:00
|
|
|
|
C: CurveAffine<ScalarExt = F>,
|
2021-02-22 10:39:14 -08:00
|
|
|
|
C::Curve: Mul<F, Output = C::Curve> + MulAssign<F>,
|
2021-02-10 06:57:08 -08:00
|
|
|
|
{
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Closure to get values of expressions and compress them
|
|
|
|
|
let compress_expressions = |expressions: &[Expression<C::Scalar>]| {
|
|
|
|
|
// Values of input expressions involved in the lookup
|
|
|
|
|
let unpermuted_expressions: Vec<_> = expressions
|
2021-02-10 06:57:08 -08:00
|
|
|
|
.iter()
|
2021-02-11 18:24:55 -08:00
|
|
|
|
.map(|expression| {
|
|
|
|
|
expression.evaluate(
|
2021-02-13 16:48:44 -08:00
|
|
|
|
&|scalar| pk.vk.domain.constant_lagrange(scalar),
|
2021-07-21 11:55:19 -07:00
|
|
|
|
&|_| panic!("virtual selectors are removed during optimization"),
|
2021-07-12 11:53:12 -07:00
|
|
|
|
&|_, column_index, rotation| {
|
2021-02-14 07:17:54 -08:00
|
|
|
|
fixed_values[column_index].clone().rotate(rotation)
|
2021-02-10 06:57:08 -08:00
|
|
|
|
},
|
2021-07-12 11:53:12 -07:00
|
|
|
|
&|_, column_index, rotation| {
|
2021-02-14 07:17:54 -08:00
|
|
|
|
advice_values[column_index].clone().rotate(rotation)
|
2021-02-10 06:57:08 -08:00
|
|
|
|
},
|
2021-07-12 11:53:12 -07:00
|
|
|
|
&|_, column_index, rotation| {
|
2021-02-14 07:17:54 -08:00
|
|
|
|
instance_values[column_index].clone().rotate(rotation)
|
2021-02-10 06:57:08 -08:00
|
|
|
|
},
|
|
|
|
|
&|a, b| a + &b,
|
|
|
|
|
&|a, b| {
|
2021-02-16 01:16:47 -08:00
|
|
|
|
let mut modified_a = vec![C::Scalar::one(); params.n as usize];
|
|
|
|
|
parallelize(&mut modified_a, |modified_a, start| {
|
|
|
|
|
for ((modified_a, a), b) in modified_a
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.zip(a[start..].iter())
|
|
|
|
|
.zip(b[start..].iter())
|
|
|
|
|
{
|
|
|
|
|
*modified_a *= *a * b;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
pk.vk.domain.lagrange_from_vec(modified_a)
|
2021-02-10 06:57:08 -08:00
|
|
|
|
},
|
|
|
|
|
&|a, scalar| a * scalar,
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
let unpermuted_cosets: Vec<_> = expressions
|
2020-12-02 21:40:35 -08:00
|
|
|
|
.iter()
|
2021-02-11 18:24:55 -08:00
|
|
|
|
.map(|expression| {
|
|
|
|
|
expression.evaluate(
|
2021-02-13 16:48:44 -08:00
|
|
|
|
&|scalar| pk.vk.domain.constant_extended(scalar),
|
2021-07-22 09:07:20 -07:00
|
|
|
|
&|_| panic!("virtual selectors are optimized away during keygen"),
|
2021-07-12 11:53:12 -07:00
|
|
|
|
&|_, column_index, rotation| {
|
|
|
|
|
pk.vk
|
|
|
|
|
.domain
|
|
|
|
|
.rotate_extended(&fixed_cosets[column_index], rotation)
|
|
|
|
|
},
|
|
|
|
|
&|_, column_index, rotation| {
|
|
|
|
|
pk.vk
|
|
|
|
|
.domain
|
|
|
|
|
.rotate_extended(&advice_cosets[column_index], rotation)
|
|
|
|
|
},
|
|
|
|
|
&|_, column_index, rotation| {
|
|
|
|
|
pk.vk
|
|
|
|
|
.domain
|
|
|
|
|
.rotate_extended(&instance_cosets[column_index], rotation)
|
|
|
|
|
},
|
2021-02-10 06:57:08 -08:00
|
|
|
|
&|a, b| a + &b,
|
|
|
|
|
&|a, b| a * &b,
|
|
|
|
|
&|a, scalar| a * scalar,
|
|
|
|
|
)
|
2020-12-02 21:40:35 -08:00
|
|
|
|
})
|
2021-02-10 06:57:08 -08:00
|
|
|
|
.collect();
|
2020-12-02 21:40:35 -08:00
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Compressed version of expressions
|
|
|
|
|
let compressed_expression = unpermuted_expressions
|
2020-12-02 21:40:35 -08:00
|
|
|
|
.iter()
|
2021-02-11 18:24:55 -08:00
|
|
|
|
.fold(domain.empty_lagrange(), |acc, expression| {
|
|
|
|
|
acc * *theta + expression
|
|
|
|
|
});
|
2020-12-02 21:40:35 -08:00
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
(
|
|
|
|
|
unpermuted_expressions,
|
|
|
|
|
unpermuted_cosets,
|
|
|
|
|
compressed_expression,
|
|
|
|
|
)
|
2020-12-02 21:40:35 -08:00
|
|
|
|
};
|
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Closure to construct commitment to vector of values
|
|
|
|
|
let commit_values = |values: &Polynomial<C::Scalar, LagrangeCoeff>| {
|
|
|
|
|
let poly = pk.vk.domain.lagrange_to_coeff(values.clone());
|
2020-12-02 21:40:35 -08:00
|
|
|
|
let blind = Blind(C::Scalar::rand());
|
2021-06-21 10:19:15 -07:00
|
|
|
|
let commitment = params.commit_lagrange(values, blind).to_affine();
|
2020-12-02 21:40:35 -08:00
|
|
|
|
(poly, blind, commitment)
|
|
|
|
|
};
|
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Get values of input expressions involved in the lookup and compress them
|
|
|
|
|
let (unpermuted_input_expressions, unpermuted_input_cosets, compressed_input_expression) =
|
|
|
|
|
compress_expressions(&self.input_expressions);
|
2020-12-02 21:40:35 -08:00
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Get values of table expressions involved in the lookup and compress them
|
|
|
|
|
let (unpermuted_table_expressions, unpermuted_table_cosets, compressed_table_expression) =
|
|
|
|
|
compress_expressions(&self.table_expressions);
|
2020-11-30 23:22:11 -08:00
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Permute compressed (InputExpression, TableExpression) pair
|
|
|
|
|
let (permuted_input_expression, permuted_table_expression) = permute_expression_pair::<C>(
|
2021-07-08 11:46:38 -07:00
|
|
|
|
pk,
|
|
|
|
|
params,
|
2021-02-11 18:24:55 -08:00
|
|
|
|
domain,
|
|
|
|
|
&compressed_input_expression,
|
|
|
|
|
&compressed_table_expression,
|
|
|
|
|
)?;
|
2020-12-02 21:40:35 -08:00
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Commit to permuted input expression
|
2020-12-02 21:40:35 -08:00
|
|
|
|
let (permuted_input_poly, permuted_input_blind, permuted_input_commitment) =
|
2021-02-11 18:24:55 -08:00
|
|
|
|
commit_values(&permuted_input_expression);
|
2020-12-02 21:40:35 -08:00
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Commit to permuted table expression
|
2020-12-02 21:40:35 -08:00
|
|
|
|
let (permuted_table_poly, permuted_table_blind, permuted_table_commitment) =
|
2021-02-11 18:24:55 -08:00
|
|
|
|
commit_values(&permuted_table_expression);
|
2020-12-02 21:40:35 -08:00
|
|
|
|
|
|
|
|
|
// Hash permuted input commitment
|
|
|
|
|
transcript
|
2020-12-23 12:03:31 -08:00
|
|
|
|
.write_point(permuted_input_commitment)
|
2020-12-02 21:40:35 -08:00
|
|
|
|
.map_err(|_| Error::TranscriptError)?;
|
|
|
|
|
|
|
|
|
|
// Hash permuted table commitment
|
|
|
|
|
transcript
|
2020-12-23 12:03:31 -08:00
|
|
|
|
.write_point(permuted_table_commitment)
|
2020-12-02 21:40:35 -08:00
|
|
|
|
.map_err(|_| Error::TranscriptError)?;
|
2020-11-30 23:22:11 -08:00
|
|
|
|
|
2021-07-12 11:57:09 -07:00
|
|
|
|
let permuted_input_coset = pk.vk.domain.coeff_to_extended(permuted_input_poly.clone());
|
|
|
|
|
let permuted_table_coset = pk.vk.domain.coeff_to_extended(permuted_table_poly.clone());
|
2020-11-30 23:22:11 -08:00
|
|
|
|
|
|
|
|
|
Ok(Permuted {
|
2021-02-11 18:24:55 -08:00
|
|
|
|
unpermuted_input_expressions,
|
2020-12-02 19:56:22 -08:00
|
|
|
|
unpermuted_input_cosets,
|
2021-02-11 18:24:55 -08:00
|
|
|
|
permuted_input_expression,
|
2020-11-30 23:22:11 -08:00
|
|
|
|
permuted_input_poly,
|
|
|
|
|
permuted_input_coset,
|
|
|
|
|
permuted_input_blind,
|
|
|
|
|
permuted_input_commitment,
|
2021-02-11 18:24:55 -08:00
|
|
|
|
unpermuted_table_expressions,
|
2020-12-02 19:56:22 -08:00
|
|
|
|
unpermuted_table_cosets,
|
2021-02-11 18:24:55 -08:00
|
|
|
|
permuted_table_expression,
|
2020-11-30 23:22:11 -08:00
|
|
|
|
permuted_table_poly,
|
|
|
|
|
permuted_table_coset,
|
|
|
|
|
permuted_table_blind,
|
|
|
|
|
permuted_table_commitment,
|
|
|
|
|
})
|
|
|
|
|
}
|
2020-12-02 19:56:22 -08:00
|
|
|
|
}
|
2020-11-30 23:30:52 -08:00
|
|
|
|
|
2021-02-10 06:57:08 -08:00
|
|
|
|
impl<C: CurveAffine> Permuted<C> {
|
2021-02-11 18:24:55 -08:00
|
|
|
|
/// Given a Lookup with input expressions, table expressions, and the permuted
|
|
|
|
|
/// input expression and permuted table expression, this method constructs the
|
2020-11-30 23:30:52 -08:00
|
|
|
|
/// 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.
|
2021-05-07 07:21:54 -07:00
|
|
|
|
pub(in crate::plonk) fn commit_product<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
2020-12-02 19:56:22 -08:00
|
|
|
|
self,
|
2020-11-30 23:30:52 -08:00
|
|
|
|
pk: &ProvingKey<C>,
|
|
|
|
|
params: &Params<C>,
|
2020-12-23 12:03:31 -08:00
|
|
|
|
theta: ChallengeTheta<C>,
|
|
|
|
|
beta: ChallengeBeta<C>,
|
|
|
|
|
gamma: ChallengeGamma<C>,
|
|
|
|
|
transcript: &mut T,
|
2021-02-10 06:57:08 -08:00
|
|
|
|
) -> Result<Committed<C>, Error> {
|
2021-07-08 11:46:38 -07:00
|
|
|
|
let blinding_factors = pk.vk.cs.blinding_factors();
|
2020-11-30 23:30:52 -08:00
|
|
|
|
// Goal is to compute the products of fractions
|
|
|
|
|
//
|
2020-12-05 14:58:48 -08:00
|
|
|
|
// 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)
|
2020-11-30 23:30:52 -08:00
|
|
|
|
//
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// 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.
|
2020-12-02 20:39:44 -08:00
|
|
|
|
let mut lookup_product = vec![C::Scalar::zero(); params.n as usize];
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Denominator uses the permuted input expression and permuted table expression
|
2020-11-30 23:30:52 -08:00
|
|
|
|
parallelize(&mut lookup_product, |lookup_product, start| {
|
|
|
|
|
for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product
|
|
|
|
|
.iter_mut()
|
2021-02-11 18:24:55 -08:00
|
|
|
|
.zip(self.permuted_input_expression[start..].iter())
|
|
|
|
|
.zip(self.permuted_table_expression[start..].iter())
|
2020-11-30 23:30:52 -08:00
|
|
|
|
{
|
2020-12-02 20:39:44 -08:00
|
|
|
|
*lookup_product = (*beta + permuted_input_value) * &(*gamma + permuted_table_value);
|
2020-11-30 23:30:52 -08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 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
|
2020-12-05 14:58:48 -08:00
|
|
|
|
// (\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)
|
2020-12-04 20:51:28 -08:00
|
|
|
|
parallelize(&mut lookup_product, |product, start| {
|
|
|
|
|
for (i, product) in product.iter_mut().enumerate() {
|
|
|
|
|
let i = i + start;
|
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Compress unpermuted input expressions
|
2020-12-04 20:51:28 -08:00
|
|
|
|
let mut input_term = C::Scalar::zero();
|
2021-02-11 18:24:55 -08:00
|
|
|
|
for unpermuted_input_expression in self.unpermuted_input_expressions.iter() {
|
2021-01-05 16:00:27 -08:00
|
|
|
|
input_term *= &*theta;
|
2021-02-11 18:24:55 -08:00
|
|
|
|
input_term += &unpermuted_input_expression[i];
|
2020-11-30 23:30:52 -08:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Compress unpermuted table expressions
|
2020-12-04 20:51:28 -08:00
|
|
|
|
let mut table_term = C::Scalar::zero();
|
2021-02-11 18:24:55 -08:00
|
|
|
|
for unpermuted_table_expression in self.unpermuted_table_expressions.iter() {
|
2021-01-05 16:00:27 -08:00
|
|
|
|
table_term *= &*theta;
|
2021-02-11 18:24:55 -08:00
|
|
|
|
table_term += &unpermuted_table_expression[i];
|
2020-11-30 23:30:52 -08:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-05 16:00:27 -08:00
|
|
|
|
*product *= &(input_term + &*beta);
|
|
|
|
|
*product *= &(table_term + &*gamma);
|
2020-11-30 23:30:52 -08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// The product vector is a vector of products of fractions of the form
|
|
|
|
|
//
|
2020-12-05 14:58:48 -08:00
|
|
|
|
// 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)
|
2020-11-30 23:30:52 -08:00
|
|
|
|
//
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// 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.
|
2020-11-30 23:30:52 -08:00
|
|
|
|
|
|
|
|
|
// Compute the evaluations of the lookup product polynomial
|
|
|
|
|
// over our domain, starting with z[0] = 1
|
2020-12-02 20:39:44 -08:00
|
|
|
|
let z = iter::once(C::Scalar::one())
|
2021-07-08 11:46:38 -07:00
|
|
|
|
.chain(lookup_product)
|
2020-12-02 20:39:44 -08:00
|
|
|
|
.scan(C::Scalar::one(), |state, cur| {
|
|
|
|
|
*state *= &cur;
|
|
|
|
|
Some(*state)
|
|
|
|
|
})
|
2021-07-08 11:46:38 -07:00
|
|
|
|
// 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::rand()))
|
2020-12-02 20:39:44 -08:00
|
|
|
|
.collect::<Vec<_>>();
|
2021-07-08 11:46:38 -07:00
|
|
|
|
assert_eq!(z.len(), params.n as usize);
|
2020-11-30 23:30:52 -08:00
|
|
|
|
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
|
2021-07-13 15:12:21 -07:00
|
|
|
|
let u = (params.n as usize) - (blinding_factors + 1);
|
2020-11-30 23:30:52 -08:00
|
|
|
|
|
2021-07-08 11:46:38 -07:00
|
|
|
|
// 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)
|
2021-07-13 15:12:21 -07:00
|
|
|
|
for i in 0..u {
|
2021-07-09 11:41:52 -07:00
|
|
|
|
let mut left = z[i + 1];
|
2021-02-11 18:24:55 -08:00
|
|
|
|
let permuted_input_value = &self.permuted_input_expression[i];
|
2020-11-30 23:30:52 -08:00
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
let permuted_table_value = &self.permuted_table_expression[i];
|
2020-11-30 23:30:52 -08:00
|
|
|
|
|
|
|
|
|
left *= &(*beta + permuted_input_value);
|
|
|
|
|
left *= &(*gamma + permuted_table_value);
|
|
|
|
|
|
2021-07-08 11:46:38 -07:00
|
|
|
|
let mut right = z[i];
|
2021-02-14 08:28:51 -08:00
|
|
|
|
let mut input_term = self
|
2021-02-11 18:24:55 -08:00
|
|
|
|
.unpermuted_input_expressions
|
2020-11-30 23:30:52 -08:00
|
|
|
|
.iter()
|
2021-01-05 16:00:27 -08:00
|
|
|
|
.fold(C::Scalar::zero(), |acc, input| acc * &*theta + &input[i]);
|
2020-11-30 23:30:52 -08:00
|
|
|
|
|
2021-02-14 08:28:51 -08:00
|
|
|
|
let mut table_term = self
|
2021-02-11 18:24:55 -08:00
|
|
|
|
.unpermuted_table_expressions
|
2020-11-30 23:30:52 -08:00
|
|
|
|
.iter()
|
2021-01-05 16:00:27 -08:00
|
|
|
|
.fold(C::Scalar::zero(), |acc, table| acc * &*theta + &table[i]);
|
2020-11-30 23:30:52 -08:00
|
|
|
|
|
|
|
|
|
input_term += &(*beta);
|
|
|
|
|
table_term += &(*gamma);
|
|
|
|
|
right *= &(input_term * &table_term);
|
|
|
|
|
|
|
|
|
|
assert_eq!(left, right);
|
|
|
|
|
}
|
2021-07-08 11:46:38 -07:00
|
|
|
|
|
|
|
|
|
// l_last(X) * (z(X)^2 - z(X)) = 0
|
|
|
|
|
// Assertion will fail only when soundness is broken, in which
|
2021-07-14 08:46:29 -07:00
|
|
|
|
// case this z[u] value will be zero. (bad!)
|
|
|
|
|
assert_eq!(z[u], C::Scalar::one());
|
2020-11-30 23:30:52 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let product_blind = Blind(C::Scalar::rand());
|
|
|
|
|
let product_commitment = params.commit_lagrange(&z, product_blind).to_affine();
|
|
|
|
|
let z = pk.vk.domain.lagrange_to_coeff(z);
|
2021-07-12 11:57:09 -07:00
|
|
|
|
let product_coset = pk.vk.domain.coeff_to_extended(z.clone());
|
2020-11-30 23:30:52 -08:00
|
|
|
|
|
2020-12-02 20:39:44 -08:00
|
|
|
|
// Hash product commitment
|
2020-11-30 23:30:52 -08:00
|
|
|
|
transcript
|
2020-12-23 12:03:31 -08:00
|
|
|
|
.write_point(product_commitment)
|
2020-11-30 23:30:52 -08:00
|
|
|
|
.map_err(|_| Error::TranscriptError)?;
|
|
|
|
|
|
2021-02-10 06:57:08 -08:00
|
|
|
|
Ok(Committed::<C> {
|
2020-12-02 19:56:22 -08:00
|
|
|
|
permuted: self,
|
2020-11-30 23:30:52 -08:00
|
|
|
|
product_poly: z,
|
|
|
|
|
product_coset,
|
|
|
|
|
product_commitment,
|
|
|
|
|
product_blind,
|
|
|
|
|
})
|
|
|
|
|
}
|
2020-11-30 23:22:11 -08:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-10 06:57:08 -08:00
|
|
|
|
impl<'a, C: CurveAffine> Committed<C> {
|
2021-02-11 18:24:55 -08:00
|
|
|
|
/// Given a Lookup with input expressions, table expressions, permuted input
|
|
|
|
|
/// expression, permuted table expression, and grand product polynomial, this
|
2020-12-01 00:19:07 -08:00
|
|
|
|
/// method constructs constraints that must hold between these values.
|
|
|
|
|
/// This method returns the constraints as a vector of polynomials in
|
|
|
|
|
/// the extended evaluation domain.
|
2020-12-02 19:56:22 -08:00
|
|
|
|
pub(in crate::plonk) fn construct(
|
2020-12-01 00:19:07 -08:00
|
|
|
|
self,
|
|
|
|
|
pk: &'a ProvingKey<C>,
|
2020-12-23 12:03:31 -08:00
|
|
|
|
theta: ChallengeTheta<C>,
|
|
|
|
|
beta: ChallengeBeta<C>,
|
|
|
|
|
gamma: ChallengeGamma<C>,
|
2021-01-14 05:29:19 -08:00
|
|
|
|
) -> (
|
|
|
|
|
Constructed<C>,
|
|
|
|
|
impl Iterator<Item = Polynomial<C::Scalar, ExtendedLagrangeCoeff>> + 'a,
|
|
|
|
|
) {
|
2021-07-12 11:53:12 -07:00
|
|
|
|
let domain = &pk.vk.domain;
|
2020-12-01 00:19:07 -08:00
|
|
|
|
let permuted = self.permuted;
|
|
|
|
|
|
2021-07-09 08:18:45 -07:00
|
|
|
|
let active_rows = Polynomial::one_minus(pk.l_last.clone() + &pk.l_blind);
|
2021-07-08 11:46:38 -07:00
|
|
|
|
|
2020-12-01 00:19:07 -08:00
|
|
|
|
let expressions = iter::empty()
|
2021-07-08 11:46:38 -07:00
|
|
|
|
// l_0(X) * (1 - z(X)) = 0
|
2020-12-01 00:19:07 -08:00
|
|
|
|
.chain(Some(
|
2020-12-02 19:56:22 -08:00
|
|
|
|
Polynomial::one_minus(self.product_coset.clone()) * &pk.l0,
|
2020-12-01 00:19:07 -08:00
|
|
|
|
))
|
2021-07-08 11:46:38 -07:00
|
|
|
|
// l_last(X) * (z(X)^2 - z(X)) = 0
|
|
|
|
|
.chain(Some(
|
|
|
|
|
(self.product_coset.clone() * &self.product_coset - &self.product_coset)
|
|
|
|
|
* &pk.l_last,
|
|
|
|
|
))
|
2021-07-13 15:30:52 -07:00
|
|
|
|
// (1 - (l_last(X) + l_blind(X))) * (
|
2021-07-08 11:46:38 -07:00
|
|
|
|
// 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
|
2020-12-01 00:19:07 -08:00
|
|
|
|
.chain({
|
2021-07-08 11:46:38 -07:00
|
|
|
|
// z(\omega X) (a'(X) + \beta) (s'(X) + \gamma)
|
2021-07-12 11:53:12 -07:00
|
|
|
|
let mut left = domain.rotate_extended(&self.product_coset, Rotation::next());
|
2020-12-01 00:19:07 -08:00
|
|
|
|
parallelize(&mut left, |left, start| {
|
|
|
|
|
for ((left, permuted_input), permuted_table) in left
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.zip(permuted.permuted_input_coset[start..].iter())
|
|
|
|
|
.zip(permuted.permuted_table_coset[start..].iter())
|
|
|
|
|
{
|
2020-12-01 11:00:59 -08:00
|
|
|
|
*left *= &(*permuted_input + &(*beta));
|
|
|
|
|
*left *= &(*permuted_table + &(*gamma));
|
2020-12-01 00:19:07 -08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2021-07-08 11:46:38 -07:00
|
|
|
|
// z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma)
|
|
|
|
|
let mut right = self.product_coset;
|
2020-12-04 20:51:28 -08:00
|
|
|
|
parallelize(&mut right, |right, start| {
|
|
|
|
|
for (i, right) in right.iter_mut().enumerate() {
|
|
|
|
|
let i = i + start;
|
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Compress the unpermuted input expressions
|
2020-12-04 20:51:28 -08:00
|
|
|
|
let mut input_term = C::Scalar::zero();
|
|
|
|
|
for input in permuted.unpermuted_input_cosets.iter() {
|
2021-01-05 16:00:27 -08:00
|
|
|
|
input_term *= &*theta;
|
2020-12-04 20:51:28 -08:00
|
|
|
|
input_term += &input[i];
|
2020-12-01 00:19:07 -08:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Compress the unpermuted table expressions
|
2020-12-04 20:51:28 -08:00
|
|
|
|
let mut table_term = C::Scalar::zero();
|
|
|
|
|
for table in permuted.unpermuted_table_cosets.iter() {
|
2021-01-05 16:00:27 -08:00
|
|
|
|
table_term *= &*theta;
|
2020-12-04 20:51:28 -08:00
|
|
|
|
table_term += &table[i];
|
2020-12-01 00:19:07 -08:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-04 20:51:28 -08:00
|
|
|
|
// Add \beta and \gamma offsets
|
2021-01-05 16:00:27 -08:00
|
|
|
|
*right *= &(input_term + &*beta);
|
|
|
|
|
*right *= &(table_term + &*gamma);
|
2020-12-01 00:19:07 -08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2021-07-08 11:46:38 -07:00
|
|
|
|
Some((left - &right) * &active_rows)
|
2020-12-01 00:19:07 -08:00
|
|
|
|
})
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Check that the first values in the permuted input expression and permuted
|
|
|
|
|
// fixed expression are the same.
|
2020-12-01 00:19:07 -08:00
|
|
|
|
// l_0(X) * (a'(X) - s'(X)) = 0
|
|
|
|
|
.chain(Some(
|
|
|
|
|
(permuted.permuted_input_coset.clone() - &permuted.permuted_table_coset) * &pk.l0,
|
|
|
|
|
))
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Check that each value in the permuted lookup input expression is either
|
2020-12-01 00:19:07 -08:00
|
|
|
|
// equal to the value above it, or the value at the same index in the
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// permuted table expression.
|
2021-07-09 11:44:27 -07:00
|
|
|
|
// (1 - (l_last + l_blind)) * (a′(X) − s′(X))⋅(a′(X) − a′(\omega^{-1} X)) = 0
|
2020-12-01 00:19:07 -08:00
|
|
|
|
.chain(Some(
|
|
|
|
|
(permuted.permuted_input_coset.clone() - &permuted.permuted_table_coset)
|
2021-07-12 11:53:12 -07:00
|
|
|
|
* &(domain.sub_extended(
|
|
|
|
|
permuted.permuted_input_coset.clone(),
|
|
|
|
|
&permuted.permuted_input_coset,
|
|
|
|
|
Rotation::prev(),
|
|
|
|
|
))
|
2021-07-08 11:46:38 -07:00
|
|
|
|
* &active_rows,
|
2020-12-01 00:19:07 -08:00
|
|
|
|
));
|
|
|
|
|
|
2021-01-14 05:29:19 -08:00
|
|
|
|
(
|
2020-12-01 00:19:07 -08:00
|
|
|
|
Constructed {
|
|
|
|
|
permuted_input_poly: permuted.permuted_input_poly,
|
|
|
|
|
permuted_input_blind: permuted.permuted_input_blind,
|
|
|
|
|
permuted_table_poly: permuted.permuted_table_poly,
|
|
|
|
|
permuted_table_blind: permuted.permuted_table_blind,
|
2020-12-02 19:56:22 -08:00
|
|
|
|
product_poly: self.product_poly,
|
|
|
|
|
product_blind: self.product_blind,
|
2020-12-01 00:19:07 -08:00
|
|
|
|
},
|
|
|
|
|
expressions,
|
2021-01-14 05:29:19 -08:00
|
|
|
|
)
|
2020-12-01 00:19:07 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 00:33:23 -08:00
|
|
|
|
impl<C: CurveAffine> Constructed<C> {
|
2021-05-07 07:21:54 -07:00
|
|
|
|
pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
|
2020-12-01 00:33:23 -08:00
|
|
|
|
self,
|
|
|
|
|
pk: &ProvingKey<C>,
|
2020-12-23 12:03:31 -08:00
|
|
|
|
x: ChallengeX<C>,
|
|
|
|
|
transcript: &mut T,
|
|
|
|
|
) -> Result<Evaluated<C>, Error> {
|
2020-12-01 00:33:23 -08:00
|
|
|
|
let domain = &pk.vk.domain;
|
2021-07-08 11:46:38 -07:00
|
|
|
|
let x_inv = domain.rotate_omega(*x, Rotation::prev());
|
|
|
|
|
let x_next = domain.rotate_omega(*x, Rotation::next());
|
2020-12-01 00:33:23 -08:00
|
|
|
|
|
|
|
|
|
let product_eval = eval_polynomial(&self.product_poly, *x);
|
2021-07-08 11:46:38 -07:00
|
|
|
|
let product_next_eval = eval_polynomial(&self.product_poly, x_next);
|
2020-12-01 00:33:23 -08:00
|
|
|
|
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))
|
2021-07-08 11:46:38 -07:00
|
|
|
|
.chain(Some(product_next_eval))
|
2020-12-01 00:33:23 -08:00
|
|
|
|
.chain(Some(permuted_input_eval))
|
|
|
|
|
.chain(Some(permuted_input_inv_eval))
|
|
|
|
|
.chain(Some(permuted_table_eval))
|
|
|
|
|
{
|
2020-12-23 12:03:31 -08:00
|
|
|
|
transcript
|
|
|
|
|
.write_scalar(eval)
|
|
|
|
|
.map_err(|_| Error::TranscriptError)?;
|
2020-12-01 00:33:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-13 16:22:32 -08:00
|
|
|
|
Ok(Evaluated { constructed: self })
|
2020-12-01 00:33:23 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 00:46:11 -08:00
|
|
|
|
impl<C: CurveAffine> Evaluated<C> {
|
|
|
|
|
pub(in crate::plonk) fn open<'a>(
|
|
|
|
|
&'a self,
|
|
|
|
|
pk: &'a ProvingKey<C>,
|
2020-12-23 12:03:31 -08:00
|
|
|
|
x: ChallengeX<C>,
|
2020-12-01 00:46:11 -08:00
|
|
|
|
) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone {
|
2021-07-08 11:46:38 -07:00
|
|
|
|
let x_inv = pk.vk.domain.rotate_omega(*x, Rotation::prev());
|
|
|
|
|
let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next());
|
2020-12-01 00:46:11 -08:00
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
}))
|
2021-07-08 11:46:38 -07:00
|
|
|
|
// Open lookup product commitments at x_next
|
2020-12-01 00:46:11 -08:00
|
|
|
|
.chain(Some(ProverQuery {
|
2021-07-08 11:46:38 -07:00
|
|
|
|
point: x_next,
|
2020-12-01 00:46:11 -08:00
|
|
|
|
poly: &self.constructed.product_poly,
|
|
|
|
|
blind: self.constructed.product_blind,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
type ExpressionPair<F> = (Polynomial<F, LagrangeCoeff>, Polynomial<F, LagrangeCoeff>);
|
2021-01-14 05:20:54 -08:00
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
/// Given a vector of input values A and a vector of table values S,
|
2020-11-30 23:22:11 -08:00
|
|
|
|
/// 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.
|
2021-02-11 18:24:55 -08:00
|
|
|
|
fn permute_expression_pair<C: CurveAffine>(
|
2021-07-08 11:46:38 -07:00
|
|
|
|
pk: &ProvingKey<C>,
|
|
|
|
|
params: &Params<C>,
|
2020-11-30 23:22:11 -08:00
|
|
|
|
domain: &EvaluationDomain<C::Scalar>,
|
2021-02-11 18:24:55 -08:00
|
|
|
|
input_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
|
|
|
|
|
table_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
|
|
|
|
|
) -> Result<ExpressionPair<C::Scalar>, Error> {
|
2021-07-08 11:46:38 -07:00
|
|
|
|
let blinding_factors = pk.vk.cs.blinding_factors();
|
2021-07-09 11:45:51 -07:00
|
|
|
|
let usable_rows = params.n as usize - (blinding_factors + 1);
|
|
|
|
|
|
2021-07-08 11:46:38 -07:00
|
|
|
|
let mut permuted_input_expression: Vec<C::Scalar> = input_expression.to_vec();
|
2021-07-09 11:45:51 -07:00
|
|
|
|
permuted_input_expression.truncate(usable_rows);
|
2020-11-30 23:22:11 -08:00
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// Sort input lookup expression values
|
|
|
|
|
permuted_input_expression.sort();
|
2020-11-30 23:22:11 -08:00
|
|
|
|
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// A BTreeMap of each unique element in the table expression and its count
|
2021-07-08 11:46:38 -07:00
|
|
|
|
let mut leftover_table_map: BTreeMap<C::Scalar, u32> = table_expression
|
|
|
|
|
.iter()
|
2021-07-09 11:45:51 -07:00
|
|
|
|
.take(usable_rows)
|
2021-07-08 11:46:38 -07:00
|
|
|
|
.fold(BTreeMap::new(), |mut acc, coeff| {
|
|
|
|
|
*acc.entry(*coeff).or_insert(0) += 1;
|
|
|
|
|
acc
|
|
|
|
|
});
|
2021-07-09 11:45:51 -07:00
|
|
|
|
let mut permuted_table_coeffs = vec![C::Scalar::zero(); usable_rows];
|
2021-02-11 18:24:55 -08:00
|
|
|
|
|
|
|
|
|
let mut repeated_input_rows = permuted_input_expression
|
2020-12-02 20:39:44 -08:00
|
|
|
|
.iter()
|
|
|
|
|
.zip(permuted_table_coeffs.iter_mut())
|
|
|
|
|
.enumerate()
|
|
|
|
|
.filter_map(|(row, (input_value, table_value))| {
|
2021-02-11 18:24:55 -08:00
|
|
|
|
// If this is the first occurence of `input_value` in the input expression
|
|
|
|
|
if row == 0 || *input_value != permuted_input_expression[row - 1] {
|
2020-12-02 20:39:44 -08:00
|
|
|
|
*table_value = *input_value;
|
|
|
|
|
// Remove one instance of input_value from leftover_table_map
|
2021-06-21 10:19:15 -07:00
|
|
|
|
if let Some(count) = leftover_table_map.get_mut(input_value) {
|
2020-12-02 20:39:44 -08:00
|
|
|
|
assert!(*count > 0);
|
|
|
|
|
*count -= 1;
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
// Return error if input_value not found
|
|
|
|
|
Some(Err(Error::ConstraintSystemFailure))
|
|
|
|
|
}
|
|
|
|
|
// If input value is repeated
|
2020-11-30 23:22:11 -08:00
|
|
|
|
} else {
|
2020-12-02 20:39:44 -08:00
|
|
|
|
Some(Ok(row))
|
2020-11-30 23:22:11 -08:00
|
|
|
|
}
|
2020-12-02 20:39:44 -08:00
|
|
|
|
})
|
|
|
|
|
.collect::<Result<Vec<_>, _>>()?;
|
2020-11-30 23:22:11 -08:00
|
|
|
|
|
|
|
|
|
// 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() as usize] = *coeff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert!(repeated_input_rows.is_empty());
|
|
|
|
|
|
2021-07-08 11:46:38 -07:00
|
|
|
|
permuted_input_expression.extend((0..(blinding_factors + 1)).map(|_| C::Scalar::rand()));
|
|
|
|
|
permuted_table_coeffs.extend((0..(blinding_factors + 1)).map(|_| C::Scalar::rand()));
|
|
|
|
|
assert_eq!(permuted_input_expression.len(), params.n as usize);
|
|
|
|
|
assert_eq!(permuted_table_coeffs.len(), params.n as usize);
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "sanity-checks")]
|
|
|
|
|
{
|
2021-07-09 11:47:36 -07:00
|
|
|
|
let mut last = None;
|
2021-07-08 11:46:38 -07:00
|
|
|
|
for (a, b) in permuted_input_expression
|
|
|
|
|
.iter()
|
|
|
|
|
.zip(permuted_table_coeffs.iter())
|
2021-07-09 11:45:51 -07:00
|
|
|
|
.take(usable_rows)
|
2021-07-08 11:46:38 -07:00
|
|
|
|
{
|
|
|
|
|
if *a != *b {
|
2021-07-09 11:47:36 -07:00
|
|
|
|
assert_eq!(*a, last.unwrap());
|
2020-11-30 23:22:11 -08:00
|
|
|
|
}
|
2021-07-09 11:47:36 -07:00
|
|
|
|
last = Some(*a);
|
2021-07-08 11:46:38 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-30 23:22:11 -08:00
|
|
|
|
|
2021-07-08 11:46:38 -07:00
|
|
|
|
Ok((
|
|
|
|
|
domain.lagrange_from_vec(permuted_input_expression),
|
|
|
|
|
domain.lagrange_from_vec(permuted_table_coeffs),
|
|
|
|
|
))
|
2020-11-30 23:22:11 -08:00
|
|
|
|
}
|