mirror of https://github.com/zcash/halo2.git
Move config responsiblities from Layouter to Chip trait
This commit is contained in:
parent
24d7100bd4
commit
40e7bec352
|
@ -1,53 +1,56 @@
|
||||||
extern crate halo2;
|
extern crate halo2;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use halo2::{
|
use halo2::{
|
||||||
arithmetic::FieldExt,
|
arithmetic::FieldExt,
|
||||||
circuit::{layouter::SingleChip, Cell, Chip, Layouter},
|
circuit::{layouter::SingleChipLayouter, Cell, Chip, Config, Layouter, Region},
|
||||||
dev::VerifyFailure,
|
dev::VerifyFailure,
|
||||||
plonk::{
|
plonk::{
|
||||||
Advice, Assignment, Circuit, Column, ConstraintSystem, Error, Instance, Permutation,
|
Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Permutation, Selector,
|
||||||
Selector,
|
|
||||||
},
|
},
|
||||||
poly::Rotation,
|
poly::Rotation,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ANCHOR: instructions
|
// ANCHOR: instructions
|
||||||
trait NumericInstructions: Chip {
|
trait NumericInstructions<F: FieldExt>: Chip<F> {
|
||||||
/// Variable representing a number.
|
/// Variable representing a number.
|
||||||
type Num;
|
type Num;
|
||||||
|
|
||||||
/// Loads a number into the circuit as a private input.
|
/// Loads a number into the circuit as a private input.
|
||||||
fn load_private(
|
fn load_private(
|
||||||
layouter: &mut impl Layouter<Self>,
|
&self,
|
||||||
a: Option<Self::Field>,
|
layouter: &mut impl Layouter<F>,
|
||||||
|
a: Option<F>,
|
||||||
) -> Result<Self::Num, Error>;
|
) -> Result<Self::Num, Error>;
|
||||||
|
|
||||||
/// Returns `c = a * b`.
|
/// Returns `c = a * b`.
|
||||||
fn mul(
|
fn mul(
|
||||||
layouter: &mut impl Layouter<Self>,
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
a: Self::Num,
|
a: Self::Num,
|
||||||
b: Self::Num,
|
b: Self::Num,
|
||||||
) -> Result<Self::Num, Error>;
|
) -> Result<Self::Num, Error>;
|
||||||
|
|
||||||
/// Exposes a number as a public input to the circuit.
|
/// Exposes a number as a public input to the circuit.
|
||||||
fn expose_public(layouter: &mut impl Layouter<Self>, num: Self::Num) -> Result<(), Error>;
|
fn expose_public(&self, layouter: &mut impl Layouter<F>, num: Self::Num) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
// ANCHOR_END: instructions
|
// ANCHOR_END: instructions
|
||||||
|
|
||||||
// ANCHOR: chip
|
// ANCHOR: chip
|
||||||
/// The chip that will implement our instructions! Chips do not store any persistent
|
/// The chip that will implement our instructions! Chips store their own
|
||||||
/// state themselves, and usually only contain type markers if necessary.
|
/// config, as well as type markers if necessary.
|
||||||
struct FieldChip<F: FieldExt> {
|
struct FieldChip<F: FieldExt> {
|
||||||
|
config: FieldConfigEnum,
|
||||||
_marker: PhantomData<F>,
|
_marker: PhantomData<F>,
|
||||||
}
|
}
|
||||||
// ANCHOR_END: chip
|
// ANCHOR_END: chip
|
||||||
|
|
||||||
// ANCHOR: chip-config
|
// ANCHOR: chip-config
|
||||||
/// Chip state is stored in a separate config struct. This is generated by the chip
|
/// Chip state is stored in a config struct. This is generated by the chip
|
||||||
/// during configuration, and then handed to the `Layouter`, which makes it available
|
/// during configuration, and then stored inside the chip.
|
||||||
/// to the chip when it needs to implement its instructions.
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct FieldConfig {
|
struct FieldConfig {
|
||||||
/// For this chip, we will use two advice columns to implement our instructions.
|
/// For this chip, we will use two advice columns to implement our instructions.
|
||||||
|
@ -70,12 +73,52 @@ struct FieldConfig {
|
||||||
s_pub: Selector,
|
s_pub: Selector,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt> FieldChip<F> {
|
#[derive(Clone, Debug)]
|
||||||
|
enum FieldConfigEnum {
|
||||||
|
Empty,
|
||||||
|
Config(FieldConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config for FieldConfigEnum {
|
||||||
|
fn empty() -> Self {
|
||||||
|
Self::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANCHOR_END: chip-config
|
||||||
|
|
||||||
|
// ANCHOR: chip-impl
|
||||||
|
impl<F: FieldExt> Chip<F> for FieldChip<F> {
|
||||||
|
type Config = FieldConfigEnum;
|
||||||
|
type Loaded = ();
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
config: Self::Config::empty(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct(config: Self::Config, _loaded: Self::Loaded) -> Self {
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn configure(
|
fn configure(
|
||||||
|
&mut self,
|
||||||
meta: &mut ConstraintSystem<F>,
|
meta: &mut ConstraintSystem<F>,
|
||||||
advice: [Column<Advice>; 2],
|
_selectors: BTreeMap<&str, Selector>,
|
||||||
instance: Column<Instance>,
|
columns: BTreeMap<&str, Column<Any>>,
|
||||||
) -> FieldConfig {
|
_perms: BTreeMap<&str, Permutation>,
|
||||||
|
) -> Self::Config {
|
||||||
|
let advice = [
|
||||||
|
*columns.get("advice0").unwrap(),
|
||||||
|
*columns.get("advice1").unwrap(),
|
||||||
|
];
|
||||||
|
let instance = *columns.get("instance").unwrap();
|
||||||
|
|
||||||
let perm = Permutation::new(
|
let perm = Permutation::new(
|
||||||
meta,
|
meta,
|
||||||
&advice
|
&advice
|
||||||
|
@ -100,9 +143,9 @@ impl<F: FieldExt> FieldChip<F> {
|
||||||
// offset adds a cost to the proof. The most common offsets are 0 (the
|
// offset adds a cost to the proof. The most common offsets are 0 (the
|
||||||
// current row), 1 (the next row), and -1 (the previous row), for which
|
// current row), 1 (the next row), and -1 (the previous row), for which
|
||||||
// `Rotation` has specific constructors.
|
// `Rotation` has specific constructors.
|
||||||
let lhs = meta.query_advice(advice[0], Rotation::cur());
|
let lhs = meta.query_any(advice[0], Rotation::cur());
|
||||||
let rhs = meta.query_advice(advice[1], Rotation::cur());
|
let rhs = meta.query_any(advice[1], Rotation::cur());
|
||||||
let out = meta.query_advice(advice[0], Rotation::next());
|
let out = meta.query_any(advice[0], Rotation::next());
|
||||||
let s_mul = meta.query_selector(s_mul, Rotation::cur());
|
let s_mul = meta.query_selector(s_mul, Rotation::cur());
|
||||||
|
|
||||||
// The polynomial expression returned from `create_gate` will be
|
// The polynomial expression returned from `create_gate` will be
|
||||||
|
@ -117,8 +160,8 @@ impl<F: FieldExt> FieldChip<F> {
|
||||||
meta.create_gate("public input", |meta| {
|
meta.create_gate("public input", |meta| {
|
||||||
// We choose somewhat-arbitrarily that we will use the second advice
|
// We choose somewhat-arbitrarily that we will use the second advice
|
||||||
// column for exposing numbers as public inputs.
|
// column for exposing numbers as public inputs.
|
||||||
let a = meta.query_advice(advice[1], Rotation::cur());
|
let a = meta.query_any(advice[1], Rotation::cur());
|
||||||
let p = meta.query_instance(instance, Rotation::cur());
|
let p = meta.query_any(instance, Rotation::cur());
|
||||||
let s = meta.query_selector(s_pub, Rotation::cur());
|
let s = meta.query_selector(s_pub, Rotation::cur());
|
||||||
|
|
||||||
// We simply constrain the advice cell to be equal to the instance cell,
|
// We simply constrain the advice cell to be equal to the instance cell,
|
||||||
|
@ -126,27 +169,30 @@ impl<F: FieldExt> FieldChip<F> {
|
||||||
s * (p + a * -F::one())
|
s * (p + a * -F::one())
|
||||||
});
|
});
|
||||||
|
|
||||||
FieldConfig {
|
FieldConfigEnum::Config(FieldConfig {
|
||||||
advice,
|
advice: [
|
||||||
perm,
|
Column::<Advice>::try_from(advice[0]).unwrap(),
|
||||||
s_mul,
|
Column::<Advice>::try_from(advice[1]).unwrap(),
|
||||||
s_pub,
|
],
|
||||||
|
perm: perm,
|
||||||
|
s_mul: s_mul,
|
||||||
|
s_pub: s_pub,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn config(&self) -> &Self::Config {
|
||||||
|
&self.config
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// ANCHOR_END: chip-config
|
|
||||||
|
|
||||||
// ANCHOR: chip-impl
|
fn load(&mut self, _layouter: &mut impl Layouter<F>) -> Result<(), halo2::plonk::Error> {
|
||||||
impl<F: FieldExt> Chip for FieldChip<F> {
|
|
||||||
type Config = FieldConfig;
|
|
||||||
type Loaded = ();
|
|
||||||
type Field = F;
|
|
||||||
|
|
||||||
fn load(_layouter: &mut impl Layouter<Self>) -> Result<(), halo2::plonk::Error> {
|
|
||||||
// None of the instructions implemented by this chip have any fixed state.
|
// None of the instructions implemented by this chip have any fixed state.
|
||||||
// But if we required e.g. a lookup table, this is where we would load it.
|
// But if we required e.g. a lookup table, this is where we would load it.
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn loaded(&self) -> &Self::Loaded {
|
||||||
|
&()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// ANCHOR_END: chip-impl
|
// ANCHOR_END: chip-impl
|
||||||
|
|
||||||
|
@ -158,14 +204,19 @@ struct Number<F: FieldExt> {
|
||||||
value: Option<F>,
|
value: Option<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt> NumericInstructions for FieldChip<F> {
|
impl<F: FieldExt> NumericInstructions<F> for FieldChip<F> {
|
||||||
type Num = Number<F>;
|
type Num = Number<F>;
|
||||||
|
|
||||||
fn load_private(
|
fn load_private(
|
||||||
layouter: &mut impl Layouter<Self>,
|
&self,
|
||||||
value: Option<Self::Field>,
|
layouter: &mut impl Layouter<F>,
|
||||||
|
value: Option<F>,
|
||||||
) -> Result<Self::Num, Error> {
|
) -> Result<Self::Num, Error> {
|
||||||
let config = layouter.config().clone();
|
let config = match self.config() {
|
||||||
|
FieldConfigEnum::Config(config) => config.clone(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
let mut num = None;
|
let mut num = None;
|
||||||
layouter.assign_region(
|
layouter.assign_region(
|
||||||
|| "load private",
|
|| "load private",
|
||||||
|
@ -184,15 +235,20 @@ impl<F: FieldExt> NumericInstructions for FieldChip<F> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mul(
|
fn mul(
|
||||||
layouter: &mut impl Layouter<Self>,
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
a: Self::Num,
|
a: Self::Num,
|
||||||
b: Self::Num,
|
b: Self::Num,
|
||||||
) -> Result<Self::Num, Error> {
|
) -> Result<Self::Num, Error> {
|
||||||
let config = layouter.config().clone();
|
let config = match self.config() {
|
||||||
|
FieldConfigEnum::Config(config) => config.clone(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
let mut out = None;
|
let mut out = None;
|
||||||
layouter.assign_region(
|
layouter.assign_region(
|
||||||
|| "mul",
|
|| "mul",
|
||||||
|mut region| {
|
|mut region: Region<'_, F>| {
|
||||||
// We only want to use a single multiplication gate in this region,
|
// We only want to use a single multiplication gate in this region,
|
||||||
// so we enable it at region offset 0; this means it will constrain
|
// so we enable it at region offset 0; this means it will constrain
|
||||||
// cells at offsets 0 and 1.
|
// cells at offsets 0 and 1.
|
||||||
|
@ -236,11 +292,15 @@ impl<F: FieldExt> NumericInstructions for FieldChip<F> {
|
||||||
Ok(out.unwrap())
|
Ok(out.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expose_public(layouter: &mut impl Layouter<Self>, num: Self::Num) -> Result<(), Error> {
|
fn expose_public(&self, layouter: &mut impl Layouter<F>, num: Self::Num) -> Result<(), Error> {
|
||||||
let config = layouter.config().clone();
|
let config = match self.config() {
|
||||||
|
FieldConfigEnum::Config(config) => config.clone(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
layouter.assign_region(
|
layouter.assign_region(
|
||||||
|| "expose public",
|
|| "expose public",
|
||||||
|mut region| {
|
|mut region: Region<'_, F>| {
|
||||||
// Enable the public-input gate.
|
// Enable the public-input gate.
|
||||||
config.s_pub.enable(&mut region, 0)?;
|
config.s_pub.enable(&mut region, 0)?;
|
||||||
|
|
||||||
|
@ -275,24 +335,29 @@ struct MyCircuit<F: FieldExt> {
|
||||||
|
|
||||||
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||||
// Since we are using a single chip for everything, we can just reuse its config.
|
// Since we are using a single chip for everything, we can just reuse its config.
|
||||||
type Config = FieldConfig;
|
type Config = FieldConfigEnum;
|
||||||
|
|
||||||
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
||||||
// We create the two advice columns that FieldChip uses for I/O.
|
// We create the two advice columns that FieldChip uses for I/O.
|
||||||
let advice = [meta.advice_column(), meta.advice_column()];
|
|
||||||
|
|
||||||
// We also need an instance column to store public inputs.
|
// We also need an instance column to store public inputs.
|
||||||
let instance = meta.instance_column();
|
|
||||||
|
|
||||||
FieldChip::configure(meta, advice, instance)
|
let mut columns: BTreeMap<&str, Column<Any>> = BTreeMap::new();
|
||||||
|
columns.insert("advice0", meta.advice_column().into());
|
||||||
|
columns.insert("advice1", meta.advice_column().into());
|
||||||
|
columns.insert("instance", meta.instance_column().into());
|
||||||
|
|
||||||
|
let mut field_chip = FieldChip::new();
|
||||||
|
|
||||||
|
field_chip.configure(meta, BTreeMap::default(), columns, BTreeMap::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn synthesize(&self, cs: &mut impl Assignment<F>, config: Self::Config) -> Result<(), Error> {
|
fn synthesize(&self, cs: &mut impl Assignment<F>, config: Self::Config) -> Result<(), Error> {
|
||||||
let mut layouter = SingleChip::new(cs, config)?;
|
let mut layouter = SingleChipLayouter::new(cs)?;
|
||||||
|
let field_chip = FieldChip::<F>::construct(config, ());
|
||||||
|
|
||||||
// Load our private values into the circuit.
|
// Load our private values into the circuit.
|
||||||
let a = FieldChip::load_private(&mut layouter, self.a)?;
|
let a = field_chip.load_private(&mut layouter, self.a)?;
|
||||||
let b = FieldChip::load_private(&mut layouter, self.b)?;
|
let b = field_chip.load_private(&mut layouter, self.b)?;
|
||||||
|
|
||||||
// We only have access to plain multiplication.
|
// We only have access to plain multiplication.
|
||||||
// We could implement our circuit as:
|
// We could implement our circuit as:
|
||||||
|
@ -303,11 +368,11 @@ impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||||
// but it's more efficient to implement it as:
|
// but it's more efficient to implement it as:
|
||||||
// ab = a*b
|
// ab = a*b
|
||||||
// c = ab^2
|
// c = ab^2
|
||||||
let ab = FieldChip::mul(&mut layouter, a, b)?;
|
let ab = field_chip.mul(&mut layouter, a, b)?;
|
||||||
let c = FieldChip::mul(&mut layouter, ab.clone(), ab)?;
|
let c = field_chip.mul(&mut layouter, ab.clone(), ab)?;
|
||||||
|
|
||||||
// Expose the result as a public input to the circuit.
|
// Expose the result as a public input to the circuit.
|
||||||
FieldChip::expose_public(&mut layouter, c)
|
field_chip.expose_public(&mut layouter, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ANCHOR_END: circuit
|
// ANCHOR_END: circuit
|
||||||
|
|
|
@ -0,0 +1,771 @@
|
||||||
|
extern crate halo2;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use halo2::{
|
||||||
|
arithmetic::FieldExt,
|
||||||
|
circuit::{layouter::SingleChipLayouter, Cell, Chip, Config, Layouter, Region},
|
||||||
|
dev::VerifyFailure,
|
||||||
|
plonk::{
|
||||||
|
Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Permutation, Selector,
|
||||||
|
},
|
||||||
|
poly::Rotation,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A variable representing a number.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Number<F: FieldExt> {
|
||||||
|
cell: Cell,
|
||||||
|
value: Option<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANCHOR: field-instructions
|
||||||
|
trait FieldInstructions<F: FieldExt>: AddInstructions<F> + MulInstructions<F> {
|
||||||
|
/// Variable representing a number.
|
||||||
|
type Num;
|
||||||
|
|
||||||
|
/// Loads a number into the circuit as a private input.
|
||||||
|
fn load_private(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
a: Option<F>,
|
||||||
|
) -> Result<<Self as FieldInstructions<F>>::Num, Error>;
|
||||||
|
|
||||||
|
/// Returns `d = (a + b) * c`.
|
||||||
|
fn add_and_mul(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
a: <Self as FieldInstructions<F>>::Num,
|
||||||
|
b: <Self as FieldInstructions<F>>::Num,
|
||||||
|
c: <Self as FieldInstructions<F>>::Num,
|
||||||
|
) -> Result<<Self as FieldInstructions<F>>::Num, Error>;
|
||||||
|
|
||||||
|
/// Exposes a number as a public input to the circuit.
|
||||||
|
fn expose_public(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
num: <Self as FieldInstructions<F>>::Num,
|
||||||
|
) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
// ANCHOR_END: field-instructions
|
||||||
|
|
||||||
|
// ANCHOR: add-instructions
|
||||||
|
trait AddInstructions<F: FieldExt>: Chip<F> {
|
||||||
|
/// Variable representing a number.
|
||||||
|
type Num;
|
||||||
|
|
||||||
|
/// Returns `c = a + b`.
|
||||||
|
fn add(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
a: Self::Num,
|
||||||
|
b: Self::Num,
|
||||||
|
) -> Result<Self::Num, Error>;
|
||||||
|
}
|
||||||
|
// ANCHOR_END: add-instructions
|
||||||
|
|
||||||
|
// ANCHOR: mul-instructions
|
||||||
|
trait MulInstructions<F: FieldExt>: Chip<F> {
|
||||||
|
/// Variable representing a number.
|
||||||
|
type Num;
|
||||||
|
|
||||||
|
/// Returns `c = a * b`.
|
||||||
|
fn mul(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
a: Self::Num,
|
||||||
|
b: Self::Num,
|
||||||
|
) -> Result<Self::Num, Error>;
|
||||||
|
}
|
||||||
|
// ANCHOR_END: mul-instructions
|
||||||
|
|
||||||
|
// ANCHOR: field-config
|
||||||
|
// The top-level config that provides all necessary columns and permutations
|
||||||
|
// for the other configs.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct FieldConfig {
|
||||||
|
/// For this chip, we will use two advice columns to implement our instructions.
|
||||||
|
/// These are also the columns through which we communicate with other parts of
|
||||||
|
/// the circuit.
|
||||||
|
advice: [Column<Advice>; 2],
|
||||||
|
|
||||||
|
// We need to create a permutation between our advice columns. This allows us to
|
||||||
|
// copy numbers within these columns from arbitrary rows, which we can use to load
|
||||||
|
// inputs into our instruction regions.
|
||||||
|
perm: Permutation,
|
||||||
|
|
||||||
|
// The selector for the public-input gate, which uses one of the advice columns.
|
||||||
|
s_pub: Selector,
|
||||||
|
|
||||||
|
add_config: AddConfigEnum,
|
||||||
|
mul_config: MulConfigEnum,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum FieldConfigEnum {
|
||||||
|
Empty,
|
||||||
|
Config(FieldConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config for FieldConfigEnum {
|
||||||
|
fn empty() -> Self {
|
||||||
|
Self::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ANCHOR END: field-config
|
||||||
|
|
||||||
|
// ANCHOR: add-config
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct AddConfig {
|
||||||
|
advice: [Column<Advice>; 2],
|
||||||
|
perm: Permutation,
|
||||||
|
s_add: Selector,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum AddConfigEnum {
|
||||||
|
Empty,
|
||||||
|
Config(AddConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config for AddConfigEnum {
|
||||||
|
fn empty() -> Self {
|
||||||
|
Self::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ANCHOR_END: add-config
|
||||||
|
|
||||||
|
// ANCHOR: mul-config
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct MulConfig {
|
||||||
|
advice: [Column<Advice>; 2],
|
||||||
|
perm: Permutation,
|
||||||
|
s_mul: Selector,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum MulConfigEnum {
|
||||||
|
Empty,
|
||||||
|
Config(MulConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config for MulConfigEnum {
|
||||||
|
fn empty() -> Self {
|
||||||
|
Self::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ANCHOR END: mul-config
|
||||||
|
|
||||||
|
// ANCHOR: field-chip
|
||||||
|
/// The top-level chip that will implement the `FieldInstructions`.
|
||||||
|
struct FieldChip<F: FieldExt> {
|
||||||
|
config: FieldConfigEnum,
|
||||||
|
_marker: PhantomData<F>,
|
||||||
|
}
|
||||||
|
// ANCHOR_END: field-chip
|
||||||
|
|
||||||
|
// ANCHOR: add-chip
|
||||||
|
struct AddChip<F: FieldExt> {
|
||||||
|
config: AddConfigEnum,
|
||||||
|
_marker: PhantomData<F>,
|
||||||
|
}
|
||||||
|
// ANCHOR END: add-chip
|
||||||
|
|
||||||
|
// ANCHOR: mul-chip
|
||||||
|
struct MulChip<F: FieldExt> {
|
||||||
|
config: MulConfigEnum,
|
||||||
|
_marker: PhantomData<F>,
|
||||||
|
}
|
||||||
|
// ANCHOR_END: mul-chip
|
||||||
|
|
||||||
|
// ANCHOR: add-chip-impl
|
||||||
|
impl<F: FieldExt> Chip<F> for AddChip<F> {
|
||||||
|
type Config = AddConfigEnum;
|
||||||
|
type Loaded = ();
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
config: Self::Config::empty(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct(config: Self::Config, _loaded: Self::Loaded) -> Self {
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure(
|
||||||
|
&mut self,
|
||||||
|
meta: &mut ConstraintSystem<F>,
|
||||||
|
_selectors: BTreeMap<&str, Selector>,
|
||||||
|
columns: BTreeMap<&str, Column<Any>>,
|
||||||
|
perms: BTreeMap<&str, Permutation>,
|
||||||
|
) -> Self::Config {
|
||||||
|
let advice = [
|
||||||
|
*columns.get("advice0").unwrap(),
|
||||||
|
*columns.get("advice1").unwrap(),
|
||||||
|
];
|
||||||
|
let perm = perms.get("perm").unwrap();
|
||||||
|
let s_add = meta.selector();
|
||||||
|
|
||||||
|
// Define our addition gate!
|
||||||
|
meta.create_gate("add", |meta| {
|
||||||
|
let lhs = meta.query_any(advice[0], Rotation::cur());
|
||||||
|
let rhs = meta.query_any(advice[1], Rotation::cur());
|
||||||
|
let out = meta.query_any(advice[0], Rotation::next());
|
||||||
|
let s_add = meta.query_selector(s_add, Rotation::cur());
|
||||||
|
s_add * (lhs + rhs + out * -F::one())
|
||||||
|
});
|
||||||
|
|
||||||
|
let config = AddConfigEnum::Config(AddConfig {
|
||||||
|
advice: [
|
||||||
|
Column::<Advice>::try_from(advice[0]).unwrap(),
|
||||||
|
Column::<Advice>::try_from(advice[1]).unwrap(),
|
||||||
|
],
|
||||||
|
perm: perm.clone(),
|
||||||
|
s_add,
|
||||||
|
});
|
||||||
|
self.config = config.clone();
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config(&self) -> &Self::Config {
|
||||||
|
&self.config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load(&mut self, _layouter: &mut impl Layouter<F>) -> Result<(), halo2::plonk::Error> {
|
||||||
|
// None of the instructions implemented by this chip have any fixed state.
|
||||||
|
// But if we required e.g. a lookup table, this is where we would load it.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loaded(&self) -> &Self::Loaded {
|
||||||
|
&()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ANCHOR END: add-chip-impl
|
||||||
|
|
||||||
|
// ANCHOR: add-instructions-impl
|
||||||
|
impl<F: FieldExt> AddInstructions<F> for FieldChip<F> {
|
||||||
|
type Num = Number<F>;
|
||||||
|
fn add(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
a: Self::Num,
|
||||||
|
b: Self::Num,
|
||||||
|
) -> Result<Self::Num, Error> {
|
||||||
|
let config = match self.config() {
|
||||||
|
FieldConfigEnum::Config(config) => config.add_config.clone(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let add_chip = AddChip::<F>::construct(config, ());
|
||||||
|
add_chip.add(layouter, a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FieldExt> AddInstructions<F> for AddChip<F> {
|
||||||
|
type Num = Number<F>;
|
||||||
|
|
||||||
|
fn add(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
a: Self::Num,
|
||||||
|
b: Self::Num,
|
||||||
|
) -> Result<Self::Num, Error> {
|
||||||
|
let config = match self.config() {
|
||||||
|
AddConfigEnum::Config(config) => config.clone(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut out = None;
|
||||||
|
layouter.assign_region(
|
||||||
|
|| "add",
|
||||||
|
|mut region: Region<'_, F>| {
|
||||||
|
// We only want to use a single multiplication gate in this region,
|
||||||
|
// so we enable it at region offset 0; this means it will constrain
|
||||||
|
// cells at offsets 0 and 1.
|
||||||
|
config.s_add.enable(&mut region, 0)?;
|
||||||
|
|
||||||
|
// The inputs we've been given could be located anywhere in the circuit,
|
||||||
|
// but we can only rely on relative offsets inside this region. So we
|
||||||
|
// assign new cells inside the region and constrain them to have the
|
||||||
|
// same values as the inputs.
|
||||||
|
let lhs = region.assign_advice(
|
||||||
|
|| "lhs",
|
||||||
|
config.advice[0],
|
||||||
|
0,
|
||||||
|
|| a.value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
let rhs = region.assign_advice(
|
||||||
|
|| "rhs",
|
||||||
|
config.advice[1],
|
||||||
|
0,
|
||||||
|
|| b.value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
region.constrain_equal(&config.perm, a.cell, lhs)?;
|
||||||
|
region.constrain_equal(&config.perm, b.cell, rhs)?;
|
||||||
|
|
||||||
|
// Now we can assign the multiplication result into the output position.
|
||||||
|
let value = a.value.and_then(|a| b.value.map(|b| a + b));
|
||||||
|
let cell = region.assign_advice(
|
||||||
|
|| "lhs * rhs",
|
||||||
|
config.advice[0],
|
||||||
|
1,
|
||||||
|
|| value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Finally, we return a variable representing the output,
|
||||||
|
// to be used in another part of the circuit.
|
||||||
|
out = Some(Number { cell, value });
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(out.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ANCHOR END: add-instructions-impl
|
||||||
|
|
||||||
|
// ANCHOR: mul-chip-impl
|
||||||
|
impl<F: FieldExt> Chip<F> for MulChip<F> {
|
||||||
|
type Config = MulConfigEnum;
|
||||||
|
type Loaded = ();
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
config: Self::Config::empty(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct(config: Self::Config, _loaded: Self::Loaded) -> Self {
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure(
|
||||||
|
&mut self,
|
||||||
|
meta: &mut ConstraintSystem<F>,
|
||||||
|
_selectors: BTreeMap<&str, Selector>,
|
||||||
|
columns: BTreeMap<&str, Column<Any>>,
|
||||||
|
perms: BTreeMap<&str, Permutation>,
|
||||||
|
) -> Self::Config {
|
||||||
|
let advice = [
|
||||||
|
*columns.get("advice0").unwrap(),
|
||||||
|
*columns.get("advice1").unwrap(),
|
||||||
|
];
|
||||||
|
let perm = perms.get("perm").unwrap();
|
||||||
|
let s_mul = meta.selector();
|
||||||
|
|
||||||
|
// Define our multiplication gate!
|
||||||
|
meta.create_gate("mul", |meta| {
|
||||||
|
// To implement multiplication, we need three advice cells and a selector
|
||||||
|
// cell. We arrange them like so:
|
||||||
|
//
|
||||||
|
// | a0 | a1 | s_mul |
|
||||||
|
// |-----|-----|-------|
|
||||||
|
// | lhs | rhs | s_mul |
|
||||||
|
// | out | | |
|
||||||
|
//
|
||||||
|
// Gates may refer to any relative offsets we want, but each distinct
|
||||||
|
// offset adds a cost to the proof. The most common offsets are 0 (the
|
||||||
|
// current row), 1 (the next row), and -1 (the previous row), for which
|
||||||
|
// `Rotation` has specific constructors.
|
||||||
|
let lhs = meta.query_any(advice[0], Rotation::cur());
|
||||||
|
let rhs = meta.query_any(advice[1], Rotation::cur());
|
||||||
|
let out = meta.query_any(advice[0], Rotation::next());
|
||||||
|
let s_mul = meta.query_selector(s_mul, Rotation::cur());
|
||||||
|
|
||||||
|
// The polynomial expression returned from `create_gate` will be
|
||||||
|
// constrained by the proving system to equal zero. Our expression
|
||||||
|
// has the following properties:
|
||||||
|
// - When s_mul = 0, any value is allowed in lhs, rhs, and out.
|
||||||
|
// - When s_mul != 0, this constrains lhs * rhs = out.
|
||||||
|
s_mul * (lhs * rhs + out * -F::one())
|
||||||
|
});
|
||||||
|
|
||||||
|
let config = MulConfigEnum::Config(MulConfig {
|
||||||
|
advice: [
|
||||||
|
Column::<Advice>::try_from(advice[0]).unwrap(),
|
||||||
|
Column::<Advice>::try_from(advice[1]).unwrap(),
|
||||||
|
],
|
||||||
|
perm: perm.clone(),
|
||||||
|
s_mul,
|
||||||
|
});
|
||||||
|
self.config = config.clone();
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config(&self) -> &Self::Config {
|
||||||
|
&self.config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load(&mut self, _layouter: &mut impl Layouter<F>) -> Result<(), halo2::plonk::Error> {
|
||||||
|
// None of the instructions implemented by this chip have any fixed state.
|
||||||
|
// But if we required e.g. a lookup table, this is where we would load it.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loaded(&self) -> &Self::Loaded {
|
||||||
|
&()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ANCHOR END: mul-chip-impl
|
||||||
|
|
||||||
|
// ANCHOR: mul-instructions-impl
|
||||||
|
impl<F: FieldExt> MulInstructions<F> for FieldChip<F> {
|
||||||
|
type Num = Number<F>;
|
||||||
|
fn mul(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
a: Self::Num,
|
||||||
|
b: Self::Num,
|
||||||
|
) -> Result<Self::Num, Error> {
|
||||||
|
let config = match self.config() {
|
||||||
|
FieldConfigEnum::Config(config) => config.mul_config.clone(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let mul_chip = MulChip::<F>::construct(config, ());
|
||||||
|
mul_chip.mul(layouter, a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FieldExt> MulInstructions<F> for MulChip<F> {
|
||||||
|
type Num = Number<F>;
|
||||||
|
|
||||||
|
fn mul(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
a: Self::Num,
|
||||||
|
b: Self::Num,
|
||||||
|
) -> Result<Self::Num, Error> {
|
||||||
|
let config = match self.config() {
|
||||||
|
MulConfigEnum::Config(config) => config.clone(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut out = None;
|
||||||
|
layouter.assign_region(
|
||||||
|
|| "mul",
|
||||||
|
|mut region: Region<'_, F>| {
|
||||||
|
// We only want to use a single multiplication gate in this region,
|
||||||
|
// so we enable it at region offset 0; this means it will constrain
|
||||||
|
// cells at offsets 0 and 1.
|
||||||
|
config.s_mul.enable(&mut region, 0)?;
|
||||||
|
|
||||||
|
// The inputs we've been given could be located anywhere in the circuit,
|
||||||
|
// but we can only rely on relative offsets inside this region. So we
|
||||||
|
// assign new cells inside the region and constrain them to have the
|
||||||
|
// same values as the inputs.
|
||||||
|
let lhs = region.assign_advice(
|
||||||
|
|| "lhs",
|
||||||
|
config.advice[0],
|
||||||
|
0,
|
||||||
|
|| a.value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
let rhs = region.assign_advice(
|
||||||
|
|| "rhs",
|
||||||
|
config.advice[1],
|
||||||
|
0,
|
||||||
|
|| b.value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
region.constrain_equal(&config.perm, a.cell, lhs)?;
|
||||||
|
region.constrain_equal(&config.perm, b.cell, rhs)?;
|
||||||
|
|
||||||
|
// Now we can assign the multiplication result into the output position.
|
||||||
|
let value = a.value.and_then(|a| b.value.map(|b| a * b));
|
||||||
|
let cell = region.assign_advice(
|
||||||
|
|| "lhs * rhs",
|
||||||
|
config.advice[0],
|
||||||
|
1,
|
||||||
|
|| value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Finally, we return a variable representing the output,
|
||||||
|
// to be used in another part of the circuit.
|
||||||
|
out = Some(Number { cell, value });
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(out.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ANCHOR END: mul-instructions-impl
|
||||||
|
|
||||||
|
// ANCHOR: field-chip-impl
|
||||||
|
impl<F: FieldExt> Chip<F> for FieldChip<F> {
|
||||||
|
type Config = FieldConfigEnum;
|
||||||
|
type Loaded = ();
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
config: Self::Config::empty(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct(config: Self::Config, _loaded: Self::Loaded) -> Self {
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure(
|
||||||
|
&mut self,
|
||||||
|
meta: &mut ConstraintSystem<F>,
|
||||||
|
_selectors: BTreeMap<&str, Selector>,
|
||||||
|
columns: BTreeMap<&str, Column<Any>>,
|
||||||
|
_perms: BTreeMap<&str, Permutation>,
|
||||||
|
) -> Self::Config {
|
||||||
|
let advice = [
|
||||||
|
*columns.get("advice0").unwrap(),
|
||||||
|
*columns.get("advice1").unwrap(),
|
||||||
|
];
|
||||||
|
let instance = *columns.get("instance").unwrap();
|
||||||
|
|
||||||
|
let perm = Permutation::new(
|
||||||
|
meta,
|
||||||
|
&advice
|
||||||
|
.iter()
|
||||||
|
.map(|column| (*column).into())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
let s_pub = meta.selector();
|
||||||
|
|
||||||
|
// Define our public-input gate!
|
||||||
|
meta.create_gate("public input", |meta| {
|
||||||
|
// We choose somewhat-arbitrarily that we will use the second advice
|
||||||
|
// column for exposing numbers as public inputs.
|
||||||
|
let a = meta.query_any(advice[1], Rotation::cur());
|
||||||
|
let p = meta.query_any(instance, Rotation::cur());
|
||||||
|
let s = meta.query_selector(s_pub, Rotation::cur());
|
||||||
|
|
||||||
|
// We simply constrain the advice cell to be equal to the instance cell,
|
||||||
|
// when the selector is enabled.
|
||||||
|
s * (p + a * -F::one())
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut perms = BTreeMap::new();
|
||||||
|
perms.insert("perm", perm.clone());
|
||||||
|
|
||||||
|
let mut add_chip = AddChip::new();
|
||||||
|
let add_config =
|
||||||
|
add_chip.configure(meta, BTreeMap::default(), columns.clone(), perms.clone());
|
||||||
|
|
||||||
|
let mut mul_chip = MulChip::new();
|
||||||
|
let mul_config = mul_chip.configure(meta, BTreeMap::default(), columns, perms);
|
||||||
|
|
||||||
|
let config = FieldConfigEnum::Config(FieldConfig {
|
||||||
|
advice: [
|
||||||
|
Column::<Advice>::try_from(advice[0]).unwrap(),
|
||||||
|
Column::<Advice>::try_from(advice[1]).unwrap(),
|
||||||
|
],
|
||||||
|
perm: perm,
|
||||||
|
s_pub: s_pub,
|
||||||
|
add_config: add_config,
|
||||||
|
mul_config: mul_config,
|
||||||
|
});
|
||||||
|
self.config = config.clone();
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config(&self) -> &Self::Config {
|
||||||
|
&self.config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load(&mut self, _layouter: &mut impl Layouter<F>) -> Result<(), halo2::plonk::Error> {
|
||||||
|
// None of the instructions implemented by this chip have any fixed state.
|
||||||
|
// But if we required e.g. a lookup table, this is where we would load it.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loaded(&self) -> &Self::Loaded {
|
||||||
|
&()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ANCHOR_END: field-chip-impl
|
||||||
|
|
||||||
|
// ANCHOR: field-instructions-impl
|
||||||
|
impl<F: FieldExt> FieldInstructions<F> for FieldChip<F> {
|
||||||
|
type Num = Number<F>;
|
||||||
|
|
||||||
|
fn load_private(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
value: Option<F>,
|
||||||
|
) -> Result<<Self as FieldInstructions<F>>::Num, Error> {
|
||||||
|
let config = match self.config() {
|
||||||
|
FieldConfigEnum::Config(config) => config.clone(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut num = None;
|
||||||
|
layouter.assign_region(
|
||||||
|
|| "load private",
|
||||||
|
|mut region| {
|
||||||
|
let cell = region.assign_advice(
|
||||||
|
|| "private input",
|
||||||
|
config.advice[0],
|
||||||
|
0,
|
||||||
|
|| value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
num = Some(Number { cell, value });
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(num.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `d = (a + b) * c`.
|
||||||
|
fn add_and_mul(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
a: <Self as FieldInstructions<F>>::Num,
|
||||||
|
b: <Self as FieldInstructions<F>>::Num,
|
||||||
|
c: <Self as FieldInstructions<F>>::Num,
|
||||||
|
) -> Result<<Self as FieldInstructions<F>>::Num, Error> {
|
||||||
|
let config = match self.config() {
|
||||||
|
FieldConfigEnum::Config(config) => config,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let add_chip = AddChip::<F>::construct(config.add_config.clone(), ());
|
||||||
|
let ab = add_chip.add(layouter, a, b)?;
|
||||||
|
|
||||||
|
let mul_chip = MulChip::<F>::construct(config.mul_config.clone(), ());
|
||||||
|
mul_chip.mul(layouter, ab, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expose_public(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<F>,
|
||||||
|
num: <Self as FieldInstructions<F>>::Num,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let config = match self.config() {
|
||||||
|
FieldConfigEnum::Config(config) => config.clone(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
layouter.assign_region(
|
||||||
|
|| "expose public",
|
||||||
|
|mut region: Region<'_, F>| {
|
||||||
|
// Enable the public-input gate.
|
||||||
|
config.s_pub.enable(&mut region, 0)?;
|
||||||
|
|
||||||
|
// Load the output into the correct advice column.
|
||||||
|
let out = region.assign_advice(
|
||||||
|
|| "public advice",
|
||||||
|
config.advice[1],
|
||||||
|
0,
|
||||||
|
|| num.value.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
region.constrain_equal(&config.perm, num.cell, out)?;
|
||||||
|
|
||||||
|
// We don't assign to the instance column inside the circuit;
|
||||||
|
// the mapping of public inputs to cells is provided to the prover.
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ANCHOR_END: field-instructions-impl
|
||||||
|
|
||||||
|
// ANCHOR: circuit
|
||||||
|
/// The full circuit implementation.
|
||||||
|
///
|
||||||
|
/// In this struct we store the private input variables. We use `Option<F>` because
|
||||||
|
/// they won't have any value during key generation. During proving, if any of these
|
||||||
|
/// were `None` we would get an error.
|
||||||
|
struct MyCircuit<F: FieldExt> {
|
||||||
|
a: Option<F>,
|
||||||
|
b: Option<F>,
|
||||||
|
c: Option<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||||
|
// Since we are using a single chip for everything, we can just reuse its config.
|
||||||
|
type Config = FieldConfigEnum;
|
||||||
|
|
||||||
|
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
||||||
|
// We create the two advice columns that FieldChip uses for I/O.
|
||||||
|
// We also need an instance column to store public inputs.
|
||||||
|
|
||||||
|
let mut columns: BTreeMap<&str, Column<Any>> = BTreeMap::new();
|
||||||
|
columns.insert("advice0", meta.advice_column().into());
|
||||||
|
columns.insert("advice1", meta.advice_column().into());
|
||||||
|
columns.insert("instance", meta.instance_column().into());
|
||||||
|
|
||||||
|
let mut field_chip = FieldChip::new();
|
||||||
|
field_chip.configure(meta, BTreeMap::default(), columns, BTreeMap::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn synthesize(&self, cs: &mut impl Assignment<F>, config: Self::Config) -> Result<(), Error> {
|
||||||
|
let mut layouter = SingleChipLayouter::new(cs)?;
|
||||||
|
let field_chip = FieldChip::<F>::construct(config, ());
|
||||||
|
|
||||||
|
// Load our private values into the circuit.
|
||||||
|
let a = field_chip.load_private(&mut layouter, self.a)?;
|
||||||
|
let b = field_chip.load_private(&mut layouter, self.b)?;
|
||||||
|
let c = field_chip.load_private(&mut layouter, self.c)?;
|
||||||
|
|
||||||
|
// Use `add_and_mul` to get `d = (a + b) * c`.
|
||||||
|
let d = field_chip.add_and_mul(&mut layouter, a, b, c)?;
|
||||||
|
|
||||||
|
// Expose the result as a public input to the circuit.
|
||||||
|
field_chip.expose_public(&mut layouter, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ANCHOR_END: circuit
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
use halo2::{dev::MockProver, pasta::Fp};
|
||||||
|
|
||||||
|
// ANCHOR: test-circuit
|
||||||
|
// The number of rows in our circuit cannot exceed 2^k. Since our example
|
||||||
|
// circuit is very small, we can pick a very small value here.
|
||||||
|
let k = 3;
|
||||||
|
|
||||||
|
// Prepare the private and public inputs to the circuit!
|
||||||
|
let a = Fp::rand();
|
||||||
|
let b = Fp::rand();
|
||||||
|
let c = Fp::rand();
|
||||||
|
let d = (a + b) * c;
|
||||||
|
|
||||||
|
// Instantiate the circuit with the private inputs.
|
||||||
|
let circuit = MyCircuit {
|
||||||
|
a: Some(a),
|
||||||
|
b: Some(b),
|
||||||
|
c: Some(c),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Arrange the public input. We expose the multiplication result in row 6
|
||||||
|
// of the instance column, so we position it there in our public inputs.
|
||||||
|
let mut public_inputs = vec![Fp::zero(); 1 << k];
|
||||||
|
public_inputs[7] = d;
|
||||||
|
|
||||||
|
// Given the correct public input, our circuit will verify.
|
||||||
|
let prover = MockProver::run(k, &circuit, vec![public_inputs.clone()]).unwrap();
|
||||||
|
assert_eq!(prover.verify(), Ok(()));
|
||||||
|
|
||||||
|
// If we try some other public input, the proof will fail!
|
||||||
|
public_inputs[7] += Fp::one();
|
||||||
|
let prover = MockProver::run(k, &circuit, vec![public_inputs]).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
prover.verify(),
|
||||||
|
Err(VerifyFailure::Gate {
|
||||||
|
gate_index: 0,
|
||||||
|
gate_name: "public input",
|
||||||
|
row: 7,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// ANCHOR_END: test-circuit
|
||||||
|
}
|
145
src/circuit.rs
145
src/circuit.rs
|
@ -1,41 +1,85 @@
|
||||||
//! Traits and structs for implementing circuit components.
|
//! Traits and structs for implementing circuit components.
|
||||||
|
|
||||||
use std::{fmt, marker::PhantomData};
|
use std::{collections::BTreeMap, fmt, marker::PhantomData};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arithmetic::FieldExt,
|
arithmetic::FieldExt,
|
||||||
plonk::{Advice, Any, Column, Error, Fixed, Permutation},
|
plonk::{Advice, Any, Column, ConstraintSystem, Error, Fixed, Permutation, Selector},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod layouter;
|
pub mod layouter;
|
||||||
|
|
||||||
|
/// A chip's configuration (i.e. columns, selectors, permutations).
|
||||||
|
pub trait Config: fmt::Debug + Clone {
|
||||||
|
/// An empty config
|
||||||
|
fn empty() -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A chip's loaded configuration (i.e. lookups, pre-computed fixed constants).
|
||||||
|
pub trait Loaded: fmt::Debug + Clone {
|
||||||
|
/// An empty loaded configuration
|
||||||
|
fn empty() -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Loaded for () {
|
||||||
|
fn empty() -> Self {}
|
||||||
|
}
|
||||||
|
|
||||||
/// A chip implements a set of instructions that can be used by gadgets.
|
/// 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
|
/// The chip stores state that is required at circuit synthesis time in
|
||||||
/// synthesis time should be stored in [`Chip::Config`], which can then be fetched via
|
/// [`Chip::Config`], which can be fetched via [`Chip::config`].
|
||||||
/// [`Layouter::config`].
|
///
|
||||||
pub trait Chip: Sized {
|
/// The chip also loads any fixed configuration needed at synthesis time
|
||||||
|
/// using [`Chip::load`], and stores it in [`Chip::Loaded`]. This can be
|
||||||
|
/// accessed via [`Chip::loaded`].
|
||||||
|
pub trait Chip<F: FieldExt>: Sized {
|
||||||
/// A type that holds the configuration for this chip, and any other state it may need
|
/// A type that holds the configuration for this chip, and any other state it may need
|
||||||
/// during circuit synthesis, that can be derived during [`Circuit::configure`].
|
/// during circuit synthesis, that can be derived during [`Circuit::configure`].
|
||||||
///
|
///
|
||||||
/// [`Circuit::configure`]: crate::plonk::Circuit::configure
|
/// [`Circuit::configure`]: crate::plonk::Circuit::configure
|
||||||
type Config: fmt::Debug;
|
type Config: Config;
|
||||||
|
|
||||||
/// A type that holds any general chip state that needs to be loaded at the start of
|
/// A type that holds any general chip state that needs to be loaded at the start of
|
||||||
/// [`Circuit::synthesize`]. This might simply be `()` for some chips.
|
/// [`Circuit::synthesize`]. This might simply be `()` for some chips.
|
||||||
///
|
///
|
||||||
/// [`Circuit::synthesize`]: crate::plonk::Circuit::synthesize
|
/// [`Circuit::synthesize`]: crate::plonk::Circuit::synthesize
|
||||||
type Loaded: fmt::Debug;
|
type Loaded: Loaded;
|
||||||
|
|
||||||
/// The field that the chip is defined over.
|
/// A new chip that is not yet configured or loaded.
|
||||||
///
|
fn new() -> Self;
|
||||||
/// 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.
|
/// A chip constructed from a given config and loaded state.
|
||||||
|
fn construct(config: Self::Config, loaded: Self::Loaded) -> Self;
|
||||||
|
|
||||||
|
/// The chip is responsible for mutating the constraint system to set up
|
||||||
|
/// the columns, gates, lookups and permutations it needs.
|
||||||
///
|
///
|
||||||
/// `layouter.loaded()` will panic if called inside this function.
|
/// We allow for the chip to use pre-existing columns, selectors, and
|
||||||
fn load(layouter: &mut impl Layouter<Self>) -> Result<Self::Loaded, Error>;
|
/// permutations in its configuration. Chips that are lower in a hierarchy
|
||||||
|
/// depend on other chips to pass down these objects.
|
||||||
|
fn configure(
|
||||||
|
&mut self,
|
||||||
|
meta: &mut ConstraintSystem<F>,
|
||||||
|
selectors: BTreeMap<&str, Selector>,
|
||||||
|
columns: BTreeMap<&str, Column<Any>>,
|
||||||
|
perms: BTreeMap<&str, Permutation>,
|
||||||
|
) -> Self::Config;
|
||||||
|
|
||||||
|
/// The chip holds its own configuration.
|
||||||
|
fn config(&self) -> &Self::Config;
|
||||||
|
|
||||||
|
/// Load any fixed configuration for this chip into the circuit, i.e.
|
||||||
|
/// assigns cells in fixed columns.
|
||||||
|
///
|
||||||
|
/// `self.loaded()` will panic if called inside this function.
|
||||||
|
fn load(&mut self, layouter: &mut impl Layouter<F>) -> Result<Self::Loaded, Error>;
|
||||||
|
|
||||||
|
/// Provides access to general chip state loaded at the beginning of circuit
|
||||||
|
/// synthesis.
|
||||||
|
///
|
||||||
|
/// Panics if called inside `Chip::load`.
|
||||||
|
fn loaded(&self) -> &Self::Loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Index of a region in a layouter
|
/// Index of a region in a layouter
|
||||||
|
@ -85,6 +129,22 @@ pub struct Cell {
|
||||||
column: Column<Any>,
|
column: Column<Any>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A structure containing a cell and its assigned value.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CellValue<T> {
|
||||||
|
/// The cell of this `CellValue`
|
||||||
|
pub cell: Cell,
|
||||||
|
/// The value assigned to this `CellValue`
|
||||||
|
pub value: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CellValue<T> {
|
||||||
|
/// Construct a `CellValue`.
|
||||||
|
pub fn new(cell: Cell, value: Option<T>) -> Self {
|
||||||
|
CellValue { cell, value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A region of the circuit in which a [`Chip`] can assign cells.
|
/// 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
|
/// Inside a region, the chip may freely use relative offsets; the [`Layouter`] will
|
||||||
|
@ -97,17 +157,17 @@ pub struct Cell {
|
||||||
/// "logical" columns that are guaranteed to correspond to the chip (and have come from
|
/// "logical" columns that are guaranteed to correspond to the chip (and have come from
|
||||||
/// `Chip::Config`).
|
/// `Chip::Config`).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Region<'r, C: Chip> {
|
pub struct Region<'r, F: FieldExt> {
|
||||||
region: &'r mut dyn layouter::RegionLayouter<C>,
|
region: &'r mut dyn layouter::RegionLayouter<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, C: Chip> From<&'r mut dyn layouter::RegionLayouter<C>> for Region<'r, C> {
|
impl<'r, F: FieldExt> From<&'r mut dyn layouter::RegionLayouter<F>> for Region<'r, F> {
|
||||||
fn from(region: &'r mut dyn layouter::RegionLayouter<C>) -> Self {
|
fn from(region: &'r mut dyn layouter::RegionLayouter<F>) -> Self {
|
||||||
Region { region }
|
Region { region }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, C: Chip> Region<'r, C> {
|
impl<'r, F: FieldExt> Region<'r, F> {
|
||||||
/// Assign an advice column value (witness).
|
/// Assign an advice column value (witness).
|
||||||
///
|
///
|
||||||
/// Even though `to` has `FnMut` bounds, it is guaranteed to be called at most once.
|
/// Even though `to` has `FnMut` bounds, it is guaranteed to be called at most once.
|
||||||
|
@ -119,7 +179,7 @@ impl<'r, C: Chip> Region<'r, C> {
|
||||||
mut to: V,
|
mut to: V,
|
||||||
) -> Result<Cell, Error>
|
) -> Result<Cell, Error>
|
||||||
where
|
where
|
||||||
V: FnMut() -> Result<C::Field, Error> + 'v,
|
V: FnMut() -> Result<F, Error> + 'v,
|
||||||
A: Fn() -> AR,
|
A: Fn() -> AR,
|
||||||
AR: Into<String>,
|
AR: Into<String>,
|
||||||
{
|
{
|
||||||
|
@ -138,7 +198,7 @@ impl<'r, C: Chip> Region<'r, C> {
|
||||||
mut to: V,
|
mut to: V,
|
||||||
) -> Result<Cell, Error>
|
) -> Result<Cell, Error>
|
||||||
where
|
where
|
||||||
V: FnMut() -> Result<C::Field, Error> + 'v,
|
V: FnMut() -> Result<F, Error> + 'v,
|
||||||
A: Fn() -> AR,
|
A: Fn() -> AR,
|
||||||
AR: Into<String>,
|
AR: Into<String>,
|
||||||
{
|
{
|
||||||
|
@ -159,25 +219,15 @@ impl<'r, C: Chip> Region<'r, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A layout strategy for a specific chip within a circuit.
|
/// A layout strategy within a circuit. The layouter is chip-agnostic and applies its
|
||||||
|
/// strategy to the context and config it is given.
|
||||||
///
|
///
|
||||||
/// This abstracts over the circuit assignments, handling row indices etc.
|
/// This abstracts over the circuit assignments, handling row indices etc.
|
||||||
///
|
///
|
||||||
/// A particular concrete layout strategy will implement this trait for each chip it
|
pub trait Layouter<F: FieldExt> {
|
||||||
/// supports.
|
|
||||||
pub trait Layouter<C: Chip> {
|
|
||||||
/// Represents the type of the "root" of this layouter, so that nested namespaces
|
/// Represents the type of the "root" of this layouter, so that nested namespaces
|
||||||
/// can minimize indirection.
|
/// can minimize indirection.
|
||||||
type Root: Layouter<C>;
|
type Root: Layouter<F>;
|
||||||
|
|
||||||
/// Provides access to the chip configuration.
|
|
||||||
fn config(&self) -> &C::Config;
|
|
||||||
|
|
||||||
/// Provides access to general chip state loaded at the beginning of circuit
|
|
||||||
/// synthesis.
|
|
||||||
///
|
|
||||||
/// Panics if called inside `C::load`.
|
|
||||||
fn loaded(&self) -> &C::Loaded;
|
|
||||||
|
|
||||||
/// Assign a region of gates to an absolute row number.
|
/// Assign a region of gates to an absolute row number.
|
||||||
///
|
///
|
||||||
|
@ -187,12 +237,13 @@ pub trait Layouter<C: Chip> {
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// fn assign_region(&mut self, || "region name", |region| {
|
/// fn assign_region(&mut self, || "region name", |region| {
|
||||||
/// region.assign_advice(self.config.a, offset, || { Some(value)});
|
/// let config = chip.config().clone();
|
||||||
|
/// region.assign_advice(config.a, offset, || { Some(value)});
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
fn assign_region<A, AR, N, NR>(&mut self, name: N, assignment: A) -> Result<AR, Error>
|
fn assign_region<A, AR, N, NR>(&mut self, name: N, assignment: A) -> Result<AR, Error>
|
||||||
where
|
where
|
||||||
A: FnMut(Region<'_, C>) -> Result<AR, Error>,
|
A: FnMut(Region<'_, F>) -> Result<AR, Error>,
|
||||||
N: Fn() -> NR,
|
N: Fn() -> NR,
|
||||||
NR: Into<String>;
|
NR: Into<String>;
|
||||||
|
|
||||||
|
@ -215,7 +266,7 @@ pub trait Layouter<C: Chip> {
|
||||||
fn pop_namespace(&mut self, gadget_name: Option<String>);
|
fn pop_namespace(&mut self, gadget_name: Option<String>);
|
||||||
|
|
||||||
/// Enters into a namespace.
|
/// Enters into a namespace.
|
||||||
fn namespace<NR, N>(&mut self, name_fn: N) -> NamespacedLayouter<'_, C, Self::Root>
|
fn namespace<NR, N>(&mut self, name_fn: N) -> NamespacedLayouter<'_, F, Self::Root>
|
||||||
where
|
where
|
||||||
NR: Into<String>,
|
NR: Into<String>,
|
||||||
N: FnOnce() -> NR,
|
N: FnOnce() -> NR,
|
||||||
|
@ -229,22 +280,14 @@ pub trait Layouter<C: Chip> {
|
||||||
/// This is a "namespaced" layouter which borrows a `Layouter` (pushing a namespace
|
/// This is a "namespaced" layouter which borrows a `Layouter` (pushing a namespace
|
||||||
/// context) and, when dropped, pops out of the namespace context.
|
/// context) and, when dropped, pops out of the namespace context.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NamespacedLayouter<'a, C: Chip, L: Layouter<C> + 'a>(&'a mut L, PhantomData<C>);
|
pub struct NamespacedLayouter<'a, F: FieldExt, L: Layouter<F> + 'a>(&'a mut L, PhantomData<F>);
|
||||||
|
|
||||||
impl<'a, C: Chip, L: Layouter<C> + 'a> Layouter<C> for NamespacedLayouter<'a, C, L> {
|
impl<'a, F: FieldExt, L: Layouter<F> + 'a> Layouter<F> for NamespacedLayouter<'a, F, L> {
|
||||||
type Root = L::Root;
|
type Root = L::Root;
|
||||||
|
|
||||||
fn config(&self) -> &C::Config {
|
|
||||||
self.0.config()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loaded(&self) -> &C::Loaded {
|
|
||||||
self.0.loaded()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assign_region<A, AR, N, NR>(&mut self, name: N, assignment: A) -> Result<AR, Error>
|
fn assign_region<A, AR, N, NR>(&mut self, name: N, assignment: A) -> Result<AR, Error>
|
||||||
where
|
where
|
||||||
A: FnMut(Region<'_, C>) -> Result<AR, Error>,
|
A: FnMut(Region<'_, F>) -> Result<AR, Error>,
|
||||||
N: Fn() -> NR,
|
N: Fn() -> NR,
|
||||||
NR: Into<String>,
|
NR: Into<String>,
|
||||||
{
|
{
|
||||||
|
@ -268,7 +311,7 @@ impl<'a, C: Chip, L: Layouter<C> + 'a> Layouter<C> for NamespacedLayouter<'a, C,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Chip, L: Layouter<C> + 'a> Drop for NamespacedLayouter<'a, C, L> {
|
impl<'a, F: FieldExt, L: Layouter<F> + 'a> Drop for NamespacedLayouter<'a, F, L> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let gadget_name = {
|
let gadget_name = {
|
||||||
#[cfg(feature = "gadget-traces")]
|
#[cfg(feature = "gadget-traces")]
|
||||||
|
|
|
@ -5,7 +5,8 @@ use std::collections::{HashMap, HashSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use super::{Cell, Chip, Layouter, Region, RegionIndex, RegionStart};
|
use super::{Cell, Layouter, Region, RegionIndex, RegionStart};
|
||||||
|
use crate::arithmetic::FieldExt;
|
||||||
use crate::plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation};
|
use crate::plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation};
|
||||||
|
|
||||||
/// Helper trait for implementing a custom [`Layouter`].
|
/// Helper trait for implementing a custom [`Layouter`].
|
||||||
|
@ -13,17 +14,17 @@ use crate::plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation};
|
||||||
/// This trait is used for implementing region assignments:
|
/// This trait is used for implementing region assignments:
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// impl<'a, C: Chip, CS: Assignment<C::Field> + 'a> Layouter<C> for MyLayouter<'a, C, CS> {
|
/// impl<'a, F: FieldExt, C: Chip<F>, CS: Assignment<F> + 'a> Layouter<C> for MyLayouter<'a, C, CS> {
|
||||||
/// fn assign_region(
|
/// fn assign_region(
|
||||||
/// &mut self,
|
/// &mut self,
|
||||||
/// assignment: impl FnOnce(Region<'_, C>) -> Result<(), Error>,
|
/// assignment: impl FnOnce(Region<'_, F, C>) -> Result<(), Error>,
|
||||||
/// ) -> Result<(), Error> {
|
/// ) -> Result<(), Error> {
|
||||||
/// let region_index = self.regions.len();
|
/// let region_index = self.regions.len();
|
||||||
/// self.regions.push(self.current_gate);
|
/// self.regions.push(self.current_gate);
|
||||||
///
|
///
|
||||||
/// let mut region = MyRegion::new(self, region_index);
|
/// let mut region = MyRegion::new(self, region_index);
|
||||||
/// {
|
/// {
|
||||||
/// let region: &mut dyn RegionLayouter<C> = &mut region;
|
/// let region: &mut dyn RegionLayouter<F> = &mut region;
|
||||||
/// assignment(region.into())?;
|
/// assignment(region.into())?;
|
||||||
/// }
|
/// }
|
||||||
/// self.current_gate += region.row_count;
|
/// self.current_gate += region.row_count;
|
||||||
|
@ -36,14 +37,14 @@ use crate::plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation};
|
||||||
/// TODO: It would be great if we could constrain the columns in these types to be
|
/// 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
|
/// "logical" columns that are guaranteed to correspond to the chip (and have come from
|
||||||
/// `Chip::Config`).
|
/// `Chip::Config`).
|
||||||
pub trait RegionLayouter<C: Chip>: fmt::Debug {
|
pub trait RegionLayouter<F: FieldExt>: fmt::Debug {
|
||||||
/// Assign an advice column value (witness)
|
/// Assign an advice column value (witness)
|
||||||
fn assign_advice<'v>(
|
fn assign_advice<'v>(
|
||||||
&'v mut self,
|
&'v mut self,
|
||||||
annotation: &'v (dyn Fn() -> String + 'v),
|
annotation: &'v (dyn Fn() -> String + 'v),
|
||||||
column: Column<Advice>,
|
column: Column<Advice>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
to: &'v mut (dyn FnMut() -> Result<C::Field, Error> + 'v),
|
to: &'v mut (dyn FnMut() -> Result<F, Error> + 'v),
|
||||||
) -> Result<Cell, Error>;
|
) -> Result<Cell, Error>;
|
||||||
|
|
||||||
/// Assign a fixed value
|
/// Assign a fixed value
|
||||||
|
@ -52,7 +53,7 @@ pub trait RegionLayouter<C: Chip>: fmt::Debug {
|
||||||
annotation: &'v (dyn Fn() -> String + 'v),
|
annotation: &'v (dyn Fn() -> String + 'v),
|
||||||
column: Column<Fixed>,
|
column: Column<Fixed>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
to: &'v mut (dyn FnMut() -> Result<C::Field, Error> + 'v),
|
to: &'v mut (dyn FnMut() -> Result<F, Error> + 'v),
|
||||||
) -> Result<Cell, Error>;
|
) -> Result<Cell, Error>;
|
||||||
|
|
||||||
/// Constraint two cells to have the same value.
|
/// Constraint two cells to have the same value.
|
||||||
|
@ -67,58 +68,43 @@ pub trait RegionLayouter<C: Chip>: fmt::Debug {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`Layouter`] for a single-chip circuit.
|
/// A [`Layouter`] for a single-chip circuit.
|
||||||
pub struct SingleChip<'a, C: Chip, CS: Assignment<C::Field> + 'a> {
|
pub struct SingleChipLayouter<'a, F: FieldExt, CS: Assignment<F> + 'a> {
|
||||||
cs: &'a mut CS,
|
cs: &'a mut CS,
|
||||||
config: C::Config,
|
|
||||||
loaded: Option<C::Loaded>,
|
|
||||||
/// Stores the starting row for each region.
|
/// Stores the starting row for each region.
|
||||||
regions: Vec<RegionStart>,
|
regions: Vec<RegionStart>,
|
||||||
/// Stores the first empty row for each column.
|
/// Stores the first empty row for each column.
|
||||||
columns: HashMap<Column<Any>, usize>,
|
columns: HashMap<Column<Any>, usize>,
|
||||||
_marker: PhantomData<C>,
|
marker: PhantomData<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Chip, CS: Assignment<C::Field> + 'a> fmt::Debug for SingleChip<'a, C, CS> {
|
impl<'a, F: FieldExt, CS: Assignment<F> + 'a> fmt::Debug for SingleChipLayouter<'a, F, CS> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("SingleChip")
|
f.debug_struct("SingleChipLayouter")
|
||||||
.field("config", &self.config)
|
|
||||||
.field("regions", &self.regions)
|
.field("regions", &self.regions)
|
||||||
.field("columns", &self.columns)
|
.field("columns", &self.columns)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Chip, CS: Assignment<C::Field>> SingleChip<'a, C, CS> {
|
impl<'a, F: FieldExt, CS: Assignment<F>> SingleChipLayouter<'a, F, CS> {
|
||||||
/// Creates a new single-chip layouter.
|
/// Creates a new single-chip layouter.
|
||||||
pub fn new(cs: &'a mut CS, config: C::Config) -> Result<Self, Error> {
|
pub fn new(cs: &'a mut CS) -> Result<Self, Error> {
|
||||||
let mut ret = SingleChip {
|
let ret = SingleChipLayouter {
|
||||||
cs,
|
cs,
|
||||||
config,
|
|
||||||
loaded: None,
|
|
||||||
regions: vec![],
|
regions: vec![],
|
||||||
columns: HashMap::default(),
|
columns: HashMap::default(),
|
||||||
_marker: PhantomData,
|
marker: PhantomData,
|
||||||
};
|
};
|
||||||
let loaded = C::load(&mut ret)?;
|
|
||||||
ret.loaded = Some(loaded);
|
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Chip, CS: Assignment<C::Field> + 'a> Layouter<C> for SingleChip<'a, C, CS> {
|
impl<'a, F: FieldExt, CS: Assignment<F> + 'a> Layouter<F> for SingleChipLayouter<'a, F, CS> {
|
||||||
type Root = Self;
|
type Root = Self;
|
||||||
|
|
||||||
fn config(&self) -> &C::Config {
|
|
||||||
&self.config
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loaded(&self) -> &C::Loaded {
|
|
||||||
self.loaded.as_ref().expect("We called C::load")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assign_region<A, AR, N, NR>(&mut self, name: N, mut assignment: A) -> Result<AR, Error>
|
fn assign_region<A, AR, N, NR>(&mut self, name: N, mut assignment: A) -> Result<AR, Error>
|
||||||
where
|
where
|
||||||
A: FnMut(Region<'_, C>) -> Result<AR, Error>,
|
A: FnMut(Region<'_, F>) -> Result<AR, Error>,
|
||||||
N: Fn() -> NR,
|
N: Fn() -> NR,
|
||||||
NR: Into<String>,
|
NR: Into<String>,
|
||||||
{
|
{
|
||||||
|
@ -127,7 +113,7 @@ impl<'a, C: Chip, CS: Assignment<C::Field> + 'a> Layouter<C> for SingleChip<'a,
|
||||||
// Get shape of the region.
|
// Get shape of the region.
|
||||||
let mut shape = RegionShape::new(region_index.into());
|
let mut shape = RegionShape::new(region_index.into());
|
||||||
{
|
{
|
||||||
let region: &mut dyn RegionLayouter<C> = &mut shape;
|
let region: &mut dyn RegionLayouter<F> = &mut shape;
|
||||||
assignment(region.into())?;
|
assignment(region.into())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,9 +131,9 @@ impl<'a, C: Chip, CS: Assignment<C::Field> + 'a> Layouter<C> for SingleChip<'a,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cs.enter_region(name);
|
self.cs.enter_region(name);
|
||||||
let mut region = SingleChipRegion::new(self, region_index.into());
|
let mut region = SingleChipLayouterRegion::new(self, region_index.into());
|
||||||
let result = {
|
let result = {
|
||||||
let region: &mut dyn RegionLayouter<C> = &mut region;
|
let region: &mut dyn RegionLayouter<F> = &mut region;
|
||||||
assignment(region.into())
|
assignment(region.into())
|
||||||
}?;
|
}?;
|
||||||
self.cs.exit_region();
|
self.cs.exit_region();
|
||||||
|
@ -207,13 +193,13 @@ impl RegionShape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Chip> RegionLayouter<C> for RegionShape {
|
impl<F: FieldExt> RegionLayouter<F> for RegionShape {
|
||||||
fn assign_advice<'v>(
|
fn assign_advice<'v>(
|
||||||
&'v mut self,
|
&'v mut self,
|
||||||
_: &'v (dyn Fn() -> String + 'v),
|
_: &'v (dyn Fn() -> String + 'v),
|
||||||
column: Column<Advice>,
|
column: Column<Advice>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
_to: &'v mut (dyn FnMut() -> Result<C::Field, Error> + 'v),
|
_to: &'v mut (dyn FnMut() -> Result<F, Error> + 'v),
|
||||||
) -> Result<Cell, Error> {
|
) -> Result<Cell, Error> {
|
||||||
self.columns.insert(column.into());
|
self.columns.insert(column.into());
|
||||||
self.row_count = cmp::max(self.row_count, offset + 1);
|
self.row_count = cmp::max(self.row_count, offset + 1);
|
||||||
|
@ -230,7 +216,7 @@ impl<C: Chip> RegionLayouter<C> for RegionShape {
|
||||||
_: &'v (dyn Fn() -> String + 'v),
|
_: &'v (dyn Fn() -> String + 'v),
|
||||||
column: Column<Fixed>,
|
column: Column<Fixed>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
_to: &'v mut (dyn FnMut() -> Result<C::Field, Error> + 'v),
|
_to: &'v mut (dyn FnMut() -> Result<F, Error> + 'v),
|
||||||
) -> Result<Cell, Error> {
|
) -> Result<Cell, Error> {
|
||||||
self.columns.insert(column.into());
|
self.columns.insert(column.into());
|
||||||
self.row_count = cmp::max(self.row_count, offset + 1);
|
self.row_count = cmp::max(self.row_count, offset + 1);
|
||||||
|
@ -253,40 +239,40 @@ impl<C: Chip> RegionLayouter<C> for RegionShape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SingleChipRegion<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> {
|
struct SingleChipLayouterRegion<'r, 'a, F: FieldExt, CS: Assignment<F> + 'a> {
|
||||||
layouter: &'r mut SingleChip<'a, C, CS>,
|
layouter: &'r mut SingleChipLayouter<'a, F, CS>,
|
||||||
region_index: RegionIndex,
|
region_index: RegionIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> fmt::Debug
|
impl<'r, 'a, F: FieldExt, CS: Assignment<F> + 'a> fmt::Debug
|
||||||
for SingleChipRegion<'r, 'a, C, CS>
|
for SingleChipLayouterRegion<'r, 'a, F, CS>
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("SingleChipRegion")
|
f.debug_struct("SingleChipLayouterRegion")
|
||||||
.field("layouter", &self.layouter)
|
.field("layouter", &self.layouter)
|
||||||
.field("region_index", &self.region_index)
|
.field("region_index", &self.region_index)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> SingleChipRegion<'r, 'a, C, CS> {
|
impl<'r, 'a, F: FieldExt, CS: Assignment<F> + 'a> SingleChipLayouterRegion<'r, 'a, F, CS> {
|
||||||
fn new(layouter: &'r mut SingleChip<'a, C, CS>, region_index: RegionIndex) -> Self {
|
fn new(layouter: &'r mut SingleChipLayouter<'a, F, CS>, region_index: RegionIndex) -> Self {
|
||||||
SingleChipRegion {
|
SingleChipLayouterRegion {
|
||||||
layouter,
|
layouter,
|
||||||
region_index,
|
region_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> RegionLayouter<C>
|
impl<'r, 'a, F: FieldExt, CS: Assignment<F> + 'a> RegionLayouter<F>
|
||||||
for SingleChipRegion<'r, 'a, C, CS>
|
for SingleChipLayouterRegion<'r, 'a, F, CS>
|
||||||
{
|
{
|
||||||
fn assign_advice<'v>(
|
fn assign_advice<'v>(
|
||||||
&'v mut self,
|
&'v mut self,
|
||||||
annotation: &'v (dyn Fn() -> String + 'v),
|
annotation: &'v (dyn Fn() -> String + 'v),
|
||||||
column: Column<Advice>,
|
column: Column<Advice>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
to: &'v mut (dyn FnMut() -> Result<C::Field, Error> + 'v),
|
to: &'v mut (dyn FnMut() -> Result<F, Error> + 'v),
|
||||||
) -> Result<Cell, Error> {
|
) -> Result<Cell, Error> {
|
||||||
self.layouter.cs.assign_advice(
|
self.layouter.cs.assign_advice(
|
||||||
annotation,
|
annotation,
|
||||||
|
@ -307,7 +293,7 @@ impl<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> RegionLayouter<C>
|
||||||
annotation: &'v (dyn Fn() -> String + 'v),
|
annotation: &'v (dyn Fn() -> String + 'v),
|
||||||
column: Column<Fixed>,
|
column: Column<Fixed>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
to: &'v mut (dyn FnMut() -> Result<C::Field, Error> + 'v),
|
to: &'v mut (dyn FnMut() -> Result<F, Error> + 'v),
|
||||||
) -> Result<Cell, Error> {
|
) -> Result<Cell, Error> {
|
||||||
self.layouter.cs.assign_fixed(
|
self.layouter.cs.assign_fixed(
|
||||||
annotation,
|
annotation,
|
||||||
|
|
|
@ -7,11 +7,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{lookup, permutation, Error};
|
use super::{lookup, permutation, Error};
|
||||||
use crate::{
|
use crate::{arithmetic::FieldExt, circuit::Region, poly::Rotation};
|
||||||
arithmetic::FieldExt,
|
|
||||||
circuit::{Chip, Region},
|
|
||||||
poly::Rotation,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A column type
|
/// A column type
|
||||||
pub trait ColumnType: 'static + Sized + std::fmt::Debug {}
|
pub trait ColumnType: 'static + Sized + std::fmt::Debug {}
|
||||||
|
@ -157,7 +153,7 @@ impl TryFrom<Column<Any>> for Column<Instance> {
|
||||||
/// Selectors are disabled on all rows by default, and must be explicitly enabled on each
|
/// Selectors are disabled on all rows by default, and must be explicitly enabled on each
|
||||||
/// row when required:
|
/// row when required:
|
||||||
/// ```
|
/// ```
|
||||||
/// use halo2::{circuit::{Chip, Layouter}, plonk::{Advice, Column, Error, Selector}};
|
/// use halo2::{arithmetic::FieldExt, circuit::{Chip, Layouter}, plonk::{Advice, Column, Error, Selector}};
|
||||||
/// # use ff::Field;
|
/// # use ff::Field;
|
||||||
/// # use halo2::plonk::Fixed;
|
/// # use halo2::plonk::Fixed;
|
||||||
///
|
///
|
||||||
|
@ -167,12 +163,12 @@ impl TryFrom<Column<Any>> for Column<Instance> {
|
||||||
/// s: Selector,
|
/// s: Selector,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn circuit_logic<C: Chip>(mut layouter: impl Layouter<C>) -> Result<(), Error> {
|
/// fn circuit_logic<F: FieldExt, C: Chip<F>>(chip: C, mut layouter: impl Layouter<F>) -> Result<(), Error> {
|
||||||
/// let config = layouter.config().clone();
|
/// let config = chip.config().clone();
|
||||||
/// # let config: Config = todo!();
|
/// # let config: Config = todo!();
|
||||||
/// layouter.assign_region(|| "bar", |mut region| {
|
/// layouter.assign_region(|| "bar", |mut region| {
|
||||||
/// region.assign_advice(|| "a", config.a, 0, || Ok(C::Field::one()))?;
|
/// region.assign_advice(|| "a", config.a, 0, || Ok(F::one()))?;
|
||||||
/// region.assign_advice(|| "a", config.b, 1, || Ok(C::Field::one()))?;
|
/// region.assign_advice(|| "a", config.b, 1, || Ok(F::one()))?;
|
||||||
/// config.s.enable(&mut region, 1)
|
/// config.s.enable(&mut region, 1)
|
||||||
/// })?;
|
/// })?;
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
|
@ -183,14 +179,14 @@ pub struct Selector(Column<Fixed>);
|
||||||
|
|
||||||
impl Selector {
|
impl Selector {
|
||||||
/// Enable this selector at the given offset within the given region.
|
/// Enable this selector at the given offset within the given region.
|
||||||
pub fn enable<C: Chip>(&self, region: &mut Region<C>, offset: usize) -> Result<(), Error> {
|
pub fn enable<F: FieldExt>(&self, region: &mut Region<F>, offset: usize) -> Result<(), Error> {
|
||||||
// TODO: Ensure that the default for a selector's cells is always zero, if we
|
// TODO: Ensure that the default for a selector's cells is always zero, if we
|
||||||
// alter the proving system to change the global default.
|
// alter the proving system to change the global default.
|
||||||
// TODO: Add Region::enable_selector method to allow the layouter to control the
|
// TODO: Add Region::enable_selector method to allow the layouter to control the
|
||||||
// selector's assignment.
|
// selector's assignment.
|
||||||
// https://github.com/zcash/halo2/issues/116
|
// https://github.com/zcash/halo2/issues/116
|
||||||
region
|
region
|
||||||
.assign_fixed(|| "", self.0, offset, || Ok(C::Field::one()))
|
.assign_fixed(|| "", self.0, offset, || Ok(F::one()))
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue