2018-11-25 21:56:48 -08:00
|
|
|
//! The `bank` module tracks client accounts and the progress of on-chain
|
|
|
|
//! programs. It offers a high-level API that signs transactions
|
2018-06-06 08:49:22 -07:00
|
|
|
//! on behalf of the caller, and a low-level API for when they have
|
2018-03-29 11:20:54 -07:00
|
|
|
//! already been signed and verified.
|
2018-02-23 13:08:19 -08:00
|
|
|
|
2019-05-07 15:51:35 -07:00
|
|
|
use crate::accounts::{AccountLockType, Accounts};
|
2019-05-09 19:27:27 -07:00
|
|
|
use crate::accounts_db::{ErrorCounters, InstructionAccounts, InstructionLoaders};
|
2019-05-06 07:31:50 -07:00
|
|
|
use crate::accounts_index::Fork;
|
2019-03-14 11:56:36 -07:00
|
|
|
use crate::blockhash_queue::BlockhashQueue;
|
2019-04-02 03:55:42 -07:00
|
|
|
use crate::locked_accounts_results::LockedAccountsResults;
|
2019-04-02 08:35:38 -07:00
|
|
|
use crate::message_processor::{MessageProcessor, ProcessInstruction};
|
2019-01-31 06:53:52 -08:00
|
|
|
use crate::status_cache::StatusCache;
|
2019-05-09 19:27:27 -07:00
|
|
|
use bincode::serialize;
|
|
|
|
use hashbrown::HashMap;
|
2019-02-27 20:57:01 -08:00
|
|
|
use log::*;
|
2019-02-18 22:26:22 -08:00
|
|
|
use solana_metrics::counter::Counter;
|
2019-03-29 21:21:59 -07:00
|
|
|
use solana_metrics::influxdb;
|
2018-11-23 19:42:01 -08:00
|
|
|
use solana_sdk::account::Account;
|
2019-03-29 15:11:21 -07:00
|
|
|
use solana_sdk::fee_calculator::FeeCalculator;
|
2019-02-18 22:26:22 -08:00
|
|
|
use solana_sdk::genesis_block::GenesisBlock;
|
2019-02-18 10:23:09 -08:00
|
|
|
use solana_sdk::hash::{extend_and_hash, Hash};
|
2019-02-07 08:03:40 -08:00
|
|
|
use solana_sdk::native_loader;
|
2018-10-25 11:13:08 -07:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2019-02-18 19:28:10 -08:00
|
|
|
use solana_sdk::signature::{Keypair, Signature};
|
2019-04-03 08:45:57 -07:00
|
|
|
use solana_sdk::system_transaction;
|
2019-03-29 21:21:59 -07:00
|
|
|
use solana_sdk::timing::{duration_as_ms, duration_as_us, MAX_RECENT_BLOCKHASHES};
|
2019-04-05 09:42:54 -07:00
|
|
|
use solana_sdk::transaction::{Result, Transaction, TransactionError};
|
2019-05-07 11:16:22 -07:00
|
|
|
use solana_vote_api::vote_state::MAX_LOCKOUT_HISTORY;
|
2019-05-07 15:51:35 -07:00
|
|
|
use std::borrow::Borrow;
|
2019-04-25 16:57:25 -07:00
|
|
|
use std::cmp;
|
2019-03-13 14:06:12 -07:00
|
|
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
2018-12-17 12:41:23 -08:00
|
|
|
use std::sync::{Arc, RwLock};
|
2018-06-07 19:35:38 -07:00
|
|
|
use std::time::Instant;
|
2018-03-02 09:16:39 -08:00
|
|
|
|
2019-04-29 15:26:52 -07:00
|
|
|
pub const MINIMUM_SLOT_LENGTH: usize = MAX_LOCKOUT_HISTORY + 1;
|
|
|
|
|
2019-05-09 19:27:27 -07:00
|
|
|
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
2019-03-06 16:32:23 -08:00
|
|
|
pub struct EpochSchedule {
|
|
|
|
/// The maximum number of slots in each epoch.
|
|
|
|
pub slots_per_epoch: u64,
|
|
|
|
|
|
|
|
/// A number of slots before slot_index 0. Used to calculate finalized staked nodes.
|
|
|
|
pub stakers_slot_offset: u64,
|
|
|
|
|
2019-04-29 15:26:52 -07:00
|
|
|
/// basically: log2(slots_per_epoch) - log2(MINIMUM_SLOT_LEN)
|
2019-03-06 16:32:23 -08:00
|
|
|
pub first_normal_epoch: u64,
|
|
|
|
|
2019-04-29 15:26:52 -07:00
|
|
|
/// basically: 2.pow(first_normal_epoch) - MINIMUM_SLOT_LEN
|
2019-03-06 16:32:23 -08:00
|
|
|
pub first_normal_slot: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EpochSchedule {
|
|
|
|
pub fn new(slots_per_epoch: u64, stakers_slot_offset: u64, warmup: bool) -> Self {
|
2019-04-29 15:26:52 -07:00
|
|
|
assert!(slots_per_epoch >= MINIMUM_SLOT_LENGTH as u64);
|
2019-03-06 16:32:23 -08:00
|
|
|
let (first_normal_epoch, first_normal_slot) = if warmup {
|
|
|
|
let next_power_of_two = slots_per_epoch.next_power_of_two();
|
2019-04-29 15:26:52 -07:00
|
|
|
let log2_slots_per_epoch = next_power_of_two
|
|
|
|
.trailing_zeros()
|
|
|
|
.saturating_sub(MINIMUM_SLOT_LENGTH.trailing_zeros());
|
2019-03-06 16:32:23 -08:00
|
|
|
|
2019-04-29 15:26:52 -07:00
|
|
|
(
|
|
|
|
u64::from(log2_slots_per_epoch),
|
|
|
|
next_power_of_two.saturating_sub(MINIMUM_SLOT_LENGTH as u64),
|
|
|
|
)
|
2019-03-06 16:32:23 -08:00
|
|
|
} else {
|
|
|
|
(0, 0)
|
|
|
|
};
|
|
|
|
EpochSchedule {
|
|
|
|
slots_per_epoch,
|
|
|
|
stakers_slot_offset,
|
|
|
|
first_normal_epoch,
|
|
|
|
first_normal_slot,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// get the length of the given epoch (in slots)
|
|
|
|
pub fn get_slots_in_epoch(&self, epoch: u64) -> u64 {
|
|
|
|
if epoch < self.first_normal_epoch {
|
2019-04-29 15:26:52 -07:00
|
|
|
2u64.pow(epoch as u32 + MINIMUM_SLOT_LENGTH.trailing_zeros() as u32)
|
2019-03-06 16:32:23 -08:00
|
|
|
} else {
|
|
|
|
self.slots_per_epoch
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// get the epoch for which the given slot should save off
|
|
|
|
/// information about stakers
|
|
|
|
pub fn get_stakers_epoch(&self, slot: u64) -> u64 {
|
|
|
|
if slot < self.first_normal_slot {
|
|
|
|
// until we get to normal slots, behave as if stakers_slot_offset == slots_per_epoch
|
|
|
|
self.get_epoch_and_slot_index(slot).0 + 1
|
|
|
|
} else {
|
|
|
|
self.first_normal_epoch
|
|
|
|
+ (slot - self.first_normal_slot + self.stakers_slot_offset) / self.slots_per_epoch
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// get epoch and offset into the epoch for the given slot
|
|
|
|
pub fn get_epoch_and_slot_index(&self, slot: u64) -> (u64, u64) {
|
|
|
|
if slot < self.first_normal_slot {
|
2019-04-29 15:26:52 -07:00
|
|
|
let epoch = (slot + MINIMUM_SLOT_LENGTH as u64 + 1)
|
|
|
|
.next_power_of_two()
|
|
|
|
.trailing_zeros()
|
|
|
|
- MINIMUM_SLOT_LENGTH.trailing_zeros()
|
|
|
|
- 1;
|
2019-03-06 16:32:23 -08:00
|
|
|
|
2019-04-29 15:26:52 -07:00
|
|
|
let epoch_len = 2u64.pow(epoch + MINIMUM_SLOT_LENGTH.trailing_zeros());
|
2019-03-06 16:32:23 -08:00
|
|
|
|
2019-04-29 15:26:52 -07:00
|
|
|
(
|
|
|
|
u64::from(epoch),
|
|
|
|
slot - (epoch_len - MINIMUM_SLOT_LENGTH as u64),
|
|
|
|
)
|
2019-03-06 16:32:23 -08:00
|
|
|
} else {
|
|
|
|
(
|
|
|
|
self.first_normal_epoch + ((slot - self.first_normal_slot) / self.slots_per_epoch),
|
|
|
|
(slot - self.first_normal_slot) % self.slots_per_epoch,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2019-05-09 16:06:57 -07:00
|
|
|
|
|
|
|
pub fn get_first_slot_in_epoch(&self, epoch: u64) -> u64 {
|
|
|
|
if epoch <= self.first_normal_epoch {
|
|
|
|
(2u64.pow(epoch as u32) - 1) * MINIMUM_SLOT_LENGTH as u64
|
|
|
|
} else {
|
|
|
|
(epoch - self.first_normal_epoch) * self.slots_per_epoch + self.first_normal_slot
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_last_slot_in_epoch(&self, epoch: u64) -> u64 {
|
|
|
|
self.get_first_slot_in_epoch(epoch) + self.get_slots_in_epoch(epoch) - 1
|
|
|
|
}
|
2019-03-06 16:32:23 -08:00
|
|
|
}
|
|
|
|
|
2019-03-29 10:03:55 -07:00
|
|
|
type BankStatusCache = StatusCache<Result<()>>;
|
2019-01-31 06:53:52 -08:00
|
|
|
|
2019-05-09 19:27:27 -07:00
|
|
|
/// Manager for the state of all accounts and programs after processing its entries.
|
2019-02-21 16:01:10 -08:00
|
|
|
#[derive(Default)]
|
2019-05-09 19:27:27 -07:00
|
|
|
pub struct Bank {
|
2019-03-06 16:32:23 -08:00
|
|
|
/// where all the Accounts are stored
|
2019-03-12 14:36:09 -07:00
|
|
|
accounts: Arc<Accounts>,
|
2018-10-04 13:15:54 -07:00
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
/// A cache of signature statuses
|
2019-03-29 10:03:55 -07:00
|
|
|
status_cache: Arc<RwLock<BankStatusCache>>,
|
2019-01-31 06:53:52 -08:00
|
|
|
|
2019-05-09 19:27:06 -07:00
|
|
|
/// FIFO queue of `recent_blockhash` items
|
|
|
|
blockhash_queue: RwLock<BlockhashQueue>,
|
2019-02-18 10:23:09 -08:00
|
|
|
|
2019-05-09 19:27:27 -07:00
|
|
|
/// Previous checkpoint of this bank
|
|
|
|
parent: RwLock<Option<Arc<Bank>>>,
|
|
|
|
|
2019-04-15 17:15:50 -07:00
|
|
|
/// The set of parents including this bank
|
2019-04-28 10:27:09 -07:00
|
|
|
pub ancestors: HashMap<u64, usize>,
|
2019-04-15 17:15:50 -07:00
|
|
|
|
2019-02-25 14:05:02 -08:00
|
|
|
/// Hash of this Bank's state. Only meaningful after freezing.
|
2019-02-20 14:26:57 -08:00
|
|
|
hash: RwLock<Hash>,
|
2019-02-21 09:22:31 -08:00
|
|
|
|
2019-02-25 14:05:02 -08:00
|
|
|
/// Hash of this Bank's parent's state
|
|
|
|
parent_hash: Hash,
|
|
|
|
|
2019-04-05 10:42:25 -07:00
|
|
|
/// The number of transactions processed without error
|
|
|
|
transaction_count: AtomicUsize, // TODO: Use AtomicU64 if/when available
|
|
|
|
|
2019-03-01 14:08:39 -08:00
|
|
|
/// Bank tick height
|
|
|
|
tick_height: AtomicUsize, // TODO: Use AtomicU64 if/when available
|
|
|
|
|
2019-04-14 19:15:31 -07:00
|
|
|
// Bank max_tick_height
|
|
|
|
max_tick_height: u64,
|
|
|
|
|
2019-02-21 09:22:31 -08:00
|
|
|
/// The number of ticks in each slot.
|
|
|
|
ticks_per_slot: u64,
|
|
|
|
|
2019-03-06 16:32:23 -08:00
|
|
|
/// Bank fork (i.e. slot, i.e. block)
|
|
|
|
slot: u64,
|
2019-02-26 05:43:43 -08:00
|
|
|
|
2019-04-16 19:35:38 -07:00
|
|
|
/// Bank height in term of banks
|
|
|
|
bank_height: u64,
|
|
|
|
|
2019-02-26 05:43:43 -08:00
|
|
|
/// The pubkey to send transactions fees to.
|
|
|
|
collector_id: Pubkey,
|
2019-03-01 11:54:28 -08:00
|
|
|
|
2019-03-29 15:11:21 -07:00
|
|
|
/// An object to calculate transaction fees.
|
|
|
|
pub fee_calculator: FeeCalculator,
|
|
|
|
|
2019-03-06 16:32:23 -08:00
|
|
|
/// initialized from genesis
|
|
|
|
epoch_schedule: EpochSchedule,
|
|
|
|
|
2019-04-05 14:23:00 -07:00
|
|
|
/// cache of vote_account state for this fork
|
|
|
|
vote_accounts: RwLock<HashMap<Pubkey, Account>>,
|
|
|
|
|
2019-03-01 11:54:28 -08:00
|
|
|
/// staked nodes on epoch boundaries, saved off when a bank.slot() is at
|
|
|
|
/// a leader schedule boundary
|
2019-03-01 17:22:49 -08:00
|
|
|
epoch_vote_accounts: HashMap<u64, HashMap<Pubkey, Account>>,
|
2019-03-13 14:06:12 -07:00
|
|
|
|
|
|
|
/// A boolean reflecting whether any entries were recorded into the PoH
|
|
|
|
/// stream for the slot == self.slot
|
|
|
|
is_delta: AtomicBool,
|
2019-03-15 19:48:11 -07:00
|
|
|
|
2019-04-02 08:35:38 -07:00
|
|
|
/// The Message processor
|
|
|
|
message_processor: MessageProcessor,
|
2019-02-21 09:22:31 -08:00
|
|
|
}
|
|
|
|
|
2019-03-14 11:56:36 -07:00
|
|
|
impl Default for BlockhashQueue {
|
2019-03-01 15:39:48 -08:00
|
|
|
fn default() -> Self {
|
2019-03-02 10:25:16 -08:00
|
|
|
Self::new(MAX_RECENT_BLOCKHASHES)
|
2019-03-01 15:39:48 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-29 11:58:29 -07:00
|
|
|
impl Bank {
|
2019-02-16 10:03:39 -08:00
|
|
|
pub fn new(genesis_block: &GenesisBlock) -> Self {
|
2019-02-25 21:22:00 -08:00
|
|
|
Self::new_with_paths(&genesis_block, None)
|
2018-12-24 16:11:20 -08:00
|
|
|
}
|
|
|
|
|
2019-02-25 21:22:00 -08:00
|
|
|
pub fn new_with_paths(genesis_block: &GenesisBlock, paths: Option<String>) -> Self {
|
2019-02-21 16:01:10 -08:00
|
|
|
let mut bank = Self::default();
|
2019-04-15 17:15:50 -07:00
|
|
|
bank.ancestors.insert(bank.slot(), 0);
|
2019-05-09 19:27:27 -07:00
|
|
|
bank.accounts = Arc::new(Accounts::new(paths));
|
2019-02-16 10:03:39 -08:00
|
|
|
bank.process_genesis_block(genesis_block);
|
2019-03-01 11:54:28 -08:00
|
|
|
// genesis needs stakes for all epochs up to the epoch implied by
|
|
|
|
// slot = 0 and genesis configuration
|
2019-04-05 14:23:00 -07:00
|
|
|
let vote_accounts = bank.vote_accounts();
|
2019-03-06 16:32:23 -08:00
|
|
|
for i in 0..=bank.get_stakers_epoch(bank.slot) {
|
2019-03-01 17:22:49 -08:00
|
|
|
bank.epoch_vote_accounts.insert(i, vote_accounts.clone());
|
2019-03-01 11:54:28 -08:00
|
|
|
}
|
2019-02-16 10:03:39 -08:00
|
|
|
bank
|
|
|
|
}
|
|
|
|
|
2019-02-19 15:35:02 -08:00
|
|
|
/// Create a new bank that points to an immutable checkpoint of another bank.
|
2019-03-09 19:28:43 -08:00
|
|
|
pub fn new_from_parent(parent: &Arc<Bank>, collector_id: &Pubkey, slot: u64) -> Self {
|
2019-02-25 14:05:02 -08:00
|
|
|
parent.freeze();
|
2019-03-04 19:22:23 -08:00
|
|
|
assert_ne!(slot, parent.slot());
|
|
|
|
|
2019-02-16 19:58:07 -08:00
|
|
|
let mut bank = Self::default();
|
2019-03-02 10:25:16 -08:00
|
|
|
bank.blockhash_queue = RwLock::new(parent.blockhash_queue.read().unwrap().clone());
|
2019-05-09 19:27:27 -07:00
|
|
|
bank.status_cache = parent.status_cache.clone();
|
2019-04-16 19:35:38 -07:00
|
|
|
bank.bank_height = parent.bank_height + 1;
|
2019-05-07 20:28:41 -07:00
|
|
|
bank.fee_calculator = parent.fee_calculator.clone();
|
2019-03-29 10:03:55 -07:00
|
|
|
|
2019-04-05 10:42:25 -07:00
|
|
|
bank.transaction_count
|
|
|
|
.store(parent.transaction_count() as usize, Ordering::Relaxed);
|
2019-04-05 14:23:00 -07:00
|
|
|
bank.vote_accounts = RwLock::new(parent.vote_accounts());
|
2019-04-05 10:42:25 -07:00
|
|
|
|
2019-03-01 14:08:39 -08:00
|
|
|
bank.tick_height
|
|
|
|
.store(parent.tick_height.load(Ordering::SeqCst), Ordering::SeqCst);
|
2019-02-21 10:59:03 -08:00
|
|
|
bank.ticks_per_slot = parent.ticks_per_slot;
|
2019-03-06 16:32:23 -08:00
|
|
|
bank.epoch_schedule = parent.epoch_schedule;
|
2019-02-21 10:59:03 -08:00
|
|
|
|
2019-03-04 19:22:23 -08:00
|
|
|
bank.slot = slot;
|
2019-04-14 19:15:31 -07:00
|
|
|
bank.max_tick_height = (bank.slot + 1) * bank.ticks_per_slot - 1;
|
2019-04-16 19:35:38 -07:00
|
|
|
solana_metrics::submit(
|
|
|
|
influxdb::Point::new("bank-new_from_parent-heights")
|
|
|
|
.add_field("slot_height", influxdb::Value::Integer(slot as i64))
|
|
|
|
.add_field(
|
|
|
|
"bank_height",
|
|
|
|
influxdb::Value::Integer(bank.bank_height as i64),
|
|
|
|
)
|
|
|
|
.to_owned(),
|
|
|
|
);
|
2019-04-14 19:15:31 -07:00
|
|
|
|
2019-05-09 19:27:27 -07:00
|
|
|
bank.parent = RwLock::new(Some(parent.clone()));
|
2019-02-25 14:05:02 -08:00
|
|
|
bank.parent_hash = parent.hash();
|
2019-03-09 19:28:43 -08:00
|
|
|
bank.collector_id = *collector_id;
|
2019-03-01 11:53:39 -08:00
|
|
|
|
2019-05-09 19:27:27 -07:00
|
|
|
bank.accounts = Arc::new(Accounts::new_from_parent(&parent.accounts));
|
2019-02-20 21:31:24 -08:00
|
|
|
|
2019-03-01 17:22:49 -08:00
|
|
|
bank.epoch_vote_accounts = {
|
|
|
|
let mut epoch_vote_accounts = parent.epoch_vote_accounts.clone();
|
2019-03-06 16:32:23 -08:00
|
|
|
let epoch = bank.get_stakers_epoch(bank.slot);
|
2019-03-01 11:54:28 -08:00
|
|
|
// update epoch_vote_states cache
|
|
|
|
// if my parent didn't populate for this epoch, we've
|
|
|
|
// crossed a boundary
|
2019-03-01 17:22:49 -08:00
|
|
|
if epoch_vote_accounts.get(&epoch).is_none() {
|
2019-04-05 14:23:00 -07:00
|
|
|
epoch_vote_accounts.insert(epoch, bank.vote_accounts());
|
2019-03-01 11:54:28 -08:00
|
|
|
}
|
2019-03-01 17:22:49 -08:00
|
|
|
epoch_vote_accounts
|
2019-03-01 11:54:28 -08:00
|
|
|
};
|
2019-04-15 17:15:50 -07:00
|
|
|
bank.ancestors.insert(bank.slot(), 0);
|
|
|
|
bank.parents().iter().enumerate().for_each(|(i, p)| {
|
|
|
|
bank.ancestors.insert(p.slot(), i + 1);
|
|
|
|
});
|
2019-03-01 11:54:28 -08:00
|
|
|
|
2019-02-16 19:58:07 -08:00
|
|
|
bank
|
|
|
|
}
|
|
|
|
|
2019-03-04 18:40:47 -08:00
|
|
|
pub fn collector_id(&self) -> Pubkey {
|
|
|
|
self.collector_id
|
|
|
|
}
|
|
|
|
|
2019-02-28 18:02:45 -08:00
|
|
|
pub fn slot(&self) -> u64 {
|
|
|
|
self.slot
|
2019-02-25 14:05:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn hash(&self) -> Hash {
|
|
|
|
*self.hash.read().unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_frozen(&self) -> bool {
|
|
|
|
*self.hash.read().unwrap() != Hash::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn freeze(&self) {
|
|
|
|
let mut hash = self.hash.write().unwrap();
|
|
|
|
|
|
|
|
if *hash == Hash::default() {
|
|
|
|
// freeze is a one-way trip, idempotent
|
|
|
|
*hash = self.hash_internal_state();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-19 02:39:44 -07:00
|
|
|
pub fn epoch_schedule(&self) -> &EpochSchedule {
|
|
|
|
&self.epoch_schedule
|
|
|
|
}
|
|
|
|
|
2019-02-25 20:34:05 -08:00
|
|
|
/// squash the parent's state up into this Bank,
|
2019-02-21 12:08:50 -08:00
|
|
|
/// this Bank becomes a root
|
2019-02-25 20:34:05 -08:00
|
|
|
pub fn squash(&self) {
|
2019-02-25 14:05:02 -08:00
|
|
|
self.freeze();
|
|
|
|
|
2019-02-21 12:08:50 -08:00
|
|
|
let parents = self.parents();
|
2019-05-09 19:27:27 -07:00
|
|
|
*self.parent.write().unwrap() = None;
|
2019-02-21 12:08:50 -08:00
|
|
|
|
2019-03-29 21:21:59 -07:00
|
|
|
let squash_accounts_start = Instant::now();
|
2019-04-28 10:27:37 -07:00
|
|
|
for p in parents.iter().rev() {
|
2019-04-15 17:15:50 -07:00
|
|
|
// root forks cannot be purged
|
2019-05-09 19:27:27 -07:00
|
|
|
self.accounts.add_root(p.slot());
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
2019-03-29 21:21:59 -07:00
|
|
|
let squash_accounts_ms = duration_as_ms(&squash_accounts_start.elapsed());
|
2019-02-21 12:08:50 -08:00
|
|
|
|
2019-03-29 21:21:59 -07:00
|
|
|
let squash_cache_start = Instant::now();
|
2019-03-29 10:03:55 -07:00
|
|
|
parents
|
2019-02-21 12:08:50 -08:00
|
|
|
.iter()
|
2019-05-09 19:27:27 -07:00
|
|
|
.for_each(|p| self.status_cache.write().unwrap().add_root(p.slot()));
|
2019-03-29 21:21:59 -07:00
|
|
|
let squash_cache_ms = duration_as_ms(&squash_cache_start.elapsed());
|
|
|
|
|
|
|
|
solana_metrics::submit(
|
|
|
|
influxdb::Point::new("counter-locktower-observed")
|
|
|
|
.add_field(
|
|
|
|
"squash_accounts_ms",
|
|
|
|
influxdb::Value::Integer(squash_accounts_ms as i64),
|
|
|
|
)
|
|
|
|
.add_field(
|
|
|
|
"squash_cache_ms",
|
|
|
|
influxdb::Value::Integer(squash_cache_ms as i64),
|
|
|
|
)
|
|
|
|
.to_owned(),
|
|
|
|
);
|
2019-02-21 12:08:50 -08:00
|
|
|
}
|
|
|
|
|
2019-02-16 19:58:07 -08:00
|
|
|
/// Return the more recent checkpoint of this bank instance.
|
|
|
|
pub fn parent(&self) -> Option<Arc<Bank>> {
|
2019-05-09 19:27:27 -07:00
|
|
|
self.parent.read().unwrap().clone()
|
2019-02-16 19:58:07 -08:00
|
|
|
}
|
|
|
|
|
2019-02-21 16:01:10 -08:00
|
|
|
fn process_genesis_block(&mut self, genesis_block: &GenesisBlock) {
|
2019-03-01 16:39:23 -08:00
|
|
|
// Bootstrap leader collects fees until `new_from_parent` is called.
|
2019-02-26 05:43:43 -08:00
|
|
|
self.collector_id = genesis_block.bootstrap_leader_id;
|
2019-05-07 20:28:41 -07:00
|
|
|
self.fee_calculator = genesis_block.fee_calculator.clone();
|
2019-02-26 05:43:43 -08:00
|
|
|
|
2019-05-07 11:16:22 -07:00
|
|
|
for (pubkey, account) in genesis_block.accounts.iter() {
|
|
|
|
self.store(pubkey, account);
|
|
|
|
}
|
2019-01-31 13:53:08 -08:00
|
|
|
|
2019-03-02 10:25:16 -08:00
|
|
|
self.blockhash_queue
|
2019-01-31 13:53:08 -08:00
|
|
|
.write()
|
|
|
|
.unwrap()
|
2019-03-01 09:55:13 -08:00
|
|
|
.genesis_hash(&genesis_block.hash());
|
2019-02-21 16:01:10 -08:00
|
|
|
|
|
|
|
self.ticks_per_slot = genesis_block.ticks_per_slot;
|
2019-04-14 19:15:31 -07:00
|
|
|
self.max_tick_height = (self.slot + 1) * self.ticks_per_slot - 1;
|
2019-03-06 16:32:23 -08:00
|
|
|
|
2019-04-10 15:03:25 -07:00
|
|
|
// make bank 0 votable
|
|
|
|
self.is_delta.store(true, Ordering::Relaxed);
|
|
|
|
|
2019-03-06 16:32:23 -08:00
|
|
|
self.epoch_schedule = EpochSchedule::new(
|
|
|
|
genesis_block.slots_per_epoch,
|
|
|
|
genesis_block.stakers_slot_offset,
|
|
|
|
genesis_block.epoch_warmup,
|
|
|
|
);
|
2019-03-12 11:44:41 -07:00
|
|
|
|
2019-04-02 08:35:38 -07:00
|
|
|
// Add native programs mandatory for the MessageProcessor to function
|
2019-04-02 07:29:28 -07:00
|
|
|
self.register_native_instruction_processor(
|
|
|
|
"solana_system_program",
|
|
|
|
&solana_sdk::system_program::id(),
|
|
|
|
);
|
|
|
|
self.register_native_instruction_processor(
|
|
|
|
"solana_bpf_loader",
|
|
|
|
&solana_sdk::bpf_loader::id(),
|
|
|
|
);
|
|
|
|
self.register_native_instruction_processor("solana_vote_program", &solana_vote_api::id());
|
2019-03-12 11:44:41 -07:00
|
|
|
|
|
|
|
// Add additional native programs specified in the genesis block
|
2019-04-02 07:57:00 -07:00
|
|
|
for (name, program_id) in &genesis_block.native_instruction_processors {
|
2019-04-02 07:29:28 -07:00
|
|
|
self.register_native_instruction_processor(name, program_id);
|
2019-03-12 11:44:41 -07:00
|
|
|
}
|
2018-04-02 12:51:44 -07:00
|
|
|
}
|
|
|
|
|
2019-04-02 07:29:28 -07:00
|
|
|
pub fn register_native_instruction_processor(&self, name: &str, program_id: &Pubkey) {
|
2019-03-12 11:44:41 -07:00
|
|
|
debug!("Adding native program {} under {:?}", name, program_id);
|
2019-04-02 07:29:28 -07:00
|
|
|
let account = native_loader::create_loadable_account(name);
|
2019-04-05 14:23:00 -07:00
|
|
|
self.store(program_id, &account);
|
2019-02-19 08:36:39 -08:00
|
|
|
}
|
2018-12-04 07:45:32 -08:00
|
|
|
|
2019-03-02 09:40:47 -08:00
|
|
|
/// Return the last block hash registered.
|
2019-03-02 10:25:16 -08:00
|
|
|
pub fn last_blockhash(&self) -> Hash {
|
|
|
|
self.blockhash_queue.read().unwrap().last_hash()
|
2018-05-03 12:24:35 -07:00
|
|
|
}
|
2019-04-25 16:57:25 -07:00
|
|
|
|
|
|
|
/// Return a confirmed blockhash with NUM_BLOCKHASH_CONFIRMATIONS
|
2019-04-12 14:38:30 -07:00
|
|
|
pub fn confirmed_last_blockhash(&self) -> Hash {
|
2019-04-25 16:57:25 -07:00
|
|
|
const NUM_BLOCKHASH_CONFIRMATIONS: usize = 3;
|
|
|
|
|
|
|
|
let parents = self.parents();
|
|
|
|
if parents.is_empty() {
|
2019-04-12 12:03:02 -07:00
|
|
|
self.last_blockhash()
|
2019-04-25 16:57:25 -07:00
|
|
|
} else {
|
|
|
|
let index = cmp::min(NUM_BLOCKHASH_CONFIRMATIONS, parents.len() - 1);
|
|
|
|
parents[index].last_blockhash()
|
2019-04-12 12:03:02 -07:00
|
|
|
}
|
|
|
|
}
|
2018-05-03 12:24:35 -07:00
|
|
|
|
2018-07-10 14:19:42 -07:00
|
|
|
/// Forget all signatures. Useful for benchmarking.
|
|
|
|
pub fn clear_signatures(&self) {
|
2019-05-09 19:27:27 -07:00
|
|
|
self.status_cache.write().unwrap().clear_signatures();
|
2018-09-21 15:07:29 -07:00
|
|
|
}
|
|
|
|
|
2019-04-23 15:32:19 -07:00
|
|
|
pub fn can_commit(result: &Result<()>) -> bool {
|
|
|
|
match result {
|
|
|
|
Ok(_) => true,
|
|
|
|
Err(TransactionError::InstructionError(_, _)) => true,
|
|
|
|
Err(_) => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
fn update_transaction_statuses(&self, txs: &[Transaction], res: &[Result<()>]) {
|
2019-05-09 19:27:27 -07:00
|
|
|
let mut status_cache = self.status_cache.write().unwrap();
|
2019-01-31 06:53:52 -08:00
|
|
|
for (i, tx) in txs.iter().enumerate() {
|
2019-04-23 15:32:19 -07:00
|
|
|
if Self::can_commit(&res[i]) && !tx.signatures.is_empty() {
|
|
|
|
status_cache.insert(
|
|
|
|
&tx.message().recent_blockhash,
|
|
|
|
&tx.signatures[0],
|
|
|
|
self.slot(),
|
|
|
|
res[i].clone(),
|
|
|
|
);
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-02 15:49:14 -07:00
|
|
|
|
|
|
|
/// Looks through a list of tick heights and stakes, and finds the latest
|
2018-12-20 15:47:48 -08:00
|
|
|
/// tick that has achieved confirmation
|
|
|
|
pub fn get_confirmation_timestamp(
|
2018-11-02 15:49:14 -07:00
|
|
|
&self,
|
2019-03-01 13:13:32 -08:00
|
|
|
mut slots_and_stakes: Vec<(u64, u64)>,
|
2018-11-05 08:36:22 -08:00
|
|
|
supermajority_stake: u64,
|
2018-11-02 15:49:14 -07:00
|
|
|
) -> Option<u64> {
|
2019-03-01 13:13:32 -08:00
|
|
|
// Sort by slot height
|
2019-03-06 12:40:12 -08:00
|
|
|
slots_and_stakes.sort_by(|a, b| b.0.cmp(&a.0));
|
2019-03-01 13:13:32 -08:00
|
|
|
|
2019-03-05 14:18:29 -08:00
|
|
|
let max_slot = self.slot();
|
2019-03-02 10:25:16 -08:00
|
|
|
let min_slot = max_slot.saturating_sub(MAX_RECENT_BLOCKHASHES as u64);
|
2019-03-01 13:13:32 -08:00
|
|
|
|
|
|
|
let mut total_stake = 0;
|
|
|
|
for (slot, stake) in slots_and_stakes.iter() {
|
|
|
|
if *slot >= min_slot && *slot <= max_slot {
|
|
|
|
total_stake += stake;
|
|
|
|
if total_stake > supermajority_stake {
|
|
|
|
return self
|
2019-03-02 10:25:16 -08:00
|
|
|
.blockhash_queue
|
2019-03-01 13:13:32 -08:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2019-03-01 14:52:27 -08:00
|
|
|
.hash_height_to_timestamp(*slot);
|
2019-03-01 13:13:32 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
2018-11-02 15:49:14 -07:00
|
|
|
}
|
|
|
|
|
2018-05-14 14:33:11 -07:00
|
|
|
/// Tell the bank which Entry IDs exist on the ledger. This function
|
2018-04-05 08:53:58 -07:00
|
|
|
/// assumes subsequent calls correspond to later entries, and will boot
|
|
|
|
/// the oldest ones once its internal cache is full. Once boot, the
|
2019-03-01 09:55:13 -08:00
|
|
|
/// bank will reject transactions using that `hash`.
|
|
|
|
pub fn register_tick(&self, hash: &Hash) {
|
2019-02-25 14:05:02 -08:00
|
|
|
if self.is_frozen() {
|
2019-02-27 10:36:15 -08:00
|
|
|
warn!("=========== FIXME: register_tick() working on a frozen bank! ================");
|
2019-02-25 14:05:02 -08:00
|
|
|
}
|
2019-03-01 14:08:39 -08:00
|
|
|
|
2019-02-25 14:05:02 -08:00
|
|
|
// TODO: put this assert back in
|
|
|
|
// assert!(!self.is_frozen());
|
2019-03-01 14:08:39 -08:00
|
|
|
|
2019-02-16 16:41:03 -08:00
|
|
|
let current_tick_height = {
|
2019-03-01 14:08:39 -08:00
|
|
|
self.tick_height.fetch_add(1, Ordering::SeqCst);
|
|
|
|
self.tick_height.load(Ordering::SeqCst) as u64
|
|
|
|
};
|
2019-03-01 14:52:27 -08:00
|
|
|
inc_new_counter_info!("bank-register_tick-registered", 1);
|
2019-03-01 14:08:39 -08:00
|
|
|
|
2019-03-01 14:52:27 -08:00
|
|
|
// Register a new block hash if at the last tick in the slot
|
|
|
|
if current_tick_height % self.ticks_per_slot == self.ticks_per_slot - 1 {
|
2019-03-29 10:03:55 -07:00
|
|
|
self.blockhash_queue.write().unwrap().register_hash(hash);
|
2019-02-16 16:41:03 -08:00
|
|
|
}
|
2018-04-02 08:36:22 -07:00
|
|
|
}
|
|
|
|
|
2018-09-17 13:36:31 -07:00
|
|
|
/// Process a Transaction. This is used for unit tests and simply calls the vector Bank::process_transactions method.
|
2018-07-10 14:19:42 -07:00
|
|
|
pub fn process_transaction(&self, tx: &Transaction) -> Result<()> {
|
2018-10-04 13:15:54 -07:00
|
|
|
let txs = vec![tx.clone()];
|
2019-03-01 18:45:10 -08:00
|
|
|
self.process_transactions(&txs)[0].clone()?;
|
|
|
|
tx.signatures
|
|
|
|
.get(0)
|
|
|
|
.map_or(Ok(()), |sig| self.get_signature_status(sig).unwrap())
|
2018-09-07 20:18:36 -07:00
|
|
|
}
|
2018-11-02 15:49:14 -07:00
|
|
|
|
2019-05-07 15:51:35 -07:00
|
|
|
pub fn lock_accounts<'a, 'b, I>(&'a self, txs: &'b [I]) -> LockedAccountsResults<'a, 'b, I>
|
|
|
|
where
|
|
|
|
I: std::borrow::Borrow<Transaction>,
|
|
|
|
{
|
2019-02-25 14:05:02 -08:00
|
|
|
if self.is_frozen() {
|
2019-02-27 10:36:15 -08:00
|
|
|
warn!("=========== FIXME: lock_accounts() working on a frozen bank! ================");
|
2019-02-25 14:05:02 -08:00
|
|
|
}
|
|
|
|
// TODO: put this assert back in
|
|
|
|
// assert!(!self.is_frozen());
|
2019-05-09 19:27:27 -07:00
|
|
|
let results = self.accounts.lock_accounts(txs);
|
2019-05-07 15:51:35 -07:00
|
|
|
LockedAccountsResults::new(results, &self, txs, AccountLockType::AccountLock)
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-01-04 16:39:04 -08:00
|
|
|
|
2019-05-07 15:51:35 -07:00
|
|
|
pub fn unlock_accounts<I>(&self, locked_accounts_results: &mut LockedAccountsResults<I>)
|
|
|
|
where
|
|
|
|
I: Borrow<Transaction>,
|
|
|
|
{
|
2019-04-02 03:55:42 -07:00
|
|
|
if locked_accounts_results.needs_unlock {
|
|
|
|
locked_accounts_results.needs_unlock = false;
|
2019-05-07 15:51:35 -07:00
|
|
|
match locked_accounts_results.lock_type() {
|
2019-05-09 19:27:27 -07:00
|
|
|
AccountLockType::AccountLock => self.accounts.unlock_accounts(
|
2019-05-07 15:51:35 -07:00
|
|
|
locked_accounts_results.transactions(),
|
|
|
|
locked_accounts_results.locked_accounts_results(),
|
|
|
|
),
|
|
|
|
AccountLockType::RecordLock => self
|
|
|
|
.accounts
|
|
|
|
.unlock_record_accounts(locked_accounts_results.transactions()),
|
|
|
|
}
|
2019-04-02 03:55:42 -07:00
|
|
|
}
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
|
|
|
|
2019-05-07 15:51:35 -07:00
|
|
|
pub fn lock_record_accounts<'a, 'b, I>(
|
|
|
|
&'a self,
|
|
|
|
txs: &'b [I],
|
|
|
|
) -> LockedAccountsResults<'a, 'b, I>
|
|
|
|
where
|
|
|
|
I: std::borrow::Borrow<Transaction>,
|
|
|
|
{
|
2019-05-09 19:27:27 -07:00
|
|
|
self.accounts.lock_record_accounts(txs);
|
2019-05-07 15:51:35 -07:00
|
|
|
LockedAccountsResults::new(vec![], &self, txs, AccountLockType::RecordLock)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unlock_record_accounts(&self, txs: &[Transaction]) {
|
2019-05-09 19:27:27 -07:00
|
|
|
self.accounts.unlock_record_accounts(txs)
|
2019-05-07 15:51:35 -07:00
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
fn load_accounts(
|
2019-01-29 16:33:28 -08:00
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
results: Vec<Result<()>>,
|
|
|
|
error_counters: &mut ErrorCounters,
|
|
|
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>> {
|
2019-05-09 19:27:27 -07:00
|
|
|
self.accounts.load_accounts(
|
2019-04-15 17:15:50 -07:00
|
|
|
&self.ancestors,
|
2019-03-29 15:11:21 -07:00
|
|
|
txs,
|
|
|
|
results,
|
|
|
|
&self.fee_calculator,
|
|
|
|
error_counters,
|
|
|
|
)
|
2019-01-29 16:33:28 -08:00
|
|
|
}
|
2019-03-19 18:03:07 -07:00
|
|
|
fn check_refs(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
2019-05-08 10:32:25 -07:00
|
|
|
lock_results: &[Result<()>],
|
2019-03-19 18:03:07 -07:00
|
|
|
error_counters: &mut ErrorCounters,
|
|
|
|
) -> Vec<Result<()>> {
|
|
|
|
txs.iter()
|
2019-05-08 10:32:25 -07:00
|
|
|
.zip(lock_results)
|
2019-03-19 18:03:07 -07:00
|
|
|
.map(|(tx, lock_res)| {
|
|
|
|
if lock_res.is_ok() && !tx.verify_refs() {
|
|
|
|
error_counters.invalid_account_index += 1;
|
|
|
|
Err(TransactionError::InvalidAccountIndex)
|
|
|
|
} else {
|
2019-04-02 03:55:42 -07:00
|
|
|
lock_res.clone()
|
2019-03-19 18:03:07 -07:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
2019-01-31 06:53:52 -08:00
|
|
|
fn check_age(
|
2018-12-17 12:41:23 -08:00
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
2019-01-06 22:06:55 -08:00
|
|
|
lock_results: Vec<Result<()>>,
|
2018-12-17 12:41:23 -08:00
|
|
|
max_age: usize,
|
|
|
|
error_counters: &mut ErrorCounters,
|
2019-01-29 16:33:28 -08:00
|
|
|
) -> Vec<Result<()>> {
|
2019-03-02 10:25:16 -08:00
|
|
|
let hash_queue = self.blockhash_queue.read().unwrap();
|
2019-01-31 06:53:52 -08:00
|
|
|
txs.iter()
|
|
|
|
.zip(lock_results.into_iter())
|
|
|
|
.map(|(tx, lock_res)| {
|
2019-03-29 09:05:06 -07:00
|
|
|
if lock_res.is_ok()
|
|
|
|
&& !hash_queue.check_hash_age(tx.message().recent_blockhash, max_age)
|
|
|
|
{
|
2019-03-02 10:25:16 -08:00
|
|
|
error_counters.reserve_blockhash += 1;
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::BlockhashNotFound)
|
2019-01-31 06:53:52 -08:00
|
|
|
} else {
|
|
|
|
lock_res
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
fn check_signatures(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
lock_results: Vec<Result<()>>,
|
|
|
|
error_counters: &mut ErrorCounters,
|
|
|
|
) -> Vec<Result<()>> {
|
2019-05-09 19:27:27 -07:00
|
|
|
let rcache = self.status_cache.read().unwrap();
|
2019-01-29 16:33:28 -08:00
|
|
|
txs.iter()
|
|
|
|
.zip(lock_results.into_iter())
|
|
|
|
.map(|(tx, lock_res)| {
|
2019-03-02 14:01:53 -08:00
|
|
|
if tx.signatures.is_empty() {
|
|
|
|
return lock_res;
|
|
|
|
}
|
2019-03-29 10:03:55 -07:00
|
|
|
if lock_res.is_ok()
|
|
|
|
&& rcache
|
2019-03-29 13:56:29 -07:00
|
|
|
.get_signature_status(
|
|
|
|
&tx.signatures[0],
|
|
|
|
&tx.message().recent_blockhash,
|
2019-04-15 17:15:50 -07:00
|
|
|
&self.ancestors,
|
2019-03-29 13:56:29 -07:00
|
|
|
)
|
2019-03-29 10:03:55 -07:00
|
|
|
.is_some()
|
|
|
|
{
|
2019-01-31 06:53:52 -08:00
|
|
|
error_counters.duplicate_signature += 1;
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::DuplicateSignature)
|
2019-01-29 16:33:28 -08:00
|
|
|
} else {
|
|
|
|
lock_res
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-05-08 10:32:25 -07:00
|
|
|
|
|
|
|
pub fn check_transactions(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
lock_results: &[Result<()>],
|
|
|
|
max_age: usize,
|
|
|
|
mut error_counters: &mut ErrorCounters,
|
|
|
|
) -> Vec<Result<()>> {
|
|
|
|
let refs_results = self.check_refs(txs, lock_results, &mut error_counters);
|
|
|
|
let age_results = self.check_age(txs, refs_results, max_age, &mut error_counters);
|
|
|
|
self.check_signatures(txs, age_results, &mut error_counters)
|
|
|
|
}
|
|
|
|
|
2019-01-21 10:17:04 -08:00
|
|
|
#[allow(clippy::type_complexity)]
|
2019-02-16 14:02:21 -08:00
|
|
|
pub fn load_and_execute_transactions(
|
2018-10-04 13:15:54 -07:00
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
2019-05-07 15:51:35 -07:00
|
|
|
lock_results: &LockedAccountsResults<Transaction>,
|
2018-10-10 17:23:06 -07:00
|
|
|
max_age: usize,
|
2019-01-21 10:17:04 -08:00
|
|
|
) -> (
|
|
|
|
Vec<Result<(InstructionAccounts, InstructionLoaders)>>,
|
|
|
|
Vec<Result<()>>,
|
|
|
|
) {
|
2018-09-18 11:31:00 -07:00
|
|
|
debug!("processing transactions: {}", txs.len());
|
2018-09-07 20:18:36 -07:00
|
|
|
let mut error_counters = ErrorCounters::default();
|
2018-06-07 19:35:38 -07:00
|
|
|
let now = Instant::now();
|
2019-05-08 10:32:25 -07:00
|
|
|
let sig_results = self.check_transactions(
|
|
|
|
txs,
|
|
|
|
lock_results.locked_accounts_results(),
|
|
|
|
max_age,
|
|
|
|
&mut error_counters,
|
|
|
|
);
|
2019-01-29 16:33:28 -08:00
|
|
|
let mut loaded_accounts = self.load_accounts(txs, sig_results, &mut error_counters);
|
2018-11-23 18:50:24 -08:00
|
|
|
let tick_height = self.tick_height();
|
2018-11-08 15:23:14 -08:00
|
|
|
|
2018-09-07 20:18:36 -07:00
|
|
|
let load_elapsed = now.elapsed();
|
2018-06-22 10:44:31 -07:00
|
|
|
let now = Instant::now();
|
2019-04-02 08:35:38 -07:00
|
|
|
let executed: Vec<Result<()>> =
|
|
|
|
loaded_accounts
|
|
|
|
.iter_mut()
|
|
|
|
.zip(txs.iter())
|
|
|
|
.map(|(accs, tx)| match accs {
|
|
|
|
Err(e) => Err(e.clone()),
|
|
|
|
Ok((ref mut accounts, ref mut loaders)) => self
|
|
|
|
.message_processor
|
|
|
|
.process_message(tx.message(), loaders, accounts, tick_height),
|
|
|
|
})
|
|
|
|
.collect();
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2018-09-07 20:18:36 -07:00
|
|
|
let execution_elapsed = now.elapsed();
|
2018-11-08 15:23:14 -08:00
|
|
|
|
2018-06-22 10:45:22 -07:00
|
|
|
debug!(
|
2019-01-21 10:17:04 -08:00
|
|
|
"load: {}us execute: {}us txs_len={}",
|
2018-09-07 20:18:36 -07:00
|
|
|
duration_as_us(&load_elapsed),
|
|
|
|
duration_as_us(&execution_elapsed),
|
2018-10-04 13:15:54 -07:00
|
|
|
txs.len(),
|
2018-06-22 10:45:22 -07:00
|
|
|
);
|
2018-06-22 12:11:27 -07:00
|
|
|
let mut tx_count = 0;
|
2018-07-19 15:38:35 -07:00
|
|
|
let mut err_count = 0;
|
2018-10-10 17:23:06 -07:00
|
|
|
for (r, tx) in executed.iter().zip(txs.iter()) {
|
2018-06-07 19:35:38 -07:00
|
|
|
if r.is_ok() {
|
2018-06-22 12:11:27 -07:00
|
|
|
tx_count += 1;
|
2018-06-22 10:44:31 -07:00
|
|
|
} else {
|
2018-07-19 15:38:35 -07:00
|
|
|
if err_count == 0 {
|
2018-10-10 17:23:06 -07:00
|
|
|
info!("tx error: {:?} {:?}", r, tx);
|
2018-07-19 15:38:35 -07:00
|
|
|
}
|
|
|
|
err_count += 1;
|
2018-06-07 19:35:38 -07:00
|
|
|
}
|
|
|
|
}
|
2018-07-19 15:38:35 -07:00
|
|
|
if err_count > 0 {
|
|
|
|
info!("{} errors of {} txs", err_count, err_count + tx_count);
|
2018-10-10 17:23:06 -07:00
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-process_transactions-account_not_found",
|
|
|
|
error_counters.account_not_found
|
|
|
|
);
|
2018-10-04 13:15:54 -07:00
|
|
|
inc_new_counter_info!("bank-process_transactions-error_count", err_count);
|
2018-07-19 15:38:35 -07:00
|
|
|
}
|
2018-10-04 13:15:54 -07:00
|
|
|
|
2019-04-05 10:42:25 -07:00
|
|
|
self.increment_transaction_count(tx_count);
|
2018-11-05 09:47:41 -08:00
|
|
|
|
2018-10-04 13:15:54 -07:00
|
|
|
inc_new_counter_info!("bank-process_transactions-txs", tx_count);
|
2019-03-02 10:25:16 -08:00
|
|
|
if 0 != error_counters.blockhash_not_found {
|
2018-10-10 17:23:06 -07:00
|
|
|
inc_new_counter_info!(
|
2019-03-02 10:25:16 -08:00
|
|
|
"bank-process_transactions-error-blockhash_not_found",
|
|
|
|
error_counters.blockhash_not_found
|
2018-10-10 17:23:06 -07:00
|
|
|
);
|
|
|
|
}
|
2019-03-19 18:03:07 -07:00
|
|
|
if 0 != error_counters.invalid_account_index {
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-process_transactions-error-invalid_account_index",
|
|
|
|
error_counters.invalid_account_index
|
|
|
|
);
|
|
|
|
}
|
2019-03-02 10:25:16 -08:00
|
|
|
if 0 != error_counters.reserve_blockhash {
|
2018-10-10 17:23:06 -07:00
|
|
|
inc_new_counter_info!(
|
2019-03-02 10:25:16 -08:00
|
|
|
"bank-process_transactions-error-reserve_blockhash",
|
|
|
|
error_counters.reserve_blockhash
|
2018-10-10 17:23:06 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
if 0 != error_counters.duplicate_signature {
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-process_transactions-error-duplicate_signature",
|
|
|
|
error_counters.duplicate_signature
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if 0 != error_counters.insufficient_funds {
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-process_transactions-error-insufficient_funds",
|
|
|
|
error_counters.insufficient_funds
|
|
|
|
);
|
|
|
|
}
|
2019-02-07 11:14:10 -08:00
|
|
|
if 0 != error_counters.account_loaded_twice {
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-process_transactions-account_loaded_twice",
|
|
|
|
error_counters.account_loaded_twice
|
|
|
|
);
|
|
|
|
}
|
2019-01-21 10:17:04 -08:00
|
|
|
(loaded_accounts, executed)
|
|
|
|
}
|
|
|
|
|
2019-02-22 11:52:48 -08:00
|
|
|
fn filter_program_errors_and_collect_fee(
|
2019-01-21 10:17:04 -08:00
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
executed: &[Result<()>],
|
2019-02-21 13:37:08 -08:00
|
|
|
) -> Vec<Result<()>> {
|
|
|
|
let mut fees = 0;
|
|
|
|
let results = txs
|
|
|
|
.iter()
|
2019-02-21 15:31:00 -08:00
|
|
|
.zip(executed.iter())
|
2019-03-29 09:05:06 -07:00
|
|
|
.map(|(tx, res)| {
|
2019-03-29 15:11:21 -07:00
|
|
|
let fee = self.fee_calculator.calculate_fee(tx.message());
|
2019-03-29 09:05:06 -07:00
|
|
|
let message = tx.message();
|
|
|
|
match *res {
|
|
|
|
Err(TransactionError::InstructionError(_, _)) => {
|
2019-04-05 16:55:58 -07:00
|
|
|
// credit the transaction fee even in case of InstructionError
|
|
|
|
// necessary to withdraw from account[0] here because previous
|
|
|
|
// work of doing so (in accounts.load()) is ignored by store()
|
2019-03-29 15:11:21 -07:00
|
|
|
self.withdraw(&message.account_keys[0], fee)?;
|
|
|
|
fees += fee;
|
2019-03-29 09:05:06 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Ok(()) => {
|
2019-03-29 15:11:21 -07:00
|
|
|
fees += fee;
|
2019-03-29 09:05:06 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
_ => res.clone(),
|
2019-02-21 13:37:08 -08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect();
|
2019-02-26 05:43:43 -08:00
|
|
|
self.deposit(&self.collector_id, fees);
|
2019-02-21 13:37:08 -08:00
|
|
|
results
|
2019-01-21 10:17:04 -08:00
|
|
|
}
|
|
|
|
|
2019-02-22 11:52:48 -08:00
|
|
|
pub fn commit_transactions(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
loaded_accounts: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
|
|
|
executed: &[Result<()>],
|
|
|
|
) -> Vec<Result<()>> {
|
2019-02-25 14:05:02 -08:00
|
|
|
if self.is_frozen() {
|
2019-02-27 10:36:15 -08:00
|
|
|
warn!("=========== FIXME: commit_transactions() working on a frozen bank! ================");
|
2019-02-25 14:05:02 -08:00
|
|
|
}
|
2019-03-13 14:06:12 -07:00
|
|
|
|
2019-04-23 15:32:19 -07:00
|
|
|
if executed.iter().any(|res| Self::can_commit(res)) {
|
|
|
|
self.is_delta.store(true, Ordering::Relaxed);
|
|
|
|
}
|
2019-03-13 14:06:12 -07:00
|
|
|
|
2019-02-25 14:05:02 -08:00
|
|
|
// TODO: put this assert back in
|
|
|
|
// assert!(!self.is_frozen());
|
2019-02-22 11:52:48 -08:00
|
|
|
let now = Instant::now();
|
2019-05-09 19:27:27 -07:00
|
|
|
self.accounts
|
2019-04-15 17:15:50 -07:00
|
|
|
.store_accounts(self.slot(), txs, executed, loaded_accounts);
|
2019-02-22 11:52:48 -08:00
|
|
|
|
2019-04-05 14:23:00 -07:00
|
|
|
self.store_vote_accounts(txs, executed, loaded_accounts);
|
|
|
|
|
2019-02-22 11:52:48 -08:00
|
|
|
// once committed there is no way to unroll
|
|
|
|
let write_elapsed = now.elapsed();
|
|
|
|
debug!(
|
|
|
|
"store: {}us txs_len={}",
|
|
|
|
duration_as_us(&write_elapsed),
|
|
|
|
txs.len(),
|
|
|
|
);
|
|
|
|
self.update_transaction_statuses(txs, &executed);
|
|
|
|
self.filter_program_errors_and_collect_fee(txs, executed)
|
|
|
|
}
|
|
|
|
|
2019-01-21 10:17:04 -08:00
|
|
|
/// Process a batch of transactions.
|
|
|
|
#[must_use]
|
|
|
|
pub fn load_execute_and_commit_transactions(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
2019-05-07 15:51:35 -07:00
|
|
|
lock_results: &LockedAccountsResults<Transaction>,
|
2019-01-21 10:17:04 -08:00
|
|
|
max_age: usize,
|
2019-02-19 17:11:43 -08:00
|
|
|
) -> Vec<Result<()>> {
|
2019-01-21 10:17:04 -08:00
|
|
|
let (loaded_accounts, executed) =
|
|
|
|
self.load_and_execute_transactions(txs, lock_results, max_age);
|
|
|
|
|
2019-02-21 13:37:08 -08:00
|
|
|
self.commit_transactions(txs, &loaded_accounts, &executed)
|
2018-10-04 13:15:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
pub fn process_transactions(&self, txs: &[Transaction]) -> Vec<Result<()>> {
|
2019-01-06 22:06:55 -08:00
|
|
|
let lock_results = self.lock_accounts(txs);
|
2019-04-02 03:55:42 -07:00
|
|
|
self.load_execute_and_commit_transactions(txs, &lock_results, MAX_RECENT_BLOCKHASHES)
|
2018-04-05 21:13:54 -07:00
|
|
|
}
|
|
|
|
|
2018-03-29 11:20:54 -07:00
|
|
|
/// Create, sign, and process a Transaction from `keypair` to `to` of
|
2019-03-05 16:28:14 -08:00
|
|
|
/// `n` lamports where `blockhash` is the last Entry ID observed by the client.
|
2019-03-27 04:59:30 -07:00
|
|
|
pub fn transfer(&self, n: u64, keypair: &Keypair, to: &Pubkey) -> Result<Signature> {
|
|
|
|
let blockhash = self.last_blockhash();
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx = system_transaction::create_user_account(keypair, to, n, blockhash, 0);
|
2018-10-26 14:43:34 -07:00
|
|
|
let signature = tx.signatures[0];
|
2018-08-09 08:26:21 -07:00
|
|
|
self.process_transaction(&tx).map(|_| signature)
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
|
|
|
|
2018-11-05 08:36:22 -08:00
|
|
|
pub fn read_balance(account: &Account) -> u64 {
|
2019-03-05 16:28:14 -08:00
|
|
|
account.lamports
|
2018-03-07 20:55:49 -08:00
|
|
|
}
|
2018-11-25 21:56:48 -08:00
|
|
|
/// Each program would need to be able to introspect its own state
|
|
|
|
/// this is hard-coded to the Budget language
|
2018-11-05 08:36:22 -08:00
|
|
|
pub fn get_balance(&self, pubkey: &Pubkey) -> u64 {
|
2018-09-07 20:18:36 -07:00
|
|
|
self.get_account(pubkey)
|
2018-09-17 13:36:31 -07:00
|
|
|
.map(|x| Self::read_balance(&x))
|
2018-09-07 20:18:36 -07:00
|
|
|
.unwrap_or(0)
|
2018-08-10 21:13:34 -07:00
|
|
|
}
|
|
|
|
|
2019-02-17 08:01:31 -08:00
|
|
|
/// Compute all the parents of the bank in order
|
2019-02-28 13:15:25 -08:00
|
|
|
pub fn parents(&self) -> Vec<Arc<Bank>> {
|
2019-02-17 08:01:31 -08:00
|
|
|
let mut parents = vec![];
|
|
|
|
let mut bank = self.parent();
|
|
|
|
while let Some(parent) = bank {
|
|
|
|
parents.push(parent.clone());
|
|
|
|
bank = parent.parent();
|
|
|
|
}
|
|
|
|
parents
|
|
|
|
}
|
|
|
|
|
2019-04-05 14:23:00 -07:00
|
|
|
fn store(&self, pubkey: &Pubkey, account: &Account) {
|
2019-05-09 19:27:27 -07:00
|
|
|
self.accounts.store_slow(self.slot(), pubkey, account);
|
2019-04-05 14:23:00 -07:00
|
|
|
if solana_vote_api::check_id(&account.owner) {
|
|
|
|
let mut vote_accounts = self.vote_accounts.write().unwrap();
|
|
|
|
if account.lamports != 0 {
|
|
|
|
vote_accounts.insert(*pubkey, account.clone());
|
|
|
|
} else {
|
|
|
|
vote_accounts.remove(pubkey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-05 16:28:14 -08:00
|
|
|
pub fn withdraw(&self, pubkey: &Pubkey, lamports: u64) -> Result<()> {
|
2019-02-21 13:37:08 -08:00
|
|
|
match self.get_account(pubkey) {
|
|
|
|
Some(mut account) => {
|
2019-03-05 16:28:14 -08:00
|
|
|
if lamports > account.lamports {
|
2019-03-13 12:58:44 -07:00
|
|
|
return Err(TransactionError::InsufficientFundsForFee);
|
2019-02-21 13:37:08 -08:00
|
|
|
}
|
|
|
|
|
2019-03-05 16:28:14 -08:00
|
|
|
account.lamports -= lamports;
|
2019-04-05 14:23:00 -07:00
|
|
|
self.store(pubkey, &account);
|
|
|
|
|
2019-02-21 13:37:08 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
2019-03-13 12:58:44 -07:00
|
|
|
None => Err(TransactionError::AccountNotFound),
|
2019-02-21 13:37:08 -08:00
|
|
|
}
|
2019-02-20 09:14:28 -08:00
|
|
|
}
|
|
|
|
|
2019-03-05 16:28:14 -08:00
|
|
|
pub fn deposit(&self, pubkey: &Pubkey, lamports: u64) {
|
2019-02-20 06:27:39 -08:00
|
|
|
let mut account = self.get_account(pubkey).unwrap_or_default();
|
2019-03-05 16:28:14 -08:00
|
|
|
account.lamports += lamports;
|
2019-04-05 14:23:00 -07:00
|
|
|
self.store(pubkey, &account);
|
2019-02-19 15:41:40 -08:00
|
|
|
}
|
|
|
|
|
2018-08-10 21:13:34 -07:00
|
|
|
pub fn get_account(&self, pubkey: &Pubkey) -> Option<Account> {
|
2019-05-09 19:27:27 -07:00
|
|
|
self.accounts
|
2019-05-06 07:31:50 -07:00
|
|
|
.load_slow(&self.ancestors, pubkey)
|
|
|
|
.map(|(account, _)| account)
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
2018-05-14 05:49:48 -07:00
|
|
|
|
2019-03-07 08:51:56 -08:00
|
|
|
pub fn get_program_accounts_modified_since_parent(
|
|
|
|
&self,
|
|
|
|
program_id: &Pubkey,
|
|
|
|
) -> Vec<(Pubkey, Account)> {
|
2019-05-09 19:27:27 -07:00
|
|
|
self.accounts.load_by_program(self.slot(), program_id)
|
2019-03-07 08:51:56 -08:00
|
|
|
}
|
|
|
|
|
2019-05-06 07:31:50 -07:00
|
|
|
pub fn get_account_modified_since_parent(&self, pubkey: &Pubkey) -> Option<(Account, Fork)> {
|
2019-04-15 17:15:50 -07:00
|
|
|
let just_self: HashMap<u64, usize> = vec![(self.slot(), 0)].into_iter().collect();
|
2019-05-09 19:27:27 -07:00
|
|
|
self.accounts.load_slow(&just_self, pubkey)
|
2019-02-18 14:21:23 -08:00
|
|
|
}
|
|
|
|
|
2018-11-05 09:47:41 -08:00
|
|
|
pub fn transaction_count(&self) -> u64 {
|
2019-04-05 10:42:25 -07:00
|
|
|
self.transaction_count.load(Ordering::Relaxed) as u64
|
|
|
|
}
|
|
|
|
fn increment_transaction_count(&self, tx_count: usize) {
|
|
|
|
self.transaction_count
|
|
|
|
.fetch_add(tx_count, Ordering::Relaxed);
|
2018-05-14 05:49:48 -07:00
|
|
|
}
|
2018-06-28 11:58:33 -07:00
|
|
|
|
2019-03-21 07:43:21 -07:00
|
|
|
pub fn get_signature_confirmation_status(
|
|
|
|
&self,
|
|
|
|
signature: &Signature,
|
|
|
|
) -> Option<(usize, Result<()>)> {
|
2019-05-09 19:27:27 -07:00
|
|
|
let rcache = self.status_cache.read().unwrap();
|
2019-04-15 17:15:50 -07:00
|
|
|
rcache.get_signature_status_slow(signature, &self.ancestors)
|
2018-09-26 14:57:34 -07:00
|
|
|
}
|
|
|
|
|
2019-03-21 07:43:21 -07:00
|
|
|
pub fn get_signature_status(&self, signature: &Signature) -> Option<Result<()>> {
|
|
|
|
self.get_signature_confirmation_status(signature)
|
|
|
|
.map(|v| v.1)
|
|
|
|
}
|
|
|
|
|
2018-09-26 14:57:34 -07:00
|
|
|
pub fn has_signature(&self, signature: &Signature) -> bool {
|
2019-03-29 10:03:55 -07:00
|
|
|
self.get_signature_confirmation_status(signature).is_some()
|
2018-09-28 16:16:35 -07:00
|
|
|
}
|
|
|
|
|
2018-09-07 12:55:30 -07:00
|
|
|
/// Hash the `accounts` HashMap. This represents a validator's interpretation
|
2018-11-05 09:47:41 -08:00
|
|
|
/// of the delta of the ledger since the last vote and up to now
|
2019-02-25 14:05:02 -08:00
|
|
|
fn hash_internal_state(&self) -> Hash {
|
2019-02-18 10:23:09 -08:00
|
|
|
// If there are no accounts, return the same hash as we did before
|
|
|
|
// checkpointing.
|
2019-05-09 19:27:27 -07:00
|
|
|
if !self.accounts.has_accounts(self.slot()) {
|
2019-02-25 14:05:02 -08:00
|
|
|
return self.parent_hash;
|
2019-02-18 10:23:09 -08:00
|
|
|
}
|
|
|
|
|
2019-05-09 19:27:27 -07:00
|
|
|
let accounts_delta_hash = self.accounts.hash_internal_state(self.slot());
|
2019-02-25 14:05:02 -08:00
|
|
|
extend_and_hash(&self.parent_hash, &serialize(&accounts_delta_hash).unwrap())
|
2018-09-07 12:55:30 -07:00
|
|
|
}
|
|
|
|
|
2019-03-05 14:18:29 -08:00
|
|
|
/// Return the number of ticks per slot
|
2019-02-21 09:22:31 -08:00
|
|
|
pub fn ticks_per_slot(&self) -> u64 {
|
|
|
|
self.ticks_per_slot
|
|
|
|
}
|
|
|
|
|
2019-02-21 08:45:36 -08:00
|
|
|
/// Return the number of ticks since genesis.
|
2018-11-05 09:47:41 -08:00
|
|
|
pub fn tick_height(&self) -> u64 {
|
2019-03-01 14:08:39 -08:00
|
|
|
// tick_height is using an AtomicUSize because AtomicU64 is not yet a stable API.
|
|
|
|
// Until we can switch to AtomicU64, fail if usize is not the same as u64
|
2019-03-01 14:52:27 -08:00
|
|
|
assert_eq!(std::usize::MAX, 0xFFFF_FFFF_FFFF_FFFF);
|
2019-03-01 14:08:39 -08:00
|
|
|
self.tick_height.load(Ordering::SeqCst) as u64
|
2018-10-25 16:58:40 -07:00
|
|
|
}
|
2019-01-31 19:21:02 -08:00
|
|
|
|
2019-04-14 19:15:31 -07:00
|
|
|
/// Return this bank's max_tick_height
|
|
|
|
pub fn max_tick_height(&self) -> u64 {
|
|
|
|
self.max_tick_height
|
|
|
|
}
|
|
|
|
|
2019-03-06 16:32:23 -08:00
|
|
|
/// Return the number of slots per epoch for the given epoch
|
|
|
|
pub fn get_slots_in_epoch(&self, epoch: u64) -> u64 {
|
|
|
|
self.epoch_schedule.get_slots_in_epoch(epoch)
|
2019-02-26 07:44:24 -08:00
|
|
|
}
|
|
|
|
|
2019-03-01 11:54:28 -08:00
|
|
|
/// returns the epoch for which this bank's stakers_slot_offset and slot would
|
|
|
|
/// need to cache stakers
|
2019-03-06 16:32:23 -08:00
|
|
|
pub fn get_stakers_epoch(&self, slot: u64) -> u64 {
|
|
|
|
self.epoch_schedule.get_stakers_epoch(slot)
|
2019-03-01 11:54:28 -08:00
|
|
|
}
|
|
|
|
|
2019-04-05 14:23:00 -07:00
|
|
|
/// a bank-level cache of vote accounts
|
|
|
|
fn store_vote_accounts(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
res: &[Result<()>],
|
|
|
|
loaded: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
|
|
|
) {
|
|
|
|
let mut vote_accounts = self.vote_accounts.write().unwrap();
|
|
|
|
|
|
|
|
for (i, raccs) in loaded.iter().enumerate() {
|
|
|
|
if res[i].is_err() || raccs.is_err() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let message = &txs[i].message();
|
|
|
|
let acc = raccs.as_ref().unwrap();
|
|
|
|
for (key, account) in message
|
|
|
|
.account_keys
|
|
|
|
.iter()
|
|
|
|
.zip(acc.0.iter())
|
|
|
|
.filter(|(_, account)| solana_vote_api::check_id(&account.owner))
|
|
|
|
{
|
|
|
|
if account.lamports != 0 {
|
|
|
|
vote_accounts.insert(*key, account.clone());
|
|
|
|
} else {
|
|
|
|
vote_accounts.remove(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-01 17:22:49 -08:00
|
|
|
/// current vote accounts for this bank
|
2019-04-05 14:23:00 -07:00
|
|
|
pub fn vote_accounts(&self) -> HashMap<Pubkey, Account> {
|
|
|
|
self.vote_accounts.read().unwrap().clone()
|
2019-03-01 17:22:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// vote accounts for the specific epoch
|
2019-03-03 18:04:13 -08:00
|
|
|
pub fn epoch_vote_accounts(&self, epoch: u64) -> Option<&HashMap<Pubkey, Account>> {
|
|
|
|
self.epoch_vote_accounts.get(&epoch)
|
2019-02-26 07:44:24 -08:00
|
|
|
}
|
2019-03-06 14:44:21 -08:00
|
|
|
|
|
|
|
/// given a slot, return the epoch and offset into the epoch this slot falls
|
|
|
|
/// e.g. with a fixed number for slots_per_epoch, the calculation is simply:
|
|
|
|
///
|
|
|
|
/// ( slot/slots_per_epoch, slot % slots_per_epoch )
|
|
|
|
///
|
|
|
|
pub fn get_epoch_and_slot_index(&self, slot: u64) -> (u64, u64) {
|
2019-03-06 16:32:23 -08:00
|
|
|
self.epoch_schedule.get_epoch_and_slot_index(slot)
|
2019-03-06 14:44:21 -08:00
|
|
|
}
|
2019-03-13 14:06:12 -07:00
|
|
|
|
|
|
|
pub fn is_votable(&self) -> bool {
|
|
|
|
let max_tick_height = (self.slot + 1) * self.ticks_per_slot - 1;
|
|
|
|
self.is_delta.load(Ordering::Relaxed) && self.tick_height() == max_tick_height
|
|
|
|
}
|
2019-03-16 04:50:44 -07:00
|
|
|
|
2019-04-19 13:18:20 -07:00
|
|
|
/// Add an instruction processor to intercept instructions before the dynamic loader.
|
2019-03-16 16:20:09 -07:00
|
|
|
pub fn add_instruction_processor(
|
|
|
|
&mut self,
|
|
|
|
program_id: Pubkey,
|
|
|
|
process_instruction: ProcessInstruction,
|
|
|
|
) {
|
2019-04-02 08:35:38 -07:00
|
|
|
self.message_processor
|
2019-03-16 16:20:09 -07:00
|
|
|
.add_instruction_processor(program_id, process_instruction);
|
2019-03-16 04:50:44 -07:00
|
|
|
|
2019-04-02 07:29:28 -07:00
|
|
|
// Register a bogus executable account, which will be loaded and ignored.
|
|
|
|
self.register_native_instruction_processor("", &program_id);
|
2019-03-16 04:50:44 -07:00
|
|
|
}
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
|
|
|
|
2019-03-21 17:36:10 -07:00
|
|
|
impl Drop for Bank {
|
|
|
|
fn drop(&mut self) {
|
2019-04-15 17:15:50 -07:00
|
|
|
// For root forks this is a noop
|
2019-05-09 19:27:27 -07:00
|
|
|
self.accounts.purge_fork(self.slot());
|
2019-03-21 17:36:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-23 13:08:19 -08:00
|
|
|
#[cfg(test)]
|
2019-05-07 22:22:43 -07:00
|
|
|
mod tests {
|
2018-02-23 13:08:19 -08:00
|
|
|
use super::*;
|
2019-05-07 22:22:43 -07:00
|
|
|
use crate::genesis_utils::{create_genesis_block_with_leader, BOOTSTRAP_LEADER_LAMPORTS};
|
2019-05-07 11:16:22 -07:00
|
|
|
use solana_sdk::genesis_block::create_genesis_block;
|
2019-03-13 14:06:12 -07:00
|
|
|
use solana_sdk::hash;
|
2019-03-26 09:03:12 -07:00
|
|
|
use solana_sdk::instruction::InstructionError;
|
2019-02-19 15:54:38 -08:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
2019-04-03 08:45:57 -07:00
|
|
|
use solana_sdk::system_instruction;
|
|
|
|
use solana_sdk::system_transaction;
|
2019-04-05 14:23:00 -07:00
|
|
|
use solana_vote_api::vote_instruction;
|
2019-05-07 22:22:43 -07:00
|
|
|
use solana_vote_api::vote_state::VoteState;
|
2018-09-07 20:18:36 -07:00
|
|
|
|
2018-11-02 14:32:05 -07:00
|
|
|
#[test]
|
2019-05-07 11:16:22 -07:00
|
|
|
fn test_bank_new() {
|
2019-03-30 20:37:33 -07:00
|
|
|
let dummy_leader_id = Pubkey::new_rand();
|
2019-03-05 16:28:14 -08:00
|
|
|
let dummy_leader_lamports = BOOTSTRAP_LEADER_LAMPORTS;
|
2019-05-07 11:16:22 -07:00
|
|
|
let mint_lamports = 10_000;
|
|
|
|
let (genesis_block, mint_keypair, voting_keypair) = create_genesis_block_with_leader(
|
|
|
|
mint_lamports,
|
|
|
|
&dummy_leader_id,
|
|
|
|
dummy_leader_lamports,
|
2019-03-05 16:28:14 -08:00
|
|
|
);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-05-07 11:16:22 -07:00
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), mint_lamports);
|
2019-02-05 08:03:52 -08:00
|
|
|
assert_eq!(
|
2019-05-07 11:16:22 -07:00
|
|
|
bank.get_balance(&voting_keypair.pubkey()),
|
|
|
|
dummy_leader_lamports /* 1 token goes to the vote account associated with dummy_leader_lamports */
|
2019-02-05 08:03:52 -08:00
|
|
|
);
|
2018-11-02 14:32:05 -07:00
|
|
|
}
|
|
|
|
|
2018-02-23 13:08:19 -08:00
|
|
|
#[test]
|
2018-06-06 08:49:22 -07:00
|
|
|
fn test_two_payments_to_one_party() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(10_000);
|
2019-03-30 20:37:33 -07:00
|
|
|
let pubkey = Pubkey::new_rand();
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-03-02 10:25:16 -08:00
|
|
|
assert_eq!(bank.last_blockhash(), genesis_block.hash());
|
2018-05-03 12:24:35 -07:00
|
|
|
|
2019-03-27 04:59:30 -07:00
|
|
|
bank.transfer(1_000, &mint_keypair, &pubkey).unwrap();
|
2018-07-02 16:31:10 -07:00
|
|
|
assert_eq!(bank.get_balance(&pubkey), 1_000);
|
2018-02-23 13:08:19 -08:00
|
|
|
|
2019-03-27 04:59:30 -07:00
|
|
|
bank.transfer(500, &mint_keypair, &pubkey).unwrap();
|
2018-07-02 16:31:10 -07:00
|
|
|
assert_eq!(bank.get_balance(&pubkey), 1_500);
|
2018-05-14 14:33:11 -07:00
|
|
|
assert_eq!(bank.transaction_count(), 2);
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
2018-02-27 10:28:10 -08:00
|
|
|
|
2018-09-28 16:16:35 -07:00
|
|
|
#[test]
|
2018-10-04 13:15:54 -07:00
|
|
|
fn test_one_source_two_tx_one_batch() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(1);
|
2019-03-30 20:37:33 -07:00
|
|
|
let key1 = Pubkey::new_rand();
|
|
|
|
let key2 = Pubkey::new_rand();
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-03-02 10:25:16 -08:00
|
|
|
assert_eq!(bank.last_blockhash(), genesis_block.hash());
|
2018-10-04 13:15:54 -07:00
|
|
|
|
2019-04-03 08:45:57 -07:00
|
|
|
let t1 = system_transaction::transfer(&mint_keypair, &key1, 1, genesis_block.hash(), 0);
|
|
|
|
let t2 = system_transaction::transfer(&mint_keypair, &key2, 1, genesis_block.hash(), 0);
|
2018-10-04 13:15:54 -07:00
|
|
|
let res = bank.process_transactions(&vec![t1.clone(), t2.clone()]);
|
|
|
|
assert_eq!(res.len(), 2);
|
|
|
|
assert_eq!(res[0], Ok(()));
|
2019-03-13 12:58:44 -07:00
|
|
|
assert_eq!(res[1], Err(TransactionError::AccountInUse));
|
2019-01-24 12:04:04 -08:00
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 0);
|
2018-10-04 13:15:54 -07:00
|
|
|
assert_eq!(bank.get_balance(&key1), 1);
|
|
|
|
assert_eq!(bank.get_balance(&key2), 0);
|
2019-01-31 06:53:52 -08:00
|
|
|
assert_eq!(bank.get_signature_status(&t1.signatures[0]), Some(Ok(())));
|
2019-04-11 11:51:34 -07:00
|
|
|
// TODO: Transactions that fail to pay a fee could be dropped silently.
|
|
|
|
// Non-instruction errors don't get logged in the signature cache
|
|
|
|
assert_eq!(bank.get_signature_status(&t2.signatures[0]), None);
|
2018-10-04 13:15:54 -07:00
|
|
|
}
|
2018-09-28 16:16:35 -07:00
|
|
|
|
2018-10-04 13:15:54 -07:00
|
|
|
#[test]
|
|
|
|
fn test_one_tx_two_out_atomic_fail() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(1);
|
2019-03-30 20:37:33 -07:00
|
|
|
let key1 = Pubkey::new_rand();
|
|
|
|
let key2 = Pubkey::new_rand();
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-03-26 09:03:12 -07:00
|
|
|
let instructions =
|
2019-04-03 08:45:57 -07:00
|
|
|
system_instruction::transfer_many(&mint_keypair.pubkey(), &[(key1, 1), (key2, 1)]);
|
2019-03-26 09:03:12 -07:00
|
|
|
let tx = Transaction::new_signed_instructions(
|
2019-01-24 12:04:04 -08:00
|
|
|
&[&mint_keypair],
|
2019-03-26 09:03:12 -07:00
|
|
|
instructions,
|
2019-03-01 09:49:37 -08:00
|
|
|
genesis_block.hash(),
|
2018-09-28 16:16:35 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2019-03-26 09:03:12 -07:00
|
|
|
bank.process_transaction(&tx).unwrap_err(),
|
|
|
|
TransactionError::InstructionError(
|
2018-11-23 14:53:39 -08:00
|
|
|
1,
|
2019-03-13 10:55:43 -07:00
|
|
|
InstructionError::new_result_with_negative_lamports(),
|
2019-03-26 09:03:12 -07:00
|
|
|
)
|
2018-09-28 16:16:35 -07:00
|
|
|
);
|
2019-03-26 09:03:12 -07:00
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 1);
|
|
|
|
assert_eq!(bank.get_balance(&key1), 0);
|
|
|
|
assert_eq!(bank.get_balance(&key2), 0);
|
2018-09-28 16:16:35 -07:00
|
|
|
}
|
2018-10-16 09:43:49 -07:00
|
|
|
|
2018-09-28 16:16:35 -07:00
|
|
|
#[test]
|
|
|
|
fn test_one_tx_two_out_atomic_pass() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(2);
|
2019-03-30 20:37:33 -07:00
|
|
|
let key1 = Pubkey::new_rand();
|
|
|
|
let key2 = Pubkey::new_rand();
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-03-26 09:03:12 -07:00
|
|
|
let instructions =
|
2019-04-03 08:45:57 -07:00
|
|
|
system_instruction::transfer_many(&mint_keypair.pubkey(), &[(key1, 1), (key2, 1)]);
|
2019-03-26 09:03:12 -07:00
|
|
|
let tx = Transaction::new_signed_instructions(
|
|
|
|
&[&mint_keypair],
|
|
|
|
instructions,
|
2019-03-01 09:49:37 -08:00
|
|
|
genesis_block.hash(),
|
2018-09-28 16:16:35 -07:00
|
|
|
);
|
2019-03-26 09:03:12 -07:00
|
|
|
bank.process_transaction(&tx).unwrap();
|
2019-01-24 12:04:04 -08:00
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 0);
|
2018-09-28 16:16:35 -07:00
|
|
|
assert_eq!(bank.get_balance(&key1), 1);
|
|
|
|
assert_eq!(bank.get_balance(&key2), 1);
|
|
|
|
}
|
|
|
|
|
2019-02-22 11:52:48 -08:00
|
|
|
// This test demonstrates that fees are paid even when a program fails.
|
2018-09-07 20:18:36 -07:00
|
|
|
#[test]
|
2019-03-01 18:45:10 -08:00
|
|
|
fn test_detect_failed_duplicate_transactions() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(2);
|
2019-03-29 15:11:21 -07:00
|
|
|
let mut bank = Bank::new(&genesis_block);
|
|
|
|
bank.fee_calculator.lamports_per_signature = 1;
|
|
|
|
|
2018-09-17 13:36:31 -07:00
|
|
|
let dest = Keypair::new();
|
2018-09-07 20:18:36 -07:00
|
|
|
|
2018-11-25 21:56:48 -08:00
|
|
|
// source with 0 program context
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx = system_transaction::create_user_account(
|
2019-01-24 12:04:04 -08:00
|
|
|
&mint_keypair,
|
2019-03-09 19:28:43 -08:00
|
|
|
&dest.pubkey(),
|
2018-09-26 15:18:45 -07:00
|
|
|
2,
|
2019-03-01 09:49:37 -08:00
|
|
|
genesis_block.hash(),
|
2019-03-29 15:11:21 -07:00
|
|
|
0,
|
2018-09-26 15:18:45 -07:00
|
|
|
);
|
2018-10-26 14:43:34 -07:00
|
|
|
let signature = tx.signatures[0];
|
2018-09-07 20:18:36 -07:00
|
|
|
assert!(!bank.has_signature(&signature));
|
2019-02-22 11:52:48 -08:00
|
|
|
|
2019-02-18 22:26:22 -08:00
|
|
|
assert_eq!(
|
2019-03-01 18:45:10 -08:00
|
|
|
bank.process_transaction(&tx),
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::InstructionError(
|
2018-11-23 14:53:39 -08:00
|
|
|
0,
|
2019-03-13 10:55:43 -07:00
|
|
|
InstructionError::new_result_with_negative_lamports(),
|
2019-03-01 18:45:10 -08:00
|
|
|
))
|
2018-09-26 15:18:45 -07:00
|
|
|
);
|
|
|
|
|
2019-03-05 16:28:14 -08:00
|
|
|
// The lamports didn't move, but the from address paid the transaction fee.
|
2018-09-17 13:36:31 -07:00
|
|
|
assert_eq!(bank.get_balance(&dest.pubkey()), 0);
|
2018-09-26 15:18:45 -07:00
|
|
|
|
2019-02-21 16:18:09 -08:00
|
|
|
// This should be the original balance minus the transaction fee.
|
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 1);
|
2018-09-07 20:18:36 -07:00
|
|
|
}
|
|
|
|
|
2018-04-05 20:00:08 -07:00
|
|
|
#[test]
|
|
|
|
fn test_account_not_found() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(0);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2018-08-09 07:56:04 -07:00
|
|
|
let keypair = Keypair::new();
|
2018-04-05 20:00:08 -07:00
|
|
|
assert_eq!(
|
2019-03-27 04:59:30 -07:00
|
|
|
bank.transfer(1, &keypair, &mint_keypair.pubkey()),
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::AccountNotFound)
|
2018-04-05 20:00:08 -07:00
|
|
|
);
|
2018-05-14 14:33:11 -07:00
|
|
|
assert_eq!(bank.transaction_count(), 0);
|
2018-04-05 20:00:08 -07:00
|
|
|
}
|
|
|
|
|
2018-02-27 10:28:10 -08:00
|
|
|
#[test]
|
2018-06-06 08:49:22 -07:00
|
|
|
fn test_insufficient_funds() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(11_000);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-03-30 20:37:33 -07:00
|
|
|
let pubkey = Pubkey::new_rand();
|
2019-03-27 04:59:30 -07:00
|
|
|
bank.transfer(1_000, &mint_keypair, &pubkey).unwrap();
|
2018-05-14 14:33:11 -07:00
|
|
|
assert_eq!(bank.transaction_count(), 1);
|
2018-09-07 20:18:36 -07:00
|
|
|
assert_eq!(bank.get_balance(&pubkey), 1_000);
|
2019-02-18 22:26:22 -08:00
|
|
|
assert_eq!(
|
2019-03-27 04:59:30 -07:00
|
|
|
bank.transfer(10_001, &mint_keypair, &pubkey),
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::InstructionError(
|
2018-11-23 14:53:39 -08:00
|
|
|
0,
|
2019-03-13 10:55:43 -07:00
|
|
|
InstructionError::new_result_with_negative_lamports(),
|
2019-03-01 18:45:10 -08:00
|
|
|
))
|
2018-03-02 09:16:39 -08:00
|
|
|
);
|
2019-03-01 18:45:10 -08:00
|
|
|
assert_eq!(bank.transaction_count(), 1);
|
2018-03-01 11:23:27 -08:00
|
|
|
|
2019-01-24 12:04:04 -08:00
|
|
|
let mint_pubkey = mint_keypair.pubkey();
|
2018-07-02 16:31:10 -07:00
|
|
|
assert_eq!(bank.get_balance(&mint_pubkey), 10_000);
|
|
|
|
assert_eq!(bank.get_balance(&pubkey), 1_000);
|
2018-02-27 10:28:10 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_transfer_to_newb() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(10_000);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-03-30 20:37:33 -07:00
|
|
|
let pubkey = Pubkey::new_rand();
|
2019-03-27 04:59:30 -07:00
|
|
|
bank.transfer(500, &mint_keypair, &pubkey).unwrap();
|
2018-07-02 16:31:10 -07:00
|
|
|
assert_eq!(bank.get_balance(&pubkey), 500);
|
2018-02-27 10:28:10 -08:00
|
|
|
}
|
2018-03-08 07:58:01 -08:00
|
|
|
|
2019-02-21 13:37:08 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_deposit() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, _mint_keypair) = create_genesis_block(100);
|
2019-02-21 13:37:08 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
|
|
|
|
// Test new account
|
|
|
|
let key = Keypair::new();
|
|
|
|
bank.deposit(&key.pubkey(), 10);
|
|
|
|
assert_eq!(bank.get_balance(&key.pubkey()), 10);
|
|
|
|
|
|
|
|
// Existing account
|
|
|
|
bank.deposit(&key.pubkey(), 3);
|
|
|
|
assert_eq!(bank.get_balance(&key.pubkey()), 13);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bank_withdraw() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, _mint_keypair) = create_genesis_block(100);
|
2019-02-21 13:37:08 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
|
|
|
|
// Test no account
|
|
|
|
let key = Keypair::new();
|
|
|
|
assert_eq!(
|
|
|
|
bank.withdraw(&key.pubkey(), 10),
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::AccountNotFound)
|
2019-02-21 13:37:08 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
bank.deposit(&key.pubkey(), 3);
|
|
|
|
assert_eq!(bank.get_balance(&key.pubkey()), 3);
|
|
|
|
|
|
|
|
// Low balance
|
|
|
|
assert_eq!(
|
|
|
|
bank.withdraw(&key.pubkey(), 10),
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::InsufficientFundsForFee)
|
2019-02-21 13:37:08 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Enough balance
|
|
|
|
assert_eq!(bank.withdraw(&key.pubkey(), 2), Ok(()));
|
|
|
|
assert_eq!(bank.get_balance(&key.pubkey()), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bank_tx_fee() {
|
2019-03-30 20:37:33 -07:00
|
|
|
let leader = Pubkey::new_rand();
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair, _voting_keypair) =
|
|
|
|
create_genesis_block_with_leader(100, &leader, 3);
|
2019-03-29 15:11:21 -07:00
|
|
|
let mut bank = Bank::new(&genesis_block);
|
|
|
|
bank.fee_calculator.lamports_per_signature = 3;
|
|
|
|
|
2019-02-21 13:37:08 -08:00
|
|
|
let key1 = Keypair::new();
|
|
|
|
let key2 = Keypair::new();
|
|
|
|
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx =
|
|
|
|
system_transaction::transfer(&mint_keypair, &key1.pubkey(), 2, genesis_block.hash(), 0);
|
2019-02-24 20:17:47 -08:00
|
|
|
let initial_balance = bank.get_balance(&leader);
|
2019-02-21 13:37:08 -08:00
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
2019-02-24 20:17:47 -08:00
|
|
|
assert_eq!(bank.get_balance(&leader), initial_balance + 3);
|
2019-02-21 13:37:08 -08:00
|
|
|
assert_eq!(bank.get_balance(&key1.pubkey()), 2);
|
2019-05-07 11:16:22 -07:00
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 100 - 5);
|
2019-02-21 13:37:08 -08:00
|
|
|
|
2019-03-29 15:11:21 -07:00
|
|
|
bank.fee_calculator.lamports_per_signature = 1;
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx = system_transaction::transfer(&key1, &key2.pubkey(), 1, genesis_block.hash(), 0);
|
2019-04-05 16:55:58 -07:00
|
|
|
|
2019-02-21 13:37:08 -08:00
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
2019-02-24 20:17:47 -08:00
|
|
|
assert_eq!(bank.get_balance(&leader), initial_balance + 4);
|
2019-02-21 13:37:08 -08:00
|
|
|
assert_eq!(bank.get_balance(&key1.pubkey()), 0);
|
|
|
|
assert_eq!(bank.get_balance(&key2.pubkey()), 1);
|
2019-05-07 11:16:22 -07:00
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 100 - 5);
|
2019-04-05 16:55:58 -07:00
|
|
|
|
|
|
|
// verify that an InstructionError collects fees, too
|
|
|
|
let mut tx =
|
|
|
|
system_transaction::transfer(&mint_keypair, &key2.pubkey(), 1, genesis_block.hash(), 0);
|
|
|
|
// send a bogus instruction to system_program, cause an instruction error
|
|
|
|
tx.message.instructions[0].data[0] = 40;
|
|
|
|
|
2019-05-01 20:08:42 -07:00
|
|
|
bank.process_transaction(&tx)
|
|
|
|
.expect_err("instruction error"); // fails with an instruction error
|
2019-04-05 16:55:58 -07:00
|
|
|
assert_eq!(bank.get_balance(&leader), initial_balance + 5); // gots our bucks
|
2019-05-07 11:16:22 -07:00
|
|
|
assert_eq!(bank.get_balance(&key2.pubkey()), 1); // our fee --V
|
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 100 - 5 - 1);
|
2019-02-21 13:37:08 -08:00
|
|
|
}
|
|
|
|
|
2019-02-22 11:52:48 -08:00
|
|
|
#[test]
|
|
|
|
fn test_filter_program_errors_and_collect_fee() {
|
2019-03-30 20:37:33 -07:00
|
|
|
let leader = Pubkey::new_rand();
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair, _voting_keypair) =
|
|
|
|
create_genesis_block_with_leader(100, &leader, 3);
|
2019-03-29 15:11:21 -07:00
|
|
|
let mut bank = Bank::new(&genesis_block);
|
2019-02-22 11:52:48 -08:00
|
|
|
|
|
|
|
let key = Keypair::new();
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx1 =
|
|
|
|
system_transaction::transfer(&mint_keypair, &key.pubkey(), 2, genesis_block.hash(), 0);
|
|
|
|
let tx2 =
|
|
|
|
system_transaction::transfer(&mint_keypair, &key.pubkey(), 5, genesis_block.hash(), 0);
|
2019-02-22 11:52:48 -08:00
|
|
|
|
|
|
|
let results = vec![
|
|
|
|
Ok(()),
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::InstructionError(
|
2019-02-22 11:52:48 -08:00
|
|
|
1,
|
2019-03-13 10:55:43 -07:00
|
|
|
InstructionError::new_result_with_negative_lamports(),
|
2019-02-22 11:52:48 -08:00
|
|
|
)),
|
|
|
|
];
|
|
|
|
|
2019-03-29 15:11:21 -07:00
|
|
|
bank.fee_calculator.lamports_per_signature = 2;
|
2019-02-24 20:17:47 -08:00
|
|
|
let initial_balance = bank.get_balance(&leader);
|
2019-02-22 11:52:48 -08:00
|
|
|
let results = bank.filter_program_errors_and_collect_fee(&vec![tx1, tx2], &results);
|
2019-03-29 15:11:21 -07:00
|
|
|
assert_eq!(bank.get_balance(&leader), initial_balance + 2 + 2);
|
2019-02-22 11:52:48 -08:00
|
|
|
assert_eq!(results[0], Ok(()));
|
|
|
|
assert_eq!(results[1], Ok(()));
|
|
|
|
}
|
|
|
|
|
2018-04-12 19:53:34 -07:00
|
|
|
#[test]
|
|
|
|
fn test_debits_before_credits() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(2);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2018-08-09 07:56:04 -07:00
|
|
|
let keypair = Keypair::new();
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx0 = system_transaction::create_user_account(
|
2019-02-01 07:36:35 -08:00
|
|
|
&mint_keypair,
|
2019-03-09 19:28:43 -08:00
|
|
|
&keypair.pubkey(),
|
2019-02-01 07:36:35 -08:00
|
|
|
2,
|
2019-03-01 09:49:37 -08:00
|
|
|
genesis_block.hash(),
|
2019-02-01 07:36:35 -08:00
|
|
|
0,
|
|
|
|
);
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx1 = system_transaction::create_user_account(
|
2019-02-01 07:36:35 -08:00
|
|
|
&keypair,
|
2019-03-09 19:28:43 -08:00
|
|
|
&mint_keypair.pubkey(),
|
2019-02-01 07:36:35 -08:00
|
|
|
1,
|
2019-03-01 09:49:37 -08:00
|
|
|
genesis_block.hash(),
|
2019-02-01 07:36:35 -08:00
|
|
|
0,
|
|
|
|
);
|
2018-05-29 09:12:27 -07:00
|
|
|
let txs = vec![tx0, tx1];
|
2018-09-21 14:48:10 -07:00
|
|
|
let results = bank.process_transactions(&txs);
|
2018-05-14 05:49:48 -07:00
|
|
|
assert!(results[1].is_err());
|
|
|
|
|
|
|
|
// Assert bad transactions aren't counted.
|
2018-05-14 14:33:11 -07:00
|
|
|
assert_eq!(bank.transaction_count(), 1);
|
2018-04-12 19:53:34 -07:00
|
|
|
}
|
2018-06-14 16:32:39 -07:00
|
|
|
|
2018-09-07 12:55:30 -07:00
|
|
|
#[test]
|
2018-10-04 13:15:54 -07:00
|
|
|
fn test_interleaving_locks() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(3);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2018-10-04 13:15:54 -07:00
|
|
|
let alice = Keypair::new();
|
|
|
|
let bob = Keypair::new();
|
|
|
|
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx1 = system_transaction::create_user_account(
|
2019-02-01 07:36:35 -08:00
|
|
|
&mint_keypair,
|
2019-03-09 19:28:43 -08:00
|
|
|
&alice.pubkey(),
|
2019-02-01 07:36:35 -08:00
|
|
|
1,
|
2019-03-01 09:49:37 -08:00
|
|
|
genesis_block.hash(),
|
2019-02-01 07:36:35 -08:00
|
|
|
0,
|
|
|
|
);
|
2018-10-04 13:15:54 -07:00
|
|
|
let pay_alice = vec![tx1];
|
|
|
|
|
2019-01-06 22:06:55 -08:00
|
|
|
let lock_result = bank.lock_accounts(&pay_alice);
|
2019-03-01 12:23:30 -08:00
|
|
|
let results_alice = bank.load_execute_and_commit_transactions(
|
|
|
|
&pay_alice,
|
2019-04-02 03:55:42 -07:00
|
|
|
&lock_result,
|
2019-03-02 10:25:16 -08:00
|
|
|
MAX_RECENT_BLOCKHASHES,
|
2019-03-01 12:23:30 -08:00
|
|
|
);
|
2018-10-04 13:15:54 -07:00
|
|
|
assert_eq!(results_alice[0], Ok(()));
|
|
|
|
|
|
|
|
// try executing an interleaved transfer twice
|
|
|
|
assert_eq!(
|
2019-03-27 04:59:30 -07:00
|
|
|
bank.transfer(1, &mint_keypair, &bob.pubkey()),
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::AccountInUse)
|
2018-10-04 13:15:54 -07:00
|
|
|
);
|
2019-01-04 16:04:31 -08:00
|
|
|
// the second time should fail as well
|
2018-10-04 13:15:54 -07:00
|
|
|
// this verifies that `unlock_accounts` doesn't unlock `AccountInUse` accounts
|
|
|
|
assert_eq!(
|
2019-03-27 04:59:30 -07:00
|
|
|
bank.transfer(1, &mint_keypair, &bob.pubkey()),
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::AccountInUse)
|
2018-10-04 13:15:54 -07:00
|
|
|
);
|
|
|
|
|
2019-04-02 03:55:42 -07:00
|
|
|
drop(lock_result);
|
2018-10-04 13:15:54 -07:00
|
|
|
|
2019-03-27 04:59:30 -07:00
|
|
|
assert!(bank.transfer(2, &mint_keypair, &bob.pubkey()).is_ok());
|
2018-10-04 13:15:54 -07:00
|
|
|
}
|
2018-10-10 13:51:43 -07:00
|
|
|
|
2019-03-19 18:03:07 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bank_invalid_account_index() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(1);
|
2019-03-19 18:03:07 -07:00
|
|
|
let keypair = Keypair::new();
|
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx = system_transaction::transfer(
|
2019-03-19 18:03:07 -07:00
|
|
|
&mint_keypair,
|
|
|
|
&keypair.pubkey(),
|
|
|
|
1,
|
|
|
|
genesis_block.hash(),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut tx_invalid_program_index = tx.clone();
|
2019-03-29 09:05:06 -07:00
|
|
|
tx_invalid_program_index.message.instructions[0].program_ids_index = 42;
|
2019-03-19 18:03:07 -07:00
|
|
|
assert_eq!(
|
|
|
|
bank.process_transaction(&tx_invalid_program_index),
|
|
|
|
Err(TransactionError::InvalidAccountIndex)
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut tx_invalid_account_index = tx.clone();
|
2019-03-29 09:05:06 -07:00
|
|
|
tx_invalid_account_index.message.instructions[0].accounts[0] = 42;
|
2019-03-19 18:03:07 -07:00
|
|
|
assert_eq!(
|
|
|
|
bank.process_transaction(&tx_invalid_account_index),
|
|
|
|
Err(TransactionError::InvalidAccountIndex)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-07 11:14:10 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_pay_to_self() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(1);
|
2019-02-07 11:14:10 -08:00
|
|
|
let key1 = Keypair::new();
|
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-01-21 10:17:04 -08:00
|
|
|
|
2019-03-27 04:59:30 -07:00
|
|
|
bank.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
|
2019-02-07 11:14:10 -08:00
|
|
|
assert_eq!(bank.get_balance(&key1.pubkey()), 1);
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx = system_transaction::transfer(&key1, &key1.pubkey(), 1, genesis_block.hash(), 0);
|
2019-02-07 11:14:10 -08:00
|
|
|
let res = bank.process_transactions(&vec![tx.clone()]);
|
|
|
|
assert_eq!(res.len(), 1);
|
|
|
|
assert_eq!(bank.get_balance(&key1.pubkey()), 1);
|
2019-03-14 14:32:12 -07:00
|
|
|
|
|
|
|
// TODO: Why do we convert errors to Oks?
|
|
|
|
//res[0].clone().unwrap_err();
|
|
|
|
|
|
|
|
bank.get_signature_status(&tx.signatures[0])
|
|
|
|
.unwrap()
|
|
|
|
.unwrap_err();
|
2019-02-07 11:14:10 -08:00
|
|
|
}
|
2019-02-17 08:01:31 -08:00
|
|
|
|
2019-03-01 16:39:23 -08:00
|
|
|
fn new_from_parent(parent: &Arc<Bank>) -> Bank {
|
2019-03-09 19:28:43 -08:00
|
|
|
Bank::new_from_parent(parent, &Pubkey::default(), parent.slot() + 1)
|
2019-03-01 16:39:23 -08:00
|
|
|
}
|
|
|
|
|
2019-02-19 15:35:02 -08:00
|
|
|
/// Verify that the parent's vector is computed correctly
|
2019-02-17 08:01:31 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_parents() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, _) = create_genesis_block(1);
|
2019-02-17 08:01:31 -08:00
|
|
|
let parent = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
2019-03-01 16:39:23 -08:00
|
|
|
let bank = new_from_parent(&parent);
|
2019-02-17 08:01:31 -08:00
|
|
|
assert!(Arc::ptr_eq(&bank.parents()[0], &parent));
|
|
|
|
}
|
|
|
|
|
2019-02-19 15:35:02 -08:00
|
|
|
/// Verifies that last ids and status cache are correctly referenced from parent
|
2019-02-17 08:01:31 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_parent_duplicate_signature() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(2);
|
2019-02-17 08:01:31 -08:00
|
|
|
let key1 = Keypair::new();
|
|
|
|
let parent = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx =
|
|
|
|
system_transaction::transfer(&mint_keypair, &key1.pubkey(), 1, genesis_block.hash(), 0);
|
2019-02-17 08:01:31 -08:00
|
|
|
assert_eq!(parent.process_transaction(&tx), Ok(()));
|
2019-03-01 16:39:23 -08:00
|
|
|
let bank = new_from_parent(&parent);
|
2019-02-17 08:01:31 -08:00
|
|
|
assert_eq!(
|
|
|
|
bank.process_transaction(&tx),
|
2019-03-13 12:58:44 -07:00
|
|
|
Err(TransactionError::DuplicateSignature)
|
2019-02-17 08:01:31 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-19 15:35:02 -08:00
|
|
|
/// Verifies that last ids and accounts are correctly referenced from parent
|
2019-02-17 08:01:31 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_parent_account_spend() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(2);
|
2019-02-17 08:01:31 -08:00
|
|
|
let key1 = Keypair::new();
|
|
|
|
let key2 = Keypair::new();
|
|
|
|
let parent = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx =
|
|
|
|
system_transaction::transfer(&mint_keypair, &key1.pubkey(), 1, genesis_block.hash(), 0);
|
2019-02-17 08:01:31 -08:00
|
|
|
assert_eq!(parent.process_transaction(&tx), Ok(()));
|
2019-03-01 16:39:23 -08:00
|
|
|
let bank = new_from_parent(&parent);
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx = system_transaction::transfer(&key1, &key2.pubkey(), 1, genesis_block.hash(), 0);
|
2019-02-17 08:01:31 -08:00
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
2019-02-17 13:14:34 -08:00
|
|
|
assert_eq!(parent.get_signature_status(&tx.signatures[0]), None);
|
2019-02-17 08:01:31 -08:00
|
|
|
}
|
|
|
|
|
2019-02-20 15:21:32 -08:00
|
|
|
#[test]
|
2019-02-25 14:05:02 -08:00
|
|
|
fn test_bank_hash_internal_state() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(2_000);
|
2019-02-20 15:21:32 -08:00
|
|
|
let bank0 = Bank::new(&genesis_block);
|
|
|
|
let bank1 = Bank::new(&genesis_block);
|
|
|
|
let initial_state = bank0.hash_internal_state();
|
|
|
|
assert_eq!(bank1.hash_internal_state(), initial_state);
|
|
|
|
|
2019-03-30 20:37:33 -07:00
|
|
|
let pubkey = Pubkey::new_rand();
|
2019-03-27 04:59:30 -07:00
|
|
|
bank0.transfer(1_000, &mint_keypair, &pubkey).unwrap();
|
2019-02-20 15:21:32 -08:00
|
|
|
assert_ne!(bank0.hash_internal_state(), initial_state);
|
2019-03-27 04:59:30 -07:00
|
|
|
bank1.transfer(1_000, &mint_keypair, &pubkey).unwrap();
|
2019-02-20 15:21:32 -08:00
|
|
|
assert_eq!(bank0.hash_internal_state(), bank1.hash_internal_state());
|
|
|
|
|
|
|
|
// Checkpointing should not change its state
|
2019-03-01 16:39:23 -08:00
|
|
|
let bank2 = new_from_parent(&Arc::new(bank1));
|
2019-02-20 15:21:32 -08:00
|
|
|
assert_eq!(bank0.hash_internal_state(), bank2.hash_internal_state());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-02-25 14:05:02 -08:00
|
|
|
fn test_hash_internal_state_genesis() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let bank0 = Bank::new(&create_genesis_block(10).0);
|
|
|
|
let bank1 = Bank::new(&create_genesis_block(20).0);
|
2019-02-20 15:21:32 -08:00
|
|
|
assert_ne!(bank0.hash_internal_state(), bank1.hash_internal_state());
|
|
|
|
}
|
2019-02-21 12:08:50 -08:00
|
|
|
|
2019-02-25 14:05:02 -08:00
|
|
|
#[test]
|
2019-02-25 20:34:05 -08:00
|
|
|
fn test_bank_hash_internal_state_squash() {
|
2019-02-26 05:43:43 -08:00
|
|
|
let collector_id = Pubkey::default();
|
2019-05-07 11:16:22 -07:00
|
|
|
let bank0 = Arc::new(Bank::new(&create_genesis_block(10).0));
|
2019-03-09 19:28:43 -08:00
|
|
|
let bank1 = Bank::new_from_parent(&bank0, &collector_id, 1);
|
2019-02-25 14:05:02 -08:00
|
|
|
|
|
|
|
// no delta in bank1, hashes match
|
|
|
|
assert_eq!(bank0.hash_internal_state(), bank1.hash_internal_state());
|
|
|
|
|
|
|
|
// remove parent
|
2019-02-25 20:34:05 -08:00
|
|
|
bank1.squash();
|
2019-02-25 14:05:02 -08:00
|
|
|
assert!(bank1.parents().is_empty());
|
|
|
|
|
|
|
|
// hash should still match
|
|
|
|
assert_eq!(bank0.hash(), bank1.hash());
|
|
|
|
}
|
|
|
|
|
2019-02-21 12:08:50 -08:00
|
|
|
/// Verifies that last ids and accounts are correctly referenced from parent
|
|
|
|
#[test]
|
2019-02-25 20:34:05 -08:00
|
|
|
fn test_bank_squash() {
|
2019-03-29 10:03:55 -07:00
|
|
|
solana_logger::setup();
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(2);
|
2019-02-21 12:08:50 -08:00
|
|
|
let key1 = Keypair::new();
|
|
|
|
let key2 = Keypair::new();
|
|
|
|
let parent = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
2019-04-23 12:30:42 -07:00
|
|
|
let tx_transfer_mint_to_1 =
|
2019-04-03 08:45:57 -07:00
|
|
|
system_transaction::transfer(&mint_keypair, &key1.pubkey(), 1, genesis_block.hash(), 0);
|
2019-03-29 10:03:55 -07:00
|
|
|
trace!("parent process tx ");
|
2019-04-23 12:30:42 -07:00
|
|
|
assert_eq!(parent.process_transaction(&tx_transfer_mint_to_1), Ok(()));
|
2019-03-29 10:03:55 -07:00
|
|
|
trace!("done parent process tx ");
|
2019-02-27 20:45:51 -08:00
|
|
|
assert_eq!(parent.transaction_count(), 1);
|
2019-03-29 10:03:55 -07:00
|
|
|
assert_eq!(
|
2019-04-23 12:30:42 -07:00
|
|
|
parent.get_signature_status(&tx_transfer_mint_to_1.signatures[0]),
|
2019-03-29 10:03:55 -07:00
|
|
|
Some(Ok(()))
|
|
|
|
);
|
2019-02-27 20:45:51 -08:00
|
|
|
|
2019-03-29 10:03:55 -07:00
|
|
|
trace!("new form parent");
|
2019-03-01 16:39:23 -08:00
|
|
|
let bank = new_from_parent(&parent);
|
2019-03-29 10:03:55 -07:00
|
|
|
trace!("done new form parent");
|
|
|
|
assert_eq!(
|
2019-04-23 12:30:42 -07:00
|
|
|
bank.get_signature_status(&tx_transfer_mint_to_1.signatures[0]),
|
2019-03-29 10:03:55 -07:00
|
|
|
Some(Ok(()))
|
|
|
|
);
|
|
|
|
|
2019-03-05 12:34:21 -08:00
|
|
|
assert_eq!(bank.transaction_count(), parent.transaction_count());
|
2019-04-23 12:30:42 -07:00
|
|
|
let tx_transfer_1_to_2 =
|
2019-04-03 08:45:57 -07:00
|
|
|
system_transaction::transfer(&key1, &key2.pubkey(), 1, genesis_block.hash(), 0);
|
2019-04-23 12:30:42 -07:00
|
|
|
assert_eq!(bank.process_transaction(&tx_transfer_1_to_2), Ok(()));
|
2019-03-05 12:34:21 -08:00
|
|
|
assert_eq!(bank.transaction_count(), 2);
|
|
|
|
assert_eq!(parent.transaction_count(), 1);
|
2019-02-21 12:08:50 -08:00
|
|
|
assert_eq!(
|
2019-04-23 12:30:42 -07:00
|
|
|
parent.get_signature_status(&tx_transfer_1_to_2.signatures[0]),
|
2019-02-21 12:08:50 -08:00
|
|
|
None
|
|
|
|
);
|
|
|
|
|
|
|
|
for _ in 0..3 {
|
|
|
|
// first time these should match what happened above, assert that parents are ok
|
|
|
|
assert_eq!(bank.get_balance(&key1.pubkey()), 0);
|
2019-02-26 13:51:39 -08:00
|
|
|
assert_eq!(bank.get_account(&key1.pubkey()), None);
|
2019-02-21 12:08:50 -08:00
|
|
|
assert_eq!(bank.get_balance(&key2.pubkey()), 1);
|
2019-03-29 10:03:55 -07:00
|
|
|
trace!("start");
|
2019-02-21 12:08:50 -08:00
|
|
|
assert_eq!(
|
2019-04-23 12:30:42 -07:00
|
|
|
bank.get_signature_status(&tx_transfer_mint_to_1.signatures[0]),
|
2019-02-21 12:08:50 -08:00
|
|
|
Some(Ok(()))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2019-04-23 12:30:42 -07:00
|
|
|
bank.get_signature_status(&tx_transfer_1_to_2.signatures[0]),
|
2019-02-21 12:08:50 -08:00
|
|
|
Some(Ok(()))
|
|
|
|
);
|
|
|
|
|
|
|
|
// works iteration 0, no-ops on iteration 1 and 2
|
2019-03-29 10:03:55 -07:00
|
|
|
trace!("SQUASH");
|
2019-02-25 20:34:05 -08:00
|
|
|
bank.squash();
|
2019-02-27 20:45:51 -08:00
|
|
|
|
|
|
|
assert_eq!(parent.transaction_count(), 1);
|
|
|
|
assert_eq!(bank.transaction_count(), 2);
|
2019-02-21 12:08:50 -08:00
|
|
|
}
|
|
|
|
}
|
2019-02-26 13:59:39 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bank_get_account_in_parent_after_squash() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(500);
|
2019-02-26 13:59:39 -08:00
|
|
|
let parent = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
|
|
|
let key1 = Keypair::new();
|
|
|
|
|
2019-03-27 04:59:30 -07:00
|
|
|
parent.transfer(1, &mint_keypair, &key1.pubkey()).unwrap();
|
2019-02-26 13:59:39 -08:00
|
|
|
assert_eq!(parent.get_balance(&key1.pubkey()), 1);
|
2019-03-01 16:39:23 -08:00
|
|
|
let bank = new_from_parent(&parent);
|
2019-02-26 13:59:39 -08:00
|
|
|
bank.squash();
|
|
|
|
assert_eq!(parent.get_balance(&key1.pubkey()), 1);
|
|
|
|
}
|
2019-03-01 11:54:28 -08:00
|
|
|
|
|
|
|
#[test]
|
2019-03-01 17:22:49 -08:00
|
|
|
fn test_bank_epoch_vote_accounts() {
|
2019-03-30 20:37:33 -07:00
|
|
|
let leader_id = Pubkey::new_rand();
|
2019-03-05 16:28:14 -08:00
|
|
|
let leader_lamports = 3;
|
2019-05-07 11:16:22 -07:00
|
|
|
let mut genesis_block = create_genesis_block_with_leader(5, &leader_id, leader_lamports).0;
|
2019-03-01 11:54:28 -08:00
|
|
|
|
2019-03-06 16:32:23 -08:00
|
|
|
// set this up weird, forces future generation, odd mod(), etc.
|
2019-04-29 15:26:52 -07:00
|
|
|
// this says: "stakes for epoch X should be generated at slot index 3 in epoch X-2...
|
|
|
|
const SLOTS_PER_EPOCH: u64 = MINIMUM_SLOT_LENGTH as u64;
|
|
|
|
const STAKERS_SLOT_OFFSET: u64 = SLOTS_PER_EPOCH * 3 - 3;
|
2019-03-01 11:54:28 -08:00
|
|
|
genesis_block.slots_per_epoch = SLOTS_PER_EPOCH;
|
|
|
|
genesis_block.stakers_slot_offset = STAKERS_SLOT_OFFSET;
|
2019-03-06 16:32:23 -08:00
|
|
|
genesis_block.epoch_warmup = false; // allows me to do the normal division stuff below
|
2019-03-01 11:54:28 -08:00
|
|
|
|
|
|
|
let parent = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
2019-03-03 18:04:13 -08:00
|
|
|
let vote_accounts0: Option<HashMap<_, _>> = parent.epoch_vote_accounts(0).map(|accounts| {
|
|
|
|
accounts
|
|
|
|
.iter()
|
|
|
|
.filter_map(|(pubkey, account)| {
|
2019-03-14 09:48:27 -07:00
|
|
|
if let Ok(vote_state) = VoteState::deserialize(&account.data) {
|
2019-04-10 17:52:47 -07:00
|
|
|
if vote_state.node_id == leader_id {
|
2019-03-03 18:04:13 -08:00
|
|
|
Some((*pubkey, true))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
2019-03-01 13:40:48 -08:00
|
|
|
});
|
2019-03-01 17:22:49 -08:00
|
|
|
assert!(vote_accounts0.is_some());
|
|
|
|
assert!(vote_accounts0.iter().len() != 0);
|
2019-03-01 11:54:28 -08:00
|
|
|
|
2019-03-01 16:39:23 -08:00
|
|
|
let mut i = 1;
|
|
|
|
loop {
|
|
|
|
if i > STAKERS_SLOT_OFFSET / SLOTS_PER_EPOCH {
|
|
|
|
break;
|
|
|
|
}
|
2019-03-03 18:04:13 -08:00
|
|
|
assert!(parent.epoch_vote_accounts(i).is_some());
|
2019-03-01 16:39:23 -08:00
|
|
|
i += 1;
|
|
|
|
}
|
2019-03-01 11:54:28 -08:00
|
|
|
|
|
|
|
// child crosses epoch boundary and is the first slot in the epoch
|
2019-03-01 16:39:23 -08:00
|
|
|
let child = Bank::new_from_parent(
|
2019-03-01 11:54:28 -08:00
|
|
|
&parent,
|
2019-03-09 19:28:43 -08:00
|
|
|
&leader_id,
|
2019-03-01 11:54:28 -08:00
|
|
|
SLOTS_PER_EPOCH - (STAKERS_SLOT_OFFSET % SLOTS_PER_EPOCH),
|
|
|
|
);
|
2019-03-01 16:39:23 -08:00
|
|
|
|
2019-03-03 18:04:13 -08:00
|
|
|
assert!(child.epoch_vote_accounts(i).is_some());
|
2019-03-01 11:54:28 -08:00
|
|
|
|
|
|
|
// child crosses epoch boundary but isn't the first slot in the epoch
|
2019-03-01 16:39:23 -08:00
|
|
|
let child = Bank::new_from_parent(
|
2019-03-01 11:54:28 -08:00
|
|
|
&parent,
|
2019-03-09 19:28:43 -08:00
|
|
|
&leader_id,
|
2019-03-01 11:54:28 -08:00
|
|
|
SLOTS_PER_EPOCH - (STAKERS_SLOT_OFFSET % SLOTS_PER_EPOCH) + 1,
|
|
|
|
);
|
2019-03-03 18:04:13 -08:00
|
|
|
assert!(child.epoch_vote_accounts(i).is_some());
|
2019-03-01 11:54:28 -08:00
|
|
|
}
|
|
|
|
|
2019-03-02 14:01:53 -08:00
|
|
|
#[test]
|
|
|
|
fn test_zero_signatures() {
|
|
|
|
solana_logger::setup();
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(500);
|
2019-03-29 15:11:21 -07:00
|
|
|
let mut bank = Bank::new(&genesis_block);
|
|
|
|
bank.fee_calculator.lamports_per_signature = 2;
|
2019-03-02 14:01:53 -08:00
|
|
|
let key = Keypair::new();
|
|
|
|
|
2019-04-23 12:30:42 -07:00
|
|
|
let mut transfer_instruction =
|
2019-04-03 08:45:57 -07:00
|
|
|
system_instruction::transfer(&mint_keypair.pubkey(), &key.pubkey(), 0);
|
2019-04-23 12:30:42 -07:00
|
|
|
transfer_instruction.accounts[0].is_signer = false;
|
2019-03-02 14:01:53 -08:00
|
|
|
|
2019-03-29 15:11:21 -07:00
|
|
|
let tx = Transaction::new_signed_instructions(
|
2019-03-26 15:26:30 -07:00
|
|
|
&Vec::<&Keypair>::new(),
|
2019-04-23 12:30:42 -07:00
|
|
|
vec![transfer_instruction],
|
2019-03-02 14:01:53 -08:00
|
|
|
bank.last_blockhash(),
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
|
|
|
assert_eq!(bank.get_balance(&key.pubkey()), 0);
|
|
|
|
}
|
|
|
|
|
2019-03-06 14:44:21 -08:00
|
|
|
#[test]
|
2019-03-06 16:32:23 -08:00
|
|
|
fn test_bank_get_slots_in_epoch() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, _) = create_genesis_block(500);
|
2019-03-06 14:44:21 -08:00
|
|
|
|
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
|
2019-04-29 15:26:52 -07:00
|
|
|
assert_eq!(bank.get_slots_in_epoch(0), MINIMUM_SLOT_LENGTH as u64);
|
|
|
|
assert_eq!(bank.get_slots_in_epoch(2), (MINIMUM_SLOT_LENGTH * 4) as u64);
|
2019-03-06 16:32:23 -08:00
|
|
|
assert_eq!(bank.get_slots_in_epoch(5000), genesis_block.slots_per_epoch);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_epoch_schedule() {
|
|
|
|
// one week of slots at 8 ticks/slot, 10 ticks/sec is
|
|
|
|
// (1 * 7 * 24 * 4500u64).next_power_of_two();
|
|
|
|
|
2019-04-29 15:26:52 -07:00
|
|
|
// test values between MINIMUM_SLOT_LEN and MINIMUM_SLOT_LEN * 16, should cover a good mix
|
|
|
|
for slots_per_epoch in MINIMUM_SLOT_LENGTH as u64..=MINIMUM_SLOT_LENGTH as u64 * 16 {
|
2019-03-06 16:32:23 -08:00
|
|
|
let epoch_schedule = EpochSchedule::new(slots_per_epoch, slots_per_epoch / 2, true);
|
|
|
|
|
2019-05-09 16:06:57 -07:00
|
|
|
assert_eq!(epoch_schedule.get_first_slot_in_epoch(0), 0);
|
|
|
|
assert_eq!(
|
|
|
|
epoch_schedule.get_last_slot_in_epoch(0),
|
|
|
|
MINIMUM_SLOT_LENGTH as u64 - 1
|
|
|
|
);
|
|
|
|
|
2019-03-06 16:32:23 -08:00
|
|
|
let mut last_stakers = 0;
|
|
|
|
let mut last_epoch = 0;
|
2019-04-29 15:26:52 -07:00
|
|
|
let mut last_slots_in_epoch = MINIMUM_SLOT_LENGTH as u64;
|
2019-03-06 16:32:23 -08:00
|
|
|
for slot in 0..(2 * slots_per_epoch) {
|
|
|
|
// verify that stakers_epoch is continuous over the warmup
|
2019-04-29 15:26:52 -07:00
|
|
|
// and into the first normal epoch
|
2019-03-06 16:32:23 -08:00
|
|
|
|
|
|
|
let stakers = epoch_schedule.get_stakers_epoch(slot);
|
|
|
|
if stakers != last_stakers {
|
|
|
|
assert_eq!(stakers, last_stakers + 1);
|
|
|
|
last_stakers = stakers;
|
|
|
|
}
|
|
|
|
|
|
|
|
let (epoch, offset) = epoch_schedule.get_epoch_and_slot_index(slot);
|
|
|
|
|
|
|
|
// verify that epoch increases continuously
|
|
|
|
if epoch != last_epoch {
|
|
|
|
assert_eq!(epoch, last_epoch + 1);
|
|
|
|
last_epoch = epoch;
|
2019-05-09 16:06:57 -07:00
|
|
|
assert_eq!(epoch_schedule.get_first_slot_in_epoch(epoch), slot);
|
|
|
|
assert_eq!(epoch_schedule.get_last_slot_in_epoch(epoch - 1), slot - 1);
|
2019-03-06 16:32:23 -08:00
|
|
|
|
|
|
|
// verify that slots in an epoch double continuously
|
|
|
|
// until they reach slots_per_epoch
|
|
|
|
|
|
|
|
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(epoch);
|
|
|
|
if slots_in_epoch != last_slots_in_epoch {
|
|
|
|
if slots_in_epoch != slots_per_epoch {
|
|
|
|
assert_eq!(slots_in_epoch, last_slots_in_epoch * 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
last_slots_in_epoch = slots_in_epoch;
|
|
|
|
}
|
|
|
|
// verify that the slot offset is less than slots_in_epoch
|
|
|
|
assert!(offset < last_slots_in_epoch);
|
|
|
|
}
|
|
|
|
|
|
|
|
// assert that these changed ;)
|
|
|
|
assert!(last_stakers != 0); // t
|
|
|
|
assert!(last_epoch != 0);
|
|
|
|
// assert that we got to "normal" mode
|
|
|
|
assert!(last_slots_in_epoch == slots_per_epoch);
|
|
|
|
}
|
2019-03-06 14:44:21 -08:00
|
|
|
}
|
|
|
|
|
2019-03-13 14:06:12 -07:00
|
|
|
#[test]
|
|
|
|
fn test_is_delta_true() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(500);
|
2019-03-13 14:06:12 -07:00
|
|
|
let bank = Arc::new(Bank::new(&genesis_block));
|
|
|
|
let key1 = Keypair::new();
|
2019-04-23 12:30:42 -07:00
|
|
|
let tx_transfer_mint_to_1 =
|
2019-04-03 08:45:57 -07:00
|
|
|
system_transaction::transfer(&mint_keypair, &key1.pubkey(), 1, genesis_block.hash(), 0);
|
2019-04-23 12:30:42 -07:00
|
|
|
assert_eq!(bank.process_transaction(&tx_transfer_mint_to_1), Ok(()));
|
2019-03-13 14:06:12 -07:00
|
|
|
assert_eq!(bank.is_delta.load(Ordering::Relaxed), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_is_votable() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(500);
|
2019-03-13 14:06:12 -07:00
|
|
|
let bank = Arc::new(Bank::new(&genesis_block));
|
|
|
|
let key1 = Keypair::new();
|
|
|
|
assert_eq!(bank.is_votable(), false);
|
|
|
|
|
|
|
|
// Set is_delta to true
|
2019-04-23 12:30:42 -07:00
|
|
|
let tx_transfer_mint_to_1 =
|
2019-04-03 08:45:57 -07:00
|
|
|
system_transaction::transfer(&mint_keypair, &key1.pubkey(), 1, genesis_block.hash(), 0);
|
2019-04-23 12:30:42 -07:00
|
|
|
assert_eq!(bank.process_transaction(&tx_transfer_mint_to_1), Ok(()));
|
2019-03-13 14:06:12 -07:00
|
|
|
assert_eq!(bank.is_votable(), false);
|
|
|
|
|
|
|
|
// Register enough ticks to hit max tick height
|
|
|
|
for i in 0..genesis_block.ticks_per_slot - 1 {
|
|
|
|
bank.register_tick(&hash::hash(format!("hello world {}", i).as_bytes()));
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(bank.is_votable(), true);
|
|
|
|
}
|
2019-03-18 16:04:36 -07:00
|
|
|
|
2019-04-05 10:42:25 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bank_inherit_tx_count() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(500);
|
2019-04-05 10:42:25 -07:00
|
|
|
let bank0 = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
|
|
|
// Bank 1
|
|
|
|
let bank1 = Arc::new(new_from_parent(&bank0));
|
|
|
|
// Bank 2
|
|
|
|
let bank2 = new_from_parent(&bank0);
|
|
|
|
|
|
|
|
// transfer a token
|
|
|
|
assert_eq!(
|
|
|
|
bank1.process_transaction(&system_transaction::transfer(
|
|
|
|
&mint_keypair,
|
|
|
|
&Keypair::new().pubkey(),
|
|
|
|
1,
|
|
|
|
genesis_block.hash(),
|
|
|
|
0
|
|
|
|
)),
|
|
|
|
Ok(())
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(bank0.transaction_count(), 0);
|
|
|
|
assert_eq!(bank2.transaction_count(), 0);
|
|
|
|
assert_eq!(bank1.transaction_count(), 1);
|
|
|
|
|
|
|
|
bank1.squash();
|
|
|
|
|
|
|
|
assert_eq!(bank0.transaction_count(), 0);
|
|
|
|
assert_eq!(bank2.transaction_count(), 0);
|
|
|
|
assert_eq!(bank1.transaction_count(), 1);
|
|
|
|
|
|
|
|
let bank6 = new_from_parent(&bank1);
|
|
|
|
assert_eq!(bank1.transaction_count(), 1);
|
|
|
|
assert_eq!(bank6.transaction_count(), 1);
|
|
|
|
|
|
|
|
bank6.squash();
|
|
|
|
assert_eq!(bank6.transaction_count(), 1);
|
|
|
|
}
|
|
|
|
|
2019-05-07 20:28:41 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bank_inherit_fee_calculator() {
|
|
|
|
let (mut genesis_block, _mint_keypair) = create_genesis_block(500);
|
|
|
|
genesis_block.fee_calculator.lamports_per_signature = 123;
|
|
|
|
let bank0 = Arc::new(Bank::new(&genesis_block));
|
|
|
|
let bank1 = Arc::new(new_from_parent(&bank0));
|
|
|
|
assert_eq!(
|
|
|
|
bank0.fee_calculator.lamports_per_signature,
|
|
|
|
bank1.fee_calculator.lamports_per_signature
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-04-05 14:23:00 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bank_vote_accounts() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair, _voting_keypair) =
|
|
|
|
create_genesis_block_with_leader(500, &Pubkey::new_rand(), 1);
|
2019-04-05 14:23:00 -07:00
|
|
|
let bank = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
|
|
|
let vote_accounts = bank.vote_accounts();
|
|
|
|
assert_eq!(vote_accounts.len(), 1); // bootstrap leader has
|
|
|
|
// to have a vote account
|
|
|
|
|
|
|
|
let vote_keypair = Keypair::new();
|
2019-04-10 17:52:47 -07:00
|
|
|
let instructions = vote_instruction::create_account(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&vote_keypair.pubkey(),
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
0,
|
|
|
|
10,
|
|
|
|
);
|
2019-04-05 14:23:00 -07:00
|
|
|
|
|
|
|
let transaction = Transaction::new_signed_instructions(
|
|
|
|
&[&mint_keypair],
|
|
|
|
instructions,
|
|
|
|
bank.last_blockhash(),
|
|
|
|
);
|
|
|
|
|
|
|
|
bank.process_transaction(&transaction).unwrap();
|
|
|
|
|
|
|
|
let vote_accounts = bank.vote_accounts();
|
|
|
|
|
|
|
|
assert_eq!(vote_accounts.len(), 2);
|
|
|
|
|
|
|
|
assert!(vote_accounts.get(&vote_keypair.pubkey()).is_some());
|
|
|
|
|
|
|
|
assert!(bank.withdraw(&vote_keypair.pubkey(), 10).is_ok());
|
|
|
|
|
|
|
|
let vote_accounts = bank.vote_accounts();
|
|
|
|
|
|
|
|
assert_eq!(vote_accounts.len(), 1);
|
|
|
|
}
|
|
|
|
|
2019-04-10 15:26:23 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bank_0_votable() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, _) = create_genesis_block(500);
|
2019-04-10 15:26:23 -07:00
|
|
|
let bank = Arc::new(Bank::new(&genesis_block));
|
|
|
|
//set tick height to max
|
|
|
|
let max_tick_height = ((bank.slot + 1) * bank.ticks_per_slot - 1) as usize;
|
|
|
|
bank.tick_height.store(max_tick_height, Ordering::Relaxed);
|
|
|
|
assert!(bank.is_votable());
|
|
|
|
}
|
2019-04-23 15:32:19 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_is_delta_with_no_committables() {
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, mint_keypair) = create_genesis_block(8000);
|
2019-04-23 15:32:19 -07:00
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
bank.is_delta.store(false, Ordering::Relaxed);
|
|
|
|
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
let fail_tx = system_transaction::create_user_account(
|
|
|
|
&keypair1,
|
|
|
|
&keypair2.pubkey(),
|
|
|
|
1,
|
|
|
|
bank.last_blockhash(),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Should fail with TransactionError::AccountNotFound, which means
|
|
|
|
// the account which this tx operated on will not be committed. Thus
|
|
|
|
// the bank is_delta should still be false
|
|
|
|
assert_eq!(
|
|
|
|
bank.process_transaction(&fail_tx),
|
|
|
|
Err(TransactionError::AccountNotFound)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Check the bank is_delta is still false
|
|
|
|
assert!(!bank.is_delta.load(Ordering::Relaxed));
|
|
|
|
|
|
|
|
// Should fail with InstructionError, but InstructionErrors are committable,
|
|
|
|
// so is_delta should be true
|
|
|
|
assert_eq!(
|
|
|
|
bank.transfer(10_001, &mint_keypair, &Pubkey::new_rand()),
|
|
|
|
Err(TransactionError::InstructionError(
|
|
|
|
0,
|
|
|
|
InstructionError::new_result_with_negative_lamports(),
|
|
|
|
))
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(bank.is_delta.load(Ordering::Relaxed));
|
|
|
|
}
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|