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};
|
2019-02-18 22:26:22 -08:00
|
|
|
use crate::last_id_queue::LastIdQueue;
|
|
|
|
use crate::runtime::{self, RuntimeError};
|
2019-01-31 06:53:52 -08:00
|
|
|
use crate::status_cache::StatusCache;
|
2019-02-18 10:23:09 -08:00
|
|
|
use bincode::{deserialize, serialize};
|
2019-02-18 22:26:22 -08:00
|
|
|
use log::{debug, info, Level};
|
|
|
|
use solana_metrics::counter::Counter;
|
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;
|
2019-02-18 22:26:22 -08:00
|
|
|
use solana_sdk::genesis_block::GenesisBlock;
|
2019-02-18 10:23:09 -08:00
|
|
|
use solana_sdk::hash::{extend_and_hash, Hash};
|
2019-02-07 08:03:40 -08:00
|
|
|
use solana_sdk::native_loader;
|
2018-12-03 12:26:23 -08:00
|
|
|
use solana_sdk::native_program::ProgramError;
|
2018-10-25 11:13:08 -07:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2019-02-18 19:28:10 -08:00
|
|
|
use solana_sdk::signature::{Keypair, Signature};
|
2018-12-04 08:20:41 -08:00
|
|
|
use solana_sdk::storage_program;
|
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;
|
2019-02-18 22:26:22 -08:00
|
|
|
use solana_sdk::timing::{duration_as_us, MAX_ENTRY_IDS, NUM_TICKS_PER_SECOND};
|
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;
|
2019-02-16 06:28:58 -08:00
|
|
|
use solana_sdk::vote_program::{self, VoteState};
|
2018-03-02 09:16:39 -08:00
|
|
|
use std::result;
|
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,
|
|
|
|
|
2019-02-07 11:14:10 -08:00
|
|
|
/// Pubkey appears twice in the same transaction, typically in a pay-to-self
|
|
|
|
/// transaction.
|
|
|
|
AccountLoadedTwice,
|
|
|
|
|
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
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
type BankStatusCache = StatusCache<BankError>;
|
|
|
|
|
2018-11-25 21:56:48 -08:00
|
|
|
/// Manager for the state of all accounts and programs after processing its entries.
|
2019-02-19 15:35:02 -08:00
|
|
|
#[derive(Default)]
|
2018-05-14 14:33:11 -07:00
|
|
|
pub struct Bank {
|
2019-02-16 06:28:58 -08:00
|
|
|
accounts: Accounts,
|
2018-10-04 13:15:54 -07:00
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
/// A cache of signature statuses
|
|
|
|
status_cache: RwLock<BankStatusCache>,
|
|
|
|
|
2018-10-17 16:28:26 -07:00
|
|
|
/// FIFO queue of `last_id` items
|
2019-01-31 06:53:52 -08:00
|
|
|
last_id_queue: RwLock<LastIdQueue>,
|
2018-06-06 08:49:22 -07:00
|
|
|
|
2019-02-19 15:35:02 -08:00
|
|
|
/// Previous checkpoint of this bank
|
2019-02-16 19:58:07 -08:00
|
|
|
parent: Option<Arc<Bank>>,
|
2019-02-18 10:23:09 -08:00
|
|
|
|
2019-02-20 14:26:57 -08:00
|
|
|
/// Hash of this Bank's state. Only meaningful after freezing it via `new_from_parent()`.
|
|
|
|
hash: RwLock<Hash>,
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
|
|
|
|
2018-06-29 11:58:29 -07:00
|
|
|
impl Bank {
|
2019-02-16 10:03:39 -08:00
|
|
|
pub fn new(genesis_block: &GenesisBlock) -> Self {
|
2019-02-20 14:26:57 -08:00
|
|
|
let bank = Self::default();
|
2019-02-16 10:03:39 -08:00
|
|
|
bank.process_genesis_block(genesis_block);
|
|
|
|
bank.add_builtin_programs();
|
|
|
|
bank
|
|
|
|
}
|
|
|
|
|
2019-02-19 15:35:02 -08:00
|
|
|
/// Create a new bank that points to an immutable checkpoint of another bank.
|
2019-02-17 13:14:34 -08:00
|
|
|
pub fn new_from_parent(parent: &Arc<Bank>) -> Self {
|
2019-02-16 19:58:07 -08:00
|
|
|
let mut bank = Self::default();
|
2019-02-17 08:01:31 -08:00
|
|
|
bank.last_id_queue = RwLock::new(parent.last_id_queue.read().unwrap().clone());
|
2019-02-20 14:26:57 -08:00
|
|
|
*parent.hash.write().unwrap() = parent.hash_internal_state();
|
2019-02-17 13:14:34 -08:00
|
|
|
bank.parent = Some(parent.clone());
|
2019-02-16 19:58:07 -08:00
|
|
|
bank
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the more recent checkpoint of this bank instance.
|
|
|
|
pub fn parent(&self) -> Option<Arc<Bank>> {
|
|
|
|
self.parent.clone()
|
|
|
|
}
|
|
|
|
|
2019-02-18 10:23:09 -08:00
|
|
|
fn process_genesis_block(&self, genesis_block: &GenesisBlock) {
|
2019-01-24 12:04:04 -08:00
|
|
|
assert!(genesis_block.mint_id != Pubkey::default());
|
2019-02-05 08:03:52 -08:00
|
|
|
assert!(genesis_block.bootstrap_leader_id != Pubkey::default());
|
|
|
|
assert!(genesis_block.bootstrap_leader_vote_account_id != Pubkey::default());
|
2019-01-24 12:04:04 -08:00
|
|
|
assert!(genesis_block.tokens >= genesis_block.bootstrap_leader_tokens);
|
2019-02-05 08:03:52 -08:00
|
|
|
assert!(genesis_block.bootstrap_leader_tokens >= 2);
|
2018-11-02 14:32:05 -07:00
|
|
|
|
2019-02-20 06:27:39 -08:00
|
|
|
let mint_tokens = genesis_block.tokens - genesis_block.bootstrap_leader_tokens;
|
|
|
|
self.deposit(&genesis_block.mint_id, mint_tokens);
|
2019-02-05 08:03:52 -08:00
|
|
|
|
2019-02-20 06:27:39 -08:00
|
|
|
let bootstrap_leader_tokens = genesis_block.bootstrap_leader_tokens - 1;
|
|
|
|
self.deposit(&genesis_block.bootstrap_leader_id, bootstrap_leader_tokens);
|
2019-02-05 08:03:52 -08:00
|
|
|
|
|
|
|
// Construct a vote account for the bootstrap_leader such that the leader_scheduler
|
|
|
|
// will be forced to select it as the leader for height 0
|
|
|
|
let mut bootstrap_leader_vote_account = Account {
|
|
|
|
tokens: 1,
|
|
|
|
userdata: vec![0; vote_program::get_max_size() as usize],
|
|
|
|
owner: vote_program::id(),
|
|
|
|
executable: false,
|
2018-11-02 14:32:05 -07:00
|
|
|
};
|
2019-01-24 12:04:04 -08:00
|
|
|
|
2019-02-16 06:28:58 -08:00
|
|
|
let mut vote_state = VoteState::new(
|
2019-02-05 08:03:52 -08:00
|
|
|
genesis_block.bootstrap_leader_id,
|
|
|
|
genesis_block.bootstrap_leader_id,
|
|
|
|
);
|
|
|
|
vote_state.votes.push_back(vote_program::Vote::new(0));
|
|
|
|
vote_state
|
|
|
|
.serialize(&mut bootstrap_leader_vote_account.userdata)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
self.accounts.store_slow(
|
|
|
|
true,
|
|
|
|
&genesis_block.bootstrap_leader_vote_account_id,
|
|
|
|
&bootstrap_leader_vote_account,
|
|
|
|
);
|
2019-01-31 13:53:08 -08:00
|
|
|
|
|
|
|
self.last_id_queue
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.genesis_last_id(&genesis_block.last_id());
|
2018-04-02 12:51:44 -07:00
|
|
|
}
|
|
|
|
|
2019-02-19 08:36:39 -08:00
|
|
|
pub fn add_native_program(&self, name: &str, program_id: &Pubkey) {
|
|
|
|
let account = native_loader::create_program_account(name);
|
|
|
|
self.accounts.store_slow(true, program_id, &account);
|
|
|
|
}
|
2018-12-04 07:45:32 -08:00
|
|
|
|
2019-02-19 08:36:39 -08:00
|
|
|
fn add_builtin_programs(&self) {
|
|
|
|
self.add_native_program("solana_system_program", &system_program::id());
|
|
|
|
self.add_native_program("solana_vote_program", &vote_program::id());
|
|
|
|
self.add_native_program("solana_storage_program", &storage_program::id());
|
|
|
|
self.add_native_program("solana_bpf_loader", &bpf_loader::id());
|
|
|
|
self.add_native_program("solana_budget_program", &budget_program::id());
|
|
|
|
self.add_native_program("solana_erc20", &token_program::id());
|
2018-11-02 17:32:54 -07:00
|
|
|
|
2019-02-13 19:43:56 -08:00
|
|
|
let storage_system_account = Account::new(1, 16 * 1024, storage_program::system_id());
|
2019-01-17 14:41:48 -08:00
|
|
|
self.accounts
|
2019-01-29 16:33:28 -08:00
|
|
|
.store_slow(true, &storage_program::system_id(), &storage_system_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 {
|
2019-01-31 06:53:52 -08:00
|
|
|
self.last_id_queue
|
2018-10-17 16:28:26 -07:00
|
|
|
.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
|
|
|
}
|
|
|
|
|
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) {
|
2019-01-31 06:53:52 -08:00
|
|
|
self.status_cache.write().unwrap().clear();
|
2018-09-21 15:07:29 -07:00
|
|
|
}
|
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
fn update_transaction_statuses(&self, txs: &[Transaction], res: &[Result<()>]) {
|
|
|
|
let mut status_cache = self.status_cache.write().unwrap();
|
|
|
|
for (i, tx) in txs.iter().enumerate() {
|
|
|
|
match &res[i] {
|
|
|
|
Ok(_) => status_cache.add(&tx.signatures[0]),
|
|
|
|
Err(BankError::LastIdNotFound) => (),
|
|
|
|
Err(BankError::DuplicateSignature) => (),
|
|
|
|
Err(BankError::AccountNotFound) => (),
|
|
|
|
Err(e) => {
|
|
|
|
status_cache.add(&tx.signatures[0]);
|
|
|
|
status_cache.save_failure_status(&tx.signatures[0], e.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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> {
|
2019-01-31 06:53:52 -08:00
|
|
|
let last_ids = self.last_id_queue.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) {
|
2019-02-16 16:41:03 -08:00
|
|
|
let current_tick_height = {
|
|
|
|
//atomic register and read the tick
|
|
|
|
let mut last_id_queue = self.last_id_queue.write().unwrap();
|
|
|
|
inc_new_counter_info!("bank-register_tick-registered", 1);
|
|
|
|
last_id_queue.register_tick(last_id);
|
|
|
|
last_id_queue.tick_height
|
|
|
|
};
|
|
|
|
if current_tick_height % NUM_TICKS_PER_SECOND as u64 == 0 {
|
|
|
|
self.status_cache.write().unwrap().new_cache(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
|
|
|
|
2019-02-16 13:17:37 -08:00
|
|
|
pub fn lock_accounts(&self, txs: &[Transaction]) -> Vec<Result<()>> {
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.lock_accounts(txs)
|
|
|
|
}
|
2019-01-04 16:39:04 -08:00
|
|
|
|
2019-02-16 13:17:37 -08:00
|
|
|
pub fn unlock_accounts(&self, txs: &[Transaction], results: &[Result<()>]) {
|
2018-12-17 12:41:23 -08:00
|
|
|
self.accounts.unlock_accounts(txs, results)
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
|
|
|
|
2018-12-17 12:41:23 -08:00
|
|
|
fn load_accounts(
|
2019-01-29 16:33:28 -08:00
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
results: Vec<Result<()>>,
|
|
|
|
error_counters: &mut ErrorCounters,
|
|
|
|
) -> Vec<Result<(InstructionAccounts, InstructionLoaders)>> {
|
2019-02-17 08:01:31 -08:00
|
|
|
let parents = self.parents();
|
|
|
|
let mut accounts = vec![&self.accounts];
|
|
|
|
accounts.extend(parents.iter().map(|b| &b.accounts));
|
|
|
|
Accounts::load_accounts(&accounts, txs, results, error_counters)
|
2019-01-29 16:33:28 -08:00
|
|
|
}
|
2019-01-31 06:53:52 -08:00
|
|
|
fn check_age(
|
2018-12-17 12:41:23 -08:00
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
2019-01-06 22:06:55 -08:00
|
|
|
lock_results: Vec<Result<()>>,
|
2018-12-17 12:41:23 -08:00
|
|
|
max_age: usize,
|
|
|
|
error_counters: &mut ErrorCounters,
|
2019-01-29 16:33:28 -08:00
|
|
|
) -> Vec<Result<()>> {
|
2019-01-31 06:53:52 -08:00
|
|
|
let last_ids = self.last_id_queue.read().unwrap();
|
|
|
|
txs.iter()
|
|
|
|
.zip(lock_results.into_iter())
|
|
|
|
.map(|(tx, lock_res)| {
|
|
|
|
if lock_res.is_ok() && !last_ids.check_entry_id_age(tx.last_id, max_age) {
|
|
|
|
error_counters.reserve_last_id += 1;
|
|
|
|
Err(BankError::LastIdNotFound)
|
|
|
|
} else {
|
|
|
|
lock_res
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
fn check_signatures(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
lock_results: Vec<Result<()>>,
|
|
|
|
error_counters: &mut ErrorCounters,
|
|
|
|
) -> Vec<Result<()>> {
|
2019-02-17 08:01:31 -08:00
|
|
|
let parents = self.parents();
|
|
|
|
let mut caches = vec![self.status_cache.read().unwrap()];
|
|
|
|
caches.extend(parents.iter().map(|b| b.status_cache.read().unwrap()));
|
2019-01-29 16:33:28 -08:00
|
|
|
txs.iter()
|
|
|
|
.zip(lock_results.into_iter())
|
|
|
|
.map(|(tx, lock_res)| {
|
2019-02-17 08:01:31 -08:00
|
|
|
if lock_res.is_ok() && StatusCache::has_signature_all(&caches, &tx.signatures[0]) {
|
2019-01-31 06:53:52 -08:00
|
|
|
error_counters.duplicate_signature += 1;
|
|
|
|
Err(BankError::DuplicateSignature)
|
2019-01-29 16:33:28 -08:00
|
|
|
} else {
|
|
|
|
lock_res
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
2018-12-17 12:41:23 -08:00
|
|
|
}
|
2019-01-21 10:17:04 -08:00
|
|
|
#[allow(clippy::type_complexity)]
|
2019-02-16 14:02:21 -08:00
|
|
|
pub fn load_and_execute_transactions(
|
2018-10-04 13:15:54 -07:00
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
2019-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();
|
2019-01-31 06:53:52 -08:00
|
|
|
let age_results = self.check_age(txs, lock_results, max_age, &mut error_counters);
|
|
|
|
let sig_results = self.check_signatures(txs, age_results, &mut error_counters);
|
2019-01-29 16:33:28 -08:00
|
|
|
let mut loaded_accounts = self.load_accounts(txs, sig_results, &mut error_counters);
|
2018-11-23 18:50:24 -08:00
|
|
|
let tick_height = self.tick_height();
|
2018-11-08 15:23:14 -08:00
|
|
|
|
2018-09-07 20:18:36 -07:00
|
|
|
let load_elapsed = now.elapsed();
|
2018-06-22 10:44:31 -07:00
|
|
|
let now = Instant::now();
|
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)) => {
|
2019-02-18 22:26:22 -08:00
|
|
|
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-02-07 11:14:10 -08:00
|
|
|
if 0 != error_counters.account_loaded_twice {
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"bank-process_transactions-account_loaded_twice",
|
|
|
|
error_counters.account_loaded_twice
|
|
|
|
);
|
|
|
|
}
|
2019-01-21 10:17:04 -08:00
|
|
|
(loaded_accounts, executed)
|
|
|
|
}
|
|
|
|
|
2019-02-16 14:02:21 -08:00
|
|
|
pub fn commit_transactions(
|
2019-01-21 10:17:04 -08:00
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
loaded_accounts: &[Result<(InstructionAccounts, InstructionLoaders)>],
|
|
|
|
executed: &[Result<()>],
|
2019-02-19 17:11:43 -08:00
|
|
|
) {
|
2019-01-21 10:17:04 -08:00
|
|
|
let now = Instant::now();
|
2019-02-19 17:11:43 -08:00
|
|
|
self.accounts
|
2019-01-29 16:33:28 -08:00
|
|
|
.store_accounts(true, txs, executed, loaded_accounts);
|
2019-01-21 10:17:04 -08:00
|
|
|
|
|
|
|
// once committed there is no way to unroll
|
|
|
|
let write_elapsed = now.elapsed();
|
|
|
|
debug!(
|
|
|
|
"store: {}us txs_len={}",
|
|
|
|
duration_as_us(&write_elapsed),
|
|
|
|
txs.len(),
|
|
|
|
);
|
|
|
|
self.update_transaction_statuses(txs, &executed);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Process a batch of transactions.
|
|
|
|
#[must_use]
|
|
|
|
pub fn load_execute_and_commit_transactions(
|
|
|
|
&self,
|
|
|
|
txs: &[Transaction],
|
|
|
|
lock_results: Vec<Result<()>>,
|
|
|
|
max_age: usize,
|
2019-02-19 17:11:43 -08:00
|
|
|
) -> Vec<Result<()>> {
|
2019-01-21 10:17:04 -08:00
|
|
|
let (loaded_accounts, executed) =
|
|
|
|
self.load_and_execute_transactions(txs, lock_results, max_age);
|
|
|
|
|
2019-02-19 17:11:43 -08:00
|
|
|
self.commit_transactions(txs, &loaded_accounts, &executed);
|
|
|
|
executed
|
2018-10-04 13:15:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
pub fn process_transactions(&self, txs: &[Transaction]) -> Vec<Result<()>> {
|
2019-01-06 22:06:55 -08:00
|
|
|
let lock_results = self.lock_accounts(txs);
|
2019-02-19 17:11:43 -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-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> {
|
2019-02-01 07:36:35 -08:00
|
|
|
let tx = SystemTransaction::new_account(keypair, to, n, last_id, 0);
|
2018-10-26 14:43:34 -07:00
|
|
|
let signature = tx.signatures[0];
|
2018-08-09 08:26:21 -07:00
|
|
|
self.process_transaction(&tx).map(|_| signature)
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
|
|
|
|
2018-11-05 08:36:22 -08:00
|
|
|
pub fn read_balance(account: &Account) -> u64 {
|
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
|
|
|
}
|
|
|
|
|
2019-02-17 08:01:31 -08:00
|
|
|
/// Compute all the parents of the bank in order
|
|
|
|
fn parents(&self) -> Vec<Arc<Bank>> {
|
|
|
|
let mut parents = vec![];
|
|
|
|
let mut bank = self.parent();
|
|
|
|
while let Some(parent) = bank {
|
|
|
|
parents.push(parent.clone());
|
|
|
|
bank = parent.parent();
|
|
|
|
}
|
|
|
|
parents
|
|
|
|
}
|
|
|
|
|
2019-02-20 06:27:39 -08:00
|
|
|
pub fn deposit(&self, pubkey: &Pubkey, tokens: u64) {
|
|
|
|
let mut account = self.get_account(pubkey).unwrap_or_default();
|
|
|
|
account.tokens += tokens;
|
|
|
|
self.accounts.store_slow(true, pubkey, &account);
|
2019-02-19 15:41:40 -08:00
|
|
|
}
|
|
|
|
|
2018-08-10 21:13:34 -07:00
|
|
|
pub fn get_account(&self, pubkey: &Pubkey) -> Option<Account> {
|
2019-02-17 08:01:31 -08:00
|
|
|
let parents = self.parents();
|
|
|
|
let mut accounts = vec![&self.accounts];
|
|
|
|
accounts.extend(parents.iter().map(|b| &b.accounts));
|
|
|
|
Accounts::load_slow(&accounts, pubkey)
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
2018-05-14 05:49:48 -07:00
|
|
|
|
2019-02-18 14:21:23 -08:00
|
|
|
pub fn get_account_modified_since_parent(&self, pubkey: &Pubkey) -> Option<Account> {
|
|
|
|
Accounts::load_slow(&[&self.accounts], pubkey)
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
pub fn get_signature_status(&self, signature: &Signature) -> Option<Result<()>> {
|
2019-02-17 08:01:31 -08:00
|
|
|
let parents = self.parents();
|
|
|
|
let mut caches = vec![self.status_cache.read().unwrap()];
|
|
|
|
caches.extend(parents.iter().map(|b| b.status_cache.read().unwrap()));
|
|
|
|
StatusCache::get_signature_status_all(&caches, signature)
|
2018-09-26 14:57:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_signature(&self, signature: &Signature) -> bool {
|
2019-02-17 08:01:31 -08:00
|
|
|
let parents = self.parents();
|
|
|
|
let mut caches = vec![self.status_cache.read().unwrap()];
|
|
|
|
caches.extend(parents.iter().map(|b| b.status_cache.read().unwrap()));
|
|
|
|
StatusCache::has_signature_all(&caches, 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 {
|
2019-02-18 10:23:09 -08:00
|
|
|
// If there are no accounts, return the same hash as we did before
|
|
|
|
// checkpointing.
|
|
|
|
let accounts = &self.accounts.accounts_db.read().unwrap().accounts;
|
2019-02-20 14:26:57 -08:00
|
|
|
let parent_hash = match &self.parent {
|
|
|
|
None => Hash::default(),
|
|
|
|
Some(parent) => *parent.hash.read().unwrap(),
|
|
|
|
};
|
2019-02-18 10:23:09 -08:00
|
|
|
if accounts.is_empty() {
|
2019-02-20 14:26:57 -08:00
|
|
|
return parent_hash;
|
2019-02-18 10:23:09 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
let accounts_delta_hash = self.accounts.hash_internal_state();
|
2019-02-20 14:26:57 -08:00
|
|
|
extend_and_hash(&parent_hash, &serialize(&accounts_delta_hash).unwrap())
|
2018-09-07 12:55:30 -07:00
|
|
|
}
|
|
|
|
|
2019-02-16 06:28:58 -08:00
|
|
|
pub fn vote_states<F>(&self, cond: F) -> Vec<VoteState>
|
|
|
|
where
|
|
|
|
F: Fn(&VoteState) -> bool,
|
|
|
|
{
|
|
|
|
self.accounts
|
|
|
|
.accounts_db
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.accounts
|
|
|
|
.values()
|
|
|
|
.filter_map(|account| {
|
|
|
|
if vote_program::check_id(&account.owner) {
|
|
|
|
if let Ok(vote_state) = VoteState::deserialize(&account.userdata) {
|
|
|
|
if cond(&vote_state) {
|
|
|
|
return Some(vote_state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2018-11-05 09:47:41 -08:00
|
|
|
pub fn tick_height(&self) -> u64 {
|
2019-01-31 06:53:52 -08:00
|
|
|
self.last_id_queue.read().unwrap().tick_height
|
2018-10-25 16:58:40 -07:00
|
|
|
}
|
2019-01-31 19:21:02 -08:00
|
|
|
|
|
|
|
#[cfg(test)]
|
2019-01-31 19:47:21 -08:00
|
|
|
pub fn last_ids(&self) -> &RwLock<LastIdQueue> {
|
|
|
|
&self.last_id_queue
|
2019-01-31 19:21:02 -08:00
|
|
|
}
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2018-12-17 12:41:23 -08:00
|
|
|
use hashbrown::HashSet;
|
2019-02-18 22:26:22 -08:00
|
|
|
use solana_sdk::genesis_block::BOOTSTRAP_LEADER_TOKENS;
|
2018-12-17 07:55:56 -08:00
|
|
|
use solana_sdk::native_program::ProgramError;
|
2019-02-19 15:54:38 -08:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
2019-01-24 12:04:04 -08:00
|
|
|
use solana_sdk::system_instruction::SystemInstruction;
|
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-02-23 13:08:19 -08:00
|
|
|
|
2018-09-07 20:18:36 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bank_new() {
|
2019-01-24 12:04:04 -08:00
|
|
|
let (genesis_block, _) = GenesisBlock::new(10_000);
|
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-02-12 15:49:23 -08:00
|
|
|
assert_eq!(bank.get_balance(&genesis_block.mint_id), 10_000);
|
2018-09-07 20:18:36 -07:00
|
|
|
}
|
|
|
|
|
2018-11-02 14:32:05 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bank_new_with_leader() {
|
|
|
|
let dummy_leader_id = Keypair::new().pubkey();
|
2019-02-18 22:26:22 -08:00
|
|
|
let dummy_leader_tokens = BOOTSTRAP_LEADER_TOKENS;
|
2019-01-24 12:04:04 -08:00
|
|
|
let (genesis_block, _) =
|
|
|
|
GenesisBlock::new_with_leader(10_000, dummy_leader_id, dummy_leader_tokens);
|
2019-02-05 08:03:52 -08:00
|
|
|
assert_eq!(genesis_block.bootstrap_leader_tokens, dummy_leader_tokens);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-02-05 08:03:52 -08:00
|
|
|
assert_eq!(
|
|
|
|
bank.get_balance(&genesis_block.mint_id),
|
|
|
|
10_000 - dummy_leader_tokens
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
bank.get_balance(&dummy_leader_id),
|
|
|
|
dummy_leader_tokens - 1 /* 1 token goes to the vote account associated with dummy_leader_tokens */
|
|
|
|
);
|
2018-11-02 14:32:05 -07:00
|
|
|
}
|
|
|
|
|
2018-02-23 13:08:19 -08:00
|
|
|
#[test]
|
2018-06-06 08:49:22 -07:00
|
|
|
fn test_two_payments_to_one_party() {
|
2019-01-24 12:04:04 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(10_000);
|
2018-08-09 07:56:04 -07:00
|
|
|
let pubkey = Keypair::new().pubkey();
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
assert_eq!(bank.last_id(), genesis_block.last_id());
|
2018-05-03 12:24:35 -07:00
|
|
|
|
2019-01-24 12:04:04 -08:00
|
|
|
bank.transfer(1_000, &mint_keypair, pubkey, genesis_block.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
|
|
|
|
2019-01-24 12:04:04 -08:00
|
|
|
bank.transfer(500, &mint_keypair, pubkey, genesis_block.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() {
|
2019-02-12 15:49:23 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(1);
|
2018-09-28 16:16:35 -07:00
|
|
|
let key1 = Keypair::new().pubkey();
|
|
|
|
let key2 = Keypair::new().pubkey();
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
assert_eq!(bank.last_id(), genesis_block.last_id());
|
2018-10-04 13:15:54 -07:00
|
|
|
|
2019-02-01 07:36:35 -08:00
|
|
|
let t1 = SystemTransaction::new_move(&mint_keypair, key1, 1, genesis_block.last_id(), 0);
|
|
|
|
let t2 = SystemTransaction::new_move(&mint_keypair, key2, 1, genesis_block.last_id(), 0);
|
2018-10-04 13:15:54 -07:00
|
|
|
let res = bank.process_transactions(&vec![t1.clone(), t2.clone()]);
|
|
|
|
assert_eq!(res.len(), 2);
|
|
|
|
assert_eq!(res[0], Ok(()));
|
|
|
|
assert_eq!(res[1], Err(BankError::AccountInUse));
|
2019-01-24 12:04:04 -08:00
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 0);
|
2018-10-04 13:15:54 -07:00
|
|
|
assert_eq!(bank.get_balance(&key1), 1);
|
|
|
|
assert_eq!(bank.get_balance(&key2), 0);
|
2019-01-31 06:53:52 -08:00
|
|
|
assert_eq!(bank.get_signature_status(&t1.signatures[0]), Some(Ok(())));
|
2018-10-04 13:15:54 -07:00
|
|
|
// TODO: Transactions that fail to pay a fee could be dropped silently
|
|
|
|
assert_eq!(
|
2019-01-31 06:53:52 -08:00
|
|
|
bank.get_signature_status(&t2.signatures[0]),
|
|
|
|
Some(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() {
|
2019-02-12 15:49:23 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(1);
|
2018-10-04 13:15:54 -07:00
|
|
|
let key1 = Keypair::new().pubkey();
|
|
|
|
let key2 = Keypair::new().pubkey();
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
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(
|
2019-01-24 12:04:04 -08:00
|
|
|
&[&mint_keypair],
|
2018-09-28 16:16:35 -07:00
|
|
|
&[key1, key2],
|
2019-01-24 12:04:04 -08:00
|
|
|
genesis_block.last_id(),
|
2018-09-28 16:16:35 -07:00
|
|
|
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
|
|
|
|
))
|
|
|
|
);
|
2019-01-24 12:04:04 -08:00
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 1);
|
2018-09-28 16:16:35 -07:00
|
|
|
assert_eq!(bank.get_balance(&key1), 0);
|
|
|
|
assert_eq!(bank.get_balance(&key2), 0);
|
|
|
|
assert_eq!(
|
2019-01-31 06:53:52 -08:00
|
|
|
bank.get_signature_status(&t1.signatures[0]),
|
|
|
|
Some(Err(BankError::ProgramError(
|
2018-11-23 14:53:39 -08:00
|
|
|
1,
|
|
|
|
ProgramError::ResultWithNegativeTokens
|
2019-01-31 06:53:52 -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() {
|
2019-02-12 15:49:23 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(2);
|
2018-09-28 16:16:35 -07:00
|
|
|
let key1 = Keypair::new().pubkey();
|
|
|
|
let key2 = Keypair::new().pubkey();
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-02-01 07:36:35 -08:00
|
|
|
let t1 = SystemTransaction::new_move_many(
|
2019-01-24 12:04:04 -08:00
|
|
|
&mint_keypair,
|
2018-10-10 17:23:06 -07:00
|
|
|
&[(key1, 1), (key2, 1)],
|
2019-01-24 12:04:04 -08:00
|
|
|
genesis_block.last_id(),
|
2018-09-28 16:16:35 -07:00
|
|
|
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(()));
|
2019-01-24 12:04:04 -08:00
|
|
|
assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 0);
|
2018-09-28 16:16:35 -07:00
|
|
|
assert_eq!(bank.get_balance(&key1), 1);
|
|
|
|
assert_eq!(bank.get_balance(&key2), 1);
|
2019-01-31 06:53:52 -08:00
|
|
|
assert_eq!(bank.get_signature_status(&t1.signatures[0]), Some(Ok(())));
|
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() {
|
2019-02-12 15:49:23 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(2);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2018-09-17 13:36:31 -07:00
|
|
|
let dest = Keypair::new();
|
2018-09-07 20:18:36 -07:00
|
|
|
|
2018-11-25 21:56:48 -08:00
|
|
|
// source with 0 program context
|
2019-02-01 07:36:35 -08:00
|
|
|
let tx = SystemTransaction::new_account(
|
2019-01-24 12:04:04 -08:00
|
|
|
&mint_keypair,
|
2018-09-26 15:18:45 -07:00
|
|
|
dest.pubkey(),
|
|
|
|
2,
|
2019-02-01 07:36:35 -08:00
|
|
|
genesis_block.last_id(),
|
2018-09-26 15:18:45 -07:00
|
|
|
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));
|
2019-02-18 22:26:22 -08:00
|
|
|
assert_eq!(
|
2018-09-26 15:18:45 -07:00
|
|
|
bank.get_signature_status(&signature),
|
2019-01-31 06:53:52 -08:00
|
|
|
Some(Err(BankError::ProgramError(
|
2018-11-23 14:53:39 -08:00
|
|
|
0,
|
|
|
|
ProgramError::ResultWithNegativeTokens
|
2019-01-31 06:53:52 -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.
|
2019-01-24 12:04:04 -08:00
|
|
|
//assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 0);
|
2018-09-07 20:18:36 -07:00
|
|
|
}
|
|
|
|
|
2018-04-05 20:00:08 -07:00
|
|
|
#[test]
|
|
|
|
fn test_account_not_found() {
|
2019-02-12 15:49:23 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(0);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2018-08-09 07:56:04 -07:00
|
|
|
let keypair = Keypair::new();
|
2018-04-05 20:00:08 -07:00
|
|
|
assert_eq!(
|
2019-01-24 12:04:04 -08:00
|
|
|
bank.transfer(1, &keypair, mint_keypair.pubkey(), genesis_block.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() {
|
2019-02-12 15:49:23 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(11_000);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2018-08-09 07:56:04 -07:00
|
|
|
let pubkey = Keypair::new().pubkey();
|
2019-01-24 12:04:04 -08:00
|
|
|
bank.transfer(1_000, &mint_keypair, pubkey, genesis_block.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);
|
2019-02-18 22:26:22 -08:00
|
|
|
assert_eq!(
|
2019-01-24 12:04:04 -08:00
|
|
|
bank.transfer(10_001, &mint_keypair, pubkey, genesis_block.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
|
|
|
|
2019-01-24 12:04:04 -08:00
|
|
|
let mint_pubkey = mint_keypair.pubkey();
|
2018-07-02 16:31:10 -07:00
|
|
|
assert_eq!(bank.get_balance(&mint_pubkey), 10_000);
|
|
|
|
assert_eq!(bank.get_balance(&pubkey), 1_000);
|
2018-02-27 10:28:10 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_transfer_to_newb() {
|
2019-01-24 12:04:04 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(10_000);
|
|
|
|
let bank = Bank::new(&genesis_block);
|
2018-08-09 07:56:04 -07:00
|
|
|
let pubkey = Keypair::new().pubkey();
|
2019-01-24 12:04:04 -08:00
|
|
|
bank.transfer(500, &mint_keypair, pubkey, genesis_block.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() {
|
2019-02-12 15:49:23 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(2);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2018-08-09 07:56:04 -07:00
|
|
|
let keypair = Keypair::new();
|
2019-02-01 07:36:35 -08:00
|
|
|
let tx0 = SystemTransaction::new_account(
|
|
|
|
&mint_keypair,
|
|
|
|
keypair.pubkey(),
|
|
|
|
2,
|
|
|
|
genesis_block.last_id(),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
let tx1 = SystemTransaction::new_account(
|
|
|
|
&keypair,
|
|
|
|
mint_keypair.pubkey(),
|
|
|
|
1,
|
|
|
|
genesis_block.last_id(),
|
|
|
|
0,
|
|
|
|
);
|
2018-05-29 09:12:27 -07:00
|
|
|
let txs = vec![tx0, tx1];
|
2018-09-21 14:48:10 -07:00
|
|
|
let results = bank.process_transactions(&txs);
|
2018-05-14 05:49:48 -07:00
|
|
|
assert!(results[1].is_err());
|
|
|
|
|
|
|
|
// Assert bad transactions aren't counted.
|
2018-05-14 14:33:11 -07:00
|
|
|
assert_eq!(bank.transaction_count(), 1);
|
2018-04-12 19:53:34 -07:00
|
|
|
}
|
2018-06-14 16:32:39 -07:00
|
|
|
|
2018-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();
|
2019-02-05 08:03:52 -08:00
|
|
|
let dummy_leader_tokens = 2;
|
2019-01-24 12:04:04 -08:00
|
|
|
let (genesis_block, _) =
|
|
|
|
GenesisBlock::new_with_leader(5, dummy_leader_id, dummy_leader_tokens);
|
2019-02-05 08:03:52 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
|
|
|
assert_eq!(bank.get_balance(&genesis_block.mint_id), 3);
|
2018-11-02 14:32:05 -07:00
|
|
|
assert_eq!(bank.get_balance(&dummy_leader_id), 1);
|
2018-07-01 21:06:40 -07:00
|
|
|
}
|
|
|
|
|
2018-09-07 12:55:30 -07:00
|
|
|
#[test]
|
2018-10-04 13:15:54 -07:00
|
|
|
fn test_interleaving_locks() {
|
2019-02-12 15:49:23 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(3);
|
2019-01-24 12:04:04 -08:00
|
|
|
let bank = Bank::new(&genesis_block);
|
2018-10-04 13:15:54 -07:00
|
|
|
let alice = Keypair::new();
|
|
|
|
let bob = Keypair::new();
|
|
|
|
|
2019-02-01 07:36:35 -08:00
|
|
|
let tx1 = SystemTransaction::new_account(
|
|
|
|
&mint_keypair,
|
|
|
|
alice.pubkey(),
|
|
|
|
1,
|
|
|
|
genesis_block.last_id(),
|
|
|
|
0,
|
|
|
|
);
|
2018-10-04 13:15:54 -07:00
|
|
|
let pay_alice = vec![tx1];
|
|
|
|
|
2019-01-06 22:06:55 -08:00
|
|
|
let lock_result = bank.lock_accounts(&pay_alice);
|
2019-02-19 17:11:43 -08: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!(
|
2019-01-24 12:04:04 -08:00
|
|
|
bank.transfer(1, &mint_keypair, bob.pubkey(), genesis_block.last_id()),
|
2018-10-04 13:15:54 -07:00
|
|
|
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!(
|
2019-01-24 12:04:04 -08:00
|
|
|
bank.transfer(1, &mint_keypair, bob.pubkey(), genesis_block.last_id()),
|
2018-10-04 13:15:54 -07:00
|
|
|
Err(BankError::AccountInUse)
|
|
|
|
);
|
|
|
|
|
|
|
|
bank.unlock_accounts(&pay_alice, &results_alice);
|
|
|
|
|
2019-02-18 22:26:22 -08:00
|
|
|
assert!(bank
|
|
|
|
.transfer(2, &mint_keypair, bob.pubkey(), genesis_block.last_id())
|
|
|
|
.is_ok());
|
2018-10-04 13:15:54 -07:00
|
|
|
}
|
2018-10-10 13:51:43 -07:00
|
|
|
|
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);
|
2019-02-07 08:03:40 -08:00
|
|
|
assert_eq!(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(),
|
2019-02-07 08:03:40 -08:00
|
|
|
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
|
|
|
|
2019-02-07 11:14:10 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_pay_to_self() {
|
2019-02-12 15:49:23 -08:00
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(1);
|
2019-02-07 11:14:10 -08:00
|
|
|
let key1 = Keypair::new();
|
|
|
|
let bank = Bank::new(&genesis_block);
|
2019-01-21 10:17:04 -08:00
|
|
|
|
2019-02-07 11:14:10 -08:00
|
|
|
bank.transfer(1, &mint_keypair, key1.pubkey(), genesis_block.last_id())
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(bank.get_balance(&key1.pubkey()), 1);
|
|
|
|
let tx = SystemTransaction::new_move(&key1, key1.pubkey(), 1, genesis_block.last_id(), 0);
|
|
|
|
let res = bank.process_transactions(&vec![tx.clone()]);
|
|
|
|
assert_eq!(res.len(), 1);
|
|
|
|
assert_eq!(bank.get_balance(&key1.pubkey()), 1);
|
|
|
|
res[0].clone().unwrap_err();
|
|
|
|
}
|
2019-02-17 08:01:31 -08:00
|
|
|
|
2019-02-19 15:35:02 -08:00
|
|
|
/// Verify that the parent's vector is computed correctly
|
2019-02-17 08:01:31 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_parents() {
|
|
|
|
let (genesis_block, _) = GenesisBlock::new(1);
|
|
|
|
let parent = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
2019-02-17 13:14:34 -08:00
|
|
|
let bank = Bank::new_from_parent(&parent);
|
2019-02-17 08:01:31 -08:00
|
|
|
assert!(Arc::ptr_eq(&bank.parents()[0], &parent));
|
|
|
|
}
|
|
|
|
|
2019-02-19 15:35:02 -08:00
|
|
|
/// Verifies that last ids and status cache are correctly referenced from parent
|
2019-02-17 08:01:31 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_parent_duplicate_signature() {
|
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(2);
|
|
|
|
let key1 = Keypair::new();
|
|
|
|
let parent = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
|
|
|
let tx = SystemTransaction::new_move(
|
|
|
|
&mint_keypair,
|
|
|
|
key1.pubkey(),
|
|
|
|
1,
|
|
|
|
genesis_block.last_id(),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
assert_eq!(parent.process_transaction(&tx), Ok(()));
|
2019-02-17 13:14:34 -08:00
|
|
|
let bank = Bank::new_from_parent(&parent);
|
2019-02-17 08:01:31 -08:00
|
|
|
assert_eq!(
|
|
|
|
bank.process_transaction(&tx),
|
|
|
|
Err(BankError::DuplicateSignature)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-19 15:35:02 -08:00
|
|
|
/// Verifies that last ids and accounts are correctly referenced from parent
|
2019-02-17 08:01:31 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bank_parent_account_spend() {
|
|
|
|
let (genesis_block, mint_keypair) = GenesisBlock::new(2);
|
|
|
|
let key1 = Keypair::new();
|
|
|
|
let key2 = Keypair::new();
|
|
|
|
let parent = Arc::new(Bank::new(&genesis_block));
|
|
|
|
|
|
|
|
let tx = SystemTransaction::new_move(
|
|
|
|
&mint_keypair,
|
|
|
|
key1.pubkey(),
|
|
|
|
1,
|
|
|
|
genesis_block.last_id(),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
assert_eq!(parent.process_transaction(&tx), Ok(()));
|
2019-02-17 13:14:34 -08:00
|
|
|
let bank = Bank::new_from_parent(&parent);
|
2019-02-17 08:01:31 -08:00
|
|
|
let tx = SystemTransaction::new_move(&key1, key2.pubkey(), 1, genesis_block.last_id(), 0);
|
|
|
|
assert_eq!(bank.process_transaction(&tx), Ok(()));
|
2019-02-17 13:14:34 -08:00
|
|
|
assert_eq!(parent.get_signature_status(&tx.signatures[0]), None);
|
2019-02-17 08:01:31 -08:00
|
|
|
}
|
|
|
|
|
2018-02-23 13:08:19 -08:00
|
|
|
}
|