1pub use solana_program::vote::state::{vote_state_versions::*, *};
4use {
5 log::*,
6 serde_derive::{Deserialize, Serialize},
7 solana_feature_set::{self as feature_set, FeatureSet},
8 solana_program::vote::{error::VoteError, program::id},
9 solana_sdk::{
10 account::{AccountSharedData, ReadableAccount, WritableAccount},
11 clock::{Epoch, Slot, UnixTimestamp},
12 epoch_schedule::EpochSchedule,
13 hash::Hash,
14 instruction::InstructionError,
15 pubkey::Pubkey,
16 rent::Rent,
17 slot_hashes::SlotHash,
18 sysvar::clock::Clock,
19 transaction_context::{
20 BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
21 },
22 },
23 std::{
24 cmp::Ordering,
25 collections::{HashSet, VecDeque},
26 fmt::Debug,
27 },
28};
29
30#[cfg_attr(
31 feature = "frozen-abi",
32 derive(AbiExample, AbiEnumVisitor),
33 frozen_abi(digest = "3dbyMxwfCN43orGKa5YiyY1EqN2K97pTicNhKYTZSUQH")
34)]
35#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
36pub enum VoteTransaction {
37 Vote(Vote),
38 VoteStateUpdate(VoteStateUpdate),
39 #[serde(with = "serde_compact_vote_state_update")]
40 CompactVoteStateUpdate(VoteStateUpdate),
41 #[serde(with = "serde_tower_sync")]
42 TowerSync(TowerSync),
43}
44
45impl VoteTransaction {
46 pub fn slots(&self) -> Vec<Slot> {
47 match self {
48 VoteTransaction::Vote(vote) => vote.slots.clone(),
49 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.slots(),
50 VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.slots(),
51 VoteTransaction::TowerSync(tower_sync) => tower_sync.slots(),
52 }
53 }
54
55 pub fn slot(&self, i: usize) -> Slot {
56 match self {
57 VoteTransaction::Vote(vote) => vote.slots[i],
58 VoteTransaction::VoteStateUpdate(vote_state_update)
59 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
60 vote_state_update.lockouts[i].slot()
61 }
62 VoteTransaction::TowerSync(tower_sync) => tower_sync.lockouts[i].slot(),
63 }
64 }
65
66 pub fn len(&self) -> usize {
67 match self {
68 VoteTransaction::Vote(vote) => vote.slots.len(),
69 VoteTransaction::VoteStateUpdate(vote_state_update)
70 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
71 vote_state_update.lockouts.len()
72 }
73 VoteTransaction::TowerSync(tower_sync) => tower_sync.lockouts.len(),
74 }
75 }
76
77 pub fn is_empty(&self) -> bool {
78 match self {
79 VoteTransaction::Vote(vote) => vote.slots.is_empty(),
80 VoteTransaction::VoteStateUpdate(vote_state_update)
81 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
82 vote_state_update.lockouts.is_empty()
83 }
84 VoteTransaction::TowerSync(tower_sync) => tower_sync.lockouts.is_empty(),
85 }
86 }
87
88 pub fn hash(&self) -> Hash {
89 match self {
90 VoteTransaction::Vote(vote) => vote.hash,
91 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.hash,
92 VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.hash,
93 VoteTransaction::TowerSync(tower_sync) => tower_sync.hash,
94 }
95 }
96
97 pub fn timestamp(&self) -> Option<UnixTimestamp> {
98 match self {
99 VoteTransaction::Vote(vote) => vote.timestamp,
100 VoteTransaction::VoteStateUpdate(vote_state_update)
101 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
102 vote_state_update.timestamp
103 }
104 VoteTransaction::TowerSync(tower_sync) => tower_sync.timestamp,
105 }
106 }
107
108 pub fn set_timestamp(&mut self, ts: Option<UnixTimestamp>) {
109 match self {
110 VoteTransaction::Vote(vote) => vote.timestamp = ts,
111 VoteTransaction::VoteStateUpdate(vote_state_update)
112 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
113 vote_state_update.timestamp = ts
114 }
115 VoteTransaction::TowerSync(tower_sync) => tower_sync.timestamp = ts,
116 }
117 }
118
119 pub fn last_voted_slot(&self) -> Option<Slot> {
120 match self {
121 VoteTransaction::Vote(vote) => vote.last_voted_slot(),
122 VoteTransaction::VoteStateUpdate(vote_state_update)
123 | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
124 vote_state_update.last_voted_slot()
125 }
126 VoteTransaction::TowerSync(tower_sync) => tower_sync.last_voted_slot(),
127 }
128 }
129
130 pub fn last_voted_slot_hash(&self) -> Option<(Slot, Hash)> {
131 Some((self.last_voted_slot()?, self.hash()))
132 }
133}
134
135impl From<Vote> for VoteTransaction {
136 fn from(vote: Vote) -> Self {
137 VoteTransaction::Vote(vote)
138 }
139}
140
141impl From<VoteStateUpdate> for VoteTransaction {
142 fn from(vote_state_update: VoteStateUpdate) -> Self {
143 VoteTransaction::VoteStateUpdate(vote_state_update)
144 }
145}
146
147impl From<TowerSync> for VoteTransaction {
148 fn from(tower_sync: TowerSync) -> Self {
149 VoteTransaction::TowerSync(tower_sync)
150 }
151}
152
153pub fn from<T: ReadableAccount>(account: &T) -> Option<VoteState> {
155 VoteState::deserialize(account.data()).ok()
156}
157
158pub fn to<T: WritableAccount>(versioned: &VoteStateVersions, account: &mut T) -> Option<()> {
160 VoteState::serialize(versioned, account.data_as_mut_slice()).ok()
161}
162
163fn set_vote_account_state(
166 vote_account: &mut BorrowedAccount,
167 vote_state: VoteState,
168) -> Result<(), InstructionError> {
169 if (vote_account.get_data().len() < VoteStateVersions::vote_state_size_of(true))
172 && (!vote_account
173 .is_rent_exempt_at_data_length(VoteStateVersions::vote_state_size_of(true))
174 || vote_account
175 .set_data_length(VoteStateVersions::vote_state_size_of(true))
176 .is_err())
177 {
178 return vote_account.set_state(&VoteStateVersions::V1_14_11(Box::new(
181 VoteState1_14_11::from(vote_state),
182 )));
183 }
184 vote_account.set_state(&VoteStateVersions::new_current(vote_state))
186}
187
188fn check_and_filter_proposed_vote_state(
192 vote_state: &VoteState,
193 proposed_lockouts: &mut VecDeque<Lockout>,
194 proposed_root: &mut Option<Slot>,
195 proposed_hash: Hash,
196 slot_hashes: &[(Slot, Hash)],
197) -> Result<(), VoteError> {
198 if proposed_lockouts.is_empty() {
199 return Err(VoteError::EmptySlots);
200 }
201
202 let last_proposed_slot = proposed_lockouts
203 .back()
204 .expect("must be nonempty, checked above")
205 .slot();
206
207 if let Some(last_vote_slot) = vote_state.votes.back().map(|lockout| lockout.slot()) {
209 if last_proposed_slot <= last_vote_slot {
210 return Err(VoteError::VoteTooOld);
211 }
212 }
213
214 if slot_hashes.is_empty() {
215 return Err(VoteError::SlotsMismatch);
216 }
217 let earliest_slot_hash_in_history = slot_hashes.last().unwrap().0;
218
219 if last_proposed_slot < earliest_slot_hash_in_history {
221 return Err(VoteError::VoteTooOld);
224 }
225
226 if let Some(root) = *proposed_root {
228 if root < earliest_slot_hash_in_history {
233 *proposed_root = vote_state.root_slot;
235
236 for vote in vote_state.votes.iter().rev() {
238 if vote.slot() <= root {
239 *proposed_root = Some(vote.slot());
240 break;
241 }
242 }
243 }
244 }
245
246 let mut root_to_check = *proposed_root;
250 let mut proposed_lockouts_index = 0;
251
252 let mut slot_hashes_index = slot_hashes.len();
255
256 let mut proposed_lockouts_indices_to_filter = vec![];
257
258 while proposed_lockouts_index < proposed_lockouts.len() && slot_hashes_index > 0 {
270 let proposed_vote_slot = if let Some(root) = root_to_check {
271 root
272 } else {
273 proposed_lockouts[proposed_lockouts_index].slot()
274 };
275 if root_to_check.is_none()
276 && proposed_lockouts_index > 0
277 && proposed_vote_slot
278 <= proposed_lockouts[proposed_lockouts_index.checked_sub(1).expect(
279 "`proposed_lockouts_index` is positive when checking `SlotsNotOrdered`",
280 )]
281 .slot()
282 {
283 return Err(VoteError::SlotsNotOrdered);
284 }
285 let ancestor_slot = slot_hashes[slot_hashes_index
286 .checked_sub(1)
287 .expect("`slot_hashes_index` is positive when computing `ancestor_slot`")]
288 .0;
289
290 match proposed_vote_slot.cmp(&ancestor_slot) {
293 Ordering::Less => {
294 if slot_hashes_index == slot_hashes.len() {
295 if proposed_vote_slot >= earliest_slot_hash_in_history {
298 return Err(VoteError::AssertionFailed);
299 }
300 if !vote_state.contains_slot(proposed_vote_slot) && root_to_check.is_none() {
301 proposed_lockouts_indices_to_filter.push(proposed_lockouts_index);
307 }
308 if let Some(new_proposed_root) = root_to_check {
309 assert_eq!(new_proposed_root, proposed_vote_slot);
313 if new_proposed_root >= earliest_slot_hash_in_history {
317 return Err(VoteError::AssertionFailed);
318 }
319 root_to_check = None;
320 } else {
321 proposed_lockouts_index = proposed_lockouts_index.checked_add(1).expect(
322 "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when `proposed_vote_slot` is too old to be in SlotHashes history",
323 );
324 }
325 continue;
326 } else {
327 if root_to_check.is_some() {
331 return Err(VoteError::RootOnDifferentFork);
332 } else {
333 return Err(VoteError::SlotsMismatch);
334 }
335 }
336 }
337 Ordering::Greater => {
338 slot_hashes_index = slot_hashes_index
340 .checked_sub(1)
341 .expect("`slot_hashes_index` is positive when finding newer slots in SlotHashes history");
342 continue;
343 }
344 Ordering::Equal => {
345 if root_to_check.is_some() {
349 root_to_check = None;
350 } else {
351 proposed_lockouts_index = proposed_lockouts_index
352 .checked_add(1)
353 .expect("`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when match is found in SlotHashes history");
354 slot_hashes_index = slot_hashes_index.checked_sub(1).expect(
355 "`slot_hashes_index` is positive when match is found in SlotHashes history",
356 );
357 }
358 }
359 }
360 }
361
362 if proposed_lockouts_index != proposed_lockouts.len() {
363 return Err(VoteError::SlotsMismatch);
365 }
366
367 assert_eq!(last_proposed_slot, slot_hashes[slot_hashes_index].0);
390
391 if slot_hashes[slot_hashes_index].1 != proposed_hash {
392 warn!(
396 "{} dropped vote {:?} root {:?} failed to match hash {} {}",
397 vote_state.node_pubkey,
398 proposed_lockouts,
399 proposed_root,
400 proposed_hash,
401 slot_hashes[slot_hashes_index].1
402 );
403 inc_new_counter_info!("dropped-vote-hash", 1);
404 return Err(VoteError::SlotHashMismatch);
405 }
406
407 let mut proposed_lockouts_index = 0;
409 let mut filter_votes_index = 0;
410 proposed_lockouts.retain(|_lockout| {
411 let should_retain = if filter_votes_index == proposed_lockouts_indices_to_filter.len() {
412 true
413 } else if proposed_lockouts_index == proposed_lockouts_indices_to_filter[filter_votes_index]
414 {
415 filter_votes_index = filter_votes_index.checked_add(1).unwrap();
416 false
417 } else {
418 true
419 };
420
421 proposed_lockouts_index = proposed_lockouts_index
422 .checked_add(1)
423 .expect("`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when filtering out irrelevant votes");
424 should_retain
425 });
426
427 Ok(())
428}
429
430fn check_slots_are_valid(
431 vote_state: &VoteState,
432 vote_slots: &[Slot],
433 vote_hash: &Hash,
434 slot_hashes: &[(Slot, Hash)],
435) -> Result<(), VoteError> {
436 let mut i = 0;
439
440 let mut j = slot_hashes.len();
443
444 while i < vote_slots.len() && j > 0 {
453 if vote_state
456 .last_voted_slot()
457 .map_or(false, |last_voted_slot| vote_slots[i] <= last_voted_slot)
458 {
459 i = i
460 .checked_add(1)
461 .expect("`i` is bounded by `MAX_LOCKOUT_HISTORY` when finding larger slots");
462 continue;
463 }
464
465 if vote_slots[i] != slot_hashes[j.checked_sub(1).expect("`j` is positive")].0 {
467 j = j
469 .checked_sub(1)
470 .expect("`j` is positive when finding newer slots");
471 continue;
472 }
473
474 i = i
477 .checked_add(1)
478 .expect("`i` is bounded by `MAX_LOCKOUT_HISTORY` when hash is found");
479 j = j
480 .checked_sub(1)
481 .expect("`j` is positive when hash is found");
482 }
483
484 if j == slot_hashes.len() {
485 debug!(
489 "{} dropped vote slots {:?}, vote hash: {:?} slot hashes:SlotHash {:?}, too old ",
490 vote_state.node_pubkey, vote_slots, vote_hash, slot_hashes
491 );
492 return Err(VoteError::VoteTooOld);
493 }
494 if i != vote_slots.len() {
495 info!(
498 "{} dropped vote slots {:?} failed to match slot hashes: {:?}",
499 vote_state.node_pubkey, vote_slots, slot_hashes,
500 );
501 return Err(VoteError::SlotsMismatch);
502 }
503 if &slot_hashes[j].1 != vote_hash {
504 warn!(
508 "{} dropped vote slots {:?} failed to match hash {} {}",
509 vote_state.node_pubkey, vote_slots, vote_hash, slot_hashes[j].1
510 );
511 return Err(VoteError::SlotHashMismatch);
512 }
513 Ok(())
514}
515
516pub fn process_new_vote_state(
554 vote_state: &mut VoteState,
555 mut new_state: VecDeque<LandedVote>,
556 new_root: Option<Slot>,
557 timestamp: Option<i64>,
558 epoch: Epoch,
559 current_slot: Slot,
560 feature_set: Option<&FeatureSet>,
561) -> Result<(), VoteError> {
562 assert!(!new_state.is_empty());
563 if new_state.len() > MAX_LOCKOUT_HISTORY {
564 return Err(VoteError::TooManyVotes);
565 }
566
567 match (new_root, vote_state.root_slot) {
568 (Some(new_root), Some(current_root)) => {
569 if new_root < current_root {
570 return Err(VoteError::RootRollBack);
571 }
572 }
573 (None, Some(_)) => {
574 return Err(VoteError::RootRollBack);
575 }
576 _ => (),
577 }
578
579 let mut previous_vote: Option<&LandedVote> = None;
580
581 for vote in &new_state {
586 if vote.confirmation_count() == 0 {
587 return Err(VoteError::ZeroConfirmations);
588 } else if vote.confirmation_count() > MAX_LOCKOUT_HISTORY as u32 {
589 return Err(VoteError::ConfirmationTooLarge);
590 } else if let Some(new_root) = new_root {
591 if vote.slot() <= new_root
592 &&
593 new_root != Slot::default()
598 {
599 return Err(VoteError::SlotSmallerThanRoot);
600 }
601 }
602
603 if let Some(previous_vote) = previous_vote {
604 if previous_vote.slot() >= vote.slot() {
605 return Err(VoteError::SlotsNotOrdered);
606 } else if previous_vote.confirmation_count() <= vote.confirmation_count() {
607 return Err(VoteError::ConfirmationsNotOrdered);
608 } else if vote.slot() > previous_vote.lockout.last_locked_out_slot() {
609 return Err(VoteError::NewVoteStateLockoutMismatch);
610 }
611 }
612 previous_vote = Some(vote);
613 }
614
615 let mut current_vote_state_index: usize = 0;
618 let mut new_vote_state_index = 0;
619
620 let timely_vote_credits = feature_set.map_or(false, |f| {
624 f.is_active(&feature_set::timely_vote_credits::id())
625 });
626 let mut earned_credits = if timely_vote_credits { 0_u64 } else { 1_u64 };
627
628 if let Some(new_root) = new_root {
629 for current_vote in &vote_state.votes {
630 if current_vote.slot() <= new_root {
633 if timely_vote_credits || (current_vote.slot() != new_root) {
634 earned_credits = earned_credits
635 .checked_add(vote_state.credits_for_vote_at_index(
636 current_vote_state_index,
637 timely_vote_credits,
638 ))
639 .expect("`earned_credits` does not overflow");
640 }
641 current_vote_state_index = current_vote_state_index
642 .checked_add(1)
643 .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when processing new root");
644 continue;
645 }
646
647 break;
648 }
649 }
650
651 while current_vote_state_index < vote_state.votes.len()
671 && new_vote_state_index < new_state.len()
672 {
673 let current_vote = &vote_state.votes[current_vote_state_index];
674 let new_vote = &mut new_state[new_vote_state_index];
675
676 match current_vote.slot().cmp(&new_vote.slot()) {
680 Ordering::Less => {
681 if current_vote.lockout.last_locked_out_slot() >= new_vote.slot() {
682 return Err(VoteError::LockoutConflict);
683 }
684 current_vote_state_index = current_vote_state_index
685 .checked_add(1)
686 .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is less than proposed");
687 }
688 Ordering::Equal => {
689 if new_vote.confirmation_count() < current_vote.confirmation_count() {
692 return Err(VoteError::ConfirmationRollBack);
693 }
694
695 new_vote.latency = vote_state.votes[current_vote_state_index].latency;
697
698 current_vote_state_index = current_vote_state_index
699 .checked_add(1)
700 .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is equal to proposed");
701 new_vote_state_index = new_vote_state_index
702 .checked_add(1)
703 .expect("`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is equal to proposed");
704 }
705 Ordering::Greater => {
706 new_vote_state_index = new_vote_state_index
707 .checked_add(1)
708 .expect("`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is greater than proposed");
709 }
710 }
711 }
712
713 if timely_vote_credits {
721 for new_vote in new_state.iter_mut() {
722 if new_vote.latency == 0 {
723 new_vote.latency = VoteState::compute_vote_latency(new_vote.slot(), current_slot);
724 }
725 }
726 }
727
728 if vote_state.root_slot != new_root {
729 vote_state.increment_credits(epoch, earned_credits);
733 }
734 if let Some(timestamp) = timestamp {
735 let last_slot = new_state.back().unwrap().slot();
736 vote_state.process_timestamp(last_slot, timestamp)?;
737 }
738 vote_state.root_slot = new_root;
739 vote_state.votes = new_state;
740
741 Ok(())
742}
743
744pub fn process_vote_unfiltered(
745 vote_state: &mut VoteState,
746 vote_slots: &[Slot],
747 vote: &Vote,
748 slot_hashes: &[SlotHash],
749 epoch: Epoch,
750 current_slot: Slot,
751 timely_vote_credits: bool,
752) -> Result<(), VoteError> {
753 check_slots_are_valid(vote_state, vote_slots, &vote.hash, slot_hashes)?;
754 vote_slots.iter().for_each(|s| {
755 vote_state.process_next_vote_slot(*s, epoch, current_slot, timely_vote_credits)
756 });
757 Ok(())
758}
759
760pub fn process_vote(
761 vote_state: &mut VoteState,
762 vote: &Vote,
763 slot_hashes: &[SlotHash],
764 epoch: Epoch,
765 current_slot: Slot,
766 timely_vote_credits: bool,
767) -> Result<(), VoteError> {
768 if vote.slots.is_empty() {
769 return Err(VoteError::EmptySlots);
770 }
771 let earliest_slot_in_history = slot_hashes.last().map(|(slot, _hash)| *slot).unwrap_or(0);
772 let vote_slots = vote
773 .slots
774 .iter()
775 .filter(|slot| **slot >= earliest_slot_in_history)
776 .cloned()
777 .collect::<Vec<Slot>>();
778 if vote_slots.is_empty() {
779 return Err(VoteError::VotesTooOldAllFiltered);
780 }
781 process_vote_unfiltered(
782 vote_state,
783 &vote_slots,
784 vote,
785 slot_hashes,
786 epoch,
787 current_slot,
788 timely_vote_credits,
789 )
790}
791
792pub fn process_vote_unchecked(vote_state: &mut VoteState, vote: Vote) -> Result<(), VoteError> {
794 if vote.slots.is_empty() {
795 return Err(VoteError::EmptySlots);
796 }
797 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
798 process_vote_unfiltered(
799 vote_state,
800 &vote.slots,
801 &vote,
802 &slot_hashes,
803 vote_state.current_epoch(),
804 0,
805 true,
806 )
807}
808
809#[cfg(test)]
810pub fn process_slot_votes_unchecked(vote_state: &mut VoteState, slots: &[Slot]) {
811 for slot in slots {
812 process_slot_vote_unchecked(vote_state, *slot);
813 }
814}
815
816pub fn process_slot_vote_unchecked(vote_state: &mut VoteState, slot: Slot) {
817 let _ = process_vote_unchecked(vote_state, Vote::new(vec![slot], Hash::default()));
818}
819
820pub fn authorize<S: std::hash::BuildHasher>(
824 vote_account: &mut BorrowedAccount,
825 authorized: &Pubkey,
826 vote_authorize: VoteAuthorize,
827 signers: &HashSet<Pubkey, S>,
828 clock: &Clock,
829) -> Result<(), InstructionError> {
830 let mut vote_state: VoteState = vote_account
831 .get_state::<VoteStateVersions>()?
832 .convert_to_current();
833
834 match vote_authorize {
835 VoteAuthorize::Voter => {
836 let authorized_withdrawer_signer =
837 verify_authorized_signer(&vote_state.authorized_withdrawer, signers).is_ok();
838
839 vote_state.set_new_authorized_voter(
840 authorized,
841 clock.epoch,
842 clock
843 .leader_schedule_epoch
844 .checked_add(1)
845 .ok_or(InstructionError::InvalidAccountData)?,
846 |epoch_authorized_voter| {
847 if authorized_withdrawer_signer {
849 Ok(())
850 } else {
851 verify_authorized_signer(&epoch_authorized_voter, signers)
852 }
853 },
854 )?;
855 }
856 VoteAuthorize::Withdrawer => {
857 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
859 vote_state.authorized_withdrawer = *authorized;
860 }
861 }
862
863 set_vote_account_state(vote_account, vote_state)
864}
865
866pub fn update_validator_identity<S: std::hash::BuildHasher>(
868 vote_account: &mut BorrowedAccount,
869 node_pubkey: &Pubkey,
870 signers: &HashSet<Pubkey, S>,
871) -> Result<(), InstructionError> {
872 let mut vote_state: VoteState = vote_account
873 .get_state::<VoteStateVersions>()?
874 .convert_to_current();
875
876 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
878
879 verify_authorized_signer(node_pubkey, signers)?;
881
882 vote_state.node_pubkey = *node_pubkey;
883
884 set_vote_account_state(vote_account, vote_state)
885}
886
887pub fn update_commission<S: std::hash::BuildHasher>(
889 vote_account: &mut BorrowedAccount,
890 commission: u8,
891 signers: &HashSet<Pubkey, S>,
892 epoch_schedule: &EpochSchedule,
893 clock: &Clock,
894 feature_set: &FeatureSet,
895) -> Result<(), InstructionError> {
896 let mut vote_state = None;
898
899 let enforce_commission_update_rule =
900 if feature_set.is_active(&feature_set::allow_commission_decrease_at_any_time::id()) {
901 if let Ok(decoded_vote_state) = vote_account.get_state::<VoteStateVersions>() {
902 vote_state = Some(decoded_vote_state.convert_to_current());
903 is_commission_increase(vote_state.as_ref().unwrap(), commission)
904 } else {
905 true
906 }
907 } else {
908 true
909 };
910
911 #[allow(clippy::collapsible_if)]
912 if enforce_commission_update_rule
913 && feature_set
914 .is_active(&feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id())
915 {
916 if !is_commission_update_allowed(clock.slot, epoch_schedule) {
917 return Err(VoteError::CommissionUpdateTooLate.into());
918 }
919 }
920
921 let mut vote_state = match vote_state {
922 Some(vote_state) => vote_state,
923 None => vote_account
924 .get_state::<VoteStateVersions>()?
925 .convert_to_current(),
926 };
927
928 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
930
931 vote_state.commission = commission;
932
933 set_vote_account_state(vote_account, vote_state)
934}
935
936pub fn is_commission_increase(vote_state: &VoteState, commission: u8) -> bool {
938 commission > vote_state.commission
939}
940
941pub fn is_commission_update_allowed(slot: Slot, epoch_schedule: &EpochSchedule) -> bool {
944 if let Some(relative_slot) = slot
946 .saturating_sub(epoch_schedule.first_normal_slot)
947 .checked_rem(epoch_schedule.slots_per_epoch)
948 {
949 relative_slot.saturating_mul(2) <= epoch_schedule.slots_per_epoch
951 } else {
952 true
954 }
955}
956
957fn verify_authorized_signer<S: std::hash::BuildHasher>(
958 authorized: &Pubkey,
959 signers: &HashSet<Pubkey, S>,
960) -> Result<(), InstructionError> {
961 if signers.contains(authorized) {
962 Ok(())
963 } else {
964 Err(InstructionError::MissingRequiredSignature)
965 }
966}
967
968pub fn withdraw<S: std::hash::BuildHasher>(
970 transaction_context: &TransactionContext,
971 instruction_context: &InstructionContext,
972 vote_account_index: IndexOfAccount,
973 lamports: u64,
974 to_account_index: IndexOfAccount,
975 signers: &HashSet<Pubkey, S>,
976 rent_sysvar: &Rent,
977 clock: &Clock,
978) -> Result<(), InstructionError> {
979 let mut vote_account = instruction_context
980 .try_borrow_instruction_account(transaction_context, vote_account_index)?;
981 let vote_state: VoteState = vote_account
982 .get_state::<VoteStateVersions>()?
983 .convert_to_current();
984
985 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
986
987 let remaining_balance = vote_account
988 .get_lamports()
989 .checked_sub(lamports)
990 .ok_or(InstructionError::InsufficientFunds)?;
991
992 if remaining_balance == 0 {
993 let reject_active_vote_account_close = vote_state
994 .epoch_credits
995 .last()
996 .map(|(last_epoch_with_credits, _, _)| {
997 let current_epoch = clock.epoch;
998 current_epoch.saturating_sub(*last_epoch_with_credits) < 2
1002 })
1003 .unwrap_or(false);
1004
1005 if reject_active_vote_account_close {
1006 return Err(VoteError::ActiveVoteAccountClose.into());
1007 } else {
1008 set_vote_account_state(&mut vote_account, VoteState::default())?;
1010 }
1011 } else {
1012 let min_rent_exempt_balance = rent_sysvar.minimum_balance(vote_account.get_data().len());
1013 if remaining_balance < min_rent_exempt_balance {
1014 return Err(InstructionError::InsufficientFunds);
1015 }
1016 }
1017
1018 vote_account.checked_sub_lamports(lamports)?;
1019 drop(vote_account);
1020 let mut to_account = instruction_context
1021 .try_borrow_instruction_account(transaction_context, to_account_index)?;
1022 to_account.checked_add_lamports(lamports)?;
1023 Ok(())
1024}
1025
1026pub fn initialize_account<S: std::hash::BuildHasher>(
1030 vote_account: &mut BorrowedAccount,
1031 vote_init: &VoteInit,
1032 signers: &HashSet<Pubkey, S>,
1033 clock: &Clock,
1034) -> Result<(), InstructionError> {
1035 if vote_account.get_data().len() != VoteStateVersions::vote_state_size_of(true) {
1036 return Err(InstructionError::InvalidAccountData);
1037 }
1038 let versioned = vote_account.get_state::<VoteStateVersions>()?;
1039
1040 if !versioned.is_uninitialized() {
1041 return Err(InstructionError::AccountAlreadyInitialized);
1042 }
1043
1044 verify_authorized_signer(&vote_init.node_pubkey, signers)?;
1046
1047 set_vote_account_state(vote_account, VoteState::new(vote_init, clock))
1048}
1049
1050fn verify_and_get_vote_state<S: std::hash::BuildHasher>(
1051 vote_account: &BorrowedAccount,
1052 clock: &Clock,
1053 signers: &HashSet<Pubkey, S>,
1054) -> Result<VoteState, InstructionError> {
1055 let versioned = vote_account.get_state::<VoteStateVersions>()?;
1056
1057 if versioned.is_uninitialized() {
1058 return Err(InstructionError::UninitializedAccount);
1059 }
1060
1061 let mut vote_state = versioned.convert_to_current();
1062 let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?;
1063 verify_authorized_signer(&authorized_voter, signers)?;
1064
1065 Ok(vote_state)
1066}
1067
1068pub fn process_vote_with_account<S: std::hash::BuildHasher>(
1069 vote_account: &mut BorrowedAccount,
1070 slot_hashes: &[SlotHash],
1071 clock: &Clock,
1072 vote: &Vote,
1073 signers: &HashSet<Pubkey, S>,
1074 feature_set: &FeatureSet,
1075) -> Result<(), InstructionError> {
1076 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1077
1078 let timely_vote_credits = feature_set.is_active(&feature_set::timely_vote_credits::id());
1079 process_vote(
1080 &mut vote_state,
1081 vote,
1082 slot_hashes,
1083 clock.epoch,
1084 clock.slot,
1085 timely_vote_credits,
1086 )?;
1087 if let Some(timestamp) = vote.timestamp {
1088 vote.slots
1089 .iter()
1090 .max()
1091 .ok_or(VoteError::EmptySlots)
1092 .and_then(|slot| vote_state.process_timestamp(*slot, timestamp))?;
1093 }
1094 set_vote_account_state(vote_account, vote_state)
1095}
1096
1097pub fn process_vote_state_update<S: std::hash::BuildHasher>(
1098 vote_account: &mut BorrowedAccount,
1099 slot_hashes: &[SlotHash],
1100 clock: &Clock,
1101 vote_state_update: VoteStateUpdate,
1102 signers: &HashSet<Pubkey, S>,
1103 feature_set: &FeatureSet,
1104) -> Result<(), InstructionError> {
1105 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1106 do_process_vote_state_update(
1107 &mut vote_state,
1108 slot_hashes,
1109 clock.epoch,
1110 clock.slot,
1111 vote_state_update,
1112 Some(feature_set),
1113 )?;
1114 set_vote_account_state(vote_account, vote_state)
1115}
1116
1117pub fn do_process_vote_state_update(
1118 vote_state: &mut VoteState,
1119 slot_hashes: &[SlotHash],
1120 epoch: u64,
1121 slot: u64,
1122 mut vote_state_update: VoteStateUpdate,
1123 feature_set: Option<&FeatureSet>,
1124) -> Result<(), VoteError> {
1125 check_and_filter_proposed_vote_state(
1126 vote_state,
1127 &mut vote_state_update.lockouts,
1128 &mut vote_state_update.root,
1129 vote_state_update.hash,
1130 slot_hashes,
1131 )?;
1132 process_new_vote_state(
1133 vote_state,
1134 vote_state_update
1135 .lockouts
1136 .iter()
1137 .map(|lockout| LandedVote::from(*lockout))
1138 .collect(),
1139 vote_state_update.root,
1140 vote_state_update.timestamp,
1141 epoch,
1142 slot,
1143 feature_set,
1144 )
1145}
1146
1147pub fn process_tower_sync<S: std::hash::BuildHasher>(
1148 vote_account: &mut BorrowedAccount,
1149 slot_hashes: &[SlotHash],
1150 clock: &Clock,
1151 tower_sync: TowerSync,
1152 signers: &HashSet<Pubkey, S>,
1153 feature_set: &FeatureSet,
1154) -> Result<(), InstructionError> {
1155 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1156 do_process_tower_sync(
1157 &mut vote_state,
1158 slot_hashes,
1159 clock.epoch,
1160 clock.slot,
1161 tower_sync,
1162 Some(feature_set),
1163 )?;
1164 set_vote_account_state(vote_account, vote_state)
1165}
1166
1167fn do_process_tower_sync(
1168 vote_state: &mut VoteState,
1169 slot_hashes: &[SlotHash],
1170 epoch: u64,
1171 slot: u64,
1172 mut tower_sync: TowerSync,
1173 feature_set: Option<&FeatureSet>,
1174) -> Result<(), VoteError> {
1175 check_and_filter_proposed_vote_state(
1176 vote_state,
1177 &mut tower_sync.lockouts,
1178 &mut tower_sync.root,
1179 tower_sync.hash,
1180 slot_hashes,
1181 )?;
1182 process_new_vote_state(
1183 vote_state,
1184 tower_sync
1185 .lockouts
1186 .iter()
1187 .map(|lockout| LandedVote::from(*lockout))
1188 .collect(),
1189 tower_sync.root,
1190 tower_sync.timestamp,
1191 epoch,
1192 slot,
1193 feature_set,
1194 )
1195}
1196
1197pub fn create_account_with_authorized(
1202 node_pubkey: &Pubkey,
1203 authorized_voter: &Pubkey,
1204 authorized_withdrawer: &Pubkey,
1205 commission: u8,
1206 lamports: u64,
1207) -> AccountSharedData {
1208 let mut vote_account = AccountSharedData::new(lamports, VoteState::size_of(), &id());
1209
1210 let vote_state = VoteState::new(
1211 &VoteInit {
1212 node_pubkey: *node_pubkey,
1213 authorized_voter: *authorized_voter,
1214 authorized_withdrawer: *authorized_withdrawer,
1215 commission,
1216 },
1217 &Clock::default(),
1218 );
1219
1220 VoteState::serialize(
1221 &VoteStateVersions::Current(Box::new(vote_state)),
1222 vote_account.data_as_mut_slice(),
1223 )
1224 .unwrap();
1225
1226 vote_account
1227}
1228
1229pub fn create_account(
1231 vote_pubkey: &Pubkey,
1232 node_pubkey: &Pubkey,
1233 commission: u8,
1234 lamports: u64,
1235) -> AccountSharedData {
1236 create_account_with_authorized(node_pubkey, vote_pubkey, vote_pubkey, commission, lamports)
1237}
1238
1239#[cfg(test)]
1240mod tests {
1241 use {
1242 super::*,
1243 crate::vote_state,
1244 assert_matches::assert_matches,
1245 solana_sdk::{
1246 account::AccountSharedData, account_utils::StateMut, clock::DEFAULT_SLOTS_PER_EPOCH,
1247 hash::hash, transaction_context::InstructionAccount,
1248 },
1249 std::cell::RefCell,
1250 test_case::test_case,
1251 };
1252
1253 const MAX_RECENT_VOTES: usize = 16;
1254
1255 fn vote_state_new_for_test(auth_pubkey: &Pubkey) -> VoteState {
1256 VoteState::new(
1257 &VoteInit {
1258 node_pubkey: solana_sdk::pubkey::new_rand(),
1259 authorized_voter: *auth_pubkey,
1260 authorized_withdrawer: *auth_pubkey,
1261 commission: 0,
1262 },
1263 &Clock::default(),
1264 )
1265 }
1266
1267 fn create_test_account() -> (Pubkey, RefCell<AccountSharedData>) {
1268 let rent = Rent::default();
1269 let balance = VoteState::get_rent_exempt_reserve(&rent);
1270 let vote_pubkey = solana_sdk::pubkey::new_rand();
1271 (
1272 vote_pubkey,
1273 RefCell::new(vote_state::create_account(
1274 &vote_pubkey,
1275 &solana_sdk::pubkey::new_rand(),
1276 0,
1277 balance,
1278 )),
1279 )
1280 }
1281
1282 #[test]
1283 fn test_vote_state_upgrade_from_1_14_11() {
1284 let node_pubkey = solana_sdk::pubkey::new_rand();
1287 let withdrawer_pubkey = solana_sdk::pubkey::new_rand();
1288 let mut vote_state = VoteState::new(
1289 &VoteInit {
1290 node_pubkey,
1291 authorized_voter: withdrawer_pubkey,
1292 authorized_withdrawer: withdrawer_pubkey,
1293 commission: 10,
1294 },
1295 &Clock::default(),
1296 );
1297 vote_state.increment_credits(0, 100);
1299 assert_eq!(
1300 vote_state
1301 .set_new_authorized_voter(&solana_sdk::pubkey::new_rand(), 0, 1, |_pubkey| Ok(())),
1302 Ok(())
1303 );
1304 vote_state.increment_credits(1, 200);
1305 assert_eq!(
1306 vote_state
1307 .set_new_authorized_voter(&solana_sdk::pubkey::new_rand(), 1, 2, |_pubkey| Ok(())),
1308 Ok(())
1309 );
1310 vote_state.increment_credits(2, 300);
1311 assert_eq!(
1312 vote_state
1313 .set_new_authorized_voter(&solana_sdk::pubkey::new_rand(), 2, 3, |_pubkey| Ok(())),
1314 Ok(())
1315 );
1316 vec![
1318 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
1319 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
1320 134, 135,
1321 ]
1322 .into_iter()
1323 .for_each(|v| vote_state.process_next_vote_slot(v, 4, 0, false));
1324
1325 let version1_14_11_serialized = bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(
1326 VoteState1_14_11::from(vote_state.clone()),
1327 )))
1328 .unwrap();
1329 let version1_14_11_serialized_len = version1_14_11_serialized.len();
1330 let rent = Rent::default();
1331 let lamports = rent.minimum_balance(version1_14_11_serialized_len);
1332 let mut vote_account =
1333 AccountSharedData::new(lamports, version1_14_11_serialized_len, &id());
1334 vote_account.set_data_from_slice(&version1_14_11_serialized);
1335
1336 let processor_account = AccountSharedData::new(0, 0, &solana_sdk::native_loader::id());
1339 let transaction_context = TransactionContext::new(
1340 vec![(id(), processor_account), (node_pubkey, vote_account)],
1341 rent.clone(),
1342 0,
1343 0,
1344 );
1345 let mut instruction_context = InstructionContext::default();
1346 instruction_context.configure(
1347 &[0],
1348 &[InstructionAccount {
1349 index_in_transaction: 1,
1350 index_in_caller: 1,
1351 index_in_callee: 0,
1352 is_signer: false,
1353 is_writable: true,
1354 }],
1355 &[],
1356 );
1357
1358 let mut borrowed_account = instruction_context
1361 .try_borrow_instruction_account(&transaction_context, 0)
1362 .unwrap();
1363
1364 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1366 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1367
1368 let converted_vote_state = vote_state_version.convert_to_current();
1370
1371 assert!(vote_state == converted_vote_state);
1373
1374 let vote_state = converted_vote_state;
1375
1376 assert_eq!(
1379 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1380 Ok(())
1381 );
1382 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1383 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1384
1385 let converted_vote_state = vote_state_version.convert_to_current();
1387
1388 assert_eq!(vote_state, converted_vote_state);
1390
1391 let vote_state = converted_vote_state;
1392
1393 assert_eq!(
1396 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1397 Ok(())
1398 );
1399 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1400 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1401
1402 let converted_vote_state = vote_state_version.convert_to_current();
1404
1405 assert_eq!(vote_state, converted_vote_state);
1407
1408 let vote_state = converted_vote_state;
1409
1410 assert_eq!(
1413 borrowed_account.set_lamports(rent.minimum_balance(VoteState::size_of()),),
1414 Ok(())
1415 );
1416 assert_eq!(
1417 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1418 Ok(())
1419 );
1420 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1421 assert_matches!(vote_state_version, VoteStateVersions::Current(_));
1422
1423 let converted_vote_state = vote_state_version.convert_to_current();
1425
1426 assert_eq!(vote_state, converted_vote_state);
1428 }
1429
1430 #[test]
1431 fn test_vote_lockout() {
1432 let (_vote_pubkey, vote_account) = create_test_account();
1433
1434 let mut vote_state: VoteState =
1435 StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
1436 .unwrap()
1437 .convert_to_current();
1438
1439 for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
1440 process_slot_vote_unchecked(&mut vote_state, (INITIAL_LOCKOUT * i) as u64);
1441 }
1442
1443 assert_eq!(vote_state.votes.len(), MAX_LOCKOUT_HISTORY);
1445 assert_eq!(vote_state.root_slot, Some(0));
1446 check_lockouts(&vote_state);
1447
1448 let top_vote = vote_state.votes.front().unwrap().slot();
1452 let slot = vote_state.last_lockout().unwrap().last_locked_out_slot();
1453 process_slot_vote_unchecked(&mut vote_state, slot);
1454 assert_eq!(Some(top_vote), vote_state.root_slot);
1455
1456 let slot = vote_state
1458 .votes
1459 .front()
1460 .unwrap()
1461 .lockout
1462 .last_locked_out_slot();
1463 process_slot_vote_unchecked(&mut vote_state, slot);
1464 assert_eq!(vote_state.votes.len(), 2);
1466 }
1467
1468 #[test]
1469 fn test_update_commission() {
1470 let node_pubkey = Pubkey::new_unique();
1471 let withdrawer_pubkey = Pubkey::new_unique();
1472 let clock = Clock::default();
1473 let vote_state = VoteState::new(
1474 &VoteInit {
1475 node_pubkey,
1476 authorized_voter: withdrawer_pubkey,
1477 authorized_withdrawer: withdrawer_pubkey,
1478 commission: 10,
1479 },
1480 &clock,
1481 );
1482
1483 let serialized =
1484 bincode::serialize(&VoteStateVersions::Current(Box::new(vote_state.clone()))).unwrap();
1485 let serialized_len = serialized.len();
1486 let rent = Rent::default();
1487 let lamports = rent.minimum_balance(serialized_len);
1488 let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id());
1489 vote_account.set_data_from_slice(&serialized);
1490
1491 let processor_account = AccountSharedData::new(0, 0, &solana_sdk::native_loader::id());
1494 let transaction_context = TransactionContext::new(
1495 vec![(id(), processor_account), (node_pubkey, vote_account)],
1496 rent,
1497 0,
1498 0,
1499 );
1500 let mut instruction_context = InstructionContext::default();
1501 instruction_context.configure(
1502 &[0],
1503 &[InstructionAccount {
1504 index_in_transaction: 1,
1505 index_in_caller: 1,
1506 index_in_callee: 0,
1507 is_signer: false,
1508 is_writable: true,
1509 }],
1510 &[],
1511 );
1512
1513 let mut borrowed_account = instruction_context
1516 .try_borrow_instruction_account(&transaction_context, 0)
1517 .unwrap();
1518
1519 let epoch_schedule = std::sync::Arc::new(EpochSchedule::without_warmup());
1520
1521 let first_half_clock = std::sync::Arc::new(Clock {
1522 slot: epoch_schedule.slots_per_epoch / 4,
1523 ..Clock::default()
1524 });
1525
1526 let second_half_clock = std::sync::Arc::new(Clock {
1527 slot: (epoch_schedule.slots_per_epoch * 3) / 4,
1528 ..Clock::default()
1529 });
1530
1531 let mut feature_set = FeatureSet::default();
1532 feature_set.activate(
1533 &feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id(),
1534 1,
1535 );
1536
1537 let signers: HashSet<Pubkey> = vec![withdrawer_pubkey].into_iter().collect();
1538
1539 assert_eq!(
1541 borrowed_account
1542 .get_state::<VoteStateVersions>()
1543 .unwrap()
1544 .convert_to_current()
1545 .commission,
1546 10
1547 );
1548 assert_matches!(
1549 update_commission(
1550 &mut borrowed_account,
1551 11,
1552 &signers,
1553 &epoch_schedule,
1554 &first_half_clock,
1555 &feature_set
1556 ),
1557 Ok(())
1558 );
1559 assert_eq!(
1560 borrowed_account
1561 .get_state::<VoteStateVersions>()
1562 .unwrap()
1563 .convert_to_current()
1564 .commission,
1565 11
1566 );
1567
1568 assert_matches!(
1570 update_commission(
1571 &mut borrowed_account,
1572 12,
1573 &signers,
1574 &epoch_schedule,
1575 &second_half_clock,
1576 &feature_set
1577 ),
1578 Err(_)
1579 );
1580 assert_eq!(
1581 borrowed_account
1582 .get_state::<VoteStateVersions>()
1583 .unwrap()
1584 .convert_to_current()
1585 .commission,
1586 11
1587 );
1588
1589 assert_matches!(
1591 update_commission(
1592 &mut borrowed_account,
1593 10,
1594 &signers,
1595 &epoch_schedule,
1596 &first_half_clock,
1597 &feature_set
1598 ),
1599 Ok(())
1600 );
1601 assert_eq!(
1602 borrowed_account
1603 .get_state::<VoteStateVersions>()
1604 .unwrap()
1605 .convert_to_current()
1606 .commission,
1607 10
1608 );
1609
1610 assert_matches!(
1612 update_commission(
1613 &mut borrowed_account,
1614 9,
1615 &signers,
1616 &epoch_schedule,
1617 &second_half_clock,
1618 &feature_set
1619 ),
1620 Err(_)
1621 );
1622 assert_eq!(
1623 borrowed_account
1624 .get_state::<VoteStateVersions>()
1625 .unwrap()
1626 .convert_to_current()
1627 .commission,
1628 10
1629 );
1630
1631 feature_set.activate(&feature_set::allow_commission_decrease_at_any_time::id(), 1);
1633 assert_matches!(
1634 update_commission(
1635 &mut borrowed_account,
1636 9,
1637 &signers,
1638 &epoch_schedule,
1639 &second_half_clock,
1640 &feature_set
1641 ),
1642 Ok(())
1643 );
1644 assert_eq!(
1645 borrowed_account
1646 .get_state::<VoteStateVersions>()
1647 .unwrap()
1648 .convert_to_current()
1649 .commission,
1650 9
1651 );
1652 }
1653
1654 #[test]
1655 fn test_vote_double_lockout_after_expiration() {
1656 let voter_pubkey = solana_sdk::pubkey::new_rand();
1657 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1658
1659 for i in 0..3 {
1660 process_slot_vote_unchecked(&mut vote_state, i as u64);
1661 }
1662
1663 check_lockouts(&vote_state);
1664
1665 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 1) as u64);
1669 check_lockouts(&vote_state);
1670
1671 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 2) as u64);
1674 check_lockouts(&vote_state);
1675
1676 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 3) as u64);
1679 check_lockouts(&vote_state);
1680 }
1681
1682 #[test]
1683 fn test_expire_multiple_votes() {
1684 let voter_pubkey = solana_sdk::pubkey::new_rand();
1685 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1686
1687 for i in 0..3 {
1688 process_slot_vote_unchecked(&mut vote_state, i as u64);
1689 }
1690
1691 assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1692
1693 let expire_slot = vote_state.votes[1].slot() + vote_state.votes[1].lockout.lockout() + 1;
1695 process_slot_vote_unchecked(&mut vote_state, expire_slot);
1696 assert_eq!(vote_state.votes.len(), 2);
1697
1698 assert_eq!(vote_state.votes[0].slot(), 0);
1700 assert_eq!(vote_state.votes[1].slot(), expire_slot);
1701
1702 process_slot_vote_unchecked(&mut vote_state, expire_slot + 1);
1704
1705 assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1707
1708 assert_eq!(vote_state.votes[1].confirmation_count(), 2);
1710 assert_eq!(vote_state.votes[2].confirmation_count(), 1);
1711 }
1712
1713 #[test]
1714 fn test_vote_credits() {
1715 let voter_pubkey = solana_sdk::pubkey::new_rand();
1716 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1717
1718 for i in 0..MAX_LOCKOUT_HISTORY {
1719 process_slot_vote_unchecked(&mut vote_state, i as u64);
1720 }
1721
1722 assert_eq!(vote_state.credits(), 0);
1723
1724 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 1);
1725 assert_eq!(vote_state.credits(), 1);
1726 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 2);
1727 assert_eq!(vote_state.credits(), 2);
1728 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 3);
1729 assert_eq!(vote_state.credits(), 3);
1730 }
1731
1732 #[test]
1733 fn test_duplicate_vote() {
1734 let voter_pubkey = solana_sdk::pubkey::new_rand();
1735 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1736 process_slot_vote_unchecked(&mut vote_state, 0);
1737 process_slot_vote_unchecked(&mut vote_state, 1);
1738 process_slot_vote_unchecked(&mut vote_state, 0);
1739 assert_eq!(vote_state.nth_recent_lockout(0).unwrap().slot(), 1);
1740 assert_eq!(vote_state.nth_recent_lockout(1).unwrap().slot(), 0);
1741 assert!(vote_state.nth_recent_lockout(2).is_none());
1742 }
1743
1744 #[test]
1745 fn test_nth_recent_lockout() {
1746 let voter_pubkey = solana_sdk::pubkey::new_rand();
1747 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1748 for i in 0..MAX_LOCKOUT_HISTORY {
1749 process_slot_vote_unchecked(&mut vote_state, i as u64);
1750 }
1751 for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
1752 assert_eq!(
1753 vote_state.nth_recent_lockout(i).unwrap().slot() as usize,
1754 MAX_LOCKOUT_HISTORY - i - 1,
1755 );
1756 }
1757 assert!(vote_state.nth_recent_lockout(MAX_LOCKOUT_HISTORY).is_none());
1758 }
1759
1760 fn check_lockouts(vote_state: &VoteState) {
1761 for (i, vote) in vote_state.votes.iter().enumerate() {
1762 let num_votes = vote_state
1763 .votes
1764 .len()
1765 .checked_sub(i)
1766 .expect("`i` is less than `vote_state.votes.len()`");
1767 assert_eq!(
1768 vote.lockout.lockout(),
1769 INITIAL_LOCKOUT.pow(num_votes as u32) as u64
1770 );
1771 }
1772 }
1773
1774 fn recent_votes(vote_state: &VoteState) -> Vec<Vote> {
1775 let start = vote_state.votes.len().saturating_sub(MAX_RECENT_VOTES);
1776 (start..vote_state.votes.len())
1777 .map(|i| {
1778 Vote::new(
1779 vec![vote_state.votes.get(i).unwrap().slot()],
1780 Hash::default(),
1781 )
1782 })
1783 .collect()
1784 }
1785
1786 #[test]
1788 fn test_process_missed_votes() {
1789 let account_a = solana_sdk::pubkey::new_rand();
1790 let mut vote_state_a = vote_state_new_for_test(&account_a);
1791 let account_b = solana_sdk::pubkey::new_rand();
1792 let mut vote_state_b = vote_state_new_for_test(&account_b);
1793
1794 (0..5).for_each(|i| process_slot_vote_unchecked(&mut vote_state_a, i as u64));
1796 assert_ne!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1797
1798 let slots = (0u64..MAX_RECENT_VOTES as u64).collect();
1800 let vote = Vote::new(slots, Hash::default());
1801 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
1802
1803 assert_eq!(
1804 process_vote(&mut vote_state_a, &vote, &slot_hashes, 0, 0, true),
1805 Ok(())
1806 );
1807 assert_eq!(
1808 process_vote(&mut vote_state_b, &vote, &slot_hashes, 0, 0, true),
1809 Ok(())
1810 );
1811 assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1812 }
1813
1814 #[test]
1815 fn test_process_vote_skips_old_vote() {
1816 let mut vote_state = VoteState::default();
1817
1818 let vote = Vote::new(vec![0], Hash::default());
1819 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1820 assert_eq!(
1821 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
1822 Ok(())
1823 );
1824 let recent = recent_votes(&vote_state);
1825 assert_eq!(
1826 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
1827 Err(VoteError::VoteTooOld)
1828 );
1829 assert_eq!(recent, recent_votes(&vote_state));
1830 }
1831
1832 #[test]
1833 fn test_check_slots_are_valid_vote_empty_slot_hashes() {
1834 let vote_state = VoteState::default();
1835
1836 let vote = Vote::new(vec![0], Hash::default());
1837 assert_eq!(
1838 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &[]),
1839 Err(VoteError::VoteTooOld)
1840 );
1841 }
1842
1843 #[test]
1844 fn test_check_slots_are_valid_new_vote() {
1845 let vote_state = VoteState::default();
1846
1847 let vote = Vote::new(vec![0], Hash::default());
1848 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1849 assert_eq!(
1850 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1851 Ok(())
1852 );
1853 }
1854
1855 #[test]
1856 fn test_check_slots_are_valid_bad_hash() {
1857 let vote_state = VoteState::default();
1858
1859 let vote = Vote::new(vec![0], Hash::default());
1860 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
1861 assert_eq!(
1862 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1863 Err(VoteError::SlotHashMismatch)
1864 );
1865 }
1866
1867 #[test]
1868 fn test_check_slots_are_valid_bad_slot() {
1869 let vote_state = VoteState::default();
1870
1871 let vote = Vote::new(vec![1], Hash::default());
1872 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1873 assert_eq!(
1874 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1875 Err(VoteError::SlotsMismatch)
1876 );
1877 }
1878
1879 #[test]
1880 fn test_check_slots_are_valid_duplicate_vote() {
1881 let mut vote_state = VoteState::default();
1882
1883 let vote = Vote::new(vec![0], Hash::default());
1884 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1885 assert_eq!(
1886 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
1887 Ok(())
1888 );
1889 assert_eq!(
1890 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1891 Err(VoteError::VoteTooOld)
1892 );
1893 }
1894
1895 #[test]
1896 fn test_check_slots_are_valid_next_vote() {
1897 let mut vote_state = VoteState::default();
1898
1899 let vote = Vote::new(vec![0], Hash::default());
1900 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1901 assert_eq!(
1902 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
1903 Ok(())
1904 );
1905
1906 let vote = Vote::new(vec![0, 1], Hash::default());
1907 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1908 assert_eq!(
1909 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1910 Ok(())
1911 );
1912 }
1913
1914 #[test]
1915 fn test_check_slots_are_valid_next_vote_only() {
1916 let mut vote_state = VoteState::default();
1917
1918 let vote = Vote::new(vec![0], Hash::default());
1919 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1920 assert_eq!(
1921 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
1922 Ok(())
1923 );
1924
1925 let vote = Vote::new(vec![1], Hash::default());
1926 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1927 assert_eq!(
1928 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1929 Ok(())
1930 );
1931 }
1932 #[test]
1933 fn test_process_vote_empty_slots() {
1934 let mut vote_state = VoteState::default();
1935
1936 let vote = Vote::new(vec![], Hash::default());
1937 assert_eq!(
1938 process_vote(&mut vote_state, &vote, &[], 0, 0, true),
1939 Err(VoteError::EmptySlots)
1940 );
1941 }
1942
1943 pub fn process_new_vote_state_from_lockouts(
1944 vote_state: &mut VoteState,
1945 new_state: VecDeque<Lockout>,
1946 new_root: Option<Slot>,
1947 timestamp: Option<i64>,
1948 epoch: Epoch,
1949 feature_set: Option<&FeatureSet>,
1950 ) -> Result<(), VoteError> {
1951 process_new_vote_state(
1952 vote_state,
1953 new_state.into_iter().map(LandedVote::from).collect(),
1954 new_root,
1955 timestamp,
1956 epoch,
1957 0,
1958 feature_set,
1959 )
1960 }
1961
1962 #[test]
1964 fn test_vote_state_update_increment_credits() {
1965 let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
1967
1968 let test_vote_groups: Vec<Vec<Slot>> = vec![
1971 vec![1, 2, 3, 4, 5, 6, 7, 8],
1973 vec![
1974 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1975 30, 31,
1976 ],
1977 vec![32],
1979 vec![33],
1981 vec![34, 35],
1983 vec![36, 37, 38],
1985 vec![
1987 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
1988 60, 61, 62, 63, 64, 65, 66, 67, 68,
1989 ],
1990 vec![
1992 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
1993 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1994 ],
1995 vec![100, 101, 106, 107, 112, 116, 120, 121, 122, 124],
1997 vec![200, 201],
1999 vec![
2000 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
2001 218, 219, 220, 221, 222, 223, 224, 225, 226,
2002 ],
2003 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
2004 ];
2005
2006 let feature_set = FeatureSet::default();
2007
2008 for vote_group in test_vote_groups {
2009 let mut vote_state_after_vote = vote_state.clone();
2011
2012 process_vote_unchecked(
2013 &mut vote_state_after_vote,
2014 Vote {
2015 slots: vote_group.clone(),
2016 hash: Hash::new_unique(),
2017 timestamp: None,
2018 },
2019 )
2020 .unwrap();
2021
2022 assert_eq!(
2024 process_new_vote_state(
2025 &mut vote_state,
2026 vote_state_after_vote.votes,
2027 vote_state_after_vote.root_slot,
2028 None,
2029 0,
2030 0,
2031 Some(&feature_set)
2032 ),
2033 Ok(())
2034 );
2035
2036 assert_eq!(
2038 vote_state.epoch_credits,
2039 vote_state_after_vote.epoch_credits
2040 );
2041 }
2042 }
2043
2044 #[test]
2046 fn test_timely_credits() {
2047 let test_vote_groups: Vec<(Vec<Slot>, Slot, u32)> = vec![
2051 (
2053 vec![1, 2, 3, 4, 5, 6, 7, 8],
2054 9,
2055 0,
2057 ),
2058 (
2059 vec![
2060 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
2061 29, 30, 31,
2062 ],
2063 34,
2064 0,
2067 ),
2068 (
2070 vec![32],
2071 35,
2072 10,
2075 ),
2076 (
2078 vec![33],
2079 36,
2080 10 + 11, ),
2084 (
2086 vec![34, 35],
2087 37,
2088 21 + 12 + 13, ),
2092 (
2094 vec![36, 37, 38],
2095 39,
2096 46 + 14 + 15 + 16, ),
2100 (
2101 vec![
2103 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
2104 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
2105 ],
2106 69,
2107 91 + 16
2117 + 9 + 2
2119 + 3
2120 + 4
2121 + 5
2122 + 6
2123 + 7
2124 + 8
2125 + 9
2126 + 10
2127 + 11
2128 + 12
2129 + 13
2130 + 14
2131 + 15
2132 + 15
2133 + 15
2134 + 15
2135 + 16
2136 + 15
2137 + 16, ),
2139 (
2141 vec![
2142 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
2143 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
2144 ],
2145 100,
2146 327 + 16
2153 + 14 + 2
2155 + 3
2156 + 4
2157 + 5
2158 + 6
2159 + 7
2160 + 8
2161 + 9
2162 + 10
2163 + 11
2164 + 12
2165 + 13
2166 + 14
2167 + 15
2168 + 16
2169 + 16, ),
2171 (
2173 vec![115, 116, 117, 118, 119, 120, 121, 122, 123, 124],
2174 130,
2175 508 + ((74 - 69) + 1), ),
2180 (
2182 vec![200, 201],
2183 202,
2184 514,
2187 ),
2188 (
2189 vec![
2190 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
2191 218, 219, 220, 221, 222, 223, 224, 225, 226,
2192 ],
2193 227,
2194 514 + 9 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13, ),
2200 (
2201 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
2202 237,
2203 613 + 3 + 4 + 5 + 6 + 16 + 16 + 1 + 1 + 1 + 1, ),
2209 ];
2210
2211 let mut feature_set = FeatureSet::default();
2212 feature_set.activate(&feature_set::timely_vote_credits::id(), 1);
2213
2214 for i in 0..test_vote_groups.len() {
2217 let mut vote_state_1 = VoteState::new(&VoteInit::default(), &Clock::default());
2219 let mut vote_state_2 = VoteState::new(&VoteInit::default(), &Clock::default());
2221 test_vote_groups.iter().take(i + 1).for_each(|vote_group| {
2222 let vote = Vote {
2223 slots: vote_group.0.clone(), hash: Hash::new_unique(),
2225 timestamp: None,
2226 };
2227 let slot_hashes: Vec<_> =
2228 vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
2229 assert_eq!(
2230 process_vote(
2231 &mut vote_state_1,
2232 &vote,
2233 &slot_hashes,
2234 0,
2235 vote_group.1, true,
2237 ),
2238 Ok(())
2239 );
2240
2241 assert_eq!(
2242 process_new_vote_state(
2243 &mut vote_state_2,
2244 vote_state_1.votes.clone(),
2245 vote_state_1.root_slot,
2246 None,
2247 0,
2248 vote_group.1, Some(&feature_set)
2250 ),
2251 Ok(())
2252 );
2253 });
2254
2255 let vote_group = &test_vote_groups[i];
2257 assert_eq!(vote_state_1.credits(), vote_group.2 as u64); assert_eq!(vote_state_2.credits(), vote_group.2 as u64); }
2260 }
2261
2262 #[test]
2263 fn test_retroactive_voting_timely_credits() {
2264 #[allow(clippy::type_complexity)]
2270 let test_vote_state_updates: Vec<(Vec<(Slot, u32)>, Slot, Option<Slot>, u32)> = vec![
2271 (
2273 vec![(7, 4), (8, 3), (9, 2), (10, 1)],
2274 11,
2275 None,
2277 0,
2279 ),
2280 (
2282 vec![
2283 (1, 10),
2284 (2, 9),
2285 (3, 8),
2286 (4, 7),
2287 (5, 6),
2288 (6, 5),
2289 (7, 4),
2290 (8, 3),
2291 (9, 2),
2292 (10, 1),
2293 ],
2294 12,
2295 None,
2297 0,
2299 ),
2300 (
2302 vec![
2303 (11, 31),
2304 (12, 30),
2305 (13, 29),
2306 (14, 28),
2307 (15, 27),
2308 (16, 26),
2309 (17, 25),
2310 (18, 24),
2311 (19, 23),
2312 (20, 22),
2313 (21, 21),
2314 (22, 20),
2315 (23, 19),
2316 (24, 18),
2317 (25, 17),
2318 (26, 16),
2319 (27, 15),
2320 (28, 14),
2321 (29, 13),
2322 (30, 12),
2323 (31, 11),
2324 (32, 10),
2325 (33, 9),
2326 (34, 8),
2327 (35, 7),
2328 (36, 6),
2329 (37, 5),
2330 (38, 4),
2331 (39, 3),
2332 (40, 2),
2333 (41, 1),
2334 ],
2335 42,
2336 Some(10),
2338 7 + 8 + 9 + 10 + 11 + 12 + 14 + 15 + 16 + 16,
2341 ),
2342 ];
2343
2344 let mut feature_set = FeatureSet::default();
2345 feature_set.activate(&feature_set::timely_vote_credits::id(), 1);
2346
2347 let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
2349
2350 test_vote_state_updates
2353 .iter()
2354 .for_each(|proposed_vote_state| {
2355 let new_state = proposed_vote_state
2356 .0 .iter()
2358 .map(|(slot, confirmation_count)| LandedVote {
2359 latency: 0,
2360 lockout: Lockout::new_with_confirmation_count(*slot, *confirmation_count),
2361 })
2362 .collect::<VecDeque<LandedVote>>();
2363 assert_eq!(
2364 process_new_vote_state(
2365 &mut vote_state,
2366 new_state,
2367 proposed_vote_state.2, None,
2369 0,
2370 proposed_vote_state.1, Some(&feature_set)
2372 ),
2373 Ok(())
2374 );
2375
2376 assert_eq!(vote_state.credits(), proposed_vote_state.3 as u64);
2378 });
2379 }
2380
2381 #[test]
2382 fn test_process_new_vote_too_many_votes() {
2383 let mut vote_state1 = VoteState::default();
2384 let bad_votes: VecDeque<Lockout> = (0..=MAX_LOCKOUT_HISTORY)
2385 .map(|slot| {
2386 Lockout::new_with_confirmation_count(
2387 slot as Slot,
2388 (MAX_LOCKOUT_HISTORY - slot + 1) as u32,
2389 )
2390 })
2391 .collect();
2392
2393 let current_epoch = vote_state1.current_epoch();
2394 assert_eq!(
2395 process_new_vote_state_from_lockouts(
2396 &mut vote_state1,
2397 bad_votes,
2398 None,
2399 None,
2400 current_epoch,
2401 None
2402 ),
2403 Err(VoteError::TooManyVotes)
2404 );
2405 }
2406
2407 #[test]
2408 fn test_process_new_vote_state_root_rollback() {
2409 let mut vote_state1 = VoteState::default();
2410 for i in 0..MAX_LOCKOUT_HISTORY + 2 {
2411 process_slot_vote_unchecked(&mut vote_state1, i as Slot);
2412 }
2413 assert_eq!(vote_state1.root_slot.unwrap(), 1);
2414
2415 let mut vote_state2 = vote_state1.clone();
2418 process_slot_vote_unchecked(&mut vote_state2, MAX_LOCKOUT_HISTORY as Slot + 3);
2419
2420 let lesser_root = Some(0);
2422
2423 let current_epoch = vote_state2.current_epoch();
2424 assert_eq!(
2425 process_new_vote_state(
2426 &mut vote_state1,
2427 vote_state2.votes.clone(),
2428 lesser_root,
2429 None,
2430 current_epoch,
2431 0,
2432 None,
2433 ),
2434 Err(VoteError::RootRollBack)
2435 );
2436
2437 let none_root = None;
2439 assert_eq!(
2440 process_new_vote_state(
2441 &mut vote_state1,
2442 vote_state2.votes.clone(),
2443 none_root,
2444 None,
2445 current_epoch,
2446 0,
2447 None,
2448 ),
2449 Err(VoteError::RootRollBack)
2450 );
2451 }
2452
2453 fn process_new_vote_state_replaced_root_vote_credits(
2454 feature_set: &FeatureSet,
2455 expected_credits: u64,
2456 ) {
2457 let mut vote_state1 = VoteState::default();
2458
2459 assert_eq!(
2461 process_new_vote_state_from_lockouts(
2462 &mut vote_state1,
2463 (0..MAX_LOCKOUT_HISTORY)
2464 .enumerate()
2465 .map(|(index, slot)| Lockout::new_with_confirmation_count(
2466 slot as Slot,
2467 (MAX_LOCKOUT_HISTORY.checked_sub(index).unwrap()) as u32
2468 ))
2469 .collect(),
2470 None,
2471 None,
2472 0,
2473 Some(feature_set),
2474 ),
2475 Ok(())
2476 );
2477
2478 assert_eq!(
2480 process_new_vote_state_from_lockouts(
2481 &mut vote_state1,
2482 (2..(MAX_LOCKOUT_HISTORY.checked_add(2).unwrap()))
2483 .enumerate()
2484 .map(|(index, slot)| Lockout::new_with_confirmation_count(
2485 slot as Slot,
2486 (MAX_LOCKOUT_HISTORY.checked_sub(index).unwrap()) as u32
2487 ))
2488 .collect(),
2489 Some(1),
2490 None,
2491 0,
2492 Some(feature_set),
2493 ),
2494 Ok(())
2495 );
2496
2497 assert_eq!(vote_state1.credits(), 2);
2499
2500 assert_eq!(
2505 process_new_vote_state_from_lockouts(
2506 &mut vote_state1,
2507 (10001..(MAX_LOCKOUT_HISTORY.checked_add(10001).unwrap()))
2508 .enumerate()
2509 .map(|(index, slot)| Lockout::new_with_confirmation_count(
2510 slot as Slot,
2511 (MAX_LOCKOUT_HISTORY.checked_sub(index).unwrap()) as u32
2512 ))
2513 .collect(),
2514 Some(10000),
2515 None,
2516 0,
2517 Some(feature_set),
2518 ),
2519 Ok(())
2520 );
2521
2522 assert_eq!(vote_state1.credits(), expected_credits)
2525 }
2526
2527 #[test]
2528 fn test_process_new_vote_state_replaced_root_vote_credits() {
2529 let mut feature_set = FeatureSet::default();
2530
2531 feature_set.activate(&feature_set::vote_state_update_credit_per_dequeue::id(), 1);
2538 process_new_vote_state_replaced_root_vote_credits(&feature_set, 34);
2539
2540 feature_set.activate(&feature_set::timely_vote_credits::id(), 1);
2545 process_new_vote_state_replaced_root_vote_credits(&feature_set, 33);
2546 }
2547
2548 #[test]
2549 fn test_process_new_vote_state_zero_confirmations() {
2550 let mut vote_state1 = VoteState::default();
2551 let current_epoch = vote_state1.current_epoch();
2552
2553 let bad_votes: VecDeque<Lockout> = vec![
2554 Lockout::new_with_confirmation_count(0, 0),
2555 Lockout::new_with_confirmation_count(1, 1),
2556 ]
2557 .into_iter()
2558 .collect();
2559 assert_eq!(
2560 process_new_vote_state_from_lockouts(
2561 &mut vote_state1,
2562 bad_votes,
2563 None,
2564 None,
2565 current_epoch,
2566 None
2567 ),
2568 Err(VoteError::ZeroConfirmations)
2569 );
2570
2571 let bad_votes: VecDeque<Lockout> = vec![
2572 Lockout::new_with_confirmation_count(0, 2),
2573 Lockout::new_with_confirmation_count(1, 0),
2574 ]
2575 .into_iter()
2576 .collect();
2577 assert_eq!(
2578 process_new_vote_state_from_lockouts(
2579 &mut vote_state1,
2580 bad_votes,
2581 None,
2582 None,
2583 current_epoch,
2584 None
2585 ),
2586 Err(VoteError::ZeroConfirmations)
2587 );
2588 }
2589
2590 #[test]
2591 fn test_process_new_vote_state_confirmations_too_large() {
2592 let mut vote_state1 = VoteState::default();
2593 let current_epoch = vote_state1.current_epoch();
2594
2595 let good_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2596 0,
2597 MAX_LOCKOUT_HISTORY as u32,
2598 )]
2599 .into_iter()
2600 .collect();
2601
2602 process_new_vote_state_from_lockouts(
2603 &mut vote_state1,
2604 good_votes,
2605 None,
2606 None,
2607 current_epoch,
2608 None,
2609 )
2610 .unwrap();
2611
2612 let mut vote_state1 = VoteState::default();
2613 let bad_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2614 0,
2615 MAX_LOCKOUT_HISTORY as u32 + 1,
2616 )]
2617 .into_iter()
2618 .collect();
2619 assert_eq!(
2620 process_new_vote_state_from_lockouts(
2621 &mut vote_state1,
2622 bad_votes,
2623 None,
2624 None,
2625 current_epoch,
2626 None
2627 ),
2628 Err(VoteError::ConfirmationTooLarge)
2629 );
2630 }
2631
2632 #[test]
2633 fn test_process_new_vote_state_slot_smaller_than_root() {
2634 let mut vote_state1 = VoteState::default();
2635 let current_epoch = vote_state1.current_epoch();
2636 let root_slot = 5;
2637
2638 let bad_votes: VecDeque<Lockout> = vec![
2639 Lockout::new_with_confirmation_count(root_slot, 2),
2640 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2641 ]
2642 .into_iter()
2643 .collect();
2644 assert_eq!(
2645 process_new_vote_state_from_lockouts(
2646 &mut vote_state1,
2647 bad_votes,
2648 Some(root_slot),
2649 None,
2650 current_epoch,
2651 None,
2652 ),
2653 Err(VoteError::SlotSmallerThanRoot)
2654 );
2655
2656 let bad_votes: VecDeque<Lockout> = vec![
2657 Lockout::new_with_confirmation_count(root_slot - 1, 2),
2658 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2659 ]
2660 .into_iter()
2661 .collect();
2662 assert_eq!(
2663 process_new_vote_state_from_lockouts(
2664 &mut vote_state1,
2665 bad_votes,
2666 Some(root_slot),
2667 None,
2668 current_epoch,
2669 None,
2670 ),
2671 Err(VoteError::SlotSmallerThanRoot)
2672 );
2673 }
2674
2675 #[test]
2676 fn test_process_new_vote_state_slots_not_ordered() {
2677 let mut vote_state1 = VoteState::default();
2678 let current_epoch = vote_state1.current_epoch();
2679
2680 let bad_votes: VecDeque<Lockout> = vec![
2681 Lockout::new_with_confirmation_count(1, 2),
2682 Lockout::new_with_confirmation_count(0, 1),
2683 ]
2684 .into_iter()
2685 .collect();
2686 assert_eq!(
2687 process_new_vote_state_from_lockouts(
2688 &mut vote_state1,
2689 bad_votes,
2690 None,
2691 None,
2692 current_epoch,
2693 None
2694 ),
2695 Err(VoteError::SlotsNotOrdered)
2696 );
2697
2698 let bad_votes: VecDeque<Lockout> = vec![
2699 Lockout::new_with_confirmation_count(1, 2),
2700 Lockout::new_with_confirmation_count(1, 1),
2701 ]
2702 .into_iter()
2703 .collect();
2704 assert_eq!(
2705 process_new_vote_state_from_lockouts(
2706 &mut vote_state1,
2707 bad_votes,
2708 None,
2709 None,
2710 current_epoch,
2711 None
2712 ),
2713 Err(VoteError::SlotsNotOrdered)
2714 );
2715 }
2716
2717 #[test]
2718 fn test_process_new_vote_state_confirmations_not_ordered() {
2719 let mut vote_state1 = VoteState::default();
2720 let current_epoch = vote_state1.current_epoch();
2721
2722 let bad_votes: VecDeque<Lockout> = vec![
2723 Lockout::new_with_confirmation_count(0, 1),
2724 Lockout::new_with_confirmation_count(1, 2),
2725 ]
2726 .into_iter()
2727 .collect();
2728 assert_eq!(
2729 process_new_vote_state_from_lockouts(
2730 &mut vote_state1,
2731 bad_votes,
2732 None,
2733 None,
2734 current_epoch,
2735 None
2736 ),
2737 Err(VoteError::ConfirmationsNotOrdered)
2738 );
2739
2740 let bad_votes: VecDeque<Lockout> = vec![
2741 Lockout::new_with_confirmation_count(0, 1),
2742 Lockout::new_with_confirmation_count(1, 1),
2743 ]
2744 .into_iter()
2745 .collect();
2746 assert_eq!(
2747 process_new_vote_state_from_lockouts(
2748 &mut vote_state1,
2749 bad_votes,
2750 None,
2751 None,
2752 current_epoch,
2753 None
2754 ),
2755 Err(VoteError::ConfirmationsNotOrdered)
2756 );
2757 }
2758
2759 #[test]
2760 fn test_process_new_vote_state_new_vote_state_lockout_mismatch() {
2761 let mut vote_state1 = VoteState::default();
2762 let current_epoch = vote_state1.current_epoch();
2763
2764 let bad_votes: VecDeque<Lockout> = vec![
2765 Lockout::new_with_confirmation_count(0, 2),
2766 Lockout::new_with_confirmation_count(7, 1),
2767 ]
2768 .into_iter()
2769 .collect();
2770
2771 assert_eq!(
2773 process_new_vote_state_from_lockouts(
2774 &mut vote_state1,
2775 bad_votes,
2776 None,
2777 None,
2778 current_epoch,
2779 None
2780 ),
2781 Err(VoteError::NewVoteStateLockoutMismatch)
2782 );
2783 }
2784
2785 #[test]
2786 fn test_process_new_vote_state_confirmation_rollback() {
2787 let mut vote_state1 = VoteState::default();
2788 let current_epoch = vote_state1.current_epoch();
2789 let votes: VecDeque<Lockout> = vec![
2790 Lockout::new_with_confirmation_count(0, 4),
2791 Lockout::new_with_confirmation_count(1, 3),
2792 ]
2793 .into_iter()
2794 .collect();
2795 process_new_vote_state_from_lockouts(
2796 &mut vote_state1,
2797 votes,
2798 None,
2799 None,
2800 current_epoch,
2801 None,
2802 )
2803 .unwrap();
2804
2805 let votes: VecDeque<Lockout> = vec![
2806 Lockout::new_with_confirmation_count(0, 4),
2807 Lockout::new_with_confirmation_count(1, 2),
2809 Lockout::new_with_confirmation_count(2, 1),
2810 ]
2811 .into_iter()
2812 .collect();
2813 assert_eq!(
2816 process_new_vote_state_from_lockouts(
2817 &mut vote_state1,
2818 votes,
2819 None,
2820 None,
2821 current_epoch,
2822 None
2823 ),
2824 Err(VoteError::ConfirmationRollBack)
2825 );
2826 }
2827
2828 #[test]
2829 fn test_process_new_vote_state_root_progress() {
2830 let mut vote_state1 = VoteState::default();
2831 for i in 0..MAX_LOCKOUT_HISTORY {
2832 process_slot_vote_unchecked(&mut vote_state1, i as u64);
2833 }
2834
2835 assert!(vote_state1.root_slot.is_none());
2836 let mut vote_state2 = vote_state1.clone();
2837
2838 for new_vote in MAX_LOCKOUT_HISTORY + 1..=MAX_LOCKOUT_HISTORY + 2 {
2845 process_slot_vote_unchecked(&mut vote_state2, new_vote as Slot);
2846 assert_ne!(vote_state1.root_slot, vote_state2.root_slot);
2847
2848 process_new_vote_state(
2849 &mut vote_state1,
2850 vote_state2.votes.clone(),
2851 vote_state2.root_slot,
2852 None,
2853 vote_state2.current_epoch(),
2854 0,
2855 None,
2856 )
2857 .unwrap();
2858
2859 assert_eq!(vote_state1, vote_state2);
2860 }
2861 }
2862
2863 #[test]
2864 fn test_process_new_vote_state_same_slot_but_not_common_ancestor() {
2865 let mut vote_state1 = VoteState::default();
2884 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5]);
2885 assert_eq!(
2886 vote_state1
2887 .votes
2888 .iter()
2889 .map(|vote| vote.slot())
2890 .collect::<Vec<Slot>>(),
2891 vec![1, 5]
2892 );
2893
2894 let mut vote_state2 = VoteState::default();
2896 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2897 assert_eq!(
2898 vote_state2
2899 .votes
2900 .iter()
2901 .map(|vote| vote.slot())
2902 .collect::<Vec<Slot>>(),
2903 vec![1, 2, 3, 5, 7]
2904 );
2905
2906 process_new_vote_state(
2908 &mut vote_state1,
2909 vote_state2.votes.clone(),
2910 vote_state2.root_slot,
2911 None,
2912 vote_state2.current_epoch(),
2913 0,
2914 None,
2915 )
2916 .unwrap();
2917
2918 assert_eq!(vote_state1, vote_state2);
2919 }
2920
2921 #[test]
2922 fn test_process_new_vote_state_lockout_violation() {
2923 let mut vote_state1 = VoteState::default();
2925 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 4, 5]);
2926 assert_eq!(
2927 vote_state1
2928 .votes
2929 .iter()
2930 .map(|vote| vote.slot())
2931 .collect::<Vec<Slot>>(),
2932 vec![1, 2, 4, 5]
2933 );
2934
2935 let mut vote_state2 = VoteState::default();
2938 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2939 assert_eq!(
2940 vote_state2
2941 .votes
2942 .iter()
2943 .map(|vote| vote.slot())
2944 .collect::<Vec<Slot>>(),
2945 vec![1, 2, 3, 5, 7]
2946 );
2947
2948 assert_eq!(
2950 process_new_vote_state(
2951 &mut vote_state1,
2952 vote_state2.votes.clone(),
2953 vote_state2.root_slot,
2954 None,
2955 vote_state2.current_epoch(),
2956 0,
2957 None
2958 ),
2959 Err(VoteError::LockoutConflict)
2960 );
2961 }
2962
2963 #[test]
2964 fn test_process_new_vote_state_lockout_violation2() {
2965 let mut vote_state1 = VoteState::default();
2967 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5, 6, 7]);
2968 assert_eq!(
2969 vote_state1
2970 .votes
2971 .iter()
2972 .map(|vote| vote.slot())
2973 .collect::<Vec<Slot>>(),
2974 vec![1, 5, 6, 7]
2975 );
2976
2977 let mut vote_state2 = VoteState::default();
2980 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 6, 8]);
2981 assert_eq!(
2982 vote_state2
2983 .votes
2984 .iter()
2985 .map(|vote| vote.slot())
2986 .collect::<Vec<Slot>>(),
2987 vec![1, 2, 3, 5, 6, 8]
2988 );
2989
2990 assert_eq!(
2993 process_new_vote_state(
2994 &mut vote_state1,
2995 vote_state2.votes.clone(),
2996 vote_state2.root_slot,
2997 None,
2998 vote_state2.current_epoch(),
2999 0,
3000 None
3001 ),
3002 Err(VoteError::LockoutConflict)
3003 );
3004 }
3005
3006 #[test]
3007 fn test_process_new_vote_state_expired_ancestor_not_removed() {
3008 let mut vote_state1 = VoteState::default();
3010 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 3, 9]);
3011 assert_eq!(
3012 vote_state1
3013 .votes
3014 .iter()
3015 .map(|vote| vote.slot())
3016 .collect::<Vec<Slot>>(),
3017 vec![1, 9]
3018 );
3019
3020 let mut vote_state2 = vote_state1.clone();
3023 process_slot_vote_unchecked(&mut vote_state2, 10);
3024
3025 assert_eq!(vote_state2.votes[0].slot(), 1);
3028 assert_eq!(vote_state2.votes[0].lockout.last_locked_out_slot(), 9);
3029 assert_eq!(
3030 vote_state2
3031 .votes
3032 .iter()
3033 .map(|vote| vote.slot())
3034 .collect::<Vec<Slot>>(),
3035 vec![1, 9, 10]
3036 );
3037
3038 process_new_vote_state(
3040 &mut vote_state1,
3041 vote_state2.votes.clone(),
3042 vote_state2.root_slot,
3043 None,
3044 vote_state2.current_epoch(),
3045 0,
3046 None,
3047 )
3048 .unwrap();
3049 assert_eq!(vote_state1, vote_state2,);
3050 }
3051
3052 #[test]
3053 fn test_process_new_vote_current_state_contains_bigger_slots() {
3054 let mut vote_state1 = VoteState::default();
3055 process_slot_votes_unchecked(&mut vote_state1, &[6, 7, 8]);
3056 assert_eq!(
3057 vote_state1
3058 .votes
3059 .iter()
3060 .map(|vote| vote.slot())
3061 .collect::<Vec<Slot>>(),
3062 vec![6, 7, 8]
3063 );
3064
3065 let bad_votes: VecDeque<Lockout> = vec![
3067 Lockout::new_with_confirmation_count(2, 5),
3068 Lockout::new_with_confirmation_count(14, 1),
3070 ]
3071 .into_iter()
3072 .collect();
3073 let root = Some(1);
3074
3075 let current_epoch = vote_state1.current_epoch();
3076 assert_eq!(
3077 process_new_vote_state_from_lockouts(
3078 &mut vote_state1,
3079 bad_votes,
3080 root,
3081 None,
3082 current_epoch,
3083 None
3084 ),
3085 Err(VoteError::LockoutConflict)
3086 );
3087
3088 let good_votes: VecDeque<LandedVote> = vec![
3089 Lockout::new_with_confirmation_count(2, 5).into(),
3090 Lockout::new_with_confirmation_count(15, 1).into(),
3091 ]
3092 .into_iter()
3093 .collect();
3094
3095 let current_epoch = vote_state1.current_epoch();
3096 process_new_vote_state(
3097 &mut vote_state1,
3098 good_votes.clone(),
3099 root,
3100 None,
3101 current_epoch,
3102 0,
3103 None,
3104 )
3105 .unwrap();
3106 assert_eq!(vote_state1.votes, good_votes);
3107 }
3108
3109 #[test]
3110 fn test_filter_old_votes() {
3111 let mut vote_state = VoteState::default();
3112 let old_vote_slot = 1;
3113 let vote = Vote::new(vec![old_vote_slot], Hash::default());
3114
3115 let slot_hashes = vec![(3, Hash::new_unique()), (2, Hash::new_unique())];
3118 assert_eq!(
3119 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
3120 Err(VoteError::VotesTooOldAllFiltered)
3121 );
3122
3123 let vote_slot = 2;
3126 let vote_slot_hash = slot_hashes
3127 .iter()
3128 .find(|(slot, _hash)| *slot == vote_slot)
3129 .unwrap()
3130 .1;
3131
3132 let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
3133 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true).unwrap();
3134 assert_eq!(
3135 vote_state
3136 .votes
3137 .into_iter()
3138 .map(|vote| vote.lockout)
3139 .collect::<Vec<Lockout>>(),
3140 vec![Lockout::new_with_confirmation_count(vote_slot, 1)]
3141 );
3142 }
3143
3144 fn build_slot_hashes(slots: Vec<Slot>) -> Vec<(Slot, Hash)> {
3145 slots
3146 .iter()
3147 .rev()
3148 .map(|x| (*x, Hash::new_unique()))
3149 .collect()
3150 }
3151
3152 fn build_vote_state(vote_slots: Vec<Slot>, slot_hashes: &[(Slot, Hash)]) -> VoteState {
3153 let mut vote_state = VoteState::default();
3154
3155 if !vote_slots.is_empty() {
3156 let vote_hash = slot_hashes
3157 .iter()
3158 .find(|(slot, _hash)| slot == vote_slots.last().unwrap())
3159 .unwrap()
3160 .1;
3161 let vote = Vote::new(vote_slots, vote_hash);
3162 process_vote_unfiltered(&mut vote_state, &vote.slots, &vote, slot_hashes, 0, 0, true)
3163 .unwrap();
3164 }
3165
3166 vote_state
3167 }
3168
3169 #[test]
3170 fn test_check_and_filter_proposed_vote_state_empty() {
3171 let empty_slot_hashes = build_slot_hashes(vec![]);
3172 let empty_vote_state = build_vote_state(vec![], &empty_slot_hashes);
3173
3174 let mut tower_sync = TowerSync::from(vec![]);
3176 assert_eq!(
3177 check_and_filter_proposed_vote_state(
3178 &empty_vote_state,
3179 &mut tower_sync.lockouts,
3180 &mut tower_sync.root,
3181 tower_sync.hash,
3182 &empty_slot_hashes
3183 ),
3184 Err(VoteError::EmptySlots),
3185 );
3186
3187 let mut tower_sync = TowerSync::from(vec![(0, 1)]);
3189 assert_eq!(
3190 check_and_filter_proposed_vote_state(
3191 &empty_vote_state,
3192 &mut tower_sync.lockouts,
3193 &mut tower_sync.root,
3194 tower_sync.hash,
3195 &empty_slot_hashes
3196 ),
3197 Err(VoteError::SlotsMismatch),
3198 );
3199 }
3200
3201 #[test]
3202 fn test_check_and_filter_proposed_vote_state_too_old() {
3203 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3204 let latest_vote = 4;
3205 let vote_state = build_vote_state(vec![1, 2, 3, latest_vote], &slot_hashes);
3206
3207 let mut tower_sync = TowerSync::from(vec![(latest_vote, 1)]);
3210 assert_eq!(
3211 check_and_filter_proposed_vote_state(
3212 &vote_state,
3213 &mut tower_sync.lockouts,
3214 &mut tower_sync.root,
3215 tower_sync.hash,
3216 &slot_hashes
3217 ),
3218 Err(VoteError::VoteTooOld),
3219 );
3220
3221 let earliest_slot_in_history = latest_vote + 2;
3225 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history]);
3226 let mut tower_sync = TowerSync::from(vec![(earliest_slot_in_history - 1, 1)]);
3227 assert_eq!(
3228 check_and_filter_proposed_vote_state(
3229 &vote_state,
3230 &mut tower_sync.lockouts,
3231 &mut tower_sync.root,
3232 tower_sync.hash,
3233 &slot_hashes
3234 ),
3235 Err(VoteError::VoteTooOld),
3236 );
3237 }
3238
3239 fn run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3240 earliest_slot_in_history: Slot,
3241 current_vote_state_slots: Vec<Slot>,
3242 current_vote_state_root: Option<Slot>,
3243 proposed_slots_and_lockouts: Vec<(Slot, u32)>,
3244 proposed_root: Slot,
3245 expected_root: Option<Slot>,
3246 expected_vote_state: Vec<Lockout>,
3247 ) {
3248 assert!(proposed_root < earliest_slot_in_history);
3249 assert_eq!(
3250 expected_root,
3251 current_vote_state_slots
3252 .iter()
3253 .rev()
3254 .find(|slot| **slot <= proposed_root)
3255 .cloned()
3256 );
3257 let latest_slot_in_history = proposed_slots_and_lockouts
3258 .last()
3259 .unwrap()
3260 .0
3261 .max(earliest_slot_in_history);
3262 let mut slot_hashes = build_slot_hashes(
3263 (current_vote_state_slots.first().copied().unwrap_or(0)..=latest_slot_in_history)
3264 .collect::<Vec<Slot>>(),
3265 );
3266
3267 let mut vote_state = build_vote_state(current_vote_state_slots, &slot_hashes);
3268 vote_state.root_slot = current_vote_state_root;
3269
3270 slot_hashes.retain(|slot| slot.0 >= earliest_slot_in_history);
3271 assert!(!proposed_slots_and_lockouts.is_empty());
3272 let proposed_hash = slot_hashes
3273 .iter()
3274 .find(|(slot, _hash)| *slot == proposed_slots_and_lockouts.last().unwrap().0)
3275 .unwrap()
3276 .1;
3277
3278 let mut tower_sync = TowerSync::from(proposed_slots_and_lockouts);
3282 tower_sync.hash = proposed_hash;
3283 tower_sync.root = Some(proposed_root);
3284 check_and_filter_proposed_vote_state(
3285 &vote_state,
3286 &mut tower_sync.lockouts,
3287 &mut tower_sync.root,
3288 tower_sync.hash,
3289 &slot_hashes,
3290 )
3291 .unwrap();
3292 assert_eq!(tower_sync.root, expected_root);
3293
3294 assert!(do_process_tower_sync(
3297 &mut vote_state,
3298 &slot_hashes,
3299 0,
3300 0,
3301 tower_sync.clone(),
3302 Some(&FeatureSet::all_enabled()),
3303 )
3304 .is_ok());
3305 assert_eq!(vote_state.root_slot, expected_root);
3306 assert_eq!(
3307 vote_state
3308 .votes
3309 .into_iter()
3310 .map(|vote| vote.lockout)
3311 .collect::<Vec<Lockout>>(),
3312 expected_vote_state,
3313 );
3314 }
3315
3316 #[test]
3317 fn test_check_and_filter_proposed_vote_state_older_than_history_root() {
3318 let earliest_slot_in_history = 5;
3321 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3322 let current_vote_state_root = None;
3323 let proposed_slots_and_lockouts = vec![(5, 1)];
3324 let proposed_root = 4;
3325 let expected_root = Some(4);
3326 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3327 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3328 earliest_slot_in_history,
3329 current_vote_state_slots,
3330 current_vote_state_root,
3331 proposed_slots_and_lockouts,
3332 proposed_root,
3333 expected_root,
3334 expected_vote_state,
3335 );
3336
3337 let earliest_slot_in_history = 5;
3340 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3341 let current_vote_state_root = Some(0);
3342 let proposed_slots_and_lockouts = vec![(5, 1)];
3343 let proposed_root = 4;
3344 let expected_root = Some(4);
3345 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3346 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3347 earliest_slot_in_history,
3348 current_vote_state_slots,
3349 current_vote_state_root,
3350 proposed_slots_and_lockouts,
3351 proposed_root,
3352 expected_root,
3353 expected_vote_state,
3354 );
3355
3356 let earliest_slot_in_history = 5;
3359 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3360 let current_vote_state_root = Some(0);
3361 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3362 let proposed_root = 3;
3363 let expected_root = Some(3);
3364 let expected_vote_state = vec![
3365 Lockout::new_with_confirmation_count(4, 2),
3366 Lockout::new_with_confirmation_count(5, 1),
3367 ];
3368 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3369 earliest_slot_in_history,
3370 current_vote_state_slots,
3371 current_vote_state_root,
3372 proposed_slots_and_lockouts,
3373 proposed_root,
3374 expected_root,
3375 expected_vote_state,
3376 );
3377
3378 let earliest_slot_in_history = 5;
3380 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 4];
3381 let current_vote_state_root = Some(0);
3382 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3383 let proposed_root = 3;
3384 let expected_root = Some(2);
3385 let expected_vote_state = vec![
3386 Lockout::new_with_confirmation_count(4, 2),
3387 Lockout::new_with_confirmation_count(5, 1),
3388 ];
3389 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3390 earliest_slot_in_history,
3391 current_vote_state_slots,
3392 current_vote_state_root,
3393 proposed_slots_and_lockouts,
3394 proposed_root,
3395 expected_root,
3396 expected_vote_state,
3397 );
3398
3399 let earliest_slot_in_history = 4;
3402 let current_vote_state_slots: Vec<Slot> = vec![3, 4];
3403 let current_vote_state_root = None;
3404 let proposed_slots_and_lockouts = vec![(3, 3), (4, 2), (5, 1)];
3405 let proposed_root = 2;
3406 let expected_root = None;
3407 let expected_vote_state = vec![
3408 Lockout::new_with_confirmation_count(3, 3),
3409 Lockout::new_with_confirmation_count(4, 2),
3410 Lockout::new_with_confirmation_count(5, 1),
3411 ];
3412 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3413 earliest_slot_in_history,
3414 current_vote_state_slots,
3415 current_vote_state_root,
3416 proposed_slots_and_lockouts,
3417 proposed_root,
3418 expected_root,
3419 expected_vote_state,
3420 );
3421
3422 let earliest_slot_in_history = 4;
3424 let current_vote_state_slots: Vec<Slot> = vec![];
3425 let current_vote_state_root = None;
3426 let proposed_slots_and_lockouts = vec![(5, 1)];
3427 let proposed_root = 2;
3428 let expected_root = None;
3429 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3430 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3431 earliest_slot_in_history,
3432 current_vote_state_slots,
3433 current_vote_state_root,
3434 proposed_slots_and_lockouts,
3435 proposed_root,
3436 expected_root,
3437 expected_vote_state,
3438 );
3439 }
3440
3441 #[test]
3442 fn test_check_and_filter_proposed_vote_state_slots_not_ordered() {
3443 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3444 let vote_state = build_vote_state(vec![1], &slot_hashes);
3445
3446 let vote_slot = 3;
3448 let vote_slot_hash = slot_hashes
3449 .iter()
3450 .find(|(slot, _hash)| *slot == vote_slot)
3451 .unwrap()
3452 .1;
3453 let mut tower_sync = TowerSync::from(vec![(2, 2), (1, 3), (vote_slot, 1)]);
3454 tower_sync.hash = vote_slot_hash;
3455 assert_eq!(
3456 check_and_filter_proposed_vote_state(
3457 &vote_state,
3458 &mut tower_sync.lockouts,
3459 &mut tower_sync.root,
3460 tower_sync.hash,
3461 &slot_hashes
3462 ),
3463 Err(VoteError::SlotsNotOrdered),
3464 );
3465
3466 let mut tower_sync = TowerSync::from(vec![(2, 2), (2, 2), (vote_slot, 1)]);
3468 tower_sync.hash = vote_slot_hash;
3469 assert_eq!(
3470 check_and_filter_proposed_vote_state(
3471 &vote_state,
3472 &mut tower_sync.lockouts,
3473 &mut tower_sync.root,
3474 tower_sync.hash,
3475 &slot_hashes
3476 ),
3477 Err(VoteError::SlotsNotOrdered),
3478 );
3479 }
3480
3481 #[test]
3482 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered() {
3483 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3484 let mut vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes);
3485
3486 let earliest_slot_in_history = 11;
3491 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3492 let vote_slot = 12;
3493 let vote_slot_hash = slot_hashes
3494 .iter()
3495 .find(|(slot, _hash)| *slot == vote_slot)
3496 .unwrap()
3497 .1;
3498 let missing_older_than_history_slot = earliest_slot_in_history - 1;
3499 let mut tower_sync = TowerSync::from(vec![
3500 (1, 4),
3501 (missing_older_than_history_slot, 2),
3502 (vote_slot, 3),
3503 ]);
3504 tower_sync.hash = vote_slot_hash;
3505 check_and_filter_proposed_vote_state(
3506 &vote_state,
3507 &mut tower_sync.lockouts,
3508 &mut tower_sync.root,
3509 tower_sync.hash,
3510 &slot_hashes,
3511 )
3512 .unwrap();
3513
3514 assert_eq!(
3516 tower_sync
3517 .clone()
3518 .lockouts
3519 .into_iter()
3520 .collect::<Vec<Lockout>>(),
3521 vec![
3522 Lockout::new_with_confirmation_count(1, 4),
3523 Lockout::new_with_confirmation_count(vote_slot, 3)
3524 ]
3525 );
3526 assert!(do_process_tower_sync(
3527 &mut vote_state,
3528 &slot_hashes,
3529 0,
3530 0,
3531 tower_sync,
3532 Some(&FeatureSet::all_enabled()),
3533 )
3534 .is_ok());
3535 }
3536
3537 #[test]
3538 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_not_filtered() {
3539 let slot_hashes = build_slot_hashes(vec![4]);
3540 let mut vote_state = build_vote_state(vec![4], &slot_hashes);
3541
3542 let earliest_slot_in_history = 11;
3547 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3548 let vote_slot = 12;
3549 let vote_slot_hash = slot_hashes
3550 .iter()
3551 .find(|(slot, _hash)| *slot == vote_slot)
3552 .unwrap()
3553 .1;
3554 let existing_older_than_history_slot = 4;
3555 let mut tower_sync =
3556 TowerSync::from(vec![(existing_older_than_history_slot, 3), (vote_slot, 2)]);
3557 tower_sync.hash = vote_slot_hash;
3558 check_and_filter_proposed_vote_state(
3559 &vote_state,
3560 &mut tower_sync.lockouts,
3561 &mut tower_sync.root,
3562 tower_sync.hash,
3563 &slot_hashes,
3564 )
3565 .unwrap();
3566 assert_eq!(tower_sync.lockouts.len(), 2);
3568 assert_eq!(
3569 tower_sync
3570 .clone()
3571 .lockouts
3572 .into_iter()
3573 .collect::<Vec<Lockout>>(),
3574 vec![
3575 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3576 Lockout::new_with_confirmation_count(vote_slot, 2)
3577 ]
3578 );
3579 assert!(do_process_tower_sync(
3580 &mut vote_state,
3581 &slot_hashes,
3582 0,
3583 0,
3584 tower_sync,
3585 Some(&FeatureSet::all_enabled()),
3586 )
3587 .is_ok());
3588 }
3589
3590 #[test]
3591 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered_and_not_filtered(
3592 ) {
3593 let slot_hashes = build_slot_hashes(vec![6]);
3594 let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3595
3596 let earliest_slot_in_history = 11;
3607 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3608 let vote_slot = 14;
3609 let vote_slot_hash = slot_hashes
3610 .iter()
3611 .find(|(slot, _hash)| *slot == vote_slot)
3612 .unwrap()
3613 .1;
3614
3615 let missing_older_than_history_slot = 4;
3616 let existing_older_than_history_slot = 6;
3617
3618 let mut tower_sync = TowerSync::from(vec![
3619 (missing_older_than_history_slot, 4),
3620 (existing_older_than_history_slot, 3),
3621 (12, 2),
3622 (vote_slot, 1),
3623 ]);
3624 tower_sync.hash = vote_slot_hash;
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 .unwrap();
3633 assert_eq!(tower_sync.lockouts.len(), 3);
3634 assert_eq!(
3635 tower_sync
3636 .clone()
3637 .lockouts
3638 .into_iter()
3639 .collect::<Vec<Lockout>>(),
3640 vec![
3641 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3642 Lockout::new_with_confirmation_count(12, 2),
3643 Lockout::new_with_confirmation_count(vote_slot, 1)
3644 ]
3645 );
3646 assert!(do_process_tower_sync(
3647 &mut vote_state,
3648 &slot_hashes,
3649 0,
3650 0,
3651 tower_sync,
3652 Some(&FeatureSet::all_enabled()),
3653 )
3654 .is_ok());
3655 }
3656
3657 #[test]
3658 fn test_check_and_filter_proposed_vote_state_slot_not_on_fork() {
3659 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3660 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3661
3662 let missing_vote_slot = 3;
3668
3669 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3672 let vote_slot_hash = slot_hashes
3673 .iter()
3674 .find(|(slot, _hash)| *slot == vote_slot)
3675 .unwrap()
3676 .1;
3677 let mut tower_sync = TowerSync::from(vec![(missing_vote_slot, 2), (vote_slot, 3)]);
3678 tower_sync.hash = vote_slot_hash;
3679 assert_eq!(
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 Err(VoteError::SlotsMismatch),
3688 );
3689
3690 let missing_vote_slot = 7;
3692 let mut tower_sync = TowerSync::from(vec![
3693 (2, 5),
3694 (4, 4),
3695 (6, 3),
3696 (missing_vote_slot, 2),
3697 (vote_slot, 1),
3698 ]);
3699 tower_sync.hash = vote_slot_hash;
3700 assert_eq!(
3701 check_and_filter_proposed_vote_state(
3702 &vote_state,
3703 &mut tower_sync.lockouts,
3704 &mut tower_sync.root,
3705 tower_sync.hash,
3706 &slot_hashes
3707 ),
3708 Err(VoteError::SlotsMismatch),
3709 );
3710 }
3711
3712 #[test]
3713 fn test_check_and_filter_proposed_vote_state_root_on_different_fork() {
3714 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3715 let vote_state = build_vote_state(vec![6], &slot_hashes);
3716
3717 let new_root = 3;
3723
3724 let vote_slot = 8;
3727 assert_eq!(vote_slot, slot_hashes.first().unwrap().0);
3728 let vote_slot_hash = slot_hashes
3729 .iter()
3730 .find(|(slot, _hash)| *slot == vote_slot)
3731 .unwrap()
3732 .1;
3733 let mut tower_sync = TowerSync::from(vec![(vote_slot, 1)]);
3734 tower_sync.hash = vote_slot_hash;
3735 tower_sync.root = Some(new_root);
3736 assert_eq!(
3737 check_and_filter_proposed_vote_state(
3738 &vote_state,
3739 &mut tower_sync.lockouts,
3740 &mut tower_sync.root,
3741 tower_sync.hash,
3742 &slot_hashes
3743 ),
3744 Err(VoteError::RootOnDifferentFork),
3745 );
3746 }
3747
3748 #[test]
3749 fn test_check_and_filter_proposed_vote_state_slot_newer_than_slot_history() {
3750 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3751 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3752
3753 let missing_vote_slot = slot_hashes.first().unwrap().0 + 1;
3759 let vote_slot_hash = Hash::new_unique();
3760 let mut tower_sync = TowerSync::from(vec![(8, 2), (missing_vote_slot, 3)]);
3761 tower_sync.hash = vote_slot_hash;
3762 assert_eq!(
3763 check_and_filter_proposed_vote_state(
3764 &vote_state,
3765 &mut tower_sync.lockouts,
3766 &mut tower_sync.root,
3767 tower_sync.hash,
3768 &slot_hashes
3769 ),
3770 Err(VoteError::SlotsMismatch),
3771 );
3772 }
3773
3774 #[test]
3775 fn test_check_and_filter_proposed_vote_state_slot_all_slot_hashes_in_update_ok() {
3776 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3777 let mut vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3778
3779 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3785 let vote_slot_hash = slot_hashes
3786 .iter()
3787 .find(|(slot, _hash)| *slot == vote_slot)
3788 .unwrap()
3789 .1;
3790 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3791 tower_sync.hash = vote_slot_hash;
3792 check_and_filter_proposed_vote_state(
3793 &vote_state,
3794 &mut tower_sync.lockouts,
3795 &mut tower_sync.root,
3796 tower_sync.hash,
3797 &slot_hashes,
3798 )
3799 .unwrap();
3800
3801 assert_eq!(
3803 tower_sync
3804 .clone()
3805 .lockouts
3806 .into_iter()
3807 .collect::<Vec<Lockout>>(),
3808 vec![
3809 Lockout::new_with_confirmation_count(2, 4),
3810 Lockout::new_with_confirmation_count(4, 3),
3811 Lockout::new_with_confirmation_count(6, 2),
3812 Lockout::new_with_confirmation_count(vote_slot, 1)
3813 ]
3814 );
3815
3816 assert!(do_process_tower_sync(
3817 &mut vote_state,
3818 &slot_hashes,
3819 0,
3820 0,
3821 tower_sync,
3822 Some(&FeatureSet::all_enabled()),
3823 )
3824 .is_ok());
3825 }
3826
3827 #[test]
3828 fn test_check_and_filter_proposed_vote_state_slot_some_slot_hashes_in_update_ok() {
3829 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3830 let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3831
3832 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3838 let vote_slot_hash = slot_hashes
3839 .iter()
3840 .find(|(slot, _hash)| *slot == vote_slot)
3841 .unwrap()
3842 .1;
3843 let mut tower_sync = TowerSync::from(vec![(4, 2), (vote_slot, 1)]);
3844 tower_sync.hash = vote_slot_hash;
3845 check_and_filter_proposed_vote_state(
3846 &vote_state,
3847 &mut tower_sync.lockouts,
3848 &mut tower_sync.root,
3849 tower_sync.hash,
3850 &slot_hashes,
3851 )
3852 .unwrap();
3853
3854 assert_eq!(
3856 tower_sync
3857 .clone()
3858 .lockouts
3859 .into_iter()
3860 .collect::<Vec<Lockout>>(),
3861 vec![
3862 Lockout::new_with_confirmation_count(4, 2),
3863 Lockout::new_with_confirmation_count(vote_slot, 1)
3864 ]
3865 );
3866
3867 assert_eq!(
3871 do_process_tower_sync(
3872 &mut vote_state,
3873 &slot_hashes,
3874 0,
3875 0,
3876 tower_sync,
3877 Some(&FeatureSet::all_enabled())
3878 ),
3879 Err(VoteError::LockoutConflict)
3880 );
3881 }
3882
3883 #[test]
3884 fn test_check_and_filter_proposed_vote_state_slot_hash_mismatch() {
3885 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3886 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3887
3888 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3893 let vote_slot_hash = Hash::new_unique();
3894 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3895 tower_sync.hash = vote_slot_hash;
3896 assert_eq!(
3897 check_and_filter_proposed_vote_state(
3898 &vote_state,
3899 &mut tower_sync.lockouts,
3900 &mut tower_sync.root,
3901 tower_sync.hash,
3902 &slot_hashes,
3903 ),
3904 Err(VoteError::SlotHashMismatch),
3905 );
3906 }
3907
3908 #[test_case(0, true; "first slot")]
3909 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3910 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3911 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3912 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3913 fn test_epoch_half_check(slot: Slot, expected_allowed: bool) {
3914 let epoch_schedule = EpochSchedule::without_warmup();
3915 assert_eq!(
3916 is_commission_update_allowed(slot, &epoch_schedule),
3917 expected_allowed
3918 );
3919 }
3920
3921 #[test]
3922 fn test_warmup_epoch_half_check_with_warmup() {
3923 let epoch_schedule = EpochSchedule::default();
3924 let first_normal_slot = epoch_schedule.first_normal_slot;
3925 assert!(is_commission_update_allowed(0, &epoch_schedule));
3927 assert!(is_commission_update_allowed(
3930 first_normal_slot - 1,
3931 &epoch_schedule
3932 ));
3933 }
3934
3935 #[test_case(0, true; "first slot")]
3936 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3937 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3938 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3939 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3940 fn test_epoch_half_check_with_warmup(slot: Slot, expected_allowed: bool) {
3941 let epoch_schedule = EpochSchedule::default();
3942 let first_normal_slot = epoch_schedule.first_normal_slot;
3943 assert_eq!(
3944 is_commission_update_allowed(first_normal_slot.saturating_add(slot), &epoch_schedule),
3945 expected_allowed
3946 );
3947 }
3948}