1use ark_ff::{batch_inversion, FftField, Zero};
2use ark_poly::univariate::DensePolynomial;
3use ark_poly::{
4 DenseUVPolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, Polynomial,
5};
6use ark_std::{vec, vec::Vec};
7
8use crate::FieldColumn;
9
10pub const ZK_ROWS: usize = 3;
11
12#[derive(Clone)]
14struct Domains<F: FftField> {
15 x1: GeneralEvaluationDomain<F>,
16 x4: GeneralEvaluationDomain<F>,
17}
18
19impl<F: FftField> Domains<F> {
20 fn new(n: usize) -> Self {
21 let x1 = GeneralEvaluationDomain::<F>::new(n)
22 .unwrap_or_else(|| panic!("No domain of size {}", n));
23 let x4 = GeneralEvaluationDomain::<F>::new(4 * n)
24 .unwrap_or_else(|| panic!("No domain of size {}", 4 * n));
25 Self { x1, x4 }
26 }
27
28 fn column_from_evals(&self, evals: Vec<F>, len: usize) -> FieldColumn<F> {
29 assert_eq!(evals.len(), self.x1.size());
30 let evals = Evaluations::from_vec_and_domain(evals, self.x1);
31 let poly = evals.interpolate_by_ref();
32 let evals_4x = poly.evaluate_over_domain_by_ref(self.x4);
33 FieldColumn {
34 len,
35 poly,
36 evals,
37 evals_4x,
38 }
39 }
40
41 fn column_from_poly(&self, poly: DensePolynomial<F>, len: usize) -> FieldColumn<F> {
42 assert!(poly.degree() < self.x1.size());
43 let evals_4x = self.amplify(&poly);
44 let evals = evals_4x.evals.iter().step_by(4).cloned().collect();
45 let evals = Evaluations::from_vec_and_domain(evals, self.x1);
46 FieldColumn {
47 len,
48 poly,
49 evals,
50 evals_4x,
51 }
52 }
53
54 fn amplify(&self, poly: &DensePolynomial<F>) -> Evaluations<F> {
56 poly.evaluate_over_domain_by_ref(self.x4)
57 }
58}
59
60#[derive(Clone)]
61pub struct Domain<F: FftField> {
62 domains: Domains<F>,
63 pub hiding: bool,
64 pub capacity: usize,
65 pub not_last_row: FieldColumn<F>,
66 pub l_first: FieldColumn<F>,
67 pub l_last: FieldColumn<F>,
68 zk_rows_vanishing_poly: Option<DensePolynomial<F>>,
69}
70
71impl<F: FftField> Domain<F> {
72 pub fn new(n: usize, hiding: bool) -> Self {
73 let domains = Domains::new(n);
74 let size = domains.x1.size();
75 let capacity = if hiding { size - ZK_ROWS } else { size };
76 let last_row_index = capacity - 1;
77
78 let l_first = l_i(0, size);
79 let l_first = domains.column_from_evals(l_first, capacity);
80 let l_last = l_i(last_row_index, size);
81 let l_last = domains.column_from_evals(l_last, capacity);
82 let not_last_row = vanishes_on_row(last_row_index, domains.x1);
83 let not_last_row = domains.column_from_poly(not_last_row, capacity);
84
85 let zk_rows_vanishing_poly = hiding.then(|| vanishes_on_last_3_rows(domains.x1));
86
87 Self {
88 domains,
89 hiding,
90 capacity,
91 not_last_row,
92 l_first,
93 l_last,
94 zk_rows_vanishing_poly,
95 }
96 }
97
98 pub(crate) fn divide_by_vanishing_poly(&self, poly: &DensePolynomial<F>) -> DensePolynomial<F> {
99 let (quotient, remainder) = if self.hiding {
100 let exclude_zk_rows = poly * self.zk_rows_vanishing_poly.as_ref().unwrap();
101 exclude_zk_rows.divide_by_vanishing_poly(self.domains.x1)
102 } else {
103 poly.divide_by_vanishing_poly(self.domains.x1)
104 };
105 assert!(remainder.is_zero()); quotient
107 }
108
109 pub(crate) fn column(&self, mut evals: Vec<F>, hidden: bool) -> FieldColumn<F> {
110 let len = evals.len();
111 assert!(len <= self.capacity);
112 if self.hiding && hidden && !cfg!(feature = "test-vectors") {
113 evals.resize(self.capacity, F::zero());
114 evals.resize_with(self.domains.x1.size(), || {
115 F::rand(&mut getrandom_or_panic::getrandom_or_panic())
116 });
117 } else {
118 evals.resize(self.domains.x1.size(), F::zero());
119 }
120 self.domains.column_from_evals(evals, len)
121 }
122
123 pub fn private_column(&self, evals: Vec<F>) -> FieldColumn<F> {
124 self.column(evals, true)
125 }
126
127 pub fn public_column(&self, evals: Vec<F>) -> FieldColumn<F> {
129 self.column(evals, false)
130 }
131
132 pub fn omega(&self) -> F {
133 self.domains.x1.group_gen()
134 }
135
136 pub fn domain(&self) -> GeneralEvaluationDomain<F> {
137 self.domains.x1
138 }
139}
140
141fn l_i<F: FftField>(i: usize, n: usize) -> Vec<F> {
142 let mut l_i = vec![F::zero(); n];
143 l_i[i] = F::one();
144 l_i
145}
146
147fn vanishes_on_row<F: FftField>(
149 i: usize,
150 domain: GeneralEvaluationDomain<F>,
151) -> DensePolynomial<F> {
152 assert!(i < domain.size());
153 let w = domain.group_gen();
154 let wi = w.pow(&[i as u64]);
155 let wi = DensePolynomial::from_coefficients_slice(&[wi]);
156 let x = DensePolynomial::from_coefficients_slice(&[F::zero(), F::one()]);
157 &x - &wi
158}
159
160fn vanishes_on_last_3_rows<F: FftField>(domain: GeneralEvaluationDomain<F>) -> DensePolynomial<F> {
162 let w = domain.group_gen();
163 let n3 = (domain.size() - ZK_ROWS) as u64;
164 let w3 = w.pow(&[n3]);
165 let w2 = w3 * w;
166 let w1 = w2 * w;
167 assert_eq!(w1, domain.group_gen_inv());
168 let x = DensePolynomial::from_coefficients_slice(&[F::zero(), F::one()]); let c = |a: F| DensePolynomial::from_coefficients_slice(&[a]);
170 &(&(&x - &c(w3)) * &(&x - &c(w2))) * &(&x - &c(w1))
171}
172
173pub struct EvaluatedDomain<F: FftField> {
174 pub domain: GeneralEvaluationDomain<F>,
175 pub not_last_row: F,
176 pub l_first: F,
177 pub l_last: F,
178 pub vanishing_polynomial_inv: F,
179}
180
181impl<F: FftField> EvaluatedDomain<F> {
182 pub fn new(domain: GeneralEvaluationDomain<F>, z: F, hiding: bool) -> Self {
183 let k = if hiding { ZK_ROWS } else { 0 };
184 let mut z_n = z; for _ in 0..domain.log_size_of_group() {
186 z_n.square_in_place();
187 }
188 let z_n_minus_one = z_n - F::one(); let mut wi = domain.group_gen_inv();
192 let mut prod = F::one();
194 for _ in 0..k {
195 prod *= z - wi;
196 wi *= domain.group_gen_inv();
197 }
198 let not_last_row = z - wi;
200
201 let wj = domain.group_gen().pow([(k + 1) as u64]);
203
204 let mut inv = [z_n_minus_one, z - F::one(), wj * z - F::one()];
205 batch_inversion(&mut inv);
206
207 let vanishing_polynomial_inv = prod * inv[0];
208 let z_n_minus_one_div_n = z_n_minus_one * domain.size_inv();
209 let l_first = z_n_minus_one_div_n * inv[1];
210 let l_last = z_n_minus_one_div_n * inv[2];
211
212 Self {
213 domain,
214 not_last_row,
215 l_first,
216 l_last,
217 vanishing_polynomial_inv,
218 }
219 }
220
221 pub(crate) fn divide_by_vanishing_poly_in_zeta(&self, poly_in_zeta: F) -> F {
222 poly_in_zeta * self.vanishing_polynomial_inv
223 }
224
225 pub fn omega(&self) -> F {
226 self.domain.group_gen()
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use ark_ed_on_bls12_381_bandersnatch::Fq;
233 use ark_poly::Polynomial;
234 use ark_std::{test_rng, UniformRand};
235
236 use crate::domain::{Domain, EvaluatedDomain};
237
238 fn _test_evaluated_domain(hiding: bool) {
239 let rng = &mut test_rng();
240
241 let n = 1024;
243 let domain = Domain::new(n, hiding);
244 let z = Fq::rand(rng);
245 let domain_eval = EvaluatedDomain::new(domain.domain(), z, hiding);
246 assert_eq!(domain.l_first.poly.evaluate(&z), domain_eval.l_first);
247 assert_eq!(domain.l_last.poly.evaluate(&z), domain_eval.l_last);
248 assert_eq!(
249 domain.not_last_row.poly.evaluate(&z),
250 domain_eval.not_last_row
251 );
252 }
253
254 #[test]
255 fn test_evaluated_domain() {
256 _test_evaluated_domain(false);
257 _test_evaluated_domain(true);
258 }
259}