2020-08-07 07:45:17 -07:00
|
|
|
//! A client for the ledger state, from the perspective of an arbitrary validator.
|
|
|
|
//!
|
|
|
|
//! Use start_tcp_client() to create a client and then import BanksClientExt to
|
|
|
|
//! access its methods. Additional "*_with_context" methods are also available,
|
|
|
|
//! but they are undocumented, may change over time, and are generally more
|
|
|
|
//! cumbersome to use.
|
|
|
|
|
2022-02-18 21:32:29 -08:00
|
|
|
pub use {
|
|
|
|
crate::error::BanksClientError,
|
|
|
|
solana_banks_interface::{BanksClient as TarpcClient, TransactionStatus},
|
|
|
|
};
|
2021-08-23 15:32:15 -07:00
|
|
|
use {
|
|
|
|
borsh::BorshDeserialize,
|
2021-12-21 18:31:02 -08:00
|
|
|
futures::{future::join_all, Future, FutureExt, TryFutureExt},
|
2021-12-28 11:25:46 -08:00
|
|
|
solana_banks_interface::{BanksRequest, BanksResponse, BanksTransactionResultWithSimulation},
|
2021-08-23 15:32:15 -07:00
|
|
|
solana_program::{
|
|
|
|
clock::Slot, fee_calculator::FeeCalculator, hash::Hash, program_pack::Pack, pubkey::Pubkey,
|
|
|
|
rent::Rent, sysvar::Sysvar,
|
|
|
|
},
|
|
|
|
solana_sdk::{
|
|
|
|
account::{from_account, Account},
|
|
|
|
commitment_config::CommitmentLevel,
|
2021-10-28 16:00:09 -07:00
|
|
|
message::Message,
|
2021-08-23 15:32:15 -07:00
|
|
|
signature::Signature,
|
|
|
|
transaction::{self, Transaction},
|
|
|
|
},
|
|
|
|
tarpc::{
|
|
|
|
client::{self, NewClient, RequestDispatch},
|
|
|
|
context::{self, Context},
|
|
|
|
serde_transport::tcp,
|
|
|
|
ClientMessage, Response, Transport,
|
|
|
|
},
|
|
|
|
tokio::{net::ToSocketAddrs, time::Duration},
|
|
|
|
tokio_serde::formats::Bincode,
|
2021-02-18 02:14:56 -08:00
|
|
|
};
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2021-12-21 18:31:02 -08:00
|
|
|
mod error;
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
// This exists only for backward compatibility
|
|
|
|
pub trait BanksClientExt {}
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct BanksClient {
|
|
|
|
inner: TarpcClient,
|
|
|
|
}
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
impl BanksClient {
|
|
|
|
#[allow(clippy::new_ret_no_self)]
|
|
|
|
pub fn new<C>(
|
|
|
|
config: client::Config,
|
|
|
|
transport: C,
|
|
|
|
) -> NewClient<TarpcClient, RequestDispatch<BanksRequest, BanksResponse, C>>
|
|
|
|
where
|
|
|
|
C: Transport<ClientMessage<BanksRequest>, Response<BanksResponse>>,
|
|
|
|
{
|
|
|
|
TarpcClient::new(config, transport)
|
|
|
|
}
|
2020-11-05 18:13:01 -08:00
|
|
|
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn send_transaction_with_context(
|
2020-08-07 07:45:17 -07:00
|
|
|
&mut self,
|
2020-11-10 18:30:25 -08:00
|
|
|
ctx: Context,
|
2020-08-07 07:45:17 -07:00
|
|
|
transaction: Transaction,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<(), BanksClientError>> + '_ {
|
2021-12-21 18:31:02 -08:00
|
|
|
self.inner
|
|
|
|
.send_transaction_with_context(ctx, transaction)
|
|
|
|
.map_err(Into::into)
|
2020-11-10 18:30:25 -08:00
|
|
|
}
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2021-11-02 16:38:23 -07:00
|
|
|
#[deprecated(
|
|
|
|
since = "1.9.0",
|
|
|
|
note = "Please use `get_fee_for_message` or `is_blockhash_valid` instead"
|
|
|
|
)]
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn get_fees_with_commitment_and_context(
|
2020-11-10 18:30:25 -08:00
|
|
|
&mut self,
|
|
|
|
ctx: Context,
|
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<(FeeCalculator, Hash, u64), BanksClientError>> + '_ {
|
2021-10-28 16:00:09 -07:00
|
|
|
#[allow(deprecated)]
|
2020-11-10 18:30:25 -08:00
|
|
|
self.inner
|
|
|
|
.get_fees_with_commitment_and_context(ctx, commitment)
|
2021-12-21 18:31:02 -08:00
|
|
|
.map_err(Into::into)
|
2020-11-10 18:30:25 -08:00
|
|
|
}
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn get_transaction_status_with_context(
|
2020-08-07 07:45:17 -07:00
|
|
|
&mut self,
|
2020-11-10 18:30:25 -08:00
|
|
|
ctx: Context,
|
2020-08-07 07:45:17 -07:00
|
|
|
signature: Signature,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Option<TransactionStatus>, BanksClientError>> + '_ {
|
2020-11-10 18:30:25 -08:00
|
|
|
self.inner
|
|
|
|
.get_transaction_status_with_context(ctx, signature)
|
2021-12-21 18:31:02 -08:00
|
|
|
.map_err(Into::into)
|
2020-11-10 18:30:25 -08:00
|
|
|
}
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn get_slot_with_context(
|
2020-08-07 07:45:17 -07:00
|
|
|
&mut self,
|
2020-11-10 18:30:25 -08:00
|
|
|
ctx: Context,
|
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Slot, BanksClientError>> + '_ {
|
2021-12-21 18:31:02 -08:00
|
|
|
self.inner
|
|
|
|
.get_slot_with_context(ctx, commitment)
|
|
|
|
.map_err(Into::into)
|
2020-11-10 18:30:25 -08:00
|
|
|
}
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2021-08-11 00:04:00 -07:00
|
|
|
pub fn get_block_height_with_context(
|
|
|
|
&mut self,
|
|
|
|
ctx: Context,
|
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Slot, BanksClientError>> + '_ {
|
2021-12-21 18:31:02 -08:00
|
|
|
self.inner
|
|
|
|
.get_block_height_with_context(ctx, commitment)
|
|
|
|
.map_err(Into::into)
|
2021-08-11 00:04:00 -07:00
|
|
|
}
|
|
|
|
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn process_transaction_with_commitment_and_context(
|
2020-09-29 17:11:49 -07:00
|
|
|
&mut self,
|
2020-11-10 18:30:25 -08:00
|
|
|
ctx: Context,
|
|
|
|
transaction: Transaction,
|
2020-09-29 17:11:49 -07:00
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Option<transaction::Result<()>>, BanksClientError>> + '_ {
|
2020-11-10 18:30:25 -08:00
|
|
|
self.inner
|
|
|
|
.process_transaction_with_commitment_and_context(ctx, transaction, commitment)
|
2021-12-21 18:31:02 -08:00
|
|
|
.map_err(Into::into)
|
2020-11-10 18:30:25 -08:00
|
|
|
}
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2021-12-28 11:25:46 -08:00
|
|
|
pub fn process_transaction_with_preflight_and_commitment_and_context(
|
|
|
|
&mut self,
|
|
|
|
ctx: Context,
|
|
|
|
transaction: Transaction,
|
|
|
|
commitment: CommitmentLevel,
|
|
|
|
) -> impl Future<Output = Result<BanksTransactionResultWithSimulation, BanksClientError>> + '_
|
|
|
|
{
|
|
|
|
self.inner
|
|
|
|
.process_transaction_with_preflight_and_commitment_and_context(
|
|
|
|
ctx,
|
|
|
|
transaction,
|
|
|
|
commitment,
|
|
|
|
)
|
|
|
|
.map_err(Into::into)
|
|
|
|
}
|
|
|
|
|
2022-06-08 04:56:54 -07:00
|
|
|
pub fn simulate_transaction_with_commitment_and_context(
|
|
|
|
&mut self,
|
|
|
|
ctx: Context,
|
|
|
|
transaction: Transaction,
|
|
|
|
commitment: CommitmentLevel,
|
|
|
|
) -> impl Future<Output = Result<BanksTransactionResultWithSimulation, BanksClientError>> + '_
|
|
|
|
{
|
|
|
|
self.inner
|
|
|
|
.simulate_transaction_with_commitment_and_context(ctx, transaction, commitment)
|
|
|
|
.map_err(Into::into)
|
|
|
|
}
|
|
|
|
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn get_account_with_commitment_and_context(
|
2020-08-07 07:45:17 -07:00
|
|
|
&mut self,
|
2020-11-10 18:30:25 -08:00
|
|
|
ctx: Context,
|
2020-08-07 07:45:17 -07:00
|
|
|
address: Pubkey,
|
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Option<Account>, BanksClientError>> + '_ {
|
2020-11-10 18:30:25 -08:00
|
|
|
self.inner
|
|
|
|
.get_account_with_commitment_and_context(ctx, address, commitment)
|
2021-12-21 18:31:02 -08:00
|
|
|
.map_err(Into::into)
|
2020-11-10 18:30:25 -08:00
|
|
|
}
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Send a transaction and return immediately. The server will resend the
|
|
|
|
/// transaction until either it is accepted by the cluster or the transaction's
|
|
|
|
/// blockhash expires.
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn send_transaction(
|
|
|
|
&mut self,
|
|
|
|
transaction: Transaction,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<(), BanksClientError>> + '_ {
|
2020-08-07 07:45:17 -07:00
|
|
|
self.send_transaction_with_context(context::current(), transaction)
|
|
|
|
}
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Return the fee parameters associated with a recent, rooted blockhash. The cluster
|
|
|
|
/// will use the transaction's blockhash to look up these same fee parameters and
|
|
|
|
/// use them to calculate the transaction fee.
|
2021-11-02 16:38:23 -07:00
|
|
|
#[deprecated(
|
|
|
|
since = "1.9.0",
|
|
|
|
note = "Please use `get_fee_for_message` or `is_blockhash_valid` instead"
|
|
|
|
)]
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn get_fees(
|
|
|
|
&mut self,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<(FeeCalculator, Hash, u64), BanksClientError>> + '_ {
|
2021-11-02 16:38:23 -07:00
|
|
|
#[allow(deprecated)]
|
2021-01-26 11:23:07 -08:00
|
|
|
self.get_fees_with_commitment_and_context(context::current(), CommitmentLevel::default())
|
2020-08-07 07:45:17 -07:00
|
|
|
}
|
|
|
|
|
2021-07-13 09:26:14 -07:00
|
|
|
/// Return the cluster Sysvar
|
2021-12-24 06:44:19 -08:00
|
|
|
pub fn get_sysvar<T: Sysvar>(
|
|
|
|
&mut self,
|
|
|
|
) -> impl Future<Output = Result<T, BanksClientError>> + '_ {
|
2021-07-13 09:26:14 -07:00
|
|
|
self.get_account(T::id()).map(|result| {
|
2021-12-24 06:44:19 -08:00
|
|
|
let sysvar = result?.ok_or(BanksClientError::ClientError("Sysvar not present"))?;
|
|
|
|
from_account::<T, _>(&sysvar).ok_or(BanksClientError::ClientError(
|
|
|
|
"Failed to deserialize sysvar",
|
|
|
|
))
|
2021-07-13 09:26:14 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Return the cluster rent
|
2021-12-24 06:44:19 -08:00
|
|
|
pub fn get_rent(&mut self) -> impl Future<Output = Result<Rent, BanksClientError>> + '_ {
|
2021-07-13 09:26:14 -07:00
|
|
|
self.get_sysvar::<Rent>()
|
2020-11-05 18:13:01 -08:00
|
|
|
}
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Return a recent, rooted blockhash from the server. The cluster will only accept
|
|
|
|
/// transactions with a blockhash that has not yet expired. Use the `get_fees`
|
|
|
|
/// method to get both a blockhash and the blockhash's last valid slot.
|
2021-11-02 16:38:23 -07:00
|
|
|
#[deprecated(since = "1.9.0", note = "Please use `get_latest_blockhash` instead")]
|
2021-12-24 06:44:19 -08:00
|
|
|
pub fn get_recent_blockhash(
|
|
|
|
&mut self,
|
|
|
|
) -> impl Future<Output = Result<Hash, BanksClientError>> + '_ {
|
2021-12-21 18:31:02 -08:00
|
|
|
#[allow(deprecated)]
|
|
|
|
self.get_fees().map(|result| Ok(result?.1))
|
2020-08-07 07:45:17 -07:00
|
|
|
}
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Send a transaction and return after the transaction has been rejected or
|
|
|
|
/// reached the given level of commitment.
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn process_transaction_with_commitment(
|
2020-08-07 07:45:17 -07:00
|
|
|
&mut self,
|
|
|
|
transaction: Transaction,
|
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<(), BanksClientError>> + '_ {
|
2020-08-07 07:45:17 -07:00
|
|
|
let mut ctx = context::current();
|
|
|
|
ctx.deadline += Duration::from_secs(50);
|
2020-11-11 17:56:26 -08:00
|
|
|
self.process_transaction_with_commitment_and_context(ctx, transaction, commitment)
|
|
|
|
.map(|result| match result? {
|
2021-12-21 18:31:02 -08:00
|
|
|
None => Err(BanksClientError::ClientError(
|
|
|
|
"invalid blockhash or fee-payer",
|
|
|
|
)),
|
2020-11-11 17:56:26 -08:00
|
|
|
Some(transaction_result) => Ok(transaction_result?),
|
|
|
|
})
|
2020-08-07 07:45:17 -07:00
|
|
|
}
|
|
|
|
|
2021-12-28 11:25:46 -08:00
|
|
|
/// Send a transaction and return any preflight (sanitization or simulation) errors, or return
|
|
|
|
/// after the transaction has been rejected or reached the given level of commitment.
|
|
|
|
pub fn process_transaction_with_preflight_and_commitment(
|
|
|
|
&mut self,
|
|
|
|
transaction: Transaction,
|
|
|
|
commitment: CommitmentLevel,
|
|
|
|
) -> impl Future<Output = Result<(), BanksClientError>> + '_ {
|
|
|
|
let mut ctx = context::current();
|
|
|
|
ctx.deadline += Duration::from_secs(50);
|
|
|
|
self.process_transaction_with_preflight_and_commitment_and_context(
|
|
|
|
ctx,
|
|
|
|
transaction,
|
|
|
|
commitment,
|
|
|
|
)
|
|
|
|
.map(|result| match result? {
|
|
|
|
BanksTransactionResultWithSimulation {
|
|
|
|
result: None,
|
|
|
|
simulation_details: _,
|
|
|
|
} => Err(BanksClientError::ClientError(
|
|
|
|
"invalid blockhash or fee-payer",
|
|
|
|
)),
|
|
|
|
BanksTransactionResultWithSimulation {
|
|
|
|
result: Some(Err(err)),
|
|
|
|
simulation_details: Some(simulation_details),
|
|
|
|
} => Err(BanksClientError::SimulationError {
|
|
|
|
err,
|
|
|
|
logs: simulation_details.logs,
|
|
|
|
units_consumed: simulation_details.units_consumed,
|
2022-03-22 15:17:05 -07:00
|
|
|
return_data: simulation_details.return_data,
|
2021-12-28 11:25:46 -08:00
|
|
|
}),
|
|
|
|
BanksTransactionResultWithSimulation {
|
|
|
|
result: Some(result),
|
|
|
|
simulation_details: _,
|
|
|
|
} => result.map_err(Into::into),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Send a transaction and return any preflight (sanitization or simulation) errors, or return
|
|
|
|
/// after the transaction has been finalized or rejected.
|
|
|
|
pub fn process_transaction_with_preflight(
|
|
|
|
&mut self,
|
|
|
|
transaction: Transaction,
|
|
|
|
) -> impl Future<Output = Result<(), BanksClientError>> + '_ {
|
|
|
|
self.process_transaction_with_preflight_and_commitment(
|
|
|
|
transaction,
|
|
|
|
CommitmentLevel::default(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-11-11 17:56:26 -08:00
|
|
|
/// Send a transaction and return until the transaction has been finalized or rejected.
|
|
|
|
pub fn process_transaction(
|
|
|
|
&mut self,
|
|
|
|
transaction: Transaction,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<(), BanksClientError>> + '_ {
|
2020-08-07 07:45:17 -07:00
|
|
|
self.process_transaction_with_commitment(transaction, CommitmentLevel::default())
|
2020-11-11 17:56:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn process_transactions_with_commitment(
|
|
|
|
&mut self,
|
|
|
|
transactions: Vec<Transaction>,
|
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> Result<(), BanksClientError> {
|
2020-11-11 17:56:26 -08:00
|
|
|
let mut clients: Vec<_> = transactions.iter().map(|_| self.clone()).collect();
|
|
|
|
let futures = clients
|
|
|
|
.iter_mut()
|
|
|
|
.zip(transactions)
|
|
|
|
.map(|(client, transaction)| {
|
|
|
|
client.process_transaction_with_commitment(transaction, commitment)
|
|
|
|
});
|
|
|
|
let statuses = join_all(futures).await;
|
|
|
|
statuses.into_iter().collect() // Convert Vec<Result<_, _>> to Result<Vec<_>>
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Send transactions and return until the transaction has been finalized or rejected.
|
|
|
|
pub fn process_transactions(
|
|
|
|
&mut self,
|
|
|
|
transactions: Vec<Transaction>,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<(), BanksClientError>> + '_ {
|
2020-11-11 17:56:26 -08:00
|
|
|
self.process_transactions_with_commitment(transactions, CommitmentLevel::default())
|
2020-08-07 07:45:17 -07:00
|
|
|
}
|
|
|
|
|
2022-06-08 04:56:54 -07:00
|
|
|
/// Simulate a transaction at the given commitment level
|
|
|
|
pub fn simulate_transaction_with_commitment(
|
|
|
|
&mut self,
|
|
|
|
transaction: Transaction,
|
|
|
|
commitment: CommitmentLevel,
|
|
|
|
) -> impl Future<Output = Result<BanksTransactionResultWithSimulation, BanksClientError>> + '_
|
|
|
|
{
|
|
|
|
self.simulate_transaction_with_commitment_and_context(
|
|
|
|
context::current(),
|
|
|
|
transaction,
|
|
|
|
commitment,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Simulate a transaction at the default commitment level
|
|
|
|
pub fn simulate_transaction(
|
|
|
|
&mut self,
|
|
|
|
transaction: Transaction,
|
|
|
|
) -> impl Future<Output = Result<BanksTransactionResultWithSimulation, BanksClientError>> + '_
|
|
|
|
{
|
|
|
|
self.simulate_transaction_with_commitment(transaction, CommitmentLevel::default())
|
|
|
|
}
|
|
|
|
|
2021-08-11 00:04:00 -07:00
|
|
|
/// Return the most recent rooted slot. All transactions at or below this slot
|
|
|
|
/// are said to be finalized. The cluster will not fork to a higher slot.
|
2021-12-24 06:44:19 -08:00
|
|
|
pub fn get_root_slot(&mut self) -> impl Future<Output = Result<Slot, BanksClientError>> + '_ {
|
2021-01-26 11:23:07 -08:00
|
|
|
self.get_slot_with_context(context::current(), CommitmentLevel::default())
|
2020-08-07 07:45:17 -07:00
|
|
|
}
|
|
|
|
|
2021-08-11 00:04:00 -07:00
|
|
|
/// Return the most recent rooted block height. All transactions at or below this height
|
|
|
|
/// are said to be finalized. The cluster will not fork to a higher block height.
|
2021-12-24 06:44:19 -08:00
|
|
|
pub fn get_root_block_height(
|
|
|
|
&mut self,
|
|
|
|
) -> impl Future<Output = Result<Slot, BanksClientError>> + '_ {
|
2021-08-11 00:04:00 -07:00
|
|
|
self.get_block_height_with_context(context::current(), CommitmentLevel::default())
|
|
|
|
}
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Return the account at the given address at the slot corresponding to the given
|
|
|
|
/// commitment level. If the account is not found, None is returned.
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn get_account_with_commitment(
|
2020-09-29 17:11:49 -07:00
|
|
|
&mut self,
|
|
|
|
address: Pubkey,
|
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Option<Account>, BanksClientError>> + '_ {
|
2020-09-29 17:11:49 -07:00
|
|
|
self.get_account_with_commitment_and_context(context::current(), address, commitment)
|
|
|
|
}
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Return the account at the given address at the time of the most recent root slot.
|
|
|
|
/// If the account is not found, None is returned.
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn get_account(
|
|
|
|
&mut self,
|
|
|
|
address: Pubkey,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Option<Account>, BanksClientError>> + '_ {
|
2020-09-29 17:11:49 -07:00
|
|
|
self.get_account_with_commitment(address, CommitmentLevel::default())
|
2020-08-07 07:45:17 -07:00
|
|
|
}
|
|
|
|
|
2021-02-18 02:14:56 -08:00
|
|
|
/// Return the unpacked account data at the given address
|
|
|
|
/// If the account is not found, an error is returned
|
|
|
|
pub fn get_packed_account_data<T: Pack>(
|
|
|
|
&mut self,
|
|
|
|
address: Pubkey,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<T, BanksClientError>> + '_ {
|
2021-02-18 02:14:56 -08:00
|
|
|
self.get_account(address).map(|result| {
|
2021-12-24 06:44:19 -08:00
|
|
|
let account = result?.ok_or(BanksClientError::ClientError("Account not found"))?;
|
2021-02-18 02:14:56 -08:00
|
|
|
T::unpack_from_slice(&account.data)
|
2021-12-21 18:31:02 -08:00
|
|
|
.map_err(|_| BanksClientError::ClientError("Failed to deserialize account"))
|
2021-02-18 02:14:56 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the unpacked account data at the given address
|
|
|
|
/// If the account is not found, an error is returned
|
|
|
|
pub fn get_account_data_with_borsh<T: BorshDeserialize>(
|
|
|
|
&mut self,
|
|
|
|
address: Pubkey,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<T, BanksClientError>> + '_ {
|
2021-02-18 02:14:56 -08:00
|
|
|
self.get_account(address).map(|result| {
|
2021-12-21 18:31:02 -08:00
|
|
|
let account = result?.ok_or(BanksClientError::ClientError("Account not found"))?;
|
|
|
|
T::try_from_slice(&account.data).map_err(Into::into)
|
2021-02-18 02:14:56 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Return the balance in lamports of an account at the given address at the slot
|
|
|
|
/// corresponding to the given commitment level.
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn get_balance_with_commitment(
|
2020-08-07 07:45:17 -07:00
|
|
|
&mut self,
|
|
|
|
address: Pubkey,
|
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<u64, BanksClientError>> + '_ {
|
2020-11-11 17:56:26 -08:00
|
|
|
self.get_account_with_commitment_and_context(context::current(), address, commitment)
|
|
|
|
.map(|result| Ok(result?.map(|x| x.lamports).unwrap_or(0)))
|
2020-08-07 07:45:17 -07:00
|
|
|
}
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Return the balance in lamports of an account at the given address at the time
|
|
|
|
/// of the most recent root slot.
|
2021-12-24 06:44:19 -08:00
|
|
|
pub fn get_balance(
|
|
|
|
&mut self,
|
|
|
|
address: Pubkey,
|
|
|
|
) -> impl Future<Output = Result<u64, BanksClientError>> + '_ {
|
2020-08-07 07:45:17 -07:00
|
|
|
self.get_balance_with_commitment(address, CommitmentLevel::default())
|
|
|
|
}
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Return the status of a transaction with a signature matching the transaction's first
|
|
|
|
/// signature. Return None if the transaction is not found, which may be because the
|
|
|
|
/// blockhash was expired or the fee-paying account had insufficient funds to pay the
|
|
|
|
/// transaction fee. Note that servers rarely store the full transaction history. This
|
|
|
|
/// method may return None if the transaction status has been discarded.
|
2020-11-11 17:56:26 -08:00
|
|
|
pub fn get_transaction_status(
|
2020-08-07 07:45:17 -07:00
|
|
|
&mut self,
|
|
|
|
signature: Signature,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Option<TransactionStatus>, BanksClientError>> + '_ {
|
2020-08-07 07:45:17 -07:00
|
|
|
self.get_transaction_status_with_context(context::current(), signature)
|
|
|
|
}
|
|
|
|
|
2020-11-10 18:30:25 -08:00
|
|
|
/// Same as get_transaction_status, but for multiple transactions.
|
|
|
|
pub async fn get_transaction_statuses(
|
2020-08-07 07:45:17 -07:00
|
|
|
&mut self,
|
|
|
|
signatures: Vec<Signature>,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> Result<Vec<Option<TransactionStatus>>, BanksClientError> {
|
2020-08-07 07:45:17 -07:00
|
|
|
// tarpc futures oddly hold a mutable reference back to the client so clone the client upfront
|
|
|
|
let mut clients_and_signatures: Vec<_> = signatures
|
|
|
|
.into_iter()
|
|
|
|
.map(|signature| (self.clone(), signature))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let futs = clients_and_signatures
|
|
|
|
.iter_mut()
|
|
|
|
.map(|(client, signature)| client.get_transaction_status(*signature));
|
|
|
|
|
|
|
|
let statuses = join_all(futs).await;
|
|
|
|
|
|
|
|
// Convert Vec<Result<_, _>> to Result<Vec<_>>
|
|
|
|
statuses.into_iter().collect()
|
|
|
|
}
|
2021-10-28 16:00:09 -07:00
|
|
|
|
2021-12-24 06:44:19 -08:00
|
|
|
pub fn get_latest_blockhash(
|
|
|
|
&mut self,
|
|
|
|
) -> impl Future<Output = Result<Hash, BanksClientError>> + '_ {
|
2021-11-02 16:38:23 -07:00
|
|
|
self.get_latest_blockhash_with_commitment(CommitmentLevel::default())
|
|
|
|
.map(|result| {
|
|
|
|
result?
|
|
|
|
.map(|x| x.0)
|
2021-12-21 18:31:02 -08:00
|
|
|
.ok_or(BanksClientError::ClientError("valid blockhash not found"))
|
|
|
|
.map_err(Into::into)
|
2021-11-02 16:38:23 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_latest_blockhash_with_commitment(
|
|
|
|
&mut self,
|
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Option<(Hash, u64)>, BanksClientError>> + '_ {
|
2021-11-02 16:38:23 -07:00
|
|
|
self.get_latest_blockhash_with_commitment_and_context(context::current(), commitment)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_latest_blockhash_with_commitment_and_context(
|
|
|
|
&mut self,
|
|
|
|
ctx: Context,
|
|
|
|
commitment: CommitmentLevel,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Option<(Hash, u64)>, BanksClientError>> + '_ {
|
2021-11-02 16:38:23 -07:00
|
|
|
self.inner
|
|
|
|
.get_latest_blockhash_with_commitment_and_context(ctx, commitment)
|
2021-12-21 18:31:02 -08:00
|
|
|
.map_err(Into::into)
|
2021-11-02 16:38:23 -07:00
|
|
|
}
|
|
|
|
|
2021-10-28 16:00:09 -07:00
|
|
|
pub fn get_fee_for_message_with_commitment_and_context(
|
|
|
|
&mut self,
|
|
|
|
ctx: Context,
|
|
|
|
commitment: CommitmentLevel,
|
|
|
|
message: Message,
|
2021-12-24 06:44:19 -08:00
|
|
|
) -> impl Future<Output = Result<Option<u64>, BanksClientError>> + '_ {
|
2021-10-28 16:00:09 -07:00
|
|
|
self.inner
|
|
|
|
.get_fee_for_message_with_commitment_and_context(ctx, commitment, message)
|
2021-12-21 18:31:02 -08:00
|
|
|
.map_err(Into::into)
|
2021-10-28 16:00:09 -07:00
|
|
|
}
|
2020-08-07 07:45:17 -07:00
|
|
|
}
|
|
|
|
|
2021-12-24 06:44:19 -08:00
|
|
|
pub async fn start_client<C>(transport: C) -> Result<BanksClient, BanksClientError>
|
2020-11-10 18:30:25 -08:00
|
|
|
where
|
|
|
|
C: Transport<ClientMessage<BanksRequest>, Response<BanksResponse>> + Send + 'static,
|
|
|
|
{
|
|
|
|
Ok(BanksClient {
|
2021-06-15 13:31:34 -07:00
|
|
|
inner: TarpcClient::new(client::Config::default(), transport).spawn(),
|
2020-11-10 18:30:25 -08:00
|
|
|
})
|
2020-08-07 07:45:17 -07:00
|
|
|
}
|
|
|
|
|
2021-12-24 06:44:19 -08:00
|
|
|
pub async fn start_tcp_client<T: ToSocketAddrs>(addr: T) -> Result<BanksClient, BanksClientError> {
|
2020-09-08 20:22:22 -07:00
|
|
|
let transport = tcp::connect(addr, Bincode::default).await?;
|
2020-11-10 18:30:25 -08:00
|
|
|
Ok(BanksClient {
|
2021-06-15 13:31:34 -07:00
|
|
|
inner: TarpcClient::new(client::Config::default(), transport).spawn(),
|
2020-11-10 18:30:25 -08:00
|
|
|
})
|
2020-08-07 07:45:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-12-03 09:00:31 -08:00
|
|
|
use {
|
|
|
|
super::*,
|
|
|
|
solana_banks_server::banks_server::start_local_server,
|
|
|
|
solana_runtime::{
|
|
|
|
bank::Bank, bank_forks::BankForks, commitment::BlockCommitmentCache,
|
|
|
|
genesis_utils::create_genesis_config,
|
|
|
|
},
|
|
|
|
solana_sdk::{message::Message, signature::Signer, system_instruction},
|
|
|
|
std::sync::{Arc, RwLock},
|
|
|
|
tarpc::transport,
|
|
|
|
tokio::{runtime::Runtime, time::sleep},
|
2021-02-03 14:31:36 -08:00
|
|
|
};
|
2020-08-07 07:45:17 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_banks_client_new() {
|
|
|
|
let (client_transport, _server_transport) = transport::channel::unbounded();
|
|
|
|
BanksClient::new(client::Config::default(), client_transport);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-09-29 06:32:24 -07:00
|
|
|
#[allow(clippy::result_large_err)]
|
2021-12-21 18:31:02 -08:00
|
|
|
fn test_banks_server_transfer_via_server() -> Result<(), BanksClientError> {
|
2020-08-07 07:45:17 -07:00
|
|
|
// This test shows the preferred way to interact with BanksServer.
|
|
|
|
// It creates a runtime explicitly (no globals via tokio macros) and calls
|
|
|
|
// `runtime.block_on()` just once, to run all the async code.
|
|
|
|
|
|
|
|
let genesis = create_genesis_config(10);
|
2021-08-05 06:42:38 -07:00
|
|
|
let bank = Bank::new_for_tests(&genesis.genesis_config);
|
2021-02-03 14:31:36 -08:00
|
|
|
let slot = bank.slot();
|
|
|
|
let block_commitment_cache = Arc::new(RwLock::new(
|
|
|
|
BlockCommitmentCache::new_for_tests_with_slots(slot, slot),
|
|
|
|
));
|
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
2020-08-07 07:45:17 -07:00
|
|
|
let mint_pubkey = genesis.mint_keypair.pubkey();
|
|
|
|
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
|
|
|
|
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
|
|
|
|
|
|
|
Runtime::new()?.block_on(async {
|
2021-10-15 09:55:41 -07:00
|
|
|
let client_transport =
|
|
|
|
start_local_server(bank_forks, block_commitment_cache, Duration::from_millis(1))
|
|
|
|
.await;
|
2020-11-10 18:30:25 -08:00
|
|
|
let mut banks_client = start_client(client_transport).await?;
|
2020-08-07 07:45:17 -07:00
|
|
|
|
2021-11-02 16:38:23 -07:00
|
|
|
let recent_blockhash = banks_client.get_latest_blockhash().await?;
|
2020-08-07 07:45:17 -07:00
|
|
|
let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);
|
2022-06-08 04:56:54 -07:00
|
|
|
let simulation_result = banks_client
|
|
|
|
.simulate_transaction(transaction.clone())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
assert!(simulation_result.result.unwrap().is_ok());
|
2020-08-07 07:45:17 -07:00
|
|
|
banks_client.process_transaction(transaction).await.unwrap();
|
|
|
|
assert_eq!(banks_client.get_balance(bob_pubkey).await?, 1);
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-09-29 06:32:24 -07:00
|
|
|
#[allow(clippy::result_large_err)]
|
2021-12-21 18:31:02 -08:00
|
|
|
fn test_banks_server_transfer_via_client() -> Result<(), BanksClientError> {
|
2020-08-07 07:45:17 -07:00
|
|
|
// The caller may not want to hold the connection open until the transaction
|
|
|
|
// is processed (or blockhash expires). In this test, we verify the
|
|
|
|
// server-side functionality is available to the client.
|
|
|
|
|
|
|
|
let genesis = create_genesis_config(10);
|
2021-08-05 06:42:38 -07:00
|
|
|
let bank = Bank::new_for_tests(&genesis.genesis_config);
|
2021-02-03 14:31:36 -08:00
|
|
|
let slot = bank.slot();
|
|
|
|
let block_commitment_cache = Arc::new(RwLock::new(
|
|
|
|
BlockCommitmentCache::new_for_tests_with_slots(slot, slot),
|
|
|
|
));
|
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
2020-08-07 07:45:17 -07:00
|
|
|
|
|
|
|
let mint_pubkey = &genesis.mint_keypair.pubkey();
|
2020-10-19 12:12:08 -07:00
|
|
|
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
2021-06-18 06:34:46 -07:00
|
|
|
let instruction = system_instruction::transfer(mint_pubkey, &bob_pubkey, 1);
|
|
|
|
let message = Message::new(&[instruction], Some(mint_pubkey));
|
2020-08-07 07:45:17 -07:00
|
|
|
|
|
|
|
Runtime::new()?.block_on(async {
|
2021-10-15 09:55:41 -07:00
|
|
|
let client_transport =
|
|
|
|
start_local_server(bank_forks, block_commitment_cache, Duration::from_millis(1))
|
|
|
|
.await;
|
2020-11-10 18:30:25 -08:00
|
|
|
let mut banks_client = start_client(client_transport).await?;
|
2021-11-02 16:38:23 -07:00
|
|
|
let (recent_blockhash, last_valid_block_height) = banks_client
|
|
|
|
.get_latest_blockhash_with_commitment(CommitmentLevel::default())
|
|
|
|
.await?
|
|
|
|
.unwrap();
|
2020-08-07 07:45:17 -07:00
|
|
|
let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);
|
|
|
|
let signature = transaction.signatures[0];
|
|
|
|
banks_client.send_transaction(transaction).await?;
|
|
|
|
|
|
|
|
let mut status = banks_client.get_transaction_status(signature).await?;
|
|
|
|
|
|
|
|
while status.is_none() {
|
2021-08-11 00:04:00 -07:00
|
|
|
let root_block_height = banks_client.get_root_block_height().await?;
|
|
|
|
if root_block_height > last_valid_block_height {
|
2020-08-07 07:45:17 -07:00
|
|
|
break;
|
|
|
|
}
|
2020-10-29 18:21:18 -07:00
|
|
|
sleep(Duration::from_millis(100)).await;
|
2020-08-07 07:45:17 -07:00
|
|
|
status = banks_client.get_transaction_status(signature).await?;
|
|
|
|
}
|
|
|
|
assert!(status.unwrap().err.is_none());
|
|
|
|
assert_eq!(banks_client.get_balance(bob_pubkey).await?, 1);
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|