1#[cfg(feature = "shuttle-test")]
2use shuttle::sync::{Arc, Mutex};
3#[cfg(not(feature = "shuttle-test"))]
4use std::sync::{Arc, Mutex};
5use {
6 ahash::{HashMap, HashMapExt as _},
7 log::*,
8 serde::Serialize,
9 smallvec::SmallVec,
10 solana_accounts_db::ancestors::Ancestors,
11 solana_clock::{MAX_RECENT_BLOCKHASHES, Slot},
12 solana_hash::Hash,
13 std::{
14 collections::{HashSet, hash_map::Entry},
15 num::{NonZero, NonZeroUsize},
16 },
17};
18
19const MAX_ROOT_ENTRIES: usize = MAX_RECENT_BLOCKHASHES;
23
24const CACHED_KEY_SIZE: usize = 20;
26
27pub type ForkStatus<T> = Vec<(Slot, T)>;
29
30pub(crate) type KeySlice = [u8; CACHED_KEY_SIZE];
32
33type KeyMap<T> = HashMap<KeySlice, ForkStatus<T>>;
34
35pub type Status<T> = Arc<Mutex<HashMap<Hash, (usize, Vec<(KeySlice, T)>)>>>;
37
38type KeyStatusMap<T> = HashMap<Hash, (Slot, usize, KeyMap<T>)>;
41
42type SlotDeltaMap<T> = HashMap<Slot, Status<T>>;
44
45pub type SlotDelta<T> = (Slot, bool, Status<T>);
48
49#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
50#[derive(Clone, Debug)]
51pub struct StatusCache<T: Serialize + Clone> {
52 cache: KeyStatusMap<T>,
55 roots: HashSet<Slot>,
56 max_root_entries: NonZeroUsize,
57 slot_deltas: SlotDeltaMap<T>,
60}
61
62impl<T: Serialize + Clone> Default for StatusCache<T> {
63 fn default() -> Self {
64 Self {
65 cache: HashMap::default(),
66 roots: HashSet::from([0]),
68 max_root_entries: NonZero::new(MAX_ROOT_ENTRIES).unwrap(),
69 slot_deltas: HashMap::default(),
70 }
71 }
72}
73
74impl<T: Serialize + Clone> StatusCache<T> {
75 pub fn clear_slot_entries(&mut self, slot: Slot) {
84 let slot_deltas = self.slot_deltas.remove(&slot);
85 if let Some(slot_deltas) = slot_deltas {
86 let slot_deltas = slot_deltas.lock().unwrap();
87 let mut warned = false;
88 for (blockhash, (_, key_list)) in slot_deltas.iter() {
89 if let Entry::Occupied(mut o_blockhash_entries) = self.cache.entry(*blockhash) {
94 let (_, _, all_hash_maps) = o_blockhash_entries.get_mut();
95
96 for (key_slice, _) in key_list {
97 if let Entry::Occupied(mut o_key_list) = all_hash_maps.entry(*key_slice) {
98 let key_list = o_key_list.get_mut();
99 key_list.retain(|(updated_slot, _)| *updated_slot != slot);
100 if key_list.is_empty() {
101 o_key_list.remove_entry();
102 }
103 } else if !warned {
104 warn!(
125 "signature found more than once in the same slot, this means \
126 we're clearing a dead slot: {slot}"
127 );
128 warned = true;
129 }
130 }
131
132 if all_hash_maps.is_empty() {
133 o_blockhash_entries.remove_entry();
134 }
135 } else {
136 panic!("Blockhash must exist if it exists in self.slot_deltas, slot: {slot}")
137 }
138 }
139 }
140 }
141
142 pub fn get_status<K: AsRef<[u8]>>(
145 &self,
146 key: K,
147 transaction_blockhash: &Hash,
148 ancestors: &Ancestors,
149 ) -> Option<(Slot, T)> {
150 let map = self.cache.get(transaction_blockhash)?;
151 let (_, index, keymap) = map;
152 let max_key_index = key.as_ref().len().saturating_sub(CACHED_KEY_SIZE + 1);
153 let index = (*index).min(max_key_index);
154 let key_slice: &[u8; CACHED_KEY_SIZE] =
155 arrayref::array_ref![key.as_ref(), index, CACHED_KEY_SIZE];
156 if let Some(stored_forks) = keymap.get(key_slice) {
157 let res = stored_forks
158 .iter()
159 .find(|(f, _)| ancestors.contains_key(f) || self.roots.contains(f))
160 .cloned();
161 if res.is_some() {
162 return res;
163 }
164 }
165 None
166 }
167
168 pub fn get_status_any_blockhash<K: AsRef<[u8]>>(
172 &self,
173 key: K,
174 ancestors: &Ancestors,
175 ) -> Option<(Slot, T)> {
176 self.cache.keys().find_map(|blockhash| {
177 trace!("get_status_any_blockhash: trying {blockhash}");
178 self.get_status(&key, blockhash, ancestors)
179 })
180 }
181
182 pub fn add_root(&mut self, fork: Slot) {
187 self.roots.insert(fork);
188 self.purge_roots();
189 }
190
191 pub fn add_roots<I: IntoIterator<Item = Slot>>(&mut self, forks: I) {
192 self.roots.extend(forks);
193 self.purge_roots();
194 }
195
196 pub fn roots(&self) -> &HashSet<Slot> {
197 &self.roots
198 }
199
200 pub fn max_root_entries(&self) -> usize {
201 self.max_root_entries.into()
202 }
203
204 pub fn set_max_root_entries(&mut self, max_root_entries: NonZeroUsize) {
205 self.max_root_entries = max_root_entries;
206 self.purge_roots();
207 }
208
209 pub fn insert<K: AsRef<[u8]>>(
211 &mut self,
212 transaction_blockhash: &Hash,
213 key: K,
214 slot: Slot,
215 res: T,
216 ) {
217 let max_key_index = key.as_ref().len().saturating_sub(CACHED_KEY_SIZE + 1);
218
219 let (max_slot, key_index, hash_map) = self
221 .cache
222 .entry(*transaction_blockhash)
223 .or_insert_with(|| (slot, 0, HashMap::new()));
224
225 *max_slot = std::cmp::max(slot, *max_slot);
227
228 let key_index = (*key_index).min(max_key_index);
230 let mut key_slice = [0u8; CACHED_KEY_SIZE];
231 key_slice.clone_from_slice(&key.as_ref()[key_index..key_index + CACHED_KEY_SIZE]);
232
233 let forks = hash_map.entry(key_slice).or_default();
236 forks.push((slot, res.clone()));
237
238 self.add_to_slot_delta(transaction_blockhash, slot, key_index, key_slice, res);
239 }
240
241 pub fn purge_roots(&mut self) {
242 let max_root_entries = self.max_root_entries();
243 if self.roots.len() > max_root_entries {
244 let num_roots_to_purge = self.roots.len() - max_root_entries;
245 let mut roots = self
246 .roots
247 .iter()
248 .copied()
249 .collect::<SmallVec<[Slot; 0x200]>>();
250 let (_, cutoff, _) = roots.select_nth_unstable(num_roots_to_purge - 1);
251 let cutoff = *cutoff;
252
253 self.roots.retain(|root| *root > cutoff);
254 self.cache.retain(|_, (fork, _, _)| *fork > cutoff);
255 self.slot_deltas.retain(|slot, _| *slot > cutoff);
256 }
257 }
258
259 #[cfg(feature = "dev-context-only-utils")]
260 pub fn clear(&mut self) {
261 for v in self.cache.values_mut() {
262 v.2 = HashMap::new();
263 }
264
265 self.slot_deltas
266 .iter_mut()
267 .for_each(|(_, status)| status.lock().unwrap().clear());
268 }
269
270 pub fn root_slot_deltas(&self) -> Vec<SlotDelta<T>> {
278 self.roots()
279 .iter()
280 .map(|root| {
281 (
282 *root,
283 true, self.slot_deltas.get(root).cloned().unwrap_or_default(),
285 )
286 })
287 .collect()
288 }
289
290 pub fn append(&mut self, slot_deltas: &[SlotDelta<T>]) {
295 for (slot, is_root, statuses) in slot_deltas {
296 statuses
297 .lock()
298 .unwrap()
299 .iter()
300 .for_each(|(tx_hash, (key_index, statuses))| {
301 for (key_slice, res) in statuses.iter() {
302 self.insert_with_slice(tx_hash, *slot, *key_index, *key_slice, res.clone())
303 }
304 });
305 if *is_root {
306 self.add_root(*slot);
307 }
308 }
309 }
310
311 fn insert_with_slice(
312 &mut self,
313 transaction_blockhash: &Hash,
314 slot: Slot,
315 key_index: usize,
316 key_slice: [u8; CACHED_KEY_SIZE],
317 res: T,
318 ) {
319 let hash_map =
320 self.cache
321 .entry(*transaction_blockhash)
322 .or_insert((slot, key_index, HashMap::new()));
323 hash_map.0 = std::cmp::max(slot, hash_map.0);
324
325 let forks = hash_map.2.entry(key_slice).or_default();
326 forks.push((slot, res.clone()));
327
328 self.add_to_slot_delta(transaction_blockhash, slot, key_index, key_slice, res);
329 }
330
331 fn add_to_slot_delta(
333 &mut self,
334 transaction_blockhash: &Hash,
335 slot: Slot,
336 key_index: usize,
337 key_slice: [u8; CACHED_KEY_SIZE],
338 res: T,
339 ) {
340 let mut fork_entry = self.slot_deltas.entry(slot).or_default().lock().unwrap();
341 let (_key_index, hash_entry) = fork_entry
342 .entry(*transaction_blockhash)
343 .or_insert((key_index, vec![]));
344 hash_entry.push((key_slice, res))
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use {super::*, solana_sha256_hasher::hash, solana_signature::Signature};
351
352 type BankStatusCache = StatusCache<()>;
353
354 impl<T: Serialize + Clone> StatusCache<T> {
355 fn from_slot_deltas(slot_deltas: &[SlotDelta<T>]) -> Self {
356 let mut cache = Self::default();
357 cache.append(slot_deltas);
358 cache
359 }
360 }
361
362 impl<T: Serialize + Clone + PartialEq> PartialEq for StatusCache<T> {
363 fn eq(&self, other: &Self) -> bool {
364 self.roots == other.roots
365 && self
366 .cache
367 .iter()
368 .all(|(hash, (slot, key_index, hash_map))| {
369 if let Some((other_slot, other_key_index, other_hash_map)) =
370 other.cache.get(hash)
371 && slot == other_slot
372 && key_index == other_key_index
373 {
374 return hash_map.iter().all(|(slice, fork_map)| {
375 if let Some(other_fork_map) = other_hash_map.get(slice) {
376 return fork_map.last() == other_fork_map.last();
379 }
380 false
381 });
382 }
383 false
384 })
385 }
386 }
387
388 #[test]
389 fn test_empty_has_no_sigs() {
390 let sig = Signature::default();
391 let blockhash = hash(Hash::default().as_ref());
392 let status_cache = BankStatusCache::default();
393 assert_eq!(
394 status_cache.get_status(sig, &blockhash, &Ancestors::default()),
395 None
396 );
397 assert_eq!(
398 status_cache.get_status_any_blockhash(sig, &Ancestors::default()),
399 None
400 );
401 }
402
403 #[test]
404 fn test_find_sig_with_ancestor_fork() {
405 let sig = Signature::default();
406 let mut status_cache = BankStatusCache::default();
407 let blockhash = hash(Hash::default().as_ref());
408 let ancestors = Ancestors::from(vec![0]);
409 status_cache.insert(&blockhash, sig, 0, ());
410 assert_eq!(
411 status_cache.get_status(sig, &blockhash, &ancestors),
412 Some((0, ()))
413 );
414 assert_eq!(
415 status_cache.get_status_any_blockhash(sig, &ancestors),
416 Some((0, ()))
417 );
418 }
419
420 #[test]
421 fn test_find_sig_without_ancestor_fork() {
422 let sig = Signature::default();
423 let mut status_cache = BankStatusCache::default();
424 let blockhash = hash(Hash::default().as_ref());
425 let ancestors = Ancestors::default();
426 status_cache.insert(&blockhash, sig, 1, ());
427 assert_eq!(status_cache.get_status(sig, &blockhash, &ancestors), None);
428 assert_eq!(status_cache.get_status_any_blockhash(sig, &ancestors), None);
429 }
430
431 #[test]
432 fn test_find_sig_with_root_ancestor_fork() {
433 let sig = Signature::default();
434 let mut status_cache = BankStatusCache::default();
435 let blockhash = hash(Hash::default().as_ref());
436 let ancestors = Ancestors::default();
437 status_cache.insert(&blockhash, sig, 0, ());
438 status_cache.add_root(0);
439 assert_eq!(
440 status_cache.get_status(sig, &blockhash, &ancestors),
441 Some((0, ()))
442 );
443 }
444
445 #[test]
446 fn test_insert_picks_latest_blockhash_fork() {
447 let sig = Signature::default();
448 let mut status_cache = BankStatusCache::default();
449 let blockhash = hash(Hash::default().as_ref());
450 let ancestors = Ancestors::from(vec![0]);
451 status_cache.insert(&blockhash, sig, 0, ());
452 status_cache.insert(&blockhash, sig, 1, ());
453 for i in 0..=status_cache.max_root_entries() {
454 status_cache.add_root(i as u64);
455 }
456 assert!(
457 status_cache
458 .get_status(sig, &blockhash, &ancestors)
459 .is_some()
460 );
461 }
462
463 #[test]
464 fn test_root_expires() {
465 let sig = Signature::default();
466 let mut status_cache = BankStatusCache::default();
467 let blockhash = hash(Hash::default().as_ref());
468 let ancestors = Ancestors::default();
469 status_cache.insert(&blockhash, sig, 0, ());
470 for i in 0..=status_cache.max_root_entries() {
471 status_cache.add_root(i as u64);
472 }
473 assert_eq!(status_cache.get_status(sig, &blockhash, &ancestors), None);
474 }
475
476 #[test]
477 fn test_max_root_entries() {
478 const INITIAL_MAX_ROOT_ENTRIES: usize = 4;
479 const GROWN_MAX_ROOT_ENTRIES: usize = 6;
480 const SHRUNK_MAX_ROOT_ENTRIES: usize = 3;
481
482 let mut status_cache = BankStatusCache::default();
483 let ancestors = Ancestors::default();
484 let oldest_sig = Signature::from([1_u8; 64]);
485 let oldest_blockhash = Hash::new_unique();
486 let newest_sig = Signature::from([2_u8; 64]);
487 let newest_blockhash = Hash::new_unique();
488
489 status_cache.set_max_root_entries(NonZeroUsize::new(INITIAL_MAX_ROOT_ENTRIES).unwrap());
490
491 status_cache.insert(&oldest_blockhash, oldest_sig, 0, ());
492 status_cache.insert(&newest_blockhash, newest_sig, 3, ());
493 for root in 1..INITIAL_MAX_ROOT_ENTRIES as Slot {
494 status_cache.add_root(root);
495 }
496
497 assert_eq!(status_cache.roots(), &HashSet::from([0_u64, 1, 2, 3]));
498 assert_eq!(
499 status_cache.get_status(oldest_sig, &oldest_blockhash, &ancestors),
500 Some((0, ()))
501 );
502 assert_eq!(
503 status_cache.get_status(newest_sig, &newest_blockhash, &ancestors),
504 Some((3, ()))
505 );
506
507 status_cache.set_max_root_entries(NonZeroUsize::new(GROWN_MAX_ROOT_ENTRIES).unwrap());
508 for root in INITIAL_MAX_ROOT_ENTRIES as Slot..GROWN_MAX_ROOT_ENTRIES as Slot {
509 status_cache.add_root(root);
510 }
511
512 assert_eq!(status_cache.roots(), &HashSet::from([0_u64, 1, 2, 3, 4, 5]));
513 assert_eq!(
514 status_cache.get_status(oldest_sig, &oldest_blockhash, &ancestors),
515 Some((0, ()))
516 );
517 assert_eq!(
518 status_cache.get_status(newest_sig, &newest_blockhash, &ancestors),
519 Some((3, ()))
520 );
521
522 status_cache.set_max_root_entries(NonZeroUsize::new(SHRUNK_MAX_ROOT_ENTRIES).unwrap());
523
524 assert_eq!(status_cache.roots(), &HashSet::from([3_u64, 4, 5]));
525 assert_eq!(
526 status_cache.get_status(oldest_sig, &oldest_blockhash, &ancestors),
527 None
528 );
529 assert_eq!(
530 status_cache.get_status(newest_sig, &newest_blockhash, &ancestors),
531 Some((3, ()))
532 );
533 assert!(!status_cache.cache.contains_key(&oldest_blockhash));
534 assert!(status_cache.cache.contains_key(&newest_blockhash));
535 assert!(!status_cache.slot_deltas.contains_key(&0));
536 assert!(status_cache.slot_deltas.contains_key(&3));
537 }
538
539 #[test]
540 fn test_clear_signatures_sigs_are_gone() {
541 let sig = Signature::default();
542 let mut status_cache = BankStatusCache::default();
543 let blockhash = hash(Hash::default().as_ref());
544 let ancestors = Ancestors::default();
545 status_cache.insert(&blockhash, sig, 0, ());
546 status_cache.add_root(0);
547 status_cache.clear();
548 assert_eq!(status_cache.get_status(sig, &blockhash, &ancestors), None);
549 }
550
551 #[test]
552 fn test_clear_signatures_insert_works() {
553 let sig = Signature::default();
554 let mut status_cache = BankStatusCache::default();
555 let blockhash = hash(Hash::default().as_ref());
556 let ancestors = Ancestors::default();
557 status_cache.add_root(0);
558 status_cache.clear();
559 status_cache.insert(&blockhash, sig, 0, ());
560 assert!(
561 status_cache
562 .get_status(sig, &blockhash, &ancestors)
563 .is_some()
564 );
565 }
566
567 #[test]
568 fn test_signatures_slice() {
569 let sig = Signature::default();
570 let mut status_cache = BankStatusCache::default();
571 let blockhash = hash(Hash::default().as_ref());
572 status_cache.clear();
573 status_cache.insert(&blockhash, sig, 0, ());
574 let (_, index, sig_map) = status_cache.cache.get(&blockhash).unwrap();
575 let sig_slice: &[u8; CACHED_KEY_SIZE] =
576 arrayref::array_ref![sig.as_ref(), *index, CACHED_KEY_SIZE];
577 assert!(sig_map.get(sig_slice).is_some());
578 }
579
580 #[test]
581 fn test_slot_deltas() {
582 let sig = Signature::default();
583 let mut status_cache = BankStatusCache::default();
584 let blockhash = hash(Hash::default().as_ref());
585 status_cache.clear();
586 status_cache.insert(&blockhash, sig, 0, ());
587 assert!(status_cache.roots().contains(&0));
588 let slot_deltas = status_cache.root_slot_deltas();
589 let cache = StatusCache::from_slot_deltas(&slot_deltas);
590 assert_eq!(cache, status_cache);
591 let slot_deltas = cache.root_slot_deltas();
592 let cache = StatusCache::from_slot_deltas(&slot_deltas);
593 assert_eq!(cache, status_cache);
594 }
595
596 #[test]
597 fn test_roots_deltas() {
598 let sig = Signature::default();
599 let mut status_cache = BankStatusCache::default();
600 let blockhash = hash(Hash::default().as_ref());
601 let blockhash2 = hash(blockhash.as_ref());
602 status_cache.insert(&blockhash, sig, 0, ());
603 status_cache.insert(&blockhash, sig, 1, ());
604 status_cache.insert(&blockhash2, sig, 1, ());
605 for i in 0..=status_cache.max_root_entries() {
606 status_cache.add_root(i as u64);
607 }
608 assert_eq!(status_cache.slot_deltas.len(), 1);
609 assert!(status_cache.slot_deltas.contains_key(&1));
610 let slot_deltas = status_cache.root_slot_deltas();
611 let cache = StatusCache::from_slot_deltas(&slot_deltas);
612 assert_eq!(cache, status_cache);
613 }
614
615 #[test]
616 fn test_clear_slot_signatures() {
617 let sig = Signature::default();
618 let mut status_cache = BankStatusCache::default();
619 let blockhash = hash(Hash::default().as_ref());
620 let blockhash2 = hash(blockhash.as_ref());
621 status_cache.insert(&blockhash, sig, 0, ());
622 status_cache.insert(&blockhash, sig, 1, ());
623 status_cache.insert(&blockhash2, sig, 1, ());
624
625 let mut ancestors0 = Ancestors::default();
626 ancestors0.insert(0);
627 let mut ancestors1 = Ancestors::default();
628 ancestors1.insert(1);
629
630 assert!(
632 status_cache
633 .get_status(sig, &blockhash, &ancestors0)
634 .is_some()
635 );
636 status_cache.clear_slot_entries(0);
637 assert!(
638 status_cache
639 .get_status(sig, &blockhash, &ancestors0)
640 .is_none()
641 );
642 assert!(
643 status_cache
644 .get_status(sig, &blockhash, &ancestors1)
645 .is_some()
646 );
647 assert!(
648 status_cache
649 .get_status(sig, &blockhash2, &ancestors1)
650 .is_some()
651 );
652
653 assert!(!status_cache.slot_deltas.contains_key(&0));
656 assert!(status_cache.slot_deltas.contains_key(&1));
657
658 status_cache.clear_slot_entries(1);
660 assert!(status_cache.slot_deltas.is_empty());
661 assert!(
662 status_cache
663 .get_status(sig, &blockhash, &ancestors1)
664 .is_none()
665 );
666 assert!(
667 status_cache
668 .get_status(sig, &blockhash2, &ancestors1)
669 .is_none()
670 );
671 assert!(status_cache.cache.is_empty());
672 }
673
674 #[test]
675 fn test_clear_invalid_slot_signatures() {
676 let mut status_cache = BankStatusCache::default();
677 let blockhash = hash(Hash::default().as_ref());
678 let sig = Signature::default();
679 status_cache.insert(&blockhash, sig, 0, ());
680 status_cache.insert(&blockhash, sig, 0, ());
683 status_cache.clear_slot_entries(0);
685 }
686
687 #[test]
690 fn test_different_sized_keys() {
691 let mut status_cache = BankStatusCache::default();
692 let ancestors = Ancestors::from(vec![0]);
693 let blockhash = Hash::default();
694 for _ in 0..100 {
695 let blockhash = hash(blockhash.as_ref());
696 let sig_key = Signature::default();
697 let hash_key = Hash::new_unique();
698 status_cache.insert(&blockhash, sig_key, 0, ());
699 status_cache.insert(&blockhash, hash_key, 0, ());
700 assert!(
701 status_cache
702 .get_status(sig_key, &blockhash, &ancestors)
703 .is_some()
704 );
705 assert!(
706 status_cache
707 .get_status(hash_key, &blockhash, &ancestors)
708 .is_some()
709 );
710 }
711 }
712}
713
714#[cfg(all(test, feature = "shuttle-test"))]
715mod shuttle_tests {
716 use {super::*, shuttle::sync::RwLock};
717
718 type BankStatusCache = RwLock<StatusCache<()>>;
719
720 const CLEAR_DFS_ITERATIONS: Option<usize> = None;
721 const CLEAR_RANDOM_ITERATIONS: usize = 20000;
722 const PURGE_DFS_ITERATIONS: Option<usize> = None;
723 const PURGE_RANDOM_ITERATIONS: usize = 8000;
724 const INSERT_DFS_ITERATIONS: Option<usize> = Some(20000);
725 const INSERT_RANDOM_ITERATIONS: usize = 20000;
726
727 fn do_test_shuttle_clear_slots_blockhash_overlap() {
728 let status_cache = Arc::new(BankStatusCache::default());
729
730 let blockhash1 = Hash::new_from_array([1; 32]);
731
732 let key1 = Hash::new_from_array([3; 32]);
733 let key2 = Hash::new_from_array([4; 32]);
734
735 status_cache
736 .write()
737 .unwrap()
738 .insert(&blockhash1, key1, 1, ());
739 let th_clear = shuttle::thread::spawn({
740 let status_cache = status_cache.clone();
741 move || {
742 status_cache.write().unwrap().clear_slot_entries(1);
743 }
744 });
745
746 let th_insert = shuttle::thread::spawn({
747 let status_cache = status_cache.clone();
748 move || {
749 status_cache
751 .write()
752 .unwrap()
753 .insert(&blockhash1, key2, 2, ());
754 }
755 });
756
757 th_clear.join().unwrap();
758 th_insert.join().unwrap();
759
760 let mut ancestors2 = Ancestors::default();
761 ancestors2.insert(2);
762
763 assert!(
764 status_cache
765 .read()
766 .unwrap()
767 .get_status(key2, &blockhash1, &ancestors2)
768 .is_some()
769 );
770 }
771 #[test]
772 fn test_shuttle_clear_slots_blockhash_overlap_random() {
773 shuttle::check_random(
774 do_test_shuttle_clear_slots_blockhash_overlap,
775 CLEAR_RANDOM_ITERATIONS,
776 );
777 }
778
779 #[test]
780 fn test_shuttle_clear_slots_blockhash_overlap_dfs() {
781 shuttle::check_dfs(
782 do_test_shuttle_clear_slots_blockhash_overlap,
783 CLEAR_DFS_ITERATIONS,
784 );
785 }
786
787 fn do_test_shuttle_purge_nonce_overlap() {
791 let status_cache = Arc::new(BankStatusCache::default());
792 let max_root_entries = status_cache.read().unwrap().max_root_entries();
794 for i in 0..max_root_entries {
795 status_cache.write().unwrap().add_root(i as u64);
796 }
797
798 let blockhash1 = Hash::new_from_array([1; 32]);
799
800 let key1 = Hash::new_from_array([3; 32]);
801 let key2 = Hash::new_from_array([4; 32]);
802
803 status_cache
805 .write()
806 .unwrap()
807 .insert(&blockhash1, key1, 0, ());
808
809 let th_purge = shuttle::thread::spawn({
810 let status_cache = status_cache.clone();
811 move || {
812 status_cache
813 .write()
814 .unwrap()
815 .add_root(max_root_entries as Slot + 1);
816 }
817 });
818
819 let th_insert = shuttle::thread::spawn({
820 let status_cache = status_cache.clone();
821 move || {
822 status_cache.write().unwrap().insert(
824 &blockhash1,
825 key2,
826 max_root_entries as Slot + 2,
827 (),
828 );
829 }
830 });
831 th_purge.join().unwrap();
832 th_insert.join().unwrap();
833
834 let mut ancestors2 = Ancestors::default();
835 ancestors2.insert(max_root_entries as Slot + 2);
836
837 assert!(
838 status_cache
839 .read()
840 .unwrap()
841 .get_status(key1, &blockhash1, &ancestors2)
842 .is_none()
843 );
844 assert!(
845 status_cache
846 .read()
847 .unwrap()
848 .get_status(key2, &blockhash1, &ancestors2)
849 .is_some()
850 );
851 }
852
853 #[test]
854 fn test_shuttle_purge_nonce_overlap_random() {
855 shuttle::check_random(do_test_shuttle_purge_nonce_overlap, PURGE_RANDOM_ITERATIONS);
856 }
857
858 #[test]
859 fn test_shuttle_purge_nonce_overlap_dfs() {
860 shuttle::check_dfs(do_test_shuttle_purge_nonce_overlap, PURGE_DFS_ITERATIONS);
861 }
862
863 fn do_test_shuttle_concurrent_inserts() {
864 let status_cache = Arc::new(BankStatusCache::default());
865 let blockhash1 = Hash::new_from_array([42; 32]);
866 let blockhash2 = Hash::new_from_array([43; 32]);
867 const N_INSERTS: u8 = 50;
868
869 let mut handles = Vec::with_capacity(N_INSERTS as usize);
870 for i in 0..N_INSERTS {
871 let status_cache = status_cache.clone();
872 let slot = (i % 3) + 1;
873 let bh = if i % 2 == 0 { blockhash1 } else { blockhash2 };
874 handles.push(shuttle::thread::spawn(move || {
875 let key = Hash::new_from_array([i; 32]);
876 status_cache
877 .write()
878 .unwrap()
879 .insert(&bh, key, slot as Slot, ());
880 }));
881 }
882
883 for handle in handles {
884 handle.join().unwrap();
885 }
886
887 let mut ancestors = Ancestors::default();
888 ancestors.insert(1);
889 ancestors.insert(2);
890 ancestors.insert(3);
891
892 for i in 0..N_INSERTS {
894 let key = Hash::new_from_array([i; 32]);
895 let bh = if i % 2 == 0 { blockhash1 } else { blockhash2 };
896 assert!(
897 status_cache
898 .read()
899 .unwrap()
900 .get_status(key, &bh, &ancestors)
901 .is_some(),
902 "missing key {}",
903 i
904 );
905 }
906 }
907
908 #[test]
909 fn test_shuttle_concurrent_inserts_dfs() {
910 shuttle::check_dfs(do_test_shuttle_concurrent_inserts, INSERT_DFS_ITERATIONS);
911 }
912
913 #[test]
914 fn test_shuttle_concurrent_inserts_random() {
915 shuttle::check_random(do_test_shuttle_concurrent_inserts, INSERT_RANDOM_ITERATIONS);
916 }
917}