cbe_program/
stake_history.rs

1//! A type to hold data for the [`StakeHistory` sysvar][sv].
2//!
3//! [sv]: https://docs.cartallum.com/developing/runtime-facilities/sysvars#stakehistory
4//!
5//! The sysvar ID is declared in [`sysvar::stake_history`].
6//!
7//! [`sysvar::stake_history`]: crate::sysvar::stake_history
8
9pub use crate::clock::Epoch;
10use std::ops::Deref;
11
12pub const MAX_ENTRIES: usize = 512; // it should never take as many as 512 epochs to warm up or cool down
13
14#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default, Clone, AbiExample)]
15pub struct StakeHistoryEntry {
16    pub effective: u64,    // effective stake at this epoch
17    pub activating: u64,   // sum of portion of stakes not fully warmed up
18    pub deactivating: u64, // requested to be cooled down, not fully deactivated yet
19}
20
21impl StakeHistoryEntry {
22    pub fn with_effective(effective: u64) -> Self {
23        Self {
24            effective,
25            ..Self::default()
26        }
27    }
28
29    pub fn with_effective_and_activating(effective: u64, activating: u64) -> Self {
30        Self {
31            effective,
32            activating,
33            ..Self::default()
34        }
35    }
36
37    pub fn with_deactivating(deactivating: u64) -> Self {
38        Self {
39            effective: deactivating,
40            deactivating,
41            ..Self::default()
42        }
43    }
44}
45
46impl std::ops::Add for StakeHistoryEntry {
47    type Output = StakeHistoryEntry;
48    fn add(self, rhs: StakeHistoryEntry) -> Self::Output {
49        Self {
50            effective: self.effective.saturating_add(rhs.effective),
51            activating: self.activating.saturating_add(rhs.activating),
52            deactivating: self.deactivating.saturating_add(rhs.deactivating),
53        }
54    }
55}
56
57#[repr(C)]
58#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default, Clone, AbiExample)]
59pub struct StakeHistory(Vec<(Epoch, StakeHistoryEntry)>);
60
61impl StakeHistory {
62    pub fn get(&self, epoch: Epoch) -> Option<&StakeHistoryEntry> {
63        self.binary_search_by(|probe| epoch.cmp(&probe.0))
64            .ok()
65            .map(|index| &self[index].1)
66    }
67
68    pub fn add(&mut self, epoch: Epoch, entry: StakeHistoryEntry) {
69        match self.binary_search_by(|probe| epoch.cmp(&probe.0)) {
70            Ok(index) => (self.0)[index] = (epoch, entry),
71            Err(index) => (self.0).insert(index, (epoch, entry)),
72        }
73        (self.0).truncate(MAX_ENTRIES);
74    }
75}
76
77impl Deref for StakeHistory {
78    type Target = Vec<(Epoch, StakeHistoryEntry)>;
79    fn deref(&self) -> &Self::Target {
80        &self.0
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_stake_history() {
90        let mut stake_history = StakeHistory::default();
91
92        for i in 0..MAX_ENTRIES as u64 + 1 {
93            stake_history.add(
94                i,
95                StakeHistoryEntry {
96                    activating: i,
97                    ..StakeHistoryEntry::default()
98                },
99            );
100        }
101        assert_eq!(stake_history.len(), MAX_ENTRIES);
102        assert_eq!(stake_history.iter().map(|entry| entry.0).min().unwrap(), 1);
103        assert_eq!(stake_history.get(0), None);
104        assert_eq!(
105            stake_history.get(1),
106            Some(&StakeHistoryEntry {
107                activating: 1,
108                ..StakeHistoryEntry::default()
109            })
110        );
111    }
112}