solana_runtime/stakes/
serde_stakes.rs

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