Count compute units even when transaction errors (#22059)

This commit is contained in:
carllin
2021-12-28 17:05:11 -05:00
committed by GitHub
parent f061059e45
commit eaa8c67bde
13 changed files with 295 additions and 97 deletions

View File

@ -13,6 +13,7 @@ use {
TransactionExecutionResult,
},
blockhash_queue::BlockhashQueue,
cost_model::ExecutionCost,
rent_collector::RentCollector,
system_instruction_processor::{get_system_account_kind, SystemAccountKind},
},
@ -114,6 +115,7 @@ pub struct LoadedTransaction {
pub program_indices: TransactionProgramIndices,
pub rent: TransactionRent,
pub rent_debits: RentDebits,
pub estimated_execution_cost: ExecutionCost,
}
pub type TransactionLoadResult = (Result<LoadedTransaction>, Option<NonceFull>);
@ -231,6 +233,7 @@ impl Accounts {
error_counters: &mut ErrorCounters,
rent_collector: &RentCollector,
feature_set: &FeatureSet,
estimated_execution_cost: ExecutionCost,
) -> Result<LoadedTransaction> {
// Copy all the accounts
let message = tx.message();
@ -374,6 +377,7 @@ impl Accounts {
program_indices,
rent: tx_rent,
rent_debits,
estimated_execution_cost,
})
} else {
error_counters.account_not_found += 1;
@ -467,7 +471,7 @@ impl Accounts {
txs.iter()
.zip(lock_results)
.map(|etx| match etx {
(tx, (Ok(()), nonce)) => {
(tx, (Ok(execution_cost), nonce)) => {
let lamports_per_signature = nonce
.as_ref()
.map(|nonce| nonce.lamports_per_signature())
@ -487,6 +491,7 @@ impl Accounts {
error_counters,
rent_collector,
feature_set,
execution_cost,
) {
Ok(loaded_transaction) => loaded_transaction,
Err(e) => return (Err(e), None),
@ -978,11 +983,14 @@ impl Accounts {
pub fn lock_accounts<'a>(
&self,
txs: impl Iterator<Item = &'a SanitizedTransaction>,
) -> Vec<Result<()>> {
) -> Vec<Result<ExecutionCost>> {
let keys: Vec<_> = txs.map(|tx| tx.get_account_locks()).collect();
let account_locks = &mut self.account_locks.lock().unwrap();
keys.into_iter()
.map(|keys| self.lock_account(account_locks, keys.writable, keys.readonly))
.map(|keys| {
self.lock_account(account_locks, keys.writable, keys.readonly)
.map(|_| 0)
})
.collect()
}
@ -991,12 +999,12 @@ impl Accounts {
pub fn lock_accounts_with_results<'a>(
&self,
txs: impl Iterator<Item = &'a SanitizedTransaction>,
results: impl Iterator<Item = Result<()>>,
) -> Vec<Result<()>> {
results: impl Iterator<Item = Result<ExecutionCost>>,
) -> Vec<Result<ExecutionCost>> {
let key_results: Vec<_> = txs
.zip(results)
.map(|(tx, result)| match result {
Ok(()) => Ok(tx.get_account_locks()),
Ok(execution_cost) => Ok((tx.get_account_locks(), execution_cost)),
Err(e) => Err(e),
})
.collect();
@ -1004,7 +1012,9 @@ impl Accounts {
key_results
.into_iter()
.map(|key_result| match key_result {
Ok(keys) => self.lock_account(account_locks, keys.writable, keys.readonly),
Ok((keys, execution_cost)) => self
.lock_account(account_locks, keys.writable, keys.readonly)
.map(|_| execution_cost),
Err(e) => Err(e),
})
.collect()
@ -1015,7 +1025,7 @@ impl Accounts {
pub fn unlock_accounts<'a>(
&self,
txs: impl Iterator<Item = &'a SanitizedTransaction>,
results: &[Result<()>],
results: &[Result<ExecutionCost>],
) {
let keys: Vec<_> = txs
.zip(results)
@ -1300,7 +1310,7 @@ mod tests {
accounts.load_accounts(
&ancestors,
&[sanitized_tx],
vec![(Ok(()), None)],
vec![(Ok(0), None)],
&hash_queue,
error_counters,
rent_collector,
@ -2448,9 +2458,9 @@ mod tests {
let txs = vec![tx0, tx1, tx2];
let qos_results = vec![
Ok(()),
Ok(0),
Err(TransactionError::WouldExceedMaxBlockCostLimit),
Ok(()),
Ok(0),
];
let results = accounts.lock_accounts_with_results(txs.iter(), qos_results.into_iter());
@ -2543,6 +2553,7 @@ mod tests {
program_indices: vec![],
rent: 0,
rent_debits: RentDebits::default(),
estimated_execution_cost: 0,
}),
None,
);
@ -2553,6 +2564,7 @@ mod tests {
program_indices: vec![],
rent: 0,
rent_debits: RentDebits::default(),
estimated_execution_cost: 0,
}),
None,
);
@ -2646,7 +2658,7 @@ mod tests {
accounts.load_accounts(
&ancestors,
&[tx],
vec![(Ok(()), None)],
vec![(Ok(0), None)],
&hash_queue,
&mut error_counters,
&rent_collector,
@ -2982,6 +2994,7 @@ mod tests {
program_indices: vec![],
rent: 0,
rent_debits: RentDebits::default(),
estimated_execution_cost: 0,
}),
nonce.clone(),
);
@ -3092,6 +3105,7 @@ mod tests {
program_indices: vec![],
rent: 0,
rent_debits: RentDebits::default(),
estimated_execution_cost: 0,
}),
nonce.clone(),
);

View File

@ -47,6 +47,7 @@ use {
ancestors::{Ancestors, AncestorsForSerialization},
blockhash_queue::BlockhashQueue,
builtins::{self, ActivationType, Builtin, Builtins},
cost_model::ExecutionCost,
cost_tracker::CostTracker,
epoch_stakes::{EpochStakes, NodeVoteAccounts},
inline_spl_token,
@ -503,7 +504,7 @@ impl StatusCacheRc {
}
}
pub type TransactionCheckResult = (Result<()>, Option<NoncePartial>);
pub type TransactionCheckResult<'a> = (Result<ExecutionCost>, Option<NoncePartial>);
pub type TransactionExecutionResult = (Result<()>, Option<NonceFull>);
pub struct TransactionResults {
pub fee_collection_results: Vec<Result<()>>,
@ -3107,7 +3108,7 @@ impl Bank {
pub fn prepare_sanitized_batch_with_results<'a, 'b>(
&'a self,
transactions: &'b [SanitizedTransaction],
transaction_results: impl Iterator<Item = Result<()>>,
transaction_results: impl Iterator<Item = Result<ExecutionCost>>,
) -> TransactionBatch<'a, 'b> {
// this lock_results could be: Ok, AccountInUse, WouldExceedBlockMaxLimit or WouldExceedAccountMaxLimit
let lock_results = self
@ -3122,7 +3123,7 @@ impl Bank {
&'a self,
transaction: SanitizedTransaction,
) -> TransactionBatch<'a, '_> {
let mut batch = TransactionBatch::new(vec![Ok(())], self, Cow::Owned(vec![transaction]));
let mut batch = TransactionBatch::new(vec![Ok(0)], self, Cow::Owned(vec![transaction]));
batch.needs_unlock = false;
batch
}
@ -3218,23 +3219,29 @@ impl Bank {
self.rc.accounts.accounts_db.set_shrink_paths(paths);
}
fn check_age<'a>(
fn check_age<'a, T>(
&self,
txs: impl Iterator<Item = &'a SanitizedTransaction>,
lock_results: &[Result<()>],
lock_results: impl Iterator<Item = T>,
max_age: usize,
error_counters: &mut ErrorCounters,
) -> Vec<TransactionCheckResult> {
) -> Vec<TransactionCheckResult>
where
T: std::borrow::Borrow<Result<ExecutionCost>>,
{
let hash_queue = self.blockhash_queue.read().unwrap();
txs.zip(lock_results)
.map(|(tx, lock_res)| match lock_res {
Ok(()) => {
.map(|(tx, lock_res)| match lock_res.borrow() {
Ok(execution_cost) => {
let recent_blockhash = tx.message().recent_blockhash();
let hash_age = hash_queue.check_hash_age(recent_blockhash, max_age);
if hash_age == Some(true) {
(Ok(()), None)
(Ok(*execution_cost), None)
} else if let Some((address, account)) = self.check_transaction_for_nonce(tx) {
(Ok(()), Some(NoncePartial::new(address, account)))
(
Ok(*execution_cost),
Some(NoncePartial::new(address, account)),
)
} else if hash_age == Some(false) {
error_counters.blockhash_too_old += 1;
(Err(TransactionError::BlockhashNotFound), None)
@ -3304,13 +3311,16 @@ impl Bank {
})
}
pub fn check_transactions(
pub fn check_transactions<T>(
&self,
sanitized_txs: &[SanitizedTransaction],
lock_results: &[Result<()>],
lock_results: impl Iterator<Item = T>,
max_age: usize,
error_counters: &mut ErrorCounters,
) -> Vec<TransactionCheckResult> {
) -> Vec<TransactionCheckResult>
where
T: std::borrow::Borrow<Result<ExecutionCost>>,
{
let age_results =
self.check_age(sanitized_txs.iter(), lock_results, max_age, error_counters);
self.check_status_cache(sanitized_txs, age_results, error_counters)
@ -3506,7 +3516,7 @@ impl Bank {
let mut check_time = Measure::start("check_transactions");
let check_results = self.check_transactions(
sanitized_txs,
batch.lock_results(),
batch.lock_results().iter(),
max_age,
&mut error_counters,
);
@ -3588,6 +3598,7 @@ impl Bank {
&self.builtin_programs.vec,
legacy_message,
&loaded_transaction.program_indices,
loaded_transaction.estimated_execution_cost,
&transaction_context,
self.rent_collector.rent,
log_collector.clone(),

View File

@ -18,6 +18,7 @@ use {
};
const MAX_WRITABLE_ACCOUNTS: usize = 256;
pub type ExecutionCost = u64;
// costs are stored in number of 'compute unit's
#[derive(Debug)]
@ -26,7 +27,7 @@ pub struct TransactionCost {
pub signature_cost: u64,
pub write_lock_cost: u64,
pub data_bytes_cost: u64,
pub execution_cost: u64,
pub execution_cost: ExecutionCost,
// `cost_weight` is a multiplier could be applied to transaction cost,
// if set to zero allows the transaction to bypass cost limit check.
pub cost_weight: u32,

View File

@ -1,9 +1,10 @@
use {
crate::cost_model::ExecutionCost,
serde::{Deserialize, Serialize},
solana_measure::measure::Measure,
solana_program_runtime::{
instruction_recorder::InstructionRecorder,
invoke_context::{BuiltinProgram, Executors, InvokeContext},
invoke_context::{BuiltinProgram, Executors, InvokeContext, ProcessInstructionResult},
log_collector::LogCollector,
timings::ExecuteDetailsTimings,
},
@ -53,6 +54,7 @@ impl MessageProcessor {
builtin_programs: &[BuiltinProgram],
message: &Message,
program_indices: &[Vec<usize>],
estimated_execution_cost: ExecutionCost,
transaction_context: &TransactionContext,
rent: Rent,
log_collector: Option<Rc<RefCell<LogCollector>>>,
@ -127,21 +129,26 @@ impl MessageProcessor {
})
.collect::<Vec<_>>();
let mut time = Measure::start("execute_instruction");
let compute_meter_consumption = invoke_context
.process_instruction(
&instruction.data,
&instruction_accounts,
None,
program_indices,
)
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
let ProcessInstructionResult {
compute_units_consumed,
result,
} = invoke_context.process_instruction(
&instruction.data,
&instruction_accounts,
None,
program_indices,
);
time.stop();
timings.accumulate_program(
instruction.program_id(&message.account_keys),
time.as_us(),
compute_meter_consumption,
compute_units_consumed,
estimated_execution_cost,
result.is_err(),
);
timings.accumulate(&invoke_context.timings);
result
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
}
Ok(ProcessedMessageInfo {
accounts_data_len: invoke_context.get_accounts_data_meter().current(),
@ -258,6 +265,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
0,
&transaction_context,
rent_collector.rent,
None,
@ -299,6 +307,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
0,
&transaction_context,
rent_collector.rent,
None,
@ -332,6 +341,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
0,
&transaction_context,
rent_collector.rent,
None,
@ -476,6 +486,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
0,
&transaction_context,
rent_collector.rent,
None,
@ -510,6 +521,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
0,
&transaction_context,
rent_collector.rent,
None,
@ -541,6 +553,7 @@ mod tests {
builtin_programs,
&message,
&program_indices,
0,
&transaction_context,
rent_collector.rent,
None,
@ -614,6 +627,7 @@ mod tests {
builtin_programs,
&message,
&[vec![0], vec![1]],
0,
&transaction_context,
RentCollector::default().rent,
None,

View File

@ -1,12 +1,12 @@
use {
crate::bank::Bank,
crate::{bank::Bank, cost_model::ExecutionCost},
solana_sdk::transaction::{Result, SanitizedTransaction},
std::borrow::Cow,
};
// Represents the results of trying to lock a set of accounts
pub struct TransactionBatch<'a, 'b> {
lock_results: Vec<Result<()>>,
lock_results: Vec<Result<ExecutionCost>>,
bank: &'a Bank,
sanitized_txs: Cow<'b, [SanitizedTransaction]>,
pub(crate) needs_unlock: bool,
@ -14,7 +14,7 @@ pub struct TransactionBatch<'a, 'b> {
impl<'a, 'b> TransactionBatch<'a, 'b> {
pub fn new(
lock_results: Vec<Result<()>>,
lock_results: Vec<Result<ExecutionCost>>,
bank: &'a Bank,
sanitized_txs: Cow<'b, [SanitizedTransaction]>,
) -> Self {
@ -27,7 +27,7 @@ impl<'a, 'b> TransactionBatch<'a, 'b> {
}
}
pub fn lock_results(&self) -> &Vec<Result<()>> {
pub fn lock_results(&self) -> &Vec<Result<ExecutionCost>> {
&self.lock_results
}