1pub use solana_vote_interface::state::{vote_state_versions::*, *};
4use {
5 log::*,
6 serde_derive::{Deserialize, Serialize},
7 solana_account::{AccountSharedData, ReadableAccount, WritableAccount},
8 solana_clock::{Clock, Epoch, Slot, UnixTimestamp},
9 solana_epoch_schedule::EpochSchedule,
10 solana_feature_set::{self as feature_set, FeatureSet},
11 solana_hash::Hash,
12 solana_instruction::error::InstructionError,
13 solana_pubkey::Pubkey,
14 solana_rent::Rent,
15 solana_slot_hashes::SlotHash,
16 solana_transaction_context::{
17 BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
18 },
19 solana_vote_interface::{error::VoteError, program::id},
20 std::{
21 cmp::Ordering,
22 collections::{HashSet, VecDeque},
23 fmt::Debug,
24 },
25};
26
27#[cfg_attr(
28 feature = "frozen-abi",
29 derive(AbiExample, AbiEnumVisitor),
30 frozen_abi(digest = "4BdRo6We16yDbjz69H1oFq6C4nXUZKDchZR76TvvGmBi")
31)]
32#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
33pub enum VoteTransaction {
34 Vote(Vote),
35 VoteStateUpdate(VoteStateUpdate),
36 #[serde(with = "serde_compact_vote_state_update")]
37 CompactVoteStateUpdate(VoteStateUpdate),
38 #[serde(with = "serde_tower_sync")]
39 TowerSync(TowerSync),
40}
41
42impl VoteTransaction {
43 pub fn slots(&self) -> Vec<Slot> {
44 match self {
45 VoteTransaction::Vote(vote) => vote.slots.clone(),
46 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.slots(),
47 VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.slots(),
48 VoteTransaction::TowerSync(tower_sync) => tower_sync.slots(),
49 }
50 }
51
52 pub fn slot(&self, i: usize) -> Slot {
53 match self {
54 VoteTransaction::Vote(vote) => vote.slots[i],
55 VoteTransaction::VoteStateUpdate(vote_state_update)
56 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
57 vote_state_update.lockouts[i].slot()
58 }
59 VoteTransaction::TowerSync(tower_sync) => tower_sync.lockouts[i].slot(),
60 }
61 }
62
63 pub fn len(&self) -> usize {
64 match self {
65 VoteTransaction::Vote(vote) => vote.slots.len(),
66 VoteTransaction::VoteStateUpdate(vote_state_update)
67 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
68 vote_state_update.lockouts.len()
69 }
70 VoteTransaction::TowerSync(tower_sync) => tower_sync.lockouts.len(),
71 }
72 }
73
74 pub fn is_empty(&self) -> bool {
75 match self {
76 VoteTransaction::Vote(vote) => vote.slots.is_empty(),
77 VoteTransaction::VoteStateUpdate(vote_state_update)
78 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
79 vote_state_update.lockouts.is_empty()
80 }
81 VoteTransaction::TowerSync(tower_sync) => tower_sync.lockouts.is_empty(),
82 }
83 }
84
85 pub fn hash(&self) -> Hash {
86 match self {
87 VoteTransaction::Vote(vote) => vote.hash,
88 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.hash,
89 VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.hash,
90 VoteTransaction::TowerSync(tower_sync) => tower_sync.hash,
91 }
92 }
93
94 pub fn timestamp(&self) -> Option<UnixTimestamp> {
95 match self {
96 VoteTransaction::Vote(vote) => vote.timestamp,
97 VoteTransaction::VoteStateUpdate(vote_state_update)
98 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
99 vote_state_update.timestamp
100 }
101 VoteTransaction::TowerSync(tower_sync) => tower_sync.timestamp,
102 }
103 }
104
105 pub fn set_timestamp(&mut self, ts: Option<UnixTimestamp>) {
106 match self {
107 VoteTransaction::Vote(vote) => vote.timestamp = ts,
108 VoteTransaction::VoteStateUpdate(vote_state_update)
109 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
110 vote_state_update.timestamp = ts
111 }
112 VoteTransaction::TowerSync(tower_sync) => tower_sync.timestamp = ts,
113 }
114 }
115
116 pub fn last_voted_slot(&self) -> Option<Slot> {
117 match self {
118 VoteTransaction::Vote(vote) => vote.last_voted_slot(),
119 VoteTransaction::VoteStateUpdate(vote_state_update)
120 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
121 vote_state_update.last_voted_slot()
122 }
123 VoteTransaction::TowerSync(tower_sync) => tower_sync.last_voted_slot(),
124 }
125 }
126
127 pub fn last_voted_slot_hash(&self) -> Option<(Slot, Hash)> {
128 Some((self.last_voted_slot()?, self.hash()))
129 }
130}
131
132impl From<Vote> for VoteTransaction {
133 fn from(vote: Vote) -> Self {
134 VoteTransaction::Vote(vote)
135 }
136}
137
138impl From<VoteStateUpdate> for VoteTransaction {
139 fn from(vote_state_update: VoteStateUpdate) -> Self {
140 VoteTransaction::VoteStateUpdate(vote_state_update)
141 }
142}
143
144impl From<TowerSync> for VoteTransaction {
145 fn from(tower_sync: TowerSync) -> Self {
146 VoteTransaction::TowerSync(tower_sync)
147 }
148}
149
150pub fn from<T: ReadableAccount>(account: &T) -> Option<VoteState> {
152 VoteState::deserialize(account.data()).ok()
153}
154
155pub fn to<T: WritableAccount>(versioned: &VoteStateVersions, account: &mut T) -> Option<()> {
157 VoteState::serialize(versioned, account.data_as_mut_slice()).ok()
158}
159
160fn set_vote_account_state(
163 vote_account: &mut BorrowedAccount,
164 vote_state: VoteState,
165) -> Result<(), InstructionError> {
166 if (vote_account.get_data().len() < VoteStateVersions::vote_state_size_of(true))
169 && (!vote_account
170 .is_rent_exempt_at_data_length(VoteStateVersions::vote_state_size_of(true))
171 || vote_account
172 .set_data_length(VoteStateVersions::vote_state_size_of(true))
173 .is_err())
174 {
175 return vote_account.set_state(&VoteStateVersions::V1_14_11(Box::new(
178 VoteState1_14_11::from(vote_state),
179 )));
180 }
181 vote_account.set_state(&VoteStateVersions::new_current(vote_state))
183}
184
185fn check_and_filter_proposed_vote_state(
189 vote_state: &VoteState,
190 proposed_lockouts: &mut VecDeque<Lockout>,
191 proposed_root: &mut Option<Slot>,
192 proposed_hash: Hash,
193 slot_hashes: &[(Slot, Hash)],
194) -> Result<(), VoteError> {
195 if proposed_lockouts.is_empty() {
196 return Err(VoteError::EmptySlots);
197 }
198
199 let last_proposed_slot = proposed_lockouts
200 .back()
201 .expect("must be nonempty, checked above")
202 .slot();
203
204 if let Some(last_vote_slot) = vote_state.votes.back().map(|lockout| lockout.slot()) {
206 if last_proposed_slot <= last_vote_slot {
207 return Err(VoteError::VoteTooOld);
208 }
209 }
210
211 if slot_hashes.is_empty() {
212 return Err(VoteError::SlotsMismatch);
213 }
214 let earliest_slot_hash_in_history = slot_hashes.last().unwrap().0;
215
216 if last_proposed_slot < earliest_slot_hash_in_history {
218 return Err(VoteError::VoteTooOld);
221 }
222
223 if let Some(root) = *proposed_root {
225 if root < earliest_slot_hash_in_history {
230 *proposed_root = vote_state.root_slot;
232
233 for vote in vote_state.votes.iter().rev() {
235 if vote.slot() <= root {
236 *proposed_root = Some(vote.slot());
237 break;
238 }
239 }
240 }
241 }
242
243 let mut root_to_check = *proposed_root;
247 let mut proposed_lockouts_index = 0;
248
249 let mut slot_hashes_index = slot_hashes.len();
252
253 let mut proposed_lockouts_indices_to_filter = vec![];
254
255 while proposed_lockouts_index < proposed_lockouts.len() && slot_hashes_index > 0 {
267 let proposed_vote_slot = if let Some(root) = root_to_check {
268 root
269 } else {
270 proposed_lockouts[proposed_lockouts_index].slot()
271 };
272 if root_to_check.is_none()
273 && proposed_lockouts_index > 0
274 && proposed_vote_slot
275 <= proposed_lockouts[proposed_lockouts_index.checked_sub(1).expect(
276 "`proposed_lockouts_index` is positive when checking `SlotsNotOrdered`",
277 )]
278 .slot()
279 {
280 return Err(VoteError::SlotsNotOrdered);
281 }
282 let ancestor_slot = slot_hashes[slot_hashes_index
283 .checked_sub(1)
284 .expect("`slot_hashes_index` is positive when computing `ancestor_slot`")]
285 .0;
286
287 match proposed_vote_slot.cmp(&ancestor_slot) {
290 Ordering::Less => {
291 if slot_hashes_index == slot_hashes.len() {
292 if proposed_vote_slot >= earliest_slot_hash_in_history {
295 return Err(VoteError::AssertionFailed);
296 }
297 if !vote_state.contains_slot(proposed_vote_slot) && root_to_check.is_none() {
298 proposed_lockouts_indices_to_filter.push(proposed_lockouts_index);
304 }
305 if let Some(new_proposed_root) = root_to_check {
306 assert_eq!(new_proposed_root, proposed_vote_slot);
310 if new_proposed_root >= earliest_slot_hash_in_history {
314 return Err(VoteError::AssertionFailed);
315 }
316 root_to_check = None;
317 } else {
318 proposed_lockouts_index = proposed_lockouts_index.checked_add(1).expect(
319 "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when `proposed_vote_slot` is too old to be in SlotHashes history",
320 );
321 }
322 continue;
323 } else {
324 if root_to_check.is_some() {
328 return Err(VoteError::RootOnDifferentFork);
329 } else {
330 return Err(VoteError::SlotsMismatch);
331 }
332 }
333 }
334 Ordering::Greater => {
335 slot_hashes_index = slot_hashes_index
337 .checked_sub(1)
338 .expect("`slot_hashes_index` is positive when finding newer slots in SlotHashes history");
339 continue;
340 }
341 Ordering::Equal => {
342 if root_to_check.is_some() {
346 root_to_check = None;
347 } else {
348 proposed_lockouts_index = proposed_lockouts_index
349 .checked_add(1)
350 .expect("`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when match is found in SlotHashes history");
351 slot_hashes_index = slot_hashes_index.checked_sub(1).expect(
352 "`slot_hashes_index` is positive when match is found in SlotHashes history",
353 );
354 }
355 }
356 }
357 }
358
359 if proposed_lockouts_index != proposed_lockouts.len() {
360 return Err(VoteError::SlotsMismatch);
362 }
363
364 assert_eq!(last_proposed_slot, slot_hashes[slot_hashes_index].0);
387
388 if slot_hashes[slot_hashes_index].1 != proposed_hash {
389 warn!(
393 "{} dropped vote {:?} root {:?} failed to match hash {} {}",
394 vote_state.node_pubkey,
395 proposed_lockouts,
396 proposed_root,
397 proposed_hash,
398 slot_hashes[slot_hashes_index].1
399 );
400 #[cfg(feature = "metrics")]
401 inc_new_counter_info!("dropped-vote-hash", 1);
402 return Err(VoteError::SlotHashMismatch);
403 }
404
405 let mut proposed_lockouts_index = 0;
407 let mut filter_votes_index = 0;
408 proposed_lockouts.retain(|_lockout| {
409 let should_retain = if filter_votes_index == proposed_lockouts_indices_to_filter.len() {
410 true
411 } else if proposed_lockouts_index == proposed_lockouts_indices_to_filter[filter_votes_index]
412 {
413 filter_votes_index = filter_votes_index.checked_add(1).unwrap();
414 false
415 } else {
416 true
417 };
418
419 proposed_lockouts_index = proposed_lockouts_index
420 .checked_add(1)
421 .expect("`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when filtering out irrelevant votes");
422 should_retain
423 });
424
425 Ok(())
426}
427
428fn check_slots_are_valid(
429 vote_state: &VoteState,
430 vote_slots: &[Slot],
431 vote_hash: &Hash,
432 slot_hashes: &[(Slot, Hash)],
433) -> Result<(), VoteError> {
434 let mut i = 0;
437
438 let mut j = slot_hashes.len();
441
442 while i < vote_slots.len() && j > 0 {
451 if vote_state
454 .last_voted_slot()
455 .is_some_and(|last_voted_slot| vote_slots[i] <= last_voted_slot)
456 {
457 i = i
458 .checked_add(1)
459 .expect("`i` is bounded by `MAX_LOCKOUT_HISTORY` when finding larger slots");
460 continue;
461 }
462
463 if vote_slots[i] != slot_hashes[j.checked_sub(1).expect("`j` is positive")].0 {
465 j = j
467 .checked_sub(1)
468 .expect("`j` is positive when finding newer slots");
469 continue;
470 }
471
472 i = i
475 .checked_add(1)
476 .expect("`i` is bounded by `MAX_LOCKOUT_HISTORY` when hash is found");
477 j = j
478 .checked_sub(1)
479 .expect("`j` is positive when hash is found");
480 }
481
482 if j == slot_hashes.len() {
483 debug!(
487 "{} dropped vote slots {:?}, vote hash: {:?} slot hashes:SlotHash {:?}, too old ",
488 vote_state.node_pubkey, vote_slots, vote_hash, slot_hashes
489 );
490 return Err(VoteError::VoteTooOld);
491 }
492 if i != vote_slots.len() {
493 info!(
496 "{} dropped vote slots {:?} failed to match slot hashes: {:?}",
497 vote_state.node_pubkey, vote_slots, slot_hashes,
498 );
499 return Err(VoteError::SlotsMismatch);
500 }
501 if &slot_hashes[j].1 != vote_hash {
502 warn!(
506 "{} dropped vote slots {:?} failed to match hash {} {}",
507 vote_state.node_pubkey, vote_slots, vote_hash, slot_hashes[j].1
508 );
509 return Err(VoteError::SlotHashMismatch);
510 }
511 Ok(())
512}
513
514pub fn process_new_vote_state(
552 vote_state: &mut VoteState,
553 mut new_state: VecDeque<LandedVote>,
554 new_root: Option<Slot>,
555 timestamp: Option<i64>,
556 epoch: Epoch,
557 current_slot: Slot,
558 _feature_set: Option<&FeatureSet>,
559) -> Result<(), VoteError> {
560 assert!(!new_state.is_empty());
561 if new_state.len() > MAX_LOCKOUT_HISTORY {
562 return Err(VoteError::TooManyVotes);
563 }
564
565 match (new_root, vote_state.root_slot) {
566 (Some(new_root), Some(current_root)) => {
567 if new_root < current_root {
568 return Err(VoteError::RootRollBack);
569 }
570 }
571 (None, Some(_)) => {
572 return Err(VoteError::RootRollBack);
573 }
574 _ => (),
575 }
576
577 let mut previous_vote: Option<&LandedVote> = None;
578
579 for vote in &new_state {
584 if vote.confirmation_count() == 0 {
585 return Err(VoteError::ZeroConfirmations);
586 } else if vote.confirmation_count() > MAX_LOCKOUT_HISTORY as u32 {
587 return Err(VoteError::ConfirmationTooLarge);
588 } else if let Some(new_root) = new_root {
589 if vote.slot() <= new_root
590 &&
591 new_root != Slot::default()
596 {
597 return Err(VoteError::SlotSmallerThanRoot);
598 }
599 }
600
601 if let Some(previous_vote) = previous_vote {
602 if previous_vote.slot() >= vote.slot() {
603 return Err(VoteError::SlotsNotOrdered);
604 } else if previous_vote.confirmation_count() <= vote.confirmation_count() {
605 return Err(VoteError::ConfirmationsNotOrdered);
606 } else if vote.slot() > previous_vote.lockout.last_locked_out_slot() {
607 return Err(VoteError::NewVoteStateLockoutMismatch);
608 }
609 }
610 previous_vote = Some(vote);
611 }
612
613 let mut current_vote_state_index: usize = 0;
616 let mut new_vote_state_index = 0;
617
618 let mut earned_credits = 0_u64;
620
621 if let Some(new_root) = new_root {
622 for current_vote in &vote_state.votes {
623 if current_vote.slot() <= new_root {
626 earned_credits = earned_credits
627 .checked_add(vote_state.credits_for_vote_at_index(current_vote_state_index))
628 .expect("`earned_credits` does not overflow");
629 current_vote_state_index = current_vote_state_index
630 .checked_add(1)
631 .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when processing new root");
632 continue;
633 }
634
635 break;
636 }
637 }
638
639 while current_vote_state_index < vote_state.votes.len()
659 && new_vote_state_index < new_state.len()
660 {
661 let current_vote = &vote_state.votes[current_vote_state_index];
662 let new_vote = &mut new_state[new_vote_state_index];
663
664 match current_vote.slot().cmp(&new_vote.slot()) {
668 Ordering::Less => {
669 if current_vote.lockout.last_locked_out_slot() >= new_vote.slot() {
670 return Err(VoteError::LockoutConflict);
671 }
672 current_vote_state_index = current_vote_state_index
673 .checked_add(1)
674 .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is less than proposed");
675 }
676 Ordering::Equal => {
677 if new_vote.confirmation_count() < current_vote.confirmation_count() {
680 return Err(VoteError::ConfirmationRollBack);
681 }
682
683 new_vote.latency = vote_state.votes[current_vote_state_index].latency;
685
686 current_vote_state_index = current_vote_state_index
687 .checked_add(1)
688 .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is equal to proposed");
689 new_vote_state_index = new_vote_state_index
690 .checked_add(1)
691 .expect("`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is equal to proposed");
692 }
693 Ordering::Greater => {
694 new_vote_state_index = new_vote_state_index
695 .checked_add(1)
696 .expect("`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is greater than proposed");
697 }
698 }
699 }
700
701 for new_vote in new_state.iter_mut() {
707 if new_vote.latency == 0 {
708 new_vote.latency = VoteState::compute_vote_latency(new_vote.slot(), current_slot);
709 }
710 }
711
712 if vote_state.root_slot != new_root {
713 vote_state.increment_credits(epoch, earned_credits);
717 }
718 if let Some(timestamp) = timestamp {
719 let last_slot = new_state.back().unwrap().slot();
720 vote_state.process_timestamp(last_slot, timestamp)?;
721 }
722 vote_state.root_slot = new_root;
723 vote_state.votes = new_state;
724
725 Ok(())
726}
727
728pub fn process_vote_unfiltered(
729 vote_state: &mut VoteState,
730 vote_slots: &[Slot],
731 vote: &Vote,
732 slot_hashes: &[SlotHash],
733 epoch: Epoch,
734 current_slot: Slot,
735) -> Result<(), VoteError> {
736 check_slots_are_valid(vote_state, vote_slots, &vote.hash, slot_hashes)?;
737 vote_slots
738 .iter()
739 .for_each(|s| vote_state.process_next_vote_slot(*s, epoch, current_slot));
740 Ok(())
741}
742
743pub fn process_vote(
744 vote_state: &mut VoteState,
745 vote: &Vote,
746 slot_hashes: &[SlotHash],
747 epoch: Epoch,
748 current_slot: Slot,
749) -> Result<(), VoteError> {
750 if vote.slots.is_empty() {
751 return Err(VoteError::EmptySlots);
752 }
753 let earliest_slot_in_history = slot_hashes.last().map(|(slot, _hash)| *slot).unwrap_or(0);
754 let vote_slots = vote
755 .slots
756 .iter()
757 .filter(|slot| **slot >= earliest_slot_in_history)
758 .cloned()
759 .collect::<Vec<Slot>>();
760 if vote_slots.is_empty() {
761 return Err(VoteError::VotesTooOldAllFiltered);
762 }
763 process_vote_unfiltered(
764 vote_state,
765 &vote_slots,
766 vote,
767 slot_hashes,
768 epoch,
769 current_slot,
770 )
771}
772
773pub fn process_vote_unchecked(vote_state: &mut VoteState, vote: Vote) -> Result<(), VoteError> {
775 if vote.slots.is_empty() {
776 return Err(VoteError::EmptySlots);
777 }
778 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
779 process_vote_unfiltered(
780 vote_state,
781 &vote.slots,
782 &vote,
783 &slot_hashes,
784 vote_state.current_epoch(),
785 0,
786 )
787}
788
789#[cfg(test)]
790pub fn process_slot_votes_unchecked(vote_state: &mut VoteState, slots: &[Slot]) {
791 for slot in slots {
792 process_slot_vote_unchecked(vote_state, *slot);
793 }
794}
795
796pub fn process_slot_vote_unchecked(vote_state: &mut VoteState, slot: Slot) {
797 let _ = process_vote_unchecked(vote_state, Vote::new(vec![slot], Hash::default()));
798}
799
800pub fn authorize<S: std::hash::BuildHasher>(
804 vote_account: &mut BorrowedAccount,
805 authorized: &Pubkey,
806 vote_authorize: VoteAuthorize,
807 signers: &HashSet<Pubkey, S>,
808 clock: &Clock,
809) -> Result<(), InstructionError> {
810 let mut vote_state: VoteState = vote_account
811 .get_state::<VoteStateVersions>()?
812 .convert_to_current();
813
814 match vote_authorize {
815 VoteAuthorize::Voter => {
816 let authorized_withdrawer_signer =
817 verify_authorized_signer(&vote_state.authorized_withdrawer, signers).is_ok();
818
819 vote_state.set_new_authorized_voter(
820 authorized,
821 clock.epoch,
822 clock
823 .leader_schedule_epoch
824 .checked_add(1)
825 .ok_or(InstructionError::InvalidAccountData)?,
826 |epoch_authorized_voter| {
827 if authorized_withdrawer_signer {
829 Ok(())
830 } else {
831 verify_authorized_signer(&epoch_authorized_voter, signers)
832 }
833 },
834 )?;
835 }
836 VoteAuthorize::Withdrawer => {
837 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
839 vote_state.authorized_withdrawer = *authorized;
840 }
841 }
842
843 set_vote_account_state(vote_account, vote_state)
844}
845
846pub fn update_validator_identity<S: std::hash::BuildHasher>(
848 vote_account: &mut BorrowedAccount,
849 node_pubkey: &Pubkey,
850 signers: &HashSet<Pubkey, S>,
851) -> Result<(), InstructionError> {
852 let mut vote_state: VoteState = vote_account
853 .get_state::<VoteStateVersions>()?
854 .convert_to_current();
855
856 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
858
859 verify_authorized_signer(node_pubkey, signers)?;
861
862 vote_state.node_pubkey = *node_pubkey;
863
864 set_vote_account_state(vote_account, vote_state)
865}
866
867pub fn update_commission<S: std::hash::BuildHasher>(
869 vote_account: &mut BorrowedAccount,
870 commission: u8,
871 signers: &HashSet<Pubkey, S>,
872 epoch_schedule: &EpochSchedule,
873 clock: &Clock,
874 feature_set: &FeatureSet,
875) -> Result<(), InstructionError> {
876 let mut vote_state = None;
878
879 let enforce_commission_update_rule =
880 if feature_set.is_active(&feature_set::allow_commission_decrease_at_any_time::id()) {
881 if let Ok(decoded_vote_state) = vote_account.get_state::<VoteStateVersions>() {
882 vote_state = Some(decoded_vote_state.convert_to_current());
883 is_commission_increase(vote_state.as_ref().unwrap(), commission)
884 } else {
885 true
886 }
887 } else {
888 true
889 };
890
891 #[allow(clippy::collapsible_if)]
892 if enforce_commission_update_rule
893 && feature_set
894 .is_active(&feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id())
895 {
896 if !is_commission_update_allowed(clock.slot, epoch_schedule) {
897 return Err(VoteError::CommissionUpdateTooLate.into());
898 }
899 }
900
901 let mut vote_state = match vote_state {
902 Some(vote_state) => vote_state,
903 None => vote_account
904 .get_state::<VoteStateVersions>()?
905 .convert_to_current(),
906 };
907
908 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
910
911 vote_state.commission = commission;
912
913 set_vote_account_state(vote_account, vote_state)
914}
915
916pub fn is_commission_increase(vote_state: &VoteState, commission: u8) -> bool {
918 commission > vote_state.commission
919}
920
921pub fn is_commission_update_allowed(slot: Slot, epoch_schedule: &EpochSchedule) -> bool {
924 if let Some(relative_slot) = slot
926 .saturating_sub(epoch_schedule.first_normal_slot)
927 .checked_rem(epoch_schedule.slots_per_epoch)
928 {
929 relative_slot.saturating_mul(2) <= epoch_schedule.slots_per_epoch
931 } else {
932 true
934 }
935}
936
937fn verify_authorized_signer<S: std::hash::BuildHasher>(
938 authorized: &Pubkey,
939 signers: &HashSet<Pubkey, S>,
940) -> Result<(), InstructionError> {
941 if signers.contains(authorized) {
942 Ok(())
943 } else {
944 Err(InstructionError::MissingRequiredSignature)
945 }
946}
947
948pub fn withdraw<S: std::hash::BuildHasher>(
950 transaction_context: &TransactionContext,
951 instruction_context: &InstructionContext,
952 vote_account_index: IndexOfAccount,
953 lamports: u64,
954 to_account_index: IndexOfAccount,
955 signers: &HashSet<Pubkey, S>,
956 rent_sysvar: &Rent,
957 clock: &Clock,
958) -> Result<(), InstructionError> {
959 let mut vote_account = instruction_context
960 .try_borrow_instruction_account(transaction_context, vote_account_index)?;
961 let vote_state: VoteState = vote_account
962 .get_state::<VoteStateVersions>()?
963 .convert_to_current();
964
965 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
966
967 let remaining_balance = vote_account
968 .get_lamports()
969 .checked_sub(lamports)
970 .ok_or(InstructionError::InsufficientFunds)?;
971
972 if remaining_balance == 0 {
973 let reject_active_vote_account_close = vote_state
974 .epoch_credits
975 .last()
976 .map(|(last_epoch_with_credits, _, _)| {
977 let current_epoch = clock.epoch;
978 current_epoch.saturating_sub(*last_epoch_with_credits) < 2
982 })
983 .unwrap_or(false);
984
985 if reject_active_vote_account_close {
986 return Err(VoteError::ActiveVoteAccountClose.into());
987 } else {
988 set_vote_account_state(&mut vote_account, VoteState::default())?;
990 }
991 } else {
992 let min_rent_exempt_balance = rent_sysvar.minimum_balance(vote_account.get_data().len());
993 if remaining_balance < min_rent_exempt_balance {
994 return Err(InstructionError::InsufficientFunds);
995 }
996 }
997
998 vote_account.checked_sub_lamports(lamports)?;
999 drop(vote_account);
1000 let mut to_account = instruction_context
1001 .try_borrow_instruction_account(transaction_context, to_account_index)?;
1002 to_account.checked_add_lamports(lamports)?;
1003 Ok(())
1004}
1005
1006pub fn initialize_account<S: std::hash::BuildHasher>(
1010 vote_account: &mut BorrowedAccount,
1011 vote_init: &VoteInit,
1012 signers: &HashSet<Pubkey, S>,
1013 clock: &Clock,
1014) -> Result<(), InstructionError> {
1015 if vote_account.get_data().len() != VoteStateVersions::vote_state_size_of(true) {
1016 return Err(InstructionError::InvalidAccountData);
1017 }
1018 let versioned = vote_account.get_state::<VoteStateVersions>()?;
1019
1020 if !versioned.is_uninitialized() {
1021 return Err(InstructionError::AccountAlreadyInitialized);
1022 }
1023
1024 verify_authorized_signer(&vote_init.node_pubkey, signers)?;
1026
1027 set_vote_account_state(vote_account, VoteState::new(vote_init, clock))
1028}
1029
1030fn verify_and_get_vote_state<S: std::hash::BuildHasher>(
1031 vote_account: &BorrowedAccount,
1032 clock: &Clock,
1033 signers: &HashSet<Pubkey, S>,
1034) -> Result<VoteState, InstructionError> {
1035 let versioned = vote_account.get_state::<VoteStateVersions>()?;
1036
1037 if versioned.is_uninitialized() {
1038 return Err(InstructionError::UninitializedAccount);
1039 }
1040
1041 let mut vote_state = versioned.convert_to_current();
1042 let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?;
1043 verify_authorized_signer(&authorized_voter, signers)?;
1044
1045 Ok(vote_state)
1046}
1047
1048pub fn process_vote_with_account<S: std::hash::BuildHasher>(
1049 vote_account: &mut BorrowedAccount,
1050 slot_hashes: &[SlotHash],
1051 clock: &Clock,
1052 vote: &Vote,
1053 signers: &HashSet<Pubkey, S>,
1054 _feature_set: &FeatureSet,
1055) -> Result<(), InstructionError> {
1056 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1057
1058 process_vote(&mut vote_state, vote, slot_hashes, clock.epoch, clock.slot)?;
1059 if let Some(timestamp) = vote.timestamp {
1060 vote.slots
1061 .iter()
1062 .max()
1063 .ok_or(VoteError::EmptySlots)
1064 .and_then(|slot| vote_state.process_timestamp(*slot, timestamp))?;
1065 }
1066 set_vote_account_state(vote_account, vote_state)
1067}
1068
1069pub fn process_vote_state_update<S: std::hash::BuildHasher>(
1070 vote_account: &mut BorrowedAccount,
1071 slot_hashes: &[SlotHash],
1072 clock: &Clock,
1073 vote_state_update: VoteStateUpdate,
1074 signers: &HashSet<Pubkey, S>,
1075 feature_set: &FeatureSet,
1076) -> Result<(), InstructionError> {
1077 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1078 do_process_vote_state_update(
1079 &mut vote_state,
1080 slot_hashes,
1081 clock.epoch,
1082 clock.slot,
1083 vote_state_update,
1084 Some(feature_set),
1085 )?;
1086 set_vote_account_state(vote_account, vote_state)
1087}
1088
1089pub fn do_process_vote_state_update(
1090 vote_state: &mut VoteState,
1091 slot_hashes: &[SlotHash],
1092 epoch: u64,
1093 slot: u64,
1094 mut vote_state_update: VoteStateUpdate,
1095 feature_set: Option<&FeatureSet>,
1096) -> Result<(), VoteError> {
1097 check_and_filter_proposed_vote_state(
1098 vote_state,
1099 &mut vote_state_update.lockouts,
1100 &mut vote_state_update.root,
1101 vote_state_update.hash,
1102 slot_hashes,
1103 )?;
1104 process_new_vote_state(
1105 vote_state,
1106 vote_state_update
1107 .lockouts
1108 .iter()
1109 .map(|lockout| LandedVote::from(*lockout))
1110 .collect(),
1111 vote_state_update.root,
1112 vote_state_update.timestamp,
1113 epoch,
1114 slot,
1115 feature_set,
1116 )
1117}
1118
1119pub fn process_tower_sync<S: std::hash::BuildHasher>(
1120 vote_account: &mut BorrowedAccount,
1121 slot_hashes: &[SlotHash],
1122 clock: &Clock,
1123 tower_sync: TowerSync,
1124 signers: &HashSet<Pubkey, S>,
1125 feature_set: &FeatureSet,
1126) -> Result<(), InstructionError> {
1127 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1128 do_process_tower_sync(
1129 &mut vote_state,
1130 slot_hashes,
1131 clock.epoch,
1132 clock.slot,
1133 tower_sync,
1134 Some(feature_set),
1135 )?;
1136 set_vote_account_state(vote_account, vote_state)
1137}
1138
1139fn do_process_tower_sync(
1140 vote_state: &mut VoteState,
1141 slot_hashes: &[SlotHash],
1142 epoch: u64,
1143 slot: u64,
1144 mut tower_sync: TowerSync,
1145 feature_set: Option<&FeatureSet>,
1146) -> Result<(), VoteError> {
1147 check_and_filter_proposed_vote_state(
1148 vote_state,
1149 &mut tower_sync.lockouts,
1150 &mut tower_sync.root,
1151 tower_sync.hash,
1152 slot_hashes,
1153 )?;
1154 process_new_vote_state(
1155 vote_state,
1156 tower_sync
1157 .lockouts
1158 .iter()
1159 .map(|lockout| LandedVote::from(*lockout))
1160 .collect(),
1161 tower_sync.root,
1162 tower_sync.timestamp,
1163 epoch,
1164 slot,
1165 feature_set,
1166 )
1167}
1168
1169pub fn create_account_with_authorized(
1174 node_pubkey: &Pubkey,
1175 authorized_voter: &Pubkey,
1176 authorized_withdrawer: &Pubkey,
1177 commission: u8,
1178 lamports: u64,
1179) -> AccountSharedData {
1180 let mut vote_account = AccountSharedData::new(lamports, VoteState::size_of(), &id());
1181
1182 let vote_state = VoteState::new(
1183 &VoteInit {
1184 node_pubkey: *node_pubkey,
1185 authorized_voter: *authorized_voter,
1186 authorized_withdrawer: *authorized_withdrawer,
1187 commission,
1188 },
1189 &Clock::default(),
1190 );
1191
1192 VoteState::serialize(
1193 &VoteStateVersions::Current(Box::new(vote_state)),
1194 vote_account.data_as_mut_slice(),
1195 )
1196 .unwrap();
1197
1198 vote_account
1199}
1200
1201pub fn create_account(
1203 vote_pubkey: &Pubkey,
1204 node_pubkey: &Pubkey,
1205 commission: u8,
1206 lamports: u64,
1207) -> AccountSharedData {
1208 create_account_with_authorized(node_pubkey, vote_pubkey, vote_pubkey, commission, lamports)
1209}
1210
1211#[cfg(test)]
1212mod tests {
1213 use {
1214 super::*,
1215 crate::vote_state,
1216 assert_matches::assert_matches,
1217 solana_account::{state_traits::StateMut, AccountSharedData},
1218 solana_clock::DEFAULT_SLOTS_PER_EPOCH,
1219 solana_sha256_hasher::hash,
1220 solana_transaction_context::InstructionAccount,
1221 std::cell::RefCell,
1222 test_case::test_case,
1223 };
1224
1225 const MAX_RECENT_VOTES: usize = 16;
1226
1227 fn vote_state_new_for_test(auth_pubkey: &Pubkey) -> VoteState {
1228 VoteState::new(
1229 &VoteInit {
1230 node_pubkey: solana_pubkey::new_rand(),
1231 authorized_voter: *auth_pubkey,
1232 authorized_withdrawer: *auth_pubkey,
1233 commission: 0,
1234 },
1235 &Clock::default(),
1236 )
1237 }
1238
1239 fn create_test_account() -> (Pubkey, RefCell<AccountSharedData>) {
1240 let rent = Rent::default();
1241 let balance = VoteState::get_rent_exempt_reserve(&rent);
1242 let vote_pubkey = solana_pubkey::new_rand();
1243 (
1244 vote_pubkey,
1245 RefCell::new(vote_state::create_account(
1246 &vote_pubkey,
1247 &solana_pubkey::new_rand(),
1248 0,
1249 balance,
1250 )),
1251 )
1252 }
1253
1254 #[test]
1255 fn test_vote_state_upgrade_from_1_14_11() {
1256 let node_pubkey = solana_pubkey::new_rand();
1259 let withdrawer_pubkey = solana_pubkey::new_rand();
1260 let mut vote_state = VoteState::new(
1261 &VoteInit {
1262 node_pubkey,
1263 authorized_voter: withdrawer_pubkey,
1264 authorized_withdrawer: withdrawer_pubkey,
1265 commission: 10,
1266 },
1267 &Clock::default(),
1268 );
1269 vote_state.increment_credits(0, 100);
1271 assert_eq!(
1272 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 0, 1, |_pubkey| Ok(())),
1273 Ok(())
1274 );
1275 vote_state.increment_credits(1, 200);
1276 assert_eq!(
1277 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 1, 2, |_pubkey| Ok(())),
1278 Ok(())
1279 );
1280 vote_state.increment_credits(2, 300);
1281 assert_eq!(
1282 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 2, 3, |_pubkey| Ok(())),
1283 Ok(())
1284 );
1285 vec![
1287 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
1288 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
1289 134, 135,
1290 ]
1291 .into_iter()
1292 .for_each(|v| vote_state.process_next_vote_slot(v, 4, 0));
1293
1294 let version1_14_11_serialized = bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(
1295 VoteState1_14_11::from(vote_state.clone()),
1296 )))
1297 .unwrap();
1298 let version1_14_11_serialized_len = version1_14_11_serialized.len();
1299 let rent = Rent::default();
1300 let lamports = rent.minimum_balance(version1_14_11_serialized_len);
1301 let mut vote_account =
1302 AccountSharedData::new(lamports, version1_14_11_serialized_len, &id());
1303 vote_account.set_data_from_slice(&version1_14_11_serialized);
1304
1305 let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id());
1308 let transaction_context = TransactionContext::new(
1309 vec![(id(), processor_account), (node_pubkey, vote_account)],
1310 rent.clone(),
1311 0,
1312 0,
1313 );
1314 let mut instruction_context = InstructionContext::default();
1315 instruction_context.configure(
1316 &[0],
1317 &[InstructionAccount {
1318 index_in_transaction: 1,
1319 index_in_caller: 1,
1320 index_in_callee: 0,
1321 is_signer: false,
1322 is_writable: true,
1323 }],
1324 &[],
1325 );
1326
1327 let mut borrowed_account = instruction_context
1330 .try_borrow_instruction_account(&transaction_context, 0)
1331 .unwrap();
1332
1333 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1335 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1336
1337 let converted_vote_state = vote_state_version.convert_to_current();
1339
1340 assert!(vote_state == converted_vote_state);
1342
1343 let vote_state = converted_vote_state;
1344
1345 assert_eq!(
1348 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1349 Ok(())
1350 );
1351 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1352 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1353
1354 let converted_vote_state = vote_state_version.convert_to_current();
1356
1357 assert_eq!(vote_state, converted_vote_state);
1359
1360 let vote_state = converted_vote_state;
1361
1362 assert_eq!(
1365 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1366 Ok(())
1367 );
1368 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1369 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1370
1371 let converted_vote_state = vote_state_version.convert_to_current();
1373
1374 assert_eq!(vote_state, converted_vote_state);
1376
1377 let vote_state = converted_vote_state;
1378
1379 assert_eq!(
1382 borrowed_account.set_lamports(rent.minimum_balance(VoteState::size_of()),),
1383 Ok(())
1384 );
1385 assert_eq!(
1386 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1387 Ok(())
1388 );
1389 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1390 assert_matches!(vote_state_version, VoteStateVersions::Current(_));
1391
1392 let converted_vote_state = vote_state_version.convert_to_current();
1394
1395 assert_eq!(vote_state, converted_vote_state);
1397 }
1398
1399 #[test]
1400 fn test_vote_lockout() {
1401 let (_vote_pubkey, vote_account) = create_test_account();
1402
1403 let mut vote_state: VoteState =
1404 StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
1405 .unwrap()
1406 .convert_to_current();
1407
1408 for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
1409 process_slot_vote_unchecked(&mut vote_state, (INITIAL_LOCKOUT * i) as u64);
1410 }
1411
1412 assert_eq!(vote_state.votes.len(), MAX_LOCKOUT_HISTORY);
1414 assert_eq!(vote_state.root_slot, Some(0));
1415 check_lockouts(&vote_state);
1416
1417 let top_vote = vote_state.votes.front().unwrap().slot();
1421 let slot = vote_state.last_lockout().unwrap().last_locked_out_slot();
1422 process_slot_vote_unchecked(&mut vote_state, slot);
1423 assert_eq!(Some(top_vote), vote_state.root_slot);
1424
1425 let slot = vote_state
1427 .votes
1428 .front()
1429 .unwrap()
1430 .lockout
1431 .last_locked_out_slot();
1432 process_slot_vote_unchecked(&mut vote_state, slot);
1433 assert_eq!(vote_state.votes.len(), 2);
1435 }
1436
1437 #[test]
1438 fn test_update_commission() {
1439 let node_pubkey = Pubkey::new_unique();
1440 let withdrawer_pubkey = Pubkey::new_unique();
1441 let clock = Clock::default();
1442 let vote_state = VoteState::new(
1443 &VoteInit {
1444 node_pubkey,
1445 authorized_voter: withdrawer_pubkey,
1446 authorized_withdrawer: withdrawer_pubkey,
1447 commission: 10,
1448 },
1449 &clock,
1450 );
1451
1452 let serialized =
1453 bincode::serialize(&VoteStateVersions::Current(Box::new(vote_state.clone()))).unwrap();
1454 let serialized_len = serialized.len();
1455 let rent = Rent::default();
1456 let lamports = rent.minimum_balance(serialized_len);
1457 let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id());
1458 vote_account.set_data_from_slice(&serialized);
1459
1460 let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id());
1463 let transaction_context = TransactionContext::new(
1464 vec![(id(), processor_account), (node_pubkey, vote_account)],
1465 rent,
1466 0,
1467 0,
1468 );
1469 let mut instruction_context = InstructionContext::default();
1470 instruction_context.configure(
1471 &[0],
1472 &[InstructionAccount {
1473 index_in_transaction: 1,
1474 index_in_caller: 1,
1475 index_in_callee: 0,
1476 is_signer: false,
1477 is_writable: true,
1478 }],
1479 &[],
1480 );
1481
1482 let mut borrowed_account = instruction_context
1485 .try_borrow_instruction_account(&transaction_context, 0)
1486 .unwrap();
1487
1488 let epoch_schedule = std::sync::Arc::new(EpochSchedule::without_warmup());
1489
1490 let first_half_clock = std::sync::Arc::new(Clock {
1491 slot: epoch_schedule.slots_per_epoch / 4,
1492 ..Clock::default()
1493 });
1494
1495 let second_half_clock = std::sync::Arc::new(Clock {
1496 slot: (epoch_schedule.slots_per_epoch * 3) / 4,
1497 ..Clock::default()
1498 });
1499
1500 let mut feature_set = FeatureSet::default();
1501 feature_set.activate(
1502 &feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id(),
1503 1,
1504 );
1505
1506 let signers: HashSet<Pubkey> = vec![withdrawer_pubkey].into_iter().collect();
1507
1508 assert_eq!(
1510 borrowed_account
1511 .get_state::<VoteStateVersions>()
1512 .unwrap()
1513 .convert_to_current()
1514 .commission,
1515 10
1516 );
1517 assert_matches!(
1518 update_commission(
1519 &mut borrowed_account,
1520 11,
1521 &signers,
1522 &epoch_schedule,
1523 &first_half_clock,
1524 &feature_set
1525 ),
1526 Ok(())
1527 );
1528 assert_eq!(
1529 borrowed_account
1530 .get_state::<VoteStateVersions>()
1531 .unwrap()
1532 .convert_to_current()
1533 .commission,
1534 11
1535 );
1536
1537 assert_matches!(
1539 update_commission(
1540 &mut borrowed_account,
1541 12,
1542 &signers,
1543 &epoch_schedule,
1544 &second_half_clock,
1545 &feature_set
1546 ),
1547 Err(_)
1548 );
1549 assert_eq!(
1550 borrowed_account
1551 .get_state::<VoteStateVersions>()
1552 .unwrap()
1553 .convert_to_current()
1554 .commission,
1555 11
1556 );
1557
1558 assert_matches!(
1560 update_commission(
1561 &mut borrowed_account,
1562 10,
1563 &signers,
1564 &epoch_schedule,
1565 &first_half_clock,
1566 &feature_set
1567 ),
1568 Ok(())
1569 );
1570 assert_eq!(
1571 borrowed_account
1572 .get_state::<VoteStateVersions>()
1573 .unwrap()
1574 .convert_to_current()
1575 .commission,
1576 10
1577 );
1578
1579 assert_matches!(
1581 update_commission(
1582 &mut borrowed_account,
1583 9,
1584 &signers,
1585 &epoch_schedule,
1586 &second_half_clock,
1587 &feature_set
1588 ),
1589 Err(_)
1590 );
1591 assert_eq!(
1592 borrowed_account
1593 .get_state::<VoteStateVersions>()
1594 .unwrap()
1595 .convert_to_current()
1596 .commission,
1597 10
1598 );
1599
1600 feature_set.activate(&feature_set::allow_commission_decrease_at_any_time::id(), 1);
1602 assert_matches!(
1603 update_commission(
1604 &mut borrowed_account,
1605 9,
1606 &signers,
1607 &epoch_schedule,
1608 &second_half_clock,
1609 &feature_set
1610 ),
1611 Ok(())
1612 );
1613 assert_eq!(
1614 borrowed_account
1615 .get_state::<VoteStateVersions>()
1616 .unwrap()
1617 .convert_to_current()
1618 .commission,
1619 9
1620 );
1621 }
1622
1623 #[test]
1624 fn test_vote_double_lockout_after_expiration() {
1625 let voter_pubkey = solana_pubkey::new_rand();
1626 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1627
1628 for i in 0..3 {
1629 process_slot_vote_unchecked(&mut vote_state, i as u64);
1630 }
1631
1632 check_lockouts(&vote_state);
1633
1634 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 1) as u64);
1638 check_lockouts(&vote_state);
1639
1640 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 2) as u64);
1643 check_lockouts(&vote_state);
1644
1645 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 3) as u64);
1648 check_lockouts(&vote_state);
1649 }
1650
1651 #[test]
1652 fn test_expire_multiple_votes() {
1653 let voter_pubkey = solana_pubkey::new_rand();
1654 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1655
1656 for i in 0..3 {
1657 process_slot_vote_unchecked(&mut vote_state, i as u64);
1658 }
1659
1660 assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1661
1662 let expire_slot = vote_state.votes[1].slot() + vote_state.votes[1].lockout.lockout() + 1;
1664 process_slot_vote_unchecked(&mut vote_state, expire_slot);
1665 assert_eq!(vote_state.votes.len(), 2);
1666
1667 assert_eq!(vote_state.votes[0].slot(), 0);
1669 assert_eq!(vote_state.votes[1].slot(), expire_slot);
1670
1671 process_slot_vote_unchecked(&mut vote_state, expire_slot + 1);
1673
1674 assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1676
1677 assert_eq!(vote_state.votes[1].confirmation_count(), 2);
1679 assert_eq!(vote_state.votes[2].confirmation_count(), 1);
1680 }
1681
1682 #[test]
1683 fn test_vote_credits() {
1684 let voter_pubkey = solana_pubkey::new_rand();
1685 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1686
1687 for i in 0..MAX_LOCKOUT_HISTORY {
1688 process_slot_vote_unchecked(&mut vote_state, i as u64);
1689 }
1690
1691 assert_eq!(vote_state.credits(), 0);
1692
1693 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 1);
1694 assert_eq!(vote_state.credits(), 1);
1695 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 2);
1696 assert_eq!(vote_state.credits(), 2);
1697 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 3);
1698 assert_eq!(vote_state.credits(), 3);
1699 }
1700
1701 #[test]
1702 fn test_duplicate_vote() {
1703 let voter_pubkey = solana_pubkey::new_rand();
1704 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1705 process_slot_vote_unchecked(&mut vote_state, 0);
1706 process_slot_vote_unchecked(&mut vote_state, 1);
1707 process_slot_vote_unchecked(&mut vote_state, 0);
1708 assert_eq!(vote_state.nth_recent_lockout(0).unwrap().slot(), 1);
1709 assert_eq!(vote_state.nth_recent_lockout(1).unwrap().slot(), 0);
1710 assert!(vote_state.nth_recent_lockout(2).is_none());
1711 }
1712
1713 #[test]
1714 fn test_nth_recent_lockout() {
1715 let voter_pubkey = solana_pubkey::new_rand();
1716 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1717 for i in 0..MAX_LOCKOUT_HISTORY {
1718 process_slot_vote_unchecked(&mut vote_state, i as u64);
1719 }
1720 for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
1721 assert_eq!(
1722 vote_state.nth_recent_lockout(i).unwrap().slot() as usize,
1723 MAX_LOCKOUT_HISTORY - i - 1,
1724 );
1725 }
1726 assert!(vote_state.nth_recent_lockout(MAX_LOCKOUT_HISTORY).is_none());
1727 }
1728
1729 fn check_lockouts(vote_state: &VoteState) {
1730 for (i, vote) in vote_state.votes.iter().enumerate() {
1731 let num_votes = vote_state
1732 .votes
1733 .len()
1734 .checked_sub(i)
1735 .expect("`i` is less than `vote_state.votes.len()`");
1736 assert_eq!(
1737 vote.lockout.lockout(),
1738 INITIAL_LOCKOUT.pow(num_votes as u32) as u64
1739 );
1740 }
1741 }
1742
1743 fn recent_votes(vote_state: &VoteState) -> Vec<Vote> {
1744 let start = vote_state.votes.len().saturating_sub(MAX_RECENT_VOTES);
1745 (start..vote_state.votes.len())
1746 .map(|i| {
1747 Vote::new(
1748 vec![vote_state.votes.get(i).unwrap().slot()],
1749 Hash::default(),
1750 )
1751 })
1752 .collect()
1753 }
1754
1755 #[test]
1757 fn test_process_missed_votes() {
1758 let account_a = solana_pubkey::new_rand();
1759 let mut vote_state_a = vote_state_new_for_test(&account_a);
1760 let account_b = solana_pubkey::new_rand();
1761 let mut vote_state_b = vote_state_new_for_test(&account_b);
1762
1763 (0..5).for_each(|i| process_slot_vote_unchecked(&mut vote_state_a, i as u64));
1765 assert_ne!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1766
1767 let slots = (0u64..MAX_RECENT_VOTES as u64).collect();
1769 let vote = Vote::new(slots, Hash::default());
1770 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
1771
1772 assert_eq!(
1773 process_vote(&mut vote_state_a, &vote, &slot_hashes, 0, 0),
1774 Ok(())
1775 );
1776 assert_eq!(
1777 process_vote(&mut vote_state_b, &vote, &slot_hashes, 0, 0),
1778 Ok(())
1779 );
1780 assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1781 }
1782
1783 #[test]
1784 fn test_process_vote_skips_old_vote() {
1785 let mut vote_state = VoteState::default();
1786
1787 let vote = Vote::new(vec![0], Hash::default());
1788 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1789 assert_eq!(
1790 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1791 Ok(())
1792 );
1793 let recent = recent_votes(&vote_state);
1794 assert_eq!(
1795 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1796 Err(VoteError::VoteTooOld)
1797 );
1798 assert_eq!(recent, recent_votes(&vote_state));
1799 }
1800
1801 #[test]
1802 fn test_check_slots_are_valid_vote_empty_slot_hashes() {
1803 let vote_state = VoteState::default();
1804
1805 let vote = Vote::new(vec![0], Hash::default());
1806 assert_eq!(
1807 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &[]),
1808 Err(VoteError::VoteTooOld)
1809 );
1810 }
1811
1812 #[test]
1813 fn test_check_slots_are_valid_new_vote() {
1814 let vote_state = VoteState::default();
1815
1816 let vote = Vote::new(vec![0], Hash::default());
1817 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1818 assert_eq!(
1819 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1820 Ok(())
1821 );
1822 }
1823
1824 #[test]
1825 fn test_check_slots_are_valid_bad_hash() {
1826 let vote_state = VoteState::default();
1827
1828 let vote = Vote::new(vec![0], Hash::default());
1829 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
1830 assert_eq!(
1831 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1832 Err(VoteError::SlotHashMismatch)
1833 );
1834 }
1835
1836 #[test]
1837 fn test_check_slots_are_valid_bad_slot() {
1838 let vote_state = VoteState::default();
1839
1840 let vote = Vote::new(vec![1], Hash::default());
1841 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1842 assert_eq!(
1843 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1844 Err(VoteError::SlotsMismatch)
1845 );
1846 }
1847
1848 #[test]
1849 fn test_check_slots_are_valid_duplicate_vote() {
1850 let mut vote_state = VoteState::default();
1851
1852 let vote = Vote::new(vec![0], Hash::default());
1853 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1854 assert_eq!(
1855 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1856 Ok(())
1857 );
1858 assert_eq!(
1859 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1860 Err(VoteError::VoteTooOld)
1861 );
1862 }
1863
1864 #[test]
1865 fn test_check_slots_are_valid_next_vote() {
1866 let mut vote_state = VoteState::default();
1867
1868 let vote = Vote::new(vec![0], Hash::default());
1869 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1870 assert_eq!(
1871 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1872 Ok(())
1873 );
1874
1875 let vote = Vote::new(vec![0, 1], Hash::default());
1876 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1877 assert_eq!(
1878 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1879 Ok(())
1880 );
1881 }
1882
1883 #[test]
1884 fn test_check_slots_are_valid_next_vote_only() {
1885 let mut vote_state = VoteState::default();
1886
1887 let vote = Vote::new(vec![0], Hash::default());
1888 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1889 assert_eq!(
1890 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1891 Ok(())
1892 );
1893
1894 let vote = Vote::new(vec![1], Hash::default());
1895 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1896 assert_eq!(
1897 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1898 Ok(())
1899 );
1900 }
1901 #[test]
1902 fn test_process_vote_empty_slots() {
1903 let mut vote_state = VoteState::default();
1904
1905 let vote = Vote::new(vec![], Hash::default());
1906 assert_eq!(
1907 process_vote(&mut vote_state, &vote, &[], 0, 0),
1908 Err(VoteError::EmptySlots)
1909 );
1910 }
1911
1912 pub fn process_new_vote_state_from_lockouts(
1913 vote_state: &mut VoteState,
1914 new_state: VecDeque<Lockout>,
1915 new_root: Option<Slot>,
1916 timestamp: Option<i64>,
1917 epoch: Epoch,
1918 feature_set: Option<&FeatureSet>,
1919 ) -> Result<(), VoteError> {
1920 process_new_vote_state(
1921 vote_state,
1922 new_state.into_iter().map(LandedVote::from).collect(),
1923 new_root,
1924 timestamp,
1925 epoch,
1926 0,
1927 feature_set,
1928 )
1929 }
1930
1931 #[test]
1933 fn test_vote_state_update_increment_credits() {
1934 let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
1936
1937 let test_vote_groups: Vec<Vec<Slot>> = vec![
1940 vec![1, 2, 3, 4, 5, 6, 7, 8],
1942 vec![
1943 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1944 30, 31,
1945 ],
1946 vec![32],
1948 vec![33],
1950 vec![34, 35],
1952 vec![36, 37, 38],
1954 vec![
1956 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
1957 60, 61, 62, 63, 64, 65, 66, 67, 68,
1958 ],
1959 vec![
1961 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
1962 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1963 ],
1964 vec![100, 101, 106, 107, 112, 116, 120, 121, 122, 124],
1966 vec![200, 201],
1968 vec![
1969 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
1970 218, 219, 220, 221, 222, 223, 224, 225, 226,
1971 ],
1972 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
1973 ];
1974
1975 let feature_set = FeatureSet::default();
1976
1977 for vote_group in test_vote_groups {
1978 let mut vote_state_after_vote = vote_state.clone();
1980
1981 process_vote_unchecked(
1982 &mut vote_state_after_vote,
1983 Vote {
1984 slots: vote_group.clone(),
1985 hash: Hash::new_unique(),
1986 timestamp: None,
1987 },
1988 )
1989 .unwrap();
1990
1991 assert_eq!(
1993 process_new_vote_state(
1994 &mut vote_state,
1995 vote_state_after_vote.votes,
1996 vote_state_after_vote.root_slot,
1997 None,
1998 0,
1999 0,
2000 Some(&feature_set)
2001 ),
2002 Ok(())
2003 );
2004
2005 assert_eq!(
2007 vote_state.epoch_credits,
2008 vote_state_after_vote.epoch_credits
2009 );
2010 }
2011 }
2012
2013 #[test]
2015 fn test_timely_credits() {
2016 let test_vote_groups: Vec<(Vec<Slot>, Slot, u32)> = vec![
2020 (
2022 vec![1, 2, 3, 4, 5, 6, 7, 8],
2023 9,
2024 0,
2026 ),
2027 (
2028 vec![
2029 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
2030 29, 30, 31,
2031 ],
2032 34,
2033 0,
2036 ),
2037 (
2039 vec![32],
2040 35,
2041 10,
2044 ),
2045 (
2047 vec![33],
2048 36,
2049 10 + 11, ),
2053 (
2055 vec![34, 35],
2056 37,
2057 21 + 12 + 13, ),
2061 (
2063 vec![36, 37, 38],
2064 39,
2065 46 + 14 + 15 + 16, ),
2069 (
2070 vec![
2072 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
2073 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
2074 ],
2075 69,
2076 91 + 16
2086 + 9 + 2
2088 + 3
2089 + 4
2090 + 5
2091 + 6
2092 + 7
2093 + 8
2094 + 9
2095 + 10
2096 + 11
2097 + 12
2098 + 13
2099 + 14
2100 + 15
2101 + 15
2102 + 15
2103 + 15
2104 + 16
2105 + 15
2106 + 16, ),
2108 (
2110 vec![
2111 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
2112 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
2113 ],
2114 100,
2115 327 + 16
2122 + 14 + 2
2124 + 3
2125 + 4
2126 + 5
2127 + 6
2128 + 7
2129 + 8
2130 + 9
2131 + 10
2132 + 11
2133 + 12
2134 + 13
2135 + 14
2136 + 15
2137 + 16
2138 + 16, ),
2140 (
2142 vec![115, 116, 117, 118, 119, 120, 121, 122, 123, 124],
2143 130,
2144 508 + ((74 - 69) + 1), ),
2149 (
2151 vec![200, 201],
2152 202,
2153 514,
2156 ),
2157 (
2158 vec![
2159 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
2160 218, 219, 220, 221, 222, 223, 224, 225, 226,
2161 ],
2162 227,
2163 514 + 9 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13, ),
2169 (
2170 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
2171 237,
2172 613 + 3 + 4 + 5 + 6 + 16 + 16 + 1 + 1 + 1 + 1, ),
2178 ];
2179
2180 let feature_set = FeatureSet::default();
2181
2182 for i in 0..test_vote_groups.len() {
2185 let mut vote_state_1 = VoteState::new(&VoteInit::default(), &Clock::default());
2187 let mut vote_state_2 = VoteState::new(&VoteInit::default(), &Clock::default());
2189 test_vote_groups.iter().take(i + 1).for_each(|vote_group| {
2190 let vote = Vote {
2191 slots: vote_group.0.clone(), hash: Hash::new_unique(),
2193 timestamp: None,
2194 };
2195 let slot_hashes: Vec<_> =
2196 vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
2197 assert_eq!(
2198 process_vote(
2199 &mut vote_state_1,
2200 &vote,
2201 &slot_hashes,
2202 0,
2203 vote_group.1, ),
2205 Ok(())
2206 );
2207
2208 assert_eq!(
2209 process_new_vote_state(
2210 &mut vote_state_2,
2211 vote_state_1.votes.clone(),
2212 vote_state_1.root_slot,
2213 None,
2214 0,
2215 vote_group.1, Some(&feature_set)
2217 ),
2218 Ok(())
2219 );
2220 });
2221
2222 let vote_group = &test_vote_groups[i];
2224 assert_eq!(vote_state_1.credits(), vote_group.2 as u64); assert_eq!(vote_state_2.credits(), vote_group.2 as u64); }
2227 }
2228
2229 #[test]
2230 fn test_retroactive_voting_timely_credits() {
2231 #[allow(clippy::type_complexity)]
2237 let test_vote_state_updates: Vec<(Vec<(Slot, u32)>, Slot, Option<Slot>, u32)> = vec![
2238 (
2240 vec![(7, 4), (8, 3), (9, 2), (10, 1)],
2241 11,
2242 None,
2244 0,
2246 ),
2247 (
2249 vec![
2250 (1, 10),
2251 (2, 9),
2252 (3, 8),
2253 (4, 7),
2254 (5, 6),
2255 (6, 5),
2256 (7, 4),
2257 (8, 3),
2258 (9, 2),
2259 (10, 1),
2260 ],
2261 12,
2262 None,
2264 0,
2266 ),
2267 (
2269 vec![
2270 (11, 31),
2271 (12, 30),
2272 (13, 29),
2273 (14, 28),
2274 (15, 27),
2275 (16, 26),
2276 (17, 25),
2277 (18, 24),
2278 (19, 23),
2279 (20, 22),
2280 (21, 21),
2281 (22, 20),
2282 (23, 19),
2283 (24, 18),
2284 (25, 17),
2285 (26, 16),
2286 (27, 15),
2287 (28, 14),
2288 (29, 13),
2289 (30, 12),
2290 (31, 11),
2291 (32, 10),
2292 (33, 9),
2293 (34, 8),
2294 (35, 7),
2295 (36, 6),
2296 (37, 5),
2297 (38, 4),
2298 (39, 3),
2299 (40, 2),
2300 (41, 1),
2301 ],
2302 42,
2303 Some(10),
2305 7 + 8 + 9 + 10 + 11 + 12 + 14 + 15 + 16 + 16,
2308 ),
2309 ];
2310
2311 let feature_set = FeatureSet::default();
2312
2313 let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
2315
2316 test_vote_state_updates
2319 .iter()
2320 .for_each(|proposed_vote_state| {
2321 let new_state = proposed_vote_state
2322 .0 .iter()
2324 .map(|(slot, confirmation_count)| LandedVote {
2325 latency: 0,
2326 lockout: Lockout::new_with_confirmation_count(*slot, *confirmation_count),
2327 })
2328 .collect::<VecDeque<LandedVote>>();
2329 assert_eq!(
2330 process_new_vote_state(
2331 &mut vote_state,
2332 new_state,
2333 proposed_vote_state.2, None,
2335 0,
2336 proposed_vote_state.1, Some(&feature_set)
2338 ),
2339 Ok(())
2340 );
2341
2342 assert_eq!(vote_state.credits(), proposed_vote_state.3 as u64);
2344 });
2345 }
2346
2347 #[test]
2348 fn test_process_new_vote_too_many_votes() {
2349 let mut vote_state1 = VoteState::default();
2350 let bad_votes: VecDeque<Lockout> = (0..=MAX_LOCKOUT_HISTORY)
2351 .map(|slot| {
2352 Lockout::new_with_confirmation_count(
2353 slot as Slot,
2354 (MAX_LOCKOUT_HISTORY - slot + 1) as u32,
2355 )
2356 })
2357 .collect();
2358
2359 let current_epoch = vote_state1.current_epoch();
2360 assert_eq!(
2361 process_new_vote_state_from_lockouts(
2362 &mut vote_state1,
2363 bad_votes,
2364 None,
2365 None,
2366 current_epoch,
2367 None
2368 ),
2369 Err(VoteError::TooManyVotes)
2370 );
2371 }
2372
2373 #[test]
2374 fn test_process_new_vote_state_root_rollback() {
2375 let mut vote_state1 = VoteState::default();
2376 for i in 0..MAX_LOCKOUT_HISTORY + 2 {
2377 process_slot_vote_unchecked(&mut vote_state1, i as Slot);
2378 }
2379 assert_eq!(vote_state1.root_slot.unwrap(), 1);
2380
2381 let mut vote_state2 = vote_state1.clone();
2384 process_slot_vote_unchecked(&mut vote_state2, MAX_LOCKOUT_HISTORY as Slot + 3);
2385
2386 let lesser_root = Some(0);
2388
2389 let current_epoch = vote_state2.current_epoch();
2390 assert_eq!(
2391 process_new_vote_state(
2392 &mut vote_state1,
2393 vote_state2.votes.clone(),
2394 lesser_root,
2395 None,
2396 current_epoch,
2397 0,
2398 None,
2399 ),
2400 Err(VoteError::RootRollBack)
2401 );
2402
2403 let none_root = None;
2405 assert_eq!(
2406 process_new_vote_state(
2407 &mut vote_state1,
2408 vote_state2.votes.clone(),
2409 none_root,
2410 None,
2411 current_epoch,
2412 0,
2413 None,
2414 ),
2415 Err(VoteError::RootRollBack)
2416 );
2417 }
2418
2419 #[test]
2420 fn test_process_new_vote_state_zero_confirmations() {
2421 let mut vote_state1 = VoteState::default();
2422 let current_epoch = vote_state1.current_epoch();
2423
2424 let bad_votes: VecDeque<Lockout> = vec![
2425 Lockout::new_with_confirmation_count(0, 0),
2426 Lockout::new_with_confirmation_count(1, 1),
2427 ]
2428 .into_iter()
2429 .collect();
2430 assert_eq!(
2431 process_new_vote_state_from_lockouts(
2432 &mut vote_state1,
2433 bad_votes,
2434 None,
2435 None,
2436 current_epoch,
2437 None
2438 ),
2439 Err(VoteError::ZeroConfirmations)
2440 );
2441
2442 let bad_votes: VecDeque<Lockout> = vec![
2443 Lockout::new_with_confirmation_count(0, 2),
2444 Lockout::new_with_confirmation_count(1, 0),
2445 ]
2446 .into_iter()
2447 .collect();
2448 assert_eq!(
2449 process_new_vote_state_from_lockouts(
2450 &mut vote_state1,
2451 bad_votes,
2452 None,
2453 None,
2454 current_epoch,
2455 None
2456 ),
2457 Err(VoteError::ZeroConfirmations)
2458 );
2459 }
2460
2461 #[test]
2462 fn test_process_new_vote_state_confirmations_too_large() {
2463 let mut vote_state1 = VoteState::default();
2464 let current_epoch = vote_state1.current_epoch();
2465
2466 let good_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2467 0,
2468 MAX_LOCKOUT_HISTORY as u32,
2469 )]
2470 .into_iter()
2471 .collect();
2472
2473 process_new_vote_state_from_lockouts(
2474 &mut vote_state1,
2475 good_votes,
2476 None,
2477 None,
2478 current_epoch,
2479 None,
2480 )
2481 .unwrap();
2482
2483 let mut vote_state1 = VoteState::default();
2484 let bad_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2485 0,
2486 MAX_LOCKOUT_HISTORY as u32 + 1,
2487 )]
2488 .into_iter()
2489 .collect();
2490 assert_eq!(
2491 process_new_vote_state_from_lockouts(
2492 &mut vote_state1,
2493 bad_votes,
2494 None,
2495 None,
2496 current_epoch,
2497 None
2498 ),
2499 Err(VoteError::ConfirmationTooLarge)
2500 );
2501 }
2502
2503 #[test]
2504 fn test_process_new_vote_state_slot_smaller_than_root() {
2505 let mut vote_state1 = VoteState::default();
2506 let current_epoch = vote_state1.current_epoch();
2507 let root_slot = 5;
2508
2509 let bad_votes: VecDeque<Lockout> = vec![
2510 Lockout::new_with_confirmation_count(root_slot, 2),
2511 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2512 ]
2513 .into_iter()
2514 .collect();
2515 assert_eq!(
2516 process_new_vote_state_from_lockouts(
2517 &mut vote_state1,
2518 bad_votes,
2519 Some(root_slot),
2520 None,
2521 current_epoch,
2522 None,
2523 ),
2524 Err(VoteError::SlotSmallerThanRoot)
2525 );
2526
2527 let bad_votes: VecDeque<Lockout> = vec![
2528 Lockout::new_with_confirmation_count(root_slot - 1, 2),
2529 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2530 ]
2531 .into_iter()
2532 .collect();
2533 assert_eq!(
2534 process_new_vote_state_from_lockouts(
2535 &mut vote_state1,
2536 bad_votes,
2537 Some(root_slot),
2538 None,
2539 current_epoch,
2540 None,
2541 ),
2542 Err(VoteError::SlotSmallerThanRoot)
2543 );
2544 }
2545
2546 #[test]
2547 fn test_process_new_vote_state_slots_not_ordered() {
2548 let mut vote_state1 = VoteState::default();
2549 let current_epoch = vote_state1.current_epoch();
2550
2551 let bad_votes: VecDeque<Lockout> = vec![
2552 Lockout::new_with_confirmation_count(1, 2),
2553 Lockout::new_with_confirmation_count(0, 1),
2554 ]
2555 .into_iter()
2556 .collect();
2557 assert_eq!(
2558 process_new_vote_state_from_lockouts(
2559 &mut vote_state1,
2560 bad_votes,
2561 None,
2562 None,
2563 current_epoch,
2564 None
2565 ),
2566 Err(VoteError::SlotsNotOrdered)
2567 );
2568
2569 let bad_votes: VecDeque<Lockout> = vec![
2570 Lockout::new_with_confirmation_count(1, 2),
2571 Lockout::new_with_confirmation_count(1, 1),
2572 ]
2573 .into_iter()
2574 .collect();
2575 assert_eq!(
2576 process_new_vote_state_from_lockouts(
2577 &mut vote_state1,
2578 bad_votes,
2579 None,
2580 None,
2581 current_epoch,
2582 None
2583 ),
2584 Err(VoteError::SlotsNotOrdered)
2585 );
2586 }
2587
2588 #[test]
2589 fn test_process_new_vote_state_confirmations_not_ordered() {
2590 let mut vote_state1 = VoteState::default();
2591 let current_epoch = vote_state1.current_epoch();
2592
2593 let bad_votes: VecDeque<Lockout> = vec![
2594 Lockout::new_with_confirmation_count(0, 1),
2595 Lockout::new_with_confirmation_count(1, 2),
2596 ]
2597 .into_iter()
2598 .collect();
2599 assert_eq!(
2600 process_new_vote_state_from_lockouts(
2601 &mut vote_state1,
2602 bad_votes,
2603 None,
2604 None,
2605 current_epoch,
2606 None
2607 ),
2608 Err(VoteError::ConfirmationsNotOrdered)
2609 );
2610
2611 let bad_votes: VecDeque<Lockout> = vec![
2612 Lockout::new_with_confirmation_count(0, 1),
2613 Lockout::new_with_confirmation_count(1, 1),
2614 ]
2615 .into_iter()
2616 .collect();
2617 assert_eq!(
2618 process_new_vote_state_from_lockouts(
2619 &mut vote_state1,
2620 bad_votes,
2621 None,
2622 None,
2623 current_epoch,
2624 None
2625 ),
2626 Err(VoteError::ConfirmationsNotOrdered)
2627 );
2628 }
2629
2630 #[test]
2631 fn test_process_new_vote_state_new_vote_state_lockout_mismatch() {
2632 let mut vote_state1 = VoteState::default();
2633 let current_epoch = vote_state1.current_epoch();
2634
2635 let bad_votes: VecDeque<Lockout> = vec![
2636 Lockout::new_with_confirmation_count(0, 2),
2637 Lockout::new_with_confirmation_count(7, 1),
2638 ]
2639 .into_iter()
2640 .collect();
2641
2642 assert_eq!(
2644 process_new_vote_state_from_lockouts(
2645 &mut vote_state1,
2646 bad_votes,
2647 None,
2648 None,
2649 current_epoch,
2650 None
2651 ),
2652 Err(VoteError::NewVoteStateLockoutMismatch)
2653 );
2654 }
2655
2656 #[test]
2657 fn test_process_new_vote_state_confirmation_rollback() {
2658 let mut vote_state1 = VoteState::default();
2659 let current_epoch = vote_state1.current_epoch();
2660 let votes: VecDeque<Lockout> = vec![
2661 Lockout::new_with_confirmation_count(0, 4),
2662 Lockout::new_with_confirmation_count(1, 3),
2663 ]
2664 .into_iter()
2665 .collect();
2666 process_new_vote_state_from_lockouts(
2667 &mut vote_state1,
2668 votes,
2669 None,
2670 None,
2671 current_epoch,
2672 None,
2673 )
2674 .unwrap();
2675
2676 let votes: VecDeque<Lockout> = vec![
2677 Lockout::new_with_confirmation_count(0, 4),
2678 Lockout::new_with_confirmation_count(1, 2),
2680 Lockout::new_with_confirmation_count(2, 1),
2681 ]
2682 .into_iter()
2683 .collect();
2684 assert_eq!(
2687 process_new_vote_state_from_lockouts(
2688 &mut vote_state1,
2689 votes,
2690 None,
2691 None,
2692 current_epoch,
2693 None
2694 ),
2695 Err(VoteError::ConfirmationRollBack)
2696 );
2697 }
2698
2699 #[test]
2700 fn test_process_new_vote_state_root_progress() {
2701 let mut vote_state1 = VoteState::default();
2702 for i in 0..MAX_LOCKOUT_HISTORY {
2703 process_slot_vote_unchecked(&mut vote_state1, i as u64);
2704 }
2705
2706 assert!(vote_state1.root_slot.is_none());
2707 let mut vote_state2 = vote_state1.clone();
2708
2709 for new_vote in MAX_LOCKOUT_HISTORY + 1..=MAX_LOCKOUT_HISTORY + 2 {
2716 process_slot_vote_unchecked(&mut vote_state2, new_vote as Slot);
2717 assert_ne!(vote_state1.root_slot, vote_state2.root_slot);
2718
2719 process_new_vote_state(
2720 &mut vote_state1,
2721 vote_state2.votes.clone(),
2722 vote_state2.root_slot,
2723 None,
2724 vote_state2.current_epoch(),
2725 0,
2726 None,
2727 )
2728 .unwrap();
2729
2730 assert_eq!(vote_state1, vote_state2);
2731 }
2732 }
2733
2734 #[test]
2735 fn test_process_new_vote_state_same_slot_but_not_common_ancestor() {
2736 let mut vote_state1 = VoteState::default();
2755 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5]);
2756 assert_eq!(
2757 vote_state1
2758 .votes
2759 .iter()
2760 .map(|vote| vote.slot())
2761 .collect::<Vec<Slot>>(),
2762 vec![1, 5]
2763 );
2764
2765 let mut vote_state2 = VoteState::default();
2767 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2768 assert_eq!(
2769 vote_state2
2770 .votes
2771 .iter()
2772 .map(|vote| vote.slot())
2773 .collect::<Vec<Slot>>(),
2774 vec![1, 2, 3, 5, 7]
2775 );
2776
2777 process_new_vote_state(
2779 &mut vote_state1,
2780 vote_state2.votes.clone(),
2781 vote_state2.root_slot,
2782 None,
2783 vote_state2.current_epoch(),
2784 0,
2785 None,
2786 )
2787 .unwrap();
2788
2789 assert_eq!(vote_state1, vote_state2);
2790 }
2791
2792 #[test]
2793 fn test_process_new_vote_state_lockout_violation() {
2794 let mut vote_state1 = VoteState::default();
2796 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 4, 5]);
2797 assert_eq!(
2798 vote_state1
2799 .votes
2800 .iter()
2801 .map(|vote| vote.slot())
2802 .collect::<Vec<Slot>>(),
2803 vec![1, 2, 4, 5]
2804 );
2805
2806 let mut vote_state2 = VoteState::default();
2809 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2810 assert_eq!(
2811 vote_state2
2812 .votes
2813 .iter()
2814 .map(|vote| vote.slot())
2815 .collect::<Vec<Slot>>(),
2816 vec![1, 2, 3, 5, 7]
2817 );
2818
2819 assert_eq!(
2821 process_new_vote_state(
2822 &mut vote_state1,
2823 vote_state2.votes.clone(),
2824 vote_state2.root_slot,
2825 None,
2826 vote_state2.current_epoch(),
2827 0,
2828 None
2829 ),
2830 Err(VoteError::LockoutConflict)
2831 );
2832 }
2833
2834 #[test]
2835 fn test_process_new_vote_state_lockout_violation2() {
2836 let mut vote_state1 = VoteState::default();
2838 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5, 6, 7]);
2839 assert_eq!(
2840 vote_state1
2841 .votes
2842 .iter()
2843 .map(|vote| vote.slot())
2844 .collect::<Vec<Slot>>(),
2845 vec![1, 5, 6, 7]
2846 );
2847
2848 let mut vote_state2 = VoteState::default();
2851 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 6, 8]);
2852 assert_eq!(
2853 vote_state2
2854 .votes
2855 .iter()
2856 .map(|vote| vote.slot())
2857 .collect::<Vec<Slot>>(),
2858 vec![1, 2, 3, 5, 6, 8]
2859 );
2860
2861 assert_eq!(
2864 process_new_vote_state(
2865 &mut vote_state1,
2866 vote_state2.votes.clone(),
2867 vote_state2.root_slot,
2868 None,
2869 vote_state2.current_epoch(),
2870 0,
2871 None
2872 ),
2873 Err(VoteError::LockoutConflict)
2874 );
2875 }
2876
2877 #[test]
2878 fn test_process_new_vote_state_expired_ancestor_not_removed() {
2879 let mut vote_state1 = VoteState::default();
2881 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 3, 9]);
2882 assert_eq!(
2883 vote_state1
2884 .votes
2885 .iter()
2886 .map(|vote| vote.slot())
2887 .collect::<Vec<Slot>>(),
2888 vec![1, 9]
2889 );
2890
2891 let mut vote_state2 = vote_state1.clone();
2894 process_slot_vote_unchecked(&mut vote_state2, 10);
2895
2896 assert_eq!(vote_state2.votes[0].slot(), 1);
2899 assert_eq!(vote_state2.votes[0].lockout.last_locked_out_slot(), 9);
2900 assert_eq!(
2901 vote_state2
2902 .votes
2903 .iter()
2904 .map(|vote| vote.slot())
2905 .collect::<Vec<Slot>>(),
2906 vec![1, 9, 10]
2907 );
2908
2909 process_new_vote_state(
2911 &mut vote_state1,
2912 vote_state2.votes.clone(),
2913 vote_state2.root_slot,
2914 None,
2915 vote_state2.current_epoch(),
2916 0,
2917 None,
2918 )
2919 .unwrap();
2920 assert_eq!(vote_state1, vote_state2,);
2921 }
2922
2923 #[test]
2924 fn test_process_new_vote_current_state_contains_bigger_slots() {
2925 let mut vote_state1 = VoteState::default();
2926 process_slot_votes_unchecked(&mut vote_state1, &[6, 7, 8]);
2927 assert_eq!(
2928 vote_state1
2929 .votes
2930 .iter()
2931 .map(|vote| vote.slot())
2932 .collect::<Vec<Slot>>(),
2933 vec![6, 7, 8]
2934 );
2935
2936 let bad_votes: VecDeque<Lockout> = vec![
2938 Lockout::new_with_confirmation_count(2, 5),
2939 Lockout::new_with_confirmation_count(14, 1),
2941 ]
2942 .into_iter()
2943 .collect();
2944 let root = Some(1);
2945
2946 let current_epoch = vote_state1.current_epoch();
2947 assert_eq!(
2948 process_new_vote_state_from_lockouts(
2949 &mut vote_state1,
2950 bad_votes,
2951 root,
2952 None,
2953 current_epoch,
2954 None
2955 ),
2956 Err(VoteError::LockoutConflict)
2957 );
2958
2959 let good_votes: VecDeque<LandedVote> = vec![
2960 Lockout::new_with_confirmation_count(2, 5).into(),
2961 Lockout::new_with_confirmation_count(15, 1).into(),
2962 ]
2963 .into_iter()
2964 .collect();
2965
2966 let current_epoch = vote_state1.current_epoch();
2967 process_new_vote_state(
2968 &mut vote_state1,
2969 good_votes.clone(),
2970 root,
2971 None,
2972 current_epoch,
2973 0,
2974 None,
2975 )
2976 .unwrap();
2977 assert_eq!(vote_state1.votes, good_votes);
2978 }
2979
2980 #[test]
2981 fn test_filter_old_votes() {
2982 let mut vote_state = VoteState::default();
2983 let old_vote_slot = 1;
2984 let vote = Vote::new(vec![old_vote_slot], Hash::default());
2985
2986 let slot_hashes = vec![(3, Hash::new_unique()), (2, Hash::new_unique())];
2989 assert_eq!(
2990 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
2991 Err(VoteError::VotesTooOldAllFiltered)
2992 );
2993
2994 let vote_slot = 2;
2997 let vote_slot_hash = slot_hashes
2998 .iter()
2999 .find(|(slot, _hash)| *slot == vote_slot)
3000 .unwrap()
3001 .1;
3002
3003 let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
3004 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0).unwrap();
3005 assert_eq!(
3006 vote_state
3007 .votes
3008 .into_iter()
3009 .map(|vote| vote.lockout)
3010 .collect::<Vec<Lockout>>(),
3011 vec![Lockout::new_with_confirmation_count(vote_slot, 1)]
3012 );
3013 }
3014
3015 fn build_slot_hashes(slots: Vec<Slot>) -> Vec<(Slot, Hash)> {
3016 slots
3017 .iter()
3018 .rev()
3019 .map(|x| (*x, Hash::new_unique()))
3020 .collect()
3021 }
3022
3023 fn build_vote_state(vote_slots: Vec<Slot>, slot_hashes: &[(Slot, Hash)]) -> VoteState {
3024 let mut vote_state = VoteState::default();
3025
3026 if !vote_slots.is_empty() {
3027 let vote_hash = slot_hashes
3028 .iter()
3029 .find(|(slot, _hash)| slot == vote_slots.last().unwrap())
3030 .unwrap()
3031 .1;
3032 let vote = Vote::new(vote_slots, vote_hash);
3033 process_vote_unfiltered(&mut vote_state, &vote.slots, &vote, slot_hashes, 0, 0)
3034 .unwrap();
3035 }
3036
3037 vote_state
3038 }
3039
3040 #[test]
3041 fn test_check_and_filter_proposed_vote_state_empty() {
3042 let empty_slot_hashes = build_slot_hashes(vec![]);
3043 let empty_vote_state = build_vote_state(vec![], &empty_slot_hashes);
3044
3045 let mut tower_sync = TowerSync::from(vec![]);
3047 assert_eq!(
3048 check_and_filter_proposed_vote_state(
3049 &empty_vote_state,
3050 &mut tower_sync.lockouts,
3051 &mut tower_sync.root,
3052 tower_sync.hash,
3053 &empty_slot_hashes
3054 ),
3055 Err(VoteError::EmptySlots),
3056 );
3057
3058 let mut tower_sync = TowerSync::from(vec![(0, 1)]);
3060 assert_eq!(
3061 check_and_filter_proposed_vote_state(
3062 &empty_vote_state,
3063 &mut tower_sync.lockouts,
3064 &mut tower_sync.root,
3065 tower_sync.hash,
3066 &empty_slot_hashes
3067 ),
3068 Err(VoteError::SlotsMismatch),
3069 );
3070 }
3071
3072 #[test]
3073 fn test_check_and_filter_proposed_vote_state_too_old() {
3074 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3075 let latest_vote = 4;
3076 let vote_state = build_vote_state(vec![1, 2, 3, latest_vote], &slot_hashes);
3077
3078 let mut tower_sync = TowerSync::from(vec![(latest_vote, 1)]);
3081 assert_eq!(
3082 check_and_filter_proposed_vote_state(
3083 &vote_state,
3084 &mut tower_sync.lockouts,
3085 &mut tower_sync.root,
3086 tower_sync.hash,
3087 &slot_hashes
3088 ),
3089 Err(VoteError::VoteTooOld),
3090 );
3091
3092 let earliest_slot_in_history = latest_vote + 2;
3096 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history]);
3097 let mut tower_sync = TowerSync::from(vec![(earliest_slot_in_history - 1, 1)]);
3098 assert_eq!(
3099 check_and_filter_proposed_vote_state(
3100 &vote_state,
3101 &mut tower_sync.lockouts,
3102 &mut tower_sync.root,
3103 tower_sync.hash,
3104 &slot_hashes
3105 ),
3106 Err(VoteError::VoteTooOld),
3107 );
3108 }
3109
3110 fn run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3111 earliest_slot_in_history: Slot,
3112 current_vote_state_slots: Vec<Slot>,
3113 current_vote_state_root: Option<Slot>,
3114 proposed_slots_and_lockouts: Vec<(Slot, u32)>,
3115 proposed_root: Slot,
3116 expected_root: Option<Slot>,
3117 expected_vote_state: Vec<Lockout>,
3118 ) {
3119 assert!(proposed_root < earliest_slot_in_history);
3120 assert_eq!(
3121 expected_root,
3122 current_vote_state_slots
3123 .iter()
3124 .rev()
3125 .find(|slot| **slot <= proposed_root)
3126 .cloned()
3127 );
3128 let latest_slot_in_history = proposed_slots_and_lockouts
3129 .last()
3130 .unwrap()
3131 .0
3132 .max(earliest_slot_in_history);
3133 let mut slot_hashes = build_slot_hashes(
3134 (current_vote_state_slots.first().copied().unwrap_or(0)..=latest_slot_in_history)
3135 .collect::<Vec<Slot>>(),
3136 );
3137
3138 let mut vote_state = build_vote_state(current_vote_state_slots, &slot_hashes);
3139 vote_state.root_slot = current_vote_state_root;
3140
3141 slot_hashes.retain(|slot| slot.0 >= earliest_slot_in_history);
3142 assert!(!proposed_slots_and_lockouts.is_empty());
3143 let proposed_hash = slot_hashes
3144 .iter()
3145 .find(|(slot, _hash)| *slot == proposed_slots_and_lockouts.last().unwrap().0)
3146 .unwrap()
3147 .1;
3148
3149 let mut tower_sync = TowerSync::from(proposed_slots_and_lockouts);
3153 tower_sync.hash = proposed_hash;
3154 tower_sync.root = Some(proposed_root);
3155 check_and_filter_proposed_vote_state(
3156 &vote_state,
3157 &mut tower_sync.lockouts,
3158 &mut tower_sync.root,
3159 tower_sync.hash,
3160 &slot_hashes,
3161 )
3162 .unwrap();
3163 assert_eq!(tower_sync.root, expected_root);
3164
3165 assert!(do_process_tower_sync(
3168 &mut vote_state,
3169 &slot_hashes,
3170 0,
3171 0,
3172 tower_sync.clone(),
3173 Some(&FeatureSet::all_enabled()),
3174 )
3175 .is_ok());
3176 assert_eq!(vote_state.root_slot, expected_root);
3177 assert_eq!(
3178 vote_state
3179 .votes
3180 .into_iter()
3181 .map(|vote| vote.lockout)
3182 .collect::<Vec<Lockout>>(),
3183 expected_vote_state,
3184 );
3185 }
3186
3187 #[test]
3188 fn test_check_and_filter_proposed_vote_state_older_than_history_root() {
3189 let earliest_slot_in_history = 5;
3192 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3193 let current_vote_state_root = None;
3194 let proposed_slots_and_lockouts = vec![(5, 1)];
3195 let proposed_root = 4;
3196 let expected_root = Some(4);
3197 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3198 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3199 earliest_slot_in_history,
3200 current_vote_state_slots,
3201 current_vote_state_root,
3202 proposed_slots_and_lockouts,
3203 proposed_root,
3204 expected_root,
3205 expected_vote_state,
3206 );
3207
3208 let earliest_slot_in_history = 5;
3211 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3212 let current_vote_state_root = Some(0);
3213 let proposed_slots_and_lockouts = vec![(5, 1)];
3214 let proposed_root = 4;
3215 let expected_root = Some(4);
3216 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3217 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3218 earliest_slot_in_history,
3219 current_vote_state_slots,
3220 current_vote_state_root,
3221 proposed_slots_and_lockouts,
3222 proposed_root,
3223 expected_root,
3224 expected_vote_state,
3225 );
3226
3227 let earliest_slot_in_history = 5;
3230 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3231 let current_vote_state_root = Some(0);
3232 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3233 let proposed_root = 3;
3234 let expected_root = Some(3);
3235 let expected_vote_state = vec![
3236 Lockout::new_with_confirmation_count(4, 2),
3237 Lockout::new_with_confirmation_count(5, 1),
3238 ];
3239 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3240 earliest_slot_in_history,
3241 current_vote_state_slots,
3242 current_vote_state_root,
3243 proposed_slots_and_lockouts,
3244 proposed_root,
3245 expected_root,
3246 expected_vote_state,
3247 );
3248
3249 let earliest_slot_in_history = 5;
3251 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 4];
3252 let current_vote_state_root = Some(0);
3253 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3254 let proposed_root = 3;
3255 let expected_root = Some(2);
3256 let expected_vote_state = vec![
3257 Lockout::new_with_confirmation_count(4, 2),
3258 Lockout::new_with_confirmation_count(5, 1),
3259 ];
3260 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3261 earliest_slot_in_history,
3262 current_vote_state_slots,
3263 current_vote_state_root,
3264 proposed_slots_and_lockouts,
3265 proposed_root,
3266 expected_root,
3267 expected_vote_state,
3268 );
3269
3270 let earliest_slot_in_history = 4;
3273 let current_vote_state_slots: Vec<Slot> = vec![3, 4];
3274 let current_vote_state_root = None;
3275 let proposed_slots_and_lockouts = vec![(3, 3), (4, 2), (5, 1)];
3276 let proposed_root = 2;
3277 let expected_root = None;
3278 let expected_vote_state = vec![
3279 Lockout::new_with_confirmation_count(3, 3),
3280 Lockout::new_with_confirmation_count(4, 2),
3281 Lockout::new_with_confirmation_count(5, 1),
3282 ];
3283 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3284 earliest_slot_in_history,
3285 current_vote_state_slots,
3286 current_vote_state_root,
3287 proposed_slots_and_lockouts,
3288 proposed_root,
3289 expected_root,
3290 expected_vote_state,
3291 );
3292
3293 let earliest_slot_in_history = 4;
3295 let current_vote_state_slots: Vec<Slot> = vec![];
3296 let current_vote_state_root = None;
3297 let proposed_slots_and_lockouts = vec![(5, 1)];
3298 let proposed_root = 2;
3299 let expected_root = None;
3300 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3301 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3302 earliest_slot_in_history,
3303 current_vote_state_slots,
3304 current_vote_state_root,
3305 proposed_slots_and_lockouts,
3306 proposed_root,
3307 expected_root,
3308 expected_vote_state,
3309 );
3310 }
3311
3312 #[test]
3313 fn test_check_and_filter_proposed_vote_state_slots_not_ordered() {
3314 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3315 let vote_state = build_vote_state(vec![1], &slot_hashes);
3316
3317 let vote_slot = 3;
3319 let vote_slot_hash = slot_hashes
3320 .iter()
3321 .find(|(slot, _hash)| *slot == vote_slot)
3322 .unwrap()
3323 .1;
3324 let mut tower_sync = TowerSync::from(vec![(2, 2), (1, 3), (vote_slot, 1)]);
3325 tower_sync.hash = vote_slot_hash;
3326 assert_eq!(
3327 check_and_filter_proposed_vote_state(
3328 &vote_state,
3329 &mut tower_sync.lockouts,
3330 &mut tower_sync.root,
3331 tower_sync.hash,
3332 &slot_hashes
3333 ),
3334 Err(VoteError::SlotsNotOrdered),
3335 );
3336
3337 let mut tower_sync = TowerSync::from(vec![(2, 2), (2, 2), (vote_slot, 1)]);
3339 tower_sync.hash = vote_slot_hash;
3340 assert_eq!(
3341 check_and_filter_proposed_vote_state(
3342 &vote_state,
3343 &mut tower_sync.lockouts,
3344 &mut tower_sync.root,
3345 tower_sync.hash,
3346 &slot_hashes
3347 ),
3348 Err(VoteError::SlotsNotOrdered),
3349 );
3350 }
3351
3352 #[test]
3353 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered() {
3354 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3355 let mut vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes);
3356
3357 let earliest_slot_in_history = 11;
3362 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3363 let vote_slot = 12;
3364 let vote_slot_hash = slot_hashes
3365 .iter()
3366 .find(|(slot, _hash)| *slot == vote_slot)
3367 .unwrap()
3368 .1;
3369 let missing_older_than_history_slot = earliest_slot_in_history - 1;
3370 let mut tower_sync = TowerSync::from(vec![
3371 (1, 4),
3372 (missing_older_than_history_slot, 2),
3373 (vote_slot, 3),
3374 ]);
3375 tower_sync.hash = vote_slot_hash;
3376 check_and_filter_proposed_vote_state(
3377 &vote_state,
3378 &mut tower_sync.lockouts,
3379 &mut tower_sync.root,
3380 tower_sync.hash,
3381 &slot_hashes,
3382 )
3383 .unwrap();
3384
3385 assert_eq!(
3387 tower_sync
3388 .clone()
3389 .lockouts
3390 .into_iter()
3391 .collect::<Vec<Lockout>>(),
3392 vec![
3393 Lockout::new_with_confirmation_count(1, 4),
3394 Lockout::new_with_confirmation_count(vote_slot, 3)
3395 ]
3396 );
3397 assert!(do_process_tower_sync(
3398 &mut vote_state,
3399 &slot_hashes,
3400 0,
3401 0,
3402 tower_sync,
3403 Some(&FeatureSet::all_enabled()),
3404 )
3405 .is_ok());
3406 }
3407
3408 #[test]
3409 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_not_filtered() {
3410 let slot_hashes = build_slot_hashes(vec![4]);
3411 let mut vote_state = build_vote_state(vec![4], &slot_hashes);
3412
3413 let earliest_slot_in_history = 11;
3418 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3419 let vote_slot = 12;
3420 let vote_slot_hash = slot_hashes
3421 .iter()
3422 .find(|(slot, _hash)| *slot == vote_slot)
3423 .unwrap()
3424 .1;
3425 let existing_older_than_history_slot = 4;
3426 let mut tower_sync =
3427 TowerSync::from(vec![(existing_older_than_history_slot, 3), (vote_slot, 2)]);
3428 tower_sync.hash = vote_slot_hash;
3429 check_and_filter_proposed_vote_state(
3430 &vote_state,
3431 &mut tower_sync.lockouts,
3432 &mut tower_sync.root,
3433 tower_sync.hash,
3434 &slot_hashes,
3435 )
3436 .unwrap();
3437 assert_eq!(tower_sync.lockouts.len(), 2);
3439 assert_eq!(
3440 tower_sync
3441 .clone()
3442 .lockouts
3443 .into_iter()
3444 .collect::<Vec<Lockout>>(),
3445 vec![
3446 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3447 Lockout::new_with_confirmation_count(vote_slot, 2)
3448 ]
3449 );
3450 assert!(do_process_tower_sync(
3451 &mut vote_state,
3452 &slot_hashes,
3453 0,
3454 0,
3455 tower_sync,
3456 Some(&FeatureSet::all_enabled()),
3457 )
3458 .is_ok());
3459 }
3460
3461 #[test]
3462 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered_and_not_filtered(
3463 ) {
3464 let slot_hashes = build_slot_hashes(vec![6]);
3465 let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3466
3467 let earliest_slot_in_history = 11;
3478 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3479 let vote_slot = 14;
3480 let vote_slot_hash = slot_hashes
3481 .iter()
3482 .find(|(slot, _hash)| *slot == vote_slot)
3483 .unwrap()
3484 .1;
3485
3486 let missing_older_than_history_slot = 4;
3487 let existing_older_than_history_slot = 6;
3488
3489 let mut tower_sync = TowerSync::from(vec![
3490 (missing_older_than_history_slot, 4),
3491 (existing_older_than_history_slot, 3),
3492 (12, 2),
3493 (vote_slot, 1),
3494 ]);
3495 tower_sync.hash = vote_slot_hash;
3496 check_and_filter_proposed_vote_state(
3497 &vote_state,
3498 &mut tower_sync.lockouts,
3499 &mut tower_sync.root,
3500 tower_sync.hash,
3501 &slot_hashes,
3502 )
3503 .unwrap();
3504 assert_eq!(tower_sync.lockouts.len(), 3);
3505 assert_eq!(
3506 tower_sync
3507 .clone()
3508 .lockouts
3509 .into_iter()
3510 .collect::<Vec<Lockout>>(),
3511 vec![
3512 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3513 Lockout::new_with_confirmation_count(12, 2),
3514 Lockout::new_with_confirmation_count(vote_slot, 1)
3515 ]
3516 );
3517 assert!(do_process_tower_sync(
3518 &mut vote_state,
3519 &slot_hashes,
3520 0,
3521 0,
3522 tower_sync,
3523 Some(&FeatureSet::all_enabled()),
3524 )
3525 .is_ok());
3526 }
3527
3528 #[test]
3529 fn test_check_and_filter_proposed_vote_state_slot_not_on_fork() {
3530 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3531 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3532
3533 let missing_vote_slot = 3;
3539
3540 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3543 let vote_slot_hash = slot_hashes
3544 .iter()
3545 .find(|(slot, _hash)| *slot == vote_slot)
3546 .unwrap()
3547 .1;
3548 let mut tower_sync = TowerSync::from(vec![(missing_vote_slot, 2), (vote_slot, 3)]);
3549 tower_sync.hash = vote_slot_hash;
3550 assert_eq!(
3551 check_and_filter_proposed_vote_state(
3552 &vote_state,
3553 &mut tower_sync.lockouts,
3554 &mut tower_sync.root,
3555 tower_sync.hash,
3556 &slot_hashes
3557 ),
3558 Err(VoteError::SlotsMismatch),
3559 );
3560
3561 let missing_vote_slot = 7;
3563 let mut tower_sync = TowerSync::from(vec![
3564 (2, 5),
3565 (4, 4),
3566 (6, 3),
3567 (missing_vote_slot, 2),
3568 (vote_slot, 1),
3569 ]);
3570 tower_sync.hash = vote_slot_hash;
3571 assert_eq!(
3572 check_and_filter_proposed_vote_state(
3573 &vote_state,
3574 &mut tower_sync.lockouts,
3575 &mut tower_sync.root,
3576 tower_sync.hash,
3577 &slot_hashes
3578 ),
3579 Err(VoteError::SlotsMismatch),
3580 );
3581 }
3582
3583 #[test]
3584 fn test_check_and_filter_proposed_vote_state_root_on_different_fork() {
3585 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3586 let vote_state = build_vote_state(vec![6], &slot_hashes);
3587
3588 let new_root = 3;
3594
3595 let vote_slot = 8;
3598 assert_eq!(vote_slot, slot_hashes.first().unwrap().0);
3599 let vote_slot_hash = slot_hashes
3600 .iter()
3601 .find(|(slot, _hash)| *slot == vote_slot)
3602 .unwrap()
3603 .1;
3604 let mut tower_sync = TowerSync::from(vec![(vote_slot, 1)]);
3605 tower_sync.hash = vote_slot_hash;
3606 tower_sync.root = Some(new_root);
3607 assert_eq!(
3608 check_and_filter_proposed_vote_state(
3609 &vote_state,
3610 &mut tower_sync.lockouts,
3611 &mut tower_sync.root,
3612 tower_sync.hash,
3613 &slot_hashes
3614 ),
3615 Err(VoteError::RootOnDifferentFork),
3616 );
3617 }
3618
3619 #[test]
3620 fn test_check_and_filter_proposed_vote_state_slot_newer_than_slot_history() {
3621 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3622 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3623
3624 let missing_vote_slot = slot_hashes.first().unwrap().0 + 1;
3630 let vote_slot_hash = Hash::new_unique();
3631 let mut tower_sync = TowerSync::from(vec![(8, 2), (missing_vote_slot, 3)]);
3632 tower_sync.hash = vote_slot_hash;
3633 assert_eq!(
3634 check_and_filter_proposed_vote_state(
3635 &vote_state,
3636 &mut tower_sync.lockouts,
3637 &mut tower_sync.root,
3638 tower_sync.hash,
3639 &slot_hashes
3640 ),
3641 Err(VoteError::SlotsMismatch),
3642 );
3643 }
3644
3645 #[test]
3646 fn test_check_and_filter_proposed_vote_state_slot_all_slot_hashes_in_update_ok() {
3647 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3648 let mut vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3649
3650 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3656 let vote_slot_hash = slot_hashes
3657 .iter()
3658 .find(|(slot, _hash)| *slot == vote_slot)
3659 .unwrap()
3660 .1;
3661 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3662 tower_sync.hash = vote_slot_hash;
3663 check_and_filter_proposed_vote_state(
3664 &vote_state,
3665 &mut tower_sync.lockouts,
3666 &mut tower_sync.root,
3667 tower_sync.hash,
3668 &slot_hashes,
3669 )
3670 .unwrap();
3671
3672 assert_eq!(
3674 tower_sync
3675 .clone()
3676 .lockouts
3677 .into_iter()
3678 .collect::<Vec<Lockout>>(),
3679 vec![
3680 Lockout::new_with_confirmation_count(2, 4),
3681 Lockout::new_with_confirmation_count(4, 3),
3682 Lockout::new_with_confirmation_count(6, 2),
3683 Lockout::new_with_confirmation_count(vote_slot, 1)
3684 ]
3685 );
3686
3687 assert!(do_process_tower_sync(
3688 &mut vote_state,
3689 &slot_hashes,
3690 0,
3691 0,
3692 tower_sync,
3693 Some(&FeatureSet::all_enabled()),
3694 )
3695 .is_ok());
3696 }
3697
3698 #[test]
3699 fn test_check_and_filter_proposed_vote_state_slot_some_slot_hashes_in_update_ok() {
3700 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3701 let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3702
3703 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3709 let vote_slot_hash = slot_hashes
3710 .iter()
3711 .find(|(slot, _hash)| *slot == vote_slot)
3712 .unwrap()
3713 .1;
3714 let mut tower_sync = TowerSync::from(vec![(4, 2), (vote_slot, 1)]);
3715 tower_sync.hash = vote_slot_hash;
3716 check_and_filter_proposed_vote_state(
3717 &vote_state,
3718 &mut tower_sync.lockouts,
3719 &mut tower_sync.root,
3720 tower_sync.hash,
3721 &slot_hashes,
3722 )
3723 .unwrap();
3724
3725 assert_eq!(
3727 tower_sync
3728 .clone()
3729 .lockouts
3730 .into_iter()
3731 .collect::<Vec<Lockout>>(),
3732 vec![
3733 Lockout::new_with_confirmation_count(4, 2),
3734 Lockout::new_with_confirmation_count(vote_slot, 1)
3735 ]
3736 );
3737
3738 assert_eq!(
3742 do_process_tower_sync(
3743 &mut vote_state,
3744 &slot_hashes,
3745 0,
3746 0,
3747 tower_sync,
3748 Some(&FeatureSet::all_enabled())
3749 ),
3750 Err(VoteError::LockoutConflict)
3751 );
3752 }
3753
3754 #[test]
3755 fn test_check_and_filter_proposed_vote_state_slot_hash_mismatch() {
3756 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3757 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3758
3759 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3764 let vote_slot_hash = Hash::new_unique();
3765 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3766 tower_sync.hash = vote_slot_hash;
3767 assert_eq!(
3768 check_and_filter_proposed_vote_state(
3769 &vote_state,
3770 &mut tower_sync.lockouts,
3771 &mut tower_sync.root,
3772 tower_sync.hash,
3773 &slot_hashes,
3774 ),
3775 Err(VoteError::SlotHashMismatch),
3776 );
3777 }
3778
3779 #[test_case(0, true; "first slot")]
3780 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3781 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3782 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3783 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3784 fn test_epoch_half_check(slot: Slot, expected_allowed: bool) {
3785 let epoch_schedule = EpochSchedule::without_warmup();
3786 assert_eq!(
3787 is_commission_update_allowed(slot, &epoch_schedule),
3788 expected_allowed
3789 );
3790 }
3791
3792 #[test]
3793 fn test_warmup_epoch_half_check_with_warmup() {
3794 let epoch_schedule = EpochSchedule::default();
3795 let first_normal_slot = epoch_schedule.first_normal_slot;
3796 assert!(is_commission_update_allowed(0, &epoch_schedule));
3798 assert!(is_commission_update_allowed(
3801 first_normal_slot - 1,
3802 &epoch_schedule
3803 ));
3804 }
3805
3806 #[test_case(0, true; "first slot")]
3807 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3808 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3809 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3810 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3811 fn test_epoch_half_check_with_warmup(slot: Slot, expected_allowed: bool) {
3812 let epoch_schedule = EpochSchedule::default();
3813 let first_normal_slot = epoch_schedule.first_normal_slot;
3814 assert_eq!(
3815 is_commission_update_allowed(first_normal_slot.saturating_add(slot), &epoch_schedule),
3816 expected_allowed
3817 );
3818 }
3819}