solana_vote_program/vote_state/
mod.rs

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