From 4f878152623c1beb05c37a625fa4bbd98260aed5 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 3 Jun 2021 09:41:54 +0800 Subject: [PATCH] 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(())); + } +}