2020-12-21 15:00:39 -08:00
|
|
|
//! Tools for developing circuits.
|
|
|
|
|
2021-05-27 06:03:12 -07:00
|
|
|
use std::collections::HashMap;
|
2021-06-03 19:18:11 -07:00
|
|
|
use std::fmt;
|
2021-05-27 06:03:12 -07:00
|
|
|
use std::iter;
|
2021-07-25 13:24:49 -07:00
|
|
|
use std::ops::{Add, Mul, Neg, Range};
|
2021-05-27 06:03:12 -07:00
|
|
|
|
2020-12-21 15:00:39 -08:00
|
|
|
use ff::Field;
|
|
|
|
|
2021-06-11 08:39:34 -07:00
|
|
|
use crate::plonk::Assigned;
|
2020-12-21 15:00:39 -08:00
|
|
|
use crate::{
|
|
|
|
arithmetic::{FieldExt, Group},
|
2021-01-22 08:57:38 -08:00
|
|
|
plonk::{
|
2021-02-18 23:07:08 -08:00
|
|
|
permutation, Advice, Any, Assignment, Circuit, Column, ColumnType, ConstraintSystem, Error,
|
2021-07-08 13:44:01 -07:00
|
|
|
Expression, Fixed, FloorPlanner, Instance, Selector,
|
2021-01-22 08:57:38 -08:00
|
|
|
},
|
2021-01-07 04:42:04 -08:00
|
|
|
poly::Rotation,
|
2020-12-21 15:00:39 -08:00
|
|
|
};
|
|
|
|
|
2021-06-23 05:14:49 -07:00
|
|
|
pub mod metadata;
|
|
|
|
|
2021-07-19 18:56:47 -07:00
|
|
|
pub mod cost;
|
|
|
|
pub use cost::CircuitCost;
|
|
|
|
|
2021-07-25 18:53:13 -07:00
|
|
|
mod gates;
|
|
|
|
pub use gates::CircuitGates;
|
|
|
|
|
2021-01-22 13:13:11 -08:00
|
|
|
#[cfg(feature = "dev-graph")]
|
|
|
|
mod graph;
|
|
|
|
|
|
|
|
#[cfg(feature = "dev-graph")]
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "dev-graph")))]
|
2021-06-08 04:06:48 -07:00
|
|
|
pub use graph::{circuit_dot_graph, layout::CircuitLayout};
|
2021-01-22 13:13:11 -08:00
|
|
|
|
2020-12-21 15:00:39 -08:00
|
|
|
/// The reasons why a particular circuit is not satisfied.
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum VerifyFailure {
|
2021-05-27 06:02:13 -07:00
|
|
|
/// A cell used in an active gate was not assigned to.
|
|
|
|
Cell {
|
2021-06-23 05:14:49 -07:00
|
|
|
/// The index of the active gate.
|
|
|
|
gate: metadata::Gate,
|
|
|
|
/// The region in which this cell should be assigned.
|
|
|
|
region: metadata::Region,
|
2021-06-18 09:25:02 -07:00
|
|
|
/// The column in which this cell should be assigned.
|
2021-05-27 06:02:13 -07:00
|
|
|
column: Column<Any>,
|
2021-06-18 09:25:02 -07:00
|
|
|
/// The offset (relative to the start of the region) at which this cell should be
|
|
|
|
/// assigned. This may be negative (for example, if a selector enables a gate at
|
|
|
|
/// offset 0, but the gate uses `Rotation::prev()`).
|
|
|
|
offset: isize,
|
2021-05-27 06:02:13 -07:00
|
|
|
},
|
2021-06-03 19:01:09 -07:00
|
|
|
/// A constraint was not satisfied for a particular row.
|
2021-07-15 14:12:50 -07:00
|
|
|
ConstraintNotSatisfied {
|
2021-06-23 05:14:49 -07:00
|
|
|
/// The polynomial constraint that is not satisfied.
|
|
|
|
constraint: metadata::Constraint,
|
2021-06-03 19:01:09 -07:00
|
|
|
/// The row on which this constraint is not satisfied.
|
2020-12-23 11:32:39 -08:00
|
|
|
row: usize,
|
|
|
|
},
|
2021-07-15 14:12:50 -07:00
|
|
|
/// A constraint was active on an unusable row, and is likely missing a selector.
|
|
|
|
ConstraintPoisoned {
|
|
|
|
/// The polynomial constraint that is not satisfied.
|
|
|
|
constraint: metadata::Constraint,
|
|
|
|
},
|
2020-12-21 15:00:39 -08:00
|
|
|
/// A lookup input did not exist in its corresponding table.
|
2020-12-23 11:32:39 -08:00
|
|
|
Lookup {
|
|
|
|
/// The index of the lookup that is not satisfied. These indices are assigned in
|
|
|
|
/// the order in which `ConstraintSystem::lookup` is called during
|
|
|
|
/// `Circuit::configure`.
|
|
|
|
lookup_index: usize,
|
|
|
|
/// The row on which this lookup is not satisfied.
|
|
|
|
row: usize,
|
|
|
|
},
|
2020-12-21 21:56:30 -08:00
|
|
|
/// A permutation did not preserve the original value of a cell.
|
|
|
|
Permutation {
|
2020-12-23 11:32:39 -08:00
|
|
|
/// The column in which this permutation is not satisfied.
|
2021-07-19 23:32:15 -07:00
|
|
|
column: metadata::Column,
|
2020-12-23 11:32:39 -08:00
|
|
|
/// The row on which this permutation is not satisfied.
|
2020-12-21 21:56:30 -08:00
|
|
|
row: usize,
|
|
|
|
},
|
2020-12-21 15:00:39 -08:00
|
|
|
}
|
|
|
|
|
2021-06-03 19:18:11 -07:00
|
|
|
impl fmt::Display for VerifyFailure {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Self::Cell {
|
2021-06-23 05:14:49 -07:00
|
|
|
gate,
|
|
|
|
region,
|
2021-06-03 19:18:11 -07:00
|
|
|
column,
|
2021-06-18 09:25:02 -07:00
|
|
|
offset,
|
2021-06-03 19:18:11 -07:00
|
|
|
} => {
|
|
|
|
write!(
|
|
|
|
f,
|
2021-06-23 05:14:49 -07:00
|
|
|
"{} uses {}, which requires cell in column {:?} at offset {} to be assigned.",
|
|
|
|
region, gate, column, offset
|
2021-06-03 19:18:11 -07:00
|
|
|
)
|
|
|
|
}
|
2021-07-15 14:12:50 -07:00
|
|
|
Self::ConstraintNotSatisfied { constraint, row } => {
|
2021-06-23 05:14:49 -07:00
|
|
|
write!(f, "{} is not satisfied on row {}", constraint, row)
|
2021-06-03 19:18:11 -07:00
|
|
|
}
|
2021-07-15 14:12:50 -07:00
|
|
|
Self::ConstraintPoisoned { constraint } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{} is active on an unusable row - missing selector?",
|
|
|
|
constraint
|
|
|
|
)
|
|
|
|
}
|
2021-06-03 19:18:11 -07:00
|
|
|
Self::Lookup { lookup_index, row } => {
|
|
|
|
write!(f, "Lookup {} is not satisfied on row {}", lookup_index, row)
|
|
|
|
}
|
2021-07-02 15:20:36 -07:00
|
|
|
Self::Permutation { column, row } => {
|
2021-06-03 19:18:11 -07:00
|
|
|
write!(
|
|
|
|
f,
|
2021-07-02 15:20:36 -07:00
|
|
|
"Equality constraint not satisfied by cell ({:?}, {})",
|
|
|
|
column, row
|
2021-06-03 19:18:11 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-18 08:48:25 -07:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct Region {
|
|
|
|
/// The name of the region. Not required to be unique.
|
|
|
|
name: String,
|
2021-06-18 09:25:02 -07:00
|
|
|
/// The row that this region starts on, if known.
|
|
|
|
start: Option<usize>,
|
2021-06-18 08:48:25 -07:00
|
|
|
/// The selectors that have been enabled in this region. All other selectors are by
|
|
|
|
/// construction not enabled.
|
|
|
|
enabled_selectors: HashMap<Selector, Vec<usize>>,
|
|
|
|
/// The cells assigned in this region. We store this as a `Vec` so that if any cells
|
|
|
|
/// are double-assigned, they will be visibly darker.
|
|
|
|
cells: Vec<(Column<Any>, usize)>,
|
|
|
|
}
|
|
|
|
|
2021-06-18 09:25:02 -07:00
|
|
|
impl Region {
|
|
|
|
fn update_start(&mut self, row: usize) {
|
|
|
|
// The region start is the earliest row assigned to.
|
|
|
|
let mut start = self.start.unwrap_or(row);
|
|
|
|
if row < start {
|
|
|
|
// The first row assigned was not at start 0 within the region.
|
|
|
|
start = row;
|
|
|
|
}
|
|
|
|
self.start = Some(start);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-15 14:12:50 -07:00
|
|
|
/// The value of a particular cell within the circuit.
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
enum CellValue<F: Group + Field> {
|
|
|
|
// An unassigned cell.
|
|
|
|
Unassigned,
|
|
|
|
// A cell that has been assigned a value.
|
|
|
|
Assigned(F),
|
|
|
|
// A unique poisoned cell.
|
|
|
|
Poison(usize),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A value within an expression.
|
2021-10-22 10:33:30 -07:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
|
2021-07-15 14:12:50 -07:00
|
|
|
enum Value<F: Group + Field> {
|
|
|
|
Real(F),
|
|
|
|
Poison,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<F: Group + Field> From<CellValue<F>> for Value<F> {
|
|
|
|
fn from(value: CellValue<F>) -> Self {
|
|
|
|
match value {
|
|
|
|
// Cells that haven't been explicitly assigned to, default to zero.
|
|
|
|
CellValue::Unassigned => Value::Real(F::zero()),
|
|
|
|
CellValue::Assigned(v) => Value::Real(v),
|
|
|
|
CellValue::Poison(_) => Value::Poison,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-25 13:24:49 -07:00
|
|
|
impl<F: Group + Field> Neg for Value<F> {
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn neg(self) -> Self::Output {
|
|
|
|
match self {
|
|
|
|
Value::Real(a) => Value::Real(-a),
|
|
|
|
_ => Value::Poison,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-15 14:12:50 -07:00
|
|
|
impl<F: Group + Field> Add for Value<F> {
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn add(self, rhs: Self) -> Self::Output {
|
|
|
|
match (self, rhs) {
|
|
|
|
(Value::Real(a), Value::Real(b)) => Value::Real(a + b),
|
|
|
|
_ => Value::Poison,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<F: Group + Field> Mul for Value<F> {
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn mul(self, rhs: Self) -> Self::Output {
|
|
|
|
match (self, rhs) {
|
|
|
|
(Value::Real(a), Value::Real(b)) => Value::Real(a * b),
|
|
|
|
// If poison is multiplied by zero, then we treat the poison as unconstrained
|
|
|
|
// and we don't propagate it.
|
2021-09-02 14:51:25 -07:00
|
|
|
(Value::Real(x), Value::Poison) | (Value::Poison, Value::Real(x))
|
|
|
|
if x.is_zero_vartime() =>
|
|
|
|
{
|
2021-07-15 14:12:50 -07:00
|
|
|
Value::Real(F::zero())
|
|
|
|
}
|
|
|
|
_ => Value::Poison,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<F: Group + Field> Mul<F> for Value<F> {
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn mul(self, rhs: F) -> Self::Output {
|
|
|
|
match self {
|
|
|
|
Value::Real(lhs) => Value::Real(lhs * rhs),
|
|
|
|
// If poison is multiplied by zero, then we treat the poison as unconstrained
|
|
|
|
// and we don't propagate it.
|
2021-09-02 14:51:25 -07:00
|
|
|
Value::Poison if rhs.is_zero_vartime() => Value::Real(F::zero()),
|
2021-07-15 14:12:50 -07:00
|
|
|
_ => Value::Poison,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-23 11:32:39 -08:00
|
|
|
/// A test prover for debugging circuits.
|
|
|
|
///
|
|
|
|
/// The normal proving process, when applied to a buggy circuit implementation, might
|
|
|
|
/// return proofs that do not validate when they should, but it can't indicate anything
|
|
|
|
/// other than "something is invalid". `MockProver` can be used to figure out _why_ these
|
|
|
|
/// are invalid: it stores all the private inputs along with the circuit internals, and
|
|
|
|
/// then checks every constraint manually.
|
2020-12-23 11:33:31 -08:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use halo2::{
|
|
|
|
/// arithmetic::FieldExt,
|
2021-06-21 11:10:59 -07:00
|
|
|
/// circuit::{Layouter, SimpleFloorPlanner},
|
2020-12-23 11:33:31 -08:00
|
|
|
/// dev::{MockProver, VerifyFailure},
|
|
|
|
/// pasta::Fp,
|
2021-07-15 14:12:50 -07:00
|
|
|
/// plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Selector},
|
2021-01-14 12:31:48 -08:00
|
|
|
/// poly::Rotation,
|
2020-12-23 11:33:31 -08:00
|
|
|
/// };
|
|
|
|
/// const K: u32 = 5;
|
|
|
|
///
|
2021-01-20 05:48:51 -08:00
|
|
|
/// #[derive(Copy, Clone)]
|
2020-12-23 11:33:31 -08:00
|
|
|
/// struct MyConfig {
|
|
|
|
/// a: Column<Advice>,
|
|
|
|
/// b: Column<Advice>,
|
|
|
|
/// c: Column<Advice>,
|
2021-07-15 14:12:50 -07:00
|
|
|
/// s: Selector,
|
2020-12-23 11:33:31 -08:00
|
|
|
/// }
|
|
|
|
///
|
2021-06-21 11:10:59 -07:00
|
|
|
/// #[derive(Clone, Default)]
|
2020-12-23 11:33:31 -08:00
|
|
|
/// struct MyCircuit {
|
|
|
|
/// a: Option<u64>,
|
|
|
|
/// b: Option<u64>,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl<F: FieldExt> Circuit<F> for MyCircuit {
|
|
|
|
/// type Config = MyConfig;
|
2021-06-21 11:10:59 -07:00
|
|
|
/// type FloorPlanner = SimpleFloorPlanner;
|
|
|
|
///
|
|
|
|
/// fn without_witnesses(&self) -> Self {
|
|
|
|
/// Self::default()
|
|
|
|
/// }
|
2020-12-23 11:33:31 -08:00
|
|
|
///
|
|
|
|
/// fn configure(meta: &mut ConstraintSystem<F>) -> MyConfig {
|
|
|
|
/// let a = meta.advice_column();
|
|
|
|
/// let b = meta.advice_column();
|
|
|
|
/// let c = meta.advice_column();
|
2021-07-15 14:12:50 -07:00
|
|
|
/// let s = meta.selector();
|
2020-12-23 11:33:31 -08:00
|
|
|
///
|
2021-01-22 11:46:06 -08:00
|
|
|
/// meta.create_gate("R1CS constraint", |meta| {
|
2021-01-14 12:31:48 -08:00
|
|
|
/// let a = meta.query_advice(a, Rotation::cur());
|
|
|
|
/// let b = meta.query_advice(b, Rotation::cur());
|
|
|
|
/// let c = meta.query_advice(c, Rotation::cur());
|
2021-07-15 14:12:50 -07:00
|
|
|
/// let s = meta.query_selector(s);
|
2020-12-23 11:33:31 -08:00
|
|
|
///
|
|
|
|
/// // BUG: Should be a * b - c
|
2021-07-15 14:12:50 -07:00
|
|
|
/// Some(("buggy R1CS", s * (a * b + c)))
|
2020-12-23 11:33:31 -08:00
|
|
|
/// });
|
|
|
|
///
|
2021-07-15 14:12:50 -07:00
|
|
|
/// MyConfig { a, b, c, s }
|
2020-12-23 11:33:31 -08:00
|
|
|
/// }
|
|
|
|
///
|
2021-06-21 11:10:59 -07:00
|
|
|
/// fn synthesize(&self, config: MyConfig, mut layouter: impl Layouter<F>) -> Result<(), Error> {
|
|
|
|
/// layouter.assign_region(|| "Example region", |mut region| {
|
2021-07-15 14:12:50 -07:00
|
|
|
/// config.s.enable(&mut region, 0)?;
|
2021-06-21 11:10:59 -07:00
|
|
|
/// region.assign_advice(|| "a", config.a, 0, || {
|
2021-09-30 14:51:58 -07:00
|
|
|
/// self.a.map(|v| F::from(v)).ok_or(Error::SynthesisError)
|
2021-06-21 11:10:59 -07:00
|
|
|
/// })?;
|
|
|
|
/// region.assign_advice(|| "b", config.b, 0, || {
|
2021-09-30 14:51:58 -07:00
|
|
|
/// self.b.map(|v| F::from(v)).ok_or(Error::SynthesisError)
|
2021-06-21 11:10:59 -07:00
|
|
|
/// })?;
|
|
|
|
/// region.assign_advice(|| "c", config.c, 0, || {
|
|
|
|
/// self.a
|
2021-09-30 14:51:58 -07:00
|
|
|
/// .and_then(|a| self.b.map(|b| F::from(a * b)))
|
2021-06-21 11:10:59 -07:00
|
|
|
/// .ok_or(Error::SynthesisError)
|
|
|
|
/// })?;
|
|
|
|
/// Ok(())
|
2020-12-23 11:33:31 -08:00
|
|
|
/// })
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// // Assemble the private inputs to the circuit.
|
|
|
|
/// let circuit = MyCircuit {
|
|
|
|
/// a: Some(2),
|
|
|
|
/// b: Some(4),
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// // This circuit has no public inputs.
|
2021-02-14 09:30:36 -08:00
|
|
|
/// let instance = vec![];
|
2020-12-23 11:33:31 -08:00
|
|
|
///
|
2021-02-14 09:30:36 -08:00
|
|
|
/// let prover = MockProver::<Fp>::run(K, &circuit, instance).unwrap();
|
2020-12-23 11:33:31 -08:00
|
|
|
/// assert_eq!(
|
|
|
|
/// prover.verify(),
|
2021-07-15 14:12:50 -07:00
|
|
|
/// Err(vec![VerifyFailure::ConstraintNotSatisfied {
|
2021-06-23 05:14:49 -07:00
|
|
|
/// constraint: ((0, "R1CS constraint").into(), 0, "buggy R1CS").into(),
|
2020-12-23 11:33:31 -08:00
|
|
|
/// row: 0
|
2021-05-18 08:32:15 -07:00
|
|
|
/// }])
|
2020-12-23 11:33:31 -08:00
|
|
|
/// );
|
|
|
|
/// ```
|
2020-12-23 11:32:39 -08:00
|
|
|
#[derive(Debug)]
|
2021-02-10 03:36:25 -08:00
|
|
|
pub struct MockProver<F: Group + Field> {
|
2020-12-21 15:00:39 -08:00
|
|
|
n: u32,
|
|
|
|
cs: ConstraintSystem<F>,
|
|
|
|
|
2021-06-18 08:48:25 -07:00
|
|
|
/// The regions in the circuit.
|
|
|
|
regions: Vec<Region>,
|
|
|
|
/// The current region being assigned to. Will be `None` after the circuit has been
|
|
|
|
/// synthesized.
|
|
|
|
current_region: Option<Region>,
|
2021-05-27 06:03:12 -07:00
|
|
|
|
2020-12-21 15:00:39 -08:00
|
|
|
// The fixed cells in the circuit, arranged as [column][row].
|
2021-07-15 14:12:50 -07:00
|
|
|
fixed: Vec<Vec<CellValue<F>>>,
|
2020-12-21 15:00:39 -08:00
|
|
|
// The advice cells in the circuit, arranged as [column][row].
|
2021-07-15 14:12:50 -07:00
|
|
|
advice: Vec<Vec<CellValue<F>>>,
|
2021-02-14 09:30:36 -08:00
|
|
|
// The instance cells in the circuit, arranged as [column][row].
|
|
|
|
instance: Vec<Vec<F>>,
|
2020-12-21 15:00:39 -08:00
|
|
|
|
2021-07-21 11:55:19 -07:00
|
|
|
selectors: Vec<Vec<bool>>,
|
|
|
|
|
2021-07-02 15:20:36 -07:00
|
|
|
permutation: permutation::keygen::Assembly,
|
|
|
|
|
2021-07-09 11:38:38 -07:00
|
|
|
// A range of available rows for assignment and copies.
|
2021-07-15 14:12:50 -07:00
|
|
|
usable_rows: Range<usize>,
|
2020-12-21 15:00:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<F: Field + Group> Assignment<F> for MockProver<F> {
|
2021-06-18 08:48:25 -07:00
|
|
|
fn enter_region<NR, N>(&mut self, name: N)
|
2021-01-22 08:43:36 -08:00
|
|
|
where
|
|
|
|
NR: Into<String>,
|
|
|
|
N: FnOnce() -> NR,
|
|
|
|
{
|
2021-06-18 08:48:25 -07:00
|
|
|
assert!(self.current_region.is_none());
|
|
|
|
self.current_region = Some(Region {
|
|
|
|
name: name().into(),
|
2021-06-18 09:25:02 -07:00
|
|
|
start: None,
|
2021-06-18 08:48:25 -07:00
|
|
|
enabled_selectors: HashMap::default(),
|
|
|
|
cells: vec![],
|
|
|
|
});
|
2021-01-22 08:43:36 -08:00
|
|
|
}
|
|
|
|
|
2021-06-18 08:48:25 -07:00
|
|
|
fn exit_region(&mut self) {
|
|
|
|
self.regions.push(self.current_region.take().unwrap());
|
|
|
|
}
|
2021-01-22 08:43:36 -08:00
|
|
|
|
2021-07-21 11:55:19 -07:00
|
|
|
fn enable_selector<A, AR>(&mut self, _: A, selector: &Selector, row: usize) -> Result<(), Error>
|
2021-05-27 04:43:32 -07:00
|
|
|
where
|
|
|
|
A: FnOnce() -> AR,
|
|
|
|
AR: Into<String>,
|
|
|
|
{
|
2021-07-09 11:38:38 -07:00
|
|
|
if !self.usable_rows.contains(&row) {
|
2021-07-02 15:20:36 -07:00
|
|
|
return Err(Error::BoundsFailure);
|
|
|
|
}
|
|
|
|
|
2021-06-18 08:48:25 -07:00
|
|
|
// Track that this selector was enabled. We require that all selectors are enabled
|
|
|
|
// inside some region (i.e. no floating selectors).
|
|
|
|
self.current_region
|
|
|
|
.as_mut()
|
|
|
|
.unwrap()
|
|
|
|
.enabled_selectors
|
2021-05-27 06:03:12 -07:00
|
|
|
.entry(*selector)
|
|
|
|
.or_default()
|
|
|
|
.push(row);
|
|
|
|
|
2021-07-21 11:55:19 -07:00
|
|
|
self.selectors[selector.0][row] = true;
|
|
|
|
|
|
|
|
Ok(())
|
2021-05-27 04:43:32 -07:00
|
|
|
}
|
|
|
|
|
2021-07-08 13:44:01 -07:00
|
|
|
fn query_instance(&self, column: Column<Instance>, row: usize) -> Result<Option<F>, Error> {
|
2021-07-10 07:25:00 -07:00
|
|
|
if !self.usable_rows.contains(&row) {
|
|
|
|
return Err(Error::BoundsFailure);
|
|
|
|
}
|
|
|
|
|
2021-07-08 13:44:01 -07:00
|
|
|
self.instance
|
|
|
|
.get(column.index())
|
|
|
|
.and_then(|column| column.get(row))
|
|
|
|
.map(|v| Some(*v))
|
|
|
|
.ok_or(Error::BoundsFailure)
|
|
|
|
}
|
|
|
|
|
2021-06-11 08:39:34 -07:00
|
|
|
fn assign_advice<V, VR, A, AR>(
|
2020-12-21 15:00:39 -08:00
|
|
|
&mut self,
|
2021-01-22 08:57:38 -08:00
|
|
|
_: A,
|
|
|
|
column: Column<Advice>,
|
2020-12-21 15:00:39 -08:00
|
|
|
row: usize,
|
2021-01-22 08:57:38 -08:00
|
|
|
to: V,
|
|
|
|
) -> Result<(), Error>
|
|
|
|
where
|
2021-06-11 08:39:34 -07:00
|
|
|
V: FnOnce() -> Result<VR, Error>,
|
|
|
|
VR: Into<Assigned<F>>,
|
2021-01-22 08:57:38 -08:00
|
|
|
A: FnOnce() -> AR,
|
|
|
|
AR: Into<String>,
|
|
|
|
{
|
2021-07-09 11:38:38 -07:00
|
|
|
if !self.usable_rows.contains(&row) {
|
2021-07-02 15:20:36 -07:00
|
|
|
return Err(Error::BoundsFailure);
|
|
|
|
}
|
|
|
|
|
2021-06-18 08:48:25 -07:00
|
|
|
if let Some(region) = self.current_region.as_mut() {
|
2021-06-18 09:25:02 -07:00
|
|
|
region.update_start(row);
|
2021-06-18 08:48:25 -07:00
|
|
|
region.cells.push((column.into(), row));
|
|
|
|
}
|
|
|
|
|
2020-12-21 15:00:39 -08:00
|
|
|
*self
|
|
|
|
.advice
|
|
|
|
.get_mut(column.index())
|
|
|
|
.and_then(|v| v.get_mut(row))
|
2021-07-15 14:12:50 -07:00
|
|
|
.ok_or(Error::BoundsFailure)? = CellValue::Assigned(to()?.into().evaluate());
|
2020-12-21 15:00:39 -08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-06-11 08:39:34 -07:00
|
|
|
fn assign_fixed<V, VR, A, AR>(
|
2020-12-21 15:00:39 -08:00
|
|
|
&mut self,
|
2021-01-22 08:57:38 -08:00
|
|
|
_: A,
|
|
|
|
column: Column<Fixed>,
|
2020-12-21 15:00:39 -08:00
|
|
|
row: usize,
|
2021-01-22 08:57:38 -08:00
|
|
|
to: V,
|
|
|
|
) -> Result<(), Error>
|
|
|
|
where
|
2021-06-11 08:39:34 -07:00
|
|
|
V: FnOnce() -> Result<VR, Error>,
|
|
|
|
VR: Into<Assigned<F>>,
|
2021-01-22 08:57:38 -08:00
|
|
|
A: FnOnce() -> AR,
|
|
|
|
AR: Into<String>,
|
|
|
|
{
|
2021-07-09 11:38:38 -07:00
|
|
|
if !self.usable_rows.contains(&row) {
|
2021-07-02 15:20:36 -07:00
|
|
|
return Err(Error::BoundsFailure);
|
|
|
|
}
|
|
|
|
|
2021-06-18 08:48:25 -07:00
|
|
|
if let Some(region) = self.current_region.as_mut() {
|
2021-06-18 09:25:02 -07:00
|
|
|
region.update_start(row);
|
2021-06-18 08:48:25 -07:00
|
|
|
region.cells.push((column.into(), row));
|
|
|
|
}
|
|
|
|
|
2020-12-21 15:00:39 -08:00
|
|
|
*self
|
|
|
|
.fixed
|
|
|
|
.get_mut(column.index())
|
|
|
|
.and_then(|v| v.get_mut(row))
|
2021-07-15 14:12:50 -07:00
|
|
|
.ok_or(Error::BoundsFailure)? = CellValue::Assigned(to()?.into().evaluate());
|
2020-12-21 15:00:39 -08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn copy(
|
|
|
|
&mut self,
|
2021-02-18 23:07:08 -08:00
|
|
|
left_column: Column<Any>,
|
2020-12-21 15:00:39 -08:00
|
|
|
left_row: usize,
|
2021-02-18 23:07:08 -08:00
|
|
|
right_column: Column<Any>,
|
2020-12-21 15:00:39 -08:00
|
|
|
right_row: usize,
|
|
|
|
) -> Result<(), crate::plonk::Error> {
|
2021-07-09 11:38:38 -07:00
|
|
|
if !self.usable_rows.contains(&left_row) || !self.usable_rows.contains(&right_row) {
|
2020-12-21 21:56:30 -08:00
|
|
|
return Err(Error::BoundsFailure);
|
|
|
|
}
|
|
|
|
|
2021-07-02 15:20:36 -07:00
|
|
|
self.permutation
|
|
|
|
.copy(left_column, left_row, right_column, right_row)
|
2020-12-21 15:00:39 -08:00
|
|
|
}
|
2021-01-22 10:36:42 -08:00
|
|
|
|
2021-07-26 17:34:51 -07:00
|
|
|
fn fill_from_row(
|
|
|
|
&mut self,
|
|
|
|
_: Column<Fixed>,
|
|
|
|
from_row: usize,
|
|
|
|
_: Option<Assigned<F>>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
if !self.usable_rows.contains(&from_row) {
|
|
|
|
return Err(Error::BoundsFailure);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-01-22 10:36:42 -08:00
|
|
|
fn push_namespace<NR, N>(&mut self, _: N)
|
|
|
|
where
|
|
|
|
NR: Into<String>,
|
|
|
|
N: FnOnce() -> NR,
|
|
|
|
{
|
|
|
|
// TODO: Do something with namespaces :)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pop_namespace(&mut self, _: Option<String>) {
|
|
|
|
// TODO: Do something with namespaces :)
|
|
|
|
}
|
2020-12-21 15:00:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<F: FieldExt> MockProver<F> {
|
2020-12-23 11:32:39 -08:00
|
|
|
/// Runs a synthetic keygen-and-prove operation on the given circuit, collecting data
|
|
|
|
/// about the constraints and their assignments.
|
2020-12-21 15:00:39 -08:00
|
|
|
pub fn run<ConcreteCircuit: Circuit<F>>(
|
|
|
|
k: u32,
|
|
|
|
circuit: &ConcreteCircuit,
|
2021-02-14 09:30:36 -08:00
|
|
|
instance: Vec<Vec<F>>,
|
2020-12-21 15:00:39 -08:00
|
|
|
) -> Result<Self, Error> {
|
2020-12-21 21:56:30 -08:00
|
|
|
let n = 1 << k;
|
|
|
|
|
2020-12-21 15:00:39 -08:00
|
|
|
let mut cs = ConstraintSystem::default();
|
|
|
|
let config = ConcreteCircuit::configure(&mut cs);
|
2021-07-02 15:20:36 -07:00
|
|
|
let cs = cs;
|
2020-12-21 15:00:39 -08:00
|
|
|
|
2021-07-09 08:14:52 -07:00
|
|
|
if n < cs.minimum_rows() {
|
|
|
|
return Err(Error::NotEnoughRowsAvailable);
|
2021-07-02 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
2021-07-13 11:08:16 -07:00
|
|
|
if instance.len() != cs.num_instance_columns {
|
2021-05-18 11:29:10 -07:00
|
|
|
return Err(Error::InvalidInstances);
|
2021-07-13 11:08:16 -07:00
|
|
|
}
|
2020-12-21 15:00:39 -08:00
|
|
|
|
2021-07-13 11:08:16 -07:00
|
|
|
let instance = instance
|
|
|
|
.into_iter()
|
|
|
|
.map(|mut instance| {
|
|
|
|
if instance.len() > n - (cs.blinding_factors() + 1) {
|
|
|
|
return Err(Error::InstanceTooLarge);
|
|
|
|
}
|
|
|
|
|
|
|
|
instance.resize(n, F::zero());
|
|
|
|
Ok(instance)
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
|
2021-07-15 14:12:50 -07:00
|
|
|
// Fixed columns contain no blinding factors.
|
|
|
|
let fixed = vec![vec![CellValue::Unassigned; n]; cs.num_fixed_columns];
|
2021-07-21 11:55:19 -07:00
|
|
|
let selectors = vec![vec![false; n]; cs.num_selectors];
|
2021-07-15 14:12:50 -07:00
|
|
|
// Advice columns contain blinding factors.
|
2021-07-02 15:20:36 -07:00
|
|
|
let blinding_factors = cs.blinding_factors();
|
2021-07-15 14:12:50 -07:00
|
|
|
let usable_rows = n - (blinding_factors + 1);
|
|
|
|
let advice = vec![
|
|
|
|
{
|
|
|
|
let mut column = vec![CellValue::Unassigned; n];
|
|
|
|
// Poison unusable rows.
|
|
|
|
for (i, cell) in column.iter_mut().enumerate().skip(usable_rows) {
|
|
|
|
*cell = CellValue::Poison(i);
|
|
|
|
}
|
|
|
|
column
|
|
|
|
};
|
|
|
|
cs.num_advice_columns
|
|
|
|
];
|
2021-07-02 15:20:36 -07:00
|
|
|
let permutation = permutation::keygen::Assembly::new(n, &cs.permutation);
|
2021-07-20 08:01:38 -07:00
|
|
|
let constants = cs.constants.clone();
|
2020-12-21 15:00:39 -08:00
|
|
|
|
|
|
|
let mut prover = MockProver {
|
2021-07-02 15:20:36 -07:00
|
|
|
n: n as u32,
|
2020-12-21 15:00:39 -08:00
|
|
|
cs,
|
2021-06-18 08:48:25 -07:00
|
|
|
regions: vec![],
|
|
|
|
current_region: None,
|
2020-12-21 15:00:39 -08:00
|
|
|
fixed,
|
|
|
|
advice,
|
2021-02-14 09:30:36 -08:00
|
|
|
instance,
|
2021-07-21 11:55:19 -07:00
|
|
|
selectors,
|
2021-07-02 15:20:36 -07:00
|
|
|
permutation,
|
2021-07-15 14:12:50 -07:00
|
|
|
usable_rows: 0..usable_rows,
|
2020-12-21 15:00:39 -08:00
|
|
|
};
|
|
|
|
|
2021-07-20 08:01:38 -07:00
|
|
|
ConcreteCircuit::FloorPlanner::synthesize(&mut prover, circuit, config, constants)?;
|
2020-12-21 15:00:39 -08:00
|
|
|
|
2021-07-21 11:55:19 -07:00
|
|
|
let (cs, selector_polys) = prover.cs.compress_selectors(prover.selectors.clone());
|
|
|
|
prover.cs = cs;
|
|
|
|
prover.fixed.extend(selector_polys.into_iter().map(|poly| {
|
|
|
|
let mut v = vec![CellValue::Unassigned; n];
|
|
|
|
for (v, p) in v.iter_mut().zip(&poly[..]) {
|
|
|
|
*v = CellValue::Assigned(*p);
|
|
|
|
}
|
|
|
|
v
|
|
|
|
}));
|
|
|
|
|
2020-12-21 15:00:39 -08:00
|
|
|
Ok(prover)
|
|
|
|
}
|
|
|
|
|
2021-05-18 08:32:15 -07:00
|
|
|
/// Returns `Ok(())` if this `MockProver` is satisfied, or a list of errors indicating
|
|
|
|
/// the reasons that the circuit is not satisfied.
|
|
|
|
pub fn verify(&self) -> Result<(), Vec<VerifyFailure>> {
|
2020-12-21 15:00:39 -08:00
|
|
|
let n = self.n as i32;
|
|
|
|
|
2021-06-18 08:48:25 -07:00
|
|
|
// Check that within each region, all cells used in instantiated gates have been
|
|
|
|
// assigned to.
|
|
|
|
let selector_errors = self.regions.iter().enumerate().flat_map(|(r_i, r)| {
|
|
|
|
r.enabled_selectors.iter().flat_map(move |(selector, at)| {
|
|
|
|
// Find the gates enabled by this selector
|
|
|
|
self.cs
|
|
|
|
.gates
|
2021-05-27 06:03:12 -07:00
|
|
|
.iter()
|
|
|
|
// Assume that if a queried selector is enabled, the user wants to use the
|
|
|
|
// corresponding gate in some way.
|
|
|
|
//
|
|
|
|
// TODO: This will trip up on the reverse case, where leaving a selector
|
|
|
|
// un-enabled keeps a gate enabled. We could alternatively require that
|
|
|
|
// every selector is explicitly enabled or disabled on every row? But that
|
|
|
|
// seems messy and confusing.
|
2021-06-18 08:48:25 -07:00
|
|
|
.enumerate()
|
2021-06-21 10:19:15 -07:00
|
|
|
.filter(move |(_, g)| g.queried_selectors().contains(selector))
|
2021-06-18 08:48:25 -07:00
|
|
|
.flat_map(move |(gate_index, gate)| {
|
2021-05-27 06:03:12 -07:00
|
|
|
at.iter().flat_map(move |selector_row| {
|
2021-06-04 08:18:51 -07:00
|
|
|
// Selectors are queried with no rotation.
|
|
|
|
let gate_row = *selector_row as i32;
|
2021-05-27 06:03:12 -07:00
|
|
|
|
|
|
|
gate.queried_cells().iter().filter_map(move |cell| {
|
|
|
|
// Determine where this cell should have been assigned.
|
|
|
|
let cell_row = ((gate_row + n + cell.rotation.0) % n) as usize;
|
|
|
|
|
|
|
|
// Check that it was assigned!
|
2021-06-18 08:48:25 -07:00
|
|
|
if r.cells.contains(&(cell.column, cell_row)) {
|
2021-05-27 06:03:12 -07:00
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(VerifyFailure::Cell {
|
2021-06-23 05:14:49 -07:00
|
|
|
gate: (gate_index, gate.name()).into(),
|
|
|
|
region: (r_i, r.name.clone()).into(),
|
2021-05-27 06:03:12 -07:00
|
|
|
column: cell.column,
|
2021-06-18 09:25:02 -07:00
|
|
|
offset: cell_row as isize - r.start.unwrap() as isize,
|
2021-05-27 06:03:12 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2021-06-18 08:48:25 -07:00
|
|
|
})
|
|
|
|
});
|
2021-05-27 06:03:12 -07:00
|
|
|
|
2020-12-21 15:00:39 -08:00
|
|
|
// Check that all gates are satisfied for all rows.
|
2021-06-03 19:01:09 -07:00
|
|
|
let gate_errors =
|
|
|
|
self.cs
|
|
|
|
.gates
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.flat_map(|(gate_index, gate)| {
|
|
|
|
// We iterate from n..2n so we can just reduce to handle wrapping.
|
|
|
|
(n..(2 * n)).flat_map(move |row| {
|
2021-07-15 14:12:50 -07:00
|
|
|
fn load_instance<'a, F: FieldExt, T: ColumnType>(
|
2021-06-03 19:01:09 -07:00
|
|
|
n: i32,
|
|
|
|
row: i32,
|
|
|
|
queries: &'a [(Column<T>, Rotation)],
|
2021-07-15 14:12:50 -07:00
|
|
|
cells: &'a [Vec<F>],
|
2021-07-19 06:46:02 -07:00
|
|
|
) -> impl Fn(usize, usize, Rotation) -> Value<F> + 'a
|
|
|
|
{
|
2021-07-12 11:53:12 -07:00
|
|
|
move |index, _, _| {
|
2021-06-03 19:01:09 -07:00
|
|
|
let (column, at) = &queries[index];
|
|
|
|
let resolved_row = (row + at.0) % n;
|
2021-07-15 14:12:50 -07:00
|
|
|
Value::Real(cells[column.index()][resolved_row as usize])
|
2021-06-03 19:01:09 -07:00
|
|
|
}
|
2021-05-18 08:32:15 -07:00
|
|
|
}
|
2021-06-03 19:01:09 -07:00
|
|
|
|
|
|
|
fn load<'a, F: FieldExt, T: ColumnType>(
|
|
|
|
n: i32,
|
|
|
|
row: i32,
|
|
|
|
queries: &'a [(Column<T>, Rotation)],
|
2021-07-15 14:12:50 -07:00
|
|
|
cells: &'a [Vec<CellValue<F>>],
|
2021-07-19 06:46:02 -07:00
|
|
|
) -> impl Fn(usize, usize, Rotation) -> Value<F> + 'a
|
|
|
|
{
|
2021-07-12 11:53:12 -07:00
|
|
|
move |index, _, _| {
|
2021-06-03 19:01:09 -07:00
|
|
|
let (column, at) = &queries[index];
|
|
|
|
let resolved_row = (row + at.0) % n;
|
2021-07-15 14:12:50 -07:00
|
|
|
cells[column.index()][resolved_row as usize].into()
|
2021-06-03 19:01:09 -07:00
|
|
|
}
|
2021-05-27 06:44:02 -07:00
|
|
|
}
|
2021-06-03 19:01:09 -07:00
|
|
|
|
|
|
|
gate.polynomials().iter().enumerate().filter_map(
|
2021-07-15 14:12:50 -07:00
|
|
|
move |(poly_index, poly)| match poly.evaluate(
|
|
|
|
&|scalar| Value::Real(scalar),
|
2021-07-21 11:55:19 -07:00
|
|
|
&|_| panic!("virtual selectors are removed during optimization"),
|
2021-07-15 14:12:50 -07:00
|
|
|
&load(n, row, &self.cs.fixed_queries, &self.fixed),
|
|
|
|
&load(n, row, &self.cs.advice_queries, &self.advice),
|
|
|
|
&load_instance(n, row, &self.cs.instance_queries, &self.instance),
|
2021-07-25 13:24:49 -07:00
|
|
|
&|a| -a,
|
2021-07-15 14:12:50 -07:00
|
|
|
&|a, b| a + b,
|
|
|
|
&|a, b| a * b,
|
|
|
|
&|a, scalar| a * scalar,
|
|
|
|
) {
|
2021-09-02 14:51:25 -07:00
|
|
|
Value::Real(x) if x.is_zero_vartime() => None,
|
2021-07-15 14:12:50 -07:00
|
|
|
Value::Real(_) => Some(VerifyFailure::ConstraintNotSatisfied {
|
|
|
|
constraint: (
|
|
|
|
(gate_index, gate.name()).into(),
|
|
|
|
poly_index,
|
|
|
|
gate.constraint_name(poly_index),
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
row: (row - n) as usize,
|
|
|
|
}),
|
|
|
|
Value::Poison => Some(VerifyFailure::ConstraintPoisoned {
|
|
|
|
constraint: (
|
|
|
|
(gate_index, gate.name()).into(),
|
|
|
|
poly_index,
|
|
|
|
gate.constraint_name(poly_index),
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
}),
|
2021-06-03 19:01:09 -07:00
|
|
|
},
|
|
|
|
)
|
2021-05-27 06:44:02 -07:00
|
|
|
})
|
2021-06-03 19:01:09 -07:00
|
|
|
});
|
2020-12-21 15:00:39 -08:00
|
|
|
|
|
|
|
// Check that all lookups exist in their respective tables.
|
2021-05-18 08:32:15 -07:00
|
|
|
let lookup_errors =
|
|
|
|
self.cs
|
|
|
|
.lookups
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.flat_map(|(lookup_index, lookup)| {
|
2021-07-15 16:26:16 -07:00
|
|
|
let load = |expression: &Expression<F>, row| {
|
|
|
|
expression.evaluate(
|
|
|
|
&|scalar| Value::Real(scalar),
|
2021-07-21 11:55:19 -07:00
|
|
|
&|_| panic!("virtual selectors are removed during optimization"),
|
2021-07-19 06:43:37 -07:00
|
|
|
&|index, _, _| {
|
2021-07-15 16:26:16 -07:00
|
|
|
let query = self.cs.fixed_queries[index];
|
|
|
|
let column_index = query.0.index();
|
|
|
|
let rotation = query.1 .0;
|
|
|
|
self.fixed[column_index]
|
|
|
|
[(row as i32 + n + rotation) as usize % n as usize]
|
|
|
|
.into()
|
|
|
|
},
|
2021-07-19 06:43:37 -07:00
|
|
|
&|index, _, _| {
|
2021-07-15 16:26:16 -07:00
|
|
|
let query = self.cs.advice_queries[index];
|
|
|
|
let column_index = query.0.index();
|
|
|
|
let rotation = query.1 .0;
|
|
|
|
self.advice[column_index]
|
|
|
|
[(row as i32 + n + rotation) as usize % n as usize]
|
|
|
|
.into()
|
|
|
|
},
|
2021-07-19 06:43:37 -07:00
|
|
|
&|index, _, _| {
|
2021-07-15 16:26:16 -07:00
|
|
|
let query = self.cs.instance_queries[index];
|
|
|
|
let column_index = query.0.index();
|
|
|
|
let rotation = query.1 .0;
|
|
|
|
Value::Real(
|
2021-05-18 08:32:15 -07:00
|
|
|
self.instance[column_index]
|
2021-07-15 16:26:16 -07:00
|
|
|
[(row as i32 + n + rotation) as usize % n as usize],
|
|
|
|
)
|
|
|
|
},
|
2021-07-25 13:24:49 -07:00
|
|
|
&|a| -a,
|
2021-07-15 16:26:16 -07:00
|
|
|
&|a, b| a + b,
|
|
|
|
&|a, b| a * b,
|
|
|
|
&|a, scalar| a * scalar,
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
2021-07-15 14:12:50 -07:00
|
|
|
// In the real prover, the lookup expressions are never enforced on
|
|
|
|
// unusable rows, due to the (1 - (l_last(X) + l_blind(X))) term.
|
2021-10-22 10:33:30 -07:00
|
|
|
let table: std::collections::BTreeSet<Vec<_>> = self
|
2021-07-15 16:26:16 -07:00
|
|
|
.usable_rows
|
|
|
|
.clone()
|
|
|
|
.map(|table_row| {
|
|
|
|
lookup
|
|
|
|
.table_expressions
|
|
|
|
.iter()
|
|
|
|
.map(move |c| load(c, table_row))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
})
|
|
|
|
.collect();
|
2021-07-15 14:12:50 -07:00
|
|
|
self.usable_rows.clone().filter_map(move |input_row| {
|
2021-05-18 08:32:15 -07:00
|
|
|
let inputs: Vec<_> = lookup
|
|
|
|
.input_expressions
|
2021-02-11 18:24:55 -08:00
|
|
|
.iter()
|
2021-05-18 08:32:15 -07:00
|
|
|
.map(|c| load(c, input_row))
|
|
|
|
.collect();
|
2021-10-22 10:33:30 -07:00
|
|
|
let lookup_passes = table.contains(&inputs);
|
2021-05-18 08:32:15 -07:00
|
|
|
if lookup_passes {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(VerifyFailure::Lookup {
|
|
|
|
lookup_index,
|
|
|
|
row: input_row as usize,
|
|
|
|
})
|
|
|
|
}
|
2021-02-11 18:24:55 -08:00
|
|
|
})
|
2021-05-18 08:32:15 -07:00
|
|
|
});
|
2020-12-21 15:00:39 -08:00
|
|
|
|
2020-12-21 21:56:30 -08:00
|
|
|
// Check that permutations preserve the original values of the cells.
|
2021-07-02 15:20:36 -07:00
|
|
|
let perm_errors = {
|
2021-07-10 07:47:57 -07:00
|
|
|
// Original values of columns involved in the permutation.
|
2021-07-02 15:20:36 -07:00
|
|
|
let original = |column, row| {
|
|
|
|
self.cs
|
|
|
|
.permutation
|
|
|
|
.get_columns()
|
|
|
|
.get(column)
|
|
|
|
.map(|c: &Column<Any>| match c.column_type() {
|
2021-07-15 14:12:50 -07:00
|
|
|
Any::Advice => self.advice[c.index()][row],
|
|
|
|
Any::Fixed => self.fixed[c.index()][row],
|
|
|
|
Any::Instance => CellValue::Assigned(self.instance[c.index()][row]),
|
2021-07-02 15:20:36 -07:00
|
|
|
})
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
// Iterate over each column of the permutation
|
|
|
|
self.permutation
|
|
|
|
.mapping
|
2020-12-21 21:56:30 -08:00
|
|
|
.iter()
|
2021-05-18 08:32:15 -07:00
|
|
|
.enumerate()
|
2021-07-02 15:20:36 -07:00
|
|
|
.flat_map(move |(column, values)| {
|
|
|
|
// Iterate over each row of the column to check that the cell's
|
|
|
|
// value is preserved by the mapping.
|
|
|
|
values.iter().enumerate().filter_map(move |(row, cell)| {
|
|
|
|
let original_cell = original(column, row);
|
|
|
|
let permuted_cell = original(cell.0, cell.1);
|
|
|
|
if original_cell == permuted_cell {
|
|
|
|
None
|
|
|
|
} else {
|
2021-07-08 14:22:38 -07:00
|
|
|
Some(VerifyFailure::Permutation {
|
2021-07-19 23:32:15 -07:00
|
|
|
column: (*self.cs.permutation.get_columns().get(column).unwrap())
|
|
|
|
.into(),
|
2021-07-08 14:22:38 -07:00
|
|
|
row,
|
|
|
|
})
|
2021-07-02 15:20:36 -07:00
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
};
|
2021-05-18 08:32:15 -07:00
|
|
|
|
2021-07-15 14:12:50 -07:00
|
|
|
let mut errors: Vec<_> = iter::empty()
|
2021-05-27 06:03:12 -07:00
|
|
|
.chain(selector_errors)
|
|
|
|
.chain(gate_errors)
|
2021-05-18 08:32:15 -07:00
|
|
|
.chain(lookup_errors)
|
|
|
|
.chain(perm_errors)
|
|
|
|
.collect();
|
|
|
|
if errors.is_empty() {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
2021-07-15 14:12:50 -07:00
|
|
|
// Remove any duplicate `ConstraintPoisoned` errors (we check all unavailable
|
|
|
|
// rows in case the trigger is row-specific, but the error message only points
|
|
|
|
// at the constraint).
|
|
|
|
errors.dedup_by(|a, b| match (a, b) {
|
|
|
|
(
|
|
|
|
a @ VerifyFailure::ConstraintPoisoned { .. },
|
|
|
|
b @ VerifyFailure::ConstraintPoisoned { .. },
|
|
|
|
) => a == b,
|
|
|
|
_ => false,
|
|
|
|
});
|
2021-05-18 08:32:15 -07:00
|
|
|
Err(errors)
|
2020-12-21 21:56:30 -08:00
|
|
|
}
|
2020-12-21 15:00:39 -08:00
|
|
|
}
|
|
|
|
}
|
2021-05-27 06:02:13 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use pasta_curves::Fp;
|
|
|
|
|
|
|
|
use super::{MockProver, VerifyFailure};
|
|
|
|
use crate::{
|
2021-06-21 11:10:59 -07:00
|
|
|
circuit::{Layouter, SimpleFloorPlanner},
|
|
|
|
plonk::{Advice, Any, Circuit, Column, ConstraintSystem, Error, Selector},
|
2021-05-27 06:02:13 -07:00
|
|
|
poly::Rotation,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn unassigned_cell() {
|
|
|
|
const K: u32 = 4;
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
struct FaultyCircuitConfig {
|
|
|
|
a: Column<Advice>,
|
|
|
|
q: Selector,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FaultyCircuit {}
|
|
|
|
|
|
|
|
impl Circuit<Fp> for FaultyCircuit {
|
|
|
|
type Config = FaultyCircuitConfig;
|
2021-06-21 11:10:59 -07:00
|
|
|
type FloorPlanner = SimpleFloorPlanner;
|
2021-05-27 06:02:13 -07:00
|
|
|
|
|
|
|
fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
|
|
|
|
let a = meta.advice_column();
|
|
|
|
let b = meta.advice_column();
|
|
|
|
let q = meta.selector();
|
|
|
|
|
|
|
|
meta.create_gate("Equality check", |cells| {
|
|
|
|
let a = cells.query_advice(a, Rotation::prev());
|
|
|
|
let b = cells.query_advice(b, Rotation::cur());
|
2021-06-04 08:18:51 -07:00
|
|
|
let q = cells.query_selector(q);
|
2021-05-27 06:02:13 -07:00
|
|
|
|
|
|
|
// If q is enabled, a and b must be assigned to.
|
|
|
|
vec![q * (a - b)]
|
|
|
|
});
|
|
|
|
|
|
|
|
FaultyCircuitConfig { a, q }
|
|
|
|
}
|
|
|
|
|
2021-06-21 11:10:59 -07:00
|
|
|
fn without_witnesses(&self) -> Self {
|
|
|
|
Self {}
|
|
|
|
}
|
|
|
|
|
2021-05-27 06:02:13 -07:00
|
|
|
fn synthesize(
|
|
|
|
&self,
|
|
|
|
config: Self::Config,
|
2021-06-21 11:10:59 -07:00
|
|
|
mut layouter: impl Layouter<Fp>,
|
2021-05-27 06:02:13 -07:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
layouter.assign_region(
|
|
|
|
|| "Faulty synthesis",
|
|
|
|
|mut region| {
|
|
|
|
// Enable the equality gate.
|
2021-06-18 09:25:02 -07:00
|
|
|
config.q.enable(&mut region, 1)?;
|
2021-05-27 06:02:13 -07:00
|
|
|
|
|
|
|
// Assign a = 0.
|
2021-06-18 09:25:02 -07:00
|
|
|
region.assign_advice(|| "a", config.a, 0, || Ok(Fp::zero()))?;
|
2021-05-27 06:02:13 -07:00
|
|
|
|
|
|
|
// BUG: Forget to assign b = 0! This could go unnoticed during
|
|
|
|
// development, because cell values default to zero, which in this
|
|
|
|
// case is fine, but for other assignments would be broken.
|
|
|
|
Ok(())
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let prover = MockProver::run(K, &FaultyCircuit {}, vec![]).unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
prover.verify(),
|
|
|
|
Err(vec![VerifyFailure::Cell {
|
2021-06-23 05:14:49 -07:00
|
|
|
gate: (0, "Equality check").into(),
|
|
|
|
region: (0, "Faulty synthesis".to_owned()).into(),
|
2021-05-27 06:02:13 -07:00
|
|
|
column: Column::new(1, Any::Advice),
|
2021-06-18 09:25:02 -07:00
|
|
|
offset: 1,
|
2021-05-27 06:02:13 -07:00
|
|
|
}])
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|