1use crate::{
25 commitment::*,
26 storage::Data,
27 utils::{evals_to_polynomial, evals_to_polynomial_and_commitment},
28 Curve, CurveScalarSponge, CurveSponge, ScalarField, Sponge,
29};
30use ark_ff::{Field, One, Zero};
31use ark_poly::{
32 univariate::DensePolynomial, EvaluationDomain, Evaluations, Polynomial,
33 Radix2EvaluationDomain as R2D,
34};
35use kimchi::{circuits::domains::EvaluationDomains, curve::KimchiCurve, plonk_sponge::FrSponge};
36use poly_commitment::{
37 commitment::{combined_inner_product, BatchEvaluationProof, CommitmentCurve, Evaluation},
38 ipa::{OpeningProof, SRS},
39 utils::DensePolynomialOrEvaluations,
40 PolyComm,
41};
42use rand::{CryptoRng, Rng, RngCore};
43use tracing::instrument;
44
45pub struct Query {
48 pub query: Vec<u16>,
49}
50
51pub struct Answer {
53 answer: Vec<ScalarField>,
54}
55
56impl Query {
57 fn to_evals_vector(&self, domain_size: usize) -> Vec<ScalarField> {
58 let mut evals = vec![ScalarField::zero(); domain_size];
59 for i in self.query.iter() {
60 evals[*i as usize] = ScalarField::one();
61 }
62 evals
63 }
64 pub fn to_polynomial(&self, domain: R2D<ScalarField>) -> DensePolynomial<ScalarField> {
65 evals_to_polynomial(self.to_evals_vector(domain.size as usize), domain)
66 }
67 pub fn to_commitment(&self, srs: &SRS<Curve>) -> Curve {
70 let query_evals: Vec<ScalarField> = self.query.iter().map(|_| ScalarField::one()).collect();
71 let indexes: Vec<u64> = self.query.iter().map(|i| *i as u64).collect();
72 commit_sparse(srs, &query_evals, &indexes)
73 }
74 pub fn to_answer(&self, data: &Data<ScalarField>) -> Answer {
75 Answer {
76 answer: self.query.iter().map(|i| data.data[*i as usize]).collect(),
77 }
78 }
79 fn to_answer_evals(&self, data: &[ScalarField], domain_size: usize) -> Vec<ScalarField> {
80 let mut evals = vec![ScalarField::zero(); domain_size];
81 for i in self.query.iter() {
82 evals[*i as usize] = data[*i as usize];
83 }
84 evals
85 }
86 pub fn random(frequency: f64, srs_size: usize) -> Query {
89 let mut query = vec![];
90 (0..srs_size).for_each(|i| {
91 if rand::thread_rng().gen::<f64>() < frequency {
92 query.push(i as u16)
93 }
94 });
95 Query { query }
96 }
97}
98
99impl Answer {
100 fn to_commitment(&self, query: &Query, srs: &SRS<Curve>) -> Curve {
101 let indexes: Vec<u64> = query.query.iter().map(|i| *i as u64).collect();
102 commit_sparse(srs, &self.answer, &indexes)
103 }
104}
105
106#[derive(Debug, Clone)]
108pub struct ReadProof {
110 pub answer_comm: Curve,
112 pub quotient_comm: Curve,
114
115 pub data_eval: ScalarField,
117 pub query_eval: ScalarField,
119 pub answer_eval: ScalarField,
121
122 pub opening_proof: OpeningProof<Curve>,
124}
125
126#[instrument(skip_all, level = "debug")]
127pub fn prove<RNG>(
128 srs: &SRS<Curve>,
129 domain: EvaluationDomains<ScalarField>,
130 group_map: &<Curve as CommitmentCurve>::Map,
131 rng: &mut RNG,
132 data: &Data<ScalarField>,
134 query: &Query,
136 data_comm: &Commitment<Curve>,
138 query_comm: &Curve,
140) -> ReadProof
141where
142 RNG: RngCore + CryptoRng,
143{
144 let data = &data.data;
145
146 let mut curve_sponge = CurveSponge::new(Curve::other_curve_sponge_params());
147
148 let data_poly = evals_to_polynomial(data.to_vec(), domain.d1);
149
150 let query_poly = query.to_polynomial(domain.d1);
151
152 let (answer_poly, answer_comm) = {
153 let answer = query.to_answer_evals(data, domain.d1.size());
154 evals_to_polynomial_and_commitment(answer, domain.d1, srs)
155 };
156
157 curve_sponge.absorb_g(&[data_comm.cm, *query_comm, answer_comm]);
158
159 let quotient_poly: DensePolynomial<ScalarField> = {
162 let data_d2 = data_poly.evaluate_over_domain_by_ref(domain.d2);
163 let query_d2 = query_poly.evaluate_over_domain_by_ref(domain.d2);
164 let answer_d2 = answer_poly.evaluate_over_domain_by_ref(domain.d2);
165
166 let numerator_eval: Evaluations<ScalarField, R2D<ScalarField>> =
168 &(&data_d2 * &query_d2) - &answer_d2;
169
170 let numerator_eval_interpolated = numerator_eval.interpolate();
171
172 let (quotient, res) = numerator_eval_interpolated.divide_by_vanishing_poly(domain.d1);
175 if !res.is_zero() {
179 panic!("Division by vanishing polynomial gave a non-zero remainder.");
180 }
181
182 quotient
183 };
184
185 let quotient_comm = commit_poly(srs, "ient_poly);
188 curve_sponge.absorb_g(&[quotient_comm]);
189
190 let evaluation_point = curve_sponge.challenge();
192
193 let mut scalar_sponge = CurveScalarSponge::new(Curve::sponge_params());
195 scalar_sponge.absorb(&curve_sponge.clone().digest());
196
197 let data_eval = data_poly.evaluate(&evaluation_point);
198 let query_eval = query_poly.evaluate(&evaluation_point);
199 let answer_eval = answer_poly.evaluate(&evaluation_point);
200 let quotient_eval = quotient_poly.evaluate(&evaluation_point);
201
202 for eval in [data_eval, query_eval, answer_eval, quotient_eval].into_iter() {
203 scalar_sponge.absorb(&eval);
204 }
205
206 let (_, endo_r) = Curve::endos();
207 let polyscale = scalar_sponge.challenge().to_field(endo_r);
209 let evalscale = scalar_sponge.challenge().to_field(endo_r);
210
211 let opening_proof_inputs: Vec<_> = {
214 let coefficients_form =
215 DensePolynomialOrEvaluations::<_, R2D<ScalarField>>::DensePolynomial;
216 let non_hiding = |n_chunks| PolyComm {
217 chunks: vec![ScalarField::zero(); n_chunks],
218 };
219
220 vec![
221 (coefficients_form(&data_poly), non_hiding(1)),
222 (coefficients_form(&query_poly), non_hiding(1)),
223 (coefficients_form(&answer_poly), non_hiding(1)),
224 (coefficients_form("ient_poly), non_hiding(1)),
225 ]
226 };
227
228 let opening_proof = srs.open(
229 group_map,
230 opening_proof_inputs.as_slice(),
231 &[evaluation_point],
232 polyscale,
233 evalscale,
234 curve_sponge,
235 rng,
236 );
237
238 ReadProof {
239 answer_comm,
240 quotient_comm,
241 data_eval,
242 query_eval,
243 answer_eval,
244 opening_proof,
245 }
246}
247
248pub fn verify<RNG>(
249 srs: &SRS<Curve>,
250 domain: EvaluationDomains<ScalarField>,
251 group_map: &<Curve as CommitmentCurve>::Map,
252 rng: &mut RNG,
253 data_comm: &Commitment<Curve>,
255 query_comm: &Curve,
257 proof: &ReadProof,
258) -> bool
259where
260 RNG: RngCore + CryptoRng,
261{
262 let mut curve_sponge = CurveSponge::new(Curve::other_curve_sponge_params());
263 curve_sponge.absorb_g(&[
264 data_comm.cm,
265 *query_comm,
266 proof.answer_comm,
267 proof.quotient_comm,
268 ]);
269
270 let evaluation_point = curve_sponge.challenge();
271
272 let mut scalar_sponge = CurveScalarSponge::new(Curve::sponge_params());
273 scalar_sponge.absorb(&curve_sponge.clone().digest());
274
275 let vanishing_poly_at_zeta = domain.d1.vanishing_polynomial().evaluate(&evaluation_point);
276 let quotient_eval = {
277 (proof.data_eval * proof.query_eval - proof.answer_eval)
278 * vanishing_poly_at_zeta
279 .inverse()
280 .unwrap_or_else(|| panic!("Inverse fails only with negligible probability"))
281 };
282
283 for eval in [
284 proof.data_eval,
285 proof.query_eval,
286 proof.answer_eval,
287 quotient_eval,
288 ]
289 .into_iter()
290 {
291 scalar_sponge.absorb(&eval);
292 }
293
294 let (_, endo_r) = Curve::endos();
295 let polyscale = scalar_sponge.challenge().to_field(endo_r);
297 let evalscale = scalar_sponge.challenge().to_field(endo_r);
298
299 let coms_and_evaluations = vec![
300 Evaluation {
301 commitment: PolyComm {
302 chunks: vec![data_comm.cm],
303 },
304 evaluations: vec![vec![proof.data_eval]],
305 },
306 Evaluation {
307 commitment: PolyComm {
308 chunks: vec![*query_comm],
309 },
310 evaluations: vec![vec![proof.query_eval]],
311 },
312 Evaluation {
313 commitment: PolyComm {
314 chunks: vec![proof.answer_comm],
315 },
316 evaluations: vec![vec![proof.answer_eval]],
317 },
318 Evaluation {
319 commitment: PolyComm {
320 chunks: vec![proof.quotient_comm],
321 },
322 evaluations: vec![vec![quotient_eval]],
323 },
324 ];
325 let combined_inner_product = {
326 let evaluations: Vec<_> = coms_and_evaluations
327 .iter()
328 .map(|Evaluation { evaluations, .. }| evaluations.clone())
329 .collect();
330
331 combined_inner_product(&polyscale, &evalscale, evaluations.as_slice())
332 };
333
334 srs.verify(
335 group_map,
336 &mut [BatchEvaluationProof {
337 sponge: curve_sponge,
338 evaluation_points: vec![evaluation_point],
339 polyscale,
340 evalscale,
341 evaluations: coms_and_evaluations,
342 opening: &proof.opening_proof,
343 combined_inner_product,
344 }],
345 rng,
346 )
347}
348
349pub fn verify_answer(srs: &SRS<Curve>, query: &Query, answer: &Answer, proof: &ReadProof) -> bool {
353 let answer_comm = answer.to_commitment(query, srs);
354 answer_comm == proof.answer_comm
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360 use crate::{Curve, ScalarField, SRS_SIZE};
361 use ark_ec::AffineRepr;
362 use ark_ff::{One, UniformRand};
363 use kimchi::{circuits::domains::EvaluationDomains, groupmap::GroupMap};
364 use poly_commitment::{commitment::CommitmentCurve, SRS as _};
365
366 #[test]
367 fn test_read_proof_completeness_soundness() {
368 let mut rng = o1_utils::tests::make_test_rng(None);
369
370 let srs = poly_commitment::precomputed_srs::get_srs_test();
371 let group_map = <Curve as CommitmentCurve>::Map::setup();
372 let domain: EvaluationDomains<ScalarField> = EvaluationDomains::create(srs.size()).unwrap();
373
374 let data = {
375 let mut data = vec![];
376 (0..SRS_SIZE).for_each(|_| data.push(ScalarField::rand(&mut rng)));
377 Data { data }
378 };
379
380 let data_comm = data.to_commitment(&srs);
381
382 let query = Query::random(0.1, SRS_SIZE);
383
384 let query_comm = { commit_poly(&srs, &query.to_polynomial(domain.d1)) };
385
386 let query_comm_sparse = query.to_commitment(&srs);
387
388 assert!(
389 query_comm == query_comm_sparse,
390 "Query commitment: commitment should be the same whatever the computation method is."
391 );
392
393 let proof = prove(
394 &srs,
395 domain,
396 &group_map,
397 &mut rng,
398 &data,
399 &query,
400 &data_comm,
401 &query_comm,
402 );
403 let res = verify(
404 &srs,
405 domain,
406 &group_map,
407 &mut rng,
408 &data_comm,
409 &query_comm,
410 &proof,
411 );
412
413 assert!(res, "Completeness: Proof must verify");
414
415 let proof_malformed_1 = ReadProof {
416 answer_comm: Curve::zero(),
417 ..proof.clone()
418 };
419
420 let res_1 = verify(
421 &srs,
422 domain,
423 &group_map,
424 &mut rng,
425 &data_comm,
426 &query_comm,
427 &proof_malformed_1,
428 );
429
430 assert!(!res_1, "Soundness: Malformed proof #1 must NOT verify");
431
432 let proof_malformed_2 = ReadProof {
433 query_eval: ScalarField::one(),
434 ..proof.clone()
435 };
436
437 let res_2 = verify(
438 &srs,
439 domain,
440 &group_map,
441 &mut rng,
442 &data_comm,
443 &query_comm,
444 &proof_malformed_2,
445 );
446
447 assert!(!res_2, "Soundness: Malformed proof #2 must NOT verify");
448
449 let mut wrong_query = query.query.clone();
450 wrong_query.truncate(query.query.len() - 2);
451
452 let proof_for_wrong_query = prove(
453 &srs,
454 domain,
455 &group_map,
456 &mut rng,
457 &data,
458 &Query { query: wrong_query },
459 &data_comm,
460 &query_comm,
461 );
462 let res_3 = verify(
463 &srs,
464 domain,
465 &group_map,
466 &mut rng,
467 &data_comm,
468 &query_comm,
469 &proof_for_wrong_query,
470 );
471
472 assert!(!res_3, "Soundness: Truncated query must NOT verify");
473
474 let mut answer = query.to_answer(&data);
475
476 let res_4 = verify_answer(&srs, &query, &answer, &proof);
477
478 assert!(res_4, "Completeness: Answer must be consistent with proof");
479
480 answer.answer[0] = ScalarField::one();
481
482 let res_5 = verify_answer(&srs, &query, &answer, &proof);
483
484 assert!(!res_5, "Soundness: Wrong answer must NOT verify");
485 }
486}