Expose all rewards (fees, rent, voting and staking) in RPC getConfirmedBlock and the cli

This commit is contained in:
Michael Vines
2020-10-09 12:55:35 -07:00
parent 403790760c
commit c5c8da1ac0
13 changed files with 239 additions and 75 deletions

View File

@ -69,7 +69,7 @@ use std::{
cell::RefCell,
collections::{HashMap, HashSet},
convert::{TryFrom, TryInto},
mem,
fmt, mem,
ops::RangeInclusive,
path::PathBuf,
ptr,
@ -525,8 +525,32 @@ impl PartialEq for Bank {
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, AbiExample, Default, Clone, Copy)]
#[derive(Debug, PartialEq, Serialize, Deserialize, AbiExample, AbiEnumVisitor, Clone, Copy)]
pub enum RewardType {
Fee,
Rent,
Staking,
Voting,
}
impl fmt::Display for RewardType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
RewardType::Fee => "fee",
RewardType::Rent => "rent",
RewardType::Staking => "staking",
RewardType::Voting => "voting",
}
)
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, AbiExample, Clone, Copy)]
pub struct RewardInfo {
pub reward_type: RewardType,
pub lamports: i64, // Reward amount
pub post_balance: u64, // Account balance in lamports after `lamports` was applied
}
@ -613,7 +637,7 @@ pub struct Bank {
/// Track cluster signature throughput and adjust fee rate
fee_rate_governor: FeeRateGovernor,
/// Rent that have been collected
/// Rent that has been collected
collected_rent: AtomicU64,
/// latest rent collector, knows the epoch
@ -645,8 +669,8 @@ pub struct Bank {
/// Last time when the cluster info vote listener has synced with this bank
pub last_vote_sync: AtomicU64,
/// Rewards that were paid out immediately after this bank was created
pub rewards: Option<Vec<(Pubkey, RewardInfo)>>,
/// Protocol-level rewards that were distributed by this bank
pub rewards: RwLock<Vec<(Pubkey, RewardInfo)>>,
pub skip_drop: AtomicBool,
@ -780,7 +804,7 @@ impl Bank {
feature_builtins: parent.feature_builtins.clone(),
hard_forks: parent.hard_forks.clone(),
last_vote_sync: AtomicU64::new(parent.last_vote_sync.load(Relaxed)),
rewards: None,
rewards: RwLock::new(vec![]),
skip_drop: AtomicBool::new(false),
cluster_type: parent.cluster_type,
lazy_rent_collection: AtomicBool::new(parent.lazy_rent_collection.load(Relaxed)),
@ -1168,7 +1192,7 @@ impl Bank {
});
}
// update reward for previous epoch
// update rewards based on the previous epoch
fn update_rewards(&mut self, prev_epoch: Epoch) {
if prev_epoch == self.epoch() {
return;
@ -1216,18 +1240,23 @@ impl Bank {
let validator_rewards_paid =
self.stakes.read().unwrap().vote_balance_and_staked() - vote_balance_and_staked;
if let Some(rewards) = self.rewards.as_ref() {
assert_eq!(
validator_rewards_paid,
u64::try_from(
rewards
.iter()
.map(|(_pubkey, reward_info)| reward_info.lamports)
.sum::<i64>()
)
.unwrap()
);
}
assert_eq!(
validator_rewards_paid,
u64::try_from(
self.rewards
.read()
.unwrap()
.iter()
.map(|(_address, reward_info)| {
match reward_info.reward_type {
RewardType::Voting | RewardType::Staking => reward_info.lamports,
_ => 0,
}
})
.sum::<i64>()
)
.unwrap()
);
// verify that we didn't pay any more than we expected to
assert!(validator_rewards >= validator_rewards_paid);
@ -1318,11 +1347,12 @@ impl Bank {
let point_value = PointValue { rewards, points };
let mut rewards = HashMap::new();
let mut rewards = vec![];
// pay according to point value
for (vote_pubkey, (stake_group, vote_account)) in stake_delegation_accounts.iter_mut() {
let mut vote_account_changed = false;
let voters_account_pre_balance = vote_account.lamports;
for (stake_pubkey, stake_account) in stake_group.iter_mut() {
let redeemed = stake_state::redeem_rewards(
stake_account,
@ -1330,24 +1360,19 @@ impl Bank {
&point_value,
Some(&stake_history),
);
if let Ok((stakers_reward, voters_reward)) = redeemed {
if let Ok((stakers_reward, _voters_reward)) = redeemed {
self.store_account(&stake_pubkey, &stake_account);
vote_account_changed = true;
if voters_reward > 0 {
let reward_info = rewards
.entry(*vote_pubkey)
.or_insert_with(RewardInfo::default);
reward_info.lamports += voters_reward as i64;
reward_info.post_balance = vote_account.lamports;
}
if stakers_reward > 0 {
let reward_info = rewards
.entry(*stake_pubkey)
.or_insert_with(RewardInfo::default);
reward_info.lamports += stakers_reward as i64;
reward_info.post_balance = stake_account.lamports;
rewards.push((
*stake_pubkey,
RewardInfo {
reward_type: RewardType::Staking,
lamports: stakers_reward as i64,
post_balance: stake_account.lamports,
},
));
}
} else {
debug!(
@ -1358,12 +1383,23 @@ impl Bank {
}
if vote_account_changed {
let post_balance = vote_account.lamports;
let lamports = (post_balance - voters_account_pre_balance) as i64;
if lamports != 0 {
rewards.push((
*vote_pubkey,
RewardInfo {
reward_type: RewardType::Voting,
lamports,
post_balance,
},
));
}
self.store_account(&vote_pubkey, &vote_account);
}
}
self.rewards.write().unwrap().append(&mut rewards);
assert_eq!(self.rewards, None);
self.rewards = Some(rewards.drain().collect());
point_value.rewards as f64 / point_value.points as f64
}
@ -1446,7 +1482,16 @@ impl Bank {
"distributed fee: {} (rounded from: {}, burned: {})",
unburned, collector_fees, burned
);
self.deposit(&self.collector_id, unburned);
let post_balance = self.deposit(&self.collector_id, unburned);
self.rewards.write().unwrap().push((
self.collector_id,
RewardInfo {
reward_type: RewardType::Fee,
lamports: unburned as i64,
post_balance,
},
));
self.capitalization.fetch_sub(burned, Relaxed);
}
}
@ -2621,6 +2666,7 @@ impl Bank {
// holder
let mut leftover_lamports = rent_to_be_distributed - rent_distributed_in_initial_round;
let mut rewards = vec![];
validator_rent_shares
.into_iter()
.for_each(|(pubkey, rent_share)| {
@ -2633,7 +2679,16 @@ impl Bank {
let mut account = self.get_account(&pubkey).unwrap_or_default();
account.lamports += rent_to_be_paid;
self.store_account(&pubkey, &account);
rewards.push((
pubkey,
RewardInfo {
reward_type: RewardType::Rent,
lamports: rent_to_be_paid as i64,
post_balance: account.lamports,
},
));
});
self.rewards.write().unwrap().append(&mut rewards);
if enforce_fix {
assert_eq!(leftover_lamports, 0);
@ -3224,7 +3279,7 @@ impl Bank {
}
}
pub fn deposit(&self, pubkey: &Pubkey, lamports: u64) {
pub fn deposit(&self, pubkey: &Pubkey, lamports: u64) -> u64 {
let mut account = self.get_account(pubkey).unwrap_or_default();
let should_be_in_new_behavior = match self.cluster_type() {
@ -3248,6 +3303,7 @@ impl Bank {
account.lamports += lamports;
self.store_account(pubkey, &account);
account.lamports
}
pub fn accounts(&self) -> Arc<Accounts> {
@ -4688,8 +4744,26 @@ mod tests {
previous_capitalization - current_capitalization,
burned_portion
);
bank.freeze();
assert!(bank.calculate_and_verify_capitalization());
assert_eq!(
rent_to_be_distributed,
bank.rewards
.read()
.unwrap()
.iter()
.map(|(address, reward)| {
assert_eq!(reward.reward_type, RewardType::Rent);
if *address == validator_2_pubkey {
assert_eq!(reward.post_balance, validator_2_portion + 42 - tweak_2);
} else if *address == validator_3_pubkey {
assert_eq!(reward.post_balance, validator_3_portion + 42);
}
reward.lamports as u64
})
.sum::<u64>()
);
}
#[test]
@ -5676,7 +5750,7 @@ mod tests {
}
#[test]
fn test_bank_update_rewards() {
fn test_bank_update_vote_stake_rewards() {
solana_logger::setup();
// create a bank that ticks really slowly...
@ -5709,7 +5783,7 @@ mod tests {
bank.lazy_rent_collection.store(true, Relaxed);
assert_eq!(bank.capitalization(), 42 * 1_000_000_000);
assert_eq!(bank.rewards, None);
assert!(bank.rewards.read().unwrap().is_empty());
let ((vote_id, mut vote_account), (stake_id, stake_account)) =
crate::stakes::tests::create_staked_node_accounts(1_0000);
@ -5782,14 +5856,15 @@ mod tests {
// verify validator rewards show up in bank1.rewards vector
assert_eq!(
bank1.rewards,
Some(vec![(
*bank1.rewards.read().unwrap(),
vec![(
stake_id,
RewardInfo {
reward_type: RewardType::Staking,
lamports: (rewards.validator_point_value * validator_points as f64) as i64,
post_balance: bank1.get_balance(&stake_id),
}
)])
)]
);
bank1.freeze();
assert!(bank1.calculate_and_verify_capitalization());
@ -5826,7 +5901,7 @@ mod tests {
bank.lazy_rent_collection.store(true, Relaxed);
assert_eq!(bank.capitalization(), 42 * 1_000_000_000);
assert_eq!(bank.rewards, None);
assert!(bank.rewards.read().unwrap().is_empty());
let vote_id = Pubkey::new_rand();
let mut vote_account = vote_state::create_account(&vote_id, &Pubkey::new_rand(), 50, 100);
@ -5866,6 +5941,18 @@ mod tests {
bank1.freeze();
assert!(bank1.calculate_and_verify_capitalization());
// verify voting and staking rewards are recorded
let rewards = bank1.rewards.read().unwrap();
rewards
.iter()
.find(|(_address, reward)| reward.reward_type == RewardType::Voting)
.unwrap();
rewards
.iter()
.find(|(_address, reward)| reward.reward_type == RewardType::Staking)
.unwrap();
bank1.capitalization()
}
@ -6127,11 +6214,13 @@ mod tests {
// Test new account
let key = Keypair::new();
bank.deposit(&key.pubkey(), 10);
let new_balance = bank.deposit(&key.pubkey(), 10);
assert_eq!(new_balance, 10);
assert_eq!(bank.get_balance(&key.pubkey()), 10);
// Existing account
bank.deposit(&key.pubkey(), 3);
let new_balance = bank.deposit(&key.pubkey(), 3);
assert_eq!(new_balance, 13);
assert_eq!(bank.get_balance(&key.pubkey()), 13);
}
@ -6248,6 +6337,18 @@ mod tests {
// verify capitalization
assert_eq!(capitalization - expected_fee_burned, bank.capitalization());
assert_eq!(
*bank.rewards.read().unwrap(),
vec![(
leader,
RewardInfo {
reward_type: RewardType::Fee,
lamports: expected_fee_collected as i64,
post_balance: initial_balance + expected_fee_collected,
}
)]
);
// Verify that an InstructionError collects fees, too
let mut bank = Bank::new_from_parent(&Arc::new(bank), &leader, 1);
let mut tx =
@ -6270,6 +6371,18 @@ mod tests {
bank.get_balance(&leader),
initial_balance + 2 * expected_fee_collected
);
assert_eq!(
*bank.rewards.read().unwrap(),
vec![(
leader,
RewardInfo {
reward_type: RewardType::Fee,
lamports: expected_fee_collected as i64,
post_balance: initial_balance + 2 * expected_fee_collected,
}
)]
);
}
#[test]