diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index b462a1088f..e0041c5815 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -25,7 +25,6 @@ use { hash::Hash, instruction::Instruction, instruction::InstructionError, - keyed_account::KeyedAccount, message::Message, native_token::sol_to_lamports, process_instruction::{ @@ -99,12 +98,13 @@ fn get_invoke_context<'a>() -> &'a mut dyn InvokeContext { pub fn builtin_process_instruction( process_instruction: solana_sdk::entrypoint::ProcessInstruction, program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], input: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { set_invoke_context(invoke_context); + let keyed_accounts = invoke_context.get_keyed_accounts()?; + // Copy all the accounts into a HashMap to ensure there are no duplicates let mut accounts: HashMap = keyed_accounts .iter() @@ -172,13 +172,11 @@ macro_rules! processor { ($process_instruction:expr) => { Some( |program_id: &Pubkey, - keyed_accounts: &[solana_sdk::keyed_account::KeyedAccount], input: &[u8], invoke_context: &mut dyn solana_sdk::process_instruction::InvokeContext| { $crate::builtin_process_instruction( $process_instruction, program_id, - keyed_accounts, input, invoke_context, ) diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index 926eff8e79..d4cf722fa3 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -76,7 +76,7 @@ fn bench_program_create_executable(bencher: &mut Bencher) { bencher.iter(|| { let _ = - Executable::::from_elf(&elf, None, Config::default()) + >::from_elf(&elf, None, Config::default()) .unwrap(); }); } @@ -91,11 +91,11 @@ fn bench_program_alu(bencher: &mut Bencher) { .unwrap(); inner_iter.write_u64::(0).unwrap(); let loader_id = bpf_loader::id(); - let mut invoke_context = MockInvokeContext::default(); + let mut invoke_context = MockInvokeContext::new(vec![]); let elf = load_elf("bench_alu").unwrap(); let mut executable = - Executable::::from_elf(&elf, None, Config::default()) + >::from_elf(&elf, None, Config::default()) .unwrap(); executable.set_syscall_registry(register_syscalls(&mut invoke_context).unwrap()); executable.jit_compile().unwrap(); @@ -105,7 +105,6 @@ fn bench_program_alu(bencher: &mut Bencher) { &loader_id, executable.as_ref(), &mut inner_iter, - &[], &mut invoke_context, ) .unwrap(); @@ -195,8 +194,6 @@ fn bench_program_execute_noop(bencher: &mut Bencher) { fn bench_instruction_count_tuner(_bencher: &mut Bencher) { const BUDGET: u64 = 200_000; let loader_id = bpf_loader::id(); - let mut invoke_context = MockInvokeContext::default(); - invoke_context.compute_meter.remaining = BUDGET; let accounts = [RefCell::new(AccountSharedData::new( 1, @@ -211,18 +208,22 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) { .collect(); let instruction_data = vec![0u8]; + let mut invoke_context = MockInvokeContext::new(keyed_accounts); + invoke_context.compute_meter.remaining = BUDGET; + // Serialize account data + let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); let mut serialized = serialize_parameters( &bpf_loader::id(), &solana_sdk::pubkey::new_rand(), - &keyed_accounts, + keyed_accounts, &instruction_data, ) .unwrap(); let elf = load_elf("tuner").unwrap(); let mut executable = - Executable::::from_elf(&elf, None, Config::default()) + >::from_elf(&elf, None, Config::default()) .unwrap(); executable.set_syscall_registry(register_syscalls(&mut invoke_context).unwrap()); let compute_meter = invoke_context.get_compute_meter(); @@ -231,7 +232,6 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) { &loader_id, executable.as_ref(), &mut serialized, - &[], &mut invoke_context, ) .unwrap(); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 62758ddbc6..406697ad19 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -179,7 +179,7 @@ fn upgrade_bpf_program( fn run_program( name: &str, program_id: &Pubkey, - parameter_accounts: &[KeyedAccount], + parameter_accounts: Vec, instruction_data: &[u8], ) -> Result { let path = create_bpf_path(name); @@ -188,14 +188,14 @@ fn run_program( let mut data = vec![]; file.read_to_end(&mut data).unwrap(); let loader_id = bpf_loader::id(); - let mut invoke_context = MockInvokeContext::default(); let parameter_bytes = serialize_parameters( &bpf_loader::id(), program_id, - parameter_accounts, + ¶meter_accounts, &instruction_data, ) .unwrap(); + let mut invoke_context = MockInvokeContext::new(parameter_accounts); let compute_meter = invoke_context.get_compute_meter(); let mut instruction_meter = ThisInstructionMeter { compute_meter }; @@ -213,20 +213,46 @@ fn run_program( let mut tracer = None; for i in 0..2 { let mut parameter_bytes = parameter_bytes.clone(); - let mut vm = create_vm( - &loader_id, - executable.as_ref(), - &mut parameter_bytes, - parameter_accounts, - &mut invoke_context, - ) - .unwrap(); - let result = if i == 0 { - vm.execute_program_interpreted(&mut instruction_meter) - } else { - vm.execute_program_jit(&mut instruction_meter) - }; - assert_eq!(SUCCESS, result.unwrap()); + { + let mut vm = create_vm( + &loader_id, + executable.as_ref(), + &mut parameter_bytes, + &mut invoke_context, + ) + .unwrap(); + let result = if i == 0 { + vm.execute_program_interpreted(&mut instruction_meter) + } else { + vm.execute_program_jit(&mut instruction_meter) + }; + assert_eq!(SUCCESS, result.unwrap()); + if i == 1 { + assert_eq!(instruction_count, vm.get_total_instruction_count()); + } + instruction_count = vm.get_total_instruction_count(); + if config.enable_instruction_tracing { + if i == 1 { + if !Tracer::compare(tracer.as_ref().unwrap(), vm.get_tracer()) { + let mut tracer_display = String::new(); + tracer + .as_ref() + .unwrap() + .write(&mut tracer_display, vm.get_program()) + .unwrap(); + println!("TRACE (interpreted): {}", tracer_display); + let mut tracer_display = String::new(); + vm.get_tracer() + .write(&mut tracer_display, vm.get_program()) + .unwrap(); + println!("TRACE (jit): {}", tracer_display); + assert!(false); + } + } + tracer = Some(vm.get_tracer().clone()); + } + } + let parameter_accounts = invoke_context.get_keyed_accounts().unwrap(); deserialize_parameters( &bpf_loader::id(), parameter_accounts, @@ -234,30 +260,6 @@ fn run_program( true, ) .unwrap(); - if i == 1 { - assert_eq!(instruction_count, vm.get_total_instruction_count()); - } - instruction_count = vm.get_total_instruction_count(); - if config.enable_instruction_tracing { - if i == 1 { - if !Tracer::compare(tracer.as_ref().unwrap(), vm.get_tracer()) { - let mut tracer_display = String::new(); - tracer - .as_ref() - .unwrap() - .write(&mut tracer_display, vm.get_program()) - .unwrap(); - println!("TRACE (interpreted): {}", tracer_display); - let mut tracer_display = String::new(); - vm.get_tracer() - .write(&mut tracer_display, vm.get_program()) - .unwrap(); - println!("TRACE (jit): {}", tracer_display); - assert!(false); - } - } - tracer = Some(vm.get_tracer().clone()); - } } Ok(instruction_count) @@ -1263,7 +1265,7 @@ fn assert_instruction_count() { let key = solana_sdk::pubkey::new_rand(); let mut account = RefCell::new(AccountSharedData::default()); let parameter_accounts = vec![KeyedAccount::new(&key, false, &mut account)]; - let count = run_program(program.0, &program_id, ¶meter_accounts[..], &[]).unwrap(); + let count = run_program(program.0, &program_id, parameter_accounts, &[]).unwrap(); let diff: i64 = count as i64 - program.1 as i64; println!(" {:30} {:8} {:6} {:+4}", program.0, program.1, count, diff); if count > program.1 { diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 7c67fcc334..fab9defc98 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -33,7 +33,7 @@ use solana_sdk::{ feature_set::{skip_ro_deserialization, upgradeable_close_instruction}, ic_logger_msg, ic_msg, instruction::InstructionError, - keyed_account::{from_keyed_account, next_keyed_account, KeyedAccount}, + keyed_account::{from_keyed_account, keyed_account_at_index}, loader_instruction::LoaderInstruction, loader_upgradeable_instruction::UpgradeableLoaderInstruction, process_instruction::{stable_log, ComputeMeter, Executor, InvokeContext}, @@ -61,70 +61,72 @@ pub enum BpfError { } impl UserDefinedError for BpfError {} -fn map_ebpf_error( - invoke_context: &mut dyn InvokeContext, - e: EbpfError, -) -> InstructionError { +fn map_ebpf_error(invoke_context: &dyn InvokeContext, e: EbpfError) -> InstructionError { ic_msg!(invoke_context, "{}", e); InstructionError::InvalidAccountData } -pub fn create_and_cache_executor( - key: &Pubkey, - data: &[u8], +pub fn create_executor( + program_account_index: usize, + program_data_offset: usize, invoke_context: &mut dyn InvokeContext, use_jit: bool, ) -> Result, InstructionError> { - let bpf_compute_budget = invoke_context.get_bpf_compute_budget(); - let mut program = >::from_elf( - data, - None, - Config { - max_call_depth: bpf_compute_budget.max_call_depth, - stack_frame_size: bpf_compute_budget.stack_frame_size, - enable_instruction_meter: true, - enable_instruction_tracing: log_enabled!(Trace), - }, - ) - .map_err(|e| map_ebpf_error(invoke_context, e))?; - let (_, elf_bytes) = program - .get_text_bytes() - .map_err(|e| map_ebpf_error(invoke_context, e))?; - bpf_verifier::check(elf_bytes) - .map_err(|e| map_ebpf_error(invoke_context, EbpfError::UserError(e)))?; let syscall_registry = syscalls::register_syscalls(invoke_context).map_err(|e| { ic_msg!(invoke_context, "Failed to register syscalls: {}", e); InstructionError::ProgramEnvironmentSetupFailure })?; - program.set_syscall_registry(syscall_registry); + let bpf_compute_budget = invoke_context.get_bpf_compute_budget(); + let config = Config { + max_call_depth: bpf_compute_budget.max_call_depth, + stack_frame_size: bpf_compute_budget.stack_frame_size, + enable_instruction_meter: true, + enable_instruction_tracing: log_enabled!(Trace), + }; + let mut executable = { + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let program = keyed_account_at_index(keyed_accounts, program_account_index)?; + let account = program.try_account_ref()?; + let data = &account.data()[program_data_offset..]; + >::from_elf(data, None, config) + } + .map_err(|e| map_ebpf_error(invoke_context, e))?; + let (_, elf_bytes) = executable + .get_text_bytes() + .map_err(|e| map_ebpf_error(invoke_context, e))?; + bpf_verifier::check(elf_bytes) + .map_err(|e| map_ebpf_error(invoke_context, EbpfError::UserError(e)))?; + executable.set_syscall_registry(syscall_registry); if use_jit { - if let Err(err) = program.jit_compile() { + if let Err(err) = executable.jit_compile() { ic_msg!(invoke_context, "Failed to compile program {:?}", err); return Err(InstructionError::ProgramFailedToCompile); } } - let executor = Arc::new(BpfExecutor { program }); - invoke_context.add_executor(key, executor.clone()); - Ok(executor) + Ok(Arc::new(BpfExecutor { executable })) } fn write_program_data( - data: &mut [u8], - offset: usize, + program_account_index: usize, + program_data_offset: usize, bytes: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let program = keyed_account_at_index(keyed_accounts, program_account_index)?; + let mut account = program.try_account_ref_mut()?; + let data = &mut account.data_as_mut_slice(); let len = bytes.len(); - if data.len() < offset + len { + if data.len() < program_data_offset + len { ic_msg!( invoke_context, "Write overflow: {} < {}", data.len(), - offset + len + program_data_offset + len ); return Err(InstructionError::AccountDataTooSmall); } - data[offset..offset + len].copy_from_slice(&bytes); + data[program_data_offset..program_data_offset + len].copy_from_slice(&bytes); Ok(()) } @@ -143,96 +145,69 @@ pub fn create_vm<'a>( loader_id: &'a Pubkey, program: &'a dyn Executable, parameter_bytes: &mut [u8], - parameter_accounts: &'a [KeyedAccount<'a>], invoke_context: &'a mut dyn InvokeContext, ) -> Result, EbpfError> { let heap = vec![0_u8; DEFAULT_HEAP_SIZE]; let heap_region = MemoryRegion::new_from_slice(&heap, MM_HEAP_START, 0, true); let mut vm = EbpfVm::new(program, parameter_bytes, &[heap_region])?; - syscalls::bind_syscall_context_objects( - loader_id, - &mut vm, - parameter_accounts, - invoke_context, - heap, - )?; + syscalls::bind_syscall_context_objects(loader_id, &mut vm, invoke_context, heap)?; Ok(vm) } pub fn process_instruction( program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - process_instruction_common( - program_id, - keyed_accounts, - instruction_data, - invoke_context, - false, - ) + process_instruction_common(program_id, instruction_data, invoke_context, false) } pub fn process_instruction_jit( program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - process_instruction_common( - program_id, - keyed_accounts, - instruction_data, - invoke_context, - true, - ) + process_instruction_common(program_id, instruction_data, invoke_context, true) } fn process_instruction_common( program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, use_jit: bool, ) -> Result<(), InstructionError> { let logger = invoke_context.get_logger(); + let keyed_accounts = invoke_context.get_keyed_accounts()?; - let account_iter = &mut keyed_accounts.iter(); - let first_account = next_keyed_account(account_iter)?; + let first_account = keyed_account_at_index(keyed_accounts, 0)?; if first_account.executable()? { if first_account.unsigned_key() != program_id { ic_logger_msg!(logger, "Program id mismatch"); return Err(InstructionError::IncorrectProgramId); } - let (program, keyed_accounts, offset) = - if bpf_loader_upgradeable::check_id(&first_account.owner()?) { - if let UpgradeableLoaderState::Program { - programdata_address, - } = first_account.state()? - { - let programdata = next_keyed_account(account_iter)?; - if programdata_address != *programdata.unsigned_key() { - ic_logger_msg!( - logger, - "Wrong ProgramData account for this Program account" - ); - return Err(InstructionError::InvalidArgument); - } - ( - programdata, - &keyed_accounts[1..], - UpgradeableLoaderState::programdata_data_offset()?, - ) - } else { - ic_logger_msg!(logger, "Invalid Program account"); - return Err(InstructionError::InvalidAccountData); + 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); } + invoke_context.remove_first_keyed_account()?; + UpgradeableLoaderState::programdata_data_offset()? } else { - (first_account, keyed_accounts, 0) - }; + ic_logger_msg!(logger, "Invalid Program account"); + return Err(InstructionError::InvalidAccountData); + } + } else { + 0 + }; + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let program = keyed_account_at_index(keyed_accounts, 0)?; let loader_id = &program.owner()?; if !check_loader_id(loader_id) { @@ -242,17 +217,15 @@ fn process_instruction_common( let executor = match invoke_context.get_executor(program_id) { Some(executor) => executor, - None => create_and_cache_executor( - program_id, - &program.try_account_ref()?.data()[offset..], - invoke_context, - use_jit, - )?, + None => { + let executor = create_executor(0, program_data_offset, invoke_context, use_jit)?; + invoke_context.add_executor(program_id, executor.clone()); + executor + } }; executor.execute( loader_id, program_id, - keyed_accounts, instruction_data, invoke_context, use_jit, @@ -266,19 +239,12 @@ fn process_instruction_common( if bpf_loader_upgradeable::check_id(program_id) { process_loader_upgradeable_instruction( program_id, - keyed_accounts, instruction_data, invoke_context, use_jit, )?; } else { - process_loader_instruction( - program_id, - keyed_accounts, - instruction_data, - invoke_context, - use_jit, - )?; + process_loader_instruction(program_id, instruction_data, invoke_context, use_jit)?; } } Ok(()) @@ -286,32 +252,31 @@ fn process_instruction_common( fn process_loader_upgradeable_instruction( program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, use_jit: bool, ) -> Result<(), InstructionError> { let logger = invoke_context.get_logger(); - let account_iter = &mut keyed_accounts.iter(); + let keyed_accounts = invoke_context.get_keyed_accounts()?; match limited_deserialize(instruction_data)? { UpgradeableLoaderInstruction::InitializeBuffer => { - let buffer = next_keyed_account(account_iter)?; + let buffer = keyed_account_at_index(keyed_accounts, 0)?; if UpgradeableLoaderState::Uninitialized != buffer.state()? { ic_logger_msg!(logger, "Buffer account already initialized"); return Err(InstructionError::AccountAlreadyInitialized); } - let authority = next_keyed_account(account_iter)?; + let authority = keyed_account_at_index(keyed_accounts, 1)?; buffer.set_state(&UpgradeableLoaderState::Buffer { authority_address: Some(*authority.unsigned_key()), })?; } UpgradeableLoaderInstruction::Write { offset, bytes } => { - let buffer = next_keyed_account(account_iter)?; - let authority = next_keyed_account(account_iter)?; + let buffer = keyed_account_at_index(keyed_accounts, 0)?; + let authority = keyed_account_at_index(keyed_accounts, 1)?; if let UpgradeableLoaderState::Buffer { authority_address } = buffer.state()? { if authority_address.is_none() { @@ -331,21 +296,22 @@ fn process_loader_upgradeable_instruction( return Err(InstructionError::InvalidAccountData); } write_program_data( - buffer.try_account_ref_mut()?.data_as_mut_slice(), + 0, UpgradeableLoaderState::buffer_data_offset()? + offset as usize, &bytes, invoke_context, )?; } UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => { - let payer = next_keyed_account(account_iter)?; - let programdata = next_keyed_account(account_iter)?; - let program = next_keyed_account(account_iter)?; - let buffer = next_keyed_account(account_iter)?; - let rent = from_keyed_account::(next_keyed_account(account_iter)?)?; - let clock = from_keyed_account::(next_keyed_account(account_iter)?)?; - let system = next_keyed_account(account_iter)?; - let authority = next_keyed_account(account_iter)?; + 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)?)?; + // TODO [KeyedAccounts to InvokeContext refactoring] + // let _system = keyed_account_at_index(keyed_accounts, 6)?; + let authority = keyed_account_at_index(keyed_accounts, 7)?; let upgrade_authority_address = Some(*authority.unsigned_key()); let upgrade_authority_signer = authority.signer_key().is_none(); @@ -409,26 +375,34 @@ fn process_loader_upgradeable_instruction( return Err(InstructionError::InvalidArgument); } + let instruction = system_instruction::create_account( + payer.unsigned_key(), + programdata.unsigned_key(), + 1.max(rent.minimum_balance(programdata_len)), + programdata_len as u64, + program_id, + ); + let caller_program_id = invoke_context.get_caller()?; + let signers = [&[program.unsigned_key().as_ref(), &[bump_seed]]] + .iter() + .map(|seeds| Pubkey::create_program_address(*seeds, caller_program_id)) + .collect::, solana_sdk::pubkey::PubkeyError>>()?; MessageProcessor::native_invoke( invoke_context, - system_instruction::create_account( - payer.unsigned_key(), - programdata.unsigned_key(), - 1.max(rent.minimum_balance(programdata_len)), - programdata_len as u64, - program_id, - ), - &[payer, programdata, system], - &[&[program.unsigned_key().as_ref(), &[bump_seed]]], + instruction, + &[0, 1, 6], + signers.as_slice(), )?; // Load and verify the program bits - let _ = create_and_cache_executor( - program_id, - &buffer.try_account_ref()?.data()[buffer_data_offset..], - invoke_context, - use_jit, - )?; + let executor = create_executor(3, buffer_data_offset, invoke_context, use_jit)?; + invoke_context.add_executor(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)?; // Update the ProgramData account and record the program bits programdata.set_state(&UpgradeableLoaderState::ProgramData { @@ -452,13 +426,12 @@ fn process_loader_upgradeable_instruction( ic_logger_msg!(logger, "Deployed program {:?}", program.unsigned_key()); } UpgradeableLoaderInstruction::Upgrade => { - let programdata = next_keyed_account(account_iter)?; - let program = next_keyed_account(account_iter)?; - let buffer = next_keyed_account(account_iter)?; - let spill = next_keyed_account(account_iter)?; - let rent = from_keyed_account::(next_keyed_account(account_iter)?)?; - let clock = from_keyed_account::(next_keyed_account(account_iter)?)?; - let authority = next_keyed_account(account_iter)?; + 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)?; // Verify Program account @@ -548,17 +521,18 @@ fn process_loader_upgradeable_instruction( } // Load and verify the program bits + let executor = create_executor(2, buffer_data_offset, invoke_context, use_jit)?; + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let program = keyed_account_at_index(keyed_accounts, 1)?; + invoke_context.add_executor(program.unsigned_key(), executor); - let _ = create_and_cache_executor( - program.unsigned_key(), - &buffer.try_account_ref()?.data()[buffer_data_offset..], - invoke_context, - use_jit, - )?; + 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)?; // Update the ProgramData account, record the upgraded data, and zero // the rest - programdata.set_state(&UpgradeableLoaderState::ProgramData { slot: clock.slot, upgrade_authority_address: Some(*authority.unsigned_key()), @@ -581,9 +555,9 @@ fn process_loader_upgradeable_instruction( ic_logger_msg!(logger, "Upgraded program {:?}", program.unsigned_key()); } UpgradeableLoaderInstruction::SetAuthority => { - let account = next_keyed_account(account_iter)?; - let present_authority = next_keyed_account(account_iter)?; - let new_authority = next_keyed_account(account_iter) + 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()); @@ -642,9 +616,9 @@ fn process_loader_upgradeable_instruction( if !invoke_context.is_feature_active(&upgradeable_close_instruction::id()) { return Err(InstructionError::InvalidInstructionData); } - let close_account = next_keyed_account(account_iter)?; - let recipient_account = next_keyed_account(account_iter)?; - let authority = next_keyed_account(account_iter)?; + let close_account = keyed_account_at_index(keyed_accounts, 0)?; + let recipient_account = keyed_account_at_index(keyed_accounts, 1)?; + let authority = keyed_account_at_index(keyed_accounts, 2)?; if close_account.unsigned_key() == recipient_account.unsigned_key() { ic_logger_msg!(logger, "Recipient is the same as the account being closed"); @@ -684,14 +658,12 @@ fn process_loader_upgradeable_instruction( fn process_loader_instruction( program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, use_jit: bool, ) -> Result<(), InstructionError> { - let account_iter = &mut keyed_accounts.iter(); - - let program = next_keyed_account(account_iter)?; + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let program = keyed_account_at_index(keyed_accounts, 0)?; if program.owner()? != *program_id { ic_msg!( invoke_context, @@ -705,12 +677,7 @@ fn process_loader_instruction( ic_msg!(invoke_context, "Program account did not sign"); return Err(InstructionError::MissingRequiredSignature); } - write_program_data( - &mut program.try_account_ref_mut()?.data_as_mut_slice(), - offset as usize, - &bytes, - invoke_context, - )?; + write_program_data(0, offset as usize, &bytes, invoke_context)?; } LoaderInstruction::Finalize => { if program.signer_key().is_none() { @@ -718,12 +685,10 @@ fn process_loader_instruction( return Err(InstructionError::MissingRequiredSignature); } - let _ = create_and_cache_executor( - program.unsigned_key(), - &program.try_account_ref()?.data(), - invoke_context, - use_jit, - )?; + let executor = create_executor(0, 0, invoke_context, use_jit)?; + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let program = keyed_account_at_index(keyed_accounts, 0)?; + invoke_context.add_executor(program.unsigned_key(), executor); program.try_account_ref_mut()?.executable = true; ic_msg!( invoke_context, @@ -758,7 +723,7 @@ impl InstructionMeter for ThisInstructionMeter { /// BPF Loader's Executor implementation pub struct BpfExecutor { - program: Box>, + executable: Box>, } // Well, implement Debug for solana_rbpf::vm::Executable in solana-rbpf... @@ -773,7 +738,6 @@ impl Executor for BpfExecutor { &self, loader_id: &Pubkey, program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, use_jit: bool, @@ -781,12 +745,12 @@ impl Executor for BpfExecutor { let logger = invoke_context.get_logger(); let invoke_depth = invoke_context.invoke_depth(); - let mut keyed_accounts_iter = keyed_accounts.iter(); - let _ = next_keyed_account(&mut keyed_accounts_iter)?; - let parameter_accounts = keyed_accounts_iter.as_slice(); + 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 = - serialize_parameters(loader_id, program_id, parameter_accounts, &instruction_data)?; + serialize_parameters(loader_id, program_id, keyed_accounts, &instruction_data)?; serialize_time.stop(); let mut create_vm_time = Measure::start("create_vm"); let mut execute_time; @@ -794,9 +758,8 @@ impl Executor for BpfExecutor { let compute_meter = invoke_context.get_compute_meter(); let mut vm = match create_vm( loader_id, - self.program.as_ref(), + self.executable.as_ref(), &mut parameter_bytes, - ¶meter_accounts, invoke_context, ) { Ok(info) => info, @@ -856,9 +819,10 @@ impl Executor for BpfExecutor { execute_time.stop(); } let mut deserialize_time = Measure::start("deserialize"); + let keyed_accounts = invoke_context.get_keyed_accounts()?; deserialize_parameters( loader_id, - parameter_accounts, + keyed_accounts, ¶meter_bytes, invoke_context.is_feature_active(&skip_ro_deserialization::id()), )?; @@ -890,8 +854,12 @@ mod tests { genesis_config::create_genesis_config, instruction::Instruction, instruction::{AccountMeta, InstructionError}, + keyed_account::KeyedAccount, message::Message, - process_instruction::{MockComputeMeter, MockInvokeContext}, + process_instruction::{ + BpfComputeBudget, InvokeContextStackFrame, MockComputeMeter, MockInvokeContext, + MockLogger, + }, pubkey::Pubkey, rent::Rent, signature::{Keypair, Signer}, @@ -962,9 +930,8 @@ mod tests { Err(InstructionError::NotEnoughAccountKeys), process_instruction( &bpf_loader::id(), - &[], &instruction_data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(vec![]) ) ); @@ -973,9 +940,8 @@ mod tests { Err(InstructionError::MissingRequiredSignature), process_instruction( &bpf_loader::id(), - &keyed_accounts, &instruction_data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -983,15 +949,12 @@ mod tests { #[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); assert_eq!( Ok(()), - process_instruction( - &bpf_loader::id(), - &keyed_accounts, - &instruction_data, - &mut MockInvokeContext::default() - ) + 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() @@ -1005,9 +968,8 @@ mod tests { Err(InstructionError::AccountDataTooSmall), process_instruction( &bpf_loader::id(), - &keyed_accounts, &instruction_data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); } @@ -1031,9 +993,8 @@ mod tests { Err(InstructionError::NotEnoughAccountKeys), process_instruction( &bpf_loader::id(), - &[], &instruction_data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(vec![]) ) ); @@ -1042,23 +1003,19 @@ mod tests { Err(InstructionError::MissingRequiredSignature), process_instruction( &bpf_loader::id(), - &keyed_accounts, &instruction_data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); // Case: Finalize let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)]; + let mut invoke_context = MockInvokeContext::new(keyed_accounts); assert_eq!( Ok(()), - process_instruction( - &bpf_loader::id(), - &keyed_accounts, - &instruction_data, - &mut MockInvokeContext::default() - ) + process_instruction(&bpf_loader::id(), &instruction_data, &mut invoke_context) ); + let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); assert!(keyed_accounts[0].account.borrow().executable); program_account.borrow_mut().executable = false; // Un-finalize the account @@ -1070,9 +1027,8 @@ mod tests { Err(InstructionError::InvalidAccountData), process_instruction( &bpf_loader::id(), - &keyed_accounts, &instruction_data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); } @@ -1092,25 +1048,31 @@ mod tests { let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)]; - let mut invoke_context = MockInvokeContext::default(); - // Case: Empty keyed accounts assert_eq!( Err(InstructionError::NotEnoughAccountKeys), - process_instruction(&program_id, &[], &[], &mut invoke_context) + process_instruction(&program_id, &[], &mut MockInvokeContext::new(vec![])) ); // Case: Only a program account assert_eq!( Ok(()), - process_instruction(&program_key, &keyed_accounts, &[], &mut invoke_context) + process_instruction( + &program_key, + &[], + &mut MockInvokeContext::new(keyed_accounts.clone()) + ) ); // Case: Account not a program keyed_accounts[0].account.borrow_mut().executable = false; assert_eq!( Err(InstructionError::InvalidInstructionData), - process_instruction(&program_id, &keyed_accounts, &[], &mut invoke_context) + process_instruction( + &program_id, + &[], + &mut MockInvokeContext::new(keyed_accounts.clone()) + ) ); keyed_accounts[0].account.borrow_mut().executable = true; @@ -1119,36 +1081,49 @@ mod tests { keyed_accounts.push(KeyedAccount::new(&program_key, false, ¶meter_account)); assert_eq!( Ok(()), - process_instruction(&program_key, &keyed_accounts, &[], &mut invoke_context) - ); - - // Case: limited budget - let program_id = Pubkey::default(); - let mut invoke_context = MockInvokeContext { - key: program_id, - compute_meter: MockComputeMeter::default(), - ..MockInvokeContext::default() - }; - assert_eq!( - Err(InstructionError::ProgramFailedToComplete), - process_instruction(&program_key, &keyed_accounts, &[], &mut invoke_context) + process_instruction( + &program_key, + &[], + &mut MockInvokeContext::new(keyed_accounts.clone()) + ) ); // 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 keyed_accounts = vec![ + KeyedAccount::new(&program_key, false, &program_account), + KeyedAccount::new(&duplicate_key, false, ¶meter_account), + KeyedAccount::new(&duplicate_key, false, ¶meter_account), + ]; assert_eq!( Ok(()), process_instruction( &program_key, - &keyed_accounts, &[], - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts.clone()) ) ); + + // Case: limited budget + let keyed_accounts_range = 0..keyed_accounts.len(); + let mut invoke_context = MockInvokeContext { + invoke_stack: vec![InvokeContextStackFrame { + key: Pubkey::default(), + keyed_accounts, + keyed_accounts_range, + }], + logger: MockLogger::default(), + bpf_compute_budget: BpfComputeBudget::default(), + compute_meter: MockComputeMeter::default(), + programs: vec![], + accounts: vec![], + sysvars: vec![], + }; + assert_eq!( + Err(InstructionError::ProgramFailedToComplete), + process_instruction(&program_key, &[], &mut invoke_context) + ); } #[test] @@ -1172,9 +1147,8 @@ mod tests { Ok(()), process_instruction( &program_key, - &keyed_accounts, &[], - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -1188,9 +1162,8 @@ mod tests { Ok(()), process_instruction( &program_key, - &keyed_accounts, &[], - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); } @@ -1216,9 +1189,8 @@ mod tests { Ok(()), process_instruction( &program_key, - &keyed_accounts, &[], - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -1232,9 +1204,8 @@ mod tests { Ok(()), process_instruction( &program_key, - &keyed_accounts, &[], - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); } @@ -1257,16 +1228,16 @@ mod tests { ); // Case: Success + let keyed_accounts = vec![ + KeyedAccount::new(&buffer_address, false, &buffer_account), + KeyedAccount::new(&authority_address, false, &authority_account), + ]; assert_eq!( Ok(()), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&authority_address, false, &authority_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); @@ -1278,16 +1249,16 @@ 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(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&authority_address, false, &authority_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); @@ -1314,16 +1285,16 @@ 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), + ]; assert_eq!( Err(InstructionError::InvalidAccountData), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, true, &buffer_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -1339,16 +1310,16 @@ 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(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, true, &buffer_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); @@ -1381,16 +1352,16 @@ 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(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, true, &buffer_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); @@ -1418,16 +1389,16 @@ mod tests { 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(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, false, &buffer_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -1443,16 +1414,16 @@ 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(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, true, &buffer_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -1468,16 +1439,16 @@ 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(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&buffer_address, true, &buffer_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -1494,16 +1465,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), + ]; assert_eq!( Err(InstructionError::IncorrectAuthority), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&authority_address, true, &buffer_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -1520,16 +1491,16 @@ 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(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&authority_address, true, &buffer_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); } @@ -2209,6 +2180,8 @@ mod tests { Pubkey::find_program_address(&[program_address.as_ref()], &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(); + let clock_id = sysvar::clock::id(); #[allow(clippy::type_complexity)] fn get_accounts( @@ -2285,25 +2258,25 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Ok(()), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); assert_eq!(0, buffer_account.borrow().lamports); @@ -2347,25 +2320,25 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::Immutable), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2380,25 +2353,26 @@ mod tests { min_program_balance, 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::IncorrectAuthority), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &Pubkey::new_unique(), - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2413,25 +2387,25 @@ 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, + false, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::MissingRequiredSignature), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - false, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2447,25 +2421,25 @@ mod tests { min_programdata_balance, ); program_account.borrow_mut().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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::AccountNotExecutable), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2481,25 +2455,25 @@ mod tests { min_programdata_balance, ); program_account.borrow_mut().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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::IncorrectProgramId), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2514,25 +2488,25 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::InvalidArgument), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2551,25 +2525,25 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::InvalidAccountData), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2584,25 +2558,26 @@ mod tests { min_program_balance, 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::InvalidArgument), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&Pubkey::new_unique(), 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2621,25 +2596,25 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::InvalidArgument), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2665,25 +2640,25 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::AccountDataTooSmall), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2705,25 +2680,25 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::InvalidAccountData), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2738,25 +2713,25 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::IncorrectAuthority), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2777,25 +2752,25 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::IncorrectAuthority), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2823,25 +2798,25 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::IncorrectAuthority), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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(&sysvar::rent::id(), false, &rent_account), - KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); } @@ -2871,25 +2846,25 @@ 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, + true, + &upgrade_authority_account, + ), + KeyedAccount::new_readonly( + &new_upgrade_authority_address, + false, + &new_upgrade_authority_account, + ), + ]; assert_eq!( Ok(()), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ), - KeyedAccount::new_readonly( - &new_upgrade_authority_address, - false, - &new_upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); let state: UpgradeableLoaderState = programdata_account.borrow().state().unwrap(); @@ -2909,20 +2884,20 @@ 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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Ok(()), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - true, - &upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); let state: UpgradeableLoaderState = programdata_account.borrow().state().unwrap(); @@ -2942,20 +2917,20 @@ 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, + false, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::MissingRequiredSignature), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &upgrade_authority_address, - false, - &upgrade_authority_account - ), - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2967,25 +2942,26 @@ mod tests { upgrade_authority_address: Some(upgrade_authority_address), }) .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, + true, + &upgrade_authority_account, + ), + KeyedAccount::new_readonly( + &new_upgrade_authority_address, + false, + &new_upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::IncorrectAuthority), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &Pubkey::new_unique(), - true, - &upgrade_authority_account - ), - KeyedAccount::new_readonly( - &new_upgrade_authority_address, - false, - &new_upgrade_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -2997,20 +2973,21 @@ mod tests { upgrade_authority_address: None, }) .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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::Immutable), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &Pubkey::new_unique(), - true, - &upgrade_authority_account - ), - ], &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -3021,20 +2998,21 @@ mod tests { programdata_address: Pubkey::new_unique(), }) .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, + true, + &upgrade_authority_account, + ), + ]; assert_eq!( Err(InstructionError::InvalidArgument), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&programdata_address, false, &programdata_account), - KeyedAccount::new_readonly( - &Pubkey::new_unique(), - true, - &upgrade_authority_account - ), - ], &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); } @@ -3060,21 +3038,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), + KeyedAccount::new_readonly(&new_authority_address, false, &new_authority_account), + ]; assert_eq!( Ok(()), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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 - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); @@ -3092,16 +3066,16 @@ 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), + ]; assert_eq!( Err(InstructionError::IncorrectAuthority), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&authority_address, true, &authority_account) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap(); @@ -3119,21 +3093,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, false, &authority_account), + KeyedAccount::new_readonly(&new_authority_address, false, &new_authority_account), + ]; assert_eq!( Err(InstructionError::MissingRequiredSignature), process_instruction( &bpf_loader_upgradeable::id(), - &[ - 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 - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -3144,21 +3114,18 @@ mod tests { authority_address: Some(authority_address), }) .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::IncorrectAuthority), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account), - KeyedAccount::new_readonly( - &new_authority_address, - false, - &new_authority_account - ) - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -3169,21 +3136,18 @@ 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(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account), - KeyedAccount::new_readonly( - &new_authority_address, - false, - &new_authority_account - ) - ], &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -3194,16 +3158,17 @@ 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), + ]; assert_eq!( Err(InstructionError::InvalidArgument), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account), - ], &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); @@ -3214,16 +3179,16 @@ 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), + ]; assert_eq!( Err(InstructionError::IncorrectAuthority), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new_readonly(&authority_address, true, &authority_account), - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ) ); } @@ -3249,17 +3214,17 @@ 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), + ]; assert_eq!( Ok(()), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&recipient_address, false, &recipient_account), - KeyedAccount::new_readonly(&authority_address, true, &authority_account), - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts), ) ); assert_eq!(0, buffer_account.borrow().lamports()); @@ -3277,17 +3242,18 @@ mod tests { authority_address: Some(authority_address), }) .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), + ]; assert_eq!( Err(InstructionError::IncorrectAuthority), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&recipient_address, false, &recipient_account), - KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account), - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts), ) ); @@ -3298,17 +3264,17 @@ mod tests { programdata_address: Pubkey::new_unique(), }) .unwrap(); + 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), + ]; assert_eq!( Err(InstructionError::InvalidArgument), process_instruction( &bpf_loader_upgradeable::id(), - &[ - KeyedAccount::new(&buffer_address, false, &buffer_account), - KeyedAccount::new(&recipient_address, false, &recipient_account), - KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account), - ], &instruction, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts), ) ); } @@ -3367,9 +3333,8 @@ mod tests { let _result = process_instruction( &bpf_loader::id(), - &keyed_accounts, &[], - &mut MockInvokeContext::default(), + &mut MockInvokeContext::new(keyed_accounts), ); }, ); diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 989247ae59..1900669cf4 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -169,7 +169,6 @@ macro_rules! bind_feature_gated_syscall_context_object { pub fn bind_syscall_context_objects<'a>( loader_id: &'a Pubkey, vm: &mut EbpfVm<'a, BpfError, crate::ThisInstructionMeter>, - callers_keyed_accounts: &'a [KeyedAccount<'a>], invoke_context: &'a mut dyn InvokeContext, heap: Vec, ) -> Result<(), EbpfError> { @@ -299,7 +298,6 @@ pub fn bind_syscall_context_objects<'a>( // Cross-program invocation syscalls vm.bind_syscall_context_object( Box::new(SyscallInvokeSignedC { - callers_keyed_accounts, invoke_context: invoke_context.clone(), loader_id, }), @@ -307,7 +305,6 @@ pub fn bind_syscall_context_objects<'a>( )?; vm.bind_syscall_context_object( Box::new(SyscallInvokeSignedRust { - callers_keyed_accounts, invoke_context: invoke_context.clone(), loader_id, }), @@ -1029,7 +1026,6 @@ type TranslatedAccounts<'a> = ( trait SyscallInvokeSigned<'a> { fn get_context_mut(&self) -> Result, EbpfError>; fn get_context(&self) -> Result, EbpfError>; - fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>]; fn translate_instruction( &self, addr: u64, @@ -1055,7 +1051,6 @@ trait SyscallInvokeSigned<'a> { /// Cross-program invocation called from Rust pub struct SyscallInvokeSignedRust<'a> { - callers_keyed_accounts: &'a [KeyedAccount<'a>], invoke_context: Rc>, loader_id: &'a Pubkey, } @@ -1070,9 +1065,6 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { .try_borrow() .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) } - fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] { - self.callers_keyed_accounts - } fn translate_instruction( &self, addr: u64, @@ -1350,7 +1342,6 @@ struct SolSignerSeedsC { /// Cross-program invocation called from C pub struct SyscallInvokeSignedC<'a> { - callers_keyed_accounts: &'a [KeyedAccount<'a>], invoke_context: Rc>, loader_id: &'a Pubkey, } @@ -1366,10 +1357,6 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { .map_err(|_| SyscallError::InvokeContextBorrowFailed.into()) } - fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] { - self.callers_keyed_accounts - } - fn translate_instruction( &self, addr: u64, @@ -1777,8 +1764,9 @@ fn call<'a>( signers_seeds_len, memory_mapping, )?; - let keyed_account_refs = syscall - .get_callers_keyed_accounts() + let keyed_account_refs = invoke_context + .get_keyed_accounts() + .map_err(SyscallError::InstructionError)? .iter() .collect::>(); let (message, callee_program_id, callee_program_id_index) = @@ -2683,7 +2671,7 @@ mod tests { leader_schedule_epoch: 4, unix_timestamp: 5, }; - let mut invoke_context = MockInvokeContext::default(); + let mut invoke_context = MockInvokeContext::new(vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_clock).unwrap(); invoke_context @@ -2725,7 +2713,7 @@ mod tests { first_normal_epoch: 3, first_normal_slot: 4, }; - let mut invoke_context = MockInvokeContext::default(); + let mut invoke_context = MockInvokeContext::new(vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_epochschedule).unwrap(); invoke_context @@ -2773,7 +2761,7 @@ mod tests { lamports_per_signature: 1, }, }; - let mut invoke_context = MockInvokeContext::default(); + let mut invoke_context = MockInvokeContext::new(vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_fees).unwrap(); invoke_context @@ -2813,7 +2801,7 @@ mod tests { exemption_threshold: 2.0, burn_percent: 3, }; - let mut invoke_context = MockInvokeContext::default(); + let mut invoke_context = MockInvokeContext::new(vec![]); let mut data = vec![]; bincode::serialize_into(&mut data, &src_rent).unwrap(); invoke_context diff --git a/programs/budget/src/budget_processor.rs b/programs/budget/src/budget_processor.rs index 9b0dc96335..5e68cc4687 100644 --- a/programs/budget/src/budget_processor.rs +++ b/programs/budget/src/budget_processor.rs @@ -10,7 +10,7 @@ use solana_sdk::{ account::{ReadableAccount, WritableAccount}, hash::hash, instruction::InstructionError, - keyed_account::{next_keyed_account, KeyedAccount}, + keyed_account::{keyed_account_at_index, KeyedAccount}, process_instruction::InvokeContext, program_utils::limited_deserialize, pubkey::Pubkey, @@ -116,22 +116,22 @@ fn apply_account_data( pub fn process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], - _invoke_context: &mut dyn InvokeContext, + invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - let keyed_accounts_iter = &mut keyed_accounts.iter(); + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let instruction = limited_deserialize(data)?; trace!("process_instruction: {:?}", instruction); match instruction { BudgetInstruction::InitializeAccount(expr) => { - let contract_keyed_account = next_keyed_account(keyed_accounts_iter)?; + let contract_keyed_account = keyed_account_at_index(keyed_accounts, 0)?; if let Some(payment) = expr.final_payment() { let to_keyed_account = contract_keyed_account; - let contract_keyed_account = next_keyed_account(keyed_accounts_iter)?; + let contract_keyed_account = keyed_account_at_index(keyed_accounts, 1)?; contract_keyed_account.try_account_ref_mut()?.lamports = 0; to_keyed_account.try_account_ref_mut()?.lamports += payment.lamports; return Ok(()); @@ -154,8 +154,8 @@ pub fn process_instruction( ) } BudgetInstruction::ApplyTimestamp(dt) => { - let witness_keyed_account = next_keyed_account(keyed_accounts_iter)?; - let contract_keyed_account = next_keyed_account(keyed_accounts_iter)?; + let witness_keyed_account = keyed_account_at_index(keyed_accounts, 0)?; + let contract_keyed_account = keyed_account_at_index(keyed_accounts, 1)?; let mut budget_state = BudgetState::deserialize(&contract_keyed_account.try_account_ref()?.data())?; if !budget_state.is_pending() { @@ -173,7 +173,7 @@ pub fn process_instruction( &mut budget_state, witness_keyed_account, contract_keyed_account, - next_keyed_account(keyed_accounts_iter), + keyed_account_at_index(keyed_accounts, 2), dt, )?; trace!("apply timestamp committed"); @@ -184,8 +184,8 @@ pub fn process_instruction( ) } BudgetInstruction::ApplySignature => { - let witness_keyed_account = next_keyed_account(keyed_accounts_iter)?; - let contract_keyed_account = next_keyed_account(keyed_accounts_iter)?; + let witness_keyed_account = keyed_account_at_index(keyed_accounts, 0)?; + let contract_keyed_account = keyed_account_at_index(keyed_accounts, 1)?; let mut budget_state = BudgetState::deserialize(&contract_keyed_account.try_account_ref()?.data())?; if !budget_state.is_pending() { @@ -203,7 +203,7 @@ pub fn process_instruction( &mut budget_state, witness_keyed_account, contract_keyed_account, - next_keyed_account(keyed_accounts_iter), + keyed_account_at_index(keyed_accounts, 2), )?; trace!("apply signature committed"); budget_state.serialize( @@ -213,8 +213,8 @@ pub fn process_instruction( ) } BudgetInstruction::ApplyAccountData => { - let witness_keyed_account = next_keyed_account(keyed_accounts_iter)?; - let contract_keyed_account = next_keyed_account(keyed_accounts_iter)?; + let witness_keyed_account = keyed_account_at_index(keyed_accounts, 0)?; + let contract_keyed_account = keyed_account_at_index(keyed_accounts, 1)?; let mut budget_state = BudgetState::deserialize(&contract_keyed_account.try_account_ref()?.data())?; if !budget_state.is_pending() { @@ -228,7 +228,7 @@ pub fn process_instruction( &mut budget_state, witness_keyed_account, contract_keyed_account, - next_keyed_account(keyed_accounts_iter), + keyed_account_at_index(keyed_accounts, 2), )?; trace!("apply account data committed"); budget_state.serialize( diff --git a/programs/config/src/config_processor.rs b/programs/config/src/config_processor.rs index 105cf8a36e..4197eea1c1 100644 --- a/programs/config/src/config_processor.rs +++ b/programs/config/src/config_processor.rs @@ -6,7 +6,7 @@ use solana_sdk::{ account::{ReadableAccount, WritableAccount}, feature_set, ic_msg, instruction::InstructionError, - keyed_account::{next_keyed_account, KeyedAccount}, + keyed_account::keyed_account_at_index, process_instruction::InvokeContext, program_utils::limited_deserialize, pubkey::Pubkey, @@ -14,14 +14,13 @@ use solana_sdk::{ pub fn process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - let key_list: ConfigKeys = limited_deserialize(data)?; - let keyed_accounts_iter = &mut keyed_accounts.iter(); - let config_keyed_account = &mut next_keyed_account(keyed_accounts_iter)?; + 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 current_data: ConfigKeys = { let config_account = config_keyed_account.try_account_ref_mut()?; if invoke_context.is_feature_active(&feature_set::check_program_owner::id()) @@ -55,6 +54,7 @@ pub fn process_instruction( } let mut counter = 0; + let mut keyed_accounts_iter = keyed_accounts.iter().skip(1); for (signer, _) in key_list.keys.iter().filter(|(_, is_signer)| *is_signer) { counter += 1; if signer != config_keyed_account.unsigned_key() { @@ -132,7 +132,7 @@ mod tests { use serde_derive::{Deserialize, Serialize}; use solana_sdk::{ account::{Account, AccountSharedData}, - keyed_account::create_keyed_is_signer_accounts, + keyed_account::create_keyed_accounts_unified, process_instruction::MockInvokeContext, signature::{Keypair, Signer}, system_instruction::SystemInstruction, @@ -185,14 +185,13 @@ mod tests { owner: id(), ..Account::default() })); - let accounts = vec![(&config_pubkey, true, &config_account)]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let accounts = vec![(true, false, &config_pubkey, &config_account)]; + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instructions[1].data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Ok(()) ); @@ -220,14 +219,13 @@ mod tests { let my_config = MyConfig::new(42); let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config); - let accounts = vec![(&config_pubkey, true, &config_account)]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let accounts = vec![(true, false, &config_pubkey, &config_account)]; + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Ok(()) ); @@ -247,14 +245,13 @@ 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![(&config_pubkey, true, &config_account)]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let accounts = vec![(true, false, &config_pubkey, &config_account)]; + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::InvalidInstructionData) ); @@ -270,14 +267,13 @@ 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![(&config_pubkey, false, &config_account)]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let accounts = vec![(false, false, &config_pubkey, &config_account)]; + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::MissingRequiredSignature) ); @@ -302,17 +298,16 @@ mod tests { let signer0_account = RefCell::new(AccountSharedData::default()); let signer1_account = RefCell::new(AccountSharedData::default()); let accounts = vec![ - (&config_pubkey, true, &config_account), - (&signer0_pubkey, true, &signer0_account), - (&signer1_pubkey, true, &signer1_account), + (true, false, &config_pubkey, &config_account), + (true, false, &signer0_pubkey, &signer0_account), + (true, false, &signer1_pubkey, &signer1_account), ]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Ok(()) ); @@ -339,14 +334,13 @@ mod tests { owner: id(), ..Account::default() })); - let accounts = vec![(&signer0_pubkey, true, &signer0_account)]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let accounts = vec![(true, false, &signer0_pubkey, &signer0_account)]; + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::InvalidAccountData) ); @@ -368,32 +362,30 @@ mod tests { // Config-data pubkey doesn't match signer let accounts = vec![ - (&config_pubkey, true, &config_account), - (&signer1_pubkey, true, &signer1_account), + (true, false, &config_pubkey, &config_account), + (true, false, &signer1_pubkey, &signer1_account), ]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::MissingRequiredSignature) ); // Config-data pubkey not a signer let accounts = vec![ - (&config_pubkey, true, &config_account), - (&signer0_pubkey, false, &signer0_account), + (true, false, &config_pubkey, &config_account), + (false, false, &signer0_pubkey, &signer0_account), ]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::MissingRequiredSignature) ); @@ -420,17 +412,16 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); let accounts = vec![ - (&config_pubkey, true, &config_account), - (&signer0_pubkey, true, &signer0_account), - (&signer1_pubkey, true, &signer1_account), + (true, false, &config_pubkey, &config_account), + (true, false, &signer0_pubkey, &signer0_account), + (true, false, &signer1_pubkey, &signer1_account), ]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Ok(()) ); @@ -440,17 +431,16 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, false, keys.clone(), &new_config); let accounts = vec![ - (&config_pubkey, false, &config_account), - (&signer0_pubkey, true, &signer0_account), - (&signer1_pubkey, true, &signer1_account), + (false, false, &config_pubkey, &config_account), + (true, false, &signer0_pubkey, &signer0_account), + (true, false, &signer1_pubkey, &signer1_account), ]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Ok(()) ); @@ -466,17 +456,16 @@ mod tests { let keys = vec![(pubkey, false), (signer0_pubkey, true)]; let instruction = config_instruction::store(&config_pubkey, false, keys, &my_config); let accounts = vec![ - (&config_pubkey, false, &config_account), - (&signer0_pubkey, true, &signer0_account), - (&signer1_pubkey, false, &signer1_account), + (false, false, &config_pubkey, &config_account), + (true, false, &signer0_pubkey, &signer0_account), + (false, false, &signer1_pubkey, &signer1_account), ]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::MissingRequiredSignature) ); @@ -489,17 +478,16 @@ mod tests { ]; let instruction = config_instruction::store(&config_pubkey, false, keys, &my_config); let accounts = vec![ - (&config_pubkey, false, &config_account), - (&signer0_pubkey, true, &signer0_account), - (&signer2_pubkey, true, &signer2_account), + (false, false, &config_pubkey, &config_account), + (true, false, &signer0_pubkey, &signer0_account), + (true, false, &signer2_pubkey, &signer2_account), ]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::MissingRequiredSignature) ); @@ -528,16 +516,15 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); let accounts = vec![ - (&config_pubkey, true, &config_account), - (&signer0_pubkey, true, &signer0_account), + (true, false, &config_pubkey, &config_account), + (true, false, &signer0_pubkey, &signer0_account), ]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Ok(()) ); @@ -547,16 +534,15 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &new_config); let accounts = vec![ - (&config_pubkey, true, &config_account), - (&signer0_pubkey, true, &signer0_account), + (true, false, &config_pubkey, &config_account), + (true, false, &signer0_pubkey, &signer0_account), ]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Ok(()) ); @@ -571,14 +557,13 @@ 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![(&config_pubkey, true, &config_account)]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let accounts = vec![(true, false, &config_pubkey, &config_account)]; + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::MissingRequiredSignature) ); @@ -591,13 +576,12 @@ mod tests { let instructions = config_instruction::create_account::(&from_pubkey, &config_pubkey, 1, vec![]); let accounts = vec![]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instructions[1].data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::NotEnoughAccountKeys) ); @@ -619,16 +603,15 @@ mod tests { let instruction = config_instruction::store(&config_pubkey, true, keys, &new_config); let accounts = vec![ - (&config_pubkey, true, &config_account), - (&signer0_pubkey, true, &signer0_account), + (true, false, &config_pubkey, &config_account), + (true, false, &signer0_pubkey, &signer0_account), ]; - let keyed_accounts = create_keyed_is_signer_accounts(&accounts); + let keyed_accounts = create_keyed_accounts_unified(&accounts); assert_eq!( process_instruction( &id(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts), ), Err(InstructionError::InvalidAccountOwner) ); diff --git a/programs/exchange/src/exchange_processor.rs b/programs/exchange/src/exchange_processor.rs index 200126c17f..f2a36630b5 100644 --- a/programs/exchange/src/exchange_processor.rs +++ b/programs/exchange/src/exchange_processor.rs @@ -487,12 +487,12 @@ impl ExchangeProcessor { pub fn process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], - _invoke_context: &mut dyn InvokeContext, + invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - solana_logger::setup(); + let keyed_accounts = invoke_context.get_keyed_accounts()?; + solana_logger::setup(); match limited_deserialize::(data)? { ExchangeInstruction::AccountRequest => { ExchangeProcessor::do_account_request(keyed_accounts) diff --git a/programs/failure/src/lib.rs b/programs/failure/src/lib.rs index f55ae06f5d..afebc3fed9 100644 --- a/programs/failure/src/lib.rs +++ b/programs/failure/src/lib.rs @@ -1,6 +1,5 @@ use solana_sdk::{ - instruction::InstructionError, keyed_account::KeyedAccount, process_instruction::InvokeContext, - pubkey::Pubkey, + instruction::InstructionError, process_instruction::InvokeContext, pubkey::Pubkey, }; solana_sdk::declare_program!( @@ -11,7 +10,6 @@ solana_sdk::declare_program!( fn process_instruction( _program_id: &Pubkey, - _keyed_accounts: &[KeyedAccount], _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { diff --git a/programs/noop/src/lib.rs b/programs/noop/src/lib.rs index 8bfc2d9d6d..3e4acad2da 100644 --- a/programs/noop/src/lib.rs +++ b/programs/noop/src/lib.rs @@ -1,7 +1,6 @@ use log::*; use solana_sdk::{ - instruction::InstructionError, keyed_account::KeyedAccount, process_instruction::InvokeContext, - pubkey::Pubkey, + instruction::InstructionError, process_instruction::InvokeContext, pubkey::Pubkey, }; solana_sdk::declare_program!( @@ -12,13 +11,11 @@ solana_sdk::declare_program!( pub fn process_instruction( program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { solana_logger::setup(); trace!("noop: program_id: {:?}", program_id); - trace!("noop: keyed_accounts: {:#?}", keyed_accounts); trace!("noop: data: {:?}", data); Ok(()) } diff --git a/programs/ownable/src/ownable_processor.rs b/programs/ownable/src/ownable_processor.rs index b5be6e6abe..d9532922ca 100644 --- a/programs/ownable/src/ownable_processor.rs +++ b/programs/ownable/src/ownable_processor.rs @@ -5,7 +5,7 @@ use bincode::serialize_into; use solana_sdk::{ account::{ReadableAccount, WritableAccount}, instruction::InstructionError, - keyed_account::{next_keyed_account, KeyedAccount}, + keyed_account::{keyed_account_at_index, KeyedAccount}, process_instruction::InvokeContext, program_utils::limited_deserialize, pubkey::Pubkey, @@ -30,20 +30,20 @@ fn set_owner( pub fn process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], - _invoke_context: &mut dyn InvokeContext, + invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let new_owner_pubkey: Pubkey = limited_deserialize(data)?; - let keyed_accounts_iter = &mut keyed_accounts.iter(); - let account_keyed_account = &mut next_keyed_account(keyed_accounts_iter)?; + let account_keyed_account = &mut keyed_account_at_index(keyed_accounts, 0)?; let mut account_owner_pubkey: Pubkey = limited_deserialize(&account_keyed_account.try_account_ref()?.data())?; if account_owner_pubkey == Pubkey::default() { account_owner_pubkey = new_owner_pubkey; } else { - let owner_keyed_account = &mut next_keyed_account(keyed_accounts_iter)?; + let owner_keyed_account = &mut keyed_account_at_index(keyed_accounts, 1)?; set_owner( &mut account_owner_pubkey, new_owner_pubkey, diff --git a/programs/secp256k1/src/lib.rs b/programs/secp256k1/src/lib.rs index 9be513ea54..ebb29bd801 100644 --- a/programs/secp256k1/src/lib.rs +++ b/programs/secp256k1/src/lib.rs @@ -1,11 +1,9 @@ use solana_sdk::{ - instruction::InstructionError, keyed_account::KeyedAccount, process_instruction::InvokeContext, - pubkey::Pubkey, + instruction::InstructionError, process_instruction::InvokeContext, pubkey::Pubkey, }; pub fn process_instruction( _program_id: &Pubkey, - _keyed_accounts: &[KeyedAccount], _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 722994ddb2..40a6ce2731 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -10,7 +10,7 @@ use solana_sdk::{ decode_error::DecodeError, feature_set, instruction::{AccountMeta, Instruction, InstructionError}, - keyed_account::{from_keyed_account, get_signers, next_keyed_account, KeyedAccount}, + keyed_account::{from_keyed_account, get_signers, keyed_account_at_index}, process_instruction::InvokeContext, program_utils::limited_deserialize, pubkey::Pubkey, @@ -483,17 +483,17 @@ pub fn set_lockup( pub fn process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { + let keyed_accounts = invoke_context.get_keyed_accounts()?; + trace!("process_instruction: {:?}", data); trace!("keyed_accounts: {:?}", keyed_accounts); let signers = get_signers(keyed_accounts); - let keyed_accounts = &mut keyed_accounts.iter(); - let me = &next_keyed_account(keyed_accounts)?; + let me = &keyed_account_at_index(keyed_accounts, 0)?; if me.owner()? != id() { if invoke_context.is_feature_active(&feature_set::check_program_owner::id()) { @@ -507,7 +507,7 @@ pub fn process_instruction( StakeInstruction::Initialize(authorized, lockup) => me.initialize( &authorized, &lockup, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, + &from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?, ), StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => { let require_custodian_for_locked_stake_authorize = invoke_context.is_feature_active( @@ -515,9 +515,11 @@ pub fn process_instruction( ); if require_custodian_for_locked_stake_authorize { - let clock = from_keyed_account::(next_keyed_account(keyed_accounts)?)?; - let _current_authority = next_keyed_account(keyed_accounts)?; - let custodian = keyed_accounts.next().map(|ka| ka.unsigned_key()); + 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).map(|ka| ka.unsigned_key()); me.authorize( &signers, @@ -525,7 +527,7 @@ pub fn process_instruction( stake_authorize, require_custodian_for_locked_stake_authorize, &clock, - custodian, + custodian.ok(), ) } else { me.authorize( @@ -539,14 +541,16 @@ pub fn process_instruction( } } StakeInstruction::AuthorizeWithSeed(args) => { - let authority_base = next_keyed_account(keyed_accounts)?; + let authority_base = keyed_account_at_index(keyed_accounts, 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::(next_keyed_account(keyed_accounts)?)?; - let custodian = keyed_accounts.next().map(|ka| ka.unsigned_key()); + let clock = + from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?; + let custodian = + keyed_account_at_index(keyed_accounts, 3).map(|ka| ka.unsigned_key()); me.authorize_with_seed( &authority_base, @@ -556,7 +560,7 @@ pub fn process_instruction( args.stake_authorize, require_custodian_for_locked_stake_authorize, &clock, - custodian, + custodian.ok(), ) } else { me.authorize_with_seed( @@ -572,44 +576,44 @@ pub fn process_instruction( } } StakeInstruction::DelegateStake => { - let vote = next_keyed_account(keyed_accounts)?; + let vote = keyed_account_at_index(keyed_accounts, 1)?; me.delegate( &vote, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, - &config::from_keyed_account(next_keyed_account(keyed_accounts)?)?, + &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)?)?, &signers, ) } StakeInstruction::Split(lamports) => { - let split_stake = &next_keyed_account(keyed_accounts)?; + let split_stake = &keyed_account_at_index(keyed_accounts, 1)?; me.split(lamports, split_stake, &signers) } StakeInstruction::Merge => { - let source_stake = &next_keyed_account(keyed_accounts)?; + let source_stake = &keyed_account_at_index(keyed_accounts, 1)?; me.merge( invoke_context, source_stake, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, + &from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?, + &from_keyed_account::(keyed_account_at_index(keyed_accounts, 3)?)?, &signers, ) } StakeInstruction::Withdraw(lamports) => { - let to = &next_keyed_account(keyed_accounts)?; + let to = &keyed_account_at_index(keyed_accounts, 1)?; me.withdraw( lamports, to, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, - next_keyed_account(keyed_accounts)?, - keyed_accounts.next(), + &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(), ) } StakeInstruction::Deactivate => me.deactivate( - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, + &from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?, &signers, ), @@ -623,6 +627,7 @@ mod tests { use bincode::serialize; use solana_sdk::{ account::{self, Account, AccountSharedData}, + keyed_account::KeyedAccount, process_instruction::MockInvokeContext, rent::Rent, sysvar::stake_history::StakeHistory, @@ -707,9 +712,8 @@ mod tests { .collect(); super::process_instruction( &Pubkey::default(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default(), + &mut MockInvokeContext::new(keyed_accounts), ) } } @@ -918,202 +922,194 @@ mod tests { assert_eq!( super::process_instruction( &Pubkey::default(), - &[], &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() )) .unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(vec![]) ), Err(InstructionError::NotEnoughAccountKeys), ); // 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)]; assert_eq!( super::process_instruction( &Pubkey::default(), - &[KeyedAccount::new( - &Pubkey::default(), - false, - &create_default_stake_account(), - )], &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() )) .unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::NotEnoughAccountKeys), ); // rent fails to deserialize + let stake_address = Pubkey::default(); + 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), + ]; assert_eq!( super::process_instruction( &Pubkey::default(), - &[ - KeyedAccount::new(&Pubkey::default(), false, &create_default_stake_account()), - KeyedAccount::new(&sysvar::rent::id(), false, &create_default_account()) - ], &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() )) .unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::InvalidArgument), ); // fails to deserialize stake state + let stake_address = Pubkey::default(); + let stake_account = create_default_stake_account(); + let rent_address = sysvar::rent::id(); + 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), + ]; assert_eq!( super::process_instruction( &Pubkey::default(), - &[ - KeyedAccount::new(&Pubkey::default(), false, &create_default_stake_account()), - KeyedAccount::new( - &sysvar::rent::id(), - false, - &RefCell::new(account::create_account_shared_data_for_test( - &Rent::default() - )) - ) - ], &serialize(&StakeInstruction::Initialize( Authorized::default(), Lockup::default() )) .unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::InvalidAccountData), ); // 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)]; assert_eq!( super::process_instruction( &Pubkey::default(), - &[KeyedAccount::new( - &Pubkey::default(), - false, - &create_default_stake_account() - ),], &serialize(&StakeInstruction::DelegateStake).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::NotEnoughAccountKeys), ); // 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)]; assert_eq!( super::process_instruction( &Pubkey::default(), - &[KeyedAccount::new( - &Pubkey::default(), - false, - &create_default_stake_account() - )], &serialize(&StakeInstruction::DelegateStake).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::NotEnoughAccountKeys), ); // gets the check non-deserialize-able account in delegate_stake + let stake_address = Pubkey::default(); + let stake_account = create_default_stake_account(); + let vote_address = Pubkey::default(); let mut bad_vote_account = create_default_account(); bad_vote_account.get_mut().owner = solana_vote_program::id(); + let clock_address = sysvar::clock::id(); + let clock_account = RefCell::new(account::create_account_shared_data_for_test( + &sysvar::clock::Clock::default(), + )); + let stake_history_address = sysvar::stake_history::id(); + let stake_history_account = RefCell::new(account::create_account_shared_data_for_test( + &sysvar::stake_history::StakeHistory::default(), + )); + let config_address = config::id(); + let config_account = RefCell::new(config::create_account(0, &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), + ]; assert_eq!( super::process_instruction( &Pubkey::default(), - &[ - KeyedAccount::new(&Pubkey::default(), true, &create_default_stake_account()), - KeyedAccount::new(&Pubkey::default(), false, &bad_vote_account), - KeyedAccount::new( - &sysvar::clock::id(), - false, - &RefCell::new(account::create_account_shared_data_for_test( - &sysvar::clock::Clock::default(), - )) - ), - KeyedAccount::new( - &sysvar::stake_history::id(), - false, - &RefCell::new(account::create_account_shared_data_for_test( - &sysvar::stake_history::StakeHistory::default(), - )) - ), - KeyedAccount::new( - &config::id(), - false, - &RefCell::new(config::create_account(0, &config::Config::default())) - ), - ], &serialize(&StakeInstruction::DelegateStake).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::InvalidAccountData), ); // Tests 3rd keyed account is of correct type (Clock instead of rewards) in withdraw + let stake_address = Pubkey::default(); + let stake_account = create_default_stake_account(); + let vote_address = Pubkey::default(); + let vote_account = create_default_account(); + let rewards_address = sysvar::rewards::id(); + let rewards_account = RefCell::new(account::create_account_shared_data_for_test( + &sysvar::rewards::Rewards::new(0.0), + )); + let stake_history_address = sysvar::stake_history::id(); + 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), + ]; assert_eq!( super::process_instruction( &Pubkey::default(), - &[ - KeyedAccount::new(&Pubkey::default(), false, &create_default_stake_account()), - KeyedAccount::new(&Pubkey::default(), false, &create_default_account()), - KeyedAccount::new( - &sysvar::rewards::id(), - false, - &RefCell::new(account::create_account_shared_data_for_test( - &sysvar::rewards::Rewards::new(0.0), - )) - ), - KeyedAccount::new( - &sysvar::stake_history::id(), - false, - &RefCell::new(account::create_account_shared_data_for_test( - &StakeHistory::default(), - )) - ), - ], &serialize(&StakeInstruction::Withdraw(42)).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::InvalidArgument), ); // 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)]; assert_eq!( super::process_instruction( &Pubkey::default(), - &[KeyedAccount::new( - &Pubkey::default(), - false, - &create_default_stake_account() - )], &serialize(&StakeInstruction::Withdraw(42)).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::NotEnoughAccountKeys), ); // Tests 2nd keyed account is of correct type (Clock instead of rewards) in deactivate + let stake_address = Pubkey::default(); + let stake_account = create_default_stake_account(); + let rewards_address = sysvar::rewards::id(); + 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), + ]; assert_eq!( super::process_instruction( &Pubkey::default(), - &[ - KeyedAccount::new(&Pubkey::default(), false, &create_default_stake_account()), - KeyedAccount::new( - &sysvar::rewards::id(), - false, - &RefCell::new(account::create_account_shared_data_for_test( - &sysvar::rewards::Rewards::new(0.0), - )) - ), - ], &serialize(&StakeInstruction::Deactivate).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(keyed_accounts) ), Err(InstructionError::InvalidArgument), ); @@ -1122,9 +1118,8 @@ mod tests { assert_eq!( super::process_instruction( &Pubkey::default(), - &[], &serialize(&StakeInstruction::Deactivate).unwrap(), - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(vec![]) ), Err(InstructionError::NotEnoughAccountKeys), ); diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index bb54eb9d13..6280848500 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -5103,9 +5103,9 @@ mod tests { let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); let stake_lamports = 42; - let invoke_context = MockInvokeContext::default(); let signers = vec![authorized_pubkey].into_iter().collect(); + let invoke_context = MockInvokeContext::new(vec![]); for state in &[ StakeState::Initialized(Meta::auto(&authorized_pubkey)), @@ -5213,7 +5213,7 @@ mod tests { #[test] fn test_merge_self_fails() { - let invoke_context = MockInvokeContext::default(); + let invoke_context = MockInvokeContext::new(vec![]); let stake_address = Pubkey::new_unique(); let authority_pubkey = Pubkey::new_unique(); let signers = HashSet::from_iter(vec![authority_pubkey]); @@ -5257,7 +5257,6 @@ mod tests { #[test] fn test_merge_incorrect_authorized_staker() { - let invoke_context = MockInvokeContext::default(); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); @@ -5266,6 +5265,7 @@ mod tests { let signers = vec![authorized_pubkey].into_iter().collect(); let wrong_signers = vec![wrong_authorized_pubkey].into_iter().collect(); + let invoke_context = MockInvokeContext::new(vec![]); for state in &[ StakeState::Initialized(Meta::auto(&authorized_pubkey)), @@ -5327,12 +5327,12 @@ mod tests { #[test] fn test_merge_invalid_account_data() { - let invoke_context = MockInvokeContext::default(); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); let stake_lamports = 42; let signers = vec![authorized_pubkey].into_iter().collect(); + let invoke_context = MockInvokeContext::new(vec![]); for state in &[ StakeState::Uninitialized, @@ -5379,7 +5379,6 @@ mod tests { #[test] fn test_merge_fake_stake_source() { - let invoke_context = MockInvokeContext::default(); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); @@ -5411,6 +5410,7 @@ mod tests { .expect("source_stake_account"); let source_stake_keyed_account = KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account); + let invoke_context = MockInvokeContext::new(vec![]); assert_eq!( stake_keyed_account.merge( @@ -5426,7 +5426,6 @@ mod tests { #[test] fn test_merge_active_stake() { - let invoke_context = MockInvokeContext::default(); let base_lamports = 4242424242; let stake_address = Pubkey::new_unique(); let source_address = Pubkey::new_unique(); @@ -5480,6 +5479,7 @@ mod tests { let mut clock = Clock::default(); let mut stake_history = StakeHistory::default(); + let invoke_context = MockInvokeContext::new(vec![]); clock.epoch = 0; let mut effective = base_lamports; @@ -6144,7 +6144,6 @@ mod tests { #[test] fn test_things_can_merge() { - let invoke_context = MockInvokeContext::default(); let good_stake = Stake { credits_observed: 4242, delegation: Delegation { @@ -6154,6 +6153,7 @@ mod tests { ..Delegation::default() }, }; + let invoke_context = MockInvokeContext::new(vec![]); let identical = good_stake; assert!( @@ -6314,7 +6314,6 @@ mod tests { #[test] fn test_merge_kind_get_if_mergeable() { - let invoke_context = MockInvokeContext::default(); let authority_pubkey = Pubkey::new_unique(); let initial_lamports = 4242424242; let rent = Rent::default(); @@ -6333,9 +6332,9 @@ mod tests { ) .expect("stake_account"); let stake_keyed_account = KeyedAccount::new(&authority_pubkey, true, &stake_account); - let mut clock = Clock::default(); let mut stake_history = StakeHistory::default(); + let invoke_context = MockInvokeContext::new(vec![]); // Uninitialized state fails assert_eq!( @@ -6547,7 +6546,6 @@ mod tests { #[test] fn test_merge_kind_merge() { - let invoke_context = MockInvokeContext::default(); let lamports = 424242; let meta = Meta { rent_exempt_reserve: 42, @@ -6563,6 +6561,7 @@ mod tests { let inactive = MergeKind::Inactive(Meta::default(), lamports); let activation_epoch = MergeKind::ActivationEpoch(meta, stake); let fully_active = MergeKind::FullyActive(meta, stake); + let invoke_context = MockInvokeContext::new(vec![]); assert_eq!( inactive diff --git a/programs/vest/src/vest_processor.rs b/programs/vest/src/vest_processor.rs index 3dd5d82cba..6adc653652 100644 --- a/programs/vest/src/vest_processor.rs +++ b/programs/vest/src/vest_processor.rs @@ -10,7 +10,7 @@ use solana_sdk::{ account::{AccountSharedData, ReadableAccount, WritableAccount}, feature_set, instruction::InstructionError, - keyed_account::{next_keyed_account, KeyedAccount}, + keyed_account::{keyed_account_at_index, KeyedAccount}, process_instruction::InvokeContext, program_utils::limited_deserialize, pubkey::Pubkey, @@ -59,12 +59,12 @@ fn verify_signed_account<'a>( pub fn process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - let keyed_accounts_iter = &mut keyed_accounts.iter(); - let contract_account = &mut next_keyed_account(keyed_accounts_iter)?.try_account_ref_mut()?; + let keyed_accounts = invoke_context.get_keyed_accounts()?; + + let contract_account = &mut keyed_account_at_index(keyed_accounts, 0)?.try_account_ref_mut()?; if invoke_context.is_feature_active(&feature_set::check_program_owner::id()) && contract_account.owner != crate::id() { @@ -97,25 +97,25 @@ pub fn process_instruction( VestInstruction::InitializeAccount { .. } => {} VestInstruction::SetTerminator(new_pubkey) => { verify_signed_account( - next_keyed_account(keyed_accounts_iter)?, + keyed_account_at_index(keyed_accounts, 1)?, &vest_state.terminator_pubkey, )?; vest_state.terminator_pubkey = new_pubkey; } VestInstruction::SetPayee(new_pubkey) => { verify_signed_account( - next_keyed_account(keyed_accounts_iter)?, + keyed_account_at_index(keyed_accounts, 1)?, &vest_state.payee_pubkey, )?; vest_state.payee_pubkey = new_pubkey; } VestInstruction::RedeemTokens => { let current_date = verify_date_account( - next_keyed_account(keyed_accounts_iter)?, + keyed_account_at_index(keyed_accounts, 1)?, &vest_state.date_pubkey, )?; let mut payee_account = verify_account( - next_keyed_account(keyed_accounts_iter)?, + keyed_account_at_index(keyed_accounts, 2)?, &vest_state.payee_pubkey, )?; vest_state.redeem_tokens(contract_account, current_date, &mut payee_account); @@ -127,11 +127,11 @@ pub fn process_instruction( contract_account.lamports }; let terminator_account = verify_signed_account( - next_keyed_account(keyed_accounts_iter)?, + keyed_account_at_index(keyed_accounts, 1)?, &vest_state.terminator_pubkey, )?; - let payee_keyed_account = keyed_accounts_iter.next(); - let mut payee_account = if let Some(payee_keyed_account) = payee_keyed_account { + let payee_keyed_account = keyed_account_at_index(keyed_accounts, 2); + let mut payee_account = if let Ok(payee_keyed_account) = payee_keyed_account { payee_keyed_account.try_account_ref_mut()? } else { terminator_account @@ -140,7 +140,7 @@ pub fn process_instruction( } VestInstruction::VestAll => { verify_signed_account( - next_keyed_account(keyed_accounts_iter)?, + keyed_account_at_index(keyed_accounts, 1)?, &vest_state.terminator_pubkey, )?; vest_state.vest_all(); diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index 402a19c067..92ce7d9812 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -14,7 +14,7 @@ use solana_sdk::{ feature_set, hash::Hash, instruction::{AccountMeta, Instruction, InstructionError}, - keyed_account::{from_keyed_account, get_signers, next_keyed_account, KeyedAccount}, + keyed_account::{from_keyed_account, get_signers, keyed_account_at_index, KeyedAccount}, process_instruction::InvokeContext, program_utils::limited_deserialize, pubkey::Pubkey, @@ -277,17 +277,17 @@ fn verify_rent_exemption( pub fn process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { + let keyed_accounts = invoke_context.get_keyed_accounts()?; + trace!("process_instruction: {:?}", data); trace!("keyed_accounts: {:?}", keyed_accounts); let signers: HashSet = get_signers(keyed_accounts); - let keyed_accounts = &mut keyed_accounts.iter(); - let me = &mut next_keyed_account(keyed_accounts)?; + let me = &mut keyed_account_at_index(keyed_accounts, 0)?; if invoke_context.is_feature_active(&feature_set::check_program_owner::id()) && me.owner()? != id() @@ -297,12 +297,12 @@ pub fn process_instruction( match limited_deserialize(data)? { VoteInstruction::InitializeAccount(vote_init) => { - verify_rent_exemption(me, next_keyed_account(keyed_accounts)?)?; + verify_rent_exemption(me, keyed_account_at_index(keyed_accounts, 1)?)?; vote_state::initialize_account( me, &vote_init, &signers, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, + &from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?, invoke_context.is_feature_active(&feature_set::check_init_vote_data::id()), ) } @@ -311,11 +311,11 @@ pub fn process_instruction( &voter_pubkey, vote_authorize, &signers, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, + &from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?, ), VoteInstruction::UpdateValidatorIdentity => vote_state::update_validator_identity( me, - next_keyed_account(keyed_accounts)?.unsigned_key(), + keyed_account_at_index(keyed_accounts, 1)?.unsigned_key(), &signers, ), VoteInstruction::UpdateCommission(commission) => { @@ -325,14 +325,14 @@ pub fn process_instruction( inc_new_counter_info!("vote-native", 1); vote_state::process_vote( me, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, - &from_keyed_account::(next_keyed_account(keyed_accounts)?)?, + &from_keyed_account::(keyed_account_at_index(keyed_accounts, 1)?)?, + &from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?, &vote, &signers, ) } VoteInstruction::Withdraw(lamports) => { - let to = next_keyed_account(keyed_accounts)?; + let to = keyed_account_at_index(keyed_accounts, 1)?; vote_state::withdraw(me, lamports, to, &signers) } } @@ -356,8 +356,7 @@ mod tests { super::process_instruction( &Pubkey::default(), &[], - &[], - &mut MockInvokeContext::default() + &mut MockInvokeContext::new(vec![]) ), Err(InstructionError::NotEnoughAccountKeys), ); @@ -401,9 +400,8 @@ mod tests { .collect(); super::process_instruction( &Pubkey::default(), - &keyed_accounts, &instruction.data, - &mut MockInvokeContext::default(), + &mut MockInvokeContext::new(keyed_accounts), ) } } diff --git a/programs/vote/src/vote_state/mod.rs b/programs/vote/src/vote_state/mod.rs index ae64453028..cb21e424a1 100644 --- a/programs/vote/src/vote_state/mod.rs +++ b/programs/vote/src/vote_state/mod.rs @@ -788,7 +788,7 @@ mod tests { account::AccountSharedData, account_utils::StateMut, hash::hash, - keyed_account::{get_signers, next_keyed_account}, + keyed_account::{get_signers, keyed_account_at_index}, }; use std::cell::RefCell; @@ -1715,9 +1715,8 @@ mod tests { KeyedAccount::new(&authorized_withdrawer_pubkey, true, &withdrawer_account), ]; let signers: HashSet = get_signers(keyed_accounts); - let keyed_accounts = &mut keyed_accounts.iter(); - let vote_keyed_account = next_keyed_account(keyed_accounts).unwrap(); - let withdrawer_keyed_account = next_keyed_account(keyed_accounts).unwrap(); + let vote_keyed_account = keyed_account_at_index(keyed_accounts, 0).unwrap(); + let withdrawer_keyed_account = keyed_account_at_index(keyed_accounts, 1).unwrap(); let res = withdraw( vote_keyed_account, lamports, diff --git a/runtime/benches/bank.rs b/runtime/benches/bank.rs index 6fd9aa445b..e739050d0c 100644 --- a/runtime/benches/bank.rs +++ b/runtime/benches/bank.rs @@ -11,7 +11,6 @@ use solana_sdk::{ clock::MAX_RECENT_BLOCKHASHES, genesis_config::create_genesis_config, instruction::InstructionError, - keyed_account::KeyedAccount, message::Message, process_instruction::InvokeContext, pubkey::Pubkey, @@ -34,7 +33,6 @@ const NOOP_PROGRAM_ID: [u8; 32] = [ #[allow(clippy::unnecessary_wraps)] fn process_instruction( _program_id: &Pubkey, - _keyed_accounts: &[KeyedAccount], _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index b8854bfbf2..0616d55b04 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -208,7 +208,7 @@ impl AbiExample for Builtin { Self { name: String::default(), id: Pubkey::default(), - process_instruction_with_context: |_, _, _, _| Ok(()), + process_instruction_with_context: |_, _, _| Ok(()), } } } @@ -5071,7 +5071,6 @@ pub(crate) mod tests { feature::Feature, genesis_config::create_genesis_config, instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, - keyed_account::KeyedAccount, message::{Message, MessageHeader}, nonce, poh_config::PohConfig, @@ -5430,10 +5429,10 @@ pub(crate) mod tests { fn mock_process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], - _invoke_context: &mut dyn InvokeContext, + invoke_context: &mut dyn InvokeContext, ) -> result::Result<(), InstructionError> { + let keyed_accounts = invoke_context.get_keyed_accounts()?; if let Ok(instruction) = bincode::deserialize(data) { match instruction { MockInstruction::Deduction => { @@ -9007,7 +9006,6 @@ pub(crate) mod tests { } fn mock_vote_processor( program_id: &Pubkey, - _keyed_accounts: &[KeyedAccount], _instruction_data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -9065,7 +9063,6 @@ pub(crate) mod tests { fn mock_vote_processor( _pubkey: &Pubkey, - _ka: &[KeyedAccount], _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -9116,7 +9113,6 @@ pub(crate) mod tests { fn mock_ix_processor( _pubkey: &Pubkey, - _ka: &[KeyedAccount], _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -9850,10 +9846,10 @@ pub(crate) mod tests { fn mock_process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], - _invoke_context: &mut dyn InvokeContext, + 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()?; @@ -9904,7 +9900,6 @@ pub(crate) mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_process_instruction( _program_id: &Pubkey, - _keyed_accounts: &[KeyedAccount], _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> result::Result<(), InstructionError> { @@ -10091,7 +10086,6 @@ pub(crate) mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_ok_vote_processor( _pubkey: &Pubkey, - _ka: &[KeyedAccount], _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -10342,10 +10336,10 @@ pub(crate) mod tests { fn test_same_program_id_uses_unqiue_executable_accounts() { fn nested_processor( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], _data: &[u8], - _invoke_context: &mut dyn InvokeContext, + 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()?; account.lamports += 1; @@ -10622,7 +10616,6 @@ pub(crate) mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_ix_processor( _pubkey: &Pubkey, - _ka: &[KeyedAccount], _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -10668,7 +10661,6 @@ pub(crate) mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_ix_processor( _pubkey: &Pubkey, - _ka: &[KeyedAccount], _data: &[u8], _context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { @@ -10962,7 +10954,6 @@ pub(crate) mod tests { &self, _loader_id: &Pubkey, _program_id: &Pubkey, - _keyed_accounts: &[KeyedAccount], _instruction_data: &[u8], _invoke_context: &mut dyn InvokeContext, _use_jit: bool, @@ -12402,12 +12393,12 @@ pub(crate) mod tests { fn mock_ix_processor( _pubkey: &Pubkey, - ka: &[KeyedAccount], _data: &[u8], - _invoke_context: &mut dyn InvokeContext, + invoke_context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { use solana_sdk::account::WritableAccount; - let mut data = ka[1].try_account_ref_mut()?; + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let mut data = keyed_accounts[1].try_account_ref_mut()?; data.data_as_mut_slice()[0] = 5; Ok(()) } diff --git a/runtime/src/builtins.rs b/runtime/src/builtins.rs index 03a9dfc7ec..899ad9a04e 100644 --- a/runtime/src/builtins.rs +++ b/runtime/src/builtins.rs @@ -5,7 +5,6 @@ use crate::{ use solana_sdk::{ feature_set, instruction::InstructionError, - keyed_account::KeyedAccount, process_instruction::{stable_log, InvokeContext, ProcessInstructionWithContext}, pubkey::Pubkey, system_program, @@ -14,14 +13,13 @@ use solana_sdk::{ fn process_instruction_with_program_logging( process_instruction: ProcessInstructionWithContext, program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { let logger = invoke_context.get_logger(); stable_log::program_invoke(&logger, program_id, invoke_context.invoke_depth()); - let result = process_instruction(program_id, keyed_accounts, instruction_data, invoke_context); + let result = process_instruction(program_id, instruction_data, invoke_context); match &result { Ok(()) => stable_log::program_success(&logger, program_id), @@ -32,14 +30,10 @@ fn process_instruction_with_program_logging( macro_rules! with_program_logging { ($process_instruction:expr) => { - |program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], - instruction_data: &[u8], - invoke_context: &mut dyn InvokeContext| { + |program_id: &Pubkey, instruction_data: &[u8], invoke_context: &mut dyn InvokeContext| { process_instruction_with_program_logging( $process_instruction, program_id, - keyed_accounts, instruction_data, invoke_context, ) diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index ed9c45498c..8f774cfa78 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -14,11 +14,11 @@ use solana_sdk::{ }, ic_msg, instruction::{CompiledInstruction, Instruction, InstructionError}, - keyed_account::{create_keyed_readonly_accounts, KeyedAccount}, + keyed_account::{create_keyed_accounts_unified, keyed_account_at_index, KeyedAccount}, message::Message, native_loader, process_instruction::{ - BpfComputeBudget, ComputeMeter, Executor, InvokeContext, Logger, + BpfComputeBudget, ComputeMeter, Executor, InvokeContext, InvokeContextStackFrame, Logger, ProcessInstructionWithContext, }, pubkey::Pubkey, @@ -215,8 +215,8 @@ impl PreAccount { self.changed = true; } - pub fn key(&self) -> Pubkey { - self.key + pub fn key(&self) -> &Pubkey { + &self.key } pub fn lamports(&self) -> u64 { @@ -250,10 +250,11 @@ impl ComputeMeter for ThisComputeMeter { } } pub struct ThisInvokeContext<'a> { - program_ids: Vec, + invoke_stack: Vec>, rent: Rent, + message: &'a Message, pre_accounts: Vec, - executables: &'a [(Pubkey, Rc>)], + executable_accounts: &'a [(Pubkey, Rc>)], account_deps: &'a [(Pubkey, Rc>)], programs: &'a [(Pubkey, ProcessInstructionWithContext)], logger: Rc>, @@ -272,8 +273,10 @@ impl<'a> ThisInvokeContext<'a> { pub fn new( program_id: &Pubkey, rent: Rent, - pre_accounts: Vec, - executables: &'a [(Pubkey, Rc>)], + message: &'a Message, + instruction: &'a CompiledInstruction, + executable_accounts: &'a [(Pubkey, Rc>)], + accounts: &'a [Rc>], account_deps: &'a [(Pubkey, Rc>)], programs: &'a [(Pubkey, ProcessInstructionWithContext)], log_collector: Option>, @@ -284,13 +287,20 @@ impl<'a> ThisInvokeContext<'a> { account_db: Arc, ancestors: &'a Ancestors, ) -> Self { - let mut program_ids = Vec::with_capacity(bpf_compute_budget.max_invoke_depth); - program_ids.push(*program_id); - Self { - program_ids, + let pre_accounts = MessageProcessor::create_pre_accounts(message, instruction, accounts); + let keyed_accounts = MessageProcessor::create_keyed_accounts( + message, + instruction, + executable_accounts, + accounts, + feature_set.is_active(&demote_sysvar_write_locks::id()), + ); + let mut invoke_context = Self { + invoke_stack: Vec::with_capacity(bpf_compute_budget.max_invoke_depth), rent, + message, pre_accounts, - executables, + executable_accounts, account_deps, programs, logger: Rc::new(RefCell::new(ThisLogger { log_collector })), @@ -305,26 +315,80 @@ impl<'a> ThisInvokeContext<'a> { account_db, ancestors, sysvars: vec![], - } + }; + invoke_context + .invoke_stack + .push(InvokeContextStackFrame::new( + *program_id, + create_keyed_accounts_unified(&keyed_accounts), + )); + invoke_context } } impl<'a> InvokeContext for ThisInvokeContext<'a> { - fn push(&mut self, key: &Pubkey) -> Result<(), InstructionError> { - if self.program_ids.len() > self.bpf_compute_budget.max_invoke_depth { + fn push( + &mut self, + key: &Pubkey, + keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + ) -> Result<(), InstructionError> { + if self.invoke_stack.len() > self.bpf_compute_budget.max_invoke_depth { return Err(InstructionError::CallDepth); } - if self.program_ids.contains(key) && self.program_ids.last() != Some(key) { + let frame_index = self.invoke_stack.iter().position(|frame| frame.key == *key); + if frame_index != None && frame_index != Some(self.invoke_stack.len().saturating_sub(1)) { // Reentrancy not allowed unless caller is calling itself return Err(InstructionError::ReentrancyNotAllowed); } - self.program_ids.push(*key); + // Alias the keys and account references in the provided keyed_accounts + // with the ones already existing in self, so that the lifetime 'a matches. + fn transmute_lifetime<'a, 'b, T: Sized>(value: &'a T) -> &'b T { + unsafe { std::mem::transmute(value) } + } + let keyed_accounts = keyed_accounts + .iter() + .map(|(is_signer, is_writable, search_key, account)| { + self.account_deps + .iter() + .map(|(key, _account)| key) + .chain(self.message.account_keys.iter()) + .position(|key| key == *search_key) + .map(|mut index| { + if index < self.account_deps.len() { + ( + *is_signer, + *is_writable, + &self.account_deps[index].0, + &self.account_deps[index].1 as &RefCell, + ) + } else { + index = index.saturating_sub(self.account_deps.len()); + ( + *is_signer, + *is_writable, + &self.message.account_keys[index], + // TODO + // Currently we are constructing new accounts on the stack + // before calling MessageProcessor::process_cross_program_instruction + // Ideally we would recycle the existing accounts here like this: + // &self.accounts[index] as &RefCell, + transmute_lifetime(*account), + ) + } + }) + }) + .collect::>>() + .ok_or(InstructionError::InvalidArgument)?; + self.invoke_stack.push(InvokeContextStackFrame::new( + *key, + create_keyed_accounts_unified(keyed_accounts.as_slice()), + )); Ok(()) } fn pop(&mut self) { - self.program_ids.pop(); + self.invoke_stack.pop(); } fn invoke_depth(&self) -> usize { - self.program_ids.len() + self.invoke_stack.len() } fn verify_and_update( &mut self, @@ -333,25 +397,42 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { accounts: &[Rc>], caller_write_privileges: Option<&[bool]>, ) -> Result<(), InstructionError> { - match self.program_ids.last() { - Some(program_id) => MessageProcessor::verify_and_update( - message, - instruction, - &mut self.pre_accounts, - accounts, - program_id, - &self.rent, - caller_write_privileges, - &mut self.timings, - self.feature_set.is_active(&demote_sysvar_write_locks::id()), - ), - None => Err(InstructionError::GenericError), // Should never happen - } + let stack_frame = self + .invoke_stack + .last() + .ok_or(InstructionError::CallDepth)?; + MessageProcessor::verify_and_update( + message, + instruction, + &mut self.pre_accounts, + accounts, + &stack_frame.key, + &self.rent, + caller_write_privileges, + &mut self.timings, + self.feature_set.is_active(&demote_sysvar_write_locks::id()), + ) } fn get_caller(&self) -> Result<&Pubkey, InstructionError> { - self.program_ids + self.invoke_stack .last() - .ok_or(InstructionError::GenericError) + .map(|frame| &frame.key) + .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); + Ok(()) + } + fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> { + self.invoke_stack + .last() + .map(|frame| &frame.keyed_accounts[frame.keyed_accounts_range.clone()]) + .ok_or(InstructionError::CallDepth) } fn get_programs(&self) -> &[(Pubkey, ProcessInstructionWithContext)] { self.programs @@ -381,7 +462,11 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { } fn get_account(&self, pubkey: &Pubkey) -> Option>> { if self.is_feature_active(&cpi_share_ro_and_exec_accounts::id()) { - if let Some((_, account)) = self.executables.iter().find(|(key, _)| key == pubkey) { + if let Some((_, account)) = self + .executable_accounts + .iter() + .find(|(key, _)| key == pubkey) + { Some(account.clone()) } else if let Some((_, account)) = self.account_deps.iter().find(|(key, _)| key == pubkey) @@ -476,7 +561,6 @@ impl std::fmt::Debug for MessageProcessor { // These are just type aliases for work around of Debug-ing above pointers type ErasedProcessInstructionWithContext = fn( &'static Pubkey, - &'static [KeyedAccount<'static>], &'static [u8], &'static mut dyn InvokeContext, ) -> Result<(), InstructionError>; @@ -554,25 +638,20 @@ impl MessageProcessor { executable_accounts: &'a [(Pubkey, Rc>)], accounts: &'a [Rc>], demote_sysvar_write_locks: bool, - ) -> Vec> { - let mut keyed_accounts = create_keyed_readonly_accounts(&executable_accounts); - let mut keyed_accounts2: Vec<_> = instruction - .accounts + ) -> Vec<(bool, bool, &'a Pubkey, &'a RefCell)> { + executable_accounts .iter() - .map(|&index| { - let is_signer = message.is_signer(index as usize); - let index = index as usize; - let key = &message.account_keys[index]; - let account = &accounts[index]; - if message.is_writable(index, demote_sysvar_write_locks) { - KeyedAccount::new(key, is_signer, account) - } else { - KeyedAccount::new_readonly(key, is_signer, account) - } - }) - .collect(); - keyed_accounts.append(&mut keyed_accounts2); - keyed_accounts + .map(|(key, account)| (false, false, key, account as &RefCell)) + .chain(instruction.accounts.iter().map(|index| { + let index = *index as usize; + ( + message.is_signer(index), + message.is_writable(index, demote_sysvar_write_locks), + &message.account_keys[index], + &accounts[index] as &RefCell, + ) + })) + .collect::>() } /// Process an instruction @@ -580,28 +659,22 @@ impl MessageProcessor { fn process_instruction( &self, program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - if let Some(root_account) = keyed_accounts.iter().next() { + if let Some(root_account) = invoke_context.get_keyed_accounts()?.iter().next() { let root_id = root_account.unsigned_key(); if 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, - &keyed_accounts[1..], - instruction_data, - invoke_context, - ); + return process_instruction(&program_id, instruction_data, invoke_context); } } // Call the program via the native loader return self.native_loader.process_instruction( &native_loader::id(), - keyed_accounts, instruction_data, invoke_context, ); @@ -610,12 +683,7 @@ impl MessageProcessor { for (id, process_instruction) in &self.programs { if id == owner_id { // Call the program via a builtin loader - return process_instruction( - &program_id, - keyed_accounts, - instruction_data, - invoke_context, - ); + return process_instruction(&program_id, instruction_data, invoke_context); } } } @@ -704,43 +772,44 @@ impl MessageProcessor { pub fn native_invoke( invoke_context: &mut dyn InvokeContext, instruction: Instruction, - keyed_accounts: &[&KeyedAccount], - signers_seeds: &[&[&[u8]]], + keyed_account_indices: &[usize], + signers: &[Pubkey], ) -> Result<(), InstructionError> { let invoke_context = RefCell::new(invoke_context); let ( message, - executables, + executable_accounts, accounts, - account_refs, + keyed_account_indices_reordered, caller_write_privileges, demote_sysvar_write_locks, ) = { let invoke_context = invoke_context.borrow(); - let caller_program_id = invoke_context.get_caller()?; - // Translate and verify caller's data - - let signers = signers_seeds + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let keyed_accounts = keyed_account_indices .iter() - .map(|seeds| Pubkey::create_program_address(&seeds, caller_program_id)) - .collect::, solana_sdk::pubkey::PubkeyError>>()?; - let mut caller_write_privileges = keyed_accounts - .iter() - .map(|keyed_account| keyed_account.is_writable()) - .collect::>(); - caller_write_privileges.insert(0, false); + .map(|index| keyed_account_at_index(keyed_accounts, *index)) + .collect::, InstructionError>>()?; let (message, callee_program_id, _) = Self::create_message(&instruction, &keyed_accounts, &signers, &invoke_context)?; + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let mut caller_write_privileges = keyed_account_indices + .iter() + .map(|index| keyed_accounts[*index].is_writable()) + .collect::>(); + caller_write_privileges.insert(0, false); let mut accounts = vec![]; - let mut account_refs = vec![]; + let mut keyed_account_indices_reordered = vec![]; + let keyed_accounts = invoke_context.get_keyed_accounts()?; 'root: for account_key in message.account_keys.iter() { - for keyed_account in keyed_accounts { + for keyed_account_index in keyed_account_indices { + let keyed_account = &keyed_accounts[*keyed_account_index]; if account_key == keyed_account.unsigned_key() { accounts.push(Rc::new(keyed_account.account.clone())); - account_refs.push(keyed_account); + keyed_account_indices_reordered.push(*keyed_account_index); continue 'root; } } @@ -771,42 +840,41 @@ impl MessageProcessor { ); return Err(InstructionError::AccountNotExecutable); } - let programdata_executable = - if program_account.borrow().owner == bpf_loader_upgradeable::id() { - if let UpgradeableLoaderState::Program { - programdata_address, - } = program_account.borrow().state()? - { - if let Some(account) = invoke_context.get_account(&programdata_address) { - Some((programdata_address, account)) - } else { - ic_msg!( - invoke_context, - "Unknown upgradeable programdata account {}", - programdata_address, - ); - return Err(InstructionError::MissingAccount); - } + let programdata = if program_account.borrow().owner == bpf_loader_upgradeable::id() { + if let UpgradeableLoaderState::Program { + programdata_address, + } = program_account.borrow().state()? + { + if let Some(account) = invoke_context.get_account(&programdata_address) { + Some((programdata_address, account)) } else { ic_msg!( invoke_context, - "Upgradeable program account state not valid {}", - callee_program_id, + "Unknown upgradeable programdata account {}", + programdata_address, ); return Err(InstructionError::MissingAccount); } } else { - None - }; - let mut executables = vec![(callee_program_id, program_account)]; - if let Some(programdata) = programdata_executable { - executables.push(programdata); + ic_msg!( + invoke_context, + "Upgradeable program account state not valid {}", + callee_program_id, + ); + return Err(InstructionError::MissingAccount); + } + } else { + None + }; + let mut executable_accounts = vec![(callee_program_id, program_account)]; + if let Some(programdata) = programdata { + executable_accounts.push(programdata); } ( message, - executables, + executable_accounts, accounts, - account_refs, + keyed_account_indices_reordered, caller_write_privileges, invoke_context.is_feature_active(&demote_sysvar_write_locks::id()), ) @@ -815,7 +883,7 @@ impl MessageProcessor { #[allow(clippy::deref_addrof)] MessageProcessor::process_cross_program_instruction( &message, - &executables, + &executable_accounts, &accounts, &caller_write_privileges, *(&mut *(invoke_context.borrow_mut())), @@ -825,13 +893,19 @@ impl MessageProcessor { { let invoke_context = invoke_context.borrow(); - for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() { - let account = account.borrow(); - if message.is_writable(i, demote_sysvar_write_locks) && !account.executable { - account_ref.try_account_ref_mut()?.lamports = account.lamports; - account_ref.try_account_ref_mut()?.owner = account.owner; - if account_ref.data_len()? != account.data().len() - && account_ref.data_len()? != 0 + let keyed_accounts = invoke_context.get_keyed_accounts()?; + for (src_keyed_account_index, (account, dst_keyed_account_index)) in accounts + .iter() + .zip(keyed_account_indices_reordered) + .enumerate() + { + let dst_keyed_account = &keyed_accounts[dst_keyed_account_index]; + let src_keyed_account = account.borrow(); + if message.is_writable(src_keyed_account_index, demote_sysvar_write_locks) + && !src_keyed_account.executable + { + if dst_keyed_account.data_len()? != src_keyed_account.data().len() + && dst_keyed_account.data_len()? != 0 { // Only support for `CreateAccount` at this time. // Need a way to limit total realloc size across multiple CPI calls @@ -841,9 +915,11 @@ impl MessageProcessor { ); return Err(InstructionError::InvalidRealloc); } - account_ref + dst_keyed_account.try_account_ref_mut()?.lamports = src_keyed_account.lamports; + dst_keyed_account.try_account_ref_mut()?.owner = src_keyed_account.owner; + dst_keyed_account .try_account_ref_mut()? - .set_data(account.data().clone()); + .set_data(src_keyed_account.data().clone()); } } } @@ -870,19 +946,18 @@ impl MessageProcessor { accounts, Some(caller_write_privileges), )?; - let demote_sysvar_write_locks = - invoke_context.is_feature_active(&demote_sysvar_write_locks::id()); + // Construct keyed accounts let keyed_accounts = Self::create_keyed_accounts( message, instruction, executable_accounts, accounts, - demote_sysvar_write_locks, + invoke_context.is_feature_active(&demote_sysvar_write_locks::id()), ); // Invoke callee - invoke_context.push(program_id)?; + invoke_context.push(program_id, &keyed_accounts)?; let mut message_processor = MessageProcessor::default(); for (program_id, process_instruction) in invoke_context.get_programs().iter() { @@ -891,7 +966,6 @@ impl MessageProcessor { let mut result = message_processor.process_instruction( program_id, - &keyed_accounts, &instruction.data, invoke_context, ); @@ -899,8 +973,9 @@ impl MessageProcessor { // Verify the called program has not misbehaved result = invoke_context.verify_and_update(message, instruction, accounts, None); } - invoke_context.pop(); + // Restore previous state + invoke_context.pop(); result } else { // This function is always called with a valid instruction, if that changes return an error @@ -1013,7 +1088,7 @@ impl MessageProcessor { }; // Find the matching PreAccount for pre_account in pre_accounts.iter_mut() { - if *key == pre_account.key() { + if key == pre_account.key() { { // Verify account has no outstanding references let _ = account @@ -1089,13 +1164,14 @@ impl MessageProcessor { } } - let pre_accounts = Self::create_pre_accounts(message, instruction, accounts); let program_id = instruction.program_id(&message.account_keys); let mut invoke_context = ThisInvokeContext::new( program_id, rent_collector.rent, - pre_accounts, + message, + instruction, executable_accounts, + accounts, account_deps, &self.programs, log_collector, @@ -1106,19 +1182,7 @@ impl MessageProcessor { account_db, ancestors, ); - let keyed_accounts = Self::create_keyed_accounts( - message, - instruction, - executable_accounts, - accounts, - demote_sysvar_write_locks, - ); - self.process_instruction( - program_id, - &keyed_accounts, - &instruction.data, - &mut invoke_context, - )?; + self.process_instruction(program_id, &instruction.data, &mut invoke_context)?; Self::verify( message, instruction, @@ -1198,31 +1262,41 @@ mod tests { #[test] fn test_invoke_context() { const MAX_DEPTH: usize = 10; - let mut program_ids = vec![]; + let mut invoke_stack = vec![]; let mut keys = vec![]; - let mut pre_accounts = vec![]; let mut accounts = vec![]; + let mut metas = vec![]; for i in 0..MAX_DEPTH { - program_ids.push(solana_sdk::pubkey::new_rand()); + invoke_stack.push(solana_sdk::pubkey::new_rand()); keys.push(solana_sdk::pubkey::new_rand()); accounts.push(Rc::new(RefCell::new(AccountSharedData::new( i as u64, 1, - &program_ids[i], + &invoke_stack[i], )))); - pre_accounts.push(PreAccount::new(&keys[i], &accounts[i].borrow())) + metas.push(AccountMeta::new(keys[i], false)); } - let account = AccountSharedData::new(1, 1, &solana_sdk::pubkey::Pubkey::default()); - for program_id in program_ids.iter() { - pre_accounts.push(PreAccount::new(program_id, &account.clone())); + for program_id in invoke_stack.iter() { + accounts.push(Rc::new(RefCell::new(AccountSharedData::new( + 1, + 1, + &solana_sdk::pubkey::Pubkey::default(), + )))); + metas.push(AccountMeta::new(*program_id, false)); } + let message = Message::new( + &[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)], + None, + ); let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new( - &program_ids[0], + &invoke_stack[0], Rent::default(), - pre_accounts, + &message, + &message.instructions[0], &[], + &accounts, &[], &[], None, @@ -1236,8 +1310,8 @@ mod tests { // Check call depth increases and has a limit let mut depth_reached = 1; - for program_id in program_ids.iter().skip(1) { - if Err(InstructionError::CallDepth) == invoke_context.push(program_id) { + for program_id in invoke_stack.iter().skip(1) { + if Err(InstructionError::CallDepth) == invoke_context.push(program_id, &[]) { break; } depth_reached += 1; @@ -1254,7 +1328,7 @@ mod tests { ]; let message = Message::new( &[Instruction::new_with_bytes( - program_ids[owned_index], + invoke_stack[owned_index], &[0], metas, )], @@ -1743,10 +1817,10 @@ mod tests { fn mock_system_process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], - _invoke_context: &mut dyn InvokeContext, + invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { + let keyed_accounts = invoke_context.get_keyed_accounts()?; if let Ok(instruction) = bincode::deserialize(data) { match instruction { MockSystemInstruction::Correct => Ok(()), @@ -1896,10 +1970,10 @@ mod tests { fn mock_system_process_instruction( _program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], - _invoke_context: &mut dyn InvokeContext, + invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { + let keyed_accounts = invoke_context.get_keyed_accounts()?; if let Ok(instruction) = bincode::deserialize(data) { match instruction { MockSystemInstruction::BorrowFail => { @@ -2075,10 +2149,10 @@ mod tests { fn mock_process_instruction( program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], data: &[u8], - _invoke_context: &mut dyn InvokeContext, + invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { + let keyed_accounts = invoke_context.get_keyed_accounts()?; assert_eq!(*program_id, keyed_accounts[0].owner()?); assert_ne!( keyed_accounts[1].owner()?, @@ -2107,7 +2181,6 @@ mod tests { let mut program_account = AccountSharedData::new(1, 0, &native_loader::id()); program_account.executable = true; - let executable_preaccount = PreAccount::new(&callee_program_id, &program_account); let executable_accounts = vec![( callee_program_id, Rc::new(RefCell::new(program_account.clone())), @@ -2115,11 +2188,9 @@ mod tests { let owned_key = solana_sdk::pubkey::new_rand(); let owned_account = AccountSharedData::new(42, 1, &callee_program_id); - let owned_preaccount = PreAccount::new(&owned_key, &owned_account); let not_owned_key = solana_sdk::pubkey::new_rand(); let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand()); - let not_owned_preaccount = PreAccount::new(¬_owned_key, ¬_owned_account); #[allow(unused_mut)] let mut accounts = vec![ @@ -2127,18 +2198,30 @@ mod tests { Rc::new(RefCell::new(not_owned_account)), Rc::new(RefCell::new(program_account)), ]; + + let compiled_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2]); let programs: Vec<(_, ProcessInstructionWithContext)> = vec![(callee_program_id, mock_process_instruction)]; + let metas = vec![ + AccountMeta::new(owned_key, false), + AccountMeta::new(not_owned_key, false), + ]; + + let instruction = Instruction::new_with_bincode( + callee_program_id, + &MockInstruction::NoopSuccess, + metas.clone(), + ); + let message = Message::new(&[instruction], None); + let ancestors = Ancestors::default(); let mut invoke_context = ThisInvokeContext::new( &caller_program_id, Rent::default(), - vec![ - owned_preaccount, - not_owned_preaccount, - executable_preaccount, - ], - &[], + &message, + &compiled_instruction, + &executable_accounts, + &accounts, &[], programs.as_slice(), None, @@ -2149,26 +2232,17 @@ mod tests { Arc::new(Accounts::default()), &ancestors, ); - let metas = vec![ - AccountMeta::new(owned_key, false), - AccountMeta::new(not_owned_key, false), - ]; // not owned account modified by the caller (before the invoke) - accounts[0].borrow_mut().data_as_mut_slice()[0] = 1; - let instruction = Instruction::new_with_bincode( - callee_program_id, - &MockInstruction::NoopSuccess, - metas.clone(), - ); - let demote_sysvar_write_locks = true; - let message = Message::new(&[instruction], None); + let demote_sysvar_write_locks = + invoke_context.is_feature_active(&demote_sysvar_write_locks::id()); let caller_write_privileges = message .account_keys .iter() .enumerate() .map(|(i, _)| message.is_writable(i, demote_sysvar_write_locks)) .collect::>(); + accounts[0].borrow_mut().data_as_mut_slice()[0] = 1; assert_eq!( MessageProcessor::process_cross_program_instruction( &message, @@ -2198,6 +2272,26 @@ mod tests { let instruction = Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone()); let message = Message::new(&[instruction], None); + + let ancestors = Ancestors::default(); + let mut invoke_context = ThisInvokeContext::new( + &caller_program_id, + Rent::default(), + &message, + &compiled_instruction, + &executable_accounts, + &accounts, + &[], + programs.as_slice(), + None, + BpfComputeBudget::default(), + Rc::new(RefCell::new(Executors::default())), + None, + Arc::new(FeatureSet::all_enabled()), + Arc::new(Accounts::default()), + &ancestors, + ); + let caller_write_privileges = message .account_keys .iter() @@ -2223,7 +2317,6 @@ mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_process_instruction( _program_id: &Pubkey, - _keyed_accounts: &[KeyedAccount], _data: &[u8], _invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { @@ -2232,7 +2325,6 @@ mod tests { #[allow(clippy::unnecessary_wraps)] fn mock_ix_processor( _pubkey: &Pubkey, - _ka: &[KeyedAccount], _data: &[u8], _context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { diff --git a/runtime/src/native_loader.rs b/runtime/src/native_loader.rs index b60b587708..9b810ef9d8 100644 --- a/runtime/src/native_loader.rs +++ b/runtime/src/native_loader.rs @@ -10,7 +10,7 @@ use solana_sdk::{ decode_error::DecodeError, entrypoint_native::ProgramEntrypoint, instruction::InstructionError, - keyed_account::{next_keyed_account, KeyedAccount}, + keyed_account::keyed_account_at_index, native_loader, process_instruction::{InvokeContext, LoaderEntrypoint}, pubkey::Pubkey, @@ -135,24 +135,30 @@ impl NativeLoader { pub fn process_instruction( &self, program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], - invoke_context: &dyn InvokeContext, + invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { - let mut keyed_accounts_iter = keyed_accounts.iter(); - let program = next_keyed_account(&mut keyed_accounts_iter)?; - if native_loader::id() != *program_id { - error!("Program id mismatch"); - return Err(InstructionError::IncorrectProgramId); - } - if program.owner()? != *program_id { - error!("Executable account now owned by loader"); - return Err(InstructionError::IncorrectProgramId); - } + let (program_id, name_vec) = { + let keyed_accounts = invoke_context.get_keyed_accounts()?; + let program = keyed_account_at_index(keyed_accounts, 0)?; + if native_loader::id() != *program_id { + error!("Program id mismatch"); + return Err(InstructionError::IncorrectProgramId); + } + if program.owner()? != *program_id { + error!("Executable account now owned by loader"); + return Err(InstructionError::IncorrectProgramId); + } + // TODO: Remove these two copies (* deref is also a copy) + // Both could be avoided as we know that the first KeyedAccount + // still exists even after invoke_context.remove_first_keyed_account() is called + ( + *program.unsigned_key(), + &program.try_account_ref()?.data().clone(), + ) + }; - let params = keyed_accounts_iter.as_slice(); - let account = program.try_account_ref()?; - let name = match str::from_utf8(account.data()) { + let name = match str::from_utf8(name_vec) { Ok(v) => v, Err(e) => { error!("Invalid UTF-8 sequence: {}", e); @@ -164,21 +170,21 @@ impl NativeLoader { return Err(NativeLoaderError::InvalidAccountData.into()); } trace!("Call native {:?}", name); + invoke_context.remove_first_keyed_account()?; if name.ends_with("loader_program") { let entrypoint = Self::get_entrypoint::(name, &self.loader_symbol_cache)?; - unsafe { - entrypoint( - program.unsigned_key(), - params, - instruction_data, - invoke_context, - ) - } + unsafe { entrypoint(&program_id, instruction_data, invoke_context) } } else { let entrypoint = Self::get_entrypoint::(name, &self.program_symbol_cache)?; - unsafe { entrypoint(program.unsigned_key(), params, instruction_data) } + unsafe { + entrypoint( + &program_id, + invoke_context.get_keyed_accounts()?, + instruction_data, + ) + } } } } diff --git a/runtime/src/system_instruction_processor.rs b/runtime/src/system_instruction_processor.rs index d336dfe836..1a91b2837b 100644 --- a/runtime/src/system_instruction_processor.rs +++ b/runtime/src/system_instruction_processor.rs @@ -4,7 +4,7 @@ use solana_sdk::{ account_utils::StateMut, ic_msg, instruction::InstructionError, - keyed_account::{from_keyed_account, get_signers, next_keyed_account, KeyedAccount}, + keyed_account::{from_keyed_account, get_signers, keyed_account_at_index, KeyedAccount}, nonce, nonce_keyed_account::NonceKeyedAccount, process_instruction::InvokeContext, @@ -35,7 +35,7 @@ impl Address { fn create( address: &Pubkey, with_seed: Option<(&Pubkey, &str, &Pubkey)>, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result { let base = if let Some((base, seed, owner)) = with_seed { let address_with_seed = Pubkey::create_with_seed(base, seed, owner)?; @@ -66,7 +66,7 @@ fn allocate( address: &Address, space: u64, signers: &HashSet, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { if !address.is_signer(signers) { ic_msg!( @@ -108,7 +108,7 @@ fn assign( address: &Address, owner: &Pubkey, signers: &HashSet, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { // no work to do, just return if account.owner == *owner { @@ -136,7 +136,7 @@ fn allocate_and_assign( space: u64, owner: &Pubkey, signers: &HashSet, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { allocate(to, to_address, space, signers, invoke_context)?; assign(to, to_address, owner, signers, invoke_context) @@ -150,7 +150,7 @@ fn create_account( space: u64, owner: &Pubkey, signers: &HashSet, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { // if it looks like the `to` account is already in use, bail { @@ -173,7 +173,7 @@ fn transfer_verified( from: &KeyedAccount, to: &KeyedAccount, lamports: u64, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { if !from.data_is_empty()? { ic_msg!(invoke_context, "Transfer: `from` must not carry data"); @@ -198,7 +198,7 @@ fn transfer( from: &KeyedAccount, to: &KeyedAccount, lamports: u64, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { if lamports == 0 { return Ok(()); @@ -223,7 +223,7 @@ fn transfer_with_seed( from_owner: &Pubkey, to: &KeyedAccount, lamports: u64, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { if lamports == 0 { return Ok(()); @@ -255,17 +255,16 @@ fn transfer_with_seed( pub fn process_instruction( _owner: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { + let keyed_accounts = invoke_context.get_keyed_accounts()?; let instruction = limited_deserialize(instruction_data)?; trace!("process_instruction: {:?}", instruction); trace!("keyed_accounts: {:?}", keyed_accounts); let signers = get_signers(keyed_accounts); - let keyed_accounts_iter = &mut keyed_accounts.iter(); match instruction { SystemInstruction::CreateAccount { @@ -273,8 +272,8 @@ pub fn process_instruction( space, owner, } => { - let from = next_keyed_account(keyed_accounts_iter)?; - let to = next_keyed_account(keyed_accounts_iter)?; + let from = keyed_account_at_index(keyed_accounts, 0)?; + let to = keyed_account_at_index(keyed_accounts, 1)?; let to_address = Address::create(to.unsigned_key(), None, invoke_context)?; create_account( from, @@ -294,8 +293,8 @@ pub fn process_instruction( space, owner, } => { - let from = next_keyed_account(keyed_accounts_iter)?; - let to = next_keyed_account(keyed_accounts_iter)?; + let from = keyed_account_at_index(keyed_accounts, 0)?; + let to = keyed_account_at_index(keyed_accounts, 1)?; let to_address = Address::create( &to.unsigned_key(), Some((&base, &seed, &owner)), @@ -313,14 +312,14 @@ pub fn process_instruction( ) } SystemInstruction::Assign { owner } => { - let keyed_account = next_keyed_account(keyed_accounts_iter)?; + let keyed_account = keyed_account_at_index(keyed_accounts, 0)?; 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 = next_keyed_account(keyed_accounts_iter)?; - let to = next_keyed_account(keyed_accounts_iter)?; + let from = keyed_account_at_index(keyed_accounts, 0)?; + let to = keyed_account_at_index(keyed_accounts, 1)?; transfer(from, to, lamports, invoke_context) } SystemInstruction::TransferWithSeed { @@ -328,9 +327,9 @@ pub fn process_instruction( from_seed, from_owner, } => { - let from = next_keyed_account(keyed_accounts_iter)?; - let base = next_keyed_account(keyed_accounts_iter)?; - let to = next_keyed_account(keyed_accounts_iter)?; + 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)?; transfer_with_seed( from, base, @@ -342,40 +341,49 @@ pub fn process_instruction( ) } SystemInstruction::AdvanceNonceAccount => { - let me = &mut next_keyed_account(keyed_accounts_iter)?; + let me = &mut keyed_account_at_index(keyed_accounts, 0)?; me.advance_nonce_account( - &from_keyed_account::(next_keyed_account(keyed_accounts_iter)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + 1, + )?)?, &signers, invoke_context, ) } SystemInstruction::WithdrawNonceAccount(lamports) => { - let me = &mut next_keyed_account(keyed_accounts_iter)?; - let to = &mut next_keyed_account(keyed_accounts_iter)?; + let me = &mut keyed_account_at_index(keyed_accounts, 0)?; + let to = &mut keyed_account_at_index(keyed_accounts, 1)?; me.withdraw_nonce_account( lamports, to, - &from_keyed_account::(next_keyed_account(keyed_accounts_iter)?)?, - &from_keyed_account::(next_keyed_account(keyed_accounts_iter)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + 2, + )?)?, + &from_keyed_account::(keyed_account_at_index(keyed_accounts, 3)?)?, &signers, invoke_context, ) } SystemInstruction::InitializeNonceAccount(authorized) => { - let me = &mut next_keyed_account(keyed_accounts_iter)?; + let me = &mut keyed_account_at_index(keyed_accounts, 0)?; me.initialize_nonce_account( &authorized, - &from_keyed_account::(next_keyed_account(keyed_accounts_iter)?)?, - &from_keyed_account::(next_keyed_account(keyed_accounts_iter)?)?, + &from_keyed_account::(keyed_account_at_index( + keyed_accounts, + 1, + )?)?, + &from_keyed_account::(keyed_account_at_index(keyed_accounts, 2)?)?, invoke_context, ) } SystemInstruction::AuthorizeNonceAccount(nonce_authority) => { - let me = &mut next_keyed_account(keyed_accounts_iter)?; + let me = &mut keyed_account_at_index(keyed_accounts, 0)?; me.authorize_nonce_account(&nonce_authority, &signers, invoke_context) } SystemInstruction::Allocate { space } => { - let keyed_account = next_keyed_account(keyed_accounts_iter)?; + let keyed_account = keyed_account_at_index(keyed_accounts, 0)?; 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) @@ -386,7 +394,7 @@ pub fn process_instruction( space, owner, } => { - let keyed_account = next_keyed_account(keyed_accounts_iter)?; + let keyed_account = keyed_account_at_index(keyed_accounts, 0)?; let mut account = keyed_account.try_account_ref_mut()?; let address = Address::create( keyed_account.unsigned_key(), @@ -403,7 +411,7 @@ pub fn process_instruction( ) } SystemInstruction::AssignWithSeed { base, seed, owner } => { - let keyed_account = next_keyed_account(keyed_accounts_iter)?; + let keyed_account = keyed_account_at_index(keyed_accounts, 0)?; let mut account = keyed_account.try_account_ref_mut()?; let address = Address::create( keyed_account.unsigned_key(), @@ -475,14 +483,13 @@ mod tests { fn process_instruction( owner: &Pubkey, - keyed_accounts: &[KeyedAccount], + keyed_accounts: Vec, instruction_data: &[u8], ) -> Result<(), InstructionError> { super::process_instruction( owner, - keyed_accounts, instruction_data, - &mut MockInvokeContext::default(), + &mut MockInvokeContext::new(keyed_accounts), ) } @@ -515,7 +522,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[ + vec![ KeyedAccount::new(&from, true, &from_account), KeyedAccount::new(&to, true, &to_account) ], @@ -547,7 +554,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[ + vec![ KeyedAccount::new(&from, true, &from_account), KeyedAccount::new(&to, false, &to_account) ], @@ -583,7 +590,7 @@ 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) @@ -616,7 +623,7 @@ mod tests { Address::create( &to, Some((&from, seed, &owner)), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ), Err(SystemError::AddressWithSeedMismatch.into()) ); @@ -634,7 +641,7 @@ mod tests { let to_address = Address::create( &to, Some((&from, seed, &new_owner)), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); @@ -647,7 +654,7 @@ mod tests { 2, &new_owner, &HashSet::new(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ), Err(InstructionError::MissingRequiredSignature) ); @@ -674,7 +681,7 @@ mod tests { 2, &new_owner, &[to].iter().cloned().collect::>(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ), Ok(()) ); @@ -706,7 +713,7 @@ mod tests { 2, &new_owner, &[from, to].iter().cloned().collect::>(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); } @@ -730,7 +737,7 @@ mod tests { MAX_PERMITTED_DATA_LENGTH + 1, &system_program::id(), &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert!(result.is_err()); assert_eq!( @@ -747,7 +754,7 @@ mod tests { MAX_PERMITTED_DATA_LENGTH, &system_program::id(), &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert!(result.is_ok()); assert_eq!(to_account.borrow().lamports, 50); @@ -780,7 +787,7 @@ mod tests { 2, &new_owner, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); @@ -799,7 +806,7 @@ mod tests { 2, &new_owner, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); let from_lamports = from_account.borrow().lamports; @@ -817,7 +824,7 @@ mod tests { 2, &new_owner, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); assert_eq!(from_lamports, 100); @@ -845,7 +852,7 @@ mod tests { 2, &new_owner, &[owned_key].iter().cloned().collect::>(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); @@ -859,7 +866,7 @@ mod tests { 2, &new_owner, &[from].iter().cloned().collect::>(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); @@ -873,7 +880,7 @@ mod tests { 2, &new_owner, &[owned_key].iter().cloned().collect::>(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Ok(())); } @@ -899,7 +906,7 @@ mod tests { 2, &sysvar::id(), &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(SystemError::InvalidProgramId.into())); @@ -933,7 +940,7 @@ mod tests { 2, &new_owner, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); } @@ -967,7 +974,7 @@ mod tests { 0, &solana_sdk::pubkey::new_rand(), &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ), Err(InstructionError::InvalidArgument), ); @@ -986,7 +993,7 @@ mod tests { &pubkey.into(), &new_owner, &HashSet::new(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ), Err(InstructionError::MissingRequiredSignature) ); @@ -997,7 +1004,7 @@ mod tests { &pubkey.into(), &system_program::id(), &HashSet::new(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ), Ok(()) ); @@ -1006,7 +1013,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[KeyedAccount::new(&pubkey, true, &account)], + vec![KeyedAccount::new(&pubkey, true, &account)], &bincode::serialize(&SystemInstruction::Assign { owner: new_owner }).unwrap() ), Ok(()) @@ -1026,7 +1033,7 @@ mod tests { &from.into(), &new_owner, &[from].iter().cloned().collect::>(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ), Err(SystemError::InvalidProgramId.into()) ); @@ -1039,7 +1046,7 @@ mod tests { owner: solana_sdk::pubkey::new_rand(), }; let data = serialize(&instruction).unwrap(); - let result = process_instruction(&system_program::id(), &[], &data); + let result = process_instruction(&system_program::id(), vec![], &data); assert_eq!(result, Err(InstructionError::NotEnoughAccountKeys)); let from = solana_sdk::pubkey::new_rand(); @@ -1049,7 +1056,7 @@ mod tests { let data = serialize(&instruction).unwrap(); let result = process_instruction( &system_program::id(), - &[KeyedAccount::new(&from, true, &from_account)], + vec![KeyedAccount::new(&from, true, &from_account)], &data, ); assert_eq!(result, Err(InstructionError::NotEnoughAccountKeys)); @@ -1067,7 +1074,7 @@ mod tests { &from_keyed_account, &to_keyed_account, 50, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let from_lamports = from_keyed_account.account.borrow().lamports; @@ -1081,7 +1088,7 @@ mod tests { &from_keyed_account, &to_keyed_account, 100, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); assert_eq!(from_keyed_account.account.borrow().lamports, 50); @@ -1094,7 +1101,7 @@ mod tests { &from_keyed_account, &to_keyed_account, 0, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .is_ok(),); assert_eq!(from_keyed_account.account.borrow().lamports, 50); @@ -1121,7 +1128,7 @@ mod tests { &from_owner, &to_keyed_account, 50, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let from_lamports = from_keyed_account.account.borrow().lamports; @@ -1138,7 +1145,7 @@ mod tests { &from_owner, &to_keyed_account, 100, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); assert_eq!(from_keyed_account.account.borrow().lamports, 50); @@ -1153,7 +1160,7 @@ mod tests { &from_owner, &to_keyed_account, 0, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .is_ok(),); assert_eq!(from_keyed_account.account.borrow().lamports, 50); @@ -1184,7 +1191,7 @@ mod tests { &KeyedAccount::new(&from, true, &from_account), &KeyedAccount::new(&to, false, &to_account), 50, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ), Err(InstructionError::InvalidArgument), ) @@ -1391,7 +1398,7 @@ mod tests { .zip(accounts.iter()) .map(|(meta, account)| KeyedAccount::new(&meta.pubkey, meta.is_signer, account)) .collect(); - process_instruction(&Pubkey::default(), &keyed_accounts, &instruction.data) + process_instruction(&Pubkey::default(), keyed_accounts, &instruction.data) } } @@ -1411,7 +1418,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[], + vec![], &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap() ), Err(InstructionError::NotEnoughAccountKeys), @@ -1423,11 +1430,11 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[KeyedAccount::new( + vec![KeyedAccount::new( &Pubkey::default(), true, &create_default_account(), - ),], + )], &serialize(&SystemInstruction::AdvanceNonceAccount).unwrap(), ), Err(InstructionError::NotEnoughAccountKeys), @@ -1439,7 +1446,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[ + vec![ KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), KeyedAccount::new( &sysvar::recent_blockhashes::id(), @@ -1458,7 +1465,7 @@ 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( &sysvar::recent_blockhashes::id(), @@ -1486,7 +1493,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[ + vec![ KeyedAccount::new(&Pubkey::default(), true, &nonce_acc,), KeyedAccount::new( &sysvar::recent_blockhashes::id(), @@ -1518,7 +1525,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[], + vec![], &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), ), Err(InstructionError::NotEnoughAccountKeys), @@ -1530,11 +1537,11 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[KeyedAccount::new( + vec![KeyedAccount::new( &Pubkey::default(), true, &create_default_account() - ),], + )], &serialize(&SystemInstruction::WithdrawNonceAccount(42)).unwrap(), ), Err(InstructionError::NotEnoughAccountKeys), @@ -1546,7 +1553,7 @@ 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( @@ -1566,7 +1573,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[ + vec![ KeyedAccount::new( &Pubkey::default(), true, @@ -1591,7 +1598,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[ + vec![ KeyedAccount::new( &Pubkey::default(), true, @@ -1616,7 +1623,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[], + vec![], &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ), Err(InstructionError::NotEnoughAccountKeys), @@ -1628,11 +1635,11 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[KeyedAccount::new( + vec![KeyedAccount::new( &Pubkey::default(), true, &nonce_account::create_account(1_000_000), - ),], + )], &serialize(&SystemInstruction::InitializeNonceAccount(Pubkey::default())).unwrap(), ), Err(InstructionError::NotEnoughAccountKeys), @@ -1644,7 +1651,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[ + vec![ KeyedAccount::new( &Pubkey::default(), true, @@ -1667,7 +1674,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[ + vec![ KeyedAccount::new( &Pubkey::default(), true, @@ -1691,7 +1698,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[ + vec![ KeyedAccount::new( &Pubkey::default(), true, @@ -1715,7 +1722,7 @@ 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( &sysvar::recent_blockhashes::id(), @@ -1730,7 +1737,7 @@ mod tests { assert_eq!( process_instruction( &Pubkey::default(), - &[KeyedAccount::new(&Pubkey::default(), true, &nonce_acc,),], + vec![KeyedAccount::new(&Pubkey::default(), true, &nonce_acc,),], &serialize(&SystemInstruction::AuthorizeNonceAccount(Pubkey::default(),)).unwrap(), ), Ok(()), diff --git a/sdk/src/entrypoint_native.rs b/sdk/src/entrypoint_native.rs index 6e49d59f38..ecbff8a6ad 100644 --- a/sdk/src/entrypoint_native.rs +++ b/sdk/src/entrypoint_native.rs @@ -89,14 +89,12 @@ macro_rules! declare_name { /// use solana_sdk::{ /// declare_program, /// instruction::InstructionError, -/// keyed_account::KeyedAccount, /// process_instruction::InvokeContext, /// pubkey::Pubkey, /// }; /// /// fn my_process_instruction( /// program_id: &Pubkey, -/// keyed_accounts: &[KeyedAccount], /// instruction_data: &[u8], /// invoke_context: &mut dyn InvokeContext, /// ) -> Result<(), InstructionError> { @@ -124,14 +122,12 @@ macro_rules! declare_name { /// use solana_sdk::{ /// declare_program, /// instruction::InstructionError, -/// keyed_account::KeyedAccount, /// process_instruction::InvokeContext, /// pubkey::Pubkey, /// }; /// /// fn my_process_instruction( /// program_id: &Pubkey, -/// keyed_accounts: &[KeyedAccount], /// instruction_data: &[u8], /// invoke_context: &mut dyn InvokeContext, /// ) -> Result<(), InstructionError> { @@ -158,11 +154,10 @@ macro_rules! declare_program( #[no_mangle] pub extern "C" fn $name( program_id: &$crate::pubkey::Pubkey, - keyed_accounts: &[$crate::keyed_account::KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn $crate::process_instruction::InvokeContext, ) -> Result<(), $crate::instruction::InstructionError> { - $entrypoint(program_id, keyed_accounts, instruction_data, invoke_context) + $entrypoint(program_id, instruction_data, invoke_context) } ) ); diff --git a/sdk/src/keyed_account.rs b/sdk/src/keyed_account.rs index 3f60387a3b..30e9eeb3ec 100644 --- a/sdk/src/keyed_account.rs +++ b/sdk/src/keyed_account.rs @@ -10,7 +10,7 @@ use std::{ }; #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct KeyedAccount<'a> { is_signer: bool, // Transaction was signed by this account's key is_writable: bool, @@ -146,6 +146,10 @@ pub fn create_keyed_accounts<'a>( accounts.iter().map(Into::into).collect() } +#[deprecated( + since = "1.7.0", + note = "Please use create_keyed_accounts_unified instead" +)] pub fn create_keyed_is_signer_accounts<'a>( accounts: &'a [(&'a Pubkey, bool, &'a RefCell)], ) -> Vec> { @@ -160,6 +164,10 @@ pub fn create_keyed_is_signer_accounts<'a>( .collect() } +#[deprecated( + since = "1.7.0", + note = "Please use create_keyed_accounts_unified instead" +)] pub fn create_keyed_readonly_accounts( accounts: &[(Pubkey, Rc>)], ) -> Vec { @@ -174,6 +182,20 @@ pub fn create_keyed_readonly_accounts( .collect() } +pub fn create_keyed_accounts_unified<'a>( + accounts: &[(bool, bool, &'a Pubkey, &'a RefCell)], +) -> Vec> { + accounts + .iter() + .map(|(is_signer, is_writable, key, account)| KeyedAccount { + is_signer: *is_signer, + is_writable: *is_writable, + key, + account, + }) + .collect() +} + /// Return all the signers from a set of KeyedAccounts pub fn get_signers(keyed_accounts: &[KeyedAccount]) -> A where @@ -186,6 +208,7 @@ where .collect::() } +#[deprecated(since = "1.7.0", note = "Please use keyed_account_at_index instead")] /// Return the next KeyedAccount or a NotEnoughAccountKeys error pub fn next_keyed_account<'a, 'b, I: Iterator>>( iter: &mut I, @@ -193,6 +216,16 @@ pub fn next_keyed_account<'a, 'b, I: Iterator>>( iter.next().ok_or(InstructionError::NotEnoughAccountKeys) } +/// Return the KeyedAccount at the specified index or a NotEnoughAccountKeys error +pub fn keyed_account_at_index<'a>( + keyed_accounts: &'a [KeyedAccount], + index: usize, +) -> Result<&'a KeyedAccount<'a>, InstructionError> { + keyed_accounts + .get(index) + .ok_or(InstructionError::NotEnoughAccountKeys) +} + /// Return true if the first keyed_account is executable, used to determine if /// the loader should call a program's 'main' pub fn is_executable(keyed_accounts: &[KeyedAccount]) -> Result { diff --git a/sdk/src/nonce_keyed_account.rs b/sdk/src/nonce_keyed_account.rs index fc9ae75176..30d14d2a1d 100644 --- a/sdk/src/nonce_keyed_account.rs +++ b/sdk/src/nonce_keyed_account.rs @@ -16,7 +16,7 @@ pub trait NonceKeyedAccount { &self, recent_blockhashes: &RecentBlockhashes, signers: &HashSet, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError>; fn withdraw_nonce_account( &self, @@ -25,20 +25,20 @@ pub trait NonceKeyedAccount { recent_blockhashes: &RecentBlockhashes, rent: &Rent, signers: &HashSet, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError>; fn initialize_nonce_account( &self, nonce_authority: &Pubkey, recent_blockhashes: &RecentBlockhashes, rent: &Rent, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError>; fn authorize_nonce_account( &self, nonce_authority: &Pubkey, signers: &HashSet, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError>; } @@ -47,7 +47,7 @@ impl<'a> NonceKeyedAccount for KeyedAccount<'a> { &self, recent_blockhashes: &RecentBlockhashes, signers: &HashSet, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { if recent_blockhashes.is_empty() { ic_msg!( @@ -102,7 +102,7 @@ impl<'a> NonceKeyedAccount for KeyedAccount<'a> { recent_blockhashes: &RecentBlockhashes, rent: &Rent, signers: &HashSet, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { let signer = match AccountUtilsState::::state(self)?.convert_to_current() { State::Uninitialized => { @@ -170,7 +170,7 @@ impl<'a> NonceKeyedAccount for KeyedAccount<'a> { nonce_authority: &Pubkey, recent_blockhashes: &RecentBlockhashes, rent: &Rent, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { if recent_blockhashes.is_empty() { ic_msg!( @@ -214,7 +214,7 @@ impl<'a> NonceKeyedAccount for KeyedAccount<'a> { &self, nonce_authority: &Pubkey, signers: &HashSet, - invoke_context: &mut dyn InvokeContext, + invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError> { match AccountUtilsState::::state(self)?.convert_to_current() { State::Initialized(data) => { @@ -300,7 +300,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let state = AccountUtilsState::::state(keyed_account) @@ -318,7 +318,7 @@ mod test { .advance_nonce_account( &recent_blockhashes, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let state = AccountUtilsState::::state(keyed_account) @@ -336,7 +336,7 @@ mod test { .advance_nonce_account( &recent_blockhashes, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let state = AccountUtilsState::::state(keyed_account) @@ -362,7 +362,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); // Empties Account balance @@ -396,7 +396,7 @@ mod test { &authority, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let pubkey = nonce_account.account.borrow().owner; @@ -415,7 +415,7 @@ mod test { let result = nonce_account.advance_nonce_account( &recent_blockhashes, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::MissingRequiredSignature),); }) @@ -438,14 +438,14 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let recent_blockhashes = vec![].into_iter().collect(); let result = keyed_account.advance_nonce_account( &recent_blockhashes, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(NonceError::NoRecentBlockhashes.into())); }) @@ -468,13 +468,13 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let result = keyed_account.advance_nonce_account( &recent_blockhashes, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(NonceError::NotExpired.into())); }) @@ -494,7 +494,7 @@ mod test { let result = keyed_account.advance_nonce_account( &recent_blockhashes, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(NonceError::BadAccountState.into())); }) @@ -518,7 +518,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let mut signers = HashSet::new(); @@ -527,7 +527,7 @@ mod test { let result = nonce_account.advance_nonce_account( &recent_blockhashes, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Ok(())); }); @@ -552,13 +552,13 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let result = nonce_account.advance_nonce_account( &recent_blockhashes, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::MissingRequiredSignature),); }); @@ -592,7 +592,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -631,7 +631,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::MissingRequiredSignature),); }) @@ -661,7 +661,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::InsufficientFunds)); }) @@ -691,7 +691,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -711,7 +711,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -741,7 +741,7 @@ mod test { &authority, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -765,7 +765,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -791,7 +791,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -819,7 +819,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); with_test_keyed_account(42, false, |to_keyed| { @@ -832,7 +832,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(NonceError::NotExpired.into())); }) @@ -854,7 +854,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); with_test_keyed_account(42, false, |to_keyed| { @@ -868,7 +868,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::InsufficientFunds)); }) @@ -890,7 +890,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); with_test_keyed_account(42, false, |to_keyed| { @@ -904,7 +904,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::InsufficientFunds)); }) @@ -926,7 +926,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); with_test_keyed_account(55, false, |to_keyed| { @@ -940,7 +940,7 @@ mod test { &recent_blockhashes, &rent, &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::InsufficientFunds)); }) @@ -967,7 +967,7 @@ mod test { &authority, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); let data = nonce::state::Data { authority, @@ -998,7 +998,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(NonceError::NoRecentBlockhashes.into())); }) @@ -1019,7 +1019,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let recent_blockhashes = create_test_recent_blockhashes(0); @@ -1027,7 +1027,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(NonceError::BadAccountState.into())); }) @@ -1047,7 +1047,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::InsufficientFunds)); }) @@ -1070,7 +1070,7 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let authority = Pubkey::default(); @@ -1082,7 +1082,7 @@ mod test { let result = nonce_account.authorize_nonce_account( &Pubkey::default(), &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Ok(())); let state = AccountUtilsState::::state(nonce_account) @@ -1105,7 +1105,7 @@ mod test { let result = nonce_account.authorize_nonce_account( &Pubkey::default(), &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(NonceError::BadAccountState.into())); }) @@ -1128,13 +1128,13 @@ mod test { &authorized, &recent_blockhashes, &rent, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); let result = nonce_account.authorize_nonce_account( &Pubkey::default(), &signers, - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ); assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); }) @@ -1155,7 +1155,7 @@ mod test { &authorized, &recent_blockhashes, &Rent::free(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); assert!(verify_nonce_account( @@ -1190,7 +1190,7 @@ mod test { &authorized, &recent_blockhashes, &Rent::free(), - &mut MockInvokeContext::default(), + &MockInvokeContext::new(vec![]), ) .unwrap(); assert!(!verify_nonce_account( diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index 3384a7267c..d591c84ac7 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -1,7 +1,7 @@ use solana_sdk::{ account::AccountSharedData, instruction::{CompiledInstruction, Instruction, InstructionError}, - keyed_account::KeyedAccount, + keyed_account::{create_keyed_accounts_unified, KeyedAccount}, message::Message, pubkey::Pubkey, }; @@ -15,19 +15,46 @@ use std::{cell::RefCell, fmt::Debug, rc::Rc, sync::Arc}; /// invoke_context: Invocation context pub type LoaderEntrypoint = unsafe extern "C" fn( program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &dyn InvokeContext, ) -> Result<(), InstructionError>; pub type ProcessInstructionWithContext = - fn(&Pubkey, &[KeyedAccount], &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>; + fn(&Pubkey, &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>; + +pub struct InvokeContextStackFrame<'a> { + pub key: Pubkey, + pub keyed_accounts: Vec>, + pub keyed_accounts_range: std::ops::Range, +} + +impl<'a> InvokeContextStackFrame<'a> { + pub fn new(key: Pubkey, keyed_accounts: Vec>) -> Self { + let keyed_accounts_range = std::ops::Range { + start: 0, + end: keyed_accounts.len(), + }; + Self { + key, + keyed_accounts, + keyed_accounts_range, + } + } +} /// Invocation context passed to loaders pub trait InvokeContext { - /// Push a program ID on to the invocation stack - fn push(&mut self, key: &Pubkey) -> Result<(), InstructionError>; - /// Pop a program ID off of the invocation stack + /// Push a stack frame onto the invocation stack + /// + /// Used in MessageProcessor::process_cross_program_instruction + fn push( + &mut self, + key: &Pubkey, + keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + ) -> Result<(), InstructionError>; + /// Pop a stack frame from the invocation stack + /// + /// Used in MessageProcessor::process_cross_program_instruction fn pop(&mut self); /// Current depth of the invocation stake fn invoke_depth(&self) -> usize; @@ -41,6 +68,10 @@ pub trait InvokeContext { ) -> Result<(), InstructionError>; /// Get the program ID of the currently executing program fn get_caller(&self) -> Result<&Pubkey, InstructionError>; + /// Removes the first keyed account + fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError>; + /// Get the list of keyed accounts + fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError>; /// Get a list of built-in programs fn get_programs(&self) -> &[(Pubkey, ProcessInstructionWithContext)]; /// Get this invocation's logger @@ -238,7 +269,6 @@ pub trait Executor: Debug + Send + Sync { &self, loader_id: &Pubkey, program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, use_jit: bool, @@ -276,42 +306,58 @@ impl Logger for MockLogger { } } -pub struct MockInvokeContext { - pub key: Pubkey, +pub struct MockInvokeContext<'a> { + pub invoke_stack: Vec>, pub logger: MockLogger, pub bpf_compute_budget: BpfComputeBudget, pub compute_meter: MockComputeMeter, pub programs: Vec<(Pubkey, ProcessInstructionWithContext)>, pub accounts: Vec<(Pubkey, Rc>)>, - pub invoke_depth: usize, pub sysvars: Vec<(Pubkey, Option>>)>, } -impl Default for MockInvokeContext { - fn default() -> Self { - MockInvokeContext { - key: Pubkey::default(), +impl<'a> MockInvokeContext<'a> { + pub fn new(keyed_accounts: Vec>) -> Self { + let bpf_compute_budget = BpfComputeBudget::default(); + let mut invoke_context = MockInvokeContext { + invoke_stack: Vec::with_capacity(bpf_compute_budget.max_invoke_depth), logger: MockLogger::default(), - bpf_compute_budget: BpfComputeBudget::default(), + bpf_compute_budget, compute_meter: MockComputeMeter { remaining: std::i64::MAX as u64, }, programs: vec![], accounts: vec![], - invoke_depth: 0, sysvars: vec![], - } + }; + invoke_context + .invoke_stack + .push(InvokeContextStackFrame::new( + Pubkey::default(), + keyed_accounts, + )); + invoke_context } } -impl InvokeContext for MockInvokeContext { - fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> { - self.invoke_depth = self.invoke_depth.saturating_add(1); +impl<'a> InvokeContext for MockInvokeContext<'a> { + fn push( + &mut self, + key: &Pubkey, + keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + ) -> Result<(), InstructionError> { + fn transmute_lifetime<'a, 'b>(value: Vec>) -> Vec> { + unsafe { std::mem::transmute(value) } + } + self.invoke_stack.push(InvokeContextStackFrame::new( + *key, + transmute_lifetime(create_keyed_accounts_unified(keyed_accounts)), + )); Ok(()) } fn pop(&mut self) { - self.invoke_depth = self.invoke_depth.saturating_sub(1); + self.invoke_stack.pop(); } fn invoke_depth(&self) -> usize { - self.invoke_depth + self.invoke_stack.len() } fn verify_and_update( &mut self, @@ -323,7 +369,25 @@ impl InvokeContext for MockInvokeContext { Ok(()) } fn get_caller(&self) -> Result<&Pubkey, InstructionError> { - Ok(&self.key) + self.invoke_stack + .last() + .map(|frame| &frame.key) + .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); + Ok(()) + } + fn get_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> { + self.invoke_stack + .last() + .map(|frame| &frame.keyed_accounts[frame.keyed_accounts_range.clone()]) + .ok_or(InstructionError::CallDepth) } fn get_programs(&self) -> &[(Pubkey, ProcessInstructionWithContext)] { &self.programs