diff --git a/program-runtime/src/instruction_processor.rs b/program-runtime/src/instruction_processor.rs index 42fb4b87df1b70..49c6381be1e4b2 100644 --- a/program-runtime/src/instruction_processor.rs +++ b/program-runtime/src/instruction_processor.rs @@ -342,6 +342,29 @@ impl InstructionProcessor { } } + /// Create the KeyedAccounts that will be passed to the program + pub fn create_keyed_accounts<'a>( + message: &'a Message, + instruction: &'a CompiledInstruction, + executable_accounts: &'a [(Pubkey, Rc>)], + accounts: &'a [(Pubkey, Rc>)], + demote_program_write_locks: bool, + ) -> Vec<(bool, bool, &'a Pubkey, &'a RefCell)> { + executable_accounts + .iter() + .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_program_write_locks), + &accounts[index].0, + &accounts[index].1 as &RefCell, + ) + })) + .collect::>() + } + /// Process an instruction /// This method calls the instruction's program entrypoint method pub fn process_instruction( @@ -467,7 +490,7 @@ impl InstructionProcessor { let ( message, - program_indices, + executable_accounts, accounts, keyed_account_indices_reordered, caller_write_privileges, @@ -512,12 +535,13 @@ impl InstructionProcessor { invoke_context.record_instruction(&instruction); - let (program_account_index, program_account) = invoke_context - .get_account(&callee_program_id) - .ok_or_else(|| { - ic_msg!(invoke_context, "Unknown program {}", callee_program_id); - InstructionError::MissingAccount - })?; + let program_account = + invoke_context + .get_account(&callee_program_id) + .ok_or_else(|| { + ic_msg!(invoke_context, "Unknown program {}", callee_program_id); + InstructionError::MissingAccount + })?; if !program_account.borrow().executable() { ic_msg!( invoke_context, @@ -526,16 +550,13 @@ impl InstructionProcessor { ); return Err(InstructionError::AccountNotExecutable); } - let mut program_indices = vec![]; - if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { + let programdata = if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { if let UpgradeableLoaderState::Program { programdata_address, } = program_account.borrow().state()? { - if let Some((programdata_account_index, _programdata_account)) = - invoke_context.get_account(&programdata_address) - { - program_indices.push(programdata_account_index); + if let Some(account) = invoke_context.get_account(&programdata_address) { + Some((programdata_address, account)) } else { ic_msg!( invoke_context, @@ -552,11 +573,16 @@ impl InstructionProcessor { ); 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); } - program_indices.insert(0, program_account_index); ( message, - program_indices, + executable_accounts, accounts, keyed_account_indices_reordered, caller_write_privileges, @@ -566,7 +592,7 @@ impl InstructionProcessor { #[allow(clippy::deref_addrof)] InstructionProcessor::process_cross_program_instruction( &message, - &program_indices, + &executable_accounts, &accounts, &caller_write_privileges, *(&mut *(invoke_context.borrow_mut())), @@ -620,7 +646,7 @@ impl InstructionProcessor { /// This method calls the instruction's program entrypoint function pub fn process_cross_program_instruction( message: &Message, - program_indices: &[usize], + executable_accounts: &[(Pubkey, Rc>)], accounts: &[(Pubkey, Rc>)], caller_write_privileges: &[bool], invoke_context: &mut dyn InvokeContext, @@ -631,8 +657,19 @@ impl InstructionProcessor { // Verify the calling program hasn't misbehaved invoke_context.verify_and_update(instruction, accounts, caller_write_privileges)?; + // Construct keyed accounts + let demote_program_write_locks = + invoke_context.is_feature_active(&demote_program_write_locks::id()); + let keyed_accounts = Self::create_keyed_accounts( + message, + instruction, + executable_accounts, + accounts, + demote_program_write_locks, + ); + // Invoke callee - invoke_context.push(program_id, message, instruction, program_indices, accounts)?; + invoke_context.push(program_id, &keyed_accounts)?; let mut instruction_processor = InstructionProcessor::default(); for (program_id, process_instruction) in invoke_context.get_programs().iter() { @@ -646,8 +683,6 @@ impl InstructionProcessor { ); if result.is_ok() { // Verify the called program has not misbehaved - let demote_program_write_locks = - invoke_context.is_feature_active(&demote_program_write_locks::id()); let write_privileges: Vec = (0..message.account_keys.len()) .map(|i| message.is_writable(i, demote_program_write_locks)) .collect(); @@ -663,6 +698,28 @@ impl InstructionProcessor { } } + /// Record the initial state of the accounts so that they can be compared + /// after the instruction is processed + pub fn create_pre_accounts( + message: &Message, + instruction: &CompiledInstruction, + accounts: &[(Pubkey, Rc>)], + ) -> Vec { + let mut pre_accounts = Vec::with_capacity(instruction.accounts.len()); + { + let mut work = |_unique_index: usize, account_index: usize| { + if account_index < message.account_keys.len() && account_index < accounts.len() { + let account = accounts[account_index].1.borrow(); + pre_accounts.push(PreAccount::new(&accounts[account_index].0, &account)); + return Ok(()); + } + Err(InstructionError::MissingAccount) + }; + let _ = instruction.visit_each_account(&mut work); + } + pre_accounts + } + /// Verify the results of a cross-program instruction #[allow(clippy::too_many_arguments)] pub fn verify_and_update( diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index 92e11756016ca1..da1ef80d86ecb1 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -254,6 +254,14 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { let message = Message::new(&[instruction.clone()], None); let program_id_index = message.instructions[0].program_id_index as usize; let program_id = message.account_keys[program_id_index]; + let program_account_info = || { + for account_info in account_infos { + if account_info.unsigned_key() == &program_id { + return account_info; + } + } + panic!("Program id {} wasn't found in account_infos", program_id); + }; let demote_program_write_locks = invoke_context.is_feature_active(&demote_program_write_locks::id()); // TODO don't have the caller's keyed_accounts so can't validate writer or signer escalation or deescalation yet @@ -266,7 +274,6 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { stable_log::program_invoke(&logger, &program_id, invoke_context.invoke_depth()); - // Convert AccountInfos into Accounts fn ai_to_a(ai: &AccountInfo) -> AccountSharedData { AccountSharedData::from(Account { lamports: ai.lamports(), @@ -276,6 +283,12 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { rent_epoch: ai.rent_epoch, }) } + let executables = vec![( + program_id, + Rc::new(RefCell::new(ai_to_a(program_account_info()))), + )]; + + // Convert AccountInfos into Accounts let mut accounts = vec![]; 'outer: for key in &message.account_keys { for account_info in account_infos { @@ -291,9 +304,6 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { message.account_keys.len(), "Missing or not enough accounts passed to invoke" ); - let (program_account_index, _program_account) = - invoke_context.get_account(&program_id).unwrap(); - let program_indices = vec![program_account_index]; // Check Signers for account_info in account_infos { @@ -321,7 +331,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { InstructionProcessor::process_cross_program_instruction( &message, - &program_indices, + &executables, &accounts, &caller_privileges, invoke_context, diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index fa61ce366e9f6d..c09727475d1ad9 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -2229,15 +2229,14 @@ where let mut accounts = Vec::with_capacity(account_keys.len()); let mut refs = Vec::with_capacity(account_keys.len()); for (i, ref account_key) in account_keys.iter().enumerate() { - let (_account_index, account) = - invoke_context.get_account(account_key).ok_or_else(|| { - ic_msg!( - invoke_context, - "Instruction references an unknown account {}", - account_key - ); - SyscallError::InstructionError(InstructionError::MissingAccount) - })?; + let account = invoke_context.get_account(account_key).ok_or_else(|| { + ic_msg!( + invoke_context, + "Instruction references an unknown account {}", + account_key + ); + SyscallError::InstructionError(InstructionError::MissingAccount) + })?; if i == program_account_index || account.borrow().executable() { // Use the known account @@ -2322,16 +2321,14 @@ fn get_upgradeable_executable( callee_program_id: &Pubkey, program_account: &Rc>, invoke_context: &Ref<&mut dyn InvokeContext>, -) -> Result, EbpfError> { +) -> Result>)>, EbpfError> { if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { match program_account.borrow().state() { Ok(UpgradeableLoaderState::Program { programdata_address, }) => { - if let Some((programdata_account_index, _programdata_account)) = - invoke_context.get_account(&programdata_address) - { - Ok(Some(programdata_account_index)) + if let Some(account) = invoke_context.get_account(&programdata_address) { + Ok(Some((programdata_address, account))) } else { ic_msg!( invoke_context, @@ -2367,7 +2364,7 @@ fn call<'a>( ) -> Result> { let ( message, - program_indices, + executables, accounts, account_refs, caller_write_privileges, @@ -2445,21 +2442,16 @@ fn call<'a>( let program_account = accounts .get(callee_program_id_index) .ok_or_else(|| { - ic_msg!(invoke_context, "Unknown program {}", callee_program_id); + ic_msg!(invoke_context, "Unknown program {}", callee_program_id,); SyscallError::InstructionError(InstructionError::MissingAccount) })? .1 .clone(); - let (program_account_index, _program_account) = - invoke_context.get_account(&callee_program_id).ok_or( - SyscallError::InstructionError(InstructionError::MissingAccount), - )?; - - let mut program_indices = vec![program_account_index]; - if let Some(programdata_account_index) = - get_upgradeable_executable(&callee_program_id, &program_account, &invoke_context)? - { - program_indices.push(programdata_account_index); + let programdata_executable = + get_upgradeable_executable(&callee_program_id, &program_account, &invoke_context)?; + let mut executables = vec![(callee_program_id, program_account)]; + if let Some(executable) = programdata_executable { + executables.push(executable); } // Record the instruction @@ -2468,7 +2460,7 @@ fn call<'a>( ( message, - program_indices, + executables, accounts, account_refs, caller_write_privileges, @@ -2481,7 +2473,7 @@ fn call<'a>( #[allow(clippy::deref_addrof)] match InstructionProcessor::process_cross_program_instruction( &message, - &program_indices, + &executables, &accounts, &caller_write_privileges, *(&mut *(syscall.get_context_mut()?)), diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 8051361f5bd617..71e1999959d4bd 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -101,11 +101,11 @@ pub struct Accounts { // for the load instructions pub type TransactionAccounts = Vec<(Pubkey, AccountSharedData)>; pub type TransactionRent = u64; -pub type TransactionProgramIndices = Vec>; +pub type TransactionLoaders = Vec>; #[derive(PartialEq, Debug, Clone)] pub struct LoadedTransaction { pub accounts: TransactionAccounts, - pub program_indices: TransactionProgramIndices, + pub loaders: TransactionLoaders, pub rent: TransactionRent, pub rent_debits: RentDebits, } @@ -244,10 +244,7 @@ impl Accounts { let is_upgradeable_loader_present = is_upgradeable_loader_present(message); for (i, key) in message.account_keys_iter().enumerate() { - let account = if !message.is_non_loader_key(i) { - // Fill in an empty account for the program slots. - AccountSharedData::default() - } else { + let account = if message.is_non_loader_key(i) { if payer_index.is_none() { payer_index = Some(i); } @@ -292,12 +289,12 @@ impl Accounts { programdata_address, }) = account.state() { - if let Some((programdata_account, _)) = self + if let Some(account) = self .accounts_db .load_with_fixed_root(ancestors, &programdata_address) + .map(|(account, _)| account) { - account_deps - .push((programdata_address, programdata_account)); + account_deps.push((programdata_address, account)); } else { error_counters.account_not_found += 1; return Err(TransactionError::ProgramAccountNotFound); @@ -320,6 +317,9 @@ impl Accounts { account } + } else { + // Fill in an empty account for the program slots. + AccountSharedData::default() }; accounts.push((*key, account)); } @@ -338,46 +338,44 @@ impl Accounts { let payer_account = &mut accounts[payer_index].1; if payer_account.lamports() == 0 { error_counters.account_not_found += 1; - return Err(TransactionError::AccountNotFound); - } - let min_balance = match get_system_account_kind(payer_account).ok_or_else(|| { - error_counters.invalid_account_for_fee += 1; - TransactionError::InvalidAccountForFee - })? { - SystemAccountKind::System => 0, - SystemAccountKind::Nonce => { - // Should we ever allow a fees charge to zero a nonce account's - // balance. The state MUST be set to uninitialized in that case - rent_collector.rent.minimum_balance(nonce::State::size()) - } - }; + Err(TransactionError::AccountNotFound) + } else { + let min_balance = + match get_system_account_kind(payer_account).ok_or_else(|| { + error_counters.invalid_account_for_fee += 1; + TransactionError::InvalidAccountForFee + })? { + SystemAccountKind::System => 0, + SystemAccountKind::Nonce => { + // Should we ever allow a fees charge to zero a nonce account's + // balance. The state MUST be set to uninitialized in that case + rent_collector.rent.minimum_balance(nonce::State::size()) + } + }; - if payer_account.lamports() < fee + min_balance { - error_counters.insufficient_funds += 1; - return Err(TransactionError::InsufficientFundsForFee); + if payer_account.lamports() < fee + min_balance { + error_counters.insufficient_funds += 1; + Err(TransactionError::InsufficientFundsForFee) + } else { + payer_account + .checked_sub_lamports(fee) + .map_err(|_| TransactionError::InsufficientFundsForFee)?; + + let message = tx.message(); + let loaders = message + .program_instructions_iter() + .map(|(program_id, _ix)| { + self.load_executable_accounts(ancestors, program_id, error_counters) + }) + .collect::>()?; + Ok(LoadedTransaction { + accounts, + loaders, + rent: tx_rent, + rent_debits, + }) + } } - payer_account - .checked_sub_lamports(fee) - .map_err(|_| TransactionError::InsufficientFundsForFee)?; - - let program_indices = message - .instructions() - .iter() - .map(|instruction| { - self.load_executable_accounts( - ancestors, - &mut accounts, - instruction.program_id_index as usize, - error_counters, - ) - }) - .collect::>>>()?; - Ok(LoadedTransaction { - accounts, - program_indices, - rent: tx_rent, - rent_debits, - }) } else { error_counters.account_not_found += 1; Err(TransactionError::AccountNotFound) @@ -388,35 +386,35 @@ impl Accounts { fn load_executable_accounts( &self, ancestors: &Ancestors, - accounts: &mut Vec<(Pubkey, AccountSharedData)>, - mut program_account_index: usize, + program_id: &Pubkey, error_counters: &mut ErrorCounters, - ) -> Result> { - let mut account_indices = Vec::new(); - let mut program_id = accounts[program_account_index].0; + ) -> Result> { + let mut accounts = Vec::new(); let mut depth = 0; - while !native_loader::check_id(&program_id) { + let mut program_id = *program_id; + loop { + if native_loader::check_id(&program_id) { + // At the root of the chain, ready to dispatch + break; + } + if depth >= 5 { error_counters.call_chain_too_deep += 1; return Err(TransactionError::CallChainTooDeep); } depth += 1; - program_account_index = match self + let program = match self .accounts_db .load_with_fixed_root(ancestors, &program_id) + .map(|(account, _)| account) { - Some((program_account, _)) => { - let account_index = accounts.len(); - accounts.push((program_id, program_account)); - account_index - } + Some(program) => program, None => { error_counters.account_not_found += 1; return Err(TransactionError::ProgramAccountNotFound); } }; - let program = &accounts[program_account_index].1; if !program.executable() { error_counters.invalid_program_for_execution += 1; return Err(TransactionError::InvalidProgramForExecution); @@ -431,31 +429,26 @@ impl Accounts { programdata_address, }) = program.state() { - let programdata_account_index = match self + if let Some(program) = self .accounts_db .load_with_fixed_root(ancestors, &programdata_address) + .map(|(account, _)| account) { - Some((programdata_account, _)) => { - let account_index = accounts.len(); - accounts.push((programdata_address, programdata_account)); - account_index - } - None => { - error_counters.account_not_found += 1; - return Err(TransactionError::ProgramAccountNotFound); - } - }; - account_indices.insert(0, programdata_account_index); + accounts.insert(0, (programdata_address, program)); + } else { + error_counters.account_not_found += 1; + return Err(TransactionError::ProgramAccountNotFound); + } } else { error_counters.invalid_program_for_execution += 1; return Err(TransactionError::InvalidProgramForExecution); } } - account_indices.insert(0, program_account_index); + accounts.insert(0, (program_id, program)); program_id = program_owner; } - Ok(account_indices) + Ok(accounts) } pub fn load_accounts( @@ -1475,8 +1468,8 @@ mod tests { (Ok(loaded_transaction), _nonce_rollback) => { assert_eq!(loaded_transaction.accounts.len(), 3); assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1); - assert_eq!(loaded_transaction.program_indices.len(), 1); - assert_eq!(loaded_transaction.program_indices[0].len(), 0); + assert_eq!(loaded_transaction.loaders.len(), 1); + assert_eq!(loaded_transaction.loaders[0].len(), 0); } (Err(e), _nonce_rollback) => Err(e).unwrap(), } @@ -1661,22 +1654,15 @@ mod tests { assert_eq!(loaded_accounts.len(), 1); match &loaded_accounts[0] { (Ok(loaded_transaction), _nonce_rollback) => { - assert_eq!(loaded_transaction.accounts.len(), 6); + assert_eq!(loaded_transaction.accounts.len(), 3); assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1); - assert_eq!(loaded_transaction.program_indices.len(), 2); - assert_eq!(loaded_transaction.program_indices[0].len(), 1); - assert_eq!(loaded_transaction.program_indices[1].len(), 2); - for program_indices in loaded_transaction.program_indices.iter() { - for (i, program_index) in program_indices.iter().enumerate() { + assert_eq!(loaded_transaction.loaders.len(), 2); + assert_eq!(loaded_transaction.loaders[0].len(), 1); + assert_eq!(loaded_transaction.loaders[1].len(), 2); + for loaders in loaded_transaction.loaders.iter() { + for (i, accounts_subset) in loaders.iter().enumerate() { // +1 to skip first not loader account - assert_eq!( - loaded_transaction.accounts[*program_index].0, - accounts[i + 1].0 - ); - assert_eq!( - loaded_transaction.accounts[*program_index].1, - accounts[i + 1].1 - ); + assert_eq!(*accounts_subset, accounts[i + 1]); } } } @@ -1766,7 +1752,7 @@ mod tests { assert_eq!(loaded_accounts.len(), 1); let result = loaded_accounts[0].0.as_ref().unwrap(); assert_eq!(result.accounts[..2], accounts[..2]); - assert_eq!(result.accounts[result.program_indices[0][0]], accounts[2]); + assert_eq!(result.loaders[0], vec![accounts[2].clone()]); } #[test] @@ -1849,7 +1835,7 @@ mod tests { assert_eq!(loaded_accounts.len(), 1); let result = loaded_accounts[0].0.as_ref().unwrap(); assert_eq!(result.accounts[..2], accounts[..2]); - assert_eq!(result.accounts[result.program_indices[0][0]], accounts[5]); + assert_eq!(result.loaders[0], vec![accounts[5].clone()]); // Solution 2: mark programdata as readonly message.account_keys = vec![key0, key1, key2]; // revert key change @@ -1861,9 +1847,14 @@ mod tests { assert_eq!(loaded_accounts.len(), 1); let result = loaded_accounts[0].0.as_ref().unwrap(); assert_eq!(result.accounts[..2], accounts[..2]); - assert_eq!(result.accounts[result.program_indices[0][0]], accounts[5]); - assert_eq!(result.accounts[result.program_indices[0][1]], accounts[3]); - assert_eq!(result.accounts[result.program_indices[0][2]], accounts[4]); + assert_eq!( + result.loaders[0], + vec![ + accounts[5].clone(), + accounts[3].clone(), + accounts[4].clone() + ] + ); } #[test] @@ -1932,8 +1923,8 @@ mod tests { let result = loaded_accounts[0].0.as_ref().unwrap(); assert_eq!(result.accounts[..2], accounts_with_upgradeable_loader[..2]); assert_eq!( - result.accounts[result.program_indices[0][0]], - accounts_with_upgradeable_loader[2] + result.loaders[0], + vec![accounts_with_upgradeable_loader[2].clone()] ); // Solution 2: mark programdata as readonly @@ -1946,7 +1937,7 @@ mod tests { assert_eq!(loaded_accounts.len(), 1); let result = loaded_accounts[0].0.as_ref().unwrap(); assert_eq!(result.accounts[..2], accounts[..2]); - assert_eq!(result.accounts[result.program_indices[0][0]], accounts[2]); + assert_eq!(result.loaders[0], vec![accounts[2].clone()]); } #[test] @@ -1961,17 +1952,11 @@ mod tests { let mut error_counters = ErrorCounters::default(); let ancestors = vec![(0, 0)].into_iter().collect(); - let keypair = Keypair::new(); - let mut account = AccountSharedData::new(1, 0, &Pubkey::default()); - account.set_executable(true); - accounts.store_slow_uncached(0, &keypair.pubkey(), &account); - assert_eq!( accounts.load_executable_accounts( &ancestors, - &mut vec![(keypair.pubkey(), account)], - 0, - &mut error_counters, + &solana_sdk::pubkey::new_rand(), + &mut error_counters ), Err(TransactionError::ProgramAccountNotFound) ); @@ -2297,21 +2282,27 @@ mod tests { ]; let tx1 = new_sanitized_tx(&[&keypair1], message, Hash::default()); + let loaders = vec![(Ok(()), None), (Ok(()), None)]; + + let transaction_loaders0 = vec![]; + let transaction_rent0 = 0; let loaded0 = ( Ok(LoadedTransaction { accounts: transaction_accounts0, - program_indices: vec![], - rent: 0, + loaders: transaction_loaders0, + rent: transaction_rent0, rent_debits: RentDebits::default(), }), None, ); + let transaction_loaders1 = vec![]; + let transaction_rent1 = 0; let loaded1 = ( Ok(LoadedTransaction { accounts: transaction_accounts1, - program_indices: vec![], - rent: 0, + loaders: transaction_loaders1, + rent: transaction_rent1, rent_debits: RentDebits::default(), }), None, @@ -2334,10 +2325,9 @@ mod tests { .insert_new_readonly(&pubkey); } let txs = vec![tx0, tx1]; - let programs = vec![(Ok(()), None), (Ok(()), None)]; let collected_accounts = accounts.collect_accounts_to_store( &txs, - &programs, + &loaders, loaded.as_mut_slice(), &rent_collector, &(Hash::default(), FeeCalculator::default()), @@ -2684,15 +2674,24 @@ mod tests { nonce_account_pre.clone(), Some(from_account_pre.clone()), )); + let loaders = vec![( + Err(TransactionError::InstructionError( + 1, + InstructionError::InvalidArgument, + )), + nonce_rollback.clone(), + )]; + let transaction_loaders = vec![]; + let transaction_rent = 0; let loaded = ( Ok(LoadedTransaction { accounts: transaction_accounts, - program_indices: vec![], - rent: 0, + loaders: transaction_loaders, + rent: transaction_rent, rent_debits: RentDebits::default(), }), - nonce_rollback.clone(), + nonce_rollback, ); let mut loaded = vec![loaded]; @@ -2706,16 +2705,9 @@ mod tests { AccountShrinkThreshold::default(), ); let txs = vec![tx]; - let programs = vec![( - Err(TransactionError::InstructionError( - 1, - InstructionError::InvalidArgument, - )), - nonce_rollback, - )]; let collected_accounts = accounts.collect_accounts_to_store( &txs, - &programs, + &loaders, loaded.as_mut_slice(), &rent_collector, &(next_blockhash, FeeCalculator::default()), @@ -2800,15 +2792,24 @@ mod tests { nonce_account_pre.clone(), None, )); + let loaders = vec![( + Err(TransactionError::InstructionError( + 1, + InstructionError::InvalidArgument, + )), + nonce_rollback.clone(), + )]; + let transaction_loaders = vec![]; + let transaction_rent = 0; let loaded = ( Ok(LoadedTransaction { accounts: transaction_accounts, - program_indices: vec![], - rent: 0, + loaders: transaction_loaders, + rent: transaction_rent, rent_debits: RentDebits::default(), }), - nonce_rollback.clone(), + nonce_rollback, ); let mut loaded = vec![loaded]; @@ -2822,16 +2823,9 @@ mod tests { AccountShrinkThreshold::default(), ); let txs = vec![tx]; - let programs = vec![( - Err(TransactionError::InstructionError( - 1, - InstructionError::InvalidArgument, - )), - nonce_rollback, - )]; let collected_accounts = accounts.collect_accounts_to_store( &txs, - &programs, + &loaders, loaded.as_mut_slice(), &rent_collector, &(next_blockhash, FeeCalculator::default()), diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index d8f987df5ffff9..9a423a32dbb444 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -34,7 +34,10 @@ //! on behalf of the caller, and a low-level API for when they have //! already been signed and verified. use crate::{ - accounts::{AccountAddressFilter, Accounts, TransactionAccounts, TransactionLoadResult}, + accounts::{ + AccountAddressFilter, Accounts, TransactionAccounts, TransactionLoadResult, + TransactionLoaders, + }, accounts_db::{ AccountShrinkThreshold, AccountsDbConfig, ErrorCounters, SnapshotStorages, ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING, @@ -189,6 +192,7 @@ type BankStatusCache = StatusCache>; #[frozen_abi(digest = "5Br3PNyyX1L7XoS4jYLt5JTeMXowLSsu7v9LhokC8vnq")] pub type BankSlotDelta = SlotDelta>; type TransactionAccountRefCells = Vec<(Pubkey, Rc>)>; +type TransactionLoaderRefCells = Vec>)>>; // Eager rent collection repeats in cyclic manner. // Each cycle is composed of number of tiny pubkey subranges @@ -2814,7 +2818,6 @@ impl Bank { ) -> TransactionSimulationResult { assert!(self.is_frozen(), "simulation bank must be frozen"); - let number_of_accounts = transaction.message().account_keys_len(); let batch = self.prepare_simulation_batch(transaction); let mut timings = ExecuteTimings::default(); @@ -2845,13 +2848,7 @@ impl Bank { .unwrap() .0 .ok() - .map(|loaded_transaction| { - loaded_transaction - .accounts - .into_iter() - .take(number_of_accounts) - .collect::>() - }) + .map(|loaded_transaction| loaded_transaction.accounts.into_iter().collect::>()) .unwrap_or_default(); let units_consumed = timings @@ -3132,19 +3129,32 @@ impl Bank { /// Converts Accounts into RefCell, this involves moving /// ownership by draining the source - fn accounts_to_refcells(accounts: &mut TransactionAccounts) -> TransactionAccountRefCells { + fn accounts_to_refcells( + accounts: &mut TransactionAccounts, + loaders: &mut TransactionLoaders, + ) -> (TransactionAccountRefCells, TransactionLoaderRefCells) { let account_refcells: Vec<_> = accounts .drain(..) .map(|(pubkey, account)| (pubkey, Rc::new(RefCell::new(account)))) .collect(); - account_refcells + let loader_refcells: Vec> = loaders + .iter_mut() + .map(|v| { + v.drain(..) + .map(|(pubkey, account)| (pubkey, Rc::new(RefCell::new(account)))) + .collect() + }) + .collect(); + (account_refcells, loader_refcells) } /// Converts back from RefCell to AccountSharedData, this involves moving /// ownership by draining the sources fn refcells_to_accounts( accounts: &mut TransactionAccounts, + loaders: &mut TransactionLoaders, mut account_refcells: TransactionAccountRefCells, + loader_refcells: TransactionLoaderRefCells, ) -> std::result::Result<(), TransactionError> { for (pubkey, account_refcell) in account_refcells.drain(..) { accounts.push(( @@ -3154,6 +3164,16 @@ impl Bank { .into_inner(), )) } + for (ls, mut lrcs) in loaders.iter_mut().zip(loader_refcells) { + for (pubkey, lrc) in lrcs.drain(..) { + ls.push(( + pubkey, + Rc::try_unwrap(lrc) + .map_err(|_| TransactionError::AccountBorrowOutstanding)? + .into_inner(), + )) + } + } Ok(()) } @@ -3180,12 +3200,11 @@ impl Bank { fn get_executors( &self, message: &SanitizedMessage, - accounts: &[(Pubkey, AccountSharedData)], - program_indices: &[Vec], + loaders: &[Vec<(Pubkey, AccountSharedData)>], ) -> Rc> { let mut num_executors = message.account_keys_len(); - for program_indices_of_instruction in program_indices.iter() { - num_executors += program_indices_of_instruction.len(); + for instruction_loaders in loaders.iter() { + num_executors += instruction_loaders.len(); } let mut executors = HashMap::with_capacity(num_executors); let cow_cache = self.cached_executors.read().unwrap(); @@ -3196,11 +3215,10 @@ impl Bank { executors.insert(*key, executor); } } - for program_indices_of_instruction in program_indices.iter() { - for account_index in program_indices_of_instruction.iter() { - let key = accounts[*account_index].0; - if let Some(executor) = cache.get(&key) { - executors.insert(key, executor); + for instruction_loaders in loaders.iter() { + for (key, _) in instruction_loaders.iter() { + if let Some(executor) = cache.get(key) { + executors.insert(*key, executor); } } } @@ -3316,14 +3334,13 @@ impl Bank { }; if process_result.is_ok() { - let executors = self.get_executors( - tx.message(), - &loaded_transaction.accounts, - &loaded_transaction.program_indices, - ); + let executors = + self.get_executors(tx.message(), &loaded_transaction.loaders); - let account_refcells = - Self::accounts_to_refcells(&mut loaded_transaction.accounts); + let (account_refcells, loader_refcells) = Self::accounts_to_refcells( + &mut loaded_transaction.accounts, + &mut loaded_transaction.loaders, + ); let instruction_recorders = if enable_cpi_recording { let ix_count = tx.message().instructions().len(); @@ -3360,7 +3377,7 @@ impl Bank { if let Some(legacy_message) = tx.message().legacy_message() { process_result = self.message_processor.process_message( legacy_message, - &loaded_transaction.program_indices, + &loader_refcells, &account_refcells, &self.rent_collector, log_collector.clone(), @@ -3388,7 +3405,9 @@ impl Bank { if let Err(e) = Self::refcells_to_accounts( &mut loaded_transaction.accounts, + &mut loaded_transaction.loaders, account_refcells, + loader_refcells, ) { warn!("Account lifetime mismanagement"); process_result = Err(e); @@ -11994,11 +12013,12 @@ pub(crate) mod tests { .try_into() .unwrap(); - let program_indices = &[vec![0, 1], vec![2]]; - let accounts = &[ - (key3, AccountSharedData::default()), - (key4, AccountSharedData::default()), - (key1, AccountSharedData::default()), + let loaders = &[ + vec![ + (key3, AccountSharedData::default()), + (key4, AccountSharedData::default()), + ], + vec![(key1, AccountSharedData::default())], ]; // don't do any work if not dirty @@ -12010,7 +12030,7 @@ pub(crate) mod tests { let executors = Rc::new(RefCell::new(executors)); executors.borrow_mut().is_dirty = false; bank.update_executors(executors); - let executors = bank.get_executors(&message, accounts, program_indices); + let executors = bank.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 0); // do work @@ -12021,7 +12041,7 @@ pub(crate) mod tests { executors.insert(key4, executor.clone()); let executors = Rc::new(RefCell::new(executors)); bank.update_executors(executors); - let executors = bank.get_executors(&message, accounts, program_indices); + let executors = bank.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 4); assert!(executors.borrow().executors.contains_key(&key1)); assert!(executors.borrow().executors.contains_key(&key2)); @@ -12030,7 +12050,7 @@ pub(crate) mod tests { // Check inheritance let bank = Bank::new_from_parent(&Arc::new(bank), &solana_sdk::pubkey::new_rand(), 1); - let executors = bank.get_executors(&message, accounts, program_indices); + let executors = bank.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 4); assert!(executors.borrow().executors.contains_key(&key1)); assert!(executors.borrow().executors.contains_key(&key2)); @@ -12041,7 +12061,7 @@ pub(crate) mod tests { bank.remove_executor(&key2); bank.remove_executor(&key3); bank.remove_executor(&key4); - let executors = bank.get_executors(&message, accounts, program_indices); + let executors = bank.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 0); assert!(!executors.borrow().executors.contains_key(&key1)); assert!(!executors.borrow().executors.contains_key(&key2)); @@ -12062,26 +12082,25 @@ pub(crate) mod tests { let message = SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(); - let program_indices = &[vec![0, 1]]; - let accounts = &[ + let loaders = &[vec![ (key1, AccountSharedData::default()), (key2, AccountSharedData::default()), - ]; + ]]; // add one to root bank let mut executors = Executors::default(); executors.insert(key1, executor.clone()); let executors = Rc::new(RefCell::new(executors)); root.update_executors(executors); - let executors = root.get_executors(&message, accounts, program_indices); + let executors = root.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 1); let fork1 = Bank::new_from_parent(&root, &Pubkey::default(), 1); let fork2 = Bank::new_from_parent(&root, &Pubkey::default(), 1); - let executors = fork1.get_executors(&message, accounts, program_indices); + let executors = fork1.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 1); - let executors = fork2.get_executors(&message, accounts, program_indices); + let executors = fork2.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 1); let mut executors = Executors::default(); @@ -12089,16 +12108,16 @@ pub(crate) mod tests { let executors = Rc::new(RefCell::new(executors)); fork1.update_executors(executors); - let executors = fork1.get_executors(&message, accounts, program_indices); + let executors = fork1.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 2); - let executors = fork2.get_executors(&message, accounts, program_indices); + let executors = fork2.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 1); fork1.remove_executor(&key1); - let executors = fork1.get_executors(&message, accounts, program_indices); + let executors = fork1.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 1); - let executors = fork2.get_executors(&message, accounts, program_indices); + let executors = fork2.get_executors(&message, loaders); assert_eq!(executors.borrow().executors.len(), 1); } diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 936e53d2e8eff2..864a196a71b67f 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -75,7 +75,7 @@ impl<'a> ThisInvokeContext<'a> { rent: Rent, message: &'a Message, instruction: &'a CompiledInstruction, - program_indices: &[usize], + executable_accounts: &'a [(Pubkey, Rc>)], accounts: &'a [(Pubkey, Rc>)], programs: &'a [(Pubkey, ProcessInstructionWithContext)], log_collector: Option>, @@ -88,8 +88,15 @@ impl<'a> ThisInvokeContext<'a> { ancestors: &'a Ancestors, blockhash: &'a Hash, fee_calculator: &'a FeeCalculator, - ) -> Result { + ) -> Self { let pre_accounts = MessageProcessor::create_pre_accounts(message, instruction, accounts); + let keyed_accounts = InstructionProcessor::create_keyed_accounts( + message, + instruction, + executable_accounts, + accounts, + feature_set.is_active(&demote_program_write_locks::id()), + ); let compute_meter = if feature_set.is_active(&tx_wide_compute_cap::id()) { compute_meter } else { @@ -117,18 +124,20 @@ impl<'a> ThisInvokeContext<'a> { blockhash, fee_calculator, }; - invoke_context.push(program_id, message, instruction, program_indices, accounts)?; - Ok(invoke_context) + 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, - message: &Message, - instruction: &CompiledInstruction, - program_indices: &[usize], - instruction_accounts: &[(Pubkey, Rc>)], + keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], ) -> Result<(), InstructionError> { if self.invoke_stack.len() > self.compute_budget.max_invoke_depth { return Err(InstructionError::CallDepth); @@ -145,31 +154,6 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { return Err(InstructionError::ReentrancyNotAllowed); } - // Create the KeyedAccounts that will be passed to the program - let demote_program_write_locks = self - .feature_set - .is_active(&demote_program_write_locks::id()); - let keyed_accounts = program_indices - .iter() - .map(|account_index| { - ( - false, - false, - &self.accounts[*account_index].0, - &self.accounts[*account_index].1 as &RefCell, - ) - }) - .chain(instruction.accounts.iter().map(|index| { - let index = *index as usize; - ( - message.is_signer(index), - message.is_writable(index, demote_program_write_locks), - &instruction_accounts[index].0, - &instruction_accounts[index].1 as &RefCell, - ) - })) - .collect::>(); - // 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 { @@ -280,13 +264,14 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { fn is_feature_active(&self, feature_id: &Pubkey) -> bool { self.feature_set.is_active(feature_id) } - fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc>)> { - for (index, (key, account)) in self.accounts.iter().enumerate().rev() { + fn get_account(&self, pubkey: &Pubkey) -> Option>> { + self.accounts.iter().find_map(|(key, account)| { if key == pubkey { - return Some((index, account.clone())); + Some(account.clone()) + } else { + None } - } - None + }) } fn update_timing( &mut self, @@ -396,11 +381,9 @@ impl MessageProcessor { /// Verify there are no outstanding borrows pub fn verify_account_references( accounts: &[(Pubkey, Rc>)], - program_indices: &[usize], ) -> Result<(), InstructionError> { - for account_index in program_indices.iter() { - accounts[*account_index] - .1 + for (_, account) in accounts.iter() { + account .try_borrow_mut() .map_err(|_| InstructionError::AccountBorrowOutstanding)?; } @@ -413,7 +396,7 @@ impl MessageProcessor { message: &Message, instruction: &CompiledInstruction, pre_accounts: &[PreAccount], - program_indices: &[usize], + executable_accounts: &[(Pubkey, Rc>)], accounts: &[(Pubkey, Rc>)], rent: &Rent, timings: &mut ExecuteDetailsTimings, @@ -422,7 +405,7 @@ impl MessageProcessor { demote_program_write_locks: bool, ) -> Result<(), InstructionError> { // Verify all executable accounts have zero outstanding refs - Self::verify_account_references(accounts, program_indices)?; + Self::verify_account_references(executable_accounts)?; // Verify the per-account instruction results let (mut pre_sum, mut post_sum) = (0_u128, 0_u128); @@ -479,7 +462,7 @@ impl MessageProcessor { &self, message: &Message, instruction: &CompiledInstruction, - program_indices: &[usize], + executable_accounts: &[(Pubkey, Rc>)], accounts: &[(Pubkey, Rc>)], rent_collector: &RentCollector, log_collector: Option>, @@ -525,7 +508,7 @@ impl MessageProcessor { rent_collector.rent, message, instruction, - program_indices, + executable_accounts, accounts, programs, log_collector, @@ -538,7 +521,7 @@ impl MessageProcessor { ancestors, blockhash, fee_calculator, - )?; + ); self.instruction_processor.process_instruction( program_id, @@ -549,7 +532,7 @@ impl MessageProcessor { message, instruction, &invoke_context.pre_accounts, - program_indices, + executable_accounts, accounts, &rent_collector.rent, timings, @@ -571,7 +554,7 @@ impl MessageProcessor { pub fn process_message( &self, message: &Message, - program_indices: &[Vec], + loaders: &[Vec<(Pubkey, Rc>)>], accounts: &[(Pubkey, Rc>)], rent_collector: &RentCollector, log_collector: Option>, @@ -596,7 +579,7 @@ impl MessageProcessor { .execute_instruction( message, instruction, - &program_indices[instruction_index], + &loaders[instruction_index], accounts, rent_collector, log_collector.clone(), @@ -693,21 +676,12 @@ mod tests { &ancestors, &blockhash, &fee_calculator, - ) - .unwrap(); + ); // Check call depth increases and has a limit let mut depth_reached = 1; for program_id in invoke_stack.iter().skip(1) { - if Err(InstructionError::CallDepth) - == invoke_context.push( - program_id, - &message, - &message.instructions[0], - &[], - &accounts, - ) - { + if Err(InstructionError::CallDepth) == invoke_context.push(program_id, &[]) { break; } depth_reached += 1; @@ -780,11 +754,11 @@ mod tests { Rc::new(RefCell::new(AccountSharedData::default())), )]; - assert!(MessageProcessor::verify_account_references(&accounts, &[0]).is_ok()); + assert!(MessageProcessor::verify_account_references(&accounts).is_ok()); let mut _borrowed = accounts[0].1.borrow(); assert_eq!( - MessageProcessor::verify_account_references(&accounts, &[0]), + MessageProcessor::verify_account_references(&accounts), Err(InstructionError::AccountBorrowOutstanding) ); } @@ -834,9 +808,6 @@ mod tests { let mut message_processor = MessageProcessor::default(); message_processor.add_program(mock_system_program_id, mock_system_process_instruction); - let program_account = Rc::new(RefCell::new(create_loadable_account_for_test( - "mock_system_program", - ))); let accounts = vec![ ( solana_sdk::pubkey::new_rand(), @@ -846,9 +817,12 @@ mod tests { solana_sdk::pubkey::new_rand(), AccountSharedData::new_ref(0, 1, &mock_system_program_id), ), - (mock_system_program_id, program_account), ]; - let program_indices = vec![vec![2]]; + + let account = Rc::new(RefCell::new(create_loadable_account_for_test( + "mock_system_program", + ))); + let loaders = vec![vec![(mock_system_program_id, account)]]; let executors = Rc::new(RefCell::new(Executors::default())); let ancestors = Ancestors::default(); @@ -868,7 +842,7 @@ mod tests { let result = message_processor.process_message( &message, - &program_indices, + &loaders, &accounts, &rent_collector, None, @@ -898,7 +872,7 @@ mod tests { let result = message_processor.process_message( &message, - &program_indices, + &loaders, &accounts, &rent_collector, None, @@ -932,7 +906,7 @@ mod tests { let result = message_processor.process_message( &message, - &program_indices, + &loaders, &accounts, &rent_collector, None, @@ -1022,9 +996,6 @@ mod tests { let mut message_processor = MessageProcessor::default(); message_processor.add_program(mock_program_id, mock_system_process_instruction); - let program_account = Rc::new(RefCell::new(create_loadable_account_for_test( - "mock_system_program", - ))); let accounts = vec![ ( solana_sdk::pubkey::new_rand(), @@ -1034,9 +1005,12 @@ mod tests { solana_sdk::pubkey::new_rand(), AccountSharedData::new_ref(0, 1, &mock_program_id), ), - (mock_program_id, program_account), ]; - let program_indices = vec![vec![2]]; + + let account = Rc::new(RefCell::new(create_loadable_account_for_test( + "mock_system_program", + ))); + let loaders = vec![vec![(mock_program_id, account)]]; let executors = Rc::new(RefCell::new(Executors::default())); let ancestors = Ancestors::default(); @@ -1058,7 +1032,7 @@ mod tests { ); let result = message_processor.process_message( &message, - &program_indices, + &loaders, &accounts, &rent_collector, None, @@ -1092,7 +1066,7 @@ mod tests { ); let result = message_processor.process_message( &message, - &program_indices, + &loaders, &accounts, &rent_collector, None, @@ -1124,7 +1098,7 @@ mod tests { let ancestors = Ancestors::default(); let result = message_processor.process_message( &message, - &program_indices, + &loaders, &accounts, &rent_collector, None, @@ -1189,6 +1163,10 @@ mod tests { let mut program_account = AccountSharedData::new(1, 0, &native_loader::id()); program_account.set_executable(true); + let executable_accounts = vec![( + callee_program_id, + Rc::new(RefCell::new(program_account.clone())), + )]; let owned_account = AccountSharedData::new(42, 1, &callee_program_id); let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand()); @@ -1203,12 +1181,8 @@ mod tests { solana_sdk::pubkey::new_rand(), Rc::new(RefCell::new(not_owned_account)), ), - ( - callee_program_id, - Rc::new(RefCell::new(program_account.clone())), - ), + (callee_program_id, Rc::new(RefCell::new(program_account))), ]; - let program_indices = vec![2]; let compiled_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2]); let programs: Vec<(_, ProcessInstructionWithContext)> = @@ -1235,7 +1209,7 @@ mod tests { Rent::default(), &message, &compiled_instruction, - &program_indices, + &executable_accounts, &accounts, programs.as_slice(), None, @@ -1248,8 +1222,7 @@ mod tests { &ancestors, &blockhash, &fee_calculator, - ) - .unwrap(); + ); // not owned account modified by the caller (before the invoke) let caller_write_privileges = message @@ -1262,7 +1235,7 @@ mod tests { assert_eq!( InstructionProcessor::process_cross_program_instruction( &message, - &program_indices, + &executable_accounts, &accounts, &caller_write_privileges, &mut invoke_context, @@ -1297,7 +1270,7 @@ mod tests { Rent::default(), &message, &compiled_instruction, - &program_indices, + &executable_accounts, &accounts, programs.as_slice(), None, @@ -1310,8 +1283,7 @@ mod tests { &ancestors, &blockhash, &fee_calculator, - ) - .unwrap(); + ); let caller_write_privileges = message .account_keys @@ -1322,7 +1294,7 @@ mod tests { assert_eq!( InstructionProcessor::process_cross_program_instruction( &message, - &program_indices, + &executable_accounts, &accounts, &caller_write_privileges, &mut invoke_context, diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index 3f2d2055e36e07..ec92892873498f 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -7,7 +7,6 @@ use solana_sdk::{ hash::Hash, instruction::{CompiledInstruction, Instruction, InstructionError}, keyed_account::{create_keyed_accounts_unified, KeyedAccount}, - message::Message, pubkey::Pubkey, sysvar::Sysvar, }; @@ -56,10 +55,7 @@ pub trait InvokeContext { fn push( &mut self, key: &Pubkey, - message: &Message, - instruction: &CompiledInstruction, - program_indices: &[usize], - instruction_accounts: &[(Pubkey, Rc>)], + keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], ) -> Result<(), InstructionError>; /// Pop a stack frame from the invocation stack /// @@ -99,8 +95,8 @@ pub trait InvokeContext { fn record_instruction(&self, instruction: &Instruction); /// Get the bank's active feature set fn is_feature_active(&self, feature_id: &Pubkey) -> bool; - /// Find an account_index and account by its key - fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc>)>; + /// Get an account by its key + fn get_account(&self, pubkey: &Pubkey) -> Option>>; /// Update timing fn update_timing( &mut self, @@ -444,15 +440,15 @@ pub fn mock_set_sysvar( impl<'a> InvokeContext for MockInvokeContext<'a> { fn push( &mut self, - _key: &Pubkey, - _message: &Message, - _instruction: &CompiledInstruction, - _program_indices: &[usize], - _instruction_accounts: &[(Pubkey, Rc>)], + 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, - create_keyed_accounts_unified(&[]), + *key, + transmute_lifetime(create_keyed_accounts_unified(keyed_accounts)), )); Ok(()) } @@ -513,10 +509,10 @@ impl<'a> InvokeContext for MockInvokeContext<'a> { fn is_feature_active(&self, feature_id: &Pubkey) -> bool { !self.disabled_features.contains(feature_id) } - fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc>)> { - for (index, (key, account)) in self.accounts.iter().enumerate().rev() { + fn get_account(&self, pubkey: &Pubkey) -> Option>> { + for (key, account) in self.accounts.iter() { if key == pubkey { - return Some((index, account.clone())); + return Some(account.clone()); } } None