light_hasher/
data_hasher.rs

1use crate::HasherError;
2
3pub trait DataHasher {
4    fn hash<H: crate::Hasher>(&self) -> Result<[u8; 32], HasherError>;
5}
6
7macro_rules! impl_data_hasher_for_array {
8    ($(
9         // For each array, specify the length and then a bracketed list of indices.
10         $len:literal => [$($index:tt),* $(,)?]
11    )*) => {
12        $(
13            impl<T: DataHasher + Default> DataHasher for [T; $len] {
14                fn hash<H: crate::Hasher>(&self) -> Result<[u8; 32], HasherError> {
15                    // We call T’s hash on each element and then pass the resulting list to H::hash.
16                    H::hashv(&[$( &self[$index].hash::<H>()?.as_slice() ),*])
17                }
18            }
19        )*
20    }
21}
22
23impl_data_hasher_for_array! {
24    1 => [0]
25}
26impl_data_hasher_for_array! {
27    2 => [0, 1]
28}
29impl_data_hasher_for_array! {
30    3 => [0, 1, 2]
31}
32impl_data_hasher_for_array! {
33    4 => [0, 1, 2, 3]
34}
35impl_data_hasher_for_array! {
36    5 => [0, 1, 2, 3, 4]
37}
38impl_data_hasher_for_array! {
39    6 => [0, 1, 2, 3, 4, 5]
40}
41impl_data_hasher_for_array! {
42    7 => [0, 1, 2, 3, 4, 5, 6]
43}
44impl_data_hasher_for_array! {
45    8 => [0, 1, 2, 3, 4, 5, 6, 7]
46}
47impl_data_hasher_for_array! {
48    9 => [0, 1, 2, 3, 4, 5, 6, 7, 8]
49}
50impl_data_hasher_for_array! {
51    10 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
52}
53impl_data_hasher_for_array! {
54    11 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
55}
56impl_data_hasher_for_array! {
57    12 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use crate::{Hasher, Poseidon};
64
65    // A simple test struct that implements DataHasher
66    #[derive(Default, Clone)]
67    struct TestHashable {
68        value: u8,
69    }
70
71    impl TestHashable {
72        fn new(value: u8) -> Self {
73            Self { value }
74        }
75    }
76
77    impl DataHasher for TestHashable {
78        fn hash<H: Hasher>(&self) -> Result<[u8; 32], HasherError> {
79            // Simple implementation that creates a predictable hash
80            let mut result = [0u8; 32];
81            result[31] = self.value;
82            Ok(result)
83        }
84    }
85
86    #[test]
87    fn test_data_hasher_array_1() {
88        let arr = [TestHashable::new(42)];
89        let hash_result = arr.hash::<Poseidon>().unwrap();
90
91        // The result should be the Poseidon hash of the single element's hash
92        let expected_input = [
93            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
94            0, 0, 42,
95        ];
96        let expected_hash = Poseidon::hash(&expected_input).unwrap();
97
98        assert_eq!(hash_result, expected_hash);
99    }
100
101    #[test]
102    fn test_data_hasher_array_2() {
103        let arr = [TestHashable::new(1), TestHashable::new(2)];
104        let hash_result = arr.hash::<Poseidon>().unwrap();
105
106        // Expected inputs are the hashes of each TestHashable
107        let hash1 = [0u8; 32];
108        let hash2 = [0u8; 32];
109
110        let mut hash1 = hash1;
111        hash1[31] = 1;
112
113        let mut hash2 = hash2;
114        hash2[31] = 2;
115
116        // The result should be the Poseidon hash of concatenated element hashes
117        let expected_hash = Poseidon::hashv(&[&hash1, &hash2]).unwrap();
118
119        assert_eq!(hash_result, expected_hash);
120    }
121
122    #[test]
123    fn test_data_hasher_array_multiple_sizes() {
124        // Test arrays of each implemented size
125        for size in 1..=12 {
126            let mut array = Vec::with_capacity(size);
127            for i in 0..size {
128                array.push(TestHashable::new(i as u8));
129            }
130
131            // Convert the Vec to an array of the appropriate size
132            let array_slice = array.as_slice();
133
134            // Create expected inputs (hashes of each TestHashable)
135            let mut expected_inputs = Vec::with_capacity(size);
136            for i in 0..size {
137                let mut hash = [0u8; 32];
138                hash[31] = i as u8;
139                expected_inputs.push(hash);
140            }
141
142            // Dynamically test each array size
143            match size {
144                1 => {
145                    let arr: [TestHashable; 1] = [array_slice[0].clone()];
146                    let hash_result = arr.hash::<Poseidon>().unwrap();
147
148                    let expected_slices: Vec<&[u8]> =
149                        expected_inputs.iter().map(|h| h.as_slice()).collect();
150                    let expected_hash = Poseidon::hashv(&expected_slices).unwrap();
151
152                    assert_eq!(hash_result, expected_hash);
153                }
154                2 => {
155                    let arr: [TestHashable; 2] = [array_slice[0].clone(), array_slice[1].clone()];
156                    let hash_result = arr.hash::<Poseidon>().unwrap();
157
158                    let expected_slices: Vec<&[u8]> =
159                        expected_inputs.iter().map(|h| h.as_slice()).collect();
160                    let expected_hash = Poseidon::hashv(&expected_slices).unwrap();
161
162                    assert_eq!(hash_result, expected_hash);
163                }
164                3 => {
165                    let arr: [TestHashable; 3] = [
166                        array_slice[0].clone(),
167                        array_slice[1].clone(),
168                        array_slice[2].clone(),
169                    ];
170                    let hash_result = arr.hash::<Poseidon>().unwrap();
171
172                    let expected_slices: Vec<&[u8]> =
173                        expected_inputs.iter().map(|h| h.as_slice()).collect();
174                    let expected_hash = Poseidon::hashv(&expected_slices).unwrap();
175
176                    assert_eq!(hash_result, expected_hash);
177                }
178                // We test one more size (4) to confirm the pattern works
179                4 => {
180                    let arr: [TestHashable; 4] = [
181                        array_slice[0].clone(),
182                        array_slice[1].clone(),
183                        array_slice[2].clone(),
184                        array_slice[3].clone(),
185                    ];
186                    let hash_result = arr.hash::<Poseidon>().unwrap();
187
188                    let expected_slices: Vec<&[u8]> =
189                        expected_inputs.iter().map(|h| h.as_slice()).collect();
190                    let expected_hash = Poseidon::hashv(&expected_slices).unwrap();
191
192                    assert_eq!(hash_result, expected_hash);
193                }
194                _ => {
195                    // For sizes 5-12, we've verified the pattern with tests for sizes 1-4
196                    // We could add more tests here if needed
197                }
198            }
199        }
200    }
201}