Split compute budget instructions process from struct itself (#33513)
* Split compute budget instruction processing from ComputeBudget struct itself, allow compute_budget_instructions be processed elsewhere without having to instantiate ComputeBudget * updated tests
This commit is contained in:
parent
4e5c545e23
commit
c73bebe984
|
@ -25,7 +25,7 @@ use {
|
||||||
itertools::Itertools,
|
itertools::Itertools,
|
||||||
log::*,
|
log::*,
|
||||||
solana_program_runtime::{
|
solana_program_runtime::{
|
||||||
compute_budget::{self, ComputeBudget},
|
compute_budget_processor::process_compute_budget_instructions,
|
||||||
loaded_programs::LoadedProgramsForTxBatch,
|
loaded_programs::LoadedProgramsForTxBatch,
|
||||||
},
|
},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
|
@ -35,9 +35,8 @@ use {
|
||||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||||
clock::{BankId, Slot},
|
clock::{BankId, Slot},
|
||||||
feature_set::{
|
feature_set::{
|
||||||
self, add_set_tx_loaded_accounts_data_size_instruction,
|
self, include_loaded_accounts_data_size_in_fee_calculation,
|
||||||
include_loaded_accounts_data_size_in_fee_calculation,
|
remove_congestion_multiplier_from_fee_calculation,
|
||||||
remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix,
|
|
||||||
simplify_writable_program_account_check, FeatureSet,
|
simplify_writable_program_account_check, FeatureSet,
|
||||||
},
|
},
|
||||||
fee::FeeStructure,
|
fee::FeeStructure,
|
||||||
|
@ -247,15 +246,16 @@ impl Accounts {
|
||||||
feature_set: &FeatureSet,
|
feature_set: &FeatureSet,
|
||||||
) -> Result<Option<NonZeroUsize>> {
|
) -> Result<Option<NonZeroUsize>> {
|
||||||
if feature_set.is_active(&feature_set::cap_transaction_accounts_data_size::id()) {
|
if feature_set.is_active(&feature_set::cap_transaction_accounts_data_size::id()) {
|
||||||
let mut compute_budget =
|
let compute_budget_limits = process_compute_budget_instructions(
|
||||||
ComputeBudget::new(compute_budget::MAX_COMPUTE_UNIT_LIMIT as u64);
|
|
||||||
let _process_transaction_result = compute_budget.process_instructions(
|
|
||||||
tx.message().program_instructions_iter(),
|
tx.message().program_instructions_iter(),
|
||||||
!feature_set.is_active(&remove_deprecated_request_unit_ix::id()),
|
feature_set,
|
||||||
feature_set.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
|
)
|
||||||
);
|
.unwrap_or_default();
|
||||||
// sanitize against setting size limit to zero
|
// sanitize against setting size limit to zero
|
||||||
NonZeroUsize::new(compute_budget.loaded_accounts_data_size_limit).map_or(
|
NonZeroUsize::new(
|
||||||
|
usize::try_from(compute_budget_limits.loaded_accounts_bytes).unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.map_or(
|
||||||
Err(TransactionError::InvalidLoadedAccountsDataSizeLimit),
|
Err(TransactionError::InvalidLoadedAccountsDataSizeLimit),
|
||||||
|v| Ok(Some(v)),
|
|v| Ok(Some(v)),
|
||||||
)
|
)
|
||||||
|
@ -722,7 +722,7 @@ impl Accounts {
|
||||||
fee_structure.calculate_fee(
|
fee_structure.calculate_fee(
|
||||||
tx.message(),
|
tx.message(),
|
||||||
lamports_per_signature,
|
lamports_per_signature,
|
||||||
&ComputeBudget::fee_budget_limits(tx.message().program_instructions_iter(), feature_set),
|
&process_compute_budget_instructions(tx.message().program_instructions_iter(), feature_set).unwrap_or_default().into(),
|
||||||
feature_set.is_active(&remove_congestion_multiplier_from_fee_calculation::id()),
|
feature_set.is_active(&remove_congestion_multiplier_from_fee_calculation::id()),
|
||||||
feature_set.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()),
|
feature_set.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()),
|
||||||
)
|
)
|
||||||
|
@ -1474,8 +1474,9 @@ mod tests {
|
||||||
transaction_results::{DurableNonceFee, TransactionExecutionDetails},
|
transaction_results::{DurableNonceFee, TransactionExecutionDetails},
|
||||||
},
|
},
|
||||||
assert_matches::assert_matches,
|
assert_matches::assert_matches,
|
||||||
solana_program_runtime::prioritization_fee::{
|
solana_program_runtime::{
|
||||||
PrioritizationFeeDetails, PrioritizationFeeType,
|
compute_budget_processor,
|
||||||
|
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
|
||||||
},
|
},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::{AccountSharedData, WritableAccount},
|
account::{AccountSharedData, WritableAccount},
|
||||||
|
@ -1751,13 +1752,15 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut feature_set = FeatureSet::all_enabled();
|
let mut feature_set = FeatureSet::all_enabled();
|
||||||
feature_set.deactivate(&remove_deprecated_request_unit_ix::id());
|
feature_set.deactivate(&solana_sdk::feature_set::remove_deprecated_request_unit_ix::id());
|
||||||
|
|
||||||
let message = SanitizedMessage::try_from(tx.message().clone()).unwrap();
|
let message = SanitizedMessage::try_from(tx.message().clone()).unwrap();
|
||||||
let fee = FeeStructure::default().calculate_fee(
|
let fee = FeeStructure::default().calculate_fee(
|
||||||
&message,
|
&message,
|
||||||
lamports_per_signature,
|
lamports_per_signature,
|
||||||
&ComputeBudget::fee_budget_limits(message.program_instructions_iter(), &feature_set),
|
&process_compute_budget_instructions(message.program_instructions_iter(), &feature_set)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into(),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
@ -4253,7 +4256,11 @@ mod tests {
|
||||||
|
|
||||||
let result_no_limit = Ok(None);
|
let result_no_limit = Ok(None);
|
||||||
let result_default_limit = Ok(Some(
|
let result_default_limit = Ok(Some(
|
||||||
NonZeroUsize::new(compute_budget::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES).unwrap(),
|
NonZeroUsize::new(
|
||||||
|
usize::try_from(compute_budget_processor::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
));
|
));
|
||||||
let result_requested_limit: Result<Option<NonZeroUsize>> =
|
let result_requested_limit: Result<Option<NonZeroUsize>> =
|
||||||
Ok(Some(NonZeroUsize::new(99).unwrap()));
|
Ok(Some(NonZeroUsize::new(99).unwrap()));
|
||||||
|
@ -4281,7 +4288,10 @@ mod tests {
|
||||||
// if tx doesn't set limit, then default limit (64MiB)
|
// if tx doesn't set limit, then default limit (64MiB)
|
||||||
// if tx sets limit, then requested limit
|
// if tx sets limit, then requested limit
|
||||||
// if tx sets limit to zero, then TransactionError::InvalidLoadedAccountsDataSizeLimit
|
// if tx sets limit to zero, then TransactionError::InvalidLoadedAccountsDataSizeLimit
|
||||||
feature_set.activate(&add_set_tx_loaded_accounts_data_size_instruction::id(), 0);
|
feature_set.activate(
|
||||||
|
&solana_sdk::feature_set::add_set_tx_loaded_accounts_data_size_instruction::id(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
test(tx_not_set_limit, &feature_set, &result_default_limit);
|
test(tx_not_set_limit, &feature_set, &result_default_limit);
|
||||||
test(tx_set_limit_99, &feature_set, &result_requested_limit);
|
test(tx_set_limit_99, &feature_set, &result_requested_limit);
|
||||||
test(tx_set_limit_0, &feature_set, &result_invalid_limit);
|
test(tx_set_limit_0, &feature_set, &result_invalid_limit);
|
||||||
|
@ -4316,13 +4326,15 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut feature_set = FeatureSet::all_enabled();
|
let mut feature_set = FeatureSet::all_enabled();
|
||||||
feature_set.deactivate(&remove_deprecated_request_unit_ix::id());
|
feature_set.deactivate(&solana_sdk::feature_set::remove_deprecated_request_unit_ix::id());
|
||||||
|
|
||||||
let message = SanitizedMessage::try_from(tx.message().clone()).unwrap();
|
let message = SanitizedMessage::try_from(tx.message().clone()).unwrap();
|
||||||
let fee = FeeStructure::default().calculate_fee(
|
let fee = FeeStructure::default().calculate_fee(
|
||||||
&message,
|
&message,
|
||||||
lamports_per_signature,
|
lamports_per_signature,
|
||||||
&ComputeBudget::fee_budget_limits(message.program_instructions_iter(), &feature_set),
|
&process_compute_budget_instructions(message.program_instructions_iter(), &feature_set)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into(),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,17 +8,17 @@
|
||||||
use {
|
use {
|
||||||
crate::{block_cost_limits::*, transaction_cost::*},
|
crate::{block_cost_limits::*, transaction_cost::*},
|
||||||
log::*,
|
log::*,
|
||||||
solana_program_runtime::compute_budget::{
|
solana_program_runtime::{
|
||||||
ComputeBudget, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_COMPUTE_UNIT_LIMIT,
|
compute_budget::DEFAULT_HEAP_COST,
|
||||||
|
compute_budget_processor::{
|
||||||
|
process_compute_budget_instructions, ComputeBudgetLimits,
|
||||||
|
DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_COMPUTE_UNIT_LIMIT,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
borsh0_10::try_from_slice_unchecked,
|
borsh0_10::try_from_slice_unchecked,
|
||||||
compute_budget::{self, ComputeBudgetInstruction},
|
compute_budget::{self, ComputeBudgetInstruction},
|
||||||
feature_set::{
|
feature_set::{include_loaded_accounts_data_size_in_fee_calculation, FeatureSet},
|
||||||
add_set_tx_loaded_accounts_data_size_instruction,
|
|
||||||
include_loaded_accounts_data_size_in_fee_calculation,
|
|
||||||
remove_deprecated_request_unit_ix, FeatureSet,
|
|
||||||
},
|
|
||||||
fee::FeeStructure,
|
fee::FeeStructure,
|
||||||
instruction::CompiledInstruction,
|
instruction::CompiledInstruction,
|
||||||
program_utils::limited_deserialize,
|
program_utils::limited_deserialize,
|
||||||
|
@ -62,10 +62,12 @@ impl CostModel {
|
||||||
// to set limit, `compute_budget.loaded_accounts_data_size_limit` is set to default
|
// to set limit, `compute_budget.loaded_accounts_data_size_limit` is set to default
|
||||||
// limit of 64MB; which will convert to (64M/32K)*8CU = 16_000 CUs
|
// limit of 64MB; which will convert to (64M/32K)*8CU = 16_000 CUs
|
||||||
//
|
//
|
||||||
pub fn calculate_loaded_accounts_data_size_cost(compute_budget: &ComputeBudget) -> u64 {
|
pub fn calculate_loaded_accounts_data_size_cost(
|
||||||
|
compute_budget_limits: &ComputeBudgetLimits,
|
||||||
|
) -> u64 {
|
||||||
FeeStructure::calculate_memory_usage_cost(
|
FeeStructure::calculate_memory_usage_cost(
|
||||||
compute_budget.loaded_accounts_data_size_limit,
|
usize::try_from(compute_budget_limits.loaded_accounts_bytes).unwrap(),
|
||||||
compute_budget.heap_cost,
|
DEFAULT_HEAP_COST,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,32 +130,28 @@ impl CostModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate bpf cost based on compute budget instructions
|
// calculate bpf cost based on compute budget instructions
|
||||||
let mut compute_budget = ComputeBudget::default();
|
|
||||||
|
|
||||||
let result = compute_budget.process_instructions(
|
|
||||||
transaction.message().program_instructions_iter(),
|
|
||||||
!feature_set.is_active(&remove_deprecated_request_unit_ix::id()),
|
|
||||||
feature_set.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
|
|
||||||
);
|
|
||||||
|
|
||||||
// if failed to process compute_budget instructions, the transaction will not be executed
|
// 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.
|
// by `bank`, therefore it should be considered as no execution cost by cost model.
|
||||||
match result {
|
match process_compute_budget_instructions(
|
||||||
Ok(_) => {
|
transaction.message().program_instructions_iter(),
|
||||||
|
feature_set,
|
||||||
|
) {
|
||||||
|
Ok(compute_budget_limits) => {
|
||||||
// if tx contained user-space instructions and a more accurate estimate available correct it,
|
// if tx contained user-space instructions and a more accurate estimate available correct it,
|
||||||
// where "user-space instructions" must be specifically checked by
|
// where "user-space instructions" must be specifically checked by
|
||||||
// 'compute_unit_limit_is_set' flag, because compute_budget does not distinguish
|
// 'compute_unit_limit_is_set' flag, because compute_budget does not distinguish
|
||||||
// builtin and bpf instructions when calculating default compute-unit-limit. (see
|
// builtin and bpf instructions when calculating default compute-unit-limit. (see
|
||||||
// compute_budget.rs test `test_process_mixed_instructions_without_compute_budget`)
|
// compute_budget.rs test `test_process_mixed_instructions_without_compute_budget`)
|
||||||
if bpf_costs > 0 && compute_unit_limit_is_set {
|
if bpf_costs > 0 && compute_unit_limit_is_set {
|
||||||
bpf_costs = compute_budget.compute_unit_limit
|
bpf_costs = u64::from(compute_budget_limits.compute_unit_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if feature_set
|
if feature_set
|
||||||
.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id())
|
.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id())
|
||||||
{
|
{
|
||||||
loaded_accounts_data_size_cost =
|
loaded_accounts_data_size_cost =
|
||||||
Self::calculate_loaded_accounts_data_size_cost(&compute_budget);
|
Self::calculate_loaded_accounts_data_size_cost(&compute_budget_limits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
@ -545,7 +543,8 @@ mod tests {
|
||||||
// default loaded_accounts_data_size_limit
|
// default loaded_accounts_data_size_limit
|
||||||
const DEFAULT_PAGE_COST: u64 = 8;
|
const DEFAULT_PAGE_COST: u64 = 8;
|
||||||
let expected_loaded_accounts_data_size_cost =
|
let expected_loaded_accounts_data_size_cost =
|
||||||
solana_program_runtime::compute_budget::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES as u64
|
solana_program_runtime::compute_budget_processor::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES
|
||||||
|
as u64
|
||||||
/ ACCOUNT_DATA_COST_PAGE_SIZE
|
/ ACCOUNT_DATA_COST_PAGE_SIZE
|
||||||
* DEFAULT_PAGE_COST;
|
* DEFAULT_PAGE_COST;
|
||||||
|
|
||||||
|
@ -663,36 +662,36 @@ mod tests {
|
||||||
#[allow(clippy::field_reassign_with_default)]
|
#[allow(clippy::field_reassign_with_default)]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_calculate_loaded_accounts_data_size_cost() {
|
fn test_calculate_loaded_accounts_data_size_cost() {
|
||||||
let mut compute_budget = ComputeBudget::default();
|
let mut compute_budget_limits = ComputeBudgetLimits::default();
|
||||||
|
|
||||||
// accounts data size are priced in block of 32K, ...
|
// accounts data size are priced in block of 32K, ...
|
||||||
|
|
||||||
// ... requesting less than 32K should still be charged as one block
|
// ... requesting less than 32K should still be charged as one block
|
||||||
compute_budget.loaded_accounts_data_size_limit = 31_usize * 1024;
|
compute_budget_limits.loaded_accounts_bytes = 31 * 1024;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compute_budget.heap_cost,
|
DEFAULT_HEAP_COST,
|
||||||
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget)
|
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget_limits)
|
||||||
);
|
);
|
||||||
|
|
||||||
// ... requesting exact 32K should be charged as one block
|
// ... requesting exact 32K should be charged as one block
|
||||||
compute_budget.loaded_accounts_data_size_limit = 32_usize * 1024;
|
compute_budget_limits.loaded_accounts_bytes = 32 * 1024;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compute_budget.heap_cost,
|
DEFAULT_HEAP_COST,
|
||||||
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget)
|
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget_limits)
|
||||||
);
|
);
|
||||||
|
|
||||||
// ... requesting slightly above 32K should be charged as 2 block
|
// ... requesting slightly above 32K should be charged as 2 block
|
||||||
compute_budget.loaded_accounts_data_size_limit = 33_usize * 1024;
|
compute_budget_limits.loaded_accounts_bytes = 33 * 1024;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compute_budget.heap_cost * 2,
|
DEFAULT_HEAP_COST * 2,
|
||||||
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget)
|
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget_limits)
|
||||||
);
|
);
|
||||||
|
|
||||||
// ... requesting exact 64K should be charged as 2 block
|
// ... requesting exact 64K should be charged as 2 block
|
||||||
compute_budget.loaded_accounts_data_size_limit = 64_usize * 1024;
|
compute_budget_limits.loaded_accounts_bytes = 64 * 1024;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compute_budget.heap_cost * 2,
|
DEFAULT_HEAP_COST * 2,
|
||||||
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget)
|
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget_limits)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
|
crate::compute_budget_processor::{self, process_compute_budget_instructions},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
borsh0_10::try_from_slice_unchecked,
|
feature_set::FeatureSet, instruction::CompiledInstruction, pubkey::Pubkey,
|
||||||
compute_budget::{self, ComputeBudgetInstruction},
|
transaction::Result,
|
||||||
entrypoint::HEAP_LENGTH as MIN_HEAP_FRAME_BYTES,
|
|
||||||
feature_set::{
|
|
||||||
add_set_tx_loaded_accounts_data_size_instruction, remove_deprecated_request_unit_ix,
|
|
||||||
FeatureSet,
|
|
||||||
},
|
|
||||||
fee::FeeBudgetLimits,
|
|
||||||
instruction::{CompiledInstruction, InstructionError},
|
|
||||||
pubkey::Pubkey,
|
|
||||||
transaction::TransactionError,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The total accounts data a transaction can load is limited to 64MiB to not break
|
|
||||||
/// anyone in Mainnet-beta today. It can be set by set_loaded_accounts_data_size_limit instruction
|
|
||||||
pub const MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES: usize = 64 * 1024 * 1024;
|
|
||||||
|
|
||||||
pub const DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT: u32 = 200_000;
|
|
||||||
pub const MAX_COMPUTE_UNIT_LIMIT: u32 = 1_400_000;
|
|
||||||
const MAX_HEAP_FRAME_BYTES: u32 = 256 * 1024;
|
|
||||||
|
|
||||||
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
||||||
impl ::solana_frozen_abi::abi_example::AbiExample for ComputeBudget {
|
impl ::solana_frozen_abi::abi_example::AbiExample for ComputeBudget {
|
||||||
fn example() -> Self {
|
fn example() -> Self {
|
||||||
|
@ -31,6 +14,10 @@ impl ::solana_frozen_abi::abi_example::AbiExample for ComputeBudget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Roughly 0.5us/page, where page is 32K; given roughly 15CU/us, the
|
||||||
|
/// default heap page cost = 0.5 * 15 ~= 8CU/page
|
||||||
|
pub const DEFAULT_HEAP_COST: u64 = 8;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct ComputeBudget {
|
pub struct ComputeBudget {
|
||||||
/// Number of compute units that a transaction or individual instruction is
|
/// Number of compute units that a transaction or individual instruction is
|
||||||
|
@ -118,9 +105,6 @@ pub struct ComputeBudget {
|
||||||
pub alt_bn128_pairing_one_pair_cost_other: u64,
|
pub alt_bn128_pairing_one_pair_cost_other: u64,
|
||||||
/// Big integer modular exponentiation cost
|
/// Big integer modular exponentiation cost
|
||||||
pub big_modular_exponentiation_cost: u64,
|
pub big_modular_exponentiation_cost: u64,
|
||||||
/// Maximum accounts data size, in bytes, that a transaction is allowed to load; The
|
|
||||||
/// value is capped by MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES to prevent overuse of memory.
|
|
||||||
pub loaded_accounts_data_size_limit: usize,
|
|
||||||
/// Coefficient `a` of the quadratic function which determines the number
|
/// Coefficient `a` of the quadratic function which determines the number
|
||||||
/// of compute units consumed to call poseidon syscall for a given number
|
/// of compute units consumed to call poseidon syscall for a given number
|
||||||
/// of inputs.
|
/// of inputs.
|
||||||
|
@ -143,7 +127,7 @@ pub struct ComputeBudget {
|
||||||
|
|
||||||
impl Default for ComputeBudget {
|
impl Default for ComputeBudget {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(MAX_COMPUTE_UNIT_LIMIT as u64)
|
Self::new(compute_budget_processor::MAX_COMPUTE_UNIT_LIMIT as u64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,14 +164,13 @@ impl ComputeBudget {
|
||||||
curve25519_ristretto_msm_base_cost: 2303,
|
curve25519_ristretto_msm_base_cost: 2303,
|
||||||
curve25519_ristretto_msm_incremental_cost: 788,
|
curve25519_ristretto_msm_incremental_cost: 788,
|
||||||
heap_size: u32::try_from(solana_sdk::entrypoint::HEAP_LENGTH).unwrap(),
|
heap_size: u32::try_from(solana_sdk::entrypoint::HEAP_LENGTH).unwrap(),
|
||||||
heap_cost: 8,
|
heap_cost: DEFAULT_HEAP_COST,
|
||||||
mem_op_base_cost: 10,
|
mem_op_base_cost: 10,
|
||||||
alt_bn128_addition_cost: 334,
|
alt_bn128_addition_cost: 334,
|
||||||
alt_bn128_multiplication_cost: 3_840,
|
alt_bn128_multiplication_cost: 3_840,
|
||||||
alt_bn128_pairing_one_pair_cost_first: 36_364,
|
alt_bn128_pairing_one_pair_cost_first: 36_364,
|
||||||
alt_bn128_pairing_one_pair_cost_other: 12_121,
|
alt_bn128_pairing_one_pair_cost_other: 12_121,
|
||||||
big_modular_exponentiation_cost: 33,
|
big_modular_exponentiation_cost: 33,
|
||||||
loaded_accounts_data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
|
|
||||||
poseidon_cost_coefficient_a: 61,
|
poseidon_cost_coefficient_a: 61,
|
||||||
poseidon_cost_coefficient_c: 542,
|
poseidon_cost_coefficient_c: 542,
|
||||||
get_remaining_compute_units_cost: 100,
|
get_remaining_compute_units_cost: 100,
|
||||||
|
@ -198,127 +181,16 @@ impl ComputeBudget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_instructions<'a>(
|
pub fn try_from_instructions<'a>(
|
||||||
&mut self,
|
|
||||||
instructions: impl Iterator<Item = (&'a Pubkey, &'a CompiledInstruction)>,
|
|
||||||
support_request_units_deprecated: bool,
|
|
||||||
support_set_loaded_accounts_data_size_limit_ix: bool,
|
|
||||||
) -> Result<PrioritizationFeeDetails, TransactionError> {
|
|
||||||
let mut num_non_compute_budget_instructions: u32 = 0;
|
|
||||||
let mut updated_compute_unit_limit = None;
|
|
||||||
let mut requested_heap_size = None;
|
|
||||||
let mut prioritization_fee = None;
|
|
||||||
let mut updated_loaded_accounts_data_size_limit = None;
|
|
||||||
|
|
||||||
for (i, (program_id, instruction)) in instructions.enumerate() {
|
|
||||||
if compute_budget::check_id(program_id) {
|
|
||||||
let invalid_instruction_data_error = TransactionError::InstructionError(
|
|
||||||
i as u8,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
);
|
|
||||||
let duplicate_instruction_error = TransactionError::DuplicateInstruction(i as u8);
|
|
||||||
|
|
||||||
match try_from_slice_unchecked(&instruction.data) {
|
|
||||||
Ok(ComputeBudgetInstruction::RequestUnitsDeprecated {
|
|
||||||
units: compute_unit_limit,
|
|
||||||
additional_fee,
|
|
||||||
}) if support_request_units_deprecated => {
|
|
||||||
if updated_compute_unit_limit.is_some() {
|
|
||||||
return Err(duplicate_instruction_error);
|
|
||||||
}
|
|
||||||
if prioritization_fee.is_some() {
|
|
||||||
return Err(duplicate_instruction_error);
|
|
||||||
}
|
|
||||||
updated_compute_unit_limit = Some(compute_unit_limit);
|
|
||||||
prioritization_fee =
|
|
||||||
Some(PrioritizationFeeType::Deprecated(additional_fee as u64));
|
|
||||||
}
|
|
||||||
Ok(ComputeBudgetInstruction::RequestHeapFrame(bytes)) => {
|
|
||||||
if requested_heap_size.is_some() {
|
|
||||||
return Err(duplicate_instruction_error);
|
|
||||||
}
|
|
||||||
requested_heap_size = Some((bytes, i as u8));
|
|
||||||
}
|
|
||||||
Ok(ComputeBudgetInstruction::SetComputeUnitLimit(compute_unit_limit)) => {
|
|
||||||
if updated_compute_unit_limit.is_some() {
|
|
||||||
return Err(duplicate_instruction_error);
|
|
||||||
}
|
|
||||||
updated_compute_unit_limit = Some(compute_unit_limit);
|
|
||||||
}
|
|
||||||
Ok(ComputeBudgetInstruction::SetComputeUnitPrice(micro_lamports)) => {
|
|
||||||
if prioritization_fee.is_some() {
|
|
||||||
return Err(duplicate_instruction_error);
|
|
||||||
}
|
|
||||||
prioritization_fee =
|
|
||||||
Some(PrioritizationFeeType::ComputeUnitPrice(micro_lamports));
|
|
||||||
}
|
|
||||||
Ok(ComputeBudgetInstruction::SetLoadedAccountsDataSizeLimit(bytes))
|
|
||||||
if support_set_loaded_accounts_data_size_limit_ix =>
|
|
||||||
{
|
|
||||||
if updated_loaded_accounts_data_size_limit.is_some() {
|
|
||||||
return Err(duplicate_instruction_error);
|
|
||||||
}
|
|
||||||
updated_loaded_accounts_data_size_limit = Some(bytes as usize);
|
|
||||||
}
|
|
||||||
_ => return Err(invalid_instruction_data_error),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// only include non-request instructions in default max calc
|
|
||||||
num_non_compute_budget_instructions =
|
|
||||||
num_non_compute_budget_instructions.saturating_add(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((bytes, i)) = requested_heap_size {
|
|
||||||
if bytes > MAX_HEAP_FRAME_BYTES
|
|
||||||
|| bytes < MIN_HEAP_FRAME_BYTES as u32
|
|
||||||
|| bytes % 1024 != 0
|
|
||||||
{
|
|
||||||
return Err(TransactionError::InstructionError(
|
|
||||||
i,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
self.heap_size = bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
let compute_unit_limit = updated_compute_unit_limit
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
num_non_compute_budget_instructions
|
|
||||||
.saturating_mul(DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT)
|
|
||||||
})
|
|
||||||
.min(MAX_COMPUTE_UNIT_LIMIT);
|
|
||||||
self.compute_unit_limit = u64::from(compute_unit_limit);
|
|
||||||
|
|
||||||
self.loaded_accounts_data_size_limit = updated_loaded_accounts_data_size_limit
|
|
||||||
.unwrap_or(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES)
|
|
||||||
.min(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES);
|
|
||||||
|
|
||||||
Ok(prioritization_fee
|
|
||||||
.map(|fee_type| PrioritizationFeeDetails::new(fee_type, self.compute_unit_limit))
|
|
||||||
.unwrap_or_default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fee_budget_limits<'a>(
|
|
||||||
instructions: impl Iterator<Item = (&'a Pubkey, &'a CompiledInstruction)>,
|
instructions: impl Iterator<Item = (&'a Pubkey, &'a CompiledInstruction)>,
|
||||||
feature_set: &FeatureSet,
|
feature_set: &FeatureSet,
|
||||||
) -> FeeBudgetLimits {
|
) -> Result<Self> {
|
||||||
let mut compute_budget = Self::default();
|
let compute_budget_limits = process_compute_budget_instructions(instructions, feature_set)?;
|
||||||
|
Ok(ComputeBudget {
|
||||||
let prioritization_fee_details = compute_budget
|
compute_unit_limit: u64::from(compute_budget_limits.compute_unit_limit),
|
||||||
.process_instructions(
|
heap_size: compute_budget_limits.updated_heap_bytes,
|
||||||
instructions,
|
..ComputeBudget::default()
|
||||||
!feature_set.is_active(&remove_deprecated_request_unit_ix::id()),
|
})
|
||||||
feature_set.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
|
|
||||||
)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
FeeBudgetLimits {
|
|
||||||
loaded_accounts_data_size_limit: compute_budget.loaded_accounts_data_size_limit,
|
|
||||||
heap_cost: compute_budget.heap_cost,
|
|
||||||
compute_unit_limit: compute_budget.compute_unit_limit,
|
|
||||||
prioritization_fee: prioritization_fee_details.get_fee(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns cost of the Poseidon hash function for the given number of
|
/// Returns cost of the Poseidon hash function for the given number of
|
||||||
|
@ -350,489 +222,3 @@ impl ComputeBudget {
|
||||||
Some(final_result)
|
Some(final_result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use {
|
|
||||||
super::*,
|
|
||||||
solana_sdk::{
|
|
||||||
hash::Hash,
|
|
||||||
instruction::Instruction,
|
|
||||||
message::Message,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
signature::Keypair,
|
|
||||||
signer::Signer,
|
|
||||||
system_instruction::{self},
|
|
||||||
transaction::{SanitizedTransaction, Transaction},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
macro_rules! test {
|
|
||||||
( $instructions: expr, $expected_result: expr, $expected_budget: expr, $support_set_loaded_accounts_data_size_limit_ix: expr ) => {
|
|
||||||
let payer_keypair = Keypair::new();
|
|
||||||
let tx = SanitizedTransaction::from_transaction_for_tests(Transaction::new(
|
|
||||||
&[&payer_keypair],
|
|
||||||
Message::new($instructions, Some(&payer_keypair.pubkey())),
|
|
||||||
Hash::default(),
|
|
||||||
));
|
|
||||||
let mut compute_budget = ComputeBudget::default();
|
|
||||||
let result = compute_budget.process_instructions(
|
|
||||||
tx.message().program_instructions_iter(),
|
|
||||||
false, /*not support request_units_deprecated*/
|
|
||||||
$support_set_loaded_accounts_data_size_limit_ix,
|
|
||||||
);
|
|
||||||
assert_eq!($expected_result, result);
|
|
||||||
assert_eq!(compute_budget, $expected_budget);
|
|
||||||
};
|
|
||||||
( $instructions: expr, $expected_result: expr, $expected_budget: expr) => {
|
|
||||||
test!($instructions, $expected_result, $expected_budget, false);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_process_instructions() {
|
|
||||||
// Units
|
|
||||||
test!(
|
|
||||||
&[],
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: 0,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
],
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: 1,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT + 1),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
],
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT as u64,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
|
|
||||||
],
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT as u64,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
|
||||||
],
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: 1,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_price(42)
|
|
||||||
],
|
|
||||||
Ok(PrioritizationFeeDetails::new(
|
|
||||||
PrioritizationFeeType::ComputeUnitPrice(42),
|
|
||||||
1
|
|
||||||
)),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: 1,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// HeapFrame
|
|
||||||
test!(
|
|
||||||
&[],
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: 0,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
ComputeBudgetInstruction::request_heap_frame(40 * 1024),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
],
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
|
||||||
heap_size: 40 * 1024,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
ComputeBudgetInstruction::request_heap_frame(40 * 1024 + 1),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
],
|
|
||||||
Err(TransactionError::InstructionError(
|
|
||||||
0,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
)),
|
|
||||||
ComputeBudget::default()
|
|
||||||
);
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
ComputeBudgetInstruction::request_heap_frame(31 * 1024),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
],
|
|
||||||
Err(TransactionError::InstructionError(
|
|
||||||
0,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
)),
|
|
||||||
ComputeBudget::default()
|
|
||||||
);
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES + 1),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
],
|
|
||||||
Err(TransactionError::InstructionError(
|
|
||||||
0,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
)),
|
|
||||||
ComputeBudget::default()
|
|
||||||
);
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
|
||||||
],
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
|
||||||
heap_size: MAX_HEAP_FRAME_BYTES,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
ComputeBudgetInstruction::request_heap_frame(1),
|
|
||||||
],
|
|
||||||
Err(TransactionError::InstructionError(
|
|
||||||
3,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
)),
|
|
||||||
ComputeBudget::default()
|
|
||||||
);
|
|
||||||
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
],
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64 * 7,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Combined
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
|
|
||||||
],
|
|
||||||
Ok(PrioritizationFeeDetails::new(
|
|
||||||
PrioritizationFeeType::ComputeUnitPrice(u64::MAX),
|
|
||||||
MAX_COMPUTE_UNIT_LIMIT as u64,
|
|
||||||
)),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT as u64,
|
|
||||||
heap_size: MAX_HEAP_FRAME_BYTES,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
|
||||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
|
|
||||||
],
|
|
||||||
Ok(PrioritizationFeeDetails::new(
|
|
||||||
PrioritizationFeeType::ComputeUnitPrice(u64::MAX),
|
|
||||||
1
|
|
||||||
)),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: 1,
|
|
||||||
heap_size: MAX_HEAP_FRAME_BYTES,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Duplicates
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT - 1),
|
|
||||||
],
|
|
||||||
Err(TransactionError::DuplicateInstruction(2)),
|
|
||||||
ComputeBudget::default()
|
|
||||||
);
|
|
||||||
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
ComputeBudgetInstruction::request_heap_frame(MIN_HEAP_FRAME_BYTES as u32),
|
|
||||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
|
||||||
],
|
|
||||||
Err(TransactionError::DuplicateInstruction(2)),
|
|
||||||
ComputeBudget::default()
|
|
||||||
);
|
|
||||||
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_price(0),
|
|
||||||
ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
|
|
||||||
],
|
|
||||||
Err(TransactionError::DuplicateInstruction(2)),
|
|
||||||
ComputeBudget::default()
|
|
||||||
);
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
test!(
|
|
||||||
&[Instruction::new_with_borsh(
|
|
||||||
compute_budget::id(),
|
|
||||||
&compute_budget::ComputeBudgetInstruction::RequestUnitsDeprecated {
|
|
||||||
units: 1_000,
|
|
||||||
additional_fee: 10
|
|
||||||
},
|
|
||||||
vec![]
|
|
||||||
)],
|
|
||||||
Err(TransactionError::InstructionError(
|
|
||||||
0,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
)),
|
|
||||||
ComputeBudget::default()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_process_loaded_accounts_data_size_limit_instruction() {
|
|
||||||
// Assert for empty instructions, change value of support_set_loaded_accounts_data_size_limit_ix
|
|
||||||
// will not change results, which should all be default
|
|
||||||
for support_set_loaded_accounts_data_size_limit_ix in [true, false] {
|
|
||||||
test!(
|
|
||||||
&[],
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: 0,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
},
|
|
||||||
support_set_loaded_accounts_data_size_limit_ix
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert when set_loaded_accounts_data_size_limit presents,
|
|
||||||
// if support_set_loaded_accounts_data_size_limit_ix then
|
|
||||||
// budget is set with data_size
|
|
||||||
// else
|
|
||||||
// return InstructionError
|
|
||||||
let data_size: usize = 1;
|
|
||||||
for support_set_loaded_accounts_data_size_limit_ix in [true, false] {
|
|
||||||
let (expected_result, expected_budget) =
|
|
||||||
if support_set_loaded_accounts_data_size_limit_ix {
|
|
||||||
(
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
|
||||||
loaded_accounts_data_size_limit: data_size,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
Err(TransactionError::InstructionError(
|
|
||||||
0,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
)),
|
|
||||||
ComputeBudget::default(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size as u32),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
],
|
|
||||||
expected_result,
|
|
||||||
expected_budget,
|
|
||||||
support_set_loaded_accounts_data_size_limit_ix
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert when set_loaded_accounts_data_size_limit presents, with greater than max value
|
|
||||||
// if support_set_loaded_accounts_data_size_limit_ix then
|
|
||||||
// budget is set to max data size
|
|
||||||
// else
|
|
||||||
// return InstructionError
|
|
||||||
let data_size: usize = MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES + 1;
|
|
||||||
for support_set_loaded_accounts_data_size_limit_ix in [true, false] {
|
|
||||||
let (expected_result, expected_budget) =
|
|
||||||
if support_set_loaded_accounts_data_size_limit_ix {
|
|
||||||
(
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
|
||||||
loaded_accounts_data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
Err(TransactionError::InstructionError(
|
|
||||||
0,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
)),
|
|
||||||
ComputeBudget::default(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size as u32),
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
],
|
|
||||||
expected_result,
|
|
||||||
expected_budget,
|
|
||||||
support_set_loaded_accounts_data_size_limit_ix
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert when set_loaded_accounts_data_size_limit is not presented
|
|
||||||
// if support_set_loaded_accounts_data_size_limit_ix then
|
|
||||||
// budget is set to default data size
|
|
||||||
// else
|
|
||||||
// return
|
|
||||||
for support_set_loaded_accounts_data_size_limit_ix in [true, false] {
|
|
||||||
let (expected_result, expected_budget) = (
|
|
||||||
Ok(PrioritizationFeeDetails::default()),
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
|
||||||
loaded_accounts_data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
test!(
|
|
||||||
&[Instruction::new_with_bincode(
|
|
||||||
Pubkey::new_unique(),
|
|
||||||
&0_u8,
|
|
||||||
vec![]
|
|
||||||
),],
|
|
||||||
expected_result,
|
|
||||||
expected_budget,
|
|
||||||
support_set_loaded_accounts_data_size_limit_ix
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert when set_loaded_accounts_data_size_limit presents more than once,
|
|
||||||
// if support_set_loaded_accounts_data_size_limit_ix then
|
|
||||||
// return DuplicateInstruction
|
|
||||||
// else
|
|
||||||
// return InstructionError
|
|
||||||
let data_size: usize = MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES;
|
|
||||||
for support_set_loaded_accounts_data_size_limit_ix in [true, false] {
|
|
||||||
let (expected_result, expected_budget) =
|
|
||||||
if support_set_loaded_accounts_data_size_limit_ix {
|
|
||||||
(
|
|
||||||
Err(TransactionError::DuplicateInstruction(2)),
|
|
||||||
ComputeBudget::default(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
Err(TransactionError::InstructionError(
|
|
||||||
1,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
)),
|
|
||||||
ComputeBudget::default(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
test!(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size as u32),
|
|
||||||
ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size as u32),
|
|
||||||
],
|
|
||||||
expected_result,
|
|
||||||
expected_budget,
|
|
||||||
support_set_loaded_accounts_data_size_limit_ix
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_process_mixed_instructions_without_compute_budget() {
|
|
||||||
let payer_keypair = Keypair::new();
|
|
||||||
|
|
||||||
let transaction =
|
|
||||||
SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
|
|
||||||
&[
|
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
||||||
system_instruction::transfer(&payer_keypair.pubkey(), &Pubkey::new_unique(), 2),
|
|
||||||
],
|
|
||||||
Some(&payer_keypair.pubkey()),
|
|
||||||
&[&payer_keypair],
|
|
||||||
Hash::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut compute_budget = ComputeBudget::default();
|
|
||||||
let result = compute_budget.process_instructions(
|
|
||||||
transaction.message().program_instructions_iter(),
|
|
||||||
false, //not support request_units_deprecated
|
|
||||||
true, //support_set_loaded_accounts_data_size_limit_ix,
|
|
||||||
);
|
|
||||||
|
|
||||||
// assert process_instructions will be successful with default,
|
|
||||||
assert_eq!(Ok(PrioritizationFeeDetails::default()), result);
|
|
||||||
// assert the default compute_unit_limit is 2 times default: one for bpf ix, one for
|
|
||||||
// builtin ix.
|
|
||||||
assert_eq!(
|
|
||||||
compute_budget,
|
|
||||||
ComputeBudget {
|
|
||||||
compute_unit_limit: 2 * DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
|
||||||
..ComputeBudget::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,619 @@
|
||||||
|
//! Process compute_budget instructions to extract and sanitize limits.
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
compute_budget::DEFAULT_HEAP_COST,
|
||||||
|
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
|
||||||
|
},
|
||||||
|
solana_sdk::{
|
||||||
|
borsh0_10::try_from_slice_unchecked,
|
||||||
|
compute_budget::{self, ComputeBudgetInstruction},
|
||||||
|
entrypoint::HEAP_LENGTH as MIN_HEAP_FRAME_BYTES,
|
||||||
|
feature_set::{
|
||||||
|
add_set_tx_loaded_accounts_data_size_instruction, remove_deprecated_request_unit_ix,
|
||||||
|
FeatureSet,
|
||||||
|
},
|
||||||
|
fee::FeeBudgetLimits,
|
||||||
|
instruction::{CompiledInstruction, InstructionError},
|
||||||
|
pubkey::Pubkey,
|
||||||
|
transaction::TransactionError,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_HEAP_FRAME_BYTES: u32 = 256 * 1024;
|
||||||
|
pub const DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT: u32 = 200_000;
|
||||||
|
pub const MAX_COMPUTE_UNIT_LIMIT: u32 = 1_400_000;
|
||||||
|
|
||||||
|
/// The total accounts data a transaction can load is limited to 64MiB to not break
|
||||||
|
/// anyone in Mainnet-beta today. It can be set by set_loaded_accounts_data_size_limit instruction
|
||||||
|
pub const MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES: u32 = 64 * 1024 * 1024;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct ComputeBudgetLimits {
|
||||||
|
pub updated_heap_bytes: u32,
|
||||||
|
pub compute_unit_limit: u32,
|
||||||
|
pub compute_unit_price: u64,
|
||||||
|
pub loaded_accounts_bytes: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ComputeBudgetLimits {
|
||||||
|
fn default() -> Self {
|
||||||
|
ComputeBudgetLimits {
|
||||||
|
updated_heap_bytes: u32::try_from(MIN_HEAP_FRAME_BYTES).unwrap(),
|
||||||
|
compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT,
|
||||||
|
compute_unit_price: 0,
|
||||||
|
loaded_accounts_bytes: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ComputeBudgetLimits> for FeeBudgetLimits {
|
||||||
|
fn from(val: ComputeBudgetLimits) -> Self {
|
||||||
|
let prioritization_fee_details = PrioritizationFeeDetails::new(
|
||||||
|
PrioritizationFeeType::ComputeUnitPrice(val.compute_unit_price),
|
||||||
|
u64::from(val.compute_unit_limit),
|
||||||
|
);
|
||||||
|
FeeBudgetLimits {
|
||||||
|
// NOTE - usize::from(u32).unwrap() may fail if target is 16-bit and
|
||||||
|
// `loaded_accounts_bytes` is greater than u16::MAX. In that case, panic is proper.
|
||||||
|
loaded_accounts_data_size_limit: usize::try_from(val.loaded_accounts_bytes).unwrap(),
|
||||||
|
heap_cost: DEFAULT_HEAP_COST,
|
||||||
|
compute_unit_limit: u64::from(val.compute_unit_limit),
|
||||||
|
prioritization_fee: prioritization_fee_details.get_fee(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processing compute_budget could be part of tx sanitizing, failed to process
|
||||||
|
/// these instructions will drop the transaction eventually without execution,
|
||||||
|
/// may as well fail it early.
|
||||||
|
/// If succeeded, the transaction's specific limits/requests (could be default)
|
||||||
|
/// are retrieved and returned,
|
||||||
|
pub fn process_compute_budget_instructions<'a>(
|
||||||
|
instructions: impl Iterator<Item = (&'a Pubkey, &'a CompiledInstruction)>,
|
||||||
|
feature_set: &FeatureSet,
|
||||||
|
) -> Result<ComputeBudgetLimits, TransactionError> {
|
||||||
|
let support_request_units_deprecated =
|
||||||
|
!feature_set.is_active(&remove_deprecated_request_unit_ix::id());
|
||||||
|
let support_set_loaded_accounts_data_size_limit_ix =
|
||||||
|
feature_set.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id());
|
||||||
|
|
||||||
|
let mut num_non_compute_budget_instructions: u32 = 0;
|
||||||
|
let mut updated_compute_unit_limit = None;
|
||||||
|
let mut updated_compute_unit_price = None;
|
||||||
|
let mut requested_heap_size = None;
|
||||||
|
let mut updated_loaded_accounts_data_size_limit = None;
|
||||||
|
|
||||||
|
for (i, (program_id, instruction)) in instructions.enumerate() {
|
||||||
|
if compute_budget::check_id(program_id) {
|
||||||
|
let invalid_instruction_data_error = TransactionError::InstructionError(
|
||||||
|
i as u8,
|
||||||
|
InstructionError::InvalidInstructionData,
|
||||||
|
);
|
||||||
|
let duplicate_instruction_error = TransactionError::DuplicateInstruction(i as u8);
|
||||||
|
|
||||||
|
match try_from_slice_unchecked(&instruction.data) {
|
||||||
|
Ok(ComputeBudgetInstruction::RequestUnitsDeprecated {
|
||||||
|
units: compute_unit_limit,
|
||||||
|
additional_fee,
|
||||||
|
}) if support_request_units_deprecated => {
|
||||||
|
if updated_compute_unit_limit.is_some() {
|
||||||
|
return Err(duplicate_instruction_error);
|
||||||
|
}
|
||||||
|
if updated_compute_unit_price.is_some() {
|
||||||
|
return Err(duplicate_instruction_error);
|
||||||
|
}
|
||||||
|
updated_compute_unit_limit = Some(compute_unit_limit);
|
||||||
|
updated_compute_unit_price =
|
||||||
|
support_deprecated_requested_units(additional_fee, compute_unit_limit);
|
||||||
|
}
|
||||||
|
Ok(ComputeBudgetInstruction::RequestHeapFrame(bytes)) => {
|
||||||
|
if requested_heap_size.is_some() {
|
||||||
|
return Err(duplicate_instruction_error);
|
||||||
|
}
|
||||||
|
if sanitize_requested_heap_size(bytes) {
|
||||||
|
requested_heap_size = Some(bytes);
|
||||||
|
} else {
|
||||||
|
return Err(invalid_instruction_data_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ComputeBudgetInstruction::SetComputeUnitLimit(compute_unit_limit)) => {
|
||||||
|
if updated_compute_unit_limit.is_some() {
|
||||||
|
return Err(duplicate_instruction_error);
|
||||||
|
}
|
||||||
|
updated_compute_unit_limit = Some(compute_unit_limit);
|
||||||
|
}
|
||||||
|
Ok(ComputeBudgetInstruction::SetComputeUnitPrice(micro_lamports)) => {
|
||||||
|
if updated_compute_unit_price.is_some() {
|
||||||
|
return Err(duplicate_instruction_error);
|
||||||
|
}
|
||||||
|
updated_compute_unit_price = Some(micro_lamports);
|
||||||
|
}
|
||||||
|
Ok(ComputeBudgetInstruction::SetLoadedAccountsDataSizeLimit(bytes))
|
||||||
|
if support_set_loaded_accounts_data_size_limit_ix =>
|
||||||
|
{
|
||||||
|
if updated_loaded_accounts_data_size_limit.is_some() {
|
||||||
|
return Err(duplicate_instruction_error);
|
||||||
|
}
|
||||||
|
updated_loaded_accounts_data_size_limit = Some(bytes);
|
||||||
|
}
|
||||||
|
_ => return Err(invalid_instruction_data_error),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// only include non-request instructions in default max calc
|
||||||
|
num_non_compute_budget_instructions =
|
||||||
|
num_non_compute_budget_instructions.saturating_add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitize limits
|
||||||
|
let updated_heap_bytes = requested_heap_size
|
||||||
|
.unwrap_or(u32::try_from(MIN_HEAP_FRAME_BYTES).unwrap()) // loader's default heap_size
|
||||||
|
.min(MAX_HEAP_FRAME_BYTES);
|
||||||
|
|
||||||
|
let compute_unit_limit = updated_compute_unit_limit
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
num_non_compute_budget_instructions
|
||||||
|
.saturating_mul(DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT)
|
||||||
|
})
|
||||||
|
.min(MAX_COMPUTE_UNIT_LIMIT);
|
||||||
|
|
||||||
|
let compute_unit_price = updated_compute_unit_price.unwrap_or(0);
|
||||||
|
|
||||||
|
let loaded_accounts_bytes = updated_loaded_accounts_data_size_limit
|
||||||
|
.unwrap_or(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES)
|
||||||
|
.min(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES);
|
||||||
|
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
updated_heap_bytes,
|
||||||
|
compute_unit_limit,
|
||||||
|
compute_unit_price,
|
||||||
|
loaded_accounts_bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sanitize_requested_heap_size(bytes: u32) -> bool {
|
||||||
|
(u32::try_from(MIN_HEAP_FRAME_BYTES).unwrap()..=MAX_HEAP_FRAME_BYTES).contains(&bytes)
|
||||||
|
&& bytes % 1024 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supports request_units_derpecated ix, returns cu_price if available.
|
||||||
|
fn support_deprecated_requested_units(additional_fee: u32, compute_unit_limit: u32) -> Option<u64> {
|
||||||
|
// TODO: remove support of 'Deprecated' after feature remove_deprecated_request_unit_ix::id() is activated
|
||||||
|
const MICRO_LAMPORTS_PER_LAMPORT: u64 = 1_000_000;
|
||||||
|
|
||||||
|
let micro_lamport_fee =
|
||||||
|
(additional_fee as u128).saturating_mul(MICRO_LAMPORTS_PER_LAMPORT as u128);
|
||||||
|
micro_lamport_fee
|
||||||
|
.checked_div(compute_unit_limit as u128)
|
||||||
|
.map(|cu_price| u64::try_from(cu_price).unwrap_or(u64::MAX))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use {
|
||||||
|
super::*,
|
||||||
|
solana_sdk::{
|
||||||
|
hash::Hash,
|
||||||
|
instruction::Instruction,
|
||||||
|
message::Message,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
signature::Keypair,
|
||||||
|
signer::Signer,
|
||||||
|
system_instruction::{self},
|
||||||
|
transaction::{SanitizedTransaction, Transaction},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! test {
|
||||||
|
( $instructions: expr, $expected_result: expr, $support_set_loaded_accounts_data_size_limit_ix: expr ) => {
|
||||||
|
let payer_keypair = Keypair::new();
|
||||||
|
let tx = SanitizedTransaction::from_transaction_for_tests(Transaction::new(
|
||||||
|
&[&payer_keypair],
|
||||||
|
Message::new($instructions, Some(&payer_keypair.pubkey())),
|
||||||
|
Hash::default(),
|
||||||
|
));
|
||||||
|
let mut feature_set = FeatureSet::default();
|
||||||
|
feature_set.activate(&remove_deprecated_request_unit_ix::id(), 0);
|
||||||
|
if $support_set_loaded_accounts_data_size_limit_ix {
|
||||||
|
feature_set.activate(&add_set_tx_loaded_accounts_data_size_instruction::id(), 0);
|
||||||
|
}
|
||||||
|
let result = process_compute_budget_instructions(
|
||||||
|
tx.message().program_instructions_iter(),
|
||||||
|
&feature_set,
|
||||||
|
);
|
||||||
|
assert_eq!($expected_result, result);
|
||||||
|
};
|
||||||
|
( $instructions: expr, $expected_result: expr ) => {
|
||||||
|
test!($instructions, $expected_result, false);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_instructions() {
|
||||||
|
// Units
|
||||||
|
test!(
|
||||||
|
&[],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: 0,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: 1,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT + 1),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
|
||||||
|
],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
||||||
|
],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: 1,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_price(42)
|
||||||
|
],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: 1,
|
||||||
|
compute_unit_price: 42,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// HeapFrame
|
||||||
|
test!(
|
||||||
|
&[],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: 0,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
ComputeBudgetInstruction::request_heap_frame(40 * 1024),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||||
|
updated_heap_bytes: 40 * 1024,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
ComputeBudgetInstruction::request_heap_frame(40 * 1024 + 1),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
],
|
||||||
|
Err(TransactionError::InstructionError(
|
||||||
|
0,
|
||||||
|
InstructionError::InvalidInstructionData,
|
||||||
|
))
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
ComputeBudgetInstruction::request_heap_frame(31 * 1024),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
],
|
||||||
|
Err(TransactionError::InstructionError(
|
||||||
|
0,
|
||||||
|
InstructionError::InvalidInstructionData,
|
||||||
|
))
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES + 1),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
],
|
||||||
|
Err(TransactionError::InstructionError(
|
||||||
|
0,
|
||||||
|
InstructionError::InvalidInstructionData,
|
||||||
|
))
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
||||||
|
],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||||
|
updated_heap_bytes: MAX_HEAP_FRAME_BYTES,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
ComputeBudgetInstruction::request_heap_frame(1),
|
||||||
|
],
|
||||||
|
Err(TransactionError::InstructionError(
|
||||||
|
3,
|
||||||
|
InstructionError::InvalidInstructionData,
|
||||||
|
))
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT * 7,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Combined
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
|
||||||
|
],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_price: u64::MAX,
|
||||||
|
compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT,
|
||||||
|
updated_heap_bytes: MAX_HEAP_FRAME_BYTES,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
||||||
|
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
|
||||||
|
],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_price: u64::MAX,
|
||||||
|
compute_unit_limit: 1,
|
||||||
|
updated_heap_bytes: MAX_HEAP_FRAME_BYTES,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Duplicates
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT - 1),
|
||||||
|
],
|
||||||
|
Err(TransactionError::DuplicateInstruction(2))
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
ComputeBudgetInstruction::request_heap_frame(MIN_HEAP_FRAME_BYTES as u32),
|
||||||
|
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
||||||
|
],
|
||||||
|
Err(TransactionError::DuplicateInstruction(2))
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_price(0),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
|
||||||
|
],
|
||||||
|
Err(TransactionError::DuplicateInstruction(2))
|
||||||
|
);
|
||||||
|
|
||||||
|
// deprecated
|
||||||
|
test!(
|
||||||
|
&[Instruction::new_with_borsh(
|
||||||
|
compute_budget::id(),
|
||||||
|
&compute_budget::ComputeBudgetInstruction::RequestUnitsDeprecated {
|
||||||
|
units: 1_000,
|
||||||
|
additional_fee: 10
|
||||||
|
},
|
||||||
|
vec![]
|
||||||
|
)],
|
||||||
|
Err(TransactionError::InstructionError(
|
||||||
|
0,
|
||||||
|
InstructionError::InvalidInstructionData,
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_loaded_accounts_data_size_limit_instruction() {
|
||||||
|
// Assert for empty instructions, change value of support_set_loaded_accounts_data_size_limit_ix
|
||||||
|
// will not change results, which should all be default
|
||||||
|
for support_set_loaded_accounts_data_size_limit_ix in [true, false] {
|
||||||
|
test!(
|
||||||
|
&[],
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: 0,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
}),
|
||||||
|
support_set_loaded_accounts_data_size_limit_ix
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert when set_loaded_accounts_data_size_limit presents,
|
||||||
|
// if support_set_loaded_accounts_data_size_limit_ix then
|
||||||
|
// budget is set with data_size
|
||||||
|
// else
|
||||||
|
// return InstructionError
|
||||||
|
let data_size = 1;
|
||||||
|
for support_set_loaded_accounts_data_size_limit_ix in [true, false] {
|
||||||
|
let expected_result = if support_set_loaded_accounts_data_size_limit_ix {
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||||
|
loaded_accounts_bytes: data_size,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(TransactionError::InstructionError(
|
||||||
|
0,
|
||||||
|
InstructionError::InvalidInstructionData,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
],
|
||||||
|
expected_result,
|
||||||
|
support_set_loaded_accounts_data_size_limit_ix
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert when set_loaded_accounts_data_size_limit presents, with greater than max value
|
||||||
|
// if support_set_loaded_accounts_data_size_limit_ix then
|
||||||
|
// budget is set to max data size
|
||||||
|
// else
|
||||||
|
// return InstructionError
|
||||||
|
let data_size = MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES + 1;
|
||||||
|
for support_set_loaded_accounts_data_size_limit_ix in [true, false] {
|
||||||
|
let expected_result = if support_set_loaded_accounts_data_size_limit_ix {
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||||
|
loaded_accounts_bytes: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(TransactionError::InstructionError(
|
||||||
|
0,
|
||||||
|
InstructionError::InvalidInstructionData,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size),
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
],
|
||||||
|
expected_result,
|
||||||
|
support_set_loaded_accounts_data_size_limit_ix
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert when set_loaded_accounts_data_size_limit is not presented
|
||||||
|
// if support_set_loaded_accounts_data_size_limit_ix then
|
||||||
|
// budget is set to default data size
|
||||||
|
// else
|
||||||
|
// return
|
||||||
|
for support_set_loaded_accounts_data_size_limit_ix in [true, false] {
|
||||||
|
let expected_result = Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||||
|
loaded_accounts_bytes: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
test!(
|
||||||
|
&[Instruction::new_with_bincode(
|
||||||
|
Pubkey::new_unique(),
|
||||||
|
&0_u8,
|
||||||
|
vec![]
|
||||||
|
),],
|
||||||
|
expected_result,
|
||||||
|
support_set_loaded_accounts_data_size_limit_ix
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert when set_loaded_accounts_data_size_limit presents more than once,
|
||||||
|
// if support_set_loaded_accounts_data_size_limit_ix then
|
||||||
|
// return DuplicateInstruction
|
||||||
|
// else
|
||||||
|
// return InstructionError
|
||||||
|
let data_size = MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES;
|
||||||
|
for support_set_loaded_accounts_data_size_limit_ix in [true, false] {
|
||||||
|
let expected_result = if support_set_loaded_accounts_data_size_limit_ix {
|
||||||
|
Err(TransactionError::DuplicateInstruction(2))
|
||||||
|
} else {
|
||||||
|
Err(TransactionError::InstructionError(
|
||||||
|
1,
|
||||||
|
InstructionError::InvalidInstructionData,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
test!(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size),
|
||||||
|
ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size),
|
||||||
|
],
|
||||||
|
expected_result,
|
||||||
|
support_set_loaded_accounts_data_size_limit_ix
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_mixed_instructions_without_compute_budget() {
|
||||||
|
let payer_keypair = Keypair::new();
|
||||||
|
|
||||||
|
let transaction =
|
||||||
|
SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
|
||||||
|
&[
|
||||||
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
||||||
|
system_instruction::transfer(&payer_keypair.pubkey(), &Pubkey::new_unique(), 2),
|
||||||
|
],
|
||||||
|
Some(&payer_keypair.pubkey()),
|
||||||
|
&[&payer_keypair],
|
||||||
|
Hash::default(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut feature_set = FeatureSet::default();
|
||||||
|
feature_set.activate(&remove_deprecated_request_unit_ix::id(), 0);
|
||||||
|
feature_set.activate(&add_set_tx_loaded_accounts_data_size_instruction::id(), 0);
|
||||||
|
|
||||||
|
let result = process_compute_budget_instructions(
|
||||||
|
transaction.message().program_instructions_iter(),
|
||||||
|
&feature_set,
|
||||||
|
);
|
||||||
|
|
||||||
|
// assert process_instructions will be successful with default,
|
||||||
|
// and the default compute_unit_limit is 2 times default: one for bpf ix, one for
|
||||||
|
// builtin ix.
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
Ok(ComputeBudgetLimits {
|
||||||
|
compute_unit_limit: 2 * DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||||
|
..ComputeBudgetLimits::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -756,7 +756,7 @@ pub fn mock_process_instruction<F: FnMut(&mut InvokeContext), G: FnMut(&mut Invo
|
||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
crate::compute_budget,
|
crate::compute_budget_processor,
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
solana_sdk::{account::WritableAccount, instruction::Instruction, rent::Rent},
|
solana_sdk::{account::WritableAccount, instruction::Instruction, rent::Rent},
|
||||||
};
|
};
|
||||||
|
@ -1084,8 +1084,9 @@ mod tests {
|
||||||
vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())];
|
vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())];
|
||||||
|
|
||||||
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
|
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
|
||||||
invoke_context.compute_budget =
|
invoke_context.compute_budget = ComputeBudget::new(
|
||||||
ComputeBudget::new(compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64);
|
compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
||||||
|
);
|
||||||
|
|
||||||
invoke_context
|
invoke_context
|
||||||
.transaction_context
|
.transaction_context
|
||||||
|
@ -1095,7 +1096,9 @@ mod tests {
|
||||||
invoke_context.push().unwrap();
|
invoke_context.push().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*invoke_context.get_compute_budget(),
|
*invoke_context.get_compute_budget(),
|
||||||
ComputeBudget::new(compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64)
|
ComputeBudget::new(
|
||||||
|
compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64
|
||||||
|
)
|
||||||
);
|
);
|
||||||
invoke_context.pop().unwrap();
|
invoke_context.pop().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ extern crate solana_metrics;
|
||||||
pub use solana_rbpf;
|
pub use solana_rbpf;
|
||||||
pub mod accounts_data_meter;
|
pub mod accounts_data_meter;
|
||||||
pub mod compute_budget;
|
pub mod compute_budget;
|
||||||
|
pub mod compute_budget_processor;
|
||||||
pub mod invoke_context;
|
pub mod invoke_context;
|
||||||
pub mod loaded_programs;
|
pub mod loaded_programs;
|
||||||
pub mod log_collector;
|
pub mod log_collector;
|
||||||
|
|
|
@ -20,7 +20,10 @@ use {
|
||||||
TransactionResults,
|
TransactionResults,
|
||||||
},
|
},
|
||||||
solana_ledger::token_balances::collect_token_balances,
|
solana_ledger::token_balances::collect_token_balances,
|
||||||
solana_program_runtime::{compute_budget::ComputeBudget, timings::ExecuteTimings},
|
solana_program_runtime::{
|
||||||
|
compute_budget::ComputeBudget,
|
||||||
|
compute_budget_processor::process_compute_budget_instructions, timings::ExecuteTimings,
|
||||||
|
},
|
||||||
solana_rbpf::vm::ContextObject,
|
solana_rbpf::vm::ContextObject,
|
||||||
solana_runtime::{
|
solana_runtime::{
|
||||||
bank::TransactionBalancesSet,
|
bank::TransactionBalancesSet,
|
||||||
|
@ -3835,10 +3838,12 @@ fn test_program_fees() {
|
||||||
let expected_normal_fee = fee_structure.calculate_fee(
|
let expected_normal_fee = fee_structure.calculate_fee(
|
||||||
&sanitized_message,
|
&sanitized_message,
|
||||||
congestion_multiplier,
|
congestion_multiplier,
|
||||||
&ComputeBudget::fee_budget_limits(
|
&process_compute_budget_instructions(
|
||||||
sanitized_message.program_instructions_iter(),
|
sanitized_message.program_instructions_iter(),
|
||||||
&feature_set,
|
&feature_set,
|
||||||
),
|
)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into(),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
@ -3862,10 +3867,12 @@ fn test_program_fees() {
|
||||||
let expected_prioritized_fee = fee_structure.calculate_fee(
|
let expected_prioritized_fee = fee_structure.calculate_fee(
|
||||||
&sanitized_message,
|
&sanitized_message,
|
||||||
congestion_multiplier,
|
congestion_multiplier,
|
||||||
&ComputeBudget::fee_budget_limits(
|
&process_compute_budget_instructions(
|
||||||
sanitized_message.program_instructions_iter(),
|
sanitized_message.program_instructions_iter(),
|
||||||
&feature_set,
|
&feature_set,
|
||||||
),
|
)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into(),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
|
@ -105,7 +105,8 @@ use {
|
||||||
solana_perf::perf_libs,
|
solana_perf::perf_libs,
|
||||||
solana_program_runtime::{
|
solana_program_runtime::{
|
||||||
accounts_data_meter::MAX_ACCOUNTS_DATA_LEN,
|
accounts_data_meter::MAX_ACCOUNTS_DATA_LEN,
|
||||||
compute_budget::{self, ComputeBudget},
|
compute_budget::ComputeBudget,
|
||||||
|
compute_budget_processor::process_compute_budget_instructions,
|
||||||
invoke_context::ProcessInstructionWithContext,
|
invoke_context::ProcessInstructionWithContext,
|
||||||
loaded_programs::{
|
loaded_programs::{
|
||||||
LoadProgramMetrics, LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType,
|
LoadProgramMetrics, LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType,
|
||||||
|
@ -135,10 +136,8 @@ use {
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
feature,
|
feature,
|
||||||
feature_set::{
|
feature_set::{
|
||||||
self, add_set_tx_loaded_accounts_data_size_instruction,
|
self, include_loaded_accounts_data_size_in_fee_calculation,
|
||||||
include_loaded_accounts_data_size_in_fee_calculation,
|
remove_congestion_multiplier_from_fee_calculation, FeatureSet,
|
||||||
remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix,
|
|
||||||
FeatureSet,
|
|
||||||
},
|
},
|
||||||
fee::FeeStructure,
|
fee::FeeStructure,
|
||||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
|
@ -4102,10 +4101,12 @@ impl Bank {
|
||||||
self.fee_structure.calculate_fee(
|
self.fee_structure.calculate_fee(
|
||||||
message,
|
message,
|
||||||
lamports_per_signature,
|
lamports_per_signature,
|
||||||
&ComputeBudget::fee_budget_limits(
|
&process_compute_budget_instructions(
|
||||||
message.program_instructions_iter(),
|
message.program_instructions_iter(),
|
||||||
&self.feature_set,
|
&self.feature_set,
|
||||||
),
|
)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into(),
|
||||||
self.feature_set
|
self.feature_set
|
||||||
.is_active(&remove_congestion_multiplier_from_fee_calculation::id()),
|
.is_active(&remove_congestion_multiplier_from_fee_calculation::id()),
|
||||||
self.feature_set
|
self.feature_set
|
||||||
|
@ -5191,36 +5192,28 @@ impl Bank {
|
||||||
.map(|(accs, tx)| match accs {
|
.map(|(accs, tx)| match accs {
|
||||||
(Err(e), _nonce) => TransactionExecutionResult::NotExecuted(e.clone()),
|
(Err(e), _nonce) => TransactionExecutionResult::NotExecuted(e.clone()),
|
||||||
(Ok(loaded_transaction), nonce) => {
|
(Ok(loaded_transaction), nonce) => {
|
||||||
let compute_budget = if let Some(compute_budget) =
|
let compute_budget =
|
||||||
self.runtime_config.compute_budget
|
if let Some(compute_budget) = self.runtime_config.compute_budget {
|
||||||
{
|
compute_budget
|
||||||
compute_budget
|
} else {
|
||||||
} else {
|
let mut compute_budget_process_transaction_time =
|
||||||
let mut compute_budget =
|
Measure::start("compute_budget_process_transaction_time");
|
||||||
ComputeBudget::new(compute_budget::MAX_COMPUTE_UNIT_LIMIT as u64);
|
let maybe_compute_budget = ComputeBudget::try_from_instructions(
|
||||||
|
tx.message().program_instructions_iter(),
|
||||||
let mut compute_budget_process_transaction_time =
|
&self.feature_set,
|
||||||
Measure::start("compute_budget_process_transaction_time");
|
);
|
||||||
let process_transaction_result = compute_budget.process_instructions(
|
compute_budget_process_transaction_time.stop();
|
||||||
tx.message().program_instructions_iter(),
|
saturating_add_assign!(
|
||||||
!self
|
timings
|
||||||
.feature_set
|
.execute_accessories
|
||||||
.is_active(&remove_deprecated_request_unit_ix::id()),
|
.compute_budget_process_transaction_us,
|
||||||
self.feature_set
|
compute_budget_process_transaction_time.as_us()
|
||||||
.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
|
);
|
||||||
);
|
if let Err(err) = maybe_compute_budget {
|
||||||
compute_budget_process_transaction_time.stop();
|
return TransactionExecutionResult::NotExecuted(err);
|
||||||
saturating_add_assign!(
|
}
|
||||||
timings
|
maybe_compute_budget.unwrap()
|
||||||
.execute_accessories
|
};
|
||||||
.compute_budget_process_transaction_us,
|
|
||||||
compute_budget_process_transaction_time.as_us()
|
|
||||||
);
|
|
||||||
if let Err(err) = process_transaction_result {
|
|
||||||
return TransactionExecutionResult::NotExecuted(err);
|
|
||||||
}
|
|
||||||
compute_budget
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = self.execute_loaded_transaction(
|
let result = self.execute_loaded_transaction(
|
||||||
tx,
|
tx,
|
||||||
|
|
|
@ -46,7 +46,8 @@ use {
|
||||||
},
|
},
|
||||||
solana_logger,
|
solana_logger,
|
||||||
solana_program_runtime::{
|
solana_program_runtime::{
|
||||||
compute_budget::{self, ComputeBudget, MAX_COMPUTE_UNIT_LIMIT},
|
compute_budget::ComputeBudget,
|
||||||
|
compute_budget_processor::{self, MAX_COMPUTE_UNIT_LIMIT},
|
||||||
declare_process_instruction,
|
declare_process_instruction,
|
||||||
invoke_context::mock_process_instruction,
|
invoke_context::mock_process_instruction,
|
||||||
loaded_programs::{LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET},
|
loaded_programs::{LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET},
|
||||||
|
@ -10120,7 +10121,9 @@ fn test_compute_budget_program_noop() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*compute_budget,
|
*compute_budget,
|
||||||
ComputeBudget {
|
ComputeBudget {
|
||||||
compute_unit_limit: compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
compute_unit_limit: u64::from(
|
||||||
|
compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
||||||
|
),
|
||||||
heap_size: 48 * 1024,
|
heap_size: 48 * 1024,
|
||||||
..ComputeBudget::default()
|
..ComputeBudget::default()
|
||||||
}
|
}
|
||||||
|
@ -10133,7 +10136,7 @@ fn test_compute_budget_program_noop() {
|
||||||
let message = Message::new(
|
let message = Message::new(
|
||||||
&[
|
&[
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(
|
ComputeBudgetInstruction::set_compute_unit_limit(
|
||||||
compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||||
),
|
),
|
||||||
ComputeBudgetInstruction::request_heap_frame(48 * 1024),
|
ComputeBudgetInstruction::request_heap_frame(48 * 1024),
|
||||||
Instruction::new_with_bincode(program_id, &0, vec![]),
|
Instruction::new_with_bincode(program_id, &0, vec![]),
|
||||||
|
@ -10163,7 +10166,9 @@ fn test_compute_request_instruction() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*compute_budget,
|
*compute_budget,
|
||||||
ComputeBudget {
|
ComputeBudget {
|
||||||
compute_unit_limit: compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
compute_unit_limit: u64::from(
|
||||||
|
compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
||||||
|
),
|
||||||
heap_size: 48 * 1024,
|
heap_size: 48 * 1024,
|
||||||
..ComputeBudget::default()
|
..ComputeBudget::default()
|
||||||
}
|
}
|
||||||
|
@ -10176,7 +10181,7 @@ fn test_compute_request_instruction() {
|
||||||
let message = Message::new(
|
let message = Message::new(
|
||||||
&[
|
&[
|
||||||
ComputeBudgetInstruction::set_compute_unit_limit(
|
ComputeBudgetInstruction::set_compute_unit_limit(
|
||||||
compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||||
),
|
),
|
||||||
ComputeBudgetInstruction::request_heap_frame(48 * 1024),
|
ComputeBudgetInstruction::request_heap_frame(48 * 1024),
|
||||||
Instruction::new_with_bincode(program_id, &0, vec![]),
|
Instruction::new_with_bincode(program_id, &0, vec![]),
|
||||||
|
@ -10213,7 +10218,9 @@ fn test_failed_compute_request_instruction() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*compute_budget,
|
*compute_budget,
|
||||||
ComputeBudget {
|
ComputeBudget {
|
||||||
compute_unit_limit: compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
compute_unit_limit: u64::from(
|
||||||
|
compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
||||||
|
),
|
||||||
heap_size: 48 * 1024,
|
heap_size: 48 * 1024,
|
||||||
..ComputeBudget::default()
|
..ComputeBudget::default()
|
||||||
}
|
}
|
||||||
|
@ -10444,14 +10451,19 @@ fn calculate_test_fee(
|
||||||
remove_congestion_multiplier: bool,
|
remove_congestion_multiplier: bool,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let mut feature_set = FeatureSet::all_enabled();
|
let mut feature_set = FeatureSet::all_enabled();
|
||||||
feature_set.deactivate(&remove_deprecated_request_unit_ix::id());
|
feature_set.deactivate(&solana_sdk::feature_set::remove_deprecated_request_unit_ix::id());
|
||||||
|
|
||||||
if !support_set_accounts_data_size_limit_ix {
|
if !support_set_accounts_data_size_limit_ix {
|
||||||
feature_set.deactivate(&include_loaded_accounts_data_size_in_fee_calculation::id());
|
feature_set.deactivate(
|
||||||
|
&solana_sdk::feature_set::include_loaded_accounts_data_size_in_fee_calculation::id(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let budget_limits =
|
let budget_limits =
|
||||||
ComputeBudget::fee_budget_limits(message.program_instructions_iter(), &feature_set);
|
process_compute_budget_instructions(message.program_instructions_iter(), &feature_set)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into();
|
||||||
|
|
||||||
fee_structure.calculate_fee(
|
fee_structure.calculate_fee(
|
||||||
message,
|
message,
|
||||||
lamports_per_signature,
|
lamports_per_signature,
|
||||||
|
@ -11478,7 +11490,9 @@ fn test_rent_state_list_len() {
|
||||||
);
|
);
|
||||||
|
|
||||||
let compute_budget = bank.runtime_config.compute_budget.unwrap_or_else(|| {
|
let compute_budget = bank.runtime_config.compute_budget.unwrap_or_else(|| {
|
||||||
ComputeBudget::new(compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64)
|
ComputeBudget::new(u64::from(
|
||||||
|
compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||||
|
))
|
||||||
});
|
});
|
||||||
let transaction_context = TransactionContext::new(
|
let transaction_context = TransactionContext::new(
|
||||||
loaded_txs[0].0.as_ref().unwrap().accounts.clone(),
|
loaded_txs[0].0.as_ref().unwrap().accounts.clone(),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
solana_program_runtime::compute_budget::ComputeBudget,
|
solana_program_runtime::compute_budget_processor::process_compute_budget_instructions,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
|
feature_set::FeatureSet,
|
||||||
instruction::CompiledInstruction,
|
instruction::CompiledInstruction,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
transaction::{SanitizedTransaction, SanitizedVersionedTransaction},
|
transaction::{SanitizedTransaction, SanitizedVersionedTransaction},
|
||||||
|
@ -23,18 +24,17 @@ pub trait GetTransactionPriorityDetails {
|
||||||
instructions: impl Iterator<Item = (&'a Pubkey, &'a CompiledInstruction)>,
|
instructions: impl Iterator<Item = (&'a Pubkey, &'a CompiledInstruction)>,
|
||||||
_round_compute_unit_price_enabled: bool,
|
_round_compute_unit_price_enabled: bool,
|
||||||
) -> Option<TransactionPriorityDetails> {
|
) -> Option<TransactionPriorityDetails> {
|
||||||
let mut compute_budget = ComputeBudget::default();
|
let mut feature_set = FeatureSet::default();
|
||||||
let prioritization_fee_details = compute_budget
|
feature_set.activate(
|
||||||
.process_instructions(
|
&solana_sdk::feature_set::add_set_tx_loaded_accounts_data_size_instruction::id(),
|
||||||
instructions,
|
0,
|
||||||
true, // supports prioritization by request_units_deprecated instruction
|
);
|
||||||
true, // enable support set accounts data size instruction
|
|
||||||
// TODO: round_compute_unit_price_enabled: bool
|
let compute_budget_limits =
|
||||||
)
|
process_compute_budget_instructions(instructions, &feature_set).ok()?;
|
||||||
.ok()?;
|
|
||||||
Some(TransactionPriorityDetails {
|
Some(TransactionPriorityDetails {
|
||||||
priority: prioritization_fee_details.get_priority(),
|
priority: compute_budget_limits.compute_unit_price,
|
||||||
compute_unit_limit: compute_budget.compute_unit_limit,
|
compute_unit_limit: u64::from(compute_budget_limits.compute_unit_limit),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,8 +98,8 @@ mod tests {
|
||||||
Some(TransactionPriorityDetails {
|
Some(TransactionPriorityDetails {
|
||||||
priority: 0,
|
priority: 0,
|
||||||
compute_unit_limit:
|
compute_unit_limit:
|
||||||
solana_program_runtime::compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
solana_program_runtime::compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
||||||
as u64
|
as u64,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -111,8 +111,8 @@ mod tests {
|
||||||
Some(TransactionPriorityDetails {
|
Some(TransactionPriorityDetails {
|
||||||
priority: 0,
|
priority: 0,
|
||||||
compute_unit_limit:
|
compute_unit_limit:
|
||||||
solana_program_runtime::compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
solana_program_runtime::compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
||||||
as u64
|
as u64,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -174,8 +174,8 @@ mod tests {
|
||||||
Some(TransactionPriorityDetails {
|
Some(TransactionPriorityDetails {
|
||||||
priority: requested_price,
|
priority: requested_price,
|
||||||
compute_unit_limit:
|
compute_unit_limit:
|
||||||
solana_program_runtime::compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
solana_program_runtime::compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
||||||
as u64
|
as u64,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -187,8 +187,8 @@ mod tests {
|
||||||
Some(TransactionPriorityDetails {
|
Some(TransactionPriorityDetails {
|
||||||
priority: requested_price,
|
priority: requested_price,
|
||||||
compute_unit_limit:
|
compute_unit_limit:
|
||||||
solana_program_runtime::compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
solana_program_runtime::compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
|
||||||
as u64
|
as u64,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue