solana_stake_interface/
stake_history.rs1pub use solana_clock::Epoch;
6use std::ops::Deref;
7
8pub const MAX_ENTRIES: usize = 512; #[repr(C)]
11#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
12#[cfg_attr(
13 feature = "serde",
14 derive(serde_derive::Deserialize, serde_derive::Serialize)
15)]
16#[derive(Debug, PartialEq, Eq, Default, Clone)]
17pub struct StakeHistoryEntry {
18 pub effective: u64, pub activating: u64, pub deactivating: u64, }
22
23impl StakeHistoryEntry {
24 pub fn with_effective(effective: u64) -> Self {
25 Self {
26 effective,
27 ..Self::default()
28 }
29 }
30
31 pub fn with_effective_and_activating(effective: u64, activating: u64) -> Self {
32 Self {
33 effective,
34 activating,
35 ..Self::default()
36 }
37 }
38
39 pub fn with_deactivating(deactivating: u64) -> Self {
40 Self {
41 effective: deactivating,
42 deactivating,
43 ..Self::default()
44 }
45 }
46}
47
48impl std::ops::Add for StakeHistoryEntry {
49 type Output = StakeHistoryEntry;
50 fn add(self, rhs: StakeHistoryEntry) -> Self::Output {
51 Self {
52 effective: self.effective.saturating_add(rhs.effective),
53 activating: self.activating.saturating_add(rhs.activating),
54 deactivating: self.deactivating.saturating_add(rhs.deactivating),
55 }
56 }
57}
58
59#[repr(C)]
60#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
61#[cfg_attr(
62 feature = "serde",
63 derive(serde_derive::Deserialize, serde_derive::Serialize)
64)]
65#[derive(Debug, PartialEq, Eq, Default, Clone)]
66pub struct StakeHistory(Vec<(Epoch, StakeHistoryEntry)>);
67
68impl StakeHistory {
69 #[inline]
70 fn latest_epoch(&self) -> Option<&Epoch> {
71 self.first().map(|(epoch, _)| epoch)
72 }
73
74 pub fn get(&self, epoch: Epoch) -> Option<&StakeHistoryEntry> {
75 self.latest_epoch()
76 .and_then(|latest| latest.checked_sub(epoch))
77 .and_then(|index| self.0.get(index as usize).map(|(_, entry)| entry))
78 }
79
80 pub fn add(&mut self, epoch: Epoch, entry: StakeHistoryEntry) {
81 match self.binary_search_by(|probe| epoch.cmp(&probe.0)) {
82 Ok(index) => (self.0)[index] = (epoch, entry),
83 Err(index) => (self.0).insert(index, (epoch, entry)),
84 }
85 (self.0).truncate(MAX_ENTRIES);
86 }
87}
88
89impl Deref for StakeHistory {
90 type Target = Vec<(Epoch, StakeHistoryEntry)>;
91 fn deref(&self) -> &Self::Target {
92 &self.0
93 }
94}
95
96pub trait StakeHistoryGetEntry {
97 fn get_entry(&self, epoch: Epoch) -> Option<StakeHistoryEntry>;
98}
99
100impl StakeHistoryGetEntry for StakeHistory {
101 fn get_entry(&self, epoch: Epoch) -> Option<StakeHistoryEntry> {
102 self.get(epoch).map(|entry| entry.to_owned())
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use {super::*, solana_sysvar_id::SysvarId};
109
110 #[test]
111 fn test_stake_history() {
112 let mut stake_history = StakeHistory::default();
113
114 for i in 0..MAX_ENTRIES as u64 + 1 {
115 stake_history.add(
116 i,
117 StakeHistoryEntry {
118 activating: i,
119 ..StakeHistoryEntry::default()
120 },
121 );
122 }
123 assert_eq!(stake_history.len(), MAX_ENTRIES);
124 assert_eq!(stake_history.iter().map(|entry| entry.0).min().unwrap(), 1);
125 assert_eq!(stake_history.get(0), None);
126 for i in 0..MAX_ENTRIES {
127 let epoch = (i + 1) as u64;
128 assert_eq!(
129 stake_history.get(epoch),
130 Some(&StakeHistoryEntry {
131 activating: epoch,
132 ..StakeHistoryEntry::default()
133 })
134 );
135 }
136 }
137
138 #[test]
139 fn test_id() {
140 assert_eq!(
141 StakeHistory::id(),
142 solana_sdk_ids::sysvar::stake_history::id()
143 );
144 }
145}