diff --git a/docs/src/developing/programming-model/runtime.md b/docs/src/developing/programming-model/runtime.md index e626bd8195..ab2494d17e 100644 --- a/docs/src/developing/programming-model/runtime.md +++ b/docs/src/developing/programming-model/runtime.md @@ -60,8 +60,9 @@ to. As the transaction is processed compute units are consumed by its instruction's programs performing operations such as executing SBF instructions, calling syscalls, etc... When the transaction consumes its entire budget, or -exceeds a bound such as attempting a call stack that is too deep, the runtime -halts the transaction processing and returns an error. +exceeds a bound such as attempting a call stack that is too deep, or loaded +account data size exceeds limit, the runtime halts the transaction processing and +returns an error. The following operations incur a compute cost: @@ -150,6 +151,21 @@ let instruction = ComputeBudgetInstruction::set_compute_unit_limit(300_000); let instruction = ComputeBudgetInstruction::set_compute_unit_price(1); ``` +### Accounts data size limit + +A transaction should request the maximum bytes of accounts data it is +allowed to load by including a `SetLoadedAccountsDataSizeLimit` instruction, requested +limit is capped by `MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES`. If no +`SetLoadedAccountsDataSizeLimit` is provided, the transaction is defaulted to +have limit of `MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES`. + +The `ComputeBudgetInstruction::set_loaded_accounts_data_size_limit` function can be used +to create this instruction: + +```rust +let instruction = ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(100_000); +``` + ## New Features As Solana evolves, new features or patches may be introduced that changes the diff --git a/program-runtime/src/compute_budget.rs b/program-runtime/src/compute_budget.rs index afc684217a..7f00f4386d 100644 --- a/program-runtime/src/compute_budget.rs +++ b/program-runtime/src/compute_budget.rs @@ -10,6 +10,10 @@ use { }, }; +/// 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; @@ -109,6 +113,9 @@ pub struct ComputeBudget { pub alt_bn128_pairing_one_pair_cost_other: u64, /// Big integer modular exponentiation cost 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, } impl Default for ComputeBudget { @@ -157,6 +164,7 @@ impl ComputeBudget { alt_bn128_pairing_one_pair_cost_first: 36_364, alt_bn128_pairing_one_pair_cost_other: 12_121, big_modular_exponentiation_cost: 33, + loaded_accounts_data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES, } } @@ -166,11 +174,13 @@ impl ComputeBudget { default_units_per_instruction: bool, support_request_units_deprecated: bool, enable_request_heap_frame_ix: bool, + support_set_loaded_accounts_data_size_limit_ix: bool, ) -> Result { let mut num_non_compute_budget_instructions: usize = 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) { @@ -214,6 +224,14 @@ impl ComputeBudget { 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 { @@ -250,6 +268,10 @@ impl ComputeBudget { .unwrap_or(MAX_COMPUTE_UNIT_LIMIT) .min(MAX_COMPUTE_UNIT_LIMIT) as u64; + 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()) @@ -272,7 +294,7 @@ mod tests { }; macro_rules! test { - ( $instructions: expr, $expected_result: expr, $expected_budget: expr, $enable_request_heap_frame_ix: expr ) => { + ( $instructions: expr, $expected_result: expr, $expected_budget: expr, $enable_request_heap_frame_ix: 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], @@ -285,12 +307,19 @@ mod tests { true, false, /*not support request_units_deprecated*/ $enable_request_heap_frame_ix, + $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, true); + test!( + $instructions, + $expected_result, + $expected_budget, + true, + false + ); }; } @@ -561,6 +590,7 @@ mod tests { compute_unit_limit: 0, ..ComputeBudget::default() }, + false, false ); @@ -575,6 +605,7 @@ mod tests { InstructionError::InvalidInstructionData )), ComputeBudget::default(), + false, false ); test!( @@ -587,6 +618,7 @@ mod tests { InstructionError::InvalidInstructionData, )), ComputeBudget::default(), + false, false ); test!( @@ -601,6 +633,7 @@ mod tests { InstructionError::InvalidInstructionData, )), ComputeBudget::default(), + false, false ); test!( @@ -615,6 +648,7 @@ mod tests { InstructionError::InvalidInstructionData, )), ComputeBudget::default(), + false, false ); @@ -635,7 +669,170 @@ mod tests { compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64 * 7, ..ComputeBudget::default() }, + false, false ); } + + #[test] + fn test_process_loaded_accounts_data_size_limit_instruction() { + let enable_request_heap_frame_ix: bool = true; + + // 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() + }, + enable_request_heap_frame_ix, + 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, + enable_request_heap_frame_ix, + 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, + enable_request_heap_frame_ix, + 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, + enable_request_heap_frame_ix, + 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, + enable_request_heap_frame_ix, + support_set_loaded_accounts_data_size_limit_ix + ); + } + } } diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index 335e54a963..24876a99b1 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -3590,6 +3590,7 @@ fn test_program_fees() { false, true, true, + true, ); bank_client .send_and_confirm_message(&[&mint_keypair], message) @@ -3614,6 +3615,7 @@ fn test_program_fees() { false, true, true, + true, ); assert!(expected_normal_fee < expected_prioritized_fee); diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 6d2b931b2e..c0de987404 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -31,8 +31,9 @@ use { bpf_loader_upgradeable::{self, UpgradeableLoaderState}, clock::{BankId, Slot}, feature_set::{ - self, enable_request_heap_frame_ix, remove_congestion_multiplier_from_fee_calculation, - remove_deprecated_request_unit_ix, use_default_units_in_fee_calculation, FeatureSet, + self, add_set_tx_loaded_accounts_data_size_instruction, enable_request_heap_frame_ix, + remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix, + use_default_units_in_fee_calculation, FeatureSet, }, fee::FeeStructure, genesis_config::ClusterType, @@ -640,6 +641,7 @@ impl Accounts { !feature_set.is_active(&remove_deprecated_request_unit_ix::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(&add_set_tx_loaded_accounts_data_size_instruction::id()), ) } else { return (Err(TransactionError::BlockhashNotFound), None); @@ -1667,6 +1669,7 @@ mod tests { false, true, true, + true, ); assert_eq!(fee, lamports_per_signature); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 9cd4dae4e0..f88dea1ced 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -118,9 +118,10 @@ use { epoch_schedule::EpochSchedule, feature, feature_set::{ - self, disable_fee_calculator, enable_early_verification_of_account_modifications, - enable_request_heap_frame_ix, remove_congestion_multiplier_from_fee_calculation, - remove_deprecated_request_unit_ix, use_default_units_in_fee_calculation, FeatureSet, + self, add_set_tx_loaded_accounts_data_size_instruction, disable_fee_calculator, + enable_early_verification_of_account_modifications, enable_request_heap_frame_ix, + remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix, + use_default_units_in_fee_calculation, FeatureSet, }, fee::FeeStructure, fee_calculator::{FeeCalculator, FeeRateGovernor}, @@ -3497,6 +3498,8 @@ impl Bank { self.feature_set .is_active(&remove_congestion_multiplier_from_fee_calculation::id()), self.enable_request_heap_frame_ix(), + self.feature_set + .is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()), )) } @@ -3544,6 +3547,8 @@ impl Bank { self.feature_set .is_active(&remove_congestion_multiplier_from_fee_calculation::id()), self.enable_request_heap_frame_ix(), + self.feature_set + .is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()), ) } @@ -4584,35 +4589,38 @@ impl Bank { .map(|(accs, tx)| match accs { (Err(e), _nonce) => TransactionExecutionResult::NotExecuted(e.clone()), (Ok(loaded_transaction), nonce) => { - let compute_budget = - if let Some(compute_budget) = self.runtime_config.compute_budget { - compute_budget - } else { - let mut compute_budget = - ComputeBudget::new(compute_budget::MAX_COMPUTE_UNIT_LIMIT as u64); + let compute_budget = if let Some(compute_budget) = + self.runtime_config.compute_budget + { + compute_budget + } else { + let mut compute_budget = + ComputeBudget::new(compute_budget::MAX_COMPUTE_UNIT_LIMIT as u64); - let mut compute_budget_process_transaction_time = - Measure::start("compute_budget_process_transaction_time"); - let process_transaction_result = compute_budget.process_instructions( - tx.message().program_instructions_iter(), - true, - !self - .feature_set - .is_active(&remove_deprecated_request_unit_ix::id()), - true, // don't reject txs that use request heap size ix - ); - compute_budget_process_transaction_time.stop(); - saturating_add_assign!( - timings - .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 mut compute_budget_process_transaction_time = + Measure::start("compute_budget_process_transaction_time"); + let process_transaction_result = compute_budget.process_instructions( + tx.message().program_instructions_iter(), + true, + !self + .feature_set + .is_active(&remove_deprecated_request_unit_ix::id()), + true, // don't reject txs that use request heap size ix + self.feature_set + .is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()), + ); + compute_budget_process_transaction_time.stop(); + saturating_add_assign!( + timings + .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 + }; self.execute_loaded_transaction( tx, @@ -4880,6 +4888,7 @@ impl Bank { support_request_units_deprecated: bool, remove_congestion_multiplier: bool, enable_request_heap_frame_ix: bool, + support_set_accounts_data_size_limit_ix: bool, ) -> u64 { // Fee based on compute units and signatures let congestion_multiplier = if lamports_per_signature == 0 { @@ -4899,6 +4908,7 @@ impl Bank { use_default_units_per_instruction, support_request_units_deprecated, enable_request_heap_frame_ix, + support_set_accounts_data_size_limit_ix, ) .unwrap_or_default(); let prioritization_fee = prioritization_fee_details.get_fee(); @@ -4970,6 +4980,8 @@ impl Bank { self.feature_set .is_active(&remove_congestion_multiplier_from_fee_calculation::id()), self.enable_request_heap_frame_ix(), + self.feature_set + .is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()), ); // In case of instruction error, even though no accounts diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 01beb58aa0..2a96af67b7 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -3220,6 +3220,7 @@ fn test_bank_tx_compute_unit_fee() { false, true, true, + true, ); let (expected_fee_collected, expected_fee_burned) = @@ -3403,6 +3404,7 @@ fn test_bank_blockhash_compute_unit_fee_structure() { false, true, true, + true, ); assert_eq!( bank.get_balance(&mint_keypair.pubkey()), @@ -3423,6 +3425,7 @@ fn test_bank_blockhash_compute_unit_fee_structure() { false, true, true, + true, ); assert_eq!( bank.get_balance(&mint_keypair.pubkey()), @@ -3538,6 +3541,7 @@ fn test_filter_program_errors_and_collect_compute_unit_fee() { false, true, true, + true, ) * 2 ) .0 @@ -10709,38 +10713,44 @@ fn test_calculate_fee() { // Default: no fee. let message = SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(); - assert_eq!( - Bank::calculate_fee( - &message, - 0, - &FeeStructure { - lamports_per_signature: 0, - ..FeeStructure::default() - }, - true, - false, - true, - true, - ), - 0 - ); + for support_set_accounts_data_size_limit_ix in [true, false] { + assert_eq!( + Bank::calculate_fee( + &message, + 0, + &FeeStructure { + lamports_per_signature: 0, + ..FeeStructure::default() + }, + true, + false, + true, + true, + support_set_accounts_data_size_limit_ix, + ), + 0 + ); + } // One signature, a fee. - assert_eq!( - Bank::calculate_fee( - &message, - 1, - &FeeStructure { - lamports_per_signature: 1, - ..FeeStructure::default() - }, - true, - false, - true, - true, - ), - 1 - ); + for support_set_accounts_data_size_limit_ix in [true, false] { + assert_eq!( + Bank::calculate_fee( + &message, + 1, + &FeeStructure { + lamports_per_signature: 1, + ..FeeStructure::default() + }, + true, + false, + true, + true, + support_set_accounts_data_size_limit_ix, + ), + 1 + ); + } // Two signatures, double the fee. let key0 = Pubkey::new_unique(); @@ -10748,21 +10758,24 @@ fn test_calculate_fee() { let ix0 = system_instruction::transfer(&key0, &key1, 1); let ix1 = system_instruction::transfer(&key1, &key0, 1); let message = SanitizedMessage::try_from(Message::new(&[ix0, ix1], Some(&key0))).unwrap(); - assert_eq!( - Bank::calculate_fee( - &message, - 2, - &FeeStructure { - lamports_per_signature: 2, - ..FeeStructure::default() - }, - true, - false, - true, - true, - ), - 4 - ); + for support_set_accounts_data_size_limit_ix in [true, false] { + assert_eq!( + Bank::calculate_fee( + &message, + 2, + &FeeStructure { + lamports_per_signature: 2, + ..FeeStructure::default() + }, + true, + false, + true, + true, + support_set_accounts_data_size_limit_ix, + ), + 4 + ); + } } #[test] @@ -10778,10 +10791,21 @@ fn test_calculate_fee_compute_units() { let message = SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(); - assert_eq!( - Bank::calculate_fee(&message, 1, &fee_structure, true, false, true, true), - max_fee + lamports_per_signature - ); + for support_set_accounts_data_size_limit_ix in [true, false] { + assert_eq!( + Bank::calculate_fee( + &message, + 1, + &fee_structure, + true, + false, + true, + true, + support_set_accounts_data_size_limit_ix + ), + max_fee + lamports_per_signature + ); + } // Three signatures, two instructions, no unit request @@ -10789,10 +10813,21 @@ fn test_calculate_fee_compute_units() { let ix1 = system_instruction::transfer(&Pubkey::new_unique(), &Pubkey::new_unique(), 1); let message = SanitizedMessage::try_from(Message::new(&[ix0, ix1], Some(&Pubkey::new_unique()))).unwrap(); - assert_eq!( - Bank::calculate_fee(&message, 1, &fee_structure, true, false, true, true), - max_fee + 3 * lamports_per_signature - ); + for support_set_accounts_data_size_limit_ix in [true, false] { + assert_eq!( + Bank::calculate_fee( + &message, + 1, + &fee_structure, + true, + false, + true, + true, + support_set_accounts_data_size_limit_ix + ), + max_fee + 3 * lamports_per_signature + ); + } // Explicit fee schedule @@ -10823,11 +10858,22 @@ fn test_calculate_fee_compute_units() { Some(&Pubkey::new_unique()), )) .unwrap(); - let fee = Bank::calculate_fee(&message, 1, &fee_structure, true, false, true, true); - assert_eq!( - fee, - lamports_per_signature + prioritization_fee_details.get_fee() - ); + for support_set_accounts_data_size_limit_ix in [true, false] { + let fee = Bank::calculate_fee( + &message, + 1, + &fee_structure, + true, + false, + true, + true, + support_set_accounts_data_size_limit_ix, + ); + assert_eq!( + fee, + lamports_per_signature + prioritization_fee_details.get_fee() + ); + } } } @@ -10861,10 +10907,21 @@ fn test_calculate_fee_secp256k1() { Some(&key0), )) .unwrap(); - assert_eq!( - Bank::calculate_fee(&message, 1, &fee_structure, true, false, true, true), - 2 - ); + for support_set_accounts_data_size_limit_ix in [true, false] { + assert_eq!( + Bank::calculate_fee( + &message, + 1, + &fee_structure, + true, + false, + true, + true, + support_set_accounts_data_size_limit_ix + ), + 2 + ); + } secp_instruction1.data = vec![0]; secp_instruction2.data = vec![10]; @@ -10873,10 +10930,21 @@ fn test_calculate_fee_secp256k1() { Some(&key0), )) .unwrap(); - assert_eq!( - Bank::calculate_fee(&message, 1, &fee_structure, true, false, true, true), - 11 - ); + for support_set_accounts_data_size_limit_ix in [true, false] { + assert_eq!( + Bank::calculate_fee( + &message, + 1, + &fee_structure, + true, + false, + true, + true, + support_set_accounts_data_size_limit_ix + ), + 11 + ); + } } #[test] @@ -12606,6 +12674,7 @@ fn test_calculate_fee_with_congestion_multiplier() { false, remove_congestion_multiplier, true, + true, ), signature_fee * signature_count ); @@ -12629,6 +12698,7 @@ fn test_calculate_fee_with_congestion_multiplier() { false, remove_congestion_multiplier, true, + true, ), signature_fee * signature_count / denominator ); @@ -12670,6 +12740,7 @@ fn test_calculate_fee_with_request_heap_frame_flag() { false, true, enable_request_heap_frame_ix, + true, ), signature_fee + request_cu * lamports_per_cu ); @@ -12686,6 +12757,7 @@ fn test_calculate_fee_with_request_heap_frame_flag() { false, true, enable_request_heap_frame_ix, + true, ), signature_fee ); diff --git a/runtime/src/cost_model.rs b/runtime/src/cost_model.rs index 88b0ea15ca..3dd60c9aff 100644 --- a/runtime/src/cost_model.rs +++ b/runtime/src/cost_model.rs @@ -13,7 +13,8 @@ use { }, solana_sdk::{ feature_set::{ - remove_deprecated_request_unit_ix, use_default_units_in_fee_calculation, FeatureSet, + add_set_tx_loaded_accounts_data_size_instruction, remove_deprecated_request_unit_ix, + use_default_units_in_fee_calculation, FeatureSet, }, instruction::CompiledInstruction, program_utils::limited_deserialize, @@ -155,6 +156,7 @@ impl CostModel { feature_set.is_active(&use_default_units_in_fee_calculation::id()), !feature_set.is_active(&remove_deprecated_request_unit_ix::id()), enable_request_heap_frame_ix, + feature_set.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()), ); // if tx contained user-space instructions and a more accurate estimate available correct it diff --git a/runtime/src/transaction_priority_details.rs b/runtime/src/transaction_priority_details.rs index 296542f15b..b7f916c9b8 100644 --- a/runtime/src/transaction_priority_details.rs +++ b/runtime/src/transaction_priority_details.rs @@ -26,6 +26,7 @@ pub trait GetTransactionPriorityDetails { true, // use default units per instruction false, // stop supporting prioritization by request_units_deprecated instruction true, // enable request heap frame instruction + true, // enable support set accounts data size instruction ) .ok()?; Some(TransactionPriorityDetails { diff --git a/sdk/src/compute_budget.rs b/sdk/src/compute_budget.rs index b51325aa5c..84d0c37660 100644 --- a/sdk/src/compute_budget.rs +++ b/sdk/src/compute_budget.rs @@ -41,6 +41,8 @@ pub enum ComputeBudgetInstruction { /// Set a compute unit price in "micro-lamports" to pay a higher transaction /// fee for higher transaction prioritization. SetComputeUnitPrice(u64), + /// Set a specific transaction-wide account data size limit, in bytes, is allowed to load. + SetLoadedAccountsDataSizeLimit(u32), } impl ComputeBudgetInstruction { @@ -65,4 +67,9 @@ impl ComputeBudgetInstruction { pub fn pack(self) -> Result, std::io::Error> { self.try_to_vec() } + + /// Create a `ComputeBudgetInstruction::SetLoadedAccountsDataSizeLimit` `Instruction` + pub fn set_loaded_accounts_data_size_limit(bytes: u32) -> Instruction { + Instruction::new_with_borsh(id(), &Self::SetLoadedAccountsDataSizeLimit(bytes), vec![]) + } } diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 4e1223a387..0acf8333d9 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -618,6 +618,10 @@ pub mod apply_cost_tracker_during_replay { solana_sdk::declare_id!("2ry7ygxiYURULZCrypHhveanvP5tzZ4toRwVp89oCNSj"); } +pub mod add_set_tx_loaded_accounts_data_size_instruction { + solana_sdk::declare_id!("G6vbf1UBok8MWb8m25ex86aoQHeKTzDKzuZADHkShqm6"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -767,6 +771,7 @@ lazy_static! { (prevent_rent_paying_rent_recipients::id(), "prevent recipients of rent rewards from ending in rent-paying state #30151"), (delay_visibility_of_program_deployment::id(), "delay visibility of program upgrades #30085"), (apply_cost_tracker_during_replay::id(), "apply cost tracker to blocks during replay #29595"), + (add_set_tx_loaded_accounts_data_size_instruction::id(), "add compute budget instruction for setting account data size per transaction #30366"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter()