Skip to main content

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