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 };
221
222 #[test]
223 fn test_v3_default_vote_state_is_uninitialized() {
224 assert!(VoteStateVersions::new_v3(VoteStateV3::default()).is_uninitialized());
228 }
229
230 #[test]
231 fn test_v4_default_vote_state_is_always_initialized() {
232 assert!(!VoteStateVersions::new_v4(VoteStateV4::default()).is_uninitialized());
234 }
235
236 #[test]
237 fn test_is_correct_size_and_initialized_v3() {
238 let mut vote_account_data = vec![0; VoteStateV3::size_of()];
240 assert!(!VoteStateVersions::is_correct_size_and_initialized(
241 &vote_account_data
242 ));
243
244 let default_account_state = VoteStateVersions::new_v3(VoteStateV3::default());
246 VoteStateV3::serialize(&default_account_state, &mut vote_account_data).unwrap();
247 assert!(!VoteStateVersions::is_correct_size_and_initialized(
248 &vote_account_data
249 ));
250
251 let short_data = vec![1; DEFAULT_PRIOR_VOTERS_OFFSET];
253 assert!(!VoteStateVersions::is_correct_size_and_initialized(
254 &short_data
255 ));
256
257 let mut large_vote_data = vec![1; 2 * VoteStateV3::size_of()];
259 let default_account_state = VoteStateVersions::new_v3(VoteStateV3::default());
260 VoteStateV3::serialize(&default_account_state, &mut large_vote_data).unwrap();
261 assert!(!VoteStateVersions::is_correct_size_and_initialized(
262 &vote_account_data
263 ));
264
265 let vote_state = VoteStateV3::new(
267 &VoteInit {
268 node_pubkey: Pubkey::new_unique(),
269 authorized_voter: Pubkey::new_unique(),
270 authorized_withdrawer: Pubkey::new_unique(),
271 commission: 0,
272 },
273 &Clock::default(),
274 );
275 let account_state = VoteStateVersions::new_v3(vote_state.clone());
276 VoteStateV3::serialize(&account_state, &mut vote_account_data).unwrap();
277 assert!(VoteStateVersions::is_correct_size_and_initialized(
278 &vote_account_data
279 ));
280
281 let old_vote_state = VoteState1_14_11::from(vote_state);
283 let account_state = VoteStateVersions::V1_14_11(Box::new(old_vote_state));
284 let mut vote_account_data = vec![0; VoteState1_14_11::size_of()];
285 VoteStateV3::serialize(&account_state, &mut vote_account_data).unwrap();
286 assert!(VoteStateVersions::is_correct_size_and_initialized(
287 &vote_account_data
288 ));
289 }
290
291 #[test]
292 fn test_is_correct_size_and_initialized_v4() {
293 let zeros = vec![0u8; VoteStateV4::size_of()];
295 assert!(!VoteStateVersions::is_correct_size_and_initialized(&zeros));
296
297 let versioned = VoteStateVersions::new_v4(VoteStateV4::default());
299 let mut buf = vec![0u8; VoteStateV4::size_of()];
300 VoteStateV4::serialize(&versioned, &mut buf).unwrap();
301 assert!(VoteStateVersions::is_correct_size_and_initialized(&buf));
302
303 let vote_state = VoteStateV4::new_with_defaults(
305 &Pubkey::new_unique(),
306 &VoteInit {
307 node_pubkey: Pubkey::new_unique(),
308 authorized_voter: Pubkey::new_unique(),
309 authorized_withdrawer: Pubkey::new_unique(),
310 commission: 50,
311 },
312 &Clock::default(),
313 );
314 let versioned = VoteStateVersions::new_v4(vote_state);
315 let mut buf = vec![0u8; VoteStateV4::size_of()];
316 VoteStateV4::serialize(&versioned, &mut buf).unwrap();
317 assert!(VoteStateVersions::is_correct_size_and_initialized(&buf));
318
319 assert!(!VoteStateVersions::is_correct_size_and_initialized(
321 &buf[..buf.len() - 1]
322 ));
323 let mut extended = buf.clone();
324 extended.push(0);
325 assert!(!VoteStateVersions::is_correct_size_and_initialized(
326 &extended
327 ));
328 }
329
330 #[test]
331 fn test_vote_state_version_conversion_bls_pubkey() {
332 let vote_pubkey = Pubkey::new_unique();
333
334 let v1_14_11_state = VoteState1_14_11::default();
336 let v1_14_11_versioned = VoteStateVersions::V1_14_11(Box::new(v1_14_11_state));
337
338 let v3_state = VoteStateV3::default();
339 let v3_versioned = VoteStateVersions::V3(Box::new(v3_state));
340
341 for versioned in [v1_14_11_versioned, v3_versioned] {
342 let converted = versioned.try_convert_to_v4(&vote_pubkey).unwrap();
343 assert_eq!(converted.bls_pubkey_compressed, None);
344 }
345
346 let test_bls_key = [128u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE];
348 let v4_state = VoteStateV4 {
349 bls_pubkey_compressed: Some(test_bls_key),
350 ..VoteStateV4::default()
351 };
352 let v4_versioned = VoteStateVersions::V4(Box::new(v4_state));
353 let converted = v4_versioned.try_convert_to_v4(&vote_pubkey).unwrap();
354 assert_eq!(converted.bls_pubkey_compressed, Some(test_bls_key));
355 }
356
357 #[test]
358 fn test_versions_deserialize_invalid_variant_tags() {
359 let mut buf = vec![0u8; VoteStateV3::size_of()];
360
361 assert_eq!(
363 VoteStateVersions::deserialize(&buf),
364 Err(InstructionError::InvalidAccountData)
365 );
366
367 buf[..4].copy_from_slice(&4u32.to_le_bytes());
369 assert_eq!(
370 VoteStateVersions::deserialize(&buf),
371 Err(InstructionError::InvalidAccountData)
372 );
373
374 buf[..4].copy_from_slice(&u32::MAX.to_le_bytes());
376 assert_eq!(
377 VoteStateVersions::deserialize(&buf),
378 Err(InstructionError::InvalidAccountData)
379 );
380 }
381
382 #[test]
383 fn test_versions_deserialize_error_paths() {
384 assert_eq!(
386 VoteStateVersions::deserialize(&[]),
387 Err(InstructionError::InvalidAccountData)
388 );
389
390 assert_eq!(
392 VoteStateVersions::deserialize(&[1, 0, 0]),
393 Err(InstructionError::InvalidAccountData)
394 );
395
396 let vote_state = VoteStateV3::new(
398 &VoteInit {
399 node_pubkey: Pubkey::new_unique(),
400 authorized_voter: Pubkey::new_unique(),
401 authorized_withdrawer: Pubkey::new_unique(),
402 commission: 50,
403 },
404 &Clock::default(),
405 );
406 let mut buf = bincode::serialize(&VoteStateVersions::new_v3(vote_state)).unwrap();
407 buf.truncate(buf.len() / 2);
408 assert_eq!(
409 VoteStateVersions::deserialize(&buf),
410 Err(InstructionError::InvalidAccountData)
411 );
412
413 let mut rng = rand::rng();
415 for _ in 0..100 {
416 let len = rng.random_range(0u64..512);
417 let random_data: Vec<u8> = (0..len).map(|_| rng.random::<u8>()).collect();
418 let _ = VoteStateVersions::deserialize(&random_data);
419 }
420 }
421
422 #[test]
423 fn test_versions_deserialize_default() {
424 let ser_deser = |original: VoteStateVersions| {
425 let serialized = bincode::serialize(&original).unwrap();
426 VoteStateVersions::deserialize(&serialized)
427 };
428
429 let v1_14_11 = VoteStateVersions::V1_14_11(Box::default());
430 assert_eq!(
431 ser_deser(v1_14_11.clone()),
432 Ok(v1_14_11), );
434
435 let v3 = VoteStateVersions::V3(Box::default());
436 assert_eq!(
437 ser_deser(v3.clone()),
438 Ok(v3), );
440
441 let v4 = VoteStateVersions::V4(Box::default());
442 assert_eq!(
443 ser_deser(v4.clone()),
444 Ok(v4), );
446 }
447
448 #[test]
449 fn test_versions_deserialize_non_default() {
450 let vote_init = VoteInit {
451 node_pubkey: Pubkey::new_unique(),
452 authorized_voter: Pubkey::new_unique(),
453 authorized_withdrawer: Pubkey::new_unique(),
454 commission: 50,
455 };
456
457 let ser_deser = |original: VoteStateVersions| {
458 let serialized = bincode::serialize(&original).unwrap();
459 let deserialized = VoteStateVersions::deserialize(&serialized).unwrap();
460 assert_eq!(original, deserialized);
461 };
462
463 let vote_state = VoteState1_14_11::from(VoteStateV3::new(&vote_init, &Clock::default()));
465 ser_deser(VoteStateVersions::V1_14_11(Box::new(vote_state)));
466
467 let vote_state = VoteStateV3::new(&vote_init, &Clock::default());
469 ser_deser(VoteStateVersions::new_v3(vote_state));
470
471 let vote_state =
473 VoteStateV4::new_with_defaults(&Pubkey::new_unique(), &vote_init, &Clock::default());
474 ser_deser(VoteStateVersions::new_v4(vote_state));
475 }
476
477 #[test]
478 fn test_versions_deserialize_arbitrary() {
479 let struct_bytes_x4 = std::mem::size_of::<VoteStateV4>() * 4;
480 for _ in 0..1000 {
481 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
482 let mut unstructured = Unstructured::new(&raw_data);
483 let original = VoteStateVersions::arbitrary(&mut unstructured).unwrap();
484 let serialized = bincode::serialize(&original).unwrap();
485 let deserialized = VoteStateVersions::deserialize(&serialized).unwrap();
486 assert_eq!(original, deserialized);
487 }
488 }
489
490 #[test]
491 fn test_collection_count_exceeding_max() {
492 let mut vote_state = VoteStateV3::default();
497 for i in 0..100u64 {
499 vote_state.votes.push_back(LandedVote {
500 latency: 0,
501 lockout: Lockout::new(i),
502 });
503 }
504 for i in 0..100u64 {
506 vote_state.epoch_credits.push((i, i * 10, i * 5));
507 }
508 let versioned = VoteStateVersions::new_v3(vote_state);
509 let serialized = bincode::serialize(&versioned).unwrap();
510 let deserialized = VoteStateVersions::deserialize(&serialized).unwrap();
511 assert_eq!(versioned, deserialized);
512 }
513}