use solana_bpf_loader_program::{ create_vm, serialization::{deserialize_parameters, serialize_parameters}, }; use solana_program::{ bpf_loader, entrypoint::SUCCESS, instruction::InstructionError, program_error::ProgramError, pubkey::Pubkey, }; use solana_rbpf::vm::EbpfVm; use solana_sdk::{ account::Account, keyed_account::KeyedAccount, process_instruction::MockInvokeContext, }; use spl_shared_memory::entrypoint; use std::{fs::File, io::Read, path::PathBuf}; fn load_program(name: &str) -> Vec { let mut path = PathBuf::new(); path.push(name); path.set_extension("so"); let mut file = File::open(&path) .unwrap_or_else(|err| panic!("Unable to open {}: {}", path.display(), err)); let mut program = Vec::new(); file.read_to_end(&mut program).unwrap(); program } fn run_program( program_id: &Pubkey, parameter_accounts: &[KeyedAccount], instruction_data: &[u8], ) -> Result { let mut program_account = Account::default(); program_account.data = load_program("spl_shared_memory"); let loader_id = bpf_loader::id(); let mut invoke_context = MockInvokeContext::default(); let executable = EbpfVm::::create_executable_from_elf( &&program_account.data, None, ) .unwrap(); let (mut vm, heap_region) = create_vm( &loader_id, executable.as_ref(), parameter_accounts, &mut invoke_context, ) .unwrap(); let mut parameter_bytes = serialize_parameters( &loader_id, program_id, parameter_accounts, &instruction_data, ) .unwrap(); assert_eq!( SUCCESS, vm.execute_program(parameter_bytes.as_mut_slice(), &[], &[heap_region]) .unwrap() ); deserialize_parameters(&loader_id, parameter_accounts, ¶meter_bytes).unwrap(); Ok(vm.get_total_instruction_count()) } #[test] fn assert_instruction_count() { const OFFSET: usize = 51; const NUM_TO_SHARE: usize = 500; let program_id = Pubkey::new_unique(); let shared_key = Pubkey::new_unique(); let shared_account = Account::new_ref(u64::MAX, OFFSET + NUM_TO_SHARE * 2, &program_id); // Send some data to share let parameter_accounts = vec![KeyedAccount::new(&shared_key, true, &shared_account)]; let content = vec![42; NUM_TO_SHARE]; let mut instruction_data = OFFSET.to_le_bytes().to_vec(); instruction_data.extend_from_slice(&content); let share_count = run_program(&program_id, ¶meter_accounts[..], &instruction_data).unwrap(); const BASELINE_COUNT: u64 = 1474; // 113 if NUM_TO_SHARE is 8 println!( "BPF instructions executed {:?} (expected {:?})", share_count, BASELINE_COUNT ); assert_eq!( &shared_account.borrow().data[OFFSET..OFFSET + NUM_TO_SHARE], content ); assert!(share_count <= BASELINE_COUNT); } #[test] fn test_share_data() { const OFFSET: usize = 51; const NUM_TO_SHARE: usize = 500; let program_id = Pubkey::new(&[0; 32]); let shared_key = Pubkey::new_unique(); let shared_account = Account::new_ref(u64::MAX, NUM_TO_SHARE * 2, &program_id); // success let content = vec![42; NUM_TO_SHARE]; let mut instruction_data = OFFSET.to_le_bytes().to_vec(); instruction_data.extend_from_slice(&content); let keyed_accounts = vec![KeyedAccount::new(&shared_key, true, &shared_account)]; let mut input = serialize_parameters( &bpf_loader::id(), &program_id, &keyed_accounts, &instruction_data, ) .unwrap(); assert_eq!(unsafe { entrypoint(input.as_mut_ptr()) }, SUCCESS); // success zero offset let content = vec![42; NUM_TO_SHARE]; let mut instruction_data = 0_usize.to_le_bytes().to_vec(); instruction_data.extend_from_slice(&content); let keyed_accounts = vec![KeyedAccount::new(&shared_key, true, &shared_account)]; let mut input = serialize_parameters( &bpf_loader::id(), &program_id, &keyed_accounts, &instruction_data, ) .unwrap(); assert_eq!(unsafe { entrypoint(input.as_mut_ptr()) }, SUCCESS); // too few accounts let mut input = serialize_parameters(&bpf_loader::id(), &program_id, &[], &instruction_data).unwrap(); assert_eq!( unsafe { entrypoint(input.as_mut_ptr()) }, u64::from(ProgramError::NotEnoughAccountKeys) ); // too many accounts let keyed_accounts = vec![ KeyedAccount::new(&shared_key, true, &shared_account), KeyedAccount::new(&shared_key, true, &shared_account), ]; let mut input = serialize_parameters( &bpf_loader::id(), &program_id, &keyed_accounts, &instruction_data, ) .unwrap(); assert_eq!( unsafe { entrypoint(input.as_mut_ptr()) }, u64::from(ProgramError::InvalidArgument) ); // account data too small let keyed_accounts = vec![KeyedAccount::new(&shared_key, true, &shared_account)]; let content = vec![42; NUM_TO_SHARE * 10]; let mut instruction_data = OFFSET.to_le_bytes().to_vec(); instruction_data.extend_from_slice(&content); let mut input = serialize_parameters( &bpf_loader::id(), &program_id, &keyed_accounts, &instruction_data, ) .unwrap(); assert_eq!( unsafe { entrypoint(input.as_mut_ptr()) }, u64::from(ProgramError::AccountDataTooSmall) ); // offset too large let keyed_accounts = vec![KeyedAccount::new(&shared_key, true, &shared_account)]; let content = vec![42; NUM_TO_SHARE]; let mut instruction_data = (OFFSET * 10).to_le_bytes().to_vec(); instruction_data.extend_from_slice(&content); let mut input = serialize_parameters( &bpf_loader::id(), &program_id, &keyed_accounts, &instruction_data, ) .unwrap(); assert_eq!( unsafe { entrypoint(input.as_mut_ptr()) }, u64::from(ProgramError::AccountDataTooSmall) ); }