Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 510b6b9

Browse files
authored
Split compute budget instructions process from struct (#33852)
* Split compute budget instruction processing from ComputeBudget struct itself, so CB instructions can be processed elsewhere without involving ComputeBudget * updated tests * avoid built ComputeBudget from dated ComputeBudgetLimits in this refactoring PR * Clean-up program-runtime/src/compute_budget_processor.rs * Add test for a corner case that deprecated instruction is used to request units greater than max limit; * Update code to handle the corner case.
1 parent ba112a0 commit 510b6b9

File tree

10 files changed

+881
-762
lines changed

10 files changed

+881
-762
lines changed

accounts-db/src/accounts.rs

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use {
2424
itertools::Itertools,
2525
log::*,
2626
solana_program_runtime::{
27-
compute_budget::{self, ComputeBudget},
27+
compute_budget_processor::process_compute_budget_instructions,
2828
loaded_programs::LoadedProgramsForTxBatch,
2929
},
3030
solana_sdk::{
@@ -34,9 +34,8 @@ use {
3434
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
3535
clock::{BankId, Slot},
3636
feature_set::{
37-
self, add_set_tx_loaded_accounts_data_size_instruction,
38-
include_loaded_accounts_data_size_in_fee_calculation,
39-
remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix,
37+
self, include_loaded_accounts_data_size_in_fee_calculation,
38+
remove_congestion_multiplier_from_fee_calculation,
4039
simplify_writable_program_account_check, FeatureSet,
4140
},
4241
fee::FeeStructure,
@@ -246,15 +245,16 @@ impl Accounts {
246245
feature_set: &FeatureSet,
247246
) -> Result<Option<NonZeroUsize>> {
248247
if feature_set.is_active(&feature_set::cap_transaction_accounts_data_size::id()) {
249-
let mut compute_budget =
250-
ComputeBudget::new(compute_budget::MAX_COMPUTE_UNIT_LIMIT as u64);
251-
let _process_transaction_result = compute_budget.process_instructions(
248+
let compute_budget_limits = process_compute_budget_instructions(
252249
tx.message().program_instructions_iter(),
253-
!feature_set.is_active(&remove_deprecated_request_unit_ix::id()),
254-
feature_set.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
255-
);
250+
feature_set,
251+
)
252+
.unwrap_or_default();
256253
// sanitize against setting size limit to zero
257-
NonZeroUsize::new(compute_budget.loaded_accounts_data_size_limit).map_or(
254+
NonZeroUsize::new(
255+
usize::try_from(compute_budget_limits.loaded_accounts_bytes).unwrap_or_default(),
256+
)
257+
.map_or(
258258
Err(TransactionError::InvalidLoadedAccountsDataSizeLimit),
259259
|v| Ok(Some(v)),
260260
)
@@ -721,7 +721,7 @@ impl Accounts {
721721
fee_structure.calculate_fee(
722722
tx.message(),
723723
lamports_per_signature,
724-
&ComputeBudget::fee_budget_limits(tx.message().program_instructions_iter(), feature_set),
724+
&process_compute_budget_instructions(tx.message().program_instructions_iter(), feature_set).unwrap_or_default().into(),
725725
feature_set.is_active(&remove_congestion_multiplier_from_fee_calculation::id()),
726726
feature_set.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()),
727727
)
@@ -1470,8 +1470,9 @@ mod tests {
14701470
transaction_results::{DurableNonceFee, TransactionExecutionDetails},
14711471
},
14721472
assert_matches::assert_matches,
1473-
solana_program_runtime::prioritization_fee::{
1474-
PrioritizationFeeDetails, PrioritizationFeeType,
1473+
solana_program_runtime::{
1474+
compute_budget_processor,
1475+
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
14751476
},
14761477
solana_sdk::{
14771478
account::{AccountSharedData, WritableAccount},
@@ -1747,13 +1748,15 @@ mod tests {
17471748
);
17481749

17491750
let mut feature_set = FeatureSet::all_enabled();
1750-
feature_set.deactivate(&remove_deprecated_request_unit_ix::id());
1751+
feature_set.deactivate(&solana_sdk::feature_set::remove_deprecated_request_unit_ix::id());
17511752

17521753
let message = SanitizedMessage::try_from(tx.message().clone()).unwrap();
17531754
let fee = FeeStructure::default().calculate_fee(
17541755
&message,
17551756
lamports_per_signature,
1756-
&ComputeBudget::fee_budget_limits(message.program_instructions_iter(), &feature_set),
1757+
&process_compute_budget_instructions(message.program_instructions_iter(), &feature_set)
1758+
.unwrap_or_default()
1759+
.into(),
17571760
true,
17581761
false,
17591762
);
@@ -4249,7 +4252,11 @@ mod tests {
42494252

42504253
let result_no_limit = Ok(None);
42514254
let result_default_limit = Ok(Some(
4252-
NonZeroUsize::new(compute_budget::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES).unwrap(),
4255+
NonZeroUsize::new(
4256+
usize::try_from(compute_budget_processor::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES)
4257+
.unwrap(),
4258+
)
4259+
.unwrap(),
42534260
));
42544261
let result_requested_limit: Result<Option<NonZeroUsize>> =
42554262
Ok(Some(NonZeroUsize::new(99).unwrap()));
@@ -4277,7 +4284,10 @@ mod tests {
42774284
// if tx doesn't set limit, then default limit (64MiB)
42784285
// if tx sets limit, then requested limit
42794286
// if tx sets limit to zero, then TransactionError::InvalidLoadedAccountsDataSizeLimit
4280-
feature_set.activate(&add_set_tx_loaded_accounts_data_size_instruction::id(), 0);
4287+
feature_set.activate(
4288+
&solana_sdk::feature_set::add_set_tx_loaded_accounts_data_size_instruction::id(),
4289+
0,
4290+
);
42814291
test(tx_not_set_limit, &feature_set, &result_default_limit);
42824292
test(tx_set_limit_99, &feature_set, &result_requested_limit);
42834293
test(tx_set_limit_0, &feature_set, &result_invalid_limit);
@@ -4312,13 +4322,15 @@ mod tests {
43124322
);
43134323

43144324
let mut feature_set = FeatureSet::all_enabled();
4315-
feature_set.deactivate(&remove_deprecated_request_unit_ix::id());
4325+
feature_set.deactivate(&solana_sdk::feature_set::remove_deprecated_request_unit_ix::id());
43164326

43174327
let message = SanitizedMessage::try_from(tx.message().clone()).unwrap();
43184328
let fee = FeeStructure::default().calculate_fee(
43194329
&message,
43204330
lamports_per_signature,
4321-
&ComputeBudget::fee_budget_limits(message.program_instructions_iter(), &feature_set),
4331+
&process_compute_budget_instructions(message.program_instructions_iter(), &feature_set)
4332+
.unwrap_or_default()
4333+
.into(),
43224334
true,
43234335
false,
43244336
);

cost-model/src/cost_model.rs

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@
88
use {
99
crate::{block_cost_limits::*, transaction_cost::*},
1010
log::*,
11-
solana_program_runtime::compute_budget::{
12-
ComputeBudget, DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_COMPUTE_UNIT_LIMIT,
11+
solana_program_runtime::{
12+
compute_budget::DEFAULT_HEAP_COST,
13+
compute_budget_processor::{
14+
process_compute_budget_instructions, ComputeBudgetLimits,
15+
DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, MAX_COMPUTE_UNIT_LIMIT,
16+
},
1317
},
1418
solana_sdk::{
1519
borsh0_10::try_from_slice_unchecked,
1620
compute_budget::{self, ComputeBudgetInstruction},
17-
feature_set::{
18-
add_set_tx_loaded_accounts_data_size_instruction,
19-
include_loaded_accounts_data_size_in_fee_calculation,
20-
remove_deprecated_request_unit_ix, FeatureSet,
21-
},
21+
feature_set::{include_loaded_accounts_data_size_in_fee_calculation, FeatureSet},
2222
fee::FeeStructure,
2323
instruction::CompiledInstruction,
2424
program_utils::limited_deserialize,
@@ -62,10 +62,12 @@ impl CostModel {
6262
// to set limit, `compute_budget.loaded_accounts_data_size_limit` is set to default
6363
// limit of 64MB; which will convert to (64M/32K)*8CU = 16_000 CUs
6464
//
65-
pub fn calculate_loaded_accounts_data_size_cost(compute_budget: &ComputeBudget) -> u64 {
65+
pub fn calculate_loaded_accounts_data_size_cost(
66+
compute_budget_limits: &ComputeBudgetLimits,
67+
) -> u64 {
6668
FeeStructure::calculate_memory_usage_cost(
67-
compute_budget.loaded_accounts_data_size_limit,
68-
compute_budget.heap_cost,
69+
usize::try_from(compute_budget_limits.loaded_accounts_bytes).unwrap(),
70+
DEFAULT_HEAP_COST,
6971
)
7072
}
7173

@@ -128,32 +130,28 @@ impl CostModel {
128130
}
129131

130132
// calculate bpf cost based on compute budget instructions
131-
let mut compute_budget = ComputeBudget::default();
132-
133-
let result = compute_budget.process_instructions(
134-
transaction.message().program_instructions_iter(),
135-
!feature_set.is_active(&remove_deprecated_request_unit_ix::id()),
136-
feature_set.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
137-
);
138133

139134
// if failed to process compute_budget instructions, the transaction will not be executed
140135
// by `bank`, therefore it should be considered as no execution cost by cost model.
141-
match result {
142-
Ok(_) => {
136+
match process_compute_budget_instructions(
137+
transaction.message().program_instructions_iter(),
138+
feature_set,
139+
) {
140+
Ok(compute_budget_limits) => {
143141
// if tx contained user-space instructions and a more accurate estimate available correct it,
144142
// where "user-space instructions" must be specifically checked by
145143
// 'compute_unit_limit_is_set' flag, because compute_budget does not distinguish
146144
// builtin and bpf instructions when calculating default compute-unit-limit. (see
147145
// compute_budget.rs test `test_process_mixed_instructions_without_compute_budget`)
148146
if bpf_costs > 0 && compute_unit_limit_is_set {
149-
bpf_costs = compute_budget.compute_unit_limit
147+
bpf_costs = u64::from(compute_budget_limits.compute_unit_limit);
150148
}
151149

152150
if feature_set
153151
.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id())
154152
{
155153
loaded_accounts_data_size_cost =
156-
Self::calculate_loaded_accounts_data_size_cost(&compute_budget);
154+
Self::calculate_loaded_accounts_data_size_cost(&compute_budget_limits);
157155
}
158156
}
159157
Err(_) => {
@@ -545,7 +543,8 @@ mod tests {
545543
// default loaded_accounts_data_size_limit
546544
const DEFAULT_PAGE_COST: u64 = 8;
547545
let expected_loaded_accounts_data_size_cost =
548-
solana_program_runtime::compute_budget::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES as u64
546+
solana_program_runtime::compute_budget_processor::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES
547+
as u64
549548
/ ACCOUNT_DATA_COST_PAGE_SIZE
550549
* DEFAULT_PAGE_COST;
551550

@@ -663,36 +662,36 @@ mod tests {
663662
#[allow(clippy::field_reassign_with_default)]
664663
#[test]
665664
fn test_calculate_loaded_accounts_data_size_cost() {
666-
let mut compute_budget = ComputeBudget::default();
665+
let mut compute_budget_limits = ComputeBudgetLimits::default();
667666

668667
// accounts data size are priced in block of 32K, ...
669668

670669
// ... requesting less than 32K should still be charged as one block
671-
compute_budget.loaded_accounts_data_size_limit = 31_usize * 1024;
670+
compute_budget_limits.loaded_accounts_bytes = 31 * 1024;
672671
assert_eq!(
673-
compute_budget.heap_cost,
674-
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget)
672+
DEFAULT_HEAP_COST,
673+
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget_limits)
675674
);
676675

677676
// ... requesting exact 32K should be charged as one block
678-
compute_budget.loaded_accounts_data_size_limit = 32_usize * 1024;
677+
compute_budget_limits.loaded_accounts_bytes = 32 * 1024;
679678
assert_eq!(
680-
compute_budget.heap_cost,
681-
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget)
679+
DEFAULT_HEAP_COST,
680+
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget_limits)
682681
);
683682

684683
// ... requesting slightly above 32K should be charged as 2 block
685-
compute_budget.loaded_accounts_data_size_limit = 33_usize * 1024;
684+
compute_budget_limits.loaded_accounts_bytes = 33 * 1024;
686685
assert_eq!(
687-
compute_budget.heap_cost * 2,
688-
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget)
686+
DEFAULT_HEAP_COST * 2,
687+
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget_limits)
689688
);
690689

691690
// ... requesting exact 64K should be charged as 2 block
692-
compute_budget.loaded_accounts_data_size_limit = 64_usize * 1024;
691+
compute_budget_limits.loaded_accounts_bytes = 64 * 1024;
693692
assert_eq!(
694-
compute_budget.heap_cost * 2,
695-
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget)
693+
DEFAULT_HEAP_COST * 2,
694+
CostModel::calculate_loaded_accounts_data_size_cost(&compute_budget_limits)
696695
);
697696
}
698697

0 commit comments

Comments
 (0)