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#[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(), rng.gen_range(0..101), rng.gen_range(0..1_000_000), );
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, &vote_pubkey,
323 &vote_account,
324 &rent,
325 rng.gen_range(0..1_000_000), );
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}