1#[cfg(test)]
4use crate::epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET;
5use {
6 crate::{
7 clock::{Epoch, Slot, UnixTimestamp},
8 hash::Hash,
9 instruction::InstructionError,
10 pubkey::Pubkey,
11 rent::Rent,
12 sysvar::clock::Clock,
13 vote::{authorized_voters::AuthorizedVoters, error::VoteError},
14 },
15 bincode::{deserialize, serialize_into, ErrorKind},
16 serde_derive::{Deserialize, Serialize},
17 std::{collections::VecDeque, fmt::Debug},
18};
19
20mod vote_state_0_23_5;
21pub mod vote_state_versions;
22pub use vote_state_versions::*;
23
24pub const MAX_LOCKOUT_HISTORY: usize = 31;
26pub const INITIAL_LOCKOUT: usize = 2;
27
28pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
30
31const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 82;
33
34#[frozen_abi(digest = "Ch2vVEwos2EjAVqSHCyJjnN2MNX1yrpapZTGhMSCjWUH")]
35#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
36pub struct Vote {
37 pub slots: Vec<Slot>,
39 pub hash: Hash,
41 pub timestamp: Option<UnixTimestamp>,
43}
44
45impl Vote {
46 pub fn new(slots: Vec<Slot>, hash: Hash) -> Self {
47 Self {
48 slots,
49 hash,
50 timestamp: None,
51 }
52 }
53
54 pub fn last_voted_slot(&self) -> Option<Slot> {
55 self.slots.last().copied()
56 }
57}
58
59#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)]
60pub struct Lockout {
61 slot: Slot,
62 confirmation_count: u32,
63}
64
65impl Lockout {
66 pub fn new(slot: Slot) -> Self {
67 Self::new_with_confirmation_count(slot, 1)
68 }
69
70 pub fn new_with_confirmation_count(slot: Slot, confirmation_count: u32) -> Self {
71 Self {
72 slot,
73 confirmation_count,
74 }
75 }
76
77 pub fn lockout(&self) -> u64 {
79 (INITIAL_LOCKOUT as u64).pow(self.confirmation_count())
80 }
81
82 pub fn last_locked_out_slot(&self) -> Slot {
86 self.slot.saturating_add(self.lockout())
87 }
88
89 pub fn is_locked_out_at_slot(&self, slot: Slot) -> bool {
90 self.last_locked_out_slot() >= slot
91 }
92
93 pub fn slot(&self) -> Slot {
94 self.slot
95 }
96
97 pub fn confirmation_count(&self) -> u32 {
98 self.confirmation_count
99 }
100
101 pub fn increase_confirmation_count(&mut self, by: u32) {
102 self.confirmation_count = self.confirmation_count.saturating_add(by)
103 }
104}
105
106#[frozen_abi(digest = "GwJfVFsATSj7nvKwtUkHYzqPRaPY6SLxPGXApuCya3x5")]
107#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
108pub struct VoteStateUpdate {
109 pub lockouts: VecDeque<Lockout>,
111 pub root: Option<Slot>,
113 pub hash: Hash,
115 pub timestamp: Option<UnixTimestamp>,
117}
118
119impl From<Vec<(Slot, u32)>> for VoteStateUpdate {
120 fn from(recent_slots: Vec<(Slot, u32)>) -> Self {
121 let lockouts: VecDeque<Lockout> = recent_slots
122 .into_iter()
123 .map(|(slot, confirmation_count)| {
124 Lockout::new_with_confirmation_count(slot, confirmation_count)
125 })
126 .collect();
127 Self {
128 lockouts,
129 root: None,
130 hash: Hash::default(),
131 timestamp: None,
132 }
133 }
134}
135
136impl VoteStateUpdate {
137 pub fn new(lockouts: VecDeque<Lockout>, root: Option<Slot>, hash: Hash) -> Self {
138 Self {
139 lockouts,
140 root,
141 hash,
142 timestamp: None,
143 }
144 }
145
146 pub fn slots(&self) -> Vec<Slot> {
147 self.lockouts.iter().map(|lockout| lockout.slot()).collect()
148 }
149
150 pub fn last_voted_slot(&self) -> Option<Slot> {
151 self.lockouts.back().map(|l| l.slot())
152 }
153}
154
155#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
156pub struct VoteInit {
157 pub node_pubkey: Pubkey,
158 pub authorized_voter: Pubkey,
159 pub authorized_withdrawer: Pubkey,
160 pub commission: u8,
161}
162
163#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
164pub enum VoteAuthorize {
165 Voter,
166 Withdrawer,
167}
168
169#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
170pub struct VoteAuthorizeWithSeedArgs {
171 pub authorization_type: VoteAuthorize,
172 pub current_authority_derived_key_owner: Pubkey,
173 pub current_authority_derived_key_seed: String,
174 pub new_authority: Pubkey,
175}
176
177#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
178pub struct VoteAuthorizeCheckedWithSeedArgs {
179 pub authorization_type: VoteAuthorize,
180 pub current_authority_derived_key_owner: Pubkey,
181 pub current_authority_derived_key_seed: String,
182}
183
184#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
185pub struct BlockTimestamp {
186 pub slot: Slot,
187 pub timestamp: UnixTimestamp,
188}
189
190const MAX_ITEMS: usize = 32;
192
193#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
194pub struct CircBuf<I> {
195 buf: [I; MAX_ITEMS],
196 idx: usize,
198 is_empty: bool,
199}
200
201impl<I: Default + Copy> Default for CircBuf<I> {
202 fn default() -> Self {
203 Self {
204 buf: [I::default(); MAX_ITEMS],
205 idx: MAX_ITEMS
206 .checked_sub(1)
207 .expect("`MAX_ITEMS` should be positive"),
208 is_empty: true,
209 }
210 }
211}
212
213impl<I> CircBuf<I> {
214 pub fn append(&mut self, item: I) {
215 self.idx = self
217 .idx
218 .checked_add(1)
219 .and_then(|idx| idx.checked_rem(MAX_ITEMS))
220 .expect("`self.idx` should be < `MAX_ITEMS` which should be non-zero");
221
222 self.buf[self.idx] = item;
223 self.is_empty = false;
224 }
225
226 pub fn buf(&self) -> &[I; MAX_ITEMS] {
227 &self.buf
228 }
229
230 pub fn last(&self) -> Option<&I> {
231 if !self.is_empty {
232 Some(&self.buf[self.idx])
233 } else {
234 None
235 }
236 }
237}
238
239#[frozen_abi(digest = "4oxo6mBc8zrZFA89RgKsNyMqqM52iVrCphsWfaHjaAAY")]
240#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
241pub struct VoteState {
242 pub node_pubkey: Pubkey,
244
245 pub authorized_withdrawer: Pubkey,
247 pub commission: u8,
250
251 pub votes: VecDeque<Lockout>,
252
253 pub root_slot: Option<Slot>,
256
257 authorized_voters: AuthorizedVoters,
259
260 prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>,
264
265 pub epoch_credits: Vec<(Epoch, u64, u64)>,
268
269 pub last_timestamp: BlockTimestamp,
271}
272
273impl VoteState {
274 pub fn new(vote_init: &VoteInit, clock: &Clock) -> Self {
275 Self {
276 node_pubkey: vote_init.node_pubkey,
277 authorized_voters: AuthorizedVoters::new(clock.epoch, vote_init.authorized_voter),
278 authorized_withdrawer: vote_init.authorized_withdrawer,
279 commission: vote_init.commission,
280 ..VoteState::default()
281 }
282 }
283
284 pub fn get_authorized_voter(&self, epoch: Epoch) -> Option<Pubkey> {
285 self.authorized_voters.get_authorized_voter(epoch)
286 }
287
288 pub fn authorized_voters(&self) -> &AuthorizedVoters {
289 &self.authorized_voters
290 }
291
292 pub fn prior_voters(&mut self) -> &CircBuf<(Pubkey, Epoch, Epoch)> {
293 &self.prior_voters
294 }
295
296 pub fn get_rent_exempt_reserve(rent: &Rent) -> u64 {
297 rent.minimum_balance(VoteState::size_of())
298 }
299
300 pub const fn size_of() -> usize {
303 3731 }
305
306 pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
307 deserialize::<VoteStateVersions>(input)
308 .map(|versioned| versioned.convert_to_current())
309 .map_err(|_| InstructionError::InvalidAccountData)
310 }
311
312 pub fn serialize(
313 versioned: &VoteStateVersions,
314 output: &mut [u8],
315 ) -> Result<(), InstructionError> {
316 serialize_into(output, versioned).map_err(|err| match *err {
317 ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
318 _ => InstructionError::GenericError,
319 })
320 }
321
322 pub fn commission_split(&self, on: u64) -> (u64, u64, bool) {
327 match self.commission.min(100) {
328 0 => (0, on, false),
329 100 => (on, 0, false),
330 split => {
331 let on = u128::from(on);
332 let mine = on
338 .checked_mul(u128::from(split))
339 .expect("multiplication of a u64 and u8 should not overflow")
340 / 100u128;
341 let theirs = on
342 .checked_mul(u128::from(
343 100u8
344 .checked_sub(split)
345 .expect("commission cannot be greater than 100"),
346 ))
347 .expect("multiplication of a u64 and u8 should not overflow")
348 / 100u128;
349
350 (mine as u64, theirs as u64, true)
351 }
352 }
353 }
354
355 pub fn contains_slot(&self, candidate_slot: Slot) -> bool {
357 self.votes
358 .binary_search_by(|lockout| lockout.slot().cmp(&candidate_slot))
359 .is_ok()
360 }
361
362 #[cfg(test)]
363 fn get_max_sized_vote_state() -> VoteState {
364 let mut authorized_voters = AuthorizedVoters::default();
365 for i in 0..=MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
366 authorized_voters.insert(i, Pubkey::new_unique());
367 }
368
369 VoteState {
370 votes: VecDeque::from(vec![Lockout::default(); MAX_LOCKOUT_HISTORY]),
371 root_slot: Some(std::u64::MAX),
372 epoch_credits: vec![(0, 0, 0); MAX_EPOCH_CREDITS_HISTORY],
373 authorized_voters,
374 ..Self::default()
375 }
376 }
377
378 pub fn process_next_vote_slot(&mut self, next_vote_slot: Slot, epoch: Epoch) {
379 if self
381 .last_voted_slot()
382 .map_or(false, |last_voted_slot| next_vote_slot <= last_voted_slot)
383 {
384 return;
385 }
386
387 let vote = Lockout::new(next_vote_slot);
388
389 self.pop_expired_votes(next_vote_slot);
390
391 if self.votes.len() == MAX_LOCKOUT_HISTORY {
393 let vote = self.votes.pop_front().unwrap();
394 self.root_slot = Some(vote.slot());
395
396 self.increment_credits(epoch, 1);
397 }
398 self.votes.push_back(vote);
399 self.double_lockouts();
400 }
401
402 pub fn increment_credits(&mut self, epoch: Epoch, credits: u64) {
404 if self.epoch_credits.is_empty() {
408 self.epoch_credits.push((epoch, 0, 0));
409 } else if epoch != self.epoch_credits.last().unwrap().0 {
410 let (_, credits, prev_credits) = *self.epoch_credits.last().unwrap();
411
412 if credits != prev_credits {
413 self.epoch_credits.push((epoch, credits, credits));
416 } else {
417 self.epoch_credits.last_mut().unwrap().0 = epoch;
419 }
420
421 if self.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY {
423 self.epoch_credits.remove(0);
424 }
425 }
426
427 self.epoch_credits.last_mut().unwrap().1 =
428 self.epoch_credits.last().unwrap().1.saturating_add(credits);
429 }
430
431 pub fn nth_recent_vote(&self, position: usize) -> Option<&Lockout> {
432 if position < self.votes.len() {
433 let pos = self
434 .votes
435 .len()
436 .checked_sub(position)
437 .and_then(|pos| pos.checked_sub(1))?;
438 self.votes.get(pos)
439 } else {
440 None
441 }
442 }
443
444 pub fn last_lockout(&self) -> Option<&Lockout> {
445 self.votes.back()
446 }
447
448 pub fn last_voted_slot(&self) -> Option<Slot> {
449 self.last_lockout().map(|v| v.slot())
450 }
451
452 pub fn tower(&self) -> Vec<Slot> {
455 self.votes.iter().map(|v| v.slot()).collect()
456 }
457
458 pub fn current_epoch(&self) -> Epoch {
459 if self.epoch_credits.is_empty() {
460 0
461 } else {
462 self.epoch_credits.last().unwrap().0
463 }
464 }
465
466 pub fn credits(&self) -> u64 {
469 if self.epoch_credits.is_empty() {
470 0
471 } else {
472 self.epoch_credits.last().unwrap().1
473 }
474 }
475
476 pub fn epoch_credits(&self) -> &Vec<(Epoch, u64, u64)> {
482 &self.epoch_credits
483 }
484
485 pub fn set_new_authorized_voter<F>(
486 &mut self,
487 authorized_pubkey: &Pubkey,
488 current_epoch: Epoch,
489 target_epoch: Epoch,
490 verify: F,
491 ) -> Result<(), InstructionError>
492 where
493 F: Fn(Pubkey) -> Result<(), InstructionError>,
494 {
495 let epoch_authorized_voter = self.get_and_update_authorized_voter(current_epoch)?;
496 verify(epoch_authorized_voter)?;
497
498 if self.authorized_voters.contains(target_epoch) {
504 return Err(VoteError::TooSoonToReauthorize.into());
505 }
506
507 let (latest_epoch, latest_authorized_pubkey) = self
509 .authorized_voters
510 .last()
511 .ok_or(InstructionError::InvalidAccountData)?;
512
513 if latest_authorized_pubkey != authorized_pubkey {
517 let epoch_of_last_authorized_switch =
519 self.prior_voters.last().map(|range| range.2).unwrap_or(0);
520
521 assert!(target_epoch > *latest_epoch);
528
529 self.prior_voters.append((
531 *latest_authorized_pubkey,
532 epoch_of_last_authorized_switch,
533 target_epoch,
534 ));
535 }
536
537 self.authorized_voters
538 .insert(target_epoch, *authorized_pubkey);
539
540 Ok(())
541 }
542
543 pub fn get_and_update_authorized_voter(
544 &mut self,
545 current_epoch: Epoch,
546 ) -> Result<Pubkey, InstructionError> {
547 let pubkey = self
548 .authorized_voters
549 .get_and_cache_authorized_voter_for_epoch(current_epoch)
550 .ok_or(InstructionError::InvalidAccountData)?;
551 self.authorized_voters
552 .purge_authorized_voters(current_epoch);
553 Ok(pubkey)
554 }
555
556 pub fn pop_expired_votes(&mut self, next_vote_slot: Slot) {
561 while let Some(vote) = self.last_lockout() {
562 if !vote.is_locked_out_at_slot(next_vote_slot) {
563 self.votes.pop_back();
564 } else {
565 break;
566 }
567 }
568 }
569
570 pub fn double_lockouts(&mut self) {
571 let stack_depth = self.votes.len();
572 for (i, v) in self.votes.iter_mut().enumerate() {
573 if stack_depth > i.checked_add(v.confirmation_count() as usize).expect("`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`") {
576 v.increase_confirmation_count(1);
577 }
578 }
579 }
580
581 pub fn process_timestamp(
582 &mut self,
583 slot: Slot,
584 timestamp: UnixTimestamp,
585 ) -> Result<(), VoteError> {
586 if (slot < self.last_timestamp.slot || timestamp < self.last_timestamp.timestamp)
587 || (slot == self.last_timestamp.slot
588 && BlockTimestamp { slot, timestamp } != self.last_timestamp
589 && self.last_timestamp.slot != 0)
590 {
591 return Err(VoteError::TimestampTooOld);
592 }
593 self.last_timestamp = BlockTimestamp { slot, timestamp };
594 Ok(())
595 }
596
597 pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
598 const VERSION_OFFSET: usize = 4;
599 const DEFAULT_PRIOR_VOTERS_END: usize = VERSION_OFFSET + DEFAULT_PRIOR_VOTERS_OFFSET;
600 data.len() == VoteState::size_of()
601 && data[VERSION_OFFSET..DEFAULT_PRIOR_VOTERS_END] != [0; DEFAULT_PRIOR_VOTERS_OFFSET]
602 }
603}
604
605pub mod serde_compact_vote_state_update {
606 use {
607 super::*,
608 crate::{
609 clock::{Slot, UnixTimestamp},
610 serde_varint, short_vec,
611 vote::state::Lockout,
612 },
613 serde::{Deserialize, Deserializer, Serialize, Serializer},
614 };
615
616 #[derive(Deserialize, Serialize, AbiExample)]
617 struct LockoutOffset {
618 #[serde(with = "serde_varint")]
619 offset: Slot,
620 confirmation_count: u8,
621 }
622
623 #[derive(Deserialize, Serialize)]
624 struct CompactVoteStateUpdate {
625 root: Slot,
626 #[serde(with = "short_vec")]
627 lockout_offsets: Vec<LockoutOffset>,
628 hash: Hash,
629 timestamp: Option<UnixTimestamp>,
630 }
631
632 pub fn serialize<S>(
633 vote_state_update: &VoteStateUpdate,
634 serializer: S,
635 ) -> Result<S::Ok, S::Error>
636 where
637 S: Serializer,
638 {
639 let lockout_offsets = vote_state_update.lockouts.iter().scan(
640 vote_state_update.root.unwrap_or_default(),
641 |slot, lockout| {
642 let offset = match lockout.slot().checked_sub(*slot) {
643 None => return Some(Err(serde::ser::Error::custom("Invalid vote lockout"))),
644 Some(offset) => offset,
645 };
646 let confirmation_count = match u8::try_from(lockout.confirmation_count()) {
647 Ok(confirmation_count) => confirmation_count,
648 Err(_) => {
649 return Some(Err(serde::ser::Error::custom("Invalid confirmation count")))
650 }
651 };
652 let lockout_offset = LockoutOffset {
653 offset,
654 confirmation_count,
655 };
656 *slot = lockout.slot();
657 Some(Ok(lockout_offset))
658 },
659 );
660 let compact_vote_state_update = CompactVoteStateUpdate {
661 root: vote_state_update.root.unwrap_or(Slot::MAX),
662 lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
663 hash: vote_state_update.hash,
664 timestamp: vote_state_update.timestamp,
665 };
666 compact_vote_state_update.serialize(serializer)
667 }
668
669 pub fn deserialize<'de, D>(deserializer: D) -> Result<VoteStateUpdate, D::Error>
670 where
671 D: Deserializer<'de>,
672 {
673 let CompactVoteStateUpdate {
674 root,
675 lockout_offsets,
676 hash,
677 timestamp,
678 } = CompactVoteStateUpdate::deserialize(deserializer)?;
679 let root = (root != Slot::MAX).then_some(root);
680 let lockouts =
681 lockout_offsets
682 .iter()
683 .scan(root.unwrap_or_default(), |slot, lockout_offset| {
684 *slot = match slot.checked_add(lockout_offset.offset) {
685 None => {
686 return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
687 }
688 Some(slot) => slot,
689 };
690 let lockout = Lockout::new_with_confirmation_count(
691 *slot,
692 u32::from(lockout_offset.confirmation_count),
693 );
694 Some(Ok(lockout))
695 });
696 Ok(VoteStateUpdate {
697 root,
698 lockouts: lockouts.collect::<Result<_, _>>()?,
699 hash,
700 timestamp,
701 })
702 }
703}
704
705#[cfg(test)]
706mod tests {
707 use {super::*, itertools::Itertools, rand::Rng};
708
709 #[test]
710 fn test_vote_serialize() {
711 let mut buffer: Vec<u8> = vec![0; VoteState::size_of()];
712 let mut vote_state = VoteState::default();
713 vote_state
714 .votes
715 .resize(MAX_LOCKOUT_HISTORY, Lockout::default());
716 vote_state.root_slot = Some(1);
717 let versioned = VoteStateVersions::new_current(vote_state);
718 assert!(VoteState::serialize(&versioned, &mut buffer[0..4]).is_err());
719 VoteState::serialize(&versioned, &mut buffer).unwrap();
720 assert_eq!(
721 VoteState::deserialize(&buffer).unwrap(),
722 versioned.convert_to_current()
723 );
724 }
725
726 #[test]
727 fn test_vote_state_commission_split() {
728 let vote_state = VoteState::default();
729
730 assert_eq!(vote_state.commission_split(1), (0, 1, false));
731
732 let mut vote_state = VoteState {
733 commission: std::u8::MAX,
734 ..VoteState::default()
735 };
736 assert_eq!(vote_state.commission_split(1), (1, 0, false));
737
738 vote_state.commission = 99;
739 assert_eq!(vote_state.commission_split(10), (9, 0, true));
740
741 vote_state.commission = 1;
742 assert_eq!(vote_state.commission_split(10), (0, 9, true));
743
744 vote_state.commission = 50;
745 let (voter_portion, staker_portion, was_split) = vote_state.commission_split(10);
746
747 assert_eq!((voter_portion, staker_portion, was_split), (5, 5, true));
748 }
749
750 #[test]
751 fn test_vote_state_epoch_credits() {
752 let mut vote_state = VoteState::default();
753
754 assert_eq!(vote_state.credits(), 0);
755 assert_eq!(vote_state.epoch_credits().clone(), vec![]);
756
757 let mut expected = vec![];
758 let mut credits = 0;
759 let epochs = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
760 for epoch in 0..epochs {
761 for _j in 0..epoch {
762 vote_state.increment_credits(epoch, 1);
763 credits += 1;
764 }
765 expected.push((epoch, credits, credits - epoch));
766 }
767
768 while expected.len() > MAX_EPOCH_CREDITS_HISTORY {
769 expected.remove(0);
770 }
771
772 assert_eq!(vote_state.credits(), credits);
773 assert_eq!(vote_state.epoch_credits().clone(), expected);
774 }
775
776 #[test]
777 fn test_vote_state_epoch0_no_credits() {
778 let mut vote_state = VoteState::default();
779
780 assert_eq!(vote_state.epoch_credits().len(), 0);
781 vote_state.increment_credits(1, 1);
782 assert_eq!(vote_state.epoch_credits().len(), 1);
783
784 vote_state.increment_credits(2, 1);
785 assert_eq!(vote_state.epoch_credits().len(), 2);
786 }
787
788 #[test]
789 fn test_vote_state_increment_credits() {
790 let mut vote_state = VoteState::default();
791
792 let credits = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
793 for i in 0..credits {
794 vote_state.increment_credits(i, 1);
795 }
796 assert_eq!(vote_state.credits(), credits);
797 assert!(vote_state.epoch_credits().len() <= MAX_EPOCH_CREDITS_HISTORY);
798 }
799
800 #[test]
801 fn test_vote_process_timestamp() {
802 let (slot, timestamp) = (15, 1_575_412_285);
803 let mut vote_state = VoteState {
804 last_timestamp: BlockTimestamp { slot, timestamp },
805 ..VoteState::default()
806 };
807
808 assert_eq!(
809 vote_state.process_timestamp(slot - 1, timestamp + 1),
810 Err(VoteError::TimestampTooOld)
811 );
812 assert_eq!(
813 vote_state.last_timestamp,
814 BlockTimestamp { slot, timestamp }
815 );
816 assert_eq!(
817 vote_state.process_timestamp(slot + 1, timestamp - 1),
818 Err(VoteError::TimestampTooOld)
819 );
820 assert_eq!(
821 vote_state.process_timestamp(slot, timestamp + 1),
822 Err(VoteError::TimestampTooOld)
823 );
824 assert_eq!(vote_state.process_timestamp(slot, timestamp), Ok(()));
825 assert_eq!(
826 vote_state.last_timestamp,
827 BlockTimestamp { slot, timestamp }
828 );
829 assert_eq!(vote_state.process_timestamp(slot + 1, timestamp), Ok(()));
830 assert_eq!(
831 vote_state.last_timestamp,
832 BlockTimestamp {
833 slot: slot + 1,
834 timestamp
835 }
836 );
837 assert_eq!(
838 vote_state.process_timestamp(slot + 2, timestamp + 1),
839 Ok(())
840 );
841 assert_eq!(
842 vote_state.last_timestamp,
843 BlockTimestamp {
844 slot: slot + 2,
845 timestamp: timestamp + 1
846 }
847 );
848
849 vote_state.last_timestamp = BlockTimestamp::default();
851 assert_eq!(vote_state.process_timestamp(0, timestamp), Ok(()));
852 }
853
854 #[test]
855 fn test_get_and_update_authorized_voter() {
856 let original_voter = Pubkey::new_unique();
857 let mut vote_state = VoteState::new(
858 &VoteInit {
859 node_pubkey: original_voter,
860 authorized_voter: original_voter,
861 authorized_withdrawer: original_voter,
862 commission: 0,
863 },
864 &Clock::default(),
865 );
866
867 assert_eq!(vote_state.authorized_voters.len(), 1);
868 assert_eq!(
869 *vote_state.authorized_voters.first().unwrap().1,
870 original_voter
871 );
872
873 assert_eq!(
876 vote_state.get_and_update_authorized_voter(1).unwrap(),
877 original_voter
878 );
879
880 assert_eq!(
883 vote_state.get_and_update_authorized_voter(5).unwrap(),
884 original_voter
885 );
886
887 assert_eq!(vote_state.authorized_voters.len(), 1);
890 for i in 0..5 {
891 assert!(vote_state
892 .authorized_voters
893 .get_authorized_voter(i)
894 .is_none());
895 }
896
897 let new_authorized_voter = Pubkey::new_unique();
899 vote_state
900 .set_new_authorized_voter(&new_authorized_voter, 5, 7, |_| Ok(()))
901 .unwrap();
902
903 assert_eq!(
905 vote_state.get_and_update_authorized_voter(6).unwrap(),
906 original_voter
907 );
908
909 for i in 7..10 {
912 assert_eq!(
913 vote_state.get_and_update_authorized_voter(i).unwrap(),
914 new_authorized_voter
915 );
916 }
917 assert_eq!(vote_state.authorized_voters.len(), 1);
918 }
919
920 #[test]
921 fn test_set_new_authorized_voter() {
922 let original_voter = Pubkey::new_unique();
923 let epoch_offset = 15;
924 let mut vote_state = VoteState::new(
925 &VoteInit {
926 node_pubkey: original_voter,
927 authorized_voter: original_voter,
928 authorized_withdrawer: original_voter,
929 commission: 0,
930 },
931 &Clock::default(),
932 );
933
934 assert!(vote_state.prior_voters.last().is_none());
935
936 let new_voter = Pubkey::new_unique();
937 vote_state
939 .set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(()))
940 .unwrap();
941
942 assert_eq!(vote_state.prior_voters.idx, 0);
943 assert_eq!(
944 vote_state.prior_voters.last(),
945 Some(&(original_voter, 0, epoch_offset))
946 );
947
948 assert_eq!(
950 vote_state.set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(())),
951 Err(VoteError::TooSoonToReauthorize.into())
952 );
953
954 vote_state
956 .set_new_authorized_voter(&new_voter, 2, 2 + epoch_offset, |_| Ok(()))
957 .unwrap();
958
959 let new_voter2 = Pubkey::new_unique();
961 vote_state
962 .set_new_authorized_voter(&new_voter2, 3, 3 + epoch_offset, |_| Ok(()))
963 .unwrap();
964 assert_eq!(vote_state.prior_voters.idx, 1);
965 assert_eq!(
966 vote_state.prior_voters.last(),
967 Some(&(new_voter, epoch_offset, 3 + epoch_offset))
968 );
969
970 let new_voter3 = Pubkey::new_unique();
971 vote_state
972 .set_new_authorized_voter(&new_voter3, 6, 6 + epoch_offset, |_| Ok(()))
973 .unwrap();
974 assert_eq!(vote_state.prior_voters.idx, 2);
975 assert_eq!(
976 vote_state.prior_voters.last(),
977 Some(&(new_voter2, 3 + epoch_offset, 6 + epoch_offset))
978 );
979
980 vote_state
982 .set_new_authorized_voter(&original_voter, 9, 9 + epoch_offset, |_| Ok(()))
983 .unwrap();
984
985 for i in 9..epoch_offset {
988 assert_eq!(
989 vote_state.get_and_update_authorized_voter(i).unwrap(),
990 original_voter
991 );
992 }
993 for i in epoch_offset..3 + epoch_offset {
994 assert_eq!(
995 vote_state.get_and_update_authorized_voter(i).unwrap(),
996 new_voter
997 );
998 }
999 for i in 3 + epoch_offset..6 + epoch_offset {
1000 assert_eq!(
1001 vote_state.get_and_update_authorized_voter(i).unwrap(),
1002 new_voter2
1003 );
1004 }
1005 for i in 6 + epoch_offset..9 + epoch_offset {
1006 assert_eq!(
1007 vote_state.get_and_update_authorized_voter(i).unwrap(),
1008 new_voter3
1009 );
1010 }
1011 for i in 9 + epoch_offset..=10 + epoch_offset {
1012 assert_eq!(
1013 vote_state.get_and_update_authorized_voter(i).unwrap(),
1014 original_voter
1015 );
1016 }
1017 }
1018
1019 #[test]
1020 fn test_authorized_voter_is_locked_within_epoch() {
1021 let original_voter = Pubkey::new_unique();
1022 let mut vote_state = VoteState::new(
1023 &VoteInit {
1024 node_pubkey: original_voter,
1025 authorized_voter: original_voter,
1026 authorized_withdrawer: original_voter,
1027 commission: 0,
1028 },
1029 &Clock::default(),
1030 );
1031
1032 let new_voter = Pubkey::new_unique();
1036 assert_eq!(
1037 vote_state.set_new_authorized_voter(&new_voter, 1, 1, |_| Ok(())),
1038 Err(VoteError::TooSoonToReauthorize.into())
1039 );
1040
1041 assert_eq!(vote_state.get_authorized_voter(1), Some(original_voter));
1042
1043 assert_eq!(
1045 vote_state.set_new_authorized_voter(&new_voter, 1, 2, |_| Ok(())),
1046 Ok(())
1047 );
1048
1049 assert_eq!(
1053 vote_state.set_new_authorized_voter(&original_voter, 3, 3, |_| Ok(())),
1054 Err(VoteError::TooSoonToReauthorize.into())
1055 );
1056
1057 assert_eq!(vote_state.get_authorized_voter(3), Some(new_voter));
1058 }
1059
1060 #[test]
1061 fn test_vote_state_size_of() {
1062 let vote_state = VoteState::get_max_sized_vote_state();
1063 let vote_state = VoteStateVersions::new_current(vote_state);
1064 let size = bincode::serialized_size(&vote_state).unwrap();
1065 assert_eq!(VoteState::size_of() as u64, size);
1066 }
1067
1068 #[test]
1069 fn test_vote_state_max_size() {
1070 let mut max_sized_data = vec![0; VoteState::size_of()];
1071 let vote_state = VoteState::get_max_sized_vote_state();
1072 let (start_leader_schedule_epoch, _) = vote_state.authorized_voters.last().unwrap();
1073 let start_current_epoch =
1074 start_leader_schedule_epoch - MAX_LEADER_SCHEDULE_EPOCH_OFFSET + 1;
1075
1076 let mut vote_state = Some(vote_state);
1077 for i in start_current_epoch..start_current_epoch + 2 * MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
1078 vote_state.as_mut().map(|vote_state| {
1079 vote_state.set_new_authorized_voter(
1080 &Pubkey::new_unique(),
1081 i,
1082 i + MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
1083 |_| Ok(()),
1084 )
1085 });
1086
1087 let versioned = VoteStateVersions::new_current(vote_state.take().unwrap());
1088 VoteState::serialize(&versioned, &mut max_sized_data).unwrap();
1089 vote_state = Some(versioned.convert_to_current());
1090 }
1091 }
1092
1093 #[test]
1094 fn test_default_vote_state_is_uninitialized() {
1095 assert!(VoteStateVersions::new_current(VoteState::default()).is_uninitialized());
1099 }
1100
1101 #[test]
1102 fn test_is_correct_size_and_initialized() {
1103 let mut vote_account_data = vec![0; VoteState::size_of()];
1105 assert!(!VoteState::is_correct_size_and_initialized(
1106 &vote_account_data
1107 ));
1108
1109 let default_account_state = VoteStateVersions::new_current(VoteState::default());
1111 VoteState::serialize(&default_account_state, &mut vote_account_data).unwrap();
1112 assert!(!VoteState::is_correct_size_and_initialized(
1113 &vote_account_data
1114 ));
1115
1116 let short_data = vec![1; DEFAULT_PRIOR_VOTERS_OFFSET];
1118 assert!(!VoteState::is_correct_size_and_initialized(&short_data));
1119
1120 let mut large_vote_data = vec![1; 2 * VoteState::size_of()];
1122 let default_account_state = VoteStateVersions::new_current(VoteState::default());
1123 VoteState::serialize(&default_account_state, &mut large_vote_data).unwrap();
1124 assert!(!VoteState::is_correct_size_and_initialized(
1125 &vote_account_data
1126 ));
1127
1128 let account_state = VoteStateVersions::new_current(VoteState::new(
1130 &VoteInit {
1131 node_pubkey: Pubkey::new_unique(),
1132 authorized_voter: Pubkey::new_unique(),
1133 authorized_withdrawer: Pubkey::new_unique(),
1134 commission: 0,
1135 },
1136 &Clock::default(),
1137 ));
1138 VoteState::serialize(&account_state, &mut vote_account_data).unwrap();
1139 assert!(VoteState::is_correct_size_and_initialized(
1140 &vote_account_data
1141 ));
1142 }
1143
1144 #[test]
1145 fn test_minimum_balance() {
1146 let rent = solana_program::rent::Rent::default();
1147 let minimum_balance = rent.minimum_balance(VoteState::size_of());
1148 assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
1150 }
1151
1152 #[test]
1153 fn test_serde_compact_vote_state_update() {
1154 let mut rng = rand::thread_rng();
1155 for _ in 0..5000 {
1156 run_serde_compact_vote_state_update(&mut rng);
1157 }
1158 }
1159
1160 fn run_serde_compact_vote_state_update<R: Rng>(rng: &mut R) {
1161 let lockouts: VecDeque<_> = std::iter::repeat_with(|| {
1162 let slot = 149_303_885_u64.saturating_add(rng.gen_range(0, 10_000));
1163 let confirmation_count = rng.gen_range(0, 33);
1164 Lockout::new_with_confirmation_count(slot, confirmation_count)
1165 })
1166 .take(32)
1167 .sorted_by_key(|lockout| lockout.slot())
1168 .collect();
1169 let root = rng.gen_ratio(1, 2).then(|| {
1170 lockouts[0]
1171 .slot()
1172 .checked_sub(rng.gen_range(0, 1_000))
1173 .expect("All slots should be greater than 1_000")
1174 });
1175 let timestamp = rng.gen_ratio(1, 2).then(|| rng.gen());
1176 let hash = Hash::from(rng.gen::<[u8; 32]>());
1177 let vote_state_update = VoteStateUpdate {
1178 lockouts,
1179 root,
1180 hash,
1181 timestamp,
1182 };
1183 #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
1184 enum VoteInstruction {
1185 #[serde(with = "serde_compact_vote_state_update")]
1186 UpdateVoteState(VoteStateUpdate),
1187 UpdateVoteStateSwitch(
1188 #[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate,
1189 Hash,
1190 ),
1191 }
1192 let vote = VoteInstruction::UpdateVoteState(vote_state_update.clone());
1193 let bytes = bincode::serialize(&vote).unwrap();
1194 assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1195 let hash = Hash::from(rng.gen::<[u8; 32]>());
1196 let vote = VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash);
1197 let bytes = bincode::serialize(&vote).unwrap();
1198 assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1199 }
1200}