diff --git a/src/circuit.rs b/src/circuit.rs index acebfde2..ced02210 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -5,7 +5,7 @@ use std::mem; use group::{Curve, GroupEncoding}; use halo2::{ circuit::{Layouter, SimpleFloorPlanner}, - plonk::{self, Advice, Column, Instance as InstanceColumn, Selector}, + plonk::{self, Advice, Column, Expression, Instance as InstanceColumn, Selector}, poly::Rotation, transcript::{Blake2bRead, Blake2bWrite}, }; @@ -55,7 +55,6 @@ use gadget::{ }, utilities::{ copy, - enable_flag::{EnableFlagChip, EnableFlagConfig}, plonk::{PLONKChip, PLONKConfig, PLONKInstructions}, CellValue, UtilitiesInstructions, Var, }, @@ -87,7 +86,6 @@ pub struct Config { primary: Column, q_orchard: Selector, advices: [Column; 10], - enable_flag_config: EnableFlagConfig, ecc_config: EccConfig, poseidon_config: PoseidonConfig, plonk_config: PLONKConfig, @@ -153,6 +151,8 @@ impl plonk::Circuit for Circuit { // Constrain v_old - v_new = magnitude * sign // Either v_old = 0, or anchor equals public input + // Constrain v_old = 0 or enable_spends = 1. + // Constrain v_new = 0 or enable_outputs = 1. let q_orchard = meta.selector(); meta.create_gate("Orchard circuit checks", |meta| { let q_orchard = meta.query_selector(q_orchard); @@ -164,14 +164,23 @@ impl plonk::Circuit for Circuit { let anchor = meta.query_advice(advices[4], Rotation::cur()); let pub_input_anchor = meta.query_advice(advices[5], Rotation::cur()); + let one = Expression::Constant(pallas::Base::one()); + let not_enable_spends = one.clone() - meta.query_advice(advices[6], Rotation::cur()); + let not_enable_outputs = one - meta.query_advice(advices[7], Rotation::cur()); + std::array::IntoIter::new([ ( "v_old - v_new = magnitude * sign", - v_old.clone() - v_new - magnitude * sign, + v_old.clone() - v_new.clone() - magnitude * sign, ), ( "Either v_old = 0, or anchor equals public input", - v_old * (anchor - pub_input_anchor), + v_old.clone() * (anchor - pub_input_anchor), + ), + ("v_old = 0 or enable_spends = 1", v_old * not_enable_spends), + ( + "v_new = 0 or enable_outputs = 1", + v_new * not_enable_outputs, ), ]) .map(move |(name, poly)| (name, q_orchard.clone() * poly)) @@ -221,10 +230,6 @@ impl plonk::Circuit for Circuit { meta.enable_equality((*fixed).into()); } - // Configuration for `enable_spends` and `enable_outputs` flags logic - // TODO: this may change with public inputs API. - let enable_flag_config = EnableFlagChip::configure(meta, [advices[0], advices[1]]); - // Configuration for curve point operations. // This uses 10 advice columns and spans the whole circuit. let ecc_config = EccChip::configure(meta, advices, table_idx, ecc_constants); @@ -291,7 +296,6 @@ impl plonk::Circuit for Circuit { primary, q_orchard, advices, - enable_flag_config, ecc_config, poseidon_config, plonk_config, @@ -717,6 +721,22 @@ impl plonk::Circuit for Circuit { 0, )?; + region.assign_advice_from_instance( + || "enable spends", + config.primary, + ENABLE_SPEND, + config.advices[6], + 0, + )?; + + region.assign_advice_from_instance( + || "enable outputs", + config.primary, + ENABLE_OUTPUT, + config.advices[7], + 0, + )?; + config.q_orchard.enable(&mut region, 0) }, )?; diff --git a/src/circuit/gadget/utilities.rs b/src/circuit/gadget/utilities.rs index 6fd2ef9c..ec607c55 100644 --- a/src/circuit/gadget/utilities.rs +++ b/src/circuit/gadget/utilities.rs @@ -8,7 +8,6 @@ use std::{array, convert::TryInto, ops::Range}; pub(crate) mod cond_swap; pub(crate) mod decompose_running_sum; -pub(crate) mod enable_flag; pub(crate) mod lookup_range_check; pub(crate) mod plonk; diff --git a/src/circuit/gadget/utilities/enable_flag.rs b/src/circuit/gadget/utilities/enable_flag.rs deleted file mode 100644 index 01227fb6..00000000 --- a/src/circuit/gadget/utilities/enable_flag.rs +++ /dev/null @@ -1,220 +0,0 @@ -use super::{copy, CellValue, UtilitiesInstructions}; -use halo2::{ - circuit::{Chip, Layouter}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, - poly::Rotation, -}; -use pasta_curves::arithmetic::FieldExt; -use std::marker::PhantomData; - -pub trait EnableFlagInstructions: UtilitiesInstructions { - /// Enforces that `value` be zero or, if non-zero, that `enable_flag` must be 1. - fn enable_flag( - &self, - layouter: impl Layouter, - value: Self::Var, - enable_flag: Option, - ) -> Result<(), Error>; -} - -#[derive(Clone, Debug)] -pub struct EnableFlagConfig { - q_enable: Selector, - value: Column, - enable_flag: Column, -} - -/// 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 { - &() - } -} - -impl UtilitiesInstructions for EnableFlagChip { - type Var = CellValue; -} - -impl EnableFlagInstructions for EnableFlagChip { - fn enable_flag( - &self, - mut layouter: impl Layouter, - value: Self::Var, - enable_flag: Option, - ) -> 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)?; - - // 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.ok_or(Error::SynthesisError), - )?; - - // Copy `value` - copy(&mut region, || "copy value", config.value, 0, &value)?; - - Ok(()) - }, - ) - } -} - -impl EnableFlagChip { - /// Configures this chip for use in a circuit. - /// - /// # Side-effects - /// - /// `advices[0]` will be equality-enabled. - pub fn configure( - meta: &mut ConstraintSystem, - advices: [Column; 2], - ) -> EnableFlagConfig { - let value = advices[0]; - meta.enable_equality(value.into()); - - let q_enable = meta.selector(); - - let config = EnableFlagConfig { - q_enable, - value, - enable_flag: advices[1], - }; - - meta.create_gate("Enable flag", |meta| { - let q_enable = meta.query_selector(config.q_enable); - 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, SimpleFloorPlanner}, - dev::{MockProver, VerifyFailure}, - plonk::{Circuit, ConstraintSystem, Error}, - }; - use pasta_curves::{arithmetic::FieldExt, pallas::Base}; - - #[test] - fn enable_flag() { - #[derive(Default)] - struct MyCircuit { - value: Option, - enable_flag: Option, - } - - impl Circuit for MyCircuit { - type Config = EnableFlagConfig; - 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()]; - - EnableFlagChip::::configure(meta, advices) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - 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)?; - - // Run the enable flag logic. - chip.enable_flag(layouter.namespace(|| "swap"), value, self.enable_flag)?; - - Ok(()) - } - } - - // Test value = 1, flag = 1 case (success) - { - let circuit: MyCircuit = MyCircuit { - value: Some(Base::one()), - enable_flag: Some(true), - }; - let prover = MockProver::::run(3, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); - } - - // Test value = 0, flag = 0 case (success) - { - let circuit: MyCircuit = MyCircuit { - value: Some(Base::zero()), - enable_flag: Some(false), - }; - let prover = MockProver::::run(3, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); - } - - // Test value = 0, flag = 1 case (success) - { - let circuit: MyCircuit = MyCircuit { - value: Some(Base::zero()), - enable_flag: Some(true), - }; - let prover = MockProver::::run(3, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); - } - - // Test value = 1, flag = 0 case (error) - { - let circuit: MyCircuit = MyCircuit { - value: Some(Base::one()), - enable_flag: Some(false), - }; - let prover = MockProver::::run(3, &circuit, vec![]).unwrap(); - assert_eq!( - prover.verify(), - Err(vec![VerifyFailure::ConstraintNotSatisfied { - constraint: ((0, "Enable flag").into(), 0, "").into(), - row: 1, - }]) - ); - } - } -}