light_hasher/
to_byte_array.rs

1use crate::{Hasher, HasherError, Poseidon};
2
3pub trait ToByteArray {
4    const NUM_FIELDS: usize;
5    const IS_PRIMITIVE: bool = false;
6    fn to_byte_array(&self) -> Result<[u8; 32], HasherError>;
7}
8
9macro_rules! impl_to_byte_array_for_integer_type {
10    ($int_ty:ty) => {
11        impl ToByteArray for $int_ty {
12            const IS_PRIMITIVE: bool = true;
13            const NUM_FIELDS: usize = 1;
14
15            /// Big endian representation of $int_ty.
16            fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
17                let bytes = self.to_be_bytes();
18                let mut result = [0; 32];
19                result[32 - std::mem::size_of::<$int_ty>()..].copy_from_slice(&bytes);
20                Ok(result)
21            }
22        }
23    };
24}
25
26impl<T: ToByteArray> ToByteArray for Option<T> {
27    const NUM_FIELDS: usize = 1;
28
29    /// Some(PrimitiveType) prefixed with 1 byte
30    /// Some(T) -> Poseidon::hash(T::to_byte_array())
31    /// None -> [0u8;32]
32    fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
33        if let Some(value) = &self {
34            let byte_array = if T::IS_PRIMITIVE {
35                let mut byte_array = value.to_byte_array()?;
36                // Prefix with 1 to indicate Some().
37                byte_array[32 - std::mem::size_of::<T>() - 1] = 1;
38                byte_array
39            } else {
40                let byte_array = value.to_byte_array()?;
41                Poseidon::hash(byte_array.as_slice())?
42            };
43            Ok(byte_array)
44        } else {
45            Ok([0; 32])
46        }
47    }
48}
49
50impl ToByteArray for bool {
51    const NUM_FIELDS: usize = 1;
52    const IS_PRIMITIVE: bool = true;
53
54    /// Big endian representation of bool.
55    fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
56        let mut bytes = [0u8; 32];
57        bytes[31] = *self as u8;
58        Ok(bytes)
59    }
60}
61
62impl_to_byte_array_for_integer_type!(i8);
63impl_to_byte_array_for_integer_type!(u8);
64impl_to_byte_array_for_integer_type!(i16);
65impl_to_byte_array_for_integer_type!(u16);
66impl_to_byte_array_for_integer_type!(i32);
67impl_to_byte_array_for_integer_type!(u32);
68impl_to_byte_array_for_integer_type!(i64);
69impl_to_byte_array_for_integer_type!(u64);
70impl_to_byte_array_for_integer_type!(i128);
71impl_to_byte_array_for_integer_type!(u128);
72
73// Macro for implementing ToByteArray for zero-copy types
74#[cfg(feature = "zero-copy")]
75macro_rules! impl_to_byte_array_for_zero_copy_type {
76    ($zero_copy_type:ty, $primitive_type:ty) => {
77        impl ToByteArray for $zero_copy_type {
78            const IS_PRIMITIVE: bool = true;
79            const NUM_FIELDS: usize = 1;
80
81            fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
82                let value: $primitive_type = (*self).into();
83                value.to_byte_array()
84            }
85        }
86    };
87}
88
89// ToByteArray implementations for zero-copy types
90#[cfg(feature = "zero-copy")]
91impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::U16, u16);
92#[cfg(feature = "zero-copy")]
93impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::U32, u32);
94#[cfg(feature = "zero-copy")]
95impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::U64, u64);
96#[cfg(feature = "zero-copy")]
97impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::I16, i16);
98#[cfg(feature = "zero-copy")]
99impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::I32, i32);
100#[cfg(feature = "zero-copy")]
101impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::I64, i64);
102
103/// Example usage:
104/// impl_to_byte_array_for_array! {
105///     MyCustomType,
106///     1 => [0],
107///     2 => [0, 1]
108/// }
109#[macro_export]
110macro_rules! impl_to_byte_array_for_array {
111    // First specify the type T, then for each array, specify the length and indices
112    ($t:ty, $( $len:literal => [$($index:tt),*] );* $(;)?) => {
113        $(
114            impl ToByteArray for [$t; $len] {
115                const NUM_FIELDS: usize = $len;
116                const IS_PRIMITIVE: bool = false;
117
118                fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
119                    let arrays = [$(self[$index].to_byte_array()?),*];
120                    let slices = [$(arrays[$index].as_slice()),*];
121                    Poseidon::hashv(&slices)
122                }
123            }
124        )*
125    }
126}
127
128// Implementation for [u8; N] arrays with N <= 31
129macro_rules! impl_to_byte_array_for_u8_array {
130    ($size:expr) => {
131        impl ToByteArray for [u8; $size] {
132            const NUM_FIELDS: usize = 1;
133
134            fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
135                let mut result = [0u8; 32];
136                result[32 - $size..].copy_from_slice(self.as_slice());
137                Ok(result)
138            }
139        }
140    };
141}
142
143// Implement for common array sizes until 31 so that it is less than field size.
144impl_to_byte_array_for_u8_array!(1);
145impl_to_byte_array_for_u8_array!(2);
146impl_to_byte_array_for_u8_array!(4);
147impl_to_byte_array_for_u8_array!(5);
148impl_to_byte_array_for_u8_array!(6);
149impl_to_byte_array_for_u8_array!(7);
150impl_to_byte_array_for_u8_array!(8);
151impl_to_byte_array_for_u8_array!(9);
152impl_to_byte_array_for_u8_array!(10);
153impl_to_byte_array_for_u8_array!(11);
154impl_to_byte_array_for_u8_array!(12);
155impl_to_byte_array_for_u8_array!(13);
156impl_to_byte_array_for_u8_array!(14);
157impl_to_byte_array_for_u8_array!(15);
158impl_to_byte_array_for_u8_array!(16);
159impl_to_byte_array_for_u8_array!(17);
160impl_to_byte_array_for_u8_array!(18);
161impl_to_byte_array_for_u8_array!(19);
162impl_to_byte_array_for_u8_array!(20);
163impl_to_byte_array_for_u8_array!(21);
164impl_to_byte_array_for_u8_array!(22);
165impl_to_byte_array_for_u8_array!(23);
166impl_to_byte_array_for_u8_array!(24);
167impl_to_byte_array_for_u8_array!(25);
168impl_to_byte_array_for_u8_array!(26);
169impl_to_byte_array_for_u8_array!(27);
170impl_to_byte_array_for_u8_array!(28);
171impl_to_byte_array_for_u8_array!(29);
172impl_to_byte_array_for_u8_array!(30);
173impl_to_byte_array_for_u8_array!(31);
174
175impl ToByteArray for String {
176    const NUM_FIELDS: usize = 1;
177
178    /// Max allowed String length is 31 bytes.
179    /// For longer strings hash to field size or provide a custom implementation.
180    fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
181        let bytes = self.as_bytes();
182        let mut result = [0u8; 32];
183        let byte_len = bytes.len();
184        if byte_len > 31 {
185            return Err(HasherError::InvalidInputLength(31, bytes.len()));
186        }
187        result[32 - byte_len..].copy_from_slice(bytes);
188        Ok(result)
189    }
190}
191
192#[cfg(test)]
193mod test {
194    use super::*;
195
196    #[test]
197    fn test_to_byte_array_integers() {
198        // i8 tests
199        let i8_min_result = i8::MIN.to_byte_array().unwrap();
200        let mut expected_i8_min = [0u8; 32];
201        expected_i8_min[31] = 128; // i8::MIN.to_be_bytes() = [128]
202        assert_eq!(i8_min_result, expected_i8_min);
203
204        let i8_max_result = i8::MAX.to_byte_array().unwrap();
205        let mut expected_i8_max = [0u8; 32];
206        expected_i8_max[31] = 127; // i8::MAX.to_be_bytes() = [127]
207        assert_eq!(i8_max_result, expected_i8_max);
208
209        // u8 tests
210        let u8_min_result = u8::MIN.to_byte_array().unwrap();
211        let mut expected_u8_min = [0u8; 32];
212        expected_u8_min[31] = 0; // u8::MIN.to_be_bytes() = [0]
213        assert_eq!(u8_min_result, expected_u8_min);
214
215        let u8_max_result = u8::MAX.to_byte_array().unwrap();
216        let mut expected_u8_max = [0u8; 32];
217        expected_u8_max[31] = 255; // u8::MAX.to_be_bytes() = [255]
218        assert_eq!(u8_max_result, expected_u8_max);
219
220        // i16 tests
221        let i16_min_result = i16::MIN.to_byte_array().unwrap();
222        let mut expected_i16_min = [0u8; 32];
223        expected_i16_min[30..32].copy_from_slice(&i16::MIN.to_be_bytes()); // [128, 0]
224        assert_eq!(i16_min_result, expected_i16_min);
225
226        let i16_max_result = i16::MAX.to_byte_array().unwrap();
227        let mut expected_i16_max = [0u8; 32];
228        expected_i16_max[30..32].copy_from_slice(&i16::MAX.to_be_bytes()); // [127, 255]
229        assert_eq!(i16_max_result, expected_i16_max);
230
231        // u16 tests
232        let u16_min_result = u16::MIN.to_byte_array().unwrap();
233        let mut expected_u16_min = [0u8; 32];
234        expected_u16_min[30..32].copy_from_slice(&u16::MIN.to_be_bytes()); // [0, 0]
235        assert_eq!(u16_min_result, expected_u16_min);
236
237        let u16_max_result = u16::MAX.to_byte_array().unwrap();
238        let mut expected_u16_max = [0u8; 32];
239        expected_u16_max[30..32].copy_from_slice(&u16::MAX.to_be_bytes()); // [255, 255]
240        assert_eq!(u16_max_result, expected_u16_max);
241
242        // i32 tests
243        let i32_min_result = i32::MIN.to_byte_array().unwrap();
244        let mut expected_i32_min = [0u8; 32];
245        expected_i32_min[28..32].copy_from_slice(&i32::MIN.to_be_bytes()); // [128, 0, 0, 0]
246        assert_eq!(i32_min_result, expected_i32_min);
247
248        let i32_max_result = i32::MAX.to_byte_array().unwrap();
249        let mut expected_i32_max = [0u8; 32];
250        expected_i32_max[28..32].copy_from_slice(&i32::MAX.to_be_bytes()); // [127, 255, 255, 255]
251        assert_eq!(i32_max_result, expected_i32_max);
252
253        // u32 tests
254        let u32_min_result = u32::MIN.to_byte_array().unwrap();
255        let mut expected_u32_min = [0u8; 32];
256        expected_u32_min[28..32].copy_from_slice(&u32::MIN.to_be_bytes()); // [0, 0, 0, 0]
257        assert_eq!(u32_min_result, expected_u32_min);
258
259        let u32_max_result = u32::MAX.to_byte_array().unwrap();
260        let mut expected_u32_max = [0u8; 32];
261        expected_u32_max[28..32].copy_from_slice(&u32::MAX.to_be_bytes()); // [255, 255, 255, 255]
262        assert_eq!(u32_max_result, expected_u32_max);
263
264        // i64 tests
265        let i64_min_result = i64::MIN.to_byte_array().unwrap();
266        let mut expected_i64_min = [0u8; 32];
267        expected_i64_min[24..32].copy_from_slice(&i64::MIN.to_be_bytes()); // [128, 0, 0, 0, 0, 0, 0, 0]
268        assert_eq!(i64_min_result, expected_i64_min);
269
270        let i64_max_result = i64::MAX.to_byte_array().unwrap();
271        let mut expected_i64_max = [0u8; 32];
272        expected_i64_max[24..32].copy_from_slice(&i64::MAX.to_be_bytes()); // [127, 255, 255, 255, 255, 255, 255, 255]
273        assert_eq!(i64_max_result, expected_i64_max);
274
275        // u64 tests
276        let u64_min_result = u64::MIN.to_byte_array().unwrap();
277        let mut expected_u64_min = [0u8; 32];
278        expected_u64_min[24..32].copy_from_slice(&u64::MIN.to_be_bytes()); // [0, 0, 0, 0, 0, 0, 0, 0]
279        assert_eq!(u64_min_result, expected_u64_min);
280
281        let u64_max_result = u64::MAX.to_byte_array().unwrap();
282        let mut expected_u64_max = [0u8; 32];
283        expected_u64_max[24..32].copy_from_slice(&u64::MAX.to_be_bytes()); // [255, 255, 255, 255, 255, 255, 255, 255]
284        assert_eq!(u64_max_result, expected_u64_max);
285
286        // i128 tests
287        let i128_min_result = i128::MIN.to_byte_array().unwrap();
288        let mut expected_i128_min = [0u8; 32];
289        expected_i128_min[16..32].copy_from_slice(&i128::MIN.to_be_bytes()); // [128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
290        assert_eq!(i128_min_result, expected_i128_min);
291
292        let i128_max_result = i128::MAX.to_byte_array().unwrap();
293        let mut expected_i128_max = [0u8; 32];
294        expected_i128_max[16..32].copy_from_slice(&i128::MAX.to_be_bytes()); // [127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]
295        assert_eq!(i128_max_result, expected_i128_max);
296
297        // u128 tests
298        let u128_min_result = u128::MIN.to_byte_array().unwrap();
299        let mut expected_u128_min = [0u8; 32];
300        expected_u128_min[16..32].copy_from_slice(&u128::MIN.to_be_bytes()); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
301        assert_eq!(u128_min_result, expected_u128_min);
302
303        let u128_max_result = u128::MAX.to_byte_array().unwrap();
304        let mut expected_u128_max = [0u8; 32];
305        expected_u128_max[16..32].copy_from_slice(&u128::MAX.to_be_bytes()); // [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]
306        assert_eq!(u128_max_result, expected_u128_max);
307    }
308
309    #[test]
310    fn test_to_byte_array_primitives() {
311        // Test bool::to_byte_array
312        let bool_false_result = false.to_byte_array().unwrap();
313        let mut expected_bool_false = [0u8; 32];
314        expected_bool_false[31] = 0;
315        assert_eq!(bool_false_result, expected_bool_false);
316
317        let bool_true_result = true.to_byte_array().unwrap();
318        let mut expected_bool_true = [0u8; 32];
319        expected_bool_true[31] = 1;
320        assert_eq!(bool_true_result, expected_bool_true);
321    }
322
323    #[test]
324    fn test_to_byte_array_option() {
325        // Very important property - `None` and `Some(0)` always have to be
326        // different and should produce different hashes!
327
328        // Test Option<u8>::to_byte_array
329        let u8_none: Option<u8> = None;
330        let u8_none_result = u8_none.to_byte_array().unwrap();
331        assert_eq!(u8_none_result, [0u8; 32]);
332
333        let u8_some_zero: Option<u8> = Some(0);
334        let u8_some_zero_result = u8_some_zero.to_byte_array().unwrap();
335        let mut expected_u8_some_zero = [0u8; 32];
336        expected_u8_some_zero[32 - std::mem::size_of::<u8>() - 1] = 1; // Mark as Some
337        assert_eq!(u8_some_zero_result, expected_u8_some_zero);
338
339        // Test Option<u16>::to_byte_array
340        let u16_none: Option<u16> = None;
341        let u16_none_result = u16_none.to_byte_array().unwrap();
342        assert_eq!(u16_none_result, [0u8; 32]);
343
344        let u16_some_zero: Option<u16> = Some(0);
345        let u16_some_zero_result = u16_some_zero.to_byte_array().unwrap();
346        let mut expected_u16_some_zero = [0u8; 32];
347        expected_u16_some_zero[32 - std::mem::size_of::<u16>() - 1] = 1; // Mark as Some
348        assert_eq!(u16_some_zero_result, expected_u16_some_zero);
349
350        // Test Option<u32>::to_byte_array
351        let u32_none: Option<u32> = None;
352        let u32_none_result = u32_none.to_byte_array().unwrap();
353        assert_eq!(u32_none_result, [0u8; 32]);
354
355        let u32_some_zero: Option<u32> = Some(0);
356        let u32_some_zero_result = u32_some_zero.to_byte_array().unwrap();
357        let mut expected_u32_some_zero = [0u8; 32];
358        expected_u32_some_zero[32 - std::mem::size_of::<u32>() - 1] = 1; // Mark as Some
359        assert_eq!(u32_some_zero_result, expected_u32_some_zero);
360
361        // Test Option<u64>::to_byte_array
362        let u64_none: Option<u64> = None;
363        let u64_none_result = u64_none.to_byte_array().unwrap();
364        assert_eq!(u64_none_result, [0u8; 32]);
365
366        let u64_some_zero: Option<u64> = Some(0);
367        let u64_some_zero_result = u64_some_zero.to_byte_array().unwrap();
368        let mut expected_u64_some_zero = [0u8; 32];
369        expected_u64_some_zero[32 - std::mem::size_of::<u64>() - 1] = 1; // Mark as Some
370        assert_eq!(u64_some_zero_result, expected_u64_some_zero);
371
372        // Test Option<u128>::to_byte_array
373        let u128_none: Option<u128> = None;
374        let u128_none_result = u128_none.to_byte_array().unwrap();
375        assert_eq!(u128_none_result, [0u8; 32]);
376
377        let u128_some_zero: Option<u128> = Some(0);
378        let u128_some_zero_result = u128_some_zero.to_byte_array().unwrap();
379        let mut expected_u128_some_zero = [0u8; 32];
380        expected_u128_some_zero[32 - std::mem::size_of::<u128>() - 1] = 1; // Mark as Some
381        assert_eq!(u128_some_zero_result, expected_u128_some_zero);
382    }
383
384    #[test]
385    fn test_to_byte_array_u8_arrays() {
386        // Test with single element array
387        let single_element_arr: [u8; 1] = [255];
388        let result = single_element_arr.to_byte_array().unwrap();
389        let mut expected = [0u8; 32];
390        expected[31] = 255;
391        assert_eq!(result, expected);
392
393        // Test with multi-element array
394        let multi_element_arr: [u8; 4] = [1, 2, 3, 4];
395        let result = multi_element_arr.to_byte_array().unwrap();
396        let mut expected = [0u8; 32];
397        expected[32 - 4..].copy_from_slice(&multi_element_arr);
398        assert_eq!(result, expected);
399
400        // Test with full 32-byte array
401        let full_arr: [u8; 31] = [
402            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
403            25, 26, 27, 28, 29, 30, 31,
404        ];
405        let result = full_arr.to_byte_array().unwrap();
406        assert_eq!(result[0], 0);
407        assert_eq!(&result[1..], full_arr.as_slice());
408    }
409
410    #[test]
411    fn test_to_byte_array_string() {
412        // Test with empty string
413        let empty_string = "".to_string();
414        let result = empty_string.to_byte_array().unwrap();
415        let expected = [0u8; 32];
416        assert_eq!(result, expected);
417
418        // Test with short string
419        let short_string = "foobar".to_string();
420        let result = short_string.to_byte_array().unwrap();
421        let mut expected = [0u8; 32];
422        expected[32 - 6..].copy_from_slice(b"foobar");
423        assert_eq!(result, expected);
424
425        // Test with longer string that gets truncated
426        let long_string =
427            "this is a string that is longer than 32 bytes and will be fail".to_string();
428        let byte_len = long_string.len();
429        let result = long_string.to_byte_array();
430        assert_eq!(result, Err(HasherError::InvalidInputLength(31, byte_len)));
431    }
432}