Refactor - Simplify program accounts in transaction loading (#29728)
* Refactors the "!validated_fee_payer" case from an "else" branch to an early "return". * Moves the early return upward. * Removes empty entries. * Adds account_found_and_dep_index. * cargo fmt. * Replaces call site of load_executable_accounts(). * Adjusts number of total loaded accounts in test_load_accounts_multiple_loaders(). * Removes test_accounts_account_not_found(). * Removes load_executable_accounts(). * Refactor back to built-in loader ownership chain loop.
This commit is contained in:
parent
5e35823b66
commit
aa2e3487ba
|
@ -258,6 +258,7 @@ impl Accounts {
|
||||||
let mut tx_rent: TransactionRent = 0;
|
let mut tx_rent: TransactionRent = 0;
|
||||||
let message = tx.message();
|
let message = tx.message();
|
||||||
let account_keys = message.account_keys();
|
let account_keys = message.account_keys();
|
||||||
|
let mut account_found_and_dep_index = Vec::with_capacity(account_keys.len());
|
||||||
let mut account_deps = Vec::with_capacity(account_keys.len());
|
let mut account_deps = Vec::with_capacity(account_keys.len());
|
||||||
let mut rent_debits = RentDebits::default();
|
let mut rent_debits = RentDebits::default();
|
||||||
|
|
||||||
|
@ -267,113 +268,119 @@ impl Accounts {
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, key)| {
|
.map(|(i, key)| {
|
||||||
let account = if !message.is_non_loader_key(i) {
|
let mut account_found = true;
|
||||||
// Fill in an empty account for the program slots.
|
let mut account_dep_index = None;
|
||||||
AccountSharedData::default()
|
#[allow(clippy::collapsible_else_if)]
|
||||||
|
let account = if solana_sdk::sysvar::instructions::check_id(key) {
|
||||||
|
Self::construct_instructions_account(
|
||||||
|
message,
|
||||||
|
feature_set
|
||||||
|
.is_active(&feature_set::instructions_sysvar_owned_by_sysvar::id()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
#[allow(clippy::collapsible_else_if)]
|
let (mut account, rent) = if let Some(account_override) =
|
||||||
if solana_sdk::sysvar::instructions::check_id(key) {
|
account_overrides.and_then(|overrides| overrides.get(key))
|
||||||
Self::construct_instructions_account(
|
{
|
||||||
message,
|
(account_override.clone(), 0)
|
||||||
feature_set
|
|
||||||
.is_active(&feature_set::instructions_sysvar_owned_by_sysvar::id()),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
let (mut account, rent) = if let Some(account_override) =
|
self.accounts_db
|
||||||
account_overrides.and_then(|overrides| overrides.get(key))
|
.load_with_fixed_root(ancestors, key)
|
||||||
{
|
.map(|(mut account, _)| {
|
||||||
(account_override.clone(), 0)
|
if message.is_writable(i) {
|
||||||
} else {
|
let rent_due = rent_collector
|
||||||
self.accounts_db
|
.collect_from_existing_account(
|
||||||
.load_with_fixed_root(ancestors, key)
|
key,
|
||||||
.map(|(mut account, _)| {
|
&mut account,
|
||||||
if message.is_writable(i) {
|
self.accounts_db.filler_account_suffix.as_ref(),
|
||||||
let rent_due = rent_collector
|
set_exempt_rent_epoch_max,
|
||||||
.collect_from_existing_account(
|
)
|
||||||
key,
|
.rent_amount;
|
||||||
&mut account,
|
(account, rent_due)
|
||||||
self.accounts_db.filler_account_suffix.as_ref(),
|
} else {
|
||||||
set_exempt_rent_epoch_max,
|
(account, 0)
|
||||||
)
|
}
|
||||||
.rent_amount;
|
})
|
||||||
(account, rent_due)
|
.unwrap_or_else(|| {
|
||||||
} else {
|
account_found = false;
|
||||||
(account, 0)
|
let mut default_account = AccountSharedData::default();
|
||||||
}
|
if set_exempt_rent_epoch_max {
|
||||||
})
|
// All new accounts must be rent-exempt (enforced in Bank::execute_loaded_transaction).
|
||||||
.unwrap_or_else(|| {
|
// Currently, rent collection sets rent_epoch to u64::MAX, but initializing the account
|
||||||
let mut default_account = AccountSharedData::default();
|
// with this field already set would allow us to skip rent collection for these accounts.
|
||||||
if set_exempt_rent_epoch_max {
|
default_account.set_rent_epoch(u64::MAX);
|
||||||
// All new accounts must be rent-exempt (enforced in Bank::execute_loaded_transaction).
|
}
|
||||||
// Currently, rent collection sets rent_epoch to u64::MAX, but initializing the account
|
(default_account, 0)
|
||||||
// with this field already set would allow us to skip rent collection for these accounts.
|
})
|
||||||
default_account.set_rent_epoch(u64::MAX);
|
};
|
||||||
}
|
|
||||||
(default_account, 0)
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
if !validated_fee_payer {
|
if !validated_fee_payer && message.is_non_loader_key(i) {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
warn!("Payer index should be 0! {:?}", tx);
|
warn!("Payer index should be 0! {:?}", tx);
|
||||||
}
|
|
||||||
|
|
||||||
Self::validate_fee_payer(
|
|
||||||
key,
|
|
||||||
&mut account,
|
|
||||||
i as IndexOfAccount,
|
|
||||||
error_counters,
|
|
||||||
rent_collector,
|
|
||||||
feature_set,
|
|
||||||
fee,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
validated_fee_payer = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if bpf_loader_upgradeable::check_id(account.owner()) {
|
Self::validate_fee_payer(
|
||||||
if message.is_writable(i) && !message.is_upgradeable_loader_present() {
|
key,
|
||||||
error_counters.invalid_writable_account += 1;
|
&mut account,
|
||||||
return Err(TransactionError::InvalidWritableAccount);
|
i as IndexOfAccount,
|
||||||
}
|
error_counters,
|
||||||
|
rent_collector,
|
||||||
|
feature_set,
|
||||||
|
fee,
|
||||||
|
)?;
|
||||||
|
|
||||||
if account.executable() {
|
validated_fee_payer = true;
|
||||||
// The upgradeable loader requires the derived ProgramData account
|
}
|
||||||
if let Ok(UpgradeableLoaderState::Program {
|
|
||||||
programdata_address,
|
if bpf_loader_upgradeable::check_id(account.owner()) {
|
||||||
}) = account.state()
|
if message.is_writable(i) && !message.is_upgradeable_loader_present() {
|
||||||
{
|
|
||||||
if let Some((programdata_account, _)) = self
|
|
||||||
.accounts_db
|
|
||||||
.load_with_fixed_root(ancestors, &programdata_address)
|
|
||||||
{
|
|
||||||
account_deps
|
|
||||||
.push((programdata_address, programdata_account));
|
|
||||||
} else {
|
|
||||||
error_counters.account_not_found += 1;
|
|
||||||
return Err(TransactionError::ProgramAccountNotFound);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error_counters.invalid_program_for_execution += 1;
|
|
||||||
return Err(TransactionError::InvalidProgramForExecution);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if account.executable() && message.is_writable(i) {
|
|
||||||
error_counters.invalid_writable_account += 1;
|
error_counters.invalid_writable_account += 1;
|
||||||
return Err(TransactionError::InvalidWritableAccount);
|
return Err(TransactionError::InvalidWritableAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
tx_rent += rent;
|
if account.executable() {
|
||||||
rent_debits.insert(key, rent, account.lamports());
|
// The upgradeable loader requires the derived ProgramData account
|
||||||
|
if let Ok(UpgradeableLoaderState::Program {
|
||||||
account
|
programdata_address,
|
||||||
|
}) = account.state()
|
||||||
|
{
|
||||||
|
if let Some((programdata_account, _)) = self
|
||||||
|
.accounts_db
|
||||||
|
.load_with_fixed_root(ancestors, &programdata_address)
|
||||||
|
{
|
||||||
|
account_dep_index =
|
||||||
|
Some(account_keys.len().saturating_add(account_deps.len())
|
||||||
|
as IndexOfAccount);
|
||||||
|
account_deps.push((programdata_address, programdata_account));
|
||||||
|
} else {
|
||||||
|
error_counters.account_not_found += 1;
|
||||||
|
return Err(TransactionError::ProgramAccountNotFound);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_counters.invalid_program_for_execution += 1;
|
||||||
|
return Err(TransactionError::InvalidProgramForExecution);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if account.executable() && message.is_writable(i) {
|
||||||
|
error_counters.invalid_writable_account += 1;
|
||||||
|
return Err(TransactionError::InvalidWritableAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tx_rent += rent;
|
||||||
|
rent_debits.insert(key, rent, account.lamports());
|
||||||
|
|
||||||
|
account
|
||||||
};
|
};
|
||||||
|
|
||||||
|
account_found_and_dep_index.push((account_found, account_dep_index));
|
||||||
Ok((*key, account))
|
Ok((*key, account))
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
if !validated_fee_payer {
|
||||||
|
error_counters.account_not_found += 1;
|
||||||
|
return Err(TransactionError::AccountNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
// Appends the account_deps at the end of the accounts,
|
// Appends the account_deps at the end of the accounts,
|
||||||
// this way they can be accessed in a uniform way.
|
// this way they can be accessed in a uniform way.
|
||||||
// At places where only the accounts are needed,
|
// At places where only the accounts are needed,
|
||||||
|
@ -381,30 +388,70 @@ impl Accounts {
|
||||||
// accounts.iter().take(message.account_keys.len())
|
// accounts.iter().take(message.account_keys.len())
|
||||||
accounts.append(&mut account_deps);
|
accounts.append(&mut account_deps);
|
||||||
|
|
||||||
if validated_fee_payer {
|
let builtins_start_index = accounts.len();
|
||||||
let program_indices = message
|
let program_indices = message
|
||||||
.instructions()
|
.instructions()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|instruction| {
|
.map(|instruction| {
|
||||||
self.load_executable_accounts(
|
let mut account_indices = Vec::new();
|
||||||
ancestors,
|
let mut program_index = instruction.program_id_index as usize;
|
||||||
&mut accounts,
|
for _ in 0..5 {
|
||||||
instruction.program_id_index as IndexOfAccount,
|
let (program_id, program_account) = accounts
|
||||||
error_counters,
|
.get(program_index)
|
||||||
)
|
.ok_or(TransactionError::ProgramAccountNotFound)?;
|
||||||
})
|
let (account_found, account_dep_index) = account_found_and_dep_index
|
||||||
.collect::<Result<Vec<Vec<IndexOfAccount>>>>()?;
|
.get(program_index)
|
||||||
|
.unwrap_or(&(true, None));
|
||||||
Ok(LoadedTransaction {
|
if native_loader::check_id(program_id) {
|
||||||
accounts,
|
return Ok(account_indices);
|
||||||
program_indices,
|
}
|
||||||
rent: tx_rent,
|
if !account_found {
|
||||||
rent_debits,
|
error_counters.account_not_found += 1;
|
||||||
|
return Err(TransactionError::ProgramAccountNotFound);
|
||||||
|
}
|
||||||
|
if !program_account.executable() {
|
||||||
|
error_counters.invalid_program_for_execution += 1;
|
||||||
|
return Err(TransactionError::InvalidProgramForExecution);
|
||||||
|
}
|
||||||
|
account_indices.insert(0, program_index as IndexOfAccount);
|
||||||
|
if let Some(account_index) = account_dep_index {
|
||||||
|
account_indices.insert(0, *account_index);
|
||||||
|
}
|
||||||
|
let owner_id = program_account.owner();
|
||||||
|
if native_loader::check_id(owner_id) {
|
||||||
|
return Ok(account_indices);
|
||||||
|
}
|
||||||
|
program_index = if let Some(owner_index) = accounts
|
||||||
|
.get(builtins_start_index..)
|
||||||
|
.ok_or(TransactionError::ProgramAccountNotFound)?
|
||||||
|
.iter()
|
||||||
|
.position(|(key, _)| key == owner_id)
|
||||||
|
{
|
||||||
|
builtins_start_index.saturating_add(owner_index)
|
||||||
|
} else {
|
||||||
|
let owner_index = accounts.len();
|
||||||
|
if let Some((program_account, _)) =
|
||||||
|
self.accounts_db.load_with_fixed_root(ancestors, owner_id)
|
||||||
|
{
|
||||||
|
accounts.push((*owner_id, program_account));
|
||||||
|
} else {
|
||||||
|
error_counters.account_not_found += 1;
|
||||||
|
return Err(TransactionError::ProgramAccountNotFound);
|
||||||
|
}
|
||||||
|
owner_index
|
||||||
|
};
|
||||||
|
}
|
||||||
|
error_counters.call_chain_too_deep += 1;
|
||||||
|
Err(TransactionError::CallChainTooDeep)
|
||||||
})
|
})
|
||||||
} else {
|
.collect::<Result<Vec<Vec<IndexOfAccount>>>>()?;
|
||||||
error_counters.account_not_found += 1;
|
|
||||||
Err(TransactionError::AccountNotFound)
|
Ok(LoadedTransaction {
|
||||||
}
|
accounts,
|
||||||
|
program_indices,
|
||||||
|
rent: tx_rent,
|
||||||
|
rent_debits,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_fee_payer(
|
fn validate_fee_payer(
|
||||||
|
@ -453,84 +500,6 @@ impl Accounts {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_executable_accounts(
|
|
||||||
&self,
|
|
||||||
ancestors: &Ancestors,
|
|
||||||
accounts: &mut Vec<TransactionAccount>,
|
|
||||||
mut program_account_index: IndexOfAccount,
|
|
||||||
error_counters: &mut TransactionErrorMetrics,
|
|
||||||
) -> Result<Vec<IndexOfAccount>> {
|
|
||||||
let mut account_indices = Vec::new();
|
|
||||||
let mut program_id = match accounts.get(program_account_index as usize) {
|
|
||||||
Some(program_account) => program_account.0,
|
|
||||||
None => {
|
|
||||||
error_counters.account_not_found += 1;
|
|
||||||
return Err(TransactionError::ProgramAccountNotFound);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut depth = 0;
|
|
||||||
while !native_loader::check_id(&program_id) {
|
|
||||||
if depth >= 5 {
|
|
||||||
error_counters.call_chain_too_deep += 1;
|
|
||||||
return Err(TransactionError::CallChainTooDeep);
|
|
||||||
}
|
|
||||||
depth += 1;
|
|
||||||
|
|
||||||
program_account_index = match self
|
|
||||||
.accounts_db
|
|
||||||
.load_with_fixed_root(ancestors, &program_id)
|
|
||||||
{
|
|
||||||
Some((program_account, _)) => {
|
|
||||||
let account_index = accounts.len() as IndexOfAccount;
|
|
||||||
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 as usize].1;
|
|
||||||
if !program.executable() {
|
|
||||||
error_counters.invalid_program_for_execution += 1;
|
|
||||||
return Err(TransactionError::InvalidProgramForExecution);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add loader to chain
|
|
||||||
let program_owner = *program.owner();
|
|
||||||
account_indices.insert(0, program_account_index);
|
|
||||||
if bpf_loader_upgradeable::check_id(&program_owner) {
|
|
||||||
// The upgradeable loader requires the derived ProgramData account
|
|
||||||
if let Ok(UpgradeableLoaderState::Program {
|
|
||||||
programdata_address,
|
|
||||||
}) = program.state()
|
|
||||||
{
|
|
||||||
let programdata_account_index = match self
|
|
||||||
.accounts_db
|
|
||||||
.load_with_fixed_root(ancestors, &programdata_address)
|
|
||||||
{
|
|
||||||
Some((programdata_account, _)) => {
|
|
||||||
let account_index = accounts.len() as IndexOfAccount;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
program_id = program_owner;
|
|
||||||
}
|
|
||||||
Ok(account_indices)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn load_accounts(
|
pub fn load_accounts(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1952,7 +1921,7 @@ mod tests {
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
match &loaded_accounts[0] {
|
match &loaded_accounts[0] {
|
||||||
(Ok(loaded_transaction), _nonce) => {
|
(Ok(loaded_transaction), _nonce) => {
|
||||||
assert_eq!(loaded_transaction.accounts.len(), 6);
|
assert_eq!(loaded_transaction.accounts.len(), 4);
|
||||||
assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
|
assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
|
||||||
assert_eq!(loaded_transaction.program_indices.len(), 2);
|
assert_eq!(loaded_transaction.program_indices.len(), 2);
|
||||||
assert_eq!(loaded_transaction.program_indices[0].len(), 1);
|
assert_eq!(loaded_transaction.program_indices[0].len(), 1);
|
||||||
|
@ -2403,34 +2372,6 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_accounts_account_not_found() {
|
|
||||||
let accounts = Accounts::new_with_config_for_tests(
|
|
||||||
Vec::new(),
|
|
||||||
&ClusterType::Development,
|
|
||||||
AccountSecondaryIndexes::default(),
|
|
||||||
AccountShrinkThreshold::default(),
|
|
||||||
);
|
|
||||||
let mut error_counters = TransactionErrorMetrics::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_for_tests(0, &keypair.pubkey(), &account);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
accounts.load_executable_accounts(
|
|
||||||
&ancestors,
|
|
||||||
&mut vec![(keypair.pubkey(), account)],
|
|
||||||
0,
|
|
||||||
&mut error_counters,
|
|
||||||
),
|
|
||||||
Err(TransactionError::ProgramAccountNotFound)
|
|
||||||
);
|
|
||||||
assert_eq!(error_counters.account_not_found, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_accounts_empty_bank_hash() {
|
fn test_accounts_empty_bank_hash() {
|
||||||
let accounts = Accounts::new_with_config_for_tests(
|
let accounts = Accounts::new_with_config_for_tests(
|
||||||
|
|
Loading…
Reference in New Issue