diff --git a/compute-budget/Cargo.toml b/compute-budget/Cargo.toml index 211aa9394806b4..6a5ca7d376f5df 100644 --- a/compute-budget/Cargo.toml +++ b/compute-budget/Cargo.toml @@ -17,6 +17,7 @@ solana-sdk = { workspace = true } rustc_version = { workspace = true } [features] +dev-context-only-utils = [] frozen-abi = [ "dep:solana-frozen-abi", "solana-sdk/frozen-abi", diff --git a/compute-budget/src/compute_budget_processor.rs b/compute-budget/src/compute_budget_processor.rs index edd56e382a6bf2..bd6e0bb19f11cf 100644 --- a/compute-budget/src/compute_budget_processor.rs +++ b/compute-budget/src/compute_budget_processor.rs @@ -21,7 +21,10 @@ pub const MIN_HEAP_FRAME_BYTES: u32 = HEAP_LENGTH as u32; /// 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; +const MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES: u32 = 64 * 1024 * 1024; +/// The default accounts data size a transaction can load if sender didn't set specific +/// value via set_loaded_accounts_data_size_limit +const DEFAULT_LOADED_ACCOUNTS_DATA_SIZE_BYTES: u32 = 2 * 1024 * 1024; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ComputeBudgetLimits { @@ -31,13 +34,35 @@ pub struct ComputeBudgetLimits { pub loaded_accounts_bytes: u32, } +// Default is only usable from dev context (ie. tests, benches etc) +#[cfg(any(test, feature = "dev-context-only-utils"))] impl Default for ComputeBudgetLimits { fn default() -> Self { + Self::new_with(false) + } +} + +impl ComputeBudgetLimits { + // default constructor with feature gate + pub fn new_with(use_default_loaded_accounts_data_size: bool) -> Self { ComputeBudgetLimits { updated_heap_bytes: MIN_HEAP_FRAME_BYTES, compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT, compute_unit_price: 0, - loaded_accounts_bytes: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES, + loaded_accounts_bytes: Self::get_default_loaded_accounts_data_size_bytes( + use_default_loaded_accounts_data_size, + ), + } + } + + // behavior before/after feature gate `default_loaded_accounts_data_size_limit` + pub fn get_default_loaded_accounts_data_size_bytes( + use_default_loaded_accounts_data_size: bool, + ) -> u32 { + if use_default_loaded_accounts_data_size { + DEFAULT_LOADED_ACCOUNTS_DATA_SIZE_BYTES + } else { + MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES } } } @@ -68,6 +93,7 @@ impl From for FeeBudgetLimits { /// are retrieved and returned, pub fn process_compute_budget_instructions<'a>( instructions: impl Iterator, + use_default_loaded_accounts_data_size: bool, ) -> Result { let mut num_non_compute_budget_instructions: u32 = 0; let mut updated_compute_unit_limit = None; @@ -136,7 +162,11 @@ pub fn process_compute_budget_instructions<'a>( 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) + .unwrap_or( + ComputeBudgetLimits::get_default_loaded_accounts_data_size_bytes( + use_default_loaded_accounts_data_size, + ), + ) .min(MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES); Ok(ComputeBudgetLimits { @@ -168,17 +198,22 @@ mod tests { }; macro_rules! test { - ( $instructions: expr, $expected_result: expr) => { + ( $instructions: expr, $expected_result: expr, $use_default_loaded_accounts_data_size: 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 result = - process_compute_budget_instructions(tx.message().program_instructions_iter()); + let result = process_compute_budget_instructions( + tx.message().program_instructions_iter(), + $use_default_loaded_accounts_data_size, + ); assert_eq!($expected_result, result); }; + ( $instructions: expr, $expected_result: expr) => { + test!($instructions, $expected_result, true); + }; } #[test] @@ -438,20 +473,26 @@ mod tests { // Assert when set_loaded_accounts_data_size_limit is not presented // budget is set to default data size - 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 - ); + for use_default_loaded_accounts_data_size in [true, false] { + let expected_result = Ok(ComputeBudgetLimits { + compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT, + loaded_accounts_bytes: + ComputeBudgetLimits::get_default_loaded_accounts_data_size_bytes( + use_default_loaded_accounts_data_size, + ), + ..ComputeBudgetLimits::default() + }); + + test!( + &[Instruction::new_with_bincode( + Pubkey::new_unique(), + &0_u8, + vec![] + ),], + expected_result, + use_default_loaded_accounts_data_size + ); + } // Assert when set_loaded_accounts_data_size_limit presents more than once, // return DuplicateInstruction @@ -483,18 +524,26 @@ mod tests { Hash::default(), )); - let result = - process_compute_budget_instructions(transaction.message().program_instructions_iter()); + for use_default_loaded_accounts_data_size in [true, false] { + let result = process_compute_budget_instructions( + transaction.message().program_instructions_iter(), + use_default_loaded_accounts_data_size, + ); - // 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() - }) - ); + // 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, + loaded_accounts_bytes: + ComputeBudgetLimits::get_default_loaded_accounts_data_size_bytes( + use_default_loaded_accounts_data_size + ), + ..ComputeBudgetLimits::default() + }) + ); + } } } diff --git a/core/src/banking_stage/consumer.rs b/core/src/banking_stage/consumer.rs index 6ae0881da45d8a..2b77660fd49730 100644 --- a/core/src/banking_stage/consumer.rs +++ b/core/src/banking_stage/consumer.rs @@ -741,8 +741,12 @@ impl Consumer { error_counters: &mut TransactionErrorMetrics, ) -> Result<(), TransactionError> { let fee_payer = message.fee_payer(); - let budget_limits = - process_compute_budget_instructions(message.program_instructions_iter())?.into(); + let budget_limits = process_compute_budget_instructions( + message.program_instructions_iter(), + bank.feature_set + .is_active(&feature_set::default_loaded_accounts_data_size_limit::id()), + )? + .into(); let fee = bank.fee_structure().calculate_fee( message, bank.get_lamports_per_signature(), diff --git a/core/src/banking_stage/forward_packet_batches_by_accounts.rs b/core/src/banking_stage/forward_packet_batches_by_accounts.rs index e01ca3b213b81e..c1fe7ec60897e3 100644 --- a/core/src/banking_stage/forward_packet_batches_by_accounts.rs +++ b/core/src/banking_stage/forward_packet_batches_by_accounts.rs @@ -187,6 +187,7 @@ mod tests { let transaction = Transaction::new_unsigned(Message::new( &[ ComputeBudgetInstruction::set_compute_unit_price(priority), + ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(64 * 1024 * 1024), system_instruction::transfer(&from_account, write_to_account, 2), ], Some(&from_account), diff --git a/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs b/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs index b84af5a902c5d0..169c70578fe6a2 100644 --- a/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs +++ b/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs @@ -30,6 +30,7 @@ use { solana_sdk::{ self, clock::{FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET, MAX_PROCESSING_AGE}, + feature_set, fee::FeeBudgetLimits, saturating_add_assign, transaction::SanitizedTransaction, @@ -508,9 +509,13 @@ impl SchedulerController { .is_ok() }) .filter_map(|(packet, tx)| { - process_compute_budget_instructions(tx.message().program_instructions_iter()) - .map(|compute_budget| (packet, tx, compute_budget.into())) - .ok() + process_compute_budget_instructions( + tx.message().program_instructions_iter(), + bank.feature_set + .is_active(&feature_set::default_loaded_accounts_data_size_limit::id()), + ) + .map(|compute_budget| (packet, tx, compute_budget.into())) + .ok() }) .for_each(|(packet, tx, fee_budget_limits)| { arc_packets.push(packet); diff --git a/cost-model/src/cost_model.rs b/cost-model/src/cost_model.rs index c444ad0885566f..63698526e82759 100644 --- a/cost-model/src/cost_model.rs +++ b/cost-model/src/cost_model.rs @@ -173,8 +173,10 @@ impl CostModel { // 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. - match process_compute_budget_instructions(transaction.message().program_instructions_iter()) - { + match process_compute_budget_instructions( + transaction.message().program_instructions_iter(), + feature_set.is_active(&feature_set::default_loaded_accounts_data_size_limit::id()), + ) { Ok(compute_budget_limits) => { // if tx contained user-space instructions and a more accurate estimate available correct it, // where "user-space instructions" must be specifically checked by @@ -279,6 +281,7 @@ impl CostModel { mod tests { use { super::*, + solana_compute_budget::compute_budget_processor::ComputeBudgetLimits, solana_sdk::{ compute_budget::{self, ComputeBudgetInstruction}, fee::ACCOUNT_DATA_COST_PAGE_SIZE, @@ -615,8 +618,7 @@ mod tests { .unwrap(); const DEFAULT_PAGE_COST: u64 = 8; let expected_loaded_accounts_data_size_cost = - solana_compute_budget::compute_budget_processor::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES - as u64 + ComputeBudgetLimits::get_default_loaded_accounts_data_size_bytes(true) as u64 / ACCOUNT_DATA_COST_PAGE_SIZE * DEFAULT_PAGE_COST; diff --git a/cost-model/src/transaction_cost.rs b/cost-model/src/transaction_cost.rs index 4951e50036ca8b..7b7a2ddc9f2ec5 100644 --- a/cost-model/src/transaction_cost.rs +++ b/cost-model/src/transaction_cost.rs @@ -199,8 +199,10 @@ mod tests { use { super::*, crate::cost_model::CostModel, + solana_compute_budget::compute_budget_processor::ComputeBudgetLimits, solana_sdk::{ feature_set::FeatureSet, + fee::ACCOUNT_DATA_COST_PAGE_SIZE, hash::Hash, message::SimpleAddressLoader, reserved_account_keys::ReservedAccountKeys, @@ -246,16 +248,21 @@ mod tests { ) .unwrap(); - // expected vote tx cost: 2 write locks, 1 sig, 1 vote ix, 8cu of loaded accounts size, + // expected vote tx cost: 2 write locks, 1 sig, 1 vote ix, 8cu of ix data bytes, let expected_vote_cost = SIMPLE_VOTE_USAGE_COST; - // expected non-vote tx cost would include default loaded accounts size cost (16384) additionally - let expected_none_vote_cost = 20535; + // expected non-vote tx cost would include default loaded accounts size cost additionally + const DEFAULT_PAGE_COST: u64 = 8; + let expected_loaded_accounts_data_size_cost = + ComputeBudgetLimits::get_default_loaded_accounts_data_size_bytes(true) as u64 + / ACCOUNT_DATA_COST_PAGE_SIZE + * DEFAULT_PAGE_COST; + let min_none_vote_cost = SIMPLE_VOTE_USAGE_COST + expected_loaded_accounts_data_size_cost; let vote_cost = CostModel::calculate_cost(&vote_transaction, &FeatureSet::all_enabled()); let none_vote_cost = CostModel::calculate_cost(&none_vote_transaction, &FeatureSet::all_enabled()); assert_eq!(expected_vote_cost, vote_cost.sum()); - assert_eq!(expected_none_vote_cost, none_vote_cost.sum()); + assert!(min_none_vote_cost < none_vote_cost.sum()); } } diff --git a/programs/bpf-loader-tests/tests/common.rs b/programs/bpf-loader-tests/tests/common.rs index 99cae212c7f481..68172c6c6842fe 100644 --- a/programs/bpf-loader-tests/tests/common.rs +++ b/programs/bpf-loader-tests/tests/common.rs @@ -6,6 +6,7 @@ use { account::AccountSharedData, account_utils::StateMut, bpf_loader_upgradeable::{id, UpgradeableLoaderState}, + compute_budget::ComputeBudgetInstruction, instruction::{Instruction, InstructionError}, pubkey::Pubkey, signature::{Keypair, Signer}, @@ -35,7 +36,10 @@ pub async fn assert_ix_error( } let transaction = Transaction::new_signed_with_payer( - &[ix], + &[ + ix, + ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(64 * 1024 * 1024), + ], Some(&fee_payer.pubkey()), &signers, recent_blockhash, diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index b26c2e74e230ff..cbc62f971647a8 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -15,7 +15,7 @@ use { }, solana_compute_budget::{ compute_budget::ComputeBudget, - compute_budget_processor::process_compute_budget_instructions, + compute_budget_processor::{process_compute_budget_instructions, ComputeBudgetLimits}, }, solana_ledger::token_balances::collect_token_balances, solana_program_runtime::{invoke_context::mock_process_instruction, timings::ExecuteTimings}, @@ -3848,6 +3848,9 @@ fn test_program_fees() { let fee_structure = FeeStructure::new(0.000005, 0.0, vec![(200, 0.0000005), (1400000, 0.000005)]); bank.set_fee_structure(&fee_structure); + let use_default_loaded_accounts_data_size = bank + .feature_set + .is_active(&feature_set::default_loaded_accounts_data_size_limit::id()); let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); let mut bank_client = BankClient::new_shared(bank); let authority_keypair = Keypair::new(); @@ -3874,9 +3877,12 @@ fn test_program_fees() { let expected_normal_fee = fee_structure.calculate_fee( &sanitized_message, congestion_multiplier, - &process_compute_budget_instructions(sanitized_message.program_instructions_iter()) - .unwrap_or_default() - .into(), + &process_compute_budget_instructions( + sanitized_message.program_instructions_iter(), + use_default_loaded_accounts_data_size, + ) + .unwrap_or_else(|_| ComputeBudgetLimits::new_with(use_default_loaded_accounts_data_size)) + .into(), false, true, ); @@ -3902,9 +3908,12 @@ fn test_program_fees() { let expected_prioritized_fee = fee_structure.calculate_fee( &sanitized_message, congestion_multiplier, - &process_compute_budget_instructions(sanitized_message.program_instructions_iter()) - .unwrap_or_default() - .into(), + &process_compute_budget_instructions( + sanitized_message.program_instructions_iter(), + use_default_loaded_accounts_data_size, + ) + .unwrap_or_else(|_| ComputeBudgetLimits::new_with(use_default_loaded_accounts_data_size)) + .into(), false, true, ); diff --git a/runtime-transaction/src/runtime_transaction.rs b/runtime-transaction/src/runtime_transaction.rs index 625ec28fdb22d8..47db0cd497e7ae 100644 --- a/runtime-transaction/src/runtime_transaction.rs +++ b/runtime-transaction/src/runtime_transaction.rs @@ -15,6 +15,7 @@ use { process_compute_budget_instructions, ComputeBudgetLimits, }, solana_sdk::{ + feature_set::{default_loaded_accounts_data_size_limit, FeatureSet}, hash::Hash, message::{AddressLoader, SanitizedMessage, SanitizedVersionedMessage}, pubkey::Pubkey, @@ -71,6 +72,7 @@ impl RuntimeTransaction { sanitized_versioned_tx: SanitizedVersionedTransaction, message_hash: Option, is_simple_vote_tx: Option, + feature_set: &FeatureSet, ) -> Result { let mut meta = TransactionMeta::default(); meta.set_is_simple_vote_tx( @@ -86,7 +88,10 @@ impl RuntimeTransaction { compute_unit_price, loaded_accounts_bytes, .. - } = process_compute_budget_instructions(message.program_instructions_iter())?; + } = process_compute_budget_instructions( + message.program_instructions_iter(), + feature_set.is_active(&default_loaded_accounts_data_size_limit::id()), + )?; meta.set_compute_unit_limit(compute_unit_limit); meta.set_compute_unit_price(compute_unit_price); meta.set_loaded_accounts_bytes(loaded_accounts_bytes); @@ -134,6 +139,7 @@ mod tests { }, solana_sdk::{ compute_budget::ComputeBudgetInstruction, + feature_set::FeatureSet, instruction::Instruction, message::Message, reserved_account_keys::ReservedAccountKeys, @@ -216,10 +222,15 @@ mod tests { svt: SanitizedVersionedTransaction, is_simple_vote: Option, ) -> bool { - RuntimeTransaction::::try_from(svt, None, is_simple_vote) - .unwrap() - .meta - .is_simple_vote_tx + RuntimeTransaction::::try_from( + svt, + None, + is_simple_vote, + &FeatureSet::default(), + ) + .unwrap() + .meta + .is_simple_vote_tx } assert!(!get_is_simple_vote( @@ -252,6 +263,7 @@ mod tests { non_vote_sanitized_versioned_transaction(), Some(hash), None, + &FeatureSet::default(), ) .unwrap(); @@ -286,6 +298,7 @@ mod tests { .to_sanitized_versioned_transaction(), Some(hash), None, + &FeatureSet::default(), ) .unwrap(); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 6a5dfd85d83c19..ebc35060bcdfe8 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -90,7 +90,7 @@ use { solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1, solana_compute_budget::{ compute_budget::ComputeBudget, - compute_budget_processor::process_compute_budget_instructions, + compute_budget_processor::{process_compute_budget_instructions, ComputeBudgetLimits}, }, solana_cost_model::cost_tracker::CostTracker, solana_loader_v4_program::create_program_runtime_environment_v2, @@ -118,7 +118,8 @@ use { epoch_schedule::EpochSchedule, feature, feature_set::{ - self, include_loaded_accounts_data_size_in_fee_calculation, + self, default_loaded_accounts_data_size_limit, + include_loaded_accounts_data_size_in_fee_calculation, remove_rounding_in_fee_calculation, reward_full_priority_fee, FeatureSet, }, fee::{FeeDetails, FeeStructure}, @@ -3124,12 +3125,20 @@ impl Bank { message: &SanitizedMessage, lamports_per_signature: u64, ) -> u64 { + let use_default_loaded_accounts_data_size = self + .feature_set + .is_active(&default_loaded_accounts_data_size_limit::id()); self.fee_structure().calculate_fee( message, lamports_per_signature, - &process_compute_budget_instructions(message.program_instructions_iter()) - .unwrap_or_default() - .into(), + &process_compute_budget_instructions( + message.program_instructions_iter(), + use_default_loaded_accounts_data_size, + ) + .unwrap_or_else(|_| { + ComputeBudgetLimits::new_with(use_default_loaded_accounts_data_size) + }) + .into(), self.feature_set .is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()), self.feature_set diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 6cf2bded8de9ee..ae44a2a1baf4bd 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -10061,9 +10061,15 @@ fn calculate_test_fee( lamports_per_signature: u64, fee_structure: &FeeStructure, ) -> u64 { - let budget_limits = process_compute_budget_instructions(message.program_instructions_iter()) - .unwrap_or_default() - .into(); + // feature gate `default_loaded_accounts_data_size_limit` status does not impact transaction + // fee. Safe to set it as activated for tests relate to fee + let use_default_loaded_accounts_data_size = true; + let budget_limits = process_compute_budget_instructions( + message.program_instructions_iter(), + use_default_loaded_accounts_data_size, + ) + .unwrap_or_else(|_| ComputeBudgetLimits::new_with(use_default_loaded_accounts_data_size)) + .into(); fee_structure.calculate_fee(message, lamports_per_signature, &budget_limits, false, true) } @@ -11138,7 +11144,10 @@ fn create_mock_realloc_tx( account_metas, ); Transaction::new_signed_with_payer( - &[instruction], + &[ + instruction, + ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(64 * 1024 * 1024), + ], Some(&payer.pubkey()), &[payer], recent_blockhash, diff --git a/runtime/src/compute_budget_details.rs b/runtime/src/compute_budget_details.rs index c14aa24c063538..eb9cca429c56a7 100644 --- a/runtime/src/compute_budget_details.rs +++ b/runtime/src/compute_budget_details.rs @@ -23,7 +23,14 @@ pub trait GetComputeBudgetDetails { instructions: impl Iterator, _round_compute_unit_price_enabled: bool, ) -> Option { - let compute_budget_limits = process_compute_budget_instructions(instructions).ok()?; + // ComputeBudgetDetails does not concern with loaded_accounts_data_size_limit, hence safe + // to hardcode its feature gate `default_loaded_accounts_data_size_limit` as activated + let use_default_loaded_accounts_data_size = true; + let compute_budget_limits = process_compute_budget_instructions( + instructions, + use_default_loaded_accounts_data_size, + ) + .ok()?; Some(ComputeBudgetDetails { compute_unit_price: compute_budget_limits.compute_unit_price, compute_unit_limit: u64::from(compute_budget_limits.compute_unit_limit), diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 3704d273bb1c55..c792011bd7d145 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -832,6 +832,10 @@ pub mod verify_retransmitter_signature { solana_sdk::declare_id!("BZ5g4hRbu5hLQQBdPyo2z9icGyJ8Khiyj3QS6dhWijTb"); } +pub mod default_loaded_accounts_data_size_limit { + solana_sdk::declare_id!("CVvWw7NMVCn2Yp5RfxqrcTHXnaYyh91A2bGbkk88XXMM"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -1035,6 +1039,7 @@ lazy_static! { (migrate_address_lookup_table_program_to_core_bpf::id(), "Migrate Address Lookup Table program to Core BPF #1651"), (zk_elgamal_proof_program_enabled::id(), "Enable ZkElGamalProof program SIMD-0153"), (verify_retransmitter_signature::id(), "Verify retransmitter signature #1840"), + (default_loaded_accounts_data_size_limit::id(), "add default loaded_accounts_data_size_limit #1568"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter() diff --git a/svm/Cargo.toml b/svm/Cargo.toml index c53f13c91c2706..d4a53e637763ed 100644 --- a/svm/Cargo.toml +++ b/svm/Cargo.toml @@ -41,6 +41,7 @@ prost = { workspace = true } prost-types = { workspace = true } rand = { workspace = true } solana-bpf-loader-program = { workspace = true } +solana-compute-budget = { workspace = true, features = ["dev-context-only-utils"] } solana-compute-budget-program = { workspace = true } solana-logger = { workspace = true } solana-sdk = { workspace = true, features = ["dev-context-only-utils"] } diff --git a/svm/src/account_loader.rs b/svm/src/account_loader.rs index c62963c09e0934..71f000a1439ccc 100644 --- a/svm/src/account_loader.rs +++ b/svm/src/account_loader.rs @@ -202,8 +202,10 @@ fn load_transaction_accounts( let mut accounts_found = Vec::with_capacity(account_keys.len()); let mut rent_debits = RentDebits::default(); - let requested_loaded_accounts_data_size_limit = - get_requested_loaded_accounts_data_size_limit(message)?; + let requested_loaded_accounts_data_size_limit = get_requested_loaded_accounts_data_size_limit( + message, + feature_set.is_active(&feature_set::default_loaded_accounts_data_size_limit::id()), + )?; let mut accumulated_accounts_data_size: usize = 0; let instruction_accounts = message @@ -377,10 +379,13 @@ fn load_transaction_accounts( /// Note, requesting zero bytes will result transaction error fn get_requested_loaded_accounts_data_size_limit( sanitized_message: &SanitizedMessage, + use_default_loaded_accounts_data_size: bool, ) -> Result> { - let compute_budget_limits = - process_compute_budget_instructions(sanitized_message.program_instructions_iter()) - .unwrap_or_default(); + let compute_budget_limits = process_compute_budget_instructions( + sanitized_message.program_instructions_iter(), + use_default_loaded_accounts_data_size, + ) + .unwrap_or_else(|_| ComputeBudgetLimits::new_with(use_default_loaded_accounts_data_size)); // sanitize against setting size limit to zero NonZeroUsize::new( usize::try_from(compute_budget_limits.loaded_accounts_bytes).unwrap_or_default(), @@ -899,10 +904,10 @@ mod tests { #[test] fn test_get_requested_loaded_accounts_data_size_limit() { - // an prrivate helper function fn test( instructions: &[solana_sdk::instruction::Instruction], expected_result: &Result>, + use_default_loaded_accounts_data_size: bool, ) { let payer_keypair = Keypair::new(); let tx = SanitizedTransaction::from_transaction_for_tests(Transaction::new( @@ -912,7 +917,10 @@ mod tests { )); assert_eq!( *expected_result, - get_requested_loaded_accounts_data_size_limit(tx.message()) + get_requested_loaded_accounts_data_size_limit( + tx.message(), + use_default_loaded_accounts_data_size + ) ); } @@ -932,24 +940,37 @@ mod tests { solana_sdk::instruction::Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), ]; - let result_default_limit = Ok(Some( - NonZeroUsize::new( - usize::try_from(compute_budget_processor::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES) - .unwrap(), - ) - .unwrap(), - )); - let result_requested_limit: Result> = - Ok(Some(NonZeroUsize::new(99).unwrap())); - let result_invalid_limit = Err(TransactionError::InvalidLoadedAccountsDataSizeLimit); - // the results should be: - // if tx doesn't set limit, then default limit (64MiB) + // if tx doesn't set limit, then default limit // if tx sets limit, then requested limit // if tx sets limit to zero, then TransactionError::InvalidLoadedAccountsDataSizeLimit - test(tx_not_set_limit, &result_default_limit); - test(tx_set_limit_99, &result_requested_limit); - test(tx_set_limit_0, &result_invalid_limit); + for use_default_loaded_accounts_data_size in [true, false] { + test( + tx_not_set_limit, + &Ok(Some( + NonZeroUsize::new( + usize::try_from( + ComputeBudgetLimits::get_default_loaded_accounts_data_size_bytes( + use_default_loaded_accounts_data_size, + ), + ) + .unwrap(), + ) + .unwrap(), + )), + use_default_loaded_accounts_data_size, + ); + test( + tx_set_limit_99, + &Ok(Some(NonZeroUsize::new(99).unwrap())), + use_default_loaded_accounts_data_size, + ); + test( + tx_set_limit_0, + &Err(TransactionError::InvalidLoadedAccountsDataSizeLimit), + use_default_loaded_accounts_data_size, + ); + } } struct ValidateFeePayerTestParameter { diff --git a/svm/src/transaction_processor.rs b/svm/src/transaction_processor.rs index a225ed1ff1d4e0..dc84ad0cf85e18 100644 --- a/svm/src/transaction_processor.rs +++ b/svm/src/transaction_processor.rs @@ -39,6 +39,7 @@ use { account::{AccountSharedData, ReadableAccount, PROGRAM_OWNERS}, clock::{Epoch, Slot}, feature_set::{ + default_loaded_accounts_data_size_limit, include_loaded_accounts_data_size_in_fee_calculation, remove_rounding_in_fee_calculation, FeatureSet, }, @@ -422,6 +423,7 @@ impl TransactionBatchProcessor { ) -> transaction::Result { let compute_budget_limits = process_compute_budget_instructions( message.program_instructions_iter(), + feature_set.is_active(&default_loaded_accounts_data_size_limit::id()), ) .map_err(|err| { error_counters.invalid_compute_budget += 1; @@ -1957,7 +1959,7 @@ mod tests { &Hash::new_unique(), )); let compute_budget_limits = - process_compute_budget_instructions(message.program_instructions_iter()).unwrap(); + process_compute_budget_instructions(message.program_instructions_iter(), true).unwrap(); let fee_payer_address = message.fee_payer(); let current_epoch = 42; let rent_collector = RentCollector { @@ -2037,7 +2039,7 @@ mod tests { &Hash::new_unique(), )); let compute_budget_limits = - process_compute_budget_instructions(message.program_instructions_iter()).unwrap(); + process_compute_budget_instructions(message.program_instructions_iter(), true).unwrap(); let fee_payer_address = message.fee_payer(); let mut rent_collector = RentCollector::default(); rent_collector.rent.lamports_per_byte_year = 1_000_000; @@ -2274,7 +2276,7 @@ mod tests { &Hash::new_unique(), )); let compute_budget_limits = - process_compute_budget_instructions(message.program_instructions_iter()).unwrap(); + process_compute_budget_instructions(message.program_instructions_iter(), true).unwrap(); let fee_payer_address = message.fee_payer(); let min_balance = Rent::default().minimum_balance(nonce::State::size()); let transaction_fee = lamports_per_signature;