diff --git a/banks-server/src/banks_server.rs b/banks-server/src/banks_server.rs index 6744154d5e4..e561e6379ca 100644 --- a/banks-server/src/banks_server.rs +++ b/banks-server/src/banks_server.rs @@ -8,7 +8,7 @@ use { TransactionSimulationDetails, TransactionStatus, }, solana_client::connection_cache::ConnectionCache, - solana_feature_set::{move_precompile_verification_to_svm, FeatureSet}, + solana_feature_set::{enable_loader_v4, move_precompile_verification_to_svm, FeatureSet}, solana_runtime::{ bank::{Bank, TransactionSimulationResult}, bank_forks::BankForks, @@ -185,6 +185,7 @@ fn simulate_transaction( Some(false), // is_simple_vote_tx bank, bank.get_reserved_account_keys(), + bank.feature_set.is_active(&enable_loader_v4::id()), ) { Err(err) => { return BanksTransactionResultWithSimulation { @@ -322,6 +323,7 @@ impl Banks for BanksServer { Some(false), // is_simple_vote_tx bank.as_ref(), bank.get_reserved_account_keys(), + bank.feature_set.is_active(&enable_loader_v4::id()), ) { Ok(tx) => tx, Err(err) => return Some(Err(err)), @@ -405,9 +407,12 @@ impl Banks for BanksServer { commitment: CommitmentLevel, ) -> Option { let bank = self.bank(commitment); - let sanitized_message = - SanitizedMessage::try_from_legacy_message(message, bank.get_reserved_account_keys()) - .ok()?; + let sanitized_message = SanitizedMessage::try_from_legacy_message( + message, + bank.get_reserved_account_keys(), + bank.feature_set.is_active(&enable_loader_v4::id()), + ) + .ok()?; bank.get_fee_for_message(&sanitized_message) } } diff --git a/cli-output/src/display.rs b/cli-output/src/display.rs index 9a31d11692b..e5722d658e4 100644 --- a/cli-output/src/display.rs +++ b/cli-output/src/display.rs @@ -220,7 +220,11 @@ fn write_transaction( for (account_index, account) in account_keys.iter().enumerate() { let account_meta = CliAccountMeta { is_signer: message.is_signer(account_index), - is_writable: message.is_maybe_writable(account_index, Some(&reserved_account_keys)), + is_writable: message.is_maybe_writable( + account_index, + Some(&reserved_account_keys), + true, + ), is_invoked: message.is_invoked(account_index), }; diff --git a/core/src/banking_stage/consume_worker.rs b/core/src/banking_stage/consume_worker.rs index 341652180a5..3f20bc88139 100644 --- a/core/src/banking_stage/consume_worker.rs +++ b/core/src/banking_stage/consume_worker.rs @@ -1175,6 +1175,7 @@ mod tests { None, loader, &HashSet::default(), + true, ) .unwrap() }; diff --git a/core/src/banking_stage/consumer.rs b/core/src/banking_stage/consumer.rs index 378131ed863..789454bdb8b 100644 --- a/core/src/banking_stage/consumer.rs +++ b/core/src/banking_stage/consumer.rs @@ -2072,6 +2072,7 @@ mod tests { Some(false), bank.as_ref(), &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); diff --git a/core/src/banking_stage/immutable_deserialized_packet.rs b/core/src/banking_stage/immutable_deserialized_packet.rs index 1fb4919a731..fbb24235708 100644 --- a/core/src/banking_stage/immutable_deserialized_packet.rs +++ b/core/src/banking_stage/immutable_deserialized_packet.rs @@ -8,7 +8,7 @@ use { solana_sanitize::SanitizeError, solana_sdk::{ clock::Slot, - feature_set::FeatureSet, + feature_set::{self, FeatureSet}, hash::Hash, message::{v0::LoadedAddresses, AddressLoaderError, Message, SimpleAddressLoader}, pubkey::Pubkey, @@ -148,6 +148,8 @@ impl ImmutableDeserializedPacket { tx, address_loader, reserved_account_keys, + bank.feature_set + .is_active(&feature_set::enable_loader_v4::id()), ) }) .ok()?; diff --git a/core/src/banking_stage/read_write_account_set.rs b/core/src/banking_stage/read_write_account_set.rs index 38da6a80bd2..3b140f0baf0 100644 --- a/core/src/banking_stage/read_write_account_set.rs +++ b/core/src/banking_stage/read_write_account_set.rs @@ -139,6 +139,7 @@ mod tests { Some(false), bank, bank.get_reserved_account_keys(), + true, ) .unwrap() } diff --git a/core/src/banking_stage/transaction_scheduler/receive_and_buffer.rs b/core/src/banking_stage/transaction_scheduler/receive_and_buffer.rs index bc56f32f269..782ecb268d8 100644 --- a/core/src/banking_stage/transaction_scheduler/receive_and_buffer.rs +++ b/core/src/banking_stage/transaction_scheduler/receive_and_buffer.rs @@ -24,6 +24,7 @@ use { crossbeam_channel::{RecvTimeoutError, TryRecvError}, solana_accounts_db::account_locks::validate_account_locks, solana_cost_model::cost_model::CostModel, + solana_feature_set::enable_loader_v4, solana_measure::measure_us, solana_runtime::{bank::Bank, bank_forks::BankForks}, solana_runtime_transaction::{ @@ -507,6 +508,7 @@ impl TransactionViewReceiveAndBuffer { view, loaded_addresses, root_bank.get_reserved_account_keys(), + root_bank.feature_set.is_active(&enable_loader_v4::id()), ) else { return Err(()); }; diff --git a/cost-model/src/transaction_cost.rs b/cost-model/src/transaction_cost.rs index 824f447392f..3f73273427f 100644 --- a/cost-model/src/transaction_cost.rs +++ b/cost-model/src/transaction_cost.rs @@ -323,6 +323,7 @@ mod tests { Some(true), SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); @@ -333,6 +334,7 @@ mod tests { Some(false), SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); diff --git a/entry/benches/entry_sigverify.rs b/entry/benches/entry_sigverify.rs index a4488af586f..8c27bc609fe 100644 --- a/entry/benches/entry_sigverify.rs +++ b/entry/benches/entry_sigverify.rs @@ -45,6 +45,7 @@ fn bench_gpusigverify(bencher: &mut Bencher) { None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) }?; @@ -89,6 +90,7 @@ fn bench_cpusigverify(bencher: &mut Bencher) { None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) }?; diff --git a/entry/src/entry.rs b/entry/src/entry.rs index 6877e73dcb7..fc3bb6573b9 100644 --- a/entry/src/entry.rs +++ b/entry/src/entry.rs @@ -1081,6 +1081,7 @@ mod tests { None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) }?; diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 180b498e8f7..baf66788ef8 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -479,6 +479,7 @@ fn compute_slot_cost( None, SimpleAddressLoader::Disabled, &reserved_account_keys.active, + true, ) .map_err(|err| { warn!("Failed to compute cost of transaction: {:?}", err); diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index c54f05f0d82..cf5ba466f31 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -3443,6 +3443,7 @@ fn test_program_fees() { let sanitized_message = SanitizedMessage::try_from_legacy_message( message.clone(), &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); let fee_budget_limits = FeeBudgetLimits::from( @@ -3476,6 +3477,7 @@ fn test_program_fees() { let sanitized_message = SanitizedMessage::try_from_legacy_message( message.clone(), &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); let fee_budget_limits = FeeBudgetLimits::from( diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 90a727baf54..4cb66b749cf 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -3870,6 +3870,9 @@ pub mod rpc_full { unsanitized_tx, preflight_bank, preflight_bank.get_reserved_account_keys(), + preflight_bank + .feature_set + .is_active(&feature_set::enable_loader_v4::id()), )?; let signature = *transaction.signature(); @@ -4003,8 +4006,13 @@ pub mod rpc_full { }); } - let transaction = - sanitize_transaction(unsanitized_tx, bank, bank.get_reserved_account_keys())?; + let transaction = sanitize_transaction( + unsanitized_tx, + bank, + bank.get_reserved_account_keys(), + bank.feature_set + .is_active(&feature_set::enable_loader_v4::id()), + )?; if sig_verify { verify_transaction(&transaction, &bank.feature_set)?; } @@ -4260,6 +4268,8 @@ pub mod rpc_full { sanitized_versioned_message, bank, bank.get_reserved_account_keys(), + bank.feature_set + .is_active(&feature_set::enable_loader_v4::id()), ) .map_err(|err| Error::invalid_params(format!("invalid transaction message: {err}")))?; let fee = bank.get_fee_for_message(&sanitized_message); @@ -4395,6 +4405,7 @@ fn sanitize_transaction( transaction: VersionedTransaction, address_loader: impl AddressLoader, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Result> { RuntimeTransaction::try_create( transaction, @@ -4402,6 +4413,7 @@ fn sanitize_transaction( None, address_loader, reserved_account_keys, + enable_loader_v4, ) .map_err(|err| Error::invalid_params(format!("invalid transaction: {err}"))) } @@ -8838,7 +8850,8 @@ pub mod tests { sanitize_transaction( unsanitary_versioned_tx, SimpleAddressLoader::Disabled, - &ReservedAccountKeys::empty_key_set() + &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap_err(), expect58 @@ -8863,7 +8876,8 @@ pub mod tests { sanitize_transaction( versioned_tx, SimpleAddressLoader::Disabled, - &ReservedAccountKeys::empty_key_set() + &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap_err(), Error::invalid_params( diff --git a/rpc/src/transaction_status_service.rs b/rpc/src/transaction_status_service.rs index c0e3a47f93e..25a366f1e1b 100644 --- a/rpc/src/transaction_status_service.rs +++ b/rpc/src/transaction_status_service.rs @@ -340,6 +340,7 @@ pub(crate) mod tests { None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); @@ -469,6 +470,7 @@ pub(crate) mod tests { None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); @@ -480,6 +482,7 @@ pub(crate) mod tests { None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); diff --git a/runtime-transaction/src/runtime_transaction/sdk_transactions.rs b/runtime-transaction/src/runtime_transaction/sdk_transactions.rs index 61167b7c253..86ca031d730 100644 --- a/runtime-transaction/src/runtime_transaction/sdk_transactions.rs +++ b/runtime-transaction/src/runtime_transaction/sdk_transactions.rs @@ -76,6 +76,7 @@ impl RuntimeTransaction { is_simple_vote_tx: Option, address_loader: impl AddressLoader, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Result { let statically_loaded_runtime_tx = RuntimeTransaction::::try_from( @@ -87,6 +88,7 @@ impl RuntimeTransaction { statically_loaded_runtime_tx, address_loader, reserved_account_keys, + enable_loader_v4, ) } @@ -97,6 +99,7 @@ impl RuntimeTransaction { statically_loaded_runtime_tx: RuntimeTransaction, address_loader: impl AddressLoader, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Result { let hash = *statically_loaded_runtime_tx.message_hash(); let is_simple_vote_tx = statically_loaded_runtime_tx.is_simple_vote_transaction(); @@ -106,6 +109,7 @@ impl RuntimeTransaction { is_simple_vote_tx, address_loader, reserved_account_keys, + enable_loader_v4, )?; let mut tx = Self { @@ -144,6 +148,7 @@ impl RuntimeTransaction { None, solana_message::SimpleAddressLoader::Disabled, &HashSet::new(), + true, ) .expect("failed to create RuntimeTransaction from Transaction") } @@ -290,6 +295,7 @@ mod tests { statically_loaded_transaction, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ); let dynamically_loaded_transaction = dynamically_loaded_transaction.expect("created from statically loaded tx"); diff --git a/runtime-transaction/src/runtime_transaction/transaction_view.rs b/runtime-transaction/src/runtime_transaction/transaction_view.rs index 0a09fc2c310..d7de904ae19 100644 --- a/runtime-transaction/src/runtime_transaction/transaction_view.rs +++ b/runtime-transaction/src/runtime_transaction/transaction_view.rs @@ -82,15 +82,20 @@ impl RuntimeTransaction> { statically_loaded_runtime_tx: RuntimeTransaction>, loaded_addresses: Option, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Result { let RuntimeTransaction { transaction, meta } = statically_loaded_runtime_tx; // transaction-view does not distinguish between different types of errors here. // return generic sanitize failure error here. // these transactions should be immediately dropped, and we generally // will not care about the specific error at this point. - let transaction = - ResolvedTransactionView::try_new(transaction, loaded_addresses, reserved_account_keys) - .map_err(|_| TransactionError::SanitizeFailure)?; + let transaction = ResolvedTransactionView::try_new( + transaction, + loaded_addresses, + reserved_account_keys, + enable_loader_v4, + ) + .map_err(|_| TransactionError::SanitizeFailure)?; let mut tx = Self { transaction, meta }; tx.load_dynamic_metadata()?; @@ -233,6 +238,7 @@ mod tests { static_runtime_transaction, None, &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); @@ -259,6 +265,7 @@ mod tests { runtime_transaction, loaded_addresses, reserved_account_keys, + true, ) .unwrap(); @@ -325,6 +332,7 @@ mod tests { runtime_transaction, loaded_addresses, reserved_account_keys, + true, ) .unwrap(); @@ -350,6 +358,7 @@ mod tests { None, SimpleAddressLoader::Disabled, &reserved_key_set, + true, ) .unwrap(); assert_translation(sanitized_transaction, None, &reserved_key_set); @@ -382,6 +391,7 @@ mod tests { None, SimpleAddressLoader::Enabled(loaded_addresses.clone()), &reserved_key_set, + true, ) .unwrap(); assert_translation( diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index a66b9edca8f..554ec818e1b 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -3112,6 +3112,9 @@ impl Bank { &self, txs: Vec, ) -> Result>> { + let enable_loader_v4 = self + .feature_set + .is_active(&feature_set::enable_loader_v4::id()); let sanitized_txs = txs .into_iter() .map(|tx| { @@ -3121,6 +3124,7 @@ impl Bank { None, self, self.get_reserved_account_keys(), + enable_loader_v4, ) }) .collect::>>()?; @@ -5638,6 +5642,9 @@ impl Bank { tx: VersionedTransaction, verification_mode: TransactionVerificationMode, ) -> Result> { + let enable_loader_v4 = self + .feature_set + .is_active(&feature_set::enable_loader_v4::id()); let sanitized_tx = { let size = bincode::serialized_size(&tx).map_err(|_| TransactionError::SanitizeFailure)?; @@ -5657,6 +5664,7 @@ impl Bank { None, self, self.get_reserved_account_keys(), + enable_loader_v4, ) }?; diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 62261e5433e..8155e1be250 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -200,7 +200,7 @@ pub(in crate::bank) fn create_genesis_config(lamports: u64) -> (GenesisConfig, K } pub(in crate::bank) fn new_sanitized_message(message: Message) -> SanitizedMessage { - SanitizedMessage::try_from_legacy_message(message, &ReservedAccountKeys::empty_key_set()) + SanitizedMessage::try_from_legacy_message(message, &ReservedAccountKeys::empty_key_set(), true) .unwrap() } diff --git a/runtime/src/bank_client.rs b/runtime/src/bank_client.rs index b702a3e7502..30ef60bf5eb 100644 --- a/runtime/src/bank_client.rs +++ b/runtime/src/bank_client.rs @@ -6,6 +6,7 @@ use { client::{AsyncClient, Client, SyncClient}, commitment_config::CommitmentConfig, epoch_info::EpochInfo, + feature_set, hash::Hash, instruction::Instruction, message::{Message, SanitizedMessage}, @@ -233,9 +234,14 @@ impl SyncClient for BankClient { } fn get_fee_for_message(&self, message: &Message) -> Result { + let enable_loader_v4 = self + .bank + .feature_set + .is_active(&feature_set::enable_loader_v4::id()); SanitizedMessage::try_from_legacy_message( message.clone(), self.bank.get_reserved_account_keys(), + enable_loader_v4, ) .ok() .and_then(|sanitized_message| self.bank.get_fee_for_message(&sanitized_message)) diff --git a/sdk/benches/serialize_instructions.rs b/sdk/benches/serialize_instructions.rs index 399cc0cde55..68a32b62970 100644 --- a/sdk/benches/serialize_instructions.rs +++ b/sdk/benches/serialize_instructions.rs @@ -33,6 +33,7 @@ fn bench_construct_instructions_data(b: &mut Bencher) { let message = SanitizedMessage::try_from_legacy_message( Message::new(&instructions, Some(&Pubkey::new_unique())), &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); b.iter(|| { @@ -56,6 +57,7 @@ fn bench_manual_instruction_deserialize(b: &mut Bencher) { let message = SanitizedMessage::try_from_legacy_message( Message::new(&instructions, Some(&Pubkey::new_unique())), &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); let serialized = construct_instructions_data(&message.decompile_instructions()); @@ -73,6 +75,7 @@ fn bench_manual_instruction_deserialize_single(b: &mut Bencher) { let message = SanitizedMessage::try_from_legacy_message( Message::new(&instructions, Some(&Pubkey::new_unique())), &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); let serialized = construct_instructions_data(&message.decompile_instructions()); diff --git a/sdk/message/src/legacy.rs b/sdk/message/src/legacy.rs index 1c467e9b7df..995c91d8d10 100644 --- a/sdk/message/src/legacy.rs +++ b/sdk/message/src/legacy.rs @@ -28,7 +28,8 @@ use { solana_pubkey::Pubkey, solana_sanitize::{Sanitize, SanitizeError}, solana_sdk_ids::{ - bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, system_program, sysvar, + bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4, system_program, + sysvar, }, std::{collections::HashSet, convert::TryFrom, str::FromStr}, }; @@ -639,8 +640,8 @@ impl Message { self.program_position(i).is_some() } - pub fn demote_program_id(&self, i: usize) -> bool { - self.is_key_called_as_program(i) && !self.is_upgradeable_loader_present() + pub fn demote_program_id(&self, i: usize, enable_loader_v4: bool) -> bool { + self.is_key_called_as_program(i) && !self.is_upgradeable_loader_present(enable_loader_v4) } /// Returns true if the account at the specified index was requested to be @@ -659,10 +660,10 @@ impl Message { /// runtime. #[deprecated(since = "2.0.0", note = "Please use `is_maybe_writable` instead")] #[allow(deprecated)] - pub fn is_writable(&self, i: usize) -> bool { + pub fn is_writable(&self, i: usize, enable_loader_v4: bool) -> bool { (self.is_writable_index(i)) && !is_builtin_key_or_sysvar(&self.account_keys[i]) - && !self.demote_program_id(i) + && !self.demote_program_id(i, enable_loader_v4) } /// Returns true if the account at the specified index is writable by the @@ -675,10 +676,11 @@ impl Message { &self, i: usize, reserved_account_keys: Option<&HashSet>, + enable_loader_v4: bool, ) -> bool { (self.is_writable_index(i)) && !self.is_account_maybe_reserved(i, reserved_account_keys) - && !self.demote_program_id(i) + && !self.demote_program_id(i, enable_loader_v4) } /// Returns true if the account at the specified index is in the optional @@ -724,11 +726,11 @@ impl Message { false } - /// Returns `true` if any account is the BPF upgradeable loader. - pub fn is_upgradeable_loader_present(&self) -> bool { - self.account_keys - .iter() - .any(|&key| key == bpf_loader_upgradeable::id()) + /// Inspect all message keys for loader v3 or v4 + pub fn is_upgradeable_loader_present(&self, enable_loader_v4: bool) -> bool { + self.account_keys.iter().any(|key| { + bpf_loader_upgradeable::check_id(key) || (enable_loader_v4 && loader_v4::check_id(key)) + }) } } @@ -862,12 +864,12 @@ mod tests { recent_blockhash: Hash::default(), instructions: vec![], }; - assert!(message.is_writable(0)); - assert!(!message.is_writable(1)); - assert!(!message.is_writable(2)); - assert!(message.is_writable(3)); - assert!(message.is_writable(4)); - assert!(!message.is_writable(5)); + assert!(message.is_writable(0, true)); + assert!(!message.is_writable(1, true)); + assert!(!message.is_writable(2, true)); + assert!(message.is_writable(3, true)); + assert!(message.is_writable(4, true)); + assert!(!message.is_writable(5, true)); } #[test] @@ -892,14 +894,14 @@ mod tests { let reserved_account_keys = HashSet::from([key3]); - assert!(message.is_maybe_writable(0, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(1, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(2, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(3, Some(&reserved_account_keys))); - assert!(message.is_maybe_writable(3, None)); - assert!(message.is_maybe_writable(4, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(5, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(6, Some(&reserved_account_keys))); + assert!(message.is_maybe_writable(0, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(1, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(2, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(3, Some(&reserved_account_keys), true)); + assert!(message.is_maybe_writable(3, None, true)); + assert!(message.is_maybe_writable(4, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(5, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(6, Some(&reserved_account_keys), true)); } #[test] diff --git a/sdk/message/src/sanitized.rs b/sdk/message/src/sanitized.rs index 92f6e23f557..b0a38f2e297 100644 --- a/sdk/message/src/sanitized.rs +++ b/sdk/message/src/sanitized.rs @@ -37,7 +37,11 @@ pub struct LegacyMessage<'a> { } impl LegacyMessage<'_> { - pub fn new(message: legacy::Message, reserved_account_keys: &HashSet) -> Self { + pub fn new( + message: legacy::Message, + reserved_account_keys: &HashSet, + enable_loader_v4: bool, + ) -> Self { let is_writable_account_cache = message .account_keys .iter() @@ -45,7 +49,7 @@ impl LegacyMessage<'_> { .map(|(i, _key)| { message.is_writable_index(i) && !reserved_account_keys.contains(&message.account_keys[i]) - && !message.demote_program_id(i) + && !message.demote_program_id(i, enable_loader_v4) }) .collect::>(); Self { @@ -62,9 +66,9 @@ impl LegacyMessage<'_> { self.message.is_key_called_as_program(key_index) } - /// Inspect all message keys for the bpf upgradeable loader - pub fn is_upgradeable_loader_present(&self) -> bool { - self.message.is_upgradeable_loader_present() + /// Inspect all message keys for loader v3 or v4 + pub fn is_upgradeable_loader_present(&self, enable_loader_v4: bool) -> bool { + self.message.is_upgradeable_loader_present(enable_loader_v4) } /// Returns the full list of account keys. @@ -94,11 +98,14 @@ impl SanitizedMessage { sanitized_msg: SanitizedVersionedMessage, address_loader: impl AddressLoader, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Result { Ok(match sanitized_msg.message { - VersionedMessage::Legacy(message) => { - SanitizedMessage::Legacy(LegacyMessage::new(message, reserved_account_keys)) - } + VersionedMessage::Legacy(message) => SanitizedMessage::Legacy(LegacyMessage::new( + message, + reserved_account_keys, + enable_loader_v4, + )), VersionedMessage::V0(message) => { let loaded_addresses = address_loader.load_addresses(&message.address_table_lookups)?; @@ -106,6 +113,7 @@ impl SanitizedMessage { message, loaded_addresses, reserved_account_keys, + enable_loader_v4, )) } }) @@ -115,11 +123,13 @@ impl SanitizedMessage { pub fn try_from_legacy_message( message: legacy::Message, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Result { message.sanitize()?; Ok(Self::Legacy(LegacyMessage::new( message, reserved_account_keys, + enable_loader_v4, ))) } @@ -312,10 +322,10 @@ impl SanitizedMessage { } /// Inspect all message keys for the bpf upgradeable loader - pub fn is_upgradeable_loader_present(&self) -> bool { + pub fn is_upgradeable_loader_present(&self, enable_loader_v4: bool) -> bool { match self { - Self::Legacy(message) => message.is_upgradeable_loader_present(), - Self::V0(message) => message.is_upgradeable_loader_present(), + Self::Legacy(message) => message.is_upgradeable_loader_present(enable_loader_v4), + Self::V0(message) => message.is_upgradeable_loader_present(enable_loader_v4), } } @@ -494,6 +504,7 @@ mod tests { SanitizedMessage::try_from_legacy_message( legacy_message_with_no_signers, &HashSet::default(), + true, ) .err(), Some(SanitizeMessageError::IndexOutOfBounds), @@ -521,6 +532,7 @@ mod tests { instructions, ), &HashSet::default(), + true, ) .unwrap(); @@ -549,6 +561,7 @@ mod tests { ..legacy::Message::default() }, &HashSet::default(), + true, ) .unwrap(); @@ -569,6 +582,7 @@ mod tests { readonly: vec![key5], }, &HashSet::default(), + true, )); assert_eq!(v0_message.num_readonly_accounts(), 3); @@ -596,6 +610,7 @@ mod tests { instructions, ), &HashSet::default(), + true, ) .unwrap(); @@ -638,6 +653,7 @@ mod tests { ..legacy::Message::default() }, &HashSet::default(), + true, ) .unwrap(); match legacy_message { @@ -671,6 +687,7 @@ mod tests { readonly: vec![key5], }, &HashSet::default(), + true, )); match v0_message { SanitizedMessage::V0(message) => { @@ -722,6 +739,7 @@ mod tests { ], ), &HashSet::new(), + true, ) .unwrap(); @@ -755,6 +773,7 @@ mod tests { ..legacy::Message::default() }, &HashSet::default(), + true, ) .unwrap(); assert_eq!(legacy_message.static_account_keys(), &keys); @@ -770,6 +789,7 @@ mod tests { readonly: vec![], }, &HashSet::default(), + true, )); assert_eq!(v0_message.static_account_keys(), &keys); @@ -784,6 +804,7 @@ mod tests { readonly: vec![Pubkey::new_unique()], }, &HashSet::default(), + true, )); assert_eq!(v0_message.static_account_keys(), &keys); } diff --git a/sdk/message/src/versions/mod.rs b/sdk/message/src/versions/mod.rs index 99918fefff9..f52062546e8 100644 --- a/sdk/message/src/versions/mod.rs +++ b/sdk/message/src/versions/mod.rs @@ -90,10 +90,15 @@ impl VersionedMessage { &self, index: usize, reserved_account_keys: Option<&HashSet>, + enable_loader_v4: bool, ) -> bool { match self { - Self::Legacy(message) => message.is_maybe_writable(index, reserved_account_keys), - Self::V0(message) => message.is_maybe_writable(index, reserved_account_keys), + Self::Legacy(message) => { + message.is_maybe_writable(index, reserved_account_keys, enable_loader_v4) + } + Self::V0(message) => { + message.is_maybe_writable(index, reserved_account_keys, enable_loader_v4) + } } } diff --git a/sdk/message/src/versions/v0/loaded.rs b/sdk/message/src/versions/v0/loaded.rs index 94325007e01..b2781d4d4d2 100644 --- a/sdk/message/src/versions/v0/loaded.rs +++ b/sdk/message/src/versions/v0/loaded.rs @@ -3,7 +3,7 @@ use serde_derive::{Deserialize, Serialize}; use { crate::{v0, AccountKeys}, solana_pubkey::Pubkey, - solana_sdk_ids::bpf_loader_upgradeable, + solana_sdk_ids::{bpf_loader_upgradeable, loader_v4}, std::{borrow::Cow, collections::HashSet}, }; @@ -60,13 +60,14 @@ impl<'a> LoadedMessage<'a> { message: v0::Message, loaded_addresses: LoadedAddresses, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Self { let mut loaded_message = Self { message: Cow::Owned(message), loaded_addresses: Cow::Owned(loaded_addresses), is_writable_account_cache: Vec::default(), }; - loaded_message.set_is_writable_account_cache(reserved_account_keys); + loaded_message.set_is_writable_account_cache(reserved_account_keys, enable_loader_v4); loaded_message } @@ -74,22 +75,27 @@ impl<'a> LoadedMessage<'a> { message: &'a v0::Message, loaded_addresses: &'a LoadedAddresses, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Self { let mut loaded_message = Self { message: Cow::Borrowed(message), loaded_addresses: Cow::Borrowed(loaded_addresses), is_writable_account_cache: Vec::default(), }; - loaded_message.set_is_writable_account_cache(reserved_account_keys); + loaded_message.set_is_writable_account_cache(reserved_account_keys, enable_loader_v4); loaded_message } - fn set_is_writable_account_cache(&mut self, reserved_account_keys: &HashSet) { + fn set_is_writable_account_cache( + &mut self, + reserved_account_keys: &HashSet, + enable_loader_v4: bool, + ) { let is_writable_account_cache = self .account_keys() .iter() .enumerate() - .map(|(i, _key)| self.is_writable_internal(i, reserved_account_keys)) + .map(|(i, _key)| self.is_writable_internal(i, reserved_account_keys, enable_loader_v4)) .collect::>(); let _ = std::mem::replace( &mut self.is_writable_account_cache, @@ -140,10 +146,12 @@ impl<'a> LoadedMessage<'a> { &self, key_index: usize, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> bool { if self.is_writable_index(key_index) { if let Some(key) = self.account_keys().get(key_index) { - return !(reserved_account_keys.contains(key) || self.demote_program_id(key_index)); + return !(reserved_account_keys.contains(key) + || self.demote_program_id(key_index, enable_loader_v4)); } } false @@ -160,8 +168,8 @@ impl<'a> LoadedMessage<'a> { i < self.message.header.num_required_signatures as usize } - pub fn demote_program_id(&self, i: usize) -> bool { - self.is_key_called_as_program(i) && !self.is_upgradeable_loader_present() + pub fn demote_program_id(&self, i: usize, enable_loader_v4: bool) -> bool { + self.is_key_called_as_program(i) && !self.is_upgradeable_loader_present(enable_loader_v4) } /// Returns true if the account at the specified index is called as a program by an instruction @@ -176,11 +184,11 @@ impl<'a> LoadedMessage<'a> { } } - /// Returns true if any account is the bpf upgradeable loader - pub fn is_upgradeable_loader_present(&self) -> bool { - self.account_keys() - .iter() - .any(|&key| key == bpf_loader_upgradeable::id()) + /// Inspect all message keys for loader v3 or v4 + pub fn is_upgradeable_loader_present(&self, enable_loader_v4: bool) -> bool { + self.account_keys().iter().any(|key| { + bpf_loader_upgradeable::check_id(key) || (enable_loader_v4 && loader_v4::check_id(key)) + }) } } @@ -216,6 +224,7 @@ mod tests { readonly: vec![key5], }, &HashSet::default(), + true, ); (message, [key0, key1, key2, key3, key4, key5]) @@ -241,6 +250,7 @@ mod tests { readonly: keys, }, &HashSet::default(), + true, ) }; @@ -289,6 +299,7 @@ mod tests { readonly: keys[3..].to_vec(), }, &reserved_account_keys, + true, ) }; @@ -339,6 +350,7 @@ mod tests { readonly: vec![], }, &HashSet::default(), + true, ); assert!(message.is_writable_index(2)); diff --git a/sdk/message/src/versions/v0/mod.rs b/sdk/message/src/versions/v0/mod.rs index 893a0664b8f..08aed29b903 100644 --- a/sdk/message/src/versions/v0/mod.rs +++ b/sdk/message/src/versions/v0/mod.rs @@ -24,7 +24,7 @@ use { solana_instruction::Instruction, solana_pubkey::Pubkey, solana_sanitize::SanitizeError, - solana_sdk_ids::bpf_loader_upgradeable, + solana_sdk_ids::{bpf_loader_upgradeable, loader_v4}, std::collections::HashSet, }; @@ -339,11 +339,11 @@ impl Message { } } - /// Returns true if any static account key is the bpf upgradeable loader - fn is_upgradeable_loader_in_static_keys(&self) -> bool { - self.account_keys - .iter() - .any(|&key| key == bpf_loader_upgradeable::id()) + /// Returns true if any static account key is loader v3 or v4 + fn is_upgradeable_loader_in_static_keys(&self, enable_loader_v4: bool) -> bool { + self.account_keys.iter().any(|key| { + bpf_loader_upgradeable::check_id(key) || (enable_loader_v4 && loader_v4::check_id(key)) + }) } /// Returns true if the account at the specified index was requested as @@ -355,13 +355,14 @@ impl Message { &self, key_index: usize, reserved_account_keys: Option<&HashSet>, + enable_loader_v4: bool, ) -> bool { self.is_writable_index(key_index) && !self.is_account_maybe_reserved(key_index, reserved_account_keys) && !{ // demote program ids self.is_key_called_as_program(key_index) - && !self.is_upgradeable_loader_in_static_keys() + && !self.is_upgradeable_loader_in_static_keys(enable_loader_v4) } } @@ -738,16 +739,16 @@ mod tests { let reserved_account_keys = HashSet::from([key3]); - assert!(message.is_maybe_writable(0, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(1, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(2, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(3, Some(&reserved_account_keys))); - assert!(message.is_maybe_writable(3, None)); - assert!(message.is_maybe_writable(4, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(5, Some(&reserved_account_keys))); - assert!(message.is_maybe_writable(6, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(7, Some(&reserved_account_keys))); - assert!(!message.is_maybe_writable(8, Some(&reserved_account_keys))); + assert!(message.is_maybe_writable(0, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(1, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(2, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(3, Some(&reserved_account_keys), true)); + assert!(message.is_maybe_writable(3, None, true)); + assert!(message.is_maybe_writable(4, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(5, Some(&reserved_account_keys), true)); + assert!(message.is_maybe_writable(6, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(7, Some(&reserved_account_keys), true)); + assert!(!message.is_maybe_writable(8, Some(&reserved_account_keys), true)); } #[test] diff --git a/sdk/transaction/src/sanitized.rs b/sdk/transaction/src/sanitized.rs index 2d0f3639c2c..6d5ec66217f 100644 --- a/sdk/transaction/src/sanitized.rs +++ b/sdk/transaction/src/sanitized.rs @@ -64,13 +64,16 @@ impl SanitizedTransaction { is_simple_vote_tx: bool, address_loader: impl AddressLoader, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Result { let signatures = tx.signatures; let SanitizedVersionedMessage { message } = tx.message; let message = match message { - VersionedMessage::Legacy(message) => { - SanitizedMessage::Legacy(LegacyMessage::new(message, reserved_account_keys)) - } + VersionedMessage::Legacy(message) => SanitizedMessage::Legacy(LegacyMessage::new( + message, + reserved_account_keys, + enable_loader_v4, + )), VersionedMessage::V0(message) => { let loaded_addresses = address_loader.load_addresses(&message.address_table_lookups)?; @@ -78,6 +81,7 @@ impl SanitizedTransaction { message, loaded_addresses, reserved_account_keys, + enable_loader_v4, )) } }; @@ -100,6 +104,7 @@ impl SanitizedTransaction { is_simple_vote_tx: Option, address_loader: impl AddressLoader, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Result { let sanitized_versioned_tx = SanitizedVersionedTransaction::try_from(tx)?; let is_simple_vote_tx = is_simple_vote_tx.unwrap_or_else(|| { @@ -117,6 +122,7 @@ impl SanitizedTransaction { is_simple_vote_tx, address_loader, reserved_account_keys, + enable_loader_v4, ) } @@ -125,6 +131,7 @@ impl SanitizedTransaction { pub fn try_from_legacy_transaction( tx: Transaction, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Result { tx.sanitize()?; @@ -133,6 +140,7 @@ impl SanitizedTransaction { message: SanitizedMessage::Legacy(LegacyMessage::new( tx.message, reserved_account_keys, + enable_loader_v4, )), is_simple_vote_tx: false, signatures: tx.signatures, @@ -142,7 +150,7 @@ impl SanitizedTransaction { /// Create a sanitized transaction from a legacy transaction. Used for tests only. #[cfg(feature = "blake3")] pub fn from_transaction_for_tests(tx: Transaction) -> Self { - Self::try_from_legacy_transaction(tx, &ReservedAccountKeys::empty_key_set()).unwrap() + Self::try_from_legacy_transaction(tx, &ReservedAccountKeys::empty_key_set(), true).unwrap() } /// Create a sanitized transaction from fields. @@ -371,6 +379,7 @@ mod tests { None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); assert!(vote_transaction.is_simple_vote_transaction()); @@ -384,6 +393,7 @@ mod tests { Some(false), SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); assert!(!vote_transaction.is_simple_vote_transaction()); @@ -399,6 +409,7 @@ mod tests { None, SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); assert!(!vote_transaction.is_simple_vote_transaction()); @@ -412,6 +423,7 @@ mod tests { Some(true), SimpleAddressLoader::Disabled, &ReservedAccountKeys::empty_key_set(), + true, ) .unwrap(); assert!(vote_transaction.is_simple_vote_transaction()); @@ -435,6 +447,7 @@ mod tests { ..legacy::Message::default() }, &HashSet::default(), + true, ) .unwrap(); diff --git a/svm-transaction/src/tests.rs b/svm-transaction/src/tests.rs index 36c80eece4d..1cbe5dd27da 100644 --- a/svm-transaction/src/tests.rs +++ b/svm-transaction/src/tests.rs @@ -63,6 +63,7 @@ fn test_get_durable_nonce() { SanitizedVersionedMessage::try_new(versioned_message).unwrap(), loader, &HashSet::new(), + true, ) .unwrap() } @@ -268,6 +269,7 @@ fn test_get_ix_signers() { instructions, ), &HashSet::default(), + true, ) .unwrap(); diff --git a/svm/examples/json-rpc/server/src/rpc_process.rs b/svm/examples/json-rpc/server/src/rpc_process.rs index 0f9e33a9ccc..fdb41357ed5 100644 --- a/svm/examples/json-rpc/server/src/rpc_process.rs +++ b/svm/examples/json-rpc/server/src/rpc_process.rs @@ -900,6 +900,7 @@ fn sanitize_transaction( None, address_loader, reserved_account_keys, + true, ) .map_err(|err| Error::invalid_params(format!("invalid transaction: {err}"))) } diff --git a/svm/examples/paytube/src/transaction.rs b/svm/examples/paytube/src/transaction.rs index 8e27a3dbb31..7875cab85cc 100644 --- a/svm/examples/paytube/src/transaction.rs +++ b/svm/examples/paytube/src/transaction.rs @@ -66,6 +66,7 @@ impl From<&PayTubeTransaction> for SolanaSanitizedTransaction { SolanaSanitizedTransaction::try_from_legacy_transaction( SolanaTransaction::from(value), &HashSet::new(), + true, ) .unwrap() } diff --git a/svm/src/account_loader.rs b/svm/src/account_loader.rs index 236ba61c388..c0211122acb 100644 --- a/svm/src/account_loader.rs +++ b/svm/src/account_loader.rs @@ -768,6 +768,7 @@ mod tests { SanitizedMessage::Legacy(LegacyMessage::new( message, &ReservedAccountKeys::empty_key_set(), + true, )) } diff --git a/svm/src/message_processor.rs b/svm/src/message_processor.rs index 87363eee8be..29adc32490e 100644 --- a/svm/src/message_processor.rs +++ b/svm/src/message_processor.rs @@ -146,8 +146,12 @@ mod tests { }; fn new_sanitized_message(message: Message) -> SanitizedMessage { - SanitizedMessage::try_from_legacy_message(message, &ReservedAccountKeys::empty_key_set()) - .unwrap() + SanitizedMessage::try_from_legacy_message( + message, + &ReservedAccountKeys::empty_key_set(), + true, + ) + .unwrap() } #[test] diff --git a/svm/src/transaction_account_state_info.rs b/svm/src/transaction_account_state_info.rs index b8ce3785f8c..47e123c88f2 100644 --- a/svm/src/transaction_account_state_info.rs +++ b/svm/src/transaction_account_state_info.rs @@ -115,6 +115,7 @@ mod test { let sanitized_message = SanitizedMessage::Legacy(LegacyMessage::new( message, &ReservedAccountKeys::empty_key_set(), + true, )); let transaction_accounts = vec![ @@ -175,6 +176,7 @@ mod test { let sanitized_message = SanitizedMessage::Legacy(LegacyMessage::new( message, &ReservedAccountKeys::empty_key_set(), + true, )); let transaction_accounts = vec![ diff --git a/svm/src/transaction_processor.rs b/svm/src/transaction_processor.rs index 3fca871513c..5a681d567b6 100644 --- a/svm/src/transaction_processor.rs +++ b/svm/src/transaction_processor.rs @@ -1235,6 +1235,7 @@ mod tests { SanitizedMessage::Legacy(LegacyMessage::new( message, &ReservedAccountKeys::empty_key_set(), + true, )) } diff --git a/svm/tests/transaction_builder.rs b/svm/tests/transaction_builder.rs index b3b1757cfa6..3f7085fc11a 100644 --- a/svm/tests/transaction_builder.rs +++ b/svm/tests/transaction_builder.rs @@ -227,6 +227,7 @@ impl SanitizedTransactionBuilder { } else { reserved_active }, + true, ) } diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index 9230c97f685..dd1fcd85179 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -559,6 +559,7 @@ impl VersionedTransactionWithStatusMeta { message, &self.meta.loaded_addresses, &reserved_account_keys.active, + true, ); parse_v0_message_accounts(&loaded_message) } @@ -768,7 +769,7 @@ impl Encodable for v0::Message { let account_keys = AccountKeys::new(&self.account_keys, None); let loaded_addresses = LoadedAddresses::default(); let loaded_message = - LoadedMessage::new_borrowed(self, &loaded_addresses, &HashSet::new()); + LoadedMessage::new_borrowed(self, &loaded_addresses, &HashSet::new(), true); UiMessage::Parsed(UiParsedMessage { account_keys: parse_v0_message_accounts(&loaded_message), recent_blockhash: self.recent_blockhash.to_string(), @@ -809,6 +810,7 @@ impl EncodableWithMeta for v0::Message { self, &meta.loaded_addresses, &reserved_account_keys.active, + true, ); UiMessage::Parsed(UiParsedMessage { account_keys: parse_v0_message_accounts(&loaded_message), diff --git a/transaction-status/src/parse_accounts.rs b/transaction-status/src/parse_accounts.rs index b40150e440f..131914b9020 100644 --- a/transaction-status/src/parse_accounts.rs +++ b/transaction-status/src/parse_accounts.rs @@ -10,7 +10,7 @@ pub fn parse_legacy_message_accounts(message: &Message) -> Vec { for (i, account_key) in message.account_keys.iter().enumerate() { accounts.push(ParsedAccount { pubkey: account_key.to_string(), - writable: message.is_maybe_writable(i, Some(&reserved_account_keys)), + writable: message.is_maybe_writable(i, Some(&reserved_account_keys), true), signer: message.is_signer(i), source: Some(ParsedAccountSource::Transaction), }); @@ -115,6 +115,7 @@ mod test { readonly: vec![pubkey5], }, &ReservedAccountKeys::empty_key_set(), + true, ); assert_eq!( diff --git a/transaction-view/src/resolved_transaction_view.rs b/transaction-view/src/resolved_transaction_view.rs index 039af79284b..8db2032e3d9 100644 --- a/transaction-view/src/resolved_transaction_view.rs +++ b/transaction-view/src/resolved_transaction_view.rs @@ -12,7 +12,7 @@ use { solana_hash::Hash, solana_message::{v0::LoadedAddresses, AccountKeys}, solana_pubkey::Pubkey, - solana_sdk_ids::bpf_loader_upgradeable, + solana_sdk_ids::{bpf_loader_upgradeable, loader_v4}, solana_signature::Signature, solana_svm_transaction::{ instruction::SVMInstruction, message_address_table_lookup::SVMMessageAddressTableLookup, @@ -49,6 +49,7 @@ impl ResolvedTransactionView { view: TransactionView, resolved_addresses: Option, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> Result { let resolved_addresses_ref = resolved_addresses.as_ref(); @@ -73,8 +74,12 @@ impl ResolvedTransactionView { return Err(TransactionViewError::AddressLookupMismatch); } - let writable_cache = - Self::cache_is_writable(&view, resolved_addresses_ref, reserved_account_keys); + let writable_cache = Self::cache_is_writable( + &view, + resolved_addresses_ref, + reserved_account_keys, + enable_loader_v4, + ); Ok(Self { view, resolved_addresses, @@ -90,6 +95,7 @@ impl ResolvedTransactionView { view: &TransactionView, resolved_addresses: Option<&LoadedAddresses>, reserved_account_keys: &HashSet, + enable_loader_v4: bool, ) -> [bool; 256] { // Build account keys so that we can iterate over and check if // an address is writable. @@ -132,7 +138,9 @@ impl ResolvedTransactionView { if is_writable_cache[program_id_index] && !*is_upgradable_loader_present.get_or_insert_with(|| { for key in account_keys.iter() { - if key == &bpf_loader_upgradeable::ID { + if bpf_loader_upgradeable::check_id(key) + || (enable_loader_v4 && loader_v4::check_id(key)) + { return true; } } @@ -277,7 +285,7 @@ mod tests { }; let bytes = bincode::serialize(&transaction).unwrap(); let view = SanitizedTransactionView::try_new_sanitized(bytes.as_ref()).unwrap(); - let result = ResolvedTransactionView::try_new(view, None, &HashSet::default()); + let result = ResolvedTransactionView::try_new(view, None, &HashSet::default(), true); assert!(matches!( result, Err(TransactionViewError::AddressLookupMismatch) @@ -308,8 +316,12 @@ mod tests { }; let bytes = bincode::serialize(&transaction).unwrap(); let view = SanitizedTransactionView::try_new_sanitized(bytes.as_ref()).unwrap(); - let result = - ResolvedTransactionView::try_new(view, Some(loaded_addresses), &HashSet::default()); + let result = ResolvedTransactionView::try_new( + view, + Some(loaded_addresses), + &HashSet::default(), + true, + ); assert!(matches!( result, Err(TransactionViewError::AddressLookupMismatch) @@ -345,8 +357,12 @@ mod tests { }; let bytes = bincode::serialize(&transaction).unwrap(); let view = SanitizedTransactionView::try_new_sanitized(bytes.as_ref()).unwrap(); - let result = - ResolvedTransactionView::try_new(view, Some(loaded_addresses), &HashSet::default()); + let result = ResolvedTransactionView::try_new( + view, + Some(loaded_addresses), + &HashSet::default(), + true, + ); assert!(matches!( result, Err(TransactionViewError::AddressLookupMismatch) @@ -399,6 +415,7 @@ mod tests { view, Some(loaded_addresses), &reserved_account_keys, + true, ) .unwrap(); @@ -422,6 +439,7 @@ mod tests { view, Some(loaded_addresses), &reserved_account_keys, + true, ) .unwrap(); @@ -445,6 +463,7 @@ mod tests { view, Some(loaded_addresses), &reserved_account_keys, + true, ) .unwrap(); @@ -506,6 +525,7 @@ mod tests { view, Some(loaded_addresses.clone()), &reserved_account_keys, + true, ) .unwrap(); @@ -525,6 +545,7 @@ mod tests { view, Some(loaded_addresses.clone()), &reserved_account_keys, + true, ) .unwrap(); @@ -549,6 +570,7 @@ mod tests { view, Some(loaded_addresses.clone()), &reserved_account_keys, + true, ) .unwrap();