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

1
Cargo.lock generated
View File

@ -4659,6 +4659,7 @@ dependencies = [
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"solana-account-decoder", "solana-account-decoder",
"solana-runtime",
"solana-sdk 1.5.0", "solana-sdk 1.5.0",
"solana-stake-program", "solana-stake-program",
"solana-vote-program", "solana-vote-program",

View File

@ -98,7 +98,7 @@ pub enum CliCommand {
Fees, Fees,
FirstAvailableBlock, FirstAvailableBlock,
GetBlock { GetBlock {
slot: Slot, slot: Option<Slot>,
}, },
GetBlockTime { GetBlockTime {
slot: Option<Slot>, slot: Option<Slot>,

View File

@ -73,8 +73,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.validator(is_slot) .validator(is_slot)
.value_name("SLOT") .value_name("SLOT")
.takes_value(true) .takes_value(true)
.index(1) .index(1),
.required(true),
), ),
) )
.subcommand( .subcommand(
@ -363,7 +362,7 @@ pub fn parse_cluster_ping(
} }
pub fn parse_get_block(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_get_block(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let slot = value_t_or_exit!(matches, "slot", Slot); let slot = value_of(matches, "slot");
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::GetBlock { slot }, command: CliCommand::GetBlock { slot },
signers: vec![], signers: vec![],
@ -700,7 +699,17 @@ pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
Ok("".to_string()) Ok("".to_string())
} }
pub fn process_get_block(rpc_client: &RpcClient, _config: &CliConfig, slot: Slot) -> ProcessResult { pub fn process_get_block(
rpc_client: &RpcClient,
_config: &CliConfig,
slot: Option<Slot>,
) -> ProcessResult {
let slot = if let Some(slot) = slot {
slot
} else {
rpc_client.get_slot()?
};
let mut block = let mut block =
rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?; rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?;
@ -716,18 +725,23 @@ pub fn process_get_block(rpc_client: &RpcClient, _config: &CliConfig, slot: Slot
let mut total_rewards = 0; let mut total_rewards = 0;
println!("Rewards:",); println!("Rewards:",);
println!( println!(
" {:<44} {:<15} {:<13} {:>14}", " {:<44} {:^15} {:<15} {:<20} {:>14}",
"Address", "Amount", "New Balance", "Percent Change" "Address", "Type", "Amount", "New Balance", "Percent Change"
); );
for reward in block.rewards { for reward in block.rewards {
let sign = if reward.lamports < 0 { "-" } else { "" }; let sign = if reward.lamports < 0 { "-" } else { "" };
total_rewards += reward.lamports; total_rewards += reward.lamports;
println!( println!(
" {:<44} {:>15} {}", " {:<44} {:^15} {:>15} {}",
reward.pubkey, reward.pubkey,
if let Some(reward_type) = reward.reward_type {
format!("{}", reward_type)
} else {
"-".to_string()
},
format!( format!(
"{}◎{:<14.4}", "{}◎{:<14.9}",
sign, sign,
lamports_to_sol(reward.lamports.abs() as u64) lamports_to_sol(reward.lamports.abs() as u64)
), ),
@ -735,7 +749,7 @@ pub fn process_get_block(rpc_client: &RpcClient, _config: &CliConfig, slot: Slot
" - -".to_string() " - -".to_string()
} else { } else {
format!( format!(
"◎{:<12.4} {:>13.9}%", "◎{:<19.9} {:>13.9}%",
lamports_to_sol(reward.post_balance), lamports_to_sol(reward.post_balance),
reward.lamports.abs() as f64 reward.lamports.abs() as f64
/ (reward.post_balance as f64 - reward.lamports as f64) / (reward.post_balance as f64 - reward.lamports as f64)
@ -746,7 +760,7 @@ pub fn process_get_block(rpc_client: &RpcClient, _config: &CliConfig, slot: Slot
let sign = if total_rewards < 0 { "-" } else { "" }; let sign = if total_rewards < 0 { "-" } else { "" };
println!( println!(
"Total Rewards: {}◎{:12.9}", "Total Rewards: {}◎{:<12.9}",
sign, sign,
lamports_to_sol(total_rewards.abs() as u64) lamports_to_sol(total_rewards.abs() as u64)
); );

View File

@ -291,7 +291,6 @@ impl ReplayStage {
&bank_forks, &bank_forks,
&leader_schedule_cache, &leader_schedule_cache,
&subscriptions, &subscriptions,
rewards_recorder_sender.clone(),
&mut progress, &mut progress,
&mut all_pubkeys, &mut all_pubkeys,
); );
@ -313,6 +312,7 @@ impl ReplayStage {
&mut heaviest_subtree_fork_choice, &mut heaviest_subtree_fork_choice,
&replay_vote_sender, &replay_vote_sender,
&bank_notification_sender, &bank_notification_sender,
&rewards_recorder_sender,
); );
replay_active_banks_time.stop(); replay_active_banks_time.stop();
Self::report_memory(&allocated, "replay_active_banks", start); Self::report_memory(&allocated, "replay_active_banks", start);
@ -554,7 +554,6 @@ impl ReplayStage {
&poh_recorder, &poh_recorder,
&leader_schedule_cache, &leader_schedule_cache,
&subscriptions, &subscriptions,
rewards_recorder_sender.clone(),
&progress, &progress,
&retransmit_slots_sender, &retransmit_slots_sender,
&mut skipped_slots_info, &mut skipped_slots_info,
@ -854,7 +853,6 @@ impl ReplayStage {
poh_recorder: &Arc<Mutex<PohRecorder>>, poh_recorder: &Arc<Mutex<PohRecorder>>,
leader_schedule_cache: &Arc<LeaderScheduleCache>, leader_schedule_cache: &Arc<LeaderScheduleCache>,
subscriptions: &Arc<RpcSubscriptions>, subscriptions: &Arc<RpcSubscriptions>,
rewards_recorder_sender: Option<RewardsRecorderSender>,
progress_map: &ProgressMap, progress_map: &ProgressMap,
retransmit_slots_sender: &RetransmitSlotsSender, retransmit_slots_sender: &RetransmitSlotsSender,
skipped_slots_info: &mut SkippedSlotsInfo, skipped_slots_info: &mut SkippedSlotsInfo,
@ -953,7 +951,6 @@ impl ReplayStage {
poh_slot, poh_slot,
root_slot, root_slot,
my_pubkey, my_pubkey,
&rewards_recorder_sender,
subscriptions, subscriptions,
); );
@ -1265,6 +1262,7 @@ impl ReplayStage {
heaviest_subtree_fork_choice: &mut HeaviestSubtreeForkChoice, heaviest_subtree_fork_choice: &mut HeaviestSubtreeForkChoice,
replay_vote_sender: &ReplayVoteSender, replay_vote_sender: &ReplayVoteSender,
bank_notification_sender: &Option<BankNotificationSender>, bank_notification_sender: &Option<BankNotificationSender>,
rewards_recorder_sender: &Option<RewardsRecorderSender>,
) -> bool { ) -> bool {
let mut did_complete_bank = false; let mut did_complete_bank = false;
let mut tx_count = 0; let mut tx_count = 0;
@ -1340,6 +1338,8 @@ impl ReplayStage {
.send(BankNotification::Frozen(bank.clone())) .send(BankNotification::Frozen(bank.clone()))
.unwrap_or_else(|err| warn!("bank_notification_sender failed: {:?}", err)); .unwrap_or_else(|err| warn!("bank_notification_sender failed: {:?}", err));
} }
Self::record_rewards(&bank, &rewards_recorder_sender);
} else { } else {
trace!( trace!(
"bank {} not completed tick_height: {}, max_tick_height: {}", "bank {} not completed tick_height: {}, max_tick_height: {}",
@ -1817,7 +1817,6 @@ impl ReplayStage {
bank_forks: &RwLock<BankForks>, bank_forks: &RwLock<BankForks>,
leader_schedule_cache: &Arc<LeaderScheduleCache>, leader_schedule_cache: &Arc<LeaderScheduleCache>,
subscriptions: &Arc<RpcSubscriptions>, subscriptions: &Arc<RpcSubscriptions>,
rewards_recorder_sender: Option<RewardsRecorderSender>,
progress: &mut ProgressMap, progress: &mut ProgressMap,
all_pubkeys: &mut PubkeyReferences, all_pubkeys: &mut PubkeyReferences,
) { ) {
@ -1863,7 +1862,6 @@ impl ReplayStage {
child_slot, child_slot,
forks.root(), forks.root(),
&leader, &leader,
&rewards_recorder_sender,
subscriptions, subscriptions,
); );
let empty: Vec<&Pubkey> = vec![]; let empty: Vec<&Pubkey> = vec![];
@ -1891,21 +1889,18 @@ impl ReplayStage {
slot: u64, slot: u64,
root_slot: u64, root_slot: u64,
leader: &Pubkey, leader: &Pubkey,
rewards_recorder_sender: &Option<RewardsRecorderSender>,
subscriptions: &Arc<RpcSubscriptions>, subscriptions: &Arc<RpcSubscriptions>,
) -> Bank { ) -> Bank {
subscriptions.notify_slot(slot, parent.slot(), root_slot); subscriptions.notify_slot(slot, parent.slot(), root_slot);
Bank::new_from_parent(parent, leader, slot)
let child_bank = Bank::new_from_parent(parent, leader, slot);
Self::record_rewards(&child_bank, &rewards_recorder_sender);
child_bank
} }
fn record_rewards(bank: &Bank, rewards_recorder_sender: &Option<RewardsRecorderSender>) { fn record_rewards(bank: &Bank, rewards_recorder_sender: &Option<RewardsRecorderSender>) {
if let Some(rewards_recorder_sender) = rewards_recorder_sender { if let Some(rewards_recorder_sender) = rewards_recorder_sender {
if let Some(ref rewards) = bank.rewards { let rewards = bank.rewards.read().unwrap();
if !rewards.is_empty() {
rewards_recorder_sender rewards_recorder_sender
.send((bank.slot(), rewards.iter().copied().collect())) .send((bank.slot(), rewards.clone()))
.unwrap_or_else(|err| warn!("rewards_recorder_sender failed: {:?}", err)); .unwrap_or_else(|err| warn!("rewards_recorder_sender failed: {:?}", err));
} }
} }
@ -2155,7 +2150,6 @@ pub(crate) mod tests {
&bank_forks, &bank_forks,
&leader_schedule_cache, &leader_schedule_cache,
&rpc_subscriptions, &rpc_subscriptions,
None,
&mut progress, &mut progress,
&mut PubkeyReferences::default(), &mut PubkeyReferences::default(),
); );
@ -2179,7 +2173,6 @@ pub(crate) mod tests {
&bank_forks, &bank_forks,
&leader_schedule_cache, &leader_schedule_cache,
&rpc_subscriptions, &rpc_subscriptions,
None,
&mut progress, &mut progress,
&mut PubkeyReferences::default(), &mut PubkeyReferences::default(),
); );

View File

@ -54,6 +54,7 @@ impl RewardsRecorderService {
pubkey: pubkey.to_string(), pubkey: pubkey.to_string(),
lamports: reward_info.lamports, lamports: reward_info.lamports,
post_balance: reward_info.post_balance, post_balance: reward_info.post_balance,
reward_type: Some(reward_info.reward_type),
}) })
.collect(); .collect();

View File

@ -342,6 +342,7 @@ The result field will be an object with the following fields:
- `pubkey: <string>` - The public key, as base-58 encoded string, of the account that received the reward - `pubkey: <string>` - The public key, as base-58 encoded string, of the account that received the reward
- `lamports: <i64>`- number of reward lamports credited or debited by the account, as a i64 - `lamports: <i64>`- number of reward lamports credited or debited by the account, as a i64
- `postBalance: <u64>` - account balance in lamports after the reward was applied - `postBalance: <u64>` - account balance in lamports after the reward was applied
- `rewardType: <string|undefined>` - type of reward: "fee", "rent", "voting", "staking"
- `blockTime: <i64 | null>` - estimated production time, as Unix timestamp (seconds since the Unix epoch). null if not available - `blockTime: <i64 | null>` - estimated production time, as Unix timestamp (seconds since the Unix epoch). null if not available
#### Example: #### Example:

View File

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

View File

@ -91,9 +91,20 @@ pub struct Reward {
pub lamports: i64, pub lamports: i64,
#[prost(uint64, tag = "3")] #[prost(uint64, tag = "3")]
pub post_balance: u64, pub post_balance: u64,
#[prost(enumeration = "RewardType", tag = "4")]
pub reward_type: i32,
} }
#[derive(Clone, PartialEq, ::prost::Message)] #[derive(Clone, PartialEq, ::prost::Message)]
pub struct UnixTimestamp { pub struct UnixTimestamp {
#[prost(int64, tag = "1")] #[prost(int64, tag = "1")]
pub timestamp: i64, pub timestamp: i64,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum RewardType {
Unspecified = 0,
Fee = 1,
Rent = 2,
Staking = 3,
Voting = 4,
}

View File

@ -57,10 +57,19 @@ message CompiledInstruction {
bytes data = 3; bytes data = 3;
} }
enum RewardType {
Unspecified = 0;
Fee = 1;
Rent = 2;
Staking = 3;
Voting = 4;
}
message Reward { message Reward {
string pubkey = 1; string pubkey = 1;
int64 lamports = 2; int64 lamports = 2;
uint64 post_balance = 3; uint64 post_balance = 3;
RewardType reward_type = 4;
} }
message UnixTimestamp { message UnixTimestamp {

View File

@ -7,7 +7,8 @@ use solana_sdk::{
transaction::Transaction, transaction::Transaction,
}; };
use solana_transaction_status::{ use solana_transaction_status::{
ConfirmedBlock, InnerInstructions, Reward, TransactionStatusMeta, TransactionWithStatusMeta, ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionStatusMeta,
TransactionWithStatusMeta,
}; };
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
@ -24,6 +25,13 @@ impl From<Reward> for generated::Reward {
pubkey: reward.pubkey, pubkey: reward.pubkey,
lamports: reward.lamports, lamports: reward.lamports,
post_balance: reward.post_balance, post_balance: reward.post_balance,
reward_type: match reward.reward_type {
None => generated::RewardType::Unspecified,
Some(RewardType::Fee) => generated::RewardType::Fee,
Some(RewardType::Rent) => generated::RewardType::Rent,
Some(RewardType::Staking) => generated::RewardType::Staking,
Some(RewardType::Voting) => generated::RewardType::Voting,
} as i32,
} }
} }
} }
@ -34,6 +42,14 @@ impl From<generated::Reward> for Reward {
pubkey: reward.pubkey, pubkey: reward.pubkey,
lamports: reward.lamports, lamports: reward.lamports,
post_balance: reward.post_balance, post_balance: reward.post_balance,
reward_type: match reward.reward_type {
0 => None,
1 => Some(RewardType::Fee),
2 => Some(RewardType::Rent),
3 => Some(RewardType::Voting),
4 => Some(RewardType::Staking),
_ => None,
},
} }
} }
} }

View File

@ -222,6 +222,7 @@ impl From<StoredConfirmedBlockReward> for Reward {
pubkey, pubkey,
lamports, lamports,
post_balance: 0, post_balance: 0,
reward_type: None,
} }
} }
} }

View File

@ -19,6 +19,7 @@ serde_derive = "1.0.103"
serde_json = "1.0.56" serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "1.5.0" } solana-account-decoder = { path = "../account-decoder", version = "1.5.0" }
solana-sdk = { path = "../sdk", version = "1.5.0" } solana-sdk = { path = "../sdk", version = "1.5.0" }
solana-runtime = { path = "../runtime", version = "1.5.0" }
solana-stake-program = { path = "../programs/stake", version = "1.5.0" } solana-stake-program = { path = "../programs/stake", version = "1.5.0" }
solana-vote-program = { path = "../programs/vote", version = "1.5.0" } solana-vote-program = { path = "../programs/vote", version = "1.5.0" }
spl-memo-v1-0 = { package = "spl-memo", version = "=1.0.7", features = ["skip-no-mangle"] } spl-memo-v1-0 = { package = "spl-memo", version = "=1.0.7", features = ["skip-no-mangle"] }

View File

@ -11,6 +11,7 @@ use crate::{
parse_accounts::{parse_accounts, ParsedAccount}, parse_accounts::{parse_accounts, ParsedAccount},
parse_instruction::{parse, ParsedInstruction}, parse_instruction::{parse, ParsedInstruction},
}; };
pub use solana_runtime::bank::RewardType;
use solana_sdk::{ use solana_sdk::{
clock::{Slot, UnixTimestamp}, clock::{Slot, UnixTimestamp},
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
@ -241,6 +242,8 @@ pub struct Reward {
pub lamports: i64, pub lamports: i64,
#[serde(deserialize_with = "default_on_eof")] #[serde(deserialize_with = "default_on_eof")]
pub post_balance: u64, // Account balance in lamports after `lamports` was applied pub post_balance: u64, // Account balance in lamports after `lamports` was applied
#[serde(default, deserialize_with = "default_on_eof")]
pub reward_type: Option<RewardType>,
} }
pub type Rewards = Vec<Reward>; pub type Rewards = Vec<Reward>;