Stop loading program accounts if program exists in cache (#30703)

* Stop loading program accounts if program exists in cache

* load accounts for upgradeable programs

* revert loader change to conditionally use program data account

* load instruction accounts

* generate TransactionExecutorCache from loaded programs

* cleanup account_found_and_dep_index variable

* address review comments

* handle tombstones in loader

* unify tombstone constructor

* handle multiple tombstones
This commit is contained in:
Pankaj Garg 2023-03-28 11:49:56 -07:00 committed by GitHub
parent e7887cfb06
commit aebc191c38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 216 additions and 135 deletions

View File

@ -1,5 +1,5 @@
use { use {
crate::loaded_programs::LoadedProgram, crate::loaded_programs::{LoadedProgram, LoadedProgramType},
log::*, log::*,
rand::Rng, rand::Rng,
solana_sdk::{pubkey::Pubkey, saturating_add_assign, slot_history::Slot, stake_history::Epoch}, solana_sdk::{pubkey::Pubkey, saturating_add_assign, slot_history::Slot, stake_history::Epoch},
@ -42,8 +42,13 @@ impl TransactionExecutorCache {
} }
pub fn set_tombstone(&mut self, key: Pubkey, slot: Slot) { pub fn set_tombstone(&mut self, key: Pubkey, slot: Slot) {
self.visible self.visible.insert(
.insert(key, Arc::new(LoadedProgram::new_tombstone(slot))); key,
Arc::new(LoadedProgram::new_tombstone(
slot,
LoadedProgramType::Closed,
)),
);
} }
pub fn set( pub fn set(
@ -58,7 +63,13 @@ impl TransactionExecutorCache {
if delay_visibility_of_program_deployment { if delay_visibility_of_program_deployment {
// Place a tombstone in the cache so that // Place a tombstone in the cache so that
// we don't load the new version from the database as it should remain invisible // we don't load the new version from the database as it should remain invisible
self.set_tombstone(key, current_slot); self.visible.insert(
key,
Arc::new(LoadedProgram::new_tombstone(
current_slot,
LoadedProgramType::DelayVisibility,
)),
);
} else { } else {
self.visible.insert(key, executor.clone()); self.visible.insert(key, executor.clone());
} }

View File

@ -14,7 +14,6 @@ use {
solana_rbpf::vm::ContextObject, solana_rbpf::vm::ContextObject,
solana_sdk::{ solana_sdk::{
account::{AccountSharedData, ReadableAccount}, account::{AccountSharedData, ReadableAccount},
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
feature_set::{ feature_set::{
enable_early_verification_of_account_modifications, native_programs_consume_cu, enable_early_verification_of_account_modifications, native_programs_consume_cu,
FeatureSet, FeatureSet,
@ -624,37 +623,11 @@ impl<'a> InvokeContext<'a> {
ic_msg!(self, "Account {} is not executable", callee_program_id); ic_msg!(self, "Account {} is not executable", callee_program_id);
return Err(InstructionError::AccountNotExecutable); return Err(InstructionError::AccountNotExecutable);
} }
let mut program_indices = vec![];
if borrowed_program_account.get_owner() == &bpf_loader_upgradeable::id() {
if let UpgradeableLoaderState::Program {
programdata_address,
} = borrowed_program_account.get_state()?
{
if let Some(programdata_account_index) = self
.transaction_context
.find_index_of_program_account(&programdata_address)
{
program_indices.push(programdata_account_index);
} else {
ic_msg!(
self,
"Unknown upgradeable programdata account {}",
programdata_address,
);
return Err(InstructionError::MissingAccount);
}
} else {
ic_msg!(
self,
"Invalid upgradeable program account {}",
callee_program_id,
);
return Err(InstructionError::MissingAccount);
}
}
program_indices.push(borrowed_program_account.get_index_in_transaction());
Ok((instruction_accounts, program_indices)) Ok((
instruction_accounts,
vec![borrowed_program_account.get_index_in_transaction()],
))
} }
/// Processes an instruction and returns how many compute units were used /// Processes an instruction and returns how many compute units were used

View File

@ -58,7 +58,9 @@ pub trait WorkingSlot {
pub enum LoadedProgramType { pub enum LoadedProgramType {
/// Tombstone for undeployed, closed or unloadable programs /// Tombstone for undeployed, closed or unloadable programs
#[default] #[default]
Invalid, FailedVerification,
Closed,
DelayVisibility,
LegacyV0(VerifiedExecutable<RequisiteVerifier, InvokeContext<'static>>), LegacyV0(VerifiedExecutable<RequisiteVerifier, InvokeContext<'static>>),
LegacyV1(VerifiedExecutable<RequisiteVerifier, InvokeContext<'static>>), LegacyV1(VerifiedExecutable<RequisiteVerifier, InvokeContext<'static>>),
Typed(VerifiedExecutable<RequisiteVerifier, InvokeContext<'static>>), Typed(VerifiedExecutable<RequisiteVerifier, InvokeContext<'static>>),
@ -68,7 +70,11 @@ pub enum LoadedProgramType {
impl Debug for LoadedProgramType { impl Debug for LoadedProgramType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
LoadedProgramType::Invalid => write!(f, "LoadedProgramType::Invalid"), LoadedProgramType::FailedVerification => {
write!(f, "LoadedProgramType::FailedVerification")
}
LoadedProgramType::Closed => write!(f, "LoadedProgramType::Closed"),
LoadedProgramType::DelayVisibility => write!(f, "LoadedProgramType::DelayVisibility"),
LoadedProgramType::LegacyV0(_) => write!(f, "LoadedProgramType::LegacyV0"), LoadedProgramType::LegacyV0(_) => write!(f, "LoadedProgramType::LegacyV0"),
LoadedProgramType::LegacyV1(_) => write!(f, "LoadedProgramType::LegacyV1"), LoadedProgramType::LegacyV1(_) => write!(f, "LoadedProgramType::LegacyV1"),
LoadedProgramType::Typed(_) => write!(f, "LoadedProgramType::Typed"), LoadedProgramType::Typed(_) => write!(f, "LoadedProgramType::Typed"),
@ -190,18 +196,25 @@ impl LoadedProgram {
} }
} }
pub fn new_tombstone(slot: Slot) -> Self { pub fn new_tombstone(slot: Slot, reason: LoadedProgramType) -> Self {
Self { let tombstone = Self {
program: LoadedProgramType::Invalid, program: reason,
account_size: 0, account_size: 0,
deployment_slot: slot, deployment_slot: slot,
effective_slot: slot, effective_slot: slot,
usage_counter: AtomicU64::default(), usage_counter: AtomicU64::default(),
} };
debug_assert!(tombstone.is_tombstone());
tombstone
} }
pub fn is_tombstone(&self) -> bool { pub fn is_tombstone(&self) -> bool {
matches!(self.program, LoadedProgramType::Invalid) matches!(
self.program,
LoadedProgramType::FailedVerification
| LoadedProgramType::Closed
| LoadedProgramType::DelayVisibility
)
} }
} }
@ -266,7 +279,15 @@ impl LoadedPrograms {
let existing = second_level let existing = second_level
.get(index) .get(index)
.expect("Missing entry, even though position was found"); .expect("Missing entry, even though position was found");
assert!( if existing.is_tombstone()
&& entry.is_tombstone()
&& existing.deployment_slot == entry.deployment_slot
{
// If there's already a tombstone for the program at the given slot, let's return
// the existing entry instead of adding another.
return existing.clone();
}
debug_assert!(
existing.deployment_slot != entry.deployment_slot existing.deployment_slot != entry.deployment_slot
|| existing.effective_slot != entry.effective_slot || existing.effective_slot != entry.effective_slot
); );
@ -395,7 +416,13 @@ mod tests {
} }
fn set_tombstone(cache: &mut LoadedPrograms, key: Pubkey, slot: Slot) -> Arc<LoadedProgram> { fn set_tombstone(cache: &mut LoadedPrograms, key: Pubkey, slot: Slot) -> Arc<LoadedProgram> {
cache.assign_program(key, Arc::new(LoadedProgram::new_tombstone(slot))) cache.assign_program(
key,
Arc::new(LoadedProgram::new_tombstone(
slot,
LoadedProgramType::FailedVerification,
)),
)
} }
#[test] #[test]
@ -637,14 +664,17 @@ mod tests {
#[test] #[test]
fn test_tombstone() { fn test_tombstone() {
let tombstone = LoadedProgram::new_tombstone(0); let tombstone = LoadedProgram::new_tombstone(0, LoadedProgramType::FailedVerification);
assert!(matches!(tombstone.program, LoadedProgramType::Invalid)); assert!(matches!(
tombstone.program,
LoadedProgramType::FailedVerification
));
assert!(tombstone.is_tombstone()); assert!(tombstone.is_tombstone());
assert_eq!(tombstone.deployment_slot, 0); assert_eq!(tombstone.deployment_slot, 0);
assert_eq!(tombstone.effective_slot, 0); assert_eq!(tombstone.effective_slot, 0);
let tombstone = LoadedProgram::new_tombstone(100); let tombstone = LoadedProgram::new_tombstone(100, LoadedProgramType::Closed);
assert!(matches!(tombstone.program, LoadedProgramType::Invalid)); assert!(matches!(tombstone.program, LoadedProgramType::Closed));
assert!(tombstone.is_tombstone()); assert!(tombstone.is_tombstone());
assert_eq!(tombstone.deployment_slot, 100); assert_eq!(tombstone.deployment_slot, 100);
assert_eq!(tombstone.effective_slot, 100); assert_eq!(tombstone.effective_slot, 100);
@ -835,7 +865,7 @@ mod tests {
usage_counter: AtomicU64, usage_counter: AtomicU64,
) -> Arc<LoadedProgram> { ) -> Arc<LoadedProgram> {
Arc::new(LoadedProgram { Arc::new(LoadedProgram {
program: LoadedProgramType::Invalid, program: LoadedProgramType::FailedVerification,
account_size: 0, account_size: 0,
deployment_slot, deployment_slot,
effective_slot, effective_slot,

View File

@ -135,7 +135,7 @@ pub fn load_program_from_bytes(
Ok(loaded_program) Ok(loaded_program)
} }
fn get_programdata_offset_and_depoyment_offset( fn get_programdata_offset_and_deployment_offset(
log_collector: &Option<Rc<RefCell<LogCollector>>>, log_collector: &Option<Rc<RefCell<LogCollector>>>,
program: &BorrowedAccount, program: &BorrowedAccount,
programdata: &BorrowedAccount, programdata: &BorrowedAccount,
@ -181,14 +181,10 @@ pub fn load_program_from_account(
return Err(InstructionError::IncorrectProgramId); return Err(InstructionError::IncorrectProgramId);
} }
let (programdata_offset, deployment_slot) =
get_programdata_offset_and_depoyment_offset(&log_collector, program, programdata)?;
if let Some(ref tx_executor_cache) = tx_executor_cache { if let Some(ref tx_executor_cache) = tx_executor_cache {
if let Some(loaded_program) = tx_executor_cache.get(program.get_key()) { if let Some(loaded_program) = tx_executor_cache.get(program.get_key()) {
if loaded_program.is_tombstone() { if loaded_program.is_tombstone() {
// We cached that the Executor does not exist, abort // We cached that the Executor does not exist, abort
// This case can only happen once delay_visibility_of_program_deployment is active.
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
// Executor exists and is cached, use it // Executor exists and is cached, use it
@ -196,6 +192,9 @@ pub fn load_program_from_account(
} }
} }
let (programdata_offset, deployment_slot) =
get_programdata_offset_and_deployment_offset(&log_collector, program, programdata)?;
let programdata_size = if programdata_offset != 0 { let programdata_size = if programdata_offset != 0 {
programdata.get_data().len() programdata.get_data().len()
} else { } else {
@ -556,14 +555,16 @@ fn process_instruction_common(
ic_logger_msg!(log_collector, "Program is not executable"); ic_logger_msg!(log_collector, "Program is not executable");
return Err(InstructionError::IncorrectProgramId); return Err(InstructionError::IncorrectProgramId);
} }
let programdata_account = if bpf_loader_upgradeable::check_id(program_account.get_owner()) { let programdata_account = if bpf_loader_upgradeable::check_id(program_account.get_owner()) {
let programdata_account = instruction_context.try_borrow_program_account( instruction_context
transaction_context, .try_borrow_program_account(
instruction_context transaction_context,
.get_number_of_program_accounts() instruction_context
.saturating_sub(2), .get_number_of_program_accounts()
)?; .saturating_sub(2),
Some(programdata_account) )
.ok()
} else { } else {
None None
}; };
@ -591,7 +592,9 @@ fn process_instruction_common(
executor.usage_counter.fetch_add(1, Ordering::Relaxed); executor.usage_counter.fetch_add(1, Ordering::Relaxed);
match &executor.program { match &executor.program {
LoadedProgramType::Invalid => Err(InstructionError::InvalidAccountData), LoadedProgramType::FailedVerification
| LoadedProgramType::Closed
| LoadedProgramType::DelayVisibility => Err(InstructionError::InvalidAccountData),
LoadedProgramType::LegacyV0(executable) => execute(executable, invoke_context), LoadedProgramType::LegacyV0(executable) => execute(executable, invoke_context),
LoadedProgramType::LegacyV1(executable) => execute(executable, invoke_context), LoadedProgramType::LegacyV1(executable) => execute(executable, invoke_context),
_ => Err(InstructionError::IncorrectProgramId), _ => Err(InstructionError::IncorrectProgramId),

View File

@ -590,7 +590,9 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
drop(program); drop(program);
loaded_program.usage_counter.fetch_add(1, Ordering::Relaxed); loaded_program.usage_counter.fetch_add(1, Ordering::Relaxed);
match &loaded_program.program { match &loaded_program.program {
LoadedProgramType::Invalid => Err(InstructionError::InvalidAccountData), LoadedProgramType::FailedVerification
| LoadedProgramType::Closed
| LoadedProgramType::DelayVisibility => Err(InstructionError::InvalidAccountData),
LoadedProgramType::Typed(executable) => execute(invoke_context, executable), LoadedProgramType::Typed(executable) => execute(invoke_context, executable),
_ => Err(InstructionError::IncorrectProgramId), _ => Err(InstructionError::IncorrectProgramId),
} }

View File

@ -24,16 +24,21 @@ use {
transaction_error_metrics::TransactionErrorMetrics, transaction_error_metrics::TransactionErrorMetrics,
}, },
dashmap::DashMap, dashmap::DashMap,
itertools::Itertools,
log::*, log::*,
solana_address_lookup_table_program::{error::AddressLookupError, state::AddressLookupTable}, solana_address_lookup_table_program::{error::AddressLookupError, state::AddressLookupTable},
solana_program_runtime::compute_budget::{self, ComputeBudget}, solana_program_runtime::{
compute_budget::{self, ComputeBudget},
loaded_programs::{LoadedProgram, LoadedProgramType},
},
solana_sdk::{ solana_sdk::{
account::{Account, AccountSharedData, ReadableAccount, WritableAccount}, account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
account_utils::StateMut, account_utils::StateMut,
bpf_loader_upgradeable::{self, UpgradeableLoaderState}, bpf_loader_upgradeable,
clock::{BankId, Slot}, clock::{BankId, Slot},
feature_set::{ feature_set::{
self, add_set_tx_loaded_accounts_data_size_instruction, enable_request_heap_frame_ix, self, add_set_tx_loaded_accounts_data_size_instruction,
delay_visibility_of_program_deployment, enable_request_heap_frame_ix,
include_loaded_accounts_data_size_in_fee_calculation, include_loaded_accounts_data_size_in_fee_calculation,
remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix, remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix,
use_default_units_in_fee_calculation, FeatureSet, use_default_units_in_fee_calculation, FeatureSet,
@ -287,6 +292,38 @@ impl Accounts {
} }
} }
fn account_shared_data_from_program(
key: &Pubkey,
feature_set: &FeatureSet,
program: &LoadedProgram,
program_accounts: &HashMap<Pubkey, &Pubkey>,
) -> Result<AccountSharedData> {
// Check for tombstone
// Ignoring the tombstone here for now. The loader will catch this condition and return
// error.
let _ignore = match &program.program {
LoadedProgramType::FailedVerification | LoadedProgramType::Closed => {
Err(TransactionError::InvalidProgramForExecution)
}
LoadedProgramType::DelayVisibility => {
debug_assert!(feature_set.is_active(&delay_visibility_of_program_deployment::id()));
Err(TransactionError::InvalidProgramForExecution)
}
_ => Ok(()),
};
// It's an executable program account. The program is already loaded in the cache.
// So the account data is not needed. Return a dummy AccountSharedData with meta
// information.
let mut program_account = AccountSharedData::default();
let program_owner = program_accounts
.get(key)
.ok_or(TransactionError::AccountNotFound)?;
program_account.set_owner(**program_owner);
program_account.set_executable(true);
Ok(program_account)
}
#[allow(clippy::too_many_arguments)]
fn load_transaction_accounts( fn load_transaction_accounts(
&self, &self,
ancestors: &Ancestors, ancestors: &Ancestors,
@ -296,6 +333,8 @@ impl Accounts {
rent_collector: &RentCollector, rent_collector: &RentCollector,
feature_set: &FeatureSet, feature_set: &FeatureSet,
account_overrides: Option<&AccountOverrides>, account_overrides: Option<&AccountOverrides>,
program_accounts: &HashMap<Pubkey, &Pubkey>,
loaded_programs: &HashMap<Pubkey, Arc<LoadedProgram>>,
) -> Result<LoadedTransaction> { ) -> Result<LoadedTransaction> {
// NOTE: this check will never fail because `tx` is sanitized // NOTE: this check will never fail because `tx` is sanitized
if tx.signatures().is_empty() && fee != 0 { if tx.signatures().is_empty() && fee != 0 {
@ -308,7 +347,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 accounts_found = 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();
@ -319,12 +358,18 @@ impl Accounts {
Self::get_requested_loaded_accounts_data_size_limit(tx, feature_set)?; Self::get_requested_loaded_accounts_data_size_limit(tx, feature_set)?;
let mut accumulated_accounts_data_size: usize = 0; let mut accumulated_accounts_data_size: usize = 0;
let instruction_accounts = message
.instructions()
.iter()
.flat_map(|instruction| &instruction.accounts)
.unique()
.collect::<Vec<&u8>>();
let mut accounts = account_keys let mut accounts = account_keys
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, key)| { .map(|(i, key)| {
let mut account_found = true; let mut account_found = true;
let mut account_dep_index = None;
#[allow(clippy::collapsible_else_if)] #[allow(clippy::collapsible_else_if)]
let account = if solana_sdk::sysvar::instructions::check_id(key) { let account = if solana_sdk::sysvar::instructions::check_id(key) {
Self::construct_instructions_account( Self::construct_instructions_account(
@ -333,10 +378,30 @@ impl Accounts {
.is_active(&feature_set::instructions_sysvar_owned_by_sysvar::id()), .is_active(&feature_set::instructions_sysvar_owned_by_sysvar::id()),
) )
} else { } else {
let (mut account, rent) = if let Some(account_override) = let instruction_account = u8::try_from(i)
.map(|i| instruction_accounts.contains(&&i))
.unwrap_or(false);
let (account_size, mut account, rent) = if let Some(account_override) =
account_overrides.and_then(|overrides| overrides.get(key)) account_overrides.and_then(|overrides| overrides.get(key))
{ {
(account_override.clone(), 0) (account_override.data().len(), account_override.clone(), 0)
} else if let Some(program) = (!instruction_account && !message.is_writable(i))
.then_some(())
.and_then(|_| loaded_programs.get(key))
{
// This condition block does special handling for accounts that are passed
// as instruction account to any of the instructions in the transaction.
// It's been noticed that some programs are reading other program accounts
// (that are passed to the program as instruction accounts). So such accounts
// are needed to be loaded even though corresponding compiled program may
// already be present in the cache.
Self::account_shared_data_from_program(
key,
feature_set,
program,
program_accounts,
)
.map(|program_account| (program.account_size, program_account, 0))?
} else { } else {
self.accounts_db self.accounts_db
.load_with_fixed_root(ancestors, key) .load_with_fixed_root(ancestors, key)
@ -350,9 +415,9 @@ impl Accounts {
set_exempt_rent_epoch_max, set_exempt_rent_epoch_max,
) )
.rent_amount; .rent_amount;
(account, rent_due) (account.data().len(), account, rent_due)
} else { } else {
(account, 0) (account.data().len(), account, 0)
} }
}) })
.unwrap_or_else(|| { .unwrap_or_else(|| {
@ -364,12 +429,12 @@ impl Accounts {
// with this field already set would allow us to skip rent collection for these accounts. // with this field already set would allow us to skip rent collection for these accounts.
default_account.set_rent_epoch(u64::MAX); default_account.set_rent_epoch(u64::MAX);
} }
(default_account, 0) (default_account.data().len(), default_account, 0)
}) })
}; };
Self::accumulate_and_check_loaded_account_data_size( Self::accumulate_and_check_loaded_account_data_size(
&mut accumulated_accounts_data_size, &mut accumulated_accounts_data_size,
account.data().len(), account_size,
requested_loaded_accounts_data_size_limit, requested_loaded_accounts_data_size_limit,
error_counters, error_counters,
)?; )?;
@ -397,36 +462,6 @@ impl Accounts {
error_counters.invalid_writable_account += 1; error_counters.invalid_writable_account += 1;
return Err(TransactionError::InvalidWritableAccount); return Err(TransactionError::InvalidWritableAccount);
} }
if account.executable() {
// The upgradeable loader requires the derived ProgramData account
if let Ok(UpgradeableLoaderState::Program {
programdata_address,
}) = account.state()
{
if let Some((programdata_account, _)) = self
.accounts_db
.load_with_fixed_root(ancestors, &programdata_address)
{
Self::accumulate_and_check_loaded_account_data_size(
&mut accumulated_accounts_data_size,
programdata_account.data().len(),
requested_loaded_accounts_data_size_limit,
error_counters,
)?;
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) { } 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);
@ -438,7 +473,7 @@ impl Accounts {
account account
}; };
account_found_and_dep_index.push((account_found, account_dep_index)); accounts_found.push(account_found);
Ok((*key, account)) Ok((*key, account))
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
@ -468,9 +503,7 @@ impl Accounts {
let (program_id, program_account) = accounts let (program_id, program_account) = accounts
.get(program_index) .get(program_index)
.ok_or(TransactionError::ProgramAccountNotFound)?; .ok_or(TransactionError::ProgramAccountNotFound)?;
let (account_found, account_dep_index) = account_found_and_dep_index let account_found = accounts_found.get(program_index).unwrap_or(&true);
.get(program_index)
.unwrap_or(&(true, None));
if native_loader::check_id(program_id) { if native_loader::check_id(program_id) {
return Ok(account_indices); return Ok(account_indices);
} }
@ -483,9 +516,6 @@ impl Accounts {
return Err(TransactionError::InvalidProgramForExecution); return Err(TransactionError::InvalidProgramForExecution);
} }
account_indices.insert(0, program_index as IndexOfAccount); 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(); let owner_id = program_account.owner();
if native_loader::check_id(owner_id) { if native_loader::check_id(owner_id) {
return Ok(account_indices); return Ok(account_indices);
@ -641,6 +671,8 @@ impl Accounts {
feature_set: &FeatureSet, feature_set: &FeatureSet,
fee_structure: &FeeStructure, fee_structure: &FeeStructure,
account_overrides: Option<&AccountOverrides>, account_overrides: Option<&AccountOverrides>,
program_accounts: &HashMap<Pubkey, &Pubkey>,
loaded_programs: &HashMap<Pubkey, Arc<LoadedProgram>>,
) -> Vec<TransactionLoadResult> { ) -> Vec<TransactionLoadResult> {
txs.iter() txs.iter()
.zip(lock_results) .zip(lock_results)
@ -676,6 +708,8 @@ impl Accounts {
rent_collector, rent_collector,
feature_set, feature_set,
account_overrides, account_overrides,
program_accounts,
loaded_programs,
) { ) {
Ok(loaded_transaction) => loaded_transaction, Ok(loaded_transaction) => loaded_transaction,
Err(e) => return (Err(e), None), Err(e) => return (Err(e), None),
@ -1416,6 +1450,7 @@ mod tests {
solana_program_runtime::executor_cache::TransactionExecutorCache, solana_program_runtime::executor_cache::TransactionExecutorCache,
solana_sdk::{ solana_sdk::{
account::{AccountSharedData, WritableAccount}, account::{AccountSharedData, WritableAccount},
bpf_loader_upgradeable::UpgradeableLoaderState,
epoch_schedule::EpochSchedule, epoch_schedule::EpochSchedule,
genesis_config::ClusterType, genesis_config::ClusterType,
hash::Hash, hash::Hash,
@ -1500,6 +1535,8 @@ mod tests {
feature_set, feature_set,
fee_structure, fee_structure,
None, None,
&HashMap::new(),
&HashMap::new(),
) )
} }
@ -2542,10 +2579,6 @@ mod tests {
); );
assert_eq!( assert_eq!(
result.accounts[result.program_indices[0][1] as usize], result.accounts[result.program_indices[0][1] as usize],
accounts[4]
);
assert_eq!(
result.accounts[result.program_indices[0][2] as usize],
accounts[3] accounts[3]
); );
} }
@ -3272,6 +3305,8 @@ mod tests {
&FeatureSet::all_enabled(), &FeatureSet::all_enabled(),
&FeeStructure::default(), &FeeStructure::default(),
account_overrides, account_overrides,
&HashMap::new(),
&HashMap::new(),
) )
} }

View File

@ -95,7 +95,9 @@ use {
compute_budget::{self, ComputeBudget}, compute_budget::{self, ComputeBudget},
executor_cache::{BankExecutorCache, TransactionExecutorCache, MAX_CACHED_EXECUTORS}, executor_cache::{BankExecutorCache, TransactionExecutorCache, MAX_CACHED_EXECUTORS},
invoke_context::{BuiltinProgram, ProcessInstructionWithContext}, invoke_context::{BuiltinProgram, ProcessInstructionWithContext},
loaded_programs::{LoadedProgram, LoadedProgramEntry, LoadedPrograms, WorkingSlot}, loaded_programs::{
LoadedProgram, LoadedProgramEntry, LoadedProgramType, LoadedPrograms, WorkingSlot,
},
log_collector::LogCollector, log_collector::LogCollector,
sysvar_cache::SysvarCache, sysvar_cache::SysvarCache,
timings::{ExecuteTimingType, ExecuteTimings}, timings::{ExecuteTimingType, ExecuteTimings},
@ -4043,6 +4045,7 @@ impl Bank {
} }
/// Get any cached executors needed by the transaction /// Get any cached executors needed by the transaction
#[cfg(test)]
fn get_tx_executor_cache( fn get_tx_executor_cache(
&self, &self,
accounts: &[TransactionAccount], accounts: &[TransactionAccount],
@ -4197,15 +4200,8 @@ impl Bank {
timings: &mut ExecuteTimings, timings: &mut ExecuteTimings,
error_counters: &mut TransactionErrorMetrics, error_counters: &mut TransactionErrorMetrics,
log_messages_bytes_limit: Option<usize>, log_messages_bytes_limit: Option<usize>,
tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>,
) -> TransactionExecutionResult { ) -> TransactionExecutionResult {
let mut get_tx_executor_cache_time = Measure::start("get_tx_executor_cache_time");
let tx_executor_cache = self.get_tx_executor_cache(&loaded_transaction.accounts);
get_tx_executor_cache_time.stop();
saturating_add_assign!(
timings.execute_accessories.get_executors_us,
get_tx_executor_cache_time.as_us()
);
let prev_accounts_data_len = self.load_accounts_data_size(); let prev_accounts_data_len = self.load_accounts_data_size();
let transaction_accounts = std::mem::take(&mut loaded_transaction.accounts); let transaction_accounts = std::mem::take(&mut loaded_transaction.accounts);
let mut transaction_context = TransactionContext::new( let mut transaction_context = TransactionContext::new(
@ -4439,11 +4435,13 @@ impl Bank {
Err(e) => { Err(e) => {
// Create a tombstone for the program in the cache // Create a tombstone for the program in the cache
debug!("Failed to load program {}, error {:?}", pubkey, e); debug!("Failed to load program {}, error {:?}", pubkey, e);
let tombstone = self let tombstone = self.loaded_programs_cache.write().unwrap().assign_program(
.loaded_programs_cache *pubkey,
.write() Arc::new(LoadedProgram::new_tombstone(
.unwrap() self.slot,
.assign_program(*pubkey, Arc::new(LoadedProgram::new_tombstone(self.slot))); LoadedProgramType::FailedVerification,
)),
);
loaded_programs_for_txs.insert(*pubkey, tombstone); loaded_programs_for_txs.insert(*pubkey, tombstone);
} }
}); });
@ -4451,11 +4449,14 @@ impl Bank {
(program_accounts_map, loaded_programs_for_txs) (program_accounts_map, loaded_programs_for_txs)
} }
fn replenish_executor_cache( fn replenish_executor_cache<'a>(
&self, &self,
program_owners: &[&Pubkey], program_owners: &[&'a Pubkey],
sanitized_txs: &[SanitizedTransaction], sanitized_txs: &[SanitizedTransaction],
check_results: &mut [TransactionCheckResult], check_results: &mut [TransactionCheckResult],
) -> (
HashMap<Pubkey, &'a Pubkey>,
HashMap<Pubkey, Arc<LoadedProgram>>,
) { ) {
let mut filter_programs_time = Measure::start("filter_programs_accounts"); let mut filter_programs_time = Measure::start("filter_programs_accounts");
let program_accounts_map = self.rc.accounts.filter_executable_program_accounts( let program_accounts_map = self.rc.accounts.filter_executable_program_accounts(
@ -4467,6 +4468,7 @@ impl Bank {
); );
filter_programs_time.stop(); filter_programs_time.stop();
let mut loaded_programs_for_txs = HashMap::new();
let mut filter_missing_programs_time = Measure::start("filter_missing_programs_accounts"); let mut filter_missing_programs_time = Measure::start("filter_missing_programs_accounts");
let missing_executors = program_accounts_map let missing_executors = program_accounts_map
.keys() .keys()
@ -4475,6 +4477,10 @@ impl Bank {
.read() .read()
.unwrap() .unwrap()
.get(key) .get(key)
.map(|program| {
loaded_programs_for_txs.insert(*key, program.clone());
program
})
.is_none() .is_none()
.then_some(key) .then_some(key)
}) })
@ -4484,15 +4490,27 @@ impl Bank {
let executors = missing_executors let executors = missing_executors
.iter() .iter()
.map(|pubkey| match self.load_program(pubkey) { .map(|pubkey| match self.load_program(pubkey) {
Ok(program) => (**pubkey, program), Ok(program) => {
loaded_programs_for_txs.insert(**pubkey, program.clone());
(**pubkey, program)
}
// Create a tombstone for the programs that failed to load // Create a tombstone for the programs that failed to load
Err(_) => (**pubkey, Arc::new(LoadedProgram::new_tombstone(self.slot))), Err(_) => {
let tombstone = Arc::new(LoadedProgram::new_tombstone(
self.slot,
LoadedProgramType::FailedVerification,
));
loaded_programs_for_txs.insert(**pubkey, tombstone.clone());
(**pubkey, tombstone)
}
}); });
// avoid locking the cache if there are no new executors // avoid locking the cache if there are no new executors
if executors.len() > 0 { if executors.len() > 0 {
self.executor_cache.write().unwrap().put(executors); self.executor_cache.write().unwrap().put(executors);
} }
(program_accounts_map, loaded_programs_for_txs)
} }
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
@ -4561,7 +4579,6 @@ impl Bank {
bpf_loader_upgradeable::id(), bpf_loader_upgradeable::id(),
bpf_loader::id(), bpf_loader::id(),
bpf_loader_deprecated::id(), bpf_loader_deprecated::id(),
native_loader::id(),
]; ];
let program_owners_refs: Vec<&Pubkey> = program_owners.iter().collect(); let program_owners_refs: Vec<&Pubkey> = program_owners.iter().collect();
@ -4574,7 +4591,12 @@ impl Bank {
&check_results, &check_results,
); );
*/ */
self.replenish_executor_cache(&program_owners_refs, sanitized_txs, &mut check_results); let (executable_programs_in_tx_batch, loaded_programs_map) =
self.replenish_executor_cache(&program_owners_refs, sanitized_txs, &mut check_results);
let tx_executor_cache = Rc::new(RefCell::new(TransactionExecutorCache::new(
loaded_programs_map.clone().into_iter(),
)));
let mut load_time = Measure::start("accounts_load"); let mut load_time = Measure::start("accounts_load");
let mut loaded_transactions = self.rc.accounts.load_accounts( let mut loaded_transactions = self.rc.accounts.load_accounts(
@ -4587,6 +4609,8 @@ impl Bank {
&self.feature_set, &self.feature_set,
&self.fee_structure, &self.fee_structure,
account_overrides, account_overrides,
&executable_programs_in_tx_batch,
&loaded_programs_map,
); );
load_time.stop(); load_time.stop();
@ -4643,6 +4667,7 @@ impl Bank {
timings, timings,
&mut error_counters, &mut error_counters,
log_messages_bytes_limit, log_messages_bytes_limit,
tx_executor_cache.clone(),
) )
} }
}) })

View File

@ -11740,6 +11740,8 @@ fn test_rent_state_list_len() {
&bank.feature_set, &bank.feature_set,
&FeeStructure::default(), &FeeStructure::default(),
None, None,
&HashMap::new(),
&HashMap::new(),
); );
let compute_budget = bank.runtime_config.compute_budget.unwrap_or_else(|| { let compute_budget = bank.runtime_config.compute_budget.unwrap_or_else(|| {