Accounts refactoring for forking.
* Move last_id and signature dup handling out of Accounts. * Accounts that handle overlays.
This commit is contained in:
parent
668d353add
commit
1c0758e3bd
335
src/accounts.rs
335
src/accounts.rs
|
@ -1,7 +1,6 @@
|
||||||
use crate::bank::BankError;
|
use crate::bank::BankError;
|
||||||
use crate::bank::Result;
|
use crate::bank::Result;
|
||||||
use crate::counter::Counter;
|
use crate::counter::Counter;
|
||||||
use crate::status_deque::{StatusDeque, StatusDequeError};
|
|
||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use log::Level;
|
use log::Level;
|
||||||
|
@ -10,6 +9,7 @@ use solana_sdk::hash::{hash, Hash};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::Transaction;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::{Mutex, RwLock};
|
use std::sync::{Mutex, RwLock};
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ pub struct ErrorCounters {
|
||||||
pub account_not_found: usize,
|
pub account_not_found: usize,
|
||||||
pub account_in_use: usize,
|
pub account_in_use: usize,
|
||||||
pub last_id_not_found: usize,
|
pub last_id_not_found: usize,
|
||||||
|
pub last_id_too_old: usize,
|
||||||
pub reserve_last_id: usize,
|
pub reserve_last_id: usize,
|
||||||
pub insufficient_funds: usize,
|
pub insufficient_funds: usize,
|
||||||
pub duplicate_signature: usize,
|
pub duplicate_signature: usize,
|
||||||
|
@ -65,10 +66,6 @@ impl Default for Accounts {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountsDB {
|
impl AccountsDB {
|
||||||
pub fn keys(&self) -> Vec<Pubkey> {
|
|
||||||
self.accounts.keys().cloned().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash_internal_state(&self) -> Hash {
|
pub fn hash_internal_state(&self) -> Hash {
|
||||||
let mut ordered_accounts = BTreeMap::new();
|
let mut ordered_accounts = BTreeMap::new();
|
||||||
|
|
||||||
|
@ -81,16 +78,28 @@ impl AccountsDB {
|
||||||
hash(&serialize(&ordered_accounts).unwrap())
|
hash(&serialize(&ordered_accounts).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(&self, pubkey: &Pubkey) -> Option<&Account> {
|
fn load<U>(checkpoints: &[U], pubkey: &Pubkey) -> Option<Account>
|
||||||
if let Some(account) = self.accounts.get(pubkey) {
|
where
|
||||||
return Some(account);
|
U: Deref<Target = Self>,
|
||||||
|
{
|
||||||
|
for db in checkpoints {
|
||||||
|
if let Some(account) = db.accounts.get(pubkey) {
|
||||||
|
return Some(account.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
/// Store the account update. If the update is to delete the account because the token balance
|
||||||
pub fn store(&mut self, pubkey: &Pubkey, account: &Account) {
|
/// 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) {
|
||||||
if account.tokens == 0 {
|
if account.tokens == 0 {
|
||||||
self.accounts.remove(pubkey);
|
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());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.accounts.insert(pubkey.clone(), account.clone());
|
self.accounts.insert(pubkey.clone(), account.clone());
|
||||||
}
|
}
|
||||||
|
@ -98,6 +107,7 @@ impl AccountsDB {
|
||||||
|
|
||||||
pub fn store_accounts(
|
pub fn store_accounts(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
purge: bool,
|
||||||
txs: &[Transaction],
|
txs: &[Transaction],
|
||||||
res: &[Result<()>],
|
res: &[Result<()>],
|
||||||
loaded: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
loaded: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
||||||
|
@ -108,65 +118,51 @@ impl AccountsDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx = &txs[i];
|
let tx = &txs[i];
|
||||||
let accs = raccs.as_ref().unwrap();
|
let acc = raccs.as_ref().unwrap();
|
||||||
for (key, account) in tx.account_keys.iter().zip(accs.0.iter()) {
|
for (key, account) in tx.account_keys.iter().zip(acc.0.iter()) {
|
||||||
self.store(key, account);
|
self.store(purge, key, account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn load_tx_accounts<U>(
|
||||||
fn load_tx_accounts(
|
checkpoints: &[U],
|
||||||
&self,
|
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
last_ids: &mut StatusDeque<Result<()>>,
|
|
||||||
max_age: usize,
|
|
||||||
error_counters: &mut ErrorCounters,
|
error_counters: &mut ErrorCounters,
|
||||||
) -> Result<Vec<Account>> {
|
) -> Result<Vec<Account>>
|
||||||
|
where
|
||||||
|
U: Deref<Target = Self>,
|
||||||
|
{
|
||||||
// Copy all the accounts
|
// Copy all the accounts
|
||||||
if tx.signatures.is_empty() && tx.fee != 0 {
|
if tx.signatures.is_empty() && tx.fee != 0 {
|
||||||
Err(BankError::MissingSignatureForFee)
|
Err(BankError::MissingSignatureForFee)
|
||||||
} else if tx.account_keys.is_empty() || self.load(&tx.account_keys[0]).is_none() {
|
|
||||||
error_counters.account_not_found += 1;
|
|
||||||
Err(BankError::AccountNotFound)
|
|
||||||
} else if self.load(&tx.account_keys[0]).unwrap().tokens < tx.fee {
|
|
||||||
error_counters.insufficient_funds += 1;
|
|
||||||
Err(BankError::InsufficientFundsForFee)
|
|
||||||
} else {
|
} else {
|
||||||
if !last_ids.check_entry_id_age(tx.last_id, max_age) {
|
|
||||||
error_counters.last_id_not_found += 1;
|
|
||||||
return Err(BankError::LastIdNotFound);
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is no way to predict what program will execute without an error
|
// 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
|
// If a fee can pay for execution then the program will be scheduled
|
||||||
last_ids
|
let mut called_accounts: Vec<Account> = vec![];
|
||||||
.reserve_signature_with_last_id(&tx.last_id, &tx.signatures[0])
|
for key in &tx.account_keys {
|
||||||
.map_err(|err| match err {
|
called_accounts.push(Self::load(checkpoints, key).unwrap_or_default());
|
||||||
StatusDequeError::LastIdNotFound => {
|
}
|
||||||
error_counters.reserve_last_id += 1;
|
if called_accounts.is_empty() || called_accounts[0].tokens == 0 {
|
||||||
BankError::LastIdNotFound
|
error_counters.account_not_found += 1;
|
||||||
}
|
Err(BankError::AccountNotFound)
|
||||||
StatusDequeError::DuplicateSignature => {
|
} else if called_accounts[0].tokens < tx.fee {
|
||||||
error_counters.duplicate_signature += 1;
|
error_counters.insufficient_funds += 1;
|
||||||
BankError::DuplicateSignature
|
Err(BankError::InsufficientFundsForFee)
|
||||||
}
|
} else {
|
||||||
})?;
|
called_accounts[0].tokens -= tx.fee;
|
||||||
|
Ok(called_accounts)
|
||||||
let mut called_accounts: Vec<Account> = tx
|
}
|
||||||
.account_keys
|
|
||||||
.iter()
|
|
||||||
.map(|key| self.load(key).cloned().unwrap_or_default())
|
|
||||||
.collect();
|
|
||||||
called_accounts[0].tokens -= tx.fee;
|
|
||||||
Ok(called_accounts)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_executable_accounts(
|
fn load_executable_accounts<U>(
|
||||||
&self,
|
checkpoints: &[U],
|
||||||
mut program_id: Pubkey,
|
mut program_id: Pubkey,
|
||||||
error_counters: &mut ErrorCounters,
|
error_counters: &mut ErrorCounters,
|
||||||
) -> Result<Vec<(Pubkey, Account)>> {
|
) -> Result<Vec<(Pubkey, Account)>>
|
||||||
|
where
|
||||||
|
U: Deref<Target = Self>,
|
||||||
|
{
|
||||||
let mut accounts = Vec::new();
|
let mut accounts = Vec::new();
|
||||||
let mut depth = 0;
|
let mut depth = 0;
|
||||||
loop {
|
loop {
|
||||||
|
@ -181,7 +177,7 @@ impl AccountsDB {
|
||||||
}
|
}
|
||||||
depth += 1;
|
depth += 1;
|
||||||
|
|
||||||
let program = match self.load(&program_id) {
|
let program = match Self::load(checkpoints, &program_id) {
|
||||||
Some(program) => program,
|
Some(program) => program,
|
||||||
None => {
|
None => {
|
||||||
error_counters.account_not_found += 1;
|
error_counters.account_not_found += 1;
|
||||||
|
@ -202,11 +198,14 @@ impl AccountsDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For each program_id in the transaction, load its loaders.
|
/// For each program_id in the transaction, load its loaders.
|
||||||
fn load_loaders(
|
fn load_loaders<U>(
|
||||||
&self,
|
checkpoints: &[U],
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
error_counters: &mut ErrorCounters,
|
error_counters: &mut ErrorCounters,
|
||||||
) -> Result<Vec<Vec<(Pubkey, Account)>>> {
|
) -> Result<Vec<Vec<(Pubkey, Account)>>>
|
||||||
|
where
|
||||||
|
U: Deref<Target = Self>,
|
||||||
|
{
|
||||||
tx.instructions
|
tx.instructions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ix| {
|
.map(|ix| {
|
||||||
|
@ -215,25 +214,26 @@ impl AccountsDB {
|
||||||
return Err(BankError::AccountNotFound);
|
return Err(BankError::AccountNotFound);
|
||||||
}
|
}
|
||||||
let program_id = tx.program_ids[ix.program_ids_index as usize];
|
let program_id = tx.program_ids[ix.program_ids_index as usize];
|
||||||
self.load_executable_accounts(program_id, error_counters)
|
Self::load_executable_accounts(checkpoints, program_id, error_counters)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_accounts(
|
fn load_accounts<U>(
|
||||||
&self,
|
checkpoints: &[U],
|
||||||
txs: &[Transaction],
|
txs: &[Transaction],
|
||||||
last_ids: &mut StatusDeque<Result<()>>,
|
|
||||||
lock_results: Vec<Result<()>>,
|
lock_results: Vec<Result<()>>,
|
||||||
max_age: usize,
|
|
||||||
error_counters: &mut ErrorCounters,
|
error_counters: &mut ErrorCounters,
|
||||||
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>> {
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>>
|
||||||
|
where
|
||||||
|
U: Deref<Target = Self>,
|
||||||
|
{
|
||||||
txs.iter()
|
txs.iter()
|
||||||
.zip(lock_results.into_iter())
|
.zip(lock_results.into_iter())
|
||||||
.map(|etx| match etx {
|
.map(|etx| match etx {
|
||||||
(tx, Ok(())) => {
|
(tx, Ok(())) => {
|
||||||
let accounts = self.load_tx_accounts(tx, last_ids, max_age, error_counters)?;
|
let accounts = Self::load_tx_accounts(checkpoints, tx, error_counters)?;
|
||||||
let loaders = self.load_loaders(tx, error_counters)?;
|
let loaders = Self::load_loaders(checkpoints, tx, error_counters)?;
|
||||||
Ok((accounts, loaders))
|
Ok((accounts, loaders))
|
||||||
}
|
}
|
||||||
(_, Err(e)) => Err(e),
|
(_, Err(e)) => Err(e),
|
||||||
|
@ -248,36 +248,35 @@ impl AccountsDB {
|
||||||
pub fn transaction_count(&self) -> u64 {
|
pub fn transaction_count(&self) -> u64 {
|
||||||
self.transaction_count
|
self.transaction_count
|
||||||
}
|
}
|
||||||
|
pub fn account_values_slow(&self) -> Vec<(Pubkey, solana_sdk::account::Account)> {
|
||||||
|
self.accounts.iter().map(|(x, y)| (*x, y.clone())).collect()
|
||||||
|
}
|
||||||
|
fn merge(&mut self, other: Self) {
|
||||||
|
self.transaction_count += other.transaction_count;
|
||||||
|
self.accounts.extend(other.accounts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Accounts {
|
impl Accounts {
|
||||||
// TODO use a fork
|
/// Slow because lock is held for 1 operation insted of many
|
||||||
pub fn copy_for_tpu(&self) -> Self {
|
pub fn load_slow<U>(checkpoints: &[U], pubkey: &Pubkey) -> Option<Account>
|
||||||
let copy = Accounts::default();
|
where
|
||||||
|
U: Deref<Target = Self>,
|
||||||
{
|
{
|
||||||
let mut accounts_db = copy.accounts_db.write().unwrap();
|
let dbs: Vec<_> = checkpoints
|
||||||
for (key, val) in self.accounts_db.read().unwrap().accounts.iter() {
|
.iter()
|
||||||
accounts_db.accounts.insert(key.clone(), val.clone());
|
.map(|obj| obj.accounts_db.read().unwrap())
|
||||||
}
|
.collect();
|
||||||
accounts_db.transaction_count = self.transaction_count();
|
AccountsDB::load(&dbs, pubkey)
|
||||||
}
|
|
||||||
|
|
||||||
copy
|
|
||||||
}
|
}
|
||||||
|
/// Slow because lock is held for 1 operation insted of many
|
||||||
pub fn keys(&self) -> Vec<Pubkey> {
|
/// * purge - if the account token value is 0 and purge is true then delete the account.
|
||||||
self.accounts_db.read().unwrap().keys()
|
/// 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
|
||||||
/// Slow because lock is held for 1 operation instead of many
|
.write()
|
||||||
pub fn load_slow(&self, pubkey: &Pubkey) -> Option<Account> {
|
.unwrap()
|
||||||
self.accounts_db.read().unwrap().load(pubkey).cloned()
|
.store(purge, pubkey, account)
|
||||||
}
|
|
||||||
|
|
||||||
/// Slow because lock is held for 1 operation instead of many
|
|
||||||
pub fn store_slow(&self, pubkey: &Pubkey, account: &Account) {
|
|
||||||
self.accounts_db.write().unwrap().store(pubkey, account)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lock_account(
|
fn lock_account(
|
||||||
|
@ -341,25 +340,28 @@ impl Accounts {
|
||||||
.for_each(|(tx, result)| Self::unlock_account(tx, result, &mut account_locks));
|
.for_each(|(tx, result)| Self::unlock_account(tx, result, &mut account_locks));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_accounts(
|
pub fn load_accounts<U>(
|
||||||
&self,
|
checkpoints: &[U],
|
||||||
txs: &[Transaction],
|
txs: &[Transaction],
|
||||||
last_ids: &mut StatusDeque<Result<()>>,
|
results: Vec<Result<()>>,
|
||||||
lock_results: Vec<Result<()>>,
|
|
||||||
max_age: usize,
|
|
||||||
error_counters: &mut ErrorCounters,
|
error_counters: &mut ErrorCounters,
|
||||||
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>> {
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>>
|
||||||
self.accounts_db.read().unwrap().load_accounts(
|
where
|
||||||
txs,
|
U: Deref<Target = Self>,
|
||||||
last_ids,
|
{
|
||||||
lock_results,
|
let dbs: Vec<_> = checkpoints
|
||||||
max_age,
|
.iter()
|
||||||
error_counters,
|
.map(|obj| obj.accounts_db.read().unwrap())
|
||||||
)
|
.collect();
|
||||||
|
AccountsDB::load_accounts(&dbs, txs, results, error_counters)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
pub fn store_accounts(
|
pub fn store_accounts(
|
||||||
&self,
|
&self,
|
||||||
|
purge: bool,
|
||||||
txs: &[Transaction],
|
txs: &[Transaction],
|
||||||
res: &[Result<()>],
|
res: &[Result<()>],
|
||||||
loaded: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
loaded: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
||||||
|
@ -367,7 +369,7 @@ impl Accounts {
|
||||||
self.accounts_db
|
self.accounts_db
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.store_accounts(txs, res, loaded)
|
.store_accounts(purge, txs, res, loaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn increment_transaction_count(&self, tx_count: usize) {
|
pub fn increment_transaction_count(&self, tx_count: usize) {
|
||||||
|
@ -380,6 +382,27 @@ impl Accounts {
|
||||||
pub fn transaction_count(&self) -> u64 {
|
pub fn transaction_count(&self) -> u64 {
|
||||||
self.accounts_db.read().unwrap().transaction_count()
|
self.accounts_db.read().unwrap().transaction_count()
|
||||||
}
|
}
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -394,22 +417,30 @@ mod tests {
|
||||||
use solana_sdk::transaction::Instruction;
|
use solana_sdk::transaction::Instruction;
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::Transaction;
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
}
|
||||||
|
|
||||||
fn load_accounts(
|
fn load_accounts(
|
||||||
tx: Transaction,
|
tx: Transaction,
|
||||||
ka: &Vec<(Pubkey, Account)>,
|
ka: &Vec<(Pubkey, Account)>,
|
||||||
error_counters: &mut ErrorCounters,
|
error_counters: &mut ErrorCounters,
|
||||||
max_age: usize,
|
|
||||||
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>> {
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>> {
|
||||||
let accounts = Accounts::default();
|
let accounts = Accounts::default();
|
||||||
for ka in ka.iter() {
|
for ka in ka.iter() {
|
||||||
accounts.store_slow(&ka.0, &ka.1);
|
accounts.store_slow(true, &ka.0, &ka.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = Default::default();
|
Accounts::load_accounts(&[&accounts], &[tx], vec![Ok(())], error_counters)
|
||||||
let mut last_ids: StatusDeque<Result<()>> = StatusDeque::default();
|
|
||||||
last_ids.register_tick(&id);
|
|
||||||
|
|
||||||
accounts.load_accounts(&[tx], &mut last_ids, vec![Ok(())], max_age, error_counters)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_counters(error_counters: &ErrorCounters, expected: [usize; 8]) {
|
fn assert_counters(error_counters: &ErrorCounters, expected: [usize; 8]) {
|
||||||
|
@ -423,34 +454,6 @@ mod tests {
|
||||||
assert_eq!(error_counters.missing_signature_for_fee, expected[7]);
|
assert_eq!(error_counters.missing_signature_for_fee, expected[7]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_load_accounts_index_out_of_bounds() {
|
|
||||||
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, 1])];
|
|
||||||
let tx = Transaction::new_with_instructions(
|
|
||||||
&[&keypair],
|
|
||||||
&[], // TODO this should contain a key, should fail
|
|
||||||
Hash::default(),
|
|
||||||
0,
|
|
||||||
vec![solana_native_loader::id()],
|
|
||||||
instructions,
|
|
||||||
);
|
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 10);
|
|
||||||
|
|
||||||
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
|
||||||
assert_eq!(loaded_accounts[0], Err(BankError::AccountNotFound));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_load_accounts_no_key() {
|
fn test_load_accounts_no_key() {
|
||||||
let accounts: Vec<(Pubkey, Account)> = Vec::new();
|
let accounts: Vec<(Pubkey, Account)> = Vec::new();
|
||||||
|
@ -466,7 +469,7 @@ mod tests {
|
||||||
instructions,
|
instructions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 10);
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
||||||
|
|
||||||
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
|
@ -490,7 +493,7 @@ mod tests {
|
||||||
instructions,
|
instructions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 10);
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
||||||
|
|
||||||
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
|
@ -522,45 +525,13 @@ mod tests {
|
||||||
instructions,
|
instructions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 10);
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
||||||
|
|
||||||
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
assert_eq!(loaded_accounts[0], Err(BankError::AccountNotFound));
|
assert_eq!(loaded_accounts[0], Err(BankError::AccountNotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_load_accounts_max_age() {
|
|
||||||
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![solana_native_loader::id()],
|
|
||||||
instructions,
|
|
||||||
);
|
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 0);
|
|
||||||
|
|
||||||
assert_counters(&error_counters, [0, 0, 1, 0, 0, 0, 0, 0]);
|
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
|
||||||
assert_eq!(loaded_accounts[0], Err(BankError::LastIdNotFound));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_load_accounts_insufficient_funds() {
|
fn test_load_accounts_insufficient_funds() {
|
||||||
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
let mut accounts: Vec<(Pubkey, Account)> = Vec::new();
|
||||||
|
@ -582,7 +553,7 @@ mod tests {
|
||||||
instructions,
|
instructions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 10);
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
||||||
|
|
||||||
assert_counters(&error_counters, [0, 0, 0, 0, 1, 0, 0, 0]);
|
assert_counters(&error_counters, [0, 0, 0, 0, 1, 0, 0, 0]);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
|
@ -614,7 +585,7 @@ mod tests {
|
||||||
instructions,
|
instructions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 10);
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
||||||
|
|
||||||
assert_counters(&error_counters, [0, 0, 0, 0, 0, 0, 0, 0]);
|
assert_counters(&error_counters, [0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
|
@ -686,7 +657,7 @@ mod tests {
|
||||||
instructions,
|
instructions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 10);
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
||||||
|
|
||||||
assert_counters(&error_counters, [0, 0, 0, 0, 0, 0, 1, 0]);
|
assert_counters(&error_counters, [0, 0, 0, 0, 0, 0, 1, 0]);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
|
@ -720,7 +691,7 @@ mod tests {
|
||||||
instructions,
|
instructions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 10);
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
||||||
|
|
||||||
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
|
@ -753,7 +724,7 @@ mod tests {
|
||||||
instructions,
|
instructions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 10);
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
||||||
|
|
||||||
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
assert_counters(&error_counters, [1, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
|
@ -802,7 +773,7 @@ mod tests {
|
||||||
instructions,
|
instructions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters, 10);
|
let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters);
|
||||||
|
|
||||||
assert_counters(&error_counters, [0, 0, 0, 0, 0, 0, 0, 0]);
|
assert_counters(&error_counters, [0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
|
|
65
src/bank.rs
65
src/bank.rs
|
@ -11,7 +11,7 @@ use crate::genesis_block::GenesisBlock;
|
||||||
use crate::leader_scheduler::LeaderScheduler;
|
use crate::leader_scheduler::LeaderScheduler;
|
||||||
use crate::poh_recorder::PohRecorder;
|
use crate::poh_recorder::PohRecorder;
|
||||||
use crate::runtime::{self, RuntimeError};
|
use crate::runtime::{self, RuntimeError};
|
||||||
use crate::status_deque::{Status, StatusDeque, MAX_ENTRY_IDS};
|
use crate::status_deque::{Status, StatusDeque, StatusDequeError, MAX_ENTRY_IDS};
|
||||||
use bincode::deserialize;
|
use bincode::deserialize;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::Level;
|
use log::Level;
|
||||||
|
@ -159,13 +159,14 @@ impl Bank {
|
||||||
mint_account.tokens -= genesis_block.bootstrap_leader_tokens;
|
mint_account.tokens -= genesis_block.bootstrap_leader_tokens;
|
||||||
bootstrap_leader_account.tokens += genesis_block.bootstrap_leader_tokens;
|
bootstrap_leader_account.tokens += genesis_block.bootstrap_leader_tokens;
|
||||||
self.accounts.store_slow(
|
self.accounts.store_slow(
|
||||||
|
true,
|
||||||
&genesis_block.bootstrap_leader_id,
|
&genesis_block.bootstrap_leader_id,
|
||||||
&bootstrap_leader_account,
|
&bootstrap_leader_account,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.accounts
|
self.accounts
|
||||||
.store_slow(&genesis_block.mint_id, &mint_account);
|
.store_slow(true, &genesis_block.mint_id, &mint_account);
|
||||||
self.register_tick(&genesis_block.last_id());
|
self.register_tick(&genesis_block.last_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ impl Bank {
|
||||||
loader: solana_native_loader::id(),
|
loader: solana_native_loader::id(),
|
||||||
};
|
};
|
||||||
self.accounts
|
self.accounts
|
||||||
.store_slow(&system_program::id(), &system_program_account);
|
.store_slow(true, &system_program::id(), &system_program_account);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_builtin_programs(&self) {
|
fn add_builtin_programs(&self) {
|
||||||
|
@ -193,7 +194,7 @@ impl Bank {
|
||||||
loader: solana_native_loader::id(),
|
loader: solana_native_loader::id(),
|
||||||
};
|
};
|
||||||
self.accounts
|
self.accounts
|
||||||
.store_slow(&vote_program::id(), &vote_program_account);
|
.store_slow(true, &vote_program::id(), &vote_program_account);
|
||||||
|
|
||||||
// Storage program
|
// Storage program
|
||||||
let storage_program_account = Account {
|
let storage_program_account = Account {
|
||||||
|
@ -204,7 +205,7 @@ impl Bank {
|
||||||
loader: solana_native_loader::id(),
|
loader: solana_native_loader::id(),
|
||||||
};
|
};
|
||||||
self.accounts
|
self.accounts
|
||||||
.store_slow(&storage_program::id(), &storage_program_account);
|
.store_slow(true, &storage_program::id(), &storage_program_account);
|
||||||
|
|
||||||
let storage_system_account = Account {
|
let storage_system_account = Account {
|
||||||
tokens: 1,
|
tokens: 1,
|
||||||
|
@ -214,7 +215,7 @@ impl Bank {
|
||||||
loader: Pubkey::default(),
|
loader: Pubkey::default(),
|
||||||
};
|
};
|
||||||
self.accounts
|
self.accounts
|
||||||
.store_slow(&storage_program::system_id(), &storage_system_account);
|
.store_slow(true, &storage_program::system_id(), &storage_system_account);
|
||||||
|
|
||||||
// Bpf Loader
|
// Bpf Loader
|
||||||
let bpf_loader_account = Account {
|
let bpf_loader_account = Account {
|
||||||
|
@ -226,7 +227,7 @@ impl Bank {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.accounts
|
self.accounts
|
||||||
.store_slow(&bpf_loader::id(), &bpf_loader_account);
|
.store_slow(true, &bpf_loader::id(), &bpf_loader_account);
|
||||||
|
|
||||||
// Budget program
|
// Budget program
|
||||||
let budget_program_account = Account {
|
let budget_program_account = Account {
|
||||||
|
@ -237,7 +238,7 @@ impl Bank {
|
||||||
loader: solana_native_loader::id(),
|
loader: solana_native_loader::id(),
|
||||||
};
|
};
|
||||||
self.accounts
|
self.accounts
|
||||||
.store_slow(&budget_program::id(), &budget_program_account);
|
.store_slow(true, &budget_program::id(), &budget_program_account);
|
||||||
|
|
||||||
// Erc20 token program
|
// Erc20 token program
|
||||||
let erc20_account = Account {
|
let erc20_account = Account {
|
||||||
|
@ -249,7 +250,7 @@ impl Bank {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.accounts
|
self.accounts
|
||||||
.store_slow(&token_program::id(), &erc20_account);
|
.store_slow(true, &token_program::id(), &erc20_account);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the last entry ID registered.
|
/// Return the last entry ID registered.
|
||||||
|
@ -428,17 +429,46 @@ impl Bank {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_accounts(
|
fn load_accounts(
|
||||||
|
&self,
|
||||||
|
txs: &[Transaction],
|
||||||
|
results: Vec<Result<()>>,
|
||||||
|
error_counters: &mut ErrorCounters,
|
||||||
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>> {
|
||||||
|
Accounts::load_accounts(&[&self.accounts], txs, results, error_counters)
|
||||||
|
}
|
||||||
|
fn check_signatures(
|
||||||
&self,
|
&self,
|
||||||
txs: &[Transaction],
|
txs: &[Transaction],
|
||||||
lock_results: Vec<Result<()>>,
|
lock_results: Vec<Result<()>>,
|
||||||
max_age: usize,
|
max_age: usize,
|
||||||
error_counters: &mut ErrorCounters,
|
error_counters: &mut ErrorCounters,
|
||||||
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>> {
|
) -> Vec<Result<()>> {
|
||||||
let mut last_ids = self.last_ids.write().unwrap();
|
let mut last_ids = self.last_ids.write().unwrap();
|
||||||
self.accounts
|
txs.iter()
|
||||||
.load_accounts(txs, &mut last_ids, lock_results, max_age, error_counters)
|
.zip(lock_results.into_iter())
|
||||||
|
.map(|(tx, lock_res)| {
|
||||||
|
if lock_res.is_ok() {
|
||||||
|
let r = if !last_ids.check_entry_id_age(tx.last_id, max_age) {
|
||||||
|
Err(StatusDequeError::LastIdNotFound)
|
||||||
|
} else {
|
||||||
|
last_ids.reserve_signature_with_last_id(&tx.last_id, &tx.signatures[0])
|
||||||
|
};
|
||||||
|
r.map_err(|err| match err {
|
||||||
|
StatusDequeError::LastIdNotFound => {
|
||||||
|
error_counters.reserve_last_id += 1;
|
||||||
|
BankError::LastIdNotFound
|
||||||
|
}
|
||||||
|
StatusDequeError::DuplicateSignature => {
|
||||||
|
error_counters.duplicate_signature += 1;
|
||||||
|
BankError::DuplicateSignature
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
lock_res
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn load_and_execute_transactions(
|
fn load_and_execute_transactions(
|
||||||
&self,
|
&self,
|
||||||
|
@ -452,8 +482,8 @@ impl Bank {
|
||||||
debug!("processing transactions: {}", txs.len());
|
debug!("processing transactions: {}", txs.len());
|
||||||
let mut error_counters = ErrorCounters::default();
|
let mut error_counters = ErrorCounters::default();
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let mut loaded_accounts =
|
let sig_results = self.check_signatures(txs, lock_results, max_age, &mut error_counters);
|
||||||
self.load_accounts(txs, lock_results, max_age, &mut error_counters);
|
let mut loaded_accounts = self.load_accounts(txs, sig_results, &mut error_counters);
|
||||||
let tick_height = self.tick_height();
|
let tick_height = self.tick_height();
|
||||||
|
|
||||||
let load_elapsed = now.elapsed();
|
let load_elapsed = now.elapsed();
|
||||||
|
@ -539,7 +569,8 @@ impl Bank {
|
||||||
executed: &[Result<()>],
|
executed: &[Result<()>],
|
||||||
) {
|
) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
self.accounts.store_accounts(txs, executed, loaded_accounts);
|
self.accounts
|
||||||
|
.store_accounts(true, txs, executed, loaded_accounts);
|
||||||
|
|
||||||
// Check account subscriptions and send notifications
|
// Check account subscriptions and send notifications
|
||||||
self.send_account_notifications(txs, executed, loaded_accounts);
|
self.send_account_notifications(txs, executed, loaded_accounts);
|
||||||
|
@ -753,7 +784,7 @@ impl Bank {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_account(&self, pubkey: &Pubkey) -> Option<Account> {
|
pub fn get_account(&self, pubkey: &Pubkey) -> Option<Account> {
|
||||||
self.accounts.load_slow(pubkey)
|
Accounts::load_slow(&[&self.accounts], pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transaction_count(&self) -> u64 {
|
pub fn transaction_count(&self) -> u64 {
|
||||||
|
|
Loading…
Reference in New Issue