solana_runtime/
stake_history.rs

1//! This module implements clone-on-write semantics for the SDK's `StakeHistory` to reduce
2//! unnecessary cloning of the underlying vector.
3pub use solana_sdk::stake_history::StakeHistoryGetEntry;
4use {
5    solana_sdk::{clock::Epoch, stake_history::StakeHistoryEntry},
6    std::{
7        ops::{Deref, DerefMut},
8        sync::Arc,
9    },
10};
11
12/// The SDK's stake history with clone-on-write semantics
13#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
14#[derive(Default, Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
15pub struct StakeHistory(Arc<StakeHistoryInner>);
16
17impl Deref for StakeHistory {
18    type Target = StakeHistoryInner;
19    fn deref(&self) -> &Self::Target {
20        &self.0
21    }
22}
23
24impl DerefMut for StakeHistory {
25    fn deref_mut(&mut self) -> &mut Self::Target {
26        Arc::make_mut(&mut self.0)
27    }
28}
29
30/// The inner type, which is the SDK's stake history
31type StakeHistoryInner = solana_sdk::stake_history::StakeHistory;
32
33impl StakeHistoryGetEntry for StakeHistory {
34    fn get_entry(&self, epoch: Epoch) -> Option<StakeHistoryEntry> {
35        self.0.get_entry(epoch)
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use {super::*, solana_sdk::stake_history::StakeHistoryEntry};
42
43    fn rand_stake_history_entry() -> StakeHistoryEntry {
44        StakeHistoryEntry {
45            effective: rand::random(),
46            activating: rand::random(),
47            deactivating: rand::random(),
48        }
49    }
50
51    /// Ensure that StakeHistory is indeed clone-on-write
52    #[test]
53    fn test_stake_history_is_cow() {
54        let mut stake_history = StakeHistory::default();
55        (100..109).for_each(|epoch| {
56            let entry = rand_stake_history_entry();
57            stake_history.add(epoch, entry);
58        });
59
60        // Test: Clone the stake history and **do not modify**.  Assert the underlying instances
61        // are the same.
62        {
63            let stake_history2 = stake_history.clone();
64            assert_eq!(stake_history, stake_history2);
65            assert!(
66                Arc::ptr_eq(&stake_history.0, &stake_history2.0),
67                "Inner Arc must point to the same underlying instance"
68            );
69            assert!(
70                std::ptr::eq(stake_history.deref(), stake_history2.deref()),
71                "Deref must point to the same underlying instance"
72            );
73        }
74
75        // Test: Clone the stake history and then modify.  Assert the underlying instances are
76        // unique.
77        {
78            let mut stake_history2 = stake_history.clone();
79            assert_eq!(stake_history, stake_history2);
80            (200..209).for_each(|epoch| {
81                let entry = rand_stake_history_entry();
82                stake_history2.add(epoch, entry);
83            });
84            assert_ne!(stake_history, stake_history2);
85            assert!(
86                !Arc::ptr_eq(&stake_history.0, &stake_history2.0),
87                "Inner Arc must point to a different underlying instance"
88            );
89            assert!(
90                !std::ptr::eq(stake_history.deref(), stake_history2.deref()),
91                "Deref must point to a different underlying instance"
92            );
93        }
94    }
95
96    /// Ensure that StakeHistory serializes and deserializes between the inner and outer types
97    #[test]
98    fn test_stake_history_serde() {
99        let mut stake_history_outer = StakeHistory::default();
100        let mut stake_history_inner = StakeHistoryInner::default();
101        (2134..).take(11).for_each(|epoch| {
102            let entry = rand_stake_history_entry();
103            stake_history_outer.add(epoch, entry.clone());
104            stake_history_inner.add(epoch, entry);
105        });
106
107        // Test: Assert that serializing the outer and inner types produces the same data
108        assert_eq!(
109            bincode::serialize(&stake_history_outer).unwrap(),
110            bincode::serialize(&stake_history_inner).unwrap(),
111        );
112
113        // Test: Assert that serializing the outer type then deserializing to the inner type
114        // produces the same values
115        {
116            let data = bincode::serialize(&stake_history_outer).unwrap();
117            let deserialized_inner: StakeHistoryInner = bincode::deserialize(&data).unwrap();
118            assert_eq!(&deserialized_inner, stake_history_outer.deref());
119        }
120
121        // Test: Assert that serializing the inner type then deserializing to the outer type
122        // produces the same values
123        {
124            let data = bincode::serialize(&stake_history_inner).unwrap();
125            let deserialized_outer: StakeHistory = bincode::deserialize(&data).unwrap();
126            assert_eq!(deserialized_outer.deref(), &stake_history_inner);
127        }
128    }
129}