halo2_axiom/plonk/
prover.rs

1#[cfg(feature = "profile")]
2use ark_std::{end_timer, start_timer};
3use ff::{Field, WithSmallOrderMulGroup};
4use group::Curve;
5use rand_core::RngCore;
6
7use std::hash::Hash;
8use std::marker::PhantomData;
9use std::ops::RangeTo;
10
11#[cfg(feature = "multicore")]
12use crate::multicore::IndexedParallelIterator;
13use crate::multicore::{IntoParallelIterator, ParallelIterator};
14use std::{collections::HashMap, iter};
15
16use super::{
17    circuit::{
18        sealed::{self},
19        Advice, Any, Assignment, Challenge, Circuit, Column, ConstraintSystem, Fixed, FloorPlanner,
20        Instance, Selector,
21    },
22    lookup, permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX,
23    ChallengeY, Error, ProvingKey,
24};
25
26use crate::{
27    arithmetic::{eval_polynomial, CurveAffine},
28    circuit::Value,
29    plonk::Assigned,
30    poly::{
31        commitment::{Blind, CommitmentScheme, Params, Prover},
32        Basis, Coeff, LagrangeCoeff, Polynomial, ProverQuery,
33    },
34};
35use crate::{
36    poly::batch_invert_assigned,
37    transcript::{EncodedChallenge, TranscriptWrite},
38};
39
40/// This creates a proof for the provided `circuit` when given the public
41/// parameters `params` and the proving key [`ProvingKey`] that was
42/// generated previously for the same circuit. The provided `instances`
43/// are zero-padded internally.
44pub fn create_proof<
45    'params,
46    'a,
47    Scheme: CommitmentScheme,
48    P: Prover<'params, Scheme>,
49    E: EncodedChallenge<Scheme::Curve>,
50    R: RngCore + 'a,
51    T: TranscriptWrite<Scheme::Curve, E>,
52    ConcreteCircuit: Circuit<Scheme::Scalar>,
53>(
54    params: &'params Scheme::ParamsProver,
55    pk: &ProvingKey<Scheme::Curve>,
56    circuits: &[ConcreteCircuit],
57    instances: &[&[&'a [Scheme::Scalar]]],
58    mut rng: R,
59    mut transcript: &'a mut T,
60) -> Result<(), Error>
61where
62    Scheme::Scalar: Hash + WithSmallOrderMulGroup<3>,
63    <Scheme as CommitmentScheme>::ParamsProver: Sync,
64{
65    if circuits.len() != instances.len() {
66        return Err(Error::InvalidInstances);
67    }
68
69    for instance in instances.iter() {
70        if instance.len() != pk.vk.cs.num_instance_columns {
71            return Err(Error::InvalidInstances);
72        }
73    }
74
75    // Hash verification key into transcript
76    pk.vk.hash_into(transcript)?;
77
78    let domain = &pk.vk.domain;
79    let mut meta = ConstraintSystem::default();
80    #[cfg(feature = "circuit-params")]
81    let config = ConcreteCircuit::configure_with_params(&mut meta, circuits[0].params());
82    #[cfg(not(feature = "circuit-params"))]
83    let config = ConcreteCircuit::configure(&mut meta);
84
85    // Selector optimizations cannot be applied here; use the ConstraintSystem
86    // from the verification key.
87    let meta = &pk.vk.cs;
88
89    struct InstanceSingle<C: CurveAffine> {
90        pub instance_values: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
91        pub instance_polys: Vec<Polynomial<C::Scalar, Coeff>>,
92    }
93
94    let instance: Vec<InstanceSingle<Scheme::Curve>> = instances
95        .iter()
96        .map(|instance| -> InstanceSingle<Scheme::Curve> {
97            let instance_values = instance
98                .iter()
99                .map(|values| {
100                    let mut poly = domain.empty_lagrange();
101                    assert_eq!(poly.len(), params.n() as usize);
102                    if values.len() > (poly.len() - (meta.blinding_factors() + 1)) {
103                        panic!("Error::InstanceTooLarge");
104                    }
105                    for (poly, value) in poly.iter_mut().zip(values.iter()) {
106                        *poly = *value;
107                    }
108                    poly
109                })
110                .collect::<Vec<_>>();
111
112            let instance_polys: Vec<_> = instance_values
113                .iter()
114                .map(|poly| {
115                    let lagrange_vec = domain.lagrange_from_vec(poly.to_vec());
116                    domain.lagrange_to_coeff(lagrange_vec)
117                })
118                .collect();
119
120            InstanceSingle {
121                instance_values,
122                instance_polys,
123            }
124        })
125        .collect();
126
127    #[derive(Clone)]
128    struct AdviceSingle<C: CurveAffine, B: Basis> {
129        pub advice_polys: Vec<Polynomial<C::Scalar, B>>,
130        pub advice_blinds: Vec<Blind<C::Scalar>>,
131    }
132
133    struct WitnessCollection<'params, 'a, 'b, Scheme, P, C, E, R, T>
134    where
135        Scheme: CommitmentScheme<Curve = C>,
136        P: Prover<'params, Scheme>,
137        C: CurveAffine,
138        E: EncodedChallenge<C>,
139        R: RngCore + 'a,
140        T: TranscriptWrite<C, E>,
141    {
142        params: &'params Scheme::ParamsProver,
143        current_phase: sealed::Phase,
144        advice: Vec<Polynomial<Assigned<C::Scalar>, LagrangeCoeff>>,
145        challenges: &'b mut HashMap<usize, C::Scalar>,
146        instances: &'b [&'a [C::Scalar]],
147        usable_rows: RangeTo<usize>,
148        advice_single: AdviceSingle<C, LagrangeCoeff>,
149        instance_single: &'b InstanceSingle<C>,
150        rng: &'b mut R,
151        transcript: &'b mut &'a mut T,
152        column_indices: [Vec<usize>; 3],
153        challenge_indices: [Vec<usize>; 3],
154        unusable_rows_start: usize,
155        _marker: PhantomData<(P, E)>,
156    }
157
158    impl<'params, 'a, 'b, F, Scheme, P, C, E, R, T> Assignment<F>
159        for WitnessCollection<'params, 'a, 'b, Scheme, P, C, E, R, T>
160    where
161        F: Field,
162        Scheme: CommitmentScheme<Curve = C>,
163        P: Prover<'params, Scheme>,
164        C: CurveAffine<ScalarExt = F>,
165        E: EncodedChallenge<C>,
166        R: RngCore,
167        T: TranscriptWrite<C, E>,
168        <Scheme as CommitmentScheme>::ParamsProver: Sync,
169    {
170        fn enter_region<NR, N>(&mut self, _: N)
171        where
172            NR: Into<String>,
173            N: FnOnce() -> NR,
174        {
175            // Do nothing; we don't care about regions in this context.
176        }
177
178        fn exit_region(&mut self) {
179            // Do nothing; we don't care about regions in this context.
180        }
181
182        fn enable_selector<A, AR>(&mut self, _: A, _: &Selector, _: usize) -> Result<(), Error>
183        where
184            A: FnOnce() -> AR,
185            AR: Into<String>,
186        {
187            // We only care about advice columns here
188
189            Ok(())
190        }
191
192        fn annotate_column<A, AR>(&mut self, _annotation: A, _column: Column<Any>)
193        where
194            A: FnOnce() -> AR,
195            AR: Into<String>,
196        {
197            // Do nothing
198        }
199
200        fn query_instance(&self, column: Column<Instance>, row: usize) -> Result<Value<F>, Error> {
201            if !self.usable_rows.contains(&row) {
202                return Err(Error::not_enough_rows_available(self.params.k()));
203            }
204
205            self.instances
206                .get(column.index())
207                .and_then(|column| column.get(row))
208                .map(|v| Value::known(*v))
209                .ok_or(Error::BoundsFailure)
210        }
211
212        fn assign_advice<'v>(
213            //<V, VR, A, AR>(
214            &mut self,
215            //_: A,
216            column: Column<Advice>,
217            row: usize,
218            to: Value<Assigned<F>>,
219        ) -> Value<&'v Assigned<F>> {
220            // debug_assert_eq!(self.current_phase, column.column_type().phase);
221
222            debug_assert!(
223                self.usable_rows.contains(&row),
224                "{:?}",
225                Error::not_enough_rows_available(self.params.k())
226            );
227
228            let advice_get_mut = self
229                .advice
230                .get_mut(column.index())
231                .expect("Not enough advice columns")
232                .get_mut(row)
233                .expect("Not enough rows");
234            // We can get another 3-4% decrease in witness gen time by using the following unsafe code, but this skips all array bound checks so we should use it only if the performance gain is really necessary:
235            /*
236            let advice_get_mut = unsafe {
237                self.advice
238                    .get_unchecked_mut(column.index())
239                    .get_unchecked_mut(row)
240            };
241            */
242            *advice_get_mut = to
243                .assign()
244                .expect("No Value::unknown() in advice column allowed during create_proof");
245            let immutable_raw_ptr = advice_get_mut as *const Assigned<F>;
246            Value::known(unsafe { &*immutable_raw_ptr })
247        }
248
249        fn assign_fixed(&mut self, _: Column<Fixed>, _: usize, _: Assigned<F>) {
250            // We only care about advice columns here
251        }
252
253        fn copy(&mut self, _: Column<Any>, _: usize, _: Column<Any>, _: usize) {
254            // We only care about advice columns here
255        }
256
257        fn fill_from_row(
258            &mut self,
259            _: Column<Fixed>,
260            _: usize,
261            _: Value<Assigned<F>>,
262        ) -> Result<(), Error> {
263            Ok(())
264        }
265
266        fn get_challenge(&self, challenge: Challenge) -> Value<F> {
267            self.challenges
268                .get(&challenge.index())
269                .cloned()
270                .map(Value::known)
271                .unwrap_or_else(Value::unknown)
272        }
273
274        fn push_namespace<NR, N>(&mut self, _: N)
275        where
276            NR: Into<String>,
277            N: FnOnce() -> NR,
278        {
279            // Do nothing; we don't care about namespaces in this context.
280        }
281
282        fn pop_namespace(&mut self, _: Option<String>) {
283            // Do nothing; we don't care about namespaces in this context.
284        }
285
286        fn next_phase(&mut self) {
287            let phase = self.current_phase.to_u8() as usize;
288            #[cfg(feature = "profile")]
289            let start1 = start_timer!(|| format!("Phase {phase} inversion and MSM commitment"));
290            if phase == 0 {
291                // Absorb instances into transcript.
292                // Do this here and not earlier in case we want to be able to mutate
293                // the instances during synthesize in FirstPhase in the future
294                if !P::QUERY_INSTANCE {
295                    for values in self.instances.iter() {
296                        for value in values.iter() {
297                            self.transcript
298                                .common_scalar(*value)
299                                .expect("Absorbing instance value to transcript failed");
300                        }
301                    }
302                } else {
303                    let instance_commitments_projective: Vec<_> =
304                        (&self.instance_single.instance_values)
305                            .into_par_iter()
306                            .map(|poly| self.params.commit_lagrange(poly, Blind::default()))
307                            .collect();
308                    let mut instance_commitments =
309                        vec![C::identity(); instance_commitments_projective.len()];
310                    C::CurveExt::batch_normalize(
311                        &instance_commitments_projective,
312                        &mut instance_commitments,
313                    );
314                    let instance_commitments = instance_commitments;
315                    drop(instance_commitments_projective);
316
317                    for commitment in &instance_commitments {
318                        self.transcript
319                            .common_point(*commitment)
320                            .expect("Absorbing instance commitment to transcript failed");
321                    }
322                }
323            }
324            // Commit the advice columns in the current phase
325            let mut advice_values = batch_invert_assigned(
326                self.column_indices
327                    .get(phase)
328                    .expect("The API only supports 3 phases right now")
329                    .iter()
330                    .map(|column_index| &self.advice[*column_index][..])
331                    .collect(),
332            );
333            // Add blinding factors to advice columns
334            for advice_values in &mut advice_values {
335                for cell in &mut advice_values[self.unusable_rows_start..] {
336                    *cell = F::random(&mut self.rng);
337                }
338            }
339            // Compute commitments to advice column polynomials
340            let blinds: Vec<_> = advice_values
341                .iter()
342                .map(|_| Blind(F::random(&mut self.rng)))
343                .collect();
344            let advice_commitments_projective: Vec<_> = (&advice_values)
345                .into_par_iter()
346                .zip((&blinds).into_par_iter())
347                .map(|(poly, blind)| self.params.commit_lagrange(poly, *blind))
348                .collect();
349            let mut advice_commitments = vec![C::identity(); advice_commitments_projective.len()];
350            C::CurveExt::batch_normalize(&advice_commitments_projective, &mut advice_commitments);
351            let advice_commitments = advice_commitments;
352            drop(advice_commitments_projective);
353
354            for commitment in &advice_commitments {
355                self.transcript
356                    .write_point(*commitment)
357                    .expect("Absorbing advice commitment to transcript failed");
358            }
359            for ((column_index, advice_poly), blind) in self.column_indices[phase]
360                .iter()
361                .zip(advice_values)
362                .zip(blinds)
363            {
364                self.advice_single.advice_polys[*column_index] = advice_poly;
365                self.advice_single.advice_blinds[*column_index] = blind;
366            }
367            for challenge_index in self.challenge_indices[phase].iter() {
368                let existing = self.challenges.insert(
369                    *challenge_index,
370                    *self.transcript.squeeze_challenge_scalar::<()>(),
371                );
372                assert!(existing.is_none());
373            }
374            self.current_phase = self.current_phase.next();
375            #[cfg(feature = "profile")]
376            end_timer!(start1);
377        }
378    }
379
380    let mut column_indices = [(); 3].map(|_| vec![]);
381    for (index, phase) in meta.advice_column_phase.iter().enumerate() {
382        column_indices[phase.to_u8() as usize].push(index);
383    }
384    let mut challenge_indices = [(); 3].map(|_| vec![]);
385    for (index, phase) in meta.challenge_phase.iter().enumerate() {
386        challenge_indices[phase.to_u8() as usize].push(index);
387    }
388
389    #[cfg(feature = "profile")]
390    let phase1_time = start_timer!(|| "Phase 1: Witness assignment and MSM commitments");
391    let (advice, challenges) = {
392        let mut advice = Vec::with_capacity(instances.len());
393        let mut challenges = HashMap::<usize, Scheme::Scalar>::with_capacity(meta.num_challenges);
394
395        let unusable_rows_start = params.n() as usize - (meta.blinding_factors() + 1);
396        let phases = pk.vk.cs.phases().collect::<Vec<_>>();
397        let num_phases = phases.len();
398        // WARNING: this will currently not work if `circuits` has more than 1 circuit
399        // because the original API squeezes the challenges for a phase after running all circuits
400        // once in that phase.
401        if num_phases > 1 {
402            assert_eq!(
403                circuits.len(),
404                1,
405                "New challenge API doesn't work with multiple circuits yet"
406            );
407        }
408        for ((circuit, instances), instance_single) in
409            circuits.iter().zip(instances).zip(instance.iter())
410        {
411            let mut witness: WitnessCollection<Scheme, P, _, E, _, _> = WitnessCollection {
412                params,
413                current_phase: phases[0],
414                advice: vec![domain.empty_lagrange_assigned(); meta.num_advice_columns],
415                instances,
416                challenges: &mut challenges,
417                // The prover will not be allowed to assign values to advice
418                // cells that exist within inactive rows, which include some
419                // number of blinding factors and an extra row for use in the
420                // permutation argument.
421                usable_rows: ..unusable_rows_start,
422                advice_single: AdviceSingle::<Scheme::Curve, LagrangeCoeff> {
423                    advice_polys: vec![domain.empty_lagrange(); meta.num_advice_columns],
424                    advice_blinds: vec![Blind::default(); meta.num_advice_columns],
425                },
426                instance_single,
427                rng: &mut rng,
428                transcript: &mut transcript,
429                column_indices: column_indices.clone(),
430                challenge_indices: challenge_indices.clone(),
431                unusable_rows_start,
432                _marker: PhantomData,
433            };
434
435            // while loop is for compatibility with circuits that do not use the new `next_phase` API to manage phases
436            // If the circuit uses the new API, then the while loop will only execute once
437            while witness.current_phase.to_u8() < num_phases as u8 {
438                #[cfg(feature = "profile")]
439                let syn_time = start_timer!(|| format!(
440                    "Synthesize time starting from phase {} (synthesize may cross multiple phases)",
441                    witness.current_phase.to_u8()
442                ));
443                // Synthesize the circuit to obtain the witness and other information.
444                ConcreteCircuit::FloorPlanner::synthesize(
445                    &mut witness,
446                    circuit,
447                    config.clone(),
448                    meta.constants.clone(),
449                )
450                .unwrap();
451                #[cfg(feature = "profile")]
452                end_timer!(syn_time);
453                if witness.current_phase.to_u8() < num_phases as u8 {
454                    witness.next_phase();
455                }
456            }
457            advice.push(witness.advice_single);
458        }
459
460        assert_eq!(challenges.len(), meta.num_challenges);
461        let challenges = (0..meta.num_challenges)
462            .map(|index| challenges.remove(&index).unwrap())
463            .collect::<Vec<_>>();
464
465        (advice, challenges)
466    };
467    #[cfg(feature = "profile")]
468    end_timer!(phase1_time);
469
470    #[cfg(feature = "profile")]
471    let phase2_time = start_timer!(|| "Phase 2: Lookup commit permuted");
472    // Sample theta challenge for keeping lookup columns linearly independent
473    let theta: ChallengeTheta<_> = transcript.squeeze_challenge_scalar();
474
475    let lookups: Vec<Vec<lookup::prover::Permuted<Scheme::Curve>>> = instance
476        .iter()
477        .zip(advice.iter())
478        .map(|(instance, advice)| -> Vec<_> {
479            // Construct and commit to permuted values for each lookup
480            pk.vk
481                .cs
482                .lookups
483                .iter()
484                .map(|lookup| {
485                    lookup
486                        .commit_permuted(
487                            pk,
488                            params,
489                            domain,
490                            theta,
491                            &advice.advice_polys,
492                            &pk.fixed_values,
493                            &instance.instance_values,
494                            &challenges,
495                            &mut rng,
496                            transcript,
497                        )
498                        .unwrap()
499                })
500                .collect()
501        })
502        .collect();
503    #[cfg(feature = "profile")]
504    end_timer!(phase2_time);
505
506    #[cfg(feature = "profile")]
507    let phase3a_time = start_timer!(|| "Phase 3a: Commit to permutations");
508
509    // Sample beta challenge
510    let beta: ChallengeBeta<_> = transcript.squeeze_challenge_scalar();
511
512    // Sample gamma challenge
513    let gamma: ChallengeGamma<_> = transcript.squeeze_challenge_scalar();
514
515    // Commit to permutations.
516    let permutations: Vec<permutation::prover::Committed<Scheme::Curve>> = instance
517        .iter()
518        .zip(advice.iter())
519        .map(|(instance, advice)| {
520            pk.vk
521                .cs
522                .permutation
523                .commit(
524                    params,
525                    pk,
526                    &pk.permutation,
527                    &advice.advice_polys,
528                    &pk.fixed_values,
529                    &instance.instance_values,
530                    beta,
531                    gamma,
532                    &mut rng,
533                    transcript,
534                )
535                .unwrap()
536        })
537        .collect::<Vec<_>>();
538    #[cfg(feature = "profile")]
539    end_timer!(phase3a_time);
540
541    #[cfg(feature = "profile")]
542    let phase3b_time = start_timer!(|| "Phase 3b: Lookup commit product");
543    let lookups: Vec<Vec<lookup::prover::Committed<Scheme::Curve>>> = lookups
544        .into_iter()
545        .map(|lookups| -> Vec<_> {
546            // Construct and commit to products for each lookup
547            lookups
548                .into_iter()
549                .map(|lookup| {
550                    lookup
551                        .commit_product(pk, params, beta, gamma, &mut rng, transcript)
552                        .unwrap()
553                })
554                .collect()
555        })
556        .collect();
557    #[cfg(feature = "profile")]
558    end_timer!(phase3b_time);
559
560    #[cfg(feature = "profile")]
561    let vanishing_time = start_timer!(|| "Commit to vanishing argument's random poly");
562    // Commit to the vanishing argument's random polynomial for blinding h(x_3)
563    let vanishing = vanishing::Argument::commit(params, domain, &mut rng, transcript).unwrap();
564
565    // Obtain challenge for keeping all separate gates linearly independent
566    let y: ChallengeY<_> = transcript.squeeze_challenge_scalar();
567
568    #[cfg(feature = "profile")]
569    end_timer!(vanishing_time);
570    #[cfg(feature = "profile")]
571    let fft_time = start_timer!(|| "Calculate advice polys (fft)");
572
573    // Calculate the advice polys
574    let advice: Vec<AdviceSingle<Scheme::Curve, Coeff>> = advice
575        .into_iter()
576        .map(
577            |AdviceSingle {
578                 advice_polys,
579                 advice_blinds,
580             }| {
581                AdviceSingle {
582                    advice_polys: advice_polys
583                        .into_iter()
584                        .map(|poly| domain.lagrange_to_coeff(poly))
585                        .collect::<Vec<_>>(),
586                    advice_blinds,
587                }
588            },
589        )
590        .collect();
591    #[cfg(feature = "profile")]
592    end_timer!(fft_time);
593
594    #[cfg(feature = "profile")]
595    let phase4_time = start_timer!(|| "Phase 4: Evaluate h(X)");
596    // Evaluate the h(X) polynomial
597    let h_poly = pk.ev.evaluate_h(
598        pk,
599        &advice
600            .iter()
601            .map(|a| a.advice_polys.as_slice())
602            .collect::<Vec<_>>(),
603        &instance
604            .iter()
605            .map(|i| i.instance_polys.as_slice())
606            .collect::<Vec<_>>(),
607        &challenges,
608        *y,
609        *beta,
610        *gamma,
611        *theta,
612        &lookups,
613        &permutations,
614    );
615    #[cfg(feature = "profile")]
616    end_timer!(phase4_time);
617
618    #[cfg(feature = "profile")]
619    let timer = start_timer!(|| "Commit to vanishing argument's h(X) commitments");
620    // Construct the vanishing argument's h(X) commitments
621    let vanishing = vanishing.construct(params, domain, h_poly, &mut rng, transcript)?;
622    #[cfg(feature = "profile")]
623    end_timer!(timer);
624    #[cfg(feature = "profile")]
625    let eval_time = start_timer!(|| "Commit to vanishing argument's h(X) commitments");
626
627    let x: ChallengeX<_> = transcript.squeeze_challenge_scalar();
628    let xn = x.pow([params.n()]);
629
630    if P::QUERY_INSTANCE {
631        // Compute and hash instance evals for each circuit instance
632        for instance in instance.iter() {
633            // Evaluate polynomials at omega^i x
634            let instance_evals: Vec<_> = meta
635                .instance_queries
636                .iter()
637                .map(|&(column, at)| {
638                    eval_polynomial(
639                        &instance.instance_polys[column.index()],
640                        domain.rotate_omega(*x, at),
641                    )
642                })
643                .collect();
644
645            // Hash each instance column evaluation
646            for eval in instance_evals.iter() {
647                transcript.write_scalar(*eval)?;
648            }
649        }
650    }
651
652    // Compute and hash advice evals for each circuit instance
653    for advice in advice.iter() {
654        // Evaluate polynomials at omega^i x
655        let advice_evals: Vec<_> = meta
656            .advice_queries
657            .iter()
658            .map(|&(column, at)| {
659                eval_polynomial(
660                    &advice.advice_polys[column.index()],
661                    domain.rotate_omega(*x, at),
662                )
663            })
664            .collect();
665
666        // Hash each advice column evaluation
667        for eval in advice_evals.iter() {
668            transcript.write_scalar(*eval)?;
669        }
670    }
671
672    // Compute and hash fixed evals (shared across all circuit instances)
673    let fixed_evals: Vec<_> = meta
674        .fixed_queries
675        .iter()
676        .map(|&(column, at)| {
677            eval_polynomial(&pk.fixed_polys[column.index()], domain.rotate_omega(*x, at))
678        })
679        .collect();
680
681    // Hash each fixed column evaluation
682    for eval in fixed_evals.iter() {
683        transcript.write_scalar(*eval)?;
684    }
685
686    let vanishing = vanishing.evaluate(x, xn, domain, transcript)?;
687
688    // Evaluate common permutation data
689    pk.permutation.evaluate(x, transcript)?;
690
691    // Evaluate the permutations, if any, at omega^i x.
692    let permutations: Vec<permutation::prover::Evaluated<Scheme::Curve>> = permutations
693        .into_iter()
694        .map(|permutation| permutation.construct().evaluate(pk, x, transcript).unwrap())
695        .collect();
696
697    // Evaluate the lookups, if any, at omega^i x.
698    let lookups: Vec<Vec<lookup::prover::Evaluated<Scheme::Curve>>> = lookups
699        .into_iter()
700        .map(|lookups| -> Vec<_> {
701            lookups
702                .into_iter()
703                .map(|p| p.evaluate(pk, x, transcript).unwrap())
704                .collect()
705        })
706        .collect();
707    #[cfg(feature = "profile")]
708    end_timer!(eval_time);
709
710    let instances = instance
711        .iter()
712        .zip(advice.iter())
713        .zip(permutations.iter())
714        .zip(lookups.iter())
715        .flat_map(|(((instance, advice), permutation), lookups)| {
716            iter::empty()
717                .chain(
718                    P::QUERY_INSTANCE
719                        .then_some(pk.vk.cs.instance_queries.iter().map(move |&(column, at)| {
720                            ProverQuery {
721                                point: domain.rotate_omega(*x, at),
722                                poly: &instance.instance_polys[column.index()],
723                                blind: Blind::default(),
724                            }
725                        }))
726                        .into_iter()
727                        .flatten(),
728                )
729                .chain(
730                    pk.vk
731                        .cs
732                        .advice_queries
733                        .iter()
734                        .map(move |&(column, at)| ProverQuery {
735                            point: domain.rotate_omega(*x, at),
736                            poly: &advice.advice_polys[column.index()],
737                            blind: advice.advice_blinds[column.index()],
738                        }),
739                )
740                .chain(permutation.open(pk, x))
741                .chain(lookups.iter().flat_map(move |p| p.open(pk, x)))
742        })
743        .chain(
744            pk.vk
745                .cs
746                .fixed_queries
747                .iter()
748                .map(|&(column, at)| ProverQuery {
749                    point: domain.rotate_omega(*x, at),
750                    poly: &pk.fixed_polys[column.index()],
751                    blind: Blind::default(),
752                }),
753        )
754        .chain(pk.permutation.open(x))
755        // We query the h(X) polynomial at x
756        .chain(vanishing.open(x));
757
758    #[cfg(feature = "profile")]
759    let multiopen_time = start_timer!(|| "Phase 5: multiopen");
760    let prover = P::new(params);
761    #[allow(clippy::let_and_return)]
762    let multiopen_res = prover
763        .create_proof(&mut rng, transcript, instances)
764        .map_err(|_| Error::ConstraintSystemFailure);
765    #[cfg(feature = "profile")]
766    end_timer!(multiopen_time);
767    multiopen_res
768}
769
770#[test]
771fn test_create_proof() {
772    use crate::{
773        circuit::SimpleFloorPlanner,
774        plonk::{keygen_pk, keygen_vk},
775        poly::kzg::{
776            commitment::{KZGCommitmentScheme, ParamsKZG},
777            multiopen::ProverSHPLONK,
778        },
779        transcript::{Blake2bWrite, Challenge255, TranscriptWriterBuffer},
780    };
781    use halo2curves::bn256::Bn256;
782    use rand_core::OsRng;
783
784    #[derive(Clone, Copy)]
785    struct MyCircuit;
786
787    impl<F: Field> Circuit<F> for MyCircuit {
788        type Config = ();
789        type FloorPlanner = SimpleFloorPlanner;
790        #[cfg(feature = "circuit-params")]
791        type Params = ();
792
793        fn without_witnesses(&self) -> Self {
794            *self
795        }
796
797        fn configure(_meta: &mut ConstraintSystem<F>) -> Self::Config {}
798
799        fn synthesize(
800            &self,
801            _config: Self::Config,
802            _layouter: impl crate::circuit::Layouter<F>,
803        ) -> Result<(), Error> {
804            Ok(())
805        }
806    }
807
808    let params: ParamsKZG<Bn256> = ParamsKZG::setup(3, OsRng);
809    let vk = keygen_vk(&params, &MyCircuit).expect("keygen_vk should not fail");
810    let pk = keygen_pk(&params, vk, &MyCircuit).expect("keygen_pk should not fail");
811    let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]);
812
813    // Create proof with wrong number of instances
814    let proof = create_proof::<KZGCommitmentScheme<_>, ProverSHPLONK<_>, _, _, _, _>(
815        &params,
816        &pk,
817        &[MyCircuit, MyCircuit],
818        &[],
819        OsRng,
820        &mut transcript,
821    );
822    assert!(matches!(proof.unwrap_err(), Error::InvalidInstances));
823
824    // Create proof with correct number of instances
825    create_proof::<KZGCommitmentScheme<_>, ProverSHPLONK<_>, _, _, _, _>(
826        &params,
827        &pk,
828        &[MyCircuit, MyCircuit],
829        &[&[], &[]],
830        OsRng,
831        &mut transcript,
832    )
833    .expect("proof generation should not fail");
834}