diff --git a/program-runtime/src/instruction_processor.rs b/program-runtime/src/instruction_processor.rs index 2c01ab14ad..3aac34b57c 100644 --- a/program-runtime/src/instruction_processor.rs +++ b/program-runtime/src/instruction_processor.rs @@ -283,6 +283,7 @@ impl std::fmt::Debug for InstructionProcessor { // These are just type aliases for work around of Debug-ing above pointers type ErasedProcessInstructionWithContext = fn( &'static Pubkey, + usize, &'static [u8], &'static mut dyn InvokeContext, ) -> Result<(), InstructionError>; @@ -361,15 +362,20 @@ impl InstructionProcessor { if solana_sdk::native_loader::check_id(&root_account.owner()?) { for (id, process_instruction) in &self.programs { if id == root_id { - invoke_context.remove_first_keyed_account()?; // Call the builtin program - return process_instruction(program_id, instruction_data, invoke_context); + return process_instruction( + program_id, + 1, // root_id to be skipped + instruction_data, + invoke_context, + ); } } if !invoke_context.is_feature_active(&remove_native_loader::id()) { // Call the program via the native loader return self.native_loader.process_instruction( &solana_sdk::native_loader::id(), + 0, instruction_data, invoke_context, ); @@ -379,7 +385,12 @@ impl InstructionProcessor { for (id, process_instruction) in &self.programs { if id == owner_id { // Call the program via a builtin loader - return process_instruction(program_id, instruction_data, invoke_context); + return process_instruction( + program_id, + 0, // no root_id was provided + instruction_data, + invoke_context, + ); } } } @@ -1074,6 +1085,7 @@ mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_process_instruction( _program_id: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { @@ -1082,6 +1094,7 @@ mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_ix_processor( _pubkey: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { diff --git a/program-runtime/src/native_loader.rs b/program-runtime/src/native_loader.rs index ec3b6dfaeb..1621d98fa2 100644 --- a/program-runtime/src/native_loader.rs +++ b/program-runtime/src/native_loader.rs @@ -138,12 +138,13 @@ impl NativeLoader { pub fn process_instruction( &self, program_id: &Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { let (program_id, name_vec) = { let keyed_accounts = invoke_context.get_keyed_accounts()?; - let program = keyed_account_at_index(keyed_accounts, 0)?; + let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?; if native_loader::id() != *program_id { error!("Program id mismatch"); return Err(InstructionError::IncorrectProgramId); @@ -173,6 +174,7 @@ impl NativeLoader { return Err(NativeLoaderError::InvalidAccountData.into()); } trace!("Call native {:?}", name); + #[allow(deprecated)] invoke_context.remove_first_keyed_account()?; if name.ends_with("loader_program") { let entrypoint = diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index 66c9e843d0..0735e104cc 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -101,6 +101,7 @@ fn get_invoke_context<'a>() -> &'a mut dyn InvokeContext { pub fn builtin_process_instruction( process_instruction: solana_sdk::entrypoint::ProcessInstruction, program_id: &Pubkey, + _first_instruction_account: usize, input: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { @@ -109,7 +110,8 @@ pub fn builtin_process_instruction( let logger = invoke_context.get_logger(); stable_log::program_invoke(&logger, program_id, invoke_context.invoke_depth()); - let keyed_accounts = invoke_context.get_keyed_accounts()?; + // Skip the processor account + let keyed_accounts = &invoke_context.get_keyed_accounts()?[1..]; // Copy all the accounts into a HashMap to ensure there are no duplicates let mut accounts: HashMap = keyed_accounts @@ -183,11 +185,13 @@ macro_rules! processor { ($process_instruction:expr) => { Some( |program_id: &Pubkey, + first_instruction_account: usize, input: &[u8], invoke_context: &mut dyn solana_sdk::process_instruction::InvokeContext| { $crate::builtin_process_instruction( $process_instruction, program_id, + first_instruction_account, input, invoke_context, ) diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index b7ff80aebf..ef7c0ff173 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -168,22 +168,37 @@ pub fn create_vm<'a>( pub fn process_instruction( program_id: &Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - process_instruction_common(program_id, instruction_data, invoke_context, false) + process_instruction_common( + program_id, + first_instruction_account, + instruction_data, + invoke_context, + false, + ) } pub fn process_instruction_jit( program_id: &Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - process_instruction_common(program_id, instruction_data, invoke_context, true) + process_instruction_common( + program_id, + first_instruction_account, + instruction_data, + invoke_context, + true, + ) } fn process_instruction_common( program_id: &Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, use_jit: bool, @@ -191,45 +206,57 @@ fn process_instruction_common( let logger = invoke_context.get_logger(); let keyed_accounts = invoke_context.get_keyed_accounts()?; - let first_account = keyed_account_at_index(keyed_accounts, 0)?; + let first_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; if first_account.executable()? { + debug_assert_eq!( + first_instruction_account, + 1 - (invoke_context.invoke_depth() > 1) as usize, + ); + if first_account.unsigned_key() != program_id { ic_logger_msg!(logger, "Program id mismatch"); return Err(InstructionError::IncorrectProgramId); } - let program_data_offset = if bpf_loader_upgradeable::check_id(&first_account.owner()?) { - if let UpgradeableLoaderState::Program { - programdata_address, - } = first_account.state()? - { - let programdata = keyed_account_at_index(keyed_accounts, 1)?; - if programdata_address != *programdata.unsigned_key() { - ic_logger_msg!(logger, "Wrong ProgramData account for this Program account"); - return Err(InstructionError::InvalidArgument); - } - if !matches!( - programdata.state()?, - UpgradeableLoaderState::ProgramData { - slot: _, - upgrade_authority_address: _, + let (first_instruction_account, program_data_offset) = + if bpf_loader_upgradeable::check_id(&first_account.owner()?) { + if let UpgradeableLoaderState::Program { + programdata_address, + } = first_account.state()? + { + let programdata = + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; + if programdata_address != *programdata.unsigned_key() { + ic_logger_msg!( + logger, + "Wrong ProgramData account for this Program account" + ); + return Err(InstructionError::InvalidArgument); } - ) { - ic_logger_msg!(logger, "Program has been closed"); + if !matches!( + programdata.state()?, + UpgradeableLoaderState::ProgramData { + slot: _, + upgrade_authority_address: _, + } + ) { + ic_logger_msg!(logger, "Program has been closed"); + return Err(InstructionError::InvalidAccountData); + } + ( + first_instruction_account + 1, + UpgradeableLoaderState::programdata_data_offset()?, + ) + } else { + ic_logger_msg!(logger, "Invalid Program account"); return Err(InstructionError::InvalidAccountData); } - invoke_context.remove_first_keyed_account()?; - UpgradeableLoaderState::programdata_data_offset()? } else { - ic_logger_msg!(logger, "Invalid Program account"); - return Err(InstructionError::InvalidAccountData); - } - } else { - 0 - }; + (first_instruction_account, 0) + }; let keyed_accounts = invoke_context.get_keyed_accounts()?; - let program = keyed_account_at_index(keyed_accounts, 0)?; + let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let loader_id = &program.owner()?; if !check_loader_id(loader_id) { @@ -240,19 +267,25 @@ fn process_instruction_common( let executor = match invoke_context.get_executor(program_id) { Some(executor) => executor, None => { - let executor = create_executor(0, program_data_offset, invoke_context, use_jit)?; + let executor = create_executor( + first_instruction_account, + program_data_offset, + invoke_context, + use_jit, + )?; invoke_context.add_executor(program_id, executor.clone()); executor } }; executor.execute( - loader_id, program_id, + first_instruction_account, instruction_data, invoke_context, use_jit, )? } else { + debug_assert_eq!(first_instruction_account, 1); if !check_loader_id(program_id) { ic_logger_msg!(logger, "Invalid BPF loader id"); return Err(InstructionError::IncorrectProgramId); @@ -261,12 +294,19 @@ fn process_instruction_common( if bpf_loader_upgradeable::check_id(program_id) { process_loader_upgradeable_instruction( program_id, + first_instruction_account, instruction_data, invoke_context, use_jit, )?; } else { - process_loader_instruction(program_id, instruction_data, invoke_context, use_jit)?; + process_loader_instruction( + program_id, + first_instruction_account, + instruction_data, + invoke_context, + use_jit, + )?; } } Ok(()) @@ -274,6 +314,7 @@ fn process_instruction_common( fn process_loader_upgradeable_instruction( program_id: &Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, use_jit: bool, @@ -283,22 +324,22 @@ fn process_loader_upgradeable_instruction( match limited_deserialize(instruction_data)? { UpgradeableLoaderInstruction::InitializeBuffer => { - let buffer = keyed_account_at_index(keyed_accounts, 0)?; + let buffer = keyed_account_at_index(keyed_accounts, first_instruction_account)?; if UpgradeableLoaderState::Uninitialized != buffer.state()? { ic_logger_msg!(logger, "Buffer account already initialized"); return Err(InstructionError::AccountAlreadyInitialized); } - let authority = keyed_account_at_index(keyed_accounts, 1)?; + let authority = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; buffer.set_state(&UpgradeableLoaderState::Buffer { authority_address: Some(*authority.unsigned_key()), })?; } UpgradeableLoaderInstruction::Write { offset, bytes } => { - let buffer = keyed_account_at_index(keyed_accounts, 0)?; - let authority = keyed_account_at_index(keyed_accounts, 1)?; + let buffer = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let authority = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; if let UpgradeableLoaderState::Buffer { authority_address } = buffer.state()? { if authority_address.is_none() { @@ -318,20 +359,27 @@ fn process_loader_upgradeable_instruction( return Err(InstructionError::InvalidAccountData); } write_program_data( - 0, + 1, UpgradeableLoaderState::buffer_data_offset()? + offset as usize, &bytes, invoke_context, )?; } UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => { - let payer = keyed_account_at_index(keyed_accounts, 0)?; - let programdata = keyed_account_at_index(keyed_accounts, 1)?; - let program = keyed_account_at_index(keyed_accounts, 2)?; - let buffer = keyed_account_at_index(keyed_accounts, 3)?; - let rent = from_keyed_account::(keyed_account_at_index(keyed_accounts, 4)?)?; - let clock = from_keyed_account::(keyed_account_at_index(keyed_accounts, 5)?)?; - let authority = keyed_account_at_index(keyed_accounts, 7)?; + let payer = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let programdata = + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; + let program = keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?; + let buffer = keyed_account_at_index(keyed_accounts, first_instruction_account + 3)?; + let rent = from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 4, + )?)?; + let clock = from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 5, + )?)?; + let authority = keyed_account_at_index(keyed_accounts, first_instruction_account + 7)?; let upgrade_authority_address = Some(*authority.unsigned_key()); let upgrade_authority_signer = authority.signer_key().is_none(); @@ -434,14 +482,15 @@ fn process_loader_upgradeable_instruction( )?; // Load and verify the program bits - let executor = create_executor(3, buffer_data_offset, invoke_context, use_jit)?; + let executor = create_executor(4, buffer_data_offset, invoke_context, use_jit)?; invoke_context.add_executor(&new_program_id, executor); let keyed_accounts = invoke_context.get_keyed_accounts()?; - let payer = keyed_account_at_index(keyed_accounts, 0)?; - let programdata = keyed_account_at_index(keyed_accounts, 1)?; - let program = keyed_account_at_index(keyed_accounts, 2)?; - let buffer = keyed_account_at_index(keyed_accounts, 3)?; + let payer = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let programdata = + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; + let program = keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?; + let buffer = keyed_account_at_index(keyed_accounts, first_instruction_account + 3)?; // Update the ProgramData account and record the program bits programdata.set_state(&UpgradeableLoaderState::ProgramData { @@ -469,12 +518,18 @@ fn process_loader_upgradeable_instruction( ic_logger_msg!(logger, "Deployed program {:?}", new_program_id); } UpgradeableLoaderInstruction::Upgrade => { - let programdata = keyed_account_at_index(keyed_accounts, 0)?; - let program = keyed_account_at_index(keyed_accounts, 1)?; - let buffer = keyed_account_at_index(keyed_accounts, 2)?; - let rent = from_keyed_account::(keyed_account_at_index(keyed_accounts, 4)?)?; - let clock = from_keyed_account::(keyed_account_at_index(keyed_accounts, 5)?)?; - let authority = keyed_account_at_index(keyed_accounts, 6)?; + let programdata = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let program = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; + let buffer = keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?; + let rent = from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 4, + )?)?; + let clock = from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 5, + )?)?; + let authority = keyed_account_at_index(keyed_accounts, first_instruction_account + 6)?; // Verify Program account @@ -566,14 +621,14 @@ fn process_loader_upgradeable_instruction( } // Load and verify the program bits - let executor = create_executor(2, buffer_data_offset, invoke_context, use_jit)?; + let executor = create_executor(3, buffer_data_offset, invoke_context, use_jit)?; invoke_context.add_executor(&new_program_id, executor); let keyed_accounts = invoke_context.get_keyed_accounts()?; - let programdata = keyed_account_at_index(keyed_accounts, 0)?; - let buffer = keyed_account_at_index(keyed_accounts, 2)?; - let spill = keyed_account_at_index(keyed_accounts, 3)?; - let authority = keyed_account_at_index(keyed_accounts, 6)?; + let programdata = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let buffer = keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?; + let spill = keyed_account_at_index(keyed_accounts, first_instruction_account + 3)?; + let authority = keyed_account_at_index(keyed_accounts, first_instruction_account + 6)?; // Update the ProgramData account, record the upgraded data, and zero // the rest @@ -602,11 +657,13 @@ fn process_loader_upgradeable_instruction( ic_logger_msg!(logger, "Upgraded program {:?}", new_program_id); } UpgradeableLoaderInstruction::SetAuthority => { - let account = keyed_account_at_index(keyed_accounts, 0)?; - let present_authority = keyed_account_at_index(keyed_accounts, 1)?; - let new_authority = keyed_account_at_index(keyed_accounts, 2) - .ok() - .map(|account| account.unsigned_key()); + let account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let present_authority = + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; + let new_authority = + keyed_account_at_index(keyed_accounts, first_instruction_account + 2) + .ok() + .map(|account| account.unsigned_key()); match account.state()? { UpgradeableLoaderState::Buffer { authority_address } => { @@ -660,10 +717,11 @@ fn process_loader_upgradeable_instruction( ic_logger_msg!(logger, "New authority {:?}", new_authority); } UpgradeableLoaderInstruction::Close => { - let close_account = keyed_account_at_index(keyed_accounts, 0)?; - let recipient_account = keyed_account_at_index(keyed_accounts, 1)?; + let close_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let recipient_account = + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; if !invoke_context.is_feature_active(&close_upgradeable_program_accounts::id()) { - let _ = keyed_account_at_index(keyed_accounts, 2)?; + let _ = keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?; } if close_account.unsigned_key() == recipient_account.unsigned_key() { @@ -697,7 +755,8 @@ fn process_loader_upgradeable_instruction( ); } UpgradeableLoaderState::Buffer { authority_address } => { - let authority = keyed_account_at_index(keyed_accounts, 2)?; + let authority = + keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?; common_close_account( &authority_address, @@ -715,7 +774,8 @@ fn process_loader_upgradeable_instruction( slot: _, upgrade_authority_address: authority_address, } => { - let program_account = keyed_account_at_index(keyed_accounts, 3)?; + let program_account = + keyed_account_at_index(keyed_accounts, first_instruction_account + 3)?; if !program_account.is_writable() { ic_logger_msg!(logger, "Program account is not writable"); @@ -738,7 +798,10 @@ fn process_loader_upgradeable_instruction( return Err(InstructionError::InvalidArgument); } - let authority = keyed_account_at_index(keyed_accounts, 2)?; + let authority = keyed_account_at_index( + keyed_accounts, + first_instruction_account + 2, + )?; common_close_account( &authority_address, authority, @@ -805,12 +868,13 @@ fn common_close_account( fn process_loader_instruction( program_id: &Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, use_jit: bool, ) -> Result<(), InstructionError> { let keyed_accounts = invoke_context.get_keyed_accounts()?; - let program = keyed_account_at_index(keyed_accounts, 0)?; + let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?; if program.owner()? != *program_id { ic_msg!( invoke_context, @@ -824,7 +888,7 @@ fn process_loader_instruction( ic_msg!(invoke_context, "Program account did not sign"); return Err(InstructionError::MissingRequiredSignature); } - write_program_data(0, offset as usize, &bytes, invoke_context)?; + write_program_data(1, offset as usize, &bytes, invoke_context)?; } LoaderInstruction::Finalize => { if program.signer_key().is_none() { @@ -832,9 +896,9 @@ fn process_loader_instruction( return Err(InstructionError::MissingRequiredSignature); } - let executor = create_executor(0, 0, invoke_context, use_jit)?; + let executor = create_executor(1, 0, invoke_context, use_jit)?; let keyed_accounts = invoke_context.get_keyed_accounts()?; - let program = keyed_account_at_index(keyed_accounts, 0)?; + let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?; invoke_context.add_executor(program.unsigned_key(), executor); program.try_account_ref_mut()?.set_executable(true); ic_msg!( @@ -883,8 +947,8 @@ impl Debug for BpfExecutor { impl Executor for BpfExecutor { fn execute( &self, - loader_id: &Pubkey, program_id: &Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, use_jit: bool, @@ -894,12 +958,16 @@ impl Executor for BpfExecutor { let add_missing_program_error_mappings = invoke_context.is_feature_active(&add_missing_program_error_mappings::id()); - invoke_context.remove_first_keyed_account()?; - let mut serialize_time = Measure::start("serialize"); let keyed_accounts = invoke_context.get_keyed_accounts()?; - let (mut parameter_bytes, account_lengths) = - serialize_parameters(loader_id, program_id, keyed_accounts, instruction_data)?; + let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let loader_id = &program.owner()?; + let (mut parameter_bytes, account_lengths) = serialize_parameters( + loader_id, + program_id, + &keyed_accounts[first_instruction_account + 1..], + instruction_data, + )?; serialize_time.stop(); let mut create_vm_time = Measure::start("create_vm"); let mut execute_time; @@ -984,7 +1052,7 @@ impl Executor for BpfExecutor { let keyed_accounts = invoke_context.get_keyed_accounts()?; deserialize_parameters( loader_id, - keyed_accounts, + &keyed_accounts[first_instruction_account + 1..], parameter_bytes.as_slice(), &account_lengths, invoke_context.is_feature_active(&do_support_realloc::id()), @@ -1018,8 +1086,9 @@ mod tests { genesis_config::create_genesis_config, instruction::Instruction, instruction::{AccountMeta, InstructionError}, - keyed_account::KeyedAccount, + keyed_account::create_keyed_accounts_unified, message::Message, + native_loader, native_token::LAMPORTS_PER_SOL, process_instruction::{MockComputeMeter, MockInvokeContext}, pubkey::Pubkey, @@ -1042,6 +1111,38 @@ mod tests { } } + type KeyedAccountTuple<'a> = (bool, bool, &'a Pubkey, &'a RefCell); + fn process_instruction( + owner: &Pubkey, + instruction_data: &[u8], + keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + ) -> Result<(), InstructionError> { + let processor_account = AccountSharedData::new_ref(0, 0, &native_loader::id()); + let mut keyed_accounts = keyed_accounts.to_vec(); + keyed_accounts.insert(0, (false, false, owner, &processor_account)); + super::process_instruction( + owner, + 1, + instruction_data, + &mut MockInvokeContext::new(create_keyed_accounts_unified(&keyed_accounts)), + ) + } + + fn load_program_account_from_elf( + loader_id: &Pubkey, + path: &str, + ) -> Rc> { + let mut file = File::open(path).expect("file open failed"); + let mut elf = Vec::new(); + file.read_to_end(&mut elf).unwrap(); + let rent = Rent::default(); + let program_account = + AccountSharedData::new_ref(rent.minimum_balance(elf.len()), 0, loader_id); + program_account.borrow_mut().set_data(elf); + program_account.borrow_mut().set_executable(true); + program_account + } + #[test] #[should_panic(expected = "ExceededMaxInstructions(31, 10)")] fn test_bpf_loader_non_terminating_program() { @@ -1081,286 +1182,202 @@ mod tests { #[test] fn test_bpf_loader_write() { - let program_id = bpf_loader::id(); + let loader_id = bpf_loader::id(); let program_key = solana_sdk::pubkey::new_rand(); - let program_account = AccountSharedData::new_ref(1, 0, &program_id); - let keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)]; + let program_account = AccountSharedData::new_ref(1, 0, &loader_id); + let mut keyed_accounts: Vec = vec![]; let instruction_data = bincode::serialize(&LoaderInstruction::Write { offset: 3, bytes: vec![1, 2, 3], }) .unwrap(); - // Case: Empty keyed accounts + // Case: No program account assert_eq!( Err(InstructionError::NotEnoughAccountKeys), - process_instruction( - &bpf_loader::id(), - &instruction_data, - &mut MockInvokeContext::new(vec![]) - ) + process_instruction(&loader_id, &instruction_data, &keyed_accounts), ); // Case: Not signed + keyed_accounts.push((false, false, &program_key, &program_account)); assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction( - &bpf_loader::id(), - &instruction_data, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction_data, &keyed_accounts), ); // Case: Write bytes to an offset - #[allow(unused_mut)] - let mut keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)]; - keyed_accounts[0].account.borrow_mut().set_data(vec![0; 6]); - let mut invoke_context = MockInvokeContext::new(keyed_accounts); + keyed_accounts[0] = (true, false, &program_key, &program_account); + keyed_accounts[0].3.borrow_mut().set_data(vec![0; 6]); assert_eq!( Ok(()), - process_instruction(&bpf_loader::id(), &instruction_data, &mut invoke_context) - ); - let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); - assert_eq!( - &vec![0, 0, 0, 1, 2, 3], - keyed_accounts[0].account.borrow().data() + process_instruction(&loader_id, &instruction_data, &keyed_accounts), ); + assert_eq!(&vec![0, 0, 0, 1, 2, 3], keyed_accounts[0].3.borrow().data()); // Case: Overflow - #[allow(unused_mut)] - let mut keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)]; - keyed_accounts[0].account.borrow_mut().set_data(vec![0; 5]); + keyed_accounts[0].3.borrow_mut().set_data(vec![0; 5]); assert_eq!( Err(InstructionError::AccountDataTooSmall), - process_instruction( - &bpf_loader::id(), - &instruction_data, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&bpf_loader::id(), &instruction_data, &keyed_accounts), ); } #[test] fn test_bpf_loader_finalize() { - let program_id = bpf_loader::id(); + let loader_id = bpf_loader::id(); let program_key = solana_sdk::pubkey::new_rand(); - let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed"); - let mut elf = Vec::new(); - let rent = Rent::default(); - file.read_to_end(&mut elf).unwrap(); let program_account = - AccountSharedData::new_ref(rent.minimum_balance(elf.len()), 0, &program_id); - program_account.borrow_mut().set_data(elf); - let keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)]; - let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap(); + load_program_account_from_elf(&loader_id, "test_elfs/noop_aligned.so"); + program_account.borrow_mut().set_executable(false); - // Case: Empty keyed accounts + // Case: No program account + let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap(); + let mut keyed_accounts: Vec = vec![]; assert_eq!( Err(InstructionError::NotEnoughAccountKeys), - process_instruction( - &bpf_loader::id(), - &instruction_data, - &mut MockInvokeContext::new(vec![]) - ) + process_instruction(&loader_id, &instruction_data, &keyed_accounts), ); // Case: Not signed + keyed_accounts.push((false, false, &program_key, &program_account)); assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction( - &bpf_loader::id(), - &instruction_data, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction_data, &keyed_accounts), ); // Case: Finalize - let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)]; - let mut invoke_context = MockInvokeContext::new(keyed_accounts); + keyed_accounts[0] = (true, false, &program_key, &program_account); assert_eq!( Ok(()), - process_instruction(&bpf_loader::id(), &instruction_data, &mut invoke_context) + process_instruction(&loader_id, &instruction_data, &keyed_accounts), ); - let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); - assert!(keyed_accounts[0].account.borrow().executable()); + assert!(keyed_accounts[0].3.borrow().executable()); program_account.borrow_mut().set_executable(false); // Un-finalize the account // Case: Finalize program_account.borrow_mut().data_as_mut_slice()[0] = 0; // bad elf - let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)]; assert_eq!( Err(InstructionError::InvalidAccountData), - process_instruction( - &bpf_loader::id(), - &instruction_data, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction_data, &keyed_accounts), ); } #[test] fn test_bpf_loader_invoke_main() { - let program_id = bpf_loader::id(); + let loader_id = bpf_loader::id(); let program_key = solana_sdk::pubkey::new_rand(); + let program_account = + load_program_account_from_elf(&loader_id, "test_elfs/noop_aligned.so"); - // Create program account - let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed"); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); - let program_account = AccountSharedData::new_ref(1, 0, &program_id); - program_account.borrow_mut().set_data(elf); - program_account.borrow_mut().set_executable(true); - - let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)]; - - // Case: Empty keyed accounts + // Case: No program account + let mut keyed_accounts: Vec = vec![]; assert_eq!( Err(InstructionError::NotEnoughAccountKeys), - process_instruction(&program_id, &[], &mut MockInvokeContext::new(vec![])) + process_instruction(&loader_id, &[], &keyed_accounts), ); // Case: Only a program account + keyed_accounts.push((false, false, &program_key, &program_account)); assert_eq!( Ok(()), - process_instruction( - &program_key, - &[], - &mut MockInvokeContext::new(keyed_accounts.clone()) - ) + process_instruction(&program_key, &[], &keyed_accounts), ); // Case: Account not a program - keyed_accounts[0].account.borrow_mut().set_executable(false); + keyed_accounts[0].3.borrow_mut().set_executable(false); assert_eq!( Err(InstructionError::InvalidInstructionData), - process_instruction( - &program_id, - &[], - &mut MockInvokeContext::new(keyed_accounts.clone()) - ) + process_instruction(&loader_id, &[], &keyed_accounts), ); - keyed_accounts[0].account.borrow_mut().set_executable(true); + keyed_accounts[0].3.borrow_mut().set_executable(true); // Case: With program and parameter account - let parameter_account = AccountSharedData::new_ref(1, 0, &program_id); - keyed_accounts.push(KeyedAccount::new(&program_key, false, ¶meter_account)); + let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); + keyed_accounts.push((false, false, &program_key, ¶meter_account)); assert_eq!( Ok(()), - process_instruction( - &program_key, - &[], - &mut MockInvokeContext::new(keyed_accounts.clone()) - ) + process_instruction(&program_key, &[], &keyed_accounts), ); // Case: With duplicate accounts let duplicate_key = solana_sdk::pubkey::new_rand(); - let parameter_account = AccountSharedData::new_ref(1, 0, &program_id); - let keyed_accounts = vec![ - KeyedAccount::new(&program_key, false, &program_account), - KeyedAccount::new(&duplicate_key, false, ¶meter_account), - KeyedAccount::new(&duplicate_key, false, ¶meter_account), - ]; + let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); + keyed_accounts[1] = (false, false, &duplicate_key, ¶meter_account); + keyed_accounts.push((false, false, &duplicate_key, ¶meter_account)); assert_eq!( Ok(()), - process_instruction( - &program_key, - &[], - &mut MockInvokeContext::new(keyed_accounts.clone()) - ) + process_instruction(&program_key, &[], &keyed_accounts), ); // Case: limited budget - let mut invoke_context = MockInvokeContext::new(keyed_accounts); + let processor_account = AccountSharedData::new_ref(0, 0, &native_loader::id()); + let mut keyed_accounts = keyed_accounts.clone(); + keyed_accounts.insert(0, (false, false, &program_key, &processor_account)); + let mut invoke_context = + MockInvokeContext::new(create_keyed_accounts_unified(&keyed_accounts)); invoke_context.compute_meter = MockComputeMeter::default(); assert_eq!( Err(InstructionError::ProgramFailedToComplete), - process_instruction(&program_key, &[], &mut invoke_context) + super::process_instruction(&program_key, 1, &[], &mut invoke_context) ); } #[test] fn test_bpf_loader_serialize_unaligned() { - let program_id = bpf_loader_deprecated::id(); + let loader_id = bpf_loader_deprecated::id(); let program_key = solana_sdk::pubkey::new_rand(); - - // Create program account - let mut file = File::open("test_elfs/noop_unaligned.so").expect("file open failed"); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); - let program_account = AccountSharedData::new_ref(1, 0, &program_id); - program_account.borrow_mut().set_data(elf); - program_account.borrow_mut().set_executable(true); - let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)]; + let program_account = + load_program_account_from_elf(&loader_id, "test_elfs/noop_unaligned.so"); // Case: With program and parameter account - let parameter_account = AccountSharedData::new_ref(1, 0, &program_id); - keyed_accounts.push(KeyedAccount::new(&program_key, false, ¶meter_account)); + let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); + let mut keyed_accounts: Vec = vec![ + (false, false, &program_key, &program_account), + (false, false, &program_key, ¶meter_account), + ]; assert_eq!( Ok(()), - process_instruction( - &program_key, - &[], - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&program_key, &[], &keyed_accounts), ); // Case: With duplicate accounts let duplicate_key = solana_sdk::pubkey::new_rand(); - let parameter_account = AccountSharedData::new_ref(1, 0, &program_id); - let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)]; - keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account)); - keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account)); + let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); + keyed_accounts[1] = (false, false, &duplicate_key, ¶meter_account); + keyed_accounts.push((false, false, &duplicate_key, ¶meter_account)); assert_eq!( Ok(()), - process_instruction( - &program_key, - &[], - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&program_key, &[], &keyed_accounts), ); } #[test] fn test_bpf_loader_serialize_aligned() { - let program_id = bpf_loader::id(); + let loader_id = bpf_loader::id(); let program_key = solana_sdk::pubkey::new_rand(); - - // Create program account - let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed"); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); - let program_account = AccountSharedData::new_ref(1, 0, &program_id); - program_account.borrow_mut().set_data(elf); - program_account.borrow_mut().set_executable(true); - let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)]; + let program_account = + load_program_account_from_elf(&loader_id, "test_elfs/noop_aligned.so"); // Case: With program and parameter account - let parameter_account = AccountSharedData::new_ref(1, 0, &program_id); - keyed_accounts.push(KeyedAccount::new(&program_key, false, ¶meter_account)); + let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); + let mut keyed_accounts: Vec = vec![ + (false, false, &program_key, &program_account), + (false, false, &program_key, ¶meter_account), + ]; assert_eq!( Ok(()), - process_instruction( - &program_key, - &[], - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&program_key, &[], &keyed_accounts), ); // Case: With duplicate accounts let duplicate_key = solana_sdk::pubkey::new_rand(); - let parameter_account = AccountSharedData::new_ref(1, 0, &program_id); - let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)]; - keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account)); - keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account)); + let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); + keyed_accounts[1] = (false, false, &duplicate_key, ¶meter_account); + keyed_accounts.push(keyed_accounts[1]); assert_eq!( Ok(()), - process_instruction( - &program_key, - &[], - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&program_key, &[], &keyed_accounts), ); } @@ -1368,31 +1385,28 @@ mod tests { fn test_bpf_loader_upgradeable_initialize_buffer() { let instruction = bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(); + let loader_id = bpf_loader_upgradeable::id(); let buffer_address = Pubkey::new_unique(); let buffer_account = AccountSharedData::new_ref( 1, UpgradeableLoaderState::buffer_len(9).unwrap(), - &bpf_loader_upgradeable::id(), + &loader_id, ); let authority_address = Pubkey::new_unique(); let authority_account = AccountSharedData::new_ref( 1, UpgradeableLoaderState::buffer_len(9).unwrap(), - &bpf_loader_upgradeable::id(), + &loader_id, ); // Case: Success - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&authority_address, false, &authority_account), + let keyed_accounts: Vec = vec![ + (false, false, &buffer_address, &buffer_account), + (false, false, &authority_address, &authority_account), ]; assert_eq!( Ok(()), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -1403,17 +1417,9 @@ mod tests { ); // Case: Already initialized - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&authority_address, false, &authority_account), - ]; assert_eq!( Err(InstructionError::AccountAlreadyInitialized), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -1426,11 +1432,12 @@ mod tests { #[test] fn test_bpf_loader_upgradeable_write() { + let loader_id = bpf_loader_upgradeable::id(); let buffer_address = Pubkey::new_unique(); let buffer_account = AccountSharedData::new_ref( 1, UpgradeableLoaderState::buffer_len(9).unwrap(), - &bpf_loader_upgradeable::id(), + &loader_id, ); // Case: Not initialized @@ -1439,17 +1446,13 @@ mod tests { bytes: vec![42; 9], }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, true, &buffer_account), + let keyed_accounts: Vec = vec![ + (false, false, &buffer_address, &buffer_account), + (true, false, &buffer_address, &buffer_account), ]; assert_eq!( Err(InstructionError::InvalidAccountData), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: Write entire buffer @@ -1464,17 +1467,9 @@ mod tests { authority_address: Some(buffer_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, true, &buffer_account), - ]; assert_eq!( Ok(()), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -1498,7 +1493,7 @@ mod tests { let buffer_account = AccountSharedData::new_ref( 1, UpgradeableLoaderState::buffer_len(9).unwrap(), - &bpf_loader_upgradeable::id(), + &loader_id, ); buffer_account .borrow_mut() @@ -1506,17 +1501,13 @@ mod tests { authority_address: Some(buffer_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, true, &buffer_account), + let keyed_accounts: Vec = vec![ + (false, false, &buffer_address, &buffer_account), + (true, false, &buffer_address, &buffer_account), ]; assert_eq!( Ok(()), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -1531,31 +1522,6 @@ mod tests { &[0, 0, 0, 42, 42, 42, 42, 42, 42] ); - // Case: Not signed - let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write { - offset: 0, - bytes: vec![42; 9], - }) - .unwrap(); - buffer_account - .borrow_mut() - .set_state(&UpgradeableLoaderState::Buffer { - authority_address: Some(buffer_address), - }) - .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - ]; - assert_eq!( - Err(InstructionError::MissingRequiredSignature), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) - ); - // Case: overflow size let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write { offset: 0, @@ -1568,17 +1534,9 @@ mod tests { authority_address: Some(buffer_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, true, &buffer_account), - ]; assert_eq!( Err(InstructionError::AccountDataTooSmall), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: overflow offset @@ -1593,17 +1551,30 @@ mod tests { authority_address: Some(buffer_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, true, &buffer_account), - ]; assert_eq!( Err(InstructionError::AccountDataTooSmall), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), + ); + + // Case: Not signed + let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write { + offset: 0, + bytes: vec![42; 9], + }) + .unwrap(); + buffer_account + .borrow_mut() + .set_state(&UpgradeableLoaderState::Buffer { + authority_address: Some(buffer_address), + }) + .unwrap(); + let keyed_accounts: Vec = vec![ + (false, false, &buffer_address, &buffer_account), + (false, false, &buffer_address, &buffer_account), + ]; + assert_eq!( + Err(InstructionError::MissingRequiredSignature), + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: wrong authority @@ -1619,21 +1590,16 @@ mod tests { authority_address: Some(buffer_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&authority_address, true, &buffer_account), + let keyed_accounts: Vec = vec![ + (false, false, &buffer_address, &buffer_account), + (true, false, &authority_address, &buffer_account), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: None authority - let authority_address = Pubkey::new_unique(); let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write { offset: 1, bytes: vec![42; 9], @@ -1645,17 +1611,9 @@ mod tests { authority_address: None, }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&authority_address, true, &buffer_account), - ]; assert_eq!( Err(InstructionError::Immutable), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); } @@ -1673,7 +1631,7 @@ mod tests { bank.add_builtin( "solana_bpf_loader_upgradeable_program", &bpf_loader_upgradeable::id(), - process_instruction, + super::process_instruction, ); let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); @@ -2339,6 +2297,7 @@ mod tests { #[test] fn test_bpf_loader_upgradeable_upgrade() { let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap(); + let loader_id = bpf_loader_upgradeable::id(); let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed"); let mut elf_orig = Vec::new(); file.read_to_end(&mut elf_orig).unwrap(); @@ -2361,10 +2320,8 @@ mod tests { let upgrade_authority_address = Pubkey::new_unique(); let buffer_address = Pubkey::new_unique(); let program_address = Pubkey::new_unique(); - let (programdata_address, _) = Pubkey::find_program_address( - &[program_address.as_ref()], - &bpf_loader_upgradeable::id(), - ); + let (programdata_address, _) = + Pubkey::find_program_address(&[program_address.as_ref()], &loader_id); let spill_address = Pubkey::new_unique(); let upgrade_authority_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique()); let rent_id = sysvar::rent::id(); @@ -2445,26 +2402,23 @@ mod tests { min_program_balance, min_programdata_balance, ); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Ok(()), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); assert_eq!(0, buffer_account.borrow().lamports()); assert_eq!( @@ -2507,26 +2461,23 @@ mod tests { upgrade_authority_address: None, }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::Immutable), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: wrong authority @@ -2541,26 +2492,23 @@ mod tests { min_programdata_balance, ); let invalid_upgrade_authority_address = Pubkey::new_unique(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &invalid_upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &invalid_upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: authority did not sign @@ -2574,26 +2522,23 @@ mod tests { min_program_balance, min_programdata_balance, ); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( false, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: Program account not executable @@ -2608,26 +2553,23 @@ mod tests { min_programdata_balance, ); program_account.borrow_mut().set_executable(false); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::AccountNotExecutable), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: Program account now owned by loader @@ -2642,26 +2584,23 @@ mod tests { min_programdata_balance, ); program_account.borrow_mut().set_owner(Pubkey::new_unique()); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::IncorrectProgramId), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: Program account not writable @@ -2675,26 +2614,23 @@ mod tests { min_program_balance, min_programdata_balance, ); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, false, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::InvalidArgument), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: Program account not initialized @@ -2712,26 +2648,23 @@ mod tests { .borrow_mut() .set_state(&UpgradeableLoaderState::Uninitialized) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::InvalidAccountData), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: Program ProgramData account mismatch @@ -2746,26 +2679,28 @@ mod tests { min_programdata_balance, ); let invalid_programdata_address = Pubkey::new_unique(); - let keyed_accounts = vec![ - KeyedAccount::new(&invalid_programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + ( + false, true, + &invalid_programdata_address, + &programdata_account, + ), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( + true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::InvalidArgument), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: Buffer account not initialized @@ -2783,26 +2718,23 @@ mod tests { .borrow_mut() .set_state(&UpgradeableLoaderState::Uninitialized) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::InvalidArgument), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: Buffer account too big @@ -2819,7 +2751,7 @@ mod tests { let buffer_account = AccountSharedData::new_ref( 1, UpgradeableLoaderState::buffer_len(elf_orig.len().max(elf_new.len()) + 1).unwrap(), - &bpf_loader_upgradeable::id(), + &loader_id, ); buffer_account .borrow_mut() @@ -2827,26 +2759,23 @@ mod tests { authority_address: Some(upgrade_authority_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::AccountDataTooSmall), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Test small buffer account @@ -2867,26 +2796,23 @@ mod tests { }) .unwrap(); truncate_data(&mut buffer_account.borrow_mut(), 5); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::InvalidAccountData), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: Mismatched buffer and program authority @@ -2900,26 +2826,23 @@ mod tests { min_program_balance, min_programdata_balance, ); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: None buffer authority @@ -2939,26 +2862,23 @@ mod tests { authority_address: None, }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: None buffer and program authority @@ -2985,32 +2905,30 @@ mod tests { upgrade_authority_address: None, }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&spill_address, false, &spill_account), - KeyedAccount::new_readonly(&rent_id, false, &rent_account), - KeyedAccount::new_readonly(&clock_id, false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, true, &programdata_address, &programdata_account), + (false, true, &program_address, &program_account), + (false, true, &buffer_address, &buffer_account), + (false, true, &spill_address, &spill_account), + (false, false, &rent_id, &rent_account), + (false, false, &clock_id, &clock_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); } #[test] fn test_bpf_loader_upgradeable_set_upgrade_authority() { let instruction = bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(); + let loader_id = bpf_loader_upgradeable::id(); let slot = 0; let upgrade_authority_address = Pubkey::new_unique(); let upgrade_authority_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique()); @@ -3035,26 +2953,24 @@ mod tests { upgrade_authority_address: Some(upgrade_authority_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, false, &programdata_address, &programdata_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), - KeyedAccount::new_readonly( - &new_upgrade_authority_address, + ( + true, false, + &new_upgrade_authority_address, &new_upgrade_authority_account, ), ]; assert_eq!( Ok(()), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = programdata_account.borrow().state().unwrap(); assert_eq!( @@ -3073,21 +2989,18 @@ mod tests { upgrade_authority_address: Some(upgrade_authority_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, false, &programdata_address, &programdata_account), + ( true, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Ok(()), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = programdata_account.borrow().state().unwrap(); assert_eq!( @@ -3106,21 +3019,18 @@ mod tests { upgrade_authority_address: Some(upgrade_authority_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, false, &programdata_address, &programdata_account), + ( false, + false, + &upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: wrong authority @@ -3132,26 +3042,24 @@ mod tests { }) .unwrap(); let invalid_upgrade_authority_address = Pubkey::new_unique(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &invalid_upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, false, &programdata_address, &programdata_account), + ( true, + false, + &invalid_upgrade_authority_address, &upgrade_authority_account, ), - KeyedAccount::new_readonly( - &new_upgrade_authority_address, + ( false, + false, + &new_upgrade_authority_address, &new_upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: No authority @@ -3163,20 +3071,21 @@ mod tests { }) .unwrap(); let invalid_upgrade_authority_address = Pubkey::new_unique(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &invalid_upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, false, &programdata_address, &programdata_account), + ( true, + false, + &invalid_upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::Immutable), process_instruction( - &bpf_loader_upgradeable::id(), + &loader_id, &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ) ); @@ -3188,20 +3097,21 @@ mod tests { }) .unwrap(); let invalid_upgrade_authority_address = Pubkey::new_unique(); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &invalid_upgrade_authority_address, + let keyed_accounts: Vec = vec![ + (false, false, &programdata_address, &programdata_account), + ( true, + false, + &invalid_upgrade_authority_address, &upgrade_authority_account, ), ]; assert_eq!( Err(InstructionError::InvalidArgument), process_instruction( - &bpf_loader_upgradeable::id(), + &loader_id, &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ) ); } @@ -3209,6 +3119,7 @@ mod tests { #[test] fn test_bpf_loader_upgradeable_set_buffer_authority() { let instruction = bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(); + let loader_id = bpf_loader_upgradeable::id(); let authority_address = Pubkey::new_unique(); let authority_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique()); let new_authority_address = Pubkey::new_unique(); @@ -3217,35 +3128,7 @@ mod tests { let buffer_account = AccountSharedData::new_ref( 1, UpgradeableLoaderState::buffer_len(0).unwrap(), - &bpf_loader_upgradeable::id(), - ); - - // Case: Set to new authority - buffer_account - .borrow_mut() - .set_state(&UpgradeableLoaderState::Buffer { - authority_address: Some(authority_address), - }) - .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&authority_address, true, &authority_account), - KeyedAccount::new_readonly(&new_authority_address, false, &new_authority_account), - ]; - assert_eq!( - Ok(()), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) - ); - let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); - assert_eq!( - state, - UpgradeableLoaderState::Buffer { - authority_address: Some(new_authority_address), - } + &loader_id, ); // Case: New authority required @@ -3255,17 +3138,13 @@ mod tests { authority_address: Some(authority_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&authority_address, true, &authority_account), + let mut keyed_accounts: Vec = vec![ + (false, false, &buffer_address, &buffer_account), + (true, false, &authority_address, &authority_account), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); assert_eq!( @@ -3275,6 +3154,26 @@ mod tests { } ); + // Case: Set to new authority + buffer_account + .borrow_mut() + .set_state(&UpgradeableLoaderState::Buffer { + authority_address: Some(authority_address), + }) + .unwrap(); + keyed_accounts.push((false, false, &new_authority_address, &new_authority_account)); + assert_eq!( + Ok(()), + process_instruction(&loader_id, &instruction, &keyed_accounts), + ); + let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); + assert_eq!( + state, + UpgradeableLoaderState::Buffer { + authority_address: Some(new_authority_address), + } + ); + // Case: Authority did not sign buffer_account .borrow_mut() @@ -3282,18 +3181,10 @@ mod tests { authority_address: Some(authority_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&authority_address, false, &authority_account), - KeyedAccount::new_readonly(&new_authority_address, false, &new_authority_account), - ]; + keyed_accounts[1] = (false, false, &authority_address, &authority_account); assert_eq!( Err(InstructionError::MissingRequiredSignature), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: wrong authority @@ -3304,18 +3195,10 @@ mod tests { }) .unwrap(); let invalid_authority_address = Pubkey::new_unique(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&invalid_authority_address, true, &authority_account), - KeyedAccount::new_readonly(&new_authority_address, false, &new_authority_account), - ]; + keyed_accounts[1] = (true, false, &invalid_authority_address, &authority_account); assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: No authority @@ -3325,18 +3208,12 @@ mod tests { authority_address: None, }) .unwrap(); - let invalid_authority_address = Pubkey::new_unique(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&invalid_authority_address, true, &authority_account), - KeyedAccount::new_readonly(&new_authority_address, false, &new_authority_account), - ]; assert_eq!( Err(InstructionError::Immutable), process_instruction( - &bpf_loader_upgradeable::id(), + &loader_id, &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ) ); @@ -3347,17 +3224,13 @@ mod tests { programdata_address: Pubkey::new_unique(), }) .unwrap(); - let invalid_authority_address = Pubkey::new_unique(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&invalid_authority_address, true, &authority_account), - ]; + keyed_accounts.pop(); assert_eq!( Err(InstructionError::InvalidArgument), process_instruction( - &bpf_loader_upgradeable::id(), + &loader_id, &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ) ); @@ -3368,23 +3241,17 @@ mod tests { authority_address: Some(authority_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&authority_address, true, &authority_account), - ]; + keyed_accounts[1] = (true, false, &authority_address, &authority_account); assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts) - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); } #[test] fn test_bpf_loader_upgradeable_close() { let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap(); + let loader_id = bpf_loader_upgradeable::id(); let authority_address = Pubkey::new_unique(); let authority_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique()); let recipient_address = Pubkey::new_unique(); @@ -3403,18 +3270,14 @@ mod tests { authority_address: Some(authority_address), }) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&recipient_address, false, &recipient_account), - KeyedAccount::new_readonly(&authority_address, true, &authority_account), + let keyed_accounts: Vec = vec![ + (false, false, &buffer_address, &buffer_account), + (false, false, &recipient_address, &recipient_account), + (true, false, &authority_address, &authority_account), ]; assert_eq!( Ok(()), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts), - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); assert_eq!(0, buffer_account.borrow().lamports()); assert_eq!(2, recipient_account.borrow().lamports()); @@ -3429,18 +3292,19 @@ mod tests { }) .unwrap(); let incorrect_authority_address = Pubkey::new_unique(); - let keyed_accounts = vec![ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&recipient_address, false, &recipient_account), - KeyedAccount::new_readonly(&incorrect_authority_address, true, &authority_account), + let keyed_accounts: Vec = vec![ + (false, false, &buffer_address, &buffer_account), + (false, false, &recipient_address, &recipient_account), + ( + true, + false, + &incorrect_authority_address, + &authority_account, + ), ]; assert_eq!( Err(InstructionError::IncorrectAuthority), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts), - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); // Case: close an uninitialized account @@ -3455,17 +3319,13 @@ mod tests { .set_state(&UpgradeableLoaderState::Uninitialized) .unwrap(); let recipient_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique()); - let keyed_accounts = vec![ - KeyedAccount::new(&uninitialized_address, false, &uninitialized_account), - KeyedAccount::new(&recipient_address, false, &recipient_account), + let keyed_accounts: Vec = vec![ + (false, false, &uninitialized_address, &uninitialized_account), + (false, false, &recipient_address, &recipient_account), ]; assert_eq!( Ok(()), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts), - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); assert_eq!(0, uninitialized_account.borrow().lamports()); assert_eq!(2, recipient_account.borrow().lamports()); @@ -3500,19 +3360,15 @@ mod tests { }) .unwrap(); let recipient_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique()); - let keyed_accounts = vec![ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new(&recipient_address, false, &recipient_account), - KeyedAccount::new_readonly(&authority_address, true, &authority_account), - KeyedAccount::new(&program_address, false, &program_account), + let keyed_accounts: Vec = vec![ + (false, false, &programdata_address, &programdata_account), + (false, false, &recipient_address, &recipient_account), + (true, false, &authority_address, &authority_account), + (false, true, &program_address, &program_account), ]; assert_eq!( Ok(()), - process_instruction( - &bpf_loader_upgradeable::id(), - &instruction, - &mut MockInvokeContext::new(keyed_accounts), - ) + process_instruction(&loader_id, &instruction, &keyed_accounts), ); assert_eq!(0, programdata_account.borrow().lamports()); assert_eq!(2, recipient_account.borrow().lamports()); @@ -3520,17 +3376,13 @@ mod tests { assert_eq!(state, UpgradeableLoaderState::Uninitialized); // Try to invoke closed account - let keyed_accounts = vec![ - KeyedAccount::new(&program_address, false, &program_account), - KeyedAccount::new(&programdata_address, false, &programdata_account), + let keyed_accounts: Vec = vec![ + (false, false, &program_address, &program_account), + (false, false, &programdata_address, &programdata_account), ]; assert_eq!( Err(InstructionError::InvalidAccountData), - process_instruction( - &program_address, - &instruction, - &mut MockInvokeContext::new(keyed_accounts), - ) + process_instruction(&program_address, &instruction, &keyed_accounts), ); } @@ -3560,7 +3412,7 @@ mod tests { #[test] #[ignore] fn test_fuzz() { - let program_id = solana_sdk::pubkey::new_rand(); + let loader_id = solana_sdk::pubkey::new_rand(); let program_key = solana_sdk::pubkey::new_rand(); // Create program account @@ -3576,21 +3428,17 @@ mod tests { 0..elf.len(), 0..255, |bytes: &mut [u8]| { - let program_account = AccountSharedData::new_ref(1, 0, &program_id); + let program_account = AccountSharedData::new_ref(1, 0, &loader_id); program_account.borrow_mut().set_data(bytes.to_vec()); program_account.borrow_mut().set_executable(true); - let parameter_account = AccountSharedData::new_ref(1, 0, &program_id); - let keyed_accounts = vec![ - KeyedAccount::new(&program_key, false, &program_account), - KeyedAccount::new(&program_key, false, ¶meter_account), + let parameter_account = AccountSharedData::new_ref(1, 0, &loader_id); + let keyed_accounts: Vec = vec![ + (false, false, &program_key, &program_account), + (false, false, &program_key, ¶meter_account), ]; - let _result = process_instruction( - &bpf_loader::id(), - &[], - &mut MockInvokeContext::new(keyed_accounts), - ); + let _result = process_instruction(&bpf_loader::id(), &[], &keyed_accounts); }, ); } diff --git a/programs/compute-budget/src/lib.rs b/programs/compute-budget/src/lib.rs index 13b1fb9b04..44fc58886a 100644 --- a/programs/compute-budget/src/lib.rs +++ b/programs/compute-budget/src/lib.rs @@ -4,6 +4,7 @@ use solana_sdk::{ pub fn process_instruction( _program_id: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { diff --git a/programs/config/src/config_processor.rs b/programs/config/src/config_processor.rs index f7c1a94dbb..0672f3c8e9 100644 --- a/programs/config/src/config_processor.rs +++ b/programs/config/src/config_processor.rs @@ -15,13 +15,15 @@ use std::collections::BTreeSet; pub fn process_instruction( _program_id: &Pubkey, + first_instruction_account: usize, data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { let keyed_accounts = invoke_context.get_keyed_accounts()?; let key_list: ConfigKeys = limited_deserialize(data)?; - let config_keyed_account = &mut keyed_account_at_index(keyed_accounts, 0)?; + let config_keyed_account = + &mut keyed_account_at_index(keyed_accounts, first_instruction_account)?; let current_data: ConfigKeys = { let config_account = config_keyed_account.try_account_ref_mut()?; if config_account.owner() != &crate::id() { @@ -53,7 +55,7 @@ pub fn process_instruction( } let mut counter = 0; - let mut keyed_accounts_iter = keyed_accounts.iter().skip(1); + let mut keyed_accounts_iter = keyed_accounts.iter().skip(2); for (signer, _) in key_list.keys.iter().filter(|(_, is_signer)| *is_signer) { counter += 1; if signer != config_keyed_account.unsigned_key() { @@ -147,6 +149,22 @@ mod tests { }; use std::cell::RefCell; + fn process_instruction( + owner: &Pubkey, + instruction_data: &[u8], + keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + ) -> Result<(), InstructionError> { + let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()); + let mut keyed_accounts = keyed_accounts.to_vec(); + keyed_accounts.insert(0, (false, false, owner, &processor_account)); + super::process_instruction( + owner, + 1, + instruction_data, + &mut MockInvokeContext::new(create_keyed_accounts_unified(&keyed_accounts)), + ) + } + #[derive(Serialize, Deserialize, Debug, PartialEq)] struct MyConfig { pub item: u64, @@ -193,14 +211,9 @@ mod tests { owner: id(), ..Account::default() })); - let accounts = vec![(true, false, &config_pubkey, &config_account)]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); + let keyed_accounts = [(true, false, &config_pubkey, &config_account)]; assert_eq!( - process_instruction( - &id(), - &instructions[1].data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instructions[1].data, &keyed_accounts), Ok(()) ); @@ -210,8 +223,7 @@ mod tests { #[test] fn test_process_create_ok() { solana_logger::setup(); - let keys = vec![]; - let (_, config_account) = create_config_account(keys); + let (_, config_account) = create_config_account(vec![]); assert_eq!( Some(MyConfig::default()), deserialize(get_config_data(config_account.borrow().data()).unwrap()).ok() @@ -227,14 +239,9 @@ mod tests { let my_config = MyConfig::new(42); let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); - let accounts = vec![(true, false, &config_pubkey, &config_account)]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); + let keyed_accounts = [(true, false, &config_pubkey, &config_account)]; assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Ok(()) ); assert_eq!( @@ -253,14 +260,9 @@ mod tests { let mut instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); instruction.data = vec![0; 123]; // <-- Replace data with a vector that's too large - let accounts = vec![(true, false, &config_pubkey, &config_account)]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); + let keyed_accounts = [(true, false, &config_pubkey, &config_account)]; assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Err(InstructionError::InvalidInstructionData) ); } @@ -275,14 +277,9 @@ mod tests { let mut instruction = config_instruction::store(&config_pubkey, true, vec![], &my_config); instruction.accounts[0].is_signer = false; // <----- not a signer - let accounts = vec![(false, false, &config_pubkey, &config_account)]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); + let keyed_accounts = [(false, false, &config_pubkey, &config_account)]; assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Err(InstructionError::MissingRequiredSignature) ); } @@ -305,18 +302,13 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); let signer0_account = RefCell::new(AccountSharedData::default()); let signer1_account = RefCell::new(AccountSharedData::default()); - let accounts = vec![ + let keyed_accounts = [ (true, false, &config_pubkey, &config_account), (true, false, &signer0_pubkey, &signer0_account), (true, false, &signer1_pubkey, &signer1_account), ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Ok(()) ); let meta_data: ConfigKeys = deserialize(config_account.borrow().data()).unwrap(); @@ -342,14 +334,9 @@ mod tests { owner: id(), ..Account::default() })); - let accounts = vec![(true, false, &signer0_pubkey, &signer0_account)]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); + let keyed_accounts = [(true, false, &signer0_pubkey, &signer0_account)]; assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Err(InstructionError::InvalidAccountData) ); } @@ -369,32 +356,19 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); // Config-data pubkey doesn't match signer - let accounts = vec![ + let mut keyed_accounts = [ (true, false, &config_pubkey, &config_account), (true, false, &signer1_pubkey, &signer1_account), ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Err(InstructionError::MissingRequiredSignature) ); // Config-data pubkey not a signer - let accounts = vec![ - (true, false, &config_pubkey, &config_account), - (false, false, &signer0_pubkey, &signer0_account), - ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); + keyed_accounts[1] = (false, false, &signer0_pubkey, &signer0_account); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Err(InstructionError::MissingRequiredSignature) ); } @@ -419,18 +393,13 @@ mod tests { let my_config = MyConfig::new(42); let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); - let accounts = vec![ + let mut keyed_accounts = [ (true, false, &config_pubkey, &config_account), (true, false, &signer0_pubkey, &signer0_account), (true, false, &signer1_pubkey, &signer1_account), ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Ok(()) ); @@ -438,18 +407,9 @@ mod tests { let new_config = MyConfig::new(84); let instruction = config_instruction::store(&config_pubkey, false, keys.clone(), &new_config); - let accounts = vec![ - (false, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), - (true, false, &signer1_pubkey, &signer1_account), - ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); + keyed_accounts[0].0 = false; assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Ok(()) ); let meta_data: ConfigKeys = deserialize(config_account.borrow().data()).unwrap(); @@ -463,18 +423,9 @@ mod tests { // Attempt update with incomplete signatures let keys = vec![(pubkey, false), (signer0_pubkey, true)]; let instruction = config_instruction::store(&config_pubkey, false, keys, &my_config); - let accounts = vec![ - (false, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), - (false, false, &signer1_pubkey, &signer1_account), - ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); + keyed_accounts[2].0 = false; assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Err(InstructionError::MissingRequiredSignature) ); @@ -485,18 +436,9 @@ mod tests { (signer2_pubkey, true), ]; let instruction = config_instruction::store(&config_pubkey, false, keys, &my_config); - let accounts = vec![ - (false, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), - (true, false, &signer2_pubkey, &signer2_account), - ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); + keyed_accounts[2] = (true, false, &signer2_pubkey, &signer2_account); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Err(InstructionError::MissingRequiredSignature) ); } @@ -518,18 +460,13 @@ mod tests { // Attempt initialization with duplicate signer inputs let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); - let accounts = vec![ + let keyed_accounts = [ (true, false, &config_pubkey, &config_account), (true, false, &signer0_pubkey, &signer0_account), (true, false, &signer0_pubkey, &signer0_account), ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Err(InstructionError::InvalidArgument), ); } @@ -552,18 +489,13 @@ mod tests { let my_config = MyConfig::new(42); let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); - let accounts = vec![ + let mut keyed_accounts = [ (true, false, &config_pubkey, &config_account), (true, false, &signer0_pubkey, &signer0_account), (true, false, &signer1_pubkey, &signer1_account), ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Ok(()), ); @@ -575,18 +507,9 @@ mod tests { (signer0_pubkey, true), ]; let instruction = config_instruction::store(&config_pubkey, false, dupe_keys, &new_config); - let accounts = vec![ - (false, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), - (true, false, &signer0_pubkey, &signer0_account), - ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); + keyed_accounts[2] = keyed_accounts[1]; assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Err(InstructionError::InvalidArgument), ); } @@ -613,17 +536,12 @@ mod tests { ]; let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); - let accounts = vec![ + let keyed_accounts = [ (true, false, &config_pubkey, &config_account), (true, false, &signer0_pubkey, &signer0_account), ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Ok(()) ); @@ -631,17 +549,8 @@ mod tests { let new_config = MyConfig::new(84); let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &new_config); - let accounts = vec![ - (true, false, &config_pubkey, &config_account), - (true, false, &signer0_pubkey, &signer0_account), - ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Ok(()) ); let meta_data: ConfigKeys = deserialize(config_account.borrow().data()).unwrap(); @@ -655,14 +564,8 @@ mod tests { // Attempt update with incomplete signatures let keys = vec![(pubkey, false), (config_keypair.pubkey(), true)]; let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); - let accounts = vec![(true, false, &config_pubkey, &config_account)]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instruction.data, &keyed_accounts[0..1]), Err(InstructionError::MissingRequiredSignature) ); } @@ -671,16 +574,11 @@ mod tests { fn test_config_initialize_no_panic() { let from_pubkey = solana_sdk::pubkey::new_rand(); let config_pubkey = solana_sdk::pubkey::new_rand(); + let (_, _config_account) = create_config_account(vec![]); let instructions = config_instruction::create_account::(&from_pubkey, &config_pubkey, 1, vec![]); - let accounts = vec![]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( - process_instruction( - &id(), - &instructions[1].data, - &mut MockInvokeContext::new(keyed_accounts) - ), + process_instruction(&id(), &instructions[1].data, &[]), Err(InstructionError::NotEnoughAccountKeys) ); } @@ -693,6 +591,7 @@ mod tests { let signer0_pubkey = solana_sdk::pubkey::new_rand(); let signer0_account = RefCell::new(AccountSharedData::default()); let config_account = RefCell::new(AccountSharedData::default()); + let (_, _config_account) = create_config_account(vec![]); let keys = vec![ (from_pubkey, false), (signer0_pubkey, true), @@ -700,17 +599,12 @@ mod tests { ]; let instruction = config_instruction::store(&config_pubkey, true, keys, &new_config); - let accounts = vec![ + let keyed_accounts = [ (true, false, &config_pubkey, &config_account), (true, false, &signer0_pubkey, &signer0_account), ]; - let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( - process_instruction( - &id(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts), - ), + process_instruction(&id(), &instruction.data, &keyed_accounts), Err(InstructionError::InvalidAccountOwner) ); } diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 62cb6f2591..af78603100 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -25,6 +25,7 @@ pub use solana_sdk::stake::instruction::*; pub fn process_instruction( _program_id: &Pubkey, + first_instruction_account: usize, data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { @@ -33,19 +34,20 @@ pub fn process_instruction( trace!("process_instruction: {:?}", data); trace!("keyed_accounts: {:?}", keyed_accounts); - let signers = get_signers(keyed_accounts); - - let me = &keyed_account_at_index(keyed_accounts, 0)?; - + let me = &keyed_account_at_index(keyed_accounts, first_instruction_account)?; if me.owner()? != id() { return Err(InstructionError::InvalidAccountOwner); } + let signers = get_signers(&keyed_accounts[1..]); match limited_deserialize(data)? { StakeInstruction::Initialize(authorized, lockup) => me.initialize( &authorized, &lockup, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 1, + )?)?, ), StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => { let require_custodian_for_locked_stake_authorize = invoke_context.is_feature_active( @@ -53,12 +55,16 @@ pub fn process_instruction( ); if require_custodian_for_locked_stake_authorize { - let clock = - from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?; - let _current_authority = keyed_account_at_index(keyed_accounts, 2)?; - let custodian = keyed_account_at_index(keyed_accounts, 3) - .ok() - .map(|ka| ka.unsigned_key()); + let clock = from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 1, + )?)?; + let _current_authority = + keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?; + let custodian = + keyed_account_at_index(keyed_accounts, first_instruction_account + 3) + .ok() + .map(|ka| ka.unsigned_key()); me.authorize( &signers, @@ -80,17 +86,21 @@ pub fn process_instruction( } } StakeInstruction::AuthorizeWithSeed(args) => { - let authority_base = keyed_account_at_index(keyed_accounts, 1)?; + let authority_base = + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; let require_custodian_for_locked_stake_authorize = invoke_context.is_feature_active( &feature_set::require_custodian_for_locked_stake_authorize::id(), ); if require_custodian_for_locked_stake_authorize { - let clock = - from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?; - let custodian = keyed_account_at_index(keyed_accounts, 3) - .ok() - .map(|ka| ka.unsigned_key()); + let clock = from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 2, + )?)?; + let custodian = + keyed_account_at_index(keyed_accounts, first_instruction_account + 3) + .ok() + .map(|ka| ka.unsigned_key()); me.authorize_with_seed( authority_base, @@ -118,48 +128,74 @@ pub fn process_instruction( StakeInstruction::DelegateStake => { let can_reverse_deactivation = invoke_context.is_feature_active(&feature_set::stake_program_v4::id()); - let vote = keyed_account_at_index(keyed_accounts, 1)?; + let vote = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; me.delegate( vote, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 3)?)?, - &config::from_keyed_account(keyed_account_at_index(keyed_accounts, 4)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 2, + )?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 3, + )?)?, + &config::from_keyed_account(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 4, + )?)?, &signers, can_reverse_deactivation, ) } StakeInstruction::Split(lamports) => { - let split_stake = &keyed_account_at_index(keyed_accounts, 1)?; + let split_stake = + &keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; me.split(lamports, split_stake, &signers) } StakeInstruction::Merge => { - let source_stake = &keyed_account_at_index(keyed_accounts, 1)?; + let source_stake = + &keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; let can_merge_expired_lockups = invoke_context.is_feature_active(&feature_set::stake_program_v4::id()); me.merge( invoke_context, source_stake, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 3)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 2, + )?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 3, + )?)?, &signers, can_merge_expired_lockups, ) } StakeInstruction::Withdraw(lamports) => { - let to = &keyed_account_at_index(keyed_accounts, 1)?; + let to = &keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; me.withdraw( lamports, to, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 3)?)?, - keyed_account_at_index(keyed_accounts, 4)?, - keyed_account_at_index(keyed_accounts, 5).ok(), + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 2, + )?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 3, + )?)?, + keyed_account_at_index(keyed_accounts, first_instruction_account + 4)?, + keyed_account_at_index(keyed_accounts, first_instruction_account + 5).ok(), invoke_context.is_feature_active(&feature_set::stake_program_v4::id()), ) } StakeInstruction::Deactivate => me.deactivate( - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 1, + )?)?, &signers, ), StakeInstruction::SetLockup(lockup) => { @@ -174,16 +210,23 @@ pub fn process_instruction( if invoke_context.is_feature_active(&feature_set::vote_stake_checked_instructions::id()) { let authorized = Authorized { - staker: *keyed_account_at_index(keyed_accounts, 2)?.unsigned_key(), - withdrawer: *keyed_account_at_index(keyed_accounts, 3)? - .signer_key() - .ok_or(InstructionError::MissingRequiredSignature)?, + staker: *keyed_account_at_index(keyed_accounts, first_instruction_account + 2)? + .unsigned_key(), + withdrawer: *keyed_account_at_index( + keyed_accounts, + first_instruction_account + 3, + )? + .signer_key() + .ok_or(InstructionError::MissingRequiredSignature)?, }; me.initialize( &authorized, &Lockup::default(), - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 1, + )?)?, ) } else { Err(InstructionError::InvalidInstructionData) @@ -192,15 +235,20 @@ pub fn process_instruction( StakeInstruction::AuthorizeChecked(stake_authorize) => { if invoke_context.is_feature_active(&feature_set::vote_stake_checked_instructions::id()) { - let clock = - from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?; - let _current_authority = keyed_account_at_index(keyed_accounts, 2)?; - let authorized_pubkey = &keyed_account_at_index(keyed_accounts, 3)? - .signer_key() - .ok_or(InstructionError::MissingRequiredSignature)?; - let custodian = keyed_account_at_index(keyed_accounts, 4) - .ok() - .map(|ka| ka.unsigned_key()); + let clock = from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 1, + )?)?; + let _current_authority = + keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?; + let authorized_pubkey = + &keyed_account_at_index(keyed_accounts, first_instruction_account + 3)? + .signer_key() + .ok_or(InstructionError::MissingRequiredSignature)?; + let custodian = + keyed_account_at_index(keyed_accounts, first_instruction_account + 4) + .ok() + .map(|ka| ka.unsigned_key()); me.authorize( &signers, @@ -217,15 +265,20 @@ pub fn process_instruction( StakeInstruction::AuthorizeCheckedWithSeed(args) => { if invoke_context.is_feature_active(&feature_set::vote_stake_checked_instructions::id()) { - let authority_base = keyed_account_at_index(keyed_accounts, 1)?; - let clock = - from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?; - let authorized_pubkey = &keyed_account_at_index(keyed_accounts, 3)? - .signer_key() - .ok_or(InstructionError::MissingRequiredSignature)?; - let custodian = keyed_account_at_index(keyed_accounts, 4) - .ok() - .map(|ka| ka.unsigned_key()); + let authority_base = + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; + let clock = from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 2, + )?)?; + let authorized_pubkey = + &keyed_account_at_index(keyed_accounts, first_instruction_account + 3)? + .signer_key() + .ok_or(InstructionError::MissingRequiredSignature)?; + let custodian = + keyed_account_at_index(keyed_accounts, first_instruction_account + 4) + .ok() + .map(|ka| ka.unsigned_key()); me.authorize_with_seed( authority_base, @@ -244,7 +297,9 @@ pub fn process_instruction( StakeInstruction::SetLockupChecked(lockup_checked) => { if invoke_context.is_feature_active(&feature_set::vote_stake_checked_instructions::id()) { - let custodian = if let Ok(custodian) = keyed_account_at_index(keyed_accounts, 2) { + let custodian = if let Ok(custodian) = + keyed_account_at_index(keyed_accounts, first_instruction_account + 2) + { Some( *custodian .signer_key() @@ -276,7 +331,7 @@ mod tests { use solana_sdk::{ account::{self, Account, AccountSharedData, WritableAccount}, instruction::{AccountMeta, Instruction}, - keyed_account::KeyedAccount, + keyed_account::create_keyed_accounts_unified, process_instruction::{mock_set_sysvar, MockInvokeContext}, rent::Rent, stake::{ @@ -315,7 +370,27 @@ mod tests { Pubkey::from_str("Spoofed111111111111111111111111111111111111").unwrap() } - fn process_instruction(instruction: &Instruction) -> Result<(), InstructionError> { + fn process_instruction( + owner: &Pubkey, + instruction_data: &[u8], + keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + ) -> Result<(), InstructionError> { + let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()); + let mut keyed_accounts = keyed_accounts.to_vec(); + keyed_accounts.insert(0, (false, false, owner, &processor_account)); + super::process_instruction( + owner, + 1, + instruction_data, + &mut MockInvokeContext::new(create_keyed_accounts_unified(&keyed_accounts)), + ) + } + + fn process_instruction_as_one_arg(instruction: &Instruction) -> Result<(), InstructionError> { + let processor_account = RefCell::new(AccountSharedData::from(Account { + owner: solana_sdk::native_loader::id(), + ..Account::default() + })); let accounts: Vec<_> = instruction .accounts .iter() @@ -357,28 +432,35 @@ mod tests { .collect(); { - let keyed_accounts: Vec<_> = instruction + let mut keyed_accounts: Vec<_> = instruction .accounts .iter() .zip(accounts.iter()) - .map(|(meta, account)| KeyedAccount::new(&meta.pubkey, meta.is_signer, account)) + .map(|(meta, account)| (meta.is_signer, false, &meta.pubkey, account)) .collect(); - - let mut invoke_context = MockInvokeContext::new(keyed_accounts); + let processor_id = id(); + keyed_accounts.insert(0, (false, false, &processor_id, &processor_account)); + let mut invoke_context = + MockInvokeContext::new(create_keyed_accounts_unified(&keyed_accounts)); mock_set_sysvar( &mut invoke_context, sysvar::clock::id(), sysvar::clock::Clock::default(), ) .unwrap(); - super::process_instruction(&Pubkey::default(), &instruction.data, &mut invoke_context) + super::process_instruction( + &Pubkey::default(), + 1, + &instruction.data, + &mut invoke_context, + ) } } #[test] fn test_stake_process_instruction() { assert_eq!( - process_instruction(&instruction::initialize( + process_instruction_as_one_arg(&instruction::initialize( &Pubkey::default(), &Authorized::default(), &Lockup::default() @@ -386,7 +468,7 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction(&instruction::authorize( + process_instruction_as_one_arg(&instruction::authorize( &Pubkey::default(), &Pubkey::default(), &Pubkey::default(), @@ -396,7 +478,7 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction( + process_instruction_as_one_arg( &instruction::split( &Pubkey::default(), &Pubkey::default(), @@ -407,7 +489,7 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction( + process_instruction_as_one_arg( &instruction::merge( &Pubkey::default(), &invalid_stake_state_pubkey(), @@ -417,7 +499,7 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction( + process_instruction_as_one_arg( &instruction::split_with_seed( &Pubkey::default(), &Pubkey::default(), @@ -430,7 +512,7 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction(&instruction::delegate_stake( + process_instruction_as_one_arg(&instruction::delegate_stake( &Pubkey::default(), &Pubkey::default(), &invalid_vote_state_pubkey(), @@ -438,7 +520,7 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction(&instruction::withdraw( + process_instruction_as_one_arg(&instruction::withdraw( &Pubkey::default(), &Pubkey::default(), &solana_sdk::pubkey::new_rand(), @@ -448,14 +530,14 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction(&instruction::deactivate_stake( + process_instruction_as_one_arg(&instruction::deactivate_stake( &Pubkey::default(), &Pubkey::default() )), Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction(&instruction::set_lockup( + process_instruction_as_one_arg(&instruction::set_lockup( &Pubkey::default(), &LockupArgs::default(), &Pubkey::default() @@ -467,7 +549,7 @@ mod tests { #[test] fn test_spoofed_stake_accounts() { assert_eq!( - process_instruction(&instruction::initialize( + process_instruction_as_one_arg(&instruction::initialize( &spoofed_stake_state_pubkey(), &Authorized::default(), &Lockup::default() @@ -475,7 +557,7 @@ mod tests { Err(InstructionError::InvalidAccountOwner), ); assert_eq!( - process_instruction(&instruction::authorize( + process_instruction_as_one_arg(&instruction::authorize( &spoofed_stake_state_pubkey(), &Pubkey::default(), &Pubkey::default(), @@ -485,7 +567,7 @@ mod tests { Err(InstructionError::InvalidAccountOwner), ); assert_eq!( - process_instruction( + process_instruction_as_one_arg( &instruction::split( &spoofed_stake_state_pubkey(), &Pubkey::default(), @@ -496,7 +578,7 @@ mod tests { Err(InstructionError::InvalidAccountOwner), ); assert_eq!( - process_instruction( + process_instruction_as_one_arg( &instruction::split( &Pubkey::default(), &Pubkey::default(), @@ -507,7 +589,7 @@ mod tests { Err(InstructionError::IncorrectProgramId), ); assert_eq!( - process_instruction( + process_instruction_as_one_arg( &instruction::merge( &spoofed_stake_state_pubkey(), &Pubkey::default(), @@ -517,7 +599,7 @@ mod tests { Err(InstructionError::InvalidAccountOwner), ); assert_eq!( - process_instruction( + process_instruction_as_one_arg( &instruction::merge( &Pubkey::default(), &spoofed_stake_state_pubkey(), @@ -527,7 +609,7 @@ mod tests { Err(InstructionError::IncorrectProgramId), ); assert_eq!( - process_instruction( + process_instruction_as_one_arg( &instruction::split_with_seed( &spoofed_stake_state_pubkey(), &Pubkey::default(), @@ -540,7 +622,7 @@ mod tests { Err(InstructionError::InvalidAccountOwner), ); assert_eq!( - process_instruction(&instruction::delegate_stake( + process_instruction_as_one_arg(&instruction::delegate_stake( &spoofed_stake_state_pubkey(), &Pubkey::default(), &Pubkey::default(), @@ -548,7 +630,7 @@ mod tests { Err(InstructionError::InvalidAccountOwner), ); assert_eq!( - process_instruction(&instruction::withdraw( + process_instruction_as_one_arg(&instruction::withdraw( &spoofed_stake_state_pubkey(), &Pubkey::default(), &solana_sdk::pubkey::new_rand(), @@ -558,14 +640,14 @@ mod tests { Err(InstructionError::InvalidAccountOwner), ); assert_eq!( - process_instruction(&instruction::deactivate_stake( + process_instruction_as_one_arg(&instruction::deactivate_stake( &spoofed_stake_state_pubkey(), &Pubkey::default() )), Err(InstructionError::InvalidAccountOwner), ); assert_eq!( - process_instruction(&instruction::set_lockup( + process_instruction_as_one_arg(&instruction::set_lockup( &spoofed_stake_state_pubkey(), &LockupArgs::default(), &Pubkey::default() @@ -580,14 +662,14 @@ mod tests { // gets the "is_empty()" check assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() )) .unwrap(), - &mut MockInvokeContext::new(vec![]) + &[], ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -595,16 +677,16 @@ mod tests { // no account for rent let stake_address = Pubkey::default(); let stake_account = create_default_stake_account(); - let keyed_accounts = vec![KeyedAccount::new(&stake_address, false, &stake_account)]; + let keyed_accounts = [(false, false, &stake_address, &stake_account)]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() )) .unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -614,19 +696,19 @@ mod tests { let stake_account = create_default_stake_account(); let rent_address = sysvar::rent::id(); let rent_account = create_default_account(); - let keyed_accounts = vec![ - KeyedAccount::new(&stake_address, false, &stake_account), - KeyedAccount::new(&rent_address, false, &rent_account), + let keyed_accounts = [ + (false, false, &stake_address, &stake_account), + (false, false, &rent_address, &rent_account), ]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() )) .unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Err(InstructionError::InvalidArgument), ); @@ -638,19 +720,19 @@ mod tests { let rent_account = RefCell::new(account::create_account_shared_data_for_test( &Rent::default(), )); - let keyed_accounts = vec![ - KeyedAccount::new(&stake_address, false, &stake_account), - KeyedAccount::new(&rent_address, false, &rent_account), + let keyed_accounts = [ + (false, false, &stake_address, &stake_account), + (false, false, &rent_address, &rent_account), ]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() )) .unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Err(InstructionError::InvalidAccountData), ); @@ -658,12 +740,12 @@ mod tests { // gets the first check in delegate, wrong number of accounts let stake_address = Pubkey::default(); let stake_account = create_default_stake_account(); - let keyed_accounts = vec![KeyedAccount::new(&stake_address, false, &stake_account)]; + let keyed_accounts = [(false, false, &stake_address, &stake_account)]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::DelegateStake).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -671,12 +753,12 @@ mod tests { // gets the sub-check for number of args let stake_address = Pubkey::default(); let stake_account = create_default_stake_account(); - let keyed_accounts = vec![KeyedAccount::new(&stake_address, false, &stake_account)]; + let keyed_accounts = [(false, false, &stake_address, &stake_account)]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::DelegateStake).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -700,18 +782,18 @@ mod tests { let config_address = stake_config::id(); let config_account = RefCell::new(config::create_account(0, &stake_config::Config::default())); - let keyed_accounts = vec![ - KeyedAccount::new(&stake_address, true, &stake_account), - KeyedAccount::new(&vote_address, false, &bad_vote_account), - KeyedAccount::new(&clock_address, false, &clock_account), - KeyedAccount::new(&stake_history_address, false, &stake_history_account), - KeyedAccount::new(&config_address, false, &config_account), + let keyed_accounts = [ + (true, false, &stake_address, &stake_account), + (false, false, &vote_address, &bad_vote_account), + (false, false, &clock_address, &clock_account), + (false, false, &stake_history_address, &stake_history_account), + (false, false, &config_address, &config_account), ]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::DelegateStake).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Err(InstructionError::InvalidAccountData), ); @@ -729,17 +811,17 @@ mod tests { let stake_history_account = RefCell::new(account::create_account_shared_data_for_test( &StakeHistory::default(), )); - let keyed_accounts = vec![ - KeyedAccount::new(&stake_address, false, &stake_account), - KeyedAccount::new(&vote_address, false, &vote_account), - KeyedAccount::new(&rewards_address, false, &rewards_account), - KeyedAccount::new(&stake_history_address, false, &stake_history_account), + let keyed_accounts = [ + (false, false, &stake_address, &stake_account), + (false, false, &vote_address, &vote_account), + (false, false, &rewards_address, &rewards_account), + (false, false, &stake_history_address, &stake_history_account), ]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::Withdraw(42)).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Err(InstructionError::InvalidArgument), ); @@ -747,12 +829,12 @@ mod tests { // Tests correct number of accounts are provided in withdraw let stake_address = Pubkey::default(); let stake_account = create_default_stake_account(); - let keyed_accounts = vec![KeyedAccount::new(&stake_address, false, &stake_account)]; + let keyed_accounts = [(false, false, &stake_address, &stake_account)]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::Withdraw(42)).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -764,25 +846,25 @@ mod tests { let rewards_account = RefCell::new(account::create_account_shared_data_for_test( &sysvar::rewards::Rewards::new(0.0), )); - let keyed_accounts = vec![ - KeyedAccount::new(&stake_address, false, &stake_account), - KeyedAccount::new(&rewards_address, false, &rewards_account), + let keyed_accounts = [ + (false, false, &stake_address, &stake_account), + (false, false, &rewards_address, &rewards_account), ]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::Deactivate).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Err(InstructionError::InvalidArgument), ); // Tests correct number of accounts are provided in deactivate assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::Deactivate).unwrap(), - &mut MockInvokeContext::new(vec![]) + &[], ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -799,7 +881,7 @@ mod tests { initialize_checked(&stake_address, &Authorized { staker, withdrawer }); instruction.accounts[3] = AccountMeta::new_readonly(withdrawer, false); assert_eq!( - process_instruction(&instruction), + process_instruction_as_one_arg(&instruction), Err(InstructionError::MissingRequiredSignature), ); @@ -816,18 +898,17 @@ mod tests { let staker_account = create_default_account(); let withdrawer_account = create_default_account(); - let keyed_accounts = vec![ - KeyedAccount::new(&stake_address, false, &stake_account), - KeyedAccount::new(&rent_address, false, &rent_account), - KeyedAccount::new(&staker, false, &staker_account), - KeyedAccount::new(&withdrawer, true, &withdrawer_account), + let keyed_accounts: [(bool, bool, &Pubkey, &RefCell); 4] = [ + (false, false, &stake_address, &stake_account), + (false, false, &rent_address, &rent_account), + (false, false, &staker, &staker_account), + (true, false, &withdrawer, &withdrawer_account), ]; - assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::InitializeChecked).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Ok(()), ); @@ -843,7 +924,7 @@ mod tests { ); instruction.accounts[3] = AccountMeta::new_readonly(staker, false); assert_eq!( - process_instruction(&instruction), + process_instruction_as_one_arg(&instruction), Err(InstructionError::MissingRequiredSignature), ); @@ -856,7 +937,7 @@ mod tests { ); instruction.accounts[3] = AccountMeta::new_readonly(withdrawer, false); assert_eq!( - process_instruction(&instruction), + process_instruction_as_one_arg(&instruction), Err(InstructionError::MissingRequiredSignature), ); @@ -875,37 +956,30 @@ mod tests { let authorized_account = create_default_account(); let new_authorized_account = create_default_account(); - let keyed_accounts = vec![ - KeyedAccount::new(&stake_address, false, &stake_account), - KeyedAccount::new(&clock_address, false, &clock_account), - KeyedAccount::new(&authorized_address, true, &authorized_account), - KeyedAccount::new(&staker, true, &new_authorized_account), + let mut keyed_accounts = [ + (false, false, &stake_address, &stake_account), + (false, false, &clock_address, &clock_account), + (true, false, &authorized_address, &authorized_account), + (true, false, &staker, &new_authorized_account), ]; - assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::AuthorizeChecked(StakeAuthorize::Staker)).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Ok(()), ); - let keyed_accounts = vec![ - KeyedAccount::new(&stake_address, false, &stake_account), - KeyedAccount::new(&clock_address, false, &clock_account), - KeyedAccount::new(&authorized_address, true, &authorized_account), - KeyedAccount::new(&withdrawer, true, &new_authorized_account), - ]; - + keyed_accounts[3] = (true, false, &withdrawer, &new_authorized_account); assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::AuthorizeChecked( StakeAuthorize::Withdrawer )) .unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Ok(()), ); @@ -926,7 +1000,7 @@ mod tests { ); instruction.accounts[3] = AccountMeta::new_readonly(staker, false); assert_eq!( - process_instruction(&instruction), + process_instruction_as_one_arg(&instruction), Err(InstructionError::MissingRequiredSignature), ); @@ -941,7 +1015,7 @@ mod tests { ); instruction.accounts[3] = AccountMeta::new_readonly(staker, false); assert_eq!( - process_instruction(&instruction), + process_instruction_as_one_arg(&instruction), Err(InstructionError::MissingRequiredSignature), ); @@ -953,15 +1027,14 @@ mod tests { &id(), ) .unwrap(); - let keyed_accounts = vec![ - KeyedAccount::new(&address_with_seed, false, &stake_account), - KeyedAccount::new(&authorized_owner, true, &authorized_account), - KeyedAccount::new(&clock_address, false, &clock_account), - KeyedAccount::new(&staker, true, &new_authorized_account), + let mut keyed_accounts = [ + (false, false, &address_with_seed, &stake_account), + (true, false, &authorized_owner, &authorized_account), + (false, false, &clock_address, &clock_account), + (true, false, &staker, &new_authorized_account), ]; - assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::AuthorizeCheckedWithSeed( AuthorizeCheckedWithSeedArgs { @@ -971,20 +1044,14 @@ mod tests { } )) .unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Ok(()), ); - let keyed_accounts = vec![ - KeyedAccount::new(&address_with_seed, false, &stake_account), - KeyedAccount::new(&authorized_owner, true, &authorized_account), - KeyedAccount::new(&clock_address, false, &clock_account), - KeyedAccount::new(&withdrawer, true, &new_authorized_account), - ]; - + keyed_accounts[3] = (true, false, &withdrawer, &new_authorized_account); assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&StakeInstruction::AuthorizeCheckedWithSeed( AuthorizeCheckedWithSeedArgs { @@ -994,7 +1061,7 @@ mod tests { } )) .unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Ok(()), ); @@ -1012,7 +1079,7 @@ mod tests { ); instruction.accounts[2] = AccountMeta::new_readonly(custodian, false); assert_eq!( - process_instruction(&instruction), + process_instruction_as_one_arg(&instruction), Err(InstructionError::MissingRequiredSignature), ); @@ -1026,13 +1093,18 @@ mod tests { .unwrap(); let custodian_account = create_default_account(); - let keyed_accounts = vec![ - KeyedAccount::new(&stake_address, false, &stake_account), - KeyedAccount::new(&withdrawer, true, &withdrawer_account), - KeyedAccount::new(&custodian, true, &custodian_account), + let processor_account = RefCell::new(AccountSharedData::from(Account { + owner: solana_sdk::native_loader::id(), + ..Account::default() + })); + let keyed_accounts = [ + (false, false, &id(), &processor_account), + (false, false, &stake_address, &stake_account), + (true, false, &withdrawer, &withdrawer_account), + (true, false, &custodian, &custodian_account), ]; - - let mut invoke_context = MockInvokeContext::new(keyed_accounts); + let mut invoke_context = + MockInvokeContext::new(create_keyed_accounts_unified(&keyed_accounts)); let clock = Clock::default(); let mut data = vec![]; bincode::serialize_into(&mut data, &clock).unwrap(); @@ -1043,12 +1115,13 @@ mod tests { assert_eq!( super::process_instruction( &Pubkey::default(), + 1, &serialize(&StakeInstruction::SetLockupChecked(LockupCheckedArgs { unix_timestamp: None, epoch: Some(1), })) .unwrap(), - &mut invoke_context + &mut invoke_context, ), Ok(()), ); diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index a653c60d48..87a6a1c500 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -309,6 +309,7 @@ fn verify_rent_exemption( pub fn process_instruction( _program_id: &Pubkey, + first_instruction_account: usize, data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { @@ -317,22 +318,26 @@ pub fn process_instruction( trace!("process_instruction: {:?}", data); trace!("keyed_accounts: {:?}", keyed_accounts); - let signers: HashSet = get_signers(keyed_accounts); - - let me = &mut keyed_account_at_index(keyed_accounts, 0)?; - + let me = &mut keyed_account_at_index(keyed_accounts, first_instruction_account)?; if me.owner()? != id() { return Err(InstructionError::InvalidAccountOwner); } + let signers: HashSet = get_signers(&keyed_accounts[1..]); match limited_deserialize(data)? { VoteInstruction::InitializeAccount(vote_init) => { - verify_rent_exemption(me, keyed_account_at_index(keyed_accounts, 1)?)?; + verify_rent_exemption( + me, + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?, + )?; vote_state::initialize_account( me, &vote_init, &signers, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 2, + )?)?, invoke_context.is_feature_active(&feature_set::check_init_vote_data::id()), ) } @@ -341,11 +346,14 @@ pub fn process_instruction( &voter_pubkey, vote_authorize, &signers, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 1, + )?)?, ), VoteInstruction::UpdateValidatorIdentity => vote_state::update_validator_identity( me, - keyed_account_at_index(keyed_accounts, 1)?.unsigned_key(), + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?.unsigned_key(), &signers, ), VoteInstruction::UpdateCommission(commission) => { @@ -355,28 +363,38 @@ pub fn process_instruction( inc_new_counter_info!("vote-native", 1); vote_state::process_vote( me, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 1, + )?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 2, + )?)?, &vote, &signers, ) } VoteInstruction::Withdraw(lamports) => { - let to = keyed_account_at_index(keyed_accounts, 1)?; + let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; vote_state::withdraw(me, lamports, to, &signers) } VoteInstruction::AuthorizeChecked(vote_authorize) => { if invoke_context.is_feature_active(&feature_set::vote_stake_checked_instructions::id()) { - let voter_pubkey = &keyed_account_at_index(keyed_accounts, 3)? - .signer_key() - .ok_or(InstructionError::MissingRequiredSignature)?; + let voter_pubkey = + &keyed_account_at_index(keyed_accounts, first_instruction_account + 3)? + .signer_key() + .ok_or(InstructionError::MissingRequiredSignature)?; vote_state::authorize( me, voter_pubkey, vote_authorize, &signers, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 1, + )?)?, ) } else { Err(InstructionError::InvalidInstructionData) @@ -391,6 +409,7 @@ mod tests { use bincode::serialize; use solana_sdk::{ account::{self, Account, AccountSharedData}, + keyed_account::create_keyed_accounts_unified, process_instruction::MockInvokeContext, rent::Rent, }; @@ -401,21 +420,24 @@ mod tests { RefCell::new(AccountSharedData::default()) } - // these are for 100% coverage in this file - #[test] - fn test_vote_process_instruction_decode_bail() { - assert_eq!( - super::process_instruction( - &Pubkey::default(), - &[], - &mut MockInvokeContext::new(vec![]) - ), - Err(InstructionError::NotEnoughAccountKeys), - ); + fn process_instruction( + owner: &Pubkey, + instruction_data: &[u8], + keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + ) -> Result<(), InstructionError> { + let processor_account = AccountSharedData::new_ref(0, 0, &solana_sdk::native_loader::id()); + let mut keyed_accounts = keyed_accounts.to_vec(); + keyed_accounts.insert(0, (false, false, owner, &processor_account)); + super::process_instruction( + owner, + 1, + instruction_data, + &mut MockInvokeContext::new(create_keyed_accounts_unified(&keyed_accounts)), + ) } #[allow(clippy::same_item_push)] - fn process_instruction(instruction: &Instruction) -> Result<(), InstructionError> { + fn process_instruction_as_one_arg(instruction: &Instruction) -> Result<(), InstructionError> { let mut accounts: Vec<_> = instruction .accounts .iter() @@ -448,13 +470,9 @@ mod tests { .accounts .iter() .zip(accounts.iter()) - .map(|(meta, account)| KeyedAccount::new(&meta.pubkey, meta.is_signer, account)) + .map(|(meta, account)| (meta.is_signer, false, &meta.pubkey, account)) .collect(); - super::process_instruction( - &Pubkey::default(), - &instruction.data, - &mut MockInvokeContext::new(keyed_accounts), - ) + process_instruction(&Pubkey::default(), &instruction.data, &keyed_accounts) } } @@ -462,10 +480,19 @@ mod tests { Pubkey::from_str("BadVote111111111111111111111111111111111111").unwrap() } + // these are for 100% coverage in this file + #[test] + fn test_vote_process_instruction_decode_bail() { + assert_eq!( + process_instruction(&Pubkey::default(), &[], &[]), + Err(InstructionError::NotEnoughAccountKeys), + ); + } + #[test] fn test_spoofed_vote() { assert_eq!( - process_instruction(&vote( + process_instruction_as_one_arg(&vote( &invalid_vote_state_pubkey(), &Pubkey::default(), Vote::default(), @@ -484,11 +511,11 @@ mod tests { 100, ); assert_eq!( - process_instruction(&instructions[1]), + process_instruction_as_one_arg(&instructions[1]), Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction(&vote( + process_instruction_as_one_arg(&vote( &Pubkey::default(), &Pubkey::default(), Vote::default(), @@ -496,7 +523,7 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction(&vote_switch( + process_instruction_as_one_arg(&vote_switch( &Pubkey::default(), &Pubkey::default(), Vote::default(), @@ -505,7 +532,7 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction(&authorize( + process_instruction_as_one_arg(&authorize( &Pubkey::default(), &Pubkey::default(), &Pubkey::default(), @@ -514,7 +541,7 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction(&update_validator_identity( + process_instruction_as_one_arg(&update_validator_identity( &Pubkey::default(), &Pubkey::default(), &Pubkey::default(), @@ -522,7 +549,7 @@ mod tests { Err(InstructionError::InvalidAccountData), ); assert_eq!( - process_instruction(&update_commission( + process_instruction_as_one_arg(&update_commission( &Pubkey::default(), &Pubkey::default(), 0, @@ -531,7 +558,7 @@ mod tests { ); assert_eq!( - process_instruction(&withdraw( + process_instruction_as_one_arg(&withdraw( &Pubkey::default(), &Pubkey::default(), 0, @@ -556,7 +583,7 @@ mod tests { ); instruction.accounts = instruction.accounts[0..2].to_vec(); assert_eq!( - process_instruction(&instruction), + process_instruction_as_one_arg(&instruction), Err(InstructionError::NotEnoughAccountKeys), ); @@ -568,7 +595,7 @@ mod tests { ); instruction.accounts = instruction.accounts[0..2].to_vec(); assert_eq!( - process_instruction(&instruction), + process_instruction_as_one_arg(&instruction), Err(InstructionError::NotEnoughAccountKeys), ); @@ -581,7 +608,7 @@ mod tests { ); instruction.accounts[3] = AccountMeta::new_readonly(new_authorized_pubkey, false); assert_eq!( - process_instruction(&instruction), + process_instruction_as_one_arg(&instruction), Err(InstructionError::MissingRequiredSignature), ); @@ -593,7 +620,7 @@ mod tests { ); instruction.accounts[3] = AccountMeta::new_readonly(new_authorized_pubkey, false); assert_eq!( - process_instruction(&instruction), + process_instruction_as_one_arg(&instruction), Err(InstructionError::MissingRequiredSignature), ); @@ -606,35 +633,29 @@ mod tests { let default_authorized_pubkey = Pubkey::default(); let authorized_account = create_default_account(); let new_authorized_account = create_default_account(); - let keyed_accounts = vec![ - KeyedAccount::new(&vote_pubkey, false, &vote_account), - KeyedAccount::new(&clock_address, false, &clock_account), - KeyedAccount::new(&default_authorized_pubkey, true, &authorized_account), - KeyedAccount::new(&new_authorized_pubkey, true, &new_authorized_account), + let keyed_accounts: [(bool, bool, &Pubkey, &RefCell); 4] = [ + (false, false, &vote_pubkey, &vote_account), + (false, false, &clock_address, &clock_account), + (true, false, &default_authorized_pubkey, &authorized_account), + (true, false, &new_authorized_pubkey, &new_authorized_account), ]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&VoteInstruction::AuthorizeChecked(VoteAuthorize::Voter)).unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Ok(()) ); - let keyed_accounts = vec![ - KeyedAccount::new(&vote_pubkey, false, &vote_account), - KeyedAccount::new(&clock_address, false, &clock_account), - KeyedAccount::new(&default_authorized_pubkey, true, &authorized_account), - KeyedAccount::new(&new_authorized_pubkey, true, &new_authorized_account), - ]; assert_eq!( - super::process_instruction( + process_instruction( &Pubkey::default(), &serialize(&VoteInstruction::AuthorizeChecked( VoteAuthorize::Withdrawer )) .unwrap(), - &mut MockInvokeContext::new(keyed_accounts) + &keyed_accounts, ), Ok(()) ); diff --git a/runtime/benches/bank.rs b/runtime/benches/bank.rs index d47ea8f6ec..5c1c9effe2 100644 --- a/runtime/benches/bank.rs +++ b/runtime/benches/bank.rs @@ -33,6 +33,7 @@ const NOOP_PROGRAM_ID: [u8; 32] = [ #[allow(clippy::unnecessary_wraps)] fn process_instruction( _program_id: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 075ca21f7e..50a6a6f4fb 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -6693,6 +6693,7 @@ pub(crate) mod tests { fn mock_process_instruction( _program_id: &Pubkey, + first_instruction_account: usize, data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> result::Result<(), InstructionError> { @@ -6700,11 +6701,11 @@ pub(crate) mod tests { if let Ok(instruction) = bincode::deserialize(data) { match instruction { MockInstruction::Deduction => { - keyed_accounts[1] + keyed_accounts[first_instruction_account + 1] .account .borrow_mut() .checked_add_lamports(1)?; - keyed_accounts[2] + keyed_accounts[first_instruction_account + 2] .account .borrow_mut() .checked_sub_lamports(1)?; @@ -10340,6 +10341,7 @@ pub(crate) mod tests { } fn mock_vote_processor( program_id: &Pubkey, + _first_instruction_account: usize, _instruction_data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -10397,6 +10399,7 @@ pub(crate) mod tests { fn mock_vote_processor( _pubkey: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -10447,6 +10450,7 @@ pub(crate) mod tests { fn mock_ix_processor( _pubkey: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -11288,21 +11292,24 @@ pub(crate) mod tests { fn mock_process_instruction( _program_id: &Pubkey, + first_instruction_account: usize, data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> result::Result<(), InstructionError> { let keyed_accounts = invoke_context.get_keyed_accounts()?; let lamports = data[0] as u64; { - let mut to_account = keyed_accounts[1].try_account_ref_mut()?; - let mut dup_account = keyed_accounts[2].try_account_ref_mut()?; + let mut to_account = + keyed_accounts[first_instruction_account + 1].try_account_ref_mut()?; + let mut dup_account = + keyed_accounts[first_instruction_account + 2].try_account_ref_mut()?; dup_account.checked_sub_lamports(lamports)?; to_account.checked_add_lamports(lamports)?; } - keyed_accounts[0] + keyed_accounts[first_instruction_account] .try_account_ref_mut()? .checked_sub_lamports(lamports)?; - keyed_accounts[1] + keyed_accounts[first_instruction_account + 1] .try_account_ref_mut()? .checked_add_lamports(lamports)?; Ok(()) @@ -11346,6 +11353,7 @@ pub(crate) mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_process_instruction( _program_id: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> result::Result<(), InstructionError> { @@ -11532,6 +11540,7 @@ pub(crate) mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_ok_vote_processor( _pubkey: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -11782,12 +11791,18 @@ pub(crate) mod tests { fn test_same_program_id_uses_unqiue_executable_accounts() { fn nested_processor( _program_id: &Pubkey, + first_instruction_account: usize, _data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> result::Result<(), InstructionError> { let keyed_accounts = invoke_context.get_keyed_accounts()?; - assert_eq!(42, keyed_accounts[0].lamports().unwrap()); - let mut account = keyed_accounts[0].try_account_ref_mut()?; + assert_eq!( + 42, + keyed_accounts[first_instruction_account] + .lamports() + .unwrap() + ); + let mut account = keyed_accounts[first_instruction_account].try_account_ref_mut()?; account.checked_add_lamports(1)?; Ok(()) } @@ -12148,6 +12163,7 @@ pub(crate) mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_ix_processor( _pubkey: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -12197,6 +12213,7 @@ pub(crate) mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_ix_processor( _pubkey: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -12583,8 +12600,8 @@ pub(crate) mod tests { impl Executor for TestExecutor { fn execute( &self, - _loader_id: &Pubkey, _program_id: &Pubkey, + _first_instruction_account: usize, _instruction_data: &[u8], _invoke_context: &mut dyn InvokeContext, _use_jit: bool, @@ -13088,6 +13105,7 @@ pub(crate) mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_process_instruction( _program_id: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), solana_sdk::instruction::InstructionError> { @@ -14585,12 +14603,13 @@ pub(crate) mod tests { fn mock_ix_processor( _pubkey: &Pubkey, + first_instruction_account: usize, _data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { use solana_sdk::account::WritableAccount; let keyed_accounts = invoke_context.get_keyed_accounts()?; - let mut data = keyed_accounts[1].try_account_ref_mut()?; + let mut data = keyed_accounts[first_instruction_account + 1].try_account_ref_mut()?; data.data_as_mut_slice()[0] = 5; Ok(()) } @@ -14794,6 +14813,7 @@ pub(crate) mod tests { fn mock_ix_processor( _pubkey: &Pubkey, + _first_instruction_account: usize, _data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { diff --git a/runtime/src/builtins.rs b/runtime/src/builtins.rs index eeb2093927..c2d9f2e25d 100644 --- a/runtime/src/builtins.rs +++ b/runtime/src/builtins.rs @@ -14,13 +14,21 @@ use solana_frozen_abi::abi_example::AbiExample; fn process_instruction_with_program_logging( process_instruction: ProcessInstructionWithContext, program_id: &Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { + debug_assert_eq!(first_instruction_account, 1); + let logger = invoke_context.get_logger(); stable_log::program_invoke(&logger, program_id, invoke_context.invoke_depth()); - let result = process_instruction(program_id, instruction_data, invoke_context); + let result = process_instruction( + program_id, + first_instruction_account, + instruction_data, + invoke_context, + ); match &result { Ok(()) => stable_log::program_success(&logger, program_id), @@ -31,10 +39,14 @@ fn process_instruction_with_program_logging( macro_rules! with_program_logging { ($process_instruction:expr) => { - |program_id: &Pubkey, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext| { + |program_id: &Pubkey, + first_instruction_account: usize, + instruction_data: &[u8], + invoke_context: &mut dyn InvokeContext| { process_instruction_with_program_logging( $process_instruction, program_id, + first_instruction_account, instruction_data, invoke_context, ) @@ -82,7 +94,7 @@ impl AbiExample for Builtin { Self { name: String::default(), id: Pubkey::default(), - process_instruction_with_context: |_, _, _| Ok(()), + process_instruction_with_context: |_, _, _, _| Ok(()), } } } @@ -130,6 +142,7 @@ fn genesis_builtins() -> Vec { /// place holder for secp256k1, remove when the precompile program is deactivated via feature activation fn dummy_process_instruction( _program_id: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index b5a5a38e18..538b52b100 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -11,7 +11,8 @@ use solana_sdk::{ compute_budget::ComputeBudget, feature_set::{ demote_program_write_locks, do_support_realloc, neon_evm_compute_budget, - prevent_calling_precompiles_as_programs, tx_wide_compute_cap, FeatureSet, + prevent_calling_precompiles_as_programs, remove_native_loader, tx_wide_compute_cap, + FeatureSet, }, fee_calculator::FeeCalculator, hash::Hash, @@ -329,12 +330,14 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { .ok_or(InstructionError::CallDepth) } fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> { - let stack_frame = &mut self - .invoke_stack - .last_mut() - .ok_or(InstructionError::CallDepth)?; - stack_frame.keyed_accounts_range.start = - stack_frame.keyed_accounts_range.start.saturating_add(1); + if !self.is_feature_active(&remove_native_loader::id()) { + let stack_frame = &mut self + .invoke_stack + .last_mut() + .ok_or(InstructionError::CallDepth)?; + stack_frame.keyed_accounts_range.start = + stack_frame.keyed_accounts_range.start.saturating_add(1); + } Ok(()) } fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> { @@ -596,14 +599,18 @@ mod tests { fn mock_process_instruction( program_id: &Pubkey, + first_instruction_account: usize, data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { let keyed_accounts = invoke_context.get_keyed_accounts()?; - assert_eq!(*program_id, keyed_accounts[0].owner()?); + assert_eq!( + *program_id, + keyed_accounts[first_instruction_account].owner()? + ); assert_ne!( - keyed_accounts[1].owner()?, - *keyed_accounts[0].unsigned_key() + keyed_accounts[first_instruction_account + 1].owner()?, + *keyed_accounts[first_instruction_account].unsigned_key() ); if let Ok(instruction) = bincode::deserialize(data) { @@ -611,13 +618,19 @@ mod tests { MockInstruction::NoopSuccess => (), MockInstruction::NoopFail => return Err(InstructionError::GenericError), MockInstruction::ModifyOwned => { - keyed_accounts[0].try_account_ref_mut()?.data_as_mut_slice()[0] = 1 + keyed_accounts[first_instruction_account] + .try_account_ref_mut()? + .data_as_mut_slice()[0] = 1 } MockInstruction::ModifyNotOwned => { - keyed_accounts[1].try_account_ref_mut()?.data_as_mut_slice()[0] = 1 + keyed_accounts[first_instruction_account + 1] + .try_account_ref_mut()? + .data_as_mut_slice()[0] = 1 } MockInstruction::ModifyReadonly => { - keyed_accounts[2].try_account_ref_mut()?.data_as_mut_slice()[0] = 1 + keyed_accounts[first_instruction_account + 2] + .try_account_ref_mut()? + .data_as_mut_slice()[0] = 1 } } } else { @@ -809,6 +822,7 @@ mod tests { fn mock_system_process_instruction( _program_id: &Pubkey, + first_instruction_account: usize, data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { @@ -817,11 +831,11 @@ mod tests { match instruction { MockSystemInstruction::Correct => Ok(()), MockSystemInstruction::AttemptCredit { lamports } => { - keyed_accounts[0] + keyed_accounts[first_instruction_account] .account .borrow_mut() .checked_sub_lamports(lamports)?; - keyed_accounts[1] + keyed_accounts[first_instruction_account + 1] .account .borrow_mut() .checked_add_lamports(lamports)?; @@ -829,7 +843,10 @@ mod tests { } // Change data in a read-only account MockSystemInstruction::AttemptDataChange { data } => { - keyed_accounts[1].account.borrow_mut().set_data(vec![data]); + keyed_accounts[first_instruction_account + 1] + .account + .borrow_mut() + .set_data(vec![data]); Ok(()) } } @@ -979,6 +996,7 @@ mod tests { fn mock_system_process_instruction( _program_id: &Pubkey, + first_instruction_account: usize, data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { @@ -986,8 +1004,10 @@ mod tests { if let Ok(instruction) = bincode::deserialize(data) { match instruction { MockSystemInstruction::BorrowFail => { - let from_account = keyed_accounts[0].try_account_ref_mut()?; - let dup_account = keyed_accounts[2].try_account_ref_mut()?; + let from_account = + keyed_accounts[first_instruction_account].try_account_ref_mut()?; + let dup_account = + keyed_accounts[first_instruction_account + 2].try_account_ref_mut()?; if from_account.lamports() != dup_account.lamports() { return Err(InstructionError::InvalidArgument); } @@ -995,11 +1015,13 @@ mod tests { } MockSystemInstruction::MultiBorrowMut => { let from_lamports = { - let from_account = keyed_accounts[0].try_account_ref_mut()?; + let from_account = + keyed_accounts[first_instruction_account].try_account_ref_mut()?; from_account.lamports() }; let dup_lamports = { - let dup_account = keyed_accounts[2].try_account_ref_mut()?; + let dup_account = keyed_accounts[first_instruction_account + 2] + .try_account_ref_mut()?; dup_account.lamports() }; if from_lamports != dup_lamports { @@ -1009,16 +1031,18 @@ mod tests { } MockSystemInstruction::DoWork { lamports, data } => { { - let mut to_account = keyed_accounts[1].try_account_ref_mut()?; - let mut dup_account = keyed_accounts[2].try_account_ref_mut()?; + let mut to_account = keyed_accounts[first_instruction_account + 1] + .try_account_ref_mut()?; + let mut dup_account = keyed_accounts[first_instruction_account + 2] + .try_account_ref_mut()?; dup_account.checked_sub_lamports(lamports)?; to_account.checked_add_lamports(lamports)?; dup_account.set_data(vec![data]); } - keyed_accounts[0] + keyed_accounts[first_instruction_account] .try_account_ref_mut()? .checked_sub_lamports(lamports)?; - keyed_accounts[1] + keyed_accounts[first_instruction_account + 1] .try_account_ref_mut()? .checked_add_lamports(lamports)?; Ok(()) @@ -1500,6 +1524,7 @@ mod tests { let mock_program_id = Pubkey::new_unique(); fn mock_process_instruction( _program_id: &Pubkey, + _first_instruction_account: usize, _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { diff --git a/runtime/src/system_instruction_processor.rs b/runtime/src/system_instruction_processor.rs index 695ee691c9..fc0a2a2740 100644 --- a/runtime/src/system_instruction_processor.rs +++ b/runtime/src/system_instruction_processor.rs @@ -264,6 +264,7 @@ fn transfer_with_seed( pub fn process_instruction( _owner: &Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { @@ -273,16 +274,16 @@ pub fn process_instruction( trace!("process_instruction: {:?}", instruction); trace!("keyed_accounts: {:?}", keyed_accounts); - let signers = get_signers(keyed_accounts); - + let _ = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let signers = get_signers(&keyed_accounts[first_instruction_account..]); match instruction { SystemInstruction::CreateAccount { lamports, space, owner, } => { - let from = keyed_account_at_index(keyed_accounts, 0)?; - let to = keyed_account_at_index(keyed_accounts, 1)?; + let from = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; let to_address = Address::create(to.unsigned_key(), None, invoke_context)?; create_account( from, @@ -302,8 +303,8 @@ pub fn process_instruction( space, owner, } => { - let from = keyed_account_at_index(keyed_accounts, 0)?; - let to = keyed_account_at_index(keyed_accounts, 1)?; + let from = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; let to_address = Address::create( to.unsigned_key(), Some((&base, &seed, &owner)), @@ -321,14 +322,14 @@ pub fn process_instruction( ) } SystemInstruction::Assign { owner } => { - let keyed_account = keyed_account_at_index(keyed_accounts, 0)?; + let keyed_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let mut account = keyed_account.try_account_ref_mut()?; let address = Address::create(keyed_account.unsigned_key(), None, invoke_context)?; assign(&mut account, &address, &owner, &signers, invoke_context) } SystemInstruction::Transfer { lamports } => { - let from = keyed_account_at_index(keyed_accounts, 0)?; - let to = keyed_account_at_index(keyed_accounts, 1)?; + let from = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; transfer(from, to, lamports, invoke_context) } SystemInstruction::TransferWithSeed { @@ -336,9 +337,9 @@ pub fn process_instruction( from_seed, from_owner, } => { - let from = keyed_account_at_index(keyed_accounts, 0)?; - let base = keyed_account_at_index(keyed_accounts, 1)?; - let to = keyed_account_at_index(keyed_accounts, 2)?; + let from = keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let base = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; + let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?; transfer_with_seed( from, base, @@ -350,10 +351,10 @@ pub fn process_instruction( ) } SystemInstruction::AdvanceNonceAccount => { - let me = &mut keyed_account_at_index(keyed_accounts, 0)?; + let me = &mut keyed_account_at_index(keyed_accounts, first_instruction_account)?; #[allow(deprecated)] if from_keyed_account::( - keyed_account_at_index(keyed_accounts, 1)?, + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?, )? .is_empty() { @@ -366,25 +367,28 @@ pub fn process_instruction( me.advance_nonce_account(&signers, invoke_context) } SystemInstruction::WithdrawNonceAccount(lamports) => { - let me = &mut keyed_account_at_index(keyed_accounts, 0)?; - let to = &mut keyed_account_at_index(keyed_accounts, 1)?; + let me = &mut keyed_account_at_index(keyed_accounts, first_instruction_account)?; + let to = &mut keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; #[allow(deprecated)] let _ = from_keyed_account::( - keyed_account_at_index(keyed_accounts, 2)?, + keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?, )?; me.withdraw_nonce_account( lamports, to, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 3)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 3, + )?)?, &signers, invoke_context, ) } SystemInstruction::InitializeNonceAccount(authorized) => { - let me = &mut keyed_account_at_index(keyed_accounts, 0)?; + let me = &mut keyed_account_at_index(keyed_accounts, first_instruction_account)?; #[allow(deprecated)] if from_keyed_account::( - keyed_account_at_index(keyed_accounts, 1)?, + keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?, )? .is_empty() { @@ -396,16 +400,19 @@ pub fn process_instruction( } me.initialize_nonce_account( &authorized, - &from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + first_instruction_account + 2, + )?)?, invoke_context, ) } SystemInstruction::AuthorizeNonceAccount(nonce_authority) => { - let me = &mut keyed_account_at_index(keyed_accounts, 0)?; + let me = &mut keyed_account_at_index(keyed_accounts, first_instruction_account)?; me.authorize_nonce_account(&nonce_authority, &signers, invoke_context) } SystemInstruction::Allocate { space } => { - let keyed_account = keyed_account_at_index(keyed_accounts, 0)?; + let keyed_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let mut account = keyed_account.try_account_ref_mut()?; let address = Address::create(keyed_account.unsigned_key(), None, invoke_context)?; allocate(&mut account, &address, space, &signers, invoke_context) @@ -416,7 +423,7 @@ pub fn process_instruction( space, owner, } => { - let keyed_account = keyed_account_at_index(keyed_accounts, 0)?; + let keyed_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let mut account = keyed_account.try_account_ref_mut()?; let address = Address::create( keyed_account.unsigned_key(), @@ -433,7 +440,7 @@ pub fn process_instruction( ) } SystemInstruction::AssignWithSeed { base, seed, owner } => { - let keyed_account = keyed_account_at_index(keyed_accounts, 0)?; + let keyed_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let mut account = keyed_account.try_account_ref_mut()?; let address = Address::create( keyed_account.unsigned_key(), @@ -484,6 +491,7 @@ mod tests { genesis_config::create_genesis_config, hash::{hash, Hash}, instruction::{AccountMeta, Instruction, InstructionError}, + keyed_account::create_keyed_accounts_unified, message::Message, nonce, nonce_account, process_instruction::MockInvokeContext, @@ -506,13 +514,21 @@ mod tests { fn process_instruction( owner: &Pubkey, - keyed_accounts: Vec, instruction_data: &[u8], + keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], ) -> Result<(), InstructionError> { + let processor_account = RefCell::new(AccountSharedData::from(Account { + owner: solana_sdk::native_loader::id(), + ..Account::default() + })); + let mut keyed_accounts = keyed_accounts.to_vec(); + let processor_id = Pubkey::default(); + keyed_accounts.insert(0, (false, false, &processor_id, &processor_account)); super::process_instruction( owner, + 1, instruction_data, - &mut MockInvokeContext::new(keyed_accounts), + &mut MockInvokeContext::new(create_keyed_accounts_unified(&keyed_accounts)), ) } @@ -546,16 +562,16 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new(&from, true, &from_account), - KeyedAccount::new(&to, true, &to_account) - ], &bincode::serialize(&SystemInstruction::CreateAccount { lamports: 50, space: 2, owner: new_owner }) - .unwrap() + .unwrap(), + &[ + (true, false, &from, &from_account), + (true, false, &to, &to_account), + ], ), Ok(()) ); @@ -571,17 +587,12 @@ mod tests { let from = solana_sdk::pubkey::new_rand(); let seed = "shiny pepper"; let to = Pubkey::create_with_seed(&from, seed, &new_owner).unwrap(); - let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); let to_account = AccountSharedData::new_ref(0, 0, &Pubkey::default()); assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new(&from, true, &from_account), - KeyedAccount::new(&to, false, &to_account) - ], &bincode::serialize(&SystemInstruction::CreateAccountWithSeed { base: from, seed: seed.to_string(), @@ -589,7 +600,11 @@ mod tests { space: 2, owner: new_owner }) - .unwrap() + .unwrap(), + &[ + (true, false, &from, &from_account), + (false, false, &to, &to_account), + ], ), Ok(()) ); @@ -606,7 +621,6 @@ mod tests { let base = solana_sdk::pubkey::new_rand(); let seed = "shiny pepper"; let to = Pubkey::create_with_seed(&base, seed, &new_owner).unwrap(); - let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); let to_account = AccountSharedData::new_ref(0, 0, &Pubkey::default()); let base_account = AccountSharedData::new_ref(0, 0, &Pubkey::default()); @@ -614,11 +628,6 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new(&from, true, &from_account), - KeyedAccount::new(&to, false, &to_account), - KeyedAccount::new(&base, true, &base_account) - ], &bincode::serialize(&SystemInstruction::CreateAccountWithSeed { base, seed: seed.to_string(), @@ -626,7 +635,12 @@ mod tests { space: 2, owner: new_owner }) - .unwrap() + .unwrap(), + &[ + (true, false, &from, &from_account), + (false, false, &to, &to_account), + (true, false, &base, &base_account), + ], ), Ok(()) ); @@ -1036,7 +1050,6 @@ mod tests { #[test] fn test_assign() { let new_owner = Pubkey::new(&[9; 32]); - let pubkey = solana_sdk::pubkey::new_rand(); let mut account = AccountSharedData::new(100, 0, &system_program::id()); @@ -1050,6 +1063,7 @@ mod tests { ), Err(InstructionError::MissingRequiredSignature) ); + // no change, no signature needed assert_eq!( assign( @@ -1066,8 +1080,8 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![KeyedAccount::new(&pubkey, true, &account)], - &bincode::serialize(&SystemInstruction::Assign { owner: new_owner }).unwrap() + &bincode::serialize(&SystemInstruction::Assign { owner: new_owner }).unwrap(), + &[(true, false, &pubkey, &account)], ), Ok(()) ); @@ -1123,18 +1137,18 @@ mod tests { owner: solana_sdk::pubkey::new_rand(), }; let data = serialize(&instruction).unwrap(); - let result = process_instruction(&system_program::id(), vec![], &data); + let result = process_instruction(&system_program::id(), &data, &[]); assert_eq!(result, Err(InstructionError::NotEnoughAccountKeys)); + // Attempt to transfer with no destination let from = solana_sdk::pubkey::new_rand(); let from_account = AccountSharedData::new_ref(100, 0, &system_program::id()); - // Attempt to transfer with no destination let instruction = SystemInstruction::Transfer { lamports: 0 }; let data = serialize(&instruction).unwrap(); let result = process_instruction( &system_program::id(), - vec![KeyedAccount::new(&from, true, &from_account)], &data, + &[(true, false, &from, &from_account)], ); assert_eq!(result, Err(InstructionError::NotEnoughAccountKeys)); } @@ -1178,7 +1192,7 @@ mod tests { 0, &MockInvokeContext::new(vec![]), ) - .is_ok(),); + .is_ok()); assert_eq!(from_keyed_account.account.borrow().lamports(), 50); assert_eq!(to_keyed_account.account.borrow().lamports(), 51); @@ -1252,7 +1266,7 @@ mod tests { 0, &MockInvokeContext::new(vec![]), ) - .is_ok(),); + .is_ok()); assert_eq!(from_keyed_account.account.borrow().lamports(), 50); assert_eq!(to_keyed_account.account.borrow().lamports(), 51); } @@ -1487,9 +1501,9 @@ mod tests { .accounts .iter() .zip(accounts.iter()) - .map(|(meta, account)| KeyedAccount::new(&meta.pubkey, meta.is_signer, account)) + .map(|(meta, account)| (meta.is_signer, false, &meta.pubkey, account)) .collect(); - process_instruction(&Pubkey::default(), keyed_accounts, &instruction.data) + process_instruction(&Pubkey::default(), &instruction.data, &keyed_accounts) } } @@ -1509,8 +1523,8 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![], - &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap() + &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), + &[], ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -1521,12 +1535,8 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![KeyedAccount::new( - &Pubkey::default(), - true, - &create_default_account(), - )], &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), + &[(true, false, &Pubkey::default(), &create_default_account())], ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -1537,16 +1547,17 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), - KeyedAccount::new( + &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), + &[ + (true, false, &Pubkey::default(), &create_default_account()), + ( + false, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &create_default_account(), ), ], - &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), ), Err(InstructionError::InvalidArgument), ); @@ -1557,17 +1568,23 @@ mod tests { let nonce_acc = nonce_account::create_account(1_000_000); process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new(&Pubkey::default(), true, &nonce_acc), - KeyedAccount::new( + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + &[ + (true, false, &Pubkey::default(), &nonce_acc), + ( + false, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &create_default_recent_blockhashes_account(), ), - KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), + ( + false, + false, + &sysvar::rent::id(), + &create_default_rent_account(), + ), ], - &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ) .unwrap(); let blockhash = &hash(&serialize(&0).unwrap()); @@ -1584,14 +1601,22 @@ mod tests { let owner = Pubkey::default(); #[allow(deprecated)] let blockhash_id = sysvar::recent_blockhashes::id(); - let mut invoke_context = &mut MockInvokeContext::new(vec![ - KeyedAccount::new(&owner, true, &nonce_acc), - KeyedAccount::new(&blockhash_id, false, &new_recent_blockhashes_account), - ]); + let processor_account = RefCell::new(AccountSharedData::from(Account { + owner: solana_sdk::native_loader::id(), + ..Account::default() + })); + let keyed_accounts = [ + (false, false, &Pubkey::default(), &processor_account), + (true, false, &owner, &nonce_acc), + (false, false, &blockhash_id, &new_recent_blockhashes_account), + ]; + let mut invoke_context = + &mut MockInvokeContext::new(create_keyed_accounts_unified(&keyed_accounts)); invoke_context.blockhash = *blockhash; assert_eq!( super::process_instruction( &Pubkey::default(), + 1, &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), invoke_context, ), @@ -1617,8 +1642,8 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![], &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), + &[], ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -1629,12 +1654,8 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![KeyedAccount::new( - &Pubkey::default(), - true, - &create_default_account() - )], &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), + &[(true, false, &Pubkey::default(), &create_default_account())], ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -1645,17 +1666,18 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), - KeyedAccount::new(&Pubkey::default(), false, &create_default_account()), - KeyedAccount::new( + &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), + &[ + (true, false, &Pubkey::default(), &create_default_account()), + (false, false, &Pubkey::default(), &create_default_account()), + ( + false, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &create_default_account() ), ], - &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), ), Err(InstructionError::InvalidArgument), ); @@ -1666,22 +1688,24 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new( - &Pubkey::default(), + &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), + &[ + ( true, + false, + &Pubkey::default(), &nonce_account::create_account(1_000_000), ), - KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), - KeyedAccount::new( + (true, false, &Pubkey::default(), &create_default_account()), + ( + false, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &create_default_recent_blockhashes_account(), ), - KeyedAccount::new(&sysvar::rent::id(), false, &create_default_account()), + (false, false, &sysvar::rent::id(), &create_default_account()), ], - &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), ), Err(InstructionError::InvalidArgument), ); @@ -1692,22 +1716,29 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new( - &Pubkey::default(), + &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), + &[ + ( true, + false, + &Pubkey::default(), &nonce_account::create_account(1_000_000), ), - KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), - KeyedAccount::new( + (true, false, &Pubkey::default(), &create_default_account()), + ( + false, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &create_default_recent_blockhashes_account(), ), - KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), + ( + false, + false, + &sysvar::rent::id(), + &create_default_rent_account() + ), ], - &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), ), Ok(()), ); @@ -1718,8 +1749,8 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![], &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + &[], ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -1730,12 +1761,13 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![KeyedAccount::new( - &Pubkey::default(), + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + &[( true, + false, + &Pubkey::default(), &nonce_account::create_account(1_000_000), )], - &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -1746,20 +1778,22 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new( - &Pubkey::default(), + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + &[ + ( true, + false, + &Pubkey::default(), &nonce_account::create_account(1_000_000), ), - KeyedAccount::new( + ( + true, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &create_default_account() ), ], - &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ), Err(InstructionError::InvalidArgument), ); @@ -1770,21 +1804,23 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new( - &Pubkey::default(), + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + &[ + ( true, + false, + &Pubkey::default(), &nonce_account::create_account(1_000_000), ), - KeyedAccount::new( + ( + false, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &create_default_recent_blockhashes_account(), ), - KeyedAccount::new(&sysvar::rent::id(), false, &create_default_account()), + (false, false, &sysvar::rent::id(), &create_default_account()), ], - &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ), Err(InstructionError::InvalidArgument), ); @@ -1795,21 +1831,28 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new( - &Pubkey::default(), + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + &[ + ( true, + false, + &Pubkey::default(), &nonce_account::create_account(1_000_000), ), - KeyedAccount::new( + ( + false, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &create_default_recent_blockhashes_account(), ), - KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), + ( + false, + false, + &sysvar::rent::id(), + &create_default_rent_account() + ), ], - &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ), Ok(()), ); @@ -1820,24 +1863,30 @@ mod tests { let nonce_acc = nonce_account::create_account(1_000_000); process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new(&Pubkey::default(), true, &nonce_acc), - KeyedAccount::new( + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + &[ + (true, false, &Pubkey::default(), &nonce_acc), + ( + false, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &create_default_recent_blockhashes_account(), ), - KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), + ( + false, + false, + &sysvar::rent::id(), + &create_default_rent_account(), + ), ], - &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ) .unwrap(); assert_eq!( process_instruction( &Pubkey::default(), - vec![KeyedAccount::new(&Pubkey::default(), true, &nonce_acc,),], - &serialize(&SystemInstruction::AuthorizeNonceAccount(Pubkey::default(),)).unwrap(), + &serialize(&SystemInstruction::AuthorizeNonceAccount(Pubkey::default())).unwrap(), + &[(true, false, &Pubkey::default(), &nonce_acc)], ), Ok(()), ); @@ -1920,17 +1969,23 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new(&Pubkey::default(), true, &nonce_acc), - KeyedAccount::new( + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + &[ + (true, false, &Pubkey::default(), &nonce_acc), + ( + false, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &new_recent_blockhashes_account, ), - KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), + ( + false, + false, + &sysvar::rent::id(), + &create_default_rent_account() + ), ], - &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ), Err(NonceError::NoRecentBlockhashes.into()) ); @@ -1941,17 +1996,23 @@ mod tests { let nonce_acc = nonce_account::create_account(1_000_000); process_instruction( &Pubkey::default(), - vec![ - KeyedAccount::new(&Pubkey::default(), true, &nonce_acc), - KeyedAccount::new( + &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), + &[ + (true, false, &Pubkey::default(), &nonce_acc), + ( + false, + false, #[allow(deprecated)] &sysvar::recent_blockhashes::id(), - false, &create_default_recent_blockhashes_account(), ), - KeyedAccount::new(&sysvar::rent::id(), false, &create_default_rent_account()), + ( + false, + false, + &sysvar::rent::id(), + &create_default_rent_account(), + ), ], - &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ) .unwrap(); let blockhash = &hash(&serialize(&0).unwrap()); @@ -1964,14 +2025,22 @@ mod tests { let owner = Pubkey::default(); #[allow(deprecated)] let blockhash_id = sysvar::recent_blockhashes::id(); - let mut invoke_context = &mut MockInvokeContext::new(vec![ - KeyedAccount::new(&owner, true, &nonce_acc), - KeyedAccount::new(&blockhash_id, false, &new_recent_blockhashes_account), - ]); + let processor_account = RefCell::new(AccountSharedData::from(Account { + owner: solana_sdk::native_loader::id(), + ..Account::default() + })); + let keyed_accounts = [ + (false, false, &Pubkey::default(), &processor_account), + (true, false, &owner, &nonce_acc), + (false, false, &blockhash_id, &new_recent_blockhashes_account), + ]; + let mut invoke_context = + &mut MockInvokeContext::new(create_keyed_accounts_unified(&keyed_accounts)); invoke_context.blockhash = *blockhash; assert_eq!( super::process_instruction( &Pubkey::default(), + 1, &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), invoke_context, ), diff --git a/sdk/src/builtins.rs b/sdk/src/builtins.rs index 7cdddb76bf..702efcc3e8 100644 --- a/sdk/src/builtins.rs +++ b/sdk/src/builtins.rs @@ -80,6 +80,7 @@ macro_rules! declare_builtin_name { /// /// fn my_process_instruction( /// program_id: &Pubkey, +/// first_instruction_account: usize, /// keyed_accounts: &[KeyedAccount], /// instruction_data: &[u8], /// ) -> Result<(), InstructionError> { @@ -111,6 +112,7 @@ macro_rules! declare_builtin_name { /// /// fn my_process_instruction( /// program_id: &Pubkey, +/// first_instruction_account: usize, /// keyed_accounts: &[KeyedAccount], /// instruction_data: &[u8], /// ) -> Result<(), InstructionError> { diff --git a/sdk/src/entrypoint_native.rs b/sdk/src/entrypoint_native.rs index ecbff8a6ad..3fa2bbf805 100644 --- a/sdk/src/entrypoint_native.rs +++ b/sdk/src/entrypoint_native.rs @@ -95,6 +95,7 @@ macro_rules! declare_name { /// /// fn my_process_instruction( /// program_id: &Pubkey, +/// first_instruction_account: usize, /// instruction_data: &[u8], /// invoke_context: &mut dyn InvokeContext, /// ) -> Result<(), InstructionError> { @@ -128,6 +129,7 @@ macro_rules! declare_name { /// /// fn my_process_instruction( /// program_id: &Pubkey, +/// first_instruction_account: usize, /// instruction_data: &[u8], /// invoke_context: &mut dyn InvokeContext, /// ) -> Result<(), InstructionError> { @@ -154,10 +156,11 @@ macro_rules! declare_program( #[no_mangle] pub extern "C" fn $name( program_id: &$crate::pubkey::Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn $crate::process_instruction::InvokeContext, ) -> Result<(), $crate::instruction::InstructionError> { - $entrypoint(program_id, instruction_data, invoke_context) + $entrypoint(program_id, first_instruction_account, instruction_data, invoke_context) } ) ); diff --git a/sdk/src/keyed_account.rs b/sdk/src/keyed_account.rs index 6188ae8fb4..d53a227119 100644 --- a/sdk/src/keyed_account.rs +++ b/sdk/src/keyed_account.rs @@ -217,6 +217,8 @@ pub fn next_keyed_account<'a, 'b, I: Iterator>>( } /// Return the KeyedAccount at the specified index or a NotEnoughAccountKeys error +/// +/// Index zero starts at the chain of program accounts, followed by the instruction accounts. pub fn keyed_account_at_index<'a>( keyed_accounts: &'a [KeyedAccount], index: usize, diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index d407cb0e41..c5043081f7 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -4,6 +4,7 @@ use itertools::Itertools; use solana_sdk::{ account::AccountSharedData, compute_budget::ComputeBudget, + feature_set::remove_native_loader, fee_calculator::FeeCalculator, hash::Hash, instruction::{CompiledInstruction, Instruction, InstructionError}, @@ -27,7 +28,7 @@ pub type LoaderEntrypoint = unsafe extern "C" fn( ) -> Result<(), InstructionError>; pub type ProcessInstructionWithContext = - fn(&Pubkey, &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>; + fn(&Pubkey, usize, &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>; pub struct InvokeContextStackFrame<'a> { pub key: Pubkey, @@ -81,6 +82,10 @@ pub trait InvokeContext { /// Get the program ID of the currently executing program fn get_caller(&self) -> Result<&Pubkey, InstructionError>; /// Removes the first keyed account + #[deprecated( + since = "1.9.0", + note = "To be removed together with remove_native_loader" + )] fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError>; /// Get the list of keyed accounts fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError>; @@ -395,8 +400,8 @@ pub trait Executor: Debug + Send + Sync { /// Execute the program fn execute( &self, - loader_id: &Pubkey, program_id: &Pubkey, + first_instruction_account: usize, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, use_jit: bool, @@ -538,12 +543,14 @@ impl<'a> InvokeContext for MockInvokeContext<'a> { .ok_or(InstructionError::CallDepth) } fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> { - let stack_frame = &mut self - .invoke_stack - .last_mut() - .ok_or(InstructionError::CallDepth)?; - stack_frame.keyed_accounts_range.start = - stack_frame.keyed_accounts_range.start.saturating_add(1); + if !self.is_feature_active(&remove_native_loader::id()) { + let stack_frame = &mut self + .invoke_stack + .last_mut() + .ok_or(InstructionError::CallDepth)?; + stack_frame.keyed_accounts_range.start = + stack_frame.keyed_accounts_range.start.saturating_add(1); + } Ok(()) } fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> {