1use crate::state::{vote_state_1_14_11::VoteState1_14_11, VoteStateV3, VoteStateV4};
2#[cfg(test)]
3use arbitrary::{Arbitrary, Unstructured};
4#[cfg(feature = "frozen-abi")]
5use solana_frozen_abi_macro::{frozen_abi, StableAbi, StableAbiSample};
6#[cfg(any(target_os = "solana", feature = "bincode"))]
7use solana_instruction_error::InstructionError;
8#[cfg(test)]
9use {
10 crate::state::{LandedVote, Lockout},
11 solana_pubkey::Pubkey,
12 std::collections::VecDeque,
13};
14
15#[cfg_attr(
16 feature = "frozen-abi",
17 frozen_abi(
18 abi_digest = "292pqhdmS6k7yrk5pUSQBpiQq1MLHb15pu6oM5WnWcug",
19 abi_serializer = ["bincode", "wincode"]
20 ),
21 derive(StableAbi, StableAbiSample)
22)]
23#[cfg_attr(
24 feature = "serde",
25 derive(serde_derive::Deserialize, serde_derive::Serialize)
26)]
27#[cfg_attr(feature = "wincode", derive(wincode::SchemaWrite, wincode::SchemaRead))]
28#[derive(Debug, PartialEq, Eq, Clone)]
29pub enum VoteStateVersions {
30 Uninitialized,
31 V1_14_11(Box<VoteState1_14_11>),
32 V3(Box<VoteStateV3>),
33 V4(Box<VoteStateV4>),
34}
35
36impl VoteStateVersions {
37 pub fn new_v3(vote_state: VoteStateV3) -> Self {
38 Self::V3(Box::new(vote_state))
39 }
40
41 pub fn new_v4(vote_state: VoteStateV4) -> Self {
42 Self::V4(Box::new(vote_state))
43 }
44
45 #[cfg(test)]
50 pub(crate) fn try_convert_to_v3(self) -> Result<VoteStateV3, InstructionError> {
51 match self {
52 VoteStateVersions::Uninitialized => Err(InstructionError::UninitializedAccount),
53
54 VoteStateVersions::V1_14_11(state) => Ok(VoteStateV3 {
55 node_pubkey: state.node_pubkey,
56 authorized_withdrawer: state.authorized_withdrawer,
57 commission: state.commission,
58
59 votes: Self::landed_votes_from_lockouts(state.votes),
60
61 root_slot: state.root_slot,
62
63 authorized_voters: state.authorized_voters.clone(),
64
65 prior_voters: state.prior_voters,
66
67 epoch_credits: state.epoch_credits,
68
69 last_timestamp: state.last_timestamp,
70 }),
71
72 VoteStateVersions::V3(state) => Ok(*state),
73
74 VoteStateVersions::V4(_) => Err(InstructionError::InvalidArgument),
76 }
77 }
78
79 #[cfg(test)]
80 pub(crate) fn try_convert_to_v4(
81 self,
82 vote_pubkey: &Pubkey,
83 ) -> Result<VoteStateV4, InstructionError> {
84 match self {
85 VoteStateVersions::Uninitialized => Err(InstructionError::UninitializedAccount),
86
87 VoteStateVersions::V1_14_11(state) => Ok(VoteStateV4 {
88 node_pubkey: state.node_pubkey,
89 authorized_withdrawer: state.authorized_withdrawer,
90 inflation_rewards_collector: *vote_pubkey,
91 block_revenue_collector: state.node_pubkey,
92 inflation_rewards_commission_bps: u16::from(state.commission).saturating_mul(100),
93 block_revenue_commission_bps: 10_000u16,
94 pending_delegator_rewards: 0,
95 bls_pubkey_compressed: None,
96 votes: Self::landed_votes_from_lockouts(state.votes),
97 root_slot: state.root_slot,
98 authorized_voters: state.authorized_voters.clone(),
99 epoch_credits: state.epoch_credits,
100 last_timestamp: state.last_timestamp,
101 }),
102
103 VoteStateVersions::V3(state) => Ok(VoteStateV4 {
104 node_pubkey: state.node_pubkey,
105 authorized_withdrawer: state.authorized_withdrawer,
106 inflation_rewards_collector: *vote_pubkey,
107 block_revenue_collector: state.node_pubkey,
108 inflation_rewards_commission_bps: u16::from(state.commission).saturating_mul(100),
109 block_revenue_commission_bps: 10_000u16,
110 pending_delegator_rewards: 0,
111 bls_pubkey_compressed: None,
112 votes: state.votes,
113 root_slot: state.root_slot,
114 authorized_voters: state.authorized_voters,
115 epoch_credits: state.epoch_credits,
116 last_timestamp: state.last_timestamp,
117 }),
118
119 VoteStateVersions::V4(state) => Ok(*state),
120 }
121 }
122
123 #[cfg(test)]
124 fn landed_votes_from_lockouts(lockouts: VecDeque<Lockout>) -> VecDeque<LandedVote> {
125 lockouts.into_iter().map(|lockout| lockout.into()).collect()
126 }
127
128 pub fn is_uninitialized(&self) -> bool {
129 match self {
130 VoteStateVersions::Uninitialized => true,
131
132 VoteStateVersions::V1_14_11(vote_state) => vote_state.is_uninitialized(),
133
134 VoteStateVersions::V3(vote_state) => vote_state.is_uninitialized(),
135
136 VoteStateVersions::V4(_) => false,
138 }
139 }
140
141 pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
142 VoteStateV4::is_correct_size_and_initialized(data)
143 || VoteStateV3::is_correct_size_and_initialized(data)
144 || VoteState1_14_11::is_correct_size_and_initialized(data)
145 }
146
147 #[cfg(any(target_os = "solana", feature = "bincode"))]
152 pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
153 use {
154 crate::state::vote_state_deserialize::{
155 deserialize_vote_state_into_v1_14_11, deserialize_vote_state_into_v3,
156 deserialize_vote_state_into_v4, SourceVersion,
157 },
158 std::mem::MaybeUninit,
159 };
160
161 let mut cursor = std::io::Cursor::new(input);
162
163 let variant = solana_serialize_utils::cursor::read_u32(&mut cursor)?;
164 match variant {
165 0 => Err(InstructionError::InvalidAccountData),
167 1 => {
169 let mut vote_state = Box::new(MaybeUninit::uninit());
170 deserialize_vote_state_into_v1_14_11(&mut cursor, vote_state.as_mut_ptr())?;
171 let vote_state =
172 unsafe { Box::from_raw(Box::into_raw(vote_state) as *mut VoteState1_14_11) };
173 Ok(VoteStateVersions::V1_14_11(vote_state))
174 }
175 2 => {
177 let mut vote_state = Box::new(MaybeUninit::uninit());
178 deserialize_vote_state_into_v3(&mut cursor, vote_state.as_mut_ptr(), true)?;
179 let vote_state =
180 unsafe { Box::from_raw(Box::into_raw(vote_state) as *mut VoteStateV3) };
181 Ok(VoteStateVersions::V3(vote_state))
182 }
183 3 => {
185 let mut vote_state = Box::new(MaybeUninit::uninit());
186 deserialize_vote_state_into_v4(
187 &mut cursor,
188 vote_state.as_mut_ptr(),
189 SourceVersion::V4,
190 )?;
191 let vote_state =
192 unsafe { Box::from_raw(Box::into_raw(vote_state) as *mut VoteStateV4) };
193 Ok(VoteStateVersions::V4(vote_state))
194 }
195 _ => Err(InstructionError::InvalidAccountData),
196 }
197 }
198}
199
200#[cfg(test)]
201impl Arbitrary<'_> for VoteStateVersions {
202 fn arbitrary(u: &mut Unstructured<'_>) -> arbitrary::Result<Self> {
203 let variant = u.choose_index(3)?;
204 match variant {
205 0 => Ok(Self::V4(Box::new(VoteStateV4::arbitrary(u)?))),
206 1 => Ok(Self::V3(Box::new(VoteStateV3::arbitrary(u)?))),
207 2 => Ok(Self::V1_14_11(Box::new(VoteState1_14_11::arbitrary(u)?))),
208 _ => unreachable!(),
209 }
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use {
216 super::*,
217 crate::state::{VoteInit, BLS_PUBLIC_KEY_COMPRESSED_SIZE, DEFAULT_PRIOR_VOTERS_OFFSET},
218 rand::Rng,
219 solana_clock::Clock,
220 solana_instruction::error::InstructionError,
221 };
222
223 #[test]
224 fn test_v3_default_vote_state_is_uninitialized() {
225 assert!(VoteStateVersions::new_v3(VoteStateV3::default()).is_uninitialized());
229 }
230
231 #[test]
232 fn test_v4_default_vote_state_is_always_initialized() {
233 assert!(!VoteStateVersions::new_v4(VoteStateV4::default()).is_uninitialized());
235 }
236
237 #[test]
238 fn test_is_correct_size_and_initialized_v3() {
239 let mut vote_account_data = vec![0; VoteStateV3::size_of()];
241 assert!(!VoteStateVersions::is_correct_size_and_initialized(
242 &vote_account_data
243 ));
244
245 let default_account_state = VoteStateVersions::new_v3(VoteStateV3::default());
247 VoteStateV3::serialize(&default_account_state, &mut vote_account_data).unwrap();
248 assert!(!VoteStateVersions::is_correct_size_and_initialized(
249 &vote_account_data
250 ));
251
252 let short_data = vec![1; DEFAULT_PRIOR_VOTERS_OFFSET];
254 assert!(!VoteStateVersions::is_correct_size_and_initialized(
255 &short_data
256 ));
257
258 let mut large_vote_data = vec![1; 2 * VoteStateV3::size_of()];
260 let default_account_state = VoteStateVersions::new_v3(VoteStateV3::default());
261 VoteStateV3::serialize(&default_account_state, &mut large_vote_data).unwrap();
262 assert!(!VoteStateVersions::is_correct_size_and_initialized(
263 &vote_account_data
264 ));
265
266 let vote_state = VoteStateV3::new(
268 &VoteInit {
269 node_pubkey: Pubkey::new_unique(),
270 authorized_voter: Pubkey::new_unique(),
271 authorized_withdrawer: Pubkey::new_unique(),
272 commission: 0,
273 },
274 &Clock::default(),
275 );
276 let account_state = VoteStateVersions::new_v3(vote_state.clone());
277 VoteStateV3::serialize(&account_state, &mut vote_account_data).unwrap();
278 assert!(VoteStateVersions::is_correct_size_and_initialized(
279 &vote_account_data
280 ));
281
282 let old_vote_state = VoteState1_14_11::from(vote_state);
284 let account_state = VoteStateVersions::V1_14_11(Box::new(old_vote_state));
285 let mut vote_account_data = vec![0; VoteState1_14_11::size_of()];
286 VoteStateV3::serialize(&account_state, &mut vote_account_data).unwrap();
287 assert!(VoteStateVersions::is_correct_size_and_initialized(
288 &vote_account_data
289 ));
290 }
291
292 #[test]
293 fn test_is_correct_size_and_initialized_v4() {
294 let zeros = vec![0u8; VoteStateV4::size_of()];
296 assert!(!VoteStateVersions::is_correct_size_and_initialized(&zeros));
297
298 let versioned = VoteStateVersions::new_v4(VoteStateV4::default());
300 let mut buf = vec![0u8; VoteStateV4::size_of()];
301 VoteStateV4::serialize(&versioned, &mut buf).unwrap();
302 assert!(VoteStateVersions::is_correct_size_and_initialized(&buf));
303
304 let vote_state = VoteStateV4::new_with_defaults(
306 &Pubkey::new_unique(),
307 &VoteInit {
308 node_pubkey: Pubkey::new_unique(),
309 authorized_voter: Pubkey::new_unique(),
310 authorized_withdrawer: Pubkey::new_unique(),
311 commission: 50,
312 },
313 &Clock::default(),
314 );
315 let versioned = VoteStateVersions::new_v4(vote_state);
316 let mut buf = vec![0u8; VoteStateV4::size_of()];
317 VoteStateV4::serialize(&versioned, &mut buf).unwrap();
318 assert!(VoteStateVersions::is_correct_size_and_initialized(&buf));
319
320 assert!(!VoteStateVersions::is_correct_size_and_initialized(
322 &buf[..buf.len() - 1]
323 ));
324 let mut extended = buf.clone();
325 extended.push(0);
326 assert!(!VoteStateVersions::is_correct_size_and_initialized(
327 &extended
328 ));
329 }
330
331 #[test]
332 fn test_vote_state_version_conversion_bls_pubkey() {
333 let vote_pubkey = Pubkey::new_unique();
334
335 let v1_14_11_state = VoteState1_14_11::default();
337 let v1_14_11_versioned = VoteStateVersions::V1_14_11(Box::new(v1_14_11_state));
338
339 let v3_state = VoteStateV3::default();
340 let v3_versioned = VoteStateVersions::V3(Box::new(v3_state));
341
342 for versioned in [v1_14_11_versioned, v3_versioned] {
343 let converted = versioned.try_convert_to_v4(&vote_pubkey).unwrap();
344 assert_eq!(converted.bls_pubkey_compressed, None);
345 }
346
347 let test_bls_key = [128u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE];
349 let v4_state = VoteStateV4 {
350 bls_pubkey_compressed: Some(test_bls_key),
351 ..VoteStateV4::default()
352 };
353 let v4_versioned = VoteStateVersions::V4(Box::new(v4_state));
354 let converted = v4_versioned.try_convert_to_v4(&vote_pubkey).unwrap();
355 assert_eq!(converted.bls_pubkey_compressed, Some(test_bls_key));
356 }
357
358 #[test]
359 fn test_versions_deserialize_invalid_variant_tags() {
360 let mut buf = vec![0u8; VoteStateV3::size_of()];
361
362 assert_eq!(
364 VoteStateVersions::deserialize(&buf),
365 Err(InstructionError::InvalidAccountData)
366 );
367
368 buf[..4].copy_from_slice(&4u32.to_le_bytes());
370 assert_eq!(
371 VoteStateVersions::deserialize(&buf),
372 Err(InstructionError::InvalidAccountData)
373 );
374
375 buf[..4].copy_from_slice(&u32::MAX.to_le_bytes());
377 assert_eq!(
378 VoteStateVersions::deserialize(&buf),
379 Err(InstructionError::InvalidAccountData)
380 );
381 }
382
383 #[test]
384 fn test_versions_deserialize_error_paths() {
385 assert_eq!(
387 VoteStateVersions::deserialize(&[]),
388 Err(InstructionError::InvalidAccountData)
389 );
390
391 assert_eq!(
393 VoteStateVersions::deserialize(&[1, 0, 0]),
394 Err(InstructionError::InvalidAccountData)
395 );
396
397 let vote_state = VoteStateV3::new(
399 &VoteInit {
400 node_pubkey: Pubkey::new_unique(),
401 authorized_voter: Pubkey::new_unique(),
402 authorized_withdrawer: Pubkey::new_unique(),
403 commission: 50,
404 },
405 &Clock::default(),
406 );
407 let mut buf = bincode::serialize(&VoteStateVersions::new_v3(vote_state)).unwrap();
408 buf.truncate(buf.len() / 2);
409 assert_eq!(
410 VoteStateVersions::deserialize(&buf),
411 Err(InstructionError::InvalidAccountData)
412 );
413
414 let mut rng = rand::rng();
416 for _ in 0..100 {
417 let len = rng.random_range(0u64..512);
418 let random_data: Vec<u8> = (0..len).map(|_| rng.random::<u8>()).collect();
419 let _ = VoteStateVersions::deserialize(&random_data);
420 }
421 }
422
423 #[test]
424 fn test_versions_deserialize_default() {
425 let ser_deser = |original: VoteStateVersions| {
426 let serialized = bincode::serialize(&original).unwrap();
427 VoteStateVersions::deserialize(&serialized)
428 };
429
430 let v1_14_11 = VoteStateVersions::V1_14_11(Box::default());
431 assert_eq!(
432 ser_deser(v1_14_11.clone()),
433 Ok(v1_14_11), );
435
436 let v3 = VoteStateVersions::V3(Box::default());
437 assert_eq!(
438 ser_deser(v3.clone()),
439 Ok(v3), );
441
442 let v4 = VoteStateVersions::V4(Box::default());
443 assert_eq!(
444 ser_deser(v4.clone()),
445 Ok(v4), );
447 }
448
449 #[test]
450 fn test_versions_deserialize_non_default() {
451 let vote_init = VoteInit {
452 node_pubkey: Pubkey::new_unique(),
453 authorized_voter: Pubkey::new_unique(),
454 authorized_withdrawer: Pubkey::new_unique(),
455 commission: 50,
456 };
457
458 let ser_deser = |original: VoteStateVersions| {
459 let serialized = bincode::serialize(&original).unwrap();
460 let deserialized = VoteStateVersions::deserialize(&serialized).unwrap();
461 assert_eq!(original, deserialized);
462 };
463
464 let vote_state = VoteState1_14_11::from(VoteStateV3::new(&vote_init, &Clock::default()));
466 ser_deser(VoteStateVersions::V1_14_11(Box::new(vote_state)));
467
468 let vote_state = VoteStateV3::new(&vote_init, &Clock::default());
470 ser_deser(VoteStateVersions::new_v3(vote_state));
471
472 let vote_state =
474 VoteStateV4::new_with_defaults(&Pubkey::new_unique(), &vote_init, &Clock::default());
475 ser_deser(VoteStateVersions::new_v4(vote_state));
476 }
477
478 #[test]
479 fn test_versions_deserialize_arbitrary() {
480 let struct_bytes_x4 = std::mem::size_of::<VoteStateV4>() * 4;
481 for _ in 0..1000 {
482 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
483 let mut unstructured = Unstructured::new(&raw_data);
484 let original = VoteStateVersions::arbitrary(&mut unstructured).unwrap();
485 let serialized = bincode::serialize(&original).unwrap();
486 let deserialized = VoteStateVersions::deserialize(&serialized).unwrap();
487 assert_eq!(original, deserialized);
488 }
489 }
490
491 #[test]
492 fn test_collection_count_exceeding_max() {
493 let mut vote_state = VoteStateV3::default();
498 for i in 0..100u64 {
500 vote_state.votes.push_back(LandedVote {
501 latency: 0,
502 lockout: Lockout::new(i),
503 });
504 }
505 for i in 0..100u64 {
507 vote_state.epoch_credits.push((i, i * 10, i * 5));
508 }
509 let versioned = VoteStateVersions::new_v3(vote_state);
510 let serialized = bincode::serialize(&versioned).unwrap();
511 let deserialized = VoteStateVersions::deserialize(&serialized).unwrap();
512 assert_eq!(versioned, deserialized);
513 }
514}