Jelajahi Sumber

zk/gadget: Implement small range check chip for 0..8 bits.

Luther Blissett 3 tahun lalu
induk
melakukan
9331d04328
2 mengubah file dengan 149 tambahan dan 3 penghapusan
  1. 6 3
      src/zk/gadget/mod.rs
  2. 143 0
      src/zk/gadget/small_range_check.rs

+ 6 - 3
src/zk/gadget/mod.rs

@@ -1,10 +1,13 @@
-/// Base field scalar arithmetic
+/// Base field arithmetic gadget
 pub mod arithmetic;
 pub mod arithmetic;
 
 
-/// Field-native range check gadget;
+/// Small range check, 0..8 bits
+pub mod small_range_check;
+
+/// Field-native range check gadget with a lookup table
 pub mod native_range_check;
 pub mod native_range_check;
 
 
-// Field-native less than comparison gadget
+/// Field-native less than comparison gadget with a lookup table
 pub mod less_than;
 pub mod less_than;
 
 
 /// is_zero comparison gadget
 /// is_zero comparison gadget

+ 143 - 0
src/zk/gadget/small_range_check.rs

@@ -0,0 +1,143 @@
+use halo2_proofs::{
+    arithmetic::FieldExt,
+    circuit::{AssignedCell, Chip, Layouter},
+    pasta::pallas,
+    plonk,
+    plonk::{Advice, Column, ConstraintSystem, Constraints, Expression, Selector},
+    poly::Rotation,
+};
+
+/// Checks that an expression is in the small range [0..range),
+/// i.e. 0 ≤ word < range.
+pub fn range_check<F: FieldExt>(word: Expression<F>, range: u8) -> Expression<F> {
+    (1..(range as usize))
+        .fold(word.clone(), |acc, i| acc * (Expression::Constant(F::from(i as u64)) - word.clone()))
+}
+
+#[derive(Clone, Debug)]
+pub struct SmallRangeCheckConfig {
+    pub z: Column<Advice>,
+    pub selector: Selector,
+}
+
+#[derive(Clone, Debug)]
+pub struct SmallRangeCheckChip {
+    config: SmallRangeCheckConfig,
+}
+
+impl Chip<pallas::Base> for SmallRangeCheckChip {
+    type Config = SmallRangeCheckConfig;
+    type Loaded = ();
+
+    fn config(&self) -> &Self::Config {
+        &self.config
+    }
+
+    fn loaded(&self) -> &Self::Loaded {
+        &()
+    }
+}
+
+impl SmallRangeCheckChip {
+    pub fn construct(config: SmallRangeCheckConfig) -> Self {
+        Self { config }
+    }
+
+    pub fn configure(
+        meta: &mut ConstraintSystem<pallas::Base>,
+        z: Column<Advice>,
+        range: u8,
+    ) -> SmallRangeCheckConfig {
+        // Enable permutation on z column
+        meta.enable_equality(z);
+
+        let selector = meta.selector();
+
+        meta.create_gate("bool check", |meta| {
+            let selector = meta.query_selector(selector);
+            let advice = meta.query_advice(z, Rotation::cur());
+            Constraints::with_selector(selector, Some(range_check(advice, range)))
+        });
+
+        SmallRangeCheckConfig { z, selector }
+    }
+
+    pub fn small_range_check(
+        &self,
+        mut layouter: impl Layouter<pallas::Base>,
+        value: AssignedCell<pallas::Base, pallas::Base>,
+    ) -> Result<(), plonk::Error> {
+        layouter.assign_region(
+            || "small range constrain",
+            |mut region| {
+                self.config.selector.enable(&mut region, 0)?;
+                value.copy_advice(|| "z_0", &mut region, self.config.z, 0)?;
+                Ok(())
+            },
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::zk::assign_free_advice;
+    use halo2_proofs::{
+        circuit::{floor_planner, Value},
+        dev::MockProver,
+        plonk,
+        plonk::Circuit,
+    };
+
+    #[derive(Default)]
+    struct SmallRangeCircuit {
+        value: Value<pallas::Base>,
+    }
+
+    impl Circuit<pallas::Base> for SmallRangeCircuit {
+        type Config = (SmallRangeCheckConfig, Column<Advice>);
+        type FloorPlanner = floor_planner::V1;
+
+        fn without_witnesses(&self) -> Self {
+            Self::default()
+        }
+
+        fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
+            let w = meta.advice_column();
+            let z = meta.advice_column();
+
+            meta.enable_equality(w);
+
+            // One bit
+            let config = SmallRangeCheckChip::configure(meta, z, 2);
+
+            (config, w)
+        }
+
+        fn synthesize(
+            &self,
+            config: Self::Config,
+            mut layouter: impl Layouter<pallas::Base>,
+        ) -> Result<(), plonk::Error> {
+            let chip = SmallRangeCheckChip::construct(config.0.clone());
+            let value = assign_free_advice(layouter.namespace(|| "val"), config.1, self.value)?;
+            chip.small_range_check(layouter.namespace(|| "boolean check"), value)?;
+            Ok(())
+        }
+    }
+
+    #[test]
+    fn boolean_range_check() {
+        let k = 3;
+
+        for i in 0..2 {
+            let circuit = SmallRangeCircuit { value: Value::known(pallas::Base::from(i as u64)) };
+            let prover = MockProver::run(k, &circuit, vec![]).unwrap();
+            prover.assert_satisfied();
+        }
+
+        let circuit = SmallRangeCircuit { value: Value::known(pallas::Base::from(2)) };
+        let prover = MockProver::run(k, &circuit, vec![]).unwrap();
+        assert!(prover.verify().is_err());
+    }
+}