2382 lines
		
	
	
		
			79 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			2382 lines
		
	
	
		
			79 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| #![cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
 | |
| 
 | |
| #[macro_use]
 | |
| extern crate solana_bpf_loader_program;
 | |
| 
 | |
| use itertools::izip;
 | |
| use solana_account_decoder::parse_bpf_loader::{
 | |
|     parse_bpf_upgradeable_loader, BpfUpgradeableLoaderAccountType,
 | |
| };
 | |
| use solana_bpf_loader_program::{
 | |
|     create_vm,
 | |
|     serialization::{deserialize_parameters, serialize_parameters},
 | |
|     syscalls::register_syscalls,
 | |
|     ThisInstructionMeter,
 | |
| };
 | |
| use solana_cli_output::display::println_transaction;
 | |
| use solana_rbpf::vm::{Config, Executable, Tracer};
 | |
| use solana_runtime::{
 | |
|     bank::{Bank, ExecuteTimings, NonceRollbackInfo, TransactionBalancesSet, TransactionResults},
 | |
|     bank_client::BankClient,
 | |
|     genesis_utils::{create_genesis_config, GenesisConfigInfo},
 | |
|     loader_utils::{
 | |
|         load_buffer_account, load_program, load_upgradeable_program, set_upgrade_authority,
 | |
|         upgrade_program,
 | |
|     },
 | |
| };
 | |
| use solana_sdk::{
 | |
|     account::{AccountSharedData, ReadableAccount},
 | |
|     bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
 | |
|     client::SyncClient,
 | |
|     clock::MAX_PROCESSING_AGE,
 | |
|     entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
 | |
|     instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
 | |
|     keyed_account::KeyedAccount,
 | |
|     message::Message,
 | |
|     process_instruction::{InvokeContext, MockInvokeContext},
 | |
|     pubkey::Pubkey,
 | |
|     signature::{keypair_from_seed, Keypair, Signer},
 | |
|     system_instruction,
 | |
|     sysvar::{clock, fees, rent},
 | |
|     transaction::{Transaction, TransactionError},
 | |
| };
 | |
| use solana_transaction_status::{
 | |
|     token_balances::collect_token_balances, ConfirmedTransaction, InnerInstructions,
 | |
|     TransactionStatusMeta, TransactionWithStatusMeta, UiTransactionEncoding,
 | |
| };
 | |
| use std::{
 | |
|     cell::RefCell, collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr,
 | |
|     sync::Arc,
 | |
| };
 | |
| 
 | |
| /// BPF program file extension
 | |
| const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
 | |
| 
 | |
| /// Create a BPF program file name
 | |
| fn create_bpf_path(name: &str) -> PathBuf {
 | |
|     let mut pathbuf = {
 | |
|         let current_exe = env::current_exe().unwrap();
 | |
|         PathBuf::from(current_exe.parent().unwrap().parent().unwrap())
 | |
|     };
 | |
|     pathbuf.push("bpf/");
 | |
|     pathbuf.push(name);
 | |
|     pathbuf.set_extension(PLATFORM_FILE_EXTENSION_BPF);
 | |
|     pathbuf
 | |
| }
 | |
| 
 | |
| fn load_bpf_program(
 | |
|     bank_client: &BankClient,
 | |
|     loader_id: &Pubkey,
 | |
|     payer_keypair: &Keypair,
 | |
|     name: &str,
 | |
| ) -> Pubkey {
 | |
|     let elf = read_bpf_program(name);
 | |
|     load_program(bank_client, payer_keypair, loader_id, elf)
 | |
| }
 | |
| 
 | |
| fn read_bpf_program(name: &str) -> Vec<u8> {
 | |
|     let path = create_bpf_path(name);
 | |
|     let mut file = File::open(&path).unwrap_or_else(|err| {
 | |
|         panic!("Failed to open {}: {}", path.display(), err);
 | |
|     });
 | |
|     let mut elf = Vec::new();
 | |
|     file.read_to_end(&mut elf).unwrap();
 | |
| 
 | |
|     elf
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| fn write_bpf_program(
 | |
|     bank_client: &BankClient,
 | |
|     loader_id: &Pubkey,
 | |
|     payer_keypair: &Keypair,
 | |
|     program_keypair: &Keypair,
 | |
|     elf: &[u8],
 | |
| ) {
 | |
|     use solana_sdk::loader_instruction;
 | |
| 
 | |
|     let chunk_size = 256; // Size of chunk just needs to fit into tx
 | |
|     let mut offset = 0;
 | |
|     for chunk in elf.chunks(chunk_size) {
 | |
|         let instruction =
 | |
|             loader_instruction::write(&program_keypair.pubkey(), loader_id, offset, chunk.to_vec());
 | |
|         let message = Message::new(&[instruction], Some(&payer_keypair.pubkey()));
 | |
| 
 | |
|         bank_client
 | |
|             .send_and_confirm_message(&[payer_keypair, &program_keypair], message)
 | |
|             .unwrap();
 | |
| 
 | |
|         offset += chunk_size as u32;
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn load_upgradeable_bpf_program(
 | |
|     bank_client: &BankClient,
 | |
|     payer_keypair: &Keypair,
 | |
|     buffer_keypair: &Keypair,
 | |
|     executable_keypair: &Keypair,
 | |
|     authority_keypair: &Keypair,
 | |
|     name: &str,
 | |
| ) {
 | |
|     let path = create_bpf_path(name);
 | |
|     let mut file = File::open(&path).unwrap_or_else(|err| {
 | |
|         panic!("Failed to open {}: {}", path.display(), err);
 | |
|     });
 | |
|     let mut elf = Vec::new();
 | |
|     file.read_to_end(&mut elf).unwrap();
 | |
|     load_upgradeable_program(
 | |
|         bank_client,
 | |
|         payer_keypair,
 | |
|         buffer_keypair,
 | |
|         executable_keypair,
 | |
|         authority_keypair,
 | |
|         elf,
 | |
|     );
 | |
| }
 | |
| 
 | |
| fn load_upgradeable_buffer(
 | |
|     bank_client: &BankClient,
 | |
|     payer_keypair: &Keypair,
 | |
|     buffer_keypair: &Keypair,
 | |
|     buffer_authority_keypair: &Keypair,
 | |
|     name: &str,
 | |
| ) {
 | |
|     let path = create_bpf_path(name);
 | |
|     let mut file = File::open(&path).unwrap_or_else(|err| {
 | |
|         panic!("Failed to open {}: {}", path.display(), err);
 | |
|     });
 | |
|     let mut elf = Vec::new();
 | |
|     file.read_to_end(&mut elf).unwrap();
 | |
|     load_buffer_account(
 | |
|         bank_client,
 | |
|         payer_keypair,
 | |
|         &buffer_keypair,
 | |
|         buffer_authority_keypair,
 | |
|         &elf,
 | |
|     );
 | |
| }
 | |
| 
 | |
| fn upgrade_bpf_program(
 | |
|     bank_client: &BankClient,
 | |
|     payer_keypair: &Keypair,
 | |
|     buffer_keypair: &Keypair,
 | |
|     executable_pubkey: &Pubkey,
 | |
|     authority_keypair: &Keypair,
 | |
|     name: &str,
 | |
| ) {
 | |
|     load_upgradeable_buffer(
 | |
|         bank_client,
 | |
|         payer_keypair,
 | |
|         buffer_keypair,
 | |
|         authority_keypair,
 | |
|         name,
 | |
|     );
 | |
|     upgrade_program(
 | |
|         bank_client,
 | |
|         payer_keypair,
 | |
|         executable_pubkey,
 | |
|         &buffer_keypair.pubkey(),
 | |
|         &authority_keypair,
 | |
|         &payer_keypair.pubkey(),
 | |
|     );
 | |
| }
 | |
| 
 | |
| fn run_program(
 | |
|     name: &str,
 | |
|     program_id: &Pubkey,
 | |
|     parameter_accounts: Vec<KeyedAccount>,
 | |
|     instruction_data: &[u8],
 | |
| ) -> Result<u64, InstructionError> {
 | |
|     let path = create_bpf_path(name);
 | |
|     let mut file = File::open(path).unwrap();
 | |
| 
 | |
|     let mut data = vec![];
 | |
|     file.read_to_end(&mut data).unwrap();
 | |
|     let loader_id = bpf_loader::id();
 | |
|     let parameter_bytes = serialize_parameters(
 | |
|         &bpf_loader::id(),
 | |
|         program_id,
 | |
|         ¶meter_accounts,
 | |
|         &instruction_data,
 | |
|     )
 | |
|     .unwrap();
 | |
|     let mut invoke_context = MockInvokeContext::new(parameter_accounts);
 | |
|     let compute_meter = invoke_context.get_compute_meter();
 | |
|     let mut instruction_meter = ThisInstructionMeter { compute_meter };
 | |
| 
 | |
|     let config = Config {
 | |
|         max_call_depth: 20,
 | |
|         stack_frame_size: 4096,
 | |
|         enable_instruction_meter: true,
 | |
|         enable_instruction_tracing: true,
 | |
|     };
 | |
|     let mut executable = Executable::from_elf(&data, None, config).unwrap();
 | |
|     executable.set_syscall_registry(register_syscalls(&mut invoke_context).unwrap());
 | |
|     executable.jit_compile().unwrap();
 | |
| 
 | |
|     let mut instruction_count = 0;
 | |
|     let mut tracer = None;
 | |
|     for i in 0..2 {
 | |
|         let mut parameter_bytes = parameter_bytes.clone();
 | |
|         {
 | |
|             let mut vm = create_vm(
 | |
|                 &loader_id,
 | |
|                 executable.as_ref(),
 | |
|                 parameter_bytes.as_slice_mut(),
 | |
|                 &mut invoke_context,
 | |
|             )
 | |
|             .unwrap();
 | |
|             let result = if i == 0 {
 | |
|                 vm.execute_program_interpreted(&mut instruction_meter)
 | |
|             } else {
 | |
|                 vm.execute_program_jit(&mut instruction_meter)
 | |
|             };
 | |
|             assert_eq!(SUCCESS, result.unwrap());
 | |
|             if i == 1 {
 | |
|                 assert_eq!(instruction_count, vm.get_total_instruction_count());
 | |
|             }
 | |
|             instruction_count = vm.get_total_instruction_count();
 | |
|             if config.enable_instruction_tracing {
 | |
|                 if i == 1 {
 | |
|                     if !Tracer::compare(tracer.as_ref().unwrap(), vm.get_tracer()) {
 | |
|                         let mut tracer_display = String::new();
 | |
|                         tracer
 | |
|                             .as_ref()
 | |
|                             .unwrap()
 | |
|                             .write(&mut tracer_display, vm.get_program())
 | |
|                             .unwrap();
 | |
|                         println!("TRACE (interpreted): {}", tracer_display);
 | |
|                         let mut tracer_display = String::new();
 | |
|                         vm.get_tracer()
 | |
|                             .write(&mut tracer_display, vm.get_program())
 | |
|                             .unwrap();
 | |
|                         println!("TRACE (jit): {}", tracer_display);
 | |
|                         assert!(false);
 | |
|                     }
 | |
|                 }
 | |
|                 tracer = Some(vm.get_tracer().clone());
 | |
|             }
 | |
|         }
 | |
|         let parameter_accounts = invoke_context.get_keyed_accounts().unwrap();
 | |
|         deserialize_parameters(
 | |
|             &bpf_loader::id(),
 | |
|             parameter_accounts,
 | |
|             parameter_bytes.as_slice(),
 | |
|             true,
 | |
|         )
 | |
|         .unwrap();
 | |
|     }
 | |
| 
 | |
|     Ok(instruction_count)
 | |
| }
 | |
| 
 | |
| fn process_transaction_and_record_inner(
 | |
|     bank: &Bank,
 | |
|     tx: Transaction,
 | |
| ) -> (Result<(), TransactionError>, Vec<Vec<CompiledInstruction>>) {
 | |
|     let signature = tx.signatures.get(0).unwrap().clone();
 | |
|     let txs = vec![tx];
 | |
|     let tx_batch = bank.prepare_batch(txs.iter());
 | |
|     let (mut results, _, mut inner, _transaction_logs) = bank.load_execute_and_commit_transactions(
 | |
|         &tx_batch,
 | |
|         MAX_PROCESSING_AGE,
 | |
|         false,
 | |
|         true,
 | |
|         false,
 | |
|         &mut ExecuteTimings::default(),
 | |
|     );
 | |
|     let inner_instructions = if inner.is_empty() {
 | |
|         Some(vec![vec![]])
 | |
|     } else {
 | |
|         inner.swap_remove(0)
 | |
|     };
 | |
|     let result = results
 | |
|         .fee_collection_results
 | |
|         .swap_remove(0)
 | |
|         .and_then(|_| bank.get_signature_status(&signature).unwrap());
 | |
|     (
 | |
|         result,
 | |
|         inner_instructions.expect("cpi recording should be enabled"),
 | |
|     )
 | |
| }
 | |
| 
 | |
| fn execute_transactions(bank: &Bank, txs: &[Transaction]) -> Vec<ConfirmedTransaction> {
 | |
|     let batch = bank.prepare_batch(txs.iter());
 | |
|     let mut timings = ExecuteTimings::default();
 | |
|     let mut mint_decimals = HashMap::new();
 | |
|     let tx_pre_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
 | |
|     let (
 | |
|         TransactionResults {
 | |
|             execution_results, ..
 | |
|         },
 | |
|         TransactionBalancesSet {
 | |
|             pre_balances,
 | |
|             post_balances,
 | |
|             ..
 | |
|         },
 | |
|         mut inner_instructions,
 | |
|         mut transaction_logs,
 | |
|     ) = bank.load_execute_and_commit_transactions(
 | |
|         &batch,
 | |
|         std::usize::MAX,
 | |
|         true,
 | |
|         true,
 | |
|         true,
 | |
|         &mut timings,
 | |
|     );
 | |
|     let tx_post_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
 | |
| 
 | |
|     for _ in 0..(txs.len() - transaction_logs.len()) {
 | |
|         transaction_logs.push(vec![]);
 | |
|     }
 | |
|     for _ in 0..(txs.len() - inner_instructions.len()) {
 | |
|         inner_instructions.push(None);
 | |
|     }
 | |
| 
 | |
|     izip!(
 | |
|         txs.iter(),
 | |
|         execution_results.into_iter(),
 | |
|         inner_instructions.into_iter(),
 | |
|         pre_balances.into_iter(),
 | |
|         post_balances.into_iter(),
 | |
|         tx_pre_token_balances.into_iter(),
 | |
|         tx_post_token_balances.into_iter(),
 | |
|         transaction_logs.into_iter(),
 | |
|     )
 | |
|     .map(
 | |
|         |(
 | |
|             tx,
 | |
|             (execute_result, nonce_rollback),
 | |
|             inner_instructions,
 | |
|             pre_balances,
 | |
|             post_balances,
 | |
|             pre_token_balances,
 | |
|             post_token_balances,
 | |
|             log_messages,
 | |
|         )| {
 | |
|             let fee_calculator = nonce_rollback
 | |
|                 .map(|nonce_rollback| nonce_rollback.fee_calculator())
 | |
|                 .unwrap_or_else(|| bank.get_fee_calculator(&tx.message().recent_blockhash))
 | |
|                 .expect("FeeCalculator must exist");
 | |
|             let fee = fee_calculator.calculate_fee(tx.message());
 | |
| 
 | |
|             let inner_instructions = inner_instructions.map(|inner_instructions| {
 | |
|                 inner_instructions
 | |
|                     .into_iter()
 | |
|                     .enumerate()
 | |
|                     .map(|(index, instructions)| InnerInstructions {
 | |
|                         index: index as u8,
 | |
|                         instructions,
 | |
|                     })
 | |
|                     .filter(|i| !i.instructions.is_empty())
 | |
|                     .collect()
 | |
|             });
 | |
| 
 | |
|             let tx_status_meta = TransactionStatusMeta {
 | |
|                 status: execute_result,
 | |
|                 fee,
 | |
|                 pre_balances,
 | |
|                 post_balances,
 | |
|                 pre_token_balances: Some(pre_token_balances),
 | |
|                 post_token_balances: Some(post_token_balances),
 | |
|                 inner_instructions,
 | |
|                 log_messages: Some(log_messages),
 | |
|             };
 | |
| 
 | |
|             ConfirmedTransaction {
 | |
|                 slot: bank.slot(),
 | |
|                 transaction: TransactionWithStatusMeta {
 | |
|                     transaction: tx.clone(),
 | |
|                     meta: Some(tx_status_meta),
 | |
|                 },
 | |
|                 block_time: None,
 | |
|             }
 | |
|         },
 | |
|     )
 | |
|     .collect()
 | |
| }
 | |
| 
 | |
| fn print_confirmed_tx(name: &str, confirmed_tx: ConfirmedTransaction) {
 | |
|     let block_time = confirmed_tx.block_time;
 | |
|     let tx = confirmed_tx.transaction.transaction.clone();
 | |
|     let encoded = confirmed_tx.encode(UiTransactionEncoding::JsonParsed);
 | |
|     println!("EXECUTE {} (slot {})", name, encoded.slot);
 | |
|     println_transaction(&tx, &encoded.transaction.meta, "  ", None, block_time);
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| #[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
 | |
| fn test_program_bpf_sanity() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let mut programs = Vec::new();
 | |
|     #[cfg(feature = "bpf_c")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[
 | |
|             ("alloc", true),
 | |
|             ("bpf_to_bpf", true),
 | |
|             ("multiple_static", true),
 | |
|             ("noop", true),
 | |
|             ("noop++", true),
 | |
|             ("panic", false),
 | |
|             ("relative_call", true),
 | |
|             ("sanity", true),
 | |
|             ("sanity++", true),
 | |
|             ("sha256", true),
 | |
|             ("struct_pass", true),
 | |
|             ("struct_ret", true),
 | |
|         ]);
 | |
|     }
 | |
|     #[cfg(feature = "bpf_rust")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[
 | |
|             ("solana_bpf_rust_128bit", true),
 | |
|             ("solana_bpf_rust_alloc", true),
 | |
|             ("solana_bpf_rust_custom_heap", true),
 | |
|             ("solana_bpf_rust_dep_crate", true),
 | |
|             ("solana_bpf_rust_external_spend", false),
 | |
|             ("solana_bpf_rust_iter", true),
 | |
|             ("solana_bpf_rust_many_args", true),
 | |
|             ("solana_bpf_rust_mem", true),
 | |
|             ("solana_bpf_rust_noop", true),
 | |
|             ("solana_bpf_rust_panic", false),
 | |
|             ("solana_bpf_rust_param_passing", true),
 | |
|             ("solana_bpf_rust_rand", true),
 | |
|             ("solana_bpf_rust_sanity", true),
 | |
|             ("solana_bpf_rust_sha256", true),
 | |
|         ]);
 | |
|     }
 | |
| 
 | |
|     for program in programs.iter() {
 | |
|         println!("Test program: {:?}", program.0);
 | |
| 
 | |
|         let GenesisConfigInfo {
 | |
|             genesis_config,
 | |
|             mint_keypair,
 | |
|             ..
 | |
|         } = create_genesis_config(50);
 | |
| 
 | |
|         let mut bank = Bank::new(&genesis_config);
 | |
|         let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|         bank.add_builtin(&name, id, entrypoint);
 | |
|         let bank_client = BankClient::new(bank);
 | |
| 
 | |
|         // Call user program
 | |
|         let program_id =
 | |
|             load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.0);
 | |
|         let account_metas = vec![
 | |
|             AccountMeta::new(mint_keypair.pubkey(), true),
 | |
|             AccountMeta::new(Keypair::new().pubkey(), false),
 | |
|         ];
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas);
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         if program.1 {
 | |
|             assert!(result.is_ok());
 | |
|         } else {
 | |
|             assert!(result.is_err());
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| #[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
 | |
| fn test_program_bpf_loader_deprecated() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let mut programs = Vec::new();
 | |
|     #[cfg(feature = "bpf_c")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[("deprecated_loader")]);
 | |
|     }
 | |
|     #[cfg(feature = "bpf_rust")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[("solana_bpf_rust_deprecated_loader")]);
 | |
|     }
 | |
| 
 | |
|     for program in programs.iter() {
 | |
|         println!("Test program: {:?}", program);
 | |
| 
 | |
|         let GenesisConfigInfo {
 | |
|             genesis_config,
 | |
|             mint_keypair,
 | |
|             ..
 | |
|         } = create_genesis_config(50);
 | |
|         let mut bank = Bank::new(&genesis_config);
 | |
|         let (name, id, entrypoint) = solana_bpf_loader_deprecated_program!();
 | |
|         bank.add_builtin(&name, id, entrypoint);
 | |
|         let bank_client = BankClient::new(bank);
 | |
| 
 | |
|         let program_id = load_bpf_program(
 | |
|             &bank_client,
 | |
|             &bpf_loader_deprecated::id(),
 | |
|             &mint_keypair,
 | |
|             program,
 | |
|         );
 | |
|         let account_metas = vec![AccountMeta::new(mint_keypair.pubkey(), true)];
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas);
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         assert!(result.is_ok());
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn test_program_bpf_duplicate_accounts() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let mut programs = Vec::new();
 | |
|     #[cfg(feature = "bpf_c")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[("dup_accounts")]);
 | |
|     }
 | |
|     #[cfg(feature = "bpf_rust")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[("solana_bpf_rust_dup_accounts")]);
 | |
|     }
 | |
| 
 | |
|     for program in programs.iter() {
 | |
|         println!("Test program: {:?}", program);
 | |
| 
 | |
|         let GenesisConfigInfo {
 | |
|             genesis_config,
 | |
|             mint_keypair,
 | |
|             ..
 | |
|         } = create_genesis_config(50);
 | |
|         let mut bank = Bank::new(&genesis_config);
 | |
|         let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|         bank.add_builtin(&name, id, entrypoint);
 | |
|         let bank = Arc::new(bank);
 | |
|         let bank_client = BankClient::new_shared(&bank);
 | |
|         let program_id = load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program);
 | |
|         let payee_account = AccountSharedData::new(10, 1, &program_id);
 | |
|         let payee_pubkey = solana_sdk::pubkey::new_rand();
 | |
|         bank.store_account(&payee_pubkey, &payee_account);
 | |
|         let account = AccountSharedData::new(10, 1, &program_id);
 | |
| 
 | |
|         let pubkey = solana_sdk::pubkey::new_rand();
 | |
|         let account_metas = vec![
 | |
|             AccountMeta::new(mint_keypair.pubkey(), true),
 | |
|             AccountMeta::new(payee_pubkey, false),
 | |
|             AccountMeta::new(pubkey, false),
 | |
|             AccountMeta::new(pubkey, false),
 | |
|         ];
 | |
| 
 | |
|         bank.store_account(&pubkey, &account);
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
 | |
|         assert!(result.is_ok());
 | |
|         assert_eq!(data[0], 1);
 | |
| 
 | |
|         bank.store_account(&pubkey, &account);
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[2], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
 | |
|         assert!(result.is_ok());
 | |
|         assert_eq!(data[0], 2);
 | |
| 
 | |
|         bank.store_account(&pubkey, &account);
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[3], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
 | |
|         assert!(result.is_ok());
 | |
|         assert_eq!(data[0], 3);
 | |
| 
 | |
|         bank.store_account(&pubkey, &account);
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[4], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         let lamports = bank_client.get_balance(&pubkey).unwrap();
 | |
|         assert!(result.is_ok());
 | |
|         assert_eq!(lamports, 11);
 | |
| 
 | |
|         bank.store_account(&pubkey, &account);
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[5], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         let lamports = bank_client.get_balance(&pubkey).unwrap();
 | |
|         assert!(result.is_ok());
 | |
|         assert_eq!(lamports, 12);
 | |
| 
 | |
|         bank.store_account(&pubkey, &account);
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[6], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         let lamports = bank_client.get_balance(&pubkey).unwrap();
 | |
|         assert!(result.is_ok());
 | |
|         assert_eq!(lamports, 13);
 | |
| 
 | |
|         let keypair = Keypair::new();
 | |
|         let pubkey = keypair.pubkey();
 | |
|         let account_metas = vec![
 | |
|             AccountMeta::new(mint_keypair.pubkey(), true),
 | |
|             AccountMeta::new(payee_pubkey, false),
 | |
|             AccountMeta::new(pubkey, false),
 | |
|             AccountMeta::new_readonly(pubkey, true),
 | |
|             AccountMeta::new_readonly(program_id, false),
 | |
|         ];
 | |
|         bank.store_account(&pubkey, &account);
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[7], account_metas.clone());
 | |
|         let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
 | |
|         let result = bank_client.send_and_confirm_message(&[&mint_keypair, &keypair], message);
 | |
|         assert!(result.is_ok());
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn test_program_bpf_error_handling() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let mut programs = Vec::new();
 | |
|     #[cfg(feature = "bpf_c")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[("error_handling")]);
 | |
|     }
 | |
|     #[cfg(feature = "bpf_rust")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[("solana_bpf_rust_error_handling")]);
 | |
|     }
 | |
| 
 | |
|     for program in programs.iter() {
 | |
|         println!("Test program: {:?}", program);
 | |
| 
 | |
|         let GenesisConfigInfo {
 | |
|             genesis_config,
 | |
|             mint_keypair,
 | |
|             ..
 | |
|         } = create_genesis_config(50);
 | |
|         let mut bank = Bank::new(&genesis_config);
 | |
|         let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|         bank.add_builtin(&name, id, entrypoint);
 | |
|         let bank_client = BankClient::new(bank);
 | |
|         let program_id = load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program);
 | |
|         let account_metas = vec![AccountMeta::new(mint_keypair.pubkey(), true)];
 | |
| 
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         assert!(result.is_ok());
 | |
| 
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[2], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         assert_eq!(
 | |
|             result.unwrap_err().unwrap(),
 | |
|             TransactionError::InstructionError(0, InstructionError::InvalidAccountData)
 | |
|         );
 | |
| 
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[3], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         assert_eq!(
 | |
|             result.unwrap_err().unwrap(),
 | |
|             TransactionError::InstructionError(0, InstructionError::Custom(0))
 | |
|         );
 | |
| 
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[4], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         assert_eq!(
 | |
|             result.unwrap_err().unwrap(),
 | |
|             TransactionError::InstructionError(0, InstructionError::Custom(42))
 | |
|         );
 | |
| 
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[5], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         let result = result.unwrap_err().unwrap();
 | |
|         if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) != result
 | |
|         {
 | |
|             assert_eq!(
 | |
|                 result,
 | |
|                 TransactionError::InstructionError(0, InstructionError::InvalidError)
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[6], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         let result = result.unwrap_err().unwrap();
 | |
|         if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) != result
 | |
|         {
 | |
|             assert_eq!(
 | |
|                 result,
 | |
|                 TransactionError::InstructionError(0, InstructionError::InvalidError)
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[7], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         let result = result.unwrap_err().unwrap();
 | |
|         if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) != result
 | |
|         {
 | |
|             assert_eq!(
 | |
|                 result,
 | |
|                 TransactionError::InstructionError(0, InstructionError::AccountBorrowFailed)
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[8], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         assert_eq!(
 | |
|             result.unwrap_err().unwrap(),
 | |
|             TransactionError::InstructionError(0, InstructionError::InvalidInstructionData)
 | |
|         );
 | |
| 
 | |
|         let instruction = Instruction::new_with_bytes(program_id, &[9], account_metas.clone());
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         assert_eq!(
 | |
|             result.unwrap_err().unwrap(),
 | |
|             TransactionError::InstructionError(0, InstructionError::MaxSeedLengthExceeded)
 | |
|         );
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn test_program_bpf_invoke_sanity() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     const TEST_SUCCESS: u8 = 1;
 | |
|     const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2;
 | |
|     const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3;
 | |
|     const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4;
 | |
|     const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5;
 | |
|     const TEST_CAP_SEEDS: u8 = 6;
 | |
|     const TEST_CAP_SIGNERS: u8 = 7;
 | |
|     const TEST_ALLOC_ACCESS_VIOLATION: u8 = 8;
 | |
|     const TEST_INSTRUCTION_DATA_TOO_LARGE: u8 = 9;
 | |
|     const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10;
 | |
|     const TEST_RETURN_ERROR: u8 = 11;
 | |
|     const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
 | |
|     const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
 | |
| 
 | |
|     #[allow(dead_code)]
 | |
|     #[derive(Debug)]
 | |
|     enum Languages {
 | |
|         C,
 | |
|         Rust,
 | |
|     }
 | |
|     let mut programs = Vec::new();
 | |
|     #[cfg(feature = "bpf_c")]
 | |
|     {
 | |
|         programs.push((Languages::C, "invoke", "invoked", "noop"));
 | |
|     }
 | |
|     #[cfg(feature = "bpf_rust")]
 | |
|     {
 | |
|         programs.push((
 | |
|             Languages::Rust,
 | |
|             "solana_bpf_rust_invoke",
 | |
|             "solana_bpf_rust_invoked",
 | |
|             "solana_bpf_rust_noop",
 | |
|         ));
 | |
|     }
 | |
|     for program in programs.iter() {
 | |
|         println!("Test program: {:?}", program);
 | |
| 
 | |
|         let GenesisConfigInfo {
 | |
|             genesis_config,
 | |
|             mint_keypair,
 | |
|             ..
 | |
|         } = create_genesis_config(50);
 | |
|         let mut bank = Bank::new(&genesis_config);
 | |
|         let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|         bank.add_builtin(&name, id, entrypoint);
 | |
|         let bank = Arc::new(bank);
 | |
|         let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|         let invoke_program_id =
 | |
|             load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.1);
 | |
|         let invoked_program_id =
 | |
|             load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.2);
 | |
|         let noop_program_id =
 | |
|             load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.3);
 | |
| 
 | |
|         let argument_keypair = Keypair::new();
 | |
|         let account = AccountSharedData::new(42, 100, &invoke_program_id);
 | |
|         bank.store_account(&argument_keypair.pubkey(), &account);
 | |
| 
 | |
|         let invoked_argument_keypair = Keypair::new();
 | |
|         let account = AccountSharedData::new(10, 10, &invoked_program_id);
 | |
|         bank.store_account(&invoked_argument_keypair.pubkey(), &account);
 | |
| 
 | |
|         let from_keypair = Keypair::new();
 | |
|         let account = AccountSharedData::new(84, 0, &solana_sdk::system_program::id());
 | |
|         bank.store_account(&from_keypair.pubkey(), &account);
 | |
| 
 | |
|         let (derived_key1, bump_seed1) =
 | |
|             Pubkey::find_program_address(&[b"You pass butter"], &invoke_program_id);
 | |
|         let (derived_key2, bump_seed2) =
 | |
|             Pubkey::find_program_address(&[b"Lil'", b"Bits"], &invoked_program_id);
 | |
|         let (derived_key3, bump_seed3) =
 | |
|             Pubkey::find_program_address(&[derived_key2.as_ref()], &invoked_program_id);
 | |
| 
 | |
|         let mint_pubkey = mint_keypair.pubkey();
 | |
|         let account_metas = vec![
 | |
|             AccountMeta::new(mint_pubkey, true),
 | |
|             AccountMeta::new(argument_keypair.pubkey(), true),
 | |
|             AccountMeta::new_readonly(invoked_program_id, false),
 | |
|             AccountMeta::new(invoked_argument_keypair.pubkey(), true),
 | |
|             AccountMeta::new_readonly(invoked_program_id, false),
 | |
|             AccountMeta::new(argument_keypair.pubkey(), true),
 | |
|             AccountMeta::new(derived_key1, false),
 | |
|             AccountMeta::new(derived_key2, false),
 | |
|             AccountMeta::new_readonly(derived_key3, false),
 | |
|             AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
 | |
|             AccountMeta::new(from_keypair.pubkey(), true),
 | |
|         ];
 | |
| 
 | |
|         // success cases
 | |
| 
 | |
|         let instruction = Instruction::new_with_bytes(
 | |
|             invoke_program_id,
 | |
|             &[TEST_SUCCESS, bump_seed1, bump_seed2, bump_seed3],
 | |
|             account_metas.clone(),
 | |
|         );
 | |
|         let noop_instruction = Instruction::new_with_bytes(noop_program_id, &[], vec![]);
 | |
|         let message = Message::new(&[instruction, noop_instruction], Some(&mint_pubkey));
 | |
|         let tx = Transaction::new(
 | |
|             &[
 | |
|                 &mint_keypair,
 | |
|                 &argument_keypair,
 | |
|                 &invoked_argument_keypair,
 | |
|                 &from_keypair,
 | |
|             ],
 | |
|             message.clone(),
 | |
|             bank.last_blockhash(),
 | |
|         );
 | |
|         let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | |
|         assert!(result.is_ok());
 | |
| 
 | |
|         let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | |
|             .iter()
 | |
|             .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | |
|             .collect();
 | |
|         let expected_invoked_programs = match program.0 {
 | |
|             Languages::C => vec![
 | |
|                 solana_sdk::system_program::id(),
 | |
|                 solana_sdk::system_program::id(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|             ],
 | |
|             Languages::Rust => vec![
 | |
|                 solana_sdk::system_program::id(),
 | |
|                 solana_sdk::system_program::id(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 invoked_program_id.clone(),
 | |
|                 solana_sdk::system_program::id(),
 | |
|             ],
 | |
|         };
 | |
|         assert_eq!(invoked_programs.len(), expected_invoked_programs.len());
 | |
|         assert_eq!(invoked_programs, expected_invoked_programs);
 | |
|         let no_invoked_programs: Vec<Pubkey> = inner_instructions[1]
 | |
|             .iter()
 | |
|             .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | |
|             .collect();
 | |
|         assert_eq!(no_invoked_programs.len(), 0);
 | |
| 
 | |
|         // failure cases
 | |
| 
 | |
|         let do_invoke_failure_test_local =
 | |
|             |test: u8, expected_error: TransactionError, expected_invoked_programs: &[Pubkey]| {
 | |
|                 println!("Running failure test #{:?}", test);
 | |
|                 let instruction_data = &[test, bump_seed1, bump_seed2, bump_seed3];
 | |
|                 let signers = vec![
 | |
|                     &mint_keypair,
 | |
|                     &argument_keypair,
 | |
|                     &invoked_argument_keypair,
 | |
|                     &from_keypair,
 | |
|                 ];
 | |
|                 let instruction = Instruction::new_with_bytes(
 | |
|                     invoke_program_id,
 | |
|                     instruction_data,
 | |
|                     account_metas.clone(),
 | |
|                 );
 | |
|                 let message = Message::new(&[instruction], Some(&mint_pubkey));
 | |
|                 let tx = Transaction::new(&signers, message.clone(), bank.last_blockhash());
 | |
|                 let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | |
|                 let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | |
|                     .iter()
 | |
|                     .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | |
|                     .collect();
 | |
|                 assert_eq!(result.unwrap_err(), expected_error);
 | |
|                 assert_eq!(invoked_programs, expected_invoked_programs);
 | |
|             };
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_PRIVILEGE_ESCALATION_SIGNER,
 | |
|             TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
 | |
|             &[invoked_program_id.clone()],
 | |
|         );
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_PRIVILEGE_ESCALATION_WRITABLE,
 | |
|             TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
 | |
|             &[invoked_program_id.clone()],
 | |
|         );
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_PPROGRAM_NOT_EXECUTABLE,
 | |
|             TransactionError::InstructionError(0, InstructionError::AccountNotExecutable),
 | |
|             &[],
 | |
|         );
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_EMPTY_ACCOUNTS_SLICE,
 | |
|             TransactionError::InstructionError(0, InstructionError::MissingAccount),
 | |
|             &[],
 | |
|         );
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_CAP_SEEDS,
 | |
|             TransactionError::InstructionError(0, InstructionError::MaxSeedLengthExceeded),
 | |
|             &[],
 | |
|         );
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_CAP_SIGNERS,
 | |
|             TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
 | |
|             &[],
 | |
|         );
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_INSTRUCTION_DATA_TOO_LARGE,
 | |
|             TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
 | |
|             &[],
 | |
|         );
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_INSTRUCTION_META_TOO_LARGE,
 | |
|             TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
 | |
|             &[],
 | |
|         );
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_RETURN_ERROR,
 | |
|             TransactionError::InstructionError(0, InstructionError::Custom(42)),
 | |
|             &[invoked_program_id.clone()],
 | |
|         );
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER,
 | |
|             TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
 | |
|             &[invoked_program_id.clone()],
 | |
|         );
 | |
| 
 | |
|         do_invoke_failure_test_local(
 | |
|             TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE,
 | |
|             TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
 | |
|             &[invoked_program_id.clone()],
 | |
|         );
 | |
| 
 | |
|         // Check resulting state
 | |
| 
 | |
|         assert_eq!(43, bank.get_balance(&derived_key1));
 | |
|         let account = bank.get_account(&derived_key1).unwrap();
 | |
|         assert_eq!(&invoke_program_id, account.owner());
 | |
|         assert_eq!(
 | |
|             MAX_PERMITTED_DATA_INCREASE,
 | |
|             bank.get_account(&derived_key1).unwrap().data().len()
 | |
|         );
 | |
|         for i in 0..20 {
 | |
|             assert_eq!(i as u8, account.data()[i]);
 | |
|         }
 | |
| 
 | |
|         // Attempt to realloc into unauthorized address space
 | |
|         let account = AccountSharedData::new(84, 0, &solana_sdk::system_program::id());
 | |
|         bank.store_account(&from_keypair.pubkey(), &account);
 | |
|         bank.store_account(&derived_key1, &AccountSharedData::default());
 | |
|         let instruction = Instruction::new_with_bytes(
 | |
|             invoke_program_id,
 | |
|             &[
 | |
|                 TEST_ALLOC_ACCESS_VIOLATION,
 | |
|                 bump_seed1,
 | |
|                 bump_seed2,
 | |
|                 bump_seed3,
 | |
|             ],
 | |
|             account_metas.clone(),
 | |
|         );
 | |
|         let message = Message::new(&[instruction], Some(&mint_pubkey));
 | |
|         let tx = Transaction::new(
 | |
|             &[
 | |
|                 &mint_keypair,
 | |
|                 &argument_keypair,
 | |
|                 &invoked_argument_keypair,
 | |
|                 &from_keypair,
 | |
|             ],
 | |
|             message.clone(),
 | |
|             bank.last_blockhash(),
 | |
|         );
 | |
|         let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
 | |
|         let invoked_programs: Vec<Pubkey> = inner_instructions[0]
 | |
|             .iter()
 | |
|             .map(|ix| message.account_keys[ix.program_id_index as usize].clone())
 | |
|             .collect();
 | |
|         assert_eq!(invoked_programs, vec![solana_sdk::system_program::id()]);
 | |
|         assert_eq!(
 | |
|             result.unwrap_err(),
 | |
|             TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
 | |
|         );
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_program_id_spoofing() {
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank = Arc::new(bank);
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     let malicious_swap_pubkey = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_spoof1",
 | |
|     );
 | |
|     let malicious_system_pubkey = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_spoof1_system",
 | |
|     );
 | |
| 
 | |
|     let from_pubkey = Pubkey::new_unique();
 | |
|     let account = AccountSharedData::new(10, 0, &solana_sdk::system_program::id());
 | |
|     bank.store_account(&from_pubkey, &account);
 | |
| 
 | |
|     let to_pubkey = Pubkey::new_unique();
 | |
|     let account = AccountSharedData::new(0, 0, &solana_sdk::system_program::id());
 | |
|     bank.store_account(&to_pubkey, &account);
 | |
| 
 | |
|     let account_metas = vec![
 | |
|         AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
 | |
|         AccountMeta::new_readonly(malicious_system_pubkey, false),
 | |
|         AccountMeta::new(from_pubkey, false),
 | |
|         AccountMeta::new(to_pubkey, false),
 | |
|     ];
 | |
| 
 | |
|     let instruction =
 | |
|         Instruction::new_with_bytes(malicious_swap_pubkey, &[], account_metas.clone());
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature)
 | |
|     );
 | |
|     assert_eq!(10, bank.get_balance(&from_pubkey));
 | |
|     assert_eq!(0, bank.get_balance(&to_pubkey));
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_caller_has_access_to_cpi_program() {
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank = Arc::new(bank);
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     let caller_pubkey = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_caller_access",
 | |
|     );
 | |
|     let caller2_pubkey = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_caller_access",
 | |
|     );
 | |
|     let account_metas = vec![
 | |
|         AccountMeta::new_readonly(caller_pubkey, false),
 | |
|         AccountMeta::new_readonly(caller2_pubkey, false),
 | |
|     ];
 | |
|     let instruction = Instruction::new_with_bytes(caller_pubkey, &[1], account_metas.clone());
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::MissingAccount)
 | |
|     );
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_ro_modify() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank = Arc::new(bank);
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     let program_pubkey = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_ro_modify",
 | |
|     );
 | |
| 
 | |
|     let test_keypair = Keypair::new();
 | |
|     let account = AccountSharedData::new(10, 0, &solana_sdk::system_program::id());
 | |
|     bank.store_account(&test_keypair.pubkey(), &account);
 | |
| 
 | |
|     let account_metas = vec![
 | |
|         AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
 | |
|         AccountMeta::new(test_keypair.pubkey(), true),
 | |
|     ];
 | |
| 
 | |
|     let instruction = Instruction::new_with_bytes(program_pubkey, &[1], account_metas.clone());
 | |
|     let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
 | |
|     let result = bank_client.send_and_confirm_message(&[&mint_keypair, &test_keypair], message);
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
 | |
|     );
 | |
| 
 | |
|     let instruction = Instruction::new_with_bytes(program_pubkey, &[3], account_metas.clone());
 | |
|     let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
 | |
|     let result = bank_client.send_and_confirm_message(&[&mint_keypair, &test_keypair], message);
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
 | |
|     );
 | |
| 
 | |
|     let instruction = Instruction::new_with_bytes(program_pubkey, &[4], account_metas.clone());
 | |
|     let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
 | |
|     let result = bank_client.send_and_confirm_message(&[&mint_keypair, &test_keypair], message);
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
 | |
|     );
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_call_depth() {
 | |
|     use solana_sdk::process_instruction::BpfComputeBudget;
 | |
| 
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     println!("Test program: solana_bpf_rust_call_depth");
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank_client = BankClient::new(bank);
 | |
|     let program_id = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_call_depth",
 | |
|     );
 | |
| 
 | |
|     let instruction = Instruction::new_with_bincode(
 | |
|         program_id,
 | |
|         &(BpfComputeBudget::default().max_call_depth - 1),
 | |
|         vec![],
 | |
|     );
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|     assert!(result.is_ok());
 | |
| 
 | |
|     let instruction = Instruction::new_with_bincode(
 | |
|         program_id,
 | |
|         &BpfComputeBudget::default().max_call_depth,
 | |
|         vec![],
 | |
|     );
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|     assert!(result.is_err());
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn assert_instruction_count() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let mut programs = Vec::new();
 | |
|     #[cfg(feature = "bpf_c")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[
 | |
|             ("alloc", 1137),
 | |
|             ("bpf_to_bpf", 13),
 | |
|             ("multiple_static", 8),
 | |
|             ("noop", 45),
 | |
|             ("noop++", 45),
 | |
|             ("relative_call", 10),
 | |
|             ("sanity", 175),
 | |
|             ("sanity++", 177),
 | |
|             ("sha256", 348),
 | |
|             ("struct_pass", 8),
 | |
|             ("struct_ret", 22),
 | |
|         ]);
 | |
|     }
 | |
|     #[cfg(feature = "bpf_rust")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[
 | |
|             ("solana_bpf_rust_128bit", 572),
 | |
|             ("solana_bpf_rust_alloc", 8906),
 | |
|             ("solana_bpf_rust_custom_heap", 516),
 | |
|             ("solana_bpf_rust_dep_crate", 2),
 | |
|             ("solana_bpf_rust_external_spend", 498),
 | |
|             ("solana_bpf_rust_iter", 724),
 | |
|             ("solana_bpf_rust_many_args", 237),
 | |
|             ("solana_bpf_rust_mem", 2297),
 | |
|             ("solana_bpf_rust_noop", 472),
 | |
|             ("solana_bpf_rust_param_passing", 46),
 | |
|             ("solana_bpf_rust_rand", 475),
 | |
|             ("solana_bpf_rust_sanity", 869),
 | |
|             ("solana_bpf_rust_sha256", 10830),
 | |
|         ]);
 | |
|     }
 | |
| 
 | |
|     let mut passed = true;
 | |
|     println!("\n  {:30} expected actual diff", "BPF program");
 | |
|     for program in programs.iter() {
 | |
|         let program_id = solana_sdk::pubkey::new_rand();
 | |
|         let key = solana_sdk::pubkey::new_rand();
 | |
|         let mut account = RefCell::new(AccountSharedData::default());
 | |
|         let parameter_accounts = vec![KeyedAccount::new(&key, false, &mut account)];
 | |
|         let count = run_program(program.0, &program_id, parameter_accounts, &[]).unwrap();
 | |
|         let diff: i64 = count as i64 - program.1 as i64;
 | |
|         println!("  {:30} {:8} {:6} {:+4}", program.0, program.1, count, diff);
 | |
|         if count > program.1 {
 | |
|             passed = false;
 | |
|         }
 | |
|     }
 | |
|     assert!(passed);
 | |
| }
 | |
| 
 | |
| #[cfg(any(feature = "bpf_rust"))]
 | |
| #[test]
 | |
| fn test_program_bpf_instruction_introspection() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50_000);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
| 
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank = Arc::new(bank);
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     let program_id = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_instruction_introspection",
 | |
|     );
 | |
| 
 | |
|     // Passing transaction
 | |
|     let account_metas = vec![AccountMeta::new_readonly(
 | |
|         solana_sdk::sysvar::instructions::id(),
 | |
|         false,
 | |
|     )];
 | |
|     let instruction0 = Instruction::new_with_bytes(program_id, &[0u8, 0u8], account_metas.clone());
 | |
|     let instruction1 = Instruction::new_with_bytes(program_id, &[0u8, 1u8], account_metas.clone());
 | |
|     let instruction2 = Instruction::new_with_bytes(program_id, &[0u8, 2u8], account_metas);
 | |
|     let message = Message::new(
 | |
|         &[instruction0, instruction1, instruction2],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     let result = bank_client.send_and_confirm_message(&[&mint_keypair], message);
 | |
|     assert!(result.is_ok());
 | |
| 
 | |
|     // writable special instructions11111 key, should not be allowed
 | |
|     let account_metas = vec![AccountMeta::new(
 | |
|         solana_sdk::sysvar::instructions::id(),
 | |
|         false,
 | |
|     )];
 | |
|     let instruction = Instruction::new_with_bytes(program_id, &[0], account_metas);
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         // sysvar write locks are demoted to read only. So this will no longer
 | |
|         // cause InvalidAccountIndex error.
 | |
|         TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
 | |
|     );
 | |
| 
 | |
|     // No accounts, should error
 | |
|     let instruction = Instruction::new_with_bytes(program_id, &[0], vec![]);
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|     assert!(result.is_err());
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(
 | |
|             0,
 | |
|             solana_sdk::instruction::InstructionError::NotEnoughAccountKeys
 | |
|         )
 | |
|     );
 | |
|     assert!(bank
 | |
|         .get_account(&solana_sdk::sysvar::instructions::id())
 | |
|         .is_none());
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_test_use_latest_executor() {
 | |
|     use solana_sdk::{loader_instruction, system_instruction};
 | |
| 
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank_client = BankClient::new(bank);
 | |
|     let panic_id = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_panic",
 | |
|     );
 | |
| 
 | |
|     let program_keypair = Keypair::new();
 | |
| 
 | |
|     // Write the panic program into the program account
 | |
|     let elf = read_bpf_program("solana_bpf_rust_panic");
 | |
|     let message = Message::new(
 | |
|         &[system_instruction::create_account(
 | |
|             &mint_keypair.pubkey(),
 | |
|             &program_keypair.pubkey(),
 | |
|             1,
 | |
|             elf.len() as u64 * 2,
 | |
|             &bpf_loader::id(),
 | |
|         )],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     assert!(bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
 | |
|         .is_ok());
 | |
|     write_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         &program_keypair,
 | |
|         &elf,
 | |
|     );
 | |
| 
 | |
|     // Finalize the panic program, but fail the tx
 | |
|     let message = Message::new(
 | |
|         &[
 | |
|             loader_instruction::finalize(&program_keypair.pubkey(), &bpf_loader::id()),
 | |
|             Instruction::new_with_bytes(panic_id, &[0], vec![]),
 | |
|         ],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     assert!(bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
 | |
|         .is_err());
 | |
| 
 | |
|     // Write the noop program into the same program account
 | |
|     let elf = read_bpf_program("solana_bpf_rust_noop");
 | |
|     write_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         &program_keypair,
 | |
|         &elf,
 | |
|     );
 | |
| 
 | |
|     // Finalize the noop program
 | |
|     let message = Message::new(
 | |
|         &[loader_instruction::finalize(
 | |
|             &program_keypair.pubkey(),
 | |
|             &bpf_loader::id(),
 | |
|         )],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     assert!(bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
 | |
|         .is_ok());
 | |
| 
 | |
|     // 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()),
 | |
|     );
 | |
|     assert!(bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair], message)
 | |
|         .is_ok());
 | |
| }
 | |
| 
 | |
| #[ignore] // Invoking BPF loaders from CPI not allowed
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_test_use_latest_executor2() {
 | |
|     use solana_sdk::{loader_instruction, system_instruction};
 | |
| 
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank_client = BankClient::new(bank);
 | |
|     let invoke_and_error = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_invoke_and_error",
 | |
|     );
 | |
|     let invoke_and_ok = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_invoke_and_ok",
 | |
|     );
 | |
| 
 | |
|     let program_keypair = Keypair::new();
 | |
| 
 | |
|     // Write the panic program into the program account
 | |
|     let elf = read_bpf_program("solana_bpf_rust_panic");
 | |
|     let message = Message::new(
 | |
|         &[system_instruction::create_account(
 | |
|             &mint_keypair.pubkey(),
 | |
|             &program_keypair.pubkey(),
 | |
|             1,
 | |
|             elf.len() as u64 * 2,
 | |
|             &bpf_loader::id(),
 | |
|         )],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     assert!(bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
 | |
|         .is_ok());
 | |
|     write_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         &program_keypair,
 | |
|         &elf,
 | |
|     );
 | |
| 
 | |
|     // - invoke finalize and return error, swallow error
 | |
|     let mut instruction =
 | |
|         loader_instruction::finalize(&program_keypair.pubkey(), &bpf_loader::id());
 | |
|     instruction.accounts.insert(
 | |
|         0,
 | |
|         AccountMeta {
 | |
|             is_signer: false,
 | |
|             is_writable: false,
 | |
|             pubkey: instruction.program_id,
 | |
|         },
 | |
|     );
 | |
|     instruction.program_id = invoke_and_ok;
 | |
|     instruction.accounts.insert(
 | |
|         0,
 | |
|         AccountMeta {
 | |
|             is_signer: false,
 | |
|             is_writable: false,
 | |
|             pubkey: invoke_and_error,
 | |
|         },
 | |
|     );
 | |
|     let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
 | |
|     assert!(bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
 | |
|         .is_ok());
 | |
| 
 | |
|     // invoke program, verify not found
 | |
|     let message = Message::new(
 | |
|         &[Instruction::new_with_bytes(
 | |
|             program_keypair.pubkey(),
 | |
|             &[0],
 | |
|             vec![],
 | |
|         )],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     assert_eq!(
 | |
|         bank_client
 | |
|             .send_and_confirm_message(&[&mint_keypair], message)
 | |
|             .unwrap_err()
 | |
|             .unwrap(),
 | |
|         TransactionError::InvalidProgramForExecution
 | |
|     );
 | |
| 
 | |
|     // Write the noop program into the same program account
 | |
|     let elf = read_bpf_program("solana_bpf_rust_noop");
 | |
|     write_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         &program_keypair,
 | |
|         &elf,
 | |
|     );
 | |
| 
 | |
|     // Finalize the noop program
 | |
|     let message = Message::new(
 | |
|         &[loader_instruction::finalize(
 | |
|             &program_keypair.pubkey(),
 | |
|             &bpf_loader::id(),
 | |
|         )],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     assert!(bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
 | |
|         .is_ok());
 | |
| 
 | |
|     // Call the program, should get noop, not panic
 | |
|     let message = Message::new(
 | |
|         &[Instruction::new_with_bytes(
 | |
|             program_keypair.pubkey(),
 | |
|             &[0],
 | |
|             vec![],
 | |
|         )],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     assert!(bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair], message)
 | |
|         .is_ok());
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_upgrade() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank_client = BankClient::new(bank);
 | |
| 
 | |
|     // Deploy upgrade program
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     let program_keypair = Keypair::new();
 | |
|     let program_id = program_keypair.pubkey();
 | |
|     let authority_keypair = Keypair::new();
 | |
|     load_upgradeable_bpf_program(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &program_keypair,
 | |
|         &authority_keypair,
 | |
|         "solana_bpf_rust_upgradeable",
 | |
|     );
 | |
| 
 | |
|     let mut instruction = Instruction::new_with_bytes(
 | |
|         program_id,
 | |
|         &[0],
 | |
|         vec![
 | |
|             AccountMeta::new(program_id.clone(), false),
 | |
|             AccountMeta::new(clock::id(), false),
 | |
|             AccountMeta::new(fees::id(), false),
 | |
|         ],
 | |
|     );
 | |
| 
 | |
|     // Call upgrade program
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::Custom(42))
 | |
|     );
 | |
| 
 | |
|     // Upgrade program
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     upgrade_bpf_program(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &program_id,
 | |
|         &authority_keypair,
 | |
|         "solana_bpf_rust_upgraded",
 | |
|     );
 | |
| 
 | |
|     // Call upgraded program
 | |
|     instruction.data[0] += 1;
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::Custom(43))
 | |
|     );
 | |
| 
 | |
|     // Set a new authority
 | |
|     let new_authority_keypair = Keypair::new();
 | |
|     set_upgrade_authority(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &program_id,
 | |
|         &authority_keypair,
 | |
|         Some(&new_authority_keypair.pubkey()),
 | |
|     );
 | |
| 
 | |
|     // Upgrade back to the original program
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     upgrade_bpf_program(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &program_id,
 | |
|         &new_authority_keypair,
 | |
|         "solana_bpf_rust_upgradeable",
 | |
|     );
 | |
| 
 | |
|     // Call original program
 | |
|     instruction.data[0] += 1;
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::Custom(42))
 | |
|     );
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_upgrade_and_invoke_in_same_tx() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank = Arc::new(bank);
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     // Deploy upgrade program
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     let program_keypair = Keypair::new();
 | |
|     let program_id = program_keypair.pubkey();
 | |
|     let authority_keypair = Keypair::new();
 | |
|     load_upgradeable_bpf_program(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &program_keypair,
 | |
|         &authority_keypair,
 | |
|         "solana_bpf_rust_noop",
 | |
|     );
 | |
| 
 | |
|     let invoke_instruction = Instruction::new_with_bytes(
 | |
|         program_id,
 | |
|         &[0],
 | |
|         vec![
 | |
|             AccountMeta::new(program_id.clone(), false),
 | |
|             AccountMeta::new(clock::id(), false),
 | |
|             AccountMeta::new(fees::id(), false),
 | |
|         ],
 | |
|     );
 | |
| 
 | |
|     // Call upgradeable program
 | |
|     let result =
 | |
|         bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone());
 | |
|     assert!(result.is_ok());
 | |
| 
 | |
|     // Prepare for upgrade
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     load_upgradeable_buffer(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &authority_keypair,
 | |
|         "solana_bpf_rust_panic",
 | |
|     );
 | |
| 
 | |
|     // Invoke, then upgrade the program, and then invoke again in same tx
 | |
|     let message = Message::new(
 | |
|         &[
 | |
|             invoke_instruction.clone(),
 | |
|             bpf_loader_upgradeable::upgrade(
 | |
|                 &program_id,
 | |
|                 &buffer_keypair.pubkey(),
 | |
|                 &authority_keypair.pubkey(),
 | |
|                 &mint_keypair.pubkey(),
 | |
|             ),
 | |
|             invoke_instruction,
 | |
|         ],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     let tx = Transaction::new(
 | |
|         &[&mint_keypair, &authority_keypair],
 | |
|         message.clone(),
 | |
|         bank.last_blockhash(),
 | |
|     );
 | |
|     let (result, _) = process_transaction_and_record_inner(&bank, tx);
 | |
|     assert_eq!(
 | |
|         result.unwrap_err(),
 | |
|         TransactionError::InstructionError(2, InstructionError::ProgramFailedToComplete)
 | |
|     );
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_invoke_upgradeable_via_cpi() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank_client = BankClient::new(bank);
 | |
|     let invoke_and_return = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_invoke_and_return",
 | |
|     );
 | |
| 
 | |
|     // Deploy upgradeable program
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     let program_keypair = Keypair::new();
 | |
|     let program_id = program_keypair.pubkey();
 | |
|     let authority_keypair = Keypair::new();
 | |
|     load_upgradeable_bpf_program(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &program_keypair,
 | |
|         &authority_keypair,
 | |
|         "solana_bpf_rust_upgradeable",
 | |
|     );
 | |
| 
 | |
|     let mut instruction = Instruction::new_with_bytes(
 | |
|         invoke_and_return,
 | |
|         &[0],
 | |
|         vec![
 | |
|             AccountMeta::new(program_id, false),
 | |
|             AccountMeta::new(program_id, false),
 | |
|             AccountMeta::new(clock::id(), false),
 | |
|             AccountMeta::new(fees::id(), false),
 | |
|         ],
 | |
|     );
 | |
| 
 | |
|     // Call invoker program to invoke the upgradeable program
 | |
|     instruction.data[0] += 1;
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::Custom(42))
 | |
|     );
 | |
| 
 | |
|     // Upgrade program
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     upgrade_bpf_program(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &program_id,
 | |
|         &authority_keypair,
 | |
|         "solana_bpf_rust_upgraded",
 | |
|     );
 | |
| 
 | |
|     // Call the upgraded program
 | |
|     instruction.data[0] += 1;
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::Custom(43))
 | |
|     );
 | |
| 
 | |
|     // Set a new authority
 | |
|     let new_authority_keypair = Keypair::new();
 | |
|     set_upgrade_authority(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &program_id,
 | |
|         &authority_keypair,
 | |
|         Some(&new_authority_keypair.pubkey()),
 | |
|     );
 | |
| 
 | |
|     // Upgrade back to the original program
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     upgrade_bpf_program(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &program_id,
 | |
|         &new_authority_keypair,
 | |
|         "solana_bpf_rust_upgradeable",
 | |
|     );
 | |
| 
 | |
|     // Call original program
 | |
|     instruction.data[0] += 1;
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::Custom(42))
 | |
|     );
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| #[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
 | |
| fn test_program_bpf_disguised_as_bpf_loader() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let mut programs = Vec::new();
 | |
|     #[cfg(feature = "bpf_c")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[("noop")]);
 | |
|     }
 | |
|     #[cfg(feature = "bpf_rust")]
 | |
|     {
 | |
|         programs.extend_from_slice(&[("solana_bpf_rust_noop")]);
 | |
|     }
 | |
| 
 | |
|     for program in programs.iter() {
 | |
|         let GenesisConfigInfo {
 | |
|             genesis_config,
 | |
|             mint_keypair,
 | |
|             ..
 | |
|         } = create_genesis_config(50);
 | |
|         let mut bank = Bank::new(&genesis_config);
 | |
|         let (name, id, entrypoint) = solana_bpf_loader_deprecated_program!();
 | |
|         bank.add_builtin(&name, id, entrypoint);
 | |
|         let bank_client = BankClient::new(bank);
 | |
| 
 | |
|         let program_id = load_bpf_program(
 | |
|             &bank_client,
 | |
|             &bpf_loader_deprecated::id(),
 | |
|             &mint_keypair,
 | |
|             program,
 | |
|         );
 | |
|         let account_metas = vec![AccountMeta::new_readonly(program_id, false)];
 | |
|         let instruction =
 | |
|             Instruction::new_with_bytes(bpf_loader_deprecated::id(), &[1], account_metas);
 | |
|         let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
 | |
|         assert_eq!(
 | |
|             result.unwrap_err().unwrap(),
 | |
|             TransactionError::InstructionError(0, InstructionError::IncorrectProgramId)
 | |
|         );
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_upgrade_via_cpi() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank_client = BankClient::new(bank);
 | |
|     let invoke_and_return = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_invoke_and_return",
 | |
|     );
 | |
| 
 | |
|     // Deploy upgradeable program
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     let program_keypair = Keypair::new();
 | |
|     let program_id = program_keypair.pubkey();
 | |
|     let authority_keypair = Keypair::new();
 | |
|     load_upgradeable_bpf_program(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &program_keypair,
 | |
|         &authority_keypair,
 | |
|         "solana_bpf_rust_upgradeable",
 | |
|     );
 | |
| 
 | |
|     let mut instruction = Instruction::new_with_bytes(
 | |
|         invoke_and_return,
 | |
|         &[0],
 | |
|         vec![
 | |
|             AccountMeta::new(program_id, false),
 | |
|             AccountMeta::new(program_id, false),
 | |
|             AccountMeta::new(clock::id(), false),
 | |
|             AccountMeta::new(fees::id(), false),
 | |
|         ],
 | |
|     );
 | |
| 
 | |
|     // Call the upgraded program
 | |
|     instruction.data[0] += 1;
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::Custom(42))
 | |
|     );
 | |
| 
 | |
|     // Load the buffer account
 | |
|     let path = create_bpf_path("solana_bpf_rust_upgraded");
 | |
|     let mut file = File::open(&path).unwrap_or_else(|err| {
 | |
|         panic!("Failed to open {}: {}", path.display(), err);
 | |
|     });
 | |
|     let mut elf = Vec::new();
 | |
|     file.read_to_end(&mut elf).unwrap();
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     load_buffer_account(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &authority_keypair,
 | |
|         &elf,
 | |
|     );
 | |
| 
 | |
|     // Upgrade program via CPI
 | |
|     let mut upgrade_instruction = bpf_loader_upgradeable::upgrade(
 | |
|         &program_id,
 | |
|         &buffer_keypair.pubkey(),
 | |
|         &authority_keypair.pubkey(),
 | |
|         &mint_keypair.pubkey(),
 | |
|     );
 | |
|     upgrade_instruction.program_id = invoke_and_return;
 | |
|     upgrade_instruction
 | |
|         .accounts
 | |
|         .insert(0, AccountMeta::new(bpf_loader_upgradeable::id(), false));
 | |
|     let message = Message::new(&[upgrade_instruction], Some(&mint_keypair.pubkey()));
 | |
|     bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair, &authority_keypair], message)
 | |
|         .unwrap();
 | |
| 
 | |
|     // Call the upgraded program
 | |
|     instruction.data[0] += 1;
 | |
|     let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::Custom(43))
 | |
|     );
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_upgrade_self_via_cpi() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank = Arc::new(bank);
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
|     let noop_program_id = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_noop",
 | |
|     );
 | |
| 
 | |
|     // Deploy upgradeable program
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     let program_keypair = Keypair::new();
 | |
|     let program_id = program_keypair.pubkey();
 | |
|     let authority_keypair = Keypair::new();
 | |
|     load_upgradeable_bpf_program(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &program_keypair,
 | |
|         &authority_keypair,
 | |
|         "solana_bpf_rust_invoke_and_return",
 | |
|     );
 | |
| 
 | |
|     let mut invoke_instruction = Instruction::new_with_bytes(
 | |
|         program_id,
 | |
|         &[0],
 | |
|         vec![
 | |
|             AccountMeta::new(noop_program_id, false),
 | |
|             AccountMeta::new(noop_program_id, false),
 | |
|             AccountMeta::new(clock::id(), false),
 | |
|             AccountMeta::new(fees::id(), false),
 | |
|         ],
 | |
|     );
 | |
| 
 | |
|     // Call the upgraded program
 | |
|     invoke_instruction.data[0] += 1;
 | |
|     let result =
 | |
|         bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone());
 | |
|     assert!(result.is_ok());
 | |
| 
 | |
|     // Prepare for upgrade
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     load_upgradeable_buffer(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &authority_keypair,
 | |
|         "solana_bpf_rust_panic",
 | |
|     );
 | |
| 
 | |
|     // Invoke, then upgrade the program, and then invoke again in same tx
 | |
|     let message = Message::new(
 | |
|         &[
 | |
|             invoke_instruction.clone(),
 | |
|             bpf_loader_upgradeable::upgrade(
 | |
|                 &program_id,
 | |
|                 &buffer_keypair.pubkey(),
 | |
|                 &authority_keypair.pubkey(),
 | |
|                 &mint_keypair.pubkey(),
 | |
|             ),
 | |
|             invoke_instruction,
 | |
|         ],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     let tx = Transaction::new(
 | |
|         &[&mint_keypair, &authority_keypair],
 | |
|         message.clone(),
 | |
|         bank.last_blockhash(),
 | |
|     );
 | |
|     let (result, _) = process_transaction_and_record_inner(&bank, tx);
 | |
|     assert_eq!(
 | |
|         result.unwrap_err(),
 | |
|         TransactionError::InstructionError(2, InstructionError::ProgramFailedToComplete)
 | |
|     );
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_set_upgrade_authority_via_cpi() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank_client = BankClient::new(bank);
 | |
| 
 | |
|     // Deploy CPI invoker program
 | |
|     let invoke_and_return = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_invoke_and_return",
 | |
|     );
 | |
| 
 | |
|     // Deploy upgradeable program
 | |
|     let buffer_keypair = Keypair::new();
 | |
|     let program_keypair = Keypair::new();
 | |
|     let program_id = program_keypair.pubkey();
 | |
|     let authority_keypair = Keypair::new();
 | |
|     load_upgradeable_bpf_program(
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &buffer_keypair,
 | |
|         &program_keypair,
 | |
|         &authority_keypair,
 | |
|         "solana_bpf_rust_upgradeable",
 | |
|     );
 | |
| 
 | |
|     // Set program upgrade authority instruction to invoke via CPI
 | |
|     let new_upgrade_authority_key = Keypair::new().pubkey();
 | |
|     let mut set_upgrade_authority_instruction = bpf_loader_upgradeable::set_upgrade_authority(
 | |
|         &program_id,
 | |
|         &authority_keypair.pubkey(),
 | |
|         Some(&new_upgrade_authority_key),
 | |
|     );
 | |
| 
 | |
|     // Invoke set_upgrade_authority via CPI invoker program
 | |
|     set_upgrade_authority_instruction.program_id = invoke_and_return;
 | |
|     set_upgrade_authority_instruction
 | |
|         .accounts
 | |
|         .insert(0, AccountMeta::new(bpf_loader_upgradeable::id(), false));
 | |
| 
 | |
|     let message = Message::new(
 | |
|         &[set_upgrade_authority_instruction],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair, &authority_keypair], message)
 | |
|         .unwrap();
 | |
| 
 | |
|     // Assert upgrade authority was changed
 | |
|     let program_account_data = bank_client.get_account_data(&program_id).unwrap().unwrap();
 | |
|     let program_account = parse_bpf_upgradeable_loader(&program_account_data).unwrap();
 | |
| 
 | |
|     let upgrade_authority_key = match program_account {
 | |
|         BpfUpgradeableLoaderAccountType::Program(ui_program) => {
 | |
|             let program_data_account_key = Pubkey::from_str(&ui_program.program_data).unwrap();
 | |
|             let program_data_account_data = bank_client
 | |
|                 .get_account_data(&program_data_account_key)
 | |
|                 .unwrap()
 | |
|                 .unwrap();
 | |
|             let program_data_account =
 | |
|                 parse_bpf_upgradeable_loader(&program_data_account_data).unwrap();
 | |
| 
 | |
|             match program_data_account {
 | |
|                 BpfUpgradeableLoaderAccountType::ProgramData(ui_program_data) => ui_program_data
 | |
|                     .authority
 | |
|                     .map(|a| Pubkey::from_str(&a).unwrap()),
 | |
|                 _ => None,
 | |
|             }
 | |
|         }
 | |
|         _ => None,
 | |
|     };
 | |
| 
 | |
|     assert_eq!(Some(new_upgrade_authority_key), upgrade_authority_key);
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_upgradeable_locks() {
 | |
|     fn setup_program_upgradeable_locks(
 | |
|         payer_keypair: &Keypair,
 | |
|         buffer_keypair: &Keypair,
 | |
|         program_keypair: &Keypair,
 | |
|     ) -> (Arc<Bank>, Transaction, Transaction) {
 | |
|         solana_logger::setup();
 | |
| 
 | |
|         let GenesisConfigInfo {
 | |
|             genesis_config,
 | |
|             mint_keypair,
 | |
|             ..
 | |
|         } = create_genesis_config(2_000_000_000);
 | |
|         let mut bank = Bank::new(&genesis_config);
 | |
|         let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
 | |
|         bank.add_builtin(&name, id, entrypoint);
 | |
|         let bank = Arc::new(bank);
 | |
|         let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|         load_upgradeable_bpf_program(
 | |
|             &bank_client,
 | |
|             &mint_keypair,
 | |
|             buffer_keypair,
 | |
|             program_keypair,
 | |
|             payer_keypair,
 | |
|             "solana_bpf_rust_panic",
 | |
|         );
 | |
| 
 | |
|         // Load the buffer account
 | |
|         let path = create_bpf_path("solana_bpf_rust_noop");
 | |
|         let mut file = File::open(&path).unwrap_or_else(|err| {
 | |
|             panic!("Failed to open {}: {}", path.display(), err);
 | |
|         });
 | |
|         let mut elf = Vec::new();
 | |
|         file.read_to_end(&mut elf).unwrap();
 | |
|         load_buffer_account(
 | |
|             &bank_client,
 | |
|             &mint_keypair,
 | |
|             buffer_keypair,
 | |
|             &payer_keypair,
 | |
|             &elf,
 | |
|         );
 | |
| 
 | |
|         bank_client
 | |
|             .send_and_confirm_instruction(
 | |
|                 &mint_keypair,
 | |
|                 system_instruction::transfer(
 | |
|                     &mint_keypair.pubkey(),
 | |
|                     &payer_keypair.pubkey(),
 | |
|                     1_000_000_000,
 | |
|                 ),
 | |
|             )
 | |
|             .unwrap();
 | |
| 
 | |
|         let invoke_tx = Transaction::new(
 | |
|             &[payer_keypair],
 | |
|             Message::new(
 | |
|                 &[Instruction::new_with_bytes(
 | |
|                     program_keypair.pubkey(),
 | |
|                     &[0; 0],
 | |
|                     vec![],
 | |
|                 )],
 | |
|                 Some(&payer_keypair.pubkey()),
 | |
|             ),
 | |
|             bank.last_blockhash(),
 | |
|         );
 | |
|         let upgrade_tx = Transaction::new(
 | |
|             &[payer_keypair],
 | |
|             Message::new(
 | |
|                 &[bpf_loader_upgradeable::upgrade(
 | |
|                     &program_keypair.pubkey(),
 | |
|                     &buffer_keypair.pubkey(),
 | |
|                     &payer_keypair.pubkey(),
 | |
|                     &payer_keypair.pubkey(),
 | |
|                 )],
 | |
|                 Some(&payer_keypair.pubkey()),
 | |
|             ),
 | |
|             bank.last_blockhash(),
 | |
|         );
 | |
| 
 | |
|         (bank, invoke_tx, upgrade_tx)
 | |
|     }
 | |
| 
 | |
|     let payer_keypair = keypair_from_seed(&[56u8; 32]).unwrap();
 | |
|     let buffer_keypair = keypair_from_seed(&[11; 32]).unwrap();
 | |
|     let program_keypair = keypair_from_seed(&[77u8; 32]).unwrap();
 | |
| 
 | |
|     let results1 = {
 | |
|         let (bank, invoke_tx, upgrade_tx) =
 | |
|             setup_program_upgradeable_locks(&payer_keypair, &buffer_keypair, &program_keypair);
 | |
|         execute_transactions(&bank, &[upgrade_tx, invoke_tx])
 | |
|     };
 | |
| 
 | |
|     let results2 = {
 | |
|         let (bank, invoke_tx, upgrade_tx) =
 | |
|             setup_program_upgradeable_locks(&payer_keypair, &buffer_keypair, &program_keypair);
 | |
|         execute_transactions(&bank, &[invoke_tx, upgrade_tx])
 | |
|     };
 | |
| 
 | |
|     if false {
 | |
|         println!("upgrade and invoke");
 | |
|         for result in &results1 {
 | |
|             print_confirmed_tx("result", result.clone());
 | |
|         }
 | |
|         println!("invoke and upgrade");
 | |
|         for result in &results2 {
 | |
|             print_confirmed_tx("result", result.clone());
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if let Some(ref meta) = results1[0].transaction.meta {
 | |
|         assert_eq!(meta.status, Ok(()));
 | |
|     } else {
 | |
|         panic!("no meta");
 | |
|     }
 | |
|     if let Some(ref meta) = results1[1].transaction.meta {
 | |
|         assert_eq!(meta.status, Err(TransactionError::AccountInUse));
 | |
|     } else {
 | |
|         panic!("no meta");
 | |
|     }
 | |
|     if let Some(ref meta) = results2[0].transaction.meta {
 | |
|         assert_eq!(
 | |
|             meta.status,
 | |
|             Err(TransactionError::InstructionError(
 | |
|                 0,
 | |
|                 InstructionError::ProgramFailedToComplete
 | |
|             ))
 | |
|         );
 | |
|     } else {
 | |
|         panic!("no meta");
 | |
|     }
 | |
|     if let Some(ref meta) = results2[1].transaction.meta {
 | |
|         assert_eq!(meta.status, Err(TransactionError::AccountInUse));
 | |
|     } else {
 | |
|         panic!("no meta");
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "bpf_rust")]
 | |
| #[test]
 | |
| fn test_program_bpf_finalize() {
 | |
|     solana_logger::setup();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(50);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let (name, id, entrypoint) = solana_bpf_loader_program!();
 | |
|     bank.add_builtin(&name, id, entrypoint);
 | |
|     let bank = Arc::new(bank);
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     let program_pubkey = load_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         "solana_bpf_rust_finalize",
 | |
|     );
 | |
| 
 | |
|     let noop_keypair = Keypair::new();
 | |
| 
 | |
|     // Write the noop program into the same program account
 | |
|     let elf = read_bpf_program("solana_bpf_rust_noop");
 | |
|     let message = Message::new(
 | |
|         &[system_instruction::create_account(
 | |
|             &mint_keypair.pubkey(),
 | |
|             &noop_keypair.pubkey(),
 | |
|             1,
 | |
|             elf.len() as u64 * 2,
 | |
|             &bpf_loader::id(),
 | |
|         )],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
|     assert!(bank_client
 | |
|         .send_and_confirm_message(&[&mint_keypair, &noop_keypair], message)
 | |
|         .is_ok());
 | |
|     write_bpf_program(
 | |
|         &bank_client,
 | |
|         &bpf_loader::id(),
 | |
|         &mint_keypair,
 | |
|         &noop_keypair,
 | |
|         &elf,
 | |
|     );
 | |
| 
 | |
|     let account_metas = vec![
 | |
|         AccountMeta::new(noop_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, &noop_keypair], message);
 | |
|     assert_eq!(
 | |
|         result.unwrap_err().unwrap(),
 | |
|         TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
 | |
|     );
 | |
| }
 |