include loaded accounts data size limit in transaction fee calculation (#30659)

* include loaded accounts data size limit in transaction base fee calculation
* citing compute_budget for heap cost;
* update sbf tests

Co-authored-by: Trent Nelson <trent.a.b.nelson@gmail.com>
This commit is contained in:
Tao Zhu 2023-03-21 18:41:41 -05:00 committed by GitHub
parent d4a6e00ffc
commit 21c287a64f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 102 additions and 5 deletions

View File

@ -3634,6 +3634,7 @@ fn test_program_fees() {
true,
true,
true,
false,
);
bank_client
.send_and_confirm_message(&[&mint_keypair], message)
@ -3659,6 +3660,7 @@ fn test_program_fees() {
true,
true,
true,
false,
);
assert!(expected_normal_fee < expected_prioritized_fee);

View File

@ -34,6 +34,7 @@ use {
clock::{BankId, Slot},
feature_set::{
self, add_set_tx_loaded_accounts_data_size_instruction, enable_request_heap_frame_ix,
include_loaded_accounts_data_size_in_fee_calculation,
remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix,
use_default_units_in_fee_calculation, FeatureSet,
},
@ -661,6 +662,7 @@ impl Accounts {
feature_set.is_active(&remove_congestion_multiplier_from_fee_calculation::id()),
feature_set.is_active(&enable_request_heap_frame_ix::id()) || self.accounts_db.expected_cluster_type() != ClusterType::MainnetBeta,
feature_set.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
feature_set.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()),
)
} else {
return (Err(TransactionError::BlockhashNotFound), None);
@ -1690,6 +1692,7 @@ mod tests {
true,
true,
true,
false,
);
assert_eq!(fee, lamports_per_signature);

View File

@ -121,6 +121,7 @@ use {
feature_set::{
self, add_set_tx_loaded_accounts_data_size_instruction, disable_fee_calculator,
enable_early_verification_of_account_modifications, enable_request_heap_frame_ix,
include_loaded_accounts_data_size_in_fee_calculation,
remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix,
use_default_units_in_fee_calculation, FeatureSet,
},
@ -3505,6 +3506,8 @@ impl Bank {
self.enable_request_heap_frame_ix(),
self.feature_set
.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
self.feature_set
.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()),
))
}
@ -3554,6 +3557,8 @@ impl Bank {
self.enable_request_heap_frame_ix(),
self.feature_set
.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
self.feature_set
.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()),
)
}
@ -4894,6 +4899,7 @@ impl Bank {
remove_congestion_multiplier: bool,
enable_request_heap_frame_ix: bool,
support_set_accounts_data_size_limit_ix: bool,
include_loaded_account_data_size_in_fee: bool,
) -> u64 {
// Fee based on compute units and signatures
let congestion_multiplier = if lamports_per_signature == 0 {
@ -4921,10 +4927,20 @@ impl Bank {
.saturating_mul(fee_structure.lamports_per_signature);
let write_lock_fee = Self::get_num_write_locks_in_message(message)
.saturating_mul(fee_structure.lamports_per_write_lock);
// `compute_fee` covers costs for both requested_compute_units and
// requested_loaded_account_data_size
let loaded_accounts_data_size_cost = if include_loaded_account_data_size_in_fee {
Self::calculate_loaded_accounts_data_size_cost(&compute_budget)
} else {
0_u64
};
let total_compute_units =
loaded_accounts_data_size_cost.saturating_add(compute_budget.compute_unit_limit);
let compute_fee = fee_structure
.compute_fee_bins
.iter()
.find(|bin| compute_budget.compute_unit_limit <= bin.limit)
.find(|bin| total_compute_units <= bin.limit)
.map(|bin| bin.fee)
.unwrap_or_else(|| {
fee_structure
@ -4942,6 +4958,23 @@ impl Bank {
.round() as u64
}
// Calculate cost of loaded accounts size in the same way heap cost is charged at
// rate of 8cu per 32K. Citing `program_runtime\src\compute_budget.rs`: "(cost of
// heap is about) 0.5us per 32k at 15 units/us rounded up"
//
// Before feature `support_set_loaded_accounts_data_size_limit_ix` is enabled, or
// if user doesn't use compute budget ix `set_loaded_accounts_data_size_limit_ix`
// 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
//
fn calculate_loaded_accounts_data_size_cost(compute_budget: &ComputeBudget) -> u64 {
const ACCOUNT_DATA_COST_PAGE_SIZE: u64 = 32_u64.saturating_mul(1024);
(compute_budget.loaded_accounts_data_size_limit as u64)
.saturating_add(ACCOUNT_DATA_COST_PAGE_SIZE.saturating_sub(1))
.saturating_div(ACCOUNT_DATA_COST_PAGE_SIZE)
.saturating_mul(compute_budget.heap_cost)
}
fn filter_program_errors_and_collect_fee(
&self,
txs: &[SanitizedTransaction],
@ -4987,6 +5020,8 @@ impl Bank {
self.enable_request_heap_frame_ix(),
self.feature_set
.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
self.feature_set
.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()),
);
// In case of instruction error, even though no accounts

View File

@ -3221,6 +3221,7 @@ fn test_bank_tx_compute_unit_fee() {
true,
true,
true,
false,
);
let (expected_fee_collected, expected_fee_burned) =
@ -3405,6 +3406,7 @@ fn test_bank_blockhash_compute_unit_fee_structure() {
true,
true,
true,
false,
);
assert_eq!(
bank.get_balance(&mint_keypair.pubkey()),
@ -3426,6 +3428,7 @@ fn test_bank_blockhash_compute_unit_fee_structure() {
true,
true,
true,
false,
);
assert_eq!(
bank.get_balance(&mint_keypair.pubkey()),
@ -3542,6 +3545,7 @@ fn test_filter_program_errors_and_collect_compute_unit_fee() {
true,
true,
true,
false,
) * 2
)
.0
@ -10713,6 +10717,7 @@ fn test_calculate_fee() {
true,
true,
support_set_accounts_data_size_limit_ix,
false,
),
0
);
@ -10733,6 +10738,7 @@ fn test_calculate_fee() {
true,
true,
support_set_accounts_data_size_limit_ix,
false,
),
1
);
@ -10758,6 +10764,7 @@ fn test_calculate_fee() {
true,
true,
support_set_accounts_data_size_limit_ix,
false,
),
4
);
@ -10787,7 +10794,8 @@ fn test_calculate_fee_compute_units() {
false,
true,
true,
support_set_accounts_data_size_limit_ix
support_set_accounts_data_size_limit_ix,
false,
),
max_fee + lamports_per_signature
);
@ -10809,7 +10817,8 @@ fn test_calculate_fee_compute_units() {
false,
true,
true,
support_set_accounts_data_size_limit_ix
support_set_accounts_data_size_limit_ix,
false,
),
max_fee + 3 * lamports_per_signature
);
@ -10854,6 +10863,7 @@ fn test_calculate_fee_compute_units() {
true,
true,
support_set_accounts_data_size_limit_ix,
false,
);
assert_eq!(
fee,
@ -10903,7 +10913,8 @@ fn test_calculate_fee_secp256k1() {
false,
true,
true,
support_set_accounts_data_size_limit_ix
support_set_accounts_data_size_limit_ix,
false,
),
2
);
@ -10926,7 +10937,8 @@ fn test_calculate_fee_secp256k1() {
false,
true,
true,
support_set_accounts_data_size_limit_ix
support_set_accounts_data_size_limit_ix,
false,
),
11
);
@ -12659,6 +12671,7 @@ fn test_calculate_fee_with_congestion_multiplier() {
remove_congestion_multiplier,
true,
true,
false,
),
signature_fee * signature_count
);
@ -12683,6 +12696,7 @@ fn test_calculate_fee_with_congestion_multiplier() {
remove_congestion_multiplier,
true,
true,
false,
),
signature_fee * signature_count / denominator
);
@ -12725,6 +12739,7 @@ fn test_calculate_fee_with_request_heap_frame_flag() {
true,
enable_request_heap_frame_ix,
true,
false,
),
signature_fee + request_cu * lamports_per_cu
);
@ -12742,6 +12757,7 @@ fn test_calculate_fee_with_request_heap_frame_flag() {
true,
enable_request_heap_frame_ix,
true,
false,
),
signature_fee
);
@ -12914,3 +12930,39 @@ fn test_bank_verify_accounts_hash_with_base() {
},
));
}
#[allow(clippy::field_reassign_with_default)]
#[test]
fn test_calculate_loaded_accounts_data_size_cost() {
let mut compute_budget = ComputeBudget::default();
// accounts data size are priced in block of 32K, ...
// ... requesting less than 32K should still be charged as one block
compute_budget.loaded_accounts_data_size_limit = 31_usize * 1024;
assert_eq!(
compute_budget.heap_cost,
Bank::calculate_loaded_accounts_data_size_cost(&compute_budget)
);
// ... requesting exact 32K should be charged as one block
compute_budget.loaded_accounts_data_size_limit = 32_usize * 1024;
assert_eq!(
compute_budget.heap_cost,
Bank::calculate_loaded_accounts_data_size_cost(&compute_budget)
);
// ... requesting slightly above 32K should be charged as 2 block
compute_budget.loaded_accounts_data_size_limit = 33_usize * 1024;
assert_eq!(
compute_budget.heap_cost * 2,
Bank::calculate_loaded_accounts_data_size_cost(&compute_budget)
);
// ... requesting exact 64K should be charged as 2 block
compute_budget.loaded_accounts_data_size_limit = 64_usize * 1024;
assert_eq!(
compute_budget.heap_cost * 2,
Bank::calculate_loaded_accounts_data_size_cost(&compute_budget)
);
}

View File

@ -634,6 +634,10 @@ pub mod remove_bpf_loader_incorrect_program_id {
solana_sdk::declare_id!("2HmTkCj9tXuPE4ueHzdD7jPeMf9JGCoZh5AsyoATiWEe");
}
pub mod include_loaded_accounts_data_size_in_fee_calculation {
solana_sdk::declare_id!("EaQpmC6GtRssaZ3PCUM5YksGqUdMLeZ46BQXYtHYakDS");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -787,6 +791,7 @@ lazy_static! {
(switch_to_new_elf_parser::id(), "switch to new ELF parser #30497"),
(round_up_heap_size::id(), "round up heap size when calculating heap cost #30679"),
(remove_bpf_loader_incorrect_program_id::id(), "stop incorrectly throwing IncorrectProgramId in bpf_loader #30747"),
(include_loaded_accounts_data_size_in_fee_calculation::id(), "include transaction loaded accounts data size in base fee calculation #30657"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()