1use crate::{
5 alphas::Alphas,
6 circuits::{
7 berkeley_columns::{BerkeleyChallengeTerm, Column},
8 expr::{Linearization, PolishToken},
9 lookup::{index::LookupSelectors, lookups::LookupInfo},
10 polynomials::permutation::{vanishes_on_last_n_rows, zk_w},
11 wires::{COLUMNS, PERMUTS},
12 },
13 curve::KimchiCurve,
14 prover_index::ProverIndex,
15};
16use ark_ff::{One, PrimeField};
17use ark_poly::{univariate::DensePolynomial, Radix2EvaluationDomain as D};
18use core::array;
19use mina_poseidon::FqSponge;
20use once_cell::sync::OnceCell;
21use poly_commitment::{
22 commitment::{absorb_commitment, CommitmentCurve, PolyComm},
23 OpenProof, SRS as _,
24};
25use serde::{de::DeserializeOwned, Deserialize, Serialize};
26use serde_with::serde_as;
27use std::{
28 fs::{File, OpenOptions},
29 io::{BufReader, BufWriter, Seek, SeekFrom::Start},
30 path::Path,
31 sync::Arc,
32};
33
34#[serde_as]
36#[derive(Serialize, Deserialize, Debug, Clone)]
37pub struct LookupVerifierIndex<G: CommitmentCurve> {
38 pub joint_lookup_used: bool,
39 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
40 pub lookup_table: Vec<PolyComm<G>>,
41 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
42 pub lookup_selectors: LookupSelectors<PolyComm<G>>,
43
44 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
47 pub table_ids: Option<PolyComm<G>>,
48
49 pub lookup_info: LookupInfo,
51
52 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
54 pub runtime_tables_selector: Option<PolyComm<G>>,
55}
56
57#[serde_as]
58#[derive(Serialize, Deserialize, Debug, Clone)]
59pub struct VerifierIndex<G: KimchiCurve, OpeningProof: OpenProof<G>> {
60 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
62 pub domain: D<G::ScalarField>,
63 pub max_poly_size: usize,
65 pub zk_rows: u64,
67 #[serde(skip)]
69 #[serde(bound(deserialize = "OpeningProof::SRS: Default"))]
70 pub srs: Arc<OpeningProof::SRS>,
71 pub public: usize,
73 pub prev_challenges: usize,
75
76 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
79 pub sigma_comm: [PolyComm<G>; PERMUTS],
80 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
82 pub coefficients_comm: [PolyComm<G>; COLUMNS],
83 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
85 pub generic_comm: PolyComm<G>,
86
87 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
90 pub psm_comm: PolyComm<G>,
91
92 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
95 pub complete_add_comm: PolyComm<G>,
96 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
98 pub mul_comm: PolyComm<G>,
99 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
101 pub emul_comm: PolyComm<G>,
102 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
104 pub endomul_scalar_comm: PolyComm<G>,
105
106 #[serde(bound = "Option<PolyComm<G>>: Serialize + DeserializeOwned")]
108 pub range_check0_comm: Option<PolyComm<G>>,
109
110 #[serde(bound = "Option<PolyComm<G>>: Serialize + DeserializeOwned")]
112 pub range_check1_comm: Option<PolyComm<G>>,
113
114 #[serde(bound = "Option<PolyComm<G>>: Serialize + DeserializeOwned")]
116 pub foreign_field_add_comm: Option<PolyComm<G>>,
117
118 #[serde(bound = "Option<PolyComm<G>>: Serialize + DeserializeOwned")]
120 pub foreign_field_mul_comm: Option<PolyComm<G>>,
121
122 #[serde(bound = "Option<PolyComm<G>>: Serialize + DeserializeOwned")]
124 pub xor_comm: Option<PolyComm<G>>,
125
126 #[serde(bound = "Option<PolyComm<G>>: Serialize + DeserializeOwned")]
128 pub rot_comm: Option<PolyComm<G>>,
129
130 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
132 pub shift: [G::ScalarField; PERMUTS],
133 #[serde(skip)]
135 pub permutation_vanishing_polynomial_m: OnceCell<DensePolynomial<G::ScalarField>>,
136 #[serde(skip)]
139 pub w: OnceCell<G::ScalarField>,
140 #[serde(skip)]
142 pub endo: G::ScalarField,
143
144 #[serde(bound = "PolyComm<G>: Serialize + DeserializeOwned")]
145 pub lookup_index: Option<LookupVerifierIndex<G>>,
146
147 #[serde(skip)]
148 pub linearization:
149 Linearization<Vec<PolishToken<G::ScalarField, Column, BerkeleyChallengeTerm>>, Column>,
150 #[serde(skip)]
152 pub powers_of_alpha: Alphas<G::ScalarField>,
153}
154impl<G: KimchiCurve, OpeningProof: OpenProof<G>> ProverIndex<G, OpeningProof>
157where
158 G::BaseField: PrimeField,
159{
160 pub fn verifier_index(&self) -> VerifierIndex<G, OpeningProof>
166 where
167 VerifierIndex<G, OpeningProof>: Clone,
168 {
169 if let Some(verifier_index) = &self.verifier_index {
170 return verifier_index.clone();
171 }
172
173 let mask_fixed = |commitment: PolyComm<G>| {
174 let blinders = commitment.map(|_| G::ScalarField::one());
175 self.srs
176 .mask_custom(commitment, &blinders)
177 .unwrap()
178 .commitment
179 };
180
181 let domain = self.cs.domain.d1;
182
183 let lookup_index = {
184 self.cs
185 .lookup_constraint_system
186 .get()
187 .as_ref()
188 .unwrap()
189 .as_ref()
190 .map(|cs| LookupVerifierIndex {
191 joint_lookup_used: cs.configuration.lookup_info.features.joint_lookup_used,
192 lookup_info: cs.configuration.lookup_info,
193 lookup_selectors: cs
194 .lookup_selectors
195 .as_ref()
196 .map(|e| self.srs.commit_evaluations_non_hiding(domain, e)),
197 lookup_table: cs
198 .lookup_table8
199 .iter()
200 .map(|e| mask_fixed(self.srs.commit_evaluations_non_hiding(domain, e)))
201 .collect(),
202 table_ids: cs.table_ids8.as_ref().map(|table_ids8| {
203 mask_fixed(self.srs.commit_evaluations_non_hiding(domain, table_ids8))
204 }),
205 runtime_tables_selector: cs
206 .runtime_selector
207 .as_ref()
208 .map(|e| self.srs.commit_evaluations_non_hiding(domain, e)),
209 })
210 };
211
212 let column_evaluations = self.column_evaluations.get();
214
215 VerifierIndex {
217 domain,
218 max_poly_size: self.max_poly_size,
219 zk_rows: self.cs.zk_rows,
220 powers_of_alpha: self.powers_of_alpha.clone(),
221 public: self.cs.public,
222 prev_challenges: self.cs.prev_challenges,
223 srs: Arc::clone(&self.srs),
224
225 sigma_comm: array::from_fn(|i| {
226 self.srs.commit_evaluations_non_hiding(
227 domain,
228 &column_evaluations.permutation_coefficients8[i],
229 )
230 }),
231 coefficients_comm: array::from_fn(|i| {
232 self.srs
233 .commit_evaluations_non_hiding(domain, &column_evaluations.coefficients8[i])
234 }),
235 generic_comm: mask_fixed(
236 self.srs
237 .commit_evaluations_non_hiding(domain, &column_evaluations.generic_selector4),
238 ),
239
240 psm_comm: mask_fixed(
241 self.srs
242 .commit_evaluations_non_hiding(domain, &column_evaluations.poseidon_selector8),
243 ),
244
245 complete_add_comm: mask_fixed(
246 self.srs.commit_evaluations_non_hiding(
247 domain,
248 &column_evaluations.complete_add_selector4,
249 ),
250 ),
251 mul_comm: mask_fixed(
252 self.srs
253 .commit_evaluations_non_hiding(domain, &column_evaluations.mul_selector8),
254 ),
255 emul_comm: mask_fixed(
256 self.srs
257 .commit_evaluations_non_hiding(domain, &column_evaluations.emul_selector8),
258 ),
259
260 endomul_scalar_comm: mask_fixed(self.srs.commit_evaluations_non_hiding(
261 domain,
262 &column_evaluations.endomul_scalar_selector8,
263 )),
264
265 range_check0_comm: column_evaluations
266 .range_check0_selector8
267 .as_ref()
268 .map(|eval8| self.srs.commit_evaluations_non_hiding(domain, eval8)),
269
270 range_check1_comm: column_evaluations
271 .range_check1_selector8
272 .as_ref()
273 .map(|eval8| self.srs.commit_evaluations_non_hiding(domain, eval8)),
274
275 foreign_field_add_comm: column_evaluations
276 .foreign_field_add_selector8
277 .as_ref()
278 .map(|eval8| self.srs.commit_evaluations_non_hiding(domain, eval8)),
279
280 foreign_field_mul_comm: column_evaluations
281 .foreign_field_mul_selector8
282 .as_ref()
283 .map(|eval8| self.srs.commit_evaluations_non_hiding(domain, eval8)),
284 xor_comm: column_evaluations
285 .xor_selector8
286 .as_ref()
287 .map(|eval8| self.srs.commit_evaluations_non_hiding(domain, eval8)),
288 rot_comm: column_evaluations
289 .rot_selector8
290 .as_ref()
291 .map(|eval8| self.srs.commit_evaluations_non_hiding(domain, eval8)),
292
293 shift: self.cs.shift,
294 permutation_vanishing_polynomial_m: {
295 let cell = OnceCell::new();
296 cell.set(
297 self.cs
298 .precomputations()
299 .permutation_vanishing_polynomial_m
300 .clone(),
301 )
302 .unwrap();
303 cell
304 },
305 w: {
306 let cell = OnceCell::new();
307 cell.set(zk_w(self.cs.domain.d1, self.cs.zk_rows)).unwrap();
308 cell
309 },
310 endo: self.cs.endo,
311 lookup_index,
312 linearization: self.linearization.clone(),
313 }
314 }
315}
316
317impl<G: KimchiCurve, OpeningProof: OpenProof<G>> VerifierIndex<G, OpeningProof> {
318 pub fn srs(&self) -> &Arc<OpeningProof::SRS>
320 where
321 G::BaseField: PrimeField,
322 {
323 &self.srs
324 }
325
326 pub fn permutation_vanishing_polynomial_m(&self) -> &DensePolynomial<G::ScalarField> {
328 self.permutation_vanishing_polynomial_m
329 .get_or_init(|| vanishes_on_last_n_rows(self.domain, self.zk_rows))
330 }
331
332 pub fn w(&self) -> &G::ScalarField {
334 self.w.get_or_init(|| zk_w(self.domain, self.zk_rows))
335 }
336
337 pub fn from_file(
343 srs: Arc<OpeningProof::SRS>,
344 path: &Path,
345 offset: Option<u64>,
346 endo: G::ScalarField,
348 ) -> Result<Self, String>
349 where
350 OpeningProof::SRS: Default,
351 {
352 let file = File::open(path).map_err(|e| e.to_string())?;
354
355 let mut reader = BufReader::new(file);
357 if let Some(offset) = offset {
358 reader.seek(Start(offset)).map_err(|e| e.to_string())?;
359 }
360
361 let mut verifier_index = Self::deserialize(&mut rmp_serde::Deserializer::new(reader))
363 .map_err(|e| e.to_string())?;
364
365 verifier_index.srs = srs;
367 verifier_index.endo = endo;
368
369 Ok(verifier_index)
370 }
371
372 pub fn to_file(&self, path: &Path, append: Option<bool>) -> Result<(), String> {
382 let append = append.unwrap_or(true);
383 let file = OpenOptions::new()
384 .append(append)
385 .open(path)
386 .map_err(|e| e.to_string())?;
387
388 let writer = BufWriter::new(file);
389
390 self.serialize(&mut rmp_serde::Serializer::new(writer))
391 .map_err(|e| e.to_string())
392 }
393
394 pub fn digest<EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>>(
397 &self,
398 ) -> G::BaseField {
399 let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params());
400 let VerifierIndex {
402 domain: _,
403 max_poly_size: _,
404 zk_rows: _,
405 srs: _,
406 public: _,
407 prev_challenges: _,
408
409 sigma_comm,
411 coefficients_comm,
412 generic_comm,
413 psm_comm,
414 complete_add_comm,
415 mul_comm,
416 emul_comm,
417 endomul_scalar_comm,
418
419 range_check0_comm,
421 range_check1_comm,
422 foreign_field_add_comm,
423 foreign_field_mul_comm,
424 xor_comm,
425 rot_comm,
426
427 lookup_index,
429
430 shift: _,
431 permutation_vanishing_polynomial_m: _,
432 w: _,
433 endo: _,
434
435 linearization: _,
436 powers_of_alpha: _,
437 } = &self;
438
439 for comm in sigma_comm.iter() {
442 absorb_commitment(&mut fq_sponge, comm);
443 }
444 for comm in coefficients_comm.iter() {
445 absorb_commitment(&mut fq_sponge, comm);
446 }
447 absorb_commitment(&mut fq_sponge, generic_comm);
448 absorb_commitment(&mut fq_sponge, psm_comm);
449 absorb_commitment(&mut fq_sponge, complete_add_comm);
450 absorb_commitment(&mut fq_sponge, mul_comm);
451 absorb_commitment(&mut fq_sponge, emul_comm);
452 absorb_commitment(&mut fq_sponge, endomul_scalar_comm);
453
454 if let Some(range_check0_comm) = range_check0_comm {
457 absorb_commitment(&mut fq_sponge, range_check0_comm);
458 }
459
460 if let Some(range_check1_comm) = range_check1_comm {
461 absorb_commitment(&mut fq_sponge, range_check1_comm);
462 }
463
464 if let Some(foreign_field_mul_comm) = foreign_field_mul_comm {
465 absorb_commitment(&mut fq_sponge, foreign_field_mul_comm);
466 }
467
468 if let Some(foreign_field_add_comm) = foreign_field_add_comm {
469 absorb_commitment(&mut fq_sponge, foreign_field_add_comm);
470 }
471
472 if let Some(xor_comm) = xor_comm {
473 absorb_commitment(&mut fq_sponge, xor_comm);
474 }
475
476 if let Some(rot_comm) = rot_comm {
477 absorb_commitment(&mut fq_sponge, rot_comm);
478 }
479
480 if let Some(LookupVerifierIndex {
483 joint_lookup_used: _,
484 lookup_info: _,
485 lookup_table,
486 table_ids,
487 runtime_tables_selector,
488
489 lookup_selectors:
490 LookupSelectors {
491 xor,
492 lookup,
493 range_check,
494 ffmul,
495 },
496 }) = lookup_index
497 {
498 for entry in lookup_table {
499 absorb_commitment(&mut fq_sponge, entry);
500 }
501 if let Some(table_ids) = table_ids {
502 absorb_commitment(&mut fq_sponge, table_ids);
503 }
504 if let Some(runtime_tables_selector) = runtime_tables_selector {
505 absorb_commitment(&mut fq_sponge, runtime_tables_selector);
506 }
507
508 if let Some(xor) = xor {
509 absorb_commitment(&mut fq_sponge, xor);
510 }
511 if let Some(lookup) = lookup {
512 absorb_commitment(&mut fq_sponge, lookup);
513 }
514 if let Some(range_check) = range_check {
515 absorb_commitment(&mut fq_sponge, range_check);
516 }
517 if let Some(ffmul) = ffmul {
518 absorb_commitment(&mut fq_sponge, ffmul);
519 }
520 }
521 fq_sponge.digest_fq()
522 }
523}