Output more inflation calc details in ledger-tool (#13345)
* Output more inflation calc details in ledger-tool * Fix broken ci... * Rename confusing variables * Fix panic by wrapping PointValue with Opiton... * Minor modifications * Remove explict needless flush; Drop already does * Yet another csv field adjustments * Add data_size and rename epochs to earned_epochs * Introduce null_tracer * Unwrap Option in new_from_parent_with_tracer * Don't shorten identifiers * Allow irrefutable_let_patterns temporalily * More null_tracer * More field adjustments
This commit is contained in:
parent
549492954e
commit
a81e7e7749
|
@ -4291,12 +4291,14 @@ dependencies = [
|
||||||
"bs58",
|
"bs58",
|
||||||
"bytecount",
|
"bytecount",
|
||||||
"clap",
|
"clap",
|
||||||
|
"csv",
|
||||||
"futures 0.3.5",
|
"futures 0.3.5",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"histogram",
|
"histogram",
|
||||||
"itertools 0.9.0",
|
"itertools 0.9.0",
|
||||||
"log 0.4.8",
|
"log 0.4.8",
|
||||||
"regex",
|
"regex",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
|
|
|
@ -12,12 +12,14 @@ homepage = "https://solana.com/"
|
||||||
bs58 = "0.3.1"
|
bs58 = "0.3.1"
|
||||||
bytecount = "0.6.0"
|
bytecount = "0.6.0"
|
||||||
clap = "2.33.1"
|
clap = "2.33.1"
|
||||||
|
csv = "1.1.3"
|
||||||
futures = "0.3.5"
|
futures = "0.3.5"
|
||||||
futures-util = "0.3.5"
|
futures-util = "0.3.5"
|
||||||
histogram = "*"
|
histogram = "*"
|
||||||
itertools = "0.9.0"
|
itertools = "0.9.0"
|
||||||
log = { version = "0.4.8" }
|
log = { version = "0.4.8" }
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
serde_yaml = "0.8.13"
|
serde_yaml = "0.8.13"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.5.0" }
|
||||||
|
|
|
@ -5,6 +5,7 @@ use clap::{
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::*;
|
use log::*;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use serde::Serialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
input_parsers::{cluster_type_of, pubkey_of, pubkeys_of},
|
input_parsers::{cluster_type_of, pubkey_of, pubkeys_of},
|
||||||
|
@ -20,7 +21,7 @@ use solana_ledger::{
|
||||||
rooted_slot_iterator::RootedSlotIterator,
|
rooted_slot_iterator::RootedSlotIterator,
|
||||||
};
|
};
|
||||||
use solana_runtime::{
|
use solana_runtime::{
|
||||||
bank::Bank,
|
bank::{Bank, RewardCalculationEvent},
|
||||||
bank_forks::{BankForks, CompressionType, SnapshotConfig},
|
bank_forks::{BankForks, CompressionType, SnapshotConfig},
|
||||||
hardened_unpack::{open_genesis_config, MAX_GENESIS_ARCHIVE_UNPACKED_SIZE},
|
hardened_unpack::{open_genesis_config, MAX_GENESIS_ARCHIVE_UNPACKED_SIZE},
|
||||||
snapshot_utils,
|
snapshot_utils,
|
||||||
|
@ -38,7 +39,7 @@ use solana_sdk::{
|
||||||
shred_version::compute_shred_version,
|
shred_version::compute_shred_version,
|
||||||
system_program,
|
system_program,
|
||||||
};
|
};
|
||||||
use solana_stake_program::stake_state::{self, StakeState};
|
use solana_stake_program::stake_state::{self, PointValue, StakeState};
|
||||||
use solana_vote_program::{
|
use solana_vote_program::{
|
||||||
self,
|
self,
|
||||||
vote_state::{self, VoteState},
|
vote_state::{self, VoteState},
|
||||||
|
@ -1192,6 +1193,13 @@ fn main() {
|
||||||
.help("Recalculate capitalization before warping; circumvents \
|
.help("Recalculate capitalization before warping; circumvents \
|
||||||
bank's out-of-sync capitalization"),
|
bank's out-of-sync capitalization"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("csv_filename")
|
||||||
|
.long("csv-filename")
|
||||||
|
.value_name("FILENAME")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Output file in the csv format"),
|
||||||
|
)
|
||||||
).subcommand(
|
).subcommand(
|
||||||
SubCommand::with_name("purge")
|
SubCommand::with_name("purge")
|
||||||
.about("Delete a range of slots from the ledger.")
|
.about("Delete a range of slots from the ledger.")
|
||||||
|
@ -2017,9 +2025,100 @@ fn main() {
|
||||||
base_bank
|
base_bank
|
||||||
.lazy_rent_collection
|
.lazy_rent_collection
|
||||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
let warped_bank =
|
#[derive(Default, Debug)]
|
||||||
Bank::new_from_parent(&base_bank, base_bank.collector_id(), next_epoch);
|
struct CalculationDetail {
|
||||||
|
epochs: usize,
|
||||||
|
voter: Pubkey,
|
||||||
|
point: u128,
|
||||||
|
stake: u128,
|
||||||
|
total_stake: u64,
|
||||||
|
rent_exempt_reserve: u64,
|
||||||
|
credits: u128,
|
||||||
|
base_rewards: u64,
|
||||||
|
commission: u8,
|
||||||
|
vote_rewards: u64,
|
||||||
|
stake_rewards: u64,
|
||||||
|
activation_epoch: Epoch,
|
||||||
|
deactivation_epoch: Option<Epoch>,
|
||||||
|
point_value: Option<PointValue>,
|
||||||
|
}
|
||||||
|
use solana_stake_program::stake_state::InflationPointCalculationEvent;
|
||||||
|
let mut stake_calcuration_details: HashMap<Pubkey, CalculationDetail> =
|
||||||
|
HashMap::new();
|
||||||
|
let mut last_point_value = None;
|
||||||
|
let tracer = |event: &RewardCalculationEvent| {
|
||||||
|
// Currently RewardCalculationEvent enum has only Staking variant
|
||||||
|
// because only staking tracing is supported!
|
||||||
|
#[allow(irrefutable_let_patterns)]
|
||||||
|
if let RewardCalculationEvent::Staking(pubkey, event) = event {
|
||||||
|
let detail = stake_calcuration_details.entry(**pubkey).or_default();
|
||||||
|
match event {
|
||||||
|
InflationPointCalculationEvent::CalculatedPoints(
|
||||||
|
point,
|
||||||
|
stake,
|
||||||
|
credits,
|
||||||
|
) => {
|
||||||
|
// Don't sum for epochs where no credits are earned
|
||||||
|
if *credits > 0 {
|
||||||
|
detail.epochs += 1;
|
||||||
|
detail.point += *point;
|
||||||
|
detail.stake += *stake;
|
||||||
|
detail.credits += *credits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InflationPointCalculationEvent::SplitRewards(
|
||||||
|
all,
|
||||||
|
voter,
|
||||||
|
staker,
|
||||||
|
point_value,
|
||||||
|
) => {
|
||||||
|
detail.base_rewards = *all;
|
||||||
|
detail.vote_rewards = *voter;
|
||||||
|
detail.stake_rewards = *staker;
|
||||||
|
detail.point_value = Some(point_value.clone());
|
||||||
|
// we have duplicate copies of `PointValue`s for possible
|
||||||
|
// miscalculation; do some minimum sanity check
|
||||||
|
let point_value = detail.point_value.clone();
|
||||||
|
if point_value.is_some() {
|
||||||
|
if last_point_value.is_some() {
|
||||||
|
assert_eq!(last_point_value, point_value,);
|
||||||
|
}
|
||||||
|
last_point_value = point_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InflationPointCalculationEvent::Commission(commission) => {
|
||||||
|
detail.commission = *commission;
|
||||||
|
}
|
||||||
|
InflationPointCalculationEvent::RentExemptReserve(reserve) => {
|
||||||
|
detail.rent_exempt_reserve = *reserve;
|
||||||
|
}
|
||||||
|
InflationPointCalculationEvent::Delegation(delegation) => {
|
||||||
|
detail.voter = delegation.voter_pubkey;
|
||||||
|
detail.total_stake = delegation.stake;
|
||||||
|
detail.activation_epoch = delegation.activation_epoch;
|
||||||
|
if delegation.deactivation_epoch < Epoch::max_value() {
|
||||||
|
detail.deactivation_epoch =
|
||||||
|
Some(delegation.deactivation_epoch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let warped_bank = Bank::new_from_parent_with_tracer(
|
||||||
|
&base_bank,
|
||||||
|
base_bank.collector_id(),
|
||||||
|
next_epoch,
|
||||||
|
tracer,
|
||||||
|
);
|
||||||
warped_bank.freeze();
|
warped_bank.freeze();
|
||||||
|
let mut csv_writer = if arg_matches.is_present("csv_filename") {
|
||||||
|
let csv_filename =
|
||||||
|
value_t_or_exit!(arg_matches, "csv_filename", String);
|
||||||
|
let file = File::create(&csv_filename).unwrap();
|
||||||
|
Some(csv::WriterBuilder::new().from_writer(file))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
println!("Slot: {} => {}", base_bank.slot(), warped_bank.slot());
|
println!("Slot: {} => {}", base_bank.slot(), warped_bank.slot());
|
||||||
println!("Epoch: {} => {}", base_bank.epoch(), warped_bank.epoch());
|
println!("Epoch: {} => {}", base_bank.epoch(), warped_bank.epoch());
|
||||||
|
@ -2041,9 +2140,10 @@ fn main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut overall_delta = 0;
|
let mut overall_delta = 0;
|
||||||
|
|
||||||
let modified_accounts =
|
let modified_accounts =
|
||||||
warped_bank.get_all_accounts_modified_since_parent();
|
warped_bank.get_all_accounts_modified_since_parent();
|
||||||
let mut sorted_accounts = modified_accounts
|
let mut rewarded_accounts = modified_accounts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(pubkey, account)| {
|
.map(|(pubkey, account)| {
|
||||||
(
|
(
|
||||||
|
@ -2056,32 +2156,133 @@ fn main() {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
sorted_accounts.sort_unstable_by_key(|(pubkey, account, base_lamports)| {
|
rewarded_accounts.sort_unstable_by_key(
|
||||||
(
|
|(pubkey, account, base_lamports)| {
|
||||||
account.owner,
|
(
|
||||||
*base_lamports,
|
account.owner,
|
||||||
account.lamports - base_lamports,
|
*base_lamports,
|
||||||
*pubkey,
|
account.lamports - base_lamports,
|
||||||
|
*pubkey,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut unchanged_accounts = stake_calcuration_details
|
||||||
|
.keys()
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.difference(
|
||||||
|
&rewarded_accounts
|
||||||
|
.iter()
|
||||||
|
.map(|(pubkey, ..)| *pubkey)
|
||||||
|
.collect(),
|
||||||
)
|
)
|
||||||
|
.map(|pubkey| (**pubkey, warped_bank.get_account(pubkey).unwrap()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
unchanged_accounts.sort_unstable_by_key(|(pubkey, account)| {
|
||||||
|
(account.owner, account.lamports, *pubkey)
|
||||||
});
|
});
|
||||||
for (pubkey, warped_account, _) in sorted_accounts {
|
let unchanged_accounts = unchanged_accounts.into_iter();
|
||||||
|
|
||||||
|
let rewarded_accounts = rewarded_accounts
|
||||||
|
.into_iter()
|
||||||
|
.map(|(pubkey, account, ..)| (*pubkey, account.clone()));
|
||||||
|
|
||||||
|
let all_accounts = unchanged_accounts.chain(rewarded_accounts);
|
||||||
|
for (pubkey, warped_account) in all_accounts {
|
||||||
|
// Don't output sysvars; it's always updated but not related to
|
||||||
|
// inflation.
|
||||||
|
if solana_sdk::sysvar::is_sysvar_id(&pubkey) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(base_account) = base_bank.get_account(&pubkey) {
|
if let Some(base_account) = base_bank.get_account(&pubkey) {
|
||||||
if base_account.lamports != warped_account.lamports {
|
let delta = warped_account.lamports - base_account.lamports;
|
||||||
let delta = warped_account.lamports - base_account.lamports;
|
let detail = stake_calcuration_details.get(&pubkey);
|
||||||
println!(
|
println!(
|
||||||
"{:<45}({}): {} => {} (+{} {:>4.9}%)",
|
"{:<45}({}): {} => {} (+{} {:>4.9}%) {:?}",
|
||||||
format!("{}", pubkey), // format! is needed to pad/justify correctly.
|
format!("{}", pubkey), // format! is needed to pad/justify correctly.
|
||||||
base_account.owner,
|
base_account.owner,
|
||||||
Sol(base_account.lamports),
|
Sol(base_account.lamports),
|
||||||
Sol(warped_account.lamports),
|
Sol(warped_account.lamports),
|
||||||
Sol(delta),
|
Sol(delta),
|
||||||
((warped_account.lamports as f64)
|
((warped_account.lamports as f64)
|
||||||
/ (base_account.lamports as f64)
|
/ (base_account.lamports as f64)
|
||||||
* 100_f64)
|
* 100_f64)
|
||||||
- 100_f64,
|
- 100_f64,
|
||||||
);
|
detail,
|
||||||
overall_delta += delta;
|
);
|
||||||
|
if let Some(ref mut csv_writer) = csv_writer {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct InflationRecord {
|
||||||
|
account: String,
|
||||||
|
owner: String,
|
||||||
|
old_balance: u64,
|
||||||
|
new_balance: u64,
|
||||||
|
data_size: usize,
|
||||||
|
delegation: String,
|
||||||
|
effective_stake: String,
|
||||||
|
delegated_stake: String,
|
||||||
|
rent_exempt_reserve: String,
|
||||||
|
activation_epoch: String,
|
||||||
|
deactivation_epoch: String,
|
||||||
|
earned_epochs: String,
|
||||||
|
earned_credits: String,
|
||||||
|
base_rewards: String,
|
||||||
|
stake_rewards: String,
|
||||||
|
vote_rewards: String,
|
||||||
|
commission: String,
|
||||||
|
cluster_rewards: String,
|
||||||
|
cluster_points: String,
|
||||||
|
};
|
||||||
|
fn format_or_na<T: std::fmt::Display>(
|
||||||
|
data: Option<T>,
|
||||||
|
) -> String {
|
||||||
|
data.map(|data| format!("{}", data))
|
||||||
|
.unwrap_or_else(|| "N/A".to_owned())
|
||||||
|
};
|
||||||
|
let record = InflationRecord {
|
||||||
|
account: format!("{}", pubkey),
|
||||||
|
owner: format!("{}", base_account.owner),
|
||||||
|
old_balance: base_account.lamports,
|
||||||
|
new_balance: warped_account.lamports,
|
||||||
|
data_size: base_account.data.len(),
|
||||||
|
delegation: format_or_na(detail.map(|d| d.voter)),
|
||||||
|
effective_stake: format_or_na(detail.map(|d| d.stake)),
|
||||||
|
delegated_stake: format_or_na(
|
||||||
|
detail.map(|d| d.total_stake),
|
||||||
|
),
|
||||||
|
rent_exempt_reserve: format_or_na(
|
||||||
|
detail.map(|d| d.rent_exempt_reserve),
|
||||||
|
),
|
||||||
|
activation_epoch: format_or_na(detail.map(|d| {
|
||||||
|
if d.activation_epoch < Epoch::max_value() {
|
||||||
|
d.activation_epoch
|
||||||
|
} else {
|
||||||
|
// bootstraped
|
||||||
|
0
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
deactivation_epoch: format_or_na(
|
||||||
|
detail.and_then(|d| d.deactivation_epoch),
|
||||||
|
),
|
||||||
|
earned_epochs: format_or_na(detail.map(|d| d.epochs)),
|
||||||
|
earned_credits: format_or_na(detail.map(|d| d.credits)),
|
||||||
|
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;
|
||||||
} else {
|
} else {
|
||||||
error!("new account!?: {}", pubkey);
|
error!("new account!?: {}", pubkey);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,19 @@ pub enum StakeState {
|
||||||
RewardsPool,
|
RewardsPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum InflationPointCalculationEvent {
|
||||||
|
CalculatedPoints(u128, u128, u128),
|
||||||
|
SplitRewards(u64, u64, u64, PointValue),
|
||||||
|
RentExemptReserve(u64),
|
||||||
|
Delegation(Delegation),
|
||||||
|
Commission(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn null_tracer() -> Option<impl FnMut(&InflationPointCalculationEvent)> {
|
||||||
|
None::<fn(&_)>
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for StakeState {
|
impl Default for StakeState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
StakeState::Uninitialized
|
StakeState::Uninitialized
|
||||||
|
@ -369,6 +382,7 @@ impl Authorized {
|
||||||
/// and the total points over which those lamports
|
/// and the total points over which those lamports
|
||||||
/// are to be distributed
|
/// are to be distributed
|
||||||
// basically read as rewards/points, but in integers instead of as an f64
|
// basically read as rewards/points, but in integers instead of as an f64
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PointValue {
|
pub struct PointValue {
|
||||||
pub rewards: u64, // lamports to split
|
pub rewards: u64, // lamports to split
|
||||||
pub points: u128, // over these points
|
pub points: u128, // over these points
|
||||||
|
@ -384,21 +398,28 @@ impl Stake {
|
||||||
point_value: &PointValue,
|
point_value: &PointValue,
|
||||||
vote_state: &VoteState,
|
vote_state: &VoteState,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
) -> Option<(u64, u64)> {
|
) -> Option<(u64, u64)> {
|
||||||
self.calculate_rewards(point_value, vote_state, stake_history)
|
self.calculate_rewards(
|
||||||
.map(|(stakers_reward, voters_reward, credits_observed)| {
|
point_value,
|
||||||
self.credits_observed = credits_observed;
|
vote_state,
|
||||||
self.delegation.stake += stakers_reward;
|
stake_history,
|
||||||
(stakers_reward, voters_reward)
|
inflation_point_calc_tracer,
|
||||||
})
|
)
|
||||||
|
.map(|(stakers_reward, voters_reward, credits_observed)| {
|
||||||
|
self.credits_observed = credits_observed;
|
||||||
|
self.delegation.stake += stakers_reward;
|
||||||
|
(stakers_reward, voters_reward)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_points(
|
pub fn calculate_points(
|
||||||
&self,
|
&self,
|
||||||
vote_state: &VoteState,
|
vote_state: &VoteState,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
) -> u128 {
|
) -> u128 {
|
||||||
self.calculate_points_and_credits(vote_state, stake_history)
|
self.calculate_points_and_credits(vote_state, stake_history, inflation_point_calc_tracer)
|
||||||
.0
|
.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,6 +430,7 @@ impl Stake {
|
||||||
&self,
|
&self,
|
||||||
new_vote_state: &VoteState,
|
new_vote_state: &VoteState,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
) -> (u128, u64) {
|
) -> (u128, u64) {
|
||||||
// if there is no newer credits since observed, return no point
|
// if there is no newer credits since observed, return no point
|
||||||
if new_vote_state.credits() <= self.credits_observed {
|
if new_vote_state.credits() <= self.credits_observed {
|
||||||
|
@ -443,6 +465,14 @@ impl Stake {
|
||||||
|
|
||||||
// finally calculate points for this epoch
|
// finally calculate points for this epoch
|
||||||
points += stake * earned_credits;
|
points += stake * earned_credits;
|
||||||
|
|
||||||
|
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
|
||||||
|
inflation_point_calc_tracer(&InflationPointCalculationEvent::CalculatedPoints(
|
||||||
|
points,
|
||||||
|
stake,
|
||||||
|
earned_credits,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(points, new_credits_observed)
|
(points, new_credits_observed)
|
||||||
|
@ -459,9 +489,13 @@ impl Stake {
|
||||||
point_value: &PointValue,
|
point_value: &PointValue,
|
||||||
vote_state: &VoteState,
|
vote_state: &VoteState,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
) -> Option<(u64, u64, u64)> {
|
) -> Option<(u64, u64, u64)> {
|
||||||
let (points, credits_observed) =
|
let (points, credits_observed) = self.calculate_points_and_credits(
|
||||||
self.calculate_points_and_credits(vote_state, stake_history);
|
vote_state,
|
||||||
|
stake_history,
|
||||||
|
inflation_point_calc_tracer,
|
||||||
|
);
|
||||||
|
|
||||||
if points == 0 || point_value.points == 0 {
|
if points == 0 || point_value.points == 0 {
|
||||||
return None;
|
return None;
|
||||||
|
@ -480,6 +514,14 @@ impl Stake {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let (voter_rewards, staker_rewards, is_split) = vote_state.commission_split(rewards);
|
let (voter_rewards, staker_rewards, is_split) = vote_state.commission_split(rewards);
|
||||||
|
if let Some(inflation_point_calc_tracer) = inflation_point_calc_tracer {
|
||||||
|
inflation_point_calc_tracer(&InflationPointCalculationEvent::SplitRewards(
|
||||||
|
rewards,
|
||||||
|
voter_rewards,
|
||||||
|
staker_rewards,
|
||||||
|
(*point_value).clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if (voter_rewards == 0 || staker_rewards == 0) && is_split {
|
if (voter_rewards == 0 || staker_rewards == 0) && is_split {
|
||||||
// don't collect if we lose a whole lamport somewhere
|
// don't collect if we lose a whole lamport somewhere
|
||||||
|
@ -972,14 +1014,26 @@ pub fn redeem_rewards(
|
||||||
vote_account: &mut Account,
|
vote_account: &mut Account,
|
||||||
point_value: &PointValue,
|
point_value: &PointValue,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
|
inflation_point_calc_tracer: &mut Option<impl FnMut(&InflationPointCalculationEvent)>,
|
||||||
) -> Result<(u64, u64), InstructionError> {
|
) -> Result<(u64, u64), InstructionError> {
|
||||||
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
||||||
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 {
|
||||||
|
inflation_point_calc_tracer(&InflationPointCalculationEvent::RentExemptReserve(
|
||||||
|
meta.rent_exempt_reserve,
|
||||||
|
));
|
||||||
|
inflation_point_calc_tracer(&InflationPointCalculationEvent::Commission(
|
||||||
|
vote_state.commission,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if let Some((stakers_reward, voters_reward)) =
|
if let Some((stakers_reward, voters_reward)) = stake.redeem_rewards(
|
||||||
stake.redeem_rewards(point_value, &vote_state, stake_history)
|
point_value,
|
||||||
{
|
&vote_state,
|
||||||
|
stake_history,
|
||||||
|
inflation_point_calc_tracer,
|
||||||
|
) {
|
||||||
stake_account.lamports += stakers_reward;
|
stake_account.lamports += stakers_reward;
|
||||||
vote_account.lamports += voters_reward;
|
vote_account.lamports += voters_reward;
|
||||||
|
|
||||||
|
@ -1004,7 +1058,7 @@ pub fn calculate_points(
|
||||||
let vote_state: VoteState =
|
let vote_state: VoteState =
|
||||||
StateMut::<VoteStateVersions>::state(vote_account)?.convert_to_current();
|
StateMut::<VoteStateVersions>::state(vote_account)?.convert_to_current();
|
||||||
|
|
||||||
Ok(stake.calculate_points(&vote_state, stake_history))
|
Ok(stake.calculate_points(&vote_state, stake_history, &mut null_tracer()))
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidAccountData)
|
Err(InstructionError::InvalidAccountData)
|
||||||
}
|
}
|
||||||
|
@ -2440,7 +2494,8 @@ mod tests {
|
||||||
points: 1
|
points: 1
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2457,7 +2512,8 @@ mod tests {
|
||||||
points: 1
|
points: 1
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2491,7 +2547,8 @@ mod tests {
|
||||||
points: 1
|
points: 1
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2505,7 +2562,7 @@ mod tests {
|
||||||
// no overflow on points
|
// no overflow on points
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
u128::from(stake.delegation.stake) * epoch_slots,
|
u128::from(stake.delegation.stake) * epoch_slots,
|
||||||
stake.calculate_points(&vote_state, None)
|
stake.calculate_points(&vote_state, None, &mut null_tracer())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2531,7 +2588,8 @@ mod tests {
|
||||||
points: 1
|
points: 1
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2548,7 +2606,8 @@ mod tests {
|
||||||
points: 2 // all his
|
points: 2 // all his
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2562,7 +2621,8 @@ mod tests {
|
||||||
points: 1
|
points: 1
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2579,7 +2639,8 @@ mod tests {
|
||||||
points: 2
|
points: 2
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2594,7 +2655,8 @@ mod tests {
|
||||||
points: 2
|
points: 2
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2615,7 +2677,8 @@ mod tests {
|
||||||
points: 4
|
points: 4
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2630,7 +2693,8 @@ mod tests {
|
||||||
points: 4
|
points: 4
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
vote_state.commission = 99;
|
vote_state.commission = 99;
|
||||||
|
@ -2642,7 +2706,8 @@ mod tests {
|
||||||
points: 4
|
points: 4
|
||||||
},
|
},
|
||||||
&vote_state,
|
&vote_state,
|
||||||
None
|
None,
|
||||||
|
&mut null_tracer(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,9 @@ use solana_sdk::{
|
||||||
timing::years_as_slots,
|
timing::years_as_slots,
|
||||||
transaction::{self, Result, Transaction, TransactionError},
|
transaction::{self, Result, Transaction, TransactionError},
|
||||||
};
|
};
|
||||||
use solana_stake_program::stake_state::{self, Delegation, PointValue};
|
use solana_stake_program::stake_state::{
|
||||||
|
self, Delegation, InflationPointCalculationEvent, PointValue,
|
||||||
|
};
|
||||||
use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::VoteState};
|
use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::VoteState};
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
@ -543,6 +545,15 @@ pub enum RewardType {
|
||||||
Voting,
|
Voting,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RewardCalculationEvent<'a, 'b> {
|
||||||
|
Staking(&'a Pubkey, &'b InflationPointCalculationEvent),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn null_tracer() -> Option<impl FnMut(&RewardCalculationEvent)> {
|
||||||
|
None::<fn(&RewardCalculationEvent)>
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for RewardType {
|
impl fmt::Display for RewardType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
|
@ -752,6 +763,24 @@ impl Bank {
|
||||||
|
|
||||||
/// Create a new bank that points to an immutable checkpoint of another bank.
|
/// Create a new bank that points to an immutable checkpoint of another bank.
|
||||||
pub fn new_from_parent(parent: &Arc<Bank>, collector_id: &Pubkey, slot: Slot) -> Self {
|
pub fn new_from_parent(parent: &Arc<Bank>, collector_id: &Pubkey, slot: Slot) -> Self {
|
||||||
|
Self::_new_from_parent(parent, collector_id, slot, &mut null_tracer())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_parent_with_tracer(
|
||||||
|
parent: &Arc<Bank>,
|
||||||
|
collector_id: &Pubkey,
|
||||||
|
slot: Slot,
|
||||||
|
reward_calc_tracer: impl FnMut(&RewardCalculationEvent),
|
||||||
|
) -> Self {
|
||||||
|
Self::_new_from_parent(parent, collector_id, slot, &mut Some(reward_calc_tracer))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _new_from_parent(
|
||||||
|
parent: &Arc<Bank>,
|
||||||
|
collector_id: &Pubkey,
|
||||||
|
slot: Slot,
|
||||||
|
reward_calc_tracer: &mut Option<impl FnMut(&RewardCalculationEvent)>,
|
||||||
|
) -> Self {
|
||||||
parent.freeze();
|
parent.freeze();
|
||||||
assert_ne!(slot, parent.slot());
|
assert_ne!(slot, parent.slot());
|
||||||
|
|
||||||
|
@ -844,7 +873,7 @@ impl Bank {
|
||||||
}
|
}
|
||||||
|
|
||||||
new.update_slot_hashes();
|
new.update_slot_hashes();
|
||||||
new.update_rewards(parent.epoch());
|
new.update_rewards(parent.epoch(), reward_calc_tracer);
|
||||||
new.update_stake_history(Some(parent.epoch()));
|
new.update_stake_history(Some(parent.epoch()));
|
||||||
new.update_clock(Some(parent.epoch()));
|
new.update_clock(Some(parent.epoch()));
|
||||||
new.update_fees();
|
new.update_fees();
|
||||||
|
@ -1256,7 +1285,11 @@ impl Bank {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update rewards based on the previous epoch
|
// update rewards based on the previous epoch
|
||||||
fn update_rewards(&mut self, prev_epoch: Epoch) {
|
fn update_rewards(
|
||||||
|
&mut self,
|
||||||
|
prev_epoch: Epoch,
|
||||||
|
reward_calc_tracer: &mut Option<impl FnMut(&RewardCalculationEvent)>,
|
||||||
|
) {
|
||||||
if prev_epoch == self.epoch() {
|
if prev_epoch == self.epoch() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1282,7 +1315,8 @@ 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(validator_rewards);
|
let validator_point_value =
|
||||||
|
self.pay_validator_rewards(validator_rewards, reward_calc_tracer);
|
||||||
|
|
||||||
if !self
|
if !self
|
||||||
.feature_set
|
.feature_set
|
||||||
|
@ -1354,7 +1388,10 @@ impl Bank {
|
||||||
/// returns a map (has to be copied) of loaded
|
/// returns a map (has to be copied) of loaded
|
||||||
/// ( Vec<(staker info)> (voter account) ) keyed by voter pubkey
|
/// ( Vec<(staker info)> (voter account) ) keyed by voter pubkey
|
||||||
///
|
///
|
||||||
fn stake_delegation_accounts(&self) -> HashMap<Pubkey, (Vec<(Pubkey, Account)>, Account)> {
|
fn stake_delegation_accounts(
|
||||||
|
&self,
|
||||||
|
reward_calc_tracer: &mut Option<impl FnMut(&RewardCalculationEvent)>,
|
||||||
|
) -> HashMap<Pubkey, (Vec<(Pubkey, Account)>, Account)> {
|
||||||
let mut accounts = HashMap::new();
|
let mut accounts = HashMap::new();
|
||||||
|
|
||||||
self.stakes
|
self.stakes
|
||||||
|
@ -1371,6 +1408,12 @@ impl Bank {
|
||||||
let entry = accounts
|
let entry = accounts
|
||||||
.entry(delegation.voter_pubkey)
|
.entry(delegation.voter_pubkey)
|
||||||
.or_insert((Vec::new(), vote_account));
|
.or_insert((Vec::new(), vote_account));
|
||||||
|
if let Some(reward_calc_tracer) = reward_calc_tracer {
|
||||||
|
reward_calc_tracer(&RewardCalculationEvent::Staking(
|
||||||
|
stake_pubkey,
|
||||||
|
&InflationPointCalculationEvent::Delegation(*delegation),
|
||||||
|
));
|
||||||
|
}
|
||||||
entry.0.push((*stake_pubkey, stake_account));
|
entry.0.push((*stake_pubkey, stake_account));
|
||||||
}
|
}
|
||||||
(_, _) => {}
|
(_, _) => {}
|
||||||
|
@ -1382,10 +1425,14 @@ impl Bank {
|
||||||
|
|
||||||
/// iterate over all stakes, redeem vote credits for each stake we can
|
/// iterate over all stakes, redeem vote credits for each stake we can
|
||||||
/// 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(&mut self, rewards: u64) -> f64 {
|
fn pay_validator_rewards(
|
||||||
|
&mut self,
|
||||||
|
rewards: u64,
|
||||||
|
reward_calc_tracer: &mut Option<impl FnMut(&RewardCalculationEvent)>,
|
||||||
|
) -> f64 {
|
||||||
let stake_history = self.stakes.read().unwrap().history().clone();
|
let stake_history = self.stakes.read().unwrap().history().clone();
|
||||||
|
|
||||||
let mut stake_delegation_accounts = self.stake_delegation_accounts();
|
let mut stake_delegation_accounts = self.stake_delegation_accounts(reward_calc_tracer);
|
||||||
|
|
||||||
let points: u128 = stake_delegation_accounts
|
let points: u128 = stake_delegation_accounts
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1413,11 +1460,20 @@ impl Bank {
|
||||||
let voters_account_pre_balance = vote_account.lamports;
|
let voters_account_pre_balance = vote_account.lamports;
|
||||||
|
|
||||||
for (stake_pubkey, stake_account) in stake_group.iter_mut() {
|
for (stake_pubkey, stake_account) in stake_group.iter_mut() {
|
||||||
|
// curry closure to add the contextual stake_pubkey
|
||||||
|
let mut reward_calc_tracer = reward_calc_tracer.as_mut().map(|outer| {
|
||||||
|
let stake_pubkey = *stake_pubkey;
|
||||||
|
// inner
|
||||||
|
move |inner_event: &_| {
|
||||||
|
outer(&RewardCalculationEvent::Staking(&stake_pubkey, inner_event))
|
||||||
|
}
|
||||||
|
});
|
||||||
let redeemed = stake_state::redeem_rewards(
|
let redeemed = stake_state::redeem_rewards(
|
||||||
stake_account,
|
stake_account,
|
||||||
vote_account,
|
vote_account,
|
||||||
&point_value,
|
&point_value,
|
||||||
Some(&stake_history),
|
Some(&stake_history),
|
||||||
|
&mut reward_calc_tracer.as_mut(),
|
||||||
);
|
);
|
||||||
if let Ok((stakers_reward, _voters_reward)) = redeemed {
|
if let Ok((stakers_reward, _voters_reward)) = redeemed {
|
||||||
self.store_account(&stake_pubkey, &stake_account);
|
self.store_account(&stake_pubkey, &stake_account);
|
||||||
|
@ -5968,7 +6024,7 @@ mod tests {
|
||||||
bank.add_account_and_update_capitalization(&vote_id, &vote_account);
|
bank.add_account_and_update_capitalization(&vote_id, &vote_account);
|
||||||
|
|
||||||
let validator_points: u128 = bank
|
let validator_points: u128 = bank
|
||||||
.stake_delegation_accounts()
|
.stake_delegation_accounts(&mut null_tracer())
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|(_vote_pubkey, (stake_group, vote_account))| {
|
.flat_map(|(_vote_pubkey, (stake_group, vote_account))| {
|
||||||
stake_group
|
stake_group
|
||||||
|
|
Loading…
Reference in New Issue