1use super::super::{
2 circuit::Expression, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error,
3 ProvingKey,
4};
5use super::Argument;
6use crate::{
7 arithmetic::{eval_polynomial, parallelize, CurveAffine, FieldExt},
8 poly::{
9 self,
10 commitment::{Blind, Params},
11 multiopen::ProverQuery,
12 Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation,
13 },
14 transcript::{EncodedChallenge, TranscriptWrite},
15};
16use group::{
17 ff::{BatchInvert, Field},
18 Curve,
19};
20use rand_core::RngCore;
21use std::{
22 collections::BTreeMap,
23 iter,
24 ops::{Mul, MulAssign},
25};
26
27#[derive(Debug)]
28pub(in crate::plonk) struct Permuted<C: CurveAffine, Ev> {
29 unpermuted_input_expressions: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
30 unpermuted_input_cosets: Vec<poly::Ast<Ev, C::Scalar, ExtendedLagrangeCoeff>>,
31 permuted_input_expression: Polynomial<C::Scalar, LagrangeCoeff>,
32 permuted_input_poly: Polynomial<C::Scalar, Coeff>,
33 permuted_input_coset: poly::AstLeaf<Ev, ExtendedLagrangeCoeff>,
34 permuted_input_blind: Blind<C::Scalar>,
35 unpermuted_table_expressions: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
36 unpermuted_table_cosets: Vec<poly::Ast<Ev, C::Scalar, ExtendedLagrangeCoeff>>,
37 permuted_table_expression: Polynomial<C::Scalar, LagrangeCoeff>,
38 permuted_table_poly: Polynomial<C::Scalar, Coeff>,
39 permuted_table_coset: poly::AstLeaf<Ev, ExtendedLagrangeCoeff>,
40 permuted_table_blind: Blind<C::Scalar>,
41}
42
43#[derive(Debug)]
44pub(in crate::plonk) struct Committed<C: CurveAffine, Ev> {
45 permuted: Permuted<C, Ev>,
46 product_poly: Polynomial<C::Scalar, Coeff>,
47 product_coset: poly::AstLeaf<Ev, ExtendedLagrangeCoeff>,
48 product_blind: Blind<C::Scalar>,
49}
50
51pub(in crate::plonk) struct Constructed<C: CurveAffine> {
52 permuted_input_poly: Polynomial<C::Scalar, Coeff>,
53 permuted_input_blind: Blind<C::Scalar>,
54 permuted_table_poly: Polynomial<C::Scalar, Coeff>,
55 permuted_table_blind: Blind<C::Scalar>,
56 product_poly: Polynomial<C::Scalar, Coeff>,
57 product_blind: Blind<C::Scalar>,
58}
59
60pub(in crate::plonk) struct Evaluated<C: CurveAffine> {
61 constructed: Constructed<C>,
62}
63
64impl<F: FieldExt> Argument<F> {
65 pub(in crate::plonk) fn commit_permuted<
75 'a,
76 C,
77 E: EncodedChallenge<C>,
78 Ev: Copy + Send + Sync,
79 Ec: Copy + Send + Sync,
80 R: RngCore,
81 T: TranscriptWrite<C, E>,
82 >(
83 &self,
84 pk: &ProvingKey<C>,
85 params: &Params<C>,
86 domain: &EvaluationDomain<C::Scalar>,
87 value_evaluator: &poly::Evaluator<Ev, C::Scalar, LagrangeCoeff>,
88 coset_evaluator: &mut poly::Evaluator<Ec, C::Scalar, ExtendedLagrangeCoeff>,
89 theta: ChallengeTheta<C>,
90 advice_values: &'a [poly::AstLeaf<Ev, LagrangeCoeff>],
91 fixed_values: &'a [poly::AstLeaf<Ev, LagrangeCoeff>],
92 instance_values: &'a [poly::AstLeaf<Ev, LagrangeCoeff>],
93 advice_cosets: &'a [poly::AstLeaf<Ec, ExtendedLagrangeCoeff>],
94 fixed_cosets: &'a [poly::AstLeaf<Ec, ExtendedLagrangeCoeff>],
95 instance_cosets: &'a [poly::AstLeaf<Ec, ExtendedLagrangeCoeff>],
96 mut rng: R,
97 transcript: &mut T,
98 ) -> Result<Permuted<C, Ec>, Error>
99 where
100 C: CurveAffine<ScalarExt = F>,
101 C::Curve: Mul<F, Output = C::Curve> + MulAssign<F>,
102 {
103 let compress_expressions = |expressions: &[Expression<C::Scalar>]| {
105 let unpermuted_expressions: Vec<_> = expressions
107 .iter()
108 .map(|expression| {
109 expression.evaluate(
110 &|scalar| poly::Ast::ConstantTerm(scalar),
111 &|_| panic!("virtual selectors are removed during optimization"),
112 &|_, column_index, rotation| {
113 fixed_values[column_index].with_rotation(rotation).into()
114 },
115 &|_, column_index, rotation| {
116 advice_values[column_index].with_rotation(rotation).into()
117 },
118 &|_, column_index, rotation| {
119 instance_values[column_index].with_rotation(rotation).into()
120 },
121 &|a| -a,
122 &|a, b| a + b,
123 &|a, b| a * b,
124 &|a, scalar| a * scalar,
125 )
126 })
127 .collect();
128
129 let unpermuted_cosets: Vec<_> = expressions
130 .iter()
131 .map(|expression| {
132 expression.evaluate(
133 &|scalar| poly::Ast::ConstantTerm(scalar),
134 &|_| panic!("virtual selectors are removed during optimization"),
135 &|_, column_index, rotation| {
136 fixed_cosets[column_index].with_rotation(rotation).into()
137 },
138 &|_, column_index, rotation| {
139 advice_cosets[column_index].with_rotation(rotation).into()
140 },
141 &|_, column_index, rotation| {
142 instance_cosets[column_index].with_rotation(rotation).into()
143 },
144 &|a| -a,
145 &|a, b| a + b,
146 &|a, b| a * b,
147 &|a, scalar| a * scalar,
148 )
149 })
150 .collect();
151
152 let compressed_expression = unpermuted_expressions.iter().fold(
154 poly::Ast::ConstantTerm(C::Scalar::zero()),
155 |acc, expression| &(acc * *theta) + expression,
156 );
157
158 (
159 unpermuted_expressions
160 .iter()
161 .map(|ast| value_evaluator.evaluate(ast, domain))
162 .collect(),
163 unpermuted_cosets,
164 value_evaluator.evaluate(&compressed_expression, domain),
165 )
166 };
167
168 let (unpermuted_input_expressions, unpermuted_input_cosets, compressed_input_expression) =
170 compress_expressions(&self.input_expressions);
171
172 let (unpermuted_table_expressions, unpermuted_table_cosets, compressed_table_expression) =
174 compress_expressions(&self.table_expressions);
175
176 let (permuted_input_expression, permuted_table_expression) = permute_expression_pair::<C, _>(
178 pk,
179 params,
180 domain,
181 &mut rng,
182 &compressed_input_expression,
183 &compressed_table_expression,
184 )?;
185
186 let mut commit_values = |values: &Polynomial<C::Scalar, LagrangeCoeff>| {
188 let poly = pk.vk.domain.lagrange_to_coeff(values.clone());
189 let blind = Blind(C::Scalar::random(&mut rng));
190 let commitment = params.commit_lagrange(values, blind).to_affine();
191 (poly, blind, commitment)
192 };
193
194 let (permuted_input_poly, permuted_input_blind, permuted_input_commitment) =
196 commit_values(&permuted_input_expression);
197
198 let (permuted_table_poly, permuted_table_blind, permuted_table_commitment) =
200 commit_values(&permuted_table_expression);
201
202 transcript.write_point(permuted_input_commitment)?;
204
205 transcript.write_point(permuted_table_commitment)?;
207
208 let permuted_input_coset = coset_evaluator
209 .register_poly(pk.vk.domain.coeff_to_extended(permuted_input_poly.clone()));
210 let permuted_table_coset = coset_evaluator
211 .register_poly(pk.vk.domain.coeff_to_extended(permuted_table_poly.clone()));
212
213 Ok(Permuted {
214 unpermuted_input_expressions,
215 unpermuted_input_cosets,
216 permuted_input_expression,
217 permuted_input_poly,
218 permuted_input_coset,
219 permuted_input_blind,
220 unpermuted_table_expressions,
221 unpermuted_table_cosets,
222 permuted_table_expression,
223 permuted_table_poly,
224 permuted_table_coset,
225 permuted_table_blind,
226 })
227 }
228}
229
230impl<C: CurveAffine, Ev: Copy + Send + Sync> Permuted<C, Ev> {
231 pub(in crate::plonk) fn commit_product<
237 E: EncodedChallenge<C>,
238 R: RngCore,
239 T: TranscriptWrite<C, E>,
240 >(
241 self,
242 pk: &ProvingKey<C>,
243 params: &Params<C>,
244 theta: ChallengeTheta<C>,
245 beta: ChallengeBeta<C>,
246 gamma: ChallengeGamma<C>,
247 evaluator: &mut poly::Evaluator<Ev, C::Scalar, ExtendedLagrangeCoeff>,
248 mut rng: R,
249 transcript: &mut T,
250 ) -> Result<Committed<C, Ev>, Error> {
251 let blinding_factors = pk.vk.cs.blinding_factors();
252 let mut lookup_product = vec![C::Scalar::zero(); params.n as usize];
264 parallelize(&mut lookup_product, |lookup_product, start| {
266 for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product
267 .iter_mut()
268 .zip(self.permuted_input_expression[start..].iter())
269 .zip(self.permuted_table_expression[start..].iter())
270 {
271 *lookup_product = (*beta + permuted_input_value) * &(*gamma + permuted_table_value);
272 }
273 });
274
275 lookup_product.iter_mut().batch_invert();
278
279 parallelize(&mut lookup_product, |product, start| {
283 for (i, product) in product.iter_mut().enumerate() {
284 let i = i + start;
285
286 let mut input_term = C::Scalar::zero();
288 for unpermuted_input_expression in self.unpermuted_input_expressions.iter() {
289 input_term *= &*theta;
290 input_term += &unpermuted_input_expression[i];
291 }
292
293 let mut table_term = C::Scalar::zero();
295 for unpermuted_table_expression in self.unpermuted_table_expressions.iter() {
296 table_term *= &*theta;
297 table_term += &unpermuted_table_expression[i];
298 }
299
300 *product *= &(input_term + &*beta);
301 *product *= &(table_term + &*gamma);
302 }
303 });
304
305 let z = iter::once(C::Scalar::one())
321 .chain(lookup_product)
322 .scan(C::Scalar::one(), |state, cur| {
323 *state *= &cur;
324 Some(*state)
325 })
326 .take(params.n as usize - blinding_factors)
329 .chain((0..blinding_factors).map(|_| C::Scalar::random(&mut rng)))
331 .collect::<Vec<_>>();
332 assert_eq!(z.len(), params.n as usize);
333 let z = pk.vk.domain.lagrange_from_vec(z);
334
335 #[cfg(feature = "sanity-checks")]
336 {
339 let u = (params.n as usize) - (blinding_factors + 1);
341
342 assert_eq!(z[0], C::Scalar::one());
344
345 for i in 0..u {
348 let mut left = z[i + 1];
349 let permuted_input_value = &self.permuted_input_expression[i];
350
351 let permuted_table_value = &self.permuted_table_expression[i];
352
353 left *= &(*beta + permuted_input_value);
354 left *= &(*gamma + permuted_table_value);
355
356 let mut right = z[i];
357 let mut input_term = self
358 .unpermuted_input_expressions
359 .iter()
360 .fold(C::Scalar::zero(), |acc, input| acc * &*theta + &input[i]);
361
362 let mut table_term = self
363 .unpermuted_table_expressions
364 .iter()
365 .fold(C::Scalar::zero(), |acc, table| acc * &*theta + &table[i]);
366
367 input_term += &(*beta);
368 table_term += &(*gamma);
369 right *= &(input_term * &table_term);
370
371 assert_eq!(left, right);
372 }
373
374 assert_eq!(z[u], C::Scalar::one());
378 }
379
380 let product_blind = Blind(C::Scalar::random(rng));
381 let product_commitment = params.commit_lagrange(&z, product_blind).to_affine();
382 let z = pk.vk.domain.lagrange_to_coeff(z);
383 let product_coset = evaluator.register_poly(pk.vk.domain.coeff_to_extended(z.clone()));
384
385 transcript.write_point(product_commitment)?;
387
388 Ok(Committed::<C, _> {
389 permuted: self,
390 product_poly: z,
391 product_coset,
392 product_blind,
393 })
394 }
395}
396
397impl<'a, C: CurveAffine, Ev: Copy + Send + Sync + 'a> Committed<C, Ev> {
398 pub(in crate::plonk) fn construct(
404 self,
405 theta: ChallengeTheta<C>,
406 beta: ChallengeBeta<C>,
407 gamma: ChallengeGamma<C>,
408 l0: poly::AstLeaf<Ev, ExtendedLagrangeCoeff>,
409 l_blind: poly::AstLeaf<Ev, ExtendedLagrangeCoeff>,
410 l_last: poly::AstLeaf<Ev, ExtendedLagrangeCoeff>,
411 ) -> (
412 Constructed<C>,
413 impl Iterator<Item = poly::Ast<Ev, C::Scalar, ExtendedLagrangeCoeff>> + 'a,
414 ) {
415 let permuted = self.permuted;
416
417 let active_rows = poly::Ast::one() - (poly::Ast::from(l_last) + l_blind);
418 let beta = poly::Ast::ConstantTerm(*beta);
419 let gamma = poly::Ast::ConstantTerm(*gamma);
420
421 let expressions = iter::empty()
422 .chain(Some((poly::Ast::one() - self.product_coset) * l0))
424 .chain(Some(
426 (poly::Ast::from(self.product_coset) * self.product_coset - self.product_coset)
427 * l_last,
428 ))
429 .chain({
434 let left: poly::Ast<_, _, _> = poly::Ast::<_, C::Scalar, _>::from(
436 self.product_coset.with_rotation(Rotation::next()),
437 ) * (poly::Ast::from(permuted.permuted_input_coset)
438 + beta.clone())
439 * (poly::Ast::from(permuted.permuted_table_coset) + gamma.clone());
440
441 let compress_cosets = |cosets: &[poly::Ast<_, _, _>]| {
443 cosets.iter().fold(
444 poly::Ast::<_, _, ExtendedLagrangeCoeff>::ConstantTerm(C::Scalar::zero()),
445 |acc, eval| acc * poly::Ast::ConstantTerm(*theta) + eval.clone(),
446 )
447 };
448 let right: poly::Ast<_, _, _> = poly::Ast::from(self.product_coset)
449 * (compress_cosets(&permuted.unpermuted_input_cosets) + beta)
450 * (compress_cosets(&permuted.unpermuted_table_cosets) + gamma);
451
452 Some((left - right) * active_rows.clone())
453 })
454 .chain(Some(
458 (poly::Ast::from(permuted.permuted_input_coset) - permuted.permuted_table_coset)
459 * l0,
460 ))
461 .chain(Some(
466 (poly::Ast::<_, C::Scalar, _>::from(permuted.permuted_input_coset)
467 - permuted.permuted_table_coset)
468 * (poly::Ast::from(permuted.permuted_input_coset)
469 - permuted
470 .permuted_input_coset
471 .with_rotation(Rotation::prev()))
472 * active_rows,
473 ));
474
475 (
476 Constructed {
477 permuted_input_poly: permuted.permuted_input_poly,
478 permuted_input_blind: permuted.permuted_input_blind,
479 permuted_table_poly: permuted.permuted_table_poly,
480 permuted_table_blind: permuted.permuted_table_blind,
481 product_poly: self.product_poly,
482 product_blind: self.product_blind,
483 },
484 expressions,
485 )
486 }
487}
488
489impl<C: CurveAffine> Constructed<C> {
490 pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
491 self,
492 pk: &ProvingKey<C>,
493 x: ChallengeX<C>,
494 transcript: &mut T,
495 ) -> Result<Evaluated<C>, Error> {
496 let domain = &pk.vk.domain;
497 let x_inv = domain.rotate_omega(*x, Rotation::prev());
498 let x_next = domain.rotate_omega(*x, Rotation::next());
499
500 let product_eval = eval_polynomial(&self.product_poly, *x);
501 let product_next_eval = eval_polynomial(&self.product_poly, x_next);
502 let permuted_input_eval = eval_polynomial(&self.permuted_input_poly, *x);
503 let permuted_input_inv_eval = eval_polynomial(&self.permuted_input_poly, x_inv);
504 let permuted_table_eval = eval_polynomial(&self.permuted_table_poly, *x);
505
506 for eval in iter::empty()
508 .chain(Some(product_eval))
509 .chain(Some(product_next_eval))
510 .chain(Some(permuted_input_eval))
511 .chain(Some(permuted_input_inv_eval))
512 .chain(Some(permuted_table_eval))
513 {
514 transcript.write_scalar(eval)?;
515 }
516
517 Ok(Evaluated { constructed: self })
518 }
519}
520
521impl<C: CurveAffine> Evaluated<C> {
522 pub(in crate::plonk) fn open<'a>(
523 &'a self,
524 pk: &'a ProvingKey<C>,
525 x: ChallengeX<C>,
526 ) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone {
527 let x_inv = pk.vk.domain.rotate_omega(*x, Rotation::prev());
528 let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next());
529
530 iter::empty()
531 .chain(Some(ProverQuery {
533 point: *x,
534 poly: &self.constructed.product_poly,
535 blind: self.constructed.product_blind,
536 }))
537 .chain(Some(ProverQuery {
539 point: *x,
540 poly: &self.constructed.permuted_input_poly,
541 blind: self.constructed.permuted_input_blind,
542 }))
543 .chain(Some(ProverQuery {
545 point: *x,
546 poly: &self.constructed.permuted_table_poly,
547 blind: self.constructed.permuted_table_blind,
548 }))
549 .chain(Some(ProverQuery {
551 point: x_inv,
552 poly: &self.constructed.permuted_input_poly,
553 blind: self.constructed.permuted_input_blind,
554 }))
555 .chain(Some(ProverQuery {
557 point: x_next,
558 poly: &self.constructed.product_poly,
559 blind: self.constructed.product_blind,
560 }))
561 }
562}
563
564type ExpressionPair<F> = (Polynomial<F, LagrangeCoeff>, Polynomial<F, LagrangeCoeff>);
565
566fn permute_expression_pair<C: CurveAffine, R: RngCore>(
573 pk: &ProvingKey<C>,
574 params: &Params<C>,
575 domain: &EvaluationDomain<C::Scalar>,
576 mut rng: R,
577 input_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
578 table_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
579) -> Result<ExpressionPair<C::Scalar>, Error> {
580 let blinding_factors = pk.vk.cs.blinding_factors();
581 let usable_rows = params.n as usize - (blinding_factors + 1);
582
583 let mut permuted_input_expression: Vec<C::Scalar> = input_expression.to_vec();
584 permuted_input_expression.truncate(usable_rows);
585
586 permuted_input_expression.sort();
588
589 let mut leftover_table_map: BTreeMap<C::Scalar, u32> = table_expression
591 .iter()
592 .take(usable_rows)
593 .fold(BTreeMap::new(), |mut acc, coeff| {
594 *acc.entry(*coeff).or_insert(0) += 1;
595 acc
596 });
597 let mut permuted_table_coeffs = vec![C::Scalar::zero(); usable_rows];
598
599 let mut repeated_input_rows = permuted_input_expression
600 .iter()
601 .zip(permuted_table_coeffs.iter_mut())
602 .enumerate()
603 .filter_map(|(row, (input_value, table_value))| {
604 if row == 0 || *input_value != permuted_input_expression[row - 1] {
606 *table_value = *input_value;
607 if let Some(count) = leftover_table_map.get_mut(input_value) {
609 assert!(*count > 0);
610 *count -= 1;
611 None
612 } else {
613 Some(Err(Error::ConstraintSystemFailure))
615 }
616 } else {
618 Some(Ok(row))
619 }
620 })
621 .collect::<Result<Vec<_>, _>>()?;
622
623 for (coeff, count) in leftover_table_map.iter() {
625 for _ in 0..*count {
626 permuted_table_coeffs[repeated_input_rows.pop().unwrap() as usize] = *coeff;
627 }
628 }
629 assert!(repeated_input_rows.is_empty());
630
631 permuted_input_expression
632 .extend((0..(blinding_factors + 1)).map(|_| C::Scalar::random(&mut rng)));
633 permuted_table_coeffs.extend((0..(blinding_factors + 1)).map(|_| C::Scalar::random(&mut rng)));
634 assert_eq!(permuted_input_expression.len(), params.n as usize);
635 assert_eq!(permuted_table_coeffs.len(), params.n as usize);
636
637 #[cfg(feature = "sanity-checks")]
638 {
639 let mut last = None;
640 for (a, b) in permuted_input_expression
641 .iter()
642 .zip(permuted_table_coeffs.iter())
643 .take(usable_rows)
644 {
645 if *a != *b {
646 assert_eq!(*a, last.unwrap());
647 }
648 last = Some(*a);
649 }
650 }
651
652 Ok((
653 domain.lagrange_from_vec(permuted_input_expression),
654 domain.lagrange_from_vec(permuted_table_coeffs),
655 ))
656}