1use crate::encoding::{decode_into, encoding_size};
2use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM};
3use ark_ff::PrimeField;
4use ark_poly::{
5 univariate::DensePolynomial, EvaluationDomain, Evaluations, Radix2EvaluationDomain as R2D,
6};
7use o1_utils::field_helpers::pows;
8use std::marker::PhantomData;
9use thiserror::Error;
10use tracing::instrument;
11
12pub(crate) fn evals_to_polynomial<F: PrimeField>(
13 evals: Vec<F>,
14 domain: R2D<F>,
15) -> DensePolynomial<F> {
16 Evaluations::from_vec_and_domain(evals, domain).interpolate_by_ref()
17}
18
19#[derive(Clone, Debug)]
20pub struct QueryBytes {
22 pub start: usize,
23 pub len: usize,
24}
25
26#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug)]
27struct FieldElt {
30 poly_index: usize,
32 eval_index: usize,
34 domain_size: usize,
35 n_polys: usize,
36}
37#[derive(Debug)]
39pub struct QueryField<F> {
40 start: FieldElt,
41 leftover_start: usize,
44 end: FieldElt,
45 leftover_end: usize,
48 tag: PhantomData<F>,
49}
50
51impl<F: PrimeField> QueryField<F> {
52 #[instrument(skip_all, level = "debug")]
53 pub fn apply(self, data: &[Vec<F>]) -> Vec<u8> {
54 let mut buffer = vec![0u8; encoding_size::<F>()];
55 let mut answer = Vec::new();
56 self.start
57 .into_iter()
58 .take_while(|x| x <= &self.end)
59 .for_each(|x| {
60 let value = data[x.poly_index][x.eval_index];
61 decode_into(&mut buffer, value);
62 answer.extend_from_slice(&buffer);
63 });
64
65 answer[(self.leftover_start)..(answer.len() - self.leftover_end)].to_vec()
66 }
67}
68
69impl Iterator for FieldElt {
70 type Item = FieldElt;
71 fn next(&mut self) -> Option<Self::Item> {
72 let current = *self;
73
74 if (self.eval_index + 1) < self.domain_size {
75 self.eval_index += 1;
76 } else if (self.poly_index + 1) < self.n_polys {
77 self.poly_index += 1;
78 self.eval_index = 0;
79 } else {
80 return None;
81 }
82
83 Some(current)
84 }
85}
86
87#[derive(Debug, Error, Clone, PartialEq)]
88pub enum QueryError {
89 #[error("Query out of bounds: poly_index {poly_index} eval_index {eval_index} n_polys {n_polys} domain_size {domain_size}")]
90 QueryOutOfBounds {
91 poly_index: usize,
92 eval_index: usize,
93 n_polys: usize,
94 domain_size: usize,
95 },
96}
97
98impl QueryBytes {
99 pub fn into_query_field<F: PrimeField>(
100 &self,
101 domain_size: usize,
102 n_polys: usize,
103 ) -> Result<QueryField<F>, QueryError> {
104 let n = encoding_size::<F>();
105 let start = {
106 let start_field_nb = self.start / n;
107 FieldElt {
108 poly_index: start_field_nb / domain_size,
109 eval_index: start_field_nb % domain_size,
110 domain_size,
111 n_polys,
112 }
113 };
114 let byte_end = self.start + self.len;
115 let end = {
116 let end_field_nb = byte_end / n;
117 FieldElt {
118 poly_index: end_field_nb / domain_size,
119 eval_index: end_field_nb % domain_size,
120 domain_size,
121 n_polys,
122 }
123 };
124
125 if start.poly_index >= n_polys || end.poly_index >= n_polys {
126 return Err(QueryError::QueryOutOfBounds {
127 poly_index: end.poly_index,
128 eval_index: end.eval_index,
129 n_polys,
130 domain_size,
131 });
132 };
133
134 let leftover_start = self.start % n;
135 let leftover_end = n - byte_end % n;
136
137 Ok(QueryField {
138 start,
139 leftover_start,
140 end,
141 leftover_end,
142 tag: std::marker::PhantomData,
143 })
144 }
145}
146
147#[cfg(test)]
148pub mod test_utils {
149 use proptest::prelude::*;
150
151 #[derive(Debug, Clone)]
152 pub struct UserData(pub Vec<u8>);
153
154 impl UserData {
155 pub fn len(&self) -> usize {
156 self.0.len()
157 }
158
159 pub fn is_empty(&self) -> bool {
160 self.0.is_empty()
161 }
162 }
163
164 #[derive(Clone, Debug)]
165 pub enum DataSize {
166 Small,
167 Medium,
168 Large,
169 }
170
171 impl DataSize {
172 const KB: usize = 1_000;
173 const MB: usize = 1_000_000;
174
175 fn size_range_bytes(&self) -> (usize, usize) {
176 match self {
177 Self::Small => (Self::KB, Self::MB),
179 Self::Medium => (Self::MB, 10 * Self::MB),
181 Self::Large => (10 * Self::MB, 100 * Self::MB),
183 }
184 }
185 }
186
187 impl Arbitrary for DataSize {
188 type Parameters = ();
189 type Strategy = BoxedStrategy<Self>;
190
191 fn arbitrary_with(_: ()) -> Self::Strategy {
192 prop_oneof![
193 6 => Just(DataSize::Small), 3 => Just(DataSize::Medium),
195 1 => Just(DataSize::Large)
196 ]
197 .boxed()
198 }
199 }
200
201 impl Default for DataSize {
202 fn default() -> Self {
203 Self::Small
204 }
205 }
206
207 impl Arbitrary for UserData {
208 type Parameters = DataSize;
209 type Strategy = BoxedStrategy<Self>;
210
211 fn arbitrary() -> Self::Strategy {
212 DataSize::arbitrary()
213 .prop_flat_map(|size| {
214 let (min, max) = size.size_range_bytes();
215 prop::collection::vec(any::<u8>(), min..max)
216 })
217 .prop_map(UserData)
218 .boxed()
219 }
220
221 fn arbitrary_with(size: Self::Parameters) -> Self::Strategy {
222 let (min, max) = size.size_range_bytes();
223 prop::collection::vec(any::<u8>(), min..max)
224 .prop_map(UserData)
225 .boxed()
226 }
227 }
228}
229
230pub fn min_encoding_chunks<F: PrimeField, D: EvaluationDomain<F>>(domain: &D, xs: &[u8]) -> usize {
232 let m = encoding_size::<F>();
233 let n = xs.len();
234 let num_field_elems = (n + m - 1) / m;
235 (num_field_elems + domain.size() - 1) / domain.size()
236}
237
238pub fn chunk_size_in_bytes<F: PrimeField, D: EvaluationDomain<F>>(domain: &D) -> usize {
239 let m = encoding_size::<F>();
240 domain.size() * m
241}
242
243pub fn aggregate_commitments<G: AffineRepr>(randomness: G::ScalarField, commitments: &[G]) -> G {
245 let powers_of_randomness = pows(commitments.len(), randomness);
247 let aggregated_commitment =
248 G::Group::msm(commitments, powers_of_randomness.as_slice()).unwrap().into_affine();
251 aggregated_commitment
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use crate::encoding::encode_for_domain;
258 use ark_poly::Radix2EvaluationDomain;
259 use mina_curves::pasta::Fp;
260 use once_cell::sync::Lazy;
261 use proptest::prelude::*;
262 use test_utils::{DataSize, UserData};
263 use tracing::debug;
264
265 static DOMAIN: Lazy<Radix2EvaluationDomain<Fp>> = Lazy::new(|| {
266 const SRS_SIZE: usize = 1 << 16;
267 Radix2EvaluationDomain::new(SRS_SIZE).unwrap()
268 });
269
270 fn padded_field_length(xs: &[u8]) -> usize {
272 let n = min_encoding_chunks(&*DOMAIN, xs);
273 n * DOMAIN.size()
274 }
275
276 proptest! {
277 #![proptest_config(ProptestConfig::with_cases(20))]
278 #[test]
279 fn test_padded_byte_length(UserData(xs) in UserData::arbitrary()
280 )
281 { let chunked = encode_for_domain::<Fp>(DOMAIN.size(), &xs);
282 let n = chunked.into_iter().flatten().count();
283 prop_assert_eq!(n, padded_field_length(&xs));
284 }
285 }
286
287 proptest! {
288 #![proptest_config(ProptestConfig::with_cases(20))]
289 #[test]
290 fn test_query(
291 (UserData(xs), queries) in UserData::arbitrary()
292 .prop_flat_map(|xs| {
293 let n = xs.len();
294 let query_strategy = (0..(n - 1)).prop_flat_map(move |start| {
295 ((start + 1)..n).prop_map(move |end| QueryBytes { start, len: end - start})
296 });
297 let queries_strategy = prop::collection::vec(query_strategy, 10);
298 (Just(xs), queries_strategy)
299 })
300 ) {
301 let chunked = encode_for_domain(DOMAIN.size(), &xs);
302 for query in queries {
303 let expected = &xs[query.start..(query.start+query.len)];
304 let field_query: QueryField<Fp> = query.into_query_field(DOMAIN.size(), chunked.len()).unwrap();
305 let got_answer = field_query.apply(&chunked);
306 prop_assert_eq!(expected, got_answer);
307 }
308 }
309 }
310
311 proptest! {
312 #![proptest_config(ProptestConfig::with_cases(20))]
313 #[test]
314 fn test_for_invalid_query_length(
315 (UserData(xs), mut query) in UserData::arbitrary()
316 .prop_flat_map(|UserData(xs)| {
317 let padded_len = {
318 let m = Fp::MODULUS_BIT_SIZE as usize / 8;
319 padded_field_length(&xs) * m
320 };
321 let query_strategy = (0..xs.len()).prop_map(move |start| {
322 let end = padded_len - 1;
324 QueryBytes { start, len: end - start }
325 });
326 (Just(UserData(xs)), query_strategy)
327 })
328 ) {
329 debug!("check that first query is valid");
330 let chunked = encode_for_domain::<Fp>(DOMAIN.size(), &xs);
331 let n_polys = chunked.len();
332 let query_field = query.into_query_field::<Fp>(DOMAIN.size(), n_polys);
333 prop_assert!(query_field.is_ok());
334 debug!("check that extending query length by 1 is invalid");
335 query.len += 1;
336 let query_field = query.into_query_field::<Fp>(DOMAIN.size(), n_polys);
337 prop_assert!(query_field.is_err());
338
339 }
340 }
341
342 proptest! {
343 #![proptest_config(ProptestConfig::with_cases(20))]
344 #[test]
345 fn test_nil_query(
346 (UserData(xs), query) in UserData::arbitrary_with(DataSize::Small)
347 .prop_flat_map(|xs| {
348 let padded_len = {
349 let m = Fp::MODULUS_BIT_SIZE as usize / 8;
350 padded_field_length(&xs.0) * m
351 };
352 let query_strategy = (0..padded_len).prop_map(move |start| {
353 QueryBytes { start, len: 0 }
354 });
355 (Just(xs), query_strategy)
356 })
357 ) {
358 let chunked = encode_for_domain(DOMAIN.size(), &xs);
359 let n_polys = chunked.len();
360 let field_query: QueryField<Fp> = query.into_query_field(DOMAIN.size(), n_polys).unwrap();
361 let got_answer = field_query.apply(&chunked);
362 prop_assert!(got_answer.is_empty());
363 }
364
365 }
366}