Browse Source

added 64 bit test for range_check gadget

Janmajaya Mall 4 years ago
parent
commit
dfc7d71940
1 changed files with 124 additions and 46 deletions
  1. 124 46
      src/zk/gadget/range_check.rs

+ 124 - 46
src/zk/gadget/range_check.rs

@@ -15,6 +15,7 @@ pub struct RangeCheckConfig {
     pub k_values_table: TableColumn,
 }
 
+#[derive(Clone, Debug)]
 pub struct RangeCheckChip<F: FieldExt + PrimeFieldBits, const WINDOW_SIZE: usize> {
     config: RangeCheckConfig,
     _marker: PhantomData<F>,
@@ -36,10 +37,14 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_SIZE: usize> Chip<F>
 }
 
 impl<F: FieldExt + PrimeFieldBits, const WINDOW_SIZE: usize> RangeCheckChip<F, WINDOW_SIZE> {
+    pub fn construct(config: RangeCheckConfig) -> Self {
+        Self { config, _marker: PhantomData }
+    }
+
     pub fn configure(
         meta: &mut ConstraintSystem<F>,
         k_values_table: TableColumn,
-    ) -> <Self as Chip<F>>::Config {
+    ) -> RangeCheckConfig {
         let z = meta.advice_column();
         let s_rc = meta.complex_selector();
 
@@ -80,60 +85,68 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_SIZE: usize> RangeCheckChip<F, W
         )
     }
 
-    pub fn range_check(
+    pub fn witness_range_check(
         &self,
-        mut layouter: impl Layouter<F>,
-        element: AssignedCell<F, F>,
+        layouter: &mut impl Layouter<F>,
+        value: Value<F>,
+        offset: usize,
         num_of_bits: usize,
         num_of_windows: usize,
     ) -> Result<(), Error> {
-        assert!(WINDOW_SIZE * num_of_windows < num_of_bits + WINDOW_SIZE);
-
         layouter.assign_region(
             || "name",
             |mut region: Region<'_, F>| {
-                // enable selectors
-                for index in 0..num_of_windows {
-                    self.config.s_rc.enable(&mut region, index);
-                }
-
-                // copy element to z_0
-                let z_0 = element.copy_advice(|| "z_0", &mut region, self.config.z, 0)?;
-
-                let mut z_values: Vec<AssignedCell<F, F>> = vec![z_0.clone()];
-                let mut z = z_0.clone();
-                let decomposed_chunks = z_0
-                    .value()
-                    .map(|val| decompose_value::<F, WINDOW_SIZE>(val, num_of_bits))
-                    .transpose_vec(num_of_windows);
-
-                let two_pow_k_inverse =
-                    Value::known(F::from(1 << WINDOW_SIZE as u64).invert().unwrap());
-                for (i, chunk) in decomposed_chunks.iter().enumerate() {
-                    let z_next = {
-                        let z_curr = z.value().copied();
-                        let chunk_value = chunk
-                            .map(|c| F::from(c.iter().fold(0, |acc, c| (acc << 1) + *c as u64)));
-                        // z_next = (z_curr - k_i) / 2^K
-                        let z_next = (z_curr - chunk_value) * two_pow_k_inverse;
-                        region.assign_advice(
-                            || format!("z_{}", i + 1),
-                            self.config.z,
-                            i + 1,
-                            || z_next,
-                        )?
-                    };
-                    z_values.push(z_next.clone());
-                    z = z_next.clone();
-                }
-
-                assert!(z_values.len() == num_of_windows + 1);
-
-                region.constrain_constant(z_values.last().unwrap().cell(), F::zero())?;
-
+                let z_0 = region.assign_advice(|| "z_0", self.config.z, offset, || value)?;
+                self.decompose(region, z_0, offset, num_of_bits, num_of_windows)?;
                 Ok(())
             },
-        );
+        )
+    }
+
+    pub fn decompose(
+        &self,
+        mut region: Region<'_, F>,
+        z_0: AssignedCell<F, F>,
+        offset: usize,
+        num_of_bits: usize,
+        num_of_windows: usize,
+    ) -> Result<(), Error> {
+        assert!(WINDOW_SIZE * num_of_windows < num_of_bits + WINDOW_SIZE);
+
+        // enable selectors
+        for index in 0..num_of_windows {
+            self.config.s_rc.enable(&mut region, index + offset)?;
+        }
+
+        let mut z_values: Vec<AssignedCell<F, F>> = vec![z_0.clone()];
+        let mut z = z_0.clone();
+        let decomposed_chunks = z_0
+            .value()
+            .map(|val| decompose_value::<F, WINDOW_SIZE>(val, num_of_bits))
+            .transpose_vec(num_of_windows);
+
+        let two_pow_k_inverse = Value::known(F::from(1 << WINDOW_SIZE as u64).invert().unwrap());
+        for (i, chunk) in decomposed_chunks.iter().enumerate() {
+            let z_next = {
+                let z_curr = z.value().copied();
+                let chunk_value =
+                    chunk.map(|c| F::from(c.iter().fold(0, |acc, c| (acc << 1) + *c as u64)));
+                // z_next = (z_curr - k_i) / 2^K
+                let z_next = (z_curr - chunk_value) * two_pow_k_inverse;
+                region.assign_advice(
+                    || format!("z_{}", i + offset + 1),
+                    self.config.z,
+                    i + offset,
+                    || z_next,
+                )?
+            };
+            z_values.push(z_next.clone());
+            z = z_next.clone();
+        }
+
+        assert!(z_values.len() == num_of_windows + 1);
+
+        region.constrain_constant(z_values.last().unwrap().cell(), F::zero())?;
 
         Ok(())
     }
@@ -162,3 +175,68 @@ pub fn decompose_value<F: FieldExt + PrimeFieldBits, const WINDOW_SIZE: usize>(
         })
         .collect()
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use group::ff::PrimeFieldBits;
+    use halo2_proofs::{
+        arithmetic::FieldExt,
+        circuit::{SimpleFloorPlanner, Value},
+        dev::MockProver,
+        plonk::Circuit,
+    };
+    use pasta_curves::pallas;
+
+    struct MyCircuit<
+        F: FieldExt + PrimeFieldBits,
+        const WINDOW_SIZE: usize,
+        const NUM_OF_BITS: usize,
+        const NUM_OF_WINDOWS: usize,
+    > {
+        value: Value<F>,
+    }
+
+    impl<
+            F: FieldExt + PrimeFieldBits,
+            const WINDOW_SIZE: usize,
+            const NUM_OF_BITS: usize,
+            const NUM_OF_WINDOWS: usize,
+        > Circuit<F> for MyCircuit<F, WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>
+    {
+        type Config = RangeCheckConfig;
+        type FloorPlanner = SimpleFloorPlanner;
+
+        fn without_witnesses(&self) -> Self {
+            Self { value: Value::unknown() }
+        }
+
+        fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
+            let table_column = meta.lookup_table_column();
+            RangeCheckChip::<F, WINDOW_SIZE>::configure(meta, table_column)
+        }
+
+        fn synthesize(
+            &self,
+            config: Self::Config,
+            layouter: impl Layouter<F>,
+        ) -> Result<(), Error> {
+            let chip = RangeCheckChip::<F, WINDOW_SIZE>::construct(config.clone());
+
+            // construct `WINDOW_SIZE` lookup table
+            RangeCheckChip::<F, WINDOW_SIZE>::load_k_table(&mut layouter, config.k_values_table);
+
+            chip.witness_range_check(&mut layouter, self.value, 0, NUM_OF_BITS, NUM_OF_WINDOWS);
+
+            Ok(())
+        }
+    }
+
+    #[test]
+    fn test_bit() {
+        let value = pallas::Base::from(rand::random::<u64>());
+        let circuit = MyCircuit::<pallas::Base, 3, 64, 22> { value: Value::known(value) };
+        let prover = MockProver::run(8, &circuit, vec![]).unwrap();
+        assert_ne!(prover.verify(), Ok(()));
+    }
+}