1#[cfg(feature = "dev-context-only-utils")]
5pub mod handler;
6#[cfg(not(feature = "dev-context-only-utils"))]
7pub(crate) mod handler;
8
9pub use solana_vote_interface::state::{vote_state_versions::*, *};
10use {
11 handler::{VoteStateHandle, VoteStateHandler, VoteStateTargetVersion},
12 log::*,
13 solana_account::{AccountSharedData, WritableAccount},
14 solana_clock::{Clock, Epoch, Slot},
15 solana_epoch_schedule::EpochSchedule,
16 solana_hash::Hash,
17 solana_instruction::error::InstructionError,
18 solana_pubkey::Pubkey,
19 solana_rent::Rent,
20 solana_slot_hashes::SlotHash,
21 solana_transaction_context::{BorrowedInstructionAccount, IndexOfAccount, InstructionContext},
22 solana_vote_interface::{error::VoteError, program::id},
23 std::{
24 cmp::Ordering,
25 collections::{HashSet, VecDeque},
26 },
27};
28
29enum PreserveBehaviorInHandlerHelper {
32 V3 { check_initialized: bool },
33 V4,
34}
35
36impl PreserveBehaviorInHandlerHelper {
37 fn new(target_version: VoteStateTargetVersion, check_initialized: bool) -> Self {
38 match target_version {
39 VoteStateTargetVersion::V3 => Self::V3 { check_initialized },
40 VoteStateTargetVersion::V4 => Self::V4,
41 }
42 }
43}
44
45fn get_vote_state_handler_checked(
46 vote_account: &BorrowedInstructionAccount,
47 preserve_behavior: PreserveBehaviorInHandlerHelper,
48) -> Result<VoteStateHandler, InstructionError> {
49 match preserve_behavior {
50 PreserveBehaviorInHandlerHelper::V3 { check_initialized } => {
51 let vote_state = VoteStateV3::deserialize(vote_account.get_data())?;
58 if check_initialized && vote_state.is_uninitialized() {
59 return Err(InstructionError::UninitializedAccount);
60 }
61 Ok(VoteStateHandler::new_v3(vote_state))
62 }
63 PreserveBehaviorInHandlerHelper::V4 => {
64 let versioned = VoteStateVersions::deserialize(vote_account.get_data())?;
69 if versioned.is_uninitialized() {
70 return Err(InstructionError::UninitializedAccount);
71 }
72 let vote_state =
73 handler::try_convert_to_vote_state_v4(versioned, vote_account.get_key())?;
74 Ok(VoteStateHandler::new_v4(vote_state))
75 }
76 }
77}
78
79fn check_and_filter_proposed_vote_state(
83 vote_state: &VoteStateHandler,
84 proposed_lockouts: &mut VecDeque<Lockout>,
85 proposed_root: &mut Option<Slot>,
86 proposed_hash: Hash,
87 slot_hashes: &[(Slot, Hash)],
88) -> Result<(), VoteError> {
89 if proposed_lockouts.is_empty() {
90 return Err(VoteError::EmptySlots);
91 }
92
93 let last_proposed_slot = proposed_lockouts
94 .back()
95 .expect("must be nonempty, checked above")
96 .slot();
97
98 if let Some(last_vote_slot) = vote_state.votes().back().map(|lockout| lockout.slot()) {
100 if last_proposed_slot <= last_vote_slot {
101 return Err(VoteError::VoteTooOld);
102 }
103 }
104
105 if slot_hashes.is_empty() {
106 return Err(VoteError::SlotsMismatch);
107 }
108 let earliest_slot_hash_in_history = slot_hashes.last().unwrap().0;
109
110 if last_proposed_slot < earliest_slot_hash_in_history {
112 return Err(VoteError::VoteTooOld);
115 }
116
117 if let Some(root) = *proposed_root {
119 if root < earliest_slot_hash_in_history {
124 *proposed_root = vote_state.root_slot();
126
127 for vote in vote_state.votes().iter().rev() {
129 if vote.slot() <= root {
130 *proposed_root = Some(vote.slot());
131 break;
132 }
133 }
134 }
135 }
136
137 let mut root_to_check = *proposed_root;
141 let mut proposed_lockouts_index = 0;
142
143 let mut slot_hashes_index = slot_hashes.len();
146
147 let mut proposed_lockouts_indices_to_filter = vec![];
148
149 while proposed_lockouts_index < proposed_lockouts.len() && slot_hashes_index > 0 {
161 let proposed_vote_slot = if let Some(root) = root_to_check {
162 root
163 } else {
164 proposed_lockouts[proposed_lockouts_index].slot()
165 };
166 if root_to_check.is_none()
167 && proposed_lockouts_index > 0
168 && proposed_vote_slot
169 <= proposed_lockouts[proposed_lockouts_index.checked_sub(1).expect(
170 "`proposed_lockouts_index` is positive when checking `SlotsNotOrdered`",
171 )]
172 .slot()
173 {
174 return Err(VoteError::SlotsNotOrdered);
175 }
176 let ancestor_slot = slot_hashes[slot_hashes_index
177 .checked_sub(1)
178 .expect("`slot_hashes_index` is positive when computing `ancestor_slot`")]
179 .0;
180
181 match proposed_vote_slot.cmp(&ancestor_slot) {
184 Ordering::Less => {
185 if slot_hashes_index == slot_hashes.len() {
186 if proposed_vote_slot >= earliest_slot_hash_in_history {
189 return Err(VoteError::AssertionFailed);
190 }
191 if !vote_state.contains_slot(proposed_vote_slot) && root_to_check.is_none() {
192 proposed_lockouts_indices_to_filter.push(proposed_lockouts_index);
198 }
199 if let Some(new_proposed_root) = root_to_check {
200 assert_eq!(new_proposed_root, proposed_vote_slot);
204 if new_proposed_root >= earliest_slot_hash_in_history {
208 return Err(VoteError::AssertionFailed);
209 }
210 root_to_check = None;
211 } else {
212 proposed_lockouts_index = proposed_lockouts_index.checked_add(1).expect(
213 "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when \
214 `proposed_vote_slot` is too old to be in SlotHashes history",
215 );
216 }
217 continue;
218 } else {
219 if root_to_check.is_some() {
223 return Err(VoteError::RootOnDifferentFork);
224 } else {
225 return Err(VoteError::SlotsMismatch);
226 }
227 }
228 }
229 Ordering::Greater => {
230 slot_hashes_index = slot_hashes_index.checked_sub(1).expect(
232 "`slot_hashes_index` is positive when finding newer slots in SlotHashes \
233 history",
234 );
235 continue;
236 }
237 Ordering::Equal => {
238 if root_to_check.is_some() {
242 root_to_check = None;
243 } else {
244 proposed_lockouts_index = proposed_lockouts_index.checked_add(1).expect(
245 "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when match \
246 is found in SlotHashes history",
247 );
248 slot_hashes_index = slot_hashes_index.checked_sub(1).expect(
249 "`slot_hashes_index` is positive when match is found in SlotHashes history",
250 );
251 }
252 }
253 }
254 }
255
256 if proposed_lockouts_index != proposed_lockouts.len() {
257 return Err(VoteError::SlotsMismatch);
259 }
260
261 assert_eq!(last_proposed_slot, slot_hashes[slot_hashes_index].0);
284
285 if slot_hashes[slot_hashes_index].1 != proposed_hash {
286 warn!(
290 "{} dropped vote {:?} root {:?} failed to match hash {} {}",
291 vote_state.node_pubkey(),
292 proposed_lockouts,
293 proposed_root,
294 proposed_hash,
295 slot_hashes[slot_hashes_index].1
296 );
297 #[cfg(feature = "metrics")]
298 inc_new_counter_info!("dropped-vote-hash", 1);
299 return Err(VoteError::SlotHashMismatch);
300 }
301
302 let mut proposed_lockouts_index = 0;
304 let mut filter_votes_index = 0;
305 proposed_lockouts.retain(|_lockout| {
306 let should_retain = if filter_votes_index == proposed_lockouts_indices_to_filter.len() {
307 true
308 } else if proposed_lockouts_index == proposed_lockouts_indices_to_filter[filter_votes_index]
309 {
310 filter_votes_index = filter_votes_index.checked_add(1).unwrap();
311 false
312 } else {
313 true
314 };
315
316 proposed_lockouts_index = proposed_lockouts_index.checked_add(1).expect(
317 "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when filtering out \
318 irrelevant votes",
319 );
320 should_retain
321 });
322
323 Ok(())
324}
325
326fn check_slots_are_valid<T: VoteStateHandle>(
327 vote_state: &T,
328 vote_slots: &[Slot],
329 vote_hash: &Hash,
330 slot_hashes: &[(Slot, Hash)],
331) -> Result<(), VoteError> {
332 let mut i = 0;
335
336 let mut j = slot_hashes.len();
339
340 while i < vote_slots.len() && j > 0 {
349 if vote_state
352 .last_voted_slot()
353 .is_some_and(|last_voted_slot| vote_slots[i] <= last_voted_slot)
354 {
355 i = i
356 .checked_add(1)
357 .expect("`i` is bounded by `MAX_LOCKOUT_HISTORY` when finding larger slots");
358 continue;
359 }
360
361 if vote_slots[i] != slot_hashes[j.checked_sub(1).expect("`j` is positive")].0 {
363 j = j
365 .checked_sub(1)
366 .expect("`j` is positive when finding newer slots");
367 continue;
368 }
369
370 i = i
373 .checked_add(1)
374 .expect("`i` is bounded by `MAX_LOCKOUT_HISTORY` when hash is found");
375 j = j
376 .checked_sub(1)
377 .expect("`j` is positive when hash is found");
378 }
379
380 if j == slot_hashes.len() {
381 debug!(
385 "{} dropped vote slots {:?}, vote hash: {:?} slot hashes:SlotHash {:?}, too old ",
386 vote_state.node_pubkey(),
387 vote_slots,
388 vote_hash,
389 slot_hashes
390 );
391 return Err(VoteError::VoteTooOld);
392 }
393 if i != vote_slots.len() {
394 info!(
397 "{} dropped vote slots {:?} failed to match slot hashes: {:?}",
398 vote_state.node_pubkey(),
399 vote_slots,
400 slot_hashes,
401 );
402 return Err(VoteError::SlotsMismatch);
403 }
404 if &slot_hashes[j].1 != vote_hash {
405 warn!(
409 "{} dropped vote slots {:?} failed to match hash {} {}",
410 vote_state.node_pubkey(),
411 vote_slots,
412 vote_hash,
413 slot_hashes[j].1
414 );
415 return Err(VoteError::SlotHashMismatch);
416 }
417 Ok(())
418}
419
420pub fn process_new_vote_state(
458 vote_state: &mut VoteStateHandler,
459 mut new_state: VecDeque<LandedVote>,
460 new_root: Option<Slot>,
461 timestamp: Option<i64>,
462 epoch: Epoch,
463 current_slot: Slot,
464) -> Result<(), VoteError> {
465 assert!(!new_state.is_empty());
466 if new_state.len() > MAX_LOCKOUT_HISTORY {
467 return Err(VoteError::TooManyVotes);
468 }
469
470 match (new_root, vote_state.root_slot()) {
471 (Some(new_root), Some(current_root)) => {
472 if new_root < current_root {
473 return Err(VoteError::RootRollBack);
474 }
475 }
476 (None, Some(_)) => {
477 return Err(VoteError::RootRollBack);
478 }
479 _ => (),
480 }
481
482 let mut previous_vote: Option<&LandedVote> = None;
483
484 for vote in &new_state {
489 if vote.confirmation_count() == 0 {
490 return Err(VoteError::ZeroConfirmations);
491 } else if vote.confirmation_count() > MAX_LOCKOUT_HISTORY as u32 {
492 return Err(VoteError::ConfirmationTooLarge);
493 } else if let Some(new_root) = new_root {
494 if vote.slot() <= new_root
495 &&
496 new_root != Slot::default()
501 {
502 return Err(VoteError::SlotSmallerThanRoot);
503 }
504 }
505
506 if let Some(previous_vote) = previous_vote {
507 if previous_vote.slot() >= vote.slot() {
508 return Err(VoteError::SlotsNotOrdered);
509 } else if previous_vote.confirmation_count() <= vote.confirmation_count() {
510 return Err(VoteError::ConfirmationsNotOrdered);
511 } else if vote.slot() > previous_vote.lockout.last_locked_out_slot() {
512 return Err(VoteError::NewVoteStateLockoutMismatch);
513 }
514 }
515 previous_vote = Some(vote);
516 }
517
518 let mut current_vote_state_index: usize = 0;
521 let mut new_vote_state_index = 0;
522
523 let mut earned_credits = 0_u64;
525
526 if let Some(new_root) = new_root {
527 for current_vote in vote_state.votes() {
528 if current_vote.slot() <= new_root {
531 earned_credits = earned_credits
532 .checked_add(vote_state.credits_for_vote_at_index(current_vote_state_index))
533 .expect("`earned_credits` does not overflow");
534 current_vote_state_index = current_vote_state_index.checked_add(1).expect(
535 "`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when \
536 processing new root",
537 );
538 continue;
539 }
540
541 break;
542 }
543 }
544
545 while current_vote_state_index < vote_state.votes().len()
565 && new_vote_state_index < new_state.len()
566 {
567 let current_vote = &vote_state.votes()[current_vote_state_index];
568 let new_vote = &mut new_state[new_vote_state_index];
569
570 match current_vote.slot().cmp(&new_vote.slot()) {
574 Ordering::Less => {
575 if current_vote.lockout.last_locked_out_slot() >= new_vote.slot() {
576 return Err(VoteError::LockoutConflict);
577 }
578 current_vote_state_index = current_vote_state_index.checked_add(1).expect(
579 "`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is \
580 less than proposed",
581 );
582 }
583 Ordering::Equal => {
584 if new_vote.confirmation_count() < current_vote.confirmation_count() {
587 return Err(VoteError::ConfirmationRollBack);
588 }
589
590 new_vote.latency = vote_state.votes()[current_vote_state_index].latency;
592
593 current_vote_state_index = current_vote_state_index.checked_add(1).expect(
594 "`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is \
595 equal to proposed",
596 );
597 new_vote_state_index = new_vote_state_index.checked_add(1).expect(
598 "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is \
599 equal to proposed",
600 );
601 }
602 Ordering::Greater => {
603 new_vote_state_index = new_vote_state_index.checked_add(1).expect(
604 "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is \
605 greater than proposed",
606 );
607 }
608 }
609 }
610
611 for new_vote in new_state.iter_mut() {
617 if new_vote.latency == 0 {
618 new_vote.latency = handler::compute_vote_latency(new_vote.slot(), current_slot);
619 }
620 }
621
622 if vote_state.root_slot() != new_root {
623 vote_state.increment_credits(epoch, earned_credits);
627 }
628 if let Some(timestamp) = timestamp {
629 let last_slot = new_state.back().unwrap().slot();
630 vote_state.process_timestamp(last_slot, timestamp)?;
631 }
632 vote_state.set_root_slot(new_root);
633 vote_state.set_votes(new_state);
634
635 Ok(())
636}
637
638pub fn process_vote_unfiltered<T: VoteStateHandle>(
639 vote_state: &mut T,
640 vote_slots: &[Slot],
641 vote: &Vote,
642 slot_hashes: &[SlotHash],
643 epoch: Epoch,
644 current_slot: Slot,
645) -> Result<(), VoteError> {
646 check_slots_are_valid(vote_state, vote_slots, &vote.hash, slot_hashes)?;
647 vote_slots
648 .iter()
649 .for_each(|s| vote_state.process_next_vote_slot(*s, epoch, current_slot));
650 Ok(())
651}
652
653pub fn process_vote(
654 vote_state: &mut VoteStateHandler,
655 vote: &Vote,
656 slot_hashes: &[SlotHash],
657 epoch: Epoch,
658 current_slot: Slot,
659) -> Result<(), VoteError> {
660 if vote.slots.is_empty() {
661 return Err(VoteError::EmptySlots);
662 }
663 let earliest_slot_in_history = slot_hashes.last().map(|(slot, _hash)| *slot).unwrap_or(0);
664 let vote_slots = vote
665 .slots
666 .iter()
667 .filter(|slot| **slot >= earliest_slot_in_history)
668 .cloned()
669 .collect::<Vec<Slot>>();
670 if vote_slots.is_empty() {
671 return Err(VoteError::VotesTooOldAllFiltered);
672 }
673 process_vote_unfiltered(
674 vote_state,
675 &vote_slots,
676 vote,
677 slot_hashes,
678 epoch,
679 current_slot,
680 )
681}
682
683pub fn process_vote_unchecked<T: VoteStateHandle>(
685 vote_state: &mut T,
686 vote: Vote,
687) -> Result<(), VoteError> {
688 if vote.slots.is_empty() {
689 return Err(VoteError::EmptySlots);
690 }
691 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
692 process_vote_unfiltered(
693 vote_state,
694 &vote.slots,
695 &vote,
696 &slot_hashes,
697 vote_state.current_epoch(),
698 0,
699 )
700}
701
702#[cfg(test)]
703pub fn process_slot_votes_unchecked<T: VoteStateHandle>(vote_state: &mut T, slots: &[Slot]) {
704 for slot in slots {
705 process_slot_vote_unchecked(vote_state, *slot);
706 }
707}
708
709pub fn process_slot_vote_unchecked<T: VoteStateHandle>(vote_state: &mut T, slot: Slot) {
710 let _ = process_vote_unchecked(vote_state, Vote::new(vec![slot], Hash::default()));
711}
712
713pub fn authorize<S: std::hash::BuildHasher>(
717 vote_account: &mut BorrowedInstructionAccount,
718 target_version: VoteStateTargetVersion,
719 authorized: &Pubkey,
720 vote_authorize: VoteAuthorize,
721 signers: &HashSet<Pubkey, S>,
722 clock: &Clock,
723) -> Result<(), InstructionError> {
724 let mut vote_state = get_vote_state_handler_checked(
725 vote_account,
726 PreserveBehaviorInHandlerHelper::new(target_version, false),
727 )?;
728
729 match vote_authorize {
730 VoteAuthorize::Voter => {
731 let authorized_withdrawer_signer =
732 verify_authorized_signer(vote_state.authorized_withdrawer(), signers).is_ok();
733
734 vote_state.set_new_authorized_voter(
735 authorized,
736 clock.epoch,
737 clock
738 .leader_schedule_epoch
739 .checked_add(1)
740 .ok_or(InstructionError::InvalidAccountData)?,
741 |epoch_authorized_voter| {
742 if authorized_withdrawer_signer {
744 Ok(())
745 } else {
746 verify_authorized_signer(&epoch_authorized_voter, signers)
747 }
748 },
749 )?;
750 }
751 VoteAuthorize::Withdrawer => {
752 verify_authorized_signer(vote_state.authorized_withdrawer(), signers)?;
754 vote_state.set_authorized_withdrawer(*authorized);
755 }
756 }
757
758 vote_state.set_vote_account_state(vote_account)
759}
760
761pub fn update_validator_identity<S: std::hash::BuildHasher>(
763 vote_account: &mut BorrowedInstructionAccount,
764 target_version: VoteStateTargetVersion,
765 node_pubkey: &Pubkey,
766 signers: &HashSet<Pubkey, S>,
767) -> Result<(), InstructionError> {
768 let mut vote_state = get_vote_state_handler_checked(
769 vote_account,
770 PreserveBehaviorInHandlerHelper::new(target_version, false),
771 )?;
772
773 verify_authorized_signer(vote_state.authorized_withdrawer(), signers)?;
775
776 verify_authorized_signer(node_pubkey, signers)?;
778
779 vote_state.set_node_pubkey(*node_pubkey);
780 vote_state.set_block_revenue_collector(*node_pubkey);
783
784 vote_state.set_vote_account_state(vote_account)
785}
786
787pub fn update_commission<S: std::hash::BuildHasher>(
789 vote_account: &mut BorrowedInstructionAccount,
790 target_version: VoteStateTargetVersion,
791 commission: u8,
792 signers: &HashSet<Pubkey, S>,
793 epoch_schedule: &EpochSchedule,
794 clock: &Clock,
795) -> Result<(), InstructionError> {
796 let vote_state_result = get_vote_state_handler_checked(
797 vote_account,
798 PreserveBehaviorInHandlerHelper::new(target_version, false),
799 );
800 let enforce_commission_update_rule = if let Ok(decoded_vote_state) = &vote_state_result {
801 commission > decoded_vote_state.commission()
802 } else {
803 true
804 };
805
806 if enforce_commission_update_rule && !is_commission_update_allowed(clock.slot, epoch_schedule) {
807 return Err(VoteError::CommissionUpdateTooLate.into());
808 }
809
810 let mut vote_state = vote_state_result?;
811
812 verify_authorized_signer(vote_state.authorized_withdrawer(), signers)?;
814
815 vote_state.set_commission(commission);
816
817 vote_state.set_vote_account_state(vote_account)
818}
819
820pub fn is_commission_update_allowed(slot: Slot, epoch_schedule: &EpochSchedule) -> bool {
823 if let Some(relative_slot) = slot
825 .saturating_sub(epoch_schedule.first_normal_slot)
826 .checked_rem(epoch_schedule.slots_per_epoch)
827 {
828 relative_slot.saturating_mul(2) <= epoch_schedule.slots_per_epoch
830 } else {
831 true
833 }
834}
835
836fn verify_authorized_signer<S: std::hash::BuildHasher>(
837 authorized: &Pubkey,
838 signers: &HashSet<Pubkey, S>,
839) -> Result<(), InstructionError> {
840 if signers.contains(authorized) {
841 Ok(())
842 } else {
843 Err(InstructionError::MissingRequiredSignature)
844 }
845}
846
847pub fn withdraw<S: std::hash::BuildHasher>(
849 instruction_context: &InstructionContext,
850 vote_account_index: IndexOfAccount,
851 target_version: VoteStateTargetVersion,
852 lamports: u64,
853 to_account_index: IndexOfAccount,
854 signers: &HashSet<Pubkey, S>,
855 rent_sysvar: &Rent,
856 clock: &Clock,
857) -> Result<(), InstructionError> {
858 let mut vote_account =
859 instruction_context.try_borrow_instruction_account(vote_account_index)?;
860 let vote_state = get_vote_state_handler_checked(
861 &vote_account,
862 PreserveBehaviorInHandlerHelper::new(target_version, false),
863 )?;
864
865 verify_authorized_signer(vote_state.authorized_withdrawer(), signers)?;
866
867 let remaining_balance = vote_account
868 .get_lamports()
869 .checked_sub(lamports)
870 .ok_or(InstructionError::InsufficientFunds)?;
871
872 if remaining_balance == 0 {
873 let reject_active_vote_account_close = vote_state
874 .epoch_credits()
875 .last()
876 .map(|(last_epoch_with_credits, _, _)| {
877 let current_epoch = clock.epoch;
878 current_epoch.saturating_sub(*last_epoch_with_credits) < 2
882 })
883 .unwrap_or(false);
884
885 if reject_active_vote_account_close {
886 return Err(VoteError::ActiveVoteAccountClose.into());
887 } else {
888 VoteStateHandler::deinitialize_vote_account_state(&mut vote_account, target_version)?;
890 }
891 } else {
892 let min_rent_exempt_balance = rent_sysvar.minimum_balance(vote_account.get_data().len());
893 if remaining_balance < min_rent_exempt_balance {
894 return Err(InstructionError::InsufficientFunds);
895 }
896 }
897
898 vote_account.checked_sub_lamports(lamports)?;
899 drop(vote_account);
900 let mut to_account = instruction_context.try_borrow_instruction_account(to_account_index)?;
901 to_account.checked_add_lamports(lamports)?;
902 Ok(())
903}
904
905pub fn initialize_account<S: std::hash::BuildHasher>(
909 vote_account: &mut BorrowedInstructionAccount,
910 target_version: VoteStateTargetVersion,
911 vote_init: &VoteInit,
912 signers: &HashSet<Pubkey, S>,
913 clock: &Clock,
914) -> Result<(), InstructionError> {
915 VoteStateHandler::check_vote_account_length(vote_account, target_version)?;
916 let versioned = vote_account.get_state::<VoteStateVersions>()?;
917
918 if !versioned.is_uninitialized() {
919 return Err(InstructionError::AccountAlreadyInitialized);
920 }
921
922 verify_authorized_signer(&vote_init.node_pubkey, signers)?;
924
925 VoteStateHandler::init_vote_account_state(vote_account, vote_init, clock, target_version)
926}
927
928pub fn process_vote_with_account<S: std::hash::BuildHasher>(
929 vote_account: &mut BorrowedInstructionAccount,
930 target_version: VoteStateTargetVersion,
931 slot_hashes: &[SlotHash],
932 clock: &Clock,
933 vote: &Vote,
934 signers: &HashSet<Pubkey, S>,
935) -> Result<(), InstructionError> {
936 let mut vote_state = get_vote_state_handler_checked(
937 vote_account,
938 PreserveBehaviorInHandlerHelper::new(target_version, true),
939 )?;
940
941 let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?;
942 verify_authorized_signer(&authorized_voter, signers)?;
943
944 process_vote(&mut vote_state, vote, slot_hashes, clock.epoch, clock.slot)?;
945 if let Some(timestamp) = vote.timestamp {
946 vote.slots
947 .iter()
948 .max()
949 .ok_or(VoteError::EmptySlots)
950 .and_then(|slot| vote_state.process_timestamp(*slot, timestamp))?;
951 }
952 vote_state.set_vote_account_state(vote_account)
953}
954
955pub fn process_vote_state_update<S: std::hash::BuildHasher>(
956 vote_account: &mut BorrowedInstructionAccount,
957 target_version: VoteStateTargetVersion,
958 slot_hashes: &[SlotHash],
959 clock: &Clock,
960 vote_state_update: VoteStateUpdate,
961 signers: &HashSet<Pubkey, S>,
962) -> Result<(), InstructionError> {
963 let mut vote_state = get_vote_state_handler_checked(
964 vote_account,
965 PreserveBehaviorInHandlerHelper::new(target_version, true),
966 )?;
967
968 let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?;
969 verify_authorized_signer(&authorized_voter, signers)?;
970
971 do_process_vote_state_update(
972 &mut vote_state,
973 slot_hashes,
974 clock.epoch,
975 clock.slot,
976 vote_state_update,
977 )?;
978 vote_state.set_vote_account_state(vote_account)
979}
980
981pub fn do_process_vote_state_update(
982 vote_state: &mut VoteStateHandler,
983 slot_hashes: &[SlotHash],
984 epoch: u64,
985 slot: u64,
986 mut vote_state_update: VoteStateUpdate,
987) -> Result<(), VoteError> {
988 check_and_filter_proposed_vote_state(
989 vote_state,
990 &mut vote_state_update.lockouts,
991 &mut vote_state_update.root,
992 vote_state_update.hash,
993 slot_hashes,
994 )?;
995 process_new_vote_state(
996 vote_state,
997 vote_state_update
998 .lockouts
999 .iter()
1000 .map(|lockout| LandedVote::from(*lockout))
1001 .collect(),
1002 vote_state_update.root,
1003 vote_state_update.timestamp,
1004 epoch,
1005 slot,
1006 )
1007}
1008
1009pub fn process_tower_sync<S: std::hash::BuildHasher>(
1010 vote_account: &mut BorrowedInstructionAccount,
1011 target_version: VoteStateTargetVersion,
1012 slot_hashes: &[SlotHash],
1013 clock: &Clock,
1014 tower_sync: TowerSync,
1015 signers: &HashSet<Pubkey, S>,
1016) -> Result<(), InstructionError> {
1017 let mut vote_state = get_vote_state_handler_checked(
1018 vote_account,
1019 PreserveBehaviorInHandlerHelper::new(target_version, true),
1020 )?;
1021
1022 let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?;
1023 verify_authorized_signer(&authorized_voter, signers)?;
1024
1025 do_process_tower_sync(
1026 &mut vote_state,
1027 slot_hashes,
1028 clock.epoch,
1029 clock.slot,
1030 tower_sync,
1031 )?;
1032 vote_state.set_vote_account_state(vote_account)
1033}
1034
1035fn do_process_tower_sync(
1036 vote_state: &mut VoteStateHandler,
1037 slot_hashes: &[SlotHash],
1038 epoch: u64,
1039 slot: u64,
1040 mut tower_sync: TowerSync,
1041) -> Result<(), VoteError> {
1042 check_and_filter_proposed_vote_state(
1043 vote_state,
1044 &mut tower_sync.lockouts,
1045 &mut tower_sync.root,
1046 tower_sync.hash,
1047 slot_hashes,
1048 )?;
1049 process_new_vote_state(
1050 vote_state,
1051 tower_sync
1052 .lockouts
1053 .iter()
1054 .map(|lockout| LandedVote::from(*lockout))
1055 .collect(),
1056 tower_sync.root,
1057 tower_sync.timestamp,
1058 epoch,
1059 slot,
1060 )
1061}
1062
1063pub fn create_v3_account_with_authorized(
1064 node_pubkey: &Pubkey,
1065 authorized_voter: &Pubkey,
1066 authorized_withdrawer: &Pubkey,
1067 commission: u8,
1068 lamports: u64,
1069) -> AccountSharedData {
1070 let mut vote_account = AccountSharedData::new(lamports, VoteStateV3::size_of(), &id());
1071
1072 let vote_state = VoteStateV3::new(
1073 &VoteInit {
1074 node_pubkey: *node_pubkey,
1075 authorized_voter: *authorized_voter,
1076 authorized_withdrawer: *authorized_withdrawer,
1077 commission,
1078 },
1079 &Clock::default(),
1080 );
1081
1082 VoteStateV3::serialize(
1083 &VoteStateVersions::V3(Box::new(vote_state)),
1084 vote_account.data_as_mut_slice(),
1085 )
1086 .unwrap();
1087
1088 vote_account
1089}
1090
1091pub fn create_v4_account_with_authorized(
1092 node_pubkey: &Pubkey,
1093 authorized_voter: &Pubkey,
1094 authorized_withdrawer: &Pubkey,
1095 bls_pubkey_compressed: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
1096 inflation_rewards_commission_bps: u16,
1097 lamports: u64,
1098) -> AccountSharedData {
1099 let mut vote_account = AccountSharedData::new(lamports, VoteStateV4::size_of(), &id());
1100
1101 let vote_state = handler::create_new_vote_state_v4_for_tests(
1102 node_pubkey,
1103 authorized_voter,
1104 authorized_withdrawer,
1105 bls_pubkey_compressed,
1106 inflation_rewards_commission_bps,
1107 );
1108
1109 VoteStateV4::serialize(
1110 &VoteStateVersions::V4(Box::new(vote_state)),
1111 vote_account.data_as_mut_slice(),
1112 )
1113 .unwrap();
1114
1115 vote_account
1116}
1117
1118#[allow(clippy::arithmetic_side_effects)]
1119#[cfg(test)]
1120mod tests {
1121 use {
1122 super::*,
1123 assert_matches::assert_matches,
1124 solana_account::{AccountSharedData, ReadableAccount},
1125 solana_clock::DEFAULT_SLOTS_PER_EPOCH,
1126 solana_sha256_hasher::hash,
1127 solana_transaction_context::{InstructionAccount, TransactionContext},
1128 solana_vote_interface::authorized_voters::AuthorizedVoters,
1129 test_case::test_case,
1130 };
1131
1132 const MAX_RECENT_VOTES: usize = 16;
1133
1134 fn vote_state_new_for_test(
1135 vote_pubkey: &Pubkey,
1136 target_version: VoteStateTargetVersion,
1137 ) -> VoteStateHandler {
1138 let auth_pubkey = solana_pubkey::new_rand();
1139 let vote_init = VoteInit {
1140 node_pubkey: solana_pubkey::new_rand(),
1141 authorized_voter: auth_pubkey,
1142 authorized_withdrawer: auth_pubkey,
1143 commission: 0,
1144 };
1145 let clock = Clock::default();
1146
1147 match target_version {
1148 VoteStateTargetVersion::V3 => {
1149 VoteStateHandler::new_v3(VoteStateV3::new(&vote_init, &clock))
1150 }
1151 VoteStateTargetVersion::V4 => VoteStateHandler::new_v4(
1152 handler::create_new_vote_state_v4(vote_pubkey, &vote_init, &clock),
1153 ),
1154 }
1155 }
1156
1157 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1158 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1159 fn test_vote_state_upgrade_from_1_14_11(target_version: VoteStateTargetVersion) {
1160 let vote_pubkey = solana_pubkey::new_rand();
1161 let mut vote_state = vote_state_new_for_test(&vote_pubkey, target_version);
1162
1163 vote_state.increment_credits(0, 100);
1165 assert_eq!(
1166 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 0, 1, |_pubkey| Ok(())),
1167 Ok(())
1168 );
1169 vote_state.increment_credits(1, 200);
1170 assert_eq!(
1171 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 1, 2, |_pubkey| Ok(())),
1172 Ok(())
1173 );
1174 vote_state.increment_credits(2, 300);
1175 assert_eq!(
1176 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 2, 3, |_pubkey| Ok(())),
1177 Ok(())
1178 );
1179
1180 vec![
1182 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
1183 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
1184 134, 135,
1185 ]
1186 .into_iter()
1187 .for_each(|v| vote_state.process_next_vote_slot(v, 4, 0));
1188
1189 let vote_state_v1_14_11 = match target_version {
1192 VoteStateTargetVersion::V3 => {
1193 VoteState1_14_11::from(vote_state.as_ref_v3().clone())
1195 }
1196 VoteStateTargetVersion::V4 => {
1197 VoteState1_14_11 {
1199 node_pubkey: *vote_state.node_pubkey(),
1200 authorized_withdrawer: *vote_state.authorized_withdrawer(),
1201 commission: vote_state.commission(),
1202 votes: vote_state
1203 .votes()
1204 .iter()
1205 .map(|landed_vote| (*landed_vote).into())
1206 .collect(),
1207 root_slot: vote_state.root_slot(),
1208 authorized_voters: vote_state.authorized_voters().clone(),
1209 epoch_credits: vote_state.epoch_credits().clone(),
1210 last_timestamp: vote_state.last_timestamp().clone(),
1211 prior_voters: CircBuf::default(), }
1213 }
1214 };
1215 let version1_14_11_serialized =
1216 bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(vote_state_v1_14_11)))
1217 .unwrap();
1218 let version1_14_11_serialized_len = version1_14_11_serialized.len();
1219 let rent = Rent::default();
1220 let lamports = rent.minimum_balance(version1_14_11_serialized_len);
1221 let mut vote_account =
1222 AccountSharedData::new(lamports, version1_14_11_serialized_len, &id());
1223 vote_account.set_data_from_slice(&version1_14_11_serialized);
1224
1225 let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id());
1228 let mut transaction_context = TransactionContext::new(
1229 vec![(id(), processor_account), (vote_pubkey, vote_account)],
1230 rent.clone(),
1231 0,
1232 0,
1233 );
1234 transaction_context
1235 .configure_next_instruction_for_tests(
1236 0,
1237 vec![InstructionAccount::new(1, false, true)],
1238 vec![],
1239 )
1240 .unwrap();
1241 let instruction_context = transaction_context.get_next_instruction_context().unwrap();
1242
1243 let mut borrowed_account = instruction_context
1246 .try_borrow_instruction_account(0)
1247 .unwrap();
1248
1249 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1251 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1252
1253 let converted_vote_state = get_vote_state_handler_checked(
1255 &borrowed_account,
1256 PreserveBehaviorInHandlerHelper::new(target_version, true),
1257 )
1258 .unwrap();
1259
1260 assert!(vote_state == converted_vote_state);
1262
1263 let vote_state = converted_vote_state;
1264
1265 match target_version {
1268 VoteStateTargetVersion::V3 => {
1269 assert_eq!(
1271 vote_state
1272 .clone()
1273 .set_vote_account_state(&mut borrowed_account),
1274 Ok(())
1275 );
1276 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1277 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1278 }
1279 VoteStateTargetVersion::V4 => {
1280 assert_eq!(
1282 vote_state
1283 .clone()
1284 .set_vote_account_state(&mut borrowed_account),
1285 Err(InstructionError::AccountNotRentExempt)
1286 );
1287 }
1288 }
1289
1290 let converted_vote_state = get_vote_state_handler_checked(
1292 &borrowed_account,
1293 PreserveBehaviorInHandlerHelper::new(target_version, true),
1294 )
1295 .unwrap();
1296
1297 assert!(vote_state == converted_vote_state);
1299
1300 let vote_state = converted_vote_state;
1301
1302 let space = match target_version {
1304 VoteStateTargetVersion::V3 => VoteStateV3::size_of(),
1305 VoteStateTargetVersion::V4 => VoteStateV4::size_of(), };
1307 assert_eq!(
1308 borrowed_account.set_lamports(rent.minimum_balance(space)),
1309 Ok(())
1310 );
1311 assert_eq!(
1312 vote_state
1313 .clone()
1314 .set_vote_account_state(&mut borrowed_account),
1315 Ok(())
1316 );
1317
1318 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1320 match target_version {
1321 VoteStateTargetVersion::V3 => {
1322 assert_matches!(vote_state_version, VoteStateVersions::V3(_));
1323 }
1324 VoteStateTargetVersion::V4 => {
1325 assert_matches!(vote_state_version, VoteStateVersions::V4(_));
1326 }
1327 }
1328
1329 let converted_vote_state = get_vote_state_handler_checked(
1331 &borrowed_account,
1332 PreserveBehaviorInHandlerHelper::new(target_version, true),
1333 )
1334 .unwrap();
1335
1336 assert_eq!(vote_state, converted_vote_state);
1338 }
1339
1340 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1341 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1342 fn test_vote_lockout(target_version: VoteStateTargetVersion) {
1343 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1344
1345 for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
1346 process_slot_vote_unchecked(&mut vote_state, (INITIAL_LOCKOUT * i) as u64);
1347 }
1348
1349 assert_eq!(vote_state.votes().len(), MAX_LOCKOUT_HISTORY);
1351 assert_eq!(vote_state.root_slot(), Some(0));
1352 check_lockouts(&vote_state);
1353
1354 let top_vote = vote_state.votes().front().unwrap().slot();
1358 let slot = vote_state.last_lockout().unwrap().last_locked_out_slot();
1359 process_slot_vote_unchecked(&mut vote_state, slot);
1360 assert_eq!(Some(top_vote), vote_state.root_slot());
1361
1362 let slot = vote_state
1364 .votes()
1365 .front()
1366 .unwrap()
1367 .lockout
1368 .last_locked_out_slot();
1369 process_slot_vote_unchecked(&mut vote_state, slot);
1370 assert_eq!(vote_state.votes().len(), 2);
1372 }
1373
1374 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1375 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1376 fn test_update_commission(target_version: VoteStateTargetVersion) {
1377 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1378 let node_pubkey = *vote_state.node_pubkey();
1379 let withdrawer_pubkey = *vote_state.authorized_withdrawer();
1380
1381 vote_state.set_commission(10);
1383
1384 let serialized = vote_state.serialize();
1385 let serialized_len = serialized.len();
1386 let rent = Rent::default();
1387 let lamports = rent.minimum_balance(serialized_len);
1388 let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id());
1389 vote_account.set_data_from_slice(&serialized);
1390
1391 let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id());
1394 let mut transaction_context = TransactionContext::new(
1395 vec![(id(), processor_account), (node_pubkey, vote_account)],
1396 rent,
1397 0,
1398 0,
1399 );
1400 transaction_context
1401 .configure_next_instruction_for_tests(
1402 0,
1403 vec![InstructionAccount::new(1, false, true)],
1404 vec![],
1405 )
1406 .unwrap();
1407 let instruction_context = transaction_context.get_next_instruction_context().unwrap();
1408
1409 let mut borrowed_account = instruction_context
1412 .try_borrow_instruction_account(0)
1413 .unwrap();
1414
1415 let epoch_schedule = std::sync::Arc::new(EpochSchedule::without_warmup());
1416
1417 let first_half_clock = std::sync::Arc::new(Clock {
1418 slot: epoch_schedule.slots_per_epoch / 4,
1419 ..Clock::default()
1420 });
1421
1422 let second_half_clock = std::sync::Arc::new(Clock {
1423 slot: (epoch_schedule.slots_per_epoch * 3) / 4,
1424 ..Clock::default()
1425 });
1426
1427 let signers: HashSet<Pubkey> = vec![withdrawer_pubkey].into_iter().collect();
1428
1429 assert_eq!(
1431 get_vote_state_handler_checked(
1432 &borrowed_account,
1433 PreserveBehaviorInHandlerHelper::new(target_version, true),
1434 )
1435 .unwrap()
1436 .commission(),
1437 10
1438 );
1439 assert_matches!(
1440 update_commission(
1441 &mut borrowed_account,
1442 target_version,
1443 11,
1444 &signers,
1445 &epoch_schedule,
1446 &first_half_clock,
1447 ),
1448 Ok(())
1449 );
1450 assert_eq!(
1451 get_vote_state_handler_checked(
1452 &borrowed_account,
1453 PreserveBehaviorInHandlerHelper::new(target_version, true),
1454 )
1455 .unwrap()
1456 .commission(),
1457 11
1458 );
1459
1460 assert_matches!(
1462 update_commission(
1463 &mut borrowed_account,
1464 target_version,
1465 12,
1466 &signers,
1467 &epoch_schedule,
1468 &second_half_clock,
1469 ),
1470 Err(_)
1471 );
1472 assert_eq!(
1473 get_vote_state_handler_checked(
1474 &borrowed_account,
1475 PreserveBehaviorInHandlerHelper::new(target_version, true),
1476 )
1477 .unwrap()
1478 .commission(),
1479 11
1480 );
1481
1482 assert_matches!(
1484 update_commission(
1485 &mut borrowed_account,
1486 target_version,
1487 10,
1488 &signers,
1489 &epoch_schedule,
1490 &first_half_clock,
1491 ),
1492 Ok(())
1493 );
1494 assert_eq!(
1495 get_vote_state_handler_checked(
1496 &borrowed_account,
1497 PreserveBehaviorInHandlerHelper::new(target_version, true),
1498 )
1499 .unwrap()
1500 .commission(),
1501 10
1502 );
1503
1504 assert_eq!(
1505 get_vote_state_handler_checked(
1506 &borrowed_account,
1507 PreserveBehaviorInHandlerHelper::new(target_version, true),
1508 )
1509 .unwrap()
1510 .commission(),
1511 10
1512 );
1513
1514 assert_matches!(
1515 update_commission(
1516 &mut borrowed_account,
1517 target_version,
1518 9,
1519 &signers,
1520 &epoch_schedule,
1521 &second_half_clock,
1522 ),
1523 Ok(())
1524 );
1525 assert_eq!(
1526 get_vote_state_handler_checked(
1527 &borrowed_account,
1528 PreserveBehaviorInHandlerHelper::new(target_version, true),
1529 )
1530 .unwrap()
1531 .commission(),
1532 9
1533 );
1534 }
1535
1536 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1537 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1538 fn test_vote_double_lockout_after_expiration(target_version: VoteStateTargetVersion) {
1539 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1540
1541 for i in 0..3 {
1542 process_slot_vote_unchecked(&mut vote_state, i as u64);
1543 }
1544
1545 check_lockouts(&vote_state);
1546
1547 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 1) as u64);
1551 check_lockouts(&vote_state);
1552
1553 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 2) as u64);
1556 check_lockouts(&vote_state);
1557
1558 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 3) as u64);
1561 check_lockouts(&vote_state);
1562 }
1563
1564 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1565 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1566 fn test_expire_multiple_votes(target_version: VoteStateTargetVersion) {
1567 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1568
1569 for i in 0..3 {
1570 process_slot_vote_unchecked(&mut vote_state, i as u64);
1571 }
1572
1573 assert_eq!(vote_state.votes()[0].confirmation_count(), 3);
1574
1575 let expire_slot =
1577 vote_state.votes()[1].slot() + vote_state.votes()[1].lockout.lockout() + 1;
1578 process_slot_vote_unchecked(&mut vote_state, expire_slot);
1579 assert_eq!(vote_state.votes().len(), 2);
1580
1581 assert_eq!(vote_state.votes()[0].slot(), 0);
1583 assert_eq!(vote_state.votes()[1].slot(), expire_slot);
1584
1585 process_slot_vote_unchecked(&mut vote_state, expire_slot + 1);
1587
1588 assert_eq!(vote_state.votes()[0].confirmation_count(), 3);
1590
1591 assert_eq!(vote_state.votes()[1].confirmation_count(), 2);
1593 assert_eq!(vote_state.votes()[2].confirmation_count(), 1);
1594 }
1595
1596 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1597 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1598 fn test_vote_credits(target_version: VoteStateTargetVersion) {
1599 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1600
1601 for i in 0..MAX_LOCKOUT_HISTORY {
1602 process_slot_vote_unchecked(&mut vote_state, i as u64);
1603 }
1604
1605 assert_eq!(vote_state.credits(), 0);
1606
1607 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 1);
1608 assert_eq!(vote_state.credits(), 1);
1609 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 2);
1610 assert_eq!(vote_state.credits(), 2);
1611 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 3);
1612 assert_eq!(vote_state.credits(), 3);
1613 }
1614
1615 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1616 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1617 fn test_duplicate_vote(target_version: VoteStateTargetVersion) {
1618 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1619 process_slot_vote_unchecked(&mut vote_state, 0);
1620 process_slot_vote_unchecked(&mut vote_state, 1);
1621 process_slot_vote_unchecked(&mut vote_state, 0);
1622 assert_eq!(vote_state.nth_recent_lockout(0).unwrap().slot(), 1);
1623 assert_eq!(vote_state.nth_recent_lockout(1).unwrap().slot(), 0);
1624 assert!(vote_state.nth_recent_lockout(2).is_none());
1625 }
1626
1627 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1628 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1629 fn test_nth_recent_lockout(target_version: VoteStateTargetVersion) {
1630 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1631 for i in 0..MAX_LOCKOUT_HISTORY {
1632 process_slot_vote_unchecked(&mut vote_state, i as u64);
1633 }
1634 for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
1635 assert_eq!(
1636 vote_state.nth_recent_lockout(i).unwrap().slot() as usize,
1637 MAX_LOCKOUT_HISTORY - i - 1,
1638 );
1639 }
1640 assert!(vote_state.nth_recent_lockout(MAX_LOCKOUT_HISTORY).is_none());
1641 }
1642
1643 fn check_lockouts(vote_state: &VoteStateHandler) {
1644 let votes = vote_state.votes();
1645 for (i, vote) in votes.iter().enumerate() {
1646 let num_votes = votes
1647 .len()
1648 .checked_sub(i)
1649 .expect("`i` is less than `vote_state.votes().len()`");
1650 assert_eq!(
1651 vote.lockout.lockout(),
1652 INITIAL_LOCKOUT.pow(num_votes as u32) as u64
1653 );
1654 }
1655 }
1656
1657 fn recent_votes(vote_state: &VoteStateHandler) -> Vec<Vote> {
1658 let votes = vote_state.votes();
1659 let start = votes.len().saturating_sub(MAX_RECENT_VOTES);
1660 (start..votes.len())
1661 .map(|i| Vote::new(vec![votes.get(i).unwrap().slot()], Hash::default()))
1662 .collect()
1663 }
1664
1665 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1667 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1668 fn test_process_missed_votes(target_version: VoteStateTargetVersion) {
1669 let mut vote_state_a = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1670 let mut vote_state_b = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1671
1672 (0..5).for_each(|i| process_slot_vote_unchecked(&mut vote_state_a, i as u64));
1674 assert_ne!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1675
1676 let slots = (0u64..MAX_RECENT_VOTES as u64).collect();
1678 let vote = Vote::new(slots, Hash::default());
1679 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
1680
1681 assert_eq!(
1682 process_vote(&mut vote_state_a, &vote, &slot_hashes, 0, 0),
1683 Ok(())
1684 );
1685 assert_eq!(
1686 process_vote(&mut vote_state_b, &vote, &slot_hashes, 0, 0),
1687 Ok(())
1688 );
1689 assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1690 }
1691
1692 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1693 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1694 fn test_process_vote_skips_old_vote(mut vote_state: VoteStateHandler) {
1695 let vote = Vote::new(vec![0], Hash::default());
1696 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1697 assert_eq!(
1698 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1699 Ok(())
1700 );
1701 let recent = recent_votes(&vote_state);
1702 assert_eq!(
1703 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1704 Err(VoteError::VoteTooOld)
1705 );
1706 assert_eq!(recent, recent_votes(&vote_state));
1707 }
1708
1709 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1710 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1711 fn test_check_slots_are_valid_vote_empty_slot_hashes(vote_state: VoteStateHandler) {
1712 let vote = Vote::new(vec![0], Hash::default());
1713 assert_eq!(
1714 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &[]),
1715 Err(VoteError::VoteTooOld)
1716 );
1717 }
1718
1719 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1720 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1721 fn test_check_slots_are_valid_new_vote(vote_state: VoteStateHandler) {
1722 let vote = Vote::new(vec![0], Hash::default());
1723 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1724 assert_eq!(
1725 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1726 Ok(())
1727 );
1728 }
1729
1730 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1731 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1732 fn test_check_slots_are_valid_bad_hash(vote_state: VoteStateHandler) {
1733 let vote = Vote::new(vec![0], Hash::default());
1734 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
1735 assert_eq!(
1736 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1737 Err(VoteError::SlotHashMismatch)
1738 );
1739 }
1740
1741 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1742 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1743 fn test_check_slots_are_valid_bad_slot(vote_state: VoteStateHandler) {
1744 let vote = Vote::new(vec![1], Hash::default());
1745 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1746 assert_eq!(
1747 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1748 Err(VoteError::SlotsMismatch)
1749 );
1750 }
1751
1752 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1753 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1754 fn test_check_slots_are_valid_duplicate_vote(mut vote_state: VoteStateHandler) {
1755 let vote = Vote::new(vec![0], Hash::default());
1756 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1757 assert_eq!(
1758 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1759 Ok(())
1760 );
1761 assert_eq!(
1762 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1763 Err(VoteError::VoteTooOld)
1764 );
1765 }
1766
1767 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1768 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1769 fn test_check_slots_are_valid_next_vote(mut vote_state: VoteStateHandler) {
1770 let vote = Vote::new(vec![0], Hash::default());
1771 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1772 assert_eq!(
1773 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1774 Ok(())
1775 );
1776
1777 let vote = Vote::new(vec![0, 1], Hash::default());
1778 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1779 assert_eq!(
1780 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1781 Ok(())
1782 );
1783 }
1784
1785 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1786 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1787 fn test_check_slots_are_valid_next_vote_only(mut vote_state: VoteStateHandler) {
1788 let vote = Vote::new(vec![0], Hash::default());
1789 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1790 assert_eq!(
1791 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1792 Ok(())
1793 );
1794
1795 let vote = Vote::new(vec![1], Hash::default());
1796 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1797 assert_eq!(
1798 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1799 Ok(())
1800 );
1801 }
1802
1803 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1804 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1805 fn test_process_vote_empty_slots(mut vote_state: VoteStateHandler) {
1806 let vote = Vote::new(vec![], Hash::default());
1807 assert_eq!(
1808 process_vote(&mut vote_state, &vote, &[], 0, 0),
1809 Err(VoteError::EmptySlots)
1810 );
1811 }
1812
1813 pub fn process_new_vote_state_from_lockouts(
1814 vote_state: &mut VoteStateHandler,
1815 new_state: VecDeque<Lockout>,
1816 new_root: Option<Slot>,
1817 timestamp: Option<i64>,
1818 epoch: Epoch,
1819 ) -> Result<(), VoteError> {
1820 process_new_vote_state(
1821 vote_state,
1822 new_state.into_iter().map(LandedVote::from).collect(),
1823 new_root,
1824 timestamp,
1825 epoch,
1826 0,
1827 )
1828 }
1829
1830 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1832 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1833 fn test_vote_state_update_increment_credits(mut vote_state: VoteStateHandler) {
1834 let test_vote_groups: Vec<Vec<Slot>> = vec![
1837 vec![1, 2, 3, 4, 5, 6, 7, 8],
1839 vec![
1840 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1841 30, 31,
1842 ],
1843 vec![32],
1845 vec![33],
1847 vec![34, 35],
1849 vec![36, 37, 38],
1851 vec![
1853 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
1854 60, 61, 62, 63, 64, 65, 66, 67, 68,
1855 ],
1856 vec![
1858 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
1859 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1860 ],
1861 vec![100, 101, 106, 107, 112, 116, 120, 121, 122, 124],
1863 vec![200, 201],
1865 vec![
1866 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
1867 218, 219, 220, 221, 222, 223, 224, 225, 226,
1868 ],
1869 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
1870 ];
1871
1872 for vote_group in test_vote_groups {
1873 let mut vote_state_after_vote = vote_state.clone();
1875
1876 process_vote_unchecked(
1877 &mut vote_state_after_vote,
1878 Vote {
1879 slots: vote_group.clone(),
1880 hash: Hash::new_unique(),
1881 timestamp: None,
1882 },
1883 )
1884 .unwrap();
1885
1886 assert_eq!(
1888 process_new_vote_state(
1889 &mut vote_state,
1890 vote_state_after_vote.votes().clone(),
1891 vote_state_after_vote.root_slot(),
1892 None,
1893 0,
1894 0,
1895 ),
1896 Ok(())
1897 );
1898
1899 assert_eq!(
1901 vote_state.epoch_credits(),
1902 vote_state_after_vote.epoch_credits()
1903 );
1904 }
1905 }
1906
1907 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1909 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1910 fn test_timely_credits(target_version: VoteStateTargetVersion) {
1911 let test_vote_groups: Vec<(Vec<Slot>, Slot, u32)> = vec![
1915 (
1917 vec![1, 2, 3, 4, 5, 6, 7, 8],
1918 9,
1919 0,
1921 ),
1922 (
1923 vec![
1924 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
1925 29, 30, 31,
1926 ],
1927 34,
1928 0,
1931 ),
1932 (
1934 vec![32],
1935 35,
1936 10,
1939 ),
1940 (
1942 vec![33],
1943 36,
1944 10 + 11, ),
1948 (
1950 vec![34, 35],
1951 37,
1952 21 + 12 + 13, ),
1956 (
1958 vec![36, 37, 38],
1959 39,
1960 46 + 14 + 15 + 16, ),
1964 (
1965 vec![
1967 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
1968 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
1969 ],
1970 69,
1971 91 + 16
1981 + 9 + 2
1983 + 3
1984 + 4
1985 + 5
1986 + 6
1987 + 7
1988 + 8
1989 + 9
1990 + 10
1991 + 11
1992 + 12
1993 + 13
1994 + 14
1995 + 15
1996 + 15
1997 + 15
1998 + 15
1999 + 16
2000 + 15
2001 + 16, ),
2003 (
2005 vec![
2006 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
2007 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
2008 ],
2009 100,
2010 327 + 16
2017 + 14 + 2
2019 + 3
2020 + 4
2021 + 5
2022 + 6
2023 + 7
2024 + 8
2025 + 9
2026 + 10
2027 + 11
2028 + 12
2029 + 13
2030 + 14
2031 + 15
2032 + 16
2033 + 16, ),
2035 (
2037 vec![115, 116, 117, 118, 119, 120, 121, 122, 123, 124],
2038 130,
2039 508 + ((74 - 69) + 1), ),
2044 (
2046 vec![200, 201],
2047 202,
2048 514,
2051 ),
2052 (
2053 vec![
2054 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
2055 218, 219, 220, 221, 222, 223, 224, 225, 226,
2056 ],
2057 227,
2058 514 + 9 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13, ),
2064 (
2065 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
2066 237,
2067 613 + 3 + 4 + 5 + 6 + 16 + 16 + 1 + 1 + 1 + 1, ),
2073 ];
2074
2075 let new_vote_state = || match target_version {
2076 VoteStateTargetVersion::V3 => VoteStateHandler::default_v3(),
2077 VoteStateTargetVersion::V4 => VoteStateHandler::default_v4(),
2078 };
2079
2080 for i in 0..test_vote_groups.len() {
2083 let mut vote_state_1 = new_vote_state();
2085 let mut vote_state_2 = new_vote_state();
2087 test_vote_groups.iter().take(i + 1).for_each(|vote_group| {
2088 let vote = Vote {
2089 slots: vote_group.0.clone(), hash: Hash::new_unique(),
2091 timestamp: None,
2092 };
2093 let slot_hashes: Vec<_> =
2094 vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
2095 assert_eq!(
2096 process_vote(
2097 &mut vote_state_1,
2098 &vote,
2099 &slot_hashes,
2100 0,
2101 vote_group.1, ),
2103 Ok(())
2104 );
2105
2106 assert_eq!(
2107 process_new_vote_state(
2108 &mut vote_state_2,
2109 vote_state_1.votes().clone(),
2110 vote_state_1.root_slot(),
2111 None,
2112 0,
2113 vote_group.1, ),
2115 Ok(())
2116 );
2117 });
2118
2119 let vote_group = &test_vote_groups[i];
2121 assert_eq!(vote_state_1.credits(), vote_group.2 as u64); assert_eq!(vote_state_2.credits(), vote_group.2 as u64); }
2124 }
2125
2126 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2127 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2128 fn test_retroactive_voting_timely_credits(mut vote_state: VoteStateHandler) {
2129 #[allow(clippy::type_complexity)]
2135 let test_vote_state_updates: Vec<(Vec<(Slot, u32)>, Slot, Option<Slot>, u32)> = vec![
2136 (
2138 vec![(7, 4), (8, 3), (9, 2), (10, 1)],
2139 11,
2140 None,
2142 0,
2144 ),
2145 (
2147 vec![
2148 (1, 10),
2149 (2, 9),
2150 (3, 8),
2151 (4, 7),
2152 (5, 6),
2153 (6, 5),
2154 (7, 4),
2155 (8, 3),
2156 (9, 2),
2157 (10, 1),
2158 ],
2159 12,
2160 None,
2162 0,
2164 ),
2165 (
2167 vec![
2168 (11, 31),
2169 (12, 30),
2170 (13, 29),
2171 (14, 28),
2172 (15, 27),
2173 (16, 26),
2174 (17, 25),
2175 (18, 24),
2176 (19, 23),
2177 (20, 22),
2178 (21, 21),
2179 (22, 20),
2180 (23, 19),
2181 (24, 18),
2182 (25, 17),
2183 (26, 16),
2184 (27, 15),
2185 (28, 14),
2186 (29, 13),
2187 (30, 12),
2188 (31, 11),
2189 (32, 10),
2190 (33, 9),
2191 (34, 8),
2192 (35, 7),
2193 (36, 6),
2194 (37, 5),
2195 (38, 4),
2196 (39, 3),
2197 (40, 2),
2198 (41, 1),
2199 ],
2200 42,
2201 Some(10),
2203 7 + 8 + 9 + 10 + 11 + 12 + 14 + 15 + 16 + 16,
2206 ),
2207 ];
2208
2209 test_vote_state_updates
2212 .iter()
2213 .for_each(|proposed_vote_state| {
2214 let new_state = proposed_vote_state
2215 .0 .iter()
2217 .map(|(slot, confirmation_count)| LandedVote {
2218 latency: 0,
2219 lockout: Lockout::new_with_confirmation_count(*slot, *confirmation_count),
2220 })
2221 .collect::<VecDeque<LandedVote>>();
2222 assert_eq!(
2223 process_new_vote_state(
2224 &mut vote_state,
2225 new_state,
2226 proposed_vote_state.2, None,
2228 0,
2229 proposed_vote_state.1, ),
2231 Ok(())
2232 );
2233
2234 assert_eq!(vote_state.credits(), proposed_vote_state.3 as u64);
2236 });
2237 }
2238
2239 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2240 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2241 fn test_process_new_vote_too_many_votes(mut vote_state1: VoteStateHandler) {
2242 let bad_votes: VecDeque<Lockout> = (0..=MAX_LOCKOUT_HISTORY)
2243 .map(|slot| {
2244 Lockout::new_with_confirmation_count(
2245 slot as Slot,
2246 (MAX_LOCKOUT_HISTORY - slot + 1) as u32,
2247 )
2248 })
2249 .collect();
2250
2251 let current_epoch = vote_state1.current_epoch();
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::TooManyVotes)
2261 );
2262 }
2263
2264 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2265 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2266 fn test_process_new_vote_state_root_rollback(mut vote_state1: VoteStateHandler) {
2267 for i in 0..MAX_LOCKOUT_HISTORY + 2 {
2268 process_slot_vote_unchecked(&mut vote_state1, i as Slot);
2269 }
2270 assert_eq!(vote_state1.root_slot().unwrap(), 1);
2271
2272 let mut vote_state2 = vote_state1.clone();
2275 process_slot_vote_unchecked(&mut vote_state2, MAX_LOCKOUT_HISTORY as Slot + 3);
2276
2277 let lesser_root = Some(0);
2279
2280 let current_epoch = vote_state2.current_epoch();
2281 assert_eq!(
2282 process_new_vote_state(
2283 &mut vote_state1,
2284 vote_state2.votes().clone(),
2285 lesser_root,
2286 None,
2287 current_epoch,
2288 0,
2289 ),
2290 Err(VoteError::RootRollBack)
2291 );
2292
2293 let none_root = None;
2295 assert_eq!(
2296 process_new_vote_state(
2297 &mut vote_state1,
2298 vote_state2.votes().clone(),
2299 none_root,
2300 None,
2301 current_epoch,
2302 0,
2303 ),
2304 Err(VoteError::RootRollBack)
2305 );
2306 }
2307
2308 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2309 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2310 fn test_process_new_vote_state_zero_confirmations(mut vote_state1: VoteStateHandler) {
2311 let current_epoch = vote_state1.current_epoch();
2312
2313 let bad_votes: VecDeque<Lockout> = vec![
2314 Lockout::new_with_confirmation_count(0, 0),
2315 Lockout::new_with_confirmation_count(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 None,
2324 None,
2325 current_epoch,
2326 ),
2327 Err(VoteError::ZeroConfirmations)
2328 );
2329
2330 let bad_votes: VecDeque<Lockout> = vec![
2331 Lockout::new_with_confirmation_count(0, 2),
2332 Lockout::new_with_confirmation_count(1, 0),
2333 ]
2334 .into_iter()
2335 .collect();
2336 assert_eq!(
2337 process_new_vote_state_from_lockouts(
2338 &mut vote_state1,
2339 bad_votes,
2340 None,
2341 None,
2342 current_epoch,
2343 ),
2344 Err(VoteError::ZeroConfirmations)
2345 );
2346 }
2347
2348 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2349 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2350 fn test_process_new_vote_state_confirmations_too_large(initial_vote_state: VoteStateHandler) {
2351 let mut vote_state1 = initial_vote_state.clone();
2352 let current_epoch = vote_state1.current_epoch();
2353
2354 let good_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2355 0,
2356 MAX_LOCKOUT_HISTORY as u32,
2357 )]
2358 .into_iter()
2359 .collect();
2360
2361 process_new_vote_state_from_lockouts(
2362 &mut vote_state1,
2363 good_votes,
2364 None,
2365 None,
2366 current_epoch,
2367 )
2368 .unwrap();
2369
2370 let mut vote_state1 = initial_vote_state;
2371 let bad_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2372 0,
2373 MAX_LOCKOUT_HISTORY as u32 + 1,
2374 )]
2375 .into_iter()
2376 .collect();
2377 assert_eq!(
2378 process_new_vote_state_from_lockouts(
2379 &mut vote_state1,
2380 bad_votes,
2381 None,
2382 None,
2383 current_epoch,
2384 ),
2385 Err(VoteError::ConfirmationTooLarge)
2386 );
2387 }
2388
2389 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2390 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2391 fn test_process_new_vote_state_slot_smaller_than_root(mut vote_state1: VoteStateHandler) {
2392 let current_epoch = vote_state1.current_epoch();
2393 let root_slot = 5;
2394
2395 let bad_votes: VecDeque<Lockout> = vec![
2396 Lockout::new_with_confirmation_count(root_slot, 2),
2397 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2398 ]
2399 .into_iter()
2400 .collect();
2401 assert_eq!(
2402 process_new_vote_state_from_lockouts(
2403 &mut vote_state1,
2404 bad_votes,
2405 Some(root_slot),
2406 None,
2407 current_epoch,
2408 ),
2409 Err(VoteError::SlotSmallerThanRoot)
2410 );
2411
2412 let bad_votes: VecDeque<Lockout> = vec![
2413 Lockout::new_with_confirmation_count(root_slot - 1, 2),
2414 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2415 ]
2416 .into_iter()
2417 .collect();
2418 assert_eq!(
2419 process_new_vote_state_from_lockouts(
2420 &mut vote_state1,
2421 bad_votes,
2422 Some(root_slot),
2423 None,
2424 current_epoch,
2425 ),
2426 Err(VoteError::SlotSmallerThanRoot)
2427 );
2428 }
2429
2430 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2431 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2432 fn test_process_new_vote_state_slots_not_ordered(mut vote_state1: VoteStateHandler) {
2433 let current_epoch = vote_state1.current_epoch();
2434
2435 let bad_votes: VecDeque<Lockout> = vec![
2436 Lockout::new_with_confirmation_count(1, 2),
2437 Lockout::new_with_confirmation_count(0, 1),
2438 ]
2439 .into_iter()
2440 .collect();
2441 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::SlotsNotOrdered)
2450 );
2451
2452 let bad_votes: VecDeque<Lockout> = vec![
2453 Lockout::new_with_confirmation_count(1, 2),
2454 Lockout::new_with_confirmation_count(1, 1),
2455 ]
2456 .into_iter()
2457 .collect();
2458 assert_eq!(
2459 process_new_vote_state_from_lockouts(
2460 &mut vote_state1,
2461 bad_votes,
2462 None,
2463 None,
2464 current_epoch,
2465 ),
2466 Err(VoteError::SlotsNotOrdered)
2467 );
2468 }
2469
2470 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2471 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2472 fn test_process_new_vote_state_confirmations_not_ordered(mut vote_state1: VoteStateHandler) {
2473 let current_epoch = vote_state1.current_epoch();
2474
2475 let bad_votes: VecDeque<Lockout> = vec![
2476 Lockout::new_with_confirmation_count(0, 1),
2477 Lockout::new_with_confirmation_count(1, 2),
2478 ]
2479 .into_iter()
2480 .collect();
2481 assert_eq!(
2482 process_new_vote_state_from_lockouts(
2483 &mut vote_state1,
2484 bad_votes,
2485 None,
2486 None,
2487 current_epoch,
2488 ),
2489 Err(VoteError::ConfirmationsNotOrdered)
2490 );
2491
2492 let bad_votes: VecDeque<Lockout> = vec![
2493 Lockout::new_with_confirmation_count(0, 1),
2494 Lockout::new_with_confirmation_count(1, 1),
2495 ]
2496 .into_iter()
2497 .collect();
2498 assert_eq!(
2499 process_new_vote_state_from_lockouts(
2500 &mut vote_state1,
2501 bad_votes,
2502 None,
2503 None,
2504 current_epoch,
2505 ),
2506 Err(VoteError::ConfirmationsNotOrdered)
2507 );
2508 }
2509
2510 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2511 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2512 fn test_process_new_vote_state_new_vote_state_lockout_mismatch(
2513 mut vote_state1: VoteStateHandler,
2514 ) {
2515 let current_epoch = vote_state1.current_epoch();
2516
2517 let bad_votes: VecDeque<Lockout> = vec![
2518 Lockout::new_with_confirmation_count(0, 2),
2519 Lockout::new_with_confirmation_count(7, 1),
2520 ]
2521 .into_iter()
2522 .collect();
2523
2524 assert_eq!(
2526 process_new_vote_state_from_lockouts(
2527 &mut vote_state1,
2528 bad_votes,
2529 None,
2530 None,
2531 current_epoch,
2532 ),
2533 Err(VoteError::NewVoteStateLockoutMismatch)
2534 );
2535 }
2536
2537 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2538 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2539 fn test_process_new_vote_state_confirmation_rollback(mut vote_state1: VoteStateHandler) {
2540 let current_epoch = vote_state1.current_epoch();
2541 let votes: VecDeque<Lockout> = vec![
2542 Lockout::new_with_confirmation_count(0, 4),
2543 Lockout::new_with_confirmation_count(1, 3),
2544 ]
2545 .into_iter()
2546 .collect();
2547 process_new_vote_state_from_lockouts(&mut vote_state1, votes, None, None, current_epoch)
2548 .unwrap();
2549
2550 let votes: VecDeque<Lockout> = vec![
2551 Lockout::new_with_confirmation_count(0, 4),
2552 Lockout::new_with_confirmation_count(1, 2),
2554 Lockout::new_with_confirmation_count(2, 1),
2555 ]
2556 .into_iter()
2557 .collect();
2558 assert_eq!(
2561 process_new_vote_state_from_lockouts(
2562 &mut vote_state1,
2563 votes,
2564 None,
2565 None,
2566 current_epoch,
2567 ),
2568 Err(VoteError::ConfirmationRollBack)
2569 );
2570 }
2571
2572 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2573 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2574 fn test_process_new_vote_state_root_progress(mut vote_state1: VoteStateHandler) {
2575 for i in 0..MAX_LOCKOUT_HISTORY {
2576 process_slot_vote_unchecked(&mut vote_state1, i as u64);
2577 }
2578
2579 assert!(vote_state1.root_slot().is_none());
2580 let mut vote_state2 = vote_state1.clone();
2581
2582 for new_vote in MAX_LOCKOUT_HISTORY + 1..=MAX_LOCKOUT_HISTORY + 2 {
2589 process_slot_vote_unchecked(&mut vote_state2, new_vote as Slot);
2590 assert_ne!(vote_state1.root_slot(), vote_state2.root_slot());
2591
2592 process_new_vote_state(
2593 &mut vote_state1,
2594 vote_state2.votes().clone(),
2595 vote_state2.root_slot(),
2596 None,
2597 vote_state2.current_epoch(),
2598 0,
2599 )
2600 .unwrap();
2601
2602 assert_eq!(vote_state1, vote_state2);
2603 }
2604 }
2605
2606 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2607 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2608 fn test_process_new_vote_state_same_slot_but_not_common_ancestor(
2609 initial_vote_state: VoteStateHandler,
2610 ) {
2611 let mut vote_state1 = initial_vote_state.clone();
2630 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5]);
2631 assert_eq!(
2632 vote_state1
2633 .votes()
2634 .iter()
2635 .map(|vote| vote.slot())
2636 .collect::<Vec<Slot>>(),
2637 vec![1, 5]
2638 );
2639
2640 let mut vote_state2 = initial_vote_state;
2642 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2643 assert_eq!(
2644 vote_state2
2645 .votes()
2646 .iter()
2647 .map(|vote| vote.slot())
2648 .collect::<Vec<Slot>>(),
2649 vec![1, 2, 3, 5, 7]
2650 );
2651
2652 process_new_vote_state(
2654 &mut vote_state1,
2655 vote_state2.votes().clone(),
2656 vote_state2.root_slot(),
2657 None,
2658 vote_state2.current_epoch(),
2659 0,
2660 )
2661 .unwrap();
2662
2663 assert_eq!(vote_state1, vote_state2);
2664 }
2665
2666 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2667 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2668 fn test_process_new_vote_state_lockout_violation(initial_vote_state: VoteStateHandler) {
2669 let mut vote_state1 = initial_vote_state.clone();
2671 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 4, 5]);
2672 assert_eq!(
2673 vote_state1
2674 .votes()
2675 .iter()
2676 .map(|vote| vote.slot())
2677 .collect::<Vec<Slot>>(),
2678 vec![1, 2, 4, 5]
2679 );
2680
2681 let mut vote_state2 = initial_vote_state;
2684 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2685 assert_eq!(
2686 vote_state2
2687 .votes()
2688 .iter()
2689 .map(|vote| vote.slot())
2690 .collect::<Vec<Slot>>(),
2691 vec![1, 2, 3, 5, 7]
2692 );
2693
2694 assert_eq!(
2696 process_new_vote_state(
2697 &mut vote_state1,
2698 vote_state2.votes().clone(),
2699 vote_state2.root_slot(),
2700 None,
2701 vote_state2.current_epoch(),
2702 0,
2703 ),
2704 Err(VoteError::LockoutConflict)
2705 );
2706 }
2707
2708 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2709 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2710 fn test_process_new_vote_state_lockout_violation2(initial_vote_state: VoteStateHandler) {
2711 let mut vote_state1 = initial_vote_state.clone();
2713 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5, 6, 7]);
2714 assert_eq!(
2715 vote_state1
2716 .votes()
2717 .iter()
2718 .map(|vote| vote.slot())
2719 .collect::<Vec<Slot>>(),
2720 vec![1, 5, 6, 7]
2721 );
2722
2723 let mut vote_state2 = initial_vote_state;
2726 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 6, 8]);
2727 assert_eq!(
2728 vote_state2
2729 .votes()
2730 .iter()
2731 .map(|vote| vote.slot())
2732 .collect::<Vec<Slot>>(),
2733 vec![1, 2, 3, 5, 6, 8]
2734 );
2735
2736 assert_eq!(
2739 process_new_vote_state(
2740 &mut vote_state1,
2741 vote_state2.votes().clone(),
2742 vote_state2.root_slot(),
2743 None,
2744 vote_state2.current_epoch(),
2745 0,
2746 ),
2747 Err(VoteError::LockoutConflict)
2748 );
2749 }
2750
2751 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2752 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2753 fn test_process_new_vote_state_expired_ancestor_not_removed(mut vote_state1: VoteStateHandler) {
2754 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 3, 9]);
2756 assert_eq!(
2757 vote_state1
2758 .votes()
2759 .iter()
2760 .map(|vote| vote.slot())
2761 .collect::<Vec<Slot>>(),
2762 vec![1, 9]
2763 );
2764
2765 let mut vote_state2 = vote_state1.clone();
2768 process_slot_vote_unchecked(&mut vote_state2, 10);
2769
2770 assert_eq!(vote_state2.votes()[0].slot(), 1);
2773 assert_eq!(vote_state2.votes()[0].lockout.last_locked_out_slot(), 9);
2774 assert_eq!(
2775 vote_state2
2776 .votes()
2777 .iter()
2778 .map(|vote| vote.slot())
2779 .collect::<Vec<Slot>>(),
2780 vec![1, 9, 10]
2781 );
2782
2783 process_new_vote_state(
2785 &mut vote_state1,
2786 vote_state2.votes().clone(),
2787 vote_state2.root_slot(),
2788 None,
2789 vote_state2.current_epoch(),
2790 0,
2791 )
2792 .unwrap();
2793 assert_eq!(vote_state1, vote_state2,);
2794 }
2795
2796 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2797 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2798 fn test_process_new_vote_current_state_contains_bigger_slots(
2799 mut vote_state1: VoteStateHandler,
2800 ) {
2801 process_slot_votes_unchecked(&mut vote_state1, &[6, 7, 8]);
2802 assert_eq!(
2803 vote_state1
2804 .votes()
2805 .iter()
2806 .map(|vote| vote.slot())
2807 .collect::<Vec<Slot>>(),
2808 vec![6, 7, 8]
2809 );
2810
2811 let bad_votes: VecDeque<Lockout> = vec![
2813 Lockout::new_with_confirmation_count(2, 5),
2814 Lockout::new_with_confirmation_count(14, 1),
2816 ]
2817 .into_iter()
2818 .collect();
2819 let root = Some(1);
2820
2821 let current_epoch = vote_state1.current_epoch();
2822 assert_eq!(
2823 process_new_vote_state_from_lockouts(
2824 &mut vote_state1,
2825 bad_votes,
2826 root,
2827 None,
2828 current_epoch,
2829 ),
2830 Err(VoteError::LockoutConflict)
2831 );
2832
2833 let good_votes: VecDeque<LandedVote> = vec![
2834 Lockout::new_with_confirmation_count(2, 5).into(),
2835 Lockout::new_with_confirmation_count(15, 1).into(),
2836 ]
2837 .into_iter()
2838 .collect();
2839
2840 let current_epoch = vote_state1.current_epoch();
2841 process_new_vote_state(
2842 &mut vote_state1,
2843 good_votes.clone(),
2844 root,
2845 None,
2846 current_epoch,
2847 0,
2848 )
2849 .unwrap();
2850 assert_eq!(*vote_state1.votes(), good_votes);
2851 }
2852
2853 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2854 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2855 fn test_filter_old_votes(mut vote_state: VoteStateHandler) {
2856 let old_vote_slot = 1;
2857 let vote = Vote::new(vec![old_vote_slot], Hash::default());
2858
2859 let slot_hashes = vec![(3, Hash::new_unique()), (2, Hash::new_unique())];
2862 assert_eq!(
2863 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
2864 Err(VoteError::VotesTooOldAllFiltered)
2865 );
2866
2867 let vote_slot = 2;
2870 let vote_slot_hash = slot_hashes
2871 .iter()
2872 .find(|(slot, _hash)| *slot == vote_slot)
2873 .unwrap()
2874 .1;
2875
2876 let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
2877 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0).unwrap();
2878 assert_eq!(
2879 vote_state
2880 .votes()
2881 .iter()
2882 .map(|vote| vote.lockout)
2883 .collect::<Vec<Lockout>>(),
2884 vec![Lockout::new_with_confirmation_count(vote_slot, 1)]
2885 );
2886 }
2887
2888 fn build_slot_hashes(slots: Vec<Slot>) -> Vec<(Slot, Hash)> {
2889 slots
2890 .iter()
2891 .rev()
2892 .map(|x| (*x, Hash::new_unique()))
2893 .collect()
2894 }
2895
2896 fn build_vote_state(
2897 target_version: VoteStateTargetVersion,
2898 vote_slots: Vec<Slot>,
2899 slot_hashes: &[(Slot, Hash)],
2900 ) -> VoteStateHandler {
2901 let mut vote_state = match target_version {
2902 VoteStateTargetVersion::V3 => VoteStateHandler::default_v3(),
2903 VoteStateTargetVersion::V4 => VoteStateHandler::default_v4(),
2904 };
2905
2906 if !vote_slots.is_empty() {
2907 let vote_hash = slot_hashes
2908 .iter()
2909 .find(|(slot, _hash)| slot == vote_slots.last().unwrap())
2910 .unwrap()
2911 .1;
2912 let vote = Vote::new(vote_slots, vote_hash);
2913 process_vote_unfiltered(&mut vote_state, &vote.slots, &vote, slot_hashes, 0, 0)
2914 .unwrap();
2915 }
2916
2917 vote_state
2918 }
2919
2920 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
2921 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
2922 fn test_check_and_filter_proposed_vote_state_empty(target_version: VoteStateTargetVersion) {
2923 let empty_slot_hashes = build_slot_hashes(vec![]);
2924 let empty_vote_state = build_vote_state(target_version, vec![], &empty_slot_hashes);
2925
2926 let mut tower_sync = TowerSync::from(vec![]);
2928 assert_eq!(
2929 check_and_filter_proposed_vote_state(
2930 &empty_vote_state,
2931 &mut tower_sync.lockouts,
2932 &mut tower_sync.root,
2933 tower_sync.hash,
2934 &empty_slot_hashes
2935 ),
2936 Err(VoteError::EmptySlots),
2937 );
2938
2939 let mut tower_sync = TowerSync::from(vec![(0, 1)]);
2941 assert_eq!(
2942 check_and_filter_proposed_vote_state(
2943 &empty_vote_state,
2944 &mut tower_sync.lockouts,
2945 &mut tower_sync.root,
2946 tower_sync.hash,
2947 &empty_slot_hashes
2948 ),
2949 Err(VoteError::SlotsMismatch),
2950 );
2951 }
2952
2953 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
2954 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
2955 fn test_check_and_filter_proposed_vote_state_too_old(target_version: VoteStateTargetVersion) {
2956 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
2957 let latest_vote = 4;
2958 let vote_state = build_vote_state(target_version, vec![1, 2, 3, latest_vote], &slot_hashes);
2959
2960 let mut tower_sync = TowerSync::from(vec![(latest_vote, 1)]);
2963 assert_eq!(
2964 check_and_filter_proposed_vote_state(
2965 &vote_state,
2966 &mut tower_sync.lockouts,
2967 &mut tower_sync.root,
2968 tower_sync.hash,
2969 &slot_hashes
2970 ),
2971 Err(VoteError::VoteTooOld),
2972 );
2973
2974 let earliest_slot_in_history = latest_vote + 2;
2978 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history]);
2979 let mut tower_sync = TowerSync::from(vec![(earliest_slot_in_history - 1, 1)]);
2980 assert_eq!(
2981 check_and_filter_proposed_vote_state(
2982 &vote_state,
2983 &mut tower_sync.lockouts,
2984 &mut tower_sync.root,
2985 tower_sync.hash,
2986 &slot_hashes
2987 ),
2988 Err(VoteError::VoteTooOld),
2989 );
2990 }
2991
2992 fn run_test_check_and_filter_proposed_vote_state_older_than_history_root(
2993 target_version: VoteStateTargetVersion,
2994 earliest_slot_in_history: Slot,
2995 current_vote_state_slots: Vec<Slot>,
2996 current_vote_state_root: Option<Slot>,
2997 proposed_slots_and_lockouts: Vec<(Slot, u32)>,
2998 proposed_root: Slot,
2999 expected_root: Option<Slot>,
3000 expected_vote_state: Vec<Lockout>,
3001 ) {
3002 assert!(proposed_root < earliest_slot_in_history);
3003 assert_eq!(
3004 expected_root,
3005 current_vote_state_slots
3006 .iter()
3007 .rev()
3008 .find(|slot| **slot <= proposed_root)
3009 .cloned()
3010 );
3011 let latest_slot_in_history = proposed_slots_and_lockouts
3012 .last()
3013 .unwrap()
3014 .0
3015 .max(earliest_slot_in_history);
3016 let mut slot_hashes = build_slot_hashes(
3017 (current_vote_state_slots.first().copied().unwrap_or(0)..=latest_slot_in_history)
3018 .collect::<Vec<Slot>>(),
3019 );
3020
3021 let mut vote_state =
3022 build_vote_state(target_version, current_vote_state_slots, &slot_hashes);
3023 vote_state.set_root_slot(current_vote_state_root);
3024
3025 slot_hashes.retain(|slot| slot.0 >= earliest_slot_in_history);
3026 assert!(!proposed_slots_and_lockouts.is_empty());
3027 let proposed_hash = slot_hashes
3028 .iter()
3029 .find(|(slot, _hash)| *slot == proposed_slots_and_lockouts.last().unwrap().0)
3030 .unwrap()
3031 .1;
3032
3033 let mut tower_sync = TowerSync::from(proposed_slots_and_lockouts);
3037 tower_sync.hash = proposed_hash;
3038 tower_sync.root = Some(proposed_root);
3039 check_and_filter_proposed_vote_state(
3040 &vote_state,
3041 &mut tower_sync.lockouts,
3042 &mut tower_sync.root,
3043 tower_sync.hash,
3044 &slot_hashes,
3045 )
3046 .unwrap();
3047 assert_eq!(tower_sync.root, expected_root);
3048
3049 assert!(
3052 do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync.clone(),).is_ok()
3053 );
3054 assert_eq!(vote_state.root_slot(), expected_root);
3055 assert_eq!(
3056 vote_state
3057 .votes()
3058 .iter()
3059 .map(|vote| vote.lockout)
3060 .collect::<Vec<Lockout>>(),
3061 expected_vote_state,
3062 );
3063 }
3064
3065 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3066 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3067 fn test_check_and_filter_proposed_vote_state_older_than_history_root(
3068 target_version: VoteStateTargetVersion,
3069 ) {
3070 let earliest_slot_in_history = 5;
3073 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3074 let current_vote_state_root = None;
3075 let proposed_slots_and_lockouts = vec![(5, 1)];
3076 let proposed_root = 4;
3077 let expected_root = Some(4);
3078 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3079 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3080 target_version,
3081 earliest_slot_in_history,
3082 current_vote_state_slots,
3083 current_vote_state_root,
3084 proposed_slots_and_lockouts,
3085 proposed_root,
3086 expected_root,
3087 expected_vote_state,
3088 );
3089
3090 let earliest_slot_in_history = 5;
3093 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3094 let current_vote_state_root = Some(0);
3095 let proposed_slots_and_lockouts = vec![(5, 1)];
3096 let proposed_root = 4;
3097 let expected_root = Some(4);
3098 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3099 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3100 target_version,
3101 earliest_slot_in_history,
3102 current_vote_state_slots,
3103 current_vote_state_root,
3104 proposed_slots_and_lockouts,
3105 proposed_root,
3106 expected_root,
3107 expected_vote_state,
3108 );
3109
3110 let earliest_slot_in_history = 5;
3113 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3114 let current_vote_state_root = Some(0);
3115 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3116 let proposed_root = 3;
3117 let expected_root = Some(3);
3118 let expected_vote_state = vec![
3119 Lockout::new_with_confirmation_count(4, 2),
3120 Lockout::new_with_confirmation_count(5, 1),
3121 ];
3122 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3123 target_version,
3124 earliest_slot_in_history,
3125 current_vote_state_slots,
3126 current_vote_state_root,
3127 proposed_slots_and_lockouts,
3128 proposed_root,
3129 expected_root,
3130 expected_vote_state,
3131 );
3132
3133 let earliest_slot_in_history = 5;
3135 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 4];
3136 let current_vote_state_root = Some(0);
3137 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3138 let proposed_root = 3;
3139 let expected_root = Some(2);
3140 let expected_vote_state = vec![
3141 Lockout::new_with_confirmation_count(4, 2),
3142 Lockout::new_with_confirmation_count(5, 1),
3143 ];
3144 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3145 target_version,
3146 earliest_slot_in_history,
3147 current_vote_state_slots,
3148 current_vote_state_root,
3149 proposed_slots_and_lockouts,
3150 proposed_root,
3151 expected_root,
3152 expected_vote_state,
3153 );
3154
3155 let earliest_slot_in_history = 4;
3158 let current_vote_state_slots: Vec<Slot> = vec![3, 4];
3159 let current_vote_state_root = None;
3160 let proposed_slots_and_lockouts = vec![(3, 3), (4, 2), (5, 1)];
3161 let proposed_root = 2;
3162 let expected_root = None;
3163 let expected_vote_state = vec![
3164 Lockout::new_with_confirmation_count(3, 3),
3165 Lockout::new_with_confirmation_count(4, 2),
3166 Lockout::new_with_confirmation_count(5, 1),
3167 ];
3168 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3169 target_version,
3170 earliest_slot_in_history,
3171 current_vote_state_slots,
3172 current_vote_state_root,
3173 proposed_slots_and_lockouts,
3174 proposed_root,
3175 expected_root,
3176 expected_vote_state,
3177 );
3178
3179 let earliest_slot_in_history = 4;
3181 let current_vote_state_slots: Vec<Slot> = vec![];
3182 let current_vote_state_root = None;
3183 let proposed_slots_and_lockouts = vec![(5, 1)];
3184 let proposed_root = 2;
3185 let expected_root = None;
3186 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3187 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3188 target_version,
3189 earliest_slot_in_history,
3190 current_vote_state_slots,
3191 current_vote_state_root,
3192 proposed_slots_and_lockouts,
3193 proposed_root,
3194 expected_root,
3195 expected_vote_state,
3196 );
3197 }
3198
3199 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3200 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3201 fn test_check_and_filter_proposed_vote_state_slots_not_ordered(
3202 target_version: VoteStateTargetVersion,
3203 ) {
3204 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3205 let vote_state = build_vote_state(target_version, vec![1], &slot_hashes);
3206
3207 let vote_slot = 3;
3209 let vote_slot_hash = slot_hashes
3210 .iter()
3211 .find(|(slot, _hash)| *slot == vote_slot)
3212 .unwrap()
3213 .1;
3214 let mut tower_sync = TowerSync::from(vec![(2, 2), (1, 3), (vote_slot, 1)]);
3215 tower_sync.hash = vote_slot_hash;
3216 assert_eq!(
3217 check_and_filter_proposed_vote_state(
3218 &vote_state,
3219 &mut tower_sync.lockouts,
3220 &mut tower_sync.root,
3221 tower_sync.hash,
3222 &slot_hashes
3223 ),
3224 Err(VoteError::SlotsNotOrdered),
3225 );
3226
3227 let mut tower_sync = TowerSync::from(vec![(2, 2), (2, 2), (vote_slot, 1)]);
3229 tower_sync.hash = vote_slot_hash;
3230 assert_eq!(
3231 check_and_filter_proposed_vote_state(
3232 &vote_state,
3233 &mut tower_sync.lockouts,
3234 &mut tower_sync.root,
3235 tower_sync.hash,
3236 &slot_hashes
3237 ),
3238 Err(VoteError::SlotsNotOrdered),
3239 );
3240 }
3241
3242 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3243 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3244 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered(
3245 target_version: VoteStateTargetVersion,
3246 ) {
3247 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3248 let mut vote_state = build_vote_state(target_version, vec![1, 2, 3, 4], &slot_hashes);
3249
3250 let earliest_slot_in_history = 11;
3255 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3256 let vote_slot = 12;
3257 let vote_slot_hash = slot_hashes
3258 .iter()
3259 .find(|(slot, _hash)| *slot == vote_slot)
3260 .unwrap()
3261 .1;
3262 let missing_older_than_history_slot = earliest_slot_in_history - 1;
3263 let mut tower_sync = TowerSync::from(vec![
3264 (1, 4),
3265 (missing_older_than_history_slot, 2),
3266 (vote_slot, 3),
3267 ]);
3268 tower_sync.hash = vote_slot_hash;
3269 check_and_filter_proposed_vote_state(
3270 &vote_state,
3271 &mut tower_sync.lockouts,
3272 &mut tower_sync.root,
3273 tower_sync.hash,
3274 &slot_hashes,
3275 )
3276 .unwrap();
3277
3278 assert_eq!(
3280 tower_sync
3281 .clone()
3282 .lockouts
3283 .into_iter()
3284 .collect::<Vec<Lockout>>(),
3285 vec![
3286 Lockout::new_with_confirmation_count(1, 4),
3287 Lockout::new_with_confirmation_count(vote_slot, 3)
3288 ]
3289 );
3290 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3291 }
3292
3293 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3294 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3295 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_not_filtered(
3296 target_version: VoteStateTargetVersion,
3297 ) {
3298 let slot_hashes = build_slot_hashes(vec![4]);
3299 let mut vote_state = build_vote_state(target_version, vec![4], &slot_hashes);
3300
3301 let earliest_slot_in_history = 11;
3306 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3307 let vote_slot = 12;
3308 let vote_slot_hash = slot_hashes
3309 .iter()
3310 .find(|(slot, _hash)| *slot == vote_slot)
3311 .unwrap()
3312 .1;
3313 let existing_older_than_history_slot = 4;
3314 let mut tower_sync =
3315 TowerSync::from(vec![(existing_older_than_history_slot, 3), (vote_slot, 2)]);
3316 tower_sync.hash = vote_slot_hash;
3317 check_and_filter_proposed_vote_state(
3318 &vote_state,
3319 &mut tower_sync.lockouts,
3320 &mut tower_sync.root,
3321 tower_sync.hash,
3322 &slot_hashes,
3323 )
3324 .unwrap();
3325 assert_eq!(tower_sync.lockouts.len(), 2);
3327 assert_eq!(
3328 tower_sync
3329 .clone()
3330 .lockouts
3331 .into_iter()
3332 .collect::<Vec<Lockout>>(),
3333 vec![
3334 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3335 Lockout::new_with_confirmation_count(vote_slot, 2)
3336 ]
3337 );
3338 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3339 }
3340
3341 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3342 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3343 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered_and_not_filtered(
3344 target_version: VoteStateTargetVersion,
3345 ) {
3346 let slot_hashes = build_slot_hashes(vec![6]);
3347 let mut vote_state = build_vote_state(target_version, vec![6], &slot_hashes);
3348
3349 let earliest_slot_in_history = 11;
3360 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3361 let vote_slot = 14;
3362 let vote_slot_hash = slot_hashes
3363 .iter()
3364 .find(|(slot, _hash)| *slot == vote_slot)
3365 .unwrap()
3366 .1;
3367
3368 let missing_older_than_history_slot = 4;
3369 let existing_older_than_history_slot = 6;
3370
3371 let mut tower_sync = TowerSync::from(vec![
3372 (missing_older_than_history_slot, 4),
3373 (existing_older_than_history_slot, 3),
3374 (12, 2),
3375 (vote_slot, 1),
3376 ]);
3377 tower_sync.hash = vote_slot_hash;
3378 check_and_filter_proposed_vote_state(
3379 &vote_state,
3380 &mut tower_sync.lockouts,
3381 &mut tower_sync.root,
3382 tower_sync.hash,
3383 &slot_hashes,
3384 )
3385 .unwrap();
3386 assert_eq!(tower_sync.lockouts.len(), 3);
3387 assert_eq!(
3388 tower_sync
3389 .clone()
3390 .lockouts
3391 .into_iter()
3392 .collect::<Vec<Lockout>>(),
3393 vec![
3394 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3395 Lockout::new_with_confirmation_count(12, 2),
3396 Lockout::new_with_confirmation_count(vote_slot, 1)
3397 ]
3398 );
3399 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3400 }
3401
3402 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3403 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3404 fn test_check_and_filter_proposed_vote_state_slot_not_on_fork(
3405 target_version: VoteStateTargetVersion,
3406 ) {
3407 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3408 let vote_state = build_vote_state(target_version, vec![2, 4, 6], &slot_hashes);
3409
3410 let missing_vote_slot = 3;
3416
3417 let vote_slot = vote_state.votes().back().unwrap().slot() + 2;
3420 let vote_slot_hash = slot_hashes
3421 .iter()
3422 .find(|(slot, _hash)| *slot == vote_slot)
3423 .unwrap()
3424 .1;
3425 let mut tower_sync = TowerSync::from(vec![(missing_vote_slot, 2), (vote_slot, 3)]);
3426 tower_sync.hash = vote_slot_hash;
3427 assert_eq!(
3428 check_and_filter_proposed_vote_state(
3429 &vote_state,
3430 &mut tower_sync.lockouts,
3431 &mut tower_sync.root,
3432 tower_sync.hash,
3433 &slot_hashes
3434 ),
3435 Err(VoteError::SlotsMismatch),
3436 );
3437
3438 let missing_vote_slot = 7;
3440 let mut tower_sync = TowerSync::from(vec![
3441 (2, 5),
3442 (4, 4),
3443 (6, 3),
3444 (missing_vote_slot, 2),
3445 (vote_slot, 1),
3446 ]);
3447 tower_sync.hash = vote_slot_hash;
3448 assert_eq!(
3449 check_and_filter_proposed_vote_state(
3450 &vote_state,
3451 &mut tower_sync.lockouts,
3452 &mut tower_sync.root,
3453 tower_sync.hash,
3454 &slot_hashes
3455 ),
3456 Err(VoteError::SlotsMismatch),
3457 );
3458 }
3459
3460 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3461 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3462 fn test_check_and_filter_proposed_vote_state_root_on_different_fork(
3463 target_version: VoteStateTargetVersion,
3464 ) {
3465 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3466 let vote_state = build_vote_state(target_version, vec![6], &slot_hashes);
3467
3468 let new_root = 3;
3474
3475 let vote_slot = 8;
3478 assert_eq!(vote_slot, slot_hashes.first().unwrap().0);
3479 let vote_slot_hash = slot_hashes
3480 .iter()
3481 .find(|(slot, _hash)| *slot == vote_slot)
3482 .unwrap()
3483 .1;
3484 let mut tower_sync = TowerSync::from(vec![(vote_slot, 1)]);
3485 tower_sync.hash = vote_slot_hash;
3486 tower_sync.root = Some(new_root);
3487 assert_eq!(
3488 check_and_filter_proposed_vote_state(
3489 &vote_state,
3490 &mut tower_sync.lockouts,
3491 &mut tower_sync.root,
3492 tower_sync.hash,
3493 &slot_hashes
3494 ),
3495 Err(VoteError::RootOnDifferentFork),
3496 );
3497 }
3498
3499 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3500 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3501 fn test_check_and_filter_proposed_vote_state_slot_newer_than_slot_history(
3502 target_version: VoteStateTargetVersion,
3503 ) {
3504 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3505 let vote_state = build_vote_state(target_version, vec![2, 4, 6], &slot_hashes);
3506
3507 let missing_vote_slot = slot_hashes.first().unwrap().0 + 1;
3513 let vote_slot_hash = Hash::new_unique();
3514 let mut tower_sync = TowerSync::from(vec![(8, 2), (missing_vote_slot, 3)]);
3515 tower_sync.hash = vote_slot_hash;
3516 assert_eq!(
3517 check_and_filter_proposed_vote_state(
3518 &vote_state,
3519 &mut tower_sync.lockouts,
3520 &mut tower_sync.root,
3521 tower_sync.hash,
3522 &slot_hashes
3523 ),
3524 Err(VoteError::SlotsMismatch),
3525 );
3526 }
3527
3528 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3529 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3530 fn test_check_and_filter_proposed_vote_state_slot_all_slot_hashes_in_update_ok(
3531 target_version: VoteStateTargetVersion,
3532 ) {
3533 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3534 let mut vote_state = build_vote_state(target_version, vec![2, 4, 6], &slot_hashes);
3535
3536 let vote_slot = vote_state.votes().back().unwrap().slot() + 2;
3542 let vote_slot_hash = slot_hashes
3543 .iter()
3544 .find(|(slot, _hash)| *slot == vote_slot)
3545 .unwrap()
3546 .1;
3547 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3548 tower_sync.hash = vote_slot_hash;
3549 check_and_filter_proposed_vote_state(
3550 &vote_state,
3551 &mut tower_sync.lockouts,
3552 &mut tower_sync.root,
3553 tower_sync.hash,
3554 &slot_hashes,
3555 )
3556 .unwrap();
3557
3558 assert_eq!(
3560 tower_sync
3561 .clone()
3562 .lockouts
3563 .into_iter()
3564 .collect::<Vec<Lockout>>(),
3565 vec![
3566 Lockout::new_with_confirmation_count(2, 4),
3567 Lockout::new_with_confirmation_count(4, 3),
3568 Lockout::new_with_confirmation_count(6, 2),
3569 Lockout::new_with_confirmation_count(vote_slot, 1)
3570 ]
3571 );
3572
3573 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3574 }
3575
3576 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3577 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3578 fn test_check_and_filter_proposed_vote_state_slot_some_slot_hashes_in_update_ok(
3579 target_version: VoteStateTargetVersion,
3580 ) {
3581 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3582 let mut vote_state = build_vote_state(target_version, vec![6], &slot_hashes);
3583
3584 let vote_slot = vote_state.votes().back().unwrap().slot() + 2;
3590 let vote_slot_hash = slot_hashes
3591 .iter()
3592 .find(|(slot, _hash)| *slot == vote_slot)
3593 .unwrap()
3594 .1;
3595 let mut tower_sync = TowerSync::from(vec![(4, 2), (vote_slot, 1)]);
3596 tower_sync.hash = vote_slot_hash;
3597 check_and_filter_proposed_vote_state(
3598 &vote_state,
3599 &mut tower_sync.lockouts,
3600 &mut tower_sync.root,
3601 tower_sync.hash,
3602 &slot_hashes,
3603 )
3604 .unwrap();
3605
3606 assert_eq!(
3608 tower_sync
3609 .clone()
3610 .lockouts
3611 .into_iter()
3612 .collect::<Vec<Lockout>>(),
3613 vec![
3614 Lockout::new_with_confirmation_count(4, 2),
3615 Lockout::new_with_confirmation_count(vote_slot, 1)
3616 ]
3617 );
3618
3619 assert_eq!(
3623 do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,),
3624 Err(VoteError::LockoutConflict)
3625 );
3626 }
3627
3628 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3629 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3630 fn test_check_and_filter_proposed_vote_state_slot_hash_mismatch(
3631 target_version: VoteStateTargetVersion,
3632 ) {
3633 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3634 let vote_state = build_vote_state(target_version, vec![2, 4, 6], &slot_hashes);
3635
3636 let vote_slot = vote_state.votes().back().unwrap().slot() + 2;
3641 let vote_slot_hash = Hash::new_unique();
3642 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3643 tower_sync.hash = vote_slot_hash;
3644 assert_eq!(
3645 check_and_filter_proposed_vote_state(
3646 &vote_state,
3647 &mut tower_sync.lockouts,
3648 &mut tower_sync.root,
3649 tower_sync.hash,
3650 &slot_hashes,
3651 ),
3652 Err(VoteError::SlotHashMismatch),
3653 );
3654 }
3655
3656 #[test_case(0, true; "first slot")]
3657 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3658 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3659 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3660 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3661 fn test_epoch_half_check(slot: Slot, expected_allowed: bool) {
3662 let epoch_schedule = EpochSchedule::without_warmup();
3663 assert_eq!(
3664 is_commission_update_allowed(slot, &epoch_schedule),
3665 expected_allowed
3666 );
3667 }
3668
3669 #[test]
3670 fn test_warmup_epoch_half_check_with_warmup() {
3671 let epoch_schedule = EpochSchedule::default();
3672 let first_normal_slot = epoch_schedule.first_normal_slot;
3673 assert!(is_commission_update_allowed(0, &epoch_schedule));
3675 assert!(is_commission_update_allowed(
3678 first_normal_slot - 1,
3679 &epoch_schedule
3680 ));
3681 }
3682
3683 #[test_case(0, true; "first slot")]
3684 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3685 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3686 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3687 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3688 fn test_epoch_half_check_with_warmup(slot: Slot, expected_allowed: bool) {
3689 let epoch_schedule = EpochSchedule::default();
3690 let first_normal_slot = epoch_schedule.first_normal_slot;
3691 assert_eq!(
3692 is_commission_update_allowed(first_normal_slot.saturating_add(slot), &epoch_schedule),
3693 expected_allowed
3694 );
3695 }
3696
3697 #[test]
3698 fn test_create_v4_account_with_authorized() {
3699 let node_pubkey = Pubkey::new_unique();
3700 let authorized_voter = Pubkey::new_unique();
3701 let authorized_withdrawer = Pubkey::new_unique();
3702 let bls_pubkey_compressed = [42; 48];
3703 let inflation_rewards_commission_bps = 10000;
3704 let lamports = 100;
3705 let vote_account = create_v4_account_with_authorized(
3706 &node_pubkey,
3707 &authorized_voter,
3708 &authorized_withdrawer,
3709 Some(bls_pubkey_compressed),
3710 inflation_rewards_commission_bps,
3711 lamports,
3712 );
3713 assert_eq!(vote_account.lamports(), lamports);
3714 assert_eq!(vote_account.owner(), &id());
3715 assert_eq!(vote_account.data().len(), VoteStateV4::size_of());
3716 let vote_state_v4 = VoteStateV4::deserialize(vote_account.data(), &node_pubkey).unwrap();
3717 assert_eq!(vote_state_v4.node_pubkey, node_pubkey);
3718 assert_eq!(
3719 vote_state_v4.authorized_voters,
3720 AuthorizedVoters::new(0, authorized_voter)
3721 );
3722 assert_eq!(vote_state_v4.authorized_withdrawer, authorized_withdrawer);
3723 assert_eq!(
3724 vote_state_v4.bls_pubkey_compressed,
3725 Some(bls_pubkey_compressed)
3726 );
3727 assert_eq!(
3728 vote_state_v4.inflation_rewards_commission_bps,
3729 inflation_rewards_commission_bps
3730 );
3731 }
3732
3733 #[test]
3734 fn test_update_validator_identity_syncs_block_revenue_collector() {
3735 let vote_state =
3736 vote_state_new_for_test(&solana_pubkey::new_rand(), VoteStateTargetVersion::V4);
3737 let node_pubkey = *vote_state.node_pubkey();
3738 let withdrawer_pubkey = *vote_state.authorized_withdrawer();
3739
3740 let serialized = vote_state.serialize();
3741 let serialized_len = serialized.len();
3742 let rent = Rent::default();
3743 let lamports = rent.minimum_balance(serialized_len);
3744 let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id());
3745 vote_account.set_data_from_slice(&serialized);
3746
3747 let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id());
3748 let mut transaction_context = TransactionContext::new(
3749 vec![(id(), processor_account), (node_pubkey, vote_account)],
3750 rent,
3751 0,
3752 0,
3753 );
3754 transaction_context
3755 .configure_next_instruction_for_tests(
3756 0,
3757 vec![InstructionAccount::new(1, false, true)],
3758 vec![],
3759 )
3760 .unwrap();
3761 let instruction_context = transaction_context.get_next_instruction_context().unwrap();
3762 let mut borrowed_account = instruction_context
3763 .try_borrow_instruction_account(0)
3764 .unwrap();
3765
3766 let new_node_pubkey = solana_pubkey::new_rand();
3767 let signers: HashSet<Pubkey> = vec![withdrawer_pubkey, new_node_pubkey]
3768 .into_iter()
3769 .collect();
3770
3771 update_validator_identity(
3772 &mut borrowed_account,
3773 VoteStateTargetVersion::V4,
3774 &new_node_pubkey,
3775 &signers,
3776 )
3777 .unwrap();
3778
3779 let vote_state =
3782 VoteStateV4::deserialize(borrowed_account.get_data(), &new_node_pubkey).unwrap();
3783 assert_eq!(vote_state.node_pubkey, new_node_pubkey);
3784 assert_eq!(vote_state.block_revenue_collector, new_node_pubkey);
3785
3786 let new_node_pubkey = solana_pubkey::new_rand();
3788 let signers: HashSet<Pubkey> = vec![withdrawer_pubkey, new_node_pubkey]
3789 .into_iter()
3790 .collect();
3791
3792 update_validator_identity(
3793 &mut borrowed_account,
3794 VoteStateTargetVersion::V4,
3795 &new_node_pubkey,
3796 &signers,
3797 )
3798 .unwrap();
3799
3800 let vote_state =
3801 VoteStateV4::deserialize(borrowed_account.get_data(), &new_node_pubkey).unwrap();
3802 assert_eq!(vote_state.node_pubkey, new_node_pubkey);
3803 assert_eq!(vote_state.block_revenue_collector, new_node_pubkey);
3804 }
3805}