@@ -332,6 +332,7 @@ use {
332332 solana_hash:: Hash ,
333333 solana_keypair:: Keypair ,
334334 solana_last_restart_slot:: LastRestartSlot ,
335+ solana_loader_v3_interface:: state:: UpgradeableLoaderState ,
335336 solana_message:: {
336337 inner_instruction:: InnerInstructionsList , Message , SanitizedMessage , VersionedMessage ,
337338 } ,
@@ -342,7 +343,9 @@ use {
342343 loaded_programs:: { LoadProgramMetrics , ProgramCacheEntry } ,
343344 } ,
344345 solana_rent:: Rent ,
345- solana_sdk_ids:: { bpf_loader, native_loader, system_program} ,
346+ solana_sdk_ids:: {
347+ bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, native_loader, system_program,
348+ } ,
346349 solana_signature:: Signature ,
347350 solana_signer:: Signer ,
348351 solana_slot_hashes:: SlotHashes ,
@@ -826,62 +829,129 @@ impl LiteSVM {
826829 Ok ( ( ) )
827830 }
828831
829- /// Adds am SBF program to the test environment.
830832 fn add_program_internal < const PREVERIFIED : bool > (
831833 & mut self ,
832834 program_id : impl Into < Address > ,
833835 program_bytes : & [ u8 ] ,
836+ loader_id : & Address ,
834837 ) -> Result < ( ) , LiteSVMError > {
835838 let program_id = program_id. into ( ) ;
836- let program_len = program_bytes. len ( ) ;
837- let lamports = self . minimum_balance_for_rent_exemption ( program_len) ;
838- let mut account = AccountSharedData :: new ( lamports, program_len, & bpf_loader:: id ( ) ) ;
839- account. set_executable ( true ) ;
840- account. set_data_from_slice ( program_bytes) ;
841839 let current_slot = self
842840 . accounts
843841 . sysvar_cache
844842 . get_clock ( )
845843 . unwrap_or_default ( )
846844 . slot ;
845+
846+ let program_size = if bpf_loader_upgradeable:: check_id ( loader_id) {
847+ let ( programdata_address, _bump) =
848+ Address :: find_program_address ( & [ program_id. as_ref ( ) ] , loader_id) ;
849+
850+ let programdata_metadata_len = UpgradeableLoaderState :: size_of_programdata_metadata ( ) ;
851+ let programdata_len = programdata_metadata_len + program_bytes. len ( ) ;
852+ let mut programdata_data = vec ! [ 0u8 ; programdata_len] ;
853+
854+ bincode:: serialize_into (
855+ & mut programdata_data[ ..programdata_metadata_len] ,
856+ & UpgradeableLoaderState :: ProgramData {
857+ slot : current_slot,
858+ upgrade_authority_address : None ,
859+ } ,
860+ )
861+ . expect ( "UpgradeableLoaderState::ProgramData serialization should never fail" ) ;
862+ programdata_data[ programdata_metadata_len..] . copy_from_slice ( program_bytes) ;
863+
864+ let programdata_lamports = self . minimum_balance_for_rent_exemption ( programdata_len) ;
865+ let mut programdata_account =
866+ AccountSharedData :: new ( programdata_lamports, programdata_len, loader_id) ;
867+ programdata_account. set_data_from_slice ( & programdata_data) ;
868+
869+ let program_account_data = bincode:: serialize ( & UpgradeableLoaderState :: Program {
870+ programdata_address,
871+ } )
872+ . expect ( "UpgradeableLoaderState::Program serialization should never fail" ) ;
873+
874+ let program_lamports =
875+ self . minimum_balance_for_rent_exemption ( program_account_data. len ( ) ) ;
876+ let mut program_account =
877+ AccountSharedData :: new ( program_lamports, program_account_data. len ( ) , loader_id) ;
878+ program_account. set_executable ( true ) ;
879+ program_account. set_data_from_slice ( & program_account_data) ;
880+
881+ self . accounts
882+ . add_account_no_checks ( programdata_address, programdata_account) ;
883+ self . accounts
884+ . add_account_no_checks ( program_id, program_account) ;
885+
886+ programdata_len
887+ } else if bpf_loader:: check_id ( loader_id) || bpf_loader_deprecated:: check_id ( loader_id) {
888+ let program_len = program_bytes. len ( ) ;
889+ let lamports = self . minimum_balance_for_rent_exemption ( program_len) ;
890+ let mut account = AccountSharedData :: new ( lamports, program_len, loader_id) ;
891+ account. set_executable ( true ) ;
892+ account. set_data_from_slice ( program_bytes) ;
893+
894+ self . accounts . add_account_no_checks ( program_id, account) ;
895+
896+ program_len
897+ } else {
898+ return Err ( LiteSVMError :: InvalidLoader ( format ! (
899+ "Unsupported loader: {loader_id}"
900+ ) ) ) ;
901+ } ;
902+
847903 let mut loaded_program = solana_bpf_loader_program:: load_program_from_bytes (
848904 None ,
849905 & mut LoadProgramMetrics :: default ( ) ,
850- account . data ( ) ,
851- account . owner ( ) ,
852- account . data ( ) . len ( ) ,
906+ program_bytes ,
907+ loader_id ,
908+ program_size ,
853909 current_slot,
854910 self . accounts . environments . program_runtime_v1 . clone ( ) ,
855911 PREVERIFIED ,
856912 )
857913 . map_err ( LiteSVMError :: from) ?;
858914 loaded_program. effective_slot = current_slot;
859915
860- // We already loaded and validated (or explicitly trusted) the executable above.
861- // Insert the account directly to avoid a second program load.
862- self . accounts . add_account_no_checks ( program_id, account) ;
863916 self . accounts
864917 . programs_cache
865918 . replenish ( program_id, Arc :: new ( loaded_program) ) ;
919+
866920 Ok ( ( ) )
867921 }
868922
869923 /// Adds an SBF program to the test environment.
924+ ///
925+ /// Uses `BPFLoaderUpgradeable` by default for the loader.
870926 pub fn add_program (
871927 & mut self ,
872928 program_id : impl Into < Address > ,
873929 program_bytes : & [ u8 ] ,
874930 ) -> Result < ( ) , LiteSVMError > {
875- self . add_program_internal :: < false > ( program_id, program_bytes)
931+ self . add_program_internal :: < false > ( program_id, program_bytes, & bpf_loader_upgradeable:: id ( ) )
932+ }
933+
934+ /// Adds an SBF program with a specific loader to match mainnet CU behavior.
935+ ///
936+ /// Use `bpf_loader::id()` for BPFLoader2, `bpf_loader_deprecated::id()` for BPFLoader1,
937+ /// or `bpf_loader_upgradeable::id()` for the upgradeable loader.
938+ pub fn add_program_with_loader (
939+ & mut self ,
940+ program_id : impl Into < Address > ,
941+ program_bytes : & [ u8 ] ,
942+ loader_id : Address ,
943+ ) -> Result < ( ) , LiteSVMError > {
944+ self . add_program_internal :: < false > ( program_id, program_bytes, & loader_id)
876945 }
877946
878947 /// Adds an SBF program that is known-good and already verified.
879948 pub ( crate ) fn add_program_preverified (
880949 & mut self ,
881950 program_id : impl Into < Address > ,
882951 program_bytes : & [ u8 ] ,
952+ loader_id : & Address ,
883953 ) -> Result < ( ) , LiteSVMError > {
884- self . add_program_internal :: < true > ( program_id, program_bytes)
954+ self . add_program_internal :: < true > ( program_id, program_bytes, loader_id )
885955 }
886956
887957 fn create_transaction_context (
0 commit comments