labrador/core/
aggregate.rs

1use crate::core::{env_params::EnvironmentParameters, statement::Statement};
2use crate::prover::{Challenges, Witness};
3use crate::ring::rq::Rq;
4use crate::ring::rq_vector::RqVector;
5use crate::ring::zq::Zq;
6
7/// First step of aggregation
8pub struct AggregationOne {
9    // a_{ij}^{''(k)}
10    pub a_ct_aggr: Vec<Vec<RqVector>>,
11    // \phi_{i}^{''(k)}
12    pub phi_ct_aggr: Vec<Vec<RqVector>>,
13    // b^{''(k)}
14    pub b_ct_aggr: RqVector,
15}
16
17impl AggregationOne {
18    pub fn new(
19        witness: &Witness,
20        st: &Statement,
21        ep: &EnvironmentParameters,
22        tr: &Challenges,
23    ) -> Self {
24        Self::aggregate(witness, st, ep, tr)
25    }
26
27    fn aggregate(
28        witness: &Witness,
29        st: &Statement,
30        ep: &EnvironmentParameters,
31        tr: &Challenges,
32    ) -> Self {
33        // calculate a_{ij}^{''(k)}
34        let a_ct_aggr: Vec<Vec<RqVector>> = Self::get_a_ct_aggr(&tr.psi, &st.a_ct, ep);
35
36        // calculate \phi_{i}^{''(k)}
37        let phi_ct_aggr: Vec<Vec<RqVector>> =
38            Self::get_phi_ct_aggr(&st.phi_ct, &tr.pi, &tr.psi, &tr.omega, ep);
39
40        // calculate b^{''(k)}
41        let b_ct_aggr: RqVector = Self::get_b_ct_aggr(&a_ct_aggr, &phi_ct_aggr, witness, ep);
42
43        Self {
44            a_ct_aggr,
45            phi_ct_aggr,
46            b_ct_aggr,
47        }
48    }
49
50    pub fn get_a_ct_aggr(
51        psi: &[Vec<Zq>],
52        a_ct: &[Vec<RqVector>],
53        ep: &EnvironmentParameters,
54    ) -> Vec<Vec<RqVector>> {
55        calculate_aggr_ct_a(psi, a_ct, ep)
56    }
57
58    pub fn get_phi_ct_aggr(
59        phi_ct: &[Vec<RqVector>],
60        pi: &[Vec<Vec<Zq>>],
61        psi: &[Vec<Zq>],
62        omega: &[Vec<Zq>],
63        ep: &EnvironmentParameters,
64    ) -> Vec<Vec<RqVector>> {
65        calculate_aggr_ct_phi(phi_ct, pi, psi, omega, ep)
66    }
67
68    pub fn get_b_ct_aggr(
69        a_ct_aggr: &[Vec<RqVector>],
70        phi_ct_aggr: &[Vec<RqVector>],
71        witness: &Witness,
72        ep: &EnvironmentParameters,
73    ) -> RqVector {
74        calculate_aggr_ct_b(a_ct_aggr, phi_ct_aggr, &witness.s, ep)
75    }
76}
77
78/// Second step of aggregation
79pub struct AggregationTwo {
80    // a_{ij}
81    pub a_i: Vec<RqVector>,
82    // \phi_{i}
83    pub phi_i: Vec<RqVector>,
84    // b
85    pub b_i: Rq,
86}
87
88impl AggregationTwo {
89    pub fn new(
90        aggr_one: &AggregationOne,
91        st: &Statement,
92        ep: &EnvironmentParameters,
93        tr: &Challenges,
94    ) -> Self {
95        Self::aggregate(aggr_one, st, ep, tr)
96    }
97
98    fn aggregate(
99        aggr_one: &AggregationOne,
100        st: &Statement,
101        ep: &EnvironmentParameters,
102        tr: &Challenges,
103    ) -> Self {
104        // calculate a_i
105        let a_i = Self::get_a_i(
106            &st.a_constraint,
107            &aggr_one.a_ct_aggr,
108            &tr.random_alpha,
109            &tr.random_beta,
110            ep,
111        );
112
113        // calculate phi_i
114        let phi_i = Self::get_phi_i(
115            &st.phi_constraint,
116            &aggr_one.phi_ct_aggr,
117            &tr.random_alpha,
118            &tr.random_beta,
119            ep,
120        );
121
122        // calculate b_i
123        let b_i = Self::get_b_i(
124            &st.b_constraint,
125            &aggr_one.b_ct_aggr,
126            &tr.random_alpha,
127            &tr.random_beta,
128            ep,
129        );
130
131        Self { a_i, phi_i, b_i }
132    }
133
134    pub fn get_a_i(
135        a_constraint: &[Vec<RqVector>],
136        a_ct_aggr: &[Vec<RqVector>],
137        random_alpha: &RqVector,
138        random_beta: &RqVector,
139        ep: &EnvironmentParameters,
140    ) -> Vec<RqVector> {
141        calculate_aggr_a(a_constraint, a_ct_aggr, random_alpha, random_beta, ep)
142    }
143
144    pub fn get_phi_i(
145        phi_constraint: &[Vec<RqVector>],
146        phi_ct_aggr: &[Vec<RqVector>],
147        random_alpha: &RqVector,
148        random_beta: &RqVector,
149        ep: &EnvironmentParameters,
150    ) -> Vec<RqVector> {
151        calculate_aggr_phi(phi_constraint, phi_ct_aggr, random_alpha, random_beta, ep)
152    }
153
154    pub fn get_b_i(
155        b_constraint: &RqVector,
156        b_ct_aggr: &RqVector,
157        random_alpha: &RqVector,
158        random_beta: &RqVector,
159        ep: &EnvironmentParameters,
160    ) -> Rq {
161        calculate_aggr_b(b_constraint, b_ct_aggr, random_alpha, random_beta, ep)
162    }
163}
164
165/// Calculate aprimes from aprime_l, a_{i,j}^{''k} = \sum_{l=1}^{L}\psi_l^{k}a_{ij}^{'(l)}
166///
167/// @param: random_psi: \psi_l^k
168/// @param: a_ct: a_{ij}^{'(l)}, each a_{ij} is a ring element (PolyRing)
169/// @param: ep: struct SizeParams
170///
171/// @return: a_{ij}^{'(k)}, return a vector length k of matrix a_{ij}
172#[rustfmt::skip]
173fn calculate_aggr_ct_a(
174    random_psi: &[Vec<Zq>],
175    a_ct: &[Vec<RqVector>],
176    ep: &EnvironmentParameters,
177) -> Vec<Vec<RqVector>> {
178    let aprimes: Vec<Vec<RqVector>> = (0..ep.kappa).map(|k| {
179        let psi_k = &random_psi[k];
180        (0..ep.r).map(|i| {
181            (0..ep.r).map(|j| {
182                // calculate a_{ij}^{'(l)} * \psi_l^k
183                (0..ep.constraint_l).map(|l| {
184                    &a_ct[l][i].get_elements()[j]
185                        * &psi_k[l]
186                })
187                .fold(
188                    // sum over all l
189                    Rq::zero(),
190                    |acc, val| acc + val,
191                )
192            }).collect::<RqVector>()
193        }).collect::<Vec<RqVector>>()
194    }).collect();
195
196    aprimes
197}
198
199/// calculate \phi_{i}^{''(k)} = \sum_{l=1}^{L}\psi_l^{k}\phi_{i}^{'(l)} + \sum(\omega_j^{k} * \sigma_{-1} * pi_i^{j})
200/// in the prover process, page 17 from the paper.
201///
202/// @param: phi_ct: \phi_{i}^{'(l)}
203/// @param: pi: pi_i^{j}
204/// @param: random_psi: \psi_l^{k}
205/// @param: random_omega: \omega_j^{k}
206/// @param: ep: struct SizeParams
207/// @param: security_level2: 256 in the paper
208///
209/// return: \phi_{i}^{''(k)}
210fn calculate_aggr_ct_phi(
211    phi_ct: &[Vec<RqVector>],
212    pi: &[Vec<Vec<Zq>>],
213    random_psi: &[Vec<Zq>],
214    random_omega: &[Vec<Zq>],
215    ep: &EnvironmentParameters,
216) -> Vec<Vec<RqVector>> {
217    let phi_ct_aggr: Vec<Vec<RqVector>> = (0..ep.kappa)
218        .map(|k| {
219            (0..ep.r)
220                .map(|i| {
221                    // \sum_{l=1}^{L}\psi_l^{k}\phi_{i}^{'(l)}
222                    let left_side = (0..ep.constraint_l)
223                        .map(|l| {
224                            phi_ct[l][i]
225                                .iter()
226                                .map(|phi| phi * &random_psi[k][l])
227                                .collect::<RqVector>()
228                        })
229                        .fold(RqVector::new(vec![Rq::zero(); ep.n]), |acc, val| {
230                            acc.iter().zip(val.iter()).map(|(a, b)| a + b).collect()
231                        });
232
233                    // Calculate the right side: \sum(\omega_j^{k} * \sigma_{-1} * pi_i^{j})
234                    // Because the length of pi is n*d, so we need to split it into n parts, each part has d elements to do the conjugate automorphism.
235                    let right_side = (0..(2 * ep.lambda))
236                        .map(|j| {
237                            let omega_j = random_omega[k][j];
238                            (0..ep.n)
239                                .map(|chunk_index| {
240                                    let start = chunk_index * Rq::DEGREE;
241                                    let end = start + Rq::DEGREE;
242
243                                    let pi_poly = Rq::new(pi[i][j][start..end].try_into().unwrap());
244                                    let pi_poly_conjugate = pi_poly.conjugate_automorphism();
245                                    &pi_poly_conjugate * &omega_j
246                                })
247                                .collect::<RqVector>()
248                        })
249                        .fold(RqVector::new(vec![Rq::zero(); ep.n]), |acc, val| {
250                            acc.iter().zip(val.iter()).map(|(a, b)| a + b).collect()
251                        });
252
253                    &left_side + &right_side
254                })
255                .collect::<Vec<RqVector>>()
256        })
257        .collect::<Vec<Vec<RqVector>>>();
258
259    phi_ct_aggr
260}
261
262/// calculate b^{''(k)} = \sum_{i,j=1}^{r} a_{ij}^{''(k)} * <s_i, s_j> + \sum_{i=1}^{r} <\phi_{i}^{''(k)} * s_i>
263///
264/// @param: a_ct_aggr: a_{ij}^{''(k)}
265/// @param: phi_ct_aggr: \phi_{i}^{''(k)}
266/// @param: witness: s_i
267/// @param: ep: struct SizeParams
268///
269/// @return: b^{''(k)}
270#[rustfmt::skip]
271fn calculate_aggr_ct_b(
272    a_ct_aggr: &[Vec<RqVector>],
273    phi_ct_aggr: &[Vec<RqVector>],
274    witness: &[RqVector],
275    ep: &EnvironmentParameters,
276) -> RqVector {
277    (0..ep.kappa).map(|k| {
278        (0..ep.r).map(|i| {
279            (0..ep.r).map(|j| {
280                // calculate a_{ij}^{''(k)} * <s_i, s_j>
281                a_ct_aggr[k][i].get_elements()[j].clone()
282                    * witness[i].inner_product_poly_vector(&witness[j])
283            })
284            .fold(
285                // sum over all i,j
286                Rq::zero(),
287                |acc, val| acc + val,
288            )
289            // add \phi_{i}^{''(k)} * s[i]
290            + phi_ct_aggr[k][i].inner_product_poly_vector(&witness[i])
291        }) // sum over all i,j
292        .fold(Rq::zero(), |acc, val| {
293            acc + val
294        })
295    }).collect()
296}
297
298/// calculate a_i = \sum(alpha_k * a_{ij}) + \sum(beta_k * a_{ij}^{''(k)})
299/// equation 5, in the verifier process, page 18 from the paper.
300///
301/// @param: a_constraint: a_{ij}
302/// @param: a_ct_aggr: a_{ij}^{''(k)}
303/// @param: random_alpha: alpha_k
304/// @param: random_beta: beta_k
305/// @param: ep: struct SizeParams
306///
307/// @return: a_i
308fn calculate_aggr_a(
309    a_constraint: &[Vec<RqVector>],
310    a_ct_aggr: &[Vec<RqVector>],
311    random_alpha: &RqVector,
312    random_beta: &RqVector,
313    ep: &EnvironmentParameters,
314) -> Vec<RqVector> {
315    let a_i: Vec<RqVector> = (0..ep.r)
316        .map(|i| {
317            (0..ep.r)
318                .map(|j| {
319                    // calculate \sum(alpha_k * a_{ij}), k is constraint_k
320                    let left_side = (0..ep.constraint_k)
321                        .map(|k| {
322                            a_constraint[k][i].get_elements()[j].clone()
323                                * random_alpha.get_elements()[k].clone()
324                        })
325                        .fold(Rq::zero(), |acc, val| acc + val);
326
327                    // calculate \sum(beta_k * a_{ij}^{''(k)}), k is size k
328                    let right_side = (0..ep.kappa)
329                        .map(|k| {
330                            a_ct_aggr[k][i].get_elements()[j].clone()
331                                * random_beta.get_elements()[k].clone()
332                        })
333                        .fold(Rq::zero(), |acc, val| acc + val);
334
335                    left_side + right_side
336                })
337                .collect::<RqVector>()
338        })
339        .collect::<Vec<RqVector>>();
340
341    a_i
342}
343
344/// calculate phi_i = \sum(alpha_k * \phi_{i}^{k}) + \sum(beta_k * \phi_{i}^{''(k)})
345/// equation 6, in the verifier process, page 18 from the paper.
346///
347/// param: phi_constraint: \phi_{i}^{k}
348/// param: phi_ct_aggr: \phi_{i}^{''(k)}
349/// param: random_alpha: alpha_k
350/// param: random_beta: beta_k
351/// param: ep: struct SizeParams
352///
353/// return: phi_i
354fn calculate_aggr_phi(
355    phi_constraint: &[Vec<RqVector>],
356    phi_ct_aggr: &[Vec<RqVector>],
357    random_alpha: &RqVector,
358    random_beta: &RqVector,
359    ep: &EnvironmentParameters,
360) -> Vec<RqVector> {
361    let phi_i: Vec<RqVector> = (0..ep.r)
362        .map(|i| {
363            // calculate \sum(alpha_k * \phi_{i}^{k})
364            let left_side: RqVector = (0..ep.constraint_k)
365                .map(|k| {
366                    phi_constraint[k][i]
367                        .iter()
368                        .map(|phi| phi * &random_alpha.get_elements()[k])
369                        .collect::<RqVector>()
370                })
371                .fold(RqVector::new(vec![Rq::zero(); ep.n]), |acc, val| {
372                    acc.iter().zip(val.iter()).map(|(a, b)| a + b).collect()
373                });
374
375            // calculate \sum(beta_k * \phi_{i}^{''(k)})
376            let right_side: RqVector = (0..ep.kappa)
377                .map(|k| {
378                    phi_ct_aggr[k][i]
379                        .iter()
380                        .map(|phi| phi * &random_beta.get_elements()[k])
381                        .collect::<RqVector>()
382                })
383                .fold(RqVector::new(vec![Rq::zero(); ep.n]), |acc, val| {
384                    acc.iter().zip(val.iter()).map(|(a, b)| a + b).collect()
385                });
386
387            &left_side + &right_side
388        })
389        .collect::<Vec<RqVector>>();
390
391    phi_i
392}
393
394/// calculate b_i = \sum(alpha_k * b^{k}) + \sum(beta_k * b^{''(k})
395/// equation 7, in the verifier process, page 18 from the paper.
396///
397/// @param: b_constraint: b^{k}
398/// @param: b_ct_aggr: b^{''(k)}
399/// @param: random_alpha: alpha_k
400/// @param: random_beta: beta_k
401/// @param: ep: struct SizeParams
402///
403/// @return: b_i
404fn calculate_aggr_b(
405    b_constraint: &RqVector,
406    b_ct_aggr: &RqVector,
407    random_alpha: &RqVector,
408    random_beta: &RqVector,
409    ep: &EnvironmentParameters,
410) -> Rq {
411    let left_side = (0..ep.constraint_k)
412        .map(|k| b_constraint.get_elements()[k].clone() * random_alpha.get_elements()[k].clone())
413        .fold(Rq::zero(), |acc, val| acc + val);
414
415    let right_side = (0..ep.kappa)
416        .map(|k| b_ct_aggr.get_elements()[k].clone() * random_beta.get_elements()[k].clone())
417        .fold(Rq::zero(), |acc, val| acc + val);
418
419    left_side + right_side
420}
421
422/// calculate h_{ij} = 1/2 * (<\phi_i, s_j> + <\phi_j, s_i>), then use base b to decompose the polynomial
423///
424/// @param: phi_i: phi_i
425/// @param: s: witness s_i
426/// @param: ep: struct SizeParams
427///
428/// return h_{ij}
429pub fn calculate_hij(
430    phi_i: &[RqVector],
431    s: &[RqVector],
432    ep: &EnvironmentParameters,
433) -> Vec<RqVector> {
434    (0..ep.r)
435        .map(|i| {
436            (0..ep.r)
437                .map(|j| {
438                    let left_side = &phi_i[i].inner_product_poly_vector(&s[j]);
439                    let right_side = &phi_i[j].inner_product_poly_vector(&s[i]);
440                    left_side + right_side
441                })
442                .collect()
443        })
444        .collect()
445}
446
447/// calculate garbage polynomial g = <s_i, s_j>
448///
449/// @param: phi_i: phi_i
450/// @param: s: witness s_i
451/// @param: r: size_r
452///
453/// return g_{ij}
454pub fn calculate_gij(s: &[RqVector], r: usize) -> Vec<RqVector> {
455    (0..r)
456        .map(|i| {
457            (0..r)
458                .map(|j| s[i].inner_product_poly_vector(&s[j]))
459                .collect()
460        })
461        .collect()
462}
463
464/// calculate z = c_1*s_1 + ... + c_r*s_r
465/// or calculate Az = c_1*t_1 + ... + c_r*t_r
466///
467/// @param: x: witness s_i or Ajtai commitments t_i
468/// @param: random_c: c_i from challenge set
469///
470/// return z
471pub fn calculate_z(x: &[RqVector], random_c: &RqVector) -> RqVector {
472    x.iter()
473        .zip(random_c.iter())
474        .map(|(s_row, c_element)| s_row * c_element)
475        .fold(
476            RqVector::new(vec![Rq::zero(); x[0].get_elements().len()]),
477            |acc, x| &acc + &x,
478        )
479}
480
481/// line 19, page 18: calculate u_1 = \sum(B_ik * t_i^(k)) + sum(C_ijk * g_ij^(k))
482pub fn calculate_u_1(
483    b: &[Vec<Vec<RqVector>>],
484    c: &[Vec<Vec<Vec<RqVector>>>],
485    t_i: &[Vec<RqVector>],
486    g_ij: &[Vec<RqVector>],
487    ep: &EnvironmentParameters,
488) -> RqVector {
489    let mut u_1 = vec![Rq::zero(); ep.kappa_1];
490    // calculate left side
491    for i in 0..ep.r {
492        for k in 0..ep.t_1 {
493            let b_ik_t_ik = &t_i[i][k] * &b[i][k];
494            u_1 = u_1
495                .iter()
496                .zip(b_ik_t_ik.iter())
497                .map(|(a, b)| a + b)
498                .collect();
499        }
500    }
501    // calculate right side
502    for i in 0..ep.r {
503        for j in i..ep.r {
504            for k in 0..ep.t_2 {
505                let c_ijk_g_ij = &g_ij[i][j] * &c[i][j][k];
506                u_1 = u_1
507                    .iter()
508                    .zip(c_ijk_g_ij.iter())
509                    .map(|(a, b)| a + b)
510                    .collect();
511            }
512        }
513    }
514
515    RqVector::new(u_1)
516}
517
518/// line 20, page 18: calculate u_2 = \sum(D_ijk * h_ij^(k))
519pub fn calculate_u_2(
520    d: &[Vec<Vec<Vec<RqVector>>>],
521    h_ij: &[Vec<RqVector>],
522    ep: &EnvironmentParameters,
523) -> RqVector {
524    // Pre-collect the iterator over (i, j, k) into a vector.
525    let flat_vec: Vec<(usize, usize, usize)> = (0..ep.r)
526        .flat_map(|i| (i..ep.r).flat_map(move |j| (0..ep.t_2).map(move |k| (i, j, k))))
527        .collect();
528
529    flat_vec
530        .into_iter()
531        .fold(RqVector::new(vec![Rq::zero(); ep.n]), |acc, (i, j, k)| {
532            let d_ijk_h_ij = &h_ij[i][j] * &d[i][j][k];
533            acc.iter()
534                .zip(d_ijk_h_ij.iter())
535                .map(|(a, b)| a + b)
536                .collect::<RqVector>()
537        })
538}
539
540// #[cfg(test)]
541// mod tests {
542//     use super::*;
543//     use crate::prover::Challenges;
544//     use crate::verifier::LabradorVerifier;
545//     #[test]
546//     fn test_check_relation_full() {
547//         // set up example environment, use set1 for testing.
548//         let ep = EnvironmentParameters::default();
549//         // generate a random witness based on ep above
550//         let witness_1 = Witness::new(&ep);
551//         // generate public statements based on witness_1
552//         let st = Statement::new(&witness_1, &ep);
553//         // generate random challenges
554//         let tr = Challenges::new(&ep);
555//         // first aggregation
556//         let aggr_1 = AggregationOne::new(&witness_1, &st, &ep, &tr);
557//         // second aggregation
558//         let aggr_2 = AggregationTwo::new(&aggr_1, &st, &ep, &tr);
559
560//         // calculate garbage polynomial g_{ij} = <s_i, s_j>
561//         let g = (0..ep.r)
562//             .map(|i| {
563//                 (0..ep.r)
564//                     .map(|j| witness_1.s[i].inner_product_poly_vector(&witness_1.s[j]))
565//                     .collect()
566//             })
567//             .collect();
568
569//         // calculate h_{ii}
570//         let h = (0..ep.r)
571//             .map(|i| {
572//                 (0..ep.r)
573//                     .map(|j| {
574//                         let inner_phii_sj =
575//                             aggr_2.phi_i[i].inner_product_poly_vector(&witness_1.s[j]);
576//                         let inner_phij_si =
577//                             aggr_2.phi_i[j].inner_product_poly_vector(&witness_1.s[i]);
578//                         &inner_phii_sj + &inner_phij_si
579//                     })
580//                     .collect()
581//             })
582//             .collect();
583
584//         // check aggregation relation
585//         let relation = LabradorVerifier::check_relation(&aggr_2.a_i, &aggr_2.b_i, &g, &h);
586
587//         assert!(relation);
588//     }
589// }