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