solana_vote_program/vote_state/
mod.rs

1//! Vote state, vote program
2//! Receive and processes votes from validators
3pub use solana_program::vote::state::{vote_state_versions::*, *};
4use {
5    log::*,
6    serde_derive::{Deserialize, Serialize},
7    solana_feature_set::{self as feature_set, FeatureSet},
8    solana_program::vote::{error::VoteError, program::id},
9    solana_sdk::{
10        account::{AccountSharedData, ReadableAccount, WritableAccount},
11        clock::{Epoch, Slot, UnixTimestamp},
12        epoch_schedule::EpochSchedule,
13        hash::Hash,
14        instruction::InstructionError,
15        pubkey::Pubkey,
16        rent::Rent,
17        slot_hashes::SlotHash,
18        sysvar::clock::Clock,
19        transaction_context::{
20            BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
21        },
22    },
23    std::{
24        cmp::Ordering,
25        collections::{HashSet, VecDeque},
26        fmt::Debug,
27    },
28};
29
30#[cfg_attr(
31    feature = "frozen-abi",
32    derive(AbiExample, AbiEnumVisitor),
33    frozen_abi(digest = "3dbyMxwfCN43orGKa5YiyY1EqN2K97pTicNhKYTZSUQH")
34)]
35#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
36pub enum VoteTransaction {
37    Vote(Vote),
38    VoteStateUpdate(VoteStateUpdate),
39    #[serde(with = "serde_compact_vote_state_update")]
40    CompactVoteStateUpdate(VoteStateUpdate),
41    #[serde(with = "serde_tower_sync")]
42    TowerSync(TowerSync),
43}
44
45impl VoteTransaction {
46    pub fn slots(&self) -> Vec<Slot> {
47        match self {
48            VoteTransaction::Vote(vote) => vote.slots.clone(),
49            VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.slots(),
50            VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.slots(),
51            VoteTransaction::TowerSync(tower_sync) => tower_sync.slots(),
52        }
53    }
54
55    pub fn slot(&self, i: usize) -> Slot {
56        match self {
57            VoteTransaction::Vote(vote) => vote.slots[i],
58            VoteTransaction::VoteStateUpdate(vote_state_update)
59            | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
60                vote_state_update.lockouts[i].slot()
61            }
62            VoteTransaction::TowerSync(tower_sync) => tower_sync.lockouts[i].slot(),
63        }
64    }
65
66    pub fn len(&self) -> usize {
67        match self {
68            VoteTransaction::Vote(vote) => vote.slots.len(),
69            VoteTransaction::VoteStateUpdate(vote_state_update)
70            | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
71                vote_state_update.lockouts.len()
72            }
73            VoteTransaction::TowerSync(tower_sync) => tower_sync.lockouts.len(),
74        }
75    }
76
77    pub fn is_empty(&self) -> bool {
78        match self {
79            VoteTransaction::Vote(vote) => vote.slots.is_empty(),
80            VoteTransaction::VoteStateUpdate(vote_state_update)
81            | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
82                vote_state_update.lockouts.is_empty()
83            }
84            VoteTransaction::TowerSync(tower_sync) => tower_sync.lockouts.is_empty(),
85        }
86    }
87
88    pub fn hash(&self) -> Hash {
89        match self {
90            VoteTransaction::Vote(vote) => vote.hash,
91            VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.hash,
92            VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.hash,
93            VoteTransaction::TowerSync(tower_sync) => tower_sync.hash,
94        }
95    }
96
97    pub fn timestamp(&self) -> Option<UnixTimestamp> {
98        match self {
99            VoteTransaction::Vote(vote) => vote.timestamp,
100            VoteTransaction::VoteStateUpdate(vote_state_update)
101            | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
102                vote_state_update.timestamp
103            }
104            VoteTransaction::TowerSync(tower_sync) => tower_sync.timestamp,
105        }
106    }
107
108    pub fn set_timestamp(&mut self, ts: Option<UnixTimestamp>) {
109        match self {
110            VoteTransaction::Vote(vote) => vote.timestamp = ts,
111            VoteTransaction::VoteStateUpdate(vote_state_update)
112            | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
113                vote_state_update.timestamp = ts
114            }
115            VoteTransaction::TowerSync(tower_sync) => tower_sync.timestamp = ts,
116        }
117    }
118
119    pub fn last_voted_slot(&self) -> Option<Slot> {
120        match self {
121            VoteTransaction::Vote(vote) => vote.last_voted_slot(),
122            VoteTransaction::VoteStateUpdate(vote_state_update)
123            | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
124                vote_state_update.last_voted_slot()
125            }
126            VoteTransaction::TowerSync(tower_sync) => tower_sync.last_voted_slot(),
127        }
128    }
129
130    pub fn last_voted_slot_hash(&self) -> Option<(Slot, Hash)> {
131        Some((self.last_voted_slot()?, self.hash()))
132    }
133}
134
135impl From<Vote> for VoteTransaction {
136    fn from(vote: Vote) -> Self {
137        VoteTransaction::Vote(vote)
138    }
139}
140
141impl From<VoteStateUpdate> for VoteTransaction {
142    fn from(vote_state_update: VoteStateUpdate) -> Self {
143        VoteTransaction::VoteStateUpdate(vote_state_update)
144    }
145}
146
147impl From<TowerSync> for VoteTransaction {
148    fn from(tower_sync: TowerSync) -> Self {
149        VoteTransaction::TowerSync(tower_sync)
150    }
151}
152
153// utility function, used by Stakes, tests
154pub fn from<T: ReadableAccount>(account: &T) -> Option<VoteState> {
155    VoteState::deserialize(account.data()).ok()
156}
157
158// utility function, used by Stakes, tests
159pub fn to<T: WritableAccount>(versioned: &VoteStateVersions, account: &mut T) -> Option<()> {
160    VoteState::serialize(versioned, account.data_as_mut_slice()).ok()
161}
162
163// Updates the vote account state with a new VoteState instance.  This is required temporarily during the
164// upgrade of vote account state from V1_14_11 to Current.
165fn set_vote_account_state(
166    vote_account: &mut BorrowedAccount,
167    vote_state: VoteState,
168) -> Result<(), InstructionError> {
169    // If the account is not large enough to store the vote state, then attempt a realloc to make it large enough.
170    // The realloc can only proceed if the vote account has balance sufficient for rent exemption at the new size.
171    if (vote_account.get_data().len() < VoteStateVersions::vote_state_size_of(true))
172        && (!vote_account
173            .is_rent_exempt_at_data_length(VoteStateVersions::vote_state_size_of(true))
174            || vote_account
175                .set_data_length(VoteStateVersions::vote_state_size_of(true))
176                .is_err())
177    {
178        // Account cannot be resized to the size of a vote state as it will not be rent exempt, or failed to be
179        // resized for other reasons.  So store the V1_14_11 version.
180        return vote_account.set_state(&VoteStateVersions::V1_14_11(Box::new(
181            VoteState1_14_11::from(vote_state),
182        )));
183    }
184    // Vote account is large enough to store the newest version of vote state
185    vote_account.set_state(&VoteStateVersions::new_current(vote_state))
186}
187
188/// Checks the proposed vote state with the current and
189/// slot hashes, making adjustments to the root / filtering
190/// votes as needed.
191fn check_and_filter_proposed_vote_state(
192    vote_state: &VoteState,
193    proposed_lockouts: &mut VecDeque<Lockout>,
194    proposed_root: &mut Option<Slot>,
195    proposed_hash: Hash,
196    slot_hashes: &[(Slot, Hash)],
197) -> Result<(), VoteError> {
198    if proposed_lockouts.is_empty() {
199        return Err(VoteError::EmptySlots);
200    }
201
202    let last_proposed_slot = proposed_lockouts
203        .back()
204        .expect("must be nonempty, checked above")
205        .slot();
206
207    // If the proposed state is not new enough, return
208    if let Some(last_vote_slot) = vote_state.votes.back().map(|lockout| lockout.slot()) {
209        if last_proposed_slot <= last_vote_slot {
210            return Err(VoteError::VoteTooOld);
211        }
212    }
213
214    if slot_hashes.is_empty() {
215        return Err(VoteError::SlotsMismatch);
216    }
217    let earliest_slot_hash_in_history = slot_hashes.last().unwrap().0;
218
219    // Check if the proposed vote state is too old to be in the SlotHash history
220    if last_proposed_slot < earliest_slot_hash_in_history {
221        // If this is the last slot in the vote update, it must be in SlotHashes,
222        // otherwise we have no way of confirming if the hash matches
223        return Err(VoteError::VoteTooOld);
224    }
225
226    // Overwrite the proposed root if it is too old to be in the SlotHash history
227    if let Some(root) = *proposed_root {
228        // If the new proposed root `R` is less than the earliest slot hash in the history
229        // such that we cannot verify whether the slot was actually was on this fork, set
230        // the root to the latest vote in the vote state that's less than R. If no
231        // votes from the vote state are less than R, use its root instead.
232        if root < earliest_slot_hash_in_history {
233            // First overwrite the proposed root with the vote state's root
234            *proposed_root = vote_state.root_slot;
235
236            // Then try to find the latest vote in vote state that's less than R
237            for vote in vote_state.votes.iter().rev() {
238                if vote.slot() <= root {
239                    *proposed_root = Some(vote.slot());
240                    break;
241                }
242            }
243        }
244    }
245
246    // Index into the new proposed vote state's slots, starting with the root if it exists then
247    // we use this mutable root to fold checking the root slot into the below loop
248    // for performance
249    let mut root_to_check = *proposed_root;
250    let mut proposed_lockouts_index = 0;
251
252    // index into the slot_hashes, starting at the oldest known
253    // slot hash
254    let mut slot_hashes_index = slot_hashes.len();
255
256    let mut proposed_lockouts_indices_to_filter = vec![];
257
258    // Note:
259    //
260    // 1) `proposed_lockouts` is sorted from oldest/smallest vote to newest/largest
261    // vote, due to the way votes are applied to the vote state (newest votes
262    // pushed to the back).
263    //
264    // 2) Conversely, `slot_hashes` is sorted from newest/largest vote to
265    // the oldest/smallest vote
266    //
267    // We check every proposed lockout because have to ensure that every slot is actually part of
268    // the history, not just the most recent ones
269    while proposed_lockouts_index < proposed_lockouts.len() && slot_hashes_index > 0 {
270        let proposed_vote_slot = if let Some(root) = root_to_check {
271            root
272        } else {
273            proposed_lockouts[proposed_lockouts_index].slot()
274        };
275        if root_to_check.is_none()
276            && proposed_lockouts_index > 0
277            && proposed_vote_slot
278                <= proposed_lockouts[proposed_lockouts_index.checked_sub(1).expect(
279                    "`proposed_lockouts_index` is positive when checking `SlotsNotOrdered`",
280                )]
281                .slot()
282        {
283            return Err(VoteError::SlotsNotOrdered);
284        }
285        let ancestor_slot = slot_hashes[slot_hashes_index
286            .checked_sub(1)
287            .expect("`slot_hashes_index` is positive when computing `ancestor_slot`")]
288        .0;
289
290        // Find if this slot in the proposed vote state exists in the SlotHashes history
291        // to confirm if it was a valid ancestor on this fork
292        match proposed_vote_slot.cmp(&ancestor_slot) {
293            Ordering::Less => {
294                if slot_hashes_index == slot_hashes.len() {
295                    // The vote slot does not exist in the SlotHashes history because it's too old,
296                    // i.e. older than the oldest slot in the history.
297                    if proposed_vote_slot >= earliest_slot_hash_in_history {
298                        return Err(VoteError::AssertionFailed);
299                    }
300                    if !vote_state.contains_slot(proposed_vote_slot) && root_to_check.is_none() {
301                        // If the vote slot is both:
302                        // 1) Too old
303                        // 2) Doesn't already exist in vote state
304                        //
305                        // Then filter it out
306                        proposed_lockouts_indices_to_filter.push(proposed_lockouts_index);
307                    }
308                    if let Some(new_proposed_root) = root_to_check {
309                        // 1. Because `root_to_check.is_some()`, then we know that
310                        // we haven't checked the root yet in this loop, so
311                        // `proposed_vote_slot` == `new_proposed_root` == `proposed_root`.
312                        assert_eq!(new_proposed_root, proposed_vote_slot);
313                        // 2. We know from the assert earlier in the function that
314                        // `proposed_vote_slot < earliest_slot_hash_in_history`,
315                        // so from 1. we know that `new_proposed_root < earliest_slot_hash_in_history`.
316                        if new_proposed_root >= earliest_slot_hash_in_history {
317                            return Err(VoteError::AssertionFailed);
318                        }
319                        root_to_check = None;
320                    } else {
321                        proposed_lockouts_index = proposed_lockouts_index.checked_add(1).expect(
322                            "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when `proposed_vote_slot` is too old to be in SlotHashes history",
323                        );
324                    }
325                    continue;
326                } else {
327                    // If the vote slot is new enough to be in the slot history,
328                    // but is not part of the slot history, then it must belong to another fork,
329                    // which means this proposed vote state is invalid.
330                    if root_to_check.is_some() {
331                        return Err(VoteError::RootOnDifferentFork);
332                    } else {
333                        return Err(VoteError::SlotsMismatch);
334                    }
335                }
336            }
337            Ordering::Greater => {
338                // Decrement `slot_hashes_index` to find newer slots in the SlotHashes history
339                slot_hashes_index = slot_hashes_index
340                    .checked_sub(1)
341                    .expect("`slot_hashes_index` is positive when finding newer slots in SlotHashes history");
342                continue;
343            }
344            Ordering::Equal => {
345                // Once the slot in `proposed_lockouts` is found, bump to the next slot
346                // in `proposed_lockouts` and continue. If we were checking the root,
347                // start checking the vote state instead.
348                if root_to_check.is_some() {
349                    root_to_check = None;
350                } else {
351                    proposed_lockouts_index = proposed_lockouts_index
352                        .checked_add(1)
353                        .expect("`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when match is found in SlotHashes history");
354                    slot_hashes_index = slot_hashes_index.checked_sub(1).expect(
355                        "`slot_hashes_index` is positive when match is found in SlotHashes history",
356                    );
357                }
358            }
359        }
360    }
361
362    if proposed_lockouts_index != proposed_lockouts.len() {
363        // The last vote slot in the proposed vote state did not exist in SlotHashes
364        return Err(VoteError::SlotsMismatch);
365    }
366
367    // This assertion must be true at this point because we can assume by now:
368    // 1) proposed_lockouts_index == proposed_lockouts.len()
369    // 2) last_proposed_slot >= earliest_slot_hash_in_history
370    // 3) !proposed_lockouts.is_empty()
371    //
372    // 1) implies that during the last iteration of the loop above,
373    // `proposed_lockouts_index` was equal to `proposed_lockouts.len() - 1`,
374    // and was then incremented to `proposed_lockouts.len()`.
375    // This means in that last loop iteration,
376    // `proposed_vote_slot ==
377    //  proposed_lockouts[proposed_lockouts.len() - 1] ==
378    //  last_proposed_slot`.
379    //
380    // Then we know the last comparison `match proposed_vote_slot.cmp(&ancestor_slot)`
381    // is equivalent to `match last_proposed_slot.cmp(&ancestor_slot)`. The result
382    // of this match to increment `proposed_lockouts_index` must have been either:
383    //
384    // 1) The Equal case ran, in which case then we know this assertion must be true
385    // 2) The Less case ran, and more specifically the case
386    // `proposed_vote_slot < earliest_slot_hash_in_history` ran, which is equivalent to
387    // `last_proposed_slot < earliest_slot_hash_in_history`, but this is impossible
388    // due to assumption 3) above.
389    assert_eq!(last_proposed_slot, slot_hashes[slot_hashes_index].0);
390
391    if slot_hashes[slot_hashes_index].1 != proposed_hash {
392        // This means the newest vote in the slot has a match that
393        // doesn't match the expected hash for that slot on this
394        // fork
395        warn!(
396            "{} dropped vote {:?} root {:?} failed to match hash {} {}",
397            vote_state.node_pubkey,
398            proposed_lockouts,
399            proposed_root,
400            proposed_hash,
401            slot_hashes[slot_hashes_index].1
402        );
403        inc_new_counter_info!("dropped-vote-hash", 1);
404        return Err(VoteError::SlotHashMismatch);
405    }
406
407    // Filter out the irrelevant votes
408    let mut proposed_lockouts_index = 0;
409    let mut filter_votes_index = 0;
410    proposed_lockouts.retain(|_lockout| {
411        let should_retain = if filter_votes_index == proposed_lockouts_indices_to_filter.len() {
412            true
413        } else if proposed_lockouts_index == proposed_lockouts_indices_to_filter[filter_votes_index]
414        {
415            filter_votes_index = filter_votes_index.checked_add(1).unwrap();
416            false
417        } else {
418            true
419        };
420
421        proposed_lockouts_index = proposed_lockouts_index
422            .checked_add(1)
423            .expect("`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when filtering out irrelevant votes");
424        should_retain
425    });
426
427    Ok(())
428}
429
430fn check_slots_are_valid(
431    vote_state: &VoteState,
432    vote_slots: &[Slot],
433    vote_hash: &Hash,
434    slot_hashes: &[(Slot, Hash)],
435) -> Result<(), VoteError> {
436    // index into the vote's slots, starting at the oldest
437    // slot
438    let mut i = 0;
439
440    // index into the slot_hashes, starting at the oldest known
441    // slot hash
442    let mut j = slot_hashes.len();
443
444    // Note:
445    //
446    // 1) `vote_slots` is sorted from oldest/smallest vote to newest/largest
447    // vote, due to the way votes are applied to the vote state (newest votes
448    // pushed to the back).
449    //
450    // 2) Conversely, `slot_hashes` is sorted from newest/largest vote to
451    // the oldest/smallest vote
452    while i < vote_slots.len() && j > 0 {
453        // 1) increment `i` to find the smallest slot `s` in `vote_slots`
454        // where `s` >= `last_voted_slot`
455        if vote_state
456            .last_voted_slot()
457            .map_or(false, |last_voted_slot| vote_slots[i] <= last_voted_slot)
458        {
459            i = i
460                .checked_add(1)
461                .expect("`i` is bounded by `MAX_LOCKOUT_HISTORY` when finding larger slots");
462            continue;
463        }
464
465        // 2) Find the hash for this slot `s`.
466        if vote_slots[i] != slot_hashes[j.checked_sub(1).expect("`j` is positive")].0 {
467            // Decrement `j` to find newer slots
468            j = j
469                .checked_sub(1)
470                .expect("`j` is positive when finding newer slots");
471            continue;
472        }
473
474        // 3) Once the hash for `s` is found, bump `s` to the next slot
475        // in `vote_slots` and continue.
476        i = i
477            .checked_add(1)
478            .expect("`i` is bounded by `MAX_LOCKOUT_HISTORY` when hash is found");
479        j = j
480            .checked_sub(1)
481            .expect("`j` is positive when hash is found");
482    }
483
484    if j == slot_hashes.len() {
485        // This means we never made it to steps 2) or 3) above, otherwise
486        // `j` would have been decremented at least once. This means
487        // there are not slots in `vote_slots` greater than `last_voted_slot`
488        debug!(
489            "{} dropped vote slots {:?}, vote hash: {:?} slot hashes:SlotHash {:?}, too old ",
490            vote_state.node_pubkey, vote_slots, vote_hash, slot_hashes
491        );
492        return Err(VoteError::VoteTooOld);
493    }
494    if i != vote_slots.len() {
495        // This means there existed some slot for which we couldn't find
496        // a matching slot hash in step 2)
497        info!(
498            "{} dropped vote slots {:?} failed to match slot hashes: {:?}",
499            vote_state.node_pubkey, vote_slots, slot_hashes,
500        );
501        return Err(VoteError::SlotsMismatch);
502    }
503    if &slot_hashes[j].1 != vote_hash {
504        // This means the newest slot in the `vote_slots` has a match that
505        // doesn't match the expected hash for that slot on this
506        // fork
507        warn!(
508            "{} dropped vote slots {:?} failed to match hash {} {}",
509            vote_state.node_pubkey, vote_slots, vote_hash, slot_hashes[j].1
510        );
511        return Err(VoteError::SlotHashMismatch);
512    }
513    Ok(())
514}
515
516// Ensure `check_and_filter_proposed_vote_state(&)` runs on the slots in `new_state`
517// before `process_new_vote_state()` is called
518
519// This function should guarantee the following about `new_state`:
520//
521// 1) It's well ordered, i.e. the slots are sorted from smallest to largest,
522// and the confirmations sorted from largest to smallest.
523// 2) Confirmations `c` on any vote slot satisfy `0 < c <= MAX_LOCKOUT_HISTORY`
524// 3) Lockouts are not expired by consecutive votes, i.e. for every consecutive
525// `v_i`, `v_{i + 1}` satisfy `v_i.last_locked_out_slot() >= v_{i + 1}`.
526
527// We also guarantee that compared to the current vote state, `new_state`
528// introduces no rollback. This means:
529//
530// 1) The last slot in `new_state` is always greater than any slot in the
531// current vote state.
532//
533// 2) From 1), this means that for every vote `s` in the current state:
534//    a) If there exists an `s'` in `new_state` where `s.slot == s'.slot`, then
535//    we must guarantee `s.confirmations <= s'.confirmations`
536//
537//    b) If there does not exist any such `s'` in `new_state`, then there exists
538//    some `t` that is the smallest vote in `new_state` where `t.slot > s.slot`.
539//    `t` must have expired/popped off s', so it must be guaranteed that
540//    `s.last_locked_out_slot() < t`.
541
542// Note these two above checks do not guarantee that the vote state being submitted
543// is a vote state that could have been created by iteratively building a tower
544// by processing one vote at a time. For instance, the tower:
545//
546// { slot 0, confirmations: 31 }
547// { slot 1, confirmations: 30 }
548//
549// is a legal tower that could be submitted on top of a previously empty tower. However,
550// there is no way to create this tower from the iterative process, because slot 1 would
551// have to have at least one other slot on top of it, even if the first 30 votes were all
552// popped off.
553pub fn process_new_vote_state(
554    vote_state: &mut VoteState,
555    mut new_state: VecDeque<LandedVote>,
556    new_root: Option<Slot>,
557    timestamp: Option<i64>,
558    epoch: Epoch,
559    current_slot: Slot,
560    feature_set: Option<&FeatureSet>,
561) -> Result<(), VoteError> {
562    assert!(!new_state.is_empty());
563    if new_state.len() > MAX_LOCKOUT_HISTORY {
564        return Err(VoteError::TooManyVotes);
565    }
566
567    match (new_root, vote_state.root_slot) {
568        (Some(new_root), Some(current_root)) => {
569            if new_root < current_root {
570                return Err(VoteError::RootRollBack);
571            }
572        }
573        (None, Some(_)) => {
574            return Err(VoteError::RootRollBack);
575        }
576        _ => (),
577    }
578
579    let mut previous_vote: Option<&LandedVote> = None;
580
581    // Check that all the votes in the new proposed state are:
582    // 1) Strictly sorted from oldest to newest vote
583    // 2) The confirmations are strictly decreasing
584    // 3) Not zero confirmation votes
585    for vote in &new_state {
586        if vote.confirmation_count() == 0 {
587            return Err(VoteError::ZeroConfirmations);
588        } else if vote.confirmation_count() > MAX_LOCKOUT_HISTORY as u32 {
589            return Err(VoteError::ConfirmationTooLarge);
590        } else if let Some(new_root) = new_root {
591            if vote.slot() <= new_root
592                &&
593                // This check is necessary because
594                // https://github.com/ryoqun/solana/blob/df55bfb46af039cbc597cd60042d49b9d90b5961/core/src/consensus.rs#L120
595                // always sets a root for even empty towers, which is then hard unwrapped here
596                // https://github.com/ryoqun/solana/blob/df55bfb46af039cbc597cd60042d49b9d90b5961/core/src/consensus.rs#L776
597                new_root != Slot::default()
598            {
599                return Err(VoteError::SlotSmallerThanRoot);
600            }
601        }
602
603        if let Some(previous_vote) = previous_vote {
604            if previous_vote.slot() >= vote.slot() {
605                return Err(VoteError::SlotsNotOrdered);
606            } else if previous_vote.confirmation_count() <= vote.confirmation_count() {
607                return Err(VoteError::ConfirmationsNotOrdered);
608            } else if vote.slot() > previous_vote.lockout.last_locked_out_slot() {
609                return Err(VoteError::NewVoteStateLockoutMismatch);
610            }
611        }
612        previous_vote = Some(vote);
613    }
614
615    // Find the first vote in the current vote state for a slot greater
616    // than the new proposed root
617    let mut current_vote_state_index: usize = 0;
618    let mut new_vote_state_index = 0;
619
620    // Accumulate credits earned by newly rooted slots.  The behavior changes with timely_vote_credits: prior to
621    // this feature, there was a bug that counted a new root slot as 1 credit even if it had never been voted on.
622    // timely_vote_credits fixes this bug by only awarding credits for slots actually voted on and finalized.
623    let timely_vote_credits = feature_set.map_or(false, |f| {
624        f.is_active(&feature_set::timely_vote_credits::id())
625    });
626    let mut earned_credits = if timely_vote_credits { 0_u64 } else { 1_u64 };
627
628    if let Some(new_root) = new_root {
629        for current_vote in &vote_state.votes {
630            // Find the first vote in the current vote state for a slot greater
631            // than the new proposed root
632            if current_vote.slot() <= new_root {
633                if timely_vote_credits || (current_vote.slot() != new_root) {
634                    earned_credits = earned_credits
635                        .checked_add(vote_state.credits_for_vote_at_index(
636                            current_vote_state_index,
637                            timely_vote_credits,
638                        ))
639                        .expect("`earned_credits` does not overflow");
640                }
641                current_vote_state_index = current_vote_state_index
642                    .checked_add(1)
643                    .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when processing new root");
644                continue;
645            }
646
647            break;
648        }
649    }
650
651    // For any slots newly added to the new vote state, the vote latency of that slot is not provided by the
652    // vote instruction contents, but instead is computed from the actual latency of the vote
653    // instruction. This prevents other validators from manipulating their own vote latencies within their vote states
654    // and forcing the rest of the cluster to accept these possibly fraudulent latency values.  If the
655    // timly_vote_credits feature is not enabled then vote latency is set to 0 for new votes.
656    //
657    // For any slot that is in both the new state and the current state, the vote latency of the new state is taken
658    // from the current state.
659    //
660    // Thus vote latencies are set here for any newly vote-on slots when a vote instruction is received.
661    // They are copied into the new vote state after every vote for already voted-on slots.
662    // And when voted-on slots are rooted, the vote latencies stored in the vote state of all the rooted slots is used
663    // to compute credits earned.
664    // All validators compute the same vote latencies because all process the same vote instruction at the
665    // same slot, and the only time vote latencies are ever computed is at the time that their slot is first voted on;
666    // after that, the latencies are retained unaltered until the slot is rooted.
667
668    // All the votes in our current vote state that are missing from the new vote state
669    // must have been expired by later votes. Check that the lockouts match this assumption.
670    while current_vote_state_index < vote_state.votes.len()
671        && new_vote_state_index < new_state.len()
672    {
673        let current_vote = &vote_state.votes[current_vote_state_index];
674        let new_vote = &mut new_state[new_vote_state_index];
675
676        // If the current slot is less than the new proposed slot, then the
677        // new slot must have popped off the old slot, so check that the
678        // lockouts are corrects.
679        match current_vote.slot().cmp(&new_vote.slot()) {
680            Ordering::Less => {
681                if current_vote.lockout.last_locked_out_slot() >= new_vote.slot() {
682                    return Err(VoteError::LockoutConflict);
683                }
684                current_vote_state_index = current_vote_state_index
685                    .checked_add(1)
686                    .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is less than proposed");
687            }
688            Ordering::Equal => {
689                // The new vote state should never have less lockout than
690                // the previous vote state for the same slot
691                if new_vote.confirmation_count() < current_vote.confirmation_count() {
692                    return Err(VoteError::ConfirmationRollBack);
693                }
694
695                // Copy the vote slot latency in from the current state to the new state
696                new_vote.latency = vote_state.votes[current_vote_state_index].latency;
697
698                current_vote_state_index = current_vote_state_index
699                    .checked_add(1)
700                    .expect("`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is equal to proposed");
701                new_vote_state_index = new_vote_state_index
702                    .checked_add(1)
703                    .expect("`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is equal to proposed");
704            }
705            Ordering::Greater => {
706                new_vote_state_index = new_vote_state_index
707                    .checked_add(1)
708                    .expect("`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` when slot is greater than proposed");
709            }
710        }
711    }
712
713    // `new_vote_state` passed all the checks, finalize the change by rewriting
714    // our state.
715
716    // Now set the vote latencies on new slots not in the current state.  New slots not in the current vote state will
717    // have had their latency initialized to 0 by the above loop.  Those will now be updated to their actual latency.
718    // If the timely_vote_credits feature is not enabled, then the latency is left as 0 for such slots, which will
719    // result in 1 credit per slot when credits are calculated at the time that the slot is rooted.
720    if timely_vote_credits {
721        for new_vote in new_state.iter_mut() {
722            if new_vote.latency == 0 {
723                new_vote.latency = VoteState::compute_vote_latency(new_vote.slot(), current_slot);
724            }
725        }
726    }
727
728    if vote_state.root_slot != new_root {
729        // Award vote credits based on the number of slots that were voted on and have reached finality
730        // For each finalized slot, there was one voted-on slot in the new vote state that was responsible for
731        // finalizing it. Each of those votes is awarded 1 credit.
732        vote_state.increment_credits(epoch, earned_credits);
733    }
734    if let Some(timestamp) = timestamp {
735        let last_slot = new_state.back().unwrap().slot();
736        vote_state.process_timestamp(last_slot, timestamp)?;
737    }
738    vote_state.root_slot = new_root;
739    vote_state.votes = new_state;
740
741    Ok(())
742}
743
744pub fn process_vote_unfiltered(
745    vote_state: &mut VoteState,
746    vote_slots: &[Slot],
747    vote: &Vote,
748    slot_hashes: &[SlotHash],
749    epoch: Epoch,
750    current_slot: Slot,
751    timely_vote_credits: bool,
752) -> Result<(), VoteError> {
753    check_slots_are_valid(vote_state, vote_slots, &vote.hash, slot_hashes)?;
754    vote_slots.iter().for_each(|s| {
755        vote_state.process_next_vote_slot(*s, epoch, current_slot, timely_vote_credits)
756    });
757    Ok(())
758}
759
760pub fn process_vote(
761    vote_state: &mut VoteState,
762    vote: &Vote,
763    slot_hashes: &[SlotHash],
764    epoch: Epoch,
765    current_slot: Slot,
766    timely_vote_credits: bool,
767) -> Result<(), VoteError> {
768    if vote.slots.is_empty() {
769        return Err(VoteError::EmptySlots);
770    }
771    let earliest_slot_in_history = slot_hashes.last().map(|(slot, _hash)| *slot).unwrap_or(0);
772    let vote_slots = vote
773        .slots
774        .iter()
775        .filter(|slot| **slot >= earliest_slot_in_history)
776        .cloned()
777        .collect::<Vec<Slot>>();
778    if vote_slots.is_empty() {
779        return Err(VoteError::VotesTooOldAllFiltered);
780    }
781    process_vote_unfiltered(
782        vote_state,
783        &vote_slots,
784        vote,
785        slot_hashes,
786        epoch,
787        current_slot,
788        timely_vote_credits,
789    )
790}
791
792/// "unchecked" functions used by tests and Tower
793pub fn process_vote_unchecked(vote_state: &mut VoteState, vote: Vote) -> Result<(), VoteError> {
794    if vote.slots.is_empty() {
795        return Err(VoteError::EmptySlots);
796    }
797    let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
798    process_vote_unfiltered(
799        vote_state,
800        &vote.slots,
801        &vote,
802        &slot_hashes,
803        vote_state.current_epoch(),
804        0,
805        true,
806    )
807}
808
809#[cfg(test)]
810pub fn process_slot_votes_unchecked(vote_state: &mut VoteState, slots: &[Slot]) {
811    for slot in slots {
812        process_slot_vote_unchecked(vote_state, *slot);
813    }
814}
815
816pub fn process_slot_vote_unchecked(vote_state: &mut VoteState, slot: Slot) {
817    let _ = process_vote_unchecked(vote_state, Vote::new(vec![slot], Hash::default()));
818}
819
820/// Authorize the given pubkey to withdraw or sign votes. This may be called multiple times,
821/// but will implicitly withdraw authorization from the previously authorized
822/// key
823pub fn authorize<S: std::hash::BuildHasher>(
824    vote_account: &mut BorrowedAccount,
825    authorized: &Pubkey,
826    vote_authorize: VoteAuthorize,
827    signers: &HashSet<Pubkey, S>,
828    clock: &Clock,
829) -> Result<(), InstructionError> {
830    let mut vote_state: VoteState = vote_account
831        .get_state::<VoteStateVersions>()?
832        .convert_to_current();
833
834    match vote_authorize {
835        VoteAuthorize::Voter => {
836            let authorized_withdrawer_signer =
837                verify_authorized_signer(&vote_state.authorized_withdrawer, signers).is_ok();
838
839            vote_state.set_new_authorized_voter(
840                authorized,
841                clock.epoch,
842                clock
843                    .leader_schedule_epoch
844                    .checked_add(1)
845                    .ok_or(InstructionError::InvalidAccountData)?,
846                |epoch_authorized_voter| {
847                    // current authorized withdrawer or authorized voter must say "yay"
848                    if authorized_withdrawer_signer {
849                        Ok(())
850                    } else {
851                        verify_authorized_signer(&epoch_authorized_voter, signers)
852                    }
853                },
854            )?;
855        }
856        VoteAuthorize::Withdrawer => {
857            // current authorized withdrawer must say "yay"
858            verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
859            vote_state.authorized_withdrawer = *authorized;
860        }
861    }
862
863    set_vote_account_state(vote_account, vote_state)
864}
865
866/// Update the node_pubkey, requires signature of the authorized voter
867pub fn update_validator_identity<S: std::hash::BuildHasher>(
868    vote_account: &mut BorrowedAccount,
869    node_pubkey: &Pubkey,
870    signers: &HashSet<Pubkey, S>,
871) -> Result<(), InstructionError> {
872    let mut vote_state: VoteState = vote_account
873        .get_state::<VoteStateVersions>()?
874        .convert_to_current();
875
876    // current authorized withdrawer must say "yay"
877    verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
878
879    // new node must say "yay"
880    verify_authorized_signer(node_pubkey, signers)?;
881
882    vote_state.node_pubkey = *node_pubkey;
883
884    set_vote_account_state(vote_account, vote_state)
885}
886
887/// Update the vote account's commission
888pub fn update_commission<S: std::hash::BuildHasher>(
889    vote_account: &mut BorrowedAccount,
890    commission: u8,
891    signers: &HashSet<Pubkey, S>,
892    epoch_schedule: &EpochSchedule,
893    clock: &Clock,
894    feature_set: &FeatureSet,
895) -> Result<(), InstructionError> {
896    // Decode vote state only once, and only if needed
897    let mut vote_state = None;
898
899    let enforce_commission_update_rule =
900        if feature_set.is_active(&feature_set::allow_commission_decrease_at_any_time::id()) {
901            if let Ok(decoded_vote_state) = vote_account.get_state::<VoteStateVersions>() {
902                vote_state = Some(decoded_vote_state.convert_to_current());
903                is_commission_increase(vote_state.as_ref().unwrap(), commission)
904            } else {
905                true
906            }
907        } else {
908            true
909        };
910
911    #[allow(clippy::collapsible_if)]
912    if enforce_commission_update_rule
913        && feature_set
914            .is_active(&feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id())
915    {
916        if !is_commission_update_allowed(clock.slot, epoch_schedule) {
917            return Err(VoteError::CommissionUpdateTooLate.into());
918        }
919    }
920
921    let mut vote_state = match vote_state {
922        Some(vote_state) => vote_state,
923        None => vote_account
924            .get_state::<VoteStateVersions>()?
925            .convert_to_current(),
926    };
927
928    // current authorized withdrawer must say "yay"
929    verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
930
931    vote_state.commission = commission;
932
933    set_vote_account_state(vote_account, vote_state)
934}
935
936/// Given a proposed new commission, returns true if this would be a commission increase, false otherwise
937pub fn is_commission_increase(vote_state: &VoteState, commission: u8) -> bool {
938    commission > vote_state.commission
939}
940
941/// Given the current slot and epoch schedule, determine if a commission change
942/// is allowed
943pub fn is_commission_update_allowed(slot: Slot, epoch_schedule: &EpochSchedule) -> bool {
944    // always allowed during warmup epochs
945    if let Some(relative_slot) = slot
946        .saturating_sub(epoch_schedule.first_normal_slot)
947        .checked_rem(epoch_schedule.slots_per_epoch)
948    {
949        // allowed up to the midpoint of the epoch
950        relative_slot.saturating_mul(2) <= epoch_schedule.slots_per_epoch
951    } else {
952        // no slots per epoch, just allow it, even though this should never happen
953        true
954    }
955}
956
957fn verify_authorized_signer<S: std::hash::BuildHasher>(
958    authorized: &Pubkey,
959    signers: &HashSet<Pubkey, S>,
960) -> Result<(), InstructionError> {
961    if signers.contains(authorized) {
962        Ok(())
963    } else {
964        Err(InstructionError::MissingRequiredSignature)
965    }
966}
967
968/// Withdraw funds from the vote account
969pub fn withdraw<S: std::hash::BuildHasher>(
970    transaction_context: &TransactionContext,
971    instruction_context: &InstructionContext,
972    vote_account_index: IndexOfAccount,
973    lamports: u64,
974    to_account_index: IndexOfAccount,
975    signers: &HashSet<Pubkey, S>,
976    rent_sysvar: &Rent,
977    clock: &Clock,
978) -> Result<(), InstructionError> {
979    let mut vote_account = instruction_context
980        .try_borrow_instruction_account(transaction_context, vote_account_index)?;
981    let vote_state: VoteState = vote_account
982        .get_state::<VoteStateVersions>()?
983        .convert_to_current();
984
985    verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
986
987    let remaining_balance = vote_account
988        .get_lamports()
989        .checked_sub(lamports)
990        .ok_or(InstructionError::InsufficientFunds)?;
991
992    if remaining_balance == 0 {
993        let reject_active_vote_account_close = vote_state
994            .epoch_credits
995            .last()
996            .map(|(last_epoch_with_credits, _, _)| {
997                let current_epoch = clock.epoch;
998                // if current_epoch - last_epoch_with_credits < 2 then the validator has received credits
999                // either in the current epoch or the previous epoch. If it's >= 2 then it has been at least
1000                // one full epoch since the validator has received credits.
1001                current_epoch.saturating_sub(*last_epoch_with_credits) < 2
1002            })
1003            .unwrap_or(false);
1004
1005        if reject_active_vote_account_close {
1006            return Err(VoteError::ActiveVoteAccountClose.into());
1007        } else {
1008            // Deinitialize upon zero-balance
1009            set_vote_account_state(&mut vote_account, VoteState::default())?;
1010        }
1011    } else {
1012        let min_rent_exempt_balance = rent_sysvar.minimum_balance(vote_account.get_data().len());
1013        if remaining_balance < min_rent_exempt_balance {
1014            return Err(InstructionError::InsufficientFunds);
1015        }
1016    }
1017
1018    vote_account.checked_sub_lamports(lamports)?;
1019    drop(vote_account);
1020    let mut to_account = instruction_context
1021        .try_borrow_instruction_account(transaction_context, to_account_index)?;
1022    to_account.checked_add_lamports(lamports)?;
1023    Ok(())
1024}
1025
1026/// Initialize the vote_state for a vote account
1027/// Assumes that the account is being init as part of a account creation or balance transfer and
1028/// that the transaction must be signed by the staker's keys
1029pub fn initialize_account<S: std::hash::BuildHasher>(
1030    vote_account: &mut BorrowedAccount,
1031    vote_init: &VoteInit,
1032    signers: &HashSet<Pubkey, S>,
1033    clock: &Clock,
1034) -> Result<(), InstructionError> {
1035    if vote_account.get_data().len() != VoteStateVersions::vote_state_size_of(true) {
1036        return Err(InstructionError::InvalidAccountData);
1037    }
1038    let versioned = vote_account.get_state::<VoteStateVersions>()?;
1039
1040    if !versioned.is_uninitialized() {
1041        return Err(InstructionError::AccountAlreadyInitialized);
1042    }
1043
1044    // node must agree to accept this vote account
1045    verify_authorized_signer(&vote_init.node_pubkey, signers)?;
1046
1047    set_vote_account_state(vote_account, VoteState::new(vote_init, clock))
1048}
1049
1050fn verify_and_get_vote_state<S: std::hash::BuildHasher>(
1051    vote_account: &BorrowedAccount,
1052    clock: &Clock,
1053    signers: &HashSet<Pubkey, S>,
1054) -> Result<VoteState, InstructionError> {
1055    let versioned = vote_account.get_state::<VoteStateVersions>()?;
1056
1057    if versioned.is_uninitialized() {
1058        return Err(InstructionError::UninitializedAccount);
1059    }
1060
1061    let mut vote_state = versioned.convert_to_current();
1062    let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?;
1063    verify_authorized_signer(&authorized_voter, signers)?;
1064
1065    Ok(vote_state)
1066}
1067
1068pub fn process_vote_with_account<S: std::hash::BuildHasher>(
1069    vote_account: &mut BorrowedAccount,
1070    slot_hashes: &[SlotHash],
1071    clock: &Clock,
1072    vote: &Vote,
1073    signers: &HashSet<Pubkey, S>,
1074    feature_set: &FeatureSet,
1075) -> Result<(), InstructionError> {
1076    let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1077
1078    let timely_vote_credits = feature_set.is_active(&feature_set::timely_vote_credits::id());
1079    process_vote(
1080        &mut vote_state,
1081        vote,
1082        slot_hashes,
1083        clock.epoch,
1084        clock.slot,
1085        timely_vote_credits,
1086    )?;
1087    if let Some(timestamp) = vote.timestamp {
1088        vote.slots
1089            .iter()
1090            .max()
1091            .ok_or(VoteError::EmptySlots)
1092            .and_then(|slot| vote_state.process_timestamp(*slot, timestamp))?;
1093    }
1094    set_vote_account_state(vote_account, vote_state)
1095}
1096
1097pub fn process_vote_state_update<S: std::hash::BuildHasher>(
1098    vote_account: &mut BorrowedAccount,
1099    slot_hashes: &[SlotHash],
1100    clock: &Clock,
1101    vote_state_update: VoteStateUpdate,
1102    signers: &HashSet<Pubkey, S>,
1103    feature_set: &FeatureSet,
1104) -> Result<(), InstructionError> {
1105    let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1106    do_process_vote_state_update(
1107        &mut vote_state,
1108        slot_hashes,
1109        clock.epoch,
1110        clock.slot,
1111        vote_state_update,
1112        Some(feature_set),
1113    )?;
1114    set_vote_account_state(vote_account, vote_state)
1115}
1116
1117pub fn do_process_vote_state_update(
1118    vote_state: &mut VoteState,
1119    slot_hashes: &[SlotHash],
1120    epoch: u64,
1121    slot: u64,
1122    mut vote_state_update: VoteStateUpdate,
1123    feature_set: Option<&FeatureSet>,
1124) -> Result<(), VoteError> {
1125    check_and_filter_proposed_vote_state(
1126        vote_state,
1127        &mut vote_state_update.lockouts,
1128        &mut vote_state_update.root,
1129        vote_state_update.hash,
1130        slot_hashes,
1131    )?;
1132    process_new_vote_state(
1133        vote_state,
1134        vote_state_update
1135            .lockouts
1136            .iter()
1137            .map(|lockout| LandedVote::from(*lockout))
1138            .collect(),
1139        vote_state_update.root,
1140        vote_state_update.timestamp,
1141        epoch,
1142        slot,
1143        feature_set,
1144    )
1145}
1146
1147pub fn process_tower_sync<S: std::hash::BuildHasher>(
1148    vote_account: &mut BorrowedAccount,
1149    slot_hashes: &[SlotHash],
1150    clock: &Clock,
1151    tower_sync: TowerSync,
1152    signers: &HashSet<Pubkey, S>,
1153    feature_set: &FeatureSet,
1154) -> Result<(), InstructionError> {
1155    let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1156    do_process_tower_sync(
1157        &mut vote_state,
1158        slot_hashes,
1159        clock.epoch,
1160        clock.slot,
1161        tower_sync,
1162        Some(feature_set),
1163    )?;
1164    set_vote_account_state(vote_account, vote_state)
1165}
1166
1167fn do_process_tower_sync(
1168    vote_state: &mut VoteState,
1169    slot_hashes: &[SlotHash],
1170    epoch: u64,
1171    slot: u64,
1172    mut tower_sync: TowerSync,
1173    feature_set: Option<&FeatureSet>,
1174) -> Result<(), VoteError> {
1175    check_and_filter_proposed_vote_state(
1176        vote_state,
1177        &mut tower_sync.lockouts,
1178        &mut tower_sync.root,
1179        tower_sync.hash,
1180        slot_hashes,
1181    )?;
1182    process_new_vote_state(
1183        vote_state,
1184        tower_sync
1185            .lockouts
1186            .iter()
1187            .map(|lockout| LandedVote::from(*lockout))
1188            .collect(),
1189        tower_sync.root,
1190        tower_sync.timestamp,
1191        epoch,
1192        slot,
1193        feature_set,
1194    )
1195}
1196
1197// This function is used:
1198// a. In many tests.
1199// b. In the genesis tool that initializes a cluster to create the bootstrap validator.
1200// c. In the ledger tool when creating bootstrap vote accounts.
1201pub fn create_account_with_authorized(
1202    node_pubkey: &Pubkey,
1203    authorized_voter: &Pubkey,
1204    authorized_withdrawer: &Pubkey,
1205    commission: u8,
1206    lamports: u64,
1207) -> AccountSharedData {
1208    let mut vote_account = AccountSharedData::new(lamports, VoteState::size_of(), &id());
1209
1210    let vote_state = VoteState::new(
1211        &VoteInit {
1212            node_pubkey: *node_pubkey,
1213            authorized_voter: *authorized_voter,
1214            authorized_withdrawer: *authorized_withdrawer,
1215            commission,
1216        },
1217        &Clock::default(),
1218    );
1219
1220    VoteState::serialize(
1221        &VoteStateVersions::Current(Box::new(vote_state)),
1222        vote_account.data_as_mut_slice(),
1223    )
1224    .unwrap();
1225
1226    vote_account
1227}
1228
1229// create_account() should be removed, use create_account_with_authorized() instead
1230pub fn create_account(
1231    vote_pubkey: &Pubkey,
1232    node_pubkey: &Pubkey,
1233    commission: u8,
1234    lamports: u64,
1235) -> AccountSharedData {
1236    create_account_with_authorized(node_pubkey, vote_pubkey, vote_pubkey, commission, lamports)
1237}
1238
1239#[cfg(test)]
1240mod tests {
1241    use {
1242        super::*,
1243        crate::vote_state,
1244        assert_matches::assert_matches,
1245        solana_sdk::{
1246            account::AccountSharedData, account_utils::StateMut, clock::DEFAULT_SLOTS_PER_EPOCH,
1247            hash::hash, transaction_context::InstructionAccount,
1248        },
1249        std::cell::RefCell,
1250        test_case::test_case,
1251    };
1252
1253    const MAX_RECENT_VOTES: usize = 16;
1254
1255    fn vote_state_new_for_test(auth_pubkey: &Pubkey) -> VoteState {
1256        VoteState::new(
1257            &VoteInit {
1258                node_pubkey: solana_sdk::pubkey::new_rand(),
1259                authorized_voter: *auth_pubkey,
1260                authorized_withdrawer: *auth_pubkey,
1261                commission: 0,
1262            },
1263            &Clock::default(),
1264        )
1265    }
1266
1267    fn create_test_account() -> (Pubkey, RefCell<AccountSharedData>) {
1268        let rent = Rent::default();
1269        let balance = VoteState::get_rent_exempt_reserve(&rent);
1270        let vote_pubkey = solana_sdk::pubkey::new_rand();
1271        (
1272            vote_pubkey,
1273            RefCell::new(vote_state::create_account(
1274                &vote_pubkey,
1275                &solana_sdk::pubkey::new_rand(),
1276                0,
1277                balance,
1278            )),
1279        )
1280    }
1281
1282    #[test]
1283    fn test_vote_state_upgrade_from_1_14_11() {
1284        // Create an initial vote account that is sized for the 1_14_11 version of vote state, and has only the
1285        // required lamports for rent exempt minimum at that size
1286        let node_pubkey = solana_sdk::pubkey::new_rand();
1287        let withdrawer_pubkey = solana_sdk::pubkey::new_rand();
1288        let mut vote_state = VoteState::new(
1289            &VoteInit {
1290                node_pubkey,
1291                authorized_voter: withdrawer_pubkey,
1292                authorized_withdrawer: withdrawer_pubkey,
1293                commission: 10,
1294            },
1295            &Clock::default(),
1296        );
1297        // Simulate prior epochs completed with credits and each setting a new authorized voter
1298        vote_state.increment_credits(0, 100);
1299        assert_eq!(
1300            vote_state
1301                .set_new_authorized_voter(&solana_sdk::pubkey::new_rand(), 0, 1, |_pubkey| Ok(())),
1302            Ok(())
1303        );
1304        vote_state.increment_credits(1, 200);
1305        assert_eq!(
1306            vote_state
1307                .set_new_authorized_voter(&solana_sdk::pubkey::new_rand(), 1, 2, |_pubkey| Ok(())),
1308            Ok(())
1309        );
1310        vote_state.increment_credits(2, 300);
1311        assert_eq!(
1312            vote_state
1313                .set_new_authorized_voter(&solana_sdk::pubkey::new_rand(), 2, 3, |_pubkey| Ok(())),
1314            Ok(())
1315        );
1316        // Simulate votes having occurred
1317        vec![
1318            100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
1319            117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
1320            134, 135,
1321        ]
1322        .into_iter()
1323        .for_each(|v| vote_state.process_next_vote_slot(v, 4, 0, false));
1324
1325        let version1_14_11_serialized = bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(
1326            VoteState1_14_11::from(vote_state.clone()),
1327        )))
1328        .unwrap();
1329        let version1_14_11_serialized_len = version1_14_11_serialized.len();
1330        let rent = Rent::default();
1331        let lamports = rent.minimum_balance(version1_14_11_serialized_len);
1332        let mut vote_account =
1333            AccountSharedData::new(lamports, version1_14_11_serialized_len, &id());
1334        vote_account.set_data_from_slice(&version1_14_11_serialized);
1335
1336        // Create a fake TransactionContext with a fake InstructionContext with a single account which is the
1337        // vote account that was just created
1338        let processor_account = AccountSharedData::new(0, 0, &solana_sdk::native_loader::id());
1339        let transaction_context = TransactionContext::new(
1340            vec![(id(), processor_account), (node_pubkey, vote_account)],
1341            rent.clone(),
1342            0,
1343            0,
1344        );
1345        let mut instruction_context = InstructionContext::default();
1346        instruction_context.configure(
1347            &[0],
1348            &[InstructionAccount {
1349                index_in_transaction: 1,
1350                index_in_caller: 1,
1351                index_in_callee: 0,
1352                is_signer: false,
1353                is_writable: true,
1354            }],
1355            &[],
1356        );
1357
1358        // Get the BorrowedAccount from the InstructionContext which is what is used to manipulate and inspect account
1359        // state
1360        let mut borrowed_account = instruction_context
1361            .try_borrow_instruction_account(&transaction_context, 0)
1362            .unwrap();
1363
1364        // Ensure that the vote state started out at 1_14_11
1365        let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1366        assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1367
1368        // Convert the vote state to current as would occur during vote instructions
1369        let converted_vote_state = vote_state_version.convert_to_current();
1370
1371        // Check to make sure that the vote_state is unchanged
1372        assert!(vote_state == converted_vote_state);
1373
1374        let vote_state = converted_vote_state;
1375
1376        // Now re-set the vote account state; because the feature is not enabled, the old 1_14_11 format should be
1377        // written out
1378        assert_eq!(
1379            set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1380            Ok(())
1381        );
1382        let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1383        assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1384
1385        // Convert the vote state to current as would occur during vote instructions
1386        let converted_vote_state = vote_state_version.convert_to_current();
1387
1388        // Check to make sure that the vote_state is unchanged
1389        assert_eq!(vote_state, converted_vote_state);
1390
1391        let vote_state = converted_vote_state;
1392
1393        // Test that if the vote account does not have sufficient lamports to realloc,
1394        // the old vote state is written out
1395        assert_eq!(
1396            set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1397            Ok(())
1398        );
1399        let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1400        assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1401
1402        // Convert the vote state to current as would occur during vote instructions
1403        let converted_vote_state = vote_state_version.convert_to_current();
1404
1405        // Check to make sure that the vote_state is unchanged
1406        assert_eq!(vote_state, converted_vote_state);
1407
1408        let vote_state = converted_vote_state;
1409
1410        // Test that when the feature is enabled, if the vote account does have sufficient lamports, the
1411        // new vote state is written out
1412        assert_eq!(
1413            borrowed_account.set_lamports(rent.minimum_balance(VoteState::size_of()),),
1414            Ok(())
1415        );
1416        assert_eq!(
1417            set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1418            Ok(())
1419        );
1420        let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1421        assert_matches!(vote_state_version, VoteStateVersions::Current(_));
1422
1423        // Convert the vote state to current as would occur during vote instructions
1424        let converted_vote_state = vote_state_version.convert_to_current();
1425
1426        // Check to make sure that the vote_state is unchanged
1427        assert_eq!(vote_state, converted_vote_state);
1428    }
1429
1430    #[test]
1431    fn test_vote_lockout() {
1432        let (_vote_pubkey, vote_account) = create_test_account();
1433
1434        let mut vote_state: VoteState =
1435            StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
1436                .unwrap()
1437                .convert_to_current();
1438
1439        for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
1440            process_slot_vote_unchecked(&mut vote_state, (INITIAL_LOCKOUT * i) as u64);
1441        }
1442
1443        // The last vote should have been popped b/c it reached a depth of MAX_LOCKOUT_HISTORY
1444        assert_eq!(vote_state.votes.len(), MAX_LOCKOUT_HISTORY);
1445        assert_eq!(vote_state.root_slot, Some(0));
1446        check_lockouts(&vote_state);
1447
1448        // One more vote that confirms the entire stack,
1449        // the root_slot should change to the
1450        // second vote
1451        let top_vote = vote_state.votes.front().unwrap().slot();
1452        let slot = vote_state.last_lockout().unwrap().last_locked_out_slot();
1453        process_slot_vote_unchecked(&mut vote_state, slot);
1454        assert_eq!(Some(top_vote), vote_state.root_slot);
1455
1456        // Expire everything except the first vote
1457        let slot = vote_state
1458            .votes
1459            .front()
1460            .unwrap()
1461            .lockout
1462            .last_locked_out_slot();
1463        process_slot_vote_unchecked(&mut vote_state, slot);
1464        // First vote and new vote are both stored for a total of 2 votes
1465        assert_eq!(vote_state.votes.len(), 2);
1466    }
1467
1468    #[test]
1469    fn test_update_commission() {
1470        let node_pubkey = Pubkey::new_unique();
1471        let withdrawer_pubkey = Pubkey::new_unique();
1472        let clock = Clock::default();
1473        let vote_state = VoteState::new(
1474            &VoteInit {
1475                node_pubkey,
1476                authorized_voter: withdrawer_pubkey,
1477                authorized_withdrawer: withdrawer_pubkey,
1478                commission: 10,
1479            },
1480            &clock,
1481        );
1482
1483        let serialized =
1484            bincode::serialize(&VoteStateVersions::Current(Box::new(vote_state.clone()))).unwrap();
1485        let serialized_len = serialized.len();
1486        let rent = Rent::default();
1487        let lamports = rent.minimum_balance(serialized_len);
1488        let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id());
1489        vote_account.set_data_from_slice(&serialized);
1490
1491        // Create a fake TransactionContext with a fake InstructionContext with a single account which is the
1492        // vote account that was just created
1493        let processor_account = AccountSharedData::new(0, 0, &solana_sdk::native_loader::id());
1494        let transaction_context = TransactionContext::new(
1495            vec![(id(), processor_account), (node_pubkey, vote_account)],
1496            rent,
1497            0,
1498            0,
1499        );
1500        let mut instruction_context = InstructionContext::default();
1501        instruction_context.configure(
1502            &[0],
1503            &[InstructionAccount {
1504                index_in_transaction: 1,
1505                index_in_caller: 1,
1506                index_in_callee: 0,
1507                is_signer: false,
1508                is_writable: true,
1509            }],
1510            &[],
1511        );
1512
1513        // Get the BorrowedAccount from the InstructionContext which is what is used to manipulate and inspect account
1514        // state
1515        let mut borrowed_account = instruction_context
1516            .try_borrow_instruction_account(&transaction_context, 0)
1517            .unwrap();
1518
1519        let epoch_schedule = std::sync::Arc::new(EpochSchedule::without_warmup());
1520
1521        let first_half_clock = std::sync::Arc::new(Clock {
1522            slot: epoch_schedule.slots_per_epoch / 4,
1523            ..Clock::default()
1524        });
1525
1526        let second_half_clock = std::sync::Arc::new(Clock {
1527            slot: (epoch_schedule.slots_per_epoch * 3) / 4,
1528            ..Clock::default()
1529        });
1530
1531        let mut feature_set = FeatureSet::default();
1532        feature_set.activate(
1533            &feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id(),
1534            1,
1535        );
1536
1537        let signers: HashSet<Pubkey> = vec![withdrawer_pubkey].into_iter().collect();
1538
1539        // Increase commission in first half of epoch -- allowed
1540        assert_eq!(
1541            borrowed_account
1542                .get_state::<VoteStateVersions>()
1543                .unwrap()
1544                .convert_to_current()
1545                .commission,
1546            10
1547        );
1548        assert_matches!(
1549            update_commission(
1550                &mut borrowed_account,
1551                11,
1552                &signers,
1553                &epoch_schedule,
1554                &first_half_clock,
1555                &feature_set
1556            ),
1557            Ok(())
1558        );
1559        assert_eq!(
1560            borrowed_account
1561                .get_state::<VoteStateVersions>()
1562                .unwrap()
1563                .convert_to_current()
1564                .commission,
1565            11
1566        );
1567
1568        // Increase commission in second half of epoch -- disallowed
1569        assert_matches!(
1570            update_commission(
1571                &mut borrowed_account,
1572                12,
1573                &signers,
1574                &epoch_schedule,
1575                &second_half_clock,
1576                &feature_set
1577            ),
1578            Err(_)
1579        );
1580        assert_eq!(
1581            borrowed_account
1582                .get_state::<VoteStateVersions>()
1583                .unwrap()
1584                .convert_to_current()
1585                .commission,
1586            11
1587        );
1588
1589        // Decrease commission in first half of epoch -- allowed
1590        assert_matches!(
1591            update_commission(
1592                &mut borrowed_account,
1593                10,
1594                &signers,
1595                &epoch_schedule,
1596                &first_half_clock,
1597                &feature_set
1598            ),
1599            Ok(())
1600        );
1601        assert_eq!(
1602            borrowed_account
1603                .get_state::<VoteStateVersions>()
1604                .unwrap()
1605                .convert_to_current()
1606                .commission,
1607            10
1608        );
1609
1610        // Decrease commission in second half of epoch -- disallowed because feature_set does not allow it
1611        assert_matches!(
1612            update_commission(
1613                &mut borrowed_account,
1614                9,
1615                &signers,
1616                &epoch_schedule,
1617                &second_half_clock,
1618                &feature_set
1619            ),
1620            Err(_)
1621        );
1622        assert_eq!(
1623            borrowed_account
1624                .get_state::<VoteStateVersions>()
1625                .unwrap()
1626                .convert_to_current()
1627                .commission,
1628            10
1629        );
1630
1631        // Decrease commission in second half of epoch -- allowed because feature_set allows it
1632        feature_set.activate(&feature_set::allow_commission_decrease_at_any_time::id(), 1);
1633        assert_matches!(
1634            update_commission(
1635                &mut borrowed_account,
1636                9,
1637                &signers,
1638                &epoch_schedule,
1639                &second_half_clock,
1640                &feature_set
1641            ),
1642            Ok(())
1643        );
1644        assert_eq!(
1645            borrowed_account
1646                .get_state::<VoteStateVersions>()
1647                .unwrap()
1648                .convert_to_current()
1649                .commission,
1650            9
1651        );
1652    }
1653
1654    #[test]
1655    fn test_vote_double_lockout_after_expiration() {
1656        let voter_pubkey = solana_sdk::pubkey::new_rand();
1657        let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1658
1659        for i in 0..3 {
1660            process_slot_vote_unchecked(&mut vote_state, i as u64);
1661        }
1662
1663        check_lockouts(&vote_state);
1664
1665        // Expire the third vote (which was a vote for slot 2). The height of the
1666        // vote stack is unchanged, so none of the previous votes should have
1667        // doubled in lockout
1668        process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 1) as u64);
1669        check_lockouts(&vote_state);
1670
1671        // Vote again, this time the vote stack depth increases, so the votes should
1672        // double for everybody
1673        process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 2) as u64);
1674        check_lockouts(&vote_state);
1675
1676        // Vote again, this time the vote stack depth increases, so the votes should
1677        // double for everybody
1678        process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 3) as u64);
1679        check_lockouts(&vote_state);
1680    }
1681
1682    #[test]
1683    fn test_expire_multiple_votes() {
1684        let voter_pubkey = solana_sdk::pubkey::new_rand();
1685        let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1686
1687        for i in 0..3 {
1688            process_slot_vote_unchecked(&mut vote_state, i as u64);
1689        }
1690
1691        assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1692
1693        // Expire the second and third votes
1694        let expire_slot = vote_state.votes[1].slot() + vote_state.votes[1].lockout.lockout() + 1;
1695        process_slot_vote_unchecked(&mut vote_state, expire_slot);
1696        assert_eq!(vote_state.votes.len(), 2);
1697
1698        // Check that the old votes expired
1699        assert_eq!(vote_state.votes[0].slot(), 0);
1700        assert_eq!(vote_state.votes[1].slot(), expire_slot);
1701
1702        // Process one more vote
1703        process_slot_vote_unchecked(&mut vote_state, expire_slot + 1);
1704
1705        // Confirmation count for the older first vote should remain unchanged
1706        assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1707
1708        // The later votes should still have increasing confirmation counts
1709        assert_eq!(vote_state.votes[1].confirmation_count(), 2);
1710        assert_eq!(vote_state.votes[2].confirmation_count(), 1);
1711    }
1712
1713    #[test]
1714    fn test_vote_credits() {
1715        let voter_pubkey = solana_sdk::pubkey::new_rand();
1716        let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1717
1718        for i in 0..MAX_LOCKOUT_HISTORY {
1719            process_slot_vote_unchecked(&mut vote_state, i as u64);
1720        }
1721
1722        assert_eq!(vote_state.credits(), 0);
1723
1724        process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 1);
1725        assert_eq!(vote_state.credits(), 1);
1726        process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 2);
1727        assert_eq!(vote_state.credits(), 2);
1728        process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 3);
1729        assert_eq!(vote_state.credits(), 3);
1730    }
1731
1732    #[test]
1733    fn test_duplicate_vote() {
1734        let voter_pubkey = solana_sdk::pubkey::new_rand();
1735        let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1736        process_slot_vote_unchecked(&mut vote_state, 0);
1737        process_slot_vote_unchecked(&mut vote_state, 1);
1738        process_slot_vote_unchecked(&mut vote_state, 0);
1739        assert_eq!(vote_state.nth_recent_lockout(0).unwrap().slot(), 1);
1740        assert_eq!(vote_state.nth_recent_lockout(1).unwrap().slot(), 0);
1741        assert!(vote_state.nth_recent_lockout(2).is_none());
1742    }
1743
1744    #[test]
1745    fn test_nth_recent_lockout() {
1746        let voter_pubkey = solana_sdk::pubkey::new_rand();
1747        let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1748        for i in 0..MAX_LOCKOUT_HISTORY {
1749            process_slot_vote_unchecked(&mut vote_state, i as u64);
1750        }
1751        for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
1752            assert_eq!(
1753                vote_state.nth_recent_lockout(i).unwrap().slot() as usize,
1754                MAX_LOCKOUT_HISTORY - i - 1,
1755            );
1756        }
1757        assert!(vote_state.nth_recent_lockout(MAX_LOCKOUT_HISTORY).is_none());
1758    }
1759
1760    fn check_lockouts(vote_state: &VoteState) {
1761        for (i, vote) in vote_state.votes.iter().enumerate() {
1762            let num_votes = vote_state
1763                .votes
1764                .len()
1765                .checked_sub(i)
1766                .expect("`i` is less than `vote_state.votes.len()`");
1767            assert_eq!(
1768                vote.lockout.lockout(),
1769                INITIAL_LOCKOUT.pow(num_votes as u32) as u64
1770            );
1771        }
1772    }
1773
1774    fn recent_votes(vote_state: &VoteState) -> Vec<Vote> {
1775        let start = vote_state.votes.len().saturating_sub(MAX_RECENT_VOTES);
1776        (start..vote_state.votes.len())
1777            .map(|i| {
1778                Vote::new(
1779                    vec![vote_state.votes.get(i).unwrap().slot()],
1780                    Hash::default(),
1781                )
1782            })
1783            .collect()
1784    }
1785
1786    /// check that two accounts with different data can be brought to the same state with one vote submission
1787    #[test]
1788    fn test_process_missed_votes() {
1789        let account_a = solana_sdk::pubkey::new_rand();
1790        let mut vote_state_a = vote_state_new_for_test(&account_a);
1791        let account_b = solana_sdk::pubkey::new_rand();
1792        let mut vote_state_b = vote_state_new_for_test(&account_b);
1793
1794        // process some votes on account a
1795        (0..5).for_each(|i| process_slot_vote_unchecked(&mut vote_state_a, i as u64));
1796        assert_ne!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1797
1798        // as long as b has missed less than "NUM_RECENT" votes both accounts should be in sync
1799        let slots = (0u64..MAX_RECENT_VOTES as u64).collect();
1800        let vote = Vote::new(slots, Hash::default());
1801        let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
1802
1803        assert_eq!(
1804            process_vote(&mut vote_state_a, &vote, &slot_hashes, 0, 0, true),
1805            Ok(())
1806        );
1807        assert_eq!(
1808            process_vote(&mut vote_state_b, &vote, &slot_hashes, 0, 0, true),
1809            Ok(())
1810        );
1811        assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1812    }
1813
1814    #[test]
1815    fn test_process_vote_skips_old_vote() {
1816        let mut vote_state = VoteState::default();
1817
1818        let vote = Vote::new(vec![0], Hash::default());
1819        let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1820        assert_eq!(
1821            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
1822            Ok(())
1823        );
1824        let recent = recent_votes(&vote_state);
1825        assert_eq!(
1826            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
1827            Err(VoteError::VoteTooOld)
1828        );
1829        assert_eq!(recent, recent_votes(&vote_state));
1830    }
1831
1832    #[test]
1833    fn test_check_slots_are_valid_vote_empty_slot_hashes() {
1834        let vote_state = VoteState::default();
1835
1836        let vote = Vote::new(vec![0], Hash::default());
1837        assert_eq!(
1838            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &[]),
1839            Err(VoteError::VoteTooOld)
1840        );
1841    }
1842
1843    #[test]
1844    fn test_check_slots_are_valid_new_vote() {
1845        let vote_state = VoteState::default();
1846
1847        let vote = Vote::new(vec![0], Hash::default());
1848        let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1849        assert_eq!(
1850            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1851            Ok(())
1852        );
1853    }
1854
1855    #[test]
1856    fn test_check_slots_are_valid_bad_hash() {
1857        let vote_state = VoteState::default();
1858
1859        let vote = Vote::new(vec![0], Hash::default());
1860        let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
1861        assert_eq!(
1862            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1863            Err(VoteError::SlotHashMismatch)
1864        );
1865    }
1866
1867    #[test]
1868    fn test_check_slots_are_valid_bad_slot() {
1869        let vote_state = VoteState::default();
1870
1871        let vote = Vote::new(vec![1], Hash::default());
1872        let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1873        assert_eq!(
1874            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1875            Err(VoteError::SlotsMismatch)
1876        );
1877    }
1878
1879    #[test]
1880    fn test_check_slots_are_valid_duplicate_vote() {
1881        let mut vote_state = VoteState::default();
1882
1883        let vote = Vote::new(vec![0], Hash::default());
1884        let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1885        assert_eq!(
1886            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
1887            Ok(())
1888        );
1889        assert_eq!(
1890            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1891            Err(VoteError::VoteTooOld)
1892        );
1893    }
1894
1895    #[test]
1896    fn test_check_slots_are_valid_next_vote() {
1897        let mut vote_state = VoteState::default();
1898
1899        let vote = Vote::new(vec![0], Hash::default());
1900        let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1901        assert_eq!(
1902            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
1903            Ok(())
1904        );
1905
1906        let vote = Vote::new(vec![0, 1], Hash::default());
1907        let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1908        assert_eq!(
1909            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1910            Ok(())
1911        );
1912    }
1913
1914    #[test]
1915    fn test_check_slots_are_valid_next_vote_only() {
1916        let mut vote_state = VoteState::default();
1917
1918        let vote = Vote::new(vec![0], Hash::default());
1919        let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1920        assert_eq!(
1921            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
1922            Ok(())
1923        );
1924
1925        let vote = Vote::new(vec![1], Hash::default());
1926        let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1927        assert_eq!(
1928            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1929            Ok(())
1930        );
1931    }
1932    #[test]
1933    fn test_process_vote_empty_slots() {
1934        let mut vote_state = VoteState::default();
1935
1936        let vote = Vote::new(vec![], Hash::default());
1937        assert_eq!(
1938            process_vote(&mut vote_state, &vote, &[], 0, 0, true),
1939            Err(VoteError::EmptySlots)
1940        );
1941    }
1942
1943    pub fn process_new_vote_state_from_lockouts(
1944        vote_state: &mut VoteState,
1945        new_state: VecDeque<Lockout>,
1946        new_root: Option<Slot>,
1947        timestamp: Option<i64>,
1948        epoch: Epoch,
1949        feature_set: Option<&FeatureSet>,
1950    ) -> Result<(), VoteError> {
1951        process_new_vote_state(
1952            vote_state,
1953            new_state.into_iter().map(LandedVote::from).collect(),
1954            new_root,
1955            timestamp,
1956            epoch,
1957            0,
1958            feature_set,
1959        )
1960    }
1961
1962    // Test vote credit updates after "one credit per slot" feature is enabled
1963    #[test]
1964    fn test_vote_state_update_increment_credits() {
1965        // Create a new Votestate
1966        let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
1967
1968        // Test data: a sequence of groups of votes to simulate having been cast, after each group a vote
1969        // state update is compared to "normal" vote processing to ensure that credits are earned equally
1970        let test_vote_groups: Vec<Vec<Slot>> = vec![
1971            // Initial set of votes that don't dequeue any slots, so no credits earned
1972            vec![1, 2, 3, 4, 5, 6, 7, 8],
1973            vec![
1974                9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1975                30, 31,
1976            ],
1977            // Now a single vote which should result in the first root and first credit earned
1978            vec![32],
1979            // Now another vote, should earn one credit
1980            vec![33],
1981            // Two votes in sequence
1982            vec![34, 35],
1983            // 3 votes in sequence
1984            vec![36, 37, 38],
1985            // 30 votes in sequence
1986            vec![
1987                39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
1988                60, 61, 62, 63, 64, 65, 66, 67, 68,
1989            ],
1990            // 31 votes in sequence
1991            vec![
1992                69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
1993                90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1994            ],
1995            // Votes with expiry
1996            vec![100, 101, 106, 107, 112, 116, 120, 121, 122, 124],
1997            // More votes with expiry of a large number of votes
1998            vec![200, 201],
1999            vec![
2000                202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
2001                218, 219, 220, 221, 222, 223, 224, 225, 226,
2002            ],
2003            vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
2004        ];
2005
2006        let feature_set = FeatureSet::default();
2007
2008        for vote_group in test_vote_groups {
2009            // Duplicate vote_state so that the new vote can be applied
2010            let mut vote_state_after_vote = vote_state.clone();
2011
2012            process_vote_unchecked(
2013                &mut vote_state_after_vote,
2014                Vote {
2015                    slots: vote_group.clone(),
2016                    hash: Hash::new_unique(),
2017                    timestamp: None,
2018                },
2019            )
2020            .unwrap();
2021
2022            // Now use the resulting new vote state to perform a vote state update on vote_state
2023            assert_eq!(
2024                process_new_vote_state(
2025                    &mut vote_state,
2026                    vote_state_after_vote.votes,
2027                    vote_state_after_vote.root_slot,
2028                    None,
2029                    0,
2030                    0,
2031                    Some(&feature_set)
2032                ),
2033                Ok(())
2034            );
2035
2036            // And ensure that the credits earned were the same
2037            assert_eq!(
2038                vote_state.epoch_credits,
2039                vote_state_after_vote.epoch_credits
2040            );
2041        }
2042    }
2043
2044    // Test vote credit updates after "timely vote credits" feature is enabled
2045    #[test]
2046    fn test_timely_credits() {
2047        // Each of the following (Vec<Slot>, Slot, u32) tuples gives a set of slots to cast votes on, a slot in which
2048        // the vote was cast, and the number of credits that should have been earned by the vote account after this
2049        // and all prior votes were cast.
2050        let test_vote_groups: Vec<(Vec<Slot>, Slot, u32)> = vec![
2051            // Initial set of votes that don't dequeue any slots, so no credits earned
2052            (
2053                vec![1, 2, 3, 4, 5, 6, 7, 8],
2054                9,
2055                // root: none, no credits earned
2056                0,
2057            ),
2058            (
2059                vec![
2060                    9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
2061                    29, 30, 31,
2062                ],
2063                34,
2064                // lockouts full
2065                // root: none, no credits earned
2066                0,
2067            ),
2068            // Now a single vote which should result in the first root and first credit earned
2069            (
2070                vec![32],
2071                35,
2072                // root: 1
2073                // when slot 1 was voted on in slot 9, it earned 10 credits
2074                10,
2075            ),
2076            // Now another vote, should earn one credit
2077            (
2078                vec![33],
2079                36,
2080                // root: 2
2081                // when slot 2 was voted on in slot 9, it earned 11 credits
2082                10 + 11, // 21
2083            ),
2084            // Two votes in sequence
2085            (
2086                vec![34, 35],
2087                37,
2088                // root: 4
2089                // when slots 3 and 4 were voted on in slot 9, they earned 12 and 13 credits
2090                21 + 12 + 13, // 46
2091            ),
2092            // 3 votes in sequence
2093            (
2094                vec![36, 37, 38],
2095                39,
2096                // root: 7
2097                // slots 5, 6, and 7 earned 14, 15, and 16 credits when voted in slot 9
2098                46 + 14 + 15 + 16, // 91
2099            ),
2100            (
2101                // 30 votes in sequence
2102                vec![
2103                    39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
2104                    58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
2105                ],
2106                69,
2107                // root: 37
2108                // slot 8 was voted in slot 9, earning 16 credits
2109                // slots 9 - 25 earned 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, and 9 credits when voted in
2110                //   slot 34
2111                // slot 26, 27, 28, 29, 30, 31 earned 10, 11, 12, 13, 14, 15 credits when voted in slot 34
2112                // slot 32 earned 15 credits when voted in slot 35
2113                // slot 33 earned 15 credits when voted in slot 36
2114                // slot 34 and 35 earned 15 and 16 credits when voted in slot 37
2115                // slot 36 and 37 earned 15 and 16 credits when voted in slot 39
2116                91 + 16
2117                    + 9 // * 1
2118                    + 2
2119                    + 3
2120                    + 4
2121                    + 5
2122                    + 6
2123                    + 7
2124                    + 8
2125                    + 9
2126                    + 10
2127                    + 11
2128                    + 12
2129                    + 13
2130                    + 14
2131                    + 15
2132                    + 15
2133                    + 15
2134                    + 15
2135                    + 16
2136                    + 15
2137                    + 16, // 327
2138            ),
2139            // 31 votes in sequence
2140            (
2141                vec![
2142                    69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
2143                    89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
2144                ],
2145                100,
2146                // root: 68
2147                // slot 38 earned 16 credits when voted in slot 39
2148                // slot 39 - 60 earned 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, and 9 credits
2149                //   when voted in slot 69
2150                // slot 61, 62, 63, 64, 65, 66, 67, 68 earned 10, 11, 12, 13, 14, 15, 16, and 16 credits when
2151                //   voted in slot 69
2152                327 + 16
2153                    + 14 // * 1
2154                    + 2
2155                    + 3
2156                    + 4
2157                    + 5
2158                    + 6
2159                    + 7
2160                    + 8
2161                    + 9
2162                    + 10
2163                    + 11
2164                    + 12
2165                    + 13
2166                    + 14
2167                    + 15
2168                    + 16
2169                    + 16, // 508
2170            ),
2171            // Votes with expiry
2172            (
2173                vec![115, 116, 117, 118, 119, 120, 121, 122, 123, 124],
2174                130,
2175                // root: 74
2176                // slots 96 - 114 expire
2177                // slots 69 - 74 earned 1 credit when voted in slot 100
2178                508 + ((74 - 69) + 1), // 514
2179            ),
2180            // More votes with expiry of a large number of votes
2181            (
2182                vec![200, 201],
2183                202,
2184                // root: 74
2185                // slots 119 - 124 expire
2186                514,
2187            ),
2188            (
2189                vec![
2190                    202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
2191                    218, 219, 220, 221, 222, 223, 224, 225, 226,
2192                ],
2193                227,
2194                // root: 95
2195                // slot 75 - 91 earned 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, and 9 credits when voted in
2196                //   slot 100
2197                // slot 92, 93, 94, 95 earned 10, 11, 12, 13, credits when voted in slot 100
2198                514 + 9 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13, // 613
2199            ),
2200            (
2201                vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
2202                237,
2203                // root: 205
2204                // slot 115 - 118 earned 3, 4, 5, and 6 credits when voted in slot 130
2205                // slot 200 and 201 earned 16 credits when voted in slot 202
2206                // slots 202 - 205 earned 1 credit when voted in slot 227
2207                613 + 3 + 4 + 5 + 6 + 16 + 16 + 1 + 1 + 1 + 1, // 667
2208            ),
2209        ];
2210
2211        let mut feature_set = FeatureSet::default();
2212        feature_set.activate(&feature_set::timely_vote_credits::id(), 1);
2213
2214        // For each vote group, process all vote groups leading up to it and it itself, and ensure that the number of
2215        // credits earned is correct for both regular votes and vote state updates
2216        for i in 0..test_vote_groups.len() {
2217            // Create a new VoteState for vote transaction
2218            let mut vote_state_1 = VoteState::new(&VoteInit::default(), &Clock::default());
2219            // Create a new VoteState for vote state update transaction
2220            let mut vote_state_2 = VoteState::new(&VoteInit::default(), &Clock::default());
2221            test_vote_groups.iter().take(i + 1).for_each(|vote_group| {
2222                let vote = Vote {
2223                    slots: vote_group.0.clone(), //vote_group.0 is the set of slots to cast votes on
2224                    hash: Hash::new_unique(),
2225                    timestamp: None,
2226                };
2227                let slot_hashes: Vec<_> =
2228                    vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
2229                assert_eq!(
2230                    process_vote(
2231                        &mut vote_state_1,
2232                        &vote,
2233                        &slot_hashes,
2234                        0,
2235                        vote_group.1, // vote_group.1 is the slot in which the vote was cast
2236                        true,
2237                    ),
2238                    Ok(())
2239                );
2240
2241                assert_eq!(
2242                    process_new_vote_state(
2243                        &mut vote_state_2,
2244                        vote_state_1.votes.clone(),
2245                        vote_state_1.root_slot,
2246                        None,
2247                        0,
2248                        vote_group.1, // vote_group.1 is the slot in which the vote was cast
2249                        Some(&feature_set)
2250                    ),
2251                    Ok(())
2252                );
2253            });
2254
2255            // Ensure that the credits earned is correct for both vote states
2256            let vote_group = &test_vote_groups[i];
2257            assert_eq!(vote_state_1.credits(), vote_group.2 as u64); // vote_group.2 is the expected number of credits
2258            assert_eq!(vote_state_2.credits(), vote_group.2 as u64); // vote_group.2 is the expected number of credits
2259        }
2260    }
2261
2262    #[test]
2263    fn test_retroactive_voting_timely_credits() {
2264        // Each of the following (Vec<(Slot, int)>, Slot, Option<Slot>, u32) tuples gives the following data:
2265        // Vec<(Slot, int)> -- the set of slots and confirmation_counts that is the proposed vote state
2266        // Slot -- the slot in which the proposed vote state landed
2267        // Option<Slot> -- the root after processing the proposed vote state
2268        // u32 -- the credits after processing the proposed vote state
2269        #[allow(clippy::type_complexity)]
2270        let test_vote_state_updates: Vec<(Vec<(Slot, u32)>, Slot, Option<Slot>, u32)> = vec![
2271            // proposed vote state to set initial vote state
2272            (
2273                vec![(7, 4), (8, 3), (9, 2), (10, 1)],
2274                11,
2275                // root: none
2276                None,
2277                // no credits earned
2278                0,
2279            ),
2280            // proposed vote state to include the missing slots *prior to previously included slots*
2281            (
2282                vec![
2283                    (1, 10),
2284                    (2, 9),
2285                    (3, 8),
2286                    (4, 7),
2287                    (5, 6),
2288                    (6, 5),
2289                    (7, 4),
2290                    (8, 3),
2291                    (9, 2),
2292                    (10, 1),
2293                ],
2294                12,
2295                // root: none
2296                None,
2297                // no credits earned
2298                0,
2299            ),
2300            // Now a single proposed vote state which roots all of the slots from 1 - 10
2301            (
2302                vec![
2303                    (11, 31),
2304                    (12, 30),
2305                    (13, 29),
2306                    (14, 28),
2307                    (15, 27),
2308                    (16, 26),
2309                    (17, 25),
2310                    (18, 24),
2311                    (19, 23),
2312                    (20, 22),
2313                    (21, 21),
2314                    (22, 20),
2315                    (23, 19),
2316                    (24, 18),
2317                    (25, 17),
2318                    (26, 16),
2319                    (27, 15),
2320                    (28, 14),
2321                    (29, 13),
2322                    (30, 12),
2323                    (31, 11),
2324                    (32, 10),
2325                    (33, 9),
2326                    (34, 8),
2327                    (35, 7),
2328                    (36, 6),
2329                    (37, 5),
2330                    (38, 4),
2331                    (39, 3),
2332                    (40, 2),
2333                    (41, 1),
2334                ],
2335                42,
2336                // root: 10
2337                Some(10),
2338                // when slots 1 - 6 were voted on in slot 12, they earned 7, 8, 9, 10, 11, and 12 credits
2339                // when slots 7 - 10 were voted on in slot 11, they earned 14, 15, 16, and 16 credits
2340                7 + 8 + 9 + 10 + 11 + 12 + 14 + 15 + 16 + 16,
2341            ),
2342        ];
2343
2344        let mut feature_set = FeatureSet::default();
2345        feature_set.activate(&feature_set::timely_vote_credits::id(), 1);
2346
2347        // Initial vote state
2348        let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
2349
2350        // Process the vote state updates in sequence and ensure that the credits earned after each is processed is
2351        // correct
2352        test_vote_state_updates
2353            .iter()
2354            .for_each(|proposed_vote_state| {
2355                let new_state = proposed_vote_state
2356                    .0 // proposed_vote_state.0 is the set of slots and confirmation_counts that is the proposed vote state
2357                    .iter()
2358                    .map(|(slot, confirmation_count)| LandedVote {
2359                        latency: 0,
2360                        lockout: Lockout::new_with_confirmation_count(*slot, *confirmation_count),
2361                    })
2362                    .collect::<VecDeque<LandedVote>>();
2363                assert_eq!(
2364                    process_new_vote_state(
2365                        &mut vote_state,
2366                        new_state,
2367                        proposed_vote_state.2, // proposed_vote_state.2 is root after processing the proposed vote state
2368                        None,
2369                        0,
2370                        proposed_vote_state.1, // proposed_vote_state.1 is the slot in which the proposed vote state was applied
2371                        Some(&feature_set)
2372                    ),
2373                    Ok(())
2374                );
2375
2376                // Ensure that the credits earned is correct
2377                assert_eq!(vote_state.credits(), proposed_vote_state.3 as u64);
2378            });
2379    }
2380
2381    #[test]
2382    fn test_process_new_vote_too_many_votes() {
2383        let mut vote_state1 = VoteState::default();
2384        let bad_votes: VecDeque<Lockout> = (0..=MAX_LOCKOUT_HISTORY)
2385            .map(|slot| {
2386                Lockout::new_with_confirmation_count(
2387                    slot as Slot,
2388                    (MAX_LOCKOUT_HISTORY - slot + 1) as u32,
2389                )
2390            })
2391            .collect();
2392
2393        let current_epoch = vote_state1.current_epoch();
2394        assert_eq!(
2395            process_new_vote_state_from_lockouts(
2396                &mut vote_state1,
2397                bad_votes,
2398                None,
2399                None,
2400                current_epoch,
2401                None
2402            ),
2403            Err(VoteError::TooManyVotes)
2404        );
2405    }
2406
2407    #[test]
2408    fn test_process_new_vote_state_root_rollback() {
2409        let mut vote_state1 = VoteState::default();
2410        for i in 0..MAX_LOCKOUT_HISTORY + 2 {
2411            process_slot_vote_unchecked(&mut vote_state1, i as Slot);
2412        }
2413        assert_eq!(vote_state1.root_slot.unwrap(), 1);
2414
2415        // Update vote_state2 with a higher slot so that `process_new_vote_state`
2416        // doesn't panic.
2417        let mut vote_state2 = vote_state1.clone();
2418        process_slot_vote_unchecked(&mut vote_state2, MAX_LOCKOUT_HISTORY as Slot + 3);
2419
2420        // Trying to set a lesser root should error
2421        let lesser_root = Some(0);
2422
2423        let current_epoch = vote_state2.current_epoch();
2424        assert_eq!(
2425            process_new_vote_state(
2426                &mut vote_state1,
2427                vote_state2.votes.clone(),
2428                lesser_root,
2429                None,
2430                current_epoch,
2431                0,
2432                None,
2433            ),
2434            Err(VoteError::RootRollBack)
2435        );
2436
2437        // Trying to set root to None should error
2438        let none_root = None;
2439        assert_eq!(
2440            process_new_vote_state(
2441                &mut vote_state1,
2442                vote_state2.votes.clone(),
2443                none_root,
2444                None,
2445                current_epoch,
2446                0,
2447                None,
2448            ),
2449            Err(VoteError::RootRollBack)
2450        );
2451    }
2452
2453    fn process_new_vote_state_replaced_root_vote_credits(
2454        feature_set: &FeatureSet,
2455        expected_credits: u64,
2456    ) {
2457        let mut vote_state1 = VoteState::default();
2458
2459        // Initial vote state: as if 31 votes had occurred on slots 0 - 30 (inclusive)
2460        assert_eq!(
2461            process_new_vote_state_from_lockouts(
2462                &mut vote_state1,
2463                (0..MAX_LOCKOUT_HISTORY)
2464                    .enumerate()
2465                    .map(|(index, slot)| Lockout::new_with_confirmation_count(
2466                        slot as Slot,
2467                        (MAX_LOCKOUT_HISTORY.checked_sub(index).unwrap()) as u32
2468                    ))
2469                    .collect(),
2470                None,
2471                None,
2472                0,
2473                Some(feature_set),
2474            ),
2475            Ok(())
2476        );
2477
2478        // Now vote as if new votes on slots 31 and 32 had occurred, yielding a new Root of 1
2479        assert_eq!(
2480            process_new_vote_state_from_lockouts(
2481                &mut vote_state1,
2482                (2..(MAX_LOCKOUT_HISTORY.checked_add(2).unwrap()))
2483                    .enumerate()
2484                    .map(|(index, slot)| Lockout::new_with_confirmation_count(
2485                        slot as Slot,
2486                        (MAX_LOCKOUT_HISTORY.checked_sub(index).unwrap()) as u32
2487                    ))
2488                    .collect(),
2489                Some(1),
2490                None,
2491                0,
2492                Some(feature_set),
2493            ),
2494            Ok(())
2495        );
2496
2497        // Vote credits should be 2, since two voted-on slots were "popped off the back" of the tower
2498        assert_eq!(vote_state1.credits(), 2);
2499
2500        // Create a new vote state that represents the validator having not voted for a long time, then voting on
2501        // slots 10001 through 10032 (inclusive) with an entirely new root of 10000 that was never previously voted
2502        // on.  This is valid because a vote state can include a root that it never voted on (if it votes after a very
2503        // long delinquency, the new votes will have a root much newer than its most recently voted slot).
2504        assert_eq!(
2505            process_new_vote_state_from_lockouts(
2506                &mut vote_state1,
2507                (10001..(MAX_LOCKOUT_HISTORY.checked_add(10001).unwrap()))
2508                    .enumerate()
2509                    .map(|(index, slot)| Lockout::new_with_confirmation_count(
2510                        slot as Slot,
2511                        (MAX_LOCKOUT_HISTORY.checked_sub(index).unwrap()) as u32
2512                    ))
2513                    .collect(),
2514                Some(10000),
2515                None,
2516                0,
2517                Some(feature_set),
2518            ),
2519            Ok(())
2520        );
2521
2522        // The vote is valid, but no vote credits should be awarded because although there is a new root, it does not
2523        // represent a slot previously voted on.
2524        assert_eq!(vote_state1.credits(), expected_credits)
2525    }
2526
2527    #[test]
2528    fn test_process_new_vote_state_replaced_root_vote_credits() {
2529        let mut feature_set = FeatureSet::default();
2530
2531        // Test without the timely_vote_credits feature.  The expected credits here of 34 is *incorrect* but is what
2532        // is expected using vote_state_update_credit_per_dequeue.  With this feature, the credits earned will be
2533        // calculated as:
2534        // 2 (from initial vote state)
2535        // + 31 (for votes which were "popped off of the back of the tower" by the new vote
2536        // + 1 (just because there is a new root, even though it was never voted on -- this is the flaw)
2537        feature_set.activate(&feature_set::vote_state_update_credit_per_dequeue::id(), 1);
2538        process_new_vote_state_replaced_root_vote_credits(&feature_set, 34);
2539
2540        // Now test using the timely_vote_credits feature.  The expected credits here of 33 is *correct*.  With
2541        // this feature, the credits earned will be calculated as:
2542        // 2 (from initial vote state)
2543        // + 31 (for votes which were "popped off of the back of the tower" by the new vote)
2544        feature_set.activate(&feature_set::timely_vote_credits::id(), 1);
2545        process_new_vote_state_replaced_root_vote_credits(&feature_set, 33);
2546    }
2547
2548    #[test]
2549    fn test_process_new_vote_state_zero_confirmations() {
2550        let mut vote_state1 = VoteState::default();
2551        let current_epoch = vote_state1.current_epoch();
2552
2553        let bad_votes: VecDeque<Lockout> = vec![
2554            Lockout::new_with_confirmation_count(0, 0),
2555            Lockout::new_with_confirmation_count(1, 1),
2556        ]
2557        .into_iter()
2558        .collect();
2559        assert_eq!(
2560            process_new_vote_state_from_lockouts(
2561                &mut vote_state1,
2562                bad_votes,
2563                None,
2564                None,
2565                current_epoch,
2566                None
2567            ),
2568            Err(VoteError::ZeroConfirmations)
2569        );
2570
2571        let bad_votes: VecDeque<Lockout> = vec![
2572            Lockout::new_with_confirmation_count(0, 2),
2573            Lockout::new_with_confirmation_count(1, 0),
2574        ]
2575        .into_iter()
2576        .collect();
2577        assert_eq!(
2578            process_new_vote_state_from_lockouts(
2579                &mut vote_state1,
2580                bad_votes,
2581                None,
2582                None,
2583                current_epoch,
2584                None
2585            ),
2586            Err(VoteError::ZeroConfirmations)
2587        );
2588    }
2589
2590    #[test]
2591    fn test_process_new_vote_state_confirmations_too_large() {
2592        let mut vote_state1 = VoteState::default();
2593        let current_epoch = vote_state1.current_epoch();
2594
2595        let good_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2596            0,
2597            MAX_LOCKOUT_HISTORY as u32,
2598        )]
2599        .into_iter()
2600        .collect();
2601
2602        process_new_vote_state_from_lockouts(
2603            &mut vote_state1,
2604            good_votes,
2605            None,
2606            None,
2607            current_epoch,
2608            None,
2609        )
2610        .unwrap();
2611
2612        let mut vote_state1 = VoteState::default();
2613        let bad_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2614            0,
2615            MAX_LOCKOUT_HISTORY as u32 + 1,
2616        )]
2617        .into_iter()
2618        .collect();
2619        assert_eq!(
2620            process_new_vote_state_from_lockouts(
2621                &mut vote_state1,
2622                bad_votes,
2623                None,
2624                None,
2625                current_epoch,
2626                None
2627            ),
2628            Err(VoteError::ConfirmationTooLarge)
2629        );
2630    }
2631
2632    #[test]
2633    fn test_process_new_vote_state_slot_smaller_than_root() {
2634        let mut vote_state1 = VoteState::default();
2635        let current_epoch = vote_state1.current_epoch();
2636        let root_slot = 5;
2637
2638        let bad_votes: VecDeque<Lockout> = vec![
2639            Lockout::new_with_confirmation_count(root_slot, 2),
2640            Lockout::new_with_confirmation_count(root_slot + 1, 1),
2641        ]
2642        .into_iter()
2643        .collect();
2644        assert_eq!(
2645            process_new_vote_state_from_lockouts(
2646                &mut vote_state1,
2647                bad_votes,
2648                Some(root_slot),
2649                None,
2650                current_epoch,
2651                None,
2652            ),
2653            Err(VoteError::SlotSmallerThanRoot)
2654        );
2655
2656        let bad_votes: VecDeque<Lockout> = vec![
2657            Lockout::new_with_confirmation_count(root_slot - 1, 2),
2658            Lockout::new_with_confirmation_count(root_slot + 1, 1),
2659        ]
2660        .into_iter()
2661        .collect();
2662        assert_eq!(
2663            process_new_vote_state_from_lockouts(
2664                &mut vote_state1,
2665                bad_votes,
2666                Some(root_slot),
2667                None,
2668                current_epoch,
2669                None,
2670            ),
2671            Err(VoteError::SlotSmallerThanRoot)
2672        );
2673    }
2674
2675    #[test]
2676    fn test_process_new_vote_state_slots_not_ordered() {
2677        let mut vote_state1 = VoteState::default();
2678        let current_epoch = vote_state1.current_epoch();
2679
2680        let bad_votes: VecDeque<Lockout> = vec![
2681            Lockout::new_with_confirmation_count(1, 2),
2682            Lockout::new_with_confirmation_count(0, 1),
2683        ]
2684        .into_iter()
2685        .collect();
2686        assert_eq!(
2687            process_new_vote_state_from_lockouts(
2688                &mut vote_state1,
2689                bad_votes,
2690                None,
2691                None,
2692                current_epoch,
2693                None
2694            ),
2695            Err(VoteError::SlotsNotOrdered)
2696        );
2697
2698        let bad_votes: VecDeque<Lockout> = vec![
2699            Lockout::new_with_confirmation_count(1, 2),
2700            Lockout::new_with_confirmation_count(1, 1),
2701        ]
2702        .into_iter()
2703        .collect();
2704        assert_eq!(
2705            process_new_vote_state_from_lockouts(
2706                &mut vote_state1,
2707                bad_votes,
2708                None,
2709                None,
2710                current_epoch,
2711                None
2712            ),
2713            Err(VoteError::SlotsNotOrdered)
2714        );
2715    }
2716
2717    #[test]
2718    fn test_process_new_vote_state_confirmations_not_ordered() {
2719        let mut vote_state1 = VoteState::default();
2720        let current_epoch = vote_state1.current_epoch();
2721
2722        let bad_votes: VecDeque<Lockout> = vec![
2723            Lockout::new_with_confirmation_count(0, 1),
2724            Lockout::new_with_confirmation_count(1, 2),
2725        ]
2726        .into_iter()
2727        .collect();
2728        assert_eq!(
2729            process_new_vote_state_from_lockouts(
2730                &mut vote_state1,
2731                bad_votes,
2732                None,
2733                None,
2734                current_epoch,
2735                None
2736            ),
2737            Err(VoteError::ConfirmationsNotOrdered)
2738        );
2739
2740        let bad_votes: VecDeque<Lockout> = vec![
2741            Lockout::new_with_confirmation_count(0, 1),
2742            Lockout::new_with_confirmation_count(1, 1),
2743        ]
2744        .into_iter()
2745        .collect();
2746        assert_eq!(
2747            process_new_vote_state_from_lockouts(
2748                &mut vote_state1,
2749                bad_votes,
2750                None,
2751                None,
2752                current_epoch,
2753                None
2754            ),
2755            Err(VoteError::ConfirmationsNotOrdered)
2756        );
2757    }
2758
2759    #[test]
2760    fn test_process_new_vote_state_new_vote_state_lockout_mismatch() {
2761        let mut vote_state1 = VoteState::default();
2762        let current_epoch = vote_state1.current_epoch();
2763
2764        let bad_votes: VecDeque<Lockout> = vec![
2765            Lockout::new_with_confirmation_count(0, 2),
2766            Lockout::new_with_confirmation_count(7, 1),
2767        ]
2768        .into_iter()
2769        .collect();
2770
2771        // Slot 7 should have expired slot 0
2772        assert_eq!(
2773            process_new_vote_state_from_lockouts(
2774                &mut vote_state1,
2775                bad_votes,
2776                None,
2777                None,
2778                current_epoch,
2779                None
2780            ),
2781            Err(VoteError::NewVoteStateLockoutMismatch)
2782        );
2783    }
2784
2785    #[test]
2786    fn test_process_new_vote_state_confirmation_rollback() {
2787        let mut vote_state1 = VoteState::default();
2788        let current_epoch = vote_state1.current_epoch();
2789        let votes: VecDeque<Lockout> = vec![
2790            Lockout::new_with_confirmation_count(0, 4),
2791            Lockout::new_with_confirmation_count(1, 3),
2792        ]
2793        .into_iter()
2794        .collect();
2795        process_new_vote_state_from_lockouts(
2796            &mut vote_state1,
2797            votes,
2798            None,
2799            None,
2800            current_epoch,
2801            None,
2802        )
2803        .unwrap();
2804
2805        let votes: VecDeque<Lockout> = vec![
2806            Lockout::new_with_confirmation_count(0, 4),
2807            // Confirmation count lowered illegally
2808            Lockout::new_with_confirmation_count(1, 2),
2809            Lockout::new_with_confirmation_count(2, 1),
2810        ]
2811        .into_iter()
2812        .collect();
2813        // Should error because newer vote state should not have lower confirmation the same slot
2814        // 1
2815        assert_eq!(
2816            process_new_vote_state_from_lockouts(
2817                &mut vote_state1,
2818                votes,
2819                None,
2820                None,
2821                current_epoch,
2822                None
2823            ),
2824            Err(VoteError::ConfirmationRollBack)
2825        );
2826    }
2827
2828    #[test]
2829    fn test_process_new_vote_state_root_progress() {
2830        let mut vote_state1 = VoteState::default();
2831        for i in 0..MAX_LOCKOUT_HISTORY {
2832            process_slot_vote_unchecked(&mut vote_state1, i as u64);
2833        }
2834
2835        assert!(vote_state1.root_slot.is_none());
2836        let mut vote_state2 = vote_state1.clone();
2837
2838        // 1) Try to update `vote_state1` with no root,
2839        // to `vote_state2`, which has a new root, should succeed.
2840        //
2841        // 2) Then try to update`vote_state1` with an existing root,
2842        // to `vote_state2`, which has a newer root, which
2843        // should succeed.
2844        for new_vote in MAX_LOCKOUT_HISTORY + 1..=MAX_LOCKOUT_HISTORY + 2 {
2845            process_slot_vote_unchecked(&mut vote_state2, new_vote as Slot);
2846            assert_ne!(vote_state1.root_slot, vote_state2.root_slot);
2847
2848            process_new_vote_state(
2849                &mut vote_state1,
2850                vote_state2.votes.clone(),
2851                vote_state2.root_slot,
2852                None,
2853                vote_state2.current_epoch(),
2854                0,
2855                None,
2856            )
2857            .unwrap();
2858
2859            assert_eq!(vote_state1, vote_state2);
2860        }
2861    }
2862
2863    #[test]
2864    fn test_process_new_vote_state_same_slot_but_not_common_ancestor() {
2865        // It might be possible that during the switch from old vote instructions
2866        // to new vote instructions, new_state contains votes for slots LESS
2867        // than the current state, for instance:
2868        //
2869        // Current on-chain state: 1, 5
2870        // New state: 1, 2 (lockout: 4), 3, 5, 7
2871        //
2872        // Imagine the validator made two of these votes:
2873        // 1) The first vote {1, 2, 3} didn't land in the old state, but didn't
2874        // land on chain
2875        // 2) A second vote {1, 2, 5} was then submitted, which landed
2876        //
2877        //
2878        // 2 is not popped off in the local tower because 3 doubled the lockout.
2879        // However, 3 did not land in the on-chain state, so the vote {1, 2, 6}
2880        // will immediately pop off 2.
2881
2882        // Construct on-chain vote state
2883        let mut vote_state1 = VoteState::default();
2884        process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5]);
2885        assert_eq!(
2886            vote_state1
2887                .votes
2888                .iter()
2889                .map(|vote| vote.slot())
2890                .collect::<Vec<Slot>>(),
2891            vec![1, 5]
2892        );
2893
2894        // Construct local tower state
2895        let mut vote_state2 = VoteState::default();
2896        process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2897        assert_eq!(
2898            vote_state2
2899                .votes
2900                .iter()
2901                .map(|vote| vote.slot())
2902                .collect::<Vec<Slot>>(),
2903            vec![1, 2, 3, 5, 7]
2904        );
2905
2906        // See that on-chain vote state can update properly
2907        process_new_vote_state(
2908            &mut vote_state1,
2909            vote_state2.votes.clone(),
2910            vote_state2.root_slot,
2911            None,
2912            vote_state2.current_epoch(),
2913            0,
2914            None,
2915        )
2916        .unwrap();
2917
2918        assert_eq!(vote_state1, vote_state2);
2919    }
2920
2921    #[test]
2922    fn test_process_new_vote_state_lockout_violation() {
2923        // Construct on-chain vote state
2924        let mut vote_state1 = VoteState::default();
2925        process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 4, 5]);
2926        assert_eq!(
2927            vote_state1
2928                .votes
2929                .iter()
2930                .map(|vote| vote.slot())
2931                .collect::<Vec<Slot>>(),
2932            vec![1, 2, 4, 5]
2933        );
2934
2935        // Construct conflicting tower state. Vote 4 is missing,
2936        // but 5 should not have popped off vote 4.
2937        let mut vote_state2 = VoteState::default();
2938        process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2939        assert_eq!(
2940            vote_state2
2941                .votes
2942                .iter()
2943                .map(|vote| vote.slot())
2944                .collect::<Vec<Slot>>(),
2945            vec![1, 2, 3, 5, 7]
2946        );
2947
2948        // See that on-chain vote state can update properly
2949        assert_eq!(
2950            process_new_vote_state(
2951                &mut vote_state1,
2952                vote_state2.votes.clone(),
2953                vote_state2.root_slot,
2954                None,
2955                vote_state2.current_epoch(),
2956                0,
2957                None
2958            ),
2959            Err(VoteError::LockoutConflict)
2960        );
2961    }
2962
2963    #[test]
2964    fn test_process_new_vote_state_lockout_violation2() {
2965        // Construct on-chain vote state
2966        let mut vote_state1 = VoteState::default();
2967        process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5, 6, 7]);
2968        assert_eq!(
2969            vote_state1
2970                .votes
2971                .iter()
2972                .map(|vote| vote.slot())
2973                .collect::<Vec<Slot>>(),
2974            vec![1, 5, 6, 7]
2975        );
2976
2977        // Construct a new vote state. Violates on-chain state because 8
2978        // should not have popped off 7
2979        let mut vote_state2 = VoteState::default();
2980        process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 6, 8]);
2981        assert_eq!(
2982            vote_state2
2983                .votes
2984                .iter()
2985                .map(|vote| vote.slot())
2986                .collect::<Vec<Slot>>(),
2987            vec![1, 2, 3, 5, 6, 8]
2988        );
2989
2990        // Both vote states contain `5`, but `5` is not part of the common prefix
2991        // of both vote states. However, the violation should still be detected.
2992        assert_eq!(
2993            process_new_vote_state(
2994                &mut vote_state1,
2995                vote_state2.votes.clone(),
2996                vote_state2.root_slot,
2997                None,
2998                vote_state2.current_epoch(),
2999                0,
3000                None
3001            ),
3002            Err(VoteError::LockoutConflict)
3003        );
3004    }
3005
3006    #[test]
3007    fn test_process_new_vote_state_expired_ancestor_not_removed() {
3008        // Construct on-chain vote state
3009        let mut vote_state1 = VoteState::default();
3010        process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 3, 9]);
3011        assert_eq!(
3012            vote_state1
3013                .votes
3014                .iter()
3015                .map(|vote| vote.slot())
3016                .collect::<Vec<Slot>>(),
3017            vec![1, 9]
3018        );
3019
3020        // Example: {1: lockout 8, 9: lockout 2}, vote on 10 will not pop off 1
3021        // because 9 is not popped off yet
3022        let mut vote_state2 = vote_state1.clone();
3023        process_slot_vote_unchecked(&mut vote_state2, 10);
3024
3025        // Slot 1 has been expired by 10, but is kept alive by its descendant
3026        // 9 which has not been expired yet.
3027        assert_eq!(vote_state2.votes[0].slot(), 1);
3028        assert_eq!(vote_state2.votes[0].lockout.last_locked_out_slot(), 9);
3029        assert_eq!(
3030            vote_state2
3031                .votes
3032                .iter()
3033                .map(|vote| vote.slot())
3034                .collect::<Vec<Slot>>(),
3035            vec![1, 9, 10]
3036        );
3037
3038        // Should be able to update vote_state1
3039        process_new_vote_state(
3040            &mut vote_state1,
3041            vote_state2.votes.clone(),
3042            vote_state2.root_slot,
3043            None,
3044            vote_state2.current_epoch(),
3045            0,
3046            None,
3047        )
3048        .unwrap();
3049        assert_eq!(vote_state1, vote_state2,);
3050    }
3051
3052    #[test]
3053    fn test_process_new_vote_current_state_contains_bigger_slots() {
3054        let mut vote_state1 = VoteState::default();
3055        process_slot_votes_unchecked(&mut vote_state1, &[6, 7, 8]);
3056        assert_eq!(
3057            vote_state1
3058                .votes
3059                .iter()
3060                .map(|vote| vote.slot())
3061                .collect::<Vec<Slot>>(),
3062            vec![6, 7, 8]
3063        );
3064
3065        // Try to process something with lockout violations
3066        let bad_votes: VecDeque<Lockout> = vec![
3067            Lockout::new_with_confirmation_count(2, 5),
3068            // Slot 14 could not have popped off slot 6 yet
3069            Lockout::new_with_confirmation_count(14, 1),
3070        ]
3071        .into_iter()
3072        .collect();
3073        let root = Some(1);
3074
3075        let current_epoch = vote_state1.current_epoch();
3076        assert_eq!(
3077            process_new_vote_state_from_lockouts(
3078                &mut vote_state1,
3079                bad_votes,
3080                root,
3081                None,
3082                current_epoch,
3083                None
3084            ),
3085            Err(VoteError::LockoutConflict)
3086        );
3087
3088        let good_votes: VecDeque<LandedVote> = vec![
3089            Lockout::new_with_confirmation_count(2, 5).into(),
3090            Lockout::new_with_confirmation_count(15, 1).into(),
3091        ]
3092        .into_iter()
3093        .collect();
3094
3095        let current_epoch = vote_state1.current_epoch();
3096        process_new_vote_state(
3097            &mut vote_state1,
3098            good_votes.clone(),
3099            root,
3100            None,
3101            current_epoch,
3102            0,
3103            None,
3104        )
3105        .unwrap();
3106        assert_eq!(vote_state1.votes, good_votes);
3107    }
3108
3109    #[test]
3110    fn test_filter_old_votes() {
3111        let mut vote_state = VoteState::default();
3112        let old_vote_slot = 1;
3113        let vote = Vote::new(vec![old_vote_slot], Hash::default());
3114
3115        // Vote with all slots that are all older than the SlotHashes history should
3116        // error with `VotesTooOldAllFiltered`
3117        let slot_hashes = vec![(3, Hash::new_unique()), (2, Hash::new_unique())];
3118        assert_eq!(
3119            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true),
3120            Err(VoteError::VotesTooOldAllFiltered)
3121        );
3122
3123        // Vote with only some slots older than the SlotHashes history should
3124        // filter out those older slots
3125        let vote_slot = 2;
3126        let vote_slot_hash = slot_hashes
3127            .iter()
3128            .find(|(slot, _hash)| *slot == vote_slot)
3129            .unwrap()
3130            .1;
3131
3132        let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
3133        process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true).unwrap();
3134        assert_eq!(
3135            vote_state
3136                .votes
3137                .into_iter()
3138                .map(|vote| vote.lockout)
3139                .collect::<Vec<Lockout>>(),
3140            vec![Lockout::new_with_confirmation_count(vote_slot, 1)]
3141        );
3142    }
3143
3144    fn build_slot_hashes(slots: Vec<Slot>) -> Vec<(Slot, Hash)> {
3145        slots
3146            .iter()
3147            .rev()
3148            .map(|x| (*x, Hash::new_unique()))
3149            .collect()
3150    }
3151
3152    fn build_vote_state(vote_slots: Vec<Slot>, slot_hashes: &[(Slot, Hash)]) -> VoteState {
3153        let mut vote_state = VoteState::default();
3154
3155        if !vote_slots.is_empty() {
3156            let vote_hash = slot_hashes
3157                .iter()
3158                .find(|(slot, _hash)| slot == vote_slots.last().unwrap())
3159                .unwrap()
3160                .1;
3161            let vote = Vote::new(vote_slots, vote_hash);
3162            process_vote_unfiltered(&mut vote_state, &vote.slots, &vote, slot_hashes, 0, 0, true)
3163                .unwrap();
3164        }
3165
3166        vote_state
3167    }
3168
3169    #[test]
3170    fn test_check_and_filter_proposed_vote_state_empty() {
3171        let empty_slot_hashes = build_slot_hashes(vec![]);
3172        let empty_vote_state = build_vote_state(vec![], &empty_slot_hashes);
3173
3174        // Test with empty TowerSync, should return EmptySlots error
3175        let mut tower_sync = TowerSync::from(vec![]);
3176        assert_eq!(
3177            check_and_filter_proposed_vote_state(
3178                &empty_vote_state,
3179                &mut tower_sync.lockouts,
3180                &mut tower_sync.root,
3181                tower_sync.hash,
3182                &empty_slot_hashes
3183            ),
3184            Err(VoteError::EmptySlots),
3185        );
3186
3187        // Test with non-empty TowerSync, should return SlotsMismatch since nothing exists in SlotHashes
3188        let mut tower_sync = TowerSync::from(vec![(0, 1)]);
3189        assert_eq!(
3190            check_and_filter_proposed_vote_state(
3191                &empty_vote_state,
3192                &mut tower_sync.lockouts,
3193                &mut tower_sync.root,
3194                tower_sync.hash,
3195                &empty_slot_hashes
3196            ),
3197            Err(VoteError::SlotsMismatch),
3198        );
3199    }
3200
3201    #[test]
3202    fn test_check_and_filter_proposed_vote_state_too_old() {
3203        let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3204        let latest_vote = 4;
3205        let vote_state = build_vote_state(vec![1, 2, 3, latest_vote], &slot_hashes);
3206
3207        // Test with a vote for a slot less than the latest vote in the vote_state,
3208        // should return error `VoteTooOld`
3209        let mut tower_sync = TowerSync::from(vec![(latest_vote, 1)]);
3210        assert_eq!(
3211            check_and_filter_proposed_vote_state(
3212                &vote_state,
3213                &mut tower_sync.lockouts,
3214                &mut tower_sync.root,
3215                tower_sync.hash,
3216                &slot_hashes
3217            ),
3218            Err(VoteError::VoteTooOld),
3219        );
3220
3221        // Test with a vote state update where the latest slot `X` in the update is
3222        // 1) Less than the earliest slot in slot_hashes history, AND
3223        // 2) `X` > latest_vote
3224        let earliest_slot_in_history = latest_vote + 2;
3225        let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history]);
3226        let mut tower_sync = TowerSync::from(vec![(earliest_slot_in_history - 1, 1)]);
3227        assert_eq!(
3228            check_and_filter_proposed_vote_state(
3229                &vote_state,
3230                &mut tower_sync.lockouts,
3231                &mut tower_sync.root,
3232                tower_sync.hash,
3233                &slot_hashes
3234            ),
3235            Err(VoteError::VoteTooOld),
3236        );
3237    }
3238
3239    fn run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3240        earliest_slot_in_history: Slot,
3241        current_vote_state_slots: Vec<Slot>,
3242        current_vote_state_root: Option<Slot>,
3243        proposed_slots_and_lockouts: Vec<(Slot, u32)>,
3244        proposed_root: Slot,
3245        expected_root: Option<Slot>,
3246        expected_vote_state: Vec<Lockout>,
3247    ) {
3248        assert!(proposed_root < earliest_slot_in_history);
3249        assert_eq!(
3250            expected_root,
3251            current_vote_state_slots
3252                .iter()
3253                .rev()
3254                .find(|slot| **slot <= proposed_root)
3255                .cloned()
3256        );
3257        let latest_slot_in_history = proposed_slots_and_lockouts
3258            .last()
3259            .unwrap()
3260            .0
3261            .max(earliest_slot_in_history);
3262        let mut slot_hashes = build_slot_hashes(
3263            (current_vote_state_slots.first().copied().unwrap_or(0)..=latest_slot_in_history)
3264                .collect::<Vec<Slot>>(),
3265        );
3266
3267        let mut vote_state = build_vote_state(current_vote_state_slots, &slot_hashes);
3268        vote_state.root_slot = current_vote_state_root;
3269
3270        slot_hashes.retain(|slot| slot.0 >= earliest_slot_in_history);
3271        assert!(!proposed_slots_and_lockouts.is_empty());
3272        let proposed_hash = slot_hashes
3273            .iter()
3274            .find(|(slot, _hash)| *slot == proposed_slots_and_lockouts.last().unwrap().0)
3275            .unwrap()
3276            .1;
3277
3278        // Test with a `TowerSync` where the root is less than `earliest_slot_in_history`.
3279        // Root slot in the `TowerSync` should be updated to match the root slot in the
3280        // current vote state
3281        let mut tower_sync = TowerSync::from(proposed_slots_and_lockouts);
3282        tower_sync.hash = proposed_hash;
3283        tower_sync.root = Some(proposed_root);
3284        check_and_filter_proposed_vote_state(
3285            &vote_state,
3286            &mut tower_sync.lockouts,
3287            &mut tower_sync.root,
3288            tower_sync.hash,
3289            &slot_hashes,
3290        )
3291        .unwrap();
3292        assert_eq!(tower_sync.root, expected_root);
3293
3294        // The proposed root slot should become the biggest slot in the current vote state less than
3295        // `earliest_slot_in_history`.
3296        assert!(do_process_tower_sync(
3297            &mut vote_state,
3298            &slot_hashes,
3299            0,
3300            0,
3301            tower_sync.clone(),
3302            Some(&FeatureSet::all_enabled()),
3303        )
3304        .is_ok());
3305        assert_eq!(vote_state.root_slot, expected_root);
3306        assert_eq!(
3307            vote_state
3308                .votes
3309                .into_iter()
3310                .map(|vote| vote.lockout)
3311                .collect::<Vec<Lockout>>(),
3312            expected_vote_state,
3313        );
3314    }
3315
3316    #[test]
3317    fn test_check_and_filter_proposed_vote_state_older_than_history_root() {
3318        // Test when `proposed_root` is in `current_vote_state_slots` but it's not the latest
3319        // slot
3320        let earliest_slot_in_history = 5;
3321        let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3322        let current_vote_state_root = None;
3323        let proposed_slots_and_lockouts = vec![(5, 1)];
3324        let proposed_root = 4;
3325        let expected_root = Some(4);
3326        let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3327        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3328            earliest_slot_in_history,
3329            current_vote_state_slots,
3330            current_vote_state_root,
3331            proposed_slots_and_lockouts,
3332            proposed_root,
3333            expected_root,
3334            expected_vote_state,
3335        );
3336
3337        // Test when `proposed_root` is in `current_vote_state_slots` but it's not the latest
3338        // slot and the `current_vote_state_root.is_some()`.
3339        let earliest_slot_in_history = 5;
3340        let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3341        let current_vote_state_root = Some(0);
3342        let proposed_slots_and_lockouts = vec![(5, 1)];
3343        let proposed_root = 4;
3344        let expected_root = Some(4);
3345        let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3346        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3347            earliest_slot_in_history,
3348            current_vote_state_slots,
3349            current_vote_state_root,
3350            proposed_slots_and_lockouts,
3351            proposed_root,
3352            expected_root,
3353            expected_vote_state,
3354        );
3355
3356        // Test when `proposed_root` is in `current_vote_state_slots` but it's not the latest
3357        // slot
3358        let earliest_slot_in_history = 5;
3359        let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3360        let current_vote_state_root = Some(0);
3361        let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3362        let proposed_root = 3;
3363        let expected_root = Some(3);
3364        let expected_vote_state = vec![
3365            Lockout::new_with_confirmation_count(4, 2),
3366            Lockout::new_with_confirmation_count(5, 1),
3367        ];
3368        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3369            earliest_slot_in_history,
3370            current_vote_state_slots,
3371            current_vote_state_root,
3372            proposed_slots_and_lockouts,
3373            proposed_root,
3374            expected_root,
3375            expected_vote_state,
3376        );
3377
3378        // Test when `proposed_root` is not in `current_vote_state_slots`
3379        let earliest_slot_in_history = 5;
3380        let current_vote_state_slots: Vec<Slot> = vec![1, 2, 4];
3381        let current_vote_state_root = Some(0);
3382        let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3383        let proposed_root = 3;
3384        let expected_root = Some(2);
3385        let expected_vote_state = vec![
3386            Lockout::new_with_confirmation_count(4, 2),
3387            Lockout::new_with_confirmation_count(5, 1),
3388        ];
3389        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3390            earliest_slot_in_history,
3391            current_vote_state_slots,
3392            current_vote_state_root,
3393            proposed_slots_and_lockouts,
3394            proposed_root,
3395            expected_root,
3396            expected_vote_state,
3397        );
3398
3399        // Test when the `proposed_root` is smaller than all the slots in
3400        // `current_vote_state_slots`, no roots should be set.
3401        let earliest_slot_in_history = 4;
3402        let current_vote_state_slots: Vec<Slot> = vec![3, 4];
3403        let current_vote_state_root = None;
3404        let proposed_slots_and_lockouts = vec![(3, 3), (4, 2), (5, 1)];
3405        let proposed_root = 2;
3406        let expected_root = None;
3407        let expected_vote_state = vec![
3408            Lockout::new_with_confirmation_count(3, 3),
3409            Lockout::new_with_confirmation_count(4, 2),
3410            Lockout::new_with_confirmation_count(5, 1),
3411        ];
3412        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3413            earliest_slot_in_history,
3414            current_vote_state_slots,
3415            current_vote_state_root,
3416            proposed_slots_and_lockouts,
3417            proposed_root,
3418            expected_root,
3419            expected_vote_state,
3420        );
3421
3422        // Test when `current_vote_state_slots` is empty, no roots should be set
3423        let earliest_slot_in_history = 4;
3424        let current_vote_state_slots: Vec<Slot> = vec![];
3425        let current_vote_state_root = None;
3426        let proposed_slots_and_lockouts = vec![(5, 1)];
3427        let proposed_root = 2;
3428        let expected_root = None;
3429        let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3430        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3431            earliest_slot_in_history,
3432            current_vote_state_slots,
3433            current_vote_state_root,
3434            proposed_slots_and_lockouts,
3435            proposed_root,
3436            expected_root,
3437            expected_vote_state,
3438        );
3439    }
3440
3441    #[test]
3442    fn test_check_and_filter_proposed_vote_state_slots_not_ordered() {
3443        let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3444        let vote_state = build_vote_state(vec![1], &slot_hashes);
3445
3446        // Test with a `TowerSync` where the slots are out of order
3447        let vote_slot = 3;
3448        let vote_slot_hash = slot_hashes
3449            .iter()
3450            .find(|(slot, _hash)| *slot == vote_slot)
3451            .unwrap()
3452            .1;
3453        let mut tower_sync = TowerSync::from(vec![(2, 2), (1, 3), (vote_slot, 1)]);
3454        tower_sync.hash = vote_slot_hash;
3455        assert_eq!(
3456            check_and_filter_proposed_vote_state(
3457                &vote_state,
3458                &mut tower_sync.lockouts,
3459                &mut tower_sync.root,
3460                tower_sync.hash,
3461                &slot_hashes
3462            ),
3463            Err(VoteError::SlotsNotOrdered),
3464        );
3465
3466        // Test with a `TowerSync` where there are multiples of the same slot
3467        let mut tower_sync = TowerSync::from(vec![(2, 2), (2, 2), (vote_slot, 1)]);
3468        tower_sync.hash = vote_slot_hash;
3469        assert_eq!(
3470            check_and_filter_proposed_vote_state(
3471                &vote_state,
3472                &mut tower_sync.lockouts,
3473                &mut tower_sync.root,
3474                tower_sync.hash,
3475                &slot_hashes
3476            ),
3477            Err(VoteError::SlotsNotOrdered),
3478        );
3479    }
3480
3481    #[test]
3482    fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered() {
3483        let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3484        let mut vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes);
3485
3486        // Test with a `TowerSync` where there:
3487        // 1) Exists a slot less than `earliest_slot_in_history`
3488        // 2) This slot does not exist in the vote state already
3489        // This slot should be filtered out
3490        let earliest_slot_in_history = 11;
3491        let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3492        let vote_slot = 12;
3493        let vote_slot_hash = slot_hashes
3494            .iter()
3495            .find(|(slot, _hash)| *slot == vote_slot)
3496            .unwrap()
3497            .1;
3498        let missing_older_than_history_slot = earliest_slot_in_history - 1;
3499        let mut tower_sync = TowerSync::from(vec![
3500            (1, 4),
3501            (missing_older_than_history_slot, 2),
3502            (vote_slot, 3),
3503        ]);
3504        tower_sync.hash = vote_slot_hash;
3505        check_and_filter_proposed_vote_state(
3506            &vote_state,
3507            &mut tower_sync.lockouts,
3508            &mut tower_sync.root,
3509            tower_sync.hash,
3510            &slot_hashes,
3511        )
3512        .unwrap();
3513
3514        // Check the earlier slot was filtered out
3515        assert_eq!(
3516            tower_sync
3517                .clone()
3518                .lockouts
3519                .into_iter()
3520                .collect::<Vec<Lockout>>(),
3521            vec![
3522                Lockout::new_with_confirmation_count(1, 4),
3523                Lockout::new_with_confirmation_count(vote_slot, 3)
3524            ]
3525        );
3526        assert!(do_process_tower_sync(
3527            &mut vote_state,
3528            &slot_hashes,
3529            0,
3530            0,
3531            tower_sync,
3532            Some(&FeatureSet::all_enabled()),
3533        )
3534        .is_ok());
3535    }
3536
3537    #[test]
3538    fn test_check_and_filter_proposed_vote_state_older_than_history_slots_not_filtered() {
3539        let slot_hashes = build_slot_hashes(vec![4]);
3540        let mut vote_state = build_vote_state(vec![4], &slot_hashes);
3541
3542        // Test with a `TowerSync` where there:
3543        // 1) Exists a slot less than `earliest_slot_in_history`
3544        // 2) This slot exists in the vote state already
3545        // This slot should *NOT* be filtered out
3546        let earliest_slot_in_history = 11;
3547        let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3548        let vote_slot = 12;
3549        let vote_slot_hash = slot_hashes
3550            .iter()
3551            .find(|(slot, _hash)| *slot == vote_slot)
3552            .unwrap()
3553            .1;
3554        let existing_older_than_history_slot = 4;
3555        let mut tower_sync =
3556            TowerSync::from(vec![(existing_older_than_history_slot, 3), (vote_slot, 2)]);
3557        tower_sync.hash = vote_slot_hash;
3558        check_and_filter_proposed_vote_state(
3559            &vote_state,
3560            &mut tower_sync.lockouts,
3561            &mut tower_sync.root,
3562            tower_sync.hash,
3563            &slot_hashes,
3564        )
3565        .unwrap();
3566        // Check the earlier slot was *NOT* filtered out
3567        assert_eq!(tower_sync.lockouts.len(), 2);
3568        assert_eq!(
3569            tower_sync
3570                .clone()
3571                .lockouts
3572                .into_iter()
3573                .collect::<Vec<Lockout>>(),
3574            vec![
3575                Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3576                Lockout::new_with_confirmation_count(vote_slot, 2)
3577            ]
3578        );
3579        assert!(do_process_tower_sync(
3580            &mut vote_state,
3581            &slot_hashes,
3582            0,
3583            0,
3584            tower_sync,
3585            Some(&FeatureSet::all_enabled()),
3586        )
3587        .is_ok());
3588    }
3589
3590    #[test]
3591    fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered_and_not_filtered(
3592    ) {
3593        let slot_hashes = build_slot_hashes(vec![6]);
3594        let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3595
3596        // Test with a `TowerSync` where there exists both a slot:
3597        // 1) Less than `earliest_slot_in_history`
3598        // 2) This slot exists in the vote state already
3599        // which should not be filtered
3600        //
3601        // AND a slot that
3602        //
3603        // 1) Less than `earliest_slot_in_history`
3604        // 2) This slot does not exist in the vote state already
3605        // which should be filtered
3606        let earliest_slot_in_history = 11;
3607        let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3608        let vote_slot = 14;
3609        let vote_slot_hash = slot_hashes
3610            .iter()
3611            .find(|(slot, _hash)| *slot == vote_slot)
3612            .unwrap()
3613            .1;
3614
3615        let missing_older_than_history_slot = 4;
3616        let existing_older_than_history_slot = 6;
3617
3618        let mut tower_sync = TowerSync::from(vec![
3619            (missing_older_than_history_slot, 4),
3620            (existing_older_than_history_slot, 3),
3621            (12, 2),
3622            (vote_slot, 1),
3623        ]);
3624        tower_sync.hash = vote_slot_hash;
3625        check_and_filter_proposed_vote_state(
3626            &vote_state,
3627            &mut tower_sync.lockouts,
3628            &mut tower_sync.root,
3629            tower_sync.hash,
3630            &slot_hashes,
3631        )
3632        .unwrap();
3633        assert_eq!(tower_sync.lockouts.len(), 3);
3634        assert_eq!(
3635            tower_sync
3636                .clone()
3637                .lockouts
3638                .into_iter()
3639                .collect::<Vec<Lockout>>(),
3640            vec![
3641                Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3642                Lockout::new_with_confirmation_count(12, 2),
3643                Lockout::new_with_confirmation_count(vote_slot, 1)
3644            ]
3645        );
3646        assert!(do_process_tower_sync(
3647            &mut vote_state,
3648            &slot_hashes,
3649            0,
3650            0,
3651            tower_sync,
3652            Some(&FeatureSet::all_enabled()),
3653        )
3654        .is_ok());
3655    }
3656
3657    #[test]
3658    fn test_check_and_filter_proposed_vote_state_slot_not_on_fork() {
3659        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3660        let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3661
3662        // Test with a `TowerSync` where there:
3663        // 1) Exists a slot not in the slot hashes history
3664        // 2) The slot is greater than the earliest slot in the history
3665        // Thus this slot is not part of the fork and the update should be rejected
3666        // with error `SlotsMismatch`
3667        let missing_vote_slot = 3;
3668
3669        // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
3670        // errors
3671        let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3672        let vote_slot_hash = slot_hashes
3673            .iter()
3674            .find(|(slot, _hash)| *slot == vote_slot)
3675            .unwrap()
3676            .1;
3677        let mut tower_sync = TowerSync::from(vec![(missing_vote_slot, 2), (vote_slot, 3)]);
3678        tower_sync.hash = vote_slot_hash;
3679        assert_eq!(
3680            check_and_filter_proposed_vote_state(
3681                &vote_state,
3682                &mut tower_sync.lockouts,
3683                &mut tower_sync.root,
3684                tower_sync.hash,
3685                &slot_hashes
3686            ),
3687            Err(VoteError::SlotsMismatch),
3688        );
3689
3690        // Test where some earlier vote slots exist in the history, but others don't
3691        let missing_vote_slot = 7;
3692        let mut tower_sync = TowerSync::from(vec![
3693            (2, 5),
3694            (4, 4),
3695            (6, 3),
3696            (missing_vote_slot, 2),
3697            (vote_slot, 1),
3698        ]);
3699        tower_sync.hash = vote_slot_hash;
3700        assert_eq!(
3701            check_and_filter_proposed_vote_state(
3702                &vote_state,
3703                &mut tower_sync.lockouts,
3704                &mut tower_sync.root,
3705                tower_sync.hash,
3706                &slot_hashes
3707            ),
3708            Err(VoteError::SlotsMismatch),
3709        );
3710    }
3711
3712    #[test]
3713    fn test_check_and_filter_proposed_vote_state_root_on_different_fork() {
3714        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3715        let vote_state = build_vote_state(vec![6], &slot_hashes);
3716
3717        // Test with a `TowerSync` where:
3718        // 1) The root is not present in slot hashes history
3719        // 2) The slot is greater than the earliest slot in the history
3720        // Thus this slot is not part of the fork and the update should be rejected
3721        // with error `RootOnDifferentFork`
3722        let new_root = 3;
3723
3724        // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
3725        // errors, but also this slot must be present in SlotHashes
3726        let vote_slot = 8;
3727        assert_eq!(vote_slot, slot_hashes.first().unwrap().0);
3728        let vote_slot_hash = slot_hashes
3729            .iter()
3730            .find(|(slot, _hash)| *slot == vote_slot)
3731            .unwrap()
3732            .1;
3733        let mut tower_sync = TowerSync::from(vec![(vote_slot, 1)]);
3734        tower_sync.hash = vote_slot_hash;
3735        tower_sync.root = Some(new_root);
3736        assert_eq!(
3737            check_and_filter_proposed_vote_state(
3738                &vote_state,
3739                &mut tower_sync.lockouts,
3740                &mut tower_sync.root,
3741                tower_sync.hash,
3742                &slot_hashes
3743            ),
3744            Err(VoteError::RootOnDifferentFork),
3745        );
3746    }
3747
3748    #[test]
3749    fn test_check_and_filter_proposed_vote_state_slot_newer_than_slot_history() {
3750        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3751        let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3752
3753        // Test with a `TowerSync` where there:
3754        // 1) The last slot in the update is a slot not in the slot hashes history
3755        // 2) The slot is greater than the newest slot in the slot history
3756        // Thus this slot is not part of the fork and the update should be rejected
3757        // with error `SlotsMismatch`
3758        let missing_vote_slot = slot_hashes.first().unwrap().0 + 1;
3759        let vote_slot_hash = Hash::new_unique();
3760        let mut tower_sync = TowerSync::from(vec![(8, 2), (missing_vote_slot, 3)]);
3761        tower_sync.hash = vote_slot_hash;
3762        assert_eq!(
3763            check_and_filter_proposed_vote_state(
3764                &vote_state,
3765                &mut tower_sync.lockouts,
3766                &mut tower_sync.root,
3767                tower_sync.hash,
3768                &slot_hashes
3769            ),
3770            Err(VoteError::SlotsMismatch),
3771        );
3772    }
3773
3774    #[test]
3775    fn test_check_and_filter_proposed_vote_state_slot_all_slot_hashes_in_update_ok() {
3776        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3777        let mut vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3778
3779        // Test with a `TowerSync` where every slot in the history is
3780        // in the update
3781
3782        // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
3783        // errors
3784        let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3785        let vote_slot_hash = slot_hashes
3786            .iter()
3787            .find(|(slot, _hash)| *slot == vote_slot)
3788            .unwrap()
3789            .1;
3790        let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3791        tower_sync.hash = vote_slot_hash;
3792        check_and_filter_proposed_vote_state(
3793            &vote_state,
3794            &mut tower_sync.lockouts,
3795            &mut tower_sync.root,
3796            tower_sync.hash,
3797            &slot_hashes,
3798        )
3799        .unwrap();
3800
3801        // Nothing in the update should have been filtered out
3802        assert_eq!(
3803            tower_sync
3804                .clone()
3805                .lockouts
3806                .into_iter()
3807                .collect::<Vec<Lockout>>(),
3808            vec![
3809                Lockout::new_with_confirmation_count(2, 4),
3810                Lockout::new_with_confirmation_count(4, 3),
3811                Lockout::new_with_confirmation_count(6, 2),
3812                Lockout::new_with_confirmation_count(vote_slot, 1)
3813            ]
3814        );
3815
3816        assert!(do_process_tower_sync(
3817            &mut vote_state,
3818            &slot_hashes,
3819            0,
3820            0,
3821            tower_sync,
3822            Some(&FeatureSet::all_enabled()),
3823        )
3824        .is_ok());
3825    }
3826
3827    #[test]
3828    fn test_check_and_filter_proposed_vote_state_slot_some_slot_hashes_in_update_ok() {
3829        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3830        let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3831
3832        // Test with a `TowerSync` where only some slots in the history are
3833        // in the update, and others slots in the history are missing.
3834
3835        // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
3836        // errors
3837        let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3838        let vote_slot_hash = slot_hashes
3839            .iter()
3840            .find(|(slot, _hash)| *slot == vote_slot)
3841            .unwrap()
3842            .1;
3843        let mut tower_sync = TowerSync::from(vec![(4, 2), (vote_slot, 1)]);
3844        tower_sync.hash = vote_slot_hash;
3845        check_and_filter_proposed_vote_state(
3846            &vote_state,
3847            &mut tower_sync.lockouts,
3848            &mut tower_sync.root,
3849            tower_sync.hash,
3850            &slot_hashes,
3851        )
3852        .unwrap();
3853
3854        // Nothing in the update should have been filtered out
3855        assert_eq!(
3856            tower_sync
3857                .clone()
3858                .lockouts
3859                .into_iter()
3860                .collect::<Vec<Lockout>>(),
3861            vec![
3862                Lockout::new_with_confirmation_count(4, 2),
3863                Lockout::new_with_confirmation_count(vote_slot, 1)
3864            ]
3865        );
3866
3867        // Because 6 from the original VoteState
3868        // should not have been popped off in the proposed state,
3869        // we should get a lockout conflict
3870        assert_eq!(
3871            do_process_tower_sync(
3872                &mut vote_state,
3873                &slot_hashes,
3874                0,
3875                0,
3876                tower_sync,
3877                Some(&FeatureSet::all_enabled())
3878            ),
3879            Err(VoteError::LockoutConflict)
3880        );
3881    }
3882
3883    #[test]
3884    fn test_check_and_filter_proposed_vote_state_slot_hash_mismatch() {
3885        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3886        let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3887
3888        // Test with a `TowerSync` where the hash is mismatched
3889
3890        // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
3891        // errors
3892        let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3893        let vote_slot_hash = Hash::new_unique();
3894        let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3895        tower_sync.hash = vote_slot_hash;
3896        assert_eq!(
3897            check_and_filter_proposed_vote_state(
3898                &vote_state,
3899                &mut tower_sync.lockouts,
3900                &mut tower_sync.root,
3901                tower_sync.hash,
3902                &slot_hashes,
3903            ),
3904            Err(VoteError::SlotHashMismatch),
3905        );
3906    }
3907
3908    #[test_case(0, true; "first slot")]
3909    #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3910    #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3911    #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3912    #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3913    fn test_epoch_half_check(slot: Slot, expected_allowed: bool) {
3914        let epoch_schedule = EpochSchedule::without_warmup();
3915        assert_eq!(
3916            is_commission_update_allowed(slot, &epoch_schedule),
3917            expected_allowed
3918        );
3919    }
3920
3921    #[test]
3922    fn test_warmup_epoch_half_check_with_warmup() {
3923        let epoch_schedule = EpochSchedule::default();
3924        let first_normal_slot = epoch_schedule.first_normal_slot;
3925        // first slot works
3926        assert!(is_commission_update_allowed(0, &epoch_schedule));
3927        // right before first normal slot works, since all warmup slots allow
3928        // commission updates
3929        assert!(is_commission_update_allowed(
3930            first_normal_slot - 1,
3931            &epoch_schedule
3932        ));
3933    }
3934
3935    #[test_case(0, true; "first slot")]
3936    #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3937    #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3938    #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3939    #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3940    fn test_epoch_half_check_with_warmup(slot: Slot, expected_allowed: bool) {
3941        let epoch_schedule = EpochSchedule::default();
3942        let first_normal_slot = epoch_schedule.first_normal_slot;
3943        assert_eq!(
3944            is_commission_update_allowed(first_normal_slot.saturating_add(slot), &epoch_schedule),
3945            expected_allowed
3946        );
3947    }
3948}