testudo/src/sumcheck.rs
src/sumcheck.rsRaw
1#![allow(clippy::too_many_arguments)]
2#![allow(clippy::type_complexity)]
3use super::commitments::{Commitments, MultiCommitGens};
4use super::dense_mlpoly::DensePolynomial;
5use super::errors::ProofVerifyError;
6use super::group::{
7 CompressGroupElement, CompressedGroup, DecompressGroupElement, GroupElement,
8 VartimeMultiscalarMul,
9};
10use super::nizk::DotProductProof;
11use super::random::RandomTape;
12use super::scalar::Scalar;
13use super::transcript::{AppendToTranscript, ProofTranscript};
14use super::unipoly::{CompressedUniPoly, UniPoly};
15use ark_ff::{One, Zero};
16use ark_serialize::*;
17use core::iter;
18use itertools::izip;
19use merlin::Transcript;
20
21#[derive(CanonicalSerialize, CanonicalDeserialize, Debug)]
22pub struct SumcheckInstanceProof {
23 compressed_polys: Vec<CompressedUniPoly>,
24}
25
26impl SumcheckInstanceProof {
27 pub fn new(compressed_polys: Vec<CompressedUniPoly>) -> SumcheckInstanceProof {
28 SumcheckInstanceProof { compressed_polys }
29 }
30
31 pub fn verify(
32 &self,
33 claim: Scalar,
34 num_rounds: usize,
35 degree_bound: usize,
36 transcript: &mut Transcript,
37 ) -> Result<(Scalar, Vec<Scalar>), ProofVerifyError> {
38 let mut e = claim;
39 let mut r: Vec<Scalar> = Vec::new();
40
41 // verify that there is a univariate polynomial for each round
42 assert_eq!(self.compressed_polys.len(), num_rounds);
43 for i in 0..self.compressed_polys.len() {
44 let poly = self.compressed_polys[i].decompress(&e);
45
46 // verify degree bound
47 assert_eq!(poly.degree(), degree_bound);
48
49 // check if G_k(0) + G_k(1) = e
50 assert_eq!(poly.eval_at_zero() + poly.eval_at_one(), e);
51
52 // append the prover's message to the transcript
53 poly.append_to_transcript(b"poly", transcript);
54
55 //derive the verifier's challenge for the next round
56 let r_i = transcript.challenge_scalar(b"challenge_nextround");
57
58 r.push(r_i);
59
60 // evaluate the claimed degree-ell polynomial at r_i
61 e = poly.evaluate(&r_i);
62 }
63
64 Ok((e, r))
65 }
66}
67
68#[derive(CanonicalSerialize, CanonicalDeserialize, Debug)]
69pub struct ZKSumcheckInstanceProof {
70 comm_polys: Vec<CompressedGroup>,
71 comm_evals: Vec<CompressedGroup>,
72 proofs: Vec<DotProductProof>,
73}
74
75impl ZKSumcheckInstanceProof {
76 pub fn new(
77 comm_polys: Vec<CompressedGroup>,
78 comm_evals: Vec<CompressedGroup>,
79 proofs: Vec<DotProductProof>,
80 ) -> Self {
81 ZKSumcheckInstanceProof {
82 comm_polys,
83 comm_evals,
84 proofs,
85 }
86 }
87
88 pub fn verify(
89 &self,
90 comm_claim: &CompressedGroup,
91 num_rounds: usize,
92 degree_bound: usize,
93 gens_1: &MultiCommitGens,
94 gens_n: &MultiCommitGens,
95 transcript: &mut Transcript,
96 ) -> Result<(CompressedGroup, Vec<Scalar>), ProofVerifyError> {
97 // verify degree bound
98 assert_eq!(gens_n.n, degree_bound + 1);
99
100 // verify that there is a univariate polynomial for each round
101 assert_eq!(self.comm_polys.len(), num_rounds);
102 assert_eq!(self.comm_evals.len(), num_rounds);
103
104 let mut r: Vec<Scalar> = Vec::new();
105 for i in 0..self.comm_polys.len() {
106 let comm_poly = &self.comm_polys[i];
107
108 // append the prover's polynomial to the transcript
109 comm_poly.append_to_transcript(b"comm_poly", transcript);
110
111 //derive the verifier's challenge for the next round
112 let r_i = transcript.challenge_scalar(b"challenge_nextround");
113
114 // verify the proof of sum-check and evals
115 let res = {
116 let comm_claim_per_round = if i == 0 {
117 comm_claim
118 } else {
119 &self.comm_evals[i - 1]
120 };
121 let mut comm_eval = &self.comm_evals[i];
122
123 // add two claims to transcript
124 comm_claim_per_round.append_to_transcript(b"comm_claim_per_round", transcript);
125 comm_eval.append_to_transcript(b"comm_eval", transcript);
126
127 // produce two weights
128 let w = transcript.challenge_vector(b"combine_two_claims_to_one", 2);
129
130 // compute a weighted sum of the RHS
131 let comm_target = GroupElement::vartime_multiscalar_mul(
132 w.as_slice(),
133 iter::once(&comm_claim_per_round)
134 .chain(iter::once(&comm_eval))
135 .map(|pt| GroupElement::decompress(pt).unwrap())
136 .collect::<Vec<GroupElement>>()
137 .as_slice(),
138 )
139 .compress();
140
141 let a = {
142 // the vector to use to decommit for sum-check test
143 let a_sc = {
144 let mut a = vec![Scalar::one(); degree_bound + 1];
145 a[0] += Scalar::one();
146 a
147 };
148
149 // the vector to use to decommit for evaluation
150 let a_eval = {
151 let mut a = vec![Scalar::one(); degree_bound + 1];
152 for j in 1..a.len() {
153 a[j] = a[j - 1] * r_i;
154 }
155 a
156 };
157
158 // take weighted sum of the two vectors using w
159 assert_eq!(a_sc.len(), a_eval.len());
160 (0..a_sc.len())
161 .map(|i| w[0] * a_sc[i] + w[1] * a_eval[i])
162 .collect::<Vec<Scalar>>()
163 };
164
165 self.proofs[i]
166 .verify(
167 gens_1,
168 gens_n,
169 transcript,
170 &a,
171 &self.comm_polys[i],
172 &comm_target,
173 )
174 .is_ok()
175 };
176 if !res {
177 return Err(ProofVerifyError::InternalError);
178 }
179
180 r.push(r_i);
181 }
182
183 Ok((self.comm_evals[&self.comm_evals.len() - 1].clone(), r))
184 }
185}
186
187impl SumcheckInstanceProof {
188 pub fn prove_cubic_with_additive_term<F>(
189 claim: &Scalar,
190 num_rounds: usize,
191 poly_tau: &mut DensePolynomial,
192 poly_A: &mut DensePolynomial,
193 poly_B: &mut DensePolynomial,
194 poly_C: &mut DensePolynomial,
195 comb_func: F,
196 transcript: &mut Transcript,
197 ) -> (Self, Vec<Scalar>, Vec<Scalar>)
198 where
199 F: Fn(&Scalar, &Scalar, &Scalar, &Scalar) -> Scalar,
200 {
201 let mut e = *claim;
202 let mut r: Vec<Scalar> = Vec::new();
203 let mut cubic_polys: Vec<CompressedUniPoly> = Vec::new();
204 for j in 0..num_rounds {
205 let mut eval_point_0 = Scalar::zero();
206 let mut eval_point_2 = Scalar::zero();
207 let mut eval_point_3 = Scalar::zero();
208
209 let len = poly_tau.len() / 2;
210 for i in 0..len {
211 // eval 0: bound_func is A(low)
212 eval_point_0 += comb_func(&poly_tau[i], &poly_A[i], &poly_B[i], &poly_C[i]);
213
214 // eval 2: bound_func is -A(low) + 2*A(high)
215 let poly_tau_bound_point = poly_tau[len + i] + poly_tau[len + i] - poly_tau[i];
216 let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i];
217 let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i];
218 let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i];
219
220 eval_point_2 += comb_func(
221 &poly_tau_bound_point,
222 &poly_A_bound_point,
223 &poly_B_bound_point,
224 &poly_C_bound_point,
225 );
226
227 // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2)
228 let poly_tau_bound_point = poly_tau_bound_point + poly_tau[len + i] - poly_tau[i];
229 let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i];
230 let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i];
231 let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i];
232
233 eval_point_3 += comb_func(
234 &poly_tau_bound_point,
235 &poly_A_bound_point,
236 &poly_B_bound_point,
237 &poly_C_bound_point,
238 );
239 }
240
241 let evals = vec![eval_point_0, e - eval_point_0, eval_point_2, eval_point_3];
242 let poly = UniPoly::from_evals(&evals);
243
244 // append the prover's message to the transcript
245 poly.append_to_transcript(b"poly", transcript);
246 //derive the verifier's challenge for the next round
247 let r_j = transcript.challenge_scalar(b"challenge_nextround");
248 r.push(r_j);
249
250 // bound all tables to the verifier's challenege
251 poly_tau.bound_poly_var_top(&r_j);
252 poly_A.bound_poly_var_top(&r_j);
253 poly_B.bound_poly_var_top(&r_j);
254 poly_C.bound_poly_var_top(&r_j);
255
256 e = poly.evaluate(&r_j);
257 cubic_polys.push(poly.compress());
258 }
259 (
260 SumcheckInstanceProof::new(cubic_polys),
261 r,
262 vec![poly_tau[0], poly_A[0], poly_B[0], poly_C[0]],
263 )
264 }
265 pub fn prove_cubic<F>(
266 claim: &Scalar,
267 num_rounds: usize,
268 poly_A: &mut DensePolynomial,
269 poly_B: &mut DensePolynomial,
270 poly_C: &mut DensePolynomial,
271 comb_func: F,
272 transcript: &mut Transcript,
273 ) -> (Self, Vec<Scalar>, Vec<Scalar>)
274 where
275 F: Fn(&Scalar, &Scalar, &Scalar) -> Scalar,
276 {
277 let mut e = *claim;
278 let mut r: Vec<Scalar> = Vec::new();
279 let mut cubic_polys: Vec<CompressedUniPoly> = Vec::new();
280 for _j in 0..num_rounds {
281 let mut eval_point_0 = Scalar::zero();
282 let mut eval_point_2 = Scalar::zero();
283 let mut eval_point_3 = Scalar::zero();
284
285 let len = poly_A.len() / 2;
286 for i in 0..len {
287 // eval 0: bound_func is A(low)
288 eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C[i]);
289
290 // eval 2: bound_func is -A(low) + 2*A(high)
291 let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i];
292 let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i];
293 let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i];
294 eval_point_2 += comb_func(
295 &poly_A_bound_point,
296 &poly_B_bound_point,
297 &poly_C_bound_point,
298 );
299
300 // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2)
301 let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i];
302 let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i];
303 let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i];
304
305 eval_point_3 += comb_func(
306 &poly_A_bound_point,
307 &poly_B_bound_point,
308 &poly_C_bound_point,
309 );
310 }
311
312 let evals = vec![eval_point_0, e - eval_point_0, eval_point_2, eval_point_3];
313 let poly = UniPoly::from_evals(&evals);
314
315 // append the prover's message to the transcript
316 poly.append_to_transcript(b"poly", transcript);
317
318 //derive the verifier's challenge for the next round
319 let r_j = transcript.challenge_scalar(b"challenge_nextround");
320 r.push(r_j);
321 // bound all tables to the verifier's challenege
322 poly_A.bound_poly_var_top(&r_j);
323 poly_B.bound_poly_var_top(&r_j);
324 poly_C.bound_poly_var_top(&r_j);
325 e = poly.evaluate(&r_j);
326 cubic_polys.push(poly.compress());
327 }
328
329 (
330 SumcheckInstanceProof::new(cubic_polys),
331 r,
332 vec![poly_A[0], poly_B[0], poly_C[0]],
333 )
334 }
335
336 pub fn prove_cubic_batched<F>(
337 claim: &Scalar,
338 num_rounds: usize,
339 poly_vec_par: (
340 &mut Vec<&mut DensePolynomial>,
341 &mut Vec<&mut DensePolynomial>,
342 &mut DensePolynomial,
343 ),
344 poly_vec_seq: (
345 &mut Vec<&mut DensePolynomial>,
346 &mut Vec<&mut DensePolynomial>,
347 &mut Vec<&mut DensePolynomial>,
348 ),
349 coeffs: &[Scalar],
350 comb_func: F,
351 transcript: &mut Transcript,
352 ) -> (
353 Self,
354 Vec<Scalar>,
355 (Vec<Scalar>, Vec<Scalar>, Scalar),
356 (Vec<Scalar>, Vec<Scalar>, Vec<Scalar>),
357 )
358 where
359 F: Fn(&Scalar, &Scalar, &Scalar) -> Scalar,
360 {
361 let (poly_A_vec_par, poly_B_vec_par, poly_C_par) = poly_vec_par;
362 let (poly_A_vec_seq, poly_B_vec_seq, poly_C_vec_seq) = poly_vec_seq;
363
364 //let (poly_A_vec_seq, poly_B_vec_seq, poly_C_vec_seq) = poly_vec_seq;
365 let mut e = *claim;
366 let mut r: Vec<Scalar> = Vec::new();
367 let mut cubic_polys: Vec<CompressedUniPoly> = Vec::new();
368
369 for _j in 0..num_rounds {
370 let mut evals: Vec<(Scalar, Scalar, Scalar)> = Vec::new();
371
372 for (poly_A, poly_B) in poly_A_vec_par.iter().zip(poly_B_vec_par.iter()) {
373 let mut eval_point_0 = Scalar::zero();
374 let mut eval_point_2 = Scalar::zero();
375 let mut eval_point_3 = Scalar::zero();
376
377 let len = poly_A.len() / 2;
378 for i in 0..len {
379 // eval 0: bound_func is A(low)
380 eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C_par[i]);
381
382 // eval 2: bound_func is -A(low) + 2*A(high)
383 let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i];
384 let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i];
385 let poly_C_bound_point = poly_C_par[len + i] + poly_C_par[len + i] - poly_C_par[i];
386 eval_point_2 += comb_func(
387 &poly_A_bound_point,
388 &poly_B_bound_point,
389 &poly_C_bound_point,
390 );
391
392 // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2)
393 let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i];
394 let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i];
395 let poly_C_bound_point = poly_C_bound_point + poly_C_par[len + i] - poly_C_par[i];
396
397 eval_point_3 += comb_func(
398 &poly_A_bound_point,
399 &poly_B_bound_point,
400 &poly_C_bound_point,
401 );
402 }
403
404 evals.push((eval_point_0, eval_point_2, eval_point_3));
405 }
406
407 for (poly_A, poly_B, poly_C) in izip!(
408 poly_A_vec_seq.iter(),
409 poly_B_vec_seq.iter(),
410 poly_C_vec_seq.iter()
411 ) {
412 let mut eval_point_0 = Scalar::zero();
413 let mut eval_point_2 = Scalar::zero();
414 let mut eval_point_3 = Scalar::zero();
415 let len = poly_A.len() / 2;
416 for i in 0..len {
417 // eval 0: bound_func is A(low)
418 eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C[i]);
419 // eval 2: bound_func is -A(low) + 2*A(high)
420 let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i];
421 let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i];
422 let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i];
423 eval_point_2 += comb_func(
424 &poly_A_bound_point,
425 &poly_B_bound_point,
426 &poly_C_bound_point,
427 );
428 // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2)
429 let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i];
430 let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i];
431 let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i];
432 eval_point_3 += comb_func(
433 &poly_A_bound_point,
434 &poly_B_bound_point,
435 &poly_C_bound_point,
436 );
437 }
438 evals.push((eval_point_0, eval_point_2, eval_point_3));
439 }
440
441 let evals_combined_0 = (0..evals.len()).map(|i| evals[i].0 * coeffs[i]).sum();
442 let evals_combined_2 = (0..evals.len()).map(|i| evals[i].1 * coeffs[i]).sum();
443 let evals_combined_3 = (0..evals.len()).map(|i| evals[i].2 * coeffs[i]).sum();
444
445 let evals = vec![
446 evals_combined_0,
447 e - evals_combined_0,
448 evals_combined_2,
449 evals_combined_3,
450 ];
451 let poly = UniPoly::from_evals(&evals);
452
453 // append the prover's message to the transcript
454 poly.append_to_transcript(b"poly", transcript);
455
456 //derive the verifier's challenge for the next round
457 let r_j = transcript.challenge_scalar(b"challenge_nextround");
458 r.push(r_j);
459
460 // bound all tables to the verifier's challenege
461 for (poly_A, poly_B) in poly_A_vec_par.iter_mut().zip(poly_B_vec_par.iter_mut()) {
462 poly_A.bound_poly_var_top(&r_j);
463 poly_B.bound_poly_var_top(&r_j);
464 }
465 poly_C_par.bound_poly_var_top(&r_j);
466
467 for (poly_A, poly_B, poly_C) in izip!(
468 poly_A_vec_seq.iter_mut(),
469 poly_B_vec_seq.iter_mut(),
470 poly_C_vec_seq.iter_mut()
471 ) {
472 poly_A.bound_poly_var_top(&r_j);
473 poly_B.bound_poly_var_top(&r_j);
474 poly_C.bound_poly_var_top(&r_j);
475 }
476
477 e = poly.evaluate(&r_j);
478 cubic_polys.push(poly.compress());
479 }
480
481 let poly_A_par_final = (0..poly_A_vec_par.len())
482 .map(|i| poly_A_vec_par[i][0])
483 .collect();
484 let poly_B_par_final = (0..poly_B_vec_par.len())
485 .map(|i| poly_B_vec_par[i][0])
486 .collect();
487 let claims_prod = (poly_A_par_final, poly_B_par_final, poly_C_par[0]);
488
489 let poly_A_seq_final = (0..poly_A_vec_seq.len())
490 .map(|i| poly_A_vec_seq[i][0])
491 .collect();
492 let poly_B_seq_final = (0..poly_B_vec_seq.len())
493 .map(|i| poly_B_vec_seq[i][0])
494 .collect();
495 let poly_C_seq_final = (0..poly_C_vec_seq.len())
496 .map(|i| poly_C_vec_seq[i][0])
497 .collect();
498 let claims_dotp = (poly_A_seq_final, poly_B_seq_final, poly_C_seq_final);
499
500 (
501 SumcheckInstanceProof::new(cubic_polys),
502 r,
503 claims_prod,
504 claims_dotp,
505 )
506 }
507
508 pub fn prove_quad<F>(
509 claim: &Scalar,
510 num_rounds: usize,
511 poly_A: &mut DensePolynomial,
512 poly_B: &mut DensePolynomial,
513 comb_func: F,
514 transcript: &mut Transcript,
515 ) -> (Self, Vec<Scalar>, Vec<Scalar>)
516 where
517 F: Fn(&Scalar, &Scalar) -> Scalar,
518 {
519 let mut e = *claim;
520 let mut r: Vec<Scalar> = Vec::new();
521 let mut quad_polys: Vec<CompressedUniPoly> = Vec::new();
522
523 for j in 0..num_rounds {
524 let mut eval_point_0 = Scalar::zero();
525 let mut eval_point_2 = Scalar::zero();
526
527 let len = poly_A.len() / 2;
528 for i in 0..len {
529 // eval 0: bound_func is A(low)
530 eval_point_0 += comb_func(&poly_A[i], &poly_B[i]);
531
532 // eval 2: bound_func is -A(low) + 2*A(high)
533 let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i];
534 let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i];
535 eval_point_2 += comb_func(&poly_A_bound_point, &poly_B_bound_point);
536 }
537
538 let evals = vec![eval_point_0, e - eval_point_0, eval_point_2];
539 let poly = UniPoly::from_evals(&evals);
540
541 // append the prover's message to the transcript
542 poly.append_to_transcript(b"poly", transcript);
543
544 //derive the verifier's challenge for the next round
545 let r_j = transcript.challenge_scalar(b"challenge_nextround");
546 r.push(r_j);
547
548 // bound all tables to the verifier's challenege
549 poly_A.bound_poly_var_top(&r_j);
550 poly_B.bound_poly_var_top(&r_j);
551 e = poly.evaluate(&r_j);
552 quad_polys.push(poly.compress());
553 }
554
555 (
556 SumcheckInstanceProof::new(quad_polys),
557 r,
558 vec![poly_A[0], poly_B[0]],
559 )
560 }
561}
562
563impl ZKSumcheckInstanceProof {
564 pub fn prove_quad<F>(
565 claim: &Scalar,
566 blind_claim: &Scalar,
567 num_rounds: usize,
568 poly_A: &mut DensePolynomial,
569 poly_B: &mut DensePolynomial,
570 comb_func: F,
571 gens_1: &MultiCommitGens,
572 gens_n: &MultiCommitGens,
573 transcript: &mut Transcript,
574 random_tape: &mut RandomTape,
575 ) -> (Self, Vec<Scalar>, Vec<Scalar>, Scalar)
576 where
577 F: Fn(&Scalar, &Scalar) -> Scalar,
578 {
579 let (blinds_poly, blinds_evals) = (
580 random_tape.random_vector(b"blinds_poly", num_rounds),
581 random_tape.random_vector(b"blinds_evals", num_rounds),
582 );
583 let mut claim_per_round = *claim;
584 let mut comm_claim_per_round = claim_per_round.commit(blind_claim, gens_1).compress();
585
586 let mut r: Vec<Scalar> = Vec::new();
587 let mut comm_polys: Vec<CompressedGroup> = Vec::new();
588 let mut comm_evals: Vec<CompressedGroup> = Vec::new();
589 let mut proofs: Vec<DotProductProof> = Vec::new();
590
591 for j in 0..num_rounds {
592 let (poly, comm_poly) = {
593 let mut eval_point_0 = Scalar::zero();
594 let mut eval_point_2 = Scalar::zero();
595
596 let len = poly_A.len() / 2;
597 for i in 0..len {
598 // eval 0: bound_func is A(low)
599 eval_point_0 += comb_func(&poly_A[i], &poly_B[i]);
600
601 // eval 2: bound_func is -A(low) + 2*A(high)
602 let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i];
603 let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i];
604 eval_point_2 += comb_func(&poly_A_bound_point, &poly_B_bound_point);
605 }
606
607 let evals = vec![eval_point_0, claim_per_round - eval_point_0, eval_point_2];
608 let poly = UniPoly::from_evals(&evals);
609 let comm_poly = poly.commit(gens_n, &blinds_poly[j]).compress();
610 (poly, comm_poly)
611 };
612
613 // append the prover's message to the transcript
614 comm_poly.append_to_transcript(b"comm_poly", transcript);
615 comm_polys.push(comm_poly);
616
617 //derive the verifier's challenge for the next round
618 let r_j = transcript.challenge_scalar(b"challenge_nextround");
619
620 // bound all tables to the verifier's challenege
621 poly_A.bound_poly_var_top(&r_j);
622 poly_B.bound_poly_var_top(&r_j);
623
624 // produce a proof of sum-check and of evaluation
625 let (proof, claim_next_round, comm_claim_next_round) = {
626 let eval = poly.evaluate(&r_j);
627 let comm_eval = eval.commit(&blinds_evals[j], gens_1).compress();
628
629 // we need to prove the following under homomorphic commitments:
630 // (1) poly(0) + poly(1) = claim_per_round
631 // (2) poly(r_j) = eval
632
633 // Our technique is to leverage dot product proofs:
634 // (1) we can prove: <poly_in_coeffs_form, (2, 1, 1, 1)> = claim_per_round
635 // (2) we can prove: <poly_in_coeffs_form, (1, r_j, r^2_j, ..) = eval
636 // for efficiency we batch them using random weights
637
638 // add two claims to transcript
639 comm_claim_per_round.append_to_transcript(b"comm_claim_per_round", transcript);
640 comm_eval.append_to_transcript(b"comm_eval", transcript);
641
642 // produce two weights
643 let w = transcript.challenge_vector(b"combine_two_claims_to_one", 2);
644
645 // compute a weighted sum of the RHS
646 let target = w[0] * claim_per_round + w[1] * eval;
647 let comm_target = GroupElement::vartime_multiscalar_mul(
648 w.as_slice(),
649 iter::once(&comm_claim_per_round)
650 .chain(iter::once(&comm_eval))
651 .map(|pt| GroupElement::decompress(pt).unwrap())
652 .collect::<Vec<GroupElement>>()
653 .as_slice(),
654 )
655 .compress();
656
657 let blind = {
658 let blind_sc = if j == 0 {
659 blind_claim
660 } else {
661 &blinds_evals[j - 1]
662 };
663
664 let blind_eval = &blinds_evals[j];
665
666 w[0] * blind_sc + w[1] * blind_eval
667 };
668 assert_eq!(target.commit(&blind, gens_1).compress(), comm_target);
669
670 let a = {
671 // the vector to use to decommit for sum-check test
672 let a_sc = {
673 let mut a = vec![Scalar::one(); poly.degree() + 1];
674 a[0] += Scalar::one();
675 a
676 };
677
678 // the vector to use to decommit for evaluation
679 let a_eval = {
680 let mut a = vec![Scalar::one(); poly.degree() + 1];
681 for j in 1..a.len() {
682 a[j] = a[j - 1] * r_j;
683 }
684 a
685 };
686
687 // take weighted sum of the two vectors using w
688 assert_eq!(a_sc.len(), a_eval.len());
689 (0..a_sc.len())
690 .map(|i| w[0] * a_sc[i] + w[1] * a_eval[i])
691 .collect::<Vec<Scalar>>()
692 };
693
694 let (proof, _comm_poly, _comm_sc_eval) = DotProductProof::prove(
695 gens_1,
696 gens_n,
697 transcript,
698 random_tape,
699 &poly.as_vec(),
700 &blinds_poly[j],
701 &a,
702 &target,
703 &blind,
704 );
705
706 (proof, eval, comm_eval)
707 };
708
709 claim_per_round = claim_next_round;
710 comm_claim_per_round = comm_claim_next_round;
711
712 proofs.push(proof);
713 r.push(r_j);
714 comm_evals.push(comm_claim_per_round.clone());
715 }
716
717 (
718 ZKSumcheckInstanceProof::new(comm_polys, comm_evals, proofs),
719 r,
720 vec![poly_A[0], poly_B[0]],
721 blinds_evals[num_rounds - 1],
722 )
723 }
724
725 pub fn prove_cubic_with_additive_term<F>(
726 claim: &Scalar,
727 blind_claim: &Scalar,
728 num_rounds: usize,
729 poly_A: &mut DensePolynomial,
730 poly_B: &mut DensePolynomial,
731 poly_C: &mut DensePolynomial,
732 poly_D: &mut DensePolynomial,
733 comb_func: F,
734 gens_1: &MultiCommitGens,
735 gens_n: &MultiCommitGens,
736 transcript: &mut Transcript,
737 random_tape: &mut RandomTape,
738 ) -> (Self, Vec<Scalar>, Vec<Scalar>, Scalar)
739 where
740 F: Fn(&Scalar, &Scalar, &Scalar, &Scalar) -> Scalar,
741 {
742 let (blinds_poly, blinds_evals) = (
743 random_tape.random_vector(b"blinds_poly", num_rounds),
744 random_tape.random_vector(b"blinds_evals", num_rounds),
745 );
746
747 let mut claim_per_round = *claim;
748 let mut comm_claim_per_round = claim_per_round.commit(blind_claim, gens_1).compress();
749
750 let mut r: Vec<Scalar> = Vec::new();
751 let mut comm_polys: Vec<CompressedGroup> = Vec::new();
752 let mut comm_evals: Vec<CompressedGroup> = Vec::new();
753 let mut proofs: Vec<DotProductProof> = Vec::new();
754
755 for j in 0..num_rounds {
756 let (poly, comm_poly) = {
757 let mut eval_point_0 = Scalar::zero();
758 let mut eval_point_2 = Scalar::zero();
759 let mut eval_point_3 = Scalar::zero();
760
761 let len = poly_A.len() / 2;
762 for i in 0..len {
763 // eval 0: bound_func is A(low)
764 eval_point_0 += comb_func(&poly_A[i], &poly_B[i], &poly_C[i], &poly_D[i]);
765
766 // eval 2: bound_func is -A(low) + 2*A(high)
767 let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i];
768 let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i];
769 let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i];
770 let poly_D_bound_point = poly_D[len + i] + poly_D[len + i] - poly_D[i];
771 eval_point_2 += comb_func(
772 &poly_A_bound_point,
773 &poly_B_bound_point,
774 &poly_C_bound_point,
775 &poly_D_bound_point,
776 );
777
778 // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2)
779 let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i];
780 let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i];
781 let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i];
782 let poly_D_bound_point = poly_D_bound_point + poly_D[len + i] - poly_D[i];
783 eval_point_3 += comb_func(
784 &poly_A_bound_point,
785 &poly_B_bound_point,
786 &poly_C_bound_point,
787 &poly_D_bound_point,
788 );
789 }
790
791 let evals = vec![
792 eval_point_0,
793 claim_per_round - eval_point_0,
794 eval_point_2,
795 eval_point_3,
796 ];
797 let poly = UniPoly::from_evals(&evals);
798 let comm_poly = poly.commit(gens_n, &blinds_poly[j]).compress();
799 (poly, comm_poly)
800 };
801
802 // append the prover's message to the transcript
803 comm_poly.append_to_transcript(b"comm_poly", transcript);
804 comm_polys.push(comm_poly);
805
806 //derive the verifier's challenge for the next round
807 let r_j = transcript.challenge_scalar(b"challenge_nextround");
808
809 // bound all tables to the verifier's challenege
810 poly_A.bound_poly_var_top(&r_j);
811 poly_B.bound_poly_var_top(&r_j);
812 poly_C.bound_poly_var_top(&r_j);
813 poly_D.bound_poly_var_top(&r_j);
814
815 // produce a proof of sum-check and of evaluation
816 let (proof, claim_next_round, comm_claim_next_round) = {
817 let eval = poly.evaluate(&r_j);
818 let comm_eval = eval.commit(&blinds_evals[j], gens_1).compress();
819
820 // we need to prove the following under homomorphic commitments:
821 // (1) poly(0) + poly(1) = claim_per_round
822 // (2) poly(r_j) = eval
823
824 // Our technique is to leverage dot product proofs:
825 // (1) we can prove: <poly_in_coeffs_form, (2, 1, 1, 1)> = claim_per_round
826 // (2) we can prove: <poly_in_coeffs_form, (1, r_j, r^2_j, ..) = eval
827 // for efficiency we batch them using random weights
828
829 // add two claims to transcript
830 comm_claim_per_round.append_to_transcript(b"comm_claim_per_round", transcript);
831 comm_eval.append_to_transcript(b"comm_eval", transcript);
832
833 // produce two weights
834 let w = transcript.challenge_vector(b"combine_two_claims_to_one", 2);
835
836 // compute a weighted sum of the RHS
837 let target = w[0] * claim_per_round + w[1] * eval;
838
839 let comm_target = GroupElement::vartime_multiscalar_mul(
840 w.as_slice(),
841 iter::once(&comm_claim_per_round)
842 .chain(iter::once(&comm_eval))
843 .map(|pt| GroupElement::decompress(&pt).unwrap())
844 .collect::<Vec<GroupElement>>()
845 .as_slice(),
846 )
847 .compress();
848
849 let blind = {
850 let blind_sc = if j == 0 {
851 blind_claim
852 } else {
853 &blinds_evals[j - 1]
854 };
855
856 let blind_eval = &blinds_evals[j];
857
858 w[0] * blind_sc + w[1] * blind_eval
859 };
860
861 let res = target.commit(&blind, gens_1);
862
863 assert_eq!(res.compress(), comm_target);
864
865 let a = {
866 // the vector to use to decommit for sum-check test
867 let a_sc = {
868 let mut a = vec![Scalar::one(); poly.degree() + 1];
869 a[0] += Scalar::one();
870 a
871 };
872
873 // the vector to use to decommit for evaluation
874 let a_eval = {
875 let mut a = vec![Scalar::one(); poly.degree() + 1];
876 for j in 1..a.len() {
877 a[j] = a[j - 1] * r_j;
878 }
879 a
880 };
881
882 // take weighted sum of the two vectors using w
883 assert_eq!(a_sc.len(), a_eval.len());
884 (0..a_sc.len())
885 .map(|i| w[0] * a_sc[i] + w[1] * a_eval[i])
886 .collect::<Vec<Scalar>>()
887 };
888
889 let (proof, _comm_poly, _comm_sc_eval) = DotProductProof::prove(
890 gens_1,
891 gens_n,
892 transcript,
893 random_tape,
894 &poly.as_vec(),
895 &blinds_poly[j],
896 &a,
897 &target,
898 &blind,
899 );
900
901 (proof, eval, comm_eval)
902 };
903
904 proofs.push(proof);
905 claim_per_round = claim_next_round;
906 comm_claim_per_round = comm_claim_next_round;
907 r.push(r_j);
908 comm_evals.push(comm_claim_per_round.clone());
909 }
910
911 (
912 ZKSumcheckInstanceProof::new(comm_polys, comm_evals, proofs),
913 r,
914 vec![poly_A[0], poly_B[0], poly_C[0], poly_D[0]],
915 blinds_evals[num_rounds - 1],
916 )
917 }
918}