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