From cba0d8672bb80d1373689dde27763be5fc9fe2e1 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 23 Jul 2021 17:58:37 +0800 Subject: [PATCH] Remove standard PLONK helper. The Action circuit only used standard PLONK in one place. Since it used non-binary selectors, it cannot be optimised by the halo2 selector optimisations. We now replace it with a custom gate which uses a binary selector. --- src/circuit.rs | 79 +++--- src/circuit/gadget.rs | 5 - src/circuit/gadget/utilities.rs | 1 - src/circuit/gadget/utilities/plonk.rs | 339 -------------------------- 4 files changed, 47 insertions(+), 377 deletions(-) delete mode 100644 src/circuit/gadget/utilities/plonk.rs diff --git a/src/circuit.rs b/src/circuit.rs index 8ad2b02f..f8e97b20 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -53,11 +53,7 @@ use gadget::{ }, note_commit::NoteCommitConfig, }, - utilities::{ - copy, - plonk::{PLONKChip, PLONKConfig, PLONKInstructions}, - CellValue, UtilitiesInstructions, Var, - }, + utilities::{copy, CellValue, UtilitiesInstructions, Var}, }; use std::convert::TryInto; @@ -85,10 +81,11 @@ const ENABLE_OUTPUT: usize = 8; pub struct Config { primary: Column, q_orchard: Selector, + // Selector for the field addition gate poseidon_hash(nk, rho_old) + psi_old. + q_add: Selector, advices: [Column; 10], ecc_config: EccConfig, poseidon_config: PoseidonConfig, - plonk_config: PLONKConfig, merkle_config_1: MerkleConfig, merkle_config_2: MerkleConfig, sinsemilla_config_1: SinsemillaConfig, @@ -186,6 +183,17 @@ impl plonk::Circuit for Circuit { .map(move |(name, poly)| (name, q_orchard.clone() * poly)) }); + // Addition of two field elements poseidon_hash(nk, rho_old) + psi_old. + let q_add = meta.selector(); + meta.create_gate("poseidon_hash(nk, rho_old) + psi_old", |meta| { + let q_add = meta.query_selector(q_add); + let sum = meta.query_advice(advices[0], Rotation::cur()); + let nk_rho_old = meta.query_advice(advices[1], Rotation::cur()); + let psi_old = meta.query_advice(advices[2], Rotation::cur()); + + vec![q_add * (nk_rho_old + psi_old - sum)] + }); + // Fixed columns for the Sinsemilla generator lookup table let table_idx = meta.fixed_column(); let lookup = (table_idx, meta.fixed_column(), meta.fixed_column()); @@ -239,9 +247,6 @@ impl plonk::Circuit for Circuit { rc_b, ); - // Configuration for standard PLONK (addition and multiplication). - let plonk_config = PLONKChip::configure(meta, [advices[0], advices[1], advices[2]]); - // Configuration for a Sinsemilla hash instantiation and a // Merkle hash instantiation using this Sinsemilla instance. // Since the Sinsemilla config uses only 5 advice columns, @@ -294,10 +299,10 @@ impl plonk::Circuit for Circuit { Config { primary, q_orchard, + q_add, advices, ecc_config, poseidon_config, - plonk_config, merkle_config_1, merkle_config_2, sinsemilla_config_1, @@ -504,32 +509,42 @@ impl plonk::Circuit for Circuit { poseidon_output }; - // Add hash output to psi using standard PLONK + // Add hash output to psi. // `scalar` = poseidon_hash(nk, rho_old) + psi_old. // - let scalar = { - let scalar_val = nk_rho_old - .value() - .zip(psi_old.value()) - .map(|(nk_rho_old, psi_old)| nk_rho_old + psi_old); - let scalar = self.load_private( - layouter.namespace(|| "poseidon_hash(nk, rho_old) + psi_old"), - config.advices[0], - scalar_val, - )?; + let scalar = layouter.assign_region( + || " `scalar` = poseidon_hash(nk, rho_old) + psi_old", + |mut region| { + config.q_add.enable(&mut region, 0)?; - config.plonk_chip().add( - layouter.namespace(|| "poseidon_hash(nk, rho_old) + psi_old"), - nk_rho_old, - psi_old, - scalar, - Some(pallas::Base::one()), - Some(pallas::Base::one()), - Some(pallas::Base::one()), - )?; + copy( + &mut region, + || "copy nk_rho_old", + config.advices[1], + 0, + &nk_rho_old, + )?; + copy( + &mut region, + || "copy psi_old", + config.advices[2], + 0, + &psi_old, + )?; - scalar - }; + let scalar_val = nk_rho_old + .value() + .zip(psi_old.value()) + .map(|(nk_rho_old, psi_old)| nk_rho_old + psi_old); + let cell = region.assign_advice( + || "poseidon_hash(nk, rho_old) + psi_old", + config.advices[0], + 0, + || scalar_val.ok_or(plonk::Error::SynthesisError), + )?; + Ok(CellValue::new(cell, scalar_val)) + }, + )?; // Multiply scalar by NullifierK // `product` = [poseidon_hash(nk, rho_old) + psi_old] NullifierK. diff --git a/src/circuit/gadget.rs b/src/circuit/gadget.rs index 866e44dd..52c7723e 100644 --- a/src/circuit/gadget.rs +++ b/src/circuit/gadget.rs @@ -3,7 +3,6 @@ use pasta_curves::pallas; use ecc::chip::EccChip; use poseidon::Pow5T3Chip as PoseidonChip; use sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip}; -use utilities::plonk::PLONKChip; pub(crate) mod ecc; pub(crate) mod poseidon; @@ -11,10 +10,6 @@ pub(crate) mod sinsemilla; pub(crate) mod utilities; impl super::Config { - pub(super) fn plonk_chip(&self) -> PLONKChip { - PLONKChip::construct(self.plonk_config.clone()) - } - pub(super) fn ecc_chip(&self) -> EccChip { EccChip::construct(self.ecc_config.clone()) } diff --git a/src/circuit/gadget/utilities.rs b/src/circuit/gadget/utilities.rs index ec607c55..eed3c61e 100644 --- a/src/circuit/gadget/utilities.rs +++ b/src/circuit/gadget/utilities.rs @@ -9,7 +9,6 @@ use std::{array, convert::TryInto, ops::Range}; pub(crate) mod cond_swap; pub(crate) mod decompose_running_sum; pub(crate) mod lookup_range_check; -pub(crate) mod plonk; /// A variable representing a field element. #[derive(Copy, Clone, Debug)] diff --git a/src/circuit/gadget/utilities/plonk.rs b/src/circuit/gadget/utilities/plonk.rs deleted file mode 100644 index 50d57f55..00000000 --- a/src/circuit/gadget/utilities/plonk.rs +++ /dev/null @@ -1,339 +0,0 @@ -use super::{copy, CellValue, UtilitiesInstructions}; -use halo2::{ - circuit::{Chip, Layouter}, - plonk::{Advice, Column, ConstraintSystem, Error, Fixed}, - 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 { - // Checks that a * sm * b = c * sc - fn mul( - &self, - layouter: impl Layouter, - a: Self::Var, - b: Self::Var, - c: Self::Var, - sc: Option, - sm: Option, - ) -> Result<(), Error>; - // Checks that a * sa + b * sb = c * sc - fn add( - &self, - layouter: impl Layouter, - a: Self::Var, - b: Self::Var, - c: Self::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, -} - -#[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 { - fn mul( - &self, - mut layouter: impl Layouter, - a: Self::Var, - b: Self::Var, - c: Self::Var, - sc: Option, - sm: Option, - ) -> Result<(), Error> { - layouter.assign_region( - || "mul", - |mut region| { - let config = self.config().clone(); - - // Copy in `a` - copy(&mut region, || "copy a", config.a, 0, &a)?; - - // Copy in `b` - copy(&mut region, || "copy b", config.b, 0, &b)?; - - // Copy in `c` - copy(&mut region, || "copy c", config.c, 0, &c)?; - - // Assign fixed columns - 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 - { - 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(()) - }, - ) - } - - fn add( - &self, - mut layouter: impl Layouter, - a: Self::Var, - b: Self::Var, - c: Self::Var, - sa: Option, - sb: Option, - sc: Option, - ) -> Result<(), Error> { - let config = self.config().clone(); - - layouter.assign_region( - || "add", - |mut region| { - // Copy in `a` - copy(&mut region, || "copy a", config.a, 0, &a)?; - - // Copy in `b` - copy(&mut region, || "copy b", config.b, 0, &b)?; - - // Copy in `c` - copy(&mut region, || "copy c", config.c, 0, &c)?; - - // Assign fixed columns - 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 - { - 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(()) - }, - ) - } -} - -#[allow(clippy::upper_case_acronyms)] -impl PLONKChip { - /// Configures this chip for use in a circuit. - /// - /// # Side-effects - /// - /// All columns in `advices` will be equality-enabled. - pub fn configure(meta: &mut ConstraintSystem, advices: [Column; 3]) -> PLONKConfig { - let a = advices[0]; - let b = advices[1]; - let c = advices[2]; - meta.enable_equality(a.into()); - meta.enable_equality(b.into()); - meta.enable_equality(c.into()); - - 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, - } - } - - 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, SimpleFloorPlanner}, - dev::MockProver, - plonk::{Circuit, ConstraintSystem, Error}, - }; - use pasta_curves::{arithmetic::FieldExt, pallas::Base}; - - #[test] - fn plonk_util() { - #[derive(Default)] - struct MyCircuit { - a: Option, - b: Option, - } - - impl Circuit for MyCircuit { - type Config = PLONKConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let advices = [ - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - ]; - - PLONKChip::::configure(meta, advices) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - 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, - Some(F::one()), - Some(F::one()), - Some(F::one()), - )?; - } - - // 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, - Some(F::one()), - Some(F::one()), - )?; - } - - // 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)), - Some(F::one()), - )?; - } - - // 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 = MockProver::::run(4, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); - } -}