solana_slot_hashes/
lib.rs

1//! A type to hold data for the [`SlotHashes` sysvar][sv].
2//!
3//! [sv]: https://docs.solanalabs.com/runtime/sysvars#slothashes
4//!
5//! The sysvar ID is declared in [`solana_program::sysvar::slot_hashes`].
6//!
7//! [`solana_program::sysvar::slot_hashes`]: https://docs.rs/solana-program/latest/solana_program/sysvar/slot_hashes/index.html
8
9#[cfg(feature = "sysvar")]
10pub mod sysvar;
11
12use {
13    solana_hash::Hash,
14    std::{
15        iter::FromIterator,
16        ops::Deref,
17        sync::atomic::{AtomicUsize, Ordering},
18    },
19};
20
21pub const MAX_ENTRIES: usize = 512; // about 2.5 minutes to get your vote in
22
23// This is to allow tests with custom slot hash expiry to avoid having to generate
24// 512 blocks for such tests.
25static NUM_ENTRIES: AtomicUsize = AtomicUsize::new(MAX_ENTRIES);
26
27pub fn get_entries() -> usize {
28    NUM_ENTRIES.load(Ordering::Relaxed)
29}
30
31pub fn set_entries_for_tests_only(entries: usize) {
32    NUM_ENTRIES.store(entries, Ordering::Relaxed);
33}
34
35pub type SlotHash = (u64, Hash);
36
37#[repr(C)]
38#[cfg_attr(
39    feature = "serde",
40    derive(serde_derive::Deserialize, serde_derive::Serialize)
41)]
42#[derive(PartialEq, Eq, Debug, Default)]
43pub struct SlotHashes(Vec<SlotHash>);
44
45impl SlotHashes {
46    pub fn add(&mut self, slot: u64, hash: Hash) {
47        match self.binary_search_by(|(probe, _)| slot.cmp(probe)) {
48            Ok(index) => (self.0)[index] = (slot, hash),
49            Err(index) => (self.0).insert(index, (slot, hash)),
50        }
51        (self.0).truncate(get_entries());
52    }
53    pub fn position(&self, slot: &u64) -> Option<usize> {
54        self.binary_search_by(|(probe, _)| slot.cmp(probe)).ok()
55    }
56    #[allow(clippy::trivially_copy_pass_by_ref)]
57    pub fn get(&self, slot: &u64) -> Option<&Hash> {
58        self.binary_search_by(|(probe, _)| slot.cmp(probe))
59            .ok()
60            .map(|index| &self[index].1)
61    }
62    pub fn new(slot_hashes: &[SlotHash]) -> Self {
63        let mut slot_hashes = slot_hashes.to_vec();
64        slot_hashes.sort_by(|(a, _), (b, _)| b.cmp(a));
65        Self(slot_hashes)
66    }
67    pub fn slot_hashes(&self) -> &[SlotHash] {
68        &self.0
69    }
70}
71
72impl FromIterator<(u64, Hash)> for SlotHashes {
73    fn from_iter<I: IntoIterator<Item = (u64, Hash)>>(iter: I) -> Self {
74        Self(iter.into_iter().collect())
75    }
76}
77
78impl Deref for SlotHashes {
79    type Target = Vec<SlotHash>;
80    fn deref(&self) -> &Self::Target {
81        &self.0
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use {super::*, solana_sha256_hasher::hash};
88
89    #[test]
90    fn test() {
91        let mut slot_hashes = SlotHashes::new(&[(1, Hash::default()), (3, Hash::default())]);
92        slot_hashes.add(2, Hash::default());
93        assert_eq!(
94            slot_hashes,
95            SlotHashes(vec![
96                (3, Hash::default()),
97                (2, Hash::default()),
98                (1, Hash::default()),
99            ])
100        );
101
102        let mut slot_hashes = SlotHashes::new(&[]);
103        for i in 0..MAX_ENTRIES + 1 {
104            slot_hashes.add(
105                i as u64,
106                hash(&[(i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]),
107            );
108        }
109        for i in 0..MAX_ENTRIES {
110            assert_eq!(slot_hashes[i].0, (MAX_ENTRIES - i) as u64);
111        }
112
113        assert_eq!(slot_hashes.len(), MAX_ENTRIES);
114    }
115}