Skip to content

Commit

Permalink
Add --no-wait (solana-labs#16)
Browse files Browse the repository at this point in the history
* Add ThinClient methods to implement --no-wait

* Plumb --no-wait through

No tests yet

* Check transaction status on startup

* Easier to test

* Wait until transaction is finalized before checking if it failed with an error

It's possible that a minority fork thinks it failed.

* Add unit tests

* Remove dead code and rustfmt

* Don't flush database to file if doing a dry-run
  • Loading branch information
garious committed May 6, 2020
1 parent ee8e5ac commit fdb5f56
Show file tree
Hide file tree
Showing 6 changed files with 363 additions and 70 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ solana-remote-wallet = "1.1.8"
solana-runtime = "1.1.8"
solana-sdk = "1.1.8"
solana-stake-program = "1.1.8"
solana-transaction-status = "1.1.8"
tempfile = "3.1.0"
thiserror = "1.0"

Expand Down
12 changes: 12 additions & 0 deletions src/arg_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ where
.long("dry-run")
.help("Do not execute any transfers"),
)
.arg(
Arg::with_name("no_wait")
.long("no-wait")
.help("Don't wait for transaction confirmations"),
)
.arg(
Arg::with_name("sender_keypair")
.long("from")
Expand Down Expand Up @@ -113,6 +118,11 @@ where
.long("dry-run")
.help("Do not execute any transfers"),
)
.arg(
Arg::with_name("no_wait")
.long("no-wait")
.help("Don't wait for transaction confirmations"),
)
.arg(
Arg::with_name("stake_account_address")
.required(true)
Expand Down Expand Up @@ -189,6 +199,7 @@ fn parse_distribute_tokens_args(matches: &ArgMatches<'_>) -> DistributeTokensArg
transactions_db: value_t_or_exit!(matches, "transactions_db", String),
dollars_per_sol: value_t!(matches, "dollars_per_sol", f64).ok(),
dry_run: matches.is_present("dry_run"),
no_wait: matches.is_present("no_wait"),
sender_keypair: value_t!(matches, "sender_keypair", String).ok(),
fee_payer: value_t!(matches, "fee_payer", String).ok(),
force: matches.is_present("force"),
Expand All @@ -200,6 +211,7 @@ fn parse_distribute_stake_args(matches: &ArgMatches<'_>) -> DistributeStakeArgs<
allocations_csv: value_t_or_exit!(matches, "allocations_csv", String),
transactions_db: value_t_or_exit!(matches, "transactions_db", String),
dry_run: matches.is_present("dry_run"),
no_wait: matches.is_present("no_wait"),
stake_account_address: value_t_or_exit!(matches, "stake_account_address", String),
sol_for_fees: value_t_or_exit!(matches, "sol_for_fees", f64),
stake_authority: value_t!(matches, "stake_authority", String).ok(),
Expand Down
4 changes: 4 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub struct DistributeTokensArgs<K> {
pub transactions_db: String,
pub dollars_per_sol: Option<f64>,
pub dry_run: bool,
pub no_wait: bool,
pub sender_keypair: Option<K>,
pub fee_payer: Option<K>,
pub force: bool,
Expand All @@ -19,6 +20,7 @@ pub struct DistributeStakeArgs<P, K> {
pub allocations_csv: String,
pub transactions_db: String,
pub dry_run: bool,
pub no_wait: bool,
pub sol_for_fees: f64,
pub stake_account_address: P,
pub stake_authority: Option<K>,
Expand Down Expand Up @@ -57,6 +59,7 @@ pub fn resolve_command(
transactions_db: args.transactions_db,
dollars_per_sol: args.dollars_per_sol,
dry_run: args.dry_run,
no_wait: args.no_wait,
sender_keypair: args.sender_keypair.as_ref().map(|key_url| {
signer_from_path(&matches, &key_url, "sender", &mut wallet_manager).unwrap()
}),
Expand All @@ -74,6 +77,7 @@ pub fn resolve_command(
allocations_csv: args.allocations_csv,
transactions_db: args.transactions_db,
dry_run: args.dry_run,
no_wait: args.no_wait,
stake_account_address: pubkey_from_path(
&matches,
&args.stake_account_address,
Expand Down
93 changes: 73 additions & 20 deletions src/thin_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,70 +6,123 @@ use solana_sdk::{
hash::Hash,
message::Message,
pubkey::Pubkey,
signature::{Keypair, Signature, Signer},
signature::{Signature, Signer},
signers::Signers,
system_instruction,
transaction::Transaction,
transport::{Result, TransportError},
};
use solana_transaction_status::TransactionStatus;

pub trait Client {
fn async_send_transaction1(&self, transaction: Transaction) -> Result<Signature>;

// TODO: Work to delete this
fn send_and_confirm_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_recent_blockhash_and_fees(&self) -> Result<(Hash, FeeCalculator)>;
fn get_recent_blockhash1(&self) -> Result<(Hash, FeeCalculator)>;
}

impl Client for RpcClient {
fn async_send_transaction1(&self, transaction: Transaction) -> Result<Signature> {
self.send_transaction(&transaction)
.map_err(|e| TransportError::Custom(e.to_string()))
}

fn send_and_confirm_transaction1(&self, mut transaction: Transaction) -> Result<Signature> {
let signers: Vec<&Keypair> = vec![]; // Don't allow resigning
let signers: Vec<&dyn Signer> = vec![]; // Don't allow resigning
self.send_and_confirm_transaction_with_spinner(&mut transaction, &signers)
.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> {
let balance = self
.get_balance(pubkey)
.map_err(|e| TransportError::Custom(e.to_string()))?;
Ok(balance)
self.get_balance(pubkey)
.map_err(|e| TransportError::Custom(e.to_string()))
}

fn get_recent_blockhash_and_fees(&self) -> Result<(Hash, FeeCalculator)> {
let blockhash = self
.get_recent_blockhash()
.map_err(|e| TransportError::Custom(e.to_string()))?;
Ok(blockhash)
fn get_recent_blockhash1(&self) -> Result<(Hash, FeeCalculator)> {
self.get_recent_blockhash()
.map_err(|e| TransportError::Custom(e.to_string()))
}
}

impl Client for BankClient {
fn async_send_transaction1(&self, transaction: Transaction) -> Result<Signature> {
self.async_send_transaction(transaction)
}

fn send_and_confirm_transaction1(&self, transaction: Transaction) -> Result<Signature> {
let signature = self.async_send_transaction(transaction)?;
let signature = self.async_send_transaction1(transaction)?;
self.poll_for_signature(&signature)?;
Ok(signature)
}

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_recent_blockhash_and_fees(&self) -> Result<(Hash, FeeCalculator)> {
fn get_recent_blockhash1(&self) -> Result<(Hash, FeeCalculator)> {
self.get_recent_blockhash()
}
}

pub struct ThinClient<C: Client>(pub C);

impl<C: Client> ThinClient<C> {
pub fn async_send_transaction(&self, transaction: Transaction) -> Result<Signature> {
self.0.async_send_transaction1(transaction)
}

pub fn get_signature_statuses(
&self,
signatures: &[Signature],
) -> Result<Vec<Option<TransactionStatus>>> {
self.0.get_signature_statuses1(signatures)
}

pub fn send_transaction(&self, transaction: Transaction) -> Result<Signature> {
// TODO: implement this in terms of ThinClient methods and then remove
// send_and_confirm_transaction1 from from the Client trait.
self.0.send_and_confirm_transaction1(transaction)
}

pub fn send_message<S: Signers>(&self, message: Message, signers: &S) -> Result<Signature> {
let (blockhash, _fee_caluclator) = self.0.get_recent_blockhash_and_fees()?;
let (blockhash, _fee_caluclator) = self.get_recent_blockhash()?;
let transaction = Transaction::new(signers, message, blockhash);
let signature = transaction.signatures[0];
self.send_transaction(transaction)?;
Ok(signature)
self.send_transaction(transaction)
}

pub fn transfer<S: Signer>(
Expand All @@ -84,8 +137,8 @@ impl<C: Client> ThinClient<C> {
self.send_message(message, &[sender_keypair])
}

pub fn get_recent_blockhash_and_fees(&self) -> Result<(Hash, FeeCalculator)> {
self.0.get_recent_blockhash_and_fees()
pub fn get_recent_blockhash(&self) -> Result<(Hash, FeeCalculator)> {
self.0.get_recent_blockhash1()
}

pub fn get_balance(&self, pubkey: &Pubkey) -> Result<u64> {
Expand Down
Loading

0 comments on commit fdb5f56

Please sign in to comment.