diff --git a/src/circuit.rs b/src/circuit.rs index 91a5fe13..7c5ba1f1 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -28,7 +28,6 @@ use self::{ use crate::{ builder::SpendInfo, bundle::Flags, - circuit::gadget::mux_chip::{MuxChip, MuxConfig}, constants::{ OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains, MERKLE_DEPTH_ORCHARD, @@ -59,7 +58,11 @@ use halo2_gadgets::{ MerklePath, }, }, - utilities::{bool_check, lookup_range_check::LookupRangeCheckConfig}, + utilities::{ + bool_check, + lookup_range_check::LookupRangeCheckConfig, + mux::{MuxChip, MuxConfig}, + }, }; mod commit_ivk; diff --git a/src/circuit/gadget.rs b/src/circuit/gadget.rs index 919b2c10..fe0dc4ce 100644 --- a/src/circuit/gadget.rs +++ b/src/circuit/gadget.rs @@ -6,7 +6,6 @@ use pasta_curves::arithmetic::CurveExt; use pasta_curves::pallas; use super::{commit_ivk::CommitIvkChip, note_commit::NoteCommitChip}; -use crate::circuit::gadget::mux_chip::{MuxChip, MuxInstructions}; use crate::constants::{NullifierK, OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains}; use crate::note::AssetBase; use halo2_gadgets::{ @@ -16,6 +15,7 @@ use halo2_gadgets::{ Hash as PoseidonHash, PoseidonSpongeInstructions, Pow5Chip as PoseidonChip, }, sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip}, + utilities::mux::{MuxChip, MuxInstructions}, }; use halo2_proofs::{ circuit::{AssignedCell, Chip, Layouter, Value}, @@ -23,7 +23,6 @@ use halo2_proofs::{ }; pub(in crate::circuit) mod add_chip; -pub(in crate::circuit) mod mux_chip; impl super::Config { pub(super) fn add_chip(&self) -> add_chip::AddChip { @@ -74,8 +73,8 @@ impl super::Config { NoteCommitChip::construct(self.old_note_commit_config.clone()) } - pub(super) fn mux_chip(&self) -> mux_chip::MuxChip { - mux_chip::MuxChip::construct(self.mux_config.clone()) + pub(super) fn mux_chip(&self) -> MuxChip { + MuxChip::construct(self.mux_config.clone()) } } @@ -224,7 +223,7 @@ pub(in crate::circuit) fn derive_nullifier< // Select the desired nullifier according to split_flag Ok(Point::from_inner( ecc_chip, - mux_chip.mux( + mux_chip.mux_on_points( layouter.namespace(|| "mux on nf"), &split_flag, nf.inner(), diff --git a/src/circuit/gadget/mux_chip.rs b/src/circuit/gadget/mux_chip.rs deleted file mode 100644 index fb81f8ea..00000000 --- a/src/circuit/gadget/mux_chip.rs +++ /dev/null @@ -1,344 +0,0 @@ -use halo2_gadgets::ecc::chip::EccPoint; -use halo2_proofs::{ - circuit::{AssignedCell, Chip, Layouter, Value}, - plonk::{self, Advice, Column, ConstraintSystem, Constraints, Expression, Selector}, - poly::Rotation, -}; -use pasta_curves::pallas; - -#[derive(Clone, Debug)] -pub(in crate::circuit) struct MuxConfig { - choice: Column, - left: Column, - right: Column, - out: Column, - q_mux: Selector, -} - -/// A chip implementing a multiplexer on a single row. -/// -/// out = if (choice == 0) {left} else {right} -/// -/// `choice` must be constrained to {0, 1} separately. -#[derive(Clone, Debug)] -pub(in crate::circuit) struct MuxChip { - config: MuxConfig, -} - -impl Chip for MuxChip { - type Config = MuxConfig; - type Loaded = (); - - fn config(&self) -> &Self::Config { - &self.config - } - - fn loaded(&self) -> &Self::Loaded { - &() - } -} - -impl MuxChip { - pub(in crate::circuit) fn configure( - meta: &mut ConstraintSystem, - choice: Column, - left: Column, - right: Column, - out: Column, - ) -> MuxConfig { - let q_mux = meta.selector(); - meta.create_gate("Field element multiplexer", |meta| { - let q_mux = meta.query_selector(q_mux); - let choice = meta.query_advice(choice, Rotation::cur()); - let left = meta.query_advice(left, Rotation::cur()); - let right = meta.query_advice(right, Rotation::cur()); - let out = meta.query_advice(out, Rotation::cur()); - - let one = Expression::Constant(pallas::Base::one()); - - let should_be_zero = (one - choice.clone()) * left + choice * right - out; - - Constraints::with_selector(q_mux, Some(should_be_zero)) - }); - - MuxConfig { - choice, - left, - right, - out, - q_mux, - } - } - - pub(in crate::circuit) fn construct(config: MuxConfig) -> Self { - Self { config } - } -} - -/// An instruction set for multiplexing two points. -pub(crate) trait MuxInstructions { - /// Constraints `MUX(choice, left, right)` and returns the selected point. - fn mux( - &self, - layouter: impl Layouter, - choice: &AssignedCell, - left: &EccPoint, - right: &EccPoint, - ) -> Result; -} - -impl MuxInstructions for MuxChip { - fn mux( - &self, - mut layouter: impl Layouter, - choice: &AssignedCell, - left: &EccPoint, - right: &EccPoint, - ) -> Result { - let x_cell = layouter.assign_region( - || "mux x", - |mut region| { - self.config.q_mux.enable(&mut region, 0)?; - - choice.copy_advice(|| "copy choice", &mut region, self.config.choice, 0)?; - left.x() - .copy_advice(|| "copy left_x", &mut region, self.config.left, 0)?; - right - .x() - .copy_advice(|| "copy right_x", &mut region, self.config.right, 0)?; - - let out_val = (Value::known(pallas::Base::one()) - choice.value()) - * left.x().value() - + choice.value() * right.x().value(); - - region.assign_advice(|| "out x", self.config.out, 0, || out_val) - }, - )?; - let y_cell = layouter.assign_region( - || "mux y", - |mut region| { - self.config.q_mux.enable(&mut region, 0)?; - - choice.copy_advice(|| "copy choice", &mut region, self.config.choice, 0)?; - left.y() - .copy_advice(|| "copy left_y", &mut region, self.config.left, 0)?; - right - .y() - .copy_advice(|| "copy right_y", &mut region, self.config.right, 0)?; - - let out_val = (Value::known(pallas::Base::one()) - choice.value()) - * left.y().value() - + choice.value() * right.y().value(); - - region.assign_advice(|| "out y", self.config.out, 0, || out_val) - }, - )?; - - Ok(EccPoint::from_coordinates_unchecked( - x_cell.into(), - y_cell.into(), - )) - } -} - -#[cfg(test)] -mod tests { - use crate::circuit::gadget::mux_chip::{MuxChip, MuxConfig, MuxInstructions}; - - use crate::{circuit::gadget::assign_free_advice, circuit::K, constants::OrchardFixedBases}; - use halo2_gadgets::{ - ecc::{ - chip::{EccChip, EccConfig}, - Point, - }, - utilities::lookup_range_check::LookupRangeCheckConfig, - }; - - use group::{cofactor::CofactorCurveAffine, Curve, Group}; - use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner, Value}, - dev::MockProver, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance}, - }; - use pasta_curves::arithmetic::CurveAffine; - use pasta_curves::{pallas, EpAffine}; - - use rand::rngs::OsRng; - - #[test] - fn test_mux_chip() { - #[derive(Clone, Debug)] - pub struct MyConfig { - primary: Column, - advice: Column, - mux_config: MuxConfig, - ecc_config: EccConfig, - } - #[derive(Default)] - struct MyCircuit { - left_point: Value, - right_point: Value, - choice: Value, - } - - impl Circuit for MyCircuit { - type Config = MyConfig; - 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(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - ]; - - for advice in advices.iter() { - meta.enable_equality(*advice); - } - - // Instance column used for public inputs - let primary = meta.instance_column(); - meta.enable_equality(primary); - - let mux_config = - MuxChip::configure(meta, advices[0], advices[1], advices[2], advices[3]); - - let table_idx = meta.lookup_table_column(); - let table_range_check_tag = meta.lookup_table_column(); - - let lagrange_coeffs = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; - meta.enable_constant(lagrange_coeffs[0]); - - let range_check = LookupRangeCheckConfig::configure( - meta, - advices[9], - table_idx, - table_range_check_tag, - ); - - let ecc_config = EccChip::::configure( - meta, - advices, - lagrange_coeffs, - range_check, - ); - - MyConfig { - primary, - advice: advices[0], - mux_config, - ecc_config, - } - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - // Construct a MUX chip - let mux_chip = MuxChip::construct(config.mux_config); - - // Construct an ECC chip - let ecc_chip = EccChip::construct(config.ecc_config); - - // Assign left point - let left_point = Point::new( - ecc_chip.clone(), - layouter.namespace(|| "left point"), - self.left_point.map(|left_point| left_point), - )?; - - // Assign right point - let right_point = Point::new( - ecc_chip, - layouter.namespace(|| "right point"), - self.right_point.map(|right_point| right_point), - )?; - - // Assign choice - let choice = assign_free_advice( - layouter.namespace(|| "choice"), - config.advice, - self.choice, - )?; - - // Apply mux - let result = mux_chip.mux( - layouter.namespace(|| "MUX"), - &choice, - left_point.inner(), - right_point.inner(), - )?; - - // Check equality with instance - layouter.constrain_instance(result.x().cell(), config.primary, 0)?; - layouter.constrain_instance(result.y().cell(), config.primary, 1) - } - } - - // Test different circuits - let mut circuits = vec![]; - let mut instances = vec![]; - for choice in [false, true] { - let choice_value = if choice { - pallas::Base::one() - } else { - pallas::Base::zero() - }; - for left_point in [ - pallas::Point::identity().to_affine(), - pallas::Point::random(OsRng).to_affine(), - ] { - for right_point in [ - pallas::Point::identity().to_affine(), - pallas::Point::random(OsRng).to_affine(), - ] { - circuits.push(MyCircuit { - left_point: Value::known(left_point), - right_point: Value::known(right_point), - choice: Value::known(choice_value), - }); - let expected_output = if choice { right_point } else { left_point }; - let (expected_x, expected_y) = if bool::from(expected_output.is_identity()) { - (pallas::Base::zero(), pallas::Base::zero()) - } else { - let coords = expected_output.coordinates().unwrap(); - (*coords.x(), *coords.y()) - }; - instances.push([[expected_x, expected_y]]); - } - } - } - - for (circuit, instance) in circuits.iter().zip(instances.iter()) { - let prover = MockProver::::run( - K, - circuit, - instance.iter().map(|p| p.to_vec()).collect(), - ) - .unwrap(); - assert_eq!(prover.verify(), Ok(())); - } - } -} diff --git a/src/circuit/note_commit.rs b/src/circuit/note_commit.rs index c5c8f44e..c41a6194 100644 --- a/src/circuit/note_commit.rs +++ b/src/circuit/note_commit.rs @@ -9,7 +9,6 @@ use halo2_proofs::{ use pasta_curves::pallas; use crate::{ - circuit::gadget::mux_chip::{MuxChip, MuxInstructions}, constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, T_P}, value::NoteValue, }; @@ -23,7 +22,10 @@ use halo2_gadgets::{ CommitDomain, Message, MessagePiece, }, utilities::{ - bool_check, lookup_range_check::LookupRangeCheckConfig, FieldValue, RangeConstrained, + bool_check, + lookup_range_check::LookupRangeCheckConfig, + mux::{MuxChip, MuxInstructions}, + FieldValue, RangeConstrained, }, }; @@ -1908,7 +1910,7 @@ pub(in crate::circuit) mod gadgets { // hash_point = hash_zsa if is_native_asset is false let hash_point = Point::from_inner( ecc_chip, - mux_chip.mux( + mux_chip.mux_on_points( layouter.namespace(|| "mux on hash point"), &is_native_asset, &(hash_point_zsa.inner().clone().into()), @@ -2297,10 +2299,7 @@ mod tests { use super::NoteCommitConfig; use crate::{ circuit::{ - gadget::{ - assign_free_advice, assign_is_native_asset, - mux_chip::{MuxChip, MuxConfig}, - }, + gadget::{assign_free_advice, assign_is_native_asset}, note_commit::{gadgets, NoteCommitChip}, }, constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, T_Q}, @@ -2313,7 +2312,10 @@ mod tests { NonIdentityPoint, ScalarFixed, }, sinsemilla::chip::SinsemillaChip, - utilities::lookup_range_check::LookupRangeCheckConfig, + utilities::{ + lookup_range_check::LookupRangeCheckConfig, + mux::{MuxChip, MuxConfig}, + }, }; use ff::{Field, PrimeField};