light_hash_set/
lib.rs

1use light_utils::{bigint::bigint_to_be_bytes_array, UtilsError};
2use num_bigint::{BigUint, ToBigUint};
3use num_traits::{FromBytes, ToPrimitive};
4use std::{
5    alloc::{self, handle_alloc_error, Layout},
6    cmp::Ordering,
7    marker::Send,
8    mem,
9    ptr::NonNull,
10};
11use thiserror::Error;
12
13pub mod zero_copy;
14
15pub const ITERATIONS: usize = 20;
16
17#[derive(Debug, Error, PartialEq)]
18pub enum HashSetError {
19    #[error("The hash set is full, cannot add any new elements")]
20    Full,
21    #[error("The provided element is already in the hash set")]
22    ElementAlreadyExists,
23    #[error("The provided element doesn't exist in the hash set")]
24    ElementDoesNotExist,
25    #[error("Could not convert the index from/to usize")]
26    UsizeConv,
27    #[error("Integer overflow")]
28    IntegerOverflow,
29    #[error("Invalid buffer size, expected {0}, got {1}")]
30    BufferSize(usize, usize),
31    #[error("Utils: big integer conversion error")]
32    Utils(#[from] UtilsError),
33}
34
35#[cfg(feature = "solana")]
36impl From<HashSetError> for u32 {
37    fn from(e: HashSetError) -> u32 {
38        match e {
39            HashSetError::Full => 9001,
40            HashSetError::ElementAlreadyExists => 9002,
41            HashSetError::ElementDoesNotExist => 9003,
42            HashSetError::UsizeConv => 9004,
43            HashSetError::IntegerOverflow => 9005,
44            HashSetError::BufferSize(_, _) => 9006,
45            HashSetError::Utils(e) => e.into(),
46        }
47    }
48}
49
50#[cfg(feature = "solana")]
51impl From<HashSetError> for solana_program::program_error::ProgramError {
52    fn from(e: HashSetError) -> Self {
53        solana_program::program_error::ProgramError::Custom(e.into())
54    }
55}
56
57#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
58pub struct HashSetCell {
59    pub value: [u8; 32],
60    pub sequence_number: Option<usize>,
61}
62
63unsafe impl Send for HashSet {}
64
65impl HashSetCell {
66    /// Returns the value as a byte array.
67    pub fn value_bytes(&self) -> [u8; 32] {
68        self.value
69    }
70
71    /// Returns the value as a big number.
72    pub fn value_biguint(&self) -> BigUint {
73        BigUint::from_bytes_be(self.value.as_slice())
74    }
75
76    /// Returns the associated sequence number.
77    pub fn sequence_number(&self) -> Option<usize> {
78        self.sequence_number
79    }
80
81    /// Checks whether the value is marked with a sequence number.
82    pub fn is_marked(&self) -> bool {
83        self.sequence_number.is_some()
84    }
85
86    /// Checks whether the value is valid according to the provided
87    /// `current_sequence_number` (which usually should be a sequence number
88    /// associated with the Merkle tree).
89    ///
90    /// The value is valid if:
91    ///
92    /// * It was not annotated with sequence number.
93    /// * Its sequence number is lower than the provided `sequence_number`.
94    ///
95    /// The value is invalid if it's lower or equal to the provided
96    /// `sequence_number`.
97    pub fn is_valid(&self, current_sequence_number: usize) -> bool {
98        match self.sequence_number {
99            Some(sequence_number) => match sequence_number.cmp(&current_sequence_number) {
100                Ordering::Less | Ordering::Equal => false,
101                Ordering::Greater => true,
102            },
103            None => true,
104        }
105    }
106}
107
108#[derive(Debug)]
109pub struct HashSet {
110    /// Capacity of the buckets.
111    capacity: usize,
112    /// Difference of sequence numbers, after which the given element can be
113    /// replaced by an another one (with a sequence number higher than the
114    /// threshold).
115    pub sequence_threshold: usize,
116
117    /// An array of buckets. It has a size equal to the expected number of
118    /// elements.
119    buckets: NonNull<Option<HashSetCell>>,
120}
121
122unsafe impl Send for HashSetCell {}
123
124impl HashSet {
125    /// Size of the struct **without** dynamically sized fields.
126    pub fn non_dyn_fields_size() -> usize {
127        // capacity
128        mem::size_of::<usize>()
129        // sequence_threshold
130        + mem::size_of::<usize>()
131    }
132
133    /// Size which needs to be allocated on Solana account to fit the hash set.
134    pub fn size_in_account(capacity_values: usize) -> usize {
135        let dyn_fields_size = Self::non_dyn_fields_size();
136
137        let buckets_size_unaligned = mem::size_of::<Option<HashSetCell>>() * capacity_values;
138        // Make sure that alignment of `values` matches the alignment of `usize`.
139        let buckets_size = buckets_size_unaligned + mem::align_of::<usize>()
140            - (buckets_size_unaligned % mem::align_of::<usize>());
141
142        dyn_fields_size + buckets_size
143    }
144
145    // Create a new hash set with the given capacity
146    pub fn new(capacity_values: usize, sequence_threshold: usize) -> Result<Self, HashSetError> {
147        // SAFETY: It's just a regular allocation.
148        let layout = Layout::array::<Option<HashSetCell>>(capacity_values).unwrap();
149        let values_ptr = unsafe { alloc::alloc(layout) as *mut Option<HashSetCell> };
150        if values_ptr.is_null() {
151            handle_alloc_error(layout);
152        }
153        let values = NonNull::new(values_ptr).unwrap();
154        for i in 0..capacity_values {
155            unsafe {
156                std::ptr::write(values_ptr.add(i), None);
157            }
158        }
159
160        Ok(HashSet {
161            sequence_threshold,
162            capacity: capacity_values,
163            buckets: values,
164        })
165    }
166
167    /// Creates a copy of `HashSet` from the given byte slice.
168    ///
169    /// # Purpose
170    ///
171    /// This method is meant to be used mostly in the SDK code, to convert
172    /// fetched Solana accounts to actual hash sets. Creating a copy is the
173    /// safest way of conversion in async Rust.
174    ///
175    /// # Safety
176    ///
177    /// This is highly unsafe. Ensuring the alignment and that the slice
178    /// provides actual actual data of the hash set is the caller's
179    /// responsibility.
180    pub unsafe fn from_bytes_copy(bytes: &mut [u8]) -> Result<Self, HashSetError> {
181        if bytes.len() < Self::non_dyn_fields_size() {
182            return Err(HashSetError::BufferSize(
183                Self::non_dyn_fields_size(),
184                bytes.len(),
185            ));
186        }
187
188        let capacity = usize::from_le_bytes(bytes[0..8].try_into().unwrap());
189        let sequence_threshold = usize::from_le_bytes(bytes[8..16].try_into().unwrap());
190        let expected_size = Self::size_in_account(capacity);
191        if bytes.len() != expected_size {
192            return Err(HashSetError::BufferSize(expected_size, bytes.len()));
193        }
194
195        let buckets_layout = Layout::array::<Option<HashSetCell>>(capacity).unwrap();
196        // SAFETY: `I` is always a signed integer. Creating a layout for an
197        // array of integers of any size won't cause any panic.
198        let buckets_dst_ptr = unsafe { alloc::alloc(buckets_layout) as *mut Option<HashSetCell> };
199        if buckets_dst_ptr.is_null() {
200            handle_alloc_error(buckets_layout);
201        }
202        let buckets = NonNull::new(buckets_dst_ptr).unwrap();
203        for i in 0..capacity {
204            std::ptr::write(buckets_dst_ptr.add(i), None);
205        }
206
207        let offset = Self::non_dyn_fields_size() + mem::size_of::<usize>();
208        let buckets_src_ptr = bytes.as_ptr().add(offset) as *const Option<HashSetCell>;
209        std::ptr::copy(buckets_src_ptr, buckets_dst_ptr, capacity);
210
211        Ok(Self {
212            capacity,
213            sequence_threshold,
214            buckets,
215        })
216    }
217
218    fn probe_index(&self, value: &BigUint, iteration: usize) -> usize {
219        // Increase stepsize over the capacity of the hash set.
220        let iteration = iteration + self.capacity / 10;
221        let probe_index = (value
222            + iteration.to_biguint().unwrap() * iteration.to_biguint().unwrap())
223            % self.capacity.to_biguint().unwrap();
224        probe_index.to_usize().unwrap()
225    }
226
227    /// Returns a reference to a bucket under the given `index`. Does not check
228    /// the validity.
229    pub fn get_bucket(&self, index: usize) -> Option<&Option<HashSetCell>> {
230        if index >= self.capacity {
231            return None;
232        }
233        let bucket = unsafe { &*self.buckets.as_ptr().add(index) };
234        Some(bucket)
235    }
236
237    /// Returns a mutable reference to a bucket under the given `index`. Does
238    /// not check the validity.
239    pub fn get_bucket_mut(&mut self, index: usize) -> Option<&mut Option<HashSetCell>> {
240        if index >= self.capacity {
241            return None;
242        }
243        let bucket = unsafe { &mut *self.buckets.as_ptr().add(index) };
244        Some(bucket)
245    }
246
247    /// Returns a reference to an unmarked bucket under the given index. If the
248    /// bucket is marked, returns `None`.
249    pub fn get_unmarked_bucket(&self, index: usize) -> Option<&Option<HashSetCell>> {
250        let bucket = self.get_bucket(index);
251        let is_unmarked = match bucket {
252            Some(Some(bucket)) => !bucket.is_marked(),
253            Some(None) => false,
254            None => false,
255        };
256        if is_unmarked {
257            bucket
258        } else {
259            None
260        }
261    }
262
263    pub fn get_capacity(&self) -> usize {
264        self.capacity
265    }
266
267    fn insert_into_occupied_cell(
268        &mut self,
269        value_index: usize,
270        value: &BigUint,
271        current_sequence_number: usize,
272    ) -> Result<bool, HashSetError> {
273        // PANICS: We trust the bounds of `value_index` here.
274        let bucket = self.get_bucket_mut(value_index).unwrap();
275
276        match bucket {
277            // The cell in the value array is already taken.
278            Some(bucket) => {
279                // We can overwrite that cell only if the element
280                // is expired - when the difference between its
281                // sequence number and provided sequence number is
282                // greater than the threshold.
283                if let Some(element_sequence_number) = bucket.sequence_number {
284                    if current_sequence_number >= element_sequence_number {
285                        *bucket = HashSetCell {
286                            value: bigint_to_be_bytes_array(value)?,
287                            sequence_number: None,
288                        };
289                        return Ok(true);
290                    }
291                }
292                // Otherwise, we need to prevent having multiple valid
293                // elements with the same value.
294                if &BigUint::from_be_bytes(bucket.value.as_slice()) == value {
295                    return Err(HashSetError::ElementAlreadyExists);
296                }
297            }
298            // Panics: If there is a hash set cell pointing to a `None` value,
299            // it means we really screwed up in the implementation...
300            // That should never happen.
301            None => unreachable!(),
302        }
303        Ok(false)
304    }
305
306    /// Inserts a value into the hash set, with `self.capacity_values` attempts.
307    ///
308    /// Every attempt uses quadratic probing to find an empty cell or a cell
309    /// which can be overwritten.
310    ///
311    /// `current sequence_number` is used to check whether existing values can
312    /// be overwritten.
313    pub fn insert(
314        &mut self,
315        value: &BigUint,
316        current_sequence_number: usize,
317    ) -> Result<usize, HashSetError> {
318        let index_bucket = self.find_element_iter(value, current_sequence_number, 0, ITERATIONS)?;
319        let (index, is_new) = match index_bucket {
320            Some(index) => index,
321            None => {
322                return Err(HashSetError::Full);
323            }
324        };
325
326        match is_new {
327            // The visited hash set cell points to a value in the array.
328            false => {
329                if self.insert_into_occupied_cell(index, value, current_sequence_number)? {
330                    return Ok(index);
331                }
332            }
333            true => {
334                // PANICS: We trust the bounds of `index`.
335                let bucket = self.get_bucket_mut(index).unwrap();
336
337                *bucket = Some(HashSetCell {
338                    value: bigint_to_be_bytes_array(value)?,
339                    sequence_number: None,
340                });
341                return Ok(index);
342            }
343        }
344        Err(HashSetError::Full)
345    }
346
347    /// Finds an index of the provided `value` inside `buckets`.
348    ///
349    /// Uses the optional `current_sequence_number` arguments for checking the
350    /// validity of the element.
351    pub fn find_element_index(
352        &self,
353        value: &BigUint,
354        current_sequence_number: Option<usize>,
355    ) -> Result<Option<usize>, HashSetError> {
356        for i in 0..ITERATIONS {
357            let probe_index = self.probe_index(value, i);
358            // PANICS: `probe_index()` ensures the bounds.
359            let bucket = self.get_bucket(probe_index).unwrap();
360            match bucket {
361                Some(bucket) => {
362                    if &bucket.value_biguint() == value {
363                        match current_sequence_number {
364                            // If the caller provided `current_sequence_number`,
365                            // check the validity of the bucket.
366                            Some(current_sequence_number) => {
367                                if bucket.is_valid(current_sequence_number) {
368                                    return Ok(Some(probe_index));
369                                }
370                                continue;
371                            }
372                            None => return Ok(Some(probe_index)),
373                        }
374                    }
375                    continue;
376                }
377                // If we found an empty bucket, it means that there is no
378                // chance of our element existing in the hash set.
379                None => {
380                    return Ok(None);
381                }
382            }
383        }
384
385        Ok(None)
386    }
387
388    pub fn find_element(
389        &self,
390        value: &BigUint,
391        current_sequence_number: Option<usize>,
392    ) -> Result<Option<(&HashSetCell, usize)>, HashSetError> {
393        let index = self.find_element_index(value, current_sequence_number)?;
394        match index {
395            Some(index) => {
396                let bucket = self.get_bucket(index).unwrap();
397                match bucket {
398                    Some(bucket) => Ok(Some((bucket, index))),
399                    None => Ok(None),
400                }
401            }
402            None => Ok(None),
403        }
404    }
405
406    pub fn find_element_mut(
407        &mut self,
408        value: &BigUint,
409        current_sequence_number: Option<usize>,
410    ) -> Result<Option<(&mut HashSetCell, usize)>, HashSetError> {
411        let index = self.find_element_index(value, current_sequence_number)?;
412        match index {
413            Some(index) => {
414                let bucket = self.get_bucket_mut(index).unwrap();
415                match bucket {
416                    Some(bucket) => Ok(Some((bucket, index))),
417                    None => Ok(None),
418                }
419            }
420            None => Ok(None),
421        }
422    }
423
424    /// find_element_iter iterates over a fixed range of elements
425    /// in the hash set.
426    /// We always have to iterate over the whole range
427    /// to make sure that the value is not in the hash-set.
428    /// Returns the position of the first free value.
429    pub fn find_element_iter(
430        &mut self,
431        value: &BigUint,
432        current_sequence_number: usize,
433        start_iter: usize,
434        num_iterations: usize,
435    ) -> Result<Option<(usize, bool)>, HashSetError> {
436        let mut first_free_element: Option<(usize, bool)> = None;
437        for i in start_iter..start_iter + num_iterations {
438            let probe_index = self.probe_index(value, i);
439            let bucket = self.get_bucket(probe_index).unwrap();
440
441            match bucket {
442                Some(bucket) => {
443                    let is_valid = bucket.is_valid(current_sequence_number);
444                    if first_free_element.is_none() && !is_valid {
445                        first_free_element = Some((probe_index, false));
446                    }
447                    if is_valid && &bucket.value_biguint() == value {
448                        return Err(HashSetError::ElementAlreadyExists);
449                    } else {
450                        continue;
451                    }
452                }
453                None => {
454                    // A previous bucket could have been freed already even
455                    // though the whole hash set has not been used yet.
456                    if first_free_element.is_none() {
457                        first_free_element = Some((probe_index, true));
458                    }
459                    // Since we encountered an empty bucket we know for sure
460                    // that the element is not in a bucket with higher probe
461                    // index.
462                    break;
463                }
464            }
465        }
466        Ok(first_free_element)
467    }
468
469    /// Returns a first available element.
470    pub fn first(
471        &self,
472        current_sequence_number: usize,
473    ) -> Result<Option<&HashSetCell>, HashSetError> {
474        for i in 0..self.capacity {
475            // PANICS: The loop ensures the bounds.
476            let bucket = self.get_bucket(i).unwrap();
477            if let Some(bucket) = bucket {
478                if bucket.is_valid(current_sequence_number) {
479                    return Ok(Some(bucket));
480                }
481            }
482        }
483
484        Ok(None)
485    }
486
487    /// Returns a first available element that does not have a sequence number.
488    pub fn first_no_seq(&self) -> Result<Option<(HashSetCell, u16)>, HashSetError> {
489        for i in 0..self.capacity {
490            // PANICS: The loop ensures the bounds.
491            let bucket = self.get_bucket(i).unwrap();
492
493            if let Some(bucket) = bucket {
494                if bucket.sequence_number.is_none() {
495                    return Ok(Some((*bucket, i as u16)));
496                }
497            }
498        }
499
500        Ok(None)
501    }
502
503    /// Checks if the hash set contains a value.
504    pub fn contains(
505        &self,
506        value: &BigUint,
507        sequence_number: Option<usize>,
508    ) -> Result<bool, HashSetError> {
509        let element = self.find_element(value, sequence_number)?;
510        Ok(element.is_some())
511    }
512
513    /// Marks the given element with a given sequence number.
514    pub fn mark_with_sequence_number(
515        &mut self,
516        index: usize,
517        sequence_number: usize,
518    ) -> Result<(), HashSetError> {
519        let sequence_threshold = self.sequence_threshold;
520        let element = self
521            .get_bucket_mut(index)
522            .ok_or(HashSetError::ElementDoesNotExist)?;
523
524        match element {
525            Some(element) => {
526                element.sequence_number = Some(sequence_number + sequence_threshold);
527                Ok(())
528            }
529            None => Err(HashSetError::ElementDoesNotExist),
530        }
531    }
532
533    /// Returns an iterator over elements.
534    pub fn iter(&self) -> HashSetIterator {
535        HashSetIterator {
536            hash_set: self,
537            current: 0,
538        }
539    }
540}
541
542impl Drop for HashSet {
543    fn drop(&mut self) {
544        // SAFETY: As long as `next_value_index`, `capacity_indices` and
545        // `capacity_values` are correct, this deallocaion is safe.
546        unsafe {
547            let layout = Layout::array::<Option<HashSetCell>>(self.capacity).unwrap();
548            alloc::dealloc(self.buckets.as_ptr() as *mut u8, layout);
549        }
550    }
551}
552
553impl PartialEq for HashSet {
554    fn eq(&self, other: &Self) -> bool {
555        self.capacity.eq(&other.capacity)
556            && self.sequence_threshold.eq(&other.sequence_threshold)
557            && self.iter().eq(other.iter())
558    }
559}
560
561pub struct HashSetIterator<'a> {
562    hash_set: &'a HashSet,
563    current: usize,
564}
565
566impl<'a> Iterator for HashSetIterator<'a> {
567    type Item = (usize, &'a HashSetCell);
568
569    fn next(&mut self) -> Option<Self::Item> {
570        while self.current < self.hash_set.get_capacity() {
571            let element_index = self.current;
572            self.current += 1;
573
574            if let Some(Some(cur_element)) = self.hash_set.get_bucket(element_index) {
575                return Some((element_index, cur_element));
576            }
577        }
578        None
579    }
580}
581
582#[cfg(test)]
583mod test {
584    use ark_bn254::Fr;
585    use ark_ff::UniformRand;
586    use rand::{thread_rng, Rng};
587
588    use crate::zero_copy::HashSetZeroCopy;
589
590    use super::*;
591
592    #[test]
593    fn test_is_valid() {
594        let mut rng = thread_rng();
595
596        let cell = HashSetCell {
597            value: [0u8; 32],
598            sequence_number: None,
599        };
600        // It should be always valid, no matter the sequence number.
601        assert_eq!(cell.is_valid(0), true);
602        for _ in 0..100 {
603            let seq: usize = rng.gen();
604            assert_eq!(cell.is_valid(seq), true);
605        }
606
607        let cell = HashSetCell {
608            value: [0u8; 32],
609            sequence_number: Some(2400),
610        };
611        // Sequence numbers up to 2400 should succeed.
612        for i in 0..2400 {
613            assert_eq!(cell.is_valid(i), true);
614        }
615        for i in 2400..10000 {
616            assert_eq!(cell.is_valid(i), false);
617        }
618    }
619
620    /// Manual test cases. A simple check whether basic properties of the hash
621    /// set work.
622    #[test]
623    fn test_hash_set_manual() {
624        let mut hs = HashSet::new(256, 4).unwrap();
625
626        // Insert an element and immediately mark it with a sequence number.
627        // An equivalent to a single insertion in Light Protocol
628        let element_1_1 = 1.to_biguint().unwrap();
629        let index_1_1 = hs.insert(&element_1_1, 0).unwrap();
630        hs.mark_with_sequence_number(index_1_1, 1).unwrap();
631
632        // Check if element exists in the set.
633        assert_eq!(hs.contains(&element_1_1, Some(1)).unwrap(), true);
634        // Try inserting the same element, even though we didn't reach the
635        // threshold.
636        assert!(matches!(
637            hs.insert(&element_1_1, 1),
638            Err(HashSetError::ElementAlreadyExists)
639        ));
640
641        // Insert multiple elements and mark them with one sequence number.
642        // An equivalent to a batched insertion in Light Protocol.
643
644        let element_2_3 = 3.to_biguint().unwrap();
645        let element_2_6 = 6.to_biguint().unwrap();
646        let element_2_8 = 8.to_biguint().unwrap();
647        let element_2_9 = 9.to_biguint().unwrap();
648        let index_2_3 = hs.insert(&element_2_3, 1).unwrap();
649        let index_2_6 = hs.insert(&element_2_6, 1).unwrap();
650        let index_2_8 = hs.insert(&element_2_8, 1).unwrap();
651        let index_2_9 = hs.insert(&element_2_9, 1).unwrap();
652        assert_eq!(hs.contains(&element_2_3, Some(2)).unwrap(), true);
653        assert_eq!(hs.contains(&element_2_6, Some(2)).unwrap(), true);
654        assert_eq!(hs.contains(&element_2_8, Some(2)).unwrap(), true);
655        assert_eq!(hs.contains(&element_2_9, Some(2)).unwrap(), true);
656        hs.mark_with_sequence_number(index_2_3, 2).unwrap();
657        hs.mark_with_sequence_number(index_2_6, 2).unwrap();
658        hs.mark_with_sequence_number(index_2_8, 2).unwrap();
659        hs.mark_with_sequence_number(index_2_9, 2).unwrap();
660        assert!(matches!(
661            hs.insert(&element_2_3, 2),
662            Err(HashSetError::ElementAlreadyExists)
663        ));
664        assert!(matches!(
665            hs.insert(&element_2_6, 2),
666            Err(HashSetError::ElementAlreadyExists)
667        ));
668        assert!(matches!(
669            hs.insert(&element_2_8, 2),
670            Err(HashSetError::ElementAlreadyExists)
671        ));
672        assert!(matches!(
673            hs.insert(&element_2_9, 2),
674            Err(HashSetError::ElementAlreadyExists)
675        ));
676
677        let element_3_11 = 11.to_biguint().unwrap();
678        let element_3_13 = 13.to_biguint().unwrap();
679        let element_3_21 = 21.to_biguint().unwrap();
680        let element_3_29 = 29.to_biguint().unwrap();
681        let index_3_11 = hs.insert(&element_3_11, 2).unwrap();
682        let index_3_13 = hs.insert(&element_3_13, 2).unwrap();
683        let index_3_21 = hs.insert(&element_3_21, 2).unwrap();
684        let index_3_29 = hs.insert(&element_3_29, 2).unwrap();
685        assert_eq!(hs.contains(&element_3_11, Some(3)).unwrap(), true);
686        assert_eq!(hs.contains(&element_3_13, Some(3)).unwrap(), true);
687        assert_eq!(hs.contains(&element_3_21, Some(3)).unwrap(), true);
688        assert_eq!(hs.contains(&element_3_29, Some(3)).unwrap(), true);
689        hs.mark_with_sequence_number(index_3_11, 3).unwrap();
690        hs.mark_with_sequence_number(index_3_13, 3).unwrap();
691        hs.mark_with_sequence_number(index_3_21, 3).unwrap();
692        hs.mark_with_sequence_number(index_3_29, 3).unwrap();
693        assert!(matches!(
694            hs.insert(&element_3_11, 3),
695            Err(HashSetError::ElementAlreadyExists)
696        ));
697        assert!(matches!(
698            hs.insert(&element_3_13, 3),
699            Err(HashSetError::ElementAlreadyExists)
700        ));
701        assert!(matches!(
702            hs.insert(&element_3_21, 3),
703            Err(HashSetError::ElementAlreadyExists)
704        ));
705        assert!(matches!(
706            hs.insert(&element_3_29, 3),
707            Err(HashSetError::ElementAlreadyExists)
708        ));
709
710        let element_4_93 = 93.to_biguint().unwrap();
711        let element_4_65 = 64.to_biguint().unwrap();
712        let element_4_72 = 72.to_biguint().unwrap();
713        let element_4_15 = 15.to_biguint().unwrap();
714        let index_4_93 = hs.insert(&element_4_93, 3).unwrap();
715        let index_4_65 = hs.insert(&element_4_65, 3).unwrap();
716        let index_4_72 = hs.insert(&element_4_72, 3).unwrap();
717        let index_4_15 = hs.insert(&element_4_15, 3).unwrap();
718        assert_eq!(hs.contains(&element_4_93, Some(4)).unwrap(), true);
719        assert_eq!(hs.contains(&element_4_65, Some(4)).unwrap(), true);
720        assert_eq!(hs.contains(&element_4_72, Some(4)).unwrap(), true);
721        assert_eq!(hs.contains(&element_4_15, Some(4)).unwrap(), true);
722        hs.mark_with_sequence_number(index_4_93, 4).unwrap();
723        hs.mark_with_sequence_number(index_4_65, 4).unwrap();
724        hs.mark_with_sequence_number(index_4_72, 4).unwrap();
725        hs.mark_with_sequence_number(index_4_15, 4).unwrap();
726
727        // Try inserting the same elements we inserted before.
728        //
729        // Ones with the sequence number difference lower or equal to the
730        // sequence threshold (4) will fail.
731        //
732        // Ones with the higher dif will succeed.
733        assert!(matches!(
734            hs.insert(&element_1_1, 4),
735            Err(HashSetError::ElementAlreadyExists)
736        ));
737        assert!(matches!(
738            hs.insert(&element_2_3, 5),
739            Err(HashSetError::ElementAlreadyExists)
740        ));
741        assert!(matches!(
742            hs.insert(&element_2_6, 5),
743            Err(HashSetError::ElementAlreadyExists)
744        ));
745        assert!(matches!(
746            hs.insert(&element_2_8, 5),
747            Err(HashSetError::ElementAlreadyExists)
748        ));
749        assert!(matches!(
750            hs.insert(&element_2_9, 5),
751            Err(HashSetError::ElementAlreadyExists)
752        ));
753        hs.insert(&element_1_1, 5).unwrap();
754        hs.insert(&element_2_3, 6).unwrap();
755        hs.insert(&element_2_6, 6).unwrap();
756        hs.insert(&element_2_8, 6).unwrap();
757        hs.insert(&element_2_9, 6).unwrap();
758    }
759
760    /// Test cases with random prime field elements.
761    #[test]
762    fn test_hash_set_random() {
763        let mut hs = HashSet::new(6857, 2400).unwrap();
764
765        // The hash set should be empty.
766        assert_eq!(hs.first(0).unwrap(), None);
767        let mut rng = thread_rng();
768        let mut seq = 0;
769        let nullifiers: [BigUint; 24000] =
770            std::array::from_fn(|_| BigUint::from(Fr::rand(&mut rng)));
771        for nf_chunk in nullifiers.chunks(2400) {
772            for nullifier in nf_chunk.iter() {
773                assert_eq!(hs.contains(&nullifier, Some(seq)).unwrap(), false);
774                let index = hs.insert(&nullifier, seq as usize).unwrap();
775                assert_eq!(hs.contains(&nullifier, Some(seq)).unwrap(), true);
776
777                let nullifier_bytes = bigint_to_be_bytes_array(&nullifier).unwrap();
778
779                let element = hs
780                    .find_element(&nullifier, Some(seq))
781                    .unwrap()
782                    .unwrap()
783                    .0
784                    .clone();
785                assert_eq!(
786                    element,
787                    HashSetCell {
788                        value: bigint_to_be_bytes_array(&nullifier).unwrap(),
789                        sequence_number: None,
790                    }
791                );
792                assert_eq!(element.value_bytes(), nullifier_bytes);
793                assert_eq!(&element.value_biguint(), nullifier);
794                assert_eq!(element.sequence_number(), None);
795                assert!(!element.is_marked());
796                assert!(element.is_valid(seq));
797
798                hs.mark_with_sequence_number(index, seq).unwrap();
799                let element = hs
800                    .find_element(&nullifier, Some(seq))
801                    .unwrap()
802                    .unwrap()
803                    .0
804                    .clone();
805
806                assert_eq!(
807                    element,
808                    HashSetCell {
809                        value: nullifier_bytes,
810                        sequence_number: Some(2400 + seq)
811                    }
812                );
813                assert_eq!(element.value_bytes(), nullifier_bytes);
814                assert_eq!(&element.value_biguint(), nullifier);
815                assert_eq!(element.sequence_number(), Some(2400 + seq));
816                assert!(element.is_marked());
817                assert!(element.is_valid(seq));
818
819                // Trying to insert the same nullifier, before reaching the
820                // sequence threshold, should fail.
821                assert!(matches!(
822                    hs.insert(&nullifier, seq as usize + 2399),
823                    Err(HashSetError::ElementAlreadyExists),
824                ));
825                seq += 1;
826            }
827            seq += 2400;
828        }
829    }
830
831    fn hash_set_from_bytes_copy<
832        const CAPACITY: usize,
833        const SEQUENCE_THRESHOLD: usize,
834        const OPERATIONS: usize,
835    >() {
836        let mut hs_1 = HashSet::new(CAPACITY, SEQUENCE_THRESHOLD).unwrap();
837
838        let mut rng = thread_rng();
839
840        // Create a buffer with random bytes.
841        let mut bytes = vec![0u8; HashSet::size_in_account(CAPACITY)];
842        rng.fill(bytes.as_mut_slice());
843
844        // Initialize a hash set on top of a byte slice.
845        {
846            let mut hs_2 = unsafe {
847                HashSetZeroCopy::from_bytes_zero_copy_init(&mut bytes, CAPACITY, SEQUENCE_THRESHOLD)
848                    .unwrap()
849            };
850
851            for seq in 0..OPERATIONS {
852                let value = BigUint::from(Fr::rand(&mut rng));
853                hs_1.insert(&value, seq).unwrap();
854                hs_2.insert(&value, seq).unwrap();
855            }
856
857            assert_eq!(hs_1, *hs_2);
858        }
859
860        // Create a copy on top of a byte slice.
861        {
862            let hs_2 = unsafe { HashSet::from_bytes_copy(&mut bytes).unwrap() };
863            assert_eq!(hs_1, hs_2);
864        }
865    }
866
867    #[test]
868    fn test_hash_set_from_bytes_copy_6857_2400_3600() {
869        hash_set_from_bytes_copy::<6857, 2400, 3600>()
870    }
871
872    #[test]
873    fn test_hash_set_from_bytes_copy_9601_2400_5000() {
874        hash_set_from_bytes_copy::<9601, 2400, 5000>()
875    }
876
877    fn hash_set_full<const CAPACITY: usize, const SEQUENCE_THRESHOLD: usize>() {
878        for _ in 0..100 {
879            let mut hs = HashSet::new(CAPACITY, SEQUENCE_THRESHOLD).unwrap();
880
881            let mut rng = rand::thread_rng();
882
883            // Insert as many values as possible. The important point is to
884            // encounter the `HashSetError::Full` at some point
885            for i in 0..CAPACITY {
886                let value = BigUint::from(Fr::rand(&mut rng));
887                match hs.insert(&value, 0) {
888                    Ok(index) => hs.mark_with_sequence_number(index, 0).unwrap(),
889                    Err(e) => {
890                        assert!(matches!(e, HashSetError::Full));
891                        println!("initial insertions: {i}: failed, stopping");
892                        break;
893                    }
894                }
895            }
896
897            // Keep inserting. It should mostly fail, although there might be
898            // also some successful insertions - there might be values which
899            // will end up in unused buckets.
900            for i in 0..1000 {
901                let value = BigUint::from(Fr::rand(&mut rng));
902                let res = hs.insert(&value, 0);
903                if res.is_err() {
904                    assert!(matches!(res, Err(HashSetError::Full)));
905                } else {
906                    println!("secondary insertions: {i}: apparent success with value: {value:?}");
907                }
908            }
909
910            // Try again with defined sequence numbers, but still too small to
911            // vacate any cell.
912            for i in 0..1000 {
913                let value = BigUint::from(Fr::rand(&mut rng));
914                // Sequence numbers lower than the threshold should not vacate
915                // any cell.
916                let sequence_number = rng.gen_range(0..hs.sequence_threshold);
917                let res = hs.insert(&value, sequence_number);
918                if res.is_err() {
919                    assert!(matches!(res, Err(HashSetError::Full)));
920                } else {
921                    println!("tertiary insertions: {i}: surprising success with value: {value:?}");
922                }
923            }
924
925            // Use sequence numbers which are going to vacate cells. All
926            // insertions should be successful now.
927            for i in 0..CAPACITY {
928                let value = BigUint::from(Fr::rand(&mut rng));
929                if let Err(e) = hs.insert(&value, SEQUENCE_THRESHOLD + i) {
930                    assert!(matches!(e, HashSetError::Full));
931                    println!("insertions after fillup: {i}: failed, stopping");
932                    break;
933                }
934            }
935        }
936    }
937
938    #[test]
939    fn test_hash_set_full_6857_2400() {
940        hash_set_full::<6857, 2400>()
941    }
942
943    #[test]
944    fn test_hash_set_full_9601_2400() {
945        hash_set_full::<9601, 2400>()
946    }
947
948    #[test]
949    fn test_hash_set_element_does_not_exist() {
950        let mut hs = HashSet::new(4800, 2400).unwrap();
951
952        let mut rng = thread_rng();
953
954        for _ in 0..1000 {
955            let index = rng.gen_range(0..4800);
956
957            // Assert `ElementDoesNotExist` error.
958            let res = hs.mark_with_sequence_number(index, 0);
959            assert!(matches!(res, Err(HashSetError::ElementDoesNotExist)));
960        }
961
962        for _ in 0..1000 {
963            // After actually appending the value, the same operation should be
964            // possible
965            let value = BigUint::from(Fr::rand(&mut rng));
966            let index = hs.insert(&value, 0).unwrap();
967            hs.mark_with_sequence_number(index, 1).unwrap();
968        }
969    }
970
971    #[test]
972    fn test_hash_set_iter_manual() {
973        let mut hs = HashSet::new(6857, 2400).unwrap();
974
975        let nullifier_1 = 945635_u32.to_biguint().unwrap();
976        let nullifier_2 = 3546656654734254353455_u128.to_biguint().unwrap();
977        let nullifier_3 = 543543656564_u64.to_biguint().unwrap();
978        let nullifier_4 = 43_u8.to_biguint().unwrap();
979        let nullifier_5 = 0_u8.to_biguint().unwrap();
980        let nullifier_6 = 65423_u32.to_biguint().unwrap();
981        let nullifier_7 = 745654665_u32.to_biguint().unwrap();
982        let nullifier_8 = 97664353453465354645645465_u128.to_biguint().unwrap();
983        let nullifier_9 = 453565465464565635475_u128.to_biguint().unwrap();
984        let nullifier_10 = 543645654645_u64.to_biguint().unwrap();
985
986        hs.insert(&nullifier_1, 0).unwrap();
987        hs.insert(&nullifier_2, 0).unwrap();
988        hs.insert(&nullifier_3, 0).unwrap();
989        hs.insert(&nullifier_4, 0).unwrap();
990        hs.insert(&nullifier_5, 0).unwrap();
991        hs.insert(&nullifier_6, 0).unwrap();
992        hs.insert(&nullifier_7, 0).unwrap();
993        hs.insert(&nullifier_8, 0).unwrap();
994        hs.insert(&nullifier_9, 0).unwrap();
995        hs.insert(&nullifier_10, 0).unwrap();
996
997        let inserted_nullifiers = hs
998            .iter()
999            .map(|(_, nullifier)| nullifier.value_biguint())
1000            .collect::<Vec<_>>();
1001        assert_eq!(inserted_nullifiers.len(), 10);
1002        assert_eq!(inserted_nullifiers[0], nullifier_7);
1003        assert_eq!(inserted_nullifiers[1], nullifier_3);
1004        assert_eq!(inserted_nullifiers[2], nullifier_10);
1005        assert_eq!(inserted_nullifiers[3], nullifier_1);
1006        assert_eq!(inserted_nullifiers[4], nullifier_8);
1007        assert_eq!(inserted_nullifiers[5], nullifier_5);
1008        assert_eq!(inserted_nullifiers[6], nullifier_4);
1009        assert_eq!(inserted_nullifiers[7], nullifier_2);
1010        assert_eq!(inserted_nullifiers[8], nullifier_9);
1011        assert_eq!(inserted_nullifiers[9], nullifier_6);
1012    }
1013
1014    fn hash_set_iter_random<
1015        const INSERTIONS: usize,
1016        const CAPACITY: usize,
1017        const SEQUENCE_THRESHOLD: usize,
1018    >() {
1019        let mut hs = HashSet::new(CAPACITY, SEQUENCE_THRESHOLD).unwrap();
1020        let mut rng = thread_rng();
1021
1022        let nullifiers: [BigUint; INSERTIONS] =
1023            std::array::from_fn(|_| BigUint::from(Fr::rand(&mut rng)));
1024
1025        for nullifier in nullifiers.iter() {
1026            hs.insert(&nullifier, 0).unwrap();
1027        }
1028
1029        let mut sorted_nullifiers = nullifiers.iter().collect::<Vec<_>>();
1030        let mut inserted_nullifiers = hs
1031            .iter()
1032            .map(|(_, nullifier)| nullifier.value_biguint())
1033            .collect::<Vec<_>>();
1034        sorted_nullifiers.sort();
1035        inserted_nullifiers.sort();
1036
1037        let inserted_nullifiers = inserted_nullifiers.iter().collect::<Vec<&BigUint>>();
1038        assert_eq!(inserted_nullifiers.len(), INSERTIONS);
1039        assert_eq!(sorted_nullifiers.as_slice(), inserted_nullifiers.as_slice());
1040    }
1041
1042    #[test]
1043    fn test_hash_set_iter_random_6857_2400() {
1044        hash_set_iter_random::<3500, 6857, 2400>()
1045    }
1046
1047    #[test]
1048    fn test_hash_set_iter_random_9601_2400() {
1049        hash_set_iter_random::<5000, 9601, 2400>()
1050    }
1051
1052    #[test]
1053    fn test_hash_set_get_bucket() {
1054        let mut hs = HashSet::new(6857, 2400).unwrap();
1055
1056        for i in 0..3600 {
1057            let bn_i = i.to_biguint().unwrap();
1058            hs.insert(&bn_i, i).unwrap();
1059        }
1060        let mut unused_indices = vec![true; 6857];
1061        for i in 0..3600 {
1062            let bn_i = i.to_biguint().unwrap();
1063            let i = hs.find_element_index(&bn_i, None).unwrap().unwrap();
1064            let element = hs.get_bucket(i).unwrap().unwrap();
1065            assert_eq!(element.value_biguint(), bn_i);
1066            unused_indices[i] = false;
1067        }
1068        // Unused cells within the capacity should be `Some(None)`.
1069        for i in unused_indices.iter().enumerate() {
1070            if *i.1 {
1071                assert!(hs.get_bucket(i.0).unwrap().is_none());
1072            }
1073        }
1074        // Cells over the capacity should be `None`.
1075        for i in 6857..10_000 {
1076            assert!(hs.get_bucket(i).is_none());
1077        }
1078    }
1079
1080    #[test]
1081    fn test_hash_set_get_bucket_mut() {
1082        let mut hs = HashSet::new(6857, 2400).unwrap();
1083
1084        for i in 0..3600 {
1085            let bn_i = i.to_biguint().unwrap();
1086            hs.insert(&bn_i, i).unwrap();
1087        }
1088        let mut unused_indices = vec![false; 6857];
1089
1090        for i in 0..3600 {
1091            let bn_i = i.to_biguint().unwrap();
1092            let i = hs.find_element_index(&bn_i, None).unwrap().unwrap();
1093
1094            let element = hs.get_bucket_mut(i).unwrap();
1095            assert_eq!(element.unwrap().value_biguint(), bn_i);
1096            unused_indices[i] = true;
1097
1098            // "Nullify" the element.
1099            *element = Some(HashSetCell {
1100                value: [0_u8; 32],
1101                sequence_number: None,
1102            });
1103        }
1104
1105        for (i, is_used) in unused_indices.iter().enumerate() {
1106            if *is_used {
1107                let element = hs.get_bucket_mut(i).unwrap().unwrap();
1108                assert_eq!(element.value_bytes(), [0_u8; 32]);
1109            }
1110        }
1111        // Unused cells within the capacity should be `Some(None)`.
1112        for (i, is_used) in unused_indices.iter().enumerate() {
1113            if !*is_used {
1114                assert!(hs.get_bucket_mut(i).unwrap().is_none());
1115            }
1116        }
1117        // Cells over the capacity should be `None`.
1118        for i in 6857..10_000 {
1119            assert!(hs.get_bucket_mut(i).is_none());
1120        }
1121    }
1122
1123    #[test]
1124    fn test_hash_set_get_unmarked_bucket() {
1125        let mut hs = HashSet::new(6857, 2400).unwrap();
1126
1127        // Insert incremental elements, so they end up being in the same
1128        // sequence in the hash set.
1129        (0..3600).for_each(|i| {
1130            let bn_i = i.to_biguint().unwrap();
1131            hs.insert(&bn_i, i).unwrap();
1132        });
1133
1134        for i in 0..3600 {
1135            let i = hs
1136                .find_element_index(&i.to_biguint().unwrap(), None)
1137                .unwrap()
1138                .unwrap();
1139            let element = hs.get_unmarked_bucket(i);
1140            assert!(element.is_some());
1141        }
1142
1143        // Mark the elements.
1144        for i in 0..3600 {
1145            let index = hs
1146                .find_element_index(&i.to_biguint().unwrap(), None)
1147                .unwrap()
1148                .unwrap();
1149            hs.mark_with_sequence_number(index, i).unwrap();
1150        }
1151
1152        for i in 0..3600 {
1153            let i = hs
1154                .find_element_index(&i.to_biguint().unwrap(), None)
1155                .unwrap()
1156                .unwrap();
1157            let element = hs.get_unmarked_bucket(i);
1158            assert!(element.is_none());
1159        }
1160    }
1161
1162    #[test]
1163    fn test_hash_set_first_no_seq() {
1164        let mut hs = HashSet::new(6857, 2400).unwrap();
1165
1166        // Insert incremental elements, so they end up being in the same
1167        // sequence in the hash set.
1168        for i in 0..3600 {
1169            let bn_i = i.to_biguint().unwrap();
1170            hs.insert(&bn_i, i).unwrap();
1171
1172            let element = hs.first_no_seq().unwrap().unwrap();
1173            assert_eq!(element.0.value_biguint(), 0.to_biguint().unwrap());
1174        }
1175    }
1176}