Add namespacing and gadget name collection to Layouter

This commit is contained in:
Jack Grigg 2021-01-22 18:36:42 +00:00
parent 60061f64fd
commit bf771a7446
7 changed files with 178 additions and 1 deletions

View file

@ -34,6 +34,7 @@ name = "plonk"
harness = false
[dependencies]
backtrace = { version = "0.3", optional = true }
bitvec = "0.18"
subtle = "2.3"
crossbeam-utils = "0.7"
@ -47,4 +48,5 @@ lazy_static = "1.4.0"
static_assertions = "1.1.0"
[features]
gadget-traces = ["backtrace"]
sanity-checks = []

View file

@ -1,6 +1,6 @@
//! Traits and structs for implementing circuit components.
use std::fmt;
use std::{fmt, marker::PhantomData};
use crate::{
arithmetic::FieldExt,
@ -136,6 +136,10 @@ impl<'r, C: Chip> Region<'r, C> {
/// A particular concrete layout strategy will implement this trait for each chip it
/// supports.
pub trait Layouter<C: Chip> {
/// Represents the type of the "root" of this layouter, so that nested namespaces
/// can minimize indirection.
type Root: Layouter<C>;
/// Provides access to the chip configuration.
fn config(&self) -> &C::Config;
@ -155,4 +159,104 @@ pub trait Layouter<C: Chip> {
A: FnMut(Region<'_, C>) -> Result<(), Error>,
N: Fn() -> NR,
NR: Into<String>;
/// Gets the "root" of this assignment, bypassing the namespacing.
///
/// Not intended for downstream consumption; use [`Layouter::namespace`] instead.
fn get_root(&mut self) -> &mut Self::Root;
/// Creates a new (sub)namespace and enters into it.
///
/// Not intended for downstream consumption; use [`Layouter::namespace`] instead.
fn push_namespace<NR, N>(&mut self, name_fn: N)
where
NR: Into<String>,
N: FnOnce() -> NR;
/// Exits out of the existing namespace.
///
/// Not intended for downstream consumption; use [`Layouter::namespace`] instead.
fn pop_namespace(&mut self, gadget_name: Option<String>);
/// Enters into a namespace.
fn namespace<NR, N>(&mut self, name_fn: N) -> NamespacedLayouter<'_, C, Self::Root>
where
NR: Into<String>,
N: FnOnce() -> NR,
{
self.get_root().push_namespace(name_fn);
NamespacedLayouter(self.get_root(), PhantomData)
}
}
/// This is a "namespaced" layouter which borrows a `Layouter` (pushing a namespace
/// context) and, when dropped, pops out of the namespace context.
#[derive(Debug)]
pub struct NamespacedLayouter<'a, C: Chip, L: Layouter<C> + 'a>(&'a mut L, PhantomData<C>);
impl<'a, C: Chip, L: Layouter<C> + 'a> Layouter<C> for NamespacedLayouter<'a, C, L> {
type Root = L::Root;
fn config(&self) -> &C::Config {
self.0.config()
}
fn assign_region<A, N, NR>(&mut self, name: N, assignment: A) -> Result<(), Error>
where
A: FnMut(Region<'_, C>) -> Result<(), Error>,
N: Fn() -> NR,
NR: Into<String>,
{
self.0.assign_region(name, assignment)
}
fn get_root(&mut self) -> &mut Self::Root {
self.0.get_root()
}
fn push_namespace<NR, N>(&mut self, _name_fn: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
panic!("Only the root's push_namespace should be called");
}
fn pop_namespace(&mut self, _gadget_name: Option<String>) {
panic!("Only the root's pop_namespace should be called");
}
}
impl<'a, C: Chip, L: Layouter<C> + 'a> Drop for NamespacedLayouter<'a, C, L> {
fn drop(&mut self) {
let gadget_name = {
#[cfg(feature = "gadget-traces")]
{
let mut gadget_name = None;
let mut is_second_frame = false;
backtrace::trace(|frame| {
if is_second_frame {
// Resolve this instruction pointer to a symbol name.
backtrace::resolve_frame(frame, |symbol| {
gadget_name = symbol.name().map(|name| format!("{:#}", name));
});
// We are done!
false
} else {
// We want the next frame.
is_second_frame = true;
true
}
});
gadget_name
}
#[cfg(not(feature = "gadget-traces"))]
None
};
self.get_root().pop_namespace(gadget_name);
}
}

View file

@ -100,6 +100,8 @@ impl<'a, C: Chip, CS: Assignment<C::Field>> SingleChip<'a, C, CS> {
}
impl<'a, C: Chip, CS: Assignment<C::Field> + 'a> Layouter<C> for SingleChip<'a, C, CS> {
type Root = Self;
fn config(&self) -> &C::Config {
&self.config
}
@ -142,6 +144,22 @@ impl<'a, C: Chip, CS: Assignment<C::Field> + 'a> Layouter<C> for SingleChip<'a,
Ok(())
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
fn push_namespace<NR, N>(&mut self, name_fn: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
self.cs.push_namespace(name_fn)
}
fn pop_namespace(&mut self, gadget_name: Option<String>) {
self.cs.pop_namespace(gadget_name)
}
}
/// The shape of a region. For a region at a certain index, we track

View file

@ -213,6 +213,18 @@ impl<F: Field + Group> Assignment<F> for MockProver<F> {
self.permutations[permutation].copy(left_column, left_row, right_column, right_row)
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
// TODO: Do something with namespaces :)
}
fn pop_namespace(&mut self, _: Option<String>) {
// TODO: Do something with namespaces :)
}
}
impl<F: FieldExt> MockProver<F> {

View file

@ -182,6 +182,23 @@ pub trait Assignment<F: Field> {
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<NR, N>(&mut self, name_fn: N)
where
NR: Into<String>,
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<String>);
}
/// This is a trait that circuits provide implementations for so that the

View file

@ -128,6 +128,18 @@ impl<F: Field> Assignment<F> for Assembly<F> {
self.permutations[permutation].copy(left_column, left_row, right_column, right_row)
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
// Do nothing; we don't care about namespaces in this context.
}
fn pop_namespace(&mut self, _: Option<String>) {
// Do nothing; we don't care about namespaces in this context.
}
}
/// Generate a `VerifyingKey` from an instance of `Circuit`.

View file

@ -162,6 +162,18 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
Ok(())
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
// Do nothing; we don't care about namespaces in this context.
}
fn pop_namespace(&mut self, _: Option<String>) {
// Do nothing; we don't care about namespaces in this context.
}
}
let mut witness = WitnessCollection {