1use ff::Field;
2use group::Curve;
3use rand_core::RngCore;
4use std::iter;
5use std::ops::RangeTo;
6
7use super::{
8 circuit::{
9 Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, FloorPlanner, Instance,
10 Selector,
11 },
12 lookup, permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX,
13 ChallengeY, Error, ProvingKey,
14};
15use crate::poly::{
16 self,
17 commitment::{Blind, Params},
18 multiopen::{self, ProverQuery},
19 Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial,
20};
21use crate::{
22 arithmetic::{eval_polynomial, CurveAffine, FieldExt},
23 plonk::Assigned,
24};
25use crate::{
26 poly::batch_invert_assigned,
27 transcript::{EncodedChallenge, TranscriptWrite},
28};
29
30pub fn create_proof<
35 C: CurveAffine,
36 E: EncodedChallenge<C>,
37 R: RngCore,
38 T: TranscriptWrite<C, E>,
39 ConcreteCircuit: Circuit<C::Scalar>,
40>(
41 params: &Params<C>,
42 pk: &ProvingKey<C>,
43 circuits: &[ConcreteCircuit],
44 instances: &[&[&[C::Scalar]]],
45 mut rng: R,
46 transcript: &mut T,
47) -> Result<(), Error> {
48 for instance in instances.iter() {
49 if instance.len() != pk.vk.cs.num_instance_columns {
50 return Err(Error::InvalidInstances);
51 }
52 }
53
54 pk.vk.hash_into(transcript)?;
56
57 let domain = &pk.vk.domain;
58 let mut meta = ConstraintSystem::default();
59 let config = ConcreteCircuit::configure(&mut meta);
60
61 let meta = &pk.vk.cs;
64
65 struct InstanceSingle<C: CurveAffine> {
66 pub instance_values: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
67 pub instance_polys: Vec<Polynomial<C::Scalar, Coeff>>,
68 pub instance_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
69 }
70
71 let instance: Vec<InstanceSingle<C>> = instances
72 .iter()
73 .map(|instance| -> Result<InstanceSingle<C>, Error> {
74 let instance_values = instance
75 .iter()
76 .map(|values| {
77 let mut poly = domain.empty_lagrange();
78 assert_eq!(poly.len(), params.n as usize);
79 if values.len() > (poly.len() - (meta.blinding_factors() + 1)) {
80 return Err(Error::InstanceTooLarge);
81 }
82 for (poly, value) in poly.iter_mut().zip(values.iter()) {
83 *poly = *value;
84 }
85 Ok(poly)
86 })
87 .collect::<Result<Vec<_>, _>>()?;
88 let instance_commitments_projective: Vec<_> = instance_values
89 .iter()
90 .map(|poly| params.commit_lagrange(poly, Blind::default()))
91 .collect();
92 let mut instance_commitments =
93 vec![C::identity(); instance_commitments_projective.len()];
94 C::Curve::batch_normalize(&instance_commitments_projective, &mut instance_commitments);
95 let instance_commitments = instance_commitments;
96 drop(instance_commitments_projective);
97
98 for commitment in &instance_commitments {
99 transcript.common_point(*commitment)?;
100 }
101
102 let instance_polys: Vec<_> = instance_values
103 .iter()
104 .map(|poly| {
105 let lagrange_vec = domain.lagrange_from_vec(poly.to_vec());
106 domain.lagrange_to_coeff(lagrange_vec)
107 })
108 .collect();
109
110 let instance_cosets: Vec<_> = instance_polys
111 .iter()
112 .map(|poly| domain.coeff_to_extended(poly.clone()))
113 .collect();
114
115 Ok(InstanceSingle {
116 instance_values,
117 instance_polys,
118 instance_cosets,
119 })
120 })
121 .collect::<Result<Vec<_>, _>>()?;
122
123 struct AdviceSingle<C: CurveAffine> {
124 pub advice_values: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
125 pub advice_polys: Vec<Polynomial<C::Scalar, Coeff>>,
126 pub advice_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
127 pub advice_blinds: Vec<Blind<C::Scalar>>,
128 }
129
130 let advice: Vec<AdviceSingle<C>> = circuits
131 .iter()
132 .zip(instances.iter())
133 .map(|(circuit, instances)| -> Result<AdviceSingle<C>, Error> {
134 struct WitnessCollection<'a, F: Field> {
135 k: u32,
136 pub advice: Vec<Polynomial<Assigned<F>, LagrangeCoeff>>,
137 instances: &'a [&'a [F]],
138 usable_rows: RangeTo<usize>,
139 _marker: std::marker::PhantomData<F>,
140 }
141
142 impl<'a, F: Field> Assignment<F> for WitnessCollection<'a, F> {
143 fn enter_region<NR, N>(&mut self, _: N)
144 where
145 NR: Into<String>,
146 N: FnOnce() -> NR,
147 {
148 }
150
151 fn exit_region(&mut self) {
152 }
154
155 fn enable_selector<A, AR>(
156 &mut self,
157 _: A,
158 _: &Selector,
159 _: usize,
160 ) -> Result<(), Error>
161 where
162 A: FnOnce() -> AR,
163 AR: Into<String>,
164 {
165 Ok(())
168 }
169
170 fn query_instance(
171 &self,
172 column: Column<Instance>,
173 row: usize,
174 ) -> Result<Option<F>, Error> {
175 if !self.usable_rows.contains(&row) {
176 return Err(Error::not_enough_rows_available(self.k));
177 }
178
179 self.instances
180 .get(column.index())
181 .and_then(|column| column.get(row))
182 .map(|v| Some(*v))
183 .ok_or(Error::BoundsFailure)
184 }
185
186 fn assign_advice<V, VR, A, AR>(
187 &mut self,
188 _: A,
189 column: Column<Advice>,
190 row: usize,
191 to: V,
192 ) -> Result<(), Error>
193 where
194 V: FnOnce() -> Result<VR, Error>,
195 VR: Into<Assigned<F>>,
196 A: FnOnce() -> AR,
197 AR: Into<String>,
198 {
199 if !self.usable_rows.contains(&row) {
200 return Err(Error::not_enough_rows_available(self.k));
201 }
202
203 *self
204 .advice
205 .get_mut(column.index())
206 .and_then(|v| v.get_mut(row))
207 .ok_or(Error::BoundsFailure)? = to()?.into();
208
209 Ok(())
210 }
211
212 fn assign_fixed<V, VR, A, AR>(
213 &mut self,
214 _: A,
215 _: Column<Fixed>,
216 _: usize,
217 _: V,
218 ) -> Result<(), Error>
219 where
220 V: FnOnce() -> Result<VR, Error>,
221 VR: Into<Assigned<F>>,
222 A: FnOnce() -> AR,
223 AR: Into<String>,
224 {
225 Ok(())
228 }
229
230 fn copy(
231 &mut self,
232 _: Column<Any>,
233 _: usize,
234 _: Column<Any>,
235 _: usize,
236 ) -> Result<(), Error> {
237 Ok(())
240 }
241
242 fn fill_from_row(
243 &mut self,
244 _: Column<Fixed>,
245 _: usize,
246 _: Option<Assigned<F>>,
247 ) -> Result<(), Error> {
248 Ok(())
249 }
250
251 fn push_namespace<NR, N>(&mut self, _: N)
252 where
253 NR: Into<String>,
254 N: FnOnce() -> NR,
255 {
256 }
258
259 fn pop_namespace(&mut self, _: Option<String>) {
260 }
262 }
263
264 let unusable_rows_start = params.n as usize - (meta.blinding_factors() + 1);
265
266 let mut witness = WitnessCollection {
267 k: params.k,
268 advice: vec![domain.empty_lagrange_assigned(); meta.num_advice_columns],
269 instances,
270 usable_rows: ..unusable_rows_start,
275 _marker: std::marker::PhantomData,
276 };
277
278 ConcreteCircuit::FloorPlanner::synthesize(
280 &mut witness,
281 circuit,
282 config.clone(),
283 meta.constants.clone(),
284 )?;
285
286 let mut advice = batch_invert_assigned(witness.advice);
287
288 for advice in &mut advice {
290 for cell in &mut advice[unusable_rows_start..] {
291 *cell = C::Scalar::random(&mut rng);
292 }
293 }
294
295 let advice_blinds: Vec<_> = advice
297 .iter()
298 .map(|_| Blind(C::Scalar::random(&mut rng)))
299 .collect();
300 let advice_commitments_projective: Vec<_> = advice
301 .iter()
302 .zip(advice_blinds.iter())
303 .map(|(poly, blind)| params.commit_lagrange(poly, *blind))
304 .collect();
305 let mut advice_commitments = vec![C::identity(); advice_commitments_projective.len()];
306 C::Curve::batch_normalize(&advice_commitments_projective, &mut advice_commitments);
307 let advice_commitments = advice_commitments;
308 drop(advice_commitments_projective);
309
310 for commitment in &advice_commitments {
311 transcript.write_point(*commitment)?;
312 }
313
314 let advice_polys: Vec<_> = advice
315 .clone()
316 .into_iter()
317 .map(|poly| domain.lagrange_to_coeff(poly))
318 .collect();
319
320 let advice_cosets: Vec<_> = advice_polys
321 .iter()
322 .map(|poly| domain.coeff_to_extended(poly.clone()))
323 .collect();
324
325 Ok(AdviceSingle {
326 advice_values: advice,
327 advice_polys,
328 advice_cosets,
329 advice_blinds,
330 })
331 })
332 .collect::<Result<Vec<_>, _>>()?;
333
334 let mut value_evaluator = poly::new_evaluator(|| {});
336
337 let fixed_values: Vec<_> = pk
339 .fixed_values
340 .iter()
341 .map(|poly| value_evaluator.register_poly(poly.clone()))
342 .collect();
343
344 let advice_values: Vec<_> = advice
346 .iter()
347 .map(|advice| {
348 advice
349 .advice_values
350 .iter()
351 .map(|poly| value_evaluator.register_poly(poly.clone()))
352 .collect::<Vec<_>>()
353 })
354 .collect();
355
356 let instance_values: Vec<_> = instance
358 .iter()
359 .map(|instance| {
360 instance
361 .instance_values
362 .iter()
363 .map(|poly| value_evaluator.register_poly(poly.clone()))
364 .collect::<Vec<_>>()
365 })
366 .collect();
367
368 let mut coset_evaluator = poly::new_evaluator(|| {});
370
371 let fixed_cosets: Vec<_> = pk
373 .fixed_cosets
374 .iter()
375 .map(|poly| coset_evaluator.register_poly(poly.clone()))
376 .collect();
377
378 let advice_cosets: Vec<_> = advice
380 .iter()
381 .map(|advice| {
382 advice
383 .advice_cosets
384 .iter()
385 .map(|poly| coset_evaluator.register_poly(poly.clone()))
386 .collect::<Vec<_>>()
387 })
388 .collect();
389
390 let instance_cosets: Vec<_> = instance
392 .iter()
393 .map(|instance| {
394 instance
395 .instance_cosets
396 .iter()
397 .map(|poly| coset_evaluator.register_poly(poly.clone()))
398 .collect::<Vec<_>>()
399 })
400 .collect();
401
402 let permutation_cosets: Vec<_> = pk
404 .permutation
405 .cosets
406 .iter()
407 .map(|poly| coset_evaluator.register_poly(poly.clone()))
408 .collect();
409
410 let l0 = coset_evaluator.register_poly(pk.l0.clone());
412 let l_blind = coset_evaluator.register_poly(pk.l_blind.clone());
413 let l_last = coset_evaluator.register_poly(pk.l_last.clone());
414
415 let theta: ChallengeTheta<_> = transcript.squeeze_challenge_scalar();
417
418 let lookups: Vec<Vec<lookup::prover::Permuted<C, _>>> = instance_values
419 .iter()
420 .zip(instance_cosets.iter())
421 .zip(advice_values.iter())
422 .zip(advice_cosets.iter())
423 .map(|(((instance_values, instance_cosets), advice_values), advice_cosets)| -> Result<Vec<_>, Error> {
424 pk.vk
426 .cs
427 .lookups
428 .iter()
429 .map(|lookup| {
430 lookup.commit_permuted(
431 pk,
432 params,
433 domain,
434 &value_evaluator,
435 &mut coset_evaluator,
436 theta,
437 advice_values,
438 &fixed_values,
439 instance_values,
440 advice_cosets,
441 &fixed_cosets,
442 instance_cosets,
443 &mut rng,
444 transcript,
445 )
446 })
447 .collect()
448 })
449 .collect::<Result<Vec<_>, _>>()?;
450
451 let beta: ChallengeBeta<_> = transcript.squeeze_challenge_scalar();
453
454 let gamma: ChallengeGamma<_> = transcript.squeeze_challenge_scalar();
456
457 let permutations: Vec<permutation::prover::Committed<C, _>> = instance
459 .iter()
460 .zip(advice.iter())
461 .map(|(instance, advice)| {
462 pk.vk.cs.permutation.commit(
463 params,
464 pk,
465 &pk.permutation,
466 &advice.advice_values,
467 &pk.fixed_values,
468 &instance.instance_values,
469 beta,
470 gamma,
471 &mut coset_evaluator,
472 &mut rng,
473 transcript,
474 )
475 })
476 .collect::<Result<Vec<_>, _>>()?;
477
478 let lookups: Vec<Vec<lookup::prover::Committed<C, _>>> = lookups
479 .into_iter()
480 .map(|lookups| -> Result<Vec<_>, _> {
481 lookups
483 .into_iter()
484 .map(|lookup| {
485 lookup.commit_product(
486 pk,
487 params,
488 theta,
489 beta,
490 gamma,
491 &mut coset_evaluator,
492 &mut rng,
493 transcript,
494 )
495 })
496 .collect::<Result<Vec<_>, _>>()
497 })
498 .collect::<Result<Vec<_>, _>>()?;
499
500 let vanishing = vanishing::Argument::commit(params, domain, &mut rng, transcript)?;
502
503 let y: ChallengeY<_> = transcript.squeeze_challenge_scalar();
505
506 let (permutations, permutation_expressions): (Vec<_>, Vec<_>) = permutations
508 .into_iter()
509 .zip(advice_cosets.iter())
510 .zip(instance_cosets.iter())
511 .map(|((permutation, advice), instance)| {
512 permutation.construct(
513 pk,
514 &pk.vk.cs.permutation,
515 advice,
516 &fixed_cosets,
517 instance,
518 &permutation_cosets,
519 l0,
520 l_blind,
521 l_last,
522 beta,
523 gamma,
524 )
525 })
526 .unzip();
527
528 let (lookups, lookup_expressions): (Vec<Vec<_>>, Vec<Vec<_>>) = lookups
529 .into_iter()
530 .map(|lookups| {
531 lookups
533 .into_iter()
534 .map(|p| p.construct(theta, beta, gamma, l0, l_blind, l_last))
535 .unzip()
536 })
537 .unzip();
538
539 let expressions = advice_cosets
540 .iter()
541 .zip(instance_cosets.iter())
542 .zip(permutation_expressions.into_iter())
543 .zip(lookup_expressions.into_iter())
544 .flat_map(
545 |(((advice_cosets, instance_cosets), permutation_expressions), lookup_expressions)| {
546 let fixed_cosets = &fixed_cosets;
547 iter::empty()
548 .chain(meta.gates.iter().flat_map(move |gate| {
550 gate.polynomials().iter().map(move |expr| {
551 expr.evaluate(
552 &poly::Ast::ConstantTerm,
553 &|_| panic!("virtual selectors are removed during optimization"),
554 &|_, column_index, rotation| {
555 fixed_cosets[column_index].with_rotation(rotation).into()
556 },
557 &|_, column_index, rotation| {
558 advice_cosets[column_index].with_rotation(rotation).into()
559 },
560 &|_, column_index, rotation| {
561 instance_cosets[column_index].with_rotation(rotation).into()
562 },
563 &|a| -a,
564 &|a, b| a + b,
565 &|a, b| a * b,
566 &|a, scalar| a * scalar,
567 )
568 })
569 }))
570 .chain(permutation_expressions.into_iter())
572 .chain(lookup_expressions.into_iter().flatten())
574 },
575 );
576
577 let vanishing = vanishing.construct(
579 params,
580 domain,
581 coset_evaluator,
582 expressions,
583 y,
584 &mut rng,
585 transcript,
586 )?;
587
588 let x: ChallengeX<_> = transcript.squeeze_challenge_scalar();
589 let xn = x.pow(&[params.n as u64, 0, 0, 0]);
590
591 for instance in instance.iter() {
593 let instance_evals: Vec<_> = meta
595 .instance_queries
596 .iter()
597 .map(|&(column, at)| {
598 eval_polynomial(
599 &instance.instance_polys[column.index()],
600 domain.rotate_omega(*x, at),
601 )
602 })
603 .collect();
604
605 for eval in instance_evals.iter() {
607 transcript.write_scalar(*eval)?;
608 }
609 }
610
611 for advice in advice.iter() {
613 let advice_evals: Vec<_> = meta
615 .advice_queries
616 .iter()
617 .map(|&(column, at)| {
618 eval_polynomial(
619 &advice.advice_polys[column.index()],
620 domain.rotate_omega(*x, at),
621 )
622 })
623 .collect();
624
625 for eval in advice_evals.iter() {
627 transcript.write_scalar(*eval)?;
628 }
629 }
630
631 let fixed_evals: Vec<_> = meta
633 .fixed_queries
634 .iter()
635 .map(|&(column, at)| {
636 eval_polynomial(&pk.fixed_polys[column.index()], domain.rotate_omega(*x, at))
637 })
638 .collect();
639
640 for eval in fixed_evals.iter() {
642 transcript.write_scalar(*eval)?;
643 }
644
645 let vanishing = vanishing.evaluate(x, xn, domain, transcript)?;
646
647 pk.permutation.evaluate(x, transcript)?;
649
650 let permutations: Vec<permutation::prover::Evaluated<C>> = permutations
652 .into_iter()
653 .map(|permutation| -> Result<_, _> { permutation.evaluate(pk, x, transcript) })
654 .collect::<Result<Vec<_>, _>>()?;
655
656 let lookups: Vec<Vec<lookup::prover::Evaluated<C>>> = lookups
658 .into_iter()
659 .map(|lookups| -> Result<Vec<_>, _> {
660 lookups
661 .into_iter()
662 .map(|p| p.evaluate(pk, x, transcript))
663 .collect::<Result<Vec<_>, _>>()
664 })
665 .collect::<Result<Vec<_>, _>>()?;
666
667 let instances = instance
668 .iter()
669 .zip(advice.iter())
670 .zip(permutations.iter())
671 .zip(lookups.iter())
672 .flat_map(|(((instance, advice), permutation), lookups)| {
673 iter::empty()
674 .chain(
675 pk.vk
676 .cs
677 .instance_queries
678 .iter()
679 .map(move |&(column, at)| ProverQuery {
680 point: domain.rotate_omega(*x, at),
681 poly: &instance.instance_polys[column.index()],
682 blind: Blind::default(),
683 }),
684 )
685 .chain(
686 pk.vk
687 .cs
688 .advice_queries
689 .iter()
690 .map(move |&(column, at)| ProverQuery {
691 point: domain.rotate_omega(*x, at),
692 poly: &advice.advice_polys[column.index()],
693 blind: advice.advice_blinds[column.index()],
694 }),
695 )
696 .chain(permutation.open(pk, x))
697 .chain(lookups.iter().flat_map(move |p| p.open(pk, x)).into_iter())
698 })
699 .chain(
700 pk.vk
701 .cs
702 .fixed_queries
703 .iter()
704 .map(|&(column, at)| ProverQuery {
705 point: domain.rotate_omega(*x, at),
706 poly: &pk.fixed_polys[column.index()],
707 blind: Blind::default(),
708 }),
709 )
710 .chain(pk.permutation.open(x))
711 .chain(vanishing.open(x));
713
714 multiopen::create_proof(params, rng, transcript, instances).map_err(|_| Error::Opening)
715}