solana_runtime/stakes/
serde_stakes.rs

1use {
2    super::{StakeAccount, Stakes},
3    crate::stake_history::StakeHistory,
4    im::HashMap as ImHashMap,
5    serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer},
6    solana_clock::Epoch,
7    solana_pubkey::Pubkey,
8    solana_stake_program::stake_state::Stake,
9    solana_vote::vote_account::VoteAccounts,
10    std::{collections::HashMap, sync::Arc},
11};
12
13/// Wrapper struct with custom serialization to support serializing
14/// `Stakes<StakeAccount>` as `Stakes<Stake>` without doing an intermediate
15/// clone of the stake data.
16#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))]
17#[derive(Debug, Clone)]
18pub enum SerdeStakesToStakeFormat {
19    Stake(Stakes<Stake>),
20    Account(Stakes<StakeAccount>),
21}
22
23impl SerdeStakesToStakeFormat {
24    pub fn vote_accounts(&self) -> &VoteAccounts {
25        match self {
26            Self::Stake(stakes) => stakes.vote_accounts(),
27            Self::Account(stakes) => stakes.vote_accounts(),
28        }
29    }
30
31    pub fn staked_nodes(&self) -> Arc<HashMap<Pubkey, u64>> {
32        match self {
33            Self::Stake(stakes) => stakes.staked_nodes(),
34            Self::Account(stakes) => stakes.staked_nodes(),
35        }
36    }
37}
38
39#[cfg(feature = "dev-context-only-utils")]
40impl PartialEq<Self> for SerdeStakesToStakeFormat {
41    fn eq(&self, other: &Self) -> bool {
42        match (self, other) {
43            (Self::Stake(stakes), Self::Stake(other)) => stakes == other,
44            (Self::Account(stakes), Self::Account(other)) => stakes == other,
45            (Self::Stake(stakes), Self::Account(other)) => {
46                stakes == &Stakes::<Stake>::from(other.clone())
47            }
48            (Self::Account(stakes), Self::Stake(other)) => {
49                other == &Stakes::<Stake>::from(stakes.clone())
50            }
51        }
52    }
53}
54
55impl From<Stakes<StakeAccount>> for SerdeStakesToStakeFormat {
56    fn from(stakes: Stakes<StakeAccount>) -> Self {
57        Self::Account(stakes)
58    }
59}
60
61impl Serialize for SerdeStakesToStakeFormat {
62    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
63    where
64        S: Serializer,
65    {
66        match self {
67            Self::Stake(stakes) => stakes.serialize(serializer),
68            Self::Account(stakes) => serialize_stake_accounts_to_stake_format(stakes, serializer),
69        }
70    }
71}
72
73impl<'de> Deserialize<'de> for SerdeStakesToStakeFormat {
74    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75    where
76        D: Deserializer<'de>,
77    {
78        let stakes = Stakes::<Stake>::deserialize(deserializer)?;
79        Ok(Self::Stake(stakes))
80    }
81}
82
83pub(crate) fn serialize_stake_accounts_to_delegation_format<S: Serializer>(
84    stakes: &Stakes<StakeAccount>,
85    serializer: S,
86) -> Result<S::Ok, S::Error> {
87    SerdeStakeAccountsToDelegationFormat::from(stakes.clone()).serialize(serializer)
88}
89
90fn serialize_stake_accounts_to_stake_format<S: Serializer>(
91    stakes: &Stakes<StakeAccount>,
92    serializer: S,
93) -> Result<S::Ok, S::Error> {
94    SerdeStakeAccountsToStakeFormat::from(stakes.clone()).serialize(serializer)
95}
96
97impl From<Stakes<Stake>> for SerdeStakesToDelegationFormat {
98    fn from(stakes: Stakes<Stake>) -> Self {
99        let Stakes {
100            vote_accounts,
101            stake_delegations,
102            unused,
103            epoch,
104            stake_history,
105        } = stakes;
106
107        Self {
108            vote_accounts,
109            stake_delegations: SerdeStakeMapToDelegationFormat(stake_delegations),
110            unused,
111            epoch,
112            stake_history,
113        }
114    }
115}
116
117impl From<Stakes<StakeAccount>> for SerdeStakeAccountsToDelegationFormat {
118    fn from(stakes: Stakes<StakeAccount>) -> Self {
119        let Stakes {
120            vote_accounts,
121            stake_delegations,
122            unused,
123            epoch,
124            stake_history,
125        } = stakes;
126
127        Self {
128            vote_accounts,
129            stake_delegations: SerdeStakeAccountMapToDelegationFormat(stake_delegations),
130            unused,
131            epoch,
132            stake_history,
133        }
134    }
135}
136
137impl From<Stakes<StakeAccount>> for SerdeStakeAccountsToStakeFormat {
138    fn from(stakes: Stakes<StakeAccount>) -> Self {
139        let Stakes {
140            vote_accounts,
141            stake_delegations,
142            unused,
143            epoch,
144            stake_history,
145        } = stakes;
146
147        Self {
148            vote_accounts,
149            stake_delegations: SerdeStakeAccountMapToStakeFormat(stake_delegations),
150            unused,
151            epoch,
152            stake_history,
153        }
154    }
155}
156
157#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
158#[derive(Serialize)]
159struct SerdeStakesToDelegationFormat {
160    vote_accounts: VoteAccounts,
161    stake_delegations: SerdeStakeMapToDelegationFormat,
162    unused: u64,
163    epoch: Epoch,
164    stake_history: StakeHistory,
165}
166
167#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
168#[derive(Serialize)]
169struct SerdeStakeAccountsToDelegationFormat {
170    vote_accounts: VoteAccounts,
171    stake_delegations: SerdeStakeAccountMapToDelegationFormat,
172    unused: u64,
173    epoch: Epoch,
174    stake_history: StakeHistory,
175}
176
177#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
178#[derive(Serialize)]
179struct SerdeStakeAccountsToStakeFormat {
180    vote_accounts: VoteAccounts,
181    stake_delegations: SerdeStakeAccountMapToStakeFormat,
182    unused: u64,
183    epoch: Epoch,
184    stake_history: StakeHistory,
185}
186
187#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
188struct SerdeStakeMapToDelegationFormat(ImHashMap<Pubkey, Stake>);
189impl Serialize for SerdeStakeMapToDelegationFormat {
190    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
191    where
192        S: Serializer,
193    {
194        let mut s = serializer.serialize_map(Some(self.0.len()))?;
195        for (pubkey, stake) in self.0.iter() {
196            s.serialize_entry(pubkey, &stake.delegation)?;
197        }
198        s.end()
199    }
200}
201
202#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
203struct SerdeStakeAccountMapToDelegationFormat(ImHashMap<Pubkey, StakeAccount>);
204impl Serialize for SerdeStakeAccountMapToDelegationFormat {
205    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
206    where
207        S: Serializer,
208    {
209        let mut s = serializer.serialize_map(Some(self.0.len()))?;
210        for (pubkey, stake_account) in self.0.iter() {
211            s.serialize_entry(pubkey, stake_account.delegation())?;
212        }
213        s.end()
214    }
215}
216
217#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
218struct SerdeStakeAccountMapToStakeFormat(ImHashMap<Pubkey, StakeAccount>);
219impl Serialize for SerdeStakeAccountMapToStakeFormat {
220    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
221    where
222        S: Serializer,
223    {
224        let mut s = serializer.serialize_map(Some(self.0.len()))?;
225        for (pubkey, stake_account) in self.0.iter() {
226            s.serialize_entry(pubkey, stake_account.stake())?;
227        }
228        s.end()
229    }
230}
231
232#[cfg(test)]
233mod tests {
234    use {
235        super::*, crate::stakes::StakesCache, rand::Rng, solana_rent::Rent,
236        solana_stake_interface::state::Delegation, solana_stake_program::stake_state,
237        solana_vote_program::vote_state,
238    };
239
240    #[test]
241    fn test_serde_stakes_to_stake_format() {
242        let mut stake_delegations = ImHashMap::new();
243        stake_delegations.insert(
244            Pubkey::new_unique(),
245            StakeAccount::try_from(stake_state::create_account(
246                &Pubkey::new_unique(),
247                &Pubkey::new_unique(),
248                &vote_state::create_account(
249                    &Pubkey::new_unique(),
250                    &Pubkey::new_unique(),
251                    0,
252                    1_000_000_000,
253                ),
254                &Rent::default(),
255                1_000_000_000,
256            ))
257            .unwrap(),
258        );
259
260        let stake_account_stakes = Stakes {
261            vote_accounts: VoteAccounts::default(),
262            stake_delegations,
263            unused: 0,
264            epoch: 0,
265            stake_history: StakeHistory::default(),
266        };
267
268        let wrapped_stakes = SerdeStakesToStakeFormat::Account(stake_account_stakes.clone());
269        let serialized_stakes = bincode::serialize(&wrapped_stakes).unwrap();
270        let stake_stakes = bincode::deserialize::<Stakes<Stake>>(&serialized_stakes).unwrap();
271        let expected_stake_stakes = Stakes::<Stake>::from(stake_account_stakes);
272        assert_eq!(expected_stake_stakes, stake_stakes);
273    }
274
275    #[test]
276    fn test_serde_stakes_to_delegation_format() {
277        #[derive(Debug, Serialize)]
278        struct SerializableDummy {
279            head: String,
280            #[serde(serialize_with = "serialize_stake_accounts_to_delegation_format")]
281            stakes: Stakes<StakeAccount>,
282            tail: String,
283        }
284
285        #[derive(Debug, PartialEq, Deserialize)]
286        struct DeserializableDummy {
287            head: String,
288            stakes: Stakes<Delegation>,
289            tail: String,
290        }
291
292        impl From<SerializableDummy> for DeserializableDummy {
293            fn from(dummy: SerializableDummy) -> Self {
294                DeserializableDummy {
295                    head: dummy.head,
296                    stakes: dummy.stakes.into(),
297                    tail: dummy.tail,
298                }
299            }
300        }
301
302        let mut rng = rand::thread_rng();
303        let stakes_cache = StakesCache::new(Stakes {
304            unused: rng.gen(),
305            epoch: rng.gen(),
306            ..Stakes::default()
307        });
308        for _ in 0..rng.gen_range(5usize..10) {
309            let vote_pubkey = solana_pubkey::new_rand();
310            let vote_account = vote_state::create_account(
311                &vote_pubkey,
312                &solana_pubkey::new_rand(),  // node_pubkey
313                rng.gen_range(0..101),       // commission
314                rng.gen_range(0..1_000_000), // lamports
315            );
316            stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
317            for _ in 0..rng.gen_range(10usize..20) {
318                let stake_pubkey = solana_pubkey::new_rand();
319                let rent = Rent::with_slots_per_epoch(rng.gen());
320                let stake_account = stake_state::create_account(
321                    &stake_pubkey, // authorized
322                    &vote_pubkey,
323                    &vote_account,
324                    &rent,
325                    rng.gen_range(0..1_000_000), // lamports
326                );
327                stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
328            }
329        }
330        let stakes: Stakes<StakeAccount> = stakes_cache.stakes().clone();
331        assert!(stakes.vote_accounts.as_ref().len() >= 5);
332        assert!(stakes.stake_delegations.len() >= 50);
333        let dummy = SerializableDummy {
334            head: String::from("dummy-head"),
335            stakes: stakes.clone(),
336            tail: String::from("dummy-tail"),
337        };
338        assert!(dummy.stakes.vote_accounts().as_ref().len() >= 5);
339        let data = bincode::serialize(&dummy).unwrap();
340        let other: DeserializableDummy = bincode::deserialize(&data).unwrap();
341        assert_eq!(other, dummy.into());
342        let stakes = Stakes::<Delegation>::from(stakes);
343        assert!(stakes.vote_accounts.as_ref().len() >= 5);
344        assert!(stakes.stake_delegations.len() >= 50);
345        assert_eq!(other.stakes, stakes)
346    }
347}