1#[cfg(feature = "bincode")]
2use super::VoteStateVersions;
3#[cfg(test)]
4use super::{MAX_EPOCH_CREDITS_HISTORY, MAX_LOCKOUT_HISTORY};
5#[cfg(feature = "dev-context-only-utils")]
6use arbitrary::Arbitrary;
7#[cfg(feature = "serde")]
8use serde_derive::{Deserialize, Serialize};
9#[cfg(feature = "frozen-abi")]
10use solana_frozen_abi_macro::{frozen_abi, AbiExample, StableAbi, StableAbiSample};
11#[cfg(any(target_os = "solana", feature = "bincode"))]
12use solana_instruction_error::InstructionError;
13use {
14 super::{BlockTimestamp, CircBuf, LandedVote, Lockout, VoteInit},
15 crate::{authorized_voters::AuthorizedVoters, state::DEFAULT_PRIOR_VOTERS_OFFSET},
16 solana_clock::{Clock, Epoch, Slot},
17 solana_pubkey::Pubkey,
18 std::{collections::VecDeque, fmt::Debug},
19};
20
21#[cfg_attr(
22 feature = "frozen-abi",
23 frozen_abi(
24 api_digest = "pZqasQc6duzMYzpzU7eriHH9cMXmubuUP4NmCrkWZjt",
25 abi_digest = "4VUwurjnJ96aMgYXaAmkDut56mMkC4uo6b5bm8iH7WzJ",
26 abi_serializer = ["bincode", "wincode"]
27 ),
28 derive(AbiExample, StableAbi, StableAbiSample)
29)]
30#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
31#[cfg_attr(feature = "wincode", derive(wincode::SchemaWrite, wincode::SchemaRead))]
32#[derive(Debug, Default, PartialEq, Eq, Clone)]
33#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
34pub struct VoteStateV3 {
35 pub node_pubkey: Pubkey,
37
38 pub authorized_withdrawer: Pubkey,
40 pub commission: u8,
43
44 pub votes: VecDeque<LandedVote>,
45
46 pub root_slot: Option<Slot>,
49
50 pub authorized_voters: AuthorizedVoters,
52
53 pub prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>,
57
58 pub epoch_credits: Vec<(Epoch, u64, u64)>,
61
62 pub last_timestamp: BlockTimestamp,
64}
65
66impl VoteStateV3 {
67 pub fn new(vote_init: &VoteInit, clock: &Clock) -> Self {
68 Self {
69 node_pubkey: vote_init.node_pubkey,
70 authorized_voters: AuthorizedVoters::new(clock.epoch, vote_init.authorized_voter),
71 authorized_withdrawer: vote_init.authorized_withdrawer,
72 commission: vote_init.commission,
73 ..VoteStateV3::default()
74 }
75 }
76
77 pub fn new_rand_for_tests(node_pubkey: Pubkey, root_slot: Slot) -> Self {
78 let votes = (1..32)
79 .map(|x| LandedVote {
80 latency: 0,
81 lockout: Lockout::new_with_confirmation_count(
82 u64::from(x).saturating_add(root_slot),
83 32_u32.saturating_sub(x),
84 ),
85 })
86 .collect();
87 Self {
88 node_pubkey,
89 root_slot: Some(root_slot),
90 votes,
91 ..VoteStateV3::default()
92 }
93 }
94
95 pub const fn size_of() -> usize {
98 3762 }
100
101 pub fn is_uninitialized(&self) -> bool {
102 self.authorized_voters.is_empty()
103 }
104
105 #[cfg(any(target_os = "solana", feature = "bincode"))]
106 pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
107 let mut vote_state = Self::default();
108 Self::deserialize_into(input, &mut vote_state)?;
109 Ok(vote_state)
110 }
111
112 #[cfg(any(target_os = "solana", feature = "bincode"))]
119 pub fn deserialize_into(
120 input: &[u8],
121 vote_state: &mut VoteStateV3,
122 ) -> Result<(), InstructionError> {
123 use super::vote_state_deserialize;
124 vote_state_deserialize::deserialize_into(input, vote_state, Self::deserialize_into_ptr)
125 }
126
127 #[cfg(any(target_os = "solana", feature = "bincode"))]
138 pub fn deserialize_into_uninit(
139 input: &[u8],
140 vote_state: &mut std::mem::MaybeUninit<VoteStateV3>,
141 ) -> Result<(), InstructionError> {
142 VoteStateV3::deserialize_into_ptr(input, vote_state.as_mut_ptr())
143 }
144
145 #[cfg(any(target_os = "solana", feature = "bincode"))]
146 fn deserialize_into_ptr(
147 input: &[u8],
148 vote_state: *mut VoteStateV3,
149 ) -> Result<(), InstructionError> {
150 use super::vote_state_deserialize::deserialize_vote_state_into_v3;
151
152 let mut cursor = std::io::Cursor::new(input);
153
154 let variant = solana_serialize_utils::cursor::read_u32(&mut cursor)?;
155 match variant {
156 0 => Err(InstructionError::InvalidAccountData),
158 1 => deserialize_vote_state_into_v3(&mut cursor, vote_state, false),
160 2 => deserialize_vote_state_into_v3(&mut cursor, vote_state, true),
162 _ => Err(InstructionError::InvalidAccountData),
163 }?;
164
165 Ok(())
166 }
167
168 #[cfg(feature = "bincode")]
169 pub fn serialize(
170 versioned: &VoteStateVersions,
171 output: &mut [u8],
172 ) -> Result<(), InstructionError> {
173 bincode::serialize_into(output, versioned).map_err(|err| match *err {
174 bincode::ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
175 _ => InstructionError::GenericError,
176 })
177 }
178
179 #[cfg(test)]
180 pub(crate) fn get_max_sized_vote_state() -> VoteStateV3 {
181 use solana_epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET;
182 let mut authorized_voters = AuthorizedVoters::default();
183 for i in 0..=MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
184 authorized_voters.insert(i, Pubkey::new_unique());
185 }
186
187 VoteStateV3 {
188 votes: VecDeque::from(vec![LandedVote::default(); MAX_LOCKOUT_HISTORY]),
189 root_slot: Some(u64::MAX),
190 epoch_credits: vec![(0, 0, 0); MAX_EPOCH_CREDITS_HISTORY],
191 authorized_voters,
192 ..Self::default()
193 }
194 }
195
196 pub fn current_epoch(&self) -> Epoch {
197 self.epoch_credits.last().map_or(0, |v| v.0)
198 }
199
200 pub fn credits(&self) -> u64 {
203 self.epoch_credits.last().map_or(0, |v| v.1)
204 }
205
206 pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
207 const VERSION_OFFSET: usize = 4;
208 const DEFAULT_PRIOR_VOTERS_END: usize = VERSION_OFFSET + DEFAULT_PRIOR_VOTERS_OFFSET;
209 data.len() == VoteStateV3::size_of()
210 && data[VERSION_OFFSET..DEFAULT_PRIOR_VOTERS_END] != [0; DEFAULT_PRIOR_VOTERS_OFFSET]
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use {
217 super::{
218 super::{VoteState1_14_11, VoteStateVersions, MAX_LOCKOUT_HISTORY},
219 *,
220 },
221 arbitrary::Unstructured,
222 bincode::serialized_size,
223 core::mem::MaybeUninit,
224 rand::Rng,
225 };
226
227 #[test]
228 fn test_size_of() {
229 let vote_state = VoteStateV3::get_max_sized_vote_state();
230 let vote_state = VoteStateVersions::new_v3(vote_state);
231 let size = serialized_size(&vote_state).unwrap();
232 assert_eq!(VoteStateV3::size_of() as u64, size);
233 }
234
235 #[test]
236 fn test_minimum_balance() {
237 let rent = solana_rent::Rent::default();
238 let minimum_balance = rent.minimum_balance(VoteStateV3::size_of());
239 assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
241 }
242
243 #[test]
244 fn test_vote_serialize() {
245 let mut buffer: Vec<u8> = vec![0; VoteStateV3::size_of()];
246 let mut vote_state = VoteStateV3::default();
247 vote_state
248 .votes
249 .resize(MAX_LOCKOUT_HISTORY, LandedVote::default());
250 vote_state.root_slot = Some(1);
251 let versioned = VoteStateVersions::new_v3(vote_state);
252 assert!(VoteStateV3::serialize(&versioned, &mut buffer[0..4]).is_err());
253 VoteStateV3::serialize(&versioned, &mut buffer).unwrap();
254 assert_eq!(
255 VoteStateV3::deserialize(&buffer).unwrap(),
256 versioned.try_convert_to_v3().unwrap()
257 );
258 }
259
260 #[test]
261 fn test_vote_deserialize_into() {
262 let target_vote_state = VoteStateV3::default();
264 let vote_state_buf =
265 bincode::serialize(&VoteStateVersions::new_v3(target_vote_state.clone())).unwrap();
266
267 let mut test_vote_state = VoteStateV3::default();
268 VoteStateV3::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
269
270 assert_eq!(target_vote_state, test_vote_state);
271
272 let struct_bytes_x4 = std::mem::size_of::<VoteStateV3>() * 4;
275 for _ in 0..1000 {
276 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
277 let mut unstructured = Unstructured::new(&raw_data);
278
279 let target_vote_state_versions =
280 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
281 let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
282
283 if let Ok(target_vote_state) = target_vote_state_versions.try_convert_to_v3() {
285 let mut test_vote_state = VoteStateV3::default();
286 VoteStateV3::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
287
288 assert_eq!(target_vote_state, test_vote_state);
289 }
290 }
291 }
292
293 #[test]
294 fn test_vote_deserialize_into_trailing_data() {
295 let target_vote_state = VoteStateV3::new_rand_for_tests(Pubkey::new_unique(), 42);
296 let vote_state_buf =
297 bincode::serialize(&VoteStateVersions::new_v3(target_vote_state.clone())).unwrap();
298
299 let mut buf_with_garbage = vote_state_buf.clone();
301 buf_with_garbage.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
302 let mut test_vote_state = VoteStateV3::default();
303 VoteStateV3::deserialize_into(&buf_with_garbage, &mut test_vote_state).unwrap();
304 assert_eq!(target_vote_state, test_vote_state);
305
306 let mut buf_with_zeroes = vote_state_buf;
308 buf_with_zeroes.extend_from_slice(&[0u8; 64]);
309 let mut test_vote_state = VoteStateV3::default();
310 VoteStateV3::deserialize_into(&buf_with_zeroes, &mut test_vote_state).unwrap();
311 assert_eq!(target_vote_state, test_vote_state);
312 }
313
314 #[test]
315 fn test_vote_deserialize_into_error() {
316 let target_vote_state = VoteStateV3::new_rand_for_tests(Pubkey::new_unique(), 42);
317 let mut vote_state_buf =
318 bincode::serialize(&VoteStateVersions::new_v3(target_vote_state.clone())).unwrap();
319 let len = vote_state_buf.len();
320 vote_state_buf.truncate(len - 1);
321
322 let mut test_vote_state = VoteStateV3::default();
323 VoteStateV3::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap_err();
324 assert_eq!(test_vote_state, VoteStateV3::default());
325 }
326
327 #[test]
328 fn test_vote_deserialize_into_error_with_pre_state() {
329 let mut vote_state = VoteStateV3::new_rand_for_tests(Pubkey::new_unique(), 42);
331 vote_state.epoch_credits = vec![(0, 100, 0), (1, 200, 100), (2, 300, 200)];
332
333 let mut buf =
335 bincode::serialize(&VoteStateVersions::new_v3(VoteStateV3::default())).unwrap();
336 buf.truncate(buf.len() - 1);
337
338 VoteStateV3::deserialize_into(&buf, &mut vote_state).unwrap_err();
339 assert_eq!(vote_state, VoteStateV3::default());
341 }
342
343 #[test]
344 fn test_deserialize_into_uninit_no_reset_on_error() {
345 let target = VoteStateV3::new_rand_for_tests(Pubkey::new_unique(), 42);
350 let mut buf = bincode::serialize(&VoteStateVersions::new_v3(target)).unwrap();
351 buf.truncate(buf.len() - 1);
352
353 let mut test_vote_state = MaybeUninit::uninit();
354 let err = VoteStateV3::deserialize_into_uninit(&buf, &mut test_vote_state);
355 assert_eq!(err, Err(InstructionError::InvalidAccountData));
356 }
358
359 #[test]
360 fn test_vote_deserialize_into_uninit() {
361 let target_vote_state = VoteStateV3::default();
363 let vote_state_buf =
364 bincode::serialize(&VoteStateVersions::new_v3(target_vote_state.clone())).unwrap();
365
366 let mut test_vote_state = MaybeUninit::uninit();
367 VoteStateV3::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state).unwrap();
368 let test_vote_state = unsafe { test_vote_state.assume_init() };
369
370 assert_eq!(target_vote_state, test_vote_state);
371
372 let struct_bytes_x4 = std::mem::size_of::<VoteStateV3>() * 4;
375 for _ in 0..1000 {
376 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
377 let mut unstructured = Unstructured::new(&raw_data);
378
379 let target_vote_state_versions =
380 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
381 let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
382
383 if let Ok(target_vote_state) = target_vote_state_versions.try_convert_to_v3() {
385 let mut test_vote_state = MaybeUninit::uninit();
386 VoteStateV3::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state)
387 .unwrap();
388 let test_vote_state = unsafe { test_vote_state.assume_init() };
389
390 assert_eq!(target_vote_state, test_vote_state);
391 }
392 }
393 }
394
395 #[test]
396 fn test_vote_deserialize_into_uninit_trailing_data() {
397 let target_vote_state = VoteStateV3::new_rand_for_tests(Pubkey::new_unique(), 42);
398 let vote_state_buf =
399 bincode::serialize(&VoteStateVersions::new_v3(target_vote_state.clone())).unwrap();
400
401 let mut buf_with_garbage = vote_state_buf.clone();
403 buf_with_garbage.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
404 let mut test_vote_state = MaybeUninit::uninit();
405 VoteStateV3::deserialize_into_uninit(&buf_with_garbage, &mut test_vote_state).unwrap();
406 let test_vote_state = unsafe { test_vote_state.assume_init() };
407 assert_eq!(target_vote_state, test_vote_state);
408
409 let mut buf_with_zeroes = vote_state_buf;
411 buf_with_zeroes.extend_from_slice(&[0u8; 64]);
412 let mut test_vote_state = MaybeUninit::uninit();
413 VoteStateV3::deserialize_into_uninit(&buf_with_zeroes, &mut test_vote_state).unwrap();
414 let test_vote_state = unsafe { test_vote_state.assume_init() };
415 assert_eq!(target_vote_state, test_vote_state);
416 }
417
418 #[test]
419 fn test_vote_deserialize_into_uninit_nopanic() {
420 let mut test_vote_state = MaybeUninit::uninit();
422 let e = VoteStateV3::deserialize_into_uninit(&[], &mut test_vote_state).unwrap_err();
423 assert_eq!(e, InstructionError::InvalidAccountData);
424
425 let serialized_len_x4 = serialized_size(&VoteStateV3::default()).unwrap() * 4;
427 let mut rng = rand::rng();
428 for _ in 0..1000 {
429 let raw_data_length = rng.random_range(1..serialized_len_x4);
430 let mut raw_data: Vec<u8> = (0..raw_data_length).map(|_| rng.random::<u8>()).collect();
431
432 if raw_data_length >= 4 && rng.random::<bool>() {
434 let tag = rng.random_range(1u8..=3);
435 raw_data[0] = tag;
436 raw_data[1] = 0;
437 raw_data[2] = 0;
438 raw_data[3] = 0;
439 }
440
441 let mut test_vote_state = MaybeUninit::uninit();
444 let test_res = VoteStateV3::deserialize_into_uninit(&raw_data, &mut test_vote_state);
445
446 let bincode_res = bincode::deserialize::<VoteStateVersions>(&raw_data)
448 .map_err(|_| InstructionError::InvalidAccountData)
449 .and_then(|versioned| versioned.try_convert_to_v3());
450
451 if test_res.is_err() {
452 assert!(bincode_res.is_err());
453 } else {
454 let test_vote_state = unsafe { test_vote_state.assume_init() };
455 assert_eq!(test_vote_state, bincode_res.unwrap());
456 }
457 }
458 }
459
460 #[test]
461 fn test_vote_deserialize_into_uninit_ill_sized() {
462 let struct_bytes_x4 = std::mem::size_of::<VoteStateV3>() * 4;
464 for _ in 0..1000 {
465 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
466 let mut unstructured = Unstructured::new(&raw_data);
467
468 let original_vote_state_versions =
469 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
470 let original_buf = bincode::serialize(&original_vote_state_versions).unwrap();
471
472 if !matches!(original_vote_state_versions, VoteStateVersions::V4(_)) {
474 let mut truncated_buf = original_buf.clone();
475 let mut expanded_buf = original_buf.clone();
476
477 truncated_buf.resize(original_buf.len() - 8, 0);
478 expanded_buf.resize(original_buf.len() + 8, 0);
479
480 let mut test_vote_state = MaybeUninit::uninit();
482 let test_res =
483 VoteStateV3::deserialize_into_uninit(&truncated_buf, &mut test_vote_state);
484 let bincode_res = bincode::deserialize::<VoteStateVersions>(&truncated_buf)
487 .map_err(|_| InstructionError::InvalidAccountData)
488 .and_then(|versioned| versioned.try_convert_to_v3());
489
490 assert!(test_res.is_err());
491 assert!(bincode_res.is_err());
492
493 let mut test_vote_state = MaybeUninit::uninit();
495 VoteStateV3::deserialize_into_uninit(&expanded_buf, &mut test_vote_state).unwrap();
496 let bincode_res = bincode::deserialize::<VoteStateVersions>(&expanded_buf)
499 .map_err(|_| InstructionError::InvalidAccountData)
500 .and_then(|versioned| versioned.try_convert_to_v3());
501
502 let test_vote_state = unsafe { test_vote_state.assume_init() };
503 assert_eq!(test_vote_state, bincode_res.unwrap());
504 }
505 }
506 }
507
508 #[test]
509 fn test_deserialize_invalid_variant_tags() {
510 let mut buf = vec![0u8; VoteStateV3::size_of()];
511
512 let mut vs = VoteStateV3::default();
514 assert_eq!(
515 VoteStateV3::deserialize_into(&buf, &mut vs),
516 Err(InstructionError::InvalidAccountData)
517 );
518
519 buf[..4].copy_from_slice(&3u32.to_le_bytes());
521 assert_eq!(
522 VoteStateV3::deserialize_into(&buf, &mut vs),
523 Err(InstructionError::InvalidAccountData)
524 );
525
526 buf[..4].copy_from_slice(&4u32.to_le_bytes());
528 assert_eq!(
529 VoteStateV3::deserialize_into(&buf, &mut vs),
530 Err(InstructionError::InvalidAccountData)
531 );
532
533 buf[..4].copy_from_slice(&u32::MAX.to_le_bytes());
535 assert_eq!(
536 VoteStateV3::deserialize_into(&buf, &mut vs),
537 Err(InstructionError::InvalidAccountData)
538 );
539 }
540
541 #[test]
542 fn test_invalid_option_bool_discriminants() {
543 let vote_state = VoteStateV3 {
544 root_slot: Some(42),
545 ..VoteStateV3::default()
546 };
547 let valid_buf = bincode::serialize(&VoteStateVersions::new_v3(vote_state)).unwrap();
548
549 let root_slot_offset = 4 + 32 + 32 + 1 + 8;
553 assert_eq!(valid_buf[root_slot_offset], 1); {
556 let mut buf = valid_buf.clone();
557 buf[root_slot_offset] = 2;
558 let mut vs = VoteStateV3::default();
559 assert_eq!(
560 VoteStateV3::deserialize_into(&buf, &mut vs),
561 Err(InstructionError::InvalidAccountData)
562 );
563 }
564
565 let is_empty_offset = root_slot_offset + 1 + 8 + 8 + 32 * 48 + 8;
569 assert_eq!(valid_buf[is_empty_offset], 1); {
572 let mut buf = valid_buf.clone();
573 buf[is_empty_offset] = 2;
574 let mut vs = VoteStateV3::default();
575 assert_eq!(
576 VoteStateV3::deserialize_into(&buf, &mut vs),
577 Err(InstructionError::InvalidAccountData)
578 );
579 }
580 }
581
582 #[test]
583 fn test_has_latency() {
584 let mut v1_state = VoteState1_14_11::default();
586 v1_state.votes.push_back(Lockout::new(100));
587 v1_state.votes.push_back(Lockout::new(200));
588 let buf = bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(v1_state))).unwrap();
589 let deserialized = VoteStateV3::deserialize(&buf).unwrap();
590 assert_eq!(deserialized.votes.len(), 2);
591 for vote in &deserialized.votes {
592 assert_eq!(vote.latency, 0);
593 }
594
595 let mut v3_state = VoteStateV3::default();
597 v3_state.votes.push_back(LandedVote {
598 latency: 42,
599 lockout: Lockout::new(100),
600 });
601 v3_state.votes.push_back(LandedVote {
602 latency: 7,
603 lockout: Lockout::new(200),
604 });
605 let buf = bincode::serialize(&VoteStateVersions::new_v3(v3_state)).unwrap();
606 let deserialized = VoteStateV3::deserialize(&buf).unwrap();
607 assert_eq!(deserialized.votes[0].latency, 42);
608 assert_eq!(deserialized.votes[1].latency, 7);
609 }
610
611 #[test]
612 fn test_empty_collections_round_trip() {
613 let vote_state = VoteStateV3 {
615 node_pubkey: Pubkey::new_unique(),
616 authorized_withdrawer: Pubkey::new_unique(),
617 commission: 50,
618 root_slot: Some(100),
619 ..VoteStateV3::default()
620 };
621 let versioned = VoteStateVersions::new_v3(vote_state.clone());
622 let buf = bincode::serialize(&versioned).unwrap();
623 let deserialized = VoteStateV3::deserialize(&buf).unwrap();
624 assert_eq!(vote_state, deserialized);
625 }
626}