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