Separate vote cost (#33230)
* Separate simple-vote transaction cost from non-vote transaction cost * remove is_simple_vote flag from transaction UsageCostDetails * update test and comment * set static usage cost for SimpleVote transaction
This commit is contained in:
parent
40f536010f
commit
a41c15e47e
|
@ -737,7 +737,7 @@ mod tests {
|
||||||
unprocessed_transaction_storage::ThreadType,
|
unprocessed_transaction_storage::ThreadType,
|
||||||
},
|
},
|
||||||
crossbeam_channel::{unbounded, Receiver},
|
crossbeam_channel::{unbounded, Receiver},
|
||||||
solana_cost_model::cost_model::CostModel,
|
solana_cost_model::{cost_model::CostModel, transaction_cost::TransactionCost},
|
||||||
solana_entry::entry::{next_entry, next_versioned_entry},
|
solana_entry::entry::{next_entry, next_versioned_entry},
|
||||||
solana_ledger::{
|
solana_ledger::{
|
||||||
blockstore::{entries_to_test_shreds, Blockstore},
|
blockstore::{entries_to_test_shreds, Blockstore},
|
||||||
|
@ -1264,7 +1264,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut cost = CostModel::calculate_cost(&transactions[0], &bank.feature_set);
|
let mut cost = CostModel::calculate_cost(&transactions[0], &bank.feature_set);
|
||||||
cost.bpf_execution_cost = actual_bpf_execution_cost;
|
if let TransactionCost::Transaction(ref mut usage_cost) = cost {
|
||||||
|
usage_cost.bpf_execution_cost = actual_bpf_execution_cost;
|
||||||
|
}
|
||||||
|
|
||||||
block_cost + cost.sum()
|
block_cost + cost.sum()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -318,25 +318,25 @@ impl QosService {
|
||||||
Ok(cost) => {
|
Ok(cost) => {
|
||||||
saturating_add_assign!(
|
saturating_add_assign!(
|
||||||
batched_transaction_details.costs.batched_signature_cost,
|
batched_transaction_details.costs.batched_signature_cost,
|
||||||
cost.signature_cost
|
cost.signature_cost()
|
||||||
);
|
);
|
||||||
saturating_add_assign!(
|
saturating_add_assign!(
|
||||||
batched_transaction_details.costs.batched_write_lock_cost,
|
batched_transaction_details.costs.batched_write_lock_cost,
|
||||||
cost.write_lock_cost
|
cost.write_lock_cost()
|
||||||
);
|
);
|
||||||
saturating_add_assign!(
|
saturating_add_assign!(
|
||||||
batched_transaction_details.costs.batched_data_bytes_cost,
|
batched_transaction_details.costs.batched_data_bytes_cost,
|
||||||
cost.data_bytes_cost
|
cost.data_bytes_cost()
|
||||||
);
|
);
|
||||||
saturating_add_assign!(
|
saturating_add_assign!(
|
||||||
batched_transaction_details
|
batched_transaction_details
|
||||||
.costs
|
.costs
|
||||||
.batched_builtins_execute_cost,
|
.batched_builtins_execute_cost,
|
||||||
cost.builtins_execution_cost
|
cost.builtins_execution_cost()
|
||||||
);
|
);
|
||||||
saturating_add_assign!(
|
saturating_add_assign!(
|
||||||
batched_transaction_details.costs.batched_bpf_execute_cost,
|
batched_transaction_details.costs.batched_bpf_execute_cost,
|
||||||
cost.bpf_execution_cost
|
cost.bpf_execution_cost()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(transaction_error) => match transaction_error {
|
Err(transaction_error) => match transaction_error {
|
||||||
|
@ -589,6 +589,7 @@ mod tests {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
itertools::Itertools,
|
itertools::Itertools,
|
||||||
|
solana_cost_model::transaction_cost::UsageCostDetails,
|
||||||
solana_runtime::genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
solana_runtime::genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
@ -734,7 +735,7 @@ mod tests {
|
||||||
let commited_status: Vec<CommitTransactionDetails> = qos_cost_results
|
let commited_status: Vec<CommitTransactionDetails> = qos_cost_results
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tx_cost| CommitTransactionDetails::Committed {
|
.map(|tx_cost| CommitTransactionDetails::Committed {
|
||||||
compute_units: tx_cost.as_ref().unwrap().bpf_execution_cost
|
compute_units: tx_cost.as_ref().unwrap().bpf_execution_cost()
|
||||||
+ execute_units_adjustment,
|
+ execute_units_adjustment,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -861,7 +862,7 @@ mod tests {
|
||||||
CommitTransactionDetails::NotCommitted
|
CommitTransactionDetails::NotCommitted
|
||||||
} else {
|
} else {
|
||||||
CommitTransactionDetails::Committed {
|
CommitTransactionDetails::Committed {
|
||||||
compute_units: tx_cost.as_ref().unwrap().bpf_execution_cost
|
compute_units: tx_cost.as_ref().unwrap().bpf_execution_cost()
|
||||||
+ execute_units_adjustment,
|
+ execute_units_adjustment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -904,14 +905,14 @@ mod tests {
|
||||||
let tx_cost_results: Vec<_> = (0..num_txs)
|
let tx_cost_results: Vec<_> = (0..num_txs)
|
||||||
.map(|n| {
|
.map(|n| {
|
||||||
if n % 2 == 0 {
|
if n % 2 == 0 {
|
||||||
Ok(TransactionCost {
|
Ok(TransactionCost::Transaction(UsageCostDetails {
|
||||||
signature_cost,
|
signature_cost,
|
||||||
write_lock_cost,
|
write_lock_cost,
|
||||||
data_bytes_cost,
|
data_bytes_cost,
|
||||||
builtins_execution_cost,
|
builtins_execution_cost,
|
||||||
bpf_execution_cost,
|
bpf_execution_cost,
|
||||||
..TransactionCost::default()
|
..UsageCostDetails::default()
|
||||||
})
|
}))
|
||||||
} else {
|
} else {
|
||||||
Err(TransactionError::WouldExceedMaxBlockCostLimit)
|
Err(TransactionError::WouldExceedMaxBlockCostLimit)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{block_cost_limits::*, transaction_cost::TransactionCost},
|
crate::{block_cost_limits::*, transaction_cost::*},
|
||||||
log::*,
|
log::*,
|
||||||
solana_program_runtime::compute_budget::{
|
solana_program_runtime::compute_budget::{
|
||||||
ComputeBudget, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_COMPUTE_UNIT_LIMIT,
|
ComputeBudget, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_COMPUTE_UNIT_LIMIT,
|
||||||
|
@ -36,16 +36,21 @@ impl CostModel {
|
||||||
transaction: &SanitizedTransaction,
|
transaction: &SanitizedTransaction,
|
||||||
feature_set: &FeatureSet,
|
feature_set: &FeatureSet,
|
||||||
) -> TransactionCost {
|
) -> TransactionCost {
|
||||||
let mut tx_cost = TransactionCost::new_with_default_capacity();
|
if transaction.is_simple_vote_transaction() {
|
||||||
|
TransactionCost::SimpleVote {
|
||||||
|
writable_accounts: Self::get_writable_accounts(transaction),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut tx_cost = UsageCostDetails::new_with_default_capacity();
|
||||||
|
|
||||||
tx_cost.signature_cost = Self::get_signature_cost(transaction);
|
tx_cost.signature_cost = Self::get_signature_cost(transaction);
|
||||||
Self::get_write_lock_cost(&mut tx_cost, transaction);
|
Self::get_write_lock_cost(&mut tx_cost, transaction);
|
||||||
Self::get_transaction_cost(&mut tx_cost, transaction, feature_set);
|
Self::get_transaction_cost(&mut tx_cost, transaction, feature_set);
|
||||||
tx_cost.account_data_size = Self::calculate_account_data_size(transaction);
|
tx_cost.account_data_size = Self::calculate_account_data_size(transaction);
|
||||||
tx_cost.is_simple_vote = transaction.is_simple_vote_transaction();
|
|
||||||
|
|
||||||
debug!("transaction {:?} has cost {:?}", transaction, tx_cost);
|
debug!("transaction {:?} has cost {:?}", transaction, tx_cost);
|
||||||
tx_cost
|
TransactionCost::Transaction(tx_cost)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate cost of loaded accounts size in the same way heap cost is charged at
|
// Calculate cost of loaded accounts size in the same way heap cost is charged at
|
||||||
|
@ -68,24 +73,30 @@ impl CostModel {
|
||||||
transaction.signatures().len() as u64 * SIGNATURE_COST
|
transaction.signatures().len() as u64 * SIGNATURE_COST
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_write_lock_cost(tx_cost: &mut TransactionCost, transaction: &SanitizedTransaction) {
|
fn get_writable_accounts(transaction: &SanitizedTransaction) -> Vec<Pubkey> {
|
||||||
let message = transaction.message();
|
let message = transaction.message();
|
||||||
message
|
message
|
||||||
.account_keys()
|
.account_keys()
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(i, k)| {
|
.filter_map(|(i, k)| {
|
||||||
let is_writable = message.is_writable(i);
|
if message.is_writable(i) {
|
||||||
|
Some(*k)
|
||||||
if is_writable {
|
} else {
|
||||||
tx_cost.writable_accounts.push(*k);
|
None
|
||||||
tx_cost.write_lock_cost += WRITE_LOCK_UNITS;
|
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_write_lock_cost(tx_cost: &mut UsageCostDetails, transaction: &SanitizedTransaction) {
|
||||||
|
tx_cost.writable_accounts = Self::get_writable_accounts(transaction);
|
||||||
|
tx_cost.write_lock_cost =
|
||||||
|
WRITE_LOCK_UNITS.saturating_mul(tx_cost.writable_accounts.len() as u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_transaction_cost(
|
fn get_transaction_cost(
|
||||||
tx_cost: &mut TransactionCost,
|
tx_cost: &mut UsageCostDetails,
|
||||||
transaction: &SanitizedTransaction,
|
transaction: &SanitizedTransaction,
|
||||||
feature_set: &FeatureSet,
|
feature_set: &FeatureSet,
|
||||||
) {
|
) {
|
||||||
|
@ -298,7 +309,7 @@ mod tests {
|
||||||
.get(&system_program::id())
|
.get(&system_program::id())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut tx_cost = TransactionCost::default();
|
let mut tx_cost = UsageCostDetails::default();
|
||||||
CostModel::get_transaction_cost(
|
CostModel::get_transaction_cost(
|
||||||
&mut tx_cost,
|
&mut tx_cost,
|
||||||
&simple_transaction,
|
&simple_transaction,
|
||||||
|
@ -327,7 +338,7 @@ mod tests {
|
||||||
let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx);
|
let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx);
|
||||||
debug!("token_transaction {:?}", token_transaction);
|
debug!("token_transaction {:?}", token_transaction);
|
||||||
|
|
||||||
let mut tx_cost = TransactionCost::default();
|
let mut tx_cost = UsageCostDetails::default();
|
||||||
CostModel::get_transaction_cost(
|
CostModel::get_transaction_cost(
|
||||||
&mut tx_cost,
|
&mut tx_cost,
|
||||||
&token_transaction,
|
&token_transaction,
|
||||||
|
@ -364,7 +375,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx);
|
let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx);
|
||||||
|
|
||||||
let mut tx_cost = TransactionCost::default();
|
let mut tx_cost = UsageCostDetails::default();
|
||||||
CostModel::get_transaction_cost(
|
CostModel::get_transaction_cost(
|
||||||
&mut tx_cost,
|
&mut tx_cost,
|
||||||
&token_transaction,
|
&token_transaction,
|
||||||
|
@ -414,7 +425,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx);
|
let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx);
|
||||||
|
|
||||||
let mut tx_cost = TransactionCost::default();
|
let mut tx_cost = UsageCostDetails::default();
|
||||||
CostModel::get_transaction_cost(
|
CostModel::get_transaction_cost(
|
||||||
&mut tx_cost,
|
&mut tx_cost,
|
||||||
&token_transaction,
|
&token_transaction,
|
||||||
|
@ -446,7 +457,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let expected_cost = program_cost * 2;
|
let expected_cost = program_cost * 2;
|
||||||
|
|
||||||
let mut tx_cost = TransactionCost::default();
|
let mut tx_cost = UsageCostDetails::default();
|
||||||
CostModel::get_transaction_cost(&mut tx_cost, &tx, &FeatureSet::all_enabled());
|
CostModel::get_transaction_cost(&mut tx_cost, &tx, &FeatureSet::all_enabled());
|
||||||
assert_eq!(expected_cost, tx_cost.builtins_execution_cost);
|
assert_eq!(expected_cost, tx_cost.builtins_execution_cost);
|
||||||
assert_eq!(0, tx_cost.bpf_execution_cost);
|
assert_eq!(0, tx_cost.bpf_execution_cost);
|
||||||
|
@ -478,7 +489,7 @@ mod tests {
|
||||||
debug!("many random transaction {:?}", tx);
|
debug!("many random transaction {:?}", tx);
|
||||||
|
|
||||||
let expected_cost = DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64 * 2;
|
let expected_cost = DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64 * 2;
|
||||||
let mut tx_cost = TransactionCost::default();
|
let mut tx_cost = UsageCostDetails::default();
|
||||||
CostModel::get_transaction_cost(&mut tx_cost, &tx, &FeatureSet::all_enabled());
|
CostModel::get_transaction_cost(&mut tx_cost, &tx, &FeatureSet::all_enabled());
|
||||||
assert_eq!(0, tx_cost.builtins_execution_cost);
|
assert_eq!(0, tx_cost.builtins_execution_cost);
|
||||||
assert_eq!(expected_cost, tx_cost.bpf_execution_cost);
|
assert_eq!(expected_cost, tx_cost.bpf_execution_cost);
|
||||||
|
@ -509,11 +520,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let tx_cost = CostModel::calculate_cost(&tx, &FeatureSet::all_enabled());
|
let tx_cost = CostModel::calculate_cost(&tx, &FeatureSet::all_enabled());
|
||||||
assert_eq!(2 + 2, tx_cost.writable_accounts.len());
|
assert_eq!(2 + 2, tx_cost.writable_accounts().len());
|
||||||
assert_eq!(signer1.pubkey(), tx_cost.writable_accounts[0]);
|
assert_eq!(signer1.pubkey(), tx_cost.writable_accounts()[0]);
|
||||||
assert_eq!(signer2.pubkey(), tx_cost.writable_accounts[1]);
|
assert_eq!(signer2.pubkey(), tx_cost.writable_accounts()[1]);
|
||||||
assert_eq!(key1, tx_cost.writable_accounts[2]);
|
assert_eq!(key1, tx_cost.writable_accounts()[2]);
|
||||||
assert_eq!(key2, tx_cost.writable_accounts[3]);
|
assert_eq!(key2, tx_cost.writable_accounts()[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -539,12 +550,12 @@ mod tests {
|
||||||
* DEFAULT_PAGE_COST;
|
* DEFAULT_PAGE_COST;
|
||||||
|
|
||||||
let tx_cost = CostModel::calculate_cost(&tx, &FeatureSet::all_enabled());
|
let tx_cost = CostModel::calculate_cost(&tx, &FeatureSet::all_enabled());
|
||||||
assert_eq!(expected_account_cost, tx_cost.write_lock_cost);
|
assert_eq!(expected_account_cost, tx_cost.write_lock_cost());
|
||||||
assert_eq!(*expected_execution_cost, tx_cost.builtins_execution_cost);
|
assert_eq!(*expected_execution_cost, tx_cost.builtins_execution_cost());
|
||||||
assert_eq!(2, tx_cost.writable_accounts.len());
|
assert_eq!(2, tx_cost.writable_accounts().len());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_loaded_accounts_data_size_cost,
|
expected_loaded_accounts_data_size_cost,
|
||||||
tx_cost.loaded_accounts_data_size_cost
|
tx_cost.loaded_accounts_data_size_cost()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,12 +579,12 @@ mod tests {
|
||||||
let expected_loaded_accounts_data_size_cost = 0;
|
let expected_loaded_accounts_data_size_cost = 0;
|
||||||
|
|
||||||
let tx_cost = CostModel::calculate_cost(&tx, &feature_set);
|
let tx_cost = CostModel::calculate_cost(&tx, &feature_set);
|
||||||
assert_eq!(expected_account_cost, tx_cost.write_lock_cost);
|
assert_eq!(expected_account_cost, tx_cost.write_lock_cost());
|
||||||
assert_eq!(*expected_execution_cost, tx_cost.builtins_execution_cost);
|
assert_eq!(*expected_execution_cost, tx_cost.builtins_execution_cost());
|
||||||
assert_eq!(2, tx_cost.writable_accounts.len());
|
assert_eq!(2, tx_cost.writable_accounts().len());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_loaded_accounts_data_size_cost,
|
expected_loaded_accounts_data_size_cost,
|
||||||
tx_cost.loaded_accounts_data_size_cost
|
tx_cost.loaded_accounts_data_size_cost()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,12 +618,12 @@ mod tests {
|
||||||
let expected_loaded_accounts_data_size_cost = (data_limit as u64) / (32 * 1024) * 8;
|
let expected_loaded_accounts_data_size_cost = (data_limit as u64) / (32 * 1024) * 8;
|
||||||
|
|
||||||
let tx_cost = CostModel::calculate_cost(&tx, &feature_set);
|
let tx_cost = CostModel::calculate_cost(&tx, &feature_set);
|
||||||
assert_eq!(expected_account_cost, tx_cost.write_lock_cost);
|
assert_eq!(expected_account_cost, tx_cost.write_lock_cost());
|
||||||
assert_eq!(expected_execution_cost, tx_cost.builtins_execution_cost);
|
assert_eq!(expected_execution_cost, tx_cost.builtins_execution_cost());
|
||||||
assert_eq!(2, tx_cost.writable_accounts.len());
|
assert_eq!(2, tx_cost.writable_accounts().len());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_loaded_accounts_data_size_cost,
|
expected_loaded_accounts_data_size_cost,
|
||||||
tx_cost.loaded_accounts_data_size_cost
|
tx_cost.loaded_accounts_data_size_cost()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,12 +651,12 @@ mod tests {
|
||||||
let expected_loaded_accounts_data_size_cost = 0;
|
let expected_loaded_accounts_data_size_cost = 0;
|
||||||
|
|
||||||
let tx_cost = CostModel::calculate_cost(&tx, &feature_set);
|
let tx_cost = CostModel::calculate_cost(&tx, &feature_set);
|
||||||
assert_eq!(expected_account_cost, tx_cost.write_lock_cost);
|
assert_eq!(expected_account_cost, tx_cost.write_lock_cost());
|
||||||
assert_eq!(expected_execution_cost, tx_cost.builtins_execution_cost);
|
assert_eq!(expected_execution_cost, tx_cost.builtins_execution_cost());
|
||||||
assert_eq!(2, tx_cost.writable_accounts.len());
|
assert_eq!(2, tx_cost.writable_accounts().len());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_loaded_accounts_data_size_cost,
|
expected_loaded_accounts_data_size_cost,
|
||||||
tx_cost.loaded_accounts_data_size_cost
|
tx_cost.loaded_accounts_data_size_cost()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,7 +716,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let expected_bpf_cost = DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT;
|
let expected_bpf_cost = DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT;
|
||||||
|
|
||||||
let mut tx_cost = TransactionCost::default();
|
let mut tx_cost = UsageCostDetails::default();
|
||||||
CostModel::get_transaction_cost(&mut tx_cost, &transaction, &FeatureSet::all_enabled());
|
CostModel::get_transaction_cost(&mut tx_cost, &transaction, &FeatureSet::all_enabled());
|
||||||
|
|
||||||
assert_eq!(expected_builtin_cost, tx_cost.builtins_execution_cost);
|
assert_eq!(expected_builtin_cost, tx_cost.builtins_execution_cost);
|
||||||
|
|
|
@ -119,7 +119,7 @@ impl CostTracker {
|
||||||
estimated_tx_cost: &TransactionCost,
|
estimated_tx_cost: &TransactionCost,
|
||||||
actual_execution_units: u64,
|
actual_execution_units: u64,
|
||||||
) {
|
) {
|
||||||
let estimated_execution_units = estimated_tx_cost.bpf_execution_cost;
|
let estimated_execution_units = estimated_tx_cost.bpf_execution_cost();
|
||||||
match actual_execution_units.cmp(&estimated_execution_units) {
|
match actual_execution_units.cmp(&estimated_execution_units) {
|
||||||
Ordering::Equal => (),
|
Ordering::Equal => (),
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
|
@ -180,17 +180,16 @@ impl CostTracker {
|
||||||
|
|
||||||
fn would_fit(&self, tx_cost: &TransactionCost) -> Result<(), CostTrackerError> {
|
fn would_fit(&self, tx_cost: &TransactionCost) -> Result<(), CostTrackerError> {
|
||||||
let cost: u64 = tx_cost.sum();
|
let cost: u64 = tx_cost.sum();
|
||||||
let vote_cost = if tx_cost.is_simple_vote { cost } else { 0 };
|
|
||||||
|
|
||||||
// check against the total package cost
|
|
||||||
if self.block_cost.saturating_add(cost) > self.block_cost_limit {
|
|
||||||
return Err(CostTrackerError::WouldExceedBlockMaxLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if tx_cost.is_simple_vote() {
|
||||||
// if vote transaction, check if it exceeds vote_transaction_limit
|
// if vote transaction, check if it exceeds vote_transaction_limit
|
||||||
if self.vote_cost.saturating_add(vote_cost) > self.vote_cost_limit {
|
if self.vote_cost.saturating_add(cost) > self.vote_cost_limit {
|
||||||
return Err(CostTrackerError::WouldExceedVoteMaxLimit);
|
return Err(CostTrackerError::WouldExceedVoteMaxLimit);
|
||||||
}
|
}
|
||||||
|
} else if self.block_cost.saturating_add(cost) > self.block_cost_limit {
|
||||||
|
// check against the total package cost
|
||||||
|
return Err(CostTrackerError::WouldExceedBlockMaxLimit);
|
||||||
|
}
|
||||||
|
|
||||||
// check if the transaction itself is more costly than the account_cost_limit
|
// check if the transaction itself is more costly than the account_cost_limit
|
||||||
if cost > self.account_cost_limit {
|
if cost > self.account_cost_limit {
|
||||||
|
@ -201,7 +200,7 @@ impl CostTracker {
|
||||||
// size. This way, transactions are not unnecessarily retried.
|
// size. This way, transactions are not unnecessarily retried.
|
||||||
let account_data_size = self
|
let account_data_size = self
|
||||||
.account_data_size
|
.account_data_size
|
||||||
.saturating_add(tx_cost.account_data_size);
|
.saturating_add(tx_cost.account_data_size());
|
||||||
if let Some(account_data_size_limit) = self.account_data_size_limit {
|
if let Some(account_data_size_limit) = self.account_data_size_limit {
|
||||||
if account_data_size > account_data_size_limit {
|
if account_data_size > account_data_size_limit {
|
||||||
return Err(CostTrackerError::WouldExceedAccountDataTotalLimit);
|
return Err(CostTrackerError::WouldExceedAccountDataTotalLimit);
|
||||||
|
@ -213,7 +212,7 @@ impl CostTracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check each account against account_cost_limit,
|
// check each account against account_cost_limit,
|
||||||
for account_key in tx_cost.writable_accounts.iter() {
|
for account_key in tx_cost.writable_accounts().iter() {
|
||||||
match self.cost_by_writable_accounts.get(account_key) {
|
match self.cost_by_writable_accounts.get(account_key) {
|
||||||
Some(chained_cost) => {
|
Some(chained_cost) => {
|
||||||
if chained_cost.saturating_add(cost) > self.account_cost_limit {
|
if chained_cost.saturating_add(cost) > self.account_cost_limit {
|
||||||
|
@ -231,7 +230,7 @@ impl CostTracker {
|
||||||
|
|
||||||
fn add_transaction_cost(&mut self, tx_cost: &TransactionCost) {
|
fn add_transaction_cost(&mut self, tx_cost: &TransactionCost) {
|
||||||
self.add_transaction_execution_cost(tx_cost, tx_cost.sum());
|
self.add_transaction_execution_cost(tx_cost, tx_cost.sum());
|
||||||
saturating_add_assign!(self.account_data_size, tx_cost.account_data_size);
|
saturating_add_assign!(self.account_data_size, tx_cost.account_data_size());
|
||||||
saturating_add_assign!(self.transaction_count, 1);
|
saturating_add_assign!(self.transaction_count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,13 +239,13 @@ impl CostTracker {
|
||||||
self.sub_transaction_execution_cost(tx_cost, cost);
|
self.sub_transaction_execution_cost(tx_cost, cost);
|
||||||
self.account_data_size = self
|
self.account_data_size = self
|
||||||
.account_data_size
|
.account_data_size
|
||||||
.saturating_sub(tx_cost.account_data_size);
|
.saturating_sub(tx_cost.account_data_size());
|
||||||
self.transaction_count = self.transaction_count.saturating_sub(1);
|
self.transaction_count = self.transaction_count.saturating_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply additional actual execution units to cost_tracker
|
/// Apply additional actual execution units to cost_tracker
|
||||||
fn add_transaction_execution_cost(&mut self, tx_cost: &TransactionCost, adjustment: u64) {
|
fn add_transaction_execution_cost(&mut self, tx_cost: &TransactionCost, adjustment: u64) {
|
||||||
for account_key in tx_cost.writable_accounts.iter() {
|
for account_key in tx_cost.writable_accounts().iter() {
|
||||||
let account_cost = self
|
let account_cost = self
|
||||||
.cost_by_writable_accounts
|
.cost_by_writable_accounts
|
||||||
.entry(*account_key)
|
.entry(*account_key)
|
||||||
|
@ -254,14 +253,14 @@ impl CostTracker {
|
||||||
*account_cost = account_cost.saturating_add(adjustment);
|
*account_cost = account_cost.saturating_add(adjustment);
|
||||||
}
|
}
|
||||||
self.block_cost = self.block_cost.saturating_add(adjustment);
|
self.block_cost = self.block_cost.saturating_add(adjustment);
|
||||||
if tx_cost.is_simple_vote {
|
if tx_cost.is_simple_vote() {
|
||||||
self.vote_cost = self.vote_cost.saturating_add(adjustment);
|
self.vote_cost = self.vote_cost.saturating_add(adjustment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subtract extra execution units from cost_tracker
|
/// Subtract extra execution units from cost_tracker
|
||||||
fn sub_transaction_execution_cost(&mut self, tx_cost: &TransactionCost, adjustment: u64) {
|
fn sub_transaction_execution_cost(&mut self, tx_cost: &TransactionCost, adjustment: u64) {
|
||||||
for account_key in tx_cost.writable_accounts.iter() {
|
for account_key in tx_cost.writable_accounts().iter() {
|
||||||
let account_cost = self
|
let account_cost = self
|
||||||
.cost_by_writable_accounts
|
.cost_by_writable_accounts
|
||||||
.entry(*account_key)
|
.entry(*account_key)
|
||||||
|
@ -269,7 +268,7 @@ impl CostTracker {
|
||||||
*account_cost = account_cost.saturating_sub(adjustment);
|
*account_cost = account_cost.saturating_sub(adjustment);
|
||||||
}
|
}
|
||||||
self.block_cost = self.block_cost.saturating_sub(adjustment);
|
self.block_cost = self.block_cost.saturating_sub(adjustment);
|
||||||
if tx_cost.is_simple_vote {
|
if tx_cost.is_simple_vote() {
|
||||||
self.vote_cost = self.vote_cost.saturating_sub(adjustment);
|
self.vote_cost = self.vote_cost.saturating_sub(adjustment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,6 +286,7 @@ impl CostTracker {
|
||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
|
crate::transaction_cost::*,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
|
@ -331,11 +331,11 @@ mod tests {
|
||||||
let simple_transaction = SanitizedTransaction::from_transaction_for_tests(
|
let simple_transaction = SanitizedTransaction::from_transaction_for_tests(
|
||||||
system_transaction::transfer(mint_keypair, &keypair.pubkey(), 2, *start_hash),
|
system_transaction::transfer(mint_keypair, &keypair.pubkey(), 2, *start_hash),
|
||||||
);
|
);
|
||||||
let mut tx_cost = TransactionCost::new_with_capacity(1);
|
let mut tx_cost = UsageCostDetails::new_with_capacity(1);
|
||||||
tx_cost.bpf_execution_cost = 5;
|
tx_cost.bpf_execution_cost = 5;
|
||||||
tx_cost.writable_accounts.push(mint_keypair.pubkey());
|
tx_cost.writable_accounts.push(mint_keypair.pubkey());
|
||||||
|
|
||||||
(simple_transaction, tx_cost)
|
(simple_transaction, TransactionCost::Transaction(tx_cost))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_simple_vote_transaction(
|
fn build_simple_vote_transaction(
|
||||||
|
@ -359,12 +359,12 @@ mod tests {
|
||||||
SimpleAddressLoader::Disabled,
|
SimpleAddressLoader::Disabled,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut tx_cost = TransactionCost::new_with_capacity(1);
|
|
||||||
tx_cost.builtins_execution_cost = 10;
|
|
||||||
tx_cost.writable_accounts.push(mint_keypair.pubkey());
|
|
||||||
tx_cost.is_simple_vote = true;
|
|
||||||
|
|
||||||
(vote_transaction, tx_cost)
|
let writable_accounts = vec![mint_keypair.pubkey()];
|
||||||
|
(
|
||||||
|
vote_transaction,
|
||||||
|
TransactionCost::SimpleVote { writable_accounts },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -413,7 +413,11 @@ mod tests {
|
||||||
fn test_cost_tracker_add_data() {
|
fn test_cost_tracker_add_data() {
|
||||||
let (mint_keypair, start_hash) = test_setup();
|
let (mint_keypair, start_hash) = test_setup();
|
||||||
let (_tx, mut tx_cost) = build_simple_transaction(&mint_keypair, &start_hash);
|
let (_tx, mut tx_cost) = build_simple_transaction(&mint_keypair, &start_hash);
|
||||||
tx_cost.account_data_size = 1;
|
if let TransactionCost::Transaction(ref mut usage_cost) = tx_cost {
|
||||||
|
usage_cost.account_data_size = 1;
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
let cost = tx_cost.sum();
|
let cost = tx_cost.sum();
|
||||||
|
|
||||||
// build testee to have capacity for one simple transaction
|
// build testee to have capacity for one simple transaction
|
||||||
|
@ -568,8 +572,16 @@ mod tests {
|
||||||
let second_account = Keypair::new();
|
let second_account = Keypair::new();
|
||||||
let (_tx1, mut tx_cost1) = build_simple_transaction(&mint_keypair, &start_hash);
|
let (_tx1, mut tx_cost1) = build_simple_transaction(&mint_keypair, &start_hash);
|
||||||
let (_tx2, mut tx_cost2) = build_simple_transaction(&second_account, &start_hash);
|
let (_tx2, mut tx_cost2) = build_simple_transaction(&second_account, &start_hash);
|
||||||
tx_cost1.account_data_size = MAX_BLOCK_ACCOUNTS_DATA_SIZE_DELTA;
|
if let TransactionCost::Transaction(ref mut usage_cost) = tx_cost1 {
|
||||||
tx_cost2.account_data_size = MAX_BLOCK_ACCOUNTS_DATA_SIZE_DELTA + 1;
|
usage_cost.account_data_size = MAX_BLOCK_ACCOUNTS_DATA_SIZE_DELTA;
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
if let TransactionCost::Transaction(ref mut usage_cost) = tx_cost2 {
|
||||||
|
usage_cost.account_data_size = MAX_BLOCK_ACCOUNTS_DATA_SIZE_DELTA + 1;
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
let cost1 = tx_cost1.sum();
|
let cost1 = tx_cost1.sum();
|
||||||
let cost2 = tx_cost2.sum();
|
let cost2 = tx_cost2.sum();
|
||||||
|
|
||||||
|
@ -596,8 +608,16 @@ mod tests {
|
||||||
let (_tx1, mut tx_cost1) = build_simple_transaction(&mint_keypair, &start_hash);
|
let (_tx1, mut tx_cost1) = build_simple_transaction(&mint_keypair, &start_hash);
|
||||||
let (_tx2, mut tx_cost2) = build_simple_transaction(&second_account, &start_hash);
|
let (_tx2, mut tx_cost2) = build_simple_transaction(&second_account, &start_hash);
|
||||||
let remaining_account_data_size = 1234;
|
let remaining_account_data_size = 1234;
|
||||||
tx_cost1.account_data_size = remaining_account_data_size;
|
if let TransactionCost::Transaction(ref mut usage_cost) = tx_cost1 {
|
||||||
tx_cost2.account_data_size = remaining_account_data_size + 1;
|
usage_cost.account_data_size = remaining_account_data_size;
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
if let TransactionCost::Transaction(ref mut usage_cost) = tx_cost2 {
|
||||||
|
usage_cost.account_data_size = remaining_account_data_size + 1;
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
let cost1 = tx_cost1.sum();
|
let cost1 = tx_cost1.sum();
|
||||||
let cost2 = tx_cost2.sum();
|
let cost2 = tx_cost2.sum();
|
||||||
|
|
||||||
|
@ -661,11 +681,11 @@ mod tests {
|
||||||
// | acct3 | $cost |
|
// | acct3 | $cost |
|
||||||
// and block_cost = $cost
|
// and block_cost = $cost
|
||||||
{
|
{
|
||||||
let tx_cost = TransactionCost {
|
let tx_cost = TransactionCost::Transaction(UsageCostDetails {
|
||||||
writable_accounts: vec![acct1, acct2, acct3],
|
writable_accounts: vec![acct1, acct2, acct3],
|
||||||
bpf_execution_cost: cost,
|
bpf_execution_cost: cost,
|
||||||
..TransactionCost::default()
|
..UsageCostDetails::default()
|
||||||
};
|
});
|
||||||
assert!(testee.try_add(&tx_cost).is_ok());
|
assert!(testee.try_add(&tx_cost).is_ok());
|
||||||
let (_costliest_account, costliest_account_cost) = testee.find_costliest_account();
|
let (_costliest_account, costliest_account_cost) = testee.find_costliest_account();
|
||||||
assert_eq!(cost, testee.block_cost);
|
assert_eq!(cost, testee.block_cost);
|
||||||
|
@ -679,11 +699,11 @@ mod tests {
|
||||||
// | acct3 | $cost |
|
// | acct3 | $cost |
|
||||||
// and block_cost = $cost * 2
|
// and block_cost = $cost * 2
|
||||||
{
|
{
|
||||||
let tx_cost = TransactionCost {
|
let tx_cost = TransactionCost::Transaction(UsageCostDetails {
|
||||||
writable_accounts: vec![acct2],
|
writable_accounts: vec![acct2],
|
||||||
bpf_execution_cost: cost,
|
bpf_execution_cost: cost,
|
||||||
..TransactionCost::default()
|
..UsageCostDetails::default()
|
||||||
};
|
});
|
||||||
assert!(testee.try_add(&tx_cost).is_ok());
|
assert!(testee.try_add(&tx_cost).is_ok());
|
||||||
let (costliest_account, costliest_account_cost) = testee.find_costliest_account();
|
let (costliest_account, costliest_account_cost) = testee.find_costliest_account();
|
||||||
assert_eq!(cost * 2, testee.block_cost);
|
assert_eq!(cost * 2, testee.block_cost);
|
||||||
|
@ -699,11 +719,11 @@ mod tests {
|
||||||
// | acct3 | $cost |
|
// | acct3 | $cost |
|
||||||
// and block_cost = $cost * 2
|
// and block_cost = $cost * 2
|
||||||
{
|
{
|
||||||
let tx_cost = TransactionCost {
|
let tx_cost = TransactionCost::Transaction(UsageCostDetails {
|
||||||
writable_accounts: vec![acct1, acct2],
|
writable_accounts: vec![acct1, acct2],
|
||||||
bpf_execution_cost: cost,
|
bpf_execution_cost: cost,
|
||||||
..TransactionCost::default()
|
..UsageCostDetails::default()
|
||||||
};
|
});
|
||||||
assert!(testee.try_add(&tx_cost).is_err());
|
assert!(testee.try_add(&tx_cost).is_err());
|
||||||
let (costliest_account, costliest_account_cost) = testee.find_costliest_account();
|
let (costliest_account, costliest_account_cost) = testee.find_costliest_account();
|
||||||
assert_eq!(cost * 2, testee.block_cost);
|
assert_eq!(cost * 2, testee.block_cost);
|
||||||
|
@ -723,11 +743,11 @@ mod tests {
|
||||||
let block_max = account_max * 3; // for three accts
|
let block_max = account_max * 3; // for three accts
|
||||||
|
|
||||||
let mut testee = CostTracker::new(account_max, block_max, block_max, None);
|
let mut testee = CostTracker::new(account_max, block_max, block_max, None);
|
||||||
let tx_cost = TransactionCost {
|
let tx_cost = TransactionCost::Transaction(UsageCostDetails {
|
||||||
writable_accounts: vec![acct1, acct2, acct3],
|
writable_accounts: vec![acct1, acct2, acct3],
|
||||||
bpf_execution_cost: cost,
|
bpf_execution_cost: cost,
|
||||||
..TransactionCost::default()
|
..UsageCostDetails::default()
|
||||||
};
|
});
|
||||||
let mut expected_block_cost = tx_cost.sum();
|
let mut expected_block_cost = tx_cost.sum();
|
||||||
let expected_tx_count = 1;
|
let expected_tx_count = 1;
|
||||||
assert!(testee.try_add(&tx_cost).is_ok());
|
assert!(testee.try_add(&tx_cost).is_ok());
|
||||||
|
@ -810,11 +830,11 @@ mod tests {
|
||||||
let acct3 = Pubkey::new_unique();
|
let acct3 = Pubkey::new_unique();
|
||||||
let cost = 100;
|
let cost = 100;
|
||||||
|
|
||||||
let tx_cost = TransactionCost {
|
let tx_cost = TransactionCost::Transaction(UsageCostDetails {
|
||||||
writable_accounts: vec![acct1, acct2, acct3],
|
writable_accounts: vec![acct1, acct2, acct3],
|
||||||
bpf_execution_cost: cost,
|
bpf_execution_cost: cost,
|
||||||
..TransactionCost::default()
|
..UsageCostDetails::default()
|
||||||
};
|
});
|
||||||
|
|
||||||
let mut cost_tracker = CostTracker::default();
|
let mut cost_tracker = CostTracker::default();
|
||||||
|
|
||||||
|
@ -857,11 +877,11 @@ mod tests {
|
||||||
let mut cost_tracker = CostTracker::default();
|
let mut cost_tracker = CostTracker::default();
|
||||||
|
|
||||||
let cost = 100u64;
|
let cost = 100u64;
|
||||||
let tx_cost = TransactionCost {
|
let tx_cost = TransactionCost::Transaction(UsageCostDetails {
|
||||||
writable_accounts: vec![Pubkey::new_unique()],
|
writable_accounts: vec![Pubkey::new_unique()],
|
||||||
bpf_execution_cost: cost,
|
bpf_execution_cost: cost,
|
||||||
..TransactionCost::default()
|
..UsageCostDetails::default()
|
||||||
};
|
});
|
||||||
|
|
||||||
cost_tracker.add_transaction_cost(&tx_cost);
|
cost_tracker.add_transaction_cost(&tx_cost);
|
||||||
// assert cost_tracker is reverted to default
|
// assert cost_tracker is reverted to default
|
||||||
|
|
|
@ -1,10 +1,99 @@
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use {crate::block_cost_limits, solana_sdk::pubkey::Pubkey};
|
||||||
|
|
||||||
|
/// TransactionCost is used to represent resources required to process
|
||||||
|
/// a transaction, denominated in CU (eg. Compute Units).
|
||||||
|
/// Resources required to process a regular transaction often include
|
||||||
|
/// an array of variables, such as execution cost, loaded bytes, write
|
||||||
|
/// lock and read lock etc.
|
||||||
|
/// SimpleVote has a simpler and pre-determined format: it has 1 or 2 signatures,
|
||||||
|
/// 2 write locks, a vote instruction and less than 32k (page size) accounts to load.
|
||||||
|
/// It's cost therefore can be static #33269.
|
||||||
|
const SIMPLE_VOTE_USAGE_COST: u64 = 3428;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TransactionCost {
|
||||||
|
SimpleVote { writable_accounts: Vec<Pubkey> },
|
||||||
|
Transaction(UsageCostDetails),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionCost {
|
||||||
|
pub fn sum(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::SimpleVote { .. } => SIMPLE_VOTE_USAGE_COST,
|
||||||
|
Self::Transaction(usage_cost) => usage_cost.sum(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bpf_execution_cost(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::SimpleVote { .. } => 0,
|
||||||
|
Self::Transaction(usage_cost) => usage_cost.bpf_execution_cost,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_simple_vote(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::SimpleVote { .. } => true,
|
||||||
|
Self::Transaction(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_bytes_cost(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::SimpleVote { .. } => 0,
|
||||||
|
Self::Transaction(usage_cost) => usage_cost.data_bytes_cost,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn account_data_size(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::SimpleVote { .. } => 0,
|
||||||
|
Self::Transaction(usage_cost) => usage_cost.account_data_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loaded_accounts_data_size_cost(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::SimpleVote { .. } => 8, // simple-vote loads less than 32K account data,
|
||||||
|
// the cost round up to be one page (32K) cost: 8CU
|
||||||
|
Self::Transaction(usage_cost) => usage_cost.loaded_accounts_data_size_cost,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature_cost(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::SimpleVote { .. } => block_cost_limits::SIGNATURE_COST,
|
||||||
|
Self::Transaction(usage_cost) => usage_cost.signature_cost,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_lock_cost(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::SimpleVote { .. } => block_cost_limits::WRITE_LOCK_UNITS.saturating_mul(2),
|
||||||
|
Self::Transaction(usage_cost) => usage_cost.write_lock_cost,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn builtins_execution_cost(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::SimpleVote { .. } => solana_vote_program::vote_processor::DEFAULT_COMPUTE_UNITS,
|
||||||
|
Self::Transaction(usage_cost) => usage_cost.builtins_execution_cost,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writable_accounts(&self) -> &[Pubkey] {
|
||||||
|
match self {
|
||||||
|
Self::SimpleVote { writable_accounts } => writable_accounts,
|
||||||
|
Self::Transaction(usage_cost) => &usage_cost.writable_accounts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const MAX_WRITABLE_ACCOUNTS: usize = 256;
|
const MAX_WRITABLE_ACCOUNTS: usize = 256;
|
||||||
|
|
||||||
// costs are stored in number of 'compute unit's
|
// costs are stored in number of 'compute unit's
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TransactionCost {
|
pub struct UsageCostDetails {
|
||||||
pub writable_accounts: Vec<Pubkey>,
|
pub writable_accounts: Vec<Pubkey>,
|
||||||
pub signature_cost: u64,
|
pub signature_cost: u64,
|
||||||
pub write_lock_cost: u64,
|
pub write_lock_cost: u64,
|
||||||
|
@ -13,10 +102,9 @@ pub struct TransactionCost {
|
||||||
pub bpf_execution_cost: u64,
|
pub bpf_execution_cost: u64,
|
||||||
pub loaded_accounts_data_size_cost: u64,
|
pub loaded_accounts_data_size_cost: u64,
|
||||||
pub account_data_size: u64,
|
pub account_data_size: u64,
|
||||||
pub is_simple_vote: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TransactionCost {
|
impl Default for UsageCostDetails {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
writable_accounts: Vec::with_capacity(MAX_WRITABLE_ACCOUNTS),
|
writable_accounts: Vec::with_capacity(MAX_WRITABLE_ACCOUNTS),
|
||||||
|
@ -27,13 +115,12 @@ impl Default for TransactionCost {
|
||||||
bpf_execution_cost: 0u64,
|
bpf_execution_cost: 0u64,
|
||||||
loaded_accounts_data_size_cost: 0u64,
|
loaded_accounts_data_size_cost: 0u64,
|
||||||
account_data_size: 0u64,
|
account_data_size: 0u64,
|
||||||
is_simple_vote: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl PartialEq for TransactionCost {
|
impl PartialEq for UsageCostDetails {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
fn to_hash_set(v: &[Pubkey]) -> std::collections::HashSet<&Pubkey> {
|
fn to_hash_set(v: &[Pubkey]) -> std::collections::HashSet<&Pubkey> {
|
||||||
v.iter().collect()
|
v.iter().collect()
|
||||||
|
@ -46,15 +133,15 @@ impl PartialEq for TransactionCost {
|
||||||
&& self.bpf_execution_cost == other.bpf_execution_cost
|
&& self.bpf_execution_cost == other.bpf_execution_cost
|
||||||
&& self.loaded_accounts_data_size_cost == other.loaded_accounts_data_size_cost
|
&& self.loaded_accounts_data_size_cost == other.loaded_accounts_data_size_cost
|
||||||
&& self.account_data_size == other.account_data_size
|
&& self.account_data_size == other.account_data_size
|
||||||
&& self.is_simple_vote == other.is_simple_vote
|
|
||||||
&& to_hash_set(&self.writable_accounts) == to_hash_set(&other.writable_accounts)
|
&& to_hash_set(&self.writable_accounts) == to_hash_set(&other.writable_accounts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl Eq for TransactionCost {}
|
impl Eq for UsageCostDetails {}
|
||||||
|
|
||||||
impl TransactionCost {
|
impl UsageCostDetails {
|
||||||
|
#[cfg(test)]
|
||||||
pub fn new_with_capacity(capacity: usize) -> Self {
|
pub fn new_with_capacity(capacity: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
writable_accounts: Vec::with_capacity(capacity),
|
writable_accounts: Vec::with_capacity(capacity),
|
||||||
|
@ -67,12 +154,6 @@ impl TransactionCost {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sum(&self) -> u64 {
|
pub fn sum(&self) -> u64 {
|
||||||
if self.is_simple_vote {
|
|
||||||
self.signature_cost
|
|
||||||
.saturating_add(self.write_lock_cost)
|
|
||||||
.saturating_add(self.data_bytes_cost)
|
|
||||||
.saturating_add(self.builtins_execution_cost)
|
|
||||||
} else {
|
|
||||||
self.signature_cost
|
self.signature_cost
|
||||||
.saturating_add(self.write_lock_cost)
|
.saturating_add(self.write_lock_cost)
|
||||||
.saturating_add(self.data_bytes_cost)
|
.saturating_add(self.data_bytes_cost)
|
||||||
|
@ -80,12 +161,12 @@ impl TransactionCost {
|
||||||
.saturating_add(self.bpf_execution_cost)
|
.saturating_add(self.bpf_execution_cost)
|
||||||
.saturating_add(self.loaded_accounts_data_size_cost)
|
.saturating_add(self.loaded_accounts_data_size_cost)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
|
super::*,
|
||||||
crate::cost_model::CostModel,
|
crate::cost_model::CostModel,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
feature_set::FeatureSet,
|
feature_set::FeatureSet,
|
||||||
|
@ -131,8 +212,8 @@ mod tests {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// expected vote tx cost: 2 write locks, 2 sig, 1 vite ix, and 11 CU tx data cost
|
// expected vote tx cost: 2 write locks, 1 sig, 1 vote ix, 8cu of loaded accounts size,
|
||||||
let expected_vote_cost = 4151;
|
let expected_vote_cost = SIMPLE_VOTE_USAGE_COST;
|
||||||
// expected non-vote tx cost would include default loaded accounts size cost (16384) additionally
|
// expected non-vote tx cost would include default loaded accounts size cost (16384) additionally
|
||||||
let expected_none_vote_cost = 20535;
|
let expected_none_vote_cost = 20535;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue