use core::cmp::max; use core::ops::{Add, Mul}; use ff::Field; use std::{ convert::TryFrom, ops::{Neg, Sub}, }; use super::{lookup, permutation, Error}; use crate::poly::Rotation; /// A column type pub trait ColumnType: 'static + Sized + std::fmt::Debug {} /// A column with an index and type #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Column { index: usize, column_type: C, } impl Column { pub(crate) fn index(&self) -> usize { self.index } pub(crate) fn column_type(&self) -> &C { &self.column_type } } /// An advice column #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Advice; /// A fixed column #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Fixed; /// An instance column #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Instance; /// An enum over the Advice, Fixed, Instance structs #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum Any { /// An Advice variant Advice, /// A Fixed variant Fixed, /// An Instance variant Instance, } impl ColumnType for Advice {} impl ColumnType for Fixed {} impl ColumnType for Instance {} impl ColumnType for Any {} impl From> for Column { fn from(advice: Column) -> Column { Column { index: advice.index(), column_type: Any::Advice, } } } impl From> for Column { fn from(advice: Column) -> Column { Column { index: advice.index(), column_type: Any::Fixed, } } } impl From> for Column { fn from(advice: Column) -> Column { Column { index: advice.index(), column_type: Any::Instance, } } } impl TryFrom> for Column { type Error = &'static str; fn try_from(any: Column) -> Result { match any.column_type() { Any::Advice => Ok(Column { index: any.index(), column_type: Advice, }), _ => Err("Cannot convert into Column"), } } } impl TryFrom> for Column { type Error = &'static str; fn try_from(any: Column) -> Result { match any.column_type() { Any::Fixed => Ok(Column { index: any.index(), column_type: Fixed, }), _ => Err("Cannot convert into Column"), } } } impl TryFrom> for Column { type Error = &'static str; fn try_from(any: Column) -> Result { match any.column_type() { Any::Instance => Ok(Column { index: any.index(), column_type: Instance, }), _ => Err("Cannot convert into Column"), } } } /// This trait allows a [`Circuit`] to direct some backend to assign a witness /// for a constraint system. pub trait Assignment { /// Creates a new region and enters into it. /// /// Panics if we are currently in a region (if `exit_region` was not called). /// /// Not intended for downstream consumption; use [`Layouter::assign_region`] instead. /// /// [`Layouter::assign_region`]: crate::circuit::Layouter#method.assign_region fn enter_region(&mut self, name_fn: N) where NR: Into, N: FnOnce() -> NR; /// Exits the current region. /// /// Panics if we are not currently in a region (if `enter_region` was not called). /// /// Not intended for downstream consumption; use [`Layouter::assign_region`] instead. /// /// [`Layouter::assign_region`]: crate::circuit::Layouter#method.assign_region fn exit_region(&mut self); /// Assign an advice column value (witness) fn assign_advice( &mut self, annotation: A, column: Column, row: usize, to: V, ) -> Result<(), Error> where V: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; /// Assign a fixed value fn assign_fixed( &mut self, annotation: A, column: Column, row: usize, to: V, ) -> Result<(), Error> where V: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; /// Assign two advice columns to have the same value fn copy( &mut self, permutation: usize, left_column: usize, left_row: usize, right_column: usize, right_row: usize, ) -> Result<(), Error>; /// Creates a new (sub)namespace and enters into it. /// /// Not intended for downstream consumption; use [`Layouter::namespace`] instead. /// /// [`Layouter::namespace`]: crate::circuit::Layouter#method.namespace fn push_namespace(&mut self, name_fn: N) where NR: Into, N: FnOnce() -> NR; /// Exits out of the existing namespace. /// /// Not intended for downstream consumption; use [`Layouter::namespace`] instead. /// /// [`Layouter::namespace`]: crate::circuit::Layouter#method.namespace fn pop_namespace(&mut self, gadget_name: Option); } /// This is a trait that circuits provide implementations for so that the /// backend prover can ask the circuit to synthesize using some given /// [`ConstraintSystem`] implementation. pub trait Circuit { /// This is a configuration object that stores things like columns. type Config: Clone; /// The circuit is given an opportunity to describe the exact gate /// arrangement, column arrangement, etc. fn configure(meta: &mut ConstraintSystem) -> Self::Config; /// Given the provided `cs`, synthesize the circuit. The concrete type of /// the caller will be different depending on the context, and they may or /// may not expect to have a witness present. fn synthesize(&self, cs: &mut impl Assignment, config: Self::Config) -> Result<(), Error>; } /// Low-degree expression representing an identity that must hold over the committed columns. #[derive(Clone, Debug)] pub enum Expression { /// This is a fixed column queried at a certain relative location Fixed(usize), /// This is an advice (witness) column queried at a certain relative location Advice(usize), /// This is an instance (external) column queried at a certain relative location Instance(usize), /// This is the sum of two polynomials Sum(Box>, Box>), /// This is the product of two polynomials Product(Box>, Box>), /// This is a scaled polynomial Scaled(Box>, F), } impl Expression { /// Evaluate the polynomial using the provided closures to perform the /// operations. pub fn evaluate( &self, fixed_column: &impl Fn(usize) -> T, advice_column: &impl Fn(usize) -> T, instance_column: &impl Fn(usize) -> T, sum: &impl Fn(T, T) -> T, product: &impl Fn(T, T) -> T, scaled: &impl Fn(T, F) -> T, ) -> T { match self { Expression::Fixed(index) => fixed_column(*index), Expression::Advice(index) => advice_column(*index), Expression::Instance(index) => instance_column(*index), Expression::Sum(a, b) => { let a = a.evaluate( fixed_column, advice_column, instance_column, sum, product, scaled, ); let b = b.evaluate( fixed_column, advice_column, instance_column, sum, product, scaled, ); sum(a, b) } Expression::Product(a, b) => { let a = a.evaluate( fixed_column, advice_column, instance_column, sum, product, scaled, ); let b = b.evaluate( fixed_column, advice_column, instance_column, sum, product, scaled, ); product(a, b) } Expression::Scaled(a, f) => { let a = a.evaluate( fixed_column, advice_column, instance_column, sum, product, scaled, ); scaled(a, *f) } } } /// Compute the degree of this polynomial pub fn degree(&self) -> usize { match self { Expression::Fixed(_) => 1, Expression::Advice(_) => 1, Expression::Instance(_) => 1, Expression::Sum(a, b) => max(a.degree(), b.degree()), Expression::Product(a, b) => a.degree() + b.degree(), Expression::Scaled(poly, _) => poly.degree(), } } } impl Neg for Expression { type Output = Expression; fn neg(self) -> Self::Output { Expression::Scaled(Box::new(self), -F::one()) } } impl Add for Expression { type Output = Expression; fn add(self, rhs: Expression) -> Expression { Expression::Sum(Box::new(self), Box::new(rhs)) } } impl Sub for Expression { type Output = Expression; fn sub(self, rhs: Expression) -> Expression { Expression::Sum(Box::new(self), Box::new(-rhs)) } } impl Mul for Expression { type Output = Expression; fn mul(self, rhs: Expression) -> Expression { Expression::Product(Box::new(self), Box::new(rhs)) } } impl Mul for Expression { type Output = Expression; fn mul(self, rhs: F) -> Expression { Expression::Scaled(Box::new(self), rhs) } } /// Represents an index into a vector where each entry corresponds to a distinct /// point that polynomials are queried at. #[derive(Copy, Clone, Debug)] pub(crate) struct PointIndex(pub usize); /// This is a description of the circuit environment, such as the gate, column and /// permutation arrangements. #[derive(Debug, Clone)] pub struct ConstraintSystem { 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)>, pub(crate) advice_queries: Vec<(Column, Rotation)>, pub(crate) instance_queries: Vec<(Column, Rotation)>, pub(crate) fixed_queries: Vec<(Column, Rotation)>, // Vector of permutation arguments, where each corresponds to a sequence of columns // that are involved in a permutation argument. pub(crate) permutations: Vec, // Vector of lookup arguments, where each corresponds to a sequence of // input columns and a sequence of table columns involved in the lookup. pub(crate) lookups: Vec, } /// Represents the minimal parameters that determine a `ConstraintSystem`. #[derive(Debug)] pub struct PinnedConstraintSystem<'a, F: Field> { num_fixed_columns: &'a usize, num_advice_columns: &'a usize, num_instance_columns: &'a usize, gates: PinnedGates<'a, F>, advice_queries: &'a Vec<(Column, Rotation)>, instance_queries: &'a Vec<(Column, Rotation)>, fixed_queries: &'a Vec<(Column, Rotation)>, permutations: &'a Vec, lookups: &'a Vec, } struct PinnedGates<'a, F: Field>(&'a Vec<(&'static str, Expression)>); 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)) .finish() } } impl Default for ConstraintSystem { fn default() -> ConstraintSystem { ConstraintSystem { num_fixed_columns: 0, num_advice_columns: 0, num_instance_columns: 0, gates: vec![], fixed_queries: Vec::new(), advice_queries: Vec::new(), instance_queries: Vec::new(), permutations: Vec::new(), lookups: Vec::new(), } } } impl ConstraintSystem { /// Obtain a pinned version of this constraint system; a structure with the /// minimal parameters needed to determine the rest of the constraint /// system. pub fn pinned(&self) -> PinnedConstraintSystem<'_, F> { PinnedConstraintSystem { num_fixed_columns: &self.num_fixed_columns, num_advice_columns: &self.num_advice_columns, num_instance_columns: &self.num_instance_columns, gates: PinnedGates(&self.gates), fixed_queries: &self.fixed_queries, advice_queries: &self.advice_queries, instance_queries: &self.instance_queries, permutations: &self.permutations, lookups: &self.lookups, } } /// Add a permutation argument for some advice columns pub fn permutation(&mut self, columns: &[Column]) -> usize { let index = self.permutations.len(); for column in columns { self.query_any_index(*column, Rotation::cur()); } self.permutations .push(permutation::Argument::new(columns.to_vec())); index } /// Add a lookup argument for some input columns and table columns. /// The function will panic if the number of input columns and table /// columns are not the same. pub fn lookup( &mut self, input_columns: &[Column], table_columns: &[Column], ) -> usize { assert_eq!(input_columns.len(), table_columns.len()); let index = self.lookups.len(); for input in input_columns { self.query_any_index(*input, Rotation::cur()); } for table in table_columns { self.query_any_index(*table, Rotation::cur()); } self.lookups .push(lookup::Argument::new(input_columns, table_columns)); index } fn query_fixed_index(&mut self, column: Column, at: Rotation) -> usize { // Return existing query, if it exists for (index, fixed_query) in self.fixed_queries.iter().enumerate() { if fixed_query == &(column, at) { return index; } } // Make a new query let index = self.fixed_queries.len(); self.fixed_queries.push((column, at)); index } /// Query a fixed column at a relative position pub fn query_fixed(&mut self, column: Column, at: Rotation) -> Expression { Expression::Fixed(self.query_fixed_index(column, at)) } pub(crate) fn query_advice_index(&mut self, column: Column, at: Rotation) -> usize { // Return existing query, if it exists for (index, advice_query) in self.advice_queries.iter().enumerate() { if advice_query == &(column, at) { return index; } } // Make a new query let index = self.advice_queries.len(); self.advice_queries.push((column, at)); index } /// Query an advice column at a relative position pub fn query_advice(&mut self, column: Column, at: Rotation) -> Expression { Expression::Advice(self.query_advice_index(column, at)) } fn query_instance_index(&mut self, column: Column, at: Rotation) -> usize { // Return existing query, if it exists for (index, instance_query) in self.instance_queries.iter().enumerate() { if instance_query == &(column, at) { return index; } } // Make a new query let index = self.instance_queries.len(); self.instance_queries.push((column, at)); index } /// Query an instance column at a relative position pub fn query_instance(&mut self, column: Column, at: Rotation) -> Expression { Expression::Instance(self.query_instance_index(column, at)) } fn query_any_index(&mut self, column: Column, at: Rotation) -> usize { match column.column_type() { Any::Advice => self.query_advice_index(Column::::try_from(column).unwrap(), at), Any::Fixed => self.query_fixed_index(Column::::try_from(column).unwrap(), at), Any::Instance => { self.query_instance_index(Column::::try_from(column).unwrap(), at) } } } /// Query an Any column at a relative position pub fn query_any(&mut self, column: Column, at: Rotation) -> Expression { match column.column_type() { Any::Advice => Expression::Advice( self.query_advice_index(Column::::try_from(column).unwrap(), at), ), Any::Fixed => Expression::Fixed( self.query_fixed_index(Column::::try_from(column).unwrap(), at), ), Any::Instance => Expression::Instance( self.query_instance_index(Column::::try_from(column).unwrap(), at), ), } } pub(crate) fn get_advice_query_index(&self, column: Column, at: Rotation) -> usize { for (index, advice_query) in self.advice_queries.iter().enumerate() { if advice_query == &(column, at) { return index; } } panic!("get_advice_query_index called for non-existent query"); } pub(crate) fn get_fixed_query_index(&self, column: Column, at: Rotation) -> usize { for (index, fixed_query) in self.fixed_queries.iter().enumerate() { if fixed_query == &(column, at) { return index; } } panic!("get_fixed_query_index called for non-existent query"); } pub(crate) fn get_instance_query_index(&self, column: Column, at: Rotation) -> usize { for (index, instance_query) in self.instance_queries.iter().enumerate() { if instance_query == &(column, at) { return index; } } panic!("get_instance_query_index called for non-existent query"); } pub(crate) fn get_any_query_index(&self, column: Column, at: Rotation) -> usize { match column.column_type() { Any::Advice => { self.get_advice_query_index(Column::::try_from(column).unwrap(), at) } Any::Fixed => { self.get_fixed_query_index(Column::::try_from(column).unwrap(), at) } Any::Instance => { self.get_instance_query_index(Column::::try_from(column).unwrap(), at) } } } /// Create a new gate pub fn create_gate(&mut self, name: &'static str, f: impl FnOnce(&mut Self) -> Expression) { let poly = f(self); self.gates.push((name, poly)); } /// Allocate a new fixed column pub fn fixed_column(&mut self) -> Column { let tmp = Column { index: self.num_fixed_columns, column_type: Fixed, }; self.num_fixed_columns += 1; tmp } /// Allocate a new advice column pub fn advice_column(&mut self) -> Column { let tmp = Column { index: self.num_advice_columns, column_type: Advice, }; self.num_advice_columns += 1; tmp } /// Allocate a new instance column pub fn instance_column(&mut self) -> Column { let tmp = Column { index: self.num_instance_columns, column_type: Instance, }; self.num_instance_columns += 1; tmp } }