1pub use solana_vote_interface::state::{vote_state_versions::*, *};
4use {
5 log::*,
6 solana_account::{AccountSharedData, ReadableAccount, WritableAccount},
7 solana_clock::{Clock, Epoch, Slot},
8 solana_epoch_schedule::EpochSchedule,
9 solana_hash::Hash,
10 solana_instruction::error::InstructionError,
11 solana_pubkey::Pubkey,
12 solana_rent::Rent,
13 solana_slot_hashes::SlotHash,
14 solana_transaction_context::{
15 BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
16 },
17 solana_vote_interface::{error::VoteError, program::id},
18 std::{
19 cmp::Ordering,
20 collections::{HashSet, VecDeque},
21 },
22};
23
24pub fn from<T: ReadableAccount>(account: &T) -> Option<VoteState> {
26 VoteState::deserialize(account.data()).ok()
27}
28
29pub fn to<T: WritableAccount>(versioned: &VoteStateVersions, account: &mut T) -> Option<()> {
31 VoteState::serialize(versioned, account.data_as_mut_slice()).ok()
32}
33
34fn set_vote_account_state(
37 vote_account: &mut BorrowedAccount,
38 vote_state: VoteState,
39) -> Result<(), InstructionError> {
40 if (vote_account.get_data().len() < VoteStateVersions::vote_state_size_of(true))
43 && (!vote_account
44 .is_rent_exempt_at_data_length(VoteStateVersions::vote_state_size_of(true))
45 || vote_account
46 .set_data_length(VoteStateVersions::vote_state_size_of(true))
47 .is_err())
48 {
49 return vote_account.set_state(&VoteStateVersions::V1_14_11(Box::new(
52 VoteState1_14_11::from(vote_state),
53 )));
54 }
55 vote_account.set_state(&VoteStateVersions::new_current(vote_state))
57}
58
59fn check_and_filter_proposed_vote_state(
63 vote_state: &VoteState,
64 proposed_lockouts: &mut VecDeque<Lockout>,
65 proposed_root: &mut Option<Slot>,
66 proposed_hash: Hash,
67 slot_hashes: &[(Slot, Hash)],
68) -> Result<(), VoteError> {
69 if proposed_lockouts.is_empty() {
70 return Err(VoteError::EmptySlots);
71 }
72
73 let last_proposed_slot = proposed_lockouts
74 .back()
75 .expect("must be nonempty, checked above")
76 .slot();
77
78 if let Some(last_vote_slot) = vote_state.votes.back().map(|lockout| lockout.slot()) {
80 if last_proposed_slot <= last_vote_slot {
81 return Err(VoteError::VoteTooOld);
82 }
83 }
84
85 if slot_hashes.is_empty() {
86 return Err(VoteError::SlotsMismatch);
87 }
88 let earliest_slot_hash_in_history = slot_hashes.last().unwrap().0;
89
90 if last_proposed_slot < earliest_slot_hash_in_history {
92 return Err(VoteError::VoteTooOld);
95 }
96
97 if let Some(root) = *proposed_root {
99 if root < earliest_slot_hash_in_history {
104 *proposed_root = vote_state.root_slot;
106
107 for vote in vote_state.votes.iter().rev() {
109 if vote.slot() <= root {
110 *proposed_root = Some(vote.slot());
111 break;
112 }
113 }
114 }
115 }
116
117 let mut root_to_check = *proposed_root;
121 let mut proposed_lockouts_index = 0;
122
123 let mut slot_hashes_index = slot_hashes.len();
126
127 let mut proposed_lockouts_indices_to_filter = vec![];
128
129 while proposed_lockouts_index < proposed_lockouts.len() && slot_hashes_index > 0 {
141 let proposed_vote_slot = if let Some(root) = root_to_check {
142 root
143 } else {
144 proposed_lockouts[proposed_lockouts_index].slot()
145 };
146 if root_to_check.is_none()
147 && proposed_lockouts_index > 0
148 && proposed_vote_slot
149 <= proposed_lockouts[proposed_lockouts_index.checked_sub(1).expect(
150 "`proposed_lockouts_index` is positive when checking `SlotsNotOrdered`",
151 )]
152 .slot()
153 {
154 return Err(VoteError::SlotsNotOrdered);
155 }
156 let ancestor_slot = slot_hashes[slot_hashes_index
157 .checked_sub(1)
158 .expect("`slot_hashes_index` is positive when computing `ancestor_slot`")]
159 .0;
160
161 match proposed_vote_slot.cmp(&ancestor_slot) {
164 Ordering::Less => {
165 if slot_hashes_index == slot_hashes.len() {
166 if proposed_vote_slot >= earliest_slot_hash_in_history {
169 return Err(VoteError::AssertionFailed);
170 }
171 if !vote_state.contains_slot(proposed_vote_slot) && root_to_check.is_none() {
172 proposed_lockouts_indices_to_filter.push(proposed_lockouts_index);
178 }
179 if let Some(new_proposed_root) = root_to_check {
180 assert_eq!(new_proposed_root, proposed_vote_slot);
184 if new_proposed_root >= earliest_slot_hash_in_history {
188 return Err(VoteError::AssertionFailed);
189 }
190 root_to_check = None;
191 } else {
192 proposed_lockouts_index = proposed_lockouts_index.checked_add(1).expect(
193 "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when `proposed_vote_slot` is too old to be in SlotHashes history",
194 );
195 }
196 continue;
197 } else {
198 if root_to_check.is_some() {
202 return Err(VoteError::RootOnDifferentFork);
203 } else {
204 return Err(VoteError::SlotsMismatch);
205 }
206 }
207 }
208 Ordering::Greater => {
209 slot_hashes_index = slot_hashes_index
211 .checked_sub(1)
212 .expect("`slot_hashes_index` is positive when finding newer slots in SlotHashes history");
213 continue;
214 }
215 Ordering::Equal => {
216 if root_to_check.is_some() {
220 root_to_check = None;
221 } else {
222 proposed_lockouts_index = proposed_lockouts_index
223 .checked_add(1)
224 .expect("`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when match is found in SlotHashes history");
225 slot_hashes_index = slot_hashes_index.checked_sub(1).expect(
226 "`slot_hashes_index` is positive when match is found in SlotHashes history",
227 );
228 }
229 }
230 }
231 }
232
233 if proposed_lockouts_index != proposed_lockouts.len() {
234 return Err(VoteError::SlotsMismatch);
236 }
237
238 assert_eq!(last_proposed_slot, slot_hashes[slot_hashes_index].0);
261
262 if slot_hashes[slot_hashes_index].1 != proposed_hash {
263 warn!(
267 "{} dropped vote {:?} root {:?} failed to match hash {} {}",
268 vote_state.node_pubkey,
269 proposed_lockouts,
270 proposed_root,
271 proposed_hash,
272 slot_hashes[slot_hashes_index].1
273 );
274 #[cfg(feature = "metrics")]
275 inc_new_counter_info!("dropped-vote-hash", 1);
276 return Err(VoteError::SlotHashMismatch);
277 }
278
279 let mut proposed_lockouts_index = 0;
281 let mut filter_votes_index = 0;
282 proposed_lockouts.retain(|_lockout| {
283 let should_retain = if filter_votes_index == proposed_lockouts_indices_to_filter.len() {
284 true
285 } else if proposed_lockouts_index == proposed_lockouts_indices_to_filter[filter_votes_index]
286 {
287 filter_votes_index = filter_votes_index.checked_add(1).unwrap();
288 false
289 } else {
290 true
291 };
292
293 proposed_lockouts_index = proposed_lockouts_index
294 .checked_add(1)
295 .expect("`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when filtering out irrelevant votes");
296 should_retain
297 });
298
299 Ok(())
300}
301
302fn check_slots_are_valid(
303 vote_state: &VoteState,
304 vote_slots: &[Slot],
305 vote_hash: &Hash,
306 slot_hashes: &[(Slot, Hash)],
307) -> Result<(), VoteError> {
308 let mut i = 0;
311
312 let mut j = slot_hashes.len();
315
316 while i < vote_slots.len() && j > 0 {
325 if vote_state
328 .last_voted_slot()
329 .is_some_and(|last_voted_slot| vote_slots[i] <= last_voted_slot)
330 {
331 i = i
332 .checked_add(1)
333 .expect("`i` is bounded by `MAX_LOCKOUT_HISTORY` when finding larger slots");
334 continue;
335 }
336
337 if vote_slots[i] != slot_hashes[j.checked_sub(1).expect("`j` is positive")].0 {
339 j = j
341 .checked_sub(1)
342 .expect("`j` is positive when finding newer slots");
343 continue;
344 }
345
346 i = i
349 .checked_add(1)
350 .expect("`i` is bounded by `MAX_LOCKOUT_HISTORY` when hash is found");
351 j = j
352 .checked_sub(1)
353 .expect("`j` is positive when hash is found");
354 }
355
356 if j == slot_hashes.len() {
357 debug!(
361 "{} dropped vote slots {:?}, vote hash: {:?} slot hashes:SlotHash {:?}, too old ",
362 vote_state.node_pubkey, vote_slots, vote_hash, slot_hashes
363 );
364 return Err(VoteError::VoteTooOld);
365 }
366 if i != vote_slots.len() {
367 info!(
370 "{} dropped vote slots {:?} failed to match slot hashes: {:?}",
371 vote_state.node_pubkey, vote_slots, slot_hashes,
372 );
373 return Err(VoteError::SlotsMismatch);
374 }
375 if &slot_hashes[j].1 != vote_hash {
376 warn!(
380 "{} dropped vote slots {:?} failed to match hash {} {}",
381 vote_state.node_pubkey, vote_slots, vote_hash, slot_hashes[j].1
382 );
383 return Err(VoteError::SlotHashMismatch);
384 }
385 Ok(())
386}
387
388pub fn process_new_vote_state(
426 vote_state: &mut VoteState,
427 mut new_state: VecDeque<LandedVote>,
428 new_root: Option<Slot>,
429 timestamp: Option<i64>,
430 epoch: Epoch,
431 current_slot: Slot,
432) -> Result<(), VoteError> {
433 assert!(!new_state.is_empty());
434 if new_state.len() > MAX_LOCKOUT_HISTORY {
435 return Err(VoteError::TooManyVotes);
436 }
437
438 match (new_root, vote_state.root_slot) {
439 (Some(new_root), Some(current_root)) => {
440 if new_root < current_root {
441 return Err(VoteError::RootRollBack);
442 }
443 }
444 (None, Some(_)) => {
445 return Err(VoteError::RootRollBack);
446 }
447 _ => (),
448 }
449
450 let mut previous_vote: Option<&LandedVote> = None;
451
452 for vote in &new_state {
457 if vote.confirmation_count() == 0 {
458 return Err(VoteError::ZeroConfirmations);
459 } else if vote.confirmation_count() > MAX_LOCKOUT_HISTORY as u32 {
460 return Err(VoteError::ConfirmationTooLarge);
461 } else if let Some(new_root) = new_root {
462 if vote.slot() <= new_root
463 &&
464 new_root != Slot::default()
469 {
470 return Err(VoteError::SlotSmallerThanRoot);
471 }
472 }
473
474 if let Some(previous_vote) = previous_vote {
475 if previous_vote.slot() >= vote.slot() {
476 return Err(VoteError::SlotsNotOrdered);
477 } else if previous_vote.confirmation_count() <= vote.confirmation_count() {
478 return Err(VoteError::ConfirmationsNotOrdered);
479 } else if vote.slot() > previous_vote.lockout.last_locked_out_slot() {
480 return Err(VoteError::NewVoteStateLockoutMismatch);
481 }
482 }
483 previous_vote = Some(vote);
484 }
485
486 let mut current_vote_state_index: usize = 0;
489 let mut new_vote_state_index = 0;
490
491 let mut earned_credits = 0_u64;
493
494 if let Some(new_root) = new_root {
495 for current_vote in &vote_state.votes {
496 if current_vote.slot() <= new_root {
499 earned_credits = earned_credits
500 .checked_add(vote_state.credits_for_vote_at_index(current_vote_state_index))
501 .expect("`earned_credits` does not overflow");
502 current_vote_state_index = current_vote_state_index
503 .checked_add(1)
504 .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when processing new root");
505 continue;
506 }
507
508 break;
509 }
510 }
511
512 while current_vote_state_index < vote_state.votes.len()
532 && new_vote_state_index < new_state.len()
533 {
534 let current_vote = &vote_state.votes[current_vote_state_index];
535 let new_vote = &mut new_state[new_vote_state_index];
536
537 match current_vote.slot().cmp(&new_vote.slot()) {
541 Ordering::Less => {
542 if current_vote.lockout.last_locked_out_slot() >= new_vote.slot() {
543 return Err(VoteError::LockoutConflict);
544 }
545 current_vote_state_index = current_vote_state_index
546 .checked_add(1)
547 .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is less than proposed");
548 }
549 Ordering::Equal => {
550 if new_vote.confirmation_count() < current_vote.confirmation_count() {
553 return Err(VoteError::ConfirmationRollBack);
554 }
555
556 new_vote.latency = vote_state.votes[current_vote_state_index].latency;
558
559 current_vote_state_index = current_vote_state_index
560 .checked_add(1)
561 .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is equal to proposed");
562 new_vote_state_index = new_vote_state_index
563 .checked_add(1)
564 .expect("`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is equal to proposed");
565 }
566 Ordering::Greater => {
567 new_vote_state_index = new_vote_state_index
568 .checked_add(1)
569 .expect("`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is greater than proposed");
570 }
571 }
572 }
573
574 for new_vote in new_state.iter_mut() {
580 if new_vote.latency == 0 {
581 new_vote.latency = VoteState::compute_vote_latency(new_vote.slot(), current_slot);
582 }
583 }
584
585 if vote_state.root_slot != new_root {
586 vote_state.increment_credits(epoch, earned_credits);
590 }
591 if let Some(timestamp) = timestamp {
592 let last_slot = new_state.back().unwrap().slot();
593 vote_state.process_timestamp(last_slot, timestamp)?;
594 }
595 vote_state.root_slot = new_root;
596 vote_state.votes = new_state;
597
598 Ok(())
599}
600
601pub fn process_vote_unfiltered(
602 vote_state: &mut VoteState,
603 vote_slots: &[Slot],
604 vote: &Vote,
605 slot_hashes: &[SlotHash],
606 epoch: Epoch,
607 current_slot: Slot,
608) -> Result<(), VoteError> {
609 check_slots_are_valid(vote_state, vote_slots, &vote.hash, slot_hashes)?;
610 vote_slots
611 .iter()
612 .for_each(|s| vote_state.process_next_vote_slot(*s, epoch, current_slot));
613 Ok(())
614}
615
616pub fn process_vote(
617 vote_state: &mut VoteState,
618 vote: &Vote,
619 slot_hashes: &[SlotHash],
620 epoch: Epoch,
621 current_slot: Slot,
622) -> Result<(), VoteError> {
623 if vote.slots.is_empty() {
624 return Err(VoteError::EmptySlots);
625 }
626 let earliest_slot_in_history = slot_hashes.last().map(|(slot, _hash)| *slot).unwrap_or(0);
627 let vote_slots = vote
628 .slots
629 .iter()
630 .filter(|slot| **slot >= earliest_slot_in_history)
631 .cloned()
632 .collect::<Vec<Slot>>();
633 if vote_slots.is_empty() {
634 return Err(VoteError::VotesTooOldAllFiltered);
635 }
636 process_vote_unfiltered(
637 vote_state,
638 &vote_slots,
639 vote,
640 slot_hashes,
641 epoch,
642 current_slot,
643 )
644}
645
646pub fn process_vote_unchecked(vote_state: &mut VoteState, vote: Vote) -> Result<(), VoteError> {
648 if vote.slots.is_empty() {
649 return Err(VoteError::EmptySlots);
650 }
651 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
652 process_vote_unfiltered(
653 vote_state,
654 &vote.slots,
655 &vote,
656 &slot_hashes,
657 vote_state.current_epoch(),
658 0,
659 )
660}
661
662#[cfg(test)]
663pub fn process_slot_votes_unchecked(vote_state: &mut VoteState, slots: &[Slot]) {
664 for slot in slots {
665 process_slot_vote_unchecked(vote_state, *slot);
666 }
667}
668
669pub fn process_slot_vote_unchecked(vote_state: &mut VoteState, slot: Slot) {
670 let _ = process_vote_unchecked(vote_state, Vote::new(vec![slot], Hash::default()));
671}
672
673pub fn authorize<S: std::hash::BuildHasher>(
677 vote_account: &mut BorrowedAccount,
678 authorized: &Pubkey,
679 vote_authorize: VoteAuthorize,
680 signers: &HashSet<Pubkey, S>,
681 clock: &Clock,
682) -> Result<(), InstructionError> {
683 let mut vote_state: VoteState = vote_account
684 .get_state::<VoteStateVersions>()?
685 .convert_to_current();
686
687 match vote_authorize {
688 VoteAuthorize::Voter => {
689 let authorized_withdrawer_signer =
690 verify_authorized_signer(&vote_state.authorized_withdrawer, signers).is_ok();
691
692 vote_state.set_new_authorized_voter(
693 authorized,
694 clock.epoch,
695 clock
696 .leader_schedule_epoch
697 .checked_add(1)
698 .ok_or(InstructionError::InvalidAccountData)?,
699 |epoch_authorized_voter| {
700 if authorized_withdrawer_signer {
702 Ok(())
703 } else {
704 verify_authorized_signer(&epoch_authorized_voter, signers)
705 }
706 },
707 )?;
708 }
709 VoteAuthorize::Withdrawer => {
710 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
712 vote_state.authorized_withdrawer = *authorized;
713 }
714 }
715
716 set_vote_account_state(vote_account, vote_state)
717}
718
719pub fn update_validator_identity<S: std::hash::BuildHasher>(
721 vote_account: &mut BorrowedAccount,
722 node_pubkey: &Pubkey,
723 signers: &HashSet<Pubkey, S>,
724) -> Result<(), InstructionError> {
725 let mut vote_state: VoteState = vote_account
726 .get_state::<VoteStateVersions>()?
727 .convert_to_current();
728
729 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
731
732 verify_authorized_signer(node_pubkey, signers)?;
734
735 vote_state.node_pubkey = *node_pubkey;
736
737 set_vote_account_state(vote_account, vote_state)
738}
739
740pub fn update_commission<S: std::hash::BuildHasher>(
742 vote_account: &mut BorrowedAccount,
743 commission: u8,
744 signers: &HashSet<Pubkey, S>,
745 epoch_schedule: &EpochSchedule,
746 clock: &Clock,
747) -> Result<(), InstructionError> {
748 let vote_state_result = vote_account
749 .get_state::<VoteStateVersions>()
750 .map(|vote_state| vote_state.convert_to_current());
751 let enforce_commission_update_rule = if let Ok(decoded_vote_state) = &vote_state_result {
752 is_commission_increase(decoded_vote_state, commission)
753 } else {
754 true
755 };
756
757 if enforce_commission_update_rule && !is_commission_update_allowed(clock.slot, epoch_schedule) {
758 return Err(VoteError::CommissionUpdateTooLate.into());
759 }
760
761 let mut vote_state = vote_state_result?;
762
763 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
765
766 vote_state.commission = commission;
767
768 set_vote_account_state(vote_account, vote_state)
769}
770
771pub fn is_commission_increase(vote_state: &VoteState, commission: u8) -> bool {
773 commission > vote_state.commission
774}
775
776pub fn is_commission_update_allowed(slot: Slot, epoch_schedule: &EpochSchedule) -> bool {
779 if let Some(relative_slot) = slot
781 .saturating_sub(epoch_schedule.first_normal_slot)
782 .checked_rem(epoch_schedule.slots_per_epoch)
783 {
784 relative_slot.saturating_mul(2) <= epoch_schedule.slots_per_epoch
786 } else {
787 true
789 }
790}
791
792fn verify_authorized_signer<S: std::hash::BuildHasher>(
793 authorized: &Pubkey,
794 signers: &HashSet<Pubkey, S>,
795) -> Result<(), InstructionError> {
796 if signers.contains(authorized) {
797 Ok(())
798 } else {
799 Err(InstructionError::MissingRequiredSignature)
800 }
801}
802
803pub fn withdraw<S: std::hash::BuildHasher>(
805 transaction_context: &TransactionContext,
806 instruction_context: &InstructionContext,
807 vote_account_index: IndexOfAccount,
808 lamports: u64,
809 to_account_index: IndexOfAccount,
810 signers: &HashSet<Pubkey, S>,
811 rent_sysvar: &Rent,
812 clock: &Clock,
813) -> Result<(), InstructionError> {
814 let mut vote_account = instruction_context
815 .try_borrow_instruction_account(transaction_context, vote_account_index)?;
816 let vote_state: VoteState = vote_account
817 .get_state::<VoteStateVersions>()?
818 .convert_to_current();
819
820 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
821
822 let remaining_balance = vote_account
823 .get_lamports()
824 .checked_sub(lamports)
825 .ok_or(InstructionError::InsufficientFunds)?;
826
827 if remaining_balance == 0 {
828 let reject_active_vote_account_close = vote_state
829 .epoch_credits
830 .last()
831 .map(|(last_epoch_with_credits, _, _)| {
832 let current_epoch = clock.epoch;
833 current_epoch.saturating_sub(*last_epoch_with_credits) < 2
837 })
838 .unwrap_or(false);
839
840 if reject_active_vote_account_close {
841 return Err(VoteError::ActiveVoteAccountClose.into());
842 } else {
843 set_vote_account_state(&mut vote_account, VoteState::default())?;
845 }
846 } else {
847 let min_rent_exempt_balance = rent_sysvar.minimum_balance(vote_account.get_data().len());
848 if remaining_balance < min_rent_exempt_balance {
849 return Err(InstructionError::InsufficientFunds);
850 }
851 }
852
853 vote_account.checked_sub_lamports(lamports)?;
854 drop(vote_account);
855 let mut to_account = instruction_context
856 .try_borrow_instruction_account(transaction_context, to_account_index)?;
857 to_account.checked_add_lamports(lamports)?;
858 Ok(())
859}
860
861pub fn initialize_account<S: std::hash::BuildHasher>(
865 vote_account: &mut BorrowedAccount,
866 vote_init: &VoteInit,
867 signers: &HashSet<Pubkey, S>,
868 clock: &Clock,
869) -> Result<(), InstructionError> {
870 if vote_account.get_data().len() != VoteStateVersions::vote_state_size_of(true) {
871 return Err(InstructionError::InvalidAccountData);
872 }
873 let versioned = vote_account.get_state::<VoteStateVersions>()?;
874
875 if !versioned.is_uninitialized() {
876 return Err(InstructionError::AccountAlreadyInitialized);
877 }
878
879 verify_authorized_signer(&vote_init.node_pubkey, signers)?;
881
882 set_vote_account_state(vote_account, VoteState::new(vote_init, clock))
883}
884
885fn verify_and_get_vote_state<S: std::hash::BuildHasher>(
886 vote_account: &BorrowedAccount,
887 clock: &Clock,
888 signers: &HashSet<Pubkey, S>,
889) -> Result<VoteState, InstructionError> {
890 let versioned = vote_account.get_state::<VoteStateVersions>()?;
891
892 if versioned.is_uninitialized() {
893 return Err(InstructionError::UninitializedAccount);
894 }
895
896 let mut vote_state = versioned.convert_to_current();
897 let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?;
898 verify_authorized_signer(&authorized_voter, signers)?;
899
900 Ok(vote_state)
901}
902
903pub fn process_vote_with_account<S: std::hash::BuildHasher>(
904 vote_account: &mut BorrowedAccount,
905 slot_hashes: &[SlotHash],
906 clock: &Clock,
907 vote: &Vote,
908 signers: &HashSet<Pubkey, S>,
909) -> Result<(), InstructionError> {
910 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
911
912 process_vote(&mut vote_state, vote, slot_hashes, clock.epoch, clock.slot)?;
913 if let Some(timestamp) = vote.timestamp {
914 vote.slots
915 .iter()
916 .max()
917 .ok_or(VoteError::EmptySlots)
918 .and_then(|slot| vote_state.process_timestamp(*slot, timestamp))?;
919 }
920 set_vote_account_state(vote_account, vote_state)
921}
922
923pub fn process_vote_state_update<S: std::hash::BuildHasher>(
924 vote_account: &mut BorrowedAccount,
925 slot_hashes: &[SlotHash],
926 clock: &Clock,
927 vote_state_update: VoteStateUpdate,
928 signers: &HashSet<Pubkey, S>,
929) -> Result<(), InstructionError> {
930 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
931 do_process_vote_state_update(
932 &mut vote_state,
933 slot_hashes,
934 clock.epoch,
935 clock.slot,
936 vote_state_update,
937 )?;
938 set_vote_account_state(vote_account, vote_state)
939}
940
941pub fn do_process_vote_state_update(
942 vote_state: &mut VoteState,
943 slot_hashes: &[SlotHash],
944 epoch: u64,
945 slot: u64,
946 mut vote_state_update: VoteStateUpdate,
947) -> Result<(), VoteError> {
948 check_and_filter_proposed_vote_state(
949 vote_state,
950 &mut vote_state_update.lockouts,
951 &mut vote_state_update.root,
952 vote_state_update.hash,
953 slot_hashes,
954 )?;
955 process_new_vote_state(
956 vote_state,
957 vote_state_update
958 .lockouts
959 .iter()
960 .map(|lockout| LandedVote::from(*lockout))
961 .collect(),
962 vote_state_update.root,
963 vote_state_update.timestamp,
964 epoch,
965 slot,
966 )
967}
968
969pub fn process_tower_sync<S: std::hash::BuildHasher>(
970 vote_account: &mut BorrowedAccount,
971 slot_hashes: &[SlotHash],
972 clock: &Clock,
973 tower_sync: TowerSync,
974 signers: &HashSet<Pubkey, S>,
975) -> Result<(), InstructionError> {
976 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
977 do_process_tower_sync(
978 &mut vote_state,
979 slot_hashes,
980 clock.epoch,
981 clock.slot,
982 tower_sync,
983 )?;
984 set_vote_account_state(vote_account, vote_state)
985}
986
987fn do_process_tower_sync(
988 vote_state: &mut VoteState,
989 slot_hashes: &[SlotHash],
990 epoch: u64,
991 slot: u64,
992 mut tower_sync: TowerSync,
993) -> Result<(), VoteError> {
994 check_and_filter_proposed_vote_state(
995 vote_state,
996 &mut tower_sync.lockouts,
997 &mut tower_sync.root,
998 tower_sync.hash,
999 slot_hashes,
1000 )?;
1001 process_new_vote_state(
1002 vote_state,
1003 tower_sync
1004 .lockouts
1005 .iter()
1006 .map(|lockout| LandedVote::from(*lockout))
1007 .collect(),
1008 tower_sync.root,
1009 tower_sync.timestamp,
1010 epoch,
1011 slot,
1012 )
1013}
1014
1015pub fn create_account_with_authorized(
1020 node_pubkey: &Pubkey,
1021 authorized_voter: &Pubkey,
1022 authorized_withdrawer: &Pubkey,
1023 commission: u8,
1024 lamports: u64,
1025) -> AccountSharedData {
1026 let mut vote_account = AccountSharedData::new(lamports, VoteState::size_of(), &id());
1027
1028 let vote_state = VoteState::new(
1029 &VoteInit {
1030 node_pubkey: *node_pubkey,
1031 authorized_voter: *authorized_voter,
1032 authorized_withdrawer: *authorized_withdrawer,
1033 commission,
1034 },
1035 &Clock::default(),
1036 );
1037
1038 VoteState::serialize(
1039 &VoteStateVersions::Current(Box::new(vote_state)),
1040 vote_account.data_as_mut_slice(),
1041 )
1042 .unwrap();
1043
1044 vote_account
1045}
1046
1047pub fn create_account(
1049 vote_pubkey: &Pubkey,
1050 node_pubkey: &Pubkey,
1051 commission: u8,
1052 lamports: u64,
1053) -> AccountSharedData {
1054 create_account_with_authorized(node_pubkey, vote_pubkey, vote_pubkey, commission, lamports)
1055}
1056
1057#[cfg(test)]
1058mod tests {
1059 use {
1060 super::*,
1061 crate::vote_state,
1062 assert_matches::assert_matches,
1063 solana_account::{state_traits::StateMut, AccountSharedData},
1064 solana_clock::DEFAULT_SLOTS_PER_EPOCH,
1065 solana_sha256_hasher::hash,
1066 solana_transaction_context::InstructionAccount,
1067 std::cell::RefCell,
1068 test_case::test_case,
1069 };
1070
1071 const MAX_RECENT_VOTES: usize = 16;
1072
1073 fn vote_state_new_for_test(auth_pubkey: &Pubkey) -> VoteState {
1074 VoteState::new(
1075 &VoteInit {
1076 node_pubkey: solana_pubkey::new_rand(),
1077 authorized_voter: *auth_pubkey,
1078 authorized_withdrawer: *auth_pubkey,
1079 commission: 0,
1080 },
1081 &Clock::default(),
1082 )
1083 }
1084
1085 fn create_test_account() -> (Pubkey, RefCell<AccountSharedData>) {
1086 let rent = Rent::default();
1087 let balance = VoteState::get_rent_exempt_reserve(&rent);
1088 let vote_pubkey = solana_pubkey::new_rand();
1089 (
1090 vote_pubkey,
1091 RefCell::new(vote_state::create_account(
1092 &vote_pubkey,
1093 &solana_pubkey::new_rand(),
1094 0,
1095 balance,
1096 )),
1097 )
1098 }
1099
1100 #[test]
1101 fn test_vote_state_upgrade_from_1_14_11() {
1102 let node_pubkey = solana_pubkey::new_rand();
1105 let withdrawer_pubkey = solana_pubkey::new_rand();
1106 let mut vote_state = VoteState::new(
1107 &VoteInit {
1108 node_pubkey,
1109 authorized_voter: withdrawer_pubkey,
1110 authorized_withdrawer: withdrawer_pubkey,
1111 commission: 10,
1112 },
1113 &Clock::default(),
1114 );
1115 vote_state.increment_credits(0, 100);
1117 assert_eq!(
1118 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 0, 1, |_pubkey| Ok(())),
1119 Ok(())
1120 );
1121 vote_state.increment_credits(1, 200);
1122 assert_eq!(
1123 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 1, 2, |_pubkey| Ok(())),
1124 Ok(())
1125 );
1126 vote_state.increment_credits(2, 300);
1127 assert_eq!(
1128 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 2, 3, |_pubkey| Ok(())),
1129 Ok(())
1130 );
1131 vec![
1133 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
1134 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
1135 134, 135,
1136 ]
1137 .into_iter()
1138 .for_each(|v| vote_state.process_next_vote_slot(v, 4, 0));
1139
1140 let version1_14_11_serialized = bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(
1141 VoteState1_14_11::from(vote_state.clone()),
1142 )))
1143 .unwrap();
1144 let version1_14_11_serialized_len = version1_14_11_serialized.len();
1145 let rent = Rent::default();
1146 let lamports = rent.minimum_balance(version1_14_11_serialized_len);
1147 let mut vote_account =
1148 AccountSharedData::new(lamports, version1_14_11_serialized_len, &id());
1149 vote_account.set_data_from_slice(&version1_14_11_serialized);
1150
1151 let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id());
1154 let transaction_context = TransactionContext::new(
1155 vec![(id(), processor_account), (node_pubkey, vote_account)],
1156 rent.clone(),
1157 0,
1158 0,
1159 );
1160 let mut instruction_context = InstructionContext::default();
1161 instruction_context.configure(
1162 &[0],
1163 &[InstructionAccount {
1164 index_in_transaction: 1,
1165 index_in_caller: 1,
1166 index_in_callee: 0,
1167 is_signer: false,
1168 is_writable: true,
1169 }],
1170 &[],
1171 );
1172
1173 let mut borrowed_account = instruction_context
1176 .try_borrow_instruction_account(&transaction_context, 0)
1177 .unwrap();
1178
1179 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1181 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1182
1183 let converted_vote_state = vote_state_version.convert_to_current();
1185
1186 assert!(vote_state == converted_vote_state);
1188
1189 let vote_state = converted_vote_state;
1190
1191 assert_eq!(
1194 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1195 Ok(())
1196 );
1197 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1198 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1199
1200 let converted_vote_state = vote_state_version.convert_to_current();
1202
1203 assert_eq!(vote_state, converted_vote_state);
1205
1206 let vote_state = converted_vote_state;
1207
1208 assert_eq!(
1211 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1212 Ok(())
1213 );
1214 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1215 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1216
1217 let converted_vote_state = vote_state_version.convert_to_current();
1219
1220 assert_eq!(vote_state, converted_vote_state);
1222
1223 let vote_state = converted_vote_state;
1224
1225 assert_eq!(
1228 borrowed_account.set_lamports(rent.minimum_balance(VoteState::size_of()),),
1229 Ok(())
1230 );
1231 assert_eq!(
1232 set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1233 Ok(())
1234 );
1235 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1236 assert_matches!(vote_state_version, VoteStateVersions::Current(_));
1237
1238 let converted_vote_state = vote_state_version.convert_to_current();
1240
1241 assert_eq!(vote_state, converted_vote_state);
1243 }
1244
1245 #[test]
1246 fn test_vote_lockout() {
1247 let (_vote_pubkey, vote_account) = create_test_account();
1248
1249 let mut vote_state: VoteState =
1250 StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
1251 .unwrap()
1252 .convert_to_current();
1253
1254 for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
1255 process_slot_vote_unchecked(&mut vote_state, (INITIAL_LOCKOUT * i) as u64);
1256 }
1257
1258 assert_eq!(vote_state.votes.len(), MAX_LOCKOUT_HISTORY);
1260 assert_eq!(vote_state.root_slot, Some(0));
1261 check_lockouts(&vote_state);
1262
1263 let top_vote = vote_state.votes.front().unwrap().slot();
1267 let slot = vote_state.last_lockout().unwrap().last_locked_out_slot();
1268 process_slot_vote_unchecked(&mut vote_state, slot);
1269 assert_eq!(Some(top_vote), vote_state.root_slot);
1270
1271 let slot = vote_state
1273 .votes
1274 .front()
1275 .unwrap()
1276 .lockout
1277 .last_locked_out_slot();
1278 process_slot_vote_unchecked(&mut vote_state, slot);
1279 assert_eq!(vote_state.votes.len(), 2);
1281 }
1282
1283 #[test]
1284 fn test_update_commission() {
1285 let node_pubkey = Pubkey::new_unique();
1286 let withdrawer_pubkey = Pubkey::new_unique();
1287 let clock = Clock::default();
1288 let 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,
1296 );
1297
1298 let serialized =
1299 bincode::serialize(&VoteStateVersions::Current(Box::new(vote_state.clone()))).unwrap();
1300 let serialized_len = serialized.len();
1301 let rent = Rent::default();
1302 let lamports = rent.minimum_balance(serialized_len);
1303 let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id());
1304 vote_account.set_data_from_slice(&serialized);
1305
1306 let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id());
1309 let transaction_context = TransactionContext::new(
1310 vec![(id(), processor_account), (node_pubkey, vote_account)],
1311 rent,
1312 0,
1313 0,
1314 );
1315 let mut instruction_context = InstructionContext::default();
1316 instruction_context.configure(
1317 &[0],
1318 &[InstructionAccount {
1319 index_in_transaction: 1,
1320 index_in_caller: 1,
1321 index_in_callee: 0,
1322 is_signer: false,
1323 is_writable: true,
1324 }],
1325 &[],
1326 );
1327
1328 let mut borrowed_account = instruction_context
1331 .try_borrow_instruction_account(&transaction_context, 0)
1332 .unwrap();
1333
1334 let epoch_schedule = std::sync::Arc::new(EpochSchedule::without_warmup());
1335
1336 let first_half_clock = std::sync::Arc::new(Clock {
1337 slot: epoch_schedule.slots_per_epoch / 4,
1338 ..Clock::default()
1339 });
1340
1341 let second_half_clock = std::sync::Arc::new(Clock {
1342 slot: (epoch_schedule.slots_per_epoch * 3) / 4,
1343 ..Clock::default()
1344 });
1345
1346 let signers: HashSet<Pubkey> = vec![withdrawer_pubkey].into_iter().collect();
1347
1348 assert_eq!(
1350 borrowed_account
1351 .get_state::<VoteStateVersions>()
1352 .unwrap()
1353 .convert_to_current()
1354 .commission,
1355 10
1356 );
1357 assert_matches!(
1358 update_commission(
1359 &mut borrowed_account,
1360 11,
1361 &signers,
1362 &epoch_schedule,
1363 &first_half_clock,
1364 ),
1365 Ok(())
1366 );
1367 assert_eq!(
1368 borrowed_account
1369 .get_state::<VoteStateVersions>()
1370 .unwrap()
1371 .convert_to_current()
1372 .commission,
1373 11
1374 );
1375
1376 assert_matches!(
1378 update_commission(
1379 &mut borrowed_account,
1380 12,
1381 &signers,
1382 &epoch_schedule,
1383 &second_half_clock,
1384 ),
1385 Err(_)
1386 );
1387 assert_eq!(
1388 borrowed_account
1389 .get_state::<VoteStateVersions>()
1390 .unwrap()
1391 .convert_to_current()
1392 .commission,
1393 11
1394 );
1395
1396 assert_matches!(
1398 update_commission(
1399 &mut borrowed_account,
1400 10,
1401 &signers,
1402 &epoch_schedule,
1403 &first_half_clock,
1404 ),
1405 Ok(())
1406 );
1407 assert_eq!(
1408 borrowed_account
1409 .get_state::<VoteStateVersions>()
1410 .unwrap()
1411 .convert_to_current()
1412 .commission,
1413 10
1414 );
1415
1416 assert_eq!(
1417 borrowed_account
1418 .get_state::<VoteStateVersions>()
1419 .unwrap()
1420 .convert_to_current()
1421 .commission,
1422 10
1423 );
1424
1425 assert_matches!(
1426 update_commission(
1427 &mut borrowed_account,
1428 9,
1429 &signers,
1430 &epoch_schedule,
1431 &second_half_clock,
1432 ),
1433 Ok(())
1434 );
1435 assert_eq!(
1436 borrowed_account
1437 .get_state::<VoteStateVersions>()
1438 .unwrap()
1439 .convert_to_current()
1440 .commission,
1441 9
1442 );
1443 }
1444
1445 #[test]
1446 fn test_vote_double_lockout_after_expiration() {
1447 let voter_pubkey = solana_pubkey::new_rand();
1448 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1449
1450 for i in 0..3 {
1451 process_slot_vote_unchecked(&mut vote_state, i as u64);
1452 }
1453
1454 check_lockouts(&vote_state);
1455
1456 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 1) as u64);
1460 check_lockouts(&vote_state);
1461
1462 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 2) as u64);
1465 check_lockouts(&vote_state);
1466
1467 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 3) as u64);
1470 check_lockouts(&vote_state);
1471 }
1472
1473 #[test]
1474 fn test_expire_multiple_votes() {
1475 let voter_pubkey = solana_pubkey::new_rand();
1476 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1477
1478 for i in 0..3 {
1479 process_slot_vote_unchecked(&mut vote_state, i as u64);
1480 }
1481
1482 assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1483
1484 let expire_slot = vote_state.votes[1].slot() + vote_state.votes[1].lockout.lockout() + 1;
1486 process_slot_vote_unchecked(&mut vote_state, expire_slot);
1487 assert_eq!(vote_state.votes.len(), 2);
1488
1489 assert_eq!(vote_state.votes[0].slot(), 0);
1491 assert_eq!(vote_state.votes[1].slot(), expire_slot);
1492
1493 process_slot_vote_unchecked(&mut vote_state, expire_slot + 1);
1495
1496 assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1498
1499 assert_eq!(vote_state.votes[1].confirmation_count(), 2);
1501 assert_eq!(vote_state.votes[2].confirmation_count(), 1);
1502 }
1503
1504 #[test]
1505 fn test_vote_credits() {
1506 let voter_pubkey = solana_pubkey::new_rand();
1507 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1508
1509 for i in 0..MAX_LOCKOUT_HISTORY {
1510 process_slot_vote_unchecked(&mut vote_state, i as u64);
1511 }
1512
1513 assert_eq!(vote_state.credits(), 0);
1514
1515 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 1);
1516 assert_eq!(vote_state.credits(), 1);
1517 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 2);
1518 assert_eq!(vote_state.credits(), 2);
1519 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 3);
1520 assert_eq!(vote_state.credits(), 3);
1521 }
1522
1523 #[test]
1524 fn test_duplicate_vote() {
1525 let voter_pubkey = solana_pubkey::new_rand();
1526 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1527 process_slot_vote_unchecked(&mut vote_state, 0);
1528 process_slot_vote_unchecked(&mut vote_state, 1);
1529 process_slot_vote_unchecked(&mut vote_state, 0);
1530 assert_eq!(vote_state.nth_recent_lockout(0).unwrap().slot(), 1);
1531 assert_eq!(vote_state.nth_recent_lockout(1).unwrap().slot(), 0);
1532 assert!(vote_state.nth_recent_lockout(2).is_none());
1533 }
1534
1535 #[test]
1536 fn test_nth_recent_lockout() {
1537 let voter_pubkey = solana_pubkey::new_rand();
1538 let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1539 for i in 0..MAX_LOCKOUT_HISTORY {
1540 process_slot_vote_unchecked(&mut vote_state, i as u64);
1541 }
1542 for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
1543 assert_eq!(
1544 vote_state.nth_recent_lockout(i).unwrap().slot() as usize,
1545 MAX_LOCKOUT_HISTORY - i - 1,
1546 );
1547 }
1548 assert!(vote_state.nth_recent_lockout(MAX_LOCKOUT_HISTORY).is_none());
1549 }
1550
1551 fn check_lockouts(vote_state: &VoteState) {
1552 for (i, vote) in vote_state.votes.iter().enumerate() {
1553 let num_votes = vote_state
1554 .votes
1555 .len()
1556 .checked_sub(i)
1557 .expect("`i` is less than `vote_state.votes.len()`");
1558 assert_eq!(
1559 vote.lockout.lockout(),
1560 INITIAL_LOCKOUT.pow(num_votes as u32) as u64
1561 );
1562 }
1563 }
1564
1565 fn recent_votes(vote_state: &VoteState) -> Vec<Vote> {
1566 let start = vote_state.votes.len().saturating_sub(MAX_RECENT_VOTES);
1567 (start..vote_state.votes.len())
1568 .map(|i| {
1569 Vote::new(
1570 vec![vote_state.votes.get(i).unwrap().slot()],
1571 Hash::default(),
1572 )
1573 })
1574 .collect()
1575 }
1576
1577 #[test]
1579 fn test_process_missed_votes() {
1580 let account_a = solana_pubkey::new_rand();
1581 let mut vote_state_a = vote_state_new_for_test(&account_a);
1582 let account_b = solana_pubkey::new_rand();
1583 let mut vote_state_b = vote_state_new_for_test(&account_b);
1584
1585 (0..5).for_each(|i| process_slot_vote_unchecked(&mut vote_state_a, i as u64));
1587 assert_ne!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1588
1589 let slots = (0u64..MAX_RECENT_VOTES as u64).collect();
1591 let vote = Vote::new(slots, Hash::default());
1592 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
1593
1594 assert_eq!(
1595 process_vote(&mut vote_state_a, &vote, &slot_hashes, 0, 0),
1596 Ok(())
1597 );
1598 assert_eq!(
1599 process_vote(&mut vote_state_b, &vote, &slot_hashes, 0, 0),
1600 Ok(())
1601 );
1602 assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1603 }
1604
1605 #[test]
1606 fn test_process_vote_skips_old_vote() {
1607 let mut vote_state = VoteState::default();
1608
1609 let vote = Vote::new(vec![0], Hash::default());
1610 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1611 assert_eq!(
1612 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1613 Ok(())
1614 );
1615 let recent = recent_votes(&vote_state);
1616 assert_eq!(
1617 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1618 Err(VoteError::VoteTooOld)
1619 );
1620 assert_eq!(recent, recent_votes(&vote_state));
1621 }
1622
1623 #[test]
1624 fn test_check_slots_are_valid_vote_empty_slot_hashes() {
1625 let vote_state = VoteState::default();
1626
1627 let vote = Vote::new(vec![0], Hash::default());
1628 assert_eq!(
1629 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &[]),
1630 Err(VoteError::VoteTooOld)
1631 );
1632 }
1633
1634 #[test]
1635 fn test_check_slots_are_valid_new_vote() {
1636 let vote_state = VoteState::default();
1637
1638 let vote = Vote::new(vec![0], Hash::default());
1639 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1640 assert_eq!(
1641 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1642 Ok(())
1643 );
1644 }
1645
1646 #[test]
1647 fn test_check_slots_are_valid_bad_hash() {
1648 let vote_state = VoteState::default();
1649
1650 let vote = Vote::new(vec![0], Hash::default());
1651 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
1652 assert_eq!(
1653 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1654 Err(VoteError::SlotHashMismatch)
1655 );
1656 }
1657
1658 #[test]
1659 fn test_check_slots_are_valid_bad_slot() {
1660 let vote_state = VoteState::default();
1661
1662 let vote = Vote::new(vec![1], Hash::default());
1663 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1664 assert_eq!(
1665 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1666 Err(VoteError::SlotsMismatch)
1667 );
1668 }
1669
1670 #[test]
1671 fn test_check_slots_are_valid_duplicate_vote() {
1672 let mut vote_state = VoteState::default();
1673
1674 let vote = Vote::new(vec![0], Hash::default());
1675 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1676 assert_eq!(
1677 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1678 Ok(())
1679 );
1680 assert_eq!(
1681 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1682 Err(VoteError::VoteTooOld)
1683 );
1684 }
1685
1686 #[test]
1687 fn test_check_slots_are_valid_next_vote() {
1688 let mut vote_state = VoteState::default();
1689
1690 let vote = Vote::new(vec![0], Hash::default());
1691 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1692 assert_eq!(
1693 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1694 Ok(())
1695 );
1696
1697 let vote = Vote::new(vec![0, 1], Hash::default());
1698 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1699 assert_eq!(
1700 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1701 Ok(())
1702 );
1703 }
1704
1705 #[test]
1706 fn test_check_slots_are_valid_next_vote_only() {
1707 let mut vote_state = VoteState::default();
1708
1709 let vote = Vote::new(vec![0], Hash::default());
1710 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1711 assert_eq!(
1712 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1713 Ok(())
1714 );
1715
1716 let vote = Vote::new(vec![1], Hash::default());
1717 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1718 assert_eq!(
1719 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1720 Ok(())
1721 );
1722 }
1723 #[test]
1724 fn test_process_vote_empty_slots() {
1725 let mut vote_state = VoteState::default();
1726
1727 let vote = Vote::new(vec![], Hash::default());
1728 assert_eq!(
1729 process_vote(&mut vote_state, &vote, &[], 0, 0),
1730 Err(VoteError::EmptySlots)
1731 );
1732 }
1733
1734 pub fn process_new_vote_state_from_lockouts(
1735 vote_state: &mut VoteState,
1736 new_state: VecDeque<Lockout>,
1737 new_root: Option<Slot>,
1738 timestamp: Option<i64>,
1739 epoch: Epoch,
1740 ) -> Result<(), VoteError> {
1741 process_new_vote_state(
1742 vote_state,
1743 new_state.into_iter().map(LandedVote::from).collect(),
1744 new_root,
1745 timestamp,
1746 epoch,
1747 0,
1748 )
1749 }
1750
1751 #[test]
1753 fn test_vote_state_update_increment_credits() {
1754 let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
1756
1757 let test_vote_groups: Vec<Vec<Slot>> = vec![
1760 vec![1, 2, 3, 4, 5, 6, 7, 8],
1762 vec![
1763 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1764 30, 31,
1765 ],
1766 vec![32],
1768 vec![33],
1770 vec![34, 35],
1772 vec![36, 37, 38],
1774 vec![
1776 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
1777 60, 61, 62, 63, 64, 65, 66, 67, 68,
1778 ],
1779 vec![
1781 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
1782 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1783 ],
1784 vec![100, 101, 106, 107, 112, 116, 120, 121, 122, 124],
1786 vec![200, 201],
1788 vec![
1789 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
1790 218, 219, 220, 221, 222, 223, 224, 225, 226,
1791 ],
1792 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
1793 ];
1794
1795 for vote_group in test_vote_groups {
1796 let mut vote_state_after_vote = vote_state.clone();
1798
1799 process_vote_unchecked(
1800 &mut vote_state_after_vote,
1801 Vote {
1802 slots: vote_group.clone(),
1803 hash: Hash::new_unique(),
1804 timestamp: None,
1805 },
1806 )
1807 .unwrap();
1808
1809 assert_eq!(
1811 process_new_vote_state(
1812 &mut vote_state,
1813 vote_state_after_vote.votes,
1814 vote_state_after_vote.root_slot,
1815 None,
1816 0,
1817 0,
1818 ),
1819 Ok(())
1820 );
1821
1822 assert_eq!(
1824 vote_state.epoch_credits,
1825 vote_state_after_vote.epoch_credits
1826 );
1827 }
1828 }
1829
1830 #[test]
1832 fn test_timely_credits() {
1833 let test_vote_groups: Vec<(Vec<Slot>, Slot, u32)> = vec![
1837 (
1839 vec![1, 2, 3, 4, 5, 6, 7, 8],
1840 9,
1841 0,
1843 ),
1844 (
1845 vec![
1846 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
1847 29, 30, 31,
1848 ],
1849 34,
1850 0,
1853 ),
1854 (
1856 vec![32],
1857 35,
1858 10,
1861 ),
1862 (
1864 vec![33],
1865 36,
1866 10 + 11, ),
1870 (
1872 vec![34, 35],
1873 37,
1874 21 + 12 + 13, ),
1878 (
1880 vec![36, 37, 38],
1881 39,
1882 46 + 14 + 15 + 16, ),
1886 (
1887 vec![
1889 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
1890 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
1891 ],
1892 69,
1893 91 + 16
1903 + 9 + 2
1905 + 3
1906 + 4
1907 + 5
1908 + 6
1909 + 7
1910 + 8
1911 + 9
1912 + 10
1913 + 11
1914 + 12
1915 + 13
1916 + 14
1917 + 15
1918 + 15
1919 + 15
1920 + 15
1921 + 16
1922 + 15
1923 + 16, ),
1925 (
1927 vec![
1928 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
1929 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1930 ],
1931 100,
1932 327 + 16
1939 + 14 + 2
1941 + 3
1942 + 4
1943 + 5
1944 + 6
1945 + 7
1946 + 8
1947 + 9
1948 + 10
1949 + 11
1950 + 12
1951 + 13
1952 + 14
1953 + 15
1954 + 16
1955 + 16, ),
1957 (
1959 vec![115, 116, 117, 118, 119, 120, 121, 122, 123, 124],
1960 130,
1961 508 + ((74 - 69) + 1), ),
1966 (
1968 vec![200, 201],
1969 202,
1970 514,
1973 ),
1974 (
1975 vec![
1976 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
1977 218, 219, 220, 221, 222, 223, 224, 225, 226,
1978 ],
1979 227,
1980 514 + 9 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13, ),
1986 (
1987 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
1988 237,
1989 613 + 3 + 4 + 5 + 6 + 16 + 16 + 1 + 1 + 1 + 1, ),
1995 ];
1996
1997 for i in 0..test_vote_groups.len() {
2000 let mut vote_state_1 = VoteState::new(&VoteInit::default(), &Clock::default());
2002 let mut vote_state_2 = VoteState::new(&VoteInit::default(), &Clock::default());
2004 test_vote_groups.iter().take(i + 1).for_each(|vote_group| {
2005 let vote = Vote {
2006 slots: vote_group.0.clone(), hash: Hash::new_unique(),
2008 timestamp: None,
2009 };
2010 let slot_hashes: Vec<_> =
2011 vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
2012 assert_eq!(
2013 process_vote(
2014 &mut vote_state_1,
2015 &vote,
2016 &slot_hashes,
2017 0,
2018 vote_group.1, ),
2020 Ok(())
2021 );
2022
2023 assert_eq!(
2024 process_new_vote_state(
2025 &mut vote_state_2,
2026 vote_state_1.votes.clone(),
2027 vote_state_1.root_slot,
2028 None,
2029 0,
2030 vote_group.1, ),
2032 Ok(())
2033 );
2034 });
2035
2036 let vote_group = &test_vote_groups[i];
2038 assert_eq!(vote_state_1.credits(), vote_group.2 as u64); assert_eq!(vote_state_2.credits(), vote_group.2 as u64); }
2041 }
2042
2043 #[test]
2044 fn test_retroactive_voting_timely_credits() {
2045 #[allow(clippy::type_complexity)]
2051 let test_vote_state_updates: Vec<(Vec<(Slot, u32)>, Slot, Option<Slot>, u32)> = vec![
2052 (
2054 vec![(7, 4), (8, 3), (9, 2), (10, 1)],
2055 11,
2056 None,
2058 0,
2060 ),
2061 (
2063 vec![
2064 (1, 10),
2065 (2, 9),
2066 (3, 8),
2067 (4, 7),
2068 (5, 6),
2069 (6, 5),
2070 (7, 4),
2071 (8, 3),
2072 (9, 2),
2073 (10, 1),
2074 ],
2075 12,
2076 None,
2078 0,
2080 ),
2081 (
2083 vec![
2084 (11, 31),
2085 (12, 30),
2086 (13, 29),
2087 (14, 28),
2088 (15, 27),
2089 (16, 26),
2090 (17, 25),
2091 (18, 24),
2092 (19, 23),
2093 (20, 22),
2094 (21, 21),
2095 (22, 20),
2096 (23, 19),
2097 (24, 18),
2098 (25, 17),
2099 (26, 16),
2100 (27, 15),
2101 (28, 14),
2102 (29, 13),
2103 (30, 12),
2104 (31, 11),
2105 (32, 10),
2106 (33, 9),
2107 (34, 8),
2108 (35, 7),
2109 (36, 6),
2110 (37, 5),
2111 (38, 4),
2112 (39, 3),
2113 (40, 2),
2114 (41, 1),
2115 ],
2116 42,
2117 Some(10),
2119 7 + 8 + 9 + 10 + 11 + 12 + 14 + 15 + 16 + 16,
2122 ),
2123 ];
2124
2125 let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
2127
2128 test_vote_state_updates
2131 .iter()
2132 .for_each(|proposed_vote_state| {
2133 let new_state = proposed_vote_state
2134 .0 .iter()
2136 .map(|(slot, confirmation_count)| LandedVote {
2137 latency: 0,
2138 lockout: Lockout::new_with_confirmation_count(*slot, *confirmation_count),
2139 })
2140 .collect::<VecDeque<LandedVote>>();
2141 assert_eq!(
2142 process_new_vote_state(
2143 &mut vote_state,
2144 new_state,
2145 proposed_vote_state.2, None,
2147 0,
2148 proposed_vote_state.1, ),
2150 Ok(())
2151 );
2152
2153 assert_eq!(vote_state.credits(), proposed_vote_state.3 as u64);
2155 });
2156 }
2157
2158 #[test]
2159 fn test_process_new_vote_too_many_votes() {
2160 let mut vote_state1 = VoteState::default();
2161 let bad_votes: VecDeque<Lockout> = (0..=MAX_LOCKOUT_HISTORY)
2162 .map(|slot| {
2163 Lockout::new_with_confirmation_count(
2164 slot as Slot,
2165 (MAX_LOCKOUT_HISTORY - slot + 1) as u32,
2166 )
2167 })
2168 .collect();
2169
2170 let current_epoch = vote_state1.current_epoch();
2171 assert_eq!(
2172 process_new_vote_state_from_lockouts(
2173 &mut vote_state1,
2174 bad_votes,
2175 None,
2176 None,
2177 current_epoch,
2178 ),
2179 Err(VoteError::TooManyVotes)
2180 );
2181 }
2182
2183 #[test]
2184 fn test_process_new_vote_state_root_rollback() {
2185 let mut vote_state1 = VoteState::default();
2186 for i in 0..MAX_LOCKOUT_HISTORY + 2 {
2187 process_slot_vote_unchecked(&mut vote_state1, i as Slot);
2188 }
2189 assert_eq!(vote_state1.root_slot.unwrap(), 1);
2190
2191 let mut vote_state2 = vote_state1.clone();
2194 process_slot_vote_unchecked(&mut vote_state2, MAX_LOCKOUT_HISTORY as Slot + 3);
2195
2196 let lesser_root = Some(0);
2198
2199 let current_epoch = vote_state2.current_epoch();
2200 assert_eq!(
2201 process_new_vote_state(
2202 &mut vote_state1,
2203 vote_state2.votes.clone(),
2204 lesser_root,
2205 None,
2206 current_epoch,
2207 0,
2208 ),
2209 Err(VoteError::RootRollBack)
2210 );
2211
2212 let none_root = None;
2214 assert_eq!(
2215 process_new_vote_state(
2216 &mut vote_state1,
2217 vote_state2.votes.clone(),
2218 none_root,
2219 None,
2220 current_epoch,
2221 0,
2222 ),
2223 Err(VoteError::RootRollBack)
2224 );
2225 }
2226
2227 #[test]
2228 fn test_process_new_vote_state_zero_confirmations() {
2229 let mut vote_state1 = VoteState::default();
2230 let current_epoch = vote_state1.current_epoch();
2231
2232 let bad_votes: VecDeque<Lockout> = vec![
2233 Lockout::new_with_confirmation_count(0, 0),
2234 Lockout::new_with_confirmation_count(1, 1),
2235 ]
2236 .into_iter()
2237 .collect();
2238 assert_eq!(
2239 process_new_vote_state_from_lockouts(
2240 &mut vote_state1,
2241 bad_votes,
2242 None,
2243 None,
2244 current_epoch,
2245 ),
2246 Err(VoteError::ZeroConfirmations)
2247 );
2248
2249 let bad_votes: VecDeque<Lockout> = vec![
2250 Lockout::new_with_confirmation_count(0, 2),
2251 Lockout::new_with_confirmation_count(1, 0),
2252 ]
2253 .into_iter()
2254 .collect();
2255 assert_eq!(
2256 process_new_vote_state_from_lockouts(
2257 &mut vote_state1,
2258 bad_votes,
2259 None,
2260 None,
2261 current_epoch,
2262 ),
2263 Err(VoteError::ZeroConfirmations)
2264 );
2265 }
2266
2267 #[test]
2268 fn test_process_new_vote_state_confirmations_too_large() {
2269 let mut vote_state1 = VoteState::default();
2270 let current_epoch = vote_state1.current_epoch();
2271
2272 let good_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2273 0,
2274 MAX_LOCKOUT_HISTORY as u32,
2275 )]
2276 .into_iter()
2277 .collect();
2278
2279 process_new_vote_state_from_lockouts(
2280 &mut vote_state1,
2281 good_votes,
2282 None,
2283 None,
2284 current_epoch,
2285 )
2286 .unwrap();
2287
2288 let mut vote_state1 = VoteState::default();
2289 let bad_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2290 0,
2291 MAX_LOCKOUT_HISTORY as u32 + 1,
2292 )]
2293 .into_iter()
2294 .collect();
2295 assert_eq!(
2296 process_new_vote_state_from_lockouts(
2297 &mut vote_state1,
2298 bad_votes,
2299 None,
2300 None,
2301 current_epoch,
2302 ),
2303 Err(VoteError::ConfirmationTooLarge)
2304 );
2305 }
2306
2307 #[test]
2308 fn test_process_new_vote_state_slot_smaller_than_root() {
2309 let mut vote_state1 = VoteState::default();
2310 let current_epoch = vote_state1.current_epoch();
2311 let root_slot = 5;
2312
2313 let bad_votes: VecDeque<Lockout> = vec![
2314 Lockout::new_with_confirmation_count(root_slot, 2),
2315 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2316 ]
2317 .into_iter()
2318 .collect();
2319 assert_eq!(
2320 process_new_vote_state_from_lockouts(
2321 &mut vote_state1,
2322 bad_votes,
2323 Some(root_slot),
2324 None,
2325 current_epoch,
2326 ),
2327 Err(VoteError::SlotSmallerThanRoot)
2328 );
2329
2330 let bad_votes: VecDeque<Lockout> = vec![
2331 Lockout::new_with_confirmation_count(root_slot - 1, 2),
2332 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2333 ]
2334 .into_iter()
2335 .collect();
2336 assert_eq!(
2337 process_new_vote_state_from_lockouts(
2338 &mut vote_state1,
2339 bad_votes,
2340 Some(root_slot),
2341 None,
2342 current_epoch,
2343 ),
2344 Err(VoteError::SlotSmallerThanRoot)
2345 );
2346 }
2347
2348 #[test]
2349 fn test_process_new_vote_state_slots_not_ordered() {
2350 let mut vote_state1 = VoteState::default();
2351 let current_epoch = vote_state1.current_epoch();
2352
2353 let bad_votes: VecDeque<Lockout> = vec![
2354 Lockout::new_with_confirmation_count(1, 2),
2355 Lockout::new_with_confirmation_count(0, 1),
2356 ]
2357 .into_iter()
2358 .collect();
2359 assert_eq!(
2360 process_new_vote_state_from_lockouts(
2361 &mut vote_state1,
2362 bad_votes,
2363 None,
2364 None,
2365 current_epoch,
2366 ),
2367 Err(VoteError::SlotsNotOrdered)
2368 );
2369
2370 let bad_votes: VecDeque<Lockout> = vec![
2371 Lockout::new_with_confirmation_count(1, 2),
2372 Lockout::new_with_confirmation_count(1, 1),
2373 ]
2374 .into_iter()
2375 .collect();
2376 assert_eq!(
2377 process_new_vote_state_from_lockouts(
2378 &mut vote_state1,
2379 bad_votes,
2380 None,
2381 None,
2382 current_epoch,
2383 ),
2384 Err(VoteError::SlotsNotOrdered)
2385 );
2386 }
2387
2388 #[test]
2389 fn test_process_new_vote_state_confirmations_not_ordered() {
2390 let mut vote_state1 = VoteState::default();
2391 let current_epoch = vote_state1.current_epoch();
2392
2393 let bad_votes: VecDeque<Lockout> = vec![
2394 Lockout::new_with_confirmation_count(0, 1),
2395 Lockout::new_with_confirmation_count(1, 2),
2396 ]
2397 .into_iter()
2398 .collect();
2399 assert_eq!(
2400 process_new_vote_state_from_lockouts(
2401 &mut vote_state1,
2402 bad_votes,
2403 None,
2404 None,
2405 current_epoch,
2406 ),
2407 Err(VoteError::ConfirmationsNotOrdered)
2408 );
2409
2410 let bad_votes: VecDeque<Lockout> = vec![
2411 Lockout::new_with_confirmation_count(0, 1),
2412 Lockout::new_with_confirmation_count(1, 1),
2413 ]
2414 .into_iter()
2415 .collect();
2416 assert_eq!(
2417 process_new_vote_state_from_lockouts(
2418 &mut vote_state1,
2419 bad_votes,
2420 None,
2421 None,
2422 current_epoch,
2423 ),
2424 Err(VoteError::ConfirmationsNotOrdered)
2425 );
2426 }
2427
2428 #[test]
2429 fn test_process_new_vote_state_new_vote_state_lockout_mismatch() {
2430 let mut vote_state1 = VoteState::default();
2431 let current_epoch = vote_state1.current_epoch();
2432
2433 let bad_votes: VecDeque<Lockout> = vec![
2434 Lockout::new_with_confirmation_count(0, 2),
2435 Lockout::new_with_confirmation_count(7, 1),
2436 ]
2437 .into_iter()
2438 .collect();
2439
2440 assert_eq!(
2442 process_new_vote_state_from_lockouts(
2443 &mut vote_state1,
2444 bad_votes,
2445 None,
2446 None,
2447 current_epoch,
2448 ),
2449 Err(VoteError::NewVoteStateLockoutMismatch)
2450 );
2451 }
2452
2453 #[test]
2454 fn test_process_new_vote_state_confirmation_rollback() {
2455 let mut vote_state1 = VoteState::default();
2456 let current_epoch = vote_state1.current_epoch();
2457 let votes: VecDeque<Lockout> = vec![
2458 Lockout::new_with_confirmation_count(0, 4),
2459 Lockout::new_with_confirmation_count(1, 3),
2460 ]
2461 .into_iter()
2462 .collect();
2463 process_new_vote_state_from_lockouts(&mut vote_state1, votes, None, None, current_epoch)
2464 .unwrap();
2465
2466 let votes: VecDeque<Lockout> = vec![
2467 Lockout::new_with_confirmation_count(0, 4),
2468 Lockout::new_with_confirmation_count(1, 2),
2470 Lockout::new_with_confirmation_count(2, 1),
2471 ]
2472 .into_iter()
2473 .collect();
2474 assert_eq!(
2477 process_new_vote_state_from_lockouts(
2478 &mut vote_state1,
2479 votes,
2480 None,
2481 None,
2482 current_epoch,
2483 ),
2484 Err(VoteError::ConfirmationRollBack)
2485 );
2486 }
2487
2488 #[test]
2489 fn test_process_new_vote_state_root_progress() {
2490 let mut vote_state1 = VoteState::default();
2491 for i in 0..MAX_LOCKOUT_HISTORY {
2492 process_slot_vote_unchecked(&mut vote_state1, i as u64);
2493 }
2494
2495 assert!(vote_state1.root_slot.is_none());
2496 let mut vote_state2 = vote_state1.clone();
2497
2498 for new_vote in MAX_LOCKOUT_HISTORY + 1..=MAX_LOCKOUT_HISTORY + 2 {
2505 process_slot_vote_unchecked(&mut vote_state2, new_vote as Slot);
2506 assert_ne!(vote_state1.root_slot, vote_state2.root_slot);
2507
2508 process_new_vote_state(
2509 &mut vote_state1,
2510 vote_state2.votes.clone(),
2511 vote_state2.root_slot,
2512 None,
2513 vote_state2.current_epoch(),
2514 0,
2515 )
2516 .unwrap();
2517
2518 assert_eq!(vote_state1, vote_state2);
2519 }
2520 }
2521
2522 #[test]
2523 fn test_process_new_vote_state_same_slot_but_not_common_ancestor() {
2524 let mut vote_state1 = VoteState::default();
2543 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5]);
2544 assert_eq!(
2545 vote_state1
2546 .votes
2547 .iter()
2548 .map(|vote| vote.slot())
2549 .collect::<Vec<Slot>>(),
2550 vec![1, 5]
2551 );
2552
2553 let mut vote_state2 = VoteState::default();
2555 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2556 assert_eq!(
2557 vote_state2
2558 .votes
2559 .iter()
2560 .map(|vote| vote.slot())
2561 .collect::<Vec<Slot>>(),
2562 vec![1, 2, 3, 5, 7]
2563 );
2564
2565 process_new_vote_state(
2567 &mut vote_state1,
2568 vote_state2.votes.clone(),
2569 vote_state2.root_slot,
2570 None,
2571 vote_state2.current_epoch(),
2572 0,
2573 )
2574 .unwrap();
2575
2576 assert_eq!(vote_state1, vote_state2);
2577 }
2578
2579 #[test]
2580 fn test_process_new_vote_state_lockout_violation() {
2581 let mut vote_state1 = VoteState::default();
2583 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 4, 5]);
2584 assert_eq!(
2585 vote_state1
2586 .votes
2587 .iter()
2588 .map(|vote| vote.slot())
2589 .collect::<Vec<Slot>>(),
2590 vec![1, 2, 4, 5]
2591 );
2592
2593 let mut vote_state2 = VoteState::default();
2596 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2597 assert_eq!(
2598 vote_state2
2599 .votes
2600 .iter()
2601 .map(|vote| vote.slot())
2602 .collect::<Vec<Slot>>(),
2603 vec![1, 2, 3, 5, 7]
2604 );
2605
2606 assert_eq!(
2608 process_new_vote_state(
2609 &mut vote_state1,
2610 vote_state2.votes.clone(),
2611 vote_state2.root_slot,
2612 None,
2613 vote_state2.current_epoch(),
2614 0,
2615 ),
2616 Err(VoteError::LockoutConflict)
2617 );
2618 }
2619
2620 #[test]
2621 fn test_process_new_vote_state_lockout_violation2() {
2622 let mut vote_state1 = VoteState::default();
2624 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5, 6, 7]);
2625 assert_eq!(
2626 vote_state1
2627 .votes
2628 .iter()
2629 .map(|vote| vote.slot())
2630 .collect::<Vec<Slot>>(),
2631 vec![1, 5, 6, 7]
2632 );
2633
2634 let mut vote_state2 = VoteState::default();
2637 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 6, 8]);
2638 assert_eq!(
2639 vote_state2
2640 .votes
2641 .iter()
2642 .map(|vote| vote.slot())
2643 .collect::<Vec<Slot>>(),
2644 vec![1, 2, 3, 5, 6, 8]
2645 );
2646
2647 assert_eq!(
2650 process_new_vote_state(
2651 &mut vote_state1,
2652 vote_state2.votes.clone(),
2653 vote_state2.root_slot,
2654 None,
2655 vote_state2.current_epoch(),
2656 0,
2657 ),
2658 Err(VoteError::LockoutConflict)
2659 );
2660 }
2661
2662 #[test]
2663 fn test_process_new_vote_state_expired_ancestor_not_removed() {
2664 let mut vote_state1 = VoteState::default();
2666 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 3, 9]);
2667 assert_eq!(
2668 vote_state1
2669 .votes
2670 .iter()
2671 .map(|vote| vote.slot())
2672 .collect::<Vec<Slot>>(),
2673 vec![1, 9]
2674 );
2675
2676 let mut vote_state2 = vote_state1.clone();
2679 process_slot_vote_unchecked(&mut vote_state2, 10);
2680
2681 assert_eq!(vote_state2.votes[0].slot(), 1);
2684 assert_eq!(vote_state2.votes[0].lockout.last_locked_out_slot(), 9);
2685 assert_eq!(
2686 vote_state2
2687 .votes
2688 .iter()
2689 .map(|vote| vote.slot())
2690 .collect::<Vec<Slot>>(),
2691 vec![1, 9, 10]
2692 );
2693
2694 process_new_vote_state(
2696 &mut vote_state1,
2697 vote_state2.votes.clone(),
2698 vote_state2.root_slot,
2699 None,
2700 vote_state2.current_epoch(),
2701 0,
2702 )
2703 .unwrap();
2704 assert_eq!(vote_state1, vote_state2,);
2705 }
2706
2707 #[test]
2708 fn test_process_new_vote_current_state_contains_bigger_slots() {
2709 let mut vote_state1 = VoteState::default();
2710 process_slot_votes_unchecked(&mut vote_state1, &[6, 7, 8]);
2711 assert_eq!(
2712 vote_state1
2713 .votes
2714 .iter()
2715 .map(|vote| vote.slot())
2716 .collect::<Vec<Slot>>(),
2717 vec![6, 7, 8]
2718 );
2719
2720 let bad_votes: VecDeque<Lockout> = vec![
2722 Lockout::new_with_confirmation_count(2, 5),
2723 Lockout::new_with_confirmation_count(14, 1),
2725 ]
2726 .into_iter()
2727 .collect();
2728 let root = Some(1);
2729
2730 let current_epoch = vote_state1.current_epoch();
2731 assert_eq!(
2732 process_new_vote_state_from_lockouts(
2733 &mut vote_state1,
2734 bad_votes,
2735 root,
2736 None,
2737 current_epoch,
2738 ),
2739 Err(VoteError::LockoutConflict)
2740 );
2741
2742 let good_votes: VecDeque<LandedVote> = vec![
2743 Lockout::new_with_confirmation_count(2, 5).into(),
2744 Lockout::new_with_confirmation_count(15, 1).into(),
2745 ]
2746 .into_iter()
2747 .collect();
2748
2749 let current_epoch = vote_state1.current_epoch();
2750 process_new_vote_state(
2751 &mut vote_state1,
2752 good_votes.clone(),
2753 root,
2754 None,
2755 current_epoch,
2756 0,
2757 )
2758 .unwrap();
2759 assert_eq!(vote_state1.votes, good_votes);
2760 }
2761
2762 #[test]
2763 fn test_filter_old_votes() {
2764 let mut vote_state = VoteState::default();
2765 let old_vote_slot = 1;
2766 let vote = Vote::new(vec![old_vote_slot], Hash::default());
2767
2768 let slot_hashes = vec![(3, Hash::new_unique()), (2, Hash::new_unique())];
2771 assert_eq!(
2772 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
2773 Err(VoteError::VotesTooOldAllFiltered)
2774 );
2775
2776 let vote_slot = 2;
2779 let vote_slot_hash = slot_hashes
2780 .iter()
2781 .find(|(slot, _hash)| *slot == vote_slot)
2782 .unwrap()
2783 .1;
2784
2785 let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
2786 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0).unwrap();
2787 assert_eq!(
2788 vote_state
2789 .votes
2790 .into_iter()
2791 .map(|vote| vote.lockout)
2792 .collect::<Vec<Lockout>>(),
2793 vec![Lockout::new_with_confirmation_count(vote_slot, 1)]
2794 );
2795 }
2796
2797 fn build_slot_hashes(slots: Vec<Slot>) -> Vec<(Slot, Hash)> {
2798 slots
2799 .iter()
2800 .rev()
2801 .map(|x| (*x, Hash::new_unique()))
2802 .collect()
2803 }
2804
2805 fn build_vote_state(vote_slots: Vec<Slot>, slot_hashes: &[(Slot, Hash)]) -> VoteState {
2806 let mut vote_state = VoteState::default();
2807
2808 if !vote_slots.is_empty() {
2809 let vote_hash = slot_hashes
2810 .iter()
2811 .find(|(slot, _hash)| slot == vote_slots.last().unwrap())
2812 .unwrap()
2813 .1;
2814 let vote = Vote::new(vote_slots, vote_hash);
2815 process_vote_unfiltered(&mut vote_state, &vote.slots, &vote, slot_hashes, 0, 0)
2816 .unwrap();
2817 }
2818
2819 vote_state
2820 }
2821
2822 #[test]
2823 fn test_check_and_filter_proposed_vote_state_empty() {
2824 let empty_slot_hashes = build_slot_hashes(vec![]);
2825 let empty_vote_state = build_vote_state(vec![], &empty_slot_hashes);
2826
2827 let mut tower_sync = TowerSync::from(vec![]);
2829 assert_eq!(
2830 check_and_filter_proposed_vote_state(
2831 &empty_vote_state,
2832 &mut tower_sync.lockouts,
2833 &mut tower_sync.root,
2834 tower_sync.hash,
2835 &empty_slot_hashes
2836 ),
2837 Err(VoteError::EmptySlots),
2838 );
2839
2840 let mut tower_sync = TowerSync::from(vec![(0, 1)]);
2842 assert_eq!(
2843 check_and_filter_proposed_vote_state(
2844 &empty_vote_state,
2845 &mut tower_sync.lockouts,
2846 &mut tower_sync.root,
2847 tower_sync.hash,
2848 &empty_slot_hashes
2849 ),
2850 Err(VoteError::SlotsMismatch),
2851 );
2852 }
2853
2854 #[test]
2855 fn test_check_and_filter_proposed_vote_state_too_old() {
2856 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
2857 let latest_vote = 4;
2858 let vote_state = build_vote_state(vec![1, 2, 3, latest_vote], &slot_hashes);
2859
2860 let mut tower_sync = TowerSync::from(vec![(latest_vote, 1)]);
2863 assert_eq!(
2864 check_and_filter_proposed_vote_state(
2865 &vote_state,
2866 &mut tower_sync.lockouts,
2867 &mut tower_sync.root,
2868 tower_sync.hash,
2869 &slot_hashes
2870 ),
2871 Err(VoteError::VoteTooOld),
2872 );
2873
2874 let earliest_slot_in_history = latest_vote + 2;
2878 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history]);
2879 let mut tower_sync = TowerSync::from(vec![(earliest_slot_in_history - 1, 1)]);
2880 assert_eq!(
2881 check_and_filter_proposed_vote_state(
2882 &vote_state,
2883 &mut tower_sync.lockouts,
2884 &mut tower_sync.root,
2885 tower_sync.hash,
2886 &slot_hashes
2887 ),
2888 Err(VoteError::VoteTooOld),
2889 );
2890 }
2891
2892 fn run_test_check_and_filter_proposed_vote_state_older_than_history_root(
2893 earliest_slot_in_history: Slot,
2894 current_vote_state_slots: Vec<Slot>,
2895 current_vote_state_root: Option<Slot>,
2896 proposed_slots_and_lockouts: Vec<(Slot, u32)>,
2897 proposed_root: Slot,
2898 expected_root: Option<Slot>,
2899 expected_vote_state: Vec<Lockout>,
2900 ) {
2901 assert!(proposed_root < earliest_slot_in_history);
2902 assert_eq!(
2903 expected_root,
2904 current_vote_state_slots
2905 .iter()
2906 .rev()
2907 .find(|slot| **slot <= proposed_root)
2908 .cloned()
2909 );
2910 let latest_slot_in_history = proposed_slots_and_lockouts
2911 .last()
2912 .unwrap()
2913 .0
2914 .max(earliest_slot_in_history);
2915 let mut slot_hashes = build_slot_hashes(
2916 (current_vote_state_slots.first().copied().unwrap_or(0)..=latest_slot_in_history)
2917 .collect::<Vec<Slot>>(),
2918 );
2919
2920 let mut vote_state = build_vote_state(current_vote_state_slots, &slot_hashes);
2921 vote_state.root_slot = current_vote_state_root;
2922
2923 slot_hashes.retain(|slot| slot.0 >= earliest_slot_in_history);
2924 assert!(!proposed_slots_and_lockouts.is_empty());
2925 let proposed_hash = slot_hashes
2926 .iter()
2927 .find(|(slot, _hash)| *slot == proposed_slots_and_lockouts.last().unwrap().0)
2928 .unwrap()
2929 .1;
2930
2931 let mut tower_sync = TowerSync::from(proposed_slots_and_lockouts);
2935 tower_sync.hash = proposed_hash;
2936 tower_sync.root = Some(proposed_root);
2937 check_and_filter_proposed_vote_state(
2938 &vote_state,
2939 &mut tower_sync.lockouts,
2940 &mut tower_sync.root,
2941 tower_sync.hash,
2942 &slot_hashes,
2943 )
2944 .unwrap();
2945 assert_eq!(tower_sync.root, expected_root);
2946
2947 assert!(
2950 do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync.clone(),).is_ok()
2951 );
2952 assert_eq!(vote_state.root_slot, expected_root);
2953 assert_eq!(
2954 vote_state
2955 .votes
2956 .into_iter()
2957 .map(|vote| vote.lockout)
2958 .collect::<Vec<Lockout>>(),
2959 expected_vote_state,
2960 );
2961 }
2962
2963 #[test]
2964 fn test_check_and_filter_proposed_vote_state_older_than_history_root() {
2965 let earliest_slot_in_history = 5;
2968 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
2969 let current_vote_state_root = None;
2970 let proposed_slots_and_lockouts = vec![(5, 1)];
2971 let proposed_root = 4;
2972 let expected_root = Some(4);
2973 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
2974 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
2975 earliest_slot_in_history,
2976 current_vote_state_slots,
2977 current_vote_state_root,
2978 proposed_slots_and_lockouts,
2979 proposed_root,
2980 expected_root,
2981 expected_vote_state,
2982 );
2983
2984 let earliest_slot_in_history = 5;
2987 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
2988 let current_vote_state_root = Some(0);
2989 let proposed_slots_and_lockouts = vec![(5, 1)];
2990 let proposed_root = 4;
2991 let expected_root = Some(4);
2992 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
2993 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
2994 earliest_slot_in_history,
2995 current_vote_state_slots,
2996 current_vote_state_root,
2997 proposed_slots_and_lockouts,
2998 proposed_root,
2999 expected_root,
3000 expected_vote_state,
3001 );
3002
3003 let earliest_slot_in_history = 5;
3006 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3007 let current_vote_state_root = Some(0);
3008 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3009 let proposed_root = 3;
3010 let expected_root = Some(3);
3011 let expected_vote_state = vec![
3012 Lockout::new_with_confirmation_count(4, 2),
3013 Lockout::new_with_confirmation_count(5, 1),
3014 ];
3015 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3016 earliest_slot_in_history,
3017 current_vote_state_slots,
3018 current_vote_state_root,
3019 proposed_slots_and_lockouts,
3020 proposed_root,
3021 expected_root,
3022 expected_vote_state,
3023 );
3024
3025 let earliest_slot_in_history = 5;
3027 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 4];
3028 let current_vote_state_root = Some(0);
3029 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3030 let proposed_root = 3;
3031 let expected_root = Some(2);
3032 let expected_vote_state = vec![
3033 Lockout::new_with_confirmation_count(4, 2),
3034 Lockout::new_with_confirmation_count(5, 1),
3035 ];
3036 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3037 earliest_slot_in_history,
3038 current_vote_state_slots,
3039 current_vote_state_root,
3040 proposed_slots_and_lockouts,
3041 proposed_root,
3042 expected_root,
3043 expected_vote_state,
3044 );
3045
3046 let earliest_slot_in_history = 4;
3049 let current_vote_state_slots: Vec<Slot> = vec![3, 4];
3050 let current_vote_state_root = None;
3051 let proposed_slots_and_lockouts = vec![(3, 3), (4, 2), (5, 1)];
3052 let proposed_root = 2;
3053 let expected_root = None;
3054 let expected_vote_state = vec![
3055 Lockout::new_with_confirmation_count(3, 3),
3056 Lockout::new_with_confirmation_count(4, 2),
3057 Lockout::new_with_confirmation_count(5, 1),
3058 ];
3059 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3060 earliest_slot_in_history,
3061 current_vote_state_slots,
3062 current_vote_state_root,
3063 proposed_slots_and_lockouts,
3064 proposed_root,
3065 expected_root,
3066 expected_vote_state,
3067 );
3068
3069 let earliest_slot_in_history = 4;
3071 let current_vote_state_slots: Vec<Slot> = vec![];
3072 let current_vote_state_root = None;
3073 let proposed_slots_and_lockouts = vec![(5, 1)];
3074 let proposed_root = 2;
3075 let expected_root = None;
3076 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3077 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3078 earliest_slot_in_history,
3079 current_vote_state_slots,
3080 current_vote_state_root,
3081 proposed_slots_and_lockouts,
3082 proposed_root,
3083 expected_root,
3084 expected_vote_state,
3085 );
3086 }
3087
3088 #[test]
3089 fn test_check_and_filter_proposed_vote_state_slots_not_ordered() {
3090 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3091 let vote_state = build_vote_state(vec![1], &slot_hashes);
3092
3093 let vote_slot = 3;
3095 let vote_slot_hash = slot_hashes
3096 .iter()
3097 .find(|(slot, _hash)| *slot == vote_slot)
3098 .unwrap()
3099 .1;
3100 let mut tower_sync = TowerSync::from(vec![(2, 2), (1, 3), (vote_slot, 1)]);
3101 tower_sync.hash = vote_slot_hash;
3102 assert_eq!(
3103 check_and_filter_proposed_vote_state(
3104 &vote_state,
3105 &mut tower_sync.lockouts,
3106 &mut tower_sync.root,
3107 tower_sync.hash,
3108 &slot_hashes
3109 ),
3110 Err(VoteError::SlotsNotOrdered),
3111 );
3112
3113 let mut tower_sync = TowerSync::from(vec![(2, 2), (2, 2), (vote_slot, 1)]);
3115 tower_sync.hash = vote_slot_hash;
3116 assert_eq!(
3117 check_and_filter_proposed_vote_state(
3118 &vote_state,
3119 &mut tower_sync.lockouts,
3120 &mut tower_sync.root,
3121 tower_sync.hash,
3122 &slot_hashes
3123 ),
3124 Err(VoteError::SlotsNotOrdered),
3125 );
3126 }
3127
3128 #[test]
3129 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered() {
3130 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3131 let mut vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes);
3132
3133 let earliest_slot_in_history = 11;
3138 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3139 let vote_slot = 12;
3140 let vote_slot_hash = slot_hashes
3141 .iter()
3142 .find(|(slot, _hash)| *slot == vote_slot)
3143 .unwrap()
3144 .1;
3145 let missing_older_than_history_slot = earliest_slot_in_history - 1;
3146 let mut tower_sync = TowerSync::from(vec![
3147 (1, 4),
3148 (missing_older_than_history_slot, 2),
3149 (vote_slot, 3),
3150 ]);
3151 tower_sync.hash = vote_slot_hash;
3152 check_and_filter_proposed_vote_state(
3153 &vote_state,
3154 &mut tower_sync.lockouts,
3155 &mut tower_sync.root,
3156 tower_sync.hash,
3157 &slot_hashes,
3158 )
3159 .unwrap();
3160
3161 assert_eq!(
3163 tower_sync
3164 .clone()
3165 .lockouts
3166 .into_iter()
3167 .collect::<Vec<Lockout>>(),
3168 vec![
3169 Lockout::new_with_confirmation_count(1, 4),
3170 Lockout::new_with_confirmation_count(vote_slot, 3)
3171 ]
3172 );
3173 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3174 }
3175
3176 #[test]
3177 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_not_filtered() {
3178 let slot_hashes = build_slot_hashes(vec![4]);
3179 let mut vote_state = build_vote_state(vec![4], &slot_hashes);
3180
3181 let earliest_slot_in_history = 11;
3186 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3187 let vote_slot = 12;
3188 let vote_slot_hash = slot_hashes
3189 .iter()
3190 .find(|(slot, _hash)| *slot == vote_slot)
3191 .unwrap()
3192 .1;
3193 let existing_older_than_history_slot = 4;
3194 let mut tower_sync =
3195 TowerSync::from(vec![(existing_older_than_history_slot, 3), (vote_slot, 2)]);
3196 tower_sync.hash = vote_slot_hash;
3197 check_and_filter_proposed_vote_state(
3198 &vote_state,
3199 &mut tower_sync.lockouts,
3200 &mut tower_sync.root,
3201 tower_sync.hash,
3202 &slot_hashes,
3203 )
3204 .unwrap();
3205 assert_eq!(tower_sync.lockouts.len(), 2);
3207 assert_eq!(
3208 tower_sync
3209 .clone()
3210 .lockouts
3211 .into_iter()
3212 .collect::<Vec<Lockout>>(),
3213 vec![
3214 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3215 Lockout::new_with_confirmation_count(vote_slot, 2)
3216 ]
3217 );
3218 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3219 }
3220
3221 #[test]
3222 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered_and_not_filtered(
3223 ) {
3224 let slot_hashes = build_slot_hashes(vec![6]);
3225 let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3226
3227 let earliest_slot_in_history = 11;
3238 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3239 let vote_slot = 14;
3240 let vote_slot_hash = slot_hashes
3241 .iter()
3242 .find(|(slot, _hash)| *slot == vote_slot)
3243 .unwrap()
3244 .1;
3245
3246 let missing_older_than_history_slot = 4;
3247 let existing_older_than_history_slot = 6;
3248
3249 let mut tower_sync = TowerSync::from(vec![
3250 (missing_older_than_history_slot, 4),
3251 (existing_older_than_history_slot, 3),
3252 (12, 2),
3253 (vote_slot, 1),
3254 ]);
3255 tower_sync.hash = vote_slot_hash;
3256 check_and_filter_proposed_vote_state(
3257 &vote_state,
3258 &mut tower_sync.lockouts,
3259 &mut tower_sync.root,
3260 tower_sync.hash,
3261 &slot_hashes,
3262 )
3263 .unwrap();
3264 assert_eq!(tower_sync.lockouts.len(), 3);
3265 assert_eq!(
3266 tower_sync
3267 .clone()
3268 .lockouts
3269 .into_iter()
3270 .collect::<Vec<Lockout>>(),
3271 vec![
3272 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3273 Lockout::new_with_confirmation_count(12, 2),
3274 Lockout::new_with_confirmation_count(vote_slot, 1)
3275 ]
3276 );
3277 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3278 }
3279
3280 #[test]
3281 fn test_check_and_filter_proposed_vote_state_slot_not_on_fork() {
3282 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3283 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3284
3285 let missing_vote_slot = 3;
3291
3292 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3295 let vote_slot_hash = slot_hashes
3296 .iter()
3297 .find(|(slot, _hash)| *slot == vote_slot)
3298 .unwrap()
3299 .1;
3300 let mut tower_sync = TowerSync::from(vec![(missing_vote_slot, 2), (vote_slot, 3)]);
3301 tower_sync.hash = vote_slot_hash;
3302 assert_eq!(
3303 check_and_filter_proposed_vote_state(
3304 &vote_state,
3305 &mut tower_sync.lockouts,
3306 &mut tower_sync.root,
3307 tower_sync.hash,
3308 &slot_hashes
3309 ),
3310 Err(VoteError::SlotsMismatch),
3311 );
3312
3313 let missing_vote_slot = 7;
3315 let mut tower_sync = TowerSync::from(vec![
3316 (2, 5),
3317 (4, 4),
3318 (6, 3),
3319 (missing_vote_slot, 2),
3320 (vote_slot, 1),
3321 ]);
3322 tower_sync.hash = vote_slot_hash;
3323 assert_eq!(
3324 check_and_filter_proposed_vote_state(
3325 &vote_state,
3326 &mut tower_sync.lockouts,
3327 &mut tower_sync.root,
3328 tower_sync.hash,
3329 &slot_hashes
3330 ),
3331 Err(VoteError::SlotsMismatch),
3332 );
3333 }
3334
3335 #[test]
3336 fn test_check_and_filter_proposed_vote_state_root_on_different_fork() {
3337 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3338 let vote_state = build_vote_state(vec![6], &slot_hashes);
3339
3340 let new_root = 3;
3346
3347 let vote_slot = 8;
3350 assert_eq!(vote_slot, slot_hashes.first().unwrap().0);
3351 let vote_slot_hash = slot_hashes
3352 .iter()
3353 .find(|(slot, _hash)| *slot == vote_slot)
3354 .unwrap()
3355 .1;
3356 let mut tower_sync = TowerSync::from(vec![(vote_slot, 1)]);
3357 tower_sync.hash = vote_slot_hash;
3358 tower_sync.root = Some(new_root);
3359 assert_eq!(
3360 check_and_filter_proposed_vote_state(
3361 &vote_state,
3362 &mut tower_sync.lockouts,
3363 &mut tower_sync.root,
3364 tower_sync.hash,
3365 &slot_hashes
3366 ),
3367 Err(VoteError::RootOnDifferentFork),
3368 );
3369 }
3370
3371 #[test]
3372 fn test_check_and_filter_proposed_vote_state_slot_newer_than_slot_history() {
3373 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3374 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3375
3376 let missing_vote_slot = slot_hashes.first().unwrap().0 + 1;
3382 let vote_slot_hash = Hash::new_unique();
3383 let mut tower_sync = TowerSync::from(vec![(8, 2), (missing_vote_slot, 3)]);
3384 tower_sync.hash = vote_slot_hash;
3385 assert_eq!(
3386 check_and_filter_proposed_vote_state(
3387 &vote_state,
3388 &mut tower_sync.lockouts,
3389 &mut tower_sync.root,
3390 tower_sync.hash,
3391 &slot_hashes
3392 ),
3393 Err(VoteError::SlotsMismatch),
3394 );
3395 }
3396
3397 #[test]
3398 fn test_check_and_filter_proposed_vote_state_slot_all_slot_hashes_in_update_ok() {
3399 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3400 let mut vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3401
3402 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3408 let vote_slot_hash = slot_hashes
3409 .iter()
3410 .find(|(slot, _hash)| *slot == vote_slot)
3411 .unwrap()
3412 .1;
3413 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3414 tower_sync.hash = vote_slot_hash;
3415 check_and_filter_proposed_vote_state(
3416 &vote_state,
3417 &mut tower_sync.lockouts,
3418 &mut tower_sync.root,
3419 tower_sync.hash,
3420 &slot_hashes,
3421 )
3422 .unwrap();
3423
3424 assert_eq!(
3426 tower_sync
3427 .clone()
3428 .lockouts
3429 .into_iter()
3430 .collect::<Vec<Lockout>>(),
3431 vec![
3432 Lockout::new_with_confirmation_count(2, 4),
3433 Lockout::new_with_confirmation_count(4, 3),
3434 Lockout::new_with_confirmation_count(6, 2),
3435 Lockout::new_with_confirmation_count(vote_slot, 1)
3436 ]
3437 );
3438
3439 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3440 }
3441
3442 #[test]
3443 fn test_check_and_filter_proposed_vote_state_slot_some_slot_hashes_in_update_ok() {
3444 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3445 let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3446
3447 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3453 let vote_slot_hash = slot_hashes
3454 .iter()
3455 .find(|(slot, _hash)| *slot == vote_slot)
3456 .unwrap()
3457 .1;
3458 let mut tower_sync = TowerSync::from(vec![(4, 2), (vote_slot, 1)]);
3459 tower_sync.hash = vote_slot_hash;
3460 check_and_filter_proposed_vote_state(
3461 &vote_state,
3462 &mut tower_sync.lockouts,
3463 &mut tower_sync.root,
3464 tower_sync.hash,
3465 &slot_hashes,
3466 )
3467 .unwrap();
3468
3469 assert_eq!(
3471 tower_sync
3472 .clone()
3473 .lockouts
3474 .into_iter()
3475 .collect::<Vec<Lockout>>(),
3476 vec![
3477 Lockout::new_with_confirmation_count(4, 2),
3478 Lockout::new_with_confirmation_count(vote_slot, 1)
3479 ]
3480 );
3481
3482 assert_eq!(
3486 do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,),
3487 Err(VoteError::LockoutConflict)
3488 );
3489 }
3490
3491 #[test]
3492 fn test_check_and_filter_proposed_vote_state_slot_hash_mismatch() {
3493 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3494 let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3495
3496 let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3501 let vote_slot_hash = Hash::new_unique();
3502 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3503 tower_sync.hash = vote_slot_hash;
3504 assert_eq!(
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 Err(VoteError::SlotHashMismatch),
3513 );
3514 }
3515
3516 #[test_case(0, true; "first slot")]
3517 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3518 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3519 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3520 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3521 fn test_epoch_half_check(slot: Slot, expected_allowed: bool) {
3522 let epoch_schedule = EpochSchedule::without_warmup();
3523 assert_eq!(
3524 is_commission_update_allowed(slot, &epoch_schedule),
3525 expected_allowed
3526 );
3527 }
3528
3529 #[test]
3530 fn test_warmup_epoch_half_check_with_warmup() {
3531 let epoch_schedule = EpochSchedule::default();
3532 let first_normal_slot = epoch_schedule.first_normal_slot;
3533 assert!(is_commission_update_allowed(0, &epoch_schedule));
3535 assert!(is_commission_update_allowed(
3538 first_normal_slot - 1,
3539 &epoch_schedule
3540 ));
3541 }
3542
3543 #[test_case(0, true; "first slot")]
3544 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3545 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3546 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3547 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3548 fn test_epoch_half_check_with_warmup(slot: Slot, expected_allowed: bool) {
3549 let epoch_schedule = EpochSchedule::default();
3550 let first_normal_slot = epoch_schedule.first_normal_slot;
3551 assert_eq!(
3552 is_commission_update_allowed(first_normal_slot.saturating_add(slot), &epoch_schedule),
3553 expected_allowed
3554 );
3555 }
3556}