mirror of https://github.com/zcash/halo2.git
Merge pull request #272 from zcash/configure-api-changes
`Circuit` API changes
This commit is contained in:
commit
845bf724ff
|
@ -275,12 +275,18 @@ fn main() {
|
|||
* ... ... ... 0 0
|
||||
* ]
|
||||
*/
|
||||
let a_ = meta.query_any(a.into(), Rotation::cur());
|
||||
let b_ = meta.query_any(b.into(), Rotation::cur());
|
||||
let sl_ = meta.query_any(sl.into(), Rotation::cur());
|
||||
let sl2_ = meta.query_any(sl2.into(), Rotation::cur());
|
||||
meta.lookup(&[a_.clone()], &[sl_.clone()]);
|
||||
meta.lookup(&[a_, b_], &[sl_, sl2_]);
|
||||
meta.lookup(|meta| {
|
||||
let a_ = meta.query_any(a.into(), Rotation::cur());
|
||||
let sl_ = meta.query_any(sl.into(), Rotation::cur());
|
||||
vec![(a_, sl_)]
|
||||
});
|
||||
meta.lookup(|meta| {
|
||||
let a_ = meta.query_any(a.into(), Rotation::cur());
|
||||
let b_ = meta.query_any(b.into(), Rotation::cur());
|
||||
let sl_ = meta.query_any(sl.into(), Rotation::cur());
|
||||
let sl2_ = meta.query_any(sl2.into(), Rotation::cur());
|
||||
vec![(a_, sl_), (b_, sl2_)]
|
||||
});
|
||||
|
||||
meta.create_gate("Combined add-mult", |meta| {
|
||||
let d = meta.query_advice(d, Rotation::next());
|
||||
|
|
|
@ -162,16 +162,20 @@ impl<F: FieldExt> SpreadTableChip<F> {
|
|||
let table_dense = meta.fixed_column();
|
||||
let table_spread = meta.fixed_column();
|
||||
|
||||
let tag_cur = meta.query_advice(input_tag, Rotation::cur());
|
||||
let dense_cur = meta.query_advice(input_dense, Rotation::cur());
|
||||
let spread_cur = meta.query_advice(input_spread, Rotation::cur());
|
||||
let table_tag_cur = meta.query_fixed(table_tag, Rotation::cur());
|
||||
let table_dense_cur = meta.query_fixed(table_dense, Rotation::cur());
|
||||
let table_spread_cur = meta.query_fixed(table_spread, Rotation::cur());
|
||||
meta.lookup(
|
||||
&[tag_cur, dense_cur, spread_cur],
|
||||
&[table_tag_cur, table_dense_cur, table_spread_cur],
|
||||
);
|
||||
meta.lookup(|meta| {
|
||||
let tag_cur = meta.query_advice(input_tag, Rotation::cur());
|
||||
let dense_cur = meta.query_advice(input_dense, Rotation::cur());
|
||||
let spread_cur = meta.query_advice(input_spread, Rotation::cur());
|
||||
let table_tag_cur = meta.query_fixed(table_tag, Rotation::cur());
|
||||
let table_dense_cur = meta.query_fixed(table_dense, Rotation::cur());
|
||||
let table_spread_cur = meta.query_fixed(table_spread, Rotation::cur());
|
||||
|
||||
vec![
|
||||
(tag_cur, table_tag_cur),
|
||||
(dense_cur, table_dense_cur),
|
||||
(spread_cur, table_spread_cur),
|
||||
]
|
||||
});
|
||||
|
||||
SpreadTableConfig {
|
||||
input: SpreadInputs {
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::{fmt, marker::PhantomData};
|
|||
|
||||
use crate::{
|
||||
arithmetic::FieldExt,
|
||||
plonk::{Advice, Any, Column, Error, Fixed, Permutation},
|
||||
plonk::{Advice, Any, Column, Error, Fixed, Permutation, Selector},
|
||||
};
|
||||
|
||||
pub mod layouter;
|
||||
|
@ -110,6 +110,21 @@ impl<'r, F: FieldExt> From<&'r mut dyn layouter::RegionLayouter<F>> for Region<'
|
|||
}
|
||||
|
||||
impl<'r, F: FieldExt> Region<'r, F> {
|
||||
/// Enables a selector at the given offset.
|
||||
pub(crate) fn enable_selector<A, AR>(
|
||||
&mut self,
|
||||
annotation: A,
|
||||
selector: &Selector,
|
||||
offset: usize,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
A: Fn() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
self.region
|
||||
.enable_selector(&|| annotation().into(), selector, offset)
|
||||
}
|
||||
|
||||
/// Assign an advice column value (witness).
|
||||
///
|
||||
/// Even though `to` has `FnMut` bounds, it is guaranteed to be called at most once.
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::marker::PhantomData;
|
|||
|
||||
use super::{Cell, Layouter, Region, RegionIndex, RegionStart};
|
||||
use crate::arithmetic::FieldExt;
|
||||
use crate::plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation};
|
||||
use crate::plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation, Selector};
|
||||
|
||||
/// Helper trait for implementing a custom [`Layouter`].
|
||||
///
|
||||
|
@ -38,6 +38,14 @@ use crate::plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation};
|
|||
/// "logical" columns that are guaranteed to correspond to the chip (and have come from
|
||||
/// `Chip::Config`).
|
||||
pub trait RegionLayouter<F: FieldExt>: fmt::Debug {
|
||||
/// Enables a selector at the given offset.
|
||||
fn enable_selector<'v>(
|
||||
&'v mut self,
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
selector: &Selector,
|
||||
offset: usize,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Assign an advice column value (witness)
|
||||
fn assign_advice<'v>(
|
||||
&'v mut self,
|
||||
|
@ -194,6 +202,19 @@ impl RegionShape {
|
|||
}
|
||||
|
||||
impl<F: FieldExt> RegionLayouter<F> for RegionShape {
|
||||
fn enable_selector<'v>(
|
||||
&'v mut self,
|
||||
_: &'v (dyn Fn() -> String + 'v),
|
||||
selector: &Selector,
|
||||
offset: usize,
|
||||
) -> Result<(), Error> {
|
||||
// Track the selector's fixed column as part of the region's shape.
|
||||
// TODO: Avoid exposing selector internals?
|
||||
self.columns.insert(selector.0.into());
|
||||
self.row_count = cmp::max(self.row_count, offset + 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_advice<'v>(
|
||||
&'v mut self,
|
||||
_: &'v (dyn Fn() -> String + 'v),
|
||||
|
@ -267,6 +288,19 @@ impl<'r, 'a, F: FieldExt, CS: Assignment<F> + 'a> SingleChipLayouterRegion<'r, '
|
|||
impl<'r, 'a, F: FieldExt, CS: Assignment<F> + 'a> RegionLayouter<F>
|
||||
for SingleChipLayouterRegion<'r, 'a, F, CS>
|
||||
{
|
||||
fn enable_selector<'v>(
|
||||
&'v mut self,
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
selector: &Selector,
|
||||
offset: usize,
|
||||
) -> Result<(), Error> {
|
||||
self.layouter.cs.enable_selector(
|
||||
annotation,
|
||||
selector,
|
||||
*self.layouter.regions[*self.region_index] + offset,
|
||||
)
|
||||
}
|
||||
|
||||
fn assign_advice<'v>(
|
||||
&'v mut self,
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
|
|
95
src/dev.rs
95
src/dev.rs
|
@ -6,7 +6,7 @@ use crate::{
|
|||
arithmetic::{FieldExt, Group},
|
||||
plonk::{
|
||||
permutation, Advice, Any, Assignment, Circuit, Column, ColumnType, ConstraintSystem, Error,
|
||||
Expression, Fixed, Permutation,
|
||||
Expression, Fixed, Permutation, Selector,
|
||||
},
|
||||
poly::Rotation,
|
||||
};
|
||||
|
@ -167,6 +167,21 @@ impl<F: Field + Group> Assignment<F> for MockProver<F> {
|
|||
|
||||
fn exit_region(&mut self) {}
|
||||
|
||||
fn enable_selector<A, AR>(
|
||||
&mut self,
|
||||
annotation: A,
|
||||
selector: &Selector,
|
||||
row: usize,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
// Selectors are just fixed columns.
|
||||
// TODO: Track which gates are enabled by this selector.
|
||||
self.assign_fixed(annotation, selector.0, row, || Ok(F::one()))
|
||||
}
|
||||
|
||||
fn assign_advice<V, A, AR>(
|
||||
&mut self,
|
||||
_: A,
|
||||
|
@ -295,47 +310,47 @@ impl<F: FieldExt> MockProver<F> {
|
|||
let n = self.n as i32;
|
||||
|
||||
// Check that all gates are satisfied for all rows.
|
||||
let gate_errors =
|
||||
self.cs
|
||||
.gates
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(gate_index, (gate_name, gate))| {
|
||||
// We iterate from n..2n so we can just reduce to handle wrapping.
|
||||
(n..(2 * n)).filter_map(move |row| {
|
||||
fn load<'a, F: FieldExt, T: ColumnType>(
|
||||
n: i32,
|
||||
row: i32,
|
||||
queries: &'a [(Column<T>, Rotation)],
|
||||
cells: &'a [Vec<F>],
|
||||
) -> 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]
|
||||
}
|
||||
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)).filter_map(move |row| {
|
||||
fn load<'a, F: FieldExt, T: ColumnType>(
|
||||
n: i32,
|
||||
row: i32,
|
||||
queries: &'a [(Column<T>, Rotation)],
|
||||
cells: &'a [Vec<F>],
|
||||
) -> 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(
|
||||
&|scalar| scalar,
|
||||
&load(n, row, &self.cs.fixed_queries, &self.fixed),
|
||||
&load(n, row, &self.cs.advice_queries, &self.advice),
|
||||
&load(n, row, &self.cs.instance_queries, &self.instance),
|
||||
&|a, b| a + &b,
|
||||
&|a, b| a * &b,
|
||||
&|a, scalar| a * scalar,
|
||||
) == F::zero()
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(VerifyFailure::Gate {
|
||||
gate_index,
|
||||
gate_name,
|
||||
row: (row - n) as usize,
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
if gate.poly().evaluate(
|
||||
&|scalar| scalar,
|
||||
&load(n, row, &self.cs.fixed_queries, &self.fixed),
|
||||
&load(n, row, &self.cs.advice_queries, &self.advice),
|
||||
&load(n, row, &self.cs.instance_queries, &self.instance),
|
||||
&|a, b| a + &b,
|
||||
&|a, b| a * &b,
|
||||
&|a, scalar| a * scalar,
|
||||
) == F::zero()
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(VerifyFailure::Gate {
|
||||
gate_index,
|
||||
gate_name: gate.name(),
|
||||
row: (row - n) as usize,
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Check that all lookups exist in their respective tables.
|
||||
let lookup_errors =
|
||||
|
|
|
@ -2,7 +2,7 @@ use ff::Field;
|
|||
use tabbycat::{AttrList, Edge, GraphBuilder, GraphType, Identity, StmtList};
|
||||
|
||||
use crate::plonk::{
|
||||
Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed, Permutation,
|
||||
Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed, Permutation, Selector,
|
||||
};
|
||||
|
||||
pub mod layout;
|
||||
|
@ -86,6 +86,15 @@ impl<F: Field> Assignment<F> for Graph {
|
|||
// Do nothing; we don't care about regions in this context.
|
||||
}
|
||||
|
||||
fn enable_selector<A, AR>(&mut self, _: A, _: &Selector, _: usize) -> Result<(), Error>
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
// Do nothing; we don't care about cells in this context.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_advice<V, A, AR>(
|
||||
&mut self,
|
||||
_: A,
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::cmp;
|
|||
use std::collections::HashSet;
|
||||
|
||||
use crate::plonk::{
|
||||
Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed, Permutation,
|
||||
Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed, Permutation, Selector,
|
||||
};
|
||||
|
||||
/// Renders the circuit layout on the given drawing area.
|
||||
|
@ -229,6 +229,20 @@ impl<F: Field> Assignment<F> for Layout {
|
|||
self.current_region = None;
|
||||
}
|
||||
|
||||
fn enable_selector<A, AR>(
|
||||
&mut self,
|
||||
annotation: A,
|
||||
selector: &Selector,
|
||||
row: usize,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
// Selectors are just fixed columns.
|
||||
self.assign_fixed(annotation, selector.0, row, || Ok(F::one()))
|
||||
}
|
||||
|
||||
fn assign_advice<V, A, AR>(
|
||||
&mut self,
|
||||
_: A,
|
||||
|
|
|
@ -175,19 +175,12 @@ impl TryFrom<Column<Any>> for Column<Instance> {
|
|||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Selector(Column<Fixed>);
|
||||
pub struct Selector(pub(crate) Column<Fixed>);
|
||||
|
||||
impl Selector {
|
||||
/// Enable this selector at the given offset within the given region.
|
||||
pub fn enable<F: FieldExt>(&self, region: &mut Region<F>, offset: usize) -> Result<(), Error> {
|
||||
// TODO: Ensure that the default for a selector's cells is always zero, if we
|
||||
// alter the proving system to change the global default.
|
||||
// TODO: Add Region::enable_selector method to allow the layouter to control the
|
||||
// selector's assignment.
|
||||
// https://github.com/zcash/halo2/issues/116
|
||||
region
|
||||
.assign_fixed(|| "", self.0, offset, || Ok(F::one()))
|
||||
.map(|_| ())
|
||||
region.enable_selector(|| "", self, offset)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,6 +234,17 @@ pub trait Assignment<F: Field> {
|
|||
/// [`Layouter::assign_region`]: crate::circuit::Layouter#method.assign_region
|
||||
fn exit_region(&mut self);
|
||||
|
||||
/// Enables a selector at the given row.
|
||||
fn enable_selector<A, AR>(
|
||||
&mut self,
|
||||
annotation: A,
|
||||
selector: &Selector,
|
||||
row: usize,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>;
|
||||
|
||||
/// Assign an advice column value (witness)
|
||||
fn assign_advice<V, A, AR>(
|
||||
&mut self,
|
||||
|
@ -460,6 +464,43 @@ impl<F> Mul<F> for Expression<F> {
|
|||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct PointIndex(pub usize);
|
||||
|
||||
/// A "virtual cell" is a PLONK cell that has been queried at a particular relative offset
|
||||
/// within a custom gate.
|
||||
#[derive(Clone, Debug)]
|
||||
struct VirtualCell {
|
||||
column: Column<Any>,
|
||||
rotation: Rotation,
|
||||
}
|
||||
|
||||
impl<Col: Into<Column<Any>>> From<(Col, Rotation)> for VirtualCell {
|
||||
fn from((column, rotation): (Col, Rotation)) -> Self {
|
||||
VirtualCell {
|
||||
column: column.into(),
|
||||
rotation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Gate<F: Field> {
|
||||
name: &'static str,
|
||||
poly: Expression<F>,
|
||||
/// We track queried selectors separately from other cells, so that we can use them to
|
||||
/// trigger debug checks on gates.
|
||||
queried_selectors: Vec<VirtualCell>,
|
||||
queried_cells: Vec<VirtualCell>,
|
||||
}
|
||||
|
||||
impl<F: Field> Gate<F> {
|
||||
pub(crate) fn name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
|
||||
pub(crate) fn poly(&self) -> &Expression<F> {
|
||||
&self.poly
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a description of the circuit environment, such as the gate, column and
|
||||
/// permutation arrangements.
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -467,7 +508,7 @@ pub struct ConstraintSystem<F: Field> {
|
|||
pub(crate) num_fixed_columns: usize,
|
||||
pub(crate) num_advice_columns: usize,
|
||||
pub(crate) num_instance_columns: usize,
|
||||
pub(crate) gates: Vec<(&'static str, Expression<F>)>,
|
||||
pub(crate) gates: Vec<Gate<F>>,
|
||||
pub(crate) advice_queries: Vec<(Column<Advice>, Rotation)>,
|
||||
pub(crate) instance_queries: Vec<(Column<Instance>, Rotation)>,
|
||||
pub(crate) fixed_queries: Vec<(Column<Fixed>, Rotation)>,
|
||||
|
@ -495,12 +536,12 @@ pub struct PinnedConstraintSystem<'a, F: Field> {
|
|||
lookups: &'a Vec<lookup::Argument<F>>,
|
||||
}
|
||||
|
||||
struct PinnedGates<'a, F: Field>(&'a Vec<(&'static str, Expression<F>)>);
|
||||
struct PinnedGates<'a, F: Field>(&'a Vec<Gate<F>>);
|
||||
|
||||
impl<'a, F: Field> std::fmt::Debug for PinnedGates<'a, F> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
f.debug_list()
|
||||
.entries(self.0.iter().map(|(_, expr)| expr))
|
||||
.entries(self.0.iter().map(|gate| gate.poly()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -556,28 +597,23 @@ impl<F: Field> ConstraintSystem<F> {
|
|||
}
|
||||
|
||||
/// Add a lookup argument for some input expressions and table expressions.
|
||||
/// The function will panic if the number of input expressions and table
|
||||
/// expressions are not the same.
|
||||
///
|
||||
/// `table_map` returns a map between input expressions and the table expressions
|
||||
/// they need to match.
|
||||
pub fn lookup(
|
||||
&mut self,
|
||||
input_expressions: &[Expression<F>],
|
||||
table_expressions: &[Expression<F>],
|
||||
table_map: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<(Expression<F>, Expression<F>)>,
|
||||
) -> usize {
|
||||
assert_eq!(input_expressions.len(), table_expressions.len());
|
||||
let mut cells = VirtualCells::new(self);
|
||||
let table_map = table_map(&mut cells);
|
||||
|
||||
let index = self.lookups.len();
|
||||
|
||||
self.lookups
|
||||
.push(lookup::Argument::new(input_expressions, table_expressions));
|
||||
self.lookups.push(lookup::Argument::new(table_map));
|
||||
|
||||
index
|
||||
}
|
||||
|
||||
/// Query a selector at a relative position.
|
||||
pub fn query_selector(&mut self, selector: Selector, at: Rotation) -> Expression<F> {
|
||||
Expression::Fixed(self.query_fixed_index(selector.0, at))
|
||||
}
|
||||
|
||||
fn query_fixed_index(&mut self, column: Column<Fixed>, at: Rotation) -> usize {
|
||||
// Return existing query, if it exists
|
||||
for (index, fixed_query) in self.fixed_queries.iter().enumerate() {
|
||||
|
@ -593,11 +629,6 @@ impl<F: Field> ConstraintSystem<F> {
|
|||
index
|
||||
}
|
||||
|
||||
/// Query a fixed column at a relative position
|
||||
pub fn query_fixed(&mut self, column: Column<Fixed>, at: Rotation) -> Expression<F> {
|
||||
Expression::Fixed(self.query_fixed_index(column, at))
|
||||
}
|
||||
|
||||
pub(crate) fn query_advice_index(&mut self, column: Column<Advice>, at: Rotation) -> usize {
|
||||
// Return existing query, if it exists
|
||||
for (index, advice_query) in self.advice_queries.iter().enumerate() {
|
||||
|
@ -613,11 +644,6 @@ impl<F: Field> ConstraintSystem<F> {
|
|||
index
|
||||
}
|
||||
|
||||
/// Query an advice column at a relative position
|
||||
pub fn query_advice(&mut self, column: Column<Advice>, at: Rotation) -> Expression<F> {
|
||||
Expression::Advice(self.query_advice_index(column, at))
|
||||
}
|
||||
|
||||
fn query_instance_index(&mut self, column: Column<Instance>, at: Rotation) -> usize {
|
||||
// Return existing query, if it exists
|
||||
for (index, instance_query) in self.instance_queries.iter().enumerate() {
|
||||
|
@ -633,11 +659,6 @@ impl<F: Field> ConstraintSystem<F> {
|
|||
index
|
||||
}
|
||||
|
||||
/// Query an instance column at a relative position
|
||||
pub fn query_instance(&mut self, column: Column<Instance>, at: Rotation) -> Expression<F> {
|
||||
Expression::Instance(self.query_instance_index(column, at))
|
||||
}
|
||||
|
||||
fn query_any_index(&mut self, column: Column<Any>, at: Rotation) -> usize {
|
||||
match column.column_type() {
|
||||
Any::Advice => self.query_advice_index(Column::<Advice>::try_from(column).unwrap(), at),
|
||||
|
@ -648,21 +669,6 @@ impl<F: Field> ConstraintSystem<F> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Query an Any column at a relative position
|
||||
pub fn query_any(&mut self, column: Column<Any>, at: Rotation) -> Expression<F> {
|
||||
match column.column_type() {
|
||||
Any::Advice => Expression::Advice(
|
||||
self.query_advice_index(Column::<Advice>::try_from(column).unwrap(), at),
|
||||
),
|
||||
Any::Fixed => Expression::Fixed(
|
||||
self.query_fixed_index(Column::<Fixed>::try_from(column).unwrap(), at),
|
||||
),
|
||||
Any::Instance => Expression::Instance(
|
||||
self.query_instance_index(Column::<Instance>::try_from(column).unwrap(), at),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_advice_query_index(&self, column: Column<Advice>, at: Rotation) -> usize {
|
||||
for (index, advice_query) in self.advice_queries.iter().enumerate() {
|
||||
if advice_query == &(column, at) {
|
||||
|
@ -708,9 +714,22 @@ impl<F: Field> ConstraintSystem<F> {
|
|||
}
|
||||
|
||||
/// Create a new gate
|
||||
pub fn create_gate(&mut self, name: &'static str, f: impl FnOnce(&mut Self) -> Expression<F>) {
|
||||
let poly = f(self);
|
||||
self.gates.push((name, poly));
|
||||
pub fn create_gate(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
f: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F>,
|
||||
) {
|
||||
let mut cells = VirtualCells::new(self);
|
||||
let poly = f(&mut cells);
|
||||
let queried_selectors = cells.queried_selectors;
|
||||
let queried_cells = cells.queried_cells;
|
||||
|
||||
self.gates.push(Gate {
|
||||
name,
|
||||
poly,
|
||||
queried_selectors,
|
||||
queried_cells,
|
||||
});
|
||||
}
|
||||
|
||||
/// Allocate a new selector.
|
||||
|
@ -775,10 +794,72 @@ impl<F: Field> ConstraintSystem<F> {
|
|||
|
||||
// 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 self.gates.iter() {
|
||||
degree = std::cmp::max(degree, poly.degree());
|
||||
for gate in &self.gates {
|
||||
degree = std::cmp::max(degree, gate.poly().degree());
|
||||
}
|
||||
|
||||
degree
|
||||
}
|
||||
}
|
||||
|
||||
/// Exposes the "virtual cells" that can be queried while creating a custom gate or lookup
|
||||
/// table.
|
||||
#[derive(Debug)]
|
||||
pub struct VirtualCells<'a, F: Field> {
|
||||
meta: &'a mut ConstraintSystem<F>,
|
||||
queried_selectors: Vec<VirtualCell>,
|
||||
queried_cells: Vec<VirtualCell>,
|
||||
}
|
||||
|
||||
impl<'a, F: Field> VirtualCells<'a, F> {
|
||||
fn new(meta: &'a mut ConstraintSystem<F>) -> Self {
|
||||
VirtualCells {
|
||||
meta,
|
||||
queried_selectors: vec![],
|
||||
queried_cells: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Query a selector at a relative position.
|
||||
pub fn query_selector(&mut self, selector: Selector, at: Rotation) -> Expression<F> {
|
||||
self.queried_selectors.push((selector.0, at).into());
|
||||
Expression::Fixed(self.meta.query_fixed_index(selector.0, at))
|
||||
}
|
||||
|
||||
/// Query a fixed column at a relative position
|
||||
pub fn query_fixed(&mut self, column: Column<Fixed>, at: Rotation) -> Expression<F> {
|
||||
self.queried_cells.push((column, at).into());
|
||||
Expression::Fixed(self.meta.query_fixed_index(column, at))
|
||||
}
|
||||
|
||||
/// Query an advice column at a relative position
|
||||
pub fn query_advice(&mut self, column: Column<Advice>, at: Rotation) -> Expression<F> {
|
||||
self.queried_cells.push((column, at).into());
|
||||
Expression::Advice(self.meta.query_advice_index(column, at))
|
||||
}
|
||||
|
||||
/// Query an instance column at a relative position
|
||||
pub fn query_instance(&mut self, column: Column<Instance>, at: Rotation) -> Expression<F> {
|
||||
self.queried_cells.push((column, at).into());
|
||||
Expression::Instance(self.meta.query_instance_index(column, at))
|
||||
}
|
||||
|
||||
/// Query an Any column at a relative position
|
||||
pub fn query_any(&mut self, column: Column<Any>, at: Rotation) -> Expression<F> {
|
||||
self.queried_cells.push((column, at).into());
|
||||
match column.column_type() {
|
||||
Any::Advice => Expression::Advice(
|
||||
self.meta
|
||||
.query_advice_index(Column::<Advice>::try_from(column).unwrap(), at),
|
||||
),
|
||||
Any::Fixed => Expression::Fixed(
|
||||
self.meta
|
||||
.query_fixed_index(Column::<Fixed>::try_from(column).unwrap(), at),
|
||||
),
|
||||
Any::Instance => Expression::Instance(
|
||||
self.meta
|
||||
.query_instance_index(Column::<Instance>::try_from(column).unwrap(), at),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use ff::Field;
|
|||
use group::Curve;
|
||||
|
||||
use super::{
|
||||
circuit::{Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed},
|
||||
circuit::{Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, Selector},
|
||||
permutation, Error, LagrangeCoeff, Permutation, Polynomial, ProvingKey, VerifyingKey,
|
||||
};
|
||||
use crate::arithmetic::CurveAffine;
|
||||
|
@ -53,6 +53,24 @@ impl<F: Field> Assignment<F> for Assembly<F> {
|
|||
// Do nothing; we don't care about regions in this context.
|
||||
}
|
||||
|
||||
fn enable_selector<A, AR>(
|
||||
&mut self,
|
||||
annotation: A,
|
||||
selector: &Selector,
|
||||
row: usize,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
// Selectors are just fixed columns.
|
||||
// TODO: Ensure that the default for a selector's cells is always zero, if we
|
||||
// alter the proving system to change the global default.
|
||||
// TODO: Implement selector combining optimization
|
||||
// https://github.com/zcash/halo2/issues/116
|
||||
self.assign_fixed(annotation, selector.0, row, || Ok(F::one()))
|
||||
}
|
||||
|
||||
fn assign_advice<V, A, AR>(
|
||||
&mut self,
|
||||
_: A,
|
||||
|
|
|
@ -11,11 +11,14 @@ pub(crate) struct Argument<F: Field> {
|
|||
}
|
||||
|
||||
impl<F: Field> Argument<F> {
|
||||
pub fn new(input_expressions: &[Expression<F>], table_expressions: &[Expression<F>]) -> Self {
|
||||
assert_eq!(input_expressions.len(), table_expressions.len());
|
||||
/// Constructs a new lookup argument.
|
||||
///
|
||||
/// `table_map` is a sequence of `(input, table)` tuples.
|
||||
pub fn new(table_map: Vec<(Expression<F>, Expression<F>)>) -> Self {
|
||||
let (input_expressions, table_expressions) = table_map.into_iter().unzip();
|
||||
Argument {
|
||||
input_expressions: input_expressions.to_vec(),
|
||||
table_expressions: table_expressions.to_vec(),
|
||||
input_expressions,
|
||||
table_expressions,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use group::Curve;
|
|||
use std::iter;
|
||||
|
||||
use super::{
|
||||
circuit::{Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed},
|
||||
circuit::{Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, Selector},
|
||||
lookup, permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX,
|
||||
ChallengeY, Error, Permutation, ProvingKey,
|
||||
};
|
||||
|
@ -123,6 +123,21 @@ pub fn create_proof<
|
|||
// Do nothing; we don't care about regions in this context.
|
||||
}
|
||||
|
||||
fn enable_selector<A, AR>(
|
||||
&mut self,
|
||||
_: A,
|
||||
_: &Selector,
|
||||
_: usize,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
// We only care about advice columns here
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_advice<V, A, AR>(
|
||||
&mut self,
|
||||
_: A,
|
||||
|
@ -373,8 +388,8 @@ pub fn create_proof<
|
|||
|(((advice, instance), permutation_expressions), lookup_expressions)| {
|
||||
iter::empty()
|
||||
// Custom constraints
|
||||
.chain(meta.gates.iter().map(move |(_, poly)| {
|
||||
poly.evaluate(
|
||||
.chain(meta.gates.iter().map(move |gate| {
|
||||
gate.poly().evaluate(
|
||||
&|scalar| pk.vk.domain.constant_extended(scalar),
|
||||
&|index| pk.fixed_cosets[index].clone(),
|
||||
&|index| advice.advice_cosets[index].clone(),
|
||||
|
|
|
@ -164,8 +164,8 @@ pub fn verify_proof<'a, C: CurveAffine, E: EncodedChallenge<C>, T: TranscriptRea
|
|||
|
||||
std::iter::empty()
|
||||
// Evaluate the circuit using the custom gates provided
|
||||
.chain(vk.cs.gates.iter().map(move |(_, poly)| {
|
||||
poly.evaluate(
|
||||
.chain(vk.cs.gates.iter().map(move |gate| {
|
||||
gate.poly().evaluate(
|
||||
&|scalar| scalar,
|
||||
&|index| fixed_evals[index],
|
||||
&|index| advice_evals[index],
|
||||
|
|
|
@ -277,12 +277,18 @@ fn plonk_api() {
|
|||
* ... ... ... 0 0
|
||||
* ]
|
||||
*/
|
||||
let a_ = meta.query_any(a.into(), Rotation::cur());
|
||||
let b_ = meta.query_any(b.into(), Rotation::cur());
|
||||
let sl_ = meta.query_any(sl.into(), Rotation::cur());
|
||||
let sl2_ = meta.query_any(sl2.into(), Rotation::cur());
|
||||
meta.lookup(&[a_.clone()], &[sl_.clone()]);
|
||||
meta.lookup(&[a_ * b_], &[sl_ * sl2_]);
|
||||
meta.lookup(|meta| {
|
||||
let a_ = meta.query_any(a.into(), Rotation::cur());
|
||||
let sl_ = meta.query_any(sl.into(), Rotation::cur());
|
||||
vec![(a_, sl_)]
|
||||
});
|
||||
meta.lookup(|meta| {
|
||||
let a_ = meta.query_any(a.into(), Rotation::cur());
|
||||
let b_ = meta.query_any(b.into(), Rotation::cur());
|
||||
let sl_ = meta.query_any(sl.into(), Rotation::cur());
|
||||
let sl2_ = meta.query_any(sl2.into(), Rotation::cur());
|
||||
vec![(a_ * b_, sl_ * sl2_)]
|
||||
});
|
||||
|
||||
meta.create_gate("Combined add-mult", |meta| {
|
||||
let d = meta.query_advice(d, Rotation::next());
|
||||
|
|
Loading…
Reference in New Issue