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

View File

@ -34,6 +34,7 @@ use {
clock::{BankId, Slot}, clock::{BankId, Slot},
feature_set::{ feature_set::{
self, add_set_tx_loaded_accounts_data_size_instruction, enable_request_heap_frame_ix, 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, remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix,
use_default_units_in_fee_calculation, FeatureSet, 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(&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(&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(&add_set_tx_loaded_accounts_data_size_instruction::id()),
feature_set.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()),
) )
} else { } else {
return (Err(TransactionError::BlockhashNotFound), None); return (Err(TransactionError::BlockhashNotFound), None);
@ -1690,6 +1692,7 @@ mod tests {
true, true,
true, true,
true, true,
false,
); );
assert_eq!(fee, lamports_per_signature); assert_eq!(fee, lamports_per_signature);

View File

@ -121,6 +121,7 @@ use {
feature_set::{ feature_set::{
self, add_set_tx_loaded_accounts_data_size_instruction, disable_fee_calculator, self, add_set_tx_loaded_accounts_data_size_instruction, disable_fee_calculator,
enable_early_verification_of_account_modifications, enable_request_heap_frame_ix, 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, remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix,
use_default_units_in_fee_calculation, FeatureSet, use_default_units_in_fee_calculation, FeatureSet,
}, },
@ -3505,6 +3506,8 @@ impl Bank {
self.enable_request_heap_frame_ix(), self.enable_request_heap_frame_ix(),
self.feature_set self.feature_set
.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()), .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.enable_request_heap_frame_ix(),
self.feature_set self.feature_set
.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()), .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, remove_congestion_multiplier: bool,
enable_request_heap_frame_ix: bool, enable_request_heap_frame_ix: bool,
support_set_accounts_data_size_limit_ix: bool, support_set_accounts_data_size_limit_ix: bool,
include_loaded_account_data_size_in_fee: bool,
) -> u64 { ) -> u64 {
// Fee based on compute units and signatures // Fee based on compute units and signatures
let congestion_multiplier = if lamports_per_signature == 0 { let congestion_multiplier = if lamports_per_signature == 0 {
@ -4921,10 +4927,20 @@ impl Bank {
.saturating_mul(fee_structure.lamports_per_signature); .saturating_mul(fee_structure.lamports_per_signature);
let write_lock_fee = Self::get_num_write_locks_in_message(message) let write_lock_fee = Self::get_num_write_locks_in_message(message)
.saturating_mul(fee_structure.lamports_per_write_lock); .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 let compute_fee = fee_structure
.compute_fee_bins .compute_fee_bins
.iter() .iter()
.find(|bin| compute_budget.compute_unit_limit <= bin.limit) .find(|bin| total_compute_units <= bin.limit)
.map(|bin| bin.fee) .map(|bin| bin.fee)
.unwrap_or_else(|| { .unwrap_or_else(|| {
fee_structure fee_structure
@ -4942,6 +4958,23 @@ impl Bank {
.round() as u64 .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( fn filter_program_errors_and_collect_fee(
&self, &self,
txs: &[SanitizedTransaction], txs: &[SanitizedTransaction],
@ -4987,6 +5020,8 @@ impl Bank {
self.enable_request_heap_frame_ix(), self.enable_request_heap_frame_ix(),
self.feature_set self.feature_set
.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()), .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 // 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, true,
true, true,
false,
); );
let (expected_fee_collected, expected_fee_burned) = let (expected_fee_collected, expected_fee_burned) =
@ -3405,6 +3406,7 @@ fn test_bank_blockhash_compute_unit_fee_structure() {
true, true,
true, true,
true, true,
false,
); );
assert_eq!( assert_eq!(
bank.get_balance(&mint_keypair.pubkey()), bank.get_balance(&mint_keypair.pubkey()),
@ -3426,6 +3428,7 @@ fn test_bank_blockhash_compute_unit_fee_structure() {
true, true,
true, true,
true, true,
false,
); );
assert_eq!( assert_eq!(
bank.get_balance(&mint_keypair.pubkey()), bank.get_balance(&mint_keypair.pubkey()),
@ -3542,6 +3545,7 @@ fn test_filter_program_errors_and_collect_compute_unit_fee() {
true, true,
true, true,
true, true,
false,
) * 2 ) * 2
) )
.0 .0
@ -10713,6 +10717,7 @@ fn test_calculate_fee() {
true, true,
true, true,
support_set_accounts_data_size_limit_ix, support_set_accounts_data_size_limit_ix,
false,
), ),
0 0
); );
@ -10733,6 +10738,7 @@ fn test_calculate_fee() {
true, true,
true, true,
support_set_accounts_data_size_limit_ix, support_set_accounts_data_size_limit_ix,
false,
), ),
1 1
); );
@ -10758,6 +10764,7 @@ fn test_calculate_fee() {
true, true,
true, true,
support_set_accounts_data_size_limit_ix, support_set_accounts_data_size_limit_ix,
false,
), ),
4 4
); );
@ -10787,7 +10794,8 @@ fn test_calculate_fee_compute_units() {
false, false,
true, true,
true, true,
support_set_accounts_data_size_limit_ix support_set_accounts_data_size_limit_ix,
false,
), ),
max_fee + lamports_per_signature max_fee + lamports_per_signature
); );
@ -10809,7 +10817,8 @@ fn test_calculate_fee_compute_units() {
false, false,
true, true,
true, true,
support_set_accounts_data_size_limit_ix support_set_accounts_data_size_limit_ix,
false,
), ),
max_fee + 3 * lamports_per_signature max_fee + 3 * lamports_per_signature
); );
@ -10854,6 +10863,7 @@ fn test_calculate_fee_compute_units() {
true, true,
true, true,
support_set_accounts_data_size_limit_ix, support_set_accounts_data_size_limit_ix,
false,
); );
assert_eq!( assert_eq!(
fee, fee,
@ -10903,7 +10913,8 @@ fn test_calculate_fee_secp256k1() {
false, false,
true, true,
true, true,
support_set_accounts_data_size_limit_ix support_set_accounts_data_size_limit_ix,
false,
), ),
2 2
); );
@ -10926,7 +10937,8 @@ fn test_calculate_fee_secp256k1() {
false, false,
true, true,
true, true,
support_set_accounts_data_size_limit_ix support_set_accounts_data_size_limit_ix,
false,
), ),
11 11
); );
@ -12659,6 +12671,7 @@ fn test_calculate_fee_with_congestion_multiplier() {
remove_congestion_multiplier, remove_congestion_multiplier,
true, true,
true, true,
false,
), ),
signature_fee * signature_count signature_fee * signature_count
); );
@ -12683,6 +12696,7 @@ fn test_calculate_fee_with_congestion_multiplier() {
remove_congestion_multiplier, remove_congestion_multiplier,
true, true,
true, true,
false,
), ),
signature_fee * signature_count / denominator signature_fee * signature_count / denominator
); );
@ -12725,6 +12739,7 @@ fn test_calculate_fee_with_request_heap_frame_flag() {
true, true,
enable_request_heap_frame_ix, enable_request_heap_frame_ix,
true, true,
false,
), ),
signature_fee + request_cu * lamports_per_cu signature_fee + request_cu * lamports_per_cu
); );
@ -12742,6 +12757,7 @@ fn test_calculate_fee_with_request_heap_frame_flag() {
true, true,
enable_request_heap_frame_ix, enable_request_heap_frame_ix,
true, true,
false,
), ),
signature_fee 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"); solana_sdk::declare_id!("2HmTkCj9tXuPE4ueHzdD7jPeMf9JGCoZh5AsyoATiWEe");
} }
pub mod include_loaded_accounts_data_size_in_fee_calculation {
solana_sdk::declare_id!("EaQpmC6GtRssaZ3PCUM5YksGqUdMLeZ46BQXYtHYakDS");
}
lazy_static! { lazy_static! {
/// Map of feature identifiers to user-visible description /// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [ 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"), (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"), (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"), (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 ***************/ /*************** ADD NEW FEATURES HERE ***************/
] ]
.iter() .iter()