Optimize account verification (#8385)
This commit is contained in:
@ -19,7 +19,6 @@ use libloading::os::windows::*;
|
||||
|
||||
// The relevant state of an account before an Instruction executes, used
|
||||
// to verify account integrity after the Instruction completes
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PreAccount {
|
||||
pub is_writable: bool,
|
||||
pub lamports: u64,
|
||||
@ -54,8 +53,6 @@ impl PreAccount {
|
||||
}
|
||||
|
||||
pub fn verify(&self, program_id: &Pubkey, post: &Account) -> Result<(), InstructionError> {
|
||||
// Verify the transaction
|
||||
|
||||
// Only the owner of the account may change owner and
|
||||
// only if the account is writable and
|
||||
// only if the data is zero-initialized or empty
|
||||
@ -221,6 +218,35 @@ impl MessageProcessor {
|
||||
)
|
||||
}
|
||||
|
||||
/// Record the initial state of the accounts so that it can be compared
|
||||
// after the instruction is processed
|
||||
pub fn create_pre_accounts(
|
||||
// program_id: &Pubkey,
|
||||
message: &Message,
|
||||
instruction: &CompiledInstruction,
|
||||
program_accounts: &[Rc<RefCell<Account>>],
|
||||
) -> Vec<Option<PreAccount>> {
|
||||
let program_id = instruction.program_id(&message.account_keys);
|
||||
|
||||
// Copy only what we need to verify after instruction processing
|
||||
let mut pre_accounts = Vec::with_capacity(program_accounts.len());
|
||||
'root: for (i, account) in program_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_after in program_accounts.iter().skip(i + 1) {
|
||||
if Rc::ptr_eq(account, account_after) {
|
||||
pre_accounts.push(None);
|
||||
continue 'root; // don't verify duplicates
|
||||
}
|
||||
}
|
||||
let is_writable = message.is_writable(instruction.accounts[i] as usize);
|
||||
let account = account.borrow();
|
||||
pre_accounts.push(Some(PreAccount::new(&account, is_writable, program_id)))
|
||||
}
|
||||
pre_accounts
|
||||
}
|
||||
|
||||
/// Verify there are no outstanding borrows
|
||||
pub fn verify_account_references(
|
||||
executable_accounts: &[(Pubkey, RefCell<Account>)],
|
||||
program_accounts: &[Rc<RefCell<Account>>],
|
||||
@ -238,33 +264,30 @@ impl MessageProcessor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify the results of an instruction
|
||||
pub fn verify(
|
||||
program_id: &Pubkey,
|
||||
pre_accounts: &[PreAccount],
|
||||
message: &Message,
|
||||
instruction: &CompiledInstruction,
|
||||
pre_accounts: &[Option<PreAccount>],
|
||||
executable_accounts: &[(Pubkey, RefCell<Account>)],
|
||||
program_accounts: &[Rc<RefCell<Account>>],
|
||||
) -> Result<(), InstructionError> {
|
||||
let program_id = instruction.program_id(&message.account_keys);
|
||||
|
||||
// Verify all accounts have zero outstanding refs
|
||||
Self::verify_account_references(executable_accounts, program_accounts)?;
|
||||
|
||||
// Verify the per-account instruction results
|
||||
let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
|
||||
'root: for (i, (pre_account, account)) in
|
||||
pre_accounts.iter().zip(program_accounts).enumerate()
|
||||
{
|
||||
// Note: This is an O(n^2) algorithm,
|
||||
// but performed on a very small slice and requires no heap allocations
|
||||
for account_after in program_accounts.iter().skip(i + 1) {
|
||||
if Rc::ptr_eq(account, account_after) {
|
||||
continue 'root; // don't verify duplicates
|
||||
}
|
||||
for (pre_account, account) in pre_accounts.iter().zip(program_accounts) {
|
||||
if let Some(pre_account) = pre_account {
|
||||
let account = account
|
||||
.try_borrow()
|
||||
.map_err(|_| InstructionError::AccountBorrowFailed)?;
|
||||
pre_account.verify(&program_id, &account)?;
|
||||
pre_sum += u128::from(pre_account.lamports);
|
||||
post_sum += u128::from(account.lamports);
|
||||
}
|
||||
let account = account
|
||||
.try_borrow()
|
||||
.map_err(|_| InstructionError::AccountBorrowFailed)?;
|
||||
pre_account.verify(&program_id, &account)?;
|
||||
pre_sum += u128::from(pre_account.lamports);
|
||||
post_sum += u128::from(account.lamports);
|
||||
}
|
||||
|
||||
// Verify that the total sum of all the lamports did not change
|
||||
@ -286,28 +309,15 @@ impl MessageProcessor {
|
||||
program_accounts: &[Rc<RefCell<Account>>],
|
||||
) -> Result<(), InstructionError> {
|
||||
assert_eq!(instruction.accounts.len(), program_accounts.len());
|
||||
let program_id = instruction.program_id(&message.account_keys);
|
||||
// Copy only what we need to verify after instruction processing
|
||||
let pre_accounts: Vec<_> = program_accounts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, account)| {
|
||||
let is_writable = message.is_writable(instruction.accounts[i] as usize);
|
||||
let account = account.borrow();
|
||||
PreAccount::new(&account, is_writable, program_id)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let pre_accounts = Self::create_pre_accounts(message, instruction, program_accounts);
|
||||
self.process_instruction(message, instruction, executable_accounts, program_accounts)?;
|
||||
|
||||
// Verify the instruction results
|
||||
Self::verify(
|
||||
&program_id,
|
||||
message,
|
||||
instruction,
|
||||
&pre_accounts,
|
||||
executable_accounts,
|
||||
program_accounts,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user