shockwave-plus/shockwave_plus/src/sumcheck/sc_phase_1.rs
shockwave_plus/src/sumcheck/sc_phase_1.rsRaw
1use crate::sumcheck::unipoly::UniPoly;
2use serde::{Deserialize, Serialize};
3use tensor_pcs::{EqPoly, SparseMLPoly, TensorMLOpening, TensorMultilinearPCS, Transcript};
4
5use crate::FieldExt;
6
7#[derive(Serialize, Deserialize)]
8pub struct SCPhase1Proof<F: FieldExt> {
9 pub blinder_poly_sum: F,
10 pub round_polys: Vec<UniPoly<F>>,
11 pub blinder_poly_eval_proof: TensorMLOpening<F>,
12}
13
14pub struct SumCheckPhase1<F: FieldExt> {
15 Az_evals: Vec<F>,
16 Bz_evals: Vec<F>,
17 Cz_evals: Vec<F>,
18 bound_eq_poly: EqPoly<F>,
19 challenge: Vec<F>,
20}
21
22impl<F: FieldExt> SumCheckPhase1<F> {
23 pub fn new(
24 Az_evals: Vec<F>,
25 Bz_evals: Vec<F>,
26 Cz_evals: Vec<F>,
27 tau: Vec<F>,
28 challenge: Vec<F>,
29 ) -> Self {
30 let bound_eq_poly = EqPoly::new(tau);
31 Self {
32 Az_evals,
33 Bz_evals,
34 Cz_evals,
35 bound_eq_poly,
36 challenge,
37 }
38 }
39
40 pub fn prove(
41 &self,
42 pcs: &TensorMultilinearPCS<F>,
43 transcript: &mut Transcript<F>,
44 ) -> (SCPhase1Proof<F>, (F, F, F)) {
45 let num_vars = (self.Az_evals.len() as f64).log2() as usize;
46 let mut round_polys = Vec::<UniPoly<F>>::with_capacity(num_vars - 1);
47
48 // We implement the zero-knowledge sumcheck protocol
49 // described in Section 4.1 https://eprint.iacr.org/2019/317.pdf
50
51 let mut rng = rand::thread_rng();
52 // Sample a blinding polynomial g(x_1, ..., x_m)
53 let random_evals = (0..2usize.pow(num_vars as u32))
54 .map(|_| F::random(&mut rng))
55 .collect::<Vec<F>>();
56 let blinder_poly_sum = random_evals.iter().fold(F::ZERO, |acc, x| acc + x);
57 let blinder_poly = SparseMLPoly::from_dense(random_evals);
58
59 let blinder_poly_comm = pcs.commit(&blinder_poly);
60
61 transcript.append_fe(&blinder_poly_sum);
62 transcript.append_bytes(&blinder_poly_comm.committed_tree.root);
63
64 let rho = transcript.challenge_fe();
65
66 // Compute the sum of g(x_1, ... x_m) over the boolean hypercube
67
68 // Do the sum check for f + \rho g
69
70 let mut A_table = self.Az_evals.clone();
71 let mut B_table = self.Bz_evals.clone();
72 let mut C_table = self.Cz_evals.clone();
73 let mut blinder_table = blinder_poly
74 .evals
75 .iter()
76 .map(|(_, x)| *x)
77 .collect::<Vec<F>>();
78 let mut eq_table = self.bound_eq_poly.evals();
79
80 let zero = F::ZERO;
81 let one = F::ONE;
82 let two = F::from(2);
83 let three = F::from(3);
84
85 for j in 0..num_vars {
86 let r_i = self.challenge[j];
87
88 let high_index = 2usize.pow((num_vars - j - 1) as u32);
89
90 let mut evals = [F::ZERO; 4];
91
92 // https://eprint.iacr.org/2019/317.pdf#subsection.3.2
93 for b in 0..high_index {
94 for (i, eval_at) in [zero, one, two, three].iter().enumerate() {
95 let a_eval = A_table[b] + (A_table[b + high_index] - A_table[b]) * eval_at;
96 let b_eval = B_table[b] + (B_table[b + high_index] - B_table[b]) * eval_at;
97 let c_eval = C_table[b] + (C_table[b + high_index] - C_table[b]) * eval_at;
98 let eq_eval = eq_table[b] + (eq_table[b + high_index] - eq_table[b]) * eval_at;
99 let blinder_eval = blinder_table[b]
100 + (blinder_table[b + high_index] - blinder_table[b]) * eval_at;
101 evals[i] += ((a_eval * b_eval - c_eval) * eq_eval) + rho * blinder_eval;
102 }
103
104 A_table[b] = A_table[b] + (A_table[b + high_index] - A_table[b]) * r_i;
105 B_table[b] = B_table[b] + (B_table[b + high_index] - B_table[b]) * r_i;
106 C_table[b] = C_table[b] + (C_table[b + high_index] - C_table[b]) * r_i;
107 eq_table[b] = eq_table[b] + (eq_table[b + high_index] - eq_table[b]) * r_i;
108 blinder_table[b] =
109 blinder_table[b] + (blinder_table[b + high_index] - blinder_table[b]) * r_i;
110 }
111
112 // TODO: Maybe send the evaluations to the verifier?
113 let round_poly = UniPoly::interpolate(&evals);
114
115 round_polys.push(round_poly);
116 }
117
118 let v_A = A_table[0];
119 let v_B = B_table[0];
120 let v_C = C_table[0];
121
122 // Prove the evaluation of the blinder polynomial at rx.
123 let mut rx_rev = self.challenge.clone();
124 rx_rev.reverse();
125 let blinder_poly_eval_proof =
126 pcs.open(&blinder_poly_comm, &blinder_poly, &rx_rev, transcript);
127
128 (
129 SCPhase1Proof {
130 blinder_poly_sum,
131 round_polys,
132 blinder_poly_eval_proof,
133 },
134 (v_A, v_B, v_C),
135 )
136 }
137
138 pub fn verify_round_polys(proof: &SCPhase1Proof<F>, challenge: &[F], rho: F) -> F {
139 debug_assert_eq!(proof.round_polys.len(), challenge.len());
140
141 let zero = F::ZERO;
142 let one = F::ONE;
143
144 println!("v phase 1 rho = {:?}", rho);
145
146 // target = 0 + rho * blinder_poly_sum
147 let mut target = rho * proof.blinder_poly_sum;
148 for (i, round_poly) in proof.round_polys.iter().enumerate() {
149 assert_eq!(
150 round_poly.eval(zero) + round_poly.eval(one),
151 target,
152 "round poly {} failed",
153 i
154 );
155 target = round_poly.eval(challenge[i]);
156 }
157
158 target
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use halo2curves::secp256k1::Fp;
166 type F = Fp;
167 use halo2curves::ff::Field;
168
169 #[test]
170 fn test_unipoly_3() {
171 let coeffs = [F::from(1u64), F::from(2u64), F::from(3u64), F::from(4u64)];
172 let eval_at = Fp::from(33);
173
174 let mut expected_eval = F::ZERO;
175 for i in 0..coeffs.len() {
176 expected_eval += coeffs[i] * eval_at.pow(&[3 - i as u64, 0, 0, 0]);
177 }
178
179 let mut evals = [F::ZERO; 4];
180 for i in 0..4 {
181 let eval_at = F::from(i as u64);
182 let mut eval_i = F::ZERO;
183 for j in 0..coeffs.len() {
184 eval_i += coeffs[j] * eval_at.pow(&[3 - j as u64, 0, 0, 0]);
185 }
186 evals[i] = eval_i;
187 }
188
189 let uni_poly = UniPoly::interpolate(&evals);
190 let eval = uni_poly.eval(eval_at);
191 assert_eq!(eval, expected_eval);
192 }
193}