From 6eebf3994b097f86847dfd55e2331003867445ab Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 21 Dec 2020 23:00:39 +0000 Subject: [PATCH 1/6] Add MockProver for developing circuits --- src/dev.rs | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/plonk.rs | 8 +++ 3 files changed, 210 insertions(+) create mode 100644 src/dev.rs diff --git a/src/dev.rs b/src/dev.rs new file mode 100644 index 00000000..0e73fe8e --- /dev/null +++ b/src/dev.rs @@ -0,0 +1,199 @@ +//! Tools for developing circuits. + +use ff::Field; +use std::collections::HashMap; + +use crate::{ + arithmetic::{FieldExt, Group}, + plonk::{Any, Assignment, Circuit, Column, ConstraintSystem, Error}, + poly::{EvaluationDomain, LagrangeCoeff, Polynomial}, +}; + +#[derive(Debug)] +struct Cell(usize, usize); + +/// The reasons why a particular circuit is not satisfied. +#[derive(Debug, PartialEq)] +pub enum VerifyFailure { + /// A gate was not satisfied for a particular row. + Gate { gate_index: usize, row: usize }, + /// A lookup input did not exist in its corresponding table. + Lookup { lookup_index: usize, row: usize }, +} + +/// A test +pub struct MockProver { + n: u32, + domain: EvaluationDomain, + cs: ConstraintSystem, + + // The fixed cells in the circuit, arranged as [column][row]. + fixed: Vec>, + // The advice cells in the circuit, arranged as [column][row]. + advice: Vec>, + // The aux cells in the circuit, arranged as [column][row]. + aux: Vec>, + + permutations: HashMap>, +} + +impl Assignment for MockProver { + fn assign_advice( + &mut self, + column: crate::plonk::Column, + row: usize, + to: impl FnOnce() -> Result, + ) -> Result<(), crate::plonk::Error> { + *self + .advice + .get_mut(column.index()) + .and_then(|v| v.get_mut(row)) + .ok_or(Error::BoundsFailure)? = to()?; + + Ok(()) + } + + fn assign_fixed( + &mut self, + column: crate::plonk::Column, + row: usize, + to: impl FnOnce() -> Result, + ) -> Result<(), crate::plonk::Error> { + *self + .fixed + .get_mut(column.index()) + .and_then(|v| v.get_mut(row)) + .ok_or(Error::BoundsFailure)? = to()?; + + Ok(()) + } + + fn copy( + &mut self, + permutation: usize, + left_column: usize, + left_row: usize, + right_column: usize, + right_row: usize, + ) -> Result<(), crate::plonk::Error> { + self.permutations + .entry(permutation) + .or_default() + .push((Cell(left_column, left_row), Cell(right_column, right_row))); + Ok(()) + } +} + +impl MockProver { + pub fn run>( + k: u32, + circuit: &ConcreteCircuit, + aux: Vec>, + ) -> Result { + let mut cs = ConstraintSystem::default(); + let config = ConcreteCircuit::configure(&mut cs); + + // The permutation argument will serve alongside the gates, so must be + // accounted for. + let mut degree = cs + .permutations + .iter() + .map(|p| p.required_degree()) + .max() + .unwrap_or(1); + + // Account for each gate to ensure our quotient polynomial is the + // correct degree and that our extended domain is the right size. + for poly in cs.gates.iter() { + degree = std::cmp::max(degree, poly.degree()); + } + + let domain = EvaluationDomain::new(degree as u32, k); + + let fixed = vec![domain.empty_lagrange(); cs.num_fixed_columns]; + let advice = vec![domain.empty_lagrange(); cs.num_advice_columns]; + + let mut prover = MockProver { + n: 1 << k, + domain, + cs, + fixed, + advice, + aux, + permutations: HashMap::default(), + }; + + circuit.synthesize(&mut prover, config)?; + + Ok(prover) + } + + /// Returns `Ok(())` if this `MockProver` is satisfied, or an error indicating the + /// reason that the circuit is not satisfied. + pub fn verify(&self) -> Result<(), VerifyFailure> { + let n = self.n as i32; + + // Check that all gates are satisfied for all rows. + for (gate_index, gate) in self.cs.gates.iter().enumerate() { + // We iterate from n..2n so we can just reduce to handle wrapping. + for row in n..(2 * n) { + if gate.evaluate( + &|index| { + let (column, at) = self.cs.fixed_queries[index]; + let resolved_row = (row + at.0) % n; + self.fixed[column.index()][resolved_row as usize].clone() + }, + &|index| { + let (column, at) = self.cs.advice_queries[index]; + let resolved_row = (row + at.0) % n; + self.advice[column.index()][resolved_row as usize].clone() + }, + &|index| { + let (column, at) = self.cs.aux_queries[index]; + let resolved_row = (row + at.0) % n; + self.aux[column.index()][resolved_row as usize].clone() + }, + &|a, b| a + &b, + &|a, b| a * &b, + &|a, scalar| a * scalar, + ) != F::zero() + { + return Err(VerifyFailure::Gate { + gate_index, + row: row as usize, + }); + } + } + } + + // Check that all lookups exist in their respective tables. + for (lookup_index, lookup) in self.cs.lookups.iter().enumerate() { + for input_row in 0..n { + let load = |column: &Column, row| match column.column_type() { + Any::Fixed => self.fixed[column.index()][row as usize], + Any::Advice => self.advice[column.index()][row as usize], + Any::Aux => self.aux[column.index()][row as usize], + }; + + let inputs: Vec<_> = lookup + .input_columns + .iter() + .map(|c| load(c, input_row)) + .collect(); + if !(0..n) + .map(|table_row| lookup.table_columns.iter().map(move |c| load(c, table_row))) + .any(|table_row| table_row.eq(inputs.iter().cloned())) + { + return Err(VerifyFailure::Lookup { + lookup_index, + row: input_row as usize, + }); + } + } + } + + // TODO: Implement the rest of the verification checks. + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 36287ac4..48d14ec4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,3 +20,6 @@ pub mod poly; pub mod transcript; pub mod model; + +#[cfg(test)] +mod dev; diff --git a/src/plonk.rs b/src/plonk.rs index 01c2b7bb..a15c9b14 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -119,6 +119,7 @@ type ChallengeX = ChallengeScalar; #[test] fn test_proving() { use crate::arithmetic::{Curve, FieldExt}; + use crate::dev::MockProver; use crate::pasta::{EqAffine, Fp, Fq}; use crate::poly::commitment::{Blind, Params}; use crate::transcript::DummyHash; @@ -462,6 +463,13 @@ fn test_proving() { .commit_lagrange(&pubinputs, Blind::default()) .to_affine(); + // Check this circuit is satisfied. + let prover = match MockProver::run(K, &circuit, vec![pubinputs.clone()]) { + Ok(prover) => prover, + Err(e) => panic!("{:?}", e), + }; + assert_eq!(prover.verify(), Ok(())); + for _ in 0..100 { // Create a proof let proof = Proof::create::, DummyHash, _>( From fb939f17a9e6d9fdf5dbdb348b4a5abc549af99a Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 22 Dec 2020 13:56:30 +0800 Subject: [PATCH 2/6] Add permutation check to MockProver --- src/dev.rs | 60 +++++++++++++++++++++++++++------ src/plonk.rs | 2 +- src/plonk/keygen.rs | 2 +- src/plonk/permutation.rs | 4 +++ src/plonk/permutation/keygen.rs | 8 ++--- 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index 0e73fe8e..3fcf7755 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -1,11 +1,10 @@ //! Tools for developing circuits. use ff::Field; -use std::collections::HashMap; use crate::{ arithmetic::{FieldExt, Group}, - plonk::{Any, Assignment, Circuit, Column, ConstraintSystem, Error}, + plonk::{permutation, Any, Assignment, Circuit, Column, ConstraintSystem, Error}, poly::{EvaluationDomain, LagrangeCoeff, Polynomial}, }; @@ -19,6 +18,12 @@ pub enum VerifyFailure { Gate { gate_index: usize, row: usize }, /// A lookup input did not exist in its corresponding table. Lookup { lookup_index: usize, row: usize }, + /// A permutation did not preserve the original value of a cell. + Permutation { + perm_index: usize, + column: usize, + row: usize, + }, } /// A test @@ -34,7 +39,7 @@ pub struct MockProver { // The aux cells in the circuit, arranged as [column][row]. aux: Vec>, - permutations: HashMap>, + permutations: Vec, } impl Assignment for MockProver { @@ -76,11 +81,12 @@ impl Assignment for MockProver { right_column: usize, right_row: usize, ) -> Result<(), crate::plonk::Error> { - self.permutations - .entry(permutation) - .or_default() - .push((Cell(left_column, left_row), Cell(right_column, right_row))); - Ok(()) + // Check bounds first + if permutation >= self.permutations.len() { + return Err(Error::BoundsFailure); + } + + self.permutations[permutation].copy(left_column, left_row, right_column, right_row) } } @@ -90,6 +96,8 @@ impl MockProver { circuit: &ConcreteCircuit, aux: Vec>, ) -> Result { + let n = 1 << k; + let mut cs = ConstraintSystem::default(); let config = ConcreteCircuit::configure(&mut cs); @@ -112,15 +120,20 @@ impl MockProver { let fixed = vec![domain.empty_lagrange(); cs.num_fixed_columns]; let advice = vec![domain.empty_lagrange(); cs.num_advice_columns]; + let permutations = cs + .permutations + .iter() + .map(|p| permutation::keygen::Assembly::new(n as usize, p)) + .collect(); let mut prover = MockProver { - n: 1 << k, + n, domain, cs, fixed, advice, aux, - permutations: HashMap::default(), + permutations, }; circuit.synthesize(&mut prover, config)?; @@ -192,6 +205,33 @@ impl MockProver { } } + // Check that permutations preserve the original values of the cells. + for (perm_index, assembly) in self.permutations.iter().enumerate() { + // Original values of columns involved in the permutation + let original = self.cs.permutations[perm_index] + .get_columns() + .iter() + .map(|c| self.advice[c.index()].clone()) + .collect::>(); + + // Iterate over each column of the permutation + for (column, values) in assembly.mapping.iter().enumerate() { + // Iterate over each row of the column to check that the cell's + // value is preserved by the mapping. + for (row, cell) in values.iter().enumerate() { + let original_cell = original[column][row]; + let permuted_cell = original[cell.0][cell.1]; + if original_cell != permuted_cell { + return Err(VerifyFailure::Permutation { + perm_index, + column, + row, + }); + } + } + } + } + // TODO: Implement the rest of the verification checks. Ok(()) diff --git a/src/plonk.rs b/src/plonk.rs index a15c9b14..257eb204 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -14,7 +14,7 @@ use crate::transcript::ChallengeScalar; mod circuit; mod keygen; mod lookup; -mod permutation; +pub(crate) mod permutation; mod vanishing; mod prover; diff --git a/src/plonk/keygen.rs b/src/plonk/keygen.rs index 984dca60..5a2aad8c 100644 --- a/src/plonk/keygen.rs +++ b/src/plonk/keygen.rs @@ -104,7 +104,7 @@ where permutations: cs .permutations .iter() - .map(|p| permutation::keygen::Assembly::new(params, p)) + .map(|p| permutation::keygen::Assembly::new(params.n as usize, p)) .collect(), _marker: std::marker::PhantomData, }; diff --git a/src/plonk/permutation.rs b/src/plonk/permutation.rs index 3bd1eef0..bade0e00 100644 --- a/src/plonk/permutation.rs +++ b/src/plonk/permutation.rs @@ -37,6 +37,10 @@ impl Argument { // - z(omega^{-1} X) \prod (p(X) + \delta^i \beta X + \gamma) std::cmp::max(self.columns.len() + 1, 2) } + + pub(crate) fn get_columns(&self) -> Vec> { + self.columns.clone() + } } /// The verifying key for a single permutation argument. diff --git a/src/plonk/permutation/keygen.rs b/src/plonk/permutation/keygen.rs index c7d92743..6c18a83c 100644 --- a/src/plonk/permutation/keygen.rs +++ b/src/plonk/permutation/keygen.rs @@ -15,19 +15,19 @@ pub(crate) struct AssemblyHelper { } pub(crate) struct Assembly { - mapping: Vec>, + pub(crate) mapping: Vec>, aux: Vec>, sizes: Vec>, } impl Assembly { - pub(crate) fn new(params: &Params, p: &Argument) -> Self { + pub(crate) fn new(n: usize, p: &Argument) -> Self { // Initialize the copy vector to keep track of copy constraints in all // the permutation arguments. let mut columns = vec![]; for i in 0..p.columns.len() { // Computes [(i, 0), (i, 1), ..., (i, n - 1)] - columns.push((0..params.n).map(|j| (i, j as usize)).collect()); + columns.push((0..n).map(|j| (i, j)).collect()); } // Before any equality constraints are applied, every cell in the permutation is @@ -36,7 +36,7 @@ impl Assembly { Assembly { mapping: columns.clone(), aux: columns, - sizes: vec![vec![1usize; params.n as usize]; p.columns.len()], + sizes: vec![vec![1usize; n]; p.columns.len()], } } From 64b06735bf0f679265e8ec5c2dbc814fe9f49c12 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 23 Dec 2020 19:32:39 +0000 Subject: [PATCH 3/6] Expose MockProver in crate, and add documentation --- src/dev.rs | 36 +++++++++++++++++++++++++++++---- src/lib.rs | 4 +--- src/plonk/permutation/keygen.rs | 1 + 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index 3fcf7755..8983dc02 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -15,18 +15,44 @@ struct Cell(usize, usize); #[derive(Debug, PartialEq)] pub enum VerifyFailure { /// A gate was not satisfied for a particular row. - Gate { gate_index: usize, row: usize }, + Gate { + /// The index of the gate that is not satisfied. These indices are assigned in the + /// order in which `ConstraintSystem::create_gate` is called during + /// `Circuit::configure`. + gate_index: usize, + /// The row on which this gate is not satisfied. + row: usize, + }, /// A lookup input did not exist in its corresponding table. - Lookup { lookup_index: usize, row: usize }, + 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, + }, /// A permutation did not preserve the original value of a cell. Permutation { + /// The index of the permutation that is not satisfied. These indices are assigned + /// in the order in which `ConstraintSystem::lookup` is called during + /// `Circuit::configure`. perm_index: usize, + /// The column in which this permutation is not satisfied. column: usize, + /// The row on which this permutation is not satisfied. row: usize, }, } -/// A test +/// 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. +#[derive(Debug)] pub struct MockProver { n: u32, domain: EvaluationDomain, @@ -91,6 +117,8 @@ impl Assignment for MockProver { } impl MockProver { + /// Runs a synthetic keygen-and-prove operation on the given circuit, collecting data + /// about the constraints and their assignments. pub fn run>( k: u32, circuit: &ConcreteCircuit, @@ -142,7 +170,7 @@ impl MockProver { } /// Returns `Ok(())` if this `MockProver` is satisfied, or an error indicating the - /// reason that the circuit is not satisfied. + /// first encountered reason that the circuit is not satisfied. pub fn verify(&self) -> Result<(), VerifyFailure> { let n = self.n as i32; diff --git a/src/lib.rs b/src/lib.rs index 48d14ec4..ac043c4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,5 @@ pub mod plonk; pub mod poly; pub mod transcript; +pub mod dev; pub mod model; - -#[cfg(test)] -mod dev; diff --git a/src/plonk/permutation/keygen.rs b/src/plonk/permutation/keygen.rs index 6c18a83c..aa95fe58 100644 --- a/src/plonk/permutation/keygen.rs +++ b/src/plonk/permutation/keygen.rs @@ -14,6 +14,7 @@ pub(crate) struct AssemblyHelper { deltaomega: Vec>, } +#[derive(Debug)] pub(crate) struct Assembly { pub(crate) mapping: Vec>, aux: Vec>, From 49f1598c0e3f5376a98596dca16ae8bd07834d1e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 23 Dec 2020 19:33:31 +0000 Subject: [PATCH 4/6] Add example to MockProver documentation Also fixes a bug in MockProver::verify (which was exposing an internal implementation detail as an incorrect row numbering). --- src/dev.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/src/dev.rs b/src/dev.rs index 8983dc02..2c49fcc2 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -52,6 +52,82 @@ pub enum VerifyFailure { /// 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. +/// +/// # Examples +/// +/// ``` +/// use halo2::{ +/// arithmetic::FieldExt, +/// dev::{MockProver, VerifyFailure}, +/// pasta::Fp, +/// plonk::{Advice, Assignment, Circuit, Column, ConstraintSystem, Error}, +/// }; +/// const K: u32 = 5; +/// +/// struct MyConfig { +/// a: Column, +/// b: Column, +/// c: Column, +/// } +/// +/// struct MyCircuit { +/// a: Option, +/// b: Option, +/// } +/// +/// impl Circuit for MyCircuit { +/// type Config = MyConfig; +/// +/// fn configure(meta: &mut ConstraintSystem) -> MyConfig { +/// let a = meta.advice_column(); +/// let b = meta.advice_column(); +/// let c = meta.advice_column(); +/// +/// meta.create_gate(|meta| { +/// let a = meta.query_advice(a, 0); +/// let b = meta.query_advice(b, 0); +/// let c = meta.query_advice(c, 0); +/// +/// // BUG: Should be a * b - c +/// a * b + c +/// }); +/// +/// MyConfig { a, b, c } +/// } +/// +/// fn synthesize(&self, cs: &mut impl Assignment, config: MyConfig) -> Result<(), Error> { +/// cs.assign_advice(config.a, 0, || { +/// self.a.map(|v| F::from_u64(v)).ok_or(Error::SynthesisError) +/// })?; +/// cs.assign_advice(config.b, 0, || { +/// self.b.map(|v| F::from_u64(v)).ok_or(Error::SynthesisError) +/// })?; +/// cs.assign_advice(config.c, 0, || { +/// self.a +/// .and_then(|a| self.b.map(|b| F::from_u64(a * b))) +/// .ok_or(Error::SynthesisError) +/// }) +/// } +/// } +/// +/// // Assemble the private inputs to the circuit. +/// let circuit = MyCircuit { +/// a: Some(2), +/// b: Some(4), +/// }; +/// +/// // This circuit has no public inputs. +/// let aux = vec![]; +/// +/// let prover = MockProver::::run(K, &circuit, aux).unwrap(); +/// assert_eq!( +/// prover.verify(), +/// Err(VerifyFailure::Gate { +/// gate_index: 0, +/// row: 0 +/// }) +/// ); +/// ``` #[derive(Debug)] pub struct MockProver { n: u32, @@ -201,7 +277,7 @@ impl MockProver { { return Err(VerifyFailure::Gate { gate_index, - row: row as usize, + row: (row - n) as usize, }); } } From 85902115857e53cb1ab08d2e5127b17f65929921 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 5 Jan 2021 22:51:40 +0000 Subject: [PATCH 5/6] Remove unnecessary parts from MockProver per review comments --- src/dev.rs | 37 ++++++------------------------------- src/plonk.rs | 2 +- 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index 2c49fcc2..0fe103d7 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -5,12 +5,8 @@ use ff::Field; use crate::{ arithmetic::{FieldExt, Group}, plonk::{permutation, Any, Assignment, Circuit, Column, ConstraintSystem, Error}, - poly::{EvaluationDomain, LagrangeCoeff, Polynomial}, }; -#[derive(Debug)] -struct Cell(usize, usize); - /// The reasons why a particular circuit is not satisfied. #[derive(Debug, PartialEq)] pub enum VerifyFailure { @@ -131,15 +127,14 @@ pub enum VerifyFailure { #[derive(Debug)] pub struct MockProver { n: u32, - domain: EvaluationDomain, cs: ConstraintSystem, // The fixed cells in the circuit, arranged as [column][row]. - fixed: Vec>, + fixed: Vec>, // The advice cells in the circuit, arranged as [column][row]. - advice: Vec>, + advice: Vec>, // The aux cells in the circuit, arranged as [column][row]. - aux: Vec>, + aux: Vec>, permutations: Vec, } @@ -198,32 +193,15 @@ impl MockProver { pub fn run>( k: u32, circuit: &ConcreteCircuit, - aux: Vec>, + aux: Vec>, ) -> Result { let n = 1 << k; let mut cs = ConstraintSystem::default(); let config = ConcreteCircuit::configure(&mut cs); - // The permutation argument will serve alongside the gates, so must be - // accounted for. - let mut degree = cs - .permutations - .iter() - .map(|p| p.required_degree()) - .max() - .unwrap_or(1); - - // Account for each gate to ensure our quotient polynomial is the - // correct degree and that our extended domain is the right size. - for poly in cs.gates.iter() { - degree = std::cmp::max(degree, poly.degree()); - } - - let domain = EvaluationDomain::new(degree as u32, k); - - let fixed = vec![domain.empty_lagrange(); cs.num_fixed_columns]; - let advice = vec![domain.empty_lagrange(); cs.num_advice_columns]; + let fixed = vec![vec![F::zero(); n as usize]; cs.num_fixed_columns]; + let advice = vec![vec![F::zero(); n as usize]; cs.num_advice_columns]; let permutations = cs .permutations .iter() @@ -232,7 +210,6 @@ impl MockProver { let mut prover = MockProver { n, - domain, cs, fixed, advice, @@ -336,8 +313,6 @@ impl MockProver { } } - // TODO: Implement the rest of the verification checks. - Ok(()) } } diff --git a/src/plonk.rs b/src/plonk.rs index 257eb204..e5e0c191 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -464,7 +464,7 @@ fn test_proving() { .to_affine(); // Check this circuit is satisfied. - let prover = match MockProver::run(K, &circuit, vec![pubinputs.clone()]) { + let prover = match MockProver::run(K, &circuit, vec![pubinputs.to_vec()]) { Ok(prover) => prover, Err(e) => panic!("{:?}", e), }; From 08da49353e1785466fa8ad59c38a0f980826fdce Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 7 Jan 2021 12:42:04 +0000 Subject: [PATCH 6/6] Fix clippy lints in MockProver --- src/dev.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index 0fe103d7..55f2de19 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -4,7 +4,8 @@ use ff::Field; use crate::{ arithmetic::{FieldExt, Group}, - plonk::{permutation, Any, Assignment, Circuit, Column, ConstraintSystem, Error}, + plonk::{permutation, Any, Assignment, Circuit, Column, ColumnType, ConstraintSystem, Error}, + poly::Rotation, }; /// The reasons why a particular circuit is not satisfied. @@ -231,22 +232,23 @@ impl MockProver { for (gate_index, gate) in self.cs.gates.iter().enumerate() { // We iterate from n..2n so we can just reduce to handle wrapping. for row in n..(2 * n) { + fn load<'a, F: FieldExt, T: ColumnType>( + n: i32, + row: i32, + queries: &'a [(Column, Rotation)], + cells: &'a [Vec], + ) -> impl Fn(usize) -> F + 'a { + move |index| { + let (column, at) = &queries[index]; + let resolved_row = (row + at.0) % n; + cells[column.index()][resolved_row as usize] + } + }; + if gate.evaluate( - &|index| { - let (column, at) = self.cs.fixed_queries[index]; - let resolved_row = (row + at.0) % n; - self.fixed[column.index()][resolved_row as usize].clone() - }, - &|index| { - let (column, at) = self.cs.advice_queries[index]; - let resolved_row = (row + at.0) % n; - self.advice[column.index()][resolved_row as usize].clone() - }, - &|index| { - let (column, at) = self.cs.aux_queries[index]; - let resolved_row = (row + at.0) % n; - self.aux[column.index()][resolved_row as usize].clone() - }, + &load(n, row, &self.cs.fixed_queries, &self.fixed), + &load(n, row, &self.cs.advice_queries, &self.advice), + &load(n, row, &self.cs.aux_queries, &self.aux), &|a, b| a + &b, &|a, b| a * &b, &|a, scalar| a * scalar,