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