halo2_axiom/dev.rs
1//! Tools for developing circuits.
2
3use std::collections::HashMap;
4use std::collections::HashSet;
5use std::iter;
6use std::ops::{Add, Mul, Neg, Range};
7use std::sync::Arc;
8
9use blake2b_simd::blake2b;
10use ff::Field;
11use ff::FromUniformBytes;
12
13use crate::plonk::permutation::keygen::Assembly;
14use crate::{
15 circuit,
16 plonk::{
17 permutation,
18 sealed::{self, SealedPhase},
19 Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, ConstraintSystem, Error,
20 Expression, FirstPhase, Fixed, FloorPlanner, Instance, Phase, Selector,
21 },
22};
23
24#[cfg(feature = "multicore")]
25use crate::multicore::{
26 IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, ParallelIterator,
27 ParallelSliceMut,
28};
29
30pub mod metadata;
31use metadata::Column as ColumnMetadata;
32mod util;
33
34mod failure;
35pub use failure::{FailureLocation, VerifyFailure};
36
37// pub mod cost;
38// pub use cost::CircuitCost;
39
40mod gates;
41pub use gates::CircuitGates;
42
43mod tfp;
44pub use tfp::TracingFloorPlanner;
45
46// #[cfg(feature = "dev-graph")]
47// mod graph;
48
49// #[cfg(feature = "dev-graph")]
50// #[cfg_attr(docsrs, doc(cfg(feature = "dev-graph")))]
51// pub use graph::{circuit_dot_graph, layout::CircuitLayout};
52
53#[derive(Debug)]
54struct Region {
55 /// The name of the region. Not required to be unique.
56 name: String,
57 /// The columns involved in this region.
58 columns: HashSet<Column<Any>>,
59 /// The rows that this region starts and ends on, if known.
60 rows: Option<(usize, usize)>,
61 /// The selectors that have been enabled in this region. All other selectors are by
62 /// construction not enabled.
63 enabled_selectors: HashMap<Selector, Vec<usize>>,
64 /// Annotations given to Advice, Fixed or Instance columns within a region context.
65 annotations: HashMap<ColumnMetadata, String>,
66 /// The cells assigned in this region. We store this as a `Vec` so that if any cells
67 /// are double-assigned, they will be visibly darker.
68 cells: HashMap<(Column<Any>, usize), usize>,
69}
70
71impl Region {
72 fn update_extent(&mut self, column: Column<Any>, row: usize) {
73 self.columns.insert(column);
74
75 // The region start is the earliest row assigned to.
76 // The region end is the latest row assigned to.
77 let (mut start, mut end) = self.rows.unwrap_or((row, row));
78 if row < start {
79 // The first row assigned was not at start 0 within the region.
80 start = row;
81 }
82 if row > end {
83 end = row;
84 }
85 self.rows = Some((start, end));
86 }
87}
88
89/// The value of a particular cell within the circuit.
90#[derive(Clone, Copy, Debug, PartialEq, Eq)]
91pub enum CellValue<F: Field> {
92 /// An unassigned cell.
93 Unassigned,
94 /// A cell that has been assigned a value.
95 Assigned(F),
96 /// A unique poisoned cell.
97 Poison(usize),
98}
99
100/// The value of a particular cell within the circuit.
101#[derive(Clone, Debug, PartialEq, Eq)]
102pub enum AdviceCellValue<F: Field> {
103 // A cell that has been assigned a value.
104 Assigned(Arc<Assigned<F>>),
105 // A unique poisoned cell.
106 Poison(usize),
107}
108
109/// A value within an expression.
110#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
111enum Value<F: Field> {
112 Real(F),
113 Poison,
114}
115
116impl<F: Field> From<CellValue<F>> for Value<F> {
117 fn from(value: CellValue<F>) -> Self {
118 match value {
119 // Cells that haven't been explicitly assigned to, default to zero.
120 CellValue::Unassigned => Value::Real(F::ZERO),
121 CellValue::Assigned(v) => Value::Real(v),
122 CellValue::Poison(_) => Value::Poison,
123 }
124 }
125}
126
127impl<F: Field> From<AdviceCellValue<F>> for Value<F> {
128 fn from(value: AdviceCellValue<F>) -> Self {
129 match value {
130 AdviceCellValue::Assigned(v) => Value::Real(v.evaluate()),
131 AdviceCellValue::Poison(_) => Value::Poison,
132 }
133 }
134}
135
136impl<F: Field> Neg for Value<F> {
137 type Output = Self;
138
139 fn neg(self) -> Self::Output {
140 match self {
141 Value::Real(a) => Value::Real(-a),
142 _ => Value::Poison,
143 }
144 }
145}
146
147impl<F: Field> Add for Value<F> {
148 type Output = Self;
149
150 fn add(self, rhs: Self) -> Self::Output {
151 match (self, rhs) {
152 (Value::Real(a), Value::Real(b)) => Value::Real(a + b),
153 _ => Value::Poison,
154 }
155 }
156}
157
158impl<F: Field> Mul for Value<F> {
159 type Output = Self;
160
161 fn mul(self, rhs: Self) -> Self::Output {
162 match (self, rhs) {
163 (Value::Real(a), Value::Real(b)) => Value::Real(a * b),
164 // If poison is multiplied by zero, then we treat the poison as unconstrained
165 // and we don't propagate it.
166 (Value::Real(x), Value::Poison) | (Value::Poison, Value::Real(x))
167 if x.is_zero_vartime() =>
168 {
169 Value::Real(F::ZERO)
170 }
171 _ => Value::Poison,
172 }
173 }
174}
175
176impl<F: Field> Mul<F> for Value<F> {
177 type Output = Self;
178
179 fn mul(self, rhs: F) -> Self::Output {
180 match self {
181 Value::Real(lhs) => Value::Real(lhs * rhs),
182 // If poison is multiplied by zero, then we treat the poison as unconstrained
183 // and we don't propagate it.
184 Value::Poison if rhs.is_zero_vartime() => Value::Real(F::ZERO),
185 _ => Value::Poison,
186 }
187 }
188}
189
190/// A test prover for debugging circuits.
191///
192/// The normal proving process, when applied to a buggy circuit implementation, might
193/// return proofs that do not validate when they should, but it can't indicate anything
194/// other than "something is invalid". `MockProver` can be used to figure out _why_ these
195/// are invalid: it stores all the private inputs along with the circuit internals, and
196/// then checks every constraint manually.
197///
198/// # Examples
199///
200/// ```
201/// use halo2_axiom::{
202/// circuit::{Layouter, SimpleFloorPlanner, Value},
203/// dev::{FailureLocation, MockProver, VerifyFailure},
204/// plonk::{Advice, Any, Circuit, Column, ConstraintSystem, Error, Selector},
205/// poly::Rotation,
206/// };
207/// use ff::PrimeField;
208/// use halo2curves::pasta::Fp;
209/// const K: u32 = 5;
210///
211/// #[derive(Copy, Clone)]
212/// struct MyConfig {
213/// a: Column<Advice>,
214/// b: Column<Advice>,
215/// c: Column<Advice>,
216/// s: Selector,
217/// }
218///
219/// #[derive(Clone, Default)]
220/// struct MyCircuit {
221/// a: Value<u64>,
222/// b: Value<u64>,
223/// }
224///
225/// impl<F: PrimeField> Circuit<F> for MyCircuit {
226/// type Config = MyConfig;
227/// type FloorPlanner = SimpleFloorPlanner;
228/// #[cfg(feature = "circuit-params")]
229/// type Params = ();
230///
231/// fn without_witnesses(&self) -> Self {
232/// Self::default()
233/// }
234///
235/// fn configure(meta: &mut ConstraintSystem<F>) -> MyConfig {
236/// let a = meta.advice_column();
237/// let b = meta.advice_column();
238/// let c = meta.advice_column();
239/// let s = meta.selector();
240///
241/// meta.create_gate("R1CS constraint", |meta| {
242/// let a = meta.query_advice(a, Rotation::cur());
243/// let b = meta.query_advice(b, Rotation::cur());
244/// let c = meta.query_advice(c, Rotation::cur());
245/// let s = meta.query_selector(s);
246///
247/// // BUG: Should be a * b - c
248/// Some(("buggy R1CS", s * (a * b + c)))
249/// });
250///
251/// MyConfig { a, b, c, s }
252/// }
253///
254/// fn synthesize(&self, config: MyConfig, mut layouter: impl Layouter<F>) -> Result<(), Error> {
255/// layouter.assign_region(|| "Example region", |mut region| {
256/// config.s.enable(&mut region, 0)?;
257/// region.assign_advice(config.a, 0, self.a.map(F::from));
258/// region.assign_advice(config.b, 0, self.b.map(F::from));
259/// region.assign_advice(config.c, 0, (self.a * self.b).map(F::from));
260/// Ok(())
261/// })
262/// }
263/// }
264///
265/// // Assemble the private inputs to the circuit.
266/// let circuit = MyCircuit {
267/// a: Value::known(2),
268/// b: Value::known(4),
269/// };
270///
271/// // This circuit has no public inputs.
272/// let instance = vec![];
273///
274/// let prover = MockProver::<Fp>::run(K, &circuit, instance).unwrap();
275/// assert_eq!(
276/// prover.verify(),
277/// Err(vec![VerifyFailure::ConstraintNotSatisfied {
278/// constraint: ((0, "R1CS constraint").into(), 0, "buggy R1CS").into(),
279/// location: FailureLocation::OutsideRegion { row: 0 },
280/// cell_values: vec![
281/// (((Any::advice(), 0).into(), 0).into(), "0x2".to_string()),
282/// (((Any::advice(), 1).into(), 0).into(), "0x4".to_string()),
283/// (((Any::advice(), 2).into(), 0).into(), "0x8".to_string()),
284/// ],
285/// }])
286/// );
287///
288/// // If we provide a too-small K, we get a panic.
289/// use std::panic;
290/// let result = panic::catch_unwind(|| {
291/// MockProver::<Fp>::run(2, &circuit, vec![]).unwrap_err()
292/// });
293/// assert_eq!(
294/// result.unwrap_err().downcast_ref::<String>().unwrap(),
295/// "n=4, minimum_rows=8, k=2"
296/// );
297/// ```
298#[derive(Debug)]
299pub struct MockProver<F: Field> {
300 k: u32,
301 n: u32,
302 cs: ConstraintSystem<F>,
303
304 /// The regions in the circuit.
305 regions: Vec<Region>,
306 /// The current region being assigned to. Will be `None` after the circuit has been
307 /// synthesized.
308 current_region: Option<Region>,
309
310 // The fixed cells in the circuit, arranged as [column][row].
311 fixed: Vec<Vec<CellValue<F>>>,
312 // The advice cells in the circuit, arranged as [column][row].
313 advice: Vec<Vec<AdviceCellValue<F>>>,
314 // The instance cells in the circuit, arranged as [column][row].
315 instance: Vec<Vec<InstanceValue<F>>>,
316
317 selectors: Vec<Vec<bool>>,
318
319 challenges: Vec<F>,
320
321 permutation: permutation::keygen::Assembly,
322
323 // A range of available rows for assignment and copies.
324 usable_rows: Range<usize>,
325
326 current_phase: sealed::Phase,
327}
328
329#[derive(Debug, Clone, PartialEq, Eq)]
330enum InstanceValue<F: Field> {
331 Assigned(F),
332 Padding,
333}
334
335impl<F: Field> InstanceValue<F> {
336 fn value(&self) -> F {
337 match self {
338 InstanceValue::Assigned(v) => *v,
339 InstanceValue::Padding => F::ZERO,
340 }
341 }
342}
343
344impl<F: Field> MockProver<F> {
345 fn in_phase<P: Phase>(&self, phase: P) -> bool {
346 self.current_phase == phase.to_sealed()
347 }
348}
349
350impl<F: Field> Assignment<F> for MockProver<F> {
351 fn enter_region<NR, N>(&mut self, name: N)
352 where
353 NR: Into<String>,
354 N: FnOnce() -> NR,
355 {
356 if !self.in_phase(FirstPhase) {
357 return;
358 }
359
360 assert!(self.current_region.is_none());
361 self.current_region = Some(Region {
362 name: name().into(),
363 columns: HashSet::default(),
364 rows: None,
365 annotations: HashMap::default(),
366 enabled_selectors: HashMap::default(),
367 cells: HashMap::default(),
368 });
369 }
370
371 fn exit_region(&mut self) {
372 if !self.in_phase(FirstPhase) {
373 return;
374 }
375
376 self.regions.push(self.current_region.take().unwrap());
377 }
378
379 fn annotate_column<A, AR>(&mut self, annotation: A, column: Column<Any>)
380 where
381 A: FnOnce() -> AR,
382 AR: Into<String>,
383 {
384 if !self.in_phase(FirstPhase) {
385 return;
386 }
387
388 if let Some(region) = self.current_region.as_mut() {
389 region
390 .annotations
391 .insert(ColumnMetadata::from(column), annotation().into());
392 }
393 }
394
395 fn enable_selector<A, AR>(&mut self, _: A, selector: &Selector, row: usize) -> Result<(), Error>
396 where
397 A: FnOnce() -> AR,
398 AR: Into<String>,
399 {
400 if !self.in_phase(FirstPhase) {
401 return Ok(());
402 }
403
404 assert!(
405 self.usable_rows.contains(&row),
406 "row={} not in usable_rows={:?}, k={}",
407 row,
408 self.usable_rows,
409 self.k,
410 );
411
412 // Track that this selector was enabled. We require that all selectors are enabled
413 // inside some region (i.e. no floating selectors).
414 self.current_region
415 .as_mut()
416 .unwrap()
417 .enabled_selectors
418 .entry(*selector)
419 .or_default()
420 .push(row);
421
422 self.selectors[selector.0][row] = true;
423
424 Ok(())
425 }
426
427 fn query_instance(
428 &self,
429 column: Column<Instance>,
430 row: usize,
431 ) -> Result<circuit::Value<F>, Error> {
432 assert!(
433 self.usable_rows.contains(&row),
434 "row={}, usable_rows={:?}, k={}",
435 row,
436 self.usable_rows,
437 self.k,
438 );
439
440 Ok(self
441 .instance
442 .get(column.index())
443 .and_then(|column| column.get(row))
444 .map(|v| circuit::Value::known(v.value()))
445 .expect("bound failure"))
446 }
447
448 fn assign_advice<'v>(
449 //<V, VR, A, AR>(
450 &mut self,
451 //_: A,
452 column: Column<Advice>,
453 row: usize,
454 to: circuit::Value<Assigned<F>>,
455 ) -> circuit::Value<&'v Assigned<F>> {
456 if self.in_phase(FirstPhase) {
457 assert!(
458 self.usable_rows.contains(&row),
459 "row={}, usable_rows={:?}, k={}",
460 row,
461 self.usable_rows,
462 self.k,
463 );
464 }
465
466 match to.assign() {
467 Ok(to) => {
468 let value = self
469 .advice
470 .get_mut(column.index())
471 .and_then(|v| v.get_mut(row))
472 .expect("bounds failure");
473 /* We don't use this because we do assign 0s in first pass of second phase sometimes
474 if let AdviceCellValue::Assigned(value) = value {
475 // Inconsistent assignment between different phases.
476 assert_eq!(value.as_ref(), &to, "value={:?}, to={:?}", &value, &to);
477 let val = Arc::clone(&value);
478 let val_ref = Arc::downgrade(&val);
479 circuit::Value::known(unsafe { &*val_ref.as_ptr() })
480 } else {
481 */
482 let val = Arc::new(to);
483 let val_ref = Arc::downgrade(&val);
484 *value = AdviceCellValue::Assigned(val);
485 circuit::Value::known(unsafe { &*val_ref.as_ptr() })
486 //}
487 }
488 Err(err) => {
489 // Propagate `assign` error if the column is in current phase.
490 if self.in_phase(column.column_type().phase) {
491 panic!("{:?}", err);
492 }
493 circuit::Value::unknown()
494 }
495 }
496 }
497
498 fn assign_fixed(&mut self, column: Column<Fixed>, row: usize, to: Assigned<F>) {
499 if !self.in_phase(FirstPhase) {
500 return;
501 }
502
503 assert!(
504 self.usable_rows.contains(&row),
505 "row={}, usable_rows={:?}, k={}",
506 row,
507 self.usable_rows,
508 self.k,
509 );
510
511 if let Some(region) = self.current_region.as_mut() {
512 region.update_extent(column.into(), row);
513 region
514 .cells
515 .entry((column.into(), row))
516 .and_modify(|count| *count += 1)
517 .or_default();
518 }
519
520 *self
521 .fixed
522 .get_mut(column.index())
523 .and_then(|v| v.get_mut(row))
524 .expect("bounds failure") = CellValue::Assigned(to.evaluate());
525 }
526
527 fn copy(
528 &mut self,
529 left_column: Column<Any>,
530 left_row: usize,
531 right_column: Column<Any>,
532 right_row: usize,
533 ) {
534 if !self.in_phase(FirstPhase) {
535 return;
536 }
537
538 assert!(
539 self.usable_rows.contains(&left_row) && self.usable_rows.contains(&right_row),
540 "left_row={}, right_row={}, usable_rows={:?}, k={}",
541 left_row,
542 right_row,
543 self.usable_rows,
544 self.k,
545 );
546
547 self.permutation
548 .copy(left_column, left_row, right_column, right_row)
549 .unwrap_or_else(|err| panic!("{err:?}"))
550 }
551
552 fn fill_from_row(
553 &mut self,
554 col: Column<Fixed>,
555 from_row: usize,
556 to: circuit::Value<Assigned<F>>,
557 ) -> Result<(), Error> {
558 if !self.in_phase(FirstPhase) {
559 return Ok(());
560 }
561
562 assert!(
563 self.usable_rows.contains(&from_row),
564 "row={}, usable_rows={:?}, k={}",
565 from_row,
566 self.usable_rows,
567 self.k,
568 );
569
570 for row in self.usable_rows.clone().skip(from_row) {
571 self.assign_fixed(col, row, to.assign()?);
572 }
573
574 Ok(())
575 }
576
577 fn get_challenge(&self, challenge: Challenge) -> circuit::Value<F> {
578 if self.current_phase <= challenge.phase {
579 return circuit::Value::unknown();
580 }
581
582 circuit::Value::known(self.challenges[challenge.index()])
583 }
584
585 fn push_namespace<NR, N>(&mut self, _: N)
586 where
587 NR: Into<String>,
588 N: FnOnce() -> NR,
589 {
590 // TODO: Do something with namespaces :)
591 }
592
593 fn pop_namespace(&mut self, _: Option<String>) {
594 // TODO: Do something with namespaces :)
595 }
596}
597
598impl<F: FromUniformBytes<64> + Ord> MockProver<F> {
599 /// Runs a synthetic keygen-and-prove operation on the given circuit, collecting data
600 /// about the constraints and their assignments.
601 pub fn run<ConcreteCircuit: Circuit<F>>(
602 k: u32,
603 circuit: &ConcreteCircuit,
604 instance: Vec<Vec<F>>,
605 ) -> Result<Self, Error> {
606 let n = 1 << k;
607
608 let mut cs = ConstraintSystem::default();
609 #[cfg(feature = "circuit-params")]
610 let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params());
611 #[cfg(not(feature = "circuit-params"))]
612 let config = ConcreteCircuit::configure(&mut cs);
613 let cs = cs;
614
615 assert!(
616 n >= cs.minimum_rows(),
617 "n={}, minimum_rows={}, k={}",
618 n,
619 cs.minimum_rows(),
620 k,
621 );
622
623 assert_eq!(instance.len(), cs.num_instance_columns);
624
625 let instance = instance
626 .into_iter()
627 .map(|instance| {
628 assert!(
629 instance.len() <= n - (cs.blinding_factors() + 1),
630 "instance.len={}, n={}, cs.blinding_factors={}",
631 instance.len(),
632 n,
633 cs.blinding_factors()
634 );
635
636 let mut instance_values = vec![InstanceValue::Padding; n];
637 for (idx, value) in instance.into_iter().enumerate() {
638 instance_values[idx] = InstanceValue::Assigned(value);
639 }
640
641 instance_values
642 })
643 .collect::<Vec<_>>();
644
645 // Fixed columns contain no blinding factors.
646 let fixed = vec![vec![CellValue::Unassigned; n]; cs.num_fixed_columns];
647 let selectors = vec![vec![false; n]; cs.num_selectors];
648 // Advice columns contain blinding factors.
649 let blinding_factors = cs.blinding_factors();
650 let usable_rows = n - (blinding_factors + 1);
651 let advice = vec![
652 {
653 // let mut column = vec![AdviceCellValue::Unassigned; n];
654 // Assign advice to 0 by default so we can have gates that query unassigned rotations to minimize number of distinct rotation sets, for SHPLONK optimization
655 let mut column =
656 vec![AdviceCellValue::Assigned(Arc::new(Assigned::Trivial(F::ZERO))); n];
657 // Poison unusable rows.
658 for (i, cell) in column.iter_mut().enumerate().skip(usable_rows) {
659 *cell = AdviceCellValue::Poison(i);
660 }
661 column
662 };
663 cs.num_advice_columns
664 ];
665 let permutation = permutation::keygen::Assembly::new(n, &cs.permutation);
666 let constants = cs.constants.clone();
667
668 // Use hash chain to derive deterministic challenges for testing
669 let challenges = {
670 let mut hash: [u8; 64] = blake2b(b"Halo2-MockProver").as_bytes().try_into().unwrap();
671 iter::repeat_with(|| {
672 hash = blake2b(&hash).as_bytes().try_into().unwrap();
673 F::from_uniform_bytes(&hash)
674 })
675 .take(cs.num_challenges)
676 .collect()
677 };
678
679 let mut prover = MockProver {
680 k,
681 n: n as u32,
682 cs,
683 regions: vec![],
684 current_region: None,
685 fixed,
686 advice,
687 instance,
688 selectors,
689 challenges,
690 permutation,
691 usable_rows: 0..usable_rows,
692 current_phase: FirstPhase.to_sealed(),
693 };
694
695 for current_phase in prover.cs.phases() {
696 prover.current_phase = current_phase;
697 ConcreteCircuit::FloorPlanner::synthesize(
698 &mut prover,
699 circuit,
700 config.clone(),
701 constants.clone(),
702 )?;
703 }
704
705 let (cs, selector_polys) = prover.cs.compress_selectors(prover.selectors.clone());
706 prover.cs = cs;
707 prover.fixed.extend(selector_polys.into_iter().map(|poly| {
708 let mut v = vec![CellValue::Unassigned; n];
709 for (v, p) in v.iter_mut().zip(&poly[..]) {
710 *v = CellValue::Assigned(*p);
711 }
712 v
713 }));
714
715 #[cfg(feature = "thread-safe-region")]
716 prover.permutation.build_ordered_mapping();
717
718 Ok(prover)
719 }
720
721 /// Return the content of an advice column as assigned by the circuit.
722 pub fn advice_values(&self, column: Column<Advice>) -> &[AdviceCellValue<F>] {
723 &self.advice[column.index()]
724 }
725
726 /// Return the content of a fixed column as assigned by the circuit.
727 pub fn fixed_values(&self, column: Column<Fixed>) -> &[CellValue<F>] {
728 &self.fixed[column.index()]
729 }
730
731 /// Returns `Ok(())` if this `MockProver` is satisfied, or a list of errors indicating
732 /// the reasons that the circuit is not satisfied.
733 pub fn verify(&self) -> Result<(), Vec<VerifyFailure>> {
734 self.verify_at_rows(self.usable_rows.clone(), self.usable_rows.clone())
735 }
736
737 /// Returns `Ok(())` if this `MockProver` is satisfied, or a list of errors indicating
738 /// the reasons that the circuit is not satisfied.
739 /// Constraints are only checked at `gate_row_ids`,
740 /// and lookup inputs are only checked at `lookup_input_row_ids`
741 pub fn verify_at_rows<I: Clone + Iterator<Item = usize>>(
742 &self,
743 gate_row_ids: I,
744 lookup_input_row_ids: I,
745 ) -> Result<(), Vec<VerifyFailure>> {
746 let n = self.n as i32;
747
748 // check all the row ids are valid
749 for row_id in gate_row_ids.clone() {
750 if !self.usable_rows.contains(&row_id) {
751 panic!("invalid gate row id {}", row_id)
752 }
753 }
754 for row_id in lookup_input_row_ids.clone() {
755 if !self.usable_rows.contains(&row_id) {
756 panic!("invalid lookup row id {}", row_id)
757 }
758 }
759
760 // Check that within each region, all cells used in instantiated gates have been
761 // assigned to.
762
763 // Turn off this check because we might query unassigned cells to increase the rotation set size of a gate (for SHPLONK optimization)
764 /*
765 let selector_errors = self.regions.iter().enumerate().flat_map(|(r_i, r)| {
766 r.enabled_selectors.iter().flat_map(move |(selector, at)| {
767 // Find the gates enabled by this selector
768 self.cs
769 .gates
770 .iter()
771 // Assume that if a queried selector is enabled, the user wants to use the
772 // corresponding gate in some way.
773 //
774 // TODO: This will trip up on the reverse case, where leaving a selector
775 // un-enabled keeps a gate enabled. We could alternatively require that
776 // every selector is explicitly enabled or disabled on every row? But that
777 // seems messy and confusing.
778 .enumerate()
779 .filter(move |(_, g)| g.queried_selectors().contains(selector))
780 .flat_map(move |(gate_index, gate)| {
781 at.iter().flat_map(move |selector_row| {
782 // Selectors are queried with no rotation.
783 let gate_row = *selector_row as i32;
784
785 gate.queried_cells().iter().filter_map(move |cell| {
786 // Determine where this cell should have been assigned.
787 let cell_row = ((gate_row + n + cell.rotation.0) % n) as usize;
788
789 match cell.column.column_type() {
790 Any::Instance => {
791 // Handle instance cells, which are not in the region.
792 let instance_value =
793 &self.instance[cell.column.index()][cell_row];
794 match instance_value {
795 InstanceValue::Assigned(_) => None,
796 _ => Some(VerifyFailure::InstanceCellNotAssigned {
797 gate: (gate_index, gate.name()).into(),
798 region: (r_i, r.name.clone()).into(),
799 gate_offset: *selector_row,
800 column: cell.column.try_into().unwrap(),
801 row: cell_row,
802 }),
803 }
804 }
805 _ => {
806 // Check that it was assigned!
807 if r.cells.contains_key(&(cell.column, cell_row)) {
808 None
809 } else {
810 Some(VerifyFailure::CellNotAssigned {
811 gate: (gate_index, gate.name()).into(),
812 region: (
813 r_i,
814 r.name.clone(),
815 r.annotations.clone(),
816 )
817 .into(),
818 gate_offset: *selector_row,
819 column: cell.column,
820 offset: cell_row as isize
821 - r.rows.unwrap().0 as isize,
822 })
823 }
824 }
825 }
826 })
827 })
828 })
829 })
830 });
831 */
832
833 let advice = self
834 .advice
835 .iter()
836 .map(|advice| {
837 advice
838 .iter()
839 .map(|rc| match *rc {
840 AdviceCellValue::Assigned(ref a) => CellValue::Assigned(match a.as_ref() {
841 Assigned::Trivial(a) => *a,
842 Assigned::Rational(a, b) => *a * b.invert().unwrap(),
843 _ => F::ZERO,
844 }),
845 AdviceCellValue::Poison(i) => CellValue::Poison(i),
846 })
847 .collect::<Vec<_>>()
848 })
849 .collect::<Vec<_>>();
850 let advice = &advice;
851 // Check that all gates are satisfied for all rows.
852 let gate_errors =
853 self.cs
854 .gates
855 .iter()
856 .enumerate()
857 .flat_map(|(gate_index, gate)| {
858 let blinding_rows =
859 (self.n as usize - (self.cs.blinding_factors() + 1))..(self.n as usize);
860 (gate_row_ids.clone().chain(blinding_rows)).flat_map(move |row| {
861 let row = row as i32 + n;
862 gate.polynomials().iter().enumerate().filter_map(
863 move |(poly_index, poly)| match poly.evaluate_lazy(
864 &|scalar| Value::Real(scalar),
865 &|_| panic!("virtual selectors are removed during optimization"),
866 &util::load(n, row, &self.cs.fixed_queries, &self.fixed),
867 &util::load(n, row, &self.cs.advice_queries, advice),
868 &util::load_instance(
869 n,
870 row,
871 &self.cs.instance_queries,
872 &self.instance,
873 ),
874 &|challenge| Value::Real(self.challenges[challenge.index()]),
875 &|a| -a,
876 &|a, b| a + b,
877 &|a, b| a * b,
878 &|a, scalar| a * scalar,
879 &Value::Real(F::ZERO),
880 ) {
881 Value::Real(x) if x.is_zero_vartime() => None,
882 Value::Real(_) => Some(VerifyFailure::ConstraintNotSatisfied {
883 constraint: (
884 (gate_index, gate.name()).into(),
885 poly_index,
886 gate.constraint_name(poly_index),
887 )
888 .into(),
889 location: FailureLocation::find_expressions(
890 &self.cs,
891 &self.regions,
892 (row - n) as usize,
893 Some(poly).into_iter(),
894 ),
895 cell_values: util::cell_values(
896 gate,
897 poly,
898 &util::load(n, row, &self.cs.fixed_queries, &self.fixed),
899 &util::load(n, row, &self.cs.advice_queries, advice),
900 &util::load_instance(
901 n,
902 row,
903 &self.cs.instance_queries,
904 &self.instance,
905 ),
906 ),
907 }),
908 Value::Poison => Some(VerifyFailure::ConstraintPoisoned {
909 constraint: (
910 (gate_index, gate.name()).into(),
911 poly_index,
912 gate.constraint_name(poly_index),
913 )
914 .into(),
915 }),
916 },
917 )
918 })
919 });
920
921 let load = |expression: &Expression<F>, row| {
922 expression.evaluate_lazy(
923 &|scalar| Value::Real(scalar),
924 &|_| panic!("virtual selectors are removed during optimization"),
925 &|query| {
926 let query = self.cs.fixed_queries[query.index.unwrap()];
927 let column_index = query.0.index();
928 let rotation = query.1 .0;
929 self.fixed[column_index][(row as i32 + n + rotation) as usize % n as usize]
930 .into()
931 },
932 &|query| {
933 let query = self.cs.advice_queries[query.index.unwrap()];
934 let column_index = query.0.index();
935 let rotation = query.1 .0;
936 self.advice[column_index][(row as i32 + n + rotation) as usize % n as usize]
937 .clone()
938 .into()
939 },
940 &|query| {
941 let query = self.cs.instance_queries[query.index.unwrap()];
942 let column_index = query.0.index();
943 let rotation = query.1 .0;
944 Value::Real(
945 self.instance[column_index]
946 [(row as i32 + n + rotation) as usize % n as usize]
947 .value(),
948 )
949 },
950 &|challenge| Value::Real(self.challenges[challenge.index()]),
951 &|a| -a,
952 &|a, b| a + b,
953 &|a, b| a * b,
954 &|a, scalar| a * scalar,
955 &Value::Real(F::ZERO),
956 )
957 };
958
959 let mut cached_table = Vec::new();
960 let mut cached_table_identifier = Vec::new();
961 // Check that all lookups exist in their respective tables.
962 let lookup_errors =
963 self.cs
964 .lookups
965 .iter()
966 .enumerate()
967 .flat_map(|(lookup_index, lookup)| {
968 assert!(lookup.table_expressions.len() == lookup.input_expressions.len());
969 assert!(self.usable_rows.end > 0);
970
971 // We optimize on the basis that the table might have been filled so that the last
972 // usable row now has the fill contents (it doesn't matter if there was no filling).
973 // Note that this "fill row" necessarily exists in the table, and we use that fact to
974 // slightly simplify the optimization: we're only trying to check that all input rows
975 // are contained in the table, and so we can safely just drop input rows that
976 // match the fill row.
977 let fill_row: Vec<_> = lookup
978 .table_expressions
979 .iter()
980 .map(move |c| load(c, self.usable_rows.end - 1))
981 .collect();
982
983 let table_identifier = lookup
984 .table_expressions
985 .iter()
986 .map(Expression::identifier)
987 .collect::<Vec<_>>();
988 if table_identifier != cached_table_identifier {
989 cached_table_identifier = table_identifier;
990
991 // In the real prover, the lookup expressions are never enforced on
992 // unusable rows, due to the (1 - (l_last(X) + l_blind(X))) term.
993 cached_table = self
994 .usable_rows
995 .clone()
996 .filter_map(|table_row| {
997 let t = lookup
998 .table_expressions
999 .iter()
1000 .map(move |c| load(c, table_row))
1001 .collect();
1002
1003 if t != fill_row {
1004 Some(t)
1005 } else {
1006 None
1007 }
1008 })
1009 .collect();
1010 cached_table.sort_unstable();
1011 }
1012 let table = &cached_table;
1013
1014 let mut inputs: Vec<(Vec<_>, usize)> = lookup_input_row_ids
1015 .clone()
1016 .filter_map(|input_row| {
1017 let t = lookup
1018 .input_expressions
1019 .iter()
1020 .map(move |c| load(c, input_row))
1021 .collect();
1022
1023 if t != fill_row {
1024 // Also keep track of the original input row, since we're going to sort.
1025 Some((t, input_row))
1026 } else {
1027 None
1028 }
1029 })
1030 .collect();
1031 inputs.sort_unstable();
1032
1033 let mut i = 0;
1034 inputs
1035 .iter()
1036 .filter_map(move |(input, input_row)| {
1037 while i < table.len() && &table[i] < input {
1038 i += 1;
1039 }
1040 if i == table.len() || &table[i] > input {
1041 assert!(table.binary_search(input).is_err());
1042
1043 Some(VerifyFailure::Lookup {
1044 name: lookup.name.clone(),
1045 lookup_index,
1046 location: FailureLocation::find_expressions(
1047 &self.cs,
1048 &self.regions,
1049 *input_row,
1050 lookup.input_expressions.iter(),
1051 ),
1052 })
1053 } else {
1054 None
1055 }
1056 })
1057 .collect::<Vec<_>>()
1058 });
1059
1060 let mapping = self.permutation.mapping();
1061 // Check that permutations preserve the original values of the cells.
1062 let perm_errors = {
1063 // Original values of columns involved in the permutation.
1064 let original = |column, row| {
1065 self.cs
1066 .permutation
1067 .get_columns()
1068 .get(column)
1069 .map(|c: &Column<Any>| match c.column_type() {
1070 Any::Advice(_) => advice[c.index()][row],
1071 Any::Fixed => self.fixed[c.index()][row],
1072 Any::Instance => {
1073 let cell: &InstanceValue<F> = &self.instance[c.index()][row];
1074 CellValue::Assigned(cell.value())
1075 }
1076 })
1077 .unwrap()
1078 };
1079
1080 // Iterate over each column of the permutation
1081 mapping.enumerate().flat_map(move |(column, values)| {
1082 // Iterate over each row of the column to check that the cell's
1083 // value is preserved by the mapping.
1084 values
1085 .enumerate()
1086 .filter_map(move |(row, cell)| {
1087 let original_cell = original(column, row);
1088 let permuted_cell = original(cell.0, cell.1);
1089 if original_cell == permuted_cell {
1090 None
1091 } else {
1092 let columns = self.cs.permutation.get_columns();
1093 let column = columns.get(column).unwrap();
1094 Some(VerifyFailure::Permutation {
1095 column: (*column).into(),
1096 location: FailureLocation::find(
1097 &self.regions,
1098 row,
1099 Some(column).into_iter().cloned().collect(),
1100 ),
1101 })
1102 }
1103 })
1104 .collect::<Vec<_>>()
1105 })
1106 };
1107
1108 let mut errors: Vec<_> = iter::empty()
1109 //.chain(selector_errors)
1110 .chain(gate_errors)
1111 .chain(lookup_errors)
1112 .chain(perm_errors)
1113 .collect();
1114 if errors.is_empty() {
1115 Ok(())
1116 } else {
1117 // Remove any duplicate `ConstraintPoisoned` errors (we check all unavailable
1118 // rows in case the trigger is row-specific, but the error message only points
1119 // at the constraint).
1120 errors.dedup_by(|a, b| match (a, b) {
1121 (
1122 a @ VerifyFailure::ConstraintPoisoned { .. },
1123 b @ VerifyFailure::ConstraintPoisoned { .. },
1124 ) => a == b,
1125 _ => false,
1126 });
1127 Err(errors)
1128 }
1129 }
1130
1131 /// Returns `Ok(())` if this `MockProver` is satisfied, or a list of errors indicating
1132 /// the reasons that the circuit is not satisfied.
1133 /// Constraints and lookup are checked at `usable_rows`, parallelly.
1134 #[cfg(feature = "multicore")]
1135 pub fn verify_par(&self) -> Result<(), Vec<VerifyFailure>> {
1136 self.verify_at_rows_par(self.usable_rows.clone(), self.usable_rows.clone())
1137 }
1138
1139 /// Returns `Ok(())` if this `MockProver` is satisfied, or a list of errors indicating
1140 /// the reasons that the circuit is not satisfied.
1141 /// Constraints are only checked at `gate_row_ids`, and lookup inputs are only checked at `lookup_input_row_ids`, parallelly.
1142 #[cfg(feature = "multicore")]
1143 pub fn verify_at_rows_par<I: Clone + Iterator<Item = usize>>(
1144 &self,
1145 gate_row_ids: I,
1146 lookup_input_row_ids: I,
1147 ) -> Result<(), Vec<VerifyFailure>> {
1148 let n = self.n as i32;
1149
1150 let gate_row_ids = gate_row_ids.collect::<Vec<_>>();
1151 let lookup_input_row_ids = lookup_input_row_ids.collect::<Vec<_>>();
1152
1153 // check all the row ids are valid
1154 gate_row_ids.par_iter().for_each(|row_id| {
1155 if !self.usable_rows.contains(row_id) {
1156 panic!("invalid gate row id {}", row_id);
1157 }
1158 });
1159 lookup_input_row_ids.par_iter().for_each(|row_id| {
1160 if !self.usable_rows.contains(row_id) {
1161 panic!("invalid gate row id {}", row_id);
1162 }
1163 });
1164
1165 // Check that within each region, all cells used in instantiated gates have been
1166 // assigned to.
1167 let selector_errors = self.regions.iter().enumerate().flat_map(|(r_i, r)| {
1168 r.enabled_selectors.iter().flat_map(move |(selector, at)| {
1169 // Find the gates enabled by this selector
1170 self.cs
1171 .gates
1172 .iter()
1173 // Assume that if a queried selector is enabled, the user wants to use the
1174 // corresponding gate in some way.
1175 //
1176 // TODO: This will trip up on the reverse case, where leaving a selector
1177 // un-enabled keeps a gate enabled. We could alternatively require that
1178 // every selector is explicitly enabled or disabled on every row? But that
1179 // seems messy and confusing.
1180 .enumerate()
1181 .filter(move |(_, g)| g.queried_selectors().contains(selector))
1182 .flat_map(move |(gate_index, gate)| {
1183 at.par_iter()
1184 .flat_map(move |selector_row| {
1185 // Selectors are queried with no rotation.
1186 let gate_row = *selector_row as i32;
1187
1188 gate.queried_cells()
1189 .iter()
1190 .filter_map(move |cell| {
1191 // Determine where this cell should have been assigned.
1192 let cell_row =
1193 ((gate_row + n + cell.rotation.0) % n) as usize;
1194
1195 match cell.column.column_type() {
1196 Any::Instance => {
1197 // Handle instance cells, which are not in the region.
1198 let instance_value =
1199 &self.instance[cell.column.index()][cell_row];
1200 match instance_value {
1201 InstanceValue::Assigned(_) => None,
1202 _ => Some(
1203 VerifyFailure::InstanceCellNotAssigned {
1204 gate: (gate_index, gate.name()).into(),
1205 region: (r_i, r.name.clone()).into(),
1206 gate_offset: *selector_row,
1207 column: cell.column.try_into().unwrap(),
1208 row: cell_row,
1209 },
1210 ),
1211 }
1212 }
1213 _ => {
1214 // Check that it was assigned!
1215 if r.cells.contains_key(&(cell.column, cell_row)) {
1216 None
1217 } else {
1218 Some(VerifyFailure::CellNotAssigned {
1219 gate: (gate_index, gate.name()).into(),
1220 region: (
1221 r_i,
1222 r.name.clone(),
1223 r.annotations.clone(),
1224 )
1225 .into(),
1226 gate_offset: *selector_row,
1227 column: cell.column,
1228 offset: cell_row as isize
1229 - r.rows.unwrap().0 as isize,
1230 })
1231 }
1232 }
1233 }
1234 })
1235 .collect::<Vec<_>>()
1236 })
1237 .collect::<Vec<_>>()
1238 })
1239 })
1240 });
1241
1242 let advice = self
1243 .advice
1244 .iter()
1245 .map(|advice| {
1246 advice
1247 .iter()
1248 .map(|rc| match *rc {
1249 AdviceCellValue::Assigned(ref a) => CellValue::Assigned(a.evaluate()),
1250 AdviceCellValue::Poison(i) => CellValue::Poison(i),
1251 })
1252 .collect::<Vec<_>>()
1253 })
1254 .collect::<Vec<_>>();
1255 let advice = &advice;
1256 // Check that all gates are satisfied for all rows.
1257 let gate_errors = self
1258 .cs
1259 .gates
1260 .iter()
1261 .enumerate()
1262 .flat_map(|(gate_index, gate)| {
1263 let blinding_rows =
1264 (self.n as usize - (self.cs.blinding_factors() + 1))..(self.n as usize);
1265 (gate_row_ids
1266 .clone()
1267 .into_par_iter()
1268 .chain(blinding_rows.into_par_iter()))
1269 .flat_map(move |row| {
1270 let row = row as i32 + n;
1271 gate.polynomials()
1272 .iter()
1273 .enumerate()
1274 .filter_map(move |(poly_index, poly)| {
1275 match poly.evaluate_lazy(
1276 &|scalar| Value::Real(scalar),
1277 &|_| panic!("virtual selectors are removed during optimization"),
1278 &util::load(n, row, &self.cs.fixed_queries, &self.fixed),
1279 &util::load(n, row, &self.cs.advice_queries, advice),
1280 &util::load_instance(
1281 n,
1282 row,
1283 &self.cs.instance_queries,
1284 &self.instance,
1285 ),
1286 &|challenge| Value::Real(self.challenges[challenge.index()]),
1287 &|a| -a,
1288 &|a, b| a + b,
1289 &|a, b| a * b,
1290 &|a, scalar| a * scalar,
1291 &Value::Real(F::ZERO),
1292 ) {
1293 Value::Real(x) if x.is_zero_vartime() => None,
1294 Value::Real(_) => Some(VerifyFailure::ConstraintNotSatisfied {
1295 constraint: (
1296 (gate_index, gate.name()).into(),
1297 poly_index,
1298 gate.constraint_name(poly_index),
1299 )
1300 .into(),
1301 location: FailureLocation::find_expressions(
1302 &self.cs,
1303 &self.regions,
1304 (row - n) as usize,
1305 Some(poly).into_iter(),
1306 ),
1307 cell_values: util::cell_values(
1308 gate,
1309 poly,
1310 &util::load(n, row, &self.cs.fixed_queries, &self.fixed),
1311 &util::load(n, row, &self.cs.advice_queries, advice),
1312 &util::load_instance(
1313 n,
1314 row,
1315 &self.cs.instance_queries,
1316 &self.instance,
1317 ),
1318 ),
1319 }),
1320 Value::Poison => Some(VerifyFailure::ConstraintPoisoned {
1321 constraint: (
1322 (gate_index, gate.name()).into(),
1323 poly_index,
1324 gate.constraint_name(poly_index),
1325 )
1326 .into(),
1327 }),
1328 }
1329 })
1330 .collect::<Vec<_>>()
1331 })
1332 .collect::<Vec<_>>()
1333 });
1334
1335 let load = |expression: &Expression<F>, row| {
1336 expression.evaluate_lazy(
1337 &|scalar| Value::Real(scalar),
1338 &|_| panic!("virtual selectors are removed during optimization"),
1339 &|query| {
1340 self.fixed[query.column_index]
1341 [(row as i32 + n + query.rotation.0) as usize % n as usize]
1342 .into()
1343 },
1344 &|query| {
1345 self.advice[query.column_index]
1346 [(row as i32 + n + query.rotation.0) as usize % n as usize]
1347 .clone()
1348 .into()
1349 },
1350 &|query| {
1351 Value::Real(
1352 self.instance[query.column_index]
1353 [(row as i32 + n + query.rotation.0) as usize % n as usize]
1354 .value(),
1355 )
1356 },
1357 &|challenge| Value::Real(self.challenges[challenge.index()]),
1358 &|a| -a,
1359 &|a, b| a + b,
1360 &|a, b| a * b,
1361 &|a, scalar| a * scalar,
1362 &Value::Real(F::ZERO),
1363 )
1364 };
1365
1366 let mut cached_table = Vec::new();
1367 let mut cached_table_identifier = Vec::new();
1368 // Check that all lookups exist in their respective tables.
1369 let lookup_errors =
1370 self.cs
1371 .lookups
1372 .iter()
1373 .enumerate()
1374 .flat_map(|(lookup_index, lookup)| {
1375 assert!(lookup.table_expressions.len() == lookup.input_expressions.len());
1376 assert!(self.usable_rows.end > 0);
1377
1378 // We optimize on the basis that the table might have been filled so that the last
1379 // usable row now has the fill contents (it doesn't matter if there was no filling).
1380 // Note that this "fill row" necessarily exists in the table, and we use that fact to
1381 // slightly simplify the optimization: we're only trying to check that all input rows
1382 // are contained in the table, and so we can safely just drop input rows that
1383 // match the fill row.
1384 let fill_row: Vec<_> = lookup
1385 .table_expressions
1386 .iter()
1387 .map(move |c| load(c, self.usable_rows.end - 1))
1388 .collect();
1389
1390 let table_identifier = lookup
1391 .table_expressions
1392 .iter()
1393 .map(Expression::identifier)
1394 .collect::<Vec<_>>();
1395 if table_identifier != cached_table_identifier {
1396 cached_table_identifier = table_identifier;
1397
1398 // In the real prover, the lookup expressions are never enforced on
1399 // unusable rows, due to the (1 - (l_last(X) + l_blind(X))) term.
1400 cached_table = self
1401 .usable_rows
1402 .clone()
1403 .into_par_iter()
1404 .filter_map(|table_row| {
1405 let t = lookup
1406 .table_expressions
1407 .iter()
1408 .map(move |c| load(c, table_row))
1409 .collect();
1410
1411 if t != fill_row {
1412 Some(t)
1413 } else {
1414 None
1415 }
1416 })
1417 .collect();
1418 cached_table.par_sort_unstable();
1419 }
1420 let table = &cached_table;
1421
1422 let mut inputs: Vec<(Vec<_>, usize)> = lookup_input_row_ids
1423 .clone()
1424 .into_par_iter()
1425 .filter_map(|input_row| {
1426 let t = lookup
1427 .input_expressions
1428 .iter()
1429 .map(move |c| load(c, input_row))
1430 .collect();
1431
1432 if t != fill_row {
1433 // Also keep track of the original input row, since we're going to sort.
1434 Some((t, input_row))
1435 } else {
1436 None
1437 }
1438 })
1439 .collect();
1440 inputs.par_sort_unstable();
1441
1442 inputs
1443 .par_iter()
1444 .filter_map(move |(input, input_row)| {
1445 if table.binary_search(input).is_err() {
1446 Some(VerifyFailure::Lookup {
1447 name: lookup.name.clone(),
1448 lookup_index,
1449 location: FailureLocation::find_expressions(
1450 &self.cs,
1451 &self.regions,
1452 *input_row,
1453 lookup.input_expressions.iter(),
1454 ),
1455 })
1456 } else {
1457 None
1458 }
1459 })
1460 .collect::<Vec<_>>()
1461 });
1462
1463 let mapping = self.permutation.mapping();
1464 // Check that permutations preserve the original values of the cells.
1465 let perm_errors = {
1466 // Original values of columns involved in the permutation.
1467 let original = |column, row| {
1468 self.cs
1469 .permutation
1470 .get_columns()
1471 .get(column)
1472 .map(|c: &Column<Any>| match c.column_type() {
1473 Any::Advice(_) => advice[c.index()][row],
1474 Any::Fixed => self.fixed[c.index()][row],
1475 Any::Instance => {
1476 let cell: &InstanceValue<F> = &self.instance[c.index()][row];
1477 CellValue::Assigned(cell.value())
1478 }
1479 })
1480 .unwrap()
1481 };
1482
1483 // Iterate over each column of the permutation
1484 mapping.enumerate().flat_map(move |(column, values)| {
1485 // Iterate over each row of the column to check that the cell's
1486 // value is preserved by the mapping.
1487 values
1488 .enumerate()
1489 .filter_map(move |(row, cell)| {
1490 let original_cell = original(column, row);
1491 let permuted_cell = original(cell.0, cell.1);
1492 if original_cell == permuted_cell {
1493 None
1494 } else {
1495 let columns = self.cs.permutation.get_columns();
1496 let column = columns.get(column).unwrap();
1497 Some(VerifyFailure::Permutation {
1498 column: (*column).into(),
1499 location: FailureLocation::find(
1500 &self.regions,
1501 row,
1502 Some(column).into_iter().cloned().collect(),
1503 ),
1504 })
1505 }
1506 })
1507 .collect::<Vec<_>>()
1508 })
1509 };
1510
1511 let mut errors: Vec<_> = iter::empty()
1512 .chain(selector_errors)
1513 .chain(gate_errors)
1514 .chain(lookup_errors)
1515 .chain(perm_errors)
1516 .collect();
1517 if errors.is_empty() {
1518 Ok(())
1519 } else {
1520 // Remove any duplicate `ConstraintPoisoned` errors (we check all unavailable
1521 // rows in case the trigger is row-specific, but the error message only points
1522 // at the constraint).
1523 errors.dedup_by(|a, b| match (a, b) {
1524 (
1525 a @ VerifyFailure::ConstraintPoisoned { .. },
1526 b @ VerifyFailure::ConstraintPoisoned { .. },
1527 ) => a == b,
1528 _ => false,
1529 });
1530 Err(errors)
1531 }
1532 }
1533
1534 /// Panics if the circuit being checked by this `MockProver` is not satisfied.
1535 ///
1536 /// Any verification failures will be pretty-printed to stderr before the function
1537 /// panics.
1538 ///
1539 /// Apart from the stderr output, this method is equivalent to:
1540 /// ```ignore
1541 /// assert_eq!(prover.verify(), Ok(()));
1542 /// ```
1543 pub fn assert_satisfied(&self) {
1544 if let Err(errs) = self.verify() {
1545 for err in errs {
1546 err.emit(self);
1547 eprintln!();
1548 }
1549 panic!("circuit was not satisfied");
1550 }
1551 }
1552
1553 /// Panics if the circuit being checked by this `MockProver` is not satisfied.
1554 ///
1555 /// Any verification failures will be pretty-printed to stderr before the function
1556 /// panics.
1557 ///
1558 /// Internally, this function uses a parallel aproach in order to verify the `MockProver` contents.
1559 ///
1560 /// Apart from the stderr output, this method is equivalent to:
1561 /// ```ignore
1562 /// assert_eq!(prover.verify_par(), Ok(()));
1563 /// ```
1564 #[cfg(feature = "multicore")]
1565 pub fn assert_satisfied_par(&self) {
1566 if let Err(errs) = self.verify_par() {
1567 for err in errs {
1568 err.emit(self);
1569 eprintln!();
1570 }
1571 panic!("circuit was not satisfied");
1572 }
1573 }
1574
1575 /// Panics if the circuit being checked by this `MockProver` is not satisfied.
1576 ///
1577 /// Any verification failures will be pretty-printed to stderr before the function
1578 /// panics.
1579 ///
1580 /// Constraints are only checked at `gate_row_ids`, and lookup inputs are only checked at `lookup_input_row_ids`, parallelly.
1581 ///
1582 /// Apart from the stderr output, this method is equivalent to:
1583 /// ```ignore
1584 /// assert_eq!(prover.verify_at_rows_par(), Ok(()));
1585 /// ```
1586 #[cfg(feature = "multicore")]
1587 pub fn assert_satisfied_at_rows_par<I: Clone + Iterator<Item = usize>>(
1588 &self,
1589 gate_row_ids: I,
1590 lookup_input_row_ids: I,
1591 ) {
1592 if let Err(errs) = self.verify_at_rows_par(gate_row_ids, lookup_input_row_ids) {
1593 for err in errs {
1594 err.emit(self);
1595 eprintln!();
1596 }
1597 panic!("circuit was not satisfied");
1598 }
1599 }
1600
1601 /// Returns the list of Fixed Columns used within a MockProver instance and the associated values contained on each Cell.
1602 pub fn fixed(&self) -> &Vec<Vec<CellValue<F>>> {
1603 &self.fixed
1604 }
1605
1606 /// Returns the permutation argument (`Assembly`) used within a MockProver instance.
1607 pub fn permutation(&self) -> &Assembly {
1608 &self.permutation
1609 }
1610}
1611
1612#[cfg(test)]
1613mod tests {
1614 use halo2curves::pasta::Fp;
1615
1616 use super::{FailureLocation, MockProver, VerifyFailure};
1617 use crate::{
1618 circuit::{Layouter, SimpleFloorPlanner, Value},
1619 plonk::{
1620 sealed::SealedPhase, Advice, Any, Assigned, Circuit, Column, ConstraintSystem, Error,
1621 Expression, FirstPhase, Fixed, Instance, Selector, TableColumn,
1622 },
1623 poly::Rotation,
1624 };
1625
1626 #[test]
1627 fn unassigned_cell() {
1628 const K: u32 = 4;
1629
1630 #[derive(Clone)]
1631 struct FaultyCircuitConfig {
1632 a: Column<Advice>,
1633 b: Column<Advice>,
1634 q: Selector,
1635 }
1636
1637 struct FaultyCircuit {}
1638
1639 impl Circuit<Fp> for FaultyCircuit {
1640 type Config = FaultyCircuitConfig;
1641 type FloorPlanner = SimpleFloorPlanner;
1642 type Params = ();
1643 #[cfg(feature = "circuit-params")]
1644 fn params(&self) {}
1645
1646 fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
1647 let a = meta.advice_column();
1648 let b = meta.advice_column();
1649 let q = meta.selector();
1650
1651 meta.create_gate("Equality check", |cells| {
1652 let a = cells.query_advice(a, Rotation::prev());
1653 let b = cells.query_advice(b, Rotation::cur());
1654 let q = cells.query_selector(q);
1655
1656 // If q is enabled, a and b must be assigned to.
1657 vec![q * (a - b)]
1658 });
1659
1660 FaultyCircuitConfig { a, b, q }
1661 }
1662
1663 fn without_witnesses(&self) -> Self {
1664 Self {}
1665 }
1666
1667 fn synthesize(
1668 &self,
1669 config: Self::Config,
1670 mut layouter: impl Layouter<Fp>,
1671 ) -> Result<(), Error> {
1672 layouter.assign_region(
1673 || "Faulty synthesis",
1674 |mut region| {
1675 // Enable the equality gate.
1676 config.q.enable(&mut region, 1)?;
1677
1678 // Assign a = 0.
1679 region.assign_advice(
1680 /*|| "a",*/ config.a,
1681 0,
1682 Value::known(Assigned::Trivial(Fp::zero())),
1683 );
1684
1685 // Name Column a
1686 region.name_column(|| "This is annotated!", config.a);
1687
1688 // Name Column b
1689 region.name_column(|| "This is also annotated!", config.b);
1690
1691 // Name Column a
1692 region.name_column(|| "This is annotated!", config.a);
1693
1694 // Name Column b
1695 region.name_column(|| "This is also annotated!", config.b);
1696
1697 // BUG: Forget to assign b = 0! This could go unnoticed during
1698 // development, because cell values default to zero, which in this
1699 // case is fine, but for other assignments would be broken.
1700 Ok(())
1701 },
1702 )
1703 }
1704 }
1705
1706 let prover = MockProver::run(K, &FaultyCircuit {}, vec![]).unwrap();
1707 assert_eq!(
1708 prover.verify(),
1709 Ok(()) // Axiom mock prover default assigns 0, so there is no bug
1710 /*Err(vec![VerifyFailure::CellNotAssigned {
1711 gate: (0, "Equality check").into(),
1712 region: (0, "Faulty synthesis".to_owned()).into(),
1713 gate_offset: 1,
1714 column: Column::new(
1715 1,
1716 Any::Advice(Advice {
1717 phase: FirstPhase.to_sealed()
1718 })
1719 ),
1720 offset: 1,
1721 }])*/
1722 );
1723 }
1724
1725 #[test]
1726 fn bad_lookup_any() {
1727 const K: u32 = 4;
1728
1729 #[derive(Clone)]
1730 struct FaultyCircuitConfig {
1731 a: Column<Advice>,
1732 table: Column<Instance>,
1733 advice_table: Column<Advice>,
1734 q: Selector,
1735 }
1736
1737 struct FaultyCircuit {}
1738
1739 impl Circuit<Fp> for FaultyCircuit {
1740 type Config = FaultyCircuitConfig;
1741 type FloorPlanner = SimpleFloorPlanner;
1742 type Params = ();
1743
1744 fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
1745 let a = meta.advice_column();
1746 let q = meta.complex_selector();
1747 let table = meta.instance_column();
1748 let advice_table = meta.advice_column();
1749
1750 meta.annotate_lookup_any_column(table, || "Inst-Table");
1751 meta.enable_equality(table);
1752 meta.annotate_lookup_any_column(advice_table, || "Adv-Table");
1753 meta.enable_equality(advice_table);
1754
1755 meta.lookup_any("lookup", |cells| {
1756 let a = cells.query_advice(a, Rotation::cur());
1757 let q = cells.query_selector(q);
1758 let advice_table = cells.query_advice(advice_table, Rotation::cur());
1759 let table = cells.query_instance(table, Rotation::cur());
1760
1761 // If q is enabled, a must be in the table.
1762 // When q is not enabled, lookup the default value instead.
1763 let not_q = Expression::Constant(Fp::one()) - q.clone();
1764 let default = Expression::Constant(Fp::from(2));
1765 vec![
1766 (
1767 q.clone() * a.clone() + not_q.clone() * default.clone(),
1768 table,
1769 ),
1770 (q * a + not_q * default, advice_table),
1771 ]
1772 });
1773
1774 FaultyCircuitConfig {
1775 a,
1776 q,
1777 table,
1778 advice_table,
1779 }
1780 }
1781
1782 fn without_witnesses(&self) -> Self {
1783 Self {}
1784 }
1785
1786 fn synthesize(
1787 &self,
1788 config: Self::Config,
1789 mut layouter: impl Layouter<Fp>,
1790 ) -> Result<(), Error> {
1791 // No assignment needed for the table as is an Instance Column.
1792
1793 layouter.assign_region(
1794 || "Bad synthesis",
1795 |mut region| {
1796 // == Good synthesis ==
1797 // Enable the lookup on rows 0 and 1.
1798 config.q.enable(&mut region, 0)?;
1799 config.q.enable(&mut region, 1)?;
1800
1801 for i in 0..4 {
1802 // Load Advice lookup table with Instance lookup table values.
1803 region.assign_advice_from_instance(
1804 || "Advice from instance tables",
1805 config.table,
1806 i,
1807 config.advice_table,
1808 i,
1809 )?;
1810 }
1811
1812 // Assign a = 2 and a = 6.
1813 region.assign_advice(config.a, 0, Value::known(Fp::from(2)));
1814 region.assign_advice(config.a, 1, Value::known(Fp::from(6)));
1815
1816 // == Faulty synthesis ==
1817 // Enable the lookup on rows 0 and 1.
1818 config.q.enable(&mut region, 2)?;
1819 config.q.enable(&mut region, 3)?;
1820
1821 // Assign a = 4.
1822 region.assign_advice(config.a, 2, Value::known(Fp::from(4)));
1823
1824 // BUG: Assign a = 5, which doesn't exist in the table!
1825 region.assign_advice(config.a, 3, Value::known(Fp::from(5)));
1826
1827 region.name_column(|| "Witness example", config.a);
1828
1829 Ok(())
1830 },
1831 )
1832 }
1833 }
1834
1835 let prover = MockProver::run(
1836 K,
1837 &FaultyCircuit {},
1838 // This is our "lookup table".
1839 vec![vec![
1840 Fp::from(1u64),
1841 Fp::from(2u64),
1842 Fp::from(4u64),
1843 Fp::from(6u64),
1844 ]],
1845 )
1846 .unwrap();
1847 assert_eq!(
1848 prover.verify(),
1849 Err(vec![VerifyFailure::Lookup {
1850 name: "lookup".to_string(),
1851 lookup_index: 0,
1852 location: FailureLocation::OutsideRegion { row: 3 }
1853 }])
1854 );
1855 }
1856
1857 #[test]
1858 fn bad_fixed_lookup() {
1859 const K: u32 = 4;
1860
1861 #[derive(Clone)]
1862 struct FaultyCircuitConfig {
1863 a: Column<Advice>,
1864 q: Selector,
1865 table: TableColumn,
1866 }
1867
1868 struct FaultyCircuit {}
1869
1870 impl Circuit<Fp> for FaultyCircuit {
1871 type Config = FaultyCircuitConfig;
1872 type FloorPlanner = SimpleFloorPlanner;
1873 #[cfg(feature = "circuit-params")]
1874 type Params = ();
1875
1876 fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
1877 let a = meta.advice_column();
1878 let q = meta.complex_selector();
1879 let table = meta.lookup_table_column();
1880 meta.annotate_lookup_column(table, || "Table1");
1881
1882 meta.lookup("lookup", |cells| {
1883 let a = cells.query_advice(a, Rotation::cur());
1884 let q = cells.query_selector(q);
1885
1886 // If q is enabled, a must be in the table.
1887 // When q is not enabled, lookup the default value instead.
1888 let not_q = Expression::Constant(Fp::one()) - q.clone();
1889 let default = Expression::Constant(Fp::from(2));
1890 vec![(q * a + not_q * default, table)]
1891 });
1892
1893 FaultyCircuitConfig { a, q, table }
1894 }
1895
1896 fn without_witnesses(&self) -> Self {
1897 Self {}
1898 }
1899
1900 fn synthesize(
1901 &self,
1902 config: Self::Config,
1903 mut layouter: impl Layouter<Fp>,
1904 ) -> Result<(), Error> {
1905 layouter.assign_table(
1906 || "Doubling table",
1907 |mut table| {
1908 (1..(1 << (K - 1)))
1909 .map(|i| {
1910 table.assign_cell(
1911 || format!("table[{}] = {}", i, 2 * i),
1912 config.table,
1913 i - 1,
1914 || Value::known(Fp::from(2 * i as u64)),
1915 )
1916 })
1917 .try_fold((), |_, res| res)
1918 },
1919 )?;
1920
1921 layouter.assign_region(
1922 || "Fauly synthesis",
1923 |mut region| {
1924 // == Good synhesis ==
1925 // Enable the lookup on rows 0 and 1.
1926 config.q.enable(&mut region, 0)?;
1927 config.q.enable(&mut region, 1)?;
1928
1929 // Assign a = 2 and a = 6.
1930 region.assign_advice(
1931 // || "a = 2",
1932 config.a,
1933 0,
1934 Value::known(Assigned::Trivial(Fp::from(2))),
1935 );
1936 region.assign_advice(
1937 // || "a = 6",
1938 config.a,
1939 1,
1940 Value::known(Assigned::Trivial(Fp::from(6))),
1941 );
1942
1943 // == Faulty synthesis ==
1944 // Enable the lookup on rows 2 and 3.
1945 config.q.enable(&mut region, 2)?;
1946 config.q.enable(&mut region, 3)?;
1947
1948 // Assign a = 4.
1949 region.assign_advice(
1950 config.a,
1951 2,
1952 Value::known(Assigned::Trivial(Fp::from(4))),
1953 );
1954
1955 // BUG: Assign a = 5, which doesn't exist in the table!
1956 region.assign_advice(
1957 config.a,
1958 3,
1959 Value::known(Assigned::Trivial(Fp::from(5))),
1960 );
1961
1962 region.name_column(|| "Witness example", config.a);
1963
1964 region.name_column(|| "Witness example", config.a);
1965
1966 Ok(())
1967 },
1968 )
1969 }
1970 }
1971
1972 let prover = MockProver::run(K, &FaultyCircuit {}, vec![]).unwrap();
1973 assert_eq!(
1974 prover.verify(),
1975 Err(vec![VerifyFailure::Lookup {
1976 name: "lookup".to_string(),
1977 lookup_index: 0,
1978 location: FailureLocation::OutsideRegion { row: 3 }
1979 }])
1980 );
1981 }
1982
1983 #[test]
1984 fn contraint_unsatisfied() {
1985 const K: u32 = 4;
1986
1987 #[derive(Clone)]
1988 struct FaultyCircuitConfig {
1989 a: Column<Advice>,
1990 b: Column<Advice>,
1991 c: Column<Advice>,
1992 d: Column<Fixed>,
1993 q: Selector,
1994 }
1995
1996 struct FaultyCircuit {}
1997
1998 impl Circuit<Fp> for FaultyCircuit {
1999 type Config = FaultyCircuitConfig;
2000 type FloorPlanner = SimpleFloorPlanner;
2001 type Params = ();
2002
2003 fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
2004 let a = meta.advice_column();
2005 let b = meta.advice_column();
2006 let c = meta.advice_column();
2007 let d = meta.fixed_column();
2008 let q = meta.selector();
2009
2010 meta.create_gate("Equality check", |cells| {
2011 let a = cells.query_advice(a, Rotation::cur());
2012 let b = cells.query_advice(b, Rotation::cur());
2013 let c = cells.query_advice(c, Rotation::cur());
2014 let d = cells.query_fixed(d, Rotation::cur());
2015 let q = cells.query_selector(q);
2016
2017 // If q is enabled, a and b must be assigned to.
2018 vec![q * (a - b) * (c - d)]
2019 });
2020
2021 FaultyCircuitConfig { a, b, c, d, q }
2022 }
2023
2024 fn without_witnesses(&self) -> Self {
2025 Self {}
2026 }
2027
2028 fn synthesize(
2029 &self,
2030 config: Self::Config,
2031 mut layouter: impl Layouter<Fp>,
2032 ) -> Result<(), Error> {
2033 layouter.assign_region(
2034 || "Wrong synthesis",
2035 |mut region| {
2036 // == Correct synthesis ==
2037 // Enable the equality gate.
2038 config.q.enable(&mut region, 0)?;
2039
2040 // Assign a = 1.
2041 region.assign_advice(config.a, 0, Value::known(Fp::one()));
2042
2043 // Assign b = 1.
2044 region.assign_advice(config.b, 0, Value::known(Fp::one()));
2045
2046 // Assign c = 5.
2047 region.assign_advice(config.c, 0, Value::known(Fp::from(5u64)));
2048 // Assign d = 7.
2049 region.assign_fixed(config.d, 0, Fp::from(7u64));
2050
2051 // == Wrong synthesis ==
2052 // Enable the equality gate.
2053 config.q.enable(&mut region, 1)?;
2054
2055 // Assign a = 1.
2056 region.assign_advice(config.a, 1, Value::known(Fp::one()));
2057
2058 // Assign b = 0.
2059 region.assign_advice(config.b, 1, Value::known(Fp::zero()));
2060
2061 // Name Column a
2062 region.name_column(|| "This is Advice!", config.a);
2063 // Name Column b
2064 region.name_column(|| "This is Advice too!", config.b);
2065
2066 // Assign c = 5.
2067 region.assign_advice(config.c, 1, Value::known(Fp::from(5u64)));
2068 // Assign d = 7.
2069 region.assign_fixed(config.d, 1, Fp::from(7u64));
2070
2071 // Name Column c
2072 region.name_column(|| "Another one!", config.c);
2073 // Name Column d
2074 region.name_column(|| "This is a Fixed!", config.d);
2075
2076 // Note that none of the terms cancel eachother. Therefore we will have a constraint that is non satisfied for
2077 // the `Equalty check` gate.
2078 Ok(())
2079 },
2080 )
2081 }
2082 }
2083
2084 let prover = MockProver::run(K, &FaultyCircuit {}, vec![]).unwrap();
2085 assert_eq!(
2086 prover.verify(),
2087 Err(vec![VerifyFailure::ConstraintNotSatisfied {
2088 constraint: ((0, "Equality check").into(), 0, "").into(),
2089 location: FailureLocation::InRegion {
2090 region: (0, "Wrong synthesis").into(),
2091 offset: 1,
2092 },
2093 cell_values: vec![
2094 (
2095 (
2096 (
2097 Any::Advice(Advice {
2098 phase: FirstPhase.to_sealed()
2099 }),
2100 0
2101 )
2102 .into(),
2103 0
2104 )
2105 .into(),
2106 "1".to_string()
2107 ),
2108 (
2109 (
2110 (
2111 Any::Advice(Advice {
2112 phase: FirstPhase.to_sealed()
2113 }),
2114 1
2115 )
2116 .into(),
2117 0
2118 )
2119 .into(),
2120 "0".to_string()
2121 ),
2122 (
2123 (
2124 (
2125 Any::Advice(Advice {
2126 phase: FirstPhase.to_sealed()
2127 }),
2128 2
2129 )
2130 .into(),
2131 0
2132 )
2133 .into(),
2134 "0x5".to_string()
2135 ),
2136 (((Any::Fixed, 0).into(), 0).into(), "0x7".to_string()),
2137 ],
2138 },])
2139 )
2140 }
2141}