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-01-06 22:06:55 -08:00
|
|
|
use crate::accounts::{Accounts, ErrorCounters, InstructionAccounts, InstructionLoaders};
|
2018-12-17 07:55:56 -08:00
|
|
|
use crate::checkpoint::Checkpoint;
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::counter::Counter;
|
|
|
|
use crate::entry::Entry;
|
2019-01-09 14:33:44 -08:00
|
|
|
use crate::entry::EntrySlice;
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::leader_scheduler::LeaderScheduler;
|
|
|
|
use crate::mint::Mint;
|
|
|
|
use crate::poh_recorder::PohRecorder;
|
|
|
|
use crate::runtime::{self, RuntimeError};
|
2018-12-17 12:41:23 -08:00
|
|
|
use crate::status_deque::{Status, StatusDeque, MAX_ENTRY_IDS};
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::storage_stage::StorageState;
|
2018-09-17 15:38:10 -07:00
|
|
|
use bincode::deserialize;
|
2018-06-29 11:58:29 -07:00
|
|
|
use itertools::Itertools;
|
2018-08-06 11:35:45 -07:00
|
|
|
use log::Level;
|
2018-10-16 12:09:48 -07:00
|
|
|
use rayon::prelude::*;
|
2018-12-08 09:25:57 -08:00
|
|
|
use solana_native_loader;
|
2018-11-23 19:42:01 -08:00
|
|
|
use solana_sdk::account::Account;
|
2018-12-03 13:32:31 -08:00
|
|
|
use solana_sdk::bpf_loader;
|
2018-12-04 14:38:19 -08:00
|
|
|
use solana_sdk::budget_program;
|
2018-12-17 12:41:23 -08:00
|
|
|
use solana_sdk::hash::Hash;
|
2018-12-03 12:26:23 -08:00
|
|
|
use solana_sdk::native_program::ProgramError;
|
2018-12-04 14:38:19 -08:00
|
|
|
use solana_sdk::payment_plan::Payment;
|
2018-10-25 11:13:08 -07:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2018-12-03 10:26:28 -08:00
|
|
|
use solana_sdk::signature::Keypair;
|
|
|
|
use solana_sdk::signature::Signature;
|
2018-12-04 08:20:41 -08:00
|
|
|
use solana_sdk::storage_program;
|
2018-11-16 08:04:46 -08:00
|
|
|
use solana_sdk::system_instruction::SystemInstruction;
|
2018-12-03 13:32:31 -08:00
|
|
|
use solana_sdk::system_program;
|
2018-12-04 20:19:48 -08:00
|
|
|
use solana_sdk::system_transaction::SystemTransaction;
|
2018-12-17 07:55:56 -08:00
|
|
|
use solana_sdk::timing::duration_as_us;
|
2018-12-03 22:30:52 -08:00
|
|
|
use solana_sdk::token_program;
|
2018-11-29 16:18:47 -08:00
|
|
|
use solana_sdk::transaction::Transaction;
|
2018-12-04 08:20:41 -08:00
|
|
|
use solana_sdk::vote_program;
|
2018-08-13 08:55:13 -07:00
|
|
|
use std;
|
2018-03-02 09:16:39 -08:00
|
|
|
use std::result;
|
2018-06-22 10:45:22 -07:00
|
|
|
use std::sync::atomic::{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
|
|
|
|
2018-06-06 08:49:22 -07:00
|
|
|
/// Reasons a transaction might be rejected.
|
2018-09-07 20:18:36 -07:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
2018-05-14 14:33:11 -07:00
|
|
|
pub enum BankError {
|
2018-10-04 13:15:54 -07:00
|
|
|
/// This Pubkey is being processed in another transaction
|
|
|
|
AccountInUse,
|
|
|
|
|
2018-08-09 08:13:57 -07:00
|
|
|
/// Attempt to debit from `Pubkey`, but no found no record of a prior credit.
|
2018-09-27 05:01:20 -07:00
|
|
|
AccountNotFound,
|
2018-06-06 08:49:22 -07:00
|
|
|
|
2018-09-17 13:36:31 -07:00
|
|
|
/// The from `Pubkey` does not have sufficient balance to pay the fee to schedule the transaction
|
2018-09-27 05:01:20 -07:00
|
|
|
InsufficientFundsForFee,
|
2018-06-06 08:49:22 -07:00
|
|
|
|
|
|
|
/// The bank has seen `Signature` before. This can occur under normal operation
|
|
|
|
/// when a UDP packet is duplicated, as a user error from a client not updating
|
|
|
|
/// its `last_id`, or as a double-spend attack.
|
2018-09-27 05:01:20 -07:00
|
|
|
DuplicateSignature,
|
2018-06-06 08:49:22 -07:00
|
|
|
|
|
|
|
/// The bank has not seen the given `last_id` or the transaction is too old and
|
|
|
|
/// the `last_id` has been discarded.
|
2018-09-27 05:01:20 -07:00
|
|
|
LastIdNotFound,
|
2018-06-06 08:49:22 -07:00
|
|
|
|
2018-06-29 11:58:29 -07:00
|
|
|
/// Proof of History verification failed.
|
|
|
|
LedgerVerificationFailed,
|
2018-11-11 19:50:03 -08:00
|
|
|
|
2018-11-23 14:53:39 -08:00
|
|
|
/// The program returned an error
|
|
|
|
ProgramError(u8, ProgramError),
|
2018-09-17 13:36:31 -07:00
|
|
|
|
2018-10-04 13:15:54 -07:00
|
|
|
/// Recoding into PoH failed
|
|
|
|
RecordFailure,
|
2018-10-16 09:43:49 -07:00
|
|
|
|
|
|
|
/// Loader call chain too deep
|
|
|
|
CallChainTooDeep,
|
2018-11-15 12:58:40 -08:00
|
|
|
|
|
|
|
/// Transaction has a fee but has no signature present
|
|
|
|
MissingSignatureForFee,
|
2018-03-02 09:16:39 -08:00
|
|
|
}
|
|
|
|
|
2018-05-14 14:33:11 -07:00
|
|
|
pub type Result<T> = result::Result<T, BankError>;
|
2018-12-17 07:55:56 -08:00
|
|
|
|
|
|
|
pub const VERIFY_BLOCK_SIZE: usize = 16;
|
2018-09-17 13:36:31 -07:00
|
|
|
|
2019-01-21 09:59:09 -08:00
|
|
|
pub trait BankSubscriptions {
|
|
|
|
fn check_account(&self, pubkey: &Pubkey, account: &Account);
|
|
|
|
fn check_signature(&self, signature: &Signature, status: &Result<()>);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct LocalSubscriptions {}
|
|
|
|
impl Default for LocalSubscriptions {
|
|
|
|
fn default() -> Self {
|
|
|
|
LocalSubscriptions {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BankSubscriptions for LocalSubscriptions {
|
|
|
|
fn check_account(&self, _pubkey: &Pubkey, _account: &Account) {}
|
|
|
|
fn check_signature(&self, _signature: &Signature, _status: &Result<()>) {}
|
|
|
|
}
|
|
|
|
|
2018-11-25 21:56:48 -08:00
|
|
|
/// Manager for the state of all accounts and programs after processing its entries.
|
2018-05-14 14:33:11 -07:00
|
|
|
pub struct Bank {
|
2018-12-17 12:41:23 -08:00
|
|
|
pub accounts: Accounts,
|
2018-10-04 13:15:54 -07:00
|
|
|
|
2018-10-17 16:28:26 -07:00
|
|
|
/// FIFO queue of `last_id` items
|
2018-12-17 07:55:56 -08:00
|
|
|
last_ids: RwLock<StatusDeque<Result<()>>>,
|
2018-06-06 08:49:22 -07:00
|
|
|
|
2018-12-20 15:47:48 -08:00
|
|
|
// The latest confirmation time for the network
|
|
|
|
confirmation_time: AtomicUsize,
|
2018-09-23 22:13:44 -07:00
|
|
|
|
2018-10-25 16:58:40 -07:00
|
|
|
/// Tracks and updates the leader schedule based on the votes and account stakes
|
|
|
|
/// processed by the bank
|
|
|
|
pub leader_scheduler: Arc<RwLock<LeaderScheduler>>,
|
2018-11-02 08:40:29 -07:00
|
|
|
|
|
|
|
pub storage_state: StorageState,
|
2019-01-21 09:59:09 -08:00
|
|
|
|
|
|
|
subscriptions: RwLock<Box<Arc<BankSubscriptions + Send + Sync>>>,
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
|
|
|
|
2018-06-29 11:58:29 -07:00
|
|
|
impl Default for Bank {
|
|
|
|
fn default() -> Self {
|
|
|
|
Bank {
|
2018-12-17 12:41:23 -08:00
|
|
|
accounts: Accounts::default(),
|
2018-12-17 07:55:56 -08:00
|
|
|
last_ids: RwLock::new(StatusDeque::default()),
|
2018-12-20 15:47:48 -08:00
|
|
|
confirmation_time: AtomicUsize::new(std::usize::MAX),
|
2018-10-25 16:58:40 -07:00
|
|
|
leader_scheduler: Arc::new(RwLock::new(LeaderScheduler::default())),
|
2018-11-02 08:40:29 -07:00
|
|
|
storage_state: StorageState::new(),
|
2019-01-21 09:59:09 -08:00
|
|
|
subscriptions: RwLock::new(Box::new(Arc::new(LocalSubscriptions::default()))),
|
2018-06-29 11:58:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Bank {
|
2018-10-23 14:44:41 -07:00
|
|
|
/// Create an Bank with built-in programs.
|
|
|
|
pub fn new_with_builtin_programs() -> Self {
|
|
|
|
let bank = Self::default();
|
|
|
|
bank.add_builtin_programs();
|
|
|
|
bank
|
|
|
|
}
|
|
|
|
|
2018-06-29 11:58:29 -07:00
|
|
|
/// Create an Bank using a deposit.
|
2018-11-02 14:32:05 -07:00
|
|
|
pub fn new_from_deposits(deposits: &[Payment]) -> Self {
|
2018-06-29 11:58:29 -07:00
|
|
|
let bank = Self::default();
|
2018-11-02 14:32:05 -07:00
|
|
|
for deposit in deposits {
|
2018-11-05 09:47:41 -08:00
|
|
|
let mut account = Account::default();
|
|
|
|
account.tokens += deposit.tokens;
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
bank.accounts.store_slow(&deposit.to, &account);
|
2018-09-07 20:18:36 -07:00
|
|
|
}
|
2018-10-23 14:44:41 -07:00
|
|
|
bank.add_builtin_programs();
|
2018-05-29 09:04:37 -07:00
|
|
|
bank
|
2018-04-02 12:51:44 -07:00
|
|
|
}
|
|
|
|
|
2019-01-21 09:59:09 -08:00
|
|
|
pub fn set_subscriptions(&self, subscriptions: Box<Arc<BankSubscriptions + Send + Sync>>) {
|
|
|
|
let mut sub = self.subscriptions.write().unwrap();
|
|
|
|
*sub = subscriptions
|
|
|
|
}
|
|
|
|
|
2018-11-05 09:47:41 -08:00
|
|
|
pub fn checkpoint(&self) {
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.checkpoint();
|
2018-11-05 09:47:41 -08:00
|
|
|
self.last_ids.write().unwrap().checkpoint();
|
|
|
|
}
|
2018-11-05 16:43:27 -08:00
|
|
|
pub fn purge(&self, depth: usize) {
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.purge(depth);
|
2018-11-05 16:43:27 -08:00
|
|
|
self.last_ids.write().unwrap().purge(depth);
|
|
|
|
}
|
|
|
|
|
2018-11-05 09:47:41 -08:00
|
|
|
pub fn rollback(&self) {
|
2018-12-17 12:41:23 -08:00
|
|
|
let rolled_back_pubkeys: Vec<Pubkey> = self.accounts.keys();
|
|
|
|
self.accounts.rollback();
|
2018-11-05 09:47:41 -08:00
|
|
|
|
|
|
|
rolled_back_pubkeys.iter().for_each(|pubkey| {
|
2018-12-17 12:41:23 -08:00
|
|
|
if let Some(account) = self.accounts.load_slow(&pubkey) {
|
2019-01-21 09:59:09 -08:00
|
|
|
self.subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.check_account(&pubkey, &account)
|
2018-11-05 09:47:41 -08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
self.last_ids.write().unwrap().rollback();
|
|
|
|
}
|
|
|
|
pub fn checkpoint_depth(&self) -> usize {
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.depth()
|
2018-11-05 09:47:41 -08:00
|
|
|
}
|
|
|
|
|
2018-05-14 14:33:11 -07:00
|
|
|
/// Create an Bank with only a Mint. Typically used by unit tests.
|
2018-04-02 13:41:07 -07:00
|
|
|
pub fn new(mint: &Mint) -> Self {
|
2018-11-02 14:32:05 -07:00
|
|
|
let mint_tokens = if mint.bootstrap_leader_id != Pubkey::default() {
|
|
|
|
mint.tokens - mint.bootstrap_leader_tokens
|
|
|
|
} else {
|
|
|
|
mint.tokens
|
|
|
|
};
|
|
|
|
|
|
|
|
let mint_deposit = Payment {
|
2018-04-02 12:51:44 -07:00
|
|
|
to: mint.pubkey(),
|
2018-11-02 14:32:05 -07:00
|
|
|
tokens: mint_tokens,
|
2018-04-02 12:51:44 -07:00
|
|
|
};
|
2018-11-02 14:32:05 -07:00
|
|
|
|
|
|
|
let deposits = if mint.bootstrap_leader_id != Pubkey::default() {
|
|
|
|
let leader_deposit = Payment {
|
|
|
|
to: mint.bootstrap_leader_id,
|
|
|
|
tokens: mint.bootstrap_leader_tokens,
|
|
|
|
};
|
|
|
|
vec![mint_deposit, leader_deposit]
|
|
|
|
} else {
|
|
|
|
vec![mint_deposit]
|
|
|
|
};
|
|
|
|
let bank = Self::new_from_deposits(&deposits);
|
2018-11-05 09:47:41 -08:00
|
|
|
bank.register_tick(&mint.last_id());
|
2018-05-14 14:33:11 -07:00
|
|
|
bank
|
2018-04-02 12:51:44 -07:00
|
|
|
}
|
|
|
|
|
2018-12-03 13:32:31 -08:00
|
|
|
fn add_system_program(&self) {
|
|
|
|
let system_program_account = Account {
|
|
|
|
tokens: 1,
|
|
|
|
owner: system_program::id(),
|
|
|
|
userdata: b"solana_system_program".to_vec(),
|
|
|
|
executable: true,
|
2018-12-08 09:25:57 -08:00
|
|
|
loader: solana_native_loader::id(),
|
2018-12-03 13:32:31 -08:00
|
|
|
};
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts
|
|
|
|
.store_slow(&system_program::id(), &system_program_account);
|
2018-12-03 13:32:31 -08:00
|
|
|
}
|
|
|
|
|
2018-12-04 08:20:41 -08:00
|
|
|
fn add_builtin_programs(&self) {
|
|
|
|
self.add_system_program();
|
2018-12-04 07:45:32 -08:00
|
|
|
|
2018-12-04 08:20:41 -08:00
|
|
|
// Vote program
|
2018-12-04 07:45:32 -08:00
|
|
|
let vote_program_account = Account {
|
|
|
|
tokens: 1,
|
|
|
|
owner: vote_program::id(),
|
|
|
|
userdata: b"solana_vote_program".to_vec(),
|
|
|
|
executable: true,
|
2018-12-08 09:25:57 -08:00
|
|
|
loader: solana_native_loader::id(),
|
2018-12-04 07:45:32 -08:00
|
|
|
};
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts
|
|
|
|
.store_slow(&vote_program::id(), &vote_program_account);
|
2018-12-04 07:45:32 -08:00
|
|
|
|
2018-12-04 08:20:41 -08:00
|
|
|
// Storage program
|
|
|
|
let storage_program_account = Account {
|
|
|
|
tokens: 1,
|
|
|
|
owner: storage_program::id(),
|
|
|
|
userdata: b"solana_storage_program".to_vec(),
|
|
|
|
executable: true,
|
2018-12-08 09:25:57 -08:00
|
|
|
loader: solana_native_loader::id(),
|
2018-12-04 08:20:41 -08:00
|
|
|
};
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts
|
|
|
|
.store_slow(&storage_program::id(), &storage_program_account);
|
2018-11-02 17:32:54 -07:00
|
|
|
|
2019-01-17 14:41:48 -08:00
|
|
|
let storage_system_account = Account {
|
|
|
|
tokens: 1,
|
|
|
|
owner: storage_program::system_id(),
|
|
|
|
userdata: vec![0; 16 * 1024],
|
|
|
|
executable: false,
|
|
|
|
loader: Pubkey::default(),
|
|
|
|
};
|
|
|
|
self.accounts
|
|
|
|
.store_slow(&storage_program::system_id(), &storage_system_account);
|
|
|
|
|
2018-12-03 13:32:31 -08:00
|
|
|
// Bpf Loader
|
|
|
|
let bpf_loader_account = Account {
|
|
|
|
tokens: 1,
|
|
|
|
owner: bpf_loader::id(),
|
|
|
|
userdata: b"solana_bpf_loader".to_vec(),
|
|
|
|
executable: true,
|
2018-12-08 09:25:57 -08:00
|
|
|
loader: solana_native_loader::id(),
|
2018-12-03 13:32:31 -08:00
|
|
|
};
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts
|
|
|
|
.store_slow(&bpf_loader::id(), &bpf_loader_account);
|
2018-12-03 13:32:31 -08:00
|
|
|
|
2018-12-04 14:38:19 -08:00
|
|
|
// Budget program
|
|
|
|
let budget_program_account = Account {
|
|
|
|
tokens: 1,
|
|
|
|
owner: budget_program::id(),
|
|
|
|
userdata: b"solana_budget_program".to_vec(),
|
|
|
|
executable: true,
|
2018-12-08 09:25:57 -08:00
|
|
|
loader: solana_native_loader::id(),
|
2018-12-04 14:38:19 -08:00
|
|
|
};
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts
|
|
|
|
.store_slow(&budget_program::id(), &budget_program_account);
|
2018-12-04 14:38:19 -08:00
|
|
|
|
2018-12-03 13:32:31 -08:00
|
|
|
// Erc20 token program
|
|
|
|
let erc20_account = Account {
|
|
|
|
tokens: 1,
|
2018-12-03 22:30:52 -08:00
|
|
|
owner: token_program::id(),
|
2018-12-03 13:32:31 -08:00
|
|
|
userdata: b"solana_erc20".to_vec(),
|
|
|
|
executable: true,
|
2018-12-08 09:25:57 -08:00
|
|
|
loader: solana_native_loader::id(),
|
2018-12-03 13:32:31 -08:00
|
|
|
};
|
2018-10-23 14:44:41 -07:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts
|
|
|
|
.store_slow(&token_program::id(), &erc20_account);
|
2018-06-07 19:35:38 -07:00
|
|
|
}
|
|
|
|
|
2018-06-06 08:49:22 -07:00
|
|
|
/// Return the last entry ID registered.
|
2018-05-03 12:24:35 -07:00
|
|
|
pub fn last_id(&self) -> Hash {
|
2018-10-17 16:28:26 -07:00
|
|
|
self.last_ids
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
2018-11-05 09:47:41 -08:00
|
|
|
.last_id
|
2018-10-17 16:28:26 -07:00
|
|
|
.expect("no last_id has been set")
|
2018-05-03 12:24:35 -07:00
|
|
|
}
|
|
|
|
|
2018-12-10 11:38:29 -08:00
|
|
|
pub fn get_pubkeys_for_entry_height(&self, entry_height: u64) -> Vec<Pubkey> {
|
|
|
|
self.storage_state
|
|
|
|
.get_pubkeys_for_entry_height(entry_height)
|
|
|
|
}
|
|
|
|
|
2019-01-17 14:41:48 -08:00
|
|
|
pub fn get_storage_entry_height(&self) -> u64 {
|
|
|
|
match self.get_account(&storage_program::system_id()) {
|
|
|
|
Some(storage_system_account) => {
|
|
|
|
let state = deserialize(&storage_system_account.userdata);
|
|
|
|
if let Ok(state) = state {
|
|
|
|
let state: storage_program::StorageProgramState = state;
|
|
|
|
return state.entry_height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
info!("error in reading entry_height");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_storage_last_id(&self) -> Hash {
|
|
|
|
if let Some(storage_system_account) = self.get_account(&storage_program::system_id()) {
|
|
|
|
let state = deserialize(&storage_system_account.userdata);
|
|
|
|
if let Ok(state) = state {
|
|
|
|
let state: storage_program::StorageProgramState = state;
|
|
|
|
return state.id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Hash::default()
|
|
|
|
}
|
|
|
|
|
2018-07-10 14:19:42 -07:00
|
|
|
/// Forget all signatures. Useful for benchmarking.
|
|
|
|
pub fn clear_signatures(&self) {
|
2018-12-17 07:55:56 -08:00
|
|
|
self.last_ids.write().unwrap().clear_signatures();
|
2018-09-21 15:07:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn update_transaction_statuses(&self, txs: &[Transaction], res: &[Result<()>]) {
|
2018-10-17 16:28:26 -07:00
|
|
|
let mut last_ids = self.last_ids.write().unwrap();
|
2018-09-21 15:07:29 -07:00
|
|
|
for (i, tx) in txs.iter().enumerate() {
|
2018-12-17 07:55:56 -08:00
|
|
|
last_ids.update_signature_status_with_last_id(&tx.signatures[0], &res[i], &tx.last_id);
|
2019-01-21 09:59:09 -08:00
|
|
|
self.subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.check_signature(&tx.signatures[0], &res[i]);
|
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,
|
2018-11-05 08:36:22 -08:00
|
|
|
ticks_and_stakes: &mut [(u64, u64)],
|
|
|
|
supermajority_stake: u64,
|
2018-11-02 15:49:14 -07:00
|
|
|
) -> Option<u64> {
|
|
|
|
let last_ids = self.last_ids.read().unwrap();
|
2018-12-20 15:47:48 -08:00
|
|
|
last_ids.get_confirmation_timestamp(ticks_and_stakes, supermajority_stake)
|
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
|
2018-05-14 14:33:11 -07:00
|
|
|
/// bank will reject transactions using that `last_id`.
|
2018-11-05 09:47:41 -08:00
|
|
|
pub fn register_tick(&self, last_id: &Hash) {
|
2018-10-17 16:28:26 -07:00
|
|
|
let mut last_ids = self.last_ids.write().unwrap();
|
2018-11-05 09:47:41 -08:00
|
|
|
inc_new_counter_info!("bank-register_tick-registered", 1);
|
2018-12-17 07:55:56 -08:00
|
|
|
last_ids.register_tick(last_id)
|
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()];
|
|
|
|
match self.process_transactions(&txs)[0] {
|
2018-09-07 20:18:36 -07:00
|
|
|
Err(ref e) => {
|
|
|
|
info!("process_transaction error: {:?}", e);
|
|
|
|
Err((*e).clone())
|
|
|
|
}
|
|
|
|
Ok(_) => Ok(()),
|
|
|
|
}
|
|
|
|
}
|
2018-11-02 15:49:14 -07:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
fn lock_accounts(&self, txs: &[Transaction]) -> Vec<Result<()>> {
|
|
|
|
self.accounts.lock_accounts(txs)
|
|
|
|
}
|
2019-01-04 16:39:04 -08:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
fn unlock_accounts(&self, txs: &[Transaction], results: &[Result<()>]) {
|
|
|
|
self.accounts.unlock_accounts(txs, results)
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
|
|
|
|
2018-10-04 13:15:54 -07:00
|
|
|
pub fn process_and_record_transactions(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
poh: &PohRecorder,
|
|
|
|
) -> Result<()> {
|
|
|
|
let now = Instant::now();
|
|
|
|
// Once accounts are locked, other threads cannot encode transactions that will modify the
|
|
|
|
// same account state
|
2019-01-06 22:06:55 -08:00
|
|
|
let lock_results = self.lock_accounts(txs);
|
2018-10-04 13:15:54 -07:00
|
|
|
let lock_time = now.elapsed();
|
2019-01-21 10:17:04 -08:00
|
|
|
|
2018-10-04 13:15:54 -07:00
|
|
|
let now = Instant::now();
|
2018-10-10 17:23:06 -07:00
|
|
|
// Use a shorter maximum age when adding transactions into the pipeline. This will reduce
|
2019-01-04 16:04:31 -08:00
|
|
|
// the likelihood of any single thread getting starved and processing old ids.
|
2018-10-16 09:43:49 -07:00
|
|
|
// TODO: Banking stage threads should be prioritized to complete faster then this queue
|
2018-10-10 17:23:06 -07:00
|
|
|
// expires.
|
2019-01-21 10:17:04 -08:00
|
|
|
let (loaded_accounts, results) =
|
|
|
|
self.load_and_execute_transactions(txs, lock_results, MAX_ENTRY_IDS as usize / 2);
|
|
|
|
let load_execute_time = now.elapsed();
|
|
|
|
|
|
|
|
let record_time = {
|
|
|
|
let now = Instant::now();
|
|
|
|
self.record_transactions(txs, &results, poh)?;
|
|
|
|
now.elapsed()
|
|
|
|
};
|
|
|
|
|
|
|
|
let commit_time = {
|
|
|
|
let now = Instant::now();
|
|
|
|
self.commit_transactions(txs, &loaded_accounts, &results);
|
|
|
|
now.elapsed()
|
|
|
|
};
|
|
|
|
|
2018-10-04 13:15:54 -07:00
|
|
|
let now = Instant::now();
|
2019-01-06 22:06:55 -08:00
|
|
|
// Once the accounts are new transactions can enter the pipeline to process them
|
2018-10-04 13:15:54 -07:00
|
|
|
self.unlock_accounts(&txs, &results);
|
|
|
|
let unlock_time = now.elapsed();
|
|
|
|
debug!(
|
2019-01-21 10:17:04 -08:00
|
|
|
"lock: {}us load_execute: {}us record: {}us commit: {}us unlock: {}us txs_len: {}",
|
2018-10-04 13:15:54 -07:00
|
|
|
duration_as_us(&lock_time),
|
2019-01-21 10:17:04 -08:00
|
|
|
duration_as_us(&load_execute_time),
|
2018-10-04 13:15:54 -07:00
|
|
|
duration_as_us(&record_time),
|
2019-01-21 10:17:04 -08:00
|
|
|
duration_as_us(&commit_time),
|
2018-10-04 13:15:54 -07:00
|
|
|
duration_as_us(&unlock_time),
|
|
|
|
txs.len(),
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn record_transactions(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
results: &[Result<()>],
|
|
|
|
poh: &PohRecorder,
|
|
|
|
) -> Result<()> {
|
|
|
|
let processed_transactions: Vec<_> = results
|
|
|
|
.iter()
|
|
|
|
.zip(txs.iter())
|
|
|
|
.filter_map(|(r, x)| match r {
|
2019-01-24 13:37:12 -08:00
|
|
|
Ok(_) => Some(x.clone()),
|
|
|
|
Err(BankError::ProgramError(index, err)) => {
|
|
|
|
info!("program error {:?}, {:?}", index, err);
|
|
|
|
Some(x.clone())
|
|
|
|
}
|
2018-10-04 13:15:54 -07:00
|
|
|
Err(ref e) => {
|
|
|
|
debug!("process transaction failed {:?}", e);
|
|
|
|
None
|
|
|
|
}
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.collect();
|
2019-01-21 10:17:04 -08:00
|
|
|
debug!("processed: {} ", processed_transactions.len());
|
2018-10-04 13:15:54 -07:00
|
|
|
// unlock all the accounts with errors which are filtered by the above `filter_map`
|
|
|
|
if !processed_transactions.is_empty() {
|
|
|
|
let hash = Transaction::hash(&processed_transactions);
|
|
|
|
// record and unlock will unlock all the successfull transactions
|
|
|
|
poh.record(hash, processed_transactions).map_err(|e| {
|
|
|
|
warn!("record failure: {:?}", e);
|
|
|
|
BankError::RecordFailure
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-01-04 16:04:31 -08:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
fn load_accounts(
|
|
|
|
&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-06 22:06:55 -08:00
|
|
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>> {
|
2018-12-17 12:41:23 -08:00
|
|
|
let mut last_ids = self.last_ids.write().unwrap();
|
|
|
|
self.accounts
|
2019-01-06 22:06:55 -08:00
|
|
|
.load_accounts(txs, &mut last_ids, lock_results, max_age, error_counters)
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2018-10-04 13:15:54 -07:00
|
|
|
|
2019-01-21 10:17:04 -08:00
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
fn load_and_execute_transactions(
|
2018-10-04 13:15:54 -07:00
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
2019-01-06 22:06:55 -08:00
|
|
|
lock_results: Vec<Result<()>>,
|
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();
|
2018-10-10 17:23:06 -07:00
|
|
|
let mut loaded_accounts =
|
2019-01-06 22:06:55 -08:00
|
|
|
self.load_accounts(txs, lock_results, max_age, &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();
|
2018-10-04 13:15:54 -07:00
|
|
|
let executed: Vec<Result<()>> = loaded_accounts
|
2018-09-07 20:18:36 -07:00
|
|
|
.iter_mut()
|
2018-09-21 14:48:10 -07:00
|
|
|
.zip(txs.iter())
|
2019-01-06 22:06:55 -08:00
|
|
|
.map(|(accs, tx)| match accs {
|
2018-09-07 20:18:36 -07:00
|
|
|
Err(e) => Err(e.clone()),
|
2019-01-06 22:06:55 -08:00
|
|
|
Ok((ref mut accounts, ref mut loaders)) => {
|
|
|
|
runtime::execute_transaction(tx, loaders, accounts, tick_height).map_err(
|
2018-11-26 22:25:02 -08:00
|
|
|
|RuntimeError::ProgramError(index, err)| {
|
|
|
|
BankError::ProgramError(index, err)
|
|
|
|
},
|
|
|
|
)
|
2018-11-26 21:44:10 -08:00
|
|
|
}
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.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
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.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);
|
2018-10-10 17:23:06 -07:00
|
|
|
if 0 != error_counters.last_id_not_found {
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-process_transactions-error-last_id_not_found",
|
|
|
|
error_counters.last_id_not_found
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if 0 != error_counters.reserve_last_id {
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-process_transactions-error-reserve_last_id",
|
|
|
|
error_counters.reserve_last_id
|
|
|
|
);
|
|
|
|
}
|
|
|
|
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-01-21 10:17:04 -08:00
|
|
|
(loaded_accounts, executed)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn commit_transactions(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
loaded_accounts: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
|
|
|
executed: &[Result<()>],
|
|
|
|
) {
|
|
|
|
let now = Instant::now();
|
|
|
|
self.accounts.store_accounts(txs, executed, loaded_accounts);
|
|
|
|
|
|
|
|
// Check account subscriptions and send notifications
|
|
|
|
self.send_account_notifications(txs, executed, loaded_accounts);
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Process a batch of transactions.
|
|
|
|
#[must_use]
|
|
|
|
pub fn load_execute_and_commit_transactions(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
lock_results: Vec<Result<()>>,
|
|
|
|
max_age: usize,
|
|
|
|
) -> Vec<Result<()>> {
|
|
|
|
let (loaded_accounts, executed) =
|
|
|
|
self.load_and_execute_transactions(txs, lock_results, max_age);
|
|
|
|
|
|
|
|
self.commit_transactions(txs, &loaded_accounts, &executed);
|
2018-10-04 13:15:54 -07:00
|
|
|
executed
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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-01-21 10:17:04 -08:00
|
|
|
let results = self.load_execute_and_commit_transactions(txs, lock_results, MAX_ENTRY_IDS);
|
2018-10-04 13:15:54 -07:00
|
|
|
self.unlock_accounts(txs, &results);
|
|
|
|
results
|
2018-04-05 21:13:54 -07:00
|
|
|
}
|
|
|
|
|
2018-10-25 16:58:40 -07:00
|
|
|
pub fn process_entry(&self, entry: &Entry) -> Result<()> {
|
2018-10-18 22:57:48 -07:00
|
|
|
if !entry.is_tick() {
|
2018-09-21 14:48:10 -07:00
|
|
|
for result in self.process_transactions(&entry.transactions) {
|
2019-01-18 17:16:17 -08:00
|
|
|
match result {
|
|
|
|
// Entries that result in a ProgramError are still valid and are written in the
|
|
|
|
// ledger so map them to an ok return value
|
|
|
|
Err(BankError::ProgramError(_, _)) => Ok(()),
|
|
|
|
_ => result,
|
|
|
|
}?;
|
2018-07-11 10:30:07 -07:00
|
|
|
}
|
2018-10-10 17:23:06 -07:00
|
|
|
} else {
|
2018-11-05 09:47:41 -08:00
|
|
|
self.register_tick(&entry.id);
|
2018-10-25 16:58:40 -07:00
|
|
|
self.leader_scheduler
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
2018-11-05 09:47:41 -08:00
|
|
|
.update_height(self.tick_height(), self);
|
2018-07-11 10:30:07 -07:00
|
|
|
}
|
2018-10-18 22:57:48 -07:00
|
|
|
|
2018-07-11 10:30:07 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2018-06-06 08:49:22 -07:00
|
|
|
/// Process an ordered list of entries.
|
2018-09-24 12:26:47 -07:00
|
|
|
pub fn process_entries(&self, entries: &[Entry]) -> Result<()> {
|
2018-10-16 12:09:48 -07:00
|
|
|
self.par_process_entries(entries)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn first_err(results: &[Result<()>]) -> Result<()> {
|
|
|
|
for r in results {
|
|
|
|
r.clone()?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-01-24 12:46:40 -08:00
|
|
|
|
2019-01-24 13:37:12 -08:00
|
|
|
fn ignore_program_errors(results: Vec<Result<()>>) -> Vec<Result<()>> {
|
|
|
|
results
|
|
|
|
.into_iter()
|
|
|
|
.map(|result| match result {
|
|
|
|
// Entries that result in a ProgramError are still valid and are written in the
|
|
|
|
// ledger so map them to an ok return value
|
|
|
|
Err(BankError::ProgramError(index, err)) => {
|
|
|
|
info!("program error {:?}, {:?}", index, err);
|
|
|
|
inc_new_counter_info!("bank-ignore_program_err", 1);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
_ => result,
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2019-01-24 12:46:40 -08:00
|
|
|
fn par_execute_entries(&self, entries: &[(&Entry, Vec<Result<()>>)]) -> Result<()> {
|
2018-10-16 12:09:48 -07:00
|
|
|
inc_new_counter_info!("bank-par_execute_entries-count", entries.len());
|
|
|
|
let results: Vec<Result<()>> = entries
|
|
|
|
.into_par_iter()
|
2019-01-06 22:06:55 -08:00
|
|
|
.map(|(e, lock_results)| {
|
2019-01-21 15:26:06 -08:00
|
|
|
let old_results = self.load_execute_and_commit_transactions(
|
2018-10-16 12:09:48 -07:00
|
|
|
&e.transactions,
|
2019-01-06 22:06:55 -08:00
|
|
|
lock_results.to_vec(),
|
2018-10-16 12:09:48 -07:00
|
|
|
MAX_ENTRY_IDS,
|
|
|
|
);
|
2019-01-24 13:37:12 -08:00
|
|
|
let results = Bank::ignore_program_errors(old_results);
|
2018-10-16 12:09:48 -07:00
|
|
|
self.unlock_accounts(&e.transactions, &results);
|
|
|
|
Self::first_err(&results)
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.collect();
|
2018-10-16 12:09:48 -07:00
|
|
|
Self::first_err(&results)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// process entries in parallel
|
|
|
|
/// 1. In order lock accounts for each entry while the lock succeeds, up to a Tick entry
|
|
|
|
/// 2. Process the locked group in parallel
|
|
|
|
/// 3. Register the `Tick` if it's available, goto 1
|
|
|
|
pub fn par_process_entries(&self, entries: &[Entry]) -> Result<()> {
|
|
|
|
// accumulator for entries that can be processed in parallel
|
|
|
|
let mut mt_group = vec![];
|
2018-05-10 13:01:42 -07:00
|
|
|
for entry in entries {
|
2018-10-18 22:57:48 -07:00
|
|
|
if entry.is_tick() {
|
2018-10-16 12:09:48 -07:00
|
|
|
// if its a tick, execute the group and register the tick
|
|
|
|
self.par_execute_entries(&mt_group)?;
|
2018-11-05 09:47:41 -08:00
|
|
|
self.register_tick(&entry.id);
|
2018-12-18 16:06:05 -08:00
|
|
|
self.leader_scheduler
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.update_height(self.tick_height(), self);
|
2018-10-16 12:09:48 -07:00
|
|
|
mt_group = vec![];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// try to lock the accounts
|
2019-01-06 22:06:55 -08:00
|
|
|
let lock_results = self.lock_accounts(&entry.transactions);
|
2018-10-16 12:09:48 -07:00
|
|
|
// if any of the locks error out
|
|
|
|
// execute the current group
|
2019-01-06 22:06:55 -08:00
|
|
|
if Self::first_err(&lock_results).is_err() {
|
2018-10-16 12:09:48 -07:00
|
|
|
self.par_execute_entries(&mt_group)?;
|
|
|
|
mt_group = vec![];
|
|
|
|
//reset the lock and push the entry
|
2019-01-06 22:06:55 -08:00
|
|
|
self.unlock_accounts(&entry.transactions, &lock_results);
|
|
|
|
let lock_results = self.lock_accounts(&entry.transactions);
|
|
|
|
mt_group.push((entry, lock_results));
|
2018-10-16 12:09:48 -07:00
|
|
|
} else {
|
|
|
|
// push the entry to the mt_group
|
2019-01-06 22:06:55 -08:00
|
|
|
mt_group.push((entry, lock_results));
|
2018-10-16 12:09:48 -07:00
|
|
|
}
|
2018-05-10 13:01:42 -07:00
|
|
|
}
|
2018-10-16 12:09:48 -07:00
|
|
|
self.par_execute_entries(&mt_group)?;
|
2018-09-03 02:48:11 -07:00
|
|
|
Ok(())
|
2018-05-10 13:01:42 -07:00
|
|
|
}
|
|
|
|
|
2018-10-30 10:05:18 -07:00
|
|
|
/// Process an ordered list of entries, populating a circular buffer "tail"
|
|
|
|
/// as we go.
|
|
|
|
fn process_block(&self, entries: &[Entry]) -> Result<()> {
|
|
|
|
for entry in entries {
|
|
|
|
self.process_entry(entry)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2018-06-29 11:58:29 -07:00
|
|
|
/// Append entry blocks to the ledger, verifying them along the way.
|
2018-10-30 10:05:18 -07:00
|
|
|
fn process_ledger_blocks<I>(
|
2018-07-10 11:22:45 -07:00
|
|
|
&self,
|
2018-08-07 11:09:33 -07:00
|
|
|
start_hash: Hash,
|
2018-10-30 10:05:18 -07:00
|
|
|
entry_height: u64,
|
2018-07-10 11:22:45 -07:00
|
|
|
entries: I,
|
2018-10-30 10:05:18 -07:00
|
|
|
) -> Result<(u64, Hash)>
|
2018-06-29 11:58:29 -07:00
|
|
|
where
|
|
|
|
I: IntoIterator<Item = Entry>,
|
|
|
|
{
|
2018-10-30 10:05:18 -07:00
|
|
|
// these magic numbers are from genesis of the mint, could pull them
|
|
|
|
// back out of this loop.
|
|
|
|
let mut entry_height = entry_height;
|
|
|
|
let mut last_id = start_hash;
|
|
|
|
|
2018-06-29 11:58:29 -07:00
|
|
|
// Ledger verification needs to be parallelized, but we can't pull the whole
|
|
|
|
// thing into memory. We therefore chunk it.
|
|
|
|
for block in &entries.into_iter().chunks(VERIFY_BLOCK_SIZE) {
|
|
|
|
let block: Vec<_> = block.collect();
|
2018-10-30 10:05:18 -07:00
|
|
|
|
|
|
|
if !block.verify(&last_id) {
|
2018-10-18 22:57:48 -07:00
|
|
|
warn!("Ledger proof of history failed at entry: {}", entry_height);
|
2018-06-29 11:58:29 -07:00
|
|
|
return Err(BankError::LedgerVerificationFailed);
|
|
|
|
}
|
2018-10-10 16:49:41 -07:00
|
|
|
|
2018-10-30 10:05:18 -07:00
|
|
|
self.process_block(&block)?;
|
|
|
|
|
|
|
|
last_id = block.last().unwrap().id;
|
|
|
|
entry_height += block.len() as u64;
|
2018-06-29 11:58:29 -07:00
|
|
|
}
|
2018-10-30 10:05:18 -07:00
|
|
|
Ok((entry_height, last_id))
|
2018-06-29 11:58:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Process a full ledger.
|
2018-10-30 10:05:18 -07:00
|
|
|
pub fn process_ledger<I>(&self, entries: I) -> Result<(u64, Hash)>
|
2018-06-29 11:58:29 -07:00
|
|
|
where
|
|
|
|
I: IntoIterator<Item = Entry>,
|
|
|
|
{
|
|
|
|
let mut entries = entries.into_iter();
|
|
|
|
|
|
|
|
// The first item in the ledger is required to be an entry with zero num_hashes,
|
|
|
|
// which implies its id can be used as the ledger's seed.
|
|
|
|
let entry0 = entries.next().expect("invalid ledger: empty");
|
|
|
|
|
2018-11-02 14:32:05 -07:00
|
|
|
// The second item in the ledger consists of a transaction with
|
|
|
|
// two special instructions:
|
|
|
|
// 1) The first is a special move instruction where the to and from
|
2018-06-29 11:58:29 -07:00
|
|
|
// fields are the same. That entry should be treated as a deposit, not a
|
|
|
|
// transfer to oneself.
|
2018-11-02 14:32:05 -07:00
|
|
|
// 2) The second is a move instruction that acts as a payment to the first
|
|
|
|
// leader from the mint. This bootstrap leader will stay in power during the
|
|
|
|
// bootstrapping period of the network
|
2018-06-29 11:58:29 -07:00
|
|
|
let entry1 = entries
|
|
|
|
.next()
|
|
|
|
.expect("invalid ledger: need at least 2 entries");
|
2018-11-05 09:47:41 -08:00
|
|
|
|
|
|
|
// genesis should conform to PoH
|
|
|
|
assert!(entry1.verify(&entry0.id));
|
|
|
|
|
2018-07-02 10:07:32 -07:00
|
|
|
{
|
2018-11-02 14:32:05 -07:00
|
|
|
// Process the first transaction
|
2018-07-02 10:07:32 -07:00
|
|
|
let tx = &entry1.transactions[0];
|
2018-11-23 12:45:34 -08:00
|
|
|
assert!(system_program::check_id(tx.program_id(0)), "Invalid ledger");
|
|
|
|
assert!(system_program::check_id(tx.program_id(1)), "Invalid ledger");
|
2018-11-16 08:04:46 -08:00
|
|
|
let mut instruction: SystemInstruction = deserialize(tx.userdata(0)).unwrap();
|
|
|
|
let mint_deposit = if let SystemInstruction::Move { tokens } = instruction {
|
2018-09-17 15:38:10 -07:00
|
|
|
Some(tokens)
|
2018-07-02 10:07:32 -07:00
|
|
|
} else {
|
|
|
|
None
|
2018-12-07 19:01:28 -08:00
|
|
|
}
|
|
|
|
.expect("invalid ledger, needs to start with mint deposit");
|
2018-11-02 14:32:05 -07:00
|
|
|
|
|
|
|
instruction = deserialize(tx.userdata(1)).unwrap();
|
2018-11-16 08:04:46 -08:00
|
|
|
let leader_payment = if let SystemInstruction::Move { tokens } = instruction {
|
2018-11-02 14:32:05 -07:00
|
|
|
Some(tokens)
|
|
|
|
} else {
|
|
|
|
None
|
2018-12-07 19:01:28 -08:00
|
|
|
}
|
|
|
|
.expect("invalid ledger, bootstrap leader payment expected");
|
2018-11-02 14:32:05 -07:00
|
|
|
|
|
|
|
assert!(leader_payment <= mint_deposit);
|
|
|
|
assert!(leader_payment > 0);
|
|
|
|
|
2018-09-07 20:18:36 -07:00
|
|
|
{
|
2018-11-02 14:32:05 -07:00
|
|
|
// 1) Deposit into the mint
|
2018-12-17 12:41:23 -08:00
|
|
|
let mut account = self
|
|
|
|
.accounts
|
|
|
|
.load_slow(&tx.account_keys[0])
|
2018-11-05 09:47:41 -08:00
|
|
|
.unwrap_or_default();
|
|
|
|
account.tokens += mint_deposit - leader_payment;
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.store_slow(&tx.account_keys[0], &account);
|
2018-11-05 09:47:41 -08:00
|
|
|
trace!(
|
|
|
|
"applied genesis payment {:?} => {:?}",
|
|
|
|
mint_deposit - leader_payment,
|
|
|
|
account
|
|
|
|
);
|
2018-11-02 14:32:05 -07:00
|
|
|
|
|
|
|
// 2) Transfer tokens to the bootstrap leader. The first two
|
|
|
|
// account keys will both be the mint (because the mint is the source
|
2019-01-22 09:47:43 -08:00
|
|
|
// for this transaction and the first move instruction is to the
|
2018-11-02 14:32:05 -07:00
|
|
|
// mint itself), so we look at the third account key to find the first
|
|
|
|
// leader id.
|
|
|
|
let bootstrap_leader_id = tx.account_keys[2];
|
2018-12-17 12:41:23 -08:00
|
|
|
let mut account = self
|
|
|
|
.accounts
|
|
|
|
.load_slow(&bootstrap_leader_id)
|
2018-11-05 09:47:41 -08:00
|
|
|
.unwrap_or_default();
|
2018-11-02 14:32:05 -07:00
|
|
|
account.tokens += leader_payment;
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.store_slow(&bootstrap_leader_id, &account);
|
2018-11-05 09:47:41 -08:00
|
|
|
|
2018-11-02 14:32:05 -07:00
|
|
|
self.leader_scheduler.write().unwrap().bootstrap_leader = bootstrap_leader_id;
|
2018-11-05 09:47:41 -08:00
|
|
|
|
2018-11-02 14:32:05 -07:00
|
|
|
trace!(
|
|
|
|
"applied genesis payment to bootstrap leader {:?} => {:?}",
|
|
|
|
leader_payment,
|
|
|
|
account
|
|
|
|
);
|
2018-09-07 20:18:36 -07:00
|
|
|
}
|
2018-07-02 10:07:32 -07:00
|
|
|
}
|
2018-10-10 16:49:41 -07:00
|
|
|
|
2018-10-30 10:05:18 -07:00
|
|
|
Ok(self.process_ledger_blocks(entry1.id, 2, entries)?)
|
2018-06-29 11:58:29 -07:00
|
|
|
}
|
|
|
|
|
2018-03-29 11:20:54 -07:00
|
|
|
/// Create, sign, and process a Transaction from `keypair` to `to` of
|
|
|
|
/// `n` tokens where `last_id` is the last Entry ID observed by the client.
|
2018-02-23 13:08:19 -08:00
|
|
|
pub fn transfer(
|
2018-04-04 11:33:00 -07:00
|
|
|
&self,
|
2018-11-05 08:36:22 -08:00
|
|
|
n: u64,
|
2018-08-09 07:56:04 -07:00
|
|
|
keypair: &Keypair,
|
2018-08-09 08:13:57 -07:00
|
|
|
to: Pubkey,
|
2018-03-20 22:15:44 -07:00
|
|
|
last_id: Hash,
|
2018-03-02 09:16:39 -08:00
|
|
|
) -> Result<Signature> {
|
2018-09-26 08:51:51 -07:00
|
|
|
let tx = Transaction::system_new(keypair, to, n, last_id);
|
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 {
|
2018-12-04 14:38:19 -08:00
|
|
|
// TODO: Re-instate budget_program special case?
|
|
|
|
/*
|
2018-12-03 13:32:31 -08:00
|
|
|
if budget_program::check_id(&account.owner) {
|
2018-12-04 14:38:19 -08:00
|
|
|
return budget_program::get_balance(account);
|
2018-09-17 13:36:31 -07:00
|
|
|
}
|
2018-12-04 14:38:19 -08:00
|
|
|
*/
|
|
|
|
account.tokens
|
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
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_account(&self, pubkey: &Pubkey) -> Option<Account> {
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.load_slow(pubkey)
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
2018-05-14 05:49:48 -07:00
|
|
|
|
2018-11-05 09:47:41 -08:00
|
|
|
pub fn transaction_count(&self) -> u64 {
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.transaction_count()
|
2018-05-14 05:49:48 -07:00
|
|
|
}
|
2018-06-28 11:58:33 -07:00
|
|
|
|
2018-12-17 07:55:56 -08:00
|
|
|
pub fn get_signature_status(&self, signature: &Signature) -> Option<Status<Result<()>>> {
|
|
|
|
self.last_ids
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.get_signature_status(signature)
|
2018-09-26 14:57:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_signature(&self, signature: &Signature) -> bool {
|
2018-12-17 07:55:56 -08:00
|
|
|
self.last_ids.read().unwrap().has_signature(signature)
|
2018-06-28 11:58:33 -07:00
|
|
|
}
|
2018-08-13 08:55:13 -07:00
|
|
|
|
2018-12-17 07:55:56 -08:00
|
|
|
pub fn get_signature(
|
|
|
|
&self,
|
|
|
|
last_id: &Hash,
|
|
|
|
signature: &Signature,
|
|
|
|
) -> Option<Status<Result<()>>> {
|
2018-10-17 16:28:26 -07:00
|
|
|
self.last_ids
|
2018-09-28 16:16:35 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2018-12-17 07:55:56 -08:00
|
|
|
.get_signature(last_id, signature)
|
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
|
2018-09-07 12:55:30 -07:00
|
|
|
pub fn hash_internal_state(&self) -> Hash {
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.hash_internal_state()
|
2018-09-07 12:55:30 -07:00
|
|
|
}
|
|
|
|
|
2018-12-21 08:25:07 -08:00
|
|
|
pub fn confirmation_time(&self) -> usize {
|
2018-12-20 15:47:48 -08:00
|
|
|
self.confirmation_time.load(Ordering::Relaxed)
|
2018-08-13 08:55:13 -07:00
|
|
|
}
|
|
|
|
|
2018-12-21 08:25:07 -08:00
|
|
|
pub fn set_confirmation_time(&self, confirmation: usize) {
|
2018-12-20 15:47:48 -08:00
|
|
|
self.confirmation_time
|
|
|
|
.store(confirmation, Ordering::Relaxed);
|
2018-08-13 08:55:13 -07:00
|
|
|
}
|
2018-10-10 13:51:43 -07:00
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
fn send_account_notifications(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
res: &[Result<()>],
|
2019-01-06 22:06:55 -08:00
|
|
|
loaded: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
2018-12-17 12:41:23 -08:00
|
|
|
) {
|
2019-01-06 22:06:55 -08:00
|
|
|
for (i, raccs) in loaded.iter().enumerate() {
|
|
|
|
if res[i].is_err() || raccs.is_err() {
|
2018-12-17 12:41:23 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let tx = &txs[i];
|
2019-01-06 22:06:55 -08:00
|
|
|
let accs = raccs.as_ref().unwrap();
|
|
|
|
for (key, account) in tx.account_keys.iter().zip(accs.0.iter()) {
|
2019-01-21 09:59:09 -08:00
|
|
|
self.subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.check_account(&key, account);
|
2018-10-10 13:51:43 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-07 13:18:14 -08:00
|
|
|
pub fn get_current_leader(&self) -> Option<(Pubkey, u64)> {
|
|
|
|
self.leader_scheduler
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
2018-12-03 00:10:43 -08:00
|
|
|
.get_scheduled_leader(self.tick_height() + 1)
|
2018-10-25 16:58:40 -07:00
|
|
|
}
|
|
|
|
|
2018-11-05 09:47:41 -08:00
|
|
|
pub fn tick_height(&self) -> u64 {
|
2018-11-02 15:49:14 -07:00
|
|
|
self.last_ids.read().unwrap().tick_height
|
2018-10-25 16:58:40 -07:00
|
|
|
}
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2019-01-09 14:33:44 -08:00
|
|
|
use crate::entry::{next_entries, next_entry, Entry};
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::signature::GenKeys;
|
2018-12-17 07:55:56 -08:00
|
|
|
use crate::status_deque;
|
2018-12-17 12:41:23 -08:00
|
|
|
use crate::status_deque::StatusDequeError;
|
2018-04-05 09:26:43 -07:00
|
|
|
use bincode::serialize;
|
2018-12-17 12:41:23 -08:00
|
|
|
use hashbrown::HashSet;
|
2018-11-16 08:04:46 -08:00
|
|
|
use solana_sdk::hash::hash;
|
2018-12-17 07:55:56 -08:00
|
|
|
use solana_sdk::native_program::ProgramError;
|
2018-12-03 10:26:28 -08:00
|
|
|
use solana_sdk::signature::Keypair;
|
|
|
|
use solana_sdk::signature::KeypairUtil;
|
2019-01-17 14:41:48 -08:00
|
|
|
use solana_sdk::storage_program::{StorageTransaction, ENTRIES_PER_SEGMENT};
|
2018-12-04 20:19:48 -08:00
|
|
|
use solana_sdk::system_transaction::SystemTransaction;
|
2018-11-29 16:18:47 -08:00
|
|
|
use solana_sdk::transaction::Instruction;
|
2018-08-13 08:55:13 -07:00
|
|
|
use std;
|
2019-01-17 09:32:42 -08:00
|
|
|
use std::sync::mpsc::channel;
|
2018-02-23 13:08:19 -08:00
|
|
|
|
2018-09-07 20:18:36 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bank_new() {
|
|
|
|
let mint = Mint::new(10_000);
|
|
|
|
let bank = Bank::new(&mint);
|
|
|
|
assert_eq!(bank.get_balance(&mint.pubkey()), 10_000);
|
|
|
|
}
|
|
|
|
|
2018-11-02 14:32:05 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bank_new_with_leader() {
|
|
|
|
let dummy_leader_id = Keypair::new().pubkey();
|
|
|
|
let dummy_leader_tokens = 1;
|
|
|
|
let mint = Mint::new_with_leader(10_000, dummy_leader_id, dummy_leader_tokens);
|
|
|
|
let bank = Bank::new(&mint);
|
|
|
|
assert_eq!(bank.get_balance(&mint.pubkey()), 9999);
|
|
|
|
assert_eq!(bank.get_balance(&dummy_leader_id), 1);
|
|
|
|
}
|
|
|
|
|
2018-02-23 13:08:19 -08:00
|
|
|
#[test]
|
2018-06-06 08:49:22 -07:00
|
|
|
fn test_two_payments_to_one_party() {
|
2018-05-14 14:39:34 -07:00
|
|
|
let mint = Mint::new(10_000);
|
2018-08-09 07:56:04 -07:00
|
|
|
let pubkey = Keypair::new().pubkey();
|
2018-05-14 14:39:34 -07:00
|
|
|
let bank = Bank::new(&mint);
|
|
|
|
assert_eq!(bank.last_id(), mint.last_id());
|
2018-05-03 12:24:35 -07:00
|
|
|
|
2018-05-14 14:39:34 -07:00
|
|
|
bank.transfer(1_000, &mint.keypair(), pubkey, mint.last_id())
|
2018-03-20 22:15:44 -07:00
|
|
|
.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
|
|
|
|
2018-05-14 14:39:34 -07:00
|
|
|
bank.transfer(500, &mint.keypair(), pubkey, mint.last_id())
|
2018-03-20 22:15:44 -07:00
|
|
|
.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() {
|
2018-09-28 16:16:35 -07:00
|
|
|
let mint = Mint::new(1);
|
|
|
|
let key1 = Keypair::new().pubkey();
|
|
|
|
let key2 = Keypair::new().pubkey();
|
|
|
|
let bank = Bank::new(&mint);
|
2018-10-04 13:15:54 -07:00
|
|
|
assert_eq!(bank.last_id(), mint.last_id());
|
|
|
|
|
|
|
|
let t1 = Transaction::system_move(&mint.keypair(), key1, 1, mint.last_id(), 0);
|
|
|
|
let t2 = Transaction::system_move(&mint.keypair(), key2, 1, mint.last_id(), 0);
|
|
|
|
let res = bank.process_transactions(&vec![t1.clone(), t2.clone()]);
|
|
|
|
assert_eq!(res.len(), 2);
|
|
|
|
assert_eq!(res[0], Ok(()));
|
|
|
|
assert_eq!(res[1], Err(BankError::AccountInUse));
|
|
|
|
assert_eq!(bank.get_balance(&mint.pubkey()), 0);
|
|
|
|
assert_eq!(bank.get_balance(&key1), 1);
|
|
|
|
assert_eq!(bank.get_balance(&key2), 0);
|
2018-10-26 14:43:34 -07:00
|
|
|
assert_eq!(
|
|
|
|
bank.get_signature(&t1.last_id, &t1.signatures[0]),
|
2018-12-17 07:55:56 -08:00
|
|
|
Some(Status::Complete(Ok(())))
|
2018-10-26 14:43:34 -07:00
|
|
|
);
|
2018-10-04 13:15:54 -07:00
|
|
|
// TODO: Transactions that fail to pay a fee could be dropped silently
|
|
|
|
assert_eq!(
|
2018-10-26 14:43:34 -07:00
|
|
|
bank.get_signature(&t2.last_id, &t2.signatures[0]),
|
2018-12-17 07:55:56 -08:00
|
|
|
Some(Status::Complete(Err(BankError::AccountInUse)))
|
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() {
|
|
|
|
let mint = Mint::new(1);
|
|
|
|
let key1 = Keypair::new().pubkey();
|
|
|
|
let key2 = Keypair::new().pubkey();
|
|
|
|
let bank = Bank::new(&mint);
|
2018-11-16 08:04:46 -08:00
|
|
|
let spend = SystemInstruction::Move { tokens: 1 };
|
2018-09-28 16:16:35 -07:00
|
|
|
let instructions = vec![
|
|
|
|
Instruction {
|
2018-10-04 13:36:15 -07:00
|
|
|
program_ids_index: 0,
|
2018-09-28 16:16:35 -07:00
|
|
|
userdata: serialize(&spend).unwrap(),
|
|
|
|
accounts: vec![0, 1],
|
|
|
|
},
|
|
|
|
Instruction {
|
2018-10-04 13:36:15 -07:00
|
|
|
program_ids_index: 0,
|
2018-09-28 16:16:35 -07:00
|
|
|
userdata: serialize(&spend).unwrap(),
|
|
|
|
accounts: vec![0, 2],
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
let t1 = Transaction::new_with_instructions(
|
2018-10-26 14:43:34 -07:00
|
|
|
&[&mint.keypair()],
|
2018-09-28 16:16:35 -07:00
|
|
|
&[key1, key2],
|
|
|
|
mint.last_id(),
|
|
|
|
0,
|
2018-11-23 12:45:34 -08:00
|
|
|
vec![system_program::id()],
|
2018-09-28 16:16:35 -07:00
|
|
|
instructions,
|
|
|
|
);
|
|
|
|
let res = bank.process_transactions(&vec![t1.clone()]);
|
|
|
|
assert_eq!(res.len(), 1);
|
2018-11-23 14:53:39 -08:00
|
|
|
assert_eq!(
|
|
|
|
res[0],
|
|
|
|
Err(BankError::ProgramError(
|
|
|
|
1,
|
|
|
|
ProgramError::ResultWithNegativeTokens
|
|
|
|
))
|
|
|
|
);
|
2018-09-28 16:16:35 -07:00
|
|
|
assert_eq!(bank.get_balance(&mint.pubkey()), 1);
|
|
|
|
assert_eq!(bank.get_balance(&key1), 0);
|
|
|
|
assert_eq!(bank.get_balance(&key2), 0);
|
|
|
|
assert_eq!(
|
2018-10-26 14:43:34 -07:00
|
|
|
bank.get_signature(&t1.last_id, &t1.signatures[0]),
|
2018-12-17 07:55:56 -08:00
|
|
|
Some(Status::Complete(Err(BankError::ProgramError(
|
2018-11-23 14:53:39 -08:00
|
|
|
1,
|
|
|
|
ProgramError::ResultWithNegativeTokens
|
2018-12-17 07:55:56 -08:00
|
|
|
))))
|
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() {
|
|
|
|
let mint = Mint::new(2);
|
|
|
|
let key1 = Keypair::new().pubkey();
|
|
|
|
let key2 = Keypair::new().pubkey();
|
|
|
|
let bank = Bank::new(&mint);
|
2018-10-10 17:23:06 -07:00
|
|
|
let t1 = Transaction::system_move_many(
|
2018-09-28 16:16:35 -07:00
|
|
|
&mint.keypair(),
|
2018-10-10 17:23:06 -07:00
|
|
|
&[(key1, 1), (key2, 1)],
|
2018-09-28 16:16:35 -07:00
|
|
|
mint.last_id(),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
let res = bank.process_transactions(&vec![t1.clone()]);
|
|
|
|
assert_eq!(res.len(), 1);
|
2018-10-04 13:15:54 -07:00
|
|
|
assert_eq!(res[0], Ok(()));
|
2018-09-28 16:16:35 -07:00
|
|
|
assert_eq!(bank.get_balance(&mint.pubkey()), 0);
|
|
|
|
assert_eq!(bank.get_balance(&key1), 1);
|
|
|
|
assert_eq!(bank.get_balance(&key2), 1);
|
2018-10-26 14:43:34 -07:00
|
|
|
assert_eq!(
|
|
|
|
bank.get_signature(&t1.last_id, &t1.signatures[0]),
|
2018-12-17 07:55:56 -08:00
|
|
|
Some(Status::Complete(Ok(())))
|
2018-10-26 14:43:34 -07:00
|
|
|
);
|
2018-09-28 16:16:35 -07:00
|
|
|
}
|
|
|
|
|
2018-09-26 15:18:45 -07:00
|
|
|
// TODO: This test demonstrates that fees are not paid when a program fails.
|
2018-09-07 20:18:36 -07:00
|
|
|
// See github issue 1157 (https://github.com/solana-labs/solana/issues/1157)
|
|
|
|
#[test]
|
|
|
|
fn test_detect_failed_duplicate_transactions_issue_1157() {
|
|
|
|
let mint = Mint::new(1);
|
|
|
|
let bank = Bank::new(&mint);
|
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
|
2018-09-26 15:18:45 -07:00
|
|
|
let tx = Transaction::system_create(
|
|
|
|
&mint.keypair(),
|
|
|
|
dest.pubkey(),
|
|
|
|
mint.last_id(),
|
|
|
|
2,
|
|
|
|
0,
|
|
|
|
Pubkey::default(),
|
|
|
|
1,
|
|
|
|
);
|
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));
|
2018-09-17 13:36:31 -07:00
|
|
|
let res = bank.process_transaction(&tx);
|
2018-09-26 15:18:45 -07:00
|
|
|
|
|
|
|
// Result failed, but signature is registered
|
2018-10-04 13:15:54 -07:00
|
|
|
assert!(res.is_err());
|
2018-09-07 20:18:36 -07:00
|
|
|
assert!(bank.has_signature(&signature));
|
2018-09-26 15:18:45 -07:00
|
|
|
assert_matches!(
|
|
|
|
bank.get_signature_status(&signature),
|
2018-12-17 07:55:56 -08:00
|
|
|
Some(Status::Complete(Err(BankError::ProgramError(
|
2018-11-23 14:53:39 -08:00
|
|
|
0,
|
|
|
|
ProgramError::ResultWithNegativeTokens
|
2018-12-17 07:55:56 -08:00
|
|
|
))))
|
2018-09-26 15:18:45 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// The tokens 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
|
|
|
|
|
|
|
// BUG: This should be the original balance minus the transaction fee.
|
|
|
|
//assert_eq!(bank.get_balance(&mint.pubkey()), 0);
|
2018-09-07 20:18:36 -07:00
|
|
|
}
|
|
|
|
|
2018-04-05 20:00:08 -07:00
|
|
|
#[test]
|
|
|
|
fn test_account_not_found() {
|
|
|
|
let mint = Mint::new(1);
|
2018-05-14 14:33:11 -07:00
|
|
|
let bank = Bank::new(&mint);
|
2018-08-09 07:56:04 -07:00
|
|
|
let keypair = Keypair::new();
|
2018-04-05 20:00:08 -07:00
|
|
|
assert_eq!(
|
2018-05-23 11:26:32 -07:00
|
|
|
bank.transfer(1, &keypair, mint.pubkey(), mint.last_id()),
|
2018-09-27 05:01:20 -07:00
|
|
|
Err(BankError::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() {
|
2018-05-14 14:39:34 -07:00
|
|
|
let mint = Mint::new(11_000);
|
|
|
|
let bank = Bank::new(&mint);
|
2018-08-09 07:56:04 -07:00
|
|
|
let pubkey = Keypair::new().pubkey();
|
2018-05-14 14:39:34 -07:00
|
|
|
bank.transfer(1_000, &mint.keypair(), pubkey, mint.last_id())
|
2018-03-20 22:15:44 -07:00
|
|
|
.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);
|
2018-09-17 13:36:31 -07:00
|
|
|
assert_matches!(
|
2018-05-14 14:39:34 -07:00
|
|
|
bank.transfer(10_001, &mint.keypair(), pubkey, mint.last_id()),
|
2018-11-23 14:53:39 -08:00
|
|
|
Err(BankError::ProgramError(
|
|
|
|
0,
|
|
|
|
ProgramError::ResultWithNegativeTokens
|
|
|
|
))
|
2018-03-02 09:16:39 -08:00
|
|
|
);
|
2018-05-14 14:33:11 -07:00
|
|
|
assert_eq!(bank.transaction_count(), 1);
|
2018-03-01 11:23:27 -08:00
|
|
|
|
2018-05-14 14:39:34 -07: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() {
|
2018-05-14 14:39:34 -07:00
|
|
|
let mint = Mint::new(10_000);
|
|
|
|
let bank = Bank::new(&mint);
|
2018-08-09 07:56:04 -07:00
|
|
|
let pubkey = Keypair::new().pubkey();
|
2018-05-14 14:39:34 -07:00
|
|
|
bank.transfer(500, &mint.keypair(), pubkey, mint.last_id())
|
2018-03-20 22:15:44 -07:00
|
|
|
.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
|
|
|
|
2018-04-12 19:53:34 -07:00
|
|
|
#[test]
|
|
|
|
fn test_debits_before_credits() {
|
|
|
|
let mint = Mint::new(2);
|
2018-05-14 14:33:11 -07:00
|
|
|
let bank = Bank::new(&mint);
|
2018-08-09 07:56:04 -07:00
|
|
|
let keypair = Keypair::new();
|
2018-09-26 08:51:51 -07:00
|
|
|
let tx0 = Transaction::system_new(&mint.keypair(), keypair.pubkey(), 2, mint.last_id());
|
|
|
|
let tx1 = Transaction::system_new(&keypair, mint.pubkey(), 1, mint.last_id());
|
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
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_process_empty_entry_is_registered() {
|
|
|
|
let mint = Mint::new(1);
|
|
|
|
let bank = Bank::new(&mint);
|
2018-08-09 07:56:04 -07:00
|
|
|
let keypair = Keypair::new();
|
2018-06-14 16:32:39 -07:00
|
|
|
let entry = next_entry(&mint.last_id(), 1, vec![]);
|
2018-09-26 08:51:51 -07:00
|
|
|
let tx = Transaction::system_new(&mint.keypair(), keypair.pubkey(), 1, entry.id);
|
2018-06-14 16:32:39 -07:00
|
|
|
|
|
|
|
// First, ensure the TX is rejected because of the unregistered last ID
|
|
|
|
assert_eq!(
|
|
|
|
bank.process_transaction(&tx),
|
2018-09-27 05:01:20 -07:00
|
|
|
Err(BankError::LastIdNotFound)
|
2018-06-14 16:32:39 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// Now ensure the TX is accepted despite pointing to the ID of an empty entry.
|
2018-09-24 12:26:47 -07:00
|
|
|
bank.process_entries(&[entry]).unwrap();
|
2018-10-04 13:15:54 -07:00
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
2018-06-14 16:32:39 -07:00
|
|
|
}
|
2018-07-01 21:06:40 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_process_genesis() {
|
2018-11-02 14:32:05 -07:00
|
|
|
let dummy_leader_id = Keypair::new().pubkey();
|
|
|
|
let dummy_leader_tokens = 1;
|
|
|
|
let mint = Mint::new_with_leader(5, dummy_leader_id, dummy_leader_tokens);
|
2018-07-01 21:06:40 -07:00
|
|
|
let genesis = mint.create_entries();
|
|
|
|
let bank = Bank::default();
|
2018-10-25 16:58:40 -07:00
|
|
|
bank.process_ledger(genesis).unwrap();
|
2018-11-02 14:32:05 -07:00
|
|
|
assert_eq!(bank.get_balance(&mint.pubkey()), 4);
|
|
|
|
assert_eq!(bank.get_balance(&dummy_leader_id), 1);
|
|
|
|
assert_eq!(
|
|
|
|
bank.leader_scheduler.read().unwrap().bootstrap_leader,
|
|
|
|
dummy_leader_id
|
|
|
|
);
|
2018-07-01 21:06:40 -07:00
|
|
|
}
|
|
|
|
|
2018-09-07 12:55:30 -07:00
|
|
|
fn create_sample_block_with_next_entries_using_keypairs(
|
|
|
|
mint: &Mint,
|
|
|
|
keypairs: &[Keypair],
|
|
|
|
) -> impl Iterator<Item = Entry> {
|
2018-10-10 17:23:06 -07:00
|
|
|
let mut last_id = mint.last_id();
|
2018-10-04 13:15:54 -07:00
|
|
|
let mut hash = mint.last_id();
|
|
|
|
let mut entries: Vec<Entry> = vec![];
|
2018-10-18 22:57:48 -07:00
|
|
|
let num_hashes = 1;
|
2018-10-04 13:15:54 -07:00
|
|
|
for k in keypairs {
|
|
|
|
let txs = vec![Transaction::system_new(
|
|
|
|
&mint.keypair(),
|
|
|
|
k.pubkey(),
|
|
|
|
1,
|
2018-10-10 17:23:06 -07:00
|
|
|
last_id,
|
2018-10-04 13:15:54 -07:00
|
|
|
)];
|
2019-01-09 14:33:44 -08:00
|
|
|
let mut e = next_entries(&hash, 0, txs);
|
2018-10-04 13:15:54 -07:00
|
|
|
entries.append(&mut e);
|
|
|
|
hash = entries.last().unwrap().id;
|
2018-12-10 20:03:04 -08:00
|
|
|
let tick = Entry::new(&hash, 0, num_hashes, vec![]);
|
2018-10-18 22:57:48 -07:00
|
|
|
hash = tick.id;
|
2018-10-10 17:23:06 -07:00
|
|
|
last_id = hash;
|
|
|
|
entries.push(tick);
|
2018-10-04 13:15:54 -07:00
|
|
|
}
|
2018-09-07 12:55:30 -07:00
|
|
|
entries.into_iter()
|
|
|
|
}
|
|
|
|
|
2018-10-10 17:23:06 -07:00
|
|
|
// create a ledger with tick entries every `ticks` entries
|
|
|
|
fn create_sample_block_with_ticks(
|
|
|
|
mint: &Mint,
|
|
|
|
length: usize,
|
|
|
|
ticks: usize,
|
|
|
|
) -> impl Iterator<Item = Entry> {
|
2018-07-02 10:07:32 -07:00
|
|
|
let mut entries = Vec::with_capacity(length);
|
|
|
|
let mut hash = mint.last_id();
|
2018-10-10 17:23:06 -07:00
|
|
|
let mut last_id = mint.last_id();
|
2018-10-18 22:57:48 -07:00
|
|
|
let num_hashes = 1;
|
2018-10-10 17:23:06 -07:00
|
|
|
for i in 0..length {
|
2018-08-09 07:56:04 -07:00
|
|
|
let keypair = Keypair::new();
|
2018-10-10 17:23:06 -07:00
|
|
|
let tx = Transaction::system_new(&mint.keypair(), keypair.pubkey(), 1, last_id);
|
2018-12-10 20:03:04 -08:00
|
|
|
let entry = Entry::new(&hash, 0, num_hashes, vec![tx]);
|
2018-10-18 22:57:48 -07:00
|
|
|
hash = entry.id;
|
2018-07-02 10:07:32 -07:00
|
|
|
entries.push(entry);
|
2019-01-18 17:16:17 -08:00
|
|
|
|
|
|
|
// Add a second Transaction that will produce a
|
|
|
|
// ProgramError<0, ResultWithNegativeTokens> error when processed
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
let tx = Transaction::system_new(&keypair, keypair2.pubkey(), 42, last_id);
|
|
|
|
let entry = Entry::new(&hash, 0, num_hashes, vec![tx]);
|
|
|
|
hash = entry.id;
|
|
|
|
entries.push(entry);
|
|
|
|
|
2018-10-10 17:23:06 -07:00
|
|
|
if (i + 1) % ticks == 0 {
|
2018-12-10 20:03:04 -08:00
|
|
|
let tick = Entry::new(&hash, 0, num_hashes, vec![]);
|
2018-10-18 22:57:48 -07:00
|
|
|
hash = tick.id;
|
2018-10-10 17:23:06 -07:00
|
|
|
last_id = hash;
|
|
|
|
entries.push(tick);
|
|
|
|
}
|
2018-07-02 10:07:32 -07:00
|
|
|
}
|
|
|
|
entries.into_iter()
|
2018-07-01 21:06:40 -07:00
|
|
|
}
|
2018-08-07 11:09:33 -07:00
|
|
|
|
2018-08-09 08:13:57 -07:00
|
|
|
fn create_sample_ledger(length: usize) -> (impl Iterator<Item = Entry>, Pubkey) {
|
2018-11-02 14:32:05 -07:00
|
|
|
let dummy_leader_id = Keypair::new().pubkey();
|
|
|
|
let dummy_leader_tokens = 1;
|
|
|
|
let mint = Mint::new_with_leader(
|
2018-11-05 08:36:22 -08:00
|
|
|
length as u64 + 1 + dummy_leader_tokens,
|
2018-11-02 14:32:05 -07:00
|
|
|
dummy_leader_id,
|
|
|
|
dummy_leader_tokens,
|
|
|
|
);
|
2018-07-01 21:06:40 -07:00
|
|
|
let genesis = mint.create_entries();
|
2018-10-10 17:23:06 -07:00
|
|
|
let block = create_sample_block_with_ticks(&mint, length, length);
|
2018-07-01 21:06:40 -07:00
|
|
|
(genesis.into_iter().chain(block), mint.pubkey())
|
|
|
|
}
|
|
|
|
|
2018-09-07 12:55:30 -07:00
|
|
|
fn create_sample_ledger_with_mint_and_keypairs(
|
|
|
|
mint: &Mint,
|
|
|
|
keypairs: &[Keypair],
|
|
|
|
) -> impl Iterator<Item = Entry> {
|
|
|
|
let genesis = mint.create_entries();
|
|
|
|
let block = create_sample_block_with_next_entries_using_keypairs(mint, keypairs);
|
|
|
|
genesis.into_iter().chain(block)
|
|
|
|
}
|
|
|
|
|
2018-07-01 21:06:40 -07:00
|
|
|
#[test]
|
2018-10-10 17:23:06 -07:00
|
|
|
fn test_process_ledger_simple() {
|
2018-07-02 10:07:32 -07:00
|
|
|
let (ledger, pubkey) = create_sample_ledger(1);
|
2018-07-01 21:06:40 -07:00
|
|
|
let bank = Bank::default();
|
2018-12-03 13:32:31 -08:00
|
|
|
bank.add_system_program();
|
2018-10-30 10:05:18 -07:00
|
|
|
let (ledger_height, last_id) = bank.process_ledger(ledger).unwrap();
|
2018-07-02 16:31:10 -07:00
|
|
|
assert_eq!(bank.get_balance(&pubkey), 1);
|
2019-01-18 17:16:17 -08:00
|
|
|
assert_eq!(ledger_height, 6);
|
2018-11-05 09:47:41 -08:00
|
|
|
assert_eq!(bank.tick_height(), 2);
|
2018-10-30 10:05:18 -07:00
|
|
|
assert_eq!(bank.last_id(), last_id);
|
2018-07-01 21:06:40 -07:00
|
|
|
}
|
|
|
|
|
2018-08-13 08:55:13 -07:00
|
|
|
#[test]
|
2018-09-07 12:55:30 -07:00
|
|
|
fn test_hash_internal_state() {
|
2018-11-02 14:32:05 -07:00
|
|
|
let dummy_leader_id = Keypair::new().pubkey();
|
|
|
|
let dummy_leader_tokens = 1;
|
|
|
|
let mint = Mint::new_with_leader(2_000, dummy_leader_id, dummy_leader_tokens);
|
|
|
|
|
2018-09-07 12:55:30 -07:00
|
|
|
let seed = [0u8; 32];
|
|
|
|
let mut rnd = GenKeys::new(seed);
|
|
|
|
let keypairs = rnd.gen_n_keypairs(5);
|
|
|
|
let ledger0 = create_sample_ledger_with_mint_and_keypairs(&mint, &keypairs);
|
|
|
|
let ledger1 = create_sample_ledger_with_mint_and_keypairs(&mint, &keypairs);
|
|
|
|
|
|
|
|
let bank0 = Bank::default();
|
2018-12-03 13:32:31 -08:00
|
|
|
bank0.add_system_program();
|
2018-10-25 16:58:40 -07:00
|
|
|
bank0.process_ledger(ledger0).unwrap();
|
2018-09-07 12:55:30 -07:00
|
|
|
let bank1 = Bank::default();
|
2018-12-03 13:32:31 -08:00
|
|
|
bank1.add_system_program();
|
2018-10-25 16:58:40 -07:00
|
|
|
bank1.process_ledger(ledger1).unwrap();
|
2018-09-07 12:55:30 -07:00
|
|
|
|
|
|
|
let initial_state = bank0.hash_internal_state();
|
|
|
|
|
|
|
|
assert_eq!(bank1.hash_internal_state(), initial_state);
|
|
|
|
|
|
|
|
let pubkey = keypairs[0].pubkey();
|
|
|
|
bank0
|
|
|
|
.transfer(1_000, &mint.keypair(), pubkey, mint.last_id())
|
|
|
|
.unwrap();
|
|
|
|
assert_ne!(bank0.hash_internal_state(), initial_state);
|
|
|
|
bank1
|
|
|
|
.transfer(1_000, &mint.keypair(), pubkey, mint.last_id())
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(bank0.hash_internal_state(), bank1.hash_internal_state());
|
|
|
|
}
|
|
|
|
#[test]
|
2018-12-21 08:25:07 -08:00
|
|
|
fn test_confirmation_time() {
|
2018-08-13 08:55:13 -07:00
|
|
|
let def_bank = Bank::default();
|
2018-12-21 08:25:07 -08:00
|
|
|
assert_eq!(def_bank.confirmation_time(), std::usize::MAX);
|
|
|
|
def_bank.set_confirmation_time(90);
|
|
|
|
assert_eq!(def_bank.confirmation_time(), 90);
|
2018-08-13 08:55:13 -07:00
|
|
|
}
|
2018-10-04 13:15:54 -07:00
|
|
|
#[test]
|
|
|
|
fn test_interleaving_locks() {
|
|
|
|
let mint = Mint::new(3);
|
|
|
|
let bank = Bank::new(&mint);
|
|
|
|
let alice = Keypair::new();
|
|
|
|
let bob = Keypair::new();
|
|
|
|
|
|
|
|
let tx1 = Transaction::system_new(&mint.keypair(), alice.pubkey(), 1, mint.last_id());
|
|
|
|
let pay_alice = vec![tx1];
|
|
|
|
|
2019-01-06 22:06:55 -08:00
|
|
|
let lock_result = bank.lock_accounts(&pay_alice);
|
2018-10-10 17:23:06 -07:00
|
|
|
let results_alice =
|
2019-01-21 10:17:04 -08:00
|
|
|
bank.load_execute_and_commit_transactions(&pay_alice, lock_result, MAX_ENTRY_IDS);
|
2018-10-04 13:15:54 -07:00
|
|
|
assert_eq!(results_alice[0], Ok(()));
|
|
|
|
|
|
|
|
// try executing an interleaved transfer twice
|
|
|
|
assert_eq!(
|
|
|
|
bank.transfer(1, &mint.keypair(), bob.pubkey(), mint.last_id()),
|
|
|
|
Err(BankError::AccountInUse)
|
|
|
|
);
|
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!(
|
|
|
|
bank.transfer(1, &mint.keypair(), bob.pubkey(), mint.last_id()),
|
|
|
|
Err(BankError::AccountInUse)
|
|
|
|
);
|
|
|
|
|
|
|
|
bank.unlock_accounts(&pay_alice, &results_alice);
|
|
|
|
|
|
|
|
assert_matches!(
|
|
|
|
bank.transfer(2, &mint.keypair(), bob.pubkey(), mint.last_id()),
|
|
|
|
Ok(_)
|
|
|
|
);
|
|
|
|
}
|
2018-10-10 13:51:43 -07:00
|
|
|
|
2018-10-10 17:23:06 -07:00
|
|
|
#[test]
|
2018-10-16 12:09:48 -07:00
|
|
|
fn test_first_err() {
|
|
|
|
assert_eq!(Bank::first_err(&[Ok(())]), Ok(()));
|
|
|
|
assert_eq!(
|
|
|
|
Bank::first_err(&[Ok(()), Err(BankError::DuplicateSignature)]),
|
|
|
|
Err(BankError::DuplicateSignature)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Bank::first_err(&[
|
|
|
|
Ok(()),
|
|
|
|
Err(BankError::DuplicateSignature),
|
|
|
|
Err(BankError::AccountInUse)
|
|
|
|
]),
|
|
|
|
Err(BankError::DuplicateSignature)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Bank::first_err(&[
|
|
|
|
Ok(()),
|
|
|
|
Err(BankError::AccountInUse),
|
|
|
|
Err(BankError::DuplicateSignature)
|
|
|
|
]),
|
|
|
|
Err(BankError::AccountInUse)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Bank::first_err(&[
|
|
|
|
Err(BankError::AccountInUse),
|
|
|
|
Ok(()),
|
|
|
|
Err(BankError::DuplicateSignature)
|
|
|
|
]),
|
|
|
|
Err(BankError::AccountInUse)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_par_process_entries_tick() {
|
|
|
|
let mint = Mint::new(1000);
|
|
|
|
let bank = Bank::new(&mint);
|
|
|
|
|
|
|
|
// ensure bank can process a tick
|
|
|
|
let tick = next_entry(&mint.last_id(), 1, vec![]);
|
|
|
|
assert_eq!(bank.par_process_entries(&[tick.clone()]), Ok(()));
|
|
|
|
assert_eq!(bank.last_id(), tick.id);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_par_process_entries_2_entries_collision() {
|
|
|
|
let mint = Mint::new(1000);
|
|
|
|
let bank = Bank::new(&mint);
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
|
|
|
|
let last_id = bank.last_id();
|
|
|
|
|
|
|
|
// ensure bank can process 2 entries that have a common account and no tick is registered
|
|
|
|
let tx = Transaction::system_new(&mint.keypair(), keypair1.pubkey(), 2, bank.last_id());
|
|
|
|
let entry_1 = next_entry(&last_id, 1, vec![tx]);
|
|
|
|
let tx = Transaction::system_new(&mint.keypair(), keypair2.pubkey(), 2, bank.last_id());
|
|
|
|
let entry_2 = next_entry(&entry_1.id, 1, vec![tx]);
|
|
|
|
assert_eq!(bank.par_process_entries(&[entry_1, entry_2]), Ok(()));
|
|
|
|
assert_eq!(bank.get_balance(&keypair1.pubkey()), 2);
|
|
|
|
assert_eq!(bank.get_balance(&keypair2.pubkey()), 2);
|
|
|
|
assert_eq!(bank.last_id(), last_id);
|
|
|
|
}
|
|
|
|
#[test]
|
2018-12-18 16:06:05 -08:00
|
|
|
fn test_par_process_entries_2_txes_collision() {
|
|
|
|
let mint = Mint::new(1000);
|
|
|
|
let bank = Bank::new(&mint);
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
let keypair3 = Keypair::new();
|
|
|
|
println!("KP1 {:?}", keypair1.pubkey());
|
|
|
|
println!("KP2 {:?}", keypair2.pubkey());
|
|
|
|
println!("KP3 {:?}", keypair3.pubkey());
|
|
|
|
println!("Mint {:?}", mint.keypair().pubkey());
|
|
|
|
|
|
|
|
// fund: put 4 in each of 1 and 2
|
|
|
|
assert_matches!(
|
|
|
|
bank.transfer(4, &mint.keypair(), keypair1.pubkey(), bank.last_id()),
|
|
|
|
Ok(_)
|
|
|
|
);
|
|
|
|
assert_matches!(
|
|
|
|
bank.transfer(4, &mint.keypair(), keypair2.pubkey(), bank.last_id()),
|
|
|
|
Ok(_)
|
|
|
|
);
|
|
|
|
|
|
|
|
// construct an Entry whose 2nd transaction would cause a lock conflict with previous entry
|
|
|
|
let entry_1_to_mint = next_entry(
|
|
|
|
&bank.last_id(),
|
|
|
|
1,
|
|
|
|
vec![Transaction::system_new(
|
|
|
|
&keypair1,
|
|
|
|
mint.keypair().pubkey(),
|
|
|
|
1,
|
|
|
|
bank.last_id(),
|
|
|
|
)],
|
|
|
|
);
|
|
|
|
|
|
|
|
let entry_2_to_3_mint_to_1 = next_entry(
|
|
|
|
&entry_1_to_mint.id,
|
|
|
|
1,
|
|
|
|
vec![
|
|
|
|
Transaction::system_new(&keypair2, keypair3.pubkey(), 2, bank.last_id()), // should be fine
|
|
|
|
Transaction::system_new(&keypair1, mint.keypair().pubkey(), 2, bank.last_id()), // will collide
|
|
|
|
],
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
bank.par_process_entries(&[entry_1_to_mint, entry_2_to_3_mint_to_1]),
|
|
|
|
Ok(())
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(bank.get_balance(&keypair1.pubkey()), 1);
|
|
|
|
assert_eq!(bank.get_balance(&keypair2.pubkey()), 2);
|
|
|
|
assert_eq!(bank.get_balance(&keypair3.pubkey()), 2);
|
|
|
|
}
|
|
|
|
#[test]
|
2018-10-16 12:09:48 -07:00
|
|
|
fn test_par_process_entries_2_entries_par() {
|
|
|
|
let mint = Mint::new(1000);
|
|
|
|
let bank = Bank::new(&mint);
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
let keypair3 = Keypair::new();
|
|
|
|
let keypair4 = Keypair::new();
|
|
|
|
|
|
|
|
//load accounts
|
|
|
|
let tx = Transaction::system_new(&mint.keypair(), keypair1.pubkey(), 1, bank.last_id());
|
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
|
|
|
let tx = Transaction::system_new(&mint.keypair(), keypair2.pubkey(), 1, bank.last_id());
|
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
|
|
|
|
|
|
|
// ensure bank can process 2 entries that do not have a common account and no tick is registered
|
|
|
|
let last_id = bank.last_id();
|
|
|
|
let tx = Transaction::system_new(&keypair1, keypair3.pubkey(), 1, bank.last_id());
|
|
|
|
let entry_1 = next_entry(&last_id, 1, vec![tx]);
|
|
|
|
let tx = Transaction::system_new(&keypair2, keypair4.pubkey(), 1, bank.last_id());
|
|
|
|
let entry_2 = next_entry(&entry_1.id, 1, vec![tx]);
|
|
|
|
assert_eq!(bank.par_process_entries(&[entry_1, entry_2]), Ok(()));
|
|
|
|
assert_eq!(bank.get_balance(&keypair3.pubkey()), 1);
|
|
|
|
assert_eq!(bank.get_balance(&keypair4.pubkey()), 1);
|
|
|
|
assert_eq!(bank.last_id(), last_id);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_par_process_entries_2_entries_tick() {
|
|
|
|
let mint = Mint::new(1000);
|
|
|
|
let bank = Bank::new(&mint);
|
|
|
|
let keypair1 = Keypair::new();
|
|
|
|
let keypair2 = Keypair::new();
|
|
|
|
let keypair3 = Keypair::new();
|
|
|
|
let keypair4 = Keypair::new();
|
|
|
|
|
|
|
|
//load accounts
|
|
|
|
let tx = Transaction::system_new(&mint.keypair(), keypair1.pubkey(), 1, bank.last_id());
|
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
|
|
|
let tx = Transaction::system_new(&mint.keypair(), keypair2.pubkey(), 1, bank.last_id());
|
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
|
|
|
|
|
|
|
let last_id = bank.last_id();
|
|
|
|
|
|
|
|
// ensure bank can process 2 entries that do not have a common account and tick is registered
|
|
|
|
let tx = Transaction::system_new(&keypair2, keypair3.pubkey(), 1, bank.last_id());
|
|
|
|
let entry_1 = next_entry(&last_id, 1, vec![tx]);
|
2018-12-10 20:03:04 -08:00
|
|
|
let tick = next_entry(&entry_1.id, 1, vec![]);
|
|
|
|
let tx = Transaction::system_new(&keypair1, keypair4.pubkey(), 1, tick.id);
|
|
|
|
let entry_2 = next_entry(&tick.id, 1, vec![tx]);
|
2018-10-16 12:09:48 -07:00
|
|
|
assert_eq!(
|
2018-12-10 20:03:04 -08:00
|
|
|
bank.par_process_entries(&[entry_1.clone(), tick.clone(), entry_2]),
|
2018-10-16 12:09:48 -07:00
|
|
|
Ok(())
|
|
|
|
);
|
|
|
|
assert_eq!(bank.get_balance(&keypair3.pubkey()), 1);
|
|
|
|
assert_eq!(bank.get_balance(&keypair4.pubkey()), 1);
|
2018-12-10 20:03:04 -08:00
|
|
|
assert_eq!(bank.last_id(), tick.id);
|
2018-10-16 12:09:48 -07:00
|
|
|
// ensure that errors are returned
|
|
|
|
assert_eq!(
|
|
|
|
bank.par_process_entries(&[entry_1]),
|
|
|
|
Err(BankError::AccountNotFound)
|
|
|
|
);
|
|
|
|
}
|
2018-10-26 19:44:53 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_program_ids() {
|
|
|
|
let system = Pubkey::new(&[
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0,
|
|
|
|
]);
|
|
|
|
let native = Pubkey::new(&[
|
|
|
|
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0,
|
|
|
|
]);
|
|
|
|
let bpf = Pubkey::new(&[
|
|
|
|
128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
]);
|
|
|
|
let budget = Pubkey::new(&[
|
|
|
|
129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
]);
|
|
|
|
let storage = Pubkey::new(&[
|
|
|
|
130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
]);
|
2018-12-03 22:30:52 -08:00
|
|
|
let token = Pubkey::new(&[
|
2018-10-26 19:44:53 -07:00
|
|
|
131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
]);
|
|
|
|
let vote = Pubkey::new(&[
|
|
|
|
132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
]);
|
2019-01-17 14:41:48 -08:00
|
|
|
let storage_system = Pubkey::new(&[
|
|
|
|
133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
]);
|
2018-10-26 19:44:53 -07:00
|
|
|
|
2018-11-23 12:45:34 -08:00
|
|
|
assert_eq!(system_program::id(), system);
|
2018-12-08 09:25:57 -08:00
|
|
|
assert_eq!(solana_native_loader::id(), native);
|
2018-12-03 13:32:31 -08:00
|
|
|
assert_eq!(bpf_loader::id(), bpf);
|
2018-11-23 12:45:34 -08:00
|
|
|
assert_eq!(budget_program::id(), budget);
|
|
|
|
assert_eq!(storage_program::id(), storage);
|
2018-12-03 22:30:52 -08:00
|
|
|
assert_eq!(token_program::id(), token);
|
2018-11-23 12:45:34 -08:00
|
|
|
assert_eq!(vote_program::id(), vote);
|
2019-01-17 14:41:48 -08:00
|
|
|
assert_eq!(storage_program::system_id(), storage_system);
|
2018-10-26 19:44:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_program_id_uniqueness() {
|
|
|
|
let mut unique = HashSet::new();
|
|
|
|
let ids = vec![
|
2018-11-23 12:45:34 -08:00
|
|
|
system_program::id(),
|
2018-12-08 09:25:57 -08:00
|
|
|
solana_native_loader::id(),
|
2018-12-03 13:32:31 -08:00
|
|
|
bpf_loader::id(),
|
2018-11-23 12:45:34 -08:00
|
|
|
budget_program::id(),
|
|
|
|
storage_program::id(),
|
2018-12-03 22:30:52 -08:00
|
|
|
token_program::id(),
|
2018-11-23 12:45:34 -08:00
|
|
|
vote_program::id(),
|
2019-01-17 14:41:48 -08:00
|
|
|
storage_program::system_id(),
|
2018-10-26 19:44:53 -07:00
|
|
|
];
|
|
|
|
assert!(ids.into_iter().all(move |id| unique.insert(id)));
|
|
|
|
}
|
2018-11-05 09:47:41 -08:00
|
|
|
|
|
|
|
#[test]
|
2018-11-05 16:43:27 -08:00
|
|
|
fn test_bank_purge() {
|
2018-11-05 09:47:41 -08:00
|
|
|
let alice = Mint::new(10_000);
|
|
|
|
let bank = Bank::new(&alice);
|
|
|
|
let bob = Keypair::new();
|
2018-11-05 16:43:27 -08:00
|
|
|
let charlie = Keypair::new();
|
2018-11-05 09:47:41 -08:00
|
|
|
|
|
|
|
// bob should have 500
|
|
|
|
bank.transfer(500, &alice.keypair(), bob.pubkey(), alice.last_id())
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
|
|
|
|
|
2018-11-05 16:43:27 -08:00
|
|
|
bank.transfer(500, &alice.keypair(), charlie.pubkey(), alice.last_id())
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
|
2018-11-05 09:47:41 -08:00
|
|
|
|
|
|
|
bank.checkpoint();
|
|
|
|
bank.checkpoint();
|
|
|
|
assert_eq!(bank.checkpoint_depth(), 2);
|
|
|
|
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
|
2018-11-05 16:43:27 -08:00
|
|
|
assert_eq!(bank.get_balance(&alice.pubkey()), 9_000);
|
|
|
|
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
|
|
|
|
assert_eq!(bank.transaction_count(), 2);
|
2018-11-05 09:47:41 -08:00
|
|
|
|
|
|
|
// transfer money back, so bob has zero
|
|
|
|
bank.transfer(500, &bob, alice.keypair().pubkey(), alice.last_id())
|
|
|
|
.unwrap();
|
|
|
|
// this has to be stored as zero in the top accounts hashmap ;)
|
2018-12-17 12:41:23 -08:00
|
|
|
assert!(bank.accounts.load_slow(&bob.pubkey()).is_some());
|
2018-11-05 16:43:27 -08:00
|
|
|
assert_eq!(bank.get_balance(&bob.pubkey()), 0);
|
|
|
|
// double-checks
|
|
|
|
assert_eq!(bank.get_balance(&alice.pubkey()), 9_500);
|
|
|
|
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
|
|
|
|
assert_eq!(bank.transaction_count(), 3);
|
|
|
|
bank.purge(1);
|
|
|
|
|
2018-11-05 09:47:41 -08:00
|
|
|
assert_eq!(bank.get_balance(&bob.pubkey()), 0);
|
2018-11-05 16:43:27 -08:00
|
|
|
// double-checks
|
|
|
|
assert_eq!(bank.get_balance(&alice.pubkey()), 9_500);
|
|
|
|
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
|
|
|
|
assert_eq!(bank.transaction_count(), 3);
|
|
|
|
assert_eq!(bank.checkpoint_depth(), 1);
|
|
|
|
|
|
|
|
bank.purge(0);
|
|
|
|
|
|
|
|
// bob should still have 0, alice should have 10_000
|
|
|
|
assert_eq!(bank.get_balance(&bob.pubkey()), 0);
|
2018-12-17 12:41:23 -08:00
|
|
|
assert!(bank.accounts.load_slow(&bob.pubkey()).is_none());
|
2018-11-05 16:43:27 -08:00
|
|
|
// double-checks
|
|
|
|
assert_eq!(bank.get_balance(&alice.pubkey()), 9_500);
|
|
|
|
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
|
|
|
|
assert_eq!(bank.transaction_count(), 3);
|
|
|
|
assert_eq!(bank.checkpoint_depth(), 0);
|
|
|
|
}
|
|
|
|
|
2018-11-27 14:17:29 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_checkpoint_zero_balance() {
|
|
|
|
let alice = Mint::new(1_000);
|
|
|
|
let bank = Bank::new(&alice);
|
|
|
|
let bob = Keypair::new();
|
|
|
|
let charlie = Keypair::new();
|
|
|
|
|
|
|
|
// bob should have 500
|
|
|
|
bank.transfer(500, &alice.keypair(), bob.pubkey(), alice.last_id())
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
|
|
|
|
assert_eq!(bank.checkpoint_depth(), 0);
|
|
|
|
|
2018-11-28 12:40:34 -08:00
|
|
|
let account = bank.get_account(&alice.pubkey()).unwrap();
|
|
|
|
let default_account = Account::default();
|
|
|
|
assert_eq!(account.userdata, default_account.userdata);
|
|
|
|
assert_eq!(account.owner, default_account.owner);
|
|
|
|
assert_eq!(account.executable, default_account.executable);
|
|
|
|
assert_eq!(account.loader, default_account.loader);
|
|
|
|
|
2018-11-27 14:17:29 -08:00
|
|
|
bank.checkpoint();
|
|
|
|
assert_eq!(bank.checkpoint_depth(), 1);
|
|
|
|
|
|
|
|
// charlie should have 500, alice should have 0
|
|
|
|
bank.transfer(500, &alice.keypair(), charlie.pubkey(), alice.last_id())
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
|
|
|
|
assert_eq!(bank.get_balance(&alice.pubkey()), 0);
|
|
|
|
|
|
|
|
let account = bank.get_account(&alice.pubkey()).unwrap();
|
|
|
|
assert_eq!(account.tokens, default_account.tokens);
|
|
|
|
assert_eq!(account.userdata, default_account.userdata);
|
|
|
|
assert_eq!(account.owner, default_account.owner);
|
|
|
|
assert_eq!(account.executable, default_account.executable);
|
|
|
|
assert_eq!(account.loader, default_account.loader);
|
|
|
|
}
|
|
|
|
|
2018-12-17 07:55:56 -08:00
|
|
|
fn reserve_signature_with_last_id_test(
|
|
|
|
bank: &Bank,
|
|
|
|
sig: &Signature,
|
|
|
|
last_id: &Hash,
|
|
|
|
) -> status_deque::Result<()> {
|
|
|
|
let mut last_ids = bank.last_ids.write().unwrap();
|
|
|
|
last_ids.reserve_signature_with_last_id(last_id, sig)
|
|
|
|
}
|
|
|
|
|
2018-11-05 16:43:27 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_checkpoint_rollback() {
|
|
|
|
let alice = Mint::new(10_000);
|
|
|
|
let bank = Bank::new(&alice);
|
|
|
|
let bob = Keypair::new();
|
|
|
|
let charlie = Keypair::new();
|
|
|
|
|
|
|
|
// bob should have 500
|
|
|
|
bank.transfer(500, &alice.keypair(), bob.pubkey(), alice.last_id())
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
|
|
|
|
bank.transfer(500, &alice.keypair(), charlie.pubkey(), alice.last_id())
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
|
|
|
|
assert_eq!(bank.checkpoint_depth(), 0);
|
|
|
|
|
|
|
|
bank.checkpoint();
|
|
|
|
bank.checkpoint();
|
|
|
|
assert_eq!(bank.checkpoint_depth(), 2);
|
|
|
|
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
|
|
|
|
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
|
2018-11-05 09:47:41 -08:00
|
|
|
assert_eq!(bank.transaction_count(), 2);
|
2018-11-05 16:43:27 -08:00
|
|
|
|
|
|
|
// transfer money back, so bob has zero
|
|
|
|
bank.transfer(500, &bob, alice.keypair().pubkey(), alice.last_id())
|
|
|
|
.unwrap();
|
|
|
|
// this has to be stored as zero in the top accounts hashmap ;)
|
|
|
|
assert_eq!(bank.get_balance(&bob.pubkey()), 0);
|
|
|
|
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
|
|
|
|
assert_eq!(bank.transaction_count(), 3);
|
2018-11-05 09:47:41 -08:00
|
|
|
bank.rollback();
|
|
|
|
|
|
|
|
// bob should have 500 again
|
|
|
|
assert_eq!(bank.get_balance(&bob.pubkey()), 500);
|
2018-11-05 16:43:27 -08:00
|
|
|
assert_eq!(bank.get_balance(&charlie.pubkey()), 500);
|
|
|
|
assert_eq!(bank.transaction_count(), 2);
|
2018-11-05 09:47:41 -08:00
|
|
|
assert_eq!(bank.checkpoint_depth(), 1);
|
|
|
|
|
|
|
|
let signature = Signature::default();
|
|
|
|
for i in 0..MAX_ENTRY_IDS + 1 {
|
|
|
|
let last_id = hash(&serialize(&i).unwrap()); // Unique hash
|
|
|
|
bank.register_tick(&last_id);
|
|
|
|
}
|
|
|
|
assert_eq!(bank.tick_height(), MAX_ENTRY_IDS as u64 + 2);
|
|
|
|
assert_eq!(
|
2018-12-17 07:55:56 -08:00
|
|
|
reserve_signature_with_last_id_test(&bank, &signature, &alice.last_id()),
|
|
|
|
Err(StatusDequeError::LastIdNotFound)
|
2018-11-05 09:47:41 -08:00
|
|
|
);
|
|
|
|
bank.rollback();
|
|
|
|
assert_eq!(bank.tick_height(), 1);
|
|
|
|
assert_eq!(
|
2018-12-17 07:55:56 -08:00
|
|
|
reserve_signature_with_last_id_test(&bank, &signature, &alice.last_id()),
|
2018-11-05 09:47:41 -08:00
|
|
|
Ok(())
|
|
|
|
);
|
|
|
|
bank.checkpoint();
|
|
|
|
assert_eq!(
|
2018-12-17 07:55:56 -08:00
|
|
|
reserve_signature_with_last_id_test(&bank, &signature, &alice.last_id()),
|
|
|
|
Err(StatusDequeError::DuplicateSignature)
|
2018-11-05 09:47:41 -08:00
|
|
|
);
|
|
|
|
}
|
2018-11-05 16:43:27 -08:00
|
|
|
|
2018-11-05 09:47:41 -08:00
|
|
|
#[test]
|
|
|
|
#[should_panic]
|
2018-11-05 16:43:27 -08:00
|
|
|
fn test_bank_rollback_panic() {
|
2018-11-05 09:47:41 -08:00
|
|
|
let alice = Mint::new(10_000);
|
|
|
|
let bank = Bank::new(&alice);
|
|
|
|
bank.rollback();
|
|
|
|
}
|
|
|
|
|
2019-01-17 09:32:42 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_record_transactions() {
|
|
|
|
let mint = Mint::new(10_000);
|
|
|
|
let bank = Arc::new(Bank::new(&mint));
|
|
|
|
let (entry_sender, entry_receiver) = channel();
|
|
|
|
let poh_recorder = PohRecorder::new(bank.clone(), entry_sender, bank.last_id(), None);
|
|
|
|
let pubkey = Keypair::new().pubkey();
|
|
|
|
|
|
|
|
let transactions = vec![
|
|
|
|
Transaction::system_move(&mint.keypair(), pubkey, 1, mint.last_id(), 0),
|
|
|
|
Transaction::system_move(&mint.keypair(), pubkey, 1, mint.last_id(), 0),
|
|
|
|
];
|
|
|
|
|
|
|
|
let mut results = vec![Ok(()), Ok(())];
|
|
|
|
bank.record_transactions(&transactions, &results, &poh_recorder)
|
|
|
|
.unwrap();
|
|
|
|
let entries = entry_receiver.recv().unwrap();
|
|
|
|
assert_eq!(entries[0].transactions.len(), transactions.len());
|
|
|
|
|
|
|
|
// ProgramErrors should still be recorded
|
|
|
|
results[0] = Err(BankError::ProgramError(
|
|
|
|
1,
|
|
|
|
ProgramError::ResultWithNegativeTokens,
|
|
|
|
));
|
|
|
|
bank.record_transactions(&transactions, &results, &poh_recorder)
|
|
|
|
.unwrap();
|
|
|
|
let entries = entry_receiver.recv().unwrap();
|
|
|
|
assert_eq!(entries[0].transactions.len(), transactions.len());
|
|
|
|
|
|
|
|
// Other BankErrors should not be recorded
|
|
|
|
results[0] = Err(BankError::AccountNotFound);
|
|
|
|
bank.record_transactions(&transactions, &results, &poh_recorder)
|
|
|
|
.unwrap();
|
|
|
|
let entries = entry_receiver.recv().unwrap();
|
|
|
|
assert_eq!(entries[0].transactions.len(), transactions.len() - 1);
|
|
|
|
}
|
2019-01-17 14:41:48 -08:00
|
|
|
|
2019-01-24 13:37:12 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_ignore_program_errors() {
|
|
|
|
let expected_results = vec![Ok(()), Ok(())];
|
|
|
|
let results = vec![Ok(()), Ok(())];
|
|
|
|
let updated_results = Bank::ignore_program_errors(results);
|
|
|
|
assert_eq!(updated_results, expected_results);
|
|
|
|
|
|
|
|
let results = vec![
|
|
|
|
Err(BankError::ProgramError(
|
|
|
|
1,
|
|
|
|
ProgramError::ResultWithNegativeTokens,
|
|
|
|
)),
|
|
|
|
Ok(()),
|
|
|
|
];
|
|
|
|
let updated_results = Bank::ignore_program_errors(results);
|
|
|
|
assert_eq!(updated_results, expected_results);
|
|
|
|
|
|
|
|
// Other BankErrors should not be ignored
|
|
|
|
let results = vec![Err(BankError::AccountNotFound), Ok(())];
|
|
|
|
let updated_results = Bank::ignore_program_errors(results);
|
|
|
|
assert_ne!(updated_results, expected_results);
|
|
|
|
}
|
|
|
|
|
2019-01-17 14:41:48 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_storage() {
|
|
|
|
solana_logger::setup();
|
|
|
|
let alice = Mint::new(1000);
|
|
|
|
let bank = Bank::new(&alice);
|
|
|
|
|
|
|
|
let bob = Keypair::new();
|
|
|
|
let jack = Keypair::new();
|
|
|
|
let jill = Keypair::new();
|
|
|
|
|
|
|
|
let x = 42;
|
|
|
|
let last_id = hash(&[x]);
|
|
|
|
let x2 = x * 2;
|
|
|
|
let storage_last_id = hash(&[x2]);
|
|
|
|
|
|
|
|
bank.register_tick(&last_id);
|
|
|
|
|
|
|
|
bank.transfer(10, &alice.keypair(), jill.pubkey(), last_id)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
bank.transfer(10, &alice.keypair(), bob.pubkey(), last_id)
|
|
|
|
.unwrap();
|
|
|
|
bank.transfer(10, &alice.keypair(), jack.pubkey(), last_id)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let tx = Transaction::storage_new_advertise_last_id(
|
|
|
|
&bob,
|
|
|
|
storage_last_id,
|
|
|
|
last_id,
|
|
|
|
ENTRIES_PER_SEGMENT,
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(bank.process_transaction(&tx).is_ok());
|
|
|
|
|
|
|
|
let entry_height = 0;
|
|
|
|
|
|
|
|
let tx = Transaction::storage_new_mining_proof(
|
|
|
|
&jack,
|
|
|
|
Hash::default(),
|
|
|
|
last_id,
|
|
|
|
entry_height,
|
|
|
|
Signature::default(),
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(bank.process_transaction(&tx).is_ok());
|
|
|
|
|
|
|
|
assert_eq!(bank.get_storage_entry_height(), ENTRIES_PER_SEGMENT);
|
|
|
|
assert_eq!(bank.get_storage_last_id(), storage_last_id);
|
|
|
|
assert_eq!(bank.get_pubkeys_for_entry_height(0), vec![]);
|
|
|
|
}
|
2019-01-21 10:17:04 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bank_process_and_record_transactions() {
|
|
|
|
let mint = Mint::new(10_000);
|
|
|
|
let bank = Arc::new(Bank::new(&mint));
|
|
|
|
let pubkey = Keypair::new().pubkey();
|
|
|
|
|
|
|
|
let transactions = vec![Transaction::system_move(
|
|
|
|
&mint.keypair(),
|
|
|
|
pubkey,
|
|
|
|
1,
|
|
|
|
mint.last_id(),
|
|
|
|
0,
|
|
|
|
)];
|
|
|
|
|
|
|
|
let (entry_sender, entry_receiver) = channel();
|
|
|
|
let mut poh_recorder = PohRecorder::new(
|
|
|
|
bank.clone(),
|
|
|
|
entry_sender,
|
|
|
|
bank.last_id(),
|
|
|
|
Some(bank.tick_height() + 1),
|
|
|
|
);
|
|
|
|
|
|
|
|
bank.process_and_record_transactions(&transactions, &poh_recorder)
|
|
|
|
.unwrap();
|
|
|
|
poh_recorder.tick().unwrap();
|
|
|
|
|
|
|
|
let mut need_tick = true;
|
|
|
|
// read entries until I find mine, might be ticks...
|
|
|
|
while need_tick {
|
|
|
|
let entries = entry_receiver.recv().unwrap();
|
|
|
|
for entry in entries {
|
|
|
|
if !entry.is_tick() {
|
|
|
|
assert_eq!(entry.transactions.len(), transactions.len());
|
|
|
|
assert_eq!(bank.get_balance(&pubkey), 1);
|
|
|
|
} else {
|
|
|
|
need_tick = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let transactions = vec![Transaction::system_move(
|
|
|
|
&mint.keypair(),
|
|
|
|
pubkey,
|
|
|
|
2,
|
|
|
|
mint.last_id(),
|
|
|
|
0,
|
|
|
|
)];
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
bank.process_and_record_transactions(&transactions, &poh_recorder),
|
|
|
|
Err(BankError::RecordFailure)
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(bank.get_balance(&pubkey), 1);
|
|
|
|
}
|
|
|
|
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|