2020-01-28 17:03:20 -08:00
|
|
|
use crate::{
|
|
|
|
accounts_db::{
|
|
|
|
AccountInfo, AccountStorage, AccountsDB, AppendVecId, BankHashInfo, ErrorCounters,
|
|
|
|
},
|
2020-04-26 19:07:03 -07:00
|
|
|
accounts_index::{AccountsIndex, Ancestors},
|
2020-01-28 17:03:20 -08:00
|
|
|
append_vec::StoredAccount,
|
|
|
|
bank::{HashAgeKind, TransactionProcessResult},
|
|
|
|
blockhash_queue::BlockhashQueue,
|
2020-09-21 22:36:23 -07:00
|
|
|
feature_set::{self, FeatureSet},
|
2020-01-28 17:03:20 -08:00
|
|
|
rent_collector::RentCollector,
|
|
|
|
system_instruction_processor::{get_system_account_kind, SystemAccountKind},
|
|
|
|
transaction_utils::OrderedIterator,
|
2020-01-14 11:57:29 -08:00
|
|
|
};
|
2019-02-27 20:57:01 -08:00
|
|
|
use log::*;
|
2020-03-17 11:02:07 -07:00
|
|
|
use rand::{thread_rng, Rng};
|
2019-07-16 16:58:30 -07:00
|
|
|
use rayon::slice::ParallelSliceMut;
|
2020-01-28 17:03:20 -08:00
|
|
|
use solana_sdk::{
|
|
|
|
account::Account,
|
2020-09-29 20:34:07 -07:00
|
|
|
account_utils::StateMut,
|
2020-09-15 18:23:21 -07:00
|
|
|
clock::{Epoch, Slot},
|
2020-09-21 22:36:23 -07:00
|
|
|
fee_calculator::{FeeCalculator, FeeConfig},
|
2020-09-08 07:55:09 -07:00
|
|
|
genesis_config::ClusterType,
|
2020-01-28 17:03:20 -08:00
|
|
|
hash::Hash,
|
2020-04-26 00:11:37 -07:00
|
|
|
message::Message,
|
2020-03-03 17:00:39 -08:00
|
|
|
native_loader, nonce,
|
2020-01-28 17:03:20 -08:00
|
|
|
pubkey::Pubkey,
|
|
|
|
transaction::Result,
|
|
|
|
transaction::{Transaction, TransactionError},
|
|
|
|
};
|
|
|
|
use std::{
|
|
|
|
collections::{HashMap, HashSet},
|
2020-05-13 00:22:14 -07:00
|
|
|
ops::RangeBounds,
|
2020-05-22 10:54:24 -07:00
|
|
|
path::PathBuf,
|
2020-01-28 17:03:20 -08:00
|
|
|
sync::{Arc, Mutex, RwLock},
|
|
|
|
};
|
2019-08-28 08:38:32 -07:00
|
|
|
|
2020-07-06 04:22:23 -07:00
|
|
|
#[derive(Default, Debug, AbiExample)]
|
2020-05-22 10:54:24 -07:00
|
|
|
pub(crate) struct ReadonlyLock {
|
2019-06-27 14:25:10 -07:00
|
|
|
lock_count: Mutex<u64>,
|
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
/// This structure handles synchronization for db
|
2020-07-06 04:22:23 -07:00
|
|
|
#[derive(Default, Debug, AbiExample)]
|
2018-12-17 12:41:23 -08:00
|
|
|
pub struct Accounts {
|
2019-10-23 22:01:22 -07:00
|
|
|
/// my slot
|
|
|
|
pub slot: Slot,
|
|
|
|
|
2020-09-15 18:23:21 -07:00
|
|
|
/// my epoch
|
|
|
|
pub epoch: Epoch,
|
|
|
|
|
2019-04-15 17:15:50 -07:00
|
|
|
/// Single global AccountsDB
|
|
|
|
pub accounts_db: Arc<AccountsDB>,
|
2018-12-17 12:41:23 -08:00
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
/// set of writable accounts which are currently in the pipeline
|
2020-05-22 10:54:24 -07:00
|
|
|
pub(crate) account_locks: Mutex<HashSet<Pubkey>>,
|
2018-12-17 12:41:23 -08:00
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
/// Set of read-only accounts which are currently in the pipeline, caching number of locks.
|
2020-05-22 10:54:24 -07:00
|
|
|
pub(crate) readonly_locks: Arc<RwLock<Option<HashMap<Pubkey, ReadonlyLock>>>>,
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2019-08-23 14:04:53 -07:00
|
|
|
// for the load instructions
|
|
|
|
pub type TransactionAccounts = Vec<Account>;
|
2019-11-20 12:27:02 -08:00
|
|
|
pub type TransactionRent = u64;
|
2019-08-23 14:04:53 -07:00
|
|
|
pub type TransactionLoaders = Vec<Vec<(Pubkey, Account)>>;
|
|
|
|
|
2019-11-20 12:27:02 -08:00
|
|
|
pub type TransactionLoadResult = (TransactionAccounts, TransactionLoaders, TransactionRent);
|
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 {
|
2020-09-08 07:55:09 -07:00
|
|
|
pub fn new(paths: Vec<PathBuf>, cluster_type: &ClusterType) -> Self {
|
2020-05-22 10:54:24 -07:00
|
|
|
Self {
|
2020-09-08 07:55:09 -07:00
|
|
|
accounts_db: Arc::new(AccountsDB::new(paths, cluster_type)),
|
2020-05-22 10:54:24 -07:00
|
|
|
account_locks: Mutex::new(HashSet::new()),
|
|
|
|
readonly_locks: Arc::new(RwLock::new(Some(HashMap::new()))),
|
2020-09-21 22:36:23 -07:00
|
|
|
..Self::default()
|
2020-05-22 10:54:24 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-15 18:23:21 -07:00
|
|
|
pub fn new_from_parent(parent: &Accounts, slot: Slot, parent_slot: Slot, epoch: Epoch) -> Self {
|
2019-04-16 13:32:22 -07:00
|
|
|
let accounts_db = parent.accounts_db.clone();
|
2019-09-20 13:21:12 -07:00
|
|
|
accounts_db.set_hash(slot, parent_slot);
|
2020-07-13 07:00:59 -07:00
|
|
|
Self {
|
2019-10-23 22:01:22 -07:00
|
|
|
slot,
|
2020-09-15 18:23:21 -07:00
|
|
|
epoch,
|
2019-04-16 13:32:22 -07:00
|
|
|
accounts_db,
|
2019-05-07 15:51:35 -07:00
|
|
|
account_locks: Mutex::new(HashSet::new()),
|
2019-11-05 08:38:35 -08:00
|
|
|
readonly_locks: Arc::new(RwLock::new(Some(HashMap::new()))),
|
2019-04-16 13:32:22 -07:00
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2018-12-24 16:11:20 -08:00
|
|
|
|
2020-07-13 07:00:59 -07:00
|
|
|
pub(crate) fn new_empty(accounts_db: AccountsDB) -> Self {
|
|
|
|
Self {
|
|
|
|
accounts_db: Arc::new(accounts_db),
|
2020-03-22 11:10:04 -07:00
|
|
|
account_locks: Mutex::new(HashSet::new()),
|
|
|
|
readonly_locks: Arc::new(RwLock::new(Some(HashMap::new()))),
|
2020-09-21 22:36:23 -07:00
|
|
|
..Self::default()
|
2020-07-13 07:00:59 -07:00
|
|
|
}
|
2020-03-22 11:10:04 -07:00
|
|
|
}
|
|
|
|
|
2020-01-22 09:11:56 -08:00
|
|
|
/// Return true if the slice has any duplicate elements
|
|
|
|
pub fn has_duplicates<T: PartialEq>(xs: &[T]) -> bool {
|
|
|
|
// Note: This is an O(n^2) algorithm, but requires no heap allocations. The benchmark
|
|
|
|
// `bench_has_duplicates` in benches/message_processor.rs shows that this implementation is
|
|
|
|
// ~50 times faster than using HashSet for very short slices.
|
|
|
|
for i in 1..xs.len() {
|
|
|
|
if xs[i..].contains(&xs[i - 1]) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2020-09-19 12:17:46 -07:00
|
|
|
fn construct_instructions_account(message: &Message) -> Account {
|
|
|
|
let mut account = Account::default();
|
|
|
|
account.data = message.serialize_instructions();
|
|
|
|
|
|
|
|
// add room for current instruction index.
|
|
|
|
account.data.resize(account.data.len() + 2, 0);
|
|
|
|
account
|
|
|
|
}
|
|
|
|
|
2018-12-24 16:11:20 -08:00
|
|
|
fn load_tx_accounts(
|
2019-10-23 22:01:22 -07:00
|
|
|
&self,
|
2019-04-23 09:56:36 -07:00
|
|
|
storage: &AccountStorage,
|
2020-04-26 19:07:03 -07:00
|
|
|
ancestors: &Ancestors,
|
2019-04-15 17:15:50 -07:00
|
|
|
accounts_index: &AccountsIndex<AccountInfo>,
|
2018-12-17 12:41:23 -08:00
|
|
|
tx: &Transaction,
|
2019-03-29 15:11:21 -07:00
|
|
|
fee: u64,
|
2018-12-17 12:41:23 -08:00
|
|
|
error_counters: &mut ErrorCounters,
|
2019-08-23 14:04:53 -07:00
|
|
|
rent_collector: &RentCollector,
|
2020-09-21 22:36:23 -07:00
|
|
|
feature_set: &FeatureSet,
|
2019-11-20 12:27:02 -08:00
|
|
|
) -> Result<(TransactionAccounts, TransactionRent)> {
|
2018-12-17 12:41:23 -08:00
|
|
|
// Copy all the accounts
|
2019-03-29 09:05:06 -07:00
|
|
|
let message = tx.message();
|
2019-03-29 15:11:21 -07:00
|
|
|
if tx.signatures.is_empty() && fee != 0 {
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::MissingSignatureForFee)
|
2018-12-17 12:41:23 -08:00
|
|
|
} else {
|
|
|
|
// 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
|
2020-05-01 17:23:33 -07:00
|
|
|
let mut payer_index = None;
|
2019-11-20 12:27:02 -08:00
|
|
|
let mut tx_rent: TransactionRent = 0;
|
2020-09-19 12:17:46 -07:00
|
|
|
let mut accounts = Vec::with_capacity(message.account_keys.len());
|
|
|
|
for (i, key) in message.account_keys.iter().enumerate() {
|
|
|
|
let account = if Self::is_non_loader_key(message, key, i) {
|
|
|
|
if payer_index.is_none() {
|
|
|
|
payer_index = Some(i);
|
|
|
|
}
|
|
|
|
|
2020-09-21 22:36:23 -07:00
|
|
|
if solana_sdk::sysvar::instructions::check_id(key)
|
2020-09-24 13:31:51 -07:00
|
|
|
&& feature_set.is_active(&feature_set::instructions_sysvar_enabled::id())
|
2020-09-19 12:17:46 -07:00
|
|
|
{
|
|
|
|
if message.is_writable(i) {
|
|
|
|
return Err(TransactionError::InvalidAccountIndex);
|
2019-11-20 12:27:02 -08:00
|
|
|
}
|
2020-09-19 12:17:46 -07:00
|
|
|
Self::construct_instructions_account(message)
|
|
|
|
} else {
|
2020-05-01 17:23:33 -07:00
|
|
|
let (account, rent) =
|
|
|
|
AccountsDB::load(storage, ancestors, accounts_index, key)
|
2020-06-08 17:38:14 -07:00
|
|
|
.map(|(mut account, _)| {
|
2020-08-11 08:04:32 -07:00
|
|
|
if message.is_writable(i) {
|
|
|
|
let rent_due = rent_collector
|
|
|
|
.collect_from_existing_account(&key, &mut account);
|
2020-06-08 17:38:14 -07:00
|
|
|
(account, rent_due)
|
2020-05-01 17:23:33 -07:00
|
|
|
} else {
|
2020-06-08 17:38:14 -07:00
|
|
|
(account, 0)
|
2020-05-01 17:23:33 -07:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
tx_rent += rent;
|
|
|
|
account
|
2020-01-03 16:34:58 -08:00
|
|
|
}
|
2020-09-19 12:17:46 -07:00
|
|
|
} else {
|
|
|
|
// Fill in an empty account for the program slots.
|
|
|
|
Account::default()
|
|
|
|
};
|
|
|
|
accounts.push(account);
|
|
|
|
}
|
|
|
|
debug_assert_eq!(accounts.len(), message.account_keys.len());
|
2020-01-03 16:34:58 -08:00
|
|
|
|
2020-05-01 17:23:33 -07:00
|
|
|
if let Some(payer_index) = payer_index {
|
|
|
|
if payer_index != 0 {
|
|
|
|
warn!("Payer index should be 0! {:?}", tx);
|
|
|
|
}
|
|
|
|
if accounts[payer_index].lamports == 0 {
|
|
|
|
error_counters.account_not_found += 1;
|
|
|
|
Err(TransactionError::AccountNotFound)
|
2020-01-03 16:34:58 -08:00
|
|
|
} else {
|
2020-05-01 17:23:33 -07:00
|
|
|
let min_balance = match get_system_account_kind(&accounts[payer_index])
|
|
|
|
.ok_or_else(|| {
|
|
|
|
error_counters.invalid_account_for_fee += 1;
|
|
|
|
TransactionError::InvalidAccountForFee
|
|
|
|
})? {
|
|
|
|
SystemAccountKind::System => 0,
|
|
|
|
SystemAccountKind::Nonce => {
|
|
|
|
rent_collector.rent.minimum_balance(nonce::State::size())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if accounts[payer_index].lamports < fee + min_balance {
|
|
|
|
error_counters.insufficient_funds += 1;
|
|
|
|
Err(TransactionError::InsufficientFundsForFee)
|
|
|
|
} else {
|
|
|
|
accounts[payer_index].lamports -= fee;
|
|
|
|
Ok((accounts, tx_rent))
|
|
|
|
}
|
2020-01-03 16:34:58 -08:00
|
|
|
}
|
2020-05-01 17:23:33 -07:00
|
|
|
} else {
|
|
|
|
error_counters.account_not_found += 1;
|
|
|
|
Err(TransactionError::AccountNotFound)
|
2019-01-29 16:33:28 -08:00
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
}
|
2019-01-04 16:39:04 -08:00
|
|
|
|
2018-12-24 16:11:20 -08:00
|
|
|
fn load_executable_accounts(
|
2019-04-23 09:56:36 -07:00
|
|
|
storage: &AccountStorage,
|
2020-04-26 19:07:03 -07:00
|
|
|
ancestors: &Ancestors,
|
2019-04-15 17:15:50 -07:00
|
|
|
accounts_index: &AccountsIndex<AccountInfo>,
|
2019-03-09 19:28:43 -08:00
|
|
|
program_id: &Pubkey,
|
2019-01-08 09:20:25 -08:00
|
|
|
error_counters: &mut ErrorCounters,
|
2018-12-24 16:11:20 -08:00
|
|
|
) -> Result<Vec<(Pubkey, Account)>> {
|
2019-01-06 22:06:55 -08:00
|
|
|
let mut accounts = Vec::new();
|
|
|
|
let mut depth = 0;
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut program_id = *program_id;
|
2019-01-06 22:06:55 -08:00
|
|
|
loop {
|
2019-02-07 08:03:40 -08:00
|
|
|
if native_loader::check_id(&program_id) {
|
2019-01-06 22:06:55 -08:00
|
|
|
// at the root of the chain, ready to dispatch
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if depth >= 5 {
|
2019-01-08 09:20:25 -08:00
|
|
|
error_counters.call_chain_too_deep += 1;
|
2019-03-13 12:58:44 -07:00
|
|
|
return Err(TransactionError::CallChainTooDeep);
|
2019-01-06 22:06:55 -08:00
|
|
|
}
|
|
|
|
depth += 1;
|
|
|
|
|
2019-05-06 07:31:50 -07:00
|
|
|
let program = match AccountsDB::load(storage, ancestors, accounts_index, &program_id)
|
|
|
|
.map(|(account, _)| account)
|
|
|
|
{
|
2019-01-06 22:06:55 -08:00
|
|
|
Some(program) => program,
|
2019-01-08 09:20:25 -08:00
|
|
|
None => {
|
|
|
|
error_counters.account_not_found += 1;
|
2019-05-16 10:32:27 -07:00
|
|
|
return Err(TransactionError::ProgramAccountNotFound);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
2019-01-06 22:06:55 -08:00
|
|
|
};
|
2020-03-25 13:23:05 -07:00
|
|
|
if !program.executable {
|
|
|
|
error_counters.invalid_program_for_execution += 1;
|
|
|
|
return Err(TransactionError::InvalidProgramForExecution);
|
2019-01-06 22:06:55 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// add loader to chain
|
2019-09-24 04:09:53 -07:00
|
|
|
let program_owner = program.owner;
|
2019-04-15 17:15:50 -07:00
|
|
|
accounts.insert(0, (program_id, program));
|
2019-09-24 04:09:53 -07:00
|
|
|
program_id = program_owner;
|
2019-01-06 22:06:55 -08:00
|
|
|
}
|
|
|
|
Ok(accounts)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// For each program_id in the transaction, load its loaders.
|
2018-12-24 16:11:20 -08:00
|
|
|
fn load_loaders(
|
2019-04-23 09:56:36 -07:00
|
|
|
storage: &AccountStorage,
|
2020-04-26 19:07:03 -07:00
|
|
|
ancestors: &Ancestors,
|
2019-04-15 17:15:50 -07:00
|
|
|
accounts_index: &AccountsIndex<AccountInfo>,
|
2019-01-08 09:20:25 -08:00
|
|
|
tx: &Transaction,
|
|
|
|
error_counters: &mut ErrorCounters,
|
2019-08-23 14:04:53 -07:00
|
|
|
) -> Result<TransactionLoaders> {
|
2019-03-29 09:05:06 -07:00
|
|
|
let message = tx.message();
|
|
|
|
message
|
|
|
|
.instructions
|
2019-01-06 22:06:55 -08:00
|
|
|
.iter()
|
|
|
|
.map(|ix| {
|
2019-07-01 17:34:22 -07:00
|
|
|
if message.account_keys.len() <= ix.program_id_index as usize {
|
2019-01-08 09:20:25 -08:00
|
|
|
error_counters.account_not_found += 1;
|
2019-03-13 12:58:44 -07:00
|
|
|
return Err(TransactionError::AccountNotFound);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
2019-07-01 17:34:22 -07:00
|
|
|
let program_id = message.account_keys[ix.program_id_index as usize];
|
2019-04-15 17:15:50 -07:00
|
|
|
Self::load_executable_accounts(
|
|
|
|
storage,
|
|
|
|
ancestors,
|
|
|
|
accounts_index,
|
|
|
|
&program_id,
|
|
|
|
error_counters,
|
|
|
|
)
|
2019-01-06 22:06:55 -08:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2019-06-05 11:43:41 -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,
|
2018-12-17 12:41:23 -08:00
|
|
|
txs: &[Transaction],
|
2019-08-28 08:38:32 -07:00
|
|
|
txs_iteration_order: Option<&[usize]>,
|
2019-12-07 11:54:10 -08:00
|
|
|
lock_results: Vec<TransactionProcessResult>,
|
2019-06-10 22:18:32 -07:00
|
|
|
hash_queue: &BlockhashQueue,
|
2018-12-17 12:41:23 -08:00
|
|
|
error_counters: &mut ErrorCounters,
|
2019-08-23 14:04:53 -07:00
|
|
|
rent_collector: &RentCollector,
|
2020-09-21 22:36:23 -07:00
|
|
|
feature_set: &FeatureSet,
|
2019-12-07 11:54:10 -08:00
|
|
|
) -> Vec<(Result<TransactionLoadResult>, Option<HashAgeKind>)> {
|
2019-04-15 17:15:50 -07:00
|
|
|
//PERF: hold the lock to scan for the references, but not to clone the accounts
|
|
|
|
//TODO: two locks usually leads to deadlocks, should this be one structure?
|
2019-04-16 13:32:22 -07:00
|
|
|
let accounts_index = self.accounts_db.accounts_index.read().unwrap();
|
|
|
|
let storage = self.accounts_db.storage.read().unwrap();
|
2020-09-21 22:36:23 -07:00
|
|
|
|
|
|
|
let fee_config = FeeConfig {
|
|
|
|
secp256k1_program_enabled: feature_set
|
2020-09-24 13:31:51 -07:00
|
|
|
.is_active(&feature_set::secp256k1_program_enabled::id()),
|
2020-09-21 22:36:23 -07:00
|
|
|
};
|
2019-08-28 08:38:32 -07:00
|
|
|
OrderedIterator::new(txs, txs_iteration_order)
|
2019-01-06 22:06:55 -08:00
|
|
|
.zip(lock_results.into_iter())
|
2018-12-17 12:41:23 -08:00
|
|
|
.map(|etx| match etx {
|
2020-08-07 11:21:35 -07:00
|
|
|
((_, tx), (Ok(()), hash_age_kind)) => {
|
2020-03-05 06:40:26 -08:00
|
|
|
let fee_calculator = match hash_age_kind.as_ref() {
|
|
|
|
Some(HashAgeKind::DurableNonce(_, account)) => {
|
2020-09-29 21:15:11 -07:00
|
|
|
nonce::utils::fee_calculator_of(account)
|
2020-03-05 06:40:26 -08:00
|
|
|
}
|
|
|
|
_ => hash_queue
|
|
|
|
.get_fee_calculator(&tx.message().recent_blockhash)
|
|
|
|
.cloned(),
|
2019-12-07 11:54:10 -08:00
|
|
|
};
|
2020-03-05 06:40:26 -08:00
|
|
|
let fee = if let Some(fee_calculator) = fee_calculator {
|
2020-09-21 22:36:23 -07:00
|
|
|
fee_calculator.calculate_fee_with_config(tx.message(), &fee_config)
|
2019-12-07 11:54:10 -08:00
|
|
|
} else {
|
|
|
|
return (Err(TransactionError::BlockhashNotFound), hash_age_kind);
|
|
|
|
};
|
|
|
|
|
|
|
|
let load_res = self.load_tx_accounts(
|
2019-04-15 17:15:50 -07:00
|
|
|
&storage,
|
|
|
|
ancestors,
|
|
|
|
&accounts_index,
|
|
|
|
tx,
|
|
|
|
fee,
|
|
|
|
error_counters,
|
2019-08-23 14:04:53 -07:00
|
|
|
rent_collector,
|
2020-09-21 22:36:23 -07:00
|
|
|
feature_set,
|
2019-12-07 11:54:10 -08:00
|
|
|
);
|
|
|
|
let (accounts, rents) = match load_res {
|
|
|
|
Ok((a, r)) => (a, r),
|
|
|
|
Err(e) => return (Err(e), hash_age_kind),
|
|
|
|
};
|
|
|
|
|
|
|
|
let load_res = Self::load_loaders(
|
2019-04-15 17:15:50 -07:00
|
|
|
&storage,
|
|
|
|
ancestors,
|
|
|
|
&accounts_index,
|
|
|
|
tx,
|
|
|
|
error_counters,
|
2019-12-07 11:54:10 -08:00
|
|
|
);
|
|
|
|
let loaders = match load_res {
|
|
|
|
Ok(loaders) => loaders,
|
|
|
|
Err(e) => return (Err(e), hash_age_kind),
|
|
|
|
};
|
|
|
|
|
|
|
|
(Ok((accounts, loaders, rents)), hash_age_kind)
|
2019-01-06 22:06:55 -08:00
|
|
|
}
|
2019-12-07 11:54:10 -08:00
|
|
|
(_, (Err(e), hash_age_kind)) => (Err(e), hash_age_kind),
|
2018-12-17 12:41:23 -08:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2019-03-07 14:58:11 -08:00
|
|
|
/// Slow because lock is held for 1 operation instead of many
|
2020-04-26 19:07:03 -07:00
|
|
|
pub fn load_slow(&self, ancestors: &Ancestors, pubkey: &Pubkey) -> Option<(Account, Slot)> {
|
2019-11-05 08:38:35 -08:00
|
|
|
let (account, slot) = self
|
2019-10-23 22:01:22 -07:00
|
|
|
.accounts_db
|
2019-04-15 17:15:50 -07:00
|
|
|
.load_slow(ancestors, pubkey)
|
2019-10-23 22:01:22 -07:00
|
|
|
.unwrap_or((Account::default(), self.slot));
|
|
|
|
|
|
|
|
if account.lamports > 0 {
|
|
|
|
Some((account, slot))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2018-12-24 16:11:20 -08:00
|
|
|
|
2019-10-23 22:01:22 -07:00
|
|
|
/// scans underlying accounts_db for this delta (slot) with a map function
|
2019-07-10 21:22:58 -07:00
|
|
|
/// from StoredAccount to B
|
2019-10-23 22:01:22 -07:00
|
|
|
/// returns only the latest/current version of B for this slot
|
|
|
|
fn scan_slot<F, B>(&self, slot: Slot, func: F) -> Vec<B>
|
2019-07-10 21:22:58 -07:00
|
|
|
where
|
2019-10-02 18:33:01 -07:00
|
|
|
F: Fn(&StoredAccount) -> Option<B> + Send + Sync,
|
2019-07-10 21:22:58 -07:00
|
|
|
B: Send + Default,
|
|
|
|
{
|
|
|
|
let accumulator: Vec<Vec<(Pubkey, u64, B)>> = self.accounts_db.scan_account_storage(
|
2019-10-23 22:01:22 -07:00
|
|
|
slot,
|
2019-05-30 21:31:35 -07:00
|
|
|
|stored_account: &StoredAccount,
|
|
|
|
_id: AppendVecId,
|
2019-07-10 21:22:58 -07:00
|
|
|
accum: &mut Vec<(Pubkey, u64, B)>| {
|
|
|
|
if let Some(val) = func(stored_account) {
|
|
|
|
accum.push((
|
2019-04-16 08:50:05 -07:00
|
|
|
stored_account.meta.pubkey,
|
2019-07-10 21:22:58 -07:00
|
|
|
std::u64::MAX - stored_account.meta.write_version,
|
|
|
|
val,
|
|
|
|
));
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
2019-07-10 21:22:58 -07:00
|
|
|
|
2019-11-14 11:27:01 -08:00
|
|
|
let mut versions: Vec<(Pubkey, u64, B)> = accumulator.into_iter().flatten().collect();
|
2019-07-16 16:58:30 -07:00
|
|
|
self.accounts_db.thread_pool.install(|| {
|
|
|
|
versions.par_sort_by_key(|s| (s.0, s.1));
|
|
|
|
});
|
2019-04-16 08:50:05 -07:00
|
|
|
versions.dedup_by_key(|s| s.0);
|
2019-07-10 21:22:58 -07:00
|
|
|
versions
|
|
|
|
.into_iter()
|
|
|
|
.map(|(_pubkey, _version, val)| val)
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2020-07-20 03:09:38 -07:00
|
|
|
pub fn load_by_program_slot(
|
|
|
|
&self,
|
|
|
|
slot: Slot,
|
|
|
|
program_id: Option<&Pubkey>,
|
|
|
|
) -> Vec<(Pubkey, Account)> {
|
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,
|
|
|
|
Some(program_id) => stored_account.account_meta.owner == *program_id,
|
|
|
|
};
|
|
|
|
|
|
|
|
if hit {
|
2019-07-10 21:22:58 -07:00
|
|
|
Some((stored_account.meta.pubkey, stored_account.clone_account()))
|
|
|
|
} 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,
|
|
|
|
num: usize,
|
|
|
|
filter_by_address: &HashSet<Pubkey>,
|
|
|
|
filter: AccountAddressFilter,
|
|
|
|
) -> Vec<(Pubkey, u64)> {
|
|
|
|
let mut accounts_balances = self.accounts_db.scan_accounts(
|
|
|
|
ancestors,
|
|
|
|
|collector: &mut Vec<(Pubkey, u64)>, option| {
|
|
|
|
if let Some(data) = option
|
|
|
|
.filter(|(pubkey, account, _)| {
|
|
|
|
let should_include_pubkey = match filter {
|
|
|
|
AccountAddressFilter::Exclude => !filter_by_address.contains(&pubkey),
|
|
|
|
AccountAddressFilter::Include => filter_by_address.contains(&pubkey),
|
|
|
|
};
|
2020-05-14 18:22:47 -07:00
|
|
|
should_include_pubkey && account.lamports != 0
|
2020-05-04 16:46:10 -07:00
|
|
|
})
|
|
|
|
.map(|(pubkey, account, _slot)| (*pubkey, account.lamports))
|
|
|
|
{
|
|
|
|
collector.push(data)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
accounts_balances.sort_by(|a, b| a.1.cmp(&b.1).reverse());
|
|
|
|
accounts_balances.truncate(num);
|
|
|
|
accounts_balances
|
|
|
|
}
|
|
|
|
|
2020-09-11 09:48:06 -07:00
|
|
|
pub fn calculate_capitalization(&self, ancestors: &Ancestors) -> u64 {
|
|
|
|
let balances = self
|
|
|
|
.load_all(ancestors)
|
|
|
|
.into_iter()
|
|
|
|
.map(|(_pubkey, account, _slot)| {
|
|
|
|
AccountsDB::account_balance_for_capitalization(
|
|
|
|
account.lamports,
|
|
|
|
&account.owner,
|
|
|
|
account.executable,
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
AccountsDB::checked_sum_for_capitalization(balances)
|
|
|
|
}
|
|
|
|
|
2020-02-10 23:46:33 -08:00
|
|
|
#[must_use]
|
2020-09-11 09:48:06 -07:00
|
|
|
pub fn verify_bank_hash_and_lamports(
|
|
|
|
&self,
|
|
|
|
slot: Slot,
|
|
|
|
ancestors: &Ancestors,
|
|
|
|
total_lamports: u64,
|
|
|
|
) -> bool {
|
|
|
|
if let Err(err) =
|
|
|
|
self.accounts_db
|
|
|
|
.verify_bank_hash_and_lamports(slot, ancestors, total_lamports)
|
|
|
|
{
|
2020-02-05 09:21:19 -08:00
|
|
|
warn!("verify_bank_hash failed: {:?}", err);
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
2019-09-20 13:21:12 -07:00
|
|
|
}
|
|
|
|
|
2020-08-26 20:28:40 -07:00
|
|
|
fn is_loadable(account: &Account) -> bool {
|
|
|
|
// Don't ever load zero lamport accounts into runtime because
|
|
|
|
// the existence of zero-lamport accounts are never deterministic!!
|
|
|
|
account.lamports > 0
|
|
|
|
}
|
|
|
|
|
2020-05-13 00:22:14 -07:00
|
|
|
fn load_while_filtering<F: Fn(&Account) -> bool>(
|
|
|
|
collector: &mut Vec<(Pubkey, Account)>,
|
2020-08-26 20:28:40 -07:00
|
|
|
some_account_tuple: Option<(&Pubkey, Account, 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
|
|
|
|
.filter(|(_, account, _)| Self::is_loadable(account) && 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-11 11:58:28 -07:00
|
|
|
pub fn load_by_program(
|
|
|
|
&self,
|
2020-04-26 19:07:03 -07:00
|
|
|
ancestors: &Ancestors,
|
2020-09-11 09:48:06 -07:00
|
|
|
program_id: &Pubkey,
|
2019-07-11 11:58:28 -07:00
|
|
|
) -> Vec<(Pubkey, Account)> {
|
|
|
|
self.accounts_db.scan_accounts(
|
|
|
|
ancestors,
|
2020-08-26 20:28:40 -07:00
|
|
|
|collector: &mut Vec<(Pubkey, Account)>, some_account_tuple| {
|
|
|
|
Self::load_while_filtering(collector, some_account_tuple, |account| {
|
2020-09-11 09:48:06 -07:00
|
|
|
account.owner == *program_id
|
2020-05-13 00:22:14 -07:00
|
|
|
})
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-08-26 20:28:40 -07:00
|
|
|
pub fn load_all(&self, ancestors: &Ancestors) -> Vec<(Pubkey, Account, Slot)> {
|
|
|
|
self.accounts_db.scan_accounts(
|
|
|
|
ancestors,
|
|
|
|
|collector: &mut Vec<(Pubkey, Account, Slot)>, some_account_tuple| {
|
|
|
|
if let Some((pubkey, account, slot)) =
|
|
|
|
some_account_tuple.filter(|(_, account, _)| Self::is_loadable(account))
|
|
|
|
{
|
|
|
|
collector.push((*pubkey, account, slot))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-05-13 00:22:14 -07:00
|
|
|
pub fn load_to_collect_rent_eagerly<R: RangeBounds<Pubkey>>(
|
|
|
|
&self,
|
|
|
|
ancestors: &Ancestors,
|
|
|
|
range: R,
|
|
|
|
) -> Vec<(Pubkey, Account)> {
|
|
|
|
self.accounts_db.range_scan_accounts(
|
|
|
|
ancestors,
|
|
|
|
range,
|
|
|
|
|collector: &mut Vec<(Pubkey, Account)>, option| {
|
|
|
|
Self::load_while_filtering(collector, option, |_| true)
|
2019-07-11 11:58:28 -07:00
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-03-07 14:58:11 -08:00
|
|
|
/// Slow because lock is held for 1 operation instead of many
|
2019-10-23 22:01:22 -07:00
|
|
|
pub fn store_slow(&self, slot: Slot, pubkey: &Pubkey, account: &Account) {
|
|
|
|
self.accounts_db.store(slot, &[(pubkey, account)]);
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
fn is_locked_readonly(&self, key: &Pubkey) -> bool {
|
|
|
|
self.readonly_locks
|
2019-10-23 22:01:22 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.as_ref()
|
|
|
|
.map_or(false, |locks| {
|
|
|
|
locks
|
|
|
|
.get(key)
|
|
|
|
.map_or(false, |lock| *lock.lock_count.lock().unwrap() > 0)
|
|
|
|
})
|
2019-07-11 18:46:49 -07:00
|
|
|
}
|
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
fn unlock_readonly(&self, key: &Pubkey) {
|
|
|
|
self.readonly_locks.read().unwrap().as_ref().map(|locks| {
|
|
|
|
locks
|
|
|
|
.get(key)
|
|
|
|
.map(|lock| *lock.lock_count.lock().unwrap() -= 1)
|
|
|
|
});
|
2019-10-23 22:01:22 -07:00
|
|
|
}
|
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
fn lock_readonly(&self, key: &Pubkey) -> bool {
|
|
|
|
self.readonly_locks
|
2019-10-23 22:01:22 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.as_ref()
|
|
|
|
.map_or(false, |locks| {
|
|
|
|
locks.get(key).map_or(false, |lock| {
|
|
|
|
*lock.lock_count.lock().unwrap() += 1;
|
|
|
|
true
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
fn insert_readonly(&self, key: &Pubkey, lock: ReadonlyLock) -> bool {
|
|
|
|
self.readonly_locks
|
2019-10-23 22:01:22 -07:00
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.as_mut()
|
|
|
|
.map_or(false, |locks| {
|
|
|
|
assert!(locks.get(key).is_none());
|
|
|
|
locks.insert(*key, lock);
|
|
|
|
true
|
|
|
|
})
|
2019-07-11 18:46:49 -07:00
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
fn lock_account(
|
2019-10-23 22:01:22 -07:00
|
|
|
&self,
|
2019-06-10 22:05:46 -07:00
|
|
|
locks: &mut HashSet<Pubkey>,
|
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() {
|
|
|
|
if locks.contains(k) || self.is_locked_readonly(k) {
|
2019-09-16 21:45:16 -07:00
|
|
|
debug!("CD 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() {
|
2019-06-10 22:05:46 -07:00
|
|
|
if locks.contains(k) {
|
2019-09-16 21:45:16 -07:00
|
|
|
debug!("CO 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 {
|
2019-06-27 14:25:10 -07:00
|
|
|
locks.insert(*k);
|
|
|
|
}
|
2019-10-23 22:01:22 -07:00
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
let readonly_writes: Vec<&&Pubkey> = readonly_keys
|
2019-10-23 22:01:22 -07:00
|
|
|
.iter()
|
2019-11-05 08:38:35 -08:00
|
|
|
.filter(|k| !self.lock_readonly(k))
|
2019-10-23 22:01:22 -07:00
|
|
|
.collect();
|
2019-06-27 14:25:10 -07:00
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
for k in readonly_writes.iter() {
|
|
|
|
self.insert_readonly(
|
2019-10-23 22:01:22 -07:00
|
|
|
*k,
|
2019-11-05 08:38:35 -08:00
|
|
|
ReadonlyLock {
|
2019-06-27 14:25:10 -07:00
|
|
|
lock_count: Mutex::new(1),
|
|
|
|
},
|
|
|
|
);
|
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(())
|
|
|
|
}
|
|
|
|
|
2019-10-23 22:01:22 -07:00
|
|
|
fn unlock_account(&self, tx: &Transaction, result: &Result<()>, locks: &mut HashSet<Pubkey>) {
|
2018-12-17 12:41:23 -08:00
|
|
|
match result {
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::AccountInUse) => (),
|
2020-05-02 08:07:52 -07:00
|
|
|
Err(TransactionError::SanitizeFailure) => (),
|
|
|
|
Err(TransactionError::AccountLoadedTwice) => (),
|
2018-12-17 12:41:23 -08:00
|
|
|
_ => {
|
2020-05-02 08:07:52 -07:00
|
|
|
let (writable_keys, readonly_keys) = &tx.message().get_account_keys_by_lock_type();
|
2019-11-05 08:38:35 -08:00
|
|
|
for k in writable_keys {
|
2019-04-15 17:15:50 -07:00
|
|
|
locks.remove(k);
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-11-05 08:38:35 -08:00
|
|
|
for k in readonly_keys {
|
|
|
|
self.unlock_readonly(k);
|
2019-06-27 14:25:10 -07:00
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2020-03-25 21:08:56 -07:00
|
|
|
pub fn bank_hash_at(&self, slot: Slot) -> Hash {
|
|
|
|
self.bank_hash_info_at(slot).hash
|
2020-01-14 11:57:29 -08:00
|
|
|
}
|
|
|
|
|
2020-03-25 21:08:56 -07:00
|
|
|
pub fn bank_hash_info_at(&self, slot: Slot) -> BankHashInfo {
|
|
|
|
let delta_hash = self.accounts_db.get_accounts_delta_hash(slot);
|
2019-12-22 17:50:31 -08:00
|
|
|
let bank_hashes = self.accounts_db.bank_hashes.read().unwrap();
|
2020-02-22 13:46:40 -08:00
|
|
|
let mut hash_info = bank_hashes
|
2020-03-25 21:08:56 -07:00
|
|
|
.get(&slot)
|
2019-12-22 17:50:31 -08:00
|
|
|
.expect("No bank hash was found for this bank, that should not be possible")
|
2020-02-22 13:46:40 -08:00
|
|
|
.clone();
|
|
|
|
hash_info.hash = delta_hash;
|
|
|
|
hash_info
|
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]
|
2019-08-28 08:38:32 -07:00
|
|
|
pub fn lock_accounts(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
txs_iteration_order: Option<&[usize]>,
|
|
|
|
) -> Vec<Result<()>> {
|
2020-05-02 08:07:52 -07:00
|
|
|
use solana_sdk::sanitize::Sanitize;
|
|
|
|
let keys: Vec<Result<_>> = OrderedIterator::new(txs, txs_iteration_order)
|
2020-08-07 11:21:35 -07:00
|
|
|
.map(|(_, tx)| {
|
2020-08-04 20:22:25 -07:00
|
|
|
tx.sanitize().map_err(TransactionError::from)?;
|
2020-05-02 08:07:52 -07:00
|
|
|
|
|
|
|
if Self::has_duplicates(&tx.message.account_keys) {
|
|
|
|
return Err(TransactionError::AccountLoadedTwice);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(tx.message().get_account_keys_by_lock_type())
|
|
|
|
})
|
2018-12-17 12:41:23 -08:00
|
|
|
.collect();
|
2020-01-16 23:26:50 -08:00
|
|
|
let mut account_locks = &mut self.account_locks.lock().unwrap();
|
|
|
|
keys.into_iter()
|
2020-05-02 08:07:52 -07:00
|
|
|
.map(|result| match result {
|
|
|
|
Ok((writable_keys, readonly_keys)) => {
|
|
|
|
self.lock_account(&mut account_locks, writable_keys, readonly_keys)
|
|
|
|
}
|
|
|
|
Err(e) => Err(e),
|
2020-01-16 23:26:50 -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
|
2019-09-16 21:45:16 -07:00
|
|
|
pub fn unlock_accounts(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
txs_iteration_order: Option<&[usize]>,
|
|
|
|
results: &[Result<()>],
|
|
|
|
) {
|
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");
|
2019-09-16 21:45:16 -07:00
|
|
|
|
|
|
|
OrderedIterator::new(txs, txs_iteration_order)
|
|
|
|
.zip(results.iter())
|
2020-08-07 11:21:35 -07:00
|
|
|
.for_each(|((_, tx), result)| self.unlock_account(tx, result, &mut account_locks));
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
/// Store the accounts into the DB
|
2018-12-17 12:41:23 -08:00
|
|
|
pub fn store_accounts(
|
|
|
|
&self,
|
2019-10-23 22:01:22 -07:00
|
|
|
slot: Slot,
|
2018-12-17 12:41:23 -08:00
|
|
|
txs: &[Transaction],
|
2019-08-28 08:38:32 -07:00
|
|
|
txs_iteration_order: Option<&[usize]>,
|
2019-12-07 11:54:10 -08:00
|
|
|
res: &[TransactionProcessResult],
|
|
|
|
loaded: &mut [(Result<TransactionLoadResult>, Option<HashAgeKind>)],
|
2019-11-20 12:27:02 -08:00
|
|
|
rent_collector: &RentCollector,
|
2020-07-09 13:22:21 -07:00
|
|
|
last_blockhash_with_fee_calculator: &(Hash, FeeCalculator),
|
2020-07-15 14:45:30 -07:00
|
|
|
fix_recent_blockhashes_sysvar_delay: bool,
|
2019-02-19 17:11:43 -08:00
|
|
|
) {
|
2020-01-10 15:57:31 -08:00
|
|
|
let accounts_to_store = self.collect_accounts_to_store(
|
|
|
|
txs,
|
|
|
|
txs_iteration_order,
|
|
|
|
res,
|
|
|
|
loaded,
|
|
|
|
rent_collector,
|
2020-07-09 13:22:21 -07:00
|
|
|
last_blockhash_with_fee_calculator,
|
2020-07-15 14:45:30 -07:00
|
|
|
fix_recent_blockhashes_sysvar_delay,
|
2020-01-10 15:57:31 -08:00
|
|
|
);
|
2019-10-23 22:01:22 -07:00
|
|
|
self.accounts_db.store(slot, &accounts_to_store);
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2019-10-23 22:01:22 -07:00
|
|
|
/// Purge a slot if it is not a root
|
|
|
|
/// Root slots cannot be purged
|
|
|
|
pub fn purge_slot(&self, slot: Slot) {
|
|
|
|
self.accounts_db.purge_slot(slot);
|
2018-12-24 16:11:20 -08:00
|
|
|
}
|
2019-10-23 22:01:22 -07:00
|
|
|
/// Add a slot to root. Root slots cannot be purged
|
|
|
|
pub fn add_root(&self, slot: Slot) {
|
|
|
|
self.accounts_db.add_root(slot)
|
2019-03-21 17:36:10 -07:00
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
|
2020-04-26 00:11:37 -07:00
|
|
|
fn is_non_loader_key(message: &Message, key: &Pubkey, key_index: usize) -> bool {
|
|
|
|
!message.program_ids().contains(&key) || message.is_key_passed_to_program(key_index)
|
|
|
|
}
|
|
|
|
|
2019-08-19 13:00:37 -07:00
|
|
|
fn collect_accounts_to_store<'a>(
|
2019-06-27 14:25:10 -07:00
|
|
|
&self,
|
|
|
|
txs: &'a [Transaction],
|
2019-08-28 08:38:32 -07:00
|
|
|
txs_iteration_order: Option<&'a [usize]>,
|
2019-12-07 11:54:10 -08:00
|
|
|
res: &'a [TransactionProcessResult],
|
|
|
|
loaded: &'a mut [(Result<TransactionLoadResult>, Option<HashAgeKind>)],
|
2019-11-20 12:27:02 -08:00
|
|
|
rent_collector: &RentCollector,
|
2020-07-09 13:22:21 -07:00
|
|
|
last_blockhash_with_fee_calculator: &(Hash, FeeCalculator),
|
2020-07-15 14:45:30 -07:00
|
|
|
fix_recent_blockhashes_sysvar_delay: bool,
|
2019-08-19 13:00:37 -07:00
|
|
|
) -> Vec<(&'a Pubkey, &'a Account)> {
|
2019-10-30 21:55:17 -07:00
|
|
|
let mut accounts = Vec::with_capacity(loaded.len());
|
2020-08-07 11:21:35 -07:00
|
|
|
for (i, ((raccs, _hash_age_kind), (_, tx))) in loaded
|
2019-08-28 08:38:32 -07:00
|
|
|
.iter_mut()
|
|
|
|
.zip(OrderedIterator::new(txs, txs_iteration_order))
|
|
|
|
.enumerate()
|
|
|
|
{
|
2020-01-10 15:57:31 -08:00
|
|
|
if raccs.is_err() {
|
2019-06-27 14:25:10 -07:00
|
|
|
continue;
|
|
|
|
}
|
2020-01-10 15:57:31 -08:00
|
|
|
let (res, hash_age_kind) = &res[i];
|
|
|
|
let maybe_nonce = match (res, hash_age_kind) {
|
|
|
|
(Ok(_), Some(HashAgeKind::DurableNonce(pubkey, acc))) => Some((pubkey, acc)),
|
|
|
|
(
|
|
|
|
Err(TransactionError::InstructionError(_, _)),
|
|
|
|
Some(HashAgeKind::DurableNonce(pubkey, acc)),
|
|
|
|
) => Some((pubkey, acc)),
|
|
|
|
(Ok(_), _hash_age_kind) => None,
|
|
|
|
(Err(_), _hash_age_kind) => continue,
|
|
|
|
};
|
2019-06-27 14:25:10 -07:00
|
|
|
|
2019-08-28 08:38:32 -07:00
|
|
|
let message = &tx.message();
|
2019-06-27 14:25:10 -07:00
|
|
|
let acc = raccs.as_mut().unwrap();
|
2019-11-20 12:27:02 -08:00
|
|
|
for ((i, key), account) in message
|
|
|
|
.account_keys
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.zip(acc.0.iter_mut())
|
2020-05-01 17:23:33 -07:00
|
|
|
.filter(|((i, key), _account)| Self::is_non_loader_key(message, key, *i))
|
2019-11-20 12:27:02 -08:00
|
|
|
{
|
2020-09-29 20:34:07 -07:00
|
|
|
prepare_if_nonce_account(
|
2020-03-05 06:40:26 -08:00
|
|
|
account,
|
|
|
|
key,
|
|
|
|
res,
|
|
|
|
maybe_nonce,
|
2020-07-09 13:22:21 -07:00
|
|
|
last_blockhash_with_fee_calculator,
|
2020-07-15 14:45:30 -07:00
|
|
|
fix_recent_blockhashes_sysvar_delay,
|
2020-03-05 06:40:26 -08:00
|
|
|
);
|
2019-11-05 08:38:35 -08:00
|
|
|
if message.is_writable(i) {
|
2019-11-20 12:27:02 -08:00
|
|
|
if account.rent_epoch == 0 {
|
2020-08-11 08:04:32 -07:00
|
|
|
acc.2 += rent_collector.collect_from_created_account(&key, account);
|
2019-11-20 12:27:02 -08:00
|
|
|
}
|
|
|
|
accounts.push((key, &*account));
|
2019-10-16 13:31:21 -07:00
|
|
|
}
|
2019-06-10 19:50:02 -07:00
|
|
|
}
|
|
|
|
}
|
2019-06-27 14:25:10 -07:00
|
|
|
accounts
|
2019-06-10 19:50:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 20:34:07 -07:00
|
|
|
pub fn prepare_if_nonce_account(
|
|
|
|
account: &mut Account,
|
|
|
|
account_pubkey: &Pubkey,
|
|
|
|
tx_result: &Result<()>,
|
|
|
|
maybe_nonce: Option<(&Pubkey, &Account)>,
|
|
|
|
last_blockhash_with_fee_calculator: &(Hash, FeeCalculator),
|
|
|
|
fix_recent_blockhashes_sysvar_delay: bool,
|
|
|
|
) {
|
|
|
|
if let Some((nonce_key, nonce_acc)) = maybe_nonce {
|
|
|
|
if account_pubkey == nonce_key {
|
|
|
|
let overwrite = if tx_result.is_err() {
|
|
|
|
// Nonce TX failed with an InstructionError. Roll back
|
|
|
|
// its account state
|
|
|
|
*account = nonce_acc.clone();
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
// Retain overwrite on successful transactions until
|
|
|
|
// recent_blockhashes_sysvar_delay fix is activated
|
|
|
|
!fix_recent_blockhashes_sysvar_delay
|
|
|
|
};
|
|
|
|
if overwrite {
|
|
|
|
// Since hash_age_kind is DurableNonce, unwrap is safe here
|
|
|
|
let state = StateMut::<nonce::state::Versions>::state(nonce_acc)
|
|
|
|
.unwrap()
|
|
|
|
.convert_to_current();
|
|
|
|
if let nonce::State::Initialized(ref data) = state {
|
|
|
|
let new_data = nonce::state::Versions::new_current(nonce::State::Initialized(
|
|
|
|
nonce::state::Data {
|
|
|
|
blockhash: last_blockhash_with_fee_calculator.0,
|
|
|
|
fee_calculator: last_blockhash_with_fee_calculator.1.clone(),
|
|
|
|
..data.clone()
|
|
|
|
},
|
|
|
|
));
|
|
|
|
account.set_state(&new_data).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-02 00:38:30 -07:00
|
|
|
pub fn create_test_accounts(
|
|
|
|
accounts: &Accounts,
|
|
|
|
pubkeys: &mut Vec<Pubkey>,
|
|
|
|
num: usize,
|
|
|
|
slot: Slot,
|
|
|
|
) {
|
2019-07-19 09:32:29 -07:00
|
|
|
for t in 0..num {
|
|
|
|
let pubkey = Pubkey::new_rand();
|
|
|
|
let account = Account::new((t + 1) as u64, 0, &Account::default().owner);
|
2019-10-23 22:01:22 -07:00
|
|
|
accounts.store_slow(slot, &pubkey, &account);
|
2019-07-19 09:32:29 -07:00
|
|
|
pubkeys.push(pubkey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-17 11:02:07 -07:00
|
|
|
pub fn update_accounts(accounts: &Accounts, pubkeys: &[Pubkey], slot: u64) {
|
|
|
|
for pubkey in pubkeys {
|
|
|
|
let amount = thread_rng().gen_range(0, 10);
|
|
|
|
let account = Account::new(amount, 0, &Account::default().owner);
|
|
|
|
accounts.store_slow(slot, &pubkey, &account);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
// TODO: all the bank tests are bank specific, issue: 2194
|
2019-01-08 09:20:25 -08:00
|
|
|
|
|
|
|
use super::*;
|
2020-05-22 10:54:24 -07:00
|
|
|
use crate::{bank::HashAgeKind, rent_collector::RentCollector};
|
2020-01-28 17:03:20 -08:00
|
|
|
use solana_sdk::{
|
|
|
|
account::Account,
|
|
|
|
epoch_schedule::EpochSchedule,
|
|
|
|
fee_calculator::FeeCalculator,
|
2020-09-08 07:55:09 -07:00
|
|
|
genesis_config::ClusterType,
|
2020-01-28 17:03:20 -08:00
|
|
|
hash::Hash,
|
2020-09-29 20:34:07 -07:00
|
|
|
instruction::{CompiledInstruction, InstructionError},
|
2020-01-28 17:03:20 -08:00
|
|
|
message::Message,
|
2020-03-03 17:00:39 -08:00
|
|
|
nonce,
|
2020-01-28 17:03:20 -08:00
|
|
|
rent::Rent,
|
2020-02-20 13:28:55 -08:00
|
|
|
signature::{Keypair, Signer},
|
2020-01-28 17:03:20 -08:00
|
|
|
system_program,
|
|
|
|
};
|
|
|
|
use std::{
|
|
|
|
sync::atomic::{AtomicBool, AtomicU64, Ordering},
|
|
|
|
{thread, time},
|
|
|
|
};
|
2019-01-08 09:20:25 -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,
|
2020-05-15 09:35:43 -07:00
|
|
|
ka: &[(Pubkey, Account)],
|
2019-03-29 15:11:21 -07:00
|
|
|
fee_calculator: &FeeCalculator,
|
2020-01-03 16:34:58 -08:00
|
|
|
rent_collector: &RentCollector,
|
2019-01-08 09:20:25 -08:00
|
|
|
error_counters: &mut ErrorCounters,
|
2019-12-07 11:54:10 -08:00
|
|
|
) -> Vec<(Result<TransactionLoadResult>, Option<HashAgeKind>)> {
|
2019-06-10 22:18:32 -07:00
|
|
|
let mut hash_queue = BlockhashQueue::new(100);
|
|
|
|
hash_queue.register_hash(&tx.message().recent_blockhash, &fee_calculator);
|
2020-09-08 07:55:09 -07:00
|
|
|
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
2019-01-08 09:20:25 -08:00
|
|
|
for ka in ka.iter() {
|
2019-02-27 16:06:06 -08:00
|
|
|
accounts.store_slow(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();
|
2020-05-15 09:35:43 -07:00
|
|
|
accounts.load_accounts(
|
2019-08-23 14:04:53 -07:00
|
|
|
&ancestors,
|
|
|
|
&[tx],
|
2019-08-28 08:38:32 -07:00
|
|
|
None,
|
2019-12-07 11:54:10 -08:00
|
|
|
vec![(Ok(()), Some(HashAgeKind::Extant))],
|
2019-08-23 14:04:53 -07:00
|
|
|
&hash_queue,
|
|
|
|
error_counters,
|
2020-01-03 16:34:58 -08:00
|
|
|
rent_collector,
|
2020-09-29 20:18:28 -07:00
|
|
|
&FeatureSet::all_enabled(),
|
2020-05-15 09:35:43 -07:00
|
|
|
)
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
2020-01-03 16:34:58 -08:00
|
|
|
fn load_accounts_with_fee(
|
|
|
|
tx: Transaction,
|
2020-05-15 09:35:43 -07:00
|
|
|
ka: &[(Pubkey, Account)],
|
2020-01-03 16:34:58 -08:00
|
|
|
fee_calculator: &FeeCalculator,
|
|
|
|
error_counters: &mut ErrorCounters,
|
|
|
|
) -> Vec<(Result<TransactionLoadResult>, Option<HashAgeKind>)> {
|
|
|
|
let rent_collector = RentCollector::default();
|
|
|
|
load_accounts_with_fee_and_rent(tx, ka, fee_calculator, &rent_collector, error_counters)
|
|
|
|
}
|
|
|
|
|
2019-03-29 15:11:21 -07:00
|
|
|
fn load_accounts(
|
|
|
|
tx: Transaction,
|
2020-05-15 09:35:43 -07:00
|
|
|
ka: &[(Pubkey, Account)],
|
2019-03-29 15:11:21 -07:00
|
|
|
error_counters: &mut ErrorCounters,
|
2019-12-07 11:54:10 -08:00
|
|
|
) -> Vec<(Result<TransactionLoadResult>, Option<HashAgeKind>)> {
|
2019-03-29 15:11:21 -07:00
|
|
|
let fee_calculator = FeeCalculator::default();
|
2019-10-16 13:31:21 -07:00
|
|
|
load_accounts_with_fee(tx, ka, &fee_calculator, error_counters)
|
2019-03-29 15:11:21 -07:00
|
|
|
}
|
|
|
|
|
2019-01-08 09:20:25 -08:00
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_no_key() {
|
|
|
|
let accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
2019-05-23 15:19:53 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(0, &(), vec![0])];
|
2020-02-20 12:13:23 -08:00
|
|
|
let tx = Transaction::new_with_compiled_instructions::<[&Keypair; 0]>(
|
2019-01-08 09:20:25 -08:00
|
|
|
&[],
|
|
|
|
&[],
|
|
|
|
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],
|
|
|
|
(
|
|
|
|
Err(TransactionError::AccountNotFound),
|
|
|
|
Some(HashAgeKind::Extant)
|
|
|
|
)
|
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_no_account_0_exists() {
|
|
|
|
let accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
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],
|
|
|
|
(
|
|
|
|
Err(TransactionError::AccountNotFound),
|
|
|
|
Some(HashAgeKind::Extant)
|
|
|
|
),
|
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_unknown_program_id() {
|
|
|
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
let key1 = Pubkey::new(&[5u8; 32]);
|
|
|
|
|
2020-01-03 16:34:58 -08:00
|
|
|
let account = Account::new(1, 0, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let account = Account::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],
|
2019-12-07 11:54:10 -08:00
|
|
|
(
|
|
|
|
Err(TransactionError::ProgramAccountNotFound),
|
|
|
|
Some(HashAgeKind::Extant)
|
|
|
|
)
|
2019-05-22 15:23:16 -07:00
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_insufficient_funds() {
|
|
|
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
|
2020-01-03 16:34:58 -08:00
|
|
|
let account = Account::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,
|
|
|
|
);
|
|
|
|
|
2020-02-28 12:27:01 -08:00
|
|
|
let fee_calculator = FeeCalculator::new(10);
|
2020-09-21 22:36:23 -07:00
|
|
|
assert_eq!(fee_calculator.calculate_fee(tx.message()), 10);
|
2019-03-29 15:11:21 -07:00
|
|
|
|
2019-10-16 13:31:21 -07:00
|
|
|
let loaded_accounts =
|
|
|
|
load_accounts_with_fee(tx, &accounts, &fee_calculator, &mut error_counters);
|
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(),
|
2019-12-07 11:54:10 -08:00
|
|
|
(
|
|
|
|
Err(TransactionError::InsufficientFundsForFee),
|
|
|
|
Some(HashAgeKind::Extant)
|
|
|
|
),
|
2019-05-24 13:06:55 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_invalid_account_for_fee() {
|
|
|
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
|
|
|
|
let account = Account::new(1, 1, &Pubkey::new_rand()); // <-- owner is not the system program
|
|
|
|
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],
|
2019-12-07 11:54:10 -08:00
|
|
|
(
|
|
|
|
Err(TransactionError::InvalidAccountForFee),
|
|
|
|
Some(HashAgeKind::Extant)
|
|
|
|
),
|
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() {
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
let rent_collector = RentCollector::new(
|
|
|
|
0,
|
|
|
|
&EpochSchedule::default(),
|
|
|
|
500_000.0,
|
|
|
|
&Rent {
|
|
|
|
lamports_per_byte_year: 42,
|
|
|
|
..Rent::default()
|
|
|
|
},
|
2020-09-08 07:55:09 -07:00
|
|
|
ClusterType::Development,
|
2020-01-03 16:34:58 -08:00
|
|
|
);
|
2020-03-03 17:00:39 -08:00
|
|
|
let min_balance = rent_collector.rent.minimum_balance(nonce::State::size());
|
2020-02-28 12:27:01 -08:00
|
|
|
let fee_calculator = FeeCalculator::new(min_balance);
|
2020-01-03 16:34:58 -08:00
|
|
|
let nonce = Keypair::new();
|
|
|
|
let mut accounts = vec![(
|
|
|
|
nonce.pubkey(),
|
|
|
|
Account::new_data(
|
|
|
|
min_balance * 2,
|
2020-03-05 06:40:26 -08:00
|
|
|
&nonce::state::Versions::new_current(nonce::State::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,
|
|
|
|
&fee_calculator,
|
|
|
|
&rent_collector,
|
|
|
|
&mut error_counters,
|
|
|
|
);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
let (load_res, _hash_age_kind) = &loaded_accounts[0];
|
|
|
|
let (tx_accounts, _loaders, _rents) = load_res.as_ref().unwrap();
|
|
|
|
assert_eq!(tx_accounts[0].lamports, min_balance);
|
|
|
|
|
|
|
|
// Fee leaves zero balance fails
|
|
|
|
accounts[0].1.lamports = min_balance;
|
|
|
|
let loaded_accounts = load_accounts_with_fee_and_rent(
|
|
|
|
tx.clone(),
|
|
|
|
&accounts,
|
|
|
|
&fee_calculator,
|
|
|
|
&rent_collector,
|
|
|
|
&mut error_counters,
|
|
|
|
);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
let (load_res, _hash_age_kind) = &loaded_accounts[0];
|
|
|
|
assert_eq!(*load_res, Err(TransactionError::InsufficientFundsForFee));
|
|
|
|
|
|
|
|
// Fee leaves non-zero, but sub-min_balance balance fails
|
|
|
|
accounts[0].1.lamports = 3 * min_balance / 2;
|
|
|
|
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,
|
|
|
|
&fee_calculator,
|
|
|
|
&rent_collector,
|
|
|
|
&mut error_counters,
|
|
|
|
);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
let (load_res, _hash_age_kind) = &loaded_accounts[0];
|
|
|
|
assert_eq!(*load_res, Err(TransactionError::InsufficientFundsForFee));
|
|
|
|
}
|
|
|
|
|
2019-01-08 09:20:25 -08:00
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_no_loaders() {
|
|
|
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
let key1 = Pubkey::new(&[5u8; 32]);
|
|
|
|
|
2020-01-03 16:34:58 -08:00
|
|
|
let mut account = Account::new(1, 0, &Pubkey::default());
|
2019-08-23 14:04:53 -07:00
|
|
|
account.rent_epoch = 1;
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2019-08-23 14:04:53 -07:00
|
|
|
let mut account = Account::new(2, 1, &Pubkey::default());
|
|
|
|
account.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,
|
|
|
|
);
|
|
|
|
|
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, 0);
|
2019-01-08 09:20:25 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
match &loaded_accounts[0] {
|
2019-12-07 11:54:10 -08:00
|
|
|
(
|
|
|
|
Ok((transaction_accounts, transaction_loaders, _transaction_rents)),
|
|
|
|
_hash_age_kind,
|
|
|
|
) => {
|
2020-05-01 17:23:33 -07:00
|
|
|
assert_eq!(transaction_accounts.len(), 3);
|
2019-08-23 14:04:53 -07:00
|
|
|
assert_eq!(transaction_accounts[0], accounts[0].1);
|
|
|
|
assert_eq!(transaction_loaders.len(), 1);
|
|
|
|
assert_eq!(transaction_loaders[0].len(), 0);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
2019-12-07 11:54:10 -08:00
|
|
|
(Err(e), _hash_age_kind) => Err(e).unwrap(),
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_max_call_depth() {
|
|
|
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
let key1 = Pubkey::new(&[5u8; 32]);
|
|
|
|
let key2 = Pubkey::new(&[6u8; 32]);
|
|
|
|
let key3 = Pubkey::new(&[7u8; 32]);
|
|
|
|
let key4 = Pubkey::new(&[8u8; 32]);
|
|
|
|
let key5 = Pubkey::new(&[9u8; 32]);
|
|
|
|
let key6 = Pubkey::new(&[10u8; 32]);
|
|
|
|
|
2020-01-03 16:34:58 -08:00
|
|
|
let account = Account::new(1, 0, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut account = Account::new(40, 1, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
account.executable = true;
|
2019-02-14 09:57:54 -08:00
|
|
|
account.owner = native_loader::id();
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key1, account));
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut account = Account::new(41, 1, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
account.executable = true;
|
2019-02-14 09:57:54 -08:00
|
|
|
account.owner = key1;
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key2, account));
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut account = Account::new(42, 1, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
account.executable = true;
|
2019-02-14 09:57:54 -08:00
|
|
|
account.owner = key2;
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key3, account));
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut account = Account::new(43, 1, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
account.executable = true;
|
2019-02-14 09:57:54 -08:00
|
|
|
account.owner = key3;
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key4, account));
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut account = Account::new(44, 1, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
account.executable = true;
|
2019-02-14 09:57:54 -08:00
|
|
|
account.owner = key4;
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key5, account));
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut account = Account::new(45, 1, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
account.executable = true;
|
2019-02-14 09:57:54 -08:00
|
|
|
account.owner = key5;
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key6, account));
|
|
|
|
|
2019-05-22 15:23:16 -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![key6],
|
|
|
|
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.call_chain_too_deep, 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],
|
|
|
|
(
|
|
|
|
Err(TransactionError::CallChainTooDeep),
|
|
|
|
Some(HashAgeKind::Extant)
|
|
|
|
)
|
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_bad_program_id() {
|
|
|
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
let key1 = Pubkey::new(&[5u8; 32]);
|
|
|
|
|
2020-03-04 10:40:41 -08:00
|
|
|
let account = Account::new(1, 0, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2020-03-04 10:40:41 -08:00
|
|
|
let mut account = Account::new(40, 1, &native_loader::id());
|
2019-01-08 09:20:25 -08:00
|
|
|
account.executable = true;
|
|
|
|
accounts.push((key1, account));
|
|
|
|
|
2019-03-15 08:47:25 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(0, &(), 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);
|
2020-03-04 10:40:41 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0],
|
|
|
|
(
|
2020-03-25 13:23:05 -07:00
|
|
|
Err(TransactionError::InvalidProgramForExecution),
|
2020-03-04 10:40:41 -08:00
|
|
|
Some(HashAgeKind::Extant)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_bad_owner() {
|
|
|
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
let key1 = Pubkey::new(&[5u8; 32]);
|
|
|
|
|
|
|
|
let account = Account::new(1, 0, &Pubkey::default());
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(40, 1, &Pubkey::default());
|
|
|
|
account.executable = true;
|
|
|
|
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-03-25 13:23:05 -07:00
|
|
|
Err(TransactionError::ProgramAccountNotFound),
|
2019-12-07 11:54:10 -08:00
|
|
|
Some(HashAgeKind::Extant)
|
|
|
|
)
|
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_not_executable() {
|
|
|
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
let key1 = Pubkey::new(&[5u8; 32]);
|
|
|
|
|
2020-01-03 16:34:58 -08:00
|
|
|
let account = Account::new(1, 0, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2020-03-04 10:40:41 -08:00
|
|
|
let account = Account::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-03-25 13:23:05 -07:00
|
|
|
Err(TransactionError::InvalidProgramForExecution),
|
2019-12-07 11:54:10 -08:00
|
|
|
Some(HashAgeKind::Extant)
|
|
|
|
)
|
|
|
|
);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_accounts_multiple_loaders() {
|
|
|
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let key0 = keypair.pubkey();
|
|
|
|
let key1 = Pubkey::new(&[5u8; 32]);
|
|
|
|
let key2 = Pubkey::new(&[6u8; 32]);
|
|
|
|
|
2020-01-03 16:34:58 -08:00
|
|
|
let mut account = Account::new(1, 0, &Pubkey::default());
|
2019-08-23 14:04:53 -07:00
|
|
|
account.rent_epoch = 1;
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key0, account));
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut account = Account::new(40, 1, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
account.executable = true;
|
2019-08-23 14:04:53 -07:00
|
|
|
account.rent_epoch = 1;
|
2019-02-14 09:57:54 -08:00
|
|
|
account.owner = native_loader::id();
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key1, account));
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut account = Account::new(41, 1, &Pubkey::default());
|
2019-01-08 09:20:25 -08:00
|
|
|
account.executable = true;
|
2019-08-23 14:04:53 -07:00
|
|
|
account.rent_epoch = 1;
|
2019-02-14 09:57:54 -08:00
|
|
|
account.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,
|
|
|
|
);
|
|
|
|
|
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, 0);
|
2019-01-08 09:20:25 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
match &loaded_accounts[0] {
|
2019-12-07 11:54:10 -08:00
|
|
|
(
|
|
|
|
Ok((transaction_accounts, transaction_loaders, _transaction_rents)),
|
|
|
|
_hash_age_kind,
|
|
|
|
) => {
|
2020-05-01 17:23:33 -07:00
|
|
|
assert_eq!(transaction_accounts.len(), 3);
|
2019-08-23 14:04:53 -07:00
|
|
|
assert_eq!(transaction_accounts[0], accounts[0].1);
|
|
|
|
assert_eq!(transaction_loaders.len(), 2);
|
|
|
|
assert_eq!(transaction_loaders[0].len(), 1);
|
|
|
|
assert_eq!(transaction_loaders[1].len(), 2);
|
|
|
|
for loaders in transaction_loaders.iter() {
|
2019-06-10 19:50:02 -07:00
|
|
|
for (i, accounts_subset) in loaders.iter().enumerate() {
|
2019-01-08 09:20:25 -08:00
|
|
|
// +1 to skip first not loader account
|
2019-09-24 04:09:53 -07:00
|
|
|
assert_eq!(*accounts_subset, accounts[i + 1]);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-07 11:54:10 -08:00
|
|
|
(Err(e), _hash_age_kind) => Err(e).unwrap(),
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
}
|
2019-02-07 11:14:10 -08:00
|
|
|
|
2019-02-21 12:08:50 -08:00
|
|
|
#[test]
|
2019-10-23 22:01:22 -07:00
|
|
|
fn test_load_by_program_slot() {
|
2020-09-08 07:55:09 -07:00
|
|
|
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
2019-03-07 12:41:00 -08:00
|
|
|
|
2019-04-16 13:32:22 -07:00
|
|
|
// Load accounts owned by various programs into AccountsDB
|
|
|
|
let pubkey0 = Pubkey::new_rand();
|
|
|
|
let account0 = Account::new(1, 0, &Pubkey::new(&[2; 32]));
|
|
|
|
accounts.store_slow(0, &pubkey0, &account0);
|
2019-03-30 20:37:33 -07:00
|
|
|
let pubkey1 = Pubkey::new_rand();
|
2019-04-16 13:32:22 -07:00
|
|
|
let account1 = Account::new(1, 0, &Pubkey::new(&[2; 32]));
|
|
|
|
accounts.store_slow(0, &pubkey1, &account1);
|
2019-03-30 20:37:33 -07:00
|
|
|
let pubkey2 = Pubkey::new_rand();
|
2019-04-16 13:32:22 -07:00
|
|
|
let account2 = Account::new(1, 0, &Pubkey::new(&[3; 32]));
|
|
|
|
accounts.store_slow(0, &pubkey2, &account2);
|
2019-02-27 21:42:14 -08:00
|
|
|
|
2020-07-20 03:09:38 -07:00
|
|
|
let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::new(&[2; 32])));
|
2019-04-16 13:32:22 -07:00
|
|
|
assert_eq!(loaded.len(), 2);
|
2020-07-20 03:09:38 -07:00
|
|
|
let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::new(&[3; 32])));
|
2019-04-16 13:32:22 -07:00
|
|
|
assert_eq!(loaded, vec![(pubkey2, account2)]);
|
2020-07-20 03:09:38 -07:00
|
|
|
let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::new(&[4; 32])));
|
2019-04-16 13:32:22 -07:00
|
|
|
assert_eq!(loaded, vec![]);
|
2019-03-01 11:53:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-04-16 13:32:22 -07:00
|
|
|
fn test_accounts_account_not_found() {
|
2020-09-08 07:55:09 -07:00
|
|
|
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
2019-03-01 11:53:39 -08:00
|
|
|
let mut error_counters = ErrorCounters::default();
|
2019-04-15 17:15:50 -07:00
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
|
|
|
|
2019-04-16 13:32:22 -07:00
|
|
|
let accounts_index = accounts.accounts_db.accounts_index.read().unwrap();
|
|
|
|
let storage = accounts.accounts_db.storage.read().unwrap();
|
2019-03-01 11:53:39 -08:00
|
|
|
assert_eq!(
|
2019-04-16 13:32:22 -07:00
|
|
|
Accounts::load_executable_accounts(
|
2019-04-15 17:15:50 -07:00
|
|
|
&storage,
|
|
|
|
&ancestors,
|
|
|
|
&accounts_index,
|
|
|
|
&Pubkey::new_rand(),
|
|
|
|
&mut error_counters
|
|
|
|
),
|
2019-05-16 10:32:27 -07:00
|
|
|
Err(TransactionError::ProgramAccountNotFound)
|
2019-03-01 11:53:39 -08:00
|
|
|
);
|
|
|
|
assert_eq!(error_counters.account_not_found, 1);
|
|
|
|
}
|
2019-03-05 12:34:21 -08:00
|
|
|
|
2019-03-07 08:51:56 -08:00
|
|
|
#[test]
|
2019-11-22 08:56:00 -08:00
|
|
|
#[should_panic]
|
2019-12-19 16:39:30 -08:00
|
|
|
fn test_accounts_empty_bank_hash() {
|
2020-09-08 07:55:09 -07:00
|
|
|
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
2020-02-22 13:46:40 -08:00
|
|
|
accounts.bank_hash_at(1);
|
2019-11-22 08:56:00 -08:00
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
let account0 = Account::new(1, 0, &Pubkey::default());
|
|
|
|
let account1 = Account::new(2, 0, &Pubkey::default());
|
|
|
|
let account2 = Account::new(3, 0, &Pubkey::default());
|
|
|
|
let account3 = Account::new(4, 0, &Pubkey::default());
|
|
|
|
|
2020-09-08 07:55:09 -07:00
|
|
|
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
2019-06-27 14:25:10 -07:00
|
|
|
accounts.store_slow(0, &keypair0.pubkey(), &account0);
|
|
|
|
accounts.store_slow(0, &keypair1.pubkey(), &account1);
|
|
|
|
accounts.store_slow(0, &keypair2.pubkey(), &account2);
|
|
|
|
accounts.store_slow(0, &keypair3.pubkey(), &account3);
|
|
|
|
|
|
|
|
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,
|
|
|
|
);
|
|
|
|
let tx = Transaction::new(&[&keypair0], message, Hash::default());
|
2019-08-28 08:38:32 -07:00
|
|
|
let results0 = accounts.lock_accounts(&[tx.clone()], None);
|
2019-06-27 14:25:10 -07:00
|
|
|
|
|
|
|
assert!(results0[0].is_ok());
|
|
|
|
assert_eq!(
|
|
|
|
*accounts
|
2019-11-05 08:38:35 -08:00
|
|
|
.readonly_locks
|
2019-06-27 14:25:10 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2019-07-11 18:46:49 -07:00
|
|
|
.as_ref()
|
|
|
|
.unwrap()
|
2019-06-27 14:25:10 -07:00
|
|
|
.get(&keypair1.pubkey())
|
|
|
|
.unwrap()
|
|
|
|
.lock_count
|
|
|
|
.lock()
|
|
|
|
.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,
|
|
|
|
);
|
|
|
|
let tx0 = Transaction::new(&[&keypair2], message, Hash::default());
|
|
|
|
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,
|
|
|
|
);
|
|
|
|
let tx1 = Transaction::new(&[&keypair1], message, Hash::default());
|
|
|
|
let txs = vec![tx0, tx1];
|
2019-08-28 08:38:32 -07:00
|
|
|
let results1 = accounts.lock_accounts(&txs, None);
|
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
|
2019-11-05 08:38:35 -08:00
|
|
|
.readonly_locks
|
2019-06-27 14:25:10 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2019-07-11 18:46:49 -07:00
|
|
|
.as_ref()
|
|
|
|
.unwrap()
|
2019-06-27 14:25:10 -07:00
|
|
|
.get(&keypair1.pubkey())
|
|
|
|
.unwrap()
|
|
|
|
.lock_count
|
|
|
|
.lock()
|
|
|
|
.unwrap(),
|
|
|
|
2
|
|
|
|
);
|
|
|
|
|
2019-09-16 21:45:16 -07:00
|
|
|
accounts.unlock_accounts(&[tx], None, &results0);
|
|
|
|
accounts.unlock_accounts(&txs, None, &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,
|
|
|
|
);
|
|
|
|
let tx = Transaction::new(&[&keypair1], message, Hash::default());
|
2019-08-28 08:38:32 -07:00
|
|
|
let results2 = accounts.lock_accounts(&[tx], None);
|
2019-06-27 14:25:10 -07:00
|
|
|
|
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
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
// Check that read-only locks are still cached in accounts struct
|
|
|
|
let readonly_locks = accounts.readonly_locks.read().unwrap();
|
|
|
|
let readonly_locks = readonly_locks.as_ref().unwrap();
|
|
|
|
let keypair1_lock = readonly_locks.get(&keypair1.pubkey());
|
2019-06-27 14:25:10 -07:00
|
|
|
assert!(keypair1_lock.is_some());
|
|
|
|
assert_eq!(*keypair1_lock.unwrap().lock_count.lock().unwrap(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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();
|
|
|
|
|
|
|
|
let account0 = Account::new(1, 0, &Pubkey::default());
|
|
|
|
let account1 = Account::new(2, 0, &Pubkey::default());
|
|
|
|
let account2 = Account::new(3, 0, &Pubkey::default());
|
|
|
|
|
2020-09-08 07:55:09 -07:00
|
|
|
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
2019-06-27 14:25:10 -07:00
|
|
|
accounts.store_slow(0, &keypair0.pubkey(), &account0);
|
|
|
|
accounts.store_slow(0, &keypair1.pubkey(), &account1);
|
|
|
|
accounts.store_slow(0, &keypair2.pubkey(), &account2);
|
|
|
|
|
|
|
|
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,
|
|
|
|
);
|
2019-11-05 08:38:35 -08:00
|
|
|
let readonly_tx = Transaction::new(&[&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,
|
|
|
|
);
|
2019-11-05 08:38:35 -08:00
|
|
|
let writable_tx = Transaction::new(&[&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()];
|
2019-08-28 08:38:32 -07:00
|
|
|
let results = accounts_clone.clone().lock_accounts(&txs, None);
|
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);
|
|
|
|
}
|
|
|
|
}
|
2019-09-16 21:45:16 -07:00
|
|
|
accounts_clone.unlock_accounts(&txs, None, &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()];
|
2019-08-28 08:38:32 -07:00
|
|
|
let results = accounts_arc.clone().lock_accounts(&txs, None);
|
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));
|
|
|
|
}
|
2019-09-16 21:45:16 -07:00
|
|
|
accounts_arc.unlock_accounts(&txs, None, &results);
|
2019-06-27 14:25:10 -07:00
|
|
|
thread::sleep(time::Duration::from_millis(50));
|
|
|
|
}
|
|
|
|
exit.store(true, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
let pubkey = Pubkey::new_rand();
|
|
|
|
|
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,
|
|
|
|
);
|
2019-06-27 14:25:10 -07:00
|
|
|
let tx0 = Transaction::new(&[&keypair0], message, Hash::default());
|
|
|
|
|
|
|
|
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,
|
|
|
|
);
|
2019-06-27 14:25:10 -07:00
|
|
|
let tx1 = Transaction::new(&[&keypair1], message, Hash::default());
|
2019-06-10 19:50:02 -07:00
|
|
|
let txs = vec![tx0, tx1];
|
|
|
|
|
2019-12-07 11:54:10 -08:00
|
|
|
let loaders = vec![
|
|
|
|
(Ok(()), Some(HashAgeKind::Extant)),
|
|
|
|
(Ok(()), Some(HashAgeKind::Extant)),
|
|
|
|
];
|
2019-06-10 19:50:02 -07:00
|
|
|
|
|
|
|
let account0 = Account::new(1, 0, &Pubkey::default());
|
|
|
|
let account1 = Account::new(2, 0, &Pubkey::default());
|
|
|
|
let account2 = Account::new(3, 0, &Pubkey::default());
|
|
|
|
|
2019-08-23 14:04:53 -07:00
|
|
|
let transaction_accounts0 = vec![account0, account2.clone()];
|
|
|
|
let transaction_loaders0 = vec![];
|
2019-11-20 12:27:02 -08:00
|
|
|
let transaction_rent0 = 0;
|
2019-12-07 11:54:10 -08:00
|
|
|
let loaded0 = (
|
|
|
|
Ok((
|
|
|
|
transaction_accounts0,
|
|
|
|
transaction_loaders0,
|
|
|
|
transaction_rent0,
|
|
|
|
)),
|
|
|
|
Some(HashAgeKind::Extant),
|
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2020-05-15 09:35:43 -07:00
|
|
|
let transaction_accounts1 = vec![account1, account2];
|
2019-08-23 14:04:53 -07:00
|
|
|
let transaction_loaders1 = vec![];
|
2019-11-20 12:27:02 -08:00
|
|
|
let transaction_rent1 = 0;
|
2019-12-07 11:54:10 -08:00
|
|
|
let loaded1 = (
|
|
|
|
Ok((
|
|
|
|
transaction_accounts1,
|
|
|
|
transaction_loaders1,
|
|
|
|
transaction_rent1,
|
|
|
|
)),
|
|
|
|
Some(HashAgeKind::Extant),
|
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
|
2019-06-27 14:25:10 -07:00
|
|
|
let mut loaded = vec![loaded0, loaded1];
|
|
|
|
|
2020-09-08 07:55:09 -07:00
|
|
|
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
2019-07-11 18:46:49 -07:00
|
|
|
{
|
2019-11-05 08:38:35 -08:00
|
|
|
let mut readonly_locks = accounts.readonly_locks.write().unwrap();
|
|
|
|
let readonly_locks = readonly_locks.as_mut().unwrap();
|
|
|
|
readonly_locks.insert(
|
2019-07-11 18:46:49 -07:00
|
|
|
pubkey,
|
2019-11-05 08:38:35 -08:00
|
|
|
ReadonlyLock {
|
2019-07-11 18:46:49 -07:00
|
|
|
lock_count: Mutex::new(1),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2020-01-10 15:57:31 -08:00
|
|
|
let collected_accounts = accounts.collect_accounts_to_store(
|
|
|
|
&txs,
|
|
|
|
None,
|
|
|
|
&loaders,
|
|
|
|
&mut loaded,
|
|
|
|
&rent_collector,
|
2020-07-09 13:22:21 -07:00
|
|
|
&(Hash::default(), FeeCalculator::default()),
|
2020-07-15 14:45:30 -07:00
|
|
|
true,
|
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
|
|
|
|
2019-11-05 08:38:35 -08:00
|
|
|
// Ensure readonly_lock reflects lock
|
|
|
|
let readonly_locks = accounts.readonly_locks.read().unwrap();
|
|
|
|
let readonly_locks = readonly_locks.as_ref().unwrap();
|
2019-06-27 14:25:10 -07:00
|
|
|
assert_eq!(
|
2019-11-05 08:38:35 -08:00
|
|
|
*readonly_locks
|
2019-06-27 14:25:10 -07:00
|
|
|
.get(&pubkey)
|
|
|
|
.unwrap()
|
2019-11-05 08:38:35 -08:00
|
|
|
.lock_count
|
|
|
|
.lock()
|
|
|
|
.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
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_has_duplicates() {
|
|
|
|
assert!(!Accounts::has_duplicates(&[1, 2]));
|
|
|
|
assert!(Accounts::has_duplicates(&[1, 2, 1]));
|
|
|
|
}
|
2020-07-15 06:49:22 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn huge_clean() {
|
|
|
|
solana_logger::setup();
|
2020-09-08 07:55:09 -07:00
|
|
|
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
2020-07-15 06:49:22 -07:00
|
|
|
let mut old_pubkey = Pubkey::default();
|
|
|
|
let zero_account = Account::new(0, 0, &Account::default().owner);
|
|
|
|
info!("storing..");
|
|
|
|
for i in 0..2_000 {
|
|
|
|
let pubkey = Pubkey::new_rand();
|
|
|
|
let account = Account::new((i + 1) as u64, 0, &Account::default().owner);
|
|
|
|
accounts.store_slow(i, &pubkey, &account);
|
|
|
|
accounts.store_slow(i, &old_pubkey, &zero_account);
|
|
|
|
old_pubkey = pubkey;
|
|
|
|
accounts.add_root(i);
|
|
|
|
if i % 1_000 == 0 {
|
|
|
|
info!(" store {}", i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
info!("done..cleaning..");
|
2020-09-28 16:04:46 -07:00
|
|
|
accounts.accounts_db.clean_accounts(None);
|
2020-07-15 06:49:22 -07:00
|
|
|
}
|
2020-09-19 12:17:46 -07:00
|
|
|
|
|
|
|
fn load_accounts_no_store(
|
|
|
|
accounts: &Accounts,
|
|
|
|
tx: Transaction,
|
|
|
|
) -> Vec<(Result<TransactionLoadResult>, Option<HashAgeKind>)> {
|
|
|
|
let rent_collector = RentCollector::default();
|
|
|
|
let fee_calculator = FeeCalculator::new(10);
|
|
|
|
let mut hash_queue = BlockhashQueue::new(100);
|
|
|
|
hash_queue.register_hash(&tx.message().recent_blockhash, &fee_calculator);
|
|
|
|
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
accounts.load_accounts(
|
|
|
|
&ancestors,
|
|
|
|
&[tx],
|
|
|
|
None,
|
|
|
|
vec![(Ok(()), Some(HashAgeKind::Extant))],
|
|
|
|
&hash_queue,
|
|
|
|
&mut error_counters,
|
|
|
|
&rent_collector,
|
2020-09-29 20:18:28 -07:00
|
|
|
&FeatureSet::all_enabled(),
|
2020-09-19 12:17:46 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_instructions() {
|
|
|
|
solana_logger::setup();
|
|
|
|
let accounts = Accounts::new(Vec::new(), &ClusterType::Development);
|
|
|
|
|
|
|
|
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],
|
|
|
|
&[Pubkey::new_rand(), instructions_key],
|
|
|
|
Hash::default(),
|
|
|
|
vec![native_loader::id()],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
|
|
|
|
let loaded_accounts = load_accounts_no_store(&accounts, tx);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
assert!(loaded_accounts[0].0.is_err());
|
|
|
|
}
|
2020-09-29 20:34:07 -07:00
|
|
|
|
|
|
|
fn create_accounts_prepare_if_nonce_account() -> (Pubkey, Account, Account, Hash, FeeCalculator)
|
|
|
|
{
|
|
|
|
let data = nonce::state::Versions::new_current(nonce::State::Initialized(
|
|
|
|
nonce::state::Data::default(),
|
|
|
|
));
|
|
|
|
let account = Account::new_data(42, &data, &system_program::id()).unwrap();
|
|
|
|
let pre_account = Account {
|
|
|
|
lamports: 43,
|
|
|
|
..account.clone()
|
|
|
|
};
|
|
|
|
(
|
|
|
|
Pubkey::default(),
|
|
|
|
pre_account,
|
|
|
|
account,
|
|
|
|
Hash::new(&[1u8; 32]),
|
|
|
|
FeeCalculator {
|
|
|
|
lamports_per_signature: 1234,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_prepare_if_nonce_account_test(
|
|
|
|
account: &mut Account,
|
|
|
|
account_pubkey: &Pubkey,
|
|
|
|
tx_result: &Result<()>,
|
|
|
|
maybe_nonce: Option<(&Pubkey, &Account)>,
|
|
|
|
last_blockhash_with_fee_calculator: &(Hash, FeeCalculator),
|
|
|
|
expect_account: &Account,
|
|
|
|
) -> bool {
|
|
|
|
// Verify expect_account's relationship
|
|
|
|
match maybe_nonce {
|
|
|
|
Some((nonce_pubkey, _nonce_account))
|
|
|
|
if nonce_pubkey == account_pubkey && tx_result.is_ok() =>
|
|
|
|
{
|
|
|
|
assert_eq!(expect_account, account) // Account update occurs in system_instruction_processor
|
|
|
|
}
|
|
|
|
Some((nonce_pubkey, nonce_account)) if nonce_pubkey == account_pubkey => {
|
|
|
|
assert_ne!(expect_account, nonce_account)
|
|
|
|
}
|
|
|
|
_ => assert_eq!(expect_account, account),
|
|
|
|
}
|
|
|
|
|
|
|
|
prepare_if_nonce_account(
|
|
|
|
account,
|
|
|
|
account_pubkey,
|
|
|
|
tx_result,
|
|
|
|
maybe_nonce,
|
|
|
|
last_blockhash_with_fee_calculator,
|
|
|
|
true,
|
|
|
|
);
|
|
|
|
expect_account == account
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_prepare_if_nonce_account_expected() {
|
|
|
|
let (
|
|
|
|
pre_account_pubkey,
|
|
|
|
pre_account,
|
|
|
|
mut post_account,
|
|
|
|
last_blockhash,
|
|
|
|
last_fee_calculator,
|
|
|
|
) = create_accounts_prepare_if_nonce_account();
|
|
|
|
let post_account_pubkey = pre_account_pubkey;
|
|
|
|
|
|
|
|
let mut expect_account = post_account.clone();
|
|
|
|
let data = nonce::state::Versions::new_current(nonce::State::Initialized(
|
|
|
|
nonce::state::Data::default(),
|
|
|
|
));
|
|
|
|
expect_account.set_state(&data).unwrap();
|
|
|
|
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
|
|
|
&mut post_account,
|
|
|
|
&post_account_pubkey,
|
|
|
|
&Ok(()),
|
|
|
|
Some((&pre_account_pubkey, &pre_account)),
|
|
|
|
&(last_blockhash, last_fee_calculator),
|
|
|
|
&expect_account,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_prepare_if_nonce_account_not_nonce_tx() {
|
|
|
|
let (pre_account_pubkey, _pre_account, _post_account, last_blockhash, last_fee_calculator) =
|
|
|
|
create_accounts_prepare_if_nonce_account();
|
|
|
|
let post_account_pubkey = pre_account_pubkey;
|
|
|
|
|
|
|
|
let mut post_account = Account::default();
|
|
|
|
let expect_account = post_account.clone();
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
|
|
|
&mut post_account,
|
|
|
|
&post_account_pubkey,
|
|
|
|
&Ok(()),
|
|
|
|
None,
|
|
|
|
&(last_blockhash, last_fee_calculator),
|
|
|
|
&expect_account,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_prepare_if_nonce_account_not_nonce_pubkey() {
|
|
|
|
let (
|
|
|
|
pre_account_pubkey,
|
|
|
|
pre_account,
|
|
|
|
mut post_account,
|
|
|
|
last_blockhash,
|
|
|
|
last_fee_calculator,
|
|
|
|
) = create_accounts_prepare_if_nonce_account();
|
|
|
|
|
|
|
|
let expect_account = post_account.clone();
|
|
|
|
// Wrong key
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
|
|
|
&mut post_account,
|
|
|
|
&Pubkey::new(&[1u8; 32]),
|
|
|
|
&Ok(()),
|
|
|
|
Some((&pre_account_pubkey, &pre_account)),
|
|
|
|
&(last_blockhash, last_fee_calculator),
|
|
|
|
&expect_account,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_prepare_if_nonce_account_tx_error() {
|
|
|
|
let (
|
|
|
|
pre_account_pubkey,
|
|
|
|
pre_account,
|
|
|
|
mut post_account,
|
|
|
|
last_blockhash,
|
|
|
|
last_fee_calculator,
|
|
|
|
) = create_accounts_prepare_if_nonce_account();
|
|
|
|
let post_account_pubkey = pre_account_pubkey;
|
|
|
|
|
|
|
|
let mut expect_account = pre_account.clone();
|
|
|
|
expect_account
|
|
|
|
.set_state(&nonce::state::Versions::new_current(
|
|
|
|
nonce::State::Initialized(nonce::state::Data {
|
|
|
|
blockhash: last_blockhash,
|
|
|
|
fee_calculator: last_fee_calculator.clone(),
|
|
|
|
..nonce::state::Data::default()
|
|
|
|
}),
|
|
|
|
))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(run_prepare_if_nonce_account_test(
|
|
|
|
&mut post_account,
|
|
|
|
&post_account_pubkey,
|
|
|
|
&Err(TransactionError::InstructionError(
|
|
|
|
0,
|
|
|
|
InstructionError::InvalidArgument,
|
|
|
|
)),
|
|
|
|
Some((&pre_account_pubkey, &pre_account)),
|
|
|
|
&(last_blockhash, last_fee_calculator),
|
|
|
|
&expect_account,
|
|
|
|
));
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|