diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 21d7b5b1b..546d1fb9d 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -211,26 +211,6 @@ impl MessageProcessor { ) } - /// Visit each unique instruction account index once - pub fn visit_instruction_accounts_once( - instruction: &CompiledInstruction, - work: &mut dyn FnMut(usize, usize) -> Result<(), InstructionError>, - ) -> Result<(), InstructionError> { - let mut unique_index = 0; - 'root: for (i, account_index) in instruction.accounts.iter().enumerate() { - // Note: This is an O(n^2) algorithm, - // but performed on a very small slice and requires no heap allocations - for account_index_before in instruction.accounts[..i].iter() { - if account_index_before == account_index { - continue 'root; // skip dups - } - } - work(unique_index, *account_index as usize)?; - unique_index += 1; - } - Ok(()) - } - /// Record the initial state of the accounts so that they can be compared /// after the instruction is processed pub fn create_pre_accounts( @@ -247,7 +227,7 @@ impl MessageProcessor { pre_accounts.push(PreAccount::new(&account, is_writable, program_id)); Ok(()) }; - let _ = Self::visit_instruction_accounts_once(instruction, &mut work); + let _ = instruction.visit_each_account(&mut work); } pre_accounts } @@ -292,7 +272,7 @@ impl MessageProcessor { post_sum += u128::from(account.lamports); Ok(()) }; - Self::visit_instruction_accounts_once(instruction, &mut work)?; + instruction.visit_each_account(&mut work)?; } // Verify that the total sum of all the lamports did not change diff --git a/sdk/src/instruction.rs b/sdk/src/instruction.rs index a4717a54b..2516ba9e7 100644 --- a/sdk/src/instruction.rs +++ b/sdk/src/instruction.rs @@ -201,6 +201,26 @@ impl CompiledInstruction { pub fn program_id<'a>(&self, program_ids: &'a [Pubkey]) -> &'a Pubkey { &program_ids[self.program_id_index as usize] } + + /// Visit each unique instruction account index once + pub fn visit_each_account( + &self, + work: &mut dyn FnMut(usize, usize) -> Result<(), InstructionError>, + ) -> Result<(), InstructionError> { + let mut unique_index = 0; + 'root: for (i, account_index) in self.accounts.iter().enumerate() { + // Note: This is an O(n^2) algorithm, + // but performed on a very small slice and requires no heap allocations + for account_index_before in self.accounts[..i].iter() { + if account_index_before == account_index { + continue 'root; // skip dups + } + } + work(unique_index, *account_index as usize)?; + unique_index += 1; + } + Ok(()) + } } #[cfg(test)] @@ -236,4 +256,27 @@ mod test { assert!(metas[1].is_signer); assert_eq!(metas[1].pubkey, signer_pubkey); } + + #[test] + fn test_visit_each_account() { + let do_work = |accounts: &[u8]| -> (usize, usize) { + let mut unique_total = 0; + let mut account_total = 0; + let mut work = |unique_index: usize, account_index: usize| { + unique_total += unique_index; + account_total += account_index; + Ok(()) + }; + let instruction = CompiledInstruction::new(0, &[0], accounts.to_vec()); + instruction.visit_each_account(&mut work).unwrap(); + + (unique_total, account_total) + }; + + assert_eq!((6, 6), do_work(&[0, 1, 2, 3])); + assert_eq!((6, 6), do_work(&[0, 1, 1, 2, 3])); + assert_eq!((6, 6), do_work(&[0, 1, 2, 3, 3])); + assert_eq!((6, 6), do_work(&[0, 0, 1, 1, 2, 2, 3, 3])); + assert_eq!((0, 2), do_work(&[2, 2])); + } }