solana_vote_program/vote_state/
mod.rs

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