1use crate::FieldColumn;
2use ark_ff::{batch_inversion, FftField, Zero};
3use ark_poly::univariate::DensePolynomial;
4use ark_poly::{
5 DenseUVPolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, Polynomial,
6};
7use ark_std::{vec, vec::Vec};
8use getrandom_or_panic::getrandom_or_panic;
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, padded_evals: Vec<F>, payload_len: usize) -> FieldColumn<F> {
29 debug_assert_eq!(padded_evals.len(), self.x1.size());
30 let evals = Evaluations::from_vec_and_domain(padded_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 poly,
35 evals,
36 evals_4x,
37 payload_len,
38 }
39 }
40
41 fn column_from_poly(&self, poly: DensePolynomial<F>) -> FieldColumn<F> {
42 debug_assert!(poly.degree() + 1 <= 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 poly,
48 evals,
49 evals_4x,
50 payload_len: self.x1.size(),
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 domain_size = domains.x1.size();
75 let domain_capacity = if hiding {
76 domain_size - ZK_ROWS
77 } else {
78 domain_size
79 };
80 let last_row_index = domain_capacity - 1;
81
82 let l_first = l_i(0, domain_size);
83 let l_first = domains.column_from_evals(l_first, 0);
84 let l_last = l_i(last_row_index, domain_size);
85 let l_last = domains.column_from_evals(l_last, 0);
86 let not_last_row = vanishes_on_row(last_row_index, domains.x1);
87 let not_last_row = domains.column_from_poly(not_last_row);
88
89 let zk_rows_vanishing_poly = hiding.then(|| vanishes_on_last_3_rows(domains.x1));
90
91 Self {
92 domains,
93 hiding,
94 capacity: domain_capacity,
95 not_last_row,
96 l_first,
97 l_last,
98 zk_rows_vanishing_poly,
99 }
100 }
101
102 pub(crate) fn divide_by_vanishing_poly(&self, poly: &DensePolynomial<F>) -> DensePolynomial<F> {
103 let (quotient, remainder) = if self.hiding {
104 let exclude_zk_rows = poly * self.zk_rows_vanishing_poly.as_ref().unwrap();
105 exclude_zk_rows.divide_by_vanishing_poly(self.domains.x1)
106 } else {
107 poly.divide_by_vanishing_poly(self.domains.x1)
108 };
109 assert!(remainder.is_zero()); quotient
111 }
112
113 pub(crate) fn column(&self, mut values: Vec<F>, hidden: bool) -> FieldColumn<F> {
114 let payload_len = values.len();
115 debug_assert!(payload_len <= self.capacity);
116 values.resize(self.capacity, F::zero());
117 if self.hiding && hidden && !cfg!(feature = "test-vectors") {
118 values.resize_with(
119 self.domains.x1.size(),
120 || F::rand(&mut getrandom_or_panic()),
121 );
122 } else {
123 values.resize(self.domains.x1.size(), F::zero());
124 }
125 self.domains.column_from_evals(values, payload_len)
126 }
127
128 pub fn private_column(&self, values: Vec<F>) -> FieldColumn<F> {
129 self.column(values, true)
130 }
131
132 pub fn public_column(&self, evals: Vec<F>) -> FieldColumn<F> {
134 self.column(evals, false)
135 }
136
137 pub fn omega(&self) -> F {
138 self.domains.x1.group_gen()
139 }
140
141 pub fn domain(&self) -> GeneralEvaluationDomain<F> {
142 self.domains.x1
143 }
144
145 pub fn evaluate(&self, zeta: F) -> EvaluatedDomain<F> {
146 EvaluatedDomain::new(self.domain(), zeta, self.hiding)
147 }
148}
149
150fn l_i<F: FftField>(i: usize, n: usize) -> Vec<F> {
151 let mut l_i = vec![F::zero(); n];
152 l_i[i] = F::one();
153 l_i
154}
155
156fn vanishes_on_row<F: FftField>(
158 i: usize,
159 domain: GeneralEvaluationDomain<F>,
160) -> DensePolynomial<F> {
161 assert!(i < domain.size());
162 let w = domain.group_gen();
163 let wi = w.pow(&[i as u64]);
164 let wi = DensePolynomial::from_coefficients_slice(&[wi]);
165 let x = DensePolynomial::from_coefficients_slice(&[F::zero(), F::one()]);
166 &x - &wi
167}
168
169fn vanishes_on_last_3_rows<F: FftField>(domain: GeneralEvaluationDomain<F>) -> DensePolynomial<F> {
171 let w = domain.group_gen();
172 let n3 = (domain.size() - ZK_ROWS) as u64;
173 let w3 = w.pow(&[n3]);
174 let w2 = w3 * w;
175 let w1 = w2 * w;
176 assert_eq!(w1, domain.group_gen_inv());
177 let x = DensePolynomial::from_coefficients_slice(&[F::zero(), F::one()]); let c = |a: F| DensePolynomial::from_coefficients_slice(&[a]);
179 &(&(&x - &c(w3)) * &(&x - &c(w2))) * &(&x - &c(w1))
180}
181
182pub struct EvaluatedDomain<F: FftField> {
183 pub domain: GeneralEvaluationDomain<F>,
184 pub not_last_row: F,
185 pub l_first: F,
186 pub l_last: F,
187 pub vanishing_polynomial_inv: F,
188}
189
190impl<F: FftField> EvaluatedDomain<F> {
191 pub fn new(domain: GeneralEvaluationDomain<F>, z: F, hiding: bool) -> Self {
192 let k = if hiding { ZK_ROWS } else { 0 };
193 let mut z_n = z; for _ in 0..domain.log_size_of_group() {
195 z_n.square_in_place();
196 }
197 let z_n_minus_one = z_n - F::one(); let mut wi = domain.group_gen_inv();
201 let mut prod = F::one();
203 for _ in 0..k {
204 prod *= z - wi;
205 wi *= domain.group_gen_inv();
206 }
207 let not_last_row = z - wi;
209
210 let wj = domain.group_gen().pow([(k + 1) as u64]);
212
213 let mut inv = [z_n_minus_one, z - F::one(), wj * z - F::one()];
214 batch_inversion(&mut inv);
215
216 let vanishing_polynomial_inv = prod * inv[0];
217 let z_n_minus_one_div_n = z_n_minus_one * domain.size_inv();
218 let l_first = z_n_minus_one_div_n * inv[1];
219 let l_last = z_n_minus_one_div_n * inv[2];
220
221 Self {
222 domain,
223 not_last_row,
224 l_first,
225 l_last,
226 vanishing_polynomial_inv,
227 }
228 }
229
230 pub(crate) fn divide_by_vanishing_poly_in_zeta(&self, poly_in_zeta: F) -> F {
231 poly_in_zeta * self.vanishing_polynomial_inv
232 }
233
234 pub fn omega(&self) -> F {
235 self.domain.group_gen()
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use ark_ed_on_bls12_381_bandersnatch::Fq;
242 use ark_poly::Polynomial;
243 use ark_std::{test_rng, UniformRand};
244
245 use crate::domain::Domain;
246
247 fn _test_evaluated_domain(hiding: bool) {
248 let rng = &mut test_rng();
249
250 let n = 1024;
252 let domain = Domain::new(n, hiding);
253 let z = Fq::rand(rng);
254 let domain_eval = domain.evaluate(z);
255 assert_eq!(domain.l_first.poly.evaluate(&z), domain_eval.l_first);
256 assert_eq!(domain.l_last.poly.evaluate(&z), domain_eval.l_last);
257 assert_eq!(
258 domain.not_last_row.poly.evaluate(&z),
259 domain_eval.not_last_row
260 );
261 }
262
263 #[test]
264 fn test_evaluated_domain() {
265 _test_evaluated_domain(false);
266 _test_evaluated_domain(true);
267 }
268}