diff --git a/src/circuit.rs b/src/circuit.rs new file mode 100644 index 0000000..fb74fad --- /dev/null +++ b/src/circuit.rs @@ -0,0 +1,143 @@ +//! Traits and structs for implementing circuit components. + +use std::fmt; + +use crate::{ + arithmetic::FieldExt, + plonk::{Advice, Any, Column, ConstraintSystem, Error, Fixed}, +}; + +pub mod layouter; + +/// A chip implements a set of instructions that can be used by gadgets. +/// +/// The chip itself should not store any state; instead, state that is required at circuit +/// synthesis time should be stored in [`Chip::Config`], which can then be fetched via +/// [`Layouter::config`]. +pub trait Chip: Sized { + /// A type that holds the configuration for this chip, and any other state it may need + /// during circuit synthesis. + type Config: fmt::Debug; + + /// The field that the chip is defined over. + /// + /// This provides a type that the chip's configuration can reference if necessary. + type Field: FieldExt; + + /// Load any fixed configuration for this chip into the circuit. + fn load(layouter: &mut impl Layouter) -> Result<(), Error>; +} + +/// A pointer to a cell within a circuit. +#[derive(Clone, Copy, Debug)] +pub struct Cell { + /// Identifies the region in which this cell resides. + region_index: usize, + row_offset: usize, + column: Column, +} + +/// A permutation configured by a chip. +#[derive(Clone, Debug)] +pub struct Permutation { + index: usize, + mapping: Vec>, +} + +impl Permutation { + /// Configures a new permutation for the given columns. + pub fn new(meta: &mut ConstraintSystem, columns: &[Column]) -> Self { + let index = meta.permutation(columns); + Permutation { + index, + mapping: columns.iter().map(|c| (*c).into()).collect(), + } + } +} + +/// A region of the circuit in which a [`Chip`] can assign cells. +/// +/// Inside a region, the chip may freely use relative offsets; the [`Layouter`] will +/// treat these assignments as a single "region" within the circuit. +/// +/// The [`Layouter`] is allowed to optimise between regions as it sees fit. Chips must use +/// [`Region::constrain_equal`] to copy in variables assigned in other regions. +/// +/// TODO: It would be great if we could constrain the columns in these types to be +/// "logical" columns that are guaranteed to correspond to the chip (and have come from +/// `Chip::Config`). +#[derive(Debug)] +pub struct Region<'r, C: Chip> { + region: &'r mut dyn layouter::RegionLayouter, +} + +impl<'r, C: Chip> From<&'r mut dyn layouter::RegionLayouter> for Region<'r, C> { + fn from(region: &'r mut dyn layouter::RegionLayouter) -> Self { + Region { region } + } +} + +impl<'r, C: Chip> Region<'r, C> { + /// Assign an advice column value (witness). + /// + /// Even though `to` has `FnMut` bounds, it is guaranteed to be called at most once. + pub fn assign_advice<'v>( + &'v mut self, + column: Column, + offset: usize, + mut to: impl FnMut() -> Result + 'v, + ) -> Result { + self.region.assign_advice(column, offset, &mut to) + } + + /// Assign a fixed value. + /// + /// Even though `to` has `FnMut` bounds, it is guaranteed to be called at most once. + pub fn assign_fixed<'v>( + &'v mut self, + column: Column, + offset: usize, + mut to: impl FnMut() -> Result + 'v, + ) -> Result { + self.region.assign_fixed(column, offset, &mut to) + } + + /// Constraint two cells to have the same value. + /// + /// Returns an error if either of the cells is not within the given permutation. + pub fn constrain_equal( + &mut self, + permutation: &Permutation, + left: Cell, + right: Cell, + ) -> Result<(), Error> { + self.region.constrain_equal(permutation, left, right) + } +} + +/// A layout strategy for a specific chip within a circuit. +/// +/// This abstracts over the circuit assignments, handling row indices etc. +/// +/// A particular concrete layout strategy will implement this trait for each chip it +/// supports. +pub trait Layouter { + /// Provides access to the chip configuration. + fn config(&self) -> &C::Config; + + /// Assign a region of gates to an absolute row number. + /// + /// Inside the closure, the chip may freely use relative offsets; the `Layouter` will + /// treat these assignments as a single "region" within the circuit. Outside this + /// closure, the `Layouter` is allowed to optimise as it sees fit. + /// + /// ```ignore + /// fn assign_region(&mut self, |region| { + /// region.assign_advice(self.config.a, offset, || { Some(value)}); + /// }); + /// ``` + fn assign_region( + &mut self, + assignment: impl FnOnce(Region<'_, C>) -> Result<(), Error>, + ) -> Result<(), Error>; +} diff --git a/src/circuit/layouter.rs b/src/circuit/layouter.rs new file mode 100644 index 0000000..9e226af --- /dev/null +++ b/src/circuit/layouter.rs @@ -0,0 +1,62 @@ +//! Implementations of common circuit layouters. + +use std::fmt; + +use super::{Cell, Chip, Permutation}; +use crate::plonk::{Advice, Column, Error, Fixed}; + +/// Helper trait for implementing a custom [`Layouter`]. +/// +/// This trait is used for implementing region assignments: +/// +/// ```ignore +/// impl<'a, C: Chip, CS: Assignment + 'a> Layouter for MyLayouter<'a, C, CS> { +/// fn assign_region( +/// &mut self, +/// assignment: impl FnOnce(Region<'_, C>) -> Result<(), Error>, +/// ) -> Result<(), Error> { +/// let region_index = self.regions.len(); +/// self.regions.push(self.current_gate); +/// +/// let mut region = MyRegion::new(self, region_index); +/// { +/// let region: &mut dyn RegionLayouter = &mut region; +/// assignment(region.into())?; +/// } +/// self.current_gate += region.row_count; +/// +/// Ok(()) +/// } +/// } +/// ``` +/// +/// TODO: It would be great if we could constrain the columns in these types to be +/// "logical" columns that are guaranteed to correspond to the chip (and have come from +/// `Chip::Config`). +pub trait RegionLayouter: fmt::Debug { + /// Assign an advice column value (witness) + fn assign_advice<'v>( + &'v mut self, + column: Column, + offset: usize, + to: &'v mut (dyn FnMut() -> Result + 'v), + ) -> Result; + + /// Assign a fixed value + fn assign_fixed<'v>( + &'v mut self, + column: Column, + offset: usize, + to: &'v mut (dyn FnMut() -> Result + 'v), + ) -> Result; + + /// Constraint two cells to have the same value. + /// + /// Returns an error if either of the cells is not within the given permutation. + fn constrain_equal( + &mut self, + permutation: &Permutation, + left: Cell, + right: Cell, + ) -> Result<(), Error>; +} diff --git a/src/lib.rs b/src/lib.rs index ac043c4..7e6bcfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ #![deny(unsafe_code)] pub mod arithmetic; +pub mod circuit; pub mod pasta; pub mod plonk; pub mod poly;