From 322697e2f40460a101ac63e229ad3c65b24dd449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Fri, 3 Sep 2021 09:31:40 +0200 Subject: [PATCH 1/6] Appends loaders / executable_accounts to accounts in transaction loading. --- runtime/src/accounts.rs | 6 +++++- runtime/src/bank.rs | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 71e1999959d4bd..9612430b65970c 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -366,6 +366,10 @@ impl Accounts { .program_instructions_iter() .map(|(program_id, _ix)| { self.load_executable_accounts(ancestors, program_id, error_counters) + .map(|loaders| { + accounts.append(&mut loaders.clone()); + loaders + }) }) .collect::>()?; Ok(LoadedTransaction { @@ -1654,7 +1658,7 @@ 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); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 310fabb99a76d6..2b63eea7bb7342 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -2818,6 +2818,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 +2849,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 From 22c91b7fd33d2a344ed33d009a43625aa91e2449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 6 Sep 2021 12:03:25 +0200 Subject: [PATCH 2/6] Adds indices to loaders / executable_accounts. --- program-runtime/src/instruction_processor.rs | 44 +++++++++------- program-test/src/lib.rs | 16 +++--- programs/bpf_loader/src/syscalls.rs | 43 +++++++++------ runtime/src/accounts.rs | 12 ++++- runtime/src/bank.rs | 23 ++++---- runtime/src/message_processor.rs | 55 ++++++++++---------- sdk/src/process_instruction.rs | 10 ++-- 7 files changed, 119 insertions(+), 84 deletions(-) diff --git a/program-runtime/src/instruction_processor.rs b/program-runtime/src/instruction_processor.rs index 49c6381be1e4b2..38f93dcc16fd23 100644 --- a/program-runtime/src/instruction_processor.rs +++ b/program-runtime/src/instruction_processor.rs @@ -346,13 +346,15 @@ impl InstructionProcessor { pub fn create_keyed_accounts<'a>( message: &'a Message, instruction: &'a CompiledInstruction, - executable_accounts: &'a [(Pubkey, Rc>)], + executable_accounts: &'a [(Pubkey, Rc>, usize)], 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)) + .map(|(key, account, _account_index)| { + (false, false, key, account as &RefCell) + }) .chain(instruction.accounts.iter().map(|index| { let index = *index as usize; ( @@ -535,13 +537,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 +551,20 @@ impl InstructionProcessor { ); return Err(InstructionError::AccountNotExecutable); } - let programdata = if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { + let mut executable_accounts = 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) + { + executable_accounts.push(( + programdata_address, + programdata_account, + programdata_account_index, + )); } else { ic_msg!( invoke_context, @@ -573,13 +581,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); } + executable_accounts.insert( + 0, + (callee_program_id, program_account, program_account_index), + ); ( message, executable_accounts, @@ -646,7 +652,7 @@ impl InstructionProcessor { /// This method calls the instruction's program entrypoint function pub fn process_cross_program_instruction( message: &Message, - executable_accounts: &[(Pubkey, Rc>)], + executable_accounts: &[(Pubkey, Rc>, usize)], accounts: &[(Pubkey, Rc>)], caller_write_privileges: &[bool], invoke_context: &mut dyn InvokeContext, diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index da1ef80d86ecb1..3dd9059437df1b 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -274,6 +274,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 +284,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 +299,15 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { message.account_keys.len(), "Missing or not enough accounts passed to invoke" ); + let program_account = ai_to_a(program_account_info()); + let (program_account_index, found_program_account) = + invoke_context.get_account(&program_id).unwrap(); + assert_eq!(program_account, *(found_program_account.borrow())); + let executables = vec![( + program_id, + Rc::new(RefCell::new(program_account)), + program_account_index, + )]; // Check Signers for account_info in account_infos { diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index c09727475d1ad9..e4bb97b2e8a5a1 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,20 @@ fn get_upgradeable_executable( callee_program_id: &Pubkey, program_account: &Rc>, invoke_context: &Ref<&mut dyn InvokeContext>, -) -> Result>)>, EbpfError> { +) -> Result>, usize)>, 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, account)) = + invoke_context.get_account(&programdata_address) + { + Ok(Some(( + programdata_address, + account, + programdata_account_index, + ))) } else { ic_msg!( invoke_context, @@ -2442,16 +2449,22 @@ 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, found_program_account) = + invoke_context.get_account(&callee_program_id).ok_or( + SyscallError::InstructionError(InstructionError::MissingAccount), + )?; + assert_eq!(program_account, found_program_account); + 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 mut executables = vec![(callee_program_id, program_account, program_account_index)]; + if let Some(programdata_executable) = programdata_executable { + executables.push(programdata_executable); } // Record the instruction diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 9612430b65970c..9d3e3e2282f73f 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -101,7 +101,7 @@ pub struct Accounts { // for the load instructions pub type TransactionAccounts = Vec<(Pubkey, AccountSharedData)>; pub type TransactionRent = u64; -pub type TransactionLoaders = Vec>; +pub type TransactionLoaders = Vec>; #[derive(PartialEq, Debug, Clone)] pub struct LoadedTransaction { pub accounts: TransactionAccounts, @@ -367,8 +367,15 @@ impl Accounts { .map(|(program_id, _ix)| { self.load_executable_accounts(ancestors, program_id, error_counters) .map(|loaders| { + let base_index = accounts.len(); accounts.append(&mut loaders.clone()); loaders + .into_iter() + .enumerate() + .map(|(index, (key, account))| { + (key, account, base_index + index) + }) + .collect::>() }) }) .collect::>()?; @@ -1666,7 +1673,8 @@ mod tests { for loaders in loaded_transaction.loaders.iter() { for (i, accounts_subset) in loaders.iter().enumerate() { // +1 to skip first not loader account - assert_eq!(*accounts_subset, accounts[i + 1]); + assert_eq!(accounts_subset.0, accounts[i + 1].0); + assert_eq!(accounts_subset.1, accounts[i + 1].1); } } } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 2b63eea7bb7342..9f9d57fec819a2 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -192,7 +192,7 @@ type BankStatusCache = StatusCache>; #[frozen_abi(digest = "5Br3PNyyX1L7XoS4jYLt5JTeMXowLSsu7v9LhokC8vnq")] pub type BankSlotDelta = SlotDelta>; type TransactionAccountRefCells = Vec<(Pubkey, Rc>)>; -type TransactionLoaderRefCells = Vec>)>>; +type TransactionLoaderRefCells = Vec>, usize)>>; // Eager rent collection repeats in cyclic manner. // Each cycle is composed of number of tiny pubkey subranges @@ -3148,7 +3148,9 @@ impl Bank { .iter_mut() .map(|v| { v.drain(..) - .map(|(pubkey, account)| (pubkey, Rc::new(RefCell::new(account)))) + .map(|(pubkey, account, account_index)| { + (pubkey, Rc::new(RefCell::new(account)), account_index) + }) .collect() }) .collect(); @@ -3172,12 +3174,13 @@ impl Bank { )) } for (ls, mut lrcs) in loaders.iter_mut().zip(loader_refcells) { - for (pubkey, lrc) in lrcs.drain(..) { + for (pubkey, lrc, account_index) in lrcs.drain(..) { ls.push(( pubkey, Rc::try_unwrap(lrc) .map_err(|_| TransactionError::AccountBorrowOutstanding)? .into_inner(), + account_index, )) } } @@ -3207,7 +3210,7 @@ impl Bank { fn get_executors( &self, message: &SanitizedMessage, - loaders: &[Vec<(Pubkey, AccountSharedData)>], + loaders: &[Vec<(Pubkey, AccountSharedData, usize)>], ) -> Rc> { let mut num_executors = message.account_keys_len(); for instruction_loaders in loaders.iter() { @@ -3223,7 +3226,7 @@ impl Bank { } } for instruction_loaders in loaders.iter() { - for (key, _) in instruction_loaders.iter() { + for (key, _account, _account_index) in instruction_loaders.iter() { if let Some(executor) = cache.get(key) { executors.insert(*key, executor); } @@ -12015,10 +12018,10 @@ pub(crate) mod tests { let loaders = &[ vec![ - (key3, AccountSharedData::default()), - (key4, AccountSharedData::default()), + (key3, AccountSharedData::default(), 3), + (key4, AccountSharedData::default(), 4), ], - vec![(key1, AccountSharedData::default())], + vec![(key1, AccountSharedData::default(), 2)], ]; // don't do any work if not dirty @@ -12083,8 +12086,8 @@ pub(crate) mod tests { SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(); let loaders = &[vec![ - (key1, AccountSharedData::default()), - (key2, AccountSharedData::default()), + (key1, AccountSharedData::default(), 0), + (key2, AccountSharedData::default(), 1), ]]; // add one to root bank diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 864a196a71b67f..06ac42edb2f598 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>)], + executable_accounts: &'a [(Pubkey, Rc>, usize)], accounts: &'a [(Pubkey, Rc>)], programs: &'a [(Pubkey, ProcessInstructionWithContext)], log_collector: Option>, @@ -264,14 +264,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, @@ -380,9 +379,9 @@ impl MessageProcessor { /// Verify there are no outstanding borrows pub fn verify_account_references( - accounts: &[(Pubkey, Rc>)], + accounts: &[(Pubkey, Rc>, usize)], ) -> Result<(), InstructionError> { - for (_, account) in accounts.iter() { + for (_key, account, _account_index) in accounts.iter() { account .try_borrow_mut() .map_err(|_| InstructionError::AccountBorrowOutstanding)?; @@ -396,7 +395,7 @@ impl MessageProcessor { message: &Message, instruction: &CompiledInstruction, pre_accounts: &[PreAccount], - executable_accounts: &[(Pubkey, Rc>)], + executable_accounts: &[(Pubkey, Rc>, usize)], accounts: &[(Pubkey, Rc>)], rent: &Rent, timings: &mut ExecuteDetailsTimings, @@ -462,7 +461,7 @@ impl MessageProcessor { &self, message: &Message, instruction: &CompiledInstruction, - executable_accounts: &[(Pubkey, Rc>)], + executable_accounts: &[(Pubkey, Rc>, usize)], accounts: &[(Pubkey, Rc>)], rent_collector: &RentCollector, log_collector: Option>, @@ -554,7 +553,7 @@ impl MessageProcessor { pub fn process_message( &self, message: &Message, - loaders: &[Vec<(Pubkey, Rc>)>], + loaders: &[Vec<(Pubkey, Rc>, usize)>], accounts: &[(Pubkey, Rc>)], rent_collector: &RentCollector, log_collector: Option>, @@ -752,6 +751,7 @@ mod tests { let accounts = vec![( solana_sdk::pubkey::new_rand(), Rc::new(RefCell::new(AccountSharedData::default())), + 0, )]; assert!(MessageProcessor::verify_account_references(&accounts).is_ok()); @@ -808,6 +808,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 +820,9 @@ mod tests { solana_sdk::pubkey::new_rand(), AccountSharedData::new_ref(0, 1, &mock_system_program_id), ), + (mock_system_program_id, program_account.clone()), ]; - - let account = Rc::new(RefCell::new(create_loadable_account_for_test( - "mock_system_program", - ))); - let loaders = vec![vec![(mock_system_program_id, account)]]; + let loaders = vec![vec![(mock_system_program_id, program_account, 2)]]; let executors = Rc::new(RefCell::new(Executors::default())); let ancestors = Ancestors::default(); @@ -996,6 +996,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 +1008,9 @@ mod tests { solana_sdk::pubkey::new_rand(), AccountSharedData::new_ref(0, 1, &mock_program_id), ), + (mock_program_id, program_account.clone()), ]; - - let account = Rc::new(RefCell::new(create_loadable_account_for_test( - "mock_system_program", - ))); - let loaders = vec![vec![(mock_program_id, account)]]; + let loaders = vec![vec![(mock_program_id, program_account, 2)]]; let executors = Rc::new(RefCell::new(Executors::default())); let ancestors = Ancestors::default(); @@ -1163,10 +1163,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 +1177,13 @@ 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 executable_accounts = + vec![(callee_program_id, Rc::new(RefCell::new(program_account)), 2)]; let compiled_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2]); let programs: Vec<(_, ProcessInstructionWithContext)> = diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index ec92892873498f..d2e83ae85f6df0 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -95,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; - /// 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, @@ -509,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>> { - 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 From 56e12117decc79f4d57481090e4a5f06c54f8d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 6 Sep 2021 12:12:54 +0200 Subject: [PATCH 3/6] Moves MessageProcessor::create_keyed_accounts() into InvokeContext::push(). --- program-runtime/src/instruction_processor.rs | 40 ++-------- runtime/src/message_processor.rs | 79 ++++++++++++++------ sdk/src/process_instruction.rs | 22 ++++-- 3 files changed, 78 insertions(+), 63 deletions(-) diff --git a/program-runtime/src/instruction_processor.rs b/program-runtime/src/instruction_processor.rs index 38f93dcc16fd23..81621ec7866ac4 100644 --- a/program-runtime/src/instruction_processor.rs +++ b/program-runtime/src/instruction_processor.rs @@ -342,31 +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>, usize)], - accounts: &'a [(Pubkey, Rc>)], - demote_program_write_locks: bool, - ) -> Vec<(bool, bool, &'a Pubkey, &'a RefCell)> { - executable_accounts - .iter() - .map(|(key, account, _account_index)| { - (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( @@ -663,19 +638,14 @@ 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( + // Invoke callee + invoke_context.push( + program_id, message, instruction, executable_accounts, accounts, - demote_program_write_locks, - ); - - // Invoke callee - invoke_context.push(program_id, &keyed_accounts)?; + )?; let mut instruction_processor = InstructionProcessor::default(); for (program_id, process_instruction) in invoke_context.get_programs().iter() { @@ -689,6 +659,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(); diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 06ac42edb2f598..e8caab0d633a8e 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -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,24 @@ 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, + executable_accounts, + 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, + executable_indices: &[(Pubkey, Rc>, usize)], + instruction_accounts: &[(Pubkey, Rc>)], ) -> Result<(), InstructionError> { if self.invoke_stack.len() > self.compute_budget.max_invoke_depth { return Err(InstructionError::CallDepth); @@ -154,6 +151,35 @@ 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 = executable_indices + .iter() + .map(|(key, account, account_index)| { + assert_eq!(*key, self.accounts[*account_index].0); + assert_eq!(*account, self.accounts[*account_index].1); + ( + false, + false, + key, + account as &RefCell, + // &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 { @@ -520,7 +546,7 @@ impl MessageProcessor { ancestors, blockhash, fee_calculator, - ); + )?; self.instruction_processor.process_instruction( program_id, @@ -675,12 +701,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; @@ -1223,7 +1258,8 @@ mod tests { &ancestors, &blockhash, &fee_calculator, - ); + ) + .unwrap(); // not owned account modified by the caller (before the invoke) let caller_write_privileges = message @@ -1284,7 +1320,8 @@ mod tests { &ancestors, &blockhash, &fee_calculator, - ); + ) + .unwrap(); let caller_write_privileges = message .account_keys diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index d2e83ae85f6df0..3df90189553659 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, + executable_indices: &[(Pubkey, Rc>, usize)], + instruction_accounts: &[(Pubkey, Rc>)], ) -> Result<(), InstructionError>; /// Pop a stack frame from the invocation stack /// @@ -438,17 +442,19 @@ pub fn mock_set_sysvar( } impl<'a> InvokeContext for MockInvokeContext<'a> { + #[allow(unreachable_code)] // TODO fn push( &mut self, - key: &Pubkey, - keyed_accounts: &[(bool, bool, &Pubkey, &RefCell)], + _key: &Pubkey, + _message: &Message, + _instruction: &CompiledInstruction, + _executable_indices: &[(Pubkey, Rc>, usize)], + _instruction_accounts: &[(Pubkey, Rc>)], ) -> Result<(), InstructionError> { - fn transmute_lifetime<'a, 'b>(value: Vec>) -> Vec> { - unsafe { std::mem::transmute(value) } - } + panic!("unimplemented"); self.invoke_stack.push(InvokeContextStackFrame::new( - *key, - transmute_lifetime(create_keyed_accounts_unified(keyed_accounts)), + *_key, + create_keyed_accounts_unified(&[]), )); Ok(()) } From 5df29f97c03bbdb0a626da46631f08f8b7ad348f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Wed, 8 Sep 2021 11:21:26 +0200 Subject: [PATCH 4/6] Removes "executable_accounts", now referenced by transaction wide index into "accounts". --- program-runtime/src/instruction_processor.rs | 31 ++---- program-test/src/lib.rs | 20 +--- programs/bpf_loader/src/syscalls.rs | 29 ++--- runtime/src/accounts.rs | 110 +++++++++--------- runtime/src/bank.rs | 111 +++++++------------ runtime/src/message_processor.rs | 82 ++++++-------- sdk/src/process_instruction.rs | 6 +- 7 files changed, 154 insertions(+), 235 deletions(-) diff --git a/program-runtime/src/instruction_processor.rs b/program-runtime/src/instruction_processor.rs index 81621ec7866ac4..53bdd13738c422 100644 --- a/program-runtime/src/instruction_processor.rs +++ b/program-runtime/src/instruction_processor.rs @@ -467,7 +467,7 @@ impl InstructionProcessor { let ( message, - executable_accounts, + program_indices, accounts, keyed_account_indices_reordered, caller_write_privileges, @@ -526,20 +526,16 @@ impl InstructionProcessor { ); return Err(InstructionError::AccountNotExecutable); } - let mut executable_accounts = vec![]; + 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((programdata_account_index, programdata_account)) = + if let Some((programdata_account_index, _programdata_account)) = invoke_context.get_account(&programdata_address) { - executable_accounts.push(( - programdata_address, - programdata_account, - programdata_account_index, - )); + program_indices.push(programdata_account_index); } else { ic_msg!( invoke_context, @@ -557,13 +553,10 @@ impl InstructionProcessor { return Err(InstructionError::MissingAccount); } } - executable_accounts.insert( - 0, - (callee_program_id, program_account, program_account_index), - ); + program_indices.insert(0, program_account_index); ( message, - executable_accounts, + program_indices, accounts, keyed_account_indices_reordered, caller_write_privileges, @@ -573,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())), @@ -627,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>, usize)], + program_indices: &[usize], accounts: &[(Pubkey, Rc>)], caller_write_privileges: &[bool], invoke_context: &mut dyn InvokeContext, @@ -639,13 +632,7 @@ impl InstructionProcessor { invoke_context.verify_and_update(instruction, accounts, caller_write_privileges)?; // Invoke callee - invoke_context.push( - program_id, - message, - instruction, - executable_accounts, - 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() { diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index 3dd9059437df1b..92e11756016ca1 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 @@ -299,15 +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 = ai_to_a(program_account_info()); - let (program_account_index, found_program_account) = + let (program_account_index, _program_account) = invoke_context.get_account(&program_id).unwrap(); - assert_eq!(program_account, *(found_program_account.borrow())); - let executables = vec![( - program_id, - Rc::new(RefCell::new(program_account)), - program_account_index, - )]; + let program_indices = vec![program_account_index]; // Check Signers for account_info in account_infos { @@ -335,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 e4bb97b2e8a5a1..fa61ce366e9f6d 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -2322,20 +2322,16 @@ fn get_upgradeable_executable( callee_program_id: &Pubkey, program_account: &Rc>, invoke_context: &Ref<&mut dyn InvokeContext>, -) -> Result>, usize)>, 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, account)) = + if let Some((programdata_account_index, _programdata_account)) = invoke_context.get_account(&programdata_address) { - Ok(Some(( - programdata_address, - account, - programdata_account_index, - ))) + Ok(Some(programdata_account_index)) } else { ic_msg!( invoke_context, @@ -2371,7 +2367,7 @@ fn call<'a>( ) -> Result> { let ( message, - executables, + program_indices, accounts, account_refs, caller_write_privileges, @@ -2454,17 +2450,16 @@ fn call<'a>( })? .1 .clone(); - let (program_account_index, found_program_account) = + let (program_account_index, _program_account) = invoke_context.get_account(&callee_program_id).ok_or( SyscallError::InstructionError(InstructionError::MissingAccount), )?; - assert_eq!(program_account, found_program_account); - let programdata_executable = - get_upgradeable_executable(&callee_program_id, &program_account, &invoke_context)?; - let mut executables = vec![(callee_program_id, program_account, program_account_index)]; - if let Some(programdata_executable) = programdata_executable { - executables.push(programdata_executable); + 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 @@ -2473,7 +2468,7 @@ fn call<'a>( ( message, - executables, + program_indices, accounts, account_refs, caller_write_privileges, @@ -2486,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 9d3e3e2282f73f..0bea867c523fc5 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, } @@ -362,26 +362,21 @@ impl Accounts { .map_err(|_| TransactionError::InsufficientFundsForFee)?; let message = tx.message(); - let loaders = message + let program_indices = message .program_instructions_iter() .map(|(program_id, _ix)| { self.load_executable_accounts(ancestors, program_id, error_counters) - .map(|loaders| { + .map(|programs| { let base_index = accounts.len(); - accounts.append(&mut loaders.clone()); - loaders - .into_iter() - .enumerate() - .map(|(index, (key, account))| { - (key, account, base_index + index) - }) + accounts.append(&mut programs.clone()); + (base_index..base_index + programs.len()) .collect::>() }) }) - .collect::>()?; + .collect::>>>()?; Ok(LoadedTransaction { accounts, - loaders, + program_indices, rent: tx_rent, rent_debits, }) @@ -1479,8 +1474,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(), } @@ -1667,14 +1662,20 @@ mod tests { (Ok(loaded_transaction), _nonce_rollback) => { 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.0, accounts[i + 1].0); - assert_eq!(accounts_subset.1, accounts[i + 1].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 + ); } } } @@ -2294,27 +2295,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, @@ -2337,9 +2332,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()), @@ -2686,24 +2682,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]; @@ -2717,9 +2704,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()), @@ -2804,24 +2798,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]; @@ -2835,9 +2820,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 9f9d57fec819a2..0da2c1b7ec1cf1 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>, usize)>>; // Eager rent collection repeats in cyclic manner. // Each cycle is composed of number of tiny pubkey subranges @@ -3136,34 +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, account_index)| { - (pubkey, Rc::new(RefCell::new(account)), account_index) - }) - .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(( @@ -3173,17 +3154,6 @@ impl Bank { .into_inner(), )) } - for (ls, mut lrcs) in loaders.iter_mut().zip(loader_refcells) { - for (pubkey, lrc, account_index) in lrcs.drain(..) { - ls.push(( - pubkey, - Rc::try_unwrap(lrc) - .map_err(|_| TransactionError::AccountBorrowOutstanding)? - .into_inner(), - account_index, - )) - } - } Ok(()) } @@ -3210,11 +3180,12 @@ impl Bank { fn get_executors( &self, message: &SanitizedMessage, - loaders: &[Vec<(Pubkey, AccountSharedData, usize)>], + 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(); @@ -3225,10 +3196,11 @@ impl Bank { executors.insert(*key, executor); } } - for instruction_loaders in loaders.iter() { - for (key, _account, _account_index) 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); } } } @@ -3344,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); @@ -3387,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(), @@ -3415,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); @@ -12016,12 +11987,11 @@ pub(crate) mod tests { .try_into() .unwrap(); - let loaders = &[ - vec![ - (key3, AccountSharedData::default(), 3), - (key4, AccountSharedData::default(), 4), - ], - vec![(key1, AccountSharedData::default(), 2)], + 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 @@ -12033,7 +12003,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 @@ -12044,7 +12014,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)); @@ -12053,7 +12023,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)); @@ -12064,7 +12034,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)); @@ -12085,25 +12055,26 @@ pub(crate) mod tests { let message = SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(); - let loaders = &[vec![ - (key1, AccountSharedData::default(), 0), - (key2, AccountSharedData::default(), 1), - ]]; + 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(); @@ -12111,16 +12082,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 e8caab0d633a8e..936e53d2e8eff2 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>, usize)], + program_indices: &[usize], accounts: &'a [(Pubkey, Rc>)], programs: &'a [(Pubkey, ProcessInstructionWithContext)], log_collector: Option>, @@ -117,13 +117,7 @@ impl<'a> ThisInvokeContext<'a> { blockhash, fee_calculator, }; - invoke_context.push( - program_id, - message, - instruction, - executable_accounts, - accounts, - )?; + invoke_context.push(program_id, message, instruction, program_indices, accounts)?; Ok(invoke_context) } } @@ -133,7 +127,7 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { key: &Pubkey, message: &Message, instruction: &CompiledInstruction, - executable_indices: &[(Pubkey, Rc>, usize)], + program_indices: &[usize], instruction_accounts: &[(Pubkey, Rc>)], ) -> Result<(), InstructionError> { if self.invoke_stack.len() > self.compute_budget.max_invoke_depth { @@ -155,18 +149,14 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { let demote_program_write_locks = self .feature_set .is_active(&demote_program_write_locks::id()); - let keyed_accounts = executable_indices + let keyed_accounts = program_indices .iter() - .map(|(key, account, account_index)| { - assert_eq!(*key, self.accounts[*account_index].0); - assert_eq!(*account, self.accounts[*account_index].1); + .map(|account_index| { ( false, false, - key, - account as &RefCell, - // &self.accounts[*account_index].0, - // &self.accounts[*account_index].1 as &RefCell, + &self.accounts[*account_index].0, + &self.accounts[*account_index].1 as &RefCell, ) }) .chain(instruction.accounts.iter().map(|index| { @@ -405,10 +395,12 @@ impl MessageProcessor { /// Verify there are no outstanding borrows pub fn verify_account_references( - accounts: &[(Pubkey, Rc>, usize)], + accounts: &[(Pubkey, Rc>)], + program_indices: &[usize], ) -> Result<(), InstructionError> { - for (_key, account, _account_index) in accounts.iter() { - account + for account_index in program_indices.iter() { + accounts[*account_index] + .1 .try_borrow_mut() .map_err(|_| InstructionError::AccountBorrowOutstanding)?; } @@ -421,7 +413,7 @@ impl MessageProcessor { message: &Message, instruction: &CompiledInstruction, pre_accounts: &[PreAccount], - executable_accounts: &[(Pubkey, Rc>, usize)], + program_indices: &[usize], accounts: &[(Pubkey, Rc>)], rent: &Rent, timings: &mut ExecuteDetailsTimings, @@ -430,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); @@ -487,7 +479,7 @@ impl MessageProcessor { &self, message: &Message, instruction: &CompiledInstruction, - executable_accounts: &[(Pubkey, Rc>, usize)], + program_indices: &[usize], accounts: &[(Pubkey, Rc>)], rent_collector: &RentCollector, log_collector: Option>, @@ -533,7 +525,7 @@ impl MessageProcessor { rent_collector.rent, message, instruction, - executable_accounts, + program_indices, accounts, programs, log_collector, @@ -557,7 +549,7 @@ impl MessageProcessor { message, instruction, &invoke_context.pre_accounts, - executable_accounts, + program_indices, accounts, &rent_collector.rent, timings, @@ -579,7 +571,7 @@ impl MessageProcessor { pub fn process_message( &self, message: &Message, - loaders: &[Vec<(Pubkey, Rc>, usize)>], + program_indices: &[Vec], accounts: &[(Pubkey, Rc>)], rent_collector: &RentCollector, log_collector: Option>, @@ -604,7 +596,7 @@ impl MessageProcessor { .execute_instruction( message, instruction, - &loaders[instruction_index], + &program_indices[instruction_index], accounts, rent_collector, log_collector.clone(), @@ -786,14 +778,13 @@ mod tests { let accounts = vec![( solana_sdk::pubkey::new_rand(), Rc::new(RefCell::new(AccountSharedData::default())), - 0, )]; - 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) ); } @@ -855,9 +846,9 @@ mod tests { solana_sdk::pubkey::new_rand(), AccountSharedData::new_ref(0, 1, &mock_system_program_id), ), - (mock_system_program_id, program_account.clone()), + (mock_system_program_id, program_account), ]; - let loaders = vec![vec![(mock_system_program_id, program_account, 2)]]; + let program_indices = vec![vec![2]]; let executors = Rc::new(RefCell::new(Executors::default())); let ancestors = Ancestors::default(); @@ -877,7 +868,7 @@ mod tests { let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -907,7 +898,7 @@ mod tests { let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -941,7 +932,7 @@ mod tests { let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -1043,9 +1034,9 @@ mod tests { solana_sdk::pubkey::new_rand(), AccountSharedData::new_ref(0, 1, &mock_program_id), ), - (mock_program_id, program_account.clone()), + (mock_program_id, program_account), ]; - let loaders = vec![vec![(mock_program_id, program_account, 2)]]; + let program_indices = vec![vec![2]]; let executors = Rc::new(RefCell::new(Executors::default())); let ancestors = Ancestors::default(); @@ -1067,7 +1058,7 @@ mod tests { ); let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -1101,7 +1092,7 @@ mod tests { ); let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -1133,7 +1124,7 @@ mod tests { let ancestors = Ancestors::default(); let result = message_processor.process_message( &message, - &loaders, + &program_indices, &accounts, &rent_collector, None, @@ -1217,8 +1208,7 @@ mod tests { Rc::new(RefCell::new(program_account.clone())), ), ]; - let executable_accounts = - vec![(callee_program_id, Rc::new(RefCell::new(program_account)), 2)]; + let program_indices = vec![2]; let compiled_instruction = CompiledInstruction::new(2, &(), vec![0, 1, 2]); let programs: Vec<(_, ProcessInstructionWithContext)> = @@ -1245,7 +1235,7 @@ mod tests { Rent::default(), &message, &compiled_instruction, - &executable_accounts, + &program_indices, &accounts, programs.as_slice(), None, @@ -1272,7 +1262,7 @@ mod tests { assert_eq!( InstructionProcessor::process_cross_program_instruction( &message, - &executable_accounts, + &program_indices, &accounts, &caller_write_privileges, &mut invoke_context, @@ -1307,7 +1297,7 @@ mod tests { Rent::default(), &message, &compiled_instruction, - &executable_accounts, + &program_indices, &accounts, programs.as_slice(), None, @@ -1332,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 3df90189553659..3f2d2055e36e07 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -58,7 +58,7 @@ pub trait InvokeContext { key: &Pubkey, message: &Message, instruction: &CompiledInstruction, - executable_indices: &[(Pubkey, Rc>, usize)], + program_indices: &[usize], instruction_accounts: &[(Pubkey, Rc>)], ) -> Result<(), InstructionError>; /// Pop a stack frame from the invocation stack @@ -442,16 +442,14 @@ pub fn mock_set_sysvar( } impl<'a> InvokeContext for MockInvokeContext<'a> { - #[allow(unreachable_code)] // TODO fn push( &mut self, _key: &Pubkey, _message: &Message, _instruction: &CompiledInstruction, - _executable_indices: &[(Pubkey, Rc>, usize)], + _program_indices: &[usize], _instruction_accounts: &[(Pubkey, Rc>)], ) -> Result<(), InstructionError> { - panic!("unimplemented"); self.invoke_stack.push(InvokeContextStackFrame::new( *_key, create_keyed_accounts_unified(&[]), From 4902e3c211fe906b42ead72b90f8703b2d62d791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Tue, 7 Sep 2021 10:44:25 +0200 Subject: [PATCH 5/6] Removes create_pre_accounts() from InstructionProcessor, as it is already in MessageProcessor. --- program-runtime/src/instruction_processor.rs | 22 -------------------- 1 file changed, 22 deletions(-) diff --git a/program-runtime/src/instruction_processor.rs b/program-runtime/src/instruction_processor.rs index 53bdd13738c422..42fb4b87df1b70 100644 --- a/program-runtime/src/instruction_processor.rs +++ b/program-runtime/src/instruction_processor.rs @@ -663,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( From f04289226c04c0fd0f20add5f7cc6f0d45154218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Thu, 9 Sep 2021 10:39:07 +0200 Subject: [PATCH 6/6] Collect program account indices directly in load_executable_accounts(). --- runtime/src/accounts.rs | 174 ++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 86 deletions(-) diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 0bea867c523fc5..8051361f5bd617 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -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,50 +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 program_indices = message - .program_instructions_iter() - .map(|(program_id, _ix)| { - self.load_executable_accounts(ancestors, program_id, error_counters) - .map(|programs| { - let base_index = accounts.len(); - accounts.append(&mut programs.clone()); - (base_index..base_index + programs.len()) - .collect::>() - }) - }) - .collect::>>>()?; - Ok(LoadedTransaction { - accounts, - program_indices, - 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) @@ -392,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); @@ -435,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( @@ -1765,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] @@ -1848,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 @@ -1860,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] @@ -1936,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 @@ -1950,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] @@ -1965,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) );