halo2_proofs/
dev.rs

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