From 17da891b2594431cf089e5010db3f8a99418a98f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 15 Dec 2020 00:36:58 +0000 Subject: [PATCH 1/3] General traits and structs for implementing circuits --- src/circuit.rs | 143 ++++++++++++++++++++++++++++++++++++++++ src/circuit/layouter.rs | 62 +++++++++++++++++ src/lib.rs | 1 + 3 files changed, 206 insertions(+) create mode 100644 src/circuit.rs create mode 100644 src/circuit/layouter.rs 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; From 7e2406cc77bfe55e898342cbd267061d45e67f7f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 8 Jan 2021 01:54:28 +0000 Subject: [PATCH 2/3] Implement a simple single-chip layouter --- src/circuit/layouter.rs | 160 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 2 deletions(-) diff --git a/src/circuit/layouter.rs b/src/circuit/layouter.rs index 9e226af..fa329f5 100644 --- a/src/circuit/layouter.rs +++ b/src/circuit/layouter.rs @@ -1,9 +1,11 @@ //! Implementations of common circuit layouters. +use std::cmp; use std::fmt; +use std::marker::PhantomData; -use super::{Cell, Chip, Permutation}; -use crate::plonk::{Advice, Column, Error, Fixed}; +use super::{Cell, Chip, Layouter, Permutation, Region}; +use crate::plonk::{Advice, Assignment, Column, Error, Fixed}; /// Helper trait for implementing a custom [`Layouter`]. /// @@ -60,3 +62,157 @@ pub trait RegionLayouter: fmt::Debug { right: Cell, ) -> Result<(), Error>; } + +/// A [`Layouter`] for a single-chip circuit. +pub struct SingleChip<'a, C: Chip, CS: Assignment + 'a> { + cs: &'a mut CS, + config: C::Config, + regions: Vec, + current_gate: usize, + _marker: PhantomData, +} + +impl<'a, C: Chip, CS: Assignment + 'a> fmt::Debug for SingleChip<'a, C, CS> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SingleChip") + .field("config", &self.config) + .field("regions", &self.regions) + .field("current_gate", &self.current_gate) + .finish() + } +} + +impl<'a, C: Chip, CS: Assignment> SingleChip<'a, C, CS> { + /// Creates a new single-chip layouter. + pub fn new(cs: &'a mut CS, config: C::Config) -> Self { + SingleChip { + cs, + config, + regions: vec![], + current_gate: 0, + _marker: PhantomData, + } + } +} + +impl<'a, C: Chip, CS: Assignment + 'a> Layouter for SingleChip<'a, C, CS> { + fn config(&self) -> &C::Config { + &self.config + } + + 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 = SingleChipRegion::new(self, region_index); + { + let region: &mut dyn RegionLayouter = &mut region; + assignment(region.into())?; + } + self.current_gate += region.row_count; + + Ok(()) + } +} + +struct SingleChipRegion<'r, 'a, C: Chip, CS: Assignment + 'a> { + layouter: &'r mut SingleChip<'a, C, CS>, + region_index: usize, + row_count: usize, +} + +impl<'r, 'a, C: Chip, CS: Assignment + 'a> fmt::Debug + for SingleChipRegion<'r, 'a, C, CS> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SingleChipRegion") + .field("layouter", &self.layouter) + .field("region_index", &self.region_index) + .field("row_count", &self.row_count) + .finish() + } +} + +impl<'r, 'a, C: Chip, CS: Assignment + 'a> SingleChipRegion<'r, 'a, C, CS> { + fn new(layouter: &'r mut SingleChip<'a, C, CS>, region_index: usize) -> Self { + SingleChipRegion { + layouter, + region_index, + row_count: 0, + } + } +} + +impl<'r, 'a, C: Chip, CS: Assignment + 'a> RegionLayouter + for SingleChipRegion<'r, 'a, C, CS> +{ + fn assign_advice<'v>( + &'v mut self, + column: Column, + offset: usize, + to: &'v mut (dyn FnMut() -> Result + 'v), + ) -> Result { + self.layouter.cs.assign_advice( + column, + self.layouter.regions[self.region_index] + offset, + to, + )?; + self.row_count = cmp::max(self.row_count, offset); + + Ok(Cell { + region_index: self.region_index, + row_offset: offset, + column: column.into(), + }) + } + + fn assign_fixed<'v>( + &'v mut self, + column: Column, + offset: usize, + to: &'v mut (dyn FnMut() -> Result + 'v), + ) -> Result { + self.layouter.cs.assign_fixed( + column, + self.layouter.regions[self.region_index] + offset, + to, + )?; + self.row_count = cmp::max(self.row_count, offset); + Ok(Cell { + region_index: self.region_index, + row_offset: offset, + column: column.into(), + }) + } + + fn constrain_equal( + &mut self, + permutation: &Permutation, + left: Cell, + right: Cell, + ) -> Result<(), Error> { + let left_column = permutation + .mapping + .iter() + .position(|c| c == &left.column) + .ok_or(Error::SynthesisError)?; + let right_column = permutation + .mapping + .iter() + .position(|c| c == &right.column) + .ok_or(Error::SynthesisError)?; + + self.layouter.cs.copy( + permutation.index, + left_column, + self.layouter.regions[left.region_index] + left.row_offset, + right_column, + self.layouter.regions[right.region_index] + right.row_offset, + )?; + + Ok(()) + } +} From f24b60b5b0513e42615bf8cdbca6e7185e179fb1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 8 Jan 2021 01:55:10 +0000 Subject: [PATCH 3/3] Add a placeholder module for gadgets --- src/gadget.rs | 1 + src/lib.rs | 1 + 2 files changed, 2 insertions(+) create mode 100644 src/gadget.rs diff --git a/src/gadget.rs b/src/gadget.rs new file mode 100644 index 0000000..a876ca7 --- /dev/null +++ b/src/gadget.rs @@ -0,0 +1 @@ +//! Self-contained circuit implementations of various primitives. diff --git a/src/lib.rs b/src/lib.rs index 7e6bcfc..2accaaa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ pub mod arithmetic; pub mod circuit; +pub mod gadget; pub mod pasta; pub mod plonk; pub mod poly;