labrador/core/
aggregate.rs

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