clone_solana_vote_program/vote_state/
mod.rs

1//! Vote state, vote program
2//! Receive and processes votes from validators
3pub use clone_solana_vote_interface::state::{vote_state_versions::*, *};
4use {
5    clone_agave_feature_set::{self as feature_set, FeatureSet},
6    clone_solana_account::{AccountSharedData, ReadableAccount, WritableAccount},
7    clone_solana_clock::{Clock, Epoch, Slot, UnixTimestamp},
8    clone_solana_epoch_schedule::EpochSchedule,
9    clone_solana_hash::Hash,
10    clone_solana_instruction::error::InstructionError,
11    clone_solana_pubkey::Pubkey,
12    clone_solana_rent::Rent,
13    clone_solana_slot_hashes::SlotHash,
14    clone_solana_transaction_context::{
15        BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
16    },
17    clone_solana_vote_interface::{error::VoteError, program::id},
18    log::*,
19    serde_derive::{Deserialize, Serialize},
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        clone_solana_account::{state_traits::StateMut, AccountSharedData},
1218        clone_solana_clock::DEFAULT_SLOTS_PER_EPOCH,
1219        clone_solana_sha256_hasher::hash,
1220        clone_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: clone_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 = clone_solana_pubkey::new_rand();
1243        (
1244            vote_pubkey,
1245            RefCell::new(vote_state::create_account(
1246                &vote_pubkey,
1247                &clone_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 = clone_solana_pubkey::new_rand();
1259        let withdrawer_pubkey = clone_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(
1273                &clone_solana_pubkey::new_rand(),
1274                0,
1275                1,
1276                |_pubkey| Ok(())
1277            ),
1278            Ok(())
1279        );
1280        vote_state.increment_credits(1, 200);
1281        assert_eq!(
1282            vote_state.set_new_authorized_voter(
1283                &clone_solana_pubkey::new_rand(),
1284                1,
1285                2,
1286                |_pubkey| Ok(())
1287            ),
1288            Ok(())
1289        );
1290        vote_state.increment_credits(2, 300);
1291        assert_eq!(
1292            vote_state.set_new_authorized_voter(
1293                &clone_solana_pubkey::new_rand(),
1294                2,
1295                3,
1296                |_pubkey| Ok(())
1297            ),
1298            Ok(())
1299        );
1300        // Simulate votes having occurred
1301        vec![
1302            100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
1303            117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
1304            134, 135,
1305        ]
1306        .into_iter()
1307        .for_each(|v| vote_state.process_next_vote_slot(v, 4, 0));
1308
1309        let version1_14_11_serialized = bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(
1310            VoteState1_14_11::from(vote_state.clone()),
1311        )))
1312        .unwrap();
1313        let version1_14_11_serialized_len = version1_14_11_serialized.len();
1314        let rent = Rent::default();
1315        let lamports = rent.minimum_balance(version1_14_11_serialized_len);
1316        let mut vote_account =
1317            AccountSharedData::new(lamports, version1_14_11_serialized_len, &id());
1318        vote_account.set_data_from_slice(&version1_14_11_serialized);
1319
1320        // Create a fake TransactionContext with a fake InstructionContext with a single account which is the
1321        // vote account that was just created
1322        let processor_account =
1323            AccountSharedData::new(0, 0, &clone_solana_sdk_ids::native_loader::id());
1324        let transaction_context = TransactionContext::new(
1325            vec![(id(), processor_account), (node_pubkey, vote_account)],
1326            rent.clone(),
1327            0,
1328            0,
1329        );
1330        let mut instruction_context = InstructionContext::default();
1331        instruction_context.configure(
1332            &[0],
1333            &[InstructionAccount {
1334                index_in_transaction: 1,
1335                index_in_caller: 1,
1336                index_in_callee: 0,
1337                is_signer: false,
1338                is_writable: true,
1339            }],
1340            &[],
1341        );
1342
1343        // Get the BorrowedAccount from the InstructionContext which is what is used to manipulate and inspect account
1344        // state
1345        let mut borrowed_account = instruction_context
1346            .try_borrow_instruction_account(&transaction_context, 0)
1347            .unwrap();
1348
1349        // Ensure that the vote state started out at 1_14_11
1350        let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1351        assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1352
1353        // Convert the vote state to current as would occur during vote instructions
1354        let converted_vote_state = vote_state_version.convert_to_current();
1355
1356        // Check to make sure that the vote_state is unchanged
1357        assert!(vote_state == converted_vote_state);
1358
1359        let vote_state = converted_vote_state;
1360
1361        // Now re-set the vote account state; because the feature is not enabled, the old 1_14_11 format should be
1362        // written out
1363        assert_eq!(
1364            set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1365            Ok(())
1366        );
1367        let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1368        assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1369
1370        // Convert the vote state to current as would occur during vote instructions
1371        let converted_vote_state = vote_state_version.convert_to_current();
1372
1373        // Check to make sure that the vote_state is unchanged
1374        assert_eq!(vote_state, converted_vote_state);
1375
1376        let vote_state = converted_vote_state;
1377
1378        // Test that if the vote account does not have sufficient lamports to realloc,
1379        // the old vote state is written out
1380        assert_eq!(
1381            set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1382            Ok(())
1383        );
1384        let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1385        assert_matches!(vote_state_version, VoteStateVersions::V1_14_11(_));
1386
1387        // Convert the vote state to current as would occur during vote instructions
1388        let converted_vote_state = vote_state_version.convert_to_current();
1389
1390        // Check to make sure that the vote_state is unchanged
1391        assert_eq!(vote_state, converted_vote_state);
1392
1393        let vote_state = converted_vote_state;
1394
1395        // Test that when the feature is enabled, if the vote account does have sufficient lamports, the
1396        // new vote state is written out
1397        assert_eq!(
1398            borrowed_account.set_lamports(rent.minimum_balance(VoteState::size_of()),),
1399            Ok(())
1400        );
1401        assert_eq!(
1402            set_vote_account_state(&mut borrowed_account, vote_state.clone()),
1403            Ok(())
1404        );
1405        let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
1406        assert_matches!(vote_state_version, VoteStateVersions::Current(_));
1407
1408        // Convert the vote state to current as would occur during vote instructions
1409        let converted_vote_state = vote_state_version.convert_to_current();
1410
1411        // Check to make sure that the vote_state is unchanged
1412        assert_eq!(vote_state, converted_vote_state);
1413    }
1414
1415    #[test]
1416    fn test_vote_lockout() {
1417        let (_vote_pubkey, vote_account) = create_test_account();
1418
1419        let mut vote_state: VoteState =
1420            StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
1421                .unwrap()
1422                .convert_to_current();
1423
1424        for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
1425            process_slot_vote_unchecked(&mut vote_state, (INITIAL_LOCKOUT * i) as u64);
1426        }
1427
1428        // The last vote should have been popped b/c it reached a depth of MAX_LOCKOUT_HISTORY
1429        assert_eq!(vote_state.votes.len(), MAX_LOCKOUT_HISTORY);
1430        assert_eq!(vote_state.root_slot, Some(0));
1431        check_lockouts(&vote_state);
1432
1433        // One more vote that confirms the entire stack,
1434        // the root_slot should change to the
1435        // second vote
1436        let top_vote = vote_state.votes.front().unwrap().slot();
1437        let slot = vote_state.last_lockout().unwrap().last_locked_out_slot();
1438        process_slot_vote_unchecked(&mut vote_state, slot);
1439        assert_eq!(Some(top_vote), vote_state.root_slot);
1440
1441        // Expire everything except the first vote
1442        let slot = vote_state
1443            .votes
1444            .front()
1445            .unwrap()
1446            .lockout
1447            .last_locked_out_slot();
1448        process_slot_vote_unchecked(&mut vote_state, slot);
1449        // First vote and new vote are both stored for a total of 2 votes
1450        assert_eq!(vote_state.votes.len(), 2);
1451    }
1452
1453    #[test]
1454    fn test_update_commission() {
1455        let node_pubkey = Pubkey::new_unique();
1456        let withdrawer_pubkey = Pubkey::new_unique();
1457        let clock = Clock::default();
1458        let vote_state = VoteState::new(
1459            &VoteInit {
1460                node_pubkey,
1461                authorized_voter: withdrawer_pubkey,
1462                authorized_withdrawer: withdrawer_pubkey,
1463                commission: 10,
1464            },
1465            &clock,
1466        );
1467
1468        let serialized =
1469            bincode::serialize(&VoteStateVersions::Current(Box::new(vote_state.clone()))).unwrap();
1470        let serialized_len = serialized.len();
1471        let rent = Rent::default();
1472        let lamports = rent.minimum_balance(serialized_len);
1473        let mut vote_account = AccountSharedData::new(lamports, serialized_len, &id());
1474        vote_account.set_data_from_slice(&serialized);
1475
1476        // Create a fake TransactionContext with a fake InstructionContext with a single account which is the
1477        // vote account that was just created
1478        let processor_account =
1479            AccountSharedData::new(0, 0, &clone_solana_sdk_ids::native_loader::id());
1480        let transaction_context = TransactionContext::new(
1481            vec![(id(), processor_account), (node_pubkey, vote_account)],
1482            rent,
1483            0,
1484            0,
1485        );
1486        let mut instruction_context = InstructionContext::default();
1487        instruction_context.configure(
1488            &[0],
1489            &[InstructionAccount {
1490                index_in_transaction: 1,
1491                index_in_caller: 1,
1492                index_in_callee: 0,
1493                is_signer: false,
1494                is_writable: true,
1495            }],
1496            &[],
1497        );
1498
1499        // Get the BorrowedAccount from the InstructionContext which is what is used to manipulate and inspect account
1500        // state
1501        let mut borrowed_account = instruction_context
1502            .try_borrow_instruction_account(&transaction_context, 0)
1503            .unwrap();
1504
1505        let epoch_schedule = std::sync::Arc::new(EpochSchedule::without_warmup());
1506
1507        let first_half_clock = std::sync::Arc::new(Clock {
1508            slot: epoch_schedule.slots_per_epoch / 4,
1509            ..Clock::default()
1510        });
1511
1512        let second_half_clock = std::sync::Arc::new(Clock {
1513            slot: (epoch_schedule.slots_per_epoch * 3) / 4,
1514            ..Clock::default()
1515        });
1516
1517        let mut feature_set = FeatureSet::default();
1518        feature_set.activate(
1519            &feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id(),
1520            1,
1521        );
1522
1523        let signers: HashSet<Pubkey> = vec![withdrawer_pubkey].into_iter().collect();
1524
1525        // Increase commission in first half of epoch -- allowed
1526        assert_eq!(
1527            borrowed_account
1528                .get_state::<VoteStateVersions>()
1529                .unwrap()
1530                .convert_to_current()
1531                .commission,
1532            10
1533        );
1534        assert_matches!(
1535            update_commission(
1536                &mut borrowed_account,
1537                11,
1538                &signers,
1539                &epoch_schedule,
1540                &first_half_clock,
1541                &feature_set
1542            ),
1543            Ok(())
1544        );
1545        assert_eq!(
1546            borrowed_account
1547                .get_state::<VoteStateVersions>()
1548                .unwrap()
1549                .convert_to_current()
1550                .commission,
1551            11
1552        );
1553
1554        // Increase commission in second half of epoch -- disallowed
1555        assert_matches!(
1556            update_commission(
1557                &mut borrowed_account,
1558                12,
1559                &signers,
1560                &epoch_schedule,
1561                &second_half_clock,
1562                &feature_set
1563            ),
1564            Err(_)
1565        );
1566        assert_eq!(
1567            borrowed_account
1568                .get_state::<VoteStateVersions>()
1569                .unwrap()
1570                .convert_to_current()
1571                .commission,
1572            11
1573        );
1574
1575        // Decrease commission in first half of epoch -- allowed
1576        assert_matches!(
1577            update_commission(
1578                &mut borrowed_account,
1579                10,
1580                &signers,
1581                &epoch_schedule,
1582                &first_half_clock,
1583                &feature_set
1584            ),
1585            Ok(())
1586        );
1587        assert_eq!(
1588            borrowed_account
1589                .get_state::<VoteStateVersions>()
1590                .unwrap()
1591                .convert_to_current()
1592                .commission,
1593            10
1594        );
1595
1596        // Decrease commission in second half of epoch -- disallowed because feature_set does not allow it
1597        assert_matches!(
1598            update_commission(
1599                &mut borrowed_account,
1600                9,
1601                &signers,
1602                &epoch_schedule,
1603                &second_half_clock,
1604                &feature_set
1605            ),
1606            Err(_)
1607        );
1608        assert_eq!(
1609            borrowed_account
1610                .get_state::<VoteStateVersions>()
1611                .unwrap()
1612                .convert_to_current()
1613                .commission,
1614            10
1615        );
1616
1617        // Decrease commission in second half of epoch -- allowed because feature_set allows it
1618        feature_set.activate(&feature_set::allow_commission_decrease_at_any_time::id(), 1);
1619        assert_matches!(
1620            update_commission(
1621                &mut borrowed_account,
1622                9,
1623                &signers,
1624                &epoch_schedule,
1625                &second_half_clock,
1626                &feature_set
1627            ),
1628            Ok(())
1629        );
1630        assert_eq!(
1631            borrowed_account
1632                .get_state::<VoteStateVersions>()
1633                .unwrap()
1634                .convert_to_current()
1635                .commission,
1636            9
1637        );
1638    }
1639
1640    #[test]
1641    fn test_vote_double_lockout_after_expiration() {
1642        let voter_pubkey = clone_solana_pubkey::new_rand();
1643        let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1644
1645        for i in 0..3 {
1646            process_slot_vote_unchecked(&mut vote_state, i as u64);
1647        }
1648
1649        check_lockouts(&vote_state);
1650
1651        // Expire the third vote (which was a vote for slot 2). The height of the
1652        // vote stack is unchanged, so none of the previous votes should have
1653        // doubled in lockout
1654        process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 1) as u64);
1655        check_lockouts(&vote_state);
1656
1657        // Vote again, this time the vote stack depth increases, so the votes should
1658        // double for everybody
1659        process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 2) as u64);
1660        check_lockouts(&vote_state);
1661
1662        // Vote again, this time the vote stack depth increases, so the votes should
1663        // double for everybody
1664        process_slot_vote_unchecked(&mut vote_state, (2 + INITIAL_LOCKOUT + 3) as u64);
1665        check_lockouts(&vote_state);
1666    }
1667
1668    #[test]
1669    fn test_expire_multiple_votes() {
1670        let voter_pubkey = clone_solana_pubkey::new_rand();
1671        let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1672
1673        for i in 0..3 {
1674            process_slot_vote_unchecked(&mut vote_state, i as u64);
1675        }
1676
1677        assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1678
1679        // Expire the second and third votes
1680        let expire_slot = vote_state.votes[1].slot() + vote_state.votes[1].lockout.lockout() + 1;
1681        process_slot_vote_unchecked(&mut vote_state, expire_slot);
1682        assert_eq!(vote_state.votes.len(), 2);
1683
1684        // Check that the old votes expired
1685        assert_eq!(vote_state.votes[0].slot(), 0);
1686        assert_eq!(vote_state.votes[1].slot(), expire_slot);
1687
1688        // Process one more vote
1689        process_slot_vote_unchecked(&mut vote_state, expire_slot + 1);
1690
1691        // Confirmation count for the older first vote should remain unchanged
1692        assert_eq!(vote_state.votes[0].confirmation_count(), 3);
1693
1694        // The later votes should still have increasing confirmation counts
1695        assert_eq!(vote_state.votes[1].confirmation_count(), 2);
1696        assert_eq!(vote_state.votes[2].confirmation_count(), 1);
1697    }
1698
1699    #[test]
1700    fn test_vote_credits() {
1701        let voter_pubkey = clone_solana_pubkey::new_rand();
1702        let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1703
1704        for i in 0..MAX_LOCKOUT_HISTORY {
1705            process_slot_vote_unchecked(&mut vote_state, i as u64);
1706        }
1707
1708        assert_eq!(vote_state.credits(), 0);
1709
1710        process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 1);
1711        assert_eq!(vote_state.credits(), 1);
1712        process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 2);
1713        assert_eq!(vote_state.credits(), 2);
1714        process_slot_vote_unchecked(&mut vote_state, MAX_LOCKOUT_HISTORY as u64 + 3);
1715        assert_eq!(vote_state.credits(), 3);
1716    }
1717
1718    #[test]
1719    fn test_duplicate_vote() {
1720        let voter_pubkey = clone_solana_pubkey::new_rand();
1721        let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1722        process_slot_vote_unchecked(&mut vote_state, 0);
1723        process_slot_vote_unchecked(&mut vote_state, 1);
1724        process_slot_vote_unchecked(&mut vote_state, 0);
1725        assert_eq!(vote_state.nth_recent_lockout(0).unwrap().slot(), 1);
1726        assert_eq!(vote_state.nth_recent_lockout(1).unwrap().slot(), 0);
1727        assert!(vote_state.nth_recent_lockout(2).is_none());
1728    }
1729
1730    #[test]
1731    fn test_nth_recent_lockout() {
1732        let voter_pubkey = clone_solana_pubkey::new_rand();
1733        let mut vote_state = vote_state_new_for_test(&voter_pubkey);
1734        for i in 0..MAX_LOCKOUT_HISTORY {
1735            process_slot_vote_unchecked(&mut vote_state, i as u64);
1736        }
1737        for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
1738            assert_eq!(
1739                vote_state.nth_recent_lockout(i).unwrap().slot() as usize,
1740                MAX_LOCKOUT_HISTORY - i - 1,
1741            );
1742        }
1743        assert!(vote_state.nth_recent_lockout(MAX_LOCKOUT_HISTORY).is_none());
1744    }
1745
1746    fn check_lockouts(vote_state: &VoteState) {
1747        for (i, vote) in vote_state.votes.iter().enumerate() {
1748            let num_votes = vote_state
1749                .votes
1750                .len()
1751                .checked_sub(i)
1752                .expect("`i` is less than `vote_state.votes.len()`");
1753            assert_eq!(
1754                vote.lockout.lockout(),
1755                INITIAL_LOCKOUT.pow(num_votes as u32) as u64
1756            );
1757        }
1758    }
1759
1760    fn recent_votes(vote_state: &VoteState) -> Vec<Vote> {
1761        let start = vote_state.votes.len().saturating_sub(MAX_RECENT_VOTES);
1762        (start..vote_state.votes.len())
1763            .map(|i| {
1764                Vote::new(
1765                    vec![vote_state.votes.get(i).unwrap().slot()],
1766                    Hash::default(),
1767                )
1768            })
1769            .collect()
1770    }
1771
1772    /// check that two accounts with different data can be brought to the same state with one vote submission
1773    #[test]
1774    fn test_process_missed_votes() {
1775        let account_a = clone_solana_pubkey::new_rand();
1776        let mut vote_state_a = vote_state_new_for_test(&account_a);
1777        let account_b = clone_solana_pubkey::new_rand();
1778        let mut vote_state_b = vote_state_new_for_test(&account_b);
1779
1780        // process some votes on account a
1781        (0..5).for_each(|i| process_slot_vote_unchecked(&mut vote_state_a, i as u64));
1782        assert_ne!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1783
1784        // as long as b has missed less than "NUM_RECENT" votes both accounts should be in sync
1785        let slots = (0u64..MAX_RECENT_VOTES as u64).collect();
1786        let vote = Vote::new(slots, Hash::default());
1787        let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
1788
1789        assert_eq!(
1790            process_vote(&mut vote_state_a, &vote, &slot_hashes, 0, 0),
1791            Ok(())
1792        );
1793        assert_eq!(
1794            process_vote(&mut vote_state_b, &vote, &slot_hashes, 0, 0),
1795            Ok(())
1796        );
1797        assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
1798    }
1799
1800    #[test]
1801    fn test_process_vote_skips_old_vote() {
1802        let mut vote_state = VoteState::default();
1803
1804        let vote = Vote::new(vec![0], Hash::default());
1805        let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1806        assert_eq!(
1807            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1808            Ok(())
1809        );
1810        let recent = recent_votes(&vote_state);
1811        assert_eq!(
1812            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1813            Err(VoteError::VoteTooOld)
1814        );
1815        assert_eq!(recent, recent_votes(&vote_state));
1816    }
1817
1818    #[test]
1819    fn test_check_slots_are_valid_vote_empty_slot_hashes() {
1820        let vote_state = VoteState::default();
1821
1822        let vote = Vote::new(vec![0], Hash::default());
1823        assert_eq!(
1824            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &[]),
1825            Err(VoteError::VoteTooOld)
1826        );
1827    }
1828
1829    #[test]
1830    fn test_check_slots_are_valid_new_vote() {
1831        let vote_state = VoteState::default();
1832
1833        let vote = Vote::new(vec![0], Hash::default());
1834        let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1835        assert_eq!(
1836            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1837            Ok(())
1838        );
1839    }
1840
1841    #[test]
1842    fn test_check_slots_are_valid_bad_hash() {
1843        let vote_state = VoteState::default();
1844
1845        let vote = Vote::new(vec![0], Hash::default());
1846        let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
1847        assert_eq!(
1848            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1849            Err(VoteError::SlotHashMismatch)
1850        );
1851    }
1852
1853    #[test]
1854    fn test_check_slots_are_valid_bad_slot() {
1855        let vote_state = VoteState::default();
1856
1857        let vote = Vote::new(vec![1], Hash::default());
1858        let slot_hashes: Vec<_> = vec![(0, vote.hash)];
1859        assert_eq!(
1860            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1861            Err(VoteError::SlotsMismatch)
1862        );
1863    }
1864
1865    #[test]
1866    fn test_check_slots_are_valid_duplicate_vote() {
1867        let mut vote_state = VoteState::default();
1868
1869        let vote = Vote::new(vec![0], Hash::default());
1870        let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1871        assert_eq!(
1872            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1873            Ok(())
1874        );
1875        assert_eq!(
1876            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1877            Err(VoteError::VoteTooOld)
1878        );
1879    }
1880
1881    #[test]
1882    fn test_check_slots_are_valid_next_vote() {
1883        let mut vote_state = VoteState::default();
1884
1885        let vote = Vote::new(vec![0], Hash::default());
1886        let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1887        assert_eq!(
1888            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1889            Ok(())
1890        );
1891
1892        let vote = Vote::new(vec![0, 1], Hash::default());
1893        let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1894        assert_eq!(
1895            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1896            Ok(())
1897        );
1898    }
1899
1900    #[test]
1901    fn test_check_slots_are_valid_next_vote_only() {
1902        let mut vote_state = VoteState::default();
1903
1904        let vote = Vote::new(vec![0], Hash::default());
1905        let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
1906        assert_eq!(
1907            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
1908            Ok(())
1909        );
1910
1911        let vote = Vote::new(vec![1], Hash::default());
1912        let slot_hashes: Vec<_> = vec![(1, vote.hash), (0, vote.hash)];
1913        assert_eq!(
1914            check_slots_are_valid(&vote_state, &vote.slots, &vote.hash, &slot_hashes),
1915            Ok(())
1916        );
1917    }
1918    #[test]
1919    fn test_process_vote_empty_slots() {
1920        let mut vote_state = VoteState::default();
1921
1922        let vote = Vote::new(vec![], Hash::default());
1923        assert_eq!(
1924            process_vote(&mut vote_state, &vote, &[], 0, 0),
1925            Err(VoteError::EmptySlots)
1926        );
1927    }
1928
1929    pub fn process_new_vote_state_from_lockouts(
1930        vote_state: &mut VoteState,
1931        new_state: VecDeque<Lockout>,
1932        new_root: Option<Slot>,
1933        timestamp: Option<i64>,
1934        epoch: Epoch,
1935        feature_set: Option<&FeatureSet>,
1936    ) -> Result<(), VoteError> {
1937        process_new_vote_state(
1938            vote_state,
1939            new_state.into_iter().map(LandedVote::from).collect(),
1940            new_root,
1941            timestamp,
1942            epoch,
1943            0,
1944            feature_set,
1945        )
1946    }
1947
1948    // Test vote credit updates after "one credit per slot" feature is enabled
1949    #[test]
1950    fn test_vote_state_update_increment_credits() {
1951        // Create a new Votestate
1952        let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
1953
1954        // Test data: a sequence of groups of votes to simulate having been cast, after each group a vote
1955        // state update is compared to "normal" vote processing to ensure that credits are earned equally
1956        let test_vote_groups: Vec<Vec<Slot>> = vec![
1957            // Initial set of votes that don't dequeue any slots, so no credits earned
1958            vec![1, 2, 3, 4, 5, 6, 7, 8],
1959            vec![
1960                9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
1961                30, 31,
1962            ],
1963            // Now a single vote which should result in the first root and first credit earned
1964            vec![32],
1965            // Now another vote, should earn one credit
1966            vec![33],
1967            // Two votes in sequence
1968            vec![34, 35],
1969            // 3 votes in sequence
1970            vec![36, 37, 38],
1971            // 30 votes in sequence
1972            vec![
1973                39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
1974                60, 61, 62, 63, 64, 65, 66, 67, 68,
1975            ],
1976            // 31 votes in sequence
1977            vec![
1978                69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
1979                90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1980            ],
1981            // Votes with expiry
1982            vec![100, 101, 106, 107, 112, 116, 120, 121, 122, 124],
1983            // More votes with expiry of a large number of votes
1984            vec![200, 201],
1985            vec![
1986                202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
1987                218, 219, 220, 221, 222, 223, 224, 225, 226,
1988            ],
1989            vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
1990        ];
1991
1992        let feature_set = FeatureSet::default();
1993
1994        for vote_group in test_vote_groups {
1995            // Duplicate vote_state so that the new vote can be applied
1996            let mut vote_state_after_vote = vote_state.clone();
1997
1998            process_vote_unchecked(
1999                &mut vote_state_after_vote,
2000                Vote {
2001                    slots: vote_group.clone(),
2002                    hash: Hash::new_unique(),
2003                    timestamp: None,
2004                },
2005            )
2006            .unwrap();
2007
2008            // Now use the resulting new vote state to perform a vote state update on vote_state
2009            assert_eq!(
2010                process_new_vote_state(
2011                    &mut vote_state,
2012                    vote_state_after_vote.votes,
2013                    vote_state_after_vote.root_slot,
2014                    None,
2015                    0,
2016                    0,
2017                    Some(&feature_set)
2018                ),
2019                Ok(())
2020            );
2021
2022            // And ensure that the credits earned were the same
2023            assert_eq!(
2024                vote_state.epoch_credits,
2025                vote_state_after_vote.epoch_credits
2026            );
2027        }
2028    }
2029
2030    // Test vote credit updates after "timely vote credits" feature is enabled
2031    #[test]
2032    fn test_timely_credits() {
2033        // Each of the following (Vec<Slot>, Slot, u32) tuples gives a set of slots to cast votes on, a slot in which
2034        // the vote was cast, and the number of credits that should have been earned by the vote account after this
2035        // and all prior votes were cast.
2036        let test_vote_groups: Vec<(Vec<Slot>, Slot, u32)> = vec![
2037            // Initial set of votes that don't dequeue any slots, so no credits earned
2038            (
2039                vec![1, 2, 3, 4, 5, 6, 7, 8],
2040                9,
2041                // root: none, no credits earned
2042                0,
2043            ),
2044            (
2045                vec![
2046                    9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
2047                    29, 30, 31,
2048                ],
2049                34,
2050                // lockouts full
2051                // root: none, no credits earned
2052                0,
2053            ),
2054            // Now a single vote which should result in the first root and first credit earned
2055            (
2056                vec![32],
2057                35,
2058                // root: 1
2059                // when slot 1 was voted on in slot 9, it earned 10 credits
2060                10,
2061            ),
2062            // Now another vote, should earn one credit
2063            (
2064                vec![33],
2065                36,
2066                // root: 2
2067                // when slot 2 was voted on in slot 9, it earned 11 credits
2068                10 + 11, // 21
2069            ),
2070            // Two votes in sequence
2071            (
2072                vec![34, 35],
2073                37,
2074                // root: 4
2075                // when slots 3 and 4 were voted on in slot 9, they earned 12 and 13 credits
2076                21 + 12 + 13, // 46
2077            ),
2078            // 3 votes in sequence
2079            (
2080                vec![36, 37, 38],
2081                39,
2082                // root: 7
2083                // slots 5, 6, and 7 earned 14, 15, and 16 credits when voted in slot 9
2084                46 + 14 + 15 + 16, // 91
2085            ),
2086            (
2087                // 30 votes in sequence
2088                vec![
2089                    39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
2090                    58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
2091                ],
2092                69,
2093                // root: 37
2094                // slot 8 was voted in slot 9, earning 16 credits
2095                // 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
2096                //   slot 34
2097                // slot 26, 27, 28, 29, 30, 31 earned 10, 11, 12, 13, 14, 15 credits when voted in slot 34
2098                // slot 32 earned 15 credits when voted in slot 35
2099                // slot 33 earned 15 credits when voted in slot 36
2100                // slot 34 and 35 earned 15 and 16 credits when voted in slot 37
2101                // slot 36 and 37 earned 15 and 16 credits when voted in slot 39
2102                91 + 16
2103                    + 9 // * 1
2104                    + 2
2105                    + 3
2106                    + 4
2107                    + 5
2108                    + 6
2109                    + 7
2110                    + 8
2111                    + 9
2112                    + 10
2113                    + 11
2114                    + 12
2115                    + 13
2116                    + 14
2117                    + 15
2118                    + 15
2119                    + 15
2120                    + 15
2121                    + 16
2122                    + 15
2123                    + 16, // 327
2124            ),
2125            // 31 votes in sequence
2126            (
2127                vec![
2128                    69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
2129                    89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
2130                ],
2131                100,
2132                // root: 68
2133                // slot 38 earned 16 credits when voted in slot 39
2134                // 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
2135                //   when voted in slot 69
2136                // slot 61, 62, 63, 64, 65, 66, 67, 68 earned 10, 11, 12, 13, 14, 15, 16, and 16 credits when
2137                //   voted in slot 69
2138                327 + 16
2139                    + 14 // * 1
2140                    + 2
2141                    + 3
2142                    + 4
2143                    + 5
2144                    + 6
2145                    + 7
2146                    + 8
2147                    + 9
2148                    + 10
2149                    + 11
2150                    + 12
2151                    + 13
2152                    + 14
2153                    + 15
2154                    + 16
2155                    + 16, // 508
2156            ),
2157            // Votes with expiry
2158            (
2159                vec![115, 116, 117, 118, 119, 120, 121, 122, 123, 124],
2160                130,
2161                // root: 74
2162                // slots 96 - 114 expire
2163                // slots 69 - 74 earned 1 credit when voted in slot 100
2164                508 + ((74 - 69) + 1), // 514
2165            ),
2166            // More votes with expiry of a large number of votes
2167            (
2168                vec![200, 201],
2169                202,
2170                // root: 74
2171                // slots 119 - 124 expire
2172                514,
2173            ),
2174            (
2175                vec![
2176                    202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
2177                    218, 219, 220, 221, 222, 223, 224, 225, 226,
2178                ],
2179                227,
2180                // root: 95
2181                // 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
2182                //   slot 100
2183                // slot 92, 93, 94, 95 earned 10, 11, 12, 13, credits when voted in slot 100
2184                514 + 9 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13, // 613
2185            ),
2186            (
2187                vec![227, 228, 229, 230, 231, 232, 233, 234, 235, 236],
2188                237,
2189                // root: 205
2190                // slot 115 - 118 earned 3, 4, 5, and 6 credits when voted in slot 130
2191                // slot 200 and 201 earned 16 credits when voted in slot 202
2192                // slots 202 - 205 earned 1 credit when voted in slot 227
2193                613 + 3 + 4 + 5 + 6 + 16 + 16 + 1 + 1 + 1 + 1, // 667
2194            ),
2195        ];
2196
2197        let feature_set = FeatureSet::default();
2198
2199        // For each vote group, process all vote groups leading up to it and it itself, and ensure that the number of
2200        // credits earned is correct for both regular votes and vote state updates
2201        for i in 0..test_vote_groups.len() {
2202            // Create a new VoteState for vote transaction
2203            let mut vote_state_1 = VoteState::new(&VoteInit::default(), &Clock::default());
2204            // Create a new VoteState for vote state update transaction
2205            let mut vote_state_2 = VoteState::new(&VoteInit::default(), &Clock::default());
2206            test_vote_groups.iter().take(i + 1).for_each(|vote_group| {
2207                let vote = Vote {
2208                    slots: vote_group.0.clone(), //vote_group.0 is the set of slots to cast votes on
2209                    hash: Hash::new_unique(),
2210                    timestamp: None,
2211                };
2212                let slot_hashes: Vec<_> =
2213                    vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
2214                assert_eq!(
2215                    process_vote(
2216                        &mut vote_state_1,
2217                        &vote,
2218                        &slot_hashes,
2219                        0,
2220                        vote_group.1, // vote_group.1 is the slot in which the vote was cast
2221                    ),
2222                    Ok(())
2223                );
2224
2225                assert_eq!(
2226                    process_new_vote_state(
2227                        &mut vote_state_2,
2228                        vote_state_1.votes.clone(),
2229                        vote_state_1.root_slot,
2230                        None,
2231                        0,
2232                        vote_group.1, // vote_group.1 is the slot in which the vote was cast
2233                        Some(&feature_set)
2234                    ),
2235                    Ok(())
2236                );
2237            });
2238
2239            // Ensure that the credits earned is correct for both vote states
2240            let vote_group = &test_vote_groups[i];
2241            assert_eq!(vote_state_1.credits(), vote_group.2 as u64); // vote_group.2 is the expected number of credits
2242            assert_eq!(vote_state_2.credits(), vote_group.2 as u64); // vote_group.2 is the expected number of credits
2243        }
2244    }
2245
2246    #[test]
2247    fn test_retroactive_voting_timely_credits() {
2248        // Each of the following (Vec<(Slot, int)>, Slot, Option<Slot>, u32) tuples gives the following data:
2249        // Vec<(Slot, int)> -- the set of slots and confirmation_counts that is the proposed vote state
2250        // Slot -- the slot in which the proposed vote state landed
2251        // Option<Slot> -- the root after processing the proposed vote state
2252        // u32 -- the credits after processing the proposed vote state
2253        #[allow(clippy::type_complexity)]
2254        let test_vote_state_updates: Vec<(Vec<(Slot, u32)>, Slot, Option<Slot>, u32)> = vec![
2255            // proposed vote state to set initial vote state
2256            (
2257                vec![(7, 4), (8, 3), (9, 2), (10, 1)],
2258                11,
2259                // root: none
2260                None,
2261                // no credits earned
2262                0,
2263            ),
2264            // proposed vote state to include the missing slots *prior to previously included slots*
2265            (
2266                vec![
2267                    (1, 10),
2268                    (2, 9),
2269                    (3, 8),
2270                    (4, 7),
2271                    (5, 6),
2272                    (6, 5),
2273                    (7, 4),
2274                    (8, 3),
2275                    (9, 2),
2276                    (10, 1),
2277                ],
2278                12,
2279                // root: none
2280                None,
2281                // no credits earned
2282                0,
2283            ),
2284            // Now a single proposed vote state which roots all of the slots from 1 - 10
2285            (
2286                vec![
2287                    (11, 31),
2288                    (12, 30),
2289                    (13, 29),
2290                    (14, 28),
2291                    (15, 27),
2292                    (16, 26),
2293                    (17, 25),
2294                    (18, 24),
2295                    (19, 23),
2296                    (20, 22),
2297                    (21, 21),
2298                    (22, 20),
2299                    (23, 19),
2300                    (24, 18),
2301                    (25, 17),
2302                    (26, 16),
2303                    (27, 15),
2304                    (28, 14),
2305                    (29, 13),
2306                    (30, 12),
2307                    (31, 11),
2308                    (32, 10),
2309                    (33, 9),
2310                    (34, 8),
2311                    (35, 7),
2312                    (36, 6),
2313                    (37, 5),
2314                    (38, 4),
2315                    (39, 3),
2316                    (40, 2),
2317                    (41, 1),
2318                ],
2319                42,
2320                // root: 10
2321                Some(10),
2322                // when slots 1 - 6 were voted on in slot 12, they earned 7, 8, 9, 10, 11, and 12 credits
2323                // when slots 7 - 10 were voted on in slot 11, they earned 14, 15, 16, and 16 credits
2324                7 + 8 + 9 + 10 + 11 + 12 + 14 + 15 + 16 + 16,
2325            ),
2326        ];
2327
2328        let feature_set = FeatureSet::default();
2329
2330        // Initial vote state
2331        let mut vote_state = VoteState::new(&VoteInit::default(), &Clock::default());
2332
2333        // Process the vote state updates in sequence and ensure that the credits earned after each is processed is
2334        // correct
2335        test_vote_state_updates
2336            .iter()
2337            .for_each(|proposed_vote_state| {
2338                let new_state = proposed_vote_state
2339                    .0 // proposed_vote_state.0 is the set of slots and confirmation_counts that is the proposed vote state
2340                    .iter()
2341                    .map(|(slot, confirmation_count)| LandedVote {
2342                        latency: 0,
2343                        lockout: Lockout::new_with_confirmation_count(*slot, *confirmation_count),
2344                    })
2345                    .collect::<VecDeque<LandedVote>>();
2346                assert_eq!(
2347                    process_new_vote_state(
2348                        &mut vote_state,
2349                        new_state,
2350                        proposed_vote_state.2, // proposed_vote_state.2 is root after processing the proposed vote state
2351                        None,
2352                        0,
2353                        proposed_vote_state.1, // proposed_vote_state.1 is the slot in which the proposed vote state was applied
2354                        Some(&feature_set)
2355                    ),
2356                    Ok(())
2357                );
2358
2359                // Ensure that the credits earned is correct
2360                assert_eq!(vote_state.credits(), proposed_vote_state.3 as u64);
2361            });
2362    }
2363
2364    #[test]
2365    fn test_process_new_vote_too_many_votes() {
2366        let mut vote_state1 = VoteState::default();
2367        let bad_votes: VecDeque<Lockout> = (0..=MAX_LOCKOUT_HISTORY)
2368            .map(|slot| {
2369                Lockout::new_with_confirmation_count(
2370                    slot as Slot,
2371                    (MAX_LOCKOUT_HISTORY - slot + 1) as u32,
2372                )
2373            })
2374            .collect();
2375
2376        let current_epoch = vote_state1.current_epoch();
2377        assert_eq!(
2378            process_new_vote_state_from_lockouts(
2379                &mut vote_state1,
2380                bad_votes,
2381                None,
2382                None,
2383                current_epoch,
2384                None
2385            ),
2386            Err(VoteError::TooManyVotes)
2387        );
2388    }
2389
2390    #[test]
2391    fn test_process_new_vote_state_root_rollback() {
2392        let mut vote_state1 = VoteState::default();
2393        for i in 0..MAX_LOCKOUT_HISTORY + 2 {
2394            process_slot_vote_unchecked(&mut vote_state1, i as Slot);
2395        }
2396        assert_eq!(vote_state1.root_slot.unwrap(), 1);
2397
2398        // Update vote_state2 with a higher slot so that `process_new_vote_state`
2399        // doesn't panic.
2400        let mut vote_state2 = vote_state1.clone();
2401        process_slot_vote_unchecked(&mut vote_state2, MAX_LOCKOUT_HISTORY as Slot + 3);
2402
2403        // Trying to set a lesser root should error
2404        let lesser_root = Some(0);
2405
2406        let current_epoch = vote_state2.current_epoch();
2407        assert_eq!(
2408            process_new_vote_state(
2409                &mut vote_state1,
2410                vote_state2.votes.clone(),
2411                lesser_root,
2412                None,
2413                current_epoch,
2414                0,
2415                None,
2416            ),
2417            Err(VoteError::RootRollBack)
2418        );
2419
2420        // Trying to set root to None should error
2421        let none_root = None;
2422        assert_eq!(
2423            process_new_vote_state(
2424                &mut vote_state1,
2425                vote_state2.votes.clone(),
2426                none_root,
2427                None,
2428                current_epoch,
2429                0,
2430                None,
2431            ),
2432            Err(VoteError::RootRollBack)
2433        );
2434    }
2435
2436    #[test]
2437    fn test_process_new_vote_state_zero_confirmations() {
2438        let mut vote_state1 = VoteState::default();
2439        let current_epoch = vote_state1.current_epoch();
2440
2441        let bad_votes: VecDeque<Lockout> = vec![
2442            Lockout::new_with_confirmation_count(0, 0),
2443            Lockout::new_with_confirmation_count(1, 1),
2444        ]
2445        .into_iter()
2446        .collect();
2447        assert_eq!(
2448            process_new_vote_state_from_lockouts(
2449                &mut vote_state1,
2450                bad_votes,
2451                None,
2452                None,
2453                current_epoch,
2454                None
2455            ),
2456            Err(VoteError::ZeroConfirmations)
2457        );
2458
2459        let bad_votes: VecDeque<Lockout> = vec![
2460            Lockout::new_with_confirmation_count(0, 2),
2461            Lockout::new_with_confirmation_count(1, 0),
2462        ]
2463        .into_iter()
2464        .collect();
2465        assert_eq!(
2466            process_new_vote_state_from_lockouts(
2467                &mut vote_state1,
2468                bad_votes,
2469                None,
2470                None,
2471                current_epoch,
2472                None
2473            ),
2474            Err(VoteError::ZeroConfirmations)
2475        );
2476    }
2477
2478    #[test]
2479    fn test_process_new_vote_state_confirmations_too_large() {
2480        let mut vote_state1 = VoteState::default();
2481        let current_epoch = vote_state1.current_epoch();
2482
2483        let good_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2484            0,
2485            MAX_LOCKOUT_HISTORY as u32,
2486        )]
2487        .into_iter()
2488        .collect();
2489
2490        process_new_vote_state_from_lockouts(
2491            &mut vote_state1,
2492            good_votes,
2493            None,
2494            None,
2495            current_epoch,
2496            None,
2497        )
2498        .unwrap();
2499
2500        let mut vote_state1 = VoteState::default();
2501        let bad_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
2502            0,
2503            MAX_LOCKOUT_HISTORY as u32 + 1,
2504        )]
2505        .into_iter()
2506        .collect();
2507        assert_eq!(
2508            process_new_vote_state_from_lockouts(
2509                &mut vote_state1,
2510                bad_votes,
2511                None,
2512                None,
2513                current_epoch,
2514                None
2515            ),
2516            Err(VoteError::ConfirmationTooLarge)
2517        );
2518    }
2519
2520    #[test]
2521    fn test_process_new_vote_state_slot_smaller_than_root() {
2522        let mut vote_state1 = VoteState::default();
2523        let current_epoch = vote_state1.current_epoch();
2524        let root_slot = 5;
2525
2526        let bad_votes: VecDeque<Lockout> = vec![
2527            Lockout::new_with_confirmation_count(root_slot, 2),
2528            Lockout::new_with_confirmation_count(root_slot + 1, 1),
2529        ]
2530        .into_iter()
2531        .collect();
2532        assert_eq!(
2533            process_new_vote_state_from_lockouts(
2534                &mut vote_state1,
2535                bad_votes,
2536                Some(root_slot),
2537                None,
2538                current_epoch,
2539                None,
2540            ),
2541            Err(VoteError::SlotSmallerThanRoot)
2542        );
2543
2544        let bad_votes: VecDeque<Lockout> = vec![
2545            Lockout::new_with_confirmation_count(root_slot - 1, 2),
2546            Lockout::new_with_confirmation_count(root_slot + 1, 1),
2547        ]
2548        .into_iter()
2549        .collect();
2550        assert_eq!(
2551            process_new_vote_state_from_lockouts(
2552                &mut vote_state1,
2553                bad_votes,
2554                Some(root_slot),
2555                None,
2556                current_epoch,
2557                None,
2558            ),
2559            Err(VoteError::SlotSmallerThanRoot)
2560        );
2561    }
2562
2563    #[test]
2564    fn test_process_new_vote_state_slots_not_ordered() {
2565        let mut vote_state1 = VoteState::default();
2566        let current_epoch = vote_state1.current_epoch();
2567
2568        let bad_votes: VecDeque<Lockout> = vec![
2569            Lockout::new_with_confirmation_count(1, 2),
2570            Lockout::new_with_confirmation_count(0, 1),
2571        ]
2572        .into_iter()
2573        .collect();
2574        assert_eq!(
2575            process_new_vote_state_from_lockouts(
2576                &mut vote_state1,
2577                bad_votes,
2578                None,
2579                None,
2580                current_epoch,
2581                None
2582            ),
2583            Err(VoteError::SlotsNotOrdered)
2584        );
2585
2586        let bad_votes: VecDeque<Lockout> = vec![
2587            Lockout::new_with_confirmation_count(1, 2),
2588            Lockout::new_with_confirmation_count(1, 1),
2589        ]
2590        .into_iter()
2591        .collect();
2592        assert_eq!(
2593            process_new_vote_state_from_lockouts(
2594                &mut vote_state1,
2595                bad_votes,
2596                None,
2597                None,
2598                current_epoch,
2599                None
2600            ),
2601            Err(VoteError::SlotsNotOrdered)
2602        );
2603    }
2604
2605    #[test]
2606    fn test_process_new_vote_state_confirmations_not_ordered() {
2607        let mut vote_state1 = VoteState::default();
2608        let current_epoch = vote_state1.current_epoch();
2609
2610        let bad_votes: VecDeque<Lockout> = vec![
2611            Lockout::new_with_confirmation_count(0, 1),
2612            Lockout::new_with_confirmation_count(1, 2),
2613        ]
2614        .into_iter()
2615        .collect();
2616        assert_eq!(
2617            process_new_vote_state_from_lockouts(
2618                &mut vote_state1,
2619                bad_votes,
2620                None,
2621                None,
2622                current_epoch,
2623                None
2624            ),
2625            Err(VoteError::ConfirmationsNotOrdered)
2626        );
2627
2628        let bad_votes: VecDeque<Lockout> = vec![
2629            Lockout::new_with_confirmation_count(0, 1),
2630            Lockout::new_with_confirmation_count(1, 1),
2631        ]
2632        .into_iter()
2633        .collect();
2634        assert_eq!(
2635            process_new_vote_state_from_lockouts(
2636                &mut vote_state1,
2637                bad_votes,
2638                None,
2639                None,
2640                current_epoch,
2641                None
2642            ),
2643            Err(VoteError::ConfirmationsNotOrdered)
2644        );
2645    }
2646
2647    #[test]
2648    fn test_process_new_vote_state_new_vote_state_lockout_mismatch() {
2649        let mut vote_state1 = VoteState::default();
2650        let current_epoch = vote_state1.current_epoch();
2651
2652        let bad_votes: VecDeque<Lockout> = vec![
2653            Lockout::new_with_confirmation_count(0, 2),
2654            Lockout::new_with_confirmation_count(7, 1),
2655        ]
2656        .into_iter()
2657        .collect();
2658
2659        // Slot 7 should have expired slot 0
2660        assert_eq!(
2661            process_new_vote_state_from_lockouts(
2662                &mut vote_state1,
2663                bad_votes,
2664                None,
2665                None,
2666                current_epoch,
2667                None
2668            ),
2669            Err(VoteError::NewVoteStateLockoutMismatch)
2670        );
2671    }
2672
2673    #[test]
2674    fn test_process_new_vote_state_confirmation_rollback() {
2675        let mut vote_state1 = VoteState::default();
2676        let current_epoch = vote_state1.current_epoch();
2677        let votes: VecDeque<Lockout> = vec![
2678            Lockout::new_with_confirmation_count(0, 4),
2679            Lockout::new_with_confirmation_count(1, 3),
2680        ]
2681        .into_iter()
2682        .collect();
2683        process_new_vote_state_from_lockouts(
2684            &mut vote_state1,
2685            votes,
2686            None,
2687            None,
2688            current_epoch,
2689            None,
2690        )
2691        .unwrap();
2692
2693        let votes: VecDeque<Lockout> = vec![
2694            Lockout::new_with_confirmation_count(0, 4),
2695            // Confirmation count lowered illegally
2696            Lockout::new_with_confirmation_count(1, 2),
2697            Lockout::new_with_confirmation_count(2, 1),
2698        ]
2699        .into_iter()
2700        .collect();
2701        // Should error because newer vote state should not have lower confirmation the same slot
2702        // 1
2703        assert_eq!(
2704            process_new_vote_state_from_lockouts(
2705                &mut vote_state1,
2706                votes,
2707                None,
2708                None,
2709                current_epoch,
2710                None
2711            ),
2712            Err(VoteError::ConfirmationRollBack)
2713        );
2714    }
2715
2716    #[test]
2717    fn test_process_new_vote_state_root_progress() {
2718        let mut vote_state1 = VoteState::default();
2719        for i in 0..MAX_LOCKOUT_HISTORY {
2720            process_slot_vote_unchecked(&mut vote_state1, i as u64);
2721        }
2722
2723        assert!(vote_state1.root_slot.is_none());
2724        let mut vote_state2 = vote_state1.clone();
2725
2726        // 1) Try to update `vote_state1` with no root,
2727        // to `vote_state2`, which has a new root, should succeed.
2728        //
2729        // 2) Then try to update`vote_state1` with an existing root,
2730        // to `vote_state2`, which has a newer root, which
2731        // should succeed.
2732        for new_vote in MAX_LOCKOUT_HISTORY + 1..=MAX_LOCKOUT_HISTORY + 2 {
2733            process_slot_vote_unchecked(&mut vote_state2, new_vote as Slot);
2734            assert_ne!(vote_state1.root_slot, vote_state2.root_slot);
2735
2736            process_new_vote_state(
2737                &mut vote_state1,
2738                vote_state2.votes.clone(),
2739                vote_state2.root_slot,
2740                None,
2741                vote_state2.current_epoch(),
2742                0,
2743                None,
2744            )
2745            .unwrap();
2746
2747            assert_eq!(vote_state1, vote_state2);
2748        }
2749    }
2750
2751    #[test]
2752    fn test_process_new_vote_state_same_slot_but_not_common_ancestor() {
2753        // It might be possible that during the switch from old vote instructions
2754        // to new vote instructions, new_state contains votes for slots LESS
2755        // than the current state, for instance:
2756        //
2757        // Current on-chain state: 1, 5
2758        // New state: 1, 2 (lockout: 4), 3, 5, 7
2759        //
2760        // Imagine the validator made two of these votes:
2761        // 1) The first vote {1, 2, 3} didn't land in the old state, but didn't
2762        // land on chain
2763        // 2) A second vote {1, 2, 5} was then submitted, which landed
2764        //
2765        //
2766        // 2 is not popped off in the local tower because 3 doubled the lockout.
2767        // However, 3 did not land in the on-chain state, so the vote {1, 2, 6}
2768        // will immediately pop off 2.
2769
2770        // Construct on-chain vote state
2771        let mut vote_state1 = VoteState::default();
2772        process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5]);
2773        assert_eq!(
2774            vote_state1
2775                .votes
2776                .iter()
2777                .map(|vote| vote.slot())
2778                .collect::<Vec<Slot>>(),
2779            vec![1, 5]
2780        );
2781
2782        // Construct local tower state
2783        let mut vote_state2 = VoteState::default();
2784        process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2785        assert_eq!(
2786            vote_state2
2787                .votes
2788                .iter()
2789                .map(|vote| vote.slot())
2790                .collect::<Vec<Slot>>(),
2791            vec![1, 2, 3, 5, 7]
2792        );
2793
2794        // See that on-chain vote state can update properly
2795        process_new_vote_state(
2796            &mut vote_state1,
2797            vote_state2.votes.clone(),
2798            vote_state2.root_slot,
2799            None,
2800            vote_state2.current_epoch(),
2801            0,
2802            None,
2803        )
2804        .unwrap();
2805
2806        assert_eq!(vote_state1, vote_state2);
2807    }
2808
2809    #[test]
2810    fn test_process_new_vote_state_lockout_violation() {
2811        // Construct on-chain vote state
2812        let mut vote_state1 = VoteState::default();
2813        process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 4, 5]);
2814        assert_eq!(
2815            vote_state1
2816                .votes
2817                .iter()
2818                .map(|vote| vote.slot())
2819                .collect::<Vec<Slot>>(),
2820            vec![1, 2, 4, 5]
2821        );
2822
2823        // Construct conflicting tower state. Vote 4 is missing,
2824        // but 5 should not have popped off vote 4.
2825        let mut vote_state2 = VoteState::default();
2826        process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 7]);
2827        assert_eq!(
2828            vote_state2
2829                .votes
2830                .iter()
2831                .map(|vote| vote.slot())
2832                .collect::<Vec<Slot>>(),
2833            vec![1, 2, 3, 5, 7]
2834        );
2835
2836        // See that on-chain vote state can update properly
2837        assert_eq!(
2838            process_new_vote_state(
2839                &mut vote_state1,
2840                vote_state2.votes.clone(),
2841                vote_state2.root_slot,
2842                None,
2843                vote_state2.current_epoch(),
2844                0,
2845                None
2846            ),
2847            Err(VoteError::LockoutConflict)
2848        );
2849    }
2850
2851    #[test]
2852    fn test_process_new_vote_state_lockout_violation2() {
2853        // Construct on-chain vote state
2854        let mut vote_state1 = VoteState::default();
2855        process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 5, 6, 7]);
2856        assert_eq!(
2857            vote_state1
2858                .votes
2859                .iter()
2860                .map(|vote| vote.slot())
2861                .collect::<Vec<Slot>>(),
2862            vec![1, 5, 6, 7]
2863        );
2864
2865        // Construct a new vote state. Violates on-chain state because 8
2866        // should not have popped off 7
2867        let mut vote_state2 = VoteState::default();
2868        process_slot_votes_unchecked(&mut vote_state2, &[1, 2, 3, 5, 6, 8]);
2869        assert_eq!(
2870            vote_state2
2871                .votes
2872                .iter()
2873                .map(|vote| vote.slot())
2874                .collect::<Vec<Slot>>(),
2875            vec![1, 2, 3, 5, 6, 8]
2876        );
2877
2878        // Both vote states contain `5`, but `5` is not part of the common prefix
2879        // of both vote states. However, the violation should still be detected.
2880        assert_eq!(
2881            process_new_vote_state(
2882                &mut vote_state1,
2883                vote_state2.votes.clone(),
2884                vote_state2.root_slot,
2885                None,
2886                vote_state2.current_epoch(),
2887                0,
2888                None
2889            ),
2890            Err(VoteError::LockoutConflict)
2891        );
2892    }
2893
2894    #[test]
2895    fn test_process_new_vote_state_expired_ancestor_not_removed() {
2896        // Construct on-chain vote state
2897        let mut vote_state1 = VoteState::default();
2898        process_slot_votes_unchecked(&mut vote_state1, &[1, 2, 3, 9]);
2899        assert_eq!(
2900            vote_state1
2901                .votes
2902                .iter()
2903                .map(|vote| vote.slot())
2904                .collect::<Vec<Slot>>(),
2905            vec![1, 9]
2906        );
2907
2908        // Example: {1: lockout 8, 9: lockout 2}, vote on 10 will not pop off 1
2909        // because 9 is not popped off yet
2910        let mut vote_state2 = vote_state1.clone();
2911        process_slot_vote_unchecked(&mut vote_state2, 10);
2912
2913        // Slot 1 has been expired by 10, but is kept alive by its descendant
2914        // 9 which has not been expired yet.
2915        assert_eq!(vote_state2.votes[0].slot(), 1);
2916        assert_eq!(vote_state2.votes[0].lockout.last_locked_out_slot(), 9);
2917        assert_eq!(
2918            vote_state2
2919                .votes
2920                .iter()
2921                .map(|vote| vote.slot())
2922                .collect::<Vec<Slot>>(),
2923            vec![1, 9, 10]
2924        );
2925
2926        // Should be able to update vote_state1
2927        process_new_vote_state(
2928            &mut vote_state1,
2929            vote_state2.votes.clone(),
2930            vote_state2.root_slot,
2931            None,
2932            vote_state2.current_epoch(),
2933            0,
2934            None,
2935        )
2936        .unwrap();
2937        assert_eq!(vote_state1, vote_state2,);
2938    }
2939
2940    #[test]
2941    fn test_process_new_vote_current_state_contains_bigger_slots() {
2942        let mut vote_state1 = VoteState::default();
2943        process_slot_votes_unchecked(&mut vote_state1, &[6, 7, 8]);
2944        assert_eq!(
2945            vote_state1
2946                .votes
2947                .iter()
2948                .map(|vote| vote.slot())
2949                .collect::<Vec<Slot>>(),
2950            vec![6, 7, 8]
2951        );
2952
2953        // Try to process something with lockout violations
2954        let bad_votes: VecDeque<Lockout> = vec![
2955            Lockout::new_with_confirmation_count(2, 5),
2956            // Slot 14 could not have popped off slot 6 yet
2957            Lockout::new_with_confirmation_count(14, 1),
2958        ]
2959        .into_iter()
2960        .collect();
2961        let root = Some(1);
2962
2963        let current_epoch = vote_state1.current_epoch();
2964        assert_eq!(
2965            process_new_vote_state_from_lockouts(
2966                &mut vote_state1,
2967                bad_votes,
2968                root,
2969                None,
2970                current_epoch,
2971                None
2972            ),
2973            Err(VoteError::LockoutConflict)
2974        );
2975
2976        let good_votes: VecDeque<LandedVote> = vec![
2977            Lockout::new_with_confirmation_count(2, 5).into(),
2978            Lockout::new_with_confirmation_count(15, 1).into(),
2979        ]
2980        .into_iter()
2981        .collect();
2982
2983        let current_epoch = vote_state1.current_epoch();
2984        process_new_vote_state(
2985            &mut vote_state1,
2986            good_votes.clone(),
2987            root,
2988            None,
2989            current_epoch,
2990            0,
2991            None,
2992        )
2993        .unwrap();
2994        assert_eq!(vote_state1.votes, good_votes);
2995    }
2996
2997    #[test]
2998    fn test_filter_old_votes() {
2999        let mut vote_state = VoteState::default();
3000        let old_vote_slot = 1;
3001        let vote = Vote::new(vec![old_vote_slot], Hash::default());
3002
3003        // Vote with all slots that are all older than the SlotHashes history should
3004        // error with `VotesTooOldAllFiltered`
3005        let slot_hashes = vec![(3, Hash::new_unique()), (2, Hash::new_unique())];
3006        assert_eq!(
3007            process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
3008            Err(VoteError::VotesTooOldAllFiltered)
3009        );
3010
3011        // Vote with only some slots older than the SlotHashes history should
3012        // filter out those older slots
3013        let vote_slot = 2;
3014        let vote_slot_hash = slot_hashes
3015            .iter()
3016            .find(|(slot, _hash)| *slot == vote_slot)
3017            .unwrap()
3018            .1;
3019
3020        let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
3021        process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0).unwrap();
3022        assert_eq!(
3023            vote_state
3024                .votes
3025                .into_iter()
3026                .map(|vote| vote.lockout)
3027                .collect::<Vec<Lockout>>(),
3028            vec![Lockout::new_with_confirmation_count(vote_slot, 1)]
3029        );
3030    }
3031
3032    fn build_slot_hashes(slots: Vec<Slot>) -> Vec<(Slot, Hash)> {
3033        slots
3034            .iter()
3035            .rev()
3036            .map(|x| (*x, Hash::new_unique()))
3037            .collect()
3038    }
3039
3040    fn build_vote_state(vote_slots: Vec<Slot>, slot_hashes: &[(Slot, Hash)]) -> VoteState {
3041        let mut vote_state = VoteState::default();
3042
3043        if !vote_slots.is_empty() {
3044            let vote_hash = slot_hashes
3045                .iter()
3046                .find(|(slot, _hash)| slot == vote_slots.last().unwrap())
3047                .unwrap()
3048                .1;
3049            let vote = Vote::new(vote_slots, vote_hash);
3050            process_vote_unfiltered(&mut vote_state, &vote.slots, &vote, slot_hashes, 0, 0)
3051                .unwrap();
3052        }
3053
3054        vote_state
3055    }
3056
3057    #[test]
3058    fn test_check_and_filter_proposed_vote_state_empty() {
3059        let empty_slot_hashes = build_slot_hashes(vec![]);
3060        let empty_vote_state = build_vote_state(vec![], &empty_slot_hashes);
3061
3062        // Test with empty TowerSync, should return EmptySlots error
3063        let mut tower_sync = TowerSync::from(vec![]);
3064        assert_eq!(
3065            check_and_filter_proposed_vote_state(
3066                &empty_vote_state,
3067                &mut tower_sync.lockouts,
3068                &mut tower_sync.root,
3069                tower_sync.hash,
3070                &empty_slot_hashes
3071            ),
3072            Err(VoteError::EmptySlots),
3073        );
3074
3075        // Test with non-empty TowerSync, should return SlotsMismatch since nothing exists in SlotHashes
3076        let mut tower_sync = TowerSync::from(vec![(0, 1)]);
3077        assert_eq!(
3078            check_and_filter_proposed_vote_state(
3079                &empty_vote_state,
3080                &mut tower_sync.lockouts,
3081                &mut tower_sync.root,
3082                tower_sync.hash,
3083                &empty_slot_hashes
3084            ),
3085            Err(VoteError::SlotsMismatch),
3086        );
3087    }
3088
3089    #[test]
3090    fn test_check_and_filter_proposed_vote_state_too_old() {
3091        let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3092        let latest_vote = 4;
3093        let vote_state = build_vote_state(vec![1, 2, 3, latest_vote], &slot_hashes);
3094
3095        // Test with a vote for a slot less than the latest vote in the vote_state,
3096        // should return error `VoteTooOld`
3097        let mut tower_sync = TowerSync::from(vec![(latest_vote, 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        // Test with a vote state update where the latest slot `X` in the update is
3110        // 1) Less than the earliest slot in slot_hashes history, AND
3111        // 2) `X` > latest_vote
3112        let earliest_slot_in_history = latest_vote + 2;
3113        let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history]);
3114        let mut tower_sync = TowerSync::from(vec![(earliest_slot_in_history - 1, 1)]);
3115        assert_eq!(
3116            check_and_filter_proposed_vote_state(
3117                &vote_state,
3118                &mut tower_sync.lockouts,
3119                &mut tower_sync.root,
3120                tower_sync.hash,
3121                &slot_hashes
3122            ),
3123            Err(VoteError::VoteTooOld),
3124        );
3125    }
3126
3127    fn run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3128        earliest_slot_in_history: Slot,
3129        current_vote_state_slots: Vec<Slot>,
3130        current_vote_state_root: Option<Slot>,
3131        proposed_slots_and_lockouts: Vec<(Slot, u32)>,
3132        proposed_root: Slot,
3133        expected_root: Option<Slot>,
3134        expected_vote_state: Vec<Lockout>,
3135    ) {
3136        assert!(proposed_root < earliest_slot_in_history);
3137        assert_eq!(
3138            expected_root,
3139            current_vote_state_slots
3140                .iter()
3141                .rev()
3142                .find(|slot| **slot <= proposed_root)
3143                .cloned()
3144        );
3145        let latest_slot_in_history = proposed_slots_and_lockouts
3146            .last()
3147            .unwrap()
3148            .0
3149            .max(earliest_slot_in_history);
3150        let mut slot_hashes = build_slot_hashes(
3151            (current_vote_state_slots.first().copied().unwrap_or(0)..=latest_slot_in_history)
3152                .collect::<Vec<Slot>>(),
3153        );
3154
3155        let mut vote_state = build_vote_state(current_vote_state_slots, &slot_hashes);
3156        vote_state.root_slot = current_vote_state_root;
3157
3158        slot_hashes.retain(|slot| slot.0 >= earliest_slot_in_history);
3159        assert!(!proposed_slots_and_lockouts.is_empty());
3160        let proposed_hash = slot_hashes
3161            .iter()
3162            .find(|(slot, _hash)| *slot == proposed_slots_and_lockouts.last().unwrap().0)
3163            .unwrap()
3164            .1;
3165
3166        // Test with a `TowerSync` where the root is less than `earliest_slot_in_history`.
3167        // Root slot in the `TowerSync` should be updated to match the root slot in the
3168        // current vote state
3169        let mut tower_sync = TowerSync::from(proposed_slots_and_lockouts);
3170        tower_sync.hash = proposed_hash;
3171        tower_sync.root = Some(proposed_root);
3172        check_and_filter_proposed_vote_state(
3173            &vote_state,
3174            &mut tower_sync.lockouts,
3175            &mut tower_sync.root,
3176            tower_sync.hash,
3177            &slot_hashes,
3178        )
3179        .unwrap();
3180        assert_eq!(tower_sync.root, expected_root);
3181
3182        // The proposed root slot should become the biggest slot in the current vote state less than
3183        // `earliest_slot_in_history`.
3184        assert!(do_process_tower_sync(
3185            &mut vote_state,
3186            &slot_hashes,
3187            0,
3188            0,
3189            tower_sync.clone(),
3190            Some(&FeatureSet::all_enabled()),
3191        )
3192        .is_ok());
3193        assert_eq!(vote_state.root_slot, expected_root);
3194        assert_eq!(
3195            vote_state
3196                .votes
3197                .into_iter()
3198                .map(|vote| vote.lockout)
3199                .collect::<Vec<Lockout>>(),
3200            expected_vote_state,
3201        );
3202    }
3203
3204    #[test]
3205    fn test_check_and_filter_proposed_vote_state_older_than_history_root() {
3206        // Test when `proposed_root` is in `current_vote_state_slots` but it's not the latest
3207        // slot
3208        let earliest_slot_in_history = 5;
3209        let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3210        let current_vote_state_root = None;
3211        let proposed_slots_and_lockouts = vec![(5, 1)];
3212        let proposed_root = 4;
3213        let expected_root = Some(4);
3214        let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3215        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3216            earliest_slot_in_history,
3217            current_vote_state_slots,
3218            current_vote_state_root,
3219            proposed_slots_and_lockouts,
3220            proposed_root,
3221            expected_root,
3222            expected_vote_state,
3223        );
3224
3225        // Test when `proposed_root` is in `current_vote_state_slots` but it's not the latest
3226        // slot and the `current_vote_state_root.is_some()`.
3227        let earliest_slot_in_history = 5;
3228        let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3229        let current_vote_state_root = Some(0);
3230        let proposed_slots_and_lockouts = vec![(5, 1)];
3231        let proposed_root = 4;
3232        let expected_root = Some(4);
3233        let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3234        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3235            earliest_slot_in_history,
3236            current_vote_state_slots,
3237            current_vote_state_root,
3238            proposed_slots_and_lockouts,
3239            proposed_root,
3240            expected_root,
3241            expected_vote_state,
3242        );
3243
3244        // Test when `proposed_root` is in `current_vote_state_slots` but it's not the latest
3245        // slot
3246        let earliest_slot_in_history = 5;
3247        let current_vote_state_slots: Vec<Slot> = vec![1, 2, 3, 4];
3248        let current_vote_state_root = Some(0);
3249        let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3250        let proposed_root = 3;
3251        let expected_root = Some(3);
3252        let expected_vote_state = vec![
3253            Lockout::new_with_confirmation_count(4, 2),
3254            Lockout::new_with_confirmation_count(5, 1),
3255        ];
3256        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3257            earliest_slot_in_history,
3258            current_vote_state_slots,
3259            current_vote_state_root,
3260            proposed_slots_and_lockouts,
3261            proposed_root,
3262            expected_root,
3263            expected_vote_state,
3264        );
3265
3266        // Test when `proposed_root` is not in `current_vote_state_slots`
3267        let earliest_slot_in_history = 5;
3268        let current_vote_state_slots: Vec<Slot> = vec![1, 2, 4];
3269        let current_vote_state_root = Some(0);
3270        let proposed_slots_and_lockouts = vec![(4, 2), (5, 1)];
3271        let proposed_root = 3;
3272        let expected_root = Some(2);
3273        let expected_vote_state = vec![
3274            Lockout::new_with_confirmation_count(4, 2),
3275            Lockout::new_with_confirmation_count(5, 1),
3276        ];
3277        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3278            earliest_slot_in_history,
3279            current_vote_state_slots,
3280            current_vote_state_root,
3281            proposed_slots_and_lockouts,
3282            proposed_root,
3283            expected_root,
3284            expected_vote_state,
3285        );
3286
3287        // Test when the `proposed_root` is smaller than all the slots in
3288        // `current_vote_state_slots`, no roots should be set.
3289        let earliest_slot_in_history = 4;
3290        let current_vote_state_slots: Vec<Slot> = vec![3, 4];
3291        let current_vote_state_root = None;
3292        let proposed_slots_and_lockouts = vec![(3, 3), (4, 2), (5, 1)];
3293        let proposed_root = 2;
3294        let expected_root = None;
3295        let expected_vote_state = vec![
3296            Lockout::new_with_confirmation_count(3, 3),
3297            Lockout::new_with_confirmation_count(4, 2),
3298            Lockout::new_with_confirmation_count(5, 1),
3299        ];
3300        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3301            earliest_slot_in_history,
3302            current_vote_state_slots,
3303            current_vote_state_root,
3304            proposed_slots_and_lockouts,
3305            proposed_root,
3306            expected_root,
3307            expected_vote_state,
3308        );
3309
3310        // Test when `current_vote_state_slots` is empty, no roots should be set
3311        let earliest_slot_in_history = 4;
3312        let current_vote_state_slots: Vec<Slot> = vec![];
3313        let current_vote_state_root = None;
3314        let proposed_slots_and_lockouts = vec![(5, 1)];
3315        let proposed_root = 2;
3316        let expected_root = None;
3317        let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
3318        run_test_check_and_filter_proposed_vote_state_older_than_history_root(
3319            earliest_slot_in_history,
3320            current_vote_state_slots,
3321            current_vote_state_root,
3322            proposed_slots_and_lockouts,
3323            proposed_root,
3324            expected_root,
3325            expected_vote_state,
3326        );
3327    }
3328
3329    #[test]
3330    fn test_check_and_filter_proposed_vote_state_slots_not_ordered() {
3331        let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3332        let vote_state = build_vote_state(vec![1], &slot_hashes);
3333
3334        // Test with a `TowerSync` where the slots are out of order
3335        let vote_slot = 3;
3336        let vote_slot_hash = slot_hashes
3337            .iter()
3338            .find(|(slot, _hash)| *slot == vote_slot)
3339            .unwrap()
3340            .1;
3341        let mut tower_sync = TowerSync::from(vec![(2, 2), (1, 3), (vote_slot, 1)]);
3342        tower_sync.hash = vote_slot_hash;
3343        assert_eq!(
3344            check_and_filter_proposed_vote_state(
3345                &vote_state,
3346                &mut tower_sync.lockouts,
3347                &mut tower_sync.root,
3348                tower_sync.hash,
3349                &slot_hashes
3350            ),
3351            Err(VoteError::SlotsNotOrdered),
3352        );
3353
3354        // Test with a `TowerSync` where there are multiples of the same slot
3355        let mut tower_sync = TowerSync::from(vec![(2, 2), (2, 2), (vote_slot, 1)]);
3356        tower_sync.hash = vote_slot_hash;
3357        assert_eq!(
3358            check_and_filter_proposed_vote_state(
3359                &vote_state,
3360                &mut tower_sync.lockouts,
3361                &mut tower_sync.root,
3362                tower_sync.hash,
3363                &slot_hashes
3364            ),
3365            Err(VoteError::SlotsNotOrdered),
3366        );
3367    }
3368
3369    #[test]
3370    fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered() {
3371        let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]);
3372        let mut vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes);
3373
3374        // Test with a `TowerSync` where there:
3375        // 1) Exists a slot less than `earliest_slot_in_history`
3376        // 2) This slot does not exist in the vote state already
3377        // This slot should be filtered out
3378        let earliest_slot_in_history = 11;
3379        let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3380        let vote_slot = 12;
3381        let vote_slot_hash = slot_hashes
3382            .iter()
3383            .find(|(slot, _hash)| *slot == vote_slot)
3384            .unwrap()
3385            .1;
3386        let missing_older_than_history_slot = earliest_slot_in_history - 1;
3387        let mut tower_sync = TowerSync::from(vec![
3388            (1, 4),
3389            (missing_older_than_history_slot, 2),
3390            (vote_slot, 3),
3391        ]);
3392        tower_sync.hash = vote_slot_hash;
3393        check_and_filter_proposed_vote_state(
3394            &vote_state,
3395            &mut tower_sync.lockouts,
3396            &mut tower_sync.root,
3397            tower_sync.hash,
3398            &slot_hashes,
3399        )
3400        .unwrap();
3401
3402        // Check the earlier slot was filtered out
3403        assert_eq!(
3404            tower_sync
3405                .clone()
3406                .lockouts
3407                .into_iter()
3408                .collect::<Vec<Lockout>>(),
3409            vec![
3410                Lockout::new_with_confirmation_count(1, 4),
3411                Lockout::new_with_confirmation_count(vote_slot, 3)
3412            ]
3413        );
3414        assert!(do_process_tower_sync(
3415            &mut vote_state,
3416            &slot_hashes,
3417            0,
3418            0,
3419            tower_sync,
3420            Some(&FeatureSet::all_enabled()),
3421        )
3422        .is_ok());
3423    }
3424
3425    #[test]
3426    fn test_check_and_filter_proposed_vote_state_older_than_history_slots_not_filtered() {
3427        let slot_hashes = build_slot_hashes(vec![4]);
3428        let mut vote_state = build_vote_state(vec![4], &slot_hashes);
3429
3430        // Test with a `TowerSync` where there:
3431        // 1) Exists a slot less than `earliest_slot_in_history`
3432        // 2) This slot exists in the vote state already
3433        // This slot should *NOT* be filtered out
3434        let earliest_slot_in_history = 11;
3435        let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3436        let vote_slot = 12;
3437        let vote_slot_hash = slot_hashes
3438            .iter()
3439            .find(|(slot, _hash)| *slot == vote_slot)
3440            .unwrap()
3441            .1;
3442        let existing_older_than_history_slot = 4;
3443        let mut tower_sync =
3444            TowerSync::from(vec![(existing_older_than_history_slot, 3), (vote_slot, 2)]);
3445        tower_sync.hash = vote_slot_hash;
3446        check_and_filter_proposed_vote_state(
3447            &vote_state,
3448            &mut tower_sync.lockouts,
3449            &mut tower_sync.root,
3450            tower_sync.hash,
3451            &slot_hashes,
3452        )
3453        .unwrap();
3454        // Check the earlier slot was *NOT* filtered out
3455        assert_eq!(tower_sync.lockouts.len(), 2);
3456        assert_eq!(
3457            tower_sync
3458                .clone()
3459                .lockouts
3460                .into_iter()
3461                .collect::<Vec<Lockout>>(),
3462            vec![
3463                Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3464                Lockout::new_with_confirmation_count(vote_slot, 2)
3465            ]
3466        );
3467        assert!(do_process_tower_sync(
3468            &mut vote_state,
3469            &slot_hashes,
3470            0,
3471            0,
3472            tower_sync,
3473            Some(&FeatureSet::all_enabled()),
3474        )
3475        .is_ok());
3476    }
3477
3478    #[test]
3479    fn test_check_and_filter_proposed_vote_state_older_than_history_slots_filtered_and_not_filtered(
3480    ) {
3481        let slot_hashes = build_slot_hashes(vec![6]);
3482        let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3483
3484        // Test with a `TowerSync` where there exists both a slot:
3485        // 1) Less than `earliest_slot_in_history`
3486        // 2) This slot exists in the vote state already
3487        // which should not be filtered
3488        //
3489        // AND a slot that
3490        //
3491        // 1) Less than `earliest_slot_in_history`
3492        // 2) This slot does not exist in the vote state already
3493        // which should be filtered
3494        let earliest_slot_in_history = 11;
3495        let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 12, 13, 14]);
3496        let vote_slot = 14;
3497        let vote_slot_hash = slot_hashes
3498            .iter()
3499            .find(|(slot, _hash)| *slot == vote_slot)
3500            .unwrap()
3501            .1;
3502
3503        let missing_older_than_history_slot = 4;
3504        let existing_older_than_history_slot = 6;
3505
3506        let mut tower_sync = TowerSync::from(vec![
3507            (missing_older_than_history_slot, 4),
3508            (existing_older_than_history_slot, 3),
3509            (12, 2),
3510            (vote_slot, 1),
3511        ]);
3512        tower_sync.hash = vote_slot_hash;
3513        check_and_filter_proposed_vote_state(
3514            &vote_state,
3515            &mut tower_sync.lockouts,
3516            &mut tower_sync.root,
3517            tower_sync.hash,
3518            &slot_hashes,
3519        )
3520        .unwrap();
3521        assert_eq!(tower_sync.lockouts.len(), 3);
3522        assert_eq!(
3523            tower_sync
3524                .clone()
3525                .lockouts
3526                .into_iter()
3527                .collect::<Vec<Lockout>>(),
3528            vec![
3529                Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
3530                Lockout::new_with_confirmation_count(12, 2),
3531                Lockout::new_with_confirmation_count(vote_slot, 1)
3532            ]
3533        );
3534        assert!(do_process_tower_sync(
3535            &mut vote_state,
3536            &slot_hashes,
3537            0,
3538            0,
3539            tower_sync,
3540            Some(&FeatureSet::all_enabled()),
3541        )
3542        .is_ok());
3543    }
3544
3545    #[test]
3546    fn test_check_and_filter_proposed_vote_state_slot_not_on_fork() {
3547        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3548        let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3549
3550        // Test with a `TowerSync` where there:
3551        // 1) Exists a slot not in the slot hashes history
3552        // 2) The slot is greater than the earliest slot in the history
3553        // Thus this slot is not part of the fork and the update should be rejected
3554        // with error `SlotsMismatch`
3555        let missing_vote_slot = 3;
3556
3557        // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
3558        // errors
3559        let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3560        let vote_slot_hash = slot_hashes
3561            .iter()
3562            .find(|(slot, _hash)| *slot == vote_slot)
3563            .unwrap()
3564            .1;
3565        let mut tower_sync = TowerSync::from(vec![(missing_vote_slot, 2), (vote_slot, 3)]);
3566        tower_sync.hash = vote_slot_hash;
3567        assert_eq!(
3568            check_and_filter_proposed_vote_state(
3569                &vote_state,
3570                &mut tower_sync.lockouts,
3571                &mut tower_sync.root,
3572                tower_sync.hash,
3573                &slot_hashes
3574            ),
3575            Err(VoteError::SlotsMismatch),
3576        );
3577
3578        // Test where some earlier vote slots exist in the history, but others don't
3579        let missing_vote_slot = 7;
3580        let mut tower_sync = TowerSync::from(vec![
3581            (2, 5),
3582            (4, 4),
3583            (6, 3),
3584            (missing_vote_slot, 2),
3585            (vote_slot, 1),
3586        ]);
3587        tower_sync.hash = vote_slot_hash;
3588        assert_eq!(
3589            check_and_filter_proposed_vote_state(
3590                &vote_state,
3591                &mut tower_sync.lockouts,
3592                &mut tower_sync.root,
3593                tower_sync.hash,
3594                &slot_hashes
3595            ),
3596            Err(VoteError::SlotsMismatch),
3597        );
3598    }
3599
3600    #[test]
3601    fn test_check_and_filter_proposed_vote_state_root_on_different_fork() {
3602        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3603        let vote_state = build_vote_state(vec![6], &slot_hashes);
3604
3605        // Test with a `TowerSync` where:
3606        // 1) The root is not present in slot hashes history
3607        // 2) The slot is greater than the earliest slot in the history
3608        // Thus this slot is not part of the fork and the update should be rejected
3609        // with error `RootOnDifferentFork`
3610        let new_root = 3;
3611
3612        // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
3613        // errors, but also this slot must be present in SlotHashes
3614        let vote_slot = 8;
3615        assert_eq!(vote_slot, slot_hashes.first().unwrap().0);
3616        let vote_slot_hash = slot_hashes
3617            .iter()
3618            .find(|(slot, _hash)| *slot == vote_slot)
3619            .unwrap()
3620            .1;
3621        let mut tower_sync = TowerSync::from(vec![(vote_slot, 1)]);
3622        tower_sync.hash = vote_slot_hash;
3623        tower_sync.root = Some(new_root);
3624        assert_eq!(
3625            check_and_filter_proposed_vote_state(
3626                &vote_state,
3627                &mut tower_sync.lockouts,
3628                &mut tower_sync.root,
3629                tower_sync.hash,
3630                &slot_hashes
3631            ),
3632            Err(VoteError::RootOnDifferentFork),
3633        );
3634    }
3635
3636    #[test]
3637    fn test_check_and_filter_proposed_vote_state_slot_newer_than_slot_history() {
3638        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3639        let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3640
3641        // Test with a `TowerSync` where there:
3642        // 1) The last slot in the update is a slot not in the slot hashes history
3643        // 2) The slot is greater than the newest slot in the slot history
3644        // Thus this slot is not part of the fork and the update should be rejected
3645        // with error `SlotsMismatch`
3646        let missing_vote_slot = slot_hashes.first().unwrap().0 + 1;
3647        let vote_slot_hash = Hash::new_unique();
3648        let mut tower_sync = TowerSync::from(vec![(8, 2), (missing_vote_slot, 3)]);
3649        tower_sync.hash = vote_slot_hash;
3650        assert_eq!(
3651            check_and_filter_proposed_vote_state(
3652                &vote_state,
3653                &mut tower_sync.lockouts,
3654                &mut tower_sync.root,
3655                tower_sync.hash,
3656                &slot_hashes
3657            ),
3658            Err(VoteError::SlotsMismatch),
3659        );
3660    }
3661
3662    #[test]
3663    fn test_check_and_filter_proposed_vote_state_slot_all_slot_hashes_in_update_ok() {
3664        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3665        let mut vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3666
3667        // Test with a `TowerSync` where every slot in the history is
3668        // in the update
3669
3670        // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
3671        // errors
3672        let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3673        let vote_slot_hash = slot_hashes
3674            .iter()
3675            .find(|(slot, _hash)| *slot == vote_slot)
3676            .unwrap()
3677            .1;
3678        let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3679        tower_sync.hash = vote_slot_hash;
3680        check_and_filter_proposed_vote_state(
3681            &vote_state,
3682            &mut tower_sync.lockouts,
3683            &mut tower_sync.root,
3684            tower_sync.hash,
3685            &slot_hashes,
3686        )
3687        .unwrap();
3688
3689        // Nothing in the update should have been filtered out
3690        assert_eq!(
3691            tower_sync
3692                .clone()
3693                .lockouts
3694                .into_iter()
3695                .collect::<Vec<Lockout>>(),
3696            vec![
3697                Lockout::new_with_confirmation_count(2, 4),
3698                Lockout::new_with_confirmation_count(4, 3),
3699                Lockout::new_with_confirmation_count(6, 2),
3700                Lockout::new_with_confirmation_count(vote_slot, 1)
3701            ]
3702        );
3703
3704        assert!(do_process_tower_sync(
3705            &mut vote_state,
3706            &slot_hashes,
3707            0,
3708            0,
3709            tower_sync,
3710            Some(&FeatureSet::all_enabled()),
3711        )
3712        .is_ok());
3713    }
3714
3715    #[test]
3716    fn test_check_and_filter_proposed_vote_state_slot_some_slot_hashes_in_update_ok() {
3717        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]);
3718        let mut vote_state = build_vote_state(vec![6], &slot_hashes);
3719
3720        // Test with a `TowerSync` where only some slots in the history are
3721        // in the update, and others slots in the history are missing.
3722
3723        // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
3724        // errors
3725        let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3726        let vote_slot_hash = slot_hashes
3727            .iter()
3728            .find(|(slot, _hash)| *slot == vote_slot)
3729            .unwrap()
3730            .1;
3731        let mut tower_sync = TowerSync::from(vec![(4, 2), (vote_slot, 1)]);
3732        tower_sync.hash = vote_slot_hash;
3733        check_and_filter_proposed_vote_state(
3734            &vote_state,
3735            &mut tower_sync.lockouts,
3736            &mut tower_sync.root,
3737            tower_sync.hash,
3738            &slot_hashes,
3739        )
3740        .unwrap();
3741
3742        // Nothing in the update should have been filtered out
3743        assert_eq!(
3744            tower_sync
3745                .clone()
3746                .lockouts
3747                .into_iter()
3748                .collect::<Vec<Lockout>>(),
3749            vec![
3750                Lockout::new_with_confirmation_count(4, 2),
3751                Lockout::new_with_confirmation_count(vote_slot, 1)
3752            ]
3753        );
3754
3755        // Because 6 from the original VoteState
3756        // should not have been popped off in the proposed state,
3757        // we should get a lockout conflict
3758        assert_eq!(
3759            do_process_tower_sync(
3760                &mut vote_state,
3761                &slot_hashes,
3762                0,
3763                0,
3764                tower_sync,
3765                Some(&FeatureSet::all_enabled())
3766            ),
3767            Err(VoteError::LockoutConflict)
3768        );
3769    }
3770
3771    #[test]
3772    fn test_check_and_filter_proposed_vote_state_slot_hash_mismatch() {
3773        let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]);
3774        let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes);
3775
3776        // Test with a `TowerSync` where the hash is mismatched
3777
3778        // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
3779        // errors
3780        let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
3781        let vote_slot_hash = Hash::new_unique();
3782        let mut tower_sync = TowerSync::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);
3783        tower_sync.hash = vote_slot_hash;
3784        assert_eq!(
3785            check_and_filter_proposed_vote_state(
3786                &vote_state,
3787                &mut tower_sync.lockouts,
3788                &mut tower_sync.root,
3789                tower_sync.hash,
3790                &slot_hashes,
3791            ),
3792            Err(VoteError::SlotHashMismatch),
3793        );
3794    }
3795
3796    #[test_case(0, true; "first slot")]
3797    #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3798    #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3799    #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3800    #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3801    fn test_epoch_half_check(slot: Slot, expected_allowed: bool) {
3802        let epoch_schedule = EpochSchedule::without_warmup();
3803        assert_eq!(
3804            is_commission_update_allowed(slot, &epoch_schedule),
3805            expected_allowed
3806        );
3807    }
3808
3809    #[test]
3810    fn test_warmup_epoch_half_check_with_warmup() {
3811        let epoch_schedule = EpochSchedule::default();
3812        let first_normal_slot = epoch_schedule.first_normal_slot;
3813        // first slot works
3814        assert!(is_commission_update_allowed(0, &epoch_schedule));
3815        // right before first normal slot works, since all warmup slots allow
3816        // commission updates
3817        assert!(is_commission_update_allowed(
3818            first_normal_slot - 1,
3819            &epoch_schedule
3820        ));
3821    }
3822
3823    #[test_case(0, true; "first slot")]
3824    #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")]
3825    #[test_case((DEFAULT_SLOTS_PER_EPOCH / 2).saturating_add(1), false; "halfway through epoch plus one")]
3826    #[test_case(DEFAULT_SLOTS_PER_EPOCH.saturating_sub(1), false; "last slot in epoch")]
3827    #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")]
3828    fn test_epoch_half_check_with_warmup(slot: Slot, expected_allowed: bool) {
3829        let epoch_schedule = EpochSchedule::default();
3830        let first_normal_slot = epoch_schedule.first_normal_slot;
3831        assert_eq!(
3832            is_commission_update_allowed(first_normal_slot.saturating_add(slot), &epoch_schedule),
3833            expected_allowed
3834        );
3835    }
3836}