Quellcode durchsuchen

conflicts fixed

parazyd vor 4 Jahren
Ursprung
Commit
0ddf3a8def

+ 0 - 246
src/zk/gadget/even_bits.rs

@@ -1,246 +0,0 @@
-use std::{marker::PhantomData, ops::Deref};
-
-use halo2_proofs::{
-    arithmetic::FieldExt,
-    circuit::{AssignedCell, Chip, Layouter, Region, Value},
-    plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, TableColumn},
-    poly::Rotation,
-};
-
-/// Chip state is stored in a config struct. This is generated by the
-/// chip during configuration, and then stored inside the chip
-#[derive(Clone, Debug)]
-pub struct EvenBitsConfig {
-    advice: [Column<Advice>; 2],
-    even_bits: TableColumn,
-
-    s_decompose: Selector,
-}
-
-impl EvenBitsConfig {
-    pub fn load_private<F: FieldExt>(
-        &self,
-        mut layouter: impl Layouter<F>,
-        value: Value<F>,
-    ) -> Result<AssignedCell<F, F>, Error> {
-        layouter.assign_region(
-            || "load private",
-            |mut region| region.assign_advice(|| "private input", self.advice[0], 0, || value),
-        )
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct EvenBitsChip<F: FieldExt, const WORD_BITS: u32> {
-    config: EvenBitsConfig,
-    _marker: PhantomData<F>,
-}
-
-impl<F: FieldExt, const WORD_BITS: u32> Chip<F> for EvenBitsChip<F, WORD_BITS> {
-    type Config = EvenBitsConfig;
-    type Loaded = ();
-
-    fn config(&self) -> &Self::Config {
-        &self.config
-    }
-
-    fn loaded(&self) -> &Self::Loaded {
-        &()
-    }
-}
-
-impl<F: FieldExt, const WORD_BITS: u32> EvenBitsChip<F, WORD_BITS> {
-    pub fn construct(config: <Self as Chip<F>>::Config) -> Self {
-        Self { config, _marker: PhantomData }
-    }
-
-    pub fn configure(meta: &mut ConstraintSystem<F>) -> <Self as Chip<F>>::Config {
-        let advice = [meta.advice_column(), meta.advice_column()];
-        for column in &advice {
-            meta.enable_equality(*column);
-        }
-
-        let s_decompose = meta.complex_selector();
-        let even_bits = meta.lookup_table_column();
-
-        meta.create_gate("decompose", |meta| {
-            let lhs = meta.query_advice(advice[0], Rotation::cur());
-            let rhs = meta.query_advice(advice[1], Rotation::cur());
-            let out = meta.query_advice(advice[0], Rotation::next());
-            let s_decompose = meta.query_selector(s_decompose);
-
-            // Finally, we return the polynomial expressions that constrain this gate.
-            // For our multiplication gate, we only need a single polynomial constraint.
-            //
-            // The polynomial expressions returned from `create_gate` will be
-            // constrained by the proving system to equal zero.
-            vec![s_decompose * (lhs + Expression::Constant(F::from(2)) * rhs - out)]
-        });
-
-        let _ = meta.lookup(|meta| {
-            let lookup = meta.query_selector(s_decompose);
-            let a = meta.query_advice(advice[0], Rotation::cur());
-
-            vec![(lookup * a, even_bits)]
-        });
-
-        let _ = meta.lookup(|meta| {
-            let lookup = meta.query_selector(s_decompose);
-            let b = meta.query_advice(advice[1], Rotation::cur());
-
-            vec![(lookup * b, even_bits)]
-        });
-
-        EvenBitsConfig { advice, even_bits, s_decompose }
-    }
-
-    // Allocates all even bits in a table for the word size WORD_BITS.
-    // `2^(WORD_BITS/2)` rows of the constraint system
-    pub fn alloc_table(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
-        layouter.assign_table(
-            || "even bits table",
-            |mut table| {
-                for i in 0..2usize.pow(WORD_BITS / 2) {
-                    table.assign_cell(
-                        || format!("even_bits row {}", i),
-                        self.config.even_bits,
-                        i,
-                        || Value::known(F::from(even_bits_at(i) as u64)),
-                    )?;
-                }
-                Ok(())
-            },
-        )
-    }
-}
-
-fn even_bits_at(mut i: usize) -> usize {
-    let mut r = 0;
-    let mut c = 0;
-
-    while i != 0 {
-        let lower_bit = i % 2;
-        r += lower_bit * 4usize.pow(c);
-        i >>= 1;
-        c += 1;
-    }
-
-    r
-}
-
-/// A newtype of a field element containing only bits that were in the
-/// even position of the decomposed element.
-/// All odd bits will be zero.
-#[derive(Clone, Copy, Debug)]
-pub struct EvenBits<W>(pub W);
-
-impl<W> Deref for EvenBits<W> {
-    type Target = W;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-/// A newtype of a field element containing only bits thet were in the
-/// odd position of the decomposed element.
-/// All odd bits will be right shifted by 1 into even positions.
-/// All odd bits will be zero.
-#[derive(Clone, Copy, Debug)]
-pub struct OddBits<W>(pub W);
-
-impl<W> Deref for OddBits<W> {
-    type Target = W;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-pub trait EvenBitsLookup<F: FieldExt>: Chip<F> {
-    type Word;
-
-    #[allow(clippy::type_complexity)]
-    fn decompose(
-        &self,
-        layouter: impl Layouter<F>,
-        c: Self::Word,
-    ) -> Result<(EvenBits<Self::Word>, OddBits<Self::Word>), Error>;
-}
-
-impl<F: FieldExt, const WORD_BITS: u32> EvenBitsLookup<F> for EvenBitsChip<F, WORD_BITS> {
-    type Word = AssignedCell<F, F>;
-
-    fn decompose(
-        &self,
-        mut layouter: impl Layouter<F>,
-        c: Self::Word,
-    ) -> Result<(EvenBits<Self::Word>, OddBits<Self::Word>), Error> {
-        let config = self.config();
-
-        layouter.assign_region(
-            || "decompose",
-            |mut region: Region<'_, F>| {
-                config.s_decompose.enable(&mut region, 0)?;
-
-                let o_eo = c.value().cloned().map(decompose);
-                let e_cell = region
-                    .assign_advice(|| "even bits", config.advice[0], 0, || o_eo.map(|eo| *eo.0))
-                    .map(EvenBits)?;
-
-                let o_cell = region
-                    .assign_advice(|| "odd bits", config.advice[1], 0, || o_eo.map(|eo| *eo.1))
-                    .map(OddBits)?;
-
-                c.copy_advice(|| "out", &mut region, config.advice[0], 1)?;
-                Ok((e_cell, o_cell))
-            },
-        )
-    }
-}
-
-fn decompose<F: FieldExt>(word: F) -> (EvenBits<F>, OddBits<F>) {
-    assert!(word <= F::from_u128(u128::MAX));
-
-    let mut even_only = word.to_repr();
-    even_only.as_mut().iter_mut().for_each(|bits| {
-        *bits &= 0b01010101;
-    });
-
-    let mut odd_only = word.to_repr();
-    odd_only.as_mut().iter_mut().for_each(|bits| {
-        *bits &= 0b10101010;
-    });
-
-    let even_only = EvenBits(F::from_repr(even_only).unwrap());
-    let odd_only = F::from_repr(odd_only).unwrap();
-    let odds_in_even = OddBits(F::from_u128(odd_only.get_lower_128() >> 1));
-    (even_only, odds_in_even)
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn even_bits_at_test() {
-        assert_eq!(0b0, even_bits_at(0));
-        assert_eq!(0b1, even_bits_at(1));
-        assert_eq!(0b100, even_bits_at(2));
-        assert_eq!(0b101, even_bits_at(3));
-    }
-
-    #[test]
-    fn decompose_even_odd_test() {
-        use pasta_curves::pallas;
-        let odds = 0xAAAA;
-        let evens = 0x5555;
-        let (e, o) = decompose(pallas::Base::from_u128(odds));
-        assert_eq!(e.get_lower_128(), 0);
-        assert_eq!(o.get_lower_128(), odds >> 1);
-        let (e, o) = decompose(pallas::Base::from_u128(evens));
-        assert_eq!(e.get_lower_128(), evens);
-        assert_eq!(o.get_lower_128(), 0);
-    }
-}
-//

+ 0 - 215
src/zk/gadget/greater_than.rs

@@ -1,215 +0,0 @@
-use std::marker::PhantomData;
-
-use halo2_proofs::{
-    arithmetic::FieldExt,
-    circuit::{AssignedCell, Chip, Layouter, Region, Value},
-    plonk::{Advice, Column, ConstraintSystem, Error, Expression, Instance, Selector},
-    poly::Rotation,
-};
-
-use pasta_curves::pallas;
-
-
-#[derive(Clone, Debug)]
-pub struct GreaterThanConfig {
-    pub advice: [Column<Advice>; 2],
-    pub instance: Column<Instance>,
-    s_gt: Selector,
-}
-
-pub struct GreaterThanChip<F: FieldExt, const WORD_BITS: u32> {
-    config: GreaterThanConfig,
-    _marker: PhantomData<F>,
-}
-
-impl<F: FieldExt, const WORD_BITS: u32> Chip<F> for GreaterThanChip<F, WORD_BITS> {
-    type Config = GreaterThanConfig;
-    type Loaded = ();
-
-    fn config(&self) -> &Self::Config {
-        &self.config
-    }
-
-    fn loaded(&self) -> &Self::Loaded {
-        &()
-    }
-}
-
-impl<F: FieldExt, const WORD_BITS: u32> GreaterThanChip<F, WORD_BITS> {
-    pub fn construct(config: <Self as Chip<F>>::Config) -> Self {
-        Self { config, _marker: PhantomData }
-    }
-
-    /*
-    pub fn configure(meta: &mut ConstraintSystem<F>) -> <Self as Chip<F>>::Config {
-        //let constant = meta.fixed_column();
-        //meta.enable_constant(constant);
-
-        let advice = [meta.advice_column(), meta.advice_column()];
-
-        for column in &advice {
-            meta.enable_equality(*column);
-        }
-
-        let s_gt = meta.selector();
-
-        meta.create_gate("greater than", |meta| {
-            let lhs = meta.query_advice(advice[0], Rotation::cur());
-            let rhs = meta.query_advice(advice[1], Rotation::cur());
-
-            // This value is `lhs - rhs` if `lhs !> rhs` and `2^W - (lhs - rhs)` if `lhs > rhs`
-            let helper = meta.query_advice(advice[0], Rotation::next());
-
-            let is_greater = meta.query_advice(advice[1], Rotation::next());
-            let s_gt = meta.query_selector(s_gt);
-
-            vec![
-                s_gt * (lhs - rhs + helper -
-                    Expression::Constant(F::from(2_u64.pow(WORD_BITS))) * is_greater),
-            ]
-        });
-
-        GreaterThanConfig { advice, s_gt }
-    }
-
-    pub fn configure(meta: &mut ConstraintSystem<F>) -> <Self as Chip<F>>::Config {
-        let advice = [meta.advice_column(), meta.advice_column()];
-
-
-        let s_gt = meta.selector();
-
-        meta.create_gate("greater than", |meta| {
-            let lhs = meta.query_advice(advice[0], Rotation::cur());
-            let rhs = meta.query_advice(advice[1], Rotation::cur());
-
-            // This value is `lhs - rhs` if `lhs !> rhs` and `2^W - (lhs - rhs)` if `lhs > rhs`
-            let helper = meta.query_advice(advice[0], Rotation::next());
-
-            let is_greater = meta.query_advice(advice[1], Rotation::next());
-            let s_gt = meta.query_selector(s_gt);
-
-            vec![
-                s_gt * (lhs - rhs + helper -
-                    Expression::Constant(F::from(2_u64.pow(WORD_BITS))) * is_greater),
-            ]
-        });
-
-        GreaterThanConfig { advice, s_gt }
-    }
-     */
-    pub fn configure(
-        meta: &mut ConstraintSystem<F>,
-        advice: [Column<Advice>; 2],
-        instance: Column<Instance>,
-    ) -> <Self as Chip<F>>::Config {
-        for column in &advice {
-            meta.enable_equality(*column);
-        }
-
-        let s_gt = meta.selector();
-
-        meta.create_gate("greater than", |meta| {
-            let lhs = meta.query_advice(advice[0], Rotation::cur());
-            let rhs = meta.query_advice(advice[1], Rotation::cur());
-
-            // This value is `lhs - rhs` if `lhs !> rhs` and `2^W - (lhs - rhs)` if `lhs > rhs`
-            let helper = meta.query_advice(advice[0], Rotation::next());
-
-            let is_greater = meta.query_advice(advice[1], Rotation::next());
-            let s_gt = meta.query_selector(s_gt);
-
-            vec![
-                s_gt * (lhs - rhs + helper -
-                    Expression::Constant(F::from(2_u64.pow(WORD_BITS))) * is_greater),
-            ]
-        });
-
-        GreaterThanConfig { advice, instance, s_gt }
-    }
-}
-
-pub trait GreaterThanInstruction<F: FieldExt>: Chip<F> {
-    type Word;
-
-    fn greater_than(
-        &self,
-        layouter: impl Layouter<F>,
-        a: Self::Word,
-        b: Self::Word,
-    ) -> Result<(Self::Word, Self::Word), Error>;
-}
-
-#[derive(Clone, Debug)]
-pub struct Word<F: FieldExt>(pub AssignedCell<F, F>);
-
-impl From<AssignedCell<pallas::Base, pallas::Base>> for Word<pallas::Base> {
-    fn from(cell: AssignedCell<pallas::Base, pallas::Base>) -> Self {
-        Self(cell)
-    }
-}
-
-impl<const WORD_BITS: u32> GreaterThanInstruction<pallas::Base>
-    for GreaterThanChip<pallas::Base, WORD_BITS>
-{
-    type Word = Word<pallas::Base>;
-
-    fn greater_than(
-        &self,
-        mut layouter: impl Layouter<pallas::Base>,
-        a: Self::Word,
-        b: Self::Word,
-    ) -> Result<(Self::Word, Self::Word), Error> {
-        let config = self.config();
-
-        layouter.assign_region(
-            || "greater than",
-            |mut region: Region<'_, pallas::Base>| {
-                config.s_gt.enable(&mut region, 0)?;
-
-                a.0.copy_advice(|| "lhs", &mut region, config.advice[0], 0)?;
-                b.0.copy_advice(|| "rhs", &mut region, config.advice[1], 0)?;
-
-                let helper_cell = region
-                    .assign_advice(
-                        || "max minus diff",
-                        config.advice[0],
-                        1,
-                        || {
-                            let is_greater = a.0.value().inner().unwrap().get_lower_128() >
-                                b.0.value().get_lower_128();
-                            a.0.value().and_then(|a| {
-                                b.0.value().map(|b| {
-                                    let x = *a - *b;
-
-                                    (if is_greater {
-                                        pallas::Base::from(2_u64.pow(WORD_BITS))
-                                    } else {
-                                        pallas::Base::zero()
-                                    }) - x
-                                })
-                            })
-                        },
-                    )
-                    .map(Word)?;
-
-                let is_greater_cell = region
-                    .assign_advice(
-                        || "is greater",
-                        config.advice[1],
-                        1,
-                        || {
-                            let is_greater = a.0.value().inner().unwrap().get_lower_128() >
-                                b.0.value().get_lower_128();
-                            Value::known(if is_greater {
-                                pallas::Base::one()
-                            } else {
-                                pallas::Base::zero()
-                            })
-                        },
-                    )
-                    .map(Word)?;
-                Ok((helper_cell, is_greater_cell))
-            },
-        )
-    }
-}

+ 0 - 0
src/zk/gadget/cmp.rs → src/zk/gadget/is_zero.rs


+ 7 - 0
src/zk/gadget/mod.rs

@@ -1,6 +1,7 @@
 /// Scalar arithmetic
 /// Scalar arithmetic
 pub mod arithmetic;
 pub mod arithmetic;
 
 
+
 /// Even-bits lookup table
 /// Even-bits lookup table
 pub mod even_bits;
 pub mod even_bits;
 
 
@@ -9,3 +10,9 @@ pub mod less_than;
 
 
 /// Comparison gadget
 /// Comparison gadget
 pub mod cmp;
 pub mod cmp;
+
+/// Field-native range check gadget;
+pub mod native_range_check;
+
+/// is_zero comparison gadget
+pub mod is_zero;

+ 424 - 0
src/zk/gadget/native_range_check.rs

@@ -0,0 +1,424 @@
+use group::ff::{Field, PrimeFieldBits};
+use halo2_proofs::{
+    circuit::{AssignedCell, Chip, Layouter, Region, Value},
+    pasta::pallas,
+    plonk,
+    plonk::{Advice, Column, ConstraintSystem, Selector, TableColumn},
+    poly::Rotation,
+};
+
+#[derive(Clone, Debug)]
+pub struct NativeRangeCheckConfig<
+    const WINDOW_SIZE: usize,
+    const NUM_BITS: usize,
+    const NUM_WINDOWS: usize,
+> {
+    pub z: Column<Advice>,
+    pub s_rc: Selector,
+    pub k_values_table: TableColumn,
+}
+
+#[derive(Clone, Debug)]
+pub struct NativeRangeCheckChip<
+    const WINDOW_SIZE: usize,
+    const NUM_BITS: usize,
+    const NUM_WINDOWS: usize,
+> {
+    config: NativeRangeCheckConfig<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>,
+}
+
+impl<const WINDOW_SIZE: usize, const NUM_BITS: usize, const NUM_WINDOWS: usize> Chip<pallas::Base>
+    for NativeRangeCheckChip<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>
+{
+    type Config = NativeRangeCheckConfig<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>;
+    type Loaded = ();
+
+    fn config(&self) -> &Self::Config {
+        &self.config
+    }
+
+    fn loaded(&self) -> &Self::Loaded {
+        &()
+    }
+}
+
+impl<const WINDOW_SIZE: usize, const NUM_BITS: usize, const NUM_WINDOWS: usize>
+    NativeRangeCheckChip<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>
+{
+    pub fn construct(config: NativeRangeCheckConfig<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>) -> Self {
+        Self { config }
+    }
+
+    pub fn configure(
+        meta: &mut ConstraintSystem<pallas::Base>,
+        z: Column<Advice>,
+        k_values_table: TableColumn,
+    ) -> NativeRangeCheckConfig<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS> {
+        // Enable permutation on z column
+        meta.enable_equality(z);
+
+        let s_rc = meta.complex_selector();
+
+        meta.lookup(|meta| {
+            let s_rc = meta.query_selector(s_rc);
+            let z_curr = meta.query_advice(z, Rotation::cur());
+            let z_next = meta.query_advice(z, Rotation::next());
+
+            //    z_next = (z_curr - k_i) / 2^K
+            // => k_i = z_curr - (z_next * 2^K)
+            vec![(s_rc * (z_curr - z_next * pallas::Base::from(1 << WINDOW_SIZE)), k_values_table)]
+        });
+
+        NativeRangeCheckConfig { z, s_rc, k_values_table }
+    }
+
+    /// `k_values_table` should be reused across different chips
+    /// which is why we don't limit it to a specific instance.
+    pub fn load_k_table(
+        layouter: &mut impl Layouter<pallas::Base>,
+        k_values_table: TableColumn,
+    ) -> Result<(), plonk::Error> {
+        layouter.assign_table(
+            || format!("{} window table", WINDOW_SIZE),
+            |mut table| {
+                for index in 0..(1 << WINDOW_SIZE) {
+                    table.assign_cell(
+                        || format!("{} window assign", WINDOW_SIZE),
+                        k_values_table,
+                        index,
+                        || Value::known(pallas::Base::from(index as u64)),
+                    )?;
+                }
+                Ok(())
+            },
+        )
+    }
+
+    fn decompose_value(value: &pallas::Base) -> Vec<[bool; WINDOW_SIZE]> {
+        let padding = (WINDOW_SIZE - NUM_BITS % WINDOW_SIZE) % WINDOW_SIZE;
+
+        let bits: Vec<bool> = value
+            .to_le_bits()
+            .into_iter()
+            .take(NUM_BITS)
+            .chain(std::iter::repeat(false).take(padding))
+            .collect();
+        assert_eq!(bits.len(), NUM_BITS + padding);
+
+        bits.chunks_exact(WINDOW_SIZE)
+            .map(|x| {
+                let mut chunks = [false; WINDOW_SIZE];
+                chunks.copy_from_slice(x);
+                chunks
+            })
+            .collect()
+    }
+
+    // TODO: strict bool
+    pub fn decompose(
+        &self,
+        region: &mut Region<'_, pallas::Base>,
+        z_0: AssignedCell<pallas::Base, pallas::Base>,
+        offset: usize,
+    ) -> Result<(), plonk::Error> {
+        assert!(WINDOW_SIZE * NUM_WINDOWS < NUM_BITS + WINDOW_SIZE);
+
+        // Enable selectors
+        for index in 0..NUM_WINDOWS {
+            self.config.s_rc.enable(region, index + offset)?;
+        }
+
+        let mut z_values: Vec<AssignedCell<pallas::Base, pallas::Base>> = vec![z_0.clone()];
+        let mut z = z_0.clone();
+        let decomposed_chunks = z_0.value().map(Self::decompose_value).transpose_vec(NUM_WINDOWS);
+
+        let two_pow_k_inverse =
+            Value::known(pallas::Base::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| {
+                    pallas::Base::from(c.iter().rev().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 + 1,
+                    || z_next,
+                )?
+            };
+            z_values.push(z_next.clone());
+            z = z_next.clone();
+        }
+
+        assert!(z_values.len() == NUM_WINDOWS + 1);
+        region.constrain_constant(z_values.last().unwrap().cell(), pallas::Base::zero())?;
+        Ok(())
+    }
+
+    pub fn witness_range_check(
+        &self,
+        mut layouter: impl Layouter<pallas::Base>,
+        value: Value<pallas::Base>,
+    ) -> Result<(), plonk::Error> {
+        layouter.assign_region(
+            || format!("witness {}-bit native range check", NUM_BITS),
+            |mut region: Region<'_, pallas::Base>| {
+                let z_0 = region.assign_advice(|| "z_0", self.config.z, 0, || value)?;
+                self.decompose(&mut region, z_0, 0)?;
+                Ok(())
+            },
+        )
+    }
+
+    pub fn copy_range_check(
+        &self,
+        mut layouter: impl Layouter<pallas::Base>,
+        value: AssignedCell<pallas::Base, pallas::Base>,
+    ) -> Result<(), plonk::Error> {
+        layouter.assign_region(
+            || format!("copy {}-bit native range check", NUM_BITS),
+            |mut region: Region<'_, pallas::Base>| {
+                let z_0 = value.copy_advice(|| "z_0", &mut region, self.config.z, 0)?;
+                self.decompose(&mut region, z_0, 0)?;
+                Ok(())
+            },
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::zk::assign_free_advice;
+    use group::ff::PrimeField;
+    use halo2_proofs::{
+        circuit::floor_planner,
+        dev::{CircuitLayout, MockProver},
+        plonk::Circuit,
+    };
+    use pasta_curves::arithmetic::FieldExt;
+
+    macro_rules! test_circuit {
+        ($window_size:expr, $num_bits:expr, $num_windows:expr) => {
+            #[derive(Default)]
+            struct RangeCheckCircuit {
+                a: Value<pallas::Base>,
+            }
+
+            impl Circuit<pallas::Base> for RangeCheckCircuit {
+                type Config =
+                    (NativeRangeCheckConfig<$window_size, $num_bits, $num_windows>, 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();
+                    meta.enable_equality(w);
+                    let z = meta.advice_column();
+                    let table_column = meta.lookup_table_column();
+
+                    let constants = meta.fixed_column();
+                    meta.enable_constant(constants);
+                    (
+                        NativeRangeCheckChip::<$window_size, $num_bits, $num_windows>::configure(
+                            meta,
+                            z,
+                            table_column,
+                        ),
+                        w,
+                    )
+                }
+
+                fn synthesize(
+                    &self,
+                    config: Self::Config,
+                    mut layouter: impl Layouter<pallas::Base>,
+                ) -> Result<(), plonk::Error> {
+                    let rangecheck_chip =
+                        NativeRangeCheckChip::<$window_size, $num_bits, $num_windows>::construct(
+                            config.0.clone(),
+                        );
+                    NativeRangeCheckChip::<$window_size, $num_bits, $num_windows>::load_k_table(
+                        &mut layouter,
+                        config.0.k_values_table,
+                    )?;
+
+                    let a = assign_free_advice(layouter.namespace(|| "load a"), config.1, self.a)?;
+                    rangecheck_chip
+                        .copy_range_check(layouter.namespace(|| "copy a and range check"), a)?;
+
+                    rangecheck_chip.witness_range_check(
+                        layouter.namespace(|| "witness a and range check"),
+                        self.a,
+                    )?;
+
+                    Ok(())
+                }
+            }
+        };
+    }
+
+    // cargo test --release --all-features --lib native_range_check -- --nocapture
+    #[test]
+    fn native_range_check_64() {
+        test_circuit!(3, 64, 22);
+        let k = 6;
+
+        let valid_values = vec![
+            pallas::Base::zero(),
+            pallas::Base::one(),
+            pallas::Base::from(u64::MAX),
+            pallas::Base::from(rand::random::<u64>()),
+        ];
+
+        let invalid_values = vec![
+            -pallas::Base::one(),
+            pallas::Base::from_u128(u64::MAX as u128 + 1),
+            -pallas::Base::from_u128(u64::MAX as u128 + 1),
+            pallas::Base::from_u128(rand::random::<u128>()),
+            // The following two are valid
+            // 2 = -28948022309329048855892746252171976963363056481941560715954676764349967630335
+            //-pallas::Base::from_str_vartime(
+            //    "28948022309329048855892746252171976963363056481941560715954676764349967630335",
+            //)
+            //.unwrap(),
+            // 1 = -28948022309329048855892746252171976963363056481941560715954676764349967630336
+            //-pallas::Base::from_str_vartime(
+            //    "28948022309329048855892746252171976963363056481941560715954676764349967630336",
+            //)
+            //.unwrap(),
+        ];
+
+        use plotters::prelude::*;
+        let circuit = RangeCheckCircuit { a: Value::known(pallas::Base::one()) };
+        let root =
+            BitMapBackend::new("target/native_range_check_64_circuit_layout.png", (3840, 2160))
+                .into_drawing_area();
+        root.fill(&WHITE).unwrap();
+        let root =
+            root.titled("64-bit Native Range Check Circuit Layout", ("sans-serif", 60)).unwrap();
+        CircuitLayout::default().render(k, &circuit, &root).unwrap();
+
+        for i in valid_values {
+            println!("64-bit (valid) range check for {:?}", i);
+            let circuit = RangeCheckCircuit { a: Value::known(i) };
+            let prover = MockProver::run(k, &circuit, vec![]).unwrap();
+            prover.assert_satisfied();
+            println!("Constraints satisfied");
+        }
+
+        for i in invalid_values {
+            println!("64-bit (invalid) range check for {:?}", i);
+            let circuit = RangeCheckCircuit { a: Value::known(i) };
+            let prover = MockProver::run(k, &circuit, vec![]).unwrap();
+            assert!(prover.verify().is_err());
+        }
+    }
+
+    #[test]
+    fn native_range_check_128() {
+        test_circuit!(3, 128, 43);
+        let k = 7;
+
+        let valid_values = vec![
+            pallas::Base::zero(),
+            pallas::Base::one(),
+            pallas::Base::from_u128(u128::MAX),
+            pallas::Base::from_u128(rand::random::<u128>()),
+        ];
+
+        let invalid_values = vec![
+            -pallas::Base::one(),
+            pallas::Base::from_u128(u128::MAX) + pallas::Base::one(),
+            -pallas::Base::from_u128(u128::MAX) + pallas::Base::one(),
+            -pallas::Base::from_u128(u128::MAX),
+        ];
+
+        use plotters::prelude::*;
+        let circuit = RangeCheckCircuit { a: Value::known(pallas::Base::one()) };
+        let root =
+            BitMapBackend::new("target/native_range_check_128_circuit_layout.png", (3840, 2160))
+                .into_drawing_area();
+        root.fill(&WHITE).unwrap();
+        let root =
+            root.titled("128-bit Native Range Check Circuit Layout", ("sans-serif", 60)).unwrap();
+        CircuitLayout::default().render(k, &circuit, &root).unwrap();
+
+        for i in valid_values {
+            println!("128-bit (valid) range check for {:?}", i);
+            let circuit = RangeCheckCircuit { a: Value::known(i) };
+            let prover = MockProver::run(k, &circuit, vec![]).unwrap();
+            prover.assert_satisfied();
+            println!("Constraints satisfied");
+        }
+
+        for i in invalid_values {
+            println!("128-bit (invalid) range check for {:?}", i);
+            let circuit = RangeCheckCircuit { a: Value::known(i) };
+            let prover = MockProver::run(k, &circuit, vec![]).unwrap();
+            assert!(prover.verify().is_err());
+        }
+    }
+
+    #[test]
+    fn native_range_check_253() {
+        test_circuit!(3, 253, 85);
+        let k = 8;
+
+        let valid_values = vec![
+            pallas::Base::zero(),
+            pallas::Base::one(),
+            // 2^253 - 1
+            pallas::Base::from_str_vartime(
+                "14474011154664524427946373126085988481658748083205070504932198000989141204991",
+            )
+            .unwrap(),
+            // 2^253 / 2
+            pallas::Base::from_str_vartime(
+                "7237005577332262213973186563042994240829374041602535252466099000494570602496",
+            )
+            .unwrap(),
+        ];
+
+        let invalid_values = vec![
+            -pallas::Base::one(),
+            // p - 1
+            pallas::Base::from_str_vartime(
+                "28948022309329048855892746252171976963363056481941560715954676764349967630336",
+            )
+            .unwrap(),
+        ];
+
+        use plotters::prelude::*;
+        let circuit = RangeCheckCircuit { a: Value::known(pallas::Base::one()) };
+        let root =
+            BitMapBackend::new("target/native_range_check_253_circuit_layout.png", (3840, 2160))
+                .into_drawing_area();
+        root.fill(&WHITE).unwrap();
+        let root =
+            root.titled("253-bit Native Range Check Circuit Layout", ("sans-serif", 60)).unwrap();
+        CircuitLayout::default().render(k, &circuit, &root).unwrap();
+
+        for i in valid_values {
+            println!("253-bit (valid) range check for {:?}", i);
+            let circuit = RangeCheckCircuit { a: Value::known(i) };
+            let prover = MockProver::run(k, &circuit, vec![]).unwrap();
+            prover.assert_satisfied();
+            println!("Constraints satisfied");
+        }
+
+        for i in invalid_values {
+            println!("253-bit (invalid) range check for {:?}", i);
+            let circuit = RangeCheckCircuit { a: Value::known(i) };
+            let prover = MockProver::run(k, &circuit, vec![]).unwrap();
+            assert!(prover.verify().is_err());
+        }
+    }
+}

+ 1 - 27
src/zk/vm.rs

@@ -26,7 +26,7 @@ use pasta_curves::{group::Curve, pallas, Fp};
 
 
 use super::gadget::{
 use super::gadget::{
     arithmetic::{ArithChip, ArithConfig, ArithInstruction},
     arithmetic::{ArithChip, ArithConfig, ArithInstruction},
-    even_bits::{EvenBitsChip, EvenBitsConfig, EvenBitsLookup},
+    //even_bits::{EvenBitsChip, EvenBitsConfig, EvenBitsLookup},
 };
 };
 
 
 use super::assign_free_advice;
 use super::assign_free_advice;
@@ -51,8 +51,6 @@ pub struct VmConfig {
     _sinsemilla_cfg2: SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
     _sinsemilla_cfg2: SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
     poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
     poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
     arith_config: ArithConfig,
     arith_config: ArithConfig,
-    evenbits_config: EvenBitsConfig,
-    //greaterthan_config: GreaterThanConfig,
 }
 }
 
 
 impl VmConfig {
 impl VmConfig {
@@ -93,14 +91,6 @@ impl VmConfig {
     fn arithmetic_chip(&self) -> ArithChip {
     fn arithmetic_chip(&self) -> ArithChip {
         ArithChip::construct(self.arith_config.clone())
         ArithChip::construct(self.arith_config.clone())
     }
     }
-
-    fn evenbits_chip(&self) -> EvenBitsChip<pallas::Base, 24> {
-        EvenBitsChip::construct(self.evenbits_config.clone())
-    }
-
-    //fn greaterthan_chip(&self) -> GreaterThanChip<pallas::Base, 24> {
-    //  GreaterThanChip::construct(self.greaterthan_config.clone())
-    //    }
 }
 }
 
 
 #[derive(Clone, Default)]
 #[derive(Clone, Default)]
@@ -200,13 +190,6 @@ impl Circuit<pallas::Base> for ZkCircuit {
         // Configuration for the Arithmetic chip
         // Configuration for the Arithmetic chip
         let arith_config = ArithChip::configure(meta, advices[7], advices[8], advices[6]);
         let arith_config = ArithChip::configure(meta, advices[7], advices[8], advices[6]);
 
 
-        // Configuration for the EvenBits chip
-        let evenbits_config = EvenBitsChip::<pallas::Base, 24>::configure(meta);
-
-        // Configuration for the GreaterThan chip
-        //let greaterthan_config =
-        //            GreaterThanChip::<pallas::Base, 24>::configure(meta, [advices[8], advices[9]], primary);
-
         // Configuration for a Sinsemilla hash instantiation and a
         // Configuration for a Sinsemilla hash instantiation and a
         // Merkle hash instantiation using this Sinsemilla instance.
         // Merkle hash instantiation using this Sinsemilla instance.
         // Since the Sinsemilla config uses only 5 advice columns,
         // Since the Sinsemilla config uses only 5 advice columns,
@@ -247,8 +230,6 @@ impl Circuit<pallas::Base> for ZkCircuit {
             _sinsemilla_cfg2,
             _sinsemilla_cfg2,
             poseidon_config,
             poseidon_config,
             arith_config,
             arith_config,
-            evenbits_config,
-            //greaterthan_config,
         }
         }
     }
     }
 
 
@@ -274,13 +255,6 @@ impl Circuit<pallas::Base> for ZkCircuit {
         // Construct the Arithmetic chip.
         // Construct the Arithmetic chip.
         let arith_chip = config.arithmetic_chip();
         let arith_chip = config.arithmetic_chip();
 
 
-        // Construct the EvenBits chip.
-        let eb_chip = config.evenbits_chip();
-        eb_chip.alloc_table(&mut layouter.namespace(|| "alloc table"))?;
-
-        // Construct the GreaterThan chip.
-        //let gt_chip = config.greaterthan_chip();
-
         // This constant one is used for short multiplication
         // This constant one is used for short multiplication
         let one = assign_free_advice(
         let one = assign_free_advice(
             layouter.namespace(|| "Load constant one"),
             layouter.namespace(|| "Load constant one"),