1use ark_ec::AffineRepr;
2use ark_ff::{Field, One, PrimeField, Zero};
3use rand::thread_rng;
4
5use kimchi::{
6 circuits::{
7 berkeley_columns::BerkeleyChallenges,
8 domains::EvaluationDomains,
9 expr::{ColumnEvaluations, Constants, Expr, ExprError, PolishToken},
10 gate::CurrOrNext,
11 },
12 curve::KimchiCurve,
13 groupmap::GroupMap,
14 plonk_sponge::FrSponge,
15 proof::PointEvaluations,
16};
17use mina_poseidon::{sponge::ScalarChallenge, FqSponge};
18use poly_commitment::{
19 commitment::{
20 absorb_commitment, combined_inner_product, BatchEvaluationProof, Evaluation, PolyComm,
21 },
22 ipa::OpeningProof,
23 OpenProof,
24};
25
26use super::{
27 column_env::{get_all_columns, RelationColumnType},
28 proof::{Proof, WitnessColumns},
29};
30use crate::{interpreters::mips::column::N_MIPS_SEL_COLS, E};
31use kimchi_msm::columns::Column;
32
33type CommitmentColumns<G> = WitnessColumns<PolyComm<G>, [PolyComm<G>; N_MIPS_SEL_COLS]>;
34type EvaluationColumns<F> = WitnessColumns<F, [F; N_MIPS_SEL_COLS]>;
35
36struct ColumnEval<'a, G: AffineRepr> {
37 commitment: &'a CommitmentColumns<G>,
38 zeta_eval: &'a EvaluationColumns<G::ScalarField>,
39 zeta_omega_eval: &'a EvaluationColumns<G::ScalarField>,
40}
41
42impl<G: AffineRepr> ColumnEvaluations<G::ScalarField> for ColumnEval<'_, G> {
43 type Column = Column<RelationColumnType>;
44 fn evaluate(
45 &self,
46 col: Self::Column,
47 ) -> Result<PointEvaluations<G::ScalarField>, ExprError<Self::Column>> {
48 let ColumnEval {
49 commitment: _,
50 zeta_eval,
51 zeta_omega_eval,
52 } = self;
53 if let Some(&zeta) = zeta_eval.get_column(&col) {
54 if let Some(&zeta_omega) = zeta_omega_eval.get_column(&col) {
55 Ok(PointEvaluations { zeta, zeta_omega })
56 } else {
57 Err(ExprError::MissingEvaluation(col, CurrOrNext::Next))
58 }
59 } else {
60 Err(ExprError::MissingEvaluation(col, CurrOrNext::Curr))
61 }
62 }
63}
64
65pub fn verify<
66 G: KimchiCurve,
67 EFqSponge: Clone + FqSponge<G::BaseField, G, G::ScalarField>,
68 EFrSponge: FrSponge<G::ScalarField>,
69>(
70 domain: EvaluationDomains<G::ScalarField>,
71 srs: &<OpeningProof<G> as OpenProof<G>>::SRS,
72 constraints: &[E<G::ScalarField>],
73 proof: &Proof<G>,
74) -> bool
75where
76 <G as AffineRepr>::BaseField: PrimeField,
77{
78 let Proof {
79 commitments,
80 zeta_evaluations,
81 zeta_omega_evaluations,
82 quotient_commitment,
83 quotient_evaluations,
84 opening_proof,
85 } = proof;
86
87 let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params());
96 for comm in commitments.scratch.iter() {
97 absorb_commitment(&mut fq_sponge, comm)
98 }
99 for comm in commitments.scratch_inverse.iter() {
100 absorb_commitment(&mut fq_sponge, comm)
101 }
102 for comm in commitments.lookup_state.iter() {
103 absorb_commitment(&mut fq_sponge, comm)
104 }
105 absorb_commitment(&mut fq_sponge, &commitments.instruction_counter);
106 absorb_commitment(&mut fq_sponge, &commitments.error);
107 for comm in commitments.selector.iter() {
108 absorb_commitment(&mut fq_sponge, comm)
109 }
110
111 let alpha = fq_sponge.challenge();
113
114 absorb_commitment(&mut fq_sponge, quotient_commitment);
119
120 let zeta_chal = ScalarChallenge(fq_sponge.challenge());
122 let (_, endo_r) = G::endos();
123 let zeta = zeta_chal.to_field(endo_r);
124 let omega = domain.d1.group_gen;
125 let zeta_omega = zeta * omega;
126
127 let column_eval = ColumnEval {
128 commitment: commitments,
129 zeta_eval: zeta_evaluations,
130 zeta_omega_eval: zeta_omega_evaluations,
131 };
132
133 let fq_sponge_before_commitments_and_evaluations = fq_sponge.clone();
135 let mut fr_sponge = EFrSponge::new(G::sponge_params());
136 fr_sponge.absorb(&fq_sponge.digest());
137
138 for (zeta_eval, zeta_omega_eval) in zeta_evaluations
139 .scratch
140 .iter()
141 .zip(zeta_omega_evaluations.scratch.iter())
142 {
143 fr_sponge.absorb(zeta_eval);
144 fr_sponge.absorb(zeta_omega_eval);
145 }
146 for (zeta_eval, zeta_omega_eval) in zeta_evaluations
147 .scratch_inverse
148 .iter()
149 .zip(zeta_omega_evaluations.scratch_inverse.iter())
150 {
151 fr_sponge.absorb(zeta_eval);
152 fr_sponge.absorb(zeta_omega_eval);
153 }
154 for (zeta_eval, zeta_omega_eval) in zeta_evaluations
155 .lookup_state
156 .iter()
157 .zip(zeta_omega_evaluations.lookup_state.iter())
158 {
159 fr_sponge.absorb(zeta_eval);
160 fr_sponge.absorb(zeta_omega_eval);
161 }
162 fr_sponge.absorb(&zeta_evaluations.instruction_counter);
163 fr_sponge.absorb(&zeta_omega_evaluations.instruction_counter);
164 fr_sponge.absorb(&zeta_evaluations.error);
165 fr_sponge.absorb(&zeta_omega_evaluations.error);
166 for (zeta_eval, zeta_omega_eval) in zeta_evaluations
167 .selector
168 .iter()
169 .zip(zeta_omega_evaluations.selector.iter())
170 {
171 fr_sponge.absorb(zeta_eval);
172 fr_sponge.absorb(zeta_omega_eval);
173 }
174 for (quotient_zeta_eval, quotient_zeta_omega_eval) in quotient_evaluations
175 .zeta
176 .iter()
177 .zip(quotient_evaluations.zeta_omega.iter())
178 {
179 fr_sponge.absorb(quotient_zeta_eval);
180 fr_sponge.absorb(quotient_zeta_omega_eval);
181 }
182
183 let challenges = BerkeleyChallenges {
185 alpha,
186 beta: G::ScalarField::zero(),
188 gamma: G::ScalarField::zero(),
189 joint_combiner: G::ScalarField::zero(),
191 };
192 let (_, endo_r) = G::endos();
193
194 let constants = Constants {
195 endo_coefficient: *endo_r,
196 mds: &G::sponge_params().mds,
197 zk_rows: 0,
198 };
199
200 let combined_expr =
201 Expr::combine_constraints(0..(constraints.len() as u32), constraints.to_vec());
202
203 let numerator_zeta = PolishToken::evaluate(
204 combined_expr.to_polish().as_slice(),
205 domain.d1,
206 zeta,
207 &column_eval,
208 &constants,
209 &challenges,
210 )
211 .unwrap_or_else(|_| panic!("Could not evaluate quotient polynomial at zeta"));
212
213 let v_chal = fr_sponge.challenge();
214 let v = v_chal.to_field(endo_r);
215 let u_chal = fr_sponge.challenge();
216 let u = u_chal.to_field(endo_r);
217
218 let mut evaluations: Vec<_> = get_all_columns(column_eval.commitment.lookup_state.len())
219 .into_iter()
220 .map(|column| {
221 let commitment = column_eval
222 .commitment
223 .get_column(&column)
224 .unwrap_or_else(|| panic!("Could not get `commitment` for `Evaluation`"))
225 .clone();
226
227 let evaluations = column_eval
228 .evaluate(column)
229 .unwrap_or_else(|_| panic!("Could not get `evaluations` for `Evaluation`"));
230
231 Evaluation {
232 commitment,
233 evaluations: vec![vec![evaluations.zeta], vec![evaluations.zeta_omega]],
234 }
235 })
236 .collect();
237
238 evaluations.push(Evaluation {
239 commitment: proof.quotient_commitment.clone(),
240 evaluations: vec![
241 quotient_evaluations.zeta.clone(),
242 quotient_evaluations.zeta_omega.clone(),
243 ],
244 });
245
246 let combined_inner_product = {
247 let es: Vec<_> = evaluations
248 .iter()
249 .map(|Evaluation { evaluations, .. }| evaluations.clone())
250 .collect();
251
252 combined_inner_product(&v, &u, es.as_slice())
253 };
254
255 let batch = BatchEvaluationProof {
256 sponge: fq_sponge_before_commitments_and_evaluations,
257 evaluations,
258 evaluation_points: vec![zeta, zeta_omega],
259 polyscale: v,
260 evalscale: u,
261 opening: opening_proof,
262 combined_inner_product,
263 };
264
265 let group_map = G::Map::setup();
266
267 let (quotient_zeta, _) = quotient_evaluations.zeta.iter().fold(
269 (G::ScalarField::zero(), G::ScalarField::one()),
270 |(res, zeta_i_n), chunk| {
271 let res = res + zeta_i_n * chunk;
272 let zeta_i_n = zeta_i_n * zeta.pow([domain.d1.size]);
273 (res, zeta_i_n)
274 },
275 );
276 (quotient_zeta == numerator_zeta / (zeta.pow([domain.d1.size]) - G::ScalarField::one()))
277 && OpeningProof::verify(srs, &group_map, &mut [batch], &mut thread_rng())
278}