1use std::{cmp::min, marker::PhantomData};
15
16use midnight_proofs::{circuit::Layouter, plonk::Error};
17use num_bigint::BigUint;
18#[cfg(any(test, feature = "testing"))]
19use {
20 crate::testing_utils::FromScratch,
21 midnight_proofs::plonk::{Advice, Column, ConstraintSystem, Fixed, Instance},
22};
23
24use crate::{
25 field::AssignedNative, instructions::NativeInstructions, types::AssignedByte, CircuitField,
26};
27
28#[derive(Clone, Debug)]
29pub struct ParserGadget<F, N>
33where
34 F: CircuitField,
35 N: NativeInstructions<F>,
36{
37 pub(crate) native_gadget: N,
38 _marker: PhantomData<F>,
39}
40
41impl<F, N> ParserGadget<F, N>
42where
43 F: CircuitField,
44 N: NativeInstructions<F>,
45{
46 pub fn new(native_gadget: &N) -> Self {
48 Self {
49 native_gadget: native_gadget.clone(),
50 _marker: PhantomData,
51 }
52 }
53
54 fn get_subsequence(
66 &self,
67 layouter: &mut impl Layouter<F>,
68 sequence: &[AssignedNative<F>],
69 idx: &AssignedNative<F>,
70 len: usize,
71 ) -> Result<Vec<AssignedNative<F>>, Error> {
72 let native = &self.native_gadget;
73 let n = sequence.len();
74 native.assert_lower_than_fixed(layouter, idx, &BigUint::from(n - len + 1))?;
75
76 let default = self.native_gadget.assign_fixed(layouter, F::default())?;
77 let mut output = vec![default; len];
78
79 for i in 0..=(n - len) {
80 let b = native.is_equal_to_fixed(layouter, idx, F::from(i as u64))?;
81 for j in 0..len {
82 output[j] = native.select(layouter, &b, &sequence[i + j], &output[j])?;
83 }
84 }
85
86 Ok(output)
87 }
88
89 pub fn fetch_bytes(
101 &self,
102 layouter: &mut impl Layouter<F>,
103 sequence: &[AssignedByte<F>],
104 idx: &AssignedNative<F>,
105 len: usize,
106 ) -> Result<Vec<AssignedByte<F>>, Error> {
107 let native = &self.native_gadget;
108 let n = sequence.len();
109 native.assert_lower_than_fixed(layouter, idx, &BigUint::from(n - len + 1))?;
110
111 let nb_bytes_per_chunk = F::CAPACITY as usize / 8;
118 let nb_chunks = n.div_ceil(nb_bytes_per_chunk);
119
120 let mut chunks = Vec::with_capacity(nb_chunks + 1); for chunk_bytes in sequence.chunks(nb_bytes_per_chunk) {
122 let chunk = native.assigned_from_le_bytes(layouter, chunk_bytes)?;
123 chunks.push(chunk);
124 }
125
126 let (chunk_idx, fine_search_idx) = native.div_rem(
131 layouter,
132 idx,
133 nb_bytes_per_chunk.into(),
134 Some((1u64 << 18).into()),
135 )?;
136
137 let len_for_chunks = min(nb_chunks, 1 + len.div_ceil(nb_bytes_per_chunk));
140
141 let dummy = native.assign_fixed(layouter, F::default())?;
145 chunks.push(dummy);
146
147 let selected_chunks =
157 self.get_subsequence(layouter, &chunks, &chunk_idx, len_for_chunks)?;
158
159 let mut selected_bytes = Vec::with_capacity(len_for_chunks * 8);
163 for chunk in selected_chunks.iter() {
164 let bytes = native.assigned_to_le_bytes(layouter, chunk, Some(nb_bytes_per_chunk))?;
165 selected_bytes.extend(bytes);
166 }
167
168 let bytes_as_native: Vec<AssignedNative<F>> =
169 selected_bytes.into_iter().map(|byte| byte.into()).collect();
170 let output = self.get_subsequence(layouter, &bytes_as_native, &fine_search_idx, len)?;
171
172 output
173 .iter()
174 .map(|x| native.convert_unsafe(layouter, x))
175 .collect::<Result<Vec<_>, Error>>()
176 }
177}
178
179#[cfg(any(test, feature = "testing"))]
180impl<F, N> FromScratch<F> for ParserGadget<F, N>
181where
182 F: CircuitField,
183 N: NativeInstructions<F> + FromScratch<F>,
184{
185 type Config = <N as FromScratch<F>>::Config;
186
187 fn new_from_scratch(config: &Self::Config) -> Self {
188 let native_gadget = <N as FromScratch<F>>::new_from_scratch(config);
189 ParserGadget::<F, N>::new(&native_gadget)
190 }
191
192 fn configure_from_scratch(
193 meta: &mut ConstraintSystem<F>,
194 advice_columns: &mut Vec<Column<Advice>>,
195 fixed_columns: &mut Vec<Column<Fixed>>,
196 instance_columns: &[Column<Instance>; 2],
197 ) -> Self::Config {
198 <N as FromScratch<F>>::configure_from_scratch(
199 meta,
200 advice_columns,
201 fixed_columns,
202 instance_columns,
203 )
204 }
205
206 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
207 self.native_gadget.load_from_scratch(layouter)
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use ff::FromUniformBytes;
214 use midnight_proofs::{
215 circuit::{SimpleFloorPlanner, Value},
216 dev::MockProver,
217 plonk::Circuit,
218 };
219
220 use super::*;
221 use crate::field::{decomposition::chip::P2RDecompositionChip, NativeChip, NativeGadget};
222
223 #[derive(Clone, Debug)]
224 enum Operation {
225 GetSubseq,
226 FetchBytes,
227 }
228
229 #[derive(Clone, Debug)]
230 struct TestCircuit<F, N> {
231 sequence: Vec<Value<F>>,
232 idx: Value<F>,
233 expected: Vec<F>,
234 operation: Operation,
235 _marker: PhantomData<N>,
236 }
237
238 impl<F, N> Circuit<F> for TestCircuit<F, N>
239 where
240 F: CircuitField,
241 N: NativeInstructions<F> + FromScratch<F>,
242 {
243 type Config = <N as FromScratch<F>>::Config;
244 type FloorPlanner = SimpleFloorPlanner;
245 type Params = ();
246
247 fn without_witnesses(&self) -> Self {
248 unreachable!()
249 }
250
251 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
252 let committed_instance_column = meta.instance_column();
253 let instance_column = meta.instance_column();
254 <N as FromScratch<F>>::configure_from_scratch(
255 meta,
256 &mut vec![],
257 &mut vec![],
258 &[committed_instance_column, instance_column],
259 )
260 }
261
262 fn synthesize(
263 &self,
264 config: Self::Config,
265 mut layouter: impl Layouter<F>,
266 ) -> Result<(), Error> {
267 let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config);
268 let parser_gadget = ParserGadget::<F, N>::new(&native_gadget);
269
270 let sequence = native_gadget.assign_many(&mut layouter, &self.sequence)?;
271 let idx = native_gadget.assign(&mut layouter, self.idx)?;
272 let len = self.expected.len();
273
274 let res = match self.operation {
275 Operation::GetSubseq => {
276 parser_gadget.get_subsequence(&mut layouter, &sequence, &idx, len)
277 }
278 Operation::FetchBytes => {
279 let bytes = sequence
280 .iter()
281 .map(|x| native_gadget.convert(&mut layouter, x))
282 .collect::<Result<Vec<AssignedByte<F>>, Error>>()?;
283 let fetched = parser_gadget.fetch_bytes(&mut layouter, &bytes, &idx, len)?;
284 Ok(fetched.iter().map(|b| b.clone().into()).collect::<Vec<_>>())
285 }
286 }?;
287
288 assert_eq!(res.len(), len);
289 for (resulted, expected) in res.iter().zip(self.expected.iter()) {
290 native_gadget.assert_equal_to_fixed(&mut layouter, resulted, *expected)?;
291 }
292
293 native_gadget.load_from_scratch(&mut layouter)
294 }
295 }
296
297 fn run<F>(sequence: &[u8], idx: usize, expected: &[u8], operation: Operation, must_pass: bool)
298 where
299 F: CircuitField + FromUniformBytes<64> + Ord,
300 {
301 let circuit = TestCircuit::<F, NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>> {
302 sequence: sequence.iter().map(|x| F::from(*x as u64)).map(Value::known).collect(),
303 idx: Value::known(F::from(idx as u64)),
304 expected: expected.iter().map(|x| F::from(*x as u64)).collect(),
305 operation,
306 _marker: PhantomData,
307 };
308 let public_inputs = vec![vec![], vec![]];
309 match MockProver::run(&circuit, public_inputs) {
310 Ok(prover) => match prover.verify() {
311 Ok(()) => assert!(must_pass),
312 Err(e) => assert!(!must_pass, "Failed verifier with error {e:?}"),
313 },
314 Err(e) => assert!(!must_pass, "Failed prover with error {e:?}"),
315 }
316 }
317
318 #[test]
319 fn test_get_subsequence() {
320 type F = midnight_curves::Fq;
321 [
322 (vec![1, 2, 3, 4, 5, 6], 0, vec![1, 2, 3], true),
323 (vec![1, 2, 3, 4, 5, 6], 1, vec![2, 3, 4, 5], true),
324 (vec![1, 2, 4, 8, 16], 3, vec![8, 16], true),
325 (vec![1, 2, 4, 8, 16], 3, vec![8], true),
326 (vec![1, 2, 4, 8, 16], 3, vec![], true),
327 (vec![1, 2, 4, 8, 16], 6, vec![], false),
328 (vec![1, 2, 4, 8, 16], 5, vec![0], false),
329 (vec![1, 2, 4, 8, 16], 4, vec![0, 0], false),
330 (vec![1, 2, 4, 8, 16], 4, vec![16], true),
331 (vec![3, 14, 15, 9, 26, 53, 58], 5, vec![26], false),
332 (vec![3, 14, 15, 9, 26, 53, 58], 5, vec![53], true),
333 ]
334 .iter()
335 .for_each(|(sequence, idx, expected, must_pass)| {
336 run::<F>(sequence, *idx, expected, Operation::GetSubseq, *must_pass)
337 });
338 }
339
340 #[test]
341 fn test_fetch_bytes() {
342 type F = midnight_curves::Fq;
343 let short = "L'essentiel est invisible pour les yeux".as_bytes();
344 let long: Vec<u8> = (0..=2000).map(|i| i as u8).collect();
345 [
346 (short, 0, "L".as_bytes(), true),
347 (short, 12, "est".as_bytes(), true),
348 (short, 26, "pour".as_bytes(), true),
349 (short, 27, "our les yeu".as_bytes(), true),
350 (short, 35, "yeu".as_bytes(), true),
351 (short, 35, "yeux".as_bytes(), true),
352 (short, 38, "x".as_bytes(), true),
353 (short, 38, "".as_bytes(), true),
354 (short, 39, "".as_bytes(), true),
355 (short, 40, "".as_bytes(), false),
356 (&long, 0, &[0, 1, 2, 3, 4], true),
357 (&long, 256, &[0, 1, 2], true),
358 (&long, 1000, &[232, 233], true),
359 (
360 &long,
361 1234,
362 &(0..30).map(|i| (1234 + i) as u8).collect::<Vec<_>>(),
363 true,
364 ),
365 (&long, 1995, &[203, 204, 205], true),
366 (&long, 1995, &[203, 204, 205, 206, 207], true),
367 (&long, 1996, &[204, 205, 206, 207, 208], true),
368 (&long, 1997, &[205, 206, 207, 208, 209], false),
369 ]
370 .iter()
371 .for_each(|(bytes, idx, expected, must_pass)| {
372 run::<F>(bytes, *idx, expected, Operation::FetchBytes, *must_pass)
373 });
374 }
375}