Enhance ledger-tool for multi-epoch rewards (#13837) (#13839)

* Support ledger-tool for multi-epoch rewards

* nits

* Ensure not to skip some records in csv

(cherry picked from commit 6048342c57)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
This commit is contained in:
mergify[bot]
2020-11-27 05:36:08 +00:00
committed by GitHub
parent b493d554ad
commit a3be6bacc6
3 changed files with 184 additions and 105 deletions

View File

@ -2038,54 +2038,74 @@ fn main() {
.lazy_rent_collection .lazy_rent_collection
.store(true, std::sync::atomic::Ordering::Relaxed); .store(true, std::sync::atomic::Ordering::Relaxed);
if arg_matches.is_present("enable_stake_program_v2") if arg_matches.is_present("enable_stake_program_v2") {
&& !base_bank.stake_program_v2_enabled()
{
let feature_account_balance = std::cmp::max( let feature_account_balance = std::cmp::max(
genesis_config.rent.minimum_balance(Feature::size_of()), genesis_config.rent.minimum_balance(Feature::size_of()),
1, 1,
); );
base_bank.store_account( let mut force_enabled_count = 0;
&feature_set::stake_program_v2::id(), if base_bank
&feature::create_account( .get_account(&feature_set::stake_program_v2::id())
&Feature { activated_at: None }, .is_none()
feature_account_balance, {
), base_bank.store_account(
); &feature_set::stake_program_v2::id(),
base_bank.store_account( &feature::create_account(
&feature_set::rewrite_stake::id(), &Feature { activated_at: None },
&feature::create_account( feature_account_balance,
&Feature { activated_at: None }, ),
feature_account_balance, );
), force_enabled_count += 1;
); }
if base_bank
.get_account(&feature_set::rewrite_stake::id())
.is_none()
{
base_bank.store_account(
&feature_set::rewrite_stake::id(),
&feature::create_account(
&Feature { activated_at: None },
feature_account_balance,
),
);
force_enabled_count += 1;
}
let mut store_failed_count = 0; let mut store_failed_count = 0;
if base_bank if force_enabled_count >= 1 {
.get_account(&feature_set::secp256k1_program_enabled::id()) if base_bank
.is_some() .get_account(&feature_set::secp256k1_program_enabled::id())
{ .is_some()
// steal some lamports from the pretty old feature not to affect {
// capitalizaion, which doesn't affect inflation behavior! // steal some lamports from the pretty old feature not to affect
base_bank.store_account( // capitalizaion, which doesn't affect inflation behavior!
&feature_set::secp256k1_program_enabled::id(), base_bank.store_account(
&Account::default(), &feature_set::secp256k1_program_enabled::id(),
); &Account::default(),
} else { );
store_failed_count += 1; force_enabled_count -= 1;
} else {
store_failed_count += 1;
}
} }
if base_bank if force_enabled_count >= 1 {
.get_account(&feature_set::instructions_sysvar_enabled::id()) if base_bank
.is_some() .get_account(&feature_set::instructions_sysvar_enabled::id())
{ .is_some()
base_bank.store_account( {
&feature_set::instructions_sysvar_enabled::id(), // steal some lamports from the pretty old feature not to affect
&Account::default(), // capitalizaion, which doesn't affect inflation behavior!
); base_bank.store_account(
} else { &feature_set::instructions_sysvar_enabled::id(),
store_failed_count += 1; &Account::default(),
);
force_enabled_count -= 1;
} else {
store_failed_count += 1;
}
} }
assert_eq!(force_enabled_count, store_failed_count);
if store_failed_count >= 1 { if store_failed_count >= 1 {
// we have no choice; maybe locally created blank cluster with // we have no choice; maybe locally created blank cluster with
// not-Development cluster type. // not-Development cluster type.
@ -2103,16 +2123,23 @@ fn main() {
} }
} }
#[derive(Default, Debug)]
struct PointDetail {
epoch: Epoch,
points: u128,
stake: u128,
credits: u128,
}
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct CalculationDetail { struct CalculationDetail {
epochs: usize, epochs: usize,
voter: Pubkey, voter: Pubkey,
voter_owner: Pubkey, voter_owner: Pubkey,
point: u128, current_effective_stake: u64,
stake: u128,
total_stake: u64, total_stake: u64,
rent_exempt_reserve: u64, rent_exempt_reserve: u64,
credits: u128, points: Vec<PointDetail>,
base_rewards: u64, base_rewards: u64,
commission: u8, commission: u8,
vote_rewards: u64, vote_rewards: u64,
@ -2120,7 +2147,8 @@ fn main() {
activation_epoch: Epoch, activation_epoch: Epoch,
deactivation_epoch: Option<Epoch>, deactivation_epoch: Option<Epoch>,
point_value: Option<PointValue>, point_value: Option<PointValue>,
credits_observed: Option<u64>, old_credits_observed: Option<u64>,
new_credits_observed: Option<u64>,
} }
use solana_stake_program::stake_state::InflationPointCalculationEvent; use solana_stake_program::stake_state::InflationPointCalculationEvent;
let mut stake_calcuration_details: HashMap<Pubkey, CalculationDetail> = let mut stake_calcuration_details: HashMap<Pubkey, CalculationDetail> =
@ -2134,16 +2162,14 @@ fn main() {
let detail = stake_calcuration_details.entry(**pubkey).or_default(); let detail = stake_calcuration_details.entry(**pubkey).or_default();
match event { match event {
InflationPointCalculationEvent::CalculatedPoints( InflationPointCalculationEvent::CalculatedPoints(
point, epoch,
stake, stake,
credits, credits,
points,
) => { ) => {
// Don't sum for epochs where no credits are earned if *points > 0 {
if *credits > 0 {
detail.epochs += 1; detail.epochs += 1;
detail.point += *point; detail.points.push(PointDetail {epoch: *epoch, points: *points, stake: *stake, credits: *credits});
detail.stake += *stake;
detail.credits += *credits;
} }
} }
InflationPointCalculationEvent::SplitRewards( InflationPointCalculationEvent::SplitRewards(
@ -2166,6 +2192,9 @@ fn main() {
last_point_value = point_value; last_point_value = point_value;
} }
} }
InflationPointCalculationEvent::EffectiveStakeAtRewardedEpoch(stake) => {
detail.current_effective_stake = *stake;
}
InflationPointCalculationEvent::Commission(commission) => { InflationPointCalculationEvent::Commission(commission) => {
detail.commission = *commission; detail.commission = *commission;
} }
@ -2173,9 +2202,11 @@ fn main() {
detail.rent_exempt_reserve = *reserve; detail.rent_exempt_reserve = *reserve;
} }
InflationPointCalculationEvent::CreditsObserved( InflationPointCalculationEvent::CreditsObserved(
credits_observed, old_credits_observed,
new_credits_observed,
) => { ) => {
detail.credits_observed = Some(*credits_observed); detail.old_credits_observed = Some(*old_credits_observed);
detail.new_credits_observed = Some(*new_credits_observed);
} }
InflationPointCalculationEvent::Delegation( InflationPointCalculationEvent::Delegation(
delegation, delegation,
@ -2316,8 +2347,12 @@ fn main() {
activation_epoch: String, activation_epoch: String,
deactivation_epoch: String, deactivation_epoch: String,
earned_epochs: String, earned_epochs: String,
earned_credits: String, epoch: String,
credits_observed: String, epoch_credits: String,
epoch_points: String,
epoch_stake: String,
old_credits_observed: String,
new_credits_observed: String,
base_rewards: String, base_rewards: String,
stake_rewards: String, stake_rewards: String,
vote_rewards: String, vote_rewards: String,
@ -2331,53 +2366,82 @@ fn main() {
data.map(|data| format!("{}", data)) data.map(|data| format!("{}", data))
.unwrap_or_else(|| "N/A".to_owned()) .unwrap_or_else(|| "N/A".to_owned())
}; };
let record = InflationRecord { let mut point_details = detail
account: format!("{}", pubkey), .map(|d| d.points.iter().map(Some).collect::<Vec<_>>())
owner: format!("{}", base_account.owner), .unwrap_or_default();
old_balance: base_account.lamports,
new_balance: warped_account.lamports, // ensure to print even if there is no calculation/point detail
data_size: base_account.data.len(), if point_details.is_empty() {
delegation: format_or_na(detail.map(|d| d.voter)), point_details.push(None);
delegation_owner: format_or_na( }
detail.map(|d| d.voter_owner),
), for point_detail in point_details {
effective_stake: format_or_na(detail.map(|d| d.stake)), let record = InflationRecord {
delegated_stake: format_or_na( account: format!("{}", pubkey),
detail.map(|d| d.total_stake), owner: format!("{}", base_account.owner),
), old_balance: base_account.lamports,
rent_exempt_reserve: format_or_na( new_balance: warped_account.lamports,
detail.map(|d| d.rent_exempt_reserve), data_size: base_account.data.len(),
), delegation: format_or_na(detail.map(|d| d.voter)),
activation_epoch: format_or_na(detail.map(|d| { delegation_owner: format_or_na(
if d.activation_epoch < Epoch::max_value() { detail.map(|d| d.voter_owner),
d.activation_epoch ),
} else { effective_stake: format_or_na(
// bootstraped detail.map(|d| d.current_effective_stake),
0 ),
} delegated_stake: format_or_na(
})), detail.map(|d| d.total_stake),
deactivation_epoch: format_or_na( ),
detail.and_then(|d| d.deactivation_epoch), rent_exempt_reserve: format_or_na(
), detail.map(|d| d.rent_exempt_reserve),
earned_epochs: format_or_na(detail.map(|d| d.epochs)), ),
earned_credits: format_or_na(detail.map(|d| d.credits)), activation_epoch: format_or_na(detail.map(|d| {
credits_observed: format_or_na( if d.activation_epoch < Epoch::max_value() {
detail.and_then(|d| d.credits_observed), d.activation_epoch
), } else {
base_rewards: format_or_na(detail.map(|d| d.base_rewards)), // bootstraped
stake_rewards: format_or_na( 0
detail.map(|d| d.stake_rewards), }
), })),
vote_rewards: format_or_na(detail.map(|d| d.vote_rewards)), deactivation_epoch: format_or_na(
commission: format_or_na(detail.map(|d| d.commission)), detail.and_then(|d| d.deactivation_epoch),
cluster_rewards: format_or_na( ),
last_point_value.as_ref().map(|pv| pv.rewards), earned_epochs: format_or_na(detail.map(|d| d.epochs)),
), epoch: format_or_na(point_detail.map(|d| d.epoch)),
cluster_points: format_or_na( epoch_credits: format_or_na(
last_point_value.as_ref().map(|pv| pv.points), point_detail.map(|d| d.credits),
), ),
}; epoch_points: format_or_na(
csv_writer.serialize(&record).unwrap(); point_detail.map(|d| d.points),
),
epoch_stake: format_or_na(
point_detail.map(|d| d.stake),
),
old_credits_observed: format_or_na(
detail.and_then(|d| d.old_credits_observed),
),
new_credits_observed: format_or_na(
detail.and_then(|d| d.new_credits_observed),
),
base_rewards: format_or_na(
detail.map(|d| d.base_rewards),
),
stake_rewards: format_or_na(
detail.map(|d| d.stake_rewards),
),
vote_rewards: format_or_na(
detail.map(|d| d.vote_rewards),
),
commission: format_or_na(detail.map(|d| d.commission)),
cluster_rewards: format_or_na(
last_point_value.as_ref().map(|pv| pv.rewards),
),
cluster_points: format_or_na(
last_point_value.as_ref().map(|pv| pv.points),
),
};
csv_writer.serialize(&record).unwrap();
}
} }
overall_delta += delta; overall_delta += delta;
} else { } else {

View File

@ -33,12 +33,13 @@ pub enum StakeState {
#[derive(Debug)] #[derive(Debug)]
pub enum InflationPointCalculationEvent { pub enum InflationPointCalculationEvent {
CalculatedPoints(u128, u128, u128), CalculatedPoints(u64, u128, u128, u128),
SplitRewards(u64, u64, u64, PointValue), SplitRewards(u64, u64, u64, PointValue),
EffectiveStakeAtRewardedEpoch(u64),
RentExemptReserve(u64), RentExemptReserve(u64),
Delegation(Delegation, Pubkey), Delegation(Delegation, Pubkey),
Commission(u8), Commission(u8),
CreditsObserved(u64), CreditsObserved(u64, u64),
} }
fn null_tracer() -> Option<impl FnMut(&InflationPointCalculationEvent)> { fn null_tracer() -> Option<impl FnMut(&InflationPointCalculationEvent)> {
@ -521,12 +522,13 @@ impl Stake {
fix_stake_deactivate, fix_stake_deactivate,
) )
.map(|(stakers_reward, voters_reward, credits_observed)| { .map(|(stakers_reward, voters_reward, credits_observed)| {
self.credits_observed = credits_observed;
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer { if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
inflation_point_calc_tracer(&InflationPointCalculationEvent::CreditsObserved( inflation_point_calc_tracer(&InflationPointCalculationEvent::CreditsObserved(
self.credits_observed,
credits_observed, credits_observed,
)); ));
} }
self.credits_observed = credits_observed;
self.delegation.stake += stakers_reward; self.delegation.stake += stakers_reward;
(stakers_reward, voters_reward) (stakers_reward, voters_reward)
}) })
@ -594,13 +596,15 @@ impl Stake {
new_credits_observed = new_credits_observed.max(final_epoch_credits); new_credits_observed = new_credits_observed.max(final_epoch_credits);
// finally calculate points for this epoch // finally calculate points for this epoch
points += stake * earned_credits; let earned_points = stake * earned_credits;
points += earned_points;
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer { if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
inflation_point_calc_tracer(&InflationPointCalculationEvent::CalculatedPoints( inflation_point_calc_tracer(&InflationPointCalculationEvent::CalculatedPoints(
points, epoch,
stake, stake,
earned_credits, earned_credits,
earned_points,
)); ));
} }
} }
@ -1298,6 +1302,7 @@ impl MergeKind {
// utility function, used by runtime // utility function, used by runtime
// returns a tuple of (stakers_reward,voters_reward) // returns a tuple of (stakers_reward,voters_reward)
pub fn redeem_rewards( pub fn redeem_rewards(
rewarded_epoch: Epoch,
stake_account: &mut Account, stake_account: &mut Account,
vote_account: &mut Account, vote_account: &mut Account,
point_value: &PointValue, point_value: &PointValue,
@ -1309,6 +1314,13 @@ pub fn redeem_rewards(
let vote_state: VoteState = let vote_state: VoteState =
StateMut::<VoteStateVersions>::state(vote_account)?.convert_to_current(); StateMut::<VoteStateVersions>::state(vote_account)?.convert_to_current();
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer { if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
inflation_point_calc_tracer(
&InflationPointCalculationEvent::EffectiveStakeAtRewardedEpoch(stake.stake(
rewarded_epoch,
stake_history,
fix_stake_deactivate,
)),
);
inflation_point_calc_tracer(&InflationPointCalculationEvent::RentExemptReserve( inflation_point_calc_tracer(&InflationPointCalculationEvent::RentExemptReserve(
meta.rent_exempt_reserve, meta.rent_exempt_reserve,
)); ));

View File

@ -1458,6 +1458,7 @@ impl Bank {
let old_vote_balance_and_staked = self.stakes.read().unwrap().vote_balance_and_staked(); let old_vote_balance_and_staked = self.stakes.read().unwrap().vote_balance_and_staked();
let validator_point_value = self.pay_validator_rewards( let validator_point_value = self.pay_validator_rewards(
prev_epoch,
validator_rewards, validator_rewards,
reward_calc_tracer, reward_calc_tracer,
self.stake_program_v2_enabled(), self.stake_program_v2_enabled(),
@ -1576,6 +1577,7 @@ impl Bank {
/// successfully load and parse, return the lamport value of one point /// successfully load and parse, return the lamport value of one point
fn pay_validator_rewards( fn pay_validator_rewards(
&mut self, &mut self,
rewarded_epoch: Epoch,
rewards: u64, rewards: u64,
reward_calc_tracer: &mut Option<impl FnMut(&RewardCalculationEvent)>, reward_calc_tracer: &mut Option<impl FnMut(&RewardCalculationEvent)>,
fix_stake_deactivate: bool, fix_stake_deactivate: bool,
@ -1624,6 +1626,7 @@ impl Bank {
} }
}); });
let redeemed = stake_state::redeem_rewards( let redeemed = stake_state::redeem_rewards(
rewarded_epoch,
stake_account, stake_account,
vote_account, vote_account,
&point_value, &point_value,