Fix duplicate account translation in CPI (#23701)

* Use visit_each_account_once() in CPI syscall to prevent duplicate accounts from being translated twice.

* remove unwrap() from runtime

Co-authored-by: Jack May <jack@solana.com>
This commit is contained in:
Alexander Meißner
2022-03-16 23:45:01 +01:00
committed by GitHub
parent be0aeea01a
commit dda3a463a2
2 changed files with 99 additions and 76 deletions

View File

@ -352,7 +352,11 @@ impl<'a> InvokeContext<'a> {
}
Err(InstructionError::MissingAccount)
};
visit_each_account_once(instruction_accounts, &mut work)?;
visit_each_account_once(
instruction_accounts,
&mut work,
InstructionError::NotEnoughAccountKeys,
)?;
} else {
let contains = (0..self
.transaction_context
@ -517,7 +521,11 @@ impl<'a> InvokeContext<'a> {
Ok(())
};
visit_each_account_once(instruction_accounts, &mut work)?;
visit_each_account_once(
instruction_accounts,
&mut work,
InstructionError::NotEnoughAccountKeys,
)?;
// Verify that the total sum of all the lamports did not change
if pre_sum != post_sum {
@ -615,7 +623,11 @@ impl<'a> InvokeContext<'a> {
}
Err(InstructionError::MissingAccount)
};
visit_each_account_once(instruction_accounts, &mut work)?;
visit_each_account_once(
instruction_accounts,
&mut work,
InstructionError::NotEnoughAccountKeys,
)?;
// Verify that the total sum of all the lamports did not change
if pre_sum != post_sum {
@ -1154,23 +1166,25 @@ pub fn mock_process_instruction(
}
/// Visit each unique instruction account index once
fn visit_each_account_once(
pub fn visit_each_account_once<E>(
instruction_accounts: &[InstructionAccount],
work: &mut dyn FnMut(usize, &InstructionAccount) -> Result<(), InstructionError>,
) -> Result<(), InstructionError> {
work: &mut dyn FnMut(usize, &InstructionAccount) -> Result<(), E>,
inner_error: E,
) -> Result<(), E> {
// Note: This is an O(n^2) algorithm,
// but performed on a very small slice and requires no heap allocations
'root: for (index, instruction_account) 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 before in instruction_accounts
.get(..index)
.ok_or(InstructionError::NotEnoughAccountKeys)?
.iter()
{
if before.index_in_transaction == instruction_account.index_in_transaction {
continue 'root; // skip dups
match instruction_accounts.get(..index) {
Some(range) => {
for before in range.iter() {
if before.index_in_transaction == instruction_account.index_in_transaction {
continue 'root; // skip dups
}
}
work(index, instruction_account)?;
}
None => return Err(inner_error),
}
work(index, instruction_account)?;
}
Ok(())
}
@ -1211,7 +1225,8 @@ mod tests {
index_sum_b += entry.index_in_transaction;
Ok(())
};
visit_each_account_once(accounts, &mut work).unwrap();
visit_each_account_once(accounts, &mut work, InstructionError::NotEnoughAccountKeys)
.unwrap();
(unique_entries, index_sum_a, index_sum_b)
};