From bc676ff6bae85a4c856001d654797e87d9b6ec7e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 22:11:09 +0100 Subject: [PATCH] v1.18: Cleanup - `disable_bpf_loader_instructions` (backport of #35164) (#35209) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup - `disable_bpf_loader_instructions` (#35164) * Cleans up disable_bpf_loader_instructions. * fix test_program_sbf_disguised_as_sbf_loader * remove bpf loader execute bench * Revert "remove bpf loader execute bench" This reverts commit f3042ee3e1d6e0208df7d7f80f61e14294f108a8. * move test utility functions out of test file * update bench to loader v3 * clippy * fix dev-context build * fix dev-context import * dev-context-util * move dev-context-util attr to module level for loader_utils --------- Co-authored-by: HaoranYi (cherry picked from commit d472725a122cef15fbe4e6607b18dde481e87be7) Co-authored-by: Alexander Meißner --- programs/bpf_loader/src/lib.rs | 213 +-------------------- programs/sbf/Cargo.toml | 1 + programs/sbf/benches/bpf_loader.rs | 26 +-- programs/sbf/tests/programs.rs | 289 +---------------------------- runtime/src/loader_utils.rs | 53 +++++- 5 files changed, 79 insertions(+), 503 deletions(-) diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 48d44b7187a658..212052dcf4cbe2 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -35,11 +35,10 @@ use { entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, feature_set::{ bpf_account_data_direct_mapping, deprecate_executable_meta_update_in_bpf_loader, - disable_bpf_loader_instructions, enable_bpf_loader_extend_program_ix, - enable_bpf_loader_set_authority_checked_ix, FeatureSet, + enable_bpf_loader_extend_program_ix, enable_bpf_loader_set_authority_checked_ix, + FeatureSet, }, instruction::{AccountMeta, InstructionError}, - loader_instruction::LoaderInstruction, loader_upgradeable_instruction::UpgradeableLoaderInstruction, native_loader, program_utils::limited_deserialize, @@ -387,7 +386,11 @@ pub fn process_instruction_inner( process_loader_upgradeable_instruction(invoke_context) } else if bpf_loader::check_id(program_id) { invoke_context.consume_checked(DEFAULT_LOADER_COMPUTE_UNITS)?; - process_loader_instruction(invoke_context) + ic_logger_msg!( + log_collector, + "BPF loader management instructions are no longer supported", + ); + Err(InstructionError::UnsupportedProgramId) } else if bpf_loader_deprecated::check_id(program_id) { invoke_context.consume_checked(DEPRECATED_LOADER_COMPUTE_UNITS)?; ic_logger_msg!(log_collector, "Deprecated loader is no longer supported"); @@ -1358,72 +1361,6 @@ fn common_close_account( Ok(()) } -fn process_loader_instruction(invoke_context: &mut InvokeContext) -> Result<(), InstructionError> { - let transaction_context = &invoke_context.transaction_context; - let instruction_context = transaction_context.get_current_instruction_context()?; - let instruction_data = instruction_context.get_instruction_data(); - let program_id = instruction_context.get_last_program_key(transaction_context)?; - let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - if program.get_owner() != program_id { - ic_msg!( - invoke_context, - "Executable account not owned by the BPF loader" - ); - return Err(InstructionError::IncorrectProgramId); - } - - // Return `UnsupportedProgramId` error for bpf_loader when - // `disable_bpf_loader_instruction` feature is activated. - if invoke_context - .feature_set - .is_active(&disable_bpf_loader_instructions::id()) - { - ic_msg!( - invoke_context, - "BPF loader management instructions are no longer supported" - ); - return Err(InstructionError::UnsupportedProgramId); - } - - let is_program_signer = program.is_signer(); - match limited_deserialize(instruction_data)? { - LoaderInstruction::Write { offset, bytes } => { - if !is_program_signer { - ic_msg!(invoke_context, "Program account did not sign"); - return Err(InstructionError::MissingRequiredSignature); - } - drop(program); - write_program_data(offset as usize, &bytes, invoke_context)?; - } - LoaderInstruction::Finalize => { - if !is_program_signer { - ic_msg!(invoke_context, "key[0] did not sign the transaction"); - return Err(InstructionError::MissingRequiredSignature); - } - deploy_program!( - invoke_context, - *program.get_key(), - program.get_owner(), - program.get_data().len(), - invoke_context.programs_loaded_for_tx_batch.slot(), - {}, - program.get_data(), - ); - - // `deprecate_executable_meta_update_in_bpf_loader` feature doesn't - // apply to bpf_loader v2. Instead, the deployment by bpf_loader - // will be deprecated by its own feature - // `disable_bpf_loader_instructions`. Before we activate - // deprecate_executable_meta_update_in_bpf_loader, we should - // activate `disable_bpf_loader_instructions` first. - program.set_executable(true)?; - ic_msg!(invoke_context, "Finalized account {:?}", program.get_key()); - } - } - - Ok(()) -} - fn execute<'a, 'b: 'a>( executable: &'a Executable>, invoke_context: &'a mut InvokeContext<'b>, @@ -1703,7 +1640,6 @@ mod tests { Entrypoint::vm, |invoke_context| { let mut features = FeatureSet::all_enabled(); - features.deactivate(&disable_bpf_loader_instructions::id()); features.deactivate(&deprecate_executable_meta_update_in_bpf_loader::id()); invoke_context.feature_set = Arc::new(features); test_utils::load_all_invoked_programs(invoke_context); @@ -1724,137 +1660,6 @@ mod tests { program_account } - #[test] - fn test_bpf_loader_write() { - let loader_id = bpf_loader::id(); - let program_id = Pubkey::new_unique(); - let mut program_account = AccountSharedData::new(1, 0, &loader_id); - let instruction_data = bincode::serialize(&LoaderInstruction::Write { - offset: 3, - bytes: vec![1, 2, 3], - }) - .unwrap(); - - // Case: No program account - process_instruction( - &loader_id, - &[], - &instruction_data, - Vec::new(), - Vec::new(), - Err(InstructionError::NotEnoughAccountKeys), - ); - - // Case: Not signed - process_instruction( - &loader_id, - &[], - &instruction_data, - vec![(program_id, program_account.clone())], - vec![AccountMeta { - pubkey: program_id, - is_signer: false, - is_writable: true, - }], - Err(InstructionError::MissingRequiredSignature), - ); - - // Case: Write bytes to an offset - program_account.set_data(vec![0; 6]); - let accounts = process_instruction( - &loader_id, - &[], - &instruction_data, - vec![(program_id, program_account.clone())], - vec![AccountMeta { - pubkey: program_id, - is_signer: true, - is_writable: true, - }], - Ok(()), - ); - assert_eq!(&vec![0, 0, 0, 1, 2, 3], accounts.first().unwrap().data()); - - // Case: Overflow - program_account.set_data(vec![0; 5]); - process_instruction( - &loader_id, - &[], - &instruction_data, - vec![(program_id, program_account)], - vec![AccountMeta { - pubkey: program_id, - is_signer: true, - is_writable: true, - }], - Err(InstructionError::AccountDataTooSmall), - ); - } - - #[test] - fn test_bpf_loader_finalize() { - let loader_id = bpf_loader::id(); - let program_id = Pubkey::new_unique(); - let mut program_account = - load_program_account_from_elf(&loader_id, "test_elfs/out/noop_aligned.so"); - program_account.set_executable(false); - let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap(); - - // Case: No program account - process_instruction( - &loader_id, - &[], - &instruction_data, - Vec::new(), - Vec::new(), - Err(InstructionError::NotEnoughAccountKeys), - ); - - // Case: Not signed - process_instruction( - &loader_id, - &[], - &instruction_data, - vec![(program_id, program_account.clone())], - vec![AccountMeta { - pubkey: program_id, - is_signer: false, - is_writable: true, - }], - Err(InstructionError::MissingRequiredSignature), - ); - - // Case: Finalize - let accounts = process_instruction( - &loader_id, - &[], - &instruction_data, - vec![(program_id, program_account.clone())], - vec![AccountMeta { - pubkey: program_id, - is_signer: true, - is_writable: true, - }], - Ok(()), - ); - assert!(accounts.first().unwrap().executable()); - - // Case: Finalize bad ELF - *program_account.data_as_mut_slice().get_mut(0).unwrap() = 0; - process_instruction( - &loader_id, - &[], - &instruction_data, - vec![(program_id, program_account)], - vec![AccountMeta { - pubkey: program_id, - is_signer: true, - is_writable: true, - }], - Err(InstructionError::InvalidAccountData), - ); - } - #[test] fn test_bpf_loader_invoke_main() { let loader_id = bpf_loader::id(); @@ -1876,7 +1681,7 @@ mod tests { &[], Vec::new(), Vec::new(), - Err(InstructionError::NotEnoughAccountKeys), + Err(InstructionError::UnsupportedProgramId), ); // Case: Only a program account @@ -1926,7 +1731,6 @@ mod tests { Entrypoint::vm, |invoke_context| { let mut features = FeatureSet::all_enabled(); - features.deactivate(&disable_bpf_loader_instructions::id()); features.deactivate(&deprecate_executable_meta_update_in_bpf_loader::id()); invoke_context.feature_set = Arc::new(features); invoke_context.mock_set_remaining(0); @@ -2476,7 +2280,6 @@ mod tests { Entrypoint::vm, |invoke_context| { let mut features = FeatureSet::all_enabled(); - features.deactivate(&disable_bpf_loader_instructions::id()); features.deactivate(&deprecate_executable_meta_update_in_bpf_loader::id()); invoke_context.feature_set = Arc::new(features); }, diff --git a/programs/sbf/Cargo.toml b/programs/sbf/Cargo.toml index d9d3f1a96b0efd..4ff635f0366648 100644 --- a/programs/sbf/Cargo.toml +++ b/programs/sbf/Cargo.toml @@ -101,6 +101,7 @@ solana_rbpf = { workspace = true } [dev-dependencies] solana-ledger = { workspace = true } +solana-runtime = { workspace = true, features = ["dev-context-only-utils"] } solana-sdk = { workspace = true, features = ["dev-context-only-utils"] } [[bench]] diff --git a/programs/sbf/benches/bpf_loader.rs b/programs/sbf/benches/bpf_loader.rs index f433c8374d8e47..47c55245000df1 100644 --- a/programs/sbf/benches/bpf_loader.rs +++ b/programs/sbf/benches/bpf_loader.rs @@ -6,7 +6,8 @@ use { solana_rbpf::memory_region::MemoryState, - solana_sdk::feature_set::bpf_account_data_direct_mapping, std::slice, + solana_sdk::{feature_set::bpf_account_data_direct_mapping, signer::keypair::Keypair}, + std::slice, }; extern crate test; @@ -27,7 +28,7 @@ use { bank::Bank, bank_client::BankClient, genesis_utils::{create_genesis_config, GenesisConfigInfo}, - loader_utils::{load_program, load_program_from_file}, + loader_utils::{load_program_from_file, load_upgradeable_program_and_advance_slot}, }, solana_sdk::{ account::AccountSharedData, @@ -190,12 +191,6 @@ fn bench_program_execute_noop(bencher: &mut Bencher) { .. } = create_genesis_config(50); - // deactivate `disable_bpf_loader_instructions` feature so that the program - // can be loaded, finalized and benched. - genesis_config - .accounts - .remove(&feature_set::disable_bpf_loader_instructions::id()); - genesis_config .accounts .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); @@ -204,12 +199,17 @@ fn bench_program_execute_noop(bencher: &mut Bencher) { let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); let mut bank_client = BankClient::new_shared(bank.clone()); - let invoke_program_id = load_program(&bank_client, &bpf_loader::id(), &mint_keypair, "noop"); - let bank = bank_client - .advance_slot(1, bank_forks.as_ref(), &Pubkey::default()) - .expect("Failed to advance the slot"); - + let authority_keypair = Keypair::new(); let mint_pubkey = mint_keypair.pubkey(); + + let (_, invoke_program_id) = load_upgradeable_program_and_advance_slot( + &mut bank_client, + bank_forks.as_ref(), + &mint_keypair, + &authority_keypair, + "noop", + ); + let account_metas = vec![AccountMeta::new(mint_pubkey, true)]; let instruction = diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index 8f8f9d2ffec92d..d67b57641446b5 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -28,9 +28,9 @@ use { solana_runtime::{ bank::TransactionBalancesSet, loader_utils::{ - create_program, load_and_finalize_program, load_program, load_program_from_file, - load_upgradeable_buffer, load_upgradeable_program, set_upgrade_authority, - upgrade_program, + create_program, load_program_from_file, load_upgradeable_buffer, + load_upgradeable_program, load_upgradeable_program_and_advance_slot, + load_upgradeable_program_wrapper, set_upgrade_authority, upgrade_program, }, }, solana_sbf_rust_invoke::instructions::*, @@ -45,12 +45,11 @@ use { entrypoint::MAX_PERMITTED_DATA_INCREASE, feature_set::{self, FeatureSet}, fee::FeeStructure, - loader_instruction, message::{v0::LoadedAddresses, SanitizedMessage}, signature::keypair_from_seed, stake, system_instruction::{self, MAX_PERMITTED_DATA_LENGTH}, - sysvar::{self, clock, rent}, + sysvar::{self, clock}, transaction::VersionedTransaction, }, solana_transaction_status::{ @@ -64,7 +63,6 @@ use { solana_runtime::{ bank::Bank, bank_client::BankClient, - bank_forks::BankForks, genesis_utils::{ bootstrap_validator_stake_lamports, create_genesis_config, create_genesis_config_with_leader_ex, GenesisConfigInfo, @@ -86,12 +84,7 @@ use { system_program, transaction::{SanitizedTransaction, Transaction, TransactionError}, }, - std::{ - cell::RefCell, - str::FromStr, - sync::{Arc, RwLock}, - time::Duration, - }, + std::{cell::RefCell, str::FromStr, sync::Arc, time::Duration}, }; #[cfg(feature = "sbf_rust")] @@ -248,64 +241,6 @@ fn execute_transactions( .collect() } -fn load_program_and_advance_slot( - bank_client: &mut BankClient, - bank_forks: &RwLock, - loader_id: &Pubkey, - payer_keypair: &Keypair, - name: &str, -) -> (Arc, Pubkey) { - let pubkey = load_program(bank_client, loader_id, payer_keypair, name); - ( - bank_client - .advance_slot(1, bank_forks, &Pubkey::default()) - .expect("Failed to advance the slot"), - pubkey, - ) -} - -fn load_upgradeable_program_wrapper( - bank_client: &BankClient, - mint_keypair: &Keypair, - authority_keypair: &Keypair, - name: &str, -) -> Pubkey { - let buffer_keypair = Keypair::new(); - let program_keypair = Keypair::new(); - load_upgradeable_program( - bank_client, - mint_keypair, - &buffer_keypair, - &program_keypair, - authority_keypair, - name, - ); - program_keypair.pubkey() -} - -fn load_upgradeable_program_and_advance_slot( - bank_client: &mut BankClient, - bank_forks: &RwLock, - mint_keypair: &Keypair, - authority_keypair: &Keypair, - name: &str, -) -> (Arc, Pubkey) { - let program_id = - load_upgradeable_program_wrapper(bank_client, mint_keypair, authority_keypair, name); - - // load_upgradeable_program sets clock sysvar to 1, which causes the program to be effective - // after 2 slots. They need to be called individually to create the correct fork graph in between. - bank_client - .advance_slot(1, bank_forks, &Pubkey::default()) - .expect("Failed to advance the slot"); - - let bank = bank_client - .advance_slot(1, bank_forks, &Pubkey::default()) - .expect("Failed to advance the slot"); - - (bank, program_id) -} - #[test] #[cfg(any(feature = "sbf_c", feature = "sbf_rust"))] fn test_program_sbf_sanity() { @@ -438,66 +373,6 @@ fn test_program_sbf_loader_deprecated() { } } -/// This test is written with bpf_loader v2 specific instructions, which will be -/// deprecated when `disable_bpf_loader_instructions` feature is activated. -/// -/// The same test has been migrated to -/// `test_sol_alloc_free_no_longer_deployable_with_upgradeable_loader` with a new version -/// of bpf_upgradeable_loader! -#[test] -#[cfg(feature = "sbf_rust")] -fn test_sol_alloc_free_no_longer_deployable() { - solana_logger::setup(); - - let program_keypair = Keypair::new(); - let program_address = program_keypair.pubkey(); - - let GenesisConfigInfo { - mut genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - - // deactivate `disable_bpf_loader_instructions` feature so that the program - // can be loaded, finalized and tested. - genesis_config - .accounts - .remove(&feature_set::disable_bpf_loader_instructions::id()); - - genesis_config - .accounts - .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); - - let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); - - let elf = load_program_from_file("solana_sbf_rust_deprecated_loader"); - let mut program_account = AccountSharedData::new(1, elf.len(), &bpf_loader::id()); - program_account - .data_as_mut_slice() - .get_mut(..) - .unwrap() - .copy_from_slice(&elf); - bank.store_account(&program_address, &program_account); - - let finalize_tx = Transaction::new( - &[&mint_keypair, &program_keypair], - Message::new( - &[loader_instruction::finalize( - &program_keypair.pubkey(), - &bpf_loader::id(), - )], - Some(&mint_keypair.pubkey()), - ), - bank.last_blockhash(), - ); - - // Try and deploy a program that depends on _sol_alloc_free - assert_eq!( - bank.process_transaction(&finalize_tx).unwrap_err(), - TransactionError::InstructionError(0, InstructionError::InvalidAccountData) - ); -} - #[test] #[cfg(feature = "sbf_rust")] #[should_panic( @@ -1628,97 +1503,6 @@ fn test_program_sbf_instruction_introspection() { assert!(bank.get_account(&sysvar::instructions::id()).is_none()); } -/// This test is to test bpf_loader v2 `Finalize` instruction with different -/// programs. It is going to be deprecated once we activate -/// `disable_bpf_loader_instructions`. -#[test] -#[cfg(feature = "sbf_rust")] -fn test_program_sbf_test_use_latest_executor() { - solana_logger::setup(); - - let GenesisConfigInfo { - mut genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - - // deactivate `disable_bpf_loader_instructions` feature so that the program - // can be loaded, finalized and tested. - genesis_config - .accounts - .remove(&feature_set::disable_bpf_loader_instructions::id()); - genesis_config - .accounts - .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); - - let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); - let mut bank_client = BankClient::new_shared(bank); - let panic_id = load_program( - &bank_client, - &bpf_loader::id(), - &mint_keypair, - "solana_sbf_rust_panic", - ); - - // Write the panic program into the program account - let (program_keypair, instruction) = load_and_finalize_program( - &bank_client, - &bpf_loader::id(), - None, - &mint_keypair, - "solana_sbf_rust_panic", - ); - - // Finalize the panic program, but fail the tx - let message = Message::new( - &[ - instruction, - Instruction::new_with_bytes(panic_id, &[0], vec![]), - ], - Some(&mint_keypair.pubkey()), - ); - - bank_client - .advance_slot(1, bank_forks.as_ref(), &Pubkey::default()) - .expect("Failed to advance the slot"); - - assert!(bank_client - .send_and_confirm_message(&[&mint_keypair, &program_keypair], message) - .is_err()); - - // Write the noop program into the same program account - let (program_keypair, instruction) = load_and_finalize_program( - &bank_client, - &bpf_loader::id(), - Some(program_keypair), - &mint_keypair, - "solana_sbf_rust_noop", - ); - bank_client - .advance_slot(1, bank_forks.as_ref(), &Pubkey::default()) - .expect("Failed to advance the slot"); - let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); - bank_client - .send_and_confirm_message(&[&mint_keypair, &program_keypair], message) - .unwrap(); - - // Call the noop program, should get noop not panic - let message = Message::new( - &[Instruction::new_with_bytes( - program_keypair.pubkey(), - &[0], - vec![], - )], - Some(&mint_keypair.pubkey()), - ); - bank_client - .advance_slot(1, bank_forks.as_ref(), &Pubkey::default()) - .expect("Failed to advance the slot"); - assert!(bank_client - .send_and_confirm_message(&[&mint_keypair], message) - .is_ok()); -} - #[test] #[cfg(feature = "sbf_rust")] fn test_program_sbf_upgrade() { @@ -2492,7 +2276,7 @@ fn test_program_sbf_disguised_as_sbf_loader() { let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); assert_eq!( result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::IncorrectProgramId) + TransactionError::InstructionError(0, InstructionError::UnsupportedProgramId) ); } } @@ -2887,67 +2671,6 @@ fn test_program_upgradeable_locks() { assert_eq!(results2[1], Err(TransactionError::AccountInUse)); } -/// This test is to test bpf_loader v2 `Finalize` instruction. It is going to be -/// deprecated once we activate `disable_bpf_loader_instructions`. -#[test] -#[cfg(feature = "sbf_rust")] -fn test_program_sbf_finalize() { - solana_logger::setup(); - - let GenesisConfigInfo { - mut genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - - // deactivate `disable_bpf_loader_instructions` feature so that the program - // can be loaded, finalized and tested. - genesis_config - .accounts - .remove(&feature_set::disable_bpf_loader_instructions::id()); - - genesis_config - .accounts - .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); - - let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); - let mut bank_client = BankClient::new_shared(bank.clone()); - - let (_, program_pubkey) = load_program_and_advance_slot( - &mut bank_client, - bank_forks.as_ref(), - &bpf_loader::id(), - &mint_keypair, - "solana_sbf_rust_finalize", - ); - - // Write the noop program into the same program account - let (program_keypair, _instruction) = load_and_finalize_program( - &bank_client, - &bpf_loader::id(), - None, - &mint_keypair, - "solana_sbf_rust_noop", - ); - - bank_client - .advance_slot(1, bank_forks.as_ref(), &Pubkey::default()) - .expect("Failed to advance the slot"); - - let account_metas = vec![ - AccountMeta::new(program_keypair.pubkey(), true), - AccountMeta::new_readonly(bpf_loader::id(), false), - AccountMeta::new(rent::id(), false), - ]; - let instruction = Instruction::new_with_bytes(program_pubkey, &[], account_metas.clone()); - let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); - let result = bank_client.send_and_confirm_message(&[&mint_keypair, &program_keypair], message); - assert_eq!( - result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete) - ); -} - #[test] #[cfg(feature = "sbf_rust")] fn test_program_sbf_ro_account_modify() { diff --git a/runtime/src/loader_utils.rs b/runtime/src/loader_utils.rs index 7f4650ae561d3f..7265641e900bc1 100644 --- a/runtime/src/loader_utils.rs +++ b/runtime/src/loader_utils.rs @@ -1,5 +1,6 @@ +#![cfg(feature = "dev-context-only-utils")] use { - crate::{bank::Bank, bank_client::BankClient}, + crate::{bank::Bank, bank_client::BankClient, bank_forks::BankForks}, serde::Serialize, solana_sdk::{ account::{AccountSharedData, WritableAccount}, @@ -13,7 +14,13 @@ use { signature::{Keypair, Signer}, system_instruction, }, - std::{env, fs::File, io::Read, path::PathBuf}, + std::{ + env, + fs::File, + io::Read, + path::PathBuf, + sync::{Arc, RwLock}, + }, }; const CHUNK_SIZE: usize = 512; // Size of chunk just needs to fit into tx @@ -206,6 +213,48 @@ pub fn load_upgradeable_program( }); } +pub fn load_upgradeable_program_wrapper( + bank_client: &BankClient, + mint_keypair: &Keypair, + authority_keypair: &Keypair, + name: &str, +) -> Pubkey { + let buffer_keypair = Keypair::new(); + let program_keypair = Keypair::new(); + load_upgradeable_program( + bank_client, + mint_keypair, + &buffer_keypair, + &program_keypair, + authority_keypair, + name, + ); + program_keypair.pubkey() +} + +pub fn load_upgradeable_program_and_advance_slot( + bank_client: &mut BankClient, + bank_forks: &RwLock, + mint_keypair: &Keypair, + authority_keypair: &Keypair, + name: &str, +) -> (Arc, Pubkey) { + let program_id = + load_upgradeable_program_wrapper(bank_client, mint_keypair, authority_keypair, name); + + // load_upgradeable_program sets clock sysvar to 1, which causes the program to be effective + // after 2 slots. They need to be called individually to create the correct fork graph in between. + bank_client + .advance_slot(1, bank_forks, &Pubkey::default()) + .expect("Failed to advance the slot"); + + let bank = bank_client + .advance_slot(1, bank_forks, &Pubkey::default()) + .expect("Failed to advance the slot"); + + (bank, program_id) +} + pub fn upgrade_program( bank_client: &T, payer_keypair: &Keypair,