1#[cfg(feature = "bincode")]
2use super::VoteStateVersions;
3#[cfg(feature = "dev-context-only-utils")]
4use arbitrary::Arbitrary;
5#[cfg(feature = "serde")]
6use serde_derive::{Deserialize, Serialize};
7#[cfg(feature = "frozen-abi")]
8use solana_frozen_abi_macro::{frozen_abi, AbiExample};
9use {
10 super::{
11 BlockTimestamp, CircBuf, LandedVote, Lockout, VoteInit, MAX_EPOCH_CREDITS_HISTORY,
12 MAX_LOCKOUT_HISTORY, VOTE_CREDITS_GRACE_SLOTS, VOTE_CREDITS_MAXIMUM_PER_SLOT,
13 },
14 crate::{
15 authorized_voters::AuthorizedVoters, error::VoteError, state::DEFAULT_PRIOR_VOTERS_OFFSET,
16 },
17 solana_clock::{Clock, Epoch, Slot, UnixTimestamp},
18 solana_instruction::error::InstructionError,
19 solana_pubkey::Pubkey,
20 solana_rent::Rent,
21 std::{collections::VecDeque, fmt::Debug},
22};
23
24#[cfg_attr(
25 feature = "frozen-abi",
26 frozen_abi(digest = "BRwozbypfYXsHqFVj9w3iH5x1ak2NWHqCCn6pr3gHBkG"),
27 derive(AbiExample)
28)]
29#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
30#[derive(Debug, Default, PartialEq, Eq, Clone)]
31#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
32pub struct VoteState {
33 pub node_pubkey: Pubkey,
35
36 pub authorized_withdrawer: Pubkey,
38 pub commission: u8,
41
42 pub votes: VecDeque<LandedVote>,
43
44 pub root_slot: Option<Slot>,
47
48 pub authorized_voters: AuthorizedVoters,
50
51 pub prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>,
55
56 pub epoch_credits: Vec<(Epoch, u64, u64)>,
59
60 pub last_timestamp: BlockTimestamp,
62}
63
64impl VoteState {
65 pub fn new(vote_init: &VoteInit, clock: &Clock) -> Self {
66 Self {
67 node_pubkey: vote_init.node_pubkey,
68 authorized_voters: AuthorizedVoters::new(clock.epoch, vote_init.authorized_voter),
69 authorized_withdrawer: vote_init.authorized_withdrawer,
70 commission: vote_init.commission,
71 ..VoteState::default()
72 }
73 }
74
75 pub fn new_rand_for_tests(node_pubkey: Pubkey, root_slot: Slot) -> Self {
76 let votes = (1..32)
77 .map(|x| LandedVote {
78 latency: 0,
79 lockout: Lockout::new_with_confirmation_count(
80 u64::from(x).saturating_add(root_slot),
81 32_u32.saturating_sub(x),
82 ),
83 })
84 .collect();
85 Self {
86 node_pubkey,
87 root_slot: Some(root_slot),
88 votes,
89 ..VoteState::default()
90 }
91 }
92
93 pub fn get_authorized_voter(&self, epoch: Epoch) -> Option<Pubkey> {
94 self.authorized_voters.get_authorized_voter(epoch)
95 }
96
97 pub fn authorized_voters(&self) -> &AuthorizedVoters {
98 &self.authorized_voters
99 }
100
101 pub fn prior_voters(&mut self) -> &CircBuf<(Pubkey, Epoch, Epoch)> {
102 &self.prior_voters
103 }
104
105 pub fn get_rent_exempt_reserve(rent: &Rent) -> u64 {
106 rent.minimum_balance(VoteState::size_of())
107 }
108
109 pub const fn size_of() -> usize {
112 3762 }
114
115 #[cfg(any(target_os = "solana", feature = "bincode"))]
121 pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
122 #[cfg(not(target_os = "solana"))]
123 {
124 bincode::deserialize::<VoteStateVersions>(input)
125 .map(|versioned| versioned.convert_to_current())
126 .map_err(|_| InstructionError::InvalidAccountData)
127 }
128 #[cfg(target_os = "solana")]
129 {
130 let mut vote_state = Self::default();
131 Self::deserialize_into(input, &mut vote_state)?;
132 Ok(vote_state)
133 }
134 }
135
136 #[cfg(any(target_os = "solana", feature = "bincode"))]
144 pub fn deserialize_into(
145 input: &[u8],
146 vote_state: &mut VoteState,
147 ) -> Result<(), InstructionError> {
148 let vote_state = vote_state as *mut VoteState;
153
154 unsafe {
158 std::ptr::drop_in_place(vote_state);
159 }
160
161 struct DropGuard {
163 vote_state: *mut VoteState,
164 }
165
166 impl Drop for DropGuard {
167 fn drop(&mut self) {
168 unsafe {
178 self.vote_state.write(VoteState::default());
179 }
180 }
181 }
182
183 let guard = DropGuard { vote_state };
184
185 let res = VoteState::deserialize_into_ptr(input, vote_state);
186 if res.is_ok() {
187 std::mem::forget(guard);
188 }
189
190 res
191 }
192
193 #[cfg(any(target_os = "solana", feature = "bincode"))]
203 pub fn deserialize_into_uninit(
204 input: &[u8],
205 vote_state: &mut std::mem::MaybeUninit<VoteState>,
206 ) -> Result<(), InstructionError> {
207 VoteState::deserialize_into_ptr(input, vote_state.as_mut_ptr())
208 }
209
210 #[cfg(any(target_os = "solana", feature = "bincode"))]
211 fn deserialize_into_ptr(
212 input: &[u8],
213 vote_state: *mut VoteState,
214 ) -> Result<(), InstructionError> {
215 use vote_state_deserialize::deserialize_vote_state_into;
216
217 let mut cursor = std::io::Cursor::new(input);
218
219 let variant = solana_serialize_utils::cursor::read_u32(&mut cursor)?;
220 match variant {
221 0 => {
224 #[cfg(not(target_os = "solana"))]
225 {
226 unsafe {
232 vote_state.write(
233 bincode::deserialize::<VoteStateVersions>(input)
234 .map(|versioned| versioned.convert_to_current())
235 .map_err(|_| InstructionError::InvalidAccountData)?,
236 );
237 }
238 Ok(())
239 }
240 #[cfg(target_os = "solana")]
241 Err(InstructionError::InvalidAccountData)
242 }
243 1 => deserialize_vote_state_into(&mut cursor, vote_state, false),
245 2 => deserialize_vote_state_into(&mut cursor, vote_state, true),
247 _ => Err(InstructionError::InvalidAccountData),
248 }?;
249
250 Ok(())
251 }
252
253 #[cfg(feature = "bincode")]
254 pub fn serialize(
255 versioned: &VoteStateVersions,
256 output: &mut [u8],
257 ) -> Result<(), InstructionError> {
258 bincode::serialize_into(output, versioned).map_err(|err| match *err {
259 bincode::ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
260 _ => InstructionError::GenericError,
261 })
262 }
263
264 #[deprecated(since = "2.2.0", note = "logic was moved into the agave runtime crate")]
269 pub fn commission_split(&self, on: u64) -> (u64, u64, bool) {
270 match self.commission.min(100) {
271 0 => (0, on, false),
272 100 => (on, 0, false),
273 split => {
274 let on = u128::from(on);
275 let mine = on
281 .checked_mul(u128::from(split))
282 .expect("multiplication of a u64 and u8 should not overflow")
283 / 100u128;
284 let theirs = on
285 .checked_mul(u128::from(
286 100u8
287 .checked_sub(split)
288 .expect("commission cannot be greater than 100"),
289 ))
290 .expect("multiplication of a u64 and u8 should not overflow")
291 / 100u128;
292
293 (mine as u64, theirs as u64, true)
294 }
295 }
296 }
297
298 pub fn contains_slot(&self, candidate_slot: Slot) -> bool {
300 self.votes
301 .binary_search_by(|vote| vote.slot().cmp(&candidate_slot))
302 .is_ok()
303 }
304
305 #[cfg(test)]
306 pub(crate) fn get_max_sized_vote_state() -> VoteState {
307 use solana_epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET;
308 let mut authorized_voters = AuthorizedVoters::default();
309 for i in 0..=MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
310 authorized_voters.insert(i, Pubkey::new_unique());
311 }
312
313 VoteState {
314 votes: VecDeque::from(vec![LandedVote::default(); MAX_LOCKOUT_HISTORY]),
315 root_slot: Some(u64::MAX),
316 epoch_credits: vec![(0, 0, 0); MAX_EPOCH_CREDITS_HISTORY],
317 authorized_voters,
318 ..Self::default()
319 }
320 }
321
322 pub fn process_next_vote_slot(
323 &mut self,
324 next_vote_slot: Slot,
325 epoch: Epoch,
326 current_slot: Slot,
327 ) {
328 if self
330 .last_voted_slot()
331 .is_some_and(|last_voted_slot| next_vote_slot <= last_voted_slot)
332 {
333 return;
334 }
335
336 self.pop_expired_votes(next_vote_slot);
337
338 let landed_vote = LandedVote {
339 latency: Self::compute_vote_latency(next_vote_slot, current_slot),
340 lockout: Lockout::new(next_vote_slot),
341 };
342
343 if self.votes.len() == MAX_LOCKOUT_HISTORY {
345 let credits = self.credits_for_vote_at_index(0);
346 let landed_vote = self.votes.pop_front().unwrap();
347 self.root_slot = Some(landed_vote.slot());
348
349 self.increment_credits(epoch, credits);
350 }
351 self.votes.push_back(landed_vote);
352 self.double_lockouts();
353 }
354
355 pub fn increment_credits(&mut self, epoch: Epoch, credits: u64) {
357 if self.epoch_credits.is_empty() {
361 self.epoch_credits.push((epoch, 0, 0));
362 } else if epoch != self.epoch_credits.last().unwrap().0 {
363 let (_, credits, prev_credits) = *self.epoch_credits.last().unwrap();
364
365 if credits != prev_credits {
366 self.epoch_credits.push((epoch, credits, credits));
369 } else {
370 self.epoch_credits.last_mut().unwrap().0 = epoch;
372 }
373
374 if self.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY {
376 self.epoch_credits.remove(0);
377 }
378 }
379
380 self.epoch_credits.last_mut().unwrap().1 =
381 self.epoch_credits.last().unwrap().1.saturating_add(credits);
382 }
383
384 pub fn compute_vote_latency(voted_for_slot: Slot, current_slot: Slot) -> u8 {
386 std::cmp::min(current_slot.saturating_sub(voted_for_slot), u8::MAX as u64) as u8
387 }
388
389 pub fn credits_for_vote_at_index(&self, index: usize) -> u64 {
391 let latency = self
392 .votes
393 .get(index)
394 .map_or(0, |landed_vote| landed_vote.latency);
395
396 if latency == 0 {
399 1
400 } else {
401 match latency.checked_sub(VOTE_CREDITS_GRACE_SLOTS) {
402 None | Some(0) => {
403 VOTE_CREDITS_MAXIMUM_PER_SLOT as u64
405 }
406
407 Some(diff) => {
408 match VOTE_CREDITS_MAXIMUM_PER_SLOT.checked_sub(diff) {
411 None | Some(0) => 1,
413
414 Some(credits) => credits as u64,
415 }
416 }
417 }
418 }
419 }
420
421 pub fn nth_recent_lockout(&self, position: usize) -> Option<&Lockout> {
422 if position < self.votes.len() {
423 let pos = self
424 .votes
425 .len()
426 .checked_sub(position)
427 .and_then(|pos| pos.checked_sub(1))?;
428 self.votes.get(pos).map(|vote| &vote.lockout)
429 } else {
430 None
431 }
432 }
433
434 pub fn last_lockout(&self) -> Option<&Lockout> {
435 self.votes.back().map(|vote| &vote.lockout)
436 }
437
438 pub fn last_voted_slot(&self) -> Option<Slot> {
439 self.last_lockout().map(|v| v.slot())
440 }
441
442 pub fn tower(&self) -> Vec<Slot> {
445 self.votes.iter().map(|v| v.slot()).collect()
446 }
447
448 pub fn current_epoch(&self) -> Epoch {
449 if self.epoch_credits.is_empty() {
450 0
451 } else {
452 self.epoch_credits.last().unwrap().0
453 }
454 }
455
456 pub fn credits(&self) -> u64 {
459 if self.epoch_credits.is_empty() {
460 0
461 } else {
462 self.epoch_credits.last().unwrap().1
463 }
464 }
465
466 pub fn epoch_credits(&self) -> &Vec<(Epoch, u64, u64)> {
472 &self.epoch_credits
473 }
474
475 pub fn set_new_authorized_voter<F>(
476 &mut self,
477 authorized_pubkey: &Pubkey,
478 current_epoch: Epoch,
479 target_epoch: Epoch,
480 verify: F,
481 ) -> Result<(), InstructionError>
482 where
483 F: Fn(Pubkey) -> Result<(), InstructionError>,
484 {
485 let epoch_authorized_voter = self.get_and_update_authorized_voter(current_epoch)?;
486 verify(epoch_authorized_voter)?;
487
488 if self.authorized_voters.contains(target_epoch) {
494 return Err(VoteError::TooSoonToReauthorize.into());
495 }
496
497 let (latest_epoch, latest_authorized_pubkey) = self
499 .authorized_voters
500 .last()
501 .ok_or(InstructionError::InvalidAccountData)?;
502
503 if latest_authorized_pubkey != authorized_pubkey {
507 let epoch_of_last_authorized_switch =
509 self.prior_voters.last().map(|range| range.2).unwrap_or(0);
510
511 if target_epoch <= *latest_epoch {
518 return Err(InstructionError::InvalidAccountData);
519 }
520
521 self.prior_voters.append((
523 *latest_authorized_pubkey,
524 epoch_of_last_authorized_switch,
525 target_epoch,
526 ));
527 }
528
529 self.authorized_voters
530 .insert(target_epoch, *authorized_pubkey);
531
532 Ok(())
533 }
534
535 pub fn get_and_update_authorized_voter(
536 &mut self,
537 current_epoch: Epoch,
538 ) -> Result<Pubkey, InstructionError> {
539 let pubkey = self
540 .authorized_voters
541 .get_and_cache_authorized_voter_for_epoch(current_epoch)
542 .ok_or(InstructionError::InvalidAccountData)?;
543 self.authorized_voters
544 .purge_authorized_voters(current_epoch);
545 Ok(pubkey)
546 }
547
548 pub fn pop_expired_votes(&mut self, next_vote_slot: Slot) {
553 while let Some(vote) = self.last_lockout() {
554 if !vote.is_locked_out_at_slot(next_vote_slot) {
555 self.votes.pop_back();
556 } else {
557 break;
558 }
559 }
560 }
561
562 pub fn double_lockouts(&mut self) {
563 let stack_depth = self.votes.len();
564 for (i, v) in self.votes.iter_mut().enumerate() {
565 if stack_depth >
568 i.checked_add(v.confirmation_count() as usize)
569 .expect("`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`")
570 {
571 v.lockout.increase_confirmation_count(1);
572 }
573 }
574 }
575
576 pub fn process_timestamp(
577 &mut self,
578 slot: Slot,
579 timestamp: UnixTimestamp,
580 ) -> Result<(), VoteError> {
581 if (slot < self.last_timestamp.slot || timestamp < self.last_timestamp.timestamp)
582 || (slot == self.last_timestamp.slot
583 && BlockTimestamp { slot, timestamp } != self.last_timestamp
584 && self.last_timestamp.slot != 0)
585 {
586 return Err(VoteError::TimestampTooOld);
587 }
588 self.last_timestamp = BlockTimestamp { slot, timestamp };
589 Ok(())
590 }
591
592 pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
593 const VERSION_OFFSET: usize = 4;
594 const DEFAULT_PRIOR_VOTERS_END: usize = VERSION_OFFSET + DEFAULT_PRIOR_VOTERS_OFFSET;
595 data.len() == VoteState::size_of()
596 && data[VERSION_OFFSET..DEFAULT_PRIOR_VOTERS_END] != [0; DEFAULT_PRIOR_VOTERS_OFFSET]
597 }
598}
599
600#[cfg(any(target_os = "solana", feature = "bincode"))]
601mod vote_state_deserialize {
602 use {
603 crate::{
604 authorized_voters::AuthorizedVoters,
605 state::{
606 BlockTimestamp, LandedVote, Lockout, VoteState, MAX_EPOCH_CREDITS_HISTORY,
607 MAX_ITEMS, MAX_LOCKOUT_HISTORY,
608 },
609 },
610 solana_clock::Epoch,
611 solana_instruction::error::InstructionError,
612 solana_pubkey::Pubkey,
613 solana_serialize_utils::cursor::{
614 read_bool, read_i64, read_option_u64, read_pubkey, read_pubkey_into, read_u32,
615 read_u64, read_u8,
616 },
617 std::{collections::VecDeque, io::Cursor, ptr::addr_of_mut},
618 };
619
620 pub(super) fn deserialize_vote_state_into(
621 cursor: &mut Cursor<&[u8]>,
622 vote_state: *mut VoteState,
623 has_latency: bool,
624 ) -> Result<(), InstructionError> {
625 read_pubkey_into(
630 cursor,
631 unsafe { addr_of_mut!((*vote_state).node_pubkey) },
633 )?;
634 read_pubkey_into(
635 cursor,
636 unsafe { addr_of_mut!((*vote_state).authorized_withdrawer) },
638 )?;
639 let commission = read_u8(cursor)?;
640 let votes = read_votes(cursor, has_latency)?;
641 let root_slot = read_option_u64(cursor)?;
642 let authorized_voters = read_authorized_voters(cursor)?;
643 read_prior_voters_into(cursor, vote_state)?;
644 let epoch_credits = read_epoch_credits(cursor)?;
645 read_last_timestamp_into(cursor, vote_state)?;
646
647 unsafe {
654 addr_of_mut!((*vote_state).commission).write(commission);
655 addr_of_mut!((*vote_state).votes).write(votes);
656 addr_of_mut!((*vote_state).root_slot).write(root_slot);
657 addr_of_mut!((*vote_state).authorized_voters).write(authorized_voters);
658 addr_of_mut!((*vote_state).epoch_credits).write(epoch_credits);
659 }
660
661 Ok(())
662 }
663
664 fn read_votes<T: AsRef<[u8]>>(
665 cursor: &mut Cursor<T>,
666 has_latency: bool,
667 ) -> Result<VecDeque<LandedVote>, InstructionError> {
668 let vote_count = read_u64(cursor)? as usize;
669 let mut votes = VecDeque::with_capacity(vote_count.min(MAX_LOCKOUT_HISTORY));
670
671 for _ in 0..vote_count {
672 let latency = if has_latency { read_u8(cursor)? } else { 0 };
673
674 let slot = read_u64(cursor)?;
675 let confirmation_count = read_u32(cursor)?;
676 let lockout = Lockout::new_with_confirmation_count(slot, confirmation_count);
677
678 votes.push_back(LandedVote { latency, lockout });
679 }
680
681 Ok(votes)
682 }
683
684 fn read_authorized_voters<T: AsRef<[u8]>>(
685 cursor: &mut Cursor<T>,
686 ) -> Result<AuthorizedVoters, InstructionError> {
687 let authorized_voter_count = read_u64(cursor)?;
688 let mut authorized_voters = AuthorizedVoters::default();
689
690 for _ in 0..authorized_voter_count {
691 let epoch = read_u64(cursor)?;
692 let authorized_voter = read_pubkey(cursor)?;
693 authorized_voters.insert(epoch, authorized_voter);
694 }
695
696 Ok(authorized_voters)
697 }
698
699 fn read_prior_voters_into<T: AsRef<[u8]>>(
700 cursor: &mut Cursor<T>,
701 vote_state: *mut VoteState,
702 ) -> Result<(), InstructionError> {
703 unsafe {
705 let prior_voters = addr_of_mut!((*vote_state).prior_voters);
706 let prior_voters_buf = addr_of_mut!((*prior_voters).buf) as *mut (Pubkey, Epoch, Epoch);
707
708 for i in 0..MAX_ITEMS {
709 let prior_voter = read_pubkey(cursor)?;
710 let from_epoch = read_u64(cursor)?;
711 let until_epoch = read_u64(cursor)?;
712
713 prior_voters_buf
714 .add(i)
715 .write((prior_voter, from_epoch, until_epoch));
716 }
717
718 (*vote_state).prior_voters.idx = read_u64(cursor)? as usize;
719 (*vote_state).prior_voters.is_empty = read_bool(cursor)?;
720 }
721 Ok(())
722 }
723
724 fn read_epoch_credits<T: AsRef<[u8]>>(
725 cursor: &mut Cursor<T>,
726 ) -> Result<Vec<(Epoch, u64, u64)>, InstructionError> {
727 let epoch_credit_count = read_u64(cursor)? as usize;
728 let mut epoch_credits =
729 Vec::with_capacity(epoch_credit_count.min(MAX_EPOCH_CREDITS_HISTORY));
730
731 for _ in 0..epoch_credit_count {
732 let epoch = read_u64(cursor)?;
733 let credits = read_u64(cursor)?;
734 let prev_credits = read_u64(cursor)?;
735 epoch_credits.push((epoch, credits, prev_credits));
736 }
737
738 Ok(epoch_credits)
739 }
740
741 fn read_last_timestamp_into<T: AsRef<[u8]>>(
742 cursor: &mut Cursor<T>,
743 vote_state: *mut VoteState,
744 ) -> Result<(), InstructionError> {
745 let slot = read_u64(cursor)?;
746 let timestamp = read_i64(cursor)?;
747
748 let last_timestamp = BlockTimestamp { slot, timestamp };
749
750 unsafe {
752 addr_of_mut!((*vote_state).last_timestamp).write(last_timestamp);
753 }
754
755 Ok(())
756 }
757}