Browse Source

cuckoo: make hash value generic

Lennart Braun 2 years ago
parent
commit
904b09234e
6 changed files with 192 additions and 103 deletions
  1. 1 0
      cuckoo/Cargo.toml
  2. 7 3
      cuckoo/benches/cuckoo.rs
  3. 1 1
      cuckoo/benches/hash.rs
  4. 88 44
      cuckoo/src/cuckoo.rs
  5. 61 23
      cuckoo/src/hash.rs
  6. 34 32
      dpf/src/mpdpf.rs

+ 1 - 0
cuckoo/Cargo.toml

@@ -7,6 +7,7 @@ edition = "2021"
 
 [dependencies]
 utils = { path = "../utils" }
+funty = "2.0.0"
 libm = "0.2.5"
 rand = "0.8.5"
 rand_chacha = "0.3.1"

+ 7 - 3
cuckoo/benches/cuckoo.rs

@@ -4,7 +4,7 @@ use cuckoo::hash::AesHashFunction;
 
 pub fn bench_hash_domain_into_buckets(c: &mut Criterion) {
     let number_inputs = 1_000;
-    let parameters = Parameters::<AesHashFunction>::sample(number_inputs);
+    let parameters = Parameters::<AesHashFunction<u32>, _>::sample(number_inputs);
     let hasher = Hasher::new(parameters);
     let domain_size = 100_000;
     c.bench_function("Hasher<AesHashFunction>.hash_domain_into_buckets", |b| {
@@ -14,7 +14,7 @@ pub fn bench_hash_domain_into_buckets(c: &mut Criterion) {
 
 pub fn bench_cuckoo_hash_items(c: &mut Criterion) {
     let number_inputs = 1_000;
-    let parameters = Parameters::<AesHashFunction>::sample(number_inputs);
+    let parameters = Parameters::<AesHashFunction<u32>, _>::sample(number_inputs);
     let hasher = Hasher::new(parameters);
     let items: Vec<u64> = (0..number_inputs as u64).collect();
     c.bench_function("Hasher<AesHashFunction>.cuckoo_hash_items", |b| {
@@ -22,5 +22,9 @@ pub fn bench_cuckoo_hash_items(c: &mut Criterion) {
     });
 }
 
-criterion_group!(benches, bench_hash_domain_into_buckets, bench_cuckoo_hash_items);
+criterion_group!(
+    benches,
+    bench_hash_domain_into_buckets,
+    bench_cuckoo_hash_items
+);
 criterion_main!(benches);

+ 1 - 1
cuckoo/benches/hash.rs

@@ -4,7 +4,7 @@ use cuckoo::hash::{AesHashFunction, HashFunction};
 pub fn bench_hash_range(c: &mut Criterion) {
     let n = 100_000;
     let range_size = 10_000;
-    let hash_function = AesHashFunction::sample(range_size);
+    let hash_function = AesHashFunction::<u32>::sample(range_size);
     c.bench_function("AesHashFunction.hash_range", |b| {
         b.iter(|| hash_function.hash_range(black_box(0..n)))
     });

+ 88 - 44
cuckoo/src/cuckoo.rs

@@ -1,4 +1,4 @@
-use crate::hash::HashFunction;
+use crate::hash::{HashFunction, HashFunctionValue};
 use core::array;
 use libm::erf;
 use rand::{Rng, SeedableRng};
@@ -9,15 +9,20 @@ use std::fmt::Debug;
 
 pub const NUMBER_HASH_FUNCTIONS: usize = 3;
 
-pub struct Parameters<H: HashFunction> {
+pub struct Parameters<H: HashFunction<Value>, Value: HashFunctionValue>
+where
+    <Value as TryInto<usize>>::Error: Debug,
+{
     number_inputs: usize,
     number_buckets: usize,
     hash_function_descriptions: [H::Description; 3],
 }
 
-impl<H> Debug for Parameters<H>
+impl<H, Value> Debug for Parameters<H, Value>
 where
-    H: HashFunction,
+    H: HashFunction<Value>,
+    Value: HashFunctionValue,
+    <Value as TryInto<usize>>::Error: Debug,
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
         write!(f, "Parameters<H>{{")?;
@@ -32,14 +37,27 @@ where
         Ok(())
     }
 }
-impl<H: HashFunction> Copy for Parameters<H> {}
-impl<H: HashFunction> Clone for Parameters<H> {
+impl<H: HashFunction<Value>, Value> Copy for Parameters<H, Value>
+where
+    Value: HashFunctionValue,
+    <Value as TryInto<usize>>::Error: Debug,
+{
+}
+impl<H: HashFunction<Value>, Value> Clone for Parameters<H, Value>
+where
+    Value: HashFunctionValue,
+    <Value as TryInto<usize>>::Error: Debug,
+{
     fn clone(&self) -> Self {
         *self
     }
 }
 
-impl<H: HashFunction> Parameters<H> {
+impl<H: HashFunction<Value>, Value> Parameters<H, Value>
+where
+    Value: HashFunctionValue,
+    <Value as TryInto<usize>>::Error: Debug,
+{
     /// Samples three hash functions from given seed
     pub fn from_seed(number_inputs: usize, seed: [u8; 32]) -> Self {
         let number_buckets = Self::compute_number_buckets(number_inputs);
@@ -48,7 +66,7 @@ impl<H: HashFunction> Parameters<H> {
             H::from_seed(number_buckets.try_into().unwrap(), rng.gen()).to_description()
         });
 
-        Parameters::<H> {
+        Parameters::<H, Value> {
             number_inputs,
             number_buckets,
             hash_function_descriptions,
@@ -61,7 +79,7 @@ impl<H: HashFunction> Parameters<H> {
         let hash_function_descriptions =
             array::from_fn(|_| H::sample(number_buckets.try_into().unwrap()).to_description());
 
-        Parameters::<H> {
+        Parameters::<H, Value> {
             number_inputs,
             number_buckets,
             hash_function_descriptions,
@@ -103,42 +121,48 @@ impl<H: HashFunction> Parameters<H> {
     }
 }
 
-pub struct Hasher<H: HashFunction> {
-    parameters: Parameters<H>,
+pub struct Hasher<H: HashFunction<Value>, Value: HashFunctionValue>
+where
+    <Value as TryInto<usize>>::Error: Debug,
+{
+    parameters: Parameters<H, Value>,
     hash_functions: [H; 3],
 }
 
-impl<H: HashFunction> Hasher<H> {
+impl<H: HashFunction<Value>, Value: HashFunctionValue> Hasher<H, Value>
+where
+    <Value as TryInto<usize>>::Error: Debug,
+{
     pub const UNOCCUPIED: u64 = u64::MAX;
 
     /// Create `Hasher` object with given parameters
-    pub fn new(parameters: Parameters<H>) -> Self {
+    pub fn new(parameters: Parameters<H, Value>) -> Self {
         let hash_functions =
             array::from_fn(|i| H::from_description(parameters.hash_function_descriptions[i]));
-        Hasher::<H> {
+        Hasher {
             parameters,
             hash_functions,
         }
     }
 
     /// Return the parameters
-    pub fn get_parameters(&self) -> &Parameters<H> {
+    pub fn get_parameters(&self) -> &Parameters<H, Value> {
         &self.parameters
     }
 
     /// Hash a single item with the given hash function
-    pub fn hash_single(&self, hash_function_index: usize, item: u64) -> u64 {
+    pub fn hash_single(&self, hash_function_index: usize, item: u64) -> Value {
         assert!(hash_function_index < NUMBER_HASH_FUNCTIONS);
         self.hash_functions[hash_function_index].hash_single(item)
     }
 
     /// Hash the whole domain [0, domain_size) with all three hash functions
-    pub fn hash_domain(&self, domain_size: u64) -> [Vec<u64>; NUMBER_HASH_FUNCTIONS] {
+    pub fn hash_domain(&self, domain_size: u64) -> [Vec<Value>; NUMBER_HASH_FUNCTIONS] {
         array::from_fn(|i| self.hash_functions[i].hash_range(0..domain_size))
     }
 
     /// Hash the given items with all three hash functions
-    pub fn hash_items(&self, items: &[u64]) -> [Vec<u64>; NUMBER_HASH_FUNCTIONS] {
+    pub fn hash_items(&self, items: &[u64]) -> [Vec<Value>; NUMBER_HASH_FUNCTIONS] {
         array::from_fn(|i| self.hash_functions[i].hash_slice(items))
     }
 
@@ -149,7 +173,7 @@ impl<H: HashFunction> Hasher<H> {
         for x in 0..domain_size {
             for hash_function_index in 0..NUMBER_HASH_FUNCTIONS {
                 let h = hashes[hash_function_index][x as usize];
-                hash_table[h as usize].push(x);
+                hash_table[H::hash_value_as_usize(h)].push(x);
             }
         }
         hash_table
@@ -162,7 +186,7 @@ impl<H: HashFunction> Hasher<H> {
         for (i, &x) in items.iter().enumerate() {
             for hash_function_index in 0..NUMBER_HASH_FUNCTIONS {
                 let h = hashes[hash_function_index][i as usize];
-                hash_table[h as usize].push(x);
+                hash_table[H::hash_value_as_usize(h)].push(x);
             }
         }
         hash_table
@@ -203,7 +227,7 @@ impl<H: HashFunction> Hasher<H> {
             let mut try_k = 0;
             while try_k < max_number_tries {
                 // try to (re)insert item with current index
-                let hash: usize = hashes[next_hash_function[index]][index].try_into().unwrap();
+                let hash: usize = H::hash_value_as_usize(hashes[next_hash_function[index]][index]);
                 // increment hash function counter for this item s.t. we use the next hash
                 // function next time
                 next_hash_function[index] = (next_hash_function[index] + 1) % NUMBER_HASH_FUNCTIONS;
@@ -239,15 +263,24 @@ mod tests {
         (0..n).map(|_| thread_rng().gen()).collect()
     }
 
-    fn create_hasher<H: HashFunction>(number_inputs: usize) -> Hasher<H> {
-        let params = Parameters::<H>::sample(number_inputs);
-        Hasher::<H>::new(params)
+    fn create_hasher<H: HashFunction<Value>, Value: HashFunctionValue>(
+        number_inputs: usize,
+    ) -> Hasher<H, Value>
+    where
+        <Value as TryInto<usize>>::Error: Debug,
+    {
+        let params = Parameters::<H, Value>::sample(number_inputs);
+        Hasher::<H, Value>::new(params)
     }
 
-    fn test_hash_cuckoo_with_param<H: HashFunction>(log_number_inputs: usize) {
+    fn test_hash_cuckoo_with_param<H: HashFunction<Value>, Value: HashFunctionValue>(
+        log_number_inputs: usize,
+    ) where
+        <Value as TryInto<usize>>::Error: Debug,
+    {
         let number_inputs = 1 << log_number_inputs;
         let inputs = gen_random_numbers(number_inputs);
-        let cuckoo = create_hasher::<H>(number_inputs);
+        let cuckoo = create_hasher::<H, Value>(number_inputs);
         let (cuckoo_table_items, cuckoo_table_indices) = cuckoo.cuckoo_hash_items(&inputs);
 
         let number_buckets = cuckoo.get_parameters().get_number_buckets();
@@ -258,13 +291,13 @@ mod tests {
         let num_unoccupied_entries = cuckoo_table_items
             .iter()
             .copied()
-            .filter(|&x| x == Hasher::<H>::UNOCCUPIED)
+            .filter(|&x| x == Hasher::<H, Value>::UNOCCUPIED)
             .count();
         assert_eq!(number_buckets - num_unoccupied_entries, number_inputs);
         // keep track of which items we have seen in the cuckoo table
         let mut found_inputs_in_table = vec![false; number_inputs];
         for bucket_i in 0..number_buckets {
-            if cuckoo_table_items[bucket_i] != Hasher::<H>::UNOCCUPIED {
+            if cuckoo_table_items[bucket_i] != Hasher::<H, Value>::UNOCCUPIED {
                 let index = cuckoo_table_indices[bucket_i];
                 // check that the right item is here
                 assert_eq!(cuckoo_table_items[bucket_i], inputs[index]);
@@ -278,9 +311,13 @@ mod tests {
         assert!(found_inputs_in_table.iter().all(|&x| x));
     }
 
-    fn test_hash_domain_into_buckets_with_param<H: HashFunction>(log_number_inputs: usize) {
+    fn test_hash_domain_into_buckets_with_param<H: HashFunction<Value>, Value: HashFunctionValue>(
+        log_number_inputs: usize,
+    ) where
+        <Value as TryInto<usize>>::Error: Debug,
+    {
         let number_inputs = 1 << log_number_inputs;
-        let cuckoo = create_hasher::<H>(number_inputs);
+        let cuckoo = create_hasher::<H, Value>(number_inputs);
         let domain_size = 1 << 10;
         let number_buckets = cuckoo.get_parameters().get_number_buckets();
 
@@ -302,7 +339,7 @@ mod tests {
             if hashes[0][element as usize] == hashes[1][element as usize]
                 && hashes[0][element as usize] == hashes[2][element as usize]
             {
-                let hash = hashes[0][element as usize] as usize;
+                let hash = H::hash_value_as_usize(hashes[0][element as usize]);
                 let idx_start = hash_table[hash]
                     .as_slice()
                     .partition_point(|x| x < &element);
@@ -315,7 +352,7 @@ mod tests {
                 // check that the element occurs three times
                 assert_eq!(idx_end - idx_start, 3);
             } else if hashes[0][element as usize] == hashes[1][element as usize] {
-                let hash = hashes[0][element as usize] as usize;
+                let hash = H::hash_value_as_usize(hashes[0][element as usize]);
                 let idx_start = hash_table[hash]
                     .as_slice()
                     .partition_point(|x| x < &element);
@@ -328,13 +365,13 @@ mod tests {
                 // check that the element occurs two times
                 assert_eq!(idx_end - idx_start, 2);
 
-                let hash_other = hashes[2][element as usize] as usize;
+                let hash_other = H::hash_value_as_usize(hashes[2][element as usize]);
                 assert!(hash_table[hash_other]
                     .as_slice()
                     .binary_search(&element)
                     .is_ok());
             } else if hashes[0][element as usize] == hashes[2][element as usize] {
-                let hash = hashes[0][element as usize] as usize;
+                let hash = H::hash_value_as_usize(hashes[0][element as usize]);
                 let idx_start = hash_table[hash]
                     .as_slice()
                     .partition_point(|x| x < &element);
@@ -347,13 +384,13 @@ mod tests {
                 // check that the element occurs two times
                 assert_eq!(idx_end - idx_start, 2);
 
-                let hash_other = hashes[1][element as usize] as usize;
+                let hash_other = H::hash_value_as_usize(hashes[1][element as usize]);
                 assert!(hash_table[hash_other]
                     .as_slice()
                     .binary_search(&element)
                     .is_ok());
             } else if hashes[1][element as usize] == hashes[2][element as usize] {
-                let hash = hashes[1][element as usize] as usize;
+                let hash = H::hash_value_as_usize(hashes[1][element as usize]);
                 let idx_start = hash_table[hash]
                     .as_slice()
                     .partition_point(|x| x < &element);
@@ -366,14 +403,14 @@ mod tests {
                 // check that the element occurs two times
                 assert_eq!(idx_end - idx_start, 2);
 
-                let hash_other = hashes[0][element as usize] as usize;
+                let hash_other = H::hash_value_as_usize(hashes[0][element as usize]);
                 assert!(hash_table[hash_other]
                     .as_slice()
                     .binary_search(&element)
                     .is_ok());
             } else {
                 for hash_j in 0..NUMBER_HASH_FUNCTIONS {
-                    let hash = hashes[hash_j][element as usize] as usize;
+                    let hash = H::hash_value_as_usize(hashes[hash_j][element as usize]);
                     assert!(hash_table[hash].as_slice().binary_search(&element).is_ok());
                 }
             }
@@ -383,9 +420,16 @@ mod tests {
         assert_eq!(num_items_in_hash_table as u64, 3 * domain_size);
     }
 
-    fn test_buckets_cuckoo_consistency_with_param<H: HashFunction>(number_inputs: usize) {
+    fn test_buckets_cuckoo_consistency_with_param<
+        H: HashFunction<Value>,
+        Value: HashFunctionValue,
+    >(
+        number_inputs: usize,
+    ) where
+        <Value as TryInto<usize>>::Error: Debug,
+    {
         let domain_size = 1 << 10;
-        let cuckoo = create_hasher::<H>(number_inputs);
+        let cuckoo = create_hasher::<H, Value>(number_inputs);
 
         // To generate random numbers in the domain, we generate the entire domain and do a random shuffle
 
@@ -401,7 +445,7 @@ mod tests {
         let number_buckets = cuckoo.get_parameters().get_number_buckets();
 
         for bucket_i in 0..number_buckets {
-            if cuckoo_table_items[bucket_i] != Hasher::<H>::UNOCCUPIED {
+            if cuckoo_table_items[bucket_i] != Hasher::<H, Value>::UNOCCUPIED {
                 assert!(hash_table[bucket_i]
                     .as_slice()
                     .binary_search(&cuckoo_table_items[bucket_i])
@@ -413,21 +457,21 @@ mod tests {
     #[test]
     fn test_hash_cuckoo() {
         for n in 5..10 {
-            test_hash_cuckoo_with_param::<AesHashFunction>(n);
+            test_hash_cuckoo_with_param::<AesHashFunction<u32>, u32>(n);
         }
     }
 
     #[test]
     fn test_hash_domain_into_buckets() {
         for n in 5..10 {
-            test_hash_domain_into_buckets_with_param::<AesHashFunction>(n);
+            test_hash_domain_into_buckets_with_param::<AesHashFunction<u32>, u32>(n);
         }
     }
 
     #[test]
     fn test_buckets_cuckoo_consistency() {
         for n in 5..10 {
-            test_buckets_cuckoo_consistency_with_param::<AesHashFunction>(n);
+            test_buckets_cuckoo_consistency_with_param::<AesHashFunction<u32>, u32>(n);
         }
     }
 }

+ 61 - 23
cuckoo/src/hash.rs

@@ -1,87 +1,121 @@
 use core::fmt::Debug;
 use core::ops::Range;
+use funty::Integral;
 use rand::{thread_rng, Rng, SeedableRng};
 use rand_chacha::ChaCha12Rng;
+use std::marker::PhantomData;
 use utils::fixed_key_aes::FixedKeyAes;
 
 pub trait HashFunctionParameters {}
+pub trait HashFunctionValue: Integral + TryInto<usize>
+where
+    <Self as TryInto<usize>>::Error: Debug,
+{
+}
+
+impl HashFunctionValue for u32 {}
+impl HashFunctionValue for u64 {}
 
-pub trait HashFunction {
+pub trait HashFunction<Value: HashFunctionValue>
+where
+    <Value as TryInto<usize>>::Error: Debug,
+{
     // type Domain;
-    // type Range;
-    type Description: Copy + Debug;
+
+    type Description: Copy + Debug + PartialEq + Eq;
 
     /// Sample a random hash function.
-    fn sample(range_size: u64) -> Self;
+    fn sample(range_size: usize) -> Self;
 
     /// Sample a hash function using a given seed.
-    fn from_seed(range_size: u64, seed: [u8; 32]) -> Self;
+    fn from_seed(range_size: usize, seed: [u8; 32]) -> Self;
 
     fn from_description(description: Self::Description) -> Self;
     fn to_description(&self) -> Self::Description;
 
     /// Return the number of elements n in the range [0, n).
-    fn get_range_size(&self) -> u64;
+    fn get_range_size(&self) -> usize;
 
     /// Hash a single item.
-    fn hash_single(&self, item: u64) -> u64;
+    fn hash_single(&self, item: u64) -> Value;
 
     /// Hash a slice of items.
-    fn hash_slice(&self, items: &[u64]) -> Vec<u64> {
+    fn hash_slice(&self, items: &[u64]) -> Vec<Value> {
         items.iter().map(|x| self.hash_single(*x)).collect()
     }
 
     /// Hash a range [a,b) of items.
-    fn hash_range(&self, items: Range<u64>) -> Vec<u64> {
+    fn hash_range(&self, items: Range<u64>) -> Vec<Value> {
         items.map(|x| self.hash_single(x)).collect()
     }
+
+    /// Convert a hash value into a usize. Useful when hashes are used as indices.
+    /// Might panic if Value is not convertible to usize.
+    #[inline(always)]
+    fn hash_value_as_usize(value: Value) -> usize
+    where
+        <Value as TryInto<usize>>::Error: Debug,
+    {
+        <Value as TryInto<usize>>::try_into(value).unwrap()
+    }
 }
 
 /// Fixed-key AES hashing using a circular correlation robust hash function
 #[derive(Clone, Debug)]
-pub struct AesHashFunction {
+pub struct AesHashFunction<Value> {
     description: AesHashFunctionDescription,
     /// FixedKeyAes object including expanded key.
     aes: FixedKeyAes,
+    _phantom: PhantomData<Value>,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub struct AesHashFunctionDescription {
     /// Size of the range.
-    range_size: u64,
+    range_size: usize,
     /// Raw AES key.
     key: [u8; 16],
 }
 
-impl HashFunction for AesHashFunction {
+impl<Value: HashFunctionValue> HashFunction<Value> for AesHashFunction<Value>
+where
+    <Value as TryInto<usize>>::Error: Debug,
+    <Value as TryFrom<u128>>::Error: Debug,
+{
     type Description = AesHashFunctionDescription;
 
-    fn get_range_size(&self) -> u64 {
+    fn get_range_size(&self) -> usize {
         self.description.range_size
     }
 
-    fn from_seed(range_size: u64, seed: [u8; 32]) -> Self {
+    fn from_seed(range_size: usize, seed: [u8; 32]) -> Self {
         let mut rng = ChaCha12Rng::from_seed(seed);
         let key = rng.gen();
         Self::from_description(AesHashFunctionDescription { range_size, key })
     }
 
-    fn sample(range_size: u64) -> Self {
+    fn sample(range_size: usize) -> Self {
         let key: [u8; 16] = thread_rng().gen();
         Self::from_description(AesHashFunctionDescription { range_size, key })
     }
 
     fn from_description(description: Self::Description) -> Self {
         let aes = FixedKeyAes::new(description.key);
-        Self { description, aes }
+        Self {
+            description,
+            aes,
+            _phantom: PhantomData,
+        }
     }
     fn to_description(&self) -> Self::Description {
         self.description
     }
 
-    fn hash_single(&self, item: u64) -> u64 {
+    fn hash_single(&self, item: u64) -> Value {
         let h = self.aes.hash_ccr(item as u128);
-        (h % self.description.range_size as u128) as u64
+        (h % self.description.range_size as u128)
+            .try_into()
+            .unwrap()
     }
 }
 
@@ -89,17 +123,20 @@ impl HashFunction for AesHashFunction {
 mod tests {
     use super::*;
 
-    fn test_hash_function<H: HashFunction>() {
+    fn test_hash_function<Value: HashFunctionValue, H: HashFunction<Value>>()
+    where
+        <Value as TryInto<usize>>::Error: Debug,
+    {
         let range_size = 42;
-        let h = AesHashFunction::sample(range_size);
-        let h2 = AesHashFunction::from_description(h.to_description());
+        let h = H::sample(range_size);
+        let h2 = H::from_description(h.to_description());
         assert_eq!(range_size, h.get_range_size());
         assert_eq!(h.to_description(), h2.to_description());
 
         for _ in 0..100 {
             let x: u64 = thread_rng().gen();
             let hx = h.hash_single(x);
-            assert!(hx < range_size);
+            assert!(<Value as TryInto<usize>>::try_into(hx).unwrap() < range_size);
             assert_eq!(hx, h2.hash_single(x));
         }
 
@@ -116,6 +153,7 @@ mod tests {
 
     #[test]
     fn test_aes_hash_function() {
-        test_hash_function::<AesHashFunction>();
+        test_hash_function::<u32, AesHashFunction<u32>>();
+        test_hash_function::<u64, AesHashFunction<u64>>();
     }
 }

+ 34 - 32
dpf/src/mpdpf.rs

@@ -12,7 +12,7 @@ use cuckoo::{
 
 pub trait MultiPointDpfKey: Clone + Debug {
     fn get_party_id(&self) -> usize;
-    fn get_log_domain_size(&self) -> u64;
+    fn get_log_domain_size(&self) -> u32;
     fn get_number_points(&self) -> usize;
 }
 
@@ -21,7 +21,7 @@ pub trait MultiPointDpf {
     type Value: Add<Output = Self::Value> + Copy + Debug + Eq + Zero;
 
     fn generate_keys(
-        log_domain_size: u64,
+        log_domain_size: u32,
         alphas: &[u64],
         betas: &[Self::Value],
     ) -> (Self::Key, Self::Key);
@@ -36,7 +36,7 @@ pub trait MultiPointDpf {
 #[derive(Clone, Debug)]
 pub struct DummyMpDpfKey<V: Copy + Debug> {
     party_id: usize,
-    log_domain_size: u64,
+    log_domain_size: u32,
     number_points: usize,
     alphas: Vec<u64>,
     betas: Vec<V>,
@@ -49,7 +49,7 @@ where
     fn get_party_id(&self) -> usize {
         self.party_id
     }
-    fn get_log_domain_size(&self) -> u64 {
+    fn get_log_domain_size(&self) -> u32 {
         self.log_domain_size
     }
     fn get_number_points(&self) -> usize {
@@ -71,7 +71,7 @@ where
     type Key = DummyMpDpfKey<V>;
     type Value = V;
 
-    fn generate_keys(log_domain_size: u64, alphas: &[u64], betas: &[V]) -> (Self::Key, Self::Key) {
+    fn generate_keys(log_domain_size: u32, alphas: &[u64], betas: &[V]) -> (Self::Key, Self::Key) {
         assert_eq!(
             alphas.len(),
             betas.len(),
@@ -116,19 +116,19 @@ where
 pub struct SmartMpDpfKey<SPDPF, H>
 where
     SPDPF: SinglePointDpf,
-    H: HashFunction,
+    H: HashFunction<u32>,
 {
     party_id: usize,
-    log_domain_size: u64,
+    log_domain_size: u32,
     number_points: usize,
     spdpf_keys: Vec<Option<SPDPF::Key>>,
-    cuckoo_parameters: CuckooParameters<H>,
+    cuckoo_parameters: CuckooParameters<H, u32>,
 }
 
 impl<SPDPF, H> Debug for SmartMpDpfKey<SPDPF, H>
 where
     SPDPF: SinglePointDpf,
-    H: HashFunction,
+    H: HashFunction<u32>,
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
         let (newline, indentation) = if f.alternate() {
@@ -173,7 +173,7 @@ where
 impl<SPDPF, H> Clone for SmartMpDpfKey<SPDPF, H>
 where
     SPDPF: SinglePointDpf,
-    H: HashFunction,
+    H: HashFunction<u32>,
 {
     fn clone(&self) -> Self {
         Self {
@@ -189,12 +189,12 @@ where
 impl<SPDPF, H> MultiPointDpfKey for SmartMpDpfKey<SPDPF, H>
 where
     SPDPF: SinglePointDpf,
-    H: HashFunction,
+    H: HashFunction<u32>,
 {
     fn get_party_id(&self) -> usize {
         self.party_id
     }
-    fn get_log_domain_size(&self) -> u64 {
+    fn get_log_domain_size(&self) -> u32 {
         self.log_domain_size
     }
     fn get_number_points(&self) -> usize {
@@ -206,7 +206,7 @@ pub struct SmartMpDpf<V, SPDPF, H>
 where
     V: Add<Output = V> + AddAssign + Copy + Debug + Eq + Zero,
     SPDPF: SinglePointDpf<Value = V>,
-    H: HashFunction,
+    H: HashFunction<u32>,
 {
     phantom_v: PhantomData<V>,
     phantom_s: PhantomData<SPDPF>,
@@ -217,23 +217,24 @@ impl<V, SPDPF, H> MultiPointDpf for SmartMpDpf<V, SPDPF, H>
 where
     V: Add<Output = V> + AddAssign + Copy + Debug + Eq + Zero,
     SPDPF: SinglePointDpf<Value = V>,
-    H: HashFunction,
+    H: HashFunction<u32>,
 {
     type Key = SmartMpDpfKey<SPDPF, H>;
     type Value = V;
 
     fn generate_keys(
-        log_domain_size: u64,
+        log_domain_size: u32,
         alphas: &[u64],
         betas: &[Self::Value],
     ) -> (Self::Key, Self::Key) {
+        assert!(log_domain_size < u32::BITS);
         assert_eq!(alphas.len(), betas.len());
         assert!(alphas.windows(2).all(|w| w[0] < w[1]));
         assert!(alphas.iter().all(|&alpha| alpha < (1 << log_domain_size)));
         let number_points = alphas.len();
 
-        let cuckoo_parameters = CuckooParameters::<H>::sample(number_points);
-        let hasher = CuckooHasher::<H>::new(cuckoo_parameters);
+        let cuckoo_parameters = CuckooParameters::<H, u32>::sample(number_points);
+        let hasher = CuckooHasher::<H, u32>::new(cuckoo_parameters);
         let (cuckoo_table_items, cuckoo_table_indices) = hasher.cuckoo_hash_items(alphas);
         let simple_htable = hasher.hash_domain_into_buckets(1 << log_domain_size);
 
@@ -267,13 +268,14 @@ where
 
             let sp_log_domain_size = (bucket_size as f64).log2().ceil() as u64;
 
-            let (alpha, beta) = if cuckoo_table_items[bucket_i] != CuckooHasher::<H>::UNOCCUPIED {
-                let alpha = pos(bucket_i, cuckoo_table_items[bucket_i]);
-                let beta = betas[cuckoo_table_indices[bucket_i]];
-                (alpha, beta)
-            } else {
-                (0, V::zero())
-            };
+            let (alpha, beta) =
+                if cuckoo_table_items[bucket_i] != CuckooHasher::<H, u32>::UNOCCUPIED {
+                    let alpha = pos(bucket_i, cuckoo_table_items[bucket_i]);
+                    let beta = betas[cuckoo_table_indices[bucket_i]];
+                    (alpha, beta)
+                } else {
+                    (0, V::zero())
+                };
             let (key_0, key_1) = SPDPF::generate_keys(sp_log_domain_size, alpha, beta);
             keys_0.push(Some(key_0));
             keys_1.push(Some(key_1));
@@ -301,7 +303,7 @@ where
         let domain_size = 1 << key.log_domain_size;
         assert!(index < domain_size);
 
-        let hasher = CuckooHasher::<H>::new(key.cuckoo_parameters);
+        let hasher = CuckooHasher::<H, u32>::new(key.cuckoo_parameters);
 
         let hashes = hasher.hash_items(&[index]);
         let simple_htable = hasher.hash_domain_into_buckets(domain_size);
@@ -314,7 +316,7 @@ where
             idx as u64
         };
         let mut output = {
-            let hash = hashes[0][0] as usize;
+            let hash = H::hash_value_as_usize(hashes[0][0]);
             assert!(key.spdpf_keys[hash].is_some());
             let sp_key = key.spdpf_keys[hash].as_ref().unwrap();
             assert_eq!(simple_htable[hash][pos(hash, index) as usize], index);
@@ -336,7 +338,7 @@ where
             if hash_bit_map[j - 1] == 0 {
                 continue;
             }
-            let hash = hashes[j][0] as usize;
+            let hash = H::hash_value_as_usize(hashes[j][0]);
             assert!(key.spdpf_keys[hash].is_some());
             let sp_key = key.spdpf_keys[hash].as_ref().unwrap();
             assert_eq!(simple_htable[hash][pos(hash, index) as usize], index);
@@ -349,7 +351,7 @@ where
     fn evaluate_domain(key: &Self::Key) -> Vec<Self::Value> {
         let domain_size = 1 << key.log_domain_size;
 
-        let hasher = CuckooHasher::<H>::new(key.cuckoo_parameters);
+        let hasher = CuckooHasher::<H, u32>::new(key.cuckoo_parameters);
         let hashes = hasher.hash_domain(domain_size);
         let simple_htable = hasher.hash_domain_into_buckets(domain_size);
 
@@ -365,7 +367,7 @@ where
 
         for index in 0..domain_size {
             outputs.push({
-                let hash = hashes[0][index as usize] as usize;
+                let hash = H::hash_value_as_usize(hashes[0][index as usize]);
                 assert!(key.spdpf_keys[hash].is_some());
                 let sp_key = key.spdpf_keys[hash].as_ref().unwrap();
                 assert_eq!(simple_htable[hash][pos(hash, index) as usize], index);
@@ -387,7 +389,7 @@ where
                 if hash_bit_map[j - 1] == 0 {
                     continue;
                 }
-                let hash = hashes[j][index as usize] as usize;
+                let hash = H::hash_value_as_usize(hashes[j][index as usize]);
                 assert!(key.spdpf_keys[hash].is_some());
                 let sp_key = key.spdpf_keys[hash].as_ref().unwrap();
                 assert_eq!(simple_htable[hash][pos(hash, index) as usize], index);
@@ -408,7 +410,7 @@ mod tests {
     use rand::{thread_rng, Rng};
     use std::num::Wrapping;
 
-    fn test_mpdpf_with_param<MPDPF: MultiPointDpf>(log_domain_size: u64, number_points: usize)
+    fn test_mpdpf_with_param<MPDPF: MultiPointDpf>(log_domain_size: u32, number_points: usize)
     where
         Standard: Distribution<MPDPF::Value>,
     {
@@ -447,7 +449,7 @@ mod tests {
         for log_domain_size in 5..10 {
             for log_number_points in 0..5 {
                 test_mpdpf_with_param::<DummyMpDpf<Value>>(log_domain_size, 1 << log_number_points);
-                test_mpdpf_with_param::<SmartMpDpf<Value, DummySpDpf<Value>, AesHashFunction>>(
+                test_mpdpf_with_param::<SmartMpDpf<Value, DummySpDpf<Value>, AesHashFunction<u32>>>(
                     log_domain_size,
                     1 << log_number_points,
                 );