1#[cfg(feature = "dev-context-only-utils")]
4use arbitrary::Arbitrary;
5#[cfg(all(not(target_os = "solana"), feature = "bincode"))]
6use bincode::deserialize;
7#[cfg(feature = "bincode")]
8use bincode::{serialize_into, ErrorKind};
9#[cfg(feature = "serde")]
10use serde_derive::{Deserialize, Serialize};
11#[cfg(feature = "frozen-abi")]
12use solana_frozen_abi_macro::{frozen_abi, AbiExample};
13use {
14 crate::{authorized_voters::AuthorizedVoters, error::VoteError},
15 solana_clock::{Clock, Epoch, Slot, UnixTimestamp},
16 solana_hash::Hash,
17 solana_instruction::error::InstructionError,
18 solana_pubkey::Pubkey,
19 solana_rent::Rent,
20 std::{collections::VecDeque, fmt::Debug},
21};
22#[cfg(test)]
23use {arbitrary::Unstructured, solana_epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET};
24
25mod vote_state_0_23_5;
26pub mod vote_state_1_14_11;
27pub use vote_state_1_14_11::*;
28#[cfg(any(target_os = "solana", feature = "bincode"))]
29mod vote_state_deserialize;
30#[cfg(any(target_os = "solana", feature = "bincode"))]
31use vote_state_deserialize::deserialize_vote_state_into;
32pub mod vote_state_versions;
33pub use vote_state_versions::*;
34
35pub const MAX_LOCKOUT_HISTORY: usize = 31;
37pub const INITIAL_LOCKOUT: usize = 2;
38
39pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
41
42const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 114;
44
45pub const VOTE_CREDITS_GRACE_SLOTS: u8 = 2;
47
48pub const VOTE_CREDITS_MAXIMUM_PER_SLOT: u8 = 16;
50
51#[cfg_attr(
52 feature = "frozen-abi",
53 frozen_abi(digest = "GvUzgtcxhKVVxPAjSntXGPqjLZK5ovgZzCiUP1tDpB9q"),
54 derive(AbiExample)
55)]
56#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
57#[derive(Default, Debug, PartialEq, Eq, Clone)]
58pub struct Vote {
59 pub slots: Vec<Slot>,
61 pub hash: Hash,
63 pub timestamp: Option<UnixTimestamp>,
65}
66
67impl Vote {
68 pub fn new(slots: Vec<Slot>, hash: Hash) -> Self {
69 Self {
70 slots,
71 hash,
72 timestamp: None,
73 }
74 }
75
76 pub fn last_voted_slot(&self) -> Option<Slot> {
77 self.slots.last().copied()
78 }
79}
80
81#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
82#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
83#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
84#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
85pub struct Lockout {
86 slot: Slot,
87 confirmation_count: u32,
88}
89
90impl Lockout {
91 pub fn new(slot: Slot) -> Self {
92 Self::new_with_confirmation_count(slot, 1)
93 }
94
95 pub fn new_with_confirmation_count(slot: Slot, confirmation_count: u32) -> Self {
96 Self {
97 slot,
98 confirmation_count,
99 }
100 }
101
102 pub fn lockout(&self) -> u64 {
104 (INITIAL_LOCKOUT as u64).wrapping_pow(std::cmp::min(
105 self.confirmation_count(),
106 MAX_LOCKOUT_HISTORY as u32,
107 ))
108 }
109
110 pub fn last_locked_out_slot(&self) -> Slot {
114 self.slot.saturating_add(self.lockout())
115 }
116
117 pub fn is_locked_out_at_slot(&self, slot: Slot) -> bool {
118 self.last_locked_out_slot() >= slot
119 }
120
121 pub fn slot(&self) -> Slot {
122 self.slot
123 }
124
125 pub fn confirmation_count(&self) -> u32 {
126 self.confirmation_count
127 }
128
129 pub fn increase_confirmation_count(&mut self, by: u32) {
130 self.confirmation_count = self.confirmation_count.saturating_add(by)
131 }
132}
133
134#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
135#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
136#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
137#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
138pub struct LandedVote {
139 pub latency: u8,
143 pub lockout: Lockout,
144}
145
146impl LandedVote {
147 pub fn slot(&self) -> Slot {
148 self.lockout.slot
149 }
150
151 pub fn confirmation_count(&self) -> u32 {
152 self.lockout.confirmation_count
153 }
154}
155
156impl From<LandedVote> for Lockout {
157 fn from(landed_vote: LandedVote) -> Self {
158 landed_vote.lockout
159 }
160}
161
162impl From<Lockout> for LandedVote {
163 fn from(lockout: Lockout) -> Self {
164 Self {
165 latency: 0,
166 lockout,
167 }
168 }
169}
170
171#[cfg_attr(
172 feature = "frozen-abi",
173 frozen_abi(digest = "CxyuwbaEdzP7jDCZyxjgQvLGXadBUZF3LoUvbSpQ6tYN"),
174 derive(AbiExample)
175)]
176#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
177#[derive(Default, Debug, PartialEq, Eq, Clone)]
178pub struct VoteStateUpdate {
179 pub lockouts: VecDeque<Lockout>,
181 pub root: Option<Slot>,
183 pub hash: Hash,
185 pub timestamp: Option<UnixTimestamp>,
187}
188
189impl From<Vec<(Slot, u32)>> for VoteStateUpdate {
190 fn from(recent_slots: Vec<(Slot, u32)>) -> Self {
191 let lockouts: VecDeque<Lockout> = recent_slots
192 .into_iter()
193 .map(|(slot, confirmation_count)| {
194 Lockout::new_with_confirmation_count(slot, confirmation_count)
195 })
196 .collect();
197 Self {
198 lockouts,
199 root: None,
200 hash: Hash::default(),
201 timestamp: None,
202 }
203 }
204}
205
206impl VoteStateUpdate {
207 pub fn new(lockouts: VecDeque<Lockout>, root: Option<Slot>, hash: Hash) -> Self {
208 Self {
209 lockouts,
210 root,
211 hash,
212 timestamp: None,
213 }
214 }
215
216 pub fn slots(&self) -> Vec<Slot> {
217 self.lockouts.iter().map(|lockout| lockout.slot()).collect()
218 }
219
220 pub fn last_voted_slot(&self) -> Option<Slot> {
221 self.lockouts.back().map(|l| l.slot())
222 }
223}
224
225#[cfg_attr(
226 feature = "frozen-abi",
227 frozen_abi(digest = "6UDiQMH4wbNwkMHosPMtekMYu2Qa6CHPZ2ymK4mc6FGu"),
228 derive(AbiExample)
229)]
230#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
231#[derive(Default, Debug, PartialEq, Eq, Clone)]
232pub struct TowerSync {
233 pub lockouts: VecDeque<Lockout>,
235 pub root: Option<Slot>,
237 pub hash: Hash,
239 pub timestamp: Option<UnixTimestamp>,
241 pub block_id: Hash,
245}
246
247impl From<Vec<(Slot, u32)>> for TowerSync {
248 fn from(recent_slots: Vec<(Slot, u32)>) -> Self {
249 let lockouts: VecDeque<Lockout> = recent_slots
250 .into_iter()
251 .map(|(slot, confirmation_count)| {
252 Lockout::new_with_confirmation_count(slot, confirmation_count)
253 })
254 .collect();
255 Self {
256 lockouts,
257 root: None,
258 hash: Hash::default(),
259 timestamp: None,
260 block_id: Hash::default(),
261 }
262 }
263}
264
265impl TowerSync {
266 pub fn new(
267 lockouts: VecDeque<Lockout>,
268 root: Option<Slot>,
269 hash: Hash,
270 block_id: Hash,
271 ) -> Self {
272 Self {
273 lockouts,
274 root,
275 hash,
276 timestamp: None,
277 block_id,
278 }
279 }
280
281 pub fn new_from_slot(slot: Slot, hash: Hash) -> Self {
285 let lowest_slot = slot
286 .saturating_add(1)
287 .saturating_sub(MAX_LOCKOUT_HISTORY as u64);
288 let slots: Vec<_> = (lowest_slot..slot.saturating_add(1)).collect();
289 Self::new_from_slots(
290 slots,
291 hash,
292 (lowest_slot > 0).then(|| lowest_slot.saturating_sub(1)),
293 )
294 }
295
296 pub fn new_from_slots(slots: Vec<Slot>, hash: Hash, root: Option<Slot>) -> Self {
298 let lockouts: VecDeque<Lockout> = slots
299 .into_iter()
300 .rev()
301 .enumerate()
302 .map(|(cc, s)| Lockout::new_with_confirmation_count(s, cc.saturating_add(1) as u32))
303 .rev()
304 .collect();
305 Self {
306 lockouts,
307 hash,
308 root,
309 timestamp: None,
310 block_id: Hash::default(),
311 }
312 }
313
314 pub fn slots(&self) -> Vec<Slot> {
315 self.lockouts.iter().map(|lockout| lockout.slot()).collect()
316 }
317
318 pub fn last_voted_slot(&self) -> Option<Slot> {
319 self.lockouts.back().map(|l| l.slot())
320 }
321}
322
323#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
324#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
325pub struct VoteInit {
326 pub node_pubkey: Pubkey,
327 pub authorized_voter: Pubkey,
328 pub authorized_withdrawer: Pubkey,
329 pub commission: u8,
330}
331
332#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
333#[derive(Debug, PartialEq, Eq, Clone, Copy)]
334pub enum VoteAuthorize {
335 Voter,
336 Withdrawer,
337}
338
339#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
340#[derive(Debug, PartialEq, Eq, Clone)]
341pub struct VoteAuthorizeWithSeedArgs {
342 pub authorization_type: VoteAuthorize,
343 pub current_authority_derived_key_owner: Pubkey,
344 pub current_authority_derived_key_seed: String,
345 pub new_authority: Pubkey,
346}
347
348#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
349#[derive(Debug, PartialEq, Eq, Clone)]
350pub struct VoteAuthorizeCheckedWithSeedArgs {
351 pub authorization_type: VoteAuthorize,
352 pub current_authority_derived_key_owner: Pubkey,
353 pub current_authority_derived_key_seed: String,
354}
355
356#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
357#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
358#[derive(Debug, Default, PartialEq, Eq, Clone)]
359#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
360pub struct BlockTimestamp {
361 pub slot: Slot,
362 pub timestamp: UnixTimestamp,
363}
364
365const MAX_ITEMS: usize = 32;
367
368#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
369#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
370#[derive(Debug, PartialEq, Eq, Clone)]
371#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
372pub struct CircBuf<I> {
373 buf: [I; MAX_ITEMS],
374 idx: usize,
376 is_empty: bool,
377}
378
379impl<I: Default + Copy> Default for CircBuf<I> {
380 fn default() -> Self {
381 Self {
382 buf: [I::default(); MAX_ITEMS],
383 idx: MAX_ITEMS
384 .checked_sub(1)
385 .expect("`MAX_ITEMS` should be positive"),
386 is_empty: true,
387 }
388 }
389}
390
391impl<I> CircBuf<I> {
392 pub fn append(&mut self, item: I) {
393 self.idx = self
395 .idx
396 .checked_add(1)
397 .and_then(|idx| idx.checked_rem(MAX_ITEMS))
398 .expect("`self.idx` should be < `MAX_ITEMS` which should be non-zero");
399
400 self.buf[self.idx] = item;
401 self.is_empty = false;
402 }
403
404 pub fn buf(&self) -> &[I; MAX_ITEMS] {
405 &self.buf
406 }
407
408 pub fn last(&self) -> Option<&I> {
409 if !self.is_empty {
410 self.buf.get(self.idx)
411 } else {
412 None
413 }
414 }
415}
416
417#[cfg_attr(
418 feature = "frozen-abi",
419 frozen_abi(digest = "BRwozbypfYXsHqFVj9w3iH5x1ak2NWHqCCn6pr3gHBkG"),
420 derive(AbiExample)
421)]
422#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
423#[derive(Debug, Default, PartialEq, Eq, Clone)]
424#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
425pub struct VoteState {
426 pub node_pubkey: Pubkey,
428
429 pub authorized_withdrawer: Pubkey,
431 pub commission: u8,
434
435 pub votes: VecDeque<LandedVote>,
436
437 pub root_slot: Option<Slot>,
440
441 authorized_voters: AuthorizedVoters,
443
444 prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>,
448
449 pub epoch_credits: Vec<(Epoch, u64, u64)>,
452
453 pub last_timestamp: BlockTimestamp,
455}
456
457impl VoteState {
458 pub fn new(vote_init: &VoteInit, clock: &Clock) -> Self {
459 Self {
460 node_pubkey: vote_init.node_pubkey,
461 authorized_voters: AuthorizedVoters::new(clock.epoch, vote_init.authorized_voter),
462 authorized_withdrawer: vote_init.authorized_withdrawer,
463 commission: vote_init.commission,
464 ..VoteState::default()
465 }
466 }
467
468 pub fn new_rand_for_tests(node_pubkey: Pubkey, root_slot: Slot) -> Self {
469 let votes = (1..32)
470 .map(|x| LandedVote {
471 latency: 0,
472 lockout: Lockout::new_with_confirmation_count(
473 u64::from(x).saturating_add(root_slot),
474 32_u32.saturating_sub(x),
475 ),
476 })
477 .collect();
478 Self {
479 node_pubkey,
480 root_slot: Some(root_slot),
481 votes,
482 ..VoteState::default()
483 }
484 }
485
486 pub fn get_authorized_voter(&self, epoch: Epoch) -> Option<Pubkey> {
487 self.authorized_voters.get_authorized_voter(epoch)
488 }
489
490 pub fn authorized_voters(&self) -> &AuthorizedVoters {
491 &self.authorized_voters
492 }
493
494 pub fn prior_voters(&mut self) -> &CircBuf<(Pubkey, Epoch, Epoch)> {
495 &self.prior_voters
496 }
497
498 pub fn get_rent_exempt_reserve(rent: &Rent) -> u64 {
499 rent.minimum_balance(VoteState::size_of())
500 }
501
502 pub const fn size_of() -> usize {
505 3762 }
507
508 #[cfg(any(target_os = "solana", feature = "bincode"))]
514 pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
515 #[cfg(not(target_os = "solana"))]
516 {
517 deserialize::<VoteStateVersions>(input)
518 .map(|versioned| versioned.convert_to_current())
519 .map_err(|_| InstructionError::InvalidAccountData)
520 }
521 #[cfg(target_os = "solana")]
522 {
523 let mut vote_state = Self::default();
524 Self::deserialize_into(input, &mut vote_state)?;
525 Ok(vote_state)
526 }
527 }
528
529 #[cfg(any(target_os = "solana", feature = "bincode"))]
537 pub fn deserialize_into(
538 input: &[u8],
539 vote_state: &mut VoteState,
540 ) -> Result<(), InstructionError> {
541 let vote_state = vote_state as *mut VoteState;
546
547 unsafe {
551 std::ptr::drop_in_place(vote_state);
552 }
553
554 struct DropGuard {
556 vote_state: *mut VoteState,
557 }
558
559 impl Drop for DropGuard {
560 fn drop(&mut self) {
561 unsafe {
571 self.vote_state.write(VoteState::default());
572 }
573 }
574 }
575
576 let guard = DropGuard { vote_state };
577
578 let res = VoteState::deserialize_into_ptr(input, vote_state);
579 if res.is_ok() {
580 std::mem::forget(guard);
581 }
582
583 res
584 }
585
586 #[cfg(any(target_os = "solana", feature = "bincode"))]
596 pub fn deserialize_into_uninit(
597 input: &[u8],
598 vote_state: &mut std::mem::MaybeUninit<VoteState>,
599 ) -> Result<(), InstructionError> {
600 VoteState::deserialize_into_ptr(input, vote_state.as_mut_ptr())
601 }
602
603 #[cfg(any(target_os = "solana", feature = "bincode"))]
604 fn deserialize_into_ptr(
605 input: &[u8],
606 vote_state: *mut VoteState,
607 ) -> Result<(), InstructionError> {
608 let mut cursor = std::io::Cursor::new(input);
609
610 let variant = solana_serialize_utils::cursor::read_u32(&mut cursor)?;
611 match variant {
612 0 => {
615 #[cfg(not(target_os = "solana"))]
616 {
617 unsafe {
623 vote_state.write(
624 bincode::deserialize::<VoteStateVersions>(input)
625 .map(|versioned| versioned.convert_to_current())
626 .map_err(|_| InstructionError::InvalidAccountData)?,
627 );
628 }
629 Ok(())
630 }
631 #[cfg(target_os = "solana")]
632 Err(InstructionError::InvalidAccountData)
633 }
634 1 => deserialize_vote_state_into(&mut cursor, vote_state, false),
636 2 => deserialize_vote_state_into(&mut cursor, vote_state, true),
638 _ => Err(InstructionError::InvalidAccountData),
639 }?;
640
641 Ok(())
642 }
643
644 #[cfg(feature = "bincode")]
645 pub fn serialize(
646 versioned: &VoteStateVersions,
647 output: &mut [u8],
648 ) -> Result<(), InstructionError> {
649 serialize_into(output, versioned).map_err(|err| match *err {
650 ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
651 _ => InstructionError::GenericError,
652 })
653 }
654
655 #[deprecated(since = "2.2.0", note = "logic was moved into the agave runtime crate")]
660 pub fn commission_split(&self, on: u64) -> (u64, u64, bool) {
661 match self.commission.min(100) {
662 0 => (0, on, false),
663 100 => (on, 0, false),
664 split => {
665 let on = u128::from(on);
666 let mine = on
672 .checked_mul(u128::from(split))
673 .expect("multiplication of a u64 and u8 should not overflow")
674 / 100u128;
675 let theirs = on
676 .checked_mul(u128::from(
677 100u8
678 .checked_sub(split)
679 .expect("commission cannot be greater than 100"),
680 ))
681 .expect("multiplication of a u64 and u8 should not overflow")
682 / 100u128;
683
684 (mine as u64, theirs as u64, true)
685 }
686 }
687 }
688
689 pub fn contains_slot(&self, candidate_slot: Slot) -> bool {
691 self.votes
692 .binary_search_by(|vote| vote.slot().cmp(&candidate_slot))
693 .is_ok()
694 }
695
696 #[cfg(test)]
697 fn get_max_sized_vote_state() -> VoteState {
698 let mut authorized_voters = AuthorizedVoters::default();
699 for i in 0..=MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
700 authorized_voters.insert(i, Pubkey::new_unique());
701 }
702
703 VoteState {
704 votes: VecDeque::from(vec![LandedVote::default(); MAX_LOCKOUT_HISTORY]),
705 root_slot: Some(u64::MAX),
706 epoch_credits: vec![(0, 0, 0); MAX_EPOCH_CREDITS_HISTORY],
707 authorized_voters,
708 ..Self::default()
709 }
710 }
711
712 pub fn process_next_vote_slot(
713 &mut self,
714 next_vote_slot: Slot,
715 epoch: Epoch,
716 current_slot: Slot,
717 ) {
718 if self
720 .last_voted_slot()
721 .is_some_and(|last_voted_slot| next_vote_slot <= last_voted_slot)
722 {
723 return;
724 }
725
726 self.pop_expired_votes(next_vote_slot);
727
728 let landed_vote = LandedVote {
729 latency: Self::compute_vote_latency(next_vote_slot, current_slot),
730 lockout: Lockout::new(next_vote_slot),
731 };
732
733 if self.votes.len() == MAX_LOCKOUT_HISTORY {
735 let credits = self.credits_for_vote_at_index(0);
736 let landed_vote = self.votes.pop_front().unwrap();
737 self.root_slot = Some(landed_vote.slot());
738
739 self.increment_credits(epoch, credits);
740 }
741 self.votes.push_back(landed_vote);
742 self.double_lockouts();
743 }
744
745 pub fn increment_credits(&mut self, epoch: Epoch, credits: u64) {
747 if self.epoch_credits.is_empty() {
751 self.epoch_credits.push((epoch, 0, 0));
752 } else if epoch != self.epoch_credits.last().unwrap().0 {
753 let (_, credits, prev_credits) = *self.epoch_credits.last().unwrap();
754
755 if credits != prev_credits {
756 self.epoch_credits.push((epoch, credits, credits));
759 } else {
760 self.epoch_credits.last_mut().unwrap().0 = epoch;
762 }
763
764 if self.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY {
766 self.epoch_credits.remove(0);
767 }
768 }
769
770 self.epoch_credits.last_mut().unwrap().1 =
771 self.epoch_credits.last().unwrap().1.saturating_add(credits);
772 }
773
774 pub fn compute_vote_latency(voted_for_slot: Slot, current_slot: Slot) -> u8 {
776 std::cmp::min(current_slot.saturating_sub(voted_for_slot), u8::MAX as u64) as u8
777 }
778
779 pub fn credits_for_vote_at_index(&self, index: usize) -> u64 {
781 let latency = self
782 .votes
783 .get(index)
784 .map_or(0, |landed_vote| landed_vote.latency);
785
786 if latency == 0 {
789 1
790 } else {
791 match latency.checked_sub(VOTE_CREDITS_GRACE_SLOTS) {
792 None | Some(0) => {
793 VOTE_CREDITS_MAXIMUM_PER_SLOT as u64
795 }
796
797 Some(diff) => {
798 match VOTE_CREDITS_MAXIMUM_PER_SLOT.checked_sub(diff) {
801 None | Some(0) => 1,
803
804 Some(credits) => credits as u64,
805 }
806 }
807 }
808 }
809 }
810
811 pub fn nth_recent_lockout(&self, position: usize) -> Option<&Lockout> {
812 if position < self.votes.len() {
813 let pos = self
814 .votes
815 .len()
816 .checked_sub(position)
817 .and_then(|pos| pos.checked_sub(1))?;
818 self.votes.get(pos).map(|vote| &vote.lockout)
819 } else {
820 None
821 }
822 }
823
824 pub fn last_lockout(&self) -> Option<&Lockout> {
825 self.votes.back().map(|vote| &vote.lockout)
826 }
827
828 pub fn last_voted_slot(&self) -> Option<Slot> {
829 self.last_lockout().map(|v| v.slot())
830 }
831
832 pub fn tower(&self) -> Vec<Slot> {
835 self.votes.iter().map(|v| v.slot()).collect()
836 }
837
838 pub fn current_epoch(&self) -> Epoch {
839 if self.epoch_credits.is_empty() {
840 0
841 } else {
842 self.epoch_credits.last().unwrap().0
843 }
844 }
845
846 pub fn credits(&self) -> u64 {
849 if self.epoch_credits.is_empty() {
850 0
851 } else {
852 self.epoch_credits.last().unwrap().1
853 }
854 }
855
856 pub fn epoch_credits(&self) -> &Vec<(Epoch, u64, u64)> {
862 &self.epoch_credits
863 }
864
865 pub fn set_new_authorized_voter<F>(
866 &mut self,
867 authorized_pubkey: &Pubkey,
868 current_epoch: Epoch,
869 target_epoch: Epoch,
870 verify: F,
871 ) -> Result<(), InstructionError>
872 where
873 F: Fn(Pubkey) -> Result<(), InstructionError>,
874 {
875 let epoch_authorized_voter = self.get_and_update_authorized_voter(current_epoch)?;
876 verify(epoch_authorized_voter)?;
877
878 if self.authorized_voters.contains(target_epoch) {
884 return Err(VoteError::TooSoonToReauthorize.into());
885 }
886
887 let (latest_epoch, latest_authorized_pubkey) = self
889 .authorized_voters
890 .last()
891 .ok_or(InstructionError::InvalidAccountData)?;
892
893 if latest_authorized_pubkey != authorized_pubkey {
897 let epoch_of_last_authorized_switch =
899 self.prior_voters.last().map(|range| range.2).unwrap_or(0);
900
901 if target_epoch <= *latest_epoch {
908 return Err(InstructionError::InvalidAccountData);
909 }
910
911 self.prior_voters.append((
913 *latest_authorized_pubkey,
914 epoch_of_last_authorized_switch,
915 target_epoch,
916 ));
917 }
918
919 self.authorized_voters
920 .insert(target_epoch, *authorized_pubkey);
921
922 Ok(())
923 }
924
925 pub fn get_and_update_authorized_voter(
926 &mut self,
927 current_epoch: Epoch,
928 ) -> Result<Pubkey, InstructionError> {
929 let pubkey = self
930 .authorized_voters
931 .get_and_cache_authorized_voter_for_epoch(current_epoch)
932 .ok_or(InstructionError::InvalidAccountData)?;
933 self.authorized_voters
934 .purge_authorized_voters(current_epoch);
935 Ok(pubkey)
936 }
937
938 pub fn pop_expired_votes(&mut self, next_vote_slot: Slot) {
943 while let Some(vote) = self.last_lockout() {
944 if !vote.is_locked_out_at_slot(next_vote_slot) {
945 self.votes.pop_back();
946 } else {
947 break;
948 }
949 }
950 }
951
952 pub fn double_lockouts(&mut self) {
953 let stack_depth = self.votes.len();
954 for (i, v) in self.votes.iter_mut().enumerate() {
955 if stack_depth >
958 i.checked_add(v.confirmation_count() as usize)
959 .expect("`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`")
960 {
961 v.lockout.increase_confirmation_count(1);
962 }
963 }
964 }
965
966 pub fn process_timestamp(
967 &mut self,
968 slot: Slot,
969 timestamp: UnixTimestamp,
970 ) -> Result<(), VoteError> {
971 if (slot < self.last_timestamp.slot || timestamp < self.last_timestamp.timestamp)
972 || (slot == self.last_timestamp.slot
973 && BlockTimestamp { slot, timestamp } != self.last_timestamp
974 && self.last_timestamp.slot != 0)
975 {
976 return Err(VoteError::TimestampTooOld);
977 }
978 self.last_timestamp = BlockTimestamp { slot, timestamp };
979 Ok(())
980 }
981
982 pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
983 const VERSION_OFFSET: usize = 4;
984 const DEFAULT_PRIOR_VOTERS_END: usize = VERSION_OFFSET + DEFAULT_PRIOR_VOTERS_OFFSET;
985 data.len() == VoteState::size_of()
986 && data[VERSION_OFFSET..DEFAULT_PRIOR_VOTERS_END] != [0; DEFAULT_PRIOR_VOTERS_OFFSET]
987 }
988}
989
990#[cfg(feature = "serde")]
991pub mod serde_compact_vote_state_update {
992 use {
993 super::*,
994 crate::state::Lockout,
995 serde::{Deserialize, Deserializer, Serialize, Serializer},
996 solana_serde_varint as serde_varint, solana_short_vec as short_vec,
997 };
998
999 #[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
1000 #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
1001 struct LockoutOffset {
1002 #[serde(with = "serde_varint")]
1003 offset: Slot,
1004 confirmation_count: u8,
1005 }
1006
1007 #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
1008 struct CompactVoteStateUpdate {
1009 root: Slot,
1010 #[serde(with = "short_vec")]
1011 lockout_offsets: Vec<LockoutOffset>,
1012 hash: Hash,
1013 timestamp: Option<UnixTimestamp>,
1014 }
1015
1016 pub fn serialize<S>(
1017 vote_state_update: &VoteStateUpdate,
1018 serializer: S,
1019 ) -> Result<S::Ok, S::Error>
1020 where
1021 S: Serializer,
1022 {
1023 let lockout_offsets = vote_state_update.lockouts.iter().scan(
1024 vote_state_update.root.unwrap_or_default(),
1025 |slot, lockout| {
1026 let Some(offset) = lockout.slot().checked_sub(*slot) else {
1027 return Some(Err(serde::ser::Error::custom("Invalid vote lockout")));
1028 };
1029 let Ok(confirmation_count) = u8::try_from(lockout.confirmation_count()) else {
1030 return Some(Err(serde::ser::Error::custom("Invalid confirmation count")));
1031 };
1032 let lockout_offset = LockoutOffset {
1033 offset,
1034 confirmation_count,
1035 };
1036 *slot = lockout.slot();
1037 Some(Ok(lockout_offset))
1038 },
1039 );
1040 let compact_vote_state_update = CompactVoteStateUpdate {
1041 root: vote_state_update.root.unwrap_or(Slot::MAX),
1042 lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
1043 hash: vote_state_update.hash,
1044 timestamp: vote_state_update.timestamp,
1045 };
1046 compact_vote_state_update.serialize(serializer)
1047 }
1048
1049 pub fn deserialize<'de, D>(deserializer: D) -> Result<VoteStateUpdate, D::Error>
1050 where
1051 D: Deserializer<'de>,
1052 {
1053 let CompactVoteStateUpdate {
1054 root,
1055 lockout_offsets,
1056 hash,
1057 timestamp,
1058 } = CompactVoteStateUpdate::deserialize(deserializer)?;
1059 let root = (root != Slot::MAX).then_some(root);
1060 let lockouts =
1061 lockout_offsets
1062 .iter()
1063 .scan(root.unwrap_or_default(), |slot, lockout_offset| {
1064 *slot = match slot.checked_add(lockout_offset.offset) {
1065 None => {
1066 return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
1067 }
1068 Some(slot) => slot,
1069 };
1070 let lockout = Lockout::new_with_confirmation_count(
1071 *slot,
1072 u32::from(lockout_offset.confirmation_count),
1073 );
1074 Some(Ok(lockout))
1075 });
1076 Ok(VoteStateUpdate {
1077 root,
1078 lockouts: lockouts.collect::<Result<_, _>>()?,
1079 hash,
1080 timestamp,
1081 })
1082 }
1083}
1084
1085#[cfg(feature = "serde")]
1086pub mod serde_tower_sync {
1087 use {
1088 super::*,
1089 crate::state::Lockout,
1090 serde::{Deserialize, Deserializer, Serialize, Serializer},
1091 solana_serde_varint as serde_varint, solana_short_vec as short_vec,
1092 };
1093
1094 #[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
1095 #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
1096 struct LockoutOffset {
1097 #[serde(with = "serde_varint")]
1098 offset: Slot,
1099 confirmation_count: u8,
1100 }
1101
1102 #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
1103 struct CompactTowerSync {
1104 root: Slot,
1105 #[serde(with = "short_vec")]
1106 lockout_offsets: Vec<LockoutOffset>,
1107 hash: Hash,
1108 timestamp: Option<UnixTimestamp>,
1109 block_id: Hash,
1110 }
1111
1112 pub fn serialize<S>(tower_sync: &TowerSync, serializer: S) -> Result<S::Ok, S::Error>
1113 where
1114 S: Serializer,
1115 {
1116 let lockout_offsets = tower_sync.lockouts.iter().scan(
1117 tower_sync.root.unwrap_or_default(),
1118 |slot, lockout| {
1119 let Some(offset) = lockout.slot().checked_sub(*slot) else {
1120 return Some(Err(serde::ser::Error::custom("Invalid vote lockout")));
1121 };
1122 let Ok(confirmation_count) = u8::try_from(lockout.confirmation_count()) else {
1123 return Some(Err(serde::ser::Error::custom("Invalid confirmation count")));
1124 };
1125 let lockout_offset = LockoutOffset {
1126 offset,
1127 confirmation_count,
1128 };
1129 *slot = lockout.slot();
1130 Some(Ok(lockout_offset))
1131 },
1132 );
1133 let compact_tower_sync = CompactTowerSync {
1134 root: tower_sync.root.unwrap_or(Slot::MAX),
1135 lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
1136 hash: tower_sync.hash,
1137 timestamp: tower_sync.timestamp,
1138 block_id: tower_sync.block_id,
1139 };
1140 compact_tower_sync.serialize(serializer)
1141 }
1142
1143 pub fn deserialize<'de, D>(deserializer: D) -> Result<TowerSync, D::Error>
1144 where
1145 D: Deserializer<'de>,
1146 {
1147 let CompactTowerSync {
1148 root,
1149 lockout_offsets,
1150 hash,
1151 timestamp,
1152 block_id,
1153 } = CompactTowerSync::deserialize(deserializer)?;
1154 let root = (root != Slot::MAX).then_some(root);
1155 let lockouts =
1156 lockout_offsets
1157 .iter()
1158 .scan(root.unwrap_or_default(), |slot, lockout_offset| {
1159 *slot = match slot.checked_add(lockout_offset.offset) {
1160 None => {
1161 return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
1162 }
1163 Some(slot) => slot,
1164 };
1165 let lockout = Lockout::new_with_confirmation_count(
1166 *slot,
1167 u32::from(lockout_offset.confirmation_count),
1168 );
1169 Some(Ok(lockout))
1170 });
1171 Ok(TowerSync {
1172 root,
1173 lockouts: lockouts.collect::<Result<_, _>>()?,
1174 hash,
1175 timestamp,
1176 block_id,
1177 })
1178 }
1179}
1180
1181#[cfg(test)]
1182mod tests {
1183 use {
1184 super::*, bincode::serialized_size, core::mem::MaybeUninit, itertools::Itertools, rand::Rng,
1185 };
1186
1187 #[test]
1188 fn test_vote_serialize() {
1189 let mut buffer: Vec<u8> = vec![0; VoteState::size_of()];
1190 let mut vote_state = VoteState::default();
1191 vote_state
1192 .votes
1193 .resize(MAX_LOCKOUT_HISTORY, LandedVote::default());
1194 vote_state.root_slot = Some(1);
1195 let versioned = VoteStateVersions::new_current(vote_state);
1196 assert!(VoteState::serialize(&versioned, &mut buffer[0..4]).is_err());
1197 VoteState::serialize(&versioned, &mut buffer).unwrap();
1198 assert_eq!(
1199 VoteState::deserialize(&buffer).unwrap(),
1200 versioned.convert_to_current()
1201 );
1202 }
1203
1204 #[test]
1205 fn test_vote_deserialize_into() {
1206 let target_vote_state = VoteState::default();
1208 let vote_state_buf =
1209 bincode::serialize(&VoteStateVersions::new_current(target_vote_state.clone())).unwrap();
1210
1211 let mut test_vote_state = VoteState::default();
1212 VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
1213
1214 assert_eq!(target_vote_state, test_vote_state);
1215
1216 let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
1219 for _ in 0..1000 {
1220 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
1221 let mut unstructured = Unstructured::new(&raw_data);
1222
1223 let target_vote_state_versions =
1224 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
1225 let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
1226 let target_vote_state = target_vote_state_versions.convert_to_current();
1227
1228 let mut test_vote_state = VoteState::default();
1229 VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
1230
1231 assert_eq!(target_vote_state, test_vote_state);
1232 }
1233 }
1234
1235 #[test]
1236 fn test_vote_deserialize_into_error() {
1237 let target_vote_state = VoteState::new_rand_for_tests(Pubkey::new_unique(), 42);
1238 let mut vote_state_buf =
1239 bincode::serialize(&VoteStateVersions::new_current(target_vote_state.clone())).unwrap();
1240 let len = vote_state_buf.len();
1241 vote_state_buf.truncate(len - 1);
1242
1243 let mut test_vote_state = VoteState::default();
1244 VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap_err();
1245 assert_eq!(test_vote_state, VoteState::default());
1246 }
1247
1248 #[test]
1249 fn test_vote_deserialize_into_uninit() {
1250 let target_vote_state = VoteState::default();
1252 let vote_state_buf =
1253 bincode::serialize(&VoteStateVersions::new_current(target_vote_state.clone())).unwrap();
1254
1255 let mut test_vote_state = MaybeUninit::uninit();
1256 VoteState::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state).unwrap();
1257 let test_vote_state = unsafe { test_vote_state.assume_init() };
1258
1259 assert_eq!(target_vote_state, test_vote_state);
1260
1261 let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
1264 for _ in 0..1000 {
1265 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
1266 let mut unstructured = Unstructured::new(&raw_data);
1267
1268 let target_vote_state_versions =
1269 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
1270 let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
1271 let target_vote_state = target_vote_state_versions.convert_to_current();
1272
1273 let mut test_vote_state = MaybeUninit::uninit();
1274 VoteState::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state).unwrap();
1275 let test_vote_state = unsafe { test_vote_state.assume_init() };
1276
1277 assert_eq!(target_vote_state, test_vote_state);
1278 }
1279 }
1280
1281 #[test]
1282 fn test_vote_deserialize_into_uninit_nopanic() {
1283 let mut test_vote_state = MaybeUninit::uninit();
1285 let e = VoteState::deserialize_into_uninit(&[], &mut test_vote_state).unwrap_err();
1286 assert_eq!(e, InstructionError::InvalidAccountData);
1287
1288 let serialized_len_x4 = serialized_size(&VoteState::default()).unwrap() * 4;
1290 let mut rng = rand::thread_rng();
1291 for _ in 0..1000 {
1292 let raw_data_length = rng.gen_range(1..serialized_len_x4);
1293 let mut raw_data: Vec<u8> = (0..raw_data_length).map(|_| rng.gen::<u8>()).collect();
1294
1295 if raw_data_length >= 4 && rng.gen::<bool>() {
1297 let tag = rng.gen::<u8>() % 3;
1298 raw_data[0] = tag;
1299 raw_data[1] = 0;
1300 raw_data[2] = 0;
1301 raw_data[3] = 0;
1302 }
1303
1304 let mut test_vote_state = MaybeUninit::uninit();
1307 let test_res = VoteState::deserialize_into_uninit(&raw_data, &mut test_vote_state);
1308 let bincode_res = bincode::deserialize::<VoteStateVersions>(&raw_data)
1309 .map(|versioned| versioned.convert_to_current());
1310
1311 if test_res.is_err() {
1312 assert!(bincode_res.is_err());
1313 } else {
1314 let test_vote_state = unsafe { test_vote_state.assume_init() };
1315 assert_eq!(test_vote_state, bincode_res.unwrap());
1316 }
1317 }
1318 }
1319
1320 #[test]
1321 fn test_vote_deserialize_into_uninit_ill_sized() {
1322 let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
1324 for _ in 0..1000 {
1325 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
1326 let mut unstructured = Unstructured::new(&raw_data);
1327
1328 let original_vote_state_versions =
1329 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
1330 let original_buf = bincode::serialize(&original_vote_state_versions).unwrap();
1331
1332 let mut truncated_buf = original_buf.clone();
1333 let mut expanded_buf = original_buf.clone();
1334
1335 truncated_buf.resize(original_buf.len() - 8, 0);
1336 expanded_buf.resize(original_buf.len() + 8, 0);
1337
1338 let mut test_vote_state = MaybeUninit::uninit();
1340 let test_res = VoteState::deserialize_into_uninit(&truncated_buf, &mut test_vote_state);
1341 let bincode_res = bincode::deserialize::<VoteStateVersions>(&truncated_buf)
1342 .map(|versioned| versioned.convert_to_current());
1343
1344 assert!(test_res.is_err());
1345 assert!(bincode_res.is_err());
1346
1347 let mut test_vote_state = MaybeUninit::uninit();
1349 VoteState::deserialize_into_uninit(&expanded_buf, &mut test_vote_state).unwrap();
1350 let bincode_res = bincode::deserialize::<VoteStateVersions>(&expanded_buf)
1351 .map(|versioned| versioned.convert_to_current());
1352
1353 let test_vote_state = unsafe { test_vote_state.assume_init() };
1354 assert_eq!(test_vote_state, bincode_res.unwrap());
1355 }
1356 }
1357
1358 #[test]
1359 #[allow(deprecated)]
1360 fn test_vote_state_commission_split() {
1361 let vote_state = VoteState::default();
1362
1363 assert_eq!(vote_state.commission_split(1), (0, 1, false));
1364
1365 let mut vote_state = VoteState {
1366 commission: u8::MAX,
1367 ..VoteState::default()
1368 };
1369 assert_eq!(vote_state.commission_split(1), (1, 0, false));
1370
1371 vote_state.commission = 99;
1372 assert_eq!(vote_state.commission_split(10), (9, 0, true));
1373
1374 vote_state.commission = 1;
1375 assert_eq!(vote_state.commission_split(10), (0, 9, true));
1376
1377 vote_state.commission = 50;
1378 let (voter_portion, staker_portion, was_split) = vote_state.commission_split(10);
1379
1380 assert_eq!((voter_portion, staker_portion, was_split), (5, 5, true));
1381 }
1382
1383 #[test]
1384 fn test_vote_state_epoch_credits() {
1385 let mut vote_state = VoteState::default();
1386
1387 assert_eq!(vote_state.credits(), 0);
1388 assert_eq!(vote_state.epoch_credits().clone(), vec![]);
1389
1390 let mut expected = vec![];
1391 let mut credits = 0;
1392 let epochs = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
1393 for epoch in 0..epochs {
1394 for _j in 0..epoch {
1395 vote_state.increment_credits(epoch, 1);
1396 credits += 1;
1397 }
1398 expected.push((epoch, credits, credits - epoch));
1399 }
1400
1401 while expected.len() > MAX_EPOCH_CREDITS_HISTORY {
1402 expected.remove(0);
1403 }
1404
1405 assert_eq!(vote_state.credits(), credits);
1406 assert_eq!(vote_state.epoch_credits().clone(), expected);
1407 }
1408
1409 #[test]
1410 fn test_vote_state_epoch0_no_credits() {
1411 let mut vote_state = VoteState::default();
1412
1413 assert_eq!(vote_state.epoch_credits().len(), 0);
1414 vote_state.increment_credits(1, 1);
1415 assert_eq!(vote_state.epoch_credits().len(), 1);
1416
1417 vote_state.increment_credits(2, 1);
1418 assert_eq!(vote_state.epoch_credits().len(), 2);
1419 }
1420
1421 #[test]
1422 fn test_vote_state_increment_credits() {
1423 let mut vote_state = VoteState::default();
1424
1425 let credits = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
1426 for i in 0..credits {
1427 vote_state.increment_credits(i, 1);
1428 }
1429 assert_eq!(vote_state.credits(), credits);
1430 assert!(vote_state.epoch_credits().len() <= MAX_EPOCH_CREDITS_HISTORY);
1431 }
1432
1433 #[test]
1434 fn test_vote_process_timestamp() {
1435 let (slot, timestamp) = (15, 1_575_412_285);
1436 let mut vote_state = VoteState {
1437 last_timestamp: BlockTimestamp { slot, timestamp },
1438 ..VoteState::default()
1439 };
1440
1441 assert_eq!(
1442 vote_state.process_timestamp(slot - 1, timestamp + 1),
1443 Err(VoteError::TimestampTooOld)
1444 );
1445 assert_eq!(
1446 vote_state.last_timestamp,
1447 BlockTimestamp { slot, timestamp }
1448 );
1449 assert_eq!(
1450 vote_state.process_timestamp(slot + 1, timestamp - 1),
1451 Err(VoteError::TimestampTooOld)
1452 );
1453 assert_eq!(
1454 vote_state.process_timestamp(slot, timestamp + 1),
1455 Err(VoteError::TimestampTooOld)
1456 );
1457 assert_eq!(vote_state.process_timestamp(slot, timestamp), Ok(()));
1458 assert_eq!(
1459 vote_state.last_timestamp,
1460 BlockTimestamp { slot, timestamp }
1461 );
1462 assert_eq!(vote_state.process_timestamp(slot + 1, timestamp), Ok(()));
1463 assert_eq!(
1464 vote_state.last_timestamp,
1465 BlockTimestamp {
1466 slot: slot + 1,
1467 timestamp
1468 }
1469 );
1470 assert_eq!(
1471 vote_state.process_timestamp(slot + 2, timestamp + 1),
1472 Ok(())
1473 );
1474 assert_eq!(
1475 vote_state.last_timestamp,
1476 BlockTimestamp {
1477 slot: slot + 2,
1478 timestamp: timestamp + 1
1479 }
1480 );
1481
1482 vote_state.last_timestamp = BlockTimestamp::default();
1484 assert_eq!(vote_state.process_timestamp(0, timestamp), Ok(()));
1485 }
1486
1487 #[test]
1488 fn test_get_and_update_authorized_voter() {
1489 let original_voter = Pubkey::new_unique();
1490 let mut vote_state = VoteState::new(
1491 &VoteInit {
1492 node_pubkey: original_voter,
1493 authorized_voter: original_voter,
1494 authorized_withdrawer: original_voter,
1495 commission: 0,
1496 },
1497 &Clock::default(),
1498 );
1499
1500 assert_eq!(vote_state.authorized_voters.len(), 1);
1501 assert_eq!(
1502 *vote_state.authorized_voters.first().unwrap().1,
1503 original_voter
1504 );
1505
1506 assert_eq!(
1509 vote_state.get_and_update_authorized_voter(1).unwrap(),
1510 original_voter
1511 );
1512
1513 assert_eq!(
1516 vote_state.get_and_update_authorized_voter(5).unwrap(),
1517 original_voter
1518 );
1519
1520 assert_eq!(vote_state.authorized_voters.len(), 1);
1523 for i in 0..5 {
1524 assert!(vote_state
1525 .authorized_voters
1526 .get_authorized_voter(i)
1527 .is_none());
1528 }
1529
1530 let new_authorized_voter = Pubkey::new_unique();
1532 vote_state
1533 .set_new_authorized_voter(&new_authorized_voter, 5, 7, |_| Ok(()))
1534 .unwrap();
1535
1536 assert_eq!(
1538 vote_state.get_and_update_authorized_voter(6).unwrap(),
1539 original_voter
1540 );
1541
1542 for i in 7..10 {
1545 assert_eq!(
1546 vote_state.get_and_update_authorized_voter(i).unwrap(),
1547 new_authorized_voter
1548 );
1549 }
1550 assert_eq!(vote_state.authorized_voters.len(), 1);
1551 }
1552
1553 #[test]
1554 fn test_set_new_authorized_voter() {
1555 let original_voter = Pubkey::new_unique();
1556 let epoch_offset = 15;
1557 let mut vote_state = VoteState::new(
1558 &VoteInit {
1559 node_pubkey: original_voter,
1560 authorized_voter: original_voter,
1561 authorized_withdrawer: original_voter,
1562 commission: 0,
1563 },
1564 &Clock::default(),
1565 );
1566
1567 assert!(vote_state.prior_voters.last().is_none());
1568
1569 let new_voter = Pubkey::new_unique();
1570 vote_state
1572 .set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(()))
1573 .unwrap();
1574
1575 assert_eq!(vote_state.prior_voters.idx, 0);
1576 assert_eq!(
1577 vote_state.prior_voters.last(),
1578 Some(&(original_voter, 0, epoch_offset))
1579 );
1580
1581 assert_eq!(
1583 vote_state.set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(())),
1584 Err(VoteError::TooSoonToReauthorize.into())
1585 );
1586
1587 vote_state
1589 .set_new_authorized_voter(&new_voter, 2, 2 + epoch_offset, |_| Ok(()))
1590 .unwrap();
1591
1592 let new_voter2 = Pubkey::new_unique();
1594 vote_state
1595 .set_new_authorized_voter(&new_voter2, 3, 3 + epoch_offset, |_| Ok(()))
1596 .unwrap();
1597 assert_eq!(vote_state.prior_voters.idx, 1);
1598 assert_eq!(
1599 vote_state.prior_voters.last(),
1600 Some(&(new_voter, epoch_offset, 3 + epoch_offset))
1601 );
1602
1603 let new_voter3 = Pubkey::new_unique();
1604 vote_state
1605 .set_new_authorized_voter(&new_voter3, 6, 6 + epoch_offset, |_| Ok(()))
1606 .unwrap();
1607 assert_eq!(vote_state.prior_voters.idx, 2);
1608 assert_eq!(
1609 vote_state.prior_voters.last(),
1610 Some(&(new_voter2, 3 + epoch_offset, 6 + epoch_offset))
1611 );
1612
1613 vote_state
1615 .set_new_authorized_voter(&original_voter, 9, 9 + epoch_offset, |_| Ok(()))
1616 .unwrap();
1617
1618 for i in 9..epoch_offset {
1621 assert_eq!(
1622 vote_state.get_and_update_authorized_voter(i).unwrap(),
1623 original_voter
1624 );
1625 }
1626 for i in epoch_offset..3 + epoch_offset {
1627 assert_eq!(
1628 vote_state.get_and_update_authorized_voter(i).unwrap(),
1629 new_voter
1630 );
1631 }
1632 for i in 3 + epoch_offset..6 + epoch_offset {
1633 assert_eq!(
1634 vote_state.get_and_update_authorized_voter(i).unwrap(),
1635 new_voter2
1636 );
1637 }
1638 for i in 6 + epoch_offset..9 + epoch_offset {
1639 assert_eq!(
1640 vote_state.get_and_update_authorized_voter(i).unwrap(),
1641 new_voter3
1642 );
1643 }
1644 for i in 9 + epoch_offset..=10 + epoch_offset {
1645 assert_eq!(
1646 vote_state.get_and_update_authorized_voter(i).unwrap(),
1647 original_voter
1648 );
1649 }
1650 }
1651
1652 #[test]
1653 fn test_authorized_voter_is_locked_within_epoch() {
1654 let original_voter = Pubkey::new_unique();
1655 let mut vote_state = VoteState::new(
1656 &VoteInit {
1657 node_pubkey: original_voter,
1658 authorized_voter: original_voter,
1659 authorized_withdrawer: original_voter,
1660 commission: 0,
1661 },
1662 &Clock::default(),
1663 );
1664
1665 let new_voter = Pubkey::new_unique();
1669 assert_eq!(
1670 vote_state.set_new_authorized_voter(&new_voter, 1, 1, |_| Ok(())),
1671 Err(VoteError::TooSoonToReauthorize.into())
1672 );
1673
1674 assert_eq!(vote_state.get_authorized_voter(1), Some(original_voter));
1675
1676 assert_eq!(
1678 vote_state.set_new_authorized_voter(&new_voter, 1, 2, |_| Ok(())),
1679 Ok(())
1680 );
1681
1682 assert_eq!(
1686 vote_state.set_new_authorized_voter(&original_voter, 3, 3, |_| Ok(())),
1687 Err(VoteError::TooSoonToReauthorize.into())
1688 );
1689
1690 assert_eq!(vote_state.get_authorized_voter(3), Some(new_voter));
1691 }
1692
1693 #[test]
1694 fn test_vote_state_size_of() {
1695 let vote_state = VoteState::get_max_sized_vote_state();
1696 let vote_state = VoteStateVersions::new_current(vote_state);
1697 let size = serialized_size(&vote_state).unwrap();
1698 assert_eq!(VoteState::size_of() as u64, size);
1699 }
1700
1701 #[test]
1702 fn test_vote_state_max_size() {
1703 let mut max_sized_data = vec![0; VoteState::size_of()];
1704 let vote_state = VoteState::get_max_sized_vote_state();
1705 let (start_leader_schedule_epoch, _) = vote_state.authorized_voters.last().unwrap();
1706 let start_current_epoch =
1707 start_leader_schedule_epoch - MAX_LEADER_SCHEDULE_EPOCH_OFFSET + 1;
1708
1709 let mut vote_state = Some(vote_state);
1710 for i in start_current_epoch..start_current_epoch + 2 * MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
1711 vote_state.as_mut().map(|vote_state| {
1712 vote_state.set_new_authorized_voter(
1713 &Pubkey::new_unique(),
1714 i,
1715 i + MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
1716 |_| Ok(()),
1717 )
1718 });
1719
1720 let versioned = VoteStateVersions::new_current(vote_state.take().unwrap());
1721 VoteState::serialize(&versioned, &mut max_sized_data).unwrap();
1722 vote_state = Some(versioned.convert_to_current());
1723 }
1724 }
1725
1726 #[test]
1727 fn test_default_vote_state_is_uninitialized() {
1728 assert!(VoteStateVersions::new_current(VoteState::default()).is_uninitialized());
1732 }
1733
1734 #[test]
1735 fn test_is_correct_size_and_initialized() {
1736 let mut vote_account_data = vec![0; VoteStateVersions::vote_state_size_of(true)];
1738 assert!(!VoteStateVersions::is_correct_size_and_initialized(
1739 &vote_account_data
1740 ));
1741
1742 let default_account_state = VoteStateVersions::new_current(VoteState::default());
1744 VoteState::serialize(&default_account_state, &mut vote_account_data).unwrap();
1745 assert!(!VoteStateVersions::is_correct_size_and_initialized(
1746 &vote_account_data
1747 ));
1748
1749 let short_data = vec![1; DEFAULT_PRIOR_VOTERS_OFFSET];
1751 assert!(!VoteStateVersions::is_correct_size_and_initialized(
1752 &short_data
1753 ));
1754
1755 let mut large_vote_data = vec![1; 2 * VoteStateVersions::vote_state_size_of(true)];
1757 let default_account_state = VoteStateVersions::new_current(VoteState::default());
1758 VoteState::serialize(&default_account_state, &mut large_vote_data).unwrap();
1759 assert!(!VoteStateVersions::is_correct_size_and_initialized(
1760 &vote_account_data
1761 ));
1762
1763 let vote_state = VoteState::new(
1765 &VoteInit {
1766 node_pubkey: Pubkey::new_unique(),
1767 authorized_voter: Pubkey::new_unique(),
1768 authorized_withdrawer: Pubkey::new_unique(),
1769 commission: 0,
1770 },
1771 &Clock::default(),
1772 );
1773 let account_state = VoteStateVersions::new_current(vote_state.clone());
1774 VoteState::serialize(&account_state, &mut vote_account_data).unwrap();
1775 assert!(VoteStateVersions::is_correct_size_and_initialized(
1776 &vote_account_data
1777 ));
1778
1779 let old_vote_state = VoteState1_14_11::from(vote_state);
1781 let account_state = VoteStateVersions::V1_14_11(Box::new(old_vote_state));
1782 let mut vote_account_data = vec![0; VoteStateVersions::vote_state_size_of(false)];
1783 VoteState::serialize(&account_state, &mut vote_account_data).unwrap();
1784 assert!(VoteStateVersions::is_correct_size_and_initialized(
1785 &vote_account_data
1786 ));
1787 }
1788
1789 #[test]
1790 fn test_minimum_balance() {
1791 let rent = solana_rent::Rent::default();
1792 let minimum_balance = rent.minimum_balance(VoteState::size_of());
1793 assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
1795 }
1796
1797 #[test]
1798 fn test_serde_compact_vote_state_update() {
1799 let mut rng = rand::thread_rng();
1800 for _ in 0..5000 {
1801 run_serde_compact_vote_state_update(&mut rng);
1802 }
1803 }
1804
1805 fn run_serde_compact_vote_state_update<R: Rng>(rng: &mut R) {
1806 let lockouts: VecDeque<_> = std::iter::repeat_with(|| {
1807 let slot = 149_303_885_u64.saturating_add(rng.gen_range(0..10_000));
1808 let confirmation_count = rng.gen_range(0..33);
1809 Lockout::new_with_confirmation_count(slot, confirmation_count)
1810 })
1811 .take(32)
1812 .sorted_by_key(|lockout| lockout.slot())
1813 .collect();
1814 let root = rng.gen_ratio(1, 2).then(|| {
1815 lockouts[0]
1816 .slot()
1817 .checked_sub(rng.gen_range(0..1_000))
1818 .expect("All slots should be greater than 1_000")
1819 });
1820 let timestamp = rng.gen_ratio(1, 2).then(|| rng.gen());
1821 let hash = Hash::from(rng.gen::<[u8; 32]>());
1822 let vote_state_update = VoteStateUpdate {
1823 lockouts,
1824 root,
1825 hash,
1826 timestamp,
1827 };
1828 #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
1829 enum VoteInstruction {
1830 #[serde(with = "serde_compact_vote_state_update")]
1831 UpdateVoteState(VoteStateUpdate),
1832 UpdateVoteStateSwitch(
1833 #[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate,
1834 Hash,
1835 ),
1836 }
1837 let vote = VoteInstruction::UpdateVoteState(vote_state_update.clone());
1838 let bytes = bincode::serialize(&vote).unwrap();
1839 assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1840 let hash = Hash::from(rng.gen::<[u8; 32]>());
1841 let vote = VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash);
1842 let bytes = bincode::serialize(&vote).unwrap();
1843 assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1844 }
1845
1846 #[test]
1847 fn test_circbuf_oob() {
1848 let data: &[u8] = &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00];
1850 let circ_buf: CircBuf<()> = bincode::deserialize(data).unwrap();
1851 assert_eq!(circ_buf.last(), None);
1852 }
1853}