1#![allow(clippy::type_complexity)]
2#![allow(clippy::boxed_local)]
3
4use crate::{
5 expr_eval::SimpleEvalEnv,
6 plonkish_lang::{PlonkishChallenge, PlonkishInstance, PlonkishWitness},
7};
8use ark_ff::{Field, One, Zero};
9use ark_poly::{
10 univariate::DensePolynomial, EvaluationDomain, Evaluations, Polynomial,
11 Radix2EvaluationDomain as R2D,
12};
13use folding::{
14 eval_leaf::EvalLeaf,
15 instance_witness::{ExtendedWitness, RelaxedInstance, RelaxedWitness},
16 Alphas, FoldingCompatibleExpr, FoldingConfig,
17};
18use kimchi::{
19 self,
20 circuits::{
21 domains::EvaluationDomains,
22 expr::{ColumnEvaluations, ExprError},
23 },
24 curve::KimchiCurve,
25 groupmap::GroupMap,
26 plonk_sponge::FrSponge,
27 proof::PointEvaluations,
28};
29use kimchi_msm::{columns::Column as GenericColumn, witness::Witness};
30use mina_poseidon::{sponge::ScalarChallenge, FqSponge};
31use o1_utils::ExtendedDensePolynomial;
32use poly_commitment::{
33 commitment::{absorb_commitment, CommitmentCurve, PolyComm},
34 kzg::{KZGProof, PairingSRS},
35 utils::DensePolynomialOrEvaluations,
36 OpenProof, SRS,
37};
38use rand::{CryptoRng, RngCore};
39use rayon::iter::{IntoParallelIterator, ParallelIterator};
40use std::collections::BTreeMap;
41use thiserror::Error;
42
43#[derive(Error, Debug, Clone)]
45pub enum ProverError {
46 #[error("the proof could not be constructed: {0}")]
47 Generic(&'static str),
48
49 #[error("the provided (witness) constraints was not satisfied: {0}")]
50 ConstraintNotSatisfied(String),
51
52 #[error("the provided (witness) constraint has degree {0} > allowed {1}; expr: {2}")]
53 ConstraintDegreeTooHigh(u64, u64, String),
54}
55
56pub type Pairing = kimchi_msm::BN254;
57pub type G = kimchi_msm::BN254G1Affine;
59pub type Fp = kimchi_msm::Fp;
61pub type Fq = ark_bn254::Fq;
64
65#[derive(Debug, Clone)]
66pub struct ProofEvaluations<
68 const N_WIT: usize,
69 const N_REL: usize,
70 const N_DSEL: usize,
71 const N_FSEL: usize,
72 F,
73> {
74 pub witness_evals: Witness<N_WIT, PointEvaluations<F>>,
76 pub fixed_selectors_evals: Box<[PointEvaluations<F>; N_FSEL]>,
78 pub error_vec: PointEvaluations<F>,
79 pub ft_eval1: F,
81}
82
83impl<
87 const N_WIT: usize,
88 const N_REL: usize,
89 const N_DSEL: usize,
90 const N_FSEL: usize,
91 F: Clone,
92 > ColumnEvaluations<F> for ProofEvaluations<N_WIT, N_REL, N_DSEL, N_FSEL, F>
93{
94 type Column = kimchi_msm::columns::Column<usize>;
95
96 fn evaluate(&self, col: Self::Column) -> Result<PointEvaluations<F>, ExprError<Self::Column>> {
97 assert!(N_WIT == N_REL + N_DSEL);
99 let res = match col {
100 Self::Column::Relation(i) => {
101 assert!(i < N_REL, "Index out of bounds");
102 self.witness_evals[i].clone()
103 }
104 Self::Column::DynamicSelector(i) => {
105 assert!(i < N_DSEL, "Index out of bounds");
106 self.witness_evals[N_REL + i].clone()
107 }
108 Self::Column::FixedSelector(i) => {
109 assert!(i < N_FSEL, "Index out of bounds");
110 self.fixed_selectors_evals[i].clone()
111 }
112 _ => panic!("lookup columns not supported"),
113 };
114 Ok(res)
115 }
116}
117
118#[derive(Debug, Clone)]
119pub struct ProofCommitments<const N_WIT: usize, G: KimchiCurve> {
120 pub witness_comms: Witness<N_WIT, PolyComm<G>>,
123 pub t_comm: PolyComm<G>,
126}
127
128#[derive(Debug, Clone)]
129pub struct Proof<
130 const N_WIT: usize,
131 const N_REL: usize,
132 const N_DSEL: usize,
133 const N_FSEL: usize,
134 G: KimchiCurve,
135 OpeningProof: OpenProof<G>,
136> {
137 pub proof_comms: ProofCommitments<N_WIT, G>,
138 pub proof_evals: ProofEvaluations<N_WIT, N_REL, N_DSEL, N_FSEL, G::ScalarField>,
139 pub opening_proof: OpeningProof,
140
141 pub alphas: Alphas<G::ScalarField>,
143 pub challenges: [G::ScalarField; 3],
144 pub u: G::ScalarField,
145}
146
147pub fn prove<
148 EFqSponge: Clone + FqSponge<Fq, G, Fp>,
149 EFrSponge: FrSponge<Fp>,
150 FC: FoldingConfig<Column = GenericColumn<usize>, Curve = G, Challenge = PlonkishChallenge>,
151 RNG,
152 const N_WIT: usize,
153 const N_WIT_QUAD: usize, const N_REL: usize,
155 const N_DSEL: usize,
156 const N_FSEL: usize,
157 const N_ALPHAS: usize,
158>(
159 domain: EvaluationDomains<Fp>,
160 srs: &PairingSRS<Pairing>,
161 combined_expr: &FoldingCompatibleExpr<FC>,
162 folded_instance: RelaxedInstance<G, PlonkishInstance<G, N_WIT, 3, N_ALPHAS>>,
163 folded_witness: RelaxedWitness<G, PlonkishWitness<N_WIT, N_FSEL, Fp>>,
164 rng: &mut RNG,
165) -> Result<Proof<N_WIT_QUAD, N_WIT_QUAD, N_DSEL, N_FSEL, G, KZGProof<Pairing>>, ProverError>
166where
167 RNG: RngCore + CryptoRng,
168{
169 assert_eq!(
170 folded_witness.extended_witness.extended.values().len(),
171 N_WIT_QUAD - N_WIT
172 );
173 assert!(N_WIT == N_REL + N_DSEL);
174
175 let group_map = <G as CommitmentCurve>::Map::setup();
180
181 let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params());
186
187 let fixed_selectors_evals_d1: Box<[Evaluations<Fp, R2D<Fp>>; N_FSEL]> =
188 folded_witness.extended_witness.witness.fixed_selectors.cols;
189
190 let fixed_selectors_polys: Box<[DensePolynomial<Fp>; N_FSEL]> =
191 o1_utils::array::vec_to_boxed_array(
192 fixed_selectors_evals_d1
193 .clone()
194 .into_par_iter()
195 .map(|evals| evals.interpolate())
196 .collect(),
197 );
198
199 let fixed_selectors_comms: Box<[PolyComm<G>; N_FSEL]> = {
200 let comm = |poly: &DensePolynomial<Fp>| srs.commit_non_hiding(poly, 1);
201 o1_utils::array::vec_to_boxed_array(
202 fixed_selectors_polys
203 .as_ref()
204 .into_par_iter()
205 .map(comm)
206 .collect(),
207 )
208 };
209
210 (fixed_selectors_comms)
212 .into_iter()
213 .for_each(|comm| absorb_commitment(&mut fq_sponge, &comm));
214
215 let witness_main: Witness<N_WIT, _> = folded_witness.extended_witness.witness.witness;
216 let witness_ext: BTreeMap<usize, Evaluations<Fp, R2D<Fp>>> =
217 folded_witness.extended_witness.extended;
218
219 let witness_evals_d1: Witness<N_WIT_QUAD, Evaluations<_, _>> = {
221 let mut acc = witness_main.cols.to_vec();
222 acc.extend(witness_ext.values().cloned());
223 acc.try_into().unwrap()
224 };
225
226 let witness_polys: Witness<N_WIT_QUAD, DensePolynomial<Fp>> = {
227 witness_evals_d1
228 .into_par_iter()
229 .map(|e| e.interpolate())
230 .collect::<Vec<_>>()
231 .try_into()
232 .unwrap()
233 };
234
235 let witness_comms: Witness<N_WIT_QUAD, PolyComm<G>> = {
236 let blinders = PolyComm {
237 chunks: vec![Fp::one()],
238 };
239 let comm = {
240 |poly: &DensePolynomial<Fp>| {
241 let comm = srs.commit_custom(poly, 1, &blinders).unwrap();
243 comm.commitment
244 }
245 };
246 (&witness_polys)
247 .into_par_iter()
248 .map(comm)
249 .collect::<Witness<N_WIT_QUAD, PolyComm<G>>>()
250 };
251
252 (&witness_comms)
254 .into_iter()
255 .for_each(|comm| absorb_commitment(&mut fq_sponge, comm));
256
257 let (_, endo_r) = G::endos();
262
263 let quotient_poly = {
264 let evaluation_domain = domain.d4;
265
266 let enlarge_to_domain_generic =
267 |evaluations: &Evaluations<Fp, R2D<Fp>>, new_domain: R2D<Fp>| {
268 assert!(evaluations.domain() == domain.d1);
269 evaluations
270 .interpolate_by_ref()
271 .evaluate_over_domain_by_ref(new_domain)
272 };
273
274 let enlarge_to_domain = |evaluations: &Evaluations<Fp, R2D<Fp>>| {
275 enlarge_to_domain_generic(evaluations, evaluation_domain)
276 };
277
278 let simple_eval_env: SimpleEvalEnv<G, N_WIT, N_FSEL> = {
279 let ext_witness = ExtendedWitness {
280 witness: PlonkishWitness {
281 witness: (&witness_main)
282 .into_par_iter()
283 .map(enlarge_to_domain)
284 .collect(),
285 fixed_selectors: (&fixed_selectors_evals_d1.to_vec())
286 .into_par_iter()
287 .map(enlarge_to_domain)
288 .collect(),
289 phantom: core::marker::PhantomData,
290 },
291 extended: (&witness_ext)
292 .into_par_iter()
293 .map(|(ix, evals)| (*ix, enlarge_to_domain(evals)))
294 .collect(),
295 };
296
297 SimpleEvalEnv {
298 ext_witness,
299 alphas: folded_instance.extended_instance.instance.alphas.clone(),
300 challenges: folded_instance.extended_instance.instance.challenges,
301 error_vec: enlarge_to_domain(&folded_witness.error_vec),
302 u: folded_instance.u,
303 }
304 };
305
306 {
307 let eval_leaf = simple_eval_env.eval_naive_fcompat(combined_expr);
308
309 let evaluations_big = match eval_leaf {
310 EvalLeaf::Result(evaluations) => evaluations,
311 EvalLeaf::Col(evaluations) => evaluations.to_vec().clone(),
312 _ => panic!("eval_leaf is not Result"),
313 };
314
315 let interpolated =
316 Evaluations::from_vec_and_domain(evaluations_big, evaluation_domain).interpolate();
317 if interpolated.is_zero() {
318 println!("Interpolated expression is zero");
319 }
320
321 let (quotient, remainder) = interpolated
322 .divide_by_vanishing_poly(domain.d1)
323 .unwrap_or_else(|| panic!("ERROR: Cannot divide by vanishing polynomial"));
324 if !remainder.is_zero() {
325 panic!("ERROR: Remainder is not zero for joint folding expression",);
326 }
327
328 quotient
329 }
330 };
331
332 let num_chunks: usize = 3;
334
335 let t_comm = srs.commit_non_hiding("ient_poly, num_chunks);
337
338 absorb_commitment(&mut fq_sponge, &t_comm);
344
345 let zeta_chal = ScalarChallenge(fq_sponge.challenge());
347
348 let zeta = zeta_chal.to_field(endo_r);
349
350 let omega = domain.d1.group_gen;
351 let zeta_omega = zeta * omega;
353
354 let eval_at_challenge = |p: &DensePolynomial<_>| PointEvaluations {
355 zeta: p.evaluate(&zeta),
356 zeta_omega: p.evaluate(&zeta_omega),
357 };
358
359 let witness_point_evals: Witness<N_WIT_QUAD, PointEvaluations<_>> = {
361 (&witness_polys)
362 .into_par_iter()
363 .map(eval_at_challenge)
364 .collect::<Witness<N_WIT_QUAD, PointEvaluations<_>>>()
365 };
366
367 let fixed_selectors_point_evals: Box<[PointEvaluations<_>; N_FSEL]> = {
368 o1_utils::array::vec_to_boxed_array(
369 fixed_selectors_polys
370 .as_ref()
371 .into_par_iter()
372 .map(eval_at_challenge)
373 .collect::<_>(),
374 )
375 };
376
377 let error_vec_point_eval = eval_at_challenge(&folded_witness.error_vec.interpolate());
378
379 let fq_sponge_before_evaluations = fq_sponge.clone();
385 let mut fr_sponge = EFrSponge::new(G::sponge_params());
386 fr_sponge.absorb(&fq_sponge.digest());
387
388 for PointEvaluations { zeta, zeta_omega } in (&witness_point_evals).into_iter() {
389 fr_sponge.absorb(zeta);
390 fr_sponge.absorb(zeta_omega);
391 }
392
393 for PointEvaluations { zeta, zeta_omega } in fixed_selectors_point_evals.as_ref().iter() {
394 fr_sponge.absorb(zeta);
395 fr_sponge.absorb(zeta_omega);
396 }
397
398 let ft: DensePolynomial<Fp> = {
409 let evaluation_point_to_domain_size = zeta.pow([domain.d1.size]);
410 let t_chunked: DensePolynomial<Fp> = quotient_poly
413 .to_chunked_polynomial(num_chunks, domain.d1.size as usize)
414 .linearize(evaluation_point_to_domain_size);
415
416 let minus_vanishing_poly_at_zeta: Fp = -domain.d1.vanishing_polynomial().evaluate(&zeta);
418 t_chunked.scale(minus_vanishing_poly_at_zeta)
421 };
422
423 let ft_eval1 = ft.evaluate(&zeta_omega);
430
431 fr_sponge.absorb(&ft_eval1);
433
434 let v_chal = fr_sponge.challenge();
435 let v = v_chal.to_field(endo_r);
436 let u_chal = fr_sponge.challenge();
437 let u = u_chal.to_field(endo_r);
438
439 let coefficients_form = DensePolynomialOrEvaluations::DensePolynomial;
440 let non_hiding = |n_chunks| PolyComm {
441 chunks: vec![Fp::zero(); n_chunks],
442 };
443 let hiding = |n_chunks| PolyComm {
444 chunks: vec![Fp::one(); n_chunks],
445 };
446
447 let mut polynomials_to_open: Vec<_> = vec![];
449
450 polynomials_to_open.extend(
451 (&witness_polys)
452 .into_par_iter()
453 .map(|poly| (coefficients_form(poly), hiding(1)))
454 .collect::<Vec<_>>(),
455 );
456
457 polynomials_to_open.extend(
460 fixed_selectors_polys
461 .as_ref()
462 .into_par_iter()
463 .map(|poly| (coefficients_form(poly), non_hiding(1)))
464 .collect::<Vec<_>>(),
465 );
466
467 polynomials_to_open.push((coefficients_form(&ft), non_hiding(1)));
468
469 let opening_proof = OpenProof::open::<_, _, R2D<Fp>>(
470 srs,
471 &group_map,
472 polynomials_to_open.as_slice(),
473 &[zeta, zeta_omega],
474 v,
475 u,
476 fq_sponge_before_evaluations,
477 rng,
478 );
479
480 let proof_evals: ProofEvaluations<N_WIT_QUAD, N_WIT_QUAD, N_DSEL, N_FSEL, Fp> = {
481 ProofEvaluations {
482 witness_evals: witness_point_evals,
483 fixed_selectors_evals: fixed_selectors_point_evals,
484 error_vec: error_vec_point_eval,
485 ft_eval1,
486 }
487 };
488
489 Ok(Proof {
490 proof_comms: ProofCommitments {
491 witness_comms,
492 t_comm,
493 },
494 proof_evals,
495 opening_proof,
496 alphas: folded_instance.extended_instance.instance.alphas,
497 challenges: folded_instance.extended_instance.instance.challenges,
498 u: folded_instance.u,
499 })
500}