1pub use clone_solana_vote_interface::state::{vote_state_versions::*, *};
4use {
5 clone_agave_feature_set::{self as feature_set, FeatureSet},
6 clone_solana_account::{AccountSharedData, ReadableAccount, WritableAccount},
7 clone_solana_clock::{Clock, Epoch, Slot, UnixTimestamp},
8 clone_solana_epoch_schedule::EpochSchedule,
9 clone_solana_hash::Hash,
10 clone_solana_instruction::error::InstructionError,
11 clone_solana_pubkey::Pubkey,
12 clone_solana_rent::Rent,
13 clone_solana_slot_hashes::SlotHash,
14 clone_solana_transaction_context::{
15 BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
16 },
17 clone_solana_vote_interface::{error::VoteError, program::id},
18 log::*,
19 serde_derive::{Deserialize, Serialize},
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 clone_solana_account::{state_traits::StateMut, AccountSharedData},
1218 clone_solana_clock::DEFAULT_SLOTS_PER_EPOCH,
1219 clone_solana_sha256_hasher::hash,
1220 clone_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: clone_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 = clone_solana_pubkey::new_rand();
1243 (
1244 vote_pubkey,
1245 RefCell::new(vote_state::create_account(
1246 &vote_pubkey,
1247 &clone_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 = clone_solana_pubkey::new_rand();
1259 let withdrawer_pubkey = clone_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(
1273 &clone_solana_pubkey::new_rand(),
1274 0,
1275 1,
1276 |_pubkey| Ok(())
1277 ),
1278 Ok(())
1279 );
1280 vote_state.increment_credits(1, 200);
1281 assert_eq!(
1282 vote_state.set_new_authorized_voter(
1283 &clone_solana_pubkey::new_rand(),
1284 1,
1285 2,
1286 |_pubkey| Ok(())
1287 ),
1288 Ok(())
1289 );
1290 vote_state.increment_credits(2, 300);
1291 assert_eq!(
1292 vote_state.set_new_authorized_voter(
1293 &clone_solana_pubkey::new_rand(),
1294 2,
1295 3,
1296 |_pubkey| Ok(())
1297 ),
1298 Ok(())
1299 );
1300 vec![
1302 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
1303 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
1304 134, 135,
1305 ]
1306 .into_iter()
1307 .for_each(|v| vote_state.process_next_vote_slot(v, 4, 0));
1308
1309 let version1_14_11_serialized = bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(
1310 VoteState1_14_11::from(vote_state.clone()),
1311 )))
1312 .unwrap();
1313 let version1_14_11_serialized_len = version1_14_11_serialized.len();
1314 let rent = Rent::default();
1315 let lamports = rent.minimum_balance(version1_14_11_serialized_len);
1316 let mut vote_account =
1317 AccountSharedData::new(lamports, version1_14_11_serialized_len, &id());
1318 vote_account.set_data_from_slice(&version1_14_11_serialized);
1319
1320 let processor_account =
1323 AccountSharedData::new(0, 0, &clone_solana_sdk_ids::native_loader::id());
1324 let transaction_context = TransactionContext::new(
1325 vec![(id(), processor_account), (node_pubkey, vote_account)],
1326 rent.clone(),
1327 0,
1328 0,
1329 );
1330 let mut instruction_context = InstructionContext::default();
1331 instruction_context.configure(
1332 &[0],
1333 &[InstructionAccount {
1334 index_in_transaction: 1,
1335 index_in_caller: 1,
1336 index_in_callee: 0,
1337 is_signer: false,
1338 is_writable: true,
1339 }],
1340 &[],
1341 );
1342
1343 let mut borrowed_account = instruction_context
1346 .try_borrow_instruction_account(&transaction_context, 0)
1347 .unwrap();
1348
1349 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1351 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1352
1353 let converted_vote_state = vote_state_version.convert_to_current();
1355
1356 assert!(vote_state == converted_vote_state);
1358
1359 let vote_state = converted_vote_state;
1360
1361 assert_eq!(
1364 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1365 Ok(())
1366 );
1367 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1368 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1369
1370 let converted_vote_state = vote_state_version.convert_to_current();
1372
1373 assert_eq!(vote_state, converted_vote_state);
1375
1376 let vote_state = converted_vote_state;
1377
1378 assert_eq!(
1381 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1382 Ok(())
1383 );
1384 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1385 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1386
1387 let converted_vote_state = vote_state_version.convert_to_current();
1389
1390 assert_eq!(vote_state, converted_vote_state);
1392
1393 let vote_state = converted_vote_state;
1394
1395 assert_eq!(
1398 borrowed_account.set_lamports(rent.minimum_balance(VoteState::size_of()),),
1399 Ok(())
1400 );
1401 assert_eq!(
1402 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1403 Ok(())
1404 );
1405 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1406 assert_matches!(vote_state_version, VoteStateVersions::Current(_));
1407
1408 let converted_vote_state = vote_state_version.convert_to_current();
1410
1411 assert_eq!(vote_state, converted_vote_state);
1413 }
1414
1415 #[test]
1416 fn test_vote_lockout() {
1417 let (_vote_pubkey, vote_account) = create_test_account();
1418
1419 let mut vote_state: VoteState =
1420 StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
1421 .unwrap()
1422 .convert_to_current();
1423
1424 for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
1425 process_slot_vote_unchecked(&mut vote_state, (INITIAL_LOCKOUT * i) as u64);
1426 }
1427
1428 assert_eq!(vote_state.votes.len(), MAX_LOCKOUT_HISTORY);
1430 assert_eq!(vote_state.root_slot, Some(0));
1431 check_lockouts(&vote_state);
1432
1433 let top_vote = vote_state.votes.front().unwrap().slot();
1437 let slot = vote_state.last_lockout().unwrap().last_locked_out_slot();
1438 process_slot_vote_unchecked(&mut vote_state, slot);
1439 assert_eq!(Some(top_vote), vote_state.root_slot);
1440
1441 let slot = vote_state
1443 .votes
1444 .front()
1445 .unwrap()
1446 .lockout
1447 .last_locked_out_slot();
1448 process_slot_vote_unchecked(&mut vote_state, slot);
1449 assert_eq!(vote_state.votes.len(), 2);
1451 }
1452
1453 #[test]
1454 fn test_update_commission() {
1455 let node_pubkey = Pubkey::new_unique();
1456 let withdrawer_pubkey = Pubkey::new_unique();
1457 let clock = Clock::default();
1458 let vote_state = VoteState::new(
1459 &VoteInit {
1460 node_pubkey,
1461 authorized_voter: withdrawer_pubkey,
1462 authorized_withdrawer: withdrawer_pubkey,
1463 commission: 10,
1464 },
1465 &clock,
1466 );
1467
1468 let serialized =
1469 bincode::serialize(&VoteStateVersions::Current(Box::new(vote_state.clone()))).unwrap();
1470 let serialized_len = serialized.len();
1471 let rent = Rent::default();
1472 let lamports = rent.minimum_balance(serialized_len);
1473 let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id());
1474 vote_account.set_data_from_slice(&serialized);
1475
1476 let processor_account =
1479 AccountSharedData::new(0, 0, &clone_solana_sdk_ids::native_loader::id());
1480 let transaction_context = TransactionContext::new(
1481 vec![(id(), processor_account), (node_pubkey, vote_account)],
1482 rent,
1483 0,
1484 0,
1485 );
1486 let mut instruction_context = InstructionContext::default();
1487 instruction_context.configure(
1488 &[0],
1489 &[InstructionAccount {
1490 index_in_transaction: 1,
1491 index_in_caller: 1,
1492 index_in_callee: 0,
1493 is_signer: false,
1494 is_writable: true,
1495 }],
1496 &[],
1497 );
1498
1499 let mut borrowed_account = instruction_context
1502 .try_borrow_instruction_account(&transaction_context, 0)
1503 .unwrap();
1504
1505 let epoch_schedule = std::sync::Arc::new(EpochSchedule::without_warmup());
1506
1507 let first_half_clock = std::sync::Arc::new(Clock {
1508 slot: epoch_schedule.slots_per_epoch / 4,
1509 ..Clock::default()
1510 });
1511
1512 let second_half_clock = std::sync::Arc::new(Clock {
1513 slot: (epoch_schedule.slots_per_epoch * 3) / 4,
1514 ..Clock::default()
1515 });
1516
1517 let mut feature_set = FeatureSet::default();
1518 feature_set.activate(
1519 &feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id(),
1520 1,
1521 );
1522
1523 let signers: HashSet<Pubkey> = vec![withdrawer_pubkey].into_iter().collect();
1524
1525 assert_eq!(
1527 borrowed_account
1528 .get_state::<VoteStateVersions>()
1529 .unwrap()
1530 .convert_to_current()
1531 .commission,
1532 10
1533 );
1534 assert_matches!(
1535 update_commission(
1536 &mut borrowed_account,
1537 11,
1538 &signers,
1539 &epoch_schedule,
1540 &first_half_clock,
1541 &feature_set
1542 ),
1543 Ok(())
1544 );
1545 assert_eq!(
1546 borrowed_account
1547 .get_state::<VoteStateVersions>()
1548 .unwrap()
1549 .convert_to_current()
1550 .commission,
1551 11
1552 );
1553
1554 assert_matches!(
1556 update_commission(
1557 &mut borrowed_account,
1558 12,
1559 &signers,
1560 &epoch_schedule,
1561 &second_half_clock,
1562 &feature_set
1563 ),
1564 Err(_)
1565 );
1566 assert_eq!(
1567 borrowed_account
1568 .get_state::<VoteStateVersions>()
1569 .unwrap()
1570 .convert_to_current()
1571 .commission,
1572 11
1573 );
1574
1575 assert_matches!(
1577 update_commission(
1578 &mut borrowed_account,
1579 10,
1580 &signers,
1581 &epoch_schedule,
1582 &first_half_clock,
1583 &feature_set
1584 ),
1585 Ok(())
1586 );
1587 assert_eq!(
1588 borrowed_account
1589 .get_state::<VoteStateVersions>()
1590 .unwrap()
1591 .convert_to_current()
1592 .commission,
1593 10
1594 );
1595
1596 assert_matches!(
1598 update_commission(
1599 &mut borrowed_account,
1600 9,
1601 &signers,
1602 &epoch_schedule,
1603 &second_half_clock,
1604 &feature_set
1605 ),
1606 Err(_)
1607 );
1608 assert_eq!(
1609 borrowed_account
1610 .get_state::<VoteStateVersions>()
1611 .unwrap()
1612 .convert_to_current()
1613 .commission,
1614 10
1615 );
1616
1617 feature_set.activate(&feature_set::allow_commission_decrease_at_any_time::id(), 1);
1619 assert_matches!(
1620 update_commission(
1621 &mut borrowed_account,
1622 9,
1623 &signers,
1624 &epoch_schedule,
1625 &second_half_clock,
1626 &feature_set
1627 ),
1628 Ok(())
1629 );
1630 assert_eq!(
1631 borrowed_account
1632 .get_state::<VoteStateVersions>()
1633 .unwrap()
1634 .convert_to_current()
1635 .commission,
1636 9
1637 );
1638 }
1639
1640 #[test]
1641 fn test_vote_double_lockout_after_expiration() {
1642 let voter_pubkey = clone_solana_pubkey::new_rand();
1643 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1644
1645 for i in 0..3 {
1646 process_slot_vote_unchecked(&mut vote_state, i as u64);
1647 }
1648
1649 check_lockouts(&vote_state);
1650
1651 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 1) as u64);
1655 check_lockouts(&vote_state);
1656
1657 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 2) as u64);
1660 check_lockouts(&vote_state);
1661
1662 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 3) as u64);
1665 check_lockouts(&vote_state);
1666 }
1667
1668 #[test]
1669 fn test_expire_multiple_votes() {
1670 let voter_pubkey = clone_solana_pubkey::new_rand();
1671 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1672
1673 for i in 0..3 {
1674 process_slot_vote_unchecked(&mut vote_state, i as u64);
1675 }
1676
1677 assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1678
1679 let expire_slot = vote_state.votes[1].slot() + vote_state.votes[1].lockout.lockout() + 1;
1681 process_slot_vote_unchecked(&mut vote_state, expire_slot);
1682 assert_eq!(vote_state.votes.len(), 2);
1683
1684 assert_eq!(vote_state.votes[0].slot(), 0);
1686 assert_eq!(vote_state.votes[1].slot(), expire_slot);
1687
1688 process_slot_vote_unchecked(&mut vote_state, expire_slot + 1);
1690
1691 assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1693
1694 assert_eq!(vote_state.votes[1].confirmation_count(), 2);
1696 assert_eq!(vote_state.votes[2].confirmation_count(), 1);
1697 }
1698
1699 #[test]
1700 fn test_vote_credits() {
1701 let voter_pubkey = clone_solana_pubkey::new_rand();
1702 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1703
1704 for i in 0..MAX_LOCKOUT_HISTORY {
1705 process_slot_vote_unchecked(&mut vote_state, i as u64);
1706 }
1707
1708 assert_eq!(vote_state.credits(), 0);
1709
1710 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 1);
1711 assert_eq!(vote_state.credits(), 1);
1712 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 2);
1713 assert_eq!(vote_state.credits(), 2);
1714 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 3);
1715 assert_eq!(vote_state.credits(), 3);
1716 }
1717
1718 #[test]
1719 fn test_duplicate_vote() {
1720 let voter_pubkey = clone_solana_pubkey::new_rand();
1721 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1722 process_slot_vote_unchecked(&mut vote_state, 0);
1723 process_slot_vote_unchecked(&mut vote_state, 1);
1724 process_slot_vote_unchecked(&mut vote_state, 0);
1725 assert_eq!(vote_state.nth_recent_lockout(0).unwrap().slot(), 1);
1726 assert_eq!(vote_state.nth_recent_lockout(1).unwrap().slot(), 0);
1727 assert!(vote_state.nth_recent_lockout(2).is_none());
1728 }
1729
1730 #[test]
1731 fn test_nth_recent_lockout() {
1732 let voter_pubkey = clone_solana_pubkey::new_rand();
1733 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1734 for i in 0..MAX_LOCKOUT_HISTORY {
1735 process_slot_vote_unchecked(&mut vote_state, i as u64);
1736 }
1737 for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
1738 assert_eq!(
1739 vote_state.nth_recent_lockout(i).unwrap().slot() as usize,
1740 MAX_LOCKOUT_HISTORY - i - 1,
1741 );
1742 }
1743 assert!(vote_state.nth_recent_lockout(MAX_LOCKOUT_HISTORY).is_none());
1744 }
1745
1746 fn check_lockouts(vote_state: &VoteState) {
1747 for (i, vote) in vote_state.votes.iter().enumerate() {
1748 let num_votes = vote_state
1749 .votes
1750 .len()
1751 .checked_sub(i)
1752 .expect("`i` is less than `vote_state.votes.len()`");
1753 assert_eq!(
1754 vote.lockout.lockout(),
1755 INITIAL_LOCKOUT.pow(num_votes as u32) as u64
1756 );
1757 }
1758 }
1759
1760 fn recent_votes(vote_state: &VoteState) -> Vec<Vote> {
1761 let start = vote_state.votes.len().saturating_sub(MAX_RECENT_VOTES);
1762 (start..vote_state.votes.len())
1763 .map(|i| {
1764 Vote::new(
1765 vec![vote_state.votes.get(i).unwrap().slot()],
1766 Hash::default(),
1767 )
1768 })
1769 .collect()
1770 }
1771
1772 #[test]
1774 fn test_process_missed_votes() {
1775 let account_a = clone_solana_pubkey::new_rand();
1776 let mut vote_state_a = vote_state_new_for_test(&account_a);
1777 let account_b = clone_solana_pubkey::new_rand();
1778 let mut vote_state_b = vote_state_new_for_test(&account_b);
1779
1780 (0..5).for_each(|i| process_slot_vote_unchecked(&mut vote_state_a, i as u64));
1782 assert_ne!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1783
1784 let slots = (0u64..MAX_RECENT_VOTES as u64).collect();
1786 let vote = Vote::new(slots, Hash::default());
1787 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
1788
1789 assert_eq!(
1790 process_vote(&mut vote_state_a, &vote, &slot_hashes, 0, 0),
1791 Ok(())
1792 );
1793 assert_eq!(
1794 process_vote(&mut vote_state_b, &vote, &slot_hashes, 0, 0),
1795 Ok(())
1796 );
1797 assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1798 }
1799
1800 #[test]
1801 fn test_process_vote_skips_old_vote() {
1802 let mut vote_state = VoteState::default();
1803
1804 let vote = Vote::new(vec![0], Hash::default());
1805 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1806 assert_eq!(
1807 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1808 Ok(())
1809 );
1810 let recent = recent_votes(&vote_state);
1811 assert_eq!(
1812 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1813 Err(VoteError::VoteTooOld)
1814 );
1815 assert_eq!(recent, recent_votes(&vote_state));
1816 }
1817
1818 #[test]
1819 fn test_check_slots_are_valid_vote_empty_slot_hashes() {
1820 let vote_state = VoteState::default();
1821
1822 let vote = Vote::new(vec![0], Hash::default());
1823 assert_eq!(
1824 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &[]),
1825 Err(VoteError::VoteTooOld)
1826 );
1827 }
1828
1829 #[test]
1830 fn test_check_slots_are_valid_new_vote() {
1831 let vote_state = VoteState::default();
1832
1833 let vote = Vote::new(vec![0], Hash::default());
1834 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1835 assert_eq!(
1836 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1837 Ok(())
1838 );
1839 }
1840
1841 #[test]
1842 fn test_check_slots_are_valid_bad_hash() {
1843 let vote_state = VoteState::default();
1844
1845 let vote = Vote::new(vec![0], Hash::default());
1846 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
1847 assert_eq!(
1848 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1849 Err(VoteError::SlotHashMismatch)
1850 );
1851 }
1852
1853 #[test]
1854 fn test_check_slots_are_valid_bad_slot() {
1855 let vote_state = VoteState::default();
1856
1857 let vote = Vote::new(vec![1], Hash::default());
1858 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1859 assert_eq!(
1860 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1861 Err(VoteError::SlotsMismatch)
1862 );
1863 }
1864
1865 #[test]
1866 fn test_check_slots_are_valid_duplicate_vote() {
1867 let mut vote_state = VoteState::default();
1868
1869 let vote = Vote::new(vec![0], Hash::default());
1870 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1871 assert_eq!(
1872 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1873 Ok(())
1874 );
1875 assert_eq!(
1876 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1877 Err(VoteError::VoteTooOld)
1878 );
1879 }
1880
1881 #[test]
1882 fn test_check_slots_are_valid_next_vote() {
1883 let mut vote_state = VoteState::default();
1884
1885 let vote = Vote::new(vec![0], Hash::default());
1886 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1887 assert_eq!(
1888 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1889 Ok(())
1890 );
1891
1892 let vote = Vote::new(vec![0, 1], Hash::default());
1893 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1894 assert_eq!(
1895 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1896 Ok(())
1897 );
1898 }
1899
1900 #[test]
1901 fn test_check_slots_are_valid_next_vote_only() {
1902 let mut vote_state = VoteState::default();
1903
1904 let vote = Vote::new(vec![0], Hash::default());
1905 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1906 assert_eq!(
1907 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1908 Ok(())
1909 );
1910
1911 let vote = Vote::new(vec![1], Hash::default());
1912 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1913 assert_eq!(
1914 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1915 Ok(())
1916 );
1917 }
1918 #[test]
1919 fn test_process_vote_empty_slots() {
1920 let mut vote_state = VoteState::default();
1921
1922 let vote = Vote::new(vec![], Hash::default());
1923 assert_eq!(
1924 process_vote(&mut vote_state, &vote, &[], 0, 0),
1925 Err(VoteError::EmptySlots)
1926 );
1927 }
1928
1929 pub fn process_new_vote_state_from_lockouts(
1930 vote_state: &mut VoteState,
1931 new_state: VecDeque<Lockout>,
1932 new_root: Option<Slot>,
1933 timestamp: Option<i64>,
1934 epoch: Epoch,
1935 feature_set: Option<&FeatureSet>,
1936 ) -> Result<(), VoteError> {
1937 process_new_vote_state(
1938 vote_state,
1939 new_state.into_iter().map(LandedVote::from).collect(),
1940 new_root,
1941 timestamp,
1942 epoch,
1943 0,
1944 feature_set,
1945 )
1946 }
1947
1948 #[test]
1950 fn test_vote_state_update_increment_credits() {
1951 let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
1953
1954 let test_vote_groups: Vec<Vec<Slot>> = vec![
1957 vec![1, 2, 3, 4, 5, 6, 7, 8],
1959 vec![
1960 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1961 30, 31,
1962 ],
1963 vec![32],
1965 vec![33],
1967 vec![34, 35],
1969 vec![36, 37, 38],
1971 vec![
1973 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
1974 60, 61, 62, 63, 64, 65, 66, 67, 68,
1975 ],
1976 vec![
1978 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
1979 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1980 ],
1981 vec![100, 101, 106, 107, 112, 116, 120, 121, 122, 124],
1983 vec![200, 201],
1985 vec![
1986 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
1987 218, 219, 220, 221, 222, 223, 224, 225, 226,
1988 ],
1989 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
1990 ];
1991
1992 let feature_set = FeatureSet::default();
1993
1994 for vote_group in test_vote_groups {
1995 let mut vote_state_after_vote = vote_state.clone();
1997
1998 process_vote_unchecked(
1999 &mut vote_state_after_vote,
2000 Vote {
2001 slots: vote_group.clone(),
2002 hash: Hash::new_unique(),
2003 timestamp: None,
2004 },
2005 )
2006 .unwrap();
2007
2008 assert_eq!(
2010 process_new_vote_state(
2011 &mut vote_state,
2012 vote_state_after_vote.votes,
2013 vote_state_after_vote.root_slot,
2014 None,
2015 0,
2016 0,
2017 Some(&feature_set)
2018 ),
2019 Ok(())
2020 );
2021
2022 assert_eq!(
2024 vote_state.epoch_credits,
2025 vote_state_after_vote.epoch_credits
2026 );
2027 }
2028 }
2029
2030 #[test]
2032 fn test_timely_credits() {
2033 let test_vote_groups: Vec<(Vec<Slot>, Slot, u32)> = vec![
2037 (
2039 vec![1, 2, 3, 4, 5, 6, 7, 8],
2040 9,
2041 0,
2043 ),
2044 (
2045 vec![
2046 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
2047 29, 30, 31,
2048 ],
2049 34,
2050 0,
2053 ),
2054 (
2056 vec![32],
2057 35,
2058 10,
2061 ),
2062 (
2064 vec![33],
2065 36,
2066 10 + 11, ),
2070 (
2072 vec![34, 35],
2073 37,
2074 21 + 12 + 13, ),
2078 (
2080 vec![36, 37, 38],
2081 39,
2082 46 + 14 + 15 + 16, ),
2086 (
2087 vec![
2089 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
2090 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
2091 ],
2092 69,
2093 91 + 16
2103 + 9 + 2
2105 + 3
2106 + 4
2107 + 5
2108 + 6
2109 + 7
2110 + 8
2111 + 9
2112 + 10
2113 + 11
2114 + 12
2115 + 13
2116 + 14
2117 + 15
2118 + 15
2119 + 15
2120 + 15
2121 + 16
2122 + 15
2123 + 16, ),
2125 (
2127 vec![
2128 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
2129 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
2130 ],
2131 100,
2132 327 + 16
2139 + 14 + 2
2141 + 3
2142 + 4
2143 + 5
2144 + 6
2145 + 7
2146 + 8
2147 + 9
2148 + 10
2149 + 11
2150 + 12
2151 + 13
2152 + 14
2153 + 15
2154 + 16
2155 + 16, ),
2157 (
2159 vec![115, 116, 117, 118, 119, 120, 121, 122, 123, 124],
2160 130,
2161 508 + ((74 - 69) + 1), ),
2166 (
2168 vec![200, 201],
2169 202,
2170 514,
2173 ),
2174 (
2175 vec![
2176 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
2177 218, 219, 220, 221, 222, 223, 224, 225, 226,
2178 ],
2179 227,
2180 514 + 9 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13, ),
2186 (
2187 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
2188 237,
2189 613 + 3 + 4 + 5 + 6 + 16 + 16 + 1 + 1 + 1 + 1, ),
2195 ];
2196
2197 let feature_set = FeatureSet::default();
2198
2199 for i in 0..test_vote_groups.len() {
2202 let mut vote_state_1 = VoteState::new(&VoteInit::default(), &Clock::default());
2204 let mut vote_state_2 = VoteState::new(&VoteInit::default(), &Clock::default());
2206 test_vote_groups.iter().take(i + 1).for_each(|vote_group| {
2207 let vote = Vote {
2208 slots: vote_group.0.clone(), hash: Hash::new_unique(),
2210 timestamp: None,
2211 };
2212 let slot_hashes: Vec<_> =
2213 vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
2214 assert_eq!(
2215 process_vote(
2216 &mut vote_state_1,
2217 &vote,
2218 &slot_hashes,
2219 0,
2220 vote_group.1, ),
2222 Ok(())
2223 );
2224
2225 assert_eq!(
2226 process_new_vote_state(
2227 &mut vote_state_2,
2228 vote_state_1.votes.clone(),
2229 vote_state_1.root_slot,
2230 None,
2231 0,
2232 vote_group.1, Some(&feature_set)
2234 ),
2235 Ok(())
2236 );
2237 });
2238
2239 let vote_group = &test_vote_groups[i];
2241 assert_eq!(vote_state_1.credits(), vote_group.2 as u64); assert_eq!(vote_state_2.credits(), vote_group.2 as u64); }
2244 }
2245
2246 #[test]
2247 fn test_retroactive_voting_timely_credits() {
2248 #[allow(clippy::type_complexity)]
2254 let test_vote_state_updates: Vec<(Vec<(Slot, u32)>, Slot, Option<Slot>, u32)> = vec![
2255 (
2257 vec![(7, 4), (8, 3), (9, 2), (10, 1)],
2258 11,
2259 None,
2261 0,
2263 ),
2264 (
2266 vec![
2267 (1, 10),
2268 (2, 9),
2269 (3, 8),
2270 (4, 7),
2271 (5, 6),
2272 (6, 5),
2273 (7, 4),
2274 (8, 3),
2275 (9, 2),
2276 (10, 1),
2277 ],
2278 12,
2279 None,
2281 0,
2283 ),
2284 (
2286 vec![
2287 (11, 31),
2288 (12, 30),
2289 (13, 29),
2290 (14, 28),
2291 (15, 27),
2292 (16, 26),
2293 (17, 25),
2294 (18, 24),
2295 (19, 23),
2296 (20, 22),
2297 (21, 21),
2298 (22, 20),
2299 (23, 19),
2300 (24, 18),
2301 (25, 17),
2302 (26, 16),
2303 (27, 15),
2304 (28, 14),
2305 (29, 13),
2306 (30, 12),
2307 (31, 11),
2308 (32, 10),
2309 (33, 9),
2310 (34, 8),
2311 (35, 7),
2312 (36, 6),
2313 (37, 5),
2314 (38, 4),
2315 (39, 3),
2316 (40, 2),
2317 (41, 1),
2318 ],
2319 42,
2320 Some(10),
2322 7 + 8 + 9 + 10 + 11 + 12 + 14 + 15 + 16 + 16,
2325 ),
2326 ];
2327
2328 let feature_set = FeatureSet::default();
2329
2330 let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
2332
2333 test_vote_state_updates
2336 .iter()
2337 .for_each(|proposed_vote_state| {
2338 let new_state = proposed_vote_state
2339 .0 .iter()
2341 .map(|(slot, confirmation_count)| LandedVote {
2342 latency: 0,
2343 lockout: Lockout::new_with_confirmation_count(*slot, *confirmation_count),
2344 })
2345 .collect::<VecDeque<LandedVote>>();
2346 assert_eq!(
2347 process_new_vote_state(
2348 &mut vote_state,
2349 new_state,
2350 proposed_vote_state.2, None,
2352 0,
2353 proposed_vote_state.1, Some(&feature_set)
2355 ),
2356 Ok(())
2357 );
2358
2359 assert_eq!(vote_state.credits(), proposed_vote_state.3 as u64);
2361 });
2362 }
2363
2364 #[test]
2365 fn test_process_new_vote_too_many_votes() {
2366 let mut vote_state1 = VoteState::default();
2367 let bad_votes: VecDeque<Lockout> = (0..=MAX_LOCKOUT_HISTORY)
2368 .map(|slot| {
2369 Lockout::new_with_confirmation_count(
2370 slot as Slot,
2371 (MAX_LOCKOUT_HISTORY - slot + 1) as u32,
2372 )
2373 })
2374 .collect();
2375
2376 let current_epoch = vote_state1.current_epoch();
2377 assert_eq!(
2378 process_new_vote_state_from_lockouts(
2379 &mut vote_state1,
2380 bad_votes,
2381 None,
2382 None,
2383 current_epoch,
2384 None
2385 ),
2386 Err(VoteError::TooManyVotes)
2387 );
2388 }
2389
2390 #[test]
2391 fn test_process_new_vote_state_root_rollback() {
2392 let mut vote_state1 = VoteState::default();
2393 for i in 0..MAX_LOCKOUT_HISTORY + 2 {
2394 process_slot_vote_unchecked(&mut vote_state1, i as Slot);
2395 }
2396 assert_eq!(vote_state1.root_slot.unwrap(), 1);
2397
2398 let mut vote_state2 = vote_state1.clone();
2401 process_slot_vote_unchecked(&mut vote_state2, MAX_LOCKOUT_HISTORY as Slot + 3);
2402
2403 let lesser_root = Some(0);
2405
2406 let current_epoch = vote_state2.current_epoch();
2407 assert_eq!(
2408 process_new_vote_state(
2409 &mut vote_state1,
2410 vote_state2.votes.clone(),
2411 lesser_root,
2412 None,
2413 current_epoch,
2414 0,
2415 None,
2416 ),
2417 Err(VoteError::RootRollBack)
2418 );
2419
2420 let none_root = None;
2422 assert_eq!(
2423 process_new_vote_state(
2424 &mut vote_state1,
2425 vote_state2.votes.clone(),
2426 none_root,
2427 None,
2428 current_epoch,
2429 0,
2430 None,
2431 ),
2432 Err(VoteError::RootRollBack)
2433 );
2434 }
2435
2436 #[test]
2437 fn test_process_new_vote_state_zero_confirmations() {
2438 let mut vote_state1 = VoteState::default();
2439 let current_epoch = vote_state1.current_epoch();
2440
2441 let bad_votes: VecDeque<Lockout> = vec![
2442 Lockout::new_with_confirmation_count(0, 0),
2443 Lockout::new_with_confirmation_count(1, 1),
2444 ]
2445 .into_iter()
2446 .collect();
2447 assert_eq!(
2448 process_new_vote_state_from_lockouts(
2449 &mut vote_state1,
2450 bad_votes,
2451 None,
2452 None,
2453 current_epoch,
2454 None
2455 ),
2456 Err(VoteError::ZeroConfirmations)
2457 );
2458
2459 let bad_votes: VecDeque<Lockout> = vec![
2460 Lockout::new_with_confirmation_count(0, 2),
2461 Lockout::new_with_confirmation_count(1, 0),
2462 ]
2463 .into_iter()
2464 .collect();
2465 assert_eq!(
2466 process_new_vote_state_from_lockouts(
2467 &mut vote_state1,
2468 bad_votes,
2469 None,
2470 None,
2471 current_epoch,
2472 None
2473 ),
2474 Err(VoteError::ZeroConfirmations)
2475 );
2476 }
2477
2478 #[test]
2479 fn test_process_new_vote_state_confirmations_too_large() {
2480 let mut vote_state1 = VoteState::default();
2481 let current_epoch = vote_state1.current_epoch();
2482
2483 let good_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2484 0,
2485 MAX_LOCKOUT_HISTORY as u32,
2486 )]
2487 .into_iter()
2488 .collect();
2489
2490 process_new_vote_state_from_lockouts(
2491 &mut vote_state1,
2492 good_votes,
2493 None,
2494 None,
2495 current_epoch,
2496 None,
2497 )
2498 .unwrap();
2499
2500 let mut vote_state1 = VoteState::default();
2501 let bad_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2502 0,
2503 MAX_LOCKOUT_HISTORY as u32 + 1,
2504 )]
2505 .into_iter()
2506 .collect();
2507 assert_eq!(
2508 process_new_vote_state_from_lockouts(
2509 &mut vote_state1,
2510 bad_votes,
2511 None,
2512 None,
2513 current_epoch,
2514 None
2515 ),
2516 Err(VoteError::ConfirmationTooLarge)
2517 );
2518 }
2519
2520 #[test]
2521 fn test_process_new_vote_state_slot_smaller_than_root() {
2522 let mut vote_state1 = VoteState::default();
2523 let current_epoch = vote_state1.current_epoch();
2524 let root_slot = 5;
2525
2526 let bad_votes: VecDeque<Lockout> = vec![
2527 Lockout::new_with_confirmation_count(root_slot, 2),
2528 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2529 ]
2530 .into_iter()
2531 .collect();
2532 assert_eq!(
2533 process_new_vote_state_from_lockouts(
2534 &mut vote_state1,
2535 bad_votes,
2536 Some(root_slot),
2537 None,
2538 current_epoch,
2539 None,
2540 ),
2541 Err(VoteError::SlotSmallerThanRoot)
2542 );
2543
2544 let bad_votes: VecDeque<Lockout> = vec![
2545 Lockout::new_with_confirmation_count(root_slot - 1, 2),
2546 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2547 ]
2548 .into_iter()
2549 .collect();
2550 assert_eq!(
2551 process_new_vote_state_from_lockouts(
2552 &mut vote_state1,
2553 bad_votes,
2554 Some(root_slot),
2555 None,
2556 current_epoch,
2557 None,
2558 ),
2559 Err(VoteError::SlotSmallerThanRoot)
2560 );
2561 }
2562
2563 #[test]
2564 fn test_process_new_vote_state_slots_not_ordered() {
2565 let mut vote_state1 = VoteState::default();
2566 let current_epoch = vote_state1.current_epoch();
2567
2568 let bad_votes: VecDeque<Lockout> = vec![
2569 Lockout::new_with_confirmation_count(1, 2),
2570 Lockout::new_with_confirmation_count(0, 1),
2571 ]
2572 .into_iter()
2573 .collect();
2574 assert_eq!(
2575 process_new_vote_state_from_lockouts(
2576 &mut vote_state1,
2577 bad_votes,
2578 None,
2579 None,
2580 current_epoch,
2581 None
2582 ),
2583 Err(VoteError::SlotsNotOrdered)
2584 );
2585
2586 let bad_votes: VecDeque<Lockout> = vec![
2587 Lockout::new_with_confirmation_count(1, 2),
2588 Lockout::new_with_confirmation_count(1, 1),
2589 ]
2590 .into_iter()
2591 .collect();
2592 assert_eq!(
2593 process_new_vote_state_from_lockouts(
2594 &mut vote_state1,
2595 bad_votes,
2596 None,
2597 None,
2598 current_epoch,
2599 None
2600 ),
2601 Err(VoteError::SlotsNotOrdered)
2602 );
2603 }
2604
2605 #[test]
2606 fn test_process_new_vote_state_confirmations_not_ordered() {
2607 let mut vote_state1 = VoteState::default();
2608 let current_epoch = vote_state1.current_epoch();
2609
2610 let bad_votes: VecDeque<Lockout> = vec![
2611 Lockout::new_with_confirmation_count(0, 1),
2612 Lockout::new_with_confirmation_count(1, 2),
2613 ]
2614 .into_iter()
2615 .collect();
2616 assert_eq!(
2617 process_new_vote_state_from_lockouts(
2618 &mut vote_state1,
2619 bad_votes,
2620 None,
2621 None,
2622 current_epoch,
2623 None
2624 ),
2625 Err(VoteError::ConfirmationsNotOrdered)
2626 );
2627
2628 let bad_votes: VecDeque<Lockout> = vec![
2629 Lockout::new_with_confirmation_count(0, 1),
2630 Lockout::new_with_confirmation_count(1, 1),
2631 ]
2632 .into_iter()
2633 .collect();
2634 assert_eq!(
2635 process_new_vote_state_from_lockouts(
2636 &mut vote_state1,
2637 bad_votes,
2638 None,
2639 None,
2640 current_epoch,
2641 None
2642 ),
2643 Err(VoteError::ConfirmationsNotOrdered)
2644 );
2645 }
2646
2647 #[test]
2648 fn test_process_new_vote_state_new_vote_state_lockout_mismatch() {
2649 let mut vote_state1 = VoteState::default();
2650 let current_epoch = vote_state1.current_epoch();
2651
2652 let bad_votes: VecDeque<Lockout> = vec![
2653 Lockout::new_with_confirmation_count(0, 2),
2654 Lockout::new_with_confirmation_count(7, 1),
2655 ]
2656 .into_iter()
2657 .collect();
2658
2659 assert_eq!(
2661 process_new_vote_state_from_lockouts(
2662 &mut vote_state1,
2663 bad_votes,
2664 None,
2665 None,
2666 current_epoch,
2667 None
2668 ),
2669 Err(VoteError::NewVoteStateLockoutMismatch)
2670 );
2671 }
2672
2673 #[test]
2674 fn test_process_new_vote_state_confirmation_rollback() {
2675 let mut vote_state1 = VoteState::default();
2676 let current_epoch = vote_state1.current_epoch();
2677 let votes: VecDeque<Lockout> = vec![
2678 Lockout::new_with_confirmation_count(0, 4),
2679 Lockout::new_with_confirmation_count(1, 3),
2680 ]
2681 .into_iter()
2682 .collect();
2683 process_new_vote_state_from_lockouts(
2684 &mut vote_state1,
2685 votes,
2686 None,
2687 None,
2688 current_epoch,
2689 None,
2690 )
2691 .unwrap();
2692
2693 let votes: VecDeque<Lockout> = vec![
2694 Lockout::new_with_confirmation_count(0, 4),
2695 Lockout::new_with_confirmation_count(1, 2),
2697 Lockout::new_with_confirmation_count(2, 1),
2698 ]
2699 .into_iter()
2700 .collect();
2701 assert_eq!(
2704 process_new_vote_state_from_lockouts(
2705 &mut vote_state1,
2706 votes,
2707 None,
2708 None,
2709 current_epoch,
2710 None
2711 ),
2712 Err(VoteError::ConfirmationRollBack)
2713 );
2714 }
2715
2716 #[test]
2717 fn test_process_new_vote_state_root_progress() {
2718 let mut vote_state1 = VoteState::default();
2719 for i in 0..MAX_LOCKOUT_HISTORY {
2720 process_slot_vote_unchecked(&mut vote_state1, i as u64);
2721 }
2722
2723 assert!(vote_state1.root_slot.is_none());
2724 let mut vote_state2 = vote_state1.clone();
2725
2726 for new_vote in MAX_LOCKOUT_HISTORY + 1..=MAX_LOCKOUT_HISTORY + 2 {
2733 process_slot_vote_unchecked(&mut vote_state2, new_vote as Slot);
2734 assert_ne!(vote_state1.root_slot, vote_state2.root_slot);
2735
2736 process_new_vote_state(
2737 &mut vote_state1,
2738 vote_state2.votes.clone(),
2739 vote_state2.root_slot,
2740 None,
2741 vote_state2.current_epoch(),
2742 0,
2743 None,
2744 )
2745 .unwrap();
2746
2747 assert_eq!(vote_state1, vote_state2);
2748 }
2749 }
2750
2751 #[test]
2752 fn test_process_new_vote_state_same_slot_but_not_common_ancestor() {
2753 let mut vote_state1 = VoteState::default();
2772 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5]);
2773 assert_eq!(
2774 vote_state1
2775 .votes
2776 .iter()
2777 .map(|vote| vote.slot())
2778 .collect::<Vec<Slot>>(),
2779 vec![1, 5]
2780 );
2781
2782 let mut vote_state2 = VoteState::default();
2784 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2785 assert_eq!(
2786 vote_state2
2787 .votes
2788 .iter()
2789 .map(|vote| vote.slot())
2790 .collect::<Vec<Slot>>(),
2791 vec![1, 2, 3, 5, 7]
2792 );
2793
2794 process_new_vote_state(
2796 &mut vote_state1,
2797 vote_state2.votes.clone(),
2798 vote_state2.root_slot,
2799 None,
2800 vote_state2.current_epoch(),
2801 0,
2802 None,
2803 )
2804 .unwrap();
2805
2806 assert_eq!(vote_state1, vote_state2);
2807 }
2808
2809 #[test]
2810 fn test_process_new_vote_state_lockout_violation() {
2811 let mut vote_state1 = VoteState::default();
2813 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 4, 5]);
2814 assert_eq!(
2815 vote_state1
2816 .votes
2817 .iter()
2818 .map(|vote| vote.slot())
2819 .collect::<Vec<Slot>>(),
2820 vec![1, 2, 4, 5]
2821 );
2822
2823 let mut vote_state2 = VoteState::default();
2826 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2827 assert_eq!(
2828 vote_state2
2829 .votes
2830 .iter()
2831 .map(|vote| vote.slot())
2832 .collect::<Vec<Slot>>(),
2833 vec![1, 2, 3, 5, 7]
2834 );
2835
2836 assert_eq!(
2838 process_new_vote_state(
2839 &mut vote_state1,
2840 vote_state2.votes.clone(),
2841 vote_state2.root_slot,
2842 None,
2843 vote_state2.current_epoch(),
2844 0,
2845 None
2846 ),
2847 Err(VoteError::LockoutConflict)
2848 );
2849 }
2850
2851 #[test]
2852 fn test_process_new_vote_state_lockout_violation2() {
2853 let mut vote_state1 = VoteState::default();
2855 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5, 6, 7]);
2856 assert_eq!(
2857 vote_state1
2858 .votes
2859 .iter()
2860 .map(|vote| vote.slot())
2861 .collect::<Vec<Slot>>(),
2862 vec![1, 5, 6, 7]
2863 );
2864
2865 let mut vote_state2 = VoteState::default();
2868 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 6, 8]);
2869 assert_eq!(
2870 vote_state2
2871 .votes
2872 .iter()
2873 .map(|vote| vote.slot())
2874 .collect::<Vec<Slot>>(),
2875 vec![1, 2, 3, 5, 6, 8]
2876 );
2877
2878 assert_eq!(
2881 process_new_vote_state(
2882 &mut vote_state1,
2883 vote_state2.votes.clone(),
2884 vote_state2.root_slot,
2885 None,
2886 vote_state2.current_epoch(),
2887 0,
2888 None
2889 ),
2890 Err(VoteError::LockoutConflict)
2891 );
2892 }
2893
2894 #[test]
2895 fn test_process_new_vote_state_expired_ancestor_not_removed() {
2896 let mut vote_state1 = VoteState::default();
2898 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 3, 9]);
2899 assert_eq!(
2900 vote_state1
2901 .votes
2902 .iter()
2903 .map(|vote| vote.slot())
2904 .collect::<Vec<Slot>>(),
2905 vec![1, 9]
2906 );
2907
2908 let mut vote_state2 = vote_state1.clone();
2911 process_slot_vote_unchecked(&mut vote_state2, 10);
2912
2913 assert_eq!(vote_state2.votes[0].slot(), 1);
2916 assert_eq!(vote_state2.votes[0].lockout.last_locked_out_slot(), 9);
2917 assert_eq!(
2918 vote_state2
2919 .votes
2920 .iter()
2921 .map(|vote| vote.slot())
2922 .collect::<Vec<Slot>>(),
2923 vec![1, 9, 10]
2924 );
2925
2926 process_new_vote_state(
2928 &mut vote_state1,
2929 vote_state2.votes.clone(),
2930 vote_state2.root_slot,
2931 None,
2932 vote_state2.current_epoch(),
2933 0,
2934 None,
2935 )
2936 .unwrap();
2937 assert_eq!(vote_state1, vote_state2,);
2938 }
2939
2940 #[test]
2941 fn test_process_new_vote_current_state_contains_bigger_slots() {
2942 let mut vote_state1 = VoteState::default();
2943 process_slot_votes_unchecked(&mut vote_state1, &[6, 7, 8]);
2944 assert_eq!(
2945 vote_state1
2946 .votes
2947 .iter()
2948 .map(|vote| vote.slot())
2949 .collect::<Vec<Slot>>(),
2950 vec![6, 7, 8]
2951 );
2952
2953 let bad_votes: VecDeque<Lockout> = vec![
2955 Lockout::new_with_confirmation_count(2, 5),
2956 Lockout::new_with_confirmation_count(14, 1),
2958 ]
2959 .into_iter()
2960 .collect();
2961 let root = Some(1);
2962
2963 let current_epoch = vote_state1.current_epoch();
2964 assert_eq!(
2965 process_new_vote_state_from_lockouts(
2966 &mut vote_state1,
2967 bad_votes,
2968 root,
2969 None,
2970 current_epoch,
2971 None
2972 ),
2973 Err(VoteError::LockoutConflict)
2974 );
2975
2976 let good_votes: VecDeque<LandedVote> = vec![
2977 Lockout::new_with_confirmation_count(2, 5).into(),
2978 Lockout::new_with_confirmation_count(15, 1).into(),
2979 ]
2980 .into_iter()
2981 .collect();
2982
2983 let current_epoch = vote_state1.current_epoch();
2984 process_new_vote_state(
2985 &mut vote_state1,
2986 good_votes.clone(),
2987 root,
2988 None,
2989 current_epoch,
2990 0,
2991 None,
2992 )
2993 .unwrap();
2994 assert_eq!(vote_state1.votes, good_votes);
2995 }
2996
2997 #[test]
2998 fn test_filter_old_votes() {
2999 let mut vote_state = VoteState::default();
3000 let old_vote_slot = 1;
3001 let vote = Vote::new(vec![old_vote_slot], Hash::default());
3002
3003 let slot_hashes = vec![(3, Hash::new_unique()), (2, Hash::new_unique())];
3006 assert_eq!(
3007 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
3008 Err(VoteError::VotesTooOldAllFiltered)
3009 );
3010
3011 let vote_slot = 2;
3014 let vote_slot_hash = slot_hashes
3015 .iter()
3016 .find(|(slot, _hash)| *slot == vote_slot)
3017 .unwrap()
3018 .1;
3019
3020 let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
3021 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0).unwrap();
3022 assert_eq!(
3023 vote_state
3024 .votes
3025 .into_iter()
3026 .map(|vote| vote.lockout)
3027 .collect::<Vec<Lockout>>(),
3028 vec![Lockout::new_with_confirmation_count(vote_slot, 1)]
3029 );
3030 }
3031
3032 fn build_slot_hashes(slots: Vec<Slot>) -> Vec<(Slot, Hash)> {
3033 slots
3034 .iter()
3035 .rev()
3036 .map(|x| (*x, Hash::new_unique()))
3037 .collect()
3038 }
3039
3040 fn build_vote_state(vote_slots: Vec<Slot>, slot_hashes: &[(Slot, Hash)]) -> VoteState {
3041 let mut vote_state = VoteState::default();
3042
3043 if !vote_slots.is_empty() {
3044 let vote_hash = slot_hashes
3045 .iter()
3046 .find(|(slot, _hash)| slot == vote_slots.last().unwrap())
3047 .unwrap()
3048 .1;
3049 let vote = Vote::new(vote_slots, vote_hash);
3050 process_vote_unfiltered(&mut vote_state, &vote.slots, &vote, slot_hashes, 0, 0)
3051 .unwrap();
3052 }
3053
3054 vote_state
3055 }
3056
3057 #[test]
3058 fn test_check_and_filter_proposed_vote_state_empty() {
3059 let empty_slot_hashes = build_slot_hashes(vec![]);
3060 let empty_vote_state = build_vote_state(vec![], &empty_slot_hashes);
3061
3062 let mut tower_sync = TowerSync::from(vec![]);
3064 assert_eq!(
3065 check_and_filter_proposed_vote_state(
3066 &empty_vote_state,
3067 &mut tower_sync.lockouts,
3068 &mut tower_sync.root,
3069 tower_sync.hash,
3070 &empty_slot_hashes
3071 ),
3072 Err(VoteError::EmptySlots),
3073 );
3074
3075 let mut tower_sync = TowerSync::from(vec![(0, 1)]);
3077 assert_eq!(
3078 check_and_filter_proposed_vote_state(
3079 &empty_vote_state,
3080 &mut tower_sync.lockouts,
3081 &mut tower_sync.root,
3082 tower_sync.hash,
3083 &empty_slot_hashes
3084 ),
3085 Err(VoteError::SlotsMismatch),
3086 );
3087 }
3088
3089 #[test]
3090 fn test_check_and_filter_proposed_vote_state_too_old() {
3091 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3092 let latest_vote = 4;
3093 let vote_state = build_vote_state(vec![1, 2, 3, latest_vote], &slot_hashes);
3094
3095 let mut tower_sync = TowerSync::from(vec![(latest_vote, 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 let earliest_slot_in_history = latest_vote + 2;
3113 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history]);
3114 let mut tower_sync = TowerSync::from(vec![(earliest_slot_in_history - 1, 1)]);
3115 assert_eq!(
3116 check_and_filter_proposed_vote_state(
3117 &vote_state,
3118 &mut tower_sync.lockouts,
3119 &mut tower_sync.root,
3120 tower_sync.hash,
3121 &slot_hashes
3122 ),
3123 Err(VoteError::VoteTooOld),
3124 );
3125 }
3126
3127 fn run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3128 earliest_slot_in_history: Slot,
3129 current_vote_state_slots: Vec<Slot>,
3130 current_vote_state_root: Option<Slot>,
3131 proposed_slots_and_lockouts: Vec<(Slot, u32)>,
3132 proposed_root: Slot,
3133 expected_root: Option<Slot>,
3134 expected_vote_state: Vec<Lockout>,
3135 ) {
3136 assert!(proposed_root < earliest_slot_in_history);
3137 assert_eq!(
3138 expected_root,
3139 current_vote_state_slots
3140 .iter()
3141 .rev()
3142 .find(|slot| **slot <= proposed_root)
3143 .cloned()
3144 );
3145 let latest_slot_in_history = proposed_slots_and_lockouts
3146 .last()
3147 .unwrap()
3148 .0
3149 .max(earliest_slot_in_history);
3150 let mut slot_hashes = build_slot_hashes(
3151 (current_vote_state_slots.first().copied().unwrap_or(0)..=latest_slot_in_history)
3152 .collect::<Vec<Slot>>(),
3153 );
3154
3155 let mut vote_state = build_vote_state(current_vote_state_slots, &slot_hashes);
3156 vote_state.root_slot = current_vote_state_root;
3157
3158 slot_hashes.retain(|slot| slot.0 >= earliest_slot_in_history);
3159 assert!(!proposed_slots_and_lockouts.is_empty());
3160 let proposed_hash = slot_hashes
3161 .iter()
3162 .find(|(slot, _hash)| *slot == proposed_slots_and_lockouts.last().unwrap().0)
3163 .unwrap()
3164 .1;
3165
3166 let mut tower_sync = TowerSync::from(proposed_slots_and_lockouts);
3170 tower_sync.hash = proposed_hash;
3171 tower_sync.root = Some(proposed_root);
3172 check_and_filter_proposed_vote_state(
3173 &vote_state,
3174 &mut tower_sync.lockouts,
3175 &mut tower_sync.root,
3176 tower_sync.hash,
3177 &slot_hashes,
3178 )
3179 .unwrap();
3180 assert_eq!(tower_sync.root, expected_root);
3181
3182 assert!(do_process_tower_sync(
3185 &mut vote_state,
3186 &slot_hashes,
3187 0,
3188 0,
3189 tower_sync.clone(),
3190 Some(&FeatureSet::all_enabled()),
3191 )
3192 .is_ok());
3193 assert_eq!(vote_state.root_slot, expected_root);
3194 assert_eq!(
3195 vote_state
3196 .votes
3197 .into_iter()
3198 .map(|vote| vote.lockout)
3199 .collect::<Vec<Lockout>>(),
3200 expected_vote_state,
3201 );
3202 }
3203
3204 #[test]
3205 fn test_check_and_filter_proposed_vote_state_older_than_history_root() {
3206 let earliest_slot_in_history = 5;
3209 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3210 let current_vote_state_root = None;
3211 let proposed_slots_and_lockouts = vec![(5, 1)];
3212 let proposed_root = 4;
3213 let expected_root = Some(4);
3214 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3215 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3216 earliest_slot_in_history,
3217 current_vote_state_slots,
3218 current_vote_state_root,
3219 proposed_slots_and_lockouts,
3220 proposed_root,
3221 expected_root,
3222 expected_vote_state,
3223 );
3224
3225 let earliest_slot_in_history = 5;
3228 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3229 let current_vote_state_root = Some(0);
3230 let proposed_slots_and_lockouts = vec![(5, 1)];
3231 let proposed_root = 4;
3232 let expected_root = Some(4);
3233 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3234 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3235 earliest_slot_in_history,
3236 current_vote_state_slots,
3237 current_vote_state_root,
3238 proposed_slots_and_lockouts,
3239 proposed_root,
3240 expected_root,
3241 expected_vote_state,
3242 );
3243
3244 let earliest_slot_in_history = 5;
3247 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3248 let current_vote_state_root = Some(0);
3249 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3250 let proposed_root = 3;
3251 let expected_root = Some(3);
3252 let expected_vote_state = vec![
3253 Lockout::new_with_confirmation_count(4, 2),
3254 Lockout::new_with_confirmation_count(5, 1),
3255 ];
3256 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3257 earliest_slot_in_history,
3258 current_vote_state_slots,
3259 current_vote_state_root,
3260 proposed_slots_and_lockouts,
3261 proposed_root,
3262 expected_root,
3263 expected_vote_state,
3264 );
3265
3266 let earliest_slot_in_history = 5;
3268 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 4];
3269 let current_vote_state_root = Some(0);
3270 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3271 let proposed_root = 3;
3272 let expected_root = Some(2);
3273 let expected_vote_state = vec![
3274 Lockout::new_with_confirmation_count(4, 2),
3275 Lockout::new_with_confirmation_count(5, 1),
3276 ];
3277 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3278 earliest_slot_in_history,
3279 current_vote_state_slots,
3280 current_vote_state_root,
3281 proposed_slots_and_lockouts,
3282 proposed_root,
3283 expected_root,
3284 expected_vote_state,
3285 );
3286
3287 let earliest_slot_in_history = 4;
3290 let current_vote_state_slots: Vec<Slot> = vec![3, 4];
3291 let current_vote_state_root = None;
3292 let proposed_slots_and_lockouts = vec![(3, 3), (4, 2), (5, 1)];
3293 let proposed_root = 2;
3294 let expected_root = None;
3295 let expected_vote_state = vec![
3296 Lockout::new_with_confirmation_count(3, 3),
3297 Lockout::new_with_confirmation_count(4, 2),
3298 Lockout::new_with_confirmation_count(5, 1),
3299 ];
3300 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3301 earliest_slot_in_history,
3302 current_vote_state_slots,
3303 current_vote_state_root,
3304 proposed_slots_and_lockouts,
3305 proposed_root,
3306 expected_root,
3307 expected_vote_state,
3308 );
3309
3310 let earliest_slot_in_history = 4;
3312 let current_vote_state_slots: Vec<Slot> = vec![];
3313 let current_vote_state_root = None;
3314 let proposed_slots_and_lockouts = vec![(5, 1)];
3315 let proposed_root = 2;
3316 let expected_root = None;
3317 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3318 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3319 earliest_slot_in_history,
3320 current_vote_state_slots,
3321 current_vote_state_root,
3322 proposed_slots_and_lockouts,
3323 proposed_root,
3324 expected_root,
3325 expected_vote_state,
3326 );
3327 }
3328
3329 #[test]
3330 fn test_check_and_filter_proposed_vote_state_slots_not_ordered() {
3331 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3332 let vote_state = build_vote_state(vec![1], &slot_hashes);
3333
3334 let vote_slot = 3;
3336 let vote_slot_hash = slot_hashes
3337 .iter()
3338 .find(|(slot, _hash)| *slot == vote_slot)
3339 .unwrap()
3340 .1;
3341 let mut tower_sync = TowerSync::from(vec![(2, 2), (1, 3), (vote_slot, 1)]);
3342 tower_sync.hash = vote_slot_hash;
3343 assert_eq!(
3344 check_and_filter_proposed_vote_state(
3345 &vote_state,
3346 &mut tower_sync.lockouts,
3347 &mut tower_sync.root,
3348 tower_sync.hash,
3349 &slot_hashes
3350 ),
3351 Err(VoteError::SlotsNotOrdered),
3352 );
3353
3354 let mut tower_sync = TowerSync::from(vec![(2, 2), (2, 2), (vote_slot, 1)]);
3356 tower_sync.hash = vote_slot_hash;
3357 assert_eq!(
3358 check_and_filter_proposed_vote_state(
3359 &vote_state,
3360 &mut tower_sync.lockouts,
3361 &mut tower_sync.root,
3362 tower_sync.hash,
3363 &slot_hashes
3364 ),
3365 Err(VoteError::SlotsNotOrdered),
3366 );
3367 }
3368
3369 #[test]
3370 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered() {
3371 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3372 let mut vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes);
3373
3374 let earliest_slot_in_history = 11;
3379 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3380 let vote_slot = 12;
3381 let vote_slot_hash = slot_hashes
3382 .iter()
3383 .find(|(slot, _hash)| *slot == vote_slot)
3384 .unwrap()
3385 .1;
3386 let missing_older_than_history_slot = earliest_slot_in_history - 1;
3387 let mut tower_sync = TowerSync::from(vec![
3388 (1, 4),
3389 (missing_older_than_history_slot, 2),
3390 (vote_slot, 3),
3391 ]);
3392 tower_sync.hash = vote_slot_hash;
3393 check_and_filter_proposed_vote_state(
3394 &vote_state,
3395 &mut tower_sync.lockouts,
3396 &mut tower_sync.root,
3397 tower_sync.hash,
3398 &slot_hashes,
3399 )
3400 .unwrap();
3401
3402 assert_eq!(
3404 tower_sync
3405 .clone()
3406 .lockouts
3407 .into_iter()
3408 .collect::<Vec<Lockout>>(),
3409 vec![
3410 Lockout::new_with_confirmation_count(1, 4),
3411 Lockout::new_with_confirmation_count(vote_slot, 3)
3412 ]
3413 );
3414 assert!(do_process_tower_sync(
3415 &mut vote_state,
3416 &slot_hashes,
3417 0,
3418 0,
3419 tower_sync,
3420 Some(&FeatureSet::all_enabled()),
3421 )
3422 .is_ok());
3423 }
3424
3425 #[test]
3426 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_not_filtered() {
3427 let slot_hashes = build_slot_hashes(vec![4]);
3428 let mut vote_state = build_vote_state(vec![4], &slot_hashes);
3429
3430 let earliest_slot_in_history = 11;
3435 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3436 let vote_slot = 12;
3437 let vote_slot_hash = slot_hashes
3438 .iter()
3439 .find(|(slot, _hash)| *slot == vote_slot)
3440 .unwrap()
3441 .1;
3442 let existing_older_than_history_slot = 4;
3443 let mut tower_sync =
3444 TowerSync::from(vec![(existing_older_than_history_slot, 3), (vote_slot, 2)]);
3445 tower_sync.hash = vote_slot_hash;
3446 check_and_filter_proposed_vote_state(
3447 &vote_state,
3448 &mut tower_sync.lockouts,
3449 &mut tower_sync.root,
3450 tower_sync.hash,
3451 &slot_hashes,
3452 )
3453 .unwrap();
3454 assert_eq!(tower_sync.lockouts.len(), 2);
3456 assert_eq!(
3457 tower_sync
3458 .clone()
3459 .lockouts
3460 .into_iter()
3461 .collect::<Vec<Lockout>>(),
3462 vec![
3463 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3464 Lockout::new_with_confirmation_count(vote_slot, 2)
3465 ]
3466 );
3467 assert!(do_process_tower_sync(
3468 &mut vote_state,
3469 &slot_hashes,
3470 0,
3471 0,
3472 tower_sync,
3473 Some(&FeatureSet::all_enabled()),
3474 )
3475 .is_ok());
3476 }
3477
3478 #[test]
3479 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered_and_not_filtered(
3480 ) {
3481 let slot_hashes = build_slot_hashes(vec![6]);
3482 let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3483
3484 let earliest_slot_in_history = 11;
3495 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3496 let vote_slot = 14;
3497 let vote_slot_hash = slot_hashes
3498 .iter()
3499 .find(|(slot, _hash)| *slot == vote_slot)
3500 .unwrap()
3501 .1;
3502
3503 let missing_older_than_history_slot = 4;
3504 let existing_older_than_history_slot = 6;
3505
3506 let mut tower_sync = TowerSync::from(vec![
3507 (missing_older_than_history_slot, 4),
3508 (existing_older_than_history_slot, 3),
3509 (12, 2),
3510 (vote_slot, 1),
3511 ]);
3512 tower_sync.hash = vote_slot_hash;
3513 check_and_filter_proposed_vote_state(
3514 &vote_state,
3515 &mut tower_sync.lockouts,
3516 &mut tower_sync.root,
3517 tower_sync.hash,
3518 &slot_hashes,
3519 )
3520 .unwrap();
3521 assert_eq!(tower_sync.lockouts.len(), 3);
3522 assert_eq!(
3523 tower_sync
3524 .clone()
3525 .lockouts
3526 .into_iter()
3527 .collect::<Vec<Lockout>>(),
3528 vec![
3529 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3530 Lockout::new_with_confirmation_count(12, 2),
3531 Lockout::new_with_confirmation_count(vote_slot, 1)
3532 ]
3533 );
3534 assert!(do_process_tower_sync(
3535 &mut vote_state,
3536 &slot_hashes,
3537 0,
3538 0,
3539 tower_sync,
3540 Some(&FeatureSet::all_enabled()),
3541 )
3542 .is_ok());
3543 }
3544
3545 #[test]
3546 fn test_check_and_filter_proposed_vote_state_slot_not_on_fork() {
3547 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3548 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3549
3550 let missing_vote_slot = 3;
3556
3557 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3560 let vote_slot_hash = slot_hashes
3561 .iter()
3562 .find(|(slot, _hash)| *slot == vote_slot)
3563 .unwrap()
3564 .1;
3565 let mut tower_sync = TowerSync::from(vec![(missing_vote_slot, 2), (vote_slot, 3)]);
3566 tower_sync.hash = vote_slot_hash;
3567 assert_eq!(
3568 check_and_filter_proposed_vote_state(
3569 &vote_state,
3570 &mut tower_sync.lockouts,
3571 &mut tower_sync.root,
3572 tower_sync.hash,
3573 &slot_hashes
3574 ),
3575 Err(VoteError::SlotsMismatch),
3576 );
3577
3578 let missing_vote_slot = 7;
3580 let mut tower_sync = TowerSync::from(vec![
3581 (2, 5),
3582 (4, 4),
3583 (6, 3),
3584 (missing_vote_slot, 2),
3585 (vote_slot, 1),
3586 ]);
3587 tower_sync.hash = vote_slot_hash;
3588 assert_eq!(
3589 check_and_filter_proposed_vote_state(
3590 &vote_state,
3591 &mut tower_sync.lockouts,
3592 &mut tower_sync.root,
3593 tower_sync.hash,
3594 &slot_hashes
3595 ),
3596 Err(VoteError::SlotsMismatch),
3597 );
3598 }
3599
3600 #[test]
3601 fn test_check_and_filter_proposed_vote_state_root_on_different_fork() {
3602 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3603 let vote_state = build_vote_state(vec![6], &slot_hashes);
3604
3605 let new_root = 3;
3611
3612 let vote_slot = 8;
3615 assert_eq!(vote_slot, slot_hashes.first().unwrap().0);
3616 let vote_slot_hash = slot_hashes
3617 .iter()
3618 .find(|(slot, _hash)| *slot == vote_slot)
3619 .unwrap()
3620 .1;
3621 let mut tower_sync = TowerSync::from(vec![(vote_slot, 1)]);
3622 tower_sync.hash = vote_slot_hash;
3623 tower_sync.root = Some(new_root);
3624 assert_eq!(
3625 check_and_filter_proposed_vote_state(
3626 &vote_state,
3627 &mut tower_sync.lockouts,
3628 &mut tower_sync.root,
3629 tower_sync.hash,
3630 &slot_hashes
3631 ),
3632 Err(VoteError::RootOnDifferentFork),
3633 );
3634 }
3635
3636 #[test]
3637 fn test_check_and_filter_proposed_vote_state_slot_newer_than_slot_history() {
3638 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3639 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3640
3641 let missing_vote_slot = slot_hashes.first().unwrap().0 + 1;
3647 let vote_slot_hash = Hash::new_unique();
3648 let mut tower_sync = TowerSync::from(vec![(8, 2), (missing_vote_slot, 3)]);
3649 tower_sync.hash = vote_slot_hash;
3650 assert_eq!(
3651 check_and_filter_proposed_vote_state(
3652 &vote_state,
3653 &mut tower_sync.lockouts,
3654 &mut tower_sync.root,
3655 tower_sync.hash,
3656 &slot_hashes
3657 ),
3658 Err(VoteError::SlotsMismatch),
3659 );
3660 }
3661
3662 #[test]
3663 fn test_check_and_filter_proposed_vote_state_slot_all_slot_hashes_in_update_ok() {
3664 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3665 let mut vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3666
3667 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3673 let vote_slot_hash = slot_hashes
3674 .iter()
3675 .find(|(slot, _hash)| *slot == vote_slot)
3676 .unwrap()
3677 .1;
3678 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3679 tower_sync.hash = vote_slot_hash;
3680 check_and_filter_proposed_vote_state(
3681 &vote_state,
3682 &mut tower_sync.lockouts,
3683 &mut tower_sync.root,
3684 tower_sync.hash,
3685 &slot_hashes,
3686 )
3687 .unwrap();
3688
3689 assert_eq!(
3691 tower_sync
3692 .clone()
3693 .lockouts
3694 .into_iter()
3695 .collect::<Vec<Lockout>>(),
3696 vec![
3697 Lockout::new_with_confirmation_count(2, 4),
3698 Lockout::new_with_confirmation_count(4, 3),
3699 Lockout::new_with_confirmation_count(6, 2),
3700 Lockout::new_with_confirmation_count(vote_slot, 1)
3701 ]
3702 );
3703
3704 assert!(do_process_tower_sync(
3705 &mut vote_state,
3706 &slot_hashes,
3707 0,
3708 0,
3709 tower_sync,
3710 Some(&FeatureSet::all_enabled()),
3711 )
3712 .is_ok());
3713 }
3714
3715 #[test]
3716 fn test_check_and_filter_proposed_vote_state_slot_some_slot_hashes_in_update_ok() {
3717 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3718 let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3719
3720 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3726 let vote_slot_hash = slot_hashes
3727 .iter()
3728 .find(|(slot, _hash)| *slot == vote_slot)
3729 .unwrap()
3730 .1;
3731 let mut tower_sync = TowerSync::from(vec![(4, 2), (vote_slot, 1)]);
3732 tower_sync.hash = vote_slot_hash;
3733 check_and_filter_proposed_vote_state(
3734 &vote_state,
3735 &mut tower_sync.lockouts,
3736 &mut tower_sync.root,
3737 tower_sync.hash,
3738 &slot_hashes,
3739 )
3740 .unwrap();
3741
3742 assert_eq!(
3744 tower_sync
3745 .clone()
3746 .lockouts
3747 .into_iter()
3748 .collect::<Vec<Lockout>>(),
3749 vec![
3750 Lockout::new_with_confirmation_count(4, 2),
3751 Lockout::new_with_confirmation_count(vote_slot, 1)
3752 ]
3753 );
3754
3755 assert_eq!(
3759 do_process_tower_sync(
3760 &mut vote_state,
3761 &slot_hashes,
3762 0,
3763 0,
3764 tower_sync,
3765 Some(&FeatureSet::all_enabled())
3766 ),
3767 Err(VoteError::LockoutConflict)
3768 );
3769 }
3770
3771 #[test]
3772 fn test_check_and_filter_proposed_vote_state_slot_hash_mismatch() {
3773 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3774 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3775
3776 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3781 let vote_slot_hash = Hash::new_unique();
3782 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3783 tower_sync.hash = vote_slot_hash;
3784 assert_eq!(
3785 check_and_filter_proposed_vote_state(
3786 &vote_state,
3787 &mut tower_sync.lockouts,
3788 &mut tower_sync.root,
3789 tower_sync.hash,
3790 &slot_hashes,
3791 ),
3792 Err(VoteError::SlotHashMismatch),
3793 );
3794 }
3795
3796 #[test_case(0, true; "first slot")]
3797 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3798 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3799 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3800 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3801 fn test_epoch_half_check(slot: Slot, expected_allowed: bool) {
3802 let epoch_schedule = EpochSchedule::without_warmup();
3803 assert_eq!(
3804 is_commission_update_allowed(slot, &epoch_schedule),
3805 expected_allowed
3806 );
3807 }
3808
3809 #[test]
3810 fn test_warmup_epoch_half_check_with_warmup() {
3811 let epoch_schedule = EpochSchedule::default();
3812 let first_normal_slot = epoch_schedule.first_normal_slot;
3813 assert!(is_commission_update_allowed(0, &epoch_schedule));
3815 assert!(is_commission_update_allowed(
3818 first_normal_slot - 1,
3819 &epoch_schedule
3820 ));
3821 }
3822
3823 #[test_case(0, true; "first slot")]
3824 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3825 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3826 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3827 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3828 fn test_epoch_half_check_with_warmup(slot: Slot, expected_allowed: bool) {
3829 let epoch_schedule = EpochSchedule::default();
3830 let first_normal_slot = epoch_schedule.first_normal_slot;
3831 assert_eq!(
3832 is_commission_update_allowed(first_normal_slot.saturating_add(slot), &epoch_schedule),
3833 expected_allowed
3834 );
3835 }
3836}