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
1063#[cfg(test)]
1064pub fn create_account_with_authorized(
1065 node_pubkey: &Pubkey,
1066 authorized_voter: &Pubkey,
1067 authorized_withdrawer: &Pubkey,
1068 commission: u8,
1069 lamports: u64,
1070) -> AccountSharedData {
1071 let mut vote_account = AccountSharedData::new(lamports, VoteStateV3::size_of(), &id());
1072
1073 let vote_state = VoteStateV3::new(
1074 &VoteInit {
1075 node_pubkey: *node_pubkey,
1076 authorized_voter: *authorized_voter,
1077 authorized_withdrawer: *authorized_withdrawer,
1078 commission,
1079 },
1080 &Clock::default(),
1081 );
1082
1083 VoteStateV3::serialize(
1084 &VoteStateVersions::V3(Box::new(vote_state)),
1085 vote_account.data_as_mut_slice(),
1086 )
1087 .unwrap();
1088
1089 vote_account
1090}
1091
1092pub fn create_v4_account_with_authorized(
1093 node_pubkey: &Pubkey,
1094 authorized_voter: &Pubkey,
1095 authorized_withdrawer: &Pubkey,
1096 bls_pubkey_compressed: Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]>,
1097 inflation_rewards_commission_bps: u16,
1098 lamports: u64,
1099) -> AccountSharedData {
1100 let mut vote_account = AccountSharedData::new(lamports, VoteStateV4::size_of(), &id());
1101
1102 let vote_state = handler::create_new_vote_state_v4_for_tests(
1103 node_pubkey,
1104 authorized_voter,
1105 authorized_withdrawer,
1106 bls_pubkey_compressed,
1107 inflation_rewards_commission_bps,
1108 );
1109
1110 VoteStateV4::serialize(
1111 &VoteStateVersions::V4(Box::new(vote_state)),
1112 vote_account.data_as_mut_slice(),
1113 )
1114 .unwrap();
1115
1116 vote_account
1117}
1118
1119#[allow(clippy::arithmetic_side_effects)]
1120#[cfg(test)]
1121mod tests {
1122 use {
1123 super::*,
1124 assert_matches::assert_matches,
1125 solana_account::{AccountSharedData, ReadableAccount},
1126 solana_clock::DEFAULT_SLOTS_PER_EPOCH,
1127 solana_sha256_hasher::hash,
1128 solana_transaction_context::{InstructionAccount, TransactionContext},
1129 solana_vote_interface::authorized_voters::AuthorizedVoters,
1130 test_case::test_case,
1131 };
1132
1133 const MAX_RECENT_VOTES: usize = 16;
1134
1135 fn vote_state_new_for_test(
1136 vote_pubkey: &Pubkey,
1137 target_version: VoteStateTargetVersion,
1138 ) -> VoteStateHandler {
1139 let auth_pubkey = solana_pubkey::new_rand();
1140 let vote_init = VoteInit {
1141 node_pubkey: solana_pubkey::new_rand(),
1142 authorized_voter: auth_pubkey,
1143 authorized_withdrawer: auth_pubkey,
1144 commission: 0,
1145 };
1146 let clock = Clock::default();
1147
1148 match target_version {
1149 VoteStateTargetVersion::V3 => {
1150 VoteStateHandler::new_v3(VoteStateV3::new(&vote_init, &clock))
1151 }
1152 VoteStateTargetVersion::V4 => VoteStateHandler::new_v4(
1153 handler::create_new_vote_state_v4(vote_pubkey, &vote_init, &clock),
1154 ),
1155 }
1156 }
1157
1158 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1159 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1160 fn test_vote_state_upgrade_from_1_14_11(target_version: VoteStateTargetVersion) {
1161 let vote_pubkey = solana_pubkey::new_rand();
1162 let mut vote_state = vote_state_new_for_test(&vote_pubkey, target_version);
1163
1164 vote_state.increment_credits(0, 100);
1166 assert_eq!(
1167 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 0, 1, |_pubkey| Ok(())),
1168 Ok(())
1169 );
1170 vote_state.increment_credits(1, 200);
1171 assert_eq!(
1172 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 1, 2, |_pubkey| Ok(())),
1173 Ok(())
1174 );
1175 vote_state.increment_credits(2, 300);
1176 assert_eq!(
1177 vote_state.set_new_authorized_voter(&solana_pubkey::new_rand(), 2, 3, |_pubkey| Ok(())),
1178 Ok(())
1179 );
1180
1181 vec![
1183 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
1184 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
1185 134, 135,
1186 ]
1187 .into_iter()
1188 .for_each(|v| vote_state.process_next_vote_slot(v, 4, 0));
1189
1190 let vote_state_v1_14_11 = match target_version {
1193 VoteStateTargetVersion::V3 => {
1194 VoteState1_14_11::from(vote_state.as_ref_v3().clone())
1196 }
1197 VoteStateTargetVersion::V4 => {
1198 VoteState1_14_11 {
1200 node_pubkey: *vote_state.node_pubkey(),
1201 authorized_withdrawer: *vote_state.authorized_withdrawer(),
1202 commission: vote_state.commission(),
1203 votes: vote_state
1204 .votes()
1205 .iter()
1206 .map(|landed_vote| (*landed_vote).into())
1207 .collect(),
1208 root_slot: vote_state.root_slot(),
1209 authorized_voters: vote_state.authorized_voters().clone(),
1210 epoch_credits: vote_state.epoch_credits().clone(),
1211 last_timestamp: vote_state.last_timestamp().clone(),
1212 prior_voters: CircBuf::default(), }
1214 }
1215 };
1216 let version1_14_11_serialized =
1217 bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(vote_state_v1_14_11)))
1218 .unwrap();
1219 let version1_14_11_serialized_len = version1_14_11_serialized.len();
1220 let rent = Rent::default();
1221 let lamports = rent.minimum_balance(version1_14_11_serialized_len);
1222 let mut vote_account =
1223 AccountSharedData::new(lamports, version1_14_11_serialized_len, &id());
1224 vote_account.set_data_from_slice(&version1_14_11_serialized);
1225
1226 let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id());
1229 let mut transaction_context = TransactionContext::new(
1230 vec![(id(), processor_account), (vote_pubkey, vote_account)],
1231 rent.clone(),
1232 0,
1233 0,
1234 );
1235 transaction_context
1236 .configure_next_instruction_for_tests(
1237 0,
1238 vec![InstructionAccount::new(1, false, true)],
1239 vec![],
1240 )
1241 .unwrap();
1242 let instruction_context = transaction_context.get_next_instruction_context().unwrap();
1243
1244 let mut borrowed_account = instruction_context
1247 .try_borrow_instruction_account(0)
1248 .unwrap();
1249
1250 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1252 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1253
1254 let converted_vote_state = get_vote_state_handler_checked(
1256 &borrowed_account,
1257 PreserveBehaviorInHandlerHelper::new(target_version, true),
1258 )
1259 .unwrap();
1260
1261 assert!(vote_state == converted_vote_state);
1263
1264 let vote_state = converted_vote_state;
1265
1266 match target_version {
1269 VoteStateTargetVersion::V3 => {
1270 assert_eq!(
1272 vote_state
1273 .clone()
1274 .set_vote_account_state(&mut borrowed_account),
1275 Ok(())
1276 );
1277 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1278 assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1279 }
1280 VoteStateTargetVersion::V4 => {
1281 assert_eq!(
1283 vote_state
1284 .clone()
1285 .set_vote_account_state(&mut borrowed_account),
1286 Err(InstructionError::AccountNotRentExempt)
1287 );
1288 }
1289 }
1290
1291 let converted_vote_state = get_vote_state_handler_checked(
1293 &borrowed_account,
1294 PreserveBehaviorInHandlerHelper::new(target_version, true),
1295 )
1296 .unwrap();
1297
1298 assert!(vote_state == converted_vote_state);
1300
1301 let vote_state = converted_vote_state;
1302
1303 let space = match target_version {
1305 VoteStateTargetVersion::V3 => VoteStateV3::size_of(),
1306 VoteStateTargetVersion::V4 => VoteStateV4::size_of(), };
1308 assert_eq!(
1309 borrowed_account.set_lamports(rent.minimum_balance(space)),
1310 Ok(())
1311 );
1312 assert_eq!(
1313 vote_state
1314 .clone()
1315 .set_vote_account_state(&mut borrowed_account),
1316 Ok(())
1317 );
1318
1319 let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1321 match target_version {
1322 VoteStateTargetVersion::V3 => {
1323 assert_matches!(vote_state_version, VoteStateVersions::V3(_));
1324 }
1325 VoteStateTargetVersion::V4 => {
1326 assert_matches!(vote_state_version, VoteStateVersions::V4(_));
1327 }
1328 }
1329
1330 let converted_vote_state = get_vote_state_handler_checked(
1332 &borrowed_account,
1333 PreserveBehaviorInHandlerHelper::new(target_version, true),
1334 )
1335 .unwrap();
1336
1337 assert_eq!(vote_state, converted_vote_state);
1339 }
1340
1341 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1342 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1343 fn test_vote_lockout(target_version: VoteStateTargetVersion) {
1344 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1345
1346 for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
1347 process_slot_vote_unchecked(&mut vote_state, (INITIAL_LOCKOUT * i) as u64);
1348 }
1349
1350 assert_eq!(vote_state.votes().len(), MAX_LOCKOUT_HISTORY);
1352 assert_eq!(vote_state.root_slot(), Some(0));
1353 check_lockouts(&vote_state);
1354
1355 let top_vote = vote_state.votes().front().unwrap().slot();
1359 let slot = vote_state.last_lockout().unwrap().last_locked_out_slot();
1360 process_slot_vote_unchecked(&mut vote_state, slot);
1361 assert_eq!(Some(top_vote), vote_state.root_slot());
1362
1363 let slot = vote_state
1365 .votes()
1366 .front()
1367 .unwrap()
1368 .lockout
1369 .last_locked_out_slot();
1370 process_slot_vote_unchecked(&mut vote_state, slot);
1371 assert_eq!(vote_state.votes().len(), 2);
1373 }
1374
1375 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1376 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1377 fn test_update_commission(target_version: VoteStateTargetVersion) {
1378 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1379 let node_pubkey = *vote_state.node_pubkey();
1380 let withdrawer_pubkey = *vote_state.authorized_withdrawer();
1381
1382 vote_state.set_commission(10);
1384
1385 let serialized = vote_state.serialize();
1386 let serialized_len = serialized.len();
1387 let rent = Rent::default();
1388 let lamports = rent.minimum_balance(serialized_len);
1389 let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id());
1390 vote_account.set_data_from_slice(&serialized);
1391
1392 let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id());
1395 let mut transaction_context = TransactionContext::new(
1396 vec![(id(), processor_account), (node_pubkey, vote_account)],
1397 rent,
1398 0,
1399 0,
1400 );
1401 transaction_context
1402 .configure_next_instruction_for_tests(
1403 0,
1404 vec![InstructionAccount::new(1, false, true)],
1405 vec![],
1406 )
1407 .unwrap();
1408 let instruction_context = transaction_context.get_next_instruction_context().unwrap();
1409
1410 let mut borrowed_account = instruction_context
1413 .try_borrow_instruction_account(0)
1414 .unwrap();
1415
1416 let epoch_schedule = std::sync::Arc::new(EpochSchedule::without_warmup());
1417
1418 let first_half_clock = std::sync::Arc::new(Clock {
1419 slot: epoch_schedule.slots_per_epoch / 4,
1420 ..Clock::default()
1421 });
1422
1423 let second_half_clock = std::sync::Arc::new(Clock {
1424 slot: (epoch_schedule.slots_per_epoch * 3) / 4,
1425 ..Clock::default()
1426 });
1427
1428 let signers: HashSet<Pubkey> = vec![withdrawer_pubkey].into_iter().collect();
1429
1430 assert_eq!(
1432 get_vote_state_handler_checked(
1433 &borrowed_account,
1434 PreserveBehaviorInHandlerHelper::new(target_version, true),
1435 )
1436 .unwrap()
1437 .commission(),
1438 10
1439 );
1440 assert_matches!(
1441 update_commission(
1442 &mut borrowed_account,
1443 target_version,
1444 11,
1445 &signers,
1446 &epoch_schedule,
1447 &first_half_clock,
1448 ),
1449 Ok(())
1450 );
1451 assert_eq!(
1452 get_vote_state_handler_checked(
1453 &borrowed_account,
1454 PreserveBehaviorInHandlerHelper::new(target_version, true),
1455 )
1456 .unwrap()
1457 .commission(),
1458 11
1459 );
1460
1461 assert_matches!(
1463 update_commission(
1464 &mut borrowed_account,
1465 target_version,
1466 12,
1467 &signers,
1468 &epoch_schedule,
1469 &second_half_clock,
1470 ),
1471 Err(_)
1472 );
1473 assert_eq!(
1474 get_vote_state_handler_checked(
1475 &borrowed_account,
1476 PreserveBehaviorInHandlerHelper::new(target_version, true),
1477 )
1478 .unwrap()
1479 .commission(),
1480 11
1481 );
1482
1483 assert_matches!(
1485 update_commission(
1486 &mut borrowed_account,
1487 target_version,
1488 10,
1489 &signers,
1490 &epoch_schedule,
1491 &first_half_clock,
1492 ),
1493 Ok(())
1494 );
1495 assert_eq!(
1496 get_vote_state_handler_checked(
1497 &borrowed_account,
1498 PreserveBehaviorInHandlerHelper::new(target_version, true),
1499 )
1500 .unwrap()
1501 .commission(),
1502 10
1503 );
1504
1505 assert_eq!(
1506 get_vote_state_handler_checked(
1507 &borrowed_account,
1508 PreserveBehaviorInHandlerHelper::new(target_version, true),
1509 )
1510 .unwrap()
1511 .commission(),
1512 10
1513 );
1514
1515 assert_matches!(
1516 update_commission(
1517 &mut borrowed_account,
1518 target_version,
1519 9,
1520 &signers,
1521 &epoch_schedule,
1522 &second_half_clock,
1523 ),
1524 Ok(())
1525 );
1526 assert_eq!(
1527 get_vote_state_handler_checked(
1528 &borrowed_account,
1529 PreserveBehaviorInHandlerHelper::new(target_version, true),
1530 )
1531 .unwrap()
1532 .commission(),
1533 9
1534 );
1535 }
1536
1537 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1538 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1539 fn test_vote_double_lockout_after_expiration(target_version: VoteStateTargetVersion) {
1540 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1541
1542 for i in 0..3 {
1543 process_slot_vote_unchecked(&mut vote_state, i as u64);
1544 }
1545
1546 check_lockouts(&vote_state);
1547
1548 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 1) as u64);
1552 check_lockouts(&vote_state);
1553
1554 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 2) as u64);
1557 check_lockouts(&vote_state);
1558
1559 process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 3) as u64);
1562 check_lockouts(&vote_state);
1563 }
1564
1565 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1566 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1567 fn test_expire_multiple_votes(target_version: VoteStateTargetVersion) {
1568 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1569
1570 for i in 0..3 {
1571 process_slot_vote_unchecked(&mut vote_state, i as u64);
1572 }
1573
1574 assert_eq!(vote_state.votes()[0].confirmation_count(), 3);
1575
1576 let expire_slot =
1578 vote_state.votes()[1].slot() + vote_state.votes()[1].lockout.lockout() + 1;
1579 process_slot_vote_unchecked(&mut vote_state, expire_slot);
1580 assert_eq!(vote_state.votes().len(), 2);
1581
1582 assert_eq!(vote_state.votes()[0].slot(), 0);
1584 assert_eq!(vote_state.votes()[1].slot(), expire_slot);
1585
1586 process_slot_vote_unchecked(&mut vote_state, expire_slot + 1);
1588
1589 assert_eq!(vote_state.votes()[0].confirmation_count(), 3);
1591
1592 assert_eq!(vote_state.votes()[1].confirmation_count(), 2);
1594 assert_eq!(vote_state.votes()[2].confirmation_count(), 1);
1595 }
1596
1597 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1598 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1599 fn test_vote_credits(target_version: VoteStateTargetVersion) {
1600 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1601
1602 for i in 0..MAX_LOCKOUT_HISTORY {
1603 process_slot_vote_unchecked(&mut vote_state, i as u64);
1604 }
1605
1606 assert_eq!(vote_state.credits(), 0);
1607
1608 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 1);
1609 assert_eq!(vote_state.credits(), 1);
1610 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 2);
1611 assert_eq!(vote_state.credits(), 2);
1612 process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 3);
1613 assert_eq!(vote_state.credits(), 3);
1614 }
1615
1616 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1617 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1618 fn test_duplicate_vote(target_version: VoteStateTargetVersion) {
1619 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1620 process_slot_vote_unchecked(&mut vote_state, 0);
1621 process_slot_vote_unchecked(&mut vote_state, 1);
1622 process_slot_vote_unchecked(&mut vote_state, 0);
1623 assert_eq!(vote_state.nth_recent_lockout(0).unwrap().slot(), 1);
1624 assert_eq!(vote_state.nth_recent_lockout(1).unwrap().slot(), 0);
1625 assert!(vote_state.nth_recent_lockout(2).is_none());
1626 }
1627
1628 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1629 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1630 fn test_nth_recent_lockout(target_version: VoteStateTargetVersion) {
1631 let mut vote_state = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1632 for i in 0..MAX_LOCKOUT_HISTORY {
1633 process_slot_vote_unchecked(&mut vote_state, i as u64);
1634 }
1635 for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
1636 assert_eq!(
1637 vote_state.nth_recent_lockout(i).unwrap().slot() as usize,
1638 MAX_LOCKOUT_HISTORY - i - 1,
1639 );
1640 }
1641 assert!(vote_state.nth_recent_lockout(MAX_LOCKOUT_HISTORY).is_none());
1642 }
1643
1644 fn check_lockouts(vote_state: &VoteStateHandler) {
1645 let votes = vote_state.votes();
1646 for (i, vote) in votes.iter().enumerate() {
1647 let num_votes = votes
1648 .len()
1649 .checked_sub(i)
1650 .expect("`i` is less than `vote_state.votes().len()`");
1651 assert_eq!(
1652 vote.lockout.lockout(),
1653 INITIAL_LOCKOUT.pow(num_votes as u32) as u64
1654 );
1655 }
1656 }
1657
1658 fn recent_votes(vote_state: &VoteStateHandler) -> Vec<Vote> {
1659 let votes = vote_state.votes();
1660 let start = votes.len().saturating_sub(MAX_RECENT_VOTES);
1661 (start..votes.len())
1662 .map(|i| Vote::new(vec![votes.get(i).unwrap().slot()], Hash::default()))
1663 .collect()
1664 }
1665
1666 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1668 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1669 fn test_process_missed_votes(target_version: VoteStateTargetVersion) {
1670 let mut vote_state_a = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1671 let mut vote_state_b = vote_state_new_for_test(&solana_pubkey::new_rand(), target_version);
1672
1673 (0..5).for_each(|i| process_slot_vote_unchecked(&mut vote_state_a, i as u64));
1675 assert_ne!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1676
1677 let slots = (0u64..MAX_RECENT_VOTES as u64).collect();
1679 let vote = Vote::new(slots, Hash::default());
1680 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
1681
1682 assert_eq!(
1683 process_vote(&mut vote_state_a, &vote, &slot_hashes, 0, 0),
1684 Ok(())
1685 );
1686 assert_eq!(
1687 process_vote(&mut vote_state_b, &vote, &slot_hashes, 0, 0),
1688 Ok(())
1689 );
1690 assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1691 }
1692
1693 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1694 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1695 fn test_process_vote_skips_old_vote(mut vote_state: VoteStateHandler) {
1696 let vote = Vote::new(vec![0], Hash::default());
1697 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1698 assert_eq!(
1699 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1700 Ok(())
1701 );
1702 let recent = recent_votes(&vote_state);
1703 assert_eq!(
1704 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1705 Err(VoteError::VoteTooOld)
1706 );
1707 assert_eq!(recent, recent_votes(&vote_state));
1708 }
1709
1710 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1711 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1712 fn test_check_slots_are_valid_vote_empty_slot_hashes(vote_state: VoteStateHandler) {
1713 let vote = Vote::new(vec![0], Hash::default());
1714 assert_eq!(
1715 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &[]),
1716 Err(VoteError::VoteTooOld)
1717 );
1718 }
1719
1720 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1721 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1722 fn test_check_slots_are_valid_new_vote(vote_state: VoteStateHandler) {
1723 let vote = Vote::new(vec![0], Hash::default());
1724 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1725 assert_eq!(
1726 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1727 Ok(())
1728 );
1729 }
1730
1731 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1732 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1733 fn test_check_slots_are_valid_bad_hash(vote_state: VoteStateHandler) {
1734 let vote = Vote::new(vec![0], Hash::default());
1735 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
1736 assert_eq!(
1737 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1738 Err(VoteError::SlotHashMismatch)
1739 );
1740 }
1741
1742 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1743 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1744 fn test_check_slots_are_valid_bad_slot(vote_state: VoteStateHandler) {
1745 let vote = Vote::new(vec![1], Hash::default());
1746 let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1747 assert_eq!(
1748 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1749 Err(VoteError::SlotsMismatch)
1750 );
1751 }
1752
1753 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1754 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1755 fn test_check_slots_are_valid_duplicate_vote(mut vote_state: VoteStateHandler) {
1756 let vote = Vote::new(vec![0], Hash::default());
1757 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1758 assert_eq!(
1759 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1760 Ok(())
1761 );
1762 assert_eq!(
1763 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1764 Err(VoteError::VoteTooOld)
1765 );
1766 }
1767
1768 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1769 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1770 fn test_check_slots_are_valid_next_vote(mut vote_state: VoteStateHandler) {
1771 let vote = Vote::new(vec![0], Hash::default());
1772 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1773 assert_eq!(
1774 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1775 Ok(())
1776 );
1777
1778 let vote = Vote::new(vec![0, 1], Hash::default());
1779 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1780 assert_eq!(
1781 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1782 Ok(())
1783 );
1784 }
1785
1786 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1787 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1788 fn test_check_slots_are_valid_next_vote_only(mut vote_state: VoteStateHandler) {
1789 let vote = Vote::new(vec![0], Hash::default());
1790 let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1791 assert_eq!(
1792 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1793 Ok(())
1794 );
1795
1796 let vote = Vote::new(vec![1], Hash::default());
1797 let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1798 assert_eq!(
1799 check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1800 Ok(())
1801 );
1802 }
1803
1804 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1805 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1806 fn test_process_vote_empty_slots(mut vote_state: VoteStateHandler) {
1807 let vote = Vote::new(vec![], Hash::default());
1808 assert_eq!(
1809 process_vote(&mut vote_state, &vote, &[], 0, 0),
1810 Err(VoteError::EmptySlots)
1811 );
1812 }
1813
1814 pub fn process_new_vote_state_from_lockouts(
1815 vote_state: &mut VoteStateHandler,
1816 new_state: VecDeque<Lockout>,
1817 new_root: Option<Slot>,
1818 timestamp: Option<i64>,
1819 epoch: Epoch,
1820 ) -> Result<(), VoteError> {
1821 process_new_vote_state(
1822 vote_state,
1823 new_state.into_iter().map(LandedVote::from).collect(),
1824 new_root,
1825 timestamp,
1826 epoch,
1827 0,
1828 )
1829 }
1830
1831 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
1833 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
1834 fn test_vote_state_update_increment_credits(mut vote_state: VoteStateHandler) {
1835 let test_vote_groups: Vec<Vec<Slot>> = vec![
1838 vec![1, 2, 3, 4, 5, 6, 7, 8],
1840 vec![
1841 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1842 30, 31,
1843 ],
1844 vec![32],
1846 vec![33],
1848 vec![34, 35],
1850 vec![36, 37, 38],
1852 vec![
1854 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
1855 60, 61, 62, 63, 64, 65, 66, 67, 68,
1856 ],
1857 vec![
1859 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
1860 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1861 ],
1862 vec![100, 101, 106, 107, 112, 116, 120, 121, 122, 124],
1864 vec![200, 201],
1866 vec![
1867 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
1868 218, 219, 220, 221, 222, 223, 224, 225, 226,
1869 ],
1870 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
1871 ];
1872
1873 for vote_group in test_vote_groups {
1874 let mut vote_state_after_vote = vote_state.clone();
1876
1877 process_vote_unchecked(
1878 &mut vote_state_after_vote,
1879 Vote {
1880 slots: vote_group.clone(),
1881 hash: Hash::new_unique(),
1882 timestamp: None,
1883 },
1884 )
1885 .unwrap();
1886
1887 assert_eq!(
1889 process_new_vote_state(
1890 &mut vote_state,
1891 vote_state_after_vote.votes().clone(),
1892 vote_state_after_vote.root_slot(),
1893 None,
1894 0,
1895 0,
1896 ),
1897 Ok(())
1898 );
1899
1900 assert_eq!(
1902 vote_state.epoch_credits(),
1903 vote_state_after_vote.epoch_credits()
1904 );
1905 }
1906 }
1907
1908 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
1910 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
1911 fn test_timely_credits(target_version: VoteStateTargetVersion) {
1912 let test_vote_groups: Vec<(Vec<Slot>, Slot, u32)> = vec![
1916 (
1918 vec![1, 2, 3, 4, 5, 6, 7, 8],
1919 9,
1920 0,
1922 ),
1923 (
1924 vec![
1925 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
1926 29, 30, 31,
1927 ],
1928 34,
1929 0,
1932 ),
1933 (
1935 vec![32],
1936 35,
1937 10,
1940 ),
1941 (
1943 vec![33],
1944 36,
1945 10 + 11, ),
1949 (
1951 vec![34, 35],
1952 37,
1953 21 + 12 + 13, ),
1957 (
1959 vec![36, 37, 38],
1960 39,
1961 46 + 14 + 15 + 16, ),
1965 (
1966 vec![
1968 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
1969 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
1970 ],
1971 69,
1972 91 + 16
1982 + 9 + 2
1984 + 3
1985 + 4
1986 + 5
1987 + 6
1988 + 7
1989 + 8
1990 + 9
1991 + 10
1992 + 11
1993 + 12
1994 + 13
1995 + 14
1996 + 15
1997 + 15
1998 + 15
1999 + 15
2000 + 16
2001 + 15
2002 + 16, ),
2004 (
2006 vec![
2007 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
2008 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
2009 ],
2010 100,
2011 327 + 16
2018 + 14 + 2
2020 + 3
2021 + 4
2022 + 5
2023 + 6
2024 + 7
2025 + 8
2026 + 9
2027 + 10
2028 + 11
2029 + 12
2030 + 13
2031 + 14
2032 + 15
2033 + 16
2034 + 16, ),
2036 (
2038 vec![115, 116, 117, 118, 119, 120, 121, 122, 123, 124],
2039 130,
2040 508 + ((74 - 69) + 1), ),
2045 (
2047 vec![200, 201],
2048 202,
2049 514,
2052 ),
2053 (
2054 vec![
2055 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
2056 218, 219, 220, 221, 222, 223, 224, 225, 226,
2057 ],
2058 227,
2059 514 + 9 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13, ),
2065 (
2066 vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
2067 237,
2068 613 + 3 + 4 + 5 + 6 + 16 + 16 + 1 + 1 + 1 + 1, ),
2074 ];
2075
2076 let new_vote_state = || match target_version {
2077 VoteStateTargetVersion::V3 => VoteStateHandler::default_v3(),
2078 VoteStateTargetVersion::V4 => VoteStateHandler::default_v4(),
2079 };
2080
2081 for i in 0..test_vote_groups.len() {
2084 let mut vote_state_1 = new_vote_state();
2086 let mut vote_state_2 = new_vote_state();
2088 test_vote_groups.iter().take(i + 1).for_each(|vote_group| {
2089 let vote = Vote {
2090 slots: vote_group.0.clone(), hash: Hash::new_unique(),
2092 timestamp: None,
2093 };
2094 let slot_hashes: Vec<_> =
2095 vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
2096 assert_eq!(
2097 process_vote(
2098 &mut vote_state_1,
2099 &vote,
2100 &slot_hashes,
2101 0,
2102 vote_group.1, ),
2104 Ok(())
2105 );
2106
2107 assert_eq!(
2108 process_new_vote_state(
2109 &mut vote_state_2,
2110 vote_state_1.votes().clone(),
2111 vote_state_1.root_slot(),
2112 None,
2113 0,
2114 vote_group.1, ),
2116 Ok(())
2117 );
2118 });
2119
2120 let vote_group = &test_vote_groups[i];
2122 assert_eq!(vote_state_1.credits(), vote_group.2 as u64); assert_eq!(vote_state_2.credits(), vote_group.2 as u64); }
2125 }
2126
2127 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2128 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2129 fn test_retroactive_voting_timely_credits(mut vote_state: VoteStateHandler) {
2130 #[allow(clippy::type_complexity)]
2136 let test_vote_state_updates: Vec<(Vec<(Slot, u32)>, Slot, Option<Slot>, u32)> = vec![
2137 (
2139 vec![(7, 4), (8, 3), (9, 2), (10, 1)],
2140 11,
2141 None,
2143 0,
2145 ),
2146 (
2148 vec![
2149 (1, 10),
2150 (2, 9),
2151 (3, 8),
2152 (4, 7),
2153 (5, 6),
2154 (6, 5),
2155 (7, 4),
2156 (8, 3),
2157 (9, 2),
2158 (10, 1),
2159 ],
2160 12,
2161 None,
2163 0,
2165 ),
2166 (
2168 vec![
2169 (11, 31),
2170 (12, 30),
2171 (13, 29),
2172 (14, 28),
2173 (15, 27),
2174 (16, 26),
2175 (17, 25),
2176 (18, 24),
2177 (19, 23),
2178 (20, 22),
2179 (21, 21),
2180 (22, 20),
2181 (23, 19),
2182 (24, 18),
2183 (25, 17),
2184 (26, 16),
2185 (27, 15),
2186 (28, 14),
2187 (29, 13),
2188 (30, 12),
2189 (31, 11),
2190 (32, 10),
2191 (33, 9),
2192 (34, 8),
2193 (35, 7),
2194 (36, 6),
2195 (37, 5),
2196 (38, 4),
2197 (39, 3),
2198 (40, 2),
2199 (41, 1),
2200 ],
2201 42,
2202 Some(10),
2204 7 + 8 + 9 + 10 + 11 + 12 + 14 + 15 + 16 + 16,
2207 ),
2208 ];
2209
2210 test_vote_state_updates
2213 .iter()
2214 .for_each(|proposed_vote_state| {
2215 let new_state = proposed_vote_state
2216 .0 .iter()
2218 .map(|(slot, confirmation_count)| LandedVote {
2219 latency: 0,
2220 lockout: Lockout::new_with_confirmation_count(*slot, *confirmation_count),
2221 })
2222 .collect::<VecDeque<LandedVote>>();
2223 assert_eq!(
2224 process_new_vote_state(
2225 &mut vote_state,
2226 new_state,
2227 proposed_vote_state.2, None,
2229 0,
2230 proposed_vote_state.1, ),
2232 Ok(())
2233 );
2234
2235 assert_eq!(vote_state.credits(), proposed_vote_state.3 as u64);
2237 });
2238 }
2239
2240 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2241 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2242 fn test_process_new_vote_too_many_votes(mut vote_state1: VoteStateHandler) {
2243 let bad_votes: VecDeque<Lockout> = (0..=MAX_LOCKOUT_HISTORY)
2244 .map(|slot| {
2245 Lockout::new_with_confirmation_count(
2246 slot as Slot,
2247 (MAX_LOCKOUT_HISTORY - slot + 1) as u32,
2248 )
2249 })
2250 .collect();
2251
2252 let current_epoch = vote_state1.current_epoch();
2253 assert_eq!(
2254 process_new_vote_state_from_lockouts(
2255 &mut vote_state1,
2256 bad_votes,
2257 None,
2258 None,
2259 current_epoch,
2260 ),
2261 Err(VoteError::TooManyVotes)
2262 );
2263 }
2264
2265 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2266 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2267 fn test_process_new_vote_state_root_rollback(mut vote_state1: VoteStateHandler) {
2268 for i in 0..MAX_LOCKOUT_HISTORY + 2 {
2269 process_slot_vote_unchecked(&mut vote_state1, i as Slot);
2270 }
2271 assert_eq!(vote_state1.root_slot().unwrap(), 1);
2272
2273 let mut vote_state2 = vote_state1.clone();
2276 process_slot_vote_unchecked(&mut vote_state2, MAX_LOCKOUT_HISTORY as Slot + 3);
2277
2278 let lesser_root = Some(0);
2280
2281 let current_epoch = vote_state2.current_epoch();
2282 assert_eq!(
2283 process_new_vote_state(
2284 &mut vote_state1,
2285 vote_state2.votes().clone(),
2286 lesser_root,
2287 None,
2288 current_epoch,
2289 0,
2290 ),
2291 Err(VoteError::RootRollBack)
2292 );
2293
2294 let none_root = None;
2296 assert_eq!(
2297 process_new_vote_state(
2298 &mut vote_state1,
2299 vote_state2.votes().clone(),
2300 none_root,
2301 None,
2302 current_epoch,
2303 0,
2304 ),
2305 Err(VoteError::RootRollBack)
2306 );
2307 }
2308
2309 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2310 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2311 fn test_process_new_vote_state_zero_confirmations(mut vote_state1: VoteStateHandler) {
2312 let current_epoch = vote_state1.current_epoch();
2313
2314 let bad_votes: VecDeque<Lockout> = vec![
2315 Lockout::new_with_confirmation_count(0, 0),
2316 Lockout::new_with_confirmation_count(1, 1),
2317 ]
2318 .into_iter()
2319 .collect();
2320 assert_eq!(
2321 process_new_vote_state_from_lockouts(
2322 &mut vote_state1,
2323 bad_votes,
2324 None,
2325 None,
2326 current_epoch,
2327 ),
2328 Err(VoteError::ZeroConfirmations)
2329 );
2330
2331 let bad_votes: VecDeque<Lockout> = vec![
2332 Lockout::new_with_confirmation_count(0, 2),
2333 Lockout::new_with_confirmation_count(1, 0),
2334 ]
2335 .into_iter()
2336 .collect();
2337 assert_eq!(
2338 process_new_vote_state_from_lockouts(
2339 &mut vote_state1,
2340 bad_votes,
2341 None,
2342 None,
2343 current_epoch,
2344 ),
2345 Err(VoteError::ZeroConfirmations)
2346 );
2347 }
2348
2349 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2350 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2351 fn test_process_new_vote_state_confirmations_too_large(initial_vote_state: VoteStateHandler) {
2352 let mut vote_state1 = initial_vote_state.clone();
2353 let current_epoch = vote_state1.current_epoch();
2354
2355 let good_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2356 0,
2357 MAX_LOCKOUT_HISTORY as u32,
2358 )]
2359 .into_iter()
2360 .collect();
2361
2362 process_new_vote_state_from_lockouts(
2363 &mut vote_state1,
2364 good_votes,
2365 None,
2366 None,
2367 current_epoch,
2368 )
2369 .unwrap();
2370
2371 let mut vote_state1 = initial_vote_state;
2372 let bad_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2373 0,
2374 MAX_LOCKOUT_HISTORY as u32 + 1,
2375 )]
2376 .into_iter()
2377 .collect();
2378 assert_eq!(
2379 process_new_vote_state_from_lockouts(
2380 &mut vote_state1,
2381 bad_votes,
2382 None,
2383 None,
2384 current_epoch,
2385 ),
2386 Err(VoteError::ConfirmationTooLarge)
2387 );
2388 }
2389
2390 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2391 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2392 fn test_process_new_vote_state_slot_smaller_than_root(mut vote_state1: VoteStateHandler) {
2393 let current_epoch = vote_state1.current_epoch();
2394 let root_slot = 5;
2395
2396 let bad_votes: VecDeque<Lockout> = vec![
2397 Lockout::new_with_confirmation_count(root_slot, 2),
2398 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2399 ]
2400 .into_iter()
2401 .collect();
2402 assert_eq!(
2403 process_new_vote_state_from_lockouts(
2404 &mut vote_state1,
2405 bad_votes,
2406 Some(root_slot),
2407 None,
2408 current_epoch,
2409 ),
2410 Err(VoteError::SlotSmallerThanRoot)
2411 );
2412
2413 let bad_votes: VecDeque<Lockout> = vec![
2414 Lockout::new_with_confirmation_count(root_slot - 1, 2),
2415 Lockout::new_with_confirmation_count(root_slot + 1, 1),
2416 ]
2417 .into_iter()
2418 .collect();
2419 assert_eq!(
2420 process_new_vote_state_from_lockouts(
2421 &mut vote_state1,
2422 bad_votes,
2423 Some(root_slot),
2424 None,
2425 current_epoch,
2426 ),
2427 Err(VoteError::SlotSmallerThanRoot)
2428 );
2429 }
2430
2431 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2432 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2433 fn test_process_new_vote_state_slots_not_ordered(mut vote_state1: VoteStateHandler) {
2434 let current_epoch = vote_state1.current_epoch();
2435
2436 let bad_votes: VecDeque<Lockout> = vec![
2437 Lockout::new_with_confirmation_count(1, 2),
2438 Lockout::new_with_confirmation_count(0, 1),
2439 ]
2440 .into_iter()
2441 .collect();
2442 assert_eq!(
2443 process_new_vote_state_from_lockouts(
2444 &mut vote_state1,
2445 bad_votes,
2446 None,
2447 None,
2448 current_epoch,
2449 ),
2450 Err(VoteError::SlotsNotOrdered)
2451 );
2452
2453 let bad_votes: VecDeque<Lockout> = vec![
2454 Lockout::new_with_confirmation_count(1, 2),
2455 Lockout::new_with_confirmation_count(1, 1),
2456 ]
2457 .into_iter()
2458 .collect();
2459 assert_eq!(
2460 process_new_vote_state_from_lockouts(
2461 &mut vote_state1,
2462 bad_votes,
2463 None,
2464 None,
2465 current_epoch,
2466 ),
2467 Err(VoteError::SlotsNotOrdered)
2468 );
2469 }
2470
2471 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2472 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2473 fn test_process_new_vote_state_confirmations_not_ordered(mut vote_state1: VoteStateHandler) {
2474 let current_epoch = vote_state1.current_epoch();
2475
2476 let bad_votes: VecDeque<Lockout> = vec![
2477 Lockout::new_with_confirmation_count(0, 1),
2478 Lockout::new_with_confirmation_count(1, 2),
2479 ]
2480 .into_iter()
2481 .collect();
2482 assert_eq!(
2483 process_new_vote_state_from_lockouts(
2484 &mut vote_state1,
2485 bad_votes,
2486 None,
2487 None,
2488 current_epoch,
2489 ),
2490 Err(VoteError::ConfirmationsNotOrdered)
2491 );
2492
2493 let bad_votes: VecDeque<Lockout> = vec![
2494 Lockout::new_with_confirmation_count(0, 1),
2495 Lockout::new_with_confirmation_count(1, 1),
2496 ]
2497 .into_iter()
2498 .collect();
2499 assert_eq!(
2500 process_new_vote_state_from_lockouts(
2501 &mut vote_state1,
2502 bad_votes,
2503 None,
2504 None,
2505 current_epoch,
2506 ),
2507 Err(VoteError::ConfirmationsNotOrdered)
2508 );
2509 }
2510
2511 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2512 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2513 fn test_process_new_vote_state_new_vote_state_lockout_mismatch(
2514 mut vote_state1: VoteStateHandler,
2515 ) {
2516 let current_epoch = vote_state1.current_epoch();
2517
2518 let bad_votes: VecDeque<Lockout> = vec![
2519 Lockout::new_with_confirmation_count(0, 2),
2520 Lockout::new_with_confirmation_count(7, 1),
2521 ]
2522 .into_iter()
2523 .collect();
2524
2525 assert_eq!(
2527 process_new_vote_state_from_lockouts(
2528 &mut vote_state1,
2529 bad_votes,
2530 None,
2531 None,
2532 current_epoch,
2533 ),
2534 Err(VoteError::NewVoteStateLockoutMismatch)
2535 );
2536 }
2537
2538 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2539 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2540 fn test_process_new_vote_state_confirmation_rollback(mut vote_state1: VoteStateHandler) {
2541 let current_epoch = vote_state1.current_epoch();
2542 let votes: VecDeque<Lockout> = vec![
2543 Lockout::new_with_confirmation_count(0, 4),
2544 Lockout::new_with_confirmation_count(1, 3),
2545 ]
2546 .into_iter()
2547 .collect();
2548 process_new_vote_state_from_lockouts(&mut vote_state1, votes, None, None, current_epoch)
2549 .unwrap();
2550
2551 let votes: VecDeque<Lockout> = vec![
2552 Lockout::new_with_confirmation_count(0, 4),
2553 Lockout::new_with_confirmation_count(1, 2),
2555 Lockout::new_with_confirmation_count(2, 1),
2556 ]
2557 .into_iter()
2558 .collect();
2559 assert_eq!(
2562 process_new_vote_state_from_lockouts(
2563 &mut vote_state1,
2564 votes,
2565 None,
2566 None,
2567 current_epoch,
2568 ),
2569 Err(VoteError::ConfirmationRollBack)
2570 );
2571 }
2572
2573 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2574 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2575 fn test_process_new_vote_state_root_progress(mut vote_state1: VoteStateHandler) {
2576 for i in 0..MAX_LOCKOUT_HISTORY {
2577 process_slot_vote_unchecked(&mut vote_state1, i as u64);
2578 }
2579
2580 assert!(vote_state1.root_slot().is_none());
2581 let mut vote_state2 = vote_state1.clone();
2582
2583 for new_vote in MAX_LOCKOUT_HISTORY + 1..=MAX_LOCKOUT_HISTORY + 2 {
2590 process_slot_vote_unchecked(&mut vote_state2, new_vote as Slot);
2591 assert_ne!(vote_state1.root_slot(), vote_state2.root_slot());
2592
2593 process_new_vote_state(
2594 &mut vote_state1,
2595 vote_state2.votes().clone(),
2596 vote_state2.root_slot(),
2597 None,
2598 vote_state2.current_epoch(),
2599 0,
2600 )
2601 .unwrap();
2602
2603 assert_eq!(vote_state1, vote_state2);
2604 }
2605 }
2606
2607 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2608 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2609 fn test_process_new_vote_state_same_slot_but_not_common_ancestor(
2610 initial_vote_state: VoteStateHandler,
2611 ) {
2612 let mut vote_state1 = initial_vote_state.clone();
2631 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5]);
2632 assert_eq!(
2633 vote_state1
2634 .votes()
2635 .iter()
2636 .map(|vote| vote.slot())
2637 .collect::<Vec<Slot>>(),
2638 vec![1, 5]
2639 );
2640
2641 let mut vote_state2 = initial_vote_state;
2643 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2644 assert_eq!(
2645 vote_state2
2646 .votes()
2647 .iter()
2648 .map(|vote| vote.slot())
2649 .collect::<Vec<Slot>>(),
2650 vec![1, 2, 3, 5, 7]
2651 );
2652
2653 process_new_vote_state(
2655 &mut vote_state1,
2656 vote_state2.votes().clone(),
2657 vote_state2.root_slot(),
2658 None,
2659 vote_state2.current_epoch(),
2660 0,
2661 )
2662 .unwrap();
2663
2664 assert_eq!(vote_state1, vote_state2);
2665 }
2666
2667 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2668 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2669 fn test_process_new_vote_state_lockout_violation(initial_vote_state: VoteStateHandler) {
2670 let mut vote_state1 = initial_vote_state.clone();
2672 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 4, 5]);
2673 assert_eq!(
2674 vote_state1
2675 .votes()
2676 .iter()
2677 .map(|vote| vote.slot())
2678 .collect::<Vec<Slot>>(),
2679 vec![1, 2, 4, 5]
2680 );
2681
2682 let mut vote_state2 = initial_vote_state;
2685 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2686 assert_eq!(
2687 vote_state2
2688 .votes()
2689 .iter()
2690 .map(|vote| vote.slot())
2691 .collect::<Vec<Slot>>(),
2692 vec![1, 2, 3, 5, 7]
2693 );
2694
2695 assert_eq!(
2697 process_new_vote_state(
2698 &mut vote_state1,
2699 vote_state2.votes().clone(),
2700 vote_state2.root_slot(),
2701 None,
2702 vote_state2.current_epoch(),
2703 0,
2704 ),
2705 Err(VoteError::LockoutConflict)
2706 );
2707 }
2708
2709 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2710 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2711 fn test_process_new_vote_state_lockout_violation2(initial_vote_state: VoteStateHandler) {
2712 let mut vote_state1 = initial_vote_state.clone();
2714 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5, 6, 7]);
2715 assert_eq!(
2716 vote_state1
2717 .votes()
2718 .iter()
2719 .map(|vote| vote.slot())
2720 .collect::<Vec<Slot>>(),
2721 vec![1, 5, 6, 7]
2722 );
2723
2724 let mut vote_state2 = initial_vote_state;
2727 process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 6, 8]);
2728 assert_eq!(
2729 vote_state2
2730 .votes()
2731 .iter()
2732 .map(|vote| vote.slot())
2733 .collect::<Vec<Slot>>(),
2734 vec![1, 2, 3, 5, 6, 8]
2735 );
2736
2737 assert_eq!(
2740 process_new_vote_state(
2741 &mut vote_state1,
2742 vote_state2.votes().clone(),
2743 vote_state2.root_slot(),
2744 None,
2745 vote_state2.current_epoch(),
2746 0,
2747 ),
2748 Err(VoteError::LockoutConflict)
2749 );
2750 }
2751
2752 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2753 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2754 fn test_process_new_vote_state_expired_ancestor_not_removed(mut vote_state1: VoteStateHandler) {
2755 process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 3, 9]);
2757 assert_eq!(
2758 vote_state1
2759 .votes()
2760 .iter()
2761 .map(|vote| vote.slot())
2762 .collect::<Vec<Slot>>(),
2763 vec![1, 9]
2764 );
2765
2766 let mut vote_state2 = vote_state1.clone();
2769 process_slot_vote_unchecked(&mut vote_state2, 10);
2770
2771 assert_eq!(vote_state2.votes()[0].slot(), 1);
2774 assert_eq!(vote_state2.votes()[0].lockout.last_locked_out_slot(), 9);
2775 assert_eq!(
2776 vote_state2
2777 .votes()
2778 .iter()
2779 .map(|vote| vote.slot())
2780 .collect::<Vec<Slot>>(),
2781 vec![1, 9, 10]
2782 );
2783
2784 process_new_vote_state(
2786 &mut vote_state1,
2787 vote_state2.votes().clone(),
2788 vote_state2.root_slot(),
2789 None,
2790 vote_state2.current_epoch(),
2791 0,
2792 )
2793 .unwrap();
2794 assert_eq!(vote_state1, vote_state2,);
2795 }
2796
2797 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2798 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2799 fn test_process_new_vote_current_state_contains_bigger_slots(
2800 mut vote_state1: VoteStateHandler,
2801 ) {
2802 process_slot_votes_unchecked(&mut vote_state1, &[6, 7, 8]);
2803 assert_eq!(
2804 vote_state1
2805 .votes()
2806 .iter()
2807 .map(|vote| vote.slot())
2808 .collect::<Vec<Slot>>(),
2809 vec![6, 7, 8]
2810 );
2811
2812 let bad_votes: VecDeque<Lockout> = vec![
2814 Lockout::new_with_confirmation_count(2, 5),
2815 Lockout::new_with_confirmation_count(14, 1),
2817 ]
2818 .into_iter()
2819 .collect();
2820 let root = Some(1);
2821
2822 let current_epoch = vote_state1.current_epoch();
2823 assert_eq!(
2824 process_new_vote_state_from_lockouts(
2825 &mut vote_state1,
2826 bad_votes,
2827 root,
2828 None,
2829 current_epoch,
2830 ),
2831 Err(VoteError::LockoutConflict)
2832 );
2833
2834 let good_votes: VecDeque<LandedVote> = vec![
2835 Lockout::new_with_confirmation_count(2, 5).into(),
2836 Lockout::new_with_confirmation_count(15, 1).into(),
2837 ]
2838 .into_iter()
2839 .collect();
2840
2841 let current_epoch = vote_state1.current_epoch();
2842 process_new_vote_state(
2843 &mut vote_state1,
2844 good_votes.clone(),
2845 root,
2846 None,
2847 current_epoch,
2848 0,
2849 )
2850 .unwrap();
2851 assert_eq!(*vote_state1.votes(), good_votes);
2852 }
2853
2854 #[test_case(VoteStateHandler::default_v3() ; "VoteStateV3")]
2855 #[test_case(VoteStateHandler::default_v4() ; "VoteStateV4")]
2856 fn test_filter_old_votes(mut vote_state: VoteStateHandler) {
2857 let old_vote_slot = 1;
2858 let vote = Vote::new(vec![old_vote_slot], Hash::default());
2859
2860 let slot_hashes = vec![(3, Hash::new_unique()), (2, Hash::new_unique())];
2863 assert_eq!(
2864 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
2865 Err(VoteError::VotesTooOldAllFiltered)
2866 );
2867
2868 let vote_slot = 2;
2871 let vote_slot_hash = slot_hashes
2872 .iter()
2873 .find(|(slot, _hash)| *slot == vote_slot)
2874 .unwrap()
2875 .1;
2876
2877 let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
2878 process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0).unwrap();
2879 assert_eq!(
2880 vote_state
2881 .votes()
2882 .iter()
2883 .map(|vote| vote.lockout)
2884 .collect::<Vec<Lockout>>(),
2885 vec![Lockout::new_with_confirmation_count(vote_slot, 1)]
2886 );
2887 }
2888
2889 fn build_slot_hashes(slots: Vec<Slot>) -> Vec<(Slot, Hash)> {
2890 slots
2891 .iter()
2892 .rev()
2893 .map(|x| (*x, Hash::new_unique()))
2894 .collect()
2895 }
2896
2897 fn build_vote_state(
2898 target_version: VoteStateTargetVersion,
2899 vote_slots: Vec<Slot>,
2900 slot_hashes: &[(Slot, Hash)],
2901 ) -> VoteStateHandler {
2902 let mut vote_state = match target_version {
2903 VoteStateTargetVersion::V3 => VoteStateHandler::default_v3(),
2904 VoteStateTargetVersion::V4 => VoteStateHandler::default_v4(),
2905 };
2906
2907 if !vote_slots.is_empty() {
2908 let vote_hash = slot_hashes
2909 .iter()
2910 .find(|(slot, _hash)| slot == vote_slots.last().unwrap())
2911 .unwrap()
2912 .1;
2913 let vote = Vote::new(vote_slots, vote_hash);
2914 process_vote_unfiltered(&mut vote_state, &vote.slots, &vote, slot_hashes, 0, 0)
2915 .unwrap();
2916 }
2917
2918 vote_state
2919 }
2920
2921 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
2922 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
2923 fn test_check_and_filter_proposed_vote_state_empty(target_version: VoteStateTargetVersion) {
2924 let empty_slot_hashes = build_slot_hashes(vec![]);
2925 let empty_vote_state = build_vote_state(target_version, vec![], &empty_slot_hashes);
2926
2927 let mut tower_sync = TowerSync::from(vec![]);
2929 assert_eq!(
2930 check_and_filter_proposed_vote_state(
2931 &empty_vote_state,
2932 &mut tower_sync.lockouts,
2933 &mut tower_sync.root,
2934 tower_sync.hash,
2935 &empty_slot_hashes
2936 ),
2937 Err(VoteError::EmptySlots),
2938 );
2939
2940 let mut tower_sync = TowerSync::from(vec![(0, 1)]);
2942 assert_eq!(
2943 check_and_filter_proposed_vote_state(
2944 &empty_vote_state,
2945 &mut tower_sync.lockouts,
2946 &mut tower_sync.root,
2947 tower_sync.hash,
2948 &empty_slot_hashes
2949 ),
2950 Err(VoteError::SlotsMismatch),
2951 );
2952 }
2953
2954 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
2955 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
2956 fn test_check_and_filter_proposed_vote_state_too_old(target_version: VoteStateTargetVersion) {
2957 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
2958 let latest_vote = 4;
2959 let vote_state = build_vote_state(target_version, vec![1, 2, 3, latest_vote], &slot_hashes);
2960
2961 let mut tower_sync = TowerSync::from(vec![(latest_vote, 1)]);
2964 assert_eq!(
2965 check_and_filter_proposed_vote_state(
2966 &vote_state,
2967 &mut tower_sync.lockouts,
2968 &mut tower_sync.root,
2969 tower_sync.hash,
2970 &slot_hashes
2971 ),
2972 Err(VoteError::VoteTooOld),
2973 );
2974
2975 let earliest_slot_in_history = latest_vote + 2;
2979 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history]);
2980 let mut tower_sync = TowerSync::from(vec![(earliest_slot_in_history - 1, 1)]);
2981 assert_eq!(
2982 check_and_filter_proposed_vote_state(
2983 &vote_state,
2984 &mut tower_sync.lockouts,
2985 &mut tower_sync.root,
2986 tower_sync.hash,
2987 &slot_hashes
2988 ),
2989 Err(VoteError::VoteTooOld),
2990 );
2991 }
2992
2993 fn run_test_check_and_filter_proposed_vote_state_older_than_history_root(
2994 target_version: VoteStateTargetVersion,
2995 earliest_slot_in_history: Slot,
2996 current_vote_state_slots: Vec<Slot>,
2997 current_vote_state_root: Option<Slot>,
2998 proposed_slots_and_lockouts: Vec<(Slot, u32)>,
2999 proposed_root: Slot,
3000 expected_root: Option<Slot>,
3001 expected_vote_state: Vec<Lockout>,
3002 ) {
3003 assert!(proposed_root < earliest_slot_in_history);
3004 assert_eq!(
3005 expected_root,
3006 current_vote_state_slots
3007 .iter()
3008 .rev()
3009 .find(|slot| **slot <= proposed_root)
3010 .cloned()
3011 );
3012 let latest_slot_in_history = proposed_slots_and_lockouts
3013 .last()
3014 .unwrap()
3015 .0
3016 .max(earliest_slot_in_history);
3017 let mut slot_hashes = build_slot_hashes(
3018 (current_vote_state_slots.first().copied().unwrap_or(0)..=latest_slot_in_history)
3019 .collect::<Vec<Slot>>(),
3020 );
3021
3022 let mut vote_state =
3023 build_vote_state(target_version, current_vote_state_slots, &slot_hashes);
3024 vote_state.set_root_slot(current_vote_state_root);
3025
3026 slot_hashes.retain(|slot| slot.0 >= earliest_slot_in_history);
3027 assert!(!proposed_slots_and_lockouts.is_empty());
3028 let proposed_hash = slot_hashes
3029 .iter()
3030 .find(|(slot, _hash)| *slot == proposed_slots_and_lockouts.last().unwrap().0)
3031 .unwrap()
3032 .1;
3033
3034 let mut tower_sync = TowerSync::from(proposed_slots_and_lockouts);
3038 tower_sync.hash = proposed_hash;
3039 tower_sync.root = Some(proposed_root);
3040 check_and_filter_proposed_vote_state(
3041 &vote_state,
3042 &mut tower_sync.lockouts,
3043 &mut tower_sync.root,
3044 tower_sync.hash,
3045 &slot_hashes,
3046 )
3047 .unwrap();
3048 assert_eq!(tower_sync.root, expected_root);
3049
3050 assert!(
3053 do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync.clone(),).is_ok()
3054 );
3055 assert_eq!(vote_state.root_slot(), expected_root);
3056 assert_eq!(
3057 vote_state
3058 .votes()
3059 .iter()
3060 .map(|vote| vote.lockout)
3061 .collect::<Vec<Lockout>>(),
3062 expected_vote_state,
3063 );
3064 }
3065
3066 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3067 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3068 fn test_check_and_filter_proposed_vote_state_older_than_history_root(
3069 target_version: VoteStateTargetVersion,
3070 ) {
3071 let earliest_slot_in_history = 5;
3074 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3075 let current_vote_state_root = None;
3076 let proposed_slots_and_lockouts = vec![(5, 1)];
3077 let proposed_root = 4;
3078 let expected_root = Some(4);
3079 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3080 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3081 target_version,
3082 earliest_slot_in_history,
3083 current_vote_state_slots,
3084 current_vote_state_root,
3085 proposed_slots_and_lockouts,
3086 proposed_root,
3087 expected_root,
3088 expected_vote_state,
3089 );
3090
3091 let earliest_slot_in_history = 5;
3094 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3095 let current_vote_state_root = Some(0);
3096 let proposed_slots_and_lockouts = vec![(5, 1)];
3097 let proposed_root = 4;
3098 let expected_root = Some(4);
3099 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3100 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3101 target_version,
3102 earliest_slot_in_history,
3103 current_vote_state_slots,
3104 current_vote_state_root,
3105 proposed_slots_and_lockouts,
3106 proposed_root,
3107 expected_root,
3108 expected_vote_state,
3109 );
3110
3111 let earliest_slot_in_history = 5;
3114 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3115 let current_vote_state_root = Some(0);
3116 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3117 let proposed_root = 3;
3118 let expected_root = Some(3);
3119 let expected_vote_state = vec![
3120 Lockout::new_with_confirmation_count(4, 2),
3121 Lockout::new_with_confirmation_count(5, 1),
3122 ];
3123 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3124 target_version,
3125 earliest_slot_in_history,
3126 current_vote_state_slots,
3127 current_vote_state_root,
3128 proposed_slots_and_lockouts,
3129 proposed_root,
3130 expected_root,
3131 expected_vote_state,
3132 );
3133
3134 let earliest_slot_in_history = 5;
3136 let current_vote_state_slots: Vec<Slot> = vec![1, 2, 4];
3137 let current_vote_state_root = Some(0);
3138 let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3139 let proposed_root = 3;
3140 let expected_root = Some(2);
3141 let expected_vote_state = vec![
3142 Lockout::new_with_confirmation_count(4, 2),
3143 Lockout::new_with_confirmation_count(5, 1),
3144 ];
3145 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3146 target_version,
3147 earliest_slot_in_history,
3148 current_vote_state_slots,
3149 current_vote_state_root,
3150 proposed_slots_and_lockouts,
3151 proposed_root,
3152 expected_root,
3153 expected_vote_state,
3154 );
3155
3156 let earliest_slot_in_history = 4;
3159 let current_vote_state_slots: Vec<Slot> = vec![3, 4];
3160 let current_vote_state_root = None;
3161 let proposed_slots_and_lockouts = vec![(3, 3), (4, 2), (5, 1)];
3162 let proposed_root = 2;
3163 let expected_root = None;
3164 let expected_vote_state = vec![
3165 Lockout::new_with_confirmation_count(3, 3),
3166 Lockout::new_with_confirmation_count(4, 2),
3167 Lockout::new_with_confirmation_count(5, 1),
3168 ];
3169 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3170 target_version,
3171 earliest_slot_in_history,
3172 current_vote_state_slots,
3173 current_vote_state_root,
3174 proposed_slots_and_lockouts,
3175 proposed_root,
3176 expected_root,
3177 expected_vote_state,
3178 );
3179
3180 let earliest_slot_in_history = 4;
3182 let current_vote_state_slots: Vec<Slot> = vec![];
3183 let current_vote_state_root = None;
3184 let proposed_slots_and_lockouts = vec![(5, 1)];
3185 let proposed_root = 2;
3186 let expected_root = None;
3187 let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3188 run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3189 target_version,
3190 earliest_slot_in_history,
3191 current_vote_state_slots,
3192 current_vote_state_root,
3193 proposed_slots_and_lockouts,
3194 proposed_root,
3195 expected_root,
3196 expected_vote_state,
3197 );
3198 }
3199
3200 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3201 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3202 fn test_check_and_filter_proposed_vote_state_slots_not_ordered(
3203 target_version: VoteStateTargetVersion,
3204 ) {
3205 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3206 let vote_state = build_vote_state(target_version, vec![1], &slot_hashes);
3207
3208 let vote_slot = 3;
3210 let vote_slot_hash = slot_hashes
3211 .iter()
3212 .find(|(slot, _hash)| *slot == vote_slot)
3213 .unwrap()
3214 .1;
3215 let mut tower_sync = TowerSync::from(vec![(2, 2), (1, 3), (vote_slot, 1)]);
3216 tower_sync.hash = vote_slot_hash;
3217 assert_eq!(
3218 check_and_filter_proposed_vote_state(
3219 &vote_state,
3220 &mut tower_sync.lockouts,
3221 &mut tower_sync.root,
3222 tower_sync.hash,
3223 &slot_hashes
3224 ),
3225 Err(VoteError::SlotsNotOrdered),
3226 );
3227
3228 let mut tower_sync = TowerSync::from(vec![(2, 2), (2, 2), (vote_slot, 1)]);
3230 tower_sync.hash = vote_slot_hash;
3231 assert_eq!(
3232 check_and_filter_proposed_vote_state(
3233 &vote_state,
3234 &mut tower_sync.lockouts,
3235 &mut tower_sync.root,
3236 tower_sync.hash,
3237 &slot_hashes
3238 ),
3239 Err(VoteError::SlotsNotOrdered),
3240 );
3241 }
3242
3243 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3244 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3245 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered(
3246 target_version: VoteStateTargetVersion,
3247 ) {
3248 let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3249 let mut vote_state = build_vote_state(target_version, vec![1, 2, 3, 4], &slot_hashes);
3250
3251 let earliest_slot_in_history = 11;
3256 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3257 let vote_slot = 12;
3258 let vote_slot_hash = slot_hashes
3259 .iter()
3260 .find(|(slot, _hash)| *slot == vote_slot)
3261 .unwrap()
3262 .1;
3263 let missing_older_than_history_slot = earliest_slot_in_history - 1;
3264 let mut tower_sync = TowerSync::from(vec![
3265 (1, 4),
3266 (missing_older_than_history_slot, 2),
3267 (vote_slot, 3),
3268 ]);
3269 tower_sync.hash = vote_slot_hash;
3270 check_and_filter_proposed_vote_state(
3271 &vote_state,
3272 &mut tower_sync.lockouts,
3273 &mut tower_sync.root,
3274 tower_sync.hash,
3275 &slot_hashes,
3276 )
3277 .unwrap();
3278
3279 assert_eq!(
3281 tower_sync
3282 .clone()
3283 .lockouts
3284 .into_iter()
3285 .collect::<Vec<Lockout>>(),
3286 vec![
3287 Lockout::new_with_confirmation_count(1, 4),
3288 Lockout::new_with_confirmation_count(vote_slot, 3)
3289 ]
3290 );
3291 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3292 }
3293
3294 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3295 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3296 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_not_filtered(
3297 target_version: VoteStateTargetVersion,
3298 ) {
3299 let slot_hashes = build_slot_hashes(vec![4]);
3300 let mut vote_state = build_vote_state(target_version, vec![4], &slot_hashes);
3301
3302 let earliest_slot_in_history = 11;
3307 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3308 let vote_slot = 12;
3309 let vote_slot_hash = slot_hashes
3310 .iter()
3311 .find(|(slot, _hash)| *slot == vote_slot)
3312 .unwrap()
3313 .1;
3314 let existing_older_than_history_slot = 4;
3315 let mut tower_sync =
3316 TowerSync::from(vec![(existing_older_than_history_slot, 3), (vote_slot, 2)]);
3317 tower_sync.hash = vote_slot_hash;
3318 check_and_filter_proposed_vote_state(
3319 &vote_state,
3320 &mut tower_sync.lockouts,
3321 &mut tower_sync.root,
3322 tower_sync.hash,
3323 &slot_hashes,
3324 )
3325 .unwrap();
3326 assert_eq!(tower_sync.lockouts.len(), 2);
3328 assert_eq!(
3329 tower_sync
3330 .clone()
3331 .lockouts
3332 .into_iter()
3333 .collect::<Vec<Lockout>>(),
3334 vec![
3335 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3336 Lockout::new_with_confirmation_count(vote_slot, 2)
3337 ]
3338 );
3339 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3340 }
3341
3342 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3343 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3344 fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered_and_not_filtered(
3345 target_version: VoteStateTargetVersion,
3346 ) {
3347 let slot_hashes = build_slot_hashes(vec![6]);
3348 let mut vote_state = build_vote_state(target_version, vec![6], &slot_hashes);
3349
3350 let earliest_slot_in_history = 11;
3361 let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3362 let vote_slot = 14;
3363 let vote_slot_hash = slot_hashes
3364 .iter()
3365 .find(|(slot, _hash)| *slot == vote_slot)
3366 .unwrap()
3367 .1;
3368
3369 let missing_older_than_history_slot = 4;
3370 let existing_older_than_history_slot = 6;
3371
3372 let mut tower_sync = TowerSync::from(vec![
3373 (missing_older_than_history_slot, 4),
3374 (existing_older_than_history_slot, 3),
3375 (12, 2),
3376 (vote_slot, 1),
3377 ]);
3378 tower_sync.hash = vote_slot_hash;
3379 check_and_filter_proposed_vote_state(
3380 &vote_state,
3381 &mut tower_sync.lockouts,
3382 &mut tower_sync.root,
3383 tower_sync.hash,
3384 &slot_hashes,
3385 )
3386 .unwrap();
3387 assert_eq!(tower_sync.lockouts.len(), 3);
3388 assert_eq!(
3389 tower_sync
3390 .clone()
3391 .lockouts
3392 .into_iter()
3393 .collect::<Vec<Lockout>>(),
3394 vec![
3395 Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3396 Lockout::new_with_confirmation_count(12, 2),
3397 Lockout::new_with_confirmation_count(vote_slot, 1)
3398 ]
3399 );
3400 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3401 }
3402
3403 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3404 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3405 fn test_check_and_filter_proposed_vote_state_slot_not_on_fork(
3406 target_version: VoteStateTargetVersion,
3407 ) {
3408 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3409 let vote_state = build_vote_state(target_version, vec![2, 4, 6], &slot_hashes);
3410
3411 let missing_vote_slot = 3;
3417
3418 let vote_slot = vote_state.votes().back().unwrap().slot() + 2;
3421 let vote_slot_hash = slot_hashes
3422 .iter()
3423 .find(|(slot, _hash)| *slot == vote_slot)
3424 .unwrap()
3425 .1;
3426 let mut tower_sync = TowerSync::from(vec![(missing_vote_slot, 2), (vote_slot, 3)]);
3427 tower_sync.hash = vote_slot_hash;
3428 assert_eq!(
3429 check_and_filter_proposed_vote_state(
3430 &vote_state,
3431 &mut tower_sync.lockouts,
3432 &mut tower_sync.root,
3433 tower_sync.hash,
3434 &slot_hashes
3435 ),
3436 Err(VoteError::SlotsMismatch),
3437 );
3438
3439 let missing_vote_slot = 7;
3441 let mut tower_sync = TowerSync::from(vec![
3442 (2, 5),
3443 (4, 4),
3444 (6, 3),
3445 (missing_vote_slot, 2),
3446 (vote_slot, 1),
3447 ]);
3448 tower_sync.hash = vote_slot_hash;
3449 assert_eq!(
3450 check_and_filter_proposed_vote_state(
3451 &vote_state,
3452 &mut tower_sync.lockouts,
3453 &mut tower_sync.root,
3454 tower_sync.hash,
3455 &slot_hashes
3456 ),
3457 Err(VoteError::SlotsMismatch),
3458 );
3459 }
3460
3461 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3462 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3463 fn test_check_and_filter_proposed_vote_state_root_on_different_fork(
3464 target_version: VoteStateTargetVersion,
3465 ) {
3466 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3467 let vote_state = build_vote_state(target_version, vec![6], &slot_hashes);
3468
3469 let new_root = 3;
3475
3476 let vote_slot = 8;
3479 assert_eq!(vote_slot, slot_hashes.first().unwrap().0);
3480 let vote_slot_hash = slot_hashes
3481 .iter()
3482 .find(|(slot, _hash)| *slot == vote_slot)
3483 .unwrap()
3484 .1;
3485 let mut tower_sync = TowerSync::from(vec![(vote_slot, 1)]);
3486 tower_sync.hash = vote_slot_hash;
3487 tower_sync.root = Some(new_root);
3488 assert_eq!(
3489 check_and_filter_proposed_vote_state(
3490 &vote_state,
3491 &mut tower_sync.lockouts,
3492 &mut tower_sync.root,
3493 tower_sync.hash,
3494 &slot_hashes
3495 ),
3496 Err(VoteError::RootOnDifferentFork),
3497 );
3498 }
3499
3500 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3501 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3502 fn test_check_and_filter_proposed_vote_state_slot_newer_than_slot_history(
3503 target_version: VoteStateTargetVersion,
3504 ) {
3505 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3506 let vote_state = build_vote_state(target_version, vec![2, 4, 6], &slot_hashes);
3507
3508 let missing_vote_slot = slot_hashes.first().unwrap().0 + 1;
3514 let vote_slot_hash = Hash::new_unique();
3515 let mut tower_sync = TowerSync::from(vec![(8, 2), (missing_vote_slot, 3)]);
3516 tower_sync.hash = vote_slot_hash;
3517 assert_eq!(
3518 check_and_filter_proposed_vote_state(
3519 &vote_state,
3520 &mut tower_sync.lockouts,
3521 &mut tower_sync.root,
3522 tower_sync.hash,
3523 &slot_hashes
3524 ),
3525 Err(VoteError::SlotsMismatch),
3526 );
3527 }
3528
3529 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3530 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3531 fn test_check_and_filter_proposed_vote_state_slot_all_slot_hashes_in_update_ok(
3532 target_version: VoteStateTargetVersion,
3533 ) {
3534 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3535 let mut vote_state = build_vote_state(target_version, vec![2, 4, 6], &slot_hashes);
3536
3537 let vote_slot = vote_state.votes().back().unwrap().slot() + 2;
3543 let vote_slot_hash = slot_hashes
3544 .iter()
3545 .find(|(slot, _hash)| *slot == vote_slot)
3546 .unwrap()
3547 .1;
3548 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3549 tower_sync.hash = vote_slot_hash;
3550 check_and_filter_proposed_vote_state(
3551 &vote_state,
3552 &mut tower_sync.lockouts,
3553 &mut tower_sync.root,
3554 tower_sync.hash,
3555 &slot_hashes,
3556 )
3557 .unwrap();
3558
3559 assert_eq!(
3561 tower_sync
3562 .clone()
3563 .lockouts
3564 .into_iter()
3565 .collect::<Vec<Lockout>>(),
3566 vec![
3567 Lockout::new_with_confirmation_count(2, 4),
3568 Lockout::new_with_confirmation_count(4, 3),
3569 Lockout::new_with_confirmation_count(6, 2),
3570 Lockout::new_with_confirmation_count(vote_slot, 1)
3571 ]
3572 );
3573
3574 assert!(do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,).is_ok());
3575 }
3576
3577 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3578 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3579 fn test_check_and_filter_proposed_vote_state_slot_some_slot_hashes_in_update_ok(
3580 target_version: VoteStateTargetVersion,
3581 ) {
3582 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3583 let mut vote_state = build_vote_state(target_version, vec![6], &slot_hashes);
3584
3585 let vote_slot = vote_state.votes().back().unwrap().slot() + 2;
3591 let vote_slot_hash = slot_hashes
3592 .iter()
3593 .find(|(slot, _hash)| *slot == vote_slot)
3594 .unwrap()
3595 .1;
3596 let mut tower_sync = TowerSync::from(vec![(4, 2), (vote_slot, 1)]);
3597 tower_sync.hash = vote_slot_hash;
3598 check_and_filter_proposed_vote_state(
3599 &vote_state,
3600 &mut tower_sync.lockouts,
3601 &mut tower_sync.root,
3602 tower_sync.hash,
3603 &slot_hashes,
3604 )
3605 .unwrap();
3606
3607 assert_eq!(
3609 tower_sync
3610 .clone()
3611 .lockouts
3612 .into_iter()
3613 .collect::<Vec<Lockout>>(),
3614 vec![
3615 Lockout::new_with_confirmation_count(4, 2),
3616 Lockout::new_with_confirmation_count(vote_slot, 1)
3617 ]
3618 );
3619
3620 assert_eq!(
3624 do_process_tower_sync(&mut vote_state, &slot_hashes, 0, 0, tower_sync,),
3625 Err(VoteError::LockoutConflict)
3626 );
3627 }
3628
3629 #[test_case(VoteStateTargetVersion::V3 ; "VoteStateV3")]
3630 #[test_case(VoteStateTargetVersion::V4 ; "VoteStateV4")]
3631 fn test_check_and_filter_proposed_vote_state_slot_hash_mismatch(
3632 target_version: VoteStateTargetVersion,
3633 ) {
3634 let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3635 let vote_state = build_vote_state(target_version, vec![2, 4, 6], &slot_hashes);
3636
3637 let vote_slot = vote_state.votes().back().unwrap().slot() + 2;
3642 let vote_slot_hash = Hash::new_unique();
3643 let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3644 tower_sync.hash = vote_slot_hash;
3645 assert_eq!(
3646 check_and_filter_proposed_vote_state(
3647 &vote_state,
3648 &mut tower_sync.lockouts,
3649 &mut tower_sync.root,
3650 tower_sync.hash,
3651 &slot_hashes,
3652 ),
3653 Err(VoteError::SlotHashMismatch),
3654 );
3655 }
3656
3657 #[test_case(0, true; "first slot")]
3658 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3659 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3660 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3661 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3662 fn test_epoch_half_check(slot: Slot, expected_allowed: bool) {
3663 let epoch_schedule = EpochSchedule::without_warmup();
3664 assert_eq!(
3665 is_commission_update_allowed(slot, &epoch_schedule),
3666 expected_allowed
3667 );
3668 }
3669
3670 #[test]
3671 fn test_warmup_epoch_half_check_with_warmup() {
3672 let epoch_schedule = EpochSchedule::default();
3673 let first_normal_slot = epoch_schedule.first_normal_slot;
3674 assert!(is_commission_update_allowed(0, &epoch_schedule));
3676 assert!(is_commission_update_allowed(
3679 first_normal_slot - 1,
3680 &epoch_schedule
3681 ));
3682 }
3683
3684 #[test_case(0, true; "first slot")]
3685 #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3686 #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3687 #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3688 #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3689 fn test_epoch_half_check_with_warmup(slot: Slot, expected_allowed: bool) {
3690 let epoch_schedule = EpochSchedule::default();
3691 let first_normal_slot = epoch_schedule.first_normal_slot;
3692 assert_eq!(
3693 is_commission_update_allowed(first_normal_slot.saturating_add(slot), &epoch_schedule),
3694 expected_allowed
3695 );
3696 }
3697
3698 #[test]
3699 fn test_create_v4_account_with_authorized() {
3700 let node_pubkey = Pubkey::new_unique();
3701 let authorized_voter = Pubkey::new_unique();
3702 let authorized_withdrawer = Pubkey::new_unique();
3703 let bls_pubkey_compressed = [42; 48];
3704 let inflation_rewards_commission_bps = 10000;
3705 let lamports = 100;
3706 let vote_account = create_v4_account_with_authorized(
3707 &node_pubkey,
3708 &authorized_voter,
3709 &authorized_withdrawer,
3710 Some(bls_pubkey_compressed),
3711 inflation_rewards_commission_bps,
3712 lamports,
3713 );
3714 assert_eq!(vote_account.lamports(), lamports);
3715 assert_eq!(vote_account.owner(), &id());
3716 assert_eq!(vote_account.data().len(), VoteStateV4::size_of());
3717 let vote_state_v4 = VoteStateV4::deserialize(vote_account.data(), &node_pubkey).unwrap();
3718 assert_eq!(vote_state_v4.node_pubkey, node_pubkey);
3719 assert_eq!(
3720 vote_state_v4.authorized_voters,
3721 AuthorizedVoters::new(0, authorized_voter)
3722 );
3723 assert_eq!(vote_state_v4.authorized_withdrawer, authorized_withdrawer);
3724 assert_eq!(
3725 vote_state_v4.bls_pubkey_compressed,
3726 Some(bls_pubkey_compressed)
3727 );
3728 assert_eq!(
3729 vote_state_v4.inflation_rewards_commission_bps,
3730 inflation_rewards_commission_bps
3731 );
3732 }
3733
3734 #[test]
3735 fn test_update_validator_identity_syncs_block_revenue_collector() {
3736 let vote_state =
3737 vote_state_new_for_test(&solana_pubkey::new_rand(), VoteStateTargetVersion::V4);
3738 let node_pubkey = *vote_state.node_pubkey();
3739 let withdrawer_pubkey = *vote_state.authorized_withdrawer();
3740
3741 let serialized = vote_state.serialize();
3742 let serialized_len = serialized.len();
3743 let rent = Rent::default();
3744 let lamports = rent.minimum_balance(serialized_len);
3745 let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id());
3746 vote_account.set_data_from_slice(&serialized);
3747
3748 let processor_account = AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id());
3749 let mut transaction_context = TransactionContext::new(
3750 vec![(id(), processor_account), (node_pubkey, vote_account)],
3751 rent,
3752 0,
3753 0,
3754 );
3755 transaction_context
3756 .configure_next_instruction_for_tests(
3757 0,
3758 vec![InstructionAccount::new(1, false, true)],
3759 vec![],
3760 )
3761 .unwrap();
3762 let instruction_context = transaction_context.get_next_instruction_context().unwrap();
3763 let mut borrowed_account = instruction_context
3764 .try_borrow_instruction_account(0)
3765 .unwrap();
3766
3767 let new_node_pubkey = solana_pubkey::new_rand();
3768 let signers: HashSet<Pubkey> = vec![withdrawer_pubkey, new_node_pubkey]
3769 .into_iter()
3770 .collect();
3771
3772 update_validator_identity(
3773 &mut borrowed_account,
3774 VoteStateTargetVersion::V4,
3775 &new_node_pubkey,
3776 &signers,
3777 )
3778 .unwrap();
3779
3780 let vote_state =
3783 VoteStateV4::deserialize(borrowed_account.get_data(), &new_node_pubkey).unwrap();
3784 assert_eq!(vote_state.node_pubkey, new_node_pubkey);
3785 assert_eq!(vote_state.block_revenue_collector, new_node_pubkey);
3786
3787 let new_node_pubkey = solana_pubkey::new_rand();
3789 let signers: HashSet<Pubkey> = vec![withdrawer_pubkey, new_node_pubkey]
3790 .into_iter()
3791 .collect();
3792
3793 update_validator_identity(
3794 &mut borrowed_account,
3795 VoteStateTargetVersion::V4,
3796 &new_node_pubkey,
3797 &signers,
3798 )
3799 .unwrap();
3800
3801 let vote_state =
3802 VoteStateV4::deserialize(borrowed_account.get_data(), &new_node_pubkey).unwrap();
3803 assert_eq!(vote_state.node_pubkey, new_node_pubkey);
3804 assert_eq!(vote_state.block_revenue_collector, new_node_pubkey);
3805 }
3806}