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