shockwave-plus/tensor_pcs/src/tensor_rs_pcs.rs
tensor_pcs/src/tensor_rs_pcs.rsRaw
1use crate::rs_config::ecfft::ECFFTConfig;
2use crate::tree::BaseOpening;
3use crate::FieldExt;
4use ecfft::extend;
5use serde::{Deserialize, Serialize};
6
7use crate::fft::fft;
8use crate::polynomial::eq_poly::EqPoly;
9use crate::tensor_code::TensorCode;
10use crate::transcript::Transcript;
11use crate::utils::{det_num_cols, det_num_rows, dot_prod, hash_all, rlc_rows, sample_indices};
12
13use super::tensor_code::CommittedTensorCode;
14
15#[derive(Clone)]
16pub struct TensorRSMultilinearPCSConfig<F: FieldExt> {
17 pub expansion_factor: usize,
18 pub domain_powers: Option<Vec<Vec<F>>>,
19 pub fft_domain: Option<Vec<F>>,
20 pub ecfft_config: Option<ECFFTConfig<F>>,
21 pub l: usize,
22}
23
24impl<F: FieldExt> TensorRSMultilinearPCSConfig<F> {
25 pub fn num_cols(&self, num_entries: usize) -> usize {
26 det_num_cols(num_entries, self.l)
27 }
28
29 pub fn num_rows(&self, num_entries: usize) -> usize {
30 det_num_rows(num_entries, self.l)
31 }
32}
33
34#[derive(Clone)]
35pub struct TensorMultilinearPCS<F: FieldExt> {
36 config: TensorRSMultilinearPCSConfig<F>,
37}
38
39#[derive(Clone, Serialize, Deserialize)]
40pub struct TensorMLOpening<F: FieldExt> {
41 pub x: Vec<F>,
42 pub y: F,
43 pub base_opening: BaseOpening,
44 pub test_query_leaves: Vec<Vec<F>>,
45 pub eval_query_leaves: Vec<Vec<F>>,
46 pub u_hat_comm: [u8; 32],
47 pub test_u_prime: Vec<F>,
48 pub test_r_prime: Vec<F>,
49 pub eval_r_prime: Vec<F>,
50 pub eval_u_prime: Vec<F>,
51 pub poly_num_vars: usize,
52}
53
54impl<F: FieldExt> TensorMultilinearPCS<F> {
55 pub fn new(config: TensorRSMultilinearPCSConfig<F>) -> Self {
56 Self { config }
57 }
58
59 pub fn commit(&self, ml_poly_evals: &[F]) -> CommittedTensorCode<F> {
60 // Merkle commit to the evaluations of the polynomial
61 let n = ml_poly_evals.len();
62 assert!(n.is_power_of_two());
63 let tensor_code = self.encode_zk(ml_poly_evals);
64 let tree = tensor_code.commit(self.config.num_cols(n), self.config.num_rows(n));
65 tree
66 }
67
68 pub fn open(
69 &self,
70 u_hat_comm: &CommittedTensorCode<F>,
71 // TODO: Remove poly and use u_hat_comm
72 ml_poly_evals: &[F],
73 point: &[F],
74 eval: F,
75 transcript: &mut Transcript<F>,
76 ) -> TensorMLOpening<F> {
77 let n = ml_poly_evals.len();
78 assert!(n.is_power_of_two());
79 let num_vars = (n as f64).log2() as usize;
80
81 let num_cols = self.config.num_cols(n);
82 let num_rows = self.config.num_rows(n);
83
84 debug_assert_eq!(num_vars, point.len());
85
86 // ########################################
87 // Testing phase
88 // Prove the consistency between the random linear combination of the evaluation tensor (u_prime)
89 // and the tensor codeword (u_hat)
90 // ########################################
91
92 // Derive the challenge vector;
93 let r_u = transcript.challenge_vec(num_rows);
94
95 let u = (0..num_rows)
96 .map(|i| ml_poly_evals[(i * num_cols)..((i + 1) * num_cols)].to_vec())
97 .collect::<Vec<Vec<F>>>();
98
99 // Random linear combination of the rows of the polynomial in a tensor structure
100 let test_u_prime = rlc_rows(u.clone(), &r_u);
101
102 // Random linear combination of the blinder
103 let blinder = u_hat_comm
104 .tensor_codeword
105 .0
106 .iter()
107 .map(|row| row[(row.len() / 2)..].to_vec())
108 .collect::<Vec<Vec<F>>>();
109
110 debug_assert_eq!(blinder[0].len(), u_hat_comm.tensor_codeword.0[0].len() / 2);
111
112 let test_r_prime = rlc_rows(blinder.clone(), &r_u);
113
114 let num_indices = self.config.l;
115 let indices = sample_indices(num_indices, num_cols * 2, transcript);
116
117 let test_queries = self.test_phase(&indices, &u_hat_comm);
118
119 // ########################################
120 // Evaluation phase
121 // Prove the consistency
122 // ########################################
123
124 // Get the evaluation point
125
126 let log2_num_rows = (num_rows as f64).log2() as usize;
127 let q1 = EqPoly::new(point[0..log2_num_rows].to_vec()).evals();
128
129 let eval_r_prime = rlc_rows(blinder, &q1);
130
131 let eval_u_prime = rlc_rows(u.clone(), &q1);
132
133 let eval_queries = self.test_phase(&indices, &u_hat_comm);
134
135 TensorMLOpening {
136 x: point.to_vec(),
137 y: eval,
138 eval_query_leaves: eval_queries,
139 test_query_leaves: test_queries,
140 u_hat_comm: u_hat_comm.committed_tree.root(),
141 test_u_prime,
142 test_r_prime,
143 eval_r_prime,
144 eval_u_prime,
145 base_opening: BaseOpening {
146 hashes: u_hat_comm.committed_tree.column_roots.clone(),
147 },
148 poly_num_vars: num_vars,
149 }
150 }
151}
152
153impl<F: FieldExt> TensorMultilinearPCS<F> {
154 pub fn verify(&self, opening: &TensorMLOpening<F>, transcript: &mut Transcript<F>) {
155 let poly_num_entries = 2usize.pow(opening.poly_num_vars as u32);
156 let num_rows = self.config.num_rows(poly_num_entries);
157 let num_cols = self.config.num_cols(poly_num_entries);
158
159 // Verify the base opening
160 let base_opening = &opening.base_opening;
161 base_opening.verify(opening.u_hat_comm);
162
163 // ########################################
164 // Verify test phase
165 // ########################################
166
167 let r_u = transcript.challenge_vec(num_rows);
168
169 let test_u_prime_rs_codeword = self
170 .rs_encode(&opening.test_u_prime)
171 .iter()
172 .zip(opening.test_r_prime.iter())
173 .map(|(c, r)| *c + *r)
174 .collect::<Vec<F>>();
175
176 let num_indices = self.config.l;
177 let indices = sample_indices(num_indices, num_cols * 2, transcript);
178
179 debug_assert_eq!(indices.len(), opening.test_query_leaves.len());
180 for (expected_index, leaves) in indices.iter().zip(opening.test_query_leaves.iter()) {
181 // Verify that the hashes of the leaves equals the corresponding column root
182 let leaf_bytes = leaves
183 .iter()
184 .map(|x| x.to_repr())
185 .collect::<Vec<[u8; 32]>>();
186 let column_root = hash_all(&leaf_bytes);
187 let expected_column_root = base_opening.hashes[*expected_index];
188 assert_eq!(column_root, expected_column_root);
189
190 let mut sum = F::ZERO;
191 for (leaf, r_i) in leaves.iter().zip(r_u.iter()) {
192 sum += *r_i * *leaf;
193 }
194 assert_eq!(sum, test_u_prime_rs_codeword[*expected_index]);
195 }
196
197 // ########################################
198 // Verify evaluation phase
199 // ########################################
200
201 let log2_num_rows = (num_rows as f64).log2() as usize;
202 let q1 = EqPoly::new(opening.x[0..log2_num_rows].to_vec()).evals();
203 let q2 = EqPoly::new(opening.x[log2_num_rows..].to_vec()).evals();
204
205 let eval_u_prime_rs_codeword = self
206 .rs_encode(&opening.eval_u_prime)
207 .iter()
208 .zip(opening.eval_r_prime.iter())
209 .map(|(c, r)| *c + *r)
210 .collect::<Vec<F>>();
211
212 debug_assert_eq!(q1.len(), opening.eval_query_leaves[0].len());
213 debug_assert_eq!(indices.len(), opening.test_query_leaves.len());
214 for (expected_index, leaves) in indices.iter().zip(opening.eval_query_leaves.iter()) {
215 // TODO: Don't need to check the leaves again?
216 // Verify that the hashes of the leaves equals the corresponding column root
217 let leaf_bytes = leaves
218 .iter()
219 .map(|x| x.to_repr())
220 .collect::<Vec<[u8; 32]>>();
221 let column_root = hash_all(&leaf_bytes);
222 let expected_column_root = base_opening.hashes[*expected_index];
223 assert_eq!(column_root, expected_column_root);
224
225 let mut sum = F::ZERO;
226 for (leaf, q1_i) in leaves.iter().zip(q1.iter()) {
227 sum += *q1_i * *leaf;
228 }
229 assert_eq!(sum, eval_u_prime_rs_codeword[*expected_index]);
230 }
231
232 let expected_eval = dot_prod(&opening.eval_u_prime, &q2);
233 assert_eq!(expected_eval, opening.y);
234 }
235
236 fn split_encode(&self, message: &[F]) -> Vec<F> {
237 let codeword = self.rs_encode(message);
238
239 let mut rng = rand::thread_rng();
240 let blinder = (0..codeword.len())
241 .map(|_| F::random(&mut rng))
242 .collect::<Vec<F>>();
243
244 let mut randomized_codeword = codeword
245 .iter()
246 .zip(blinder.clone().iter())
247 .map(|(c, b)| *b + *c)
248 .collect::<Vec<F>>();
249
250 randomized_codeword.extend_from_slice(&blinder);
251 debug_assert_eq!(randomized_codeword.len(), codeword.len() * 2);
252 randomized_codeword
253 }
254
255 fn rs_encode(&self, message: &[F]) -> Vec<F> {
256 let mut padded_message = message.to_vec();
257 padded_message.resize(message.len().next_power_of_two(), F::ZERO);
258 let codeword_len = padded_message.len() * self.config.expansion_factor;
259
260 let codeword_len_log2 = (codeword_len as f64).log2() as usize;
261 let codeword = if self.config.fft_domain.is_some() {
262 // TODO: Resize the domain according to the message length
263 let fft_domain = self.config.fft_domain.as_ref().unwrap();
264 let mut padded_coeffs = message.clone().to_vec();
265 padded_coeffs.resize(fft_domain.len(), F::ZERO);
266 let codeword = fft(&padded_coeffs, &fft_domain);
267
268 codeword
269 } else if self.config.ecfft_config.is_some() {
270 let mut ecfft_config = self.config.ecfft_config.clone().unwrap();
271
272 // Resize the domain to the correct size
273 let config_domain_size = ecfft_config.domain.len();
274
275 assert!(config_domain_size >= codeword_len_log2 - 1);
276 ecfft_config.domain =
277 ecfft_config.domain[(config_domain_size - (codeword_len_log2 - 1))..].to_vec();
278 ecfft_config.matrices =
279 ecfft_config.matrices[(config_domain_size - (codeword_len_log2 - 1))..].to_vec();
280 ecfft_config.inverse_matrices = ecfft_config.inverse_matrices
281 [(config_domain_size - (codeword_len_log2 - 1))..]
282 .to_vec();
283
284 assert_eq!(
285 padded_message.len() * self.config.expansion_factor,
286 ecfft_config.domain[0].len()
287 );
288
289 let extended_evals = extend(
290 &padded_message,
291 &ecfft_config.domain,
292 &ecfft_config.matrices,
293 &ecfft_config.inverse_matrices,
294 0,
295 );
296
297 let codeword = [message.to_vec(), extended_evals].concat();
298 codeword
299 } else {
300 // TODO: Resize the domain according to the message length
301 let domain_powers = self.config.domain_powers.as_ref().unwrap();
302 assert_eq!(message.len(), domain_powers[0].len());
303 assert_eq!(
304 message.len() * self.config.expansion_factor,
305 domain_powers.len()
306 );
307
308 let codeword = domain_powers
309 .iter()
310 .map(|powers| {
311 message
312 .iter()
313 .zip(powers.iter())
314 .fold(F::ZERO, |acc, (m, p)| acc + *m * *p)
315 })
316 .collect::<Vec<F>>();
317
318 codeword
319 };
320
321 codeword
322 }
323
324 fn test_phase(&self, indices: &[usize], u_hat_comm: &CommittedTensorCode<F>) -> Vec<Vec<F>> {
325 // Query the columns of u_hat
326 let num_indices = self.config.l;
327
328 let u_hat_openings = indices
329 .iter()
330 .map(|index| u_hat_comm.query_column(*index))
331 .collect::<Vec<Vec<F>>>();
332
333 debug_assert_eq!(u_hat_openings.len(), num_indices);
334
335 u_hat_openings
336 }
337
338 fn encode_zk(&self, ml_poly_evals: &[F]) -> TensorCode<F> {
339 let n = ml_poly_evals.len();
340 assert!(n.is_power_of_two());
341
342 let num_rows = self.config.num_rows(n);
343 let num_cols = self.config.num_cols(n);
344 debug_assert_eq!(n, num_cols * num_rows);
345
346 let codewords = (0..num_rows)
347 .map(|i| &ml_poly_evals[i * num_cols..(i + 1) * num_cols])
348 .map(|row| self.split_encode(&row))
349 .collect::<Vec<Vec<F>>>();
350
351 TensorCode(codewords)
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358 use crate::polynomial::ml_poly::MlPoly;
359 use crate::rs_config::{ecfft, good_curves::secp256k1::secp256k1_good_curve, naive, smooth};
360
361 const TEST_NUM_VARS: usize = 8;
362 const TEST_L: usize = 10;
363
364 fn test_poly_evals<F: FieldExt>() -> MlPoly<F> {
365 let num_entries: usize = 2usize.pow(TEST_NUM_VARS as u32);
366
367 let evals = (0..num_entries)
368 .map(|i| F::from((i + 1) as u64))
369 .collect::<Vec<F>>();
370
371 MlPoly::new(evals)
372 }
373
374 fn prove_and_verify<F: FieldExt>(ml_poly: &MlPoly<F>, pcs: TensorMultilinearPCS<F>) {
375 let ml_poly_evals = &ml_poly.evals;
376
377 let comm = pcs.commit(ml_poly_evals);
378
379 let ml_poly_num_vars = (ml_poly_evals.len() as f64).log2() as usize;
380 let open_at = (0..ml_poly_num_vars)
381 .map(|i| F::from(i as u64))
382 .collect::<Vec<F>>();
383 let y = ml_poly.eval(&open_at);
384
385 let mut prover_transcript = Transcript::<F>::new(b"test");
386 prover_transcript.append_bytes(&comm.committed_tree.root);
387 let opening = pcs.open(&comm, ml_poly_evals, &open_at, y, &mut prover_transcript);
388
389 let mut verifier_transcript = Transcript::<F>::new(b"test");
390 verifier_transcript.append_bytes(&comm.committed_tree.root);
391 pcs.verify(&opening, &mut verifier_transcript);
392 }
393
394 fn config_base<F: FieldExt>() -> TensorRSMultilinearPCSConfig<F> {
395 let expansion_factor = 2;
396
397 TensorRSMultilinearPCSConfig::<F> {
398 expansion_factor,
399 domain_powers: None,
400 fft_domain: None,
401 ecfft_config: None,
402 l: TEST_L,
403 }
404 }
405
406 #[test]
407 fn test_tensor_pcs_fft() {
408 type F = halo2curves::pasta::Fp;
409 // FFT config
410 let ml_poly = test_poly_evals();
411 let mut config = config_base();
412
413 // The test polynomial has 2^k non-zero entries
414 let num_entries = ml_poly.evals.len();
415 config.fft_domain = Some(smooth::gen_config(config.num_cols(num_entries)));
416
417 // Test FFT PCS
418 let tensor_pcs_fft = TensorMultilinearPCS::<F>::new(config);
419 prove_and_verify(&ml_poly, tensor_pcs_fft);
420 }
421
422 #[test]
423 fn test_tensor_pcs_ecfft() {
424 type F = halo2curves::secp256k1::Fp;
425 let ml_poly = test_poly_evals();
426
427 let mut config = config_base();
428
429 let n = ml_poly.evals.len();
430 let num_cols = config.num_cols(n);
431 let k = ((num_cols * config.expansion_factor).next_power_of_two() as f64).log2() as usize;
432
433 let (curve, coset_offset) = secp256k1_good_curve(k);
434 config.ecfft_config = Some(ecfft::gen_config_form_curve(curve, coset_offset));
435
436 // Test FFT PCS
437 let tensor_pcs_ecf = TensorMultilinearPCS::<F>::new(config);
438 prove_and_verify(&ml_poly, tensor_pcs_ecf);
439 }
440
441 #[test]
442 fn test_tensor_pcs_naive() {
443 type F = halo2curves::secp256k1::Fp;
444 // FFT config
445 let ml_poly = test_poly_evals();
446 let n = ml_poly.evals.len();
447
448 // Naive config
449 let mut config = config_base();
450 config.domain_powers = Some(naive::gen_config(config.num_cols(n)));
451
452 // Test FFT PCS
453 let tensor_pcs_naive = TensorMultilinearPCS::<F>::new(config);
454 prove_and_verify(&ml_poly, tensor_pcs_naive);
455 }
456}