Transaction simulation includes cost (#18715)

This commit is contained in:
Jack May 2021-07-16 14:58:15 -07:00 committed by GitHub
parent ae5ad5cf9b
commit 74539020b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 41 deletions

View File

@ -328,6 +328,7 @@ pub struct RpcSimulateTransactionResult {
pub err: Option<TransactionError>, pub err: Option<TransactionError>,
pub logs: Option<Vec<String>>, pub logs: Option<Vec<String>>,
pub accounts: Option<Vec<Option<UiAccount>>>, pub accounts: Option<Vec<Option<UiAccount>>>,
pub units_consumed: Option<u64>,
} }
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]

View File

@ -43,7 +43,7 @@ use {
solana_runtime::{ solana_runtime::{
accounts::AccountAddressFilter, accounts::AccountAddressFilter,
accounts_index::{AccountIndex, AccountSecondaryIndexes, IndexKey}, accounts_index::{AccountIndex, AccountSecondaryIndexes, IndexKey},
bank::Bank, bank::{Bank, TransactionSimulationResult},
bank_forks::BankForks, bank_forks::BankForks,
commitment::{BlockCommitmentArray, BlockCommitmentCache, CommitmentSlots}, commitment::{BlockCommitmentArray, BlockCommitmentCache, CommitmentSlots},
inline_spl_token_v2_0::{SPL_TOKEN_ACCOUNT_MINT_OFFSET, SPL_TOKEN_ACCOUNT_OWNER_OFFSET}, 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 { match err {
TransactionError::BlockhashNotFound => { TransactionError::BlockhashNotFound => {
inc_new_counter_info!("rpc-send-tx_err-blockhash-not-found", 1); inc_new_counter_info!("rpc-send-tx_err-blockhash-not-found", 1);
@ -3044,6 +3050,7 @@ pub mod rpc_full {
err: Some(err), err: Some(err),
logs: Some(logs), logs: Some(logs),
accounts: None, accounts: None,
units_consumed: Some(units_consumed),
}, },
} }
.into()); .into());
@ -3087,7 +3094,12 @@ pub mod rpc_full {
if config.replace_recent_blockhash { if config.replace_recent_blockhash {
transaction.message.recent_blockhash = bank.last_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 = if let Some(config_accounts) = config.accounts {
let accounts_encoding = config_accounts let accounts_encoding = config_accounts
@ -3142,6 +3154,7 @@ pub mod rpc_full {
err: result.err(), err: result.err(),
logs: Some(logs), logs: Some(logs),
accounts, accounts,
units_consumed: Some(units_consumed),
}, },
)) ))
} }
@ -5183,7 +5196,8 @@ pub mod tests {
"logs":[ "logs":[
"Program 11111111111111111111111111111111 invoke [1]", "Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success" "Program 11111111111111111111111111111111 success"
] ],
"unitsConsumed":0
} }
}, },
"id": 1, "id": 1,
@ -5262,10 +5276,15 @@ pub mod tests {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context":{"slot":0},
"value":{"accounts": null, "err":null, "logs":[ "value":{
"Program 11111111111111111111111111111111 invoke [1]", "accounts":null,
"Program 11111111111111111111111111111111 success" "err":null,
]} "logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"unitsConsumed":0
}
}, },
"id": 1, "id": 1,
}); });
@ -5285,10 +5304,15 @@ pub mod tests {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context":{"slot":0},
"value":{"accounts": null, "err":null, "logs":[ "value":{
"Program 11111111111111111111111111111111 invoke [1]", "accounts":null,
"Program 11111111111111111111111111111111 success" "err":null,
]} "logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"unitsConsumed":0
}
}, },
"id": 1, "id": 1,
}); });
@ -5333,7 +5357,12 @@ pub mod tests {
"jsonrpc":"2.0", "jsonrpc":"2.0",
"result": { "result": {
"context":{"slot":0}, "context":{"slot":0},
"value":{"err": "BlockhashNotFound", "accounts": null, "logs":[]} "value":{
"err":"BlockhashNotFound",
"accounts":null,
"logs":[],
"unitsConsumed":0
}
}, },
"id":1 "id":1
}); });
@ -5354,10 +5383,15 @@ pub mod tests {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context":{"slot":0},
"value":{"accounts": null, "err":null, "logs":[ "value":{
"Program 11111111111111111111111111111111 invoke [1]", "accounts":null,
"Program 11111111111111111111111111111111 success" "err":null,
]} "logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"unitsConsumed":0
}
}, },
"id": 1, "id": 1,
}); });
@ -5698,7 +5732,7 @@ pub mod tests {
assert_eq!( assert_eq!(
res, res,
Some( 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(),
) )
); );

View File

@ -138,7 +138,6 @@ pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5;
#[derive(Clone, Debug, Default, PartialEq)] #[derive(Clone, Debug, Default, PartialEq)]
pub struct RentDebits(pub Vec<(Pubkey, RewardInfo)>); pub struct RentDebits(pub Vec<(Pubkey, RewardInfo)>);
impl RentDebits { impl RentDebits {
pub fn push(&mut self, account: &Pubkey, rent: u64, post_balance: u64) { pub fn push(&mut self, account: &Pubkey, rent: u64, post_balance: u64) {
if rent != 0 { if rent != 0 {
@ -168,7 +167,6 @@ pub struct ExecuteTimings {
pub num_execute_batches: u64, pub num_execute_batches: u64,
pub details: ExecuteDetailsTimings, pub details: ExecuteDetailsTimings,
} }
impl ExecuteTimings { impl ExecuteTimings {
pub fn accumulate(&mut self, other: &ExecuteTimings) { pub fn accumulate(&mut self, other: &ExecuteTimings) {
self.check_us += other.check_us; self.check_us += other.check_us;
@ -415,15 +413,15 @@ impl CachedExecutors {
} }
} }
pub struct TxComputeMeter { pub struct TransactionComputeMeter {
remaining: u64, remaining: u64,
} }
impl TxComputeMeter { impl TransactionComputeMeter {
pub fn new(cap: u64) -> Self { pub fn new(cap: u64) -> Self {
Self { remaining: cap } Self { remaining: cap }
} }
} }
impl ComputeMeter for TxComputeMeter { impl ComputeMeter for TransactionComputeMeter {
fn consume(&mut self, amount: u64) -> std::result::Result<(), InstructionError> { fn consume(&mut self, amount: u64) -> std::result::Result<(), InstructionError> {
let exceeded = self.remaining < amount; let exceeded = self.remaining < amount;
self.remaining = self.remaining.saturating_sub(amount); self.remaining = self.remaining.saturating_sub(amount);
@ -524,6 +522,12 @@ pub struct TransactionResults {
pub overwritten_vote_accounts: Vec<OverwrittenVoteAccount>, pub overwritten_vote_accounts: Vec<OverwrittenVoteAccount>,
pub rent_debits: Vec<RentDebits>, pub rent_debits: Vec<RentDebits>,
} }
pub struct TransactionSimulationResult {
pub result: Result<()>,
pub logs: TransactionLogMessages,
pub post_simulation_accounts: Vec<(Pubkey, AccountSharedData)>,
pub units_consumed: u64,
}
pub struct TransactionBalancesSet { pub struct TransactionBalancesSet {
pub pre_balances: TransactionBalances, pub pre_balances: TransactionBalances,
pub post_balances: TransactionBalances, pub post_balances: TransactionBalances,
@ -2705,36 +2709,36 @@ impl Bank {
pub(crate) fn prepare_simulation_batch<'a, 'b>( pub(crate) fn prepare_simulation_batch<'a, 'b>(
&'a self, &'a self,
tx: SanitizedTransaction<'b>, transaction: SanitizedTransaction<'b>,
) -> TransactionBatch<'a, '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.needs_unlock = false;
batch batch
} }
/// Run transactions against a frozen bank without committing the results /// Run transactions against a frozen bank without committing the results
pub fn simulate_transaction( pub fn simulate_transaction(&self, transaction: &Transaction) -> TransactionSimulationResult {
&self,
transaction: &Transaction,
) -> (
Result<()>,
TransactionLogMessages,
Vec<(Pubkey, AccountSharedData)>,
) {
assert!(self.is_frozen(), "simulation bank must be frozen"); assert!(self.is_frozen(), "simulation bank must be frozen");
let batch = match SanitizedTransaction::try_from(transaction) { let batch = match SanitizedTransaction::try_from(transaction) {
Ok(sanitized_tx) => self.prepare_simulation_batch(sanitized_tx), 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 mut timings = ExecuteTimings::default();
let ( let (
loaded_txs, loaded_transactions,
executed, executed,
_inner_instructions, _inner_instructions,
log_messages, logs,
_retryable_transactions, _retryable_transactions,
_transaction_count, _transaction_count,
_signature_count, _signature_count,
@ -2749,9 +2753,9 @@ impl Bank {
&mut timings, &mut timings,
); );
let transaction_result = executed[0].0.clone().map(|_| ()); let result = executed[0].0.clone().map(|_| ());
let log_messages = log_messages.get(0).cloned().flatten().unwrap_or_default(); let logs = logs.get(0).cloned().flatten().unwrap_or_default();
let post_transaction_accounts = loaded_txs let post_simulation_accounts = loaded_transactions
.into_iter() .into_iter()
.next() .next()
.unwrap() .unwrap()
@ -2760,9 +2764,22 @@ impl Bank {
.map(|loaded_transaction| loaded_transaction.accounts.into_iter().collect::<Vec<_>>()) .map(|loaded_transaction| loaded_transaction.accounts.into_iter().collect::<Vec<_>>())
.unwrap_or_default(); .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); 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) { pub fn unlock_accounts(&self, batch: &mut TransactionBatch) {
@ -3245,7 +3262,7 @@ impl Bank {
None None
}; };
let compute_meter = Rc::new(RefCell::new(TxComputeMeter::new( let compute_meter = Rc::new(RefCell::new(TransactionComputeMeter::new(
bpf_compute_budget.max_units, bpf_compute_budget.max_units,
))); )));