Merge pull request #286 from zcash/constraint-annotations

Enable annotating individual constraints within gates
This commit is contained in:
str4d 2021-06-05 23:36:28 +01:00 committed by GitHub
commit 879509491f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 7 deletions

View File

@ -364,6 +364,7 @@ fn main() {
gate_index: 1,
gate_name: "public input",
constraint_index: 0,
constraint_name: "",
row: 6,
}])
);

View File

@ -631,6 +631,7 @@ fn main() {
gate_index: 0,
gate_name: "public input",
constraint_index: 0,
constraint_name: "",
row: 7,
}])
);

View File

@ -59,6 +59,9 @@ pub enum VerifyFailure {
/// from the closure passed to `ConstraintSystem::create_gate` during
/// `Circuit::configure`.
constraint_index: usize,
/// The name of the unsatisfied constraint. This is specified by the gate creator
/// (such as a chip implementation), and is not enforced to be unique.
constraint_name: &'static str,
/// The row on which this constraint is not satisfied.
row: usize,
},
@ -103,12 +106,21 @@ impl fmt::Display for VerifyFailure {
gate_index,
gate_name,
constraint_index,
constraint_name,
row,
} => {
write!(
f,
"Constraint {} in gate {} ('{}') is not satisfied on row {}",
constraint_index, gate_index, gate_name, row
"Constraint {}{} in gate {} ('{}') is not satisfied on row {}",
constraint_index,
if constraint_name.is_empty() {
String::new()
} else {
format!(" ('{}')", constraint_name)
},
gate_index,
gate_name,
row
)
}
Self::Lookup { lookup_index, row } => {
@ -176,7 +188,7 @@ impl fmt::Display for VerifyFailure {
/// let c = meta.query_advice(c, Rotation::cur());
///
/// // BUG: Should be a * b - c
/// vec![a * b + c]
/// Some(("buggy R1CS", a * b + c))
/// });
///
/// MyConfig { a, b, c }
@ -213,6 +225,7 @@ impl fmt::Display for VerifyFailure {
/// gate_index: 0,
/// gate_name: "R1CS constraint",
/// constraint_index: 0,
/// constraint_name: "buggy R1CS",
/// row: 0
/// }])
/// );
@ -502,6 +515,7 @@ impl<F: FieldExt> MockProver<F> {
gate_index,
gate_name: gate.name(),
constraint_index: poly_index,
constraint_name: gate.constraint_name(poly_index),
row: (row - n) as usize,
})
}

View File

@ -486,9 +486,37 @@ impl<Col: Into<Column<Any>>> From<(Col, Rotation)> for VirtualCell {
}
}
/// An individual polynomial constraint.
///
/// These are returned by the closures passed to `ConstraintSystem::create_gate`.
#[derive(Debug)]
pub struct Constraint<F: Field> {
name: &'static str,
poly: Expression<F>,
}
impl<F: Field> From<Expression<F>> for Constraint<F> {
fn from(poly: Expression<F>) -> Self {
Constraint { name: "", poly }
}
}
impl<F: Field> From<(&'static str, Expression<F>)> for Constraint<F> {
fn from((name, poly): (&'static str, Expression<F>)) -> Self {
Constraint { name, poly }
}
}
impl<F: Field> From<Expression<F>> for Vec<Constraint<F>> {
fn from(poly: Expression<F>) -> Self {
vec![Constraint { name: "", poly }]
}
}
#[derive(Clone, Debug)]
pub(crate) struct Gate<F: Field> {
name: &'static str,
constraint_names: Vec<&'static str>,
polys: Vec<Expression<F>>,
/// We track queried selectors separately from other cells, so that we can use them to
/// trigger debug checks on gates.
@ -501,6 +529,10 @@ impl<F: Field> Gate<F> {
self.name
}
pub(crate) fn constraint_name(&self, constraint_index: usize) -> &'static str {
self.constraint_names[constraint_index]
}
pub(crate) fn polynomials(&self) -> &[Expression<F>] {
&self.polys
}
@ -726,19 +758,36 @@ impl<F: Field> ConstraintSystem<F> {
}
}
/// Create a new gate
pub fn create_gate(
/// Creates a new gate.
///
/// # Panics
///
/// A gate is required to contain polynomial constraints. This method will panic if
/// `constraints` returns an empty iterator.
pub fn create_gate<C: Into<Constraint<F>>, Iter: IntoIterator<Item = C>>(
&mut self,
name: &'static str,
f: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<Expression<F>>,
constraints: impl FnOnce(&mut VirtualCells<'_, F>) -> Iter,
) {
let mut cells = VirtualCells::new(self);
let polys = f(&mut cells);
let constraints = constraints(&mut cells);
let queried_selectors = cells.queried_selectors;
let queried_cells = cells.queried_cells;
let (constraint_names, polys): (_, Vec<_>) = constraints
.into_iter()
.map(|c| c.into())
.map(|c| (c.name, c.poly))
.unzip();
assert!(
!polys.is_empty(),
"Gates must contain at least one constraint."
);
self.gates.push(Gate {
name,
constraint_names,
polys,
queried_selectors,
queried_cells,