2019-04-16 13:32:22 -07:00
|
|
|
use crate::accounts_db::{
|
2019-05-30 21:31:35 -07:00
|
|
|
get_paths_vec, AccountInfo, AccountStorage, AccountsDB, AppendVecId, ErrorCounters,
|
2019-06-10 19:50:02 -07:00
|
|
|
InstructionAccounts, InstructionCredits, InstructionLoaders,
|
2019-04-16 13:32:22 -07:00
|
|
|
};
|
2019-04-15 17:15:50 -07:00
|
|
|
use crate::accounts_index::{AccountsIndex, Fork};
|
2019-04-16 13:32:22 -07:00
|
|
|
use crate::append_vec::StoredAccount;
|
2019-06-10 22:18:32 -07:00
|
|
|
use crate::blockhash_queue::BlockhashQueue;
|
2019-04-02 08:35:38 -07:00
|
|
|
use crate::message_processor::has_duplicates;
|
2018-12-17 12:41:23 -08:00
|
|
|
use bincode::serialize;
|
2019-02-27 20:57:01 -08:00
|
|
|
use log::*;
|
2019-05-17 07:00:06 -07:00
|
|
|
use solana_metrics::inc_new_counter_error;
|
2019-06-27 14:25:10 -07:00
|
|
|
use solana_sdk::account::Account;
|
2019-04-16 08:50:05 -07:00
|
|
|
use solana_sdk::hash::{Hash, Hasher};
|
2019-06-27 14:25:10 -07:00
|
|
|
use solana_sdk::message::Message;
|
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;
|
2019-02-20 21:31:24 -08:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
2019-05-31 12:26:45 -07:00
|
|
|
use solana_sdk::syscall;
|
2019-05-24 13:06:55 -07:00
|
|
|
use solana_sdk::system_program;
|
2019-04-05 09:42:54 -07:00
|
|
|
use solana_sdk::transaction::Result;
|
2019-03-13 13:37:24 -07:00
|
|
|
use solana_sdk::transaction::{Transaction, TransactionError};
|
2019-05-30 21:31:35 -07:00
|
|
|
use std::collections::{HashMap, HashSet};
|
2019-02-23 14:23:55 -08:00
|
|
|
use std::env;
|
2019-04-16 13:32:22 -07:00
|
|
|
use std::fs::remove_dir_all;
|
2019-06-05 21:51:44 -07:00
|
|
|
use std::io::{BufReader, Read};
|
2019-04-15 17:15:50 -07:00
|
|
|
use std::ops::Neg;
|
2018-12-24 16:11:20 -08:00
|
|
|
use std::path::Path;
|
2019-06-27 14:25:10 -07:00
|
|
|
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
|
|
|
use std::sync::{Arc, Mutex, RwLock};
|
2018-12-17 12:41:23 -08:00
|
|
|
|
2019-02-20 21:31:24 -08:00
|
|
|
const ACCOUNTSDB_DIR: &str = "accountsdb";
|
|
|
|
const NUM_ACCOUNT_DIRS: usize = 4;
|
2019-04-17 13:45:33 -07:00
|
|
|
|
2019-06-27 14:25:10 -07:00
|
|
|
#[derive(Default, Debug)]
|
|
|
|
struct CreditOnlyLock {
|
|
|
|
credits: AtomicU64,
|
|
|
|
lock_count: Mutex<u64>,
|
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
/// This structure handles synchronization for db
|
2019-06-05 21:51:44 -07:00
|
|
|
#[derive(Default, Debug)]
|
2018-12-17 12:41:23 -08:00
|
|
|
pub struct Accounts {
|
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-06-27 14:25:10 -07:00
|
|
|
/// set of credit-debit accounts which are currently in the pipeline
|
2019-06-10 22:05:46 -07:00
|
|
|
account_locks: Mutex<HashSet<Pubkey>>,
|
2018-12-17 12:41:23 -08:00
|
|
|
|
2019-06-27 14:25:10 -07:00
|
|
|
/// Set of credit-only accounts which are currently in the pipeline, caching account balance and number of locks
|
|
|
|
credit_only_account_locks: Arc<RwLock<HashMap<Pubkey, CreditOnlyLock>>>,
|
|
|
|
|
2018-12-24 16:11:20 -08:00
|
|
|
/// List of persistent stores
|
2019-06-05 21:51:44 -07:00
|
|
|
pub paths: String,
|
2019-03-02 14:02:11 -08:00
|
|
|
|
|
|
|
/// set to true if object created the directories in paths
|
|
|
|
/// when true, delete parents of 'paths' on drop
|
|
|
|
own_paths: bool,
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2018-12-24 16:11:20 -08:00
|
|
|
impl Drop for Accounts {
|
|
|
|
fn drop(&mut self) {
|
2019-05-30 21:31:35 -07:00
|
|
|
if self.own_paths {
|
|
|
|
let paths = get_paths_vec(&self.paths);
|
|
|
|
paths.iter().for_each(|p| {
|
|
|
|
let _ignored = remove_dir_all(p);
|
2019-03-02 14:02:11 -08:00
|
|
|
|
2019-05-30 21:31:35 -07:00
|
|
|
// it is safe to delete the parent
|
2019-03-02 14:02:11 -08:00
|
|
|
let path = Path::new(p);
|
|
|
|
let _ignored = remove_dir_all(path.parent().unwrap());
|
2019-05-30 21:31:35 -07:00
|
|
|
});
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-16 13:32:22 -07:00
|
|
|
impl Accounts {
|
|
|
|
fn make_new_dir() -> String {
|
|
|
|
static ACCOUNT_DIR: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
let dir = ACCOUNT_DIR.fetch_add(1, Ordering::Relaxed);
|
|
|
|
let out_dir = env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string());
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
format!(
|
|
|
|
"{}/{}/{}/{}",
|
|
|
|
out_dir,
|
|
|
|
ACCOUNTSDB_DIR,
|
|
|
|
keypair.pubkey(),
|
|
|
|
dir.to_string()
|
2019-03-07 12:41:00 -08:00
|
|
|
)
|
2019-03-06 15:12:50 -08:00
|
|
|
}
|
|
|
|
|
2019-04-16 13:32:22 -07:00
|
|
|
fn make_default_paths() -> String {
|
|
|
|
let mut paths = "".to_string();
|
|
|
|
for index in 0..NUM_ACCOUNT_DIRS {
|
|
|
|
if index > 0 {
|
|
|
|
paths.push_str(",");
|
2019-02-20 12:17:32 -08:00
|
|
|
}
|
2019-04-16 13:32:22 -07:00
|
|
|
paths.push_str(&Self::make_new_dir());
|
2019-02-20 12:17:32 -08:00
|
|
|
}
|
2019-04-16 13:32:22 -07:00
|
|
|
paths
|
2019-04-16 08:50:05 -07:00
|
|
|
}
|
|
|
|
|
2019-04-16 13:32:22 -07:00
|
|
|
pub fn new(in_paths: Option<String>) -> Self {
|
|
|
|
let (paths, own_paths) = if in_paths.is_none() {
|
|
|
|
(Self::make_default_paths(), true)
|
2019-04-15 17:15:50 -07:00
|
|
|
} else {
|
2019-04-16 13:32:22 -07:00
|
|
|
(in_paths.unwrap(), false)
|
|
|
|
};
|
|
|
|
let accounts_db = Arc::new(AccountsDB::new(&paths));
|
|
|
|
Accounts {
|
|
|
|
accounts_db,
|
2019-05-07 15:51:35 -07:00
|
|
|
account_locks: Mutex::new(HashSet::new()),
|
2019-06-27 14:25:10 -07:00
|
|
|
credit_only_account_locks: Arc::new(RwLock::new(HashMap::new())),
|
2019-04-16 13:32:22 -07:00
|
|
|
paths,
|
|
|
|
own_paths,
|
2018-12-24 16:11:20 -08:00
|
|
|
}
|
2019-02-28 17:34:37 -08:00
|
|
|
}
|
2019-04-16 13:32:22 -07:00
|
|
|
pub fn new_from_parent(parent: &Accounts) -> Self {
|
|
|
|
let accounts_db = parent.accounts_db.clone();
|
|
|
|
Accounts {
|
|
|
|
accounts_db,
|
2019-05-07 15:51:35 -07:00
|
|
|
account_locks: Mutex::new(HashSet::new()),
|
2019-06-27 14:25:10 -07:00
|
|
|
credit_only_account_locks: Arc::new(RwLock::new(HashMap::new())),
|
2019-04-16 13:32:22 -07:00
|
|
|
paths: parent.paths.clone(),
|
|
|
|
own_paths: parent.own_paths,
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2018-12-24 16:11:20 -08:00
|
|
|
|
2019-06-05 21:51:44 -07:00
|
|
|
pub fn update_from_stream<R: Read>(
|
|
|
|
&self,
|
|
|
|
stream: &mut BufReader<R>,
|
|
|
|
) -> std::result::Result<(), std::io::Error> {
|
|
|
|
self.accounts_db.update_from_stream(stream)
|
|
|
|
}
|
|
|
|
|
2018-12-24 16:11:20 -08:00
|
|
|
fn load_tx_accounts(
|
2019-04-23 09:56:36 -07:00
|
|
|
storage: &AccountStorage,
|
2019-04-15 17:15:50 -07:00
|
|
|
ancestors: &HashMap<Fork, usize>,
|
|
|
|
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-06-10 19:50:02 -07:00
|
|
|
) -> Result<(Vec<Account>, InstructionCredits)> {
|
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 {
|
2019-02-07 11:14:10 -08:00
|
|
|
// Check for unique account keys
|
2019-03-29 09:05:06 -07:00
|
|
|
if has_duplicates(&message.account_keys) {
|
2019-02-07 11:14:10 -08:00
|
|
|
error_counters.account_loaded_twice += 1;
|
2019-03-13 12:58:44 -07:00
|
|
|
return Err(TransactionError::AccountLoadedTwice);
|
2019-02-07 11:14:10 -08:00
|
|
|
}
|
|
|
|
|
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![];
|
2019-06-10 19:50:02 -07:00
|
|
|
let mut credits: InstructionCredits = vec![];
|
2019-06-27 14:25:10 -07:00
|
|
|
for key in message.account_keys.iter() {
|
2019-05-22 15:23:16 -07:00
|
|
|
if !message.program_ids().contains(&key) {
|
|
|
|
called_accounts.push(
|
|
|
|
AccountsDB::load(storage, ancestors, accounts_index, key)
|
|
|
|
.map(|(account, _)| account)
|
|
|
|
.unwrap_or_default(),
|
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
credits.push(0);
|
2019-05-22 15:23:16 -07:00
|
|
|
}
|
2019-01-29 16:33:28 -08:00
|
|
|
}
|
2019-03-05 16:28:14 -08:00
|
|
|
if called_accounts.is_empty() || called_accounts[0].lamports == 0 {
|
2019-01-29 16:33:28 -08:00
|
|
|
error_counters.account_not_found += 1;
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::AccountNotFound)
|
2019-05-24 13:06:55 -07:00
|
|
|
} else if called_accounts[0].owner != system_program::id() {
|
|
|
|
error_counters.invalid_account_for_fee += 1;
|
|
|
|
Err(TransactionError::InvalidAccountForFee)
|
2019-03-29 15:11:21 -07:00
|
|
|
} else if called_accounts[0].lamports < fee {
|
2019-01-29 16:33:28 -08:00
|
|
|
error_counters.insufficient_funds += 1;
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::InsufficientFundsForFee)
|
2019-01-29 16:33:28 -08:00
|
|
|
} else {
|
2019-03-29 15:11:21 -07:00
|
|
|
called_accounts[0].lamports -= fee;
|
2019-06-10 19:50:02 -07:00
|
|
|
Ok((called_accounts, credits))
|
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,
|
2019-04-15 17:15:50 -07:00
|
|
|
ancestors: &HashMap<Fork, usize>,
|
|
|
|
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
|
|
|
};
|
2019-02-14 09:57:54 -08:00
|
|
|
if !program.executable || program.owner == Pubkey::default() {
|
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-06 22:06:55 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// add loader to chain
|
2019-02-14 09:57:54 -08:00
|
|
|
program_id = program.owner;
|
2019-04-15 17:15:50 -07:00
|
|
|
accounts.insert(0, (program_id, program));
|
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,
|
2019-04-15 17:15:50 -07:00
|
|
|
ancestors: &HashMap<Fork, usize>,
|
|
|
|
accounts_index: &AccountsIndex<AccountInfo>,
|
2019-01-08 09:20:25 -08:00
|
|
|
tx: &Transaction,
|
|
|
|
error_counters: &mut ErrorCounters,
|
2018-12-24 16:11:20 -08:00
|
|
|
) -> Result<Vec<Vec<(Pubkey, Account)>>> {
|
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,
|
2019-04-15 17:15:50 -07:00
|
|
|
ancestors: &HashMap<Fork, usize>,
|
2018-12-17 12:41:23 -08:00
|
|
|
txs: &[Transaction],
|
2019-01-06 22:06:55 -08:00
|
|
|
lock_results: Vec<Result<()>>,
|
2019-06-10 22:18:32 -07:00
|
|
|
hash_queue: &BlockhashQueue,
|
2018-12-17 12:41:23 -08:00
|
|
|
error_counters: &mut ErrorCounters,
|
2019-06-10 19:50:02 -07:00
|
|
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>> {
|
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();
|
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-06-10 22:18:32 -07:00
|
|
|
let fee_calculator = hash_queue
|
|
|
|
.get_fee_calculator(&tx.message().recent_blockhash)
|
|
|
|
.ok_or(TransactionError::BlockhashNotFound)?;
|
|
|
|
|
2019-03-29 15:11:21 -07:00
|
|
|
let fee = fee_calculator.calculate_fee(tx.message());
|
2019-06-10 19:50:02 -07:00
|
|
|
let (accounts, credits) = Self::load_tx_accounts(
|
2019-04-15 17:15:50 -07:00
|
|
|
&storage,
|
|
|
|
ancestors,
|
|
|
|
&accounts_index,
|
|
|
|
tx,
|
|
|
|
fee,
|
|
|
|
error_counters,
|
|
|
|
)?;
|
|
|
|
let loaders = Self::load_loaders(
|
|
|
|
&storage,
|
|
|
|
ancestors,
|
|
|
|
&accounts_index,
|
|
|
|
tx,
|
|
|
|
error_counters,
|
|
|
|
)?;
|
2019-06-10 19:50:02 -07:00
|
|
|
Ok((accounts, loaders, credits))
|
2019-01-06 22:06:55 -08:00
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
(_, Err(e)) => Err(e),
|
|
|
|
})
|
|
|
|
.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
|
2019-05-06 07:31:50 -07:00
|
|
|
pub fn load_slow(
|
|
|
|
&self,
|
|
|
|
ancestors: &HashMap<Fork, usize>,
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
) -> Option<(Account, Fork)> {
|
2019-02-26 21:41:05 -08:00
|
|
|
self.accounts_db
|
2019-04-15 17:15:50 -07:00
|
|
|
.load_slow(ancestors, pubkey)
|
2019-05-06 07:31:50 -07:00
|
|
|
.filter(|(acc, _)| acc.lamports != 0)
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2018-12-24 16:11:20 -08:00
|
|
|
|
2019-04-15 17:15:50 -07:00
|
|
|
pub fn load_by_program(&self, fork: Fork, program_id: &Pubkey) -> Vec<(Pubkey, Account)> {
|
2019-04-16 08:50:05 -07:00
|
|
|
let accumulator: Vec<Vec<(Pubkey, u64, Account)>> = self.accounts_db.scan_account_storage(
|
2019-04-15 17:15:50 -07:00
|
|
|
fork,
|
2019-05-30 21:31:35 -07:00
|
|
|
|stored_account: &StoredAccount,
|
|
|
|
_id: AppendVecId,
|
|
|
|
accum: &mut Vec<(Pubkey, u64, Account)>| {
|
2019-04-16 08:50:05 -07:00
|
|
|
if stored_account.balance.owner == *program_id {
|
|
|
|
let val = (
|
|
|
|
stored_account.meta.pubkey,
|
|
|
|
stored_account.meta.write_version,
|
|
|
|
stored_account.clone_account(),
|
|
|
|
);
|
|
|
|
accum.push(val)
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
2019-04-16 08:50:05 -07:00
|
|
|
let mut versions: Vec<(Pubkey, u64, Account)> =
|
|
|
|
accumulator.into_iter().flat_map(|x| x).collect();
|
|
|
|
versions.sort_by_key(|s| (s.0, (s.1 as i64).neg()));
|
|
|
|
versions.dedup_by_key(|s| s.0);
|
|
|
|
versions.into_iter().map(|s| (s.0, s.2)).collect()
|
2019-03-07 08:51:56 -08:00
|
|
|
}
|
|
|
|
|
2019-03-07 14:58:11 -08:00
|
|
|
/// Slow because lock is held for 1 operation instead of many
|
2019-02-27 16:06:06 -08:00
|
|
|
pub fn store_slow(&self, fork: Fork, pubkey: &Pubkey, account: &Account) {
|
2019-06-10 19:50:02 -07:00
|
|
|
let mut accounts = HashMap::new();
|
2019-06-27 14:25:10 -07:00
|
|
|
accounts.insert(pubkey, account);
|
2019-06-10 19:50:02 -07:00
|
|
|
self.accounts_db.store(fork, &accounts);
|
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(
|
2019-06-10 22:05:46 -07:00
|
|
|
locks: &mut HashSet<Pubkey>,
|
2019-06-27 14:25:10 -07:00
|
|
|
credit_only_locks: &Arc<RwLock<HashMap<Pubkey, CreditOnlyLock>>>,
|
|
|
|
message: &Message,
|
2018-12-17 12:41:23 -08:00
|
|
|
error_counters: &mut ErrorCounters,
|
|
|
|
) -> Result<()> {
|
2019-06-27 14:25:10 -07:00
|
|
|
let (credit_debit_keys, credit_only_keys) = message.get_account_keys_by_lock_type();
|
|
|
|
|
|
|
|
for k in credit_debit_keys.iter() {
|
|
|
|
let credit_only_locks = credit_only_locks.read().unwrap();
|
|
|
|
if locks.contains(k)
|
|
|
|
|| credit_only_locks
|
|
|
|
.get(&k)
|
|
|
|
.map_or(false, |lock| *lock.lock_count.lock().unwrap() > 0)
|
|
|
|
{
|
|
|
|
error_counters.account_in_use += 1;
|
|
|
|
debug!("Account in use: {:?}", k);
|
|
|
|
return Err(TransactionError::AccountInUse);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for k in credit_only_keys.iter() {
|
2019-06-10 22:05:46 -07:00
|
|
|
if locks.contains(k) {
|
2018-12-17 12:41:23 -08:00
|
|
|
error_counters.account_in_use += 1;
|
2019-03-28 11:45:34 -07:00
|
|
|
debug!("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
|
|
|
|
|
|
|
for k in credit_debit_keys {
|
|
|
|
locks.insert(*k);
|
|
|
|
}
|
|
|
|
let mut credit_only_writes: Vec<&Pubkey> = vec![];
|
|
|
|
for k in credit_only_keys {
|
|
|
|
let credit_only_locks = credit_only_locks.read().unwrap();
|
|
|
|
if let Some(credit_only_lock) = credit_only_locks.get(&k) {
|
|
|
|
*credit_only_lock.lock_count.lock().unwrap() += 1;
|
|
|
|
} else {
|
|
|
|
credit_only_writes.push(k);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for k in credit_only_writes.iter() {
|
|
|
|
let mut credit_only_locks = credit_only_locks.write().unwrap();
|
|
|
|
assert!(credit_only_locks.get(&k).is_none());
|
|
|
|
credit_only_locks.insert(
|
|
|
|
**k,
|
|
|
|
CreditOnlyLock {
|
|
|
|
credits: AtomicU64::new(0),
|
|
|
|
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-06-27 14:25:10 -07:00
|
|
|
fn unlock_account(
|
|
|
|
tx: &Transaction,
|
|
|
|
result: &Result<()>,
|
|
|
|
locks: &mut HashSet<Pubkey>,
|
|
|
|
credit_only_locks: &Arc<RwLock<HashMap<Pubkey, CreditOnlyLock>>>,
|
|
|
|
) {
|
|
|
|
let (credit_debit_keys, credit_only_keys) = &tx.message().get_account_keys_by_lock_type();
|
2018-12-17 12:41:23 -08:00
|
|
|
match result {
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::AccountInUse) => (),
|
2018-12-17 12:41:23 -08:00
|
|
|
_ => {
|
2019-06-27 14:25:10 -07:00
|
|
|
for k in credit_debit_keys {
|
2019-04-15 17:15:50 -07:00
|
|
|
locks.remove(k);
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-06-27 14:25:10 -07:00
|
|
|
for k in credit_only_keys {
|
|
|
|
let locks = credit_only_locks.read().unwrap();
|
|
|
|
if let Some(lock) = locks.get(&k) {
|
|
|
|
*lock.lock_count.lock().unwrap() -= 1;
|
|
|
|
}
|
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2019-04-16 13:32:22 -07:00
|
|
|
fn hash_account(stored_account: &StoredAccount) -> Hash {
|
|
|
|
let mut hasher = Hasher::default();
|
|
|
|
hasher.hash(&serialize(&stored_account.balance).unwrap());
|
|
|
|
hasher.hash(stored_account.data);
|
|
|
|
hasher.result()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn hash_internal_state(&self, fork_id: Fork) -> Option<Hash> {
|
|
|
|
let accumulator: Vec<Vec<(Pubkey, u64, Hash)>> = self.accounts_db.scan_account_storage(
|
|
|
|
fork_id,
|
2019-05-30 21:31:35 -07:00
|
|
|
|stored_account: &StoredAccount,
|
|
|
|
_id: AppendVecId,
|
|
|
|
accum: &mut Vec<(Pubkey, u64, Hash)>| {
|
2019-05-31 12:26:45 -07:00
|
|
|
if !syscall::check_id(&stored_account.balance.owner) {
|
|
|
|
accum.push((
|
|
|
|
stored_account.meta.pubkey,
|
|
|
|
stored_account.meta.write_version,
|
|
|
|
Self::hash_account(stored_account),
|
|
|
|
));
|
|
|
|
}
|
2019-04-16 13:32:22 -07:00
|
|
|
},
|
|
|
|
);
|
|
|
|
let mut account_hashes: Vec<_> = accumulator.into_iter().flat_map(|x| x).collect();
|
|
|
|
account_hashes.sort_by_key(|s| (s.0, (s.1 as i64).neg()));
|
|
|
|
account_hashes.dedup_by_key(|s| s.0);
|
|
|
|
if account_hashes.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
let mut hasher = Hasher::default();
|
|
|
|
for (_, _, hash) in account_hashes {
|
|
|
|
hasher.hash(hash.as_ref());
|
|
|
|
}
|
|
|
|
Some(hasher.result())
|
|
|
|
}
|
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-06-10 22:05:46 -07:00
|
|
|
pub fn lock_accounts(&self, txs: &[Transaction]) -> Vec<Result<()>> {
|
2018-12-17 12:41:23 -08:00
|
|
|
let mut error_counters = ErrorCounters::default();
|
|
|
|
let rv = txs
|
|
|
|
.iter()
|
2019-02-20 21:31:24 -08:00
|
|
|
.map(|tx| {
|
2019-06-10 22:05:46 -07:00
|
|
|
let message = &tx.message();
|
2019-02-20 21:31:24 -08:00
|
|
|
Self::lock_account(
|
2019-06-10 22:05:46 -07:00
|
|
|
&mut self.account_locks.lock().unwrap(),
|
2019-06-27 14:25:10 -07:00
|
|
|
&self.credit_only_account_locks,
|
|
|
|
&message,
|
2019-02-20 21:31:24 -08:00
|
|
|
&mut error_counters,
|
|
|
|
)
|
|
|
|
})
|
2018-12-17 12:41:23 -08:00
|
|
|
.collect();
|
|
|
|
if error_counters.account_in_use != 0 {
|
2019-05-17 07:00:06 -07:00
|
|
|
inc_new_counter_error!(
|
2018-12-17 12:41:23 -08:00
|
|
|
"bank-process_transactions-account_in_use",
|
2019-05-17 07:00:06 -07:00
|
|
|
error_counters.account_in_use,
|
|
|
|
0,
|
|
|
|
100
|
2018-12-17 12:41:23 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
rv
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Once accounts are unlocked, new transactions that modify that state can enter the pipeline
|
2019-06-10 22:05:46 -07:00
|
|
|
pub fn unlock_accounts(&self, txs: &[Transaction], results: &[Result<()>]) {
|
|
|
|
let mut account_locks = self.account_locks.lock().unwrap();
|
2019-06-27 14:25:10 -07:00
|
|
|
let credit_only_locks = self.credit_only_account_locks.clone();
|
2018-12-17 12:41:23 -08:00
|
|
|
debug!("bank unlock accounts");
|
2019-06-27 14:25:10 -07:00
|
|
|
txs.iter().zip(results.iter()).for_each(|(tx, result)| {
|
|
|
|
Self::unlock_account(tx, result, &mut account_locks, &credit_only_locks)
|
|
|
|
});
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2019-02-20 12:17:32 -08:00
|
|
|
pub fn has_accounts(&self, fork: Fork) -> bool {
|
|
|
|
self.accounts_db.has_accounts(fork)
|
|
|
|
}
|
|
|
|
|
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,
|
2018-12-24 16:11:20 -08:00
|
|
|
fork: Fork,
|
2018-12-17 12:41:23 -08:00
|
|
|
txs: &[Transaction],
|
|
|
|
res: &[Result<()>],
|
2019-06-27 14:25:10 -07:00
|
|
|
loaded: &mut [Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>],
|
2019-02-19 17:11:43 -08:00
|
|
|
) {
|
2019-06-27 14:25:10 -07:00
|
|
|
let accounts = self.collect_accounts(txs, res, loaded);
|
|
|
|
// Only store credit-debit accounts immediately
|
|
|
|
let mut accounts_to_store: HashMap<&Pubkey, &Account> = HashMap::new();
|
|
|
|
|
|
|
|
for (pubkey, (account, is_debitable)) in accounts.iter() {
|
|
|
|
if *is_debitable {
|
|
|
|
accounts_to_store.insert(pubkey, account);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.accounts_db.store(fork, &accounts_to_store);
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
|
|
|
|
2019-04-15 17:15:50 -07:00
|
|
|
/// Purge a fork if it is not a root
|
|
|
|
/// Root forks cannot be purged
|
|
|
|
pub fn purge_fork(&self, fork: Fork) {
|
|
|
|
self.accounts_db.purge_fork(fork);
|
2018-12-24 16:11:20 -08:00
|
|
|
}
|
2019-04-15 17:15:50 -07:00
|
|
|
/// Add a fork to root. Root forks cannot be purged
|
|
|
|
pub fn add_root(&self, fork: Fork) {
|
|
|
|
self.accounts_db.add_root(fork)
|
2019-03-21 17:36:10 -07:00
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
|
2019-06-27 14:25:10 -07:00
|
|
|
/// Commit remaining credit-only changes, regardless of reference count
|
|
|
|
pub fn commit_credits(&self, ancestors: &HashMap<Fork, usize>, fork: Fork) {
|
2019-07-08 20:46:21 -07:00
|
|
|
for (pubkey, lock) in self.credit_only_account_locks.write().unwrap().drain() {
|
|
|
|
let lock_count = *lock.lock_count.lock().unwrap();
|
|
|
|
if lock_count != 0 {
|
|
|
|
warn!(
|
|
|
|
"dropping credit-only lock on {}, still has {} locks",
|
|
|
|
pubkey, lock_count
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let credit = lock.credits.load(Ordering::Relaxed);
|
2019-06-27 14:25:10 -07:00
|
|
|
if credit > 0 {
|
|
|
|
let mut account = self
|
2019-07-08 20:46:21 -07:00
|
|
|
.load_slow(ancestors, &pubkey)
|
2019-06-27 14:25:10 -07:00
|
|
|
.map(|(account, _)| account)
|
|
|
|
.unwrap_or_default();
|
|
|
|
account.lamports += credit;
|
2019-07-08 20:46:21 -07:00
|
|
|
self.store_slow(fork, &pubkey, &account);
|
2019-06-27 14:25:10 -07:00
|
|
|
}
|
2019-06-10 19:50:02 -07:00
|
|
|
}
|
2019-06-27 14:25:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn collect_accounts<'a>(
|
|
|
|
&self,
|
|
|
|
txs: &'a [Transaction],
|
|
|
|
res: &'a [Result<()>],
|
|
|
|
loaded: &'a mut [Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>],
|
|
|
|
) -> HashMap<&'a Pubkey, (&'a Account, bool)> {
|
|
|
|
let mut accounts: HashMap<&Pubkey, (&Account, bool)> = HashMap::new();
|
|
|
|
for (i, raccs) in loaded.iter_mut().enumerate() {
|
|
|
|
if res[i].is_err() || raccs.is_err() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let message = &txs[i].message();
|
|
|
|
let acc = raccs.as_mut().unwrap();
|
|
|
|
for (((i, key), account), credit) in message
|
|
|
|
.account_keys
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.zip(acc.0.iter())
|
|
|
|
.zip(acc.2.iter())
|
|
|
|
{
|
|
|
|
if !accounts.contains_key(key) {
|
|
|
|
accounts.insert(key, (account, message.is_debitable(i)));
|
|
|
|
}
|
|
|
|
if *credit > 0 {
|
|
|
|
// Increment credit-only account balance Atomic
|
|
|
|
if accounts.get_mut(key).is_some() {
|
|
|
|
self.credit_only_account_locks
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.get(key)
|
|
|
|
.unwrap()
|
|
|
|
.credits
|
|
|
|
.fetch_add(*credit, Ordering::Relaxed);
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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::*;
|
2019-06-05 21:51:44 -07:00
|
|
|
use bincode::{serialize_into, serialized_size};
|
2019-05-30 21:31:35 -07:00
|
|
|
use rand::{thread_rng, Rng};
|
2019-01-08 09:20:25 -08:00
|
|
|
use solana_sdk::account::Account;
|
2019-06-10 22:18:32 -07:00
|
|
|
use solana_sdk::fee_calculator::FeeCalculator;
|
2019-01-08 09:20:25 -08:00
|
|
|
use solana_sdk::hash::Hash;
|
2019-03-23 20:12:27 -07:00
|
|
|
use solana_sdk::instruction::CompiledInstruction;
|
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
2019-05-31 12:26:45 -07:00
|
|
|
use solana_sdk::syscall;
|
2019-01-08 09:20:25 -08:00
|
|
|
use solana_sdk::transaction::Transaction;
|
2019-05-30 21:31:35 -07:00
|
|
|
use std::io::Cursor;
|
2019-06-27 14:25:10 -07:00
|
|
|
use std::sync::atomic::AtomicBool;
|
|
|
|
use std::{thread, time};
|
2019-01-08 09:20:25 -08:00
|
|
|
|
2019-03-29 15:11:21 -07:00
|
|
|
fn load_accounts_with_fee(
|
2019-01-08 09:20:25 -08:00
|
|
|
tx: Transaction,
|
|
|
|
ka: &Vec<(Pubkey, Account)>,
|
2019-03-29 15:11:21 -07:00
|
|
|
fee_calculator: &FeeCalculator,
|
2019-01-08 09:20:25 -08:00
|
|
|
error_counters: &mut ErrorCounters,
|
2019-06-10 19:50:02 -07:00
|
|
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>> {
|
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);
|
2019-04-15 17:15:50 -07:00
|
|
|
let accounts = Accounts::new(None);
|
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();
|
2019-06-10 22:18:32 -07:00
|
|
|
let res =
|
|
|
|
accounts.load_accounts(&ancestors, &[tx], vec![Ok(())], &hash_queue, error_counters);
|
2018-12-24 16:11:20 -08:00
|
|
|
res
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
|
2019-03-29 15:11:21 -07:00
|
|
|
fn load_accounts(
|
|
|
|
tx: Transaction,
|
|
|
|
ka: &Vec<(Pubkey, Account)>,
|
|
|
|
error_counters: &mut ErrorCounters,
|
2019-06-10 19:50:02 -07:00
|
|
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>> {
|
2019-03-29 15:11:21 -07:00
|
|
|
let fee_calculator = FeeCalculator::default();
|
|
|
|
load_accounts_with_fee(tx, ka, &fee_calculator, 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();
|
|
|
|
|
2019-05-23 15:19:53 -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::<Keypair>(
|
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-03-13 12:58:44 -07:00
|
|
|
assert_eq!(loaded_accounts[0], Err(TransactionError::AccountNotFound));
|
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-03-13 12:58:44 -07:00
|
|
|
assert_eq!(loaded_accounts[0], Err(TransactionError::AccountNotFound));
|
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]);
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let account = Account::new(1, 1, &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],
|
|
|
|
Err(TransactionError::ProgramAccountNotFound)
|
|
|
|
);
|
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();
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let account = Account::new(1, 1, &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,
|
|
|
|
);
|
|
|
|
|
2019-03-29 15:11:21 -07:00
|
|
|
let fee_calculator = FeeCalculator::new(10);
|
|
|
|
assert_eq!(fee_calculator.calculate_fee(tx.message()), 10);
|
|
|
|
|
|
|
|
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-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::InsufficientFundsForFee)
|
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],
|
|
|
|
Err(TransactionError::InvalidAccountForFee)
|
2019-03-13 12:58:44 -07:00
|
|
|
);
|
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]);
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let account = Account::new(1, 1, &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-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-06-10 19:50:02 -07:00
|
|
|
Ok((instruction_accounts, instruction_loaders, instruction_credits)) => {
|
|
|
|
assert_eq!(instruction_accounts.len(), 2);
|
|
|
|
assert_eq!(instruction_accounts[0], accounts[0].1);
|
|
|
|
assert_eq!(instruction_loaders.len(), 1);
|
|
|
|
assert_eq!(instruction_loaders[0].len(), 0);
|
|
|
|
assert_eq!(instruction_credits.len(), 2);
|
|
|
|
assert_eq!(instruction_credits, &vec![0, 0]);
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
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]);
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let account = Account::new(1, 1, &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-03-13 12:58:44 -07:00
|
|
|
assert_eq!(loaded_accounts[0], Err(TransactionError::CallChainTooDeep));
|
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]);
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let account = Account::new(1, 1, &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 = 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(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
|
|
|
|
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-03-13 12:58:44 -07:00
|
|
|
assert_eq!(loaded_accounts[0], Err(TransactionError::AccountNotFound));
|
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]);
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let account = Account::new(1, 1, &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-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-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
|
|
|
|
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-03-13 12:58:44 -07:00
|
|
|
assert_eq!(loaded_accounts[0], Err(TransactionError::AccountNotFound));
|
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]);
|
|
|
|
let key3 = Pubkey::new(&[7u8; 32]);
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let account = Account::new(1, 1, &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));
|
|
|
|
|
|
|
|
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-06-10 19:50:02 -07:00
|
|
|
Ok((instruction_accounts, instruction_loaders, instruction_credits)) => {
|
|
|
|
assert_eq!(instruction_accounts.len(), 1);
|
|
|
|
assert_eq!(instruction_accounts[0], accounts[0].1);
|
|
|
|
assert_eq!(instruction_loaders.len(), 2);
|
|
|
|
assert_eq!(instruction_loaders[0].len(), 1);
|
|
|
|
assert_eq!(instruction_loaders[1].len(), 2);
|
|
|
|
assert_eq!(instruction_credits.len(), 1);
|
|
|
|
assert_eq!(instruction_credits, &vec![0]);
|
|
|
|
for loaders in instruction_loaders.iter() {
|
|
|
|
for (i, accounts_subset) in loaders.iter().enumerate() {
|
2019-01-08 09:20:25 -08:00
|
|
|
// +1 to skip first not loader account
|
2019-06-10 19:50:02 -07:00
|
|
|
assert_eq![accounts_subset.1, accounts[i + 1].1];
|
2019-01-08 09:20:25 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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();
|
|
|
|
|
2019-03-09 19:28:43 -08:00
|
|
|
let account = Account::new(10, 1, &Pubkey::default());
|
2019-02-07 11:14:10 -08:00
|
|
|
accounts.push((pubkey, account));
|
|
|
|
|
2019-03-15 08:47:25 -07:00
|
|
|
let instructions = vec![CompiledInstruction::new(0, &(), vec![0, 1])];
|
2019-02-07 11:14:10 -08:00
|
|
|
// Simulate pay-to-self transaction, which loads the same account twice
|
2019-03-15 09:02:28 -07:00
|
|
|
let tx = Transaction::new_with_compiled_instructions(
|
2019-02-07 11:14:10 -08:00
|
|
|
&[&keypair],
|
|
|
|
&[pubkey],
|
|
|
|
Hash::default(),
|
|
|
|
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);
|
2019-03-13 12:58:44 -07:00
|
|
|
assert_eq!(
|
|
|
|
loaded_accounts[0],
|
|
|
|
Err(TransactionError::AccountLoadedTwice)
|
|
|
|
);
|
2019-02-07 11:14:10 -08:00
|
|
|
}
|
2019-02-21 12:08:50 -08:00
|
|
|
|
|
|
|
#[test]
|
2019-04-16 13:32:22 -07:00
|
|
|
fn test_load_by_program() {
|
|
|
|
let accounts = Accounts::new(None);
|
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
|
|
|
|
2019-04-16 13:32:22 -07:00
|
|
|
let loaded = accounts.load_by_program(0, &Pubkey::new(&[2; 32]));
|
|
|
|
assert_eq!(loaded.len(), 2);
|
|
|
|
let loaded = accounts.load_by_program(0, &Pubkey::new(&[3; 32]));
|
|
|
|
assert_eq!(loaded, vec![(pubkey2, account2)]);
|
|
|
|
let loaded = accounts.load_by_program(0, &Pubkey::new(&[4; 32]));
|
|
|
|
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() {
|
|
|
|
let accounts = Accounts::new(None);
|
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-04-16 13:32:22 -07:00
|
|
|
fn test_accounts_empty_hash_internal_state() {
|
|
|
|
let accounts = Accounts::new(None);
|
|
|
|
assert_eq!(accounts.hash_internal_state(0), None);
|
2019-05-31 12:26:45 -07:00
|
|
|
accounts.store_slow(0, &Pubkey::default(), &Account::new(1, 0, &syscall::id()));
|
|
|
|
assert_eq!(accounts.hash_internal_state(0), None);
|
2019-03-07 08:51:56 -08:00
|
|
|
}
|
2019-04-16 13:32:22 -07:00
|
|
|
|
2019-05-30 21:31:35 -07:00
|
|
|
fn create_accounts(accounts: &Accounts, pubkeys: &mut Vec<Pubkey>, num: usize) {
|
|
|
|
for t in 0..num {
|
|
|
|
let pubkey = Pubkey::new_rand();
|
|
|
|
let account = Account::new((t + 1) as u64, 0, &Account::default().owner);
|
|
|
|
accounts.store_slow(0, &pubkey, &account);
|
|
|
|
pubkeys.push(pubkey.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_accounts(accounts: &Accounts, pubkeys: &Vec<Pubkey>, num: usize) {
|
|
|
|
for _ in 1..num {
|
|
|
|
let idx = thread_rng().gen_range(0, num - 1);
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
|
|
|
let account = accounts.load_slow(&ancestors, &pubkeys[idx]).unwrap();
|
|
|
|
let account1 = Account::new((idx + 1) as u64, 0, &Account::default().owner);
|
|
|
|
assert_eq!(account, (account1, 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_accounts_serialize() {
|
|
|
|
solana_logger::setup();
|
|
|
|
let accounts = Accounts::new(Some("serialize_accounts".to_string()));
|
|
|
|
|
|
|
|
let mut pubkeys: Vec<Pubkey> = vec![];
|
|
|
|
create_accounts(&accounts, &mut pubkeys, 100);
|
|
|
|
check_accounts(&accounts, &pubkeys, 100);
|
|
|
|
accounts.add_root(0);
|
|
|
|
|
2019-06-05 21:51:44 -07:00
|
|
|
let sz = serialized_size(&*accounts.accounts_db).unwrap();
|
2019-05-30 21:31:35 -07:00
|
|
|
let mut buf = vec![0u8; sz as usize];
|
|
|
|
let mut writer = Cursor::new(&mut buf[..]);
|
|
|
|
serialize_into(&mut writer, &*accounts.accounts_db).unwrap();
|
|
|
|
|
2019-06-05 21:51:44 -07:00
|
|
|
let mut reader = BufReader::new(&buf[..]);
|
|
|
|
let daccounts = Accounts::new(Some("serialize_accounts".to_string()));
|
|
|
|
assert!(daccounts.update_from_stream(&mut reader).is_ok());
|
2019-05-30 21:31:35 -07:00
|
|
|
check_accounts(&daccounts, &pubkeys, 100);
|
|
|
|
assert_eq!(
|
|
|
|
accounts.hash_internal_state(0),
|
|
|
|
daccounts.hash_internal_state(0)
|
|
|
|
);
|
|
|
|
}
|
2019-06-10 19:50:02 -07: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());
|
|
|
|
|
|
|
|
let accounts = Accounts::new(None);
|
|
|
|
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());
|
|
|
|
let results0 = accounts.lock_accounts(&[tx.clone()]);
|
|
|
|
|
|
|
|
assert!(results0[0].is_ok());
|
|
|
|
assert_eq!(
|
|
|
|
*accounts
|
|
|
|
.credit_only_account_locks
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.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];
|
|
|
|
let results1 = accounts.lock_accounts(&txs);
|
|
|
|
|
|
|
|
assert!(results1[0].is_ok()); // Credit-only account (keypair1) can be referenced multiple times
|
|
|
|
assert!(results1[1].is_err()); // Credit-only account (keypair1) cannot also be locked as credit-debit
|
|
|
|
assert_eq!(
|
|
|
|
*accounts
|
|
|
|
.credit_only_account_locks
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.get(&keypair1.pubkey())
|
|
|
|
.unwrap()
|
|
|
|
.lock_count
|
|
|
|
.lock()
|
|
|
|
.unwrap(),
|
|
|
|
2
|
|
|
|
);
|
|
|
|
|
|
|
|
accounts.unlock_accounts(&[tx], &results0);
|
|
|
|
accounts.unlock_accounts(&txs, &results1);
|
|
|
|
|
|
|
|
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());
|
|
|
|
let results2 = accounts.lock_accounts(&[tx]);
|
|
|
|
|
|
|
|
assert!(results2[0].is_ok()); // Now keypair1 account can be locked as credit-debit
|
|
|
|
|
|
|
|
// Check that credit-only credits are still cached in accounts struct
|
|
|
|
let credit_only_account_locks = accounts.credit_only_account_locks.read().unwrap();
|
|
|
|
let keypair1_lock = credit_only_account_locks.get(&keypair1.pubkey());
|
|
|
|
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());
|
|
|
|
|
|
|
|
let accounts = Accounts::new(None);
|
|
|
|
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])];
|
|
|
|
let credit_only_message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair0.pubkey(), keypair1.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let credit_only_tx = Transaction::new(&[&keypair0], credit_only_message, Hash::default());
|
|
|
|
|
|
|
|
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
|
|
|
|
let credit_debit_message = Message::new_with_compiled_instructions(
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
vec![keypair1.pubkey(), keypair2.pubkey(), native_loader::id()],
|
|
|
|
Hash::default(),
|
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let credit_debit_tx = Transaction::new(&[&keypair1], credit_debit_message, Hash::default());
|
|
|
|
|
|
|
|
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 {
|
|
|
|
let txs = vec![credit_debit_tx.clone()];
|
|
|
|
let results = accounts_clone.clone().lock_accounts(&txs);
|
|
|
|
for result in results.iter() {
|
|
|
|
if result.is_ok() {
|
|
|
|
counter_clone.clone().fetch_add(1, Ordering::SeqCst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
accounts_clone.unlock_accounts(&txs, &results);
|
|
|
|
if exit_clone.clone().load(Ordering::Relaxed) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
let counter_clone = counter.clone();
|
|
|
|
for _ in 0..5 {
|
|
|
|
let txs = vec![credit_only_tx.clone()];
|
|
|
|
let results = accounts_arc.clone().lock_accounts(&txs);
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
accounts_arc.unlock_accounts(&txs, &results);
|
|
|
|
thread::sleep(time::Duration::from_millis(50));
|
|
|
|
}
|
|
|
|
exit.store(true, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_commit_credits() {
|
|
|
|
let pubkey0 = Pubkey::new_rand();
|
|
|
|
let pubkey1 = Pubkey::new_rand();
|
|
|
|
let pubkey2 = Pubkey::new_rand();
|
|
|
|
|
|
|
|
let account0 = Account::new(1, 0, &Pubkey::default());
|
|
|
|
let account1 = Account::new(2, 0, &Pubkey::default());
|
|
|
|
|
|
|
|
let accounts = Accounts::new(None);
|
|
|
|
accounts.store_slow(0, &pubkey0, &account0);
|
|
|
|
accounts.store_slow(0, &pubkey1, &account1);
|
|
|
|
|
|
|
|
let mut credit_only_account_locks = accounts.credit_only_account_locks.write().unwrap();
|
|
|
|
credit_only_account_locks.insert(
|
|
|
|
pubkey0,
|
|
|
|
CreditOnlyLock {
|
|
|
|
credits: AtomicU64::new(0),
|
|
|
|
lock_count: Mutex::new(1),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
credit_only_account_locks.insert(
|
|
|
|
pubkey1,
|
|
|
|
CreditOnlyLock {
|
|
|
|
credits: AtomicU64::new(5),
|
|
|
|
lock_count: Mutex::new(1),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
credit_only_account_locks.insert(
|
|
|
|
pubkey2,
|
|
|
|
CreditOnlyLock {
|
|
|
|
credits: AtomicU64::new(10),
|
|
|
|
lock_count: Mutex::new(1),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
drop(credit_only_account_locks);
|
|
|
|
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
|
|
|
accounts.commit_credits(&ancestors, 0);
|
|
|
|
|
|
|
|
// No change when CreditOnlyLock credits are 0
|
|
|
|
assert_eq!(
|
|
|
|
accounts.load_slow(&ancestors, &pubkey0).unwrap().0.lamports,
|
|
|
|
1
|
|
|
|
);
|
|
|
|
// New balance should equal previous balance plus CreditOnlyLock credits
|
|
|
|
assert_eq!(
|
|
|
|
accounts.load_slow(&ancestors, &pubkey1).unwrap().0.lamports,
|
|
|
|
7
|
|
|
|
);
|
|
|
|
// New account should be created
|
|
|
|
assert_eq!(
|
|
|
|
accounts.load_slow(&ancestors, &pubkey2).unwrap().0.lamports,
|
|
|
|
10
|
|
|
|
);
|
|
|
|
// Account locks should be cleared
|
|
|
|
assert_eq!(accounts.credit_only_account_locks.read().unwrap().len(), 0);
|
|
|
|
}
|
|
|
|
|
2019-06-10 19:50:02 -07:00
|
|
|
#[test]
|
|
|
|
fn test_collect_accounts() {
|
|
|
|
let keypair0 = Keypair::new();
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let pubkey = Pubkey::new_rand();
|
|
|
|
|
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];
|
|
|
|
|
|
|
|
let loaders = vec![Ok(()), Ok(())];
|
|
|
|
|
|
|
|
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 instruction_accounts0 = vec![account0, account2.clone()];
|
|
|
|
let instruction_loaders0 = vec![];
|
|
|
|
let instruction_credits0 = vec![0, 2];
|
|
|
|
let loaded0 = Ok((
|
|
|
|
instruction_accounts0,
|
|
|
|
instruction_loaders0,
|
|
|
|
instruction_credits0,
|
|
|
|
));
|
|
|
|
|
|
|
|
let instruction_accounts1 = vec![account1, account2.clone()];
|
|
|
|
let instruction_loaders1 = vec![];
|
2019-06-27 14:25:10 -07:00
|
|
|
let instruction_credits1 = vec![0, 3];
|
2019-06-10 19:50:02 -07:00
|
|
|
let loaded1 = Ok((
|
|
|
|
instruction_accounts1,
|
|
|
|
instruction_loaders1,
|
|
|
|
instruction_credits1,
|
|
|
|
));
|
|
|
|
|
2019-06-27 14:25:10 -07:00
|
|
|
let mut loaded = vec![loaded0, loaded1];
|
|
|
|
|
|
|
|
let accounts = Accounts::new(None);
|
|
|
|
let mut credit_only_locks = accounts.credit_only_account_locks.write().unwrap();
|
|
|
|
credit_only_locks.insert(
|
|
|
|
pubkey,
|
|
|
|
CreditOnlyLock {
|
|
|
|
credits: AtomicU64::new(0),
|
|
|
|
lock_count: Mutex::new(1),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
drop(credit_only_locks);
|
|
|
|
let collected_accounts = accounts.collect_accounts(&txs, &loaders, &mut loaded);
|
|
|
|
assert_eq!(collected_accounts.len(), 3);
|
|
|
|
assert!(collected_accounts.contains_key(&keypair0.pubkey()));
|
|
|
|
assert!(collected_accounts.contains_key(&keypair1.pubkey()));
|
|
|
|
assert!(collected_accounts.contains_key(&pubkey));
|
|
|
|
|
|
|
|
let credit_debit_account0 = collected_accounts.get(&keypair0.pubkey()).unwrap();
|
|
|
|
assert_eq!(credit_debit_account0.1, true);
|
|
|
|
let credit_debit_account1 = collected_accounts.get(&keypair1.pubkey()).unwrap();
|
|
|
|
assert_eq!(credit_debit_account1.1, true);
|
|
|
|
|
|
|
|
let credit_only_account = collected_accounts.get(&pubkey).unwrap();
|
|
|
|
assert_eq!(credit_only_account.1, false);
|
|
|
|
// Ensure credit_only_lock reflects credits from both accounts: 2 + 3 = 5
|
|
|
|
let credit_only_locks = accounts.credit_only_account_locks.read().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
credit_only_locks
|
|
|
|
.get(&pubkey)
|
|
|
|
.unwrap()
|
|
|
|
.credits
|
|
|
|
.load(Ordering::Relaxed),
|
|
|
|
5
|
|
|
|
);
|
2019-06-10 19:50:02 -07:00
|
|
|
}
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|