|
|
@@ -12,13 +12,13 @@ use {
|
|
|
DEFAULT_HEAP_COST, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_COMPUTE_UNIT_LIMIT,
|
|
|
},
|
|
|
solana_feature_set::{self as feature_set, FeatureSet},
|
|
|
- solana_runtime_transaction::instructions_processor::process_compute_budget_instructions,
|
|
|
+ solana_runtime_transaction::{
|
|
|
+ runtime_transaction::RuntimeTransaction, transaction_meta::StaticMeta,
|
|
|
+ },
|
|
|
solana_sdk::{
|
|
|
borsh1::try_from_slice_unchecked,
|
|
|
compute_budget::{self, ComputeBudgetInstruction},
|
|
|
fee::FeeStructure,
|
|
|
- instruction::CompiledInstruction,
|
|
|
- message::TransactionSignatureDetails,
|
|
|
program_utils::limited_deserialize,
|
|
|
pubkey::Pubkey,
|
|
|
saturating_add_assign,
|
|
|
@@ -27,9 +27,8 @@ use {
|
|
|
MAX_PERMITTED_DATA_LENGTH,
|
|
|
},
|
|
|
system_program,
|
|
|
- transaction::SanitizedTransaction,
|
|
|
},
|
|
|
- solana_svm_transaction::svm_message::SVMMessage,
|
|
|
+ solana_svm_transaction::{instruction::SVMInstruction, svm_message::SVMMessage},
|
|
|
};
|
|
|
|
|
|
pub struct CostModel;
|
|
|
@@ -42,15 +41,14 @@ enum SystemProgramAccountAllocation {
|
|
|
}
|
|
|
|
|
|
impl CostModel {
|
|
|
- pub fn calculate_cost<'a>(
|
|
|
- transaction: &'a SanitizedTransaction,
|
|
|
+ pub fn calculate_cost<'a, Tx: SVMMessage>(
|
|
|
+ transaction: &'a RuntimeTransaction<Tx>,
|
|
|
feature_set: &FeatureSet,
|
|
|
- ) -> TransactionCost<'a, SanitizedTransaction> {
|
|
|
+ ) -> TransactionCost<'a, Tx> {
|
|
|
if transaction.is_simple_vote_transaction() {
|
|
|
TransactionCost::SimpleVote { transaction }
|
|
|
} else {
|
|
|
- let (signatures_count_detail, signature_cost) =
|
|
|
- Self::get_signature_cost(transaction, feature_set);
|
|
|
+ let signature_cost = Self::get_signature_cost(transaction, feature_set);
|
|
|
let write_lock_cost = Self::get_write_lock_cost(transaction, feature_set);
|
|
|
let (programs_execution_cost, loaded_accounts_data_size_cost, data_bytes_cost) =
|
|
|
Self::get_transaction_cost(transaction, feature_set);
|
|
|
@@ -65,7 +63,6 @@ impl CostModel {
|
|
|
programs_execution_cost,
|
|
|
loaded_accounts_data_size_cost,
|
|
|
allocated_accounts_data_size,
|
|
|
- signature_details: signatures_count_detail,
|
|
|
};
|
|
|
|
|
|
TransactionCost::Transaction(usage_cost_details)
|
|
|
@@ -74,17 +71,16 @@ impl CostModel {
|
|
|
|
|
|
// Calculate executed transaction CU cost, with actual execution and loaded accounts size
|
|
|
// costs.
|
|
|
- pub fn calculate_cost_for_executed_transaction<'a>(
|
|
|
- transaction: &'a SanitizedTransaction,
|
|
|
+ pub fn calculate_cost_for_executed_transaction<'a, Tx: SVMMessage>(
|
|
|
+ transaction: &'a RuntimeTransaction<Tx>,
|
|
|
actual_programs_execution_cost: u64,
|
|
|
actual_loaded_accounts_data_size_bytes: u32,
|
|
|
feature_set: &FeatureSet,
|
|
|
- ) -> TransactionCost<'a, SanitizedTransaction> {
|
|
|
+ ) -> TransactionCost<'a, Tx> {
|
|
|
if transaction.is_simple_vote_transaction() {
|
|
|
TransactionCost::SimpleVote { transaction }
|
|
|
} else {
|
|
|
- let (signatures_count_detail, signature_cost) =
|
|
|
- Self::get_signature_cost(transaction, feature_set);
|
|
|
+ let signature_cost = Self::get_signature_cost(transaction, feature_set);
|
|
|
let write_lock_cost = Self::get_write_lock_cost(transaction, feature_set);
|
|
|
|
|
|
let instructions_data_cost = Self::get_instructions_data_cost(transaction);
|
|
|
@@ -105,7 +101,6 @@ impl CostModel {
|
|
|
programs_execution_cost,
|
|
|
loaded_accounts_data_size_cost,
|
|
|
allocated_accounts_data_size,
|
|
|
- signature_details: signatures_count_detail,
|
|
|
};
|
|
|
|
|
|
TransactionCost::Transaction(usage_cost_details)
|
|
|
@@ -114,10 +109,10 @@ impl CostModel {
|
|
|
|
|
|
/// Returns signature details and the total signature cost
|
|
|
fn get_signature_cost(
|
|
|
- transaction: &SanitizedTransaction,
|
|
|
+ transaction: &RuntimeTransaction<impl SVMMessage>,
|
|
|
feature_set: &FeatureSet,
|
|
|
- ) -> (TransactionSignatureDetails, u64) {
|
|
|
- let signatures_count_detail = transaction.message().get_signature_details();
|
|
|
+ ) -> u64 {
|
|
|
+ let signatures_count_detail = transaction.signature_details();
|
|
|
|
|
|
let ed25519_verify_cost =
|
|
|
if feature_set.is_active(&feature_set::ed25519_precompile_verify_strict::id()) {
|
|
|
@@ -126,7 +121,7 @@ impl CostModel {
|
|
|
ED25519_VERIFY_COST
|
|
|
};
|
|
|
|
|
|
- let signature_cost = signatures_count_detail
|
|
|
+ signatures_count_detail
|
|
|
.num_transaction_signatures()
|
|
|
.saturating_mul(SIGNATURE_COST)
|
|
|
.saturating_add(
|
|
|
@@ -138,9 +133,7 @@ impl CostModel {
|
|
|
signatures_count_detail
|
|
|
.num_ed25519_instruction_signatures()
|
|
|
.saturating_mul(ed25519_verify_cost),
|
|
|
- );
|
|
|
-
|
|
|
- (signatures_count_detail, signature_cost)
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
fn get_writable_accounts(message: &impl SVMMessage) -> impl Iterator<Item = &Pubkey> {
|
|
|
@@ -164,7 +157,7 @@ impl CostModel {
|
|
|
|
|
|
/// Return (programs_execution_cost, loaded_accounts_data_size_cost, data_bytes_cost)
|
|
|
fn get_transaction_cost(
|
|
|
- transaction: &impl SVMMessage,
|
|
|
+ transaction: &RuntimeTransaction<impl SVMMessage>,
|
|
|
feature_set: &FeatureSet,
|
|
|
) -> (u64, u64, u64) {
|
|
|
let mut programs_execution_costs = 0u64;
|
|
|
@@ -198,15 +191,20 @@ impl CostModel {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // if failed to process compute_budget instructions, the transaction will not be executed
|
|
|
- // by `bank`, therefore it should be considered as no execution cost by cost model.
|
|
|
- match process_compute_budget_instructions(transaction.program_instructions_iter()) {
|
|
|
+ // if failed to process compute budget instructions, the transaction
|
|
|
+ // will not be executed by `bank`, therefore it should be considered
|
|
|
+ // as no execution cost by cost model.
|
|
|
+ match transaction.compute_budget_limits(feature_set) {
|
|
|
Ok(compute_budget_limits) => {
|
|
|
- // if tx contained user-space instructions and a more accurate estimate available correct it,
|
|
|
- // where "user-space instructions" must be specifically checked by
|
|
|
- // 'compute_unit_limit_is_set' flag, because compute_budget does not distinguish
|
|
|
- // builtin and bpf instructions when calculating default compute-unit-limit. (see
|
|
|
- // compute_budget.rs test `test_process_mixed_instructions_without_compute_budget`)
|
|
|
+ // if tx contained user-space instructions and a more accurate
|
|
|
+ // estimate available correct it, where
|
|
|
+ // "user-space instructions" must be specifically checked by
|
|
|
+ // 'compute_unit_limit_is_set' flag, because compute_budget
|
|
|
+ // does not distinguish builtin and bpf instructions when
|
|
|
+ // calculating default compute-unit-limit.
|
|
|
+ //
|
|
|
+ // (see compute_budget.rs test
|
|
|
+ // `test_process_mixed_instructions_without_compute_budget`)
|
|
|
if has_user_space_instructions && compute_unit_limit_is_set {
|
|
|
programs_execution_costs = u64::from(compute_budget_limits.compute_unit_limit);
|
|
|
}
|
|
|
@@ -229,11 +227,9 @@ impl CostModel {
|
|
|
}
|
|
|
|
|
|
/// Return the instruction data bytes cost.
|
|
|
- fn get_instructions_data_cost(transaction: &SanitizedTransaction) -> u64 {
|
|
|
+ fn get_instructions_data_cost(transaction: &impl SVMMessage) -> u64 {
|
|
|
let ix_data_bytes_len_total: u64 = transaction
|
|
|
- .message()
|
|
|
- .instructions()
|
|
|
- .iter()
|
|
|
+ .instructions_iter()
|
|
|
.map(|instruction| instruction.data.len() as u64)
|
|
|
.sum();
|
|
|
|
|
|
@@ -267,10 +263,10 @@ impl CostModel {
|
|
|
|
|
|
fn calculate_account_data_size_on_instruction(
|
|
|
program_id: &Pubkey,
|
|
|
- instruction: &CompiledInstruction,
|
|
|
+ instruction: SVMInstruction,
|
|
|
) -> SystemProgramAccountAllocation {
|
|
|
if program_id == &system_program::id() {
|
|
|
- if let Ok(instruction) = limited_deserialize(&instruction.data) {
|
|
|
+ if let Ok(instruction) = limited_deserialize(instruction.data) {
|
|
|
Self::calculate_account_data_size_on_deserialized_system_instruction(instruction)
|
|
|
} else {
|
|
|
SystemProgramAccountAllocation::Failed
|
|
|
@@ -282,9 +278,9 @@ impl CostModel {
|
|
|
|
|
|
/// eventually, potentially determine account data size of all writable accounts
|
|
|
/// at the moment, calculate account data size of account creation
|
|
|
- fn calculate_allocated_accounts_data_size(transaction: &SanitizedTransaction) -> u64 {
|
|
|
+ fn calculate_allocated_accounts_data_size(transaction: &impl SVMMessage) -> u64 {
|
|
|
let mut tx_attempted_allocation_size: u64 = 0;
|
|
|
- for (program_id, instruction) in transaction.message().program_instructions_iter() {
|
|
|
+ for (program_id, instruction) in transaction.program_instructions_iter() {
|
|
|
match Self::calculate_account_data_size_on_instruction(program_id, instruction) {
|
|
|
SystemProgramAccountAllocation::Failed => {
|
|
|
// If any system program instructions can be statically
|
|
|
@@ -351,7 +347,7 @@ mod tests {
|
|
|
)],
|
|
|
Some(&Pubkey::new_unique()),
|
|
|
));
|
|
|
- let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction);
|
|
|
+ let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction);
|
|
|
|
|
|
assert_eq!(
|
|
|
CostModel::calculate_allocated_accounts_data_size(&sanitized_tx),
|
|
|
@@ -376,7 +372,7 @@ mod tests {
|
|
|
],
|
|
|
Some(&Pubkey::new_unique()),
|
|
|
));
|
|
|
- let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction);
|
|
|
+ let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction);
|
|
|
|
|
|
assert_eq!(
|
|
|
CostModel::calculate_allocated_accounts_data_size(&sanitized_tx),
|
|
|
@@ -417,7 +413,7 @@ mod tests {
|
|
|
],
|
|
|
Some(&Pubkey::new_unique()),
|
|
|
));
|
|
|
- let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction);
|
|
|
+ let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction);
|
|
|
|
|
|
assert_eq!(
|
|
|
CostModel::calculate_allocated_accounts_data_size(&sanitized_tx),
|
|
|
@@ -440,7 +436,7 @@ mod tests {
|
|
|
],
|
|
|
Some(&Pubkey::new_unique()),
|
|
|
));
|
|
|
- let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction);
|
|
|
+ let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction);
|
|
|
|
|
|
assert_eq!(
|
|
|
0, // SystemProgramAccountAllocation::Failed,
|
|
|
@@ -457,7 +453,7 @@ mod tests {
|
|
|
],
|
|
|
Some(&Pubkey::new_unique()),
|
|
|
));
|
|
|
- let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(transaction);
|
|
|
+ let sanitized_tx = RuntimeTransaction::from_transaction_for_tests(transaction);
|
|
|
|
|
|
assert_eq!(
|
|
|
0, // SystemProgramAccountAllocation::Failed,
|
|
|
@@ -517,7 +513,7 @@ mod tests {
|
|
|
let (mint_keypair, start_hash) = test_setup();
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
- let simple_transaction = SanitizedTransaction::from_transaction_for_tests(
|
|
|
+ let simple_transaction = RuntimeTransaction::from_transaction_for_tests(
|
|
|
system_transaction::transfer(&mint_keypair, &keypair.pubkey(), 2, start_hash),
|
|
|
);
|
|
|
debug!(
|
|
|
@@ -552,7 +548,7 @@ mod tests {
|
|
|
vec![Pubkey::new_unique()],
|
|
|
instructions,
|
|
|
);
|
|
|
- let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx);
|
|
|
+ let token_transaction = RuntimeTransaction::from_transaction_for_tests(tx);
|
|
|
debug!("token_transaction {:?}", token_transaction);
|
|
|
|
|
|
let (program_execution_cost, _loaded_accounts_data_size_cost, data_bytes_cost) =
|
|
|
@@ -570,7 +566,7 @@ mod tests {
|
|
|
|
|
|
// Cannot write-lock the system program, it will be demoted when taking locks.
|
|
|
// However, the cost should be calculated as if it were taken.
|
|
|
- let simple_transaction = SanitizedTransaction::from_transaction_for_tests(
|
|
|
+ let simple_transaction = RuntimeTransaction::from_transaction_for_tests(
|
|
|
system_transaction::transfer(&mint_keypair, &system_program::id(), 2, start_hash),
|
|
|
);
|
|
|
|
|
|
@@ -614,7 +610,7 @@ mod tests {
|
|
|
vec![Pubkey::new_unique(), compute_budget::id()],
|
|
|
instructions,
|
|
|
);
|
|
|
- let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx);
|
|
|
+ let token_transaction = RuntimeTransaction::from_transaction_for_tests(tx);
|
|
|
|
|
|
let (program_execution_cost, _loaded_accounts_data_size_cost, data_bytes_cost) =
|
|
|
CostModel::get_transaction_cost(&token_transaction, &FeatureSet::all_enabled());
|
|
|
@@ -637,10 +633,10 @@ mod tests {
|
|
|
.unwrap(),
|
|
|
vec![],
|
|
|
),
|
|
|
- // to trigger `duplicate_instruction_error` error
|
|
|
+ // to trigger failure in `sanitize_and_convert_to_compute_budget_limits`
|
|
|
CompiledInstruction::new_from_raw_parts(
|
|
|
4,
|
|
|
- ComputeBudgetInstruction::SetComputeUnitLimit(1_000)
|
|
|
+ ComputeBudgetInstruction::SetLoadedAccountsDataSizeLimit(0)
|
|
|
.pack()
|
|
|
.unwrap(),
|
|
|
vec![],
|
|
|
@@ -656,7 +652,7 @@ mod tests {
|
|
|
vec![Pubkey::new_unique(), compute_budget::id()],
|
|
|
instructions,
|
|
|
);
|
|
|
- let token_transaction = SanitizedTransaction::from_transaction_for_tests(tx);
|
|
|
+ let token_transaction = RuntimeTransaction::from_transaction_for_tests(tx);
|
|
|
|
|
|
let (program_execution_cost, _loaded_accounts_data_size_cost, _data_bytes_cost) =
|
|
|
CostModel::get_transaction_cost(&token_transaction, &FeatureSet::all_enabled());
|
|
|
@@ -672,7 +668,7 @@ mod tests {
|
|
|
let instructions =
|
|
|
system_instruction::transfer_many(&mint_keypair.pubkey(), &[(key1, 1), (key2, 1)]);
|
|
|
let message = Message::new(&instructions, Some(&mint_keypair.pubkey()));
|
|
|
- let tx = SanitizedTransaction::from_transaction_for_tests(Transaction::new(
|
|
|
+ let tx = RuntimeTransaction::from_transaction_for_tests(Transaction::new(
|
|
|
&[&mint_keypair],
|
|
|
message,
|
|
|
start_hash,
|
|
|
@@ -704,7 +700,7 @@ mod tests {
|
|
|
CompiledInstruction::new(3, &(), vec![0, 1]),
|
|
|
CompiledInstruction::new(4, &(), vec![0, 2]),
|
|
|
];
|
|
|
- let tx = SanitizedTransaction::from_transaction_for_tests(
|
|
|
+ let tx = RuntimeTransaction::from_transaction_for_tests(
|
|
|
Transaction::new_with_compiled_instructions(
|
|
|
&[&mint_keypair],
|
|
|
&[key1, key2],
|
|
|
@@ -735,7 +731,7 @@ mod tests {
|
|
|
CompiledInstruction::new(4, &(), vec![0, 2]),
|
|
|
CompiledInstruction::new(5, &(), vec![1, 3]),
|
|
|
];
|
|
|
- let tx = SanitizedTransaction::from_transaction_for_tests(
|
|
|
+ let tx = RuntimeTransaction::from_transaction_for_tests(
|
|
|
Transaction::new_with_compiled_instructions(
|
|
|
&[&signer1, &signer2],
|
|
|
&[key1, key2],
|
|
|
@@ -757,7 +753,7 @@ mod tests {
|
|
|
#[test]
|
|
|
fn test_cost_model_calculate_cost_all_default() {
|
|
|
let (mint_keypair, start_hash) = test_setup();
|
|
|
- let tx = SanitizedTransaction::from_transaction_for_tests(system_transaction::transfer(
|
|
|
+ let tx = RuntimeTransaction::from_transaction_for_tests(system_transaction::transfer(
|
|
|
&mint_keypair,
|
|
|
&Keypair::new().pubkey(),
|
|
|
2,
|
|
|
@@ -791,7 +787,7 @@ mod tests {
|
|
|
let to_keypair = Keypair::new();
|
|
|
let data_limit = 32 * 1024u32;
|
|
|
let tx =
|
|
|
- SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
|
|
|
+ RuntimeTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
|
|
|
&[
|
|
|
system_instruction::transfer(&mint_keypair.pubkey(), &to_keypair.pubkey(), 2),
|
|
|
ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_limit),
|
|
|
@@ -826,7 +822,7 @@ mod tests {
|
|
|
let (mint_keypair, start_hash) = test_setup();
|
|
|
|
|
|
let transaction =
|
|
|
- SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
|
|
|
+ RuntimeTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
|
|
|
&[
|
|
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
|
system_instruction::transfer(&mint_keypair.pubkey(), &Pubkey::new_unique(), 2),
|
|
|
@@ -855,7 +851,7 @@ mod tests {
|
|
|
let (mint_keypair, start_hash) = test_setup();
|
|
|
|
|
|
let transaction =
|
|
|
- SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
|
|
|
+ RuntimeTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
|
|
|
&[
|
|
|
system_instruction::transfer(&mint_keypair.pubkey(), &Pubkey::new_unique(), 2),
|
|
|
ComputeBudgetInstruction::set_compute_unit_limit(12_345),
|