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