solana_slot_hashes/
lib.rs1#![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; static 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
36const LEN_PREFIX: usize = size_of::<u64>();
37const SLOT_HASH_SERIALIZED_SIZE: usize = size_of::<u64>() + size_of::<Hash>();
38
39pub const SIZE: usize = LEN_PREFIX + MAX_ENTRIES * SLOT_HASH_SERIALIZED_SIZE;
41const _: () = assert!(SIZE == 20_488);
42
43#[repr(C)]
47#[cfg_attr(
48 feature = "serde",
49 derive(serde_derive::Deserialize, serde_derive::Serialize)
50)]
51#[cfg_attr(feature = "wincode", derive(wincode::SchemaWrite, wincode::SchemaRead))]
52#[cfg_attr(
54 all(feature = "wincode", target_endian = "little"),
55 wincode(assert_zero_copy)
56)]
57#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
58pub struct SlotHash {
59 pub slot: u64,
60 pub hash: Hash,
61}
62
63const _: () = assert!(size_of::<SlotHash>() == SLOT_HASH_SERIALIZED_SIZE);
65
66impl SlotHash {
67 pub const fn new(slot: u64, hash: Hash) -> Self {
68 Self { slot, hash }
69 }
70}
71
72impl From<(u64, Hash)> for SlotHash {
73 fn from((slot, hash): (u64, Hash)) -> Self {
74 Self { slot, hash }
75 }
76}
77
78impl From<SlotHash> for (u64, Hash) {
79 fn from(SlotHash { slot, hash }: SlotHash) -> Self {
80 (slot, hash)
81 }
82}
83
84#[repr(C)]
85#[cfg_attr(
86 feature = "serde",
87 derive(serde_derive::Deserialize, serde_derive::Serialize)
88)]
89#[cfg_attr(feature = "wincode", derive(wincode::SchemaWrite, wincode::SchemaRead))]
90#[derive(PartialEq, Eq, Debug, Default)]
91pub struct SlotHashes(Vec<SlotHash>);
92
93impl SlotHashes {
94 pub fn add(&mut self, slot: u64, hash: Hash) {
95 let entry = SlotHash { slot, hash };
96 match self.binary_search_by(|probe| slot.cmp(&probe.slot)) {
97 Ok(index) => (self.0)[index] = entry,
98 Err(index) => (self.0).insert(index, entry),
99 }
100 (self.0).truncate(get_entries());
101 }
102 pub fn position(&self, slot: &u64) -> Option<usize> {
103 self.binary_search_by(|probe| slot.cmp(&probe.slot)).ok()
104 }
105 #[allow(clippy::trivially_copy_pass_by_ref)]
106 pub fn get(&self, slot: &u64) -> Option<&Hash> {
107 self.binary_search_by(|probe| slot.cmp(&probe.slot))
108 .ok()
109 .map(|index| &self[index].hash)
110 }
111 pub fn new(slot_hashes: &[SlotHash]) -> Self {
112 let mut slot_hashes = slot_hashes.to_vec();
113 slot_hashes.sort_by_key(|entry| std::cmp::Reverse(entry.slot));
114 Self(slot_hashes)
115 }
116 pub fn slot_hashes(&self) -> &[SlotHash] {
117 &self.0
118 }
119}
120
121impl FromIterator<SlotHash> for SlotHashes {
122 fn from_iter<I: IntoIterator<Item = SlotHash>>(iter: I) -> Self {
123 Self(iter.into_iter().collect())
124 }
125}
126
127impl FromIterator<(u64, Hash)> for SlotHashes {
128 fn from_iter<I: IntoIterator<Item = (u64, Hash)>>(iter: I) -> Self {
129 Self(iter.into_iter().map(SlotHash::from).collect())
130 }
131}
132
133impl Deref for SlotHashes {
134 type Target = Vec<SlotHash>;
135 fn deref(&self) -> &Self::Target {
136 &self.0
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use {super::*, solana_sha256_hasher::hash};
143
144 fn entry(slot: u64) -> SlotHash {
145 SlotHash::new(slot, hash(&slot.to_le_bytes()))
146 }
147
148 #[test]
149 fn test_size_of() {
150 let slot_hashes = SlotHashes(vec![SlotHash::default(); MAX_ENTRIES]);
151 assert_eq!(
152 wincode::serialized_size(&slot_hashes).unwrap() as usize,
153 SIZE,
154 );
155 }
156
157 #[test]
158 fn test() {
159 let mut slot_hashes = SlotHashes::new(&[entry(1), entry(3)]);
160 slot_hashes.add(2, hash(&2u64.to_le_bytes()));
161 assert_eq!(slot_hashes, SlotHashes(vec![entry(3), entry(2), entry(1)]));
162
163 let mut slot_hashes = SlotHashes::new(&[]);
164 for i in 0..MAX_ENTRIES + 1 {
165 slot_hashes.add(
166 i as u64,
167 hash(&[(i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]),
168 );
169 }
170 for i in 0..MAX_ENTRIES {
171 assert_eq!(slot_hashes[i].slot, (MAX_ENTRIES - i) as u64);
172 }
173
174 assert_eq!(slot_hashes.len(), MAX_ENTRIES);
175 }
176
177 #[test]
180 fn test_wire_compat() {
181 let entries: Vec<SlotHash> = (0..MAX_ENTRIES as u64).rev().map(entry).collect();
182 let tuples: Vec<(u64, Hash)> = entries.iter().cloned().map(Into::into).collect();
183 let slot_hashes = SlotHashes::new(&entries);
184
185 let expected = wincode::serialize(&tuples).unwrap();
186 assert_eq!(expected.len(), SIZE);
187 assert_eq!(wincode::serialize(&slot_hashes).unwrap(), expected);
188 assert_eq!(
189 wincode::deserialize::<SlotHashes>(&expected).unwrap(),
190 slot_hashes
191 );
192 }
193}