ForkStats: compute and wire up fork_weight (#34623)

* wire up fork stats fork_weight

* convert weight to percentage for logging

* add bank_stake

* remove old fork choice measure and stats - vote stake * lockout

* update tests

* fix bank_weight and rename it to fork_weight

* fix u64 multiple overflow in fork_weight calculation

* format fork_weight as percentage in logging

---------

Co-authored-by: HaoranYi <haoran.yi@solana.com>
This commit is contained in:
HaoranYi 2024-01-18 10:35:53 -05:00 committed by GitHub
parent e2c2029ac4
commit 6a9f729101
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 49 additions and 35 deletions

View File

@ -156,8 +156,7 @@ pub type PubkeyVotes = Vec<(Pubkey, Slot)>;
pub(crate) struct ComputedBankState {
pub voted_stakes: VotedStakes,
pub total_stake: Stake,
#[allow(dead_code)]
bank_weight: u128,
pub fork_stake: Stake,
// Tree of intervals of lockouts of the form [slot, slot + slot.lockout],
// keyed by end of the range
pub lockout_intervals: LockoutIntervals,
@ -319,7 +318,7 @@ impl Tower {
let mut vote_slots = HashSet::new();
let mut voted_stakes = HashMap::new();
let mut total_stake = 0;
let mut bank_weight = 0;
// Tree of intervals of lockouts of the form [slot, slot + slot.lockout],
// keyed by end of the range
let mut lockout_intervals = LockoutIntervals::new();
@ -390,7 +389,6 @@ impl Tower {
process_slot_vote_unchecked(&mut vote_state, bank_slot);
for vote in &vote_state.votes {
bank_weight += vote.lockout.lockout() as u128 * voted_stake as u128;
vote_slots.insert(vote.slot());
}
@ -399,13 +397,11 @@ impl Tower {
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());
}
}
if let Some(root) = vote_state.root_slot {
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());
}
@ -437,10 +433,26 @@ impl Tower {
// TODO: populate_ancestor_voted_stakes only adds zeros. Comment why
// that is necessary (if so).
Self::populate_ancestor_voted_stakes(&mut voted_stakes, vote_slots, ancestors);
// As commented above, since the votes at current bank_slot are
// simulated votes, the voted_stake for `bank_slot` is not populated.
// Therefore, we use the voted_stake for the parent of bank_slot as the
// `fork_stake` instead.
let fork_stake = ancestors
.get(&bank_slot)
.and_then(|ancestors| {
ancestors
.iter()
.max()
.and_then(|parent| voted_stakes.get(parent))
.copied()
})
.unwrap_or(0);
ComputedBankState {
voted_stakes,
total_stake,
bank_weight,
fork_stake,
lockout_intervals,
my_latest_landed_vote,
}
@ -2271,7 +2283,6 @@ pub mod test {
let ComputedBankState {
voted_stakes,
total_stake,
bank_weight,
..
} = Tower::collect_vote_lockouts(
&Pubkey::default(),
@ -2286,10 +2297,6 @@ pub mod test {
let mut new_votes = latest_validator_votes_for_frozen_banks.take_votes_dirty_set(0);
new_votes.sort();
assert_eq!(new_votes, account_latest_votes);
// Each account has 1 vote in it. After simulating a vote in collect_vote_lockouts,
// the account will have 2 votes, with lockout 2 + 4 = 6. So expected weight for
assert_eq!(bank_weight, 12)
}
#[test]
@ -2314,21 +2321,15 @@ pub mod test {
ancestors.insert(i as u64, (0..i as u64).collect());
}
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
.votes
.iter()
.map(|v| v.lockout.lockout() as u128)
.sum::<u128>()
+ root_weight;
let expected_bank_weight = 2 * vote_account_expected_weight;
let expected_bank_stake = 2;
let expected_total_stake = 2;
assert_eq!(tower.vote_state.root_slot, Some(0));
let mut latest_validator_votes_for_frozen_banks =
LatestValidatorVotesForFrozenBanks::default();
let ComputedBankState {
voted_stakes,
bank_weight,
fork_stake,
total_stake,
..
} = Tower::collect_vote_lockouts(
&Pubkey::default(),
@ -2342,8 +2343,9 @@ pub mod test {
assert_eq!(voted_stakes[&(i as u64)], 2);
}
// should be the sum of all the weights for root
assert_eq!(bank_weight, expected_bank_weight);
// should be the sum of all voted stake for on the fork
assert_eq!(fork_stake, expected_bank_stake);
assert_eq!(total_stake, expected_total_stake);
let mut new_votes =
latest_validator_votes_for_frozen_banks.take_votes_dirty_set(root.slot());
new_votes.sort();

View File

@ -293,8 +293,7 @@ impl ForkProgress {
#[derive(Debug, Clone, Default)]
pub struct ForkStats {
pub weight: u128,
pub fork_weight: u128,
pub fork_stake: Stake,
pub total_stake: Stake,
pub block_height: u64,
pub has_voted: bool,
@ -310,6 +309,13 @@ pub struct ForkStats {
pub my_latest_landed_vote: Option<Slot>,
}
impl ForkStats {
/// Return fork_weight, i.e. bank_stake over total_stake.
pub fn fork_weight(&self) -> f64 {
self.fork_stake as f64 / self.total_stake as f64
}
}
#[derive(Clone, Default)]
pub struct PropagatedStats {
pub propagated_validators: HashSet<Pubkey>,

View File

@ -3295,6 +3295,7 @@ impl ReplayStage {
let ComputedBankState {
voted_stakes,
total_stake,
fork_stake,
lockout_intervals,
my_latest_landed_vote,
..
@ -3302,6 +3303,7 @@ impl ReplayStage {
let stats = progress
.get_fork_stats_mut(bank_slot)
.expect("All frozen banks must exist in the Progress map");
stats.fork_stake = fork_stake;
stats.total_stake = total_stake;
stats.voted_stakes = voted_stakes;
stats.lockout_intervals = lockout_intervals;
@ -3312,15 +3314,15 @@ impl ReplayStage {
datapoint_info!(
"bank_weight",
("slot", bank_slot, i64),
// u128 too large for influx, convert to hex
("weight", format!("{:X}", stats.weight), String),
("fork_stake", stats.fork_stake, i64),
("fork_weight", stats.fork_weight(), f64),
);
info!(
"{} slot_weight: {} {} {} {}",
"{} slot_weight: {} {:.1}% {}",
my_vote_pubkey,
bank_slot,
stats.weight,
stats.fork_weight,
100.0 * stats.fork_weight(), // percentage fork_stake in total_stake
bank.parent().map(|b| b.slot()).unwrap_or(0)
);
}
@ -3677,7 +3679,7 @@ impl ReplayStage {
fork_stats.vote_threshold,
propagated_stats.propagated_validators_stake,
propagated_stats.is_leader_slot,
fork_stats.weight,
fork_stats.fork_weight(),
fork_stats.total_stake,
propagated_stats.total_epoch_stake,
)
@ -3712,7 +3714,11 @@ impl ReplayStage {
&& propagation_confirmed
&& switch_fork_decision.can_vote()
{
info!("voting: {} {}", candidate_vote_bank.slot(), fork_weight);
info!(
"voting: {} {:.1}%",
candidate_vote_bank.slot(),
100.0 * fork_weight
);
SelectVoteAndResetForkResult {
vote_bank: Some((candidate_vote_bank.clone(), switch_fork_decision)),
reset_bank: Some(candidate_vote_bank.clone()),
@ -5427,12 +5433,12 @@ pub(crate) mod tests {
.progress
.get_fork_stats(pair[0].slot())
.unwrap()
.fork_weight;
.fork_weight();
let second = vote_simulator
.progress
.get_fork_stats(pair[1].slot())
.unwrap()
.fork_weight;
.fork_weight();
assert!(second >= first);
}
for bank in frozen_banks {