diff --git a/cli-output/src/display.rs b/cli-output/src/display.rs index 405e57de00..ec4f8b4a41 100644 --- a/cli-output/src/display.rs +++ b/cli-output/src/display.rs @@ -322,7 +322,38 @@ pub fn write_transaction( 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),) + )?; } } } diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index de08c90619..34684e7a3c 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -816,6 +816,7 @@ impl BankingStage { TransactionTokenBalancesSet::new(pre_token_balances, post_token_balances), inner_instructions, transaction_logs, + tx_results.rent_debits, ); } } diff --git a/core/src/transaction_status_service.rs b/core/src/transaction_status_service.rs index 2cf3cbb53d..148648b39f 100644 --- a/core/src/transaction_status_service.rs +++ b/core/src/transaction_status_service.rs @@ -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"); diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index cf92f5e5b0..618244f949 100644 --- a/docs/src/developing/clients/jsonrpc-api.md +++ b/docs/src/developing/clients/jsonrpc-api.md @@ -1015,6 +1015,11 @@ Returns transaction details for a confirmed transaction - DEPRECATED: `status: ` - Transaction status - `"Ok": ` - Transaction was successful - `"Err": ` - Transaction failed with TransactionError + - `rewards: ` - present if rewards are requested; an array of JSON objects containing: + - `pubkey: ` - The public key, as base-58 encoded string, of the account that received the reward + - `lamports: `- number of reward lamports credited or debited by the account, as a i64 + - `postBalance: ` - account balance in lamports after the reward was applied + - `rewardType: ` - type of reward: currently only "rent", other types may be added in the future #### Example: Request: diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 3e0d86bbff..cb431b6463 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -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)) ); } } diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index 0cdf8244cc..d0a27a85fb 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -5926,6 +5926,7 @@ pub mod tests { log_messages: Some(vec![]), pre_token_balances: Some(vec![]), post_token_balances: Some(vec![]), + rewards: Some(vec![]), } .into(); ledger @@ -5941,6 +5942,7 @@ pub mod tests { log_messages: Some(vec![]), pre_token_balances: Some(vec![]), post_token_balances: Some(vec![]), + rewards: Some(vec![]), } .into(); ledger @@ -5956,6 +5958,7 @@ pub mod tests { log_messages: Some(vec![]), pre_token_balances: Some(vec![]), post_token_balances: Some(vec![]), + rewards: Some(vec![]), } .into(); ledger @@ -5973,6 +5976,7 @@ pub mod tests { log_messages: Some(vec![]), pre_token_balances: Some(vec![]), post_token_balances: Some(vec![]), + rewards: Some(vec![]), }), } }) @@ -6067,6 +6071,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 @@ -6090,6 +6095,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 @@ -6106,6 +6112,7 @@ pub mod tests { log_messages, pre_token_balances, post_token_balances, + rewards, } = transaction_status_cf .get_protobuf_or_bincode::(( 0, @@ -6124,6 +6131,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 { @@ -6135,6 +6143,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 @@ -6151,6 +6160,7 @@ pub mod tests { log_messages, pre_token_balances, post_token_balances, + rewards, } = transaction_status_cf .get_protobuf_or_bincode::(( 0, @@ -6171,6 +6181,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"); } @@ -6401,6 +6412,7 @@ pub mod tests { log_messages: Some(vec![]), pre_token_balances: Some(vec![]), post_token_balances: Some(vec![]), + rewards: Some(vec![]), } .into(); @@ -6602,6 +6614,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(()), @@ -6612,6 +6625,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 @@ -6629,6 +6643,7 @@ pub mod tests { log_messages, pre_token_balances, post_token_balances, + rewards, }), } }) @@ -6698,6 +6713,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(()), @@ -6708,6 +6724,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 @@ -6725,6 +6742,7 @@ pub mod tests { log_messages, pre_token_balances, post_token_balances, + rewards, }), } }) @@ -7452,6 +7470,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 @@ -7986,6 +8005,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(); diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index f108a75a30..327433bd8c 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -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>>, pub transaction_logs: Option>, + pub rent_debits: Vec, } #[derive(Clone)] @@ -1182,6 +1185,7 @@ impl TransactionStatusSender { token_balances: TransactionTokenBalancesSet, inner_instructions: Vec>, transaction_logs: Vec, + rent_debits: Vec, ) { 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!( diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 55c911a9db..98ec43043a 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -382,6 +382,7 @@ fn execute_transactions(bank: &Bank, txs: &[Transaction]) -> Vec; pub type TransactionAccountDeps = Vec<(Pubkey, AccountSharedData)>; pub type TransactionRent = u64; pub type TransactionLoaders = Vec>; -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, Option); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index f2b8b241d7..6f44cab106 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -400,6 +400,7 @@ pub struct TransactionResults { pub fee_collection_results: Vec>, pub execution_results: Vec, pub overwritten_vote_accounts: Vec, + pub rent_debits: Vec, } pub struct TransactionBalancesSet { pub pre_balances: TransactionBalances, @@ -3286,7 +3287,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(), @@ -3310,6 +3311,7 @@ impl Bank { fee_collection_results, execution_results: executed.to_vec(), overwritten_vote_accounts, + rent_debits, } } @@ -3458,24 +3460,24 @@ impl Bank { &self, res: &[TransactionExecutionResult], loaded_accounts: &mut [TransactionLoadResult], - ) { + ) -> Vec { let mut collected_rent: u64 = 0; + let mut rent_debits: Vec = 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) { diff --git a/storage-bigtable/src/bigtable.rs b/storage-bigtable/src/bigtable.rs index 93f95b1f88..eb04440326 100644 --- a/storage-bigtable/src/bigtable.rs +++ b/storage-bigtable/src/bigtable.rs @@ -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 { diff --git a/storage-bigtable/src/lib.rs b/storage-bigtable/src/lib.rs index 895e057db4..937a495f16 100644 --- a/storage-bigtable/src/lib.rs +++ b/storage-bigtable/src/lib.rs @@ -188,6 +188,7 @@ impl From for TransactionStatusMeta { log_messages: None, pre_token_balances: None, post_token_balances: None, + rewards: None, } } } diff --git a/storage-proto/proto/solana.storage.confirmed_block.rs b/storage-proto/proto/solana.storage.confirmed_block.rs index 3ca842dad7..c8fb7d014d 100644 --- a/storage-proto/proto/solana.storage.confirmed_block.rs +++ b/storage-proto/proto/solana.storage.confirmed_block.rs @@ -65,6 +65,8 @@ pub struct TransactionStatusMeta { pub pre_token_balances: ::prost::alloc::vec::Vec, #[prost(message, repeated, tag = "8")] pub post_token_balances: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "9")] + pub rewards: ::prost::alloc::vec::Vec, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct TransactionError { diff --git a/storage-proto/src/confirmed_block.proto b/storage-proto/src/confirmed_block.proto index d492a359d6..78b7eb2b81 100644 --- a/storage-proto/src/confirmed_block.proto +++ b/storage-proto/src/confirmed_block.proto @@ -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 { diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index 5f98420087..adbc3c1817 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -267,6 +267,7 @@ impl From 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 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 for generated::TransactionStatusMeta { log_messages, pre_token_balances, post_token_balances, + rewards, } } } @@ -324,6 +331,7 @@ impl TryFrom for TransactionStatusMeta { log_messages, pre_token_balances, post_token_balances, + rewards, } = value; let status = match &err { None => Ok(()), @@ -348,6 +356,7 @@ impl TryFrom 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 for TransactionStatusMeta { log_messages, pre_token_balances, post_token_balances, + rewards, }) } } diff --git a/storage-proto/src/lib.rs b/storage-proto/src/lib.rs index 1a4148987f..a132a8797f 100644 --- a/storage-proto/src/lib.rs +++ b/storage-proto/src/lib.rs @@ -150,6 +150,8 @@ pub struct StoredTransactionStatusMeta { pub pre_token_balances: Option>, #[serde(deserialize_with = "default_on_eof")] pub post_token_balances: Option>, + #[serde(deserialize_with = "default_on_eof")] + pub rewards: Option>, } impl From for TransactionStatusMeta { @@ -163,6 +165,7 @@ impl From for TransactionStatusMeta { log_messages, pre_token_balances, post_token_balances, + rewards, } = value; Self { status, @@ -175,6 +178,8 @@ impl From 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 for StoredTransactionStatusMeta { log_messages, pre_token_balances, post_token_balances, + rewards, } = value; Self { status, @@ -202,6 +208,8 @@ impl From 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()), } } } diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index 942e581326..3b10974e3c 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -185,6 +185,8 @@ pub struct TransactionStatusMeta { pub pre_token_balances: Option>, #[serde(deserialize_with = "default_on_eof")] pub post_token_balances: Option>, + #[serde(deserialize_with = "default_on_eof")] + pub rewards: Option, } 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>, pub pre_token_balances: Option>, pub post_token_balances: Option>, + pub rewards: Option, } 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 for UiTransactionStatusMeta { post_token_balances: meta .post_token_balances .map(|balance| balance.into_iter().map(|balance| balance.into()).collect()), + rewards: meta.rewards, } } }