patches crds vote-index assignment bug (#14438)

If tower is full, old votes are evicted from the front of the deque:
https://github.com/solana-labs/solana/blob/2074e407c/programs/vote/src/vote_state/mod.rs#L367-L373
whereas recent votes if expire are evicted from the back:
https://github.com/solana-labs/solana/blob/2074e407c/programs/vote/src/vote_state/mod.rs#L529-L537

As a result, from a single tower_index scalar, we cannot infer which crds-vote
should be overwritten:
https://github.com/solana-labs/solana/blob/2074e407c/core/src/crds_value.rs#L576

In addition there is an off by one bug in the existing code. tower_index is
bounded by MAX_LOCKOUT_HISTORY - 1:
https://github.com/solana-labs/solana/blob/2074e407c/core/src/consensus.rs#L382
So, it is at most 30, whereas MAX_VOTES is 32:
https://github.com/solana-labs/solana/blob/2074e407c/core/src/crds_value.rs#L29
Which means that this branch is never taken:
https://github.com/solana-labs/solana/blob/2074e407c/core/src/crds_value.rs#L590-L593
so crds table alwasys keeps 29 **oldest** votes by wallclock, and then
only overrides the 30st one each time. (i.e a tally of only two most
recent votes).
This commit is contained in:
behzad nouri
2021-01-21 13:08:07 +00:00
committed by GitHub
parent fcd72f309a
commit 8e581601d6
7 changed files with 289 additions and 132 deletions

View File

@ -429,6 +429,12 @@ impl VoteState {
self.last_lockout().map(|v| v.slot)
}
// Upto MAX_LOCKOUT_HISTORY many recent unexpired
// vote slots pushed onto the stack.
pub fn tower(&self) -> Vec<Slot> {
self.votes.iter().map(|v| v.slot).collect()
}
fn current_epoch(&self) -> Epoch {
if self.epoch_credits.is_empty() {
0