mirror of https://github.com/zcash/halo2.git
Add MockProver for developing circuits
This commit is contained in:
parent
fb37172ffa
commit
6eebf3994b
|
@ -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<F: Group> {
|
||||||
|
n: u32,
|
||||||
|
domain: EvaluationDomain<F>,
|
||||||
|
cs: ConstraintSystem<F>,
|
||||||
|
|
||||||
|
// The fixed cells in the circuit, arranged as [column][row].
|
||||||
|
fixed: Vec<Polynomial<F, LagrangeCoeff>>,
|
||||||
|
// The advice cells in the circuit, arranged as [column][row].
|
||||||
|
advice: Vec<Polynomial<F, LagrangeCoeff>>,
|
||||||
|
// The aux cells in the circuit, arranged as [column][row].
|
||||||
|
aux: Vec<Polynomial<F, LagrangeCoeff>>,
|
||||||
|
|
||||||
|
permutations: HashMap<usize, Vec<(Cell, Cell)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field + Group> Assignment<F> for MockProver<F> {
|
||||||
|
fn assign_advice(
|
||||||
|
&mut self,
|
||||||
|
column: crate::plonk::Column<crate::plonk::Advice>,
|
||||||
|
row: usize,
|
||||||
|
to: impl FnOnce() -> Result<F, crate::plonk::Error>,
|
||||||
|
) -> 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<crate::plonk::Fixed>,
|
||||||
|
row: usize,
|
||||||
|
to: impl FnOnce() -> Result<F, crate::plonk::Error>,
|
||||||
|
) -> 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<F: FieldExt> MockProver<F> {
|
||||||
|
pub fn run<ConcreteCircuit: Circuit<F>>(
|
||||||
|
k: u32,
|
||||||
|
circuit: &ConcreteCircuit,
|
||||||
|
aux: Vec<Polynomial<F, LagrangeCoeff>>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
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<Any>, 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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,3 +20,6 @@ pub mod poly;
|
||||||
pub mod transcript;
|
pub mod transcript;
|
||||||
|
|
||||||
pub mod model;
|
pub mod model;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod dev;
|
||||||
|
|
|
@ -119,6 +119,7 @@ type ChallengeX<F> = ChallengeScalar<F, X>;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_proving() {
|
fn test_proving() {
|
||||||
use crate::arithmetic::{Curve, FieldExt};
|
use crate::arithmetic::{Curve, FieldExt};
|
||||||
|
use crate::dev::MockProver;
|
||||||
use crate::pasta::{EqAffine, Fp, Fq};
|
use crate::pasta::{EqAffine, Fp, Fq};
|
||||||
use crate::poly::commitment::{Blind, Params};
|
use crate::poly::commitment::{Blind, Params};
|
||||||
use crate::transcript::DummyHash;
|
use crate::transcript::DummyHash;
|
||||||
|
@ -462,6 +463,13 @@ fn test_proving() {
|
||||||
.commit_lagrange(&pubinputs, Blind::default())
|
.commit_lagrange(&pubinputs, Blind::default())
|
||||||
.to_affine();
|
.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 {
|
for _ in 0..100 {
|
||||||
// Create a proof
|
// Create a proof
|
||||||
let proof = Proof::create::<DummyHash<Fq>, DummyHash<Fp>, _>(
|
let proof = Proof::create::<DummyHash<Fq>, DummyHash<Fp>, _>(
|
||||||
|
|
Loading…
Reference in New Issue