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