From 40e7bec3524d4b943d5183747c8f2714837ac9b9 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Mon, 19 Apr 2021 21:25:32 +0800 Subject: [PATCH 1/9] Move config responsiblities from Layouter to Chip trait --- examples/simple-example.rs | 179 ++++++--- examples/two-chip.rs | 771 +++++++++++++++++++++++++++++++++++++ src/circuit.rs | 145 ++++--- src/circuit/layouter.rs | 86 ++--- src/plonk/circuit.rs | 20 +- 5 files changed, 1031 insertions(+), 170 deletions(-) create mode 100644 examples/two-chip.rs diff --git a/examples/simple-example.rs b/examples/simple-example.rs index e0b39548..64aa0b98 100644 --- a/examples/simple-example.rs +++ b/examples/simple-example.rs @@ -1,53 +1,56 @@ extern crate halo2; +use std::collections::BTreeMap; +use std::convert::TryFrom; use std::marker::PhantomData; use halo2::{ arithmetic::FieldExt, - circuit::{layouter::SingleChip, Cell, Chip, Layouter}, + circuit::{layouter::SingleChipLayouter, Cell, Chip, Config, Layouter, Region}, dev::VerifyFailure, plonk::{ - Advice, Assignment, Circuit, Column, ConstraintSystem, Error, Instance, Permutation, - Selector, + Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Permutation, Selector, }, poly::Rotation, }; // ANCHOR: instructions -trait NumericInstructions: Chip { +trait NumericInstructions: Chip { /// Variable representing a number. type Num; /// Loads a number into the circuit as a private input. fn load_private( - layouter: &mut impl Layouter, - a: Option, + &self, + layouter: &mut impl Layouter, + a: Option, ) -> Result; /// Returns `c = a * b`. fn mul( - layouter: &mut impl Layouter, + &self, + layouter: &mut impl Layouter, a: Self::Num, b: Self::Num, ) -> Result; /// Exposes a number as a public input to the circuit. - fn expose_public(layouter: &mut impl Layouter, num: Self::Num) -> Result<(), Error>; + fn expose_public(&self, layouter: &mut impl Layouter, num: Self::Num) -> Result<(), Error>; } // ANCHOR_END: instructions // ANCHOR: chip -/// The chip that will implement our instructions! Chips do not store any persistent -/// state themselves, and usually only contain type markers if necessary. +/// The chip that will implement our instructions! Chips store their own +/// config, as well as type markers if necessary. struct FieldChip { + config: FieldConfigEnum, _marker: PhantomData, } // ANCHOR_END: chip // ANCHOR: chip-config -/// Chip state is stored in a separate config struct. This is generated by the chip -/// during configuration, and then handed to the `Layouter`, which makes it available -/// to the chip when it needs to implement its instructions. +/// Chip state is stored in a config struct. This is generated by the chip +/// during configuration, and then stored inside the chip. #[derive(Clone, Debug)] struct FieldConfig { /// For this chip, we will use two advice columns to implement our instructions. @@ -70,12 +73,52 @@ struct FieldConfig { s_pub: Selector, } -impl FieldChip { +#[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 Chip for FieldChip { + 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, - advice: [Column; 2], - instance: Column, - ) -> FieldConfig { + _selectors: BTreeMap<&str, Selector>, + columns: BTreeMap<&str, Column>, + _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 @@ -100,9 +143,9 @@ impl FieldChip { // 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_advice(advice[0], Rotation::cur()); - let rhs = meta.query_advice(advice[1], Rotation::cur()); - let out = meta.query_advice(advice[0], Rotation::next()); + 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 @@ -117,8 +160,8 @@ impl FieldChip { 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_advice(advice[1], Rotation::cur()); - let p = meta.query_instance(instance, Rotation::cur()); + 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, @@ -126,27 +169,30 @@ impl FieldChip { s * (p + a * -F::one()) }); - FieldConfig { - advice, - perm, - s_mul, - s_pub, - } + FieldConfigEnum::Config(FieldConfig { + advice: [ + Column::::try_from(advice[0]).unwrap(), + Column::::try_from(advice[1]).unwrap(), + ], + perm: perm, + s_mul: s_mul, + s_pub: s_pub, + }) } -} -// ANCHOR_END: chip-config -// ANCHOR: chip-impl -impl Chip for FieldChip { - type Config = FieldConfig; - type Loaded = (); - type Field = F; + fn config(&self) -> &Self::Config { + &self.config + } - fn load(_layouter: &mut impl Layouter) -> Result<(), halo2::plonk::Error> { + fn load(&mut self, _layouter: &mut impl Layouter) -> 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: chip-impl @@ -158,14 +204,19 @@ struct Number { value: Option, } -impl NumericInstructions for FieldChip { +impl NumericInstructions for FieldChip { type Num = Number; fn load_private( - layouter: &mut impl Layouter, - value: Option, + &self, + layouter: &mut impl Layouter, + value: Option, ) -> Result { - let config = layouter.config().clone(); + let config = match self.config() { + FieldConfigEnum::Config(config) => config.clone(), + _ => unreachable!(), + }; + let mut num = None; layouter.assign_region( || "load private", @@ -184,15 +235,20 @@ impl NumericInstructions for FieldChip { } fn mul( - layouter: &mut impl Layouter, + &self, + layouter: &mut impl Layouter, a: Self::Num, b: Self::Num, ) -> Result { - let config = layouter.config().clone(); + let config = match self.config() { + FieldConfigEnum::Config(config) => config.clone(), + _ => unreachable!(), + }; + let mut out = None; layouter.assign_region( || "mul", - |mut region| { + |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. @@ -236,11 +292,15 @@ impl NumericInstructions for FieldChip { Ok(out.unwrap()) } - fn expose_public(layouter: &mut impl Layouter, num: Self::Num) -> Result<(), Error> { - let config = layouter.config().clone(); + fn expose_public(&self, layouter: &mut impl Layouter, num: Self::Num) -> Result<(), Error> { + let config = match self.config() { + FieldConfigEnum::Config(config) => config.clone(), + _ => unreachable!(), + }; + layouter.assign_region( || "expose public", - |mut region| { + |mut region: Region<'_, F>| { // Enable the public-input gate. config.s_pub.enable(&mut region, 0)?; @@ -275,24 +335,29 @@ struct MyCircuit { impl Circuit for MyCircuit { // 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) -> Self::Config { // 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. - let instance = meta.instance_column(); - FieldChip::configure(meta, advice, instance) + let mut columns: BTreeMap<&str, Column> = 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, config: Self::Config) -> Result<(), Error> { - let mut layouter = SingleChip::new(cs, config)?; + let mut layouter = SingleChipLayouter::new(cs)?; + let field_chip = FieldChip::::construct(config, ()); // Load our private values into the circuit. - let a = FieldChip::load_private(&mut layouter, self.a)?; - let b = FieldChip::load_private(&mut layouter, self.b)?; + let a = field_chip.load_private(&mut layouter, self.a)?; + let b = field_chip.load_private(&mut layouter, self.b)?; // We only have access to plain multiplication. // We could implement our circuit as: @@ -303,11 +368,11 @@ impl Circuit for MyCircuit { // but it's more efficient to implement it as: // ab = a*b // c = ab^2 - let ab = FieldChip::mul(&mut layouter, a, b)?; - let c = FieldChip::mul(&mut layouter, ab.clone(), ab)?; + let ab = field_chip.mul(&mut layouter, a, b)?; + let c = field_chip.mul(&mut layouter, ab.clone(), ab)?; // 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 diff --git a/examples/two-chip.rs b/examples/two-chip.rs new file mode 100644 index 00000000..d21bf05b --- /dev/null +++ b/examples/two-chip.rs @@ -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 { + cell: Cell, + value: Option, +} + +// ANCHOR: field-instructions +trait FieldInstructions: AddInstructions + MulInstructions { + /// Variable representing a number. + type Num; + + /// Loads a number into the circuit as a private input. + fn load_private( + &self, + layouter: &mut impl Layouter, + a: Option, + ) -> Result<>::Num, Error>; + + /// Returns `d = (a + b) * c`. + fn add_and_mul( + &self, + layouter: &mut impl Layouter, + a: >::Num, + b: >::Num, + c: >::Num, + ) -> Result<>::Num, Error>; + + /// Exposes a number as a public input to the circuit. + fn expose_public( + &self, + layouter: &mut impl Layouter, + num: >::Num, + ) -> Result<(), Error>; +} +// ANCHOR_END: field-instructions + +// ANCHOR: add-instructions +trait AddInstructions: Chip { + /// Variable representing a number. + type Num; + + /// Returns `c = a + b`. + fn add( + &self, + layouter: &mut impl Layouter, + a: Self::Num, + b: Self::Num, + ) -> Result; +} +// ANCHOR_END: add-instructions + +// ANCHOR: mul-instructions +trait MulInstructions: Chip { + /// Variable representing a number. + type Num; + + /// Returns `c = a * b`. + fn mul( + &self, + layouter: &mut impl Layouter, + a: Self::Num, + b: Self::Num, + ) -> Result; +} +// 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; 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; 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; 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 { + config: FieldConfigEnum, + _marker: PhantomData, +} +// ANCHOR_END: field-chip + +// ANCHOR: add-chip +struct AddChip { + config: AddConfigEnum, + _marker: PhantomData, +} +// ANCHOR END: add-chip + +// ANCHOR: mul-chip +struct MulChip { + config: MulConfigEnum, + _marker: PhantomData, +} +// ANCHOR_END: mul-chip + +// ANCHOR: add-chip-impl +impl Chip for AddChip { + 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, + _selectors: BTreeMap<&str, Selector>, + columns: BTreeMap<&str, Column>, + 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::::try_from(advice[0]).unwrap(), + Column::::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) -> 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 AddInstructions for FieldChip { + type Num = Number; + fn add( + &self, + layouter: &mut impl Layouter, + a: Self::Num, + b: Self::Num, + ) -> Result { + let config = match self.config() { + FieldConfigEnum::Config(config) => config.add_config.clone(), + _ => unreachable!(), + }; + let add_chip = AddChip::::construct(config, ()); + add_chip.add(layouter, a, b) + } +} + +impl AddInstructions for AddChip { + type Num = Number; + + fn add( + &self, + layouter: &mut impl Layouter, + a: Self::Num, + b: Self::Num, + ) -> Result { + 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 Chip for MulChip { + 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, + _selectors: BTreeMap<&str, Selector>, + columns: BTreeMap<&str, Column>, + 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::::try_from(advice[0]).unwrap(), + Column::::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) -> 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 MulInstructions for FieldChip { + type Num = Number; + fn mul( + &self, + layouter: &mut impl Layouter, + a: Self::Num, + b: Self::Num, + ) -> Result { + let config = match self.config() { + FieldConfigEnum::Config(config) => config.mul_config.clone(), + _ => unreachable!(), + }; + let mul_chip = MulChip::::construct(config, ()); + mul_chip.mul(layouter, a, b) + } +} + +impl MulInstructions for MulChip { + type Num = Number; + + fn mul( + &self, + layouter: &mut impl Layouter, + a: Self::Num, + b: Self::Num, + ) -> Result { + 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 Chip for FieldChip { + 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, + _selectors: BTreeMap<&str, Selector>, + columns: BTreeMap<&str, Column>, + _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::>(), + ); + 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::::try_from(advice[0]).unwrap(), + Column::::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) -> 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 FieldInstructions for FieldChip { + type Num = Number; + + fn load_private( + &self, + layouter: &mut impl Layouter, + value: Option, + ) -> Result<>::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, + a: >::Num, + b: >::Num, + c: >::Num, + ) -> Result<>::Num, Error> { + let config = match self.config() { + FieldConfigEnum::Config(config) => config, + _ => unreachable!(), + }; + + let add_chip = AddChip::::construct(config.add_config.clone(), ()); + let ab = add_chip.add(layouter, a, b)?; + + let mul_chip = MulChip::::construct(config.mul_config.clone(), ()); + mul_chip.mul(layouter, ab, c) + } + + fn expose_public( + &self, + layouter: &mut impl Layouter, + num: >::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` 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 { + a: Option, + b: Option, + c: Option, +} + +impl Circuit for MyCircuit { + // Since we are using a single chip for everything, we can just reuse its config. + type Config = FieldConfigEnum; + + fn configure(meta: &mut ConstraintSystem) -> 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> = 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, config: Self::Config) -> Result<(), Error> { + let mut layouter = SingleChipLayouter::new(cs)?; + let field_chip = FieldChip::::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 +} diff --git a/src/circuit.rs b/src/circuit.rs index 50658338..56216218 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -1,41 +1,85 @@ //! Traits and structs for implementing circuit components. -use std::{fmt, marker::PhantomData}; +use std::{collections::BTreeMap, fmt, marker::PhantomData}; use crate::{ arithmetic::FieldExt, - plonk::{Advice, Any, Column, Error, Fixed, Permutation}, + plonk::{Advice, Any, Column, ConstraintSystem, Error, Fixed, Permutation, Selector}, }; 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. /// -/// 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 { +/// The chip stores state that is required at circuit synthesis time in +/// [`Chip::Config`], which can be fetched via [`Chip::config`]. +/// +/// 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: Sized { /// 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`]. /// /// [`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 /// [`Circuit::synthesize`]. This might simply be `()` for some chips. /// /// [`Circuit::synthesize`]: crate::plonk::Circuit::synthesize - type Loaded: fmt::Debug; + type Loaded: Loaded; - /// The field that the chip is defined over. - /// - /// This provides a type that the chip's configuration can reference if necessary. - type Field: FieldExt; + /// A new chip that is not yet configured or loaded. + fn new() -> Self; - /// 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. - fn load(layouter: &mut impl Layouter) -> Result; + /// We allow for the chip to use pre-existing columns, selectors, and + /// 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, + selectors: BTreeMap<&str, Selector>, + columns: BTreeMap<&str, Column>, + 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) -> Result; + + /// 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 @@ -85,6 +129,22 @@ pub struct Cell { column: Column, } +/// A structure containing a cell and its assigned value. +#[derive(Clone, Debug)] +pub struct CellValue { + /// The cell of this `CellValue` + pub cell: Cell, + /// The value assigned to this `CellValue` + pub value: Option, +} + +impl CellValue { + /// Construct a `CellValue`. + pub fn new(cell: Cell, value: Option) -> Self { + CellValue { cell, value } + } +} + /// 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 @@ -97,17 +157,17 @@ pub struct Cell { /// "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, +pub struct Region<'r, F: FieldExt> { + 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 { +impl<'r, F: FieldExt> From<&'r mut dyn layouter::RegionLayouter> for Region<'r, F> { + fn from(region: &'r mut dyn layouter::RegionLayouter) -> Self { Region { region } } } -impl<'r, C: Chip> Region<'r, C> { +impl<'r, F: FieldExt> Region<'r, F> { /// Assign an advice column value (witness). /// /// 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, ) -> Result where - V: FnMut() -> Result + 'v, + V: FnMut() -> Result + 'v, A: Fn() -> AR, AR: Into, { @@ -138,7 +198,7 @@ impl<'r, C: Chip> Region<'r, C> { mut to: V, ) -> Result where - V: FnMut() -> Result + 'v, + V: FnMut() -> Result + 'v, A: Fn() -> AR, AR: Into, { @@ -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. /// -/// A particular concrete layout strategy will implement this trait for each chip it -/// supports. -pub trait Layouter { +pub trait Layouter { /// Represents the type of the "root" of this layouter, so that nested namespaces /// can minimize indirection. - type Root: Layouter; - - /// 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; + type Root: Layouter; /// Assign a region of gates to an absolute row number. /// @@ -187,12 +237,13 @@ pub trait Layouter { /// /// ```ignore /// 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(&mut self, name: N, assignment: A) -> Result where - A: FnMut(Region<'_, C>) -> Result, + A: FnMut(Region<'_, F>) -> Result, N: Fn() -> NR, NR: Into; @@ -215,7 +266,7 @@ pub trait Layouter { fn pop_namespace(&mut self, gadget_name: Option); /// Enters into a namespace. - fn namespace(&mut self, name_fn: N) -> NamespacedLayouter<'_, C, Self::Root> + fn namespace(&mut self, name_fn: N) -> NamespacedLayouter<'_, F, Self::Root> where NR: Into, N: FnOnce() -> NR, @@ -229,22 +280,14 @@ pub trait Layouter { /// 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 + 'a>(&'a mut L, PhantomData); +pub struct NamespacedLayouter<'a, F: FieldExt, L: Layouter + 'a>(&'a mut L, PhantomData); -impl<'a, C: Chip, L: Layouter + 'a> Layouter for NamespacedLayouter<'a, C, L> { +impl<'a, F: FieldExt, L: Layouter + 'a> Layouter for NamespacedLayouter<'a, F, L> { type Root = L::Root; - fn config(&self) -> &C::Config { - self.0.config() - } - - fn loaded(&self) -> &C::Loaded { - self.0.loaded() - } - fn assign_region(&mut self, name: N, assignment: A) -> Result where - A: FnMut(Region<'_, C>) -> Result, + A: FnMut(Region<'_, F>) -> Result, N: Fn() -> NR, NR: Into, { @@ -268,7 +311,7 @@ impl<'a, C: Chip, L: Layouter + 'a> Layouter for NamespacedLayouter<'a, C, } } -impl<'a, C: Chip, L: Layouter + 'a> Drop for NamespacedLayouter<'a, C, L> { +impl<'a, F: FieldExt, L: Layouter + 'a> Drop for NamespacedLayouter<'a, F, L> { fn drop(&mut self) { let gadget_name = { #[cfg(feature = "gadget-traces")] diff --git a/src/circuit/layouter.rs b/src/circuit/layouter.rs index fc7274a3..b6a46080 100644 --- a/src/circuit/layouter.rs +++ b/src/circuit/layouter.rs @@ -5,7 +5,8 @@ use std::collections::{HashMap, HashSet}; use std::fmt; 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}; /// 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: /// /// ```ignore -/// impl<'a, C: Chip, CS: Assignment + 'a> Layouter for MyLayouter<'a, C, CS> { +/// impl<'a, F: FieldExt, C: Chip, CS: Assignment + 'a> Layouter for MyLayouter<'a, C, CS> { /// fn assign_region( /// &mut self, -/// assignment: impl FnOnce(Region<'_, C>) -> Result<(), Error>, +/// assignment: impl FnOnce(Region<'_, F, 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; +/// let region: &mut dyn RegionLayouter = &mut region; /// assignment(region.into())?; /// } /// 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 /// "logical" columns that are guaranteed to correspond to the chip (and have come from /// `Chip::Config`). -pub trait RegionLayouter: fmt::Debug { +pub trait RegionLayouter: fmt::Debug { /// Assign an advice column value (witness) fn assign_advice<'v>( &'v mut self, annotation: &'v (dyn Fn() -> String + 'v), column: Column, offset: usize, - to: &'v mut (dyn FnMut() -> Result + 'v), + to: &'v mut (dyn FnMut() -> Result + 'v), ) -> Result; /// Assign a fixed value @@ -52,7 +53,7 @@ pub trait RegionLayouter: fmt::Debug { annotation: &'v (dyn Fn() -> String + 'v), column: Column, offset: usize, - to: &'v mut (dyn FnMut() -> Result + 'v), + to: &'v mut (dyn FnMut() -> Result + 'v), ) -> Result; /// Constraint two cells to have the same value. @@ -67,58 +68,43 @@ pub trait RegionLayouter: fmt::Debug { } /// A [`Layouter`] for a single-chip circuit. -pub struct SingleChip<'a, C: Chip, CS: Assignment + 'a> { +pub struct SingleChipLayouter<'a, F: FieldExt, CS: Assignment + 'a> { cs: &'a mut CS, - config: C::Config, - loaded: Option, /// Stores the starting row for each region. regions: Vec, /// Stores the first empty row for each column. columns: HashMap, usize>, - _marker: PhantomData, + marker: PhantomData, } -impl<'a, C: Chip, CS: Assignment + 'a> fmt::Debug for SingleChip<'a, C, CS> { +impl<'a, F: FieldExt, CS: Assignment + 'a> fmt::Debug for SingleChipLayouter<'a, F, CS> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SingleChip") - .field("config", &self.config) + f.debug_struct("SingleChipLayouter") .field("regions", &self.regions) .field("columns", &self.columns) .finish() } } -impl<'a, C: Chip, CS: Assignment> SingleChip<'a, C, CS> { +impl<'a, F: FieldExt, CS: Assignment> SingleChipLayouter<'a, F, CS> { /// Creates a new single-chip layouter. - pub fn new(cs: &'a mut CS, config: C::Config) -> Result { - let mut ret = SingleChip { + pub fn new(cs: &'a mut CS) -> Result { + let ret = SingleChipLayouter { cs, - config, - loaded: None, regions: vec![], columns: HashMap::default(), - _marker: PhantomData, + marker: PhantomData, }; - let loaded = C::load(&mut ret)?; - ret.loaded = Some(loaded); Ok(ret) } } -impl<'a, C: Chip, CS: Assignment + 'a> Layouter for SingleChip<'a, C, CS> { +impl<'a, F: FieldExt, CS: Assignment + 'a> Layouter for SingleChipLayouter<'a, F, CS> { 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(&mut self, name: N, mut assignment: A) -> Result where - A: FnMut(Region<'_, C>) -> Result, + A: FnMut(Region<'_, F>) -> Result, N: Fn() -> NR, NR: Into, { @@ -127,7 +113,7 @@ impl<'a, C: Chip, CS: Assignment + 'a> Layouter for SingleChip<'a, // Get shape of the region. let mut shape = RegionShape::new(region_index.into()); { - let region: &mut dyn RegionLayouter = &mut shape; + let region: &mut dyn RegionLayouter = &mut shape; assignment(region.into())?; } @@ -145,9 +131,9 @@ impl<'a, C: Chip, CS: Assignment + 'a> Layouter for SingleChip<'a, } 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 region: &mut dyn RegionLayouter = &mut region; + let region: &mut dyn RegionLayouter = &mut region; assignment(region.into()) }?; self.cs.exit_region(); @@ -207,13 +193,13 @@ impl RegionShape { } } -impl RegionLayouter for RegionShape { +impl RegionLayouter for RegionShape { fn assign_advice<'v>( &'v mut self, _: &'v (dyn Fn() -> String + 'v), column: Column, offset: usize, - _to: &'v mut (dyn FnMut() -> Result + 'v), + _to: &'v mut (dyn FnMut() -> Result + 'v), ) -> Result { self.columns.insert(column.into()); self.row_count = cmp::max(self.row_count, offset + 1); @@ -230,7 +216,7 @@ impl RegionLayouter for RegionShape { _: &'v (dyn Fn() -> String + 'v), column: Column, offset: usize, - _to: &'v mut (dyn FnMut() -> Result + 'v), + _to: &'v mut (dyn FnMut() -> Result + 'v), ) -> Result { self.columns.insert(column.into()); self.row_count = cmp::max(self.row_count, offset + 1); @@ -253,40 +239,40 @@ impl RegionLayouter for RegionShape { } } -struct SingleChipRegion<'r, 'a, C: Chip, CS: Assignment + 'a> { - layouter: &'r mut SingleChip<'a, C, CS>, +struct SingleChipLayouterRegion<'r, 'a, F: FieldExt, CS: Assignment + 'a> { + layouter: &'r mut SingleChipLayouter<'a, F, CS>, region_index: RegionIndex, } -impl<'r, 'a, C: Chip, CS: Assignment + 'a> fmt::Debug - for SingleChipRegion<'r, 'a, C, CS> +impl<'r, 'a, F: FieldExt, CS: Assignment + 'a> fmt::Debug + for SingleChipLayouterRegion<'r, 'a, F, CS> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SingleChipRegion") + f.debug_struct("SingleChipLayouterRegion") .field("layouter", &self.layouter) .field("region_index", &self.region_index) .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: RegionIndex) -> Self { - SingleChipRegion { +impl<'r, 'a, F: FieldExt, CS: Assignment + 'a> SingleChipLayouterRegion<'r, 'a, F, CS> { + fn new(layouter: &'r mut SingleChipLayouter<'a, F, CS>, region_index: RegionIndex) -> Self { + SingleChipLayouterRegion { layouter, region_index, } } } -impl<'r, 'a, C: Chip, CS: Assignment + 'a> RegionLayouter - for SingleChipRegion<'r, 'a, C, CS> +impl<'r, 'a, F: FieldExt, CS: Assignment + 'a> RegionLayouter + for SingleChipLayouterRegion<'r, 'a, F, CS> { fn assign_advice<'v>( &'v mut self, annotation: &'v (dyn Fn() -> String + 'v), column: Column, offset: usize, - to: &'v mut (dyn FnMut() -> Result + 'v), + to: &'v mut (dyn FnMut() -> Result + 'v), ) -> Result { self.layouter.cs.assign_advice( annotation, @@ -307,7 +293,7 @@ impl<'r, 'a, C: Chip, CS: Assignment + 'a> RegionLayouter annotation: &'v (dyn Fn() -> String + 'v), column: Column, offset: usize, - to: &'v mut (dyn FnMut() -> Result + 'v), + to: &'v mut (dyn FnMut() -> Result + 'v), ) -> Result { self.layouter.cs.assign_fixed( annotation, diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index a86b0d89..2dbad5e8 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -7,11 +7,7 @@ use std::{ }; use super::{lookup, permutation, Error}; -use crate::{ - arithmetic::FieldExt, - circuit::{Chip, Region}, - poly::Rotation, -}; +use crate::{arithmetic::FieldExt, circuit::Region, poly::Rotation}; /// A column type pub trait ColumnType: 'static + Sized + std::fmt::Debug {} @@ -157,7 +153,7 @@ impl TryFrom> for Column { /// Selectors are disabled on all rows by default, and must be explicitly enabled on each /// row when required: /// ``` -/// use halo2::{circuit::{Chip, Layouter}, plonk::{Advice, Column, Error, Selector}}; +/// use halo2::{arithmetic::FieldExt, circuit::{Chip, Layouter}, plonk::{Advice, Column, Error, Selector}}; /// # use ff::Field; /// # use halo2::plonk::Fixed; /// @@ -167,12 +163,12 @@ impl TryFrom> for Column { /// s: Selector, /// } /// -/// fn circuit_logic(mut layouter: impl Layouter) -> Result<(), Error> { -/// let config = layouter.config().clone(); +/// fn circuit_logic>(chip: C, mut layouter: impl Layouter) -> Result<(), Error> { +/// let config = chip.config().clone(); /// # let config: Config = todo!(); /// layouter.assign_region(|| "bar", |mut region| { -/// region.assign_advice(|| "a", config.a, 0, || Ok(C::Field::one()))?; -/// region.assign_advice(|| "a", config.b, 1, || Ok(C::Field::one()))?; +/// region.assign_advice(|| "a", config.a, 0, || Ok(F::one()))?; +/// region.assign_advice(|| "a", config.b, 1, || Ok(F::one()))?; /// config.s.enable(&mut region, 1) /// })?; /// Ok(()) @@ -183,14 +179,14 @@ pub struct Selector(Column); impl Selector { /// Enable this selector at the given offset within the given region. - pub fn enable(&self, region: &mut Region, offset: usize) -> Result<(), Error> { + pub fn enable(&self, region: &mut Region, offset: usize) -> Result<(), Error> { // TODO: Ensure that the default for a selector's cells is always zero, if we // alter the proving system to change the global default. // TODO: Add Region::enable_selector method to allow the layouter to control the // selector's assignment. // https://github.com/zcash/halo2/issues/116 region - .assign_fixed(|| "", self.0, offset, || Ok(C::Field::one())) + .assign_fixed(|| "", self.0, offset, || Ok(F::one())) .map(|_| ()) } } From 3ad1c43fb889594ac2ebf55ccb2e21832e32e2c7 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 22 Apr 2021 06:58:44 +0800 Subject: [PATCH 2/9] Only keep config() and loaded() generic on Chip trait Co-authored-by: Jack Grigg --- examples/simple-example.rs | 120 ++++++++---------- examples/two-chip.rs | 241 +++++++++++++++---------------------- src/circuit.rs | 32 +---- src/circuit/layouter.rs | 4 +- 4 files changed, 152 insertions(+), 245 deletions(-) diff --git a/examples/simple-example.rs b/examples/simple-example.rs index 64aa0b98..25449dfa 100644 --- a/examples/simple-example.rs +++ b/examples/simple-example.rs @@ -1,7 +1,5 @@ extern crate halo2; -use std::collections::BTreeMap; -use std::convert::TryFrom; use std::marker::PhantomData; use halo2::{ @@ -9,33 +7,37 @@ use halo2::{ circuit::{layouter::SingleChipLayouter, Cell, Chip, Config, Layouter, Region}, dev::VerifyFailure, plonk::{ - Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Permutation, Selector, + Advice, Assignment, Circuit, Column, ConstraintSystem, Error, Instance, Permutation, + Selector, }, poly::Rotation, }; +/// A variable representing a number. +#[derive(Clone)] +struct Number { + cell: Cell, + value: Option, +} + // ANCHOR: instructions trait NumericInstructions: Chip { /// Variable representing a number. type Num; /// Loads a number into the circuit as a private input. - fn load_private( - &self, - layouter: &mut impl Layouter, - a: Option, - ) -> Result; + fn load_private(&self, layouter: impl Layouter, a: Option) -> Result; /// Returns `c = a * b`. fn mul( &self, - layouter: &mut impl Layouter, + layouter: impl Layouter, a: Self::Num, b: Self::Num, ) -> Result; /// Exposes a number as a public input to the circuit. - fn expose_public(&self, layouter: &mut impl Layouter, num: Self::Num) -> Result<(), Error>; + fn expose_public(&self, layouter: impl Layouter, num: Self::Num) -> Result<(), Error>; } // ANCHOR_END: instructions @@ -87,19 +89,31 @@ impl Config for FieldConfigEnum { // ANCHOR_END: chip-config -// ANCHOR: chip-impl +// ANCHOR: chip-trait-impl impl Chip for FieldChip { type Config = FieldConfigEnum; type Loaded = (); + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} +// ANCHOR_END: chip-trait-impl + +// ANCHOR: chip-impl +impl FieldChip { fn new() -> Self { Self { - config: Self::Config::empty(), + config: >::Config::empty(), _marker: PhantomData, } } - fn construct(config: Self::Config, _loaded: Self::Loaded) -> Self { + fn construct(config: >::Config, _loaded: >::Loaded) -> Self { Self { config, _marker: PhantomData, @@ -109,16 +123,9 @@ impl Chip for FieldChip { fn configure( &mut self, meta: &mut ConstraintSystem, - _selectors: BTreeMap<&str, Selector>, - columns: BTreeMap<&str, Column>, - _perms: BTreeMap<&str, Permutation>, - ) -> Self::Config { - let advice = [ - *columns.get("advice0").unwrap(), - *columns.get("advice1").unwrap(), - ]; - let instance = *columns.get("instance").unwrap(); - + advice: [Column; 2], + instance: Column, + ) -> >::Config { let perm = Permutation::new( meta, &advice @@ -143,9 +150,9 @@ impl Chip for FieldChip { // 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 lhs = meta.query_advice(advice[0], Rotation::cur()); + let rhs = meta.query_advice(advice[1], Rotation::cur()); + let out = meta.query_advice(advice[0], Rotation::next()); let s_mul = meta.query_selector(s_mul, Rotation::cur()); // The polynomial expression returned from `create_gate` will be @@ -160,8 +167,8 @@ impl Chip for FieldChip { 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 a = meta.query_advice(advice[1], Rotation::cur()); + let p = meta.query_instance(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, @@ -170,46 +177,23 @@ impl Chip for FieldChip { }); FieldConfigEnum::Config(FieldConfig { - advice: [ - Column::::try_from(advice[0]).unwrap(), - Column::::try_from(advice[1]).unwrap(), - ], - perm: perm, - s_mul: s_mul, - s_pub: s_pub, + advice, + perm, + s_mul, + s_pub, }) } - - fn config(&self) -> &Self::Config { - &self.config - } - - fn load(&mut self, _layouter: &mut impl Layouter) -> 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: chip-impl // ANCHOR: instructions-impl -/// A variable representing a number. -#[derive(Clone)] -struct Number { - cell: Cell, - value: Option, -} impl NumericInstructions for FieldChip { type Num = Number; fn load_private( &self, - layouter: &mut impl Layouter, + mut layouter: impl Layouter, value: Option, ) -> Result { let config = match self.config() { @@ -236,7 +220,7 @@ impl NumericInstructions for FieldChip { fn mul( &self, - layouter: &mut impl Layouter, + mut layouter: impl Layouter, a: Self::Num, b: Self::Num, ) -> Result { @@ -292,7 +276,7 @@ impl NumericInstructions for FieldChip { Ok(out.unwrap()) } - fn expose_public(&self, layouter: &mut impl Layouter, num: Self::Num) -> Result<(), Error> { + fn expose_public(&self, mut layouter: impl Layouter, num: Self::Num) -> Result<(), Error> { let config = match self.config() { FieldConfigEnum::Config(config) => config.clone(), _ => unreachable!(), @@ -339,16 +323,14 @@ impl Circuit for MyCircuit { fn configure(meta: &mut ConstraintSystem) -> 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 advice = [meta.advice_column(), meta.advice_column()]; - let mut columns: BTreeMap<&str, Column> = BTreeMap::new(); - columns.insert("advice0", meta.advice_column().into()); - columns.insert("advice1", meta.advice_column().into()); - columns.insert("instance", meta.instance_column().into()); + // We also need an instance column to store public inputs. + let instance = meta.instance_column(); let mut field_chip = FieldChip::new(); - field_chip.configure(meta, BTreeMap::default(), columns, BTreeMap::default()) + field_chip.configure(meta, advice, instance) } fn synthesize(&self, cs: &mut impl Assignment, config: Self::Config) -> Result<(), Error> { @@ -356,8 +338,8 @@ impl Circuit for MyCircuit { let field_chip = FieldChip::::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 a = field_chip.load_private(layouter.namespace(|| "load a"), self.a)?; + let b = field_chip.load_private(layouter.namespace(|| "load b"), self.b)?; // We only have access to plain multiplication. // We could implement our circuit as: @@ -368,11 +350,11 @@ impl Circuit for MyCircuit { // but it's more efficient to implement it as: // ab = a*b // c = ab^2 - let ab = field_chip.mul(&mut layouter, a, b)?; - let c = field_chip.mul(&mut layouter, ab.clone(), ab)?; + let ab = field_chip.mul(layouter.namespace(|| "a * b"), a, b)?; + let c = field_chip.mul(layouter.namespace(|| "ab * ab"), ab.clone(), ab)?; // Expose the result as a public input to the circuit. - field_chip.expose_public(&mut layouter, c) + field_chip.expose_public(layouter.namespace(|| "expose c"), c) } } // ANCHOR_END: circuit diff --git a/examples/two-chip.rs b/examples/two-chip.rs index d21bf05b..216fb1e5 100644 --- a/examples/two-chip.rs +++ b/examples/two-chip.rs @@ -1,7 +1,5 @@ extern crate halo2; -use std::collections::BTreeMap; -use std::convert::TryFrom; use std::marker::PhantomData; use halo2::{ @@ -9,7 +7,8 @@ use halo2::{ circuit::{layouter::SingleChipLayouter, Cell, Chip, Config, Layouter, Region}, dev::VerifyFailure, plonk::{ - Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Permutation, Selector, + Advice, Assignment, Circuit, Column, ConstraintSystem, Error, Instance, Permutation, + Selector, }, poly::Rotation, }; @@ -29,7 +28,7 @@ trait FieldInstructions: AddInstructions + MulInstructions { /// Loads a number into the circuit as a private input. fn load_private( &self, - layouter: &mut impl Layouter, + layouter: impl Layouter, a: Option, ) -> Result<>::Num, Error>; @@ -45,7 +44,7 @@ trait FieldInstructions: AddInstructions + MulInstructions { /// Exposes a number as a public input to the circuit. fn expose_public( &self, - layouter: &mut impl Layouter, + layouter: impl Layouter, num: >::Num, ) -> Result<(), Error>; } @@ -59,7 +58,7 @@ trait AddInstructions: Chip { /// Returns `c = a + b`. fn add( &self, - layouter: &mut impl Layouter, + layouter: impl Layouter, a: Self::Num, b: Self::Num, ) -> Result; @@ -74,7 +73,7 @@ trait MulInstructions: Chip { /// Returns `c = a * b`. fn mul( &self, - layouter: &mut impl Layouter, + layouter: impl Layouter, a: Self::Num, b: Self::Num, ) -> Result; @@ -180,19 +179,31 @@ struct MulChip { } // ANCHOR_END: mul-chip -// ANCHOR: add-chip-impl +// ANCHOR: add-chip-trait-impl impl Chip for AddChip { type Config = AddConfigEnum; type Loaded = (); + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} +// ANCHOR END: add-chip-trait-impl + +// ANCHOR: add-chip-impl +impl AddChip { fn new() -> Self { Self { - config: Self::Config::empty(), + config: >::Config::empty(), _marker: PhantomData, } } - fn construct(config: Self::Config, _loaded: Self::Loaded) -> Self { + fn construct(config: >::Config, _loaded: >::Loaded) -> Self { Self { config, _marker: PhantomData, @@ -202,51 +213,28 @@ impl Chip for AddChip { fn configure( &mut self, meta: &mut ConstraintSystem, - _selectors: BTreeMap<&str, Selector>, - columns: BTreeMap<&str, Column>, - perms: BTreeMap<&str, Permutation>, - ) -> Self::Config { - let advice = [ - *columns.get("advice0").unwrap(), - *columns.get("advice1").unwrap(), - ]; - let perm = perms.get("perm").unwrap(); + advice: [Column; 2], + perm: Permutation, + ) -> >::Config { 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 lhs = meta.query_advice(advice[0], Rotation::cur()); + let rhs = meta.query_advice(advice[1], Rotation::cur()); + let out = meta.query_advice(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::::try_from(advice[0]).unwrap(), - Column::::try_from(advice[1]).unwrap(), - ], - perm: perm.clone(), + advice, + perm, s_add, }); self.config = config.clone(); config } - - fn config(&self) -> &Self::Config { - &self.config - } - - fn load(&mut self, _layouter: &mut impl Layouter) -> 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 @@ -255,7 +243,7 @@ impl AddInstructions for FieldChip { type Num = Number; fn add( &self, - layouter: &mut impl Layouter, + layouter: impl Layouter, a: Self::Num, b: Self::Num, ) -> Result { @@ -273,7 +261,7 @@ impl AddInstructions for AddChip { fn add( &self, - layouter: &mut impl Layouter, + mut layouter: impl Layouter, a: Self::Num, b: Self::Num, ) -> Result { @@ -331,19 +319,31 @@ impl AddInstructions for AddChip { } // ANCHOR END: add-instructions-impl -// ANCHOR: mul-chip-impl +// ANCHOR: mul-chip-trait-impl impl Chip for MulChip { type Config = MulConfigEnum; type Loaded = (); + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} +// ANCHOR END: mul-chip-trait-impl + +// ANCHOR: mul-chip-impl +impl MulChip { fn new() -> Self { Self { - config: Self::Config::empty(), + config: >::Config::empty(), _marker: PhantomData, } } - fn construct(config: Self::Config, _loaded: Self::Loaded) -> Self { + fn construct(config: >::Config, _loaded: >::Loaded) -> Self { Self { config, _marker: PhantomData, @@ -353,15 +353,9 @@ impl Chip for MulChip { fn configure( &mut self, meta: &mut ConstraintSystem, - _selectors: BTreeMap<&str, Selector>, - columns: BTreeMap<&str, Column>, - perms: BTreeMap<&str, Permutation>, - ) -> Self::Config { - let advice = [ - *columns.get("advice0").unwrap(), - *columns.get("advice1").unwrap(), - ]; - let perm = perms.get("perm").unwrap(); + advice: [Column; 2], + perm: Permutation, + ) -> >::Config { let s_mul = meta.selector(); // Define our multiplication gate! @@ -378,9 +372,9 @@ impl Chip for MulChip { // 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 lhs = meta.query_advice(advice[0], Rotation::cur()); + let rhs = meta.query_advice(advice[1], Rotation::cur()); + let out = meta.query_advice(advice[0], Rotation::next()); let s_mul = meta.query_selector(s_mul, Rotation::cur()); // The polynomial expression returned from `create_gate` will be @@ -392,39 +386,22 @@ impl Chip for MulChip { }); let config = MulConfigEnum::Config(MulConfig { - advice: [ - Column::::try_from(advice[0]).unwrap(), - Column::::try_from(advice[1]).unwrap(), - ], - perm: perm.clone(), + advice, + perm, s_mul, }); self.config = config.clone(); config } - - fn config(&self) -> &Self::Config { - &self.config - } - - fn load(&mut self, _layouter: &mut impl Layouter) -> 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_END: mul-chip-impl // ANCHOR: mul-instructions-impl impl MulInstructions for FieldChip { type Num = Number; fn mul( &self, - layouter: &mut impl Layouter, + layouter: impl Layouter, a: Self::Num, b: Self::Num, ) -> Result { @@ -442,7 +419,7 @@ impl MulInstructions for MulChip { fn mul( &self, - layouter: &mut impl Layouter, + mut layouter: impl Layouter, a: Self::Num, b: Self::Num, ) -> Result { @@ -500,19 +477,31 @@ impl MulInstructions for MulChip { } // ANCHOR END: mul-instructions-impl -// ANCHOR: field-chip-impl +// ANCHOR: field-chip-trait-impl impl Chip for FieldChip { type Config = FieldConfigEnum; type Loaded = (); + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} +// ANCHOR_END: field-chip-trait-impl + +// ANCHOR: field-chip-impl +impl FieldChip { fn new() -> Self { Self { - config: Self::Config::empty(), + config: >::Config::empty(), _marker: PhantomData, } } - fn construct(config: Self::Config, _loaded: Self::Loaded) -> Self { + fn construct(config: >::Config, _loaded: >::Loaded) -> Self { Self { config, _marker: PhantomData, @@ -522,16 +511,9 @@ impl Chip for FieldChip { fn configure( &mut self, meta: &mut ConstraintSystem, - _selectors: BTreeMap<&str, Selector>, - columns: BTreeMap<&str, Column>, - _perms: BTreeMap<&str, Permutation>, - ) -> Self::Config { - let advice = [ - *columns.get("advice0").unwrap(), - *columns.get("advice1").unwrap(), - ]; - let instance = *columns.get("instance").unwrap(); - + advice: [Column; 2], + instance: Column, + ) -> >::Config { let perm = Permutation::new( meta, &advice @@ -545,8 +527,8 @@ impl Chip for FieldChip { 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 a = meta.query_advice(advice[1], Rotation::cur()); + let p = meta.query_instance(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, @@ -554,43 +536,22 @@ impl Chip for FieldChip { 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 add_config = add_chip.configure(meta, advice.clone(), perm.clone()); let mut mul_chip = MulChip::new(); - let mul_config = mul_chip.configure(meta, BTreeMap::default(), columns, perms); + let mul_config = mul_chip.configure(meta, advice.clone(), perm.clone()); let config = FieldConfigEnum::Config(FieldConfig { - advice: [ - Column::::try_from(advice[0]).unwrap(), - Column::::try_from(advice[1]).unwrap(), - ], - perm: perm, - s_pub: s_pub, - add_config: add_config, - mul_config: mul_config, + advice, + perm, + s_pub, + add_config, + mul_config, }); self.config = config.clone(); config } - - fn config(&self) -> &Self::Config { - &self.config - } - - fn load(&mut self, _layouter: &mut impl Layouter) -> 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 @@ -600,7 +561,7 @@ impl FieldInstructions for FieldChip { fn load_private( &self, - layouter: &mut impl Layouter, + mut layouter: impl Layouter, value: Option, ) -> Result<>::Num, Error> { let config = match self.config() { @@ -633,21 +594,13 @@ impl FieldInstructions for FieldChip { b: >::Num, c: >::Num, ) -> Result<>::Num, Error> { - let config = match self.config() { - FieldConfigEnum::Config(config) => config, - _ => unreachable!(), - }; - - let add_chip = AddChip::::construct(config.add_config.clone(), ()); - let ab = add_chip.add(layouter, a, b)?; - - let mul_chip = MulChip::::construct(config.mul_config.clone(), ()); - mul_chip.mul(layouter, ab, c) + let ab = self.add(layouter.namespace(|| "a + b"), a, b)?; + self.mul(layouter.namespace(|| "(a + b) * c"), ab, c) } fn expose_public( &self, - layouter: &mut impl Layouter, + mut layouter: impl Layouter, num: >::Num, ) -> Result<(), Error> { let config = match self.config() { @@ -697,15 +650,13 @@ impl Circuit for MyCircuit { fn configure(meta: &mut ConstraintSystem) -> 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 advice = [meta.advice_column(), meta.advice_column()]; - let mut columns: BTreeMap<&str, Column> = BTreeMap::new(); - columns.insert("advice0", meta.advice_column().into()); - columns.insert("advice1", meta.advice_column().into()); - columns.insert("instance", meta.instance_column().into()); + // We also need an instance column to store public inputs. + let instance = meta.instance_column(); let mut field_chip = FieldChip::new(); - field_chip.configure(meta, BTreeMap::default(), columns, BTreeMap::default()) + field_chip.configure(meta, advice, instance) } fn synthesize(&self, cs: &mut impl Assignment, config: Self::Config) -> Result<(), Error> { @@ -713,15 +664,15 @@ impl Circuit for MyCircuit { let field_chip = FieldChip::::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)?; + let a = field_chip.load_private(layouter.namespace(|| "load a"), self.a)?; + let b = field_chip.load_private(layouter.namespace(|| "load b"), self.b)?; + let c = field_chip.load_private(layouter.namespace(|| "load c"), 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) + field_chip.expose_public(layouter.namespace(|| "expose d"), d) } } // ANCHOR_END: circuit diff --git a/src/circuit.rs b/src/circuit.rs index 56216218..b3d02d5a 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -1,10 +1,10 @@ //! Traits and structs for implementing circuit components. -use std::{collections::BTreeMap, fmt, marker::PhantomData}; +use std::{fmt, marker::PhantomData}; use crate::{ arithmetic::FieldExt, - plonk::{Advice, Any, Column, ConstraintSystem, Error, Fixed, Permutation, Selector}, + plonk::{Advice, Any, Column, Error, Fixed, Permutation}, }; pub mod layouter; @@ -46,39 +46,13 @@ pub trait Chip: Sized { /// [`Circuit::synthesize`]: crate::plonk::Circuit::synthesize type Loaded: Loaded; - /// A new chip that is not yet configured or loaded. - fn new() -> Self; - - /// 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. - /// - /// We allow for the chip to use pre-existing columns, selectors, and - /// 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, - selectors: BTreeMap<&str, Selector>, - columns: BTreeMap<&str, Column>, - 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) -> Result; - /// Provides access to general chip state loaded at the beginning of circuit /// synthesis. /// - /// Panics if called inside `Chip::load`. + /// Panics if called before `Chip::load`. fn loaded(&self) -> &Self::Loaded; } diff --git a/src/circuit/layouter.rs b/src/circuit/layouter.rs index b6a46080..fc414805 100644 --- a/src/circuit/layouter.rs +++ b/src/circuit/layouter.rs @@ -74,7 +74,7 @@ pub struct SingleChipLayouter<'a, F: FieldExt, CS: Assignment + 'a> { regions: Vec, /// Stores the first empty row for each column. columns: HashMap, usize>, - marker: PhantomData, + _marker: PhantomData, } impl<'a, F: FieldExt, CS: Assignment + 'a> fmt::Debug for SingleChipLayouter<'a, F, CS> { @@ -93,7 +93,7 @@ impl<'a, F: FieldExt, CS: Assignment> SingleChipLayouter<'a, F, CS> { cs, regions: vec![], columns: HashMap::default(), - marker: PhantomData, + _marker: PhantomData, }; Ok(ret) } From 03da010d6ac457fc490044ef20ea8bc8a50fcb17 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 23 Apr 2021 16:50:29 +0800 Subject: [PATCH 3/9] [tmp] Comment out sha256 example --- examples/sha256/main.rs | 274 ++++++++++++++++++++-------------------- 1 file changed, 137 insertions(+), 137 deletions(-) diff --git a/examples/sha256/main.rs b/examples/sha256/main.rs index a6ffb66d..dc156e51 100644 --- a/examples/sha256/main.rs +++ b/examples/sha256/main.rs @@ -1,162 +1,162 @@ -//! Gadget and chips for the [SHA-256] hash function. -//! -//! [SHA-256]: https://tools.ietf.org/html/rfc6234 +// //! Gadget and chips for the [SHA-256] hash function. +// //! +// //! [SHA-256]: https://tools.ietf.org/html/rfc6234 -use std::cmp::min; -use std::convert::TryInto; -use std::fmt; +// use std::cmp::min; +// use std::convert::TryInto; +// use std::fmt; -use halo2::{ - circuit::{Chip, Layouter}, - plonk::Error, -}; +// use halo2::{ +// circuit::{Chip, Layouter}, +// plonk::Error, +// }; -mod benches; -mod table16; +// mod benches; +// mod table16; -pub use table16::{BlockWord, Table16Chip, Table16Config}; +// pub use table16::{BlockWord, Table16Chip, Table16Config}; -/// The size of a SHA-256 block, in 32-bit words. -pub const BLOCK_SIZE: usize = 16; -/// The size of a SHA-256 digest, in 32-bit words. -const DIGEST_SIZE: usize = 8; +// /// The size of a SHA-256 block, in 32-bit words. +// pub const BLOCK_SIZE: usize = 16; +// /// The size of a SHA-256 digest, in 32-bit words. +// const DIGEST_SIZE: usize = 8; -/// The set of circuit instructions required to use the [`Sha256`] gadget. -pub trait Sha256Instructions: Chip { - /// Variable representing the SHA-256 internal state. - type State: Clone + fmt::Debug; - /// Variable representing a 32-bit word of the input block to the SHA-256 compression - /// function. - type BlockWord: Copy + fmt::Debug; +// /// The set of circuit instructions required to use the [`Sha256`] gadget. +// pub trait Sha256Instructions: Chip { +// /// Variable representing the SHA-256 internal state. +// type State: Clone + fmt::Debug; +// /// Variable representing a 32-bit word of the input block to the SHA-256 compression +// /// function. +// type BlockWord: Copy + fmt::Debug; - /// The zero BlockWord - fn zero() -> Self::BlockWord; +// /// The zero BlockWord +// fn zero() -> Self::BlockWord; - /// Places the SHA-256 IV in the circuit, returning the initial state variable. - fn initialization_vector(layouter: &mut impl Layouter) -> Result; +// /// Places the SHA-256 IV in the circuit, returning the initial state variable. +// fn initialization_vector(layouter: &mut impl Layouter) -> Result; - /// Creates an initial state from the output state of a previous block - fn initialization( - layouter: &mut impl Layouter, - init_state: &Self::State, - ) -> Result; +// /// Creates an initial state from the output state of a previous block +// fn initialization( +// layouter: &mut impl Layouter, +// init_state: &Self::State, +// ) -> Result; - /// Starting from the given initialized state, processes a block of input and returns the - /// final state. - fn compress( - layouter: &mut impl Layouter, - initialized_state: &Self::State, - input: [Self::BlockWord; BLOCK_SIZE], - ) -> Result; +// /// Starting from the given initialized state, processes a block of input and returns the +// /// final state. +// fn compress( +// layouter: &mut impl Layouter, +// initialized_state: &Self::State, +// input: [Self::BlockWord; BLOCK_SIZE], +// ) -> Result; - /// Converts the given state into a message digest. - fn digest( - layouter: &mut impl Layouter, - state: &Self::State, - ) -> Result<[Self::BlockWord; DIGEST_SIZE], Error>; -} +// /// Converts the given state into a message digest. +// fn digest( +// layouter: &mut impl Layouter, +// state: &Self::State, +// ) -> Result<[Self::BlockWord; DIGEST_SIZE], Error>; +// } -/// The output of a SHA-256 circuit invocation. -#[derive(Debug)] -pub struct Sha256Digest([BlockWord; DIGEST_SIZE]); +// /// The output of a SHA-256 circuit invocation. +// #[derive(Debug)] +// pub struct Sha256Digest([BlockWord; DIGEST_SIZE]); -/// A gadget that constrains a SHA-256 invocation. It supports input at a granularity of -/// 32 bits. -#[derive(Debug)] -pub struct Sha256 { - state: CS::State, - cur_block: Vec, - length: usize, -} +// /// A gadget that constrains a SHA-256 invocation. It supports input at a granularity of +// /// 32 bits. +// #[derive(Debug)] +// pub struct Sha256 { +// state: CS::State, +// cur_block: Vec, +// length: usize, +// } -impl Sha256 { - /// Create a new hasher instance. - pub fn new(mut layouter: impl Layouter) -> Result { - Ok(Sha256 { - state: Sha256Chip::initialization_vector(&mut layouter)?, - cur_block: Vec::with_capacity(BLOCK_SIZE), - length: 0, - }) - } +// impl Sha256 { +// /// Create a new hasher instance. +// pub fn new(mut layouter: impl Layouter) -> Result { +// Ok(Sha256 { +// state: Sha256Chip::initialization_vector(&mut layouter)?, +// cur_block: Vec::with_capacity(BLOCK_SIZE), +// length: 0, +// }) +// } - /// Digest data, updating the internal state. - pub fn update( - &mut self, - mut layouter: impl Layouter, - mut data: &[Sha256Chip::BlockWord], - ) -> Result<(), Error> { - self.length += data.len() * 32; +// /// Digest data, updating the internal state. +// pub fn update( +// &mut self, +// mut layouter: impl Layouter, +// mut data: &[Sha256Chip::BlockWord], +// ) -> Result<(), Error> { +// self.length += data.len() * 32; - // Fill the current block, if possible. - let remaining = BLOCK_SIZE - self.cur_block.len(); - let (l, r) = data.split_at(min(remaining, data.len())); - self.cur_block.extend_from_slice(l); - data = r; +// // Fill the current block, if possible. +// let remaining = BLOCK_SIZE - self.cur_block.len(); +// let (l, r) = data.split_at(min(remaining, data.len())); +// self.cur_block.extend_from_slice(l); +// data = r; - // If we still don't have a full block, we are done. - if self.cur_block.len() < BLOCK_SIZE { - return Ok(()); - } +// // If we still don't have a full block, we are done. +// if self.cur_block.len() < BLOCK_SIZE { +// return Ok(()); +// } - // Process the now-full current block. - self.state = Sha256Chip::compress( - &mut layouter, - &self.state, - self.cur_block[..] - .try_into() - .expect("cur_block.len() == BLOCK_SIZE"), - )?; - self.cur_block.clear(); +// // Process the now-full current block. +// self.state = Sha256Chip::compress( +// &mut layouter, +// &self.state, +// self.cur_block[..] +// .try_into() +// .expect("cur_block.len() == BLOCK_SIZE"), +// )?; +// self.cur_block.clear(); - // Process any additional full blocks. - let mut chunks_iter = data.chunks_exact(BLOCK_SIZE); - for chunk in &mut chunks_iter { - self.state = Sha256Chip::initialization(&mut layouter, &self.state)?; - self.state = Sha256Chip::compress( - &mut layouter, - &self.state, - chunk.try_into().expect("chunk.len() == BLOCK_SIZE"), - )?; - } +// // Process any additional full blocks. +// let mut chunks_iter = data.chunks_exact(BLOCK_SIZE); +// for chunk in &mut chunks_iter { +// self.state = Sha256Chip::initialization(&mut layouter, &self.state)?; +// self.state = Sha256Chip::compress( +// &mut layouter, +// &self.state, +// chunk.try_into().expect("chunk.len() == BLOCK_SIZE"), +// )?; +// } - // Cache the remaining partial block, if any. - let rem = chunks_iter.remainder(); - self.cur_block.extend_from_slice(rem); +// // Cache the remaining partial block, if any. +// let rem = chunks_iter.remainder(); +// self.cur_block.extend_from_slice(rem); - Ok(()) - } +// Ok(()) +// } - /// Retrieve result and consume hasher instance. - pub fn finalize( - mut self, - mut layouter: impl Layouter, - ) -> Result, Error> { - // Pad the remaining block - if !self.cur_block.is_empty() { - let padding = vec![Sha256Chip::zero(); BLOCK_SIZE - self.cur_block.len()]; - self.cur_block.extend_from_slice(&padding); - self.state = Sha256Chip::initialization(&mut layouter, &self.state)?; - self.state = Sha256Chip::compress( - &mut layouter, - &self.state, - self.cur_block[..] - .try_into() - .expect("cur_block.len() == BLOCK_SIZE"), - )?; - } - Sha256Chip::digest(&mut layouter, &self.state).map(Sha256Digest) - } +// /// Retrieve result and consume hasher instance. +// pub fn finalize( +// mut self, +// mut layouter: impl Layouter, +// ) -> Result, Error> { +// // Pad the remaining block +// if !self.cur_block.is_empty() { +// let padding = vec![Sha256Chip::zero(); BLOCK_SIZE - self.cur_block.len()]; +// self.cur_block.extend_from_slice(&padding); +// self.state = Sha256Chip::initialization(&mut layouter, &self.state)?; +// self.state = Sha256Chip::compress( +// &mut layouter, +// &self.state, +// self.cur_block[..] +// .try_into() +// .expect("cur_block.len() == BLOCK_SIZE"), +// )?; +// } +// Sha256Chip::digest(&mut layouter, &self.state).map(Sha256Digest) +// } - /// Convenience function to compute hash of the data. It will handle hasher creation, - /// data feeding and finalization. - pub fn digest( - mut layouter: impl Layouter, - data: &[Sha256Chip::BlockWord], - ) -> Result, Error> { - let mut hasher = Self::new(layouter.namespace(|| "init"))?; - hasher.update(layouter.namespace(|| "update"), data)?; - hasher.finalize(layouter.namespace(|| "finalize")) - } -} +// /// Convenience function to compute hash of the data. It will handle hasher creation, +// /// data feeding and finalization. +// pub fn digest( +// mut layouter: impl Layouter, +// data: &[Sha256Chip::BlockWord], +// ) -> Result, Error> { +// let mut hasher = Self::new(layouter.namespace(|| "init"))?; +// hasher.update(layouter.namespace(|| "update"), data)?; +// hasher.finalize(layouter.namespace(|| "finalize")) +// } +// } fn main() {} From 7524c95547d45a87b0eb33bbbf44e08c37bcc21b Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Mon, 26 Apr 2021 08:53:43 +0800 Subject: [PATCH 4/9] Clippy fixes --- examples/two-chip.rs | 5 +++-- src/circuit.rs | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/two-chip.rs b/examples/two-chip.rs index 216fb1e5..47fda53e 100644 --- a/examples/two-chip.rs +++ b/examples/two-chip.rs @@ -537,10 +537,10 @@ impl FieldChip { }); let mut add_chip = AddChip::new(); - let add_config = add_chip.configure(meta, advice.clone(), perm.clone()); + let add_config = add_chip.configure(meta, advice, perm.clone()); let mut mul_chip = MulChip::new(); - let mul_config = mul_chip.configure(meta, advice.clone(), perm.clone()); + let mul_config = mul_chip.configure(meta, advice, perm.clone()); let config = FieldConfigEnum::Config(FieldConfig { advice, @@ -677,6 +677,7 @@ impl Circuit for MyCircuit { } // ANCHOR_END: circuit +#[allow(clippy::many_single_char_names)] fn main() { use halo2::{dev::MockProver, pasta::Fp}; diff --git a/src/circuit.rs b/src/circuit.rs index b3d02d5a..4c8f8284 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -31,8 +31,8 @@ impl Loaded for () { /// [`Chip::Config`], which can be fetched via [`Chip::config`]. /// /// 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`]. +/// using its own implementation of `load`, and stores it in [`Chip::Loaded`]. +/// This can be accessed via [`Chip::loaded`]. pub trait Chip: Sized { /// 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`]. @@ -211,7 +211,7 @@ pub trait Layouter { /// /// ```ignore /// fn assign_region(&mut self, || "region name", |region| { - /// let config = chip.config().clone(); + /// let config = chip.config(); /// region.assign_advice(config.a, offset, || { Some(value)}); /// }); /// ``` From 21aaab20f6f5bb23152cc869b23a1ec30a6f8355 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 28 Apr 2021 10:47:26 +0800 Subject: [PATCH 5/9] Remove empty() methods from Config and Loaded traits Co-authored-by: Jack Grigg --- examples/simple-example.rs | 150 +++++++++++++++---------------------- examples/two-chip.rs | 141 +++++++--------------------------- src/circuit.rs | 20 +---- 3 files changed, 88 insertions(+), 223 deletions(-) diff --git a/examples/simple-example.rs b/examples/simple-example.rs index 25449dfa..a2720d68 100644 --- a/examples/simple-example.rs +++ b/examples/simple-example.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; use halo2::{ arithmetic::FieldExt, - circuit::{layouter::SingleChipLayouter, Cell, Chip, Config, Layouter, Region}, + circuit::{layouter::SingleChipLayouter, Cell, Chip, Layouter, Region}, dev::VerifyFailure, plonk::{ Advice, Assignment, Circuit, Column, ConstraintSystem, Error, Instance, Permutation, @@ -13,13 +13,6 @@ use halo2::{ poly::Rotation, }; -/// A variable representing a number. -#[derive(Clone)] -struct Number { - cell: Cell, - value: Option, -} - // ANCHOR: instructions trait NumericInstructions: Chip { /// Variable representing a number. @@ -45,75 +38,14 @@ trait NumericInstructions: Chip { /// The chip that will implement our instructions! Chips store their own /// config, as well as type markers if necessary. struct FieldChip { - config: FieldConfigEnum, + config: FieldConfig, _marker: PhantomData, } // ANCHOR_END: chip -// ANCHOR: chip-config -/// Chip state is stored in a config struct. This is generated by the chip -/// during configuration, and then stored inside the chip. -#[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; 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, - - // We need a selector to enable the multiplication gate, so that we aren't placing - // any constraints on cells where `NumericInstructions::mul` is not being used. - // This is important when building larger circuits, where columns are used by - // multiple sets of instructions. - s_mul: Selector, - - // The selector for the public-input gate, which uses one of the advice columns. - s_pub: Selector, -} - -#[derive(Clone, Debug)] -enum FieldConfigEnum { - Empty, - Config(FieldConfig), -} - -impl Config for FieldConfigEnum { - fn empty() -> Self { - Self::Empty - } -} - -// ANCHOR_END: chip-config - -// ANCHOR: chip-trait-impl -impl Chip for FieldChip { - type Config = FieldConfigEnum; - type Loaded = (); - - fn config(&self) -> &Self::Config { - &self.config - } - - fn loaded(&self) -> &Self::Loaded { - &() - } -} -// ANCHOR_END: chip-trait-impl - // ANCHOR: chip-impl impl FieldChip { - fn new() -> Self { - Self { - config: >::Config::empty(), - _marker: PhantomData, - } - } - - fn construct(config: >::Config, _loaded: >::Loaded) -> Self { + fn construct(config: >::Config) -> Self { Self { config, _marker: PhantomData, @@ -121,7 +53,6 @@ impl FieldChip { } fn configure( - &mut self, meta: &mut ConstraintSystem, advice: [Column; 2], instance: Column, @@ -176,17 +107,65 @@ impl FieldChip { s * (p + a * -F::one()) }); - FieldConfigEnum::Config(FieldConfig { + FieldConfig { advice, perm, s_mul, s_pub, - }) + } } } + +// ANCHOR: chip-trait-impl +impl Chip for FieldChip { + type Config = FieldConfig; + type Loaded = (); + + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} +// ANCHOR_END: chip-trait-impl + // ANCHOR_END: chip-impl +// ANCHOR: chip-config +/// Chip state is stored in a config struct. This is generated by the chip +/// during configuration, and then stored inside the chip. +#[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; 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, + + // We need a selector to enable the multiplication gate, so that we aren't placing + // any constraints on cells where `NumericInstructions::mul` is not being used. + // This is important when building larger circuits, where columns are used by + // multiple sets of instructions. + s_mul: Selector, + + // The selector for the public-input gate, which uses one of the advice columns. + s_pub: Selector, +} +// ANCHOR_END: chip-config + // ANCHOR: instructions-impl +/// A variable representing a number. +#[derive(Clone)] +struct Number { + cell: Cell, + value: Option, +} impl NumericInstructions for FieldChip { type Num = Number; @@ -196,10 +175,7 @@ impl NumericInstructions for FieldChip { mut layouter: impl Layouter, value: Option, ) -> Result { - let config = match self.config() { - FieldConfigEnum::Config(config) => config.clone(), - _ => unreachable!(), - }; + let config = self.config(); let mut num = None; layouter.assign_region( @@ -224,10 +200,7 @@ impl NumericInstructions for FieldChip { a: Self::Num, b: Self::Num, ) -> Result { - let config = match self.config() { - FieldConfigEnum::Config(config) => config.clone(), - _ => unreachable!(), - }; + let config = self.config(); let mut out = None; layouter.assign_region( @@ -277,10 +250,7 @@ impl NumericInstructions for FieldChip { } fn expose_public(&self, mut layouter: impl Layouter, num: Self::Num) -> Result<(), Error> { - let config = match self.config() { - FieldConfigEnum::Config(config) => config.clone(), - _ => unreachable!(), - }; + let config = self.config(); layouter.assign_region( || "expose public", @@ -319,7 +289,7 @@ struct MyCircuit { impl Circuit for MyCircuit { // Since we are using a single chip for everything, we can just reuse its config. - type Config = FieldConfigEnum; + type Config = FieldConfig; fn configure(meta: &mut ConstraintSystem) -> Self::Config { // We create the two advice columns that FieldChip uses for I/O. @@ -328,14 +298,12 @@ impl Circuit for MyCircuit { // We also need an instance column to store public inputs. let instance = meta.instance_column(); - let mut field_chip = FieldChip::new(); - - field_chip.configure(meta, advice, instance) + FieldChip::configure(meta, advice, instance) } fn synthesize(&self, cs: &mut impl Assignment, config: Self::Config) -> Result<(), Error> { let mut layouter = SingleChipLayouter::new(cs)?; - let field_chip = FieldChip::::construct(config, ()); + let field_chip = FieldChip::::construct(config); // Load our private values into the circuit. let a = field_chip.load_private(layouter.namespace(|| "load a"), self.a)?; diff --git a/examples/two-chip.rs b/examples/two-chip.rs index 47fda53e..5e7595e7 100644 --- a/examples/two-chip.rs +++ b/examples/two-chip.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; use halo2::{ arithmetic::FieldExt, - circuit::{layouter::SingleChipLayouter, Cell, Chip, Config, Layouter, Region}, + circuit::{layouter::SingleChipLayouter, Cell, Chip, Layouter, Region}, dev::VerifyFailure, plonk::{ Advice, Assignment, Circuit, Column, ConstraintSystem, Error, Instance, Permutation, @@ -13,6 +13,7 @@ use halo2::{ poly::Rotation, }; +// ANCHOR: field-instructions /// A variable representing a number. #[derive(Clone)] struct Number { @@ -20,7 +21,6 @@ struct Number { value: Option, } -// ANCHOR: field-instructions trait FieldInstructions: AddInstructions + MulInstructions { /// Variable representing a number. type Num; @@ -98,20 +98,8 @@ struct FieldConfig { // 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 - } + add_config: AddConfig, + mul_config: MulConfig, } // ANCHOR END: field-config @@ -122,18 +110,6 @@ struct AddConfig { 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 @@ -143,45 +119,33 @@ struct MulConfig { 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 { - config: FieldConfigEnum, + config: FieldConfig, _marker: PhantomData, } // ANCHOR_END: field-chip // ANCHOR: add-chip struct AddChip { - config: AddConfigEnum, + config: AddConfig, _marker: PhantomData, } // ANCHOR END: add-chip // ANCHOR: mul-chip struct MulChip { - config: MulConfigEnum, + config: MulConfig, _marker: PhantomData, } // ANCHOR_END: mul-chip // ANCHOR: add-chip-trait-impl impl Chip for AddChip { - type Config = AddConfigEnum; + type Config = AddConfig; type Loaded = (); fn config(&self) -> &Self::Config { @@ -196,13 +160,6 @@ impl Chip for AddChip { // ANCHOR: add-chip-impl impl AddChip { - fn new() -> Self { - Self { - config: >::Config::empty(), - _marker: PhantomData, - } - } - fn construct(config: >::Config, _loaded: >::Loaded) -> Self { Self { config, @@ -211,7 +168,6 @@ impl AddChip { } fn configure( - &mut self, meta: &mut ConstraintSystem, advice: [Column; 2], perm: Permutation, @@ -227,13 +183,11 @@ impl AddChip { s_add * (lhs + rhs + out * -F::one()) }); - let config = AddConfigEnum::Config(AddConfig { + AddConfig { advice, perm, s_add, - }); - self.config = config.clone(); - config + } } } // ANCHOR END: add-chip-impl @@ -247,10 +201,8 @@ impl AddInstructions for FieldChip { a: Self::Num, b: Self::Num, ) -> Result { - let config = match self.config() { - FieldConfigEnum::Config(config) => config.add_config.clone(), - _ => unreachable!(), - }; + let config = self.config().add_config.clone(); + let add_chip = AddChip::::construct(config, ()); add_chip.add(layouter, a, b) } @@ -265,10 +217,7 @@ impl AddInstructions for AddChip { a: Self::Num, b: Self::Num, ) -> Result { - let config = match self.config() { - AddConfigEnum::Config(config) => config.clone(), - _ => unreachable!(), - }; + let config = self.config(); let mut out = None; layouter.assign_region( @@ -321,7 +270,7 @@ impl AddInstructions for AddChip { // ANCHOR: mul-chip-trait-impl impl Chip for MulChip { - type Config = MulConfigEnum; + type Config = MulConfig; type Loaded = (); fn config(&self) -> &Self::Config { @@ -336,13 +285,6 @@ impl Chip for MulChip { // ANCHOR: mul-chip-impl impl MulChip { - fn new() -> Self { - Self { - config: >::Config::empty(), - _marker: PhantomData, - } - } - fn construct(config: >::Config, _loaded: >::Loaded) -> Self { Self { config, @@ -351,7 +293,6 @@ impl MulChip { } fn configure( - &mut self, meta: &mut ConstraintSystem, advice: [Column; 2], perm: Permutation, @@ -385,13 +326,11 @@ impl MulChip { s_mul * (lhs * rhs + out * -F::one()) }); - let config = MulConfigEnum::Config(MulConfig { + MulConfig { advice, perm, s_mul, - }); - self.config = config.clone(); - config + } } } // ANCHOR_END: mul-chip-impl @@ -405,10 +344,7 @@ impl MulInstructions for FieldChip { a: Self::Num, b: Self::Num, ) -> Result { - let config = match self.config() { - FieldConfigEnum::Config(config) => config.mul_config.clone(), - _ => unreachable!(), - }; + let config = self.config().mul_config.clone(); let mul_chip = MulChip::::construct(config, ()); mul_chip.mul(layouter, a, b) } @@ -423,10 +359,7 @@ impl MulInstructions for MulChip { a: Self::Num, b: Self::Num, ) -> Result { - let config = match self.config() { - MulConfigEnum::Config(config) => config.clone(), - _ => unreachable!(), - }; + let config = self.config(); let mut out = None; layouter.assign_region( @@ -479,7 +412,7 @@ impl MulInstructions for MulChip { // ANCHOR: field-chip-trait-impl impl Chip for FieldChip { - type Config = FieldConfigEnum; + type Config = FieldConfig; type Loaded = (); fn config(&self) -> &Self::Config { @@ -494,13 +427,6 @@ impl Chip for FieldChip { // ANCHOR: field-chip-impl impl FieldChip { - fn new() -> Self { - Self { - config: >::Config::empty(), - _marker: PhantomData, - } - } - fn construct(config: >::Config, _loaded: >::Loaded) -> Self { Self { config, @@ -509,7 +435,6 @@ impl FieldChip { } fn configure( - &mut self, meta: &mut ConstraintSystem, advice: [Column; 2], instance: Column, @@ -536,21 +461,16 @@ impl FieldChip { s * (p + a * -F::one()) }); - let mut add_chip = AddChip::new(); - let add_config = add_chip.configure(meta, advice, perm.clone()); + let add_config = AddChip::configure(meta, advice, perm.clone()); + let mul_config = MulChip::configure(meta, advice, perm.clone()); - let mut mul_chip = MulChip::new(); - let mul_config = mul_chip.configure(meta, advice, perm.clone()); - - let config = FieldConfigEnum::Config(FieldConfig { + FieldConfig { advice, perm, s_pub, add_config, mul_config, - }); - self.config = config.clone(); - config + } } } // ANCHOR_END: field-chip-impl @@ -564,10 +484,7 @@ impl FieldInstructions for FieldChip { mut layouter: impl Layouter, value: Option, ) -> Result<>::Num, Error> { - let config = match self.config() { - FieldConfigEnum::Config(config) => config.clone(), - _ => unreachable!(), - }; + let config = self.config(); let mut num = None; layouter.assign_region( @@ -603,10 +520,7 @@ impl FieldInstructions for FieldChip { mut layouter: impl Layouter, num: >::Num, ) -> Result<(), Error> { - let config = match self.config() { - FieldConfigEnum::Config(config) => config.clone(), - _ => unreachable!(), - }; + let config = self.config(); layouter.assign_region( || "expose public", @@ -646,7 +560,7 @@ struct MyCircuit { impl Circuit for MyCircuit { // Since we are using a single chip for everything, we can just reuse its config. - type Config = FieldConfigEnum; + type Config = FieldConfig; fn configure(meta: &mut ConstraintSystem) -> Self::Config { // We create the two advice columns that FieldChip uses for I/O. @@ -655,8 +569,7 @@ impl Circuit for MyCircuit { // We also need an instance column to store public inputs. let instance = meta.instance_column(); - let mut field_chip = FieldChip::new(); - field_chip.configure(meta, advice, instance) + FieldChip::configure(meta, advice, instance) } fn synthesize(&self, cs: &mut impl Assignment, config: Self::Config) -> Result<(), Error> { diff --git a/src/circuit.rs b/src/circuit.rs index 4c8f8284..a963d89f 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -9,22 +9,6 @@ use crate::{ 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. /// /// The chip stores state that is required at circuit synthesis time in @@ -38,13 +22,13 @@ pub trait Chip: Sized { /// during circuit synthesis, that can be derived during [`Circuit::configure`]. /// /// [`Circuit::configure`]: crate::plonk::Circuit::configure - type Config: Config; + type Config: fmt::Debug + Clone; /// 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`]: crate::plonk::Circuit::synthesize - type Loaded: Loaded; + type Loaded: fmt::Debug + Clone; /// The chip holds its own configuration. fn config(&self) -> &Self::Config; From b2d42ef3441fa83ae75f0de856c02021652994ad Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 28 Apr 2021 10:57:51 +0800 Subject: [PATCH 6/9] Remove superfluous clone() in doc example Co-authored-by: Jack Grigg --- src/plonk/circuit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 2dbad5e8..0eaa99bd 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -164,7 +164,7 @@ impl TryFrom> for Column { /// } /// /// fn circuit_logic>(chip: C, mut layouter: impl Layouter) -> Result<(), Error> { -/// let config = chip.config().clone(); +/// let config = chip.config(); /// # let config: Config = todo!(); /// layouter.assign_region(|| "bar", |mut region| { /// region.assign_advice(|| "a", config.a, 0, || Ok(F::one()))?; From f3021beda618364e91adcd43020fdf6610205b20 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 28 Apr 2021 12:36:37 +0800 Subject: [PATCH 7/9] Clean up anchors in simple-example.rs Co-authored-by: Jack Grigg --- examples/simple-example.rs | 57 ++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/examples/simple-example.rs b/examples/simple-example.rs index a2720d68..9d25b4aa 100644 --- a/examples/simple-example.rs +++ b/examples/simple-example.rs @@ -43,7 +43,31 @@ struct FieldChip { } // ANCHOR_END: chip -// ANCHOR: chip-impl +// ANCHOR: chip-config +/// Chip state is stored in a config struct. This is generated by the chip +/// during configuration, and then stored inside the chip. +#[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; 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, + + // We need a selector to enable the multiplication gate, so that we aren't placing + // any constraints on cells where `NumericInstructions::mul` is not being used. + // This is important when building larger circuits, where columns are used by + // multiple sets of instructions. + s_mul: Selector, + + // The selector for the public-input gate, which uses one of the advice columns. + s_pub: Selector, +} + impl FieldChip { fn construct(config: >::Config) -> Self { Self { @@ -115,8 +139,9 @@ impl FieldChip { } } } +// ANCHOR_END: chip-config -// ANCHOR: chip-trait-impl +// ANCHOR: chip-impl impl Chip for FieldChip { type Config = FieldConfig; type Loaded = (); @@ -129,36 +154,8 @@ impl Chip for FieldChip { &() } } -// ANCHOR_END: chip-trait-impl - // ANCHOR_END: chip-impl -// ANCHOR: chip-config -/// Chip state is stored in a config struct. This is generated by the chip -/// during configuration, and then stored inside the chip. -#[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; 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, - - // We need a selector to enable the multiplication gate, so that we aren't placing - // any constraints on cells where `NumericInstructions::mul` is not being used. - // This is important when building larger circuits, where columns are used by - // multiple sets of instructions. - s_mul: Selector, - - // The selector for the public-input gate, which uses one of the advice columns. - s_pub: Selector, -} -// ANCHOR_END: chip-config - // ANCHOR: instructions-impl /// A variable representing a number. #[derive(Clone)] From 1f6b3c0014d07236233e5e8eef67554d8c7355bf Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 28 Apr 2021 20:39:08 +0800 Subject: [PATCH 8/9] Remove CellValue type --- src/circuit.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/circuit.rs b/src/circuit.rs index a963d89f..4f0bf755 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -87,22 +87,6 @@ pub struct Cell { column: Column, } -/// A structure containing a cell and its assigned value. -#[derive(Clone, Debug)] -pub struct CellValue { - /// The cell of this `CellValue` - pub cell: Cell, - /// The value assigned to this `CellValue` - pub value: Option, -} - -impl CellValue { - /// Construct a `CellValue`. - pub fn new(cell: Cell, value: Option) -> Self { - CellValue { cell, value } - } -} - /// 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 From 6f199d3b2fb08ba8e922867ba266075958e781e8 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 4 May 2021 11:14:32 +0800 Subject: [PATCH 9/9] [tmp] Block quote SHA256 example --- examples/sha256/main.rs | 276 ++++++++++++++++++++-------------------- 1 file changed, 139 insertions(+), 137 deletions(-) diff --git a/examples/sha256/main.rs b/examples/sha256/main.rs index dc156e51..7e0b74b8 100644 --- a/examples/sha256/main.rs +++ b/examples/sha256/main.rs @@ -1,162 +1,164 @@ -// //! Gadget and chips for the [SHA-256] hash function. -// //! -// //! [SHA-256]: https://tools.ietf.org/html/rfc6234 +//! Gadget and chips for the [SHA-256] hash function. +//! +//! [SHA-256]: https://tools.ietf.org/html/rfc6234 -// use std::cmp::min; -// use std::convert::TryInto; -// use std::fmt; +/* +use std::cmp::min; +use std::convert::TryInto; +use std::fmt; -// use halo2::{ -// circuit::{Chip, Layouter}, -// plonk::Error, -// }; +use halo2::{ + circuit::{Chip, Layouter}, + plonk::Error, +}; -// mod benches; -// mod table16; +mod benches; +mod table16; -// pub use table16::{BlockWord, Table16Chip, Table16Config}; +pub use table16::{BlockWord, Table16Chip, Table16Config}; -// /// The size of a SHA-256 block, in 32-bit words. -// pub const BLOCK_SIZE: usize = 16; -// /// The size of a SHA-256 digest, in 32-bit words. -// const DIGEST_SIZE: usize = 8; +/// The size of a SHA-256 block, in 32-bit words. +pub const BLOCK_SIZE: usize = 16; +/// The size of a SHA-256 digest, in 32-bit words. +const DIGEST_SIZE: usize = 8; -// /// The set of circuit instructions required to use the [`Sha256`] gadget. -// pub trait Sha256Instructions: Chip { -// /// Variable representing the SHA-256 internal state. -// type State: Clone + fmt::Debug; -// /// Variable representing a 32-bit word of the input block to the SHA-256 compression -// /// function. -// type BlockWord: Copy + fmt::Debug; +/// The set of circuit instructions required to use the [`Sha256`] gadget. +pub trait Sha256Instructions: Chip { + /// Variable representing the SHA-256 internal state. + type State: Clone + fmt::Debug; + /// Variable representing a 32-bit word of the input block to the SHA-256 compression + /// function. + type BlockWord: Copy + fmt::Debug; -// /// The zero BlockWord -// fn zero() -> Self::BlockWord; + /// The zero BlockWord + fn zero() -> Self::BlockWord; -// /// Places the SHA-256 IV in the circuit, returning the initial state variable. -// fn initialization_vector(layouter: &mut impl Layouter) -> Result; + /// Places the SHA-256 IV in the circuit, returning the initial state variable. + fn initialization_vector(layouter: &mut impl Layouter) -> Result; -// /// Creates an initial state from the output state of a previous block -// fn initialization( -// layouter: &mut impl Layouter, -// init_state: &Self::State, -// ) -> Result; + /// Creates an initial state from the output state of a previous block + fn initialization( + layouter: &mut impl Layouter, + init_state: &Self::State, + ) -> Result; -// /// Starting from the given initialized state, processes a block of input and returns the -// /// final state. -// fn compress( -// layouter: &mut impl Layouter, -// initialized_state: &Self::State, -// input: [Self::BlockWord; BLOCK_SIZE], -// ) -> Result; + /// Starting from the given initialized state, processes a block of input and returns the + /// final state. + fn compress( + layouter: &mut impl Layouter, + initialized_state: &Self::State, + input: [Self::BlockWord; BLOCK_SIZE], + ) -> Result; -// /// Converts the given state into a message digest. -// fn digest( -// layouter: &mut impl Layouter, -// state: &Self::State, -// ) -> Result<[Self::BlockWord; DIGEST_SIZE], Error>; -// } + /// Converts the given state into a message digest. + fn digest( + layouter: &mut impl Layouter, + state: &Self::State, + ) -> Result<[Self::BlockWord; DIGEST_SIZE], Error>; +} -// /// The output of a SHA-256 circuit invocation. -// #[derive(Debug)] -// pub struct Sha256Digest([BlockWord; DIGEST_SIZE]); +/// The output of a SHA-256 circuit invocation. +#[derive(Debug)] +pub struct Sha256Digest([BlockWord; DIGEST_SIZE]); -// /// A gadget that constrains a SHA-256 invocation. It supports input at a granularity of -// /// 32 bits. -// #[derive(Debug)] -// pub struct Sha256 { -// state: CS::State, -// cur_block: Vec, -// length: usize, -// } +/// A gadget that constrains a SHA-256 invocation. It supports input at a granularity of +/// 32 bits. +#[derive(Debug)] +pub struct Sha256 { + state: CS::State, + cur_block: Vec, + length: usize, +} -// impl Sha256 { -// /// Create a new hasher instance. -// pub fn new(mut layouter: impl Layouter) -> Result { -// Ok(Sha256 { -// state: Sha256Chip::initialization_vector(&mut layouter)?, -// cur_block: Vec::with_capacity(BLOCK_SIZE), -// length: 0, -// }) -// } +impl Sha256 { + /// Create a new hasher instance. + pub fn new(mut layouter: impl Layouter) -> Result { + Ok(Sha256 { + state: Sha256Chip::initialization_vector(&mut layouter)?, + cur_block: Vec::with_capacity(BLOCK_SIZE), + length: 0, + }) + } -// /// Digest data, updating the internal state. -// pub fn update( -// &mut self, -// mut layouter: impl Layouter, -// mut data: &[Sha256Chip::BlockWord], -// ) -> Result<(), Error> { -// self.length += data.len() * 32; + /// Digest data, updating the internal state. + pub fn update( + &mut self, + mut layouter: impl Layouter, + mut data: &[Sha256Chip::BlockWord], + ) -> Result<(), Error> { + self.length += data.len() * 32; -// // Fill the current block, if possible. -// let remaining = BLOCK_SIZE - self.cur_block.len(); -// let (l, r) = data.split_at(min(remaining, data.len())); -// self.cur_block.extend_from_slice(l); -// data = r; + // Fill the current block, if possible. + let remaining = BLOCK_SIZE - self.cur_block.len(); + let (l, r) = data.split_at(min(remaining, data.len())); + self.cur_block.extend_from_slice(l); + data = r; -// // If we still don't have a full block, we are done. -// if self.cur_block.len() < BLOCK_SIZE { -// return Ok(()); -// } + // If we still don't have a full block, we are done. + if self.cur_block.len() < BLOCK_SIZE { + return Ok(()); + } -// // Process the now-full current block. -// self.state = Sha256Chip::compress( -// &mut layouter, -// &self.state, -// self.cur_block[..] -// .try_into() -// .expect("cur_block.len() == BLOCK_SIZE"), -// )?; -// self.cur_block.clear(); + // Process the now-full current block. + self.state = Sha256Chip::compress( + &mut layouter, + &self.state, + self.cur_block[..] + .try_into() + .expect("cur_block.len() == BLOCK_SIZE"), + )?; + self.cur_block.clear(); -// // Process any additional full blocks. -// let mut chunks_iter = data.chunks_exact(BLOCK_SIZE); -// for chunk in &mut chunks_iter { -// self.state = Sha256Chip::initialization(&mut layouter, &self.state)?; -// self.state = Sha256Chip::compress( -// &mut layouter, -// &self.state, -// chunk.try_into().expect("chunk.len() == BLOCK_SIZE"), -// )?; -// } + // Process any additional full blocks. + let mut chunks_iter = data.chunks_exact(BLOCK_SIZE); + for chunk in &mut chunks_iter { + self.state = Sha256Chip::initialization(&mut layouter, &self.state)?; + self.state = Sha256Chip::compress( + &mut layouter, + &self.state, + chunk.try_into().expect("chunk.len() == BLOCK_SIZE"), + )?; + } -// // Cache the remaining partial block, if any. -// let rem = chunks_iter.remainder(); -// self.cur_block.extend_from_slice(rem); + // Cache the remaining partial block, if any. + let rem = chunks_iter.remainder(); + self.cur_block.extend_from_slice(rem); -// Ok(()) -// } + Ok(()) + } -// /// Retrieve result and consume hasher instance. -// pub fn finalize( -// mut self, -// mut layouter: impl Layouter, -// ) -> Result, Error> { -// // Pad the remaining block -// if !self.cur_block.is_empty() { -// let padding = vec![Sha256Chip::zero(); BLOCK_SIZE - self.cur_block.len()]; -// self.cur_block.extend_from_slice(&padding); -// self.state = Sha256Chip::initialization(&mut layouter, &self.state)?; -// self.state = Sha256Chip::compress( -// &mut layouter, -// &self.state, -// self.cur_block[..] -// .try_into() -// .expect("cur_block.len() == BLOCK_SIZE"), -// )?; -// } -// Sha256Chip::digest(&mut layouter, &self.state).map(Sha256Digest) -// } + /// Retrieve result and consume hasher instance. + pub fn finalize( + mut self, + mut layouter: impl Layouter, + ) -> Result, Error> { + // Pad the remaining block + if !self.cur_block.is_empty() { + let padding = vec![Sha256Chip::zero(); BLOCK_SIZE - self.cur_block.len()]; + self.cur_block.extend_from_slice(&padding); + self.state = Sha256Chip::initialization(&mut layouter, &self.state)?; + self.state = Sha256Chip::compress( + &mut layouter, + &self.state, + self.cur_block[..] + .try_into() + .expect("cur_block.len() == BLOCK_SIZE"), + )?; + } + Sha256Chip::digest(&mut layouter, &self.state).map(Sha256Digest) + } -// /// Convenience function to compute hash of the data. It will handle hasher creation, -// /// data feeding and finalization. -// pub fn digest( -// mut layouter: impl Layouter, -// data: &[Sha256Chip::BlockWord], -// ) -> Result, Error> { -// let mut hasher = Self::new(layouter.namespace(|| "init"))?; -// hasher.update(layouter.namespace(|| "update"), data)?; -// hasher.finalize(layouter.namespace(|| "finalize")) -// } -// } + /// Convenience function to compute hash of the data. It will handle hasher creation, + /// data feeding and finalization. + pub fn digest( + mut layouter: impl Layouter, + data: &[Sha256Chip::BlockWord], + ) -> Result, Error> { + let mut hasher = Self::new(layouter.namespace(|| "init"))?; + hasher.update(layouter.namespace(|| "update"), data)?; + hasher.finalize(layouter.namespace(|| "finalize")) + } +} +*/ fn main() {}