halo2_proofs/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};
7
8use ff::Field;
9
10use crate::plonk::Assigned;
11use crate::{
12 circuit,
13 plonk::{
14 permutation, Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Expression,
15 Fixed, FloorPlanner, Instance, Selector,
16 },
17};
18
19pub mod metadata;
20mod util;
21
22mod failure;
23pub use failure::{FailureLocation, VerifyFailure};
24
25pub mod cost;
26pub use cost::CircuitCost;
27
28mod gates;
29pub use gates::CircuitGates;
30
31mod tfp;
32pub use tfp::TracingFloorPlanner;
33
34#[cfg(feature = "dev-graph")]
35mod graph;
36
37#[cfg(feature = "dev-graph")]
38#[cfg_attr(docsrs, doc(cfg(feature = "dev-graph")))]
39pub use graph::{circuit_dot_graph, layout::CircuitLayout};
40
41#[derive(Debug)]
42struct Region {
43 /// The name of the region. Not required to be unique.
44 name: String,
45 /// The columns involved in this region.
46 columns: HashSet<Column<Any>>,
47 /// The rows that this region starts and ends on, if known.
48 rows: Option<(usize, usize)>,
49 /// The selectors that have been enabled in this region. All other selectors are by
50 /// construction not enabled.
51 enabled_selectors: HashMap<Selector, Vec<usize>>,
52 /// The cells assigned in this region. We store this as a `Vec` so that if any cells
53 /// are double-assigned, they will be visibly darker.
54 cells: Vec<(Column<Any>, usize)>,
55}
56
57impl Region {
58 fn update_extent(&mut self, column: Column<Any>, row: usize) {
59 self.columns.insert(column);
60
61 // The region start is the earliest row assigned to.
62 // The region end is the latest row assigned to.
63 let (mut start, mut end) = self.rows.unwrap_or((row, row));
64 if row < start {
65 // The first row assigned was not at start 0 within the region.
66 start = row;
67 }
68 if row > end {
69 end = row;
70 }
71 self.rows = Some((start, end));
72 }
73}
74
75/// The value of a particular cell within the circuit.
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
77enum CellValue<F: Field> {
78 // An unassigned cell.
79 Unassigned,
80 // A cell that has been assigned a value.
81 Assigned(F),
82 // A unique poisoned cell.
83 Poison(usize),
84}
85
86/// A value within an expression.
87#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
88enum Value<F: Field> {
89 Real(F),
90 Poison,
91}
92
93impl<F: Field> From<CellValue<F>> for Value<F> {
94 fn from(value: CellValue<F>) -> Self {
95 match value {
96 // Cells that haven't been explicitly assigned to, default to zero.
97 CellValue::Unassigned => Value::Real(F::ZERO),
98 CellValue::Assigned(v) => Value::Real(v),
99 CellValue::Poison(_) => Value::Poison,
100 }
101 }
102}
103
104impl<F: Field> Neg for Value<F> {
105 type Output = Self;
106
107 fn neg(self) -> Self::Output {
108 match self {
109 Value::Real(a) => Value::Real(-a),
110 _ => Value::Poison,
111 }
112 }
113}
114
115impl<F: Field> Add for Value<F> {
116 type Output = Self;
117
118 fn add(self, rhs: Self) -> Self::Output {
119 match (self, rhs) {
120 (Value::Real(a), Value::Real(b)) => Value::Real(a + b),
121 _ => Value::Poison,
122 }
123 }
124}
125
126impl<F: Field> Mul for Value<F> {
127 type Output = Self;
128
129 fn mul(self, rhs: Self) -> Self::Output {
130 match (self, rhs) {
131 (Value::Real(a), Value::Real(b)) => Value::Real(a * b),
132 // If poison is multiplied by zero, then we treat the poison as unconstrained
133 // and we don't propagate it.
134 (Value::Real(x), Value::Poison) | (Value::Poison, Value::Real(x))
135 if x.is_zero_vartime() =>
136 {
137 Value::Real(F::ZERO)
138 }
139 _ => Value::Poison,
140 }
141 }
142}
143
144impl<F: Field> Mul<F> for Value<F> {
145 type Output = Self;
146
147 fn mul(self, rhs: F) -> Self::Output {
148 match self {
149 Value::Real(lhs) => Value::Real(lhs * rhs),
150 // If poison is multiplied by zero, then we treat the poison as unconstrained
151 // and we don't propagate it.
152 Value::Poison if rhs.is_zero_vartime() => Value::Real(F::ZERO),
153 _ => Value::Poison,
154 }
155 }
156}
157
158/// A test prover for debugging circuits.
159///
160/// The normal proving process, when applied to a buggy circuit implementation, might
161/// return proofs that do not validate when they should, but it can't indicate anything
162/// other than "something is invalid". `MockProver` can be used to figure out _why_ these
163/// are invalid: it stores all the private inputs along with the circuit internals, and
164/// then checks every constraint manually.
165///
166/// # Examples
167///
168/// ```
169/// use group::ff::PrimeField;
170/// use halo2_proofs::{
171/// circuit::{Layouter, SimpleFloorPlanner, Value},
172/// dev::{FailureLocation, MockProver, VerifyFailure},
173/// pasta::Fp,
174/// plonk::{Advice, Any, Circuit, Column, ConstraintSystem, Error, Selector},
175/// poly::Rotation,
176/// };
177/// const K: u32 = 5;
178///
179/// #[derive(Copy, Clone)]
180/// struct MyConfig {
181/// a: Column<Advice>,
182/// b: Column<Advice>,
183/// c: Column<Advice>,
184/// s: Selector,
185/// }
186///
187/// #[derive(Clone, Default)]
188/// struct MyCircuit {
189/// a: Value<u64>,
190/// b: Value<u64>,
191/// }
192///
193/// impl<F: PrimeField> Circuit<F> for MyCircuit {
194/// type Config = MyConfig;
195/// type FloorPlanner = SimpleFloorPlanner;
196///
197/// fn without_witnesses(&self) -> Self {
198/// Self::default()
199/// }
200///
201/// fn configure(meta: &mut ConstraintSystem<F>) -> MyConfig {
202/// let a = meta.advice_column();
203/// let b = meta.advice_column();
204/// let c = meta.advice_column();
205/// let s = meta.selector();
206///
207/// meta.create_gate("R1CS constraint", |meta| {
208/// let a = meta.query_advice(a, Rotation::cur());
209/// let b = meta.query_advice(b, Rotation::cur());
210/// let c = meta.query_advice(c, Rotation::cur());
211/// let s = meta.query_selector(s);
212///
213/// // BUG: Should be a * b - c
214/// Some(("buggy R1CS", s * (a * b + c)))
215/// });
216///
217/// MyConfig { a, b, c, s }
218/// }
219///
220/// fn synthesize(&self, config: MyConfig, mut layouter: impl Layouter<F>) -> Result<(), Error> {
221/// layouter.assign_region(|| "Example region", |mut region| {
222/// config.s.enable(&mut region, 0)?;
223/// region.assign_advice(|| "a", config.a, 0, || {
224/// self.a.map(F::from)
225/// })?;
226/// region.assign_advice(|| "b", config.b, 0, || {
227/// self.b.map(F::from)
228/// })?;
229/// region.assign_advice(|| "c", config.c, 0, || {
230/// (self.a * self.b).map(F::from)
231/// })?;
232/// Ok(())
233/// })
234/// }
235/// }
236///
237/// // Assemble the private inputs to the circuit.
238/// let circuit = MyCircuit {
239/// a: Value::known(2),
240/// b: Value::known(4),
241/// };
242///
243/// // This circuit has no public inputs.
244/// let instance = vec![];
245///
246/// let prover = MockProver::<Fp>::run(K, &circuit, instance).unwrap();
247/// assert_eq!(
248/// prover.verify(),
249/// Err(vec![VerifyFailure::ConstraintNotSatisfied {
250/// constraint: ((0, "R1CS constraint").into(), 0, "buggy R1CS").into(),
251/// location: FailureLocation::InRegion {
252/// region: (0, "Example region").into(),
253/// offset: 0,
254/// },
255/// cell_values: vec![
256/// (((Any::Advice, 0).into(), 0).into(), "0x2".to_string()),
257/// (((Any::Advice, 1).into(), 0).into(), "0x4".to_string()),
258/// (((Any::Advice, 2).into(), 0).into(), "0x8".to_string()),
259/// ],
260/// }])
261/// );
262///
263/// // If we provide a too-small K, we get an error.
264/// assert!(matches!(
265/// MockProver::<Fp>::run(2, &circuit, vec![]).unwrap_err(),
266/// Error::NotEnoughRowsAvailable {
267/// current_k,
268/// } if current_k == 2,
269/// ));
270/// ```
271#[derive(Debug)]
272pub struct MockProver<F: Field> {
273 k: u32,
274 n: u32,
275 cs: ConstraintSystem<F>,
276
277 /// The regions in the circuit.
278 regions: Vec<Region>,
279 /// The current region being assigned to. Will be `None` after the circuit has been
280 /// synthesized.
281 current_region: Option<Region>,
282
283 // The fixed cells in the circuit, arranged as [column][row].
284 fixed: Vec<Vec<CellValue<F>>>,
285 // The advice cells in the circuit, arranged as [column][row].
286 advice: Vec<Vec<CellValue<F>>>,
287 // The instance cells in the circuit, arranged as [column][row].
288 instance: Vec<Vec<InstanceValue<F>>>,
289
290 selectors: Vec<Vec<bool>>,
291
292 permutation: permutation::keygen::Assembly,
293
294 // A range of available rows for assignment and copies.
295 usable_rows: Range<usize>,
296}
297
298#[derive(Debug, Clone, PartialEq, Eq)]
299enum InstanceValue<F: Field> {
300 Assigned(F),
301 Padding,
302}
303
304impl<F: Field> InstanceValue<F> {
305 fn value(&self) -> F {
306 match self {
307 InstanceValue::Assigned(v) => *v,
308 InstanceValue::Padding => F::ZERO,
309 }
310 }
311}
312
313impl<F: Field> Assignment<F> for MockProver<F> {
314 fn enter_region<NR, N>(&mut self, name: N)
315 where
316 NR: Into<String>,
317 N: FnOnce() -> NR,
318 {
319 assert!(self.current_region.is_none());
320 self.current_region = Some(Region {
321 name: name().into(),
322 columns: HashSet::default(),
323 rows: None,
324 enabled_selectors: HashMap::default(),
325 cells: vec![],
326 });
327 }
328
329 fn exit_region(&mut self) {
330 self.regions.push(self.current_region.take().unwrap());
331 }
332
333 fn enable_selector<A, AR>(&mut self, _: A, selector: &Selector, row: usize) -> Result<(), Error>
334 where
335 A: FnOnce() -> AR,
336 AR: Into<String>,
337 {
338 if !self.usable_rows.contains(&row) {
339 return Err(Error::not_enough_rows_available(self.k));
340 }
341
342 // Track that this selector was enabled. We require that all selectors are enabled
343 // inside some region (i.e. no floating selectors).
344 self.current_region
345 .as_mut()
346 .unwrap()
347 .enabled_selectors
348 .entry(*selector)
349 .or_default()
350 .push(row);
351
352 self.selectors[selector.0][row] = true;
353
354 Ok(())
355 }
356
357 fn query_instance(
358 &self,
359 column: Column<Instance>,
360 row: usize,
361 ) -> Result<circuit::Value<F>, Error> {
362 if !self.usable_rows.contains(&row) {
363 return Err(Error::not_enough_rows_available(self.k));
364 }
365
366 self.instance
367 .get(column.index())
368 .and_then(|column| column.get(row))
369 .map(|v| circuit::Value::known(v.value()))
370 .ok_or(Error::BoundsFailure)
371 }
372
373 fn assign_advice<V, VR, A, AR>(
374 &mut self,
375 _: A,
376 column: Column<Advice>,
377 row: usize,
378 to: V,
379 ) -> Result<(), Error>
380 where
381 V: FnOnce() -> circuit::Value<VR>,
382 VR: Into<Assigned<F>>,
383 A: FnOnce() -> AR,
384 AR: Into<String>,
385 {
386 if !self.usable_rows.contains(&row) {
387 return Err(Error::not_enough_rows_available(self.k));
388 }
389
390 if let Some(region) = self.current_region.as_mut() {
391 region.update_extent(column.into(), row);
392 region.cells.push((column.into(), row));
393 }
394
395 *self
396 .advice
397 .get_mut(column.index())
398 .and_then(|v| v.get_mut(row))
399 .ok_or(Error::BoundsFailure)? =
400 CellValue::Assigned(to().into_field().evaluate().assign()?);
401
402 Ok(())
403 }
404
405 fn assign_fixed<V, VR, A, AR>(
406 &mut self,
407 _: A,
408 column: Column<Fixed>,
409 row: usize,
410 to: V,
411 ) -> Result<(), Error>
412 where
413 V: FnOnce() -> circuit::Value<VR>,
414 VR: Into<Assigned<F>>,
415 A: FnOnce() -> AR,
416 AR: Into<String>,
417 {
418 if !self.usable_rows.contains(&row) {
419 return Err(Error::not_enough_rows_available(self.k));
420 }
421
422 if let Some(region) = self.current_region.as_mut() {
423 region.update_extent(column.into(), row);
424 region.cells.push((column.into(), row));
425 }
426
427 *self
428 .fixed
429 .get_mut(column.index())
430 .and_then(|v| v.get_mut(row))
431 .ok_or(Error::BoundsFailure)? =
432 CellValue::Assigned(to().into_field().evaluate().assign()?);
433
434 Ok(())
435 }
436
437 fn copy(
438 &mut self,
439 left_column: Column<Any>,
440 left_row: usize,
441 right_column: Column<Any>,
442 right_row: usize,
443 ) -> Result<(), crate::plonk::Error> {
444 if !self.usable_rows.contains(&left_row) || !self.usable_rows.contains(&right_row) {
445 return Err(Error::not_enough_rows_available(self.k));
446 }
447
448 self.permutation
449 .copy(left_column, left_row, right_column, right_row)
450 }
451
452 fn fill_from_row(
453 &mut self,
454 col: Column<Fixed>,
455 from_row: usize,
456 to: circuit::Value<Assigned<F>>,
457 ) -> Result<(), Error> {
458 if !self.usable_rows.contains(&from_row) {
459 return Err(Error::not_enough_rows_available(self.k));
460 }
461
462 for row in self.usable_rows.clone().skip(from_row) {
463 self.assign_fixed(|| "", col, row, || to)?;
464 }
465
466 Ok(())
467 }
468
469 fn push_namespace<NR, N>(&mut self, _: N)
470 where
471 NR: Into<String>,
472 N: FnOnce() -> NR,
473 {
474 // TODO: Do something with namespaces :)
475 }
476
477 fn pop_namespace(&mut self, _: Option<String>) {
478 // TODO: Do something with namespaces :)
479 }
480}
481
482impl<F: Field + Ord> MockProver<F> {
483 /// Runs a synthetic keygen-and-prove operation on the given circuit, collecting data
484 /// about the constraints and their assignments.
485 pub fn run<ConcreteCircuit: Circuit<F>>(
486 k: u32,
487 circuit: &ConcreteCircuit,
488 instance: Vec<Vec<F>>,
489 ) -> Result<Self, Error> {
490 let n = 1 << k;
491
492 let mut cs = ConstraintSystem::default();
493 let config = ConcreteCircuit::configure(&mut cs);
494 let cs = cs;
495
496 if n < cs.minimum_rows() {
497 return Err(Error::not_enough_rows_available(k));
498 }
499
500 if instance.len() != cs.num_instance_columns {
501 return Err(Error::InvalidInstances);
502 }
503
504 let instance = instance
505 .into_iter()
506 .map(|instance| {
507 if instance.len() > n - (cs.blinding_factors() + 1) {
508 return Err(Error::InstanceTooLarge);
509 }
510
511 let mut instance_values = vec![InstanceValue::Padding; n];
512 for (idx, value) in instance.into_iter().enumerate() {
513 instance_values[idx] = InstanceValue::Assigned(value);
514 }
515
516 Ok(instance_values)
517 })
518 .collect::<Result<Vec<_>, _>>()?;
519
520 // Fixed columns contain no blinding factors.
521 let fixed = vec![vec![CellValue::Unassigned; n]; cs.num_fixed_columns];
522 let selectors = vec![vec![false; n]; cs.num_selectors];
523 // Advice columns contain blinding factors.
524 let blinding_factors = cs.blinding_factors();
525 let usable_rows = n - (blinding_factors + 1);
526 let advice = vec![
527 {
528 let mut column = vec![CellValue::Unassigned; n];
529 // Poison unusable rows.
530 for (i, cell) in column.iter_mut().enumerate().skip(usable_rows) {
531 *cell = CellValue::Poison(i);
532 }
533 column
534 };
535 cs.num_advice_columns
536 ];
537 let permutation = permutation::keygen::Assembly::new(n, &cs.permutation);
538 let constants = cs.constants.clone();
539
540 let mut prover = MockProver {
541 k,
542 n: n as u32,
543 cs,
544 regions: vec![],
545 current_region: None,
546 fixed,
547 advice,
548 instance,
549 selectors,
550 permutation,
551 usable_rows: 0..usable_rows,
552 };
553
554 ConcreteCircuit::FloorPlanner::synthesize(&mut prover, circuit, config, constants)?;
555
556 let (cs, selector_polys) = prover.cs.compress_selectors(prover.selectors.clone());
557 prover.cs = cs;
558 prover.fixed.extend(selector_polys.into_iter().map(|poly| {
559 let mut v = vec![CellValue::Unassigned; n];
560 for (v, p) in v.iter_mut().zip(&poly[..]) {
561 *v = CellValue::Assigned(*p);
562 }
563 v
564 }));
565
566 Ok(prover)
567 }
568
569 /// Returns `Ok(())` if this `MockProver` is satisfied, or a list of errors indicating
570 /// the reasons that the circuit is not satisfied.
571 ///
572 /// Note: When writing positive tests (i.e. checking that a circuit is valid), prefer using
573 /// [`MockProver::assert_satisfied`] which provides more readable error output. This method
574 /// is primarily intended for writing negative tests to check that specific invalid circuit
575 /// constructions fail with expected errors.
576 pub fn verify(&self) -> Result<(), Vec<VerifyFailure>> {
577 let n = self.n as i32;
578
579 // Check that within each region, all cells used in instantiated gates have been
580 // assigned to.
581 let selector_errors = self.regions.iter().enumerate().flat_map(|(r_i, r)| {
582 r.enabled_selectors.iter().flat_map(move |(selector, at)| {
583 // Find the gates enabled by this selector
584 self.cs
585 .gates
586 .iter()
587 // Assume that if a queried selector is enabled, the user wants to use the
588 // corresponding gate in some way.
589 //
590 // TODO: This will trip up on the reverse case, where leaving a selector
591 // un-enabled keeps a gate enabled. We could alternatively require that
592 // every selector is explicitly enabled or disabled on every row? But that
593 // seems messy and confusing.
594 .enumerate()
595 .filter(move |(_, g)| g.queried_selectors().contains(selector))
596 .flat_map(move |(gate_index, gate)| {
597 at.iter().flat_map(move |selector_row| {
598 // Selectors are queried with no rotation.
599 let gate_row = *selector_row as i32;
600
601 gate.queried_cells().iter().filter_map(move |cell| {
602 // Determine where this cell should have been assigned.
603 let cell_row = ((gate_row + n + cell.rotation.0) % n) as usize;
604
605 match cell.column.column_type() {
606 Any::Instance => {
607 // Handle instance cells, which are not in the region.
608 let instance_value =
609 &self.instance[cell.column.index()][cell_row];
610 match instance_value {
611 InstanceValue::Assigned(_) => None,
612 _ => Some(VerifyFailure::InstanceCellNotAssigned {
613 gate: (gate_index, gate.name()).into(),
614 region: (r_i, r.name.clone()).into(),
615 gate_offset: *selector_row,
616 column: cell.column.try_into().unwrap(),
617 row: cell_row,
618 }),
619 }
620 }
621 _ => {
622 // Check that it was assigned!
623 if r.cells.contains(&(cell.column, cell_row)) {
624 None
625 } else {
626 Some(VerifyFailure::CellNotAssigned {
627 gate: (gate_index, gate.name()).into(),
628 region: (r_i, r.name.clone()).into(),
629 gate_offset: *selector_row,
630 column: cell.column,
631 offset: cell_row as isize
632 - r.rows.unwrap().0 as isize,
633 })
634 }
635 }
636 }
637 })
638 })
639 })
640 })
641 });
642
643 // Check that all gates are satisfied for all rows.
644 let gate_errors =
645 self.cs
646 .gates
647 .iter()
648 .enumerate()
649 .flat_map(|(gate_index, gate)| {
650 // We iterate from n..2n so we can just reduce to handle wrapping.
651 (n..(2 * n)).flat_map(move |row| {
652 gate.polynomials().iter().enumerate().filter_map(
653 move |(poly_index, poly)| match poly.evaluate(
654 &|scalar| Value::Real(scalar),
655 &|_| panic!("virtual selectors are removed during optimization"),
656 &util::load(n, row, &self.cs.fixed_queries, &self.fixed),
657 &util::load(n, row, &self.cs.advice_queries, &self.advice),
658 &util::load_instance(
659 n,
660 row,
661 &self.cs.instance_queries,
662 &self.instance,
663 ),
664 &|a| -a,
665 &|a, b| a + b,
666 &|a, b| a * b,
667 &|a, scalar| a * scalar,
668 ) {
669 Value::Real(x) if x.is_zero_vartime() => None,
670 Value::Real(_) => Some(VerifyFailure::ConstraintNotSatisfied {
671 constraint: (
672 (gate_index, gate.name()).into(),
673 poly_index,
674 gate.constraint_name(poly_index),
675 )
676 .into(),
677 location: FailureLocation::find_expressions(
678 &self.cs,
679 &self.regions,
680 (row - n) as usize,
681 Some(poly).into_iter(),
682 ),
683 cell_values: util::cell_values(
684 gate,
685 poly,
686 &util::load(n, row, &self.cs.fixed_queries, &self.fixed),
687 &util::load(n, row, &self.cs.advice_queries, &self.advice),
688 &util::load_instance(
689 n,
690 row,
691 &self.cs.instance_queries,
692 &self.instance,
693 ),
694 ),
695 }),
696 Value::Poison => Some(VerifyFailure::ConstraintPoisoned {
697 constraint: (
698 (gate_index, gate.name()).into(),
699 poly_index,
700 gate.constraint_name(poly_index),
701 )
702 .into(),
703 }),
704 },
705 )
706 })
707 });
708
709 // Check that all lookups exist in their respective tables.
710 let lookup_errors =
711 self.cs
712 .lookups
713 .iter()
714 .enumerate()
715 .flat_map(|(lookup_index, lookup)| {
716 let load = |expression: &Expression<F>, row| {
717 expression.evaluate(
718 &|scalar| Value::Real(scalar),
719 &|_| panic!("virtual selectors are removed during optimization"),
720 &|query| {
721 let query = self.cs.fixed_queries[query.index];
722 let column_index = query.0.index();
723 let rotation = query.1 .0;
724 self.fixed[column_index]
725 [(row as i32 + n + rotation) as usize % n as usize]
726 .into()
727 },
728 &|query| {
729 let query = self.cs.advice_queries[query.index];
730 let column_index = query.0.index();
731 let rotation = query.1 .0;
732 self.advice[column_index]
733 [(row as i32 + n + rotation) as usize % n as usize]
734 .into()
735 },
736 &|query| {
737 let query = self.cs.instance_queries[query.index];
738 let column_index = query.0.index();
739 let rotation = query.1 .0;
740 Value::Real(
741 self.instance[column_index]
742 [(row as i32 + n + rotation) as usize % n as usize]
743 .value(),
744 )
745 },
746 &|a| -a,
747 &|a, b| a + b,
748 &|a, b| a * b,
749 &|a, scalar| a * scalar,
750 )
751 };
752
753 assert!(lookup.table_expressions.len() == lookup.input_expressions.len());
754 assert!(self.usable_rows.end > 0);
755
756 // We optimize on the basis that the table might have been filled so that the last
757 // usable row now has the fill contents (it doesn't matter if there was no filling).
758 // Note that this "fill row" necessarily exists in the table, and we use that fact to
759 // slightly simplify the optimization: we're only trying to check that all input rows
760 // are contained in the table, and so we can safely just drop input rows that
761 // match the fill row.
762 let fill_row: Vec<_> = lookup
763 .table_expressions
764 .iter()
765 .map(move |c| load(c, self.usable_rows.end - 1))
766 .collect();
767
768 // In the real prover, the lookup expressions are never enforced on
769 // unusable rows, due to the (1 - (l_last(X) + l_blind(X))) term.
770 let mut table: Vec<Vec<_>> = self
771 .usable_rows
772 .clone()
773 .filter_map(|table_row| {
774 let t = lookup
775 .table_expressions
776 .iter()
777 .map(move |c| load(c, table_row))
778 .collect();
779
780 if t != fill_row {
781 Some(t)
782 } else {
783 None
784 }
785 })
786 .collect();
787 table.sort_unstable();
788
789 let mut inputs: Vec<(Vec<_>, usize)> = self
790 .usable_rows
791 .clone()
792 .filter_map(|input_row| {
793 let t = lookup
794 .input_expressions
795 .iter()
796 .map(move |c| load(c, input_row))
797 .collect();
798
799 if t != fill_row {
800 // Also keep track of the original input row, since we're going to sort.
801 Some((t, input_row))
802 } else {
803 None
804 }
805 })
806 .collect();
807 inputs.sort_unstable();
808
809 let mut i = 0;
810 inputs
811 .iter()
812 .filter_map(move |(input, input_row)| {
813 while i < table.len() && &table[i] < input {
814 i += 1;
815 }
816 if i == table.len() || &table[i] > input {
817 assert!(table.binary_search(input).is_err());
818
819 Some(VerifyFailure::Lookup {
820 lookup_index,
821 location: FailureLocation::find_expressions(
822 &self.cs,
823 &self.regions,
824 *input_row,
825 lookup.input_expressions.iter(),
826 ),
827 })
828 } else {
829 None
830 }
831 })
832 .collect::<Vec<_>>()
833 });
834
835 // Check that permutations preserve the original values of the cells.
836 let perm_errors = {
837 // Original values of columns involved in the permutation.
838 let original = |column, row| {
839 self.cs
840 .permutation
841 .get_columns()
842 .get(column)
843 .map(|c: &Column<Any>| match c.column_type() {
844 Any::Advice => self.advice[c.index()][row],
845 Any::Fixed => self.fixed[c.index()][row],
846 Any::Instance => {
847 let cell: &InstanceValue<F> = &self.instance[c.index()][row];
848 CellValue::Assigned(cell.value())
849 }
850 })
851 .unwrap()
852 };
853
854 // Iterate over each column of the permutation
855 self.permutation
856 .mapping
857 .iter()
858 .enumerate()
859 .flat_map(move |(column, values)| {
860 // Iterate over each row of the column to check that the cell's
861 // value is preserved by the mapping.
862 values.iter().enumerate().filter_map(move |(row, cell)| {
863 let original_cell = original(column, row);
864 let permuted_cell = original(cell.0, cell.1);
865 if original_cell == permuted_cell {
866 None
867 } else {
868 let columns = self.cs.permutation.get_columns();
869 let column = columns.get(column).unwrap();
870 Some(VerifyFailure::Permutation {
871 column: (*column).into(),
872 location: FailureLocation::find(
873 &self.regions,
874 row,
875 Some(column).into_iter().cloned().collect(),
876 ),
877 })
878 }
879 })
880 })
881 };
882
883 let mut errors: Vec<_> = iter::empty()
884 .chain(selector_errors)
885 .chain(gate_errors)
886 .chain(lookup_errors)
887 .chain(perm_errors)
888 .collect();
889 if errors.is_empty() {
890 Ok(())
891 } else {
892 // Remove any duplicate `ConstraintPoisoned` errors (we check all unavailable
893 // rows in case the trigger is row-specific, but the error message only points
894 // at the constraint).
895 errors.dedup_by(|a, b| match (a, b) {
896 (
897 a @ VerifyFailure::ConstraintPoisoned { .. },
898 b @ VerifyFailure::ConstraintPoisoned { .. },
899 ) => a == b,
900 _ => false,
901 });
902 Err(errors)
903 }
904 }
905
906 /// Panics if the circuit being checked by this `MockProver` is not satisfied.
907 ///
908 /// Any verification failures will be pretty-printed to stderr before the function
909 /// panics.
910 ///
911 /// Apart from the stderr output, this method is equivalent to:
912 /// ```ignore
913 /// assert_eq!(prover.verify(), Ok(()));
914 /// ```
915 pub fn assert_satisfied(&self) {
916 if let Err(errs) = self.verify() {
917 for err in errs {
918 err.emit(self);
919 eprintln!();
920 }
921 panic!("circuit was not satisfied");
922 }
923 }
924}
925
926#[cfg(test)]
927mod tests {
928 use group::ff::Field;
929 use pasta_curves::Fp;
930
931 use super::{FailureLocation, MockProver, VerifyFailure};
932 use crate::{
933 circuit::{Layouter, SimpleFloorPlanner, Value},
934 plonk::{
935 Advice, Any, Circuit, Column, ConstraintSystem, Error, Expression, Selector,
936 TableColumn,
937 },
938 poly::Rotation,
939 };
940
941 #[test]
942 fn unassigned_cell() {
943 const K: u32 = 4;
944
945 #[derive(Clone)]
946 struct FaultyCircuitConfig {
947 a: Column<Advice>,
948 q: Selector,
949 }
950
951 struct FaultyCircuit {}
952
953 impl Circuit<Fp> for FaultyCircuit {
954 type Config = FaultyCircuitConfig;
955 type FloorPlanner = SimpleFloorPlanner;
956
957 fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
958 let a = meta.advice_column();
959 let b = meta.advice_column();
960 let q = meta.selector();
961
962 meta.create_gate("Equality check", |cells| {
963 let a = cells.query_advice(a, Rotation::prev());
964 let b = cells.query_advice(b, Rotation::cur());
965 let q = cells.query_selector(q);
966
967 // If q is enabled, a and b must be assigned to.
968 vec![q * (a - b)]
969 });
970
971 FaultyCircuitConfig { a, q }
972 }
973
974 fn without_witnesses(&self) -> Self {
975 Self {}
976 }
977
978 fn synthesize(
979 &self,
980 config: Self::Config,
981 mut layouter: impl Layouter<Fp>,
982 ) -> Result<(), Error> {
983 layouter.assign_region(
984 || "Faulty synthesis",
985 |mut region| {
986 // Enable the equality gate.
987 config.q.enable(&mut region, 1)?;
988
989 // Assign a = 0.
990 region.assign_advice(|| "a", config.a, 0, || Value::known(Fp::ZERO))?;
991
992 // BUG: Forget to assign b = 0! This could go unnoticed during
993 // development, because cell values default to zero, which in this
994 // case is fine, but for other assignments would be broken.
995 Ok(())
996 },
997 )
998 }
999 }
1000
1001 let prover = MockProver::run(K, &FaultyCircuit {}, vec![]).unwrap();
1002 assert_eq!(
1003 prover.verify(),
1004 Err(vec![VerifyFailure::CellNotAssigned {
1005 gate: (0, "Equality check").into(),
1006 region: (0, "Faulty synthesis".to_owned()).into(),
1007 gate_offset: 1,
1008 column: Column::new(1, Any::Advice),
1009 offset: 1,
1010 }])
1011 );
1012 }
1013
1014 #[test]
1015 fn bad_lookup() {
1016 const K: u32 = 4;
1017
1018 #[derive(Clone)]
1019 struct FaultyCircuitConfig {
1020 a: Column<Advice>,
1021 q: Selector,
1022 table: TableColumn,
1023 }
1024
1025 struct FaultyCircuit {}
1026
1027 impl Circuit<Fp> for FaultyCircuit {
1028 type Config = FaultyCircuitConfig;
1029 type FloorPlanner = SimpleFloorPlanner;
1030
1031 fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
1032 let a = meta.advice_column();
1033 let q = meta.complex_selector();
1034 let table = meta.lookup_table_column();
1035
1036 meta.lookup(|cells| {
1037 let a = cells.query_advice(a, Rotation::cur());
1038 let q = cells.query_selector(q);
1039
1040 // If q is enabled, a must be in the table.
1041 // When q is not enabled, lookup the default value instead.
1042 let not_q = Expression::Constant(Fp::one()) - q.clone();
1043 let default = Expression::Constant(Fp::from(2));
1044 vec![(q * a + not_q * default, table)]
1045 });
1046
1047 FaultyCircuitConfig { a, q, table }
1048 }
1049
1050 fn without_witnesses(&self) -> Self {
1051 Self {}
1052 }
1053
1054 fn synthesize(
1055 &self,
1056 config: Self::Config,
1057 mut layouter: impl Layouter<Fp>,
1058 ) -> Result<(), Error> {
1059 layouter.assign_table(
1060 || "Doubling table",
1061 |mut table| {
1062 (1..(1 << (K - 1)))
1063 .map(|i| {
1064 table.assign_cell(
1065 || format!("table[{}] = {}", i, 2 * i),
1066 config.table,
1067 i - 1,
1068 || Value::known(Fp::from(2 * i as u64)),
1069 )
1070 })
1071 .fold(Ok(()), |acc, res| acc.and(res))
1072 },
1073 )?;
1074
1075 layouter.assign_region(
1076 || "Good synthesis",
1077 |mut region| {
1078 // Enable the lookup on rows 0 and 1.
1079 config.q.enable(&mut region, 0)?;
1080 config.q.enable(&mut region, 1)?;
1081
1082 // Assign a = 2 and a = 6.
1083 region.assign_advice(
1084 || "a = 2",
1085 config.a,
1086 0,
1087 || Value::known(Fp::from(2)),
1088 )?;
1089 region.assign_advice(
1090 || "a = 6",
1091 config.a,
1092 1,
1093 || Value::known(Fp::from(6)),
1094 )?;
1095
1096 Ok(())
1097 },
1098 )?;
1099
1100 layouter.assign_region(
1101 || "Faulty synthesis",
1102 |mut region| {
1103 // Enable the lookup on rows 0 and 1.
1104 config.q.enable(&mut region, 0)?;
1105 config.q.enable(&mut region, 1)?;
1106
1107 // Assign a = 4.
1108 region.assign_advice(
1109 || "a = 4",
1110 config.a,
1111 0,
1112 || Value::known(Fp::from(4)),
1113 )?;
1114
1115 // BUG: Assign a = 5, which doesn't exist in the table!
1116 region.assign_advice(
1117 || "a = 5",
1118 config.a,
1119 1,
1120 || Value::known(Fp::from(5)),
1121 )?;
1122
1123 Ok(())
1124 },
1125 )
1126 }
1127 }
1128
1129 let prover = MockProver::run(K, &FaultyCircuit {}, vec![]).unwrap();
1130 assert_eq!(
1131 prover.verify(),
1132 Err(vec![VerifyFailure::Lookup {
1133 lookup_index: 0,
1134 location: FailureLocation::InRegion {
1135 region: (2, "Faulty synthesis").into(),
1136 offset: 1,
1137 }
1138 }])
1139 );
1140 }
1141}