vote: encapsulate `Lockout` (#29753)

This commit is contained in:
Trent Nelson 2023-01-18 19:28:28 -07:00 committed by GitHub
parent 4973fe18f1
commit c4e43f1de4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 223 additions and 361 deletions

View File

@ -22,8 +22,8 @@ pub fn parse_vote(data: &[u8]) -> Result<VoteAccountType, ParseAccountError> {
.votes
.iter()
.map(|lockout| UiLockout {
slot: lockout.slot,
confirmation_count: lockout.confirmation_count,
slot: lockout.slot(),
confirmation_count: lockout.confirmation_count(),
})
.collect();
let authorized_voters = vote_state
@ -92,8 +92,8 @@ struct UiLockout {
impl From<&Lockout> for UiLockout {
fn from(lockout: &Lockout) -> Self {
Self {
slot: lockout.slot,
confirmation_count: lockout.confirmation_count,
slot: lockout.slot(),
confirmation_count: lockout.confirmation_count(),
}
}
}

View File

@ -1536,8 +1536,8 @@ pub struct CliLockout {
impl From<&Lockout> for CliLockout {
fn from(lockout: &Lockout) -> Self {
Self {
slot: lockout.slot,
confirmation_count: lockout.confirmation_count,
slot: lockout.slot(),
confirmation_count: lockout.confirmation_count(),
}
}
}

View File

@ -227,11 +227,11 @@ impl AggregateCommitmentService {
}
for vote in &vote_state.votes {
while ancestors[ancestors_index] <= vote.slot {
while ancestors[ancestors_index] <= vote.slot() {
commitment
.entry(ancestors[ancestors_index])
.or_insert_with(BlockCommitment::default)
.increase_confirmation_stake(vote.confirmation_count as usize, lamports);
.increase_confirmation_stake(vote.confirmation_count() as usize, lamports);
ancestors_index += 1;
if ancestors_index == ancestors.len() {

View File

@ -304,22 +304,22 @@ impl Tower {
lockout_intervals
.entry(vote.last_locked_out_slot())
.or_insert_with(Vec::new)
.push((vote.slot, key));
.push((vote.slot(), key));
}
if key == *vote_account_pubkey {
my_latest_landed_vote = vote_state.nth_recent_vote(0).map(|v| v.slot);
my_latest_landed_vote = vote_state.nth_recent_vote(0).map(|v| v.slot());
debug!("vote state {:?}", vote_state);
debug!(
"observed slot {}",
vote_state.nth_recent_vote(0).map(|v| v.slot).unwrap_or(0) as i64
vote_state.nth_recent_vote(0).map(|v| v.slot()).unwrap_or(0) as i64
);
debug!("observed root {}", vote_state.root_slot.unwrap_or(0) as i64);
datapoint_info!(
"tower-observed",
(
"slot",
vote_state.nth_recent_vote(0).map(|v| v.slot).unwrap_or(0),
vote_state.nth_recent_vote(0).map(|v| v.slot()).unwrap_or(0),
i64
),
("root", vote_state.root_slot.unwrap_or(0), i64)
@ -341,27 +341,22 @@ impl Tower {
for vote in &vote_state.votes {
bank_weight += vote.lockout() as u128 * voted_stake as u128;
vote_slots.insert(vote.slot);
vote_slots.insert(vote.slot());
}
if start_root != vote_state.root_slot {
if let Some(root) = start_root {
let vote = Lockout {
confirmation_count: MAX_LOCKOUT_HISTORY as u32,
slot: root,
};
trace!("ROOT: {}", vote.slot);
let vote =
Lockout::new_with_confirmation_count(root, MAX_LOCKOUT_HISTORY as u32);
trace!("ROOT: {}", vote.slot());
bank_weight += vote.lockout() as u128 * voted_stake as u128;
vote_slots.insert(vote.slot);
vote_slots.insert(vote.slot());
}
}
if let Some(root) = vote_state.root_slot {
let vote = Lockout {
confirmation_count: MAX_LOCKOUT_HISTORY as u32,
slot: root,
};
let vote = Lockout::new_with_confirmation_count(root, MAX_LOCKOUT_HISTORY as u32);
bank_weight += vote.lockout() as u128 * voted_stake as u128;
vote_slots.insert(vote.slot);
vote_slots.insert(vote.slot());
}
// The last vote in the vote stack is a simulated vote on bank_slot, which
@ -374,14 +369,14 @@ impl Tower {
// this vote stack is the simulated vote, so this fetch should be sufficient
// to find the last unsimulated vote.
assert_eq!(
vote_state.nth_recent_vote(0).map(|l| l.slot),
vote_state.nth_recent_vote(0).map(|l| l.slot()),
Some(bank_slot)
);
if let Some(vote) = vote_state.nth_recent_vote(1) {
// Update all the parents of this last vote with the stake of this vote account
Self::update_ancestor_voted_stakes(
&mut voted_stakes,
vote.slot,
vote.slot(),
voted_stake,
ancestors,
);
@ -443,11 +438,11 @@ impl Tower {
local_vote_state
.votes
.iter()
.map(|v| v.slot)
.map(|v| v.slot())
.skip_while(|s| *s <= last_voted_slot)
.collect()
} else {
local_vote_state.votes.iter().map(|v| v.slot).collect()
local_vote_state.votes.iter().map(|v| v.slot()).collect()
};
VoteTransaction::from(Vote::new(slots, hash))
}
@ -523,7 +518,7 @@ impl Tower {
/// Used for tests
pub fn increase_lockout(&mut self, confirmation_count_increase: u32) {
for vote in self.vote_state.votes.iter_mut() {
vote.confirmation_count += confirmation_count_increase;
vote.increase_confirmation_count(confirmation_count_increase);
}
}
@ -591,7 +586,7 @@ impl Tower {
pub fn has_voted(&self, slot: Slot) -> bool {
for vote in &self.vote_state.votes {
if slot == vote.slot {
if slot == vote.slot() {
return true;
}
}
@ -610,7 +605,7 @@ impl Tower {
let mut vote_state = self.vote_state.clone();
process_slot_vote_unchecked(&mut vote_state, slot);
for vote in &vote_state.votes {
if slot != vote.slot && !ancestors.contains(&vote.slot) {
if slot != vote.slot() && !ancestors.contains(&vote.slot()) {
return true;
}
}
@ -993,16 +988,16 @@ impl Tower {
process_slot_vote_unchecked(&mut vote_state, slot);
let vote = vote_state.nth_recent_vote(self.threshold_depth);
if let Some(vote) = vote {
if let Some(fork_stake) = voted_stakes.get(&vote.slot) {
if let Some(fork_stake) = voted_stakes.get(&vote.slot()) {
let lockout = *fork_stake as f64 / total_stake as f64;
trace!(
"fork_stake slot: {}, vote slot: {}, lockout: {} fork_stake: {} total_stake: {}",
slot, vote.slot, lockout, fork_stake, total_stake
slot, vote.slot(), lockout, fork_stake, total_stake
);
if vote.confirmation_count as usize > self.threshold_depth {
if vote.confirmation_count() as usize > self.threshold_depth {
for old_vote in &self.vote_state.votes {
if old_vote.slot == vote.slot
&& old_vote.confirmation_count == vote.confirmation_count
if old_vote.slot() == vote.slot()
&& old_vote.confirmation_count() == vote.confirmation_count()
{
return true;
}
@ -1058,7 +1053,7 @@ impl Tower {
self.vote_state
.votes
.iter()
.map(|lockout| lockout.slot)
.map(|lockout| lockout.slot())
.collect()
}
@ -1270,7 +1265,7 @@ impl Tower {
.expect("vote_account isn't a VoteState?")
.clone();
self.initialize_root(root);
self.initialize_lockouts(|v| v.slot > root);
self.initialize_lockouts(|v| v.slot() > root);
trace!(
"Lockouts in tower for {} is initialized using bank {}",
self.vote_state.node_pubkey,
@ -1554,9 +1549,9 @@ pub mod test {
}
for i in 1..5 {
assert_eq!(tower.vote_state.votes[i - 1].slot as usize, i);
assert_eq!(tower.vote_state.votes[i - 1].slot() as usize, i);
assert_eq!(
tower.vote_state.votes[i - 1].confirmation_count as usize,
tower.vote_state.votes[i - 1].confirmation_count() as usize,
6 - i
);
}
@ -2166,10 +2161,7 @@ pub mod test {
tower.record_vote(i as u64, Hash::default());
ancestors.insert(i as u64, (0..i as u64).collect());
}
let root = Lockout {
confirmation_count: MAX_LOCKOUT_HISTORY as u32,
slot: 0,
};
let root = Lockout::new_with_confirmation_count(0, MAX_LOCKOUT_HISTORY as u32);
let root_weight = root.lockout() as u128;
let vote_account_expected_weight = tower
.vote_state
@ -2200,7 +2192,8 @@ pub mod test {
// should be the sum of all the weights for root
assert_eq!(bank_weight, expected_bank_weight);
let mut new_votes = latest_validator_votes_for_frozen_banks.take_votes_dirty_set(root.slot);
let mut new_votes =
latest_validator_votes_for_frozen_banks.take_votes_dirty_set(root.slot());
new_votes.sort();
assert_eq!(new_votes, account_latest_votes);
}
@ -2325,10 +2318,10 @@ pub mod test {
tower.record_vote(1, Hash::default());
assert!(!tower.is_locked_out(4, &ancestors));
tower.record_vote(4, Hash::default());
assert_eq!(tower.vote_state.votes[0].slot, 0);
assert_eq!(tower.vote_state.votes[0].confirmation_count, 2);
assert_eq!(tower.vote_state.votes[1].slot, 4);
assert_eq!(tower.vote_state.votes[1].confirmation_count, 1);
assert_eq!(tower.vote_state.votes[0].slot(), 0);
assert_eq!(tower.vote_state.votes[0].confirmation_count(), 2);
assert_eq!(tower.vote_state.votes[1].slot(), 4);
assert_eq!(tower.vote_state.votes[1].confirmation_count(), 1);
}
#[test]
@ -2515,9 +2508,8 @@ pub mod test {
let mut tower = Tower::new_for_tests(1, 0.67);
let slots = if num_votes > 0 {
{ 0..num_votes }
.map(|i| Lockout {
slot: i as u64,
confirmation_count: (num_votes as u32) - (i as u32),
.map(|i| {
Lockout::new_with_confirmation_count(i as Slot, (num_votes as u32) - (i as u32))
})
.collect()
} else {

View File

@ -2840,7 +2840,7 @@ impl ReplayStage {
bank_vote_state.root_slot = Some(local_root);
bank_vote_state
.votes
.retain(|lockout| lockout.slot > local_root);
.retain(|lockout| lockout.slot() > local_root);
info!(
"Local root is larger than on chain root,
overwrote bank root {:?} and updated votes {:?}",
@ -2849,7 +2849,7 @@ impl ReplayStage {
if let Some(first_vote) = bank_vote_state.votes.front() {
assert!(ancestors
.get(&first_vote.slot)
.get(&first_vote.slot())
.expect(
"Ancestors map must contain an
entry for all slots on this fork

View File

@ -713,9 +713,8 @@ mod tests {
// Now send some new votes
for i in 101..201 {
let slots = std::iter::zip((i - 30)..(i + 1), (1..32).rev())
.map(|(slot, confirmation_count)| Lockout {
slot,
confirmation_count,
.map(|(slot, confirmation_count)| {
Lockout::new_with_confirmation_count(slot, confirmation_count)
})
.collect::<VecDeque<Lockout>>();
let vote = VoteTransaction::from(VoteStateUpdate::new(

View File

@ -111,7 +111,7 @@ impl VoteSimulator {
.unwrap()
.votes
.iter()
.any(|lockout| lockout.slot == parent));
.any(|lockout| lockout.slot() == parent));
}
}
while new_bank.tick_height() < new_bank.max_tick_height() {

View File

@ -516,13 +516,13 @@ fn graph_forks(bank_forks: &BankForks, config: &GraphConfig) -> String {
let vote_state = vote_state.as_ref().unwrap_or(&default_vote_state);
if let Some(last_vote) = vote_state.votes.iter().last() {
let entry = last_votes.entry(vote_state.node_pubkey).or_insert((
last_vote.slot,
last_vote.slot(),
vote_state.clone(),
*stake,
total_stake,
));
if entry.0 < last_vote.slot {
*entry = (last_vote.slot, vote_state.clone(), *stake, total_stake);
if entry.0 < last_vote.slot() {
*entry = (last_vote.slot(), vote_state.clone(), *stake, total_stake);
}
}
}
@ -558,7 +558,7 @@ fn graph_forks(bank_forks: &BankForks, config: &GraphConfig) -> String {
if let Some(last_vote) = vote_state.votes.iter().last() {
let validator_votes = all_votes.entry(vote_state.node_pubkey).or_default();
validator_votes
.entry(last_vote.slot)
.entry(last_vote.slot())
.or_insert_with(|| vote_state.clone());
}
}
@ -662,7 +662,8 @@ fn graph_forks(bank_forks: &BankForks, config: &GraphConfig) -> String {
.iter()
.map(|vote| format!(
"slot {} (conf={})",
vote.slot, vote.confirmation_count
vote.slot(),
vote.confirmation_count()
))
.collect::<Vec<_>>()
.join("\n")
@ -673,7 +674,7 @@ fn graph_forks(bank_forks: &BankForks, config: &GraphConfig) -> String {
vote_state
.votes
.back()
.map(|vote| vote.slot.to_string())
.map(|vote| vote.slot().to_string())
.unwrap_or_else(|| "none".to_string())
)
};
@ -721,7 +722,7 @@ fn graph_forks(bank_forks: &BankForks, config: &GraphConfig) -> String {
vote_state
.votes
.iter()
.map(|vote| format!("slot {} (conf={})", vote.slot, vote.confirmation_count))
.map(|vote| format!("slot {} (conf={})", vote.slot(), vote.confirmation_count()))
.collect::<Vec<_>>()
.join("\n")
));

View File

@ -51,7 +51,7 @@ impl VoteTransaction {
VoteTransaction::Vote(vote) => vote.slots[i],
VoteTransaction::VoteStateUpdate(vote_state_update)
| VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
vote_state_update.lockouts[i].slot
vote_state_update.lockouts[i].slot()
}
}
}
@ -152,8 +152,8 @@ fn check_update_vote_state_slots_are_valid(
}
// If the vote state update is not new enough, return
if let Some(last_vote_slot) = vote_state.votes.back().map(|lockout| lockout.slot) {
if vote_state_update.lockouts.back().unwrap().slot <= last_vote_slot {
if let Some(last_vote_slot) = vote_state.votes.back().map(|lockout| lockout.slot()) {
if vote_state_update.lockouts.back().unwrap().slot() <= last_vote_slot {
return Err(VoteError::VoteTooOld);
}
}
@ -162,7 +162,7 @@ fn check_update_vote_state_slots_are_valid(
.lockouts
.back()
.expect("must be nonempty, checked above")
.slot;
.slot();
if slot_hashes.is_empty() {
return Err(VoteError::SlotsMismatch);
@ -193,16 +193,16 @@ fn check_update_vote_state_slots_are_valid(
let current_root = vote_state_update.root;
for lockout in vote_state.votes.iter().rev() {
let is_slot_bigger_than_root = current_root
.map(|current_root| lockout.slot > current_root)
.map(|current_root| lockout.slot() > current_root)
.unwrap_or(true);
// Ensure we're iterating from biggest to smallest vote in the
// current vote state
assert!(lockout.slot < prev_slot && is_slot_bigger_than_root);
if lockout.slot <= new_proposed_root {
vote_state_update.root = Some(lockout.slot);
assert!(lockout.slot() < prev_slot && is_slot_bigger_than_root);
if lockout.slot() <= new_proposed_root {
vote_state_update.root = Some(lockout.slot());
break;
}
prev_slot = lockout.slot;
prev_slot = lockout.slot();
}
}
}
@ -236,11 +236,11 @@ fn check_update_vote_state_slots_are_valid(
let proposed_vote_slot = if let Some(root) = root_to_check {
root
} else {
vote_state_update.lockouts[vote_state_update_index].slot
vote_state_update.lockouts[vote_state_update_index].slot()
};
if root_to_check.is_none()
&& vote_state_update_index > 0
&& proposed_vote_slot <= vote_state_update.lockouts[vote_state_update_index - 1].slot
&& proposed_vote_slot <= vote_state_update.lockouts[vote_state_update_index - 1].slot()
{
return Err(VoteError::SlotsNotOrdered);
}
@ -530,12 +530,12 @@ pub fn process_new_vote_state(
// 2) The confirmations are strictly decreasing
// 3) Not zero confirmation votes
for vote in &new_state {
if vote.confirmation_count == 0 {
if vote.confirmation_count() == 0 {
return Err(VoteError::ZeroConfirmations);
} else if vote.confirmation_count > MAX_LOCKOUT_HISTORY as u32 {
} else if vote.confirmation_count() > MAX_LOCKOUT_HISTORY as u32 {
return Err(VoteError::ConfirmationTooLarge);
} else if let Some(new_root) = new_root {
if vote.slot <= new_root
if vote.slot() <= new_root
&&
// This check is necessary because
// https://github.com/ryoqun/solana/blob/df55bfb46af039cbc597cd60042d49b9d90b5961/core/src/consensus.rs#L120
@ -548,11 +548,11 @@ pub fn process_new_vote_state(
}
if let Some(previous_vote) = previous_vote {
if previous_vote.slot >= vote.slot {
if previous_vote.slot() >= vote.slot() {
return Err(VoteError::SlotsNotOrdered);
} else if previous_vote.confirmation_count <= vote.confirmation_count {
} else if previous_vote.confirmation_count() <= vote.confirmation_count() {
return Err(VoteError::ConfirmationsNotOrdered);
} else if vote.slot > previous_vote.last_locked_out_slot() {
} else if vote.slot() > previous_vote.last_locked_out_slot() {
return Err(VoteError::NewVoteStateLockoutMismatch);
}
}
@ -576,9 +576,9 @@ pub fn process_new_vote_state(
for current_vote in &vote_state.votes {
// Find the first vote in the current vote state for a slot greater
// than the new proposed root
if current_vote.slot <= new_root {
if current_vote.slot() <= new_root {
current_vote_state_index += 1;
if current_vote.slot != new_root {
if current_vote.slot() != new_root {
finalized_slot_count += 1;
}
continue;
@ -599,9 +599,9 @@ pub fn process_new_vote_state(
// If the current slot is less than the new proposed slot, then the
// new slot must have popped off the old slot, so check that the
// lockouts are corrects.
match current_vote.slot.cmp(&new_vote.slot) {
match current_vote.slot().cmp(&new_vote.slot()) {
Ordering::Less => {
if current_vote.last_locked_out_slot() >= new_vote.slot {
if current_vote.last_locked_out_slot() >= new_vote.slot() {
return Err(VoteError::LockoutConflict);
}
current_vote_state_index += 1;
@ -609,7 +609,7 @@ pub fn process_new_vote_state(
Ordering::Equal => {
// The new vote state should never have less lockout than
// the previous vote state for the same slot
if new_vote.confirmation_count < current_vote.confirmation_count {
if new_vote.confirmation_count() < current_vote.confirmation_count() {
return Err(VoteError::ConfirmationRollBack);
}
@ -640,7 +640,7 @@ pub fn process_new_vote_state(
}
}
if let Some(timestamp) = timestamp {
let last_slot = new_state.back().unwrap().slot;
let last_slot = new_state.back().unwrap().slot();
vote_state.process_timestamp(last_slot, timestamp)?;
}
vote_state.root_slot = new_root;
@ -1095,7 +1095,7 @@ mod tests {
// One more vote that confirms the entire stack,
// the root_slot should change to the
// second vote
let top_vote = vote_state.votes.front().unwrap().slot;
let top_vote = vote_state.votes.front().unwrap().slot();
let slot = vote_state.last_lockout().unwrap().last_locked_out_slot();
process_slot_vote_unchecked(&mut vote_state, slot);
assert_eq!(Some(top_vote), vote_state.root_slot);
@ -1144,26 +1144,26 @@ mod tests {
process_slot_vote_unchecked(&mut vote_state, i as u64);
}
assert_eq!(vote_state.votes[0].confirmation_count, 3);
assert_eq!(vote_state.votes[0].confirmation_count(), 3);
// Expire the second and third votes
let expire_slot = vote_state.votes[1].slot + vote_state.votes[1].lockout() + 1;
let expire_slot = vote_state.votes[1].slot() + vote_state.votes[1].lockout() + 1;
process_slot_vote_unchecked(&mut vote_state, expire_slot);
assert_eq!(vote_state.votes.len(), 2);
// Check that the old votes expired
assert_eq!(vote_state.votes[0].slot, 0);
assert_eq!(vote_state.votes[1].slot, expire_slot);
assert_eq!(vote_state.votes[0].slot(), 0);
assert_eq!(vote_state.votes[1].slot(), expire_slot);
// Process one more vote
process_slot_vote_unchecked(&mut vote_state, expire_slot + 1);
// Confirmation count for the older first vote should remain unchanged
assert_eq!(vote_state.votes[0].confirmation_count, 3);
assert_eq!(vote_state.votes[0].confirmation_count(), 3);
// The later votes should still have increasing confirmation counts
assert_eq!(vote_state.votes[1].confirmation_count, 2);
assert_eq!(vote_state.votes[2].confirmation_count, 1);
assert_eq!(vote_state.votes[1].confirmation_count(), 2);
assert_eq!(vote_state.votes[2].confirmation_count(), 1);
}
#[test]
@ -1192,8 +1192,8 @@ mod tests {
process_slot_vote_unchecked(&mut vote_state, 0);
process_slot_vote_unchecked(&mut vote_state, 1);
process_slot_vote_unchecked(&mut vote_state, 0);
assert_eq!(vote_state.nth_recent_vote(0).unwrap().slot, 1);
assert_eq!(vote_state.nth_recent_vote(1).unwrap().slot, 0);
assert_eq!(vote_state.nth_recent_vote(0).unwrap().slot(), 1);
assert_eq!(vote_state.nth_recent_vote(1).unwrap().slot(), 0);
assert!(vote_state.nth_recent_vote(2).is_none());
}
@ -1206,7 +1206,7 @@ mod tests {
}
for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
assert_eq!(
vote_state.nth_recent_vote(i).unwrap().slot as usize,
vote_state.nth_recent_vote(i).unwrap().slot() as usize,
MAX_LOCKOUT_HISTORY - i - 1,
);
}
@ -1223,7 +1223,12 @@ mod tests {
fn recent_votes(vote_state: &VoteState) -> Vec<Vote> {
let start = vote_state.votes.len().saturating_sub(MAX_RECENT_VOTES);
(start..vote_state.votes.len())
.map(|i| Vote::new(vec![vote_state.votes.get(i).unwrap().slot], Hash::default()))
.map(|i| {
Vote::new(
vec![vote_state.votes.get(i).unwrap().slot()],
Hash::default(),
)
})
.collect()
}
@ -1511,9 +1516,11 @@ mod tests {
fn test_process_new_vote_too_many_votes() {
let mut vote_state1 = VoteState::default();
let bad_votes: VecDeque<Lockout> = (0..=MAX_LOCKOUT_HISTORY)
.map(|slot| Lockout {
slot: slot as Slot,
confirmation_count: (MAX_LOCKOUT_HISTORY - slot + 1) as u32,
.map(|slot| {
Lockout::new_with_confirmation_count(
slot as Slot,
(MAX_LOCKOUT_HISTORY - slot + 1) as u32,
)
})
.collect();
@ -1574,14 +1581,8 @@ mod tests {
let current_epoch = vote_state1.current_epoch();
let bad_votes: VecDeque<Lockout> = vec![
Lockout {
slot: 0,
confirmation_count: 0,
},
Lockout {
slot: 1,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(0, 0),
Lockout::new_with_confirmation_count(1, 1),
]
.into_iter()
.collect();
@ -1591,14 +1592,8 @@ mod tests {
);
let bad_votes: VecDeque<Lockout> = vec![
Lockout {
slot: 0,
confirmation_count: 2,
},
Lockout {
slot: 1,
confirmation_count: 0,
},
Lockout::new_with_confirmation_count(0, 2),
Lockout::new_with_confirmation_count(1, 0),
]
.into_iter()
.collect();
@ -1613,10 +1608,10 @@ mod tests {
let mut vote_state1 = VoteState::default();
let current_epoch = vote_state1.current_epoch();
let good_votes: VecDeque<Lockout> = vec![Lockout {
slot: 0,
confirmation_count: MAX_LOCKOUT_HISTORY as u32,
}]
let good_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
0,
MAX_LOCKOUT_HISTORY as u32,
)]
.into_iter()
.collect();
@ -1631,10 +1626,10 @@ mod tests {
.unwrap();
let mut vote_state1 = VoteState::default();
let bad_votes: VecDeque<Lockout> = vec![Lockout {
slot: 0,
confirmation_count: MAX_LOCKOUT_HISTORY as u32 + 1,
}]
let bad_votes: VecDeque<Lockout> = vec![Lockout::new_with_confirmation_count(
0,
MAX_LOCKOUT_HISTORY as u32 + 1,
)]
.into_iter()
.collect();
assert_eq!(
@ -1650,14 +1645,8 @@ mod tests {
let root_slot = 5;
let bad_votes: VecDeque<Lockout> = vec![
Lockout {
slot: root_slot,
confirmation_count: 2,
},
Lockout {
slot: root_slot + 1,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(root_slot, 2),
Lockout::new_with_confirmation_count(root_slot + 1, 1),
]
.into_iter()
.collect();
@ -1674,14 +1663,8 @@ mod tests {
);
let bad_votes: VecDeque<Lockout> = vec![
Lockout {
slot: root_slot - 1,
confirmation_count: 2,
},
Lockout {
slot: root_slot + 1,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(root_slot - 1, 2),
Lockout::new_with_confirmation_count(root_slot + 1, 1),
]
.into_iter()
.collect();
@ -1704,14 +1687,8 @@ mod tests {
let current_epoch = vote_state1.current_epoch();
let bad_votes: VecDeque<Lockout> = vec![
Lockout {
slot: 1,
confirmation_count: 2,
},
Lockout {
slot: 0,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(1, 2),
Lockout::new_with_confirmation_count(0, 1),
]
.into_iter()
.collect();
@ -1721,14 +1698,8 @@ mod tests {
);
let bad_votes: VecDeque<Lockout> = vec![
Lockout {
slot: 1,
confirmation_count: 2,
},
Lockout {
slot: 1,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(1, 2),
Lockout::new_with_confirmation_count(1, 1),
]
.into_iter()
.collect();
@ -1744,14 +1715,8 @@ mod tests {
let current_epoch = vote_state1.current_epoch();
let bad_votes: VecDeque<Lockout> = vec![
Lockout {
slot: 0,
confirmation_count: 1,
},
Lockout {
slot: 1,
confirmation_count: 2,
},
Lockout::new_with_confirmation_count(0, 1),
Lockout::new_with_confirmation_count(1, 2),
]
.into_iter()
.collect();
@ -1761,14 +1726,8 @@ mod tests {
);
let bad_votes: VecDeque<Lockout> = vec![
Lockout {
slot: 0,
confirmation_count: 1,
},
Lockout {
slot: 1,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(0, 1),
Lockout::new_with_confirmation_count(1, 1),
]
.into_iter()
.collect();
@ -1784,14 +1743,8 @@ mod tests {
let current_epoch = vote_state1.current_epoch();
let bad_votes: VecDeque<Lockout> = vec![
Lockout {
slot: 0,
confirmation_count: 2,
},
Lockout {
slot: 7,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(0, 2),
Lockout::new_with_confirmation_count(7, 1),
]
.into_iter()
.collect();
@ -1808,33 +1761,18 @@ mod tests {
let mut vote_state1 = VoteState::default();
let current_epoch = vote_state1.current_epoch();
let votes: VecDeque<Lockout> = vec![
Lockout {
slot: 0,
confirmation_count: 4,
},
Lockout {
slot: 1,
confirmation_count: 3,
},
Lockout::new_with_confirmation_count(0, 4),
Lockout::new_with_confirmation_count(1, 3),
]
.into_iter()
.collect();
process_new_vote_state(&mut vote_state1, votes, None, None, current_epoch, None).unwrap();
let votes: VecDeque<Lockout> = vec![
Lockout {
slot: 0,
confirmation_count: 4,
},
Lockout {
slot: 1,
// Confirmation count lowered illegally
confirmation_count: 2,
},
Lockout {
slot: 2,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(0, 4),
// Confirmation count lowered illegally
Lockout::new_with_confirmation_count(1, 2),
Lockout::new_with_confirmation_count(2, 1),
]
.into_iter()
.collect();
@ -1906,7 +1844,7 @@ mod tests {
vote_state1
.votes
.iter()
.map(|vote| vote.slot)
.map(|vote| vote.slot())
.collect::<Vec<Slot>>(),
vec![1, 5]
);
@ -1918,7 +1856,7 @@ mod tests {
vote_state2
.votes
.iter()
.map(|vote| vote.slot)
.map(|vote| vote.slot())
.collect::<Vec<Slot>>(),
vec![1, 2, 3, 5, 7]
);
@ -1946,7 +1884,7 @@ mod tests {
vote_state1
.votes
.iter()
.map(|vote| vote.slot)
.map(|vote| vote.slot())
.collect::<Vec<Slot>>(),
vec![1, 2, 4, 5]
);
@ -1959,7 +1897,7 @@ mod tests {
vote_state2
.votes
.iter()
.map(|vote| vote.slot)
.map(|vote| vote.slot())
.collect::<Vec<Slot>>(),
vec![1, 2, 3, 5, 7]
);
@ -1987,7 +1925,7 @@ mod tests {
vote_state1
.votes
.iter()
.map(|vote| vote.slot)
.map(|vote| vote.slot())
.collect::<Vec<Slot>>(),
vec![1, 5, 6, 7]
);
@ -2000,7 +1938,7 @@ mod tests {
vote_state2
.votes
.iter()
.map(|vote| vote.slot)
.map(|vote| vote.slot())
.collect::<Vec<Slot>>(),
vec![1, 2, 3, 5, 6, 8]
);
@ -2029,7 +1967,7 @@ mod tests {
vote_state1
.votes
.iter()
.map(|vote| vote.slot)
.map(|vote| vote.slot())
.collect::<Vec<Slot>>(),
vec![1, 9]
);
@ -2041,13 +1979,13 @@ mod tests {
// Slot 1 has been expired by 10, but is kept alive by its descendant
// 9 which has not been expired yet.
assert_eq!(vote_state2.votes[0].slot, 1);
assert_eq!(vote_state2.votes[0].slot(), 1);
assert_eq!(vote_state2.votes[0].last_locked_out_slot(), 9);
assert_eq!(
vote_state2
.votes
.iter()
.map(|vote| vote.slot)
.map(|vote| vote.slot())
.collect::<Vec<Slot>>(),
vec![1, 9, 10]
);
@ -2073,22 +2011,16 @@ mod tests {
vote_state1
.votes
.iter()
.map(|vote| vote.slot)
.map(|vote| vote.slot())
.collect::<Vec<Slot>>(),
vec![6, 7, 8]
);
// Try to process something with lockout violations
let bad_votes: VecDeque<Lockout> = vec![
Lockout {
slot: 2,
confirmation_count: 5,
},
Lockout {
// Slot 14 could not have popped off slot 6 yet
slot: 14,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(2, 5),
// Slot 14 could not have popped off slot 6 yet
Lockout::new_with_confirmation_count(14, 1),
]
.into_iter()
.collect();
@ -2101,14 +2033,8 @@ mod tests {
);
let good_votes: VecDeque<Lockout> = vec![
Lockout {
slot: 2,
confirmation_count: 5,
},
Lockout {
slot: 15,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(2, 5),
Lockout::new_with_confirmation_count(15, 1),
]
.into_iter()
.collect();
@ -2157,10 +2083,7 @@ mod tests {
process_vote(&mut vote_state, &vote, &slot_hashes, 0, Some(&feature_set)).unwrap();
assert_eq!(
vote_state.votes.into_iter().collect::<Vec<Lockout>>(),
vec![Lockout {
slot: vote_slot,
confirmation_count: 1,
}]
vec![Lockout::new_with_confirmation_count(vote_slot, 1)]
);
}
@ -2341,10 +2264,7 @@ mod tests {
let vote_state_update_slots_and_lockouts = vec![(5, 1)];
let vote_state_update_root = 4;
let expected_root = Some(4);
let expected_vote_state = vec![Lockout {
slot: 5,
confirmation_count: 1,
}];
let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
run_test_check_update_vote_state_older_than_history_root(
earliest_slot_in_history,
current_vote_state_slots,
@ -2363,10 +2283,7 @@ mod tests {
let vote_state_update_slots_and_lockouts = vec![(5, 1)];
let vote_state_update_root = 4;
let expected_root = Some(4);
let expected_vote_state = vec![Lockout {
slot: 5,
confirmation_count: 1,
}];
let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
run_test_check_update_vote_state_older_than_history_root(
earliest_slot_in_history,
current_vote_state_slots,
@ -2386,14 +2303,8 @@ mod tests {
let vote_state_update_root = 3;
let expected_root = Some(3);
let expected_vote_state = vec![
Lockout {
slot: 4,
confirmation_count: 2,
},
Lockout {
slot: 5,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(4, 2),
Lockout::new_with_confirmation_count(5, 1),
];
run_test_check_update_vote_state_older_than_history_root(
earliest_slot_in_history,
@ -2413,14 +2324,8 @@ mod tests {
let vote_state_update_root = 3;
let expected_root = Some(2);
let expected_vote_state = vec![
Lockout {
slot: 4,
confirmation_count: 2,
},
Lockout {
slot: 5,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(4, 2),
Lockout::new_with_confirmation_count(5, 1),
];
run_test_check_update_vote_state_older_than_history_root(
earliest_slot_in_history,
@ -2441,18 +2346,9 @@ mod tests {
let vote_state_update_root = 2;
let expected_root = None;
let expected_vote_state = vec![
Lockout {
slot: 3,
confirmation_count: 3,
},
Lockout {
slot: 4,
confirmation_count: 2,
},
Lockout {
slot: 5,
confirmation_count: 1,
},
Lockout::new_with_confirmation_count(3, 3),
Lockout::new_with_confirmation_count(4, 2),
Lockout::new_with_confirmation_count(5, 1),
];
run_test_check_update_vote_state_older_than_history_root(
earliest_slot_in_history,
@ -2471,10 +2367,7 @@ mod tests {
let vote_state_update_slots_and_lockouts = vec![(5, 1)];
let vote_state_update_root = 2;
let expected_root = None;
let expected_vote_state = vec![Lockout {
slot: 5,
confirmation_count: 1,
}];
let expected_vote_state = vec![Lockout::new_with_confirmation_count(5, 1)];
run_test_check_update_vote_state_older_than_history_root(
earliest_slot_in_history,
current_vote_state_slots,
@ -2564,14 +2457,8 @@ mod tests {
.into_iter()
.collect::<Vec<Lockout>>(),
vec![
Lockout {
slot: 1,
confirmation_count: 4,
},
Lockout {
slot: vote_slot,
confirmation_count: 3,
}
Lockout::new_with_confirmation_count(1, 4),
Lockout::new_with_confirmation_count(vote_slot, 3)
]
);
assert!(do_process_vote_state_update(
@ -2621,14 +2508,8 @@ mod tests {
.into_iter()
.collect::<Vec<Lockout>>(),
vec![
Lockout {
slot: existing_older_than_history_slot,
confirmation_count: 3,
},
Lockout {
slot: vote_slot,
confirmation_count: 2,
}
Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
Lockout::new_with_confirmation_count(vote_slot, 2)
]
);
assert!(do_process_vote_state_update(
@ -2690,18 +2571,9 @@ mod tests {
.into_iter()
.collect::<Vec<Lockout>>(),
vec![
Lockout {
slot: existing_older_than_history_slot,
confirmation_count: 3,
},
Lockout {
slot: 12,
confirmation_count: 2,
},
Lockout {
slot: vote_slot,
confirmation_count: 1,
}
Lockout::new_with_confirmation_count(existing_older_than_history_slot, 3),
Lockout::new_with_confirmation_count(12, 2),
Lockout::new_with_confirmation_count(vote_slot, 1)
]
);
assert!(do_process_vote_state_update(
@ -2728,7 +2600,7 @@ mod tests {
// Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
// errors
let vote_slot = vote_state.votes.back().unwrap().slot + 2;
let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
let vote_slot_hash = slot_hashes
.iter()
.find(|(slot, _hash)| *slot == vote_slot)
@ -2838,7 +2710,7 @@ mod tests {
// Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
// errors
let vote_slot = vote_state.votes.back().unwrap().slot + 2;
let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
let vote_slot_hash = slot_hashes
.iter()
.find(|(slot, _hash)| *slot == vote_slot)
@ -2863,22 +2735,10 @@ mod tests {
.into_iter()
.collect::<Vec<Lockout>>(),
vec![
Lockout {
slot: 2,
confirmation_count: 4,
},
Lockout {
slot: 4,
confirmation_count: 3,
},
Lockout {
slot: 6,
confirmation_count: 2,
},
Lockout {
slot: vote_slot,
confirmation_count: 1,
}
Lockout::new_with_confirmation_count(2, 4),
Lockout::new_with_confirmation_count(4, 3),
Lockout::new_with_confirmation_count(6, 2),
Lockout::new_with_confirmation_count(vote_slot, 1)
]
);
@ -2902,7 +2762,7 @@ mod tests {
// Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
// errors
let vote_slot = vote_state.votes.back().unwrap().slot + 2;
let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
let vote_slot_hash = slot_hashes
.iter()
.find(|(slot, _hash)| *slot == vote_slot)
@ -2926,14 +2786,8 @@ mod tests {
.into_iter()
.collect::<Vec<Lockout>>(),
vec![
Lockout {
slot: 4,
confirmation_count: 2,
},
Lockout {
slot: vote_slot,
confirmation_count: 1,
}
Lockout::new_with_confirmation_count(4, 2),
Lockout::new_with_confirmation_count(vote_slot, 1)
]
);
@ -2961,7 +2815,7 @@ mod tests {
// Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld
// errors
let vote_slot = vote_state.votes.back().unwrap().slot + 2;
let vote_slot = vote_state.votes.back().unwrap().slot() + 2;
let vote_slot_hash = Hash::new_unique();
let mut vote_state_update =
VoteStateUpdate::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]);

View File

@ -936,7 +936,7 @@ impl JsonRpcRequestProcessor {
let vote_state = account.vote_state();
let vote_state = vote_state.as_ref().unwrap_or(&default_vote_state);
let last_vote = if let Some(vote) = vote_state.votes.iter().last() {
vote.slot
vote.slot()
} else {
0
};

View File

@ -19,7 +19,7 @@ impl VoteTransaction {
VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update
.lockouts
.iter()
.map(|lockout| lockout.slot)
.map(|lockout| lockout.slot())
.collect(),
}
}
@ -51,7 +51,7 @@ impl VoteTransaction {
match self {
VoteTransaction::Vote(vote) => vote.slots.last().copied(),
VoteTransaction::VoteStateUpdate(vote_state_update) => {
Some(vote_state_update.lockouts.back()?.slot)
Some(vote_state_update.lockouts.back()?.slot())
}
}
}

View File

@ -58,21 +58,25 @@ impl Vote {
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)]
pub struct Lockout {
pub slot: Slot,
pub confirmation_count: u32,
slot: Slot,
confirmation_count: u32,
}
impl Lockout {
pub fn new(slot: Slot) -> Self {
Self::new_with_confirmation_count(slot, 1)
}
pub fn new_with_confirmation_count(slot: Slot, confirmation_count: u32) -> Self {
Self {
slot,
confirmation_count: 1,
confirmation_count,
}
}
// The number of slots for which this vote is locked
pub fn lockout(&self) -> u64 {
(INITIAL_LOCKOUT as u64).pow(self.confirmation_count)
(INITIAL_LOCKOUT as u64).pow(self.confirmation_count())
}
// The last slot at which a vote is still locked out. Validators should not
@ -86,6 +90,18 @@ impl Lockout {
pub fn is_locked_out_at_slot(&self, slot: Slot) -> bool {
self.last_locked_out_slot() >= slot
}
pub fn slot(&self) -> Slot {
self.slot
}
pub fn confirmation_count(&self) -> u32 {
self.confirmation_count
}
pub fn increase_confirmation_count(&mut self, by: u32) {
self.confirmation_count = self.confirmation_count.saturating_add(by)
}
}
#[frozen_abi(digest = "GwJfVFsATSj7nvKwtUkHYzqPRaPY6SLxPGXApuCya3x5")]
@ -105,9 +121,8 @@ impl From<Vec<(Slot, u32)>> for VoteStateUpdate {
fn from(recent_slots: Vec<(Slot, u32)>) -> Self {
let lockouts: VecDeque<Lockout> = recent_slots
.into_iter()
.map(|(slot, confirmation_count)| Lockout {
slot,
confirmation_count,
.map(|(slot, confirmation_count)| {
Lockout::new_with_confirmation_count(slot, confirmation_count)
})
.collect();
Self {
@ -130,11 +145,11 @@ impl VoteStateUpdate {
}
pub fn slots(&self) -> Vec<Slot> {
self.lockouts.iter().map(|lockout| lockout.slot).collect()
self.lockouts.iter().map(|lockout| lockout.slot()).collect()
}
pub fn last_voted_slot(&self) -> Option<Slot> {
self.lockouts.back().map(|l| l.slot)
self.lockouts.back().map(|l| l.slot())
}
}
@ -329,7 +344,7 @@ impl VoteState {
/// Returns if the vote state contains a slot `candidate_slot`
pub fn contains_slot(&self, candidate_slot: Slot) -> bool {
self.votes
.binary_search_by(|lockout| lockout.slot.cmp(&candidate_slot))
.binary_search_by(|lockout| lockout.slot().cmp(&candidate_slot))
.is_ok()
}
@ -365,7 +380,7 @@ impl VoteState {
// Once the stack is full, pop the oldest lockout and distribute rewards
if self.votes.len() == MAX_LOCKOUT_HISTORY {
let vote = self.votes.pop_front().unwrap();
self.root_slot = Some(vote.slot);
self.root_slot = Some(vote.slot());
self.increment_credits(epoch, 1);
}
@ -417,13 +432,13 @@ impl VoteState {
}
pub fn last_voted_slot(&self) -> Option<Slot> {
self.last_lockout().map(|v| v.slot)
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()
self.votes.iter().map(|v| v.slot()).collect()
}
pub fn current_epoch(&self) -> Epoch {
@ -544,8 +559,8 @@ impl VoteState {
for (i, v) in self.votes.iter_mut().enumerate() {
// Don't increase the lockout for this vote until we get more confirmations
// than the max number of confirmations this vote has seen
if stack_depth > i + v.confirmation_count as usize {
v.confirmation_count += 1;
if stack_depth > i + v.confirmation_count() as usize {
v.increase_confirmation_count(1);
}
}
}
@ -612,11 +627,11 @@ pub mod serde_compact_vote_state_update {
let lockout_offsets = vote_state_update.lockouts.iter().scan(
vote_state_update.root.unwrap_or_default(),
|slot, lockout| {
let offset = match lockout.slot.checked_sub(*slot) {
let offset = match lockout.slot().checked_sub(*slot) {
None => return Some(Err(serde::ser::Error::custom("Invalid vote lockout"))),
Some(offset) => offset,
};
let confirmation_count = match u8::try_from(lockout.confirmation_count) {
let confirmation_count = match u8::try_from(lockout.confirmation_count()) {
Ok(confirmation_count) => confirmation_count,
Err(_) => {
return Some(Err(serde::ser::Error::custom("Invalid confirmation count")))
@ -626,7 +641,7 @@ pub mod serde_compact_vote_state_update {
offset,
confirmation_count,
};
*slot = lockout.slot;
*slot = lockout.slot();
Some(Ok(lockout_offset))
},
);
@ -660,10 +675,10 @@ pub mod serde_compact_vote_state_update {
}
Some(slot) => slot,
};
let lockout = Lockout {
slot: *slot,
confirmation_count: u32::from(lockout_offset.confirmation_count),
};
let lockout = Lockout::new_with_confirmation_count(
*slot,
u32::from(lockout_offset.confirmation_count),
);
Some(Ok(lockout))
});
Ok(VoteStateUpdate {
@ -1132,16 +1147,17 @@ mod tests {
#[allow(clippy::integer_arithmetic)]
fn run_serde_compact_vote_state_update<R: Rng>(rng: &mut R) {
let lockouts: VecDeque<_> = std::iter::repeat_with(|| Lockout {
slot: 149_303_885 + rng.gen_range(0, 10_000),
confirmation_count: rng.gen_range(0, 33),
let lockouts: VecDeque<_> = std::iter::repeat_with(|| {
let slot = 149_303_885 + rng.gen_range(0, 10_000);
let confirmation_count = rng.gen_range(0, 33);
Lockout::new_with_confirmation_count(slot, confirmation_count)
})
.take(32)
.sorted_by_key(|lockout| lockout.slot)
.sorted_by_key(|lockout| lockout.slot())
.collect();
let root = rng
.gen_ratio(1, 2)
.then(|| lockouts[0].slot - rng.gen_range(0, 1_000));
.then(|| lockouts[0].slot() - rng.gen_range(0, 1_000));
let timestamp = rng.gen_ratio(1, 2).then(|| rng.gen());
let hash = Hash::from(rng.gen::<[u8; 32]>());
let vote_state_update = VoteStateUpdate {