1use midnight_proofs::{circuit::Layouter, plonk::Error};
17use num_bigint::BigUint;
18
19use super::{varlen::ScannerVec, ScannerChip, PARSING_MAX_LEN_BITS};
20use crate::{
21 field::AssignedNative,
22 instructions::{AssertionInstructions, AssignmentInstructions, RangeCheckInstructions},
23 types::AssignedByte,
24 vec::get_lims,
25 CircuitField,
26};
27
28impl<F> ScannerChip<F>
29where
30 F: CircuitField + Ord,
31{
32 pub fn check_bytes_varlen<
37 const M_SEQ: usize,
38 const A_SEQ: usize,
39 const M_SUB: usize,
40 const A_SUB: usize,
41 >(
42 &self,
43 layouter: &mut impl Layouter<F>,
44 sequence: &ScannerVec<F, M_SEQ, A_SEQ>,
45 idx: &AssignedNative<F>,
46 sub: &ScannerVec<F, M_SUB, A_SUB>,
47 ) -> Result<(), Error> {
48 let ng = &self.native_gadget;
49
50 ng.assert_lower_than_fixed(layouter, idx, &(BigUint::from(1u8) << PARSING_MAX_LEN_BITS))?;
52
53 let idx_offsets = vec![
55 (F::ONE, sequence.limits.0.clone()),
56 (-F::ONE, sub.limits.0.clone()),
57 ];
58
59 let seq_native = sequence.buffer.to_vec();
60 let sub_native = sub.buffer.to_vec();
61 let flags = sub.padding_flags.to_vec();
62 let sub_entry = (idx.clone(), idx_offsets, sub_native, Some(flags));
63
64 self.sequence_cache
65 .borrow_mut()
66 .entry(seq_native)
67 .and_modify(|(calls, len)| {
68 *len += M_SUB;
69 calls.push(sub_entry.clone())
70 })
71 .or_insert_with(|| (vec![sub_entry], M_SUB));
72
73 Ok(())
74 }
75
76 pub fn check_bytes_varlen_partial<const M: usize, const A: usize>(
80 &self,
81 layouter: &mut impl Layouter<F>,
82 sequence: &ScannerVec<F, M, A>,
83 idx: &AssignedNative<F>,
84 sub: &[AssignedByte<F>],
85 ) -> Result<(), Error> {
86 if sub.is_empty() {
87 return Ok(());
88 }
89 let ng = &self.native_gadget;
90
91 ng.assert_lower_than_fixed(layouter, idx, &(BigUint::from(1u8) << PARSING_MAX_LEN_BITS))?;
93
94 let idx_offsets = vec![(F::ONE, sequence.limits.0.clone())];
96
97 let seq_native = sequence.buffer.to_vec();
98 let sub_native: Vec<AssignedNative<F>> = sub.iter().map(AssignedNative::from).collect();
99
100 self.sequence_cache
101 .borrow_mut()
102 .entry(seq_native)
103 .and_modify(|(calls, len)| {
104 *len += sub_native.len();
105 calls.push((idx.clone(), idx_offsets.clone(), sub_native.clone(), None))
106 })
107 .or_insert_with(|| {
108 let sub_entry = (idx.clone(), idx_offsets, sub_native, None);
109 (vec![sub_entry], sub.len())
110 });
111
112 Ok(())
113 }
114
115 pub fn assert_equal_varlen<
121 const M1: usize,
122 const A1: usize,
123 const M2: usize,
124 const A2: usize,
125 >(
126 &self,
127 layouter: &mut impl Layouter<F>,
128 v1: &ScannerVec<F, M1, A1>,
129 v2: &ScannerVec<F, M2, A2>,
130 ) -> Result<(), Error> {
131 self.native_gadget.assert_equal(layouter, v1.len(), v2.len())?;
132 if M1 == M2 && A1 == A2 {
133 for (x, y) in v1.buffer.iter().zip(v2.buffer.iter()) {
134 self.native_gadget.assert_equal(layouter, x, y)?;
135 }
136 Ok(())
137 } else {
138 let zero: AssignedNative<F> = self.native_gadget.assign_fixed(layouter, F::ZERO)?;
139 self.check_bytes_varlen(layouter, v1, &zero, v2)
140 }
141 }
142
143 pub fn assert_equal_varlen_partial<const M: usize, const A: usize>(
147 &self,
148 layouter: &mut impl Layouter<F>,
149 v1: &ScannerVec<F, M, A>,
150 v2: &[AssignedByte<F>],
151 ) -> Result<(), Error> {
152 self.native_gadget
153 .assert_equal_to_fixed(layouter, v1.len(), F::from(v2.len() as u64))?;
154 let l1 = v1.buffer[get_lims::<M, A>(v2.len())].iter();
155 let l2 = v2.iter().map(AssignedNative::from);
156 for (x, y) in l1.zip(l2) {
157 self.native_gadget.assert_equal(layouter, x, &y)?;
158 }
159 Ok(())
160 }
161}
162
163#[cfg(test)]
164mod test {
165 use ff::FromUniformBytes;
166 use midnight_proofs::{
167 circuit::{Layouter, SimpleFloorPlanner, Value},
168 dev::MockProver,
169 plonk::{Circuit, ConstraintSystem, Error},
170 };
171
172 use super::super::ScannerChip;
173 use crate::{
174 instructions::AssignmentInstructions, testing_utils::FromScratch, types::AssignedByte,
175 utils::circuit_modeling::circuit_to_json, CircuitField,
176 };
177
178 #[derive(Clone, Debug)]
182 struct VarlenPartialCircuit<F: CircuitField> {
183 full: Value<Vec<u8>>,
184 sub: Vec<Value<u8>>,
185 idx: Value<F>,
186 }
187
188 impl<F: CircuitField> VarlenPartialCircuit<F> {
189 fn new(full: &[u8], sub: &[u8], idx: usize) -> Self {
190 Self {
191 full: Value::known(full.to_vec()),
192 sub: sub.iter().map(|&b| Value::known(b)).collect(),
193 idx: Value::known(F::from(idx as u64)),
194 }
195 }
196 }
197
198 impl<F> Circuit<F> for VarlenPartialCircuit<F>
199 where
200 F: CircuitField + FromUniformBytes<64> + Ord,
201 {
202 type Config = <ScannerChip<F> as FromScratch<F>>::Config;
203 type FloorPlanner = SimpleFloorPlanner;
204 type Params = ();
205
206 fn without_witnesses(&self) -> Self {
207 unreachable!()
208 }
209
210 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
211 let instance_columns = [meta.instance_column(), meta.instance_column()];
212 ScannerChip::<F>::configure_from_scratch(
213 meta,
214 &mut vec![],
215 &mut vec![],
216 &instance_columns,
217 )
218 }
219
220 fn synthesize(
221 &self,
222 config: Self::Config,
223 mut layouter: impl Layouter<F>,
224 ) -> Result<(), Error> {
225 let scanner = ScannerChip::<F>::new_from_scratch(&config);
226 let ng = &scanner.native_gadget;
227
228 let sequence = scanner.assign_scanner_vec::<16, 1>(&mut layouter, self.full.clone())?;
229 let sub: Vec<AssignedByte<F>> = ng.assign_many(&mut layouter, &self.sub)?;
230 let idx = ng.assign(&mut layouter, self.idx)?;
231
232 scanner.check_bytes_varlen_partial(&mut layouter, &sequence, &idx, &sub)?;
233 scanner.load_from_scratch(&mut layouter)
234 }
235 }
236
237 fn varlen_partial_test(full: &[u8], sub: &[u8], idx: usize, must_pass: bool) {
238 type F = midnight_curves::Fq;
239 let circuit = VarlenPartialCircuit::<F>::new(full, sub, idx);
240 println!(
241 ">> [varlen_partial] [must{} pass] idx={idx}, sub len={}, full len={}",
242 if must_pass { "" } else { " not" },
243 sub.len(),
244 full.len(),
245 );
246 let result = MockProver::run(&circuit, vec![vec![], vec![]]);
247 match result {
248 Ok(p) => {
249 let verified = p.verify();
250 if must_pass {
251 verified.expect("should have passed")
252 } else {
253 assert!(verified.is_err(), "should have failed");
254 }
255 }
256 Err(e) => assert!(!must_pass, "Prover failed unexpectedly: {:?}", e),
257 }
258 println!("... ok!");
259 }
260
261 #[test]
262 fn test_check_bytes_varlen_partial() {
263 varlen_partial_test(b"hello world", b"hello", 0, true);
265 varlen_partial_test(b"hello world", b"world", 6, true);
266 varlen_partial_test(b"hello world", b"lo wo", 3, true);
267 varlen_partial_test(b"hello world", b"hello world", 0, true);
268 varlen_partial_test(b"abcdef", b"d", 3, true);
269
270 varlen_partial_test(b"hello", b"", 0, true);
272
273 varlen_partial_test(b"hello world", b"xyz", 0, false);
275 varlen_partial_test(b"hello world", b"world", 0, false);
277
278 {
280 type F = midnight_curves::Fq;
281 let circuit = VarlenPartialCircuit::<F>::new(b"hello world", b"world", 6);
282 circuit_to_json::<F>(
283 "Scanner",
284 "varlen partial substring perf (M = 16, sub length = 5)",
285 circuit,
286 );
287 }
288 }
289
290 #[derive(Clone, Debug)]
294 struct VarlenFullCircuit<F: CircuitField> {
295 full: Value<Vec<u8>>,
296 sub: Value<Vec<u8>>,
297 idx: Value<F>,
298 }
299
300 impl<F: CircuitField> VarlenFullCircuit<F> {
301 fn new(full: &[u8], sub: &[u8], idx: usize) -> Self {
302 Self {
303 full: Value::known(full.to_vec()),
304 sub: Value::known(sub.to_vec()),
305 idx: Value::known(F::from(idx as u64)),
306 }
307 }
308 }
309
310 impl<F> Circuit<F> for VarlenFullCircuit<F>
311 where
312 F: CircuitField + FromUniformBytes<64> + Ord,
313 {
314 type Config = <ScannerChip<F> as FromScratch<F>>::Config;
315 type FloorPlanner = SimpleFloorPlanner;
316 type Params = ();
317
318 fn without_witnesses(&self) -> Self {
319 unreachable!()
320 }
321
322 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
323 let instance_columns = [meta.instance_column(), meta.instance_column()];
324 ScannerChip::<F>::configure_from_scratch(
325 meta,
326 &mut vec![],
327 &mut vec![],
328 &instance_columns,
329 )
330 }
331
332 fn synthesize(
333 &self,
334 config: Self::Config,
335 mut layouter: impl Layouter<F>,
336 ) -> Result<(), Error> {
337 let scanner = ScannerChip::<F>::new_from_scratch(&config);
338 let ng = &scanner.native_gadget;
339
340 let sequence = scanner.assign_scanner_vec::<16, 1>(&mut layouter, self.full.clone())?;
341 let sub = scanner.assign_scanner_vec::<8, 1>(&mut layouter, self.sub.clone())?;
342 let idx = ng.assign(&mut layouter, self.idx)?;
343
344 scanner.check_bytes_varlen(&mut layouter, &sequence, &idx, &sub)?;
345 scanner.load_from_scratch(&mut layouter)
346 }
347 }
348
349 fn varlen_full_test(full: &[u8], sub: &[u8], idx: usize, must_pass: bool) {
350 type F = midnight_curves::Fq;
351 let circuit = VarlenFullCircuit::<F>::new(full, sub, idx);
352 println!(
353 ">> [varlen_full] [must{} pass] idx={idx}, sub len={}, full len={}",
354 if must_pass { "" } else { " not" },
355 sub.len(),
356 full.len(),
357 );
358 let result = MockProver::run(&circuit, vec![vec![], vec![]]);
359 match result {
360 Ok(p) => {
361 let verified = p.verify();
362 if must_pass {
363 verified.expect("should have passed")
364 } else {
365 assert!(verified.is_err(), "should have failed");
366 }
367 }
368 Err(e) => assert!(!must_pass, "Prover failed unexpectedly: {:?}", e),
369 }
370 println!("... ok!");
371 }
372
373 #[test]
374 fn test_check_bytes_varlen() {
375 varlen_full_test(b"hello world", b"hello", 0, true);
377 varlen_full_test(b"hello world", b"world", 6, true);
378 varlen_full_test(b"hello world", b"lo wo", 3, true);
379 varlen_full_test(b"abcdef", b"d", 3, true);
380
381 varlen_full_test(b"hello world", b"xyz", 0, false);
383 varlen_full_test(b"hello world", b"world", 0, false);
385
386 {
388 type F = midnight_curves::Fq;
389 let circuit = VarlenFullCircuit::<F>::new(b"hello world", b"world", 6);
390 circuit_to_json::<F>(
391 "Scanner",
392 "varlen full substring perf (M_SEQ = 16, M_SUB = 8, sub length = 5)",
393 circuit,
394 );
395 }
396 }
397}