Plumb transaction-level rewards (aka "rent debits") into the `getTransaction` RPC method

This commit is contained in:
Michael Vines 2021-05-26 14:43:15 -07:00 committed by mergify[bot]
parent 7dfc1d9790
commit 9541411c15
17 changed files with 146 additions and 16 deletions

View File

@ -322,7 +322,38 @@ pub fn write_transaction<W: io::Write>(
if !log_messages.is_empty() {
writeln!(w, "{}Log Messages:", prefix,)?;
for log_message in log_messages {
writeln!(w, "{} {}", prefix, log_message,)?;
writeln!(w, "{} {}", prefix, log_message)?;
}
}
}
if let Some(rewards) = &transaction_status.rewards {
if !rewards.is_empty() {
writeln!(w, "{}Rewards:", prefix,)?;
writeln!(
w,
"{} {:<44} {:^15} {:<15} {:<20}",
prefix, "Address", "Type", "Amount", "New Balance"
)?;
for reward in rewards {
let sign = if reward.lamports < 0 { "-" } else { "" };
writeln!(
w,
"{} {:<44} {:^15} {:<15} {}",
prefix,
reward.pubkey,
if let Some(reward_type) = reward.reward_type {
format!("{}", reward_type)
} else {
"-".to_string()
},
format!(
"{}◎{:<14.9}",
sign,
lamports_to_sol(reward.lamports.abs() as u64)
),
format!("{:<18.9}", lamports_to_sol(reward.post_balance),)
)?;
}
}
}

View File

@ -815,6 +815,7 @@ impl BankingStage {
TransactionTokenBalancesSet::new(pre_token_balances, post_token_balances),
inner_instructions,
transaction_logs,
tx_results.rent_debits,
);
}
}

View File

@ -7,7 +7,7 @@ use solana_ledger::{
use solana_runtime::bank::{
Bank, InnerInstructionsList, NonceRollbackInfo, TransactionLogMessages,
};
use solana_transaction_status::{InnerInstructions, TransactionStatusMeta};
use solana_transaction_status::{InnerInstructions, Reward, TransactionStatusMeta};
use std::{
sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
@ -62,6 +62,7 @@ impl TransactionStatusService {
token_balances,
inner_instructions,
transaction_logs,
rent_debits,
}) => {
let slot = bank.slot();
let inner_instructions_iter: Box<
@ -86,6 +87,7 @@ impl TransactionStatusService {
post_token_balances,
inner_instructions,
log_messages,
rent_debits,
) in izip!(
&transactions,
statuses,
@ -94,7 +96,8 @@ impl TransactionStatusService {
token_balances.pre_token_balances,
token_balances.post_token_balances,
inner_instructions_iter,
transaction_logs_iter
transaction_logs_iter,
rent_debits.into_iter(),
) {
if Bank::can_commit(&status) && !transaction.signatures.is_empty() {
let fee_calculator = nonce_rollback
@ -123,6 +126,18 @@ impl TransactionStatusService {
let log_messages = Some(log_messages);
let pre_token_balances = Some(pre_token_balances);
let post_token_balances = Some(post_token_balances);
let rewards = Some(
rent_debits
.0
.into_iter()
.map(|(pubkey, reward_info)| Reward {
pubkey: pubkey.to_string(),
lamports: reward_info.lamports,
post_balance: reward_info.post_balance,
reward_type: Some(reward_info.reward_type),
})
.collect(),
);
blockstore
.write_transaction_status(
@ -139,6 +154,7 @@ impl TransactionStatusService {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
},
)
.expect("Expect database write to succeed");

View File

@ -2803,6 +2803,12 @@ Returns transaction details for a confirmed transaction
- DEPRECATED: `status: <object>` - Transaction status
- `"Ok": <null>` - Transaction was successful
- `"Err": <ERR>` - Transaction failed with TransactionError
- `rewards: <array>` - present if rewards are requested; an array of JSON objects containing:
- `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
- `postBalance: <u64>` - account balance in lamports after the reward was applied
- `rewardType: <string>` - type of reward: currently only "rent", other types may be added in the future
#### Example:
Request:

View File

@ -51,7 +51,6 @@ use solana_vote_program::{
};
use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
convert::TryInto,
ffi::OsStr,
fs::{self, File},
io::{self, stdout, BufRead, BufReader, Write},
@ -76,12 +75,27 @@ fn output_slot_rewards(blockstore: &Blockstore, slot: Slot, method: &LedgerOutpu
if let Ok(Some(rewards)) = blockstore.read_rewards(slot) {
if !rewards.is_empty() {
println!(" Rewards:");
println!(
" {:<44} {:^15} {:<15} {:<20}",
"Address", "Type", "Amount", "New Balance"
);
for reward in rewards {
let sign = if reward.lamports < 0 { "-" } else { "" };
println!(
" Account {}: {}{} SOL",
" {:<44} {:^15} {:<15} {}",
reward.pubkey,
if reward.lamports < 0 { '-' } else { ' ' },
lamports_to_sol(reward.lamports.abs().try_into().unwrap())
if let Some(reward_type) = reward.reward_type {
format!("{}", reward_type)
} else {
"-".to_string()
},
format!(
"{}◎{:<14.9}",
sign,
lamports_to_sol(reward.lamports.abs() as u64)
),
format!("{:<18.9}", lamports_to_sol(reward.post_balance))
);
}
}

View File

@ -5985,6 +5985,7 @@ pub mod tests {
log_messages: Some(vec![]),
pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]),
rewards: Some(vec![]),
}
.into();
ledger
@ -6000,6 +6001,7 @@ pub mod tests {
log_messages: Some(vec![]),
pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]),
rewards: Some(vec![]),
}
.into();
ledger
@ -6015,6 +6017,7 @@ pub mod tests {
log_messages: Some(vec![]),
pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]),
rewards: Some(vec![]),
}
.into();
ledger
@ -6032,6 +6035,7 @@ pub mod tests {
log_messages: Some(vec![]),
pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]),
rewards: Some(vec![]),
}),
}
})
@ -6126,6 +6130,7 @@ pub mod tests {
let log_messages_vec = vec![String::from("Test message\n")];
let pre_token_balances_vec = vec![];
let post_token_balances_vec = vec![];
let rewards_vec = vec![];
// result not found
assert!(transaction_status_cf
@ -6149,6 +6154,7 @@ pub mod tests {
log_messages: Some(log_messages_vec.clone()),
pre_token_balances: Some(pre_token_balances_vec.clone()),
post_token_balances: Some(post_token_balances_vec.clone()),
rewards: Some(rewards_vec.clone()),
}
.into();
assert!(transaction_status_cf
@ -6165,6 +6171,7 @@ pub mod tests {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
} = transaction_status_cf
.get_protobuf_or_bincode::<StoredTransactionStatusMeta>((
0,
@ -6183,6 +6190,7 @@ pub mod tests {
assert_eq!(log_messages.unwrap(), log_messages_vec);
assert_eq!(pre_token_balances.unwrap(), pre_token_balances_vec);
assert_eq!(post_token_balances.unwrap(), post_token_balances_vec);
assert_eq!(rewards.unwrap(), rewards_vec);
// insert value
let status = TransactionStatusMeta {
@ -6194,6 +6202,7 @@ pub mod tests {
log_messages: Some(log_messages_vec.clone()),
pre_token_balances: Some(pre_token_balances_vec.clone()),
post_token_balances: Some(post_token_balances_vec.clone()),
rewards: Some(rewards_vec.clone()),
}
.into();
assert!(transaction_status_cf
@ -6210,6 +6219,7 @@ pub mod tests {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
} = transaction_status_cf
.get_protobuf_or_bincode::<StoredTransactionStatusMeta>((
0,
@ -6230,6 +6240,7 @@ pub mod tests {
assert_eq!(log_messages.unwrap(), log_messages_vec);
assert_eq!(pre_token_balances.unwrap(), pre_token_balances_vec);
assert_eq!(post_token_balances.unwrap(), post_token_balances_vec);
assert_eq!(rewards.unwrap(), rewards_vec);
}
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
}
@ -6460,6 +6471,7 @@ pub mod tests {
log_messages: Some(vec![]),
pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]),
rewards: Some(vec![]),
}
.into();
@ -6661,6 +6673,7 @@ pub mod tests {
let log_messages = Some(vec![String::from("Test message\n")]);
let pre_token_balances = Some(vec![]);
let post_token_balances = Some(vec![]);
let rewards = Some(vec![]);
let signature = transaction.signatures[0];
let status = TransactionStatusMeta {
status: Ok(()),
@ -6671,6 +6684,7 @@ pub mod tests {
log_messages: log_messages.clone(),
pre_token_balances: pre_token_balances.clone(),
post_token_balances: post_token_balances.clone(),
rewards: rewards.clone(),
}
.into();
blockstore
@ -6688,6 +6702,7 @@ pub mod tests {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
}),
}
})
@ -6757,6 +6772,7 @@ pub mod tests {
let log_messages = Some(vec![String::from("Test message\n")]);
let pre_token_balances = Some(vec![]);
let post_token_balances = Some(vec![]);
let rewards = Some(vec![]);
let signature = transaction.signatures[0];
let status = TransactionStatusMeta {
status: Ok(()),
@ -6767,6 +6783,7 @@ pub mod tests {
log_messages: log_messages.clone(),
pre_token_balances: pre_token_balances.clone(),
post_token_balances: post_token_balances.clone(),
rewards: rewards.clone(),
}
.into();
blockstore
@ -6784,6 +6801,7 @@ pub mod tests {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
}),
}
})
@ -7511,6 +7529,7 @@ pub mod tests {
log_messages: Some(vec![]),
pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]),
rewards: Some(vec![]),
}
.into();
transaction_status_cf
@ -8045,6 +8064,12 @@ pub mod tests {
ui_amount_string: "1.1".to_string(),
},
}]),
rewards: Some(vec![Reward {
pubkey: "My11111111111111111111111111111111111111111".to_string(),
lamports: -42,
post_balance: 42,
reward_type: Some(RewardType::Rent),
}]),
};
let deprecated_status: StoredTransactionStatusMeta = status.clone().into();
let protobuf_status: generated::TransactionStatusMeta = status.into();

View File

@ -18,7 +18,7 @@ use solana_rayon_threadlimit::get_thread_count;
use solana_runtime::{
accounts_index::AccountSecondaryIndexes,
bank::{
Bank, ExecuteTimings, InnerInstructionsList, TransactionBalancesSet,
Bank, ExecuteTimings, InnerInstructionsList, RentDebits, TransactionBalancesSet,
TransactionExecutionResult, TransactionLogMessages, TransactionResults,
},
bank_forks::BankForks,
@ -130,6 +130,7 @@ fn execute_batch(
let TransactionResults {
fee_collection_results,
execution_results,
rent_debits,
..
} = tx_results;
@ -152,6 +153,7 @@ fn execute_batch(
token_balances,
inner_instructions,
transaction_logs,
rent_debits,
);
}
@ -1164,6 +1166,7 @@ pub struct TransactionStatusBatch {
pub token_balances: TransactionTokenBalancesSet,
pub inner_instructions: Option<Vec<Option<InnerInstructionsList>>>,
pub transaction_logs: Option<Vec<TransactionLogMessages>>,
pub rent_debits: Vec<RentDebits>,
}
#[derive(Clone)]
@ -1182,6 +1185,7 @@ impl TransactionStatusSender {
token_balances: TransactionTokenBalancesSet,
inner_instructions: Vec<Option<InnerInstructionsList>>,
transaction_logs: Vec<TransactionLogMessages>,
rent_debits: Vec<RentDebits>,
) {
let slot = bank.slot();
let (inner_instructions, transaction_logs) = if !self.enable_cpi_and_log_storage {
@ -1199,6 +1203,7 @@ impl TransactionStatusSender {
token_balances,
inner_instructions,
transaction_logs,
rent_debits,
}))
{
trace!(

View File

@ -397,6 +397,7 @@ fn execute_transactions(bank: &Bank, txs: &[Transaction]) -> Vec<ConfirmedTransa
post_token_balances: Some(post_token_balances),
inner_instructions,
log_messages: Some(log_messages),
rewards: None,
};
ConfirmedTransaction {

View File

@ -100,14 +100,13 @@ pub type TransactionAccounts = Vec<AccountSharedData>;
pub type TransactionAccountDeps = Vec<(Pubkey, AccountSharedData)>;
pub type TransactionRent = u64;
pub type TransactionLoaders = Vec<Vec<(Pubkey, AccountSharedData)>>;
pub type TransactionRentDebits = RentDebits;
#[derive(PartialEq, Debug, Clone)]
pub struct LoadedTransaction {
pub accounts: TransactionAccounts,
pub account_deps: TransactionAccountDeps,
pub loaders: TransactionLoaders,
pub rent: TransactionRent,
pub rent_debits: TransactionRentDebits,
pub rent_debits: RentDebits,
}
pub type TransactionLoadResult = (Result<LoadedTransaction>, Option<NonceRollbackFull>);

View File

@ -484,6 +484,7 @@ pub struct TransactionResults {
pub fee_collection_results: Vec<Result<()>>,
pub execution_results: Vec<TransactionExecutionResult>,
pub overwritten_vote_accounts: Vec<OverwrittenVoteAccount>,
pub rent_debits: Vec<RentDebits>,
}
pub struct TransactionBalancesSet {
pub pre_balances: TransactionBalances,
@ -3389,7 +3390,7 @@ impl Bank {
self.fix_recent_blockhashes_sysvar_delay(),
self.demote_sysvar_write_locks(),
);
self.collect_rent(executed, loaded_accounts);
let rent_debits = self.collect_rent(executed, loaded_accounts);
let overwritten_vote_accounts = self.update_cached_accounts(
hashed_txs.as_transactions_iter(),
@ -3413,6 +3414,7 @@ impl Bank {
fee_collection_results,
execution_results: executed.to_vec(),
overwritten_vote_accounts,
rent_debits,
}
}
@ -3575,24 +3577,24 @@ impl Bank {
&self,
res: &[TransactionExecutionResult],
loaded_accounts: &mut [TransactionLoadResult],
) {
) -> Vec<RentDebits> {
let mut collected_rent: u64 = 0;
let mut rent_debits: Vec<RentDebits> = Vec::with_capacity(loaded_accounts.len());
for (i, (raccs, _nonce_rollback)) in loaded_accounts.iter_mut().enumerate() {
let (res, _nonce_rollback) = &res[i];
if res.is_err() || raccs.is_err() {
rent_debits.push(RentDebits::default());
continue;
}
let loaded_transaction = raccs.as_mut().unwrap();
collected_rent += loaded_transaction.rent;
self.rewards
.write()
.unwrap()
.append(&mut loaded_transaction.rent_debits.0);
rent_debits.push(mem::take(&mut loaded_transaction.rent_debits));
}
self.collected_rent.fetch_add(collected_rent, Relaxed);
rent_debits
}
fn run_incinerator(&self) {

View File

@ -684,6 +684,7 @@ mod tests {
log_messages: Some(vec![]),
pre_token_balances: Some(vec![]),
post_token_balances: Some(vec![]),
rewards: Some(vec![]),
}),
};
let block = ConfirmedBlock {
@ -735,6 +736,7 @@ mod tests {
meta.log_messages = None; // Legacy bincode implementation does not support log_messages
meta.pre_token_balances = None; // Legacy bincode implementation does not support token balances
meta.post_token_balances = None; // Legacy bincode implementation does not support token balances
meta.rewards = None; // Legacy bincode implementation does not support rewards
}
assert_eq!(block, bincode_block.into());
} else {

View File

@ -188,6 +188,7 @@ impl From<StoredConfirmedBlockTransactionStatusMeta> for TransactionStatusMeta {
log_messages: None,
pre_token_balances: None,
post_token_balances: None,
rewards: None,
}
}
}

View File

@ -65,6 +65,8 @@ pub struct TransactionStatusMeta {
pub pre_token_balances: ::prost::alloc::vec::Vec<TokenBalance>,
#[prost(message, repeated, tag = "8")]
pub post_token_balances: ::prost::alloc::vec::Vec<TokenBalance>,
#[prost(message, repeated, tag = "9")]
pub rewards: ::prost::alloc::vec::Vec<Reward>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionError {

View File

@ -43,6 +43,7 @@ message TransactionStatusMeta {
repeated string log_messages = 6;
repeated TokenBalance pre_token_balances = 7;
repeated TokenBalance post_token_balances = 8;
repeated Reward rewards = 9;
}
message TransactionError {

View File

@ -267,6 +267,7 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
} = value;
let err = match status {
Ok(()) => None,
@ -290,6 +291,11 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
.into_iter()
.map(|balance| balance.into())
.collect();
let rewards = rewards
.unwrap_or_default()
.into_iter()
.map(|reward| reward.into())
.collect();
Self {
err,
@ -300,6 +306,7 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
}
}
}
@ -324,6 +331,7 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
} = value;
let status = match &err {
None => Ok(()),
@ -348,6 +356,7 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
.map(|balance| balance.into())
.collect(),
);
let rewards = Some(rewards.into_iter().map(|reward| reward.into()).collect());
Ok(Self {
status,
fee,
@ -357,6 +366,7 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
})
}
}

View File

@ -150,6 +150,8 @@ pub struct StoredTransactionStatusMeta {
pub pre_token_balances: Option<Vec<StoredTransactionTokenBalance>>,
#[serde(deserialize_with = "default_on_eof")]
pub post_token_balances: Option<Vec<StoredTransactionTokenBalance>>,
#[serde(deserialize_with = "default_on_eof")]
pub rewards: Option<Vec<StoredExtendedReward>>,
}
impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
@ -163,6 +165,7 @@ impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
} = value;
Self {
status,
@ -175,6 +178,8 @@ impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
.map(|balances| balances.into_iter().map(|balance| balance.into()).collect()),
post_token_balances: post_token_balances
.map(|balances| balances.into_iter().map(|balance| balance.into()).collect()),
rewards: rewards
.map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()),
}
}
}
@ -190,6 +195,7 @@ impl From<TransactionStatusMeta> for StoredTransactionStatusMeta {
log_messages,
pre_token_balances,
post_token_balances,
rewards,
} = value;
Self {
status,
@ -202,6 +208,8 @@ impl From<TransactionStatusMeta> for StoredTransactionStatusMeta {
.map(|balances| balances.into_iter().map(|balance| balance.into()).collect()),
post_token_balances: post_token_balances
.map(|balances| balances.into_iter().map(|balance| balance.into()).collect()),
rewards: rewards
.map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()),
}
}
}

View File

@ -185,6 +185,8 @@ pub struct TransactionStatusMeta {
pub pre_token_balances: Option<Vec<TransactionTokenBalance>>,
#[serde(deserialize_with = "default_on_eof")]
pub post_token_balances: Option<Vec<TransactionTokenBalance>>,
#[serde(deserialize_with = "default_on_eof")]
pub rewards: Option<Rewards>,
}
impl Default for TransactionStatusMeta {
@ -198,6 +200,7 @@ impl Default for TransactionStatusMeta {
log_messages: None,
pre_token_balances: None,
post_token_balances: None,
rewards: None,
}
}
}
@ -215,6 +218,7 @@ pub struct UiTransactionStatusMeta {
pub log_messages: Option<Vec<String>>,
pub pre_token_balances: Option<Vec<UiTransactionTokenBalance>>,
pub post_token_balances: Option<Vec<UiTransactionTokenBalance>>,
pub rewards: Option<Rewards>,
}
impl UiTransactionStatusMeta {
@ -237,6 +241,7 @@ impl UiTransactionStatusMeta {
post_token_balances: meta
.post_token_balances
.map(|balance| balance.into_iter().map(|balance| balance.into()).collect()),
rewards: meta.rewards,
}
}
}
@ -259,6 +264,7 @@ impl From<TransactionStatusMeta> for UiTransactionStatusMeta {
post_token_balances: meta
.post_token_balances
.map(|balance| balance.into_iter().map(|balance| balance.into()).collect()),
rewards: meta.rewards,
}
}
}