diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 307e4993c..6fcf7e16a 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -2,7 +2,7 @@ use crate::{ checks::*, cli_output::{CliAccount, CliSignOnlyData, CliSignature, OutputFormat}, cluster_query::*, - display::println_name_value, + display::{new_spinner_progress_bar, println_name_value, println_transaction}, nonce::{self, *}, offline::{blockhash_query::BlockhashQuery, *}, spend_utils::*, @@ -27,7 +27,7 @@ use solana_clap_utils::{ use solana_client::{ client_error::{ClientError, ClientErrorKind, Result as ClientResult}, rpc_client::RpcClient, - rpc_config::RpcLargestAccountsFilter, + rpc_config::{RpcLargestAccountsFilter, RpcSendTransactionConfig}, rpc_response::{RpcAccount, RpcKeyedAccount}, }; #[cfg(not(test))] @@ -37,7 +37,7 @@ use solana_faucet::faucet_mock::request_airdrop_transaction; use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_sdk::{ bpf_loader, - clock::{Epoch, Slot}, + clock::{Epoch, Slot, DEFAULT_TICKS_PER_SECOND}, commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, hash::Hash, @@ -48,6 +48,7 @@ use solana_sdk::{ program_utils::DecodeError, pubkey::{Pubkey, MAX_SEED_LEN}, signature::{Keypair, Signature, Signer, SignerError}, + signers::Signers, system_instruction::{self, SystemError}, system_program, transaction::{Transaction, TransactionError}, @@ -1159,7 +1160,7 @@ fn process_confirm( "\nTransaction executed in slot {}:", confirmed_transaction.slot ); - crate::display::println_transaction( + println_transaction( &confirmed_transaction .transaction .transaction @@ -1189,7 +1190,7 @@ fn process_confirm( } fn process_decode_transaction(transaction: &Transaction) -> ProcessResult { - crate::display::println_transaction(transaction, &None, ""); + println_transaction(transaction, &None, ""); Ok("".to_string()) } @@ -1227,6 +1228,103 @@ fn process_show_account( Ok(account_string) } +fn send_and_confirm_transactions_with_spinner( + rpc_client: &RpcClient, + mut transactions: Vec, + signer_keys: &T, +) -> Result<(), Box> { + let progress_bar = new_spinner_progress_bar(); + let mut send_retries = 5; + loop { + let mut status_retries = 15; + + // Send all transactions + let mut transactions_signatures = vec![]; + let num_transactions = transactions.len(); + for transaction in transactions { + if cfg!(not(test)) { + // Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors + // when all the write transactions modify the same program account (eg, deploying a + // new program) + sleep(Duration::from_millis(1000 / DEFAULT_TICKS_PER_SECOND)); + } + + let signature = rpc_client + .send_transaction_with_config( + &transaction, + RpcSendTransactionConfig { + skip_preflight: true, + }, + ) + .ok(); + transactions_signatures.push((transaction, signature)); + + progress_bar.set_message(&format!( + "[{}/{}] Transactions sent", + transactions_signatures.len(), + num_transactions + )); + } + + // Collect statuses for all the transactions, drop those that are confirmed + while status_retries > 0 { + status_retries -= 1; + + progress_bar.set_message(&format!( + "[{}/{}] Transactions confirmed", + num_transactions - transactions_signatures.len(), + num_transactions + )); + + if cfg!(not(test)) { + // Retry twice a second + sleep(Duration::from_millis(500)); + } + + transactions_signatures = transactions_signatures + .into_iter() + .filter(|(_transaction, signature)| { + if let Some(signature) = signature { + if let Ok(status) = rpc_client.get_signature_status(&signature) { + if rpc_client + .get_num_blocks_since_signature_confirmation(&signature) + .unwrap_or(0) + > 1 + { + return false; + } else { + return match status { + None => true, + Some(result) => result.is_err(), + }; + } + } + } + true + }) + .collect(); + + if transactions_signatures.is_empty() { + return Ok(()); + } + } + + if send_retries == 0 { + return Err("Transactions failed".into()); + } + send_retries -= 1; + + // Re-sign any failed transactions with a new blockhash and retry + let (blockhash, _fee_calculator) = rpc_client + .get_new_blockhash(&transactions_signatures[0].0.message().recent_blockhash)?; + transactions = vec![]; + for (mut transaction, _) in transactions_signatures.into_iter() { + transaction.try_sign(signer_keys, blockhash)?; + transactions.push(transaction); + } + } +} + fn process_deploy( rpc_client: &RpcClient, config: &CliConfig, @@ -1294,15 +1392,18 @@ fn process_deploy( })?; trace!("Writing program data"); - rpc_client - .send_and_confirm_transactions_with_spinner(write_transactions, &signers) - .map_err(|_| { - CliError::DynamicProgramError("Data writes to program account failed".to_string()) - })?; + send_and_confirm_transactions_with_spinner(&rpc_client, write_transactions, &signers).map_err( + |_| CliError::DynamicProgramError("Data writes to program account failed".to_string()), + )?; trace!("Finalizing program account"); rpc_client - .send_and_confirm_transaction_with_spinner(&finalize_tx) + .send_and_confirm_transaction_with_spinner_and_config( + &finalize_tx, + RpcSendTransactionConfig { + skip_preflight: true, + }, + ) .map_err(|e| { CliError::DynamicProgramError(format!("Finalizing program account failed: {}", e)) })?; diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index 867189a90..274f327b9 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -1,12 +1,11 @@ use crate::{ cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, cli_output::*, - display::println_name_value, + display::{new_spinner_progress_bar, println_name_value}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, }; use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand}; use console::{style, Emoji}; -use indicatif::{ProgressBar, ProgressStyle}; use solana_clap_utils::{ commitment::{commitment_arg, COMMITMENT_ARG}, input_parsers::*, @@ -467,15 +466,6 @@ pub fn parse_transaction_history( }) } -/// Creates a new process bar for processing that will take an unknown amount of time -fn new_spinner_progress_bar() -> ProgressBar { - let progress_bar = ProgressBar::new(42); - progress_bar - .set_style(ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}")); - progress_bar.enable_steady_tick(100); - progress_bar -} - pub fn process_catchup( rpc_client: &RpcClient, node_pubkey: &Pubkey, diff --git a/cli/src/display.rs b/cli/src/display.rs index 42328bd5a..b089564b1 100644 --- a/cli/src/display.rs +++ b/cli/src/display.rs @@ -1,5 +1,6 @@ use crate::cli::SettingType; use console::style; +use indicatif::{ProgressBar, ProgressStyle}; use solana_sdk::{ hash::Hash, native_token::lamports_to_sol, program_utils::limited_deserialize, transaction::Transaction, @@ -200,3 +201,12 @@ pub fn println_transaction( } } } + +/// Creates a new process bar for processing that will take an unknown amount of time +pub fn new_spinner_progress_bar() -> ProgressBar { + let progress_bar = ProgressBar::new(42); + progress_bar + .set_style(ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}")); + progress_bar.enable_steady_tick(100); + progress_bar +} diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 51d3c6503..9a50b0a93 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -32,7 +32,6 @@ use solana_transaction_status::{ }; use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY; use std::{ - error, net::SocketAddr, thread::sleep, time::{Duration, Instant}, @@ -416,96 +415,6 @@ impl RpcClient { } } - pub fn send_and_confirm_transactions_with_spinner( - &self, - mut transactions: Vec, - signer_keys: &T, - ) -> Result<(), Box> { - let progress_bar = new_spinner_progress_bar(); - let mut send_retries = 5; - loop { - let mut status_retries = 15; - - // Send all transactions - let mut transactions_signatures = vec![]; - let num_transactions = transactions.len(); - for transaction in transactions { - if cfg!(not(test)) { - // Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors - // when all the write transactions modify the same program account (eg, deploying a - // new program) - sleep(Duration::from_millis(1000 / DEFAULT_TICKS_PER_SECOND)); - } - - let signature = self.send_transaction(&transaction).ok(); - transactions_signatures.push((transaction, signature)); - - progress_bar.set_message(&format!( - "[{}/{}] Transactions sent", - transactions_signatures.len(), - num_transactions - )); - } - - // Collect statuses for all the transactions, drop those that are confirmed - while status_retries > 0 { - status_retries -= 1; - - progress_bar.set_message(&format!( - "[{}/{}] Transactions confirmed", - num_transactions - transactions_signatures.len(), - num_transactions - )); - - if cfg!(not(test)) { - // Retry twice a second - sleep(Duration::from_millis(500)); - } - - transactions_signatures = transactions_signatures - .into_iter() - .filter(|(_transaction, signature)| { - if let Some(signature) = signature { - if let Ok(status) = self.get_signature_status(&signature) { - if self - .get_num_blocks_since_signature_confirmation(&signature) - .unwrap_or(0) - > 1 - { - return false; - } else { - return match status { - None => true, - Some(result) => result.is_err(), - }; - } - } - } - true - }) - .collect(); - - if transactions_signatures.is_empty() { - return Ok(()); - } - } - - if send_retries == 0 { - return Err(RpcError::ForUser("Transactions failed".to_string()).into()); - } - send_retries -= 1; - - // Re-sign any failed transactions with a new blockhash and retry - let (blockhash, _fee_calculator) = - self.get_new_blockhash(&transactions_signatures[0].0.message().recent_blockhash)?; - transactions = vec![]; - for (mut transaction, _) in transactions_signatures.into_iter() { - transaction.try_sign(signer_keys, blockhash)?; - transactions.push(transaction); - } - } - } - pub fn resign_transaction( &self, tx: &mut Transaction, @@ -962,6 +871,17 @@ impl RpcClient { pub fn send_and_confirm_transaction_with_spinner( &self, transaction: &Transaction, + ) -> ClientResult { + self.send_and_confirm_transaction_with_spinner_and_config( + transaction, + RpcSendTransactionConfig::default(), + ) + } + + pub fn send_and_confirm_transaction_with_spinner_and_config( + &self, + transaction: &Transaction, + config: RpcSendTransactionConfig, ) -> ClientResult { let mut confirmations = 0; @@ -977,7 +897,7 @@ impl RpcClient { )); let mut status_retries = 15; let (signature, status) = loop { - let signature = self.send_transaction(transaction)?; + let signature = self.send_transaction_with_config(transaction, config.clone())?; // Get recent commitment in order to count confirmations for successful transactions let status = self