diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 2f1bba6..d7d04c7 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -7,8 +7,11 @@ use std::{ }; use super::{lookup, permutation, Error}; -use crate::arithmetic::FieldExt; -use crate::poly::Rotation; +use crate::{ + arithmetic::FieldExt, + circuit::{Chip, Region}, + poly::Rotation, +}; /// A column type pub trait ColumnType: 'static + Sized + std::fmt::Debug {} @@ -127,6 +130,71 @@ impl TryFrom> for Column { } } +/// A selector, representing a fixed boolean value per row of the circuit. +/// +/// Selectors can be used to conditionally enable (portions of) gates: +/// ``` +/// use halo2::poly::Rotation; +/// # use halo2::pasta::Fp; +/// # use halo2::plonk::ConstraintSystem; +/// +/// # let mut meta = ConstraintSystem::::default(); +/// let a = meta.advice_column(); +/// let b = meta.advice_column(); +/// let s = meta.selector(); +/// +/// meta.create_gate("foo", |meta| { +/// let a = meta.query_advice(a, Rotation::prev()); +/// let b = meta.query_advice(b, Rotation::cur()); +/// let s = meta.query_selector(s, Rotation::cur()); +/// +/// // On rows where the selector is enabled, a is constrained to equal b. +/// // On rows where the selector is disabled, a and b can take any value. +/// s * (a - b) +/// }); +/// ``` +/// +/// Selectors are disabled on all rows by default, and must be explicitly enabled on each +/// row when required: +/// ``` +/// use halo2::{circuit::{Chip, Layouter}, plonk::{Advice, Column, Error, Selector}}; +/// # use ff::Field; +/// # use halo2::plonk::Fixed; +/// +/// struct Config { +/// a: Column, +/// b: Column, +/// s: Selector, +/// } +/// +/// fn circuit_logic(mut layouter: impl Layouter) -> Result<(), Error> { +/// let config = layouter.config().clone(); +/// # let config: Config = todo!(); +/// layouter.assign_region(|| "bar", |mut region| { +/// region.assign_advice(|| "a", config.a, 0, || Ok(C::Field::one()))?; +/// region.assign_advice(|| "a", config.b, 1, || Ok(C::Field::one()))?; +/// config.s.enable(&mut region, 1) +/// })?; +/// Ok(()) +/// } +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Selector(Column); + +impl Selector { + /// Enable this selector at the given offset within the given region. + pub fn enable(&self, region: &mut Region, 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(C::Field::one())) + .map(|_| ()) + } +} + /// A permutation. #[derive(Clone, Debug)] pub struct Permutation { @@ -507,6 +575,11 @@ impl ConstraintSystem { index } + /// Query a selector at a relative position. + pub fn query_selector(&mut self, selector: Selector, at: Rotation) -> Expression { + Expression::Fixed(self.query_fixed_index(selector.0, at)) + } + 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() { @@ -642,6 +715,13 @@ impl ConstraintSystem { self.gates.push((name, poly)); } + /// Allocate a new selector. + pub fn selector(&mut self) -> Selector { + // TODO: Track selectors separately, and combine selectors where possible. + // https://github.com/zcash/halo2/issues/116 + Selector(self.fixed_column()) + } + /// Allocate a new fixed column pub fn fixed_column(&mut self) -> Column { let tmp = Column {