halo2_axiom/plonk/lookup/
verifier.rs

1use std::iter;
2
3use super::super::{
4    circuit::Expression, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX,
5};
6use super::Argument;
7use crate::{
8    arithmetic::CurveAffine,
9    plonk::{Error, VerifyingKey},
10    poly::{commitment::MSM, Rotation, VerifierQuery},
11    transcript::{EncodedChallenge, TranscriptRead},
12};
13use ff::Field;
14
15pub struct PermutationCommitments<C: CurveAffine> {
16    permuted_input_commitment: C,
17    permuted_table_commitment: C,
18}
19
20pub struct Committed<C: CurveAffine> {
21    permuted: PermutationCommitments<C>,
22    product_commitment: C,
23}
24
25pub struct Evaluated<C: CurveAffine> {
26    committed: Committed<C>,
27    product_eval: C::Scalar,
28    product_next_eval: C::Scalar,
29    permuted_input_eval: C::Scalar,
30    permuted_input_inv_eval: C::Scalar,
31    permuted_table_eval: C::Scalar,
32}
33
34impl<F: Field> Argument<F> {
35    pub(in crate::plonk) fn read_permuted_commitments<
36        C: CurveAffine,
37        E: EncodedChallenge<C>,
38        T: TranscriptRead<C, E>,
39    >(
40        &self,
41        transcript: &mut T,
42    ) -> Result<PermutationCommitments<C>, Error> {
43        let permuted_input_commitment = transcript.read_point()?;
44        let permuted_table_commitment = transcript.read_point()?;
45
46        Ok(PermutationCommitments {
47            permuted_input_commitment,
48            permuted_table_commitment,
49        })
50    }
51}
52
53impl<C: CurveAffine> PermutationCommitments<C> {
54    pub(in crate::plonk) fn read_product_commitment<
55        E: EncodedChallenge<C>,
56        T: TranscriptRead<C, E>,
57    >(
58        self,
59        transcript: &mut T,
60    ) -> Result<Committed<C>, Error> {
61        let product_commitment = transcript.read_point()?;
62
63        Ok(Committed {
64            permuted: self,
65            product_commitment,
66        })
67    }
68}
69
70impl<C: CurveAffine> Committed<C> {
71    pub(crate) fn evaluate<E: EncodedChallenge<C>, T: TranscriptRead<C, E>>(
72        self,
73        transcript: &mut T,
74    ) -> Result<Evaluated<C>, Error> {
75        let product_eval = transcript.read_scalar()?;
76        let product_next_eval = transcript.read_scalar()?;
77        let permuted_input_eval = transcript.read_scalar()?;
78        let permuted_input_inv_eval = transcript.read_scalar()?;
79        let permuted_table_eval = transcript.read_scalar()?;
80
81        Ok(Evaluated {
82            committed: self,
83            product_eval,
84            product_next_eval,
85            permuted_input_eval,
86            permuted_input_inv_eval,
87            permuted_table_eval,
88        })
89    }
90}
91
92impl<C: CurveAffine> Evaluated<C> {
93    #[allow(clippy::too_many_arguments)]
94    pub(in crate::plonk) fn expressions<'a>(
95        &'a self,
96        l_0: C::Scalar,
97        l_last: C::Scalar,
98        l_blind: C::Scalar,
99        argument: &'a Argument<C::Scalar>,
100        theta: ChallengeTheta<C>,
101        beta: ChallengeBeta<C>,
102        gamma: ChallengeGamma<C>,
103        advice_evals: &[C::Scalar],
104        fixed_evals: &[C::Scalar],
105        instance_evals: &[C::Scalar],
106        challenges: &[C::Scalar],
107    ) -> impl Iterator<Item = C::Scalar> + 'a {
108        let active_rows = C::Scalar::ONE - (l_last + l_blind);
109
110        let product_expression = || {
111            // z(\omega X) (a'(X) + \beta) (s'(X) + \gamma)
112            // - z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma)
113            let left = self.product_next_eval
114                * &(self.permuted_input_eval + &*beta)
115                * &(self.permuted_table_eval + &*gamma);
116
117            let compress_expressions = |expressions: &[Expression<C::Scalar>]| {
118                expressions
119                    .iter()
120                    .map(|expression| {
121                        expression.evaluate(
122                            &|scalar| scalar,
123                            &|_| panic!("virtual selectors are removed during optimization"),
124                            &|query| fixed_evals[query.index.unwrap()],
125                            &|query| advice_evals[query.index.unwrap()],
126                            &|query| instance_evals[query.index.unwrap()],
127                            &|challenge| challenges[challenge.index()],
128                            &|a| -a,
129                            &|a, b| a + &b,
130                            &|a, b| a * &b,
131                            &|a, scalar| a * &scalar,
132                        )
133                    })
134                    .fold(C::Scalar::ZERO, |acc, eval| acc * &*theta + &eval)
135            };
136            let right = self.product_eval
137                * &(compress_expressions(&argument.input_expressions) + &*beta)
138                * &(compress_expressions(&argument.table_expressions) + &*gamma);
139
140            (left - &right) * &active_rows
141        };
142
143        std::iter::empty()
144            .chain(
145                // l_0(X) * (1 - z(X)) = 0
146                Some(l_0 * &(C::Scalar::ONE - &self.product_eval)),
147            )
148            .chain(
149                // l_last(X) * (z(X)^2 - z(X)) = 0
150                Some(l_last * &(self.product_eval.square() - &self.product_eval)),
151            )
152            .chain(
153                // (1 - (l_last(X) + l_blind(X))) * (
154                //   z(\omega X) (a'(X) + \beta) (s'(X) + \gamma)
155                //   - z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma)
156                // ) = 0
157                Some(product_expression()),
158            )
159            .chain(Some(
160                // l_0(X) * (a'(X) - s'(X)) = 0
161                l_0 * &(self.permuted_input_eval - &self.permuted_table_eval),
162            ))
163            .chain(Some(
164                // (1 - (l_last(X) + l_blind(X))) * (a′(X) − s′(X))⋅(a′(X) − a′(\omega^{-1} X)) = 0
165                (self.permuted_input_eval - &self.permuted_table_eval)
166                    * &(self.permuted_input_eval - &self.permuted_input_inv_eval)
167                    * &active_rows,
168            ))
169    }
170
171    pub(in crate::plonk) fn queries<'r, M: MSM<C> + 'r>(
172        &'r self,
173        vk: &'r VerifyingKey<C>,
174        x: ChallengeX<C>,
175    ) -> impl Iterator<Item = VerifierQuery<'r, C, M>> + Clone {
176        let x_inv = vk.domain.rotate_omega(*x, Rotation::prev());
177        let x_next = vk.domain.rotate_omega(*x, Rotation::next());
178
179        iter::empty()
180            // Open lookup product commitment at x
181            .chain(Some(VerifierQuery::new_commitment(
182                &self.committed.product_commitment,
183                *x,
184                self.product_eval,
185            )))
186            // Open lookup input commitments at x
187            .chain(Some(VerifierQuery::new_commitment(
188                &self.committed.permuted.permuted_input_commitment,
189                *x,
190                self.permuted_input_eval,
191            )))
192            // Open lookup table commitments at x
193            .chain(Some(VerifierQuery::new_commitment(
194                &self.committed.permuted.permuted_table_commitment,
195                *x,
196                self.permuted_table_eval,
197            )))
198            // Open lookup input commitments at \omega^{-1} x
199            .chain(Some(VerifierQuery::new_commitment(
200                &self.committed.permuted.permuted_input_commitment,
201                x_inv,
202                self.permuted_input_inv_eval,
203            )))
204            // Open lookup product commitment at \omega x
205            .chain(Some(VerifierQuery::new_commitment(
206                &self.committed.product_commitment,
207                x_next,
208                self.product_next_eval,
209            )))
210    }
211}