From 4d8ae89aa9331c3f0d24e183951b725bf03ab4b0 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 3 Jun 2021 09:41:03 +0800 Subject: [PATCH 01/10] Add Utilities chip --- src/circuit/gadget.rs | 1 + src/circuit/gadget/utilities.rs | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 src/circuit/gadget/utilities.rs diff --git a/src/circuit/gadget.rs b/src/circuit/gadget.rs index 7d6426a7..20572cd7 100644 --- a/src/circuit/gadget.rs +++ b/src/circuit/gadget.rs @@ -1,2 +1,3 @@ pub(crate) mod ecc; pub(crate) mod poseidon; +pub(crate) mod utilities; diff --git a/src/circuit/gadget/utilities.rs b/src/circuit/gadget/utilities.rs new file mode 100644 index 00000000..fe8d6925 --- /dev/null +++ b/src/circuit/gadget/utilities.rs @@ -0,0 +1,78 @@ +use halo2::{ + circuit::{Cell, Chip, Layouter, Region}, + plonk::{Advice, Column, Error, Permutation}, +}; +use pasta_curves::arithmetic::FieldExt; + +/// A variable representing a number. +#[derive(Copy, Clone, Debug)] +pub struct CellValue { + cell: Cell, + value: Option, +} + +pub trait Var { + fn new(cell: Cell, value: Option) -> Self; + fn cell(&self) -> Cell; + fn value(&self) -> Option; +} + +impl Var for CellValue { + fn new(cell: Cell, value: Option) -> Self { + Self { cell, value } + } + + fn cell(&self) -> Cell { + self.cell + } + + fn value(&self) -> Option { + self.value + } +} + +pub trait UtilitiesInstructions: Chip { + type Var: Var; + + fn load_private( + &self, + mut layouter: impl Layouter, + column: Column, + value: Option, + ) -> Result { + layouter.assign_region( + || "load private", + |mut region| { + let cell = region.assign_advice( + || "load private", + column, + 0, + || value.ok_or(Error::SynthesisError), + )?; + Ok(Var::new(cell, value)) + }, + ) + } +} + +/// Assign a cell the same value as another cell and set up a copy constraint between them. +pub fn copy( + region: &mut Region<'_, F>, + annotation: A, + column: Column, + row: usize, + copy: &CellValue, + perm: &Permutation, +) -> Result, Error> +where + A: Fn() -> AR, + AR: Into, +{ + let cell = region.assign_advice(annotation, column, row, || { + copy.value.ok_or(Error::SynthesisError) + })?; + + region.constrain_equal(perm, cell, copy.cell)?; + + Ok(CellValue::new(cell, copy.value)) +} From 4f878152623c1beb05c37a625fa4bbd98260aed5 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 3 Jun 2021 09:41:54 +0800 Subject: [PATCH 02/10] Add standard PLONK chip --- src/circuit/gadget/utilities.rs | 2 + src/circuit/gadget/utilities/plonk.rs | 339 ++++++++++++++++++++++++++ 2 files changed, 341 insertions(+) create mode 100644 src/circuit/gadget/utilities/plonk.rs diff --git a/src/circuit/gadget/utilities.rs b/src/circuit/gadget/utilities.rs index fe8d6925..a7644726 100644 --- a/src/circuit/gadget/utilities.rs +++ b/src/circuit/gadget/utilities.rs @@ -4,6 +4,8 @@ use halo2::{ }; use pasta_curves::arithmetic::FieldExt; +mod plonk; + /// A variable representing a number. #[derive(Copy, Clone, Debug)] pub struct CellValue { diff --git a/src/circuit/gadget/utilities/plonk.rs b/src/circuit/gadget/utilities/plonk.rs new file mode 100644 index 00000000..ab41dc63 --- /dev/null +++ b/src/circuit/gadget/utilities/plonk.rs @@ -0,0 +1,339 @@ +use super::{copy, CellValue, UtilitiesInstructions}; +use halo2::{ + circuit::{Chip, Layouter}, + plonk::{Advice, Column, ConstraintSystem, Error, Fixed, Permutation}, + poly::Rotation, +}; +use pasta_curves::arithmetic::FieldExt; +use std::marker::PhantomData; + +#[allow(clippy::upper_case_acronyms)] +#[allow(clippy::too_many_arguments)] +pub trait PLONKInstructions: UtilitiesInstructions { + type Var; + + // Checks that a * sm * b = c * sc + fn mul( + &self, + layouter: impl Layouter, + a: >::Var, + b: >::Var, + c: >::Var, + sc: Option, + sm: Option, + ) -> Result<(), Error>; + // Checks that a * sa + b * sb = c * sc + fn add( + &self, + layouter: impl Layouter, + a: >::Var, + b: >::Var, + c: >::Var, + sa: Option, + sb: Option, + sc: Option, + ) -> Result<(), Error>; +} + +#[allow(clippy::upper_case_acronyms)] +#[derive(Clone, Debug)] +pub struct PLONKConfig { + a: Column, + b: Column, + c: Column, + + sa: Column, + sb: Column, + sc: Column, + sm: Column, + + perm: Permutation, +} + +#[allow(clippy::upper_case_acronyms)] +pub struct PLONKChip { + config: PLONKConfig, + _marker: PhantomData, +} + +#[allow(clippy::upper_case_acronyms)] +impl Chip for PLONKChip { + type Config = PLONKConfig; + type Loaded = (); + + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} + +#[allow(clippy::upper_case_acronyms)] +impl UtilitiesInstructions for PLONKChip { + type Var = CellValue; +} + +#[allow(clippy::upper_case_acronyms)] +impl PLONKInstructions for PLONKChip { + type Var = CellValue; + + fn mul( + &self, + mut layouter: impl Layouter, + a: >::Var, + b: >::Var, + c: >::Var, + sc: Option, + sm: Option, + ) -> Result<(), Error> { + layouter.assign_region( + || "mul", + |mut region| { + let config = self.config().clone(); + let sc = sc.unwrap_or_else(F::one); + let sm = sm.unwrap_or_else(F::one); + + // Copy in `a` + copy(&mut region, || "copy a", config.a, 0, &a, &config.perm)?; + + // Copy in `b` + copy(&mut region, || "copy b", config.b, 0, &b, &config.perm)?; + + // Copy in `c` + copy(&mut region, || "copy c", config.c, 0, &c, &config.perm)?; + + // Assign fixed columns + region.assign_fixed(|| "sc", config.sc, 0, || Ok(sc))?; + region.assign_fixed(|| "a * (sm) * b", config.sm, 0, || Ok(sm))?; + + #[cfg(test)] + // Checks that a * sm * b = c * sc + { + let a = a.value.unwrap(); + let b = b.value.unwrap(); + let c = c.value.unwrap(); + assert_eq!(a * sm * b, c * sc); + } + + Ok(()) + }, + ) + } + + fn add( + &self, + mut layouter: impl Layouter, + a: >::Var, + b: >::Var, + c: >::Var, + sa: Option, + sb: Option, + sc: Option, + ) -> Result<(), Error> { + let config = self.config().clone(); + let sa = sa.unwrap_or_else(F::one); + let sb = sb.unwrap_or_else(F::one); + let sc = sc.unwrap_or_else(F::one); + + layouter.assign_region( + || "add", + |mut region| { + // Copy in `a` + copy(&mut region, || "copy a", config.a, 0, &a, &config.perm)?; + + // Copy in `b` + copy(&mut region, || "copy b", config.b, 0, &b, &config.perm)?; + + // Copy in `c` + copy(&mut region, || "copy c", config.c, 0, &c, &config.perm)?; + + // Assign fixed columns + region.assign_fixed(|| "a", config.sa, 0, || Ok(sa))?; + region.assign_fixed(|| "b", config.sb, 0, || Ok(sb))?; + region.assign_fixed(|| "c", config.sc, 0, || Ok(sc))?; + + #[cfg(test)] + // Checks that a * sa + b * sb = c * sc + { + let a = a.value.unwrap(); + let b = b.value.unwrap(); + let c = c.value.unwrap(); + assert_eq!(a * sa + b * sb, c * sc); + } + + Ok(()) + }, + ) + } +} + +#[allow(clippy::upper_case_acronyms)] +impl PLONKChip { + /// Configures this chip for use in a circuit. + pub fn configure( + meta: &mut ConstraintSystem, + advices: [Column; 3], + perm: Permutation, + ) -> PLONKConfig { + let a = advices[0]; + let b = advices[1]; + let c = advices[2]; + + let sa = meta.fixed_column(); + let sb = meta.fixed_column(); + let sc = meta.fixed_column(); + let sm = meta.fixed_column(); + + meta.create_gate("Combined add-mult", |meta| { + let a = meta.query_advice(a, Rotation::cur()); + let b = meta.query_advice(b, Rotation::cur()); + let c = meta.query_advice(c, Rotation::cur()); + + let sa = meta.query_fixed(sa, Rotation::cur()); + let sb = meta.query_fixed(sb, Rotation::cur()); + let sc = meta.query_fixed(sc, Rotation::cur()); + let sm = meta.query_fixed(sm, Rotation::cur()); + + vec![a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one()))] + }); + + PLONKConfig { + a, + b, + c, + sa, + sb, + sc, + sm, + perm, + } + } + + pub fn construct(config: PLONKConfig) -> Self { + PLONKChip { + config, + _marker: PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use super::super::UtilitiesInstructions; + use super::{PLONKChip, PLONKConfig, PLONKInstructions}; + use halo2::{ + circuit::{layouter::SingleChipLayouter, Layouter}, + dev::MockProver, + plonk::{Any, Assignment, Circuit, Column, ConstraintSystem, Error}, + }; + use pasta_curves::{arithmetic::FieldExt, pallas::Base}; + + #[test] + fn plonk_util() { + struct MyCircuit { + a: Option, + b: Option, + } + + impl Circuit for MyCircuit { + type Config = PLONKConfig; + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let advices = [ + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + ]; + + let perm = meta.permutation( + &advices + .iter() + .map(|advice| (*advice).into()) + .collect::>>(), + ); + + PLONKChip::::configure(meta, advices, perm) + } + + fn synthesize( + &self, + cs: &mut impl Assignment, + config: Self::Config, + ) -> Result<(), Error> { + let mut layouter = SingleChipLayouter::new(cs)?; + let chip = PLONKChip::::construct(config.clone()); + + let a = chip.load_private(layouter.namespace(|| "a"), config.a, self.a)?; + let b = chip.load_private(layouter.namespace(|| "b"), config.b, self.b)?; + + // a + b = c + { + let c = self.a.zip(self.b).map(|(a, b)| a + b); + let c = chip.load_private(layouter.namespace(|| "c"), config.c, c)?; + chip.add( + layouter.namespace(|| "a + b = c"), + a, + b, + c, + None, + None, + None, + )?; + } + + // a * b = c + { + let c = self.a.zip(self.b).map(|(a, b)| a * b); + let c = chip.load_private(layouter.namespace(|| "c"), config.c, c)?; + chip.mul(layouter.namespace(|| "a * b = c"), a, b, c, None, None)?; + } + + // 2a + 3b = c + { + let c = self + .a + .zip(self.b) + .map(|(a, b)| a * F::from_u64(2) + b * F::from_u64(3)); + let c = chip.load_private(layouter.namespace(|| "c"), config.c, c)?; + chip.add( + layouter.namespace(|| "2a + 3b = c"), + a, + b, + c, + Some(F::from_u64(2)), + Some(F::from_u64(3)), + None, + )?; + } + + // 4 * a * b = 2 * c => c = 2ab + { + let c = self.a.zip(self.b).map(|(a, b)| a * b * F::from_u64(2)); + let c = chip.load_private(layouter.namespace(|| "c"), config.c, c)?; + chip.mul( + layouter.namespace(|| "4 * a * b = 2 * c"), + a, + b, + c, + Some(F::from_u64(2)), + Some(F::from_u64(4)), + )?; + } + + Ok(()) + } + } + + let circuit: MyCircuit = MyCircuit { + a: Some(Base::rand()), + b: Some(Base::rand()), + }; + let prover = match MockProver::::run(3, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{:?}", e), + }; + assert_eq!(prover.verify(), Ok(())); + } +} From 4b0ea0be155b17a2d0e8364d9ce8a6d7a5184dc3 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 3 Jun 2021 09:42:36 +0800 Subject: [PATCH 03/10] Add conditional swap chip --- src/circuit/gadget/utilities.rs | 1 + src/circuit/gadget/utilities/cond_swap.rs | 356 ++++++++++++++++++++++ 2 files changed, 357 insertions(+) create mode 100644 src/circuit/gadget/utilities/cond_swap.rs diff --git a/src/circuit/gadget/utilities.rs b/src/circuit/gadget/utilities.rs index a7644726..a7794868 100644 --- a/src/circuit/gadget/utilities.rs +++ b/src/circuit/gadget/utilities.rs @@ -4,6 +4,7 @@ use halo2::{ }; use pasta_curves::arithmetic::FieldExt; +mod cond_swap; mod plonk; /// A variable representing a number. diff --git a/src/circuit/gadget/utilities/cond_swap.rs b/src/circuit/gadget/utilities/cond_swap.rs new file mode 100644 index 00000000..566c6658 --- /dev/null +++ b/src/circuit/gadget/utilities/cond_swap.rs @@ -0,0 +1,356 @@ +use super::{copy, CellValue, UtilitiesInstructions}; +use halo2::{ + circuit::{Cell, Chip, Layouter}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Permutation, Selector}, + poly::Rotation, +}; +use pasta_curves::arithmetic::FieldExt; +use std::marker::PhantomData; + +pub trait CondSwapInstructions: UtilitiesInstructions { + /// Variable representing cell with a certain value in the circuit. + type Var; + + /// Variable representing a `swap` boolean flag. + type Swap: From<>::Var>; + + /// Given an input pair (x,y) and a `swap` boolean flag, return + /// (y,x) if `swap` is set, else (x,y) if `swap` is not set. + fn swap( + &self, + layouter: impl Layouter, + pair: ( + >::Var, + >::Var, + ), + swap: Self::Swap, + ) -> Result< + ( + >::Var, + >::Var, + ), + Error, + >; +} + +/// A chip implementing a conditional swap. +#[derive(Clone, Debug)] +pub struct CondSwapChip { + config: CondSwapConfig, + _marker: PhantomData, +} + +impl Chip for CondSwapChip { + type Config = CondSwapConfig; + type Loaded = (); + + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} + +#[derive(Clone, Debug)] +pub struct CondSwapConfig { + q_swap: Selector, + x: Column, + y: Column, + x_swapped: Column, + y_swapped: Column, + swap: Column, + perm: Permutation, +} + +/// A variable representing a `swap` boolean flag. +#[derive(Copy, Clone)] +pub struct Swap { + cell: Cell, + value: Option, +} + +impl From> for Swap { + fn from(var: CellValue) -> Self { + let value = var.value.map(|value| { + let zero = value == F::zero(); + let one = value == F::one(); + if zero { + false + } else if one { + true + } else { + panic!("Value must be boolean.") + } + }); + Swap { + cell: var.cell, + value, + } + } +} + +impl UtilitiesInstructions for CondSwapChip { + type Var = CellValue; +} + +impl CondSwapInstructions for CondSwapChip { + type Var = CellValue; + type Swap = Swap; + + fn swap( + &self, + mut layouter: impl Layouter, + pair: ( + >::Var, + >::Var, + ), + swap: Self::Swap, + ) -> Result< + ( + >::Var, + >::Var, + ), + Error, + > { + let config = self.config(); + + layouter.assign_region( + || "swap", + |mut region| { + // Enable `q_swap` selector + config.q_swap.enable(&mut region, 0)?; + + // Copy in `x` value + let x = copy(&mut region, || "copy x", config.x, 0, &pair.0, &config.perm)?; + + // Copy in `y` value + let y = copy(&mut region, || "copy y", config.y, 0, &pair.1, &config.perm)?; + + // Copy in `swap` value + let swap_val = swap.value; + let swap_cell = region.assign_advice( + || "swap", + config.swap, + 0, + || { + swap_val + .map(|swap| F::from_u64(swap as u64)) + .ok_or(Error::SynthesisError) + }, + )?; + region.constrain_equal(&config.perm, swap_cell, swap.cell)?; + + // Conditionally swap x + let x_swapped = { + let x_swapped = + x.value + .zip(y.value) + .zip(swap_val) + .map(|((x, y), swap)| if swap { y } else { x }); + let x_swapped_cell = region.assign_advice( + || "x_swapped", + config.x_swapped, + 0, + || x_swapped.ok_or(Error::SynthesisError), + )?; + CellValue { + cell: x_swapped_cell, + value: x_swapped, + } + }; + + // Conditionally swap y + let y_swapped = { + let y_swapped = + x.value + .zip(y.value) + .zip(swap_val) + .map(|((x, y), swap)| if swap { x } else { y }); + let y_swapped_cell = region.assign_advice( + || "y_swapped", + config.y_swapped, + 0, + || y_swapped.ok_or(Error::SynthesisError), + )?; + CellValue { + cell: y_swapped_cell, + value: y_swapped, + } + }; + + // Return swapped pair + Ok((x_swapped, y_swapped)) + }, + ) + } +} + +impl CondSwapChip { + /// Configures this chip for use in a circuit. + pub fn configure( + meta: &mut ConstraintSystem, + advices: [Column; 5], + perm: Permutation, + ) -> CondSwapConfig { + let q_swap = meta.selector(); + + let config = CondSwapConfig { + q_swap, + x: advices[0], + y: advices[1], + x_swapped: advices[2], + y_swapped: advices[3], + swap: advices[4], + perm, + }; + + // TODO: optimise shape of gate for Merkle path validation + + meta.create_gate("x' = y ⋅ swap + x ⋅ (1-swap)", |meta| { + let q_swap = meta.query_selector(q_swap, Rotation::cur()); + + let x = meta.query_advice(config.x, Rotation::cur()); + let y = meta.query_advice(config.y, Rotation::cur()); + let x_swapped = meta.query_advice(config.x_swapped, Rotation::cur()); + let y_swapped = meta.query_advice(config.y_swapped, Rotation::cur()); + let swap = meta.query_advice(config.swap, Rotation::cur()); + + let one = Expression::Constant(F::one()); + + // x_swapped - y ⋅ swap - x ⋅ (1-swap) = 0 + // This checks that `x_swapped` is equal to `y` when `swap` is set, + // but remains as `x` when `swap` is not set. + let x_check = + x_swapped - y.clone() * swap.clone() - x.clone() * (one.clone() - swap.clone()); + + // y_swapped - x ⋅ swap - y ⋅ (1-swap) = 0 + // This checks that `y_swapped` is equal to `x` when `swap` is set, + // but remains as `y` when `swap` is not set. + let y_check = y_swapped - x * swap.clone() - y * (one.clone() - swap.clone()); + + // Check `swap` is boolean. + let bool_check = swap.clone() * (one - swap); + + [x_check, y_check, bool_check] + .iter() + .map(|poly| q_swap.clone() * poly.clone()) + .collect() + }); + + config + } + + pub fn construct(config: CondSwapConfig) -> Self { + CondSwapChip { + config, + _marker: PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use super::super::UtilitiesInstructions; + use super::{CondSwapChip, CondSwapConfig, CondSwapInstructions}; + use halo2::{ + circuit::{layouter::SingleChipLayouter, Layouter}, + dev::MockProver, + plonk::{Any, Assignment, Circuit, Column, ConstraintSystem, Error}, + }; + use pasta_curves::{arithmetic::FieldExt, pallas::Base}; + + #[test] + fn cond_swap() { + struct MyCircuit { + x: Option, + y: Option, + swap: Option, + } + + impl Circuit for MyCircuit { + type Config = CondSwapConfig; + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let advices = [ + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + ]; + + let perm = meta.permutation( + &advices + .iter() + .map(|advice| (*advice).into()) + .collect::>>(), + ); + + CondSwapChip::::configure(meta, advices, perm) + } + + fn synthesize( + &self, + cs: &mut impl Assignment, + config: Self::Config, + ) -> Result<(), Error> { + let mut layouter = SingleChipLayouter::new(cs)?; + let chip = CondSwapChip::::construct(config.clone()); + + // Load the pair and the swap flag into the circuit. + let x = chip.load_private(layouter.namespace(|| "x"), config.x, self.x)?; + let y = chip.load_private(layouter.namespace(|| "y"), config.y, self.y)?; + let swap = + chip.load_private(layouter.namespace(|| "swap"), config.swap, self.swap)?; + + // Return the swapped pair. + let swapped_pair = + chip.swap(layouter.namespace(|| "swap"), (x, y).into(), swap.into())?; + + if let Some(swap) = self.swap { + if swap == F::one() { + // Check that `x` and `y` have been swapped + assert_eq!(swapped_pair.0.value.unwrap(), y.value.unwrap()); + assert_eq!(swapped_pair.1.value.unwrap(), x.value.unwrap()); + } else { + // Check that `x` and `y` have not been swapped + assert_eq!(swapped_pair.0.value.unwrap(), x.value.unwrap()); + assert_eq!(swapped_pair.1.value.unwrap(), y.value.unwrap()); + } + } + + Ok(()) + } + } + + // Test swap case + { + let circuit: MyCircuit = MyCircuit { + x: Some(Base::rand()), + y: Some(Base::rand()), + swap: Some(Base::one()), + }; + let prover = match MockProver::::run(3, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{:?}", e), + }; + assert_eq!(prover.verify(), Ok(())); + } + + // Test non-swap case + { + let circuit: MyCircuit = MyCircuit { + x: Some(Base::rand()), + y: Some(Base::rand()), + swap: Some(Base::zero()), + }; + let prover = match MockProver::::run(3, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{:?}", e), + }; + assert_eq!(prover.verify(), Ok(())); + } + } +} From fea88c814c3d7b159756342e42ae134e51ae3293 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 4 Jun 2021 18:04:51 +0800 Subject: [PATCH 04/10] Add enable flag chip --- src/circuit/gadget/utilities.rs | 1 + src/circuit/gadget/utilities/enable_flag.rs | 285 ++++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 src/circuit/gadget/utilities/enable_flag.rs diff --git a/src/circuit/gadget/utilities.rs b/src/circuit/gadget/utilities.rs index a7794868..041fbbfd 100644 --- a/src/circuit/gadget/utilities.rs +++ b/src/circuit/gadget/utilities.rs @@ -5,6 +5,7 @@ use halo2::{ use pasta_curves::arithmetic::FieldExt; mod cond_swap; +mod enable_flag; mod plonk; /// A variable representing a number. diff --git a/src/circuit/gadget/utilities/enable_flag.rs b/src/circuit/gadget/utilities/enable_flag.rs new file mode 100644 index 00000000..47ecf89f --- /dev/null +++ b/src/circuit/gadget/utilities/enable_flag.rs @@ -0,0 +1,285 @@ +use super::{copy, CellValue, UtilitiesInstructions, Var}; +use halo2::{ + circuit::{Cell, Chip, Layouter}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Permutation, Selector}, + poly::Rotation, +}; +use pasta_curves::arithmetic::FieldExt; +use std::marker::PhantomData; + +pub trait EnableFlagInstructions: UtilitiesInstructions { + /// Variable representing cell with a certain value in the circuit. + type Var: Var; + + /// Variable representing an `enable` boolean flag. + type Flag: From<>::Var>; + + /// Given a `value` and an `enable_flag`, check that either `value = 0` + /// or `enable_flag = 1`. + fn enable_flag( + &self, + layouter: impl Layouter, + value: >::Var, + enable_flag: >::Flag, + ) -> Result<(), Error>; +} + +#[derive(Clone, Debug)] +pub struct EnableFlagConfig { + q_enable: Selector, + value: Column, + enable_flag: Column, + perm: Permutation, +} + +/// A chip implementing an enable flag. +#[derive(Clone, Debug)] +pub struct EnableFlagChip { + config: EnableFlagConfig, + _marker: PhantomData, +} + +impl Chip for EnableFlagChip { + type Config = EnableFlagConfig; + type Loaded = (); + + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} + +/// A variable representing an `enable` boolean flag. +#[derive(Copy, Clone)] +pub struct Flag { + cell: Cell, + value: Option, +} + +impl From> for Flag { + fn from(var: CellValue) -> Self { + let value = var.value.map(|value| { + let zero = value == F::zero(); + let one = value == F::one(); + if zero { + false + } else if one { + true + } else { + panic!("Value must be boolean.") + } + }); + Flag { + cell: var.cell, + value, + } + } +} + +impl UtilitiesInstructions for EnableFlagChip { + type Var = CellValue; +} + +impl EnableFlagInstructions for EnableFlagChip { + type Var = CellValue; + type Flag = Flag; + + fn enable_flag( + &self, + mut layouter: impl Layouter, + value: >::Var, + enable_flag: >::Flag, + ) -> Result<(), Error> { + let config = self.config().clone(); + layouter.assign_region( + || "enable flag", + |mut region| { + // Enable `q_enable` selector + config.q_enable.enable(&mut region, 0)?; + + // Copy in `enable_flag` value + let enable_flag_val = enable_flag.value; + let enable_flag_cell = region.assign_advice( + || "enable_flag", + config.enable_flag, + 0, + || { + enable_flag_val + .map(|enable_flag| F::from_u64(enable_flag as u64)) + .ok_or(Error::SynthesisError) + }, + )?; + region.constrain_equal(&config.perm, enable_flag_cell, enable_flag.cell)?; + + // Copy `value` + copy( + &mut region, + || "copy value", + config.value, + 0, + &value, + &config.perm, + )?; + + Ok(()) + }, + ) + } +} + +impl EnableFlagChip { + /// Configures this chip for use in a circuit. + pub fn configure( + meta: &mut ConstraintSystem, + advices: [Column; 2], + perm: Permutation, + ) -> EnableFlagConfig { + let q_enable = meta.selector(); + + let config = EnableFlagConfig { + q_enable, + value: advices[0], + enable_flag: advices[1], + perm, + }; + + meta.create_gate("Enable flag", |meta| { + let q_enable = meta.query_selector(config.q_enable, Rotation::cur()); + let value = meta.query_advice(config.value, Rotation::cur()); + let enable_flag = meta.query_advice(config.enable_flag, Rotation::cur()); + + vec![q_enable * (Expression::Constant(F::one()) - enable_flag) * value] + }); + + config + } + + pub fn construct(config: EnableFlagConfig) -> Self { + EnableFlagChip { + config, + _marker: PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use super::super::UtilitiesInstructions; + use super::{EnableFlagChip, EnableFlagConfig, EnableFlagInstructions}; + use halo2::{ + circuit::{layouter::SingleChipLayouter, Layouter}, + dev::{MockProver, VerifyFailure}, + plonk::{Any, Assignment, Circuit, Column, ConstraintSystem, Error}, + }; + use pasta_curves::{arithmetic::FieldExt, pallas::Base}; + + #[test] + fn enable_flag() { + struct MyCircuit { + value: Option, + enable_flag: Option, + } + + impl Circuit for MyCircuit { + type Config = EnableFlagConfig; + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let advices = [meta.advice_column(), meta.advice_column()]; + + let perm = meta.permutation( + &advices + .iter() + .map(|advice| (*advice).into()) + .collect::>>(), + ); + + EnableFlagChip::::configure(meta, advices, perm) + } + + fn synthesize( + &self, + cs: &mut impl Assignment, + config: Self::Config, + ) -> Result<(), Error> { + let mut layouter = SingleChipLayouter::new(cs)?; + let chip = EnableFlagChip::::construct(config.clone()); + + // Load the value and the enable flag into the circuit. + let value = + chip.load_private(layouter.namespace(|| "value"), config.value, self.value)?; + let enable_flag = chip.load_private( + layouter.namespace(|| "enable_flag"), + config.enable_flag, + self.enable_flag, + )?; + + // Run the enable flag logic. + chip.enable_flag(layouter.namespace(|| "swap"), value, enable_flag.into())?; + + Ok(()) + } + } + + // Test value = 1, flag = 1 case (success) + { + let circuit: MyCircuit = MyCircuit { + value: Some(Base::one()), + enable_flag: Some(Base::one()), + }; + let prover = match MockProver::::run(1, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{:?}", e), + }; + assert_eq!(prover.verify(), Ok(())); + } + + // Test value = 0, flag = 0 case (success) + { + let circuit: MyCircuit = MyCircuit { + value: Some(Base::zero()), + enable_flag: Some(Base::zero()), + }; + let prover = match MockProver::::run(1, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{:?}", e), + }; + assert_eq!(prover.verify(), Ok(())); + } + + // Test value = 0, flag = 1 case (success) + { + let circuit: MyCircuit = MyCircuit { + value: Some(Base::zero()), + enable_flag: Some(Base::one()), + }; + let prover = match MockProver::::run(1, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{:?}", e), + }; + assert_eq!(prover.verify(), Ok(())); + } + + // Test value = 1, flag = 0 case (error) + { + let circuit: MyCircuit = MyCircuit { + value: Some(Base::one()), + enable_flag: Some(Base::zero()), + }; + let prover = match MockProver::::run(1, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{:?}", e), + }; + assert_eq!( + prover.verify(), + Err(vec![VerifyFailure::Gate { + gate_index: 0, + gate_name: "Enable flag", + row: 1, + }]) + ); + } + } +} From f31b9feba02b6d68dccf5c2b61af6a7f18205cb0 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sat, 5 Jun 2021 09:40:44 +0800 Subject: [PATCH 05/10] utilities::enable_flag: Directly witness flag --- src/circuit/gadget/utilities/enable_flag.rs | 67 ++++----------------- 1 file changed, 13 insertions(+), 54 deletions(-) diff --git a/src/circuit/gadget/utilities/enable_flag.rs b/src/circuit/gadget/utilities/enable_flag.rs index 47ecf89f..2ccc4fda 100644 --- a/src/circuit/gadget/utilities/enable_flag.rs +++ b/src/circuit/gadget/utilities/enable_flag.rs @@ -1,6 +1,6 @@ use super::{copy, CellValue, UtilitiesInstructions, Var}; use halo2::{ - circuit::{Cell, Chip, Layouter}, + circuit::{Chip, Layouter}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Permutation, Selector}, poly::Rotation, }; @@ -11,16 +11,13 @@ pub trait EnableFlagInstructions: UtilitiesInstructions { /// Variable representing cell with a certain value in the circuit. type Var: Var; - /// Variable representing an `enable` boolean flag. - type Flag: From<>::Var>; - /// Given a `value` and an `enable_flag`, check that either `value = 0` /// or `enable_flag = 1`. fn enable_flag( &self, layouter: impl Layouter, value: >::Var, - enable_flag: >::Flag, + enable_flag: Option, ) -> Result<(), Error>; } @@ -52,46 +49,18 @@ impl Chip for EnableFlagChip { } } -/// A variable representing an `enable` boolean flag. -#[derive(Copy, Clone)] -pub struct Flag { - cell: Cell, - value: Option, -} - -impl From> for Flag { - fn from(var: CellValue) -> Self { - let value = var.value.map(|value| { - let zero = value == F::zero(); - let one = value == F::one(); - if zero { - false - } else if one { - true - } else { - panic!("Value must be boolean.") - } - }); - Flag { - cell: var.cell, - value, - } - } -} - impl UtilitiesInstructions for EnableFlagChip { type Var = CellValue; } impl EnableFlagInstructions for EnableFlagChip { type Var = CellValue; - type Flag = Flag; fn enable_flag( &self, mut layouter: impl Layouter, value: >::Var, - enable_flag: >::Flag, + enable_flag: Option, ) -> Result<(), Error> { let config = self.config().clone(); layouter.assign_region( @@ -100,19 +69,14 @@ impl EnableFlagInstructions for EnableFlagChip { // Enable `q_enable` selector config.q_enable.enable(&mut region, 0)?; - // Copy in `enable_flag` value - let enable_flag_val = enable_flag.value; - let enable_flag_cell = region.assign_advice( + // Witness `enable_flag` value + let enable_flag_val = enable_flag.map(|flag| F::from_u64(flag as u64)); + region.assign_advice( || "enable_flag", config.enable_flag, 0, - || { - enable_flag_val - .map(|enable_flag| F::from_u64(enable_flag as u64)) - .ok_or(Error::SynthesisError) - }, + || enable_flag_val.ok_or(Error::SynthesisError), )?; - region.constrain_equal(&config.perm, enable_flag_cell, enable_flag.cell)?; // Copy `value` copy( @@ -180,7 +144,7 @@ mod tests { fn enable_flag() { struct MyCircuit { value: Option, - enable_flag: Option, + enable_flag: Option, } impl Circuit for MyCircuit { @@ -210,14 +174,9 @@ mod tests { // Load the value and the enable flag into the circuit. let value = chip.load_private(layouter.namespace(|| "value"), config.value, self.value)?; - let enable_flag = chip.load_private( - layouter.namespace(|| "enable_flag"), - config.enable_flag, - self.enable_flag, - )?; // Run the enable flag logic. - chip.enable_flag(layouter.namespace(|| "swap"), value, enable_flag.into())?; + chip.enable_flag(layouter.namespace(|| "swap"), value, self.enable_flag)?; Ok(()) } @@ -227,7 +186,7 @@ mod tests { { let circuit: MyCircuit = MyCircuit { value: Some(Base::one()), - enable_flag: Some(Base::one()), + enable_flag: Some(true), }; let prover = match MockProver::::run(1, &circuit, vec![]) { Ok(prover) => prover, @@ -240,7 +199,7 @@ mod tests { { let circuit: MyCircuit = MyCircuit { value: Some(Base::zero()), - enable_flag: Some(Base::zero()), + enable_flag: Some(false), }; let prover = match MockProver::::run(1, &circuit, vec![]) { Ok(prover) => prover, @@ -253,7 +212,7 @@ mod tests { { let circuit: MyCircuit = MyCircuit { value: Some(Base::zero()), - enable_flag: Some(Base::one()), + enable_flag: Some(true), }; let prover = match MockProver::::run(1, &circuit, vec![]) { Ok(prover) => prover, @@ -266,7 +225,7 @@ mod tests { { let circuit: MyCircuit = MyCircuit { value: Some(Base::one()), - enable_flag: Some(Base::zero()), + enable_flag: Some(false), }; let prover = match MockProver::::run(1, &circuit, vec![]) { Ok(prover) => prover, From 6603e996edd70f56448dfdee67c80be88d436c01 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sat, 5 Jun 2021 09:43:02 +0800 Subject: [PATCH 06/10] utilities::cond_swap: Directly witness swap bit --- src/circuit/gadget/utilities/cond_swap.rs | 99 +++++++---------------- 1 file changed, 31 insertions(+), 68 deletions(-) diff --git a/src/circuit/gadget/utilities/cond_swap.rs b/src/circuit/gadget/utilities/cond_swap.rs index 566c6658..f2d6ed89 100644 --- a/src/circuit/gadget/utilities/cond_swap.rs +++ b/src/circuit/gadget/utilities/cond_swap.rs @@ -1,6 +1,6 @@ use super::{copy, CellValue, UtilitiesInstructions}; use halo2::{ - circuit::{Cell, Chip, Layouter}, + circuit::{Chip, Layouter}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Permutation, Selector}, poly::Rotation, }; @@ -11,9 +11,7 @@ pub trait CondSwapInstructions: UtilitiesInstructions { /// Variable representing cell with a certain value in the circuit. type Var; - /// Variable representing a `swap` boolean flag. - type Swap: From<>::Var>; - + #[allow(clippy::type_complexity)] /// Given an input pair (x,y) and a `swap` boolean flag, return /// (y,x) if `swap` is set, else (x,y) if `swap` is not set. fn swap( @@ -23,7 +21,7 @@ pub trait CondSwapInstructions: UtilitiesInstructions { >::Var, >::Var, ), - swap: Self::Swap, + swap: Option, ) -> Result< ( >::Var, @@ -55,40 +53,13 @@ impl Chip for CondSwapChip { #[derive(Clone, Debug)] pub struct CondSwapConfig { - q_swap: Selector, - x: Column, - y: Column, - x_swapped: Column, - y_swapped: Column, - swap: Column, - perm: Permutation, -} - -/// A variable representing a `swap` boolean flag. -#[derive(Copy, Clone)] -pub struct Swap { - cell: Cell, - value: Option, -} - -impl From> for Swap { - fn from(var: CellValue) -> Self { - let value = var.value.map(|value| { - let zero = value == F::zero(); - let one = value == F::one(); - if zero { - false - } else if one { - true - } else { - panic!("Value must be boolean.") - } - }); - Swap { - cell: var.cell, - value, - } - } + pub q_swap: Selector, + pub x: Column, + pub y: Column, + pub x_swapped: Column, + pub y_swapped: Column, + pub swap: Column, + pub perm: Permutation, } impl UtilitiesInstructions for CondSwapChip { @@ -97,8 +68,8 @@ impl UtilitiesInstructions for CondSwapChip { impl CondSwapInstructions for CondSwapChip { type Var = CellValue; - type Swap = Swap; + #[allow(clippy::type_complexity)] fn swap( &self, mut layouter: impl Layouter, @@ -106,7 +77,7 @@ impl CondSwapInstructions for CondSwapChip { >::Var, >::Var, ), - swap: Self::Swap, + swap: Option, ) -> Result< ( >::Var, @@ -128,27 +99,22 @@ impl CondSwapInstructions for CondSwapChip { // Copy in `y` value let y = copy(&mut region, || "copy y", config.y, 0, &pair.1, &config.perm)?; - // Copy in `swap` value - let swap_val = swap.value; - let swap_cell = region.assign_advice( + // Witness `swap` value + let swap_val = swap.map(|swap| F::from_u64(swap as u64)); + region.assign_advice( || "swap", config.swap, 0, - || { - swap_val - .map(|swap| F::from_u64(swap as u64)) - .ok_or(Error::SynthesisError) - }, + || swap_val.ok_or(Error::SynthesisError), )?; - region.constrain_equal(&config.perm, swap_cell, swap.cell)?; // Conditionally swap x let x_swapped = { - let x_swapped = - x.value - .zip(y.value) - .zip(swap_val) - .map(|((x, y), swap)| if swap { y } else { x }); + let x_swapped = x + .value + .zip(y.value) + .zip(swap) + .map(|((x, y), swap)| if swap { y } else { x }); let x_swapped_cell = region.assign_advice( || "x_swapped", config.x_swapped, @@ -163,11 +129,11 @@ impl CondSwapInstructions for CondSwapChip { // Conditionally swap y let y_swapped = { - let y_swapped = - x.value - .zip(y.value) - .zip(swap_val) - .map(|((x, y), swap)| if swap { x } else { y }); + let y_swapped = x + .value + .zip(y.value) + .zip(swap) + .map(|((x, y), swap)| if swap { x } else { y }); let y_swapped_cell = region.assign_advice( || "y_swapped", config.y_swapped, @@ -266,7 +232,7 @@ mod tests { struct MyCircuit { x: Option, y: Option, - swap: Option, + swap: Option, } impl Circuit for MyCircuit { @@ -302,15 +268,12 @@ mod tests { // Load the pair and the swap flag into the circuit. let x = chip.load_private(layouter.namespace(|| "x"), config.x, self.x)?; let y = chip.load_private(layouter.namespace(|| "y"), config.y, self.y)?; - let swap = - chip.load_private(layouter.namespace(|| "swap"), config.swap, self.swap)?; - // Return the swapped pair. let swapped_pair = - chip.swap(layouter.namespace(|| "swap"), (x, y).into(), swap.into())?; + chip.swap(layouter.namespace(|| "swap"), (x, y).into(), self.swap)?; if let Some(swap) = self.swap { - if swap == F::one() { + if swap { // Check that `x` and `y` have been swapped assert_eq!(swapped_pair.0.value.unwrap(), y.value.unwrap()); assert_eq!(swapped_pair.1.value.unwrap(), x.value.unwrap()); @@ -330,7 +293,7 @@ mod tests { let circuit: MyCircuit = MyCircuit { x: Some(Base::rand()), y: Some(Base::rand()), - swap: Some(Base::one()), + swap: Some(true), }; let prover = match MockProver::::run(3, &circuit, vec![]) { Ok(prover) => prover, @@ -344,7 +307,7 @@ mod tests { let circuit: MyCircuit = MyCircuit { x: Some(Base::rand()), y: Some(Base::rand()), - swap: Some(Base::zero()), + swap: Some(false), }; let prover = match MockProver::::run(3, &circuit, vec![]) { Ok(prover) => prover, From 40599144bf972bcd5039391a4731ecc6eafdf58b Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sat, 5 Jun 2021 11:02:22 +0800 Subject: [PATCH 07/10] utilities::plonk: Remove assumption that fixed columns default to 1 --- src/circuit/gadget/utilities/plonk.rs | 55 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/circuit/gadget/utilities/plonk.rs b/src/circuit/gadget/utilities/plonk.rs index ab41dc63..a6437723 100644 --- a/src/circuit/gadget/utilities/plonk.rs +++ b/src/circuit/gadget/utilities/plonk.rs @@ -92,8 +92,6 @@ impl PLONKInstructions for PLONKChip { || "mul", |mut region| { let config = self.config().clone(); - let sc = sc.unwrap_or_else(F::one); - let sm = sm.unwrap_or_else(F::one); // Copy in `a` copy(&mut region, || "copy a", config.a, 0, &a, &config.perm)?; @@ -105,16 +103,22 @@ impl PLONKInstructions for PLONKChip { copy(&mut region, || "copy c", config.c, 0, &c, &config.perm)?; // Assign fixed columns - region.assign_fixed(|| "sc", config.sc, 0, || Ok(sc))?; - region.assign_fixed(|| "a * (sm) * b", config.sm, 0, || Ok(sm))?; + region.assign_fixed(|| "sc", config.sc, 0, || sc.ok_or(Error::SynthesisError))?; + region.assign_fixed( + || "a * (sm) * b", + config.sm, + 0, + || sm.ok_or(Error::SynthesisError), + )?; #[cfg(test)] // Checks that a * sm * b = c * sc { - let a = a.value.unwrap(); - let b = b.value.unwrap(); - let c = c.value.unwrap(); - assert_eq!(a * sm * b, c * sc); + if let (Some(a), Some(b), Some(c), Some(sm), Some(sc)) = + (a.value, b.value, c.value, sm, sc) + { + assert_eq!(a * sm * b, c * sc); + } } Ok(()) @@ -133,9 +137,6 @@ impl PLONKInstructions for PLONKChip { sc: Option, ) -> Result<(), Error> { let config = self.config().clone(); - let sa = sa.unwrap_or_else(F::one); - let sb = sb.unwrap_or_else(F::one); - let sc = sc.unwrap_or_else(F::one); layouter.assign_region( || "add", @@ -150,17 +151,18 @@ impl PLONKInstructions for PLONKChip { copy(&mut region, || "copy c", config.c, 0, &c, &config.perm)?; // Assign fixed columns - region.assign_fixed(|| "a", config.sa, 0, || Ok(sa))?; - region.assign_fixed(|| "b", config.sb, 0, || Ok(sb))?; - region.assign_fixed(|| "c", config.sc, 0, || Ok(sc))?; + region.assign_fixed(|| "a", config.sa, 0, || sa.ok_or(Error::SynthesisError))?; + region.assign_fixed(|| "b", config.sb, 0, || sb.ok_or(Error::SynthesisError))?; + region.assign_fixed(|| "c", config.sc, 0, || sc.ok_or(Error::SynthesisError))?; #[cfg(test)] // Checks that a * sa + b * sb = c * sc { - let a = a.value.unwrap(); - let b = b.value.unwrap(); - let c = c.value.unwrap(); - assert_eq!(a * sa + b * sb, c * sc); + if let (Some(a), Some(b), Some(c), Some(sa), Some(sb), Some(sc)) = + (a.value, b.value, c.value, sa, sb, sc) + { + assert_eq!(a * sa + b * sb, c * sc); + } } Ok(()) @@ -277,9 +279,9 @@ mod tests { a, b, c, - None, - None, - None, + Some(F::one()), + Some(F::one()), + Some(F::one()), )?; } @@ -287,7 +289,14 @@ mod tests { { let c = self.a.zip(self.b).map(|(a, b)| a * b); let c = chip.load_private(layouter.namespace(|| "c"), config.c, c)?; - chip.mul(layouter.namespace(|| "a * b = c"), a, b, c, None, None)?; + chip.mul( + layouter.namespace(|| "a * b = c"), + a, + b, + c, + Some(F::one()), + Some(F::one()), + )?; } // 2a + 3b = c @@ -304,7 +313,7 @@ mod tests { c, Some(F::from_u64(2)), Some(F::from_u64(3)), - None, + Some(F::one()), )?; } From 0f2dfc550807b9a98143c3cdfdeb90a65a5496bb Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 8 Jun 2021 00:20:09 +0800 Subject: [PATCH 08/10] Use UtilitiesInstructions::Var instead of internal associated type. Co-authored-by: Jack Grigg --- src/circuit/gadget/utilities/cond_swap.rs | 31 +++------------------ src/circuit/gadget/utilities/enable_flag.rs | 11 ++------ src/circuit/gadget/utilities/plonk.rs | 28 ++++++++----------- 3 files changed, 19 insertions(+), 51 deletions(-) diff --git a/src/circuit/gadget/utilities/cond_swap.rs b/src/circuit/gadget/utilities/cond_swap.rs index f2d6ed89..8a805b7d 100644 --- a/src/circuit/gadget/utilities/cond_swap.rs +++ b/src/circuit/gadget/utilities/cond_swap.rs @@ -8,27 +8,15 @@ use pasta_curves::arithmetic::FieldExt; use std::marker::PhantomData; pub trait CondSwapInstructions: UtilitiesInstructions { - /// Variable representing cell with a certain value in the circuit. - type Var; - #[allow(clippy::type_complexity)] /// Given an input pair (x,y) and a `swap` boolean flag, return /// (y,x) if `swap` is set, else (x,y) if `swap` is not set. fn swap( &self, layouter: impl Layouter, - pair: ( - >::Var, - >::Var, - ), + pair: (Self::Var, Self::Var), swap: Option, - ) -> Result< - ( - >::Var, - >::Var, - ), - Error, - >; + ) -> Result<(Self::Var, Self::Var), Error>; } /// A chip implementing a conditional swap. @@ -67,24 +55,13 @@ impl UtilitiesInstructions for CondSwapChip { } impl CondSwapInstructions for CondSwapChip { - type Var = CellValue; - #[allow(clippy::type_complexity)] fn swap( &self, mut layouter: impl Layouter, - pair: ( - >::Var, - >::Var, - ), + pair: (Self::Var, Self::Var), swap: Option, - ) -> Result< - ( - >::Var, - >::Var, - ), - Error, - > { + ) -> Result<(Self::Var, Self::Var), Error> { let config = self.config(); layouter.assign_region( diff --git a/src/circuit/gadget/utilities/enable_flag.rs b/src/circuit/gadget/utilities/enable_flag.rs index 2ccc4fda..2497f3a3 100644 --- a/src/circuit/gadget/utilities/enable_flag.rs +++ b/src/circuit/gadget/utilities/enable_flag.rs @@ -1,4 +1,4 @@ -use super::{copy, CellValue, UtilitiesInstructions, Var}; +use super::{copy, CellValue, UtilitiesInstructions}; use halo2::{ circuit::{Chip, Layouter}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Permutation, Selector}, @@ -8,15 +8,12 @@ use pasta_curves::arithmetic::FieldExt; use std::marker::PhantomData; pub trait EnableFlagInstructions: UtilitiesInstructions { - /// Variable representing cell with a certain value in the circuit. - type Var: Var; - /// Given a `value` and an `enable_flag`, check that either `value = 0` /// or `enable_flag = 1`. fn enable_flag( &self, layouter: impl Layouter, - value: >::Var, + value: Self::Var, enable_flag: Option, ) -> Result<(), Error>; } @@ -54,12 +51,10 @@ impl UtilitiesInstructions for EnableFlagChip { } impl EnableFlagInstructions for EnableFlagChip { - type Var = CellValue; - fn enable_flag( &self, mut layouter: impl Layouter, - value: >::Var, + value: Self::Var, enable_flag: Option, ) -> Result<(), Error> { let config = self.config().clone(); diff --git a/src/circuit/gadget/utilities/plonk.rs b/src/circuit/gadget/utilities/plonk.rs index a6437723..29d7c889 100644 --- a/src/circuit/gadget/utilities/plonk.rs +++ b/src/circuit/gadget/utilities/plonk.rs @@ -10,15 +10,13 @@ use std::marker::PhantomData; #[allow(clippy::upper_case_acronyms)] #[allow(clippy::too_many_arguments)] pub trait PLONKInstructions: UtilitiesInstructions { - type Var; - // Checks that a * sm * b = c * sc fn mul( &self, layouter: impl Layouter, - a: >::Var, - b: >::Var, - c: >::Var, + a: Self::Var, + b: Self::Var, + c: Self::Var, sc: Option, sm: Option, ) -> Result<(), Error>; @@ -26,9 +24,9 @@ pub trait PLONKInstructions: UtilitiesInstructions { fn add( &self, layouter: impl Layouter, - a: >::Var, - b: >::Var, - c: >::Var, + a: Self::Var, + b: Self::Var, + c: Self::Var, sa: Option, sb: Option, sc: Option, @@ -77,14 +75,12 @@ impl UtilitiesInstructions for PLONKChip { #[allow(clippy::upper_case_acronyms)] impl PLONKInstructions for PLONKChip { - type Var = CellValue; - fn mul( &self, mut layouter: impl Layouter, - a: >::Var, - b: >::Var, - c: >::Var, + a: Self::Var, + b: Self::Var, + c: Self::Var, sc: Option, sm: Option, ) -> Result<(), Error> { @@ -129,9 +125,9 @@ impl PLONKInstructions for PLONKChip { fn add( &self, mut layouter: impl Layouter, - a: >::Var, - b: >::Var, - c: >::Var, + a: Self::Var, + b: Self::Var, + c: Self::Var, sa: Option, sb: Option, sc: Option, From 54c8cfd1d0c3e9a1642f94b7b44d6bc71bb64158 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 8 Jun 2021 00:28:32 +0800 Subject: [PATCH 09/10] Documentation improvements and minor refactors. Co-authored-by: Jack Grigg --- src/circuit/gadget/utilities.rs | 9 +- src/circuit/gadget/utilities/cond_swap.rs | 153 ++++++++++---------- src/circuit/gadget/utilities/enable_flag.rs | 23 +-- src/circuit/gadget/utilities/plonk.rs | 8 +- 4 files changed, 91 insertions(+), 102 deletions(-) diff --git a/src/circuit/gadget/utilities.rs b/src/circuit/gadget/utilities.rs index 041fbbfd..58535f5f 100644 --- a/src/circuit/gadget/utilities.rs +++ b/src/circuit/gadget/utilities.rs @@ -59,12 +59,15 @@ pub trait UtilitiesInstructions: Chip { } } -/// Assign a cell the same value as another cell and set up a copy constraint between them. +/// Assigns a cell at a specific offset within the given region, constraining it +/// to the same value as another cell (which may be in any region). +/// +/// Returns an error if either `column` or `copy` is not within `perm`. pub fn copy( region: &mut Region<'_, F>, annotation: A, column: Column, - row: usize, + offset: usize, copy: &CellValue, perm: &Permutation, ) -> Result, Error> @@ -72,7 +75,7 @@ where A: Fn() -> AR, AR: Into, { - let cell = region.assign_advice(annotation, column, row, || { + let cell = region.assign_advice(annotation, column, offset, || { copy.value.ok_or(Error::SynthesisError) })?; diff --git a/src/circuit/gadget/utilities/cond_swap.rs b/src/circuit/gadget/utilities/cond_swap.rs index 8a805b7d..1015b54f 100644 --- a/src/circuit/gadget/utilities/cond_swap.rs +++ b/src/circuit/gadget/utilities/cond_swap.rs @@ -5,12 +5,12 @@ use halo2::{ poly::Rotation, }; use pasta_curves::arithmetic::FieldExt; -use std::marker::PhantomData; +use std::{array, marker::PhantomData}; pub trait CondSwapInstructions: UtilitiesInstructions { #[allow(clippy::type_complexity)] - /// Given an input pair (x,y) and a `swap` boolean flag, return - /// (y,x) if `swap` is set, else (x,y) if `swap` is not set. + /// Given an input pair (a,b) and a `swap` boolean flag, returns + /// (b,a) if `swap` is set, else (a,b) if `swap` is not set. fn swap( &self, layouter: impl Layouter, @@ -42,10 +42,10 @@ impl Chip for CondSwapChip { #[derive(Clone, Debug)] pub struct CondSwapConfig { pub q_swap: Selector, - pub x: Column, - pub y: Column, - pub x_swapped: Column, - pub y_swapped: Column, + pub a: Column, + pub b: Column, + pub a_swapped: Column, + pub b_swapped: Column, pub swap: Column, pub perm: Permutation, } @@ -70,11 +70,11 @@ impl CondSwapInstructions for CondSwapChip { // Enable `q_swap` selector config.q_swap.enable(&mut region, 0)?; - // Copy in `x` value - let x = copy(&mut region, || "copy x", config.x, 0, &pair.0, &config.perm)?; + // Copy in `a` value + let a = copy(&mut region, || "copy a", config.a, 0, &pair.0, &config.perm)?; - // Copy in `y` value - let y = copy(&mut region, || "copy y", config.y, 0, &pair.1, &config.perm)?; + // Copy in `b` value + let b = copy(&mut region, || "copy b", config.b, 0, &pair.1, &config.perm)?; // Witness `swap` value let swap_val = swap.map(|swap| F::from_u64(swap as u64)); @@ -85,46 +85,46 @@ impl CondSwapInstructions for CondSwapChip { || swap_val.ok_or(Error::SynthesisError), )?; - // Conditionally swap x - let x_swapped = { - let x_swapped = x + // Conditionally swap a + let a_swapped = { + let a_swapped = a .value - .zip(y.value) + .zip(b.value) .zip(swap) - .map(|((x, y), swap)| if swap { y } else { x }); - let x_swapped_cell = region.assign_advice( - || "x_swapped", - config.x_swapped, + .map(|((a, b), swap)| if swap { b } else { a }); + let a_swapped_cell = region.assign_advice( + || "a_swapped", + config.a_swapped, 0, - || x_swapped.ok_or(Error::SynthesisError), + || a_swapped.ok_or(Error::SynthesisError), )?; CellValue { - cell: x_swapped_cell, - value: x_swapped, + cell: a_swapped_cell, + value: a_swapped, } }; - // Conditionally swap y - let y_swapped = { - let y_swapped = x + // Conditionally swap b + let b_swapped = { + let b_swapped = a .value - .zip(y.value) + .zip(b.value) .zip(swap) - .map(|((x, y), swap)| if swap { x } else { y }); - let y_swapped_cell = region.assign_advice( - || "y_swapped", - config.y_swapped, + .map(|((a, b), swap)| if swap { a } else { b }); + let b_swapped_cell = region.assign_advice( + || "b_swapped", + config.b_swapped, 0, - || y_swapped.ok_or(Error::SynthesisError), + || b_swapped.ok_or(Error::SynthesisError), )?; CellValue { - cell: y_swapped_cell, - value: y_swapped, + cell: b_swapped_cell, + value: b_swapped, } }; // Return swapped pair - Ok((x_swapped, y_swapped)) + Ok((a_swapped, b_swapped)) }, ) } @@ -132,6 +132,9 @@ impl CondSwapInstructions for CondSwapChip { impl CondSwapChip { /// Configures this chip for use in a circuit. + /// + /// `perm` must cover `advices[0..2]`, as well as any columns that will + /// be passed to this chip. pub fn configure( meta: &mut ConstraintSystem, advices: [Column; 5], @@ -141,44 +144,43 @@ impl CondSwapChip { let config = CondSwapConfig { q_swap, - x: advices[0], - y: advices[1], - x_swapped: advices[2], - y_swapped: advices[3], + a: advices[0], + b: advices[1], + a_swapped: advices[2], + b_swapped: advices[3], swap: advices[4], perm, }; // TODO: optimise shape of gate for Merkle path validation - meta.create_gate("x' = y ⋅ swap + x ⋅ (1-swap)", |meta| { + meta.create_gate("a' = b ⋅ swap + a ⋅ (1-swap)", |meta| { let q_swap = meta.query_selector(q_swap, Rotation::cur()); - let x = meta.query_advice(config.x, Rotation::cur()); - let y = meta.query_advice(config.y, Rotation::cur()); - let x_swapped = meta.query_advice(config.x_swapped, Rotation::cur()); - let y_swapped = meta.query_advice(config.y_swapped, Rotation::cur()); + let a = meta.query_advice(config.a, Rotation::cur()); + let b = meta.query_advice(config.b, Rotation::cur()); + let a_swapped = meta.query_advice(config.a_swapped, Rotation::cur()); + let b_swapped = meta.query_advice(config.b_swapped, Rotation::cur()); let swap = meta.query_advice(config.swap, Rotation::cur()); let one = Expression::Constant(F::one()); - // x_swapped - y ⋅ swap - x ⋅ (1-swap) = 0 - // This checks that `x_swapped` is equal to `y` when `swap` is set, - // but remains as `x` when `swap` is not set. - let x_check = - x_swapped - y.clone() * swap.clone() - x.clone() * (one.clone() - swap.clone()); + // a_swapped - b ⋅ swap - a ⋅ (1-swap) = 0 + // This checks that `a_swapped` is equal to `y` when `swap` is set, + // but remains as `a` when `swap` is not set. + let a_check = + a_swapped - b.clone() * swap.clone() - a.clone() * (one.clone() - swap.clone()); - // y_swapped - x ⋅ swap - y ⋅ (1-swap) = 0 - // This checks that `y_swapped` is equal to `x` when `swap` is set, - // but remains as `y` when `swap` is not set. - let y_check = y_swapped - x * swap.clone() - y * (one.clone() - swap.clone()); + // b_swapped - a ⋅ swap - b ⋅ (1-swap) = 0 + // This checks that `b_swapped` is equal to `a` when `swap` is set, + // but remains as `b` when `swap` is not set. + let b_check = b_swapped - a * swap.clone() - b * (one.clone() - swap.clone()); // Check `swap` is boolean. let bool_check = swap.clone() * (one - swap); - [x_check, y_check, bool_check] - .iter() - .map(|poly| q_swap.clone() * poly.clone()) + array::IntoIter::new([a_check, b_check, bool_check]) + .map(|poly| q_swap.clone() * poly) .collect() }); @@ -207,8 +209,8 @@ mod tests { #[test] fn cond_swap() { struct MyCircuit { - x: Option, - y: Option, + a: Option, + b: Option, swap: Option, } @@ -243,21 +245,20 @@ mod tests { let chip = CondSwapChip::::construct(config.clone()); // Load the pair and the swap flag into the circuit. - let x = chip.load_private(layouter.namespace(|| "x"), config.x, self.x)?; - let y = chip.load_private(layouter.namespace(|| "y"), config.y, self.y)?; + let a = chip.load_private(layouter.namespace(|| "a"), config.a, self.a)?; + let b = chip.load_private(layouter.namespace(|| "b"), config.b, self.b)?; // Return the swapped pair. - let swapped_pair = - chip.swap(layouter.namespace(|| "swap"), (x, y).into(), self.swap)?; + let swapped_pair = chip.swap(layouter.namespace(|| "swap"), (a, b), self.swap)?; if let Some(swap) = self.swap { if swap { - // Check that `x` and `y` have been swapped - assert_eq!(swapped_pair.0.value.unwrap(), y.value.unwrap()); - assert_eq!(swapped_pair.1.value.unwrap(), x.value.unwrap()); + // Check that `a` and `b` have been swapped + assert_eq!(swapped_pair.0.value.unwrap(), b.value.unwrap()); + assert_eq!(swapped_pair.1.value.unwrap(), a.value.unwrap()); } else { - // Check that `x` and `y` have not been swapped - assert_eq!(swapped_pair.0.value.unwrap(), x.value.unwrap()); - assert_eq!(swapped_pair.1.value.unwrap(), y.value.unwrap()); + // Check that `a` and `b` have not been swapped + assert_eq!(swapped_pair.0.value.unwrap(), a.value.unwrap()); + assert_eq!(swapped_pair.1.value.unwrap(), b.value.unwrap()); } } @@ -268,28 +269,22 @@ mod tests { // Test swap case { let circuit: MyCircuit = MyCircuit { - x: Some(Base::rand()), - y: Some(Base::rand()), + a: Some(Base::rand()), + b: Some(Base::rand()), swap: Some(true), }; - let prover = match MockProver::::run(3, &circuit, vec![]) { - Ok(prover) => prover, - Err(e) => panic!("{:?}", e), - }; + let prover = MockProver::::run(3, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } // Test non-swap case { let circuit: MyCircuit = MyCircuit { - x: Some(Base::rand()), - y: Some(Base::rand()), + a: Some(Base::rand()), + b: Some(Base::rand()), swap: Some(false), }; - let prover = match MockProver::::run(3, &circuit, vec![]) { - Ok(prover) => prover, - Err(e) => panic!("{:?}", e), - }; + let prover = MockProver::::run(3, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } } diff --git a/src/circuit/gadget/utilities/enable_flag.rs b/src/circuit/gadget/utilities/enable_flag.rs index 2497f3a3..5da59aa8 100644 --- a/src/circuit/gadget/utilities/enable_flag.rs +++ b/src/circuit/gadget/utilities/enable_flag.rs @@ -91,6 +91,9 @@ impl EnableFlagInstructions for EnableFlagChip { impl EnableFlagChip { /// Configures this chip for use in a circuit. + /// + /// `perm` must cover `advices[0]`, as well as any columns that will be + /// passed to this chip. pub fn configure( meta: &mut ConstraintSystem, advices: [Column; 2], @@ -183,10 +186,7 @@ mod tests { value: Some(Base::one()), enable_flag: Some(true), }; - let prover = match MockProver::::run(1, &circuit, vec![]) { - Ok(prover) => prover, - Err(e) => panic!("{:?}", e), - }; + let prover = MockProver::::run(1, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } @@ -196,10 +196,7 @@ mod tests { value: Some(Base::zero()), enable_flag: Some(false), }; - let prover = match MockProver::::run(1, &circuit, vec![]) { - Ok(prover) => prover, - Err(e) => panic!("{:?}", e), - }; + let prover = MockProver::::run(1, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } @@ -209,10 +206,7 @@ mod tests { value: Some(Base::zero()), enable_flag: Some(true), }; - let prover = match MockProver::::run(1, &circuit, vec![]) { - Ok(prover) => prover, - Err(e) => panic!("{:?}", e), - }; + let prover = MockProver::::run(1, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } @@ -222,10 +216,7 @@ mod tests { value: Some(Base::one()), enable_flag: Some(false), }; - let prover = match MockProver::::run(1, &circuit, vec![]) { - Ok(prover) => prover, - Err(e) => panic!("{:?}", e), - }; + let prover = MockProver::::run(1, &circuit, vec![]).unwrap(); assert_eq!( prover.verify(), Err(vec![VerifyFailure::Gate { diff --git a/src/circuit/gadget/utilities/plonk.rs b/src/circuit/gadget/utilities/plonk.rs index 29d7c889..f3edb0b5 100644 --- a/src/circuit/gadget/utilities/plonk.rs +++ b/src/circuit/gadget/utilities/plonk.rs @@ -170,6 +170,9 @@ impl PLONKInstructions for PLONKChip { #[allow(clippy::upper_case_acronyms)] impl PLONKChip { /// Configures this chip for use in a circuit. + /// + /// `perm` must cover `advices`, as well as any columns that will be passed + /// to this chip. pub fn configure( meta: &mut ConstraintSystem, advices: [Column; 3], @@ -335,10 +338,7 @@ mod tests { a: Some(Base::rand()), b: Some(Base::rand()), }; - let prover = match MockProver::::run(3, &circuit, vec![]) { - Ok(prover) => prover, - Err(e) => panic!("{:?}", e), - }; + let prover = MockProver::::run(3, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } } From 3ff307f9464ee00d8f606cb7df2a849d46c791f3 Mon Sep 17 00:00:00 2001 From: str4d Date: Mon, 7 Jun 2021 19:34:48 +0100 Subject: [PATCH 10/10] docs: Clarify EnableFlagInstructions::enable_flag --- src/circuit/gadget/utilities/enable_flag.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/circuit/gadget/utilities/enable_flag.rs b/src/circuit/gadget/utilities/enable_flag.rs index 5da59aa8..a024f634 100644 --- a/src/circuit/gadget/utilities/enable_flag.rs +++ b/src/circuit/gadget/utilities/enable_flag.rs @@ -8,8 +8,7 @@ use pasta_curves::arithmetic::FieldExt; use std::marker::PhantomData; pub trait EnableFlagInstructions: UtilitiesInstructions { - /// Given a `value` and an `enable_flag`, check that either `value = 0` - /// or `enable_flag = 1`. + /// Enforces that `value` be zero or, if non-zero, that `enable_flag` must be 1. fn enable_flag( &self, layouter: impl Layouter,