1use std::array;
2
3use ark_ff::{One, PrimeField, Zero};
4use ark_poly::{univariate::DensePolynomial, Evaluations, Polynomial, Radix2EvaluationDomain as D};
5use kimchi::{
6 circuits::{
7 berkeley_columns::BerkeleyChallenges,
8 domains::EvaluationDomains,
9 expr::{l0_1, Constants},
10 },
11 curve::KimchiCurve,
12 groupmap::GroupMap,
13 plonk_sponge::FrSponge,
14 proof::PointEvaluations,
15};
16use log::debug;
17use mina_poseidon::{sponge::ScalarChallenge, FqSponge};
18use o1_utils::ExtendedDensePolynomial;
19use poly_commitment::{
20 commitment::{absorb_commitment, PolyComm},
21 ipa::{OpeningProof, SRS},
22 utils::DensePolynomialOrEvaluations,
23 OpenProof as _, SRS as _,
24};
25use rand::{CryptoRng, RngCore};
26use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
27
28use super::{
29 column_env::ColumnEnvironment,
30 proof::{Proof, ProofInputs, WitnessColumns},
31 DEGREE_QUOTIENT_POLYNOMIAL,
32};
33use crate::{interpreters::mips::column::N_MIPS_SEL_COLS, E};
34use thiserror::Error;
35
36#[derive(Error, Debug, Clone)]
38pub enum ProverError {
39 #[error("the provided constraint has degree {0} > allowed {1}; expr: {2}")]
40 ConstraintDegreeTooHigh(u64, u64, String),
41}
42
43pub fn prove<
56 G: KimchiCurve,
57 EFqSponge: FqSponge<G::BaseField, G, G::ScalarField> + Clone,
58 EFrSponge: FrSponge<G::ScalarField>,
59 RNG,
60>(
61 domain: EvaluationDomains<G::ScalarField>,
62 srs: &SRS<G>,
63 inputs: ProofInputs<G>,
64 constraints: &[E<G::ScalarField>],
65 rng: &mut RNG,
66) -> Result<Proof<G>, ProverError>
67where
68 G::BaseField: PrimeField,
69 RNG: RngCore + CryptoRng,
70{
71 let num_chunks = 1;
72 let omega = domain.d1.group_gen;
73
74 let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params());
75
76 debug!("Prover: interpolating all columns, including the selectors");
81 let ProofInputs { evaluations } = inputs;
82 let polys: WitnessColumns<
83 DensePolynomial<G::ScalarField>,
84 [DensePolynomial<G::ScalarField>; N_MIPS_SEL_COLS],
85 > = {
86 let WitnessColumns {
87 scratch,
88 scratch_inverse,
89 lookup_state,
90 instruction_counter,
91 error,
92 selector,
93 } = evaluations;
94
95 let domain_size = domain.d1.size as usize;
96
97 let selector: [Vec<G::ScalarField>; N_MIPS_SEL_COLS] = array::from_fn(|i| {
99 let mut s_i = Vec::with_capacity(domain_size);
100 for s in &selector {
101 s_i.push(if G::ScalarField::from(i as u64) == *s {
102 G::ScalarField::one()
103 } else {
104 G::ScalarField::zero()
105 })
106 }
107 s_i
108 });
109
110 let eval_col = |evals: Vec<G::ScalarField>| {
111 Evaluations::<G::ScalarField, D<G::ScalarField>>::from_vec_and_domain(evals, domain.d1)
112 .interpolate()
113 };
114 let scratch = scratch.into_par_iter().map(eval_col).collect::<Vec<_>>();
116 let scratch_inverse = scratch_inverse
117 .into_par_iter()
118 .map(|mut evals| {
119 ark_ff::batch_inversion(&mut evals);
120 eval_col(evals)
121 })
122 .collect::<Vec<_>>();
123 let lookup_state = lookup_state
124 .into_par_iter()
125 .map(eval_col)
126 .collect::<Vec<_>>();
127 let selector = selector.into_par_iter().map(eval_col).collect::<Vec<_>>();
128 WitnessColumns {
129 scratch: scratch.try_into().unwrap(),
130 scratch_inverse: scratch_inverse.try_into().unwrap(),
131 lookup_state,
132 instruction_counter: eval_col(instruction_counter),
133 error: eval_col(error.clone()),
134 selector: selector.try_into().unwrap(),
135 }
136 };
137
138 debug!("Prover: committing to all columns, including the selectors");
139 let commitments: WitnessColumns<PolyComm<G>, [PolyComm<G>; N_MIPS_SEL_COLS]> = {
140 let WitnessColumns {
141 scratch,
142 scratch_inverse,
143 lookup_state,
144 instruction_counter,
145 error,
146 selector,
147 } = &polys;
148
149 let comm = |poly: &DensePolynomial<G::ScalarField>| {
150 srs.commit_custom(
151 poly,
152 num_chunks,
153 &PolyComm::new(vec![G::ScalarField::one()]),
154 )
155 .unwrap()
156 .commitment
157 };
158 let scratch = scratch.par_iter().map(comm).collect::<Vec<_>>();
160 let scratch_inverse = scratch_inverse.par_iter().map(comm).collect::<Vec<_>>();
161 let lookup_state = lookup_state.par_iter().map(comm).collect::<Vec<_>>();
162 let selector = selector.par_iter().map(comm).collect::<Vec<_>>();
163 WitnessColumns {
164 scratch: scratch.try_into().unwrap(),
165 scratch_inverse: scratch_inverse.try_into().unwrap(),
166 lookup_state,
167 instruction_counter: comm(instruction_counter),
168 error: comm(error),
169 selector: selector.try_into().unwrap(),
170 }
171 };
172
173 debug!("Prover: evaluating all columns, including the selectors, on d8");
174 let evaluations_d8 = {
179 let WitnessColumns {
180 scratch,
181 scratch_inverse,
182 lookup_state,
183 instruction_counter,
184 error,
185 selector,
186 } = &polys;
187 let eval_d8 =
188 |poly: &DensePolynomial<G::ScalarField>| poly.evaluate_over_domain_by_ref(domain.d8);
189 let scratch = scratch.into_par_iter().map(eval_d8).collect::<Vec<_>>();
191 let scratch_inverse = scratch_inverse
192 .into_par_iter()
193 .map(eval_d8)
194 .collect::<Vec<_>>();
195 let lookup_state = lookup_state
196 .into_par_iter()
197 .map(eval_d8)
198 .collect::<Vec<_>>();
199 let selector = selector.into_par_iter().map(eval_d8).collect::<Vec<_>>();
200 WitnessColumns {
201 scratch: scratch.try_into().unwrap(),
202 scratch_inverse: scratch_inverse.try_into().unwrap(),
203 lookup_state,
204 instruction_counter: eval_d8(instruction_counter),
205 error: eval_d8(error),
206 selector: selector.try_into().unwrap(),
207 }
208 };
209
210 for comm in commitments.scratch.iter() {
213 absorb_commitment(&mut fq_sponge, comm)
214 }
215 for comm in commitments.scratch_inverse.iter() {
216 absorb_commitment(&mut fq_sponge, comm)
217 }
218 for comm in commitments.lookup_state.iter() {
219 absorb_commitment(&mut fq_sponge, comm)
220 }
221 absorb_commitment(&mut fq_sponge, &commitments.instruction_counter);
222 absorb_commitment(&mut fq_sponge, &commitments.error);
223 for comm in commitments.selector.iter() {
224 absorb_commitment(&mut fq_sponge, comm)
225 }
226
227 let (_, endo_r) = G::endos();
232
233 let alpha: G::ScalarField = fq_sponge.challenge();
235
236 let zk_rows = 0;
237 let column_env: ColumnEnvironment<'_, G::ScalarField> = {
238 let challenges = BerkeleyChallenges {
240 alpha,
241 beta: G::ScalarField::zero(),
243 gamma: G::ScalarField::zero(),
244 joint_combiner: G::ScalarField::zero(),
246 };
247 ColumnEnvironment {
248 constants: Constants {
249 endo_coefficient: *endo_r,
250 mds: &G::sponge_params().mds,
251 zk_rows,
252 },
253 challenges,
254 witness: &evaluations_d8,
255 l0_1: l0_1(domain.d1),
256 domain,
257 }
258 };
259
260 debug!("Prover: computing the quotient polynomial");
261 let quotient_poly: DensePolynomial<G::ScalarField> = {
269 let combined_expr =
271 E::combine_constraints(0..(constraints.len() as u32), (constraints).to_vec());
272
273 let expr_evaluation: Evaluations<G::ScalarField, D<G::ScalarField>> =
286 combined_expr.evaluations(&column_env);
287
288 let expr_evaluation_interpolated = expr_evaluation.interpolate();
290
291 let fail_remainder_not_zero =
292 || panic!("The constraints are not satisfied since the remainder is not zero");
293 let (quotient, rem) = expr_evaluation_interpolated.divide_by_vanishing_poly(domain.d1);
296 if !rem.is_zero() {
300 fail_remainder_not_zero();
301 }
302
303 quotient
304 };
305
306 let quotient_commitment = srs
307 .commit_custom(
308 "ient_poly,
309 DEGREE_QUOTIENT_POLYNOMIAL as usize,
310 &PolyComm::new(vec![
311 G::ScalarField::one();
312 DEGREE_QUOTIENT_POLYNOMIAL as usize
313 ]),
314 )
315 .unwrap();
316 absorb_commitment(&mut fq_sponge, "ient_commitment.commitment);
317
318 debug!("Prover: evaluating all columns, including the selectors, at ζ and ζω");
323 let zeta_chal = ScalarChallenge(fq_sponge.challenge());
324
325 let zeta = zeta_chal.to_field(endo_r);
326 let zeta_omega = zeta * omega;
327
328 let evals = |point| {
329 let WitnessColumns {
330 scratch,
331 scratch_inverse,
332 lookup_state,
333 instruction_counter,
334 error,
335 selector,
336 } = &polys;
337 let eval = |poly: &DensePolynomial<G::ScalarField>| poly.evaluate(point);
338 let scratch = scratch.par_iter().map(eval).collect::<Vec<_>>();
339 let scratch_inverse = scratch_inverse.par_iter().map(eval).collect::<Vec<_>>();
340 let lookup_state = lookup_state.par_iter().map(eval).collect::<Vec<_>>();
341 let selector = selector.par_iter().map(eval).collect::<Vec<_>>();
342 WitnessColumns {
343 scratch: scratch.try_into().unwrap(),
344 scratch_inverse: scratch_inverse.try_into().unwrap(),
345 lookup_state,
346 instruction_counter: eval(instruction_counter),
347 error: eval(error),
348 selector: selector.try_into().unwrap(),
349 }
350 };
351 let zeta_evaluations: WitnessColumns<G::ScalarField, [G::ScalarField; N_MIPS_SEL_COLS]> =
353 evals(&zeta);
354
355 let zeta_omega_evaluations: WitnessColumns<G::ScalarField, [G::ScalarField; N_MIPS_SEL_COLS]> =
357 evals(&zeta_omega);
358
359 let chunked_quotient = quotient_poly
360 .to_chunked_polynomial(DEGREE_QUOTIENT_POLYNOMIAL as usize, domain.d1.size as usize);
361 let quotient_evaluations = PointEvaluations {
362 zeta: chunked_quotient
363 .polys
364 .iter()
365 .map(|p| p.evaluate(&zeta))
366 .collect::<Vec<_>>(),
367 zeta_omega: chunked_quotient
368 .polys
369 .iter()
370 .map(|p| p.evaluate(&zeta_omega))
371 .collect(),
372 };
373
374 let fq_sponge_before_evaluations = fq_sponge.clone();
377 let mut fr_sponge = EFrSponge::new(G::sponge_params());
378 fr_sponge.absorb(&fq_sponge.digest());
379
380 for (zeta_eval, zeta_omega_eval) in zeta_evaluations
381 .scratch
382 .iter()
383 .zip(zeta_omega_evaluations.scratch.iter())
384 {
385 fr_sponge.absorb(zeta_eval);
386 fr_sponge.absorb(zeta_omega_eval);
387 }
388 for (zeta_eval, zeta_omega_eval) in zeta_evaluations
389 .scratch_inverse
390 .iter()
391 .zip(zeta_omega_evaluations.scratch_inverse.iter())
392 {
393 fr_sponge.absorb(zeta_eval);
394 fr_sponge.absorb(zeta_omega_eval);
395 }
396
397 for (zeta_eval, zeta_omega_eval) in zeta_evaluations
398 .lookup_state
399 .iter()
400 .zip(zeta_omega_evaluations.lookup_state.iter())
401 {
402 fr_sponge.absorb(zeta_eval);
403 fr_sponge.absorb(zeta_omega_eval);
404 }
405 fr_sponge.absorb(&zeta_evaluations.instruction_counter);
406 fr_sponge.absorb(&zeta_omega_evaluations.instruction_counter);
407 fr_sponge.absorb(&zeta_evaluations.error);
408 fr_sponge.absorb(&zeta_omega_evaluations.error);
409 for (zeta_eval, zeta_omega_eval) in zeta_evaluations
410 .selector
411 .iter()
412 .zip(zeta_omega_evaluations.selector.iter())
413 {
414 fr_sponge.absorb(zeta_eval);
415 fr_sponge.absorb(zeta_omega_eval);
416 }
417 for (quotient_zeta_eval, quotient_zeta_omega_eval) in quotient_evaluations
418 .zeta
419 .iter()
420 .zip(quotient_evaluations.zeta_omega.iter())
421 {
422 fr_sponge.absorb(quotient_zeta_eval);
423 fr_sponge.absorb(quotient_zeta_omega_eval);
424 }
425 let mut polynomials: Vec<_> = polys.scratch.into_iter().collect();
430 polynomials.extend(polys.scratch_inverse);
431 polynomials.extend(polys.lookup_state);
432 polynomials.push(polys.instruction_counter);
433 polynomials.push(polys.error);
434 polynomials.extend(polys.selector);
435
436 let mut polynomials: Vec<_> = polynomials
438 .iter()
439 .map(|poly| {
440 (
441 DensePolynomialOrEvaluations::DensePolynomial(poly),
442 PolyComm::new(vec![G::ScalarField::one()]),
444 )
445 })
446 .collect();
447 polynomials.push((
450 DensePolynomialOrEvaluations::DensePolynomial("ient_poly),
451 quotient_commitment.blinders,
452 ));
453
454 let v_chal = fr_sponge.challenge();
456 let v = v_chal.to_field(endo_r);
457 let u_chal = fr_sponge.challenge();
459 let u = u_chal.to_field(endo_r);
460
461 let group_map = G::Map::setup();
462
463 debug!("Prover: computing the (batched) opening proof using the IPA PCS");
464 let opening_proof = OpeningProof::open::<_, _, D<G::ScalarField>>(
466 srs,
467 &group_map,
468 polynomials.as_slice(),
469 &[zeta, zeta_omega],
470 v,
471 u,
472 fq_sponge_before_evaluations,
473 rng,
474 );
475
476 Ok(Proof {
477 commitments,
478 zeta_evaluations,
479 zeta_omega_evaluations,
480 quotient_commitment: quotient_commitment.commitment,
481 quotient_evaluations,
482 opening_proof,
483 })
484}