Add a client for BankForks (#10728)
Also: * Use BanksClient in solana-tokens
This commit is contained in:
@ -1,18 +1,20 @@
|
||||
use crate::args::{BalancesArgs, DistributeTokensArgs, StakeArgs, TransactionLogArgs};
|
||||
use crate::db::{self, TransactionInfo};
|
||||
use crate::thin_client::{Client, ThinClient};
|
||||
use console::style;
|
||||
use csv::{ReaderBuilder, Trim};
|
||||
use indexmap::IndexMap;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use pickledb::PickleDb;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_banks_client::{BanksClient, BanksClientExt};
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentLevel,
|
||||
message::Message,
|
||||
native_token::{lamports_to_sol, sol_to_lamports},
|
||||
signature::{unique_signers, Signature, Signer},
|
||||
system_instruction,
|
||||
transport::TransportError,
|
||||
transaction::Transaction,
|
||||
transport::{self, TransportError},
|
||||
};
|
||||
use solana_stake_program::{
|
||||
stake_instruction,
|
||||
@ -21,9 +23,9 @@ use solana_stake_program::{
|
||||
use std::{
|
||||
cmp::{self},
|
||||
io,
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::time::delay_for;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
struct Bid {
|
||||
@ -92,8 +94,25 @@ fn create_allocation(bid: &Bid, dollars_per_sol: f64) -> Allocation {
|
||||
}
|
||||
}
|
||||
|
||||
fn distribute_tokens(
|
||||
client: &ThinClient,
|
||||
async fn transfer<S: Signer>(
|
||||
client: &mut BanksClient,
|
||||
lamports: u64,
|
||||
sender_keypair: &S,
|
||||
to_pubkey: &Pubkey,
|
||||
) -> io::Result<Transaction> {
|
||||
let create_instruction =
|
||||
system_instruction::transfer(&sender_keypair.pubkey(), &to_pubkey, lamports);
|
||||
let message = Message::new(&[create_instruction], Some(&sender_keypair.pubkey()));
|
||||
let recent_blockhash = client.get_recent_blockhash().await?;
|
||||
Ok(Transaction::new(
|
||||
&[sender_keypair],
|
||||
message,
|
||||
recent_blockhash,
|
||||
))
|
||||
}
|
||||
|
||||
async fn distribute_tokens(
|
||||
client: &mut BanksClient,
|
||||
db: &mut PickleDb,
|
||||
allocations: &[Allocation],
|
||||
args: &DistributeTokensArgs,
|
||||
@ -159,7 +178,17 @@ fn distribute_tokens(
|
||||
|
||||
let fee_payer_pubkey = args.fee_payer.pubkey();
|
||||
let message = Message::new(&instructions, Some(&fee_payer_pubkey));
|
||||
match client.send_and_confirm_message(message, &signers) {
|
||||
let result: transport::Result<(Transaction, u64)> = {
|
||||
if args.dry_run {
|
||||
Ok((Transaction::new_unsigned(message), std::u64::MAX))
|
||||
} else {
|
||||
let (_fee_calculator, blockhash, last_valid_slot) = client.get_fees().await?;
|
||||
let transaction = Transaction::new(&signers, message, blockhash);
|
||||
client.send_transaction(transaction.clone()).await?;
|
||||
Ok((transaction, last_valid_slot))
|
||||
}
|
||||
};
|
||||
match result {
|
||||
Ok((transaction, last_valid_slot)) => {
|
||||
db::set_transaction_info(
|
||||
db,
|
||||
@ -206,8 +235,8 @@ fn new_spinner_progress_bar() -> ProgressBar {
|
||||
progress_bar
|
||||
}
|
||||
|
||||
pub fn process_distribute_tokens(
|
||||
client: &ThinClient,
|
||||
pub async fn process_distribute_tokens(
|
||||
client: &mut BanksClient,
|
||||
args: &DistributeTokensArgs,
|
||||
) -> Result<Option<usize>, Error> {
|
||||
let mut allocations: Vec<Allocation> =
|
||||
@ -230,7 +259,7 @@ pub fn process_distribute_tokens(
|
||||
let mut db = db::open_db(&args.transaction_db, args.dry_run)?;
|
||||
|
||||
// Start by finalizing any transactions from the previous run.
|
||||
let confirmations = finalize_transactions(client, &mut db, args.dry_run)?;
|
||||
let confirmations = finalize_transactions(client, &mut db, args.dry_run).await?;
|
||||
|
||||
let transaction_infos = db::read_transaction_infos(&db);
|
||||
apply_previous_transactions(&mut allocations, &transaction_infos);
|
||||
@ -284,14 +313,14 @@ pub fn process_distribute_tokens(
|
||||
);
|
||||
}
|
||||
|
||||
distribute_tokens(client, &mut db, &allocations, args)?;
|
||||
distribute_tokens(client, &mut db, &allocations, args).await?;
|
||||
|
||||
let opt_confirmations = finalize_transactions(client, &mut db, args.dry_run)?;
|
||||
let opt_confirmations = finalize_transactions(client, &mut db, args.dry_run).await?;
|
||||
Ok(opt_confirmations)
|
||||
}
|
||||
|
||||
fn finalize_transactions(
|
||||
client: &ThinClient,
|
||||
async fn finalize_transactions(
|
||||
client: &mut BanksClient,
|
||||
db: &mut PickleDb,
|
||||
dry_run: bool,
|
||||
) -> Result<Option<usize>, Error> {
|
||||
@ -299,7 +328,7 @@ fn finalize_transactions(
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut opt_confirmations = update_finalized_transactions(client, db)?;
|
||||
let mut opt_confirmations = update_finalized_transactions(client, db).await?;
|
||||
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
|
||||
@ -312,8 +341,8 @@ fn finalize_transactions(
|
||||
}
|
||||
|
||||
// Sleep for about 1 slot
|
||||
sleep(Duration::from_millis(500));
|
||||
let opt_conf = update_finalized_transactions(client, db)?;
|
||||
delay_for(Duration::from_millis(500)).await;
|
||||
let opt_conf = update_finalized_transactions(client, db).await?;
|
||||
opt_confirmations = opt_conf;
|
||||
}
|
||||
|
||||
@ -322,8 +351,8 @@ fn finalize_transactions(
|
||||
|
||||
// Update the finalized bit on any transactions that are now rooted
|
||||
// Return the lowest number of confirmations on the unfinalized transactions or None if all are finalized.
|
||||
fn update_finalized_transactions(
|
||||
client: &ThinClient,
|
||||
async fn update_finalized_transactions(
|
||||
client: &mut BanksClient,
|
||||
db: &mut PickleDb,
|
||||
) -> Result<Option<usize>, Error> {
|
||||
let transaction_infos = db::read_transaction_infos(db);
|
||||
@ -342,8 +371,10 @@ fn update_finalized_transactions(
|
||||
.map(|(tx, _slot)| tx.signatures[0])
|
||||
.filter(|sig| *sig != Signature::default()) // Filter out dry-run signatures
|
||||
.collect();
|
||||
let transaction_statuses = client.get_signature_statuses(&unconfirmed_signatures)?;
|
||||
let root_slot = client.get_slot()?;
|
||||
let transaction_statuses = client
|
||||
.get_transaction_statuses(unconfirmed_signatures)
|
||||
.await?;
|
||||
let root_slot = client.get_root_slot().await?;
|
||||
|
||||
let mut confirmations = None;
|
||||
for ((transaction, last_valid_slot), opt_transaction_status) in unconfirmed_transactions
|
||||
@ -368,7 +399,10 @@ fn update_finalized_transactions(
|
||||
Ok(confirmations)
|
||||
}
|
||||
|
||||
pub fn process_balances(client: &ThinClient, args: &BalancesArgs) -> Result<(), csv::Error> {
|
||||
pub async fn process_balances(
|
||||
client: &mut BanksClient,
|
||||
args: &BalancesArgs,
|
||||
) -> Result<(), csv::Error> {
|
||||
let allocations: Vec<Allocation> =
|
||||
read_allocations(&args.input_csv, args.from_bids, args.dollars_per_sol);
|
||||
let allocations = merge_allocations(&allocations);
|
||||
@ -385,7 +419,7 @@ pub fn process_balances(client: &ThinClient, args: &BalancesArgs) -> Result<(),
|
||||
for allocation in &allocations {
|
||||
let address = allocation.recipient.parse().unwrap();
|
||||
let expected = lamports_to_sol(sol_to_lamports(allocation.amount));
|
||||
let actual = lamports_to_sol(client.get_balance(&address).unwrap());
|
||||
let actual = lamports_to_sol(client.get_balance(address).await.unwrap());
|
||||
println!(
|
||||
"{:<44} {:>24.9} {:>24.9} {:>24.9}",
|
||||
allocation.recipient,
|
||||
@ -406,15 +440,30 @@ pub fn process_transaction_log(args: &TransactionLogArgs) -> Result<(), Error> {
|
||||
|
||||
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
|
||||
use tempfile::{tempdir, NamedTempFile};
|
||||
pub fn test_process_distribute_tokens_with_client<C: Client>(client: C, sender_keypair: Keypair) {
|
||||
let thin_client = ThinClient::new(client, false);
|
||||
pub async fn test_process_distribute_tokens_with_client(
|
||||
client: &mut BanksClient,
|
||||
sender_keypair: Keypair,
|
||||
) {
|
||||
let fee_payer = Keypair::new();
|
||||
let (transaction, _last_valid_slot) = thin_client
|
||||
.transfer(sol_to_lamports(1.0), &sender_keypair, &fee_payer.pubkey())
|
||||
.unwrap();
|
||||
thin_client
|
||||
.poll_for_confirmation(&transaction.signatures[0])
|
||||
let transaction = transfer(
|
||||
client,
|
||||
sol_to_lamports(1.0),
|
||||
&sender_keypair,
|
||||
&fee_payer.pubkey(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
client
|
||||
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client
|
||||
.get_balance_with_commitment(fee_payer.pubkey(), CommitmentLevel::Recent)
|
||||
.await
|
||||
.unwrap(),
|
||||
sol_to_lamports(1.0),
|
||||
);
|
||||
|
||||
let alice_pubkey = Pubkey::new_rand();
|
||||
let allocation = Allocation {
|
||||
@ -445,7 +494,7 @@ pub fn test_process_distribute_tokens_with_client<C: Client>(client: C, sender_k
|
||||
dollars_per_sol: None,
|
||||
stake_args: None,
|
||||
};
|
||||
let confirmations = process_distribute_tokens(&thin_client, &args).unwrap();
|
||||
let confirmations = process_distribute_tokens(client, &args).await.unwrap();
|
||||
assert_eq!(confirmations, None);
|
||||
|
||||
let transaction_infos =
|
||||
@ -459,12 +508,12 @@ pub fn test_process_distribute_tokens_with_client<C: Client>(client: C, sender_k
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
thin_client.get_balance(&alice_pubkey).unwrap(),
|
||||
client.get_balance(alice_pubkey).await.unwrap(),
|
||||
expected_amount,
|
||||
);
|
||||
|
||||
// Now, run it again, and check there's no double-spend.
|
||||
process_distribute_tokens(&thin_client, &args).unwrap();
|
||||
process_distribute_tokens(client, &args).await.unwrap();
|
||||
let transaction_infos =
|
||||
db::read_transaction_infos(&db::open_db(&transaction_db, true).unwrap());
|
||||
assert_eq!(transaction_infos.len(), 1);
|
||||
@ -476,19 +525,27 @@ pub fn test_process_distribute_tokens_with_client<C: Client>(client: C, sender_k
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
thin_client.get_balance(&alice_pubkey).unwrap(),
|
||||
client.get_balance(alice_pubkey).await.unwrap(),
|
||||
expected_amount,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn test_process_distribute_stake_with_client<C: Client>(client: C, sender_keypair: Keypair) {
|
||||
let thin_client = ThinClient::new(client, false);
|
||||
pub async fn test_process_distribute_stake_with_client(
|
||||
client: &mut BanksClient,
|
||||
sender_keypair: Keypair,
|
||||
) {
|
||||
let fee_payer = Keypair::new();
|
||||
let (transaction, _last_valid_slot) = thin_client
|
||||
.transfer(sol_to_lamports(1.0), &sender_keypair, &fee_payer.pubkey())
|
||||
.unwrap();
|
||||
thin_client
|
||||
.poll_for_confirmation(&transaction.signatures[0])
|
||||
let transaction = transfer(
|
||||
client,
|
||||
sol_to_lamports(1.0),
|
||||
&sender_keypair,
|
||||
&fee_payer.pubkey(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
client
|
||||
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let stake_account_keypair = Keypair::new();
|
||||
@ -510,8 +567,11 @@ pub fn test_process_distribute_stake_with_client<C: Client>(client: C, sender_ke
|
||||
);
|
||||
let message = Message::new(&instructions, Some(&sender_keypair.pubkey()));
|
||||
let signers = [&sender_keypair, &stake_account_keypair];
|
||||
thin_client
|
||||
.send_and_confirm_message(message, &signers)
|
||||
let blockhash = client.get_recent_blockhash().await.unwrap();
|
||||
let transaction = Transaction::new(&signers, message, blockhash);
|
||||
client
|
||||
.process_transaction_with_commitment(transaction, CommitmentLevel::Recent)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let alice_pubkey = Pubkey::new_rand();
|
||||
@ -549,7 +609,7 @@ pub fn test_process_distribute_stake_with_client<C: Client>(client: C, sender_ke
|
||||
sender_keypair: Box::new(sender_keypair),
|
||||
dollars_per_sol: None,
|
||||
};
|
||||
let confirmations = process_distribute_tokens(&thin_client, &args).unwrap();
|
||||
let confirmations = process_distribute_tokens(client, &args).await.unwrap();
|
||||
assert_eq!(confirmations, None);
|
||||
|
||||
let transaction_infos =
|
||||
@ -563,17 +623,17 @@ pub fn test_process_distribute_stake_with_client<C: Client>(client: C, sender_ke
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
thin_client.get_balance(&alice_pubkey).unwrap(),
|
||||
client.get_balance(alice_pubkey).await.unwrap(),
|
||||
sol_to_lamports(1.0),
|
||||
);
|
||||
let new_stake_account_address = transaction_infos[0].new_stake_account_address.unwrap();
|
||||
assert_eq!(
|
||||
thin_client.get_balance(&new_stake_account_address).unwrap(),
|
||||
client.get_balance(new_stake_account_address).await.unwrap(),
|
||||
expected_amount - sol_to_lamports(1.0),
|
||||
);
|
||||
|
||||
// Now, run it again, and check there's no double-spend.
|
||||
process_distribute_tokens(&thin_client, &args).unwrap();
|
||||
process_distribute_tokens(client, &args).await.unwrap();
|
||||
let transaction_infos =
|
||||
db::read_transaction_infos(&db::open_db(&transaction_db, true).unwrap());
|
||||
assert_eq!(transaction_infos.len(), 1);
|
||||
@ -585,11 +645,11 @@ pub fn test_process_distribute_stake_with_client<C: Client>(client: C, sender_ke
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
thin_client.get_balance(&alice_pubkey).unwrap(),
|
||||
client.get_balance(alice_pubkey).await.unwrap(),
|
||||
sol_to_lamports(1.0),
|
||||
);
|
||||
assert_eq!(
|
||||
thin_client.get_balance(&new_stake_account_address).unwrap(),
|
||||
client.get_balance(new_stake_account_address).await.unwrap(),
|
||||
expected_amount - sol_to_lamports(1.0),
|
||||
);
|
||||
}
|
||||
@ -597,23 +657,33 @@ pub fn test_process_distribute_stake_with_client<C: Client>(client: C, sender_ke
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_runtime::{bank::Bank, bank_client::BankClient};
|
||||
use solana_banks_client::start_client;
|
||||
use solana_banks_server::banks_server::start_local_server;
|
||||
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
||||
use solana_sdk::genesis_config::create_genesis_config;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
#[test]
|
||||
fn test_process_distribute_tokens() {
|
||||
let (genesis_config, sender_keypair) = create_genesis_config(sol_to_lamports(9_000_000.0));
|
||||
let bank = Bank::new(&genesis_config);
|
||||
let bank_client = BankClient::new(bank);
|
||||
test_process_distribute_tokens_with_client(bank_client, sender_keypair);
|
||||
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(&genesis_config))));
|
||||
Runtime::new().unwrap().block_on(async {
|
||||
let transport = start_local_server(&bank_forks).await;
|
||||
let mut banks_client = start_client(transport).await.unwrap();
|
||||
test_process_distribute_tokens_with_client(&mut banks_client, sender_keypair).await;
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_distribute_stake() {
|
||||
let (genesis_config, sender_keypair) = create_genesis_config(sol_to_lamports(9_000_000.0));
|
||||
let bank = Bank::new(&genesis_config);
|
||||
let bank_client = BankClient::new(bank);
|
||||
test_process_distribute_stake_with_client(bank_client, sender_keypair);
|
||||
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(&genesis_config))));
|
||||
Runtime::new().unwrap().block_on(async {
|
||||
let transport = start_local_server(&bank_forks).await;
|
||||
let mut banks_client = start_client(transport).await.unwrap();
|
||||
test_process_distribute_stake_with_client(&mut banks_client, sender_keypair).await;
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,8 +1,8 @@
|
||||
use chrono::prelude::*;
|
||||
use pickledb::{error::Error, PickleDb, PickleDbDumpPolicy};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_banks_client::TransactionStatus;
|
||||
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature, transaction::Transaction};
|
||||
use solana_transaction_status::TransactionStatus;
|
||||
use std::{cmp::Ordering, fs, io, path::Path};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
@ -155,7 +155,7 @@ pub fn update_finalized_transaction(
|
||||
return Ok(Some(confirmations));
|
||||
}
|
||||
|
||||
if let Err(e) = &transaction_status.status {
|
||||
if let Some(e) = &transaction_status.err {
|
||||
// The transaction was finalized, but execution failed. Drop it.
|
||||
eprintln!(
|
||||
"Error in transaction with signature {}: {}",
|
||||
@ -273,7 +273,6 @@ mod tests {
|
||||
let transaction_status = TransactionStatus {
|
||||
slot: 0,
|
||||
confirmations: Some(1),
|
||||
status: Ok(()),
|
||||
err: None,
|
||||
};
|
||||
assert_eq!(
|
||||
@ -297,12 +296,10 @@ mod tests {
|
||||
let signature = Signature::default();
|
||||
let transaction_info = TransactionInfo::default();
|
||||
db.set(&signature.to_string(), &transaction_info).unwrap();
|
||||
let status = Err(TransactionError::AccountNotFound);
|
||||
let transaction_status = TransactionStatus {
|
||||
slot: 0,
|
||||
confirmations: None,
|
||||
status,
|
||||
err: None,
|
||||
err: Some(TransactionError::AccountNotFound),
|
||||
};
|
||||
assert_eq!(
|
||||
update_finalized_transaction(&mut db, &signature, Some(transaction_status), 0, 0)
|
||||
@ -325,7 +322,6 @@ mod tests {
|
||||
let transaction_status = TransactionStatus {
|
||||
slot: 0,
|
||||
confirmations: None,
|
||||
status: Ok(()),
|
||||
err: None,
|
||||
};
|
||||
assert_eq!(
|
||||
|
@ -2,4 +2,3 @@ pub mod arg_parser;
|
||||
pub mod args;
|
||||
pub mod commands;
|
||||
mod db;
|
||||
pub mod thin_client;
|
||||
|
@ -1,11 +1,9 @@
|
||||
use solana_cli_config::Config;
|
||||
use solana_cli_config::CONFIG_FILE;
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_tokens::{arg_parser::parse_args, args::Command, commands, thin_client::ThinClient};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use solana_banks_client::start_tcp_client;
|
||||
use solana_cli_config::{Config, CONFIG_FILE};
|
||||
use solana_tokens::{arg_parser::parse_args, args::Command, commands};
|
||||
use std::{env, error::Error, path::Path, process};
|
||||
use tokio::runtime::Runtime;
|
||||
use url::Url;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let command_args = parse_args(env::args_os())?;
|
||||
@ -20,16 +18,22 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
Config::default()
|
||||
};
|
||||
let json_rpc_url = command_args.url.unwrap_or(config.json_rpc_url);
|
||||
let client = RpcClient::new(json_rpc_url);
|
||||
let rpc_banks_url = Config::compute_rpc_banks_url(&json_rpc_url);
|
||||
let url = Url::parse(&rpc_banks_url)?;
|
||||
let host_port = (url.host_str().unwrap(), url.port().unwrap());
|
||||
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
let mut banks_client = runtime.block_on(start_tcp_client(&host_port))?;
|
||||
|
||||
match command_args.command {
|
||||
Command::DistributeTokens(args) => {
|
||||
let thin_client = ThinClient::new(client, args.dry_run);
|
||||
commands::process_distribute_tokens(&thin_client, &args)?;
|
||||
runtime.block_on(commands::process_distribute_tokens(
|
||||
&mut banks_client,
|
||||
&args,
|
||||
))?;
|
||||
}
|
||||
Command::Balances(args) => {
|
||||
let thin_client = ThinClient::new(client, false);
|
||||
commands::process_balances(&thin_client, &args)?;
|
||||
runtime.block_on(commands::process_balances(&mut banks_client, &args))?;
|
||||
}
|
||||
Command::TransactionLog(args) => {
|
||||
commands::process_transaction_log(&args)?;
|
||||
|
@ -1,193 +0,0 @@
|
||||
use solana_client::{rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig};
|
||||
use solana_runtime::bank_client::BankClient;
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
client::{AsyncClient, SyncClient},
|
||||
clock::Slot,
|
||||
commitment_config::CommitmentConfig,
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{Signature, Signer},
|
||||
signers::Signers,
|
||||
system_instruction,
|
||||
transaction::Transaction,
|
||||
transport::{Result, TransportError},
|
||||
};
|
||||
use solana_transaction_status::TransactionStatus;
|
||||
|
||||
pub trait Client {
|
||||
fn send_transaction1(&self, transaction: Transaction) -> Result<Signature>;
|
||||
fn get_signature_statuses1(
|
||||
&self,
|
||||
signatures: &[Signature],
|
||||
) -> Result<Vec<Option<TransactionStatus>>>;
|
||||
fn get_balance1(&self, pubkey: &Pubkey) -> Result<u64>;
|
||||
fn get_fees1(&self) -> Result<(Hash, FeeCalculator, Slot)>;
|
||||
fn get_slot1(&self) -> Result<Slot>;
|
||||
fn get_account1(&self, pubkey: &Pubkey) -> Result<Option<Account>>;
|
||||
}
|
||||
|
||||
impl Client for RpcClient {
|
||||
fn send_transaction1(&self, transaction: Transaction) -> Result<Signature> {
|
||||
self.send_transaction_with_config(
|
||||
&transaction,
|
||||
RpcSendTransactionConfig {
|
||||
skip_preflight: true,
|
||||
},
|
||||
)
|
||||
.map_err(|e| TransportError::Custom(e.to_string()))
|
||||
}
|
||||
|
||||
fn get_signature_statuses1(
|
||||
&self,
|
||||
signatures: &[Signature],
|
||||
) -> Result<Vec<Option<TransactionStatus>>> {
|
||||
self.get_signature_statuses(signatures)
|
||||
.map(|response| response.value)
|
||||
.map_err(|e| TransportError::Custom(e.to_string()))
|
||||
}
|
||||
|
||||
fn get_balance1(&self, pubkey: &Pubkey) -> Result<u64> {
|
||||
self.get_balance(pubkey)
|
||||
.map_err(|e| TransportError::Custom(e.to_string()))
|
||||
}
|
||||
|
||||
fn get_fees1(&self) -> Result<(Hash, FeeCalculator, Slot)> {
|
||||
let result = self
|
||||
.get_recent_blockhash_with_commitment(CommitmentConfig::default())
|
||||
.map_err(|e| TransportError::Custom(e.to_string()))?;
|
||||
Ok(result.value)
|
||||
}
|
||||
|
||||
fn get_slot1(&self) -> Result<Slot> {
|
||||
self.get_slot()
|
||||
.map_err(|e| TransportError::Custom(e.to_string()))
|
||||
}
|
||||
|
||||
fn get_account1(&self, pubkey: &Pubkey) -> Result<Option<Account>> {
|
||||
self.get_account(pubkey)
|
||||
.map(Some)
|
||||
.map_err(|e| TransportError::Custom(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Client for BankClient {
|
||||
fn send_transaction1(&self, transaction: Transaction) -> Result<Signature> {
|
||||
self.async_send_transaction(transaction)
|
||||
}
|
||||
|
||||
fn get_signature_statuses1(
|
||||
&self,
|
||||
signatures: &[Signature],
|
||||
) -> Result<Vec<Option<TransactionStatus>>> {
|
||||
signatures
|
||||
.iter()
|
||||
.map(|signature| {
|
||||
self.get_signature_status(signature).map(|opt| {
|
||||
opt.map(|status| TransactionStatus {
|
||||
slot: 0,
|
||||
confirmations: None,
|
||||
status,
|
||||
err: None,
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_balance1(&self, pubkey: &Pubkey) -> Result<u64> {
|
||||
self.get_balance(pubkey)
|
||||
}
|
||||
|
||||
fn get_fees1(&self) -> Result<(Hash, FeeCalculator, Slot)> {
|
||||
self.get_recent_blockhash_with_commitment(CommitmentConfig::default())
|
||||
}
|
||||
|
||||
fn get_slot1(&self) -> Result<Slot> {
|
||||
self.get_slot()
|
||||
}
|
||||
|
||||
fn get_account1(&self, pubkey: &Pubkey) -> Result<Option<Account>> {
|
||||
self.get_account(pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThinClient<'a> {
|
||||
client: Box<dyn Client + 'a>,
|
||||
dry_run: bool,
|
||||
}
|
||||
|
||||
impl<'a> ThinClient<'a> {
|
||||
pub fn new<C: Client + 'a>(client: C, dry_run: bool) -> Self {
|
||||
Self {
|
||||
client: Box::new(client),
|
||||
dry_run,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_transaction(&self, transaction: Transaction) -> Result<Signature> {
|
||||
if self.dry_run {
|
||||
return Ok(Signature::default());
|
||||
}
|
||||
self.client.send_transaction1(transaction)
|
||||
}
|
||||
|
||||
pub fn poll_for_confirmation(&self, signature: &Signature) -> Result<()> {
|
||||
while self.get_signature_statuses(&[*signature])?[0].is_none() {
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_signature_statuses(
|
||||
&self,
|
||||
signatures: &[Signature],
|
||||
) -> Result<Vec<Option<TransactionStatus>>> {
|
||||
self.client.get_signature_statuses1(signatures)
|
||||
}
|
||||
|
||||
pub fn send_and_confirm_message<S: Signers>(
|
||||
&self,
|
||||
message: Message,
|
||||
signers: &S,
|
||||
) -> Result<(Transaction, Slot)> {
|
||||
if self.dry_run {
|
||||
return Ok((Transaction::new_unsigned(message), std::u64::MAX));
|
||||
}
|
||||
let (blockhash, _fee_caluclator, last_valid_slot) = self.get_fees()?;
|
||||
|
||||
let transaction = Transaction::new(signers, message, blockhash);
|
||||
self.send_transaction(transaction.clone())?;
|
||||
Ok((transaction, last_valid_slot))
|
||||
}
|
||||
|
||||
pub fn transfer<S: Signer>(
|
||||
&self,
|
||||
lamports: u64,
|
||||
sender_keypair: &S,
|
||||
to_pubkey: &Pubkey,
|
||||
) -> Result<(Transaction, u64)> {
|
||||
let create_instruction =
|
||||
system_instruction::transfer(&sender_keypair.pubkey(), &to_pubkey, lamports);
|
||||
let message = Message::new(&[create_instruction], Some(&sender_keypair.pubkey()));
|
||||
self.send_and_confirm_message(message, &[sender_keypair])
|
||||
}
|
||||
|
||||
pub fn get_fees(&self) -> Result<(Hash, FeeCalculator, Slot)> {
|
||||
self.client.get_fees1()
|
||||
}
|
||||
|
||||
pub fn get_slot(&self) -> Result<Slot> {
|
||||
self.client.get_slot1()
|
||||
}
|
||||
|
||||
pub fn get_balance(&self, pubkey: &Pubkey) -> Result<u64> {
|
||||
self.client.get_balance1(pubkey)
|
||||
}
|
||||
|
||||
pub fn get_account(&self, pubkey: &Pubkey) -> Result<Option<Account>> {
|
||||
self.client.get_account1(pubkey)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user