diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index ddbc41ef20..4ea68e0b63 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -328,6 +328,7 @@ pub struct RpcSimulateTransactionResult { pub err: Option, pub logs: Option>, pub accounts: Option>>, + pub units_consumed: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 31db823a2b..f0034178cd 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -43,7 +43,7 @@ use { solana_runtime::{ accounts::AccountAddressFilter, accounts_index::{AccountIndex, AccountSecondaryIndexes, IndexKey}, - bank::Bank, + bank::{Bank, TransactionSimulationResult}, bank_forks::BankForks, commitment::{BlockCommitmentArray, BlockCommitmentCache, CommitmentSlots}, inline_spl_token_v2_0::{SPL_TOKEN_ACCOUNT_MINT_OFFSET, SPL_TOKEN_ACCOUNT_OWNER_OFFSET}, @@ -3029,7 +3029,13 @@ pub mod rpc_full { } } - if let (Err(err), logs, _) = preflight_bank.simulate_transaction(&transaction) { + if let TransactionSimulationResult { + result: Err(err), + logs, + post_simulation_accounts: _, + units_consumed, + } = preflight_bank.simulate_transaction(&transaction) + { match err { TransactionError::BlockhashNotFound => { inc_new_counter_info!("rpc-send-tx_err-blockhash-not-found", 1); @@ -3044,6 +3050,7 @@ pub mod rpc_full { err: Some(err), logs: Some(logs), accounts: None, + units_consumed: Some(units_consumed), }, } .into()); @@ -3087,7 +3094,12 @@ pub mod rpc_full { if config.replace_recent_blockhash { transaction.message.recent_blockhash = bank.last_blockhash(); } - let (result, logs, post_simulation_accounts) = bank.simulate_transaction(&transaction); + let TransactionSimulationResult { + result, + logs, + post_simulation_accounts, + units_consumed, + } = bank.simulate_transaction(&transaction); let accounts = if let Some(config_accounts) = config.accounts { let accounts_encoding = config_accounts @@ -3142,6 +3154,7 @@ pub mod rpc_full { err: result.err(), logs: Some(logs), accounts, + units_consumed: Some(units_consumed), }, )) } @@ -5183,7 +5196,8 @@ pub mod tests { "logs":[ "Program 11111111111111111111111111111111 invoke [1]", "Program 11111111111111111111111111111111 success" - ] + ], + "unitsConsumed":0 } }, "id": 1, @@ -5262,10 +5276,15 @@ pub mod tests { "jsonrpc": "2.0", "result": { "context":{"slot":0}, - "value":{"accounts": null, "err":null, "logs":[ - "Program 11111111111111111111111111111111 invoke [1]", - "Program 11111111111111111111111111111111 success" - ]} + "value":{ + "accounts":null, + "err":null, + "logs":[ + "Program 11111111111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 success" + ], + "unitsConsumed":0 + } }, "id": 1, }); @@ -5285,10 +5304,15 @@ pub mod tests { "jsonrpc": "2.0", "result": { "context":{"slot":0}, - "value":{"accounts": null, "err":null, "logs":[ - "Program 11111111111111111111111111111111 invoke [1]", - "Program 11111111111111111111111111111111 success" - ]} + "value":{ + "accounts":null, + "err":null, + "logs":[ + "Program 11111111111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 success" + ], + "unitsConsumed":0 + } }, "id": 1, }); @@ -5333,7 +5357,12 @@ pub mod tests { "jsonrpc":"2.0", "result": { "context":{"slot":0}, - "value":{"err": "BlockhashNotFound", "accounts": null, "logs":[]} + "value":{ + "err":"BlockhashNotFound", + "accounts":null, + "logs":[], + "unitsConsumed":0 + } }, "id":1 }); @@ -5354,10 +5383,15 @@ pub mod tests { "jsonrpc": "2.0", "result": { "context":{"slot":0}, - "value":{"accounts": null, "err":null, "logs":[ - "Program 11111111111111111111111111111111 invoke [1]", - "Program 11111111111111111111111111111111 success" - ]} + "value":{ + "accounts":null, + "err":null, + "logs":[ + "Program 11111111111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 success" + ], + "unitsConsumed":0 + } }, "id": 1, }); @@ -5698,7 +5732,7 @@ pub mod tests { assert_eq!( res, Some( - r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"accounts":null,"err":"BlockhashNotFound","logs":[]}},"id":1}"#.to_string(), + r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"accounts":null,"err":"BlockhashNotFound","logs":[],"unitsConsumed":0}},"id":1}"#.to_string(), ) ); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 439e96d4f1..cdfe878704 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -138,7 +138,6 @@ pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5; #[derive(Clone, Debug, Default, PartialEq)] pub struct RentDebits(pub Vec<(Pubkey, RewardInfo)>); - impl RentDebits { pub fn push(&mut self, account: &Pubkey, rent: u64, post_balance: u64) { if rent != 0 { @@ -168,7 +167,6 @@ pub struct ExecuteTimings { pub num_execute_batches: u64, pub details: ExecuteDetailsTimings, } - impl ExecuteTimings { pub fn accumulate(&mut self, other: &ExecuteTimings) { self.check_us += other.check_us; @@ -415,15 +413,15 @@ impl CachedExecutors { } } -pub struct TxComputeMeter { +pub struct TransactionComputeMeter { remaining: u64, } -impl TxComputeMeter { +impl TransactionComputeMeter { pub fn new(cap: u64) -> Self { Self { remaining: cap } } } -impl ComputeMeter for TxComputeMeter { +impl ComputeMeter for TransactionComputeMeter { fn consume(&mut self, amount: u64) -> std::result::Result<(), InstructionError> { let exceeded = self.remaining < amount; self.remaining = self.remaining.saturating_sub(amount); @@ -524,6 +522,12 @@ pub struct TransactionResults { pub overwritten_vote_accounts: Vec, pub rent_debits: Vec, } +pub struct TransactionSimulationResult { + pub result: Result<()>, + pub logs: TransactionLogMessages, + pub post_simulation_accounts: Vec<(Pubkey, AccountSharedData)>, + pub units_consumed: u64, +} pub struct TransactionBalancesSet { pub pre_balances: TransactionBalances, pub post_balances: TransactionBalances, @@ -2705,36 +2709,36 @@ impl Bank { pub(crate) fn prepare_simulation_batch<'a, 'b>( &'a self, - tx: SanitizedTransaction<'b>, + transaction: SanitizedTransaction<'b>, ) -> TransactionBatch<'a, 'b> { - let mut batch = TransactionBatch::new(vec![Ok(())], self, Cow::Owned(vec![tx])); + let mut batch = TransactionBatch::new(vec![Ok(())], self, Cow::Owned(vec![transaction])); batch.needs_unlock = false; batch } /// Run transactions against a frozen bank without committing the results - pub fn simulate_transaction( - &self, - transaction: &Transaction, - ) -> ( - Result<()>, - TransactionLogMessages, - Vec<(Pubkey, AccountSharedData)>, - ) { + pub fn simulate_transaction(&self, transaction: &Transaction) -> TransactionSimulationResult { assert!(self.is_frozen(), "simulation bank must be frozen"); let batch = match SanitizedTransaction::try_from(transaction) { Ok(sanitized_tx) => self.prepare_simulation_batch(sanitized_tx), - Err(err) => return (Err(err), vec![], vec![]), + Err(err) => { + return TransactionSimulationResult { + result: Err(err), + logs: vec![], + post_simulation_accounts: vec![], + units_consumed: 0, + } + } }; let mut timings = ExecuteTimings::default(); let ( - loaded_txs, + loaded_transactions, executed, _inner_instructions, - log_messages, + logs, _retryable_transactions, _transaction_count, _signature_count, @@ -2749,9 +2753,9 @@ impl Bank { &mut timings, ); - let transaction_result = executed[0].0.clone().map(|_| ()); - let log_messages = log_messages.get(0).cloned().flatten().unwrap_or_default(); - let post_transaction_accounts = loaded_txs + let result = executed[0].0.clone().map(|_| ()); + let logs = logs.get(0).cloned().flatten().unwrap_or_default(); + let post_simulation_accounts = loaded_transactions .into_iter() .next() .unwrap() @@ -2760,9 +2764,22 @@ impl Bank { .map(|loaded_transaction| loaded_transaction.accounts.into_iter().collect::>()) .unwrap_or_default(); + let units_consumed = timings + .details + .per_program_timings + .iter() + .fold(0, |acc, (_, program_timing)| { + acc + program_timing.accumulated_units + }); + debug!("simulate_transaction: {:?}", timings); - (transaction_result, log_messages, post_transaction_accounts) + TransactionSimulationResult { + result, + logs, + post_simulation_accounts, + units_consumed, + } } pub fn unlock_accounts(&self, batch: &mut TransactionBatch) { @@ -3245,7 +3262,7 @@ impl Bank { None }; - let compute_meter = Rc::new(RefCell::new(TxComputeMeter::new( + let compute_meter = Rc::new(RefCell::new(TransactionComputeMeter::new( bpf_compute_budget.max_units, )));