testudo/src/sparse_mlpoly.rs
src/sparse_mlpoly.rsRaw
1#![allow(clippy::type_complexity)]
2#![allow(clippy::too_many_arguments)]
3#![allow(clippy::needless_range_loop)]
4use super::dense_mlpoly::DensePolynomial;
5use super::dense_mlpoly::{
6 EqPolynomial, IdentityPolynomial, PolyCommitment, PolyCommitmentGens, PolyEvalProof,
7};
8use super::errors::ProofVerifyError;
9use super::math::Math;
10use super::product_tree::{DotProductCircuit, ProductCircuit, ProductCircuitEvalProofBatched};
11use super::random::RandomTape;
12use super::scalar::Scalar;
13use super::timer::Timer;
14use super::transcript::{AppendToTranscript, ProofTranscript};
15use core::cmp::Ordering;
16use merlin::Transcript;
17use ark_serialize::*;
18use ark_ff::{One, Zero, Field};
19
20#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
21pub struct SparseMatEntry {
22 row: usize,
23 col: usize,
24 val: Scalar,
25}
26
27impl SparseMatEntry {
28 pub fn new(row: usize, col: usize, val: Scalar) -> Self {
29 SparseMatEntry { row, col, val }
30 }
31}
32
33#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
34pub struct SparseMatPolynomial {
35 num_vars_x: usize,
36 num_vars_y: usize,
37 M: Vec<SparseMatEntry>,
38}
39
40pub struct Derefs {
41 row_ops_val: Vec<DensePolynomial>,
42 col_ops_val: Vec<DensePolynomial>,
43 comb: DensePolynomial,
44}
45
46#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
47pub struct DerefsCommitment {
48 comm_ops_val: PolyCommitment,
49}
50
51impl Derefs {
52 pub fn new(row_ops_val: Vec<DensePolynomial>, col_ops_val: Vec<DensePolynomial>) -> Self {
53 assert_eq!(row_ops_val.len(), col_ops_val.len());
54
55 let derefs = {
56 // combine all polynomials into a single polynomial (used below to produce a single commitment)
57 let comb = DensePolynomial::merge(row_ops_val.iter().chain(col_ops_val.iter()));
58
59 Derefs {
60 row_ops_val,
61 col_ops_val,
62 comb,
63 }
64 };
65
66 derefs
67 }
68
69 pub fn commit(&self, gens: &PolyCommitmentGens) -> DerefsCommitment {
70 let (comm_ops_val, _blinds) = self.comb.commit(gens, None);
71 DerefsCommitment { comm_ops_val }
72 }
73}
74
75#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
76pub struct DerefsEvalProof {
77 proof_derefs: PolyEvalProof,
78}
79
80impl DerefsEvalProof {
81 fn protocol_name() -> &'static [u8] {
82 b"Derefs evaluation proof"
83 }
84
85 fn prove_single(
86 joint_poly: &DensePolynomial,
87 r: &[Scalar],
88 evals: Vec<Scalar>,
89 gens: &PolyCommitmentGens,
90 transcript: &mut Transcript,
91 random_tape: &mut RandomTape,
92 ) -> PolyEvalProof {
93 assert_eq!(
94 joint_poly.get_num_vars(),
95 r.len() + evals.len().log2() as usize
96 );
97
98 // append the claimed evaluations to transcript
99 evals.append_to_transcript(b"evals_ops_val", transcript);
100
101 // n-to-1 reduction
102 let (r_joint, eval_joint) = {
103 let challenges =
104 transcript.challenge_vector(b"challenge_combine_n_to_one", evals.len().log2() as usize);
105 let mut poly_evals = DensePolynomial::new(evals);
106 for i in (0..challenges.len()).rev() {
107 poly_evals.bound_poly_var_bot(&challenges[i]);
108 }
109 assert_eq!(poly_evals.len(), 1);
110 let joint_claim_eval = poly_evals[0];
111 let mut r_joint = challenges;
112 r_joint.extend(r);
113
114 debug_assert_eq!(joint_poly.evaluate(&r_joint), joint_claim_eval);
115 (r_joint, joint_claim_eval)
116 };
117 // decommit the joint polynomial at r_joint
118 eval_joint.append_to_transcript(b"joint_claim_eval", transcript);
119 let (proof_derefs, _comm_derefs_eval) = PolyEvalProof::prove(
120 joint_poly,
121 None,
122 &r_joint,
123 &eval_joint,
124 None,
125 gens,
126 transcript,
127 random_tape,
128 );
129
130 proof_derefs
131 }
132
133 // evalues both polynomials at r and produces a joint proof of opening
134 pub fn prove(
135 derefs: &Derefs,
136 eval_row_ops_val_vec: &[Scalar],
137 eval_col_ops_val_vec: &[Scalar],
138 r: &[Scalar],
139 gens: &PolyCommitmentGens,
140 transcript: &mut Transcript,
141 random_tape: &mut RandomTape,
142 ) -> Self {
143 transcript.append_protocol_name(DerefsEvalProof::protocol_name());
144
145 let evals = {
146 let mut evals = eval_row_ops_val_vec.to_owned();
147 evals.extend(eval_col_ops_val_vec);
148 evals.resize(evals.len().next_power_of_two(), Scalar::zero());
149 evals
150 };
151 let proof_derefs =
152 DerefsEvalProof::prove_single(&derefs.comb, r, evals, gens, transcript, random_tape);
153
154 DerefsEvalProof { proof_derefs }
155 }
156
157 fn verify_single(
158 proof: &PolyEvalProof,
159 comm: &PolyCommitment,
160 r: &[Scalar],
161 evals: Vec<Scalar>,
162 gens: &PolyCommitmentGens,
163 transcript: &mut Transcript,
164 ) -> Result<(), ProofVerifyError> {
165 // append the claimed evaluations to transcript
166 evals.append_to_transcript(b"evals_ops_val", transcript);
167
168 // n-to-1 reduction
169 let challenges =
170 transcript.challenge_vector(b"challenge_combine_n_to_one", evals.len().log2() as usize);
171 let mut poly_evals = DensePolynomial::new(evals);
172 for i in (0..challenges.len()).rev() {
173 poly_evals.bound_poly_var_bot(&challenges[i]);
174 }
175 assert_eq!(poly_evals.len(), 1);
176 let joint_claim_eval = poly_evals[0];
177 let mut r_joint = challenges;
178 r_joint.extend(r);
179
180 // decommit the joint polynomial at r_joint
181 joint_claim_eval.append_to_transcript(b"joint_claim_eval", transcript);
182
183 proof.verify_plain(gens, transcript, &r_joint, &joint_claim_eval, comm)
184 }
185
186 // verify evaluations of both polynomials at r
187 pub fn verify(
188 &self,
189 r: &[Scalar],
190 eval_row_ops_val_vec: &[Scalar],
191 eval_col_ops_val_vec: &[Scalar],
192 gens: &PolyCommitmentGens,
193 comm: &DerefsCommitment,
194 transcript: &mut Transcript,
195 ) -> Result<(), ProofVerifyError> {
196 transcript.append_protocol_name(DerefsEvalProof::protocol_name());
197 let mut evals = eval_row_ops_val_vec.to_owned();
198 evals.extend(eval_col_ops_val_vec);
199 evals.resize(evals.len().next_power_of_two(), Scalar::zero());
200
201 DerefsEvalProof::verify_single(
202 &self.proof_derefs,
203 &comm.comm_ops_val,
204 r,
205 evals,
206 gens,
207 transcript,
208 )
209 }
210}
211
212impl AppendToTranscript for DerefsCommitment {
213 fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
214 transcript.append_message(b"derefs_commitment", b"begin_derefs_commitment");
215 self.comm_ops_val.append_to_transcript(label, transcript);
216 transcript.append_message(b"derefs_commitment", b"end_derefs_commitment");
217 }
218}
219
220struct AddrTimestamps {
221 ops_addr_usize: Vec<Vec<usize>>,
222 ops_addr: Vec<DensePolynomial>,
223 read_ts: Vec<DensePolynomial>,
224 audit_ts: DensePolynomial,
225}
226
227impl AddrTimestamps {
228 pub fn new(num_cells: usize, num_ops: usize, ops_addr: Vec<Vec<usize>>) -> Self {
229 for item in ops_addr.iter() {
230 assert_eq!(item.len(), num_ops);
231 }
232
233 let mut audit_ts = vec![0usize; num_cells];
234 let mut ops_addr_vec: Vec<DensePolynomial> = Vec::new();
235 let mut read_ts_vec: Vec<DensePolynomial> = Vec::new();
236 for ops_addr_inst in ops_addr.iter() {
237 let mut read_ts = vec![0usize; num_ops];
238
239 // since read timestamps are trustworthy, we can simply increment the r-ts to obtain a w-ts
240 // this is sufficient to ensure that the write-set, consisting of (addr, val, ts) tuples, is a set
241 for i in 0..num_ops {
242 let addr = ops_addr_inst[i];
243 assert!(addr < num_cells);
244 let r_ts = audit_ts[addr];
245 read_ts[i] = r_ts;
246
247 let w_ts = r_ts + 1;
248 audit_ts[addr] = w_ts;
249 }
250
251 ops_addr_vec.push(DensePolynomial::from_usize(ops_addr_inst));
252 read_ts_vec.push(DensePolynomial::from_usize(&read_ts));
253 }
254
255 AddrTimestamps {
256 ops_addr: ops_addr_vec,
257 ops_addr_usize: ops_addr,
258 read_ts: read_ts_vec,
259 audit_ts: DensePolynomial::from_usize(&audit_ts),
260 }
261 }
262
263 fn deref_mem(addr: &[usize], mem_val: &[Scalar]) -> DensePolynomial {
264 DensePolynomial::new(
265 (0..addr.len())
266 .map(|i| {
267 let a = addr[i];
268 mem_val[a]
269 })
270 .collect::<Vec<Scalar>>(),
271 )
272 }
273
274 pub fn deref(&self, mem_val: &[Scalar]) -> Vec<DensePolynomial> {
275 (0..self.ops_addr.len())
276 .map(|i| AddrTimestamps::deref_mem(&self.ops_addr_usize[i], mem_val))
277 .collect::<Vec<DensePolynomial>>()
278 }
279}
280
281pub struct MultiSparseMatPolynomialAsDense {
282 batch_size: usize,
283 val: Vec<DensePolynomial>,
284 row: AddrTimestamps,
285 col: AddrTimestamps,
286 comb_ops: DensePolynomial,
287 comb_mem: DensePolynomial,
288}
289
290pub struct SparseMatPolyCommitmentGens {
291 gens_ops: PolyCommitmentGens,
292 gens_mem: PolyCommitmentGens,
293 gens_derefs: PolyCommitmentGens,
294}
295
296impl SparseMatPolyCommitmentGens {
297 pub fn new(
298 label: &'static [u8],
299 num_vars_x: usize,
300 num_vars_y: usize,
301 num_nz_entries: usize,
302 batch_size: usize,
303 ) -> SparseMatPolyCommitmentGens {
304 let num_vars_ops = num_nz_entries.next_power_of_two().log2() as usize
305 + (batch_size * 5).next_power_of_two().log2() as usize;
306 let num_vars_mem = if num_vars_x > num_vars_y {
307 num_vars_x
308 } else {
309 num_vars_y
310 } + 1;
311 let num_vars_derefs = num_nz_entries.next_power_of_two().log2() as usize
312 + (batch_size * 2).next_power_of_two().log2() as usize;
313
314 let gens_ops = PolyCommitmentGens::new(num_vars_ops, label);
315 let gens_mem = PolyCommitmentGens::new(num_vars_mem, label);
316 let gens_derefs = PolyCommitmentGens::new(num_vars_derefs, label);
317 SparseMatPolyCommitmentGens {
318 gens_ops,
319 gens_mem,
320 gens_derefs,
321 }
322 }
323}
324
325#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
326pub struct SparseMatPolyCommitment {
327 batch_size: usize,
328 num_ops: usize,
329 num_mem_cells: usize,
330 comm_comb_ops: PolyCommitment,
331 comm_comb_mem: PolyCommitment,
332}
333
334impl AppendToTranscript for SparseMatPolyCommitment {
335 fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) {
336 transcript.append_u64(b"batch_size", self.batch_size as u64);
337 transcript.append_u64(b"num_ops", self.num_ops as u64);
338 transcript.append_u64(b"num_mem_cells", self.num_mem_cells as u64);
339 self
340 .comm_comb_ops
341 .append_to_transcript(b"comm_comb_ops", transcript);
342 self
343 .comm_comb_mem
344 .append_to_transcript(b"comm_comb_mem", transcript);
345 }
346}
347
348impl SparseMatPolynomial {
349 pub fn new(num_vars_x: usize, num_vars_y: usize, M: Vec<SparseMatEntry>) -> Self {
350 SparseMatPolynomial {
351 num_vars_x,
352 num_vars_y,
353 M,
354 }
355 }
356
357 pub fn get_num_nz_entries(&self) -> usize {
358 self.M.len().next_power_of_two()
359 }
360
361 fn sparse_to_dense_vecs(&self, N: usize) -> (Vec<usize>, Vec<usize>, Vec<Scalar>) {
362 assert!(N >= self.get_num_nz_entries());
363 let mut ops_row: Vec<usize> = vec![0; N];
364 let mut ops_col: Vec<usize> = vec![0; N];
365 let mut val: Vec<Scalar> = vec![Scalar::zero(); N];
366
367 for i in 0..self.M.len() {
368 ops_row[i] = self.M[i].row;
369 ops_col[i] = self.M[i].col;
370 val[i] = self.M[i].val;
371 }
372 (ops_row, ops_col, val)
373 }
374
375 fn multi_sparse_to_dense_rep(
376 sparse_polys: &[&SparseMatPolynomial],
377 ) -> MultiSparseMatPolynomialAsDense {
378 assert!(!sparse_polys.is_empty());
379 for i in 1..sparse_polys.len() {
380 assert_eq!(sparse_polys[i].num_vars_x, sparse_polys[0].num_vars_x);
381 assert_eq!(sparse_polys[i].num_vars_y, sparse_polys[0].num_vars_y);
382 }
383
384 let N = (0..sparse_polys.len())
385 .map(|i| sparse_polys[i].get_num_nz_entries())
386 .max()
387 .unwrap()
388 .next_power_of_two();
389
390 let mut ops_row_vec: Vec<Vec<usize>> = Vec::new();
391 let mut ops_col_vec: Vec<Vec<usize>> = Vec::new();
392 let mut val_vec: Vec<DensePolynomial> = Vec::new();
393 for poly in sparse_polys {
394 let (ops_row, ops_col, val) = poly.sparse_to_dense_vecs(N);
395 ops_row_vec.push(ops_row);
396 ops_col_vec.push(ops_col);
397 val_vec.push(DensePolynomial::new(val));
398 }
399
400 let any_poly = &sparse_polys[0];
401
402 let num_mem_cells = if any_poly.num_vars_x > any_poly.num_vars_y {
403 any_poly.num_vars_x.pow2()
404 } else {
405 any_poly.num_vars_y.pow2()
406 };
407
408 let row = AddrTimestamps::new(num_mem_cells, N, ops_row_vec);
409 let col = AddrTimestamps::new(num_mem_cells, N, ops_col_vec);
410
411 // combine polynomials into a single polynomial for commitment purposes
412 let comb_ops = DensePolynomial::merge(
413 row
414 .ops_addr
415 .iter()
416 .chain(row.read_ts.iter())
417 .chain(col.ops_addr.iter())
418 .chain(col.read_ts.iter())
419 .chain(val_vec.iter()),
420 );
421 let mut comb_mem = row.audit_ts.clone();
422 comb_mem.extend(&col.audit_ts);
423
424 MultiSparseMatPolynomialAsDense {
425 batch_size: sparse_polys.len(),
426 row,
427 col,
428 val: val_vec,
429 comb_ops,
430 comb_mem,
431 }
432 }
433
434 fn evaluate_with_tables(&self, eval_table_rx: &[Scalar], eval_table_ry: &[Scalar]) -> Scalar {
435 assert_eq!(self.num_vars_x.pow2(), eval_table_rx.len());
436 assert_eq!(self.num_vars_y.pow2(), eval_table_ry.len());
437
438 (0..self.M.len())
439 .map(|i| {
440 let row = self.M[i].row;
441 let col = self.M[i].col;
442 let val = &self.M[i].val;
443 eval_table_rx[row] * eval_table_ry[col] * val
444 })
445 .sum()
446 }
447
448 pub fn multi_evaluate(
449 polys: &[&SparseMatPolynomial],
450 rx: &[Scalar],
451 ry: &[Scalar],
452 ) -> Vec<Scalar> {
453 let eval_table_rx = EqPolynomial::new(rx.to_vec()).evals();
454 let eval_table_ry = EqPolynomial::new(ry.to_vec()).evals();
455
456 (0..polys.len())
457 .map(|i| polys[i].evaluate_with_tables(&eval_table_rx, &eval_table_ry))
458 .collect::<Vec<Scalar>>()
459 }
460
461 pub fn multiply_vec(&self, num_rows: usize, num_cols: usize, z: &[Scalar]) -> Vec<Scalar> {
462 assert_eq!(z.len(), num_cols);
463
464 (0..self.M.len())
465 .map(|i| {
466 let row = self.M[i].row;
467 let col = self.M[i].col;
468 let val = &self.M[i].val;
469 (row, z[col] * val)
470 })
471 .fold(vec![Scalar::zero(); num_rows], |mut Mz, (r, v)| {
472 Mz[r] += v;
473 Mz
474 })
475 }
476
477 pub fn compute_eval_table_sparse(
478 &self,
479 rx: &[Scalar],
480 num_rows: usize,
481 num_cols: usize,
482 ) -> Vec<Scalar> {
483 assert_eq!(rx.len(), num_rows);
484
485 let mut M_evals: Vec<Scalar> = vec![Scalar::zero(); num_cols];
486
487 for i in 0..self.M.len() {
488 let entry = &self.M[i];
489 M_evals[entry.col] += rx[entry.row] * entry.val;
490 }
491 M_evals
492 }
493
494 pub fn multi_commit(
495 sparse_polys: &[&SparseMatPolynomial],
496 gens: &SparseMatPolyCommitmentGens,
497 ) -> (SparseMatPolyCommitment, MultiSparseMatPolynomialAsDense) {
498 let batch_size = sparse_polys.len();
499 let dense = SparseMatPolynomial::multi_sparse_to_dense_rep(sparse_polys);
500
501 let (comm_comb_ops, _blinds_comb_ops) = dense.comb_ops.commit(&gens.gens_ops, None);
502 let (comm_comb_mem, _blinds_comb_mem) = dense.comb_mem.commit(&gens.gens_mem, None);
503
504 (
505 SparseMatPolyCommitment {
506 batch_size,
507 num_mem_cells: dense.row.audit_ts.len(),
508 num_ops: dense.row.read_ts[0].len(),
509 comm_comb_ops,
510 comm_comb_mem,
511 },
512 dense,
513 )
514 }
515}
516
517impl MultiSparseMatPolynomialAsDense {
518 pub fn deref(&self, row_mem_val: &[Scalar], col_mem_val: &[Scalar]) -> Derefs {
519 let row_ops_val = self.row.deref(row_mem_val);
520 let col_ops_val = self.col.deref(col_mem_val);
521
522 Derefs::new(row_ops_val, col_ops_val)
523 }
524}
525
526#[derive(Debug)]
527struct ProductLayer {
528 init: ProductCircuit,
529 read_vec: Vec<ProductCircuit>,
530 write_vec: Vec<ProductCircuit>,
531 audit: ProductCircuit,
532}
533
534#[derive(Debug)]
535struct Layers {
536 prod_layer: ProductLayer,
537}
538
539impl Layers {
540 fn build_hash_layer(
541 eval_table: &[Scalar],
542 addrs_vec: &[DensePolynomial],
543 derefs_vec: &[DensePolynomial],
544 read_ts_vec: &[DensePolynomial],
545 audit_ts: &DensePolynomial,
546 r_mem_check: &(Scalar, Scalar),
547 ) -> (
548 DensePolynomial,
549 Vec<DensePolynomial>,
550 Vec<DensePolynomial>,
551 DensePolynomial,
552 ) {
553 let (r_hash, r_multiset_check) = r_mem_check;
554
555 //hash(addr, val, ts) = ts * r_hash_sqr + val * r_hash + addr
556 let r_hash_sqr = r_hash.square();
557 let hash_func = |addr: &Scalar, val: &Scalar, ts: &Scalar| -> Scalar {
558 r_hash_sqr * ts + (*val) * r_hash + addr
559 };
560
561 // hash init and audit that does not depend on #instances
562 let num_mem_cells = eval_table.len();
563 let poly_init_hashed = DensePolynomial::new(
564 (0..num_mem_cells)
565 .map(|i| {
566 // at init time, addr is given by i, init value is given by eval_table, and ts = 0
567 hash_func(&Scalar::from(i as u64), &eval_table[i], &Scalar::zero()) - r_multiset_check
568 })
569 .collect::<Vec<Scalar>>(),
570 );
571 let poly_audit_hashed = DensePolynomial::new(
572 (0..num_mem_cells)
573 .map(|i| {
574 // at audit time, addr is given by i, value is given by eval_table, and ts is given by audit_ts
575 hash_func(&Scalar::from(i as u64), &eval_table[i], &audit_ts[i]) - r_multiset_check
576 })
577 .collect::<Vec<Scalar>>(),
578 );
579
580 // hash read and write that depends on #instances
581 let mut poly_read_hashed_vec: Vec<DensePolynomial> = Vec::new();
582 let mut poly_write_hashed_vec: Vec<DensePolynomial> = Vec::new();
583 for i in 0..addrs_vec.len() {
584 let (addrs, derefs, read_ts) = (&addrs_vec[i], &derefs_vec[i], &read_ts_vec[i]);
585 assert_eq!(addrs.len(), derefs.len());
586 assert_eq!(addrs.len(), read_ts.len());
587 let num_ops = addrs.len();
588 let poly_read_hashed = DensePolynomial::new(
589 (0..num_ops)
590 .map(|i| {
591 // at read time, addr is given by addrs, value is given by derefs, and ts is given by read_ts
592 hash_func(&addrs[i], &derefs[i], &read_ts[i]) - r_multiset_check
593 })
594 .collect::<Vec<Scalar>>(),
595 );
596 poly_read_hashed_vec.push(poly_read_hashed);
597
598 let poly_write_hashed = DensePolynomial::new(
599 (0..num_ops)
600 .map(|i| {
601 // at write time, addr is given by addrs, value is given by derefs, and ts is given by write_ts = read_ts + 1
602 hash_func(&addrs[i], &derefs[i], &(read_ts[i] + Scalar::one())) - r_multiset_check
603 })
604 .collect::<Vec<Scalar>>(),
605 );
606 poly_write_hashed_vec.push(poly_write_hashed);
607 }
608
609 (
610 poly_init_hashed,
611 poly_read_hashed_vec,
612 poly_write_hashed_vec,
613 poly_audit_hashed,
614 )
615 }
616
617 pub fn new(
618 eval_table: &[Scalar],
619 addr_timestamps: &AddrTimestamps,
620 poly_ops_val: &[DensePolynomial],
621 r_mem_check: &(Scalar, Scalar),
622 ) -> Self {
623 let (poly_init_hashed, poly_read_hashed_vec, poly_write_hashed_vec, poly_audit_hashed) =
624 Layers::build_hash_layer(
625 eval_table,
626 &addr_timestamps.ops_addr,
627 poly_ops_val,
628 &addr_timestamps.read_ts,
629 &addr_timestamps.audit_ts,
630 r_mem_check,
631 );
632
633 let prod_init = ProductCircuit::new(&poly_init_hashed);
634 let prod_read_vec = (0..poly_read_hashed_vec.len())
635 .map(|i| ProductCircuit::new(&poly_read_hashed_vec[i]))
636 .collect::<Vec<ProductCircuit>>();
637 let prod_write_vec = (0..poly_write_hashed_vec.len())
638 .map(|i| ProductCircuit::new(&poly_write_hashed_vec[i]))
639 .collect::<Vec<ProductCircuit>>();
640 let prod_audit = ProductCircuit::new(&poly_audit_hashed);
641
642 // subset audit check
643 let hashed_writes: Scalar = (0..prod_write_vec.len())
644 .map(|i| prod_write_vec[i].evaluate())
645 .product();
646 let hashed_write_set: Scalar = prod_init.evaluate() * hashed_writes;
647
648 let hashed_reads: Scalar = (0..prod_read_vec.len())
649 .map(|i| prod_read_vec[i].evaluate())
650 .product();
651 let hashed_read_set: Scalar = hashed_reads * prod_audit.evaluate();
652
653 //assert_eq!(hashed_read_set, hashed_write_set);
654 debug_assert_eq!(hashed_read_set, hashed_write_set);
655
656 Layers {
657 prod_layer: ProductLayer {
658 init: prod_init,
659 read_vec: prod_read_vec,
660 write_vec: prod_write_vec,
661 audit: prod_audit,
662 },
663 }
664 }
665}
666
667#[derive(Debug)]
668struct PolyEvalNetwork {
669 row_layers: Layers,
670 col_layers: Layers,
671}
672
673impl PolyEvalNetwork {
674 pub fn new(
675 dense: &MultiSparseMatPolynomialAsDense,
676 derefs: &Derefs,
677 mem_rx: &[Scalar],
678 mem_ry: &[Scalar],
679 r_mem_check: &(Scalar, Scalar),
680 ) -> Self {
681 let row_layers = Layers::new(mem_rx, &dense.row, &derefs.row_ops_val, r_mem_check);
682 let col_layers = Layers::new(mem_ry, &dense.col, &derefs.col_ops_val, r_mem_check);
683
684 PolyEvalNetwork {
685 row_layers,
686 col_layers,
687 }
688 }
689}
690
691#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
692struct HashLayerProof {
693 eval_row: (Vec<Scalar>, Vec<Scalar>, Scalar),
694 eval_col: (Vec<Scalar>, Vec<Scalar>, Scalar),
695 eval_val: Vec<Scalar>,
696 eval_derefs: (Vec<Scalar>, Vec<Scalar>),
697 proof_ops: PolyEvalProof,
698 proof_mem: PolyEvalProof,
699 proof_derefs: DerefsEvalProof,
700}
701
702impl HashLayerProof {
703 fn protocol_name() -> &'static [u8] {
704 b"Sparse polynomial hash layer proof"
705 }
706
707 fn prove_helper(
708 rand: (&Vec<Scalar>, &Vec<Scalar>),
709 addr_timestamps: &AddrTimestamps,
710 ) -> (Vec<Scalar>, Vec<Scalar>, Scalar) {
711 let (rand_mem, rand_ops) = rand;
712
713 // decommit ops-addr at rand_ops
714 let mut eval_ops_addr_vec: Vec<Scalar> = Vec::new();
715 for i in 0..addr_timestamps.ops_addr.len() {
716 let eval_ops_addr = addr_timestamps.ops_addr[i].evaluate(rand_ops);
717 eval_ops_addr_vec.push(eval_ops_addr);
718 }
719
720 // decommit read_ts at rand_ops
721 let mut eval_read_ts_vec: Vec<Scalar> = Vec::new();
722 for i in 0..addr_timestamps.read_ts.len() {
723 let eval_read_ts = addr_timestamps.read_ts[i].evaluate(rand_ops);
724 eval_read_ts_vec.push(eval_read_ts);
725 }
726
727 // decommit audit-ts at rand_mem
728 let eval_audit_ts = addr_timestamps.audit_ts.evaluate(rand_mem);
729
730 (eval_ops_addr_vec, eval_read_ts_vec, eval_audit_ts)
731 }
732
733 fn prove(
734 rand: (&Vec<Scalar>, &Vec<Scalar>),
735 dense: &MultiSparseMatPolynomialAsDense,
736 derefs: &Derefs,
737 gens: &SparseMatPolyCommitmentGens,
738 transcript: &mut Transcript,
739 random_tape: &mut RandomTape,
740 ) -> Self {
741 transcript.append_protocol_name(HashLayerProof::protocol_name());
742
743 let (rand_mem, rand_ops) = rand;
744
745 // decommit derefs at rand_ops
746 let eval_row_ops_val = (0..derefs.row_ops_val.len())
747 .map(|i| derefs.row_ops_val[i].evaluate(rand_ops))
748 .collect::<Vec<Scalar>>();
749 let eval_col_ops_val = (0..derefs.col_ops_val.len())
750 .map(|i| derefs.col_ops_val[i].evaluate(rand_ops))
751 .collect::<Vec<Scalar>>();
752 let proof_derefs = DerefsEvalProof::prove(
753 derefs,
754 &eval_row_ops_val,
755 &eval_col_ops_val,
756 rand_ops,
757 &gens.gens_derefs,
758 transcript,
759 random_tape,
760 );
761 let eval_derefs = (eval_row_ops_val, eval_col_ops_val);
762
763 // evaluate row_addr, row_read-ts, col_addr, col_read-ts, val at rand_ops
764 // evaluate row_audit_ts and col_audit_ts at rand_mem
765 let (eval_row_addr_vec, eval_row_read_ts_vec, eval_row_audit_ts) =
766 HashLayerProof::prove_helper((rand_mem, rand_ops), &dense.row);
767 let (eval_col_addr_vec, eval_col_read_ts_vec, eval_col_audit_ts) =
768 HashLayerProof::prove_helper((rand_mem, rand_ops), &dense.col);
769 let eval_val_vec = (0..dense.val.len())
770 .map(|i| dense.val[i].evaluate(rand_ops))
771 .collect::<Vec<Scalar>>();
772
773 // form a single decommitment using comm_comb_ops
774 let mut evals_ops: Vec<Scalar> = Vec::new();
775 evals_ops.extend(&eval_row_addr_vec);
776 evals_ops.extend(&eval_row_read_ts_vec);
777 evals_ops.extend(&eval_col_addr_vec);
778 evals_ops.extend(&eval_col_read_ts_vec);
779 evals_ops.extend(&eval_val_vec);
780 evals_ops.resize(evals_ops.len().next_power_of_two(), Scalar::zero());
781 evals_ops.append_to_transcript(b"claim_evals_ops", transcript);
782 let challenges_ops = transcript.challenge_vector(
783 b"challenge_combine_n_to_one",
784 evals_ops.len().log2() as usize,
785 );
786
787 let mut poly_evals_ops = DensePolynomial::new(evals_ops);
788 for i in (0..challenges_ops.len()).rev() {
789 poly_evals_ops.bound_poly_var_bot(&challenges_ops[i]);
790 }
791 assert_eq!(poly_evals_ops.len(), 1);
792 let joint_claim_eval_ops = poly_evals_ops[0];
793 let mut r_joint_ops = challenges_ops;
794 r_joint_ops.extend(rand_ops);
795 debug_assert_eq!(dense.comb_ops.evaluate(&r_joint_ops), joint_claim_eval_ops);
796 joint_claim_eval_ops.append_to_transcript(b"joint_claim_eval_ops", transcript);
797 let (proof_ops, _comm_ops_eval) = PolyEvalProof::prove(
798 &dense.comb_ops,
799 None,
800 &r_joint_ops,
801 &joint_claim_eval_ops,
802 None,
803 &gens.gens_ops,
804 transcript,
805 random_tape,
806 );
807
808 // form a single decommitment using comb_comb_mem at rand_mem
809 let evals_mem: Vec<Scalar> = vec![eval_row_audit_ts, eval_col_audit_ts];
810 evals_mem.append_to_transcript(b"claim_evals_mem", transcript);
811 let challenges_mem = transcript.challenge_vector(
812 b"challenge_combine_two_to_one",
813 evals_mem.len().log2() as usize,
814 );
815
816 let mut poly_evals_mem = DensePolynomial::new(evals_mem);
817 for i in (0..challenges_mem.len()).rev() {
818 poly_evals_mem.bound_poly_var_bot(&challenges_mem[i]);
819 }
820 assert_eq!(poly_evals_mem.len(), 1);
821 let joint_claim_eval_mem = poly_evals_mem[0];
822 let mut r_joint_mem = challenges_mem;
823 r_joint_mem.extend(rand_mem);
824 debug_assert_eq!(dense.comb_mem.evaluate(&r_joint_mem), joint_claim_eval_mem);
825 joint_claim_eval_mem.append_to_transcript(b"joint_claim_eval_mem", transcript);
826 let (proof_mem, _comm_mem_eval) = PolyEvalProof::prove(
827 &dense.comb_mem,
828 None,
829 &r_joint_mem,
830 &joint_claim_eval_mem,
831 None,
832 &gens.gens_mem,
833 transcript,
834 random_tape,
835 );
836
837 HashLayerProof {
838 eval_row: (eval_row_addr_vec, eval_row_read_ts_vec, eval_row_audit_ts),
839 eval_col: (eval_col_addr_vec, eval_col_read_ts_vec, eval_col_audit_ts),
840 eval_val: eval_val_vec,
841 eval_derefs,
842 proof_ops,
843 proof_mem,
844 proof_derefs,
845 }
846 }
847
848 fn verify_helper(
849 rand: &(&Vec<Scalar>, &Vec<Scalar>),
850 claims: &(Scalar, Vec<Scalar>, Vec<Scalar>, Scalar),
851 eval_ops_val: &[Scalar],
852 eval_ops_addr: &[Scalar],
853 eval_read_ts: &[Scalar],
854 eval_audit_ts: &Scalar,
855 r: &[Scalar],
856 r_hash: &Scalar,
857 r_multiset_check: &Scalar,
858 ) -> Result<(), ProofVerifyError> {
859 let r_hash_sqr = r_hash.square();
860 let hash_func = |addr: &Scalar, val: &Scalar, ts: &Scalar| -> Scalar {
861 r_hash_sqr * ts + (*val) * r_hash + addr
862 };
863
864 let (rand_mem, _rand_ops) = rand;
865 let (claim_init, claim_read, claim_write, claim_audit) = claims;
866
867 // init
868 let eval_init_addr = IdentityPolynomial::new(rand_mem.len()).evaluate(rand_mem);
869 let eval_init_val = EqPolynomial::new(r.to_vec()).evaluate(rand_mem);
870 let hash_init_at_rand_mem =
871 hash_func(&eval_init_addr, &eval_init_val, &Scalar::zero()) - r_multiset_check; // verify the claim_last of init chunk
872 assert_eq!(&hash_init_at_rand_mem, claim_init);
873
874 // read
875 for i in 0..eval_ops_addr.len() {
876 let hash_read_at_rand_ops =
877 hash_func(&eval_ops_addr[i], &eval_ops_val[i], &eval_read_ts[i]) - r_multiset_check; // verify the claim_last of init chunk
878 assert_eq!(&hash_read_at_rand_ops, &claim_read[i]);
879 }
880
881 // write: shares addr, val component; only decommit write_ts
882 for i in 0..eval_ops_addr.len() {
883 let eval_write_ts = eval_read_ts[i] + Scalar::one();
884 let hash_write_at_rand_ops =
885 hash_func(&eval_ops_addr[i], &eval_ops_val[i], &eval_write_ts) - r_multiset_check; // verify the claim_last of init chunk
886 assert_eq!(&hash_write_at_rand_ops, &claim_write[i]);
887 }
888
889 // audit: shares addr and val with init
890 let eval_audit_addr = eval_init_addr;
891 let eval_audit_val = eval_init_val;
892 let hash_audit_at_rand_mem =
893 hash_func(&eval_audit_addr, &eval_audit_val, eval_audit_ts) - r_multiset_check;
894 assert_eq!(&hash_audit_at_rand_mem, claim_audit); // verify the last step of the sum-check for audit
895
896 Ok(())
897 }
898
899 fn verify(
900 &self,
901 rand: (&Vec<Scalar>, &Vec<Scalar>),
902 claims_row: &(Scalar, Vec<Scalar>, Vec<Scalar>, Scalar),
903 claims_col: &(Scalar, Vec<Scalar>, Vec<Scalar>, Scalar),
904 claims_dotp: &[Scalar],
905 comm: &SparseMatPolyCommitment,
906 gens: &SparseMatPolyCommitmentGens,
907 comm_derefs: &DerefsCommitment,
908 rx: &[Scalar],
909 ry: &[Scalar],
910 r_hash: &Scalar,
911 r_multiset_check: &Scalar,
912 transcript: &mut Transcript,
913 ) -> Result<(), ProofVerifyError> {
914 let timer = Timer::new("verify_hash_proof");
915 transcript.append_protocol_name(HashLayerProof::protocol_name());
916
917 let (rand_mem, rand_ops) = rand;
918
919 // verify derefs at rand_ops
920 let (eval_row_ops_val, eval_col_ops_val) = &self.eval_derefs;
921 assert_eq!(eval_row_ops_val.len(), eval_col_ops_val.len());
922 self.proof_derefs.verify(
923 rand_ops,
924 eval_row_ops_val,
925 eval_col_ops_val,
926 &gens.gens_derefs,
927 comm_derefs,
928 transcript,
929 )?;
930
931 // verify the decommitments used in evaluation sum-check
932 let eval_val_vec = &self.eval_val;
933 assert_eq!(claims_dotp.len(), 3 * eval_row_ops_val.len());
934 for i in 0..claims_dotp.len() / 3 {
935 let claim_row_ops_val = claims_dotp[3 * i];
936 let claim_col_ops_val = claims_dotp[3 * i + 1];
937 let claim_val = claims_dotp[3 * i + 2];
938
939 assert_eq!(claim_row_ops_val, eval_row_ops_val[i]);
940 assert_eq!(claim_col_ops_val, eval_col_ops_val[i]);
941 assert_eq!(claim_val, eval_val_vec[i]);
942 }
943
944 // verify addr-timestamps using comm_comb_ops at rand_ops
945 let (eval_row_addr_vec, eval_row_read_ts_vec, eval_row_audit_ts) = &self.eval_row;
946 let (eval_col_addr_vec, eval_col_read_ts_vec, eval_col_audit_ts) = &self.eval_col;
947
948 let mut evals_ops: Vec<Scalar> = Vec::new();
949 evals_ops.extend(eval_row_addr_vec);
950 evals_ops.extend(eval_row_read_ts_vec);
951 evals_ops.extend(eval_col_addr_vec);
952 evals_ops.extend(eval_col_read_ts_vec);
953 evals_ops.extend(eval_val_vec);
954 evals_ops.resize(evals_ops.len().next_power_of_two(), Scalar::zero());
955 evals_ops.append_to_transcript(b"claim_evals_ops", transcript);
956 let challenges_ops = transcript.challenge_vector(
957 b"challenge_combine_n_to_one",
958 evals_ops.len().log2() as usize,
959 );
960
961 let mut poly_evals_ops = DensePolynomial::new(evals_ops);
962 for i in (0..challenges_ops.len()).rev() {
963 poly_evals_ops.bound_poly_var_bot(&challenges_ops[i]);
964 }
965 assert_eq!(poly_evals_ops.len(), 1);
966 let joint_claim_eval_ops = poly_evals_ops[0];
967 let mut r_joint_ops = challenges_ops;
968 r_joint_ops.extend(rand_ops);
969 joint_claim_eval_ops.append_to_transcript(b"joint_claim_eval_ops", transcript);
970 assert!(self
971 .proof_ops
972 .verify_plain(
973 &gens.gens_ops,
974 transcript,
975 &r_joint_ops,
976 &joint_claim_eval_ops,
977 &comm.comm_comb_ops
978 )
979 .is_ok());
980
981 // verify proof-mem using comm_comb_mem at rand_mem
982 // form a single decommitment using comb_comb_mem at rand_mem
983 let evals_mem: Vec<Scalar> = vec![*eval_row_audit_ts, *eval_col_audit_ts];
984 evals_mem.append_to_transcript(b"claim_evals_mem", transcript);
985 let challenges_mem = transcript.challenge_vector(
986 b"challenge_combine_two_to_one",
987 evals_mem.len().log2() as usize,
988 );
989
990 let mut poly_evals_mem = DensePolynomial::new(evals_mem);
991 for i in (0..challenges_mem.len()).rev() {
992 poly_evals_mem.bound_poly_var_bot(&challenges_mem[i]);
993 }
994 assert_eq!(poly_evals_mem.len(), 1);
995 let joint_claim_eval_mem = poly_evals_mem[0];
996 let mut r_joint_mem = challenges_mem;
997 r_joint_mem.extend(rand_mem);
998 joint_claim_eval_mem.append_to_transcript(b"joint_claim_eval_mem", transcript);
999 self.proof_mem.verify_plain(
1000 &gens.gens_mem,
1001 transcript,
1002 &r_joint_mem,
1003 &joint_claim_eval_mem,
1004 &comm.comm_comb_mem,
1005 )?;
1006
1007 // verify the claims from the product layer
1008 let (eval_ops_addr, eval_read_ts, eval_audit_ts) = &self.eval_row;
1009 HashLayerProof::verify_helper(
1010 &(rand_mem, rand_ops),
1011 claims_row,
1012 eval_row_ops_val,
1013 eval_ops_addr,
1014 eval_read_ts,
1015 eval_audit_ts,
1016 rx,
1017 r_hash,
1018 r_multiset_check,
1019 )?;
1020
1021 let (eval_ops_addr, eval_read_ts, eval_audit_ts) = &self.eval_col;
1022 HashLayerProof::verify_helper(
1023 &(rand_mem, rand_ops),
1024 claims_col,
1025 eval_col_ops_val,
1026 eval_ops_addr,
1027 eval_read_ts,
1028 eval_audit_ts,
1029 ry,
1030 r_hash,
1031 r_multiset_check,
1032 )?;
1033
1034 timer.stop();
1035 Ok(())
1036 }
1037}
1038
1039#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
1040struct ProductLayerProof {
1041 eval_row: (Scalar, Vec<Scalar>, Vec<Scalar>, Scalar),
1042 eval_col: (Scalar, Vec<Scalar>, Vec<Scalar>, Scalar),
1043 eval_val: (Vec<Scalar>, Vec<Scalar>),
1044 proof_mem: ProductCircuitEvalProofBatched,
1045 proof_ops: ProductCircuitEvalProofBatched,
1046}
1047
1048impl ProductLayerProof {
1049 fn protocol_name() -> &'static [u8] {
1050 b"Sparse polynomial product layer proof"
1051 }
1052
1053 pub fn prove(
1054 row_prod_layer: &mut ProductLayer,
1055 col_prod_layer: &mut ProductLayer,
1056 dense: &MultiSparseMatPolynomialAsDense,
1057 derefs: &Derefs,
1058 eval: &[Scalar],
1059 transcript: &mut Transcript,
1060 ) -> (Self, Vec<Scalar>, Vec<Scalar>) {
1061 transcript.append_protocol_name(ProductLayerProof::protocol_name());
1062
1063 let row_eval_init = row_prod_layer.init.evaluate();
1064 let row_eval_audit = row_prod_layer.audit.evaluate();
1065 let row_eval_read = (0..row_prod_layer.read_vec.len())
1066 .map(|i| row_prod_layer.read_vec[i].evaluate())
1067 .collect::<Vec<Scalar>>();
1068 let row_eval_write = (0..row_prod_layer.write_vec.len())
1069 .map(|i| row_prod_layer.write_vec[i].evaluate())
1070 .collect::<Vec<Scalar>>();
1071
1072 // subset check
1073 let ws: Scalar = (0..row_eval_write.len())
1074 .map(|i| row_eval_write[i])
1075 .product();
1076 let rs: Scalar = (0..row_eval_read.len()).map(|i| row_eval_read[i]).product();
1077 assert_eq!(row_eval_init * ws, rs * row_eval_audit);
1078
1079 row_eval_init.append_to_transcript(b"claim_row_eval_init", transcript);
1080 row_eval_read.append_to_transcript(b"claim_row_eval_read", transcript);
1081 row_eval_write.append_to_transcript(b"claim_row_eval_write", transcript);
1082 row_eval_audit.append_to_transcript(b"claim_row_eval_audit", transcript);
1083
1084 let col_eval_init = col_prod_layer.init.evaluate();
1085 let col_eval_audit = col_prod_layer.audit.evaluate();
1086 let col_eval_read: Vec<Scalar> = (0..col_prod_layer.read_vec.len())
1087 .map(|i| col_prod_layer.read_vec[i].evaluate())
1088 .collect();
1089 let col_eval_write: Vec<Scalar> = (0..col_prod_layer.write_vec.len())
1090 .map(|i| col_prod_layer.write_vec[i].evaluate())
1091 .collect();
1092
1093 // subset check
1094 let ws: Scalar = (0..col_eval_write.len())
1095 .map(|i| col_eval_write[i])
1096 .product();
1097 let rs: Scalar = (0..col_eval_read.len()).map(|i| col_eval_read[i]).product();
1098 assert_eq!(col_eval_init * ws, rs * col_eval_audit);
1099
1100 col_eval_init.append_to_transcript(b"claim_col_eval_init", transcript);
1101 col_eval_read.append_to_transcript(b"claim_col_eval_read", transcript);
1102 col_eval_write.append_to_transcript(b"claim_col_eval_write", transcript);
1103 col_eval_audit.append_to_transcript(b"claim_col_eval_audit", transcript);
1104
1105 // prepare dotproduct circuit for batching then with ops-related product circuits
1106 assert_eq!(eval.len(), derefs.row_ops_val.len());
1107 assert_eq!(eval.len(), derefs.col_ops_val.len());
1108 assert_eq!(eval.len(), dense.val.len());
1109 let mut dotp_circuit_left_vec: Vec<DotProductCircuit> = Vec::new();
1110 let mut dotp_circuit_right_vec: Vec<DotProductCircuit> = Vec::new();
1111 let mut eval_dotp_left_vec: Vec<Scalar> = Vec::new();
1112 let mut eval_dotp_right_vec: Vec<Scalar> = Vec::new();
1113 for i in 0..derefs.row_ops_val.len() {
1114 // evaluate sparse polynomial evaluation using two dotp checks
1115 let left = derefs.row_ops_val[i].clone();
1116 let right = derefs.col_ops_val[i].clone();
1117 let weights = dense.val[i].clone();
1118
1119 // build two dot product circuits to prove evaluation of sparse polynomial
1120 let mut dotp_circuit = DotProductCircuit::new(left, right, weights);
1121 let (dotp_circuit_left, dotp_circuit_right) = dotp_circuit.split();
1122
1123 let (eval_dotp_left, eval_dotp_right) =
1124 (dotp_circuit_left.evaluate(), dotp_circuit_right.evaluate());
1125
1126 eval_dotp_left.append_to_transcript(b"claim_eval_dotp_left", transcript);
1127 eval_dotp_right.append_to_transcript(b"claim_eval_dotp_right", transcript);
1128 assert_eq!(eval_dotp_left + eval_dotp_right, eval[i]);
1129 eval_dotp_left_vec.push(eval_dotp_left);
1130 eval_dotp_right_vec.push(eval_dotp_right);
1131
1132 dotp_circuit_left_vec.push(dotp_circuit_left);
1133 dotp_circuit_right_vec.push(dotp_circuit_right);
1134 }
1135
1136 // The number of operations into the memory encoded by rx and ry are always the same (by design)
1137 // So we can produce a batched product proof for all of them at the same time.
1138 // prove the correctness of claim_row_eval_read, claim_row_eval_write, claim_col_eval_read, and claim_col_eval_write
1139 // TODO: we currently only produce proofs for 3 batched sparse polynomial evaluations
1140 assert_eq!(row_prod_layer.read_vec.len(), 3);
1141 let (row_read_A, row_read_B, row_read_C) = {
1142 let (vec_A, vec_BC) = row_prod_layer.read_vec.split_at_mut(1);
1143 let (vec_B, vec_C) = vec_BC.split_at_mut(1);
1144 (vec_A, vec_B, vec_C)
1145 };
1146
1147 let (row_write_A, row_write_B, row_write_C) = {
1148 let (vec_A, vec_BC) = row_prod_layer.write_vec.split_at_mut(1);
1149 let (vec_B, vec_C) = vec_BC.split_at_mut(1);
1150 (vec_A, vec_B, vec_C)
1151 };
1152
1153 let (col_read_A, col_read_B, col_read_C) = {
1154 let (vec_A, vec_BC) = col_prod_layer.read_vec.split_at_mut(1);
1155 let (vec_B, vec_C) = vec_BC.split_at_mut(1);
1156 (vec_A, vec_B, vec_C)
1157 };
1158
1159 let (col_write_A, col_write_B, col_write_C) = {
1160 let (vec_A, vec_BC) = col_prod_layer.write_vec.split_at_mut(1);
1161 let (vec_B, vec_C) = vec_BC.split_at_mut(1);
1162 (vec_A, vec_B, vec_C)
1163 };
1164
1165 let (dotp_left_A, dotp_left_B, dotp_left_C) = {
1166 let (vec_A, vec_BC) = dotp_circuit_left_vec.split_at_mut(1);
1167 let (vec_B, vec_C) = vec_BC.split_at_mut(1);
1168 (vec_A, vec_B, vec_C)
1169 };
1170
1171 let (dotp_right_A, dotp_right_B, dotp_right_C) = {
1172 let (vec_A, vec_BC) = dotp_circuit_right_vec.split_at_mut(1);
1173 let (vec_B, vec_C) = vec_BC.split_at_mut(1);
1174 (vec_A, vec_B, vec_C)
1175 };
1176
1177 let (proof_ops, rand_ops) = ProductCircuitEvalProofBatched::prove(
1178 &mut vec![
1179 &mut row_read_A[0],
1180 &mut row_read_B[0],
1181 &mut row_read_C[0],
1182 &mut row_write_A[0],
1183 &mut row_write_B[0],
1184 &mut row_write_C[0],
1185 &mut col_read_A[0],
1186 &mut col_read_B[0],
1187 &mut col_read_C[0],
1188 &mut col_write_A[0],
1189 &mut col_write_B[0],
1190 &mut col_write_C[0],
1191 ],
1192 &mut vec![
1193 &mut dotp_left_A[0],
1194 &mut dotp_right_A[0],
1195 &mut dotp_left_B[0],
1196 &mut dotp_right_B[0],
1197 &mut dotp_left_C[0],
1198 &mut dotp_right_C[0],
1199 ],
1200 transcript,
1201 );
1202
1203 // produce a batched proof of memory-related product circuits
1204 let (proof_mem, rand_mem) = ProductCircuitEvalProofBatched::prove(
1205 &mut vec![
1206 &mut row_prod_layer.init,
1207 &mut row_prod_layer.audit,
1208 &mut col_prod_layer.init,
1209 &mut col_prod_layer.audit,
1210 ],
1211 &mut Vec::new(),
1212 transcript,
1213 );
1214
1215 let product_layer_proof = ProductLayerProof {
1216 eval_row: (row_eval_init, row_eval_read, row_eval_write, row_eval_audit),
1217 eval_col: (col_eval_init, col_eval_read, col_eval_write, col_eval_audit),
1218 eval_val: (eval_dotp_left_vec, eval_dotp_right_vec),
1219 proof_mem,
1220 proof_ops,
1221 };
1222
1223 let mut product_layer_proof_encoded: Vec<u8> = Vec::new();
1224 product_layer_proof.serialize(&mut product_layer_proof_encoded).unwrap();
1225 let msg = format!(
1226 "len_product_layer_proof {:?}",
1227 product_layer_proof_encoded.len()
1228 );
1229 Timer::print(&msg);
1230
1231 (product_layer_proof, rand_mem, rand_ops)
1232 }
1233
1234 pub fn verify(
1235 &self,
1236 num_ops: usize,
1237 num_cells: usize,
1238 eval: &[Scalar],
1239 transcript: &mut Transcript,
1240 ) -> Result<
1241 (
1242 Vec<Scalar>,
1243 Vec<Scalar>,
1244 Vec<Scalar>,
1245 Vec<Scalar>,
1246 Vec<Scalar>,
1247 ),
1248 ProofVerifyError,
1249 > {
1250 transcript.append_protocol_name(ProductLayerProof::protocol_name());
1251
1252 let timer = Timer::new("verify_prod_proof");
1253 let num_instances = eval.len();
1254
1255 // subset check
1256 let (row_eval_init, row_eval_read, row_eval_write, row_eval_audit) = &self.eval_row;
1257 assert_eq!(row_eval_write.len(), num_instances);
1258 assert_eq!(row_eval_read.len(), num_instances);
1259 let ws: Scalar = (0..row_eval_write.len())
1260 .map(|i| row_eval_write[i])
1261 .product();
1262 let rs: Scalar = (0..row_eval_read.len()).map(|i| row_eval_read[i]).product();
1263 assert_eq!( ws * row_eval_init , rs * row_eval_audit);
1264
1265 row_eval_init.append_to_transcript(b"claim_row_eval_init", transcript);
1266 row_eval_read.append_to_transcript(b"claim_row_eval_read", transcript);
1267 row_eval_write.append_to_transcript(b"claim_row_eval_write", transcript);
1268 row_eval_audit.append_to_transcript(b"claim_row_eval_audit", transcript);
1269
1270 // subset check
1271 let (col_eval_init, col_eval_read, col_eval_write, col_eval_audit) = &self.eval_col;
1272 assert_eq!(col_eval_write.len(), num_instances);
1273 assert_eq!(col_eval_read.len(), num_instances);
1274 let ws: Scalar = (0..col_eval_write.len())
1275 .map(|i| col_eval_write[i])
1276 .product();
1277 let rs: Scalar = (0..col_eval_read.len()).map(|i| col_eval_read[i]).product();
1278 assert_eq!(ws * col_eval_init, rs * col_eval_audit);
1279
1280 col_eval_init.append_to_transcript(b"claim_col_eval_init", transcript);
1281 col_eval_read.append_to_transcript(b"claim_col_eval_read", transcript);
1282 col_eval_write.append_to_transcript(b"claim_col_eval_write", transcript);
1283 col_eval_audit.append_to_transcript(b"claim_col_eval_audit", transcript);
1284
1285 // verify the evaluation of the sparse polynomial
1286 let (eval_dotp_left, eval_dotp_right) = &self.eval_val;
1287 assert_eq!(eval_dotp_left.len(), eval_dotp_left.len());
1288 assert_eq!(eval_dotp_left.len(), num_instances);
1289 let mut claims_dotp_circuit: Vec<Scalar> = Vec::new();
1290 for i in 0..num_instances {
1291 assert_eq!(eval_dotp_left[i] + eval_dotp_right[i], eval[i]);
1292 eval_dotp_left[i].append_to_transcript(b"claim_eval_dotp_left", transcript);
1293 eval_dotp_right[i].append_to_transcript(b"claim_eval_dotp_right", transcript);
1294
1295 claims_dotp_circuit.push(eval_dotp_left[i]);
1296 claims_dotp_circuit.push(eval_dotp_right[i]);
1297 }
1298
1299 // verify the correctness of claim_row_eval_read, claim_row_eval_write, claim_col_eval_read, and claim_col_eval_write
1300 let mut claims_prod_circuit: Vec<Scalar> = Vec::new();
1301 claims_prod_circuit.extend(row_eval_read);
1302 claims_prod_circuit.extend(row_eval_write);
1303 claims_prod_circuit.extend(col_eval_read);
1304 claims_prod_circuit.extend(col_eval_write);
1305
1306 let (claims_ops, claims_dotp, rand_ops) = self.proof_ops.verify(
1307 &claims_prod_circuit,
1308 &claims_dotp_circuit,
1309 num_ops,
1310 transcript,
1311 );
1312 // verify the correctness of claim_row_eval_init and claim_row_eval_audit
1313 let (claims_mem, _claims_mem_dotp, rand_mem) = self.proof_mem.verify(
1314 &[
1315 *row_eval_init,
1316 *row_eval_audit,
1317 *col_eval_init,
1318 *col_eval_audit,
1319 ],
1320 &Vec::new(),
1321 num_cells,
1322 transcript,
1323 );
1324 timer.stop();
1325
1326 Ok((claims_mem, rand_mem, claims_ops, claims_dotp, rand_ops))
1327 }
1328}
1329
1330#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
1331struct PolyEvalNetworkProof {
1332 proof_prod_layer: ProductLayerProof,
1333 proof_hash_layer: HashLayerProof,
1334}
1335
1336impl PolyEvalNetworkProof {
1337 fn protocol_name() -> &'static [u8] {
1338 b"Sparse polynomial evaluation proof"
1339 }
1340
1341 pub fn prove(
1342 network: &mut PolyEvalNetwork,
1343 dense: &MultiSparseMatPolynomialAsDense,
1344 derefs: &Derefs,
1345 evals: &[Scalar],
1346 gens: &SparseMatPolyCommitmentGens,
1347 transcript: &mut Transcript,
1348 random_tape: &mut RandomTape,
1349 ) -> Self {
1350 transcript.append_protocol_name(PolyEvalNetworkProof::protocol_name());
1351
1352 let (proof_prod_layer, rand_mem, rand_ops) = ProductLayerProof::prove(
1353 &mut network.row_layers.prod_layer,
1354 &mut network.col_layers.prod_layer,
1355 dense,
1356 derefs,
1357 evals,
1358 transcript,
1359 );
1360
1361 // proof of hash layer for row and col
1362 let proof_hash_layer = HashLayerProof::prove(
1363 (&rand_mem, &rand_ops),
1364 dense,
1365 derefs,
1366 gens,
1367 transcript,
1368 random_tape,
1369 );
1370
1371 PolyEvalNetworkProof {
1372 proof_prod_layer,
1373 proof_hash_layer,
1374 }
1375 }
1376
1377 pub fn verify(
1378 &self,
1379 comm: &SparseMatPolyCommitment,
1380 comm_derefs: &DerefsCommitment,
1381 evals: &[Scalar],
1382 gens: &SparseMatPolyCommitmentGens,
1383 rx: &[Scalar],
1384 ry: &[Scalar],
1385 r_mem_check: &(Scalar, Scalar),
1386 nz: usize,
1387 transcript: &mut Transcript,
1388 ) -> Result<(), ProofVerifyError> {
1389 let timer = Timer::new("verify_polyeval_proof");
1390 transcript.append_protocol_name(PolyEvalNetworkProof::protocol_name());
1391
1392 let num_instances = evals.len();
1393 let (r_hash, r_multiset_check) = r_mem_check;
1394
1395 let num_ops = nz.next_power_of_two();
1396 let num_cells = rx.len().pow2();
1397 assert_eq!(rx.len(), ry.len());
1398
1399 let (claims_mem, rand_mem, mut claims_ops, claims_dotp, rand_ops) = self
1400 .proof_prod_layer
1401 .verify(num_ops, num_cells, evals, transcript)?;
1402 assert_eq!(claims_mem.len(), 4);
1403 assert_eq!(claims_ops.len(), 4 * num_instances);
1404 assert_eq!(claims_dotp.len(), 3 * num_instances);
1405
1406 let (claims_ops_row, claims_ops_col) = claims_ops.split_at_mut(2 * num_instances);
1407 let (claims_ops_row_read, claims_ops_row_write) = claims_ops_row.split_at_mut(num_instances);
1408 let (claims_ops_col_read, claims_ops_col_write) = claims_ops_col.split_at_mut(num_instances);
1409
1410 // verify the proof of hash layer
1411 assert!(self
1412 .proof_hash_layer
1413 .verify(
1414 (&rand_mem, &rand_ops),
1415 &(
1416 claims_mem[0],
1417 claims_ops_row_read.to_vec(),
1418 claims_ops_row_write.to_vec(),
1419 claims_mem[1],
1420 ),
1421 &(
1422 claims_mem[2],
1423 claims_ops_col_read.to_vec(),
1424 claims_ops_col_write.to_vec(),
1425 claims_mem[3],
1426 ),
1427 &claims_dotp,
1428 comm,
1429 gens,
1430 comm_derefs,
1431 rx,
1432 ry,
1433 r_hash,
1434 r_multiset_check,
1435 transcript
1436 )
1437 .is_ok());
1438 timer.stop();
1439
1440 Ok(())
1441 }
1442}
1443
1444#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
1445pub struct SparseMatPolyEvalProof {
1446 comm_derefs: DerefsCommitment,
1447 poly_eval_network_proof: PolyEvalNetworkProof,
1448}
1449
1450impl SparseMatPolyEvalProof {
1451 fn protocol_name() -> &'static [u8] {
1452 b"Sparse polynomial evaluation proof"
1453 }
1454
1455 fn equalize(rx: &[Scalar], ry: &[Scalar]) -> (Vec<Scalar>, Vec<Scalar>) {
1456 match rx.len().cmp(&ry.len()) {
1457 Ordering::Less => {
1458 let diff = ry.len() - rx.len();
1459 let mut rx_ext = vec![Scalar::zero(); diff];
1460 rx_ext.extend(rx);
1461 (rx_ext, ry.to_vec())
1462 }
1463 Ordering::Greater => {
1464 let diff = rx.len() - ry.len();
1465 let mut ry_ext = vec![Scalar::zero(); diff];
1466 ry_ext.extend(ry);
1467 (rx.to_vec(), ry_ext)
1468 }
1469 Ordering::Equal => (rx.to_vec(), ry.to_vec()),
1470 }
1471 }
1472
1473 pub fn prove(
1474 dense: &MultiSparseMatPolynomialAsDense,
1475 rx: &[Scalar], // point at which the polynomial is evaluated
1476 ry: &[Scalar],
1477 evals: &[Scalar], // a vector evaluation of \widetilde{M}(r = (rx,ry)) for each M
1478 gens: &SparseMatPolyCommitmentGens,
1479 transcript: &mut Transcript,
1480 random_tape: &mut RandomTape,
1481 ) -> SparseMatPolyEvalProof {
1482 transcript.append_protocol_name(SparseMatPolyEvalProof::protocol_name());
1483
1484 // ensure there is one eval for each polynomial in dense
1485 assert_eq!(evals.len(), dense.batch_size);
1486
1487 let (mem_rx, mem_ry) = {
1488 // equalize the lengths of rx and ry
1489 let (rx_ext, ry_ext) = SparseMatPolyEvalProof::equalize(rx, ry);
1490 let poly_rx = EqPolynomial::new(rx_ext).evals();
1491 let poly_ry = EqPolynomial::new(ry_ext).evals();
1492 (poly_rx, poly_ry)
1493 };
1494
1495 let derefs = dense.deref(&mem_rx, &mem_ry);
1496
1497 // commit to non-deterministic choices of the prover
1498 let timer_commit = Timer::new("commit_nondet_witness");
1499 let comm_derefs = {
1500 let comm = derefs.commit(&gens.gens_derefs);
1501 comm.append_to_transcript(b"comm_poly_row_col_ops_val", transcript);
1502 comm
1503 };
1504 timer_commit.stop();
1505
1506 let poly_eval_network_proof = {
1507 // produce a random element from the transcript for hash function
1508 let r_mem_check = transcript.challenge_vector(b"challenge_r_hash", 2);
1509
1510 // build a network to evaluate the sparse polynomial
1511 let timer_build_network = Timer::new("build_layered_network");
1512 let mut net = PolyEvalNetwork::new(
1513 dense,
1514 &derefs,
1515 &mem_rx,
1516 &mem_ry,
1517 &(r_mem_check[0], r_mem_check[1]),
1518 );
1519 timer_build_network.stop();
1520
1521 let timer_eval_network = Timer::new("evalproof_layered_network");
1522 let poly_eval_network_proof = PolyEvalNetworkProof::prove(
1523 &mut net,
1524 dense,
1525 &derefs,
1526 evals,
1527 gens,
1528 transcript,
1529 random_tape,
1530 );
1531 timer_eval_network.stop();
1532
1533 poly_eval_network_proof
1534 };
1535
1536 SparseMatPolyEvalProof {
1537 comm_derefs,
1538 poly_eval_network_proof,
1539 }
1540 }
1541
1542 pub fn verify(
1543 &self,
1544 comm: &SparseMatPolyCommitment,
1545 rx: &[Scalar], // point at which the polynomial is evaluated
1546 ry: &[Scalar],
1547 evals: &[Scalar], // evaluation of \widetilde{M}(r = (rx,ry))
1548 gens: &SparseMatPolyCommitmentGens,
1549 transcript: &mut Transcript,
1550 ) -> Result<(), ProofVerifyError> {
1551 transcript.append_protocol_name(SparseMatPolyEvalProof::protocol_name());
1552
1553 // equalize the lengths of rx and ry
1554 let (rx_ext, ry_ext) = SparseMatPolyEvalProof::equalize(rx, ry);
1555
1556 let (nz, num_mem_cells) = (comm.num_ops, comm.num_mem_cells);
1557 assert_eq!(rx_ext.len().pow2(), num_mem_cells);
1558
1559 // add claims to transcript and obtain challenges for randomized mem-check circuit
1560 self
1561 .comm_derefs
1562 .append_to_transcript(b"comm_poly_row_col_ops_val", transcript);
1563
1564 // produce a random element from the transcript for hash function
1565 let r_mem_check = transcript.challenge_vector(b"challenge_r_hash", 2);
1566
1567 self.poly_eval_network_proof.verify(
1568 comm,
1569 &self.comm_derefs,
1570 evals,
1571 gens,
1572 &rx_ext,
1573 &ry_ext,
1574 &(r_mem_check[0], r_mem_check[1]),
1575 nz,
1576 transcript,
1577 )
1578 }
1579}
1580
1581pub struct SparsePolyEntry {
1582 idx: usize,
1583 val: Scalar,
1584}
1585
1586impl SparsePolyEntry {
1587 pub fn new(idx: usize, val: Scalar) -> Self {
1588 SparsePolyEntry { idx, val }
1589 }
1590}
1591
1592pub struct SparsePolynomial {
1593 num_vars: usize,
1594 Z: Vec<SparsePolyEntry>,
1595}
1596
1597impl SparsePolynomial {
1598 pub fn new(num_vars: usize, Z: Vec<SparsePolyEntry>) -> Self {
1599 SparsePolynomial { num_vars, Z }
1600 }
1601
1602 fn compute_chi(a: &[bool], r: &[Scalar]) -> Scalar {
1603 assert_eq!(a.len(), r.len());
1604 let mut chi_i = Scalar::one();
1605 for j in 0..r.len() {
1606 if a[j] {
1607 chi_i *= r[j];
1608 } else {
1609 chi_i *= Scalar::one() - r[j];
1610 }
1611 }
1612 chi_i
1613 }
1614
1615 // Takes O(n log n). TODO: do this in O(n) where n is the number of entries in Z
1616 pub fn evaluate(&self, r: &[Scalar]) -> Scalar {
1617 assert_eq!(self.num_vars, r.len());
1618
1619 (0..self.Z.len())
1620 .map(|i| {
1621 let bits = self.Z[i].idx.get_bits(r.len());
1622 SparsePolynomial::compute_chi(&bits, r) * self.Z[i].val
1623 })
1624 .sum()
1625 }
1626}
1627
1628#[cfg(test)]
1629mod tests {
1630 use super::*;
1631use ark_std::{UniformRand};
1632use rand::RngCore;
1633
1634 #[test]
1635 fn check_sparse_polyeval_proof() {
1636 let mut rng = ark_std::rand::thread_rng();
1637
1638 let num_nz_entries: usize = 256;
1639 let num_rows: usize = 256;
1640 let num_cols: usize = 256;
1641 let num_vars_x: usize = num_rows.log2() as usize;
1642 let num_vars_y: usize = num_cols.log2() as usize;
1643
1644 let mut M: Vec<SparseMatEntry> = Vec::new();
1645
1646 for _i in 0..num_nz_entries {
1647 M.push(SparseMatEntry::new(
1648 (rng.next_u64()% (num_rows as u64)) as usize,
1649 (rng.next_u64() % (num_cols as u64)) as usize,
1650 Scalar::rand(&mut rng),
1651 ));
1652 }
1653
1654 let poly_M = SparseMatPolynomial::new(num_vars_x, num_vars_y, M);
1655 let gens = SparseMatPolyCommitmentGens::new(
1656 b"gens_sparse_poly",
1657 num_vars_x,
1658 num_vars_y,
1659 num_nz_entries,
1660 3,
1661 );
1662
1663 // commitment
1664 let (poly_comm, dense) = SparseMatPolynomial::multi_commit(&[&poly_M, &poly_M, &poly_M], &gens);
1665
1666 // evaluation
1667 let rx: Vec<Scalar> = (0..num_vars_x)
1668 .map(|_i| Scalar::rand(&mut rng))
1669 .collect::<Vec<Scalar>>();
1670 let ry: Vec<Scalar> = (0..num_vars_y)
1671 .map(|_i| Scalar::rand(&mut rng))
1672 .collect::<Vec<Scalar>>();
1673 let eval = SparseMatPolynomial::multi_evaluate(&[&poly_M], &rx, &ry);
1674 let evals = vec![eval[0], eval[0], eval[0]];
1675
1676 let mut random_tape = RandomTape::new(b"proof");
1677 let mut prover_transcript = Transcript::new(b"example");
1678 let proof = SparseMatPolyEvalProof::prove(
1679 &dense,
1680 &rx,
1681 &ry,
1682 &evals,
1683 &gens,
1684 &mut prover_transcript,
1685 &mut random_tape,
1686 );
1687
1688 let mut verifier_transcript = Transcript::new(b"example");
1689 assert!(proof
1690 .verify(
1691 &poly_comm,
1692 &rx,
1693 &ry,
1694 &evals,
1695 &gens,
1696 &mut verifier_transcript,
1697 )
1698 .is_ok());
1699 }
1700}