diff --git a/program-runtime/src/instruction_processor.rs b/program-runtime/src/instruction_processor.rs index 49c6381be..42fb4b87d 100644 --- a/program-runtime/src/instruction_processor.rs +++ b/program-runtime/src/instruction_processor.rs @@ -342,29 +342,6 @@ 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( @@ -490,7 +467,7 @@ impl InstructionProcessor { let ( message, - executable_accounts, + program_indices, accounts, keyed_account_indices_reordered, caller_write_privileges, @@ -535,13 +512,12 @@ impl InstructionProcessor { invoke_context.record_instruction(&instruction); - let 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_index, 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, @@ -550,13 +526,16 @@ impl InstructionProcessor { ); return Err(InstructionError::AccountNotExecutable); } - let programdata = if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { + let mut program_indices = vec![]; + 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)) + if let Some((programdata_account_index, _programdata_account)) = + invoke_context.get_account(&programdata_address) + { + program_indices.push(programdata_account_index); } else { ic_msg!( invoke_context, @@ -573,16 +552,11 @@ 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, - executable_accounts, + program_indices, accounts, keyed_account_indices_reordered, caller_write_privileges, @@ -592,7 +566,7 @@ impl InstructionProcessor { #[allow(clippy::deref_addrof)] InstructionProcessor::process_cross_program_instruction( &message, - &executable_accounts, + &program_indices, &accounts, &caller_write_privileges, *(&mut *(invoke_context.borrow_mut())), @@ -646,7 +620,7 @@ impl InstructionProcessor { /// This method calls the instruction's program entrypoint function pub fn process_cross_program_instruction( message: &Message, - executable_accounts: &[(Pubkey, Rc>)], + program_indices: &[usize], accounts: &[(Pubkey, Rc>)], caller_write_privileges: &[bool], invoke_context: &mut dyn InvokeContext, @@ -657,19 +631,8 @@ 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, &keyed_accounts)?; + invoke_context.push(program_id, message, instruction, program_indices, accounts)?; let mut instruction_processor = InstructionProcessor::default(); for (program_id, process_instruction) in invoke_context.get_programs().iter() { @@ -683,6 +646,8 @@ 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(); @@ -698,28 +663,6 @@ 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 da1ef80d8..92e117560 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -254,14 +254,6 @@ 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 @@ -274,6 +266,7 @@ 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(), @@ -283,12 +276,6 @@ 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 { @@ -304,6 +291,9 @@ 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 { @@ -331,7 +321,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { InstructionProcessor::process_cross_program_instruction( &message, - &executables, + &program_indices, &accounts, &caller_privileges, invoke_context, diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index c09727475..fa61ce366 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -2229,14 +2229,15 @@ 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 = 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_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) + })?; if i == program_account_index || account.borrow().executable() { // Use the known account @@ -2321,14 +2322,16 @@ 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(account) = invoke_context.get_account(&programdata_address) { - Ok(Some((programdata_address, account))) + if let Some((programdata_account_index, _programdata_account)) = + invoke_context.get_account(&programdata_address) + { + Ok(Some(programdata_account_index)) } else { ic_msg!( invoke_context, @@ -2364,7 +2367,7 @@ fn call<'a>( ) -> Result> { let ( message, - executables, + program_indices, accounts, account_refs, caller_write_privileges, @@ -2442,16 +2445,21 @@ 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 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); + 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); } // Record the instruction @@ -2460,7 +2468,7 @@ fn call<'a>( ( message, - executables, + program_indices, accounts, account_refs, caller_write_privileges, @@ -2473,7 +2481,7 @@ fn call<'a>( #[allow(clippy::deref_addrof)] match InstructionProcessor::process_cross_program_instruction( &message, - &executables, + &program_indices, &accounts, &caller_write_privileges, *(&mut *(syscall.get_context_mut()?)), diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 71e199995..8051361f5 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 TransactionLoaders = Vec>; +pub type TransactionProgramIndices = Vec>; #[derive(PartialEq, Debug, Clone)] pub struct LoadedTransaction { pub accounts: TransactionAccounts, - pub loaders: TransactionLoaders, + pub program_indices: TransactionProgramIndices, pub rent: TransactionRent, pub rent_debits: RentDebits, } @@ -244,7 +244,10 @@ 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) { + let account = if !message.is_non_loader_key(i) { + // Fill in an empty account for the program slots. + AccountSharedData::default() + } else { if payer_index.is_none() { payer_index = Some(i); } @@ -289,12 +292,12 @@ impl Accounts { programdata_address, }) = account.state() { - if let Some(account) = self + if let Some((programdata_account, _)) = self .accounts_db .load_with_fixed_root(ancestors, &programdata_address) - .map(|(account, _)| account) { - account_deps.push((programdata_address, account)); + account_deps + .push((programdata_address, programdata_account)); } else { error_counters.account_not_found += 1; return Err(TransactionError::ProgramAccountNotFound); @@ -317,9 +320,6 @@ impl Accounts { account } - } else { - // Fill in an empty account for the program slots. - AccountSharedData::default() }; accounts.push((*key, account)); } @@ -338,44 +338,46 @@ impl Accounts { let payer_account = &mut accounts[payer_index].1; if payer_account.lamports() == 0 { error_counters.account_not_found += 1; - 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; - 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, - }) - } + 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()) + } + }; + + if payer_account.lamports() < fee + min_balance { + error_counters.insufficient_funds += 1; + return Err(TransactionError::InsufficientFundsForFee); + } + 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) @@ -386,35 +388,35 @@ impl Accounts { fn load_executable_accounts( &self, ancestors: &Ancestors, - program_id: &Pubkey, + accounts: &mut Vec<(Pubkey, AccountSharedData)>, + mut program_account_index: usize, error_counters: &mut ErrorCounters, - ) -> Result> { - let mut accounts = Vec::new(); + ) -> Result> { + let mut account_indices = Vec::new(); + let mut program_id = accounts[program_account_index].0; let mut depth = 0; - let mut program_id = *program_id; - loop { - if native_loader::check_id(&program_id) { - // At the root of the chain, ready to dispatch - break; - } - + while !native_loader::check_id(&program_id) { if depth >= 5 { error_counters.call_chain_too_deep += 1; return Err(TransactionError::CallChainTooDeep); } depth += 1; - let program = match self + program_account_index = match self .accounts_db .load_with_fixed_root(ancestors, &program_id) - .map(|(account, _)| account) { - Some(program) => program, + Some((program_account, _)) => { + let account_index = accounts.len(); + accounts.push((program_id, program_account)); + account_index + } 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); @@ -429,26 +431,31 @@ impl Accounts { programdata_address, }) = program.state() { - if let Some(program) = self + let programdata_account_index = match self .accounts_db .load_with_fixed_root(ancestors, &programdata_address) - .map(|(account, _)| account) { - accounts.insert(0, (programdata_address, program)); - } else { - error_counters.account_not_found += 1; - return Err(TransactionError::ProgramAccountNotFound); - } + 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); } else { error_counters.invalid_program_for_execution += 1; return Err(TransactionError::InvalidProgramForExecution); } } - accounts.insert(0, (program_id, program)); + account_indices.insert(0, program_account_index); program_id = program_owner; } - Ok(accounts) + Ok(account_indices) } pub fn load_accounts( @@ -1468,8 +1475,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.loaders.len(), 1); - assert_eq!(loaded_transaction.loaders[0].len(), 0); + assert_eq!(loaded_transaction.program_indices.len(), 1); + assert_eq!(loaded_transaction.program_indices[0].len(), 0); } (Err(e), _nonce_rollback) => Err(e).unwrap(), } @@ -1654,15 +1661,22 @@ mod tests { assert_eq!(loaded_accounts.len(), 1); match &loaded_accounts[0] { (Ok(loaded_transaction), _nonce_rollback) => { - assert_eq!(loaded_transaction.accounts.len(), 3); + assert_eq!(loaded_transaction.accounts.len(), 6); assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1); - 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() { + 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() { // +1 to skip first not loader account - assert_eq!(*accounts_subset, accounts[i + 1]); + assert_eq!( + loaded_transaction.accounts[*program_index].0, + accounts[i + 1].0 + ); + assert_eq!( + loaded_transaction.accounts[*program_index].1, + accounts[i + 1].1 + ); } } } @@ -1752,7 +1766,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.loaders[0], vec![accounts[2].clone()]); + assert_eq!(result.accounts[result.program_indices[0][0]], accounts[2]); } #[test] @@ -1835,7 +1849,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.loaders[0], vec![accounts[5].clone()]); + assert_eq!(result.accounts[result.program_indices[0][0]], accounts[5]); // Solution 2: mark programdata as readonly message.account_keys = vec![key0, key1, key2]; // revert key change @@ -1847,14 +1861,9 @@ 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.loaders[0], - vec![ - accounts[5].clone(), - accounts[3].clone(), - accounts[4].clone() - ] - ); + 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]); } #[test] @@ -1923,8 +1932,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.loaders[0], - vec![accounts_with_upgradeable_loader[2].clone()] + result.accounts[result.program_indices[0][0]], + accounts_with_upgradeable_loader[2] ); // Solution 2: mark programdata as readonly @@ -1937,7 +1946,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.loaders[0], vec![accounts[2].clone()]); + assert_eq!(result.accounts[result.program_indices[0][0]], accounts[2]); } #[test] @@ -1952,11 +1961,17 @@ 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, - &solana_sdk::pubkey::new_rand(), - &mut error_counters + &mut vec![(keypair.pubkey(), account)], + 0, + &mut error_counters, ), Err(TransactionError::ProgramAccountNotFound) ); @@ -2282,27 +2297,21 @@ 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, - loaders: transaction_loaders0, - rent: transaction_rent0, + program_indices: vec![], + rent: 0, rent_debits: RentDebits::default(), }), None, ); - let transaction_loaders1 = vec![]; - let transaction_rent1 = 0; let loaded1 = ( Ok(LoadedTransaction { accounts: transaction_accounts1, - loaders: transaction_loaders1, - rent: transaction_rent1, + program_indices: vec![], + rent: 0, rent_debits: RentDebits::default(), }), None, @@ -2325,9 +2334,10 @@ 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, - &loaders, + &programs, loaded.as_mut_slice(), &rent_collector, &(Hash::default(), FeeCalculator::default()), @@ -2674,24 +2684,15 @@ 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, - loaders: transaction_loaders, - rent: transaction_rent, + program_indices: vec![], + rent: 0, rent_debits: RentDebits::default(), }), - nonce_rollback, + nonce_rollback.clone(), ); let mut loaded = vec![loaded]; @@ -2705,9 +2706,16 @@ 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, - &loaders, + &programs, loaded.as_mut_slice(), &rent_collector, &(next_blockhash, FeeCalculator::default()), @@ -2792,24 +2800,15 @@ 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, - loaders: transaction_loaders, - rent: transaction_rent, + program_indices: vec![], + rent: 0, rent_debits: RentDebits::default(), }), - nonce_rollback, + nonce_rollback.clone(), ); let mut loaded = vec![loaded]; @@ -2823,9 +2822,16 @@ 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, - &loaders, + &programs, loaded.as_mut_slice(), &rent_collector, &(next_blockhash, FeeCalculator::default()), diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 9a423a32d..d8f987df5 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -34,10 +34,7 @@ //! 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, - TransactionLoaders, - }, + accounts::{AccountAddressFilter, Accounts, TransactionAccounts, TransactionLoadResult}, accounts_db::{ AccountShrinkThreshold, AccountsDbConfig, ErrorCounters, SnapshotStorages, ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING, @@ -192,7 +189,6 @@ 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 @@ -2818,6 +2814,7 @@ 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(); @@ -2848,7 +2845,13 @@ impl Bank { .unwrap() .0 .ok() - .map(|loaded_transaction| loaded_transaction.accounts.into_iter().collect::>()) + .map(|loaded_transaction| { + loaded_transaction + .accounts + .into_iter() + .take(number_of_accounts) + .collect::>() + }) .unwrap_or_default(); let units_consumed = timings @@ -3129,32 +3132,19 @@ impl Bank { /// Converts Accounts into RefCell, this involves moving /// ownership by draining the source - fn accounts_to_refcells( - accounts: &mut TransactionAccounts, - loaders: &mut TransactionLoaders, - ) -> (TransactionAccountRefCells, TransactionLoaderRefCells) { + fn accounts_to_refcells(accounts: &mut TransactionAccounts) -> TransactionAccountRefCells { let account_refcells: Vec<_> = accounts .drain(..) .map(|(pubkey, account)| (pubkey, Rc::new(RefCell::new(account)))) .collect(); - 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) + account_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(( @@ -3164,16 +3154,6 @@ 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(()) } @@ -3200,11 +3180,12 @@ impl Bank { fn get_executors( &self, message: &SanitizedMessage, - loaders: &[Vec<(Pubkey, AccountSharedData)>], + accounts: &[(Pubkey, AccountSharedData)], + program_indices: &[Vec], ) -> Rc> { let mut num_executors = message.account_keys_len(); - for instruction_loaders in loaders.iter() { - num_executors += instruction_loaders.len(); + for program_indices_of_instruction in program_indices.iter() { + num_executors += program_indices_of_instruction.len(); } let mut executors = HashMap::with_capacity(num_executors); let cow_cache = self.cached_executors.read().unwrap(); @@ -3215,10 +3196,11 @@ impl Bank { 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); + 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); } } } @@ -3334,14 +3316,15 @@ impl Bank { }; if process_result.is_ok() { - let executors = - self.get_executors(tx.message(), &loaded_transaction.loaders); - - let (account_refcells, loader_refcells) = Self::accounts_to_refcells( - &mut loaded_transaction.accounts, - &mut loaded_transaction.loaders, + let executors = self.get_executors( + tx.message(), + &loaded_transaction.accounts, + &loaded_transaction.program_indices, ); + let account_refcells = + Self::accounts_to_refcells(&mut loaded_transaction.accounts); + let instruction_recorders = if enable_cpi_recording { let ix_count = tx.message().instructions().len(); let mut recorders = Vec::with_capacity(ix_count); @@ -3377,7 +3360,7 @@ impl Bank { if let Some(legacy_message) = tx.message().legacy_message() { process_result = self.message_processor.process_message( legacy_message, - &loader_refcells, + &loaded_transaction.program_indices, &account_refcells, &self.rent_collector, log_collector.clone(), @@ -3405,9 +3388,7 @@ 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); @@ -12013,12 +11994,11 @@ pub(crate) mod tests { .try_into() .unwrap(); - let loaders = &[ - vec![ - (key3, AccountSharedData::default()), - (key4, AccountSharedData::default()), - ], - vec![(key1, AccountSharedData::default())], + let program_indices = &[vec![0, 1], vec![2]]; + let accounts = &[ + (key3, AccountSharedData::default()), + (key4, AccountSharedData::default()), + (key1, AccountSharedData::default()), ]; // don't do any work if not dirty @@ -12030,7 +12010,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, loaders); + let executors = bank.get_executors(&message, accounts, program_indices); assert_eq!(executors.borrow().executors.len(), 0); // do work @@ -12041,7 +12021,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, loaders); + let executors = bank.get_executors(&message, accounts, program_indices); assert_eq!(executors.borrow().executors.len(), 4); assert!(executors.borrow().executors.contains_key(&key1)); assert!(executors.borrow().executors.contains_key(&key2)); @@ -12050,7 +12030,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, loaders); + let executors = bank.get_executors(&message, accounts, program_indices); assert_eq!(executors.borrow().executors.len(), 4); assert!(executors.borrow().executors.contains_key(&key1)); assert!(executors.borrow().executors.contains_key(&key2)); @@ -12061,7 +12041,7 @@ pub(crate) mod tests { bank.remove_executor(&key2); bank.remove_executor(&key3); bank.remove_executor(&key4); - let executors = bank.get_executors(&message, loaders); + let executors = bank.get_executors(&message, accounts, program_indices); assert_eq!(executors.borrow().executors.len(), 0); assert!(!executors.borrow().executors.contains_key(&key1)); assert!(!executors.borrow().executors.contains_key(&key2)); @@ -12082,25 +12062,26 @@ pub(crate) mod tests { let message = SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(); - let loaders = &[vec![ + let program_indices = &[vec![0, 1]]; + let accounts = &[ (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, loaders); + let executors = root.get_executors(&message, accounts, program_indices); 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, loaders); + let executors = fork1.get_executors(&message, accounts, program_indices); assert_eq!(executors.borrow().executors.len(), 1); - let executors = fork2.get_executors(&message, loaders); + let executors = fork2.get_executors(&message, accounts, program_indices); assert_eq!(executors.borrow().executors.len(), 1); let mut executors = Executors::default(); @@ -12108,16 +12089,16 @@ pub(crate) mod tests { let executors = Rc::new(RefCell::new(executors)); fork1.update_executors(executors); - let executors = fork1.get_executors(&message, loaders); + let executors = fork1.get_executors(&message, accounts, program_indices); assert_eq!(executors.borrow().executors.len(), 2); - let executors = fork2.get_executors(&message, loaders); + let executors = fork2.get_executors(&message, accounts, program_indices); assert_eq!(executors.borrow().executors.len(), 1); fork1.remove_executor(&key1); - let executors = fork1.get_executors(&message, loaders); + let executors = fork1.get_executors(&message, accounts, program_indices); assert_eq!(executors.borrow().executors.len(), 1); - let executors = fork2.get_executors(&message, loaders); + let executors = fork2.get_executors(&message, accounts, program_indices); assert_eq!(executors.borrow().executors.len(), 1); } diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 864a196a7..936e53d2e 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, - executable_accounts: &'a [(Pubkey, Rc>)], + program_indices: &[usize], accounts: &'a [(Pubkey, Rc>)], programs: &'a [(Pubkey, ProcessInstructionWithContext)], log_collector: Option>, @@ -88,15 +88,8 @@ impl<'a> ThisInvokeContext<'a> { ancestors: &'a Ancestors, blockhash: &'a Hash, fee_calculator: &'a FeeCalculator, - ) -> Self { + ) -> Result { 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 { @@ -124,20 +117,18 @@ impl<'a> ThisInvokeContext<'a> { blockhash, fee_calculator, }; - invoke_context - .invoke_stack - .push(InvokeContextStackFrame::new( - *program_id, - create_keyed_accounts_unified(&keyed_accounts), - )); - invoke_context + invoke_context.push(program_id, message, instruction, program_indices, accounts)?; + Ok(invoke_context) } } impl<'a> InvokeContext for ThisInvokeContext<'a> { fn push( &mut self, key: &Pubkey, - keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + message: &Message, + instruction: &CompiledInstruction, + program_indices: &[usize], + instruction_accounts: &[(Pubkey, Rc>)], ) -> Result<(), InstructionError> { if self.invoke_stack.len() > self.compute_budget.max_invoke_depth { return Err(InstructionError::CallDepth); @@ -154,6 +145,31 @@ 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 { @@ -264,14 +280,13 @@ 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>> { - self.accounts.iter().find_map(|(key, account)| { + fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc>)> { + for (index, (key, account)) in self.accounts.iter().enumerate().rev() { if key == pubkey { - Some(account.clone()) - } else { - None + return Some((index, account.clone())); } - }) + } + None } fn update_timing( &mut self, @@ -381,9 +396,11 @@ impl MessageProcessor { /// Verify there are no outstanding borrows pub fn verify_account_references( accounts: &[(Pubkey, Rc>)], + program_indices: &[usize], ) -> Result<(), InstructionError> { - for (_, account) in accounts.iter() { - account + for account_index in program_indices.iter() { + accounts[*account_index] + .1 .try_borrow_mut() .map_err(|_| InstructionError::AccountBorrowOutstanding)?; } @@ -396,7 +413,7 @@ impl MessageProcessor { message: &Message, instruction: &CompiledInstruction, pre_accounts: &[PreAccount], - executable_accounts: &[(Pubkey, Rc>)], + program_indices: &[usize], accounts: &[(Pubkey, Rc>)], rent: &Rent, timings: &mut ExecuteDetailsTimings, @@ -405,7 +422,7 @@ impl MessageProcessor { demote_program_write_locks: bool, ) -> Result<(), InstructionError> { // Verify all executable accounts have zero outstanding refs - Self::verify_account_references(executable_accounts)?; + Self::verify_account_references(accounts, program_indices)?; // Verify the per-account instruction results let (mut pre_sum, mut post_sum) = (0_u128, 0_u128); @@ -462,7 +479,7 @@ impl MessageProcessor { &self, message: &Message, instruction: &CompiledInstruction, - executable_accounts: &[(Pubkey, Rc>)], + program_indices: &[usize], accounts: &[(Pubkey, Rc>)], rent_collector: &RentCollector, log_collector: Option>, @@ -508,7 +525,7 @@ impl MessageProcessor { rent_collector.rent, message, instruction, - executable_accounts, + program_indices, accounts, programs, log_collector, @@ -521,7 +538,7 @@ impl MessageProcessor { ancestors, blockhash, fee_calculator, - ); + )?; self.instruction_processor.process_instruction( program_id, @@ -532,7 +549,7 @@ impl MessageProcessor { message, instruction, &invoke_context.pre_accounts, - executable_accounts, + program_indices, accounts, &rent_collector.rent, timings, @@ -554,7 +571,7 @@ impl MessageProcessor { pub fn process_message( &self, message: &Message, - loaders: &[Vec<(Pubkey, Rc>)>], + program_indices: &[Vec], accounts: &[(Pubkey, Rc>)], rent_collector: &RentCollector, log_collector: Option>, @@ -579,7 +596,7 @@ impl MessageProcessor { .execute_instruction( message, instruction, - &loaders[instruction_index], + &program_indices[instruction_index], accounts, rent_collector, log_collector.clone(), @@ -676,12 +693,21 @@ 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, &[]) { + if Err(InstructionError::CallDepth) + == invoke_context.push( + program_id, + &message, + &message.instructions[0], + &[], + &accounts, + ) + { break; } depth_reached += 1; @@ -754,11 +780,11 @@ mod tests { Rc::new(RefCell::new(AccountSharedData::default())), )]; - assert!(MessageProcessor::verify_account_references(&accounts).is_ok()); + assert!(MessageProcessor::verify_account_references(&accounts, &[0]).is_ok()); let mut _borrowed = accounts[0].1.borrow(); assert_eq!( - MessageProcessor::verify_account_references(&accounts), + MessageProcessor::verify_account_references(&accounts, &[0]), Err(InstructionError::AccountBorrowOutstanding) ); } @@ -808,6 +834,9 @@ 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(), @@ -817,12 +846,9 @@ mod tests { solana_sdk::pubkey::new_rand(), AccountSharedData::new_ref(0, 1, &mock_system_program_id), ), + (mock_system_program_id, program_account), ]; - - let account = Rc::new(RefCell::new(create_loadable_account_for_test( - "mock_system_program", - ))); - let loaders = vec![vec![(mock_system_program_id, account)]]; + let program_indices = vec![vec![2]]; let executors = Rc::new(RefCell::new(Executors::default())); let ancestors = Ancestors::default(); @@ -842,7 +868,7 @@ mod tests { let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -872,7 +898,7 @@ mod tests { let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -906,7 +932,7 @@ mod tests { let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -996,6 +1022,9 @@ 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(), @@ -1005,12 +1034,9 @@ mod tests { solana_sdk::pubkey::new_rand(), AccountSharedData::new_ref(0, 1, &mock_program_id), ), + (mock_program_id, program_account), ]; - - let account = Rc::new(RefCell::new(create_loadable_account_for_test( - "mock_system_program", - ))); - let loaders = vec![vec![(mock_program_id, account)]]; + let program_indices = vec![vec![2]]; let executors = Rc::new(RefCell::new(Executors::default())); let ancestors = Ancestors::default(); @@ -1032,7 +1058,7 @@ mod tests { ); let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -1066,7 +1092,7 @@ mod tests { ); let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -1098,7 +1124,7 @@ mod tests { let ancestors = Ancestors::default(); let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -1163,10 +1189,6 @@ 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()); @@ -1181,8 +1203,12 @@ mod tests { solana_sdk::pubkey::new_rand(), Rc::new(RefCell::new(not_owned_account)), ), - (callee_program_id, Rc::new(RefCell::new(program_account))), + ( + callee_program_id, + Rc::new(RefCell::new(program_account.clone())), + ), ]; + let program_indices = vec![2]; let compiled_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2]); let programs: Vec<(_, ProcessInstructionWithContext)> = @@ -1209,7 +1235,7 @@ mod tests { Rent::default(), &message, &compiled_instruction, - &executable_accounts, + &program_indices, &accounts, programs.as_slice(), None, @@ -1222,7 +1248,8 @@ mod tests { &ancestors, &blockhash, &fee_calculator, - ); + ) + .unwrap(); // not owned account modified by the caller (before the invoke) let caller_write_privileges = message @@ -1235,7 +1262,7 @@ mod tests { assert_eq!( InstructionProcessor::process_cross_program_instruction( &message, - &executable_accounts, + &program_indices, &accounts, &caller_write_privileges, &mut invoke_context, @@ -1270,7 +1297,7 @@ mod tests { Rent::default(), &message, &compiled_instruction, - &executable_accounts, + &program_indices, &accounts, programs.as_slice(), None, @@ -1283,7 +1310,8 @@ mod tests { &ancestors, &blockhash, &fee_calculator, - ); + ) + .unwrap(); let caller_write_privileges = message .account_keys @@ -1294,7 +1322,7 @@ mod tests { assert_eq!( InstructionProcessor::process_cross_program_instruction( &message, - &executable_accounts, + &program_indices, &accounts, &caller_write_privileges, &mut invoke_context, diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index ec9289287..3f2d2055e 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -7,6 +7,7 @@ use solana_sdk::{ hash::Hash, instruction::{CompiledInstruction, Instruction, InstructionError}, keyed_account::{create_keyed_accounts_unified, KeyedAccount}, + message::Message, pubkey::Pubkey, sysvar::Sysvar, }; @@ -55,7 +56,10 @@ pub trait InvokeContext { fn push( &mut self, key: &Pubkey, - keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + message: &Message, + instruction: &CompiledInstruction, + program_indices: &[usize], + instruction_accounts: &[(Pubkey, Rc>)], ) -> Result<(), InstructionError>; /// Pop a stack frame from the invocation stack /// @@ -95,8 +99,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; - /// Get an account by its key - fn get_account(&self, pubkey: &Pubkey) -> Option>>; + /// Find an account_index and account by its key + fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc>)>; /// Update timing fn update_timing( &mut self, @@ -440,15 +444,15 @@ pub fn mock_set_sysvar( impl<'a> InvokeContext for MockInvokeContext<'a> { fn push( &mut self, - key: &Pubkey, - keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + _key: &Pubkey, + _message: &Message, + _instruction: &CompiledInstruction, + _program_indices: &[usize], + _instruction_accounts: &[(Pubkey, Rc>)], ) -> 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)), + *_key, + create_keyed_accounts_unified(&[]), )); Ok(()) } @@ -509,10 +513,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>> { - for (key, account) in self.accounts.iter() { + fn get_account(&self, pubkey: &Pubkey) -> Option<(usize, Rc>)> { + for (index, (key, account)) in self.accounts.iter().enumerate().rev() { if key == pubkey { - return Some(account.clone()); + return Some((index, account.clone())); } } None