2018-12-17 12:41:23 -08:00
|
|
|
use crate::bank::BankError;
|
|
|
|
use crate::bank::Result;
|
|
|
|
use crate::counter::Counter;
|
2019-02-07 11:14:10 -08:00
|
|
|
use crate::runtime::has_duplicates;
|
2018-12-17 12:41:23 -08:00
|
|
|
use bincode::serialize;
|
|
|
|
use hashbrown::{HashMap, HashSet};
|
|
|
|
use log::Level;
|
|
|
|
use solana_sdk::account::Account;
|
|
|
|
use solana_sdk::hash::{hash, Hash};
|
2019-02-07 08:03:40 -08:00
|
|
|
use solana_sdk::native_loader;
|
2018-12-17 12:41:23 -08:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
|
|
|
use solana_sdk::transaction::Transaction;
|
|
|
|
use std::collections::BTreeMap;
|
2019-01-29 16:33:28 -08:00
|
|
|
use std::ops::Deref;
|
2018-12-17 12:41:23 -08:00
|
|
|
use std::sync::atomic::AtomicUsize;
|
|
|
|
use std::sync::{Mutex, RwLock};
|
|
|
|
|
2019-01-06 22:06:55 -08:00
|
|
|
pub type InstructionAccounts = Vec<Account>;
|
|
|
|
pub type InstructionLoaders = Vec<Vec<(Pubkey, Account)>>;
|
|
|
|
|
2019-01-08 09:20:25 -08:00
|
|
|
#[derive(Debug, Default)]
|
2018-12-17 12:41:23 -08:00
|
|
|
pub struct ErrorCounters {
|
|
|
|
pub account_not_found: usize,
|
|
|
|
pub account_in_use: usize,
|
2019-02-07 11:14:10 -08:00
|
|
|
pub account_loaded_twice: usize,
|
2018-12-17 12:41:23 -08:00
|
|
|
pub last_id_not_found: usize,
|
2019-01-29 16:33:28 -08:00
|
|
|
pub last_id_too_old: usize,
|
2018-12-17 12:41:23 -08:00
|
|
|
pub reserve_last_id: usize,
|
|
|
|
pub insufficient_funds: usize,
|
|
|
|
pub duplicate_signature: usize,
|
2019-01-08 09:20:25 -08:00
|
|
|
pub call_chain_too_deep: usize,
|
|
|
|
pub missing_signature_for_fee: usize,
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// This structure handles the load/store of the accounts
|
|
|
|
pub struct AccountsDB {
|
|
|
|
/// Mapping of known public keys/IDs to accounts
|
|
|
|
pub accounts: HashMap<Pubkey, Account>,
|
|
|
|
|
|
|
|
/// The number of transactions the bank has processed without error since the
|
|
|
|
/// start of the ledger.
|
|
|
|
transaction_count: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This structure handles synchronization for db
|
|
|
|
pub struct Accounts {
|
|
|
|
pub accounts_db: RwLock<AccountsDB>,
|
|
|
|
|
|
|
|
/// set of accounts which are currently in the pipeline
|
|
|
|
account_locks: Mutex<HashSet<Pubkey>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for AccountsDB {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
accounts: HashMap::new(),
|
|
|
|
transaction_count: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Accounts {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
account_locks: Mutex::new(HashSet::new()),
|
|
|
|
accounts_db: RwLock::new(AccountsDB::default()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AccountsDB {
|
|
|
|
pub fn hash_internal_state(&self) -> Hash {
|
|
|
|
let mut ordered_accounts = BTreeMap::new();
|
|
|
|
|
|
|
|
// only hash internal state of the part being voted upon, i.e. since last
|
|
|
|
// checkpoint
|
|
|
|
for (pubkey, account) in &self.accounts {
|
|
|
|
ordered_accounts.insert(*pubkey, account.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
hash(&serialize(&ordered_accounts).unwrap())
|
|
|
|
}
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
fn load<U>(checkpoints: &[U], pubkey: &Pubkey) -> Option<Account>
|
|
|
|
where
|
|
|
|
U: Deref<Target = Self>,
|
|
|
|
{
|
|
|
|
for db in checkpoints {
|
|
|
|
if let Some(account) = db.accounts.get(pubkey) {
|
|
|
|
return Some(account.clone());
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2019-01-29 16:33:28 -08:00
|
|
|
/// Store the account update. If the update is to delete the account because the token balance
|
|
|
|
/// is 0, purge needs to be set to true for the delete to occur in place.
|
|
|
|
pub fn store(&mut self, purge: bool, pubkey: &Pubkey, account: &Account) {
|
2018-12-17 12:41:23 -08:00
|
|
|
if account.tokens == 0 {
|
2019-01-29 16:33:28 -08:00
|
|
|
if purge {
|
|
|
|
// purge if balance is 0 and no checkpoints
|
|
|
|
self.accounts.remove(pubkey);
|
|
|
|
} else {
|
|
|
|
// store default account if balance is 0 and there's a checkpoint
|
|
|
|
self.accounts.insert(pubkey.clone(), Account::default());
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
} else {
|
|
|
|
self.accounts.insert(pubkey.clone(), account.clone());
|
|
|
|
}
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
pub fn store_accounts(
|
|
|
|
&mut self,
|
2019-01-29 16:33:28 -08:00
|
|
|
purge: bool,
|
2018-12-17 12:41:23 -08:00
|
|
|
txs: &[Transaction],
|
|
|
|
res: &[Result<()>],
|
2019-01-06 22:06:55 -08:00
|
|
|
loaded: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
2018-12-17 12:41:23 -08:00
|
|
|
) {
|
2019-01-06 22:06:55 -08:00
|
|
|
for (i, raccs) in loaded.iter().enumerate() {
|
|
|
|
if res[i].is_err() || raccs.is_err() {
|
2018-12-17 12:41:23 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let tx = &txs[i];
|
2019-01-29 16:33:28 -08:00
|
|
|
let acc = raccs.as_ref().unwrap();
|
|
|
|
for (key, account) in tx.account_keys.iter().zip(acc.0.iter()) {
|
|
|
|
self.store(purge, key, account);
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-29 16:33:28 -08:00
|
|
|
fn load_tx_accounts<U>(
|
|
|
|
checkpoints: &[U],
|
2018-12-17 12:41:23 -08:00
|
|
|
tx: &Transaction,
|
|
|
|
error_counters: &mut ErrorCounters,
|
2019-01-29 16:33:28 -08:00
|
|
|
) -> Result<Vec<Account>>
|
|
|
|
where
|
|
|
|
U: Deref<Target = Self>,
|
|
|
|
{
|
2018-12-17 12:41:23 -08:00
|
|
|
// Copy all the accounts
|
|
|
|
if tx.signatures.is_empty() && tx.fee != 0 {
|
|
|
|
Err(BankError::MissingSignatureForFee)
|
|
|
|
} else {
|
2019-02-07 11:14:10 -08:00
|
|
|
// Check for unique account keys
|
|
|
|
if has_duplicates(&tx.account_keys) {
|
|
|
|
error_counters.account_loaded_twice += 1;
|
|
|
|
return Err(BankError::AccountLoadedTwice);
|
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
// 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
|
2019-01-29 16:33:28 -08:00
|
|
|
let mut called_accounts: Vec<Account> = vec![];
|
|
|
|
for key in &tx.account_keys {
|
|
|
|
called_accounts.push(Self::load(checkpoints, key).unwrap_or_default());
|
|
|
|
}
|
|
|
|
if called_accounts.is_empty() || called_accounts[0].tokens == 0 {
|
|
|
|
error_counters.account_not_found += 1;
|
|
|
|
Err(BankError::AccountNotFound)
|
|
|
|
} else if called_accounts[0].tokens < tx.fee {
|
|
|
|
error_counters.insufficient_funds += 1;
|
|
|
|
Err(BankError::InsufficientFundsForFee)
|
|
|
|
} else {
|
|
|
|
called_accounts[0].tokens -= tx.fee;
|
|
|
|
Ok(called_accounts)
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
}
|
2019-01-04 16:39:04 -08:00
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
fn load_executable_accounts<U>(
|
|
|
|
checkpoints: &[U],
|
2019-01-08 09:20:25 -08:00
|
|
|
mut program_id: Pubkey,
|
|
|
|
error_counters: &mut ErrorCounters,
|
2019-01-29 16:33:28 -08:00
|
|
|
) -> Result<Vec<(Pubkey, Account)>>
|
|
|
|
where
|
|
|
|
U: Deref<Target = Self>,
|
|
|
|
{
|
2019-01-06 22:06:55 -08:00
|
|
|
let mut accounts = Vec::new();
|
|
|
|
let mut depth = 0;
|
|
|
|
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-01-06 22:06:55 -08:00
|
|
|
return Err(BankError::CallChainTooDeep);
|
|
|
|
}
|
|
|
|
depth += 1;
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
let program = match Self::load(checkpoints, &program_id) {
|
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;
|
|
|
|
return Err(BankError::AccountNotFound);
|
|
|
|
}
|
2019-01-06 22:06:55 -08:00
|
|
|
};
|
|
|
|
if !program.executable || program.loader == Pubkey::default() {
|
2019-01-08 09:20:25 -08:00
|
|
|
error_counters.account_not_found += 1;
|
2019-01-06 22:06:55 -08:00
|
|
|
return Err(BankError::AccountNotFound);
|
|
|
|
}
|
|
|
|
|
|
|
|
// add loader to chain
|
|
|
|
accounts.insert(0, (program_id, program.clone()));
|
|
|
|
|
|
|
|
program_id = program.loader;
|
|
|
|
}
|
|
|
|
Ok(accounts)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// For each program_id in the transaction, load its loaders.
|
2019-01-29 16:33:28 -08:00
|
|
|
fn load_loaders<U>(
|
|
|
|
checkpoints: &[U],
|
2019-01-08 09:20:25 -08:00
|
|
|
tx: &Transaction,
|
|
|
|
error_counters: &mut ErrorCounters,
|
2019-01-29 16:33:28 -08:00
|
|
|
) -> Result<Vec<Vec<(Pubkey, Account)>>>
|
|
|
|
where
|
|
|
|
U: Deref<Target = Self>,
|
|
|
|
{
|
2019-01-06 22:06:55 -08:00
|
|
|
tx.instructions
|
|
|
|
.iter()
|
|
|
|
.map(|ix| {
|
2019-01-08 09:20:25 -08:00
|
|
|
if tx.program_ids.len() <= ix.program_ids_index as usize {
|
|
|
|
error_counters.account_not_found += 1;
|
|
|
|
return Err(BankError::AccountNotFound);
|
|
|
|
}
|
2019-01-06 22:06:55 -08:00
|
|
|
let program_id = tx.program_ids[ix.program_ids_index as usize];
|
2019-01-29 16:33:28 -08:00
|
|
|
Self::load_executable_accounts(checkpoints, program_id, error_counters)
|
2019-01-06 22:06:55 -08:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
fn load_accounts<U>(
|
|
|
|
checkpoints: &[U],
|
2018-12-17 12:41:23 -08:00
|
|
|
txs: &[Transaction],
|
2019-01-06 22:06:55 -08:00
|
|
|
lock_results: Vec<Result<()>>,
|
2018-12-17 12:41:23 -08:00
|
|
|
error_counters: &mut ErrorCounters,
|
2019-01-29 16:33:28 -08:00
|
|
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>>
|
|
|
|
where
|
|
|
|
U: Deref<Target = Self>,
|
|
|
|
{
|
2018-12-17 12:41:23 -08:00
|
|
|
txs.iter()
|
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 {
|
2019-01-06 22:06:55 -08:00
|
|
|
(tx, Ok(())) => {
|
2019-01-29 16:33:28 -08:00
|
|
|
let accounts = Self::load_tx_accounts(checkpoints, tx, error_counters)?;
|
|
|
|
let loaders = Self::load_loaders(checkpoints, tx, error_counters)?;
|
2019-01-06 22:06:55 -08:00
|
|
|
Ok((accounts, loaders))
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
(_, Err(e)) => Err(e),
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
pub fn increment_transaction_count(&mut self, tx_count: usize) {
|
|
|
|
self.transaction_count += tx_count as u64
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
pub fn transaction_count(&self) -> u64 {
|
|
|
|
self.transaction_count
|
|
|
|
}
|
2019-01-29 16:33:28 -08:00
|
|
|
pub fn account_values_slow(&self) -> Vec<(Pubkey, solana_sdk::account::Account)> {
|
|
|
|
self.accounts.iter().map(|(x, y)| (*x, y.clone())).collect()
|
2019-01-29 11:18:56 -08:00
|
|
|
}
|
2019-01-29 16:33:28 -08:00
|
|
|
fn merge(&mut self, other: Self) {
|
|
|
|
self.transaction_count += other.transaction_count;
|
|
|
|
self.accounts.extend(other.accounts)
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-01-29 16:33:28 -08:00
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
impl Accounts {
|
|
|
|
/// Slow because lock is held for 1 operation insted of many
|
|
|
|
pub fn load_slow<U>(checkpoints: &[U], pubkey: &Pubkey) -> Option<Account>
|
|
|
|
where
|
|
|
|
U: Deref<Target = Self>,
|
|
|
|
{
|
|
|
|
let dbs: Vec<_> = checkpoints
|
|
|
|
.iter()
|
|
|
|
.map(|obj| obj.accounts_db.read().unwrap())
|
|
|
|
.collect();
|
|
|
|
AccountsDB::load(&dbs, pubkey)
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-01-29 16:33:28 -08:00
|
|
|
/// Slow because lock is held for 1 operation insted of many
|
|
|
|
/// * purge - if the account token value is 0 and purge is true then delete the account.
|
|
|
|
/// purge should be set to false for overlays, and true for the root checkpoint.
|
|
|
|
pub fn store_slow(&self, purge: bool, pubkey: &Pubkey, account: &Account) {
|
|
|
|
self.accounts_db
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.store(purge, pubkey, account)
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
fn lock_account(
|
|
|
|
account_locks: &mut HashSet<Pubkey>,
|
|
|
|
keys: &[Pubkey],
|
|
|
|
error_counters: &mut ErrorCounters,
|
|
|
|
) -> Result<()> {
|
|
|
|
// Copy all the accounts
|
|
|
|
for k in keys {
|
|
|
|
if account_locks.contains(k) {
|
|
|
|
error_counters.account_in_use += 1;
|
|
|
|
return Err(BankError::AccountInUse);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for k in keys {
|
|
|
|
account_locks.insert(*k);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unlock_account(tx: &Transaction, result: &Result<()>, account_locks: &mut HashSet<Pubkey>) {
|
|
|
|
match result {
|
|
|
|
Err(BankError::AccountInUse) => (),
|
|
|
|
_ => {
|
|
|
|
for k in &tx.account_keys {
|
|
|
|
account_locks.remove(k);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
pub fn hash_internal_state(&self) -> Hash {
|
|
|
|
self.accounts_db.read().unwrap().hash_internal_state()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This function will prevent multiple threads from modifying the same account state at the
|
|
|
|
/// same time
|
|
|
|
#[must_use]
|
|
|
|
pub fn lock_accounts(&self, txs: &[Transaction]) -> Vec<Result<()>> {
|
|
|
|
let mut account_locks = self.account_locks.lock().unwrap();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
let rv = txs
|
|
|
|
.iter()
|
|
|
|
.map(|tx| Self::lock_account(&mut account_locks, &tx.account_keys, &mut error_counters))
|
|
|
|
.collect();
|
|
|
|
if error_counters.account_in_use != 0 {
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-process_transactions-account_in_use",
|
|
|
|
error_counters.account_in_use
|
|
|
|
);
|
|
|
|
}
|
|
|
|
rv
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Once accounts are unlocked, new transactions that modify that state can enter the pipeline
|
|
|
|
pub fn unlock_accounts(&self, txs: &[Transaction], results: &[Result<()>]) {
|
|
|
|
let mut account_locks = self.account_locks.lock().unwrap();
|
|
|
|
debug!("bank unlock accounts");
|
|
|
|
txs.iter()
|
|
|
|
.zip(results.iter())
|
|
|
|
.for_each(|(tx, result)| Self::unlock_account(tx, result, &mut account_locks));
|
|
|
|
}
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
pub fn load_accounts<U>(
|
|
|
|
checkpoints: &[U],
|
2018-12-17 12:41:23 -08:00
|
|
|
txs: &[Transaction],
|
2019-01-29 16:33:28 -08:00
|
|
|
results: Vec<Result<()>>,
|
2018-12-17 12:41:23 -08:00
|
|
|
error_counters: &mut ErrorCounters,
|
2019-01-29 16:33:28 -08:00
|
|
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>>
|
|
|
|
where
|
|
|
|
U: Deref<Target = Self>,
|
|
|
|
{
|
|
|
|
let dbs: Vec<_> = checkpoints
|
|
|
|
.iter()
|
|
|
|
.map(|obj| obj.accounts_db.read().unwrap())
|
|
|
|
.collect();
|
|
|
|
AccountsDB::load_accounts(&dbs, txs, results, error_counters)
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
/// Store the accounts into the DB
|
|
|
|
/// * purge - if the account token value is 0 and purge is true then delete the account.
|
|
|
|
/// purge should be set to false for overlays, and true for the root checkpoint.
|
2018-12-17 12:41:23 -08:00
|
|
|
pub fn store_accounts(
|
|
|
|
&self,
|
2019-01-29 16:33:28 -08:00
|
|
|
purge: bool,
|
2018-12-17 12:41:23 -08:00
|
|
|
txs: &[Transaction],
|
|
|
|
res: &[Result<()>],
|
2019-01-06 22:06:55 -08:00
|
|
|
loaded: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
2018-12-17 12:41:23 -08:00
|
|
|
) {
|
|
|
|
self.accounts_db
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
2019-01-29 16:33:28 -08:00
|
|
|
.store_accounts(purge, txs, res, loaded)
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn increment_transaction_count(&self, tx_count: usize) {
|
|
|
|
self.accounts_db
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.increment_transaction_count(tx_count)
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
pub fn transaction_count(&self) -> u64 {
|
|
|
|
self.accounts_db.read().unwrap().transaction_count()
|
|
|
|
}
|
2019-01-29 16:33:28 -08:00
|
|
|
/// accounts starts with an empty data structure for every fork
|
|
|
|
/// self is root, merge the fork into self
|
|
|
|
pub fn merge_into_root(&self, other: Self) {
|
|
|
|
assert!(other.account_locks.lock().unwrap().is_empty());
|
|
|
|
let db = other.accounts_db.into_inner().unwrap();
|
|
|
|
let mut mydb = self.accounts_db.write().unwrap();
|
|
|
|
mydb.merge(db)
|
|
|
|
}
|
|
|
|
pub fn copy_for_tpu(&self) -> Self {
|
|
|
|
//TODO: deprecate this in favor of forks and merge_into_root
|
|
|
|
let copy = Accounts::default();
|
|
|
|
|
|
|
|
{
|
|
|
|
let mut accounts_db = copy.accounts_db.write().unwrap();
|
|
|
|
for (key, val) in self.accounts_db.read().unwrap().accounts.iter() {
|
|
|
|
accounts_db.accounts.insert(key.clone(), val.clone());
|
|
|
|
}
|
|
|
|
accounts_db.transaction_count = self.transaction_count();
|
|
|
|
}
|
|
|
|
copy
|
|
|
|
}
|
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::*;
|
|
|
|
use solana_sdk::account::Account;
|
|
|
|
use solana_sdk::hash::Hash;
|
|
|
|
use solana_sdk::signature::Keypair;
|
|
|
|
use solana_sdk::signature::KeypairUtil;
|
|
|
|
use solana_sdk::transaction::Instruction;
|
|
|
|
use solana_sdk::transaction::Transaction;
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
#[test]
|
|
|
|
fn test_purge() {
|
|
|
|
let mut db = AccountsDB::default();
|
|
|
|
let key = Pubkey::default();
|
|
|
|
let account = Account::new(0, 0, Pubkey::default());
|
|
|
|
// accounts are deleted when their token value is 0 and purge is true
|
|
|
|
db.store(false, &key, &account);
|
|
|
|
assert_eq!(AccountsDB::load(&[&db], &key), Some(account.clone()));
|
|
|
|
// purge should be set to true for the root checkpoint
|
|
|
|
db.store(true, &key, &account);
|
|
|
|
assert_eq!(AccountsDB::load(&[&db], &key), None);
|
|
|
|
}
|
|
|
|
|
2019-01-08 09:20:25 -08:00
|
|
|
fn load_accounts(
|
|
|
|
tx: Transaction,
|
|
|
|
ka: &Vec<(Pubkey, Account)>,
|
|
|
|
error_counters: &mut ErrorCounters,
|
|
|
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>> {
|
|
|
|
let accounts = Accounts::default();
|
|
|
|
for ka in ka.iter() {
|
2019-01-29 16:33:28 -08:00
|
|
|
accounts.store_slow(true, &ka.0, &ka.1);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
2019-01-29 16:33:28 -08:00
|
|
|
Accounts::load_accounts(&[&accounts], &[tx], vec![Ok(())], error_counters)
|
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();
|
|
|
|
|
|
|
|
let instructions = vec![Instruction::new(1, &(), vec![0])];
|
2019-01-29 15:57:48 -08:00
|
|
|
let tx = Transaction::new_with_instructions::<Keypair>(
|
2019-01-08 09:20:25 -08:00
|
|
|
&[],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
0,
|
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);
|
|
|
|
assert_eq!(loaded_accounts[0], Err(BankError::AccountNotFound));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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();
|
|
|
|
|
|
|
|
let instructions = vec![Instruction::new(1, &(), vec![0])];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
0,
|
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);
|
|
|
|
assert_eq!(loaded_accounts[0], Err(BankError::AccountNotFound));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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]);
|
|
|
|
|
|
|
|
let account = Account::new(1, 1, Pubkey::default());
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let account = Account::new(2, 1, Pubkey::default());
|
|
|
|
accounts.push((key1, account));
|
|
|
|
|
|
|
|
let instructions = vec![Instruction::new(1, &(), vec![0])];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
0,
|
|
|
|
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);
|
|
|
|
assert_eq!(loaded_accounts[0], Err(BankError::AccountNotFound));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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();
|
|
|
|
|
|
|
|
let account = Account::new(1, 1, Pubkey::default());
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let instructions = vec![Instruction::new(1, &(), vec![0])];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
10,
|
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.insufficient_funds, 1);
|
2019-01-08 09:20:25 -08:00
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
assert_eq!(loaded_accounts[0], Err(BankError::InsufficientFundsForFee));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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]);
|
|
|
|
|
|
|
|
let account = Account::new(1, 1, Pubkey::default());
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let account = Account::new(2, 1, Pubkey::default());
|
|
|
|
accounts.push((key1, account));
|
|
|
|
|
|
|
|
let instructions = vec![Instruction::new(0, &(), vec![0, 1])];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[key1],
|
|
|
|
Hash::default(),
|
|
|
|
0,
|
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] {
|
|
|
|
Ok((a, l)) => {
|
|
|
|
assert_eq!(a.len(), 2);
|
|
|
|
assert_eq!(a[0], accounts[0].1);
|
|
|
|
assert_eq!(l.len(), 1);
|
|
|
|
assert_eq!(l[0].len(), 0);
|
|
|
|
}
|
|
|
|
Err(e) => Err(e).unwrap(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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]);
|
|
|
|
|
|
|
|
let account = Account::new(1, 1, Pubkey::default());
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(40, 1, Pubkey::default());
|
|
|
|
account.executable = true;
|
2019-02-07 08:03:40 -08:00
|
|
|
account.loader = native_loader::id();
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key1, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(41, 1, Pubkey::default());
|
|
|
|
account.executable = true;
|
|
|
|
account.loader = key1;
|
|
|
|
accounts.push((key2, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(42, 1, Pubkey::default());
|
|
|
|
account.executable = true;
|
|
|
|
account.loader = key2;
|
|
|
|
accounts.push((key3, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(43, 1, Pubkey::default());
|
|
|
|
account.executable = true;
|
|
|
|
account.loader = key3;
|
|
|
|
accounts.push((key4, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(44, 1, Pubkey::default());
|
|
|
|
account.executable = true;
|
|
|
|
account.loader = key4;
|
|
|
|
accounts.push((key5, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(45, 1, Pubkey::default());
|
|
|
|
account.executable = true;
|
|
|
|
account.loader = key5;
|
|
|
|
accounts.push((key6, account));
|
|
|
|
|
|
|
|
let instructions = vec![Instruction::new(0, &(), vec![0])];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
0,
|
|
|
|
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);
|
|
|
|
assert_eq!(loaded_accounts[0], Err(BankError::CallChainTooDeep));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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]);
|
|
|
|
|
|
|
|
let account = Account::new(1, 1, Pubkey::default());
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(40, 1, Pubkey::default());
|
|
|
|
account.executable = true;
|
|
|
|
account.loader = Pubkey::default();
|
|
|
|
accounts.push((key1, account));
|
|
|
|
|
|
|
|
let instructions = vec![Instruction::new(0, &(), vec![0])];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
0,
|
|
|
|
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
|
|
|
|
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);
|
|
|
|
assert_eq!(loaded_accounts[0], Err(BankError::AccountNotFound));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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]);
|
|
|
|
|
|
|
|
let account = Account::new(1, 1, Pubkey::default());
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(40, 1, Pubkey::default());
|
2019-02-07 08:03:40 -08:00
|
|
|
account.loader = native_loader::id();
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key1, account));
|
|
|
|
|
|
|
|
let instructions = vec![Instruction::new(0, &(), vec![0])];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
0,
|
|
|
|
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
|
|
|
|
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);
|
|
|
|
assert_eq!(loaded_accounts[0], Err(BankError::AccountNotFound));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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]);
|
|
|
|
let key3 = Pubkey::new(&[7u8; 32]);
|
|
|
|
|
|
|
|
let account = Account::new(1, 1, Pubkey::default());
|
|
|
|
accounts.push((key0, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(40, 1, Pubkey::default());
|
|
|
|
account.executable = true;
|
2019-02-07 08:03:40 -08:00
|
|
|
account.loader = native_loader::id();
|
2019-01-08 09:20:25 -08:00
|
|
|
accounts.push((key1, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(41, 1, Pubkey::default());
|
|
|
|
account.executable = true;
|
|
|
|
account.loader = key1;
|
|
|
|
accounts.push((key2, account));
|
|
|
|
|
|
|
|
let mut account = Account::new(42, 1, Pubkey::default());
|
|
|
|
account.executable = true;
|
|
|
|
account.loader = key2;
|
|
|
|
accounts.push((key3, account));
|
|
|
|
|
|
|
|
let instructions = vec![
|
|
|
|
Instruction::new(0, &(), vec![0]),
|
|
|
|
Instruction::new(1, &(), vec![0]),
|
|
|
|
];
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[],
|
|
|
|
Hash::default(),
|
|
|
|
0,
|
|
|
|
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] {
|
|
|
|
Ok((a, l)) => {
|
|
|
|
assert_eq!(a.len(), 1);
|
|
|
|
assert_eq!(a[0], accounts[0].1);
|
|
|
|
assert_eq!(l.len(), 2);
|
|
|
|
assert_eq!(l[0].len(), 1);
|
|
|
|
assert_eq!(l[1].len(), 2);
|
|
|
|
for instruction_loaders in l.iter() {
|
|
|
|
for (i, a) in instruction_loaders.iter().enumerate() {
|
|
|
|
// +1 to skip first not loader account
|
|
|
|
assert_eq![a.1, accounts[i + 1].1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => Err(e).unwrap(),
|
|
|
|
}
|
|
|
|
}
|
2019-02-07 11:14:10 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_load_account_pay_to_self() {
|
|
|
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let pubkey = keypair.pubkey();
|
|
|
|
|
|
|
|
let account = Account::new(10, 1, Pubkey::default());
|
|
|
|
accounts.push((pubkey, account));
|
|
|
|
|
|
|
|
let instructions = vec![Instruction::new(0, &(), vec![0, 1])];
|
|
|
|
// Simulate pay-to-self transaction, which loads the same account twice
|
|
|
|
let tx = Transaction::new_with_instructions(
|
|
|
|
&[&keypair],
|
|
|
|
&[pubkey],
|
|
|
|
Hash::default(),
|
|
|
|
0,
|
|
|
|
vec![native_loader::id()],
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
|
|
|
|
|
|
|
assert_eq!(error_counters.account_loaded_twice, 1);
|
|
|
|
assert_eq!(loaded_accounts.len(), 1);
|
|
|
|
loaded_accounts[0].clone().unwrap_err();
|
|
|
|
assert_eq!(loaded_accounts[0], Err(BankError::AccountLoadedTwice));
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|