diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 62b1230fee..d91fa1fcef 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -127,17 +127,13 @@ impl Accounts { // If a fee can pay for execution then the program will be scheduled let mut accounts: TransactionAccounts = Vec::with_capacity(message.account_keys.len()); let mut tx_rent: TransactionRent = 0; - for (i, key) in message - .account_keys - .iter() - .enumerate() - .filter(|(_, key)| !message.program_ids().contains(key)) - { + for (i, key) in message.account_keys.iter().enumerate().filter(|(i, key)| { + !message.program_ids().contains(key) || message.is_key_passed_to_program(*i) + }) { let (account, rent) = AccountsDB::load(storage, ancestors, accounts_index, key) .and_then(|(mut account, _)| { - let rent_due: u64; - if message.is_writable(i) { - rent_due = rent_collector.update(&mut account); + if message.is_writable(i) && !account.executable { + let rent_due = rent_collector.update(&mut account); Some((account, rent_due)) } else { Some((account, 0)) @@ -1125,12 +1121,12 @@ mod tests { let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters); - assert_eq!(error_counters.invalid_account_for_fee, 1); + assert_eq!(error_counters.account_not_found, 1); assert_eq!(loaded_accounts.len(), 1); assert_eq!( loaded_accounts[0], ( - Err(TransactionError::InvalidAccountForFee), + Err(TransactionError::AccountNotFound), Some(HashAgeKind::Extant) ) ); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 000f7467df..7e06bf30dd 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -5566,4 +5566,46 @@ mod tests { assert_eq!(bank.get_balance(&from_pubkey), 80); assert_eq!(bank.get_balance(&to_pubkey), 20); } + + #[test] + fn test_transaction_with_program_ids_passed_to_programs() { + let (genesis_config, mint_keypair) = create_genesis_config(500); + let mut bank = Bank::new(&genesis_config); + + fn mock_process_instruction( + _program_id: &Pubkey, + _keyed_accounts: &[KeyedAccount], + _data: &[u8], + ) -> result::Result<(), InstructionError> { + Ok(()) + } + + let mock_program_id = Pubkey::new(&[2u8; 32]); + bank.add_instruction_processor(mock_program_id, mock_process_instruction); + + let from_pubkey = Pubkey::new_rand(); + let to_pubkey = Pubkey::new_rand(); + let dup_pubkey = from_pubkey.clone(); + let from_account = Account::new(100, 1, &mock_program_id); + let to_account = Account::new(0, 1, &mock_program_id); + bank.store_account(&from_pubkey, &from_account); + bank.store_account(&to_pubkey, &to_account); + + let account_metas = vec![ + AccountMeta::new(from_pubkey, false), + AccountMeta::new(to_pubkey, false), + AccountMeta::new(dup_pubkey, false), + AccountMeta::new(mock_program_id, false), + ]; + let instruction = Instruction::new(mock_program_id, &10, account_metas); + let tx = Transaction::new_signed_with_payer( + vec![instruction], + Some(&mint_keypair.pubkey()), + &[&mint_keypair], + bank.last_blockhash(), + ); + + let result = bank.process_transaction(&tx); + assert_eq!(result, Ok(())); + } } diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 578407e740..5a2fd2ad8f 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -336,9 +336,6 @@ impl MessageProcessor { .ok_or(TransactionError::InvalidAccountIndex)?; let executable_accounts = &loaders[executable_index]; - // TODO: panics on an index out of bounds if an executable - // account is also included as a regular account for an instruction, because the - // executable account is not passed in as part of the accounts slice let program_accounts: Vec<_> = instruction .accounts .iter() diff --git a/sdk/src/message.rs b/sdk/src/message.rs index f444ca7867..386fcd46a4 100644 --- a/sdk/src/message.rs +++ b/sdk/src/message.rs @@ -7,6 +7,7 @@ use crate::{ short_vec, system_instruction, }; use itertools::Itertools; +use std::convert::TryFrom; fn position(keys: &[Pubkey], key: &Pubkey) -> u8 { keys.iter().position(|k| k == key).unwrap() as u8 @@ -233,6 +234,17 @@ impl Message { .collect() } + pub fn is_key_passed_to_program(&self, index: usize) -> bool { + if let Ok(index) = u8::try_from(index) { + for ix in self.instructions.iter() { + if ix.accounts.contains(&index) { + return true; + } + } + } + false + } + pub fn program_position(&self, index: usize) -> Option { let program_ids = self.program_ids(); program_ids