2021-12-03 09:00:31 -08:00
|
|
|
use {
|
|
|
|
crate::{
|
2022-04-22 03:32:31 -07:00
|
|
|
account_overrides::AccountOverrides,
|
2022-02-26 11:10:01 -08:00
|
|
|
account_rent_state::{check_rent_state_with_account, RentState},
|
2021-12-03 09:00:31 -08:00
|
|
|
accounts_db::{
|
|
|
|
AccountShrinkThreshold, AccountsAddRootTiming, AccountsDb, AccountsDbConfig,
|
2023-03-10 17:02:14 -08:00
|
|
|
IncludeSlotInHash, LoadHint, LoadedAccount, ScanStorageResult,
|
|
|
|
VerifyAccountsHashAndLamportsConfig, ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS,
|
|
|
|
ACCOUNTS_DB_CONFIG_FOR_TESTING,
|
2021-12-03 09:00:31 -08:00
|
|
|
},
|
2022-06-22 10:13:56 -07:00
|
|
|
accounts_index::{
|
|
|
|
AccountSecondaryIndexes, IndexKey, ScanConfig, ScanError, ScanResult, ZeroLamport,
|
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
accounts_update_notifier_interface::AccountsUpdateNotifier,
|
|
|
|
ancestors::Ancestors,
|
2023-04-14 12:41:10 -07:00
|
|
|
bank::{Bank, NonceFull, NonceInfo, TransactionCheckResult, TransactionExecutionResult},
|
2021-12-03 09:00:31 -08:00
|
|
|
blockhash_queue::BlockhashQueue,
|
|
|
|
rent_collector::RentCollector,
|
2023-04-14 12:41:10 -07:00
|
|
|
rent_debits::RentDebits,
|
2022-06-22 10:13:56 -07:00
|
|
|
storable_accounts::StorableAccounts,
|
2021-12-03 09:00:31 -08:00
|
|
|
system_instruction_processor::{get_system_account_kind, SystemAccountKind},
|
2022-04-23 16:10:47 -07:00
|
|
|
transaction_error_metrics::TransactionErrorMetrics,
|
2021-08-10 03:45:46 -07:00
|
|
|
},
|
2023-01-13 12:05:15 -08:00
|
|
|
dashmap::DashMap,
|
2023-03-28 11:49:56 -07:00
|
|
|
itertools::Itertools,
|
2021-12-03 09:00:31 -08:00
|
|
|
log::*,
|
2022-03-16 17:21:07 -07:00
|
|
|
solana_address_lookup_table_program::{error::AddressLookupError, state::AddressLookupTable},
|
2023-03-28 11:49:56 -07:00
|
|
|
solana_program_runtime::{
|
|
|
|
compute_budget::{self, ComputeBudget},
|
|
|
|
loaded_programs::{LoadedProgram, LoadedProgramType},
|
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
solana_sdk::{
|
|
|
|
account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
|
|
|
|
account_utils::StateMut,
|
2023-03-28 11:49:56 -07:00
|
|
|
bpf_loader_upgradeable,
|
2022-11-01 06:36:59 -07:00
|
|
|
clock::{BankId, Slot},
|
2022-09-09 15:24:21 -07:00
|
|
|
feature_set::{
|
2023-03-28 11:49:56 -07:00
|
|
|
self, add_set_tx_loaded_accounts_data_size_instruction,
|
|
|
|
delay_visibility_of_program_deployment, enable_request_heap_frame_ix,
|
2023-03-21 16:41:41 -07:00
|
|
|
include_loaded_accounts_data_size_in_fee_calculation,
|
2023-02-24 07:27:49 -08:00
|
|
|
remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix,
|
2023-03-28 11:50:49 -07:00
|
|
|
simplify_writable_program_account_check, use_default_units_in_fee_calculation,
|
|
|
|
FeatureSet,
|
2022-09-09 15:24:21 -07:00
|
|
|
},
|
2022-02-11 16:23:16 -08:00
|
|
|
fee::FeeStructure,
|
2021-12-03 09:00:31 -08:00
|
|
|
genesis_config::ClusterType,
|
2022-01-06 19:59:09 -08:00
|
|
|
message::{
|
|
|
|
v0::{LoadedAddresses, MessageAddressTableLookup},
|
|
|
|
SanitizedMessage,
|
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
native_loader,
|
2022-06-03 07:36:27 -07:00
|
|
|
nonce::{
|
|
|
|
state::{DurableNonce, Versions as NonceVersions},
|
|
|
|
State as NonceState,
|
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
pubkey::Pubkey,
|
2023-01-31 20:51:35 -08:00
|
|
|
saturating_add_assign,
|
2022-01-06 19:59:09 -08:00
|
|
|
slot_hashes::SlotHashes,
|
2021-12-03 09:00:31 -08:00
|
|
|
system_program,
|
2023-02-15 14:03:50 -08:00
|
|
|
sysvar::{self, instructions::construct_instructions_data},
|
2022-03-16 17:21:07 -07:00
|
|
|
transaction::{Result, SanitizedTransaction, TransactionAccountLocks, TransactionError},
|
2022-09-06 02:31:40 -07:00
|
|
|
transaction_context::{IndexOfAccount, TransactionAccount},
|
2021-12-03 09:00:31 -08:00
|
|
|
},
|
|
|
|
std::{
|
|
|
|
cmp::Reverse,
|
|
|
|
collections::{hash_map, BinaryHeap, HashMap, HashSet},
|
2023-01-31 20:51:35 -08:00
|
|
|
num::NonZeroUsize,
|
2021-12-03 09:00:31 -08:00
|
|
|
ops::RangeBounds,
|
|
|
|
path::PathBuf,
|
|
|
|
sync::{
|
2022-09-12 11:51:12 -07:00
|
|
|
atomic::{AtomicBool, AtomicUsize, Ordering},
|
2021-12-03 09:00:31 -08:00
|
|
|
Arc, Mutex,
|
|
|
|
},
|
2020-11-29 11:21:55 -08:00
|
|
|
},
|
2020-01-28 17:03:20 -08:00
|
|
|
};
|
2019-08-28 08:38:32 -07:00
|
|
|
|
2022-04-19 19:34:00 -07:00
|
|
|
pub type PubkeyAccountSlot = (Pubkey, AccountSharedData, Slot);
|
|
|
|
|
2021-02-22 23:56:43 -08:00
|
|
|
#[derive(Debug, Default, AbiExample)]
|
|
|
|
pub struct AccountLocks {
|
|
|
|
write_locks: HashSet<Pubkey>,
|
|
|
|
readonly_locks: HashMap<Pubkey, u64>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AccountLocks {
|
|
|
|
fn is_locked_readonly(&self, key: &Pubkey) -> bool {
|
|
|
|
self.readonly_locks
|
|
|
|
.get(key)
|
|
|
|
.map_or(false, |count| *count > 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_locked_write(&self, key: &Pubkey) -> bool {
|
|
|
|
self.write_locks.contains(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn insert_new_readonly(&mut self, key: &Pubkey) {
|
|
|
|
assert!(self.readonly_locks.insert(*key, 1).is_none());
|
|
|
|
}
|
|
|
|
|
|
|
|
fn lock_readonly(&mut self, key: &Pubkey) -> bool {
|
|
|
|
self.readonly_locks.get_mut(key).map_or(false, |count| {
|
|
|
|
*count += 1;
|
|
|
|
true
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unlock_readonly(&mut self, key: &Pubkey) {
|
|
|
|
if let hash_map::Entry::Occupied(mut occupied_entry) = self.readonly_locks.entry(*key) {
|
|
|
|
let count = occupied_entry.get_mut();
|
|
|
|
*count -= 1;
|
|
|
|
if *count == 0 {
|
|
|
|
occupied_entry.remove_entry();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unlock_write(&mut self, key: &Pubkey) {
|
|
|
|
self.write_locks.remove(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
/// This structure handles synchronization for db
|
2021-08-05 15:27:13 -07:00
|
|
|
#[derive(Debug, AbiExample)]
|
2018-12-17 12:41:23 -08:00
|
|
|
pub struct Accounts {
|
2021-02-18 23:42:09 -08:00
|
|
|
/// Single global AccountsDb
|
|
|
|
pub accounts_db: Arc<AccountsDb>,
|
2018-12-17 12:41:23 -08:00
|
|
|
|
2021-02-22 23:56:43 -08:00
|
|
|
/// set of read-only and writable accounts which are currently
|
|
|
|
/// being processed by banking/replay threads
|
|
|
|
pub(crate) account_locks: Mutex<AccountLocks>,
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2019-08-23 14:04:53 -07:00
|
|
|
// for the load instructions
|
2019-11-20 12:27:02 -08:00
|
|
|
pub type TransactionRent = u64;
|
2022-09-06 02:31:40 -07:00
|
|
|
pub type TransactionProgramIndices = Vec<Vec<IndexOfAccount>>;
|
2022-05-22 18:00:42 -07:00
|
|
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
2021-01-25 01:35:08 -08:00
|
|
|
pub struct LoadedTransaction {
|
2021-12-27 09:49:32 -08:00
|
|
|
pub accounts: Vec<TransactionAccount>,
|
2021-09-09 23:36:21 -07:00
|
|
|
pub program_indices: TransactionProgramIndices,
|
2021-01-25 01:35:08 -08:00
|
|
|
pub rent: TransactionRent,
|
2021-05-26 14:43:15 -07:00
|
|
|
pub rent_debits: RentDebits,
|
2021-01-25 01:35:08 -08:00
|
|
|
}
|
2019-08-23 14:04:53 -07:00
|
|
|
|
2021-11-18 16:07:17 -08:00
|
|
|
pub type TransactionLoadResult = (Result<LoadedTransaction>, Option<NonceFull>);
|
2019-09-08 11:13:59 -07:00
|
|
|
|
2020-05-04 16:46:10 -07:00
|
|
|
pub enum AccountAddressFilter {
|
2020-06-17 20:54:52 -07:00
|
|
|
Exclude, // exclude all addresses matching the filter
|
2020-05-04 16:46:10 -07:00
|
|
|
Include, // only include addresses matching the filter
|
|
|
|
}
|
|
|
|
|
2019-04-16 13:32:22 -07:00
|
|
|
impl Accounts {
|
2021-08-05 15:27:13 -07:00
|
|
|
pub fn default_for_tests() -> Self {
|
2023-02-15 15:34:52 -08:00
|
|
|
Self::new_empty(AccountsDb::default_for_tests())
|
2021-08-05 15:27:13 -07:00
|
|
|
}
|
|
|
|
|
2021-08-09 07:01:12 -07:00
|
|
|
pub fn new_with_config_for_tests(
|
|
|
|
paths: Vec<PathBuf>,
|
|
|
|
cluster_type: &ClusterType,
|
|
|
|
account_indexes: AccountSecondaryIndexes,
|
|
|
|
shrink_ratio: AccountShrinkThreshold,
|
|
|
|
) -> Self {
|
|
|
|
Self::new_with_config(
|
|
|
|
paths,
|
|
|
|
cluster_type,
|
|
|
|
account_indexes,
|
|
|
|
shrink_ratio,
|
2021-09-07 21:30:38 -07:00
|
|
|
Some(ACCOUNTS_DB_CONFIG_FOR_TESTING),
|
2021-09-30 14:26:17 -07:00
|
|
|
None,
|
2022-09-12 11:51:12 -07:00
|
|
|
&Arc::default(),
|
2021-08-09 07:01:12 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-08-06 10:36:42 -07:00
|
|
|
pub fn new_with_config_for_benches(
|
|
|
|
paths: Vec<PathBuf>,
|
|
|
|
cluster_type: &ClusterType,
|
|
|
|
account_indexes: AccountSecondaryIndexes,
|
|
|
|
shrink_ratio: AccountShrinkThreshold,
|
|
|
|
) -> Self {
|
|
|
|
Self::new_with_config(
|
|
|
|
paths,
|
|
|
|
cluster_type,
|
|
|
|
account_indexes,
|
|
|
|
shrink_ratio,
|
2021-09-07 21:30:38 -07:00
|
|
|
Some(ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS),
|
2021-09-30 14:26:17 -07:00
|
|
|
None,
|
2022-09-12 11:51:12 -07:00
|
|
|
&Arc::default(),
|
2021-08-06 10:36:42 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-01-11 17:00:23 -08:00
|
|
|
pub fn new_with_config(
|
2020-12-31 18:06:03 -08:00
|
|
|
paths: Vec<PathBuf>,
|
|
|
|
cluster_type: &ClusterType,
|
2021-05-10 07:22:48 -07:00
|
|
|
account_indexes: AccountSecondaryIndexes,
|
2021-06-09 21:21:32 -07:00
|
|
|
shrink_ratio: AccountShrinkThreshold,
|
2021-09-07 21:30:38 -07:00
|
|
|
accounts_db_config: Option<AccountsDbConfig>,
|
2021-09-30 14:26:17 -07:00
|
|
|
accounts_update_notifier: Option<AccountsUpdateNotifier>,
|
2022-09-12 11:51:12 -07:00
|
|
|
exit: &Arc<AtomicBool>,
|
2020-12-31 18:06:03 -08:00
|
|
|
) -> Self {
|
2023-02-15 15:34:52 -08:00
|
|
|
Self::new_empty(AccountsDb::new_with_config(
|
|
|
|
paths,
|
|
|
|
cluster_type,
|
|
|
|
account_indexes,
|
|
|
|
shrink_ratio,
|
|
|
|
accounts_db_config,
|
|
|
|
accounts_update_notifier,
|
|
|
|
exit,
|
|
|
|
))
|
2020-05-22 10:54:24 -07:00
|
|
|
}
|
|
|
|
|
2021-02-18 23:42:09 -08:00
|
|
|
pub(crate) fn new_empty(accounts_db: AccountsDb) -> Self {
|
2023-02-15 15:34:52 -08:00
|
|
|
Self::new(Arc::new(accounts_db))
|
|
|
|
}
|
|
|
|
|
2023-02-23 13:50:51 -08:00
|
|
|
pub(crate) fn new(accounts_db: Arc<AccountsDb>) -> Self {
|
2020-07-13 07:00:59 -07:00
|
|
|
Self {
|
2023-02-15 15:34:52 -08:00
|
|
|
accounts_db,
|
2021-02-22 23:56:43 -08:00
|
|
|
account_locks: Mutex::new(AccountLocks::default()),
|
2020-07-13 07:00:59 -07:00
|
|
|
}
|
2020-03-22 11:10:04 -07:00
|
|
|
}
|
|
|
|
|
2021-08-20 08:32:28 -07:00
|
|
|
fn construct_instructions_account(
|
|
|
|
message: &SanitizedMessage,
|
|
|
|
is_owned_by_sysvar: bool,
|
|
|
|
) -> AccountSharedData {
|
2022-01-20 01:33:49 -08:00
|
|
|
let data = construct_instructions_data(&message.decompile_instructions());
|
2021-08-20 08:32:28 -07:00
|
|
|
let owner = if is_owned_by_sysvar {
|
|
|
|
sysvar::id()
|
|
|
|
} else {
|
|
|
|
system_program::id()
|
|
|
|
};
|
2021-03-11 16:09:04 -08:00
|
|
|
AccountSharedData::from(Account {
|
2021-03-05 09:02:35 -08:00
|
|
|
data,
|
2021-08-20 08:32:28 -07:00
|
|
|
owner,
|
2021-03-11 16:09:04 -08:00
|
|
|
..Account::default()
|
|
|
|
})
|
2020-09-19 12:17:46 -07:00
|
|
|
}
|
|
|
|
|
2023-01-31 20:51:35 -08:00
|
|
|
/// If feature `cap_transaction_accounts_data_size` is active, total accounts data a
|
2023-03-08 17:41:45 -08:00
|
|
|
/// transaction can load is limited to
|
|
|
|
/// if `set_tx_loaded_accounts_data_size` instruction is not activated or not used, then
|
|
|
|
/// default value of 64MiB to not break anyone in Mainnet-beta today
|
|
|
|
/// else
|
|
|
|
/// user requested loaded accounts size.
|
|
|
|
/// Note, requesting zero bytes will result transaction error
|
2023-01-31 20:51:35 -08:00
|
|
|
fn get_requested_loaded_accounts_data_size_limit(
|
2023-03-08 17:41:45 -08:00
|
|
|
tx: &SanitizedTransaction,
|
2023-01-31 20:51:35 -08:00
|
|
|
feature_set: &FeatureSet,
|
2023-03-08 17:41:45 -08:00
|
|
|
) -> Result<Option<NonZeroUsize>> {
|
|
|
|
if feature_set.is_active(&feature_set::cap_transaction_accounts_data_size::id()) {
|
|
|
|
let mut compute_budget =
|
|
|
|
ComputeBudget::new(compute_budget::MAX_COMPUTE_UNIT_LIMIT as u64);
|
|
|
|
let _process_transaction_result = compute_budget.process_instructions(
|
|
|
|
tx.message().program_instructions_iter(),
|
|
|
|
feature_set.is_active(&use_default_units_in_fee_calculation::id()),
|
|
|
|
!feature_set.is_active(&remove_deprecated_request_unit_ix::id()),
|
|
|
|
true, // don't reject txs that use request heap size ix
|
|
|
|
feature_set.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
|
|
|
|
);
|
|
|
|
// sanitize against setting size limit to zero
|
|
|
|
NonZeroUsize::new(compute_budget.loaded_accounts_data_size_limit).map_or(
|
|
|
|
Err(TransactionError::InvalidLoadedAccountsDataSizeLimit),
|
|
|
|
|v| Ok(Some(v)),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// feature not activated, no loaded accounts data limit imposed.
|
|
|
|
Ok(None)
|
|
|
|
}
|
2023-01-31 20:51:35 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Accumulate loaded account data size into `accumulated_accounts_data_size`.
|
|
|
|
/// Returns TransactionErr::MaxLoadedAccountsDataSizeExceeded if
|
|
|
|
/// `requested_loaded_accounts_data_size_limit` is specified and
|
|
|
|
/// `accumulated_accounts_data_size` exceeds it.
|
|
|
|
fn accumulate_and_check_loaded_account_data_size(
|
|
|
|
accumulated_loaded_accounts_data_size: &mut usize,
|
|
|
|
account_data_size: usize,
|
|
|
|
requested_loaded_accounts_data_size_limit: Option<NonZeroUsize>,
|
|
|
|
error_counters: &mut TransactionErrorMetrics,
|
|
|
|
) -> Result<()> {
|
|
|
|
if let Some(requested_loaded_accounts_data_size) = requested_loaded_accounts_data_size_limit
|
|
|
|
{
|
|
|
|
saturating_add_assign!(*accumulated_loaded_accounts_data_size, account_data_size);
|
|
|
|
if *accumulated_loaded_accounts_data_size > requested_loaded_accounts_data_size.get() {
|
|
|
|
error_counters.max_loaded_accounts_data_size_exceeded += 1;
|
|
|
|
Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-28 11:49:56 -07:00
|
|
|
fn account_shared_data_from_program(
|
|
|
|
key: &Pubkey,
|
|
|
|
feature_set: &FeatureSet,
|
|
|
|
program: &LoadedProgram,
|
|
|
|
program_accounts: &HashMap<Pubkey, &Pubkey>,
|
|
|
|
) -> Result<AccountSharedData> {
|
|
|
|
// Check for tombstone
|
2023-03-30 11:16:01 -07:00
|
|
|
let result = match &program.program {
|
2023-03-28 11:49:56 -07:00
|
|
|
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(()),
|
|
|
|
};
|
2023-03-30 11:16:01 -07:00
|
|
|
if feature_set.is_active(&simplify_writable_program_account_check::id()) {
|
|
|
|
// Currently CPI only fails if an execution is actually attempted. With this check it
|
|
|
|
// would also fail if a transaction just references an invalid program. So the checking
|
|
|
|
// of the result is being feature gated.
|
|
|
|
result?;
|
|
|
|
}
|
2023-03-28 11:49:56 -07:00
|
|
|
// 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)]
|
2022-11-18 07:44:09 -08:00
|
|
|
fn load_transaction_accounts(
|
2019-10-23 22:01:22 -07:00
|
|
|
&self,
|
2020-04-26 19:07:03 -07:00
|
|
|
ancestors: &Ancestors,
|
2021-08-17 15:17:56 -07:00
|
|
|
tx: &SanitizedTransaction,
|
2019-03-29 15:11:21 -07:00
|
|
|
fee: u64,
|
2022-04-23 16:10:47 -07:00
|
|
|
error_counters: &mut TransactionErrorMetrics,
|
2019-08-23 14:04:53 -07:00
|
|
|
rent_collector: &RentCollector,
|
2020-09-21 22:36:23 -07:00
|
|
|
feature_set: &FeatureSet,
|
2022-04-22 03:32:31 -07:00
|
|
|
account_overrides: Option<&AccountOverrides>,
|
2023-03-28 11:49:56 -07:00
|
|
|
program_accounts: &HashMap<Pubkey, &Pubkey>,
|
|
|
|
loaded_programs: &HashMap<Pubkey, Arc<LoadedProgram>>,
|
2021-01-25 01:35:08 -08:00
|
|
|
) -> Result<LoadedTransaction> {
|
2021-08-17 15:17:56 -07:00
|
|
|
// NOTE: this check will never fail because `tx` is sanitized
|
|
|
|
if tx.signatures().is_empty() && fee != 0 {
|
2022-11-18 07:44:09 -08:00
|
|
|
return Err(TransactionError::MissingSignatureForFee);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
let mut tx_rent: TransactionRent = 0;
|
|
|
|
let message = tx.message();
|
|
|
|
let account_keys = message.account_keys();
|
2023-03-28 11:49:56 -07:00
|
|
|
let mut accounts_found = Vec::with_capacity(account_keys.len());
|
2022-11-18 07:44:09 -08:00
|
|
|
let mut account_deps = Vec::with_capacity(account_keys.len());
|
|
|
|
let mut rent_debits = RentDebits::default();
|
2022-11-14 08:29:35 -08:00
|
|
|
|
2023-01-02 07:51:08 -08:00
|
|
|
let set_exempt_rent_epoch_max =
|
|
|
|
feature_set.is_active(&solana_sdk::feature_set::set_exempt_rent_epoch_max::id());
|
2023-01-31 20:51:35 -08:00
|
|
|
|
|
|
|
let requested_loaded_accounts_data_size_limit =
|
2023-03-08 17:41:45 -08:00
|
|
|
Self::get_requested_loaded_accounts_data_size_limit(tx, feature_set)?;
|
2023-01-31 20:51:35 -08:00
|
|
|
let mut accumulated_accounts_data_size: usize = 0;
|
|
|
|
|
2023-03-28 11:49:56 -07:00
|
|
|
let instruction_accounts = message
|
|
|
|
.instructions()
|
|
|
|
.iter()
|
|
|
|
.flat_map(|instruction| &instruction.accounts)
|
|
|
|
.unique()
|
|
|
|
.collect::<Vec<&u8>>();
|
|
|
|
|
2022-11-18 07:44:09 -08:00
|
|
|
let mut accounts = account_keys
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, key)| {
|
2023-01-27 12:24:21 -08:00
|
|
|
let mut account_found = true;
|
|
|
|
#[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()),
|
|
|
|
)
|
2021-09-09 23:36:21 -07:00
|
|
|
} else {
|
2023-03-28 11:49:56 -07:00
|
|
|
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) =
|
2023-01-27 12:24:21 -08:00
|
|
|
account_overrides.and_then(|overrides| overrides.get(key))
|
|
|
|
{
|
2023-03-28 11:49:56 -07:00
|
|
|
(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))?
|
2020-09-19 12:17:46 -07:00
|
|
|
} else {
|
2023-01-27 12:24:21 -08:00
|
|
|
self.accounts_db
|
|
|
|
.load_with_fixed_root(ancestors, key)
|
|
|
|
.map(|(mut account, _)| {
|
|
|
|
if message.is_writable(i) {
|
|
|
|
let rent_due = rent_collector
|
|
|
|
.collect_from_existing_account(
|
|
|
|
key,
|
|
|
|
&mut account,
|
|
|
|
self.accounts_db.filler_account_suffix.as_ref(),
|
|
|
|
set_exempt_rent_epoch_max,
|
|
|
|
)
|
|
|
|
.rent_amount;
|
2023-03-28 11:49:56 -07:00
|
|
|
(account.data().len(), account, rent_due)
|
2023-01-27 12:24:21 -08:00
|
|
|
} else {
|
2023-03-28 11:49:56 -07:00
|
|
|
(account.data().len(), account, 0)
|
2023-01-27 12:24:21 -08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
account_found = false;
|
|
|
|
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).
|
|
|
|
// Currently, rent collection sets rent_epoch to u64::MAX, but initializing the account
|
|
|
|
// with this field already set would allow us to skip rent collection for these accounts.
|
|
|
|
default_account.set_rent_epoch(u64::MAX);
|
|
|
|
}
|
2023-03-28 11:49:56 -07:00
|
|
|
(default_account.data().len(), default_account, 0)
|
2023-01-27 12:24:21 -08:00
|
|
|
})
|
|
|
|
};
|
2023-01-31 20:51:35 -08:00
|
|
|
Self::accumulate_and_check_loaded_account_data_size(
|
|
|
|
&mut accumulated_accounts_data_size,
|
2023-03-28 11:49:56 -07:00
|
|
|
account_size,
|
2023-01-31 20:51:35 -08:00
|
|
|
requested_loaded_accounts_data_size_limit,
|
|
|
|
error_counters,
|
|
|
|
)?;
|
2022-05-26 20:44:29 -07:00
|
|
|
|
2023-01-27 12:24:21 -08:00
|
|
|
if !validated_fee_payer && message.is_non_loader_key(i) {
|
|
|
|
if i != 0 {
|
|
|
|
warn!("Payer index should be 0! {:?}", tx);
|
2022-05-26 20:44:29 -07:00
|
|
|
}
|
|
|
|
|
2023-01-27 12:24:21 -08:00
|
|
|
Self::validate_fee_payer(
|
|
|
|
key,
|
|
|
|
&mut account,
|
|
|
|
i as IndexOfAccount,
|
|
|
|
error_counters,
|
|
|
|
rent_collector,
|
|
|
|
feature_set,
|
|
|
|
fee,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
validated_fee_payer = true;
|
|
|
|
}
|
2021-09-08 14:21:52 -07:00
|
|
|
|
2023-01-27 12:24:21 -08:00
|
|
|
if bpf_loader_upgradeable::check_id(account.owner()) {
|
2023-03-28 11:50:49 -07:00
|
|
|
if !feature_set.is_active(&simplify_writable_program_account_check::id())
|
|
|
|
&& message.is_writable(i)
|
|
|
|
&& !message.is_upgradeable_loader_present()
|
|
|
|
{
|
2023-01-27 12:24:21 -08:00
|
|
|
error_counters.invalid_writable_account += 1;
|
|
|
|
return Err(TransactionError::InvalidWritableAccount);
|
|
|
|
}
|
|
|
|
} else if account.executable() && message.is_writable(i) {
|
|
|
|
error_counters.invalid_writable_account += 1;
|
|
|
|
return Err(TransactionError::InvalidWritableAccount);
|
|
|
|
}
|
2020-12-22 09:26:55 -08:00
|
|
|
|
2023-01-27 12:24:21 -08:00
|
|
|
tx_rent += rent;
|
|
|
|
rent_debits.insert(key, rent, account.lamports());
|
2021-05-25 22:11:26 -07:00
|
|
|
|
2023-01-27 12:24:21 -08:00
|
|
|
account
|
2020-09-19 12:17:46 -07:00
|
|
|
};
|
2022-11-14 08:29:35 -08:00
|
|
|
|
2023-03-28 11:49:56 -07:00
|
|
|
accounts_found.push(account_found);
|
2022-11-18 07:44:09 -08:00
|
|
|
Ok((*key, account))
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>>>()?;
|
|
|
|
|
2023-01-27 12:24:21 -08:00
|
|
|
if !validated_fee_payer {
|
|
|
|
error_counters.account_not_found += 1;
|
|
|
|
return Err(TransactionError::AccountNotFound);
|
|
|
|
}
|
|
|
|
|
2022-11-18 07:44:09 -08:00
|
|
|
// Appends the account_deps at the end of the accounts,
|
|
|
|
// this way they can be accessed in a uniform way.
|
|
|
|
// At places where only the accounts are needed,
|
|
|
|
// the account_deps are truncated using e.g:
|
|
|
|
// accounts.iter().take(message.account_keys.len())
|
|
|
|
accounts.append(&mut account_deps);
|
|
|
|
|
2023-01-29 10:27:49 -08:00
|
|
|
let disable_builtin_loader_ownership_chains =
|
|
|
|
feature_set.is_active(&feature_set::disable_builtin_loader_ownership_chains::ID);
|
2023-01-27 12:24:21 -08:00
|
|
|
let builtins_start_index = accounts.len();
|
|
|
|
let program_indices = message
|
|
|
|
.instructions()
|
|
|
|
.iter()
|
|
|
|
.map(|instruction| {
|
|
|
|
let mut account_indices = Vec::new();
|
|
|
|
let mut program_index = instruction.program_id_index as usize;
|
|
|
|
for _ in 0..5 {
|
|
|
|
let (program_id, program_account) = accounts
|
|
|
|
.get(program_index)
|
|
|
|
.ok_or(TransactionError::ProgramAccountNotFound)?;
|
2023-03-28 11:49:56 -07:00
|
|
|
let account_found = accounts_found.get(program_index).unwrap_or(&true);
|
2023-01-27 12:24:21 -08:00
|
|
|
if native_loader::check_id(program_id) {
|
|
|
|
return Ok(account_indices);
|
|
|
|
}
|
|
|
|
if !account_found {
|
|
|
|
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);
|
|
|
|
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)
|
|
|
|
{
|
2023-01-31 20:51:35 -08:00
|
|
|
Self::accumulate_and_check_loaded_account_data_size(
|
|
|
|
&mut accumulated_accounts_data_size,
|
|
|
|
program_account.data().len(),
|
|
|
|
requested_loaded_accounts_data_size_limit,
|
|
|
|
error_counters,
|
|
|
|
)?;
|
2023-01-27 12:24:21 -08:00
|
|
|
accounts.push((*owner_id, program_account));
|
|
|
|
} else {
|
|
|
|
error_counters.account_not_found += 1;
|
|
|
|
return Err(TransactionError::ProgramAccountNotFound);
|
|
|
|
}
|
|
|
|
owner_index
|
|
|
|
};
|
2023-01-29 10:27:49 -08:00
|
|
|
if disable_builtin_loader_ownership_chains {
|
|
|
|
account_indices.insert(0, program_index as IndexOfAccount);
|
|
|
|
return Ok(account_indices);
|
|
|
|
}
|
2023-01-27 12:24:21 -08:00
|
|
|
}
|
|
|
|
error_counters.call_chain_too_deep += 1;
|
|
|
|
Err(TransactionError::CallChainTooDeep)
|
2022-11-18 07:44:09 -08:00
|
|
|
})
|
2023-01-27 12:24:21 -08:00
|
|
|
.collect::<Result<Vec<Vec<IndexOfAccount>>>>()?;
|
|
|
|
|
|
|
|
Ok(LoadedTransaction {
|
|
|
|
accounts,
|
|
|
|
program_indices,
|
|
|
|
rent: tx_rent,
|
|
|
|
rent_debits,
|
|
|
|
})
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-01-04 16:39:04 -08:00
|
|
|
|
2022-05-26 20:44:29 -07:00
|
|
|
fn validate_fee_payer(
|
|
|
|
payer_address: &Pubkey,
|
|
|
|
payer_account: &mut AccountSharedData,
|
2022-09-06 02:31:40 -07:00
|
|
|
payer_index: IndexOfAccount,
|
2022-05-25 03:44:03 -07:00
|
|
|
error_counters: &mut TransactionErrorMetrics,
|
|
|
|
rent_collector: &RentCollector,
|
|
|
|
feature_set: &FeatureSet,
|
|
|
|
fee: u64,
|
|
|
|
) -> Result<()> {
|
|
|
|
if payer_account.lamports() == 0 {
|
|
|
|
error_counters.account_not_found += 1;
|
|
|
|
return Err(TransactionError::AccountNotFound);
|
|
|
|
}
|
|
|
|
let min_balance = match get_system_account_kind(payer_account).ok_or_else(|| {
|
|
|
|
error_counters.invalid_account_for_fee += 1;
|
|
|
|
TransactionError::InvalidAccountForFee
|
|
|
|
})? {
|
|
|
|
SystemAccountKind::System => 0,
|
|
|
|
SystemAccountKind::Nonce => {
|
|
|
|
// Should we ever allow a fees charge to zero a nonce account's
|
|
|
|
// balance. The state MUST be set to uninitialized in that case
|
|
|
|
rent_collector.rent.minimum_balance(NonceState::size())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if payer_account.lamports() < fee + min_balance {
|
|
|
|
error_counters.insufficient_funds += 1;
|
|
|
|
return Err(TransactionError::InsufficientFundsForFee);
|
|
|
|
}
|
|
|
|
let payer_pre_rent_state = RentState::from_account(payer_account, &rent_collector.rent);
|
|
|
|
payer_account
|
|
|
|
.checked_sub_lamports(fee)
|
|
|
|
.map_err(|_| TransactionError::InsufficientFundsForFee)?;
|
|
|
|
|
|
|
|
let payer_post_rent_state = RentState::from_account(payer_account, &rent_collector.rent);
|
2022-06-16 14:35:25 -07:00
|
|
|
check_rent_state_with_account(
|
2022-05-25 03:44:03 -07:00
|
|
|
&payer_pre_rent_state,
|
|
|
|
&payer_post_rent_state,
|
|
|
|
payer_address,
|
|
|
|
payer_account,
|
|
|
|
feature_set
|
|
|
|
.is_active(&feature_set::include_account_index_in_rent_error::ID)
|
2022-08-22 18:01:03 -07:00
|
|
|
.then_some(payer_index),
|
2022-06-16 14:35:25 -07:00
|
|
|
)
|
2022-05-25 03:44:03 -07:00
|
|
|
}
|
|
|
|
|
2023-02-16 13:29:38 -08:00
|
|
|
/// 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.
|
|
|
|
pub fn filter_executable_program_accounts<'a>(
|
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
|
|
|
txs: &[SanitizedTransaction],
|
|
|
|
lock_results: &mut [TransactionCheckResult],
|
|
|
|
program_owners: &[&'a Pubkey],
|
|
|
|
hash_queue: &BlockhashQueue,
|
|
|
|
) -> HashMap<Pubkey, &'a Pubkey> {
|
|
|
|
let mut result = HashMap::new();
|
|
|
|
lock_results.iter_mut().zip(txs).for_each(|etx| {
|
|
|
|
if let ((Ok(()), nonce), tx) = etx {
|
|
|
|
if nonce
|
|
|
|
.as_ref()
|
|
|
|
.map(|nonce| nonce.lamports_per_signature())
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
hash_queue.get_lamports_per_signature(tx.message().recent_blockhash())
|
|
|
|
})
|
|
|
|
.is_some()
|
|
|
|
{
|
|
|
|
tx.message()
|
|
|
|
.account_keys()
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.for_each(|(i, key)| {
|
|
|
|
if !tx.message().is_writable(i) && !result.contains_key(key) {
|
|
|
|
if let Ok(index) = self.accounts_db.account_matches_owners(
|
|
|
|
ancestors,
|
|
|
|
key,
|
|
|
|
program_owners,
|
|
|
|
) {
|
|
|
|
program_owners
|
|
|
|
.get(index)
|
|
|
|
.and_then(|owner| result.insert(*key, *owner));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// If the transaction's nonce account was not valid, and blockhash is not found,
|
|
|
|
// the transaction will fail to process. Let's not load any programs from the
|
|
|
|
// transaction, and update the status of the transaction.
|
|
|
|
*etx.0 = (Err(TransactionError::BlockhashNotFound), None);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
2022-04-22 03:32:31 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2021-08-17 15:17:56 -07:00
|
|
|
pub fn load_accounts(
|
2018-12-24 16:11:20 -08:00
|
|
|
&self,
|
2020-04-26 19:07:03 -07:00
|
|
|
ancestors: &Ancestors,
|
2021-08-17 15:17:56 -07:00
|
|
|
txs: &[SanitizedTransaction],
|
2020-11-29 11:21:55 -08:00
|
|
|
lock_results: Vec<TransactionCheckResult>,
|
2019-06-10 22:18:32 -07:00
|
|
|
hash_queue: &BlockhashQueue,
|
2022-04-23 16:10:47 -07:00
|
|
|
error_counters: &mut TransactionErrorMetrics,
|
2019-08-23 14:04:53 -07:00
|
|
|
rent_collector: &RentCollector,
|
2020-09-21 22:36:23 -07:00
|
|
|
feature_set: &FeatureSet,
|
2022-02-11 16:23:16 -08:00
|
|
|
fee_structure: &FeeStructure,
|
2022-04-22 03:32:31 -07:00
|
|
|
account_overrides: Option<&AccountOverrides>,
|
2023-03-28 11:49:56 -07:00
|
|
|
program_accounts: &HashMap<Pubkey, &Pubkey>,
|
|
|
|
loaded_programs: &HashMap<Pubkey, Arc<LoadedProgram>>,
|
2020-11-29 11:21:55 -08:00
|
|
|
) -> Vec<TransactionLoadResult> {
|
2021-08-17 15:17:56 -07:00
|
|
|
txs.iter()
|
|
|
|
.zip(lock_results)
|
2018-12-17 12:41:23 -08:00
|
|
|
.map(|etx| match etx {
|
2021-12-29 23:42:32 -08:00
|
|
|
(tx, (Ok(()), nonce)) => {
|
2021-11-18 16:07:17 -08:00
|
|
|
let lamports_per_signature = nonce
|
2020-11-24 23:53:51 -08:00
|
|
|
.as_ref()
|
2021-11-18 16:07:17 -08:00
|
|
|
.map(|nonce| nonce.lamports_per_signature())
|
2020-11-24 23:53:51 -08:00
|
|
|
.unwrap_or_else(|| {
|
2021-10-22 14:32:40 -07:00
|
|
|
hash_queue.get_lamports_per_signature(tx.message().recent_blockhash())
|
2020-11-24 23:53:51 -08:00
|
|
|
});
|
2021-10-22 14:32:40 -07:00
|
|
|
let fee = if let Some(lamports_per_signature) = lamports_per_signature {
|
2022-02-11 16:23:16 -08:00
|
|
|
Bank::calculate_fee(
|
|
|
|
tx.message(),
|
|
|
|
lamports_per_signature,
|
|
|
|
fee_structure,
|
2022-07-26 12:39:24 -07:00
|
|
|
feature_set.is_active(&use_default_units_in_fee_calculation::id()),
|
2022-09-09 15:24:21 -07:00
|
|
|
!feature_set.is_active(&remove_deprecated_request_unit_ix::id()),
|
2023-02-01 14:26:50 -08:00
|
|
|
feature_set.is_active(&remove_congestion_multiplier_from_fee_calculation::id()),
|
2023-02-03 07:21:57 -08:00
|
|
|
feature_set.is_active(&enable_request_heap_frame_ix::id()) || self.accounts_db.expected_cluster_type() != ClusterType::MainnetBeta,
|
2023-02-24 07:27:49 -08:00
|
|
|
feature_set.is_active(&add_set_tx_loaded_accounts_data_size_instruction::id()),
|
2023-03-21 16:41:41 -07:00
|
|
|
feature_set.is_active(&include_loaded_accounts_data_size_in_fee_calculation::id()),
|
2022-02-11 16:23:16 -08:00
|
|
|
)
|
2019-12-07 11:54:10 -08:00
|
|
|
} else {
|
2020-11-29 11:21:55 -08:00
|
|
|
return (Err(TransactionError::BlockhashNotFound), None);
|
2019-12-07 11:54:10 -08:00
|
|
|
};
|
|
|
|
|
2022-11-18 07:44:09 -08:00
|
|
|
let loaded_transaction = match self.load_transaction_accounts(
|
2019-04-15 17:15:50 -07:00
|
|
|
ancestors,
|
|
|
|
tx,
|
|
|
|
fee,
|
|
|
|
error_counters,
|
2019-08-23 14:04:53 -07:00
|
|
|
rent_collector,
|
2020-09-21 22:36:23 -07:00
|
|
|
feature_set,
|
2022-04-22 03:32:31 -07:00
|
|
|
account_overrides,
|
2023-03-28 11:49:56 -07:00
|
|
|
program_accounts,
|
|
|
|
loaded_programs,
|
2021-01-25 01:35:08 -08:00
|
|
|
) {
|
|
|
|
Ok(loaded_transaction) => loaded_transaction,
|
2020-11-29 11:21:55 -08:00
|
|
|
Err(e) => return (Err(e), None),
|
2019-12-07 11:54:10 -08:00
|
|
|
};
|
|
|
|
|
2021-11-18 16:07:17 -08:00
|
|
|
// Update nonce with fee-subtracted accounts
|
|
|
|
let nonce = if let Some(nonce) = nonce {
|
|
|
|
match NonceFull::from_partial(
|
|
|
|
nonce,
|
2020-11-29 11:21:55 -08:00
|
|
|
tx.message(),
|
2021-01-25 01:35:08 -08:00
|
|
|
&loaded_transaction.accounts,
|
2021-10-29 17:47:38 -07:00
|
|
|
&loaded_transaction.rent_debits,
|
2020-11-29 11:21:55 -08:00
|
|
|
) {
|
2021-11-18 16:07:17 -08:00
|
|
|
Ok(nonce) => Some(nonce),
|
2020-11-29 11:21:55 -08:00
|
|
|
Err(e) => return (Err(e), None),
|
2020-11-24 23:53:51 -08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2021-11-18 16:07:17 -08:00
|
|
|
(Ok(loaded_transaction), nonce)
|
2019-01-06 22:06:55 -08:00
|
|
|
}
|
2021-11-18 16:07:17 -08:00
|
|
|
(_, (Err(e), _nonce)) => (Err(e), None),
|
2018-12-17 12:41:23 -08:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2022-01-06 19:59:09 -08:00
|
|
|
pub fn load_lookup_table_addresses(
|
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
|
|
|
address_table_lookup: &MessageAddressTableLookup,
|
|
|
|
slot_hashes: &SlotHashes,
|
|
|
|
) -> std::result::Result<LoadedAddresses, AddressLookupError> {
|
|
|
|
let table_account = self
|
|
|
|
.accounts_db
|
2022-10-04 08:06:56 -07:00
|
|
|
.load_with_fixed_root(ancestors, &address_table_lookup.account_key)
|
2022-01-06 19:59:09 -08:00
|
|
|
.map(|(account, _rent)| account)
|
|
|
|
.ok_or(AddressLookupError::LookupTableAccountNotFound)?;
|
|
|
|
|
|
|
|
if table_account.owner() == &solana_address_lookup_table_program::id() {
|
|
|
|
let current_slot = ancestors.max_slot();
|
|
|
|
let lookup_table = AddressLookupTable::deserialize(table_account.data())
|
|
|
|
.map_err(|_ix_err| AddressLookupError::InvalidAccountData)?;
|
|
|
|
|
|
|
|
Ok(LoadedAddresses {
|
|
|
|
writable: lookup_table.lookup(
|
|
|
|
current_slot,
|
|
|
|
&address_table_lookup.writable_indexes,
|
|
|
|
slot_hashes,
|
|
|
|
)?,
|
|
|
|
readonly: lookup_table.lookup(
|
|
|
|
current_slot,
|
|
|
|
&address_table_lookup.readonly_indexes,
|
|
|
|
slot_hashes,
|
|
|
|
)?,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Err(AddressLookupError::InvalidAccountOwner)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-16 08:23:32 -07:00
|
|
|
/// Slow because lock is held for 1 operation instead of many
|
2022-10-11 07:43:03 -07:00
|
|
|
/// This always returns None for zero-lamport accounts.
|
2021-04-16 08:23:32 -07:00
|
|
|
fn load_slow(
|
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
load_hint: LoadHint,
|
|
|
|
) -> Option<(AccountSharedData, Slot)> {
|
2022-10-11 07:43:03 -07:00
|
|
|
self.accounts_db.load(ancestors, pubkey, load_hint)
|
2021-04-16 08:23:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_with_fixed_root(
|
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
) -> Option<(AccountSharedData, Slot)> {
|
|
|
|
self.load_slow(ancestors, pubkey, LoadHint::FixedMaxRoot)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_without_fixed_root(
|
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
) -> Option<(AccountSharedData, Slot)> {
|
|
|
|
self.load_slow(ancestors, pubkey, LoadHint::Unspecified)
|
|
|
|
}
|
|
|
|
|
2019-10-23 22:01:22 -07:00
|
|
|
/// scans underlying accounts_db for this delta (slot) with a map function
|
2021-01-11 17:00:23 -08:00
|
|
|
/// from LoadedAccount to B
|
2019-10-23 22:01:22 -07:00
|
|
|
/// returns only the latest/current version of B for this slot
|
2021-01-11 17:00:23 -08:00
|
|
|
pub fn scan_slot<F, B>(&self, slot: Slot, func: F) -> Vec<B>
|
2019-07-10 21:22:58 -07:00
|
|
|
where
|
2021-01-11 17:00:23 -08:00
|
|
|
F: Fn(LoadedAccount) -> Option<B> + Send + Sync,
|
2021-01-20 00:50:17 -08:00
|
|
|
B: Sync + Send + Default + std::cmp::Eq,
|
2019-07-10 21:22:58 -07:00
|
|
|
{
|
2021-01-20 00:50:17 -08:00
|
|
|
let scan_result = self.accounts_db.scan_account_storage(
|
2019-10-23 22:01:22 -07:00
|
|
|
slot,
|
2021-01-20 00:50:17 -08:00
|
|
|
|loaded_account: LoadedAccount| {
|
|
|
|
// Cache only has one version per key, don't need to worry about versioning
|
|
|
|
func(loaded_account)
|
|
|
|
},
|
2023-01-13 12:05:15 -08:00
|
|
|
|accum: &DashMap<Pubkey, B>, loaded_account: LoadedAccount| {
|
2021-01-20 00:50:17 -08:00
|
|
|
let loaded_account_pubkey = *loaded_account.pubkey();
|
2023-01-13 12:05:15 -08:00
|
|
|
if let Some(val) = func(loaded_account) {
|
|
|
|
accum.insert(loaded_account_pubkey, val);
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
2019-07-10 21:22:58 -07:00
|
|
|
|
2021-01-20 00:50:17 -08:00
|
|
|
match scan_result {
|
|
|
|
ScanStorageResult::Cached(cached_result) => cached_result,
|
|
|
|
ScanStorageResult::Stored(stored_result) => stored_result
|
|
|
|
.into_iter()
|
2023-01-13 12:05:15 -08:00
|
|
|
.map(|(_pubkey, val)| val)
|
2021-01-20 00:50:17 -08:00
|
|
|
.collect(),
|
|
|
|
}
|
2019-07-10 21:22:58 -07:00
|
|
|
}
|
|
|
|
|
2020-07-20 03:09:38 -07:00
|
|
|
pub fn load_by_program_slot(
|
|
|
|
&self,
|
|
|
|
slot: Slot,
|
|
|
|
program_id: Option<&Pubkey>,
|
2021-12-27 09:49:32 -08:00
|
|
|
) -> Vec<TransactionAccount> {
|
2019-10-23 22:01:22 -07:00
|
|
|
self.scan_slot(slot, |stored_account| {
|
2020-07-20 03:09:38 -07:00
|
|
|
let hit = match program_id {
|
|
|
|
None => true,
|
2021-01-11 17:00:23 -08:00
|
|
|
Some(program_id) => stored_account.owner() == program_id,
|
2020-07-20 03:09:38 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
if hit {
|
2021-04-16 08:23:32 -07:00
|
|
|
Some((*stored_account.pubkey(), stored_account.take_account()))
|
2019-07-10 21:22:58 -07:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
2019-03-07 08:51:56 -08:00
|
|
|
}
|
|
|
|
|
2020-05-04 16:46:10 -07:00
|
|
|
pub fn load_largest_accounts(
|
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
2021-06-14 21:04:01 -07:00
|
|
|
bank_id: BankId,
|
2020-05-04 16:46:10 -07:00
|
|
|
num: usize,
|
|
|
|
filter_by_address: &HashSet<Pubkey>,
|
|
|
|
filter: AccountAddressFilter,
|
2021-06-14 21:04:01 -07:00
|
|
|
) -> ScanResult<Vec<(Pubkey, u64)>> {
|
2021-03-10 10:22:02 -08:00
|
|
|
if num == 0 {
|
2021-06-14 21:04:01 -07:00
|
|
|
return Ok(vec![]);
|
2021-03-10 10:22:02 -08:00
|
|
|
}
|
2022-10-21 16:53:06 -07:00
|
|
|
let mut account_balances = BinaryHeap::new();
|
|
|
|
self.accounts_db.scan_accounts(
|
2021-03-10 10:22:02 -08:00
|
|
|
ancestors,
|
2021-06-14 21:04:01 -07:00
|
|
|
bank_id,
|
2022-10-21 16:53:06 -07:00
|
|
|
|option| {
|
2021-03-10 10:22:02 -08:00
|
|
|
if let Some((pubkey, account, _slot)) = option {
|
2021-04-29 08:44:46 -07:00
|
|
|
if account.lamports() == 0 {
|
2021-03-10 10:22:02 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
let contains_address = filter_by_address.contains(pubkey);
|
|
|
|
let collect = match filter {
|
|
|
|
AccountAddressFilter::Exclude => !contains_address,
|
|
|
|
AccountAddressFilter::Include => contains_address,
|
|
|
|
};
|
|
|
|
if !collect {
|
|
|
|
return;
|
|
|
|
}
|
2022-10-21 16:53:06 -07:00
|
|
|
if account_balances.len() == num {
|
|
|
|
let Reverse(entry) = account_balances
|
2021-03-10 10:22:02 -08:00
|
|
|
.peek()
|
|
|
|
.expect("BinaryHeap::peek should succeed when len > 0");
|
2021-04-29 08:44:46 -07:00
|
|
|
if *entry >= (account.lamports(), *pubkey) {
|
2021-03-10 10:22:02 -08:00
|
|
|
return;
|
2021-03-09 16:23:41 -08:00
|
|
|
}
|
2022-10-21 16:53:06 -07:00
|
|
|
account_balances.pop();
|
2021-03-09 16:23:41 -08:00
|
|
|
}
|
2022-10-21 16:53:06 -07:00
|
|
|
account_balances.push(Reverse((account.lamports(), *pubkey)));
|
2020-05-04 16:46:10 -07:00
|
|
|
}
|
2021-03-10 10:22:02 -08:00
|
|
|
},
|
2021-11-19 07:00:19 -08:00
|
|
|
&ScanConfig::default(),
|
2021-06-14 21:04:01 -07:00
|
|
|
)?;
|
|
|
|
Ok(account_balances
|
2021-03-10 10:22:02 -08:00
|
|
|
.into_sorted_vec()
|
|
|
|
.into_iter()
|
|
|
|
.map(|Reverse((balance, pubkey))| (pubkey, balance))
|
2021-06-14 21:04:01 -07:00
|
|
|
.collect())
|
2020-05-04 16:46:10 -07:00
|
|
|
}
|
|
|
|
|
2021-10-15 15:41:23 -07:00
|
|
|
/// Only called from startup or test code.
|
2020-02-10 23:46:33 -08:00
|
|
|
#[must_use]
|
2023-03-10 15:11:47 -08:00
|
|
|
pub fn verify_accounts_hash_and_lamports(
|
2020-09-11 09:48:06 -07:00
|
|
|
&self,
|
|
|
|
slot: Slot,
|
|
|
|
total_lamports: u64,
|
2023-03-16 06:48:06 -07:00
|
|
|
base: Option<(Slot, /*capitalization*/ u64)>,
|
2023-03-10 17:02:14 -08:00
|
|
|
config: VerifyAccountsHashAndLamportsConfig,
|
2020-09-11 09:48:06 -07:00
|
|
|
) -> bool {
|
2023-02-15 14:03:50 -08:00
|
|
|
if let Err(err) =
|
|
|
|
self.accounts_db
|
2023-03-16 06:48:06 -07:00
|
|
|
.verify_accounts_hash_and_lamports(slot, total_lamports, base, config)
|
2023-02-15 14:03:50 -08:00
|
|
|
{
|
2023-03-10 15:11:47 -08:00
|
|
|
warn!("verify_accounts_hash failed: {err:?}, slot: {slot}");
|
2020-02-05 09:21:19 -08:00
|
|
|
false
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
2019-09-20 13:21:12 -07:00
|
|
|
}
|
|
|
|
|
2022-10-18 13:03:13 -07:00
|
|
|
pub fn is_loadable(lamports: u64) -> bool {
|
2020-08-26 20:28:40 -07:00
|
|
|
// Don't ever load zero lamport accounts into runtime because
|
|
|
|
// the existence of zero-lamport accounts are never deterministic!!
|
2021-02-03 15:00:42 -08:00
|
|
|
lamports > 0
|
2020-08-26 20:28:40 -07:00
|
|
|
}
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
fn load_while_filtering<F: Fn(&AccountSharedData) -> bool>(
|
2021-12-27 09:49:32 -08:00
|
|
|
collector: &mut Vec<TransactionAccount>,
|
2021-03-09 13:06:07 -08:00
|
|
|
some_account_tuple: Option<(&Pubkey, AccountSharedData, Slot)>,
|
2020-05-13 00:22:14 -07:00
|
|
|
filter: F,
|
|
|
|
) {
|
2020-08-26 20:28:40 -07:00
|
|
|
if let Some(mapped_account_tuple) = some_account_tuple
|
2021-04-29 08:44:46 -07:00
|
|
|
.filter(|(_, account, _)| Self::is_loadable(account.lamports()) && filter(account))
|
2020-05-13 00:22:14 -07:00
|
|
|
.map(|(pubkey, account, _slot)| (*pubkey, account))
|
|
|
|
{
|
2020-08-26 20:28:40 -07:00
|
|
|
collector.push(mapped_account_tuple)
|
2020-05-13 00:22:14 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-20 07:14:03 -07:00
|
|
|
fn load_with_slot(
|
|
|
|
collector: &mut Vec<PubkeyAccountSlot>,
|
|
|
|
some_account_tuple: Option<(&Pubkey, AccountSharedData, Slot)>,
|
|
|
|
) {
|
|
|
|
if let Some(mapped_account_tuple) = some_account_tuple
|
|
|
|
.filter(|(_, account, _)| Self::is_loadable(account.lamports()))
|
|
|
|
.map(|(pubkey, account, slot)| (*pubkey, account, slot))
|
|
|
|
{
|
|
|
|
collector.push(mapped_account_tuple)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-11 11:58:28 -07:00
|
|
|
pub fn load_by_program(
|
|
|
|
&self,
|
2020-04-26 19:07:03 -07:00
|
|
|
ancestors: &Ancestors,
|
2021-06-14 21:04:01 -07:00
|
|
|
bank_id: BankId,
|
2020-09-11 09:48:06 -07:00
|
|
|
program_id: &Pubkey,
|
2021-11-19 07:00:19 -08:00
|
|
|
config: &ScanConfig,
|
2021-12-27 09:49:32 -08:00
|
|
|
) -> ScanResult<Vec<TransactionAccount>> {
|
2022-10-21 16:53:06 -07:00
|
|
|
let mut collector = Vec::new();
|
|
|
|
self.accounts_db
|
|
|
|
.scan_accounts(
|
|
|
|
ancestors,
|
|
|
|
bank_id,
|
|
|
|
|some_account_tuple| {
|
|
|
|
Self::load_while_filtering(&mut collector, some_account_tuple, |account| {
|
|
|
|
account.owner() == program_id
|
|
|
|
})
|
|
|
|
},
|
|
|
|
config,
|
|
|
|
)
|
|
|
|
.map(|_| collector)
|
2020-05-13 00:22:14 -07:00
|
|
|
}
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
pub fn load_by_program_with_filter<F: Fn(&AccountSharedData) -> bool>(
|
2020-12-31 18:06:03 -08:00
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
2021-06-14 21:04:01 -07:00
|
|
|
bank_id: BankId,
|
2020-12-31 18:06:03 -08:00
|
|
|
program_id: &Pubkey,
|
|
|
|
filter: F,
|
2021-11-19 07:00:19 -08:00
|
|
|
config: &ScanConfig,
|
2021-12-27 09:49:32 -08:00
|
|
|
) -> ScanResult<Vec<TransactionAccount>> {
|
2022-10-21 16:53:06 -07:00
|
|
|
let mut collector = Vec::new();
|
|
|
|
self.accounts_db
|
|
|
|
.scan_accounts(
|
|
|
|
ancestors,
|
|
|
|
bank_id,
|
|
|
|
|some_account_tuple| {
|
|
|
|
Self::load_while_filtering(&mut collector, some_account_tuple, |account| {
|
|
|
|
account.owner() == program_id && filter(account)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
config,
|
|
|
|
)
|
|
|
|
.map(|_| collector)
|
2020-12-31 18:06:03 -08:00
|
|
|
}
|
|
|
|
|
2021-12-08 12:09:34 -08:00
|
|
|
fn calc_scan_result_size(account: &AccountSharedData) -> usize {
|
|
|
|
account.data().len()
|
|
|
|
+ std::mem::size_of::<AccountSharedData>()
|
|
|
|
+ std::mem::size_of::<Pubkey>()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Accumulate size of (pubkey + account) into sum.
|
|
|
|
/// Return true iff sum > 'byte_limit_for_scan'
|
|
|
|
fn accumulate_and_check_scan_result_size(
|
|
|
|
sum: &AtomicUsize,
|
|
|
|
account: &AccountSharedData,
|
|
|
|
byte_limit_for_scan: &Option<usize>,
|
|
|
|
) -> bool {
|
|
|
|
if let Some(byte_limit_for_scan) = byte_limit_for_scan.as_ref() {
|
|
|
|
let added = Self::calc_scan_result_size(account);
|
|
|
|
sum.fetch_add(added, Ordering::Relaxed)
|
|
|
|
.saturating_add(added)
|
|
|
|
> *byte_limit_for_scan
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn maybe_abort_scan(
|
2021-12-27 09:49:32 -08:00
|
|
|
result: ScanResult<Vec<TransactionAccount>>,
|
2021-12-08 12:09:34 -08:00
|
|
|
config: &ScanConfig,
|
2021-12-27 09:49:32 -08:00
|
|
|
) -> ScanResult<Vec<TransactionAccount>> {
|
2021-12-08 12:09:34 -08:00
|
|
|
if config.is_aborted() {
|
|
|
|
ScanResult::Err(ScanError::Aborted(
|
|
|
|
"The accumulated scan results exceeded the limit".to_string(),
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
pub fn load_by_index_key_with_filter<F: Fn(&AccountSharedData) -> bool>(
|
2020-12-31 18:06:03 -08:00
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
2021-06-14 21:04:01 -07:00
|
|
|
bank_id: BankId,
|
2020-12-31 18:06:03 -08:00
|
|
|
index_key: &IndexKey,
|
|
|
|
filter: F,
|
2021-11-19 07:00:19 -08:00
|
|
|
config: &ScanConfig,
|
|
|
|
byte_limit_for_scan: Option<usize>,
|
2021-12-27 09:49:32 -08:00
|
|
|
) -> ScanResult<Vec<TransactionAccount>> {
|
2021-11-19 07:00:19 -08:00
|
|
|
let sum = AtomicUsize::default();
|
2021-12-08 12:09:34 -08:00
|
|
|
let config = config.recreate_with_abort();
|
2022-10-21 16:53:06 -07:00
|
|
|
let mut collector = Vec::new();
|
2021-11-19 07:00:19 -08:00
|
|
|
let result = self
|
|
|
|
.accounts_db
|
2021-05-11 15:06:22 -07:00
|
|
|
.index_scan_accounts(
|
|
|
|
ancestors,
|
2021-06-14 21:04:01 -07:00
|
|
|
bank_id,
|
2021-05-11 15:06:22 -07:00
|
|
|
*index_key,
|
2022-10-21 16:53:06 -07:00
|
|
|
|some_account_tuple| {
|
|
|
|
Self::load_while_filtering(&mut collector, some_account_tuple, |account| {
|
2021-11-19 07:00:19 -08:00
|
|
|
let use_account = filter(account);
|
2021-12-08 12:09:34 -08:00
|
|
|
if use_account
|
|
|
|
&& Self::accumulate_and_check_scan_result_size(
|
|
|
|
&sum,
|
|
|
|
account,
|
|
|
|
&byte_limit_for_scan,
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// total size of results exceeds size limit, so abort scan
|
|
|
|
config.abort();
|
2021-11-19 07:00:19 -08:00
|
|
|
}
|
|
|
|
use_account
|
|
|
|
});
|
2021-05-11 15:06:22 -07:00
|
|
|
},
|
2021-11-19 07:00:19 -08:00
|
|
|
&config,
|
2021-05-11 15:06:22 -07:00
|
|
|
)
|
2022-10-21 16:53:06 -07:00
|
|
|
.map(|_| collector);
|
2021-12-08 12:09:34 -08:00
|
|
|
Self::maybe_abort_scan(result, &config)
|
2020-12-31 18:06:03 -08:00
|
|
|
}
|
|
|
|
|
2021-05-13 14:04:21 -07:00
|
|
|
pub fn account_indexes_include_key(&self, key: &Pubkey) -> bool {
|
|
|
|
self.accounts_db.account_indexes.include_key(key)
|
|
|
|
}
|
|
|
|
|
2021-06-14 21:04:01 -07:00
|
|
|
pub fn load_all(
|
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
|
|
|
bank_id: BankId,
|
2022-04-19 19:34:00 -07:00
|
|
|
) -> ScanResult<Vec<PubkeyAccountSlot>> {
|
2022-10-21 16:53:06 -07:00
|
|
|
let mut collector = Vec::new();
|
|
|
|
self.accounts_db
|
|
|
|
.scan_accounts(
|
|
|
|
ancestors,
|
|
|
|
bank_id,
|
|
|
|
|some_account_tuple| {
|
|
|
|
if let Some((pubkey, account, slot)) = some_account_tuple
|
|
|
|
.filter(|(_, account, _)| Self::is_loadable(account.lamports()))
|
|
|
|
{
|
|
|
|
collector.push((*pubkey, account, slot))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
&ScanConfig::default(),
|
|
|
|
)
|
|
|
|
.map(|_| collector)
|
2020-11-16 17:23:11 -08:00
|
|
|
}
|
|
|
|
|
2022-10-18 13:03:13 -07:00
|
|
|
pub fn scan_all<F>(
|
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
|
|
|
bank_id: BankId,
|
|
|
|
scan_func: F,
|
|
|
|
) -> ScanResult<()>
|
|
|
|
where
|
|
|
|
F: FnMut(Option<(&Pubkey, AccountSharedData, Slot)>),
|
|
|
|
{
|
|
|
|
self.accounts_db
|
|
|
|
.scan_accounts(ancestors, bank_id, scan_func, &ScanConfig::default())
|
|
|
|
}
|
|
|
|
|
2021-12-21 15:31:48 -08:00
|
|
|
pub fn hold_range_in_memory<R>(
|
|
|
|
&self,
|
|
|
|
range: &R,
|
|
|
|
start_holding: bool,
|
|
|
|
thread_pool: &rayon::ThreadPool,
|
|
|
|
) where
|
|
|
|
R: RangeBounds<Pubkey> + std::fmt::Debug + Sync,
|
2021-09-17 15:19:29 -07:00
|
|
|
{
|
|
|
|
self.accounts_db
|
|
|
|
.accounts_index
|
2021-12-21 15:31:48 -08:00
|
|
|
.hold_range_in_memory(range, start_holding, thread_pool)
|
2021-09-17 15:19:29 -07:00
|
|
|
}
|
|
|
|
|
2021-09-01 06:13:56 -07:00
|
|
|
pub fn load_to_collect_rent_eagerly<R: RangeBounds<Pubkey> + std::fmt::Debug>(
|
2020-05-13 00:22:14 -07:00
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
|
|
|
range: R,
|
2022-04-20 07:14:03 -07:00
|
|
|
) -> Vec<PubkeyAccountSlot> {
|
2022-10-21 16:53:06 -07:00
|
|
|
let mut collector = Vec::new();
|
2020-05-13 00:22:14 -07:00
|
|
|
self.accounts_db.range_scan_accounts(
|
2022-06-15 11:16:00 -07:00
|
|
|
"", // disable logging of this. We now parallelize it and this results in multiple parallel logs
|
2020-05-13 00:22:14 -07:00
|
|
|
ancestors,
|
|
|
|
range,
|
2021-11-19 07:00:19 -08:00
|
|
|
&ScanConfig::new(true),
|
2022-10-21 16:53:06 -07:00
|
|
|
|option| Self::load_with_slot(&mut collector, option),
|
|
|
|
);
|
|
|
|
collector
|
2019-07-11 11:58:28 -07:00
|
|
|
}
|
|
|
|
|
2021-01-11 17:00:23 -08:00
|
|
|
/// Slow because lock is held for 1 operation instead of many.
|
|
|
|
/// WARNING: This noncached version is only to be used for tests/benchmarking
|
|
|
|
/// as bypassing the cache in general is not supported
|
2021-03-09 13:06:07 -08:00
|
|
|
pub fn store_slow_uncached(&self, slot: Slot, pubkey: &Pubkey, account: &AccountSharedData) {
|
2021-01-11 17:00:23 -08:00
|
|
|
self.accounts_db.store_uncached(slot, &[(pubkey, account)]);
|
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
fn lock_account(
|
2019-10-23 22:01:22 -07:00
|
|
|
&self,
|
2021-02-22 23:56:43 -08:00
|
|
|
account_locks: &mut AccountLocks,
|
2019-12-21 10:43:22 -08:00
|
|
|
writable_keys: Vec<&Pubkey>,
|
|
|
|
readonly_keys: Vec<&Pubkey>,
|
2018-12-17 12:41:23 -08:00
|
|
|
) -> Result<()> {
|
2019-11-05 08:38:35 -08:00
|
|
|
for k in writable_keys.iter() {
|
2021-02-22 23:56:43 -08:00
|
|
|
if account_locks.is_locked_write(k) || account_locks.is_locked_readonly(k) {
|
|
|
|
debug!("Writable account in use: {:?}", k);
|
2019-06-27 14:25:10 -07:00
|
|
|
return Err(TransactionError::AccountInUse);
|
|
|
|
}
|
|
|
|
}
|
2019-11-05 08:38:35 -08:00
|
|
|
for k in readonly_keys.iter() {
|
2021-02-22 23:56:43 -08:00
|
|
|
if account_locks.is_locked_write(k) {
|
|
|
|
debug!("Read-only account in use: {:?}", k);
|
2019-03-13 12:58:44 -07:00
|
|
|
return Err(TransactionError::AccountInUse);
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
}
|
2019-06-27 14:25:10 -07:00
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
for k in writable_keys {
|
2021-02-22 23:56:43 -08:00
|
|
|
account_locks.write_locks.insert(*k);
|
2019-06-27 14:25:10 -07:00
|
|
|
}
|
2019-10-23 22:01:22 -07:00
|
|
|
|
2021-02-22 23:56:43 -08:00
|
|
|
for k in readonly_keys {
|
|
|
|
if !account_locks.lock_readonly(k) {
|
|
|
|
account_locks.insert_new_readonly(k);
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-06-27 14:25:10 -07:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-30 10:05:09 -07:00
|
|
|
fn unlock_account(
|
|
|
|
&self,
|
2021-07-06 12:11:54 -07:00
|
|
|
account_locks: &mut AccountLocks,
|
|
|
|
writable_keys: Vec<&Pubkey>,
|
|
|
|
readonly_keys: Vec<&Pubkey>,
|
2021-03-30 10:05:09 -07:00
|
|
|
) {
|
2021-07-06 12:11:54 -07:00
|
|
|
for k in writable_keys {
|
|
|
|
account_locks.unlock_write(k);
|
|
|
|
}
|
|
|
|
for k in readonly_keys {
|
|
|
|
account_locks.unlock_readonly(k);
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
/// This function will prevent multiple threads from modifying the same account state at the
|
|
|
|
/// same time
|
|
|
|
#[must_use]
|
2021-05-19 07:31:47 -07:00
|
|
|
#[allow(clippy::needless_collect)]
|
2021-08-17 15:17:56 -07:00
|
|
|
pub fn lock_accounts<'a>(
|
|
|
|
&self,
|
|
|
|
txs: impl Iterator<Item = &'a SanitizedTransaction>,
|
2022-08-12 07:07:48 -07:00
|
|
|
tx_account_lock_limit: usize,
|
2021-12-29 23:42:32 -08:00
|
|
|
) -> Vec<Result<()>> {
|
2022-08-12 07:07:48 -07:00
|
|
|
let tx_account_locks_results: Vec<Result<_>> = txs
|
|
|
|
.map(|tx| tx.get_account_locks(tx_account_lock_limit))
|
|
|
|
.collect();
|
2022-01-03 22:25:23 -08:00
|
|
|
self.lock_accounts_inner(tx_account_locks_results)
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2021-11-23 13:17:55 -08:00
|
|
|
#[must_use]
|
2021-11-11 23:04:53 -08:00
|
|
|
#[allow(clippy::needless_collect)]
|
|
|
|
pub fn lock_accounts_with_results<'a>(
|
|
|
|
&self,
|
|
|
|
txs: impl Iterator<Item = &'a SanitizedTransaction>,
|
2022-04-07 13:42:39 -07:00
|
|
|
results: impl Iterator<Item = &'a Result<()>>,
|
2022-08-12 07:07:48 -07:00
|
|
|
tx_account_lock_limit: usize,
|
2021-12-29 23:42:32 -08:00
|
|
|
) -> Vec<Result<()>> {
|
2022-01-03 22:25:23 -08:00
|
|
|
let tx_account_locks_results: Vec<Result<_>> = txs
|
2021-11-23 13:17:55 -08:00
|
|
|
.zip(results)
|
|
|
|
.map(|(tx, result)| match result {
|
2022-08-12 07:07:48 -07:00
|
|
|
Ok(()) => tx.get_account_locks(tx_account_lock_limit),
|
2022-04-07 13:42:39 -07:00
|
|
|
Err(err) => Err(err.clone()),
|
2021-11-23 13:17:55 -08:00
|
|
|
})
|
2021-11-11 23:04:53 -08:00
|
|
|
.collect();
|
2022-01-03 22:25:23 -08:00
|
|
|
self.lock_accounts_inner(tx_account_locks_results)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
fn lock_accounts_inner(
|
|
|
|
&self,
|
|
|
|
tx_account_locks_results: Vec<Result<TransactionAccountLocks>>,
|
|
|
|
) -> Vec<Result<()>> {
|
2021-11-11 23:04:53 -08:00
|
|
|
let account_locks = &mut self.account_locks.lock().unwrap();
|
2022-01-03 22:25:23 -08:00
|
|
|
tx_account_locks_results
|
2021-11-23 13:17:55 -08:00
|
|
|
.into_iter()
|
2022-01-03 22:25:23 -08:00
|
|
|
.map(|tx_account_locks_result| match tx_account_locks_result {
|
|
|
|
Ok(tx_account_locks) => self.lock_account(
|
|
|
|
account_locks,
|
|
|
|
tx_account_locks.writable,
|
|
|
|
tx_account_locks.readonly,
|
|
|
|
),
|
|
|
|
Err(err) => Err(err),
|
2021-11-11 23:04:53 -08:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
/// Once accounts are unlocked, new transactions that modify that state can enter the pipeline
|
2021-07-06 12:11:54 -07:00
|
|
|
#[allow(clippy::needless_collect)]
|
2021-04-12 23:28:08 -07:00
|
|
|
pub fn unlock_accounts<'a>(
|
2019-09-16 21:45:16 -07:00
|
|
|
&self,
|
2021-08-17 15:17:56 -07:00
|
|
|
txs: impl Iterator<Item = &'a SanitizedTransaction>,
|
2021-12-29 23:42:32 -08:00
|
|
|
results: &[Result<()>],
|
2019-09-16 21:45:16 -07:00
|
|
|
) {
|
2021-07-06 12:11:54 -07:00
|
|
|
let keys: Vec<_> = txs
|
|
|
|
.zip(results)
|
|
|
|
.filter_map(|(tx, res)| match res {
|
2022-01-03 22:25:23 -08:00
|
|
|
Err(TransactionError::AccountLoadedTwice)
|
|
|
|
| Err(TransactionError::AccountInUse)
|
2021-12-12 12:57:18 -08:00
|
|
|
| Err(TransactionError::SanitizeFailure)
|
2022-01-03 22:25:23 -08:00
|
|
|
| Err(TransactionError::TooManyAccountLocks)
|
2021-12-12 12:57:18 -08:00
|
|
|
| Err(TransactionError::WouldExceedMaxBlockCostLimit)
|
2022-01-12 21:27:19 -08:00
|
|
|
| Err(TransactionError::WouldExceedMaxVoteCostLimit)
|
2021-12-12 12:57:18 -08:00
|
|
|
| Err(TransactionError::WouldExceedMaxAccountCostLimit)
|
2022-03-13 08:58:57 -07:00
|
|
|
| Err(TransactionError::WouldExceedAccountDataBlockLimit)
|
|
|
|
| Err(TransactionError::WouldExceedAccountDataTotalLimit) => None,
|
2022-01-03 22:25:23 -08:00
|
|
|
_ => Some(tx.get_account_locks_unchecked()),
|
2021-07-06 12:11:54 -07:00
|
|
|
})
|
|
|
|
.collect();
|
2019-06-10 22:05:46 -07:00
|
|
|
let mut account_locks = self.account_locks.lock().unwrap();
|
2018-12-17 12:41:23 -08:00
|
|
|
debug!("bank unlock accounts");
|
2021-08-17 15:17:56 -07:00
|
|
|
keys.into_iter().for_each(|keys| {
|
|
|
|
self.unlock_account(&mut account_locks, keys.writable, keys.readonly);
|
2021-07-06 12:11:54 -07:00
|
|
|
});
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
/// Store the accounts into the DB
|
2020-10-21 06:19:25 -07:00
|
|
|
// allow(clippy) needed for various gating flags
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
2022-07-08 13:04:08 -07:00
|
|
|
pub(crate) fn store_cached(
|
2018-12-17 12:41:23 -08:00
|
|
|
&self,
|
2019-10-23 22:01:22 -07:00
|
|
|
slot: Slot,
|
2022-07-08 13:04:08 -07:00
|
|
|
txs: &[SanitizedTransaction],
|
|
|
|
res: &[TransactionExecutionResult],
|
|
|
|
loaded: &mut [TransactionLoadResult],
|
2019-11-20 12:27:02 -08:00
|
|
|
rent_collector: &RentCollector,
|
2022-07-06 05:03:13 -07:00
|
|
|
durable_nonce: &DurableNonce,
|
2021-10-22 14:32:40 -07:00
|
|
|
lamports_per_signature: u64,
|
2022-10-18 08:03:37 -07:00
|
|
|
include_slot_in_hash: IncludeSlotInHash,
|
2019-02-19 17:11:43 -08:00
|
|
|
) {
|
2023-02-09 12:00:33 -08:00
|
|
|
let (accounts_to_store, transactions) = self.collect_accounts_to_store(
|
2020-01-10 15:57:31 -08:00
|
|
|
txs,
|
|
|
|
res,
|
|
|
|
loaded,
|
|
|
|
rent_collector,
|
2022-06-03 07:36:27 -07:00
|
|
|
durable_nonce,
|
2021-10-22 14:32:40 -07:00
|
|
|
lamports_per_signature,
|
2020-01-10 15:57:31 -08:00
|
|
|
);
|
2022-10-18 08:03:37 -07:00
|
|
|
self.accounts_db.store_cached(
|
|
|
|
(slot, &accounts_to_store[..], include_slot_in_hash),
|
2023-02-09 12:00:33 -08:00
|
|
|
Some(&transactions),
|
2022-10-18 08:03:37 -07:00
|
|
|
);
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2022-11-22 15:36:57 -08:00
|
|
|
pub fn store_accounts_cached<'a, T: ReadableAccount + Sync + ZeroLamport + 'a>(
|
2022-06-22 10:13:56 -07:00
|
|
|
&self,
|
|
|
|
accounts: impl StorableAccounts<'a, T>,
|
|
|
|
) {
|
|
|
|
self.accounts_db.store_cached(accounts, None)
|
2022-06-03 13:00:39 -07:00
|
|
|
}
|
|
|
|
|
2019-10-23 22:01:22 -07:00
|
|
|
/// Add a slot to root. Root slots cannot be purged
|
2021-11-22 09:29:45 -08:00
|
|
|
pub fn add_root(&self, slot: Slot) -> AccountsAddRootTiming {
|
2019-10-23 22:01:22 -07:00
|
|
|
self.accounts_db.add_root(slot)
|
2019-03-21 17:36:10 -07:00
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
|
2021-10-22 14:32:40 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2019-08-19 13:00:37 -07:00
|
|
|
fn collect_accounts_to_store<'a>(
|
2019-06-27 14:25:10 -07:00
|
|
|
&self,
|
2021-08-17 15:17:56 -07:00
|
|
|
txs: &'a [SanitizedTransaction],
|
2021-11-18 16:07:17 -08:00
|
|
|
execution_results: &'a [TransactionExecutionResult],
|
|
|
|
load_results: &'a mut [TransactionLoadResult],
|
2022-11-01 06:36:59 -07:00
|
|
|
_rent_collector: &RentCollector,
|
2022-07-06 05:03:13 -07:00
|
|
|
durable_nonce: &DurableNonce,
|
2021-10-22 14:32:40 -07:00
|
|
|
lamports_per_signature: u64,
|
2022-06-10 10:24:35 -07:00
|
|
|
) -> (
|
|
|
|
Vec<(&'a Pubkey, &'a AccountSharedData)>,
|
2023-02-09 12:00:33 -08:00
|
|
|
Vec<Option<&'a SanitizedTransaction>>,
|
2022-06-10 10:24:35 -07:00
|
|
|
) {
|
2021-11-18 16:07:17 -08:00
|
|
|
let mut accounts = Vec::with_capacity(load_results.len());
|
2023-02-09 12:00:33 -08:00
|
|
|
let mut transactions = Vec::with_capacity(load_results.len());
|
2022-01-04 18:15:15 -08:00
|
|
|
for (i, ((tx_load_result, nonce), tx)) in load_results.iter_mut().zip(txs).enumerate() {
|
2021-11-18 16:07:17 -08:00
|
|
|
if tx_load_result.is_err() {
|
|
|
|
// Don't store any accounts if tx failed to load
|
2019-06-27 14:25:10 -07:00
|
|
|
continue;
|
|
|
|
}
|
2021-11-18 16:07:17 -08:00
|
|
|
|
2022-01-04 18:15:15 -08:00
|
|
|
let execution_status = match &execution_results[i] {
|
2022-04-20 09:20:29 -07:00
|
|
|
TransactionExecutionResult::Executed { details, .. } => &details.status,
|
2022-01-04 18:15:15 -08:00
|
|
|
// Don't store any accounts if tx wasn't executed
|
|
|
|
TransactionExecutionResult::NotExecuted(_) => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
let maybe_nonce = match (execution_status, &*nonce) {
|
2022-07-06 10:59:34 -07:00
|
|
|
(Ok(_), _) => None, // Success, don't do any additional nonce processing
|
2022-01-04 18:15:15 -08:00
|
|
|
(Err(_), Some(nonce)) => {
|
2021-12-02 09:57:05 -08:00
|
|
|
Some((nonce, true /* rollback */))
|
|
|
|
}
|
2022-01-04 18:15:15 -08:00
|
|
|
(Err(_), None) => {
|
|
|
|
// Fees for failed transactions which don't use durable nonces are
|
|
|
|
// deducted in Bank::filter_program_errors_and_collect_fee
|
|
|
|
continue;
|
|
|
|
}
|
2020-01-10 15:57:31 -08:00
|
|
|
};
|
2019-06-27 14:25:10 -07:00
|
|
|
|
2021-08-17 15:17:56 -07:00
|
|
|
let message = tx.message();
|
2021-11-18 16:07:17 -08:00
|
|
|
let loaded_transaction = tx_load_result.as_mut().unwrap();
|
2020-11-25 15:18:43 -08:00
|
|
|
let mut fee_payer_index = None;
|
2022-02-05 04:00:31 -08:00
|
|
|
for (i, (address, account)) in (0..message.account_keys().len())
|
2021-01-25 01:35:08 -08:00
|
|
|
.zip(loaded_transaction.accounts.iter_mut())
|
2021-11-18 16:07:17 -08:00
|
|
|
.filter(|(i, _)| message.is_non_loader_key(*i))
|
2019-11-20 12:27:02 -08:00
|
|
|
{
|
2020-11-25 15:18:43 -08:00
|
|
|
if fee_payer_index.is_none() {
|
|
|
|
fee_payer_index = Some(i);
|
|
|
|
}
|
|
|
|
let is_fee_payer = Some(i) == fee_payer_index;
|
2021-12-16 14:27:22 -08:00
|
|
|
if message.is_writable(i) {
|
2021-12-02 09:57:05 -08:00
|
|
|
let is_nonce_account = prepare_if_nonce_account(
|
|
|
|
address,
|
|
|
|
account,
|
2022-01-04 18:15:15 -08:00
|
|
|
execution_status,
|
2021-12-02 09:57:05 -08:00
|
|
|
is_fee_payer,
|
|
|
|
maybe_nonce,
|
2022-06-03 07:36:27 -07:00
|
|
|
durable_nonce,
|
2021-12-02 09:57:05 -08:00
|
|
|
lamports_per_signature,
|
|
|
|
);
|
|
|
|
|
2022-01-04 18:15:15 -08:00
|
|
|
if execution_status.is_ok() || is_nonce_account || is_fee_payer {
|
2021-12-02 09:57:05 -08:00
|
|
|
// Add to the accounts to store
|
|
|
|
accounts.push((&*address, &*account));
|
2023-02-09 12:00:33 -08:00
|
|
|
transactions.push(Some(tx));
|
2021-12-02 09:57:05 -08:00
|
|
|
}
|
2019-10-16 13:31:21 -07:00
|
|
|
}
|
2019-06-10 19:50:02 -07:00
|
|
|
}
|
|
|
|
}
|
2023-02-09 12:00:33 -08:00
|
|
|
(accounts, transactions)
|
2019-06-10 19:50:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-20 09:44:17 -08:00
|
|
|
fn prepare_if_nonce_account(
|
2021-11-18 16:07:17 -08:00
|
|
|
address: &Pubkey,
|
2021-03-09 13:06:07 -08:00
|
|
|
account: &mut AccountSharedData,
|
2021-11-18 16:07:17 -08:00
|
|
|
execution_result: &Result<()>,
|
2021-12-02 09:57:05 -08:00
|
|
|
is_fee_payer: bool,
|
2022-12-20 09:44:17 -08:00
|
|
|
maybe_nonce: Option<(&NonceFull, bool)>,
|
2022-07-06 05:03:13 -07:00
|
|
|
&durable_nonce: &DurableNonce,
|
2021-10-22 14:32:40 -07:00
|
|
|
lamports_per_signature: u64,
|
2020-11-24 23:53:51 -08:00
|
|
|
) -> bool {
|
2021-12-02 09:57:05 -08:00
|
|
|
if let Some((nonce, rollback)) = maybe_nonce {
|
|
|
|
if address == nonce.address() {
|
|
|
|
if rollback {
|
2021-11-18 16:07:17 -08:00
|
|
|
// The transaction failed which would normally drop the account
|
2021-12-02 09:57:05 -08:00
|
|
|
// processing changes, since this account is now being included
|
|
|
|
// in the accounts written back to the db, roll it back to
|
2021-11-18 16:07:17 -08:00
|
|
|
// pre-processing state.
|
2021-12-02 09:57:05 -08:00
|
|
|
*account = nonce.account().clone();
|
2021-06-01 16:25:53 -07:00
|
|
|
}
|
|
|
|
|
2021-12-02 09:57:05 -08:00
|
|
|
// Advance the stored blockhash to prevent fee theft by someone
|
|
|
|
// replaying nonce transactions that have failed with an
|
|
|
|
// `InstructionError`.
|
|
|
|
//
|
|
|
|
// Since we know we are dealing with a valid nonce account,
|
|
|
|
// unwrap is safe here
|
2022-06-09 08:28:37 -07:00
|
|
|
let nonce_versions = StateMut::<NonceVersions>::state(nonce.account()).unwrap();
|
|
|
|
if let NonceState::Initialized(ref data) = nonce_versions.state() {
|
|
|
|
let nonce_state = NonceState::new_initialized(
|
|
|
|
&data.authority,
|
|
|
|
durable_nonce,
|
|
|
|
lamports_per_signature,
|
|
|
|
);
|
2022-07-06 05:03:13 -07:00
|
|
|
let nonce_versions = NonceVersions::new(nonce_state);
|
2022-06-09 08:28:37 -07:00
|
|
|
account.set_state(&nonce_versions).unwrap();
|
2021-12-02 09:57:05 -08:00
|
|
|
}
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
if execution_result.is_err() && is_fee_payer {
|
|
|
|
if let Some(fee_payer_account) = nonce.fee_payer_account() {
|
|
|
|
// Instruction error and fee-payer for this nonce tx is not
|
|
|
|
// the nonce account itself, rollback the fee payer to the
|
|
|
|
// fee-paid original state.
|
|
|
|
*account = fee_payer_account.clone();
|
2020-09-29 20:34:07 -07:00
|
|
|
}
|
|
|
|
}
|
2021-12-02 09:57:05 -08:00
|
|
|
|
|
|
|
false
|
2020-09-29 20:34:07 -07:00
|
|
|
}
|
2021-12-02 09:57:05 -08:00
|
|
|
} else {
|
|
|
|
false
|
2020-09-29 20:34:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-12-03 09:00:31 -08:00
|
|
|
use {
|
|
|
|
super::*,
|
2022-01-04 18:15:15 -08:00
|
|
|
crate::{
|
|
|
|
bank::{DurableNonceFee, TransactionExecutionDetails},
|
|
|
|
rent_collector::RentCollector,
|
|
|
|
},
|
2022-06-09 08:28:37 -07:00
|
|
|
assert_matches::assert_matches,
|
2022-01-06 19:59:09 -08:00
|
|
|
solana_address_lookup_table_program::state::LookupTableMeta,
|
2023-04-11 14:22:19 -07:00
|
|
|
solana_program_runtime::{
|
|
|
|
executor_cache::TransactionExecutorCache,
|
|
|
|
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
|
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
solana_sdk::{
|
|
|
|
account::{AccountSharedData, WritableAccount},
|
2023-03-28 11:49:56 -07:00
|
|
|
bpf_loader_upgradeable::UpgradeableLoaderState,
|
2023-04-11 14:22:19 -07:00
|
|
|
compute_budget::ComputeBudgetInstruction,
|
2021-12-03 09:00:31 -08:00
|
|
|
epoch_schedule::EpochSchedule,
|
|
|
|
genesis_config::ClusterType,
|
|
|
|
hash::Hash,
|
|
|
|
instruction::{CompiledInstruction, InstructionError},
|
2022-01-03 22:25:23 -08:00
|
|
|
message::{Message, MessageHeader},
|
2021-12-03 09:00:31 -08:00
|
|
|
nonce, nonce_account,
|
|
|
|
rent::Rent,
|
|
|
|
signature::{keypair_from_seed, signers::Signers, Keypair, Signer},
|
|
|
|
system_instruction, system_program,
|
2022-01-03 22:25:23 -08:00
|
|
|
transaction::{Transaction, MAX_TX_ACCOUNT_LOCKS},
|
2021-12-03 09:00:31 -08:00
|
|
|
},
|
|
|
|
std::{
|
2022-01-06 19:59:09 -08:00
|
|
|
borrow::Cow,
|
2022-04-20 09:20:29 -07:00
|
|
|
cell::RefCell,
|
2021-12-03 09:00:31 -08:00
|
|
|
convert::TryFrom,
|
2022-04-20 09:20:29 -07:00
|
|
|
rc::Rc,
|
2021-12-03 09:00:31 -08:00
|
|
|
sync::atomic::{AtomicBool, AtomicU64, Ordering},
|
|
|
|
thread, time,
|
|
|
|
},
|
2020-01-28 17:03:20 -08:00
|
|
|
};
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2021-08-17 15:17:56 -07:00
|
|
|
fn new_sanitized_tx<T: Signers>(
|
|
|
|
from_keypairs: &T,
|
|
|
|
message: Message,
|
|
|
|
recent_blockhash: Hash,
|
|
|
|
) -> SanitizedTransaction {
|
2021-10-27 10:09:16 -07:00
|
|
|
SanitizedTransaction::from_transaction_for_tests(Transaction::new(
|
|
|
|
from_keypairs,
|
|
|
|
message,
|
|
|
|
recent_blockhash,
|
|
|
|
))
|
2021-08-17 15:17:56 -07:00
|
|
|
}
|
|
|
|
|
2022-01-04 18:15:15 -08:00
|
|
|
fn new_execution_result(
|
|
|
|
status: Result<()>,
|
|
|
|
nonce: Option<&NonceFull>,
|
|
|
|
) -> TransactionExecutionResult {
|
2022-04-20 09:20:29 -07:00
|
|
|
TransactionExecutionResult::Executed {
|
|
|
|
details: TransactionExecutionDetails {
|
|
|
|
status,
|
|
|
|
log_messages: None,
|
|
|
|
inner_instructions: None,
|
|
|
|
durable_nonce_fee: nonce.map(DurableNonceFee::from),
|
|
|
|
return_data: None,
|
2022-05-19 10:24:13 -07:00
|
|
|
executed_units: 0,
|
|
|
|
accounts_data_len_delta: 0,
|
2022-04-20 09:20:29 -07:00
|
|
|
},
|
2022-10-12 09:09:03 -07:00
|
|
|
tx_executor_cache: Rc::new(RefCell::new(TransactionExecutorCache::default())),
|
2022-04-20 09:20:29 -07:00
|
|
|
}
|
2022-01-04 18:15:15 -08:00
|
|
|
}
|
|
|
|
|
2020-01-03 16:34:58 -08:00
|
|
|
fn load_accounts_with_fee_and_rent(
|
2019-01-08 09:20:25 -08:00
|
|
|
tx: Transaction,
|
2021-12-27 09:49:32 -08:00
|
|
|
ka: &[TransactionAccount],
|
2021-10-22 14:32:40 -07:00
|
|
|
lamports_per_signature: u64,
|
2020-01-03 16:34:58 -08:00
|
|
|
rent_collector: &RentCollector,
|
2022-04-23 16:10:47 -07:00
|
|
|
error_counters: &mut TransactionErrorMetrics,
|
2022-02-11 16:23:16 -08:00
|
|
|
feature_set: &FeatureSet,
|
|
|
|
fee_structure: &FeeStructure,
|
2020-11-29 11:21:55 -08:00
|
|
|
) -> Vec<TransactionLoadResult> {
|
2019-06-10 22:18:32 -07:00
|
|
|
let mut hash_queue = BlockhashQueue::new(100);
|
2021-10-22 14:32:40 -07:00
|
|
|
hash_queue.register_hash(&tx.message().recent_blockhash, lamports_per_signature);
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
for ka in ka.iter() {
|
2023-01-06 15:30:42 -08:00
|
|
|
accounts.store_for_tests(0, &ka.0, &ka.1);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
2019-04-15 17:15:50 -07:00
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
2021-10-27 10:09:16 -07:00
|
|
|
let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
|
2020-05-15 09:35:43 -07:00
|
|
|
accounts.load_accounts(
|
2019-08-23 14:04:53 -07:00
|
|
|
&ancestors,
|
2021-08-17 15:17:56 -07:00
|
|
|
&[sanitized_tx],
|
2021-12-29 23:42:32 -08:00
|
|
|
vec![(Ok(()), None)],
|
2019-08-23 14:04:53 -07:00
|
|
|
&hash_queue,
|
|
|
|
error_counters,
|
2020-01-03 16:34:58 -08:00
|
|
|
rent_collector,
|
2022-02-11 16:23:16 -08:00
|
|
|
feature_set,
|
|
|
|
fee_structure,
|
2022-04-22 03:32:31 -07:00
|
|
|
None,
|
2023-03-28 11:49:56 -07:00
|
|
|
&HashMap::new(),
|
|
|
|
&HashMap::new(),
|
2020-05-15 09:35:43 -07:00
|
|
|
)
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
2022-12-08 11:46:37 -08:00
|
|
|
/// get a feature set with all features activated
|
|
|
|
/// with the optional except of 'exclude'
|
|
|
|
fn all_features_except(exclude: Option<&[Pubkey]>) -> FeatureSet {
|
|
|
|
let mut features = FeatureSet::all_enabled();
|
|
|
|
if let Some(exclude) = exclude {
|
|
|
|
features.active.retain(|k, _v| !exclude.contains(k));
|
|
|
|
}
|
|
|
|
features
|
|
|
|
}
|
|
|
|
|
2020-01-03 16:34:58 -08:00
|
|
|
fn load_accounts_with_fee(
|
|
|
|
tx: Transaction,
|
2021-12-27 09:49:32 -08:00
|
|
|
ka: &[TransactionAccount],
|
2021-10-22 14:32:40 -07:00
|
|
|
lamports_per_signature: u64,
|
2022-04-23 16:10:47 -07:00
|
|
|
error_counters: &mut TransactionErrorMetrics,
|
2022-12-08 11:46:37 -08:00
|
|
|
exclude_features: Option<&[Pubkey]>,
|
2020-11-29 11:21:55 -08:00
|
|
|
) -> Vec<TransactionLoadResult> {
|
2021-10-22 14:32:40 -07:00
|
|
|
load_accounts_with_fee_and_rent(
|
|
|
|
tx,
|
|
|
|
ka,
|
|
|
|
lamports_per_signature,
|
|
|
|
&RentCollector::default(),
|
|
|
|
error_counters,
|
2022-12-08 11:46:37 -08:00
|
|
|
&all_features_except(exclude_features),
|
2022-02-11 16:23:16 -08:00
|
|
|
&FeeStructure::default(),
|
2021-10-22 14:32:40 -07:00
|
|
|
)
|
2020-01-03 16:34:58 -08:00
|
|
|
}
|
|
|
|
|
2019-03-29 15:11:21 -07:00
|
|
|
fn load_accounts(
|
|
|
|
tx: Transaction,
|
2021-12-27 09:49:32 -08:00
|
|
|
ka: &[TransactionAccount],
|
2022-04-23 16:10:47 -07:00
|
|
|
error_counters: &mut TransactionErrorMetrics,
|
2020-11-29 11:21:55 -08:00
|
|
|
) -> Vec<TransactionLoadResult> {
|
2022-12-08 11:46:37 -08:00
|
|
|
load_accounts_with_fee(tx, ka, 0, error_counters, None)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_accounts_with_excluded_features(
|
|
|
|
tx: Transaction,
|
|
|
|
ka: &[TransactionAccount],
|
|
|
|
error_counters: &mut TransactionErrorMetrics,
|
|
|
|
exclude_features: Option<&[Pubkey]>,
|
|
|
|
) -> Vec<TransactionLoadResult> {
|
|
|
|
load_accounts_with_fee(tx, ka, 0, error_counters, exclude_features)
|
2019-03-29 15:11:21 -07:00
|
|
|
}
|
|
|
|
|
2021-09-17 15:19:29 -07:00
|
|
|
#[test]
|
|
|
|
fn test_hold_range_in_memory() {
|
|
|
|
let accts = Accounts::default_for_tests();
|
2023-01-21 10:06:27 -08:00
|
|
|
let range = Pubkey::from([0; 32])..=Pubkey::from([0xff; 32]);
|
2021-12-21 15:31:48 -08:00
|
|
|
accts.hold_range_in_memory(&range, true, &test_thread_pool());
|
|
|
|
accts.hold_range_in_memory(&range, false, &test_thread_pool());
|
|
|
|
accts.hold_range_in_memory(&range, true, &test_thread_pool());
|
|
|
|
accts.hold_range_in_memory(&range, true, &test_thread_pool());
|
|
|
|
accts.hold_range_in_memory(&range, false, &test_thread_pool());
|
|
|
|
accts.hold_range_in_memory(&range, false, &test_thread_pool());
|
2021-09-17 15:19:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_hold_range_in_memory2() {
|
|
|
|
let accts = Accounts::default_for_tests();
|
2023-01-21 10:06:27 -08:00
|
|
|
let range = Pubkey::from([0; 32])..=Pubkey::from([0xff; 32]);
|
2021-09-17 15:19:29 -07:00
|
|
|
let idx = &accts.accounts_db.accounts_index;
|
|
|
|
let bins = idx.account_maps.len();
|
|
|
|
// use bins * 2 to get the first half of the range within bin 0
|
|
|
|
let bins_2 = bins * 2;
|
2021-10-18 12:05:16 -07:00
|
|
|
let binner = crate::pubkey_bins::PubkeyBinCalculator24::new(bins_2);
|
2021-09-17 15:19:29 -07:00
|
|
|
let range2 =
|
|
|
|
binner.lowest_pubkey_from_bin(0, bins_2)..binner.lowest_pubkey_from_bin(1, bins_2);
|
|
|
|
let range2_inclusive = range2.start..=range2.end;
|
|
|
|
assert_eq!(0, idx.bin_calculator.bin_from_pubkey(&range2.start));
|
|
|
|
assert_eq!(0, idx.bin_calculator.bin_from_pubkey(&range2.end));
|
2021-12-21 15:31:48 -08:00
|
|
|
accts.hold_range_in_memory(&range, true, &test_thread_pool());
|
2022-07-20 08:06:08 -07:00
|
|
|
idx.account_maps.iter().for_each(|map| {
|
2021-09-17 15:19:29 -07:00
|
|
|
assert_eq!(
|
|
|
|
map.cache_ranges_held.read().unwrap().to_vec(),
|
2021-12-16 19:22:04 -08:00
|
|
|
vec![range.clone()]
|
2021-09-17 15:19:29 -07:00
|
|
|
);
|
|
|
|
});
|
2021-12-21 15:31:48 -08:00
|
|
|
accts.hold_range_in_memory(&range2, true, &test_thread_pool());
|
2021-09-17 15:19:29 -07:00
|
|
|
idx.account_maps.iter().enumerate().for_each(|(bin, map)| {
|
|
|
|
let expected = if bin == 0 {
|
2021-12-16 19:22:04 -08:00
|
|
|
vec![range.clone(), range2_inclusive.clone()]
|
2021-09-17 15:19:29 -07:00
|
|
|
} else {
|
2021-12-16 19:22:04 -08:00
|
|
|
vec![range.clone()]
|
2021-09-17 15:19:29 -07:00
|
|
|
};
|
|
|
|
assert_eq!(
|
|
|
|
map.cache_ranges_held.read().unwrap().to_vec(),
|
|
|
|
expected,
|
2022-12-06 06:30:06 -08:00
|
|
|
"bin: {bin}"
|
2021-09-17 15:19:29 -07:00
|
|
|
);
|
|
|
|
});
|
2021-12-21 15:31:48 -08:00
|
|
|
accts.hold_range_in_memory(&range, false, &test_thread_pool());
|
|
|
|
accts.hold_range_in_memory(&range2, false, &test_thread_pool());
|
|
|
|
}
|
|
|
|
|
|
|
|
fn test_thread_pool() -> rayon::ThreadPool {
|
|
|
|
crate::accounts_db::make_min_priority_thread_pool()
|
2021-09-17 15:19:29 -07:00
|
|
|
}
|
|
|
|
|
2019-01-08 09:20:25 -08:00
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_no_account_0_exists() {
|
2021-12-27 09:49:32 -08:00
|
|
|
let accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2019-01-08 09:20:25 -08:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
|
2019-03-15 08:47:25 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
|
2019-03-15 09:02:28 -07:00
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
2019-01-08 09:20:25 -08:00
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
2019-02-07 08:03:40 -08:00
|
|
|
vec![native_loader::id()],
|
2019-01-08 09:20:25 -08:00
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2019-02-07 01:54:27 -08:00
|
|
|
assert_eq!(error_counters.account_not_found, 1);
|
2019-01-08 09:20:25 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
2019-12-07 11:54:10 -08:00
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0],
|
2020-11-29 11:21:55 -08:00
|
|
|
(Err(TransactionError::AccountNotFound), None,),
|
2019-12-07 11:54:10 -08:00
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_unknown_program_id() {
|
2021-12-27 09:49:32 -08:00
|
|
|
let mut accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2019-01-08 09:20:25 -08:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
2023-01-21 10:06:27 -08:00
|
|
|
let key1 = Pubkey::from([5u8; 32]);
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let account = AccountSharedData::new(1, 0, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let account = AccountSharedData::new(2, 1, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key1, account));
|
|
|
|
|
2019-03-15 08:47:25 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
|
2019-03-15 09:02:28 -07:00
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
2019-01-08 09:20:25 -08:00
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
vec![Pubkey::default()],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2019-02-07 01:54:27 -08:00
|
|
|
assert_eq!(error_counters.account_not_found, 1);
|
2019-01-08 09:20:25 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
2019-05-22 15:23:16 -07:00
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0],
|
2020-11-29 11:21:55 -08:00
|
|
|
(Err(TransactionError::ProgramAccountNotFound), None,)
|
2019-05-22 15:23:16 -07:00
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_insufficient_funds() {
|
2022-06-29 23:54:45 -07:00
|
|
|
let lamports_per_signature = 5000;
|
2021-12-27 09:49:32 -08:00
|
|
|
let mut accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2019-01-08 09:20:25 -08:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let account = AccountSharedData::new(1, 0, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2019-03-15 08:47:25 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
|
2019-03-15 09:02:28 -07:00
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
2019-01-08 09:20:25 -08:00
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
2019-02-07 08:03:40 -08:00
|
|
|
vec![native_loader::id()],
|
2019-01-08 09:20:25 -08:00
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
2021-10-22 14:32:40 -07:00
|
|
|
let fee = Bank::calculate_fee(
|
|
|
|
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
|
2022-06-29 23:54:45 -07:00
|
|
|
lamports_per_signature,
|
2022-02-11 16:23:16 -08:00
|
|
|
&FeeStructure::default(),
|
2022-05-12 11:07:36 -07:00
|
|
|
true,
|
2022-09-09 15:24:21 -07:00
|
|
|
false,
|
2023-02-01 14:26:50 -08:00
|
|
|
true,
|
2023-02-24 07:27:49 -08:00
|
|
|
true,
|
2023-02-03 07:21:57 -08:00
|
|
|
true,
|
2023-03-21 16:41:41 -07:00
|
|
|
false,
|
2021-10-22 14:32:40 -07:00
|
|
|
);
|
2022-06-29 23:54:45 -07:00
|
|
|
assert_eq!(fee, lamports_per_signature);
|
2019-03-29 15:11:21 -07:00
|
|
|
|
2022-12-08 11:46:37 -08:00
|
|
|
let loaded_accounts = load_accounts_with_fee(
|
|
|
|
tx,
|
|
|
|
&accounts,
|
|
|
|
lamports_per_signature,
|
|
|
|
&mut error_counters,
|
|
|
|
None,
|
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2019-02-07 01:54:27 -08:00
|
|
|
assert_eq!(error_counters.insufficient_funds, 1);
|
2019-01-08 09:20:25 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
2019-03-13 12:58:44 -07:00
|
|
|
assert_eq!(
|
2019-06-27 14:25:10 -07:00
|
|
|
loaded_accounts[0].clone(),
|
2020-11-29 11:21:55 -08:00
|
|
|
(Err(TransactionError::InsufficientFundsForFee), None,),
|
2019-05-24 13:06:55 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_invalid_account_for_fee() {
|
2021-12-27 09:49:32 -08:00
|
|
|
let mut accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2019-05-24 13:06:55 -07:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let account = AccountSharedData::new(1, 1, &solana_sdk::pubkey::new_rand()); // <-- owner is not the system program
|
2019-05-24 13:06:55 -07:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
|
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
vec![native_loader::id()],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
|
|
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_account_for_fee, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0],
|
2020-11-29 11:21:55 -08:00
|
|
|
(Err(TransactionError::InvalidAccountForFee), None,),
|
2019-03-13 12:58:44 -07:00
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
2020-01-03 16:34:58 -08:00
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_fee_payer_is_nonce() {
|
2022-06-29 23:54:45 -07:00
|
|
|
let lamports_per_signature = 5000;
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2020-01-03 16:34:58 -08:00
|
|
|
let rent_collector = RentCollector::new(
|
|
|
|
0,
|
2022-07-06 13:01:16 -07:00
|
|
|
EpochSchedule::default(),
|
2020-01-03 16:34:58 -08:00
|
|
|
500_000.0,
|
2022-07-06 13:01:16 -07:00
|
|
|
Rent {
|
2020-01-03 16:34:58 -08:00
|
|
|
lamports_per_byte_year: 42,
|
|
|
|
..Rent::default()
|
|
|
|
},
|
|
|
|
);
|
2021-11-18 16:07:17 -08:00
|
|
|
let min_balance = rent_collector.rent.minimum_balance(NonceState::size());
|
2020-01-03 16:34:58 -08:00
|
|
|
let nonce = Keypair::new();
|
|
|
|
let mut accounts = vec![(
|
|
|
|
nonce.pubkey(),
|
2021-03-09 13:06:07 -08:00
|
|
|
AccountSharedData::new_data(
|
2022-06-29 23:54:45 -07:00
|
|
|
min_balance + lamports_per_signature,
|
2022-07-06 05:03:13 -07:00
|
|
|
&NonceVersions::new(NonceState::Initialized(nonce::state::Data::default())),
|
2020-01-03 16:34:58 -08:00
|
|
|
&system_program::id(),
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
)];
|
|
|
|
let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
|
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
|
|
|
&[&nonce],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
vec![native_loader::id()],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Fee leaves min_balance balance succeeds
|
|
|
|
let loaded_accounts = load_accounts_with_fee_and_rent(
|
|
|
|
tx.clone(),
|
|
|
|
&accounts,
|
2022-06-29 23:54:45 -07:00
|
|
|
lamports_per_signature,
|
2020-01-03 16:34:58 -08:00
|
|
|
&rent_collector,
|
|
|
|
&mut error_counters,
|
2022-12-08 11:46:37 -08:00
|
|
|
&all_features_except(None),
|
2022-02-11 16:23:16 -08:00
|
|
|
&FeeStructure::default(),
|
2020-01-03 16:34:58 -08:00
|
|
|
);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
2021-11-18 16:07:17 -08:00
|
|
|
let (load_res, _nonce) = &loaded_accounts[0];
|
2021-01-25 01:35:08 -08:00
|
|
|
let loaded_transaction = load_res.as_ref().unwrap();
|
2021-07-05 04:49:37 -07:00
|
|
|
assert_eq!(loaded_transaction.accounts[0].1.lamports(), min_balance);
|
2020-01-03 16:34:58 -08:00
|
|
|
|
|
|
|
// Fee leaves zero balance fails
|
2022-06-29 23:54:45 -07:00
|
|
|
accounts[0].1.set_lamports(lamports_per_signature);
|
2020-01-03 16:34:58 -08:00
|
|
|
let loaded_accounts = load_accounts_with_fee_and_rent(
|
|
|
|
tx.clone(),
|
|
|
|
&accounts,
|
2022-06-29 23:54:45 -07:00
|
|
|
lamports_per_signature,
|
2020-01-03 16:34:58 -08:00
|
|
|
&rent_collector,
|
|
|
|
&mut error_counters,
|
2022-06-29 23:54:45 -07:00
|
|
|
&FeatureSet::all_enabled(),
|
2022-02-11 16:23:16 -08:00
|
|
|
&FeeStructure::default(),
|
2020-01-03 16:34:58 -08:00
|
|
|
);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
2021-11-18 16:07:17 -08:00
|
|
|
let (load_res, _nonce) = &loaded_accounts[0];
|
2020-01-03 16:34:58 -08:00
|
|
|
assert_eq!(*load_res, Err(TransactionError::InsufficientFundsForFee));
|
|
|
|
|
|
|
|
// Fee leaves non-zero, but sub-min_balance balance fails
|
2022-06-29 23:54:45 -07:00
|
|
|
accounts[0]
|
|
|
|
.1
|
|
|
|
.set_lamports(lamports_per_signature + min_balance / 2);
|
2020-01-03 16:34:58 -08:00
|
|
|
let loaded_accounts = load_accounts_with_fee_and_rent(
|
2020-05-15 09:35:43 -07:00
|
|
|
tx,
|
2020-01-03 16:34:58 -08:00
|
|
|
&accounts,
|
2022-06-29 23:54:45 -07:00
|
|
|
lamports_per_signature,
|
2020-01-03 16:34:58 -08:00
|
|
|
&rent_collector,
|
|
|
|
&mut error_counters,
|
2022-06-29 23:54:45 -07:00
|
|
|
&FeatureSet::all_enabled(),
|
2022-02-11 16:23:16 -08:00
|
|
|
&FeeStructure::default(),
|
2020-01-03 16:34:58 -08:00
|
|
|
);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
2021-11-18 16:07:17 -08:00
|
|
|
let (load_res, _nonce) = &loaded_accounts[0];
|
2020-01-03 16:34:58 -08:00
|
|
|
assert_eq!(*load_res, Err(TransactionError::InsufficientFundsForFee));
|
|
|
|
}
|
|
|
|
|
2019-01-08 09:20:25 -08:00
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_no_loaders() {
|
2021-12-27 09:49:32 -08:00
|
|
|
let mut accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2019-01-08 09:20:25 -08:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
2023-01-21 10:06:27 -08:00
|
|
|
let key1 = Pubkey::from([5u8; 32]);
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
|
2021-04-27 11:51:13 -07:00
|
|
|
account.set_rent_epoch(1);
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let mut account = AccountSharedData::new(2, 1, &Pubkey::default());
|
2021-04-27 11:51:13 -07:00
|
|
|
account.set_rent_epoch(1);
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key1, account));
|
|
|
|
|
2019-05-22 15:23:16 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
2019-03-15 09:02:28 -07:00
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
2019-01-08 09:20:25 -08:00
|
|
|
&[&keypair],
|
|
|
|
&[key1],
|
|
|
|
Hash::default(),
|
2019-02-07 08:03:40 -08:00
|
|
|
vec![native_loader::id()],
|
2019-01-08 09:20:25 -08:00
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
2022-12-08 11:46:37 -08:00
|
|
|
let loaded_accounts =
|
|
|
|
load_accounts_with_excluded_features(tx, &accounts, &mut error_counters, None);
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2019-02-07 01:54:27 -08:00
|
|
|
assert_eq!(error_counters.account_not_found, 0);
|
2019-01-08 09:20:25 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
match &loaded_accounts[0] {
|
2021-11-18 16:07:17 -08:00
|
|
|
(Ok(loaded_transaction), _nonce) => {
|
2021-01-25 01:35:08 -08:00
|
|
|
assert_eq!(loaded_transaction.accounts.len(), 3);
|
2021-07-05 04:49:37 -07:00
|
|
|
assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
|
2021-09-09 23:36:21 -07:00
|
|
|
assert_eq!(loaded_transaction.program_indices.len(), 1);
|
|
|
|
assert_eq!(loaded_transaction.program_indices[0].len(), 0);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
2021-11-18 16:07:17 -08:00
|
|
|
(Err(e), _nonce) => Err(e).unwrap(),
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 10:40:41 -08:00
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_bad_owner() {
|
2021-12-27 09:49:32 -08:00
|
|
|
let mut accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2020-03-04 10:40:41 -08:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
2023-01-21 10:06:27 -08:00
|
|
|
let key1 = Pubkey::from([5u8; 32]);
|
2020-03-04 10:40:41 -08:00
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let account = AccountSharedData::new(1, 0, &Pubkey::default());
|
2020-03-04 10:40:41 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
|
2021-04-28 07:07:43 -07:00
|
|
|
account.set_executable(true);
|
2020-03-04 10:40:41 -08:00
|
|
|
accounts.push((key1, account));
|
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
|
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
vec![key1],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
|
|
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
|
|
|
|
2019-02-07 01:54:27 -08:00
|
|
|
assert_eq!(error_counters.account_not_found, 1);
|
2019-01-08 09:20:25 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
2019-12-07 11:54:10 -08:00
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0],
|
2020-11-29 11:21:55 -08:00
|
|
|
(Err(TransactionError::ProgramAccountNotFound), None,)
|
2019-12-07 11:54:10 -08:00
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_not_executable() {
|
2021-12-27 09:49:32 -08:00
|
|
|
let mut accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2019-01-08 09:20:25 -08:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
2023-01-21 10:06:27 -08:00
|
|
|
let key1 = Pubkey::from([5u8; 32]);
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let account = AccountSharedData::new(1, 0, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let account = AccountSharedData::new(40, 1, &native_loader::id());
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key1, account));
|
|
|
|
|
2019-05-24 11:04:05 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(1, &(), vec![0])];
|
2019-03-15 09:02:28 -07:00
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
2019-01-08 09:20:25 -08:00
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
vec![key1],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2020-03-25 13:23:05 -07:00
|
|
|
assert_eq!(error_counters.invalid_program_for_execution, 1);
|
2019-01-08 09:20:25 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
2019-12-07 11:54:10 -08:00
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0],
|
2020-11-29 11:21:55 -08:00
|
|
|
(Err(TransactionError::InvalidProgramForExecution), None,)
|
2019-12-07 11:54:10 -08:00
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
2023-02-16 13:29:38 -08:00
|
|
|
#[test]
|
|
|
|
fn test_filter_executable_program_accounts() {
|
|
|
|
let mut tx_accounts: Vec<TransactionAccount> = Vec::new();
|
|
|
|
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
|
|
|
|
let non_program_pubkey1 = Pubkey::new_unique();
|
|
|
|
let non_program_pubkey2 = Pubkey::new_unique();
|
|
|
|
let program1_pubkey = Pubkey::new_unique();
|
|
|
|
let program2_pubkey = Pubkey::new_unique();
|
|
|
|
let account1_pubkey = Pubkey::new_unique();
|
|
|
|
let account2_pubkey = Pubkey::new_unique();
|
|
|
|
let account3_pubkey = Pubkey::new_unique();
|
|
|
|
let account4_pubkey = Pubkey::new_unique();
|
|
|
|
|
|
|
|
let account5_pubkey = Pubkey::new_unique();
|
|
|
|
|
|
|
|
tx_accounts.push((
|
|
|
|
non_program_pubkey1,
|
|
|
|
AccountSharedData::new(1, 10, &account5_pubkey),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
non_program_pubkey2,
|
|
|
|
AccountSharedData::new(1, 10, &account5_pubkey),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
program1_pubkey,
|
|
|
|
AccountSharedData::new(40, 1, &account5_pubkey),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
program2_pubkey,
|
|
|
|
AccountSharedData::new(40, 1, &account5_pubkey),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
account1_pubkey,
|
|
|
|
AccountSharedData::new(1, 10, &non_program_pubkey1),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
account2_pubkey,
|
|
|
|
AccountSharedData::new(1, 10, &non_program_pubkey2),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
account3_pubkey,
|
|
|
|
AccountSharedData::new(40, 1, &program1_pubkey),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
account4_pubkey,
|
|
|
|
AccountSharedData::new(40, 1, &program2_pubkey),
|
|
|
|
));
|
|
|
|
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
|
|
|
for tx_account in tx_accounts.iter() {
|
|
|
|
accounts.store_for_tests(0, &tx_account.0, &tx_account.1);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut hash_queue = BlockhashQueue::new(100);
|
|
|
|
|
|
|
|
let tx1 = Transaction::new_with_compiled_instructions(
|
|
|
|
&[&keypair1],
|
|
|
|
&[non_program_pubkey1],
|
|
|
|
Hash::new_unique(),
|
|
|
|
vec![account1_pubkey, account2_pubkey, account3_pubkey],
|
|
|
|
vec![CompiledInstruction::new(1, &(), vec![0])],
|
|
|
|
);
|
|
|
|
hash_queue.register_hash(&tx1.message().recent_blockhash, 0);
|
|
|
|
let sanitized_tx1 = SanitizedTransaction::from_transaction_for_tests(tx1);
|
|
|
|
|
|
|
|
let tx2 = Transaction::new_with_compiled_instructions(
|
|
|
|
&[&keypair2],
|
|
|
|
&[non_program_pubkey2],
|
|
|
|
Hash::new_unique(),
|
|
|
|
vec![account4_pubkey, account3_pubkey, account2_pubkey],
|
|
|
|
vec![CompiledInstruction::new(1, &(), vec![0])],
|
|
|
|
);
|
|
|
|
hash_queue.register_hash(&tx2.message().recent_blockhash, 0);
|
|
|
|
let sanitized_tx2 = SanitizedTransaction::from_transaction_for_tests(tx2);
|
|
|
|
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
|
|
|
let programs = accounts.filter_executable_program_accounts(
|
|
|
|
&ancestors,
|
|
|
|
&[sanitized_tx1, sanitized_tx2],
|
|
|
|
&mut [(Ok(()), None), (Ok(()), None)],
|
|
|
|
&[&program1_pubkey, &program2_pubkey],
|
|
|
|
&hash_queue,
|
|
|
|
);
|
|
|
|
|
|
|
|
// The result should contain only account3_pubkey, and account4_pubkey as the program accounts
|
|
|
|
assert_eq!(programs.len(), 2);
|
|
|
|
assert_eq!(
|
|
|
|
programs
|
|
|
|
.get(&account3_pubkey)
|
|
|
|
.expect("failed to find the program account"),
|
|
|
|
&&program1_pubkey
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
programs
|
|
|
|
.get(&account4_pubkey)
|
|
|
|
.expect("failed to find the program account"),
|
|
|
|
&&program2_pubkey
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_filter_executable_program_accounts_invalid_blockhash() {
|
|
|
|
let mut tx_accounts: Vec<TransactionAccount> = Vec::new();
|
|
|
|
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
|
|
|
|
let non_program_pubkey1 = Pubkey::new_unique();
|
|
|
|
let non_program_pubkey2 = Pubkey::new_unique();
|
|
|
|
let program1_pubkey = Pubkey::new_unique();
|
|
|
|
let program2_pubkey = Pubkey::new_unique();
|
|
|
|
let account1_pubkey = Pubkey::new_unique();
|
|
|
|
let account2_pubkey = Pubkey::new_unique();
|
|
|
|
let account3_pubkey = Pubkey::new_unique();
|
|
|
|
let account4_pubkey = Pubkey::new_unique();
|
|
|
|
|
|
|
|
let account5_pubkey = Pubkey::new_unique();
|
|
|
|
|
|
|
|
tx_accounts.push((
|
|
|
|
non_program_pubkey1,
|
|
|
|
AccountSharedData::new(1, 10, &account5_pubkey),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
non_program_pubkey2,
|
|
|
|
AccountSharedData::new(1, 10, &account5_pubkey),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
program1_pubkey,
|
|
|
|
AccountSharedData::new(40, 1, &account5_pubkey),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
program2_pubkey,
|
|
|
|
AccountSharedData::new(40, 1, &account5_pubkey),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
account1_pubkey,
|
|
|
|
AccountSharedData::new(1, 10, &non_program_pubkey1),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
account2_pubkey,
|
|
|
|
AccountSharedData::new(1, 10, &non_program_pubkey2),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
account3_pubkey,
|
|
|
|
AccountSharedData::new(40, 1, &program1_pubkey),
|
|
|
|
));
|
|
|
|
tx_accounts.push((
|
|
|
|
account4_pubkey,
|
|
|
|
AccountSharedData::new(40, 1, &program2_pubkey),
|
|
|
|
));
|
|
|
|
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
|
|
|
for tx_account in tx_accounts.iter() {
|
|
|
|
accounts.store_for_tests(0, &tx_account.0, &tx_account.1);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut hash_queue = BlockhashQueue::new(100);
|
|
|
|
|
|
|
|
let tx1 = Transaction::new_with_compiled_instructions(
|
|
|
|
&[&keypair1],
|
|
|
|
&[non_program_pubkey1],
|
|
|
|
Hash::new_unique(),
|
|
|
|
vec![account1_pubkey, account2_pubkey, account3_pubkey],
|
|
|
|
vec![CompiledInstruction::new(1, &(), vec![0])],
|
|
|
|
);
|
|
|
|
hash_queue.register_hash(&tx1.message().recent_blockhash, 0);
|
|
|
|
let sanitized_tx1 = SanitizedTransaction::from_transaction_for_tests(tx1);
|
|
|
|
|
|
|
|
let tx2 = Transaction::new_with_compiled_instructions(
|
|
|
|
&[&keypair2],
|
|
|
|
&[non_program_pubkey2],
|
|
|
|
Hash::new_unique(),
|
|
|
|
vec![account4_pubkey, account3_pubkey, account2_pubkey],
|
|
|
|
vec![CompiledInstruction::new(1, &(), vec![0])],
|
|
|
|
);
|
|
|
|
// 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 mut lock_results = vec![(Ok(()), None), (Ok(()), None)];
|
|
|
|
let programs = accounts.filter_executable_program_accounts(
|
|
|
|
&ancestors,
|
|
|
|
&[sanitized_tx1, sanitized_tx2],
|
|
|
|
&mut lock_results,
|
|
|
|
&[&program1_pubkey, &program2_pubkey],
|
|
|
|
&hash_queue,
|
|
|
|
);
|
|
|
|
|
|
|
|
// The result should contain only account3_pubkey as the program accounts
|
|
|
|
assert_eq!(programs.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
programs
|
|
|
|
.get(&account3_pubkey)
|
|
|
|
.expect("failed to find the program account"),
|
|
|
|
&&program1_pubkey
|
|
|
|
);
|
|
|
|
assert_eq!(lock_results[1].0, Err(TransactionError::BlockhashNotFound));
|
|
|
|
}
|
|
|
|
|
2019-01-08 09:20:25 -08:00
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_multiple_loaders() {
|
2021-12-27 09:49:32 -08:00
|
|
|
let mut accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2019-01-08 09:20:25 -08:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
2023-01-21 10:06:27 -08:00
|
|
|
let key1 = Pubkey::from([5u8; 32]);
|
|
|
|
let key2 = Pubkey::from([6u8; 32]);
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
|
2021-04-27 11:51:13 -07:00
|
|
|
account.set_rent_epoch(1);
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let mut account = AccountSharedData::new(40, 1, &Pubkey::default());
|
2021-04-28 07:07:43 -07:00
|
|
|
account.set_executable(true);
|
2021-04-27 11:51:13 -07:00
|
|
|
account.set_rent_epoch(1);
|
2021-04-23 07:34:28 -07:00
|
|
|
account.set_owner(native_loader::id());
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key1, account));
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let mut account = AccountSharedData::new(41, 1, &Pubkey::default());
|
2021-04-28 07:07:43 -07:00
|
|
|
account.set_executable(true);
|
2021-04-27 11:51:13 -07:00
|
|
|
account.set_rent_epoch(1);
|
2021-04-23 07:34:28 -07:00
|
|
|
account.set_owner(key1);
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key2, account));
|
|
|
|
|
|
|
|
let instructions = vec![
|
2019-03-15 08:47:25 -07:00
|
|
|
CompiledInstruction::new(1, &(), vec![0]),
|
2019-05-22 15:23:16 -07:00
|
|
|
CompiledInstruction::new(2, &(), vec![0]),
|
2019-01-08 09:20:25 -08:00
|
|
|
];
|
2019-03-15 09:02:28 -07:00
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
2019-01-08 09:20:25 -08:00
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
vec![key1, key2],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
2022-12-08 11:46:37 -08:00
|
|
|
let loaded_accounts =
|
|
|
|
load_accounts_with_excluded_features(tx, &accounts, &mut error_counters, None);
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2019-02-07 01:54:27 -08:00
|
|
|
assert_eq!(error_counters.account_not_found, 0);
|
2019-01-08 09:20:25 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
match &loaded_accounts[0] {
|
2021-11-18 16:07:17 -08:00
|
|
|
(Ok(loaded_transaction), _nonce) => {
|
2023-01-27 12:24:21 -08:00
|
|
|
assert_eq!(loaded_transaction.accounts.len(), 4);
|
2021-07-05 04:49:37 -07:00
|
|
|
assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
|
2021-09-09 23:36:21 -07:00
|
|
|
assert_eq!(loaded_transaction.program_indices.len(), 2);
|
|
|
|
assert_eq!(loaded_transaction.program_indices[0].len(), 1);
|
|
|
|
assert_eq!(loaded_transaction.program_indices[1].len(), 2);
|
|
|
|
for program_indices in loaded_transaction.program_indices.iter() {
|
|
|
|
for (i, program_index) in program_indices.iter().enumerate() {
|
2019-01-08 09:20:25 -08:00
|
|
|
// +1 to skip first not loader account
|
2021-09-09 23:36:21 -07:00
|
|
|
assert_eq!(
|
2022-09-06 02:31:40 -07:00
|
|
|
loaded_transaction.accounts[*program_index as usize].0,
|
2021-09-09 23:36:21 -07:00
|
|
|
accounts[i + 1].0
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2022-09-06 02:31:40 -07:00
|
|
|
loaded_transaction.accounts[*program_index as usize].1,
|
2021-09-09 23:36:21 -07:00
|
|
|
accounts[i + 1].1
|
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-18 16:07:17 -08:00
|
|
|
(Err(e), _nonce) => Err(e).unwrap(),
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
}
|
2019-02-07 11:14:10 -08:00
|
|
|
|
2022-01-06 19:59:09 -08:00
|
|
|
#[test]
|
|
|
|
fn test_load_lookup_table_addresses_account_not_found() {
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let invalid_table_key = Pubkey::new_unique();
|
|
|
|
let address_table_lookup = MessageAddressTableLookup {
|
|
|
|
account_key: invalid_table_key,
|
|
|
|
writable_indexes: vec![],
|
|
|
|
readonly_indexes: vec![],
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
accounts.load_lookup_table_addresses(
|
|
|
|
&ancestors,
|
|
|
|
&address_table_lookup,
|
|
|
|
&SlotHashes::default(),
|
|
|
|
),
|
|
|
|
Err(AddressLookupError::LookupTableAccountNotFound),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_lookup_table_addresses_invalid_account_owner() {
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let invalid_table_key = Pubkey::new_unique();
|
2022-09-15 08:46:32 -07:00
|
|
|
let mut invalid_table_account = AccountSharedData::default();
|
|
|
|
invalid_table_account.set_lamports(1);
|
2022-01-06 19:59:09 -08:00
|
|
|
accounts.store_slow_uncached(0, &invalid_table_key, &invalid_table_account);
|
|
|
|
|
|
|
|
let address_table_lookup = MessageAddressTableLookup {
|
|
|
|
account_key: invalid_table_key,
|
|
|
|
writable_indexes: vec![],
|
|
|
|
readonly_indexes: vec![],
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
accounts.load_lookup_table_addresses(
|
|
|
|
&ancestors,
|
|
|
|
&address_table_lookup,
|
|
|
|
&SlotHashes::default(),
|
|
|
|
),
|
|
|
|
Err(AddressLookupError::InvalidAccountOwner),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_lookup_table_addresses_invalid_account_data() {
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let invalid_table_key = Pubkey::new_unique();
|
|
|
|
let invalid_table_account =
|
|
|
|
AccountSharedData::new(1, 0, &solana_address_lookup_table_program::id());
|
|
|
|
accounts.store_slow_uncached(0, &invalid_table_key, &invalid_table_account);
|
|
|
|
|
|
|
|
let address_table_lookup = MessageAddressTableLookup {
|
|
|
|
account_key: invalid_table_key,
|
|
|
|
writable_indexes: vec![],
|
|
|
|
readonly_indexes: vec![],
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
accounts.load_lookup_table_addresses(
|
|
|
|
&ancestors,
|
|
|
|
&address_table_lookup,
|
|
|
|
&SlotHashes::default(),
|
|
|
|
),
|
|
|
|
Err(AddressLookupError::InvalidAccountData),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_lookup_table_addresses() {
|
|
|
|
let ancestors = vec![(1, 1), (0, 0)].into_iter().collect();
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let table_key = Pubkey::new_unique();
|
|
|
|
let table_addresses = vec![Pubkey::new_unique(), Pubkey::new_unique()];
|
|
|
|
let table_account = {
|
|
|
|
let table_state = AddressLookupTable {
|
|
|
|
meta: LookupTableMeta::default(),
|
|
|
|
addresses: Cow::Owned(table_addresses.clone()),
|
|
|
|
};
|
|
|
|
AccountSharedData::create(
|
|
|
|
1,
|
2022-03-07 23:20:34 -08:00
|
|
|
table_state.serialize_for_tests().unwrap(),
|
2022-01-06 19:59:09 -08:00
|
|
|
solana_address_lookup_table_program::id(),
|
|
|
|
false,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
};
|
|
|
|
accounts.store_slow_uncached(0, &table_key, &table_account);
|
|
|
|
|
|
|
|
let address_table_lookup = MessageAddressTableLookup {
|
|
|
|
account_key: table_key,
|
|
|
|
writable_indexes: vec![0],
|
|
|
|
readonly_indexes: vec![1],
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
accounts.load_lookup_table_addresses(
|
|
|
|
&ancestors,
|
|
|
|
&address_table_lookup,
|
|
|
|
&SlotHashes::default(),
|
|
|
|
),
|
|
|
|
Ok(LoadedAddresses {
|
|
|
|
writable: vec![table_addresses[0]],
|
|
|
|
readonly: vec![table_addresses[1]],
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-21 12:08:50 -08:00
|
|
|
#[test]
|
2019-10-23 22:01:22 -07:00
|
|
|
fn test_load_by_program_slot() {
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2019-03-07 12:41:00 -08:00
|
|
|
|
2021-02-18 23:42:09 -08:00
|
|
|
// Load accounts owned by various programs into AccountsDb
|
2020-10-19 12:12:08 -07:00
|
|
|
let pubkey0 = solana_sdk::pubkey::new_rand();
|
2023-01-21 10:06:27 -08:00
|
|
|
let account0 = AccountSharedData::new(1, 0, &Pubkey::from([2; 32]));
|
2021-01-11 17:00:23 -08:00
|
|
|
accounts.store_slow_uncached(0, &pubkey0, &account0);
|
2020-10-19 12:12:08 -07:00
|
|
|
let pubkey1 = solana_sdk::pubkey::new_rand();
|
2023-01-21 10:06:27 -08:00
|
|
|
let account1 = AccountSharedData::new(1, 0, &Pubkey::from([2; 32]));
|
2021-01-11 17:00:23 -08:00
|
|
|
accounts.store_slow_uncached(0, &pubkey1, &account1);
|
2020-10-19 12:12:08 -07:00
|
|
|
let pubkey2 = solana_sdk::pubkey::new_rand();
|
2023-01-21 10:06:27 -08:00
|
|
|
let account2 = AccountSharedData::new(1, 0, &Pubkey::from([3; 32]));
|
2021-01-11 17:00:23 -08:00
|
|
|
accounts.store_slow_uncached(0, &pubkey2, &account2);
|
2019-02-27 21:42:14 -08:00
|
|
|
|
2023-01-21 10:06:27 -08:00
|
|
|
let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::from([2; 32])));
|
2019-04-16 13:32:22 -07:00
|
|
|
assert_eq!(loaded.len(), 2);
|
2023-01-21 10:06:27 -08:00
|
|
|
let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::from([3; 32])));
|
2019-04-16 13:32:22 -07:00
|
|
|
assert_eq!(loaded, vec![(pubkey2, account2)]);
|
2023-01-21 10:06:27 -08:00
|
|
|
let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::from([4; 32])));
|
2019-04-16 13:32:22 -07:00
|
|
|
assert_eq!(loaded, vec![]);
|
2019-03-01 11:53:39 -08:00
|
|
|
}
|
|
|
|
|
2021-09-08 14:21:52 -07:00
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_executable_with_write_lock() {
|
2021-12-27 09:49:32 -08:00
|
|
|
let mut accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
2023-01-21 10:06:27 -08:00
|
|
|
let key1 = Pubkey::from([5u8; 32]);
|
|
|
|
let key2 = Pubkey::from([6u8; 32]);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let mut account = AccountSharedData::new(40, 1, &native_loader::id());
|
|
|
|
account.set_executable(true);
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((key1, account));
|
|
|
|
|
|
|
|
let mut account = AccountSharedData::new(40, 1, &native_loader::id());
|
|
|
|
account.set_executable(true);
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((key2, account));
|
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let mut message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
1, // only one executable marked as readonly
|
|
|
|
vec![key0, key1, key2],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let tx = Transaction::new(&[&keypair], message.clone(), Hash::default());
|
2022-12-08 11:46:37 -08:00
|
|
|
let loaded_accounts =
|
|
|
|
load_accounts_with_excluded_features(tx, &accounts, &mut error_counters, None);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_writable_account, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0],
|
|
|
|
(Err(TransactionError::InvalidWritableAccount), None)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Mark executables as readonly
|
|
|
|
message.account_keys = vec![key0, key1, key2]; // revert key change
|
|
|
|
message.header.num_readonly_unsigned_accounts = 2; // mark both executables as readonly
|
|
|
|
let tx = Transaction::new(&[&keypair], message, Hash::default());
|
2022-12-08 11:46:37 -08:00
|
|
|
let loaded_accounts =
|
|
|
|
load_accounts_with_excluded_features(tx, &accounts, &mut error_counters, None);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_writable_account, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
let result = loaded_accounts[0].0.as_ref().unwrap();
|
|
|
|
assert_eq!(result.accounts[..2], accounts[..2]);
|
2022-09-06 02:31:40 -07:00
|
|
|
assert_eq!(
|
|
|
|
result.accounts[result.program_indices[0][0] as usize],
|
|
|
|
accounts[2]
|
|
|
|
);
|
2021-09-08 14:21:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_upgradeable_with_write_lock() {
|
2021-12-27 09:49:32 -08:00
|
|
|
let mut accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
2023-01-21 10:06:27 -08:00
|
|
|
let key1 = Pubkey::from([5u8; 32]);
|
|
|
|
let key2 = Pubkey::from([6u8; 32]);
|
|
|
|
let programdata_key1 = Pubkey::from([7u8; 32]);
|
|
|
|
let programdata_key2 = Pubkey::from([8u8; 32]);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let program_data = UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: 42,
|
|
|
|
upgrade_authority_address: None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let program = UpgradeableLoaderState::Program {
|
|
|
|
programdata_address: programdata_key1,
|
|
|
|
};
|
|
|
|
let mut account =
|
|
|
|
AccountSharedData::new_data(40, &program, &bpf_loader_upgradeable::id()).unwrap();
|
|
|
|
account.set_executable(true);
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((key1, account));
|
|
|
|
let mut account =
|
|
|
|
AccountSharedData::new_data(40, &program_data, &bpf_loader_upgradeable::id()).unwrap();
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((programdata_key1, account));
|
|
|
|
|
|
|
|
let program = UpgradeableLoaderState::Program {
|
|
|
|
programdata_address: programdata_key2,
|
|
|
|
};
|
|
|
|
let mut account =
|
|
|
|
AccountSharedData::new_data(40, &program, &bpf_loader_upgradeable::id()).unwrap();
|
|
|
|
account.set_executable(true);
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((key2, account));
|
|
|
|
let mut account =
|
|
|
|
AccountSharedData::new_data(40, &program_data, &bpf_loader_upgradeable::id()).unwrap();
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((programdata_key2, account));
|
|
|
|
|
|
|
|
let mut account = AccountSharedData::new(40, 1, &native_loader::id()); // create mock bpf_loader_upgradeable
|
|
|
|
account.set_executable(true);
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((bpf_loader_upgradeable::id(), account));
|
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let mut message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
1, // only one executable marked as readonly
|
|
|
|
vec![key0, key1, key2],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let tx = Transaction::new(&[&keypair], message.clone(), Hash::default());
|
2023-03-28 11:50:49 -07:00
|
|
|
let loaded_accounts = load_accounts_with_excluded_features(
|
|
|
|
tx.clone(),
|
|
|
|
&accounts,
|
|
|
|
&mut error_counters,
|
|
|
|
Some(&[simplify_writable_program_account_check::id()]),
|
|
|
|
);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_writable_account, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0],
|
|
|
|
(Err(TransactionError::InvalidWritableAccount), None)
|
|
|
|
);
|
|
|
|
|
2023-03-28 11:50:49 -07:00
|
|
|
// Solution 0: Include feature simplify_writable_program_account_check
|
|
|
|
let loaded_accounts =
|
|
|
|
load_accounts_with_excluded_features(tx, &accounts, &mut error_counters, None);
|
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_writable_account, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
|
2021-09-08 14:21:52 -07:00
|
|
|
// Solution 1: include bpf_loader_upgradeable account
|
|
|
|
message.account_keys = vec![key0, key1, bpf_loader_upgradeable::id()];
|
|
|
|
let tx = Transaction::new(&[&keypair], message.clone(), Hash::default());
|
2023-03-28 11:50:49 -07:00
|
|
|
let loaded_accounts = load_accounts_with_excluded_features(
|
|
|
|
tx,
|
|
|
|
&accounts,
|
|
|
|
&mut error_counters,
|
|
|
|
Some(&[simplify_writable_program_account_check::id()]),
|
|
|
|
);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_writable_account, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
let result = loaded_accounts[0].0.as_ref().unwrap();
|
|
|
|
assert_eq!(result.accounts[..2], accounts[..2]);
|
2022-09-06 02:31:40 -07:00
|
|
|
assert_eq!(
|
|
|
|
result.accounts[result.program_indices[0][0] as usize],
|
|
|
|
accounts[5]
|
|
|
|
);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
// Solution 2: mark programdata as readonly
|
|
|
|
message.account_keys = vec![key0, key1, key2]; // revert key change
|
|
|
|
message.header.num_readonly_unsigned_accounts = 2; // mark both executables as readonly
|
|
|
|
let tx = Transaction::new(&[&keypair], message, Hash::default());
|
2023-03-28 11:50:49 -07:00
|
|
|
let loaded_accounts = load_accounts_with_excluded_features(
|
|
|
|
tx,
|
|
|
|
&accounts,
|
|
|
|
&mut error_counters,
|
|
|
|
Some(&[simplify_writable_program_account_check::id()]),
|
|
|
|
);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_writable_account, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
let result = loaded_accounts[0].0.as_ref().unwrap();
|
|
|
|
assert_eq!(result.accounts[..2], accounts[..2]);
|
2022-09-06 02:31:40 -07:00
|
|
|
assert_eq!(
|
|
|
|
result.accounts[result.program_indices[0][0] as usize],
|
|
|
|
accounts[5]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
result.accounts[result.program_indices[0][1] as usize],
|
|
|
|
accounts[3]
|
|
|
|
);
|
2021-09-08 14:21:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_programdata_with_write_lock() {
|
2021-12-27 09:49:32 -08:00
|
|
|
let mut accounts: Vec<TransactionAccount> = Vec::new();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
2023-01-21 10:06:27 -08:00
|
|
|
let key1 = Pubkey::from([5u8; 32]);
|
|
|
|
let key2 = Pubkey::from([6u8; 32]);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let program_data = UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: 42,
|
|
|
|
upgrade_authority_address: None,
|
|
|
|
};
|
|
|
|
let mut account =
|
|
|
|
AccountSharedData::new_data(40, &program_data, &bpf_loader_upgradeable::id()).unwrap();
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((key1, account));
|
|
|
|
|
|
|
|
let mut account = AccountSharedData::new(40, 1, &native_loader::id());
|
|
|
|
account.set_executable(true);
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
accounts.push((key2, account));
|
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let mut message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
1, // only the program marked as readonly
|
|
|
|
vec![key0, key1, key2],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let tx = Transaction::new(&[&keypair], message.clone(), Hash::default());
|
2023-03-28 11:50:49 -07:00
|
|
|
let loaded_accounts = load_accounts_with_excluded_features(
|
|
|
|
tx.clone(),
|
|
|
|
&accounts,
|
|
|
|
&mut error_counters,
|
|
|
|
Some(&[simplify_writable_program_account_check::id()]),
|
|
|
|
);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_writable_account, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0],
|
|
|
|
(Err(TransactionError::InvalidWritableAccount), None)
|
|
|
|
);
|
|
|
|
|
2023-03-28 11:50:49 -07:00
|
|
|
// Solution 0: Include feature simplify_writable_program_account_check
|
|
|
|
let loaded_accounts =
|
|
|
|
load_accounts_with_excluded_features(tx, &accounts, &mut error_counters, None);
|
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_writable_account, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
|
2021-09-08 14:21:52 -07:00
|
|
|
// Solution 1: include bpf_loader_upgradeable account
|
|
|
|
let mut account = AccountSharedData::new(40, 1, &native_loader::id()); // create mock bpf_loader_upgradeable
|
|
|
|
account.set_executable(true);
|
|
|
|
account.set_rent_epoch(1);
|
|
|
|
let accounts_with_upgradeable_loader = vec![
|
|
|
|
accounts[0].clone(),
|
|
|
|
accounts[1].clone(),
|
|
|
|
(bpf_loader_upgradeable::id(), account),
|
|
|
|
];
|
|
|
|
message.account_keys = vec![key0, key1, bpf_loader_upgradeable::id()];
|
|
|
|
let tx = Transaction::new(&[&keypair], message.clone(), Hash::default());
|
2022-12-08 11:46:37 -08:00
|
|
|
let loaded_accounts = load_accounts_with_excluded_features(
|
|
|
|
tx,
|
|
|
|
&accounts_with_upgradeable_loader,
|
|
|
|
&mut error_counters,
|
2023-03-28 11:50:49 -07:00
|
|
|
Some(&[simplify_writable_program_account_check::id()]),
|
2022-12-08 11:46:37 -08:00
|
|
|
);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_writable_account, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
let result = loaded_accounts[0].0.as_ref().unwrap();
|
|
|
|
assert_eq!(result.accounts[..2], accounts_with_upgradeable_loader[..2]);
|
|
|
|
assert_eq!(
|
2022-09-06 02:31:40 -07:00
|
|
|
result.accounts[result.program_indices[0][0] as usize],
|
2021-09-09 23:36:21 -07:00
|
|
|
accounts_with_upgradeable_loader[2]
|
2021-09-08 14:21:52 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// Solution 2: mark programdata as readonly
|
|
|
|
message.account_keys = vec![key0, key1, key2]; // revert key change
|
|
|
|
message.header.num_readonly_unsigned_accounts = 2; // extend readonly set to include programdata
|
|
|
|
let tx = Transaction::new(&[&keypair], message, Hash::default());
|
2023-03-28 11:50:49 -07:00
|
|
|
let loaded_accounts = load_accounts_with_excluded_features(
|
|
|
|
tx,
|
|
|
|
&accounts,
|
|
|
|
&mut error_counters,
|
|
|
|
Some(&[simplify_writable_program_account_check::id()]),
|
|
|
|
);
|
2021-09-08 14:21:52 -07:00
|
|
|
|
|
|
|
assert_eq!(error_counters.invalid_writable_account, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
let result = loaded_accounts[0].0.as_ref().unwrap();
|
|
|
|
assert_eq!(result.accounts[..2], accounts[..2]);
|
2022-09-06 02:31:40 -07:00
|
|
|
assert_eq!(
|
|
|
|
result.accounts[result.program_indices[0][0] as usize],
|
|
|
|
accounts[2]
|
|
|
|
);
|
2021-09-08 14:21:52 -07:00
|
|
|
}
|
|
|
|
|
2019-03-07 08:51:56 -08:00
|
|
|
#[test]
|
2023-02-02 13:50:44 -08:00
|
|
|
fn test_accounts_empty_bank_hash_stats() {
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2023-02-02 13:50:44 -08:00
|
|
|
assert!(accounts.accounts_db.get_bank_hash_stats(0).is_some());
|
|
|
|
assert!(accounts.accounts_db.get_bank_hash_stats(1).is_none());
|
2019-11-22 08:56:00 -08:00
|
|
|
}
|
|
|
|
|
2022-01-03 22:25:23 -08:00
|
|
|
#[test]
|
|
|
|
fn test_lock_accounts_with_duplicates() {
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let message = Message {
|
|
|
|
header: MessageHeader {
|
|
|
|
num_required_signatures: 1,
|
|
|
|
..MessageHeader::default()
|
|
|
|
},
|
|
|
|
account_keys: vec![keypair.pubkey(), keypair.pubkey()],
|
|
|
|
..Message::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
let tx = new_sanitized_tx(&[&keypair], message, Hash::default());
|
2022-08-12 07:07:48 -07:00
|
|
|
let results = accounts.lock_accounts([tx].iter(), MAX_TX_ACCOUNT_LOCKS);
|
2022-01-03 22:25:23 -08:00
|
|
|
assert_eq!(results[0], Err(TransactionError::AccountLoadedTwice));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_lock_accounts_with_too_many_accounts() {
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
|
|
|
|
// Allow up to MAX_TX_ACCOUNT_LOCKS
|
|
|
|
{
|
|
|
|
let num_account_keys = MAX_TX_ACCOUNT_LOCKS;
|
|
|
|
let mut account_keys: Vec<_> = (0..num_account_keys)
|
|
|
|
.map(|_| Pubkey::new_unique())
|
|
|
|
.collect();
|
|
|
|
account_keys[0] = keypair.pubkey();
|
|
|
|
let message = Message {
|
|
|
|
header: MessageHeader {
|
|
|
|
num_required_signatures: 1,
|
|
|
|
..MessageHeader::default()
|
|
|
|
},
|
|
|
|
account_keys,
|
|
|
|
..Message::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
let txs = vec![new_sanitized_tx(&[&keypair], message, Hash::default())];
|
2022-08-12 07:07:48 -07:00
|
|
|
let results = accounts.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS);
|
2022-01-03 22:25:23 -08:00
|
|
|
assert_eq!(results[0], Ok(()));
|
|
|
|
accounts.unlock_accounts(txs.iter(), &results);
|
|
|
|
}
|
|
|
|
|
2022-07-06 08:06:03 -07:00
|
|
|
// Disallow over MAX_TX_ACCOUNT_LOCKS
|
2022-01-03 22:25:23 -08:00
|
|
|
{
|
|
|
|
let num_account_keys = MAX_TX_ACCOUNT_LOCKS + 1;
|
|
|
|
let mut account_keys: Vec<_> = (0..num_account_keys)
|
|
|
|
.map(|_| Pubkey::new_unique())
|
|
|
|
.collect();
|
|
|
|
account_keys[0] = keypair.pubkey();
|
|
|
|
let message = Message {
|
|
|
|
header: MessageHeader {
|
|
|
|
num_required_signatures: 1,
|
|
|
|
..MessageHeader::default()
|
|
|
|
},
|
|
|
|
account_keys,
|
|
|
|
..Message::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
let txs = vec![new_sanitized_tx(&[&keypair], message, Hash::default())];
|
2022-08-12 07:07:48 -07:00
|
|
|
let results = accounts.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS);
|
2022-01-03 22:25:23 -08:00
|
|
|
assert_eq!(results[0], Err(TransactionError::TooManyAccountLocks));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-27 14:25:10 -07:00
|
|
|
#[test]
|
|
|
|
fn test_accounts_locks() {
|
|
|
|
let keypair0 = Keypair::new();
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
let keypair3 = Keypair::new();
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let account0 = AccountSharedData::new(1, 0, &Pubkey::default());
|
|
|
|
let account1 = AccountSharedData::new(2, 0, &Pubkey::default());
|
|
|
|
let account2 = AccountSharedData::new(3, 0, &Pubkey::default());
|
|
|
|
let account3 = AccountSharedData::new(4, 0, &Pubkey::default());
|
2019-06-27 14:25:10 -07:00
|
|
|
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2023-01-06 12:07:36 -08:00
|
|
|
accounts.store_for_tests(0, &keypair0.pubkey(), &account0);
|
|
|
|
accounts.store_for_tests(0, &keypair1.pubkey(), &account1);
|
|
|
|
accounts.store_for_tests(0, &keypair2.pubkey(), &account2);
|
|
|
|
accounts.store_for_tests(0, &keypair3.pubkey(), &account3);
|
2019-06-27 14:25:10 -07:00
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair0.pubkey(), keypair1.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
2021-08-17 15:17:56 -07:00
|
|
|
let tx = new_sanitized_tx(&[&keypair0], message, Hash::default());
|
2022-08-12 07:07:48 -07:00
|
|
|
let results0 = accounts.lock_accounts([tx.clone()].iter(), MAX_TX_ACCOUNT_LOCKS);
|
2019-06-27 14:25:10 -07:00
|
|
|
|
|
|
|
assert!(results0[0].is_ok());
|
|
|
|
assert_eq!(
|
|
|
|
*accounts
|
2021-02-22 23:56:43 -08:00
|
|
|
.account_locks
|
|
|
|
.lock()
|
2019-07-11 18:46:49 -07:00
|
|
|
.unwrap()
|
2021-02-22 23:56:43 -08:00
|
|
|
.readonly_locks
|
2019-06-27 14:25:10 -07:00
|
|
|
.get(&keypair1.pubkey())
|
|
|
|
.unwrap(),
|
|
|
|
1
|
|
|
|
);
|
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair2.pubkey(), keypair1.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
2021-08-17 15:17:56 -07:00
|
|
|
let tx0 = new_sanitized_tx(&[&keypair2], message, Hash::default());
|
2019-06-27 14:25:10 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair1.pubkey(), keypair3.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
2021-08-17 15:17:56 -07:00
|
|
|
let tx1 = new_sanitized_tx(&[&keypair1], message, Hash::default());
|
2019-06-27 14:25:10 -07:00
|
|
|
let txs = vec![tx0, tx1];
|
2022-08-12 07:07:48 -07:00
|
|
|
let results1 = accounts.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS);
|
2019-06-27 14:25:10 -07:00
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
assert!(results1[0].is_ok()); // Read-only account (keypair1) can be referenced multiple times
|
|
|
|
assert!(results1[1].is_err()); // Read-only account (keypair1) cannot also be locked as writable
|
2019-06-27 14:25:10 -07:00
|
|
|
assert_eq!(
|
|
|
|
*accounts
|
2021-02-22 23:56:43 -08:00
|
|
|
.account_locks
|
|
|
|
.lock()
|
2019-07-11 18:46:49 -07:00
|
|
|
.unwrap()
|
2021-02-22 23:56:43 -08:00
|
|
|
.readonly_locks
|
2019-06-27 14:25:10 -07:00
|
|
|
.get(&keypair1.pubkey())
|
|
|
|
.unwrap(),
|
|
|
|
2
|
|
|
|
);
|
|
|
|
|
2021-12-16 14:27:22 -08:00
|
|
|
accounts.unlock_accounts([tx].iter(), &results0);
|
|
|
|
accounts.unlock_accounts(txs.iter(), &results1);
|
2019-06-27 14:25:10 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair1.pubkey(), keypair3.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
2021-08-17 15:17:56 -07:00
|
|
|
let tx = new_sanitized_tx(&[&keypair1], message, Hash::default());
|
2022-08-12 07:07:48 -07:00
|
|
|
let results2 = accounts.lock_accounts([tx].iter(), MAX_TX_ACCOUNT_LOCKS);
|
2019-11-05 08:38:35 -08:00
|
|
|
assert!(results2[0].is_ok()); // Now keypair1 account can be locked as writable
|
2019-06-27 14:25:10 -07:00
|
|
|
|
2021-02-22 23:56:43 -08:00
|
|
|
// Check that read-only lock with zero references is deleted
|
|
|
|
assert!(accounts
|
|
|
|
.account_locks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.readonly_locks
|
|
|
|
.get(&keypair1.pubkey())
|
|
|
|
.is_none());
|
2019-06-27 14:25:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_accounts_locks_multithreaded() {
|
|
|
|
let counter = Arc::new(AtomicU64::new(0));
|
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
|
|
|
|
|
|
|
let keypair0 = Keypair::new();
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let account0 = AccountSharedData::new(1, 0, &Pubkey::default());
|
|
|
|
let account1 = AccountSharedData::new(2, 0, &Pubkey::default());
|
|
|
|
let account2 = AccountSharedData::new(3, 0, &Pubkey::default());
|
2019-06-27 14:25:10 -07:00
|
|
|
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2023-01-06 12:07:36 -08:00
|
|
|
accounts.store_for_tests(0, &keypair0.pubkey(), &account0);
|
|
|
|
accounts.store_for_tests(0, &keypair1.pubkey(), &account1);
|
|
|
|
accounts.store_for_tests(0, &keypair2.pubkey(), &account2);
|
2019-06-27 14:25:10 -07:00
|
|
|
|
|
|
|
let accounts_arc = Arc::new(accounts);
|
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
2019-11-05 08:38:35 -08:00
|
|
|
let readonly_message = Message::new_with_compiled_instructions(
|
2019-06-27 14:25:10 -07:00
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair0.pubkey(), keypair1.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
2021-08-17 15:17:56 -07:00
|
|
|
let readonly_tx = new_sanitized_tx(&[&keypair0], readonly_message, Hash::default());
|
2019-06-27 14:25:10 -07:00
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
2019-11-05 08:38:35 -08:00
|
|
|
let writable_message = Message::new_with_compiled_instructions(
|
2019-06-27 14:25:10 -07:00
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair1.pubkey(), keypair2.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
2021-08-17 15:17:56 -07:00
|
|
|
let writable_tx = new_sanitized_tx(&[&keypair1], writable_message, Hash::default());
|
2019-06-27 14:25:10 -07:00
|
|
|
|
|
|
|
let counter_clone = counter.clone();
|
|
|
|
let accounts_clone = accounts_arc.clone();
|
|
|
|
let exit_clone = exit.clone();
|
|
|
|
thread::spawn(move || {
|
|
|
|
let counter_clone = counter_clone.clone();
|
|
|
|
let exit_clone = exit_clone.clone();
|
|
|
|
loop {
|
2019-11-05 08:38:35 -08:00
|
|
|
let txs = vec![writable_tx.clone()];
|
2022-08-12 07:07:48 -07:00
|
|
|
let results = accounts_clone
|
|
|
|
.clone()
|
|
|
|
.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS);
|
2019-06-27 14:25:10 -07:00
|
|
|
for result in results.iter() {
|
|
|
|
if result.is_ok() {
|
|
|
|
counter_clone.clone().fetch_add(1, Ordering::SeqCst);
|
|
|
|
}
|
|
|
|
}
|
2021-12-16 14:27:22 -08:00
|
|
|
accounts_clone.unlock_accounts(txs.iter(), &results);
|
2019-06-27 14:25:10 -07:00
|
|
|
if exit_clone.clone().load(Ordering::Relaxed) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2020-05-15 09:35:43 -07:00
|
|
|
let counter_clone = counter;
|
2019-06-27 14:25:10 -07:00
|
|
|
for _ in 0..5 {
|
2019-11-05 08:38:35 -08:00
|
|
|
let txs = vec![readonly_tx.clone()];
|
2022-08-12 07:07:48 -07:00
|
|
|
let results = accounts_arc
|
|
|
|
.clone()
|
|
|
|
.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS);
|
2019-06-27 14:25:10 -07:00
|
|
|
if results[0].is_ok() {
|
|
|
|
let counter_value = counter_clone.clone().load(Ordering::SeqCst);
|
|
|
|
thread::sleep(time::Duration::from_millis(50));
|
|
|
|
assert_eq!(counter_value, counter_clone.clone().load(Ordering::SeqCst));
|
|
|
|
}
|
2021-12-16 14:27:22 -08:00
|
|
|
accounts_arc.unlock_accounts(txs.iter(), &results);
|
2019-06-27 14:25:10 -07:00
|
|
|
thread::sleep(time::Duration::from_millis(50));
|
|
|
|
}
|
|
|
|
exit.store(true, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
|
2021-09-03 20:05:30 -07:00
|
|
|
#[test]
|
|
|
|
fn test_demote_program_write_locks() {
|
|
|
|
let keypair0 = Keypair::new();
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
let keypair3 = Keypair::new();
|
|
|
|
|
|
|
|
let account0 = AccountSharedData::new(1, 0, &Pubkey::default());
|
|
|
|
let account1 = AccountSharedData::new(2, 0, &Pubkey::default());
|
|
|
|
let account2 = AccountSharedData::new(3, 0, &Pubkey::default());
|
|
|
|
let account3 = AccountSharedData::new(4, 0, &Pubkey::default());
|
|
|
|
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
2023-01-06 15:30:42 -08:00
|
|
|
accounts.store_for_tests(0, &keypair0.pubkey(), &account0);
|
|
|
|
accounts.store_for_tests(0, &keypair1.pubkey(), &account1);
|
|
|
|
accounts.store_for_tests(0, &keypair2.pubkey(), &account2);
|
|
|
|
accounts.store_for_tests(0, &keypair3.pubkey(), &account3);
|
2021-09-03 20:05:30 -07:00
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
0, // All accounts marked as writable
|
|
|
|
vec![keypair0.pubkey(), keypair1.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let tx = new_sanitized_tx(&[&keypair0], message, Hash::default());
|
2022-08-12 07:07:48 -07:00
|
|
|
let results0 = accounts.lock_accounts([tx].iter(), MAX_TX_ACCOUNT_LOCKS);
|
2021-09-03 20:05:30 -07:00
|
|
|
|
|
|
|
assert!(results0[0].is_ok());
|
|
|
|
// Instruction program-id account demoted to readonly
|
|
|
|
assert_eq!(
|
|
|
|
*accounts
|
|
|
|
.account_locks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.readonly_locks
|
|
|
|
.get(&native_loader::id())
|
|
|
|
.unwrap(),
|
|
|
|
1
|
|
|
|
);
|
|
|
|
// Non-program accounts remain writable
|
|
|
|
assert!(accounts
|
|
|
|
.account_locks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.write_locks
|
|
|
|
.contains(&keypair0.pubkey()));
|
|
|
|
assert!(accounts
|
|
|
|
.account_locks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.write_locks
|
|
|
|
.contains(&keypair1.pubkey()));
|
|
|
|
}
|
|
|
|
|
2023-01-06 12:07:36 -08:00
|
|
|
impl Accounts {
|
2023-01-06 15:30:42 -08:00
|
|
|
/// callers used to call store_uncached. But, this is not allowed anymore.
|
2023-01-06 12:07:36 -08:00
|
|
|
pub fn store_for_tests(&self, slot: Slot, pubkey: &Pubkey, account: &AccountSharedData) {
|
|
|
|
self.accounts_db.store_for_tests(slot, &[(pubkey, account)])
|
|
|
|
}
|
2023-01-06 15:30:42 -08:00
|
|
|
|
|
|
|
/// useful to adapt tests written prior to introduction of the write cache
|
|
|
|
/// to use the write cache
|
|
|
|
pub fn add_root_and_flush_write_cache(&self, slot: Slot) {
|
|
|
|
self.add_root(slot);
|
|
|
|
self.accounts_db.flush_accounts_cache_slot_for_tests(slot);
|
|
|
|
}
|
2023-01-06 12:07:36 -08:00
|
|
|
}
|
|
|
|
|
2021-11-23 13:17:55 -08:00
|
|
|
#[test]
|
|
|
|
fn test_accounts_locks_with_results() {
|
|
|
|
let keypair0 = Keypair::new();
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
let keypair3 = Keypair::new();
|
|
|
|
|
|
|
|
let account0 = AccountSharedData::new(1, 0, &Pubkey::default());
|
|
|
|
let account1 = AccountSharedData::new(2, 0, &Pubkey::default());
|
|
|
|
let account2 = AccountSharedData::new(3, 0, &Pubkey::default());
|
|
|
|
let account3 = AccountSharedData::new(4, 0, &Pubkey::default());
|
|
|
|
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
2023-01-06 12:07:36 -08:00
|
|
|
accounts.store_for_tests(0, &keypair0.pubkey(), &account0);
|
|
|
|
accounts.store_for_tests(0, &keypair1.pubkey(), &account1);
|
|
|
|
accounts.store_for_tests(0, &keypair2.pubkey(), &account2);
|
|
|
|
accounts.store_for_tests(0, &keypair3.pubkey(), &account3);
|
2021-11-23 13:17:55 -08:00
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair1.pubkey(), keypair0.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let tx0 = new_sanitized_tx(&[&keypair1], message, Hash::default());
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair2.pubkey(), keypair0.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let tx1 = new_sanitized_tx(&[&keypair2], message, Hash::default());
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair3.pubkey(), keypair0.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let tx2 = new_sanitized_tx(&[&keypair3], message, Hash::default());
|
|
|
|
let txs = vec![tx0, tx1, tx2];
|
|
|
|
|
|
|
|
let qos_results = vec![
|
2021-12-29 23:42:32 -08:00
|
|
|
Ok(()),
|
2021-11-23 13:17:55 -08:00
|
|
|
Err(TransactionError::WouldExceedMaxBlockCostLimit),
|
2021-12-29 23:42:32 -08:00
|
|
|
Ok(()),
|
2021-11-23 13:17:55 -08:00
|
|
|
];
|
|
|
|
|
2022-08-12 07:07:48 -07:00
|
|
|
let results = accounts.lock_accounts_with_results(
|
|
|
|
txs.iter(),
|
|
|
|
qos_results.iter(),
|
|
|
|
MAX_TX_ACCOUNT_LOCKS,
|
|
|
|
);
|
2021-11-23 13:17:55 -08:00
|
|
|
|
|
|
|
assert!(results[0].is_ok()); // Read-only account (keypair0) can be referenced multiple times
|
|
|
|
assert!(results[1].is_err()); // is not locked due to !qos_results[1].is_ok()
|
|
|
|
assert!(results[2].is_ok()); // Read-only account (keypair0) can be referenced multiple times
|
|
|
|
|
|
|
|
// verify that keypair0 read-only lock twice (for tx0 and tx2)
|
|
|
|
assert_eq!(
|
|
|
|
*accounts
|
|
|
|
.account_locks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.readonly_locks
|
|
|
|
.get(&keypair0.pubkey())
|
|
|
|
.unwrap(),
|
|
|
|
2
|
|
|
|
);
|
|
|
|
// verify that keypair2 (for tx1) is not write-locked
|
|
|
|
assert!(accounts
|
|
|
|
.account_locks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.write_locks
|
|
|
|
.get(&keypair2.pubkey())
|
|
|
|
.is_none());
|
|
|
|
|
2021-12-16 14:27:22 -08:00
|
|
|
accounts.unlock_accounts(txs.iter(), &results);
|
2021-11-23 13:17:55 -08:00
|
|
|
|
|
|
|
// check all locks to be removed
|
|
|
|
assert!(accounts
|
|
|
|
.account_locks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.readonly_locks
|
|
|
|
.is_empty());
|
|
|
|
assert!(accounts
|
|
|
|
.account_locks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.write_locks
|
|
|
|
.is_empty());
|
|
|
|
}
|
|
|
|
|
2019-06-10 19:50:02 -07:00
|
|
|
#[test]
|
2019-08-19 13:00:37 -07:00
|
|
|
fn test_collect_accounts_to_store() {
|
2019-06-10 19:50:02 -07:00
|
|
|
let keypair0 = Keypair::new();
|
|
|
|
let keypair1 = Keypair::new();
|
2020-10-19 12:12:08 -07:00
|
|
|
let pubkey = solana_sdk::pubkey::new_rand();
|
2021-07-05 04:49:37 -07:00
|
|
|
let account0 = AccountSharedData::new(1, 0, &Pubkey::default());
|
|
|
|
let account1 = AccountSharedData::new(2, 0, &Pubkey::default());
|
|
|
|
let account2 = AccountSharedData::new(3, 0, &Pubkey::default());
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2019-11-20 12:27:02 -08:00
|
|
|
let rent_collector = RentCollector::default();
|
|
|
|
|
2019-06-27 14:25:10 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair0.pubkey(), pubkey, native_loader::id()],
|
2019-06-10 19:50:02 -07:00
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
2021-07-05 04:49:37 -07:00
|
|
|
let transaction_accounts0 = vec![
|
|
|
|
(message.account_keys[0], account0),
|
|
|
|
(message.account_keys[1], account2.clone()),
|
|
|
|
];
|
2021-08-17 15:17:56 -07:00
|
|
|
let tx0 = new_sanitized_tx(&[&keypair0], message, Hash::default());
|
2019-06-27 14:25:10 -07:00
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair1.pubkey(), pubkey, native_loader::id()],
|
2019-06-10 19:50:02 -07:00
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
2021-07-05 04:49:37 -07:00
|
|
|
let transaction_accounts1 = vec![
|
|
|
|
(message.account_keys[0], account1),
|
|
|
|
(message.account_keys[1], account2),
|
|
|
|
];
|
2021-08-17 15:17:56 -07:00
|
|
|
let tx1 = new_sanitized_tx(&[&keypair1], message, Hash::default());
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2019-12-07 11:54:10 -08:00
|
|
|
let loaded0 = (
|
2021-01-25 01:35:08 -08:00
|
|
|
Ok(LoadedTransaction {
|
|
|
|
accounts: transaction_accounts0,
|
2021-09-09 23:36:21 -07:00
|
|
|
program_indices: vec![],
|
|
|
|
rent: 0,
|
2021-05-25 22:11:26 -07:00
|
|
|
rent_debits: RentDebits::default(),
|
2021-01-25 01:35:08 -08:00
|
|
|
}),
|
2020-11-29 11:21:55 -08:00
|
|
|
None,
|
2019-12-07 11:54:10 -08:00
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2019-12-07 11:54:10 -08:00
|
|
|
let loaded1 = (
|
2021-01-25 01:35:08 -08:00
|
|
|
Ok(LoadedTransaction {
|
|
|
|
accounts: transaction_accounts1,
|
2021-09-09 23:36:21 -07:00
|
|
|
program_indices: vec![],
|
|
|
|
rent: 0,
|
2021-05-25 22:11:26 -07:00
|
|
|
rent_debits: RentDebits::default(),
|
2021-01-25 01:35:08 -08:00
|
|
|
}),
|
2020-11-29 11:21:55 -08:00
|
|
|
None,
|
2019-12-07 11:54:10 -08:00
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2019-06-27 14:25:10 -07:00
|
|
|
let mut loaded = vec![loaded0, loaded1];
|
|
|
|
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2019-07-11 18:46:49 -07:00
|
|
|
{
|
2021-02-22 23:56:43 -08:00
|
|
|
accounts
|
|
|
|
.account_locks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.insert_new_readonly(&pubkey);
|
2019-07-11 18:46:49 -07:00
|
|
|
}
|
2023-02-09 12:00:33 -08:00
|
|
|
let txs = vec![tx0.clone(), tx1.clone()];
|
2022-01-04 18:15:15 -08:00
|
|
|
let execution_results = vec![new_execution_result(Ok(()), None); 2];
|
2023-02-09 12:00:33 -08:00
|
|
|
let (collected_accounts, transactions) = accounts.collect_accounts_to_store(
|
2021-08-17 15:17:56 -07:00
|
|
|
&txs,
|
2022-01-04 18:15:15 -08:00
|
|
|
&execution_results,
|
2021-01-25 01:35:08 -08:00
|
|
|
loaded.as_mut_slice(),
|
2020-01-10 15:57:31 -08:00
|
|
|
&rent_collector,
|
2022-07-06 05:03:13 -07:00
|
|
|
&DurableNonce::default(),
|
2021-10-22 14:32:40 -07:00
|
|
|
0,
|
2020-01-10 15:57:31 -08:00
|
|
|
);
|
2019-08-19 13:00:37 -07:00
|
|
|
assert_eq!(collected_accounts.len(), 2);
|
|
|
|
assert!(collected_accounts
|
|
|
|
.iter()
|
2020-05-15 09:35:43 -07:00
|
|
|
.any(|(pubkey, _account)| *pubkey == &keypair0.pubkey()));
|
2019-08-19 13:00:37 -07:00
|
|
|
assert!(collected_accounts
|
|
|
|
.iter()
|
2020-05-15 09:35:43 -07:00
|
|
|
.any(|(pubkey, _account)| *pubkey == &keypair1.pubkey()));
|
2019-08-19 13:00:37 -07:00
|
|
|
|
2023-02-09 12:00:33 -08:00
|
|
|
assert_eq!(transactions.len(), 2);
|
|
|
|
assert!(transactions.iter().any(|txn| txn.unwrap().eq(&tx0)));
|
|
|
|
assert!(transactions.iter().any(|txn| txn.unwrap().eq(&tx1)));
|
2022-06-10 10:24:35 -07:00
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
// Ensure readonly_lock reflects lock
|
2019-06-27 14:25:10 -07:00
|
|
|
assert_eq!(
|
2021-02-22 23:56:43 -08:00
|
|
|
*accounts
|
|
|
|
.account_locks
|
2019-11-05 08:38:35 -08:00
|
|
|
.lock()
|
2021-02-22 23:56:43 -08:00
|
|
|
.unwrap()
|
|
|
|
.readonly_locks
|
|
|
|
.get(&pubkey)
|
2019-11-05 08:38:35 -08:00
|
|
|
.unwrap(),
|
|
|
|
1
|
2019-06-27 14:25:10 -07:00
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
}
|
2020-01-22 09:11:56 -08:00
|
|
|
|
2020-07-15 06:49:22 -07:00
|
|
|
#[test]
|
|
|
|
fn huge_clean() {
|
|
|
|
solana_logger::setup();
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2020-07-15 06:49:22 -07:00
|
|
|
let mut old_pubkey = Pubkey::default();
|
2021-04-23 07:35:09 -07:00
|
|
|
let zero_account = AccountSharedData::new(0, 0, AccountSharedData::default().owner());
|
2020-07-15 06:49:22 -07:00
|
|
|
info!("storing..");
|
|
|
|
for i in 0..2_000 {
|
2020-10-19 12:12:08 -07:00
|
|
|
let pubkey = solana_sdk::pubkey::new_rand();
|
2022-11-09 11:39:38 -08:00
|
|
|
let account = AccountSharedData::new(i + 1, 0, AccountSharedData::default().owner());
|
2023-01-06 15:30:42 -08:00
|
|
|
accounts.store_for_tests(i, &pubkey, &account);
|
|
|
|
accounts.store_for_tests(i, &old_pubkey, &zero_account);
|
2020-07-15 06:49:22 -07:00
|
|
|
old_pubkey = pubkey;
|
2023-01-06 15:30:42 -08:00
|
|
|
accounts.add_root_and_flush_write_cache(i);
|
|
|
|
|
2020-07-15 06:49:22 -07:00
|
|
|
if i % 1_000 == 0 {
|
|
|
|
info!(" store {}", i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
info!("done..cleaning..");
|
2022-08-17 15:45:59 -07:00
|
|
|
accounts.accounts_db.clean_accounts_for_tests();
|
2020-07-15 06:49:22 -07:00
|
|
|
}
|
2020-09-19 12:17:46 -07:00
|
|
|
|
2022-04-22 03:32:31 -07:00
|
|
|
fn load_accounts_no_store(
|
|
|
|
accounts: &Accounts,
|
|
|
|
tx: Transaction,
|
|
|
|
account_overrides: Option<&AccountOverrides>,
|
|
|
|
) -> Vec<TransactionLoadResult> {
|
2021-10-27 10:09:16 -07:00
|
|
|
let tx = SanitizedTransaction::from_transaction_for_tests(tx);
|
2020-09-19 12:17:46 -07:00
|
|
|
let rent_collector = RentCollector::default();
|
|
|
|
let mut hash_queue = BlockhashQueue::new(100);
|
2021-10-22 14:32:40 -07:00
|
|
|
hash_queue.register_hash(tx.message().recent_blockhash(), 10);
|
2020-09-19 12:17:46 -07:00
|
|
|
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
2022-04-23 16:10:47 -07:00
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
2020-09-19 12:17:46 -07:00
|
|
|
accounts.load_accounts(
|
|
|
|
&ancestors,
|
2021-08-17 15:17:56 -07:00
|
|
|
&[tx],
|
2021-12-29 23:42:32 -08:00
|
|
|
vec![(Ok(()), None)],
|
2020-09-19 12:17:46 -07:00
|
|
|
&hash_queue,
|
|
|
|
&mut error_counters,
|
|
|
|
&rent_collector,
|
2020-09-29 20:18:28 -07:00
|
|
|
&FeatureSet::all_enabled(),
|
2022-02-11 16:23:16 -08:00
|
|
|
&FeeStructure::default(),
|
2022-04-22 03:32:31 -07:00
|
|
|
account_overrides,
|
2023-03-28 11:49:56 -07:00
|
|
|
&HashMap::new(),
|
|
|
|
&HashMap::new(),
|
2020-09-19 12:17:46 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_instructions() {
|
|
|
|
solana_logger::setup();
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2020-09-19 12:17:46 -07:00
|
|
|
|
|
|
|
let instructions_key = solana_sdk::sysvar::instructions::id();
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let instructions = vec![CompiledInstruction::new(1, &(), vec![0, 1])];
|
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
|
|
|
&[&keypair],
|
2020-10-19 12:12:08 -07:00
|
|
|
&[solana_sdk::pubkey::new_rand(), instructions_key],
|
2020-09-19 12:17:46 -07:00
|
|
|
Hash::default(),
|
|
|
|
vec![native_loader::id()],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
2022-04-22 03:32:31 -07:00
|
|
|
let loaded_accounts = load_accounts_no_store(&accounts, tx, None);
|
2020-09-19 12:17:46 -07:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
assert!(loaded_accounts[0].0.is_err());
|
|
|
|
}
|
2020-09-29 20:34:07 -07:00
|
|
|
|
2022-04-22 03:32:31 -07:00
|
|
|
#[test]
|
|
|
|
fn test_overrides() {
|
|
|
|
solana_logger::setup();
|
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
|
|
|
AccountShrinkThreshold::default(),
|
|
|
|
);
|
|
|
|
let mut account_overrides = AccountOverrides::default();
|
|
|
|
let slot_history_id = sysvar::slot_history::id();
|
|
|
|
let account = AccountSharedData::new(42, 0, &Pubkey::default());
|
|
|
|
account_overrides.set_slot_history(Some(account));
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let account = AccountSharedData::new(1_000_000, 0, &Pubkey::default());
|
|
|
|
accounts.store_slow_uncached(0, &keypair.pubkey(), &account);
|
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0])];
|
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[slot_history_id],
|
|
|
|
Hash::default(),
|
|
|
|
vec![native_loader::id()],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
|
|
|
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());
|
|
|
|
assert_eq!(loaded_transaction.accounts[1].0, slot_history_id);
|
|
|
|
assert_eq!(loaded_transaction.accounts[1].1.lamports(), 42);
|
|
|
|
}
|
|
|
|
|
2020-11-24 23:53:51 -08:00
|
|
|
fn create_accounts_prepare_if_nonce_account() -> (
|
|
|
|
Pubkey,
|
2021-03-09 13:06:07 -08:00
|
|
|
AccountSharedData,
|
|
|
|
AccountSharedData,
|
2022-07-06 05:03:13 -07:00
|
|
|
DurableNonce,
|
2021-10-22 14:32:40 -07:00
|
|
|
u64,
|
2021-03-09 13:06:07 -08:00
|
|
|
Option<AccountSharedData>,
|
2020-11-24 23:53:51 -08:00
|
|
|
) {
|
2022-07-06 05:03:13 -07:00
|
|
|
let data = NonceVersions::new(NonceState::Initialized(nonce::state::Data::default()));
|
2021-03-09 13:06:07 -08:00
|
|
|
let account = AccountSharedData::new_data(42, &data, &system_program::id()).unwrap();
|
2021-03-11 16:09:04 -08:00
|
|
|
let mut pre_account = account.clone();
|
|
|
|
pre_account.set_lamports(43);
|
2022-07-06 05:03:13 -07:00
|
|
|
let durable_nonce = DurableNonce::from_blockhash(&Hash::new(&[1u8; 32]));
|
2020-09-29 20:34:07 -07:00
|
|
|
(
|
|
|
|
Pubkey::default(),
|
|
|
|
pre_account,
|
|
|
|
account,
|
2022-07-06 05:03:13 -07:00
|
|
|
durable_nonce,
|
2021-10-22 14:32:40 -07:00
|
|
|
1234,
|
2020-11-24 23:53:51 -08:00
|
|
|
None,
|
2020-09-29 20:34:07 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_prepare_if_nonce_account_test(
|
2021-11-18 16:07:17 -08:00
|
|
|
account_address: &Pubkey,
|
2021-03-09 13:06:07 -08:00
|
|
|
account: &mut AccountSharedData,
|
2020-09-29 20:34:07 -07:00
|
|
|
tx_result: &Result<()>,
|
2021-12-02 09:57:05 -08:00
|
|
|
is_fee_payer: bool,
|
|
|
|
maybe_nonce: Option<(&NonceFull, bool)>,
|
2022-07-06 05:03:13 -07:00
|
|
|
durable_nonce: &DurableNonce,
|
2021-10-22 14:32:40 -07:00
|
|
|
lamports_per_signature: u64,
|
2021-03-09 13:06:07 -08:00
|
|
|
expect_account: &AccountSharedData,
|
2020-09-29 20:34:07 -07:00
|
|
|
) -> bool {
|
|
|
|
// Verify expect_account's relationship
|
2021-12-02 09:57:05 -08:00
|
|
|
if !is_fee_payer {
|
|
|
|
match maybe_nonce {
|
|
|
|
Some((nonce, _)) if nonce.address() == account_address => {
|
|
|
|
assert_ne!(expect_account, nonce.account())
|
|
|
|
}
|
|
|
|
_ => assert_eq!(expect_account, account),
|
2020-09-29 20:34:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
prepare_if_nonce_account(
|
2021-11-18 16:07:17 -08:00
|
|
|
account_address,
|
2020-09-29 20:34:07 -07:00
|
|
|
account,
|
|
|
|
tx_result,
|
2021-12-02 09:57:05 -08:00
|
|
|
is_fee_payer,
|
2021-11-18 16:07:17 -08:00
|
|
|
maybe_nonce,
|
2022-06-03 07:36:27 -07:00
|
|
|
durable_nonce,
|
2021-10-22 14:32:40 -07:00
|
|
|
lamports_per_signature,
|
2020-09-29 20:34:07 -07:00
|
|
|
);
|
2021-12-02 09:57:05 -08:00
|
|
|
assert_eq!(expect_account, account);
|
2020-09-29 20:34:07 -07:00
|
|
|
expect_account == account
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_prepare_if_nonce_account_expected() {
|
|
|
|
let (
|
2021-11-18 16:07:17 -08:00
|
|
|
pre_account_address,
|
2020-09-29 20:34:07 -07:00
|
|
|
pre_account,
|
|
|
|
mut post_account,
|
2021-10-22 14:32:40 -07:00
|
|
|
blockhash,
|
|
|
|
lamports_per_signature,
|
2021-11-18 16:07:17 -08:00
|
|
|
maybe_fee_payer_account,
|
2020-09-29 20:34:07 -07:00
|
|
|
) = create_accounts_prepare_if_nonce_account();
|
2021-11-18 16:07:17 -08:00
|
|
|
let post_account_address = pre_account_address;
|
2021-12-02 09:57:05 -08:00
|
|
|
let nonce = NonceFull::new(
|
|
|
|
pre_account_address,
|
|
|
|
pre_account.clone(),
|
|
|
|
maybe_fee_payer_account,
|
|
|
|
);
|
2020-09-29 20:34:07 -07:00
|
|
|
|
2021-12-02 09:57:05 -08:00
|
|
|
let mut expect_account = pre_account;
|
|
|
|
expect_account
|
2022-07-06 05:03:13 -07:00
|
|
|
.set_state(&NonceVersions::new(NonceState::Initialized(
|
|
|
|
nonce::state::Data::new(Pubkey::default(), blockhash, lamports_per_signature),
|
|
|
|
)))
|
2021-12-02 09:57:05 -08:00
|
|
|
.unwrap();
|
2020-09-29 20:34:07 -07:00
|
|
|
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
2021-11-18 16:07:17 -08:00
|
|
|
&post_account_address,
|
2020-09-29 20:34:07 -07:00
|
|
|
&mut post_account,
|
|
|
|
&Ok(()),
|
2021-12-02 09:57:05 -08:00
|
|
|
false,
|
|
|
|
Some((&nonce, true)),
|
2021-10-22 14:32:40 -07:00
|
|
|
&blockhash,
|
|
|
|
lamports_per_signature,
|
2020-09-29 20:34:07 -07:00
|
|
|
&expect_account,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_prepare_if_nonce_account_not_nonce_tx() {
|
2020-11-24 23:53:51 -08:00
|
|
|
let (
|
2021-11-18 16:07:17 -08:00
|
|
|
pre_account_address,
|
2020-11-24 23:53:51 -08:00
|
|
|
_pre_account,
|
|
|
|
_post_account,
|
2021-10-22 14:32:40 -07:00
|
|
|
blockhash,
|
|
|
|
lamports_per_signature,
|
2021-11-18 16:07:17 -08:00
|
|
|
_maybe_fee_payer_account,
|
2020-11-24 23:53:51 -08:00
|
|
|
) = create_accounts_prepare_if_nonce_account();
|
2021-11-18 16:07:17 -08:00
|
|
|
let post_account_address = pre_account_address;
|
2020-09-29 20:34:07 -07:00
|
|
|
|
2021-03-09 13:06:07 -08:00
|
|
|
let mut post_account = AccountSharedData::default();
|
2020-09-29 20:34:07 -07:00
|
|
|
let expect_account = post_account.clone();
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
2021-11-18 16:07:17 -08:00
|
|
|
&post_account_address,
|
2020-09-29 20:34:07 -07:00
|
|
|
&mut post_account,
|
|
|
|
&Ok(()),
|
2021-12-02 09:57:05 -08:00
|
|
|
false,
|
2020-09-29 20:34:07 -07:00
|
|
|
None,
|
2021-10-22 14:32:40 -07:00
|
|
|
&blockhash,
|
|
|
|
lamports_per_signature,
|
2020-09-29 20:34:07 -07:00
|
|
|
&expect_account,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-11-18 16:07:17 -08:00
|
|
|
fn test_prepare_if_nonce_account_not_nonce_address() {
|
2020-09-29 20:34:07 -07:00
|
|
|
let (
|
2021-11-18 16:07:17 -08:00
|
|
|
pre_account_address,
|
2020-09-29 20:34:07 -07:00
|
|
|
pre_account,
|
|
|
|
mut post_account,
|
2021-10-22 14:32:40 -07:00
|
|
|
blockhash,
|
|
|
|
lamports_per_signature,
|
2021-11-18 16:07:17 -08:00
|
|
|
maybe_fee_payer_account,
|
2020-09-29 20:34:07 -07:00
|
|
|
) = create_accounts_prepare_if_nonce_account();
|
|
|
|
|
2021-12-02 09:57:05 -08:00
|
|
|
let nonce = NonceFull::new(pre_account_address, pre_account, maybe_fee_payer_account);
|
|
|
|
|
2020-09-29 20:34:07 -07:00
|
|
|
let expect_account = post_account.clone();
|
|
|
|
// Wrong key
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
2023-01-21 10:06:27 -08:00
|
|
|
&Pubkey::from([1u8; 32]),
|
2021-11-18 16:07:17 -08:00
|
|
|
&mut post_account,
|
2020-09-29 20:34:07 -07:00
|
|
|
&Ok(()),
|
2021-12-02 09:57:05 -08:00
|
|
|
false,
|
|
|
|
Some((&nonce, true)),
|
2021-10-22 14:32:40 -07:00
|
|
|
&blockhash,
|
|
|
|
lamports_per_signature,
|
2020-09-29 20:34:07 -07:00
|
|
|
&expect_account,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_prepare_if_nonce_account_tx_error() {
|
|
|
|
let (
|
2021-11-18 16:07:17 -08:00
|
|
|
pre_account_address,
|
2020-09-29 20:34:07 -07:00
|
|
|
pre_account,
|
|
|
|
mut post_account,
|
2021-10-22 14:32:40 -07:00
|
|
|
blockhash,
|
|
|
|
lamports_per_signature,
|
2021-11-18 16:07:17 -08:00
|
|
|
maybe_fee_payer_account,
|
2020-09-29 20:34:07 -07:00
|
|
|
) = create_accounts_prepare_if_nonce_account();
|
2021-11-18 16:07:17 -08:00
|
|
|
let post_account_address = pre_account_address;
|
2020-09-29 20:34:07 -07:00
|
|
|
let mut expect_account = pre_account.clone();
|
2021-12-02 09:57:05 -08:00
|
|
|
|
|
|
|
let nonce = NonceFull::new(pre_account_address, pre_account, maybe_fee_payer_account);
|
|
|
|
|
2020-09-29 20:34:07 -07:00
|
|
|
expect_account
|
2022-07-06 05:03:13 -07:00
|
|
|
.set_state(&NonceVersions::new(NonceState::Initialized(
|
|
|
|
nonce::state::Data::new(Pubkey::default(), blockhash, lamports_per_signature),
|
|
|
|
)))
|
2020-09-29 20:34:07 -07:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
2021-11-18 16:07:17 -08:00
|
|
|
&post_account_address,
|
2020-09-29 20:34:07 -07:00
|
|
|
&mut post_account,
|
|
|
|
&Err(TransactionError::InstructionError(
|
|
|
|
0,
|
|
|
|
InstructionError::InvalidArgument,
|
|
|
|
)),
|
2021-12-02 09:57:05 -08:00
|
|
|
false,
|
|
|
|
Some((&nonce, true)),
|
2021-10-22 14:32:40 -07:00
|
|
|
&blockhash,
|
|
|
|
lamports_per_signature,
|
2020-09-29 20:34:07 -07:00
|
|
|
&expect_account,
|
|
|
|
));
|
|
|
|
}
|
2020-11-24 23:53:51 -08:00
|
|
|
|
2021-12-02 09:57:05 -08:00
|
|
|
#[test]
|
|
|
|
fn test_rollback_nonce_fee_payer() {
|
|
|
|
let nonce_account = AccountSharedData::new_data(1, &(), &system_program::id()).unwrap();
|
|
|
|
let pre_fee_payer_account =
|
|
|
|
AccountSharedData::new_data(42, &(), &system_program::id()).unwrap();
|
|
|
|
let mut post_fee_payer_account =
|
|
|
|
AccountSharedData::new_data(84, &[1, 2, 3, 4], &system_program::id()).unwrap();
|
|
|
|
let nonce = NonceFull::new(
|
|
|
|
Pubkey::new_unique(),
|
|
|
|
nonce_account,
|
|
|
|
Some(pre_fee_payer_account.clone()),
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
|
|
|
&Pubkey::new_unique(),
|
|
|
|
&mut post_fee_payer_account.clone(),
|
|
|
|
&Err(TransactionError::InstructionError(
|
|
|
|
0,
|
|
|
|
InstructionError::InvalidArgument,
|
|
|
|
)),
|
|
|
|
false,
|
|
|
|
Some((&nonce, true)),
|
2022-07-06 05:03:13 -07:00
|
|
|
&DurableNonce::default(),
|
2021-12-02 09:57:05 -08:00
|
|
|
1,
|
2023-01-05 10:42:19 -08:00
|
|
|
&post_fee_payer_account,
|
2021-12-02 09:57:05 -08:00
|
|
|
));
|
|
|
|
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
|
|
|
&Pubkey::new_unique(),
|
|
|
|
&mut post_fee_payer_account.clone(),
|
|
|
|
&Ok(()),
|
|
|
|
true,
|
|
|
|
Some((&nonce, true)),
|
2022-07-06 05:03:13 -07:00
|
|
|
&DurableNonce::default(),
|
2021-12-02 09:57:05 -08:00
|
|
|
1,
|
2023-01-05 10:42:19 -08:00
|
|
|
&post_fee_payer_account,
|
2021-12-02 09:57:05 -08:00
|
|
|
));
|
|
|
|
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
|
|
|
&Pubkey::new_unique(),
|
|
|
|
&mut post_fee_payer_account.clone(),
|
|
|
|
&Err(TransactionError::InstructionError(
|
|
|
|
0,
|
|
|
|
InstructionError::InvalidArgument,
|
|
|
|
)),
|
|
|
|
true,
|
|
|
|
None,
|
2022-07-06 05:03:13 -07:00
|
|
|
&DurableNonce::default(),
|
2021-12-02 09:57:05 -08:00
|
|
|
1,
|
2023-01-05 10:42:19 -08:00
|
|
|
&post_fee_payer_account,
|
2021-12-02 09:57:05 -08:00
|
|
|
));
|
|
|
|
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
|
|
|
&Pubkey::new_unique(),
|
|
|
|
&mut post_fee_payer_account,
|
|
|
|
&Err(TransactionError::InstructionError(
|
|
|
|
0,
|
|
|
|
InstructionError::InvalidArgument,
|
|
|
|
)),
|
|
|
|
true,
|
|
|
|
Some((&nonce, true)),
|
2022-07-06 05:03:13 -07:00
|
|
|
&DurableNonce::default(),
|
2021-12-02 09:57:05 -08:00
|
|
|
1,
|
|
|
|
&pre_fee_payer_account,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2020-11-24 23:53:51 -08:00
|
|
|
#[test]
|
|
|
|
fn test_nonced_failure_accounts_rollback_from_pays() {
|
|
|
|
let rent_collector = RentCollector::default();
|
|
|
|
|
|
|
|
let nonce_address = Pubkey::new_unique();
|
|
|
|
let nonce_authority = keypair_from_seed(&[0; 32]).unwrap();
|
|
|
|
let from = keypair_from_seed(&[1; 32]).unwrap();
|
|
|
|
let from_address = from.pubkey();
|
|
|
|
let to_address = Pubkey::new_unique();
|
2022-07-06 05:03:13 -07:00
|
|
|
let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique());
|
|
|
|
let nonce_state = NonceVersions::new(NonceState::Initialized(nonce::state::Data::new(
|
|
|
|
nonce_authority.pubkey(),
|
|
|
|
durable_nonce,
|
|
|
|
0,
|
|
|
|
)));
|
2021-07-05 04:49:37 -07:00
|
|
|
let nonce_account_post =
|
|
|
|
AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
|
|
|
|
let from_account_post = AccountSharedData::new(4199, 0, &Pubkey::default());
|
|
|
|
let to_account = AccountSharedData::new(2, 0, &Pubkey::default());
|
|
|
|
let nonce_authority_account = AccountSharedData::new(3, 0, &Pubkey::default());
|
|
|
|
let recent_blockhashes_sysvar_account = AccountSharedData::new(4, 0, &Pubkey::default());
|
|
|
|
|
2020-11-24 23:53:51 -08:00
|
|
|
let instructions = vec![
|
|
|
|
system_instruction::advance_nonce_account(&nonce_address, &nonce_authority.pubkey()),
|
|
|
|
system_instruction::transfer(&from_address, &to_address, 42),
|
|
|
|
];
|
|
|
|
let message = Message::new(&instructions, Some(&from_address));
|
|
|
|
let blockhash = Hash::new_unique();
|
2021-07-05 04:49:37 -07:00
|
|
|
let transaction_accounts = vec![
|
|
|
|
(message.account_keys[0], from_account_post),
|
|
|
|
(message.account_keys[1], nonce_authority_account),
|
|
|
|
(message.account_keys[2], nonce_account_post),
|
|
|
|
(message.account_keys[3], to_account),
|
|
|
|
(message.account_keys[4], recent_blockhashes_sysvar_account),
|
|
|
|
];
|
2021-08-17 15:17:56 -07:00
|
|
|
let tx = new_sanitized_tx(&[&nonce_authority, &from], message, blockhash);
|
2020-11-24 23:53:51 -08:00
|
|
|
|
2022-07-06 05:03:13 -07:00
|
|
|
let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique());
|
|
|
|
let nonce_state = NonceVersions::new(NonceState::Initialized(nonce::state::Data::new(
|
|
|
|
nonce_authority.pubkey(),
|
|
|
|
durable_nonce,
|
|
|
|
0,
|
|
|
|
)));
|
2021-03-09 13:06:07 -08:00
|
|
|
let nonce_account_pre =
|
|
|
|
AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap();
|
|
|
|
let from_account_pre = AccountSharedData::new(4242, 0, &Pubkey::default());
|
2020-11-24 23:53:51 -08:00
|
|
|
|
2021-11-18 16:07:17 -08:00
|
|
|
let nonce = Some(NonceFull::new(
|
2020-11-24 23:53:51 -08:00
|
|
|
nonce_address,
|
|
|
|
nonce_account_pre.clone(),
|
|
|
|
Some(from_account_pre.clone()),
|
|
|
|
));
|
|
|
|
|
|
|
|
let loaded = (
|
2021-01-25 01:35:08 -08:00
|
|
|
Ok(LoadedTransaction {
|
|
|
|
accounts: transaction_accounts,
|
2021-09-09 23:36:21 -07:00
|
|
|
program_indices: vec![],
|
|
|
|
rent: 0,
|
2021-05-25 22:11:26 -07:00
|
|
|
rent_debits: RentDebits::default(),
|
2021-01-25 01:35:08 -08:00
|
|
|
}),
|
2021-11-18 16:07:17 -08:00
|
|
|
nonce.clone(),
|
2020-11-24 23:53:51 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut loaded = vec![loaded];
|
|
|
|
|
2022-07-06 05:03:13 -07:00
|
|
|
let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique());
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2021-08-17 15:17:56 -07:00
|
|
|
let txs = vec![tx];
|
2022-01-04 18:15:15 -08:00
|
|
|
let execution_results = vec![new_execution_result(
|
2021-09-09 23:36:21 -07:00
|
|
|
Err(TransactionError::InstructionError(
|
|
|
|
1,
|
|
|
|
InstructionError::InvalidArgument,
|
|
|
|
)),
|
2022-01-04 18:15:15 -08:00
|
|
|
nonce.as_ref(),
|
2021-09-09 23:36:21 -07:00
|
|
|
)];
|
2022-06-10 10:24:35 -07:00
|
|
|
let (collected_accounts, _) = accounts.collect_accounts_to_store(
|
2021-08-17 15:17:56 -07:00
|
|
|
&txs,
|
2022-01-04 18:15:15 -08:00
|
|
|
&execution_results,
|
2021-01-25 01:35:08 -08:00
|
|
|
loaded.as_mut_slice(),
|
2020-11-24 23:53:51 -08:00
|
|
|
&rent_collector,
|
2022-07-06 05:03:13 -07:00
|
|
|
&durable_nonce,
|
2021-10-22 14:32:40 -07:00
|
|
|
0,
|
2020-11-24 23:53:51 -08:00
|
|
|
);
|
|
|
|
assert_eq!(collected_accounts.len(), 2);
|
|
|
|
assert_eq!(
|
|
|
|
collected_accounts
|
|
|
|
.iter()
|
|
|
|
.find(|(pubkey, _account)| *pubkey == &from_address)
|
|
|
|
.map(|(_pubkey, account)| *account)
|
|
|
|
.cloned()
|
|
|
|
.unwrap(),
|
|
|
|
from_account_pre,
|
|
|
|
);
|
|
|
|
let collected_nonce_account = collected_accounts
|
|
|
|
.iter()
|
|
|
|
.find(|(pubkey, _account)| *pubkey == &nonce_address)
|
|
|
|
.map(|(_pubkey, account)| *account)
|
|
|
|
.cloned()
|
|
|
|
.unwrap();
|
2021-05-03 08:45:54 -07:00
|
|
|
assert_eq!(
|
|
|
|
collected_nonce_account.lamports(),
|
|
|
|
nonce_account_pre.lamports(),
|
|
|
|
);
|
2022-06-09 08:28:37 -07:00
|
|
|
assert_matches!(
|
2022-07-06 05:03:13 -07:00
|
|
|
nonce_account::verify_nonce_account(&collected_nonce_account, durable_nonce.as_hash()),
|
2022-06-09 08:28:37 -07:00
|
|
|
Some(_)
|
|
|
|
);
|
2020-11-24 23:53:51 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_nonced_failure_accounts_rollback_nonce_pays() {
|
|
|
|
let rent_collector = RentCollector::default();
|
|
|
|
|
|
|
|
let nonce_authority = keypair_from_seed(&[0; 32]).unwrap();
|
|
|
|
let nonce_address = nonce_authority.pubkey();
|
|
|
|
let from = keypair_from_seed(&[1; 32]).unwrap();
|
|
|
|
let from_address = from.pubkey();
|
|
|
|
let to_address = Pubkey::new_unique();
|
2022-07-06 05:03:13 -07:00
|
|
|
let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique());
|
|
|
|
let nonce_state = NonceVersions::new(NonceState::Initialized(nonce::state::Data::new(
|
|
|
|
nonce_authority.pubkey(),
|
|
|
|
durable_nonce,
|
|
|
|
0,
|
|
|
|
)));
|
2021-07-05 04:49:37 -07:00
|
|
|
let nonce_account_post =
|
|
|
|
AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
|
|
|
|
let from_account_post = AccountSharedData::new(4200, 0, &Pubkey::default());
|
|
|
|
let to_account = AccountSharedData::new(2, 0, &Pubkey::default());
|
|
|
|
let nonce_authority_account = AccountSharedData::new(3, 0, &Pubkey::default());
|
|
|
|
let recent_blockhashes_sysvar_account = AccountSharedData::new(4, 0, &Pubkey::default());
|
|
|
|
|
2020-11-24 23:53:51 -08:00
|
|
|
let instructions = vec![
|
|
|
|
system_instruction::advance_nonce_account(&nonce_address, &nonce_authority.pubkey()),
|
|
|
|
system_instruction::transfer(&from_address, &to_address, 42),
|
|
|
|
];
|
|
|
|
let message = Message::new(&instructions, Some(&nonce_address));
|
|
|
|
let blockhash = Hash::new_unique();
|
2021-07-05 04:49:37 -07:00
|
|
|
let transaction_accounts = vec![
|
|
|
|
(message.account_keys[0], from_account_post),
|
|
|
|
(message.account_keys[1], nonce_authority_account),
|
|
|
|
(message.account_keys[2], nonce_account_post),
|
|
|
|
(message.account_keys[3], to_account),
|
|
|
|
(message.account_keys[4], recent_blockhashes_sysvar_account),
|
|
|
|
];
|
2021-08-17 15:17:56 -07:00
|
|
|
let tx = new_sanitized_tx(&[&nonce_authority, &from], message, blockhash);
|
2020-11-24 23:53:51 -08:00
|
|
|
|
2022-07-06 05:03:13 -07:00
|
|
|
let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique());
|
|
|
|
let nonce_state = NonceVersions::new(NonceState::Initialized(nonce::state::Data::new(
|
|
|
|
nonce_authority.pubkey(),
|
|
|
|
durable_nonce,
|
|
|
|
0,
|
|
|
|
)));
|
2021-03-09 13:06:07 -08:00
|
|
|
let nonce_account_pre =
|
|
|
|
AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap();
|
2020-11-24 23:53:51 -08:00
|
|
|
|
2021-11-18 16:07:17 -08:00
|
|
|
let nonce = Some(NonceFull::new(
|
2020-11-24 23:53:51 -08:00
|
|
|
nonce_address,
|
|
|
|
nonce_account_pre.clone(),
|
|
|
|
None,
|
|
|
|
));
|
|
|
|
|
|
|
|
let loaded = (
|
2021-01-25 01:35:08 -08:00
|
|
|
Ok(LoadedTransaction {
|
|
|
|
accounts: transaction_accounts,
|
2021-09-09 23:36:21 -07:00
|
|
|
program_indices: vec![],
|
|
|
|
rent: 0,
|
2021-05-25 22:11:26 -07:00
|
|
|
rent_debits: RentDebits::default(),
|
2021-01-25 01:35:08 -08:00
|
|
|
}),
|
2021-11-18 16:07:17 -08:00
|
|
|
nonce.clone(),
|
2020-11-24 23:53:51 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut loaded = vec![loaded];
|
|
|
|
|
2022-07-06 05:03:13 -07:00
|
|
|
let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique());
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2021-08-17 15:17:56 -07:00
|
|
|
let txs = vec![tx];
|
2022-01-04 18:15:15 -08:00
|
|
|
let execution_results = vec![new_execution_result(
|
2021-09-09 23:36:21 -07:00
|
|
|
Err(TransactionError::InstructionError(
|
|
|
|
1,
|
|
|
|
InstructionError::InvalidArgument,
|
|
|
|
)),
|
2022-01-04 18:15:15 -08:00
|
|
|
nonce.as_ref(),
|
2021-09-09 23:36:21 -07:00
|
|
|
)];
|
2022-06-10 10:24:35 -07:00
|
|
|
let (collected_accounts, _) = accounts.collect_accounts_to_store(
|
2021-08-17 15:17:56 -07:00
|
|
|
&txs,
|
2022-01-04 18:15:15 -08:00
|
|
|
&execution_results,
|
2021-01-25 01:35:08 -08:00
|
|
|
loaded.as_mut_slice(),
|
2020-11-24 23:53:51 -08:00
|
|
|
&rent_collector,
|
2022-07-06 05:03:13 -07:00
|
|
|
&durable_nonce,
|
2021-10-22 14:32:40 -07:00
|
|
|
0,
|
2020-11-24 23:53:51 -08:00
|
|
|
);
|
|
|
|
assert_eq!(collected_accounts.len(), 1);
|
|
|
|
let collected_nonce_account = collected_accounts
|
|
|
|
.iter()
|
|
|
|
.find(|(pubkey, _account)| *pubkey == &nonce_address)
|
|
|
|
.map(|(_pubkey, account)| *account)
|
|
|
|
.cloned()
|
|
|
|
.unwrap();
|
2021-05-03 08:45:54 -07:00
|
|
|
assert_eq!(
|
|
|
|
collected_nonce_account.lamports(),
|
|
|
|
nonce_account_pre.lamports()
|
|
|
|
);
|
2022-06-09 08:28:37 -07:00
|
|
|
assert_matches!(
|
2022-07-06 05:03:13 -07:00
|
|
|
nonce_account::verify_nonce_account(&collected_nonce_account, durable_nonce.as_hash()),
|
2022-06-09 08:28:37 -07:00
|
|
|
Some(_)
|
|
|
|
);
|
2020-11-24 23:53:51 -08:00
|
|
|
}
|
2021-03-10 10:22:02 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_largest_accounts() {
|
2021-08-09 07:01:12 -07:00
|
|
|
let accounts = Accounts::new_with_config_for_tests(
|
2021-05-10 07:22:48 -07:00
|
|
|
Vec::new(),
|
|
|
|
&ClusterType::Development,
|
|
|
|
AccountSecondaryIndexes::default(),
|
2021-06-09 21:21:32 -07:00
|
|
|
AccountShrinkThreshold::default(),
|
2021-05-10 07:22:48 -07:00
|
|
|
);
|
2021-03-10 10:22:02 -08:00
|
|
|
|
2022-07-06 15:35:32 -07:00
|
|
|
/* This test assumes pubkey0 < pubkey1 < pubkey2.
|
|
|
|
* But the keys created with new_unique() does not gurantee this
|
|
|
|
* order because of the endianness. new_unique() calls add 1 at each
|
|
|
|
* key generaration as the little endian integer. A pubkey stores its
|
|
|
|
* value in a 32-byte array bytes, and its eq-partial trait considers
|
|
|
|
* the lower-address bytes more significant, which is the big-endian
|
|
|
|
* order.
|
|
|
|
* So, sort first to ensure the order assumption holds.
|
|
|
|
*/
|
|
|
|
let mut keys = vec![];
|
|
|
|
for _idx in 0..3 {
|
|
|
|
keys.push(Pubkey::new_unique());
|
|
|
|
}
|
|
|
|
keys.sort();
|
|
|
|
let pubkey2 = keys.pop().unwrap();
|
|
|
|
let pubkey1 = keys.pop().unwrap();
|
|
|
|
let pubkey0 = keys.pop().unwrap();
|
2021-03-10 10:22:02 -08:00
|
|
|
let account0 = AccountSharedData::new(42, 0, &Pubkey::default());
|
2023-01-06 12:07:36 -08:00
|
|
|
accounts.store_for_tests(0, &pubkey0, &account0);
|
2021-03-10 10:22:02 -08:00
|
|
|
let account1 = AccountSharedData::new(42, 0, &Pubkey::default());
|
2023-01-06 12:07:36 -08:00
|
|
|
accounts.store_for_tests(0, &pubkey1, &account1);
|
2021-03-10 10:22:02 -08:00
|
|
|
let account2 = AccountSharedData::new(41, 0, &Pubkey::default());
|
2023-01-06 12:07:36 -08:00
|
|
|
accounts.store_for_tests(0, &pubkey2, &account2);
|
2021-03-10 10:22:02 -08:00
|
|
|
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
|
|
|
let all_pubkeys: HashSet<_> = vec![pubkey0, pubkey1, pubkey2].into_iter().collect();
|
|
|
|
|
|
|
|
// num == 0 should always return empty set
|
2021-06-14 21:04:01 -07:00
|
|
|
let bank_id = 0;
|
2021-03-10 10:22:02 -08:00
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
0,
|
|
|
|
&HashSet::new(),
|
|
|
|
AccountAddressFilter::Exclude
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
0,
|
|
|
|
&all_pubkeys,
|
|
|
|
AccountAddressFilter::Include
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![]
|
|
|
|
);
|
|
|
|
|
|
|
|
// list should be sorted by balance, then pubkey, descending
|
|
|
|
assert!(pubkey1 > pubkey0);
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
1,
|
|
|
|
&HashSet::new(),
|
|
|
|
AccountAddressFilter::Exclude
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![(pubkey1, 42)]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
2,
|
|
|
|
&HashSet::new(),
|
|
|
|
AccountAddressFilter::Exclude
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![(pubkey1, 42), (pubkey0, 42)]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
3,
|
|
|
|
&HashSet::new(),
|
|
|
|
AccountAddressFilter::Exclude
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![(pubkey1, 42), (pubkey0, 42), (pubkey2, 41)]
|
|
|
|
);
|
|
|
|
|
|
|
|
// larger num should not affect results
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
6,
|
|
|
|
&HashSet::new(),
|
|
|
|
AccountAddressFilter::Exclude
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![(pubkey1, 42), (pubkey0, 42), (pubkey2, 41)]
|
|
|
|
);
|
|
|
|
|
|
|
|
// AccountAddressFilter::Exclude should exclude entry
|
|
|
|
let exclude1: HashSet<_> = vec![pubkey1].into_iter().collect();
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
1,
|
|
|
|
&exclude1,
|
|
|
|
AccountAddressFilter::Exclude
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![(pubkey0, 42)]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
2,
|
|
|
|
&exclude1,
|
|
|
|
AccountAddressFilter::Exclude
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![(pubkey0, 42), (pubkey2, 41)]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
3,
|
|
|
|
&exclude1,
|
|
|
|
AccountAddressFilter::Exclude
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![(pubkey0, 42), (pubkey2, 41)]
|
|
|
|
);
|
|
|
|
|
|
|
|
// AccountAddressFilter::Include should limit entries
|
|
|
|
let include1_2: HashSet<_> = vec![pubkey1, pubkey2].into_iter().collect();
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
1,
|
|
|
|
&include1_2,
|
|
|
|
AccountAddressFilter::Include
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![(pubkey1, 42)]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
2,
|
|
|
|
&include1_2,
|
|
|
|
AccountAddressFilter::Include
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![(pubkey1, 42), (pubkey2, 41)]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-06-14 21:04:01 -07:00
|
|
|
accounts
|
|
|
|
.load_largest_accounts(
|
|
|
|
&ancestors,
|
|
|
|
bank_id,
|
|
|
|
3,
|
|
|
|
&include1_2,
|
|
|
|
AccountAddressFilter::Include
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2021-03-10 10:22:02 -08:00
|
|
|
vec![(pubkey1, 42), (pubkey2, 41)]
|
|
|
|
);
|
|
|
|
}
|
2021-12-08 12:09:34 -08:00
|
|
|
|
|
|
|
fn zero_len_account_size() -> usize {
|
|
|
|
std::mem::size_of::<AccountSharedData>() + std::mem::size_of::<Pubkey>()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_calc_scan_result_size() {
|
|
|
|
for len in 0..3 {
|
|
|
|
assert_eq!(
|
|
|
|
Accounts::calc_scan_result_size(&AccountSharedData::new(
|
|
|
|
0,
|
|
|
|
len,
|
|
|
|
&Pubkey::default()
|
|
|
|
)),
|
|
|
|
zero_len_account_size() + len
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_maybe_abort_scan() {
|
|
|
|
assert!(Accounts::maybe_abort_scan(ScanResult::Ok(vec![]), &ScanConfig::default()).is_ok());
|
|
|
|
let config = ScanConfig::default().recreate_with_abort();
|
|
|
|
assert!(Accounts::maybe_abort_scan(ScanResult::Ok(vec![]), &config).is_ok());
|
|
|
|
config.abort();
|
|
|
|
assert!(Accounts::maybe_abort_scan(ScanResult::Ok(vec![]), &config).is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_accumulate_and_check_scan_result_size() {
|
|
|
|
for (account, byte_limit_for_scan, result) in [
|
|
|
|
(AccountSharedData::default(), zero_len_account_size(), false),
|
|
|
|
(
|
|
|
|
AccountSharedData::new(0, 1, &Pubkey::default()),
|
|
|
|
zero_len_account_size(),
|
|
|
|
true,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
AccountSharedData::new(0, 2, &Pubkey::default()),
|
|
|
|
zero_len_account_size() + 3,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
] {
|
|
|
|
let sum = AtomicUsize::default();
|
|
|
|
assert_eq!(
|
|
|
|
result,
|
|
|
|
Accounts::accumulate_and_check_scan_result_size(
|
|
|
|
&sum,
|
|
|
|
&account,
|
|
|
|
&Some(byte_limit_for_scan)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
// calling a second time should accumulate above the threshold
|
|
|
|
assert!(Accounts::accumulate_and_check_scan_result_size(
|
|
|
|
&sum,
|
|
|
|
&account,
|
|
|
|
&Some(byte_limit_for_scan)
|
|
|
|
));
|
|
|
|
assert!(!Accounts::accumulate_and_check_scan_result_size(
|
|
|
|
&sum, &account, &None
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
2023-01-31 20:51:35 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_accumulate_and_check_loaded_account_data_size() {
|
|
|
|
let mut error_counter = TransactionErrorMetrics::default();
|
|
|
|
|
|
|
|
// assert check is OK if data limit is not enabled
|
|
|
|
{
|
|
|
|
let mut accumulated_data_size: usize = 0;
|
|
|
|
let data_size = usize::MAX;
|
|
|
|
let requested_data_size_limit = None;
|
|
|
|
|
|
|
|
assert!(Accounts::accumulate_and_check_loaded_account_data_size(
|
|
|
|
&mut accumulated_data_size,
|
|
|
|
data_size,
|
|
|
|
requested_data_size_limit,
|
|
|
|
&mut error_counter
|
|
|
|
)
|
|
|
|
.is_ok());
|
|
|
|
}
|
|
|
|
|
|
|
|
// assert check will fail with correct error if loaded data exceeds limit
|
|
|
|
{
|
|
|
|
let mut accumulated_data_size: usize = 0;
|
|
|
|
let data_size: usize = 123;
|
|
|
|
let requested_data_size_limit = NonZeroUsize::new(data_size);
|
|
|
|
|
|
|
|
// OK - loaded data size is up to limit
|
|
|
|
assert!(Accounts::accumulate_and_check_loaded_account_data_size(
|
|
|
|
&mut accumulated_data_size,
|
|
|
|
data_size,
|
|
|
|
requested_data_size_limit,
|
|
|
|
&mut error_counter
|
|
|
|
)
|
|
|
|
.is_ok());
|
|
|
|
assert_eq!(data_size, accumulated_data_size);
|
|
|
|
|
|
|
|
// fail - loading more data that would exceed limit
|
|
|
|
let another_byte: usize = 1;
|
|
|
|
assert_eq!(
|
|
|
|
Accounts::accumulate_and_check_loaded_account_data_size(
|
|
|
|
&mut accumulated_data_size,
|
|
|
|
another_byte,
|
|
|
|
requested_data_size_limit,
|
|
|
|
&mut error_counter
|
|
|
|
),
|
|
|
|
Err(TransactionError::MaxLoadedAccountsDataSizeExceeded)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2023-03-08 17:41:45 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_get_requested_loaded_accounts_data_size_limit() {
|
|
|
|
// an prrivate helper function
|
|
|
|
fn test(
|
|
|
|
instructions: &[solana_sdk::instruction::Instruction],
|
|
|
|
feature_set: &FeatureSet,
|
|
|
|
expected_result: &Result<Option<NonZeroUsize>>,
|
|
|
|
) {
|
|
|
|
let payer_keypair = Keypair::new();
|
|
|
|
let tx = SanitizedTransaction::from_transaction_for_tests(Transaction::new(
|
|
|
|
&[&payer_keypair],
|
|
|
|
Message::new(instructions, Some(&payer_keypair.pubkey())),
|
|
|
|
Hash::default(),
|
|
|
|
));
|
|
|
|
assert_eq!(
|
|
|
|
*expected_result,
|
|
|
|
Accounts::get_requested_loaded_accounts_data_size_limit(&tx, feature_set)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let tx_not_set_limit = &[solana_sdk::instruction::Instruction::new_with_bincode(
|
|
|
|
Pubkey::new_unique(),
|
|
|
|
&0_u8,
|
|
|
|
vec![],
|
|
|
|
)];
|
|
|
|
let tx_set_limit_99 =
|
|
|
|
&[
|
|
|
|
solana_sdk::compute_budget::ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(99u32),
|
|
|
|
solana_sdk::instruction::Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
|
|
];
|
|
|
|
let tx_set_limit_0 =
|
|
|
|
&[
|
|
|
|
solana_sdk::compute_budget::ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(0u32),
|
|
|
|
solana_sdk::instruction::Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
|
|
|
|
];
|
|
|
|
|
|
|
|
let result_no_limit = Ok(None);
|
|
|
|
let result_default_limit = Ok(Some(
|
|
|
|
NonZeroUsize::new(compute_budget::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES).unwrap(),
|
|
|
|
));
|
|
|
|
let result_requested_limit: Result<Option<NonZeroUsize>> =
|
|
|
|
Ok(Some(NonZeroUsize::new(99).unwrap()));
|
|
|
|
let result_invalid_limit = Err(TransactionError::InvalidLoadedAccountsDataSizeLimit);
|
|
|
|
|
|
|
|
let mut feature_set = FeatureSet::default();
|
|
|
|
|
|
|
|
// if `cap_transaction_accounts_data_size feature` is disable,
|
|
|
|
// the result will always be no limit
|
|
|
|
test(tx_not_set_limit, &feature_set, &result_no_limit);
|
|
|
|
test(tx_set_limit_99, &feature_set, &result_no_limit);
|
|
|
|
test(tx_set_limit_0, &feature_set, &result_no_limit);
|
|
|
|
|
|
|
|
// if `cap_transaction_accounts_data_size` is enabled, and
|
|
|
|
// `add_set_tx_loaded_accounts_data_size_instruction` is disabled,
|
|
|
|
// the result will always be default limit (64MiB)
|
|
|
|
feature_set.activate(&feature_set::cap_transaction_accounts_data_size::id(), 0);
|
|
|
|
test(tx_not_set_limit, &feature_set, &result_default_limit);
|
|
|
|
test(tx_set_limit_99, &feature_set, &result_default_limit);
|
|
|
|
test(tx_set_limit_0, &feature_set, &result_default_limit);
|
|
|
|
|
|
|
|
// if `cap_transaction_accounts_data_size` and
|
|
|
|
// `add_set_tx_loaded_accounts_data_size_instruction` are both enabled,
|
|
|
|
// the results are:
|
|
|
|
// if tx doesn't set limit, then default limit (64MiB)
|
|
|
|
// if tx sets limit, then requested limit
|
|
|
|
// if tx sets limit to zero, then TransactionError::InvalidLoadedAccountsDataSizeLimit
|
|
|
|
feature_set.activate(&add_set_tx_loaded_accounts_data_size_instruction::id(), 0);
|
|
|
|
test(tx_not_set_limit, &feature_set, &result_default_limit);
|
|
|
|
test(tx_set_limit_99, &feature_set, &result_requested_limit);
|
|
|
|
test(tx_set_limit_0, &feature_set, &result_invalid_limit);
|
|
|
|
}
|
2023-04-11 14:22:19 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_too_high_prioritization_fee() {
|
|
|
|
solana_logger::setup();
|
|
|
|
let lamports_per_signature = 5000_u64;
|
|
|
|
let request_units = 1_000_000_u32;
|
|
|
|
let request_unit_price = 2_000_000_000_u64;
|
|
|
|
let prioritization_fee_details = PrioritizationFeeDetails::new(
|
|
|
|
PrioritizationFeeType::ComputeUnitPrice(request_unit_price),
|
|
|
|
request_units as u64,
|
|
|
|
);
|
|
|
|
let prioritization_fee = prioritization_fee_details.get_fee();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
// set up account with balance of `prioritization_fee`
|
|
|
|
let account = AccountSharedData::new(prioritization_fee, 0, &Pubkey::default());
|
|
|
|
let accounts = vec![(key0, account)];
|
|
|
|
|
|
|
|
let instructions = &[
|
|
|
|
ComputeBudgetInstruction::set_compute_unit_limit(request_units),
|
|
|
|
ComputeBudgetInstruction::set_compute_unit_price(request_unit_price),
|
|
|
|
];
|
|
|
|
let tx = Transaction::new(
|
|
|
|
&[&keypair],
|
|
|
|
Message::new(instructions, Some(&key0)),
|
|
|
|
Hash::default(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let fee = Bank::calculate_fee(
|
|
|
|
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
|
|
|
|
lamports_per_signature,
|
|
|
|
&FeeStructure::default(),
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
);
|
|
|
|
assert_eq!(fee, lamports_per_signature + prioritization_fee);
|
|
|
|
|
|
|
|
// assert fail to load account with 2B lamport balance for transaction asking for 2B
|
|
|
|
// lamports as prioritization fee.
|
|
|
|
let mut error_counters = TransactionErrorMetrics::default();
|
|
|
|
let loaded_accounts = load_accounts_with_fee(
|
|
|
|
tx,
|
|
|
|
&accounts,
|
|
|
|
lamports_per_signature,
|
|
|
|
&mut error_counters,
|
|
|
|
None,
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(error_counters.insufficient_funds, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0].clone(),
|
|
|
|
(Err(TransactionError::InsufficientFundsForFee), None),
|
|
|
|
);
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|