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