halo2_proofs/plonk/
prover.rs

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
30/// This creates a proof for the provided `circuit` when given the public
31/// parameters `params` and the proving key [`ProvingKey`] that was
32/// generated previously for the same circuit. The provided `instances`
33/// are zero-padded internally.
34pub 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    // Hash verification key into transcript
55    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    // Selector optimizations cannot be applied here; use the ConstraintSystem
62    // from the verification key.
63    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                    // Do nothing; we don't care about regions in this context.
149                }
150
151                fn exit_region(&mut self) {
152                    // Do nothing; we don't care about regions in this context.
153                }
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                    // We only care about advice columns here
166
167                    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                    // We only care about advice columns here
226
227                    Ok(())
228                }
229
230                fn copy(
231                    &mut self,
232                    _: Column<Any>,
233                    _: usize,
234                    _: Column<Any>,
235                    _: usize,
236                ) -> Result<(), Error> {
237                    // We only care about advice columns here
238
239                    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                    // Do nothing; we don't care about namespaces in this context.
257                }
258
259                fn pop_namespace(&mut self, _: Option<String>) {
260                    // Do nothing; we don't care about namespaces in this context.
261                }
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                // The prover will not be allowed to assign values to advice
271                // cells that exist within inactive rows, which include some
272                // number of blinding factors and an extra row for use in the
273                // permutation argument.
274                usable_rows: ..unusable_rows_start,
275                _marker: std::marker::PhantomData,
276            };
277
278            // Synthesize the circuit to obtain the witness and other information.
279            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            // Add blinding factors to advice columns
289            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            // Compute commitments to advice column polynomials
296            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    // Create polynomial evaluator context for values.
335    let mut value_evaluator = poly::new_evaluator(|| {});
336
337    // Register fixed values with the polynomial evaluator.
338    let fixed_values: Vec<_> = pk
339        .fixed_values
340        .iter()
341        .map(|poly| value_evaluator.register_poly(poly.clone()))
342        .collect();
343
344    // Register advice values with the polynomial evaluator.
345    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    // Register instance values with the polynomial evaluator.
357    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    // Create polynomial evaluator context for cosets.
369    let mut coset_evaluator = poly::new_evaluator(|| {});
370
371    // Register fixed cosets with the polynomial evaluator.
372    let fixed_cosets: Vec<_> = pk
373        .fixed_cosets
374        .iter()
375        .map(|poly| coset_evaluator.register_poly(poly.clone()))
376        .collect();
377
378    // Register advice cosets with the polynomial evaluator.
379    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    // Register instance cosets with the polynomial evaluator.
391    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    // Register permutation cosets with the polynomial evaluator.
403    let permutation_cosets: Vec<_> = pk
404        .permutation
405        .cosets
406        .iter()
407        .map(|poly| coset_evaluator.register_poly(poly.clone()))
408        .collect();
409
410    // Register boundary polynomials used in the lookup and permutation arguments.
411    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    // Sample theta challenge for keeping lookup columns linearly independent
416    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            // Construct and commit to permuted values for each lookup
425            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    // Sample beta challenge
452    let beta: ChallengeBeta<_> = transcript.squeeze_challenge_scalar();
453
454    // Sample gamma challenge
455    let gamma: ChallengeGamma<_> = transcript.squeeze_challenge_scalar();
456
457    // Commit to permutations.
458    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            // Construct and commit to products for each lookup
482            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    // Commit to the vanishing argument's random polynomial for blinding h(x_3)
501    let vanishing = vanishing::Argument::commit(params, domain, &mut rng, transcript)?;
502
503    // Obtain challenge for keeping all separate gates linearly independent
504    let y: ChallengeY<_> = transcript.squeeze_challenge_scalar();
505
506    // Evaluate the h(X) polynomial's constraint system expressions for the permutation constraints.
507    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            // Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any.
532            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                    // Custom constraints
549                    .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                    // Permutation constraints, if any.
571                    .chain(permutation_expressions.into_iter())
572                    // Lookup constraints, if any.
573                    .chain(lookup_expressions.into_iter().flatten())
574            },
575        );
576
577    // Construct the vanishing argument's h(X) commitments
578    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    // Compute and hash instance evals for each circuit instance
592    for instance in instance.iter() {
593        // Evaluate polynomials at omega^i x
594        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        // Hash each instance column evaluation
606        for eval in instance_evals.iter() {
607            transcript.write_scalar(*eval)?;
608        }
609    }
610
611    // Compute and hash advice evals for each circuit instance
612    for advice in advice.iter() {
613        // Evaluate polynomials at omega^i x
614        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        // Hash each advice column evaluation
626        for eval in advice_evals.iter() {
627            transcript.write_scalar(*eval)?;
628        }
629    }
630
631    // Compute and hash fixed evals (shared across all circuit instances)
632    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    // Hash each fixed column evaluation
641    for eval in fixed_evals.iter() {
642        transcript.write_scalar(*eval)?;
643    }
644
645    let vanishing = vanishing.evaluate(x, xn, domain, transcript)?;
646
647    // Evaluate common permutation data
648    pk.permutation.evaluate(x, transcript)?;
649
650    // Evaluate the permutations, if any, at omega^i x.
651    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    // Evaluate the lookups, if any, at omega^i x.
657    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        // We query the h(X) polynomial at x
712        .chain(vanishing.open(x));
713
714    multiopen::create_proof(params, rng, transcript, instances).map_err(|_| Error::Opening)
715}