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_interface::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<StakeAccount>> for SerdeStakeAccountsToDelegationFormat {
98 fn from(stakes: Stakes<StakeAccount>) -> 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: SerdeStakeAccountMapToDelegationFormat(stake_delegations),
110 unused,
111 epoch,
112 stake_history,
113 }
114 }
115}
116
117impl From<Stakes<StakeAccount>> for SerdeStakeAccountsToStakeFormat {
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: SerdeStakeAccountMapToStakeFormat(stake_delegations),
130 unused,
131 epoch,
132 stake_history,
133 }
134 }
135}
136
137#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
138#[derive(Serialize)]
139struct SerdeStakeAccountsToDelegationFormat {
140 vote_accounts: VoteAccounts,
141 stake_delegations: SerdeStakeAccountMapToDelegationFormat,
142 unused: u64,
143 epoch: Epoch,
144 stake_history: StakeHistory,
145}
146
147#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
148#[derive(Serialize)]
149struct SerdeStakeAccountsToStakeFormat {
150 vote_accounts: VoteAccounts,
151 stake_delegations: SerdeStakeAccountMapToStakeFormat,
152 unused: u64,
153 epoch: Epoch,
154 stake_history: StakeHistory,
155}
156
157#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
158struct SerdeStakeAccountMapToDelegationFormat(ImHashMap<Pubkey, StakeAccount>);
159impl Serialize for SerdeStakeAccountMapToDelegationFormat {
160 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
161 where
162 S: Serializer,
163 {
164 let mut s = serializer.serialize_map(Some(self.0.len()))?;
165 for (pubkey, stake_account) in self.0.iter() {
166 s.serialize_entry(pubkey, stake_account.delegation())?;
167 }
168 s.end()
169 }
170}
171
172#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
173struct SerdeStakeAccountMapToStakeFormat(ImHashMap<Pubkey, StakeAccount>);
174impl Serialize for SerdeStakeAccountMapToStakeFormat {
175 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
176 where
177 S: Serializer,
178 {
179 let mut s = serializer.serialize_map(Some(self.0.len()))?;
180 for (pubkey, stake_account) in self.0.iter() {
181 s.serialize_entry(pubkey, stake_account.stake())?;
182 }
183 s.end()
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use {
190 super::*,
191 crate::{stake_utils, stakes::StakesCache},
192 rand::Rng,
193 solana_rent::Rent,
194 solana_stake_interface::state::Delegation,
195 solana_vote_program::vote_state,
196 };
197
198 #[test]
199 fn test_serde_stakes_to_stake_format() {
200 let mut stake_delegations = ImHashMap::new();
201 let vote_pubkey = Pubkey::new_unique();
202 let node_pubkey = Pubkey::new_unique();
203 stake_delegations.insert(
204 Pubkey::new_unique(),
205 StakeAccount::try_from(stake_utils::create_stake_account(
206 &Pubkey::new_unique(),
207 &vote_pubkey,
208 &vote_state::create_v4_account_with_authorized(
209 &node_pubkey,
210 &vote_pubkey,
211 &vote_pubkey,
212 None,
213 0,
214 1_000_000_000,
215 ),
216 &Rent::default(),
217 1_000_000_000,
218 ))
219 .unwrap(),
220 );
221
222 let stake_account_stakes = Stakes {
223 vote_accounts: VoteAccounts::default(),
224 stake_delegations,
225 unused: 0,
226 epoch: 0,
227 stake_history: StakeHistory::default(),
228 };
229
230 let wrapped_stakes = SerdeStakesToStakeFormat::Account(stake_account_stakes.clone());
231 let serialized_stakes = bincode::serialize(&wrapped_stakes).unwrap();
232 let stake_stakes = bincode::deserialize::<Stakes<Stake>>(&serialized_stakes).unwrap();
233 let expected_stake_stakes = Stakes::<Stake>::from(stake_account_stakes);
234 assert_eq!(expected_stake_stakes, stake_stakes);
235 }
236
237 #[test]
238 fn test_serde_stakes_to_delegation_format() {
239 #[derive(Debug, Serialize)]
240 struct SerializableDummy {
241 head: String,
242 #[serde(serialize_with = "serialize_stake_accounts_to_delegation_format")]
243 stakes: Stakes<StakeAccount>,
244 tail: String,
245 }
246
247 #[derive(Debug, PartialEq, Deserialize)]
248 struct DeserializableDummy {
249 head: String,
250 stakes: Stakes<Delegation>,
251 tail: String,
252 }
253
254 impl From<SerializableDummy> for DeserializableDummy {
255 fn from(dummy: SerializableDummy) -> Self {
256 DeserializableDummy {
257 head: dummy.head,
258 stakes: dummy.stakes.into(),
259 tail: dummy.tail,
260 }
261 }
262 }
263
264 let mut rng = rand::thread_rng();
265 let stakes_cache = StakesCache::new(Stakes {
266 unused: rng.gen(),
267 epoch: rng.gen(),
268 ..Stakes::default()
269 });
270 for _ in 0..rng.gen_range(5usize..10) {
271 let vote_pubkey = solana_pubkey::new_rand();
272 let node_pubkey = solana_pubkey::new_rand();
273 let commission = rng.gen_range(0..101);
274 let commission_bps = commission * 100;
275 let vote_account = vote_state::create_v4_account_with_authorized(
276 &node_pubkey,
277 &vote_pubkey,
278 &vote_pubkey,
279 None,
280 commission_bps,
281 rng.gen_range(0..1_000_000), );
283 stakes_cache.check_and_store(&vote_pubkey, &vote_account, None);
284 for _ in 0..rng.gen_range(10usize..20) {
285 let stake_pubkey = solana_pubkey::new_rand();
286 let rent = Rent::with_slots_per_epoch(rng.gen());
287 let stake_account = stake_utils::create_stake_account(
288 &stake_pubkey, &vote_pubkey,
290 &vote_account,
291 &rent,
292 rng.gen_range(0..1_000_000), );
294 stakes_cache.check_and_store(&stake_pubkey, &stake_account, None);
295 }
296 }
297 let stakes: Stakes<StakeAccount> = stakes_cache.stakes().clone();
298 assert!(stakes.vote_accounts.as_ref().len() >= 5);
299 assert!(stakes.stake_delegations.len() >= 50);
300 let dummy = SerializableDummy {
301 head: String::from("dummy-head"),
302 stakes: stakes.clone(),
303 tail: String::from("dummy-tail"),
304 };
305 assert!(dummy.stakes.vote_accounts().as_ref().len() >= 5);
306 let data = bincode::serialize(&dummy).unwrap();
307 let other: DeserializableDummy = bincode::deserialize(&data).unwrap();
308 assert_eq!(other, dummy.into());
309 let stakes = Stakes::<Delegation>::from(stakes);
310 assert!(stakes.vote_accounts.as_ref().len() >= 5);
311 assert!(stakes.stake_delegations.len() >= 50);
312 assert_eq!(other.stakes, stakes)
313 }
314}