halo2_axiom/plonk/permutation/
prover.rs

1use ff::PrimeField;
2use group::{
3    ff::{BatchInvert, Field},
4    Curve,
5};
6use rand_core::RngCore;
7use std::iter::{self, ExactSizeIterator};
8
9use super::super::{circuit::Any, ChallengeBeta, ChallengeGamma, ChallengeX};
10use super::{Argument, ProvingKey};
11use crate::{
12    arithmetic::{eval_polynomial, parallelize, CurveAffine},
13    plonk::{self, Error},
14    poly::{
15        commitment::{Blind, Params},
16        Coeff, LagrangeCoeff, Polynomial, ProverQuery, Rotation,
17    },
18    transcript::{EncodedChallenge, TranscriptWrite},
19};
20
21pub(crate) struct CommittedSet<C: CurveAffine> {
22    pub(crate) permutation_product_poly: Polynomial<C::Scalar, Coeff>,
23    permutation_product_blind: Blind<C::Scalar>,
24}
25
26pub(crate) struct Committed<C: CurveAffine> {
27    pub(crate) sets: Vec<CommittedSet<C>>,
28}
29
30pub struct ConstructedSet<C: CurveAffine> {
31    permutation_product_poly: Polynomial<C::Scalar, Coeff>,
32    permutation_product_blind: Blind<C::Scalar>,
33}
34
35pub(crate) struct Constructed<C: CurveAffine> {
36    sets: Vec<ConstructedSet<C>>,
37}
38
39pub(crate) struct Evaluated<C: CurveAffine> {
40    constructed: Constructed<C>,
41}
42
43impl Argument {
44    #[allow(clippy::too_many_arguments)]
45    pub(in crate::plonk) fn commit<
46        'params,
47        C: CurveAffine,
48        P: Params<'params, C>,
49        E: EncodedChallenge<C>,
50        R: RngCore,
51        T: TranscriptWrite<C, E>,
52    >(
53        &self,
54        params: &P,
55        pk: &plonk::ProvingKey<C>,
56        pkey: &ProvingKey<C>,
57        advice: &[Polynomial<C::Scalar, LagrangeCoeff>],
58        fixed: &[Polynomial<C::Scalar, LagrangeCoeff>],
59        instance: &[Polynomial<C::Scalar, LagrangeCoeff>],
60        beta: ChallengeBeta<C>,
61        gamma: ChallengeGamma<C>,
62        mut rng: R,
63        transcript: &mut T,
64    ) -> Result<Committed<C>, Error> {
65        let domain = &pk.vk.domain;
66
67        // How many columns can be included in a single permutation polynomial?
68        // We need to multiply by z(X) and (1 - (l_last(X) + l_blind(X))). This
69        // will never underflow because of the requirement of at least a degree
70        // 3 circuit for the permutation argument.
71        assert!(pk.vk.cs_degree >= 3);
72        let chunk_len = pk.vk.cs_degree - 2;
73        let blinding_factors = pk.vk.cs.blinding_factors();
74
75        // Each column gets its own delta power.
76        let mut deltaomega = C::Scalar::ONE;
77
78        // Track the "last" value from the previous column set
79        let mut last_z = C::Scalar::ONE;
80
81        let mut sets = vec![];
82
83        for (columns, permutations) in self
84            .columns
85            .chunks(chunk_len)
86            .zip(pkey.permutations.chunks(chunk_len))
87        {
88            // Goal is to compute the products of fractions
89            //
90            // (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) /
91            // (p_j(\omega^i) + \beta s_j(\omega^i) + \gamma)
92            //
93            // where p_j(X) is the jth column in this permutation,
94            // and i is the ith row of the column.
95
96            let mut modified_values = vec![C::Scalar::ONE; params.n() as usize];
97
98            // Iterate over each column of the permutation
99            for (&column, permuted_column_values) in columns.iter().zip(permutations.iter()) {
100                let values = match column.column_type() {
101                    Any::Advice(_) => advice,
102                    Any::Fixed => fixed,
103                    Any::Instance => instance,
104                };
105                parallelize(&mut modified_values, |modified_values, start| {
106                    for ((modified_values, value), permuted_value) in modified_values
107                        .iter_mut()
108                        .zip(values[column.index()][start..].iter())
109                        .zip(permuted_column_values[start..].iter())
110                    {
111                        *modified_values *= &(*beta * permuted_value + &*gamma + value);
112                    }
113                });
114            }
115
116            // Invert to obtain the denominator for the permutation product polynomial
117            modified_values.batch_invert();
118
119            // Iterate over each column again, this time finishing the computation
120            // of the entire fraction by computing the numerators
121            for &column in columns.iter() {
122                let omega = domain.get_omega();
123                let values = match column.column_type() {
124                    Any::Advice(_) => advice,
125                    Any::Fixed => fixed,
126                    Any::Instance => instance,
127                };
128                parallelize(&mut modified_values, |modified_values, start| {
129                    let mut deltaomega = deltaomega * &omega.pow_vartime([start as u64, 0, 0, 0]);
130                    for (modified_values, value) in modified_values
131                        .iter_mut()
132                        .zip(values[column.index()][start..].iter())
133                    {
134                        // Multiply by p_j(\omega^i) + \delta^j \omega^i \beta
135                        *modified_values *= &(deltaomega * &*beta + &*gamma + value);
136                        deltaomega *= &omega;
137                    }
138                });
139                deltaomega *= &<C::Scalar as PrimeField>::DELTA;
140            }
141
142            // The modified_values vector is a vector of products of fractions
143            // of the form
144            //
145            // (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) /
146            // (p_j(\omega^i) + \beta s_j(\omega^i) + \gamma)
147            //
148            // where i is the index into modified_values, for the jth column in
149            // the permutation
150
151            // Compute the evaluations of the permutation product polynomial
152            // over our domain, starting with z[0] = 1
153            let mut z = vec![last_z];
154            for row in 1..(params.n() as usize) {
155                let mut tmp = z[row - 1];
156
157                tmp *= &modified_values[row - 1];
158                z.push(tmp);
159            }
160            let mut z = domain.lagrange_from_vec(z);
161            // Set blinding factors
162            for z in &mut z[params.n() as usize - blinding_factors..] {
163                *z = C::Scalar::random(&mut rng);
164            }
165            // Set new last_z
166            last_z = z[params.n() as usize - (blinding_factors + 1)];
167
168            let blind = Blind(C::Scalar::random(&mut rng));
169
170            let permutation_product_commitment_projective = params.commit_lagrange(&z, blind);
171            let permutation_product_blind = blind;
172            let z = domain.lagrange_to_coeff(z);
173            let permutation_product_poly = z.clone();
174
175            let permutation_product_commitment =
176                permutation_product_commitment_projective.to_affine();
177
178            // Hash the permutation product commitment
179            transcript.write_point(permutation_product_commitment)?;
180
181            sets.push(CommittedSet {
182                permutation_product_poly,
183                permutation_product_blind,
184            });
185        }
186
187        Ok(Committed { sets })
188    }
189}
190
191impl<C: CurveAffine> Committed<C> {
192    pub(in crate::plonk) fn construct(self) -> Constructed<C> {
193        Constructed {
194            sets: self
195                .sets
196                .iter()
197                .map(|set| ConstructedSet {
198                    permutation_product_poly: set.permutation_product_poly.clone(),
199                    permutation_product_blind: set.permutation_product_blind,
200                })
201                .collect(),
202        }
203    }
204}
205
206impl<C: CurveAffine> super::ProvingKey<C> {
207    pub(in crate::plonk) fn open(
208        &self,
209        x: ChallengeX<C>,
210    ) -> impl Iterator<Item = ProverQuery<'_, C>> + Clone {
211        self.polys.iter().map(move |poly| ProverQuery {
212            point: *x,
213            poly,
214            blind: Blind::default(),
215        })
216    }
217
218    pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
219        &self,
220        x: ChallengeX<C>,
221        transcript: &mut T,
222    ) -> Result<(), Error> {
223        // Hash permutation evals
224        for eval in self.polys.iter().map(|poly| eval_polynomial(poly, *x)) {
225            transcript.write_scalar(eval)?;
226        }
227
228        Ok(())
229    }
230}
231
232impl<C: CurveAffine> Constructed<C> {
233    pub(in crate::plonk) fn evaluate<E: EncodedChallenge<C>, T: TranscriptWrite<C, E>>(
234        self,
235        pk: &plonk::ProvingKey<C>,
236        x: ChallengeX<C>,
237        transcript: &mut T,
238    ) -> Result<Evaluated<C>, Error> {
239        let domain = &pk.vk.domain;
240        let blinding_factors = pk.vk.cs.blinding_factors();
241
242        {
243            let mut sets = self.sets.iter();
244
245            while let Some(set) = sets.next() {
246                let permutation_product_eval = eval_polynomial(&set.permutation_product_poly, *x);
247
248                let permutation_product_next_eval = eval_polynomial(
249                    &set.permutation_product_poly,
250                    domain.rotate_omega(*x, Rotation::next()),
251                );
252
253                // Hash permutation product evals
254                for eval in iter::empty()
255                    .chain(Some(&permutation_product_eval))
256                    .chain(Some(&permutation_product_next_eval))
257                {
258                    transcript.write_scalar(*eval)?;
259                }
260
261                // If we have any remaining sets to process, evaluate this set at omega^u
262                // so we can constrain the last value of its running product to equal the
263                // first value of the next set's running product, chaining them together.
264                if sets.len() > 0 {
265                    let permutation_product_last_eval = eval_polynomial(
266                        &set.permutation_product_poly,
267                        domain.rotate_omega(*x, Rotation(-((blinding_factors + 1) as i32))),
268                    );
269
270                    transcript.write_scalar(permutation_product_last_eval)?;
271                }
272            }
273        }
274
275        Ok(Evaluated { constructed: self })
276    }
277}
278
279impl<C: CurveAffine> Evaluated<C> {
280    pub(in crate::plonk) fn open<'a>(
281        &'a self,
282        pk: &'a plonk::ProvingKey<C>,
283        x: ChallengeX<C>,
284    ) -> impl Iterator<Item = ProverQuery<'a, C>> + Clone {
285        let blinding_factors = pk.vk.cs.blinding_factors();
286        let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next());
287        let x_last = pk
288            .vk
289            .domain
290            .rotate_omega(*x, Rotation(-((blinding_factors + 1) as i32)));
291
292        iter::empty()
293            .chain(self.constructed.sets.iter().flat_map(move |set| {
294                iter::empty()
295                    // Open permutation product commitments at x and \omega x
296                    .chain(Some(ProverQuery {
297                        point: *x,
298                        poly: &set.permutation_product_poly,
299                        blind: set.permutation_product_blind,
300                    }))
301                    .chain(Some(ProverQuery {
302                        point: x_next,
303                        poly: &set.permutation_product_poly,
304                        blind: set.permutation_product_blind,
305                    }))
306            }))
307            // Open it at \omega^{last} x for all but the last set. This rotation is only
308            // sensical for the first row, but we only use this rotation in a constraint
309            // that is gated on l_0.
310            .chain(
311                self.constructed
312                    .sets
313                    .iter()
314                    .rev()
315                    .skip(1)
316                    .flat_map(move |set| {
317                        Some(ProverQuery {
318                            point: x_last,
319                            poly: &set.permutation_product_poly,
320                            blind: set.permutation_product_blind,
321                        })
322                    }),
323            )
324    }
325}