2021-12-03 09:00:31 -08:00
|
|
|
use {
|
|
|
|
crate::bank::Bank,
|
2022-01-11 02:44:46 -08:00
|
|
|
crossbeam_channel::{unbounded, Receiver, Sender},
|
2021-12-03 09:00:31 -08:00
|
|
|
solana_sdk::{
|
|
|
|
account::Account,
|
|
|
|
client::{AsyncClient, Client, SyncClient},
|
|
|
|
commitment_config::CommitmentConfig,
|
|
|
|
epoch_info::EpochInfo,
|
|
|
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
|
|
|
hash::Hash,
|
|
|
|
instruction::Instruction,
|
|
|
|
message::{Message, SanitizedMessage},
|
|
|
|
pubkey::Pubkey,
|
|
|
|
signature::{Keypair, Signature, Signer},
|
|
|
|
signers::Signers,
|
|
|
|
system_instruction,
|
2022-04-01 20:12:02 -07:00
|
|
|
transaction::{self, Transaction, VersionedTransaction},
|
2021-12-03 09:00:31 -08:00
|
|
|
transport::{Result, TransportError},
|
|
|
|
},
|
|
|
|
std::{
|
|
|
|
convert::TryFrom,
|
|
|
|
io,
|
2022-01-11 02:44:46 -08:00
|
|
|
sync::{Arc, Mutex},
|
2021-12-03 09:00:31 -08:00
|
|
|
thread::{sleep, Builder},
|
|
|
|
time::{Duration, Instant},
|
2019-11-06 13:15:00 -08:00
|
|
|
},
|
|
|
|
};
|
2019-03-14 19:42:01 -07:00
|
|
|
|
2019-04-11 11:29:59 -07:00
|
|
|
pub struct BankClient {
|
2019-04-19 06:29:07 -07:00
|
|
|
bank: Arc<Bank>,
|
2022-04-01 20:12:02 -07:00
|
|
|
transaction_sender: Mutex<Sender<VersionedTransaction>>,
|
2019-03-14 19:42:01 -07:00
|
|
|
}
|
|
|
|
|
2019-04-19 14:04:36 -07:00
|
|
|
impl Client for BankClient {
|
2019-09-06 09:07:40 -07:00
|
|
|
fn tpu_addr(&self) -> String {
|
2019-04-19 14:04:36 -07:00
|
|
|
"Local BankClient".to_string()
|
|
|
|
}
|
|
|
|
}
|
2019-04-19 06:29:07 -07:00
|
|
|
|
2019-04-11 11:29:59 -07:00
|
|
|
impl AsyncClient for BankClient {
|
2022-04-01 20:12:02 -07:00
|
|
|
fn async_send_versioned_transaction(
|
|
|
|
&self,
|
|
|
|
transaction: VersionedTransaction,
|
|
|
|
) -> Result<Signature> {
|
2019-04-19 06:29:07 -07:00
|
|
|
let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
|
2019-04-19 13:18:20 -07:00
|
|
|
let transaction_sender = self.transaction_sender.lock().unwrap();
|
|
|
|
transaction_sender.send(transaction).unwrap();
|
2019-04-19 06:29:07 -07:00
|
|
|
Ok(signature)
|
2019-04-05 09:26:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-11 11:29:59 -07:00
|
|
|
impl SyncClient for BankClient {
|
2020-06-24 20:35:38 -07:00
|
|
|
fn send_and_confirm_message<T: Signers>(
|
|
|
|
&self,
|
|
|
|
keypairs: &T,
|
|
|
|
message: Message,
|
|
|
|
) -> Result<Signature> {
|
2019-03-27 04:36:01 -07:00
|
|
|
let blockhash = self.bank.last_blockhash();
|
2020-02-20 12:13:23 -08:00
|
|
|
let transaction = Transaction::new(keypairs, message, blockhash);
|
2019-04-03 15:36:10 -07:00
|
|
|
self.bank.process_transaction(&transaction)?;
|
|
|
|
Ok(transaction.signatures.get(0).cloned().unwrap_or_default())
|
2019-03-16 13:30:10 -07:00
|
|
|
}
|
|
|
|
|
2019-03-17 08:55:42 -07:00
|
|
|
/// Create and process a transaction from a single instruction.
|
2020-06-24 20:35:38 -07:00
|
|
|
fn send_and_confirm_instruction(
|
|
|
|
&self,
|
|
|
|
keypair: &Keypair,
|
|
|
|
instruction: Instruction,
|
|
|
|
) -> Result<Signature> {
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(&[instruction], Some(&keypair.pubkey()));
|
2020-06-24 20:35:38 -07:00
|
|
|
self.send_and_confirm_message(&[keypair], message)
|
2019-03-16 13:30:10 -07:00
|
|
|
}
|
|
|
|
|
2019-03-27 06:34:01 -07:00
|
|
|
/// Transfer `lamports` from `keypair` to `pubkey`
|
2020-06-24 20:35:38 -07:00
|
|
|
fn transfer_and_confirm(
|
|
|
|
&self,
|
|
|
|
lamports: u64,
|
|
|
|
keypair: &Keypair,
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
) -> Result<Signature> {
|
2019-04-05 09:26:48 -07:00
|
|
|
let transfer_instruction =
|
|
|
|
system_instruction::transfer(&keypair.pubkey(), pubkey, lamports);
|
2020-06-24 20:35:38 -07:00
|
|
|
self.send_and_confirm_instruction(keypair, transfer_instruction)
|
2019-04-03 14:11:08 -07:00
|
|
|
}
|
2019-04-03 15:36:10 -07:00
|
|
|
|
2019-04-03 20:40:29 -07:00
|
|
|
fn get_account_data(&self, pubkey: &Pubkey) -> Result<Option<Vec<u8>>> {
|
2021-03-15 17:27:53 -07:00
|
|
|
Ok(self
|
|
|
|
.bank
|
|
|
|
.get_account(pubkey)
|
|
|
|
.map(|account| Account::from(account).data))
|
2019-04-03 15:36:10 -07:00
|
|
|
}
|
|
|
|
|
2019-06-13 22:30:51 -07:00
|
|
|
fn get_account(&self, pubkey: &Pubkey) -> Result<Option<Account>> {
|
2021-03-09 13:06:07 -08:00
|
|
|
Ok(self.bank.get_account(pubkey).map(Account::from))
|
2019-06-13 22:30:51 -07:00
|
|
|
}
|
|
|
|
|
2019-11-06 13:15:00 -08:00
|
|
|
fn get_account_with_commitment(
|
|
|
|
&self,
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
_commitment_config: CommitmentConfig,
|
|
|
|
) -> Result<Option<Account>> {
|
2021-03-09 13:06:07 -08:00
|
|
|
Ok(self.bank.get_account(pubkey).map(Account::from))
|
2019-11-06 13:15:00 -08:00
|
|
|
}
|
|
|
|
|
2019-04-03 20:40:29 -07:00
|
|
|
fn get_balance(&self, pubkey: &Pubkey) -> Result<u64> {
|
|
|
|
Ok(self.bank.get_balance(pubkey))
|
2019-04-03 15:36:10 -07:00
|
|
|
}
|
2019-04-05 09:42:54 -07:00
|
|
|
|
2019-11-06 13:15:00 -08:00
|
|
|
fn get_balance_with_commitment(
|
|
|
|
&self,
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
_commitment_config: CommitmentConfig,
|
|
|
|
) -> Result<u64> {
|
|
|
|
Ok(self.bank.get_balance(pubkey))
|
|
|
|
}
|
|
|
|
|
2020-12-10 16:39:28 -08:00
|
|
|
fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> Result<u64> {
|
|
|
|
Ok(self.bank.get_minimum_balance_for_rent_exemption(data_len))
|
|
|
|
}
|
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
fn get_recent_blockhash(&self) -> Result<(Hash, FeeCalculator)> {
|
2021-10-22 14:32:40 -07:00
|
|
|
Ok((
|
|
|
|
self.bank.last_blockhash(),
|
|
|
|
FeeCalculator::new(self.bank.get_lamports_per_signature()),
|
|
|
|
))
|
2019-06-12 16:43:05 -07:00
|
|
|
}
|
|
|
|
|
2019-11-06 13:15:00 -08:00
|
|
|
fn get_recent_blockhash_with_commitment(
|
|
|
|
&self,
|
|
|
|
_commitment_config: CommitmentConfig,
|
2020-06-09 17:07:32 -07:00
|
|
|
) -> Result<(Hash, FeeCalculator, u64)> {
|
2021-10-22 14:32:40 -07:00
|
|
|
let blockhash = self.bank.last_blockhash();
|
2021-08-11 00:04:00 -07:00
|
|
|
#[allow(deprecated)]
|
2020-06-09 17:07:32 -07:00
|
|
|
let last_valid_slot = self
|
|
|
|
.bank
|
|
|
|
.get_blockhash_last_valid_slot(&blockhash)
|
|
|
|
.expect("bank blockhash queue should contain blockhash");
|
2021-10-22 14:32:40 -07:00
|
|
|
Ok((
|
|
|
|
blockhash,
|
|
|
|
FeeCalculator::new(self.bank.get_lamports_per_signature()),
|
|
|
|
last_valid_slot,
|
|
|
|
))
|
2019-11-06 13:15:00 -08:00
|
|
|
}
|
|
|
|
|
2020-03-06 16:01:31 -08:00
|
|
|
fn get_fee_calculator_for_blockhash(&self, blockhash: &Hash) -> Result<Option<FeeCalculator>> {
|
2021-10-22 14:32:40 -07:00
|
|
|
Ok(self
|
|
|
|
.bank
|
|
|
|
.get_lamports_per_signature_for_blockhash(blockhash)
|
|
|
|
.map(FeeCalculator::new))
|
2020-03-06 16:01:31 -08:00
|
|
|
}
|
|
|
|
|
2020-02-28 12:27:01 -08:00
|
|
|
fn get_fee_rate_governor(&self) -> Result<FeeRateGovernor> {
|
2021-08-13 09:08:20 -07:00
|
|
|
#[allow(deprecated)]
|
2020-02-28 12:27:01 -08:00
|
|
|
Ok(self.bank.get_fee_rate_governor().clone())
|
|
|
|
}
|
|
|
|
|
2019-04-05 09:42:54 -07:00
|
|
|
fn get_signature_status(
|
|
|
|
&self,
|
|
|
|
signature: &Signature,
|
|
|
|
) -> Result<Option<transaction::Result<()>>> {
|
|
|
|
Ok(self.bank.get_signature_status(signature))
|
|
|
|
}
|
2019-04-11 00:25:14 -07:00
|
|
|
|
2019-11-06 13:15:00 -08:00
|
|
|
fn get_signature_status_with_commitment(
|
|
|
|
&self,
|
|
|
|
signature: &Signature,
|
|
|
|
_commitment_config: CommitmentConfig,
|
|
|
|
) -> Result<Option<transaction::Result<()>>> {
|
|
|
|
Ok(self.bank.get_signature_status(signature))
|
|
|
|
}
|
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
fn get_slot(&self) -> Result<u64> {
|
|
|
|
Ok(self.bank.slot())
|
2019-04-11 00:25:14 -07:00
|
|
|
}
|
|
|
|
|
2019-11-06 13:15:00 -08:00
|
|
|
fn get_slot_with_commitment(&self, _commitment_config: CommitmentConfig) -> Result<u64> {
|
|
|
|
Ok(self.bank.slot())
|
|
|
|
}
|
|
|
|
|
2019-04-11 00:25:14 -07:00
|
|
|
fn get_transaction_count(&self) -> Result<u64> {
|
|
|
|
Ok(self.bank.transaction_count())
|
|
|
|
}
|
2019-04-25 12:46:40 -07:00
|
|
|
|
2019-11-06 13:15:00 -08:00
|
|
|
fn get_transaction_count_with_commitment(
|
|
|
|
&self,
|
|
|
|
_commitment_config: CommitmentConfig,
|
|
|
|
) -> Result<u64> {
|
|
|
|
Ok(self.bank.transaction_count())
|
|
|
|
}
|
|
|
|
|
2019-04-25 12:46:40 -07:00
|
|
|
fn poll_for_signature_confirmation(
|
|
|
|
&self,
|
|
|
|
signature: &Signature,
|
|
|
|
min_confirmed_blocks: usize,
|
2019-07-02 20:56:10 -07:00
|
|
|
) -> Result<usize> {
|
2020-03-30 16:53:25 -07:00
|
|
|
// https://github.com/solana-labs/solana/issues/7199
|
|
|
|
assert_eq!(min_confirmed_blocks, 1, "BankClient cannot observe the passage of multiple blocks, so min_confirmed_blocks must be 1");
|
|
|
|
let now = Instant::now();
|
|
|
|
let confirmed_blocks;
|
2019-04-25 12:46:40 -07:00
|
|
|
loop {
|
2020-03-30 16:53:25 -07:00
|
|
|
if self.bank.get_signature_status(signature).is_some() {
|
|
|
|
confirmed_blocks = 1;
|
|
|
|
break;
|
|
|
|
}
|
2019-04-25 12:46:40 -07:00
|
|
|
if now.elapsed().as_secs() > 15 {
|
|
|
|
return Err(TransportError::IoError(io::Error::new(
|
|
|
|
io::ErrorKind::Other,
|
2019-11-13 08:43:15 -08:00
|
|
|
format!(
|
|
|
|
"signature not found after {} seconds",
|
|
|
|
now.elapsed().as_secs()
|
|
|
|
),
|
2019-04-25 12:46:40 -07:00
|
|
|
)));
|
|
|
|
}
|
|
|
|
sleep(Duration::from_millis(250));
|
|
|
|
}
|
2019-07-02 20:56:10 -07:00
|
|
|
Ok(confirmed_blocks)
|
2019-04-25 12:46:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn poll_for_signature(&self, signature: &Signature) -> Result<()> {
|
|
|
|
let now = Instant::now();
|
|
|
|
loop {
|
|
|
|
let response = self.bank.get_signature_status(signature);
|
|
|
|
if let Some(res) = response {
|
|
|
|
if res.is_ok() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if now.elapsed().as_secs() > 15 {
|
|
|
|
return Err(TransportError::IoError(io::Error::new(
|
|
|
|
io::ErrorKind::Other,
|
2019-11-13 08:43:15 -08:00
|
|
|
format!(
|
|
|
|
"signature not found after {} seconds",
|
|
|
|
now.elapsed().as_secs()
|
|
|
|
),
|
2019-04-25 12:46:40 -07:00
|
|
|
)));
|
|
|
|
}
|
|
|
|
sleep(Duration::from_millis(250));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-04-27 08:39:29 -07:00
|
|
|
|
2019-05-13 12:49:37 -07:00
|
|
|
fn get_new_blockhash(&self, blockhash: &Hash) -> Result<(Hash, FeeCalculator)> {
|
2021-10-22 14:32:40 -07:00
|
|
|
let recent_blockhash = self.get_latest_blockhash()?;
|
2021-08-13 09:08:20 -07:00
|
|
|
if recent_blockhash != *blockhash {
|
2021-10-22 14:32:40 -07:00
|
|
|
Ok((
|
|
|
|
recent_blockhash,
|
|
|
|
FeeCalculator::new(self.bank.get_lamports_per_signature()),
|
|
|
|
))
|
2019-04-27 08:39:29 -07:00
|
|
|
} else {
|
|
|
|
Err(TransportError::IoError(io::Error::new(
|
|
|
|
io::ErrorKind::Other,
|
|
|
|
"Unable to get new blockhash",
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
}
|
2020-05-20 16:42:46 -07:00
|
|
|
|
|
|
|
fn get_epoch_info(&self) -> Result<EpochInfo> {
|
|
|
|
Ok(self.bank.get_epoch_info())
|
|
|
|
}
|
2021-08-13 09:08:20 -07:00
|
|
|
|
|
|
|
fn get_latest_blockhash(&self) -> Result<Hash> {
|
|
|
|
Ok(self.bank.last_blockhash())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_latest_blockhash_with_commitment(
|
|
|
|
&self,
|
|
|
|
_commitment_config: CommitmentConfig,
|
|
|
|
) -> Result<(Hash, u64)> {
|
|
|
|
let blockhash = self.bank.last_blockhash();
|
|
|
|
let last_valid_block_height = self
|
|
|
|
.bank
|
|
|
|
.get_blockhash_last_valid_block_height(&blockhash)
|
|
|
|
.expect("bank blockhash queue should contain blockhash");
|
|
|
|
Ok((blockhash, last_valid_block_height))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_blockhash_valid(
|
|
|
|
&self,
|
|
|
|
blockhash: &Hash,
|
|
|
|
_commitment_config: CommitmentConfig,
|
|
|
|
) -> Result<bool> {
|
|
|
|
Ok(self.bank.is_blockhash_valid(blockhash))
|
|
|
|
}
|
|
|
|
|
2021-10-13 13:10:58 -07:00
|
|
|
fn get_fee_for_message(&self, message: &Message) -> Result<u64> {
|
2021-08-17 15:17:56 -07:00
|
|
|
SanitizedMessage::try_from(message.clone())
|
2021-10-29 13:52:59 -07:00
|
|
|
.ok()
|
2022-01-21 16:01:22 -08:00
|
|
|
.and_then(|sanitized_message| self.bank.get_fee_for_message(&sanitized_message))
|
2021-10-29 13:52:59 -07:00
|
|
|
.ok_or_else(|| {
|
2021-08-13 09:08:20 -07:00
|
|
|
TransportError::IoError(io::Error::new(
|
|
|
|
io::ErrorKind::Other,
|
|
|
|
"Unable calculate fee",
|
|
|
|
))
|
|
|
|
})
|
|
|
|
}
|
2019-04-03 14:11:08 -07:00
|
|
|
}
|
|
|
|
|
2019-04-11 11:29:59 -07:00
|
|
|
impl BankClient {
|
2022-04-01 20:12:02 -07:00
|
|
|
fn run(bank: &Bank, transaction_receiver: Receiver<VersionedTransaction>) {
|
2019-04-19 06:29:07 -07:00
|
|
|
while let Ok(tx) = transaction_receiver.recv() {
|
2019-11-12 14:26:21 -08:00
|
|
|
let mut transactions = vec![tx];
|
|
|
|
while let Ok(tx) = transaction_receiver.try_recv() {
|
|
|
|
transactions.push(tx);
|
|
|
|
}
|
2022-04-01 20:12:02 -07:00
|
|
|
let _ = bank.try_process_entry_transactions(transactions);
|
2019-04-19 06:29:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-29 13:09:11 -07:00
|
|
|
pub fn new_shared(bank: &Arc<Bank>) -> Self {
|
2022-01-11 02:44:46 -08:00
|
|
|
let (transaction_sender, transaction_receiver) = unbounded();
|
2019-04-19 13:18:20 -07:00
|
|
|
let transaction_sender = Mutex::new(transaction_sender);
|
2019-04-19 06:29:07 -07:00
|
|
|
let thread_bank = bank.clone();
|
|
|
|
let bank = bank.clone();
|
|
|
|
Builder::new()
|
|
|
|
.name("solana-bank-client".to_string())
|
|
|
|
.spawn(move || Self::run(&thread_bank, transaction_receiver))
|
|
|
|
.unwrap();
|
|
|
|
Self {
|
|
|
|
bank,
|
|
|
|
transaction_sender,
|
|
|
|
}
|
2019-03-14 19:42:01 -07:00
|
|
|
}
|
2019-04-29 13:09:11 -07:00
|
|
|
|
|
|
|
pub fn new(bank: Bank) -> Self {
|
|
|
|
Self::new_shared(&Arc::new(bank))
|
|
|
|
}
|
2019-03-14 19:42:01 -07:00
|
|
|
}
|
2019-03-18 14:35:47 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-12-03 09:00:31 -08:00
|
|
|
use {
|
|
|
|
super::*,
|
|
|
|
solana_sdk::{genesis_config::create_genesis_config, instruction::AccountMeta},
|
|
|
|
};
|
2019-03-18 14:35:47 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bank_client_new_with_keypairs() {
|
2019-11-08 20:56:57 -08:00
|
|
|
let (genesis_config, john_doe_keypair) = create_genesis_config(10_000);
|
2019-03-27 06:34:01 -07:00
|
|
|
let john_pubkey = john_doe_keypair.pubkey();
|
2019-03-18 14:35:47 -07:00
|
|
|
let jane_doe_keypair = Keypair::new();
|
2019-03-27 06:34:01 -07:00
|
|
|
let jane_pubkey = jane_doe_keypair.pubkey();
|
|
|
|
let doe_keypairs = vec![&john_doe_keypair, &jane_doe_keypair];
|
2021-08-05 06:42:38 -07:00
|
|
|
let bank = Bank::new_for_tests(&genesis_config);
|
2019-04-11 11:29:59 -07:00
|
|
|
let bank_client = BankClient::new(bank);
|
2019-03-18 14:35:47 -07:00
|
|
|
|
2019-04-02 20:52:07 -07:00
|
|
|
// Create 2-2 Multisig Transfer instruction.
|
2020-10-19 12:12:08 -07:00
|
|
|
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
2019-04-23 12:30:42 -07:00
|
|
|
let mut transfer_instruction = system_instruction::transfer(&john_pubkey, &bob_pubkey, 42);
|
|
|
|
transfer_instruction
|
2019-03-19 12:03:20 -07:00
|
|
|
.accounts
|
2019-03-19 14:25:48 -07:00
|
|
|
.push(AccountMeta::new(jane_pubkey, true));
|
2019-03-18 14:35:47 -07:00
|
|
|
|
2020-06-24 13:52:38 -07:00
|
|
|
let message = Message::new(&[transfer_instruction], Some(&john_pubkey));
|
2020-06-24 20:35:38 -07:00
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&doe_keypairs, message)
|
|
|
|
.unwrap();
|
2019-04-03 20:40:29 -07:00
|
|
|
assert_eq!(bank_client.get_balance(&bob_pubkey).unwrap(), 42);
|
2019-03-18 14:35:47 -07:00
|
|
|
}
|
|
|
|
}
|