Просмотр исходного кода

zk::vm: Refactor range checks to reuse table

therealyingtong 2 лет назад
Родитель
Сommit
ce35921cab
3 измененных файлов с 89 добавлено и 158 удалено
  1. 28 87
      src/zk/gadget/less_than.rs
  2. 30 42
      src/zk/gadget/native_range_check.rs
  3. 31 29
      src/zk/vm.rs

+ 28 - 87
src/zk/gadget/less_than.rs

@@ -34,34 +34,26 @@ use halo2_proofs::{
 use super::native_range_check::{NativeRangeCheckChip, NativeRangeCheckConfig};
 
 #[derive(Clone, Debug)]
-pub struct LessThanConfig<
-    const WINDOW_SIZE: usize,
-    const NUM_OF_BITS: usize,
-    const NUM_OF_WINDOWS: usize,
-> {
+pub struct LessThanConfig<const WINDOW_SIZE: usize, const NUM_OF_BITS: usize> {
     pub s_lt: Selector,
     pub s_leq: Selector,
     pub a: Column<Advice>,
     pub b: Column<Advice>,
     pub a_offset: Column<Advice>,
-    pub range_a_config: NativeRangeCheckConfig<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>,
-    pub range_a_offset_config: NativeRangeCheckConfig<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>,
+    pub range_a_config: NativeRangeCheckConfig<WINDOW_SIZE, NUM_OF_BITS>,
+    pub range_a_offset_config: NativeRangeCheckConfig<WINDOW_SIZE, NUM_OF_BITS>,
     pub k_values_table: TableColumn,
 }
 
 #[derive(Clone, Debug)]
-pub struct LessThanChip<
-    const WINDOW_SIZE: usize,
-    const NUM_OF_BITS: usize,
-    const NUM_OF_WINDOWS: usize,
-> {
-    config: LessThanConfig<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>,
+pub struct LessThanChip<const WINDOW_SIZE: usize, const NUM_OF_BITS: usize> {
+    config: LessThanConfig<WINDOW_SIZE, NUM_OF_BITS>,
 }
 
-impl<const WINDOW_SIZE: usize, const NUM_OF_BITS: usize, const NUM_OF_WINDOWS: usize>
-    Chip<pallas::Base> for LessThanChip<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>
+impl<const WINDOW_SIZE: usize, const NUM_OF_BITS: usize> Chip<pallas::Base>
+    for LessThanChip<WINDOW_SIZE, NUM_OF_BITS>
 {
-    type Config = LessThanConfig<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>;
+    type Config = LessThanConfig<WINDOW_SIZE, NUM_OF_BITS>;
     type Loaded = ();
 
     fn config(&self) -> &Self::Config {
@@ -73,10 +65,8 @@ impl<const WINDOW_SIZE: usize, const NUM_OF_BITS: usize, const NUM_OF_WINDOWS: u
     }
 }
 
-impl<const WINDOW_SIZE: usize, const NUM_OF_BITS: usize, const NUM_OF_WINDOWS: usize>
-    LessThanChip<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>
-{
-    pub fn construct(config: LessThanConfig<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>) -> Self {
+impl<const WINDOW_SIZE: usize, const NUM_OF_BITS: usize> LessThanChip<WINDOW_SIZE, NUM_OF_BITS> {
+    pub fn construct(config: LessThanConfig<WINDOW_SIZE, NUM_OF_BITS>) -> Self {
         Self { config }
     }
 
@@ -88,7 +78,7 @@ impl<const WINDOW_SIZE: usize, const NUM_OF_BITS: usize, const NUM_OF_WINDOWS: u
         z1: Column<Advice>,
         z2: Column<Advice>,
         k_values_table: TableColumn,
-    ) -> LessThanConfig<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS> {
+    ) -> LessThanConfig<WINDOW_SIZE, NUM_OF_BITS> {
         let s_lt = meta.selector();
         let s_leq = meta.selector();
 
@@ -100,18 +90,10 @@ impl<const WINDOW_SIZE: usize, const NUM_OF_BITS: usize, const NUM_OF_WINDOWS: u
 
         // configure range check for `a` and `offset`
         let range_a_config =
-            NativeRangeCheckChip::<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>::configure(
-                meta,
-                z1,
-                k_values_table,
-            );
+            NativeRangeCheckChip::<WINDOW_SIZE, NUM_OF_BITS>::configure(meta, z1, k_values_table);
 
         let range_a_offset_config =
-            NativeRangeCheckChip::<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>::configure(
-                meta,
-                z2,
-                k_values_table,
-            );
+            NativeRangeCheckChip::<WINDOW_SIZE, NUM_OF_BITS>::configure(meta, z2, k_values_table);
 
         let config = LessThanConfig {
             s_lt,
@@ -223,14 +205,12 @@ impl<const WINDOW_SIZE: usize, const NUM_OF_BITS: usize, const NUM_OF_WINDOWS: u
         a: AssignedCell<pallas::Base, pallas::Base>,
         a_offset: AssignedCell<pallas::Base, pallas::Base>,
     ) -> Result<(), Error> {
-        let range_a_chip =
-            NativeRangeCheckChip::<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>::construct(
-                self.config.range_a_config.clone(),
-            );
-        let range_a_offset_chip =
-            NativeRangeCheckChip::<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>::construct(
-                self.config.range_a_offset_config.clone(),
-            );
+        let range_a_chip = NativeRangeCheckChip::<WINDOW_SIZE, NUM_OF_BITS>::construct(
+            self.config.range_a_config.clone(),
+        );
+        let range_a_offset_chip = NativeRangeCheckChip::<WINDOW_SIZE, NUM_OF_BITS>::construct(
+            self.config.range_a_offset_config.clone(),
+        );
 
         range_a_chip.copy_range_check(layouter.namespace(|| "a copy_range_check"), a)?;
 
@@ -279,7 +259,7 @@ mod tests {
     };
 
     macro_rules! test_circuit {
-        ($k: expr, $strict:expr, $window_size:expr, $num_bits:expr, $num_windows:expr, $valid_pairs:expr, $invalid_pairs:expr) => {
+        ($k: expr, $strict:expr, $window_size:expr, $num_bits:expr, $valid_pairs:expr, $invalid_pairs:expr) => {
             #[derive(Default)]
             struct LessThanCircuit {
                 a: Value<pallas::Base>,
@@ -287,8 +267,7 @@ mod tests {
             }
 
             impl Circuit<pallas::Base> for LessThanCircuit {
-                type Config =
-                    (LessThanConfig<$window_size, $num_bits, $num_windows>, Column<Advice>);
+                type Config = (LessThanConfig<$window_size, $num_bits>, Column<Advice>);
                 type FloorPlanner = floor_planner::V1;
                 type Params = ();
 
@@ -312,7 +291,7 @@ mod tests {
                     meta.enable_constant(constants);
 
                     (
-                        LessThanChip::<$window_size, $num_bits, $num_windows>::configure(
+                        LessThanChip::<$window_size, $num_bits>::configure(
                             meta,
                             a,
                             b,
@@ -331,11 +310,9 @@ mod tests {
                     mut layouter: impl Layouter<pallas::Base>,
                 ) -> Result<(), Error> {
                     let less_than_chip =
-                        LessThanChip::<$window_size, $num_bits, $num_windows>::construct(
-                            config.0.clone(),
-                        );
+                        LessThanChip::<$window_size, $num_bits>::construct(config.0.clone());
 
-                    NativeRangeCheckChip::<$window_size, $num_bits, $num_windows>::load_k_table(
+                    NativeRangeCheckChip::<$window_size, $num_bits>::load_k_table(
                         &mut layouter,
                         config.0.k_values_table,
                     )?;
@@ -383,7 +360,6 @@ mod tests {
         let k = 5;
         const WINDOW_SIZE: usize = 3;
         const NUM_OF_BITS: usize = 64;
-        const NUM_OF_WINDOWS: usize = 22;
 
         let valid_pairs = [
             (pallas::Base::ZERO, pallas::Base::ZERO),
@@ -400,15 +376,7 @@ mod tests {
             (pallas::Base::from(u64::MAX), pallas::Base::ZERO),
             (pallas::Base::ONE, pallas::Base::ZERO),
         ];
-        test_circuit!(
-            k,
-            false,
-            WINDOW_SIZE,
-            NUM_OF_BITS,
-            NUM_OF_WINDOWS,
-            valid_pairs,
-            invalid_pairs
-        );
+        test_circuit!(k, false, WINDOW_SIZE, NUM_OF_BITS, valid_pairs, invalid_pairs);
     }
 
     #[test]
@@ -416,7 +384,6 @@ mod tests {
         let k = 5;
         const WINDOW_SIZE: usize = 3;
         const NUM_OF_BITS: usize = 64;
-        const NUM_OF_WINDOWS: usize = 22;
 
         let valid_pairs = [
             (pallas::Base::from(13), pallas::Base::from(15)),
@@ -433,15 +400,7 @@ mod tests {
             (pallas::Base::ONE, pallas::Base::ZERO),
             (pallas::Base::from(u64::MAX), pallas::Base::from(u64::MAX)),
         ];
-        test_circuit!(
-            k,
-            true,
-            WINDOW_SIZE,
-            NUM_OF_BITS,
-            NUM_OF_WINDOWS,
-            valid_pairs,
-            invalid_pairs
-        );
+        test_circuit!(k, true, WINDOW_SIZE, NUM_OF_BITS, valid_pairs, invalid_pairs);
     }
 
     #[test]
@@ -449,7 +408,6 @@ mod tests {
         let k = 7;
         const WINDOW_SIZE: usize = 3;
         const NUM_OF_BITS: usize = 253;
-        const NUM_OF_WINDOWS: usize = 85;
 
         const P_MINUS_1: pallas::Base = pallas::Base::from_raw([
             0x992d30ed00000000,
@@ -493,15 +451,7 @@ mod tests {
             (MAX_253 + pallas::Base::ONE, MAX_253 + pallas::Base::from(2)),
         ];
 
-        test_circuit!(
-            k,
-            false,
-            WINDOW_SIZE,
-            NUM_OF_BITS,
-            NUM_OF_WINDOWS,
-            valid_pairs,
-            invalid_pairs
-        );
+        test_circuit!(k, false, WINDOW_SIZE, NUM_OF_BITS, valid_pairs, invalid_pairs);
     }
 
     #[test]
@@ -509,7 +459,6 @@ mod tests {
         let k = 7;
         const WINDOW_SIZE: usize = 3;
         const NUM_OF_BITS: usize = 253;
-        const NUM_OF_WINDOWS: usize = 85;
 
         const P_MINUS_1: pallas::Base = pallas::Base::from_raw([
             0x992d30ed00000000,
@@ -553,14 +502,6 @@ mod tests {
             (MAX_253 + pallas::Base::ONE, MAX_253 + pallas::Base::from(2)),
         ];
 
-        test_circuit!(
-            k,
-            true,
-            WINDOW_SIZE,
-            NUM_OF_BITS,
-            NUM_OF_WINDOWS,
-            valid_pairs,
-            invalid_pairs
-        );
+        test_circuit!(k, true, WINDOW_SIZE, NUM_OF_BITS, valid_pairs, invalid_pairs);
     }
 }

+ 30 - 42
src/zk/gadget/native_range_check.rs

@@ -28,11 +28,7 @@ use halo2_proofs::{
 };
 
 #[derive(Clone, Debug)]
-pub struct NativeRangeCheckConfig<
-    const WINDOW_SIZE: usize,
-    const NUM_BITS: usize,
-    const NUM_WINDOWS: usize,
-> {
+pub struct NativeRangeCheckConfig<const WINDOW_SIZE: usize, const NUM_BITS: usize> {
     pub z: Column<Advice>,
     pub s_rc: Selector,
     pub s_short: Selector,
@@ -40,18 +36,14 @@ pub struct NativeRangeCheckConfig<
 }
 
 #[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>,
+pub struct NativeRangeCheckChip<const WINDOW_SIZE: usize, const NUM_BITS: usize> {
+    config: NativeRangeCheckConfig<WINDOW_SIZE, NUM_BITS>,
 }
 
-impl<const WINDOW_SIZE: usize, const NUM_BITS: usize, const NUM_WINDOWS: usize> Chip<pallas::Base>
-    for NativeRangeCheckChip<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>
+impl<const WINDOW_SIZE: usize, const NUM_BITS: usize> Chip<pallas::Base>
+    for NativeRangeCheckChip<WINDOW_SIZE, NUM_BITS>
 {
-    type Config = NativeRangeCheckConfig<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>;
+    type Config = NativeRangeCheckConfig<WINDOW_SIZE, NUM_BITS>;
     type Loaded = ();
 
     fn config(&self) -> &Self::Config {
@@ -63,10 +55,8 @@ impl<const WINDOW_SIZE: usize, const NUM_BITS: usize, const NUM_WINDOWS: usize>
     }
 }
 
-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 {
+impl<const WINDOW_SIZE: usize, const NUM_BITS: usize> NativeRangeCheckChip<WINDOW_SIZE, NUM_BITS> {
+    pub fn construct(config: NativeRangeCheckConfig<WINDOW_SIZE, NUM_BITS>) -> Self {
         Self { config }
     }
 
@@ -74,7 +64,7 @@ impl<const WINDOW_SIZE: usize, const NUM_BITS: usize, const NUM_WINDOWS: usize>
         meta: &mut ConstraintSystem<pallas::Base>,
         z: Column<Advice>,
         k_values_table: TableColumn,
-    ) -> NativeRangeCheckConfig<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS> {
+    ) -> NativeRangeCheckConfig<WINDOW_SIZE, NUM_BITS> {
         // Enable permutation on z column
         meta.enable_equality(z);
 
@@ -157,8 +147,12 @@ impl<const WINDOW_SIZE: usize, const NUM_BITS: usize, const NUM_WINDOWS: usize>
     }
 
     fn decompose_value(value: &pallas::Base) -> Vec<[bool; WINDOW_SIZE]> {
-        let bits: Vec<bool> =
-            value.to_le_bits().into_iter().take(WINDOW_SIZE * NUM_WINDOWS).collect();
+        let bits: Vec<_> = value
+            .to_le_bits()
+            .into_iter()
+            .take(NUM_BITS)
+            .chain(std::iter::repeat(false).take(WINDOW_SIZE - (NUM_BITS % WINDOW_SIZE)))
+            .collect();
 
         bits.chunks_exact(WINDOW_SIZE)
             .map(|x| {
@@ -180,22 +174,21 @@ impl<const WINDOW_SIZE: usize, const NUM_BITS: usize, const NUM_WINDOWS: usize>
         z_0: AssignedCell<pallas::Base, pallas::Base>,
         offset: usize,
     ) -> Result<(), plonk::Error> {
-        // Check NUM_WINDOWS is the minimum required to cover NUM_BITS
-        assert!(WINDOW_SIZE * NUM_WINDOWS < NUM_BITS + WINDOW_SIZE);
+        let num_windows = NUM_BITS.div_ceil(WINDOW_SIZE);
 
         // The number of bits in the last chunk.
-        let last_chunk_length = NUM_BITS - (WINDOW_SIZE * (NUM_WINDOWS - 1));
+        let last_chunk_length = NUM_BITS - (WINDOW_SIZE * (num_windows - 1));
         assert!(last_chunk_length > 0);
 
         // Enable selectors for running sum decomposition
-        for index in 0..NUM_WINDOWS {
+        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;
         // Convert `z_0` into a `Vec<Value<Fp>>` where each value corresponds to a chunk.
-        let decomposed_chunks = z.value().map(Self::decompose_value).transpose_vec(NUM_WINDOWS);
+        let decomposed_chunks = z.value().map(Self::decompose_value).transpose_vec(num_windows);
 
         let two_pow_k = pallas::Base::from(1 << WINDOW_SIZE as u64);
         let two_pow_k_inverse = Value::known(two_pow_k.invert().unwrap());
@@ -232,7 +225,7 @@ impl<const WINDOW_SIZE: usize, const NUM_BITS: usize, const NUM_WINDOWS: usize>
             z = z_next.clone();
         }
 
-        assert!(z_values.len() == NUM_WINDOWS + 1);
+        assert!(z_values.len() == num_windows + 1);
 
         // Constrain the last chunk zₘ = 0
         region.constrain_constant(z_values.last().unwrap().cell(), pallas::Base::zero())?;
@@ -248,7 +241,7 @@ impl<const WINDOW_SIZE: usize, const NUM_BITS: usize, const NUM_WINDOWS: usize>
         //  |  0  |    0    |             1 >> s              |
 
         if last_chunk_length < WINDOW_SIZE {
-            let s_short_offset = NUM_WINDOWS + offset;
+            let s_short_offset = num_windows + offset;
             self.config.s_short.enable(region, s_short_offset)?;
 
             // 1 >> s = 2^{-s}
@@ -324,15 +317,14 @@ mod tests {
     };
 
     macro_rules! test_circuit {
-        ($k: expr, $window_size:expr, $num_bits: expr, $num_windows:expr, $valid_values:expr, $invalid_values:expr) => {
+        ($k: expr, $window_size:expr, $num_bits: expr, $valid_values:expr, $invalid_values: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 Config = (NativeRangeCheckConfig<$window_size, $num_bits>, Column<Advice>);
                 type FloorPlanner = floor_planner::V1;
                 type Params = ();
 
@@ -349,7 +341,7 @@ mod tests {
                     let constants = meta.fixed_column();
                     meta.enable_constant(constants);
                     (
-                        NativeRangeCheckChip::<$window_size, $num_bits, $num_windows>::configure(
+                        NativeRangeCheckChip::<$window_size, $num_bits>::configure(
                             meta,
                             z,
                             table_column,
@@ -364,10 +356,10 @@ mod tests {
                     mut layouter: impl Layouter<pallas::Base>,
                 ) -> Result<(), plonk::Error> {
                     let rangecheck_chip =
-                        NativeRangeCheckChip::<$window_size, $num_bits, $num_windows>::construct(
+                        NativeRangeCheckChip::<$window_size, $num_bits>::construct(
                             config.0.clone(),
                         );
-                    NativeRangeCheckChip::<$window_size, $num_bits, $num_windows>::load_k_table(
+                    NativeRangeCheckChip::<$window_size, $num_bits>::load_k_table(
                         &mut layouter,
                         config.0.k_values_table,
                     )?;
@@ -421,14 +413,13 @@ mod tests {
         let k = 6;
         const WINDOW_SIZE: usize = 5;
         const NUM_BITS: usize = 2;
-        const NUM_WINDOWS: usize = 1;
 
         // [0, 1, 2, 3]
         let valid_values: Vec<_> = (0..(1 << NUM_BITS)).map(pallas::Base::from).collect();
         // [4, 5, 6, ..., 32]
         let invalid_values: Vec<_> =
             ((1 << NUM_BITS)..=(1 << WINDOW_SIZE)).map(pallas::Base::from).collect();
-        test_circuit!(k, WINDOW_SIZE, NUM_BITS, NUM_WINDOWS, valid_values, invalid_values);
+        test_circuit!(k, WINDOW_SIZE, NUM_BITS, valid_values, invalid_values);
     }
 
     #[test]
@@ -436,7 +427,6 @@ mod tests {
         let k = 6;
         const WINDOW_SIZE: usize = 3;
         const NUM_BITS: usize = 64;
-        const NUM_WINDOWS: usize = 22;
 
         let valid_values = vec![
             pallas::Base::zero(),
@@ -461,7 +451,7 @@ mod tests {
             //)
             //.unwrap(),
         ];
-        test_circuit!(k, WINDOW_SIZE, NUM_BITS, NUM_WINDOWS, valid_values, invalid_values);
+        test_circuit!(k, WINDOW_SIZE, NUM_BITS, valid_values, invalid_values);
     }
 
     #[test]
@@ -469,7 +459,6 @@ mod tests {
         let k = 7;
         const WINDOW_SIZE: usize = 3;
         const NUM_BITS: usize = 128;
-        const NUM_WINDOWS: usize = 43;
 
         let valid_values = vec![
             pallas::Base::zero(),
@@ -484,7 +473,7 @@ mod tests {
             -pallas::Base::from_u128(u128::MAX) + pallas::Base::one(),
             -pallas::Base::from_u128(u128::MAX),
         ];
-        test_circuit!(k, WINDOW_SIZE, NUM_BITS, NUM_WINDOWS, valid_values, invalid_values);
+        test_circuit!(k, WINDOW_SIZE, NUM_BITS, valid_values, invalid_values);
     }
 
     #[test]
@@ -492,7 +481,6 @@ mod tests {
         let k = 8;
         const WINDOW_SIZE: usize = 3;
         const NUM_BITS: usize = 253;
-        const NUM_WINDOWS: usize = 85;
 
         // 2^253 - 1
         let max_253 = pallas::Base::from_str_vartime(
@@ -520,6 +508,6 @@ mod tests {
             .unwrap(),
             max_253 + pallas::Base::one(),
         ];
-        test_circuit!(k, WINDOW_SIZE, NUM_BITS, NUM_WINDOWS, valid_values, invalid_values);
+        test_circuit!(k, WINDOW_SIZE, NUM_BITS, valid_values, invalid_values);
     }
 }

+ 31 - 29
src/zk/vm.rs

@@ -20,7 +20,7 @@ use std::collections::HashSet;
 
 use darkfi_sdk::crypto::{
     constants::{
-        sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
+        sinsemilla::{OrchardCommitDomains, OrchardHashDomains, K},
         util::gen_const_array,
         ConstBaseFieldElement, OrchardFixedBases, OrchardFixedBasesFull, ValueCommitV,
         MERKLE_DEPTH_ORCHARD,
@@ -107,13 +107,13 @@ enum VmChip {
     Arithmetic(ArithConfig),
 
     /// 64 bit native range check
-    NativeRange64(NativeRangeCheckConfig<3, 64, 22>),
+    NativeRange64(NativeRangeCheckConfig<K, 64>),
 
     /// 253 bit native range check
-    NativeRange253(NativeRangeCheckConfig<3, 253, 85>),
+    NativeRange253(NativeRangeCheckConfig<K, 253>),
 
     /// 253 bit `a < b` check
-    LessThan(LessThanConfig<3, 253, 85>),
+    LessThan(LessThanConfig<K, 253>),
 
     /// Boolean check
     BoolCheck(SmallRangeCheckConfig),
@@ -221,7 +221,7 @@ impl VmConfig {
         Some(ZeroCondChip::construct(zerocond_config.clone()))
     }
 
-    fn rangecheck64_chip(&self) -> Option<NativeRangeCheckChip<3, 64, 22>> {
+    fn rangecheck64_chip(&self) -> Option<NativeRangeCheckChip<K, 64>> {
         let Some(VmChip::NativeRange64(range_config)) =
             self.chips.iter().find(|&c| matches!(c, VmChip::NativeRange64(_)))
         else {
@@ -231,7 +231,7 @@ impl VmConfig {
         Some(NativeRangeCheckChip::construct(range_config.clone()))
     }
 
-    fn rangecheck253_chip(&self) -> Option<NativeRangeCheckChip<3, 253, 85>> {
+    fn rangecheck253_chip(&self) -> Option<NativeRangeCheckChip<K, 253>> {
         let Some(VmChip::NativeRange253(range_config)) =
             self.chips.iter().find(|&c| matches!(c, VmChip::NativeRange253(_)))
         else {
@@ -241,7 +241,7 @@ impl VmConfig {
         Some(NativeRangeCheckChip::construct(range_config.clone()))
     }
 
-    fn lessthan_chip(&self) -> Option<LessThanChip<3, 253, 85>> {
+    fn lessthan_chip(&self) -> Option<LessThanChip<K, 253>> {
         let Some(VmChip::LessThan(lessthan_config)) =
             self.chips.iter().find(|&c| matches!(c, VmChip::LessThan(_)))
         else {
@@ -497,27 +497,19 @@ impl Circuit<pallas::Base> for ZkCircuit {
         );
 
         // K-table for 64 bit range check lookups
-        let k_values_table_64 = meta.lookup_table_column();
         let native_64_range_check_config =
-            NativeRangeCheckChip::<3, 64, 22>::configure(meta, advices[8], k_values_table_64);
+            NativeRangeCheckChip::<K, 64>::configure(meta, advices[8], table_idx);
 
         // K-table for 253 bit range check lookups
-        let k_values_table_253 = meta.lookup_table_column();
         let native_253_range_check_config =
-            NativeRangeCheckChip::<3, 253, 85>::configure(meta, advices[8], k_values_table_253);
+            NativeRangeCheckChip::<K, 253>::configure(meta, advices[8], table_idx);
 
         // TODO: FIXME: Configure these better, this is just a stop-gap
         let z1 = meta.advice_column();
         let z2 = meta.advice_column();
 
-        let lessthan_config = LessThanChip::<3, 253, 85>::configure(
-            meta,
-            advices[6],
-            advices[7],
-            advices[8],
-            z1,
-            z2,
-            k_values_table_253,
+        let lessthan_config = LessThanChip::<K, 253>::configure(
+            meta, advices[6], advices[7], advices[8], z1, z2, table_idx,
         );
 
         // Configuration for boolean checks, it uses the small_range_check
@@ -582,18 +574,26 @@ impl Circuit<pallas::Base> for ZkCircuit {
             SinsemillaChip::load(sinsemilla_cfg1.clone(), &mut layouter)?;
         }
 
+        let no_sinsemilla_chip =
+            config.chips.iter().find(|&c| matches!(c, VmChip::Sinsemilla(_))).is_none();
+
         // Construct the 64-bit NativeRangeCheck chip
         let rangecheck64_chip = config.rangecheck64_chip();
         if let Some(VmChip::NativeRange64(rangecheck64_config)) =
             config.chips.iter().find(|&c| matches!(c, VmChip::NativeRange64(_)))
         {
-            trace!(target: "zk::vm", "Initializing k table for 64bit NativeRangeCheck");
-            NativeRangeCheckChip::<3, 64, 22>::load_k_table(
-                &mut layouter,
-                rangecheck64_config.k_values_table,
-            )?;
+            if no_sinsemilla_chip {
+                trace!(target: "zk::vm", "Initializing k table for 64bit NativeRangeCheck");
+                NativeRangeCheckChip::<K, 64>::load_k_table(
+                    &mut layouter,
+                    rangecheck64_config.k_values_table,
+                )?;
+            }
         }
 
+        let no_rangecheck64_chip =
+            config.chips.iter().find(|&c| matches!(c, VmChip::NativeRange64(_))).is_none();
+
         // Construct the 253-bit NativeRangeCheck and LessThan chips.
         let rangecheck253_chip = config.rangecheck253_chip();
         let lessthan_chip = config.lessthan_chip();
@@ -601,11 +601,13 @@ impl Circuit<pallas::Base> for ZkCircuit {
         if let Some(VmChip::NativeRange253(rangecheck253_config)) =
             config.chips.iter().find(|&c| matches!(c, VmChip::NativeRange253(_)))
         {
-            trace!(target: "zk::vm", "Initializing k table for 253bit NativeRangeCheck");
-            NativeRangeCheckChip::<3, 253, 85>::load_k_table(
-                &mut layouter,
-                rangecheck253_config.k_values_table,
-            )?;
+            if no_sinsemilla_chip && no_rangecheck64_chip {
+                trace!(target: "zk::vm", "Initializing k table for 253bit NativeRangeCheck");
+                NativeRangeCheckChip::<K, 253>::load_k_table(
+                    &mut layouter,
+                    rangecheck253_config.k_values_table,
+                )?;
+            }
         }
 
         // Construct the ECC chip.