diff --git a/halo2_backend/src/plonk/prover.rs b/halo2_backend/src/plonk/prover.rs index c5a735b5e..0285bd1dd 100644 --- a/halo2_backend/src/plonk/prover.rs +++ b/halo2_backend/src/plonk/prover.rs @@ -507,99 +507,282 @@ impl< where Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, { - let params = self.params; let cs = &self.pk.vk.cs; - let pk = self.pk; - let domain = &self.pk.vk.domain; - - let mut rng = self.rng; - - let instances = std::mem::take(&mut self.instances); - let advices = std::mem::take(&mut self.advices); - let mut challenges = self.challenges; - - assert_eq!(challenges.len(), cs.num_challenges); + assert_eq!(self.challenges.len(), cs.num_challenges); let challenges = (0..cs.num_challenges) - .map(|index| challenges.remove(&index).unwrap()) + .map(|index| self.challenges.remove(&index).unwrap()) .collect::>(); // 1. Generate committed ( added to transcript ) lookup polys --------------------------------------- // Sample theta challenge for keeping lookup columns linearly independent // [TRANSCRIPT-5] - let theta: ChallengeTheta<_> = self.transcript.squeeze_challenge_scalar(); // 2. Get permuted lookup polys // [TRANSCRIPT-6] + let permuted_lookups = self.gen_permuted_lookups(theta, &challenges)?; + + // Sample beta challenge + // [TRANSCRIPT-7] + let beta: ChallengeBeta<_> = self.transcript.squeeze_challenge_scalar(); + + // Sample gamma challenge + // [TRANSCRIPT-8] + let gamma: ChallengeGamma<_> = self.transcript.squeeze_challenge_scalar(); + + // 2. Generate committed permutation polys ----------------------------------------- + // [TRANSCRIPT-9] + let permutations_committed = self.gen_committed_permutation_polys(beta, gamma)?; + + // 3. Generate committed lookup polys ---------------------------------------------------------- + // [TRANSCRIPT-10] + let lookups_committed = self.gen_committed_lookups_polys(permuted_lookups, beta, gamma)?; + + // 4. Generate committed shuffle polys ------------------------------------------------------- + // [TRANSCRIPT-11] + let shuffles_committed = self.gen_committed_shuffles(theta, gamma, &challenges)?; + + // 5. Commit to the vanishing argument's random polynomial for blinding h(x_3) ------------------- + // [TRANSCRIPT-12] + let vanishing_committed = self.gen_committed_vanishing()?; + + // 6. Generate the advice polys ------------------------------------------------------------------ + let advice = self.gen_advice_polys(); + + // 7. Evaluate the h(X) polynomial ----------------------------------------------------------- + + // Obtain challenge for keeping all separate gates linearly independent + // [TRANSCRIPT-13] + let y: ChallengeY<_> = self.transcript.squeeze_challenge_scalar(); + + let h_poly = self.pk.ev.evaluate_h( + self.pk, + &advice + .iter() + .map(|a| a.advice_polys.as_slice()) + .collect::>(), + &self + .instances + .iter() + .map(|i| i.instance_polys.as_slice()) + .collect::>(), + &challenges, + *y, + *beta, + *gamma, + *theta, + &lookups_committed, + &shuffles_committed, + &permutations_committed, + ); + + // 8. Construct the vanishing argument's h(X) commitments -------------------------------------- + // [TRANSCRIPT-14] + let vanishing = self.gen_constructed_vanishing(vanishing_committed, h_poly)?; + + // 9. Compute x -------------------------------------------------------------------------------- + // [TRANSCRIPT-15] + let x: ChallengeX<_> = self.transcript.squeeze_challenge_scalar(); + + let x_pow_n = x.pow([self.params.n()]); + + // [TRANSCRIPT-16] + self.write_instance_evals(x)?; + + // 10. Compute and hash advice evals for the circuit instance ------------------------------------ + // [TRANSCRIPT-17] + self.write_advice_evals(x, &advice)?; + + // 11. Compute and hash fixed evals ----------------------------------------------------------- + + // Hash each fixed column evaluation + // [TRANSCRIPT-18] + self.write_fixed_evals(x)?; + + // [TRANSCRIPT-19] + let vanishing = vanishing.evaluate(x, x_pow_n, &self.pk.vk.domain, self.transcript)?; + + // 12. Evaluate permutation, lookups and shuffles at x ----------------------------------- + + // Evaluate common permutation data + // [TRANSCRIPT-20] + self.pk.permutation.evaluate(x, self.transcript)?; + + // Evaluate the permutations, if any, at omega^i x. + // [TRANSCRIPT-21] + let permutations_evaluated = self.evaluate_permutations(x, permutations_committed)?; + + // Evaluate the lookups, if any, at omega^i x. + // [TRANSCRIPT-22] + let lookups_evaluated = self.evaluate_lookups(x, lookups_committed)?; + + // Evaluate the shuffles, if any, at omega^i x. + // [TRANSCRIPT-23] + let shuffles_evaluated = self.evaluate_shuffles(x, shuffles_committed)?; + + // 13. Generate all queries ([`ProverQuery`]) that needs to be sent to prover -------------------- + let instances = std::mem::take(&mut self.instances); + let queries = instances + // group the instance, advice, permutation, lookups and shuffles + .iter() + .zip(advice.iter()) + .zip(permutations_evaluated.iter()) + .zip(lookups_evaluated.iter()) + .zip(shuffles_evaluated.iter()) + .flat_map(|((((instance, advice), permutation), lookups), shuffles)| { + // Build a (an iterator) over a set of ProverQueries for each instance, advice, permutatiom, lookup and shuffle + iter::empty() + // Instances + .chain( + P::QUERY_INSTANCE + .then_some(self.pk.vk.cs.instance_queries.iter().map( + move |&(column, at)| ProverQuery { + point: self.pk.vk.domain.rotate_omega(*x, at), + poly: &instance.instance_polys[column.index], + blind: Blind::default(), + }, + )) + .into_iter() + .flatten(), + ) + // Advices + .chain( + self.pk + .vk + .cs + .advice_queries + .iter() + .map(move |&(column, at)| ProverQuery { + point: self.pk.vk.domain.rotate_omega(*x, at), + poly: &advice.advice_polys[column.index], + blind: advice.advice_blinds[column.index], + }), + ) + // Permutations + .chain(permutation.open(self.pk, x)) + // Lookups + .chain(lookups.iter().flat_map(move |p| p.open(self.pk, x))) + // Shuffles + .chain(shuffles.iter().flat_map(move |p| p.open(self.pk, x))) + }) + // Queries to fixed columns + .chain( + self.pk + .vk + .cs + .fixed_queries + .iter() + .map(|&(column, at)| ProverQuery { + point: self.pk.vk.domain.rotate_omega(*x, at), + poly: &self.pk.fixed_polys[column.index], + blind: Blind::default(), + }), + ) + // Copy constraints + .chain(self.pk.permutation.open(x)) + // We query the h(X) polynomial at x + .chain(vanishing.open(x)); + + // 14. Send the queries to the [`Prover`] ------------------------------------------------ + let prover = P::new(self.params); + prover + .create_proof_with_engine(&self.engine.msm_backend, self.rng, self.transcript, queries) + .map_err(|_| Error::ConstraintSystemFailure)?; + + Ok(()) + } + + fn gen_permuted_lookups( + &mut self, + theta: ChallengeTheta, + challenges: &[::Scalar], + ) -> Result>>, Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { let mut lookups_fn = |instance: &InstanceSingle, advice: &AdviceSingle| -> Result>, Error> { - cs.lookups + self.pk + .vk + .cs + .lookups .iter() .map(|lookup| { lookup_commit_permuted( &self.engine, lookup, - pk, - params, - domain, + self.pk, + self.params, + &self.pk.vk.domain, theta, &advice.advice_polys, - &pk.fixed_values, + &self.pk.fixed_values, &instance.instance_values, - &challenges, - &mut rng, + challenges, + &mut self.rng, self.transcript, ) }) .collect::, _>>() }; - let permuted_lookups: Vec>> = instances + let permuted_lookups: Vec>> = self + .instances .iter() - .zip(advices.iter()) + .zip(self.advices.iter()) .map(|(instance, advice)| -> Result, Error> { // Construct and commit to permuted values for each lookup lookups_fn(instance, advice) }) .collect::, _>>()?; - // Sample beta challenge - // [TRANSCRIPT-7] - let beta: ChallengeBeta<_> = self.transcript.squeeze_challenge_scalar(); - - // Sample gamma challenge - // [TRANSCRIPT-8] - let gamma: ChallengeGamma<_> = self.transcript.squeeze_challenge_scalar(); + Ok(permuted_lookups) + } - // 2. Generate committed permutation polys ----------------------------------------- - // [TRANSCRIPT-9] - let permutations_committed: Vec> = instances + fn gen_committed_permutation_polys( + &mut self, + beta: ChallengeBeta, + gamma: ChallengeGamma, + ) -> Result>, Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { + let permutations_committed = self + .instances .iter() - .zip(advices.iter()) + .zip(self.advices.iter()) .map(|(instance, advice)| { permutation_commit( &self.engine, - &cs.permutation, - params, - pk, - &pk.permutation, + &self.pk.vk.cs.permutation, + self.params, + self.pk, + &self.pk.permutation, &advice.advice_polys, - &pk.fixed_values, + &self.pk.fixed_values, &instance.instance_values, beta, gamma, - &mut rng, + &mut self.rng, self.transcript, ) }) .collect::, _>>()?; + Ok(permutations_committed) + } - // 3. Generate committed lookup polys ---------------------------------------------------------- - - // [TRANSCRIPT-10] + fn gen_committed_lookups_polys( + &mut self, + permuted_lookups: Vec>>, + beta: ChallengeBeta, + gamma: ChallengeGamma, + ) -> Result>>, Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { let lookups_committed: Vec>> = permuted_lookups .into_iter() @@ -610,62 +793,82 @@ impl< .map(|lookup| { lookup.commit_product( &self.engine, - pk, - params, + self.pk, + self.params, beta, gamma, - &mut rng, + &mut self.rng, self.transcript, ) }) .collect::, _>>() }) .collect::, _>>()?; + Ok(lookups_committed) + } - // 4. Generate committed shuffle polys ------------------------------------------------------- - - // [TRANSCRIPT-11] - let shuffles_committed: Vec>> = instances + fn gen_committed_shuffles( + &mut self, + theta: ChallengeTheta, + gamma: ChallengeGamma, + challenges: &[::Scalar], + ) -> Result>>, Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { + let shuffles_committed: Vec>> = self + .instances .iter() - .zip(advices.iter()) + .zip(self.advices.iter()) .map(|(instance, advice)| -> Result, _> { // Compress expressions for each shuffle - cs.shuffles + self.pk + .vk + .cs + .shuffles .iter() .map(|shuffle| { shuffle_commit_product( &self.engine, shuffle, - pk, - params, - domain, + self.pk, + self.params, + &self.pk.vk.domain, theta, gamma, &advice.advice_polys, - &pk.fixed_values, + &self.pk.fixed_values, &instance.instance_values, - &challenges, - &mut rng, + challenges, + &mut self.rng, self.transcript, ) }) .collect::, _>>() }) .collect::, _>>()?; + Ok(shuffles_committed) + } - // 5. Commit to the vanishing argument's random polynomial for blinding h(x_3) ------------------- - // [TRANSCRIPT-12] - let vanishing = vanishing::Argument::commit( + fn gen_committed_vanishing( + &mut self, + ) -> Result, Error> { + let committed_vanishing = vanishing::Argument::commit( &self.engine.msm_backend, - params, - domain, - &mut rng, + self.params, + &self.pk.vk.domain, + &mut self.rng, self.transcript, )?; + Ok(committed_vanishing) + } - // 6. Generate the advice polys ------------------------------------------------------------------ - - let advice: Vec> = advices + fn gen_advice_polys(&mut self) -> Vec> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { + let advices = std::mem::take(&mut self.advices); + advices .into_iter() .map( |AdviceSingle { @@ -675,69 +878,55 @@ impl< AdviceSingle { advice_polys: advice_polys .into_iter() - .map(|poly| domain.lagrange_to_coeff(poly)) + .map(|poly| self.pk.vk.domain.lagrange_to_coeff(poly)) .collect::>(), advice_blinds, } }, ) - .collect(); - - // 7. Evaluate the h(X) polynomial ----------------------------------------------------------- - - // Obtain challenge for keeping all separate gates linearly independent - // [TRANSCRIPT-13] - let y: ChallengeY<_> = self.transcript.squeeze_challenge_scalar(); - - let h_poly = pk.ev.evaluate_h( - pk, - &advice - .iter() - .map(|a| a.advice_polys.as_slice()) - .collect::>(), - &instances - .iter() - .map(|i| i.instance_polys.as_slice()) - .collect::>(), - &challenges, - *y, - *beta, - *gamma, - *theta, - &lookups_committed, - &shuffles_committed, - &permutations_committed, - ); + .collect() + } - // 8. Construct the vanishing argument's h(X) commitments -------------------------------------- - // [TRANSCRIPT-14] + fn gen_constructed_vanishing( + &mut self, + vanishing: vanishing::prover::Committed, + h_poly: Polynomial< + <::Curve as CurveAffine>::ScalarExt, + crate::poly::ExtendedLagrangeCoeff, + >, + ) -> Result, Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { let vanishing = vanishing.construct( &self.engine, - params, - domain, + self.params, + &self.pk.vk.domain, h_poly, - &mut rng, + &mut self.rng, self.transcript, )?; + Ok(vanishing) + } - // 9. Compute x -------------------------------------------------------------------------------- - // [TRANSCRIPT-15] - let x: ChallengeX<_> = self.transcript.squeeze_challenge_scalar(); - - let x_pow_n = x.pow([params.n()]); - - // [TRANSCRIPT-16] + fn write_instance_evals(&mut self, x: ChallengeX) -> Result<(), Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { if P::QUERY_INSTANCE { // Compute and hash instance evals for the circuit instance - for instance in instances.iter() { + for instance in self.instances.iter() { // Evaluate polynomials at omega^i x - let instance_evals: Vec<_> = cs + let instance_evals: Vec<_> = self + .pk + .vk + .cs .instance_queries .iter() .map(|&(column, at)| { eval_polynomial( &instance.instance_polys[column.index], - domain.rotate_omega(*x, at), + self.pk.vk.domain.rotate_omega(*x, at), ) }) .collect(); @@ -748,18 +937,29 @@ impl< } } } + Ok(()) + } - // 10. Compute and hash advice evals for the circuit instance ------------------------------------ - // [TRANSCRIPT-17] + fn write_advice_evals( + &mut self, + x: ChallengeX, + advice: &[AdviceSingle], + ) -> Result<(), Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { for advice in advice.iter() { // Evaluate polynomials at omega^i x - let advice_evals: Vec<_> = cs + let advice_evals: Vec<_> = self + .pk + .vk + .cs .advice_queries .iter() .map(|&(column, at)| { eval_polynomial( &advice.advice_polys[column.index], - domain.rotate_omega(*x, at), + self.pk.vk.domain.rotate_omega(*x, at), ) }) .collect(); @@ -769,13 +969,24 @@ impl< self.transcript.write_scalar(*eval)?; } } + Ok(()) + } - // 11. Compute and hash fixed evals ----------------------------------------------------------- - let fixed_evals: Vec<_> = cs + fn write_fixed_evals(&mut self, x: ChallengeX) -> Result<(), Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { + let fixed_evals: Vec<_> = self + .pk + .vk + .cs .fixed_queries .iter() .map(|&(column, at)| { - eval_polynomial(&pk.fixed_polys[column.index], domain.rotate_omega(*x, at)) + eval_polynomial( + &self.pk.fixed_polys[column.index], + self.pk.vk.domain.rotate_omega(*x, at), + ) }) .collect(); @@ -785,110 +996,68 @@ impl< self.transcript.write_scalar(*eval)?; } - // [TRANSCRIPT-19] - let vanishing = vanishing.evaluate(x, x_pow_n, domain, self.transcript)?; - - // 12. Evaluate permutation, lookups and shuffles at x ----------------------------------- - - // Evaluate common permutation data - // [TRANSCRIPT-20] - pk.permutation.evaluate(x, self.transcript)?; + Ok(()) + } - // Evaluate the permutations, if any, at omega^i x. - // [TRANSCRIPT-21] + fn evaluate_permutations( + &mut self, + x: ChallengeX, + permutations_committed: Vec>, + ) -> Result>, Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { let permutations_evaluated: Vec> = permutations_committed .into_iter() - .map(|permutation| -> Result<_, _> { permutation.evaluate(pk, x, self.transcript) }) + .map(|permutation| -> Result<_, _> { + permutation.evaluate(self.pk, x, self.transcript) + }) .collect::, _>>()?; + Ok(permutations_evaluated) + } - // Evaluate the lookups, if any, at omega^i x. - // [TRANSCRIPT-22] + fn evaluate_lookups( + &mut self, + x: ChallengeX, + lookups_committed: Vec>>, + ) -> Result>>, Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { let lookups_evaluated: Vec>> = lookups_committed .into_iter() .map(|lookups| -> Result, _> { lookups .into_iter() - .map(|p| p.evaluate(pk, x, self.transcript)) + .map(|p| p.evaluate(self.pk, x, self.transcript)) .collect::, _>>() }) .collect::, _>>()?; - // Evaluate the shuffles, if any, at omega^i x. - // [TRANSCRIPT-23] + Ok(lookups_evaluated) + } + + fn evaluate_shuffles( + &mut self, + x: ChallengeX, + shuffles_committed: Vec>>, + ) -> Result>>, Error> + where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { let shuffles_evaluated: Vec>> = shuffles_committed .into_iter() .map(|shuffles| -> Result, _> { shuffles .into_iter() - .map(|p| p.evaluate(pk, x, self.transcript)) + .map(|p| p.evaluate(self.pk, x, self.transcript)) .collect::, _>>() }) .collect::, _>>()?; - - // 13. Generate all queries ([`ProverQuery`]) that needs to be sent to prover -------------------- - - let queries = instances - // group the instance, advice, permutation, lookups and shuffles - .iter() - .zip(advice.iter()) - .zip(permutations_evaluated.iter()) - .zip(lookups_evaluated.iter()) - .zip(shuffles_evaluated.iter()) - .flat_map(|((((instance, advice), permutation), lookups), shuffles)| { - // Build a (an iterator) over a set of ProverQueries for each instance, advice, permutatiom, lookup and shuffle - iter::empty() - // Instances - .chain( - P::QUERY_INSTANCE - .then_some(cs.instance_queries.iter().map(move |&(column, at)| { - ProverQuery { - point: domain.rotate_omega(*x, at), - poly: &instance.instance_polys[column.index], - blind: Blind::default(), - } - })) - .into_iter() - .flatten(), - ) - // Advices - .chain( - cs.advice_queries - .iter() - .map(move |&(column, at)| ProverQuery { - point: domain.rotate_omega(*x, at), - poly: &advice.advice_polys[column.index], - blind: advice.advice_blinds[column.index], - }), - ) - // Permutations - .chain(permutation.open(pk, x)) - // Lookups - .chain(lookups.iter().flat_map(move |p| p.open(pk, x))) - // Shuffles - .chain(shuffles.iter().flat_map(move |p| p.open(pk, x))) - }) - // Queries to fixed columns - .chain(cs.fixed_queries.iter().map(|&(column, at)| ProverQuery { - point: domain.rotate_omega(*x, at), - poly: &pk.fixed_polys[column.index], - blind: Blind::default(), - })) - // Copy constraints - .chain(pk.permutation.open(x)) - // We query the h(X) polynomial at x - .chain(vanishing.open(x)); - - // 14. Send the queries to the [`Prover`] ------------------------------------------------ - - let prover = P::new(params); - prover - .create_proof_with_engine(&self.engine.msm_backend, rng, self.transcript, queries) - .map_err(|_| Error::ConstraintSystemFailure)?; - - Ok(()) + Ok(shuffles_evaluated) } /// Returns the phases of the circuit diff --git a/halo2_backend/src/plonk/vanishing.rs b/halo2_backend/src/plonk/vanishing.rs index 81f86b02e..4c688fe91 100644 --- a/halo2_backend/src/plonk/vanishing.rs +++ b/halo2_backend/src/plonk/vanishing.rs @@ -2,8 +2,8 @@ use std::marker::PhantomData; use crate::arithmetic::CurveAffine; -mod prover; -mod verifier; +pub(crate) mod prover; +pub(crate) mod verifier; /// A vanishing argument. pub(crate) struct Argument {