1use ff::Field;
2use group::Curve;
3use rand_core::RngCore;
4use std::iter;
5
6use super::{
7 vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error,
8 VerifyingKey,
9};
10use crate::arithmetic::{CurveAffine, FieldExt};
11use crate::poly::{
12 commitment::{Blind, Guard, Params, MSM},
13 multiopen::{self, VerifierQuery},
14};
15use crate::transcript::{read_n_points, read_n_scalars, EncodedChallenge, TranscriptRead};
16
17pub trait VerificationStrategy<'params, C: CurveAffine> {
19 type Output;
21
22 fn process<E: EncodedChallenge<C>>(
25 self,
26 f: impl FnOnce(MSM<'params, C>) -> Result<Guard<'params, C, E>, Error>,
27 ) -> Result<Self::Output, Error>;
28}
29
30#[derive(Debug)]
32pub struct SingleVerifier<'params, C: CurveAffine> {
33 msm: MSM<'params, C>,
34}
35
36impl<'params, C: CurveAffine> SingleVerifier<'params, C> {
37 pub fn new(params: &'params Params<C>) -> Self {
39 SingleVerifier {
40 msm: MSM::new(params),
41 }
42 }
43}
44
45impl<'params, C: CurveAffine> VerificationStrategy<'params, C> for SingleVerifier<'params, C> {
46 type Output = ();
47
48 fn process<E: EncodedChallenge<C>>(
49 self,
50 f: impl FnOnce(MSM<'params, C>) -> Result<Guard<'params, C, E>, Error>,
51 ) -> Result<Self::Output, Error> {
52 let guard = f(self.msm)?;
53 let msm = guard.use_challenges();
54 if msm.eval() {
55 Ok(())
56 } else {
57 Err(Error::ConstraintSystemFailure)
58 }
59 }
60}
61
62#[derive(Debug)]
64pub struct BatchVerifier<'params, C: CurveAffine, R: RngCore> {
65 msm: MSM<'params, C>,
66 rng: R,
67}
68
69impl<'params, C: CurveAffine, R: RngCore> BatchVerifier<'params, C, R> {
70 pub fn new(params: &'params Params<C>, rng: R) -> Self {
72 BatchVerifier {
73 msm: MSM::new(params),
74 rng,
75 }
76 }
77
78 #[must_use]
83 pub fn finalize(self) -> bool {
84 self.msm.eval()
85 }
86}
87
88impl<'params, C: CurveAffine, R: RngCore> VerificationStrategy<'params, C>
89 for BatchVerifier<'params, C, R>
90{
91 type Output = Self;
92
93 fn process<E: EncodedChallenge<C>>(
94 mut self,
95 f: impl FnOnce(MSM<'params, C>) -> Result<Guard<'params, C, E>, Error>,
96 ) -> Result<Self::Output, Error> {
97 self.msm.scale(C::Scalar::random(&mut self.rng));
101
102 let guard = f(self.msm)?;
103 let msm = guard.use_challenges();
104 Ok(Self { msm, rng: self.rng })
105 }
106}
107
108pub fn verify_proof<
110 'params,
111 C: CurveAffine,
112 E: EncodedChallenge<C>,
113 T: TranscriptRead<C, E>,
114 V: VerificationStrategy<'params, C>,
115>(
116 params: &'params Params<C>,
117 vk: &VerifyingKey<C>,
118 strategy: V,
119 instances: &[&[&[C::Scalar]]],
120 transcript: &mut T,
121) -> Result<V::Output, Error> {
122 for instances in instances.iter() {
124 if instances.len() != vk.cs.num_instance_columns {
125 return Err(Error::InvalidInstances);
126 }
127 }
128
129 let instance_commitments = instances
130 .iter()
131 .map(|instance| {
132 instance
133 .iter()
134 .map(|instance| {
135 if instance.len() > params.n as usize - (vk.cs.blinding_factors() + 1) {
136 return Err(Error::InstanceTooLarge);
137 }
138 let mut poly = instance.to_vec();
139 poly.resize(params.n as usize, C::Scalar::zero());
140 let poly = vk.domain.lagrange_from_vec(poly);
141
142 Ok(params.commit_lagrange(&poly, Blind::default()).to_affine())
143 })
144 .collect::<Result<Vec<_>, _>>()
145 })
146 .collect::<Result<Vec<_>, _>>()?;
147
148 let num_proofs = instance_commitments.len();
149
150 vk.hash_into(transcript)?;
152
153 for instance_commitments in instance_commitments.iter() {
154 for commitment in instance_commitments {
156 transcript.common_point(*commitment)?
157 }
158 }
159
160 let advice_commitments = (0..num_proofs)
161 .map(|_| -> Result<Vec<_>, _> {
162 read_n_points(transcript, vk.cs.num_advice_columns)
164 })
165 .collect::<Result<Vec<_>, _>>()?;
166
167 let theta: ChallengeTheta<_> = transcript.squeeze_challenge_scalar();
169
170 let lookups_permuted = (0..num_proofs)
171 .map(|_| -> Result<Vec<_>, _> {
172 vk.cs
174 .lookups
175 .iter()
176 .map(|argument| argument.read_permuted_commitments(transcript))
177 .collect::<Result<Vec<_>, _>>()
178 })
179 .collect::<Result<Vec<_>, _>>()?;
180
181 let beta: ChallengeBeta<_> = transcript.squeeze_challenge_scalar();
183
184 let gamma: ChallengeGamma<_> = transcript.squeeze_challenge_scalar();
186
187 let permutations_committed = (0..num_proofs)
188 .map(|_| {
189 vk.cs.permutation.read_product_commitments(vk, transcript)
191 })
192 .collect::<Result<Vec<_>, _>>()?;
193
194 let lookups_committed = lookups_permuted
195 .into_iter()
196 .map(|lookups| {
197 lookups
199 .into_iter()
200 .map(|lookup| lookup.read_product_commitment(transcript))
201 .collect::<Result<Vec<_>, _>>()
202 })
203 .collect::<Result<Vec<_>, _>>()?;
204
205 let vanishing = vanishing::Argument::read_commitments_before_y(transcript)?;
206
207 let y: ChallengeY<_> = transcript.squeeze_challenge_scalar();
209
210 let vanishing = vanishing.read_commitments_after_y(vk, transcript)?;
211
212 let x: ChallengeX<_> = transcript.squeeze_challenge_scalar();
215 let instance_evals = (0..num_proofs)
216 .map(|_| -> Result<Vec<_>, _> { read_n_scalars(transcript, vk.cs.instance_queries.len()) })
217 .collect::<Result<Vec<_>, _>>()?;
218
219 let advice_evals = (0..num_proofs)
220 .map(|_| -> Result<Vec<_>, _> { read_n_scalars(transcript, vk.cs.advice_queries.len()) })
221 .collect::<Result<Vec<_>, _>>()?;
222
223 let fixed_evals = read_n_scalars(transcript, vk.cs.fixed_queries.len())?;
224
225 let vanishing = vanishing.evaluate_after_x(transcript)?;
226
227 let permutations_common = vk.permutation.evaluate(transcript)?;
228
229 let permutations_evaluated = permutations_committed
230 .into_iter()
231 .map(|permutation| permutation.evaluate(transcript))
232 .collect::<Result<Vec<_>, _>>()?;
233
234 let lookups_evaluated = lookups_committed
235 .into_iter()
236 .map(|lookups| -> Result<Vec<_>, _> {
237 lookups
238 .into_iter()
239 .map(|lookup| lookup.evaluate(transcript))
240 .collect::<Result<Vec<_>, _>>()
241 })
242 .collect::<Result<Vec<_>, _>>()?;
243
244 let vanishing = {
247 let xn = x.pow(&[params.n as u64, 0, 0, 0]);
249
250 let blinding_factors = vk.cs.blinding_factors();
251 let l_evals = vk
252 .domain
253 .l_i_range(*x, xn, (-((blinding_factors + 1) as i32))..=0);
254 assert_eq!(l_evals.len(), 2 + blinding_factors);
255 let l_last = l_evals[0];
256 let l_blind: C::Scalar = l_evals[1..(1 + blinding_factors)]
257 .iter()
258 .fold(C::Scalar::zero(), |acc, eval| acc + eval);
259 let l_0 = l_evals[1 + blinding_factors];
260
261 let expressions = advice_evals
263 .iter()
264 .zip(instance_evals.iter())
265 .zip(permutations_evaluated.iter())
266 .zip(lookups_evaluated.iter())
267 .flat_map(|(((advice_evals, instance_evals), permutation), lookups)| {
268 let fixed_evals = &fixed_evals;
269 std::iter::empty()
270 .chain(vk.cs.gates.iter().flat_map(move |gate| {
272 gate.polynomials().iter().map(move |poly| {
273 poly.evaluate(
274 &|scalar| scalar,
275 &|_| panic!("virtual selectors are removed during optimization"),
276 &|index, _, _| fixed_evals[index],
277 &|index, _, _| advice_evals[index],
278 &|index, _, _| instance_evals[index],
279 &|a| -a,
280 &|a, b| a + &b,
281 &|a, b| a * &b,
282 &|a, scalar| a * &scalar,
283 )
284 })
285 }))
286 .chain(permutation.expressions(
287 vk,
288 &vk.cs.permutation,
289 &permutations_common,
290 advice_evals,
291 fixed_evals,
292 instance_evals,
293 l_0,
294 l_last,
295 l_blind,
296 beta,
297 gamma,
298 x,
299 ))
300 .chain(
301 lookups
302 .iter()
303 .zip(vk.cs.lookups.iter())
304 .flat_map(move |(p, argument)| {
305 p.expressions(
306 l_0,
307 l_last,
308 l_blind,
309 argument,
310 theta,
311 beta,
312 gamma,
313 advice_evals,
314 fixed_evals,
315 instance_evals,
316 )
317 })
318 .into_iter(),
319 )
320 });
321
322 vanishing.verify(params, expressions, y, xn)
323 };
324
325 let queries = instance_commitments
326 .iter()
327 .zip(instance_evals.iter())
328 .zip(advice_commitments.iter())
329 .zip(advice_evals.iter())
330 .zip(permutations_evaluated.iter())
331 .zip(lookups_evaluated.iter())
332 .flat_map(
333 |(
334 (
335 (((instance_commitments, instance_evals), advice_commitments), advice_evals),
336 permutation,
337 ),
338 lookups,
339 )| {
340 iter::empty()
341 .chain(vk.cs.instance_queries.iter().enumerate().map(
342 move |(query_index, &(column, at))| {
343 VerifierQuery::new_commitment(
344 &instance_commitments[column.index()],
345 vk.domain.rotate_omega(*x, at),
346 instance_evals[query_index],
347 )
348 },
349 ))
350 .chain(vk.cs.advice_queries.iter().enumerate().map(
351 move |(query_index, &(column, at))| {
352 VerifierQuery::new_commitment(
353 &advice_commitments[column.index()],
354 vk.domain.rotate_omega(*x, at),
355 advice_evals[query_index],
356 )
357 },
358 ))
359 .chain(permutation.queries(vk, x))
360 .chain(
361 lookups
362 .iter()
363 .flat_map(move |p| p.queries(vk, x))
364 .into_iter(),
365 )
366 },
367 )
368 .chain(
369 vk.cs
370 .fixed_queries
371 .iter()
372 .enumerate()
373 .map(|(query_index, &(column, at))| {
374 VerifierQuery::new_commitment(
375 &vk.fixed_commitments[column.index()],
376 vk.domain.rotate_omega(*x, at),
377 fixed_evals[query_index],
378 )
379 }),
380 )
381 .chain(permutations_common.queries(&vk.permutation, x))
382 .chain(vanishing.queries(x));
383
384 strategy.process(|msm| {
387 multiopen::verify_proof(params, transcript, queries, msm).map_err(|_| Error::Opening)
388 })
389}