SVM: Split transaction processing code into its own struct (#35044)
* Split transaction processing code into its own struct * define and implement callback trait
This commit is contained in:
parent
9935c2b5e7
commit
cb0f13ef07
|
@ -86,6 +86,7 @@ use {
|
|||
AccountShrinkThreshold, AccountStorageEntry, AccountsDb, AccountsDbConfig,
|
||||
CalcAccountsHashDataSource, VerifyAccountsHashAndLamportsConfig,
|
||||
},
|
||||
accounts_file::MatchAccountOwnerError,
|
||||
accounts_hash::{
|
||||
AccountHash, AccountsHash, CalcAccountsHashConfig, HashStats, IncrementalAccountsHash,
|
||||
},
|
||||
|
@ -576,15 +577,14 @@ impl PartialEq for Bank {
|
|||
freeze_started: _,
|
||||
vote_only_bank: _,
|
||||
cost_tracker: _,
|
||||
sysvar_cache: _,
|
||||
accounts_data_size_initial: _,
|
||||
accounts_data_size_delta_on_chain: _,
|
||||
accounts_data_size_delta_off_chain: _,
|
||||
fee_structure: _,
|
||||
incremental_snapshot_persistence: _,
|
||||
loaded_programs_cache: _,
|
||||
check_program_modification_slot: _,
|
||||
epoch_reward_status: _,
|
||||
transaction_processor: _,
|
||||
// Ignore new fields explicitly if they do not impact PartialEq.
|
||||
// Adding ".." will remove compile-time checks that if a new field
|
||||
// is added to the struct, this PartialEq is accordingly updated.
|
||||
|
@ -824,8 +824,6 @@ pub struct Bank {
|
|||
|
||||
cost_tracker: RwLock<CostTracker>,
|
||||
|
||||
sysvar_cache: RwLock<SysvarCache>,
|
||||
|
||||
/// The initial accounts data size at the start of this Bank, before processing any transactions/etc
|
||||
accounts_data_size_initial: u64,
|
||||
/// The change to accounts data size in this Bank, due on-chain events (i.e. transactions)
|
||||
|
@ -844,9 +842,9 @@ pub struct Bank {
|
|||
|
||||
pub loaded_programs_cache: Arc<RwLock<LoadedPrograms<BankForks>>>,
|
||||
|
||||
pub check_program_modification_slot: bool,
|
||||
|
||||
epoch_reward_status: EpochRewardStatus,
|
||||
|
||||
transaction_processor: TransactionBatchProcessor,
|
||||
}
|
||||
|
||||
struct VoteWithStakeDelegations {
|
||||
|
@ -1026,7 +1024,6 @@ impl Bank {
|
|||
freeze_started: AtomicBool::default(),
|
||||
vote_only_bank: false,
|
||||
cost_tracker: RwLock::<CostTracker>::default(),
|
||||
sysvar_cache: RwLock::<SysvarCache>::default(),
|
||||
accounts_data_size_initial: 0,
|
||||
accounts_data_size_delta_on_chain: AtomicI64::new(0),
|
||||
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
||||
|
@ -1035,10 +1032,12 @@ impl Bank {
|
|||
Slot::default(),
|
||||
Epoch::default(),
|
||||
))),
|
||||
check_program_modification_slot: false,
|
||||
epoch_reward_status: EpochRewardStatus::default(),
|
||||
transaction_processor: TransactionBatchProcessor::default(),
|
||||
};
|
||||
|
||||
bank.transaction_processor = TransactionBatchProcessor::new(&bank);
|
||||
|
||||
let accounts_data_size_initial = bank.get_total_accounts_stats().unwrap().data_len as u64;
|
||||
bank.accounts_data_size_initial = accounts_data_size_initial;
|
||||
|
||||
|
@ -1339,16 +1338,17 @@ impl Bank {
|
|||
)),
|
||||
freeze_started: AtomicBool::new(false),
|
||||
cost_tracker: RwLock::new(CostTracker::default()),
|
||||
sysvar_cache: RwLock::new(SysvarCache::default()),
|
||||
accounts_data_size_initial,
|
||||
accounts_data_size_delta_on_chain: AtomicI64::new(0),
|
||||
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
||||
fee_structure: parent.fee_structure.clone(),
|
||||
loaded_programs_cache: parent.loaded_programs_cache.clone(),
|
||||
check_program_modification_slot: false,
|
||||
epoch_reward_status: parent.epoch_reward_status.clone(),
|
||||
transaction_processor: TransactionBatchProcessor::default(),
|
||||
};
|
||||
|
||||
new.transaction_processor = TransactionBatchProcessor::new(&new);
|
||||
|
||||
let (_, ancestors_time_us) = measure_us!({
|
||||
let mut ancestors = Vec::with_capacity(1 + new.parents().len());
|
||||
ancestors.push(new.slot());
|
||||
|
@ -1843,7 +1843,6 @@ impl Bank {
|
|||
freeze_started: AtomicBool::new(fields.hash != Hash::default()),
|
||||
vote_only_bank: false,
|
||||
cost_tracker: RwLock::new(CostTracker::default()),
|
||||
sysvar_cache: RwLock::new(SysvarCache::default()),
|
||||
accounts_data_size_initial,
|
||||
accounts_data_size_delta_on_chain: AtomicI64::new(0),
|
||||
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
||||
|
@ -1852,9 +1851,12 @@ impl Bank {
|
|||
fields.slot,
|
||||
fields.epoch,
|
||||
))),
|
||||
check_program_modification_slot: false,
|
||||
epoch_reward_status: fields.epoch_reward_status,
|
||||
transaction_processor: TransactionBatchProcessor::default(),
|
||||
};
|
||||
|
||||
bank.transaction_processor = TransactionBatchProcessor::new(&bank);
|
||||
|
||||
bank.finish_init(
|
||||
genesis_config,
|
||||
additional_builtins,
|
||||
|
@ -3340,7 +3342,7 @@ impl Bank {
|
|||
let pre_lamport = curr_stake_account.lamports();
|
||||
let post_lamport = post_stake_account.lamports();
|
||||
assert_eq!(pre_lamport + u64::try_from(reward_amount).unwrap(), post_lamport,
|
||||
"stake account balance has changed since the reward calculation! account: {stake_pubkey}, pre balance: {pre_lamport}, post balance: {post_lamport}, rewards: {reward_amount}");
|
||||
"stake account balance has changed since the reward calculation! account: {stake_pubkey}, pre balance: {pre_lamport}, post balance: {post_lamport}, rewards: {reward_amount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4588,18 +4590,24 @@ impl Bank {
|
|||
}
|
||||
balances
|
||||
}
|
||||
}
|
||||
|
||||
fn program_modification_slot(&self, pubkey: &Pubkey) -> Result<Slot> {
|
||||
let program = self
|
||||
.get_account_with_fixed_root(pubkey)
|
||||
impl TransactionBatchProcessor {
|
||||
fn program_modification_slot<CB: TransactionProcessingCallback>(
|
||||
&self,
|
||||
callbacks: &CB,
|
||||
pubkey: &Pubkey,
|
||||
) -> Result<Slot> {
|
||||
let program = callbacks
|
||||
.get_account_shared_data(pubkey)
|
||||
.ok_or(TransactionError::ProgramAccountNotFound)?;
|
||||
if bpf_loader_upgradeable::check_id(program.owner()) {
|
||||
if let Ok(UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
}) = program.state()
|
||||
{
|
||||
let programdata = self
|
||||
.get_account_with_fixed_root(&programdata_address)
|
||||
let programdata = callbacks
|
||||
.get_account_shared_data(&programdata_address)
|
||||
.ok_or(TransactionError::ProgramAccountNotFound)?;
|
||||
if let Ok(UpgradeableLoaderState::ProgramData {
|
||||
slot,
|
||||
|
@ -4619,12 +4627,13 @@ impl Bank {
|
|||
}
|
||||
}
|
||||
|
||||
fn load_program_accounts(
|
||||
fn load_program_accounts<CB: TransactionProcessingCallback>(
|
||||
&self,
|
||||
callbacks: &CB,
|
||||
pubkey: &Pubkey,
|
||||
environments: &ProgramRuntimeEnvironments,
|
||||
) -> ProgramAccountLoadResult {
|
||||
let program_account = match self.get_account_with_fixed_root(pubkey) {
|
||||
let program_account = match callbacks.get_account_shared_data(pubkey) {
|
||||
None => return ProgramAccountLoadResult::AccountNotFound,
|
||||
Some(account) => account,
|
||||
};
|
||||
|
@ -4653,7 +4662,8 @@ impl Bank {
|
|||
programdata_address,
|
||||
}) = program_account.state()
|
||||
{
|
||||
let programdata_account = match self.get_account_with_fixed_root(&programdata_address) {
|
||||
let programdata_account = match callbacks.get_account_shared_data(&programdata_address)
|
||||
{
|
||||
None => return ProgramAccountLoadResult::AccountNotFound,
|
||||
Some(account) => account,
|
||||
};
|
||||
|
@ -4710,8 +4720,9 @@ impl Bank {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_program(
|
||||
pub fn load_program<CB: TransactionProcessingCallback>(
|
||||
&self,
|
||||
callbacks: &CB,
|
||||
pubkey: &Pubkey,
|
||||
reload: bool,
|
||||
recompile: Option<Arc<LoadedProgram>>,
|
||||
|
@ -4728,100 +4739,107 @@ impl Bank {
|
|||
..LoadProgramMetrics::default()
|
||||
};
|
||||
|
||||
let mut loaded_program = match self.load_program_accounts(pubkey, environments) {
|
||||
ProgramAccountLoadResult::AccountNotFound => Ok(LoadedProgram::new_tombstone(
|
||||
self.slot,
|
||||
LoadedProgramType::Closed,
|
||||
)),
|
||||
let mut loaded_program =
|
||||
match self.load_program_accounts(callbacks, pubkey, environments) {
|
||||
ProgramAccountLoadResult::AccountNotFound => Ok(LoadedProgram::new_tombstone(
|
||||
self.slot,
|
||||
LoadedProgramType::Closed,
|
||||
)),
|
||||
|
||||
ProgramAccountLoadResult::InvalidAccountData(env) => Err((self.slot, env)),
|
||||
ProgramAccountLoadResult::InvalidAccountData(env) => Err((self.slot, env)),
|
||||
|
||||
ProgramAccountLoadResult::ProgramOfLoaderV1orV2(program_account) => {
|
||||
Self::load_program_from_bytes(
|
||||
&mut load_program_metrics,
|
||||
program_account.data(),
|
||||
program_account.owner(),
|
||||
program_account.data().len(),
|
||||
0,
|
||||
environments.program_runtime_v1.clone(),
|
||||
reload,
|
||||
)
|
||||
.map_err(|_| (0, environments.program_runtime_v1.clone()))
|
||||
}
|
||||
|
||||
ProgramAccountLoadResult::ProgramOfLoaderV3(
|
||||
program_account,
|
||||
programdata_account,
|
||||
slot,
|
||||
) => programdata_account
|
||||
.data()
|
||||
.get(UpgradeableLoaderState::size_of_programdata_metadata()..)
|
||||
.ok_or(Box::new(InstructionError::InvalidAccountData).into())
|
||||
.and_then(|programdata| {
|
||||
ProgramAccountLoadResult::ProgramOfLoaderV1orV2(program_account) => {
|
||||
Self::load_program_from_bytes(
|
||||
&mut load_program_metrics,
|
||||
programdata,
|
||||
program_account.data(),
|
||||
program_account.owner(),
|
||||
program_account
|
||||
.data()
|
||||
.len()
|
||||
.saturating_add(programdata_account.data().len()),
|
||||
slot,
|
||||
program_account.data().len(),
|
||||
0,
|
||||
environments.program_runtime_v1.clone(),
|
||||
reload,
|
||||
)
|
||||
})
|
||||
.map_err(|_| (slot, environments.program_runtime_v1.clone())),
|
||||
.map_err(|_| (0, environments.program_runtime_v1.clone()))
|
||||
}
|
||||
|
||||
ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot) => program_account
|
||||
.data()
|
||||
.get(LoaderV4State::program_data_offset()..)
|
||||
.ok_or(Box::new(InstructionError::InvalidAccountData).into())
|
||||
.and_then(|elf_bytes| {
|
||||
Self::load_program_from_bytes(
|
||||
&mut load_program_metrics,
|
||||
elf_bytes,
|
||||
&loader_v4::id(),
|
||||
program_account.data().len(),
|
||||
slot,
|
||||
environments.program_runtime_v2.clone(),
|
||||
reload,
|
||||
)
|
||||
})
|
||||
.map_err(|_| (slot, environments.program_runtime_v2.clone())),
|
||||
}
|
||||
.unwrap_or_else(|(slot, env)| {
|
||||
LoadedProgram::new_tombstone(slot, LoadedProgramType::FailedVerification(env))
|
||||
});
|
||||
ProgramAccountLoadResult::ProgramOfLoaderV3(
|
||||
program_account,
|
||||
programdata_account,
|
||||
slot,
|
||||
) => programdata_account
|
||||
.data()
|
||||
.get(UpgradeableLoaderState::size_of_programdata_metadata()..)
|
||||
.ok_or(Box::new(InstructionError::InvalidAccountData).into())
|
||||
.and_then(|programdata| {
|
||||
Self::load_program_from_bytes(
|
||||
&mut load_program_metrics,
|
||||
programdata,
|
||||
program_account.owner(),
|
||||
program_account
|
||||
.data()
|
||||
.len()
|
||||
.saturating_add(programdata_account.data().len()),
|
||||
slot,
|
||||
environments.program_runtime_v1.clone(),
|
||||
reload,
|
||||
)
|
||||
})
|
||||
.map_err(|_| (slot, environments.program_runtime_v1.clone())),
|
||||
|
||||
ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot) => {
|
||||
program_account
|
||||
.data()
|
||||
.get(LoaderV4State::program_data_offset()..)
|
||||
.ok_or(Box::new(InstructionError::InvalidAccountData).into())
|
||||
.and_then(|elf_bytes| {
|
||||
Self::load_program_from_bytes(
|
||||
&mut load_program_metrics,
|
||||
elf_bytes,
|
||||
&loader_v4::id(),
|
||||
program_account.data().len(),
|
||||
slot,
|
||||
environments.program_runtime_v2.clone(),
|
||||
reload,
|
||||
)
|
||||
})
|
||||
.map_err(|_| (slot, environments.program_runtime_v2.clone()))
|
||||
}
|
||||
}
|
||||
.unwrap_or_else(|(slot, env)| {
|
||||
LoadedProgram::new_tombstone(slot, LoadedProgramType::FailedVerification(env))
|
||||
});
|
||||
|
||||
let mut timings = ExecuteDetailsTimings::default();
|
||||
load_program_metrics.submit_datapoint(&mut timings);
|
||||
if let Some(recompile) = recompile {
|
||||
loaded_program.effective_slot = loaded_program.effective_slot.max(
|
||||
self.epoch_schedule()
|
||||
.get_first_slot_in_epoch(effective_epoch),
|
||||
);
|
||||
loaded_program.effective_slot = loaded_program
|
||||
.effective_slot
|
||||
.max(self.epoch_schedule.get_first_slot_in_epoch(effective_epoch));
|
||||
loaded_program.tx_usage_counter =
|
||||
AtomicU64::new(recompile.tx_usage_counter.load(Ordering::Relaxed));
|
||||
loaded_program.ix_usage_counter =
|
||||
AtomicU64::new(recompile.ix_usage_counter.load(Ordering::Relaxed));
|
||||
}
|
||||
loaded_program.update_access_slot(self.slot());
|
||||
loaded_program.update_access_slot(self.slot);
|
||||
Arc::new(loaded_program)
|
||||
}
|
||||
}
|
||||
|
||||
impl Bank {
|
||||
pub fn clear_program_cache(&self) {
|
||||
self.loaded_programs_cache
|
||||
.write()
|
||||
.unwrap()
|
||||
.unload_all_programs();
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionBatchProcessor {
|
||||
/// Execute a transaction using the provided loaded accounts and update
|
||||
/// the executors cache if the transaction was successful.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn execute_loaded_transaction(
|
||||
fn execute_loaded_transaction<CB: TransactionProcessingCallback>(
|
||||
&self,
|
||||
callback: &CB,
|
||||
tx: &SanitizedTransaction,
|
||||
loaded_transaction: &mut LoadedTransaction,
|
||||
compute_budget: ComputeBudget,
|
||||
|
@ -4853,7 +4871,7 @@ impl Bank {
|
|||
|
||||
let mut transaction_context = TransactionContext::new(
|
||||
transaction_accounts,
|
||||
self.rent_collector.rent.clone(),
|
||||
callback.get_rent_collector().rent.clone(),
|
||||
compute_budget.max_invoke_stack_height,
|
||||
compute_budget.max_instruction_trace_length,
|
||||
);
|
||||
|
@ -4861,7 +4879,7 @@ impl Bank {
|
|||
transaction_context.set_signature(tx.signature());
|
||||
|
||||
let pre_account_state_info = TransactionAccountStateInfo::new(
|
||||
&self.rent_collector.rent,
|
||||
&callback.get_rent_collector().rent,
|
||||
&transaction_context,
|
||||
tx.message(),
|
||||
);
|
||||
|
@ -4877,7 +4895,8 @@ impl Bank {
|
|||
None
|
||||
};
|
||||
|
||||
let (blockhash, lamports_per_signature) = self.last_blockhash_and_lamports_per_signature();
|
||||
let (blockhash, lamports_per_signature) =
|
||||
callback.get_last_blockhash_and_lamports_per_signature();
|
||||
|
||||
let mut executed_units = 0u64;
|
||||
let mut programs_modified_by_tx = LoadedProgramsForTxBatch::new(
|
||||
|
@ -4892,7 +4911,7 @@ impl Bank {
|
|||
log_collector.clone(),
|
||||
programs_loaded_for_tx_batch,
|
||||
&mut programs_modified_by_tx,
|
||||
self.feature_set.clone(),
|
||||
callback.get_feature_set(),
|
||||
compute_budget,
|
||||
timings,
|
||||
&self.sysvar_cache.read().unwrap(),
|
||||
|
@ -4910,7 +4929,7 @@ impl Bank {
|
|||
let mut status = process_result
|
||||
.and_then(|info| {
|
||||
let post_account_state_info = TransactionAccountStateInfo::new(
|
||||
&self.rent_collector.rent,
|
||||
&callback.get_rent_collector().rent,
|
||||
&transaction_context,
|
||||
tx.message(),
|
||||
);
|
||||
|
@ -4995,8 +5014,9 @@ impl Bank {
|
|||
}
|
||||
}
|
||||
|
||||
fn replenish_program_cache(
|
||||
fn replenish_program_cache<CB: TransactionProcessingCallback>(
|
||||
&self,
|
||||
callback: &CB,
|
||||
program_accounts_map: &HashMap<Pubkey, (&Pubkey, u64)>,
|
||||
) -> LoadedProgramsForTxBatch {
|
||||
let mut missing_programs: Vec<(Pubkey, (LoadedProgramMatchCriteria, u64))> =
|
||||
|
@ -5007,7 +5027,7 @@ impl Bank {
|
|||
(
|
||||
*pubkey,
|
||||
(
|
||||
self.program_modification_slot(pubkey)
|
||||
self.program_modification_slot(callback, pubkey)
|
||||
.map_or(LoadedProgramMatchCriteria::Tombstone, |slot| {
|
||||
LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(slot)
|
||||
}),
|
||||
|
@ -5043,11 +5063,7 @@ impl Bank {
|
|||
}
|
||||
// Submit our last completed loading task.
|
||||
if let Some((key, program)) = program_to_store.take() {
|
||||
loaded_programs_cache.finish_cooperative_loading_task(
|
||||
self.slot(),
|
||||
key,
|
||||
program,
|
||||
);
|
||||
loaded_programs_cache.finish_cooperative_loading_task(self.slot, key, program);
|
||||
}
|
||||
// Figure out which program needs to be loaded next.
|
||||
let program_to_load = loaded_programs_cache.extract(
|
||||
|
@ -5062,7 +5078,7 @@ impl Bank {
|
|||
|
||||
if let Some((key, count)) = program_to_load {
|
||||
// Load, verify and compile one program.
|
||||
let program = self.load_program(&key, false, None);
|
||||
let program = self.load_program(callback, &key, false, None);
|
||||
program.tx_usage_counter.store(count, Ordering::Relaxed);
|
||||
program_to_store = Some((key, program));
|
||||
} else if missing_programs.is_empty() {
|
||||
|
@ -5081,9 +5097,9 @@ impl Bank {
|
|||
/// Returns a hash map of executable program accounts (program accounts that are not writable
|
||||
/// in the given transactions), and their owners, for the transactions with a valid
|
||||
/// blockhash or nonce.
|
||||
fn filter_executable_program_accounts<'a>(
|
||||
fn filter_executable_program_accounts<'a, CB: TransactionProcessingCallback>(
|
||||
&self,
|
||||
ancestors: &Ancestors,
|
||||
callbacks: &CB,
|
||||
txs: &[SanitizedTransaction],
|
||||
lock_results: &mut [TransactionCheckResult],
|
||||
program_owners: &'a [Pubkey],
|
||||
|
@ -5101,11 +5117,8 @@ impl Bank {
|
|||
saturating_add_assign!(*count, 1);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
if let Ok(index) = self
|
||||
.rc
|
||||
.accounts
|
||||
.accounts_db
|
||||
.account_matches_owners(ancestors, key, program_owners)
|
||||
if let Ok(index) =
|
||||
callbacks.account_matches_owners(key, program_owners)
|
||||
{
|
||||
program_owners
|
||||
.get(index)
|
||||
|
@ -5123,7 +5136,9 @@ impl Bank {
|
|||
});
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Bank {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn load_and_execute_transactions(
|
||||
&self,
|
||||
|
@ -5187,17 +5202,21 @@ impl Bank {
|
|||
debug!("check: {}us", check_time.as_us());
|
||||
timings.saturating_add_in_place(ExecuteTimingType::CheckUs, check_time.as_us());
|
||||
|
||||
let sanitized_output = self.load_and_execute_sanitized_transactions(
|
||||
sanitized_txs,
|
||||
&mut check_results,
|
||||
&mut error_counters,
|
||||
enable_cpi_recording,
|
||||
enable_log_recording,
|
||||
enable_return_data_recording,
|
||||
timings,
|
||||
account_overrides,
|
||||
log_messages_bytes_limit,
|
||||
);
|
||||
let sanitized_output = self
|
||||
.transaction_processor
|
||||
.load_and_execute_sanitized_transactions(
|
||||
self,
|
||||
sanitized_txs,
|
||||
&mut check_results,
|
||||
&mut error_counters,
|
||||
enable_cpi_recording,
|
||||
enable_log_recording,
|
||||
enable_return_data_recording,
|
||||
timings,
|
||||
account_overrides,
|
||||
self.builtin_programs.iter(),
|
||||
log_messages_bytes_limit,
|
||||
);
|
||||
|
||||
let mut signature_count = 0;
|
||||
|
||||
|
@ -5325,10 +5344,13 @@ impl Bank {
|
|||
error_counters,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionBatchProcessor {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn load_and_execute_sanitized_transactions(
|
||||
fn load_and_execute_sanitized_transactions<'a, CB: TransactionProcessingCallback>(
|
||||
&self,
|
||||
callbacks: &CB,
|
||||
sanitized_txs: &[SanitizedTransaction],
|
||||
check_results: &mut [TransactionCheckResult],
|
||||
error_counters: &mut TransactionErrorMetrics,
|
||||
|
@ -5337,35 +5359,32 @@ impl Bank {
|
|||
enable_return_data_recording: bool,
|
||||
timings: &mut ExecuteTimings,
|
||||
account_overrides: Option<&AccountOverrides>,
|
||||
builtin_programs: impl Iterator<Item = &'a Pubkey>,
|
||||
log_messages_bytes_limit: Option<usize>,
|
||||
) -> LoadAndExecuteSanitizedTransactionsOutput {
|
||||
let mut program_accounts_map = self.filter_executable_program_accounts(
|
||||
&self.ancestors,
|
||||
callbacks,
|
||||
sanitized_txs,
|
||||
check_results,
|
||||
PROGRAM_OWNERS,
|
||||
);
|
||||
let native_loader = native_loader::id();
|
||||
for builtin_program in self.builtin_programs.iter() {
|
||||
for builtin_program in builtin_programs {
|
||||
program_accounts_map.insert(*builtin_program, (&native_loader, 0));
|
||||
}
|
||||
|
||||
let programs_loaded_for_tx_batch = Rc::new(RefCell::new(
|
||||
self.replenish_program_cache(&program_accounts_map),
|
||||
self.replenish_program_cache(callbacks, &program_accounts_map),
|
||||
));
|
||||
|
||||
let mut load_time = Measure::start("accounts_load");
|
||||
let mut loaded_transactions = load_accounts(
|
||||
&self.rc.accounts.accounts_db,
|
||||
&self.ancestors,
|
||||
callbacks,
|
||||
sanitized_txs,
|
||||
check_results,
|
||||
error_counters,
|
||||
&self.rent_collector,
|
||||
&self.feature_set,
|
||||
&self.fee_structure,
|
||||
account_overrides,
|
||||
self.get_reward_interval(),
|
||||
&program_accounts_map,
|
||||
&programs_loaded_for_tx_batch.borrow(),
|
||||
);
|
||||
|
@ -5402,6 +5421,7 @@ impl Bank {
|
|||
};
|
||||
|
||||
let result = self.execute_loaded_transaction(
|
||||
callbacks,
|
||||
tx,
|
||||
loaded_transaction,
|
||||
compute_budget,
|
||||
|
@ -5442,7 +5462,7 @@ impl Bank {
|
|||
.unwrap()
|
||||
.evict_using_2s_random_selection(
|
||||
Percentage::from(SHRINK_LOADED_PROGRAMS_TO_PERCENTAGE),
|
||||
self.slot(),
|
||||
self.slot,
|
||||
);
|
||||
|
||||
debug!(
|
||||
|
@ -5460,7 +5480,9 @@ impl Bank {
|
|||
execution_results,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bank {
|
||||
/// Load the accounts data size, in bytes
|
||||
pub fn load_accounts_data_size(&self) -> u64 {
|
||||
self.accounts_data_size_initial
|
||||
|
@ -8190,7 +8212,7 @@ impl Bank {
|
|||
|
||||
pub fn is_in_slot_hashes_history(&self, slot: &Slot) -> bool {
|
||||
if slot < &self.slot {
|
||||
if let Ok(sysvar_cache) = self.sysvar_cache.read() {
|
||||
if let Ok(sysvar_cache) = self.transaction_processor.sysvar_cache.read() {
|
||||
if let Ok(slot_hashes) = sysvar_cache.get_slot_hashes() {
|
||||
return slot_hashes.get(slot).is_some();
|
||||
}
|
||||
|
@ -8198,6 +8220,156 @@ impl Bank {
|
|||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn check_program_modification_slot(&mut self) {
|
||||
self.transaction_processor.check_program_modification_slot = true;
|
||||
}
|
||||
|
||||
pub fn load_program(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
reload: bool,
|
||||
recompile: Option<Arc<LoadedProgram>>,
|
||||
) -> Arc<LoadedProgram> {
|
||||
self.transaction_processor
|
||||
.load_program(self, pubkey, reload, recompile)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TransactionProcessingCallback {
|
||||
fn account_matches_owners(
|
||||
&self,
|
||||
account: &Pubkey,
|
||||
owners: &[Pubkey],
|
||||
) -> std::result::Result<usize, MatchAccountOwnerError>;
|
||||
|
||||
fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData>;
|
||||
|
||||
fn get_last_blockhash_and_lamports_per_signature(&self) -> (Hash, u64);
|
||||
|
||||
fn get_rent_collector(&self) -> &RentCollector;
|
||||
|
||||
fn get_feature_set(&self) -> Arc<FeatureSet>;
|
||||
|
||||
fn check_account_access(
|
||||
&self,
|
||||
_tx: &SanitizedTransaction,
|
||||
_account_index: usize,
|
||||
_account: &AccountSharedData,
|
||||
_error_counters: &mut TransactionErrorMetrics,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionProcessingCallback for Bank {
|
||||
fn account_matches_owners(
|
||||
&self,
|
||||
account: &Pubkey,
|
||||
owners: &[Pubkey],
|
||||
) -> std::result::Result<usize, MatchAccountOwnerError> {
|
||||
self.rc
|
||||
.accounts
|
||||
.accounts_db
|
||||
.account_matches_owners(&self.ancestors, account, owners)
|
||||
}
|
||||
|
||||
fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
|
||||
self.rc
|
||||
.accounts
|
||||
.accounts_db
|
||||
.load_with_fixed_root(&self.ancestors, pubkey)
|
||||
.map(|(acc, _)| acc)
|
||||
}
|
||||
|
||||
fn get_last_blockhash_and_lamports_per_signature(&self) -> (Hash, u64) {
|
||||
self.last_blockhash_and_lamports_per_signature()
|
||||
}
|
||||
|
||||
fn get_rent_collector(&self) -> &RentCollector {
|
||||
&self.rent_collector
|
||||
}
|
||||
|
||||
fn get_feature_set(&self) -> Arc<FeatureSet> {
|
||||
self.feature_set.clone()
|
||||
}
|
||||
|
||||
fn check_account_access(
|
||||
&self,
|
||||
tx: &SanitizedTransaction,
|
||||
account_index: usize,
|
||||
account: &AccountSharedData,
|
||||
error_counters: &mut TransactionErrorMetrics,
|
||||
) -> Result<()> {
|
||||
if self.get_reward_interval() == RewardInterval::InsideInterval
|
||||
&& tx.message().is_writable(account_index)
|
||||
&& solana_stake_program::check_id(account.owner())
|
||||
{
|
||||
error_counters.program_execution_temporarily_restricted += 1;
|
||||
Err(TransactionError::ProgramExecutionTemporarilyRestricted {
|
||||
account_index: account_index as u8,
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AbiExample, Debug)]
|
||||
struct TransactionBatchProcessor {
|
||||
/// Bank slot (i.e. block)
|
||||
slot: Slot,
|
||||
|
||||
/// Bank epoch
|
||||
epoch: Epoch,
|
||||
|
||||
/// initialized from genesis
|
||||
epoch_schedule: EpochSchedule,
|
||||
|
||||
/// Transaction fee structure
|
||||
fee_structure: FeeStructure,
|
||||
|
||||
pub check_program_modification_slot: bool,
|
||||
|
||||
/// Optional config parameters that can override runtime behavior
|
||||
runtime_config: Arc<RuntimeConfig>,
|
||||
|
||||
sysvar_cache: RwLock<SysvarCache>,
|
||||
|
||||
pub loaded_programs_cache: Arc<RwLock<LoadedPrograms<BankForks>>>,
|
||||
}
|
||||
|
||||
impl Default for TransactionBatchProcessor {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
slot: Slot::default(),
|
||||
epoch: Epoch::default(),
|
||||
epoch_schedule: EpochSchedule::default(),
|
||||
fee_structure: FeeStructure::default(),
|
||||
check_program_modification_slot: false,
|
||||
runtime_config: Arc::<RuntimeConfig>::default(),
|
||||
sysvar_cache: RwLock::<SysvarCache>::default(),
|
||||
loaded_programs_cache: Arc::new(RwLock::new(LoadedPrograms::new(
|
||||
Slot::default(),
|
||||
Epoch::default(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionBatchProcessor {
|
||||
fn new(bank: &Bank) -> Self {
|
||||
Self {
|
||||
slot: bank.slot(),
|
||||
epoch: bank.epoch(),
|
||||
epoch_schedule: bank.epoch_schedule.clone(),
|
||||
fee_structure: bank.fee_structure.clone(),
|
||||
check_program_modification_slot: false,
|
||||
runtime_config: bank.runtime_config.clone(),
|
||||
sysvar_cache: RwLock::<SysvarCache>::default(),
|
||||
loaded_programs_cache: bank.loaded_programs_cache.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-context-only-utils")]
|
||||
|
|
|
@ -16,6 +16,7 @@ impl AddressLoader for &Bank {
|
|||
address_table_lookups: &[MessageAddressTableLookup],
|
||||
) -> Result<LoadedAddresses, AddressLoaderError> {
|
||||
let slot_hashes = self
|
||||
.transaction_processor
|
||||
.sysvar_cache
|
||||
.read()
|
||||
.unwrap()
|
||||
|
|
|
@ -5,7 +5,7 @@ use {
|
|||
|
||||
impl Bank {
|
||||
pub(crate) fn fill_missing_sysvar_cache_entries(&self) {
|
||||
let mut sysvar_cache = self.sysvar_cache.write().unwrap();
|
||||
let mut sysvar_cache = self.transaction_processor.sysvar_cache.write().unwrap();
|
||||
sysvar_cache.fill_missing_entries(|pubkey, callback| {
|
||||
if let Some(account) = self.get_account_with_fixed_root(pubkey) {
|
||||
callback(account.data());
|
||||
|
@ -14,12 +14,16 @@ impl Bank {
|
|||
}
|
||||
|
||||
pub(crate) fn reset_sysvar_cache(&self) {
|
||||
let mut sysvar_cache = self.sysvar_cache.write().unwrap();
|
||||
let mut sysvar_cache = self.transaction_processor.sysvar_cache.write().unwrap();
|
||||
sysvar_cache.reset();
|
||||
}
|
||||
|
||||
pub fn get_sysvar_cache_for_tests(&self) -> SysvarCache {
|
||||
self.sysvar_cache.read().unwrap().clone()
|
||||
self.transaction_processor
|
||||
.sysvar_cache
|
||||
.read()
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +44,7 @@ mod tests {
|
|||
let (genesis_config, _mint_keypair) = create_genesis_config(100_000);
|
||||
let bank0 = Arc::new(Bank::new_for_tests(&genesis_config));
|
||||
|
||||
let bank0_sysvar_cache = bank0.sysvar_cache.read().unwrap();
|
||||
let bank0_sysvar_cache = bank0.transaction_processor.sysvar_cache.read().unwrap();
|
||||
let bank0_cached_clock = bank0_sysvar_cache.get_clock();
|
||||
let bank0_cached_epoch_schedule = bank0_sysvar_cache.get_epoch_schedule();
|
||||
let bank0_cached_fees = bank0_sysvar_cache.get_fees();
|
||||
|
@ -60,7 +64,7 @@ mod tests {
|
|||
bank1_slot,
|
||||
));
|
||||
|
||||
let bank1_sysvar_cache = bank1.sysvar_cache.read().unwrap();
|
||||
let bank1_sysvar_cache = bank1.transaction_processor.sysvar_cache.read().unwrap();
|
||||
let bank1_cached_clock = bank1_sysvar_cache.get_clock();
|
||||
let bank1_cached_epoch_schedule = bank1_sysvar_cache.get_epoch_schedule();
|
||||
let bank1_cached_fees = bank1_sysvar_cache.get_fees();
|
||||
|
@ -81,7 +85,7 @@ mod tests {
|
|||
let bank2_slot = bank1.slot() + 1;
|
||||
let bank2 = Bank::new_from_parent(bank1.clone(), &Pubkey::default(), bank2_slot);
|
||||
|
||||
let bank2_sysvar_cache = bank2.sysvar_cache.read().unwrap();
|
||||
let bank2_sysvar_cache = bank2.transaction_processor.sysvar_cache.read().unwrap();
|
||||
let bank2_cached_clock = bank2_sysvar_cache.get_clock();
|
||||
let bank2_cached_epoch_schedule = bank2_sysvar_cache.get_epoch_schedule();
|
||||
let bank2_cached_fees = bank2_sysvar_cache.get_fees();
|
||||
|
@ -112,7 +116,7 @@ mod tests {
|
|||
let bank1_slot = bank0.slot() + 1;
|
||||
let mut bank1 = Bank::new_from_parent(bank0, &Pubkey::default(), bank1_slot);
|
||||
|
||||
let bank1_sysvar_cache = bank1.sysvar_cache.read().unwrap();
|
||||
let bank1_sysvar_cache = bank1.transaction_processor.sysvar_cache.read().unwrap();
|
||||
let bank1_cached_clock = bank1_sysvar_cache.get_clock();
|
||||
let bank1_cached_epoch_schedule = bank1_sysvar_cache.get_epoch_schedule();
|
||||
let bank1_cached_fees = bank1_sysvar_cache.get_fees();
|
||||
|
@ -130,7 +134,7 @@ mod tests {
|
|||
drop(bank1_sysvar_cache);
|
||||
bank1.reset_sysvar_cache();
|
||||
|
||||
let bank1_sysvar_cache = bank1.sysvar_cache.read().unwrap();
|
||||
let bank1_sysvar_cache = bank1.transaction_processor.sysvar_cache.read().unwrap();
|
||||
assert!(bank1_sysvar_cache.get_clock().is_err());
|
||||
assert!(bank1_sysvar_cache.get_epoch_schedule().is_err());
|
||||
assert!(bank1_sysvar_cache.get_fees().is_err());
|
||||
|
@ -155,7 +159,7 @@ mod tests {
|
|||
|
||||
bank1.fill_missing_sysvar_cache_entries();
|
||||
|
||||
let bank1_sysvar_cache = bank1.sysvar_cache.read().unwrap();
|
||||
let bank1_sysvar_cache = bank1.transaction_processor.sysvar_cache.read().unwrap();
|
||||
assert_eq!(bank1_sysvar_cache.get_clock(), bank1_cached_clock);
|
||||
assert_eq!(
|
||||
bank1_sysvar_cache.get_epoch_schedule(),
|
||||
|
|
|
@ -10987,16 +10987,12 @@ fn test_rent_state_list_len() {
|
|||
let sanitized_tx = SanitizedTransaction::try_from_legacy_transaction(tx).unwrap();
|
||||
let mut error_counters = TransactionErrorMetrics::default();
|
||||
let loaded_txs = load_accounts(
|
||||
&bank.accounts().accounts_db,
|
||||
&bank.ancestors,
|
||||
&bank,
|
||||
&[sanitized_tx.clone()],
|
||||
&[(Ok(()), None, Some(0))],
|
||||
&mut error_counters,
|
||||
&bank.rent_collector,
|
||||
&bank.feature_set,
|
||||
&FeeStructure::default(),
|
||||
None,
|
||||
RewardInterval::OutsideInterval,
|
||||
&HashMap::new(),
|
||||
&LoadedProgramsForTxBatch::default(),
|
||||
);
|
||||
|
@ -13744,10 +13740,10 @@ fn test_filter_executable_program_accounts() {
|
|||
);
|
||||
let sanitized_tx2 = SanitizedTransaction::from_transaction_for_tests(tx2);
|
||||
|
||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||
let owners = &[program1_pubkey, program2_pubkey];
|
||||
let programs = bank.filter_executable_program_accounts(
|
||||
&ancestors,
|
||||
let transaction_processor = TransactionBatchProcessor::new(&bank);
|
||||
let programs = transaction_processor.filter_executable_program_accounts(
|
||||
&bank,
|
||||
&[sanitized_tx1, sanitized_tx2],
|
||||
&mut [(Ok(()), None, Some(0)), (Ok(()), None, Some(0))],
|
||||
owners,
|
||||
|
@ -13839,11 +13835,11 @@ fn test_filter_executable_program_accounts_invalid_blockhash() {
|
|||
// Let's not register blockhash from tx2. This should cause the tx2 to fail
|
||||
let sanitized_tx2 = SanitizedTransaction::from_transaction_for_tests(tx2);
|
||||
|
||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||
let owners = &[program1_pubkey, program2_pubkey];
|
||||
let mut lock_results = vec![(Ok(()), None, Some(0)), (Ok(()), None, None)];
|
||||
let programs = bank.filter_executable_program_accounts(
|
||||
&ancestors,
|
||||
let transaction_processor = TransactionBatchProcessor::new(&bank);
|
||||
let programs = transaction_processor.filter_executable_program_accounts(
|
||||
&bank,
|
||||
&[sanitized_tx1, sanitized_tx2],
|
||||
&mut lock_results,
|
||||
owners,
|
||||
|
|
|
@ -221,8 +221,9 @@ impl BankForks {
|
|||
}
|
||||
|
||||
pub fn insert(&mut self, mut bank: Bank) -> BankWithScheduler {
|
||||
bank.check_program_modification_slot =
|
||||
self.root.load(Ordering::Relaxed) < self.highest_slot_at_startup;
|
||||
if self.root.load(Ordering::Relaxed) < self.highest_slot_at_startup {
|
||||
bank.check_program_modification_slot();
|
||||
}
|
||||
|
||||
let bank = Arc::new(bank);
|
||||
let bank = if let Some(scheduler_pool) = &self.scheduler_pool {
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use {
|
||||
crate::{bank::RewardInterval, svm::account_rent_state::RentState},
|
||||
crate::{bank::TransactionProcessingCallback, svm::account_rent_state::RentState},
|
||||
itertools::Itertools,
|
||||
log::warn,
|
||||
solana_accounts_db::{
|
||||
account_overrides::AccountOverrides,
|
||||
accounts::{LoadedTransaction, TransactionLoadResult, TransactionRent},
|
||||
accounts_db::AccountsDb,
|
||||
ancestors::Ancestors,
|
||||
nonce_info::NonceFull,
|
||||
rent_collector::{RentCollector, RENT_EXEMPT_RENT_EPOCH},
|
||||
rent_debits::RentDebits,
|
||||
|
@ -22,7 +20,7 @@ use {
|
|||
create_executable_meta, is_builtin, is_executable, Account, AccountSharedData,
|
||||
ReadableAccount, WritableAccount,
|
||||
},
|
||||
feature_set::{self, include_loaded_accounts_data_size_in_fee_calculation, FeatureSet},
|
||||
feature_set::{self, include_loaded_accounts_data_size_in_fee_calculation},
|
||||
fee::FeeStructure,
|
||||
message::SanitizedMessage,
|
||||
native_loader,
|
||||
|
@ -38,21 +36,17 @@ use {
|
|||
std::{collections::HashMap, num::NonZeroUsize},
|
||||
};
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn load_accounts(
|
||||
accounts_db: &AccountsDb,
|
||||
ancestors: &Ancestors,
|
||||
pub(crate) fn load_accounts<CB: TransactionProcessingCallback>(
|
||||
callbacks: &CB,
|
||||
txs: &[SanitizedTransaction],
|
||||
lock_results: &[TransactionCheckResult],
|
||||
error_counters: &mut TransactionErrorMetrics,
|
||||
rent_collector: &RentCollector,
|
||||
feature_set: &FeatureSet,
|
||||
fee_structure: &FeeStructure,
|
||||
account_overrides: Option<&AccountOverrides>,
|
||||
in_reward_interval: RewardInterval,
|
||||
program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
|
||||
loaded_programs: &LoadedProgramsForTxBatch,
|
||||
) -> Vec<TransactionLoadResult> {
|
||||
let feature_set = callbacks.get_feature_set();
|
||||
txs.iter()
|
||||
.zip(lock_results)
|
||||
.map(|etx| match etx {
|
||||
|
@ -75,15 +69,11 @@ pub(crate) fn load_accounts(
|
|||
|
||||
// load transactions
|
||||
let loaded_transaction = match load_transaction_accounts(
|
||||
accounts_db,
|
||||
ancestors,
|
||||
callbacks,
|
||||
tx,
|
||||
fee,
|
||||
error_counters,
|
||||
rent_collector,
|
||||
feature_set,
|
||||
account_overrides,
|
||||
in_reward_interval,
|
||||
program_accounts,
|
||||
loaded_programs,
|
||||
) {
|
||||
|
@ -113,27 +103,22 @@ pub(crate) fn load_accounts(
|
|||
.collect()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn load_transaction_accounts(
|
||||
accounts_db: &AccountsDb,
|
||||
ancestors: &Ancestors,
|
||||
fn load_transaction_accounts<CB: TransactionProcessingCallback>(
|
||||
callbacks: &CB,
|
||||
tx: &SanitizedTransaction,
|
||||
fee: u64,
|
||||
error_counters: &mut TransactionErrorMetrics,
|
||||
rent_collector: &RentCollector,
|
||||
feature_set: &FeatureSet,
|
||||
account_overrides: Option<&AccountOverrides>,
|
||||
reward_interval: RewardInterval,
|
||||
program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
|
||||
loaded_programs: &LoadedProgramsForTxBatch,
|
||||
) -> Result<LoadedTransaction> {
|
||||
let in_reward_interval = reward_interval == RewardInterval::InsideInterval;
|
||||
|
||||
// NOTE: this check will never fail because `tx` is sanitized
|
||||
if tx.signatures().is_empty() && fee != 0 {
|
||||
return Err(TransactionError::MissingSignatureForFee);
|
||||
}
|
||||
|
||||
let feature_set = callbacks.get_feature_set();
|
||||
|
||||
// There is no way to predict what program will execute without an error
|
||||
// If a fee can pay for execution then the program will be scheduled
|
||||
let mut validated_fee_payer = false;
|
||||
|
@ -143,6 +128,7 @@ fn load_transaction_accounts(
|
|||
let mut accounts_found = Vec::with_capacity(account_keys.len());
|
||||
let mut account_deps = Vec::with_capacity(account_keys.len());
|
||||
let mut rent_debits = RentDebits::default();
|
||||
let rent_collector = callbacks.get_rent_collector();
|
||||
|
||||
let set_exempt_rent_epoch_max =
|
||||
feature_set.is_active(&solana_sdk::feature_set::set_exempt_rent_epoch_max::id());
|
||||
|
@ -183,9 +169,9 @@ fn load_transaction_accounts(
|
|||
account_shared_data_from_program(key, program_accounts)
|
||||
.map(|program_account| (program.account_size, program_account, 0))?
|
||||
} else {
|
||||
accounts_db
|
||||
.load_with_fixed_root(ancestors, key)
|
||||
.map(|(mut account, _)| {
|
||||
callbacks
|
||||
.get_account_shared_data(key)
|
||||
.map(|mut account| {
|
||||
if message.is_writable(i) {
|
||||
if !feature_set
|
||||
.is_active(&feature_set::disable_rent_fees_collection::id())
|
||||
|
@ -253,15 +239,7 @@ fn load_transaction_accounts(
|
|||
validated_fee_payer = true;
|
||||
}
|
||||
|
||||
if in_reward_interval
|
||||
&& message.is_writable(i)
|
||||
&& solana_stake_program::check_id(account.owner())
|
||||
{
|
||||
error_counters.program_execution_temporarily_restricted += 1;
|
||||
return Err(TransactionError::ProgramExecutionTemporarilyRestricted {
|
||||
account_index: i as u8,
|
||||
});
|
||||
}
|
||||
callbacks.check_account_access(tx, i, &account, error_counters)?;
|
||||
|
||||
tx_rent += rent;
|
||||
rent_debits.insert(key, rent, account.lamports());
|
||||
|
@ -306,7 +284,7 @@ fn load_transaction_accounts(
|
|||
return Err(TransactionError::ProgramAccountNotFound);
|
||||
}
|
||||
|
||||
if !(is_builtin(program_account) || is_executable(program_account, feature_set)) {
|
||||
if !(is_builtin(program_account) || is_executable(program_account, &feature_set)) {
|
||||
error_counters.invalid_program_for_execution += 1;
|
||||
return Err(TransactionError::InvalidProgramForExecution);
|
||||
}
|
||||
|
@ -324,12 +302,10 @@ fn load_transaction_accounts(
|
|||
builtins_start_index.saturating_add(owner_index)
|
||||
} else {
|
||||
let owner_index = accounts.len();
|
||||
if let Some((owner_account, _)) =
|
||||
accounts_db.load_with_fixed_root(ancestors, owner_id)
|
||||
{
|
||||
if let Some(owner_account) = callbacks.get_account_shared_data(owner_id) {
|
||||
if !native_loader::check_id(owner_account.owner())
|
||||
|| !(is_builtin(&owner_account)
|
||||
|| is_executable(&owner_account, feature_set))
|
||||
|| is_executable(&owner_account, &feature_set))
|
||||
{
|
||||
error_counters.invalid_program_for_execution += 1;
|
||||
return Err(TransactionError::InvalidProgramForExecution);
|
||||
|
@ -484,7 +460,10 @@ mod tests {
|
|||
use {
|
||||
super::*,
|
||||
nonce::state::Versions as NonceVersions,
|
||||
solana_accounts_db::{accounts::Accounts, rent_collector::RentCollector},
|
||||
solana_accounts_db::{
|
||||
accounts::Accounts, accounts_db::AccountsDb, accounts_file::MatchAccountOwnerError,
|
||||
ancestors::Ancestors, rent_collector::RentCollector,
|
||||
},
|
||||
solana_program_runtime::{
|
||||
compute_budget_processor,
|
||||
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
|
||||
|
@ -494,6 +473,7 @@ mod tests {
|
|||
bpf_loader_upgradeable,
|
||||
compute_budget::ComputeBudgetInstruction,
|
||||
epoch_schedule::EpochSchedule,
|
||||
feature_set::FeatureSet,
|
||||
hash::Hash,
|
||||
instruction::CompiledInstruction,
|
||||
message::{Message, SanitizedMessage},
|
||||
|
@ -507,6 +487,41 @@ mod tests {
|
|||
std::{convert::TryFrom, sync::Arc},
|
||||
};
|
||||
|
||||
struct TestCallbacks {
|
||||
accounts: Accounts,
|
||||
ancestors: Ancestors,
|
||||
rent_collector: RentCollector,
|
||||
feature_set: Arc<FeatureSet>,
|
||||
}
|
||||
|
||||
impl TransactionProcessingCallback for TestCallbacks {
|
||||
fn account_matches_owners(
|
||||
&self,
|
||||
_account: &Pubkey,
|
||||
_owners: &[Pubkey],
|
||||
) -> std::result::Result<usize, MatchAccountOwnerError> {
|
||||
Err(MatchAccountOwnerError::UnableToLoad)
|
||||
}
|
||||
|
||||
fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
|
||||
self.accounts
|
||||
.load_without_fixed_root(&self.ancestors, pubkey)
|
||||
.map(|(acc, _slot)| acc)
|
||||
}
|
||||
|
||||
fn get_last_blockhash_and_lamports_per_signature(&self) -> (Hash, u64) {
|
||||
(Hash::new_unique(), 0)
|
||||
}
|
||||
|
||||
fn get_rent_collector(&self) -> &RentCollector {
|
||||
&self.rent_collector
|
||||
}
|
||||
|
||||
fn get_feature_set(&self) -> Arc<FeatureSet> {
|
||||
self.feature_set.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn load_accounts_with_fee_and_rent(
|
||||
tx: Transaction,
|
||||
ka: &[TransactionAccount],
|
||||
|
@ -525,17 +540,19 @@ mod tests {
|
|||
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||
feature_set.deactivate(&feature_set::disable_rent_fees_collection::id());
|
||||
let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
|
||||
let callbacks = TestCallbacks {
|
||||
accounts,
|
||||
ancestors,
|
||||
rent_collector: rent_collector.clone(),
|
||||
feature_set: Arc::new(feature_set.clone()),
|
||||
};
|
||||
load_accounts(
|
||||
&accounts.accounts_db,
|
||||
&ancestors,
|
||||
&callbacks,
|
||||
&[sanitized_tx],
|
||||
&[(Ok(()), None, Some(lamports_per_signature))],
|
||||
error_counters,
|
||||
rent_collector,
|
||||
feature_set,
|
||||
fee_structure,
|
||||
None,
|
||||
RewardInterval::OutsideInterval,
|
||||
&HashMap::new(),
|
||||
&LoadedProgramsForTxBatch::default(),
|
||||
)
|
||||
|
@ -990,26 +1007,27 @@ mod tests {
|
|||
}
|
||||
|
||||
fn load_accounts_no_store(
|
||||
accounts: &Accounts,
|
||||
accounts: Accounts,
|
||||
tx: Transaction,
|
||||
account_overrides: Option<&AccountOverrides>,
|
||||
) -> Vec<TransactionLoadResult> {
|
||||
let tx = SanitizedTransaction::from_transaction_for_tests(tx);
|
||||
let rent_collector = RentCollector::default();
|
||||
|
||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||
let mut error_counters = TransactionErrorMetrics::default();
|
||||
let callbacks = TestCallbacks {
|
||||
accounts,
|
||||
ancestors,
|
||||
rent_collector: RentCollector::default(),
|
||||
feature_set: Arc::new(FeatureSet::all_enabled()),
|
||||
};
|
||||
load_accounts(
|
||||
&accounts.accounts_db,
|
||||
&ancestors,
|
||||
&callbacks,
|
||||
&[tx],
|
||||
&[(Ok(()), None, Some(10))],
|
||||
&mut error_counters,
|
||||
&rent_collector,
|
||||
&FeatureSet::all_enabled(),
|
||||
&FeeStructure::default(),
|
||||
account_overrides,
|
||||
RewardInterval::OutsideInterval,
|
||||
&HashMap::new(),
|
||||
&LoadedProgramsForTxBatch::default(),
|
||||
)
|
||||
|
@ -1032,7 +1050,7 @@ mod tests {
|
|||
instructions,
|
||||
);
|
||||
|
||||
let loaded_accounts = load_accounts_no_store(&accounts, tx, None);
|
||||
let loaded_accounts = load_accounts_no_store(accounts, tx, None);
|
||||
assert_eq!(loaded_accounts.len(), 1);
|
||||
assert!(loaded_accounts[0].0.is_err());
|
||||
}
|
||||
|
@ -1060,7 +1078,7 @@ mod tests {
|
|||
instructions,
|
||||
);
|
||||
|
||||
let loaded_accounts = load_accounts_no_store(&accounts, tx, Some(&account_overrides));
|
||||
let loaded_accounts = load_accounts_no_store(accounts, tx, Some(&account_overrides));
|
||||
assert_eq!(loaded_accounts.len(), 1);
|
||||
let loaded_transaction = loaded_accounts[0].0.as_ref().unwrap();
|
||||
assert_eq!(loaded_transaction.accounts[0].0, keypair.pubkey());
|
||||
|
|
Loading…
Reference in New Issue